diff --git a/README.md b/README.md index 89b62c6..3554316 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,5 @@ vax_mp ====== VAX MP - a multiprocessor VAX simulator + +Main site: http://oboguev.net/vax_mp diff --git a/docs/VAX MP OpenVMS User Manual.pdf b/docs/VAX MP OpenVMS User Manual.pdf new file mode 100644 index 0000000..cf356d7 Binary files /dev/null and b/docs/VAX MP OpenVMS User Manual.pdf differ diff --git a/docs/VAX MP Technical Overview.pdf b/docs/VAX MP Technical Overview.pdf new file mode 100644 index 0000000..0be9bec Binary files /dev/null and b/docs/VAX MP Technical Overview.pdf differ diff --git a/src/0readmeAsynchIO.txt b/src/0readmeAsynchIO.txt new file mode 100644 index 0000000..c1fd22f --- /dev/null +++ b/src/0readmeAsynchIO.txt @@ -0,0 +1,164 @@ +SIM_ASYNCH_IO + +Theory of operation. + +Features. + - Optional Use. Build with or without SIM_ASYNCH_IO defined and + simulators will still build and perform correctly when run. + Additionmally, a simulator built with SIM_ASYNCH_IO defined can + dynamically disable and reenable asynchronous operation with + the scp commands SET NOASYNCH and SET ASYNCH respectively. + - Consistent Save/Restore state. The state of a simulator saved + on a simulator with (or without) Asynch support can be restored + on any simulator of the same version with or without Asynch + support. + - Optimal behavior/performance with simulator running with or + without CPU idling enabled. + - Consistent minimum instruction scheduling delays when operating + with or without SIM_ASYNCH_IO. When SIM_ASYNCH_IO is emabled, + any operation which would have been scheduled to occurr in 'n' + instructions will still occur (from the simulated computer's + point of view) at least 'n' instructions after it was initiated. + +Benefits. + Allows a simulator to execute simulated instructions concurrently + with I/O operations which may take numerous milliseconds to perform. + Allows a simulated device to potentially avoid polling for the arrival + of data. Polling consumes host processor CPU cycles which may better + be spent executing simulated instructions or letting other host + processes run. Measurements made of available instruction execution + easily demonstrate the benefits of parallel instruction and I/O + activities. A VAX simulator with a process running a disk intensive + application in one process was able to process 11 X the number of + Dhrystone operations with Asynch I/O enabled. + +Asynch I/O is provided through a callback model. +SimH Libraries which provide Asynch I/O support: + sim_disk + sim_tape + sim_ether + +Requirements to use: +The Simulator's instruction loop needs to be modified to include a single +line which checks for asynchronouzly arrived events. The vax_cpu.c +module added the following line indicated by >>>: + + /* Main instruction loop */ + + for ( ;; ) { + + [...] +>>> AIO_CHECK_EVENT; + if (sim_interval <= 0) { /* chk clock queue */ + temp = sim_process_event (); + if (temp) + ABORT (temp); + SET_IRQL; /* update interrupts */ + } + +A global variable (sim_asynch_latency) is used to indicate the "interrupt +dispatch latency". This variable is the number of nanoseconds between checks +for completed asynchronous I/O. The default value is 4000 (4 usec) which +corresponds reasonably with simulated hardware. This variable controls +the computation of sim_asynch_inst_latency which is the number of simulated +instructions in the sim_asynch_latency interval. We are trying to avoid +checking for completed asynchronous I/O after every instruction since the +actual checking every instruction can slow down execution. Periodic checks +provide a balance which allows response similar to real hardware while also +providing minimal impact on actual instruction execution. Meanwhile, if +maximal response is desired, then the value of sim_asynch_latency can be +set sufficiently low to assure that sim_asynch_inst_latency computes to 1. +The sim_asynch_inst_latency is dynamically updated once per second in the +sim_rtcn_calb routine where clock to instruction execution is dynamically +determined. A simulator would usually add register definitions +to enable viewing and setting of these variables via scp: + +#if defined (SIM_ASYNCH_IO) + { DRDATA (LATENCY, sim_asynch_latency, 32), PV_LEFT }, + { DRDATA (INST_LATENCY, sim_asynch_inst_latency, 32), PV_LEFT }, +#endif + + +Naming conventions: +All of the routines implemented in sim_disk and sim_tape have been kept +in place. All routines which perform I/O have a variant routine available +with a "_a" appended to the the routine name with the addition of a single +parameter which indicates the asynch completion callback routine. For +example there now exists the routines: + t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max); + t_stat sim_tape_rdrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback); + +The Purpose of the callback function is to record the I/O completion status +and then to schedule the activation of the unit. + +Considerations: +Avoiding multiple concurrent users of the unit structure. While asynch +I/O is pending on a Unit, the unit should not otherwise be on the event +queue. The I/O completion will cause the Unit to be scheduled to run +immediately to actually dispatch control flow to the callback routine. +The callback routine is always called in the same thread which is +executing instructions. Since all simulator device data structures are +only referenced from this thread there are no host multi-processor cache +coherency issues to be concerned about. + +Arguments to the callback routine: +UNIT *, and IO Status +Requirements of the Callback routine. +The callback routine must save the I/O completion status in a place +which the next invocation of the unit service routine will reference +and act on it. This allows device code to return error conditions +back to scp in a consistent way without regard to how the callback +routine (and the actual I/O) may have been executed. + +Required change in device coding. +Devices which wish to leverage the benefits of asynch I/O must rearrange +the code which implements the unit service routine. This rearrangement +usually entails breaking the activities into two phases. The first phase +(I'll call the top half) involves performing whatever is needed to +initiate a call to perform an I/O operation with a callback argument. +Control is then immediately returned to the scp event dispatcher. +The callback routine needs to be coded to stash away the io completion +status and some indicator that an I/O has completed. +The top/bottom half separation of the unit service routine would be +coded to examine the I/O completion indicator and invoke the bottom half +code upon completion. The bottom half code should clear the I/O +completion indicator and then perform any activities which normally +need to occur after the I/O completes. Care should be taken while +performing these top/bottom half activities to return to the scp event +dispatcher with either SCPE_OK or an appropriate error code when needed. +The need to return error indications to the scp event dispatcher is why +the bottom half activities can't simply be performed in the +callback routine (the callback routine does not return a status). +Care should also be taken to realize that local variables in the +unit service routine will not directly survive between the separate +top and bottom half calls to the unit service routine. If any such +information must be referenced in both the top and bottom half code paths +then it must either be recomputed prior to the top/bottom half check +or not stored in local variables of the unit service routine. + +Run time requirements to use SIM_ASYNCH_IO. +The Posix threads API (pthreads) is required for asynchronous execution. +Most *nix platforms have these APIs available and on these platforms +simh is typically built with these available since on these platforms, +pthreads is required for simh networking support. Windows can also +utilize the pthreads APIs if the compile and run time support for the +win32Pthreads package has been installed on the build system and the +run time dll is available in the execution environment. + +Sample Asynch I/O device implementations. +The pdp11_rq.c module has been refactored to leverage the asynch I/O +features of the sim_disk library. The impact to this code to adopt the +asynch I/O paradigm was quite minimal. +The pdp11_rp.c module has also been refactored to leverage the asynch I/O +features of the sim_disk library. +The pdp11_tq.c module has been refactored to leverage the asynch I/O +features of the sim_tape library. The impact to this code to adopt the +asynch I/O paradigm was very significant. This was due to the two facts: +1) there are many different operations which can be requested of tape +devices and 2) some of the tmscp operations required many separate +operations on the physical device layer to perform a single tmscp request. +This issue was addressed by adding additional routines to the physical +device layer (in sim_tape.c) which combined these multiple operations. +This approach will dovetail well with a potential future addition of +operations on physical tapes as yet another supported tape format. + diff --git a/src/0readme_38.txt b/src/0readme_38.txt new file mode 100644 index 0000000..ffe54c0 --- /dev/null +++ b/src/0readme_38.txt @@ -0,0 +1,80 @@ +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. diff --git a/src/0readme_ethernet.txt b/src/0readme_ethernet.txt new file mode 100644 index 0000000..0f11569 --- /dev/null +++ b/src/0readme_ethernet.txt @@ -0,0 +1,643 @@ +This file contains information about the SIMH Ethernet package. + +------------------------------------------------------------------------------- + +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-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 +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 +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 +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. + +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. + +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 and OSX. Each of these +platforms has some way to create a tap pseudo device (and possibly then to +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 make + apt-get install libpcap-dev + 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" + +OSX (Snow Leopard) + OSX Does NOT have native support for tun/tap interfaces. It also does not have native + support for bridging. + + Mattias Nissler has created tun/tap functionality available at http://tuntaposx,sourceforge.net/ + + We'll punt on bridging for the sake of this example and move on to use a basic tap + based internal network so a host and guest can communicate directly. + + Download the install package from: + http://sourceforge.net/projects/tuntaposx/files/tuntap/20090913/tuntap_20090913.tar.gz + + Expand the tarball to a directory. + Invoke the package installer tuntap_20090913.pkg + Click through the various prompts accepting things and eventually installing the package. + + # Build and Run simulator and: + sim> attach xq tap:tap0 + sim> ! ifconfig tap0 192.168.6.1 netmask 255.255.255.0 + + Simulated system uses IP address 192.168.6.2 and host uses 192.167.6.1 + and things work. + You must run as root for this to work. + +------------------------------------------------------------------------------- +An alternative to direct pcap and tun/tap networking on *nix environments is +VDE (Virtual Distributed Ethernet). + +Note 1: Using vde based networking is likely more flexible, but it isn't + nearly as efficient. Host OS overhead will always be higher when + vde networking is used as compared to native pcap and/or tun/tap + networking. +Note 2: Root access will likely be needed to configure or start the vde + environment prior to starting a simulator which may use it. +Note 3: Simulators running using VDE networking can run without root + privilege. + +Linux (Ubuntu 10.04): + apt-get install make + apt-get install libvdeplug-dev + apt-get install vde2 + + vde_switch -s /tmp/switch1 -tap tap0 -m 666 + ifconfig tap0 192.168.6.1 netmask 255.255.255.0 up + + # Build and Run simulator and: + sim> attach xq vde:/tmp/switch1 #simulator uses IP address 192.168.6.2 + +------------------------------------------------------------------------------- + +Windows notes: + 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. + + 2. You must *install* the WinPCAP runtime package. + + 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 "npf" driver to autostart. + Current WinPcap installers provide an option to configure this at + installation time. + + +Building on Windows: + You should be able to build with any of the free compiler environments + available on the Windows platform. If you want to use the Visual C++ + Express 2008 or 2010 interactive development environments, read the file + ".\Visual Studio Projects\0ReadMe_Projects.txt" for details about the + required dependencies. Alternatively, you can build simh with networking + support using the MinGW GCC compiler environment. Both the Visual C++ + and MinGW build environments require WinPcap and Posix packages being + available. These should be located in a directory structure parallel to + the current simulator source directory. + + For Example, the directory structure should look like: + + .../simh/simhv38-2-rc1/VAX/vax_cpu.c + .../simh/simhv38-2-rc1/scp.c + .../simh/simhv38-2-rc1/Visual Studio Projects/simh.sln + .../simh/simhv38-2-rc1/Visual Studio Projects/VAX.vcproj + .../simh/simhv38-2-rc1/BIN/Nt/Win32-Release/vax.exe + .../simh/windows-build/pthreads/pthread.h + .../simh/windows-build/winpcap/WpdPack/Include/pcap.h + + The contents of the windows-build directory can be downloaded from: + + https://github.com/downloads/markpizz/simh/windows-build.zip + + + There are Windows batch files provided to initiate compiles using the MinGW + compiler tool chain. These batch files are located in the same directory + as this file and are called: build_mingw.bat, build_mingw_ether.bat, and + build_mingw_noasync.bat. These batch files each presume that the MinGW + toolchain is either in the current path or, if not that it is located at + C:\MinGW\bin. These batch files merely invoke the MinGW make (GNU make) + passing some specific arguments along with the optional arguments the batch + file is invoked with. + + The current windows network built binaries will run on any system without + regard to whether or not WinPcap is installed, and will provide + Network functionality when WinPcap is available. + +------------------------------------------------------------------------------- + +Linux, {Free|Net|Open}BSD, OS/X, and Un*x notes: + +----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- + +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. All current Linux +distributions provide a libpcap-dev package which has the needed version +of libpcap and the required components to build applications using it. +If you are running an older Linux OS, you can download and build the required +library from www.tcpdump.org - see the comments at the top of Sim_ether.c +for details. + +----- 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. + 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, and any surrounding system network/bridge + setup must be done before running SIMH. However, once that is done + (possibly at system boot time), using the TAP devices can be done without + root privileges. + +Building on Linux, {Free|Net|Open}BSD, OS/X, Un*x: + + 1. Get/make/install the libpcap-dev package for your operating system. Sources: + All : http://www.tcpdump.org/ + Older versions of libpcap can be found, for various systems, at: + Linux : search for your variant on http://rpmfind.net + OS/X : Apple Developer's site? + + NOTE: The 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. + The OS provided libpcap-dev components will be prefereable + to a package built from www.tcpdump.org sources. This is + due to the fact that various OS supplied packages will + depend on the OS supplied libpcap. The improper build or + install of the www.tcpdump.org source package can conflict + with the OS provided one and break the OS provided + applications (i.e. tcpdump and/or wireshark) as well as + not working correctly for use by simh. + + 2. If you install the vendor supplied libpcap-dev package then the simh + makefile will automatically use the vendor supplied library without any + additional arguments. If you have downloaded and built libpcap from + www.tcpdump.org, then you can force its use during a build by typing + 'make USE_NETWORK=1' + + 3. Build it! + +------------------------------------------------------------------------------- + +OpenVMS Alpha and OpenVMS Integrety (IA64) notes: + 1. Ethernet support will only work on Alpha VMS 7.3-1 or later, which is + when required VCI promiscuous mode support was added. Hobbyists can + 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 + the inability of the software to set the PCAPVCM into promiscuous mode. + + 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 + 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 + build commands in descrip.mms. + + 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 + that creates an I/O structure (SCS, DECNET, TCP) must be running on the + adapter prior trying to connect with SIMH, or the host may crash. + The execlet is not written to create an I/O structure for the device. + +Building on OpenVMS Alpha and OpenVMS Integrety (IA64): + The current descrip.mms file will build simulators capable of using + Ethernet support with them automatically. These currently are: VAX, + VAX780, and PDP11. 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. + 3. Build the simulator(s) with MMS or MMK: + $ MMx {VAX,PDP11,PDP10, etc...} + +------------------------------------------------------------------------------- + +VAX simulator support: + +An OpenVMS VAX v7.2 system with DECNET Phase IV, MultiNet 4.4a, and LAT 5.3 has +been successfully run. Other testers have reported success booting NetBSD and +OpenVMS VAX 5.5-2 also. + + +PDP11 simulator support: + +An RT-11 v5.3 system with a freeware TCP/IP stack has been successfully run. +Other testers have reported that RSX with DECNET and the NetBSD operating +systems also work. RSTS/E v10.1 has preliminary support - RSTS/E boots and +enables the XH (XQ) device - DECNET and LAT software have not been tested. + +The XU module has been tested by a third party for basic packet functionality +under a modified RSX11M environment. I am unable to test it in-house until +someone can arrange to send me a disk image containing a stock RSTS/E or +RSX11M+ system image that also contains DECNET, LAT, and/or TCP/IP software. + +------------------------------------------------------------------------------- + +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: + + 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 +style of debugging, which required recompilation before debugging. + +------------------------------------------------------------------------------- + +Things planned for future releases: + 1. Full MOP implementation + +------------------------------------------------------------------------------- + +Things which I need help with: + 1. Information about Remote MOP processing + 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. + +------------------------------------------------------------------------------- + +Please send all patches, questions, feedback, clarifications, and help to: + david DOT hittner AT ngc DOT com + +Thanks, and Enjoy!! +Dave + + +=============================================================================== + Change Log +=============================================================================== + + 17-Nov-11 MP Added dynamic loading of libpcap on *nix platforms + 30-Oct-11 MP Added support for vde (Virtual Distributed Ethernet) networking + 29-Oct-11 MP Added support for integrated Tap networking interfaces on OSX + 17-Aug-11 RMS Fix from Sergey Oboguev relating to XU and XQ Auto Config and + vector assignments + 12-Aug-11 MP Cleaned up payload length determination + Fixed race condition detecting reflections when threaded + reading and writing is enabled + 07-Jul-11 MB VMS Pcap (from Mike Burke) + - Fixed Alpha issues + - Added OpenVMS Integrety support + 20-Apr-11 MP Fixed save/restore behavior + 12-Jan-11 DTH Added SHOW XU FILTERS modifier + 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 signaling for PDP11s (David Hittner) + 5. Added inline debugging support (David Hittner) + +------------------------------------------------------------------------------- + +26-Nov-03 Release: + 1. Added VMS support to Sim_Ether; created pcap-vms port (Anders Ahgren) + 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 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) + 9. Corrected interrupts on XQB device (David Hittner) + +------------------------------------------------------------------------------- + +05-Jun-03 Release: + 1. Added SET/SHOW XQ STATS (David Hittner) + 2. Added SHOW XQ FILTERS (David Hittner) + 3. Added ability to split rcv packets into multiple buffers (David Hittner) + 4. Added explicit runt & giant packet processing (David Hittner) + +------------------------------------------------------------------------------- + +30-May-03 Release: + 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 consistency (Mark Pizzolato) + +------------------------------------------------------------------------------- + +06-May-03 Release: + 1. Added second XQ controller (David Hittner) + 2. Added SIMH v3.0 compatibility (David Hittner) + 3. Removed SET ADDRESS functionality (David Hittner) + +------------------------------------------------------------------------------- + +10-Apr-03 Release: + 1. Added preliminary support for RSTS/E (David Hittner) + 2. Added PDP-11 bootrom load via CSR flags (David Hittner) + 3. Support for SPARC linux (Mark Pizzolato) + +------------------------------------------------------------------------------- + +11-Mar-03 Release: + 1. Added support for RT-11 TCP/IP + 2. Corrected interrupts (thanks to Tom Evans and Bob Supnik) + 3. Moved change log to the bottom of the readme file, cleaned up document + +------------------------------------------------------------------------------- + +16-Jan-03 Release: + 1. Added VMScluster support (thanks to Mark Pizzolato) + 2. Verified VAX remote boot functionality (>>>B XQA0) + 3. Added major performance enhancements (thanks to Mark Pizzolato again) + 4. Changed _DEBUG tracers to XQ_DEBUG and ETH_DEBUG + 5. Added local packet processing + 6. Added system id broadcast + +------------------------------------------------------------------------------- + +08-Nov-02 Release: + 1. Added USE_NETWORK conditional to Sim_Ether + 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/src/Makefile b/src/Makefile new file mode 100644 index 0000000..cea1554 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,57 @@ +# +# makefile for VAX MP for Linux, OS X and Unix +# +# To build VAX MP invoke as: +# +# make CONFIG=... USE_NETWORK={1|0} +# +# where possible values for CONFIG are: +# +# x86-dbg for 32-bit i86 debug build +# x86-rel 32-bit i86 release build +# x64-dbg 64-bit x86 debug build +# x64-rel 64-bit x86 release build +# +# To clean the target, invoke +# +# make CONFIG=... clean +# +# To rebuild dependency list before building, invoke as +# +# make CONFIG=... depend +# +# Suggested sequence for clean rebuild is: +# +# make CONFIG=... clean +# make CONFIG=... depend +# make CONFIG=... USE_NETWORK={1|0} +# +# or +# +# make CONFIG=... rebuild +# + +SHELL = /bin/sh +MK2 = $(MAKE) $(MAKEFLAGS) -f makefile2 --no-print-directory +VAX = vax_mp + +.PHONY : all clean rebuild depend $(VAX) check-config ; + +all: $(VAX) ; + +check-config: + @chmod a+x ./check-config.sh + @./check-config.sh CONFIG=$(CONFIG) USE_NETWORK=$(USE_NETWORK) + +$(VAX): check-config + @$(MK2) $(VAX) + +depend: check-config + @$(MK2) depend + +rebuild: clean + @$(MK2) depend + @$(MK2) $(VAX) + +clean: check-config + -rm -rf $(CONFIG) $(CONFIG)-depend.mk diff --git a/src/PDP11/pdp11_cr.cpp b/src/PDP11/pdp11_cr.cpp new file mode 100644 index 0000000..e5c248a --- /dev/null +++ b/src/PDP11/pdp11_cr.cpp @@ -0,0 +1,1295 @@ +/* pdp11_cr.c: CR/CM/CD-11 card reader simulator + + Copyright (c) 2005-2007, John A. Dundas III + Portions derived from work by Douglas W. Jones, jones@cs.uiowa.edu + Portions derived from work by 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 + THE AUTHOR 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 the Author shall + not be used in advertising or otherwise to promote the sale, use + or other dealings in this Software without prior written + authorization from the Author. + + ------------------------------------------------------------------------------ + + cr CR11/CD11 punched and mark sense card reader for SIMH + The CR11 controller is also compatible with the CM11-F, CME11, and CMS11. + + Information necessary to create this simulation was gathered from + a number of sources including: + + CR11 Card Reader System Manual, DEC-11-HCRB-D + http://www.bitsavers.org/pdf/dec/unibus/DEC-11-HCRB-D_CR11_Mar72.pdf + Various editions of the Peripherals Handbook + OpenVMS VAX Card Reader, Line Printer, and LPA11-K I/O User's + Reference Manual, AA-PVXGA-TE + http://h71000.www7.hp.com/DOC/73final/documentation/pdf/OVMS_VAX_CARD_LP_REF.pdf + OpenVMS System Manager's Manual, Volume 1: Essentials + http://h71000.www7.hp.com/DOC/732FINAL/aa-pv5mh-tk/aa-pv5mh-tk.PDF + CRDRIVER.LIS - CR11 Card Reader Driver, X-9, graciously made available + by HP + Various RSTS manuals + RT-11 Software Support Manual + RT-11 System Reference Manual, DEC-11-ORUGA-C-D + Professor Douglas W. Jones's web site: + http://www.cs.uiowa.edu/~jones/cards/ + Paul Mattes' x026 keypunch simulator + http://x3270.bgp.nu/x026.html + CD2SER.MAC - TOPS card reader driver source + http://pdp-10.trailing-edge.com/custsupcuspmar86_bb-x130b-sb/02/cd2ser.mac + + The Card Image format code and documentation is adapted from Prof. + Jones's site, with his permission. Please see his site for additional + documentation as well as the card image utilities referenced in + his documentation (cardmake, cardlist, etc.). + http://www.cs.uiowa.edu/~jones/cards/format.html + + Known limitations: + 1. Need a copy of the CR bootstrap (and some way to test it) + 2. Need a copy of the XXDP+ test deck + 3. No testing under RSX; volunteers needed + 4. No testing under Ultrix or Unix for PDP-11; volunteers needed + 5. No testing under Ultrix or Unix for VAX; volunteers needed + 6. The simulator implements a single controller/reader combination + + Operating System Notes + + RT-11 (and CTS-300) support one CR11 or CM11, but no CD11. + + VMS supports multiple CR11 controllers, but no CD11. + + RSTS/E supports either the CR11/CM11 or CD11 but not both in + the same SIL. It appears to support only one unit. + + For RSX there exists a CR/CM task handler. Is there a CD + handler? + + Don't have any information about Unix or Ultrix-11 yet. Same + for VAX Unices. + + TOPS: only the CD11 is supported, under the name CD20. + + Revision History: + + 01-Feb-07 RMS Added PDP-10 support + 12-May-06 JAD Modify the DEBUG code to use the SIMH DEBUG_x + macros. Modify the UNIT structure to include + the DEBUG bit. + Mark the trans[] array contents constant. + Make device data structures static and constant + as appropriate. + 18-Mar-05 JAD Slight optimization for blank punches recognizing + that blank is 0 in all character encodings. + 17-Mar-05 JAD Completely initialize ascii_code correctly. + Define the end of deck punch code separately from + the cardcode.i file. + Make initTranslation() set a pointer to the correct + punch code table to use. Modify card read functions + to use this table pointer. + 16-Mar-05 JAD Make certain switches passed to the ATTACH command + are valid; return error on any others. + Make default unit wait time compatible with default + device specification. + Implement SET TRANSLATION=value. Still need to + modify the H2ASCII table used for text files; + currently hard-coded to 029. + 24-Feb-05 JAD Allow the maintenance bits in CRM to clear as + well as set status bits. Not sure this is the + correct behavior, though, without more documentation. + Catch three more places to spin down the blower + correctly. + Zero the CDDB and CRM at INIT. + 17-Feb-05 JAD When the hopper empties, a pick check should + be generated 300ms later. They are simultaneous + for now. + Make sure readColumnBinary() generates a complete + EOF card. + 08-Feb-05 JAD Replace blowerWait with different times for blower + spin up and down. + 06-Feb-05 JAD After DETACH: mark CD offline, set appropriate + blower state. + Make sure unit wait time is recalculated every + time cpm is set. + 04-Feb-05 JAD Better tracking of blower state throughout driver. + Make sure IE gets cleared for CR at INIT. + Normalize error response in read routines. + Finish condition handling for column binary. + 02-Feb-05 JAD Remove Qbus support; Unibus only. + Support ATTACH switches: + A - ASCII, B - column binary, I - Card Image + If none given, check for .TXT or .CBN; if none, + examine file for magic header. + Finer granularity to blower state. Expose this + variable to examine/deposit from SIMH. + Preliminary implementation of support for + column binary format. + 24-Jan-05 JAD Make AUTOEOF work as a surrogate for the EOF + button of a CD11 reader. May need to separate + this later, though. + Partial implementation of DATAERR for CD11. + Implement the Rev. J mods (as best I understand + them) to the CD11 affecting the CDDB used as a + second status register. + 23-Jan-05 JAD Preliminary clean-up of CD state transitions. + Tested with RSTS/E (V9.1-05). + 22-Jan-05 JAD Finish CR state transitions; should be close now. + Tested with RSTS/E (V9.1-05), RT-11 (V5.3), and + VAX/VMS (V7.2). + 19-Jan-05 JAD Add bounds to the RATE command; also default and + help a la the XQ driver. + Improved handling of empty files. + 17-Jan-05 JAD Add the CR maintenance register. + 16-Jan-05 JAD Add preliminary CD11 support. + Simulate the STOP and RESET switches. + 14-Jan-05 JAD Add the ability to automatically generate an 'EOF' + card recognized by DEC operating systems when + reading ASCII files. + 08-Jan-05 JAD Original creation and testing +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#include "pdp10_defs.h" +extern int32 int_req; +#define DFLT_DIS (DEV_DIS) +#define DFLT_CR11 (0) /* CD11 only */ +#define DFLT_CPM 1000 + +#elif defined (VM_VAX) /* VAX version */ +#include "sim_defs.h" +#include "vax_defs.h" +#define DFLT_DIS (0) +#define DFLT_CR11 (UNIT_CR11) +#define DFLT_CPM 285 + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#define DFLT_DIS (0) +#define DFLT_CR11 (UNIT_CR11) +#define DFLT_CPM 285 +#endif + +extern SMP_FILE *sim_deb; /* sim_console.c */ + +/* create a int32 constant from four characters */ +#define I4C(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) +#define I4C_CBN I4C ('C','B','N',' ') +#define I4C_H80 I4C ('H','8','0',' ') +#define I4C_H82 I4C ('H','8','2',' ') +#define I4C_H40 I4C ('H','4','0',' ') + +#define UNIT_V_CR11 (UNIT_V_UF + 0) +#define UNIT_CR11 (1u << UNIT_V_CR11) +#define UNIT_V_AUTOEOF (UNIT_V_UF + 1) +#define UNIT_AUTOEOF (1u << UNIT_V_AUTOEOF) + +#include +#define ERROR (00404) +#include "pdp11_cr_dat.h" +#define PUNCH_EOD (07417) +#define PUNCH_SPACE (0) /* same for all encodings */ + +/* CR */ +/* also use CSR_ERR, CSR_IE, and CSR_GO */ +#define CRCSR_V_CRDDONE 14 /* card done */ +#define CRCSR_V_SUPPLY 13 /* supply error */ +#define CRCSR_V_RDCHK 12 /* reader check */ +#define CRCSR_V_TIMERR 11 /* timing error */ +#define CRCSR_V_ONLINE 10 /* on line */ +#define CRCSR_V_BUSY 9 /* busy reading */ +#define CRCSR_V_OFFLINE 8 /* off line AKA READY? */ +#define CRCSR_V_COLRDY 7 /* column ready */ +#define CRCSR_V_EJECT 1 /* ignore card */ + +#define CRCSR_CRDDONE (1u << CRCSR_V_CRDDONE) +#define CRCSR_SUPPLY (1u << CRCSR_V_SUPPLY) +#define CRCSR_RDCHK (1u << CRCSR_V_RDCHK) +#define CRCSR_TIMERR (1u << CRCSR_V_TIMERR) +#define CRCSR_ONLINE (1u << CRCSR_V_ONLINE) +#define CRCSR_BUSY (1u << CRCSR_V_BUSY) +#define CRCSR_OFFLINE (1u << CRCSR_V_OFFLINE) +#define CRCSR_COLRDY (1u << CRCSR_V_COLRDY) +#define CRCSR_EJECT (1u << CRCSR_V_EJECT) + +#define CRCSR_IMP (CSR_ERR | CRCSR_CRDDONE | CRCSR_SUPPLY | \ + CRCSR_RDCHK | CRCSR_TIMERR | CRCSR_ONLINE | \ + CRCSR_BUSY | CRCSR_OFFLINE | CRCSR_COLRDY | \ + CSR_IE | CRCSR_EJECT) +#define CRCSR_RW (CSR_IE | CRCSR_EJECT | CSR_GO) /* read/write */ + +#define CRM_V_MAINT 15 /* enable maint funct */ +#define CRM_V_BUSY 14 +#define CRM_V_READY 13 +#define CRM_V_HOPPER 12 + +#define CRM_MAINT (1u << CRM_V_MAINT) +#define CRM_BUSY (1u << CRM_V_BUSY) +#define CRM_READY (1u << CRM_V_READY) +#define CRM_HOPPER (1u << CRM_V_HOPPER) + +/* CD */ +/* also use CSR_ERR, CSR_IE, and CSR_GO */ +#define CDCSR_V_RDRCHK 14 /* reader check */ +#define CDCSR_V_EOF 13 /* CD11-E EOF button */ +#define CDCSR_V_OFFLINE 12 /* off line */ +#define CDCSR_V_DATAERR 11 /* data error */ +#define CDCSR_V_LATE 10 /* data late */ +#define CDCSR_V_NXM 9 /* non-existent memory */ +#define CDCSR_V_PWRCLR 8 /* power clear */ +#define CDCSR_V_RDY 7 /* ready */ +#define CDCSR_V_XBA17 5 +#define CDCSR_V_XBA16 4 +#define CDCSR_V_ONLINE 3 /* on line transition */ +#define CDCSR_V_HOPPER 2 /* hopper check */ +#define CDCSR_V_PACK 1 /* data packing */ + +#define CDCSR_RDRCHK (1u << CDCSR_V_RDRCHK) +#define CDCSR_EOF (1u << CDCSR_V_EOF) +#define CDCSR_OFFLINE (1u << CDCSR_V_OFFLINE) +#define CDCSR_DATAERR (1u << CDCSR_V_DATAERR) +#define CDCSR_LATE (1u << CDCSR_V_LATE) +#define CDCSR_NXM (1u << CDCSR_V_NXM) +#define CDCSR_PWRCLR (1u << CDCSR_V_PWRCLR) +#define CDCSR_RDY (1u << CDCSR_V_RDY) +#define CDCSR_XBA17 (1u << CDCSR_V_XBA17) +#define CDCSR_XBA16 (1u << CDCSR_V_XBA16) +#define CDCSR_ONLINE (1u << CDCSR_V_ONLINE) +#define CDCSR_HOPPER (1u << CDCSR_V_HOPPER) +#define CDCSR_PACK (1u << CDCSR_V_PACK) + +#define CDCSR_IMP (CSR_ERR | CDCSR_RDRCHK | CDCSR_EOF | CDCSR_OFFLINE | \ + CDCSR_DATAERR | CDCSR_LATE | CDCSR_NXM | \ + CDCSR_PWRCLR | CDCSR_RDY | CSR_IE | \ + CDCSR_XBA17 | CDCSR_XBA16 | CDCSR_ONLINE | \ + CDCSR_HOPPER | CDCSR_PACK | CSR_GO) + +#define CDCSR_RW (CDCSR_PWRCLR | CSR_IE | CDCSR_XBA17 | CDCSR_XBA16 | \ + CDCSR_PACK | CSR_GO) + +/* Blower state values */ +#define BLOW_OFF (0) /* steady state off */ +#define BLOW_START (1) /* starting up */ +#define BLOW_ON (2) /* steady state on */ +#define BLOW_STOP (3) /* shutting down */ + +/* Card Reader state */ +static char *cardFormat = "unknown"; +static t_bool (*readRtn)(SMP_FILE *, int16 *, char *, char *); +static char ascii_code[4096]; /* 2^12 possible values */ +static int currCol; /* current column when reading */ +static int colStart; /* starting column */ +static int colEnd; /* ending column */ +static int table = 3; /* character translation table */ +static const int *codeTbl = o29_code; /* punch translation table */ +static int32 blowerState = BLOW_OFF; /* reader vacuum/blower motor */ +static int32 spinUp = 3000; /* blower spin-up time: 3 seconds */ +static int32 spinDown = 2000; /* blower spin-down time: 2 seconds */ +static t_bool EOFcard = FALSE; /* played special card yet? */ +static int32 cpm = DFLT_CPM; /* reader rate: cards per minute */ +/* card image in various formats */ +static int16 hcard[82]; /* Hollerith format */ +static char ccard[82]; /* DEC compressed format */ +static char acard[82]; /* ASCII format */ +/* CR/CM registers */ +static int32 crs = 0; /* control/status */ +static int32 crb1 = 0; /* 12-bit Hollerith characters */ +static int32 crb2 = 0; /* 8-bit compressed characters */ +static int32 crm = 0; /* CMS maintenance register */ +/* CD registers */ +static int32 cdst = 0; /* control/status */ +static int32 cdcc = 0; /* column count */ +static int32 cdba = 0; /* current address, low 16 bits */ +static int32 cddb = 0; /* data, 2nd status */ + +AUTO_INIT_DEVLOCK(cr_lock); + +/* forward references */ +static void setupCardFile (UNIT *, int32); +t_stat cr_rd (int32 *, int32, int32); +t_stat cr_wr (int32, int32, int32); +t_stat cr_svc (RUN_SVC_DECL, UNIT *); +t_stat cr_reset (DEVICE *); +t_stat cr_attach (UNIT *, char *); +t_stat cr_detach (UNIT *); +t_stat cr_set_type (UNIT *, int32, char *, void *); +t_stat cr_show_format (SMP_FILE *, UNIT *, int32, void *); +t_stat cr_set_rate (UNIT *, int32, char *, void *); +t_stat cr_show_rate (SMP_FILE *, UNIT *, int32, void *); +t_stat cr_set_reset (UNIT *, int32, char *, void *); +t_stat cr_set_stop (UNIT *, int32, char *, void *); +t_stat cr_set_trans (UNIT *, int32, char*, void *); +t_stat cr_show_trans (SMP_FILE *, UNIT *, int32, void *); + +/* CR data structures + + cr_dib CR device information block + cr_unit CR unit descriptor + cr_reg CR register list + cr_mod CR modifier table + cr_dev CR device descriptor +*/ + +static DIB cr_dib = { IOBA_CR, IOLN_CR, &cr_rd, &cr_wr, + 1, IVCL (CR), VEC_CR, { NULL } }; + +static UNIT cr_unit UDATA_SINGLE_WAIT ( + &cr_svc, + UNIT_ATTABLE+UNIT_SEQ+UNIT_ROABLE+UNIT_DISABLE+DFLT_CR11+UNIT_AUTOEOF, 0, + (60 * 1000) / DFLT_CPM ); +UNIT_TABLE_SINGLE(cr_unit); + +static const REG cr_reg[] = { + { GRDATA_GBL (BUF, cr_unit.buf, DEV_RDX, 8, 0) }, + { GRDATA_GBL (CRS, crs, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CRB1, crb1, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CRB2, crb2, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CRM, crm, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CDST, cdst, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CDCC, cdcc, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CDBA, cdba, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CDDB, cddb, DEV_RDX, 16, 0) }, + { GRDATA_GBL (BLOWER, blowerState, DEV_RDX, 2, 0) }, + { IRDATA_DEV (INT, IVCL (CR)) }, + { FLDATA_GBL (ERR, crs, CSR_V_ERR) }, + { FLDATA_GBL (IE, crs, CSR_V_IE) }, + { DRDATA_GBL (POS, cr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA_GBL (TIME, cr_unit.wait, 24), PV_LEFT }, + { GRDATA_GBL (DEVADDR, cr_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA_GBL (DEVVEC, cr_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } }; + +static const MTAB cr_mod[] = { +#if defined (VM_PDP11) + { UNIT_CR11, UNIT_CR11, "CR11", "CR11", &cr_set_type }, + { UNIT_CR11, 0, "CD11", "CD11", &cr_set_type }, +#else + { UNIT_CR11, UNIT_CR11, "CR11", NULL }, + { UNIT_CR11, 0, "CD11", NULL }, +#endif + { UNIT_AUTOEOF, UNIT_AUTOEOF, "auto EOF", "AUTOEOF", NULL }, + { UNIT_AUTOEOF, 0, "no auto EOF", "NOAUTOEOF", NULL }, + /* card reader RESET switch */ + { MTAB_XTD|MTAB_VDV, 0, NULL, "RESET", + &cr_set_reset, NULL, NULL }, + /* card reader STOP switch */ + { MTAB_XTD|MTAB_VDV, 0, NULL, "STOP", + &cr_set_stop, NULL, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", NULL, + NULL, &cr_show_format, NULL }, + { MTAB_XTD|MTAB_VDV, 006, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "RATE", "RATE={DEFAULT|200..1200}", + &cr_set_rate, &cr_show_rate, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "TRANSLATION", + "TRANSLATION={DEFAULT|026|026FTN|029|EBCDIC}", + &cr_set_trans, &cr_show_trans, NULL }, + { 0 } }; + +DEVICE cr_dev = { + "CR", cr_unit_table, (REG *) &cr_reg, (MTAB *) &cr_mod, + 1, 10, 31, 1, DEV_RDX, 8, + NULL, NULL, &cr_reset, + NULL, &cr_attach, &cr_detach, + &cr_dib, DEV_DISABLE | DFLT_DIS | DEV_UBUS | DEV_DEBUG }; + +/* Utility routines */ + +/* +These functions read a "card" from a virtual deck file (passed in +fp) and fill in three arrays. The first array 'hcard' contains the +12-bit binary image of the punch in each column; the second array +'ccard' contains the 8-bit DEC encoded representation of the +corresponding column; the third array 'acard' contains the ASCII +representation (if possible) of the character. The routines return +TRUE if a card was read (possibly with errors) and FALSE if the +"hopper is empty" (EOF) or fatal file errors prevented any portion +of a card from being read. + +Errors other than EOF are signaled out of band in the controller +state variables. Possible errors are data in columns 0 or 81 +(signalled as read check; currently these columns are ignored), or +any file errors (signalled as motion check). + +Might rethink this. Should probably treat file errors as "pick +check". Retry 3 times. After that, give up with error. + +*/ + +static t_bool readCardImage ( SMP_FILE *fp, + int16 *hcard, + char *ccard, + char *acard ) +{ + int c1, c2, c3, col; + + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "readCardImage pos %d\n", (int) ftell (fp)); + /* get card header bytes */ + c1 = fgetc (fp); + c2 = fgetc (fp); + c3 = fgetc (fp); + cr_unit.pos = ftell (fp); + /* check for EOF */ + if (c1 == EOF) { + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "hopper empty\n"); + if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) { + EOFcard = TRUE; + for (col = 1; col <= 8; col++) { + hcard[col] = PUNCH_EOD; + ccard[col] = h2c_code[PUNCH_EOD]; + acard[col] = ' '; + } + while (col <= colEnd) { + hcard[col] = PUNCH_SPACE; + ccard[col] = PUNCH_SPACE; + acard[col] = ' '; + col++; + } + return (TRUE); + } + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; + crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE); + cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; + if (cr_unit.flags & UNIT_AUTOEOF) + cdst |= CDCSR_EOF; + blowerState = BLOW_STOP; + return (FALSE); + } + /* check for valid header */ + if ((c2 == EOF) || (c3 == EOF) || ((c1 & 0x80) == 0) || + ((c2 & 0x80) == 0) || ((c3 & 0x80) == 0)) { + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "header error\n"); + /* unexpected EOF or format problems */ + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; + crs &= ~CRCSR_ONLINE; + cdst |= CSR_ERR | CDCSR_RDRCHK; + blowerState = BLOW_STOP; + return (FALSE); + } + assert (colStart < colEnd); + assert (colStart >= 0); + assert (colEnd <= 81); + for (col = colStart; col < colEnd; ) { + int16 i; + /* get 3 bytes */ + c1 = fgetc (fp); + c2 = fgetc (fp); + c3 = fgetc (fp); + cr_unit.pos = ftell (fp); + if (ferror (fp) || feof (fp)) { + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "file error\n"); +/* signal error; unexpected EOF, format problems, or file error(s) */ + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; + crs &= ~CRCSR_ONLINE; + cdst |= CSR_ERR | CDCSR_RDRCHK; + blowerState = BLOW_STOP; + return (FALSE); + } + /* convert to 2 columns */ + i = ((c1 << 4) | ( c2 >> 4)) & 0xFFF; + hcard[col] = i; + ccard[col] = h2c_code[i]; + acard[col] = ascii_code[i]; + col++; + + i = (((c2 & 017) << 8) | c3) & 0xFFF; + hcard[col] = i; + ccard[col] = h2c_code[i]; + acard[col] = ascii_code[i]; + col++; + } + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "successfully loaded card\n"); + return (TRUE); +} + +static t_bool readColumnBinary ( SMP_FILE *fp, + int16 *hcard, + char *ccard, + char *acard ) +{ + int col; + + for (col = colStart; col <= colEnd; col++) { + int16 i; + i = fgetc (fp) & 077; + i |= ((fgetc (fp) & 077) << 6); + cr_unit.pos = ftell (fp); + if (feof (fp)) { + if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) { + EOFcard = TRUE; + for (col = 1; col <= 8; col++) { + hcard[col] = PUNCH_EOD; + ccard[col] = h2c_code[PUNCH_EOD]; + acard[col] = ' '; + } + while (col <= colEnd) { + hcard[col] = PUNCH_SPACE; + ccard[col] = PUNCH_SPACE; + acard[col] = ' '; + col++; + } + return (TRUE); + } + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | + CRCSR_OFFLINE; + crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE); + cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; + if (cr_unit.flags & UNIT_AUTOEOF) + cdst |= CDCSR_EOF; + blowerState = BLOW_STOP; + return (FALSE); + } + if (ferror (fp)) { + /* signal error */ + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; + crs &= ~CRCSR_ONLINE; + cdst |= CSR_ERR | CDCSR_RDRCHK; + blowerState = BLOW_STOP; + return (FALSE); + } + hcard[col] = i; + ccard[col] = h2c_code[i]; + acard[col] = ascii_code[i]; + } + return (TRUE); +} + +/* + +Should this routine perform special handling of non-printable, +(e.g., control) characters or characters that have no encoded +representation? + +*/ + +static t_bool readCardASCII ( SMP_FILE *fp, + int16 *hcard, + char *ccard, + char *acard ) +{ + int c = 0, col; + + assert (colStart < colEnd); + assert (colStart >= 1); + assert (colEnd <= 80); + + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "readCardASCII\n"); + for (col = colStart; col <= colEnd; ) { + switch (c = fgetc (fp)) { + case EOF: + if (ferror (fp)) { + /* signal error */ + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; + crs &= ~CRCSR_ONLINE; + cdst |= CSR_ERR | CDCSR_RDRCHK; + blowerState = BLOW_STOP; + cr_unit.pos = ftell (fp); + return (FALSE); + } + if (col == colStart) { + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "hopper empty\n"); + if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) { + EOFcard = TRUE; + for (col = 1; col <= 8; col++) { + hcard[col] = PUNCH_EOD; + ccard[col] = h2c_code[PUNCH_EOD]; + acard[col] = ' '; + } + c = '\n'; + goto fill_card; + } + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; + crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE); + cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; + if (cr_unit.flags & UNIT_AUTOEOF) + cdst |= CDCSR_EOF; + blowerState = BLOW_STOP; + cr_unit.pos = ftell (fp); + return (FALSE); + } + /* fall through */ + case '\r': + case '\n': + fill_card: + while (col <= colEnd) { + hcard[col] = PUNCH_SPACE; + ccard[col] = PUNCH_SPACE; + acard[col] = ' '; + col++; + } + break; + case '\t': + do { + hcard[col] = PUNCH_SPACE; + ccard[col] = PUNCH_SPACE; + acard[col] = ' '; + col++; + } while (((col & 07) != 1) && (col <= colEnd)); + break; + default: + hcard[col] = codeTbl[c & 0177]; + /* check for unrepresentable ASCII characters */ + if (hcard[col] == ERROR) { + cdst |= CDCSR_DATAERR; + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, + "error character at column %d\n", + col); + } + ccard[col] = h2c_code[hcard[col]]; + acard[col] = c; + col++; + break; + } + } + /* silently truncate/flush long lines, or flag over-length card? */ + if (c != '\n') { + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "truncating card\n"); + do c = fgetc (fp); + while ((c != EOF) && (c != '\n') && (c != '\r')); + } + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "successfully loaded card\n"); + cr_unit.pos = ftell (fp); + return (TRUE); +} + +/* + +Initialize the binary translation table. Generally called when a +new deck is attached but could be set manually as well. + +*/ + +static void initTranslation (void) +{ + int32 i; + + memset (ascii_code, '~', sizeof (ascii_code)); + switch (table) { + case 1: + codeTbl = o26_comm_code; + for (i = ' '; i < '`'; i++) + ascii_code[o26_comm_code[i]] = i; + break; + case 2: + codeTbl = o26_ftn_code; + for (i = ' '; i < '`'; i++) + ascii_code[o26_ftn_code[i]] = i; + break; + case 3: + codeTbl = o29_code; + for (i = ' '; i < '`'; i++) + ascii_code[o29_code[i]] = i; + break; + case 4: + codeTbl = EBCDIC_code; + for (i = 0; i < 0177; i++) + ascii_code[EBCDIC_code[i]] = i; + break; + default: + /* can't happen */ + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, + "bad CR translation initialization value\n"); + break; + } +} + +/* + +Examine the command switches, file extension, and virtual card deck +file to determine the format. Set up the global variables +appropriately. Rewind ASCII files to the beginning + +*/ + +static void setupCardFile ( UNIT *uptr, + int32 switches ) +{ + int32 i; + + if (switches & SWMASK ('A')) + i = 0; + else if (switches & SWMASK ('B')) + i = I4C_CBN; + else if (switches & SWMASK ('I')) + goto read_header; + else if (match_ext (uptr->filename, "TXT")) + i = 0; + else if (match_ext (uptr->filename, "CBN")) + i = I4C_CBN; + else { +read_header: + /* look for card image magic file number */ + i = fgetc (uptr->fileref); + i = (i << 8) | fgetc (uptr->fileref); + i = (i << 8) | fgetc (uptr->fileref); + i = (i << 8) | ' '; + } + switch (i) { + case I4C_H80: + colStart = 1; + colEnd = 80; + cardFormat = "card image"; + readRtn = readCardImage; + break; + case I4C_H82: + colStart = 0; + colEnd = 81; + cardFormat = "card image"; + readRtn = readCardImage; + break; + case I4C_H40: + colStart = 1; + colEnd = 40; + cardFormat = "card image"; + readRtn = readCardImage; + break; + case I4C_CBN: + colStart = 1; + colEnd = 80; + cardFormat = "column binary"; + readRtn = readColumnBinary; + break; + default: + colStart = 1; + colEnd = 80; + cardFormat = "ASCII"; + readRtn = readCardASCII; + fseek (uptr->fileref, 0L, SEEK_SET); + break; + } + initTranslation (); + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "colStart = %d, colEnd = %d\n", + colStart, colEnd); + cr_unit.pos = ftell (uptr->fileref); +} + +/* Card reader routines + + cr_rd I/O page read + cr_wr I/O page write + cr_svc process event (reader ready) + cr_reset process reset + cr_attach process attach + cr_detach process detach +*/ + +t_stat cr_rd ( int32 *data, + int32 PA, + int32 access ) +{ + AUTO_LOCK(cr_lock); + + switch ((PA >> 1) & 03) { + case 0: /* CSR */ + if (cdst & (077000)) + cdst |= CSR_ERR; + else + cdst &= ~CSR_ERR; + *data = (cr_unit.flags & UNIT_CR11) ? + crs & CRCSR_IMP : cdst & CDCSR_IMP; + /* CR: if error removed, clear 15, 14, 11, 10 */ + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_rd crs %06o cdst %06o\n", + crs, cdst); + break; + case 1: + *data = (cr_unit.flags & UNIT_CR11) ? crb1 : cdcc; + /* Does crb1 clear after read? Implied by VMS driver. */ + crb1 = 0; + crs &= ~CRCSR_COLRDY; + if (DEBUG_PRS (cr_dev)) { + if (cr_unit.flags & UNIT_CR11) + fprintf (sim_deb, "cr_rd crb1 %06o '%c' %d\n", + crb1, cr_unit.buf, cr_unit.buf); + else + fprintf (sim_deb, "cr_rd cdcc %06o\n", cdcc); + } + break; + case 2: + *data = (cr_unit.flags & UNIT_CR11) ? crb2 : cdba; + crb2 = 0; /* see note for crb1 */ + crs &= ~CRCSR_COLRDY; + if (DEBUG_PRS (cr_dev)) { + if (cr_unit.flags & UNIT_CR11) + fprintf (sim_deb, "cr_rd crb2 %06o\n", crb2); + else + fprintf (sim_deb, "\r\ncr_rd cdba %06o\n", cdba); + } + break; + case 3: + default: + if (cr_unit.flags & UNIT_CR11) + *data = crm; + else + *data = 0100000 | (cdst & CDCSR_RDRCHK) | + (cdst & CDCSR_OFFLINE) ? + cddb & 0777 : 0777; + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_rd crm %06o cddb %06o data %06o\n", + crm, cddb, *data); + break; + } + return (SCPE_OK); +} + +t_stat cr_wr ( int32 data, + int32 PA, + int32 access ) +{ + AUTO_LOCK(cr_lock); + + switch ((PA >> 1) & 03) { + case 0: + if (cr_unit.flags & UNIT_CR11) { + /* ignore high-byte writes */ + if (PA & 1) + break; + /* fixup data for low byte write */ + if (access == WRITEB) + data = (crs & ~0377) | (data & 0377); + if (!(data & CSR_IE)) + CLR_INT (CR); + crs = (crs & ~CRCSR_RW) | (data & CRCSR_RW); + crs &= ~(CSR_ERR | CRCSR_CRDDONE | CRCSR_TIMERR); + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_wr data %06o crs %06o\n", + data, crs); + if (data & CSR_GO) { + if (blowerState != BLOW_ON) { + sim_activate (&cr_unit, spinUp); + blowerState = BLOW_START; + } else + sim_activate (&cr_unit, cr_unit.wait); + } + } else { + if (data & CDCSR_PWRCLR) { + CLR_INT (CR); + sim_cancel (&cr_unit); + cdst &= ~(CDCSR_RDRCHK |CDCSR_OFFLINE | + CDCSR_RDY | CDCSR_HOPPER); + cdst |= CDCSR_RDY; + cdcc = 0; + cdba = 0; + break; + } + if (!(data & CSR_IE)) + CLR_INT (CR); + cdst = (cdst & ~CDCSR_RW) | (data & CDCSR_RW); + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_wr data %06o cdst %06o\n", + data, cdst); + if (data & CSR_GO) { + if (blowerState != BLOW_ON) { + sim_activate (&cr_unit, spinUp); + blowerState = BLOW_START; + } else + sim_activate (&cr_unit, cr_unit.wait); + } + } + break; + case 1: + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_wr cdcc %06o\n", data); + if (cr_unit.flags & UNIT_CR11) + break; + cdcc = data & 0177777; + break; + case 2: + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_wr crba %06o\n", data); + if (cr_unit.flags & UNIT_CR11) + break; + cdba = data & 0177777; + break; + case 3: + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_wr cddb/crm %06o\n", data); + /* ignore writes to cddb */ + if (!(cr_unit.flags & UNIT_CR11)) + break; + /* fixup data for byte writes and read-modify-write */ + if (access == WRITEB) + data = (PA & 1) ? + (crm & 0377) | (data << 8) : + (crm & ~0377) | (data & 0377); + crm = data & 0177777; + /* not 100% certain how these work */ + if (!(crm & CRM_MAINT)) + break; + crs = (crm & CRM_BUSY) ? + (crs | CRCSR_BUSY) : (crs & ~CRCSR_BUSY); + crs = (crm & CRM_READY) ? + (crs | CRCSR_OFFLINE) : (crs & ~CRCSR_OFFLINE); + crs = (crm & CRM_HOPPER) ? + (crs | CRCSR_SUPPLY | CRCSR_RDCHK) : + (crs & ~(CRCSR_SUPPLY | CRCSR_RDCHK)); + crb1 = crm & 07777; /* load low 12 bits */ + break; + default: + /* can't happen */ + break; + } + return (SCPE_OK); +} + +/* +Enter the service routine once for each column read from the card. +CR state bits drive this primarily (see _BUSY and _CRDDONE). However, +when in CD mode, also execute one column of DMA input. + +*/ + +t_stat cr_svc (RUN_SVC_DECL, UNIT *uptr) +{ + uint32 pa; + uint8 c; + uint16 w; + + AUTO_LOCK(cr_lock); + RUN_SVC_CHECK_CANCELLED(uptr); + + if (blowerState == BLOW_STOP) { + blowerState = BLOW_OFF; + return (SCPE_OK); + } + if (blowerState == BLOW_START) + blowerState = BLOW_ON; + /* (almost) anything we do now will cause a CR interrupt */ + if (crs & CSR_IE) + SET_INT (CR); + if (!(uptr->flags & UNIT_ATT) || (crs & CSR_ERR) || (cdst & CSR_ERR)) + return (SCPE_OK); + if ((crs & CRCSR_BUSY) && (currCol > colEnd)) { + crs &= ~(CRCSR_BUSY | CSR_GO | CRCSR_COLRDY); + crs |= CRCSR_CRDDONE; + if (cdst & (CDCSR_DATAERR | CDCSR_LATE | CDCSR_NXM)) + cdst |= CSR_ERR; + if (cdst & CSR_IE) + SET_INT (CR); + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_svc card done\n"); + return (SCPE_OK); + } + if (!(crs & CRCSR_BUSY)) { + /* try to read a card */ + /* crs &= ~CRCSR_CRDDONE; */ + if (!readRtn (uptr->fileref, hcard, ccard, acard)) { + sim_activate (uptr, spinDown); + return (SCPE_OK); + } + currCol = colStart; + crs |= CRCSR_BUSY; /* indicate reader busy */ + } + /* check for overrun (timing error) */ + if ((uptr->flags & UNIT_CR11) && (crs & CRCSR_COLRDY)) + crs |= CSR_ERR | CRCSR_TIMERR; + crb1 = hcard[currCol] & 07777; + crb2 = ccard[currCol] & 0377; + uptr->buf = acard[currCol] & 0377; /* helpful for debugging */ + if (!(uptr->flags & UNIT_CR11)) { + pa = cdba | ((cdst & 060) << 12); +/* +The implementation of _NXM here is not quite the same as I interpret +the (limited) documentaiton I have to indicate. However the effect +should be similar. Documentation indicates that once _NXM is set, +further NPR requests are inhibited though the card is allowed to +read until completion. This implies that CDBA and the XBA bits are +incremented accordingly, even though no data transfer occurs. This +code detects and flags the NXM condition but allows attempts at +subsequent memory writes, thus insuring the address registers are +incremented properly. If this causes problems, I'll fix it. +*/ + if (cdst & CDCSR_PACK) { + c = cddb = ccard[currCol] & 0377; + if (Map_WriteB (RUN_PASS, pa, 1, &c)) + cdst |= CDCSR_NXM; + pa = (pa + 1) & 0777777; + } else { + w = cddb = hcard[currCol] & 07777; + if (Map_WriteW (RUN_PASS, pa, 2, &w)) + cdst |= CDCSR_NXM; + pa = (pa + 2) & 0777777; + } + cdba = pa & 0177777; + cdst = (cdst & ~(CDCSR_XBA17|CDCSR_XBA16)) | + ((pa & 0600000) >> 12); + cdcc = (cdcc + 1) & 0177777; +#if 0 + if (!(cdst & CSR_IE) && !(crs & CRCSR_CRDDONE)) + CLR_INT (CR); +#endif + } + currCol++; /* advance the column counter */ + if (!(crs & CRCSR_EJECT)) + crs |= CRCSR_COLRDY; + else + CLR_INT (CR); + sim_activate (uptr, uptr->wait); + return (SCPE_OK); +} + +t_stat cr_reset ( DEVICE *dptr ) +{ + AUTO_LOCK(cr_lock); + + sim_bind_devunits_lock(&cr_dev, cr_lock); + + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_reset\n"); + cr_unit.buf = 0; + currCol = 1; + crs &= ~(CSR_ERR|CRCSR_CRDDONE|CRCSR_TIMERR|CRCSR_ONLINE|CRCSR_BUSY| + CRCSR_COLRDY|CSR_IE|CRCSR_EJECT|CSR_GO); + crb1 = 0; + crb2 = 0; + crm = 0; + cdst &= ~(CSR_ERR|CDCSR_RDRCHK|CDCSR_EOF|CDCSR_DATAERR|CDCSR_LATE| + CDCSR_NXM|CSR_IE|CDCSR_XBA17|CDCSR_XBA16|CDCSR_ONLINE| + CDCSR_PACK|CSR_GO); + cdst |= CDCSR_RDY; + cdcc = 0; + cdba = 0; + cddb = 0; + if ((cr_unit.flags & UNIT_ATT) && !feof (cr_unit.fileref)) { + crs |= CRCSR_ONLINE; /* non-standard */ + crs &= ~(CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE); + cdst &= ~(CDCSR_RDRCHK | CDCSR_HOPPER); + } else { + cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; + crs = CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; + } + sim_cancel (&cr_unit); /* deactivate unit */ + if (blowerState != BLOW_OFF) { + blowerState = BLOW_STOP; + sim_activate (&cr_unit, spinDown); + } + EOFcard = FALSE; + CLR_INT (CR); + /* TBD: flush current card */ + /* init uptr->wait ? */ + return (SCPE_OK); +} + +/* +Handle the interface status and SIMH portion of the ATTACH. Another +routine is used to evaluate the file and initialize other state +globals correctly. +*/ + +#define MASK (SWMASK('A')|SWMASK('B')|SWMASK('I')|SWMASK('R')) + +t_stat cr_attach ( UNIT *uptr, + char *cptr ) +{ + t_stat reason; + extern int32 sim_switches; + + if (sim_switches & ~MASK) + return (SCPE_INVSW); + /* file must previously exist; kludge */ + sim_switches |= SWMASK ('R'); + reason = attach_unit (uptr, cptr); + if (!(uptr->flags & UNIT_ATT)) { + crs &= ~CRCSR_ONLINE; + crs |= CSR_ERR | CRCSR_OFFLINE | CRCSR_RDCHK | CRCSR_SUPPLY; + cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; + } else { + setupCardFile (uptr, sim_switches); + crs |= CRCSR_ONLINE; + crs &= ~(CSR_ERR | CRCSR_OFFLINE | CRCSR_RDCHK | CRCSR_SUPPLY); + cdst &= ~(CDCSR_RDRCHK | CDCSR_HOPPER); + EOFcard = FALSE; + } + return (reason); +} + +t_stat cr_detach ( UNIT *uptr ) +{ + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; + /* interrupt? */ + crs &= ~CRCSR_ONLINE; + cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER | CDCSR_OFFLINE; + cardFormat = "unknown"; + if (blowerState != BLOW_OFF) { + blowerState = BLOW_STOP; + sim_activate (uptr, spinDown); + } + return (detach_unit (uptr)); +} + +t_stat cr_set_type ( UNIT *uptr, + int32 val, + char *cptr, + void *desc ) +{ + /* disallow type change if currently attached */ + if (uptr->flags & UNIT_ATT) + return (SCPE_NOFNC); + cpm = (val & UNIT_CR11) ? 285 : 1000; + uptr->wait = (60 * 1000) / cpm; + return (SCPE_OK); +} + +t_stat cr_show_format ( SMP_FILE *st, + UNIT *uptr, + int32 val, + void *desc ) +{ + fprintf (st, "%s format", cardFormat); + return (SCPE_OK); +} + +t_stat cr_set_rate ( UNIT *uptr, + int32 val, + char *cptr, + void *desc ) +{ + t_stat status = SCPE_OK; + int32 i; + + if (!cptr) + return (SCPE_MISVAL); + if (strcmp (cptr, "DEFAULT") == 0) + i = (uptr->flags & UNIT_CR11) ? 285 : 1000; + else + i = (int32) get_uint (cptr, 10, 0xFFFFFFFF, &status); + if (status == SCPE_OK) { + if (i < 200 || i > 1200) + status = SCPE_ARG; + else { + cpm = i; + uptr->wait = (60 * 1000) / cpm; + } + } + return (status); +} + +t_stat cr_show_rate ( SMP_FILE *st, + UNIT *uptr, + int32 val, + void *desc ) +{ + fprintf (st, "%d cards per minute", cpm); + return (SCPE_OK); +} + +/* simulate pressing the card reader RESET button */ + +t_stat cr_set_reset ( UNIT *uptr, + int32 val, + char *cptr, + void *desc ) +{ + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_set_reset\n"); +/* +Ignore the RESET switch while a read cycle is in progress or the +unit simply is not attached. +*/ + if ((crs & CRCSR_BUSY) || !(uptr->flags & UNIT_ATT)) + return (SCPE_OK); + /* if no errors, signal transition to on line */ + crs |= CRCSR_ONLINE; + crs &= ~(CSR_ERR|CRCSR_CRDDONE|CRCSR_SUPPLY|CRCSR_RDCHK|CRCSR_TIMERR| + CRCSR_BUSY|CRCSR_COLRDY|CRCSR_EJECT|CSR_GO); + cdst |= CDCSR_ONLINE; + cdst &= ~(CSR_ERR | CDCSR_OFFLINE | CDCSR_RDRCHK | CDCSR_HOPPER | + CDCSR_EOF); + if ((crs & CSR_IE) || (cdst & CSR_IE)) { + SET_INT (CR); + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_set_reset setting interrupt\n"); + } + /* start up the blower if the hopper is not empty */ + if (blowerState != BLOW_ON) + blowerState = BLOW_START; + return (SCPE_OK); +} + +/* simulate pressing the card reader STOP button */ + +t_stat cr_set_stop ( UNIT *uptr, + int32 val, + char *cptr, + void *desc ) +{ + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "set_stop\n"); + crs &= ~CRCSR_ONLINE; + crs |= CSR_ERR | CRCSR_OFFLINE; + cdst |= CDCSR_OFFLINE; + /* CD11 does not appear to interrupt on STOP. */ + if (crs & CSR_IE) + SET_INT (CR); + if (blowerState != BLOW_OFF) { + blowerState = BLOW_STOP; + /* set timer to turn it off completely */ + sim_activate (uptr, spinDown); + } + return (SCPE_OK); +} + +static const char * const trans[] = { + "unknown", "026", "026FTN", "029", "EBCDIC" +}; + +t_stat cr_set_trans ( UNIT *uptr, + int32 val, + char *cptr, + void *desc ) +{ + int i; + + if (!cptr) + return (SCPE_MISVAL); + if (strcmp (cptr, "DEFAULT") == 0) + i = 3; + else { + for (i = 1; i < 5; i++) { + if (strcmp (cptr, trans[i]) == 0) + break; + } + } + if (i < 1 || i > 4) + return (SCPE_ARG); + table = i; + initTranslation (); /* reinitialize tables */ + return (SCPE_OK); +} + +t_stat cr_show_trans ( SMP_FILE *st, + UNIT *uptr, + int32 val, + void *desc ) +{ + fprintf (st, "translation %s", trans[table]); + return (SCPE_OK); +} diff --git a/src/PDP11/pdp11_cr_dat.h b/src/PDP11/pdp11_cr_dat.h new file mode 100644 index 0000000..5f272e5 --- /dev/null +++ b/src/PDP11/pdp11_cr_dat.h @@ -0,0 +1,622 @@ +/* pdp11_cr_dat.h + * + * card code arrays are indexed by 7-bit ASCII code, and + * give corresponding 12-bit card codes using the indicated + * collating sequence. + * + * ERROR should be externally defined, either as an illegal + * card code (on conversion from ASCII to card codes) or as + * a code with a bit set outside the least significant 12. + * + * author: Douglas Jones, jones@cs.uiowa.edu + * revisions: + * March 5, 1996 + * Feb 18, 1997 to add 026 and EBCDIC converstion tables + * Jan 10, 2005, (JAD) Added 'static const' to the array + * definitions. + * Jan 11, 2005, (JAD) Create the h2c_code array. + * Jan 14, 2005, (JAD) Added the special DEC code for 'end of deck' + * (12-11-0-1-6-7-8-9) to the o29_code array at position 26. (^Z). + * Should I add this to the other arrays? + */ + +/* DEC's version of the IBM 029 kepunch encoding, (thus avoiding IBM's + use of non-ASCII punctuation), based on that given in the appendix + to Digital's "Small Computer Handbook, 1973", and augmented to + translate lower case to upper case. As a result of this modification, + inversion of this table should be done with care! */ +static const int o29_code[] = { + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + ERROR,ERROR,07417,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ + 00000,02202,00006,00102,02102,01042,04000,00022, /* !"#$%&' */ + 04022,02022,02042,04012,01102,02000,04102,01400, /* ()*+,-./ */ + 01000,00400,00200,00100,00040,00020,00010,00004, /* 01234567 */ + 00002,00001,00202,02012,04042,00012,01012,01006, /* 89:;<=>? */ + 00042,04400,04200,04100,04040,04020,04010,04004, /* @ABCDEFG */ + 04002,04001,02400,02200,02100,02040,02020,02010, /* HIJKLMNO */ + 02004,02002,02001,01200,01100,01040,01020,01010, /* PQRSTUVW */ + 01004,01002,01001,04202,02006,01202,04006,01022, /* XYZ[\]^_ */ + ERROR,04400,04200,04100,04040,04020,04010,04004, /* `abcdefg */ + 04002,04001,02400,02200,02100,02040,02020,02010, /* hijklmno */ + 02004,02002,02001,01200,01100,01040,01020,01010, /* pqrstuvw */ + 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR /* xyz{|}~ */ + }; + +/* Bare bones 026 kepunch encodings */ +static const int o26_ftn_code[] = { + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ + 00000,ERROR,ERROR,ERROR,02102,ERROR,ERROR,00042, /* !"#$%&' */ + 01042,04042,02042,04000,01102,02000,04102,01400, /* ()*+,-./ */ + 01000,00400,00200,00100,00040,00020,00010,00004, /* 01234567 */ + 00002,00001,ERROR,ERROR,ERROR,00102,ERROR,ERROR, /* 89:;<=>? */ + ERROR,04400,04200,04100,04040,04020,04010,04004, /* @ABCDEFG */ + 04002,04001,02400,02200,02100,02040,02020,02010, /* HIJKLMNO */ + 02004,02002,02001,01200,01100,01040,01020,01010, /* PQRSTUVW */ + 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR, /* XYZ[\]^_ */ + ERROR,04400,04200,04100,04040,04020,04010,04004, /* `abcdefg */ + 04002,04001,02400,02200,02100,02040,02020,02010, /* hijklmno */ + 02004,02002,02001,01200,01100,01040,01020,01010, /* pqrstuvw */ + 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR /* xyz{|}~ */ + }; + +static const int o26_comm_code[] = { + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ + 00000,ERROR,ERROR,00102,02102,01042,04000,ERROR, /* !"#$%&' */ + ERROR,ERROR,02042,ERROR,01102,02000,04102,01400, /* ()*+,-./ */ + 01000,00400,00200,00100,00040,00020,00010,00004, /* 01234567 */ + 00002,00001,ERROR,ERROR,04042,ERROR,ERROR,ERROR, /* 89:;<=>? */ + 00042,04400,04200,04100,04040,04020,04010,04004, /* @ABCDEFG */ + 04002,04001,02400,02200,02100,02040,02020,02010, /* HIJKLMNO */ + 02004,02002,02001,01200,01100,01040,01020,01010, /* PQRSTUVW */ + 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR, /* XYZ[\]^_ */ + ERROR,04400,04200,04100,04040,04020,04010,04004, /* `abcdefg */ + 04002,04001,02400,02200,02100,02040,02020,02010, /* hijklmno */ + 02004,02002,02001,01200,01100,01040,01020,01010, /* pqrstuvw */ + 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR /* xyz{|}~ */ + }; + +/* FULL EBCDIC, from Appendix C of System 360 Programming by Alex Thomas, + 1977, Reinhart Press, San Francisco. Codes not in that table have been + left compatable with DEC's 029 table. Some control codes have been + left out */ +static const int EBCDIC_code[] = { + 05403,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + 02011,04021,01021,ERROR,04041,02021,ERROR,ERROR, /* chars */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + ERROR,ERROR,ERROR,ERROR,01201,ERROR,ERROR,ERROR, /* chars */ + 00000,02202,00006,00102,02102,01042,04000,00022, /* !"#$%&' */ + 04022,02022,02042,04012,01102,02000,04102,01400, /* ()*+,-./ */ + 01000,00400,00200,00100,00040,00020,00010,00004, /* 01234567 */ + 00002,00001,00202,02012,04042,00012,01012,01006, /* 89:;<=>? */ + 00042,04400,04200,04100,04040,04020,04010,04004, /* @ABCDEFG */ + 04002,04001,02400,02200,02100,02040,02020,02010, /* HIJKLMNO */ + 02004,02002,02001,01200,01100,01040,01020,01010, /* PQRSTUVW */ + 01004,01002,01001,04202,02006,01202,04006,01022, /* XYZ[\]^_ */ + ERROR,05400,05200,05100,05040,05020,05010,05004, /* `abcdefg */ + 05002,05001,06400,06200,06100,06040,06020,06010, /* hijklmno */ + 06004,06002,06001,03200,03100,03040,03020,03010, /* pqrstuvw */ + 03004,03002,03001,ERROR,ERROR,ERROR,ERROR,ERROR /* xyz{|}~ */ + }; + +static const int h2c_code[4096] = { + 0000, 0020, 0010, 0030, 0007, 0027, 0017, 0037, + 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, + 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0004, 0024, 0014, 0034, 0007, 0027, 0017, 0037, + 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, + 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0002, 0022, 0012, 0032, 0007, 0027, 0017, 0037, + 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, + 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0001, 0021, 0011, 0031, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0040, 0060, 0050, 0070, 0047, 0067, 0057, 0077, + 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, + 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0044, 0064, 0054, 0074, 0047, 0067, 0057, 0077, + 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, + 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0042, 0062, 0052, 0072, 0047, 0067, 0057, 0077, + 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, + 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0041, 0061, 0051, 0071, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0100, 0120, 0110, 0130, 0107, 0127, 0117, 0137, + 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, + 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0104, 0124, 0114, 0134, 0107, 0127, 0117, 0137, + 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, + 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0102, 0122, 0112, 0132, 0107, 0127, 0117, 0137, + 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, + 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0101, 0121, 0111, 0131, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0140, 0160, 0150, 0170, 0147, 0167, 0157, 0177, + 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, + 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0144, 0164, 0154, 0174, 0147, 0167, 0157, 0177, + 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, + 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0142, 0162, 0152, 0172, 0147, 0167, 0157, 0177, + 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, + 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0141, 0161, 0151, 0171, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0200, 0220, 0210, 0230, 0207, 0227, 0217, 0237, + 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, + 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0204, 0224, 0214, 0234, 0207, 0227, 0217, 0237, + 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, + 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0202, 0222, 0212, 0232, 0207, 0227, 0217, 0237, + 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, + 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0201, 0221, 0211, 0231, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0240, 0260, 0250, 0270, 0247, 0267, 0257, 0277, + 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, + 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0244, 0264, 0254, 0274, 0247, 0267, 0257, 0277, + 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, + 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0242, 0262, 0252, 0272, 0247, 0267, 0257, 0277, + 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, + 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0241, 0261, 0251, 0271, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0300, 0320, 0310, 0330, 0307, 0327, 0317, 0337, + 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, + 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0304, 0324, 0314, 0334, 0307, 0327, 0317, 0337, + 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, + 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0302, 0322, 0312, 0332, 0307, 0327, 0317, 0337, + 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, + 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0301, 0321, 0311, 0331, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0340, 0360, 0350, 0370, 0347, 0367, 0357, 0377, + 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, + 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0344, 0364, 0354, 0374, 0347, 0367, 0357, 0377, + 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, + 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0342, 0362, 0352, 0372, 0347, 0367, 0357, 0377, + 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, + 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0341, 0361, 0351, 0371, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, +}; diff --git a/src/PDP11/pdp11_dz.cpp b/src/PDP11/pdp11_dz.cpp new file mode 100644 index 0000000..d986b0c --- /dev/null +++ b/src/PDP11/pdp11_dz.cpp @@ -0,0 +1,799 @@ +/* pdp11_dz.c: DZ11 terminal multiplexor simulator + + 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. + + dz DZ11 terminal multiplexor + + 29-Dec-08 RMS Added MTAB_NC to SET LOG command (found by Walter Mueller) + 19-Nov-08 RMS Revised for common TMXR show routines + 18-Jun-07 RMS Added UNIT_IDLE flag + 29-Oct-06 RMS Synced poll and clock + 22-Nov-05 RMS Revised for new terminal processing routines + 07-Jul-05 RMS Removed extraneous externs + 15-Jun-05 RMS Revised for new autoconfigure interface + 04-Apr-04 RMS Added per-line logging + 05-Jan-04 RMS Revised for tmxr library changes + 19-May-03 RMS Revised for new conditional compilation scheme + 09-May-03 RMS Added network device flag + 22-Dec-02 RMS Added break (framing error) support + 31-Oct-02 RMS Added 8b support + 12-Oct-02 RMS Added autoconfigure support + 29-Sep-02 RMS Fixed bug in set number of lines routine + Added variable vector support + New data structures + 22-Apr-02 RMS Updated for changes in sim_tmxr + 28-Apr-02 RMS Fixed interrupt acknowledge, fixed SHOW DZ ADDRESS + 14-Jan-02 RMS Added multiboard support + 30-Dec-01 RMS Added show statistics, set disconnect + Removed statistics registers + 03-Dec-01 RMS Modified for extended SET/SHOW + 09-Nov-01 RMS Added VAX support + 20-Oct-01 RMS Moved getchar from sim_tmxr, changed interrupt + logic to use tmxr_rqln + 06-Oct-01 RMS Fixed bug in carrier detect logic + 03-Oct-01 RMS Added support for BSD-style "ringless" modems + 27-Sep-01 RMS Fixed bug in xmte initialization + 17-Sep-01 RMS Added separate autodisconnect switch + 16-Sep-01 RMS Fixed modem control bit offsets +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#include "pdp10_defs.h" +#define RANK_DZ 0 /* no autoconfig */ +#define DZ_8B_DFLT 0 +extern int32 int_req; + +#elif defined (VM_VAX) /* VAX version */ +#include "sim_defs.h" +#include "vax_defs.h" +#define DZ_8B_DFLT TT_MODE_8B + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#define DZ_8B_DFLT TT_MODE_8B +#endif + +#include "sim_sock.h" +#include "sim_tmxr.h" + +#if !defined (DZ_MUXES) +#define DZ_MUXES 1 +#endif +#if !defined (DZ_LINES) +#define DZ_LINES 8 +#endif + +#define DZ_MNOMASK (DZ_MUXES - 1) /* mask for mux no */ +#define DZ_LNOMASK (DZ_LINES - 1) /* mask for lineno */ +#define DZ_LMASK ((1 << DZ_LINES) - 1) /* mask of lines */ +#define DZ_SILO_ALM 16 /* silo alarm level */ + +/* DZCSR - 160100 - control/status register */ + +#define CSR_MAINT 0000010 /* maint - NI */ +#define CSR_CLR 0000020 /* clear */ +#define CSR_MSE 0000040 /* master scan enb */ +#define CSR_RIE 0000100 /* rcv int enb */ +#define CSR_RDONE 0000200 /* rcv done - RO */ +#define CSR_V_TLINE 8 /* xmit line - RO */ +#define CSR_TLINE (DZ_LNOMASK << CSR_V_TLINE) +#define CSR_SAE 0010000 /* silo alm enb */ +#define CSR_SA 0020000 /* silo alm - RO */ +#define CSR_TIE 0040000 /* xmit int enb */ +#define CSR_TRDY 0100000 /* xmit rdy - RO */ +#define CSR_RW (CSR_MSE | CSR_RIE | CSR_SAE | CSR_TIE) +#define CSR_MBZ (0004003 | CSR_CLR | CSR_MAINT) + +#define CSR_GETTL(x) (((x) >> CSR_V_TLINE) & DZ_LNOMASK) +#define CSR_PUTTL(x,y) x = ((x) & ~CSR_TLINE) | (((y) & DZ_LNOMASK) << CSR_V_TLINE) + +/* DZRBUF - 160102 - receive buffer, read only */ + +#define RBUF_CHAR 0000377 /* rcv char */ +#define RBUF_V_RLINE 8 /* rcv line */ +#define RBUF_PARE 0010000 /* parity err - NI */ +#define RBUF_FRME 0020000 /* frame err */ +#define RBUF_OVRE 0040000 /* overrun err - NI */ +#define RBUF_VALID 0100000 /* rcv valid */ +#define RBUF_MBZ 0004000 + +/* DZLPR - 160102 - line parameter register, write only, word access only */ + +#define LPR_V_LINE 0 /* line */ +#define LPR_LPAR 0007770 /* line pars - NI */ +#define LPR_RCVE 0010000 /* receive enb */ +#define LPR_GETLN(x) (((x) >> LPR_V_LINE) & DZ_LNOMASK) + +/* DZTCR - 160104 - transmission control register */ + +#define TCR_V_XMTE 0 /* xmit enables */ +#define TCR_V_DTR 8 /* DTRs */ + +/* DZMSR - 160106 - modem status register, read only */ + +#define MSR_V_RI 0 /* ring indicators */ +#define MSR_V_CD 8 /* carrier detect */ + +/* DZTDR - 160106 - transmit data, write only */ + +#define TDR_CHAR 0000377 /* xmit char */ +#define TDR_V_TBR 8 /* xmit break - NI */ + +extern int32 sim_switches; +extern SMP_FILE *sim_log; + +uint16 dz_csr[DZ_MUXES] = { 0 }; /* csr */ +uint16 dz_rbuf[DZ_MUXES] = { 0 }; /* rcv buffer */ +uint16 dz_lpr[DZ_MUXES] = { 0 }; /* line param */ +uint16 dz_tcr[DZ_MUXES] = { 0 }; /* xmit control */ +uint16 dz_msr[DZ_MUXES] = { 0 }; /* modem status */ +uint16 dz_tdr[DZ_MUXES] = { 0 }; /* xmit data */ +uint8 dz_sae[DZ_MUXES] = { 0 }; /* silo alarm enabled */ +uint32 dz_rxi = 0; /* rcv interrupts */ +uint32 dz_txi = 0; /* xmt interrupts */ +int32 dz_mctl = 0; /* modem ctrl enabled */ +int32 dz_auto = 0; /* autodiscon enabled */ +TMLN dz_ldsc[DZ_MUXES * DZ_LINES] = { {0} }; /* line descriptors */ +TMXR dz_desc = { DZ_MUXES * DZ_LINES, 0, 0, dz_ldsc }; /* mux descriptor */ +AUTO_INIT_DEVLOCK(dz_lock); /* device structures lock */ + +/* debugging bitmaps */ +#define DBG_REG 0x0001 /* trace read/write registers */ +#define DBG_INT 0x0002 /* display transfer requests */ +#define DBG_XMT TMXR_DBG_XMT /* display Transmitted Data */ +#define DBG_RCV TMXR_DBG_RCV /* display Received Data */ + +DEBTAB dz_debug[] = { + {"REG", DBG_REG}, + {"INT", DBG_INT}, + {"XMT", DBG_XMT}, + {"RCV", DBG_RCV}, + {0} +}; + +t_stat dz_rd (int32 *data, int32 PA, int32 access); +t_stat dz_wr (int32 data, int32 PA, int32 access); +int32 dz_rxinta (void); +int32 dz_txinta (void); +t_stat dz_svc (RUN_SVC_DECL, UNIT *uptr); +t_stat dz_reset (DEVICE *dptr); +t_stat dz_attach (UNIT *uptr, char *cptr); +t_stat dz_detach (UNIT *uptr); +t_stat dz_clear (int32 dz, t_bool flag); +int32 dz_getc (int32 dz); +void dz_update_rcvi (void); +void dz_update_xmti (void); +void dz_clr_rxint (int32 dz, t_bool immediate = TRUE); +void dz_set_rxint (int32 dz, t_bool immediate = TRUE); +void dz_clr_txint (int32 dz, t_bool immediate = TRUE); +void dz_set_txint (int32 dz, t_bool immediate = TRUE); +t_stat dz_setnl (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dz_set_log (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dz_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dz_show_log (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); + +/* DZ data structures + + dz_dev DZ device descriptor + dz_unit DZ unit list + dz_reg DZ register list +*/ + +DIB dz_dib = { + IOBA_DZ, IOLN_DZ * DZ_MUXES, &dz_rd, &dz_wr, + 2, IVCL (DZRX), VEC_DZRX, { &dz_rxinta, &dz_txinta } + }; + +UNIT dz_unit UDATA_SINGLE (&dz_svc, UNIT_IDLE|UNIT_ATTABLE|DZ_8B_DFLT, 0); +UNIT_TABLE_SINGLE(dz_unit); + +REG dz_reg[] = { + { BRDATA_GBL (CSR, dz_csr, DEV_RDX, 16, DZ_MUXES) }, + { BRDATA_GBL (RBUF, dz_rbuf, DEV_RDX, 16, DZ_MUXES) }, + { BRDATA_GBL (LPR, dz_lpr, DEV_RDX, 16, DZ_MUXES) }, + { BRDATA_GBL (TCR, dz_tcr, DEV_RDX, 16, DZ_MUXES) }, + { BRDATA_GBL (MSR, dz_msr, DEV_RDX, 16, DZ_MUXES) }, + { BRDATA_GBL (TDR, dz_tdr, DEV_RDX, 16, DZ_MUXES) }, + { BRDATA_GBL (SAENB, dz_sae, DEV_RDX, 1, DZ_MUXES) }, + { GRDATA_GBL (RXINT, dz_rxi, DEV_RDX, DZ_MUXES, 0) }, + { GRDATA_GBL (TXINT, dz_txi, DEV_RDX, DZ_MUXES, 0) }, + { FLDATA_GBL (MDMCTL, dz_mctl, 0) }, + { FLDATA_GBL (AUTODS, dz_auto, 0) }, + { GRDATA_GBL (DEVADDR, dz_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA_GBL (DEVVEC, dz_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB dz_mod[] = { + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &dz_desc }, + { UNIT_ATT, UNIT_ATT, "summary", NULL, + NULL, &tmxr_show_summ, (void *) &dz_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &tmxr_show_cstat, (void *) &dz_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &tmxr_show_cstat, (void *) &dz_desc }, + { MTAB_XTD|MTAB_VDV, 010, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, DZ_LINES, "VECTOR", "VECTOR", + &set_vec, &show_vec_mux, (void *) &dz_desc }, +#if !defined (VM_PDP10) + { MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE", + &set_addr_flt, NULL, NULL }, +#endif + { MTAB_XTD | MTAB_VDV, 0, "LINES", "LINES", + &dz_setnl, &tmxr_show_lines, (void *) &dz_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NC, 0, NULL, "LOG", + &dz_set_log, NULL, &dz_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NC, 0, NULL, "NOLOG", + &dz_set_nolog, NULL, &dz_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "LOG", NULL, + NULL, &dz_show_log, &dz_desc }, + { 0 } + }; + +DEVICE dz_dev = { + "DZ", dz_unit_table, dz_reg, dz_mod, + 1, DEV_RDX, 8, 1, DEV_RDX, 8, + &tmxr_ex, &tmxr_dep, &dz_reset, + NULL, &dz_attach, &dz_detach, + &dz_dib, DEV_FLTA | DEV_DISABLE | DEV_NET | DEV_UBUS | DEV_QBUS | DEV_DEBUG, + 0, dz_debug + }; + +/* Register names for Debug tracing */ +static char *dz_rd_regs[] = + {"CSR ", "RBUF", "TCR ", "MSR " }; +static char *dz_wr_regs[] = + {"CSR ", "LPR ", "TCR ", "TDR "}; + +/* IO dispatch routines, I/O addresses 177601x0 - 177601x7 */ + +t_stat dz_rd (int32 *data, int32 PA, int32 access) +{ + AUTO_LOCK(dz_lock); + int32 dz = ((PA - dz_dib.ba) >> 3) & DZ_MNOMASK; /* get mux num */ + + switch ((PA >> 1) & 03) /* case on PA<2:1> */ + { + case 00: /* CSR */ + *data = dz_csr[dz] = dz_csr[dz] & ~CSR_MBZ; + break; + + case 01: /* RBUF */ + dz_csr[dz] = dz_csr[dz] & ~CSR_SA; /* clr silo alarm */ + if (dz_csr[dz] & CSR_MSE) { /* scanner on? */ + dz_rbuf[dz] = dz_getc (dz); /* get top of silo */ + if (!dz_rbuf[dz]) /* empty? re-enable */ + dz_sae[dz] = 1; + tmxr_poll_rx (&dz_desc); /* poll input */ + dz_update_rcvi (); /* update rx intr */ + } + else { + dz_rbuf[dz] = 0; /* no data */ + dz_update_rcvi (); /* no rx intr */ + } + *data = dz_rbuf[dz]; + break; + + case 02: /* TCR */ + *data = dz_tcr[dz]; + break; + + case 03: /* MSR */ + *data = dz_msr[dz]; + break; + } + + sim_debug(DBG_REG, &dz_dev, "dz_rd(PA=0x%08X [%s], access=%d, data=0x%X)\n", PA, dz_rd_regs[(PA >> 1) & 03], access, *data); + + return SCPE_OK; +} + +t_stat dz_wr (int32 data, int32 PA, int32 access) +{ + AUTO_LOCK(dz_lock); + int32 dz = ((PA - dz_dib.ba) >> 3) & DZ_MNOMASK; /* get mux num */ + int32 i, c, line; + TMLN *lp; + + sim_debug(DBG_REG, &dz_dev, "dz_wr(PA=0x%08X [%s], access=%d, data=0x%X)\n", PA, dz_wr_regs[(PA >> 1) & 03], access, data); + + switch ((PA >> 1) & 03) /* case on PA<2:1> */ + { + case 00: /* CSR */ + if (access == WRITEB) data = (PA & 1) ? /* byte? merge */ + (dz_csr[dz] & 0377) | (data << 8): + (dz_csr[dz] & ~0377) | data; + if (data & CSR_CLR) /* clr? reset */ + dz_clear (dz, FALSE); + if (data & CSR_MSE) /* MSE? start poll */ + sim_activate_clk_cosched (&dz_unit, TMXR_MULT); + else + dz_csr[dz] &= ~(CSR_SA | CSR_RDONE | CSR_TRDY); + if ((data & CSR_RIE) == 0) /* RIE = 0? */ + dz_clr_rxint (dz); + else if ((dz_csr[dz] & CSR_IE) == 0 && /* RIE 0->1? */ + ((dz_csr[dz] & CSR_SAE)? + (dz_csr[dz] & CSR_SA): (dz_csr[dz] & CSR_RDONE))) + dz_set_rxint (dz); + if ((data & CSR_TIE) == 0) /* TIE = 0? */ + dz_clr_txint (dz); + else if (((dz_csr[dz] & CSR_TIE) == 0) && (dz_csr[dz] & CSR_TRDY)) + dz_set_txint (dz); + dz_csr[dz] = (dz_csr[dz] & ~CSR_RW) | (data & CSR_RW); + break; + + case 01: /* LPR */ + dz_lpr[dz] = data; + line = (dz * DZ_LINES) + LPR_GETLN (data); /* get line num */ + lp = &dz_ldsc[line]; /* get line desc */ + if (dz_lpr[dz] & LPR_RCVE) /* rcv enb? on */ + lp->rcve = 1; + else lp->rcve = 0; /* else line off */ + tmxr_poll_rx (&dz_desc); /* poll input */ + dz_update_rcvi (); /* update rx intr */ + break; + + case 02: /* TCR */ + if (access == WRITEB) data = (PA & 1)? /* byte? merge */ + (dz_tcr[dz] & 0377) | (data << 8): + (dz_tcr[dz] & ~0377) | data; + if (dz_mctl) /* modem ctl? */ + { + dz_msr[dz] |= ((data & 0177400) & /* dcd |= dtr & ring */ + ((dz_msr[dz] & DZ_LMASK) << MSR_V_CD)); + dz_msr[dz] &= ~(data >> TCR_V_DTR); /* ring &= ~dtr */ + if (dz_auto) /* auto disconnect? */ + { + int32 drop; + drop = (dz_tcr[dz] & ~data) >> TCR_V_DTR; /* drop = dtr & ~data */ + for (i = 0; i < DZ_LINES; i++) /* drop hangups */ + { + line = (dz * DZ_LINES) + i; /* get line num */ + lp = &dz_ldsc[line]; /* get line desc */ + if (lp->conn && (drop & (1 << i))) + { + tmxr_linemsg (lp, "\r\nLine hangup\r\n"); + tmxr_reset_ln (lp); /* reset line, cdet */ + dz_msr[dz] &= ~(1 << (i + MSR_V_CD)); + } + } + } + } + dz_tcr[dz] = data; + tmxr_poll_tx (&dz_desc); /* poll output */ + dz_update_xmti (); /* update int */ + break; + + case 03: /* TDR */ + if (PA & 1) { /* odd byte? */ + dz_tdr[dz] = (dz_tdr[dz] & 0377) | (data << 8); /* just save */ + break; + } + dz_tdr[dz] = data; + if (dz_csr[dz] & CSR_MSE) { /* enabled? */ + line = (dz * DZ_LINES) + CSR_GETTL (dz_csr[dz]); + lp = &dz_ldsc[line]; /* get line desc */ + c = sim_tt_outcvt (dz_tdr[dz], TT_GET_MODE (dz_unit.flags)); + if (c >= 0) /* store char */ + tmxr_putc_ln (lp, c); + tmxr_poll_tx (&dz_desc); /* poll output */ + dz_update_xmti (); /* update int */ + } + break; + } + + return SCPE_OK; +} + +/* Unit service routine + + The DZ11 polls to see if asynchronous activity has occurred and now + needs to be processed. The polling interval is controlled by the clock + simulator, so for most environments, it is calibrated to real time. + Typical polling intervals are 50-60 times per second. + + The simulator assumes that software enables all of the multiplexors, + or none of them. +*/ + +t_stat dz_svc (RUN_SVC_DECL, UNIT *uptr) +{ + AUTO_LOCK(dz_lock); + RUN_SVC_CHECK_CANCELLED(uptr); + int32 dz, t, newln; + + for (dz = t = 0; dz < DZ_MUXES; dz++) /* check enabled */ + t = t | (dz_csr[dz] & CSR_MSE); + + if (t) /* any enabled? */ + { + newln = tmxr_poll_conn (&dz_desc); /* poll connect */ + if (newln >= 0 && dz_mctl) /* got a live one? */ + { + dz = newln / DZ_LINES; /* get mux num */ + if (dz_tcr[dz] & (1 << (newln + TCR_V_DTR))) /* DTR set? */ + dz_msr[dz] |= (1 << (newln + MSR_V_CD)); /* set cdet */ + else + dz_msr[dz] |= (1 << newln); /* set ring */ + } + tmxr_poll_rx (&dz_desc); /* poll input */ + dz_update_rcvi (); /* upd rcv intr */ + tmxr_poll_tx (&dz_desc); /* poll output */ + dz_update_xmti (); /* upd xmt intr */ + sim_activate_clk_cosched (uptr, TMXR_MULT); /* reactivate */ + } + + return SCPE_OK; +} + +/* Get first available character for mux, if any */ + +int32 dz_getc (int32 dz) +{ + uint32 i, line, c; + + for (i = c = 0; (i < DZ_LINES) && (c == 0); i++) /* loop thru lines */ + { + line = (dz * DZ_LINES) + i; /* get line num */ + c = tmxr_getc_ln (&dz_ldsc[line]); /* test for input */ + if (c & SCPE_BREAK) /* break? frame err */ + c = RBUF_VALID | RBUF_FRME; + if (c) /* or in line # */ + c = c | (i << RBUF_V_RLINE); + } + return c; +} + +/* Update receive interrupts */ + +void dz_update_rcvi (void) +{ + int32 i, dz, line, scnt[DZ_MUXES]; + TMLN *lp; + + for (dz = 0; dz < DZ_MUXES; dz++) /* loop thru muxes */ + { + scnt[dz] = 0; /* clr input count */ + for (i = 0; i < DZ_LINES; i++) /* poll lines */ + { + line = (dz * DZ_LINES) + i; /* get line num */ + lp = &dz_ldsc[line]; /* get line desc */ + scnt[dz] = scnt[dz] + tmxr_rqln (lp); /* sum buffers */ + if (dz_mctl && !lp->conn) /* if disconn */ + dz_msr[dz] &= ~(1 << (i + MSR_V_CD)); /* reset car det */ + } + } + + for (dz = 0; dz < DZ_MUXES; dz++) /* loop thru muxes */ + { + if (scnt[dz] && (dz_csr[dz] & CSR_MSE)) /* input & enabled? */ + { + dz_csr[dz] |= CSR_RDONE; /* set done */ + if (dz_sae[dz] && scnt[dz] >= DZ_SILO_ALM) /* alm enb & cnt hi? */ + { + dz_csr[dz] |= CSR_SA; /* set status */ + dz_sae[dz] = 0; /* disable alarm */ + } + } + else + { + dz_csr[dz] &= ~CSR_RDONE; /* no, clear done */ + } + + if ((dz_csr[dz] & CSR_RIE) && /* int enable */ + ((dz_csr[dz] & CSR_SAE)? + (dz_csr[dz] & CSR_SA) : (dz_csr[dz] & CSR_RDONE))) + { + dz_set_rxint (dz, FALSE); /* and alm/done? */ + } + else + { + dz_clr_rxint (dz, FALSE); /* no, clear int */ + } + } + + if (dz_rxi == 0) /* all clr? */ + CLR_INT (DZRX); + else + SET_INT (DZRX); /* no, set intr */ +} + +/* Update transmit interrupts */ + +void dz_update_xmti (void) +{ + int32 dz, linemask, i, j, line; + + for (dz = 0; dz < DZ_MUXES; dz++) /* loop thru muxes */ + { + linemask = dz_tcr[dz] & DZ_LMASK; /* enabled lines */ + dz_csr[dz] &= ~CSR_TRDY; /* assume not rdy */ + j = CSR_GETTL (dz_csr[dz]); /* start at current */ + for (i = 0; i < DZ_LINES; i++) /* loop thru lines */ + { + j = (j + 1) & DZ_LNOMASK; /* next line */ + line = (dz * DZ_LINES) + j; /* get line num */ + if ((linemask & (1 << j)) && dz_ldsc[line].xmte) + { + CSR_PUTTL (dz_csr[dz], j); /* put ln in csr */ + dz_csr[dz] |= CSR_TRDY; /* set xmt rdy */ + break; + } + } + + if ((dz_csr[dz] & CSR_TIE) && (dz_csr[dz] & CSR_TRDY)) /* ready plus int? */ + { + dz_set_txint (dz, FALSE); + } + else + { + dz_clr_txint (dz, FALSE); /* no int req */ + } + } + + if (dz_txi == 0) /* all clr? */ + CLR_INT (DZTX); + else + SET_INT (DZTX); /* no, set intr */ +} + +/* Interrupt routines */ + +void dz_clr_rxint (int32 dz, t_bool immediate) +{ + dz_rxi = dz_rxi & ~(1 << dz); /* clr mux rcv int */ + if (immediate) + { + if (dz_rxi == 0) /* all clr? */ + CLR_INT (DZRX); + else + SET_INT (DZRX); /* no, set intr */ + } +} + +void dz_set_rxint (int32 dz, t_bool immediate) +{ + dz_rxi = dz_rxi | (1 << dz); /* set mux rcv int */ + if (immediate) + SET_INT (DZRX); /* set master intr */ +} + +int32 dz_rxinta (void) +{ + AUTO_LOCK(dz_lock); + int32 dz; + + for (dz = 0; dz < DZ_MUXES; dz++) /* find 1st mux */ + { + if (dz_rxi & (1 << dz)) + { + sim_debug(DBG_INT, &dz_dev, "dz_rzinta(dz=%d)\n", dz); + dz_clr_rxint (dz); /* clear intr */ + return (dz_dib.vec + (dz * 010)); /* return vector */ + } + } + + return 0; +} + +void dz_clr_txint (int32 dz, t_bool immediate) +{ + dz_txi = dz_txi & ~(1 << dz); /* clr mux xmt int */ + if (immediate) + { + if (dz_txi == 0) /* all clr? */ + CLR_INT (DZTX); + else + SET_INT (DZTX); /* no, set intr */ + } +} + +void dz_set_txint (int32 dz, t_bool immediate) +{ + dz_txi = dz_txi | (1 << dz); /* set mux xmt int */ + if (immediate) + SET_INT (DZTX); /* set master intr */ +} + +int32 dz_txinta (void) +{ + AUTO_LOCK(dz_lock); + int32 dz; + + for (dz = 0; dz < DZ_MUXES; dz++) /* find 1st mux */ + { + if (dz_txi & (1 << dz)) + { + sim_debug(DBG_INT, &dz_dev, "dz_txinta(dz=%d)\n", dz); + dz_clr_txint (dz); /* clear intr */ + return (dz_dib.vec + 4 + (dz * 010)); /* return vector */ + } + } + return 0; +} + +/* Device reset */ + +t_stat dz_clear (int32 dz, t_bool flag) +{ +int32 i, line; + +dz_csr[dz] = 0; /* clear CSR */ +dz_rbuf[dz] = 0; /* silo empty */ +dz_lpr[dz] = 0; /* no params */ +if (flag) /* INIT? clr all */ + dz_tcr[dz] = 0; +else dz_tcr[dz] &= ~0377; /* else save dtr */ +dz_tdr[dz] = 0; +dz_sae[dz] = 1; /* alarm on */ +dz_clr_rxint (dz); /* clear int */ +dz_clr_txint (dz); +for (i = 0; i < DZ_LINES; i++) { /* loop thru lines */ + line = (dz * DZ_LINES) + i; + if (!dz_ldsc[line].conn) /* set xmt enb */ + dz_ldsc[line].xmte = 1; + dz_ldsc[line].rcve = 0; /* clr rcv enb */ + } +return SCPE_OK; +} + +t_stat dz_reset (DEVICE *dptr) +{ +AUTO_LOCK(dz_lock); +sim_bind_devunits_lock(&dz_dev, dz_lock); +int32 i, ndev; + +for (i = 0; i < DZ_MUXES; i++) /* init muxes */ + dz_clear (i, TRUE); +dz_rxi = dz_txi = 0; /* clr master int */ +CLR_INT (DZRX); +CLR_INT (DZTX); +sim_cancel (&dz_unit); /* stop poll */ +ndev = ((dptr->flags & DEV_DIS)? 0: (dz_desc.lines / DZ_LINES)); +return auto_config (dptr->name, ndev); /* auto config */ +} + +/* Attach */ + +t_stat dz_attach (UNIT *uptr, char *cptr) +{ +t_stat r; +extern int32 sim_switches; + +dz_mctl = dz_auto = 0; /* modem ctl off */ +r = tmxr_attach (&dz_desc, uptr, cptr); /* attach mux */ +if (r != SCPE_OK) /* error? */ + return r; +if (sim_switches & SWMASK ('M')) { /* modem control? */ + dz_mctl = 1; + smp_printf ("Modem control activated\n"); + if (sim_log) + fprintf (sim_log, "Modem control activated\n"); + if (sim_switches & SWMASK ('A')) { /* autodisconnect? */ + dz_auto = 1; + smp_printf ("Auto disconnect activated\n"); + if (sim_log) + fprintf (sim_log, "Auto disconnect activated\n"); + } + } +return SCPE_OK; +} + +/* Detach */ + +t_stat dz_detach (UNIT *uptr) +{ +return tmxr_detach (&dz_desc, uptr); +} + +/* SET LINES processor */ + +t_stat dz_setnl (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 newln, i, t, ndev; +t_stat r; + +if (cptr == NULL) + return SCPE_ARG; +newln = (int32) get_uint (cptr, 10, (DZ_MUXES * DZ_LINES), &r); +if ((r != SCPE_OK) || (newln == dz_desc.lines)) + return r; +if ((newln == 0) || (newln % DZ_LINES)) + return SCPE_ARG; +if (newln < dz_desc.lines) { + for (i = newln, t = 0; i < dz_desc.lines; i++) + t = t | (dz_ldsc[i].conn != 0); + if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) + return SCPE_OK; + for (i = newln; i < dz_desc.lines; i++) { + if (dz_ldsc[i].conn) { + tmxr_linemsg (&dz_ldsc[i], "\r\nOperator disconnected line\r\n"); + tmxr_reset_ln (&dz_ldsc[i]); /* reset line */ + } + if ((i % DZ_LINES) == (DZ_LINES - 1)) + dz_clear (i / DZ_LINES, TRUE); /* reset mux */ + } + } +dz_dib.lnt = (newln / DZ_LINES) * IOLN_DZ; /* set length */ +dz_desc.lines = newln; +ndev = ((dz_dev.flags & DEV_DIS)? 0: (dz_desc.lines / DZ_LINES)); +return auto_config (dz_dev.name, ndev); /* auto config */ +} + +/* SET LOG processor */ + +t_stat dz_set_log (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +char *tptr; +t_stat r; +int32 ln; + +if (cptr == NULL) + return SCPE_ARG; +tptr = strchr (cptr, '='); +if ((tptr == NULL) || (*tptr == 0)) + return SCPE_ARG; +*tptr++ = 0; +ln = (int32) get_uint (cptr, 10, (DZ_MUXES * DZ_LINES), &r); +if ((r != SCPE_OK) || (ln >= dz_desc.lines)) + return SCPE_ARG; +return tmxr_set_log (NULL, ln, tptr, desc); +} + +/* SET NOLOG processor */ + +t_stat dz_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +t_stat r; +int32 ln; + +if (cptr == NULL) + return SCPE_ARG; +ln = (int32) get_uint (cptr, 10, (DZ_MUXES * DZ_LINES), &r); +if ((r != SCPE_OK) || (ln >= dz_desc.lines)) + return SCPE_ARG; +return tmxr_set_nolog (NULL, ln, NULL, desc); +} + +/* SHOW LOG processor */ + +t_stat dz_show_log (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i; + +for (i = 0; i < dz_desc.lines; i++) { + fprintf (st, "line %d: ", i); + tmxr_show_log (st, NULL, i, desc); + fprintf (st, "\n"); + } +return SCPE_OK; +} + + + diff --git a/src/PDP11/pdp11_io_lib.cpp b/src/PDP11/pdp11_io_lib.cpp new file mode 100644 index 0000000..dd15c88 --- /dev/null +++ b/src/PDP11/pdp11_io_lib.cpp @@ -0,0 +1,667 @@ +/* pdp11_io_lib.c: Unibus/Qbus common support routines + + 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. +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#include "pdp10_defs.h" + +#elif defined (VM_VAX) /* VAX version */ +#include "sim_defs.h" +#include "vax_defs.h" + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#endif +#include "sim_sock.h" +#include "sim_tmxr.h" + +extern SMP_FILE *sim_log; +extern int32 autcon_enb; +extern atomic_int32 int_vec[IPL_HLVL][32]; +extern SIM_ALIGN_PTR int32 (* volatile int_ack[IPL_HLVL][32])(void); +extern t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md); +extern t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md); + +static DIB *iodibp[IOPAGESIZE >> 1]; + +/* Enable/disable autoconfiguration */ + +t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + if (cptr != NULL) + return SCPE_ARG; + autcon_enb = val; + return auto_config (NULL, 0); +} + +/* Show autoconfiguration status */ + +t_stat show_autocon (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ + fprintf (st, "autoconfiguration "); + fprintf (st, autcon_enb? "enabled": "disabled"); + return SCPE_OK; +} + +/* Change device address */ + +t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + DEVICE *dptr; + DIB *dibp; + uint32 newba; + t_stat r; + + if (cptr == NULL) + return SCPE_ARG; + if ((val == 0) || (uptr == NULL)) + return SCPE_IERR; + dptr = find_dev_from_unit (uptr); + if (dptr == NULL) + return SCPE_IERR; + dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + newba = (uint32) get_uint (cptr, DEV_RDX, IOPAGEBASE+IOPAGEMASK, &r); /* get new */ + if (r != SCPE_OK) + return r; + if ((newba <= IOPAGEBASE) || /* > IO page base? */ + (newba % ((uint32) val))) /* check modulus */ + return SCPE_ARG; + dibp->ba = newba; /* store */ + dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */ + autcon_enb = 0; /* autoconfig off */ + return SCPE_OK; +} + +/* Show device address */ + +t_stat show_addr (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ + DEVICE *dptr; + DIB *dibp; + + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit (uptr); + if (dptr == NULL) + return SCPE_IERR; + dibp = (DIB *) dptr->ctxt; + if ((dibp == NULL) || (dibp->ba <= IOPAGEBASE)) + return SCPE_IERR; + fprintf (st, "address="); + fprint_val (st, (t_value) dibp->ba, DEV_RDX, 32, PV_LEFT); + if (dibp->lnt > 1) + { + fprintf (st, "-"); + fprint_val (st, (t_value) dibp->ba + dibp->lnt - 1, DEV_RDX, 32, PV_LEFT); + } + +#if defined(VM_VAX) + if (ADDR_IS_IO(dibp->ba)) + { + fprintf(st, " (QBus: %06o", (t_value) dibp->ba - IOPAGEBASE + 0760000); + if (dibp->lnt > 1) + { + fprintf (st, "-%06o", (t_value) dibp->ba + dibp->lnt - 1 - IOPAGEBASE + 0760000); + } + fprintf(st, ")"); + } +#endif + + if (dptr->flags & DEV_FLTA) + fprintf (st, "*"); + + return SCPE_OK; +} + +/* Set address floating */ + +t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + DEVICE *dptr; + + if (cptr != NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit (uptr); + if (dptr == NULL) + return SCPE_IERR; + dptr->flags = dptr->flags | DEV_FLTA; /* floating */ + return auto_config (NULL, 0); /* autoconfigure */ +} + +/* Change device vector */ + +t_stat set_vec (UNIT *uptr, int32 arg, char *cptr, void *desc) +{ + DEVICE *dptr; + DIB *dibp; + uint32 newvec; + t_stat r; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit (uptr); + if (dptr == NULL) + return SCPE_IERR; + dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + newvec = (uint32) get_uint (cptr, DEV_RDX, VEC_Q + 01000, &r); + if (r != SCPE_OK || newvec == VEC_Q || + newvec + (dibp->vnum * 4) >= VEC_Q + 01000 || + (newvec & ((dibp->vnum > 1) ? 07: 03))) + return SCPE_ARG; + dibp->vec = newvec; + dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */ + autcon_enb = 0; /* autoconfig off */ + return SCPE_OK; +} + +/* Show device vector */ + +t_stat show_vec (SMP_FILE *st, UNIT *uptr, int32 arg, void *desc) +{ + DEVICE *dptr; + DIB *dibp; + uint32 vec, numvec; + + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit (uptr); + if (dptr == NULL) + return SCPE_IERR; + dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + vec = dibp->vec; + if (arg) + numvec = arg; + else numvec = dibp->vnum; + if (vec == 0) + fprintf (st, "no vector"); + else + { + fprintf (st, "vector="); + fprint_val (st, (t_value) vec, DEV_RDX, 16, PV_LEFT); + if (numvec > 1) + { + fprintf (st, "-"); + fprint_val (st, (t_value) vec + (4 * (numvec - 1)), DEV_RDX, 16, PV_LEFT); + } + +#if defined(VM_VAX) + if (vec >= VEC_Q && 4 * (numvec - 1) < 0x200) + { + fprintf (st, " (QBus: %03o", vec - VEC_Q); + if (numvec > 1) + fprintf (st, "-%03o", vec + 4 * (numvec - 1) - VEC_Q); + fprintf (st, ")"); + } +#endif + } + return SCPE_OK; +} + +/* Show vector for terminal multiplexor */ + +t_stat show_vec_mux (SMP_FILE *st, UNIT *uptr, int32 arg, void *desc) +{ + TMXR *mp = (TMXR *) desc; + + if ((mp == NULL) || (arg == 0)) + return SCPE_IERR; + return show_vec (st, uptr, ((mp->lines * 2) / arg), desc); +} + +/* Init Unibus tables */ + +AUTO_INIT_DEVLOCK(ubus_table_lock); + +void init_ubus_tab (void) +{ + uint32 i, j; + + for (i = 0; i < IPL_HLVL; i++) /* clear intr tab */ + { + for (j = 0; j < 32; j++) + { + int_vec[i][j] = 0; + int_ack[i][j] = NULL; + } + } + + for (i = 0; i < (IOPAGESIZE >> 1); i++) /* clear dispatch tab */ + { + iodispR[i] = NULL; + iodispW[i] = NULL; + iodibp[i] = NULL; + } +} + +/* Build Unibus tables */ + +t_stat build_ubus_tab (DEVICE *dptr, DIB *dibp) +{ + int32 i, idx, vec, ilvl, ibit; + + if (dptr == NULL || dibp == NULL) /* validate args */ + return SCPE_IERR; + + if (dibp->vnum > VEC_DEVMAX) + return SCPE_IERR; + + int_vec[IPL_IPINTR][INT_V_IPINTR] = SCB_IPINTR; /* interprocessor interrupt */ + + for (i = 0; i < dibp->vnum; i++) /* loop thru vec */ + { + idx = dibp->vloc + i; /* vector index */ + vec = dibp->vec ? (dibp->vec + (i * 4)): 0; /* vector addr */ + ilvl = idx / 32; + ibit = idx % 32; + + if ((int_ack[ilvl][ibit] && dibp->ack[i] && /* conflict? */ + (int_ack[ilvl][ibit] != dibp->ack[i])) || + (int_vec[ilvl][ibit] && vec && + (int_vec[ilvl][ibit] != vec))) + { + smp_printf ("Device %s interrupt slot conflict at %d\n", sim_dname (dptr), idx); + if (sim_log) + fprintf (sim_log, "Device %s interrupt slot conflict at %d\n", sim_dname (dptr), idx); + return SCPE_STOP; + } + + if (dibp->ack[i]) + int_ack[ilvl][ibit] = dibp->ack[i]; + else if (vec) + int_vec[ilvl][ibit] = vec; + } + + for (i = 0; i < (int32) dibp->lnt; i = i + 2) /* create entries */ + { + idx = ((dibp->ba + i) & IOPAGEMASK) >> 1; /* index into disp */ + + if ((iodispR[idx] && dibp->rd && /* conflict? */ + (iodispR[idx] != dibp->rd)) || + (iodispW[idx] && dibp->wr && + (iodispW[idx] != dibp->wr))) + { + smp_printf ("Device %s address conflict at \n", sim_dname (dptr)); + fprint_val (smp_stdout, (t_value) dibp->ba, DEV_RDX, 32, PV_LEFT); + if (sim_log) + { + fprintf (sim_log, "Device %s address conflict at \n", sim_dname (dptr)); + fprint_val (sim_log, (t_value) dibp->ba, DEV_RDX, 32, PV_LEFT); + } + return SCPE_STOP; + } + + if (dibp->rd) /* set rd dispatch */ + iodispR[idx] = dibp->rd; + + if (dibp->wr) /* set wr dispatch */ + iodispW[idx] = dibp->wr; + + iodibp[idx] = dibp; /* remember DIB */ + } + + return SCPE_OK; +} + + +#if defined(VM_VAX) +void io_change_vec(DIB* dibp, int32 vec) +{ + int32 idx, ilvl, ibit; + + /* + * Lock the table and also RMB the changes performed by other threads + */ + AUTO_LOCK(ubus_table_lock); + + dibp->vec = vec; + + for (int32 i = 0; i < dibp->vnum; i++) /* loop thru vec */ + { + idx = dibp->vloc + i; /* vector index */ + vec = dibp->vec ? (dibp->vec + (i * 4)): 0; /* vector addr */ + ilvl = idx / 32; + ibit = idx % 32; + + if ((int_ack[ilvl][ibit] && dibp->ack[i] && /* conflict? */ + (int_ack[ilvl][ibit] != dibp->ack[i])) || + (int_vec[ilvl][ibit] && vec && + (int_vec[ilvl][ibit] != vec))) + { + RUN_SCOPE; + DEVICE* dptr = find_dev (dibp); + smp_printf ("\nDevice %s interrupt slot conflict at %d\n", sim_dname (dptr), idx); + if (sim_log) + fprintf (sim_log, "Device %s interrupt slot conflict at %d\n", sim_dname (dptr), idx); + ABORT_INVALID_SYSOP; + } + + if (dibp->ack[i]) + int_ack[ilvl][ibit] = dibp->ack[i]; + else if (vec) + int_vec[ilvl][ibit] = vec; + } + + /* + * WMB will be performed by auto-unlock of ubus_table_lock. + * get_vector() that reads int_ack and int_vec or its callers are responsible for RMB. + * + * Thus, on the device's side change of vector is ordered before the interrupt: + * + * change int_vec/int_ack + * wmb + * signal interrupt + * + * Interrupt receiver must insure that it simularly orders: + * + * notice the interrupt + * rmb + * fetch int_vec/int_ack + * + */ +} +#endif + +/* Show IO space */ + +t_stat show_iospace (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ + uint32 i, j; + DEVICE *dptr; + DIB *dibp; + + if (build_dib_tab ()) /* build IO page */ + return SCPE_OK; + + for (i = 0, dibp = NULL; i < (IOPAGESIZE >> 1); i++) /* loop thru entries */ + { + if (iodibp[i] && (iodibp[i] != dibp)) /* new block? */ + { + dibp = iodibp[i]; /* DIB for block */ + for (j = 0, dptr = NULL; sim_devices[j] != NULL; j++) + { + if (((DIB*) sim_devices[j]->ctxt) == dibp) + { + dptr = sim_devices[j]; /* locate device */ + break; + } + } + fprint_val (st, (t_value) dibp->ba, DEV_RDX, 32, PV_LEFT); + fprintf (st, " - "); + fprint_val (st, (t_value) dibp->ba + dibp->lnt - 1, DEV_RDX, 32, PV_LEFT); + fprintf (st, "%c\t%s\n", /* print block entry */ + (dptr && (dptr->flags & DEV_FLTA))? '*': ' ', + dptr? sim_dname (dptr): "CPU"); + } + } + return SCPE_OK; +} + +/* Autoconfiguration + + The table reflects the MicroVAX 3900 microcode, with one addition - the + number of controllers field handles devices where multiple instances + are simulated through a single DEVICE structure (e.g., DZ, VH). + + A minus number of vectors indicates a field that should be calculated + but not placed in the DIB (RQ, TQ dynamic vectors) */ + +#define AUTO_MAXC 4 +#define AUTO_CSRBASE 0010 +#define AUTO_VECBASE 0300 + +typedef struct { + char *dnam[AUTO_MAXC]; + int32 numc; + int32 numv; + uint32 amod; + uint32 vmod; + uint32 fixa[AUTO_MAXC]; + uint32 fixv[AUTO_MAXC]; + } AUTO_CON; + +AUTO_CON auto_tab[] = { + { { "DCI" }, DCX_LINES, 2, 0, 8, { 0 } }, /* DC11 - fx CSRs */ + { { "DLI" }, DLX_LINES, 2, 0, 8, { 0 } }, /* KL11/DL11/DLV11 - fx CSRs */ + { { NULL }, 1, 2, 0, 8, { 0 } }, /* DLV11J - fx CSRs */ + { { NULL }, 1, 2, 8, 8 }, /* DJ11 */ + { { NULL }, 1, 2, 16, 8 }, /* DH11 */ + { { NULL }, 1, 2, 8, 8 }, /* DQ11 */ + { { NULL }, 1, 2, 8, 8 }, /* DU11 */ + { { NULL }, 1, 2, 8, 8 }, /* DUP11 */ + { { NULL }, 10, 2, 8, 8 }, /* LK11A */ + { { NULL }, 1, 2, 8, 8 }, /* DMC11 */ + { { "DZ" }, DZ_MUXES, 2, 8, 8 }, /* DZ11 */ + { { NULL }, 1, 2, 8, 8 }, /* KMC11 */ + { { NULL }, 1, 2, 8, 8 }, /* LPP11 */ + { { NULL }, 1, 2, 8, 8 }, /* VMV21 */ + { { NULL }, 1, 2, 16, 8 }, /* VMV31 */ + { { NULL }, 1, 2, 8, 8 }, /* DWR70 */ + { { "RL", "RLB" }, 1, 1, 8, 4, {IOBA_RL}, {VEC_RL} }, /* RL11 */ + { { "TS", "TSB", "TSC", "TSD" }, 1, 1, 0, 4, /* TS11 */ + {IOBA_TS, IOBA_TS + 4, IOBA_TS + 8, IOBA_TS + 12}, + {VEC_TS} }, + { { NULL }, 1, 2, 16, 8 }, /* LPA11K */ + { { NULL }, 1, 2, 8, 8 }, /* KW11C */ + { { NULL }, 1, 1, 8, 8 }, /* reserved */ + { { "RX", "RY" }, 1, 1, 8, 4, {IOBA_RX} , {VEC_RX} }, /* RX11/RX211 */ + { { NULL }, 1, 1, 8, 4 }, /* DR11W */ + { { NULL }, 1, 1, 8, 4, { 0, 0 }, { 0 } }, /* DR11B - fx CSRs,vec */ + { { NULL }, 1, 2, 8, 8 }, /* DMP11 */ + { { NULL }, 1, 2, 8, 8 }, /* DPV11 */ + { { NULL }, 1, 2, 8, 8 }, /* ISB11 */ + { { NULL }, 1, 2, 16, 8 }, /* DMV11 */ + { { "XU", "XUB" }, 1, 1, 8, 4, {IOBA_XU}, {VEC_XU} }, /* DEUNA */ + { { "XQ", "XQB" }, 1, 1, 0, 4, /* DEQNA */ + {IOBA_XQ,IOBA_XQB}, {VEC_XQ} }, + { { "RQ", "RQB", "RQC", "RQD" }, 1, -1, 4, 4, /* RQDX3 */ + {IOBA_RQ}, {VEC_RQ} }, + { { NULL }, 1, 8, 32, 4 }, /* DMF32 */ + { { NULL }, 1, 2, 16, 8 }, /* KMS11 */ + { { NULL }, 1, 1, 16, 4 }, /* VS100 */ + { { "TQ", "TQB" }, 1, -1, 4, 4, {IOBA_TQ}, {VEC_TQ} }, /* TQK50 */ + { { NULL }, 1, 2, 16, 8 }, /* KMV11 */ + { { "VH" }, VH_MUXES, 2, 16, 8 }, /* DHU11/DHQ11 */ + { { NULL }, 1, 6, 32, 4 }, /* DMZ32 */ + { { NULL }, 1, 6, 32, 4 }, /* CP132 */ + { { NULL }, 1, 2, 64, 8, { 0 } }, /* QVSS - fx CSR */ + { { NULL }, 1, 1, 8, 4 }, /* VS31 */ + { { NULL }, 1, 1, 0, 4, { 0 } }, /* LNV11 - fx CSR */ + { { NULL }, 1, 1, 16, 4 }, /* LNV21/QPSS */ + { { NULL }, 1, 1, 8, 4, { 0 } }, /* QTA - fx CSR */ + { { NULL }, 1, 1, 8, 4 }, /* DSV11 */ + { { NULL }, 1, 2, 8, 8 }, /* CSAM */ + { { NULL }, 1, 2, 8, 8 }, /* ADV11C */ + { { NULL }, 1, 0, 8, 0 }, /* AAV11C */ + { { NULL }, 1, 2, 8, 8, { 0 }, { 0 } }, /* AXV11C - fx CSR,vec */ + { { NULL }, 1, 2, 4, 8, { 0 } }, /* KWV11C - fx CSR */ + { { NULL }, 1, 2, 8, 8, { 0 } }, /* ADV11D - fx CSR */ + { { NULL }, 1, 2, 8, 8, { 0 } }, /* AAV11D - fx CSR */ + { { "QDSS" }, 1, 3, 0, 16, {IOBA_QDSS} }, /* QDSS - fx CSR */ + { { NULL }, -1 } /* end table */ +}; + +t_stat auto_config (const char *name, int32 nctrl) +{ + /* + * SMP note: auto_config can be invoked by devices at run time, such as when RQ or RY perform INIT command + * or RQ executes auto-reset after a fatal error. auto_config will write DIB.ba and DIB.vec for + * every device in the system. However unless the configuration actually changed, it will + * write the same values, so it presents no problem for concurrently executing threads, + * they are not going to find that device CSR addresses or vectors suddenly changed under + * their feet. + */ + + uint32 csr = IOPAGEBASE + AUTO_CSRBASE; + uint32 vec = VEC_Q + AUTO_VECBASE; + AUTO_CON *autp; + DEVICE *dptr; + DIB *dibp; + uint32 j, k, vmask, amask; + + if (autcon_enb == 0) /* enabled? */ + return SCPE_OK; + + if (name) /* updating? */ + { + if (nctrl < 0) + return SCPE_ARG; + + for (autp = auto_tab; autp->numc >= 0; autp++) + { + for (j = 0; j < AUTO_MAXC && autp->dnam[j]; j++) + { + if (strcmp (name, autp->dnam[j]) == 0) + autp->numc = nctrl; + } + } + } + + for (autp = auto_tab; autp->numc >= 0; autp++) /* loop thru table */ + { + if (autp->amod) /* floating csr? */ + { + amask = autp->amod - 1; + csr = (csr + amask) & ~amask; /* align csr */ + } + + for (j = k = 0; j < AUTO_MAXC && autp->dnam[j]; j++) + { + if (autp->dnam[j] == NULL) /* no device? */ + continue; + + dptr = find_dev (autp->dnam[j]); /* find ctrl */ + + if (dptr == NULL || (dptr->flags & DEV_DIS)) /* enabled? */ + continue; + + if (! (dptr->flags & DEV_FLTA)) /* floating? */ + { + k++; + continue; + } + + dibp = (DIB*) dptr->ctxt; /* get DIB */ + + if (dibp == NULL) /* not there??? */ + return SCPE_IERR; + + if (autp->amod) /* dyn csr needed? */ + { + if (autp->fixa[k]) /* fixed csr avail? */ + dibp->ba = autp->fixa[k]; /* use it */ + else /* no fixed left */ + { + dibp->ba = csr; /* set CSR */ + csr += (autp->numc * autp->amod); /* next CSR */ + } /* end else */ + } /* end if dyn csr */ + + if (autp->numv && autp->vmod) /* dyn vec needed? */ + { + uint32 numv = abs (autp->numv); /* get num vec */ + if (autp->fixv[k]) /* fixed vec avail? */ + { + if (autp->numv > 0) + dibp->vec = autp->fixv[k]; /* use it */ + } + else /* no fixed left */ + { + vmask = autp->vmod - 1; + vec = (vec + vmask) & ~vmask; /* align vector */ + if (autp->numv > 0) + dibp->vec = vec; /* set vector */ + vec += (autp->numc * numv * 4); + } + } /* end if dyn vec */ + + k++; /* next instance */ + } + + if (autp->amod) /* flt CSR? gap */ + csr = csr + 2; + } + + return SCPE_OK; +} + +/* Factory bad block table creation routine + + This routine writes a DEC standard 044 compliant bad block table on the + last track of the specified unit. The bad block table consists of 10 + repetitions of the same table, formatted as follows: + + words 0-1 pack id number + words 2-3 cylinder/sector/surface specifications + : + words n-n+1 end of table (-1,-1) + + Inputs: + uptr = pointer to unit + sec = number of sectors per surface + wds = number of words per sector + Outputs: + sta = status code +*/ + +t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds) +{ + int32 i; + t_addr da; + uint16 *buf; + + if ((sec < 2) || (wds < 16)) + return SCPE_ARG; + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; + if (uptr->flags & UNIT_RO) + return SCPE_RO; + if (!get_yn ("Overwrite last track? [N]", FALSE)) + return SCPE_OK; + da = (uptr->capac - (sec * wds)) * sizeof (uint16); + if (sim_fseek (uptr->fileref, da, SEEK_SET)) + return SCPE_IOERR; + if ((buf = (uint16 *) malloc (wds * sizeof (uint16))) == NULL) + return SCPE_MEM; + buf[0] = buf[1] = 012345u; + buf[2] = buf[3] = 0; + for (i = 4; i < wds; i++) + buf[i] = 0177777u; + for (i = 0; (i < sec) && (i < 10); i++) + sim_fwrite (buf, sizeof (uint16), wds, uptr->fileref); + free (buf); + if (ferror (uptr->fileref)) + return SCPE_IOERR; + return SCPE_OK; +} diff --git a/src/PDP11/pdp11_io_lib.h b/src/PDP11/pdp11_io_lib.h new file mode 100644 index 0000000..953337b --- /dev/null +++ b/src/PDP11/pdp11_io_lib.h @@ -0,0 +1,46 @@ +/* pdp11_io_lib.h: Unibus/Qbus common support routines header file + + 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. +*/ + +#ifndef _PDP11_IO_LIB_H_ +#define _PDP11_IO_LIB_H_ 0 + +t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_autocon (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_addr (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat set_vec (UNIT *uptr, int32 arg, char *cptr, void *desc); +t_stat show_vec (SMP_FILE *st, UNIT *uptr, int32 arg, void *desc); +t_stat show_vec_mux (SMP_FILE *st, UNIT *uptr, int32 arg, void *desc); +t_stat show_iospace (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat auto_config (const char *name, int32 nctrl); +t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds); +void init_ubus_tab (void); +t_stat build_ubus_tab (DEVICE *dptr, DIB *dibp); +void io_change_vec(DIB* dibp, int32 vec); +extern t_stat build_dib_tab (void); + +#endif diff --git a/src/PDP11/pdp11_lp.cpp b/src/PDP11/pdp11_lp.cpp new file mode 100644 index 0000000..f3a5232 --- /dev/null +++ b/src/PDP11/pdp11_lp.cpp @@ -0,0 +1,203 @@ +/* pdp11_lp.c: PDP-11 line printer simulator + + 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. + + lpt LP11 line printer + + 19-Jan-07 RMS Added UNIT_TEXT flag + 07-Jul-05 RMS Removed extraneous externs + 19-May-03 RMS Revised for new conditional compilation scheme + 25-Apr-03 RMS Revised for extended file support + 29-Sep-02 RMS Added vector change/display support + New data structures + 30-May-02 RMS Widened POS to 32b + 06-Jan-02 RMS Added enable/disable support + 09-Nov-01 RMS Added VAX support + 07-Sep-01 RMS Revised interrupt mechanism + 30-Oct-00 RMS Standardized register naming +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#error "LP11 is not supported on the PDP-10!" + +#elif defined (VM_VAX) /* VAX version */ +#include "sim_defs.h" +#include "vax_defs.h" + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#endif + +#define LPTCSR_IMP (CSR_ERR + CSR_DONE + CSR_IE) /* implemented */ +#define LPTCSR_RW (CSR_IE) /* read/write */ + +int32 lpt_csr = 0; /* control/status */ +int32 lpt_stopioe = 0; /* stop on error */ +AUTO_INIT_DEVLOCK(lp_lock); + +t_stat lpt_rd (int32 *data, int32 PA, int32 access); +t_stat lpt_wr (int32 data, int32 PA, int32 access); +t_stat lpt_svc (RUN_SVC_DECL, UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, char *ptr); +t_stat lpt_detach (UNIT *uptr); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +DIB lpt_dib = { + IOBA_LPT, IOLN_LPT, &lpt_rd, &lpt_wr, + 1, IVCL (LPT), VEC_LPT, { NULL } + }; + +UNIT lpt_unit UDATA_SINGLE_WAIT (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0, SERIAL_OUT_WAIT); +UNIT_TABLE_SINGLE(lpt_unit); + +REG lpt_reg[] = { + { GRDATA_GBL (BUF, lpt_unit.buf, DEV_RDX, 8, 0) }, + { GRDATA_GBL (CSR, lpt_csr, DEV_RDX, 16, 0) }, + { IRDATA_DEV (INT, IVCL (LPT)) }, + { FLDATA_GBL (ERR, lpt_csr, CSR_V_ERR) }, + { FLDATA_GBL (DONE, lpt_csr, CSR_V_DONE) }, + { FLDATA_GBL (IE, lpt_csr, CSR_V_IE) }, + { DRDATA_GBL (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA_GBL (TIME, lpt_unit.wait, 24), PV_LEFT }, + { FLDATA_GBL (STOP_IOE, lpt_stopioe, 0) }, + { GRDATA_GBL (DEVADDR, lpt_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA_GBL (DEVVEC, lpt_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB lpt_mod[] = { + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEVICE lpt_dev = { + "LPT", lpt_unit_table, lpt_reg, lpt_mod, + 1, 10, 31, 1, DEV_RDX, 8, + NULL, NULL, &lpt_reset, + NULL, &lpt_attach, &lpt_detach, + &lpt_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS + }; + +/* Line printer routines + + lpt_rd I/O page read + lpt_wr I/O page write + lpt_svc process event (printer ready) + lpt_reset process reset + lpt_attach process attach + lpt_detach process detach +*/ + +t_stat lpt_rd (int32 *data, int32 PA, int32 access) +{ + AUTO_LOCK(lp_lock); + if ((PA & 02) == 0) /* csr */ + *data = lpt_csr & LPTCSR_IMP; + else *data = lpt_unit.buf; /* buffer */ + return SCPE_OK; +} + +t_stat lpt_wr (int32 data, int32 PA, int32 access) +{ + AUTO_LOCK(lp_lock); + if ((PA & 02) == 0) { /* csr */ + if (PA & 1) + return SCPE_OK; + if ((data & CSR_IE) == 0) + CLR_INT (LPT); + else if ((lpt_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + SET_INT (LPT); + lpt_csr = (lpt_csr & ~LPTCSR_RW) | (data & LPTCSR_RW); + } + else { /* buffer */ + if ((PA & 1) == 0) + lpt_unit.buf = data & 0177; + lpt_csr = lpt_csr & ~CSR_DONE; + CLR_INT (LPT); + if ((lpt_unit.buf == 015) || (lpt_unit.buf == 014) || + (lpt_unit.buf == 012)) sim_activate (&lpt_unit, lpt_unit.wait); + else sim_activate (&lpt_unit, 0); + } + return SCPE_OK; +} + +t_stat lpt_svc (RUN_SVC_DECL, UNIT *uptr) +{ + AUTO_LOCK(lp_lock); + RUN_SVC_CHECK_CANCELLED(uptr); + lpt_csr = lpt_csr | CSR_ERR | CSR_DONE; + if (lpt_csr & CSR_IE) + SET_INT (LPT); + if ((uptr->flags & UNIT_ATT) == 0) + return IORETURN (lpt_stopioe, SCPE_UNATT); + fputc (uptr->buf & 0177, uptr->fileref); + uptr->pos = ftell (uptr->fileref); + if (ferror (uptr->fileref)) { + smp_perror ("LPT I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + lpt_csr = lpt_csr & ~CSR_ERR; + return SCPE_OK; +} + +t_stat lpt_reset (DEVICE *dptr) +{ + AUTO_LOCK(lp_lock); + sim_bind_devunits_lock(&lpt_dev, lp_lock); + lpt_unit.buf = 0; + lpt_csr = CSR_DONE; + if ((lpt_unit.flags & UNIT_ATT) == 0) + lpt_csr = lpt_csr | CSR_ERR; + CLR_INT (LPT); + sim_cancel (&lpt_unit); /* deactivate unit */ + return SCPE_OK; +} + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ + t_stat reason; + + lpt_csr = lpt_csr & ~CSR_ERR; + reason = attach_unit (uptr, cptr); + if ((lpt_unit.flags & UNIT_ATT) == 0) + lpt_csr = lpt_csr | CSR_ERR; + return reason; +} + +t_stat lpt_detach (UNIT *uptr) +{ + lpt_csr = lpt_csr | CSR_ERR; + return detach_unit (uptr); +} diff --git a/src/PDP11/pdp11_mscp.h b/src/PDP11/pdp11_mscp.h new file mode 100644 index 0000000..59c1f95 --- /dev/null +++ b/src/PDP11/pdp11_mscp.h @@ -0,0 +1,515 @@ +/* pdp11_mscp.h: DEC MSCP and TMSCP definitionsn + + Copyright (c) 2001-2008, Robert M Supnik + Derived from work by Stephen F. Shirron + + 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. + + 09-Jan-03 RMS Tape read/write end pkt is longer than disk read/write + 20-Sep-02 RMS Merged TMSCP definitions +*/ + +#ifndef _PDP11_MSCP_H_ +#define _PDP11_MSCP_H_ 0 + +/* Misc constants */ + +#define UID_DISK 2 /* disk class */ +#define UID_TAPE 3 /* tape class */ + +/* Opcodes */ + +#define OP_ABO 1 /* b: abort */ +#define OP_GCS 2 /* b: get command status */ +#define OP_GUS 3 /* b: get unit status */ +#define OP_SCC 4 /* b: set controller char */ +#define OP_AVL 8 /* b: available */ +#define OP_ONL 9 /* b: online */ +#define OP_SUC 10 /* b: set unit char */ +#define OP_DAP 11 /* b: det acc paths - nop */ +#define OP_ACC 16 /* b: access */ +#define OP_CCD 17 /* d: compare - nop */ +#define OP_ERS 18 /* b: erase */ +#define OP_FLU 19 /* d: flush - nop */ +#define OP_ERG 22 /* t: erase gap */ +#define OP_CMP 32 /* b: compare */ +#define OP_RD 33 /* b: read */ +#define OP_WR 34 /* b: write */ +#define OP_WTM 36 /* t: write tape mark */ +#define OP_POS 37 /* t: reposition */ +#define OP_FMT 47 /* d: format */ +#define OP_AVA 64 /* b: unit now avail */ +#define OP_END 0x80 /* b: end flag */ + +/* Modifiers */ + +#define MD_EXP 0x8000 /* d: express NI */ +#define MD_CMP 0x4000 /* b: compare NI */ +#define MD_CSE 0x2000 /* b: clr ser err */ +#define MD_ERR 0x1000 /* d: force error NI*/ +#define MD_CDL 0x1000 /* t: clr data lost NI*/ +#define MD_SCH 0x0800 /* t: supr cache NI */ +#define MD_SEC 0x0200 /* b: supr err corr NI */ +#define MD_SER 0x0100 /* b: supr err rec NI */ +#define MD_DLE 0x0080 /* t: detect LEOT */ +#define MD_IMM 0x0040 /* t: immediate NI */ +#define MD_EXA 0x0020 /* b: excl access NI */ +#define MD_SHD 0x0010 /* d: shadow NI */ +#define MD_UNL 0x0010 /* t avl: unload */ +#define MD_ERW 0x0008 /* t wr: enb rewrite */ +#define MD_REV 0x0008 /* t rd, pos: reverse */ +#define MD_SWP 0x0004 /* b suc: enb set wrp */ +#define MD_OBC 0x0004 /* t: pos: obj count */ +#define MD_IMF 0x0002 /* d onl: ign fmte NI */ +#define MD_RWD 0x0002 /* t pos: rewind */ +#define MD_ACL 0x0002 /* t avl: all class NI */ +#define MD_NXU 0x0001 /* b gus: next unit */ +#define MD_RIP 0x0001 /* d onl: allow rip NI */ +#define MD_SPD 0x0001 /* d avl: spin-down */ + +/* End flags */ + +#define EF_LOG 0x0020 /* b: error log */ +#define EF_SXC 0x0010 /* b: serious exc */ +#define EF_EOT 0x0008 /* end of tape */ +#define EF_PLS 0x0004 /* pos lost */ +#define EF_DLS 0x0002 /* cached data lost NI */ + +/* Controller flags */ + +#define CF_RPL 0x8000 /* ctrl bad blk repl */ +#define CF_ATN 0x0080 /* enb attention */ +#define CF_MSC 0x0040 /* enb misc msgs */ +#define CF_OTH 0x0020 /* enb othr host msgs */ +#define CF_THS 0x0010 /* enb this host msgs */ +#define CF_MSK (CF_ATN|CF_MSC|CF_OTH|CF_THS) + +/* Unit flags */ + +#define UF_RPL 0x8000 /* d: ctrl bad blk repl */ +#define UF_CAC 0x8000 /* t: cache write back */ +#define UF_WPH 0x2000 /* b: wr prot hwre */ +#define UF_WPS 0x1000 /* b: wr prot swre */ +#define UF_SCH 0x0800 /* t: supr cache NI */ +#define UF_EXA 0x0400 /* b: exclusive NI */ +#define UF_WPD 0x0100 /* b: wr prot data NI */ +#define UF_RMV 0x0080 /* d: removable */ +#define UF_WBN 0x0040 /* t: write back NI */ +#define UF_VSS 0x0020 /* t: supr var speed NI */ +#define UF_VSU 0x0010 /* t: var speed unit NI */ +#define UF_EWR 0x0008 /* t: enh wr recovery NI */ +#define UF_CMW 0x0002 /* cmp writes NI */ +#define UF_CMR 0x0001 /* cmp reads NI */ + +/* Error log flags */ + +#define LF_SUC 0x0080 /* b: successful */ +#define LF_CON 0x0040 /* b: continuing */ +#define LF_BBR 0x0020 /* d: bad blk repl NI */ +#define LF_RCT 0x0010 /* d: err in repl NI */ +#define LF_SNR 0x0001 /* b: seq # reset */ + +/* Error log formats */ + +#define FM_CNT 0 /* b: port lf err */ +#define FM_BAD 1 /* b: bad host addr */ +#define FM_DSK 2 /* d: disk xfer */ +#define FM_SDI 3 /* d: SDI err */ +#define FM_SDE 4 /* d: sm disk err */ +#define FM_TAP 5 /* t: tape errors */ +#define FM_RPL 9 /* d: bad blk repl */ + +/* Status codes */ + +#define ST_SUC 0 /* b: successful */ +#define ST_CMD 1 /* b: invalid cmd */ +#define ST_ABO 2 /* b: aborted cmd */ +#define ST_OFL 3 /* b: unit offline */ +#define ST_AVL 4 /* b: unit avail */ +#define ST_MFE 5 /* b: media fmt err */ +#define ST_WPR 6 /* b: write prot err */ +#define ST_CMP 7 /* b: compare err */ +#define ST_DAT 8 /* b: data err */ +#define ST_HST 9 /* b: host acc err */ +#define ST_CNT 10 /* b: ctrl err */ +#define ST_DRV 11 /* b: drive err */ +#define ST_FMT 12 /* t: formatter err */ +#define ST_BOT 13 /* t: BOT encountered */ +#define ST_TMK 14 /* t: tape mark */ +#define ST_RDT 16 /* t: record trunc */ +#define ST_POL 17 /* t: pos lost */ +#define ST_SXC 18 /* b: serious exc */ +#define ST_LED 19 /* t: LEOT detect */ +#define ST_BBR 20 /* d: bad block */ +#define ST_DIA 31 /* b: diagnostic */ +#define ST_V_SUB 5 /* subcode */ +#define ST_V_INV 8 /* invalid op */ + +/* Status subcodes */ + +#define SB_SUC_IGN (1 << ST_V_SUB) /* t: unload ignored */ +#define SB_SUC_ON (8 << ST_V_SUB) /* b: already online */ +#define SB_SUC_EOT (32 << ST_V_SUB) /* t: EOT encountered */ +#define SB_SUC_RO (128 << ST_V_SUB) /* t: read only */ +#define SB_OFL_NV (1 << ST_V_SUB) /* b: no volume */ +#define SB_OFL_INOP (2 << ST_V_SUB) /* t: inoperative */ +#define SB_AVL_INU (32 << ST_V_SUB) /* b: in use */ +#define SB_WPR_SW (128 << ST_V_SUB) /* b: swre wlk */ +#define SB_WPR_HW (256 << ST_V_SUB) /* b: hwre wlk */ +#define SB_HST_OA (1 << ST_V_SUB) /* b: odd addr */ +#define SB_HST_OC (2 << ST_V_SUB) /* d: odd count */ +#define SB_HST_NXM (3 << ST_V_SUB) /* b: nx memory */ +#define SB_HST_PAR (4 << ST_V_SUB) /* b: parity err */ +#define SB_HST_PTE (5 << ST_V_SUB) /* b: mapping err */ +#define SB_DAT_RDE (7 << ST_V_SUB) /* t: read err */ + +/* Status invalid command subcodes */ + +#define I_OPCD (8 << ST_V_INV) /* inv opcode */ +#define I_FLAG (9 << ST_V_INV) /* inv flags */ +#define I_MODF (10 << ST_V_INV) /* inv modifier */ +#define I_BCNT (12 << ST_V_INV) /* inv byte cnt */ +#define I_LBN (28 << ST_V_INV) /* inv LBN */ +#define I_VRSN (12 << ST_V_INV) /* inv version */ +#define I_FMTI (28 << ST_V_INV) /* inv format */ + +/* Tape format flags */ + +#define TF_9TK 0x0100 /* 9 track */ +#define TF_9TK_NRZ 0x0001 /* 800 bpi */ +#define TF_9TK_PE 0x0002 /* 1600 bpi */ +#define TF_9TK_GRP 0x0004 /* 6250 bpi */ +#define TF_CTP 0x0200 /* TK50 */ +#define TF_CTP_LO 0x0001 /* low density */ +#define TF_CTP_HI 0x0002 /* hi density */ +#define TF_3480 0x0300 /* 3480 */ +#define TF_WOD 0x0400 /* RV80 */ + +/* Packet formats - note that all packet lengths must be multiples of 4 bytes */ + +/* Command packet header */ + +#define CMD_REFL 2 /* ref # */ +#define CMD_REFH 3 +#define CMD_UN 4 /* unit # */ +// 5 /* reserved */ +#define CMD_OPC 6 /* opcode */ +#define CMD_MOD 7 /* modifier */ + +#define CMD_OPC_V_OPC 0 /* opcode */ +#define CMD_OPC_M_OPC 0xFF +#define CMD_OPC_V_CAA 8 /* cache NI */ +#define CMD_OPC_M_CAA 0xFF +#define CMD_OPC_V_FLG 8 /* flags */ +#define CMD_OPC_M_FLG 0xFF + +/* Response packet header */ + +#define RSP_LNT 12 +#define RSP_REFL 2 /* ref # */ +#define RSP_REFH 3 +#define RSP_UN 4 /* unit # */ +#define RSP_RSV 5 /* reserved */ +#define RSP_OPF 6 /* opcd,flg */ +#define RSP_STS 7 /* modifiers */ + +#define RSP_OPF_V_OPC 0 /* opcode */ +#define RSP_OPF_V_FLG 8 /* flags */ + +/* Abort packet - 2 W parameter, 2 W status */ + +#define ABO_LNT 16 +#define ABO_REFL 8 /* ref # */ +#define ABO_REFH 9 + +/* Avail packet - min size */ + +#define AVL_LNT 12 + +/* Erase packet - min size */ + +#define ERS_LNT 12 + +/* Erase gap - min size */ + +#define ERG_LNT 12 + +/* Flush - 10 W status (8 undefined) */ + +#define FLU_LNT 32 +// 8 - 15 /* reserved */ +#define FLU_POSL 16 /* position */ +#define FLU_POSH 17 + +/* Write tape mark - 10W status (8 undefined) */ + +#define WTM_LNT 32 +// 8 - 15 /* reserved */ +#define WTM_POSL 16 /* position */ +#define WTM_POSH 17 + +/* Get command status packet - 2 W parameter, 4 W status */ + +#define GCS_LNT 20 +#define GCS_REFL 8 /* ref # */ +#define GCS_REFH 9 +#define GCS_STSL 10 /* status */ +#define GCS_STSH 11 + +/* Format packet - 8 W parameters, none returned */ + +#define FMT_LNT 12 +#define FMT_IH 17 /* magic bit */ + +/* Get unit status packet - 18 W status (disk), 16W status (tape) */ + +#define GUS_LNT_D 48 +#define GUS_LNT_T 44 +#define GUS_MLUN 8 /* mlun */ +#define GUS_UFL 9 /* flags */ +#define GUS_RSVL 10 /* reserved */ +#define GUS_RSVH 11 +#define GUS_UIDA 12 /* unit ID */ +#define GUS_UIDB 13 +#define GUS_UIDC 14 +#define GUS_UIDD 15 +#define GUS_MEDL 16 /* media ID */ +#define GUS_MEDH 17 +#define GUS_UVER 23 /* unit version */ + +/* Disk specific status */ + +#define GUS_SHUN 18 /* shadowing */ +#define GUS_SHST 19 +#define GUS_TRK 20 /* track */ +#define GUS_GRP 21 /* group */ +#define GUS_CYL 22 /* cylinder */ +#define GUS_RCTS 24 /* RCT size */ +#define GUS_RBSC 25 /* RBNs, copies */ + +/* Tape specific status */ + +#define GUS_FMT 18 /* format */ +#define GUS_SPEED 19 /* speed */ +#define GUS_MENU 20 /* menu */ +#define GUS_CAP 21 /* capacity */ +#define GUS_FVER 22 /* fmtr version */ + +#define GUS_UIDD_V_MOD 0 /* unit model */ +#define GUS_UIDD_V_CLS 8 /* unit class */ +#define GUS_RB_V_RBNS 0 /* RBNs/track */ +#define GUS_RB_V_RCTC 8 /* RCT copies */ + +/* Unit online - 2 W parameter, 16 W status (disk or tape) */ + +#define ONL_LNT 44 +#define ONL_MLUN 8 /* mlun */ +#define ONL_UFL 9 /* flags */ +#define ONL_RSVL 10 /* reserved */ +#define ONL_RSVH 11 +#define ONL_UIDA 12 /* unit ID */ +#define ONL_UIDB 13 +#define ONL_UIDC 14 +#define ONL_UIDD 15 +#define ONL_MEDL 16 /* media ID */ +#define ONL_MEDH 17 + +/* Disk specific status */ + +#define ONL_SHUN 18 /* shadowing */ +#define ONL_SHST 19 +#define ONL_SIZL 20 /* size */ +#define ONL_SIZH 21 +#define ONL_VSNL 22 /* vol ser # */ +#define ONL_VSNH 23 + +/* Tape specific status */ + +#define ONL_FMT 18 /* format */ +#define ONL_SPD 19 /* speed */ +#define ONL_MAXL 20 /* max rec size */ +#define ONL_MAXH 21 +#define ONL_NREC 22 /* noise rec */ +#define ONL_RSVE 23 /* reserved */ + +#define ONL_UIDD_V_MOD 0 /* unit model */ +#define ONL_UIDD_V_CLS 8 /* unit class */ + +/* Set controller characteristics packet - 8 W parameters, 10 W status */ + +#define SCC_LNT 32 +#define SCC_MSV 8 /* MSCP version */ +#define SCC_CFL 9 /* flags */ +#define SCC_TMO 10 /* timeout */ +#define SCC_VER 11 /* ctrl version */ +#define SCC_CIDA 12 /* ctrl ID */ +#define SCC_CIDB 13 +#define SCC_CIDC 14 +#define SCC_CIDD 15 +#define SCC_MBCL 16 /* max byte count */ +#define SCC_MBCH 17 + +#define SCC_VER_V_SVER 0 /* swre vrsn */ +#define SCC_VER_V_HVER 8 /* hwre vrsn */ +#define SCC_CIDD_V_MOD 0 /* ctrl model */ +#define SCC_CIDD_V_CLS 8 /* ctrl class */ + +/* Set unit characteristics - 2 W parameter, 16 W status - same as ONL */ + +#define SUC_LNT 44 + +/* Reposition - 4 W parameters, 10 W status */ + +#define POS_LNT 32 +#define POS_RCL 8 /* record cnt */ +#define POS_RCH 9 +#define POS_TMCL 10 /* tape mk cnt */ +#define POS_TMCH 11 +/* reserved 12 - 15 */ +#define POS_POSL 16 /* position */ +#define POS_POSH 17 + +/* Data transfer packet - 10 W parameters (disk), 6W parameters (tape), + 10 W status (disk), 12W status (tape) */ + +#define RW_LNT_D 32 +#define RW_LNT_T 36 +#define RW_BCL 8 /* byte count */ +#define RW_BCH 9 +#define RW_BAL 10 /* buff desc */ +#define RW_BAH 11 +#define RW_MAPL 12 /* map table */ +#define RW_MAPH 13 +// 14 /* reserved */ +// 15 /* reserved */ + +/* Disk specific parameters */ + +#define RW_LBNL 16 /* LBN */ +#define RW_LBNH 17 +#define RW_WBCL 18 /* working bc */ +#define RW_WBCH 19 +#define RW_WBAL 20 /* working ba */ +#define RW_WBAH 21 +#define RW_WBLL 22 /* working lbn */ +#define RW_WBLH 23 + +/* Tape specific status */ + +#define RW_POSL 16 /* position */ +#define RW_POSH 17 +#define RW_RSZL 18 /* record size */ +#define RW_RSZH 19 + +/* Error log packet header */ + +#define ELP_REFL 2 /* ref # */ +#define ELP_REFH 3 +#define ELP_UN 4 /* unit */ +#define ELP_SEQ 5 +#define ELP_FF 6 /* fmt,flg */ +#define ELP_EVT 7 /* event */ + +#define ELP_EV_V_FMT 0 /* format */ +#define ELP_EV_V_FLG 8 /* flag */ + +/* Port last failure error log packet - 6 W status */ + +#define PLF_LNT 24 /* length */ +#define PLF_CIDA 8 /* ctrl ID */ +#define PLF_CIDB 9 +#define PLF_CIDC 10 +#define PLF_CIDD 11 +#define PLF_VER 12 /* ctrl version */ +#define PLF_ERR 13 /* err */ + +#define PLF_CIDD_V_MOD 0 /* ctrl model */ +#define PLF_CIDD_V_CLS 8 /* ctrl class */ +#define PLF_VER_V_SVER 0 /* swre ver */ +#define PLF_VER_V_HVER 8 /* hwre ver */ + +/* Disk transfer error log packet - 18 W status */ + +#define DTE_LNT 48 +#define DTE_CIDA 8 /* ctrl ID */ +#define DTE_CIDB 9 +#define DTE_CIDC 10 +#define DTE_CIDD 11 +#define DTE_VER 12 /* version */ +#define DTE_MLUN 13 /* mlun */ +#define DTE_UIDA 14 /* unit ID */ +#define DTE_UIDB 15 +#define DTE_UIDC 16 +#define DTE_UIDD 17 +#define DTE_UVER 18 +#define DTE_D2 23 +#define DTE_D3 24 +#define DTE_D4 25 + +/* Disk specific status */ + +#define DTE_SCYL 19 /* cylinder */ +#define DTE_VSNL 20 /* vol ser # */ +#define DTE_VSNH 21 +#define DTE_D1 22 /* dev params */ + +/* Tape specific status */ + +#define DTE_RETR 19 /* retry */ +#define DTE_POSL 20 /* position */ +#define DTE_POSH 21 +#define DTE_FVER 22 /* formatter ver */ + +#define DTE_CIDD_V_MOD 0 /* ctrl model */ +#define DTE_CIDD_V_CLS 8 /* ctrl class */ +#define DTE_VER_V_SVER 0 /* ctrl swre ver */ +#define DTE_VER_V_HVER 8 /* ctrl hwre ver */ +#define DTE_UIDD_V_MOD 0 /* unit model */ +#define DTE_UIDD_V_CLS 8 /* unit class */ +#define DTE_D2_V_SECT 8 +#define DTE_D3_V_SURF 0 +#define DTE_D3_V_CYL 8 + +/* Host bus error log packet - 8 W status */ + +#define HBE_LNT 28 +#define HBE_CIDA 8 /* ctrl ID */ +#define HBE_CIDB 9 +#define HBE_CIDC 10 +#define HBE_CIDD 11 +#define HBE_VER 12 /* ctrl version */ +#define HBE_RSV 13 /* reserved */ +#define HBE_BADL 14 /* bad address */ +#define HBE_BADH 15 + +#define HBE_CIDD_V_MOD 0 /* ctrl model */ +#define HBE_CIDD_V_CLS 8 /* ctrl class */ +#define HBE_VER_V_SVER 0 /* ctrl swre ver */ +#define HBE_VER_V_HVER 8 /* ctrl hwre ver */ + +/* Unit now available attention message - 10 W status, same as + first 10 W of status from get unit status +*/ + +#define UNA_LNT 32 + +#endif diff --git a/src/PDP11/pdp11_rl.cpp b/src/PDP11/pdp11_rl.cpp new file mode 100644 index 0000000..38d2a33 --- /dev/null +++ b/src/PDP11/pdp11_rl.cpp @@ -0,0 +1,722 @@ +/* pdp11_rl.c: RL11 (RLV12) cartridge disk simulator + + 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. + + rl RL11(RLV12)/RL01/RL02 cartridge disk + + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 07-Jul-05 RMS Removed extraneous externs + 30-Sep-04 RMS Revised Unibus interface + 04-Jan-04 RMS Changed sim_fsize calling sequence + 19-May-03 RMS Revised for new conditional compilation scheme + 25-Apr-03 RMS Revised for extended file support + 29-Sep-02 RMS Added variable address support to bootstrap + Added vector change/display support + Revised mapping nomenclature + New data structures + 26-Jan-02 RMS Revised bootstrap to conform to M9312 + 06-Jan-02 RMS Revised enable/disable support + 30-Nov-01 RMS Added read only, extended SET/SHOW support + 26-Nov-01 RMS Fixed per-drive error handling + 24-Nov-01 RMS Converted FLG, CAPAC to arrays + 19-Nov-01 RMS Fixed signed/unsigned mismatch in write check + 09-Nov-01 RMS Added bus map, VAX support + 07-Sep-01 RMS Revised device disable and interrupt mechanisms + 20-Aug-01 RMS Added bad block option in attach + 17-Jul-01 RMS Fixed warning from VC++ 6.0 + 26-Apr-01 RMS Added device enable/disable support + 25-Mar-01 RMS Fixed block fill calculation + 15-Feb-01 RMS Corrected bootstrap string + 12-Nov-97 RMS Added bad block table command + 25-Nov-96 RMS Default units to autosize + 29-Jun-96 RMS Added unit disable support + + The RL11 is a four drive cartridge disk subsystem. An RL01 drive + consists of 256 cylinders, each with 2 surfaces containing 40 sectors + of 256 bytes. An RL02 drive has 512 cylinders. The RLV12 is a + controller variant which supports 22b direct addressing. + + The most complicated part of the RL11 controller is the way it does + seeks. Seeking is relative to the current disk address; this requires + keeping accurate track of the current cylinder. The RL11 will not + switch heads or cross cylinders during transfers. + + The RL11 functions in three environments: + + - PDP-11 Q22 systems - the I/O map is one for one, so it's safe to + go through the I/O map + - PDP-11 Unibus 22b systems - the RL11 behaves as an 18b Unibus + peripheral and must go through the I/O map + - VAX Q22 systems - the RL11 must go through the I/O map +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#error "RL11 is not supported on the PDP-10!" + +#elif defined (VM_VAX) /* VAX version */ +#include "sim_defs.h" +#include "vax_defs.h" + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +extern uint32 cpu_opt; +#endif + +/* Constants */ + +#define RL_NUMWD 128 /* words/sector */ +#define RL_NUMSC 40 /* sectors/surface */ +#define RL_NUMSF 2 /* surfaces/cylinder */ +#define RL_NUMCY 256 /* cylinders/drive */ +#define RL_NUMDR 4 /* drives/controller */ +#define RL_MAXFR (1 << 16) /* max transfer */ +#define RL01_SIZE (RL_NUMCY * RL_NUMSF * RL_NUMSC * RL_NUMWD) /* words/drive */ +#define RL02_SIZE (RL01_SIZE * 2) /* words/drive */ + +/* Flags in the unit flags word */ + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* hwre write lock */ +#define UNIT_V_RL02 (UNIT_V_UF + 1) /* RL01 vs RL02 */ +#define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize enable */ +#define UNIT_V_DUMMY (UNIT_V_UF + 3) /* dummy flag */ +#define UNIT_DUMMY (1 << UNIT_V_DUMMY) +#define UNIT_WLK (1u << UNIT_V_WLK) +#define UNIT_RL02 (1u << UNIT_V_RL02) +#define UNIT_AUTO (1u << UNIT_V_AUTO) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protected */ + +/* Parameters in the unit descriptor */ + +#define TRK u3 /* current track */ +#define STAT u4 /* status */ + +/* RLDS, NI = not implemented, * = kept in STAT, ^ = kept in TRK */ + +#define RLDS_LOAD 0 /* no cartridge */ +#define RLDS_LOCK 5 /* lock on */ +#define RLDS_BHO 0000010 /* brushes home NI */ +#define RLDS_HDO 0000020 /* heads out NI */ +#define RLDS_CVO 0000040 /* cover open NI */ +#define RLDS_HD 0000100 /* head select ^ */ +#define RLDS_RL02 0000200 /* RL02 */ +#define RLDS_DSE 0000400 /* drv sel err NI */ +#define RLDS_VCK 0001000 /* vol check * */ +#define RLDS_WGE 0002000 /* wr gate err * */ +#define RLDS_SPE 0004000 /* spin err * */ +#define RLDS_STO 0010000 /* seek time out NI */ +#define RLDS_WLK 0020000 /* wr locked */ +#define RLDS_HCE 0040000 /* hd curr err NI */ +#define RLDS_WDE 0100000 /* wr data err NI */ +#define RLDS_ATT (RLDS_HDO+RLDS_BHO+RLDS_LOCK) /* att status */ +#define RLDS_UNATT (RLDS_CVO+RLDS_LOAD) /* unatt status */ +#define RLDS_ERR (RLDS_WDE+RLDS_HCE+RLDS_STO+RLDS_SPE+RLDS_WGE+ \ + RLDS_VCK+RLDS_DSE) /* errors bits */ + +/* RLCS */ + +#define RLCS_DRDY 0000001 /* drive ready */ +#define RLCS_M_FUNC 0000007 /* function */ +#define RLCS_NOP 0 +#define RLCS_WCHK 1 +#define RLCS_GSTA 2 +#define RLCS_SEEK 3 +#define RLCS_RHDR 4 +#define RLCS_WRITE 5 +#define RLCS_READ 6 +#define RLCS_RNOHDR 7 +#define RLCS_V_FUNC 1 +#define RLCS_M_MEX 03 /* memory extension */ +#define RLCS_V_MEX 4 +#define RLCS_MEX (RLCS_M_MEX << RLCS_V_MEX) +#define RLCS_M_DRIVE 03 +#define RLCS_V_DRIVE 8 +#define RLCS_INCMP 0002000 /* incomplete */ +#define RLCS_CRC 0004000 /* CRC error */ +#define RLCS_HDE 0010000 /* header error */ +#define RLCS_NXM 0020000 /* non-exist memory */ +#define RLCS_DRE 0040000 /* drive error */ +#define RLCS_ERR 0100000 /* error summary */ +#define RLCS_ALLERR (RLCS_ERR+RLCS_DRE+RLCS_NXM+RLCS_HDE+RLCS_CRC+RLCS_INCMP) +#define RLCS_RW 0001776 /* read/write */ +#define GET_FUNC(x) (((x) >> RLCS_V_FUNC) & RLCS_M_FUNC) +#define GET_DRIVE(x) (((x) >> RLCS_V_DRIVE) & RLCS_M_DRIVE) + +/* RLDA */ + +#define RLDA_SK_DIR 0000004 /* direction */ +#define RLDA_GS_CLR 0000010 /* clear errors */ +#define RLDA_SK_HD 0000020 /* head select */ + +#define RLDA_V_SECT 0 /* sector */ +#define RLDA_M_SECT 077 +#define RLDA_V_TRACK 6 /* track */ +#define RLDA_M_TRACK 01777 +#define RLDA_HD0 (0 << RLDA_V_TRACK) +#define RLDA_HD1 (1u << RLDA_V_TRACK) +#define RLDA_V_CYL 7 /* cylinder */ +#define RLDA_M_CYL 0777 +#define RLDA_TRACK (RLDA_M_TRACK << RLDA_V_TRACK) +#define RLDA_CYL (RLDA_M_CYL << RLDA_V_CYL) +#define GET_SECT(x) (((x) >> RLDA_V_SECT) & RLDA_M_SECT) +#define GET_CYL(x) (((x) >> RLDA_V_CYL) & RLDA_M_CYL) +#define GET_TRACK(x) (((x) >> RLDA_V_TRACK) & RLDA_M_TRACK) +#define GET_DA(x) ((GET_TRACK (x) * RL_NUMSC) + GET_SECT (x)) + +/* RLBA */ + +#define RLBA_IMP 0177776 /* implemented */ + +/* RLBAE */ + +#define RLBAE_IMP 0000077 /* implemented */ + +uint16 *rlxb = NULL; /* xfer buffer */ +int32 rlcs = 0; /* control/status */ +int32 rlba = 0; /* memory address */ +int32 rlbae = 0; /* mem addr extension */ +int32 rlda = 0; /* disk addr */ +int32 rlmp = 0, rlmp1 = 0, rlmp2 = 0; /* mp register queue */ +int32 rl_swait = 10; /* seek wait */ +int32 rl_rwait = 10; /* rotate wait */ +int32 rl_stopioe = 1; /* stop on error */ +AUTO_INIT_DEVLOCK(rl_lock); /* device structures lock */ + +t_stat rl_rd (int32 *data, int32 PA, int32 access); +t_stat rl_wr (int32 data, int32 PA, int32 access); +t_stat rl_svc (RUN_SVC_DECL, UNIT *uptr); +t_stat rl_reset (DEVICE *dptr); +void rl_set_done (int32 error); +t_stat rl_boot (int32 unitno, DEVICE *dptr); +t_stat rl_attach (UNIT *uptr, char *cptr); +t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* RL11 data structures + + rl_dev RL device descriptor + rl_unit RL unit list + rl_reg RL register list + rl_mod RL modifier list +*/ + +DIB rl_dib = { + IOBA_RL, IOLN_RL, &rl_rd, &rl_wr, + 1, IVCL (RL), VEC_RL, { NULL } }; + +UNIT* rl_unit[] = { + UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_AUTO, RL01_SIZE), + UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_AUTO, RL01_SIZE), + UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_AUTO, RL01_SIZE), + UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_AUTO, RL01_SIZE) + }; + +REG rl_reg[] = { + { GRDATA_GBL (RLCS, rlcs, DEV_RDX, 16, 0) }, + { GRDATA_GBL (RLDA, rlda, DEV_RDX, 16, 0) }, + { GRDATA_GBL (RLBA, rlba, DEV_RDX, 16, 0) }, + { GRDATA_GBL (RLBAE, rlbae, DEV_RDX, 6, 0) }, + { GRDATA_GBL (RLMP, rlmp, DEV_RDX, 16, 0) }, + { GRDATA_GBL (RLMP1, rlmp1, DEV_RDX, 16, 0) }, + { GRDATA_GBL (RLMP2, rlmp2, DEV_RDX, 16, 0) }, + { IRDATA_DEV (INT, IVCL (RL)) }, + { FLDATA_GBL (ERR, rlcs, CSR_V_ERR) }, + { FLDATA_GBL (DONE, rlcs, CSR_V_DONE) }, + { FLDATA_GBL (IE, rlcs, CSR_V_IE) }, + { DRDATA_GBL (STIME, rl_swait, 24), PV_LEFT }, + { DRDATA_GBL (RTIME, rl_rwait, 24), PV_LEFT }, + { URDATA_GBL (CAPAC, rl_unit, 0, capac, 10, T_ADDR_W, 0, RL_NUMDR, PV_LEFT + REG_HRO) }, + { FLDATA_GBL (STOP_IOE, rl_stopioe, 0) }, + { GRDATA_GBL (DEVADDR, rl_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA_GBL (DEVVEC, rl_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB rl_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_DUMMY, 0, NULL, "BADBLOCK", &rl_set_bad }, + { (UNIT_RL02+UNIT_ATT), UNIT_ATT, "RL01", NULL, NULL }, + { (UNIT_RL02+UNIT_ATT), (UNIT_RL02+UNIT_ATT), "RL02", NULL, NULL }, + { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), 0, "RL01", NULL, NULL }, + { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), UNIT_RL02, "RL02", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_RL02), 0, NULL, "RL01", &rl_set_size }, + { (UNIT_AUTO+UNIT_RL02), UNIT_RL02, NULL, "RL02", &rl_set_size }, + { MTAB_XTD|MTAB_VDV, 010, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEVICE rl_dev = { + "RL", rl_unit, rl_reg, rl_mod, + RL_NUMDR, DEV_RDX, 24, 1, DEV_RDX, 16, + NULL, NULL, &rl_reset, + &rl_boot, &rl_attach, NULL, + &rl_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS + }; + +/* I/O dispatch routines, I/O addresses 17774400 - 17774407 + + 17774400 RLCS read/write + 17774402 RLBA read/write + 17774404 RLDA read/write + 17774406 RLMP read/write + 17774410 RLBAE read/write +*/ + +t_stat rl_rd (int32 *data, int32 PA, int32 access) +{ +AUTO_LOCK(rl_lock); +UNIT *uptr; + +switch ((PA >> 1) & 07) { /* decode PA<2:1> */ + + case 0: /* RLCS */ + rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); + if (rlcs & RLCS_ALLERR) + rlcs = rlcs | RLCS_ERR; + uptr = rl_dev.units[GET_DRIVE (rlcs)]; + if (sim_is_active (uptr)) + rlcs = rlcs & ~RLCS_DRDY; + else rlcs = rlcs | RLCS_DRDY; /* see if ready */ + *data = rlcs; + break; + + case 1: /* RLBA */ + *data = rlba & RLBA_IMP; + break; + + case 2: /* RLDA */ + *data = rlda; + break; + + case 3: /* RLMP */ + *data = rlmp; + rlmp = rlmp1; /* ripple data */ + rlmp1 = rlmp2; + break; + + case 4: /* RLBAE */ + if (UNIBUS) /* not in RL11 */ + return SCPE_NXM; + *data = rlbae & RLBAE_IMP; + break; + } /* end switch */ + +return SCPE_OK; +} + +t_stat rl_wr (int32 data, int32 PA, int32 access) +{ +AUTO_LOCK(rl_lock); +int32 curr, offs, newc, maxc; +UNIT *uptr; + +switch ((PA >> 1) & 07) { /* decode PA<2:1> */ + + case 0: /* RLCS */ + rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); + if (rlcs & RLCS_ALLERR) + rlcs = rlcs | RLCS_ERR; + uptr = rl_dev.units[GET_DRIVE (data)]; /* get new drive */ + if (sim_is_active (uptr)) + rlcs = rlcs & ~RLCS_DRDY; + else rlcs = rlcs | RLCS_DRDY; /* see if ready */ + + if (access == WRITEB) + data = (PA & 1)? (rlcs & 0377) | (data << 8): (rlcs & ~0377) | data; + rlcs = (rlcs & ~RLCS_RW) | (data & RLCS_RW); + rlbae = (rlbae & ~RLCS_M_MEX) | ((rlcs >> RLCS_V_MEX) & RLCS_M_MEX); + if (data & CSR_DONE) { /* ready set? */ + if ((data & CSR_IE) == 0) + CLR_INT (RL); + else if ((rlcs & (CSR_DONE + CSR_IE)) == CSR_DONE) + SET_INT (RL); + return SCPE_OK; + } + + CLR_INT (RL); /* clear interrupt */ + rlcs = rlcs & ~RLCS_ALLERR; /* clear errors */ + switch (GET_FUNC (rlcs)) { /* case on RLCS<3:1> */ + case RLCS_NOP: /* nop */ + rl_set_done (0); + break; + case RLCS_SEEK: /* seek */ + curr = GET_CYL (uptr->TRK); /* current cylinder */ + offs = GET_CYL (rlda); /* offset */ + if (rlda & RLDA_SK_DIR) { /* in or out? */ + newc = curr + offs; /* out */ + maxc = (uptr->flags & UNIT_RL02)? + RL_NUMCY * 2: RL_NUMCY; + if (newc >= maxc) + newc = maxc - 1; + } + else { + newc = curr - offs; /* in */ + if (newc < 0) + newc = 0; + } + uptr->TRK = (newc << RLDA_V_CYL) | /* put on track */ + ((rlda & RLDA_SK_HD)? RLDA_HD1: RLDA_HD0); + sim_activate (uptr, rl_swait * abs (newc - curr)); + break; + default: /* data transfer */ + sim_activate (uptr, rl_swait); /* activate unit */ + break; + } /* end switch func */ + break; /* end case RLCS */ + + case 1: /* RLBA */ + if (access == WRITEB) + data = (PA & 1)? (rlba & 0377) | (data << 8): (rlba & ~0377) | data; + rlba = data & RLBA_IMP; + break; + + case 2: /* RLDA */ + if (access == WRITEB) + data = (PA & 1)? (rlda & 0377) | (data << 8): (rlda & ~0377) | data; + rlda = data; + break; + + case 3: /* RLMP */ + if (access == WRITEB) + data = (PA & 1)? (rlmp & 0377) | (data << 8): (rlmp & ~0377) | data; + rlmp = rlmp1 = rlmp2 = data; + break; + + case 4: /* RLBAE */ + if (UNIBUS) /* not in RL11 */ + return SCPE_NXM; + if (PA & 1) + return SCPE_OK; + rlbae = data & RLBAE_IMP; + rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); + break; + } /* end switch */ + +return SCPE_OK; +} + +/* Service unit timeout + + If seek in progress, complete seek command + Else complete data transfer command + + The unit control block contains the function and cylinder for + the current command. +*/ + +t_stat rl_svc (RUN_SVC_DECL, UNIT *uptr) +{ +AUTO_LOCK(rl_lock); +RUN_SVC_CHECK_CANCELLED(uptr); +int32 err, wc, maxwc, t; +int32 i, func, da, awc; +uint32 ma; +uint16 comp; + +func = GET_FUNC (rlcs); /* get function */ +if (func == RLCS_GSTA) { /* get status */ + if (rlda & RLDA_GS_CLR) + uptr->STAT = uptr->STAT & ~RLDS_ERR; + rlmp = uptr->STAT | (uptr->TRK & RLDS_HD) | + ((uptr->flags & UNIT_ATT)? RLDS_ATT: RLDS_UNATT); + if (uptr->flags & UNIT_RL02) + rlmp = rlmp | RLDS_RL02; + if (uptr->flags & UNIT_WPRT) + rlmp = rlmp | RLDS_WLK; + rlmp2 = rlmp1 = rlmp; + rl_set_done (0); /* done */ + return SCPE_OK; + } + +if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ + rlcs = rlcs & ~RLCS_DRDY; /* clear drive ready */ + uptr->STAT = uptr->STAT | RLDS_SPE; /* spin error */ + rl_set_done (RLCS_ERR | RLCS_INCMP); /* flag error */ + return IORETURN (rl_stopioe, SCPE_UNATT); + } + +if ((func == RLCS_WRITE) && (uptr->flags & UNIT_WPRT)) { + uptr->STAT = uptr->STAT | RLDS_WGE; /* write and locked */ + rl_set_done (RLCS_ERR | RLCS_DRE); + return SCPE_OK; + } + +if (func == RLCS_SEEK) { /* seek? */ + rl_set_done (0); /* done */ + return SCPE_OK; + } + +if (func == RLCS_RHDR) { /* read header? */ + rlmp = (uptr->TRK & RLDA_TRACK) | GET_SECT (rlda); + rlmp1 = rlmp2 = 0; + rl_set_done (0); /* done */ + return SCPE_OK; + } + +if (((func != RLCS_RNOHDR) && ((uptr->TRK & RLDA_CYL) != (rlda & RLDA_CYL))) + || (GET_SECT (rlda) >= RL_NUMSC)) { /* bad cyl or sector? */ + rl_set_done (RLCS_ERR | RLCS_HDE | RLCS_INCMP); /* wrong cylinder? */ + return SCPE_OK; + } + +ma = (rlbae << 16) | rlba; /* get mem addr */ +da = GET_DA (rlda) * RL_NUMWD; /* get disk addr */ +wc = 0200000 - rlmp; /* get true wc */ + +maxwc = (RL_NUMSC - GET_SECT (rlda)) * RL_NUMWD; /* max transfer */ +if (wc > maxwc) /* track overrun? */ + wc = maxwc; +err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET); + +if ((func >= RLCS_READ) && (err == 0)) { /* read (no hdr)? */ + i = (int32) fxread (rlxb, sizeof (int16), wc, uptr->fileref); + err = ferror (uptr->fileref); + for ( ; i < wc; i++) /* fill buffer */ + rlxb[i] = 0; + if (t = Map_WriteW (RUN_PASS, ma, wc << 1, rlxb)) { /* store buffer */ + rlcs = rlcs | RLCS_ERR | RLCS_NXM; /* nxm */ + wc = wc - t; /* adjust wc */ + } + } /* end read */ + +if ((func == RLCS_WRITE) && (err == 0)) { /* write? */ + if (t = Map_ReadW (RUN_PASS, ma, wc << 1, rlxb)) { /* fetch buffer */ + rlcs = rlcs | RLCS_ERR | RLCS_NXM; /* nxm */ + wc = wc - t; /* adj xfer lnt */ + } + if (wc) { /* any xfer? */ + awc = (wc + (RL_NUMWD - 1)) & ~(RL_NUMWD - 1); /* clr to */ + for (i = wc; i < awc; i++) /* end of blk */ + rlxb[i] = 0; + fxwrite (rlxb, sizeof (int16), awc, uptr->fileref); + err = ferror (uptr->fileref); + } + } /* end write */ + +if ((func == RLCS_WCHK) && (err == 0)) { /* write check? */ + i = (int32) fxread (rlxb, sizeof (int16), wc, uptr->fileref); + err = ferror (uptr->fileref); + for ( ; i < wc; i++) /* fill buffer */ + rlxb[i] = 0; + awc = wc; /* save wc */ + for (wc = 0; (err == 0) && (wc < awc); wc++) { /* loop thru buf */ + if (Map_ReadW (RUN_PASS, ma + (wc << 1), 2, &comp)) { /* mem wd */ + rlcs = rlcs | RLCS_ERR | RLCS_NXM; /* nxm */ + break; + } + if (comp != rlxb[wc]) /* check to buf */ + rlcs = rlcs | RLCS_ERR | RLCS_CRC; + } /* end for */ + } /* end wcheck */ + +rlmp = (rlmp + wc) & 0177777; /* final word count */ +if (rlmp != 0) /* completed? */ + rlcs = rlcs | RLCS_ERR | RLCS_INCMP; +ma = ma + (wc << 1); /* final byte addr */ +rlbae = (ma >> 16) & RLBAE_IMP; /* upper 6b */ +rlba = ma & RLBA_IMP; /* lower 16b */ +rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); +rlda = rlda + ((wc + (RL_NUMWD - 1)) / RL_NUMWD); +rl_set_done (0); + +if (err != 0) { /* error? */ + smp_perror ("RL I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Set done and possibly errors */ + +void rl_set_done (int32 status) +{ +rlcs = rlcs | status | CSR_DONE; /* set done */ +if (rlcs & CSR_IE) + SET_INT (RL); +else CLR_INT (RL); +return; +} + +/* Device reset + + Note that the RL11 does NOT recalibrate its drives on RESET +*/ + +t_stat rl_reset (DEVICE *dptr) +{ +AUTO_LOCK(rl_lock); +sim_bind_devunits_lock(&rl_dev, rl_lock); +int32 i; +UNIT *uptr; + +rlcs = CSR_DONE; +rlda = rlba = rlbae = rlmp = rlmp1 = rlmp2 = 0; +CLR_INT (RL); +for (i = 0; i < RL_NUMDR; i++) { + uptr = rl_dev.units[i]; + sim_cancel (uptr); + uptr->STAT = 0; + } +if (rlxb == NULL) + rlxb = (uint16 *) calloc (RL_MAXFR, sizeof (uint16)); +if (rlxb == NULL) + return SCPE_MEM; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat rl_attach (UNIT *uptr, char *cptr) +{ +uint32 p; +t_stat r; + +uptr->capac = (uptr->flags & UNIT_RL02)? RL02_SIZE: RL01_SIZE; +r = attach_unit (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) /* error? */ + return r; +uptr->TRK = 0; /* cylinder 0 */ +uptr->STAT = RLDS_VCK; /* new volume */ +if ((p = sim_fsize (uptr->fileref)) == 0) { /* new disk image? */ + if (uptr->flags & UNIT_RO) /* if ro, done */ + return SCPE_OK; + return pdp11_bad_block (uptr, RL_NUMSC, RL_NUMWD); + } +if ((uptr->flags & UNIT_AUTO) == 0) /* autosize? */ + return SCPE_OK; +if (p > (RL01_SIZE * sizeof (int16))) { + uptr->flags = uptr->flags | UNIT_RL02; + uptr->capac = RL02_SIZE; + } +else { + uptr->flags = uptr->flags & ~UNIT_RL02; + uptr->capac = RL01_SIZE; + } +return SCPE_OK; +} + +/* Set size routine */ + +t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; +uptr->capac = (val & UNIT_RL02)? RL02_SIZE: RL01_SIZE; +return SCPE_OK; +} + +/* Set bad block routine */ + +t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +return pdp11_bad_block (uptr, RL_NUMSC, RL_NUMWD); +} + +/* Device bootstrap */ + +#if defined (VM_PDP11) + +#define BOOT_START 02000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_UNIT (BOOT_START + 010) /* unit number */ +#define BOOT_CSR (BOOT_START + 020) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 0042114, /* "LD" */ + 0012706, BOOT_START, /* MOV #boot_start, SP */ + 0012700, 0000000, /* MOV #unit, R0 */ + 0010003, /* MOV R0, R3 */ + 0000303, /* SWAB R3 */ + 0012701, 0174400, /* MOV #RLCS, R1 ; csr */ + 0012761, 0000013, 0000004, /* MOV #13, 4(R1) ; clr err */ + 0052703, 0000004, /* BIS #4, R3 ; unit+gstat */ + 0010311, /* MOV R3, (R1) ; issue cmd */ + 0105711, /* TSTB (R1) ; wait */ + 0100376, /* BPL .-2 */ + 0105003, /* CLRB R3 */ + 0052703, 0000010, /* BIS #10, R3 ; unit+rdhdr */ + 0010311, /* MOV R3, (R1) ; issue cmd */ + 0105711, /* TSTB (R1) ; wait */ + 0100376, /* BPL .-2 */ + 0016102, 0000006, /* MOV 6(R1), R2 ; get hdr */ + 0042702, 0000077, /* BIC #77, R2 ; clr sector */ + 0005202, /* INC R2 ; magic bit */ + 0010261, 0000004, /* MOV R2, 4(R1) ; seek to 0 */ + 0105003, /* CLRB R3 */ + 0052703, 0000006, /* BIS #6, R3 ; unit+seek */ + 0010311, /* MOV R3, (R1) ; issue cmd */ + 0105711, /* TSTB (R1) ; wait */ + 0100376, /* BPL .-2 */ + 0005061, 0000002, /* CLR 2(R1) ; clr ba */ + 0005061, 0000004, /* CLR 4(R1) ; clr da */ + 0012761, 0177000, 0000006, /* MOV #-512., 6(R1) ; set wc */ + 0105003, /* CLRB R3 */ + 0052703, 0000014, /* BIS #14, R3 ; unit+read */ + 0010311, /* MOV R3, (R1) ; issue cmd */ + 0105711, /* TSTB (R1) ; wait */ + 0100376, /* BPL .-2 */ + 0042711, 0000377, /* BIC #377, (R1) */ + 0005002, /* CLR R2 */ + 0005003, /* CLR R3 */ + 0012704, BOOT_START+020, /* MOV #START+20, R4 */ + 0005005, /* CLR R5 */ + 0005007 /* CLR PC */ + }; + +t_stat rl_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern uint16 *M; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) + M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & RLCS_M_DRIVE; +M[BOOT_CSR >> 1] = rl_dib.ba & DMASK; +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} + +#else + +t_stat rl_boot (int32 unitno, DEVICE *dptr) +{ +//AUTO_LOCK(rl_lock); +return SCPE_NOFNC; +} + +#endif + diff --git a/src/PDP11/pdp11_rq.cpp b/src/PDP11/pdp11_rq.cpp new file mode 100644 index 0000000..1dc3cd7 --- /dev/null +++ b/src/PDP11/pdp11_rq.cpp @@ -0,0 +1,3179 @@ +/* pdp11_rq.c: MSCP disk controller simulator + + Copyright (c) 2002-2010, Robert M Supnik + Derived from work by Stephen F. Shirron + + 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. + + rq RQDX3 disk controller + + 07-Mar-11 MP Added working behaviors for removable device types. + This allows physical CDROM's to come online and be + ejected. + 02-Mar-11 MP Fixed missing information from save/restore which + caused operations to not complete correctly after + a restore until the OS reset the controller. + 02-Feb-11 MP Added Autosize support to rq_attach + 28-Jan-11 MP Adopted use of sim_disk disk I/O library + - added support for the multiple formats sim_disk + provides (SimH, RAW, and VHD) + - adjusted to potentially leverage asynch I/O when + available + - Added differing detailed debug output via sim_debug + 14-Jan-09 JH Added support for RD32 disc drive + 18-Jun-07 RMS Added UNIT_IDLE flag to timer thread + 31-Oct-05 RMS Fixed address width for large files + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 22-Jul-05 RMS Fixed warning from Solaris C (from Doug Gwyn) + 17-Jan-05 RMS Added more RA and RD disks + 31-Oct-04 RMS Added -L switch (LBNs) to RAUSER size specification + 01-Oct-04 RMS Revised Unibus interface + Changed to identify as UDA50 in Unibus configurations + Changed width to be 16b in all configurations + Changed default timing for VAX + 24-Jul-04 RMS VAX controllers luns start with 0 (from Andreas Cejna) + 05-Feb-04 RMS Revised for file I/O library + 25-Jan-04 RMS Revised for device debug support + 12-Jan-04 RMS Fixed bug in interrupt control (found by Tom Evans) + 07-Oct-03 RMS Fixed problem with multiple RAUSER drives + 17-Sep-03 RMS Fixed MB to LBN conversion to be more accurate + 11-Jul-03 RMS Fixed bug in user disk size (found by Chaskiel M Grundman) + 19-May-03 RMS Revised for new conditional compilation scheme + 25-Apr-03 RMS Revised for extended file support + 14-Mar-03 RMS Fixed variable size interaction with save/restore + 27-Feb-03 RMS Added user-defined drive support + 26-Feb-03 RMS Fixed bug in vector calculation for VAXen + 22-Feb-03 RMS Fixed ordering bug in queue process + 12-Oct-02 RMS Added multicontroller support + 29-Sep-02 RMS Changed addressing to 18b in Unibus mode + Added variable address support to bootstrap + Added vector display support + Fixed status code in HBE error log + Consolidated MSCP/TMSCP header file + New data structures + 16-Aug-02 RMS Removed unused variables (found by David Hittner) + 04-May-02 RMS Fixed bug in polling loop for queued operations + 26-Mar-02 RMS Fixed bug, reset routine cleared UF_WPH + 09-Mar-02 RMS Adjusted delays for M+ timing bugs + 04-Mar-02 RMS Added delays to initialization for M+, RSTS/E + 16-Feb-02 RMS Fixed bugs in host timeout logic, boot + 26-Jan-02 RMS Revised bootstrap to conform to M9312 + 06-Jan-02 RMS Revised enable/disable support + 30-Dec-01 RMS Revised show routines + 19-Dec-01 RMS Added bigger drives + 17-Dec-01 RMS Added queue process +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#error "RQDX3 not supported on PDP-10!" + +#elif defined (VM_VAX) /* VAX version */ +#include "sim_defs.h" +#include "vax_defs.h" +#define RQ_QTIME 100 +#define RQ_XTIME 200 +#define OLDPC fault_PC + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#define RQ_QTIME 200 +#define RQ_XTIME 500 +#define OLDPC MMR2 +extern int32 MMR2; +extern int32 cpu_opt; +#endif + +#if !defined (RQ_NUMCT) +#define RQ_NUMCT 4 +#elif (RQ_NUMCT > 4) +#error "Assertion failure: RQ_NUMCT exceeds 4" +#endif + +#include "pdp11_uqssp.h" +#include "pdp11_mscp.h" +#include "sim_disk.h" + +#define UF_MSK (UF_CMR|UF_CMW) /* settable flags */ + +#define RQ_SH_MAX 24 /* max display wds */ +#define RQ_SH_PPL 8 /* wds per line */ +#define RQ_SH_DPL 4 /* desc per line */ +#define RQ_SH_RI 001 /* show rings */ +#define RQ_SH_FR 002 /* show free q */ +#define RQ_SH_RS 004 /* show resp q */ +#define RQ_SH_UN 010 /* show unit q's */ +#define RQ_SH_ALL 017 /* show all */ + +#define RQ_CLASS 1 /* RQ class */ +#define RQU_UQPM 6 /* UB port model */ +#define RQQ_UQPM 19 /* QB port model */ +#define RQ_UQPM (UNIBUS? RQU_UQPM: RQQ_UQPM) +#define RQU_MODEL 6 /* UB MSCP ctrl model */ +#define RQQ_MODEL 19 /* QB MSCP ctrl model */ +#define RQ_MODEL (UNIBUS? RQU_MODEL: RQQ_MODEL) +#define RQ_HVER 1 /* hardware version */ +#define RQ_SVER 3 /* software version */ +#define RQ_DHTMO 60 /* def host timeout */ +/* + * Allow longer controller timeout interval for multiprocessor system, + * to avoid disruption that may be caused by thread preemption. + * We do not care about similarly increasing RQ_DHTMO because VMS sets + * host timeeout to 0 (no timeout) anyway. + */ +#if defined(VM_VAX_MP) +# define RQ_DCTMO 240 /* def ctrl timeout */ +#else +# define RQ_DCTMO 120 /* def ctrl timeout */ +#endif +#define RQ_NUMDR 4 /* # drives */ +#define RQ_NUMBY 512 /* bytes per block */ +#define RQ_MAXFR (1 << 16) /* max xfer */ + +#define UNIT_V_ONL (UNIT_V_UF + 0) /* online */ +#define UNIT_V_WLK (UNIT_V_UF + 1) /* hwre write lock */ +#define UNIT_V_ATP (UNIT_V_UF + 2) /* attn pending */ +#define UNIT_V_DTYPE (UNIT_V_UF + 3) /* drive type */ +#define UNIT_M_DTYPE 0x1F +#define UNIT_V_NOAUTO (UNIT_V_UF + 8) /* noautosize */ +#define UNIT_ONL (1 << UNIT_V_ONL) +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_ATP (1 << UNIT_V_ATP) +#define UNIT_NOAUTO (1 << UNIT_V_NOAUTO) +#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) +#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) +#define cpkt u3 /* current packet */ +#define pktq u4 /* packet queue */ +#define uf buf /* settable unit flags */ +#define cnum wait /* controller index */ +#define io_status u5 /* io status from callback */ +#define io_complete u6 /* io completion flag */ +#define rqxb filebuf /* xfer buffer */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */ +#define RQ_RMV(u) ((drv_tab[GET_DTYPE (u->flags)].flgs & RQDF_RMV)? \ + UF_RMV: 0) +#define RQ_WPH(u) (((drv_tab[GET_DTYPE (u->flags)].flgs & RQDF_RO) || \ + (u->flags & UNIT_WPRT) || sim_disk_wrp (u))? UF_WPH: 0) + +#define CST_S1 0 /* init stage 1 */ +#define CST_S1_WR 1 /* stage 1 wrap */ +#define CST_S2 2 /* init stage 2 */ +#define CST_S3 3 /* init stage 3 */ +#define CST_S3_PPA 4 /* stage 3 sa wait */ +#define CST_S3_PPB 5 /* stage 3 ip wait */ +#define CST_S4 6 /* stage 4 */ +#define CST_UP 7 /* online */ +#define CST_DEAD 8 /* fatal error */ + +#define ERR 0 /* must be SCPE_OK! */ +#define OK 1 + +#define RQ_TIMER (RQ_NUMDR) +#define RQ_QUEUE (RQ_TIMER + 1) + +/* Internal packet management. The real RQDX3 manages its packets as true + linked lists. However, use of actual addresses in structures won't work + with save/restore. Accordingly, the packets are an arrayed structure, + and links are actually subscripts. To minimize complexity, packet[0] + is not used (0 = end of list), and the number of packets must be a power + of two. +*/ + +#define RQ_NPKTS 32 /* # packets (pwr of 2) */ +#define RQ_M_NPKTS (RQ_NPKTS - 1) /* mask */ +#define RQ_PKT_SIZE_W 32 /* payload size (wds) */ +#define RQ_PKT_SIZE (RQ_PKT_SIZE_W * sizeof (int16)) + +struct rqpkt { + int16 link; /* link to next */ + uint16 d[RQ_PKT_SIZE_W]; /* data */ + }; + +/* Packet payload extraction and insertion; cp defines controller */ + +#define GETP(p,w,f) ((cp->pak[p].d[w] >> w##_V_##f) & w##_M_##f) +#define GETP32(p,w) (((uint32) cp->pak[p].d[w]) | \ + (((uint32) cp->pak[p].d[(w)+1]) << 16)) +#define PUTP32(p,w,x) cp->pak[p].d[w] = (x) & 0xFFFF; \ + cp->pak[p].d[(w)+1] = ((x) >> 16) & 0xFFFF + +/* Disk formats. An RQDX3 disk consists of the following regions: + + XBNs Extended blocks - contain information about disk format, + also holds track being reformatted during bad block repl. + Size = sectors/track + 1, replicated 3 times. + DBNs Diagnostic blocks - used by diagnostics. Sized to pad + out the XBNs to a cylinder boundary. + LBNs Logical blocks - contain user information. + RCT Replacement control table - first block contains status, + second contains data from block being replaced, remaining + contain information about replaced bad blocks. + Size = RBNs/128 + 3, replicated 4-8 times. + RBNs Replacement blocks - used to replace bad blocks. + + The simulator does not need to perform bad block replacement; the + information below is for simulating RCT reads, if required. + + Note that an RA drive has a different order: LBNs, RCT, XBN, DBN; + the RBNs are spare blocks at the end of every track. +*/ + +#define RCT_OVHD 2 /* #ovhd blks */ +#define RCT_ENTB 128 /* entries/blk */ +#define RCT_END 0x80000000 /* marks RCT end */ + +/* The RQDX3 supports multiple disk drive types (x = not implemented): + + type sec surf cyl tpg gpc RCT LBNs + + RX50 10 1 80 5 16 - 800 + RX33 15 2 80 2 1 - 2400 + RD51 18 4 306 4 1 36*4 21600 + RD31 17 4 615 4 1 3*8 41560 + RD52 17 8 512 8 1 4*8 60480 + RD32 17 6 820 6 1 4*8 83204 +x RD33 17 7 1170 ? ? ? 138565 + RD53 17 7 1024 7 1 5*8 138672 + RD54 17 15 1225 15 1 7*8 311200 + + The simulator also supports larger drives that only existed + on SDI controllers. + + RA60 42(+1) 6 1600 6 1 1008 400176 +x RA70 33(+1) 11 1507+ 11 1 ? 547041 + RA81 51(+1) 14 1258 14 1 2856 891072 + RA82 57(+1) 15 1435 15 1 3420 1216665 + RA71 51(+1) 14 1921 14 1 1428 1367310 + RA72 51(+1) 20 1921 20 1 2040 1953300 + RA90 69(+1) 13 2656 13 1 1794 2376153 + RA92 73(+1) 13 3101 13 1 949 2940951 +x RA73 70(+1) 21 2667+ 21 1 ? 3920490 + + Each drive can be a different type. The drive field in the + unit flags specified the drive type and thus, indirectly, + the drive size. +*/ + +#define RQDF_RMV 01 /* removable */ +#define RQDF_RO 02 /* read only */ +#define RQDF_SDI 04 /* SDI drive */ + +#define RX50_DTYPE 0 +#define RX50_SECT 10 +#define RX50_SURF 1 +#define RX50_CYL 80 +#define RX50_TPG 5 +#define RX50_GPC 16 +#define RX50_XBN 0 +#define RX50_DBN 0 +#define RX50_LBN 800 +#define RX50_RCTS 0 +#define RX50_RCTC 0 +#define RX50_RBN 0 +#define RX50_MOD 7 +#define RX50_MED 0x25658032 +#define RX50_FLGS RQDF_RMV + +#define RX33_DTYPE 1 +#define RX33_SECT 15 +#define RX33_SURF 2 +#define RX33_CYL 80 +#define RX33_TPG 2 +#define RX33_GPC 1 +#define RX33_XBN 0 +#define RX33_DBN 0 +#define RX33_LBN 2400 +#define RX33_RCTS 0 +#define RX33_RCTC 0 +#define RX33_RBN 0 +#define RX33_MOD 10 +#define RX33_MED 0x25658021 +#define RX33_FLGS RQDF_RMV + +#define RD51_DTYPE 2 +#define RD51_SECT 18 +#define RD51_SURF 4 +#define RD51_CYL 306 +#define RD51_TPG 4 +#define RD51_GPC 1 +#define RD51_XBN 57 +#define RD51_DBN 87 +#define RD51_LBN 21600 +#define RD51_RCTS 36 +#define RD51_RCTC 4 +#define RD51_RBN 144 +#define RD51_MOD 6 +#define RD51_MED 0x25644033 +#define RD51_FLGS 0 + +#define RD31_DTYPE 3 +#define RD31_SECT 17 +#define RD31_SURF 4 +#define RD31_CYL 615 /* last unused */ +#define RD31_TPG RD31_SURF +#define RD31_GPC 1 +#define RD31_XBN 54 +#define RD31_DBN 14 +#define RD31_LBN 41560 +#define RD31_RCTS 3 +#define RD31_RCTC 8 +#define RD31_RBN 100 +#define RD31_MOD 12 +#define RD31_MED 0x2564401F +#define RD31_FLGS 0 + +#define RD52_DTYPE 4 /* Quantum params */ +#define RD52_SECT 17 +#define RD52_SURF 8 +#define RD52_CYL 512 +#define RD52_TPG RD52_SURF +#define RD52_GPC 1 +#define RD52_XBN 54 +#define RD52_DBN 82 +#define RD52_LBN 60480 +#define RD52_RCTS 4 +#define RD52_RCTC 8 +#define RD52_RBN 168 +#define RD52_MOD 8 +#define RD52_MED 0x25644034 +#define RD52_FLGS 0 + +#define RD53_DTYPE 5 +#define RD53_SECT 17 +#define RD53_SURF 8 +#define RD53_CYL 1024 /* last unused */ +#define RD53_TPG RD53_SURF +#define RD53_GPC 1 +#define RD53_XBN 54 +#define RD53_DBN 82 +#define RD53_LBN 138672 +#define RD53_RCTS 5 +#define RD53_RCTC 8 +#define RD53_RBN 280 +#define RD53_MOD 9 +#define RD53_MED 0x25644035 +#define RD53_FLGS 0 + +#define RD54_DTYPE 6 +#define RD54_SECT 17 +#define RD54_SURF 15 +#define RD54_CYL 1225 /* last unused */ +#define RD54_TPG RD54_SURF +#define RD54_GPC 1 +#define RD54_XBN 54 +#define RD54_DBN 201 +#define RD54_LBN 311200 +#define RD54_RCTS 7 +#define RD54_RCTC 8 +#define RD54_RBN 609 +#define RD54_MOD 13 +#define RD54_MED 0x25644036 +#define RD54_FLGS 0 + +#define RA82_DTYPE 7 /* SDI drive */ +#define RA82_SECT 57 /* +1 spare/track */ +#define RA82_SURF 15 +#define RA82_CYL 1435 /* 0-1422 user */ +#define RA82_TPG RA82_SURF +#define RA82_GPC 1 +#define RA82_XBN 3480 /* cyl 1427-1430 */ +#define RA82_DBN 3480 /* cyl 1431-1434 */ +#define RA82_LBN 1216665 /* 57*15*1423 */ +#define RA82_RCTS 3420 /* cyl 1423-1426 */ +#define RA82_RCTC 1 +#define RA82_RBN 21345 /* 1 *15*1423 */ +#define RA82_MOD 11 +#define RA82_MED 0x25641052 +#define RA82_FLGS RQDF_SDI + +#define RRD40_DTYPE 8 +#define RRD40_SECT 128 +#define RRD40_SURF 1 +#define RRD40_CYL 10400 +#define RRD40_TPG RRD40_SURF +#define RRD40_GPC 1 +#define RRD40_XBN 0 +#define RRD40_DBN 0 +#define RRD40_LBN 1331200 +#define RRD40_RCTS 0 +#define RRD40_RCTC 0 +#define RRD40_RBN 0 +#define RRD40_MOD 26 +#define RRD40_MED 0x25652228 +#define RRD40_FLGS (RQDF_RMV | RQDF_RO) + +#define RA72_DTYPE 9 /* SDI drive */ +#define RA72_SECT 51 /* +1 spare/trk */ +#define RA72_SURF 20 +#define RA72_CYL 1921 /* 0-1914 user */ +#define RA72_TPG RA72_SURF +#define RA72_GPC 1 +#define RA72_XBN 2080 /* cyl 1917-1918? */ +#define RA72_DBN 2080 /* cyl 1920-1921? */ +#define RA72_LBN 1953300 /* 51*20*1915 */ +#define RA72_RCTS 2040 /* cyl 1915-1916? */ +#define RA72_RCTC 1 +#define RA72_RBN 38300 /* 1 *20*1915 */ +#define RA72_MOD 37 +#define RA72_MED 0x25641048 +#define RA72_FLGS RQDF_SDI + +#define RA90_DTYPE 10 /* SDI drive */ +#define RA90_SECT 69 /* +1 spare/trk */ +#define RA90_SURF 13 +#define RA90_CYL 2656 /* 0-2648 user */ +#define RA90_TPG RA90_SURF +#define RA90_GPC 1 +#define RA90_XBN 1820 /* cyl 2651-2652? */ +#define RA90_DBN 1820 /* cyl 2653-2654? */ +#define RA90_LBN 2376153 /* 69*13*2649 */ +#define RA90_RCTS 1794 /* cyl 2649-2650? */ +#define RA90_RCTC 1 +#define RA90_RBN 34437 /* 1 *13*2649 */ +#define RA90_MOD 19 +#define RA90_MED 0x2564105A +#define RA90_FLGS RQDF_SDI + +#define RA92_DTYPE 11 /* SDI drive */ +#define RA92_SECT 73 /* +1 spare/trk */ +#define RA92_SURF 13 +#define RA92_CYL 3101 /* 0-3098 user */ +#define RA92_TPG RA92_SURF +#define RA92_GPC 1 +#define RA92_XBN 174 /* cyl 3100? */ +#define RA92_DBN 788 +#define RA92_LBN 2940951 /* 73*13*3099 */ +#define RA92_RCTS 949 /* cyl 3099? */ +#define RA92_RCTC 1 +#define RA92_RBN 40287 /* 1 *13*3099 */ +#define RA92_MOD 29 +#define RA92_MED 0x2564105C +#define RA92_FLGS RQDF_SDI + +#define RA8U_DTYPE 12 /* user defined */ +#define RA8U_SECT 57 /* from RA82 */ +#define RA8U_SURF 15 +#define RA8U_CYL 1435 /* from RA82 */ +#define RA8U_TPG RA8U_SURF +#define RA8U_GPC 1 +#define RA8U_XBN 0 +#define RA8U_DBN 0 +#define RA8U_LBN 1216665 /* from RA82 */ +#define RA8U_RCTS 400 +#define RA8U_RCTC 8 +#define RA8U_RBN 21345 +#define RA8U_MOD 11 /* RA82 */ +#define RA8U_MED 0x25641052 /* RA82 */ +#define RA8U_FLGS RQDF_SDI +#define RA8U_MINC 10000 /* min cap LBNs */ +#define RA8U_MAXC 4000000 /* max cap LBNs */ +#define RA8U_EMAXC 2000000000 /* ext max cap */ + +#define RA60_DTYPE 13 /* SDI drive */ +#define RA60_SECT 42 /* +1 spare/track */ +#define RA60_SURF 6 +#define RA60_CYL 1600 /* 0-1587 user */ +#define RA60_TPG RA60_SURF +#define RA60_GPC 1 +#define RA60_XBN 1032 /* cyl 1592-1595 */ +#define RA60_DBN 1032 /* cyl 1596-1599 */ +#define RA60_LBN 400176 /* 42*6*1588 */ +#define RA60_RCTS 1008 /* cyl 1588-1591 */ +#define RA60_RCTC 1 +#define RA60_RBN 9528 /* 1 *6*1588 */ +#define RA60_MOD 4 +#define RA60_MED 0x22A4103C +#define RA60_FLGS (RQDF_RMV | RQDF_SDI) + +#define RA81_DTYPE 14 /* SDI drive */ +#define RA81_SECT 51 /* +1 spare/track */ +#define RA81_SURF 14 +#define RA81_CYL 1258 /* 0-1247 user */ +#define RA81_TPG RA81_SURF +#define RA81_GPC 1 +#define RA81_XBN 2436 /* cyl 1252-1254? */ +#define RA81_DBN 2436 /* cyl 1255-1256? */ +#define RA81_LBN 891072 /* 51*14*1248 */ +#define RA81_RCTS 2856 /* cyl 1248-1251? */ +#define RA81_RCTC 1 +#define RA81_RBN 17472 /* 1 *14*1248 */ +#define RA81_MOD 5 +#define RA81_MED 0x25641051 +#define RA81_FLGS RQDF_SDI + +#define RA71_DTYPE 15 /* SDI drive */ +#define RA71_SECT 51 /* +1 spare/track */ +#define RA71_SURF 14 +#define RA71_CYL 1921 /* 0-1914 user */ +#define RA71_TPG RA71_SURF +#define RA71_GPC 1 +#define RA71_XBN 1456 /* cyl 1917-1918? */ +#define RA71_DBN 1456 /* cyl 1919-1920? */ +#define RA71_LBN 1367310 /* 51*14*1915 */ +#define RA71_RCTS 1428 /* cyl 1915-1916? */ +#define RA71_RCTC 1 +#define RA71_RBN 26810 /* 1 *14*1915 */ +#define RA71_MOD 40 +#define RA71_MED 0x25641047 +#define RA71_FLGS RQDF_SDI + +#define RD32_DTYPE 16 +#define RD32_SECT 17 +#define RD32_SURF 6 +#define RD32_CYL 820 +#define RD32_TPG RD32_SURF +#define RD32_GPC 1 +#define RD32_XBN 54 +#define RD32_DBN 48 +#define RD32_LBN 83204 +#define RD32_RCTS 4 +#define RD32_RCTC 8 +#define RD32_RBN 200 +#define RD32_MOD 15 +#define RD32_MED 0x25644020 +#define RD32_FLGS 0 + +struct drvtyp { + int32 sect; /* sectors */ + int32 surf; /* surfaces */ + int32 cyl; /* cylinders */ + int32 tpg; /* trk/grp */ + int32 gpc; /* grp/cyl */ + int32 xbn; /* XBN size */ + int32 dbn; /* DBN size */ + uint32 lbn; /* LBN size */ + int32 rcts; /* RCT size */ + int32 rctc; /* RCT copies */ + int32 rbn; /* RBNs */ + int32 mod; /* MSCP model */ + int32 med; /* MSCP media */ + int32 flgs; /* flags */ + char *name; /* name */ + }; + +#define RQ_DRV(d) \ + d##_SECT, d##_SURF, d##_CYL, d##_TPG, \ + d##_GPC, d##_XBN, d##_DBN, d##_LBN, \ + d##_RCTS, d##_RCTC, d##_RBN, d##_MOD, \ + d##_MED, d##_FLGS +#define RQ_SIZE(d) (d##_LBN * RQ_NUMBY) + +static struct drvtyp drv_tab[] = { + { RQ_DRV (RX50), "RX50" }, { RQ_DRV (RX33), "RX33" }, + { RQ_DRV (RD51), "RD51" }, { RQ_DRV (RD31), "RD31" }, + { RQ_DRV (RD52), "RD52" }, { RQ_DRV (RD53), "RD53" }, + { RQ_DRV (RD54), "RD54" }, { RQ_DRV (RA82), "RA82" }, + { RQ_DRV (RRD40), "RRD40" }, { RQ_DRV (RA72), "RA72" }, + { RQ_DRV (RA90), "RA90" }, { RQ_DRV (RA92), "RA92" }, + { RQ_DRV (RA8U), "RAUSER" }, { RQ_DRV (RA60), "RA60" }, + { RQ_DRV (RA81), "RA81" }, { RQ_DRV (RA71), "RA71" }, + { RQ_DRV (RD32), "RD32" }, + { 0 } + }; + +extern SMP_FILE *sim_deb; +extern int32 sim_switches; + +static int32 rq_itime = 200; /* init time, except */ +static int32 rq_itime4 = 10; /* stage 4 */ +static int32 rq_qtime = RQ_QTIME; /* queue time */ +static int32 rq_xtime = RQ_XTIME; /* transfer time */ +static smp_interlocked_uint32_var rq_pending_intrs = smp_var_init(0); /* active interrupt count */ + +static void init_rq_data() +{ + smp_check_aligned(& rq_pending_intrs); +} +ON_INIT_INVOKE(init_rq_data); + +typedef struct +{ + uint32 cnum; /* ctrl number */ + uint32 ubase; /* unit base */ + uint32 sa; /* status, addr */ + uint32 saw; /* written data */ + uint32 s1dat; /* S1 data */ + uint32 comm; /* comm region */ + uint32 csta; /* ctrl state */ + uint32 perr; /* last error */ + uint32 cflgs; /* ctrl flags */ + uint32 irq; /* intr request */ + uint32 prgi; /* purge int */ + uint32 pip; /* poll in progress */ + int32 freq; /* free list */ + int32 rspq; /* resp list */ + uint32 pbsy; /* #busy pkts */ + uint32 credits; /* credits */ + uint32 hat; /* host timer */ + uint32 htmo; /* host timeout */ + struct uq_ring cq; /* cmd ring */ + struct uq_ring rq; /* rsp ring */ + struct rqpkt pak[RQ_NPKTS]; /* packet queue */ +} MSC; + +/* debugging bitmaps */ +#define DBG_TRC 0x0001 /* trace routine calls */ +#define DBG_INI 0x0002 /* display setup/init sequence info */ +#define DBG_REG 0x0004 /* trace read/write registers */ +#define DBG_REQ 0x0008 /* display transfer requests */ +#define DBG_DSK 0x0010 /* display sim_disk activities */ +#define DBG_DAT 0x0020 /* display transfer data */ + +DEBTAB rq_debug[] = { + {"TRACE", DBG_TRC}, + {"INIT", DBG_INI}, + {"REG", DBG_REG}, + {"REQ", DBG_REQ}, + {"DISK", DBG_DSK}, + {"DATA", DBG_DAT}, + {0} +}; + +static char *rq_cmdname[] = { + "", /* 0 */ + "ABO", /* 1 b: abort */ + "GCS", /* 2 b: get command status */ + "GUS", /* 3 b: get unit status */ + "SCC", /* 4 b: set controller char */ + "","","", /* 5-7 */ + "AVL", /* 8 b: available */ + "ONL", /* 9 b: online */ + "SUC", /* 10 b: set unit char */ + "DAP", /* 11 b: det acc paths - nop */ + "","","","", /* 12-15 */ + "ACC", /* 16 b: access */ + "CCD", /* 17 d: compare - nop */ + "ERS", /* 18 b: erase */ + "FLU", /* 19 d: flush - nop */ + "","", /* 20-21 */ + "ERG", /* 22 t: erase gap */ + "","","","","","","","","", /* 23-31 */ + "CMP", /* 32 b: compare */ + "RD", /* 33 b: read */ + "WR", /* 34 b: write */ + "", /* 35 */ + "WTM", /* 36 t: write tape mark */ + "POS", /* 37 t: reposition */ + "","","","","","","","","", /* 38-46 */ + "FMT", /* 47 d: format */ + "","","","","","","","","","","","","","","","", /* 48-63 */ + "AVA", /* 64 b: unit now avail */ + }; + +t_stat rq_rd (int32 *data, int32 PA, int32 access); +t_stat rq_wr (int32 data, int32 PA, int32 access); +t_stat rq_svc (RUN_SVC_DECL, UNIT *uptr); +t_stat rq_tmrsvc (RUN_SVC_DECL, UNIT *uptr); +t_stat rq_quesvc (RUN_SVC_DECL, UNIT *uptr); +t_stat rq_reset (DEVICE *dptr); +t_stat rq_attach (UNIT *uptr, char *cptr); +t_stat rq_detach (UNIT *uptr); +t_stat rq_boot (int32 unitno, DEVICE *dptr); +t_stat rq_set_wlk (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat rq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat rq_show_type (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat rq_show_wlk (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat rq_show_ctrl (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat rq_show_unitq (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); + +t_bool rq_step4 (RUN_DECL, MSC *cp); +t_bool rq_mscp (MSC *cp, int32 pkt, t_bool q); +t_bool rq_abo (MSC *cp, int32 pkt, t_bool q); +t_bool rq_avl (MSC *cp, int32 pkt, t_bool q); +t_bool rq_fmt (MSC *cp, int32 pkt, t_bool q); +t_bool rq_gcs (MSC *cp, int32 pkt, t_bool q); +t_bool rq_gus (MSC *cp, int32 pkt, t_bool q); +t_bool rq_onl (MSC *cp, int32 pkt, t_bool q); +t_bool rq_rw (MSC *cp, int32 pkt, t_bool q); +t_bool rq_scc (MSC *cp, int32 pkt, t_bool q); +t_bool rq_suc (MSC *cp, int32 pkt, t_bool q); +t_bool rq_plf (MSC *cp, uint32 err); +t_bool rq_dte (MSC *cp, UNIT *uptr, uint32 err); +t_bool rq_hbe (MSC *cp, UNIT *uptr); +t_bool rq_una (MSC *cp, int32 un); +t_bool rq_deqf (MSC *cp, int32 *pkt); +int32 rq_deqh (MSC *cp, int32 *lh); +void rq_enqh (MSC *cp, int32 *lh, int32 pkt); +void rq_enqt (MSC *cp, int32 *lh, int32 pkt); +t_bool rq_getpkt (RUN_DECL, MSC *cp, int32 *pkt); +t_bool rq_putpkt (RUN_DECL, MSC *cp, int32 pkt, t_bool qt); +t_bool rq_getdesc (RUN_DECL, MSC *cp, struct uq_ring *ring, uint32 *desc); +t_bool rq_putdesc (RUN_DECL, MSC *cp, struct uq_ring *ring, uint32 desc); +int32 rq_rw_valid (MSC *cp, int32 pkt, UNIT *uptr, uint32 cmd); +t_bool rq_rw_end (MSC *cp, UNIT *uptr, uint32 flg, uint32 sts); +void rq_putr (MSC *cp, int32 pkt, uint32 cmd, uint32 flg, + uint32 sts, uint32 lnt, uint32 typ); +void rq_putr_unit (MSC *cp, int32 pkt, UNIT *uptr, uint32 lu, t_bool all); +void rq_setf_unit (MSC *cp, int32 pkt, UNIT *uptr); +void rq_init_int (MSC *cp); +void rq_ring_int (RUN_DECL, MSC *cp, struct uq_ring *ring); +t_bool rq_fatal (MSC *cp, uint32 err); +UNIT *rq_getucb (MSC *cp, uint32 lu); +int32 rq_map_pa (uint32 pa); +void rq_setint (MSC *cp); +void rq_clrint (MSC *cp, t_bool intack = FALSE); +int32 rq_inta (void); + +/* RQ data structures + + rq_dev RQ device descriptor + rq_unit RQ unit list + rq_reg RQ register list + rq_mod RQ modifier list +*/ + +MSC rq_ctx = { 0 }; + +DIB rq_dib = { + IOBA_RQ, IOLN_RQ, &rq_rd, &rq_wr, + 1, IVCL (RQ), 0, { &rq_inta } + }; + +UNIT* rq_unit[] = { + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RX50_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RX50)), + UDATA (&rq_tmrsvc, UNIT_IDLE|UNIT_DIS, 0), + UDATA (&rq_quesvc, UNIT_DIS, 0) + }; + +REG rq_reg[] = { + { GRDATA_GBL (SA, rq_ctx.sa, DEV_RDX, 16, 0) }, + { GRDATA_GBL (SAW, rq_ctx.saw, DEV_RDX, 16, 0) }, + { GRDATA_GBL (S1DAT, rq_ctx.s1dat, DEV_RDX, 16, 0) }, + { GRDATA_GBL (COMM, rq_ctx.comm, DEV_RDX, 22, 0) }, + { GRDATA_GBL (CQIOFF, rq_ctx.cq.ioff, DEV_RDX, 32, 0) }, + { GRDATA_GBL (CQBA, rq_ctx.cq.ba, DEV_RDX, 22, 0) }, + { GRDATA_GBL (CQLNT, rq_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA_GBL (CQIDX, rq_ctx.cq.idx, DEV_RDX, 8, 2) }, + { GRDATA_GBL (RQIOFF, rq_ctx.rq.ioff, DEV_RDX, 32, 0) }, + { GRDATA_GBL (RQBA, rq_ctx.rq.ba, DEV_RDX, 22, 0) }, + { GRDATA_GBL (RQLNT, rq_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA_GBL (RQIDX, rq_ctx.rq.idx, DEV_RDX, 8, 2) }, + { DRDATA_GBL (FREE, rq_ctx.freq, 5) }, + { DRDATA_GBL (RESP, rq_ctx.rspq, 5) }, + { DRDATA_GBL (PBSY, rq_ctx.pbsy, 5) }, + { GRDATA_GBL (CFLGS, rq_ctx.cflgs, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CSTA, rq_ctx.csta, DEV_RDX, 4, 0) }, + { GRDATA_GBL (PERR, rq_ctx.perr, DEV_RDX, 9, 0) }, + { DRDATA_GBL (CRED, rq_ctx.credits, 5) }, + { DRDATA_GBL (HAT, rq_ctx.hat, 17) }, + { DRDATA_GBL (HTMO, rq_ctx.htmo, 17) }, + { FLDATA_GBL (PRGI, rq_ctx.prgi, 0), REG_HIDDEN }, + { FLDATA_GBL (PIP, rq_ctx.pip, 0), REG_HIDDEN }, + { FLDATA_GBL (INT, rq_ctx.irq, 0) }, + { DRDATA_GBL (ITIME, rq_itime, 24), PV_LEFT + REG_NZ }, + { DRDATA_GBL (I4TIME, rq_itime4, 24), PV_LEFT + REG_NZ }, + { DRDATA_GBL (QTIME, rq_qtime, 24), PV_LEFT + REG_NZ }, + { DRDATA_GBL (XTIME, rq_xtime, 24), PV_LEFT + REG_NZ }, + { BRDATA_GBL (PKTS, rq_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) }, + { URDATA_GBL (CPKT, rq_unit, 0, cpkt, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA_GBL (PKTQ, rq_unit, 0, pktq, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA_GBL (UFLG, rq_unit, 0, uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, + { URDATA_GBL (CAPAC, rq_unit, 0, capac, 10, T_ADDR_W, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, + { GRDATA_GBL (DEVADDR, rq_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA_GBL (DEVVEC, rq_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { DRDATA_GBL (DEVLBN, drv_tab[RA8U_DTYPE].lbn, 22), REG_HRO }, + { NULL } + }; + +MTAB rq_mod[] = { + { UNIT_WLK, 0, NULL, "WRITEENABLED", &rq_set_wlk }, + { UNIT_WLK, UNIT_WLK, NULL, "LOCKED", &rq_set_wlk }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_RI, "RINGS", NULL, + NULL, &rq_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_FR, "FREEQ", NULL, + NULL, &rq_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_RS, "RESPQ", NULL, + NULL, &rq_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_UN, "UNITQ", NULL, + NULL, &rq_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_ALL, "ALL", NULL, + NULL, &rq_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VUN | MTAB_NMO, 0, "UNITQ", NULL, + NULL, &rq_show_unitq, 0 }, + { MTAB_XTD | MTAB_VUN, 0, "WRITE", NULL, + NULL, &rq_show_wlk, NULL }, + { MTAB_XTD | MTAB_VUN, RX50_DTYPE, NULL, "RX50", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RX33_DTYPE, NULL, "RX33", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RD31_DTYPE, NULL, "RD31", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RD32_DTYPE, NULL, "RD32", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RD51_DTYPE, NULL, "RD51", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RD52_DTYPE, NULL, "RD52", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RD53_DTYPE, NULL, "RD53", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RD54_DTYPE, NULL, "RD54", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA60_DTYPE, NULL, "RA60", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA81_DTYPE, NULL, "RA81", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA82_DTYPE, NULL, "RA82", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RRD40_DTYPE, NULL, "RRD40", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RRD40_DTYPE, NULL, "CDROM", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA71_DTYPE, NULL, "RA71", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA72_DTYPE, NULL, "RA72", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA90_DTYPE, NULL, "RA90", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA92_DTYPE, NULL, "RA92", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA8U_DTYPE, NULL, "RAUSER", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, 0, "TYPE", NULL, + NULL, &rq_show_type, NULL }, + { UNIT_NOAUTO, UNIT_NOAUTO, "noautosize", "NOAUTOSIZE", NULL }, + { UNIT_NOAUTO, 0, "autosize", "AUTOSIZE", NULL }, + { MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_disk_set_fmt, &sim_disk_show_fmt, NULL }, +#if defined (VM_PDP11) + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, +#else + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL, + NULL, &show_addr, NULL }, +#endif + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE", + &set_addr_flt, NULL, NULL }, + { 0 } + }; + +DEVICE rq_dev = { + "RQ", rq_unit, rq_reg, rq_mod, + RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, + NULL, NULL, &rq_reset, + &rq_boot, &rq_attach, &rq_detach, + &rq_dib, DEV_FLTA | DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_DEBUG, + 0, rq_debug + }; + +/* RQB data structures + + rqb_dev RQB device descriptor + rqb_unit RQB unit list + rqb_reg RQB register list + rqb_mod RQB modifier list +*/ + +MSC rqb_ctx = { 1 }; + +DIB rqb_dib = { + IOBA_RQB, IOLN_RQB, &rq_rd, &rq_wr, + 1, IVCL (RQ), 0, { &rq_inta } + }; + +UNIT* rqb_unit[] = { + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_tmrsvc, UNIT_IDLE|UNIT_DIS, 0), + UDATA (&rq_quesvc, UNIT_DIS, 0) + }; + + +REG rqb_reg[] = { + { GRDATA_GBL (SA, rqb_ctx.sa, DEV_RDX, 16, 0) }, + { GRDATA_GBL (SAW, rqb_ctx.saw, DEV_RDX, 16, 0) }, + { GRDATA_GBL (S1DAT, rqb_ctx.s1dat, DEV_RDX, 16, 0) }, + { GRDATA_GBL (COMM, rqb_ctx.comm, DEV_RDX, 22, 0) }, + { GRDATA_GBL (CQIOFF, rqb_ctx.cq.ioff, DEV_RDX, 32, 0) }, + { GRDATA_GBL (CQBA, rqb_ctx.cq.ba, DEV_RDX, 22, 0) }, + { GRDATA_GBL (CQLNT, rqb_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA_GBL (CQIDX, rqb_ctx.cq.idx, DEV_RDX, 8, 2) }, + { GRDATA_GBL (RQIOFF, rqb_ctx.rq.ioff, DEV_RDX, 32, 0) }, + { GRDATA_GBL (RQBA, rqb_ctx.rq.ba, DEV_RDX, 22, 0) }, + { GRDATA_GBL (RQLNT, rqb_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA_GBL (RQIDX, rqb_ctx.rq.idx, DEV_RDX, 8, 2) }, + { DRDATA_GBL (FREE, rqb_ctx.freq, 5) }, + { DRDATA_GBL (RESP, rqb_ctx.rspq, 5) }, + { DRDATA_GBL (PBSY, rqb_ctx.pbsy, 5) }, + { GRDATA_GBL (CFLGS, rqb_ctx.cflgs, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CSTA, rqb_ctx.csta, DEV_RDX, 4, 0) }, + { GRDATA_GBL (PERR, rqb_ctx.perr, DEV_RDX, 9, 0) }, + { DRDATA_GBL (CRED, rqb_ctx.credits, 5) }, + { DRDATA_GBL (HAT, rqb_ctx.hat, 17) }, + { DRDATA_GBL (HTMO, rqb_ctx.htmo, 17) }, + { FLDATA_GBL (PRGI, rqb_ctx.prgi, 0), REG_HIDDEN }, + { FLDATA_GBL (PIP, rqb_ctx.pip, 0), REG_HIDDEN }, + { FLDATA_GBL (INT, rqb_ctx.irq, 0) }, + { BRDATA_GBL (PKTS, rqb_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) }, + { URDATA_GBL (CPKT, rqb_unit, 0, cpkt, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA_GBL (PKTQ, rqb_unit, 0, pktq, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA_GBL (UFLG, rqb_unit, 0, uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, + { URDATA_GBL (CAPAC, rqb_unit, 0, capac, 10, T_ADDR_W, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, + { GRDATA_GBL (DEVADDR, rqb_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA_GBL (DEVVEC, rqb_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +DEVICE rqb_dev = { + "RQB", rqb_unit, rqb_reg, rq_mod, + RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, + NULL, NULL, &rq_reset, + &rq_boot, &rq_attach, &rq_detach, + &rqb_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG, + 0, rq_debug + }; + +/* RQC data structures + + rqc_dev RQC device descriptor + rqc_unit RQC unit list + rqc_reg RQC register list + rqc_mod RQC modifier list +*/ + +MSC rqc_ctx = { 2 }; + +DIB rqc_dib = { + IOBA_RQC, IOLN_RQC, &rq_rd, &rq_wr, + 1, IVCL (RQ), 0, { &rq_inta } + }; + +UNIT* rqc_unit[] = { + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_tmrsvc, UNIT_IDLE|UNIT_DIS, 0), + UDATA (&rq_quesvc, UNIT_DIS, 0) + }; + +REG rqc_reg[] = { + { GRDATA_GBL (SA, rqc_ctx.sa, DEV_RDX, 16, 0) }, + { GRDATA_GBL (SAW, rqc_ctx.saw, DEV_RDX, 16, 0) }, + { GRDATA_GBL (S1DAT, rqc_ctx.s1dat, DEV_RDX, 16, 0) }, + { GRDATA_GBL (COMM, rqc_ctx.comm, DEV_RDX, 22, 0) }, + { GRDATA_GBL (CQIOFF, rqc_ctx.cq.ioff, DEV_RDX, 32, 0) }, + { GRDATA_GBL (CQBA, rqc_ctx.cq.ba, DEV_RDX, 22, 0) }, + { GRDATA_GBL (CQLNT, rqc_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA_GBL (CQIDX, rqc_ctx.cq.idx, DEV_RDX, 8, 2) }, + { GRDATA_GBL (RQIOFF, rqc_ctx.rq.ioff, DEV_RDX, 32, 0) }, + { GRDATA_GBL (RQBA, rqc_ctx.rq.ba, DEV_RDX, 22, 0) }, + { GRDATA_GBL (RQLNT, rqc_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA_GBL (RQIDX, rqc_ctx.rq.idx, DEV_RDX, 8, 2) }, + { DRDATA_GBL (FREE, rqc_ctx.freq, 5) }, + { DRDATA_GBL (RESP, rqc_ctx.rspq, 5) }, + { DRDATA_GBL (PBSY, rqc_ctx.pbsy, 5) }, + { GRDATA_GBL (CFLGS, rqc_ctx.cflgs, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CSTA, rqc_ctx.csta, DEV_RDX, 4, 0) }, + { GRDATA_GBL (PERR, rqc_ctx.perr, DEV_RDX, 9, 0) }, + { DRDATA_GBL (CRED, rqc_ctx.credits, 5) }, + { DRDATA_GBL (HAT, rqc_ctx.hat, 17) }, + { DRDATA_GBL (HTMO, rqc_ctx.htmo, 17) }, + { FLDATA_GBL (PRGI, rqc_ctx.prgi, 0), REG_HIDDEN }, + { FLDATA_GBL (PIP, rqc_ctx.pip, 0), REG_HIDDEN }, + { FLDATA_GBL (INT, rqc_ctx.irq, 0) }, + { BRDATA_GBL (PKTS, rqc_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) }, + { URDATA_GBL (CPKT, rqc_unit, 0, cpkt, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA_GBL (PKTQ, rqc_unit, 0, pktq, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA_GBL (UFLG, rqc_unit, 0, uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, + { URDATA_GBL (CAPAC, rqc_unit, 0, capac, 10, T_ADDR_W, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, + { GRDATA_GBL (DEVADDR, rqc_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA_GBL (DEVVEC, rqc_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +DEVICE rqc_dev = { + "RQC", rqc_unit, rqc_reg, rq_mod, + RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, + NULL, NULL, &rq_reset, + &rq_boot, &rq_attach, &rq_detach, + &rqc_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG, + 0, rq_debug + }; + +/* RQD data structures + + rqd_dev RQ device descriptor + rqd_unit RQ unit list + rqd_reg RQ register list + rqd_mod RQ modifier list +*/ + +MSC rqd_ctx = { 3 }; + +DIB rqd_dib = { + IOBA_RQD, IOLN_RQD, &rq_rd, &rq_wr, + 1, IVCL (RQ), 0, { &rq_inta } + }; + +UNIT* rqd_unit[] = { + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+(RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)), + UDATA (&rq_tmrsvc, UNIT_IDLE|UNIT_DIS, 0), + UDATA (&rq_quesvc, UNIT_DIS, 0) + }; + +REG rqd_reg[] = { + { GRDATA_GBL (SA, rqd_ctx.sa, DEV_RDX, 16, 0) }, + { GRDATA_GBL (SAW, rqd_ctx.saw, DEV_RDX, 16, 0) }, + { GRDATA_GBL (S1DAT, rqd_ctx.s1dat, DEV_RDX, 16, 0) }, + { GRDATA_GBL (COMM, rqd_ctx.comm, DEV_RDX, 22, 0) }, + { GRDATA_GBL (CQIOFF, rqd_ctx.cq.ioff, DEV_RDX, 32, 0) }, + { GRDATA_GBL (CQBA, rqd_ctx.cq.ba, DEV_RDX, 22, 0) }, + { GRDATA_GBL (CQLNT, rqd_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA_GBL (CQIDX, rqd_ctx.cq.idx, DEV_RDX, 8, 2) }, + { GRDATA_GBL (RQIOFF, rqd_ctx.rq.ioff, DEV_RDX, 32, 0) }, + { GRDATA_GBL (RQBA, rqd_ctx.rq.ba, DEV_RDX, 22, 0) }, + { GRDATA_GBL (RQLNT, rqd_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA_GBL (RQIDX, rqd_ctx.rq.idx, DEV_RDX, 8, 2) }, + { DRDATA_GBL (FREE, rqd_ctx.freq, 5) }, + { DRDATA_GBL (RESP, rqd_ctx.rspq, 5) }, + { DRDATA_GBL (PBSY, rqd_ctx.pbsy, 5) }, + { GRDATA_GBL (CFLGS, rqd_ctx.cflgs, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CSTA, rqd_ctx.csta, DEV_RDX, 4, 0) }, + { GRDATA_GBL (PERR, rqd_ctx.perr, DEV_RDX, 9, 0) }, + { DRDATA_GBL (CRED, rqd_ctx.credits, 5) }, + { DRDATA_GBL (HAT, rqd_ctx.hat, 17) }, + { DRDATA_GBL (HTMO, rqd_ctx.htmo, 17) }, + { FLDATA_GBL (PRGI, rqd_ctx.prgi, 0), REG_HIDDEN }, + { FLDATA_GBL (PIP, rqd_ctx.pip, 0), REG_HIDDEN }, + { FLDATA_GBL (INT, rqd_ctx.irq, 0) }, + { BRDATA_GBL (PKTS, rqd_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) }, + { URDATA_GBL (CPKT, rqd_unit, 0, cpkt, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA_GBL (PKTQ, rqd_unit, 0, pktq, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA_GBL (UFLG, rqd_unit, 0, uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, + { URDATA_GBL (CAPAC, rqd_unit, 0, capac, 10, T_ADDR_W, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, + { GRDATA_GBL (DEVADDR, rqd_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA_GBL (DEVVEC, rqd_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +DEVICE rqd_dev = { + "RQD", rqd_unit, rqd_reg, rq_mod, + RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, + NULL, NULL, &rq_reset, + &rq_boot, &rq_attach, &rq_detach, + &rqd_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG, + 0, rq_debug + }; + +static DEVICE *rq_devmap[RQ_NUMCT] = { + &rq_dev, &rqb_dev, &rqc_dev, &rqd_dev + }; + +static MSC *rq_ctxmap[RQ_NUMCT] = { + &rq_ctx, &rqb_ctx, &rqc_ctx, &rqd_ctx + }; + +AUTO_INIT_DEVLOCK(rqa_lock); +AUTO_INIT_DEVLOCK(rqb_lock); +AUTO_INIT_DEVLOCK(rqc_lock); +AUTO_INIT_DEVLOCK(rqd_lock); + +static smp_lock** rq_lockmap[RQ_NUMCT] = { + &rqa_lock, &rqb_lock, &rqc_lock, &rqd_lock + }; + +#define AUTO_LOCK_CTRL(cidx) AUTO_LOCK_NM(rq_autolock, *rq_lockmap[cidx]) + +/* I/O dispatch routines, I/O addresses 17772150 - 17772152 + + base + 0 IP read/write + base + 2 SA read/write +*/ + +t_stat rq_rd (int32 *data, int32 PA, int32 access) +{ + RUN_SCOPE; + int32 cidx = rq_map_pa ((uint32) PA); + if (cidx < 0) + return SCPE_IERR; + + AUTO_LOCK_CTRL(cidx); + MSC *cp = rq_ctxmap[cidx]; + DEVICE *dptr = rq_devmap[cidx]; + + sim_debug(DBG_REG, dptr, "rq_rd(PA=0x%08X [%s], access=%d)\n", PA, ((PA >> 1) & 01) ? "IP" : "SA", access); + + switch ((PA >> 1) & 01) /* decode PA<1> */ + { + case 0: /* IP */ + *data = 0; /* reads zero */ + if (cp->csta == CST_S3_PPB) /* waiting for poll? */ + rq_step4 (RUN_PASS, cp); + else if (cp->csta == CST_UP) /* if up */ + { + sim_debug (DBG_REQ, dptr, "poll started, PC=%X\n", OLDPC); + cp->pip = 1; /* poll host */ + sim_activate (dptr->units[RQ_QUEUE], rq_qtime); + } + break; + + case 1: /* SA */ + *data = cp->sa; + break; + } + + return SCPE_OK; +} + +t_stat rq_wr (int32 data, int32 PA, int32 access) +{ + RUN_SCOPE; + int32 cidx = rq_map_pa ((uint32) PA); + if (cidx < 0) + return SCPE_IERR; + + AUTO_LOCK_CTRL(cidx); + MSC *cp = rq_ctxmap[cidx]; + DEVICE *dptr = rq_devmap[cidx]; + + sim_debug(DBG_REG, dptr, "rq_wr(PA=0x%08X [%s], access=%d)\n", PA, ((PA >> 1) & 01) ? "IP" : "SA", access); + + switch ((PA >> 1) & 01) /* decode PA<1> */ + { + case 0: /* IP */ + rq_reset (rq_devmap[cidx]); /* init device */ + sim_debug (DBG_REQ, dptr, "initialization started\n"); + break; + + case 1: /* SA */ + cp->saw = data; + if (cp->csta < CST_S4) /* stages 1-3 */ + sim_activate (dptr->units[RQ_QUEUE], rq_itime); + else if (cp->csta == CST_S4) /* stage 4 (fast) */ + sim_activate (dptr->units[RQ_QUEUE], rq_itime4); + break; + } + + return SCPE_OK; +} + +/* Map physical address to device context */ + +int32 rq_map_pa (uint32 pa) +{ + int32 i; + DEVICE *dptr; + DIB *dibp; + + for (i = 0; i < RQ_NUMCT; i++) /* loop thru ctrls */ + { + dptr = rq_devmap[i]; /* get device */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (pa >= dibp->ba && /* in range? */ + pa < dibp->ba + dibp->lnt) + { + return i; /* return ctrl idx */ + } + } + return -1; +} + +/* Transition to step 4 - init communications region */ + +t_bool rq_step4 (RUN_DECL, MSC *cp) +{ + int32 i, lnt; + uint32 base; + uint16 zero[SA_COMM_MAX >> 1]; + int32 wstatus; + + cp->rq.ioff = SA_COMM_RI; /* set intr offset */ + cp->rq.ba = cp->comm; /* set rsp q base */ + cp->rq.lnt = SA_S1H_RQ (cp->s1dat) << 2; /* get resp q len */ + cp->cq.ioff = SA_COMM_CI; /* set intr offset */ + cp->cq.ba = cp->comm + cp->rq.lnt; /* set cmd q base */ + cp->cq.lnt = SA_S1H_CQ (cp->s1dat) << 2; /* get cmd q len */ + cp->cq.idx = cp->rq.idx = 0; /* clear q idx's */ + if (cp->prgi) + base = cp->comm + SA_COMM_QQ; + else base = cp->comm + SA_COMM_CI; + lnt = cp->comm + cp->cq.lnt + cp->rq.lnt - base; /* comm lnt */ + if (lnt > SA_COMM_MAX) /* paranoia */ + lnt = SA_COMM_MAX; + for (i = 0; i < (lnt >> 1); i++) /* clr buffer */ + zero[i] = 0; + smp_wmb(); + wstatus = Map_WriteW (RUN_PASS, base, lnt, zero); /* zero comm area */ + smp_wmb(); + if (wstatus) /* error? */ + return rq_fatal (cp, PE_QWE); + cp->sa = SA_S4 | (RQ_UQPM << SA_S4C_V_MOD) | /* send step 4 */ + (RQ_SVER << SA_S4C_V_VER); + cp->csta = CST_S4; /* set step 4 */ + rq_init_int (cp); /* poke host */ + return OK; +} + +/* Queue service - invoked when any of the queues (host queue, unit + queues, response queue) require servicing. Also invoked during + initialization to provide some delay to the next step. + + Process at most one item off each unit queue + If the unit queues were empty, process at most one item off the host queue + Process at most one item off the response queue + + If all queues are idle, terminate thread +*/ + +t_stat rq_quesvc (RUN_SVC_DECL, UNIT *uptr) +{ + int32 i, cnid; + int32 pkt = 0; + UNIT *nuptr; + AUTO_LOCK_CTRL(uptr->cnum); + RUN_SVC_CHECK_CANCELLED(uptr); + MSC *cp = rq_ctxmap[uptr->cnum]; + DEVICE *dptr = rq_devmap[uptr->cnum]; + DIB *dibp = (DIB *) dptr->ctxt; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_quesvc\n"); + + if (cp->csta < CST_UP) /* still init? */ + { + sim_debug(DBG_INI, dptr, "CSTA=%d, SAW=0x%X\n", cp->csta, cp->saw); + + switch (cp->csta) /* controller state? */ + { + case CST_S1: /* need S1 reply */ + if (cp->saw & SA_S1H_VL) /* valid? */ + { + if (cp->saw & SA_S1H_WR) /* wrap? */ + { + cp->sa = cp->saw; /* echo data */ + cp->csta = CST_S1_WR; /* endless loop */ + } + else + { + cp->s1dat = cp->saw; /* save data */ + int32 newvec = (cp->s1dat & SA_S1H_VEC) << 2; /* get vector */ + if (newvec) /* if nz, bias */ + newvec += VEC_Q; + io_change_vec(dibp, newvec); + cp->sa = SA_S2 | SA_S2C_PT | SA_S2C_EC (cp->s1dat); + cp->csta = CST_S2; /* now in step 2 */ + rq_init_int (cp); /* intr if req */ + } + } + break; + + case CST_S1_WR: /* wrap mode */ + cp->sa = cp->saw; /* echo data */ + break; + + case CST_S2: /* need S2 reply */ + cp->comm = cp->saw & SA_S2H_CLO; /* get low addr */ + cp->prgi = cp->saw & SA_S2H_PI; /* get purge int */ + cp->sa = SA_S3 | SA_S3C_EC (cp->s1dat); + cp->csta = CST_S3; /* now in step 3 */ + rq_init_int (cp); /* intr if req */ + break; + + case CST_S3: /* need S3 reply */ + cp->comm = ((cp->saw & SA_S3H_CHI) << 16) | cp->comm; + if (cp->saw & SA_S3H_PP) /* purge/poll test? */ + { + cp->sa = 0; /* put 0 */ + cp->csta = CST_S3_PPA; /* wait for 0 write */ + } + else + { + rq_step4 (RUN_PASS, cp); /* send step 4 */ + } + break; + + case CST_S3_PPA: /* need purge test */ + if (cp->saw) /* data not zero? */ + rq_fatal (cp, PE_PPF); + else + cp->csta = CST_S3_PPB; /* wait for poll */ + break; + + case CST_S4: /* need S4 reply */ + if (cp->saw & SA_S4H_GO) /* go set? */ + { + sim_debug (DBG_REQ, dptr, "initialization complete\n"); + cp->csta = CST_UP; /* we're up */ + cp->sa = 0; /* clear SA */ + sim_activate_clk_cosched (dptr->units[RQ_TIMER], clk_tps); + if ((cp->saw & SA_S4H_LF) && cp->perr) + rq_plf (cp, cp->perr); + cp->perr = 0; + } + break; + } + + return SCPE_OK; + } + + for (i = 0; i < RQ_NUMDR; i++) /* chk unit q's */ + { + nuptr = dptr->units[i]; /* ptr to unit */ + if (nuptr->cpkt || nuptr->pktq == 0) + continue; + pkt = rq_deqh (cp, &nuptr->pktq); /* get top of q */ + if (!rq_mscp (cp, pkt, FALSE)) /* process */ + return SCPE_OK; + } + + if (pkt == 0 && cp->pip) /* polling? */ + { + if (!rq_getpkt (RUN_PASS, cp, &pkt)) /* get host pkt */ + return SCPE_OK; + if (pkt) /* got one? */ + { + sim_debug (DBG_REQ, dptr, "cmd=%04X(%3s), mod=%04X, unit=%d, bc=%04X%04X, ma=%04X%04X, lbn=%04X%04X\n", + cp->pak[pkt].d[CMD_OPC], rq_cmdname[cp->pak[pkt].d[CMD_OPC]&0x3f], + cp->pak[pkt].d[CMD_MOD], cp->pak[pkt].d[CMD_UN], + cp->pak[pkt].d[RW_BCH], cp->pak[pkt].d[RW_BCL], + cp->pak[pkt].d[RW_BAH], cp->pak[pkt].d[RW_BAL], + cp->pak[pkt].d[RW_LBNH], cp->pak[pkt].d[RW_LBNL]); + if (GETP (pkt, UQ_HCTC, TYP) != UQ_TYP_SEQ) /* seq packet? */ + return rq_fatal (cp, PE_PIE); /* no, term thread */ + cnid = GETP (pkt, UQ_HCTC, CID); /* get conn ID */ + if (cnid == UQ_CID_MSCP) /* MSCP packet? */ + { + if (!rq_mscp (cp, pkt, TRUE)) /* proc, q non-seq */ + return SCPE_OK; + } + else if (cnid == UQ_CID_DUP) /* DUP packet? */ + { + rq_putr (cp, pkt, OP_END, 0, ST_CMD | I_OPCD, RSP_LNT, UQ_TYP_SEQ); + if (!rq_putpkt (RUN_PASS, cp, pkt, TRUE)) /* ill cmd */ + return SCPE_OK; + } + else + { + return rq_fatal (cp, PE_ICI); /* no, term thread */ + } + } + else + { + cp->pip = 0; /* discontinue poll */ + } + } + + if (cp->rspq) /* resp q? */ + { + pkt = rq_deqh (cp, &cp->rspq); /* get top of q */ + if (!rq_putpkt (RUN_PASS, cp, pkt, FALSE)) /* send to host */ + return SCPE_OK; + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_quesvc - rq_putpkt failed - 1\n"); + } + if (pkt) /* more to do? */ + sim_activate (uptr, rq_qtime); + return SCPE_OK; /* done */ +} + +/* Clock service (roughly once per second) */ + +t_stat rq_tmrsvc (RUN_SVC_DECL, UNIT *uptr) +{ + int32 i; + UNIT *nuptr; + AUTO_LOCK_CTRL(uptr->cnum); + RUN_SVC_CHECK_CANCELLED(uptr); + MSC *cp = rq_ctxmap[uptr->cnum]; + DEVICE *dptr = rq_devmap[uptr->cnum]; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_tmrsvc\n"); + + sim_activate_clk_cosched (uptr, clk_tps); /* reactivate */ + for (i = 0; i < RQ_NUMDR; i++) /* poll */ + { + nuptr = dptr->units[i]; + if ((nuptr->flags & UNIT_ATP) && /* ATN pending? */ + (nuptr->flags & UNIT_ATT) && /* still online? */ + (cp->cflgs & CF_ATN)) /* wanted? */ + { + if (!rq_una (cp, i)) + return SCPE_OK; + } + nuptr->flags = nuptr->flags & ~UNIT_ATP; + } + if (cp->hat > 0 && --cp->hat == 0) /* host timeout? */ + rq_fatal (cp, PE_HAT); /* fatal err */ + return SCPE_OK; +} + +/* MSCP packet handling */ + +t_bool rq_mscp (MSC *cp, int32 pkt, t_bool q) +{ + RUN_SCOPE; + uint32 sts, cmd = GETP (pkt, CMD_OPC, OPC); + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_mscp - %s\n", q ? "Queue" : "No Queue"); + + switch (cmd) + { + case OP_ABO: /* abort */ + return rq_abo (cp, pkt, q); + + case OP_AVL: /* avail */ + return rq_avl (cp, pkt, q); + + case OP_FMT: /* format */ + return rq_fmt (cp, pkt, q); + + case OP_GCS: /* get cmd status */ + return rq_gcs (cp, pkt, q); + + case OP_GUS: /* get unit status */ + return rq_gus (cp, pkt, q); + + case OP_ONL: /* online */ + return rq_onl (cp, pkt, q); + + case OP_SCC: /* set ctrl char */ + return rq_scc (cp, pkt, q); + + case OP_SUC: /* set unit char */ + return rq_suc (cp, pkt, q); + + case OP_ACC: /* access */ + case OP_CMP: /* compare */ + case OP_ERS: /* erase */ + case OP_RD: /* read */ + case OP_WR: /* write */ + return rq_rw (cp, pkt, q); + + case OP_CCD: /* nops */ + case OP_DAP: + case OP_FLU: + cmd = cmd | OP_END; /* set end flag */ + sts = ST_SUC; /* success */ + break; + + default: + cmd = OP_END; /* set end op */ + sts = ST_CMD | I_OPCD; /* ill op */ + break; + } + + rq_putr (cp, pkt, cmd, 0, sts, RSP_LNT, UQ_TYP_SEQ); + return rq_putpkt (RUN_PASS, cp, pkt, TRUE); +} + +/* Abort a command - 1st parameter is ref # of cmd to abort */ + +t_bool rq_abo (MSC *cp, int32 pkt, t_bool q) +{ + RUN_SCOPE; + uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ + uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ + uint32 ref = GETP32 (pkt, ABO_REFL); /* cmd ref # */ + int32 tpkt, prv; + UNIT *uptr; + DEVICE *dptr = rq_devmap[cp->cnum]; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_abo\n"); + + tpkt = 0; /* set no mtch */ + if (uptr = rq_getucb (cp, lu)) /* get unit */ + { + if (uptr->cpkt && /* curr pkt? */ + (GETP32 (uptr->cpkt, CMD_REFL) == ref)) /* match ref? */ + { + tpkt = uptr->cpkt; /* save match */ + uptr->cpkt = 0; /* gonzo */ + sim_cancel (uptr); /* cancel unit */ + sim_activate (dptr->units[RQ_QUEUE], rq_qtime); + } + else if (uptr->pktq && /* head of q? */ + (GETP32 (uptr->pktq, CMD_REFL) == ref)) /* match ref? */ + { + tpkt = uptr->pktq; /* save match */ + uptr->pktq = cp->pak[tpkt].link; /* unlink */ + } + else if (prv = uptr->pktq) /* srch pkt q */ + { + while (tpkt = cp->pak[prv].link) /* walk list */ + { + if (GETP32 (tpkt, RSP_REFL) == ref) /* match? unlink */ + { + cp->pak[prv].link = cp->pak[tpkt].link; + break; + } + } + } + if (tpkt) /* found target? */ + { + uint32 tcmd = GETP (tpkt, CMD_OPC, OPC); /* get opcode */ + rq_putr (cp, tpkt, tcmd | OP_END, 0, ST_ABO, RSP_LNT, UQ_TYP_SEQ); + if (!rq_putpkt (RUN_PASS, cp, tpkt, TRUE)) + return ERR; + } + } + rq_putr (cp, pkt, cmd | OP_END, 0, ST_SUC, ABO_LNT, UQ_TYP_SEQ); + return rq_putpkt (RUN_PASS, cp, pkt, TRUE); +} + +/* Unit available - set unit status to available - defer if q'd cmds */ + +t_bool rq_avl (MSC *cp, int32 pkt, t_bool q) +{ + RUN_SCOPE; + uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ + uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ + uint32 mdf = cp->pak[pkt].d[CMD_MOD]; /* modifier */ + uint32 sts; + UNIT *uptr; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_avl\n"); + + if (uptr = rq_getucb (cp, lu)) /* unit exist? */ + { + if (q && uptr->cpkt) /* need to queue? */ + { + rq_enqt (cp, &uptr->pktq, pkt); /* do later */ + return OK; + } + uptr->flags = uptr->flags & ~UNIT_ONL; /* not online */ + if ((mdf & MD_SPD) && RQ_RMV (uptr)) /* unload of removable device */ + sim_disk_unload (uptr); + uptr->uf = 0; /* clr flags */ + sts = ST_SUC; /* success */ + } + else + { + sts = ST_OFL; /* offline */ + } + rq_putr (cp, pkt, cmd | OP_END, 0, sts, AVL_LNT, UQ_TYP_SEQ); + return rq_putpkt (RUN_PASS, cp, pkt, TRUE); +} + +/* Get command status - only interested in active xfr cmd */ + +t_bool rq_gcs (MSC *cp, int32 pkt, t_bool q) +{ + RUN_SCOPE; + uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ + uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ + uint32 ref = GETP32 (pkt, GCS_REFL); /* ref # */ + int32 tpkt; + UNIT *uptr; + + if ((uptr = rq_getucb (cp, lu)) && /* valid lu? */ + (tpkt = uptr->cpkt) && /* queued pkt? */ + (GETP32 (tpkt, CMD_REFL) == ref) && /* match ref? */ + (GETP (tpkt, CMD_OPC, OPC) >= OP_ACC)) /* rd/wr cmd? */ + { + cp->pak[pkt].d[GCS_STSL] = cp->pak[tpkt].d[RW_WBCL]; + cp->pak[pkt].d[GCS_STSH] = cp->pak[tpkt].d[RW_WBCH]; + } + else + { + cp->pak[pkt].d[GCS_STSL] = 0; /* return 0 */ + cp->pak[pkt].d[GCS_STSH] = 0; + } + rq_putr (cp, pkt, cmd | OP_END, 0, ST_SUC, GCS_LNT, UQ_TYP_SEQ); + return rq_putpkt (RUN_PASS, cp, pkt, TRUE); +} + +/* Get unit status */ + +t_bool rq_gus (MSC *cp, int32 pkt, t_bool q) +{ + RUN_SCOPE; + uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ + uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ + uint32 dtyp, sts, rbpar; + UNIT *uptr; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_gus\n"); + + if (cp->pak[pkt].d[CMD_MOD] & MD_NXU) /* next unit? */ + { + if (lu >= cp->ubase + RQ_NUMDR) /* end of range? */ + { + lu = 0; /* reset to 0 */ + cp->pak[pkt].d[RSP_UN] = lu; + } + } + if (uptr = rq_getucb (cp, lu)) /* unit exist? */ + { + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + sts = ST_OFL | SB_OFL_NV; /* offl no vol */ + else if (uptr->flags & UNIT_ONL) /* online */ + sts = ST_SUC; + else sts = ST_AVL; /* avail */ + rq_putr_unit (cp, pkt, uptr, lu, FALSE); /* fill unit fields */ + dtyp = GET_DTYPE (uptr->flags); /* get drive type */ + if (drv_tab[dtyp].rcts) /* ctrl bad blk? */ + rbpar = 1; + else rbpar = 0; /* fill geom, bblk */ + cp->pak[pkt].d[GUS_TRK] = drv_tab[dtyp].sect; + cp->pak[pkt].d[GUS_GRP] = drv_tab[dtyp].tpg; + cp->pak[pkt].d[GUS_CYL] = drv_tab[dtyp].gpc; + cp->pak[pkt].d[GUS_UVER] = 0; + cp->pak[pkt].d[GUS_RCTS] = drv_tab[dtyp].rcts; + cp->pak[pkt].d[GUS_RBSC] = + (rbpar << GUS_RB_V_RBNS) | (rbpar << GUS_RB_V_RCTC); + } + else + { + sts = ST_OFL; /* offline */ + } + cp->pak[pkt].d[GUS_SHUN] = lu; /* shadowing */ + cp->pak[pkt].d[GUS_SHST] = 0; + rq_putr (cp, pkt, cmd | OP_END, 0, sts, GUS_LNT_D, UQ_TYP_SEQ); + return rq_putpkt (RUN_PASS, cp, pkt, TRUE); +} + +/* Unit online - defer if q'd commands */ + +t_bool rq_onl (MSC *cp, int32 pkt, t_bool q) +{ + RUN_SCOPE; + uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ + uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ + uint32 sts; + UNIT *uptr; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_onl\n"); + + if (uptr = rq_getucb (cp, lu)) /* unit exist? */ + { + if (q && uptr->cpkt) /* need to queue? */ + { + rq_enqt (cp, &uptr->pktq, pkt); /* do later */ + return OK; + } + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + { + sts = ST_OFL | SB_OFL_NV; /* offl no vol */ + } + else if (uptr->flags & UNIT_ONL) /* already online? */ + { + sts = ST_SUC | SB_SUC_ON; + } + else if (sim_disk_isavailable (uptr)) + { + sts = ST_SUC; + uptr->flags = uptr->flags | UNIT_ONL; + rq_setf_unit (cp, pkt, uptr); /* hack flags */ + } + else + { + sts = ST_OFL | SB_OFL_NV; /* offl no vol */ + } + rq_putr_unit (cp, pkt, uptr, lu, TRUE); /* set fields */ + } + else + { + sts = ST_OFL; /* offline */ + } + cp->pak[pkt].d[ONL_SHUN] = lu; /* shadowing */ + cp->pak[pkt].d[ONL_SHST] = 0; + rq_putr (cp, pkt, cmd | OP_END, 0, sts, ONL_LNT, UQ_TYP_SEQ); + return rq_putpkt (RUN_PASS, cp, pkt, TRUE); +} + +/* Set controller characteristics */ + +t_bool rq_scc (MSC *cp, int32 pkt, t_bool q) +{ + RUN_SCOPE; + int32 sts, cmd; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_scc\n"); + + if (cp->pak[pkt].d[SCC_MSV]) /* MSCP ver = 0? */ + { + sts = ST_CMD | I_VRSN; /* no, lose */ + cmd = 0; + } + else + { + sts = ST_SUC; /* success */ + cmd = GETP (pkt, CMD_OPC, OPC); /* get opcode */ + cp->cflgs = (cp->cflgs & CF_RPL) | /* hack ctrl flgs */ + cp->pak[pkt].d[SCC_CFL]; + if (cp->htmo = cp->pak[pkt].d[SCC_TMO]) /* set timeout */ + cp->htmo = cp->htmo + 2; /* if nz, round up */ + cp->pak[pkt].d[SCC_CFL] = cp->cflgs; /* return flags */ + cp->pak[pkt].d[SCC_TMO] = RQ_DCTMO; /* ctrl timeout */ + cp->pak[pkt].d[SCC_VER] = (RQ_HVER << SCC_VER_V_HVER) | + (RQ_SVER << SCC_VER_V_SVER); + cp->pak[pkt].d[SCC_CIDA] = 0; /* ctrl ID */ + cp->pak[pkt].d[SCC_CIDB] = 0; + cp->pak[pkt].d[SCC_CIDC] = 0; + cp->pak[pkt].d[SCC_CIDD] = (RQ_CLASS << SCC_CIDD_V_CLS) | + (RQ_MODEL << SCC_CIDD_V_MOD); + cp->pak[pkt].d[SCC_MBCL] = 0; /* max bc */ + cp->pak[pkt].d[SCC_MBCH] = 0; + } + rq_putr (cp, pkt, cmd | OP_END, 0, sts, SCC_LNT, UQ_TYP_SEQ); + return rq_putpkt (RUN_PASS, cp, pkt, TRUE); +} + +/* Set unit characteristics - defer if q'd commands */ + +t_bool rq_suc (MSC *cp, int32 pkt, t_bool q) +{ + RUN_SCOPE; + uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ + uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ + uint32 sts; + UNIT *uptr; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_suc\n"); + + if (uptr = rq_getucb (cp, lu)) /* unit exist? */ + { + if (q && uptr->cpkt) /* need to queue? */ + { + rq_enqt (cp, &uptr->pktq, pkt); /* do later */ + return OK; + } + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + { + sts = ST_OFL | SB_OFL_NV; /* offl no vol */ + } + else /* avail or onl */ + { + sts = ST_SUC; + rq_setf_unit (cp, pkt, uptr); /* hack flags */ + } + rq_putr_unit (cp, pkt, uptr, lu, TRUE); /* set fields */ + } + else + { + sts = ST_OFL; /* offline */ + } + cp->pak[pkt].d[ONL_SHUN] = lu; /* shadowing */ + cp->pak[pkt].d[ONL_SHST] = 0; + rq_putr (cp, pkt, cmd | OP_END, 0, sts, SUC_LNT, UQ_TYP_SEQ); + return rq_putpkt (RUN_PASS, cp, pkt, TRUE); +} + +/* Format command - floppies only */ + +t_bool rq_fmt (MSC *cp, int32 pkt, t_bool q) +{ + RUN_SCOPE; + uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ + uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ + uint32 sts; + UNIT *uptr; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_fmt\n"); + + if (uptr = rq_getucb (cp, lu)) /* unit exist? */ + { + if (q && uptr->cpkt) /* need to queue? */ + { + rq_enqt (cp, &uptr->pktq, pkt); /* do later */ + return OK; + } + if (GET_DTYPE (uptr->flags) != RX33_DTYPE) /* RX33? */ + sts = ST_CMD | I_OPCD; /* no, err */ + else if ((cp->pak[pkt].d[FMT_IH] & 0100000) == 0) /* magic bit set? */ + sts = ST_CMD | I_FMTI; /* no, err */ + else if ((uptr->flags & UNIT_ATT) == 0) /* offline? */ + sts = ST_OFL | SB_OFL_NV; /* no vol */ + else if (uptr->flags & UNIT_ONL) /* online? */ + { + uptr->flags = uptr->flags & ~UNIT_ONL; + uptr->uf = 0; /* clear flags */ + sts = ST_AVL | SB_AVL_INU; /* avail, in use */ + } + else if (RQ_WPH (uptr)) /* write prot? */ + sts = ST_WPR | SB_WPR_HW; /* can't fmt */ + else + sts = ST_SUC; /*** for now ***/ + } + else + { + sts = ST_OFL; /* offline */ + } + rq_putr (cp, pkt, cmd | OP_END, 0, sts, FMT_LNT, UQ_TYP_SEQ); + return rq_putpkt (RUN_PASS, cp, pkt, TRUE); +} + +/* Data transfer commands */ + +t_bool rq_rw (MSC *cp, int32 pkt, t_bool q) +{ + RUN_SCOPE; + uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ + uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ + uint32 sts; + UNIT *uptr; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_rw(lu=%d, pkt=%d, queue=%s)\n", lu, pkt, q ? "yes" : "no"); + + if (uptr = rq_getucb (cp, lu)) /* unit exist? */ + { + if (q && uptr->cpkt) /* need to queue? */ + { + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_rw - queued\n"); + rq_enqt (cp, &uptr->pktq, pkt); /* do later */ + return OK; + } + sts = rq_rw_valid (cp, pkt, uptr, cmd); /* validity checks */ + if (sts == 0) /* ok? */ + { + uptr->cpkt = pkt; /* op in progress */ + cp->pak[pkt].d[RW_WBAL] = cp->pak[pkt].d[RW_BAL]; + cp->pak[pkt].d[RW_WBAH] = cp->pak[pkt].d[RW_BAH]; + cp->pak[pkt].d[RW_WBCL] = cp->pak[pkt].d[RW_BCL]; + cp->pak[pkt].d[RW_WBCH] = cp->pak[pkt].d[RW_BCH]; + cp->pak[pkt].d[RW_WBLL] = cp->pak[pkt].d[RW_LBNL]; + cp->pak[pkt].d[RW_WBLH] = cp->pak[pkt].d[RW_LBNH]; + uptr->io_starttime = CPU_CURRENT_CYCLES; + uptr->io_startcpu = cpu_unit->cpu_id; + sim_activate (uptr, /*rq_xtime*/ 0); /* activate */ + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_rw - started\n"); + return OK; /* done */ + } + } + else + { + sts = ST_OFL; /* offline */ + } + cp->pak[pkt].d[RW_BCL] = cp->pak[pkt].d[RW_BCH] = 0; /* bad packet */ + rq_putr (cp, pkt, cmd | OP_END, 0, sts, RW_LNT_D, UQ_TYP_SEQ); + return rq_putpkt (RUN_PASS, cp, pkt, TRUE); +} + +/* Validity checks */ + +int32 rq_rw_valid (MSC *cp, int32 pkt, UNIT *uptr, uint32 cmd) +{ + uint32 dtyp = GET_DTYPE (uptr->flags); /* get drive type */ + uint32 lbn = GETP32 (pkt, RW_LBNL); /* get lbn */ + uint32 bc = GETP32 (pkt, RW_BCL); /* get byte cnt */ + uint32 maxlbn = (uint32) (uptr->capac / RQ_NUMBY); /* get max lbn */ + + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + return (ST_OFL | SB_OFL_NV); /* offl no vol */ + if ((uptr->flags & UNIT_ONL) == 0) /* not online? */ + return ST_AVL; /* only avail */ + if (cmd != OP_ACC && cmd != OP_ERS && /* 'real' xfer */ + (cp->pak[pkt].d[RW_BAL] & 1)) /* odd address? */ + return (ST_HST | SB_HST_OA); /* host buf odd */ + if (bc & 1) /* odd byte cnt? */ + return (ST_HST | SB_HST_OC); + if (bc & 0xF0000000) /* 'reasonable' bc? */ + return (ST_CMD | I_BCNT); + // if (lbn & 0xF0000000) return (ST_CMD | I_LBN); /* 'reasonable' lbn? */ + if (lbn >= maxlbn) /* accessing RCT? */ + { + if (lbn >= (maxlbn + drv_tab[dtyp].rcts)) /* beyond copy 1? */ + return (ST_CMD | I_LBN); /* lbn err */ + if (bc != RQ_NUMBY) /* bc must be 512 */ + return (ST_CMD | I_BCNT); + } + else if ((lbn + ((bc + (RQ_NUMBY - 1)) / RQ_NUMBY)) > maxlbn) + return (ST_CMD | I_BCNT); /* spiral to RCT */ + if (cmd == OP_WR || cmd == OP_ERS) /* write op? */ + { + if (lbn >= maxlbn) /* accessing RCT? */ + return (ST_CMD | I_LBN); /* lbn err */ + if (uptr->uf & UF_WPS) /* swre wlk? */ + return (ST_WPR | SB_WPR_SW); + if (RQ_WPH (uptr)) /* hwre wlk? */ + return (ST_WPR | SB_WPR_HW); + } + return 0; /* success! */ +} + +/* I/O completion callback */ + +void rq_io_complete (UNIT *uptr, t_stat status) +{ + RUN_SCOPE; + MSC *cp = rq_ctxmap[uptr->cnum]; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_io_complete(status=%d)\n", status); + + uptr->io_status = status; + uptr->io_complete = 1; + + if (cpu_unit->cpu_id == uptr->io_startcpu) + { + uint32 elapsed = CPU_CURRENT_CYCLES - uptr->io_starttime; + if (elapsed > (uint32) rq_xtime) + sim_activate (uptr, 0); + else + sim_activate (uptr, (uint32) rq_xtime - elapsed); + } + else + { + sim_activate (uptr, 0); + } +} +/* Unit service for data transfer commands */ + +t_stat rq_svc (RUN_SVC_DECL, UNIT *uptr) +{ + AUTO_LOCK_CTRL(uptr->cnum); + RUN_SVC_CHECK_CANCELLED(uptr); + MSC *cp = rq_ctxmap[uptr->cnum]; + + uint32 i, t, tbc, abc, wwc; + uint32 err = 0; + int32 pkt = uptr->cpkt; /* get packet */ + uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */ + uint32 ba = GETP32 (pkt, RW_WBAL); /* buf addr */ + uint32 bc = GETP32 (pkt, RW_WBCL); /* byte count */ + uint32 bl = GETP32 (pkt, RW_WBLL); /* block addr */ + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_svc(unit=%d, pkt=%d, cmd=%s, lbn=%0X, bc=%0x, phase=%s)\n", + sim_unit_index (uptr), pkt, rq_cmdname[cp->pak[pkt].d[CMD_OPC]&0x3f], bl, bc, + uptr->io_complete ? "bottom" : "top"); + + if (cp == NULL || pkt == 0) /* what??? */ + return STOP_RQ; + tbc = (bc > RQ_MAXFR)? RQ_MAXFR: bc; /* trim cnt to max */ + + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + { + rq_rw_end (cp, uptr, 0, ST_OFL | SB_OFL_NV); /* offl no vol */ + return SCPE_OK; + } + if (bc == 0) /* no xfer? */ + { + rq_rw_end (cp, uptr, 0, ST_SUC); /* ok by me... */ + return SCPE_OK; + } + + if (cmd == OP_ERS || cmd == OP_WR) /* write op? */ + { + if (RQ_WPH (uptr)) + { + rq_rw_end (cp, uptr, 0, ST_WPR | SB_WPR_HW); + return SCPE_OK; + } + if (uptr->uf & UF_WPS) + { + rq_rw_end (cp, uptr, 0, ST_WPR | SB_WPR_SW); + return SCPE_OK; + } + } + + if (! uptr->io_complete) /* Top End (I/O Initiation) Processing */ + { + if (cmd == OP_ERS) /* erase? */ + { + wwc = ((tbc + (RQ_NUMBY - 1)) & ~(RQ_NUMBY - 1)) >> 1; + memset (uptr->rqxb, 0, wwc * sizeof(uint16)); /* clr buf */ + sim_disk_data_trace(uptr, (const uint8*) uptr->rqxb, bl, wwc << 1, "sim_disk_wrsect-ERS", DBG_DAT & rq_devmap[cp->cnum]->dctrl, DBG_REQ); + err = sim_disk_wrsect_a (uptr, bl, (uint8*) uptr->rqxb, NULL, (wwc << 1) / RQ_NUMBY, rq_io_complete); + } + else if (cmd == OP_WR) /* write? */ + { + t = Map_ReadW (RUN_PASS, ba, tbc, (uint16*) uptr->rqxb); /* fetch buffer */ + if (abc = tbc - t) /* any xfer? */ + { + wwc = ((abc + (RQ_NUMBY - 1)) & ~(RQ_NUMBY - 1)) >> 1; + for (i = (abc >> 1); i < wwc; i++) + ((uint16 *)(uptr->rqxb))[i] = 0; + sim_disk_data_trace(uptr, (const uint8*) uptr->rqxb, bl, wwc << 1, "sim_disk_wrsect-WR", DBG_DAT & rq_devmap[cp->cnum]->dctrl, DBG_REQ); + err = sim_disk_wrsect_a (uptr, bl, (uint8*) uptr->rqxb, NULL, (wwc << 1) / RQ_NUMBY, rq_io_complete); + } + } + else /* OP_RD & OP_CMP */ + { + err = sim_disk_rdsect_a (uptr, bl, (uint8*) uptr->rqxb, NULL, (tbc + RQ_NUMBY - 1) / RQ_NUMBY, rq_io_complete); + } + return SCPE_OK; /* done for now until callback */ + } + else /* Bottom End (After I/O processing) */ + { + uptr->io_complete = 0; + err = uptr->io_status; + if (cmd == OP_ERS) /* erase? */ + { + } + else if (cmd == OP_WR) /* write? */ + { + t = Map_ReadW (RUN_PASS, ba, tbc, (uint16*) uptr->rqxb); /* fetch buffer */ + abc = tbc - t; /* any xfer? */ + if (t) /* nxm? */ + { + PUTP32 (pkt, RW_WBCL, bc - abc); /* adj bc */ + PUTP32 (pkt, RW_WBAL, ba + abc); /* adj ba */ + if (rq_hbe (cp, uptr)) /* post err log */ + rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); + return SCPE_OK; /* end else wr */ + } + } + else + { + sim_disk_data_trace(uptr, (const uint8*) uptr->rqxb, bl, tbc, "sim_disk_rdsect", DBG_DAT & rq_devmap[cp->cnum]->dctrl, DBG_REQ); + + if (cmd == OP_RD && !err) /* read? */ + { + if (t = Map_WriteW (RUN_PASS, ba, tbc, (uint16*) uptr->rqxb)) /* store, nxm? */ + { + PUTP32 (pkt, RW_WBCL, bc - (tbc - t)); /* adj bc */ + PUTP32 (pkt, RW_WBAL, ba + (tbc - t)); /* adj ba */ + if (rq_hbe (cp, uptr)) /* post err log */ + rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); + return SCPE_OK; + } + } + else if (cmd == OP_CMP && !err) /* compare? */ + { + uint8 dby, mby; + for (i = 0; i < tbc; i++) /* loop */ + { + if (Map_ReadB (RUN_PASS, ba + i, 1, &mby)) /* fetch, nxm? */ + { + PUTP32 (pkt, RW_WBCL, bc - i); /* adj bc */ + PUTP32 (pkt, RW_WBAL, bc - i); /* adj ba */ + if (rq_hbe (cp, uptr)) /* post err log */ + rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); + return SCPE_OK; + } + dby = (((uint16 *)(uptr->rqxb))[i >> 1] >> ((i & 1) ? 8: 0)) & 0xFF; + if (mby != dby) /* cmp err? */ + { + PUTP32 (pkt, RW_WBCL, bc - i); /* adj bc */ + rq_rw_end (cp, uptr, 0, ST_CMP); /* done */ + return SCPE_OK; /* exit */ + } + } + } + } + } + + if (err != 0) /* error? */ + { + if (rq_dte (cp, uptr, ST_DRV)) /* post err log */ + rq_rw_end (cp, uptr, EF_LOG, ST_DRV); /* if ok, report err */ + smp_perror ("RQ I/O error"); + if (! (uptr->flags & UNIT_RAW)) + clearerr (uptr->fileref); + return SCPE_IOERR; + } + ba = ba + tbc; /* incr bus addr */ + bc = bc - tbc; /* decr byte cnt */ + bl = bl + ((tbc + (RQ_NUMBY - 1)) / RQ_NUMBY); /* incr blk # */ + PUTP32 (pkt, RW_WBAL, ba); /* update pkt */ + PUTP32 (pkt, RW_WBCL, bc); + PUTP32 (pkt, RW_WBLL, bl); + if (bc) /* more? resched */ + sim_activate (uptr, /*rq_xtime*/ 0); + else + rq_rw_end (cp, uptr, 0, ST_SUC); /* done! */ + return SCPE_OK; +} + +/* Transfer command complete */ + +t_bool rq_rw_end (MSC *cp, UNIT *uptr, uint32 flg, uint32 sts) +{ + RUN_SCOPE; + int32 pkt = uptr->cpkt; /* packet */ + uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */ + uint32 bc = GETP32 (pkt, RW_BCL); /* init bc */ + uint32 wbc = GETP32 (pkt, RW_WBCL); /* work bc */ + DEVICE *dptr = rq_devmap[uptr->cnum]; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_rw_end\n"); + + uptr->cpkt = 0; /* done */ + PUTP32 (pkt, RW_BCL, bc - wbc); /* bytes processed */ + cp->pak[pkt].d[RW_WBAL] = 0; /* clear temps */ + cp->pak[pkt].d[RW_WBAH] = 0; + cp->pak[pkt].d[RW_WBCL] = 0; + cp->pak[pkt].d[RW_WBCH] = 0; + cp->pak[pkt].d[RW_WBLL] = 0; + cp->pak[pkt].d[RW_WBLH] = 0; + rq_putr (cp, pkt, cmd | OP_END, flg, sts, RW_LNT_D, UQ_TYP_SEQ); /* fill pkt */ + if (!rq_putpkt (RUN_PASS, cp, pkt, TRUE)) /* send pkt */ + return ERR; + if (uptr->pktq) /* more to do? */ + sim_activate (dptr->units[RQ_QUEUE], rq_qtime); /* activate thread */ + return OK; +} + +/* Data transfer error log packet */ + +t_bool rq_dte (MSC *cp, UNIT *uptr, uint32 err) +{ + RUN_SCOPE; + int32 pkt, tpkt; + uint32 lu, dtyp, lbn, ccyl, csurf, csect, t; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_dte\n"); + + if ((cp->cflgs & CF_THS) == 0) /* logging? */ + return OK; + if (!rq_deqf (cp, &pkt)) /* get log pkt */ + return ERR; + tpkt = uptr->cpkt; /* rw pkt */ + lu = cp->pak[tpkt].d[CMD_UN]; /* unit # */ + lbn = GETP32 (tpkt, RW_WBLL); /* recent LBN */ + dtyp = GET_DTYPE (uptr->flags); /* drv type */ + if (drv_tab[dtyp].flgs & RQDF_SDI) /* SDI? ovhd @ end */ + t = 0; + else t = (drv_tab[dtyp].xbn + drv_tab[dtyp].dbn) / /* ovhd cylinders */ + (drv_tab[dtyp].sect * drv_tab[dtyp].surf); + ccyl = t + (lbn / drv_tab[dtyp].cyl); /* curr real cyl */ + t = lbn % drv_tab[dtyp].cyl; /* trk relative blk */ + csurf = t / drv_tab[dtyp].surf; /* curr surf */ + csect = t % drv_tab[dtyp].surf; /* curr sect */ + + cp->pak[pkt].d[ELP_REFL] = cp->pak[tpkt].d[CMD_REFL]; /* copy cmd ref */ + cp->pak[pkt].d[ELP_REFH] = cp->pak[tpkt].d[CMD_REFH]; + cp->pak[pkt].d[ELP_UN] = lu; /* copy unit */ + cp->pak[pkt].d[ELP_SEQ] = 0; /* clr seq # */ + cp->pak[pkt].d[DTE_CIDA] = 0; /* ctrl ID */ + cp->pak[pkt].d[DTE_CIDB] = 0; + cp->pak[pkt].d[DTE_CIDC] = 0; + cp->pak[pkt].d[DTE_CIDD] = (RQ_CLASS << DTE_CIDD_V_CLS) | + (RQ_MODEL << DTE_CIDD_V_MOD); + cp->pak[pkt].d[DTE_VER] = (RQ_HVER << DTE_VER_V_HVER) | + (RQ_SVER << DTE_VER_V_SVER); + cp->pak[pkt].d[DTE_MLUN] = lu; /* MLUN */ + cp->pak[pkt].d[DTE_UIDA] = lu; /* unit ID */ + cp->pak[pkt].d[DTE_UIDB] = 0; + cp->pak[pkt].d[DTE_UIDC] = 0; + cp->pak[pkt].d[DTE_UIDD] = (UID_DISK << DTE_UIDD_V_CLS) | + (drv_tab[dtyp].mod << DTE_UIDD_V_MOD); + cp->pak[pkt].d[DTE_UVER] = 0; /* unit versn */ + cp->pak[pkt].d[DTE_SCYL] = ccyl; /* cylinder */ + cp->pak[pkt].d[DTE_VSNL] = 01234 + lu; /* vol ser # */ + cp->pak[pkt].d[DTE_VSNH] = 0; + cp->pak[pkt].d[DTE_D1] = 0; + cp->pak[pkt].d[DTE_D2] = csect << DTE_D2_V_SECT; /* geometry */ + cp->pak[pkt].d[DTE_D3] = (ccyl << DTE_D3_V_CYL) | + (csurf << DTE_D3_V_SURF); + rq_putr (cp, pkt, FM_SDE, LF_SNR, err, DTE_LNT, UQ_TYP_DAT); + return rq_putpkt (RUN_PASS, cp, pkt, TRUE); +} + +/* Host bus error log packet */ + +t_bool rq_hbe (MSC *cp, UNIT *uptr) +{ + RUN_SCOPE; + int32 pkt, tpkt; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_hbe\n"); + + if ((cp->cflgs & CF_THS) == 0) /* logging? */ + return OK; + if (!rq_deqf (cp, &pkt)) /* get log pkt */ + return ERR; + tpkt = uptr->cpkt; /* rw pkt */ + cp->pak[pkt].d[ELP_REFL] = cp->pak[tpkt].d[CMD_REFL]; /* copy cmd ref */ + cp->pak[pkt].d[ELP_REFH] = cp->pak[tpkt].d[CMD_REFH]; + cp->pak[pkt].d[ELP_UN] = cp->pak[tpkt].d[CMD_UN]; /* copy unit */ + cp->pak[pkt].d[ELP_SEQ] = 0; /* clr seq # */ + cp->pak[pkt].d[HBE_CIDA] = 0; /* ctrl ID */ + cp->pak[pkt].d[HBE_CIDB] = 0; + cp->pak[pkt].d[HBE_CIDC] = 0; + cp->pak[pkt].d[HBE_CIDD] = (RQ_CLASS << DTE_CIDD_V_CLS) | + (RQ_MODEL << DTE_CIDD_V_MOD); + cp->pak[pkt].d[HBE_VER] = (RQ_HVER << HBE_VER_V_HVER) | /* versions */ + (RQ_SVER << HBE_VER_V_SVER); + cp->pak[pkt].d[HBE_RSV] = 0; + cp->pak[pkt].d[HBE_BADL] = cp->pak[tpkt].d[RW_WBAL]; /* bad addr */ + cp->pak[pkt].d[HBE_BADH] = cp->pak[tpkt].d[RW_WBAH]; + rq_putr (cp, pkt, FM_BAD, LF_SNR, ST_HST | SB_HST_NXM, HBE_LNT, UQ_TYP_DAT); + return rq_putpkt (RUN_PASS, cp, pkt, TRUE); +} + +/* Port last failure error log packet */ + +t_bool rq_plf (MSC *cp, uint32 err) +{ + RUN_SCOPE; + int32 pkt; + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_plf\n"); + + if (!rq_deqf (cp, &pkt)) /* get log pkt */ + return ERR; + cp->pak[pkt].d[ELP_REFL] = 0; /* ref = 0 */ + cp->pak[pkt].d[ELP_REFH] = 0; + cp->pak[pkt].d[ELP_UN] = 0; /* no unit */ + cp->pak[pkt].d[ELP_SEQ] = 0; /* no seq */ + cp->pak[pkt].d[PLF_CIDA] = 0; /* cntl ID */ + cp->pak[pkt].d[PLF_CIDB] = 0; + cp->pak[pkt].d[PLF_CIDC] = 0; + cp->pak[pkt].d[PLF_CIDD] = (RQ_CLASS << PLF_CIDD_V_CLS) | + (RQ_MODEL << PLF_CIDD_V_MOD); + cp->pak[pkt].d[PLF_VER] = (RQ_SVER << PLF_VER_V_SVER) | + (RQ_HVER << PLF_VER_V_HVER); + cp->pak[pkt].d[PLF_ERR] = err; + rq_putr (cp, pkt, FM_CNT, LF_SNR, ST_CNT, PLF_LNT, UQ_TYP_DAT); + cp->pak[pkt].d[UQ_HCTC] |= (UQ_CID_DIAG << UQ_HCTC_V_CID); + return rq_putpkt (RUN_PASS, cp, pkt, TRUE); +} + +/* Unit now available attention packet */ + +t_bool rq_una (MSC *cp, int32 un) +{ + RUN_SCOPE; + int32 pkt; + uint32 lu = cp->ubase + un; + UNIT *uptr = rq_getucb (cp, lu); + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_una (Unit=%d)\n", lu); + + if (uptr == NULL) /* huh? */ + return OK; + if (!rq_deqf (cp, &pkt)) /* get log pkt */ + return ERR; + cp->pak[pkt].d[RSP_REFL] = 0; /* ref = 0 */ + cp->pak[pkt].d[RSP_REFH] = 0; + cp->pak[pkt].d[RSP_UN] = lu; + cp->pak[pkt].d[RSP_RSV] = 0; + rq_putr_unit (cp, pkt, uptr, lu, FALSE); /* fill unit fields */ + rq_putr (cp, pkt, OP_AVA, 0, 0, UNA_LNT, UQ_TYP_SEQ); /* fill std fields */ + return rq_putpkt (RUN_PASS, cp, pkt, TRUE); +} + +/* List handling + + rq_deqf - dequeue head of free list (fatal err if none) + rq_deqh - dequeue head of list + rq_enqh - enqueue at head of list + rq_enqt - enqueue at tail of list +*/ + +t_bool rq_deqf (MSC *cp, int32 *pkt) +{ + if (cp->freq == 0) /* no free pkts?? */ + return rq_fatal (cp, PE_NSR); + cp->pbsy = cp->pbsy + 1; /* cnt busy pkts */ + *pkt = cp->freq; /* head of list */ + cp->freq = cp->pak[cp->freq].link; /* next */ + return OK; +} + +int32 rq_deqh (MSC *cp, int32 *lh) +{ + int32 ptr = *lh; /* head of list */ + + if (ptr) /* next */ + *lh = cp->pak[ptr].link; + return ptr; +} + +void rq_enqh (MSC *cp, int32 *lh, int32 pkt) +{ + if (pkt == 0) /* any pkt? */ + return; + cp->pak[pkt].link = *lh; /* link is old lh */ + *lh = pkt; /* pkt is new lh */ +} + +void rq_enqt (MSC *cp, int32 *lh, int32 pkt) +{ + if (pkt == 0) /* any pkt? */ + return; + cp->pak[pkt].link = 0; /* it will be tail */ + if (*lh == 0) /* if empty, enqh */ + *lh = pkt; + else + { + uint32 ptr = *lh; /* chase to end */ + while (cp->pak[ptr].link) + ptr = cp->pak[ptr].link; + cp->pak[ptr].link = pkt; /* enq at tail */ + } +} + +/* Packet and descriptor handling */ + +/* Get packet from command ring */ + +t_bool rq_getpkt (RUN_DECL, MSC *cp, int32 *pkt) +{ + uint32 addr, desc; + + if (!rq_getdesc (RUN_PASS, cp, &cp->cq, &desc)) /* get cmd desc */ + return ERR; + if ((desc & UQ_DESC_OWN) == 0) /* none */ + { + *pkt = 0; /* pkt = 0 */ + return OK; /* no error */ + } + if (!rq_deqf (cp, pkt)) /* get cmd pkt */ + return ERR; + cp->hat = 0; /* dsbl hst timer */ + addr = desc & UQ_ADDR; /* get Q22 addr */ + if (Map_ReadW (RUN_PASS, addr + UQ_HDR_OFF, RQ_PKT_SIZE, cp->pak[*pkt].d)) + return rq_fatal (cp, PE_PRE); /* read pkt */ + return rq_putdesc (RUN_PASS, cp, &cp->cq, desc); /* release desc */ +} + +/* Put packet to response ring - note the clever hack about credits. + The controller sends all its credits to the host. Thereafter, it + supplies one credit for every response packet sent over. Simple! +*/ + +t_bool rq_putpkt (RUN_DECL, MSC *cp, int32 pkt, t_bool qt) +{ + uint32 addr, desc, lnt, cr; + DEVICE *dptr = rq_devmap[cp->cnum]; + + if (pkt == 0) /* any packet? */ + return OK; + sim_debug (DBG_REQ, dptr, "rsp=%04X, sts=%04X\n", + cp->pak[pkt].d[RSP_OPF], cp->pak[pkt].d[RSP_STS]); + if (!rq_getdesc (RUN_PASS, cp, &cp->rq, &desc)) /* get rsp desc */ + return ERR; + if ((desc & UQ_DESC_OWN) == 0) /* not valid? */ + { + if (qt) /* normal? q tail */ + rq_enqt (cp, &cp->rspq, pkt); + else rq_enqh (cp, &cp->rspq, pkt); /* resp q call */ + sim_activate (dptr->units[RQ_QUEUE], rq_qtime); /* activate q thrd */ + return OK; + } + addr = desc & UQ_ADDR; /* get Q22 addr */ + lnt = cp->pak[pkt].d[UQ_HLNT] - UQ_HDR_OFF; /* size, with hdr */ + if ((GETP (pkt, UQ_HCTC, TYP) == UQ_TYP_SEQ) && /* seq packet? */ + (GETP (pkt, CMD_OPC, OPC) & OP_END)) /* end packet? */ + { + cr = (cp->credits >= 14)? 14: cp->credits; /* max 14 credits */ + cp->credits = cp->credits - cr; /* decr credits */ + cp->pak[pkt].d[UQ_HCTC] |= ((cr + 1) << UQ_HCTC_V_CR); + } + if (Map_WriteW (RUN_PASS, addr + UQ_HDR_OFF, lnt, cp->pak[pkt].d)) + return rq_fatal (cp, PE_PWE); /* write pkt */ + rq_enqh (cp, &cp->freq, pkt); /* pkt is free */ + cp->pbsy = cp->pbsy - 1; /* decr busy cnt */ + if (cp->pbsy == 0) /* idle? strt hst tmr */ + cp->hat = cp->htmo; + return rq_putdesc (RUN_PASS, cp, &cp->rq, desc); /* release desc */ +} + +/* + * Multiprocessor note: + * + * UQSSP port and operating system driver comminicate not only via CSR registers, but also via + * shared memory area (comm area) composed of command/response descriptor rings and interrupt + * indicators. + * + * VMS PUDRIVER relies on VAX strong memory model where CPU sees UQSSP writes in the order + * the port executed them, and vice versa. This does not hold true when simulated on the host machine + * with weaker memory consistency model, unless explicit memory barriers are used by both VMS + * PUDRIVER and SIMH RQ/TQ handlers, and are paired up. + * + * Memory barriers are provided by VAX MP RQ and TQ handlers, however they have to be matched by the + * barriers on the driver side. + * + * Standard OpenVMS PUDRIVER does not provide such barriers. Therefore VSMP tool modifies + * loaded PUDRIVER code to install the code in PUDRIVER to issue such barriers. These modifications + * are implemented as patches PU1 - PU7. + * + * PU1 patches routine PU$INT to add memory barrier when checking for CMD snd RSP interrupt indicators. + * New code structure is: + * read_csr (causing memory barrier due to lock in RQ/TQ hadnlers) + * MOVL of CMDINT and RSPINT from comm area + * BEQL ... no interrupt detected + * MEMBAR + * + * PU2 patches routine INSERT_IN_RRING to add memory barrier when storing RSP descriptor. + * New code structure is: + * MEMBAR + * MOVL ... to RSP ring descriptor + * ADAWI (causes memory barrier) + * + * PU3 and PU4 modify routines POLL_RSPRING and PU$SA_POLL reading response ring descriptors, in a + * similar way. New code structure is: + * TSTL descriptor + * BLSS ... still owned by port + * MEMBAR + * + * PU5 modifies routine POLL_CMDRING reading descriptor from the command ring. + * New code structure is: + * TSTL descriptor + * BLSS ... still owned by port + * MEMBAR + * + * PU6 and PU7 modify code that writes descriptor to the command ring. + * New code structure is: + * MEMBAR + * MOVL descriptor to cmd ring + * subsequent barrier is performed by read_csr + * + * Symbolic name of targeted fields are PDT$W_CMDINT, PDT$W_RSPINT, PDT$L_RSPRING, @PDT$L_CMDRING. + * + * Note that PU6 will fail to install if MSCP tracing is enabled on the driver and hence + * target PUDRIVER location is patched by the driver. + * + ****************************************************************** + * + * Note: Current code in InterlockedOpLock::virt_lock and InterlockedOpLock::phys_lock + * assumes that RQ performs access to UQSSP COMM area only within the context of + * VCPU threads, not in IOP thread. If it were to change, phys_lock and virt_lock + * should be modified to issue smp_mb even in uniprocessor case, since PUDRIVER + * patches use BBSSI to issue memory barriers, and PUDRIVER itself uses ADAWI to + * issue memory barrier. The code that implements VAXMP_API_OP_MEMBAR would need + * to be also modified to always issue memory barrier, even in uniprocessor case. + */ + +/* Get a descriptor from the host */ + +t_bool rq_getdesc (RUN_DECL, MSC *cp, struct uq_ring *ring, uint32 *desc) +{ + uint32 addr = ring->ba + ring->idx; + uint16 d[2]; + +#if defined(VM_VAX_MP) + /* + * Execute RMB before fetching high word from the host, to ensure that in + * multiprocessor case fetching Ownership bit comes before fetching low word + * and access is not reordered. Note that OpenVMS PUDRIVER stores descriptors + * to memory atomically as a longword with MOVL or BISL. + */ + smp_mb(); /* segregate with previous operations */ + if (Map_ReadW (RUN_PASS, addr + 2, 2, d + 1)) /* fetch desc hi word */ + return rq_fatal (cp, PE_QRE); /* err? dead */ + smp_rmb(); + if (Map_ReadW (RUN_PASS, addr, 2, d)) /* fetch desc low word */ + return rq_fatal (cp, PE_QRE); /* err? dead */ +#else + if (Map_ReadW (RUN_PASS, addr, 4, d)) /* fetch desc */ + return rq_fatal (cp, PE_QRE); /* err? dead */ +#endif + + *desc = ((uint32) d[0]) | (((uint32) d[1]) << 16); + + /* + * Execute MB to ensure that subsequent access to the packet or data + * is not reordered with access to O-bit. + */ +#if defined(VM_VAX_MP) + smp_mb(); +#endif + + return OK; +} + +/* Return a descriptor to the host, clearing owner bit + If rings transitions from "empty" to "not empty" or "full" to + "not full", and interrupt bit was set, interrupt the host. + Actually, test whether previous ring entry was owned by host. +*/ + +t_bool rq_putdesc (RUN_DECL, MSC *cp, struct uq_ring *ring, uint32 desc) +{ + uint32 prvd, newd = (desc & ~UQ_DESC_OWN) | UQ_DESC_F; + uint32 prva, addr = ring->ba + ring->idx; + uint16 d[2]; + + d[0] = newd & 0xFFFF; /* 32b to 16b */ + d[1] = (newd >> 16) & 0xFFFF; +#if defined(VM_VAX_MP) + /* execute MB (to sync memory in multiprocessor case) before posting ownership bit back to host */ + if (Map_WriteW (RUN_PASS, addr, 2, d)) /* store desc low word */ + return rq_fatal (cp, PE_QWE); /* err? dead */ + smp_mb(); + if (Map_WriteW (RUN_PASS, addr + 2, 2, d + 1)) /* store desc hi word (with O-bit) */ + return rq_fatal (cp, PE_QWE); /* err? dead */ +#else + if (Map_WriteW (RUN_PASS, addr, 4, d)) /* store desc */ + return rq_fatal (cp, PE_QWE); /* err? dead */ +#endif + if (desc & UQ_DESC_F) /* was F set? */ + { + if (ring->lnt <= 4) /* lnt = 1? intr */ + rq_ring_int (RUN_PASS, cp, ring); + else /* prv desc */ + { + prva = ring->ba + ((ring->idx - 4) & (ring->lnt - 1)); +#if defined(VM_VAX_MP) + smp_mb(); /* order access */ + if (Map_ReadW (RUN_PASS, prva + 2, 2, d + 1)) /* read prv hi word (incl. O-bit) */ + return rq_fatal (cp, PE_QRE); + smp_rmb(); + if (Map_ReadW (RUN_PASS, prva, 2, d)) /* read prv low word */ + return rq_fatal (cp, PE_QRE); +#else + if (Map_ReadW (RUN_PASS, prva, 4, d)) /* read prv */ + return rq_fatal (cp, PE_QRE); +#endif + prvd = ((uint32) d[0]) | (((uint32) d[1]) << 16); + if (prvd & UQ_DESC_OWN) + { + /* rq_ring_int performs smp_mb */ + rq_ring_int (RUN_PASS, cp, ring); + } + } + } + ring->idx = (ring->idx + 4) & (ring->lnt - 1); + return OK; +} + +/* Get unit descriptor for logical unit */ + +UNIT *rq_getucb (MSC *cp, uint32 lu) +{ + DEVICE *dptr = rq_devmap[cp->cnum]; + UNIT *uptr; + + if ((lu < cp->ubase) || (lu >= (cp->ubase + RQ_NUMDR))) + return NULL; + uptr = dptr->units[lu % RQ_NUMDR]; + if (uptr->flags & UNIT_DIS) + return NULL; + return uptr; +} + +/* Hack unit flags */ + +void rq_setf_unit (MSC *cp, int32 pkt, UNIT *uptr) +{ + uptr->uf = cp->pak[pkt].d[ONL_UFL] & UF_MSK; /* settable flags */ + if ((cp->pak[pkt].d[CMD_MOD] & MD_SWP) && /* swre wrp enb? */ + (cp->pak[pkt].d[ONL_UFL] & UF_WPS)) /* swre wrp on? */ + uptr->uf = uptr->uf | UF_WPS; /* simon says... */ +} + +/* Unit response fields */ + +void rq_putr_unit (MSC *cp, int32 pkt, UNIT *uptr, uint32 lu, t_bool all) +{ + uint32 dtyp = GET_DTYPE (uptr->flags); /* get drive type */ + uint32 maxlbn = (uint32) (uptr->capac / RQ_NUMBY); /* get max lbn */ + + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_putr_unit\n"); + + cp->pak[pkt].d[ONL_MLUN] = lu; /* unit */ + cp->pak[pkt].d[ONL_UFL] = uptr->uf | UF_RPL | RQ_WPH (uptr) | RQ_RMV (uptr); + cp->pak[pkt].d[ONL_RSVL] = 0; /* reserved */ + cp->pak[pkt].d[ONL_RSVH] = 0; + cp->pak[pkt].d[ONL_UIDA] = lu; /* UID low */ + cp->pak[pkt].d[ONL_UIDB] = 0; + cp->pak[pkt].d[ONL_UIDC] = 0; + cp->pak[pkt].d[ONL_UIDD] = (UID_DISK << ONL_UIDD_V_CLS) | + (drv_tab[dtyp].mod << ONL_UIDD_V_MOD); /* UID hi */ + PUTP32 (pkt, ONL_MEDL, drv_tab[dtyp].med); /* media type */ + if (all) /* if long form */ + { + PUTP32 (pkt, ONL_SIZL, maxlbn); /* user LBNs */ + cp->pak[pkt].d[ONL_VSNL] = 01234 + lu; /* vol serial # */ + cp->pak[pkt].d[ONL_VSNH] = 0; + } +} + +/* UQ_HDR and RSP_OP fields */ + +void rq_putr (MSC *cp, int32 pkt, uint32 cmd, uint32 flg, + uint32 sts, uint32 lnt, uint32 typ) +{ + cp->pak[pkt].d[RSP_OPF] = (cmd << RSP_OPF_V_OPC) | /* set cmd, flg */ + (flg << RSP_OPF_V_FLG); + cp->pak[pkt].d[RSP_STS] = sts; + cp->pak[pkt].d[UQ_HLNT] = lnt; /* length */ + cp->pak[pkt].d[UQ_HCTC] = (typ << UQ_HCTC_V_TYP) | /* type, cid */ + (UQ_CID_MSCP << UQ_HCTC_V_CID); /* clr credits */ +} + +/* Post interrupt during init */ + +void rq_init_int (MSC *cp) +{ + if ((cp->s1dat & SA_S1H_IE) && /* int enab & */ + (cp->s1dat & SA_S1H_VEC)) /* ved set? int */ + { + rq_setint (cp); + } +} + +/* Post interrupt during putpkt - note that NXMs are ignored! */ + +void rq_ring_int (RUN_DECL, MSC *cp, struct uq_ring *ring) +{ + uint32 iadr = cp->comm + ring->ioff; /* addr intr wd */ + uint16 flag = 1; + + /* + * We are about to signal host that command ring transitioned full to non-full + * or response ring transitioned empty to non-empty. Issue memory barrier + * to ensure coming write to comm area is not reordered with previous access + * to the rings or data buffers. + */ + smp_mb(); + Map_WriteW (RUN_PASS, iadr, 2, &flag); /* write flag */ + smp_mb(); + + if (cp->s1dat & SA_S1H_VEC) /* if enb, intr */ + rq_setint (cp); +} + +/* + * Unfortunately there appears to be no lock-free way to consolidate per-controller interrupts + * for multiple controllers into master interrupt. Such consolidation is possible to shared counter, + * but it is impossible to atomically transfer this counter to VCPU interrupt state. + * + * Whereas non-atomic transfer would yield incorrect results. + * Consider for example the sequence where controller A clears interrupt, then controller B raises it: + * + * [controller A] [controller B] + * + * if (atomic_decr(interrupt_count) == 0) + * . + * . if (atomic_incr(interrupt_count) == 1) + * . SET_INT(RQ) + * . + * . + * CLR_INT(RQ) + * + * Because of race condition, interrupt incorrectly turns out cleared at the end. + * + * Therefore we have to set and clear interrupt under the protection of master lock. + * We could have used separate lock for master lock, but it is better to use controller A's lock. + * + */ + +/* Set RQ interrupt */ +void rq_setint (MSC *cp) +{ + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_setint\n"); + + if (cp->irq == 0) + { + cp->irq = 1; /* set ctrl int */ + + if (cp->cnum != 0) /* acquire master lock unless already holding it */ + rqa_lock->lock(); + + if (++smp_var(rq_pending_intrs) == 1) + SET_INT (RQ); /* set master int */ + + if (cp->cnum != 0) /* release master lock if was acquired */ + rqa_lock->unlock(); + } +} + +/* Clear RQ interrupt */ +void rq_clrint (MSC *cp, t_bool intack) +{ + sim_debug (DBG_TRC, rq_devmap[cp->cnum], "rq_clrint\n"); + + if (cp->irq == 1) + { + cp->irq = 0; /* clr ctrl int */ + + if (cp->cnum != 0) /* acquire master lock unless already holding it */ + rqa_lock->lock(); + + if (--smp_var(rq_pending_intrs) == 0) + CLR_INT (RQ); /* clear master int */ + else + { + if (intack) + SET_INT (RQ); /* set master int */ + } + + if (cp->cnum != 0) /* release master lock if was acquired */ + rqa_lock->unlock(); + } +} + +/* Return interrupt vector */ +int32 rq_inta (void) +{ + int32 i; + MSC *ncp; + DEVICE *dptr; + DIB *dibp; + + for (i = 0; i < RQ_NUMCT; i++) /* loop thru ctrl */ + { + dptr = rq_devmap[i]; /* get device */ + if (dptr->flags & DEV_DIS) /* skip unconfigured devices */ + continue; + + smp_lock* devlock = *rq_lockmap[i]; + devlock->lock(); + + ncp = rq_ctxmap[i]; /* get context */ + if (ncp->irq) /* ctrl int set? */ + { + dibp = (DIB *) dptr->ctxt; /* get DIB */ + rq_clrint (ncp, TRUE); /* clear int req */ + devlock->unlock(); /* release controller lock */ + return dibp->vec; /* return vector */ + } + + devlock->unlock(); + } + return 0; /* no intr req */ +} + +/* Fatal error */ + +t_bool rq_fatal (MSC *cp, uint32 err) +{ + DEVICE *dptr = rq_devmap[cp->cnum]; + + sim_debug (DBG_TRC, dptr, "rq_fatal\n"); + sim_debug (DBG_REQ, dptr, "fatal err=%X\n", err); + + smp_mb(); + rq_reset (rq_devmap[cp->cnum]); /* reset device */ + cp->sa = SA_ER | err; /* SA = dead code */ + cp->csta = CST_DEAD; /* state = dead */ + cp->perr = err; /* save error */ + return ERR; +} + +/* Set/clear hardware write lock */ + +t_stat rq_set_wlk (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + uint32 dtyp = GET_DTYPE (uptr->flags); /* get drive type */ + + if (drv_tab[dtyp].flgs & RQDF_RO) /* not on read only */ + return SCPE_NOFNC; + return SCPE_OK; +} + +/* Show write lock status */ + +t_stat rq_show_wlk (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ + uint32 dtyp = GET_DTYPE (uptr->flags); /* get drive type */ + + if (drv_tab[dtyp].flgs & RQDF_RO) + fprintf (st, "read only"); + else if (uptr->flags & UNIT_WPRT) + fprintf (st, "write locked"); + else + fprintf (st, "write enabled"); + + return SCPE_OK; +} + +/* Set unit type (and capacity if user defined) */ + +t_stat rq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + uint32 cap; + uint32 max = sim_taddr_64? RA8U_EMAXC: RA8U_MAXC; + t_stat r; + + if (val < 0 || (val != RA8U_DTYPE && cptr)) + return SCPE_ARG; + if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; + if (cptr) + { + cap = (uint32) get_uint (cptr, 10, 0xFFFFFFFF, &r); + if ((sim_switches & SWMASK ('L')) == 0) + cap = cap * 1954; + if ((r != SCPE_OK) || (cap < RA8U_MINC) || (cap >= max)) + return SCPE_ARG; + drv_tab[val].lbn = cap; + } + uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (val << UNIT_V_DTYPE); + uptr->capac = ((t_addr) drv_tab[val].lbn) * RQ_NUMBY; + + return SCPE_OK; +} + +/* Show unit type */ + +t_stat rq_show_type (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ + fprintf (st, "%s", drv_tab[GET_DTYPE (uptr->flags)].name); + return SCPE_OK; +} + +/* Device attach */ + +t_stat rq_attach (UNIT *uptr, char *cptr) +{ + MSC *cp = rq_ctxmap[uptr->cnum]; + t_stat r; + + r = sim_disk_attach (uptr, cptr, RQ_NUMBY, sizeof (uint16), (uptr->flags & UNIT_NOAUTO), DBG_DSK, drv_tab[GET_DTYPE (uptr->flags)].name, 0); + if (r != SCPE_OK) + return r; + if (cp->csta == CST_UP && sim_disk_isavailable (uptr)) + uptr->flags = uptr->flags | UNIT_ATP; + return SCPE_OK; +} + +/* Device detach */ + +t_stat rq_detach (UNIT *uptr) +{ + t_stat r; + + r = sim_disk_detach (uptr); /* detach unit */ + if (r != SCPE_OK) + return r; + uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP); /* clr onl, atn pend */ + uptr->uf = 0; /* clr unit flgs */ + return SCPE_OK; +} + +/* Device reset */ + +t_stat rq_reset (DEVICE *dptr) +{ + int32 i, j, cidx; + UNIT *uptr; + MSC *cp; + DIB *dibp = (DIB *) dptr->ctxt; + + sim_debug (DBG_TRC, dptr, "rq_reset\n"); + + for (i = 0, cidx = -1; i < RQ_NUMCT; i++) /* find ctrl num */ + { + if (rq_devmap[i] == dptr) + cidx = i; + } + if (cidx < 0) /* not found??? */ + return SCPE_IERR; + AUTO_LOCK_CTRL(cidx); + sim_bind_devunits_lock(dptr, *rq_lockmap[cidx]); + cp = rq_ctxmap[cidx]; /* get context */ + cp->cnum = cidx; /* init index */ + +#if defined (VM_VAX) /* VAX */ + cp->ubase = 0; /* unit base = 0 */ +#else /* PDP-11 */ + cp->ubase = cidx * RQ_NUMDR; /* init unit base */ +#endif + + cp->csta = CST_S1; /* init stage 1 */ + cp->s1dat = 0; /* no S1 data */ + io_change_vec(dibp, 0); /* no vector */ + cp->comm = 0; /* no comm region */ + if (UNIBUS) /* Unibus? */ + cp->sa = SA_S1 | SA_S1C_DI | SA_S1C_MP; + else cp->sa = SA_S1 | SA_S1C_Q22 | SA_S1C_DI | SA_S1C_MP; /* init SA val */ + cp->cflgs = CF_RPL; /* ctrl flgs off */ + cp->htmo = RQ_DHTMO; /* default timeout */ + cp->hat = cp->htmo; /* default timer */ + cp->cq.ba = cp->cq.lnt = cp->cq.idx = 0; /* clr cmd ring */ + cp->rq.ba = cp->rq.lnt = cp->rq.idx = 0; /* clr rsp ring */ + cp->credits = (RQ_NPKTS / 2) - 1; /* init credits */ + cp->freq = 1; /* init free list */ + for (i = 0; i < RQ_NPKTS; i++) /* all pkts free */ + { + if (i) + cp->pak[i].link = (i + 1) & RQ_M_NPKTS; + else + cp->pak[i].link = 0; + for (j = 0; j < RQ_PKT_SIZE_W; j++) + cp->pak[i].d[j] = 0; + } + cp->rspq = 0; /* no q'd rsp pkts */ + cp->pbsy = 0; /* all pkts free */ + cp->pip = 0; /* not polling */ + rq_clrint (cp); /* clr intr req */ + sim_disk_reset (dptr); + for (i = 0; i < RQ_NUMDR + 2; i++) /* init units */ + { + uptr = dptr->units[i]; + sim_cancel (uptr); /* clr activity */ + if (uptr->flags & UNIT_ATTABLE) + uptr->io_complete = 0; + uptr->cnum = cidx; /* set ctrl index */ + uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP); + uptr->uf = 0; /* clr unit flags */ + uptr->cpkt = uptr->pktq = 0; /* clr pkt q's */ + uptr->rqxb = (uint16 *) realloc (uptr->rqxb, (RQ_MAXFR >> 1) * sizeof (uint16)); + if (uptr->rqxb == NULL) + return SCPE_MEM; + } + return auto_config (0, 0); /* run autoconfig */ +} + +/* Device bootstrap */ + +#if defined (VM_PDP11) + +#define BOOT_START 016000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_UNIT (BOOT_START + 010) /* unit number */ +#define BOOT_CSR (BOOT_START + 014) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + + 0042125, /* st: "UD" */ + + /* Four step init process */ + + 0012706, 0016000, /* mov #st,sp */ + 0012700, 0000000, /* mov #unit,r0 */ + 0012701, 0172150, /* mov #172150, r1 ; ip addr */ + 0012704, 0016162, /* mov #it, r4 */ + 0012705, 0004000, /* mov #4000,r5 ; s1 mask */ + 0010102, /* mov r1,r2 */ + 0005022, /* clr (r2)+ ; init */ + 0005712, /* 10$: tst (r2) ; err? */ + 0100001, /* bpl 20$ */ + 0000000, /* halt */ + 0030512, /* 20$: bit r5,(r2) ; step set? */ + 0001773, /* beq 10$ ; wait */ + 0012412, /* mov (r4)+,(r2) ; send next */ + 0006305, /* asl r5 ; next mask */ + 0100370, /* bpl 10$ ; s4 done? */ + + /* Send ONL, READ commands */ + + 0105714, /* 30$: tstb (r4) ; end tbl? */ + 0001434, /* beq done ; 0 = yes */ + 0012702, 0007000, /* mov #rpkt-4,r2 ; clr pkts */ + 0005022, /* 40$: clr (r2)+ */ + 0020227, 0007204, /* cmp r2,#comm */ + 0103774, /* blo 40$ */ + 0112437, 0007100, /* movb (r4)+,cpkt-4 ; set lnt */ + 0110037, 0007110, /* movb r0,cpkt+4 ; set unit */ + 0112437, 0007114, /* movb (r4)+,cpkt+10 ; set op */ + 0112437, 0007121, /* movb (r4)+,cpkt+15 ; set param */ + 0012722, 0007004, /* mov #rpkt,(r2)+ ; rq desc */ + 0010522, /* mov r5,(r2)+ ; rq own */ + 0012722, 0007104, /* mov #ckpt,(r2)+ ; cq desc */ + 0010512, /* mov r5,(r2) ; cq own */ + 0024242, /* cmp -(r2),-(r2) ; back up */ + 0005711, /* tst (r1) ; wake ctrl */ + 0005712, /* 50$: tst (r2) ; rq own clr? */ + 0100776, /* bmi 50$ ; wait */ + 0005737, 0007016, /* tst rpkt+12 ; stat ok? */ + 0001743, /* beq 30$ ; next cmd */ + 0000000, /* halt */ + + /* Boot block read in, jump to 0 */ + + 0005011, /* done: clr (r1) ; for M+ */ + 0005003, /* clr r3 */ + 0012704, BOOT_START+020, /* mov #st+020,r4 */ + 0005005, /* clr r5 */ + 0005007, /* clr pc */ + + /* Data */ + + 0100000, /* it: no ints, ring sz = 1 */ + 0007204, /* .word comm */ + 0000000, /* .word 0 */ + 0000001, /* .word 1 */ + 0004420, /* .byte 20,11 */ + 0020000, /* .byte 0,40 */ + 0001041, /* .byte 41,2 */ + 0000000 + }; + +t_stat rq_boot (int32 unitno, DEVICE *dptr) +{ + int32 i; + extern int32 saved_PC; + extern uint16 *M; + DIB *dibp = (DIB *) dptr->ctxt; + + for (i = 0; i < BOOT_LEN; i++) + M[(BOOT_START >> 1) + i] = boot_rom[i]; + M[BOOT_UNIT >> 1] = unitno & 3; + M[BOOT_CSR >> 1] = dibp->ba & DMASK; + saved_PC = BOOT_ENTRY; + return SCPE_OK; +} + +#else + +t_stat rq_boot (int32 unitno, DEVICE *dptr) +{ + //AUTO_LOCK_CTRL(...); + return SCPE_NOFNC; +} +#endif + +/* Special show commands */ + +void rq_show_ring (SMP_FILE *st, struct uq_ring *rp) +{ + RUN_SCOPE; + uint32 i, desc; + uint16 d[2]; + +#if defined (VM_PDP11) + fprintf (st, "ring, base = %o, index = %d, length = %d\n", + rp->ba, rp->idx >> 2, rp->lnt >> 2); +#else + fprintf (st, "ring, base = %x, index = %d, length = %d\n", + rp->ba, rp->idx >> 2, rp->lnt >> 2); +#endif + + for (i = 0; i < (rp->lnt >> 2); i++) + { + if (Map_ReadW (RUN_PASS, rp->ba + (i << 2), 4, d)) { + fprintf (st, " %3d: non-existent memory\n", i); + break; + } + desc = ((uint32) d[0]) | (((uint32) d[1]) << 16); +#if defined (VM_PDP11) + fprintf (st, " %3d: %011o\n", i, desc); +#else + fprintf (st, " %3d: %08x\n", i, desc); +#endif + } +} + +void rq_show_pkt (SMP_FILE *st, MSC *cp, int32 pkt) +{ + int32 i, j; + uint32 cr = GETP (pkt, UQ_HCTC, CR); + uint32 typ = GETP (pkt, UQ_HCTC, TYP); + uint32 cid = GETP (pkt, UQ_HCTC, CID); + + fprintf (st, "packet %d, credits = %d, type = %d, cid = %d\n", + pkt, cr, typ, cid); + for (i = 0; i < RQ_SH_MAX; i = i + RQ_SH_PPL) + { + fprintf (st, " %2d:", i); + for (j = i; j < (i + RQ_SH_PPL); j++) +#if defined (VM_PDP11) + fprintf (st, " %06o", cp->pak[pkt].d[j]); +#else + fprintf (st, " %04x", cp->pak[pkt].d[j]); +#endif + fprintf (st, "\n"); + } +} + +t_stat rq_show_unitq (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ + MSC *cp = rq_ctxmap[uptr->cnum]; + DEVICE *dptr = rq_devmap[uptr->cnum]; + int32 pkt, u; + + u = sim_unit_index (uptr); + + if (cp->csta != CST_UP) + { + fprintf (st, "Controller is not initialized\n"); + return SCPE_OK; + } + + if ((uptr->flags & UNIT_ONL) == 0) + { + if (uptr->flags & UNIT_ATT) + fprintf (st, "Unit %d is available\n", u); + else fprintf (st, "Unit %d is offline\n", u); + return SCPE_OK; + } + + if (uptr->cpkt) + { + fprintf (st, "Unit %d current ", u); + rq_show_pkt (st, cp, uptr->cpkt); + if (pkt = uptr->pktq) { + do { + fprintf (st, "Unit %d queued ", u); + rq_show_pkt (st, cp, pkt); + } while (pkt = cp->pak[pkt].link); + } + } + else + { + fprintf (st, "Unit %d queues are empty\n", u); + } + + return SCPE_OK; +} + +t_stat rq_show_ctrl (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ + MSC *cp = rq_ctxmap[uptr->cnum]; + DEVICE *dptr = rq_devmap[uptr->cnum]; + int32 i, pkt; + + if (cp->csta != CST_UP) + { + fprintf (st, "Controller is not initialized\n"); + return SCPE_OK; + } + + if (val & RQ_SH_RI) + { + if (cp->pip) + fprintf (st, "Polling in progress, host timer = %d\n", cp->hat); + else fprintf (st, "Host timer = %d\n", cp->hat); + fprintf (st, "Command "); + rq_show_ring (st, &cp->cq); + fprintf (st, "Response "); + rq_show_ring (st, &cp->rq); + } + + if (val & RQ_SH_FR) + { + if (pkt = cp->freq) + { + for (i = 0; pkt != 0; i++, pkt = cp->pak[pkt].link) + { + if (i == 0) + fprintf (st, "Free queue = %d", pkt); + else if ((i % 16) == 0) + fprintf (st, ",\n %d", pkt); + else + fprintf (st, ", %d", pkt); + } + fprintf (st, "\n"); + } + else + { + fprintf (st, "Free queue is empty\n"); + } + } + if (val & RQ_SH_RS) + { + if (pkt = cp->rspq) + { + do + { + fprintf (st, "Response "); + rq_show_pkt (st, cp, pkt); + } + while (pkt = cp->pak[pkt].link); + } + else + { + fprintf (st, "Response queue is empty\n"); + } + } + + if (val & RQ_SH_UN) + { + for (i = 0; i < RQ_NUMDR; i++) + rq_show_unitq (st, dptr->units[i], 0, desc); + } + + return SCPE_OK; +} diff --git a/src/PDP11/pdp11_ry.cpp b/src/PDP11/pdp11_ry.cpp new file mode 100644 index 0000000..22be5d1 --- /dev/null +++ b/src/PDP11/pdp11_ry.cpp @@ -0,0 +1,700 @@ +/* pdp11_ry.c: RX211/RXV21/RX02 floppy disk simulator + + 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. + + ry RX211/RXV21/RX02 floppy disk + + 15-May-06 RMS Fixed bug in autosize attach (reported by David Gesswein) + 07-Jul-05 RMS Removed extraneous externs + 18-Feb-05 RMS Fixed bug in boot code (reported by Graham Toal) + 30-Sep-04 RMS Revised Unibus interface + 21-Mar-04 RMS Added VAX support + 29-Dec-03 RMS Added RXV21 support + 19-May-03 RMS Revised for new conditional compilation scheme + 25-Apr-03 RMS Revised for extended file support + 14-Mar-03 RMS Fixed variable size interaction with save/restore + 03-Mar-03 RMS Fixed autosizing + 12-Oct-02 RMS Added autoconfigure support + + An RX02 diskette consists of 77 tracks, each with 26 sectors of 256B. + Tracks are numbered 0-76, sectors 1-26. +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#include "pdp10_defs.h" +extern int32 int_req; +#define DEV_DISI DEV_DIS + +#elif defined (VM_VAX) /* VAX version */ +#include "sim_defs.h" +#include "vax_defs.h" +#define DEV_DISI 0 + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#define DEV_DISI DEV_DIS +#endif + +#define RX_NUMTR 77 /* tracks/disk */ +#define RX_M_TRACK 0377 +#define RX_NUMSC 26 /* sectors/track */ +#define RX_M_SECTOR 0177 +#define RX_NUMBY 128 +#define RX_SIZE (RX_NUMTR * RX_NUMSC * RX_NUMBY) +#define RY_NUMBY 256 /* bytes/sector */ +#define RY_SIZE (RX_NUMTR * RX_NUMSC * RY_NUMBY) +#define RX_NUMDR 2 /* drives/controller */ +#define RX_M_NUMDR 01 +#define UNIT_V_WLK (UNIT_V_UF) /* write locked */ +#define UNIT_V_DEN (UNIT_V_UF + 1) /* double density */ +#define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize */ +#define UNIT_WLK (1u << UNIT_V_WLK) +#define UNIT_DEN (1u << UNIT_V_DEN) +#define UNIT_AUTO (1u << UNIT_V_AUTO) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +#define IDLE 0 /* idle state */ +#define RWDS 1 /* rw, sect next */ +#define RWDT 2 /* rw, track next */ +#define RWXFR 3 /* rw, transfer */ +#define FEWC 4 /* fill empty, wc next */ +#define FEBA 5 /* fill empty, ba next */ +#define FEXFR 6 /* fill empty, transfer */ +#define SDCNF 7 /* set dens, conf next */ +#define SDXFR 8 /* set dens, transfer */ +#define ESBA 9 /* ext sta, ba next */ +#define ESXFR 10 /* ext sta, transfer */ +#define CMD_COMPLETE 11 /* set done next */ +#define INIT_COMPLETE 12 /* init compl next */ + +#define RYCS_V_FUNC 1 /* function */ +#define RYCS_M_FUNC 7 +#define RYCS_FILL 0 /* fill buffer */ +#define RYCS_EMPTY 1 /* empty buffer */ +#define RYCS_WRITE 2 /* write sector */ +#define RYCS_READ 3 /* read sector */ +#define RYCS_SDEN 4 /* set density */ +#define RYCS_RYES 5 /* read status */ +#define RYCS_WRDEL 6 /* write del data */ +#define RYCS_ESTAT 7 /* read ext status */ +#define RYCS_V_DRV 4 /* drive select */ +#define RYCS_V_DONE 5 /* done */ +#define RYCS_V_IE 6 /* int enable */ +#define RYCS_V_TR 7 /* xfer request */ +#define RYCS_V_DEN 8 /* density select */ +#define RYCS_V_RY 11 /* RX02 flag */ +#define RYCS_V_UAE 12 /* addr ext */ +#define RYCS_M_UAE 03 +#define RYCS_V_INIT 14 /* init */ +#define RYCS_V_ERR 15 /* error */ +#define RYCS_FUNC (RYCS_M_FUNC << RYCS_V_FUNC) +#define RYCS_DRV (1u << RYCS_V_DRV) +#define RYCS_DONE (1u << RYCS_V_DONE) +#define RYCS_IE (1u << RYCS_V_IE) +#define RYCS_TR (1u << RYCS_V_TR) +#define RYCS_DEN (1u << RYCS_V_DEN) +#define RYCS_RY (1u << RYCS_V_RY) +#define RYCS_UAE (RYCS_M_UAE << RYCS_V_UAE) +#define RYCS_INIT (1u << RYCS_V_INIT) +#define RYCS_ERR (1u << RYCS_V_ERR) +#define RYCS_IMP (RYCS_ERR+RYCS_UAE+RYCS_DEN+RYCS_TR+RYCS_IE+\ + RYCS_DONE+RYCS_DRV+RYCS_FUNC) +#define RYCS_RW (RYCS_UAE+RYCS_DEN+RYCS_IE+RYCS_DRV+RYCS_FUNC) +#define RYCS_GETFNC(x) (((x) >> RYCS_V_FUNC) & RYCS_M_FUNC) +#define RYCS_GETUAE(x) (((x) >> RYCS_V_UAE) & RYCS_M_UAE) + +#define RYES_CRC 00001 /* CRC error NI */ +#define RYES_ID 00004 /* init done */ +#define RYES_ACLO 00010 /* ACLO NI */ +#define RYES_DERR 00020 /* density err */ +#define RYES_DDEN 00040 /* drive density */ +#define RYES_DD 00100 /* deleted data */ +#define RYES_DRDY 00200 /* drive ready */ +#define RYES_USEL 00400 /* unit selected */ +#define RYES_WCO 02000 /* wc overflow */ +#define RYES_NXM 04000 /* nxm */ +#define RYES_ERR (RYES_NXM|RYES_WCO|RYES_DERR|RYES_ACLO|RYES_CRC) + +#define TRACK u3 /* current track */ +#define CALC_DA(t,s,b) (((t) * RX_NUMSC) + ((s) - 1)) * b + +int32 ry_csr = 0; /* control/status */ +int32 ry_dbr = 0; /* data buffer */ +int32 ry_esr = 0; /* error status */ +int32 ry_ecode = 0; /* error code */ +int32 ry_track = 0; /* desired track */ +int32 ry_sector = 0; /* desired sector */ +int32 ry_ba = 0; /* bus addr */ +int32 ry_wc = 0; /* word count */ +int32 ry_state = IDLE; /* controller state */ +int32 ry_stopioe = 1; /* stop on error */ +int32 ry_cwait = 100; /* command time */ +int32 ry_swait = 10; /* seek, per track */ +int32 ry_xwait = 1; /* tr set time */ +uint8 rx2xb[RY_NUMBY] = { 0 }; /* sector buffer */ +AUTO_INIT_DEVLOCK(ry_lock); /* device structures lock */ + +t_stat ry_rd (int32 *data, int32 PA, int32 access); +t_stat ry_wr (int32 data, int32 PA, int32 access); +t_stat ry_svc (RUN_SVC_DECL, UNIT *uptr); +t_stat ry_reset (DEVICE *dptr); +t_stat ry_boot (int32 unitno, DEVICE *dptr); +void ry_done (int32 esr_flags, int32 new_ecode); +t_stat ry_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat ry_attach (UNIT *uptr, char *cptr); + +/* RY11 data structures + + ry_dev RY device descriptor + ry_unit RY unit list + ry_reg RY register list + ry_mod RY modifier list +*/ + +DIB ry_dib = { + IOBA_RY, IOLN_RY, &ry_rd, &ry_wr, + 1, IVCL (RY), VEC_RY, { NULL } + }; + +UNIT* ry_unit[] = { + UDATA (&ry_svc, UNIT_DEN+UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RY_SIZE), + UDATA (&ry_svc, UNIT_DEN+UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RY_SIZE) + }; + +REG ry_reg[] = { + { GRDATA_GBL (RYCS, ry_csr, DEV_RDX, 16, 0) }, + { GRDATA_GBL (RYBA, ry_ba, DEV_RDX, 16, 0) }, + { GRDATA_GBL (RYWC, ry_wc, DEV_RDX, 8, 0) }, + { GRDATA_GBL (RYDB, ry_dbr, DEV_RDX, 16, 0) }, + { GRDATA_GBL (RYES, ry_esr, DEV_RDX, 12, 0) }, + { GRDATA_GBL (RYERR, ry_ecode, DEV_RDX, 8, 0) }, + { GRDATA_GBL (RYTA, ry_track, DEV_RDX, 8, 0) }, + { GRDATA_GBL (RYSA, ry_sector, DEV_RDX, 8, 0) }, + { DRDATA_GBL (STAPTR, ry_state, 4), REG_RO }, + { IRDATA_DEV (INT, IVCL (RY)) }, + { FLDATA_GBL (ERR, ry_csr, RYCS_V_ERR) }, + { FLDATA_GBL (TR, ry_csr, RYCS_V_TR) }, + { FLDATA_GBL (IE, ry_csr, RYCS_V_IE) }, + { FLDATA_GBL (DONE, ry_csr, RYCS_V_DONE) }, + { DRDATA_GBL (CTIME, ry_cwait, 24), PV_LEFT }, + { DRDATA_GBL (STIME, ry_swait, 24), PV_LEFT }, + { DRDATA_GBL (XTIME, ry_xwait, 24), PV_LEFT }, + { BRDATA_GBL (SBUF, rx2xb, 8, 8, RY_NUMBY) }, + { FLDATA_GBL (STOP_IOE, ry_stopioe, 0) }, + { URDATA_GBL (CAPAC, ry_unit, 0, capac, 10, T_ADDR_W, 0, RX_NUMDR, REG_HRO | PV_LEFT) }, + { GRDATA_GBL (DEVADDR, ry_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA_GBL (DEVVEC, ry_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB ry_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { (UNIT_DEN+UNIT_ATT), UNIT_ATT, "single density", NULL, NULL }, + { (UNIT_DEN+UNIT_ATT), (UNIT_DEN+UNIT_ATT), "double density", NULL, NULL }, + { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), 0, "single density", NULL, NULL }, + { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), UNIT_DEN, "double density", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_DEN), 0, NULL, "SINGLE", &ry_set_size }, + { (UNIT_AUTO+UNIT_DEN), UNIT_DEN, NULL, "DOUBLE", &ry_set_size }, + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, +#if defined (VM_PDP11) + { MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE", + &set_addr_flt, NULL, NULL }, +#endif + { 0 } + }; + +DEVICE ry_dev = { + "RY", ry_unit, ry_reg, ry_mod, + RX_NUMDR, DEV_RDX, 20, 1, DEV_RDX, 8, + NULL, NULL, &ry_reset, + &ry_boot, &ry_attach, NULL, + &ry_dib, DEV_FLTA | DEV_DISABLE | DEV_DISI | DEV_UBUS | DEV_Q18 + }; + +/* I/O dispatch routine, I/O addresses 17777170 - 17777172 + + 17777170 floppy CSR + 17777172 floppy data register +*/ + +t_stat ry_rd (int32 *data, int32 PA, int32 access) +{ +AUTO_LOCK(ry_lock); +switch ((PA >> 1) & 1) { /* decode PA<1> */ + + case 0: /* RYCS */ + ry_csr = (ry_csr & RYCS_IMP) | RYCS_RY; /* clear junk */ + *data = ry_csr; + break; + + case 1: /* RYDB */ + *data = ry_dbr; /* return data */ + break; + } /* end switch PA */ + +return SCPE_OK; +} + +t_stat ry_wr (int32 data, int32 PA, int32 access) +{ +AUTO_LOCK(ry_lock); +int32 drv; + +switch ((PA >> 1) & 1) { /* decode PA<1> */ + +/* Writing RYCS, three cases: + 1. Writing INIT, reset device + 2. Idle and writing new function + - clear error, done, transfer ready, int req + - save int enable, function, drive + - start new function + 3. Otherwise, write IE and update interrupts +*/ + + case 0: /* RYCS */ + ry_csr = (ry_csr & RYCS_IMP) | RYCS_RY; /* clear junk */ + if (access == WRITEB) data = (PA & 1)? /* write byte? */ + (ry_csr & 0377) | (data << 8): (ry_csr & ~0377) | data; + if (data & RYCS_INIT) { /* initialize? */ + ry_reset (&ry_dev); /* reset device */ + return SCPE_OK; /* end if init */ + } + if ((data & CSR_GO) && (ry_state == IDLE)) { /* new function? */ + ry_csr = (data & RYCS_RW) | RYCS_RY; + drv = ((ry_csr & RYCS_DRV)? 1: 0); /* reselect drv */ + switch (RYCS_GETFNC (data)) { + + case RYCS_FILL: case RYCS_EMPTY: + ry_state = FEWC; /* state = get wc */ + ry_csr = ry_csr | RYCS_TR; /* xfer is ready */ + break; + + case RYCS_SDEN: + ry_state = SDCNF; /* state = get conf */ + ry_csr = ry_csr | RYCS_TR; /* xfer is ready */ + break; + + case RYCS_ESTAT: + ry_state = ESBA; /* state = get ba */ + ry_csr = ry_csr | RYCS_TR; /* xfer is ready */ + break; + + case RYCS_READ: case RYCS_WRITE: case RYCS_WRDEL: + ry_state = RWDS; /* state = get sector */ + ry_csr = ry_csr | RYCS_TR; /* xfer is ready */ + ry_esr = ry_esr & RYES_ID; /* clear errors */ + ry_ecode = 0; + break; + + default: + ry_state = CMD_COMPLETE; /* state = cmd compl */ + sim_activate (ry_unit[drv], ry_cwait); + break; + } /* end switch func */ + return SCPE_OK; + } /* end if GO */ + if ((data & RYCS_IE) == 0) + CLR_INT (RY); + else if ((ry_csr & (RYCS_DONE + RYCS_IE)) == RYCS_DONE) + SET_INT (RY); + ry_csr = (ry_csr & ~RYCS_RW) | (data & RYCS_RW); + break; /* end case RYCS */ + +/* Accessing RYDB, two cases: + 1. Write idle, write + 2. Write not idle and TR set, state dependent +*/ + + case 1: /* RYDB */ + if ((PA & 1) || ((ry_state != IDLE) && ((ry_csr & RYCS_TR) == 0))) + return SCPE_OK; /* if ~IDLE, need tr */ + ry_dbr = data; /* save data */ + if (ry_state != IDLE) { + drv = ((ry_csr & RYCS_DRV)? 1: 0); /* select drv */ + sim_activate (ry_unit[drv], ry_xwait); /* sched event */ + ry_csr = ry_csr & ~RYCS_TR; /* clear xfer */ + } + break; /* end case RYDB */ + } /* end switch PA */ + +return SCPE_OK; +} + +/* Unit service; the action to be taken depends on the transfer state: + + IDLE Should never get here + FEWC Save word count, set TR, set FEBA + FEBA Save bus address, set FEXFR + FEXFR Fill/empty buffer + RWDS Save sector, set TR, set RWDT + RWDT Save track, set RWXFR + RWXFR Read/write buffer + SDCNF Check confirmation, set SDXFR + SDXFR Erase disk + CMD_COMPLETE copy requested data to ir, finish command + INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command +*/ + +t_stat ry_svc (RUN_SVC_DECL, UNIT *uptr) +{ +AUTO_LOCK(ry_lock); +RUN_SVC_CHECK_CANCELLED(uptr); +int32 i, t, func, bps; +static uint8 estat[8]; +uint32 ba, da; +int8 *fbuf = (int8*) uptr->filebuf; + +func = RYCS_GETFNC (ry_csr); /* get function */ +bps = (ry_csr & RYCS_DEN)? RY_NUMBY: RX_NUMBY; /* get sector size */ +ba = (RYCS_GETUAE (ry_csr) << 16) | ry_ba; /* get mem addr */ +switch (ry_state) { /* case on state */ + + case IDLE: /* idle */ + return SCPE_IERR; + + case FEWC: /* word count */ + ry_wc = ry_dbr & 0377; /* save WC */ + ry_csr = ry_csr | RYCS_TR; /* set TR */ + ry_state = FEBA; /* next state */ + return SCPE_OK; + + case FEBA: /* buffer address */ + ry_ba = ry_dbr; /* save buf addr */ + ry_state = FEXFR; /* next state */ + sim_activate (uptr, ry_cwait); /* schedule xfer */ + return SCPE_OK; + + case FEXFR: /* transfer */ + if ((ry_wc << 1) > bps) { /* wc too big? */ + ry_done (RYES_WCO, 0230); /* error */ + break; + } + if (func == RYCS_FILL) { /* fill? read */ + for (i = 0; i < RY_NUMBY; i++) + rx2xb[i] = 0; + t = Map_ReadB (RUN_PASS, ba, ry_wc << 1, rx2xb); + } + else t = Map_WriteB (RUN_PASS, ba, ry_wc << 1, rx2xb); + ry_wc = t >> 1; /* adjust wc */ + ry_done (t? RYES_NXM: 0, 0); /* done */ + break; + + case RWDS: /* wait for sector */ + ry_sector = ry_dbr & RX_M_SECTOR; /* save sector */ + ry_csr = ry_csr | RYCS_TR; /* set xfer */ + ry_state = RWDT; /* advance state */ + return SCPE_OK; + + case RWDT: /* wait for track */ + ry_track = ry_dbr & RX_M_TRACK; /* save track */ + ry_state = RWXFR; /* next state */ + sim_activate (uptr, /* sched xfer */ + ry_swait * abs (ry_track - uptr->TRACK)); + return SCPE_OK; + + case RWXFR: /* read/write */ + if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */ + ry_done (0, 0110); /* done, error */ + return IORETURN (ry_stopioe, SCPE_UNATT); + } + if (ry_track >= RX_NUMTR) { /* bad track? */ + ry_done (0, 0040); /* done, error */ + break; + } + uptr->TRACK = ry_track; /* now on track */ + if ((ry_sector == 0) || (ry_sector > RX_NUMSC)) { /* bad sect? */ + ry_done (0, 0070); /* done, error */ + break; + } + if (((uptr->flags & UNIT_DEN) != 0) ^ + ((ry_csr & RYCS_DEN) != 0)) { /* densities agree? */ + ry_done (RYES_DERR, 0240); /* no, error */ + break; + } + da = CALC_DA (ry_track, ry_sector, bps); /* get disk address */ + if (func == RYCS_WRDEL) /* del data? */ + ry_esr = ry_esr | RYES_DD; + if (func == RYCS_READ) { /* read? */ + for (i = 0; i < bps; i++) + rx2xb[i] = fbuf[da + i]; + } + else { + if (uptr->flags & UNIT_WPRT) { /* write and locked? */ + ry_done (0, 0100); /* done, error */ + break; + } + for (i = 0; i < bps; i++) /* write */ + fbuf[da + i] = rx2xb[i]; + da = da + bps; + if (da > uptr->hwmark) + uptr->hwmark = da; + } + ry_done (0, 0); /* done */ + break; + + case SDCNF: /* confirm set density */ + if ((ry_dbr & 0377) != 0111) { /* confirmed? */ + ry_done (0, 0250); /* no, error */ + break; + } + ry_state = SDXFR; /* next state */ + sim_activate (uptr, ry_cwait * 100); /* schedule operation */ + break; + + case SDXFR: /* erase disk */ + for (i = 0; i < (int32) uptr->capac; i++) + fbuf[i] = 0; + uptr->hwmark = (uint32) uptr->capac; + if (ry_csr & RYCS_DEN) + uptr->flags = uptr->flags | UNIT_DEN; + else uptr->flags = uptr->flags & ~UNIT_DEN; + ry_done (0, 0); + break; + + + case ESBA: + ry_ba = ry_dbr; /* save WC */ + ry_state = ESXFR; /* next state */ + sim_activate (uptr, ry_cwait); /* schedule xfer */ + return SCPE_OK; + + case ESXFR: + estat[0] = ry_ecode; /* fill 8B status */ + estat[1] = ry_wc; + estat[2] = ry_unit[0]->TRACK; + estat[3] = ry_unit[1]->TRACK; + estat[4] = ry_track; + estat[5] = ry_sector; + estat[6] = ((ry_csr & RYCS_DRV)? 0200: 0) | + ((ry_unit[1]->flags & UNIT_DEN)? 0100: 0) | + ((uptr->flags & UNIT_ATT)? 0040: 0) | + ((ry_unit[0]->flags & UNIT_DEN)? 0020: 0) | + ((ry_csr & RYCS_DEN)? 0001: 0); + estat[7] = uptr->TRACK; + t = Map_WriteB (RUN_PASS, ba, 8, estat); /* DMA to memory */ + ry_done (t? RYES_NXM: 0, 0); /* done */ + break; + + case CMD_COMPLETE: /* command complete */ + ry_done (0, 0); + break; + + case INIT_COMPLETE: /* init complete */ + ry_unit[0]->TRACK = 1; /* drive 0 to trk 1 */ + ry_unit[1]->TRACK = 0; /* drive 1 to trk 0 */ + if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */ + ry_done (RYES_ID, 0010); /* init done, error */ + break; + } + da = CALC_DA (1, 1, bps); /* track 1, sector 1 */ + for (i = 0; i < bps; i++) /* read sector */ + rx2xb[i] = fbuf[da + i]; + ry_done (RYES_ID, 0); /* set done */ + if ((ry_unit[1]->flags & UNIT_ATT) == 0) + ry_ecode = 0020; + break; + } /* end case state */ + +return SCPE_OK; +} + +/* Command complete. Set done and put final value in interface register, + request interrupt if needed, return to IDLE state. +*/ + +void ry_done (int32 esr_flags, int32 new_ecode) +{ +int32 drv = (ry_csr & RYCS_DRV)? 1: 0; + +ry_state = IDLE; /* now idle */ +ry_csr = ry_csr | RYCS_DONE; /* set done */ +if (ry_csr & CSR_IE) /* if ie, intr */ + SET_INT (RY); +ry_esr = (ry_esr | esr_flags) & ~(RYES_USEL|RYES_DDEN|RYES_DRDY); +if (drv) /* updates RYES */ + ry_esr = ry_esr | RYES_USEL; +if (ry_unit[drv]->flags & UNIT_ATT) { + ry_esr = ry_esr | RYES_DRDY; + if (ry_unit[drv]->flags & UNIT_DEN) + ry_esr = ry_esr | RYES_DDEN; + } +if ((new_ecode > 0) || (ry_esr & RYES_ERR)) /* test for error */ + ry_csr = ry_csr | RYCS_ERR; +ry_ecode = new_ecode; /* update ecode */ +ry_dbr = ry_esr; /* update RYDB */ +return; +} + +/* Device initialization. The RY is one of the few devices that schedules + an I/O transfer as part of its initialization. +*/ + +t_stat ry_reset (DEVICE *dptr) +{ +AUTO_LOCK(ry_lock); +sim_bind_devunits_lock(&ry_dev, ry_lock); +ry_csr = ry_dbr = 0; /* clear registers */ +ry_esr = ry_ecode = 0; /* clear error */ +ry_ba = ry_wc = 0; /* clear wc, ba */ +ry_track = ry_sector = 0; /* clear trk, sector */ +ry_state = IDLE; /* ctrl idle */ +CLR_INT (RY); /* clear int req */ +sim_cancel (ry_unit[1]); /* cancel drive 1 */ +if (dptr->flags & UNIT_DIS) /* disabled? */ + sim_cancel (ry_unit[0]); +else if (ry_unit[0]->flags & UNIT_BUF) { /* attached? */ + ry_state = INIT_COMPLETE; /* yes, sched init */ + sim_activate (ry_unit[0], ry_swait * abs (1 - ry_unit[0]->TRACK)); + } +else ry_done (RYES_ID, 0010); /* no, error */ +return auto_config (0, 0); /* run autoconfig */ +} + +/* Attach routine */ + +t_stat ry_attach (UNIT *uptr, char *cptr) +{ +uint32 sz; + +if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { + if (sz > RX_SIZE) + uptr->flags = uptr->flags | UNIT_DEN; + else uptr->flags = uptr->flags & ~UNIT_DEN; + } +uptr->capac = (uptr->flags & UNIT_DEN)? RY_SIZE: RX_SIZE; +return attach_unit (uptr, cptr); +} + +/* Set size routine */ + +t_stat ry_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; +uptr->capac = val? RY_SIZE: RX_SIZE; +return SCPE_OK; +} + +/* Device bootstrap */ + +#if defined (VM_PDP11) + +#define BOOT_START 02000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_UNIT (BOOT_START + 010) /* unit number */ +#define BOOT_CSR (BOOT_START + 026) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 042131, /* "YD" */ + 0012706, BOOT_START, /* MOV #boot_start, SP */ + 0012700, 0000000, /* MOV #unit, R0 ; unit number */ + 0010003, /* MOV R0, R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0012701, 0177170, /* MOV #RYCS, R1 ; csr */ + 0005002, /* CLR R2 ; ba */ + 0005004, /* CLR R4 ; density */ + 0012705, 0000001, /* MOV #1, R5 ; sector */ + 0005104, /* DN: COM R4 ; compl dens */ + 0042704, 0177377, /* BIC #177377, R4 ; clr rest */ + 0032711, 0000040, /* RD: BIT #40, (R1) ; ready? */ + 0001775, /* BEQ .-4 */ + 0012746, 0000007, /* MOV #READ+GO, -(SP) */ + 0050316, /* BIS R3, (SP) ; or unit */ + 0050416, /* BIS R4, (SP) ; or density */ + 0012611, /* MOV (SP)+, (R1) ; read & go */ + 0105711, /* TSTB (R1) ; xfr ready? */ + 0100376, /* BPL .-2 */ + 0010561, 0000002, /* MOV R5, 2(R1) ; sector */ + 0105711, /* TSTB (R1) ; xfr ready? */ + 0100376, /* BPL .-2 */ + 0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; track */ + 0032711, 0000040, /* BIT #40, (R1) ; ready? */ + 0001775, /* BEQ .-4 */ + 0005711, /* TST (R1) ; error? */ + 0100003, /* BEQ OK */ + 0005704, /* TST R4 ; single? */ + 0001345, /* BNE DN ; no, try again */ + 0000000, /* HALT ; dead */ + 0012746, 0000003, /* OK: MOV #EMPTY+GO, -(SP); empty & go */ + 0050416, /* BIS R4, (SP) ; or density */ + 0012611, /* MOV (SP)+, (R1) ; read & go */ + 0105711, /* TSTB (R1) ; xfr, done? */ + 0001776, /* BPL .-2 */ + 0012746, 0000100, /* MOV #100, -(SP) ; assume sd */ + 0005704, /* TST R4 ; test dd */ + 0001401, /* BEQ .+4 */ + 0006316, /* ASL (SP) ; dd, double */ + 0011661, 0000002, /* MOV (SP), 2(R1) ; wc */ + 0105711, /* TSTB (R1) ; xfr, done? */ + 0001776, /* BPL .-2 */ + 0010261, 0000002, /* MOV R2, 2(R1) ; ba */ + 0032711, 0000040, /* BIT #40, (R1) ; ready? */ + 0001775, /* BEQ .-4 */ + 0061602, /* ADD (SP), R2 ; cvt wd to byte */ + 0062602, /* ADD (SP)+, R2 ; adv buf addr */ + 0122525, /* CMPB (R5)+, (R5)+ ; sect += 2 */ + 0020527, 0000007, /* CMP R5, #7 ; end? */ + 0101715, /* BLOS RD ; read next */ + 0005002, /* CLR R2 */ + 0005003, /* CLR R3 */ + 0012704, BOOT_START+020, /* MOV #START+20, R4 */ + 0005005, /* CLR R5 */ + 0005007 /* CLR R7 */ + }; + +t_stat ry_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; +extern uint16 *M; + +if ((ry_unit[unitno & RX_M_NUMDR]->flags & UNIT_DEN) == 0) + return SCPE_NOFNC; +for (i = 0; i < BOOT_LEN; i++) + M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & RX_M_NUMDR; +M[BOOT_CSR >> 1] = ry_dib.ba & DMASK; +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} + +#else + +t_stat ry_boot (int32 unitno, DEVICE *dptr) +{ +//AUTO_LOCK(ry_lock); +return SCPE_NOFNC; +} + +#endif diff --git a/src/PDP11/pdp11_tq.cpp b/src/PDP11/pdp11_tq.cpp new file mode 100644 index 0000000..ea4c815 --- /dev/null +++ b/src/PDP11/pdp11_tq.cpp @@ -0,0 +1,2500 @@ +/* pdp11_tq.c: TMSCP tape controller simulator + + 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"), + 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. + + tq TQK50 tape controller + + 23-Jan-12 MP Added missing support for Logical EOT detection while + positioning. + 05-Mar-11 MP Added missing state for proper save/restore + 01-Mar-11 MP - Migrated complex physical tape activities to sim_tape + - adopted use of asynch I/O interfaces from sim_tape + - Added differing detailed debug output via sim_debug + 14-Jan-11 MP Various fixes discovered while exploring Ultrix issue: + - Set UNIT_SXC flag when a tape mark is encountered + during forward motion read operations. + - Fixed logic which clears UNIT_SXC to check command + modifier. + - Added CMF_WR flag to tq_cmf entry for OP_WTM. + - Made Non-immediate rewind positioning operations + take 2 seconds. + - Added UNIT_IDLE flag to tq units. + - Fixed debug output of tape file positions when they + are 64b. Added more debug output after positioning + operations. Also, added textual display of the + command being performed (GUS,POS,RD,WR,etc…) + 18-Jun-07 RMS Added UNIT_IDLE flag to timer thread + 16-Feb-06 RMS Revised for new magtape capacity checking + 31-Oct-05 RMS Fixed address width for large files + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 22-Jul-05 RMS Fixed warning from Solaris C (from Doug Gwyn) + 30-Sep-04 RMS Revised Unibus interface + 12-Jun-04 RMS Fixed bug in reporting write protect (reported by Lyle Bickley) + 18-Apr-04 RMS Fixed TQK70 media ID and model byte (found by Robert Schaffrath) + 26-Mar-04 RMS Fixed warnings with -std=c99 + 25-Jan-04 RMS Revised for device debug support + 19-May-03 RMS Revised for new conditional compilation scheme + 25-Apr-03 RMS Revised for extended file support + 28-Mar-03 RMS Added multiformat support + 28-Feb-03 RMS Added variable controller, user-defined drive support + 26-Feb-03 RMS Fixed bug in vector calculation for VAXen + 22-Feb-03 RMS Fixed ordering bug in queue process + Fixed flags table to allow MD_CSE everywhere + 09-Jan-03 RMS Fixed bug in transfer end packet status + 17-Oct-02 RMS Fixed bug in read reverse (found by Hans Pufal) +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#error "TQK50 not supported on PDP-10!" + +#elif defined (VM_VAX) /* VAX version */ +#include "sim_defs.h" +#include "vax_defs.h" +#if (UNIBUS) +#define INIT_TYPE TQ8_TYPE +#define INIT_CAP TQ8_CAP +#else +#define INIT_TYPE TQ5_TYPE +#define INIT_CAP TQ5_CAP +#endif + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#define INIT_TYPE TQ5_TYPE +#define INIT_CAP TQ5_CAP +extern int32 cpu_opt; +#endif + +#include "pdp11_uqssp.h" +#include "pdp11_mscp.h" +#include "sim_tape.h" + +#define UF_MSK (UF_SCH|UF_VSS|UF_CMR|UF_CMW) /* settable flags */ + +#define TQ_SH_MAX 24 /* max display wds */ +#define TQ_SH_PPL 8 /* wds per line */ +#define TQ_SH_DPL 4 /* desc per line */ +#define TQ_SH_RI 001 /* show rings */ +#define TQ_SH_FR 002 /* show free q */ +#define TQ_SH_RS 004 /* show resp q */ +#define TQ_SH_UN 010 /* show unit q's */ +#define TQ_SH_ALL 017 /* show all */ + +#define TQ_CLASS 1 /* TQK50 class */ +#define TQ_DHTMO 0 /* def host timeout */ +/* + * Allow longer controller timeout interval for multiprocessor system, + * to avoid disruption that may be caused by thread preemption. + */ +#if defined(VM_VAX_MP) +# define TQ_DCTMO 240 /* def ctrl timeout */ +#else +# define TQ_DCTMO 120 /* def ctrl timeout */ +#endif +#define TQ_NUMDR 4 /* # drives */ +#define TQ_MAXFR (1 << 16) /* max xfer */ + +#define UNIT_V_ONL (MTUF_V_UF + 0) /* online */ +#define UNIT_V_ATP (MTUF_V_UF + 1) /* attn pending */ +#define UNIT_V_SXC (MTUF_V_UF + 2) /* serious exc */ +#define UNIT_V_POL (MTUF_V_UF + 3) /* position lost */ +#define UNIT_V_TMK (MTUF_V_UF + 4) /* tape mark seen */ +#define UNIT_ONL (1 << UNIT_V_ONL) +#define UNIT_ATP (1 << UNIT_V_ATP) +#define UNIT_SXC (1 << UNIT_V_SXC) +#define UNIT_POL (1 << UNIT_V_POL) +#define UNIT_TMK (1 << UNIT_V_TMK) +#define cpkt u3 /* current packet */ +#define pktq u4 /* packet queue */ +#define uf buf /* settable unit flags */ +#define objp wait /* object position */ +#define io_status u5 /* io status from callback */ +#define io_complete u6 /* io completion flag */ +#define TQ_WPH(u) ((sim_tape_wrp (u))? UF_WPH: 0) +#define results up7 /* xfer buffer & results */ + +#define CST_S1 0 /* init stage 1 */ +#define CST_S1_WR 1 /* stage 1 wrap */ +#define CST_S2 2 /* init stage 2 */ +#define CST_S3 3 /* init stage 3 */ +#define CST_S3_PPA 4 /* stage 3 sa wait */ +#define CST_S3_PPB 5 /* stage 3 ip wait */ +#define CST_S4 6 /* stage 4 */ +#define CST_UP 7 /* online */ +#define CST_DEAD 8 /* fatal error */ + +#define tq_comm tq_rq.ba + +#define ERR 0 /* must be SCPE_OK! */ +#define OK 1 + +#define CMF_IMM 0x10000 /* immediate */ +#define CMF_SEQ 0x20000 /* sequential */ +#define CMF_WR 0x40000 /* write */ +#define CMF_RW 0x80000 /* resp to GCS */ + +/* Internal packet management */ + +#define TQ_NPKTS 32 /* # packets (pwr of 2) */ +#define TQ_M_NPKTS (TQ_NPKTS - 1) /* mask */ +#define TQ_PKT_SIZE_W 32 /* payload size (wds) */ +#define TQ_PKT_SIZE (TQ_PKT_SIZE_W * sizeof (int16)) + +struct tqpkt { + int16 link; /* link to next */ + uint16 d[TQ_PKT_SIZE_W]; /* data */ + }; + +/* Packet payload extraction and insertion */ + +#define GETP(p,w,f) ((tq_pkt[p].d[w] >> w##_V_##f) & w##_M_##f) +#define GETP32(p,w) (((uint32) tq_pkt[p].d[w]) | \ + (((uint32) tq_pkt[p].d[(w)+1]) << 16)) +#define PUTP32(p,w,x) tq_pkt[p].d[w] = (x) & 0xFFFF; \ + tq_pkt[p].d[(w)+1] = ((x) >> 16) & 0xFFFF + +/* Controller and device types - TQK50 must be swre rev 5 or later */ + +#define TQ5_TYPE 0 /* TK50 */ +#define TQ5_UQPM 3 /* UQ port ID */ +#define TQ5_CMOD 9 /* ctrl ID */ +#define TQ5_UMOD 3 /* unit ID */ +#define TQ5_MED 0x6D68B032 /* media ID */ +#define TQ5_CREV ((1 << 8) | 5) /* ctrl revs */ +#define TQ5_FREV 0 /* formatter revs */ +#define TQ5_UREV 0 /* unit revs */ +#define TQ5_CAP (94 * (1 << 20)) /* capacity */ +#define TQ5_FMT (TF_CTP|TF_CTP_LO) /* menu */ + +#define TQ7_TYPE 1 /* TK70 */ +#define TQ7_UQPM 14 /* UQ port ID */ +#define TQ7_CMOD 14 /* ctrl ID */ +#define TQ7_UMOD 14 /* unit ID */ +#define TQ7_MED 0x6D68B046 /* media ID */ +#define TQ7_CREV ((1 << 8) | 5) /* ctrl revs */ +#define TQ7_FREV 0 /* formatter revs */ +#define TQ7_UREV 0 /* unit revs */ +#define TQ7_CAP (300 * (1 << 20)) /* capacity */ +#define TQ7_FMT (TF_CTP|TF_CTP_LO) /* menu */ + +#define TQ8_TYPE 2 /* TU81 */ +#define TQ8_UQPM 5 /* UQ port ID */ +#define TQ8_CMOD 5 /* ctrl ID */ +#define TQ8_UMOD 2 /* unit ID */ +#define TQ8_MED 0x6D695051 /* media ID */ +#define TQ8_CREV ((1 << 8) | 5) /* ctrl revs */ +#define TQ8_FREV 0 /* formatter revs */ +#define TQ8_UREV 0 /* unit revs */ +#define TQ8_CAP (180 * (1 << 20)) /* capacity */ +#define TQ8_FMT (TF_9TK|TF_9TK_GRP) /* menu */ + +#define TQU_TYPE 3 /* TKuser defined */ +#define TQU_UQPM 3 /* UQ port ID */ +#define TQU_CMOD 9 /* ctrl ID */ +#define TQU_UMOD 3 /* unit ID */ +#define TQU_MED 0x6D68B032 /* media ID */ +#define TQU_CREV ((1 << 8) | 5) /* ctrl revs */ +#define TQU_FREV 0 /* formatter revs */ +#define TQU_UREV 0 /* unit revs */ +#define TQU_CAP (94 * (1 << 20)) /* capacity */ +#define TQU_FMT (TF_CTP|TF_CTP_LO) /* menu */ +#define TQU_MINC 30 /* min cap MB */ +#define TQU_MAXC 2000 /* max cap MB */ +#define TQU_EMAXC 2000000000 /* ext max cap MB */ + +#define TQ_DRV(d) \ + d##_UQPM, \ + d##_CMOD, d##_MED, d##_FMT, d##_CAP, \ + d##_UMOD, d##_CREV, d##_FREV, d##_UREV + +#define TEST_EOT(u) (sim_tape_eot (u)) + +struct drvtyp { + uint32 uqpm; /* UQ port model */ + uint32 cmod; /* ctrl model */ + uint32 med; /* MSCP media */ + uint32 fmt; /* flags */ + t_addr cap; /* capacity */ + uint32 umod; /* unit model */ + uint32 cver; + uint32 fver; + uint32 uver; + char *name; + }; + +static struct drvtyp drv_tab[] = { + { TQ_DRV (TQ5), "TK50" }, + { TQ_DRV (TQ7), "TK70" }, + { TQ_DRV (TQ8), "TU81" }, + { TQ_DRV (TQU), "TKUSER" }, + }; + +/* Data */ + +extern SMP_FILE *sim_deb; + +static uint32 tq_sa = 0; /* status, addr */ +static uint32 tq_saw = 0; /* written data */ +static uint32 tq_s1dat = 0; /* S1 data */ +static uint32 tq_csta = 0; /* ctrl state */ +static uint32 tq_perr = 0; /* last error */ +static uint32 tq_cflgs = 0; /* ctrl flags */ +static uint32 tq_prgi = 0; /* purge int */ +static uint32 tq_pip = 0; /* poll in progress */ +static struct uq_ring tq_cq = { 0 }; /* cmd ring */ +static struct uq_ring tq_rq = { 0 }; /* rsp ring */ +static struct tqpkt tq_pkt[TQ_NPKTS]; /* packet queue */ +static int32 tq_freq = 0; /* free list */ +static int32 tq_rspq = 0; /* resp list */ +static uint32 tq_pbsy = 0; /* #busy pkts */ +static uint32 tq_credits = 0; /* credits */ +static uint32 tq_hat = 0; /* host timer */ +static uint32 tq_htmo = TQ_DHTMO; /* host timeout */ +static int32 tq_itime = 200; /* init time, except */ +static int32 tq_itime4 = 10; /* stage 4 */ +static int32 tq_qtime = 200; /* queue time */ +static int32 tq_xtime = 500; /* transfer time */ +static int32 tq_rwtime = 200; /* rewind time 2 sec (in clock ticks, adjusted later) */ +static int32 tq_typ = INIT_TYPE; /* device type */ +AUTO_INIT_DEVLOCK(tq_lock); /* device structures lock */ + +/* Command table - legal modifiers (low 16b) and flags (high 16b) */ + +static uint32 tq_cmf[64] = { + 0, /* 0 */ + CMF_IMM, /* abort */ + CMF_IMM|MD_CSE, /* get cmd status */ + CMF_IMM|MD_CSE|MD_NXU, /* get unit status */ + CMF_IMM|MD_CSE, /* set ctrl char */ + 0, 0, 0, /* 5-7 */ + CMF_SEQ|MD_ACL|MD_CDL|MD_CSE|MD_EXA|MD_UNL, /* available */ + CMF_SEQ|MD_CDL|MD_CSE|MD_SWP|MD_EXA, /* online */ + CMF_SEQ|MD_CDL|MD_CSE|MD_SWP|MD_EXA, /* set unit char */ + CMF_IMM, /* define acc paths */ + 0, 0, 0, 0, /* 12-15 */ + CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV| /* access */ + MD_SCH|MD_SEC|MD_SER, + 0, /* 17 */ + CMF_SEQ|CMF_WR|MD_CDL|MD_CSE|MD_IMM, /* erase */ + CMF_SEQ|CMF_WR|MD_CDL|MD_CSE, /* flush */ + 0, 0, /* 20-21 */ + CMF_SEQ|CMF_WR|MD_CDL|MD_CSE|MD_IMM, /* erase gap */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 22-31 */ + CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV| /* compare */ + MD_SCH|MD_SEC|MD_SER, + CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV|MD_CMP| /* read */ + MD_SCH|MD_SEC|MD_SER, + 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|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, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 38-47 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 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 */ + +t_stat tq_rd (int32 *data, int32 PA, int32 access); +t_stat tq_wr (int32 data, int32 PA, int32 access); +int32 tq_inta (void); +t_stat tq_svc (RUN_SVC_DECL, UNIT *uptr); +t_stat tq_tmrsvc (RUN_SVC_DECL, UNIT *uptr); +t_stat tq_quesvc (RUN_SVC_DECL, UNIT *uptr); +t_stat tq_reset (DEVICE *dptr); +t_stat tq_attach (UNIT *uptr, char *cptr); +t_stat tq_detach (UNIT *uptr); +t_stat tq_boot (int32 unitno, DEVICE *dptr); +t_stat tq_set_wlk (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tq_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tq_show_ctrl (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat tq_show_unitq (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat tq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tq_show_type (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); + +t_bool tq_step4 (RUN_DECL); +t_bool tq_mscp (int32 pkt, t_bool q); +t_bool tq_abo (int32 pkt); +t_bool tq_avl (int32 pkt); +t_bool tq_erase (int32 pkt); +t_bool tq_flu (int32 pkt); +t_bool tq_gcs (int32 pkt); +t_bool tq_gus (int32 pkt); +t_bool tq_onl (int32 pkt); +t_bool tq_pos (int32 pkt); +t_bool tq_rw (int32 pkt); +t_bool tq_scc (int32 pkt); +t_bool tq_suc (int32 pkt); +t_bool tq_wtm (int32 pkt); +t_bool tq_plf (uint32 err); +t_bool tq_dte (UNIT *uptr, uint32 err); +t_bool tq_hbe (UNIT *uptr, uint32 ba); +t_bool tq_una (UNIT *uptr); +uint32 tq_map_status (UNIT *uptr, t_stat st); +void tq_rdbuff_top (UNIT *uptr, t_mtrlnt *tbc); +uint32 tq_rdbuff_bottom (UNIT *uptr, t_mtrlnt *tbc); +void tq_rdbufr_top (UNIT *uptr, t_mtrlnt *tbc); +uint32 tq_rdbufr_bottom (UNIT *uptr, t_mtrlnt *tbc); +t_bool tq_deqf (int32 *pkt); +int32 tq_deqh (int32 *lh); +void tq_enqh (int32 *lh, int32 pkt); +void tq_enqt (int32 *lh, int32 pkt); +t_bool tq_getpkt (RUN_DECL, int32 *pkt); +t_bool tq_putpkt (RUN_DECL, int32 pkt, t_bool qt); +t_bool tq_getdesc (RUN_DECL, struct uq_ring *ring, uint32 *desc); +t_bool tq_putdesc (RUN_DECL, struct uq_ring *ring, uint32 desc); +int32 tq_mot_valid (UNIT *uptr, uint32 cmd); +t_stat tq_mot_err (UNIT *uptr, uint32 rsiz); +t_bool tq_mot_end (UNIT *uptr, uint32 flg, uint32 sts, uint32 rsiz); +void tq_putr (int32 pkt, uint32 cmd, uint32 flg, uint32 sts, uint32 lnt, uint32 typ); +void tq_putr_unit (int32 pkt, UNIT *uptr, uint32 lu, t_bool all); +void tq_setf_unit (int32 pkt, UNIT *uptr); +uint32 tq_efl (UNIT *uptr); +void tq_init_int (void); +void tq_ring_int (RUN_DECL, struct uq_ring *ring); +t_bool tq_fatal (uint32 err); +UNIT *tq_getucb (uint32 lu); + +/* TQ data structures + + tq_dev TQ device descriptor + tq_unit TQ unit list + tq_reg TQ register list + tq_mod TQ modifier list +*/ + +DIB tq_dib = { + IOBA_TQ, IOLN_TQ, &tq_rd, &tq_wr, + 1, IVCL (TQ), 0, { &tq_inta } + }; + +UNIT* tq_unit[] = { + UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE, INIT_CAP), + UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE, INIT_CAP), + UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE, INIT_CAP), + UDATA (&tq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE, INIT_CAP), + UDATA (&tq_tmrsvc, UNIT_IDLE|UNIT_DIS, 0), + UDATA (&tq_quesvc, UNIT_IDLE|UNIT_DIS, 0) + }; + +#define TQ_TIMER (TQ_NUMDR) +#define TQ_QUEUE (TQ_TIMER + 1) + +REG tq_reg[] = { + { GRDATA_GBL (SA, tq_sa, DEV_RDX, 16, 0) }, + { GRDATA_GBL (SAW, tq_saw, DEV_RDX, 16, 0) }, + { GRDATA_GBL (S1DAT, tq_s1dat, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CQIOFF, tq_cq.ioff, DEV_RDX, 32, 0) }, + { GRDATA_GBL (CQBA, tq_cq.ba, DEV_RDX, 22, 0) }, + { GRDATA_GBL (CQLNT, tq_cq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA_GBL (CQIDX, tq_cq.idx, DEV_RDX, 8, 2) }, + { GRDATA_GBL (TQIOFF, tq_rq.ioff, DEV_RDX, 32, 0) }, + { GRDATA_GBL (TQBA, tq_rq.ba, DEV_RDX, 22, 0) }, + { GRDATA_GBL (TQLNT, tq_rq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA_GBL (TQIDX, tq_rq.idx, DEV_RDX, 8, 2) }, + { DRDATA_GBL (FREE, tq_freq, 5) }, + { DRDATA_GBL (RESP, tq_rspq, 5) }, + { DRDATA_GBL (PBSY, tq_pbsy, 5) }, + { GRDATA_GBL (CFLGS, tq_cflgs, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CSTA, tq_csta, DEV_RDX, 4, 0) }, + { GRDATA_GBL (PERR, tq_perr, DEV_RDX, 9, 0) }, + { DRDATA_GBL (CRED, tq_credits, 5) }, + { DRDATA_GBL (HAT, tq_hat, 17) }, + { DRDATA_GBL (HTMO, tq_htmo, 17) }, + { URDATA_GBL (CPKT, tq_unit, 0, cpkt, 10, 5, 0, TQ_NUMDR, 0) }, + { URDATA_GBL (PKTQ, tq_unit, 0, pktq, 10, 5, 0, TQ_NUMDR, 0) }, + { URDATA_GBL (UFLG, tq_unit, 0, uf, DEV_RDX, 16, 0, TQ_NUMDR, 0) }, + { URDATA_GBL (POS, tq_unit, 0, pos, 10, T_ADDR_W, 0, TQ_NUMDR, 0) }, + { URDATA_GBL (OBJP, tq_unit, 0, objp, 10, 32, 0, TQ_NUMDR, 0) }, + { FLDATA_GBL (PRGI, tq_prgi, 0), REG_HIDDEN }, + { FLDATA_GBL (PIP, tq_pip, 0), REG_HIDDEN }, + { IRDATA_DEV (INT, IVCL (TQ)) }, + { DRDATA_GBL (ITIME, tq_itime, 24), PV_LEFT + REG_NZ }, + { DRDATA_GBL (I4TIME, tq_itime4, 24), PV_LEFT + REG_NZ }, + { DRDATA_GBL (QTIME, tq_qtime, 24), PV_LEFT + REG_NZ }, + { DRDATA_GBL (XTIME, tq_xtime, 24), PV_LEFT + REG_NZ }, + { DRDATA_GBL (RWTIME, tq_rwtime, 32), PV_LEFT + REG_NZ }, + { BRDATA_GBL (PKTS, tq_pkt, DEV_RDX, 16, TQ_NPKTS * (TQ_PKT_SIZE_W + 1)) }, + { DRDATA_GBL (DEVTYPE, tq_typ, 2), REG_HRO }, + { DRDATA_GBL (DEVCAP, drv_tab[TQU_TYPE].cap, T_ADDR_W), PV_LEFT | REG_HRO }, + { GRDATA_GBL (DEVADDR, tq_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA_GBL (DEVVEC, tq_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB tq_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD | MTAB_VDV, TQ5_TYPE, NULL, "TK50", + &tq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, TQ7_TYPE, NULL, "TK70", + &tq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, TQ8_TYPE, NULL, "TU81", + &tq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, TQU_TYPE, NULL, "TKUSER", + &tq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, + NULL, &tq_show_type, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_RI, "RINGS", NULL, + NULL, &tq_show_ctrl, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_FR, "FREEQ", NULL, + NULL, &tq_show_ctrl, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_RS, "RESPQ", NULL, + NULL, &tq_show_ctrl, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_UN, "UNITQ", NULL, + NULL, &tq_show_ctrl, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_ALL, "ALL", NULL, + NULL, &tq_show_ctrl, NULL }, + { MTAB_XTD | MTAB_VUN | MTAB_NMO, 0, "UNITQ", NULL, + NULL, &tq_show_unitq, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", + &sim_tape_set_capac, &sim_tape_show_capac, NULL }, +#if defined (VM_PDP11) + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE", + &set_addr_flt, NULL, NULL }, +#else + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL, + NULL, &show_addr, NULL }, +#endif + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { 0 } + }; + +/* debugging bitmaps */ +#define DBG_TRC 0x0001 /* trace routine calls */ +#define DBG_INI 0x0002 /* display setup/init sequence info */ +#define DBG_REG 0x0004 /* trace read/write registers */ +#define DBG_REQ 0x0008 /* display transfer requests */ +#define DBG_TAP 0x0010 /* display sim_tape activities */ +#define DBG_DAT 0x0020 /* display transfer data */ + +DEBTAB tq_debug[] = { + {"TRACE", DBG_TRC}, + {"INIT", DBG_INI}, + {"REG", DBG_REG}, + {"REQ", DBG_REQ}, + {"TAPE", DBG_TAP}, + {"DATA", DBG_DAT}, + {0} +}; + +DEVICE tq_dev = { + "TQ", tq_unit, tq_reg, tq_mod, + TQ_NUMDR + 2, 10, T_ADDR_W, 1, DEV_RDX, 8, + NULL, NULL, &tq_reset, + &tq_boot, &tq_attach, &tq_detach, + &tq_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_DEBUG, + 0, tq_debug + }; + + +struct tq_req_results { /* intermediate State during tape motion commands */ + t_stat io_status; + int32 io_complete; + int rewind_done; + uint32 sts; + uint32 sktmk; + uint32 skrec; + t_mtrlnt tbc; + int32 objupd; + uint8 tqxb[TQ_MAXFR]; + }; + +/* I/O dispatch routines, I/O addresses 17774500 - 17774502 + + 17774500 IP read/write + 17774502 SA read/write +*/ + +t_stat tq_rd (int32 *data, int32 PA, int32 access) +{ +RUN_SCOPE; +AUTO_LOCK(tq_lock); +sim_debug(DBG_REG, &tq_dev, "tq_rd(PA=0x%08X [%s], access=%d)\n", PA, ((PA >> 1) & 01) ? "IP" : "SA", access); + +switch ((PA >> 1) & 01) { /* decode PA<1> */ + case 0: /* IP */ + *data = 0; /* reads zero */ + if (tq_csta == CST_S3_PPB) /* waiting for poll? */ + tq_step4 (RUN_PASS); + else if (tq_csta == CST_UP) { /* if up */ + tq_pip = 1; /* poll host */ + sim_activate (tq_unit[TQ_QUEUE], tq_qtime); + } + break; + + case 1: /* SA */ + *data = tq_sa; + break; + } + +return SCPE_OK; +} + +t_stat tq_wr (int32 data, int32 PA, int32 access) +{ +AUTO_LOCK(tq_lock); +sim_debug(DBG_REG, &tq_dev, "tq_wr(PA=0x%08X [%s], access=%d)\n", PA, ((PA >> 1) & 01) ? "IP" : "SA", access); + +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 0: /* IP */ + tq_reset (&tq_dev); /* init device */ + sim_debug (DBG_REQ, &tq_dev, "initialization started\n"); + break; + + case 1: /* SA */ + tq_saw = data; + if (tq_csta < CST_S4) /* stages 1-3 */ + sim_activate (tq_unit[TQ_QUEUE], tq_itime); + else if (tq_csta == CST_S4) /* stage 4 (fast) */ + sim_activate (tq_unit[TQ_QUEUE], tq_itime4); + break; + } + +return SCPE_OK; +} + +/* Transition to step 4 - init communications region */ + +t_bool tq_step4 (RUN_DECL) +{ + int32 i, lnt; + uint32 base; + uint16 zero[SA_COMM_MAX >> 1]; + int32 wstatus; + + tq_rq.ioff = SA_COMM_RI; /* set intr offset */ + tq_rq.ba = tq_comm; /* set rsp q base */ + tq_rq.lnt = SA_S1H_RQ (tq_s1dat) << 2; /* get resp q len */ + tq_cq.ioff = SA_COMM_CI; /* set intr offset */ + tq_cq.ba = tq_comm + tq_rq.lnt; /* set cmd q base */ + tq_cq.lnt = SA_S1H_CQ (tq_s1dat) << 2; /* get cmd q len */ + tq_cq.idx = tq_rq.idx = 0; /* clear q idx's */ + if (tq_prgi) + base = tq_comm + SA_COMM_QQ; + else base = tq_comm + SA_COMM_CI; + lnt = tq_comm + tq_cq.lnt + tq_rq.lnt - base; /* comm lnt */ + if (lnt > SA_COMM_MAX) /* paranoia */ + lnt = SA_COMM_MAX; + for (i = 0; i < (lnt >> 1); i++) /* clr buffer */ + zero[i] = 0; + smp_wmb(); + wstatus = Map_WriteW (RUN_PASS, base, lnt, zero); /* zero comm area */ + smp_wmb(); + if (wstatus) /* error? */ + return tq_fatal (PE_QWE); + tq_sa = SA_S4 | (drv_tab[tq_typ].uqpm << SA_S4C_V_MOD) |/* send step 4 */ + ((drv_tab[tq_typ].cver & 0xFF) << SA_S4C_V_VER); + tq_csta = CST_S4; /* set step 4 */ + tq_init_int (); /* poke host */ + return OK; +} + +/* Queue service - invoked when any of the queues (host queue, unit + queues, response queue) require servicing. Also invoked during + initialization to provide some delay to the next step. + + Process at most one item off each unit queue + If the unit queues were empty, process at most one item off the host queue + Process at most one item off the response queue + + If all queues are idle, terminate thread +*/ + +t_stat tq_quesvc (RUN_SVC_DECL, UNIT *uptr) +{ +AUTO_LOCK(tq_lock); +RUN_SVC_CHECK_CANCELLED(uptr); +int32 i, cnid; +int32 pkt = 0; +UNIT *nuptr; + +sim_debug(DBG_TRC, &tq_dev, "tq_quesvc\n"); + +if (tq_csta < CST_UP) { /* still init? */ + sim_debug(DBG_INI, &tq_dev, "CSTA=%d, SAW=0x%X\n", tq_csta, tq_saw); + + switch (tq_csta) { /* controller state? */ + case CST_S1: /* need S1 reply */ + if (tq_saw & SA_S1H_VL) { /* valid? */ + if (tq_saw & SA_S1H_WR) { /* wrap? */ + tq_sa = tq_saw; /* echo data */ + tq_csta = CST_S1_WR; /* endless loop */ + } + else { + tq_s1dat = tq_saw; /* save data */ + tq_dib.vec = (tq_s1dat & SA_S1H_VEC) << 2; /* get vector */ + if (tq_dib.vec) /* if nz, bias */ + tq_dib.vec = tq_dib.vec + VEC_Q; + tq_sa = SA_S2 | SA_S2C_PT | SA_S2C_EC (tq_s1dat); + tq_csta = CST_S2; /* now in step 2 */ + tq_init_int (); /* intr if req */ + } + } /* end if valid */ + break; + + case CST_S1_WR: /* wrap mode */ + tq_sa = tq_saw; /* echo data */ + break; + + case CST_S2: /* need S2 reply */ + tq_comm = tq_saw & SA_S2H_CLO; /* get low addr */ + tq_prgi = tq_saw & SA_S2H_PI; /* get purge int */ + tq_sa = SA_S3 | SA_S3C_EC (tq_s1dat); + tq_csta = CST_S3; /* now in step 3 */ + tq_init_int (); /* intr if req */ + break; + + case CST_S3: /* need S3 reply */ + tq_comm = ((tq_saw & SA_S3H_CHI) << 16) | tq_comm; + if (tq_saw & SA_S3H_PP) { /* purge/poll test? */ + tq_sa = 0; /* put 0 */ + tq_csta = CST_S3_PPA; /* wait for 0 write */ + } + else tq_step4 (RUN_PASS); /* send step 4 */ + break; + + case CST_S3_PPA: /* need purge test */ + if (tq_saw) /* data not zero? */ + tq_fatal (PE_PPF); + else tq_csta = CST_S3_PPB; /* wait for poll */ + break; + + case CST_S4: /* need S4 reply */ + if (tq_saw & SA_S4H_GO) { /* go set? */ + sim_debug (DBG_REQ, &tq_dev, "initialization complete\n"); + tq_csta = CST_UP; /* we're up */ + tq_sa = 0; /* clear SA */ + sim_activate_clk_cosched (tq_unit[TQ_TIMER], clk_tps); + if ((tq_saw & SA_S4H_LF) && tq_perr) + tq_plf (tq_perr); + tq_perr = 0; + } + break; + } /* end switch */ + return SCPE_OK; + } /* end if */ + +for (i = 0; i < TQ_NUMDR; i++) { /* chk unit q's */ + nuptr = tq_dev.units[i]; /* ptr to unit */ + if (nuptr->cpkt || (nuptr->pktq == 0)) + continue; + pkt = tq_deqh (&nuptr->pktq); /* get top of q */ + if (!tq_mscp (pkt, FALSE)) /* process */ + return SCPE_OK; + } +if ((pkt == 0) && tq_pip) { /* polling? */ + if (!tq_getpkt (RUN_PASS, &pkt)) /* get host pkt */ + return SCPE_OK; + if (pkt) { /* got one? */ + UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]); + + if (up) + sim_debug (DBG_REQ, &tq_dev, "cmd=%04X(%3s), mod=%04X, unit=%d, bc=%04X%04X, ma=%04X%04X, obj=%d, pos=0x%X\n", + tq_pkt[pkt].d[CMD_OPC], tq_cmdname[tq_pkt[pkt].d[CMD_OPC]&0x3f], + tq_pkt[pkt].d[CMD_MOD], tq_pkt[pkt].d[CMD_UN], + tq_pkt[pkt].d[RW_BCH], tq_pkt[pkt].d[RW_BCL], + tq_pkt[pkt].d[RW_BAH], tq_pkt[pkt].d[RW_BAL], + up->objp, up->pos); + else + sim_debug (DBG_REQ, &tq_dev, "cmd=%04X(%3s), mod=%04X, unit=%d, bc=%04X%04X, ma=%04X%04X\n", + tq_pkt[pkt].d[CMD_OPC], tq_cmdname[tq_pkt[pkt].d[CMD_OPC]&0x3f], + tq_pkt[pkt].d[CMD_MOD], tq_pkt[pkt].d[CMD_UN], + tq_pkt[pkt].d[RW_BCH], tq_pkt[pkt].d[RW_BCL], + tq_pkt[pkt].d[RW_BAH], tq_pkt[pkt].d[RW_BAL]); + + if (GETP (pkt, UQ_HCTC, TYP) != UQ_TYP_SEQ) /* seq packet? */ + return tq_fatal (PE_PIE); /* no, term thread */ + cnid = GETP (pkt, UQ_HCTC, CID); /* get conn ID */ + if (cnid == UQ_CID_TMSCP) { /* TMSCP packet? */ + if (!tq_mscp (pkt, TRUE)) /* proc, q non-seq */ + return SCPE_OK; + } + else if (cnid == UQ_CID_DUP) { /* DUP packet? */ + tq_putr (pkt, OP_END, 0, ST_CMD | I_OPCD, RSP_LNT, UQ_TYP_SEQ); + if (!tq_putpkt (RUN_PASS, pkt, TRUE)) /* ill cmd */ + return SCPE_OK; + } + else return tq_fatal (PE_ICI); /* no, term thread */ + } /* end if pkt */ + else tq_pip = 0; /* discontinue poll */ + } /* end if pip */ +if (tq_rspq) { /* resp q? */ + pkt = tq_deqh (&tq_rspq); /* get top of q */ + if (!tq_putpkt (RUN_PASS, pkt, FALSE)) /* send to host */ + return SCPE_OK; + } /* end if resp q */ +if (pkt) /* more to do? */ + sim_activate (tq_unit[TQ_QUEUE], tq_qtime); +return SCPE_OK; /* done */ +} + +/* Clock service (roughly once per second) */ + +t_stat tq_tmrsvc (RUN_SVC_DECL, UNIT *uptr) +{ +AUTO_LOCK(tq_lock); +RUN_SVC_CHECK_CANCELLED(uptr); +int32 i; +UNIT *nuptr; + +sim_debug(DBG_TRC, &tq_dev, "tq_tmrsvc\n"); + +sim_activate_clk_cosched (uptr, clk_tps); /* reactivate */ +for (i = 0; i < TQ_NUMDR; i++) { /* poll */ + nuptr = tq_dev.units[i]; + if ((nuptr->flags & UNIT_ATP) && /* ATN pending? */ + (nuptr->flags & UNIT_ATT) && /* still online? */ + (tq_cflgs & CF_ATN)) { /* wanted? */ + if (!tq_una (nuptr)) + return SCPE_OK; + } + nuptr->flags = nuptr->flags & ~UNIT_ATP; + } +if ((tq_hat > 0) && (--tq_hat == 0)) /* host timeout? */ + tq_fatal (PE_HAT); /* fatal err */ +return SCPE_OK; +} + +/* MSCP packet handling */ + +t_bool tq_mscp (int32 pkt, t_bool q) +{ +RUN_SCOPE; +uint32 sts; +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* command */ +uint32 flg = GETP (pkt, CMD_OPC, FLG); /* flags */ +uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifier */ +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +UNIT *uptr; + +sim_debug(DBG_TRC, &tq_dev, "tq_mscp\n"); + +if ((cmd >= 64) || (tq_cmf[cmd] == 0)) { /* invalid cmd? */ + cmd = OP_END; /* set end op */ + sts = ST_CMD | I_OPCD; /* ill op */ + } +else if (flg) { /* flags? */ + cmd = cmd | OP_END; /* set end flag */ + sts = ST_CMD | I_FLAG; /* ill flags */ + } +else if (mdf & ~tq_cmf[cmd]) { /* invalid mod? */ + cmd = cmd | OP_END; /* set end flag */ + sts = ST_CMD | I_MODF; /* ill mods */ + } +else { /* valid cmd */ + if (uptr = tq_getucb (lu)) { /* valid unit? */ + if (q && (tq_cmf[cmd] & CMF_SEQ) && /* queueing, seq, */ + (uptr->cpkt || uptr->pktq)) { /* and active? */ + tq_enqt (&uptr->pktq, pkt); /* do later */ + return OK; + } +// if (tq_cmf[cmd] & MD_CDL) /* clr cch lost? */ +// uptr->flags = uptr->flags & ~UNIT_CDL; + if ((mdf & MD_CSE) && (uptr->flags & UNIT_SXC)) /* clr ser exc? */ + uptr->flags = uptr->flags & ~UNIT_SXC; + memset (uptr->results, 0, sizeof (struct tq_req_results)); /* init request state */ + } + switch (cmd) { + + case OP_ABO: /* abort */ + return tq_abo (pkt); + + case OP_AVL: /* avail */ + return tq_avl (pkt); + + case OP_GCS: /* get cmd status */ + return tq_gcs (pkt); + + case OP_GUS: /* get unit status */ + return tq_gus (pkt); + + case OP_ONL: /* online */ + return tq_onl (pkt); + + case OP_SCC: /* set ctrl char */ + return tq_scc (pkt); + + case OP_SUC: /* set unit char */ + return tq_suc (pkt); + + case OP_ERS: /* erase */ + case OP_ERG: /* erase gap */ + return tq_erase (pkt); + + case OP_FLU: /* flush */ + return tq_flu (pkt); + + case OP_POS: /* position */ + return tq_pos (pkt); + + case OP_WTM: /* write tape mark */ + return tq_wtm (pkt); + + case OP_ACC: /* access */ + case OP_CMP: /* compare */ + case OP_RD: /* read */ + case OP_WR: /* write */ + return tq_rw (pkt); + + case OP_DAP: + cmd = cmd | OP_END; /* set end flag */ + sts = ST_SUC; /* success */ + break; + + default: + cmd = OP_END; /* set end op */ + sts = ST_CMD | I_OPCD; /* ill op */ + break; + } /* end switch */ + } /* end else */ +tq_putr (pkt, cmd, 0, sts, RSP_LNT, UQ_TYP_SEQ); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Abort a command - 1st parameter is ref # of cmd to abort */ + +t_bool tq_abo (int32 pkt) +{ +RUN_SCOPE; +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 ref = GETP32 (pkt, ABO_REFL); /* cmd ref # */ +int32 tpkt, prv; +UNIT *uptr; + +sim_debug(DBG_TRC, &tq_dev, "tq_abo\n"); + +tpkt = 0; /* set no mtch */ +if (uptr = tq_getucb (lu)) { /* get unit */ + if (uptr->cpkt && /* curr pkt? */ + (GETP32 (uptr->cpkt, CMD_REFL) == ref)) { /* match ref? */ + tpkt = uptr->cpkt; /* save match */ + uptr->cpkt = 0; /* gonzo */ + sim_cancel (uptr); /* cancel unit */ + sim_activate (tq_unit[TQ_QUEUE], tq_qtime); + } + else if (uptr->pktq && /* head of q? */ + (GETP32 (uptr->pktq, CMD_REFL) == ref)) { /* match ref? */ + tpkt = uptr->pktq; /* save match */ + uptr->pktq = tq_pkt[tpkt].link; /* unlink */ + } + else if (prv = uptr->pktq) { /* srch pkt q */ + while (tpkt = tq_pkt[prv].link) { /* walk list */ + if (GETP32 (tpkt, RSP_REFL) == ref) { /* match ref? */ + tq_pkt[prv].link = tq_pkt[tpkt].link; /* unlink */ + break; + } + } + } + if (tpkt) { /* found target? */ + uint32 tcmd = GETP (tpkt, CMD_OPC, OPC); /* get opcode */ + tq_putr (tpkt, tcmd | OP_END, 0, ST_ABO, RSP_LNT, UQ_TYP_SEQ); + if (!tq_putpkt (RUN_PASS, tpkt, TRUE)) + return ERR; + } + } /* end if unit */ +tq_putr (pkt, OP_ABO | OP_END, 0, ST_SUC, ABO_LNT, UQ_TYP_SEQ); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Unit available - set unit status to available + Deferred if q'd cmds, bypassed if ser exc */ + +t_bool tq_avl (int32 pkt) +{ +RUN_SCOPE; +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifiers */ +uint32 sts; +UNIT *uptr; + +sim_debug(DBG_TRC, &tq_dev, "tq_avl\n"); + +if (uptr = tq_getucb (lu)) { /* unit exist? */ + if (uptr->flags & UNIT_SXC) /* ser exc pending? */ + 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? */ + tq_detach (uptr); + } + 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); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Get command status - only interested in active xfr cmd */ + +t_bool tq_gcs (int32 pkt) +{ +RUN_SCOPE; +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 ref = GETP32 (pkt, GCS_REFL); /* ref # */ +int32 tpkt; +UNIT *uptr; + +sim_debug(DBG_TRC, &tq_dev, "tq_gcs\n"); + +if ((uptr = tq_getucb (lu)) && /* valid lu? */ + (tpkt = uptr->cpkt) && /* queued pkt? */ + (GETP32 (tpkt, CMD_REFL) == ref) && /* match ref? */ + (tq_cmf[GETP (tpkt, CMD_OPC, OPC)] & CMF_RW)) { /* rd/wr cmd? */ + tq_pkt[pkt].d[GCS_STSL] = tq_pkt[tpkt].d[RW_BCL]; + tq_pkt[pkt].d[GCS_STSH] = tq_pkt[tpkt].d[RW_BCH]; + } +else tq_pkt[pkt].d[GCS_STSL] = tq_pkt[pkt].d[GCS_STSH] = 0; +tq_putr (pkt, OP_GCS | OP_END, 0, ST_SUC, GCS_LNT, UQ_TYP_SEQ); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Get unit status */ + +t_bool tq_gus (int32 pkt) +{ +RUN_SCOPE; +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 sts; +UNIT *uptr; + +sim_debug(DBG_TRC, &tq_dev, "tq_gus\n"); + +if (tq_pkt[pkt].d[CMD_MOD] & MD_NXU) { /* next unit? */ + if (lu >= TQ_NUMDR) { /* end of range? */ + lu = 0; /* reset to 0 */ + tq_pkt[pkt].d[RSP_UN] = lu; + } + } +if (uptr = tq_getucb (lu)) { /* unit exist? */ + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + sts = ST_OFL | SB_OFL_NV; /* offl no vol */ + else if (uptr->flags & UNIT_ONL) /* online */ + sts = ST_SUC; + else sts = ST_AVL; /* avail */ + tq_putr_unit (pkt, uptr, lu, FALSE); /* fill unit fields */ + tq_pkt[pkt].d[GUS_MENU] = drv_tab[tq_typ].fmt; /* format menu */ + tq_pkt[pkt].d[GUS_CAP] = 0; /* free capacity */ + tq_pkt[pkt].d[GUS_FVER] = drv_tab[tq_typ].fver; /* formatter version */ + tq_pkt[pkt].d[GUS_UVER] = drv_tab[tq_typ].uver; /* unit version */ + } +else sts = ST_OFL; /* offline */ +tq_putr (pkt, OP_GUS | OP_END, tq_efl (uptr), sts, GUS_LNT_T, UQ_TYP_SEQ); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Unit online - deferred if q'd commands */ + +t_bool tq_onl (int32 pkt) +{ +RUN_SCOPE; +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 sts; +UNIT *uptr; + +sim_debug(DBG_TRC, &tq_dev, "tq_onl\n"); + +if (uptr = tq_getucb (lu)) { /* unit exist? */ + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + sts = ST_OFL | SB_OFL_NV; /* offl no vol */ + else if (uptr->flags & UNIT_ONL) /* already online? */ + sts = ST_SUC | SB_SUC_ON; + else { + sts = ST_SUC; /* mark online */ + sim_tape_rewind (uptr); /* rewind */ + uptr->objp = 0; /* clear flags */ + uptr->flags = (uptr->flags | UNIT_ONL) & + ~(UNIT_TMK | UNIT_POL); /* onl, pos ok */ + tq_setf_unit (pkt, uptr); /* hack flags */ + } + tq_putr_unit (pkt, uptr, lu, TRUE); /* set fields */ + } +else sts = ST_OFL; /* offline */ +tq_putr (pkt, OP_ONL | OP_END, tq_efl (uptr), sts, ONL_LNT, UQ_TYP_SEQ); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Set controller characteristics */ + +t_bool tq_scc (int32 pkt) +{ +RUN_SCOPE; +sim_debug(DBG_TRC, &tq_dev, "tq_scc\n"); + +if (tq_pkt[pkt].d[SCC_MSV]) /* MSCP ver = 0? */ + tq_putr (pkt, 0, 0, ST_CMD | I_VRSN, SCC_LNT, UQ_TYP_SEQ); +else { + tq_cflgs = (tq_cflgs & CF_RPL) | /* hack ctrl flgs */ + tq_pkt[pkt].d[SCC_CFL]; + if (tq_htmo = tq_pkt[pkt].d[SCC_TMO]) /* set timeout */ + tq_htmo = tq_htmo + 2; /* if nz, round up */ + tq_pkt[pkt].d[SCC_CFL] = tq_cflgs; /* return flags */ + tq_pkt[pkt].d[SCC_TMO] = TQ_DCTMO; /* ctrl timeout */ + tq_pkt[pkt].d[SCC_VER] = drv_tab[tq_typ].cver; /* ctrl version */ + tq_pkt[pkt].d[SCC_CIDA] = 0; /* ctrl ID */ + tq_pkt[pkt].d[SCC_CIDB] = 0; + tq_pkt[pkt].d[SCC_CIDC] = 0; + tq_pkt[pkt].d[SCC_CIDD] = (TQ_CLASS << SCC_CIDD_V_CLS) | + (drv_tab[tq_typ].cmod << SCC_CIDD_V_MOD); + PUTP32 (pkt, SCC_MBCL, TQ_MAXFR); /* max bc */ + tq_putr (pkt, OP_SCC | OP_END, 0, ST_SUC, SCC_LNT, UQ_TYP_SEQ); + } +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Set unit characteristics - defer if q'd commands */ + +t_bool tq_suc (int32 pkt) +{ +RUN_SCOPE; +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 sts; +UNIT *uptr; + +sim_debug(DBG_TRC, &tq_dev, "tq_suc\n"); + +if (uptr = tq_getucb (lu)) { /* unit exist? */ + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + sts = ST_OFL | SB_OFL_NV; /* offl no vol */ + else { + sts = ST_SUC; /* avail or onl */ + tq_setf_unit (pkt, uptr); /* hack flags */ + } + tq_putr_unit (pkt, uptr, lu, TRUE); /* set fields */ + } +else sts = ST_OFL; /* offline */ +tq_putr (pkt, OP_SUC | OP_END, 0, sts, SUC_LNT, UQ_TYP_SEQ); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Flush - sequential nop - deferred if q'd cmds, bypassed if ser exc */ + +t_bool tq_flu (int32 pkt) +{ +RUN_SCOPE; +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 sts; +UNIT *uptr; + +sim_debug(DBG_TRC, &tq_dev, "tq_flu\n"); + +if (uptr = tq_getucb (lu)) /* unit exist? */ + sts = tq_mot_valid (uptr, OP_FLU); /* validate req */ +else sts = ST_OFL; /* offline */ +tq_putr (pkt, OP_FLU | OP_END, tq_efl (uptr), sts, FLU_LNT, UQ_TYP_SEQ); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Erase, erase gap - deferred if q'd cmds, bypassed if ser exc */ + +t_bool tq_erase (int32 pkt) +{ +RUN_SCOPE; +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ +uint32 sts; +UNIT *uptr; + +sim_debug(DBG_TRC, &tq_dev, "tq_erase\n"); + +if (uptr = tq_getucb (lu)) { /* unit exist? */ + sts = tq_mot_valid (uptr, cmd); /* validity checks */ + if (sts == ST_SUC) { /* ok? */ + uptr->cpkt = pkt; /* op in progress */ + uptr->io_starttime = CPU_CURRENT_CYCLES; + uptr->io_startcpu = cpu_unit->cpu_id; + sim_activate (uptr, 0); /* activate */ + return OK; /* done */ + } + } +else sts = ST_OFL; /* offline */ +tq_putr (pkt, cmd | OP_END, tq_efl (uptr), sts, ERS_LNT, UQ_TYP_SEQ); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Write tape mark - deferred if q'd cmds, bypassed if ser exc */ + +t_bool tq_wtm (int32 pkt) +{ +RUN_SCOPE; +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 sts, objp = 0; +UNIT *uptr; + +sim_debug(DBG_TRC, &tq_dev, "tq_wtm\n"); + +if (uptr = tq_getucb (lu)) { /* unit exist? */ + objp = uptr->objp; /* position op */ + sts = tq_mot_valid (uptr, OP_WTM); /* validity checks */ + if (sts == ST_SUC) { /* ok? */ + uptr->cpkt = pkt; /* op in progress */ + uptr->io_starttime = CPU_CURRENT_CYCLES; + uptr->io_startcpu = cpu_unit->cpu_id; + sim_activate (uptr, 0); /* activate */ + return OK; /* done */ + } + } +else sts = ST_OFL; /* offline */ +PUTP32 (pkt, WTM_POSL, objp); /* set obj pos */ +tq_putr (pkt, OP_WTM | OP_END, tq_efl (uptr), sts, WTM_LNT, UQ_TYP_SEQ); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Position - deferred if q'd cmds, bypassed if ser exc */ + +t_bool tq_pos (int32 pkt) +{ +RUN_SCOPE; +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 sts, objp = 0; +UNIT *uptr; + +sim_debug(DBG_TRC, &tq_dev, "tq_pos\n"); + +if (uptr = tq_getucb (lu)) { /* unit exist? */ + objp = uptr->objp; /* position op */ + sts = tq_mot_valid (uptr, OP_POS); /* validity checks */ + if (sts == ST_SUC) { /* ok? */ + uptr->cpkt = pkt; /* op in progress */ + tq_rwtime = 2 * 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_clk_cosched (uptr, tq_rwtime); /* use 2 sec rewind execute time */ + else { /* otherwise */ + uptr->io_starttime = CPU_CURRENT_CYCLES; + uptr->io_startcpu = cpu_unit->cpu_id; + sim_activate (uptr, 0); /* use normal execute time */ + } + return OK; /* done */ + } + } +else sts = ST_OFL; /* offline */ +PUTP32 (pkt, POS_RCL, 0); /* clear #skipped */ +PUTP32 (pkt, POS_TMCL, 0); +PUTP32 (pkt, POS_POSL, objp); /* set obj pos */ +tq_putr (pkt, OP_POS | OP_END, tq_efl (uptr), sts, POS_LNT, UQ_TYP_SEQ); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Data transfer commands - deferred if q'd commands, bypassed if ser exc */ + +t_bool tq_rw (int32 pkt) +{ +RUN_SCOPE; +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ +uint32 bc = GETP32 (pkt, RW_BCL); /* byte count */ +uint32 sts, objp = 0; +UNIT *uptr; + +sim_debug(DBG_TRC, &tq_dev, "tq_rw\n"); + +if (uptr = tq_getucb (lu)) { /* unit exist? */ + objp = uptr->objp; /* position op */ + sts = tq_mot_valid (uptr, cmd); /* validity checks */ + if (sts == ST_SUC) { /* ok? */ + if ((bc == 0) || (bc > TQ_MAXFR)) { /* invalid? */ + uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ + sts = ST_CMD | I_BCNT; + } + else { + uptr->cpkt = pkt; /* op in progress */ + uptr->io_starttime = CPU_CURRENT_CYCLES; + uptr->io_startcpu = cpu_unit->cpu_id; + sim_activate (uptr, 0); /* activate */ + return OK; /* done */ + } + } + } +else sts = ST_OFL; /* offline */ +PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */ +PUTP32 (pkt, RW_POSL, objp); /* set obj pos */ +PUTP32 (pkt, RW_RSZL, 0); /* clr rec size */ +tq_putr (pkt, cmd | OP_END, tq_efl (uptr), sts, RW_LNT_T, UQ_TYP_SEQ); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Validity checks */ + +int32 tq_mot_valid (UNIT *uptr, uint32 cmd) +{ +sim_debug(DBG_TRC, &tq_dev, "tq_mot_valid\n"); + +if (uptr->flags & UNIT_SXC) /* ser exc pend? */ + return ST_SXC; +if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + return (ST_OFL | SB_OFL_NV); /* offl no vol */ +if ((uptr->flags & UNIT_ONL) == 0) /* not online? */ + return ST_AVL; /* only avail */ +if (tq_cmf[cmd] & CMF_WR) { /* write op? */ + if (uptr->uf & UF_WPS) { /* swre wlk? */ + uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ + return (ST_WPR | SB_WPR_SW); + } + if (TQ_WPH (uptr)) { /* hwre wlk? */ + uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ + return (ST_WPR | SB_WPR_HW); + } + } +return ST_SUC; /* success! */ +} + +/* Unit service for motion commands */ + +/* I/O completion callback */ +void tq_io_complete (UNIT *uptr, t_stat status) +{ + RUN_SCOPE; + struct tq_req_results* res = (struct tq_req_results*) uptr->results; + + sim_debug(DBG_TRC, &tq_dev, "tq_io_complete(status=%d)\n", status); + + res->io_status = status; + res->io_complete = 1; + + if (cpu_unit->cpu_id == uptr->io_startcpu) + { + uint32 elapsed = CPU_CURRENT_CYCLES - uptr->io_starttime; + if (elapsed > (uint32) tq_xtime) + sim_activate (uptr, 0); + else + sim_activate (uptr, (uint32) tq_xtime - elapsed); + } + else + { + sim_activate (uptr, 0); + } +} + + +t_stat tq_svc (RUN_SVC_DECL, UNIT *uptr) +{ +AUTO_LOCK(tq_lock); +RUN_SVC_CHECK_CANCELLED(uptr); +uint32 t; +t_mtrlnt wbc; +int32 pkt = uptr->cpkt; /* get packet */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */ +uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifier */ +uint32 ba = GETP32 (pkt, RW_BAL); /* buf addr */ +t_mtrlnt bc = GETP32 (pkt, RW_BCL); /* byte count */ +uint32 nrec = GETP32 (pkt, POS_RCL); /* #rec to skip */ +uint32 ntmk = GETP32 (pkt, POS_TMCL); /* #tmk to skp */ +struct tq_req_results *res = (struct tq_req_results *)uptr->results; +int32 io_complete = res->io_complete; + +sim_debug (DBG_TRC, &tq_dev, "tq_svc(unit=%d, pkt=%d, cmd=%s, mdf=0x%0X, bc=0x%0x, phase=%s)\n", + sim_unit_index (uptr), pkt, tq_cmdname[tq_pkt[pkt].d[CMD_OPC]&0x3f], mdf, bc, + uptr->io_complete ? "bottom" : "top"); + +res->io_complete = 0; +if (pkt == 0) /* what??? */ + return SCPE_IERR; +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + tq_mot_end (uptr, 0, ST_OFL | SB_OFL_NV, 0); /* offl no vol */ + return SCPE_OK; + } + +if (tq_cmf[cmd] & CMF_WR) { /* write op? */ + if (TQ_WPH (uptr)) { /* hwre write prot? */ + uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ + tq_mot_end (uptr, 0, ST_WPR | SB_WPR_HW, 0); + return SCPE_OK; + } + if (uptr->uf & UF_WPS) { /* swre write prot? */ + uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ + tq_mot_end (uptr, 0, ST_WPR | SB_WPR_SW, 0); + return SCPE_OK; + } + } +if (!io_complete) { + res->sts = ST_SUC; /* assume success */ + res->tbc = 0; /* assume zero rec */ + } +switch (cmd) { /* case on command */ + + case OP_RD:case OP_ACC:case OP_CMP: /* read-like op */ + if (!io_complete) { + if (mdf & MD_REV) /* read record */ + tq_rdbufr_top (uptr, &res->tbc); + else + tq_rdbuff_top (uptr, &res->tbc); + return SCPE_OK; + } + if (mdf & MD_REV) /* read record */ + res->sts = tq_rdbufr_bottom (uptr, &res->tbc); + else + res->sts = tq_rdbuff_bottom (uptr, &res->tbc); + if (res->sts == ST_DRV) { /* read error? */ + PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */ + return tq_mot_err (uptr, res->tbc); /* log, done */ + } + if ((res->sts != ST_SUC) || (cmd == OP_ACC)) { /* error or access? */ + if (res->sts == ST_TMK) + uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ + PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */ + break; + } + if (res->tbc > bc) { /* tape rec > buf? */ + uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */ + res->sts = ST_RDT; /* data truncated */ + wbc = bc; /* set working bc */ + } + else wbc = res->tbc; + if (cmd == OP_RD) { /* read? */ + if (t = Map_WriteB (RUN_PASS, ba, wbc, res->tqxb)) { /* store, nxm? */ + PUTP32 (pkt, RW_BCL, wbc - t); /* adj bc */ + if (tq_hbe (uptr, ba + wbc - t)) /* post err log */ + tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, res->tbc); + return SCPE_OK; /* end if nxm */ + } + } /* end if read */ + else { /* compare */ + uint8 mby, dby; + uint32 mba; + t_mtrlnt i; + for (i = 0; i < wbc; i++) { /* loop */ + if (mdf & MD_REV) { /* reverse? */ + mba = ba + bc - 1 - i; /* mem addr */ + dby = ((uint8 *)res->tqxb)[res->tbc - 1 - i]; /* byte */ + } + else { + mba = ba + i; + dby = ((uint8 *)res->tqxb)[i]; + } + if (Map_ReadB (RUN_PASS, mba, 1, &mby)) { /* fetch, nxm? */ + PUTP32 (pkt, RW_BCL, i); /* adj bc */ + if (tq_hbe (uptr, mba)) /* post err log */ + tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, res->tbc); + return SCPE_OK; + } + if (mby != dby) { /* cmp err? */ + uptr->flags = uptr->flags | UNIT_SXC; /* ser exc */ + PUTP32 (pkt, RW_BCL, i); /* adj bc */ + tq_mot_end (uptr, 0, ST_CMP, res->tbc); + return SCPE_OK; /* exit */ + } + } /* end for */ + } /* end if compare */ + PUTP32 (pkt, RW_BCL, wbc); /* bytes read/cmp'd */ + break; + + case OP_WR: /* write */ + if (!io_complete) { /* Top half processing */ + if (t = Map_ReadB (RUN_PASS, ba, bc, res->tqxb)) { /* fetch buf, nxm? */ + PUTP32 (pkt, RW_BCL, 0); /* no bytes xfer'd */ + if (tq_hbe (uptr, ba + bc - t)) /* post err log */ + tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, bc); + return SCPE_OK; /* end else wr */ + } + sim_tape_wrrecf_a (uptr, res->tqxb, bc, tq_io_complete); /* write rec fwd */ + return SCPE_OK; + } + if (res->io_status) + return tq_mot_err (uptr, bc); /* log, end */ + uptr->objp = uptr->objp + 1; /* upd obj pos */ + if (TEST_EOT (uptr)) /* EOT on write? */ + uptr->flags = uptr->flags | UNIT_SXC; + uptr->flags = uptr->flags & ~UNIT_TMK; /* disable LEOT */ + res->tbc = bc; /* RW_BC is ok */ + break; + + case OP_WTM: /* write tape mark */ + if (!io_complete) { /* Top half processing */ + sim_tape_wrtmk_a (uptr, tq_io_complete); /* write tmk, err? */ + return SCPE_OK; + } + if (res->io_status) + return tq_mot_err (uptr, 0); /* log, end */ + uptr->objp = uptr->objp + 1; /* incr obj cnt */ + case OP_ERG: /* erase gap */ + if (TEST_EOT (uptr)) /* EOT on write? */ + uptr->flags = uptr->flags | UNIT_SXC; + uptr->flags = uptr->flags & ~UNIT_TMK; /* disable LEOT */ + break; + + case OP_ERS: /* erase */ + if (!io_complete) { /* Top half processing */ + sim_tape_wreomrw_a (uptr, tq_io_complete); /* write eom, err? */ + return SCPE_OK; + } + if (res->io_status) + return tq_mot_err (uptr, 0); /* log, end */ + uptr->objp = 0; + uptr->flags = uptr->flags & ~(UNIT_TMK | UNIT_POL); + break; + + case OP_POS: /* position */ + if (!io_complete) { /* Top half processing */ + res->sktmk = res->skrec = 0; /* clr skipped */ + if (mdf & MD_RWD) { /* rewind? */ + uptr->objp = 0; /* clr flags */ + uptr->flags = uptr->flags & ~(UNIT_TMK | UNIT_POL); + } + sim_tape_position_a (uptr, + ((mdf & MD_RWD) ? MTPOS_M_REW : 0) | + ((mdf & MD_REV) ? MTPOS_M_REV : 0) | + ((mdf & MD_OBC) ? MTPOS_M_OBJ : 0) | + (((mdf & MD_DLE) && !(mdf & MD_REV)) ? MTPOS_M_DLE : 0), + nrec, &res->skrec, ntmk, &res->sktmk, (uint32 *)&res->objupd, tq_io_complete); + return SCPE_OK; + } + res->sts = tq_map_status (uptr, res->io_status); + if ((res->io_status != MTSE_OK) && (res->io_status != MTSE_BOT) && (res->io_status != MTSE_LEOT)) + return tq_mot_err (uptr, 0); /* log, end */ + sim_debug (DBG_REQ, &tq_dev, "Position Done: mdf=0x%04X, nrec=%d, ntmk=%d, skrec=%d, sktmk=%d, skobj=%d\n", + mdf, nrec, ntmk, res->skrec, res->sktmk, res->objupd); + if (mdf & MD_REV) + uptr->objp = uptr->objp - res->objupd; + else + uptr->objp = uptr->objp + res->objupd; + PUTP32 (pkt, POS_RCL, res->skrec); /* #rec skipped */ + PUTP32 (pkt, POS_TMCL, res->sktmk); /* #tmk skipped */ + break; + + default: + return SCPE_IERR; + } + +tq_mot_end (uptr, 0, res->sts, res->tbc); /* done */ +return SCPE_OK; +} + +/* Motion command drive error */ + +t_stat tq_mot_err (UNIT *uptr, uint32 rsiz) +{ +uptr->flags = (uptr->flags | UNIT_SXC) & ~UNIT_TMK; /* serious exception */ +if (tq_dte (uptr, ST_DRV)) /* post err log */ + tq_mot_end (uptr, EF_LOG, ST_DRV, rsiz); /* if ok, report err */ +smp_perror ("TQ I/O error"); +clearerr (uptr->fileref); +return SCPE_IOERR; +} + +/* Motion command complete */ + +t_bool tq_mot_end (UNIT *uptr, uint32 flg, uint32 sts, uint32 rsiz) +{ +RUN_SCOPE; +int32 pkt = uptr->cpkt; /* packet */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */ +uint32 lnt = RW_LNT_T; /* assume rw */ + +if (cmd == OP_ERG) /* set pkt lnt */ + lnt = ERG_LNT; +else if (cmd == OP_ERS) + lnt = ERS_LNT; +else if (cmd == OP_WTM) + lnt = WTM_LNT; +else if (cmd == OP_POS) + lnt = POS_LNT; + +uptr->cpkt = 0; /* done */ +if (lnt > ERG_LNT) { /* xfer cmd? */ + PUTP32 (pkt, RW_POSL, uptr->objp); /* position */ + PUTP32 (pkt, RW_RSZL, rsiz); /* record size */ + } +tq_putr (pkt, cmd | OP_END, flg | tq_efl (uptr), sts, lnt, UQ_TYP_SEQ); +if (!tq_putpkt (RUN_PASS, pkt, TRUE)) /* send pkt */ + return ERR; +if (uptr->pktq) /* more to do? */ + sim_activate (tq_unit[TQ_QUEUE], tq_qtime); /* activate thread */ +return OK; +} + +/* Tape motion routines */ + +uint32 tq_map_status (UNIT *uptr, t_stat st) +{ +switch (st) { + + case MTSE_OK: + break; + + case MTSE_UNATT: + uptr->flags = uptr->flags | UNIT_SXC; + return (ST_OFL | SB_OFL_NV); + + case MTSE_FMT: + uptr->flags = uptr->flags | UNIT_SXC; + return ST_MFE; + + case MTSE_TMK: + uptr->flags = uptr->flags | UNIT_SXC; + return ST_TMK; + + case MTSE_INVRL: + uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL; + return ST_FMT; + + case MTSE_RECE: + case MTSE_IOERR: + uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL; + return ST_DRV; + + case MTSE_EOM: + uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL; + return ST_DAT; + + case MTSE_BOT: + uptr->flags = (uptr->flags | UNIT_SXC) & ~UNIT_POL; + return ST_BOT; + + case MTSE_WRP: + uptr->flags = uptr->flags | UNIT_SXC; + return ST_WPR; + + case MTSE_LEOT: + return ST_LED; + } + +return ST_SUC; +} + +/* Read buffer - can return ST_TMK, ST_FMT, or ST_DRV */ + +void tq_rdbuff_top (UNIT *uptr, t_mtrlnt *tbc) +{ +struct tq_req_results *res = (struct tq_req_results *)uptr->results; + +sim_tape_rdrecf_a (uptr, res->tqxb, tbc, MT_MAXFR, tq_io_complete);/* read rec fwd */ +} + +uint32 tq_rdbuff_bottom (UNIT *uptr, t_mtrlnt *tbc) +{ +t_stat st; +struct tq_req_results *res = (struct tq_req_results *)uptr->results; + +st = res->io_status; /* read rec fwd io status */ +if (st == MTSE_TMK) { /* tape mark? */ + uptr->flags = uptr->flags | UNIT_SXC | UNIT_TMK; /* serious exc */ + uptr->objp = uptr->objp + 1; /* update obj cnt */ + return ST_TMK; + } +if (st != MTSE_OK) /* other error? */ + return tq_map_status (uptr, st); +uptr->flags = uptr->flags & ~UNIT_TMK; /* clr tape mark */ +uptr->objp = uptr->objp + 1; /* upd obj cnt */ +return ST_SUC; +} + +void tq_rdbufr_top (UNIT *uptr, t_mtrlnt *tbc) +{ +struct tq_req_results *res = (struct tq_req_results *)uptr->results; + +sim_tape_rdrecr_a (uptr, res->tqxb, tbc, MT_MAXFR, tq_io_complete); /* read rec rev */ +} + +uint32 tq_rdbufr_bottom (UNIT *uptr, t_mtrlnt *tbc) +{ +t_stat st; +struct tq_req_results *res = (struct tq_req_results *)uptr->results; + +st = res->io_status; /* read rec rev io status */ +if (st == MTSE_TMK) { /* tape mark? */ + uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */ + uptr->objp = uptr->objp - 1; /* update obj cnt */ + return ST_TMK; + } +if (st != MTSE_OK) /* other error? */ + return tq_map_status (uptr, st); +uptr->objp = uptr->objp - 1; /* upd obj cnt */ +return ST_SUC; +} + +/* Data transfer error log packet */ + +t_bool tq_dte (UNIT *uptr, uint32 err) +{ +RUN_SCOPE; +int32 pkt, tpkt; +uint32 lu; + +if ((tq_cflgs & CF_THS) == 0) /* logging? */ + return OK; +if (!tq_deqf (&pkt)) /* get log pkt */ + return ERR; +tpkt = uptr->cpkt; /* rw pkt */ +lu = tq_pkt[tpkt].d[CMD_UN]; /* unit # */ + +tq_pkt[pkt].d[ELP_REFL] = tq_pkt[tpkt].d[CMD_REFL]; /* copy cmd ref */ +tq_pkt[pkt].d[ELP_REFH] = tq_pkt[tpkt].d[CMD_REFH]; /* copy cmd ref */ +tq_pkt[pkt].d[ELP_UN] = lu; /* copy unit */ +tq_pkt[pkt].d[ELP_SEQ] = 0; /* clr seq # */ +tq_pkt[pkt].d[DTE_CIDA] = 0; /* ctrl ID */ +tq_pkt[pkt].d[DTE_CIDB] = 0; +tq_pkt[pkt].d[DTE_CIDC] = 0; +tq_pkt[pkt].d[DTE_CIDD] = (TQ_CLASS << DTE_CIDD_V_CLS) | + (drv_tab[tq_typ].cmod << DTE_CIDD_V_MOD); +tq_pkt[pkt].d[DTE_VER] = drv_tab[tq_typ].cver; /* ctrl ver */ +tq_pkt[pkt].d[DTE_MLUN] = lu; /* MLUN */ +tq_pkt[pkt].d[DTE_UIDA] = lu; /* unit ID */ +tq_pkt[pkt].d[DTE_UIDB] = 0; +tq_pkt[pkt].d[DTE_UIDC] = 0; +tq_pkt[pkt].d[DTE_UIDD] = (UID_TAPE << DTE_UIDD_V_CLS) | + (drv_tab[tq_typ].umod << DTE_UIDD_V_MOD); +tq_pkt[pkt].d[DTE_UVER] = drv_tab[tq_typ].uver; /* unit ver */ +PUTP32 (pkt, DTE_POSL, uptr->objp); /* position */ +tq_pkt[pkt].d[DTE_FVER] = drv_tab[tq_typ].fver; /* fmtr ver */ +tq_putr (pkt, FM_TAP, LF_SNR, err, DTE_LNT, UQ_TYP_DAT); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Host bus error log packet */ + +t_bool tq_hbe (UNIT *uptr, uint32 ba) +{ +RUN_SCOPE; +int32 pkt, tpkt; + +if ((tq_cflgs & CF_THS) == 0) /* logging? */ + return OK; +if (!tq_deqf (&pkt)) /* get log pkt */ + return ERR; +tpkt = uptr->cpkt; /* rw pkt */ +tq_pkt[pkt].d[ELP_REFL] = tq_pkt[tpkt].d[CMD_REFL]; /* copy cmd ref */ +tq_pkt[pkt].d[ELP_REFH] = tq_pkt[tpkt].d[CMD_REFH]; /* copy cmd ref */ +tq_pkt[pkt].d[ELP_UN] = tq_pkt[tpkt].d[CMD_UN]; /* copy unit */ +tq_pkt[pkt].d[ELP_SEQ] = 0; /* clr seq # */ +tq_pkt[pkt].d[HBE_CIDA] = 0; /* ctrl ID */ +tq_pkt[pkt].d[HBE_CIDB] = 0; +tq_pkt[pkt].d[HBE_CIDC] = 0; +tq_pkt[pkt].d[DTE_CIDD] = (TQ_CLASS << DTE_CIDD_V_CLS) | + (drv_tab[tq_typ].cmod << DTE_CIDD_V_MOD); +tq_pkt[pkt].d[HBE_VER] = drv_tab[tq_typ].cver; /* ctrl ver */ +tq_pkt[pkt].d[HBE_RSV] = 0; +PUTP32 (pkt, HBE_BADL, ba); /* bad addr */ +tq_putr (pkt, FM_BAD, LF_SNR, ST_HST | SB_HST_NXM, HBE_LNT, UQ_TYP_DAT); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Port last failure error log packet */ + +t_bool tq_plf (uint32 err) +{ +RUN_SCOPE; +int32 pkt; + +if (!tq_deqf (&pkt)) /* get log pkt */ + return ERR; +tq_pkt[pkt].d[ELP_REFL] = tq_pkt[pkt].d[ELP_REFH] = 0; /* ref = 0 */ +tq_pkt[pkt].d[ELP_UN] = tq_pkt[pkt].d[ELP_SEQ] = 0; /* no unit, seq */ +tq_pkt[pkt].d[PLF_CIDA] = 0; /* cntl ID */ +tq_pkt[pkt].d[PLF_CIDB] = 0; +tq_pkt[pkt].d[PLF_CIDC] = 0; +tq_pkt[pkt].d[PLF_CIDD] = (TQ_CLASS << PLF_CIDD_V_CLS) | + (drv_tab[tq_typ].cmod << PLF_CIDD_V_MOD); +tq_pkt[pkt].d[PLF_VER] = drv_tab[tq_typ].cver; +tq_pkt[pkt].d[PLF_ERR] = err; +tq_putr (pkt, FM_CNT, LF_SNR, ST_CNT, PLF_LNT, UQ_TYP_DAT); +tq_pkt[pkt].d[UQ_HCTC] |= (UQ_CID_DIAG << UQ_HCTC_V_CID); +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* Unit now available attention packet */ + +t_bool tq_una (UNIT *uptr) +{ +RUN_SCOPE; +int32 pkt; +uint32 lu; + +if (!tq_deqf (&pkt)) /* get log pkt */ + return ERR; +lu = (uint32) sim_unit_index (uptr); /* get unit */ +tq_pkt[pkt].d[RSP_REFL] = tq_pkt[pkt].d[RSP_REFH] = 0; /* ref = 0 */ +tq_pkt[pkt].d[RSP_UN] = lu; +tq_pkt[pkt].d[RSP_RSV] = 0; +tq_putr_unit (pkt, uptr, lu, FALSE); /* fill unit fields */ +tq_putr (pkt, OP_AVA, 0, 0, UNA_LNT, UQ_TYP_SEQ); /* fill std fields */ +return tq_putpkt (RUN_PASS, pkt, TRUE); +} + +/* List handling + + tq_deqf - dequeue head of free list (fatal err if none) + tq_deqh - dequeue head of list + tq_enqh - enqueue at head of list + tq_enqt - enqueue at tail of list +*/ + +t_bool tq_deqf (int32 *pkt) +{ +if (tq_freq == 0) /* no free pkts?? */ + return tq_fatal (PE_NSR); +tq_pbsy = tq_pbsy + 1; /* cnt busy pkts */ +*pkt = tq_freq; /* head of list */ +tq_freq = tq_pkt[tq_freq].link; /* next */ +return OK; +} + +int32 tq_deqh (int32 *lh) +{ +int32 ptr = *lh; /* head of list */ + +if (ptr) /* next */ + *lh = tq_pkt[ptr].link; +return ptr; +} + +void tq_enqh (int32 *lh, int32 pkt) +{ +if (pkt == 0) /* any pkt? */ + return; +tq_pkt[pkt].link = *lh; /* link is old lh */ +*lh = pkt; /* pkt is new lh */ +return; +} + +void tq_enqt (int32 *lh, int32 pkt) +{ +if (pkt == 0) /* any pkt? */ + return; +tq_pkt[pkt].link = 0; /* it will be tail */ +if (*lh == 0) /* if empty, enqh */ + *lh = pkt; +else { + uint32 ptr = *lh; /* chase to end */ + while (tq_pkt[ptr].link) + ptr = tq_pkt[ptr].link; + tq_pkt[ptr].link = pkt; /* enq at tail */ + } +return; +} + +/* Packet and descriptor handling */ + +/* Get packet from command ring */ + +t_bool tq_getpkt (RUN_DECL, int32 *pkt) +{ + uint32 addr, desc; + + if (!tq_getdesc (RUN_PASS, &tq_cq, &desc)) /* get cmd desc */ + return ERR; + if ((desc & UQ_DESC_OWN) == 0) /* none */ + { + *pkt = 0; /* pkt = 0 */ + return OK; /* no error */ + } + if (!tq_deqf (pkt)) /* get cmd pkt */ + return ERR; + tq_hat = 0; /* dsbl hst timer */ + addr = desc & UQ_ADDR; /* get Q22 addr */ + if (Map_ReadW (RUN_PASS, addr + UQ_HDR_OFF, TQ_PKT_SIZE, tq_pkt[*pkt].d)) + return tq_fatal (PE_PRE); /* read pkt */ + return tq_putdesc (RUN_PASS, &tq_cq, desc); /* release desc */ +} + +/* Put packet to response ring - note the clever hack about credits. + The controller sends all its credits to the host. Thereafter, it + supplies one credit for every response packet sent over. Simple! +*/ + +t_bool tq_putpkt (RUN_DECL, int32 pkt, t_bool qt) +{ + uint32 addr, desc, lnt, cr; + UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]); + + if (pkt == 0) /* any packet? */ + return OK; + if (up) + sim_debug (DBG_REQ, &tq_dev, "rsp=%04X, sts=%04X, rszl=%04X, obj=%d, pos=%d\n", + tq_pkt[pkt].d[RSP_OPF], tq_pkt[pkt].d[RSP_STS], tq_pkt[pkt].d[RW_RSZL], + up->objp, up->pos); + else + sim_debug (DBG_REQ, &tq_dev, "rsp=%04X, sts=%04X\n", + tq_pkt[pkt].d[RSP_OPF], tq_pkt[pkt].d[RSP_STS]); + if (!tq_getdesc (RUN_PASS, &tq_rq, &desc)) /* get rsp desc */ + return ERR; + if ((desc & UQ_DESC_OWN) == 0) { /* not valid? */ + if (qt) /* normal? q tail */ + tq_enqt (&tq_rspq, pkt); + else tq_enqh (&tq_rspq, pkt); /* resp q call */ + sim_activate (tq_unit[TQ_QUEUE], tq_qtime); /* activate q thrd */ + return OK; + } + addr = desc & UQ_ADDR; /* get Q22 addr */ + lnt = tq_pkt[pkt].d[UQ_HLNT] - UQ_HDR_OFF; /* size, with hdr */ + if ((GETP (pkt, UQ_HCTC, TYP) == UQ_TYP_SEQ) && /* seq packet? */ + (GETP (pkt, CMD_OPC, OPC) & OP_END)) { /* end packet? */ + cr = (tq_credits >= 14)? 14: tq_credits; /* max 14 credits */ + tq_credits = tq_credits - cr; /* decr credits */ + tq_pkt[pkt].d[UQ_HCTC] |= ((cr + 1) << UQ_HCTC_V_CR); + } + if (Map_WriteW (RUN_PASS, addr + UQ_HDR_OFF, lnt, tq_pkt[pkt].d)) + return tq_fatal (PE_PWE); /* write pkt */ + tq_enqh (&tq_freq, pkt); /* pkt is free */ + tq_pbsy = tq_pbsy - 1; /* decr busy cnt */ + if (tq_pbsy == 0) /* idle? strt hst tmr */ + tq_hat = tq_htmo; + return tq_putdesc (RUN_PASS, &tq_rq, desc); /* release desc */ +} + +/* + * For VMS PUDRIVER multiprocessor note refer to RQ handler (pdp11_rq.cpp). + */ + +/* Get a descriptor from the host */ + +t_bool tq_getdesc (RUN_DECL, struct uq_ring *ring, uint32 *desc) +{ + uint32 addr = ring->ba + ring->idx; + uint16 d[2]; + +#if defined(VM_VAX_MP) + /* + * Execute RMB before fetching high word from the host, to ensure that in + * multiprocessor case fetching Ownership bit comes before fetching low word + * and access is not reordered. Note that OpenVMS PUDRIVER stores descriptors + * to memory atomically as a longword with MOVL or BISL. + */ + smp_mb(); /* segregate with previous operations */ + if (Map_ReadW (RUN_PASS, addr + 2, 2, d + 1)) /* fetch desc hi word */ + return tq_fatal (PE_QRE); /* err? dead */ + smp_rmb(); + if (Map_ReadW (RUN_PASS, addr, 2, d)) /* fetch desc low word */ + return tq_fatal (PE_QRE); /* err? dead */ +#else + if (Map_ReadW (RUN_PASS, addr, 4, d)) /* fetch desc */ + return tq_fatal (PE_QRE); /* err? dead */ +#endif + + *desc = ((uint32) d[0]) | (((uint32) d[1]) << 16); + + /* + * Execute MB to ensure that subsequent access to the packet or data + * is not reordered with access to O-bit. + */ +#if defined(VM_VAX_MP) + smp_mb(); +#endif + + return OK; +} + +/* Return a descriptor to the host, clearing owner bit + If rings transitions from "empty" to "not empty" or "full" to + "not full", and interrupt bit was set, interrupt the host. + Actually, test whether previous ring entry was owned by host. +*/ + +t_bool tq_putdesc (RUN_DECL, struct uq_ring *ring, uint32 desc) +{ + uint32 prvd, newd = (desc & ~UQ_DESC_OWN) | UQ_DESC_F; + uint32 prva, addr = ring->ba + ring->idx; + uint16 d[2]; + + d[0] = newd & 0xFFFF; /* 32b to 16b */ + d[1] = (newd >> 16) & 0xFFFF; +#if defined(VM_VAX_MP) + /* execute MB (to sync memory in multiprocessor case) before posting ownership bit back to host */ + if (Map_WriteW (RUN_PASS, addr, 2, d)) /* store desc low word */ + return tq_fatal (PE_QWE); /* err? dead */ + smp_mb(); + if (Map_WriteW (RUN_PASS, addr + 2, 2, d + 1)) /* store desc hi word (with O-bit) */ + return tq_fatal (PE_QWE); /* err? dead */ +#else + if (Map_WriteW (RUN_PASS, addr, 4, d)) /* store desc */ + return tq_fatal (PE_QWE); /* err? dead */ +#endif + if (desc & UQ_DESC_F) /* was F set? */ + { + if (ring->lnt <= 4) /* lnt = 1? intr */ + tq_ring_int (RUN_PASS, ring); + else /* prv desc */ + { + prva = ring->ba + ((ring->idx - 4) & (ring->lnt - 1)); +#if defined(VM_VAX_MP) + smp_mb(); /* order access */ + if (Map_ReadW (RUN_PASS, prva + 2, 2, d + 1)) /* read prv hi word (incl. O-bit) */ + return tq_fatal (PE_QRE); + smp_rmb(); + if (Map_ReadW (RUN_PASS, prva, 2, d)) /* read prv low word */ + return tq_fatal (PE_QRE); +#else + if (Map_ReadW (RUN_PASS, prva, 4, d)) /* read prv */ + return tq_fatal (PE_QRE); +#endif + prvd = ((uint32) d[0]) | (((uint32) d[1]) << 16); + if (prvd & UQ_DESC_OWN) + { + /* tq_ring_int performs smp_mb */ + tq_ring_int (RUN_PASS, ring); + } + } + } + ring->idx = (ring->idx + 4) & (ring->lnt - 1); + return OK; +} + +/* Get unit descriptor for logical unit - trivial now, + but eventually, hide multiboard complexities here */ + +UNIT *tq_getucb (uint32 lu) +{ +UNIT *uptr; + +if (lu >= TQ_NUMDR) + return NULL; +uptr = tq_dev.units[lu]; +if (uptr->flags & UNIT_DIS) + return NULL; +return uptr; +} + +/* Hack unit flags */ + +void tq_setf_unit (int32 pkt, UNIT *uptr) +{ +uptr->uf = tq_pkt[pkt].d[ONL_UFL] & UF_MSK; /* settable flags */ +if ((tq_pkt[pkt].d[CMD_MOD] & MD_SWP) && /* swre wrp enb? */ + (tq_pkt[pkt].d[ONL_UFL] & UF_WPS)) /* swre wrp on? */ + uptr->uf = uptr->uf | UF_WPS; /* simon says... */ +return; +} + +/* Hack end flags */ + +uint32 tq_efl (UNIT *uptr) +{ +uint32 t = 0; + +if (uptr) { /* any unit? */ + if (uptr->flags & UNIT_POL) /* note pos lost */ + t = t | EF_PLS; + if (uptr->flags & UNIT_SXC) /* note ser exc */ + t = t | EF_SXC; + if (TEST_EOT (uptr)) /* note EOT */ + t = t | EF_EOT; + } +return t; +} + +/* Unit response fields */ + +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; +tq_pkt[pkt].d[ONL_UIDC] = 0; +tq_pkt[pkt].d[ONL_UIDD] = (UID_TAPE << ONL_UIDD_V_CLS) | + (drv_tab[tq_typ].umod << ONL_UIDD_V_MOD); /* UID hi */ +PUTP32 (pkt, ONL_MEDL, drv_tab[tq_typ].med); /* media type */ +if (all) { /* if long form */ + tq_pkt[pkt].d[ONL_FMT] = drv_tab[tq_typ].fmt; /* format */ + tq_pkt[pkt].d[ONL_SPD] = 0; /* speed */ + PUTP32 (pkt, ONL_MAXL, TQ_MAXFR); /* max xfr */ + tq_pkt[pkt].d[ONL_NREC] = 0; /* noise rec */ + tq_pkt[pkt].d[ONL_RSVE] = 0; /* reserved */ + } +return; +} + +/* UQ_HDR and RSP_OP fields */ + +void tq_putr (int32 pkt, uint32 cmd, uint32 flg, uint32 sts, uint32 lnt, uint32 typ) +{ +tq_pkt[pkt].d[RSP_OPF] = (cmd << RSP_OPF_V_OPC) | /* set cmd, flg */ + (flg << RSP_OPF_V_FLG); +tq_pkt[pkt].d[RSP_STS] = sts; +tq_pkt[pkt].d[UQ_HLNT] = lnt; /* length */ +tq_pkt[pkt].d[UQ_HCTC] = (typ << UQ_HCTC_V_TYP) | /* type, cid */ + (UQ_CID_TMSCP << UQ_HCTC_V_CID); /* clr credits */ +return; +} + +/* Post interrupt during init */ + +void tq_init_int (void) +{ + if ((tq_s1dat & SA_S1H_IE) && tq_dib.vec) + SET_INT (TQ); +} + +/* Post interrupt during putpkt - note that NXMs are ignored! */ + +void tq_ring_int (RUN_DECL, struct uq_ring *ring) +{ + uint32 iadr = tq_comm + ring->ioff; /* addr intr wd */ + uint16 flag = 1; + + /* + * We are about to signal host that command ring transitioned full to non-full + * or response ring transitioned empty to non-empty. Issue memory barrier + * to ensure coming write to comm area is not reordered with previous access + * to the rings or data buffers. + */ + smp_mb(); + Map_WriteW (RUN_PASS, iadr, 2, &flag); /* write flag */ + smp_mb(); + + if (tq_dib.vec) /* if enb, intr */ + SET_INT (TQ); +} + +/* Return interrupt vector */ + +int32 tq_inta (void) +{ + AUTO_LOCK(tq_lock); + return tq_dib.vec; /* prog vector */ +} + +/* Fatal error */ + +t_bool tq_fatal (uint32 err) +{ + sim_debug (DBG_TRC, &tq_dev, "tq_fatal\n"); + + sim_debug (DBG_REQ, &tq_dev, "fatal err=%X\n", err); + smp_mb(); + tq_reset (&tq_dev); /* reset device */ + tq_sa = SA_ER | err; /* SA = dead code */ + tq_csta = CST_DEAD; /* state = dead */ + tq_perr = err; /* save error */ + return ERR; +} + +/* Device attach */ + +t_stat tq_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = sim_tape_attach_ex (uptr, cptr, DBG_TAP); +if (r != SCPE_OK) + return r; +if (tq_csta == CST_UP) + uptr->flags = (uptr->flags | UNIT_ATP) & ~(UNIT_SXC | UNIT_POL | UNIT_TMK); +return SCPE_OK; +} + +/* Device detach */ + +t_stat tq_detach (UNIT *uptr) +{ +t_stat r; + +r = sim_tape_detach (uptr); /* detach unit */ +if (r != SCPE_OK) + return r; +uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP | UNIT_SXC | UNIT_POL | UNIT_TMK); +uptr->uf = 0; /* clr unit flgs */ +return SCPE_OK; +} + +/* Device reset */ + +t_stat tq_reset (DEVICE *dptr) +{ +AUTO_LOCK(tq_lock); +int32 i, j; +UNIT *uptr; + +sim_bind_devunits_lock(&tq_dev, tq_lock); +tq_csta = CST_S1; /* init stage 1 */ +tq_s1dat = 0; /* no S1 data */ +tq_dib.vec = 0; /* no vector */ +if (UNIBUS) /* Unibus? */ + tq_sa = SA_S1 | SA_S1C_DI | SA_S1C_MP; +else tq_sa = SA_S1 | SA_S1C_Q22 | SA_S1C_DI | SA_S1C_MP; /* init SA val */ +tq_cflgs = CF_RPL; /* ctrl flgs off */ +tq_htmo = TQ_DHTMO; /* default timeout */ +tq_hat = tq_htmo; /* default timer */ +tq_cq.ba = tq_cq.lnt = tq_cq.idx = 0; /* clr cmd ring */ +tq_rq.ba = tq_rq.lnt = tq_rq.idx = 0; /* clr rsp ring */ +tq_credits = (TQ_NPKTS / 2) - 1; /* init credits */ +tq_freq = 1; /* init free list */ +for (i = 0; i < TQ_NPKTS; i++) { /* all pkts free */ + if (i) + tq_pkt[i].link = (i + 1) & TQ_M_NPKTS; + else tq_pkt[i].link = 0; + for (j = 0; j < TQ_PKT_SIZE_W; j++) + tq_pkt[i].d[j] = 0; + } +tq_rspq = 0; /* no q'd rsp pkts */ +tq_pbsy = 0; /* all pkts free */ +tq_pip = 0; /* not polling */ +CLR_INT (TQ); /* clr intr req */ +sim_tape_reset (& tq_dev); +for (i = 0; i < TQ_NUMDR + 2; i++) { /* init units */ + uptr = tq_dev.units[i]; + sim_cancel (uptr); /* clr activity */ + if (uptr->flags & UNIT_ATTABLE) + uptr->io_complete = 0; + uptr->flags = uptr->flags & /* not online */ + ~(UNIT_ONL|UNIT_ATP|UNIT_SXC|UNIT_POL|UNIT_TMK); + uptr->uf = 0; /* clr unit flags */ + uptr->cpkt = uptr->pktq = 0; /* clr pkt q's */ + if (uptr->results == NULL) + uptr->results = calloc (1, sizeof (struct tq_req_results)); + if (uptr->results == NULL) + return SCPE_MEM; + } +return SCPE_OK; +} + +/* Device bootstrap */ + +#if defined (VM_PDP11) + +#define BOOT_START 016000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_UNIT (BOOT_START + 010) /* unit number */ +#define BOOT_CSR (BOOT_START + 014) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +/* Data structure definitions */ + +#define B_CMDINT (BOOT_START - 01000) /* cmd int */ +#define B_RSPINT (B_CMDINT + 002) /* rsp int */ +#define B_RING (B_RSPINT + 002) /* ring base */ +#define B_RSPH (B_RING + 010) /* resp pkt hdr */ +#define B_TKRSP (B_RSPH + 004) /* resp pkt */ +#define B_CMDH (B_TKRSP + 060) /* cmd pkt hdr */ +#define B_TKCMD (B_CMDH + 004) /* cmd pkt */ +#define B_UNIT (B_TKCMD + 004) /* unit # */ + +static const uint16 boot_rom[] = { + + 0046525, /* ST: "UM" */ + + 0012706, 0016000, /* mov #st,sp */ + 0012700, 0000000, /* mov #unitno,r0 */ + 0012701, 0174500, /* mov #174500,r1 ; ip addr */ + 0005021, /* clr (r1)+ ; init */ + 0012704, 0004000, /* mov #4000,r4 ; s1 mask */ + 0005002, /* clr r2 */ + 0005022, /* 10$: clr (r2)+ ; clr up to boot */ + 0020237, BOOT_START - 2, /* cmp r2,#st-2 */ + 0103774, /* blo 10$ */ + 0012705, BOOT_START+0312, /* mov #cmdtbl,r5 ; addr of tbl */ + + /* Four step init process */ + + 0005711, /* 20$: tst (r1) ; err? */ + 0100001, /* bpl 30$ */ + 0000000, /* halt */ + 0030411, /* 30$: bit r4,(r1) ; step set? */ + 0001773, /* beq 20$ ; wait */ + 0012511, /* mov (r5)+,(r1) ; send next */ + 0006304, /* asl r4 ; next mask */ + 0100370, /* bpl 20$ ; s4 done? */ + + /* Set up rings, issue ONLINE, REWIND, READ */ + + 0012737, 0000400, B_CMDH + 2, /* mov #400,cmdh+2 ; VCID = 1 */ + 0012737, 0000044, B_CMDH, /* mov #36.,cmdh ; cmd pkt lnt */ + 0010037, B_UNIT, /* mov r0,unit ; unit # */ + 0012737, 0000011, B_TKCMD + 8, /* mov #11,tkcmd+8. ; online op */ + 0012737, 0020000, B_TKCMD + 10, /* mov #20000,tkcmd+10. ; clr ser ex */ + 0012702, B_RING, /* mov #ring,r2 ; init rings */ + 0012722, B_TKRSP, /* mov #tkrsp,(r2)+ ; rsp pkt addr */ + 0010203, /* mov r2,r3 ; save ring+2 */ + 0010423, /* mov r4,(r3)+ ; set TK own */ + 0012723, B_TKCMD, /* mov #tkcmd,(r3)+ ; cmd pkt addr */ + 0010423, /* mov r4,(r3)+ ; set TK own */ + 0005741, /* tst -(r1) ; start poll */ + 0005712, /* 40$: tst (r2) ; wait for resp */ + 0100776, /* bmi 40$ */ + 0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */ + 0001401, /* beq 50$ */ + 0000000, /* halt */ + 0012703, B_TKCMD + 8, /* 50$: mov #tkcmd+8.,r3 */ + 0012723, 0000045, /* mov #45,(r3)+ ; reposition */ + 0012723, 0020002, /* mov #20002,(r3)+ ; rew, clr exc */ + 0012723, 0000001, /* mov #1,(r3)+ ; lo rec skp */ + 0005023, /* clr (r3)+ ; hi rec skp */ + 0005023, /* clr (r3)+ ; lo tmk skp */ + 0005023, /* clr (r3)+ ; hi tmk skp */ + 0010412, /* mov r4,(r2) ; TK own rsp */ + 0010437, B_RING + 6, /* mov r4,ring+6 ; TK own cmd */ + 0005711, /* tst (r1) ; start poll */ + 0005712, /* 60$: tst (r2) ; wait for resp */ + 0100776, /* bmi 60$ */ + 0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */ + 0001401, /* beq 70$ */ + 0000000, /* halt */ + 0012703, B_TKCMD + 8, /* 70$: mov #tkcmd+8.,r3 */ + 0012723, 0000041, /* mov #41,(r3)+ ; read */ + 0012723, 0020000, /* mov #20000,(r3)+ ; clr exc */ + 0012723, 0001000, /* mov #512.,(r3)+ ; bc = 512 */ + 0005023, /* clr (r3)+ ; clr args */ + 0005023, /* clr (r3)+ ; ba = 0 */ + 0010412, /* mov r4,(r2) ; TK own rsp */ + 0010437, B_RING + 6, /* mov r4,ring+6 ; TK own cmd */ + 0005711, /* tst (r1) ; start poll */ + 0005712, /* 80$: tst (r2) ; wait for resp */ + 0100776, /* bmi 80$ */ + 0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */ + 0001401, /* beq 90$ */ + 0000000, /* halt */ + + /* Boot block read in, jump to 0 - leave controller init'd */ + + 0005003, /* clr r3 */ + 0012704, BOOT_START+020, /* mov #st+020,r4 */ + 0005005, /* clr r5 */ + 0005007, /* clr pc */ + + 0100000, /* cmdtbl: init step 1 */ + B_RING, /* ring base */ + 0000000, /* high ring base */ + 0000001 /* go */ + }; + +t_stat tq_boot (int32 unitno, DEVICE *dptr) +{ +//AUTO_LOCK(tq_lock); +int32 i; +extern int32 saved_PC; +extern uint16 *M; + +for (i = 0; i < BOOT_LEN; i++) + M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & 3; +M[BOOT_CSR >> 1] = tq_dib.ba & DMASK; +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} + +#else + +t_stat tq_boot (int32 unitno, DEVICE *dptr) +{ +return SCPE_NOFNC; +} + +#endif + +/* Special show commands */ + +void tq_show_ring (SMP_FILE *st, struct uq_ring *rp) +{ +RUN_SCOPE; +uint32 i, desc; +uint16 d[2]; + +#if defined (VM_PDP11) +fprintf (st, "ring, base = %o, index = %d, length = %d\n", + rp->ba, rp->idx >> 2, rp->lnt >> 2); +#else +fprintf (st, "ring, base = %x, index = %d, length = %d\n", + rp->ba, rp->idx >> 2, rp->lnt >> 2); +#endif +for (i = 0; i < (rp->lnt >> 2); i++) { + if (Map_ReadW (RUN_PASS, rp->ba + (i << 2), 4, d)) { + fprintf (st, " %3d: non-existent memory\n", i); + break; + } + desc = ((uint32) d[0]) | (((uint32) d[1]) << 16); +#if defined (VM_PDP11) + fprintf (st, " %3d: %011o\n", i, desc); +#else + fprintf (st, " %3d: %08x\n", i, desc); +#endif + } +return; +} + +void tq_show_pkt (SMP_FILE *st, int32 pkt) +{ +int32 i, j; +uint32 cr = GETP (pkt, UQ_HCTC, CR); +uint32 typ = GETP (pkt, UQ_HCTC, TYP); +uint32 cid = GETP (pkt, UQ_HCTC, CID); + +fprintf (st, "packet %d, credits = %d, type = %d, cid = %d\n", + pkt, cr, typ, cid); +for (i = 0; i < TQ_SH_MAX; i = i + TQ_SH_PPL) { + fprintf (st, " %2d:", i); + for (j = i; j < (i + TQ_SH_PPL); j++) +#if defined (VM_PDP11) + fprintf (st, " %06o", tq_pkt[pkt].d[j]); +#else + fprintf (st, " %04x", tq_pkt[pkt].d[j]); +#endif + fprintf (st, "\n"); + } +return; +} + +t_stat tq_show_unitq (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 pkt, u = sim_unit_index (uptr); + +if (tq_csta != CST_UP) { + fprintf (st, "Controller is not initialized\n"); + return SCPE_OK; + } +if ((uptr->flags & UNIT_ONL) == 0) { + if (uptr->flags & UNIT_ATT) + fprintf (st, "Unit %d is available\n", u); + else fprintf (st, "Unit %d is offline\n", u); + return SCPE_OK; + } +if (uptr->cpkt) { + fprintf (st, "Unit %d current ", u); + tq_show_pkt (st, uptr->cpkt); + if (pkt = uptr->pktq) { + do { + fprintf (st, "Unit %d queued ", u); + tq_show_pkt (st, pkt); + } while (pkt = tq_pkt[pkt].link); + } + } +else fprintf (st, "Unit %d queues are empty\n", u); +return SCPE_OK; +} + +t_stat tq_show_ctrl (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, pkt; + +if (tq_csta != CST_UP) { + fprintf (st, "Controller is not initialized\n"); + return SCPE_OK; + } +if (val & TQ_SH_RI) { + if (tq_pip) + fprintf (st, "Polling in progress, host timer = %d\n", tq_hat); + else fprintf (st, "Host timer = %d\n", tq_hat); + fprintf (st, "Command "); + tq_show_ring (st, &tq_cq); + fprintf (st, "Response "); + tq_show_ring (st, &tq_rq); + } +if (val & TQ_SH_FR) { + if (pkt = tq_freq) { + for (i = 0; pkt != 0; i++, pkt = tq_pkt[pkt].link) { + if (i == 0) + fprintf (st, "Free queue = %d", pkt); + else if ((i % 16) == 0) + fprintf (st, ",\n %d", pkt); + else fprintf (st, ", %d", pkt); + } + fprintf (st, "\n"); + } + else fprintf (st, "Free queue is empty\n"); + } +if (val & TQ_SH_RS) { + if (pkt = tq_rspq) { + do { + fprintf (st, "Response "); + tq_show_pkt (st, pkt); + } while (pkt = tq_pkt[pkt].link); + } + else fprintf (st, "Response queue is empty\n"); + } +if (val & TQ_SH_UN) { + for (i = 0; i < TQ_NUMDR; i++) + tq_show_unitq (st, tq_unit[i], 0, NULL); + } +return SCPE_OK; +} + +/* Set controller type (and capacity for user-defined type) */ + +t_stat tq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 i, cap; +uint32 max = sim_taddr_64? TQU_EMAXC: TQU_MAXC; +t_stat r; + +if ((val < 0) || (val > TQU_TYPE) || ((val != TQU_TYPE) && cptr)) + return SCPE_ARG; +for (i = 0; i < TQ_NUMDR; i++) { + if (tq_unit[i]->flags & UNIT_ATT) + return SCPE_ALATT; + } +if (cptr) { + cap = (uint32) get_uint (cptr, 10, max, &r); + if ((r != SCPE_OK) || (cap < TQU_MINC)) + return SCPE_ARG; + drv_tab[TQU_TYPE].cap = ((t_addr) cap) << 20; + } +tq_typ = val; +for (i = 0; i < TQ_NUMDR; i++) + tq_unit[i]->capac = drv_tab[tq_typ].cap; +return SCPE_OK; +} + +/* Show controller type and capacity */ + +t_stat tq_show_type (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ + fprintf (st, "%s (%dMB)", drv_tab[tq_typ].name, (uint32) (drv_tab[tq_typ].cap >> 20)); + return SCPE_OK; +} diff --git a/src/PDP11/pdp11_ts.cpp b/src/PDP11/pdp11_ts.cpp new file mode 100644 index 0000000..0d2c142 --- /dev/null +++ b/src/PDP11/pdp11_ts.cpp @@ -0,0 +1,1181 @@ +/* pdp11_ts.c: TS11/TSV05 magnetic tape simulator + + 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"), + 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. + + 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 + 07-Jul-05 RMS Removed extraneous externs + 18-Mar-05 RMS Added attached test to detach routine + 07-Dec-04 RMS Added read-only file support + 30-Sep-04 RMS Revised Unibus interface + 25-Jan-04 RMS Revised for device debug support + 19-May-03 RMS Revised for new conditional compilation scheme + 25-Apr-03 RMS Revised for extended file support + 28-Mar-03 RMS Added multiformat support + 28-Feb-03 RMS Revised to use magtape library + 30-Sep-02 RMS Added variable address support to bootstrap + Added vector change/display support + Fixed CTL unload/clean decode + Implemented XS0_MOT in extended status + New data structures, revamped error recovery + 28-Aug-02 RMS Added end of medium support + 30-May-02 RMS Widened POS to 32b + 22-Apr-02 RMS Added maximum record length protection + 04-Apr-02 RMS Fixed bug in residual frame count after space operation + 16-Feb-02 RMS Fixed bug in message header logic + 26-Jan-02 RMS Revised bootstrap to conform to M9312 + 06-Jan-02 RMS Revised enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 09-Nov-01 RMS Added bus map, VAX support + 15-Oct-01 RMS Integrated debug logging across simulator + 27-Sep-01 RMS Implemented extended characteristics and status + Fixed bug in write characteristics status return + 19-Sep-01 RMS Fixed bug in bootstrap + 15-Sep-01 RMS Fixed bug in NXM test + 07-Sep-01 RMS Revised device disable and interrupt mechanism + 13-Jul-01 RMS Fixed bug in space reverse (found by Peter Schorn) + + Magnetic tapes are represented as a series of variable 8b records + of the form: + + 32b record length in bytes - exact number + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b record length in bytes - exact number + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a single record length of 0. + End of tape is two consecutive end of file marks. + + The TS11 functions in three environments: + + - PDP-11 Q22 systems - the I/O map is one for one, so it's safe to + go through the I/O map + - PDP-11 Unibus 22b systems - the TS11 behaves as an 18b Unibus + peripheral and must go through the I/O map + - VAX Q22 systems - the TS11 must go through the I/O map +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#error "TS11 not supported on PDP10!" + +#elif defined (VM_VAX) /* VAX version */ +#include "sim_defs.h" +#include "vax_defs.h" +#define TS_DIS 0 /* on by default */ +#define DMASK 0xFFFF + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#define TS_DIS DEV_DIS /* off by default */ +extern int32 cpu_opt; +#endif + +#include "sim_tape.h" +#define ADDRTEST (UNIBUS? 0177774: 0177700) + +/* TSBA/TSDB - 17772520: base address/data buffer register + + read: most recent memory address + write word: initiate command + write byte: diagnostic use +*/ + +/* TSSR - 17772522: subsystem status register + TSDBX - 17772523: extended address register + + read: return status + write word: initialize + write byte: if odd, set extended packet address register +*/ + +#define TSSR_SC 0100000 /* special condition */ +#define TSSR_RMR 0010000 /* reg mod refused */ +#define TSSR_NXM 0004000 /* nxm */ +#define TSSR_NBA 0002000 /* need buf addr */ +#define TSSR_V_EMA 8 /* mem addr<17:16> */ +#define TSSR_EMA 0001400 +#define TSSR_SSR 0000200 /* subsystem ready */ +#define TSSR_OFL 0000100 /* offline */ +#define TSSR_V_TC 1 /* term class */ +#define TSSR_M_TC 07 +#define TSSR_TC (TSSR_M_TC << TSSR_V_TC) +#define TC0 (0 << TSSR_V_TC) /* ok */ +#define TC1 (1 << TSSR_V_TC) /* attention */ +#define TC2 (2 << TSSR_V_TC) /* status alert */ +#define TC3 (3 << TSSR_V_TC) /* func reject */ +#define TC4 (4 << TSSR_V_TC) /* retry, moved */ +#define TC5 (5 << TSSR_V_TC) /* retry */ +#define TC6 (6 << TSSR_V_TC) /* pos lost */ +#define TC7 (7 << TSSR_V_TC) /* fatal err */ +#define TSSR_MBZ 0060060 +#define GET_TC(x) (((x) >> TSSR_V_TC) & TSSR_M_TC) + +#define TSDBX_M_XA 017 /* ext addr */ +#define TSDBX_BOOT 0000200 /* boot */ + +/* Command packet offsets */ + +#define CMD_PLNT 4 /* cmd pkt length */ +#define cmdhdr tscmdp[0] /* header */ +#define cmdadl tscmdp[1] /* address low */ +#define cmdadh tscmdp[2] /* address high */ +#define cmdlnt tscmdp[3] /* length */ + +/* Command packet header */ + +#define CMD_ACK 0100000 /* acknowledge */ +#define CMD_CVC 0040000 /* clear vol chk */ +#define CMD_OPP 0020000 /* opposite */ +#define CMD_SWP 0010000 /* swap bytes */ +#define CMD_V_MODE 8 /* mode */ +#define CMD_M_MODE 017 +#define CMD_IE 0000200 /* int enable */ +#define CMD_V_FNC 0 /* function */ +#define CMD_M_FNC 037 /* function */ +#define CMD_N_FNC (CMD_M_FNC + 1) +#define FNC_READ 001 /* read */ +#define FNC_WCHR 004 /* write char */ +#define FNC_WRIT 005 /* write */ +#define FNC_WSSM 006 /* write mem */ +#define FNC_POS 010 /* position */ +#define FNC_FMT 011 /* format */ +#define FNC_CTL 012 /* control */ +#define FNC_INIT 013 /* init */ +#define FNC_GSTA 017 /* get status */ +#define CMD_MBZ 0000140 +#define GET_FNC(x) (((x) >> CMD_V_FNC) & CMD_M_FNC) +#define GET_MOD(x) (((x) >> CMD_V_MODE) & CMD_M_MODE) + +/* Function test flags */ + +#define FLG_MO 001 /* motion */ +#define FLG_WR 002 /* write */ +#define FLG_AD 004 /* addr mem */ + +/* Message packet offsets */ + +#define MSG_PLNT 8 /* packet length */ +#define msghdr tsmsgp[0] /* header */ +#define msglnt tsmsgp[1] /* length */ +#define msgrfc tsmsgp[2] /* residual frame */ +#define msgxs0 tsmsgp[3] /* ext status 0 */ +#define msgxs1 tsmsgp[4] /* ext status 1 */ +#define msgxs2 tsmsgp[5] /* ext status 2 */ +#define msgxs3 tsmsgp[6] /* ext status 3 */ +#define msgxs4 tsmsgp[7] /* ext status 4 */ + +/* Message packet header */ + +#define MSG_ACK 0100000 /* acknowledge */ +#define MSG_MATN 0000000 /* attention */ +#define MSG_MILL 0000400 /* illegal */ +#define MSG_MNEF 0001000 /* non exec fnc */ +#define MSG_CEND 0000020 /* end */ +#define MSG_CFAIL 0000021 /* fail */ +#define MSG_CERR 0000022 /* error */ +#define MSG_CATN 0000023 /* attention */ + +/* Extended status register 0 */ + +#define XS0_TMK 0100000 /* tape mark */ +#define XS0_RLS 0040000 /* rec lnt short */ +#define XS0_LET 0020000 /* log end tape */ +#define XS0_RLL 0010000 /* rec lnt long */ +#define XS0_WLE 0004000 /* write lock err */ +#define XS0_NEF 0002000 /* non exec fnc */ +#define XS0_ILC 0001000 /* illegal cmd */ +#define XS0_ILA 0000400 /* illegal addr */ +#define XS0_MOT 0000200 /* tape has moved */ +#define XS0_ONL 0000100 /* online */ +#define XS0_IE 0000040 /* int enb */ +#define XS0_VCK 0000020 /* volume check */ +#define XS0_PET 0000010 /* 1600 bpi */ +#define XS0_WLK 0000004 /* write lock */ +#define XS0_BOT 0000002 /* BOT */ +#define XS0_EOT 0000001 /* EOT */ +#define XS0_ALLCLR 0177600 /* clear at start */ + +/* Extended status register 1 */ + +#define XS1_UCOR 0000002 /* uncorrectable */ + +/* Extended status register 2 */ + +#define XS2_XTF 0000200 /* ext features */ + +/* Extended status register 3 */ + +#define XS3_OPI 0000100 /* op incomplete */ +#define XS3_REV 0000040 /* reverse */ +#define XS3_RIB 0000001 /* reverse to BOT */ + +/* Extended status register 4 */ + +#define XS4_HDS 0100000 /* high density */ + +/* Write characteristics packet offsets */ + +#define WCH_PLNT 5 /* packet length */ +#define wchadl tswchp[0] /* address low */ +#define wchadh tswchp[1] /* address high */ +#define wchlnt tswchp[2] /* length */ +#define wchopt tswchp[3] /* options */ +#define wchxopt tswchp[4] /* ext options */ + +/* Write characteristics options */ + +#define WCH_ESS 0000200 /* stop dbl tmk */ +#define WCH_ENB 0000100 /* BOT = tmk */ +#define WCH_EAI 0000040 /* enb attn int */ +#define WCH_ERI 0000020 /* enb mrls int */ + +/* Write characteristics extended options */ + +#define WCHX_HDS 0000040 /* high density */ + +#define MAX(a,b) (((a) >= (b))? (a): (b)) +#define MAX_PLNT 8 /* max pkt length */ + +extern SMP_FILE *sim_deb; + +uint8 *tsxb = NULL; /* xfer buffer */ +int32 tssr = 0; /* status register */ +int32 tsba = 0; /* mem addr */ +int32 tsdbx = 0; /* data buf ext */ +int32 tscmdp[CMD_PLNT] = { 0 }; /* command packet */ +int32 tsmsgp[MSG_PLNT] = { 0 }; /* message packet */ +int32 tswchp[WCH_PLNT] = { 0 }; /* wr char packet */ +int32 ts_ownc = 0; /* tape owns cmd */ +int32 ts_ownm = 0; /* tape owns msg */ +int32 ts_qatn = 0; /* queued attn */ +int32 ts_bcmd = 0; /* boot cmd */ +int32 ts_time = 10; /* record latency */ +static uint16 cpy_buf[MAX_PLNT]; /* copy buffer */ +AUTO_INIT_DEVLOCK(ts_lock); + +t_stat ts_rd (int32 *data, int32 PA, int32 access); +t_stat ts_wr (int32 data, int32 PA, int32 access); +t_stat ts_svc (RUN_SVC_DECL, UNIT *uptr); +t_stat ts_reset (DEVICE *dptr); +t_stat ts_attach (UNIT *uptr, char *cptr); +t_stat ts_detach (UNIT *uptr); +t_stat ts_boot (int32 unitno, DEVICE *dptr); +int32 ts_updtssr (int32 t); +int32 ts_updxs0 (int32 t); +void ts_cmpendcmd (int32 s0, int32 s1); +void ts_endcmd (int32 ssf, int32 xs0f, int32 msg); +int32 ts_map_status (t_stat st); + +/* TS data structures + + ts_dev TS device descriptor + ts_unit TS unit list + ts_reg TS register list + ts_mod TS modifier list +*/ + +DIB ts_dib = { + IOBA_TS, IOLN_TS, &ts_rd, &ts_wr, + 1, IVCL (TS), VEC_TS, { NULL } + }; + +UNIT ts_unit UDATA_SINGLE (&ts_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0); +UNIT_TABLE_SINGLE(ts_unit); + +REG ts_reg[] = { + { GRDATA_GBL (TSSR, tssr, DEV_RDX, 16, 0) }, + { GRDATA_GBL (TSBA, tsba, DEV_RDX, 22, 0) }, + { GRDATA_GBL (TSDBX, tsdbx, DEV_RDX, 8, 0) }, + { GRDATA_GBL (CHDR, cmdhdr, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CADL, cmdadl, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CADH, cmdadh, DEV_RDX, 16, 0) }, + { GRDATA_GBL (CLNT, cmdlnt, DEV_RDX, 16, 0) }, + { GRDATA_GBL (MHDR, msghdr, DEV_RDX, 16, 0) }, + { GRDATA_GBL (MRFC, msgrfc, DEV_RDX, 16, 0) }, + { GRDATA_GBL (MXS0, msgxs0, DEV_RDX, 16, 0) }, + { GRDATA_GBL (MXS1, msgxs1, DEV_RDX, 16, 0) }, + { GRDATA_GBL (MXS2, msgxs2, DEV_RDX, 16, 0) }, + { GRDATA_GBL (MXS3, msgxs3, DEV_RDX, 16, 0) }, + { GRDATA_GBL (MSX4, msgxs4, DEV_RDX, 16, 0) }, + { GRDATA_GBL (WADL, wchadl, DEV_RDX, 16, 0) }, + { GRDATA_GBL (WADH, wchadh, DEV_RDX, 16, 0) }, + { GRDATA_GBL (WLNT, wchlnt, DEV_RDX, 16, 0) }, + { GRDATA_GBL (WOPT, wchopt, DEV_RDX, 16, 0) }, + { GRDATA_GBL (WXOPT, wchxopt, DEV_RDX, 16, 0) }, + { IRDATA_DEV (INT, IVCL (TS)) }, + { FLDATA_GBL (ATTN, ts_qatn, 0) }, + { FLDATA_GBL (BOOT, ts_bcmd, 0) }, + { FLDATA_GBL (OWNC, ts_ownc, 0) }, + { FLDATA_GBL (OWNM, ts_ownm, 0) }, + { DRDATA_GBL (TIME, ts_time, 24), PV_LEFT + REG_NZ }, + { DRDATA_GBL (POS, ts_unit.pos, T_ADDR_W), PV_LEFT + REG_RO }, + { GRDATA_GBL (DEVADDR, ts_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA_GBL (DEVVEC, ts_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB ts_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", + &sim_tape_set_capac, &sim_tape_show_capac, NULL }, + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEVICE ts_dev = { + "TS", ts_unit_table, ts_reg, ts_mod, + 1, 10, T_ADDR_W, 1, DEV_RDX, 8, + NULL, NULL, &ts_reset, + &ts_boot, &ts_attach, &ts_detach, + &ts_dib, DEV_DISABLE | TS_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG + }; + +/* I/O dispatch routines, I/O addresses 17772520 - 17772522 + + 17772520 TSBA read/write + 17772522 TSSR read/write +*/ + +t_stat ts_rd (int32 *data, int32 PA, int32 access) +{ +AUTO_LOCK(ts_lock); +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 0: /* TSBA */ + *data = tsba & DMASK; /* low 16b of ba */ + break; + case 1: /* TSSR */ + *data = tssr = ts_updtssr (tssr); /* update tssr */ + break; + } + +return SCPE_OK; +} + +t_stat ts_wr (int32 data, int32 PA, int32 access) +{ +AUTO_LOCK(ts_lock); +RUN_SCOPE; +int32 i, t; + +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 0: /* TSDB */ + if ((tssr & TSSR_SSR) == 0) { /* ready? */ + tssr = tssr | TSSR_RMR; /* no, refuse */ + break; + } + tsba = ((tsdbx & TSDBX_M_XA) << 18) | /* form pkt addr */ + ((data & 03) << 16) | (data & 0177774); + tsdbx = 0; /* clr tsdbx */ + tssr = ts_updtssr (tssr & TSSR_NBA); /* clr ssr, err */ + msgxs0 = ts_updxs0 (msgxs0 & ~XS0_ALLCLR); /* clr, upd xs0 */ + msgrfc = msgxs1 = msgxs2 = msgxs3 = msgxs4 = 0; /* clr status */ + CLR_INT (TS); /* clr int req */ + t = Map_ReadW (RUN_PASS, tsba, CMD_PLNT << 1, cpy_buf); /* read cmd pkt */ + tsba = tsba + ((CMD_PLNT << 1) - t); /* incr tsba */ + if (t) { /* nxm? */ + ts_endcmd (TSSR_NXM + TC5, 0, MSG_ACK|MSG_MNEF|MSG_CFAIL); + return SCPE_OK; + } + for (i = 0; i < CMD_PLNT; i++) /* copy packet */ + tscmdp[i] = cpy_buf[i]; + ts_ownc = ts_ownm = 1; /* tape owns all */ + sim_activate (&ts_unit, ts_time); /* activate */ + break; + + case 1: /* TSSR */ + if (PA & 1) { /* TSDBX */ + if (UNIBUS) /* not in TS11 */ + return SCPE_OK; + if (tssr & TSSR_SSR) { /* ready? */ + tsdbx = data; /* save */ + if (data & TSDBX_BOOT) { + ts_bcmd = 1; + sim_activate (&ts_unit, ts_time); + } + } + else tssr = tssr | TSSR_RMR; /* no, err */ + } + else if (access == WRITE) /* reset */ + ts_reset (&ts_dev); + break; + } + +return SCPE_OK; +} + +/* Tape motion routines */ + +#define XTC(x,t) (((unsigned) (x) << 16) | (t)) +#define GET_X(x) (((x) >> 16) & 0177777) +#define GET_T(x) ((x) & 0177777) + +int32 ts_map_status (t_stat st) +{ +switch (st) { + + case MTSE_OK: + break; + + case MTSE_TMK: + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + return (XTC (XS0_TMK | XS0_RLS, TC2)); + + case MTSE_RECE: /* record in error */ + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + case MTSE_INVRL: /* invalid rec lnt */ + case MTSE_IOERR: /* IO error */ + msgxs1 = msgxs1 | XS1_UCOR; /* uncorrectable */ + return (XTC (XS0_RLS, TC6)); /* pos lost */ + + case MTSE_FMT: + case MTSE_UNATT: + case MTSE_EOM: /* end of medium */ + msgxs3 = msgxs3 | XS3_OPI; /* incomplete */ + return (XTC (XS0_RLS, TC6)); /* pos lost */ + + case MTSE_BOT: /* reverse into BOT */ + msgxs3 = msgxs3 | XS3_RIB; /* set status */ + return (XTC (XS0_BOT | XS0_RLS, TC2)); /* tape alert */ + + case MTSE_WRP: /* write protect */ + msgxs0 = msgxs0 | XS0_WLE | XS0_NEF; /* can't execute */ + return (XTC (XS0_WLE | XS0_NEF, TC3)); + } + +return 0; +} + +int32 ts_spacef (UNIT *uptr, int32 fc, t_bool upd) +{ +t_stat st; +t_mtrlnt tbc; + +do { + fc = (fc - 1) & DMASK; /* decr wc */ + if (upd) + msgrfc = fc; + if (st = sim_tape_sprecf (uptr, &tbc)) /* space rec fwd, err? */ + return ts_map_status (st); /* map status */ + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + } while (fc != 0); +return 0; +} + +int32 ts_skipf (UNIT *uptr, int32 fc) +{ +t_stat st; +t_mtrlnt tbc; +t_bool tmkprv = FALSE; + +msgrfc = fc; +if (sim_tape_bot (uptr) && (wchopt & WCH_ENB)) + tmkprv = TRUE; +do { + st = sim_tape_sprecf (uptr, &tbc); /* space rec fwd */ + if (st == MTSE_TMK) { /* tape mark? */ + msgrfc = (msgrfc - 1) & DMASK; /* decr count */ + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + if (tmkprv && (wchopt & WCH_ESS)) /* 2nd tmk & ESS? */ + return (XTC ((msgrfc? XS0_RLS: 0) | + XS0_TMK | XS0_LET, TC2)); + tmkprv = TRUE; /* flag tmk */ + } + else if (st != MTSE_OK) + return ts_map_status (st); + else tmkprv = FALSE; /* not a tmk */ + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + } while (msgrfc != 0); +return 0; +} + +int32 ts_spacer (UNIT *uptr, int32 fc, t_bool upd) +{ +int32 st; +t_mtrlnt tbc; + +do { + fc = (fc - 1) & DMASK; /* decr wc */ + if (upd) + msgrfc = fc; + if (st = sim_tape_sprecr (uptr, &tbc)) /* space rec rev, err? */ + return ts_map_status (st); /* map status */ + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + } while (fc != 0); +return 0; +} + +int32 ts_skipr (UNIT *uptr, int32 fc) +{ +t_stat st; +t_mtrlnt tbc; +t_bool tmkprv = FALSE; + +msgrfc = fc; +do { + st = sim_tape_sprecr (uptr, &tbc); /* space rec rev */ + if (st == MTSE_TMK) { /* tape mark? */ + msgrfc = (msgrfc - 1) & DMASK; /* decr count */ + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + if (tmkprv && (wchopt & WCH_ESS)) /* 2nd tmk & ESS? */ + return (XTC ((msgrfc? XS0_RLS: 0) | + XS0_TMK | XS0_LET, TC2)); + tmkprv = TRUE; /* flag tmk */ + } + else if (st != MTSE_OK) + return ts_map_status (st); + else tmkprv = FALSE; /* not a tmk */ + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + } while (msgrfc != 0); +return 0; +} + +int32 ts_readf (UNIT *uptr, uint32 fc) +{ +RUN_SCOPE; +t_stat st; +t_mtrlnt i, t, tbc, wbc; +int32 wa; + +msgrfc = fc; +st = sim_tape_rdrecf (uptr, tsxb, &tbc, MT_MAXFR); /* read rec fwd */ +if (st != MTSE_OK) /* error? */ + return ts_map_status (st); +if (fc == 0) /* byte count */ + fc = 0200000; +tsba = (cmdadh << 16) | cmdadl; /* buf addr */ +wbc = (tbc > fc)? fc: tbc; /* cap buf size */ +msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ +if (cmdhdr & CMD_SWP) { /* swapped? */ + for (i = 0; i < wbc; i++) { /* copy buffer */ + wa = tsba ^ 1; /* apply OPP */ + if (Map_WriteB (RUN_PASS, tsba, 1, &tsxb[i])) { /* store byte, nxm? */ + tssr = ts_updtssr (tssr | TSSR_NXM); /* set error */ + return (XTC (XS0_RLS, TC4)); + } + tsba = tsba + 1; + msgrfc = (msgrfc - 1) & DMASK; + } + } +else { + t = Map_WriteB (RUN_PASS, tsba, wbc, tsxb); /* store record */ + tsba = tsba + (wbc - t); /* update tsba */ + if (t) { /* nxm? */ + tssr = ts_updtssr (tssr | TSSR_NXM); /* set error */ + return (XTC (XS0_RLS, TC4)); + } + msgrfc = (msgrfc - (wbc - t)) & DMASK; /* update fc */ + } +if (msgrfc) /* buf too big? */ + return (XTC (XS0_RLS, TC2)); +if (tbc > wbc) /* rec too big? */ + return (XTC (XS0_RLL, TC2)); +return 0; +} + +int32 ts_readr (UNIT *uptr, uint32 fc) +{ +RUN_SCOPE; +t_stat st; +t_mtrlnt i, tbc, wbc; +int32 wa; + +msgrfc = fc; +st = sim_tape_rdrecr (uptr, tsxb, &tbc, MT_MAXFR); /* read rec rev */ +if (st != MTSE_OK) /* error? */ + return ts_map_status (st); +if (fc == 0) /* byte count */ + fc = 0200000; +tsba = (cmdadh << 16) | cmdadl + fc; /* buf addr */ +wbc = (tbc > fc)? fc: tbc; /* cap buf size */ +msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ +for (i = wbc; i > 0; i--) { /* copy buffer */ + tsba = tsba - 1; + wa = (cmdhdr & CMD_SWP)? tsba ^ 1: tsba; /* apply OPP */ + if (Map_WriteB (RUN_PASS, wa, 1, &tsxb[i - 1])) { /* store byte, nxm? */ + tssr = ts_updtssr (tssr | TSSR_NXM); + return (XTC (XS0_RLS, TC4)); + } + msgrfc = (msgrfc - 1) & DMASK; + } +if (msgrfc) /* buf too big? */ + return (XTC (XS0_RLS, TC2)); +if (tbc > wbc) /* rec too big? */ + return (XTC (XS0_RLL, TC2)); +return 0; +} + +int32 ts_write (UNIT *uptr, int32 fc) +{ +RUN_SCOPE; +int32 i, t; +uint32 wa; +t_stat st; + +msgrfc = fc; +if (fc == 0) /* byte count */ + fc = 0200000; +tsba = (cmdadh << 16) | cmdadl; /* buf addr */ +if (cmdhdr & CMD_SWP) { /* swapped? */ + for (i = 0; i < fc; i++) { /* copy mem to buf */ + wa = tsba ^ 1; /* apply OPP */ + if (Map_ReadB (RUN_PASS, wa, 1, &tsxb[i])) { /* fetch byte, nxm? */ + tssr = ts_updtssr (tssr | TSSR_NXM); + return TC5; + } + tsba = tsba + 1; + } + } +else { + t = Map_ReadB (RUN_PASS, tsba, fc, tsxb); /* fetch record */ + tsba = tsba + (fc - t); /* update tsba */ + if (t) { /* nxm? */ + tssr = ts_updtssr (tssr | TSSR_NXM); + return TC5; + } + } +if (st = sim_tape_wrrecf (uptr, tsxb, fc)) /* write rec, err? */ + return ts_map_status (st); /* return status */ +msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ +msgrfc = 0; +if (sim_tape_eot (&ts_unit)) /* EOT on write? */ + return XTC (XS0_EOT, TC2); +return 0; +} + +int32 ts_wtmk (UNIT *uptr) +{ +t_stat st; + +if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ + return ts_map_status (st); /* return status */ +msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ +if (sim_tape_eot (&ts_unit)) /* EOT on write? */ + return XTC (XS0_EOT, TC2); +return XTC (XS0_TMK, TC0); +} + +/* Unit service */ + +t_stat ts_svc (RUN_SVC_DECL, UNIT *uptr) +{ +AUTO_LOCK(ts_lock); +RUN_SVC_CHECK_CANCELLED(uptr); +int32 i, t, bc, fnc, mod, st0, st1; + +static const int32 fnc_mod[CMD_N_FNC] = { /* max mod+1 0 ill */ + 0, 4, 0, 0, 1, 2, 1, 0, /* 00 - 07 */ + 5, 3, 5, 1, 0, 0, 0, 1, /* 10 - 17 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 30 - 37 */ + }; +static const int32 fnc_flg[CMD_N_FNC] = { + 0, FLG_MO+FLG_AD, 0, 0, 0, FLG_MO+FLG_WR+FLG_AD, FLG_AD, 0, + FLG_MO, FLG_MO+FLG_WR, FLG_MO, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 30 - 37 */ + }; +static const char *fnc_name[CMD_N_FNC] = { + "0", "READ", "2", "3", "WCHR", "WRITE", "WSSM", "7", + "POS", "FMT", "CTL", "INIT", "14", "15", "16", "GSTA", + "20", "21", "22", "23", "24", "25", "26", "27", + "30", "31", "32", "33", "34", "35", "36", "37" + }; + +if (ts_bcmd) { /* boot? */ + ts_bcmd = 0; /* clear flag */ + sim_tape_rewind (uptr); /* rewind */ + if (uptr->flags & UNIT_ATT) { /* attached? */ + cmdlnt = cmdadh = cmdadl = 0; /* defang rd */ + ts_spacef (uptr, 1, FALSE); /* space fwd */ + ts_readf (uptr, 512); /* read blk */ + tssr = ts_updtssr (tssr | TSSR_SSR); + } + else tssr = ts_updtssr (tssr | TSSR_SSR | TC3); + if (cmdhdr & CMD_IE) + SET_INT (TS); + return SCPE_OK; + } + +if (!(cmdhdr & CMD_ACK)) { /* no acknowledge? */ + tssr = ts_updtssr (tssr | TSSR_SSR); /* set rdy, int */ + if (cmdhdr & CMD_IE) + SET_INT (TS); + ts_ownc = ts_ownm = 0; /* CPU owns all */ + return SCPE_OK; + } +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=", + 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; + } +if (ts_qatn && (wchopt & WCH_EAI)) { /* attn pending? */ + ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn msg */ + SET_INT (TS); /* set interrupt */ + ts_qatn = 0; /* not pending */ + return SCPE_OK; + } +if (cmdhdr & CMD_CVC) /* cvc? clr vck */ + msgxs0 = msgxs0 & ~XS0_VCK; +if ((cmdhdr & CMD_MBZ) || (mod >= fnc_mod[fnc])) { /* test mbz */ + ts_endcmd (TC3, XS0_ILC, MSG_ACK | MSG_MILL | MSG_CFAIL); + return SCPE_OK; + } +if ((fnc_flg[fnc] & FLG_MO) && /* mot+(vck|!att)? */ + ((msgxs0 & XS0_VCK) || !(uptr->flags & UNIT_ATT))) { + ts_endcmd (TC3, XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL); + return SCPE_OK; + } +if ((fnc_flg[fnc] & FLG_WR) && /* write? */ + sim_tape_wrp (uptr)) { /* write lck? */ + ts_endcmd (TC3, XS0_WLE | XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL); + return SCPE_OK; + } +if ((((fnc == FNC_READ) && (mod == 1)) || /* read rev */ + ((fnc == FNC_POS) && (mod & 1))) && /* space rev */ + sim_tape_bot (uptr)) { /* BOT? */ + ts_endcmd (TC3, XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL); + return SCPE_OK; + } +if ((fnc_flg[fnc] & FLG_AD) && (cmdadh & ADDRTEST)) { /* buf addr > 22b? */ + ts_endcmd (TC3, XS0_ILA, MSG_ACK | MSG_MILL | MSG_CFAIL); + return SCPE_OK; + } + +st0 = st1 = 0; +switch (fnc) { /* case on func */ + + case FNC_INIT: /* init */ + if (!sim_tape_bot (uptr)) /* set if tape moves */ + msgxs0 = msgxs0 | XS0_MOT; + sim_tape_rewind (uptr); /* rewind */ + case FNC_WSSM: /* write mem */ + case FNC_GSTA: /* get status */ + ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); /* send end packet */ + return SCPE_OK; + + case FNC_WCHR: /* write char */ + if ((cmdadh & ADDRTEST) || (cmdadl & 1) || (cmdlnt < 6)) { + ts_endcmd (TSSR_NBA | TC3, XS0_ILA, 0); + break; + } + tsba = (cmdadh << 16) | cmdadl; + bc = ((WCH_PLNT << 1) > cmdlnt)? cmdlnt: WCH_PLNT << 1; + t = Map_ReadW (RUN_PASS, tsba, bc, cpy_buf); /* fetch packet */ + tsba = tsba + (bc - t); /* inc tsba */ + if (t) { /* nxm? */ + ts_endcmd (TSSR_NBA | TSSR_NXM | TC5, 0, 0); + return SCPE_OK; + } + for (i = 0; i < (bc / 2); i++) /* copy packet */ + tswchp[i] = cpy_buf[i]; + if ((wchlnt < ((MSG_PLNT - 1) * 2)) || (wchadh & 0177700) || (wchadl & 1)) + ts_endcmd (TSSR_NBA | TC3, 0, 0); + else { + msgxs2 = msgxs2 | XS2_XTF | 1; + tssr = ts_updtssr (tssr & ~TSSR_NBA); + ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); + } + return SCPE_OK; + + case FNC_CTL: /* control */ + switch (mod) { /* case mode */ + + case 00: /* msg buf rls */ + tssr = ts_updtssr (tssr | TSSR_SSR); /* set SSR */ + if (wchopt & WCH_ERI) + SET_INT (TS); + ts_ownc = 0; ts_ownm = 1; /* keep msg */ + break; + + case 01: /* rewind and unload */ + if (!sim_tape_bot (uptr)) /* if tape moves */ + msgxs0 = msgxs0 | XS0_MOT; + sim_tape_detach (uptr); /* unload */ + ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); + break; + + case 02: /* clean */ + ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); /* nop */ + break; + + case 03: /* undefined */ + ts_endcmd (TC3, XS0_ILC, MSG_ACK | MSG_MILL | MSG_CFAIL); + return SCPE_OK; + + case 04: /* rewind */ + if (!sim_tape_bot (uptr)) /* if tape moves */ + msgxs0 = msgxs0 | XS0_MOT; + sim_tape_rewind (uptr); + ts_endcmd (TC0, XS0_BOT, MSG_ACK | MSG_CEND); + break; + } + break; + + case FNC_READ: /* read */ + switch (mod) { /* case mode */ + + case 00: /* fwd */ + st0 = ts_readf (uptr, cmdlnt); /* read */ + break; + + case 01: /* back */ + st0 = ts_readr (uptr, cmdlnt); /* read */ + break; + + case 02: /* reread fwd */ + if (cmdhdr & CMD_OPP) { /* opposite? */ + st0 = ts_readr (uptr, cmdlnt); + st1 = ts_spacef (uptr, 1, FALSE); + } + else { + st0 = ts_spacer (uptr, 1, FALSE); + st1 = ts_readf (uptr, cmdlnt); + } + break; + + case 03: /* reread back */ + if (cmdhdr & CMD_OPP) { /* opposite */ + st0 = ts_readf (uptr, cmdlnt); + st1 = ts_spacer (uptr, 1, FALSE); + } + else { + st0 = ts_spacef (uptr, 1, FALSE); + st1 = ts_readr (uptr, cmdlnt); + } + break; + } + ts_cmpendcmd (st0, st1); + break; + + case FNC_WRIT: /* write */ + switch (mod) { /* case mode */ + + case 00: /* write */ + st0 = ts_write (uptr, cmdlnt); + break; + + case 01: /* rewrite */ + st0 = ts_spacer (uptr, 1, FALSE); + st1 = ts_write (uptr, cmdlnt); + break; + } + ts_cmpendcmd (st0, st1); + break; + + case FNC_FMT: /* format */ + switch (mod) { /* case mode */ + + case 00: /* write tmk */ + st0 = ts_wtmk (uptr); + break; + + case 01: /* erase */ + break; + + case 02: /* retry tmk */ + st0 = ts_spacer (uptr, 1, FALSE); + st1 = ts_wtmk (uptr); + break; + } + ts_cmpendcmd (st0, st1); + break; + + case FNC_POS: /* position */ + switch (mod) { /* case mode */ + + case 00: /* space fwd */ + st0 = ts_spacef (uptr, cmdadl, TRUE); + break; + + case 01: /* space rev */ + st0 = ts_spacer (uptr, cmdadl, TRUE); + break; + + case 02: /* space ffwd */ + st0 = ts_skipf (uptr, cmdadl); + break; + + case 03: /* space frev */ + st0 = ts_skipr (uptr, cmdadl); + break; + + case 04: /* rewind */ + if (!sim_tape_bot (uptr)) /* if tape moves */ + msgxs0 = msgxs0 | XS0_MOT; + sim_tape_rewind (uptr); + break; + } + ts_cmpendcmd (st0, 0); + break; + } + +return SCPE_OK; +} + +/* Utility routines */ + +int32 ts_updtssr (int32 t) +{ +t = (t & ~TSSR_EMA) | ((tsba >> (16 - TSSR_V_EMA)) & TSSR_EMA); +if (ts_unit.flags & UNIT_ATT) + t = t & ~TSSR_OFL; +else t = t | TSSR_OFL; +return (t & ~TSSR_MBZ); +} + +int32 ts_updxs0 (int32 t) +{ +t = (t & ~(XS0_ONL | XS0_WLK | XS0_BOT | XS0_IE)) | XS0_PET; +if (ts_unit.flags & UNIT_ATT) { + t = t | XS0_ONL; + if (sim_tape_wrp (&ts_unit)) + t = t | XS0_WLK; + if (sim_tape_bot (&ts_unit)) + t = (t | XS0_BOT) & ~XS0_EOT; + if (sim_tape_eot (&ts_unit)) + t = (t | XS0_EOT) & ~XS0_BOT; + } +else t = t & ~XS0_EOT; +if (cmdhdr & CMD_IE) + t = t | XS0_IE; +return t; +} + +void ts_cmpendcmd (int32 s0, int32 s1) +{ +int32 xs0, ssr, tc; +static const int32 msg[8] = { + MSG_ACK | MSG_CEND, MSG_ACK | MSG_MATN | MSG_CATN, + MSG_ACK | MSG_CEND, MSG_ACK | MSG_CFAIL, + MSG_ACK | MSG_CERR, MSG_ACK | MSG_CERR, + MSG_ACK | MSG_CERR, MSG_ACK | MSG_CERR + }; + +xs0 = GET_X (s0) | GET_X (s1); /* or XS0 errs */ +s0 = GET_T (s0); /* get SSR errs */ +s1 = GET_T (s1); +ssr = (s0 | s1) & ~TSSR_TC; /* or SSR errs */ +tc = MAX (GET_TC (s0), GET_TC (s1)); /* max term code */ +ts_endcmd (ssr | (tc << TSSR_V_TC), xs0, msg[tc]); /* end cmd */ +return; +} + +void ts_endcmd (int32 tc, int32 xs0, int32 msg) +{ +RUN_SCOPE; +int32 i, t; + +msgxs0 = ts_updxs0 (msgxs0 | xs0); /* update XS0 */ +if (wchxopt & WCHX_HDS) /* update XS4 */ + msgxs4 = msgxs4 | XS4_HDS; +if (msg && !(tssr & TSSR_NBA)) { /* send end pkt */ + msghdr = msg; + msglnt = wchlnt - 4; /* exclude hdr, bc */ + tsba = (wchadh << 16) | wchadl; + for (i = 0; (i < MSG_PLNT) && (i < (wchlnt / 2)); i++) + cpy_buf[i] = (uint16) tsmsgp[i]; /* copy buffer */ + t = Map_WriteW (RUN_PASS, tsba, i << 1, cpy_buf); /* write to mem */ + tsba = tsba + ((i << 1) - t); /* incr tsba */ + if (t) { /* nxm? */ + tssr = tssr | TSSR_NXM; + tc = (tc & ~TSSR_TC) | TC4; + } + } +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=", + msgxs0, GET_TC (tssr), msgrfc); + fprint_val (sim_deb, ts_unit.pos, 10, T_ADDR_W, PV_LEFT); + fprintf (sim_deb, "\n"); + } +} + +/* Device reset */ + +t_stat ts_reset (DEVICE *dptr) +{ +AUTO_LOCK(ts_lock); +int32 i; + +sim_bind_devunits_lock(&ts_dev, ts_lock); +sim_tape_rewind (&ts_unit); +tsba = tsdbx = 0; +ts_ownc = ts_ownm = 0; +ts_bcmd = 0; +ts_qatn = 0; +tssr = ts_updtssr (TSSR_NBA | TSSR_SSR); +for (i = 0; i < CMD_PLNT; i++) + tscmdp[i] = 0; +for (i = 0; i < WCH_PLNT; i++) + tswchp[i] = 0; +for (i = 0; i < MSG_PLNT; i++) + tsmsgp[i] = 0; +msgxs0 = ts_updxs0 (XS0_VCK); +CLR_INT (TS); +if (tsxb == NULL) + tsxb = (uint8 *) calloc (MT_MAXFR, sizeof (uint8)); +if (tsxb == NULL) + return SCPE_MEM; +return SCPE_OK; +} + +/* Attach */ + +t_stat ts_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = sim_tape_attach (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) /* error? */ + return r; +tssr = tssr & ~TSSR_OFL; /* clr offline */ +if ((tssr & TSSR_NBA) || !(wchopt & WCH_EAI)) /* attn msg? */ + return r; +if (ts_ownm) { /* own msg buf? */ + ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn */ + SET_INT (TS); /* set interrupt */ + ts_qatn = 0; /* don't queue */ + } +else ts_qatn = 1; /* else queue */ +return r; +} + +/* Detach routine */ + +t_stat ts_detach (UNIT* uptr) +{ +t_stat r; + +if (!(uptr->flags & UNIT_ATT)) /* attached? */ + return SCPE_OK; +r = sim_tape_detach (uptr); /* detach unit */ +if (r != SCPE_OK) + return r; /* error? */ +tssr = tssr | TSSR_OFL; /* set offline */ +if ((tssr & TSSR_NBA) || !(wchopt & WCH_EAI)) /* attn msg? */ + return r; +if (ts_ownm) { /* own msg buf? */ + ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn */ + SET_INT (TS); /* set interrupt */ + ts_qatn = 0; /* don't queue */ + } +else ts_qatn = 1; /* else queue */ +return r; +} + +/* Boot */ + +#if defined (VM_PDP11) +#define BOOT_START 01000 +#define BOOT_CSR0 (BOOT_START + 006) +#define BOOT_CSR1 (BOOT_START + 012) +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 0012706, 0001000, /* mov #boot_start, sp */ + 0012700, 0172520, /* mov #tsba, r0 */ + 0012701, 0172522, /* mov #tssr, r1 */ + 0005011, /* clr (r1) ; init, rew */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0012710, 0001070, /* mov #pkt1, (r0) ; set char */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0012710, 0001110, /* mov #pkt2, (r0) ; read, skip */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0012710, 0001110, /* mov #pkt2, (r0) ; read */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0005711, /* tst (r1) ; err? */ + 0100421, /* bmi hlt */ + 0005000, /* clr r0 */ + 0012704, 0001066+020, /* mov #sgnt+20, r4 */ + 0005007, /* clr r7 */ + 0046523, /* sgnt: "SM" */ + 0140004, /* pkt1: 140004, wcpk, 0, 8. */ + 0001100, + 0000000, + 0000010, + 0001122, /* wcpk: msg, 0, 14., 0 */ + 0000000, + 0000016, + 0000000, + 0140001, /* pkt2: 140001, 0, 0, 512. */ + 0000000, + 0000000, + 0001000, + 0000000 /* hlt: halt */ + /* msg: .blk 4 */ + }; + +t_stat ts_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; +extern uint16 *M; + +sim_tape_rewind (&ts_unit); +for (i = 0; i < BOOT_LEN; i++) + M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_CSR0 >> 1] = ts_dib.ba & DMASK; +M[BOOT_CSR1 >> 1] = (ts_dib.ba & DMASK) + 02; +saved_PC = BOOT_START; +return SCPE_OK; +} + +#else + +t_stat ts_boot (int32 unitno, DEVICE *dptr) +{ +return SCPE_NOFNC; +} +#endif diff --git a/src/PDP11/pdp11_uqssp.h b/src/PDP11/pdp11_uqssp.h new file mode 100644 index 0000000..7e7a790 --- /dev/null +++ b/src/PDP11/pdp11_uqssp.h @@ -0,0 +1,169 @@ +/* pdp11_uqssp.h: Unibus/Qbus storage systems port definitions file + + Copyright (c) 2001-2008, Robert M Supnik + Derived from work by Stephen F. Shirron + + 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-Aug-02 RMS Added TMSCP support +*/ + +#ifndef _PDP11_UQSSP_H_ +#define _PDP11_UQSSP_H_ 0 + +/* IP register - initialization and polling + + read - controller polls command queue + write - controller re-initializes +*/ + +/* SA register - status, address, and purge + + read - data and error information + write - host startup information, purge complete +*/ + +#define SA_ER 0x8000 /* error */ +#define SA_S4 0x4000 /* init step 4 */ +#define SA_S3 0x2000 /* init step 3 */ +#define SA_S2 0x1000 /* init step 2 */ +#define SA_S1 0x0800 /* init step 1 */ + +/* Init step 1, controller to host */ + +#define SA_S1C_NV 0x0400 /* fixed vec NI */ +#define SA_S1C_Q22 0x0200 /* Q22 device */ +#define SA_S1C_DI 0x0100 /* ext diags */ +#define SA_S1C_OD 0x0080 /* odd addrs NI */ +#define SA_S1C_MP 0x0040 /* mapping */ +#define SA_S1C_SM 0x0020 /* spec fncs NI */ +#define SA_S1C_CN 0x0010 /* node name NI */ + +/* Init step 1, host to controller */ + +#define SA_S1H_VL 0x8000 /* valid */ +#define SA_S1H_WR 0x4000 /* wrap mode */ +#define SA_S1H_V_CQ 11 /* cmd q len */ +#define SA_S1H_M_CQ 0x7 +#define SA_S1H_V_RQ 8 /* resp q len */ +#define SA_S1H_M_RQ 0x7 +#define SA_S1H_IE 0x0080 /* int enb */ +#define SA_S1H_VEC 0x007F /* vector */ +#define SA_S1H_CQ(x) (1 << (((x) >> SA_S1H_V_CQ) & SA_S1H_M_CQ)) +#define SA_S1H_RQ(x) (1 << (((x) >> SA_S1H_V_RQ) & SA_S1H_M_RQ)) + +/* Init step 2, controller to host */ + +#define SA_S2C_PT 0x0000 /* port type */ +#define SA_S2C_V_EC 8 /* info to echo */ +#define SA_S2C_M_EC 0xFF +#define SA_S2C_EC(x) (((x) >> SA_S2C_V_EC) & SA_S2C_M_EC) + +/* Init step 2, host to controller */ + +#define SA_S2H_CLO 0xFFFE /* comm addr lo */ +#define SA_S2H_PI 0x0001 /* adp prg int */ + +/* Init step 3, controller to host */ + +#define SA_S3C_V_EC 0 /* info to echo */ +#define SA_S3C_M_EC 0xFF +#define SA_S3C_EC(x) (((x) >> SA_S3C_V_EC) & SA_S3C_M_EC) + +/* Init step 3, host to controller */ + +#define SA_S3H_PP 0x8000 /* purge, poll test */ +#define SA_S3H_CHI 0x7FFF /* comm addr hi */ + +/* Init step 4, controller to host */ + +#define SA_S4C_V_MOD 4 /* adapter # */ +#define SA_S4C_V_VER 0 /* version # */ + +/* Init step 4, host to controller */ + +#define SA_S4H_CS 0x0400 /* host scrpad NI */ +#define SA_S4H_NN 0x0200 /* snd node name NI */ +#define SA_S4H_SF 0x0100 /* spec fnc NI */ +#define SA_S4H_LF 0x0002 /* send last fail */ +#define SA_S4H_GO 0x0001 /* go */ + +/* Fatal error codes (generic through 32) */ + +#define PE_PRE 1 /* packet read err */ +#define PE_PWE 2 /* packet write err */ +#define PE_QRE 6 /* queue read err */ +#define PE_QWE 7 /* queue write err */ +#define PE_HAT 9 /* host access tmo */ +#define PE_ICI 14 /* inv conn ident */ +#define PE_PIE 20 /* prot incompat */ +#define PE_PPF 21 /* prg/poll err */ +#define PE_MRE 22 /* map reg rd err */ +#define PE_T11 475 /* T11 err NI */ +#define PE_SND 476 /* SND err NI */ +#define PE_RCV 477 /* RCV err NI */ +#define PE_NSR 478 /* no such rsrc */ + +/* Comm region offsets */ + +#define SA_COMM_QQ -8 /* unused */ +#define SA_COMM_PI -6 /* purge int */ +#define SA_COMM_CI -4 /* cmd int */ +#define SA_COMM_RI -2 /* resp int */ +#define SA_COMM_MAX ((4 << SA_S1H_M_CQ) + (4 << SA_S1H_M_RQ) - SA_COMM_QQ) + +/* Command/response rings */ + +struct uq_ring { + int32 ioff; /* intr offset */ + uint32 ba; /* base addr */ + uint32 lnt; /* size in bytes */ + uint32 idx; /* current index */ + }; + +/* Ring descriptor entry */ + +#define UQ_DESC_OWN 0x80000000 /* ownership */ +#define UQ_DESC_F 0x40000000 /* flag */ +#define UQ_ADDR 0x003FFFFE /* addr, word aligned */ + +/* Packet header */ + +#define UQ_HDR_OFF -4 /* offset */ + +#define UQ_HLNT 0 /* length */ +#define UQ_HCTC 1 /* credits, type, CID */ + +#define UQ_HCTC_V_CR 0 /* credits */ +#define UQ_HCTC_M_CR 0xF +#define UQ_HCTC_V_TYP 4 /* type */ +#define UQ_HCTC_M_TYP 0xF +#define UQ_TYP_SEQ 0 /* sequential */ +#define UQ_TYP_DAT 1 /* datagram */ +#define UQ_HCTC_V_CID 8 /* conn ID */ +#define UQ_HCTC_M_CID 0xFF +#define UQ_CID_MSCP 0 /* MSCP */ +#define UQ_CID_TMSCP 1 /* TMSCP */ +#define UQ_CID_DUP 2 /* DUP */ +#define UQ_CID_DIAG 0xFF /* diagnostic */ + +#endif diff --git a/src/PDP11/pdp11_vh.cpp b/src/PDP11/pdp11_vh.cpp new file mode 100644 index 0000000..b3e20d0 --- /dev/null +++ b/src/PDP11/pdp11_vh.cpp @@ -0,0 +1,1480 @@ +/* pdp11_vh.c: DHQ11 asynchronous terminal multiplexor simulator + + Copyright (c) 2004-2010, John A. Dundas III + Portions derived from work by 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 + THE AUTHOR 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 the Author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the Author. + + vh DHQ11 asynch multiplexor for SIMH + + 02-Jun-11 MP Added debugging support to trace register, interrupt + and data traffic (SET VH DEBUG[=REG;INT;XMT;RCV]) + Added SET LOG and SET NOLOG support for logging mux + traffic + Fixed SET VH LINES=n to correctly adjust the number + of lines available to be 8, 16, 24, or 32. + Fixed performance issue avoiding redundant polling + 03-Jan-10 JAD Eliminate gcc warnings + 19-Nov-08 RMS Revised for common TMXR show routines + 18-Jun-07 RMS Added UNIT_IDLE flag + 29-Oct-06 RMS Synced poll and clock + 07-Jul-05 RMS Removed extraneous externs + 15-Jun-05 RMS Revised for new autoconfigure interface + Fixed bug in vector display routine + 12-Jun-04 RMS Repair MS2SIMH macro to avoid divide by 0 bug + 08-Jun-04 JAD Repair vh_dev initialization; remove unused + variables, cast to avoid conversion confusion + 07-Jun-04 JAD Complete function prototypes of forward declarations. + Repair broken prototypes of vh_rd() and vh_wr() + Explicitly size integer declarations + 4-Jun-04 JAD Preliminary code: If operating in a PDP-11 Unibus + environment, force DHU mode + 29-May-04 JAD Make certain RX.TIMER is within allowable range + 25-May-04 JAD All time-based operations are scaled by tmxr_poll units + 23-May-04 JAD Change to fifo_get() and dq_tx_report() to avoid + gratuitous stack manipulation + 20-May-04 JAD Made modem control and auto-hangup unit flags + 19-May-04 JAD Fix problem with modem status where the line number + was not being included + 12-May-04 JAD Revised for updated tmxr interfaces + 28-Jan-04 JAD Original creation and testing + +I/O Page Registers + +CSR 17 760 440 (float) + +Vector: 300 (float) + +Priority: BR4 + +Rank: 32 + +*/ +/* MANY constants needed! */ + +#if defined (VM_VAX) +#include "sim_defs.h" +#include "vax_defs.h" +#endif + +#if defined (VM_PDP11) +#include "pdp11_defs.h" +extern int32 cpu_opt; +#endif + +#include "sim_sock.h" +#include "sim_tmxr.h" + +/* imports from pdp11_stddev.c: */ +/* convert ms to SIMH time units based on tmxr_poll polls per second */ +#define MS2SIMH(ms) (((ms) * clk_tps + 500) / 1000) + +#ifndef VH_MUXES +#define VH_MUXES (4) +#endif +#define VH_MNOMASK (VH_MUXES - 1) + +#define VH_LINES (8) + +#define UNIT_V_MODEDHU (UNIT_V_UF + 0) +#define UNIT_V_FASTDMA (UNIT_V_UF + 1) +#define UNIT_V_MODEM (UNIT_V_UF + 2) +#define UNIT_V_HANGUP (UNIT_V_UF + 3) +#define UNIT_MODEDHU (1 << UNIT_V_MODEDHU) +#define UNIT_FASTDMA (1 << UNIT_V_FASTDMA) +#define UNIT_MODEM (1 << UNIT_V_MODEM) +#define UNIT_HANGUP (1 << UNIT_V_HANGUP) + +/* VHCSR - 160440 - Control and Status Register */ + +#define CSR_M_IND_ADDR (017) +#define CSR_SKIP (1 << 4) +#define CSR_MASTER_RESET (1 << 5) +#define CSR_RXIE (1 << 6) +#define CSR_RX_DATA_AVAIL (1 << 7) +#define CSR_M_TX_LINE (017) +#define CSR_V_TX_LINE (8) +#define CSR_TX_DMA_ERR (1 << 12) +#define CSR_DIAG_FAIL (1 << 13) +#define CSR_TXIE (1 << 14) +#define CSR_TX_ACTION (1 << 15) +#define CSR_GETCHAN(x) ((x) & CSR_M_IND_ADDR) +#define CSR_RW \ + (CSR_TXIE|CSR_RXIE|CSR_SKIP|CSR_M_IND_ADDR|CSR_MASTER_RESET) +#define RESET_ABORT (052525) + +/* Receive Buffer (RBUF) */ + +#define FIFO_SIZE (256) +#define FIFO_ALARM (191) +#define FIFO_HALF (FIFO_SIZE / 2) +#define RBUF_M_RX_CHAR (0377) +#define RBUF_M_RX_LINE (07) +#define RBUF_V_RX_LINE (8) +#define RBUF_PARITY_ERR (1 << 12) +#define RBUF_FRAME_ERR (1 << 13) +#define RBUF_OVERRUN_ERR (1 << 14) +#define RBUF_DATA_VALID (1 << 15) +#define RBUF_GETLINE(x) (((x) >> RBUF_V_RX_LINE) & RBUF_M_RX_LINE) +#define RBUF_PUTLINE(x) ((x) << RBUF_V_RX_LINE) +#define RBUF_DIAG \ + (RBUF_PARITY_ERR|RBUF_FRAME_ERR|RBUF_OVERRUN_ERR) +#define XON (021) +#define XOFF (023) + +/* Transmit Character Register (TXCHAR) */ + +#define TXCHAR_M_CHAR (0377) +#define TXCHAR_TX_DATA_VALID (1 << 15) + +/* Receive Timer Register (RXTIMER) */ + +#define RXTIMER_M_RX_TIMER (0377) + +/* Line-Parameter Register (LPR) */ + +#define LPR_DISAB_XRPT (1 << 0) /* not impl. in real DHU */ +#define LPR_V_DIAG (1) +#define LPR_M_DIAG (03) +#define LPR_V_CHAR_LGTH (3) +#define LPR_M_CHAR_LGTH (03) +#define LPR_PARITY_ENAB (1 << 5) +#define LPR_EVEN_PARITY (1 << 6) +#define LPR_STOP_CODE (1 << 7) +#define LPR_V_RX_SPEED (8) +#define LPR_M_RX_SPEED (017) +#define LPR_V_TX_SPEED (12) +#define LPR_M_TX_SPEED (017) + +#define RATE_50 (0) +#define RATE_75 (1) +#define RATE_110 (2) +#define RATE_134 (3) +#define RATE_150 (4) +#define RATE_300 (5) +#define RATE_600 (6) +#define RATE_1200 (7) +#define RATE_1800 (8) +#define RATE_2000 (9) +#define RATE_2400 (10) +#define RATE_4800 (11) +#define RATE_7200 (12) +#define RATE_9600 (13) +#define RATE_19200 (14) +#define RATE_38400 (15) + +/* Line-Status Register (STAT) */ + +#define STAT_DHUID (1 << 8) /* mode: 0=DHV, 1=DHU */ +#define STAT_MDL (1 << 9) /* always 0, has modem support */ +#define STAT_CTS (1 << 11) /* CTS from modem */ +#define STAT_DCD (1 << 12) /* DCD from modem */ +#define STAT_RI (1 << 13) /* RI from modem */ +#define STAT_DSR (1 << 15) /* DSR from modem */ + +/* FIFO Size Register (FIFOSIZE) */ + +#define FIFOSIZE_M_SIZE (0377) + +/* FIFO Data Register (FIFODATA) */ + +#define FIFODATA_W0 (0377) +#define FIFODATA_V_W1 (8) +#define FIFODATA_M_W1 (0377) + +/* Line-Control Register (LNCTRL) */ + +#define LNCTRL_TX_ABORT (1 << 0) +#define LNCTRL_IAUTO (1 << 1) +#define LNCTRL_RX_ENA (1 << 2) +#define LNCTRL_BREAK (1 << 3) +#define LNCTRL_OAUTO (1 << 4) +#define LNCTRL_FORCE_XOFF (1 << 5) +#define LNCTRL_V_MAINT (6) +#define LNCTRL_M_MAINT (03) +#define LNCTRL_LINK_TYPE (1 << 8) /* 0=data leads only, 1=modem */ +#define LNCTRL_DTR (1 << 9) /* DTR to modem */ +#define LNCTRL_RTS (1 << 12) /* RTS to modem */ + +/* Transmit Buffer Address Register Number 1 (TBUFFAD1) */ + +/* Transmit Buffer Address Register Number 2 (TBUFFAD2) */ + +#define TB2_M_TBUFFAD (077) +#define TB2_TX_DMA_START (1 << 7) +#define TB2_TX_ENA (1 << 15) + +/* Transmit DMA Buffer Counter (TBUFFCT) */ + +/* Self-Test Error Codes */ + +#define SELF_NULL (0201) +#define SELF_SKIP (0203) +#define SELF_OCT (0211) +#define SELF_RAM (0225) +#define SELF_RCD (0231) +#define SELF_DRD (0235) + +#define BMP_OK (0305) +#define BMP_BAD (0307) + +/* Loopback types */ + +#define LOOP_NONE (0) +#define LOOP_H325 (1) +#define LOOP_H3101 (2) /* p.2-13 DHQ manual */ +/* Local storage */ + +static uint16 vh_csr[VH_MUXES] = { 0 }; /* CSRs */ +static uint16 vh_timer[VH_MUXES] = { 1 }; /* controller timeout */ +static uint16 vh_mcount[VH_MUXES] = { 0 }; +static uint32 vh_timeo[VH_MUXES] = { 0 }; +static uint32 vh_ovrrun[VH_MUXES] = { 0 }; /* line overrun bits */ +/* XOFF'd channels, one bit/channel */ +static uint32 vh_stall[VH_MUXES] = { 0 }; +static uint16 vh_loop[VH_MUXES] = { 0 }; /* loopback status */ + +/* One bit per controller: */ +static uint32 vh_rxi = 0; /* rcv interrupts */ +static uint32 vh_txi = 0; /* xmt interrupts */ +static uint32 vh_crit = 0; /* FIFO.CRIT */ + +static const int32 bitmask[4] = { 037, 077, 0177, 0377 }; + +/* RX FIFO state */ + +static int32 rbuf_idx[VH_MUXES] = { 0 };/* index into vh_rbuf */ +static uint32 vh_rbuf[VH_MUXES][FIFO_SIZE] = { {0} }; + +/* TXQ state */ + +#define TXQ_SIZE (16) +static int32 txq_idx[VH_MUXES] = { 0 }; +static uint32 vh_txq[VH_MUXES][TXQ_SIZE] = { {0} }; + +/* Need to extend the TMLN structure */ + +typedef struct { + TMLN *tmln; + uint16 lpr; /* line parameters */ + uint16 lnctrl; /* line control */ + uint16 lstat; /* line modem status */ + uint16 tbuffct; /* remaining character count */ + uint16 tbuf1; + uint16 tbuf2; + uint16 txchar; /* single character I/O */ +} TMLX; + +static TMLN vh_ldsc[VH_MUXES * VH_LINES] = { {0} }; +static TMXR vh_desc = { VH_MUXES * VH_LINES, 0, 0, vh_ldsc }; +static TMLX vh_parm[VH_MUXES * VH_LINES] = { {0} }; + +/* debugging bitmaps */ +#define DBG_REG 0x0001 /* trace read/write registers */ +#define DBG_INT 0x0002 /* display transfer requests */ +#define DBG_XMT TMXR_DBG_XMT /* display Transmitted Data */ +#define DBG_RCV TMXR_DBG_RCV /* display Received Data */ + +DEBTAB vh_debug[] = { + {"REG", DBG_REG}, + {"INT", DBG_INT}, + {"XMT", DBG_XMT}, + {"RCV", DBG_RCV}, + {0} +}; + +/* Forward references */ +static t_stat vh_rd (int32 *data, int32 PA, int32 access); +static t_stat vh_wr (int32 data, int32 PA, int32 access); +static t_stat vh_svc (RUN_SVC_DECL, UNIT *uptr); +static int32 vh_rxinta (void); +static int32 vh_txinta (void); +static t_stat vh_clear (int32 vh, t_bool flag); +static t_stat vh_reset (DEVICE *dptr); +static t_stat vh_attach (UNIT *uptr, char *cptr); +static t_stat vh_detach (UNIT *uptr); +static t_stat vh_show_detail (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat vh_show_rbuf (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat vh_show_txq (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat vh_putc (int32 vh, TMLX *lp, int32 chan, int32 data); +static void doDMA (int32 vh, int32 chan); +static t_stat vh_setnl (UNIT *uptr, int32 val, char *cptr, void *desc); +static t_stat vh_set_log (UNIT *uptr, int32 val, char *cptr, void *desc); +static t_stat vh_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc); +static t_stat vh_show_log (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); + +int32 tmxr_send_buffered_data (TMLN *lp); + +/* SIMH I/O Structures */ + +static DIB vh_dib = { + IOBA_VH, + IOLN_VH * VH_MUXES, + &vh_rd, /* read */ + &vh_wr, /* write */ + 2, /* # of vectors */ + IVCL (VHRX), + VEC_VHRX, + { &vh_rxinta, &vh_txinta } /* int. ack. routines */ +}; + +#if VH_MUXES > 4 +# error must extend initializer array of vh_unit +#endif + +static UNIT* vh_unit[VH_MUXES] = { + UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0), + UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0), + UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0), + UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) +}; + +static const REG vh_reg[] = { + { BRDATA_GBL (CSR, vh_csr, DEV_RDX, 16, VH_MUXES) }, + { GRDATA_GBL (DEVADDR, vh_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA_GBL (DEVVEC, vh_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } +}; + +static const MTAB vh_mod[] = { + { UNIT_MODEDHU, 0, "DHV mode", "DHV", NULL }, + { UNIT_MODEDHU, UNIT_MODEDHU, "DHU mode", "DHU", NULL }, + { UNIT_FASTDMA, 0, NULL, "NORMAL", NULL }, + { UNIT_FASTDMA, UNIT_FASTDMA, "fast DMA", "FASTDMA", NULL }, + { UNIT_MODEM, 0, NULL, "NOMODEM", NULL }, + { UNIT_MODEM, UNIT_MODEM, "modem", "MODEM", NULL }, + { UNIT_HANGUP, 0, NULL, "NOHANGUP", NULL }, + { UNIT_HANGUP, UNIT_HANGUP, "hangup", "HANGUP", NULL }, + { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, VH_LINES, "VECTOR", "VECTOR", + &set_vec, &show_vec_mux, (void *) &vh_desc }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "AUTOCONFIGURE", + &set_addr_flt, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "LINES", "LINES", + &vh_setnl, &tmxr_show_lines, (void *) &vh_desc }, + { UNIT_ATT, UNIT_ATT, "summary", NULL, + NULL, &tmxr_show_summ, (void *) &vh_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &tmxr_show_cstat, (void *) &vh_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &tmxr_show_cstat, (void *) &vh_desc }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &vh_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DETAIL", NULL, + NULL, &vh_show_detail, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "RBUF", NULL, + NULL, &vh_show_rbuf, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "TXQ", NULL, + NULL, &vh_show_txq, NULL }, + { MTAB_XTD|MTAB_VDV | MTAB_NC, 0, NULL, "LOG", + &vh_set_log, NULL, &vh_desc }, + { MTAB_XTD|MTAB_VDV | MTAB_NC, 0, NULL, "NOLOG", + &vh_set_nolog, NULL, &vh_desc }, + { MTAB_XTD|MTAB_VDV | MTAB_NMO, 0, "LOG", NULL, + NULL, &vh_show_log, &vh_desc }, + { 0 } +}; + +DEVICE vh_dev = { + "VH", /* name */ + vh_unit, /* units */ + (REG *)vh_reg, /* registers */ + (MTAB *)vh_mod, /* modifiers */ + VH_MUXES, /* # units */ + DEV_RDX, /* address radix */ + 8, /* address width */ + 1, /* address increment */ + DEV_RDX, /* data radix */ + 8, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &vh_reset, /* reset routine */ + NULL, /* boot routine */ + &vh_attach, /* attach routine */ + &vh_detach, /* detach routine */ + (void *)&vh_dib, /* context */ + DEV_FLTA | DEV_DISABLE | DEV_DIS |DEV_NET | DEV_QBUS | DEV_UBUS | DEV_DEBUG, /* flags */ + 0, vh_debug +}; + +/* + * Even though there are VH_MUXES virtual multiplexors, they are just facets to + * underlying single actual controller (TMXR vh_desc), so we use just single lock for them all. + */ +AUTO_INIT_DEVLOCK(vh_lock); + +/* Register names for Debug tracing */ +static char *vh_rd_dhv_regs[] = + {"CSR ", "RBUF ", "LPR ", "STAT ", "LNCTRL", "TBFAD1", "TBFAD2", "TBFCNT" }; +static char *vh_wr_dhv_regs[] = + {"CSR ", "TXCHAR", "LPR ", "STAT ", "LNCTRL", "TBFAD1", "TBFAD2", "TBFCNT" }; +static char *vh_rd_dhu_regs[] = + {"CSR ", "RBUF ", "LPR ", "FIFOSZ", "LNCTRL", "TBFAD1", "TBFAD2", "TBFCNT" }; +static char *vh_wr_dhu_regs[] = + {"CSR ", "RXTIMR", "LPR ", "FIFODT", "LNCTRL", "TBFAD1", "TBFAD2", "TBFCNT" }; + +/* Interrupt routines */ + +static void vh_clr_rxint ( int32 vh ) +{ + vh_rxi &= ~(1 << vh); + if (vh_rxi == 0) + CLR_INT (VHRX); + else + SET_INT (VHRX); +} + +static void vh_set_rxint ( int32 vh ) +{ + vh_rxi |= (1 << vh); + SET_INT (VHRX); +} + +/* RX interrupt ack. (bus cycle) */ + +static int32 vh_rxinta (void) +{ + AUTO_LOCK(vh_lock); + int32 vh; + + for (vh = 0; vh < vh_desc.lines/VH_LINES; vh++) { + if (vh_rxi & (1 << vh)) { + sim_debug(DBG_INT, &vh_dev, "vh_rxinta(vh=%d)\n", vh); + vh_clr_rxint (vh); + return (vh_dib.vec + (vh * 010)); + } + } + return (0); +} + +static void vh_clr_txint ( int32 vh ) +{ + vh_txi &= ~(1 << vh); + if (vh_txi == 0) + CLR_INT (VHTX); + else + SET_INT (VHTX); +} + +static void vh_set_txint ( int32 vh ) +{ + vh_txi |= (1 << vh); + SET_INT (VHTX); +} + +/* TX interrupt ack. (bus cycle) */ + +static int32 vh_txinta (void) +{ + AUTO_LOCK(vh_lock); + int32 vh; + + for (vh = 0; vh < vh_desc.lines/VH_LINES; vh++) { + if (vh_txi & (1 << vh)) { + sim_debug(DBG_INT, &vh_dev, "vh_txinta(vh=%d)\n", vh); + vh_clr_txint (vh); + return (vh_dib.vec + 4 + (vh * 010)); + } + } + return (0); +} +/* RX FIFO get/put routines */ + +/* return 0 on success, -1 on FIFO overflow */ + +static int32 fifo_put ( int32 vh, + TMLX *lp, + int32 data ) +{ + int32 status = 0; + + if (lp == NULL) + goto override; + /* this might have to move to vh_getc() */ + if ((lp->lnctrl & LNCTRL_OAUTO) && ((data & RBUF_DIAG) == 0)) { + TMLX *l0p; + /* implement transmitted data flow control */ + switch (data & 0377) { + case XON: + lp->tbuf2 |= TB2_TX_ENA; + goto common; + case XOFF: + lp->tbuf2 &= ~TB2_TX_ENA; + common: + /* find line 0 for this controller */ + l0p = &vh_parm[vh * VH_LINES]; + if (l0p->lpr & LPR_DISAB_XRPT) + return (0); + break; + default: + break; + } + } +/* BUG: which of the following 2 is correct? */ + /* if ((data & RBUF_DIAG) == RBUF_DIAG) */ + if (data & RBUF_DIAG) + goto override; + if (((lp->lnctrl >> LNCTRL_V_MAINT) & LNCTRL_M_MAINT) == 2) + goto override; + if (!(lp->lnctrl & LNCTRL_RX_ENA)) + return (0); +override: + vh_csr[vh] |= CSR_RX_DATA_AVAIL; + if (rbuf_idx[vh] < FIFO_SIZE) { + vh_rbuf[vh][rbuf_idx[vh]] = data; + rbuf_idx[vh] += 1; + } else { + vh_ovrrun[vh] |= (1 << RBUF_GETLINE (data)); + status = -1; + } + if (vh_csr[vh] & CSR_RXIE) { + if (vh_unit[vh]->flags & UNIT_MODEDHU) { + /* was it a modem status change? */ + if ((data & RBUF_DIAG) == RBUF_DIAG) + vh_set_rxint (vh); + /* look for FIFO alarm @ 3/4 full */ + else if (rbuf_idx[vh] == FIFO_ALARM) + vh_set_rxint (vh); + else if (vh_timer[vh] == 0) + ; /* nothing, infinite timeout */ + else if (vh_timer[vh] == 1) + vh_set_rxint (vh); + else if (vh_timeo[vh] == 0) + vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1; + } else { + /* Interrupt on transition _from_ an empty FIFO */ + if (rbuf_idx[vh] == 1) + vh_set_rxint (vh); + } + } + if (rbuf_idx[vh] > FIFO_ALARM) + vh_crit |= (1 << vh); + /* Implement RX FIFO-level flow control */ + if (lp != NULL) { + if ((lp->lnctrl & LNCTRL_FORCE_XOFF) || + ((vh_crit & (1 << vh)) && (lp->lnctrl & LNCTRL_IAUTO))) { + int32 chan = RBUF_GETLINE(data); + vh_stall[vh] ^= (1 << chan); + /* send XOFF every other character received */ + if (vh_stall[vh] & (1 << chan)) + vh_putc (vh, lp, chan, XOFF); + } + } + return (status); +} + +static int32 fifo_get ( int32 vh ) +{ + int32 data, i; + + if (rbuf_idx[vh] == 0) { + vh_csr[vh] &= ~CSR_RX_DATA_AVAIL; + return (0); + } + /* pick off the first character, mark valid */ + data = vh_rbuf[vh][0] | RBUF_DATA_VALID; + /* move the remainder up */ + rbuf_idx[vh] -= 1; + for (i = 0; i < rbuf_idx[vh]; i++) + vh_rbuf[vh][i] = vh_rbuf[vh][i + 1]; + /* rbuf_idx[vh] -= 1; */ + /* look for any previous overruns */ + if (vh_ovrrun[vh]) { + for (i = 0; i < VH_LINES; i++) { + if (vh_ovrrun[vh] & (1 << i)) { + fifo_put (vh, NULL, RBUF_OVERRUN_ERR | + RBUF_PUTLINE (i)); + vh_ovrrun[vh] &= ~(1 << i); + break; + } + } + } + /* recompute FIFO alarm condition */ + if ((rbuf_idx[vh] < FIFO_HALF) && (vh_crit & (1 << vh))) { + vh_crit &= ~(1 << vh); + /* send XON to all XOFF'd channels on this controller */ + for (i = 0; i < VH_LINES; i++) { + TMLX *lp = &vh_parm[(vh * VH_LINES) + i]; + if (lp->lnctrl & LNCTRL_FORCE_XOFF) + continue; + if (vh_stall[vh] & (1 << i)) { + vh_putc (vh, NULL, i, XON); + vh_stall[vh] &= ~(1 << i); + } + } + } + return (data & 0177777); +} +/* TX Q manipulation */ + +static int32 dq_tx_report ( int32 vh ) +{ + int32 data, i; + + if (txq_idx[vh] == 0) + return (0); + data = vh_txq[vh][0]; + txq_idx[vh] -= 1; + for (i = 0; i < txq_idx[vh]; i++) + vh_txq[vh][i] = vh_txq[vh][i + 1]; + /* txq_idx[vh] -= 1; */ + return (data & 0177777); +} + +static void q_tx_report ( int32 vh, + int32 data ) +{ + if (vh_csr[vh] & CSR_TXIE) + vh_set_txint (vh); + if (txq_idx[vh] >= TXQ_SIZE) { +/* BUG: which of the following 2 is correct? */ + dq_tx_report (vh); + /* return; */ + } + vh_txq[vh][txq_idx[vh]] = CSR_TX_ACTION | data; + txq_idx[vh] += 1; +} +/* Channel get/put routines */ + +static void HangupModem ( int32 vh, + TMLX *lp, + int32 chan ) +{ + if (vh_unit[vh]->flags & UNIT_MODEM) + lp->lstat &= ~(STAT_DCD|STAT_DSR|STAT_CTS|STAT_RI); + if (lp->lnctrl & LNCTRL_LINK_TYPE) + /* RBUF<0> = 0 for modem status */ + fifo_put (vh, lp, RBUF_DIAG | + RBUF_PUTLINE (chan) | + ((lp->lstat >> 8) & 0376)); + /* BUG: check for overflow above */ +} + +/* TX a character on a line, regardless of the TX enable state */ + +static t_stat vh_putc ( int32 vh, + TMLX *lp, + int32 chan, + int32 data ) +{ + int32 val; + t_stat status = SCPE_OK; + + /* truncate to desired character length */ + data &= bitmask[(lp->lpr >> LPR_V_CHAR_LGTH) & LPR_M_CHAR_LGTH]; + switch ((lp->lnctrl >> LNCTRL_V_MAINT) & LNCTRL_M_MAINT) { + case 0: /* normal */ +#if 0 + /* check for (external) loopback setting */ + switch (vh_loop[vh]) { + default: + case LOOP_NONE: + break; + } +#endif + status = tmxr_putc_ln (lp->tmln, data); + if (status == SCPE_LOST) { + tmxr_reset_ln (lp->tmln); + HangupModem (vh, lp, chan); + } else if (status == SCPE_STALL) { + /* let's flush and try again */ + tmxr_send_buffered_data (lp->tmln); + status = tmxr_putc_ln (lp->tmln, data); + } + break; + case 1: /* auto echo */ + break; + case 2: /* local loopback */ + if (lp->lnctrl & LNCTRL_BREAK) + val = fifo_put (vh, lp, + RBUF_FRAME_ERR | RBUF_PUTLINE (chan)); + else + val = fifo_put (vh, lp, + RBUF_PUTLINE (chan) | data); + status = (val < 0) ? SCPE_TTMO : SCPE_OK; + break; + default: /* remote loopback */ + break; + } + return (status); +} + +/* Retrieve all stored input from TMXR and place in RX FIFO */ + +static void vh_getc ( int32 vh ) +{ + uint32 i, c; + TMLX *lp; + + for (i = 0; i < VH_LINES; i++) { + lp = &vh_parm[(vh * VH_LINES) + i]; + while (0 != (c = tmxr_getc_ln (lp->tmln))) { + if (c & SCPE_BREAK) { + fifo_put (vh, lp, + RBUF_FRAME_ERR | RBUF_PUTLINE (i)); + /* BUG: check for overflow above */ + } else { + c &= bitmask[(lp->lpr >> LPR_V_CHAR_LGTH) & + LPR_M_CHAR_LGTH]; + fifo_put (vh, lp, RBUF_PUTLINE (i) | c); + /* BUG: check for overflow above */ + } + } + } +} + +/* I/O dispatch routines */ + +static t_stat vh_rd ( int32 *data, + int32 PA, + int32 access ) +{ + AUTO_LOCK(vh_lock); + int32 vh = ((PA - vh_dib.ba) >> 4) & VH_MNOMASK, line; + TMLX *lp; + + switch ((PA >> 1) & 7) { + case 0: /* CSR */ + *data = vh_csr[vh] | dq_tx_report (vh); + vh_csr[vh] &= ~0117400; /* clear the read-once bits */ + break; + case 1: /* RBUF */ + *data = fifo_get (vh); + break; + case 2: /* LPR */ + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { + *data = 0; + break; + } + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + *data = lp->lpr; + break; + case 3: /* STAT/FIFOSIZE */ + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { + *data = 0; + break; + } + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + *data = (lp->lstat & ~0377) | /* modem status */ +#if 0 + (64 - tmxr_tqln (lp->tmln)); +fprintf (smp_stderr, "\rtqln %d\n", 64 - tmxr_tqln (lp->tmln)); +#else + 64; +#endif + break; + case 4: /* LNCTRL */ + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { + *data = 0; + break; + } + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + *data = lp->lnctrl; + break; + case 5: /* TBUFFAD1 */ + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { + *data = 0; + break; + } + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + *data = lp->tbuf1; + break; + case 6: /* TBUFFAD2 */ + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { + *data = 0; + break; + } + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + *data = lp->tbuf2; + break; + case 7: /* TBUFFCT */ + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { + *data = 0; + break; + } + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + *data = lp->tbuffct; + break; + default: + /* can't happen */ + break; + } + + sim_debug(DBG_REG, &vh_dev, "vh_rd(PA=0x%08X [%s], access=%d, data=0x%X)\n", PA, + ((vh_unit[vh]->flags & UNIT_MODEDHU) ? vh_rd_dhu_regs : vh_rd_dhv_regs)[(PA >> 1) & 07], access, data); + + return (SCPE_OK); +} + +static t_stat vh_wr ( int32 data, + int32 PA, + int32 access ) +{ + AUTO_LOCK(vh_lock); + int32 vh = ((PA - vh_dib.ba) >> 4) & VH_MNOMASK, line; + TMLX *lp; + + sim_debug(DBG_REG, &vh_dev, "vh_wr(PA=0x%08X [%s], access=%d, data=0x%X)\n", PA, + ((vh_unit[vh]->flags & UNIT_MODEDHU) ? vh_wr_dhu_regs : vh_wr_dhv_regs)[(PA >> 1) & 07], access, data); + + switch ((PA >> 1) & 7) { + case 0: /* CSR, but no read-modify-write */ + if (access == WRITEB) + data = (PA & 1) ? + (vh_csr[vh] & 0377) | (data << 8) : + (vh_csr[vh] & ~0377) | (data & 0377); + if (data & CSR_MASTER_RESET) { + if ((vh_unit[vh]->flags & UNIT_MODEDHU) && (data & CSR_SKIP)) + data &= ~CSR_MASTER_RESET; + if (vh == 0) /* Only start unit service on the first unit. Units are polled there */ + sim_activate_clk_cosched (vh_unit[vh], TMXR_MULT); + /* vh_mcount[vh] = 72; */ /* 1.2 seconds */ + vh_mcount[vh] = MS2SIMH (1200); /* 1.2 seconds */ + } + if ((data & CSR_RXIE) == 0) + vh_clr_rxint (vh); + /* catch the RXIE transition if the FIFO is not empty */ + else if (((vh_csr[vh] & CSR_RXIE) == 0) && + (rbuf_idx[vh] != 0)) { + if (vh_unit[vh]->flags & UNIT_MODEDHU) { + if (rbuf_idx[vh] > FIFO_ALARM) + vh_set_rxint (vh); + else if (vh_timer[vh] == 0) + ; + else if (vh_timer[vh] == 1) + vh_set_rxint (vh); + else if (vh_timeo[vh] == 0) + vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1; + } else { + vh_set_rxint (vh); + } + } + if ((data & CSR_TXIE) == 0) + vh_clr_txint (vh); + else if (((vh_csr[vh] & CSR_TXIE) == 0) && + (txq_idx[vh] != 0)) + vh_set_txint (vh); + vh_csr[vh] = (vh_csr[vh] & ~((uint16) CSR_RW)) | (data & (uint16) CSR_RW); + break; + case 1: /* TXCHAR/RXTIMER */ + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) + break; + if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { + vh_mcount[vh] = 1; + break; + } + if (vh_unit[vh]->flags & UNIT_MODEDHU) { + if (CSR_GETCHAN (vh_csr[vh]) != 0) + break; + if (access == WRITEB) + data = (PA & 1) ? + (vh_timer[vh] & 0377) | (data << 8) : + (vh_timer[vh] & ~0377) | (data & 0377); + vh_timer[vh] = data & 0377; +#if 0 + if (vh_csr[vh] & CSR_RXIE) { + if (rbuf_idx[vh] > FIFO_ALARM) + vh_set_rxint (vh); + else if (vh_timer[vh] == 0) + ; + else if (vh_timer[vh] == 1) + vh_set_rxint (vh); + else if (vh_timeo[vh] == 0) + vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1; + } +#endif + } else { + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + if (access == WRITEB) + data = (PA & 1) ? + (lp->txchar & 0377) | (data << 8) : + (lp->txchar & ~0377) | (data & 0377); + lp->txchar = data; /* TXCHAR */ + if (lp->txchar & TXCHAR_TX_DATA_VALID) { + if (lp->tbuf2 & TB2_TX_ENA) + vh_putc (vh, lp, + CSR_GETCHAN (vh_csr[vh]), + lp->txchar); + q_tx_report (vh, + CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE); + lp->txchar &= ~TXCHAR_TX_DATA_VALID; + } + } + break; + case 2: /* LPR */ + if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { + vh_mcount[vh] = 1; + break; + } + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) + break; + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + if (access == WRITEB) + data = (PA & 1) ? + (lp->lpr & 0377) | (data << 8) : + (lp->lpr & ~0377) | (data & 0377); + /* Modify only if CSR<3:0> == 0 */ + if (CSR_GETCHAN (vh_csr[vh]) != 0) + data &= ~LPR_DISAB_XRPT; + lp->lpr = data; + if (((lp->lpr >> LPR_V_DIAG) & LPR_M_DIAG) == 1) { + fifo_put (vh, lp, + RBUF_DIAG | + RBUF_PUTLINE (CSR_GETCHAN (vh_csr[vh])) | + BMP_OK); + /* BUG: check for overflow above */ + lp->lpr &= ~(LPR_M_DIAG << LPR_V_DIAG); + } + break; + case 3: /* STAT/FIFODATA */ + if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { + vh_mcount[vh] = 1; + break; + } + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) + break; + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + if (vh_unit[vh]->flags & UNIT_MODEDHU) { + /* high byte writes not allowed */ + if (PA & 1) + break; + /* transmit 1 or 2 characters */ + if (!(lp->tbuf2 & TB2_TX_ENA)) + break; + vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), data); + q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE); + if (access != WRITEB) + vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), + data >> 8); + } + break; + case 4: /* LNCTRL */ + if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { + vh_mcount[vh] = 1; + break; + } + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) + break; + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + if (access == WRITEB) + data = (PA & 1) ? + (lp->lnctrl & 0377) | (data << 8) : + (lp->lnctrl & ~0377) | (data & 0377); + /* catch the abort TX transition */ + if (!(lp->lnctrl & LNCTRL_TX_ABORT) && + (data & LNCTRL_TX_ABORT)) { + if ((lp->tbuf2 & TB2_TX_ENA) && + (lp->tbuf2 & TB2_TX_DMA_START)) { + lp->tbuf2 &= ~TB2_TX_DMA_START; + q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE); + } + } + /* Implement program-initiated flow control */ + if ( (data & LNCTRL_FORCE_XOFF) && + !(lp->lnctrl & LNCTRL_FORCE_XOFF) ) { + if (!(lp->lnctrl & LNCTRL_IAUTO)) + vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XOFF); + } else if ( !(data & LNCTRL_FORCE_XOFF) && + (lp->lnctrl & LNCTRL_FORCE_XOFF) ) { + if (!(lp->lnctrl & LNCTRL_IAUTO)) + vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON); + else if (!(vh_crit & (1 << vh)) && + (vh_stall[vh] & (1 << CSR_GETCHAN (vh_csr[vh])))) + vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON); + } + if ( (data & LNCTRL_IAUTO) && /* IAUTO 0->1 */ + !(lp->lnctrl & LNCTRL_IAUTO) ) { + if (!(lp->lnctrl & LNCTRL_FORCE_XOFF)) { + if (vh_crit & (1 << vh)) { + vh_putc (vh, lp, + CSR_GETCHAN (vh_csr[vh]), XOFF); + vh_stall[vh] |= (1 << CSR_GETCHAN (vh_csr[vh])); + } + } else { + /* vh_stall[vh] |= (1 << CSR_GETCHAN (vh_csr[vh])) */; + } + } else if ( !(data & LNCTRL_IAUTO) && + (lp->lnctrl & LNCTRL_IAUTO) ) { + if (!(lp->lnctrl & LNCTRL_FORCE_XOFF)) + vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON); + } + /* check modem control bits */ + if ( !(data & LNCTRL_DTR) && /* DTR 1->0 */ + (lp->lnctrl & LNCTRL_DTR)) { + if ((lp->tmln->conn) && (vh_unit[vh]->flags & UNIT_HANGUP)) { + tmxr_linemsg (lp->tmln, "\r\nLine hangup\r\n"); + tmxr_reset_ln (lp->tmln); + } + HangupModem (vh, lp, CSR_GETCHAN (vh_csr[vh])); + } + lp->lnctrl = data; + lp->tmln->rcve = (data & LNCTRL_RX_ENA) ? 1 : 0; + tmxr_poll_rx (&vh_desc); + vh_getc (vh); + if (lp->lnctrl & LNCTRL_BREAK) + vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), 0); + break; + case 5: /* TBUFFAD1 */ + if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { + vh_mcount[vh] = 1; + break; + } + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) + break; + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + if (access == WRITEB) + data = (PA & 1) ? + (lp->tbuf1 & 0377) | (data << 8) : + (lp->tbuf1 & ~0377) | (data & 0377); + lp->tbuf1 = data; + break; + case 6: /* TBUFFAD2 */ + if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { + vh_mcount[vh] = 1; + break; + } + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) + break; + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + if (access == WRITEB) + data = (PA & 1) ? + (lp->tbuf2 & 0377) | (data << 8) : + (lp->tbuf2 & ~0377) | (data & 0377); + lp->tbuf2 = data; + /* if starting a DMA, clear DMA_ERR */ + if (vh_unit[vh]->flags & UNIT_FASTDMA) { + doDMA (vh, CSR_GETCHAN (vh_csr[vh])); + tmxr_send_buffered_data (lp->tmln); + } + break; + case 7: /* TBUFFCT */ + if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { + vh_mcount[vh] = 1; + break; + } + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) + break; + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + if (access == WRITEB) + data = (PA & 1) ? + (lp->tbuffct & 0377) | (data << 8) : + (lp->tbuffct & ~0377) | (data & 0377); + lp->tbuffct = data; + break; + default: + /* can't happen */ + break; + } + return (SCPE_OK); +} + +static void doDMA ( int32 vh, + int32 chan ) +{ + RUN_SCOPE; + int32 line, status; + uint32 pa; + TMLX *lp; + + line = (vh * VH_LINES) + chan; + lp = &vh_parm[line]; + if ((lp->tbuf2 & TB2_TX_ENA) && (lp->tbuf2 & TB2_TX_DMA_START)) { +/* BUG: should compare against available xmit buffer space */ + pa = lp->tbuf1; + pa |= (lp->tbuf2 & TB2_M_TBUFFAD) << 16; + status = chan << CSR_V_TX_LINE; + while (lp->tbuffct) { + uint8 buf; + if (Map_ReadB (RUN_PASS, pa, 1, &buf)) { + status |= CSR_TX_DMA_ERR; + lp->tbuffct = 0; + break; + } + if (vh_putc (vh, lp, chan, buf) != SCPE_OK) + break; + /* pa = (pa + 1) & PAMASK; */ + pa = (pa + 1) & ((1 << 22) - 1); + lp->tbuffct--; + } + lp->tbuf1 = pa & 0177777; + lp->tbuf2 = (lp->tbuf2 & ~TB2_M_TBUFFAD) | + ((pa >> 16) & TB2_M_TBUFFAD); + if (lp->tbuffct == 0) { + lp->tbuf2 &= ~TB2_TX_DMA_START; + q_tx_report (vh, status); + } + } +} + +/* Perform many of the functions of PROC2 */ + +static t_stat vh_svc (RUN_SVC_DECL, UNIT *uptr) +{ + AUTO_LOCK(vh_lock); + RUN_SVC_CHECK_CANCELLED(uptr); + int32 vh, newln, i; + + /* scan all muxes for countdown reset */ + for (vh = 0; vh < vh_desc.lines/VH_LINES; vh++) { + if (vh_csr[vh] & CSR_MASTER_RESET) { + if (vh_mcount[vh] != 0) + vh_mcount[vh] -= 1; + else + vh_clear (vh, FALSE); + } + } + /* sample every 10ms for modem changes (new connections) */ + newln = tmxr_poll_conn (&vh_desc); + if (newln >= 0) { + TMLX *lp; + int32 line; + vh = newln / VH_LINES; /* determine which mux */ + line = newln - (vh * VH_LINES); + lp = &vh_parm[newln]; + lp->lstat |= STAT_DSR | STAT_DCD | STAT_CTS; + if (!(lp->lnctrl & LNCTRL_DTR)) + lp->lstat |= STAT_RI; + if (lp->lnctrl & LNCTRL_LINK_TYPE) + fifo_put (vh, lp, RBUF_DIAG | + RBUF_PUTLINE (line) | + ((lp->lstat >> 8) & 0376)); + /* BUG: should check for overflow above */ + } + /* scan all muxes, lines for DMA to complete; start every 3.12ms */ + for (vh = 0; vh < vh_desc.lines/VH_LINES; vh++) { + for (i = 0; i < VH_LINES; i++) + doDMA (vh, i); + } + /* interrupt driven in a real DHQ */ + tmxr_poll_rx (&vh_desc); + for (vh = 0; vh < vh_desc.lines/VH_LINES; vh++) + vh_getc (vh); + tmxr_poll_tx (&vh_desc); + /* scan all DHU-mode muxes for RX FIFO timeout */ + for (vh = 0; vh < vh_desc.lines/VH_LINES; vh++) { + if (vh_unit[vh]->flags & UNIT_MODEDHU) { + if (vh_timeo[vh] && (vh_csr[vh] & CSR_RXIE)) { + vh_timeo[vh] -= 1; + if ((vh_timeo[vh] == 0) && rbuf_idx[vh]) + vh_set_rxint (vh); + } + } + } + sim_activate_clk_cosched (uptr, TMXR_MULT); /* requeue ourselves */ + return (SCPE_OK); +} + +/* init a channel on a controller */ + +/* set for: +send/receive 9600 +8 data bits +1 stop bit +no parity +parity odd +auto-flow off +RX disabled +TX enabled +no break on line +no loopback +link type set to data-leads only +DTR & RTS off +DMA character counter 0 +DMA start address registers 0 +TX_DMA_START 0 +TX_ABORT 0 +auto-flow reports enabled +FIFO size set to 64 +*/ + +static void vh_init_chan ( int32 vh, + int32 chan ) +{ + int32 line; + TMLX *lp; + + line = (vh * VH_LINES) + chan; + lp = &vh_parm[line]; + lp->lpr = (RATE_9600 << LPR_V_TX_SPEED) | + (RATE_9600 << LPR_V_RX_SPEED) | + (03 << LPR_V_CHAR_LGTH); + lp->lnctrl = 0; + lp->lstat &= ~(STAT_MDL | STAT_DHUID | STAT_RI); + if (vh_unit[vh]->flags & UNIT_MODEDHU) + lp->lstat |= STAT_DHUID | 64; + if (!(vh_unit[vh]->flags & UNIT_MODEM)) + lp->lstat |= STAT_DSR | STAT_DCD | STAT_CTS; + lp->tmln->xmte = 1; + lp->tmln->rcve = 0; + lp->tbuffct = 0; + lp->tbuf1 = 0; + lp->tbuf2 = TB2_TX_ENA; + lp->txchar = 0; +} + +/* init a controller; flag true if BINIT, false if master.reset */ + +static t_stat vh_clear ( int32 vh, + t_bool flag ) +{ + int32 i; + + txq_idx[vh] = 0; + rbuf_idx[vh] = 0; + /* put 8 diag bytes in FIFO: 6 SELF_x, 2 circuit revision codes */ + if (vh_csr[vh] & CSR_SKIP) { + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(0) | SELF_SKIP); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(1) | SELF_SKIP); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(2) | SELF_SKIP); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(3) | SELF_SKIP); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(4) | SELF_SKIP); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(5) | SELF_SKIP); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(6) | 0107); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(7) | 0105); + } else { + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(0) | SELF_NULL); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(1) | SELF_NULL); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(2) | SELF_NULL); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(3) | SELF_NULL); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(4) | SELF_NULL); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(5) | SELF_NULL); + /* PROC2 ver. 1 */ + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(6) | 0107); + /* PROC1 ver. 1 */ + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(7) | 0105); + } + vh_csr[vh] &= ~(CSR_TX_ACTION|CSR_DIAG_FAIL|CSR_MASTER_RESET); + if (flag) + vh_csr[vh] &= ~(CSR_TXIE|CSR_RXIE|CSR_SKIP); + vh_csr[vh] |= CSR_TX_DMA_ERR | (CSR_M_TX_LINE << CSR_V_TX_LINE); + vh_clr_rxint (vh); + vh_clr_txint (vh); + vh_timer[vh] = 1; + vh_timeo[vh] = 0; + vh_ovrrun[vh] = 0; + for (i = 0; i < VH_LINES; i++) + vh_init_chan (vh, i); + vh_crit &= ~(1 << vh); + vh_stall[vh] = 0; + vh_loop[vh] = LOOP_NONE; + return (SCPE_OK); +} + +/* Reset all controllers. Used by BINIT and RESET. */ + +static t_stat vh_reset ( DEVICE *dptr ) +{ + AUTO_LOCK(vh_lock); + int32 i; + + sim_bind_devunits_lock(&vh_dev, vh_lock); + + for (i = 0; i < vh_desc.lines; i++) + vh_parm[i].tmln = &vh_ldsc[i]; + + for (i = 0; i < vh_desc.lines/VH_LINES; i++) { +#if defined (VM_PDP11) + /* if Unibus, force DHU mode */ + if (UNIBUS) + vh_unit[i]->flags |= UNIT_MODEDHU; +#endif + vh_clear (i, TRUE); + } + vh_rxi = vh_txi = 0; + CLR_INT (VHRX); + CLR_INT (VHTX); + sim_cancel (vh_unit[0]); + return (auto_config (dptr->name, (dptr->flags & DEV_DIS) ? 0 : vh_desc.lines/VH_LINES)); +} + + +static t_stat vh_attach ( UNIT *uptr, + char *cptr ) +{ + if (uptr == vh_unit[0]) + return (tmxr_attach (&vh_desc, uptr, cptr)); + return (SCPE_NOATT); +} + +static t_stat vh_detach ( UNIT *uptr ) +{ + return (tmxr_detach (&vh_desc, uptr)); +} + +static void vh_detail_line ( SMP_FILE *st, + int32 vh, + int32 chan ) +{ + int32 line; + TMLX *lp; + + line = (vh * VH_LINES) + chan; + lp = &vh_parm[line]; + fprintf (st, "\tline %d\tlpr %06o, lnctrl %06o, lstat %06o\n", + chan, lp->lpr, lp->lnctrl, lp->lstat); + fprintf (st, "\t\ttbuffct %06o, tbuf1 %06o, tbuf2 %06o, txchar %06o\n", + lp->tbuffct, lp->tbuf1, lp->tbuf2, lp->txchar); + fprintf (st, "\t\ttmln rcve %d xmte %d\n", + lp->tmln->rcve, lp->tmln->xmte); +} + +static t_stat vh_show_detail ( SMP_FILE *st, + UNIT *uptr, + int32 val, + void *desc ) +{ + int32 i, j; + + fprintf (st, "VH:\trxi %d, txi %d\n", vh_rxi, vh_txi); + for (i = 0; i < vh_desc.lines/VH_LINES; i++) { + fprintf (st, "VH%d:\tmode %s, crit %d\n", i, + vh_unit[i]->flags & UNIT_MODEDHU ? "DHU" : "DHV", + vh_crit & (1 << i)); + fprintf (st, "\tCSR %06o, mcount %d, rbuf_idx %d, txq_idx %d\n", + vh_csr[i], vh_mcount[i], rbuf_idx[i], txq_idx[i]); + for (j = 0; j < VH_LINES; j++) + vh_detail_line (st, i, j); + } + return (SCPE_OK); +} + +static t_stat vh_show_rbuf ( SMP_FILE *st, + UNIT *uptr, + int32 val, + void *desc ) +{ + int32 i; + + for (i = 0; i < rbuf_idx[0]; i++) + fprintf (st, "%03d: %06o\n", i, vh_rbuf[0][i]); + return (SCPE_OK); +} + +static t_stat vh_show_txq ( SMP_FILE *st, + UNIT *uptr, + int32 val, + void *desc ) +{ + int32 i; + + for (i = 0; i < txq_idx[0]; i++) + fprintf (st, "%02d: %06o\n\r", i, vh_txq[0][i]); + return (SCPE_OK); +} + +/* SET LINES processor */ + +static t_stat vh_setnl (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 newln, i, t, ndev; +t_stat r; + +if (cptr == NULL) + return SCPE_ARG; +newln = (int32) get_uint (cptr, 10, (VH_MUXES * VH_LINES), &r); +if ((r != SCPE_OK) || (newln == vh_desc.lines)) + return r; +if ((newln == 0) || (newln % VH_LINES)) + return SCPE_ARG; +if (newln < vh_desc.lines) { + for (i = newln, t = 0; i < vh_desc.lines; i++) + t = t | (vh_ldsc[i].conn != 0); + if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) + return SCPE_OK; + for (i = newln; i < vh_desc.lines; i++) { + if (vh_ldsc[i].conn) { + tmxr_linemsg (&vh_ldsc[i], "\r\nOperator disconnected line\r\n"); + tmxr_reset_ln (&vh_ldsc[i]); /* reset line */ + } + if ((i % VH_LINES) == (VH_LINES - 1)) + vh_clear (i / VH_LINES, TRUE); /* reset mux */ + } + } +vh_dib.lnt = (newln / VH_LINES) * IOLN_VH; /* set length */ +vh_desc.lines = newln; +ndev = ((vh_dev.flags & DEV_DIS)? 0: (vh_desc.lines / VH_LINES)); +vh_dev.numunits = (newln / VH_LINES); +return auto_config (vh_dev.name, ndev); /* auto config */ +} + +/* SET LOG processor */ + +static t_stat vh_set_log (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +char *tptr; +t_stat r; +int32 ln; + +if (cptr == NULL) + return SCPE_ARG; +tptr = strchr (cptr, '='); +if ((tptr == NULL) || (*tptr == 0)) + return SCPE_ARG; +*tptr++ = 0; +ln = (int32) get_uint (cptr, 10, (VH_MUXES * VH_LINES), &r); +if ((r != SCPE_OK) || (ln >= vh_desc.lines)) + return SCPE_ARG; +return tmxr_set_log (NULL, ln, tptr, desc); +} + +/* SET NOLOG processor */ + +static t_stat vh_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +t_stat r; +int32 ln; + +if (cptr == NULL) + return SCPE_ARG; +ln = (int32) get_uint (cptr, 10, (VH_MUXES * VH_LINES), &r); +if ((r != SCPE_OK) || (ln >= vh_desc.lines)) + return SCPE_ARG; +return tmxr_set_nolog (NULL, ln, NULL, desc); +} + +/* SHOW LOG processor */ + +static t_stat vh_show_log (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i; + +for (i = 0; i < vh_desc.lines; i++) { + fprintf (st, "line %d: ", i); + tmxr_show_log (st, NULL, i, desc); + fprintf (st, "\n"); + } +return SCPE_OK; +} diff --git a/src/PDP11/pdp11_xq.cpp b/src/PDP11/pdp11_xq.cpp new file mode 100644 index 0000000..e7ce4a5 --- /dev/null +++ b/src/PDP11/pdp11_xq.cpp @@ -0,0 +1,3228 @@ +/* pdp11_xq.c: DEQNA/DELQA ethernet controller simulator + ------------------------------------------------------------------------------ + + Copyright (c) 2002-2007, 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"), + 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 + THE AUTHOR 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 the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + 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.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. + External Loopback does not go out to the physical adapter, it is + implemented more like an extended Internal Loopback + Time Domain Reflectometry (TDR) numbers are faked + The 10-second approx. hardware/software reset delay does not exist + Some physical ethernet receive events like Runts, Overruns, etc. are + never reported back, since the packet-level driver never sees them + + Certain advantages are derived from this emulation: + If the real ethernet controller is faster than 10Mbit/sec, the speed is + seen by the simulated cpu since there are no minimum response times. + + Known Bugs or Unsupported features, in priority order: + 1) PDP11 bootstrap + 2) MOP functionality not implemented + 3) Local packet processing not implemented + + Regression Tests: + VAX: 1. Console SHOW DEVICE + 2. VMS v7.2 boots/initializes/shows device + 3. VMS DECNET - SET HOST and COPY tests + 4. VMS MultiNet - SET HOST/TELNET and FTP tests + 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 + + ------------------------------------------------------------------------------ + + Modification history: + + 20-Apr-11 MP Fixed missing information from save/restore which + caused operations to not complete correctly after + a restore until the OS reset the controller. + 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 smp_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 + 01-Dec-04 DTH Added runtime attach prompt + 27-Feb-04 DTH Removed struct timeb deuggers + 31-Jan-04 DTH Replaced #ifdef debuggers with inline debugging + 19-Jan-04 DTH Combined service timers into one for efficiency + 16-Jan-04 DTH Added more info to SHOW MOD commands, added SET/SHOW XQ DEBUG + 13-Jan-04 DTH Corrected interrupt code with help from Tom Evans + 06-Jan-04 DTH Added protection against changing mac and type if attached + 05-Jan-04 DTH Moved most of xq_setmac to sim_ether + 26-Dec-03 DTH Moved ethernet show and queue functions to sim_ether + 03-Dec-03 DTH Added minimum name length to show xq eth + 25-Nov-03 DTH Reworked interrupts to fix broken XQB implementation + 19-Nov-03 MP Rearranged timer reset sequencing to allow for a device to be + disabled after it had been enabled. + 17-Nov-03 DTH Standardized #include of timeb.h + 28-Sep-03 MP - Fixed bug in xq_process_setup which would leave the + 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 Multicast and promiscuous to + xq_show_filters + - The stuck in All Multicast or Promiscuous issue is + worse than previously thought. See comments in + xq_process_setup. + - Change xq_setmac to also allow ":" as a address + 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 manual. + The manual mentions "N/A" which I'm interpreting to + mean "Not Affected". + 05-Jun-03 DTH Added receive packet splitting + 03-Jun-03 DTH Added SHOW XQ FILTERS + 02-Jun-03 DTH Added SET/SHOW XQ STATS (packet statistics), runt & giant processing + 28-May-03 DTH Modified message queue for dynamic size to shrink executable + 28-May-03 MP Fixed bug in xq_setmac + 06-May-03 DTH Changed 32-bit t_addr to uint32 for v3.0 + Removed SET ADDRESS functionality + 05-May-03 DTH Added second controller + 26-Mar-03 DTH Added PDP11 bootrom loader + Adjusted xq_ex and xq_dev to allow pdp11 to look at bootrom + Patched bootrom to allow "pass" of diagnostics on RSTS/E + 06-Mar-03 DTH Corrected interrupts on IE state transition (code by Tom Evans) + Added interrupt clear on soft reset (first noted by Bob Supnik) + Removed interrupt when setting XL or RL (multiple people) + 16-Jan-03 DTH Merged Mark Pizzolato's enhancements with main source + Corrected PDP11 XQ_DEBUG compilation + 15-Jan-03 MP Fixed the number of units in the xq device structure. + 13-Jan-03 MP Reworked the timer management logic which initiated + the system id broadcast messages. The original + implementation triggered this on the CSR transition + of Receiver Enabled. This was an issue since the + it seems that at least VMS's XQ driver makes this + transition often and the resulting overhead reduces + the simulated CPU instruction execution throughput by + about 40%. I start the system id timer on device + reset and it fires once a second so that it can + leverage the reasonably recalibrated tmr_poll value. + 13-Jan-03 MP Changed the scheduling of xq_svc to leverage the + dynamically computed clock values to achieve an + approximate interval of 100 per second. This is + more than sufficient for normal system behaviour + expecially since we service receives with every + transmit. The previous fixed value of 2500 + attempted to get 200/sec but it was a guess that + didn't adapt. On faster host systems (possibly + most of them) the 2500 number spends too much time + polling. + 10-Jan-03 DTH Removed XQ_DEBUG dependency from Borland #pragmas + Added SET XQ BOOTROM command for PDP11s + 07-Jan-03 DTH Added pointer to online manuals + 02-Jan-03 DTH Added local packet processing + 30-Dec-02 DTH Added automatic system id broadcast + 27-Dec-02 DTH Merged Mark Pizzolato's enhancements with main source + 20-Dec-02 MP Fix bug that caused VMS system crashes when attempting cluster + operations. Added additional conditionally compiled debug + info needed to track down the issue. + 17-Dec-02 MP Added SIMH "registers" describing the Ethernet state + so this information can be recorded in a "saved" snapshot. + 05-Dec-02 MP Adjusted the rtime value from 100 to 2500 which increased the + available CPU cycles for Instruction execution by almost 100%. + This made sense after the below enhancements which, in general + caused the draining of the received data stream much more + agressively with less overhead. + 05-Dec-02 MP Added a call to xq_svc after all successful calls to eth_write + to allow receive processing to happen before the next event + service time. + 05-Dec-02 MP Restructured the flow of processing in xq_svc so that eth_read + is called repeatedly until either a packet isn't found or + there is no room for another one in the queue. Once that has + been done, xq_process_rdbl is called to pass the queued packets + into the simulated system as space is available there. + xq_process_rdbl is also called at the beginning of xq_svc to + drain the queue into the simulated system, making more room + available in the queue. No processing is done at all in + xq_svc if the receiver is disabled. + 04-Dec-02 MP Changed interface and usage to xq_insert_queue to pass + the packet to be inserted by reference. This avoids 3K bytes + of buffer copy operations for each packet received. Now only + copy actual received packet data. + 31-Oct-02 DTH Cleaned up pointer warnings (found by Federico Schwindt) + Corrected unattached and no network behavior + Added message when SHOW XQ ETH finds no devices + 23-Oct-02 DTH Beta 5 released + 22-Oct-02 DTH Added all_multicast and promiscuous support + 21-Oct-02 DTH Added write buffer max size check (code by Jason Thorpe) + Corrected copyright again + Implemented NXM testing and recovery + 16-Oct-02 DTH Beta 4 released + Added and debugged Sanity Timer code + Corrected copyright + 15-Oct-02 DTH Rollback to known good Beta3 and roll forward; TCP broken + 12-Oct-02 DTH Fixed VAX network bootstrap; setup packets must return TDR > 0 + 11-Oct-02 DTH Added SET/SHOW XQ TYPE and SET/SHOW XQ SANITY commands + 10-Oct-02 DTH Beta 3 released; Integrated with 2.10-0b1 + Fixed off-by-1 bug on xq->setup.macs[7..13] + Added xq_make_checksum + Added rejection of multicast addresses in SET XQ MAC + 08-Oct-02 DTH Beta 2 released; Integrated with 2.10-0p4 + Added variable vector (fixes PDP11) and copyrights + 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11 + 24-Sep-02 DTH Moved more code to Sim_Ether module, added SHOW ETH command + 23-Sep-02 DTH Added SET/SHOW MAC command + 22-Sep-02 DTH Multinet TCP/IP loaded, tests OK via SET HOST/TELNET + 20-Sep-02 DTH Cleaned up code fragments, fixed non-DECNET MAC use + 19-Sep-02 DTH DECNET finally stays up; successful SET HOST to another node + 15-Sep-02 DTH Added ethernet packet read/write + 13-Sep-02 DTH DECNET starts, but circuit keeps going up & down + 26-Aug-02 DTH DECNET loaded, returns device timeout + 22-Aug-02 DTH VMS 7.2 recognizes device as XQA0 + 18-Aug-02 DTH VAX sees device as XQA0; shows hardcoded MAC correctly + 15-Aug-02 DTH Started XQ simulation + + ------------------------------------------------------------------------------ +*/ + +#include +#include "pdp11_xq.h" +#include "pdp11_xq_bootrom.h" + +/* + * Multiprocessor note: + * + * VAX MP supports only DEQNA and DELQA since VSMP currently provides XQDRIVER patches + * only for these two controllers. DELQA PLUS is not supported: VSMP patches for it + * are not provided, and code in xq_process_turbo_rbdl and xq_process_turbo_xbdl also + * had not been modified to provide appropriate memory barriers. + * + ****************************************************************** + * + * Note: Current code in InterlockedOpLock::virt_lock and InterlockedOpLock::phys_lock + * assumes that XQ performs access to BDL only within the context of VCPU threads, + * not in IOP thread. If it were to change, phys_lock and virt_lock should be modified + * to issue smp_mb even in uniprocessor case, since XQDRIVER patches use BBSSI + * to issue memory barrier and since XQDRIVER code itself also uses interlocked + * instructions to issue memory barrier. The code that implements VAXMP_API_OP_MEMBAR + * would need to be also modified to always issue memory barrier, even in uniprocessor + * case. + */ + +/* 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(RUN_SVC_DECL, UNIT * uptr); +t_stat xq_svc_ex(RUN_DECL, UNIT * uptr, CTLR* xq); +t_stat xq_tmrsvc(RUN_SVC_DECL, UNIT * uptr); +t_stat xq_reset (DEVICE * dptr); +t_stat xq_attach (UNIT * uptr, char * cptr); +t_stat xq_detach (UNIT * uptr); +t_stat xq_showmac (SMP_FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat xq_setmac (UNIT* uptr, int32 val, char* cptr, void* desc); +t_stat xq_show_filters (SMP_FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat xq_show_stats (SMP_FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat xq_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc); +t_stat xq_show_type (SMP_FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat xq_set_type (UNIT* uptr, int32 val, char* cptr, void* desc); +t_stat xq_show_sanity (SMP_FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat xq_set_sanity (UNIT* uptr, int32 val, char* cptr, void* desc); +t_stat xq_show_poll (SMP_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); +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); +void xq_reset_santmr(CTLR* xq); +t_stat xq_boot_host(CTLR* xq); +t_stat xq_system_id(CTLR* xq, const ETH_MAC dst, uint16 receipt_id); +void xqa_read_callback(int status); +void xqb_read_callback(int status); +void xqa_write_callback(int status); +void xqb_write_callback(int status); +void xq_setint (CTLR* xq); +void xq_clrint (CTLR* xq, t_bool intack = FALSE); +int32 xq_int (void); +void xq_csr_set_clr(CTLR* xq, uint16 set_bits, uint16 clear_bits); +static int32 xq_update_bdl_status_words(RUN_DECL, uint32 bdl_ba, uint16* stw); +static int32 xq_fetch_bdl_entry(RUN_DECL, uint32 bdl_ba, uint16* buf); +static void xq_activate(UNIT* uptr, t_bool try_at_idletime, uint32 fraction); +static void xq_activate_abs(UNIT* uptr, t_bool try_at_idletime, uint32 fraction); + +struct xq_device xqa = { + xqa_read_callback, /* read callback routine */ + xqa_write_callback, /* write callback routine */ + {0x08, 0x00, 0x2B, 0xAA, 0xBB, 0xCC}, /* mac */ +#if defined(VM_VAX_MP) + XQ_T_DELQA, /* type */ +#else + XQ_T_DELQA_PLUS, /* type */ +#endif + XQ_T_DELQA, /* mode */ + XQ_SERVICE_INTERVAL, /* poll */ + 0, 0, /* coalesce */ + {0} /* sanity */ + }; + +struct xq_device xqb = { + xqb_read_callback, /* read callback routine */ + xqb_write_callback, /* write callback routine */ + {0x08, 0x00, 0x2B, 0xBB, 0xCC, 0xDD}, /* mac */ +#if defined(VM_VAX_MP) + XQ_T_DELQA, /* type */ +#else + XQ_T_DELQA_PLUS, /* type */ +#endif + XQ_T_DELQA, /* mode */ + XQ_SERVICE_INTERVAL, /* poll */ + 0, 0, /* coalesce */ + {0} /* sanity */ + }; + +/* SIMH device structures */ +DIB xqa_dib = { IOBA_XQ, IOLN_XQ, &xq_rd, &xq_wr, + 1, IVCL (XQ), 0, { &xq_int } }; + +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_GBL ( SA0, xqa.mac[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( SA1, xqa.mac[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( SA2, xqa.mac[2], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( SA3, xqa.mac[3], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( SA4, xqa.mac[4], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( SA5, xqa.mac[5], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( MX0, xqa.mac_checksum[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( MX1, xqa.mac_checksum[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( RBDL, xqa.rbdl[0], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( RBDH, xqa.rbdl[1], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( XBDL, xqa.xbdl[0], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( XBDH, xqa.xbdl[1], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( VAR, xqa.var, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( CSR, xqa.csr, XQ_RDX, 16, 0), REG_FIT }, + { FLDATA_GBL ( INT, xqa.irq, 0) }, + { GRDATA_GBL ( TYPE, xqa.type, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA_GBL ( MODE, xqa.mode, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA_GBL ( POLL, xqa.poll, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA_GBL ( CLAT, xqa.coalesce_latency, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA_GBL ( CLATT, xqa.coalesce_latency_ticks, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA_GBL ( RBDL_BA, xqa.rbdl_ba, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( XBDL_BA, xqa.xbdl_ba, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SETUP_PRM, xqa.setup.promiscuous, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SETUP_MLT, xqa.setup.multicast, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SETUP_L1, xqa.setup.l1, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SETUP_L2, xqa.setup.l2, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SETUP_L3, xqa.setup.l3, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SETUP_SAN, xqa.setup.sanity_timer, XQ_RDX, 32, 0), REG_HRO}, + { BRDATA_GBL ( SETUP_MACS, &xqa.setup.macs, XQ_RDX, 8, sizeof(xqa.setup.macs)), REG_HRO}, + { BRDATA_GBL ( STATS, &xqa.stats, XQ_RDX, 8, sizeof(xqa.setup.macs)), REG_HRO}, + { BRDATA_GBL ( TURBO_INIT, &xqa.init, XQ_RDX, 8, sizeof(xqa.init)), REG_HRO}, + { GRDATA_GBL ( SRR, xqa.srr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( SRQR, xqa.srqr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( IBA, xqa.iba, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA_GBL ( ICR, xqa.icr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( IPEND, xqa.pending_interrupt, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( TBINDX, xqa.tbindx, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( RBINDX, xqa.rbindx, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( IDTMR, xqa.idtmr, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( VECTOR, xqa_dib.vec, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( MUST_POLL, xqa.must_poll, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SANT_ENAB, xqa.sanity.enabled, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SANT_QSECS, xqa.sanity.quarter_secs, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SANT_TIMR, xqa.sanity.timer, XQ_RDX, 32, 0), REG_HRO}, + { NULL }, +}; + +DIB xqb_dib = { IOBA_XQB, IOLN_XQB, &xq_rd, &xq_wr, + 1, IVCL (XQ), 0, { &xq_int } }; + +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_GBL ( SA0, xqb.mac[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( SA1, xqb.mac[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( SA2, xqb.mac[2], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( SA3, xqb.mac[3], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( SA4, xqb.mac[4], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( SA5, xqb.mac[5], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( MX0, xqb.mac_checksum[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( MX1, xqb.mac_checksum[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA_GBL ( RBDL, xqb.rbdl[0], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( RBDH, xqb.rbdl[1], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( XBDL, xqb.xbdl[0], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( XBDH, xqb.xbdl[1], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( VAR, xqb.var, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( CSR, xqb.csr, XQ_RDX, 16, 0), REG_FIT }, + { FLDATA_GBL ( INT, xqb.irq, 0) }, + { GRDATA_GBL ( TYPE, xqb.type, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA_GBL ( MODE, xqb.mode, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA_GBL ( POLL, xqb.poll, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA_GBL ( CLAT, xqb.coalesce_latency, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA_GBL ( CLATT, xqb.coalesce_latency_ticks, XQ_RDX, 16, 0), REG_HRO}, + { GRDATA_GBL ( RBDL_BA, xqb.rbdl_ba, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( XBDL_BA, xqb.xbdl_ba, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SETUP_PRM, xqb.setup.promiscuous, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SETUP_MLT, xqb.setup.multicast, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SETUP_L1, xqb.setup.l1, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SETUP_L2, xqb.setup.l2, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SETUP_L3, xqb.setup.l3, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SETUP_SAN, xqb.setup.sanity_timer, XQ_RDX, 32, 0), REG_HRO}, + { BRDATA_GBL ( SETUP_MACS, &xqb.setup.macs, XQ_RDX, 8, sizeof(xqb.setup.macs)), REG_HRO}, + { BRDATA_GBL ( STATS, &xqb.stats, XQ_RDX, 8, sizeof(xqa.setup.macs)), REG_HRO}, + { BRDATA_GBL ( TURBO_INIT, &xqb.init, XQ_RDX, 8, sizeof(xqb.init)), REG_HRO}, + { GRDATA_GBL ( SRR, xqb.srr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( SRQR, xqb.srqr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( IBA, xqb.iba, XQ_RDX, 32, 0), REG_FIT }, + { GRDATA_GBL ( ICR, xqb.icr, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( IPEND, xqb.pending_interrupt, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA_GBL ( TBINDX, xqb.tbindx, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( RBINDX, xqb.rbindx, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( IDTMR, xqb.idtmr, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( VECTOR, xqb_dib.vec, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( MUST_POLL, xqb.must_poll, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SANT_ENAB, xqb.sanity.enabled, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SANT_QSECS, xqb.sanity.quarter_secs, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA_GBL ( SANT_TIMR, xqb.sanity.timer, XQ_RDX, 32, 0), REG_HRO}, + { NULL }, +}; + +MTAB xq_mod[] = { + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL, + NULL, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx", + &xq_setmac, &xq_showmac, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "ETH", "ETH", + NULL, ð_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "FILTERS", "FILTERS", + 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|DELQA-T}", + &xq_set_type, &xq_show_type, NULL }, +#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 }, +}; + +DEBTAB xq_debug[] = { + {"TRACE", DBG_TRC}, + {"CSR", DBG_CSR}, + {"VAR", DBG_VAR}, + {"WARN", DBG_WRN}, + {"SETUP", DBG_SET}, + {"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, + 2, XQ_RDX, 11, 1, XQ_RDX, 16, + &xq_ex, &xq_dep, &xq_reset, + NULL, &xq_attach, &xq_detach, + &xqa_dib, /*DEV_FLTA |*/ DEV_DISABLE | DEV_QBUS | DEV_DEBUG, + 0, xq_debug +}; + +DEVICE xqb_dev = { + "XQB", xqb_unit, xqb_reg, xq_mod, + 2, XQ_RDX, 11, 1, XQ_RDX, 16, + &xq_ex, &xq_dep, &xq_reset, + NULL, &xq_attach, &xq_detach, + &xqb_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_QBUS | DEV_DEBUG, + 0, xq_debug +}; + +AUTO_INIT_DEVLOCK(xqa_lock); +AUTO_INIT_DEVLOCK(xqb_lock); +static smp_interlocked_uint32_var xq_pending_intrs = smp_var_init(0); /* active interrupt count */ + +CTLR xq_ctrl[] = { + {&xq_dev, xqa_unit, &xqa_dib, &xqa, & xqa_lock}, /* XQA controller */ + {&xqb_dev, xqb_unit, &xqb_dib, &xqb, & xqb_lock} /* XQB controller */ +}; + +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[] = { + "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[] = { + "RE", "SR", "NI", "BD", "XL", "RL", "IE", "XI", + "IL", "EL", "SE", "RR", "OK", "CA", "PE", "RI" +}; + +const char* const xq_var_bits[] = { + "ID", "RR", "V0", "V1", "V2", "V3", "V4", "V5", + "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); + +static void init_xq_data() +{ + smp_check_aligned(& xq_pending_intrs); +} +ON_INIT_INVOKE(init_xq_data); + +/*============================================================================*/ + +/* Multicontroller support */ + +CTLR* xq_unit2ctlr(UNIT* uptr) +{ + unsigned int i,j; + for (i=0; inumunits; j++) + if (xq_ctrl[i].unit[j] == uptr) + return &xq_ctrl[i]; + /* not found */ + return 0; +} + +CTLR* xq_dev2ctlr(DEVICE* dptr) +{ + int i; + for (i=0; i= xq_ctrl[i].dib->ba) && (PA < (xq_ctrl[i].dib->ba + xq_ctrl[i].dib->lnt))) + return &xq_ctrl[i]; + /* not found */ + return 0; +} + +/*============================================================================*/ + +/* stop simh from reading non-existant unit data stream */ +t_stat xq_ex (t_value* vptr, t_addr addr, UNIT* uptr, int32 sw) +{ + /* on PDP-11, allow EX command to look at bootrom */ +#ifdef VM_PDP11 + if (addr <= sizeof(xq_bootrom)/2) + *vptr = xq_bootrom[addr]; + else + *vptr = 0; + return SCPE_OK; +#else + return SCPE_NOFNC; +#endif +} + +/* stop simh from writing non-existant unit data stream */ +t_stat xq_dep (t_value val, t_addr addr, UNIT* uptr, int32 sw) +{ + return SCPE_NOFNC; +} + +t_stat xq_showmac (SMP_FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + char buffer[20]; + + eth_mac_fmt((ETH_MAC*)xq->var->mac, buffer); + fprintf(st, "MAC=%s", buffer); + return SCPE_OK; +} + +void xq_make_checksum(CTLR* xq) +{ + /* checksum calculation routine detailed in vaxboot.zip/xqbtdrivr.mar */ + uint32 checksum = 0; + const uint32 wmask = 0xFFFF; + int i; + + for (i = 0; i < (int) sizeof(ETH_MAC); i += 2) { + checksum <<= 1; + if (checksum > wmask) + checksum -= wmask; + checksum += (xq->var->mac[i] << 8) | xq->var->mac[i+1]; + if (checksum > wmask) + checksum -= wmask; + } + if (checksum == wmask) + checksum = 0; + + /* set checksum bytes */ + xq->var->mac_checksum[0] = checksum & 0xFF; + xq->var->mac_checksum[1] = checksum >> 8; +} + +t_stat xq_setmac (UNIT* uptr, int32 val, char* cptr, void* desc) +{ + t_stat status; + CTLR* xq = xq_unit2ctlr(uptr); + + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + status = eth_mac_scan(&xq->var->mac, cptr); + if (status != SCPE_OK) + return status; + + /* calculate mac checksum */ + xq_make_checksum(xq); + return SCPE_OK; +} + +t_stat xq_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc) +{ + /* this sets all ints in the stats structure to the integer passed */ + CTLR* xq = xq_unit2ctlr(uptr); + + if (cptr) { + /* set individual stats to passed parameter value */ + int init = atoi(cptr); + int* stat_array = (int*) &xq->var->stats; + int elements = sizeof(struct xq_stats)/sizeof(int); + int i; + for (i=0; ivar->stats, 0, sizeof(struct xq_stats)); + } + return SCPE_OK; +} + +t_stat xq_show_stats (SMP_FILE* st, UNIT* uptr, int32 val, void* desc) +{ + char* fmt = " %-15s%d\n"; + CTLR* xq = xq_unit2ctlr(uptr); + + 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; +} + +t_stat xq_show_filters (SMP_FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + char buffer[20]; + int i; + + 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; i < (int) sizeof(xq->var->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; +} + +t_stat xq_show_type (SMP_FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + fprintf(st, "type="); + 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; +} + +t_stat xq_set_type (UNIT* uptr, int32 val, char* cptr, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + + /* 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; +#if defined(VM_VAX_MP) + if (xq->var->type == XQ_T_DELQA_PLUS) + { + smp_printf("Warning!!! DELQA-T is not supported by VAX MP in multiprocessor mode.\n"); + if (sim_log) + fprintf(sim_log, "Warning!!! DELQA-T is not supported by VAX MP in multiprocessor mode.\n"); + } +#endif + xq->var->mode = XQ_T_DELQA; + if (xq->var->type == XQ_T_DEQNA) + xq->var->mode = XQ_T_DEQNA; + + return SCPE_OK; +} + +t_stat xq_show_poll (SMP_FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + 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; +} + +t_stat xq_set_poll (UNIT* uptr, int32 val, char* cptr, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + + /* 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 = (atomic_var(tmr_poll) * clk_tps * xq->var->coalesce_latency) / 1000000; + } + } + else { + int newpoll = 0; + 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; + } + + return SCPE_OK; +} + +t_stat xq_show_sanity (SMP_FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + + fprintf(st, "sanity="); + switch (xq->var->sanity.enabled) { + case 2: fprintf(st, "ON\n"); break; + default: fprintf(st, "OFF\n"); break; + } + return SCPE_OK; +} + +t_stat xq_set_sanity (UNIT* uptr, int32 val, char* cptr, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + + /* this assumes that the parameter has already been upcased */ + if (!strcmp(cptr, "ON")) xq->var->sanity.enabled = 2; + else if (!strcmp(cptr, "OFF")) xq->var->sanity.enabled = 0; + else return SCPE_ARG; + + return SCPE_OK; +} + +/*============================================================================*/ + +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"); + + 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; +} + +/* +** write callback +*/ +void xq_write_callback (CTLR* xq, int status) +{ + RUN_SCOPE; + int32 wstatus; + const uint16 TDR = 100 + xq->var->write_buffer.len * 8; /* arbitrary value */ + uint16 write_success[2] = {0}; + uint16 write_failure[2] = {XQ_DSC_C}; + write_success[1] = TDR & 0x03FF; /* Does TDR get set on successful packets ?? */ + write_failure[1] = TDR & 0x03FF; /* TSW2<09:00> */ + + xq->var->stats.xmit += 1; + + /* + * Multiprocessor note: + * + * Abstractly speaking, we might have put smp_mb here to ensure that update + * of status words is not reordered before reading transmit buffer on a multiprocessor + * system. However multiple system calls performed after buffer reading (including + * lock acquisition by xqa_write_callback and xqb_write_callback just before calling + * xq_write_callback) already do provide this assurance, so exlicit MB here would have + * been redundant. + * + * Also, xq_update_bdl_status_words does perform memory barrier internally before + * writing status word 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 = xq_update_bdl_status_words(RUN_PASS, xq->var->xbdl_ba, write_success); + } else { /* failure */ + sim_debug(DBG_WRN, xq->dev, "Packet Write Error!\n"); + xq->var->stats.fail += 1; + wstatus = xq_update_bdl_status_words(RUN_PASS, xq->var->xbdl_ba, write_failure); + } + if (wstatus) { + xq_nxm_error(xq); + return; + } + + /* update csr */ + xq_csr_set_clr(xq, XQ_CSR_XI, 0); + + /* reset sanity timer */ + xq_reset_santmr(xq); + + /* clear write buffer */ + xq->var->write_buffer.len = 0; + +} + +void xqa_write_callback (int status) +{ + CTLR* xq = &xq_ctrl[0]; + AUTO_LOCK_NM(xq_autolock, *xq->xq_lock); + xq_write_callback(xq, status); +} + +void xqb_write_callback (int status) +{ + CTLR* xq = &xq_ctrl[1]; + AUTO_LOCK_NM(xq_autolock, *xq->xq_lock); + xq_write_callback(xq, status); +} + + +/* + * Multiprocessor note: + * + * Standard VMS XQDRIVER relies on VAX strong memory model where CPU sees XQ writes in the order + * XQ executed them, and vice versa. This does not hold true when simulated on the host machine + * with weaker memory consistency model, unless explicit memory barriers are used by both VMS + * XQDRIVER and SIMH XQ handler, and are paired up. + * + ********************************************************************************************* + * + * Let's first consider the case of updates initiated by XQ and flowing to XQDRIVER. + * These are signalled by XQ at the end of processing BDL entry by XQ, by XQ changing flags in + * BD status word 1. + * + * XQDRIVER on it side contains code that directly checks buffer descriptors in the BDL + * for their completion/availability, by first checking flags in status word 1 (at BDL desc + 8), + * then content of status word 2 (at BDL + 10) and any other data. + * + * This does not present a problem if XQ fully processes BDL, sends an interrupt and driver scans + * BDL in response to this interrupt, as interrupt signalling/delivery sequence peforms required + * memory barriers. + * + * However it does present a problem if XQDRIVER scans BDL in response to an interrupt raised + * after partial BDL processing by XQ, after timeout or on new IO request. + * + * Solution is provided by (1) supplying memory barriers in XQ handler, and (2) modifying active + * XQDRIVER code with VSMP tool to inject appropriate memory barriers into the driver's code. + * + * General approach is that "check ST1" in the driver code is replaced with sequence + * "read ST1, RMB, dispatch on flags fetched from ST1". + * In particular sequences + * "check ST1, check ST2" + * are replaced with + * "check ST1, RMB, check ST2". + * RMB is implemented with BBSSI instruction. + * + * On XQ handler side, general sequence is: + * + * perform buffer access + * optionally MB + * set all necessary BD words except ST1 + * MB or WMB + * set ST1 + * + * Do note that for multiprocessor case we reverse here, in routine xq_update_bdl_status_words, + * writing order for BDL status words compared to what real DEQNA/DELQA does. Instead of writing + * ST1, then ST2, we reverse the order. We first write status word 2, perform WMB and then write + * status word 1. This alleviates race condition for the driver described in XQDRIVER's routine NEXTMSG + * and makes changes to the driver code more manageable and providing stable result on weak-memory-model + * host. + * + * For XQDRIVER source module that interacts with DEQNA and DELQA, refer to [PHV_LAN.SRC]DEQNA.MAR + * or corresponding LIS file in OpenVMS source listings. + * + * Call graph for XQDRIVER routines: + * + * (qio) -> IOREQ -> INIT_DEQNA + * (qio) -> IOREQ -> QNA_XMIT + * (qio) -> IOREQ -> SUB_SETUP_MODE -> QNA_XMIT + * (qio) -> IOREQ -> SUB_SETUP_MODE -> QNA_START_RECEIVE + * (TQE entry) -> CTRL_TIMER_EXP -> SYSID_TIMER_EXP -> QNA_XMIT -> SETUP_XMTDSC_UV2 + * (TQE entry) -> CTRL_TIMER_EXP -> QNA_START_RECEIVE + * (interrupt, timeout, IOREQ for some requests) -> fork -> RCV_COMPLETE, XMT_COMPLETE + * RCV_COMPLETE -> NEXTMSG, QNA_XMIT, QNA_START_RECEIVE + * XMT_COMPLETE -> UNMAP_XMTBUF, QNA_XMIT + * + * Note that SETUP_XMTDSC_UV2 applies also to any processor (including KA650) other than MicroVAX I. + * + * Scanning of Rx BDL is handled in: + * + * routine NEXTMSG (around label 220$) - fixed by patch XQRX1 + * routine QNA_XMIT (below label 20$ located below XMIT.ALLVAX) - no need to fix this one, + * as existing code already causes MBs + * + * Scanning of Tx BDL is handled in: + * + * routine NEXTMSG (at the beginng) - fixed by patch XQTX1 + * (around label 111$) - fixed by patch XQTX2 + * (below label 2$) - fixed by patch XQTX3 + * (below label 8$) - fixed by patch XQTX4 + * + ********************************************************************************************* + * + * Now let's consider the case of BDL updates flowing the other direction: initiated by XQDRIVER + * and noticed asynchronously by XQ. + * + * These are signalled by XQDRIVER to XQ by setting "Valid" bit in BDL entry. + * + * Therefore proper sequence on the driver side should be: + * + * setup buffer and descriptor + * WMB + * set "Valid" bit + * optionally write CSR + * + * On XQ side, it should be: detect "Valid" bit, RMB, access buffer and descriptor. + * + * Finally, after completion of request, XQDRIVER clears "Valid" bit and should execute WMB + * at this point (after clearing the bit, before starting to modify descriptor fields). + * + * Valid bit setting in Rx descriptor is handled in: + * + * routine QNA_START_RECEIVE (below QNA_START_RECEIVE.COMMON) - fixed by patch XQRX3 + * + * Valid bit clearing in Rx descriptor is handled in: + * + * routine NEXTMSG (below label 220$) - fixed by patch XQRX4 + * routine INIT_DEQNA (below label 20$) - fixed by patch XQRX2 + * + * Valid bit setting in Tx descriptor is handled in: + * + * routine QNA_XMIT (above label XMIT.UV1) - fixed by patch XQTX6 + * routine QNA_XMIT (below label 20$ located below XMIT.ALLVAX) - fixed by patch XQTX10 + * + * Valid bit clearing in Tx descriptor is handled in: + * + * routine UNNAP_XMTBUF (below label 5050$, two times) - fixed by patches XQTX7, XQTX8 + * routine INIT_DEQNA (below label 30$) - fixed by patch XQTX5 + * + * in addition, we put memory barrier for clearning SETUP bit in Tx descriptor + * in routine XMT_COMPLETE (below label 20$) - by patch XQTX9, this is almost surely + * unnecessary, but just to be on the safe side + * + */ +static int32 xq_update_bdl_status_words(RUN_DECL, uint32 bdl_ba, uint16* stw) +{ + int32 wstatus; +#if defined(VM_VAX_MP) + /* write status word 2 */ + wstatus = Map_WriteW(RUN_PASS, bdl_ba + 10, 2, stw + 1); + if (wstatus) return wstatus; + + /* perform full memory barrier (rather than just wmb) so we could also eliminate (comment out) + smp_mb during xq_process_xbdl, and so avoid the overhead of double barriers */ + smp_mb(); + + /* write status word 1 */ + wstatus = Map_WriteW(RUN_PASS, bdl_ba + 8, 2, stw); +#else + /* write status words 1 and 2 */ + wstatus = Map_WriteW(RUN_PASS, bdl_ba + 8, 4, stw); +#endif + return wstatus; +} + +/* retrieve BDL from memory */ +static int32 xq_fetch_bdl_entry(RUN_DECL, uint32 bdl_ba, uint16* buf, int32 bcnt) +{ + int32 rwstatus; + + buf[0] = 0xFFFF; + rwstatus = Map_WriteW(RUN_PASS, bdl_ba, 2, &buf[0]); + if (rwstatus) return rwstatus; +#if defined(VM_VAX_MP) + rwstatus = Map_ReadW(RUN_PASS, bdl_ba + 2, 2, &buf[1]); + if (rwstatus) return rwstatus; + /* perform RMB after reading descriptor flags word, including Valid bit */ + smp_rmb(); + if (bcnt > 2) + rwstatus = Map_ReadW(RUN_PASS, bdl_ba + 4, bcnt - 2, &buf[2]); +#else + rwstatus = Map_ReadW(RUN_PASS, bdl_ba + 2, bcnt, &buf[1]); +#endif + return rwstatus; +} + +/* read registers: */ +t_stat xq_rd(int32* data, int32 PA, int32 access) +{ + CTLR* xq = xq_pa2ctlr(PA); + AUTO_LOCK_NM(xq_autolock, *xq->xq_lock); + int index = (PA >> 1) & 07; /* word index */ + + 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: + /* return checksum in external loopback mode */ + if (xq->var->csr & XQ_CSR_EL) + *data = 0xFF00 | xq->var->mac_checksum[index]; + else + *data = 0xFF00 | xq->var->mac[index]; + break; + case 2: + case 3: + case 4: + case 5: + *data = 0xFF00 | xq->var->mac[index]; + break; + case 6: + 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); + *data = xq->var->csr; + break; + } + return SCPE_OK; +} + + +/* dispatch ethernet read request + procedure documented in sec. 3.2.2 */ + +t_stat xq_process_rbdl(CTLR* xq) +{ + RUN_SCOPE; + int32 rstatus, wstatus, rwstatus; + uint16 b_length, w_length, rbl; + uint32 address; + 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 */ + while(1) { + + /* get receive bdl from memory */ + rwstatus = xq_fetch_bdl_entry(RUN_PASS, xq->var->rbdl_ba, xq->var->rbdl_buf, 6); + if (rwstatus) return xq_nxm_error(xq); + + /* invalid buffer? */ + if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { + xq_csr_set_clr(xq, XQ_CSR_RL, 0); + return SCPE_OK; + } + + /* explicit chain buffer? */ + if (xq->var->rbdl_buf[1] & XQ_DSC_C) { + xq->var->rbdl_ba = ((xq->var->rbdl_buf[1] & 0x3F) << 16) | xq->var->rbdl_buf[2]; + continue; + } + + /* stop processing if nothing in read queue */ + if (!xq->var->ReadQ.count) break; + + /* get status words */ + rstatus = Map_ReadW(RUN_PASS, xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (rstatus) return xq_nxm_error(xq); + + /* get host memory address */ + address = ((xq->var->rbdl_buf[1] & 0x3F) << 16) | xq->var->rbdl_buf[2]; + + /* decode buffer length - two's complement (in words) */ + w_length = ~xq->var->rbdl_buf[3] + 1; + b_length = w_length * 2; + if (xq->var->rbdl_buf[1] & XQ_DSC_H) b_length -= 1; + if (xq->var->rbdl_buf[1] & XQ_DSC_L) b_length -= 1; + + item = &xq->var->ReadQ.item[xq->var->ReadQ.head]; + rbl = item->packet.len; + 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_MAX_PACKET) { + 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_MAX_PACKET; + }; + }; + + /* 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 */ + wstatus = Map_WriteB(RUN_PASS, address, rbl, rbuf); + if (wstatus) 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->rbdl_buf[4] = 0; + switch (item->type) { + case 0: /* setup packet */ + xq->var->stats.setup += 1; + xq->var->rbdl_buf[4] = 0x2700; /* set esetup and RBL 10:8 */ + break; + case 1: /* loopback packet */ + xq->var->stats.loop += 1; + xq->var->rbdl_buf[4] = 0x2000; /* loopback flag */ + xq->var->rbdl_buf[4] |= (rbl & 0x0700); /* high bits of rbl */ + break; + case 2: /* normal packet */ + rbl -= 60; /* keeps max packet size in 11 bits */ + xq->var->rbdl_buf[4] = (rbl & 0x0700); /* high bits of rbl */ + break; + } + if (item->packet.used < item->packet.len) + xq->var->rbdl_buf[4] |= 0xC000; /* not last segment */ + xq->var->rbdl_buf[5] = ((rbl & 0x00FF) << 8) | (rbl & 0x00FF); + 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 */ + } + + /* + * Ensure update of RX BDL status words is not reordered before sending the data to user. + * We do not perform it as driver should check status word 1 flags first, and we do perform + * memory barrier before writing it in xq_update_bdl_status_words. + */ + // smp_wmb(); + + /* update read status words*/ + wstatus = xq_update_bdl_status_words(RUN_PASS, xq->var->rbdl_ba, &xq->var->rbdl_buf[4]); + if (wstatus) return xq_nxm_error(xq); + + /* remove packet from queue */ + if (item->packet.used >= item->packet.len) + ethq_remove(&xq->var->ReadQ); + + /* mark transmission complete */ + xq_csr_set_clr(xq, XQ_CSR_RI, 0); + + /* set to next bdl (implicit chain) */ + xq->var->rbdl_ba += 12; + + } /* while */ + + return SCPE_OK; +} + +t_stat xq_process_mop(CTLR* xq) +{ + RUN_SCOPE; + uint32 address; + uint16 size; + int32 wstatus; + struct xq_meb* meb = (struct xq_meb*) &xq->var->write_buffer.msg[0200]; + const struct xq_meb* limit = (struct xq_meb*) &xq->var->write_buffer.msg[0400]; + + sim_debug(DBG_TRC, xq->dev, "xq_process_mop()\n"); + + if (xq->var->type == XQ_T_DEQNA) /* DEQNA's don't MOP */ + return SCPE_NOFNC; + + while ((meb->type != 0) && (meb < limit)) { + address = (meb->add_hi << 16) || (meb->add_mi << 8) || meb->add_lo; + size = (meb->siz_hi << 8) || meb->siz_lo; + + /* MOP stuff here - NOT YET FULLY IMPLEMENTED */ + sim_debug (DBG_WRN, xq->dev, "Processing MEB type: %d\n", meb->type); + switch (meb->type) { + case 0: /* MOP Termination */ + break; + case 1: /* MOP Read Ethernet Address */ + wstatus = Map_WriteB(RUN_PASS, address, sizeof(ETH_MAC), (uint8*) &xq->var->setup.macs[0]); + if (wstatus) return xq_nxm_error(xq); + break; + case 2: /* MOP Reset System ID */ + break; + case 3: /* MOP Read Last MOP Boot */ + break; + case 4: /* MOP Read Boot Password */ + break; + case 5: /* MOP Write Boot Password */ + break; + case 6: /* MOP Read System ID */ + break; + case 7: /* MOP Write System ID */ + break; + case 8: /* MOP Read Counters */ + 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(RUN_PASS, address, sizeof(Delqa_Plus_ROM_Version), (uint8*) Delqa_Plus_ROM_Version); + if (wstatus) return xq_nxm_error(xq); + } + break; + } /* switch */ + + /* process next meb */ + meb += sizeof(struct xq_meb); + + } /* while */ + return SCPE_OK; +} + +t_stat xq_process_setup(CTLR* xq) +{ + int i,j; + 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++) + for (j = 0; j < 6; j++) { + xq->var->setup.macs[i] [j] = xq->var->write_buffer.msg[(i + 01) + (j * 8)]; + if (xq->var->write_buffer.len > 112) + xq->var->setup.macs[i+7][j] = xq->var->write_buffer.msg[(i + 0101) + (j * 8)]; + } + + /* + Under VMS the setup packet that is passed to turn promiscuous + off after it has been on doesn't seem to follow the rules documented + in both the DEQNA and DELQA manuals. + These rules seem to say that setup packets less than 128 should only + modify the address filter set and probably not the All-Multicast and + Promiscuous modes, however, VMS V5-5 and V7.3 seem to send a 127 byte + packet to turn this functionality off. I'm not sure how real hardware + behaves in this case, since the only consequence is extra interrupt + load. To realize and retain the benefits of the newly added BPF + functionality in sim_ether, I've modified the logic implemented here + to disable Promiscuous mode when a "small" setup packet is processed. + I'm deliberately not modifying the All-Multicast mode the same way + since I don't have an observable case of its behavior. These two + different modes come from very different usage situations: + 1) Promiscuous mode is usually entered for relatively short periods + of time due to the needs of a specific application program which + is doing some sort of management/monitoring function (i.e. tcpdump) + 2) All-Multicast mode is only entered by the OS Kernel Port Driver + when it happens to have clients (usually network stacks or service + programs) which as a group need to listen to more multicast ethernet + addresses than the 12 (or so) which the hardware supports directly. + so, I believe that the All-Multicast mode, is first rarely used, and if + it ever is used, once set, it will probably be set either forever or for + long periods of time, and the additional interrupt processing load to + deal with the distinctly lower multicast traffic set is clearly lower than + that of the promiscuous mode. + */ + xq->var->setup.promiscuous = 0; + /* process high byte count */ + if (xq->var->write_buffer.len > 128) { + uint16 len = xq->var->write_buffer.len; + uint16 led, san; + + xq->var->setup.multicast = (0 != (len & XQ_SETUP_MC)); + xq->var->setup.promiscuous = (0 != (len & XQ_SETUP_PM)); + if (led = (len & XQ_SETUP_LD) >> 2) { + switch (led) { + case 1: xq->var->setup.l1 = 0; break; + case 2: xq->var->setup.l2 = 0; break; + case 3: xq->var->setup.l3 = 0; break; + } /* switch */ + } /* if led */ + + /* set sanity timer timeout */ + san = (len & XQ_SETUP_ST) >> 4; + switch(san) { + case 0: secs = 0.25; break; /* 1/4 second */ + case 1: secs = 1; break; /* 1 second */ + case 2: secs = 4; break; /* 4 seconds */ + case 3: secs = 16; break; /* 16 seconds */ + case 4: secs = 1 * 60; break; /* 1 minute */ + case 5: secs = 4 * 60; break; /* 4 minutes */ + case 6: secs = 16 * 60; break; /* 16 minutes */ + case 7: secs = 64 * 60; break; /* 64 minutes */ + } + xq->var->sanity.quarter_secs = (int) (secs * 4); + } + + /* finalize sanity timer state */ + 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)); */ + for (i = 0; i < XQ_FILTER_MAX; i++) + if (memcmp(zeros, &xq->var->setup.macs[i], sizeof(ETH_MAC))) + memcpy (filters[count++], xq->var->setup.macs[i], sizeof(ETH_MAC)); + status = eth_filter (xq->var->etherface, count, filters, xq->var->setup.multicast, xq->var->setup.promiscuous); + + /* process MOP information */ + if (xq->var->write_buffer.msg[0]) + status = xq_process_mop(xq); + + /* mark setup block valid */ + xq->var->setup.valid = 1; + + xq_debug_setup(xq); + + xq->dev->dctrl = saved_debug; /* restore original debugging */ + + return SCPE_OK; +} + +/* + Dispatch Write Operation + + 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) +{ + RUN_SCOPE; + const uint16 implicit_chain_status[2] = {XQ_DSC_V | XQ_DSC_C, 1}; + const uint16 write_success[2] = {0, 1 /*Non-Zero TDR*/}; + uint16 b_length, w_length; + int32 rstatus, wstatus, rwstatus; + uint32 address; + t_stat status; + + sim_debug(DBG_TRC, xq->dev, "xq_process_xbdl()\n"); + + /* clear write buffer */ + xq->var->write_buffer.len = 0; + + /* process buffer descriptors until not valid */ + while (1) { + + /* Get transmit bdl from memory */ + rwstatus = xq_fetch_bdl_entry(RUN_PASS, xq->var->xbdl_ba, xq->var->xbdl_buf, 10); + if (rwstatus) return xq_nxm_error(xq); + + /* invalid buffer? */ + if (~xq->var->xbdl_buf[1] & XQ_DSC_V) { + xq_csr_set_clr(xq, XQ_CSR_XL, 0); + sim_debug(DBG_WRN, xq->dev, "XBDL List empty\n"); + return SCPE_OK; + } + + /* compute host memory address */ + address = ((xq->var->xbdl_buf[1] & 0x3F) << 16) | xq->var->xbdl_buf[2]; + + /* decode buffer length - two's complement (in words) */ + w_length = ~xq->var->xbdl_buf[3] + 1; + b_length = w_length * 2; + if (xq->var->xbdl_buf[1] & XQ_DSC_H) b_length -= 1; + if (xq->var->xbdl_buf[1] & XQ_DSC_L) b_length -= 1; + + /* explicit chain buffer? */ + if (xq->var->xbdl_buf[1] & XQ_DSC_C) { + xq->var->xbdl_ba = address; + sim_debug(DBG_WRN, xq->dev, "XBDL chained buffer encountered: %d\n", b_length); + continue; + } + + /* add to transmit buffer, making sure it's not too big */ + if (xq->var->write_buffer.len + b_length > (int) sizeof(xq->var->write_buffer.msg)) + b_length = (uint16) (sizeof(xq->var->write_buffer.msg) - xq->var->write_buffer.len); + rstatus = Map_ReadB(RUN_PASS, 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; + + /* end of message? */ + if (xq->var->xbdl_buf[1] & XQ_DSC_E) { + if (((~xq->var->csr & XQ_CSR_RE) && ((~xq->var->csr & XQ_CSR_IL) || (xq->var->csr & XQ_CSR_EL))) || /* loopback */ + (xq->var->xbdl_buf[1] & XQ_DSC_S)) { /* or setup packet (forces loopback regardless of state) */ + if (xq->var->xbdl_buf[1] & XQ_DSC_S) { /* setup packet */ + status = xq_process_setup(xq); + + /* put packet in read buffer */ + ethq_insert (&xq->var->ReadQ, 0, &xq->var->write_buffer, status); + } else { /* loopback */ + /* put packet in read buffer */ + ethq_insert (&xq->var->ReadQ, 1, &xq->var->write_buffer, 0); + } + + /* + * Ensure update of TX BDL status words is not reordered before reading tx ring and buffer. + * We do not perform it as driver should check status word 1 flags first, and we do perform + * memory barrier before writing it in xq_update_bdl_status_words. + */ + // smp_mb(); + + /* update write status */ + wstatus = xq_update_bdl_status_words(RUN_PASS, xq->var->xbdl_ba, (uint16*) write_success); + if (wstatus) return xq_nxm_error(xq); + + /* clear write buffer */ + xq->var->write_buffer.len = 0; + + /* reset sanity timer */ + xq_reset_santmr(xq); + + /* mark transmission complete */ + xq_csr_set_clr(xq, XQ_CSR_XI, 0); + + /* now trigger "read" of setup or loopback packet */ + if (~xq->var->csr & XQ_CSR_RL) + status = xq_process_rbdl(xq); + + } else { /* not loopback */ + + 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 */ + else { + if (xq->var->coalesce_latency == 0) + xq_svc_ex(RUN_PASS, xq->unit[0], xq); /* service any received data */ + } + sim_debug(DBG_WRN, xq->dev, "XBDL completed processing write\n"); + + } /* loopback/non-loopback */ + + } else { /* not at end-of-message */ + + sim_debug(DBG_WRN, xq->dev, "XBDL processing implicit chain buffer segment\n"); + + /* + * Ensure update of TX BDL status words is not reordered before reading tx ring and buffer. + * We do not perform it as driver should check status word 1 flags first, and we do perform + * memory barrier before writing it in xq_update_bdl_status_words. + */ + // smp_mb(); + + /* update bdl status words */ + wstatus = xq_update_bdl_status_words(RUN_PASS, xq->var->xbdl_ba, (uint16*) implicit_chain_status); + if(wstatus) return xq_nxm_error(xq); + } + + /* set to next bdl (implicit chain) */ + xq->var->xbdl_ba += 12; + + } /* while */ +} + +t_stat xq_dispatch_rbdl(CTLR* xq) +{ + RUN_SCOPE; + int i; + int32 rwstatus; + t_stat status; + + sim_debug(DBG_TRC, xq->dev, "xq_dispatch_rbdl()\n"); + + /* mark receive bdl valid */ + xq_csr_set_clr(xq, 0, XQ_CSR_RL); + + /* init receive bdl buffer */ + for (i=0; i<6; i++) + xq->var->rbdl_buf[i] = 0; + + /* get address of first receive buffer */ + xq->var->rbdl_ba = ((xq->var->rbdl[1] & 0x3F) << 16) | (xq->var->rbdl[0] & ~01); + + /* get first receive buffer */ + rwstatus = xq_fetch_bdl_entry(RUN_PASS, xq->var->rbdl_ba, xq->var->rbdl_buf, 6); + if (rwstatus) return xq_nxm_error(xq); + + /* is buffer valid? */ + if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { + xq_csr_set_clr(xq, XQ_CSR_RL, 0); + return SCPE_OK; + } + + /* process any waiting packets in receive queue */ + if (xq->var->ReadQ.count) + status = xq_process_rbdl(xq); + + return SCPE_OK; +} + +t_stat xq_dispatch_xbdl(CTLR* xq) +{ + int i; + t_stat status; + + sim_debug(DBG_TRC, xq->dev, "xq_dispatch_xbdl()\n"); + + /* mark transmit bdl valid */ + xq_csr_set_clr(xq, 0, XQ_CSR_XL); + + /* initialize transmit bdl buffers */ + for (i=0; i<6; i++) + xq->var->xbdl_buf[i] = 0; + + /* clear transmit buffer */ + xq->var->write_buffer.len = 0; + + /* get base address of first transmit descriptor */ + xq->var->xbdl_ba = ((xq->var->xbdl[1] & 0x3F) << 16) | (xq->var->xbdl[0] & ~01); + + /* process xbdl */ + status = xq_process_xbdl(xq); + + return status; +} + +t_stat xq_process_turbo_rbdl(CTLR* xq) +{ + RUN_SCOPE; + 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 (RUN_PASS, 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(RUN_PASS, 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 (RUN_PASS, 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 (RUN_PASS, 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) +{ + RUN_SCOPE; + 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 (RUN_PASS, 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 > (int) sizeof(xq->var->write_buffer.msg)) + b_length = (uint16)(sizeof(xq->var->write_buffer.msg) - xq->var->write_buffer.len); + status = Map_ReadB(RUN_PASS, 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 (RUN_PASS, 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 (RUN_PASS, 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_ex (RUN_PASS, xq->unit[0], xq); /* 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 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, xq->dev, "xq_process_loopback()\n"); + + if (function != 2 /*forward*/) + return SCPE_NOFNC; + + /* 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; + + /* The only packets we should be responding to are ones which + we received due to them being directed to our physical MAC address, + OR the Broadcast address OR to a Multicast address we're listening to + (we may receive others if we're in promiscuous mode, but shouldn't + respond to them) */ + if ((0 == (pack->msg[0]&1)) && /* Multicast or Broadcast */ + (0 != memcmp(physical_address, pack->msg, sizeof(ETH_MAC)))) + return SCPE_NOFNC; + + 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 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; +} + +t_stat xq_process_remote_console (CTLR* xq, ETH_PACK* pack) +{ + t_stat status; + ETH_MAC source; + uint16 receipt; + int code = pack->msg[16]; + + sim_debug(DBG_TRC, xq->dev, "xq_process_remote_console()\n"); + + switch (code) { + case 0x05: /* request id */ + receipt = pack->msg[18] | (pack->msg[19] << 8); + memcpy(source, &pack->msg[6], sizeof(ETH_MAC)); + + /* send system id to requestor */ + status = xq_system_id (xq, source, receipt); + return status; + break; + case 0x06: /* boot */ + /* + NOTE: the verification field should be checked here against the + verification value established in the setup packet. If they match the + reboot should occur, otherwise nothing happens, and the packet + is passed on to the host. + + Verification is not implemented, since the setup packet processing code + isn't complete yet. + + Various values are also passed: processor, control, and software id. + These control the various boot parameters, however SIMH does not + have a mechanism to pass these to the host, so just reboot. + */ + + status = xq_boot_host(xq); + return status; + break; + } /* switch */ + + return SCPE_NOFNC; +} + +t_stat xq_process_local (CTLR* xq, ETH_PACK* pack) +{ + /* returns SCPE_OK if local processing occurred, + otherwise returns SCPE_NOFNC or some other code */ + int protocol; + + sim_debug(DBG_TRC, xq->dev, "xq_process_local()\n"); + /* DEQNA's have no local processing capability */ + if (xq->var->type == XQ_T_DEQNA) + return SCPE_NOFNC; + + protocol = pack->msg[12] | (pack->msg[13] << 8); + switch (protocol) { + case 0x0090: /* ethernet loopback */ + return xq_process_loopback(xq, pack); + break; + case 0x0260: /* MOP remote console */ + return xq_process_remote_console(xq, pack); + break; + } + return SCPE_NOFNC; +} + +void xq_read_callback(CTLR* xq, int status) +{ + xq->var->stats.recv += 1; + + 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); + + /* add packet to read queue */ + 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"); + } +} + +void xqa_read_callback(int status) +{ + CTLR* xq = &xq_ctrl[0]; + AUTO_LOCK_NM(xq_autolock, *xq->xq_lock); + xq_read_callback(xq, status); +} + +void xqb_read_callback(int status) +{ + CTLR* xq = &xq_ctrl[1]; + AUTO_LOCK_NM(xq_autolock, *xq->xq_lock); + xq_read_callback(xq, status); +} + +void xq_sw_reset(CTLR* xq) +{ + const uint16 set_bits = XQ_CSR_XL | XQ_CSR_RL; + 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); + + if (xq->var->etherface) + xq_csr_set_clr(xq, XQ_CSR_OK, 0); + + /* clear interrupt unconditionally */ + xq_clrint(xq); + + /* flush read queue */ + ethq_clear(&xq->var->ReadQ); + + /* clear setup info */ + xq->var->setup.multicast = 0; + xq->var->setup.promiscuous = 0; + if (xq->var->etherface) { + int count = 0; + ETH_MAC zeros = {0, 0, 0, 0, 0, 0}; + ETH_MAC filters[XQ_FILTER_MAX + 1]; + + /* set ethernet filter */ + /* memcpy (filters[count++], xq->mac, sizeof(ETH_MAC)); */ + for (i = 0; i < XQ_FILTER_MAX; i++) + if (memcmp(zeros, &xq->var->setup.macs[i], sizeof(ETH_MAC))) + 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: */ + +t_stat xq_wr_var(CTLR* xq, int32 data) +{ + uint16 save_var = xq->var->var; + sim_debug(DBG_REG, xq->dev, "xq_wr_var(data= 0x%08X\n", data); + + switch (xq->var->type) { + case XQ_T_DEQNA: + 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) { + 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; + } + + /* set vector of SIMH device */ + if (data & XQ_VEC_IV) + { + io_change_vec(xq->dib, (data & XQ_VEC_IV) + VEC_Q); + } + else + { + io_change_vec(xq->dib, 0); + } + + sim_debug_u16(DBG_VAR, xq->dev, xq_var_bits, save_var, xq->var->var, 1); + + return SCPE_OK; +} + +#ifdef VM_PDP11 +t_stat xq_process_bootrom (CTLR* xq) +{ + /* + NOTE: BOOT ROMs are a PDP-11ism, since they contain PDP-11 binary code. + the host is responsible for creating two *2KB* receive buffers. + + RSTS/E v10.1 source (INIONE.MAR/XHLOOK:) indicates that both the DEQNA and + DELQA will set receive status word 1 bits 15 & 14 on both packets. It also + states that a hardware bug in the DEQNA will set receive status word 1 bit 15 + (only) in the *third* receive buffer (oops!). + + RSTS/E v10.1 will run the Citizenship test from the bootrom after loading it. + Documentation on the Boot ROM can be found in INIQNA.MAR. + */ + + int32 rstatus, wstatus; + uint16 b_length, w_length; + uint32 address; + uint8* bootrom = (uint8*) xq_bootrom; + int i, checksum; + + sim_debug(DBG_TRC, xq->dev, "xq_process_bootrom()\n"); + + /* + RSTS/E v10.1 invokes the Citizenship tests in the Bootrom. For some + reason, the current state of the XQ emulator cannot pass these. So, + to get moving on RSTE/E support, we will replace the following line in + INIQNA.MAR/CITQNA:: + 70$: MOV (R2),R0 ;get the status word + with + 70$: CLR R0 ;force success + to cause the Citizenship test to return success to RSTS/E. + + At some point, the real problem (failure to pass citizenship diagnostics) + does need to be corrected to find incompatibilities in the emulation, and to + ultimately allow it to pass Digital hardware diagnostic tests. + */ + for (i=0; ivar->rbdl_buf[0] = 0xFFFF; + wstatus = Map_WriteW(RUN_PASS, xq->var->rbdl_ba, 2, &xq->var->rbdl_buf[0]); + rstatus = Map_ReadW (RUN_PASS, xq->var->rbdl_ba + 2, 6, &xq->var->rbdl_buf[1]); + if (rstatus || wstatus) return xq_nxm_error(xq); + + /* invalid buffer? */ + if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { + xq_csr_set_clr(xq, XQ_CSR_RL, 0); + return SCPE_OK; + } + + /* get status words */ + rstatus = Map_ReadW(RUN_PASS, xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (rstatus) return xq_nxm_error(xq); + + /* get host memory address */ + address = ((xq->var->rbdl_buf[1] & 0x3F) << 16) | xq->var->rbdl_buf[2]; + + /* decode buffer length - two's complement (in words) */ + w_length = ~xq->var->rbdl_buf[3] + 1; + b_length = w_length * 2; + if (xq->var->rbdl_buf[1] & XQ_DSC_H) b_length -= 1; + if (xq->var->rbdl_buf[1] & XQ_DSC_L) b_length -= 1; + + /* make sure entire packet fits in buffer */ + assert(b_length >= sizeof(xq_bootrom)/2); + + /* send data to host */ + wstatus = Map_WriteB(RUN_PASS, address, sizeof(xq_bootrom)/2, bootrom); + if (wstatus) return xq_nxm_error(xq); + + /* update read status words */ + xq->var->rbdl_buf[4] = XQ_DSC_V | XQ_DSC_C; /* valid, chain */ + xq->var->rbdl_buf[5] = 0; + + /* update read status words*/ + wstatus = Map_WriteW(RUN_PASS, xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (wstatus) return xq_nxm_error(xq); + + /* set to next bdl (implicit chain) */ + xq->var->rbdl_ba += 12; + + /* --------------------------- bootrom part 2 -----------------------------*/ + + /* get receive bdl from memory */ + xq->var->rbdl_buf[0] = 0xFFFF; + wstatus = Map_WriteW(RUN_PASS, xq->var->rbdl_ba, 2, &xq->var->rbdl_buf[0]); + rstatus = Map_ReadW (RUN_PASS, xq->var->rbdl_ba + 2, 6, &xq->var->rbdl_buf[1]); + if (rstatus || wstatus) return xq_nxm_error(xq); + + /* invalid buffer? */ + if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { + xq_csr_set_clr(xq, XQ_CSR_RL, 0); + return SCPE_OK; + } + + /* get status words */ + rstatus = Map_ReadW(RUN_PASS, xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (rstatus) return xq_nxm_error(xq); + + /* get host memory address */ + address = ((xq->var->rbdl_buf[1] & 0x3F) << 16) | xq->var->rbdl_buf[2]; + + /* decode buffer length - two's complement (in words) */ + w_length = ~xq->var->rbdl_buf[3] + 1; + b_length = w_length * 2; + if (xq->var->rbdl_buf[1] & XQ_DSC_H) b_length -= 1; + if (xq->var->rbdl_buf[1] & XQ_DSC_L) b_length -= 1; + + /* make sure entire packet fits in buffer */ + assert(b_length >= sizeof(xq_bootrom)/2); + + /* send data to host */ + wstatus = Map_WriteB(RUN_PASS, address, sizeof(xq_bootrom)/2, &bootrom[2048]); + if (wstatus) return xq_nxm_error(xq); + + /* update read status words */ + xq->var->rbdl_buf[4] = XQ_DSC_V | XQ_DSC_C; /* valid, chain */ + xq->var->rbdl_buf[5] = 0; + + /* update read status words*/ + wstatus = Map_WriteW(RUN_PASS, xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (wstatus) return xq_nxm_error(xq); + + /* set to next bdl (implicit chain) */ + xq->var->rbdl_ba += 12; + + /* --------------------------- bootrom part 3 -----------------------------*/ + + switch (xq->var->type) { + case XQ_T_DEQNA: + + /* get receive bdl from memory */ + xq->var->rbdl_buf[0] = 0xFFFF; + wstatus = Map_WriteW(RUN_PASS, xq->var->rbdl_ba, 2, &xq->var->rbdl_buf[0]); + rstatus = Map_ReadW (RUN_PASS, xq->var->rbdl_ba + 2, 6, &xq->var->rbdl_buf[1]); + if (rstatus || wstatus) return xq_nxm_error(xq); + + /* invalid buffer? */ + if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { + xq_csr_set_clr(xq, XQ_CSR_RL, 0); + return SCPE_OK; + } + + /* get status words */ + rstatus = Map_ReadW(RUN_PASS, xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (rstatus) return xq_nxm_error(xq); + + /* update read status words */ + xq->var->rbdl_buf[4] = XQ_DSC_V; /* valid */ + xq->var->rbdl_buf[5] = 0; + + /* update read status words*/ + wstatus = Map_WriteW(RUN_PASS, xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (wstatus) return xq_nxm_error(xq); + + /* set to next bdl (implicit chain) */ + xq->var->rbdl_ba += 12; + break; + } /* switch */ + + /* --------------------------- Done, finish up -----------------------------*/ + + /* mark transmission complete */ + xq_csr_set_clr(xq, XQ_CSR_RI, 0); + + /* reset sanity timer */ + xq_reset_santmr(xq); + + return SCPE_OK; +} +#endif /* ifdef VM_PDP11 */ + +t_stat xq_wr_csr(CTLR* xq, int32 data) +{ + uint16 set_bits = data & XQ_CSR_RW; /* set RW set bits */ + uint16 clr_bits = ((data ^ XQ_CSR_RW) & XQ_CSR_RW) /* clear RW cleared bits */ + | (data & XQ_CSR_W1) /* write 1 to clear bits */ + | ((data & XQ_CSR_XI) ? XQ_CSR_NI : 0); /* clearing XI clears NI */ + + sim_debug(DBG_REG, xq->dev, "xq_wr_csr(data=0x%08X)\n", data); + + /* reset controller when SR transitions to cleared */ + if (xq->var->csr & XQ_CSR_SR & ~data) { + xq_sw_reset(xq); + return SCPE_OK; + } + + /* 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); + } + + /* update CSR bits */ + xq_csr_set_clr (xq, set_bits, clr_bits); + +#ifdef VM_PDP11 + /* request boot/diagnostic rom? [PDP-11 only] */ + if ((xq->var->csr & XQ_CSR_BP) == XQ_CSR_BP) /* all bits must be on */ + xq_process_bootrom(xq); +#endif + + 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) + { + xq_activate(xq->unit[0], TRUE, 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 + xq_activate(xq->unit[0], TRUE, 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) +{ + RUN_SCOPE; + 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 (RUN_PASS, 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); + AUTO_LOCK_NM(xq_autolock, *xq->xq_lock); + 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->var->mode == XQ_T_DELQA_PLUS) ? xqt_xmit_regnames[index] : xq_xmit_regnames[index]), access); + + 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; + 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[0]); /* 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; +} + + +/* reset device */ +t_stat xq_reset(DEVICE* dptr) +{ + t_stat status; + CTLR* xq = xq_dev2ctlr(dptr); + AUTO_LOCK_NM(xq_autolock, *xq->xq_lock); + const uint16 set_bits = XQ_CSR_RL | XQ_CSR_XL; + + sim_bind_devunits_lock(dptr, *xq->xq_lock); + + sim_debug(DBG_TRC, xq->dev, "xq_reset()\n"); + + /* calculate MAC checksum */ + xq_make_checksum(xq); + + /* init vector address register */ + 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; + } + io_change_vec(xq->dib, 0); + + /* init control status register */ + xq_csr_set_clr(xq, set_bits, (uint16) ~set_bits); + + /* clear interrupts unconditionally */ + xq_clrint(xq); + + /* init read queue (first time only) */ + status = ethq_init(&xq->var->ReadQ, XQ_QUE_MAX); + if (status != SCPE_OK) + return status; + + /* clear read queue */ + ethq_clear(&xq->var->ReadQ); + + /* 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 */ + xq_activate_abs(xq->unit[1], FALSE, 4); + + /* stop the receiver */ + eth_clr_async(xq->var->etherface); + } + + /* stop the receiver */ + sim_cancel(xq->unit[0]); + + /* set hardware sanity controls */ + if (xq->var->sanity.enabled) { + xq->var->sanity.quarter_secs = XQ_HW_SANITY_SECS * 4/*qsec*/; + } + + return auto_config (0, 0); /* run autoconfig */ +} + +void xq_reset_santmr(CTLR* xq) +{ + sim_debug(DBG_TRC, xq->dev, "xq_reset_santmr()\n"); + if (xq->var->sanity.enabled) { + 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.quarter_secs; + } +} + +t_stat xq_boot_host(CTLR* xq) +{ + sim_debug(DBG_TRC, xq->dev, "xq_boot_host()\n"); + /* + 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 return + a special STOP_ code, and let the CPU stop dispatch routine decide + what the appropriate cpu-specific behavior should be. + */ + return STOP_SANITY; +} + +t_stat xq_system_id (CTLR* xq, const ETH_MAC dest, uint16 receipt_id) +{ + static smp_interlocked_uint32_var receipt = smp_var_init(0); + ETH_PACK system_id; + uint8* const msg = &system_id.msg[0]; + t_stat status; + + if (! smp_check_aligned(& receipt, FALSE)) + return SCPE_IERR; + + sim_debug(DBG_TRC, xq->dev, "xq_system_id()\n"); + + /* 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 = (atomic_var(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)); + memcpy (&msg[0], dest, sizeof(ETH_MAC)); + memcpy (&msg[6], xq->var->setup.valid ? xq->var->setup.macs[0] : xq->var->mac, sizeof(ETH_MAC)); + msg[12] = 0x60; /* type */ + msg[13] = 0x02; /* type */ + msg[14] = 0x1C; /* character count */ + msg[15] = 0x00; /* character count */ + msg[16] = 0x07; /* code */ + msg[17] = 0x00; /* zero pad */ + if (receipt_id) { + msg[18] = receipt_id & 0xFF; /* receipt number */ + msg[19] = (receipt_id >> 8) & 0xFF; /* receipt number */ + } else { + uint32 xreceipt = smp_interlocked_increment_var(& receipt) - 1; + msg[18] = xreceipt & 0xFF; /* receipt number */ + msg[19] = (xreceipt >> 8) & 0xFF; /* receipt number */ + } + + /* MOP VERSION */ + msg[20] = 0x01; /* type */ + msg[21] = 0x00; /* type */ + msg[22] = 0x03; /* length */ + msg[23] = 0x03; /* version */ + msg[24] = 0x01; /* eco */ + msg[25] = 0x00; /* user eco */ + + /* FUNCTION */ + msg[26] = 0x02; /* type */ + msg[27] = 0x00; /* type */ + msg[28] = 0x02; /* length */ + msg[29] = 0x00; /* value 1 ??? */ + msg[30] = 0x00; /* value 2 */ + + /* HARDWARE ADDRESS */ + msg[31] = 0x07; /* type */ + msg[32] = 0x00; /* type */ + msg[33] = 0x06; /* length */ + memcpy (&msg[34], xq->var->mac, sizeof(ETH_MAC)); /* ROM address */ + + /* DEVICE TYPE */ + msg[40] = 37; /* type */ + 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; +} + +/* +** service routine - used for ethernet reading loop +*/ +t_stat xq_svc(RUN_SVC_DECL, UNIT* uptr) +{ + CTLR* xq = xq_unit2ctlr(uptr); + + AUTO_LOCK_NM(xq_autolock, *xq->xq_lock); + RUN_SVC_CHECK_CANCELLED(uptr); + + return xq_svc_ex(RUN_PASS, uptr, xq); +} + +t_stat xq_svc_ex(RUN_DECL, UNIT* uptr, CTLR* xq) +{ + /* if the receiver is enabled */ + if ((xq->var->mode == XQ_T_DELQA_PLUS) || (xq->var->csr & XQ_CSR_RE)) { + t_stat status; + + /* First pump any queued packets into the system */ + 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 */ + 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 (status); + + /* Now pump any still queued packets into the system */ + 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) + xq_activate(uptr, TRUE, xq->var->poll); + + return SCPE_OK; +} + +/* +** service routine - used for timer based activities +*/ +t_stat xq_tmrsvc(RUN_SVC_DECL, UNIT* uptr) +{ + CTLR* xq = xq_unit2ctlr(uptr); + + AUTO_LOCK_NM(xq_autolock, *xq->xq_lock); + RUN_SVC_CHECK_CANCELLED(uptr); + + /* has sanity timer expired? if so, reboot */ + if (xq->var->sanity.enabled) + if (--xq->var->sanity.timer <= 0) + 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); + } + + /* resubmit service timer */ + xq_activate(uptr, FALSE, 4); + + return SCPE_OK; +} + + +/* attach device: */ +t_stat xq_attach(UNIT* uptr, char* cptr) +{ + t_stat status; + char* tptr; + CTLR* xq = xq_unit2ctlr(uptr); + char buffer[80]; /* buffer for runtime input */ + + sim_debug(DBG_TRC, xq->dev, "xq_attach(cptr=%s)\n", cptr); + + /* runtime selection of ethernet port? */ + if (*cptr == '?') { /* I/O style derived from main() */ + memset (buffer, 0, sizeof(buffer)); /* clear read buffer */ + eth_show (smp_stdout, uptr, 0, NULL); /* show ETH devices */ + smp_printf ("Select device (ethX or )? "); /* prompt for device */ + cptr = read_line (buffer, sizeof(buffer), smp_stdin); /* read command line */ + if (cptr == NULL) return SCPE_ARG; /* ignore EOF */ + if (*cptr == 0) return SCPE_ARG; /* ignore blank */ + } /* resume attaching */ + + tptr = (char *) malloc(strlen(cptr) + 1); + if (tptr == NULL) return SCPE_MEM; + strcpy(tptr, cptr); + + xq->var->etherface = (ETH_DEV *) malloc(sizeof(ETH_DEV)); + if (!xq->var->etherface) + { + free(tptr); + return SCPE_MEM; + } + + status = eth_open(xq->var->etherface, cptr, xq->dev, DBG_ETH); + if (status != SCPE_OK) { + free(tptr); + free(xq->var->etherface); + xq->var->etherface = NULL; + return status; + } + if (xq->var->poll == 0) { + status = eth_set_async(xq->var->etherface, xq->var->coalesce_latency_ticks); + if (status != SCPE_OK) { + eth_close(xq->var->etherface); + free(tptr); + free(xq->var->etherface); + xq->var->etherface = NULL; + 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 */ + smp_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); + free(tptr); + free(xq->var->etherface); + xq->var->etherface = NULL; + return SCPE_NOATT; + } + uptr->filename = tptr; + uptr->flags |= UNIT_ATT; + + /* turn on transceiver power indicator */ + xq_csr_set_clr(xq, XQ_CSR_OK, 0); + + /* init read queue (first time only) */ + status = ethq_init(&xq->var->ReadQ, XQ_QUE_MAX); + if (status != SCPE_OK) { + eth_close(xq->var->etherface); + free(tptr); + free(xq->var->etherface); + xq->var->etherface = NULL; + return status; + } + + if (xq->var->mode == XQ_T_DELQA_PLUS) + 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); + else + if (xq->var->setup.valid) { + int i, count = 0; + ETH_MAC zeros = {0, 0, 0, 0, 0, 0}; + ETH_MAC filters[XQ_FILTER_MAX + 1]; + + for (i = 0; i < XQ_FILTER_MAX; i++) + if (memcmp(zeros, &xq->var->setup.macs[i], sizeof(ETH_MAC))) + 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); + } + else + /* reset the device with the new attach info */ + xq_reset(xq->dev); + + return SCPE_OK; +} + +/* detach device: */ + +t_stat xq_detach(UNIT* uptr) +{ + CTLR* xq = xq_unit2ctlr(uptr); + sim_debug(DBG_TRC, xq->dev, "xq_detach()\n"); + + if (uptr->flags & UNIT_ATT) { + eth_close (xq->var->etherface); + free(xq->var->etherface); + xq->var->etherface = NULL; + free(uptr->filename); + uptr->filename = NULL; + uptr->flags &= ~UNIT_ATT; + /* cancel service timers */ + sim_cancel(xq->unit[0]); + sim_cancel(xq->unit[1]); + } + + /* turn off transceiver power indicator */ + xq_csr_set_clr(xq, 0, XQ_CSR_OK); + + return SCPE_OK; +} + +/* + * Unfortunately there appears to be no lock-free way to consolidate per-controller interrupts + * for multiple controllers into master interrupt. Such consolidation is possible to shared counter, + * but it is impossible to atomically transfer this counter to VCPU interrupt state. + * + * Whereas non-atomic transfer would yield incorrect results. + * Consider for example the sequence where controller A clears interrupt, then controller B raises it: + * + * [controller A] [controller B] + * + * if (atomic_decr(interrupt_count) == 0) + * . + * . if (atomic_incr(interrupt_count) == 1) + * . SET_INT(XQ) + * . + * . + * CLR_INT(XQ) + * + * Because of race condition, interrupt incorrectly turns out cleared at the end. + * + * Therefore we have to set and clear interrupt under the protection of master lock. + * We could have used separate lock for master lock, but it is better to use controller A's lock. + * + */ + +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"); + + if (xq->var->irq == 0) + { + xq->var->irq = 1; /* set ctrl int */ + + if (xq->xq_lock != &xqa_lock) /* acquire master lock unless already holding it */ + xqa_lock->lock(); + + if (++smp_var(xq_pending_intrs) == 1) + SET_INT (XQ); /* set master int */ + + if (xq->xq_lock != &xqa_lock) /* release master lock if was acquired */ + xqa_lock->unlock(); + } +} + +void xq_clrint (CTLR* xq, t_bool intack) +{ + if (xq->var->irq == 1) + { + xq->var->irq = 0; /* clr ctrl int */ + + if (xq->xq_lock != &xqa_lock) /* acquire master lock unless already holding it */ + xqa_lock->lock(); + + if (--smp_var(xq_pending_intrs) == 0) + CLR_INT (XQ); /* clear master int */ + else + { + if (intack) + SET_INT (XQ); /* set master int */ + } + + if (xq->xq_lock != &xqa_lock) /* release master lock if was acquired */ + xqa_lock->unlock(); + } +} + +int32 xq_int (void) +{ + int i; + for (i = 0; i < XQ_MAX_CONTROLLERS; i++) + { + CTLR* xq = &xq_ctrl[i]; + + if (xq->dev->flags & DEV_DIS) /* skip unconfigured devices */ + continue; + + (*xq->xq_lock)->lock(); + if (xq->var->irq) { /* if interrupt pending */ + xq_clrint(xq, TRUE); /* clear interrupt */ + (*xq->xq_lock)->unlock(); + return xq->dib->vec; /* return vector */ + } + (*xq->xq_lock)->unlock(); + } + return 0; /* no interrupt request active */ +} + +void xq_csr_set_clr (CTLR* xq, uint16 set_bits, uint16 clear_bits) +{ + uint16 saved_csr = xq->var->csr; + + /* set the bits in the csr */ + xq->var->csr = (xq->var->csr | set_bits) & ~clear_bits; + + sim_debug_u16(DBG_CSR, xq->dev, xq_csr_bits, saved_csr, xq->var->csr, 1); + + /* check and correct the state of controller interrupt */ + + /* if IE is transitioning, process it */ + if ((saved_csr ^ xq->var->csr) & XQ_CSR_IE) { + + /* if IE transitioning low and interrupt set, clear interrupt */ + if ((clear_bits & XQ_CSR_IE) && xq->var->irq) + xq_clrint(xq); + + /* if IE transitioning high, and XI or RI is high, + set interrupt if interrupt is off */ + if ((set_bits & XQ_CSR_IE) && (xq->var->csr & XQ_CSR_XIRI) && !xq->var->irq) + xq_setint(xq); + + } else { /* IE is not transitioning */ + + /* if interrupts are enabled */ + if (xq->var->csr & XQ_CSR_IE) { + + /* if XI or RI transitioning high and interrupt off, set interrupt */ + if (((saved_csr ^ xq->var->csr) & (set_bits & XQ_CSR_XIRI)) && !xq->var->irq) { + xq_setint(xq); + + } else { + + /* if XI or RI transitioning low, and both XI and RI are now low, + clear interrupt if interrupt is on */ + if (((saved_csr ^ xq->var->csr) & (clear_bits & XQ_CSR_XIRI)) + && !(xq->var->csr & XQ_CSR_XIRI) + && xq->var->irq) + xq_clrint(xq); + } + + } /* IE enabled */ + + } /* IE transitioning */ +} + +static void xq_activate(UNIT* uptr, t_bool try_at_idletime, uint32 fraction) +{ + t_bool at_idletime = FALSE; + + /* + * If idle sleep is used, avoid interrupting it mid-tick for activities that are + * not time critical and rather co-schedule the latter with next clock tick. + */ + if (try_at_idletime) + at_idletime = sim_vsmp_active ? sim_vsmp_idle_sleep : sim_idle_enab; + + if (at_idletime) + sim_activate_clk_cosched(uptr, TMXR_MULT); + else + sim_activate(uptr, (weak_read_var(tmr_poll) * clk_tps) / fraction); +} + +static void xq_activate_abs(UNIT* uptr, t_bool try_at_idletime, uint32 fraction) +{ + t_bool at_idletime = FALSE; + + /* + * If idle sleep is used, avoid interrupting it mid-tick for activities that are + * not time critical and rather co-schedule the latter with next clock tick. + */ + if (try_at_idletime) + at_idletime = sim_vsmp_active ? sim_vsmp_idle_sleep : sim_idle_enab; + + if (at_idletime) + sim_activate_clk_cosched_abs(uptr, TMXR_MULT); + else + sim_activate_abs(uptr, (weak_read_var(tmr_poll) * clk_tps) / fraction); +} + +/*============================================================================== +/ debugging routines +/=============================================================================*/ + + +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]) + 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); + 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) { + char buffer[20] = {0}; + uint16 len = xq->var->write_buffer.len; + if (len & XQ_SETUP_MC) strcat(buffer, "MC "); + if (len & XQ_SETUP_PM) strcat(buffer, "PM "); + if (len & XQ_SETUP_LD) strcat(buffer, "LD "); + if (len & XQ_SETUP_ST) strcat(buffer, "ST "); + 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 < (int) 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 < (int) 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/src/PDP11/pdp11_xq.h b/src/PDP11/pdp11_xq.h new file mode 100644 index 0000000..614c8fc --- /dev/null +++ b/src/PDP11/pdp11_xq.h @@ -0,0 +1,404 @@ +/* pdp11_xq.h: DEQNA/DELQA ethernet controller information + ------------------------------------------------------------------------------ + + Copyright (c) 2002-2008, 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"), + 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 + THE AUTHOR 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 the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + 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 + 09-Jan-04 DTH Added Boot PDP diagnostic definition, XI/RI combination + 26-Dec-03 DTH Moved ethernet queue definitions to sim_ether + 25-Nov-03 DTH Added interrupt request flag + 02-Jun-03 DTH Added struct xq_stats + 28-May-03 DTH Made xq_msg_que.item dynamic + 28-May-03 MP Optimized structures, removed rtime variable + 06-May-03 DTH Changed 32-bit t_addr to uint32 for v3.0 + 28-Apr-03 DTH Added callbacks for multicontroller identification + 25-Mar-03 DTH Removed bootrom field - no longer needed; Updated copyright + 15-Jan-03 DTH Merged Mark Pizzolato's changes into main source + 13-Jan-03 MP Added countdown for System Id multicast packets + 10-Jan-03 DTH Added bootrom field + 30-Dec-02 DTH Added setup valid field + 21-Oct-02 DTH Corrected copyright again + 15-Oct-02 DTH Fixed copyright, added sanity timer support + 10-Oct-02 DTH Added more setup fields and bitmasks + 08-Oct-02 DTH Integrated with 2.10-0p4, added variable vector and copyrights + 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11 + 15-Aug-02 DTH Started XQ simulation + + ------------------------------------------------------------------------------ +*/ + +#ifndef _PDP11_XQ_H +#define _PDP11_XQ_H + +#if defined (VM_PDP10) /* PDP10 version */ +#error "DEQNA/DELQA not supported on PDP10!" + +#elif defined (VM_VAX) /* VAX version */ +#include "sim_defs.h" +#include "vax_defs.h" +#define XQ_RDX 16 +#define XQ_WID 32 + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#define XQ_RDX 8 +#define XQ_WID 16 +#endif + +#include "sim_ether.h" + +#define XQ_QUE_MAX 500 /* read queue size in packets */ +#define XQ_FILTER_MAX 14 /* number of filters allowed */ +#if defined(USE_READER_THREAD) +#define XQ_SERVICE_INTERVAL 0 /* polling interval - No Polling with Asynch I/O */ +#else +#define XQ_SERVICE_INTERVAL 100 /* polling interval - X per second */ +#endif +#define XQ_SYSTEM_ID_SECS 540 /* seconds before system ID timer expires */ +#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, 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 timer; /* countdown timer */ +}; + +struct xq_setup { + int valid; /* is the setup block valid? */ + int promiscuous; /* promiscuous mode enabled */ + int multicast; /* enable all multicast addresses */ + int l1; /* first diagnostic led state */ + int l2; /* second diagnostic led state */ + int l3; /* third diagnostic led state */ + int sanity_timer; /* sanity timer value (encoded) */ + 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 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; + uint8 add_mi; + uint8 add_hi; + uint8 siz_lo; + uint8 siz_hi; +}; + +struct xq_device { + /*+ initialized values - DO NOT MOVE */ + ETH_PCALLBACK rcallback; /* read callback routine */ + ETH_PCALLBACK wcallback; /* write callback routine */ + ETH_MAC mac; /* Hardware MAC address */ + enum xq_type type; /* controller type */ + 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 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. */ + struct xq_setup setup; + struct xq_stats stats; + uint8 mac_checksum[2]; + uint16 rbdl_buf[6]; + uint16 xbdl_buf[6]; + uint32 rbdl_ba; + uint32 xbdl_ba; + ETH_DEV* etherface; + ETH_PACK read_buffer; + ETH_PACK write_buffer; + ETH_QUE ReadQ; + int32 idtmr; /* countdown for ID Timer */ + uint32 must_poll; /* receiver must poll instead of counting on asynch polls */ +}; + +struct xq_controller { + DEVICE* dev; /* device block */ + UNIT** unit; /* unit block */ + DIB* dib; /* device interface block */ + struct xq_device* var; /* controller-specific variables */ + smp_lock** xq_lock; /* controller lock */ +}; + +typedef struct xq_controller CTLR; + + +#define XQ_CSR_RI 0x8000 /* Receive Interrupt Request (RI) [RO/W1] */ +#define XQ_CSR_PE 0x4000 /* Parity Error in Host Memory (PE) [RO] */ +#define XQ_CSR_CA 0x2000 /* Carrier from Receiver Enabled (CA) [RO] */ +#define XQ_CSR_OK 0x1000 /* Ethernet Transceiver Power (OK) [RO] */ +#define XQ_CSR_RR 0x0800 /* Reserved : Set to Zero (RR) [RO] */ +#define XQ_CSR_SE 0x0400 /* Sanity Timer Enable (SE) [RW] */ +#define XQ_CSR_EL 0x0200 /* External Loopback (EL) [RW] */ +#define XQ_CSR_IL 0x0100 /* Internal Loopback (IL) [RW] */ +#define XQ_CSR_XI 0x0080 /* Transmit Interrupt Request (XI) [RO/W1] */ +#define XQ_CSR_IE 0x0040 /* Interrupt Enable (IE) [RW] */ +#define XQ_CSR_RL 0x0020 /* Receive List Invalid/Empty (RL) [RO] */ +#define XQ_CSR_XL 0x0010 /* Transmit List Invalid/Empty (XL) [RO] */ +#define XQ_CSR_BD 0x0008 /* Boot/Diagnostic ROM Load (BD) [RW] */ +#define XQ_CSR_NI 0x0004 /* NonExistant Memory Timeout (NXM) [RO] */ +#define XQ_CSR_SR 0x0002 /* Software Reset (SR) [RW] */ +#define XQ_CSR_RE 0x0001 /* Receiver Enable (RE) [RW] */ + +/* special access bitmaps */ +#define XQ_CSR_RO 0xF8B4 /* Read-Only bits */ +#define XQ_CSR_RW 0x074B /* Read/Write bits */ +#define XQ_CSR_W1 0x8080 /* Write-one-to-clear bits */ +#define XQ_CSR_BP 0x0208 /* Boot PDP diagnostic ROM */ +#define XQ_CSR_XIRI 0X8080 /* Transmit & Receive Interrupts */ + +#define XQ_VEC_MS 0x8000 /* Mode Select (MO) [RW] */ +#define XQ_VEC_OS 0x4000 /* Option Switch Setting (OS) [RO] */ +#define XQ_VEC_RS 0x2000 /* Request Self-Test (RS) [RW] */ +#define XQ_VEC_S3 0x1000 /* Self-Test Status (S3) [RO] */ +#define XQ_VEC_S2 0x0800 /* Self-Test Status (S2) [RO] */ +#define XQ_VEC_S1 0x0400 /* Self-Test Status (S1) [RO] */ +#define XQ_VEC_ST 0x1C00 /* Self-Test (S1 + S2 + S3) [RO] */ +#define XQ_VEC_IV 0x03FC /* Interrupt Vector (IV) [RW] */ +#define XQ_VEC_RR 0x0002 /* Reserved (RR) [RO] */ +#define XQ_VEC_ID 0x0001 /* Identity Test Bit (ID) [RW] */ + +/* special access bitmaps */ +#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] */ +#define XQ_DSC_S 0x1000 /* Setup bit [Transmit only] */ +#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 */ +#define DBG_CSR 0x0004 /* watch CSR */ +#define DBG_VAR 0x0008 /* watch VAR */ +#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 packet headers */ +#define DBG_DAT 0x0100 /* display packet data */ +#define DBG_ETH 0x8000 /* debug ethernet device */ + +#endif /* _PDP11_XQ_H */ diff --git a/src/PDP11/pdp11_xq_bootrom.h b/src/PDP11/pdp11_xq_bootrom.h new file mode 100644 index 0000000..0fb978b --- /dev/null +++ b/src/PDP11/pdp11_xq_bootrom.h @@ -0,0 +1,312 @@ +/* pdp11_xq_bootrom.h: DEQNA/DELQA bootrom data + ------------------------------------------------------------------------------ + + Copyright (c) 2003-2008, 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"), + 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 + THE AUTHOR 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 the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + Modification history: + + 26-Mar-03 DTH Removed 'static' declaration + 23-Mar-03 DTH Created by extracting from merged DEQNA bootrom dumps + + ------------------------------------------------------------------------------ +*/ + +#ifndef _PDP11_XQ_BOOTROM_H +#define _PDP11_XQ_BOOTROM_H + +#ifdef VM_PDP11 + /* + Bootrom code is from merged file 23-334e5.bin, offset 050000, for 4096. bytes. + + Word 0: NOP + Word 1: Branch to extended primary boot + Word 2: Branch/Vector to Citizenship tests + Word 3: Offset from beginning to checksum word + + See INIQNA.MAR for further information on format and contents. + */ + + uint16 xq_bootrom[] = { + 0000240,0000423,0000546,0007776,0000520,0000000,0100000,0100000, + 0002000,0176000,0000000,0000000,0100000,0100000,0006000,0176000, + 0000000,0000000,0100000,0020000,0140000,0012706,0001776,0010046, + 0012761,0000014,0000004,0005061,0000006,0012761,0001010,0000016, + 0005000,0005300,0001376,0005061,0000016,0005000,0005300,0001376, + 0012761,0000002,0000016,0005061,0000016,0042767,0037777,0177664, + 0026767,0177660,0177702,0001057,0042767,0037777,0177652,0026767, + 0177646,0177664,0001050,0042767,0037777,0177650,0026767,0177644, + 0177646,0001041,0012704,0007776,0005003,0005002,0116200,0002000, + 0005202,0042700,0177400,0060003,0005304,0001370,0013700,0000006, + 0026003,0002000,0001020,0000137,0002010,0012702,0012000,0004767, + 0000040,0005700,0001010,0011602,0001002,0000167,0004530,0022702, + 0000777,0103001,0000112,0013703,0000012,0001401,0000113,0000000, + 0000776,0010637,0000764,0062701,0000016,0032761,0100000,0177776, + 0001421,0052761,0020000,0177776,0012703,0000777,0005000,0005300, + 0001376,0032761,0016000,0177776,0001405,0005303,0001370,0012700, + 0000200,0000454,0004767,0000136,0052711,0000002,0042711,0000002, + 0012703,0017777,0005303,0001376,0004567,0003514,0177700,0001014, + 0005712,0001014,0032762,0002000,0000050,0001402,0052711,0002000, + 0004567,0003464,0177622,0001402,0052712,0000100,0012711,0000002, + 0005011,0012703,0017777,0005303,0001376,0011100,0042700,0064000, + 0022700,0010060,0001402,0052712,0000100,0011200,0162701,0000016, + 0000207,0052400,0177652,0013746,0000034,0013746,0000036,0010703, + 0062703,0000210,0010337,0000034,0012737,0000340,0000036,0104400, + 0012637,0000036,0012637,0000034,0013700,0000762,0052700,0000340, + 0062703,0000010,0010337,0000004,0010037,0000006,0010637,0000766, + 0010137,0000772,0010237,0000770,0062703,0000012,0010337,0000024, + 0010037,0000026,0062703,0000022,0012761,0000774,0177776,0052761, + 0100000,0177776,0010337,0000774,0010037,0000776,0005062,0000002, + 0005012,0012700,0000162,0060200,0012704,0000112,0005020,0005304, + 0001375,0004567,0003202,0177666,0001434,0005262,0000002,0022762, + 0000002,0000002,0003355,0000207,0016637,0000002,0000762,0000006, + 0052712,0002000,0013706,0000764,0000207,0052712,0020000,0013706, + 0000766,0013701,0000772,0013702,0000770,0000207,0052712,0004000, + 0000002,0106427,0000000,0010103,0162703,0000016,0010204,0062704, + 0000012,0012705,0000006,0012300,0110024,0005305,0001374,0010204, + 0062704,0000012,0010405,0005724,0001004,0005724,0001002,0005714, + 0001421,0010504,0012700,0177777,0020024,0001016,0020024,0001014, + 0020014,0001410,0001011,0010504,0022724,0000252,0001003,0122714, + 0000004,0103002,0052712,0000001,0012700,0177777,0004767,0003314, + 0013705,0000774,0010703,0062703,0000044,0010337,0000774,0052711, + 0000100,0010461,0177772,0005000,0010061,0177774,0012703,0010000, + 0005303,0001376,0052712,0004000,0000207,0062706,0000004,0010537, + 0000774,0005200,0001767,0011100,0032700,0000200,0001763,0011400, + 0042700,0037777,0022700,0140000,0001355,0005764,0000010,0001752, + 0005764,0000012,0001747,0052711,0000002,0042711,0000002,0012711, + 0002000,0106437,0000762,0004567,0002576,0177666,0001402,0000207, + 0010703,0062703,0177160,0010362,0000002,0010362,0000006,0062703, + 0000005,0010362,0000004,0005062,0000010,0010203,0062703,0000162, + 0012700,0000002,0012705,0000006,0105023,0012704,0000007,0026262, + 0000004,0000006,0003003,0016262,0000002,0000006,0117223,0000006, + 0005262,0000006,0005304,0001363,0005305,0001356,0012704,0000020, + 0105023,0005304,0001375,0005300,0001345,0004567,0002432,0177705, + 0001403,0052712,0000002,0000207,0005262,0000010,0022762,0000764, + 0000010,0003323,0042761,0100000,0177776,0005062,0000006,0010204, + 0062704,0000163,0010462,0000010,0005304,0012703,0000060,0105024, + 0005303,0001375,0062762,0000010,0000002,0016262,0000010,0000002, + 0012762,0000060,0000004,0105062,0000012,0000261,0106162,0000012, + 0103041,0106162,0000012,0062762,0000010,0000002,0000433,0016204, + 0000010,0005304,0012703,0000060,0112724,0000377,0005303,0001374, + 0012762,0000060,0000004,0016262,0000010,0000002,0112762,0000377, + 0000012,0000241,0106162,0000012,0103405,0106162,0000012,0062762, + 0000010,0000002,0016204,0000002,0012703,0000007,0105064,0177770, + 0116224,0000012,0005303,0001372,0004567,0002154,0177750,0001402, + 0000167,0000414,0005762,0000006,0001011,0000241,0106172,0000002, + 0103010,0106072,0000002,0106072,0000002,0000403,0000261,0106172, + 0000002,0016204,0000010,0010203,0062703,0004362,0012700,0000006, + 0111423,0062704,0000010,0005300,0001373,0012711,0000001,0012700, + 0177775,0004767,0002352,0010461,0177766,0005061,0177770,0004767, + 0002372,0010461,0177772,0005061,0177774,0012700,0077777,0032711, + 0100000,0001003,0005300,0001373,0000523,0005000,0004567,0002412, + 0000000,0040000,0001115,0016204,0000010,0005204,0010203,0062703, + 0004362,0012700,0000006,0111423,0062704,0000010,0005300,0001373, + 0042711,0100200,0012700,0177775,0004767,0002224,0010461,0177766, + 0005061,0177770,0012700,0177775,0004767,0002240,0010461,0177772, + 0005061,0177774,0012700,0077777,0032711,0100000,0001003,0005300, + 0001373,0000454,0005000,0004567,0002260,0000000,0000000,0001040, + 0042711,0000001,0010204,0062704,0001362,0010205,0062705,0004362, + 0012700,0000006,0122425,0001024,0005300,0001374,0005362,0000004, + 0001007,0005762,0000006,0001034,0005262,0000006,0000167,0177256, + 0005762,0000006,0001002,0000167,0177222,0000261,0000167,0177304, + 0052712,0000004,0000405,0052712,0004000,0000402,0052712,0001004, + 0052761,0100000,0177776,0000207,0000074,0001422,0002752,0177777, + 0052761,0100000,0177776,0052711,0001000,0010703,0062703,0176046, + 0010362,0000002,0010362,0000006,0062703,0000004,0010362,0000004, + 0010703,0062703,0177726,0010362,0000010,0010203,0062703,0004362, + 0017205,0000010,0026262,0000004,0000006,0003003,0016262,0000002, + 0000006,0117223,0000006,0005262,0000006,0005305,0001363,0017200, + 0000010,0004567,0001536,0103425,0017200,0000010,0004567,0001752, + 0000000,0020000,0001401,0000003,0010204,0062704,0001362,0010205, + 0062705,0004362,0017200,0000010,0122425,0001003,0005300,0001374, + 0000403,0052712,0000010,0000207,0062762,0000002,0000010,0022772, + 0177777,0000010,0001402,0000167,0177620,0012700,0177770,0004767, + 0001536,0010461,0177766,0005061,0177770,0010203,0062703,0000040, + 0010304,0012700,0000010,0012723,0100000,0012723,0100000,0010213, + 0062723,0000012,0012723,0177777,0005023,0005023,0005300,0001363, + 0010403,0052763,0000200,0000002,0052763,0000300,0000016,0012763, + 0177776,0000022,0052763,0000100,0000032,0062763,0000002,0000034, + 0062763,0000004,0000050,0012763,0040000,0000062,0010363,0000064, + 0062763,0000074,0000064,0010363,0000100,0062763,0000070,0000100, + 0012763,0177776,0000102,0012763,0120000,0000112,0012763,0177775, + 0000116,0012763,0020000,0000126,0010461,0177772,0005061,0177774, + 0012700,0077777,0032711,0100000,0001005,0005300,0001373,0052712, + 0001000,0000411,0012700,0000020,0004567,0001376,0100000,0020000, + 0001405,0052712,0040000,0052712,0000020,0000207,0010203,0062703, + 0000040,0012700,0000010,0016305,0000000,0042705,0037777,0022705, + 0140000,0001357,0022700,0000004,0001403,0022700,0000001,0001007, + 0005763,0000010,0001346,0005763,0000012,0001343,0000424,0022700, + 0000002,0001405,0032763,0100000,0000010,0001733,0000414,0016305, + 0000010,0042705,0026417,0022705,0000000,0001323,0016305,0000012, + 0042705,0176000,0001716,0062703,0000014,0005300,0001324,0010203, + 0062703,0000012,0010204,0062704,0001362,0010405,0022324,0001303, + 0022324,0001301,0022324,0001277,0005724,0001275,0005724,0001273, + 0022524,0001271,0022524,0001267,0022524,0001265,0010203,0062703, + 0000162,0010305,0012700,0000113,0005023,0005300,0001375,0010204, + 0062704,0000012,0004767,0000046,0062705,0000020,0004767,0000036, + 0004567,0000444,0177674,0001401,0000207,0012700,0177777,0032711, + 0020000,0001423,0005300,0001373,0052712,0100000,0000207,0010446, + 0012700,0000006,0005205,0012703,0000007,0111425,0005303,0001375, + 0005204,0005300,0001367,0012604,0000207,0005712,0001017,0052711, + 0001400,0012700,0000056,0004767,0000132,0012700,0000074,0004567, + 0000522,0103005,0042712,0001000,0052712,0100000,0000207,0012700, + 0000074,0004767,0000156,0001761,0001403,0052712,0000040,0000207, + 0012700,0000074,0004767,0000232,0001370,0012700,0002734,0004767, + 0000042,0012700,0002752,0004567,0000432,0103757,0012700,0002752, + 0004767,0000100,0001766,0001351,0012700,0002752,0004767,0000162, + 0001344,0000207,0010203,0062703,0004362,0010204,0062704,0000012, + 0010405,0012423,0012423,0012423,0012523,0012523,0012523,0012723, + 0000220,0005023,0012723,0000001,0110023,0005300,0001375,0005062, + 0000002,0000207,0004567,0000542,0000000,0020000,0001004,0062716, + 0000002,0005712,0000207,0016200,0000050,0042700,0137777,0001010, + 0016200,0000030,0032700,0137777,0001003,0042712,0040000,0000757, + 0005262,0000002,0022762,0000144,0000002,0003751,0042712,0040000, + 0000750,0010204,0062704,0001362,0010205,0062705,0004362,0122425, + 0001002,0005300,0001374,0000207,0010200,0062700,0000162,0010046, + 0011500,0005300,0004767,0000270,0010461,0177766,0005061,0177770, + 0012500,0004767,0000306,0012764,0130000,0000002,0011664,0000004, + 0010461,0177772,0005061,0177774,0012704,0017777,0032711,0100000, + 0001010,0005304,0001373,0052712,0001000,0005726,0052712,0010000, + 0000430,0016500,0177776,0006300,0005400,0004567,0000274,0000000, + 0020000,0001363,0016500,0177776,0006300,0005400,0010204,0062704, + 0001362,0012603,0122423,0001352,0005300,0001374,0022714,0051343, + 0001345,0000205,0005046,0006000,0005516,0061600,0005400,0004767, + 0000076,0010461,0177766,0005061,0177770,0004767,0000116,0005726, + 0001403,0052764,0000200,0000002,0010461,0177772,0005061,0177774, + 0012703,0000777,0005000,0032711,0100000,0001010,0005300,0001376, + 0005303,0001371,0052712,0001000,0000261,0000401,0000241,0000205, + 0010203,0062703,0001362,0012704,0000200,0012723,0051343,0005304, + 0001374,0004567,0000020,0000020,0001362,0000207,0004567,0000006, + 0000040,0004362,0000207,0012503,0060203,0010304,0012723,0100000, + 0012723,0120000,0012513,0060223,0010023,0012723,0100000,0012723, + 0100000,0012723,0100000,0005023,0000205,0010046,0005000,0011104, + 0052711,0100200,0042704,0077401,0022704,0100260,0001401,0010700, + 0016204,0000040,0042704,0037777,0022704,0140000,0001401,0010700, + 0016204,0000050,0100002,0042704,0077777,0042704,0076417,0022504, + 0001401,0010700,0016204,0000052,0042704,0176000,0001001,0010700, + 0016204,0000020,0042704,0037777,0022704,0140000,0001401,0010700, + 0016204,0000030,0010446,0042704,0007777,0022504,0001401,0010700, + 0012604,0042704,0174377,0022762,0177775,0000046,0001002,0005726, + 0000415,0032762,0010000,0000042,0001401,0005004,0016203,0000032, + 0042703,0177400,0060304,0022604,0001401,0010700,0010003,0001402, + 0052712,0040000,0000205,0000005,0012706,0017776,0010616,0011646, + 0162716,0003056,0010703,0062703,0000014,0010337,0000004,0011100, + 0000401,0000000,0004767,0000230,0011605,0012725,0022410,0012725, + 0000401,0105025,0105025,0012725,0000621,0112725,0000002,0012702, + 0002752,0110225,0000302,0110225,0012702,0000013,0005000,0004767, + 0000452,0001350,0012702,0002756,0004767,0000660,0001046,0011603, + 0112304,0005302,0120427,0000002,0001404,0105704,0001335,0162702, + 0000004,0105713,0001402,0121300,0001030,0112300,0105200,0005302, + 0003410,0012305,0005723,0162702,0000004,0003403,0112325,0005302, + 0003375,0105704,0001417,0005003,0011605,0112725,0000012,0110025, + 0110325,0005005,0012702,0000003,0000722,0105700,0001673,0012703, + 0000001,0000762,0004767,0001232,0112346,0112366,0000001,0000207, + 0042761,0000002,0000016,0016605,0000002,0010504,0062704,0177720, + 0010466,0000004,0012702,0000020,0005024,0077202,0010504,0062704, + 0177760,0005065,0177722,0010465,0177724,0005065,0177742,0010465, + 0177744,0052765,0100000,0177722,0012702,0002756,0006202,0005402, + 0010265,0177726,0052765,0120000,0177742,0016604,0000004,0010467, + 0001324,0005067,0001322,0062704,0000020,0010467,0001314,0005067, + 0001312,0116167,0000000,0001262,0116167,0000002,0001255,0116167, + 0000004,0001250,0116167,0000006,0001243,0116167,0000010,0001236, + 0116167,0000012,0001231,0105267,0001232,0042761,0000002,0000016, + 0052761,0000400,0000016,0004767,0001104,0005065,0000002,0016744, + 0001174,0016744,0001166,0016744,0001160,0012744,0000000,0012744, + 0001000,0012744,0000253,0004767,0000046,0000207,0016605,0000002, + 0010504,0010244,0012744,0000540,0016744,0001122,0016744,0001114, + 0016744,0001106,0016744,0001060,0016744,0001052,0016744,0001044, + 0062705,0177740,0062702,0000016,0020227,0000074,0002003,0012702, + 0000074,0000407,0032702,0000001,0001404,0052765,0000200,0000002, + 0005202,0006202,0005402,0010265,0000006,0005065,0000010,0005065, + 0000012,0016761,0001024,0000010,0016761,0001020,0000012,0012704, + 0000204,0004767,0000610,0103012,0001404,0032765,0001000,0000010, + 0001354,0042765,0000200,0000002,0000244,0000207,0042765,0000200, + 0000002,0032702,0040004,0001401,0000000,0000207,0016605,0000002, + 0062705,0177720,0005065,0000010,0005065,0000012,0016761,0000706, + 0000004,0016761,0000702,0000006,0052761,0000001,0000016,0012704, + 0100004,0004767,0000470,0103030,0001355,0052761,0000002,0000016, + 0012767,0000253,0000576,0012767,0000400,0000572,0012767,0000000, + 0000566,0105267,0000616,0005000,0042761,0000002,0000016,0052761, + 0000400,0000016,0000244,0000207,0042761,0000001,0000016,0052761, + 0000002,0000016,0016605,0000002,0016502,0177776,0042761,0000002, + 0000016,0052761,0000400,0000016,0022765,0000540,0177774,0001041, + 0105767,0000520,0001015,0026765,0000456,0177772,0001267,0026765, + 0000444,0177770,0001263,0026765,0000432,0177766,0001257,0000207, + 0122715,0000003,0001253,0016567,0177766,0000410,0016567,0177770, + 0000404,0016567,0177772,0000400,0105067,0000430,0000244,0005000, + 0000207,0022765,0000220,0177774,0001423,0022765,0001140,0177774, + 0001225,0122715,0000005,0001222,0004767,0000262,0016464,0177776, + 0177770,0016464,0177774,0177766,0016464,0177772,0177764,0000437, + 0010504,0060204,0010503,0062703,0177720,0016302,0000010,0042702, + 0174377,0156302,0000012,0062702,0000056,0022724,0000002,0001027, + 0062765,0000010,0177776,0032714,0000001,0001021,0010503,0062703, + 0177760,0012423,0012423,0012423,0010504,0062704,0177774,0016744, + 0000234,0016744,0000226,0016744,0000220,0004767,0177122,0000167, + 0177272,0016737,0000156,0000030,0016737,0000152,0000032,0016737, + 0000146,0000034,0052761,0000002,0000016,0000264,0000207,0012703, + 0037777,0000241,0012702,0000220,0030461,0000016,0001006,0005303, + 0001376,0005302,0001371,0000261,0000207,0016102,0000016,0010261, + 0000016,0032765,0040000,0000010,0001401,0000261,0000207,0010546, + 0010703,0062703,0000050,0012702,0000030,0012325,0005725,0112325, + 0005302,0001375,0012605,0010504,0012702,0000034,0010244,0012744, + 0001140,0000207,0000253,0000400,0000000,0000007,0000001,0001403, + 0000000,0000002,0000402,0003400,0003000,0000000,0000000,0000000, + 0000144,0022401,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0102206 + }; +#endif /* VM_PDP11 */ + +#endif /* _PDP11_XQ_BOOTROM_H */ diff --git a/src/VAX/ka655_patch.com b/src/VAX/ka655_patch.com new file mode 100644 index 0000000..783c331 --- /dev/null +++ b/src/VAX/ka655_patch.com @@ -0,0 +1,509 @@ +$! +$! This procedure patches the base KA655.BIN Boot ROM image to work under +$! the SIMH simulator +$ +$! The second part of the patch adds support for Extended Memory in the +$! simulator. A simulated system can have up to 512MB of RAM. +$! +$ PATCH /ABSOLUTE /NEW_VERSION /OUTPUT=cp$exe:KA655.BIN cp$src:ka655_orig.BIN +! CVAX Bootstrap Notes +! +! [2004c87e] - de$read_script +! [2004c916] - launch next test, r2 = test #, r4 = header, r5 = offset to main routine +! +! Script BA +! --------- +! +! Test 9D - Utility - ok +! Test 42 - Wait for interrupt - ok +! Test C6 - SSC register check - ok +! Test 60 [2004de37] - Serial line - requires diagnostic loopback mode and +! break detection - bypass +! 2004de99/ brw 2004e0a7 +Replace/Instruction 0DE99 = 'MOVB #03,B^76(R9)' +'BRW 0000E0A7' +Exit +! Test 62 - QDSS disable - ok +! +! Script BC +! --------- +! +! 40. Test 91 - CQBIC init check - ok +! 39. Test 90 [2004d748] - CQBIC registers - ok +! 38. Test 33 [2004d54e] - CMCTL init check - ok +! 37. Test 32 [2004d5b0] - CMCTL registers - ok +! 36. Test 31 [200512d0] - CMCTL CSR setup to map memory - ok +! 35. Test 49 [20052a4b] - CMCTL FDM test - requires fast diagnostic mode +! and XMX counters - bypass +! 20052a55:20052a58/ nop +Delete/Instruction 12A55 = 'BBC #26,(R9),00012A5C' +! 34. Test 30 [20051909] - init map - ok +! +! Script BD +! --------- +! +! 33. Test 52 [2004e656] - prog timer 0 - ok +! 32. Test 52 [2004e656] - prog timer 1 - ok +! 31. Test 53 [2004e918] - toy clock - ok +! 30. Test C1 [2004f8f1] - SSC RAM - ok +! 29. Test 34 [2004d4a0] - ROM - checksum off due to other patches - patch +! 2004d52c:2004d52d/ nop +Delete/Instruction 0D52C = 'BNEQ 0000D531' ! 2004D52C +! 28. Test C5 [2004fc0e] - SSC registers - ok +! 27. Test 55 [2004ea8c] - interval timer - ok +! 26. Test 51 [2004e42d] - CFPA - ok +! 25. Test C7 [2004D3D3] - CBTCR<31:30> - ok +! 24. Test 46 [2004ef1a] - L1 cache diagnostic mode - bypass +! 2004ef80/ brw 2004f47a +Replace/Instruction 0EF80 = 'MOVB #06,B^76(R9)' +'BRW 0000F47A' ! 2004FE80 +Exit +! 23. Test 35 [20050554] - L2 cache integrity test - bypass +! 20050554/ brw 20050a48 +Replace/Instruction 10554 = 'INSV #00,#10,#02,(R9)' +'BRW 00010A48' ! 20050554 +Exit +! 22. Test 43 [20050d65] - L1 with L2 test - bypass +! 20050d65/ brw 20050fca +Replace/Instruction 10D65 = 'MOVAL B^00010D65,W^0080(R9)' +'BRW 00010FCA' ! 20050D65 +Exit +! 21. (Rerun of C2) +! 20. Test 4F [20051d4f] - memory data - bypass, run from ROM +! 20055205/ 0 +Replace/Byte 15205 = 3 +0 ! 20055205 +Exit +! 20051d4f/ brw 2005163a +Replace/Instruction 11D4F = 'MOVAL B^00011D4F,W^0080(R9)' +'BRW 0001163A' ! 20051D4F +Exit +! 19. Test 4E [20051edb] - memory byte write - ok, run from ROM +! 2005521c/ 0 +Replace/Byte 1521C = 3 +0 ! 2005521C +Exit +! 18. Test 4D [20051ff3] - memory addressing - ok, run from ROM +! 20055233/ 0 +Replace/Byte 15233 = 3 +0 ! 20055233 +Exit +! 17. Test 4C [20052190] - ECC test - bypass, run from ROM +! 2005524a/ 0 +Replace/Byte 1524A = 3 +0 ! 2005524A +Exit +! 20052190/ brw 2005163a +Replace/Instruction 12190 = 'MOVAL B^00012190,W^0080(R9)' +'BRW 0001163A' ! 20052190 +Exit +! 16. Test 4B [2005264e] - masked writes with errors - bypass, run from ROM +! 20055261/ 0 +Replace/Byte 15261 = 3 +0 ! 20055261 +Exit +! 2005264e/ brw 2005163a +Replace/Instruction 1264E = 'MOVAL B^0001264E,W^0080(R9)' +'BRW 0001163A' ! 2005264E +Exit +! 15. Test 4A [20052823] - single bit correction - bypass, run from ROM +! 20055278/ 0 +Replace/Byte 15278 = 3 +0 ! 20055278 +Exit +! 20052823/ brw 2005163a +Replace/Instruction 12823 = 'MOVAL B^00012823,W^0080(R9)' +'BRW 0001163A' ! 20052823 +Exit +! 14. Test 48 [20053062] - memory address shorts - bypass, run from ROM +! 2005528f/ 0 +Replace/Byte 1528F = 3 +0 ! 2005528F +Exit +! 20053062/ brw 2005163a +Replace/Instruction 13062 = 'MOVAL B^00013062,W^0080(R9)' +'BRW 0001163A' ! 20053062 +Exit +! 13. Test 47 [200536c3] - verify refresh - run from ROM +! 200552aa/ 0 +Replace/Byte 152AA = 3 +0 ! 200552AA +Exit +! 12. Test 41 [] - count bad pages, relocate bit map +! 11. Test 44 [20050d34] - L1 with memory - bypass +! 20050d34/ brw 20050fca +Replace/Instruction 10D34 = 'MOVAL B^00010D34,W^0080(R9)' +'BRW 00010FCA' ! 20050D34 +Exit +! 10. Test 36 [2004ffdf] - L2 with memory - bypass +! 2004ffdf/ brw 20050428 +Replace/Instruction 0FFDF = 'JSB L^0000CEFD' +'BRW 00010428' ! 2004FFDF +Exit +! 9. Test 80 [2004d7de] - CQBIC memory - bypass last 2 subtests, run from ROM +! 2004dbc0/ brw 2004dd8a +Replace/Instruction 0DBC0 = 'MOVB #1B,B^76(R9)' +'BRW 0000DD8A' ! 2004DBC0 +Exit +! 200552f6/ 0 +Replace/Byte 152F6 = 3 +0 ! 200552F6 +Exit +! 8. Test 54 [] - virtual mode - ok +! 7. Test 34 [] - ROM in virtual mode - see previous notes +! 6. Test C5 [] - SSC registers in virtual mode - ok +! 5. Test 45 [2004ec5d] - cache, memory, CQBIC - bypass +! 2004ec5d/ brw 2004ee90 +Replace/Instruction 0EC5D = 'BICL2 #03,B^28(R9)' +'BRW 0000EE90' ! 2004EC5D +Exit +! 4. Test 5A [2004eb5f] - CVAX CMCTL DAL drivers - ok +! 3. Test 41 [20051096] - reset board +! +! =========================================================================== +! +! +! All of the above patches were done against the base ROM image extracted +! from a genuine MicroVAX 3900. These were all part of SIMH prior to +! extended memory support. +! +! The Diagnostic State Variable DST$W_BITMAP_LENGTH, being 16 bits, can only +! describe a PFN Bitmap describing up to, but NOT including 256MB of RAM. To +! get to 256MB and beyond, we must correctly determine a correct bitmap size. +! all of the Diagnostic state space is in use, either since it is already +! defined, and the space beyond that is used for stack. So, we change the +! references to DST$W_BITMAP_LENGTH to call a subroutine to properly determine +! the PFN BITMAP size. +! +! Most of the code which references DST$W_BITMAP_LENGTH are done from a +! diagnostic test routine which may be relocated to RAM before execution. +! The assumption of such relocating activity is that none of the relocated code +! references any other instructions or data in the ROM image via any PC +! relative addressing modes. Given this requirement, each of the above +! patches must be done with a JSB to an explicit ROM address. As a +! consequence, the patched instruction will generally be longer than the +! instruction which is being replaced. To cleanly affect this +! we must overwrite multiple instructions and incorporate the activities of +! each of the overwritten instructions into the target subroutine. +! Additionally, even without the relocation concerns, numerous places which +! reference these extra routines may be beyond a PC relative displacement +! due to the size of the ROM. +! +! The KA655 ROM size is 128KB. As it turns out, the ROM image is only using +! approximately 105,136 bytes of the total 131072 bytes. We use this unused +! ROM space to store code which implements the required extra functionality +! +Define PATCH_BASE = 00019C00 +Define P$END = PATCH_BASE +Define CP$K_MEMSIZE = 20080148 +Define CP$K_QBMBR = 20080010 +Define DST_BASE = 20140758 +Define CTX_BASE = 201404B2 +Define CTX$L_R2 = 8 +Define CTX$A_GPTR = 66 +Define CTX$L_ERROR_COUNT = 54 +Define CTX$L_ERROR_STATUS = 58 +Define DST$W_BITMAP_LENGTH = 20 +Define DST$A_BITMAP = 1C +Define DST$A_BUSMAP = 24 +Define DST$W_BITMAP_CHECKSUM = 22 +Define CP$CHECKSUM_RTN = 20041C6A +Define GUARD$S_GUARD_BLOCK = 12 +Define GUARD$l_pc = 0 +Define GUARD$a_gptr = 4 +Define GUARD$w_rmask = 8 +Define GUARD$l_save_error_count = 0A +Define GUARD$l_save_error_status = 0E +! +! This routine determines the memory size of the current system. This is +! done by referencing the CMCTL18 memory size register. On an older simulator +! or one with less than 64MB of RAM configured, this register may not exist. +! If it doesn't exist the machine check generated by the reference to the +! non-existant register is caught, and the appropriate memory size is +! determined from the existing PFN Bitmap size. +! +DEFINE GETMEMSIZE_R0 = P$End+1 +Deposit/Instruction GETMEMSIZE_R0 +' pushr #0 ' +' subl2 #guard$s_guard_block, sp' +' movw #0, B^guard$w_rmask (sp)' +' movab B^G$ERR, B^guard$l_pc (sp)' +' movl @#, B^guard$a_gptr (sp)' +' movl @#, B^guard$l_save_error_count (sp)' +' movl @#, B^guard$l_save_error_status (sp)' +' movl sp, @#' +' brb G$RD ' +'G$ERR: movzwl @#,R0' +' clrl @#' +' clrl @#' +' ashl #^d12,r0,r0 ' +' brb G$DON ' +'G$RD: movl @#CP$K_MEMSIZE,R0 ' +'G$DON: movl @#, sp' +' movl B^guard$a_gptr (sp), @#' +' movl B^guard$l_save_error_count (sp), @#' +' movl B^guard$l_save_error_status (sp), @#' +' addl2 #guard$s_guard_block - 2, sp' +' popr (sp)+ ' +'P$End: rsb ' +Exit +! +Define GETMAPENDADDR_R6 = P$End+1 +Deposit/Instruction GETMAPENDADDR_R6 +' MOVZWL @#,R6' +' BNEQ X$1 ' +' MOVL R0, -(SP) ' +' JSB GETMEMSIZE_R0 ' +' ashl #-^D12,R0,R6 ' +' MOVL (SP)+, R0 ' +'X$1: addl @#,R6' +'P$End: rsb ' +Exit + +! DE_QDSS_ANY [2004E2A8] Uses R6 for BitMap Size +! 2004E390/ BSBW GETMAPSIZE_R6 +Replace/Instruction 0E390 +' MOVZWL B^20(R9),R6 ' +' ADDL3 R6,B^1C(R9),R6 ' +Exit +' JSB GETMAPENDADDR_R6 ' +Exit +! +! +DEFINE GETMAPSIZEMAPADDR_STACK = P$End+1 +Deposit/Instruction GETMAPSIZEMAPADDR_STACK +' PUSHL @#,' +' MOVL B^4(SP),-(SP) ' +' MOVZWL @#,B^8(SP)' +' BNEQ X$2' +' MOVL R0, -(SP) ' +' JSB GETMEMSIZE_R0 ' +' ASHL #-^D12,R0,B^0C(SP) ' +' MOVL (SP)+, R0 ' +'X$2: NOP ' +'P$END: RSB' +Exit + +! CP_FIND [200419E8] Uses (SP) for BitMap Size R1 Scratch +! 20041A16/ BSBW GETMAPSIZE_STACK +Replace/Instruction 01A16 +' MOVZWL B^20(R0),-(SP) ' +' PUSHL B^1C(R0) ' +Exit +' JSB GETMAPSIZEMAPADDR_STACK ' +Exit +! +! CP_SCAN [200459D0] Uses R3 for BitMap Size +DEFINE GETMBMEMSIZE_STACK = P$End+1 +Deposit/Instruction GETMBMEMSIZE_STACK +' MOVAB L^0000AACF,-(SP) ' +' MOVL B^4(SP),-(SP) ' +' MOVL R0, -(SP) ' +' JSB GETMEMSIZE_R0 ' +' ASHL #-^D20,R0,B^0C(SP) ' +' MOVL (SP)+, R0 ' +' RSB ' +'GETMAPSIZE_R3: MOVZWL @#,R3' +' BNEQ X$3' +' MOVL R0, -(SP) ' +' JSB GETMEMSIZE_R0 ' +' ASHL #-^D12,R0,R3 ' +' MOVL (SP)+, R0 ' +'X$3: RSB' +'P$END: NOP' +Exit +! 20045B05/ BSBW GETMBMEMSIZE_STACK +Replace/Instruction 05B05 +' MOVL R8,-(SP) ' +' MOVAB L^0000AACF,-(SP) ' +Exit +' JSB GETMBMEMSIZE_STACK ' +Exit +! 20045B80/ BSBW GETMAPSIZE_R3 +Replace/Instruction 05B80 +' MOVZWL @#20140778,R3 ' +Exit +' JSB GETMAPSIZE_R3 ' +Exit +! DE_CQBIC_MEMORY [2004D7B2] +DEFINE GETBITMAPPAGES_R5 = P$End+1 +Deposit/Instruction GETBITMAPPAGES_R5 +' MOVZWL @#,R5' +' BNEQ X$4 ' +' MOVL R0,-(SP) ' +' JSB GETMEMSIZE_R0 ' +' ASHL #-^D12,R0,R5 ' +' MOVL (SP)+,R0 ' +'X$4: ASHL #3,R5,R5 ' +' RSB ' +'GETBITMAPMEMSIZE_R3: MOVZWL @#,R3' +' BNEQ X$5 ' +' MOVL R0,-(SP) ' +' JSB GETMEMSIZE_R0 ' +' ASHL #-^D12,R0,R3 ' +' MOVL (SP)+,R0 ' +'X$5: ASHL #^D12,R3,R3 ' +'P$END: RSB' +Exit +! 2004D8A5/ BSBW GETMAPSIZE_R5 +Replace/Instruction 0D8A5 +' MOVZWL B^20(R9),R5 ' +' ASHL #03,R5,R5 ' +Exit +' JSB GETBITMAPPAGES_R5 ' +Exit +! 2004DA41/ BSBW GETMAPSIZE_R5 +Replace/Instruction 0DA41 +' MOVZWL B^20(R9),R5 ' +' ASHL #03,R5,R5 ' +Exit +' JSB GETBITMAPPAGES_R5 ' +Exit +! 2004DA8C/ BSBW GETMAPSIZE_R5 +Replace/Instruction 0DA8C +' MOVZWL B^20(R9),R5 ' +' ASHL #03,R5,R5 ' +Exit +' JSB GETBITMAPPAGES_R5 ' +Exit +! DE_CACHE_MEM_CQBIC [2004EBF0] +! 2004ECD0/ BSBW GETMAPSIZE_R3 +Replace/Instruction 0ECD0 +' MOVZWL B^20(R9),R3 ' +' ASHL #0C,R3,R3 ' +Exit +' JSB GETBITMAPMEMSIZE_R3 ' +Exit +! CP_BOOTSTRAP +DEFINE GET_X_PFNMAP_SIZEADDR_STACK = P$End+1 +Deposit/Instruction GET_X_PFNMAP_SIZEADDR_STACK +' movl B^dst$a_bitmap (r11), r2' +' movzwl B^dst$w_bitmap_length (r11), r1' +' bneq X$20 ' ! Zero Bitmap size means extended mem +' ashl #-^D12, @#CP$K_MEMSIZE, r1' ! Map Size = MEMSIZE/512/8 +'X$10: brw X$70 ' ! already fixed +'X$20: cmpl r1, #^D16384 ' ! Original Map of 64MB? +' blss X$10 ' ! Too Small For Extended +' JSB GETMEMSIZE_R0 ' +' ashl #-^D12, R0, r1 ' ! Map Size = MEMSIZE/512/8 +' cmpl r1, #^D16384 ' +' beql X$10 ' ! Normal 64MB map +!; +!; First move the Console Scratch Area (16KB), and the Qbus Map (32KB) +!; to the end of physical memory. +!; +' movl @#CP$K_MEMSIZE, r1 ' ! MEMSIZE +' subl3 #^D48*^D1024, r1, r3 ' ! New Destination Address +' addl #^D16384, r2 ' ! Point at end of prior Map +' clrl r0 ' ! Index +'X$63: movb (r2)[r0], (r3)[r0] ' ! Move the Console Scratch Pad and QBMRs +' aoblss #^D48*^D1024, r0, X$63 ' +' movab W^4000(r3), B^DST$A_BUSMAP(r11)' ! Save Qbus Map Register Space +' movab W^4000(r3), @#CP$K_QBMBR' ! Save Qbus Map Register Space +!; +!; Fixup the boot device descriptor previously saved in the scratchpad RAM +!; +' subl3 #^D512, B^DST$A_BUSMAP (r11), r1' +' movab B^8(r1), B^4(r1)' +!; +!; Now we build a new bitmap, with all bits set except for the reserved +!; area containing the bitmap itself, and the console scratch area and +!; the Qbus Map. +!; +' ashl #-^D12, @#CP$K_MEMSIZE, r1' ! Map Size = MEMSIZE/512/8 +' subl3 r1, r3, r2 ' ! Point at new Destination Address +' movl r2, B^dst$a_bitmap (r11)' ! Save Bitmap address +' ashl #-9, @#CP$K_MEMSIZE, r1 ' ! PFN count = MEMSIZE/512 +' ashl #-^D12, @#CP$K_MEMSIZE, r0' ! Map Size = MEMSIZE/512/8 +' addl #^D48*^D1024+^D511, r0 ' ! Plus other reserved page size +' ashl #-9, r0, r0 ' +' subl r0, r1 ' ! Adjust for bitmap of reserved pages +' clrl r0 ' +' pushl r1 ' ! Save total Bit Count +' ashl #-5, r1, r1 ' ! Convert limit to Longword Count +'X$632: movl #-1,(r2)[r0] ' ! Set bitmap entry for 32 pages +' aoblss r1, r0, X$632 ' +' ashl #5, r0, r0 ' ! Convert back to Bit Count +' movl (SP)+, r1 ' ! Restore total Bit Count +' cmpl r0, r1' +' bgeq X$651' +'X$64: bbss r0, (r2), X$65 ' ! Set page bitmap entry +'X$65: aoblss r1, r0, X$64 ' ! Done ? +'X$651: ashl #-9, @#CP$K_MEMSIZE, r1 ' ! PFN count = MEMSIZE/512 +'X$66: bbcc r0, (r2), X$67 ' ! Clear reserved page bitmap entry +'X$67: aoblss r1, r0, X$66 ' ! Done? +' clrl r0 ' ! Zero Initial checksum value +' ashl #-^D12, @#CP$K_MEMSIZE, r1' ! Map Size = MEMSIZE/512/8 +' jsb @#cp$checksum_rtn ' ! Compute checksum for revised bitmap +' movw r0, B^dst$w_bitmap_checksum (r11)' ! save it +' clrw B^dst$w_bitmap_length (r11)' ! Mark as extended bitmap +' ashl #-^D12, @#CP$K_MEMSIZE, r1' ! Map Size = MEMSIZE/512/8 +' movl B^dst$a_bitmap (r11), r2' +'X$70: jmp GETMAPSIZEMAPADDR_STACK' +! +'GETMAPSIZE_CTX_R2: movzwl @#,@#' +' bneq X$71' +' MOVL R0, -(SP) ' +' JSB GETMEMSIZE_R0 ' +' ASHL #-^D12,R0,@#' +' MOVL (SP)+, R0 ' +'X$71: rsb' +Exit +Replace/Instruction 517F = 'movzwl B^20(r11), @#201404BA' +' jsb GETMAPSIZE_CTX_R2 ' +Exit +Replace/Instruction 514B +' MOVZWL B^20(R11),-(SP) ' +' PUSHL B^1C(R11) ' +Exit +' JSB GET_X_PFNMAP_SIZEADDR_STACK' +Exit +! +! DE_MEMORY [200512AC] +! CP_UTIL [] +! +! Identify the Extended Memory Mode in the Console Banner +! (i.e. KA655X-B vs KA655-B) +! +Replace 83D8 = 00303436 +58353536 +Exit +Replace/Byte 83DC = 4B +0 +Exit +Deposit/Instruction 1C04 +' PUSHAB L^000083E2 ' +' JSB GETMEMSIZE_R0 ' +' CMPL R0, #<^D64*^D1024*^D1024>' +' BLEQ B$1 ' +' MOVAB L^000083D6,(SP) ' +'B$1: NOP ' +' NOP ' +' NOP ' +' NOP ' +' NOP ' +' NOP ' +' NOP ' +' NOP ' +' NOP ' +Exit +! +! Extended Memory Patches: +! 9. Test 80 [2004d7de] - CQBIC memory - bypass last 2 subtests, run from ROM +! MP Revised to bypass tests starting at test of NXM reference through MAP +! 2004db2e/ brw 2004dd8a +Replace/Instruction 0db2e = 'MOVB #17,B^76(R9)' +'BRW 0DD8A' +EXIT +! +! Interval Timer Patch +! In operational environments, Test 82 subtests 17 and 20 have been observed +! to ocaisionally fail. Disable the timer portion of the interval timer tests. +! 2004e7c1/ brw 2004e870 +Replace/Instruction 0e7c1 = 'MOVB #10,B^76(R9)' +'BRW 0e870' +EXIT +! +UPDATE +EXIT +$ diff --git a/src/VAX/ka655x.bin b/src/VAX/ka655x.bin new file mode 100644 index 0000000..ade93ab Binary files /dev/null and b/src/VAX/ka655x.bin differ diff --git a/src/VAX/ka655x.bin_old b/src/VAX/ka655x.bin_old new file mode 100644 index 0000000..d15f41d Binary files /dev/null and b/src/VAX/ka655x.bin_old differ diff --git a/src/VAX/vax780_bug_history.txt b/src/VAX/vax780_bug_history.txt new file mode 100644 index 0000000..794baa4 --- /dev/null +++ b/src/VAX/vax780_bug_history.txt @@ -0,0 +1,66 @@ +Bugs Found And Fixed During Simulator Debug + +1. RP: drive clear does not clear RPDA. +2. TU: default formatter must be TM03. +3. SBI: drive 'letter' is actually a 1-based number. +4. MBA: drive register reads return SR<31:16> as high word. +5. UBA: DMA addresses must be masked to Unibus width (18b). +6. HK: thread used multiple times if SEEK is followed by NOP or DCLR. +7. HK: only DCLR clears ATA. +8. MEM: MS780E size declaration off-by-1. +9. MEM: MS780E array start off by >> 4. +10. MEM: CSR-C register write logic incorrect. +11. CIS: CMPP3/CMPP4 using wrong arguments to ReadDstr. +12. CPU, OCTA: CVTfi with integer overflow not setting trap if PSW = 1. +13. STDDEV: read of ICR was missing the call parameter. +14. ACBD/G: testing wrong operand register to get limit sign. +15. CPU: faults not clearing PSL. +16. ADAWI: register mode implemented incorrectly. +17. MOVTC: condition codes not preserved through page fault. +18. MOVTUC: condition codes not preserved through page fault. +19. MOVTUC: escape tested against untranslated rather than translated character. +20. CVTPT: condition code and decimal overflow calculation incorrect. +21. CVTPS: condition code and decimal overflow calculation incorrect. +22. CVTPL: if destination is register, result is stored after register updates. +23. CVTPL: integer overflow set rather than . +24. all decimal string: 11/780 does not validate characters in decimal strings. +25. EDITPC: condition codes not preserved through page fault. +26. EDITPC EO$INSERT: inserts sign instead of fill. +27. EDITPC EO$BLANK_ZERO: address off by one. +28. EDITPC EO$BLANK_ZERO: not testing for set. +29. EDITPC EO$LOAD_PLUS: not skipping character if test fails. +30. EDITPC EO$LOAD_MINUS: not skipping character if test fails. +31. Compatibility mode: SXT not implemented. +32. Compatibility mode: XOR operands fetched in wrong order. +33. MNEGH: condition codes set from original sign. +34. MNEGH: not cleared. +35. H_floating quad precision integer routines (add, inc, neg): carry propagation incorrect. +36. H_floating packup routines: test for zero used exponent not fraction. +37. MULH: carries out of floating accumulator lost. +38. DIVH: stores wrong operand as result. +39. POLYF/D/G: truncation after add not needed. +40. POLYF/D/G: early SRM requires truncation to 31b/63b, not 32b/64b. +41. POLYF/D/G/H: exits too early if argument is zero. +42. POLYD/G/H: calculates address of residual pointer result incorrectly. +43. POLYD/G: performs single precision rather than double precision multiply. +44. POLYH: fails to truncate intermediate result from 128b to 127b. +45. POLYF/D/G/H: internal add routine must test fraction rather than exponent to + detect zero, POLYx can create "denormalized" intermediate result. +46. EMODH: concatenate 16b of extension operand instead of 15b. +47. Specifier flows: modify flows testing for read access rather than write access. +48. Quad/octa writes: wrong address reported on faulting cross-page writes. +49. Memory management: 11/780 implements access control test on first level PTE's. +50. LDPCTX: 11/780 implements mbz tests on PCB fields. +51. LDPCTX/MTPR: 11/780 validity checks PCBB, SCBB, SBR, SLR, P0BR, P0LR, P1BR, P1LR. +52. TMR: tmr_inc not updated in standard (100Hz) mode. +53. MTPR SBR/PCBB/SCBB: 11/780 only checks that bits<1:0> == 0. +54. MTPR xLR: 11/780 excludes bits<31:24> from mbz test. +55. MTPR PxBR: 11/780 only checks bits<31,1:0> == 1,00. + + + + + + + + diff --git a/src/VAX/vax_cis.cpp b/src/VAX/vax_cis.cpp new file mode 100644 index 0000000..790e873 --- /dev/null +++ b/src/VAX/vax_cis.cpp @@ -0,0 +1,1704 @@ +/* vax_cis.c: VAX CIS instructions + + Copyright (c) 2004-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. + + On a full VAX, this module simulates the VAX commercial instruction set (CIS). + On a subset VAX, this module implements the emulated instruction fault. + + 16-Oct-08 RMS Fixed bug in ASHP left overflow calc (Word/NibbleLShift) + Fixed bug in DIVx (LntDstr calculation) + 28-May-08 RMS Inlined physical memory routines + 16-May-06 RMS Fixed bug in length calculation (found by Tim Stark) + 03-May-06 RMS Fixed MOVTC, MOVTUC to preserve cc's through page faults + Fixed MOVTUC to stop on translated == escape + Fixed CVTPL to set registers before destination reg write + Fixed CVTPL to set correct cc bit on overflow + Fixed EDITPC to preserve cc's through page faults + Fixed EDITPC EO$BLANK_ZERO count, cc test + Fixed EDITPC EO$INSERT to insert fill instead of blank + Fixed EDITPC EO$LOAD_PLUS/MINUS to skip character + (all reported by Tim Stark) + 12-Apr-04 RMS Cloned from pdp11_cis.c and vax_cpu1.c + + Zero length decimal strings require either zero bytes (trailing) or one byte + (separate, packed). + + CIS instructions can run for a very long time, so they are interruptible + and restartable. In the simulator, string instructions (and EDITPC) are + interruptible by faults, but decimal instructions run to completion. + The code is unoptimized. +*/ + +#include "sim_defs.h" +#include "vax_defs.h" +#include "vax_cpu.h" + +#if defined (FULL_VAX) + +/* Decimal string structure */ + +#define DSTRLNT 4 +#define DSTRMAX (DSTRLNT - 1) +#define MAXDVAL 429496730 /* 2^32 / 10 */ + +#define C_SPACE 0x20 /* ASCII chars */ +#define C_PLUS 0x2B +#define C_MINUS 0x2D +#define C_ZERO 0x30 +#define C_NINE 0x39 + +typedef struct { + uint32 sign; + uint32 val[DSTRLNT]; + } DSTR; + +const static DSTR Dstr_zero = { 0, 0, 0, 0, 0 }; +const static DSTR Dstr_one = { 0, 0x10, 0, 0, 0 }; + +int32 ReadDstr (int32 lnt, int32 addr, DSTR *dec, int32 acc); +int32 WriteDstr (int32 lnt, int32 addr, DSTR *dec, int32 v, int32 acc); +int32 SetCCDstr (int32 lnt, DSTR *src, int32 pslv); +int32 AddDstr (DSTR *src1, const DSTR *src2, DSTR *dst, int32 cin); +void SubDstr (DSTR *src1, DSTR *src2, DSTR *dst); +int32 CmpDstr (DSTR *src1, DSTR *src2); +int32 TestDstr (DSTR *dsrc); +void ProbeDstr (int32 lnt, int32 addr, int32 acc); +int32 LntDstr (DSTR *dsrc, int32 nz); +uint32 NibbleLshift (DSTR *dsrc, int32 sc, uint32 cin); +uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin); +int32 wordlshift (dstr *dsrc, int32 sc); +void WordRshift (DSTR *dsrc, int32 sc); +void CreateTable (DSTR *dsrc, DSTR mtable[10]); +int32 do_crc_4b (int32 crc, int32 tbl, int32 acc); +int32 edit_read_src (int32 inc, int32 acc); +void edit_adv_src (int32 inc); +int32 edit_read_sign (int32 acc); + +/* CIS emulator */ + +int32 op_cis (RUN_DECL, int32 *op, int32 cc, int32 opc, int32 acc) +{ +int32 i, j, c, t, pop, rpt, V; +int32 match, fill, sign, shift; +int32 ldivd, ldivr; +int32 lenl, lenp; +uint32 nc, d, result; +t_stat r; +DSTR accum, src1, src2, dst; +DSTR mptable[10]; + +switch (opc) { /* case on opcode */ + +/* MOVTC + + Operands if PSL = 0: + op[0:1] = source string descriptor + op[2] = fill character + op[3] = table address + op[4:5] = destination string descriptor + + Registers if PSL = 1: + R[0] = delta-PC/fill/source string length + R[1] = source string address + R[2] = number of bytes remaining to move + R[3] = table address + R[4] = saved cc's/destination string length + R[5] = destination string address + + Condition codes: + NZC = set from op[0]:op[4] + V = 0 + + Registers: + R0 = src length remaining, or 0 + R1 = addr of end of source string + 1 + R2 = 0 + R3 = table address + R4 = 0 + R5 = addr of end of dest string + 1 +*/ + + case MOVTC: + if (PSL & PSL_FPD) { /* FPD set? */ + SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ + fill = STR_GETCHR (R[0]); /* get fill */ + R[2] = R[2] & STR_LNMASK; /* remaining move */ + cc = (R[4] >> 16) & CC_MASK; /* restore cc's */ + } + else { + CC_CMP_W (op[0], op[4]); /* set cc's */ + R[0] = STR_PACK (op[2], op[0]); /* src len, fill */ + R[1] = op[1]; /* src addr */ + fill = op[2]; /* set fill */ + R[3] = op[3]; /* table addr */ + R[4] = op[4] | ((cc & CC_MASK) << 16); /* dst len + cc's */ + R[5] = op[5]; /* dst addr */ + R[2] = (op[0] < op[4])? op[0]: op[4]; /* remaining move */ + PSL = PSL | PSL_FPD; /* set FPD */ + } + if (R[2]) { /* move to do? */ + int32 mvl; + mvl = R[0] & STR_LNMASK; /* orig move len */ + if (mvl >= (R[4] & STR_LNMASK)) + mvl = R[4] & STR_LNMASK; + if (((uint32) R[1]) < ((uint32) R[5])) { /* backward? */ + while (R[2]) { /* loop thru char */ + t = Read (RUN_PASS, (R[1] + R[2] - 1) & LMASK, L_BYTE, RA); + c = Read (RUN_PASS, (R[3] + t) & LMASK, L_BYTE, RA); + Write (RUN_PASS, (R[5] + R[2] - 1) & LMASK, c, L_BYTE, WA); + R[2] = (R[2] - 1) & STR_LNMASK; + } + R[1] = (R[1] + mvl) & LMASK; /* adv src, dst */ + R[5] = (R[5] + mvl) & LMASK; + } + else { /* forward */ + while (R[2]) { /* loop thru char */ + t = Read (RUN_PASS, R[1], L_BYTE, RA); /* read src */ + c = Read (RUN_PASS, (R[3] + t) & LMASK, L_BYTE, RA); + Write (RUN_PASS, R[5], c, L_BYTE, WA); /* write dst */ + R[1] = (R[1] + 1) & LMASK; /* adv src, dst */ + R[2] = (R[2] - 1) & STR_LNMASK; + R[5] = (R[5] + 1) & LMASK; + } + } /* update lengths */ + R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - mvl) & STR_LNMASK); + R[4] = (R[4] & ~STR_LNMASK) | ((R[4] - mvl) & STR_LNMASK); + } + while (R[4] & STR_LNMASK) { /* fill if needed */ + Write (RUN_PASS, R[5], fill, L_BYTE, WA); + R[4] = (R[4] & ~STR_LNMASK) | ((R[4] - 1) & STR_LNMASK); + R[5] = (R[5] + 1) & LMASK; /* adv dst */ + } + R[0] = R[0] & STR_LNMASK; /* mask off state */ + R[4] = 0; + PSL = PSL & ~PSL_FPD; + return cc; + +/* MOVTUC + + Operands: + op[0:1] = source string descriptor + op[2] = escape character + op[3] = table address + op[4:5] = destination string descriptor + + Registers if PSL = 1: + R[0] = delta-PC/match/source string length + R[1] = source string address + R[2] = saved condition codes + R[3] = table address + R[4] = destination string length + R[5] = destination string address + + Condition codes: + NZC = set from op[0]:op[4] + V = 1 if match to escape character + + Registers: + R0 = src length remaining, or 0 + R1 = addr of end of source string + 1 + R2 = 0 + R3 = table address + R4 = dst length remaining, or 0 + R5 = addr of end of dest string + 1 +*/ + + case MOVTUC: + if (PSL & PSL_FPD) { /* FPD set? */ + SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ + fill = STR_GETCHR (R[0]); /* get match */ + R[4] = R[4] & STR_LNMASK; + cc = R[2] & CC_MASK; /* restore cc's */ + } + else { + CC_CMP_W (op[0], op[4]); /* set cc's */ + R[0] = STR_PACK (op[2], op[0]); /* src len, fill */ + R[1] = op[1]; /* src addr */ + fill = op[2]; /* set match */ + R[3] = op[3]; /* table addr */ + R[4] = op[4]; /* dst len */ + R[5] = op[5]; /* dst addr */ + R[2] = cc; /* save cc's */ + PSL = PSL | PSL_FPD; /* set FPD */ + } + while ((R[0] & STR_LNMASK) && R[4]) { /* while src & dst */ + t = Read (RUN_PASS, R[1], L_BYTE, RA); /* read src */ + c = Read (RUN_PASS, (R[3] + t) & LMASK, L_BYTE, RA); /* translate */ + if (c == fill) { /* stop char? */ + cc = cc | CC_V; /* set V, done */ + break; + } + Write (RUN_PASS, R[5], c, L_BYTE, WA); /* write dst */ + R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - 1) & STR_LNMASK); + R[1] = (R[1] + 1) & LMASK; + R[4] = (R[4] - 1) & STR_LNMASK; /* adv src, dst */ + R[5] = (R[5] + 1) & LMASK; + } + R[0] = R[0] & STR_LNMASK; /* mask off state */ + R[2] = 0; + PSL = PSL & ~PSL_FPD; + return cc; + +/* MATCHC + + Operands: + op[0:1] = substring descriptor + op[2:3] = string descriptor + + Registers if PSL = 1: + R[0] = delta-PC/match/substring length + R[1] = substring address + R[2] = source string length + R[3] = source string address + + Condition codes: + NZ = set from R0 + VC = 0 + + Registers: + R0 = if match, 0, else, op[0] + R1 = if match, op[0] + op[1], else, op[1] + R2 = if match, src bytes remaining, else, 0 + R3 = if match, end of substr, else, op[2] + op[3] + + Notes: + - If the string is zero length, and the substring is not, + the outer loop exits immediately, and the result is + "no match" + - If the substring is zero length, the inner loop always + exits immediately, and the result is a "match" + - If the string is zero length, and the substring is as + well, the outer loop executes, the inner loop exits + immediately, and the result is a match, but the result + is the length of the string (zero) + - This instruction can potentially run a very long time - worst + case execution on a real VAX-11/780 was more than 30 minutes. + Accordingly, the instruction tests for interrupts and stops + if one is found. +*/ + + case MATCHC: + if (PSL & PSL_FPD) /* FPD? */ + { + SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ + R[2] = R[2] & STR_LNMASK; + } + else + { + R[0] = STR_PACK (0, op[0]); /* srclen + FPD data */ + R[1] = op[1]; /* save operands */ + R[2] = op[2]; + R[3] = op[3]; + PSL = PSL | PSL_FPD; /* set FPD */ + } + for (match = 0; R[2] >= (R[0] & STR_LNMASK); ) + { + for (i = 0, match = 1; match && (i < (R[0] & STR_LNMASK)); i++) + { + c = Read (RUN_PASS, (R[1] + i) & LMASK, L_BYTE, RA); + t = Read (RUN_PASS, (R[3] + i) & LMASK, L_BYTE, RA); + match = (c == t); /* continue if match */ + } /* end for substring */ + if (match) /* exit if match */ + break; + R[2] = (R[2] - 1) & STR_LNMASK; /* decr src length */ + R[3] = (R[3] + 1) & LMASK; /* next string char */ + if (i >= sim_interval) /* done with interval? */ + { + sim_interval = 0; + if (r = sim_process_event (RUN_PASS)) /* presumably WRU */ + { + SETPC(fault_PC); /* backup up PC */ + ABORT (r); /* abort flushes IB */ + } + SET_IRQL; /* update interrupts */ + if (trpirq) /* pending? stop */ + ABORT (ABORT_INTR); + } + else + { + cpu_cycle(); + } + } /* end for string */ + R[0] = R[0] & STR_LNMASK; + if (match) /* if match */ + { + R[1] = (R[1] + R[0]) & LMASK; + R[2] = (R[2] - R[0]) & STR_LNMASK; + R[3] = (R[3] + R[0]) & LMASK; + R[0] = 0; + } + else + { /* if no match */ + R[3] = (R[3] + R[2]) & LMASK; + R[2] = 0; + } + PSL = PSL & ~PSL_FPD; + CC_IIZZ_L (R[0]); /* set cc's */ + return cc; + +/* CRC + + Operands: + op[0] = table address + op[1] = initial CRC + op[2:3] = source string descriptor + + Registers if PSL = 1: + R[0] = result + R[1] = table address + R[2] = delta-PC/0/source string length + R[3] = source string address + + Condition codes: + NZ = set from result + VC = 0 + + Registers: + R0 = result + R1 = 0 + R2 = 0 + R3 = addr + 1 of last byte in source string +*/ + + case CRC: + if (PSL & PSL_FPD) { /* FPD? */ + SETPC (fault_PC + STR_GETDPC (R[2])); /* reset PC */ + } + else { + R[2] = STR_PACK (0, op[2]); /* srclen + FPD data */ + R[0] = op[1]; /* save operands */ + R[1] = op[0]; + R[3] = op[3]; + PSL = PSL | PSL_FPD; /* set FPD */ + } + while ((R[2] & STR_LNMASK) != 0) { /* loop thru chars */ + c = Read (RUN_PASS, R[3], L_BYTE, RA); /* get char */ + t = R[0] ^ c; /* XOR to CRC */ + t = do_crc_4b (t, R[1], acc); /* do 4b shift */ + R[0] = do_crc_4b (t, R[1], acc); /* do 4b shift */ + R[3] = (R[3] + 1) & LMASK; + R[2] = R[2] - 1; + } + R[1] = 0; + R[2] = 0; + PSL = PSL & ~PSL_FPD; + CC_IIZZ_L (R[0]); /* set cc's */ + return cc; + +/* MOVP + + Operands: + op[0] = length + op[1] = source string address + op[2] = dest string address + + Condition codes: + NZ = set from result + V = 0 + C = unchanged + + Registers: + R0 = 0 + R1 = addr of source string + R2 = 0 + R3 = addr of dest string +*/ + + case MOVP: + if ((PSL & PSL_FPD) || (op[0] > 31)) + RSVD_OPND_FAULT; + ReadDstr (op[0], op[1], &dst, acc); /* read source */ + cc = WriteDstr (op[0], op[2], &dst, 0, acc) | /* write dest */ + (cc & CC_C); /* preserve C */ + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[2]; + return cc; + +/* ADDP4, ADDP6, SUBP4, SUBP6 + + Operands: + op[0:1] = src1 string descriptor + op[2:3] = src2 string descriptor + (ADDP6, SUBP6 only) + op[4:5] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of src1 string + R2 = 0 + R3 = addr of src2 string + (ADDP6, SUBP6 only) + R4 = 0 + R5 = addr of dest string +*/ + + case ADDP4: case SUBP4: + op[4] = op[2]; /* copy dst */ + op[5] = op[3]; + case ADDP6: case SUBP6: + if ((PSL & PSL_FPD) || (op[0] > 31) || + (op[2] > 31) || (op[4] > 31)) + RSVD_OPND_FAULT; + ReadDstr (op[0], op[1], &src1, acc); /* get src1 */ + ReadDstr (op[2], op[3], &src2, acc); /* get src2 */ + if (opc & 2) /* sub? invert sign */ + src1.sign = src1.sign ^ 1; + if (src1.sign ^ src2.sign) { /* opp signs? sub */ + if (CmpDstr (&src1, &src2) < 0) { /* src1 < src2? */ + SubDstr (&src1, &src2, &dst); /* src2 - src1 */ + dst.sign = src2.sign; /* sign = src2 */ + } + else { + SubDstr (&src2, &src1, &dst); /* src1 - src2 */ + dst.sign = src1.sign; /* sign = src1 */ + } + V = 0; /* can't carry */ + } + else { /* addition */ + V = AddDstr (&src1, &src2, &dst, 0); /* add magnitudes */ + dst.sign = src1.sign; /* set result sign */ + } + cc = WriteDstr (op[4], op[5], &dst, V, acc); /* store result */ + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[3]; + if (opc & 1) { /* ADDP6, SUBP6? */ + R[4] = 0; + R[5] = op[5]; + } + return cc; + +/* MULP + + Operands: + op[0:1] = src1 string descriptor + op[2:3] = src2 string descriptor + op[4:5] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of src1 string + R2 = 0 + R3 = addr of src2 string + R4 = 0 + R5 = addr of dest string +*/ + + case MULP: + if ((PSL & PSL_FPD) || (op[0] > 31) || + (op[2] > 31) || (op[4] > 31)) + RSVD_OPND_FAULT; + dst = Dstr_zero; /* clear result */ + if (ReadDstr (op[0], op[1], &src1, acc) && /* read src1, src2 */ + ReadDstr (op[2], op[3], &src2, acc)) { /* if both > 0 */ + dst.sign = src1.sign ^ src2.sign; /* sign of result */ + accum = Dstr_zero; /* clear accum */ + NibbleRshift (&src1, 1, 0); /* shift out sign */ + CreateTable (&src1, mptable); /* create *1, *2, ... */ + for (i = 1; i < (DSTRLNT * 8); i++) { /* 31 iterations */ + d = (src2.val[i / 8] >> ((i % 8) * 4)) & 0xF; + if (d > 0) /* add in digit*mpcnd */ + AddDstr (&mptable[d], &accum, &accum, 0); + nc = NibbleRshift (&accum, 1, 0); /* ac right 4 */ + NibbleRshift (&dst, 1, nc); /* result right 4 */ + } + V = TestDstr (&accum) != 0; /* if ovflo, set V */ + } + else V = 0; /* result = 0 */ + cc = WriteDstr (op[4], op[5], &dst, V, acc); /* store result */ + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[3]; + R[4] = 0; + R[5] = op[5]; + return cc; + +/* DIVP + + Operands: + op[0:1] = src1 string descriptor + op[2:3] = src2 string descriptor + op[4:5] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of src1 string + R2 = 0 + R3 = addr of src2 string + R4 = 0 + R5 = addr of dest string +*/ + + case DIVP: + if ((PSL & PSL_FPD) || (op[0] > 31) || + (op[2] > 31) || (op[4] > 31)) + RSVD_OPND_FAULT; + ldivr = ReadDstr (op[0], op[1], &src1, acc); /* get divisor */ + if (ldivr == 0) { /* divisor = 0? */ + SET_TRAP (TRAP_FLTDIV); /* dec div trap */ + return cc; + } + ldivr = LntDstr (&src1, ldivr); /* get exact length */ + ldivd = ReadDstr (op[2], op[3], &src2, acc); /* get dividend */ + ldivd = LntDstr (&src2, ldivd); /* get exact length */ + dst = Dstr_zero; /* clear dest */ + NibbleRshift (&src1, 1, 0); /* right justify ops */ + NibbleRshift (&src2, 1, 0); + if ((t = ldivd - ldivr) >= 0) { /* any divide to do? */ + dst.sign = src1.sign ^ src2.sign; /* calculate sign */ + WordLshift (&src1, t / 8); /* align divr to divd */ + NibbleLshift (&src1, t % 8, 0); + CreateTable (&src1, mptable); /* create *1, *2, ... */ + for (i = 0; i <= t; i++) { /* divide loop */ + for (d = 9; d > 0; d--) { /* find digit */ + if (CmpDstr (&src2, &mptable[d]) >= 0) { + SubDstr (&mptable[d], &src2, &src2); + dst.val[0] = dst.val[0] | d; + break; + } /* end if */ + } /* end for */ + NibbleLshift (&src2, 1, 0); /* shift dividend */ + NibbleLshift (&dst, 1, 0); /* shift quotient */ + } /* end divide loop */ + } /* end if */ + cc = WriteDstr (op[4], op[5], &dst, 0, acc); /* store result */ + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[3]; + R[4] = 0; + R[5] = op[5]; + return cc; + +/* CMPP3, CMPP4 + + Operands (CMPP3): + op[0] = string length + op[1], op[2] = string lengths + + Operands (CMPP4): + op[0:1] = string1 descriptor + op[2:3] = string2 descriptor + + Condition codes: + NZ = set from comparison + VC = 0 + + Registers: + R0 = 0 + R1 = addr of src1 string + R2 = 0 + R3 = addr of src2 string +*/ + + case CMPP3: + op[3] = op[2]; /* reposition ops */ + op[2] = op[0]; + case CMPP4: + if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31)) + RSVD_OPND_FAULT; + ReadDstr (op[0], op[1], &src1, acc); /* get src1 */ + ReadDstr (op[2], op[3], &src2, acc); /* get src2 */ + cc = 0; + if (src1.sign != src2.sign) cc = (src1.sign)? CC_N: 0; + else { + t = CmpDstr (&src1, &src2); /* compare strings */ + if (t < 0) + cc = (src1.sign? 0: CC_N); + else if (t > 0) + cc = (src1.sign? CC_N: 0); + else cc = CC_Z; + } + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[3]; + return cc ; + +/* ASHP + + Operands: + op[0] = shift count + op[1:2] = source string descriptor + op[3] = round digit + op[4:5] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of src1 string + R2 = 0 + R3 = addr of src2 string +*/ + + case ASHP: + if ((PSL & PSL_FPD) || (op[1] > 31) || (op[4] > 31)) + RSVD_OPND_FAULT; + ReadDstr (op[1], op[2], &src1, acc); /* get source */ + V = 0; /* init V */ + shift = op[0]; /* get shift count */ + if (shift & BSIGN) { /* right shift? */ + shift = BMASK + 1 - shift; /* !shift! */ + WordRshift (&src1, shift / 8); /* do word shifts */ + NibbleRshift (&src1, shift % 8, 0); /* do nibble shifts */ + t = op[3] & 0xF; /* get round nibble */ + if ((t + (src1.val[0] & 0xF)) > 9) /* rounding needed? */ + AddDstr (&src1, &Dstr_one, &src1, 0); /* round */ + src1.val[0] = src1.val[0] & ~0xF; /* clear sign */ + } /* end right shift */ + else if (shift) { /* left shift? */ + if (WordLshift (&src1, shift / 8)) /* do word shifts */ + V = 1; + if (NibbleLshift (&src1, shift % 8, 0)) + V = 1; + } /* end left shift */ + cc = WriteDstr (op[4], op[5], &src1, V, acc); /* store result */ + R[0] = 0; + R[1] = op[2]; + R[2] = 0; + R[3] = op[5]; + return cc ; + +/* CVTPL + + Operands: + op[0:1] = source string descriptor + op[2] = memory flag/register number + op[3] = memory address + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of source string + R2 = 0 + R3 = 0 +*/ + + case CVTPL: + if ((PSL & PSL_FPD) || (op[0] > 31)) + RSVD_OPND_FAULT; + ReadDstr (op[0], op[1], &src1, acc); /* get source */ + V = result = 0; /* clear V, result */ + for (i = (DSTRLNT * 8) - 1; i > 0; i--) { /* loop thru digits */ + d = (src1.val[i / 8] >> ((i % 8) * 4)) & 0xF; + if (d || result || V) { /* skip initial 0's */ + if (result >= MAXDVAL) + V = 1; + result = ((result * 10) + d) & LMASK; + if (result < d) + V = 1; + } /* end if */ + } /* end for */ + if (src1.sign) /* negative? */ + result = (~result + 1) & LMASK; + if (src1.sign ^ ((result & LSIGN) != 0)) /* test for overflow */ + V = 1; + if (op[2] < 0) /* if mem, store result */ + Write (RUN_PASS, op[3], result, L_LONG, WA); /* before reg update */ + R[0] = 0; /* update registers */ + R[1] = op[1]; + R[2] = 0; + R[3] = 0; + if (op[2] >= 0) /* if reg, store result */ + R[op[2]] = result; /* after reg update */ + if (V && (PSL & PSW_IV)) /* ovflo and IV? trap */ + SET_TRAP (TRAP_INTOV); + CC_IIZZ_L (result); + return cc | (V? CC_V: 0); + +/* CVTLP + + Operands: + op[0] = source long + op[1:2] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = 0 + R2 = 0 + R3 = addr of dest string +*/ + + case CVTLP: + if ((PSL & PSL_FPD) || (op[1] > 31)) + RSVD_OPND_FAULT; + dst = Dstr_zero; /* clear result */ + result = op[0]; + if ((result & LSIGN) != 0) { + dst.sign = 1; + result = (~result + 1) & LMASK; + } + for (i = 1; (i < (DSTRLNT * 8)) && result; i++) { + d = result % 10; + result = result / 10; + dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4)); + } + cc = WriteDstr (op[1], op[2], &dst, 0, acc); /* write result */ + R[0] = 0; + R[1] = 0; + R[2] = 0; + R[3] = op[2]; + return cc; + +/* CVTSP + + Operands: + op[0:1] = source string descriptor + op[2:3] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = address of sign byte of source string + R2 = 0 + R3 = addr of dest string +*/ + + case CVTSP: + if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31)) + RSVD_OPND_FAULT; + dst = Dstr_zero; /* clear result */ + t = Read (RUN_PASS, op[1], L_BYTE, RA); /* read source sign */ + if (t == C_MINUS) /* sign -, */ + dst.sign = 1; + else if ((t != C_PLUS) && (t != C_SPACE)) /* + or blank? */ + RSVD_OPND_FAULT; + for (i = 1; i <= op[0]; i++) { /* loop thru chars */ + c = Read (RUN_PASS, (op[1] + op[0] + 1 - i) & LMASK, L_BYTE, RA); + if ((c < C_ZERO) || (c > C_NINE)) /* [0:9]? */ + RSVD_OPND_FAULT; + d = c & 0xF; + dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4)); + } + TestDstr (&dst); /* correct -0 */ + cc = WriteDstr (op[2], op[3], &dst, 0, acc); /* write result */ + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[3]; + return cc; + +/* CVTPS + + Operands: + op[0:1] = source string descriptor + op[2:3] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of source string + R2 = 0 + R3 = addr of dest string +*/ + + case CVTPS: + if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31)) + RSVD_OPND_FAULT; + lenl = ReadDstr (op[0], op[1], &dst, acc); /* get source, lw len */ + lenp = LntDstr (&dst, lenl); /* get exact nz src len */ + ProbeDstr (op[2], op[3], WA); /* test dst write */ + Write (RUN_PASS, op[3], dst.sign? C_MINUS: C_PLUS, L_BYTE, WA); + for (i = 1; i <= op[2]; i++) { /* loop thru chars */ + d = (dst.val[i / 8] >> ((i % 8) * 4)) & 0xF;/* get digit */ + c = d | C_ZERO; /* cvt to ASCII */ + Write (RUN_PASS, (op[3] + op[2] + 1 - i) & LMASK, c, L_BYTE, WA); + } + cc = SetCCDstr (op[0], &dst, 0); /* set cc's */ + if (lenp > op[2]) { /* src fit in dst? */ + cc = cc | CC_V; /* set ovflo */ + if (PSL & PSW_DV) SET_TRAP (TRAP_DECOVF); /* if enabled, trap */ + } + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[3]; + return cc; + +/* CVTTP + + Operands: + op[0:1] = source string descriptor + op[2] = table address + op[3:4] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of source string + R2 = 0 + R3 = addr of dest string +*/ + + case CVTTP: + if ((PSL & PSL_FPD) || (op[0] > 31) || (op[3] > 31)) + RSVD_OPND_FAULT; + dst = Dstr_zero; /* clear result */ + for (i = 1; i <= op[0]; i++) { /* loop thru char */ + c = Read (RUN_PASS, (op[1] + op[0] - i) & LMASK, L_BYTE, RA); /* read char */ + if (i != 1) { /* normal byte? */ + if ((c < C_ZERO) || (c > C_NINE)) /* valid digit? */ + RSVD_OPND_FAULT; + d = c & 0xF; + } + else { /* highest byte */ + t = Read (RUN_PASS, (op[2] + c) & LMASK, L_BYTE, RA); /* xlate */ + d = (t >> 4) & 0xF; /* digit */ + t = t & 0xF; /* sign */ + if ((d > 0x9) || (t < 0xA)) + RSVD_OPND_FAULT; + if ((t == 0xB) || (t == 0xD)) + dst.sign = 1; + } + dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4)); + } + TestDstr (&dst); /* correct -0 */ + cc = WriteDstr (op[3], op[4], &dst, 0, acc); /* write result */ + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[4]; + return cc; + +/* CVTPT + + Operands: + op[0:1] = source string descriptor + op[2] = table address + op[3:4] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of source string + R2 = 0 + R3 = addr of dest string +*/ + + case CVTPT: + if ((PSL & PSL_FPD) || (op[0] > 31) || (op[3] > 31)) + RSVD_OPND_FAULT; + lenl = ReadDstr (op[0], op[1], &dst, acc); /* get source, lw len */ + lenp = LntDstr (&dst, lenl); /* get exact src len */ + ProbeDstr (op[3], op[4], WA); /* test writeability */ + for (i = 1; i <= op[3]; i++) { /* loop thru chars */ + if (i != 1) { /* not last? */ + d = (dst.val[i / 8] >> ((i % 8) * 4)) & 0xF; /* get digit */ + c = d + C_ZERO; /* convert */ + } + else { /* translate last */ + t = Read (RUN_PASS, (op[1] + (op[0] / 2)) & LMASK, L_BYTE, RA); + c = Read (RUN_PASS, (op[2] + t) & LMASK, L_BYTE, RA); + } + Write (RUN_PASS, (op[4] + op[3] - i) & LMASK, c, L_BYTE, WA); + } + cc = SetCCDstr (op[0], &dst, 0); /* set cc's from src */ + if (lenp > op[3]) { /* src fit in dst? */ + cc = cc | CC_V; /* set ovflo */ + if (PSL & PSW_DV) SET_TRAP (TRAP_DECOVF); /* if enabled, trap */ + } + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[4]; + return cc; + +/* EDITPC + + Operands: + op[0:1] = source string descriptor + op[2] = pattern string address + op[3] = dest string address + + Condition codes: + N = source is negative + Z = source is zero + V = significant digits lost + C = significant digits seen + + Registers at packup: + R0<31:16> = -count of source zeroes to supply + R0<15:0> = remaining source length + R1 = source address + R2<31:24> = delta PC + R2<19:16> = condition codes + R2<15:8> = sign char + R2<7:0> = fill char + R3 = pattern string address + R4 = original source length + R5 = dest string addr + + Registers at end: + R0 = source length + R1 = source addr + R2 = 0 + R3 = addr of byte containing EO$END + R4 = 0 + R5 = addr of end of dst string + 1 + + Fault and abort conditions for EDITPC are complicated. In general: + - It is safe to take a memory management fault on the read of + any pattern byte. After correction of the fault, the pattern + operator is fetched and executed again. + - It is safe to take a memory management fault on a write-only + operation, like fill. After correction of the fault, the + pattern operator is fetched and executed again. + - The move operators do not alter visible state (registers or saved cc) + until all memory operations are complete. +*/ + + case EDITPC: + if (PSL & PSL_FPD) { /* FPD set? */ + SETPC (fault_PC + STR_GETDPC (R[2])); /* reset PC */ + fill = ED_GETFILL (R[2]); /* get fill */ + sign = ED_GETSIGN (R[2]); /* get sign */ + cc = ED_GETCC (R[2]); /* get cc's */ + R[0] = R[0] & ~0xFFE0; /* src len <= 31 */ + } + else { /* new instr */ + if (op[0] > 31) /* lnt > 31? */ + RSVD_OPND_FAULT; + t = Read (RUN_PASS, (op[1] + (op[0] / 2)) & LMASK, L_BYTE, RA) & 0xF; + if ((t == 0xB) || (t == 0xD)) { + cc = CC_N | CC_Z; + sign = C_MINUS; + } + else { + cc = CC_Z; + sign = C_SPACE; + } + fill = C_SPACE; + R[0] = R[4] = op[0]; /* src len */ + R[1] = op[1]; /* src addr */ + R[2] = STR_PACK (cc, (sign << ED_V_SIGN) | (fill << ED_V_FILL)); + /* delta PC, cc, sign, fill */ + R[3] = op[2]; /* pattern */ + R[5] = op[3]; /* dst addr */ + PSL = PSL | PSL_FPD; /* set FPD */ + } + + for ( ;; ) { /* loop thru pattern */ + pop = Read (RUN_PASS, R[3], L_BYTE, RA); /* rd pattern op */ + if (pop == EO_END) /* end? */ + break; + if (pop & EO_RPT_FLAG) { /* repeat class? */ + rpt = pop & EO_RPT_MASK; /* isolate count */ + if (rpt == 0) /* can't be zero */ + RSVD_OPND_FAULT; + pop = pop & ~EO_RPT_MASK; /* isolate op */ + } + switch (pop) { /* case on op */ + + case EO_END_FLOAT: /* end float */ + if (!(cc & CC_C)) { /* not signif? */ + Write (RUN_PASS, R[5], sign, L_BYTE, WA); /* write sign */ + R[5] = (R[5] + 1) & LMASK; /* now fault safe */ + cc = cc | CC_C; /* set signif */ + } + break; + + case EO_CLR_SIGNIF: /* clear signif */ + cc = cc & ~CC_C; /* clr C */ + break; + + case EO_SET_SIGNIF: /* set signif */ + cc = cc | CC_C; /* set C */ + break; + + case EO_STORE_SIGN: /* store sign */ + Write (RUN_PASS, R[5], sign, L_BYTE, WA); /* write sign */ + R[5] = (R[5] + 1) & LMASK; /* now fault safe */ + break; + + case EO_LOAD_FILL: /* load fill */ + fill = Read (RUN_PASS, (R[3] + 1) & LMASK, L_BYTE, RA); + R[2] = ED_PUTFILL (R[2], fill); /* now fault safe */ + R[3]++; + break; + + case EO_LOAD_SIGN: /* load sign */ + sign = edit_read_sign (acc); + R[3]++; + break; + + case EO_LOAD_PLUS: /* load sign if + */ + if (!(cc & CC_N)) + sign = edit_read_sign (acc); + R[3]++; + break; + + case EO_LOAD_MINUS: /* load sign if - */ + if (cc & CC_N) + sign = edit_read_sign (acc); + R[3]++; + break; + + case EO_INSERT: /* insert char */ + c = Read (RUN_PASS, (R[3] + 1) & LMASK, L_BYTE, RA); + Write (RUN_PASS, R[5], ((cc & CC_C)? c: fill), L_BYTE, WA); + R[5] = (R[5] + 1) & LMASK; /* now fault safe */ + R[3]++; + break; + + case EO_BLANK_ZERO: /* blank zero */ + t = Read (RUN_PASS, (R[3] + 1) & LMASK, L_BYTE, RA); + if (t == 0) + RSVD_OPND_FAULT; + if (cc & CC_Z) { /* zero? */ + do { /* repeat and blank */ + Write (RUN_PASS, (R[5] - t) & LMASK, fill, L_BYTE, WA); + } while (--t); + } + R[3]++; /* now fault safe */ + break; + + case EO_REPL_SIGN: /* replace sign */ + t = Read (RUN_PASS, (R[3] + 1) & LMASK, L_BYTE, RA); + if (t == 0) + RSVD_OPND_FAULT; + if (cc & CC_Z) + Write (RUN_PASS, (R[5] - t) & LMASK, fill, L_BYTE, WA); + R[3]++; /* now fault safe */ + break; + + case EO_ADJUST_LNT: /* adjust length */ + t = Read (RUN_PASS, (R[3] + 1) & LMASK, L_BYTE, RA); + if ((t == 0) || (t > 31)) + RSVD_OPND_FAULT; + R[0] = R[0] & WMASK; /* clr old ld zero */ + if (R[0] > t) { /* decrease */ + for (i = 0; i < (R[0] - t); i++) { /* loop thru src */ + d = edit_read_src (i, acc); /* get nibble */ + if (d) + cc = (cc | CC_V | CC_C) & ~CC_Z; + } /* end for */ + edit_adv_src (R[0] - t); /* adv src ptr */ + } /* end else */ + else R[0] = R[0] | (((R[0] - t) & WMASK) << 16); + R[3]++; + break; + + case EO_FILL: /* fill */ + for (i = 0; i < rpt; i++) /* fill string */ + Write (RUN_PASS, (R[5] + i) & LMASK, fill, L_BYTE, WA); + R[5] = (R[5] + rpt) & LMASK; /* now fault safe */ + break; + + case EO_MOVE: + for (i = 0; i < rpt; i++) { /* for repeat */ + d = edit_read_src (i, acc); /* get nibble */ + if (d) /* test for non-zero */ + cc = (cc | CC_C) & ~CC_Z; + c = (cc & CC_C)? (d | 0x30): fill; /* test for signif */ + Write (RUN_PASS, (R[5] + i) & LMASK, c, L_BYTE, WA); + } /* end for */ + edit_adv_src (rpt); /* advance src */ + R[5] = (R[5] + rpt) & LMASK; /* advance dst */ + break; + + case EO_FLOAT: + for (i = j = 0; i < rpt; i++, j++) { /* for repeat */ + d = edit_read_src (i, acc); /* get nibble */ + if (d && !(cc & CC_C)) { /* nz, signif clear? */ + Write (RUN_PASS, (R[5] + j) & LMASK, sign, L_BYTE, WA); + cc = (cc | CC_C) & ~CC_Z; /* set signif */ + j++; /* extra dst char */ + } /* end if */ + c = (cc & CC_C)? (d | 0x30): fill; /* test for signif */ + Write (RUN_PASS, (R[5] + j) & LMASK, c, L_BYTE, WA); + } /* end for */ + edit_adv_src (rpt); /* advance src */ + R[5] = (R[5] + j) & LMASK; /* advance dst */ + break; + + default: /* undefined */ + RSVD_OPND_FAULT; + } /* end case pattern */ + + R[3] = (R[3] + 1) & LMASK; /* next pattern byte */ + R[2] = ED_PUTCC (R[2], cc); /* update cc's */ + } /* end for pattern */ + + if (R[0]) /* pattern too short */ + RSVD_OPND_FAULT; + PSL = PSL & ~PSL_FPD; /* clear FPD */ + if (cc & CC_Z) /* zero? clear n */ + cc = cc & ~CC_N; + if ((cc & CC_V) && (PSL & PSW_DV)) /* overflow & trap enabled? */ + SET_TRAP (TRAP_DECOVF); + R[0] = R[4]; /* restore src len */ + R[1] = R[1] - (R[0] >> 1); /* restore src addr */ + R[2] = R[4] = 0; + return cc; + + default: + RSVD_INST_FAULT; + } + /* end case op */ +return cc; +} + +/* Get packed decimal string + + Arguments: + lnt = decimal string length + adr = decimal string address + src = decimal string structure + acc = access mode + + The routine returns the length in int32's of the non-zero part of + the string. + + To simplify the code elsewhere, digits are range checked, + and bad digits cause a fault. +*/ + +int32 ReadDstr (int32 lnt, int32 adr, DSTR *src, int32 acc) +{ +int32 c, i, end, t; + +*src = Dstr_zero; /* clear result */ +end = lnt / 2; /* last byte */ +for (i = 0; i <= end; i++) { /* loop thru string */ + c = Read (RUN_PASS, (adr + end - i) & LMASK, L_BYTE, RA); /* get byte */ + if (i == 0) { /* sign char? */ + t = c & 0xF; /* save sign */ + c = c & 0xF0; /* erase sign */ + } + if ((i == end) && ((lnt & 1) == 0)) + c = c & 0xF; +// if (((c & 0xF0) > 0x90) || /* check hi digit */ +// ((c & 0x0F) > 0x09)) /* check lo digit */ +// RSVD_OPND_FAULT; + src->val[i / 4] = src->val[i / 4] | (c << ((i % 4) * 8)); + } /* end for */ +if ((t == 0xB) || (t == 0xD)) /* if -, set sign */ + src->sign = 1; +return TestDstr (src); /* clean -0 */ +} + +/* Store decimal string + + Arguments: + lnt = decimal string length + adr = decimal string address + dst = decimal string structure + V = initial overflow flag + acc = access mode + + Returns condition codes. + + PSL.NZ are also set to their proper values + PSL.V will be set on overflow; it must be initialized elsewhere + (to allow for external overflow calculations) + + The rules for the stored sign and the PSW sign are: + + - Stored sign is negative if input is negative, and the result + is non-zero or there was overflow + - PSL sign is negative if input is negative, and the result is + non-zero + + Thus, the stored sign and the PSL sign will differ in one case: + a negative zero generated by overflow is stored with a negative + sign, but PSL.N is clear +*/ + +int32 WriteDstr (int32 lnt, int32 adr, DSTR *dst, int32 pslv, int32 acc) +{ +int32 c, i, cc, end; + +end = lnt / 2; /* end of string */ +ProbeDstr (end, adr, WA); /* test writeability */ +cc = SetCCDstr (lnt, dst, pslv); /* set cond codes */ +dst->val[0] = dst->val[0] | 0xC | dst->sign; /* set sign */ +for (i = 0; i <= end; i++) { /* store string */ + c = (dst->val[i / 4] >> ((i % 4) * 8)) & 0xFF; + Write (RUN_PASS, (adr + end - i) & LMASK, c, L_BYTE, WA); + } /* end for */ +return cc; +} + +/* Set CC for decimal string + + Arguments: + lnt = string length + dst = decimal string structure + pslv = initial V + + Output: + cc = condition codes +*/ + +int32 SetCCDstr (int32 lnt, DSTR *dst, int32 pslv) +{ +int32 psln, pslz, i, limit; +uint32 mask; +const static uint32 masktab[8] = { + 0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000, + 0xFFF00000, 0xFF000000, 0xF0000000, 0x00000000 + }; + +mask = 0; /* can't ovflo */ +pslz = 1; /* assume all 0's */ +limit = lnt / 8; /* limit for test */ +for (i = 0; i < DSTRLNT; i++) { /* loop thru value */ + if (i == limit) /* at limit, get mask */ + mask = masktab[lnt % 8]; + else if (i > limit) /* beyond, all ovflo */ + mask = 0xFFFFFFFF; + if (dst->val[i] & mask) /* test for ovflo */ + pslv = 1; + dst->val[i] = dst->val[i] & ~mask; /* clr digits past end */ + if (dst->val[i]) /* test nz */ + pslz = 0; + } +dst->sign = dst->sign & ~(pslz & ~pslv); +psln = dst->sign & ~pslz; /* N = sign, if ~zero */ +if (pslv && (PSL & PSW_DV)) + SET_TRAP (TRAP_DECOVF); +return (psln? CC_N: 0) | (pslz? CC_Z: 0) | (pslv? CC_V: 0); +} + +/* Probe decimal string for accessibility */ + +void ProbeDstr (int32 lnt, int32 addr, int32 acc) +{ +Read (RUN_PASS, addr, L_BYTE, acc); +Read (RUN_PASS, (addr + lnt) & LMASK, L_BYTE, acc); +return; +} + +/* Add decimal string magnitudes + + Arguments: + s1 = src1 decimal string + s2 = src2 decimal string + ds = dest decimal string + cy = carry in + Output = 1 if carry, 0 if no carry + + This algorithm courtesy Anton Chernoff, circa 1992 or even earlier. + + We trace the history of a pair of adjacent digits to see how the + carry is fixed; each parenthesized item is a 4b digit. + + Assume we are adding: + + (a)(b) I + + (x)(y) J + + First compute I^J: + + (a^x)(b^y) TMP + + Note that the low bit of each digit is the same as the low bit of + the sum of the digits, ignoring the carry, since the low bit of the + sum is the xor of the bits. + + Now compute I+J+66 to get decimal addition with carry forced left + one digit: + + (a+x+6+carry mod 16)(b+y+6 mod 16) SUM + + Note that if there was a carry from b+y+6, then the low bit of the + left digit is different from the expected low bit from the xor. + If we xor this SUM into TMP, then the low bit of each digit is 1 + if there was a carry, and 0 if not. We need to subtract 6 from each + digit that did not have a carry, so take ~(SUM ^ TMP) & 0x11, shift + it right 4 to the digits that are affected, and subtract 6*adjustment + (actually, shift it right 3 and subtract 3*adjustment). +*/ + +int32 AddDstr (DSTR *s1, const DSTR *s2, DSTR *ds, int32 cy) +{ +int32 i; +uint32 sm1, sm2, tm1, tm2, tm3, tm4; + +for (i = 0; i < DSTRLNT; i++) { /* loop low to high */ + tm1 = s1->val[i] ^ (s2->val[i] + cy); /* xor operands */ + sm1 = s1->val[i] + (s2->val[i] + cy); /* sum operands */ + sm2 = sm1 + 0x66666666; /* force carry out */ + cy = ((sm1 < s1->val[i]) || (sm2 < sm1)); /* check for overflow */ + tm2 = tm1 ^ sm2; /* get carry flags */ + tm3 = (tm2 >> 3) | (cy << 29); /* compute adjustment */ + tm4 = 0x22222222 & ~tm3; /* clear where carry */ + ds->val[i] = (sm2 - (3 * tm4)) & LMASK; /* final result */ + } +return cy; +} + +/* Subtract decimal string magnitudes + + Arguments: + s1 = src1 decimal string + s2 = src2 decimal string + ds = dest decimal string + Outputs: s2 - s1 in ds + + Note: the routine assumes that s1 <= s2 + +*/ + +void SubDstr (DSTR *s1, DSTR *s2, DSTR *ds) +{ +int32 i; +DSTR compl; + +for (i = 0; i < DSTRLNT; i++) /* 10's comp s2 */ + compl.val[i] = 0x99999999 - s1->val[i]; +AddDstr (&compl, s2, ds, 1); /* s1 + ~s2 + 1 */ +return; +} + +/* Compare decimal string magnitudes + + Arguments: + s1 = src1 decimal string + s2 = src2 decimal string + Output = 1 if >, 0 if =, -1 if < +*/ + +int32 CmpDstr (DSTR *s1, DSTR *s2) +{ +int32 i; + +for (i = DSTRMAX; i >=0; i--) { + if (s1->val[i] > s2->val[i]) + return 1; + if (s1->val[i] < s2->val[i]) + return -1; + } +return 0; +} + +/* Test decimal string for zero + + Arguments: + dsrc = decimal string structure + + Returns the non-zero length of the string, in int32 units + If the string is zero, the sign is cleared +*/ + +int32 TestDstr (DSTR *dsrc) +{ +int32 i; + +for (i = DSTRMAX; i >= 0; i--) { + if (dsrc->val[i]) + return (i + 1); + } +dsrc->sign = 0; +return 0; +} + +/* Get exact length of decimal string + + Arguments: + dsrc = decimal string structure + nz = result from TestDstr +*/ + +int32 LntDstr (DSTR *dsrc, int32 nz) +{ +int32 i; + +if (nz == 0) + return 0; +for (i = 7; i >= 0; i--) { + if ((dsrc->val[nz - 1] >> (i * 4)) & 0xF) + break; + } +return ((nz - 1) * 8) + i; +} + +/* Create table of multiples + + Arguments: + dsrc = base decimal string structure + mtable[10] = array of decimal string structures + + Note that dsrc has a high order zero nibble; this + guarantees that the largest multiple won't overflow + Also note that mtable[0] is not filled in +*/ + +void CreateTable (DSTR *dsrc, DSTR mtable[10]) +{ +int32 (i); + +mtable[1] = *dsrc; +for (i = 2; i < 10; i++) + AddDstr (&mtable[1], &mtable[i-1], &mtable[i], 0); +return; +} + +/* Word shift right + + Arguments: + dsrc = decimal string structure + sc = shift count +*/ + +void WordRshift (DSTR *dsrc, int32 sc) +{ +int32 i; + +if (sc) { + for (i = 0; i < DSTRLNT; i++) { + if ((i + sc) < DSTRLNT) + dsrc->val[i] = dsrc->val[i + sc]; + else dsrc->val[i] = 0; + } + } +return; +} + +/* Word shift left + + Arguments: + dsrc = decimal string structure + sc = shift count +*/ + +int32 WordLshift (DSTR *dsrc, int32 sc) +{ +int32 i, c; + +c = 0; +if (sc) { + for (i = DSTRMAX; i >= 0; i--) { + if (i >= sc) + dsrc->val[i] = dsrc->val[i - sc]; + else { + c |= dsrc->val[i]; + dsrc->val[i] = 0; + } + } + } +return c; +} + +/* Nibble shift decimal string right + + Arguments: + dsrc = decimal string structure + sc = shift count + cin = carry in +*/ + +uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin) +{ +int32 i, s, nc; + +if (s = sc * 4) { + for (i = DSTRMAX; i >= 0; i--) { + nc = (dsrc->val[i] << (32 - s)) & LMASK; + dsrc->val[i] = ((dsrc->val[i] >> s) | + cin) & LMASK; + cin = nc; + } + return cin; + } +return 0; +} + +/* Nibble shift decimal string left + + Arguments: + dsrc = decimal string structure + sc = shift count + cin = carry in +*/ + +uint32 NibbleLshift (DSTR *dsrc, int32 sc, uint32 cin) +{ +int32 i, s, nc; + +if (s = sc * 4) { + for (i = 0; i < DSTRLNT; i++) { + nc = dsrc->val[i] >> (32 - s); + dsrc->val[i] = ((dsrc->val[i] << s) | + cin) & LMASK; + cin = nc; + } + return cin; + } +return 0; +} + +/* Do 4b of CRC calculation + + Arguments: + crc = current CRC ^ char + tbl = 16 lw table base + + Output: + new CRC +*/ + +int32 do_crc_4b (int32 crc, int32 tbl, int32 acc) +{ +int32 idx = (crc & 0xF) << 2; +int32 t; + +crc = (crc >> 4) & 0x0FFFFFFF; +t = Read (RUN_PASS, (tbl + idx) & LMASK, L_LONG, RA); +return crc ^ t; +} + +/* Edit routines */ + +int32 edit_read_src (int32 inc, int32 acc) +{ +int32 c, r0, r1; + +if (R[0] & LSIGN) { /* ld zeroes? */ + r0 = (R[0] + (inc << 16)) & LMASK; /* retire increment */ + if (r0 & LSIGN) /* more? return 0 */ + return 0; + inc = (r0 >> 16) & 0x1F; /* effective inc */ + } +r1 = (R[1] + (inc / 2) + ((~R[0] & inc) & 1)) & LMASK; /* eff addr */ +r0 = (R[0] - inc) & 0x1F; /* eff lnt left */ +if (r0 == 0) { /* nothing left? */ + R[0] = -1; /* out of input */ + RSVD_OPND_FAULT; + } +c = Read (RUN_PASS, r1, L_BYTE, RA); +return (((r0 & 1)? (c >> 4): c) & 0xF); +} + +void edit_adv_src (int32 inc) +{ +if (R[0] & LSIGN) { /* ld zeroes? */ + R[0] = (R[0] + (inc << 16)) & LMASK; /* retire 0's */ + if (R[0] & LSIGN) /* more to do? */ + return; + inc = (R[0] >> 16) & 0x1F; /* get excess */ + if (inc == 0) /* more to do? */ + return; + } +R[1] = (R[1] + (inc / 2) + ((~R[0] & inc) & 1)) & LMASK;/* retire src */ +R[0] = (R[0] - inc) & 0x1F; +return; +} + +int32 edit_read_sign (int32 acc) +{ +int32 sign; + +sign = Read (RUN_PASS, (R[3] + 1) & LMASK, L_BYTE, RA); /* read */ +R[2] = ED_PUTSIGN (R[2], sign); /* now fault safe */ +return sign; +} + +#else + +/* CIS instructions - invoke emulator interface + + opnd[0:5] = six operands to be pushed (if PSL = 0) + cc = condition codes + opc = opcode + + If FPD is set, push old PC and PSL on stack, vector thru SCB. + If FPD is clear, push opcode, old PC, operands, new PC, and PSL + on stack, vector thru SCB. + In both cases, the exception occurs in the current mode. +*/ + +int32 op_cis (RUN_DECL, int32 *opnd, int32 cc, int32 opc, int32 acc) +{ +int32 vec; + +if (PSL & PSL_FPD) { /* FPD set? */ + Read (RUN_PASS, SP - 1, L_BYTE, WA); /* wchk stack */ + Write (RUN_PASS, SP - 8, fault_PC, L_LONG, WA); /* push old PC */ + Write (RUN_PASS, SP - 4, PSL | cc, L_LONG, WA); /* push PSL */ + SP = SP - 8; /* decr stk ptr */ + vec = ReadLP (RUN_PASS, (SCBB + SCB_EMULFPD) & PAMASK); + } +else { + if (opc == CVTPL) /* CVTPL? .wl */ + opnd[2] = (opnd[2] >= 0)? ~opnd[2]: opnd[3]; + Read (RUN_PASS, SP - 1, L_BYTE, WA); /* wchk stack */ + Write (RUN_PASS, SP - 48, opc, L_LONG, WA); /* push opcode */ + Write (RUN_PASS, SP - 44, fault_PC, L_LONG, WA); /* push old PC */ + Write (RUN_PASS, SP - 40, opnd[0], L_LONG, WA); /* push operands */ + Write (RUN_PASS, SP - 36, opnd[1], L_LONG, WA); + Write (RUN_PASS, SP - 32, opnd[2], L_LONG, WA); + Write (RUN_PASS, SP - 28, opnd[3], L_LONG, WA); + Write (RUN_PASS, SP - 24, opnd[4], L_LONG, WA); + Write (RUN_PASS, SP - 20, opnd[5], L_LONG, WA); + Write (RUN_PASS, SP - 8, PC, L_LONG, WA); /* push cur PC */ + Write (RUN_PASS, SP - 4, PSL | cc, L_LONG, WA); /* push PSL */ + SP = SP - 48; /* decr stk ptr */ + vec = ReadLP (RUN_PASS, (SCBB + SCB_EMULATE) & PAMASK); + } +PSL = PSL & ~(PSL_TP | PSL_FPD | PSW_DV | PSW_FU | PSW_IV | PSW_T); +JUMP (vec & ~03); /* set new PC */ +return 0; /* set new cc's */ +} + +#endif diff --git a/src/VAX/vax_cmode.cpp b/src/VAX/vax_cmode.cpp new file mode 100644 index 0000000..9b23ead --- /dev/null +++ b/src/VAX/vax_cmode.cpp @@ -0,0 +1,1318 @@ +/* vax_cmode.c: VAX compatibility mode + + Copyright (c) 2004-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. + + On a full VAX, this module implements PDP-11 compatibility mode. + On a subset VAX, this module forces a fault if REI attempts to set PSL. + + 28-May-08 RMS Inlined physical memory routines + 25-Jan-08 RMS Fixed declaration (from Mark Pizzolato) + 03-May-06 RMS Fixed omission of SXT + Fixed order of operand fetching in XOR + 24-Aug-04 RMS Cloned from PDP-11 CPU + + In compatibility mode, the Istream prefetch mechanism is not used. The + prefetcher will be explicitly resynchronized through intexc on any exit + from compatibility mode. +*/ + +#include "sim_defs.h" +#include "vax_defs.h" + +#if defined (FULL_VAX) + +#define RdMemB(a) Read (RUN_PASS, a, L_BYTE, RA) +#define RdMemMB(a) Read (RUN_PASS, a, L_BYTE, WA) +#define WrMemB(d,a) Write (RUN_PASS, a, d, L_BYTE, WA) +#define BRANCH_F(x) CMODE_JUMP ((PC + (((x) + (x)) & BMASK)) & WMASK) +#define BRANCH_B(x) CMODE_JUMP ((PC + (((x) + (x)) | 0177400)) & WMASK) +#define CC_XOR_NV(x) ((((x) & CC_N) != 0) ^ (((x) & CC_V) != 0)) +#define CC_XOR_NC(x) ((((x) & CC_N) != 0) ^ (((x) & CC_C) != 0)) + +extern uint32 sim_brk_summ; + +int32 GeteaB (int32 spec); +int32 GeteaW (int32 spec); +int32 RdMemW (int32 a); +int32 RdMemMW (int32 a); +void WrMemW (int32 d, int32 a); +int32 RdRegB (int32 rn); +int32 RdRegW (int32 rn); +void WrRegB (int32 val, int32 rn); +void WrRegW (int32 val, int32 rn); + +/* Validate PSL for compatibility mode */ + +t_bool BadCmPSL (RUN_DECL, int32 newpsl) +{ +if ((newpsl & (PSL_FPD|PSL_IS|PSL_CUR|PSL_PRV|PSL_IPL)) != + ((USER << PSL_V_CUR) | (USER << PSL_V_PRV))) + return TRUE; +else return FALSE; +} + +/* Compatibility mode execution */ + +int32 op_cmode (RUN_DECL, int32 cc) +{ +int32 IR, srcspec, dstspec, srcreg, dstreg, ea; +int32 i, t, src, src2, dst, sign, oc; +int32 acc = ACC_MASK (USER); + +PC = PC & WMASK; /* PC must be 16b */ +if (sim_brk_summ && sim_brk_test (RUN_PASS, PC, SWMASK ('E'))) { /* breakpoint? */ + ABORT (STOP_IBKPT); /* stop simulation */ +} +cpu_cycle(); /* count cycles */ +cpu_unit->sim_instrs++; /* ... and instructions */ + +IR = RdMemW (PC); /* fetch instruction */ +PC = (PC + 2) & WMASK; /* incr PC, mod 65k */ +srcspec = (IR >> 6) & 077; /* src, dst specs */ +dstspec = IR & 077; +srcreg = (srcspec <= 07); /* src, dst = rmode? */ +dstreg = (dstspec <= 07); +switch ((IR >> 12) & 017) { /* decode IR<15:12> */ + +/* Opcode 0: no operands, specials, branches, JSR, SOPs */ + + case 000: /* 00xxxx */ + switch ((IR >> 6) & 077) { /* decode IR<11:6> */ + case 000: /* 0000xx */ + switch (IR) { /* decode IR<5:0> */ + case 3: /* BPT */ + CMODE_FAULT (CMODE_BPT); + break; + + case 4: /* IOT */ + CMODE_FAULT (CMODE_IOT); + break; + + case 2: /* RTI */ + case 6: /* RTT */ + src = RdMemW (R[6] & WMASK); /* new PC */ + src2 = RdMemW ((R[6] + 2) & WMASK); /* new PSW */ + R[6] = (R[6] + 4) & WMASK; + cc = src2 & CC_MASK; /* update cc, T */ + if (src2 & PSW_T) + PSL = PSL | PSW_T; + else PSL = PSL & ~PSW_T; + CMODE_JUMP (src); /* update PC */ + break; + + default: /* undefined */ + CMODE_FAULT (CMODE_RSVI); + break; + } /* end switch IR */ + break; /* end case 0000xx */ + + case 001: /* JMP */ + if (dstreg) /* mode 0 illegal */ + CMODE_FAULT (CMODE_ILLI); + else { + CMODE_JUMP (GeteaW (dstspec)); + } + break; + + case 002: /* 0002xx */ + if (IR < 000210) { /* RTS */ + dstspec = dstspec & 07; + if (dstspec != 7) { /* PC <- r */ + CMODE_JUMP (RdRegW (dstspec)); + } + dst = RdMemW (R[6]); /* t <- (sp)+ */ + R[6] = (R[6] + 2) & WMASK; + WrRegW (dst, dstspec); /* r <- t */ + break; /* end if RTS */ + } + if (IR < 000240) { /* [210:237] */ + CMODE_FAULT (CMODE_RSVI); + break; + } + if (IR < 000260) /* clear CC */ + cc = cc & ~(IR & CC_MASK); + else cc = cc | (IR & CC_MASK); /* set CC */ + break; + + case 003: /* SWAB */ + if (dstreg) + src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = ((src & BMASK) << 8) | ((src >> 8) & BMASK); + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_B ((dst & BMASK)); + break; + + case 004: case 005: /* BR */ + BRANCH_F (IR); + break; + + case 006: case 007: /* BR */ + BRANCH_B (IR); + break; + + case 010: case 011: /* BNE */ + if ((cc & CC_Z) == 0) { + BRANCH_F (IR); + } + break; + + case 012: case 013: /* BNE */ + if ((cc & CC_Z) == 0) { + BRANCH_B (IR); + } + break; + + case 014: case 015: /* BEQ */ + if (cc & CC_Z) { + BRANCH_F (IR); + } + break; + + case 016: case 017: /* BEQ */ + if (cc & CC_Z) { + BRANCH_B (IR); + } + break; + + case 020: case 021: /* BGE */ + if (CC_XOR_NV (cc) == 0) { + BRANCH_F (IR); + } + break; + + case 022: case 023: /* BGE */ + if (CC_XOR_NV (cc) == 0) { + BRANCH_B (IR); + } + break; + + case 024: case 025: /* BLT */ + if (CC_XOR_NV (cc)) { + BRANCH_F (IR); + } + break; + + case 026: case 027: /* BLT */ + if (CC_XOR_NV (cc)) { + BRANCH_B (IR); + } + break; + + case 030: case 031: /* BGT */ + if (((cc & CC_Z) || CC_XOR_NV (cc)) == 0) { + BRANCH_F (IR); + } + break; + + case 032: case 033: /* BGT */ + if (((cc & CC_Z) || CC_XOR_NV (cc)) == 0) { + BRANCH_B (IR); + } + break; + + case 034: case 035: /* BLE */ + if ((cc & CC_Z) || CC_XOR_NV (cc)) { + BRANCH_F (IR); + } + break; + + case 036: case 037: /* BLE */ + if ((cc & CC_Z) || CC_XOR_NV (cc)) { + BRANCH_B (IR); + } + break; + + case 040: case 041: case 042: case 043: /* JSR */ + case 044: case 045: case 046: case 047: + if (dstreg) { /* mode 0 illegal */ + CMODE_FAULT (CMODE_ILLI); + } + else { + srcspec = srcspec & 07; /* get reg num */ + dst = GeteaW (dstspec); /* get dst addr */ + src = RdRegW (srcspec); /* get src reg */ + WrMemW (src, (R[6] - 2) & WMASK); /* -(sp) <- r */ + R[6] = (R[6] - 2) & WMASK; + if (srcspec != 7) /* r <- PC */ + WrRegW (PC, srcspec); + CMODE_JUMP (dst); /* PC <- dst */ + } + break; /* end JSR */ + + case 050: /* CLR */ + if (dstreg) + WrRegW (0, dstspec); + else WrMemW (0, GeteaW (dstspec)); + cc = CC_Z; + break; + + case 051: /* COM */ + if (dstreg) + src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = src ^ WMASK; + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + cc = cc | CC_C; + break; + + case 052: /* INC */ + if (dstreg) + src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (src + 1) & WMASK; + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZP_W (dst); + if (dst == 0100000) + cc = cc | CC_V; + break; + + case 053: /* DEC */ + if (dstreg) + src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (src - 1) & WMASK; + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZP_W (dst); + if (dst == 077777) + cc = cc | CC_V; + break; + + case 054: /* NEG */ + if (dstreg) + src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (-src) & WMASK; + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if (dst == 0100000) + cc = cc | CC_V; + if (dst) + cc = cc | CC_C; + break; + + case 055: /* ADC */ + if (dstreg) + src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (src + (cc & CC_C)) & WMASK; + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if ((src == 077777) && (dst == 0100000)) + cc = cc | CC_V; + if ((src == 0177777) && (dst == 0)) + cc = cc | CC_C; + break; + + case 056: /* SBC */ + if (dstreg) + src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (src - (cc & CC_C)) & WMASK; + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if ((src == 0100000) && (dst == 077777)) + cc = cc | CC_V; + if ((src == 0) && (dst == 0177777)) + cc = cc | CC_C; + break; + + case 057: /* TST */ + if (dstreg) + src = RdRegW (dstspec); + else src = RdMemW (GeteaW (dstspec)); + CC_IIZZ_W (src); + break; + + case 060: /* ROR */ + if (dstreg) + src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (src >> 1) | ((cc & CC_C)? WSIGN: 0); + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if (src & 1) + cc = cc | CC_C; + if (CC_XOR_NC (cc)) + cc = cc | CC_V; + break; + + case 061: /* ROL */ + if (dstreg) + src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = ((src << 1) | ((cc & CC_C)? 1: 0)) & WMASK; + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if (src & WSIGN) + cc = cc | CC_C; + if (CC_XOR_NC (cc)) + cc = cc | CC_V; + break; + + case 062: /* ASR */ + if (dstreg) + src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (src & WSIGN) | (src >> 1); + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if (src & 1) + cc = cc | CC_C; + if (CC_XOR_NC (cc)) + cc = cc | CC_V; + break; + + case 063: /* ASL */ + if (dstreg) + src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (src << 1) & WMASK; + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if (src & WSIGN) + cc = cc | CC_C; + if (CC_XOR_NC (cc)) + cc = cc | CC_V; + break; + + case 065: /* MFPI */ + if (dstreg) /* "mov dst,-(sp)" */ + dst = RdRegW (dstspec); + else dst = RdMemW (GeteaW (dstspec)); + WrMemW (dst, (R[6] - 2) & WMASK); + R[6] = (R[6] - 2) & WMASK; + CC_IIZP_W (dst); + break; + + case 066: /* MTPI */ + dst = RdMemW (R[6] & WMASK); /* "mov (sp)+,dst" */ + R[6] = (R[6] + 2) & WMASK; + recq[recqptr++] = RQ_REC (AIN|RW, 6); + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, (GeteaW (dstspec) & WMASK)); + CC_IIZP_W (dst); + break; + + case 067: /* SXT */ + dst = (cc & CC_N)? 0177777: 0; + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, GeteaW (dstspec)); + CC_IIZP_W (dst); + break; + + default: /* undefined */ + CMODE_FAULT (CMODE_RSVI); + break; + } /* end switch SOPs */ + break; /* end case 000 */ + +/* Opcodes 01 - 06: double operand word instructions + + Compatibility mode requires source address decode, source fetch, + dest address decode, dest fetch/store. + + Add: v = [sign (src) = sign (src2)] and [sign (src) != sign (result)] + Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)] +*/ + + case 001: /* MOV */ + if (srcreg) + src = RdRegW (srcspec); + else src = RdMemW (GeteaW (srcspec)); + if (dstreg) + WrRegW (src, dstspec); + else WrMemW (src, GeteaW (dstspec)); + CC_IIZP_W (src); + break; + + case 002: /* CMP */ + if (srcreg) + src = RdRegW (srcspec); + else src = RdMemW (GeteaW (srcspec)); + if (dstreg) + src2 = RdRegW (dstspec); + else src2 = RdMemW (GeteaW (dstspec)); + dst = (src - src2) & WMASK; + CC_IIZZ_W (dst); + if (((src ^ src2) & (~src2 ^ dst)) & WSIGN) + cc = cc | CC_V; + if (src < src2) + cc = cc | CC_C; + break; + + case 003: /* BIT */ + if (srcreg) + src = RdRegW (srcspec); + else src = RdMemW (GeteaW (srcspec)); + if (dstreg) + src2 = RdRegW (dstspec); + else src2 = RdMemW (GeteaW (dstspec)); + dst = src2 & src; + CC_IIZP_W (dst); + break; + + case 004: /* BIC */ + if (srcreg) + src = RdRegW (srcspec); + else src = RdMemW (GeteaW (srcspec)); + if (dstreg) + src2 = RdRegW (dstspec); + else src2 = RdMemMW (ea = GeteaW (dstspec)); + dst = src2 & ~src; + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZP_W (dst); + break; + + case 005: /* BIS */ + if (srcreg) + src = RdRegW (srcspec); + else src = RdMemW (GeteaW (srcspec)); + if (dstreg) + src2 = RdRegW (dstspec); + else src2 = RdMemMW (ea = GeteaW (dstspec)); + dst = src2 | src; + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZP_W (dst); + break; + + case 006: /* ADD */ + if (srcreg) + src = RdRegW (srcspec); + else src = RdMemW (GeteaW (srcspec)); + if (dstreg) + src2 = RdRegW (dstspec); + else src2 = RdMemMW (ea = GeteaW (dstspec)); + dst = (src2 + src) & WMASK; + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_ADD_W (dst, src, src2); + break; + +/* Opcode 07: EIS, FIS (not implemented), CIS + + Notes: + - MUL carry: C is set if the (signed) result doesn't fit in 16 bits. + - Divide has three error cases: + 1. Divide by zero. + 2. Divide largest negative number by -1. + 3. (Signed) quotient doesn't fit in 16 bits. + Cases 1 and 2 must be tested in advance, to avoid C runtime errors. + - ASHx left: overflow if the bits shifted out do not equal the sign + of the result (convert shift out to 1/0, xor against sign). + - ASHx right: if right shift sign extends, then the shift and + conditional or of shifted -1 is redundant. If right shift zero + extends, then the shift and conditional or does sign extension. +*/ + + case 007: /* EIS */ + srcspec = srcspec & 07; /* get src reg */ + switch ((IR >> 9) & 07) { /* decode IR<11:9> */ + + case 0: /* MUL */ + if (dstreg) /* get src2 */ + src2 = RdRegW (dstspec); + else src2 = RdMemW (GeteaW (dstspec)); + src = RdRegW (srcspec); /* get src */ + if (src2 & WSIGN) /* sext src, src2 */ + src2 = src2 | ~WMASK; + if (src & WSIGN) + src = src | ~WMASK; + dst = src * src2; /* multiply */ + WrRegW ((dst >> 16) & WMASK, srcspec); /* high 16b */ + WrRegW (dst & WMASK, srcspec | 1); /* low 16b */ + CC_IIZZ_L (dst & LMASK); + if ((dst > 077777) || (dst < -0100000)) + cc = cc | CC_C; + break; + + case 1: /* DIV */ + if (dstreg) /* get src2 */ + src2 = RdRegW (dstspec); + else src2 = RdMemW (GeteaW (dstspec)); + t = RdRegW (srcspec); + src = (((uint32) t) << 16) | RdRegW (srcspec | 1); + if (src2 == 0) { /* div by 0? */ + cc = CC_V | CC_C; /* set cc's */ + break; /* done */ + } + if ((src == LSIGN) && (src2 == WMASK)) { /* -2^31 / -1? */ + cc = CC_V; /* overflow */ + break; /* done */ + } + if (src2 & WSIGN) /* sext src, src2 */ + src2 = src2 | ~WMASK; + if (t & WSIGN) + src = src | ~LMASK; + dst = src / src2; /* divide */ + if ((dst > 077777) || (dst < -0100000)) { /* out of range? */ + cc = CC_V; /* overflow */ + break; + } + CC_IIZZ_W (dst & WMASK); /* set cc's */ + WrRegW (dst & WMASK, srcspec); /* quotient */ + WrRegW ((src - (src2 * dst)) & WMASK, srcspec | 1); + break; + + case 2: /* ASH */ + if (dstreg) /* get src2 */ + src2 = RdRegW (dstspec); + else src2 = RdMemW (GeteaW (dstspec)); + src2 = src2 & 077; + src = RdRegW (srcspec); /* get src */ + if (sign = ((src & WSIGN)? 1: 0)) + src = src | ~WMASK; + if (src2 == 0) { /* [0] */ + dst = src; /* result */ + oc = 0; /* last bit out */ + } + else if (src2 <= 15) { /* [1,15] */ + dst = src << src2; + i = (src >> (16 - src2)) & WMASK; + oc = (i & 1)? CC_C: 0; + if ((dst & WSIGN)? (i != WMASK): (i != 0)) + oc = oc | CC_V; + } + else if (src2 <= 31) { /* [16,31] */ + dst = 0; + oc = ((src << (src2 - 16)) & 1)? CC_C: 0; + if (src) + oc = oc | CC_V; + } + else if (src2 == 32) { /* [32] = -32 */ + dst = -sign; + oc = sign? CC_C: 0; + } + else { /* [33,63] = -31,-1 */ + dst = (src >> (64 - src2)) | (-sign << (src2 - 32)); + oc = ((src >> (63 - src2)) & 1)? CC_C: 0; + } + WrRegW (dst = dst & WMASK, srcspec); /* result */ + CC_IIZZ_W (dst); + cc = cc | oc; + break; + + case 3: /* ASHC */ + if (dstreg) /* get src2 */ + src2 = RdRegW (dstspec); + else src2 = RdMemW (GeteaW (dstspec)); + src2 = src2 & 077; + t = RdRegW (srcspec); + src = (((uint32) t) << 16) | RdRegW (srcspec | 1); + sign = (t & WSIGN)? 1: 0; /* get src sign */ + if (src2 == 0) { /* [0] */ + dst = src; /* result */ + oc = 0; /* last bit out */ + } + else if (src2 <= 31) { /* [1,31] */ + dst = ((uint32) src) << src2; + i = ((src >> (32 - src2)) | (-sign << src2)) & LMASK; + oc = (i & 1)? CC_C: 0; + if ((dst & LSIGN)? (i != LMASK): (i != 0)) + oc = oc | CC_V; + } + else if (src2 == 32) { /* [32] = -32 */ + dst = -sign; + oc = sign? CC_C: 0; + } + else { /* [33,63] = -31,-1 */ + dst = (src >> (64 - src2)) | (-sign << (src2 - 32)); + oc = ((src >> (63 - src2)) & 1)? CC_C: 0; + } + WrRegW ((dst >> 16) & WMASK, srcspec); /* high result */ + WrRegW (dst & WMASK, srcspec | 1); /* low result */ + CC_IIZZ_L (dst & LMASK); + cc = cc | oc; + break; + + case 4: /* XOR */ + src = RdRegW (srcspec); /* get src */ + if (dstreg) /* get dst */ + src2 = RdRegW (dstspec); + else src2 = RdMemMW (ea = GeteaW (dstspec)); + dst = src2 ^ src; + if (dstreg) /* result */ + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZP_W (dst); + break; + + case 7: /* SOB */ + dst = (RdRegW (srcspec) - 1) & WMASK; /* decr reg */ + WrRegW (dst, srcspec); /* result */ + if (dst != 0) { /* br if zero */ + CMODE_JUMP ((PC - dstspec - dstspec) & WMASK); + } + break; + + default: + CMODE_FAULT (CMODE_RSVI); /* end switch EIS */ + } + break; /* end case 007 */ + +/* Opcode 10: branches, traps, SOPs */ + + case 010: + switch ((IR >> 6) & 077) { /* decode IR<11:6> */ + case 000: case 001: /* BPL */ + if ((cc & CC_N) == 0) { + BRANCH_F (IR); + } + break; + + case 002: case 003: /* BPL */ + if ((cc & CC_N) == 0) { + BRANCH_B (IR); + } + break; + + case 004: case 005: /* BMI */ + if (cc & CC_N) { + BRANCH_F (IR); + } + break; + + case 006: case 007: /* BMI */ + if (cc & CC_N) { + BRANCH_B (IR); + } + break; + + case 010: case 011: /* BHI */ + if ((cc & (CC_C | CC_Z)) == 0) { + BRANCH_F (IR); + } + break; + + case 012: case 013: /* BHI */ + if ((cc & (CC_C | CC_Z)) == 0) { + BRANCH_B (IR); + } + break; + + case 014: case 015: /* BLOS */ + if (cc & (CC_C | CC_Z)) { + BRANCH_F (IR); + } + break; + + case 016: case 017: /* BLOS */ + if (cc & (CC_C | CC_Z)) { + BRANCH_B (IR); + } + break; + + case 020: case 021: /* BVC */ + if ((cc & CC_V) == 0) { + BRANCH_F (IR); + } + break; + + case 022: case 023: /* BVC */ + if ((cc & CC_V) == 0) { + BRANCH_B (IR); + } + break; + + case 024: case 025: /* BVS */ + if (cc & CC_V) { + BRANCH_F (IR); + } + break; + + case 026: case 027: /* BVS */ + if (cc & CC_V) { + BRANCH_B (IR); + } + break; + + case 030: case 031: /* BCC */ + if ((cc & CC_C) == 0) { + BRANCH_F (IR); + } + break; + + case 032: case 033: /* BCC */ + if ((cc & CC_C) == 0) { + BRANCH_B (IR); + } + break; + + case 034: case 035: /* BCS */ + if (cc & CC_C) { + BRANCH_F (IR); + } + break; + + case 036: case 037: /* BCS */ + if (cc & CC_C) { + BRANCH_B (IR); + } + break; + + case 040: case 041: case 042: case 043: /* EMT */ + CMODE_FAULT (CMODE_EMT); + break; + + case 044: case 045: case 046: case 047: /* TRAP */ + CMODE_FAULT (CMODE_TRAP); + break; + + case 050: /* CLRB */ + if (dstreg) + WrRegB (0, dstspec); + else WrMemB (0, GeteaB (dstspec)); + cc = CC_Z; + break; + + case 051: /* COMB */ + if (dstreg) + src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = src ^ BMASK; + if (dstreg) + WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + cc = cc | CC_C; + break; + + case 052: /* INCB */ + if (dstreg) + src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (src + 1) & BMASK; + if (dstreg) + WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZP_B (dst); + if (dst == 0200) + cc = cc | CC_V; + break; + + case 053: /* DECB */ + if (dstreg) + src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (src - 1) & BMASK; + if (dstreg) + WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZP_B (dst); + if (dst == 0177) + cc = cc | CC_V; + break; + + case 054: /* NEGB */ + if (dstreg) + src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (-src) & BMASK; + if (dstreg) + WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + if (dst == 0200) + cc = cc | CC_V; + if (dst) + cc = cc | CC_C; + break; + + case 055: /* ADCB */ + if (dstreg) + src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (src + (cc & CC_C)) & BMASK; + if (dstreg) + WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + if ((src == 0177) && (dst == 0200)) + cc = cc | CC_V; + if ((src == 0377) && (dst == 0)) + cc = cc | CC_C; + break; + + case 056: /* SBCB */ + if (dstreg) + src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (src - (cc & CC_C)) & BMASK; + if (dstreg) + WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + if ((src == 0200) && (dst == 0177)) + cc = cc | CC_V; + if ((src == 0) && (dst == 0377)) + cc = cc | CC_C; + break; + + case 057: /* TSTB */ + if (dstreg) + src = RdRegB (dstspec); + else src = RdMemB (GeteaB (dstspec)); + CC_IIZZ_B (src); + break; + + case 060: /* RORB */ + if (dstreg) + src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (src >> 1) | ((cc & CC_C)? BSIGN: 0); + if (dstreg) + WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + if (src & 1) + cc = cc | CC_C; + if (CC_XOR_NC (cc)) + cc = cc | CC_V; + break; + + case 061: /* ROLB */ + if (dstreg) + src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = ((src << 1) | ((cc & CC_C)? 1: 0)) & BMASK; + if (dstreg) + WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + if (src & BSIGN) + cc = cc | CC_C; + if (CC_XOR_NC (cc)) + cc = cc | CC_V; + break; + + case 062: /* ASRB */ + if (dstreg) + src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (src >> 1) | (src & BSIGN); + if (dstreg) + WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + if (src & 1) + cc = cc | CC_C; + if (CC_XOR_NC (cc)) + cc = cc | CC_V; + break; + + case 063: /* ASLB */ + if (dstreg) + src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (src << 1) & BMASK; + if (dstreg) + WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + if (src & BSIGN) + cc = cc | CC_C; + if (CC_XOR_NC (cc)) + cc = cc | CC_V; + break; + + case 065: /* MFPD */ + if (dstreg) /* "mov dst,-(sp)" */ + dst = RdRegW (dstspec); + else dst = RdMemW (GeteaW (dstspec)); + WrMemW (dst, (R[6] - 2) & WMASK); + R[6] = (R[6] - 2) & WMASK; + CC_IIZP_W (dst); + break; + + case 066: /* MTPD */ + dst = RdMemW (R[6] & WMASK); /* "mov (sp)+,dst" */ + R[6] = (R[6] + 2) & WMASK; + recq[recqptr++] = RQ_REC (AIN|RW, 6); + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, (GeteaW (dstspec) & WMASK)); + CC_IIZP_W (dst); + break; + + default: + CMODE_FAULT (CMODE_RSVI); + break; } /* end switch SOPs */ + break; /* end case 010 */ + +/* Opcodes 11 - 16: double operand byte instructions + + Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)] + Sub: v = [sign (src) != sign (src2)] and [sign (src) = sign (result)] +*/ + + case 011: /* MOVB */ + if (srcreg) + src = RdRegB (srcspec); + else src = RdMemB (GeteaB (srcspec)); + if (dstreg) + WrRegW ((src & BSIGN)? (0xFF00 | src): src, dstspec); + else WrMemB (src, GeteaB (dstspec)); + CC_IIZP_B (src); + break; + + case 012: /* CMPB */ + if (srcreg) + src = RdRegB (srcspec); + else src = RdMemB (GeteaB (srcspec)); + if (dstreg) + src2 = RdRegB (dstspec); + else src2 = RdMemB (GeteaB (dstspec)); + dst = (src - src2) & BMASK; + CC_IIZZ_B (dst); + if (((src ^ src2) & (~src2 ^ dst)) & BSIGN) + cc = cc | CC_V; + if (src < src2) + cc = cc | CC_C; + break; + + case 013: /* BITB */ + if (srcreg) + src = RdRegB (srcspec); + else src = RdMemB (GeteaB (srcspec)); + if (dstreg) + src2 = RdRegB (dstspec); + else src2 = RdMemB (GeteaB (dstspec)); + dst = src2 & src; + CC_IIZP_B (dst); + break; + + case 014: /* BICB */ + if (srcreg) + src = RdRegB (srcspec); + else src = RdMemB (GeteaB (srcspec)); + if (dstreg) + src2 = RdRegB (dstspec); + else src2 = RdMemMB (ea = GeteaB (dstspec)); + dst = src2 & ~src; + if (dstreg) + WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZP_B (dst); + break; + + case 015: /* BISB */ + if (srcreg) + src = RdRegB (srcspec); + else src = RdMemB (GeteaB (srcspec)); + if (dstreg) + src2 = RdRegB (dstspec); + else src2 = RdMemMB (ea = GeteaB (dstspec)); + dst = src2 | src; + if (dstreg) + WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZP_B (dst); + break; + + case 016: /* SUB */ + if (srcreg) + src = RdRegW (srcspec); + else src = RdMemW (GeteaW (srcspec)); + if (dstreg) + src2 = RdRegW (dstspec); + else src2 = RdMemMW (ea = GeteaW (dstspec)); + dst = (src2 - src) & WMASK; + if (dstreg) + WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if (((src ^ src2) & (~src ^ dst)) & WSIGN) + cc = cc | CC_V; + if (src2 < src) + cc = cc | CC_C; + break; + + default: + CMODE_FAULT (CMODE_RSVI); + break; + } /* end switch op */ + +return cc; +} + +/* Effective address calculations + + Inputs: + spec = specifier <5:0> + Outputs: + ea = effective address +*/ + +int32 GeteaW (int32 spec) +{ +int32 adr, reg; + +reg = spec & 07; /* register number */ +switch (spec >> 3) { /* decode spec<5:3> */ + + default: /* can't get here */ + case 1: /* (R) */ + if (reg == 7) + return (PC & WMASK); + else return (R[reg] & WMASK); + + case 2: /* (R)+ */ + if (reg == 7) + PC = ((adr = PC) + 2) & WMASK; + else { + R[reg] = ((adr = R[reg]) + 2) & WMASK; + recq[recqptr++] = RQ_REC (AIN|RW, reg); + } + return adr; + + case 3: /* @(R)+ */ + if (reg == 7) + PC = ((adr = PC) + 2) & WMASK; + else { + R[reg] = ((adr = R[reg]) + 2) & WMASK; + recq[recqptr++] = RQ_REC (AIN|RW, reg); + } + return RdMemW (adr); + + case 4: /* -(R) */ + if (reg == 7) + adr = PC = (PC - 2) & WMASK; + else { + adr = R[reg] = (R[reg] - 2) & WMASK; + recq[recqptr++] = RQ_REC (ADC|RW, reg); + } + return adr; + + case 5: /* @-(R) */ + if (reg == 7) + adr = PC = (PC - 2) & WMASK; + else { + adr = R[reg] = (R[reg] - 2) & WMASK; + recq[recqptr++] = RQ_REC (ADC|RW, reg); + } + return RdMemW (adr); + + case 6: /* d(r) */ + adr = RdMemW (PC); + PC = (PC + 2) & WMASK; + if (reg == 7) + return ((PC + adr) & WMASK); + else return ((R[reg] + adr) & WMASK); + + case 7: /* @d(R) */ + adr = RdMemW (PC); + PC = (PC + 2) & WMASK; + if (reg == 7) + adr = (PC + adr) & WMASK; + else adr = (R[reg] + adr) & WMASK; + return RdMemW (adr); + } /* end switch */ +} + +int32 GeteaB (int32 spec) +{ +int32 adr, reg; + +reg = spec & 07; /* reg number */ +switch (spec >> 3) { /* decode spec<5:3> */ + + default: /* can't get here */ + case 1: /* (R) */ + if (reg == 7) + return (PC & WMASK); + else return (R[reg] & WMASK); + + case 2: /* (R)+ */ + if (reg == 7) + PC = ((adr = PC) + 2) & WMASK; + else if (reg == 6) { + R[reg] = ((adr = R[reg]) + 2) & WMASK; + recq[recqptr++] = RQ_REC (AIN|RW, reg); + } + else { + R[reg] = ((adr = R[reg]) + 1) & WMASK; + recq[recqptr++] = RQ_REC (AIN|RB, reg); + } + return adr; + + case 3: /* @(R)+ */ + if (reg == 7) + PC = ((adr = PC) + 2) & WMASK; + else { + R[reg] = ((adr = R[reg]) + 2) & WMASK; + recq[recqptr++] = RQ_REC (AIN|RW, reg); + } + return RdMemW (adr); + + case 4: /* -(R) */ + if (reg == 7) + adr = PC = (PC - 2) & WMASK; + else if (reg == 6) { + adr = R[reg] = (R[reg] - 2) & WMASK; + recq[recqptr++] = RQ_REC (ADC|RW, reg); + } + else { + adr = R[reg] = (R[reg] - 1) & WMASK; + recq[recqptr++] = RQ_REC (ADC|RB, reg); + } + return adr; + + case 5: /* @-(R) */ + if (reg == 7) + adr = PC = (PC - 2) & WMASK; + else { + adr = R[reg] = (R[reg] - 2) & WMASK; + recq[recqptr++] = RQ_REC (ADC|RW, reg); + } + return RdMemW (adr); + + case 6: /* d(r) */ + adr = RdMemW (PC); + PC = (PC + 2) & WMASK; + if (reg == 7) + return ((PC + adr) & WMASK); + else return ((R[reg] + adr) & WMASK); + + case 7: /* @d(R) */ + adr = RdMemW (PC); + PC = (PC + 2) & WMASK; + if (reg == 7) + adr = (PC + adr) & WMASK; + else adr = (R[reg] + adr) & WMASK; + return RdMemW (adr); + } /* end switch */ +} + +/* Memory and register access routines */ + +int32 RdMemW (int32 a) +{ +int32 acc = ACC_MASK (USER); + +if (a & 1) + CMODE_FAULT (CMODE_ODD); +return Read (RUN_PASS, a, L_WORD, RA); +} + +int32 RdMemMW (int32 a) +{ +int32 acc = ACC_MASK (USER); + +if (a & 1) + CMODE_FAULT (CMODE_ODD); +return Read (RUN_PASS, a, L_WORD, WA); +} + +void WrMemW (int32 d, int32 a) +{ +int32 acc = ACC_MASK (USER); + +if (a & 1) + CMODE_FAULT (CMODE_ODD); +Write (RUN_PASS, a, d, L_WORD, WA); +return; +} + +int32 RdRegB (int32 rn) +{ +if (rn == 7) + return (PC & BMASK); +else return (R[rn] & BMASK); +} + +int32 RdRegW (int32 rn) +{ +if (rn == 7) + return (PC & WMASK); +else return (R[rn] & WMASK); +} + +void WrRegB (int32 val, int32 rn) +{ +if (rn == 7) { + CMODE_JUMP ((PC & ~BMASK) | val); + } +else R[rn] = (R[rn] & ~BMASK) | val; +return; +} + +void WrRegW (int32 val, int32 rn) +{ +if (rn == 7) { + CMODE_JUMP (val); + } +else R[rn] = val; +return; +} + +#else + +/* Subset VAX + + Never legal to set CM in PSL + Should never get to instruction execution +*/ + +t_bool BadCmPSL (RUN_DECL, int32 newpsl) +{ +return TRUE; /* always bad */ +} + +int32 op_cmode (RUN_DECL, int32 cc) +{ +RSVD_INST_FAULT; +return cc; +} + +#endif diff --git a/src/VAX/vax_cpu.cpp b/src/VAX/vax_cpu.cpp new file mode 100644 index 0000000..4ccca6a --- /dev/null +++ b/src/VAX/vax_cpu.cpp @@ -0,0 +1,6067 @@ +/* vax_cpu.c: VAX CPU + + 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"), + 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 VAX central processor + + 20-Sep-11 MP Fixed idle conditions for various versions of Ultrix, + Quasijarus-4.3BSD, NetBSD and OpenBSD. + Note: Since NetBSD and OpenBSD are still actively + developed operating systems, new versions of + these OSes are moving targets with regard to + providing idle detection. At this time, recent versions + of OpenBSD have veered from the traditional OS idle + approach taken in the other BSD derived OSes. + Determining a reasonable idle detection pattern does + not seem possible for these versions. + 23-Mar-11 RMS Revised for new idle design (from Mark Pizzolato) + 05-Jan-11 MP Added Asynch I/O support + 24-Apr-10 RMS Added OLDVMS idle timer option + Fixed bug in SET CPU IDLE + 21-May-08 RMS Removed inline support + 28-May-08 RMS Inlined instruction prefetch, physical memory routines + 13-Aug-07 RMS Fixed bug in read access g-format indexed specifiers + 28-Apr-07 RMS Removed clock initialization + 29-Oct-06 RMS Added idle support + 22-May-06 RMS Fixed format error in CPU history (found by Peter Schorn) + 10-May-06 RMS Added -kesu switches for virtual addressing modes + Fixed bugs in examine virtual + Rewrote history function for greater usability + Fixed bug in reported VA on faulting cross-page write + 02-May-06 RMS Fixed fault cleanup to clear PSL + Fixed ADAWI r-mode to preserve dst<31:16> + Fixed ACBD/G to test correct operand + Fixed access checking on modify-class specifiers + Fixed branch displacements in history buffer + (all reported by Tim Stark) + 17-Nov-05 RMS Fixed CVTfi with integer overflow to trap if PSW set + 13-Nov-05 RMS Fixed breakpoint test with 64b addresses + 25-Oct-05 RMS Removed cpu_extmem + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 13-Jan-05 RMS Fixed initial state of cpu_extmem + 06-Nov-04 RMS Added =n to SHOW HISTORY + 30-Sep-04 RMS Added octaword specifier decodes and instructions + Moved model-specific routines to system module + 02-Sep-04 RMS Fixed bug in EMODD/G, second word of quad dst not probed + 28-Jun-04 RMS Fixed bug in DIVBx, DIVWx (reported by Peter Trimmel) + 18-Apr-04 RMS Added octaword macros + 25-Jan-04 RMS Removed local debug logging support + RMS,MP Added extended physical memory support + 31-Dec-03 RMS Fixed bug in set_cpu_hist + 21-Dec-03 RMS Added autoconfiguration controls + 29-Oct-03 RMS Fixed WriteB declaration (found by Mark Pizzolato) + 23-Sep-03 RMS Revised instruction history for dynamic sizing + 17-May-03 RMS Fixed operand order in EMODx + 23-Apr-03 RMS Revised for 32b/64b t_addr + 05-Jan-02 RMS Added memory size restore support + 25-Dec-02 RMS Added instruction history (from Mark Pizzolato) + 29-Sep-02 RMS Revised to build dib_tab dynamically + 14-Jul-02 RMS Added halt to console, infinite loop detection + (from Mark Pizzolato) + 02-May-02 RMS Fixed bug in indexed autoincrement register logging + 30-Apr-02 RMS Added TODR powerup routine + 18-Apr-02 RMS Cleanup ambiguous signed left shifts + 15-Apr-02 RMS Fixed bug in CASEL condition codes + + The register state for the VAX is: + + R[0:15] general registers + PSL<31:0> processor status longword + TP<30> trace pending + FPD<27> first part done + IS<26> interrupt stack + CM<25:24> current mode + PM<23:22> previous mode + IPL<20:16> interrupt priority level + PSW<15:0> non-privileged processor status word + DV<7> decimal overflow trap enable + FU<6> floating underflow fault enable + IV<5> integer overflow trap enable + T<4> trace trap enable + CC<3:0> condition codes + SCBB system control block base + PCBB process control block base + SBR system page table base + SLR system page table length + P0BR process region 0 page table base + P0LR process region 0 page table length + P1BR process region 1 page table base + P1LR process region 1 page table length + SIRR/SISR software interrupt request/summary register + ASTLVL AST level register + + The VAX has a variable length instruction format with up to six operands: + + opcode byte + operand 1 specifier + : + operand n specifier + + Each operand specifier is a byte consisting of an addressing mode, a + register, and possibly 1-8 bytes of extension: + + number name extension mnemonic operation + + 0-3 short literal - #n op <- specifier + 4 index - [Rn] index by Rn + 5 register - Rn op <- Rn + 6 register def - (Rn) op <- M[Rn] + 7 autodecrement - -(Rn) Rn <- Rn - length + op <- M[Rn] + 8 autoincrement - (Rn)+ op <- M[Rn] + Rn <- Rn + length + 9 auto deferred - @(Rn)+ op <- M[M[Rn]] + Rn <- Rn + 4 + A byte displ byte d d(Rn) op <- M[Rn + sxt.d] + B byte displ def byte d @d(Rn) op <- M[M[Rn + sxt.d]] + C word displ word d d(Rn) op <- M[Rn + sxt.d] + D word displ def word d @d(Rn) op <- M[M[Rn + sxt.d]] + E long displ long d d(Rn) op <- M[Rn + d] + F long displ def long d @d(Rn) op <- M[M[Rn + d]] + + When the general register is the PC, certain modes are forbidden, and + others have special interpretations: + + 4F index fault + 5F register fault + 6F register def fault + 7F autodecrement fault + 8F immediate 1-8B #imm op <- imm + 9 absolute 4B @#imm op <- M[imm] + A byte relative byte d d(Rn) op <- M[PC + sxt.d] + B byte rel def byte d @d(Rn) op <- M[M[PC + sxt.d]] + C word relative word d d(Rn) op <- M[PC + sxt.d] + D word rel def word d @d(Rn) op <- M[M[PC + sxt.d]] + E long relative long d d(Rn) op <- M[PC + d] + F long rel def long d @d(Rn) op <- M[M[PC + d]] + + This routine is the instruction decode routine for the VAX. It + is called from the simulator control program to execute instructions + in simulated memory, starting at the simulated PC. It runs until an + enabled exception is encountered. + + General notes: + + 1. Traps and interrupts. Variable trpirq microencodes the outstanding + trap request (if any) and the level of the highest outstanding + interrupt (if any). + + 2. Interrupt requests are maintained in the array in cpu_intreg (of type InterruptRegister), + one word per interrupt level, one bit per device. + + 3. Adding I/O devices. These modules must be modified: + + vax_defs.h add device address and interrupt definitions + vax_sys.c add sim_devices table entry +*/ + +/* Definitions */ + +#include "sim_defs.h" +#include "vax_defs.h" +#include "sim_rev.h" + +#define OP_MEM -1 +#define UNIT_V_CONH (UNIT_V_UF + 0) /* halt to console */ +#define UNIT_V_MSIZE (UNIT_V_UF + 1) /* dummy */ +#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_VMS 0x01 +#define VAX_IDLE_ULT 0x02 +#define VAX_IDLE_ULTOLD 0x04 +#define VAX_IDLE_QUAD 0x08 + +#define OPND_SIZE 16 +#define INST_SIZE 52 +#define op0 opnd[0] +#define op1 opnd[1] +#define op2 opnd[2] +#define op3 opnd[3] +#define op4 opnd[4] +#define op5 opnd[5] +#define op6 opnd[6] +#define op7 opnd[7] +#define op8 opnd[8] +#define CHECK_FOR_PC if (rn == nPC) \ + RSVD_ADDR_FAULT +#define CHECK_FOR_SP if (rn >= nSP) \ + RSVD_ADDR_FAULT +#define CHECK_FOR_AP if (rn >= nAP) \ + RSVD_ADDR_FAULT +#define WRITE_B(r) if (spec > (GRN | nPC)) \ + Write (RUN_PASS, va, r, L_BYTE, WA); \ + else R[rn] = (R[rn] & ~BMASK) | ((r) & BMASK) +#define WRITE_W(r) if (spec > (GRN | nPC)) \ + Write (RUN_PASS, va, r, L_WORD, WA); \ + else R[rn] = (R[rn] & ~WMASK) | ((r) & WMASK) +#define WRITE_L(r) if (spec > (GRN | nPC)) \ + Write (RUN_PASS, va, r, L_LONG, WA); \ + else R[rn] = (r) +#define WRITE_Q(rl,rh) if (spec > (GRN | nPC)) { \ + if ((Test (RUN_PASS, va + 7, WA, &mstat) >= 0) || \ + (Test (RUN_PASS, va, WA, &mstat) < 0)) \ + Write (RUN_PASS, va, rl, L_LONG, WA); \ + Write (RUN_PASS, va + 4, rh, L_LONG, WA); \ + } \ + else { \ + if (rn >= nSP) \ + RSVD_ADDR_FAULT; \ + R[rn] = rl; \ + R[rn + 1] = rh; \ + } + +#define HIST_MIN 64 +#define HIST_MAX 65536 + +#include "vax_cpu.h" + +class SIM_ALIGN_64 InstHistory +{ +public: + UINT64 stamp; + uint8 isRecorded; + SIM_ALIGN_32 int32 iPC; + int32 iPSL; + int32 opc; + int32 opnd[OPND_SIZE]; + uint8 inst[INST_SIZE]; +}; + +static uint32 hst_lnt = 0; /* length of history buffer */ +static t_bool hst_on = FALSE; /* history recording enabled */ +static t_bool hst_sync = TRUE; /* true if using global stamp */ +#include "vax_hist.h" +static void cpu_free_history (); + +volatile uint32* M = NULL; /* memory */ +atomic_int32 hlt_pin = 0; /* HLT pin intr */ +int32 sys_idle_cpu_mask_va = 0; /* virtual address of system idle CPUs mask (VMS: SCH$GL_IDLE_CPUS) or NULL */ +int32 sys_critical_section_ipl = -1; /* IPL for entering O/S critical section */ +uint32 cpu_idle_mask = VAX_IDLE_VMS; /* idle mask */ +uint32 cpu_idle_type = 1; /* default VMS */ + + +REG *pcq_r = NULL; /* PC queue register descriptor (backing store is in per-CPU area) */ +static uint32 bugcheck_ctrl = 0; /* control handling of bugcheck pseudo-instructions */ + +// int32 cpu_astop = 0; + +extern const uint32 byte_mask[33]; +extern const uint32 align[4]; + +const uint32 byte_mask[33] = { 0x00000000, + 0x00000001, 0x00000003, 0x00000007, 0x0000000F, + 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF, + 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, + 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, + 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, + 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF, + 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, + 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF + }; +const uint32 byte_sign[33] = { 0x00000000, + 0x00000001, 0x00000002, 0x00000004, 0x00000008, + 0x00000010, 0x00000020, 0x00000040, 0x00000080, + 0x00000100, 0x00000200, 0x00000400, 0x00000800, + 0x00001000, 0x00002000, 0x00004000, 0x00008000, + 0x00010000, 0x00020000, 0x00040000, 0x00080000, + 0x00100000, 0x00200000, 0x00400000, 0x00800000, + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000 + }; +const uint32 align[4] = { + 0xFFFFFFFF, 0x00FFFFFF, 0x0000FFFF, 0x000000FF + }; + +/* External and forward references */ + +extern int32 sim_int_char; +extern int32 sim_switches; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern uint32 devs_per_irql[IPL_HLVL]; + +extern t_stat build_dib_tab (void); +extern UNIT rom_unit, nvr_unit; +extern int32 BadCmPSL (RUN_DECL, int32 newpsl); +extern int32 get_vector (RUN_DECL, int32 lvl); +extern void set_map_reg (RUN_DECL); +extern void rom_wr_B (RUN_DECL, int32 pa, int32 val); +extern int32 machine_check (RUN_DECL, int32 p1, int32 opc, int32 cc, int32 delta); +extern const uint16 drom[NUM_INST][MAX_SPEC + 1]; +extern t_stat cpu_boot (int32 unitno, DEVICE *dptr); +extern int32 con_halt (int32 code, int32 cc); + +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); +t_stat cpu_ex_run (RUN_DECL, t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat cpu_show_virt (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat cpu_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_idle (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +int32 cpu_get_vsw (RUN_DECL, int32 sw); +int32 get_istr (RUN_DECL, int32 lnt, int32 acc); +int32 ReadOcta (RUN_DECL, int32 va, int32 *opnd, int32 j, int32 acc); +t_bool cpu_show_opnd (SMP_FILE *st, InstHistory *h, int32 line); +void cpu_idle (RUN_DECL); +t_stat cpu_idle_svc (RUN_SVC_DECL, UNIT *uptr); + +static t_stat handle_abort(RUN_DECL, sim_exception_ABORT* exabort, + volatile int32& cc, volatile int32& acc, volatile int32& opc); +static void op_reserved_ff(RUN_DECL, int32 acc); +static t_bool is_bug_instruction(RUN_DECL, int32 acc, int32 va, char* wl, uint32* bug_code); +static t_bool cpu_start_secondary(RUN_DECL, CPU_UNIT* xcpu, uint32* pcb, uint32 scbb, + uint32 x_mapen, uint32 sbr, uint32 slr, uint32 isp); +static void op_adawi(RUN_DECL, int32 *opnd, int32 acc, int32 spec, int32 rn, int32 va, volatile int32& cc); + +/* + * static part of initializer, cannot fail or throw exceptions + */ +CPU_UNIT::CPU_UNIT() : UNIT(&cpu_idle_svc, UNIT_FIX|UNIT_BINK|UNIT_ISCPU, INITMEMSIZE) +{ + device = &cpu_dev; + cpu_state = CPU_STATE_STANDBY; + atomic_var(cpu_adv_cycles) = 0; + + sim_time = 0; + sim_rtime = 0; + noqueue_time = 0; + + cpu_stop_code = SCPE_OK; + cpu_dostop = FALSE; + + cpu_hst = NULL; + UINT64_SET_ZERO(cpu_hst_stamp); + cpu_hst_index = 0; + + memzero(sim_brk_pend); + memzero(sim_brk_ploc); + sim_brk_act = NULL; + + memzero(cpu_rtc_ticks); + memzero(cpu_rtc_hz); + memzero(cpu_rtc_rtime); + memzero(cpu_rtc_vtime); + memzero(cpu_rtc_nxintv); + memzero(cpu_rtc_based); + memzero(cpu_rtc_currd); + memzero(cpu_rtc_initd); + memzero(cpu_rtc_elapsed); + + cpu_idle_sleep_us = 0; + cpu_idle_sleep_cycles = 0; + + cpu_ssc_delta_timer[0] = NULL; + cpu_ssc_delta_timer[1] = NULL; + + clk_active = FALSE; + cpu_last_synclk_cycles = 0; + cpu_last_tslice_tick_cycles = 0; + cpu_last_second_tick_cycles = 0; + + sysd_active_mask = 0; + + cpu_active_clk_interrupt = FALSE; + cpu_active_ipi_interrupt = FALSE; + + cpu_synclk_protect = FALSE; + cpu_synclk_protect_os = 0; + cpu_synclk_protect_dev = 0; + cpu_synclk_pending = SynclkNotPending; + + cpu_inside_reevaluate_thread_priority = FALSE; + cpu_redo_reevaluate_thread_priority = FALSE; + + smp_var(cpu_sleeping) = 0; + + cpu_wakeup_event = NULL; + cpu_run_gate = NULL; + cpu_thread = SMP_THREAD_NULL; + cpu_thread_created = FALSE; + cpu_thread_priority = SIMH_THREAD_PRIORITY_INVALID; + + cpu_requeue_syswide_pending = FALSE; + + syncw_active = 0; + syncw_countdown = 0; + syncw_countdown_start = 0; + syncw_countdown_sys = 0; + syncw_countdown_ilk = 0; + syncw_wait_event = NULL; + syncw_wait_cpu_id = NO_CPU_ID; + + cpu_con_rei_on = FALSE; +} + +/* + * dynamic part of initializer, can fail and throw exceptions + */ +void CPU_UNIT::init(uint8 cpu_id, uint8 cpu_state) +{ + check_aligned(this, SMP_MAXCACHELINESIZE); + smp_check_aligned(& cpu_adv_cycles); + this->unitno = cpu_id; + this->cpu_id = cpu_id; + this->cpu_state = cpu_state; + cpu_exception_ABORT = new sim_exception_ABORT(0, TRUE); + cpu_intreg.init(IPL_HMIN, IPL_HMAX, devs_per_irql); + init_clock_queue(); + sim_step = 0; + sim_instrs = 0; + cpu_ssc_delta_timer[0] = sim_delta_timer::create(); + cpu_ssc_delta_timer[1] = sim_delta_timer::create(); + cpu_wakeup_event = smp_event::create(); + this->cpu_run_gate = smp_semaphore::create(0); + syncw_wait_event = smp_event::create(); + smp_create_thread(sim_cpu_work_thread_proc, this, & this->cpu_thread); + cpu_thread_created = TRUE; + cpu_context.reset(this); +} + +static inline uint32 ROUNDUP(uint32 n, uint32 r) +{ + return ((n + r - 1) / r) * r; +} + +void CPU_UNIT::init_clock_queue() +{ + this->clock_queue_freelist = NULL; + this->clock_queue = NULL; + + /* two extra entries: one for throttle unit, another for step unit */ + int nentries = sim_units_percpu + sim_units_global + 2; + int entrysize = ROUNDUP(sizeof(clock_queue_entry), __SIZEOF_POINTER__); + t_byte* mp = (t_byte*) malloc_aligned(nentries * entrysize, __SIZEOF_POINTER__); + if (mp == NULL) + panic("Unable to allocate memory"); + clock_queue_entry* tail = NULL; + for (int k = 0; k < nentries; k++) + { + clock_queue_entry* ep = (clock_queue_entry*) (mp + (k * entrysize)); + ep->next = NULL; + if (tail) + { + tail->next = ep; + } + else + { + this->clock_queue_freelist = ep; + } + tail = ep; + } +} + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +CPU_UNIT cpu_unit_0; +CPU_UNIT* cpu_units[SIM_MAX_CPUS] = { & cpu_unit_0 }; +UNIT* cpu_units_as_units[SIM_MAX_CPUS] = { & cpu_unit_0 }; + +void init_cpu_unit_0() +{ + /* + * Verify that a pointer to CPU_UNIT will hold the same memory address as the pointer to its base class UNIT. + */ + if ((void*) (CPU_UNIT*) &cpu_unit_0 != (void*) (UNIT*) &cpu_unit_0) + panic("Broken assumption: CPU_UNIT casts to UNIT with address change"); + + for (int k = 1; k < SIM_MAX_CPUS; k++) + { + cpu_units[k] = NULL; + cpu_units_as_units[k] = NULL; + } + + cpu_unit_0.init(0, CPU_STATE_RUNNABLE); +} + +t_bool cpu_create_cpus(uint32 ncpus) +{ + if (ncpus == sim_ncpus) return TRUE; + if (ncpus < sim_ncpus || ncpus > SIM_MAX_CPUS) return FALSE; + + if (hst_lnt) + { + smp_printf ("Warning: Stopping history recording ...\n"); + if (sim_log) + fprintf (sim_log, "Warning: Stopping history recording ...\n"); + cpu_free_history (); + } + + for (uint32 k = sim_ncpus; k < ncpus; k++) + { + CPU_UNIT* cpu = new CPU_UNIT(); + cpu->capac = cpu_unit_0.capac; + cpu->flags |= cpu_unit_0.flags & UNIT_CONH; + + cpu->init(k, CPU_STATE_STANDBY); + + cpu_units[k] = cpu; + cpu_units_as_units[k] = cpu; + smp_wmb(); + sim_ncpus++; + smp_wmb(); + + reset_cpu_and_its_devices (cpu); + } + + return TRUE; +} + +/* + * For CPU0: reset the CPU and all devices, both per-CPU and global. + * + * For other CPUs: reset this CPU and its per-CPU devices, but not global devices. + * + * It is crucial that devices are reset in the order listed in sim_devices. + * Otherwise system is not properly initialized and, at best, boot ROM tests fail. + */ +t_stat reset_cpu_and_its_devices (CPU_UNIT* cpu_unit) +{ + t_stat reason = SCPE_OK; + DEVICE *dptr; + + run_scope_context* rscx = run_scope_context::get_current(); + CPU_UNIT* sv_cpu_unit = rscx->cpu_unit; + rscx->cpu_unit = cpu_unit; + + for (int k = 0; (dptr = sim_devices[k]) != NULL; k++) + { + if (cpu_unit->is_primary_cpu() || (dptr->flags & DEV_PERCPU)) + { + if ((reason = reset_dev_thiscpu (dptr)) != SCPE_OK) + { + break; + } + } + } + + cpu_unit->cpu_state = CPU_STATE_STANDBY; + cpu_running_set.clear(cpu_unit->cpu_id); + sim_mp_active_update(); + + rscx->cpu_unit = sv_cpu_unit; + + smp_wmb(); + + return reason; +} + +const char* cpu_describe_state(CPU_UNIT* cpu_unit) +{ + switch (cpu_unit->cpu_state) + { + case CPU_STATE_STANDBY: + return "STANDBY"; + case CPU_STATE_RUNNABLE: + return "RUNNABLE"; + case CPU_STATE_RUNNING: + return "RUNNING"; + default: + return "UNKNOWN STATE"; + } +} + +REG cpu_reg[] = { + { HRDATA_CPU ("PC", r_R[nPC], 32) }, + { HRDATA_CPU ("R0", r_R[0], 32) }, + { HRDATA_CPU ("R1", r_R[1], 32) }, + { HRDATA_CPU ("R2", r_R[2], 32) }, + { HRDATA_CPU ("R3", r_R[3], 32) }, + { HRDATA_CPU ("R4", r_R[4], 32) }, + { HRDATA_CPU ("R5", r_R[5], 32) }, + { HRDATA_CPU ("R6", r_R[6], 32) }, + { HRDATA_CPU ("R7", r_R[7], 32) }, + { HRDATA_CPU ("R8", r_R[8], 32) }, + { HRDATA_CPU ("R9", r_R[9], 32) }, + { HRDATA_CPU ("R10", r_R[10], 32) }, + { HRDATA_CPU ("R11", r_R[11], 32) }, + { HRDATA_CPU ("R12", r_R[12], 32) }, + { HRDATA_CPU ("R13", r_R[13], 32) }, + { HRDATA_CPU ("R14", r_R[14], 32) }, + { HRDATA_CPU ("AP", r_R[nAP], 32) }, + { HRDATA_CPU ("FP", r_R[nFP], 32) }, + { HRDATA_CPU ("SP", r_R[nSP], 32) }, + { HRDATA_CPU ("PSL", r_PSL, 32) }, + { HRDATA_CPU ("CC", r_PSL, 4) }, + { HRDATA_CPU ("KSP", r_STK[KERN], 32) }, + { HRDATA_CPU ("ESP", r_STK[EXEC], 32) }, + { HRDATA_CPU ("SSP", r_STK[SUPV], 32) }, + { HRDATA_CPU ("USP", r_STK[USER], 32) }, + { HRDATA_CPU ("IS", r_STK[4], 32) }, + { HRDATA_CPU ("SCBB", r_SCBB, 32) }, + { HRDATA_CPU ("PCBB", r_PCBB, 32) }, + { HRDATA_CPU ("P0BR", r_P0BR, 32) }, + { HRDATA_CPU ("P0LR", r_P0LR, 22) }, + { HRDATA_CPU ("P1BR", r_P1BR, 32) }, + { HRDATA_CPU ("P1LR", r_P1LR, 22) }, + { HRDATA_CPU ("SBR", r_SBR, 32) }, + { HRDATA_CPU ("SLR", r_SLR, 22) }, + { HRDATA_CPU ("SISR", r_SISR, 16) }, + { HRDATA_CPU ("ASTLVL", r_ASTLVL, 4) }, + { FLDATA_CPU ("MAPEN", r_mapen, 0) }, + { FLDATA_CPU ("PME", r_pme, 0) }, + { HRDATA_CPU ("TRPIRQ", r_trpirq, 8) }, + { IRDATA_LVL (IPL17, 17, 1), REG_RO }, + { IRDATA_LVL (IPL16, 16, 1), REG_RO }, + { IRDATA_LVL (IPL15, 15, 1), REG_RO }, + { IRDATA_LVL (IPL14, 14, 1), REG_RO }, + { FLDATA_CPU ("CRDERR", r_crd_err, 0) }, + { FLDATA_CPU ("MEMERR", r_mem_err, 0) }, + { FLDATA_GBL (HLTPIN, hlt_pin, 0) }, + { HRDATA_GBL (IDLE_MASK, cpu_idle_mask, 16), REG_HIDDEN }, + { DRDATA_GBL (IDLE_INDX, cpu_idle_type, 4), REG_HRO }, + { DRDATA_GBL (IDLE_ENAB, sim_idle_enab, 4), REG_HRO }, + { BRDATA_CPU ("PCQ", r_pcq, 16, 32, PCQ_SIZE), REG_RO+REG_CIRC, BRDATA_CPU_QPTR(r_pcq_qptr) }, + { HRDATA_CPU ("PCQP", r_pcq_p, 6), REG_HRO }, + { HRDATA_GBL (WRU, sim_int_char, 8) }, + { HRDATA_CPU ("BADABO", r_badabo, 32), REG_HRO }, + // { HRDATA_CPU ("CQBIC_SCR", r_cq_scr, 16) }, + // { HRDATA_CPU ("CQBIC_DSER", r_cq_dser, 8) }, + // { HRDATA_CPU ("CQBIC_MEAR", r_cq_mear, 13) }, + // { HRDATA_CPU ("CQBIC_SEAR", r_cq_sear, 20) }, + { HRDATA_DYN("SIRR", 4, reg_sirr_rd, reg_sirr_wr, NULL), REG_HIDDEN }, + { HRDATA_GBL (SYNCLK_SAFE_IPL, synclk_safe_ipl, 5), REG_HIDDEN }, + { HRDATA_GBL (SYNCLK_SAFE_CYCLES, synclk_safe_cycles, 32), REG_HIDDEN }, + { HRDATA_GBL (BUGCHECK_CTRL, bugcheck_ctrl, 32), REG_HIDDEN }, + { HRDATA_GBL (VCPU_PER_CORE, sim_vcpu_per_core, 1), REG_HIDDEN }, + { HRDATA_DYN("WS_MIN", 32, ws_min_rd, ws_min_wr, NULL), REG_HIDDEN }, + { HRDATA_DYN("WS_MAX", 32, ws_max_rd, ws_max_wr, NULL), REG_HIDDEN }, + { HRDATA_DYN("WS_LOCK", 1, ws_lock_rd, ws_lock_wr, NULL), REG_HIDDEN }, + { HRDATA_GBL (HOST_DEDICATED, sim_host_dedicated, 1), REG_HIDDEN }, + { HRDATA_GBL_RDX(HOST_TURBO, sim_host_turbo, 10, 10), REG_HIDDEN }, + { NULL } +}; + +MTAB cpu_mod[] = { + { UNIT_CONH, 0, "HALT to SIMH", "SIMHALT", NULL }, + { UNIT_CONH, UNIT_CONH, "HALT to console", "CONHALT", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &cpu_set_idle, &cpu_show_idle }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, + /* bit flags 23...29 are also handled in cpu_sync_flags */ + { UNIT_MSIZE, (1u << 23), NULL, "8M", &cpu_set_size }, + { UNIT_MSIZE, (1u << 24), NULL, "16M", &cpu_set_size }, + { UNIT_MSIZE, (1u << 25), NULL, "32M", &cpu_set_size }, + { UNIT_MSIZE, (1u << 25) + (1u << 24), NULL, "48M", &cpu_set_size }, + { UNIT_MSIZE, (1u << 26), NULL, "64M", &cpu_set_size }, + { UNIT_MSIZE, (1u << 27), NULL, "128M", &cpu_set_size }, +#if !defined (VAX_780) + { UNIT_MSIZE, (1u << 28), NULL, "256M", &cpu_set_size }, + { UNIT_MSIZE, (1u << 29), NULL, "512M", &cpu_set_size }, +#endif + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL, + NULL, &cpu_show_virt }, + { 0 } +}; + +/* after console sets flags for this CPU, replicate them to other CPUs */ +void cpu_sync_flags(CPU_UNIT* uptr) +{ + uint32 mask = UNIT_CONH; + for (uint32 k = 23; k <= 29; k++) + mask |= (1 << k); + + for (uint32 k = 0; k < sim_ncpus; k++) + { + CPU_UNIT* xcpu = cpu_units[k]; + if (xcpu != uptr) + { + xcpu->flags = (xcpu->flags & ~mask) | (uptr->flags & mask); + } + } +} + +DEBTAB cpu_deb[] = { + { "INTEXC", LOG_CPU_I }, + { "REI", LOG_CPU_R }, + { "CONTEXT", LOG_CPU_P }, + { NULL, 0 } +}; + +DEVICE cpu_dev = { + "CPU", cpu_units_as_units, cpu_reg, cpu_mod, + 1, 16, 32, 1, 16, 8, + &cpu_ex, &cpu_dep, &cpu_reset, + &cpu_boot, NULL, NULL, + NULL, DEV_DYNM | DEV_DEBUG | DEV_PERCPU, 0, + cpu_deb, &cpu_set_size, NULL +}; + + +/* + * Instruction stream prefetch routines. + * Special inline-able cases of get_istr for 1/2/4 byte fetches from the instruction stream. + * + * VAX_DIRECT_PREFETCH is streamlined for direct fetching from memory with minimum overhead + * when PC is in ADDR_IS_MEM range. + * + * When VAX_DIRECT_PREFETCH is enabled, prefetch can be in one of three modes: + * + * 1) Fetching from ADDR_IS_MEM range. + * + * mppc_rem > 0 + * mppc points to next byte to be fetched from M + * + * ibcnt = 0 + * ppc = -1 + * + * 2) Fetching from any range other than MEM, such as ROM or IO range. + * Will also work for MEM range, but less efficient than mode 1. + * + * mppc_rem = 0 + * mppc = invalid + * + * ibcnt >= 0 + * ppc != -1 + * + * 3) Fetching is in undefined mode and need to be flipped either to mode 1 or mode 2. + * + * mppc_rem = 0 + * mppc = invalid + * + * ibcnt = 0 + * ppc = -1 + * + */ +#if VAX_DIRECT_PREFETCH == 0 +SIM_INLINE static int32 get_istr_b (RUN_DECL, int32 acc) +{ + int32 bo = PC & 3; + const int32 lnt = L_BYTE; + + while (bo + lnt > ibcnt) + { + if (ppc < 0 || VA_GETOFF (ppc) == 0) + { + return get_istr (RUN_PASS, lnt, acc); + } + if (ibcnt == 0) + ibufl = ReadLP (RUN_PASS, ppc); + else + ibufh = ReadLP (RUN_PASS, ppc); + ppc = ppc + 4; + ibcnt = ibcnt + 4; + } + + PC += lnt; + int32 val = (ibufl >> (bo << 3)) & BMASK; + + if (bo + lnt >= 4) + { + ibufl = ibufh; + ibcnt = ibcnt - 4; + } + + return val; +} + +SIM_INLINE static int32 get_istr_w (RUN_DECL, int32 acc) +{ + int32 bo = PC & 3; + int32 val; + const int32 lnt = L_WORD; + + while (bo + lnt > ibcnt) + { + if (ppc < 0 || VA_GETOFF (ppc) == 0) + { + return get_istr (RUN_PASS, lnt, acc); + } + if (ibcnt == 0) + ibufl = ReadLP (RUN_PASS, ppc); + else + ibufh = ReadLP (RUN_PASS, ppc); + ppc = ppc + 4; + ibcnt = ibcnt + 4; + } + + PC += lnt; + + if (bo == 3) + val = ((ibufl >> 24) & 0xFF) | ((ibufh & 0xFF) << 8); + else + val = (ibufl >> (bo << 3)) & WMASK; + + if (bo + lnt >= 4) + { + ibufl = ibufh; + ibcnt = ibcnt - 4; + } + + return val; +} + +SIM_INLINE static int32 get_istr_l (RUN_DECL, int32 acc) +{ + int32 bo = PC & 3; + int32 val; + const int32 lnt = L_LONG; + + while (bo + lnt > ibcnt) + { + if (ppc < 0 || VA_GETOFF (ppc) == 0) + { + return get_istr (RUN_PASS, lnt, acc); + } + if (ibcnt == 0) + ibufl = ReadLP (RUN_PASS, ppc); + else + ibufh = ReadLP (RUN_PASS, ppc); + ppc = ppc + 4; + ibcnt = ibcnt + 4; + } + + PC += lnt; + + if (bo) + { + int32 sc = bo << 3; + val = (((ibufl >> sc) & align[bo]) | (((uint32) ibufh) << (32 - sc))); + } + else + { + val = ibufl; + } + + if (bo + lnt >= 4) + { + ibufl = ibufh; + ibcnt = ibcnt - 4; + } + + return val; +} +#endif // VAX_DIRECT_PREFETCH + +SIM_INLINE static int32 get_istr_x (RUN_DECL, int32 lnt, int32 acc) +{ + int32 bo = PC & 3; + int32 val; + + while (bo + lnt > ibcnt) + { + if (ppc < 0 || VA_GETOFF (ppc) == 0) + { + return get_istr (RUN_PASS, lnt, acc); + } + if (ibcnt == 0) + ibufl = ReadLP (RUN_PASS, ppc); + else + ibufh = ReadLP (RUN_PASS, ppc); + ppc = ppc + 4; + ibcnt = ibcnt + 4; + } + + PC += lnt; + + if (lnt == L_BYTE) + { + val = (ibufl >> (bo << 3)) & BMASK; + } + else if (lnt == L_WORD) + { + if (bo == 3) + val = ((ibufl >> 24) & 0xFF) | ((ibufh & 0xFF) << 8); + else + val = (ibufl >> (bo << 3)) & WMASK; + } + else if (bo) /* unaligned lw? */ + { + int32 sc = bo << 3; + val = (((ibufl >> sc) & align[bo]) | (((uint32) ibufh) << (32 - sc))); + } + else + { + val = ibufl; /* aligned lw */ + } + + if (bo + lnt >= 4) + { + ibufl = ibufh; + ibcnt = ibcnt - 4; + } + + return val; +} + +#if VAX_DIRECT_PREFETCH == 1 +static int32 get_istr_x_ni (RUN_DECL, int32 lnt, int32 acc); +static int32 get_istr_x2 (RUN_DECL, int32 lnt, int32 acc); + +SIM_INLINE static int32 get_istr_b_dir (RUN_DECL, int32 acc) +{ + const int32 lnt = L_BYTE; + int32 val; + if (likely(mppc_rem >= lnt)) + { + val = * (t_byte*) mppc; + PC += lnt; + mppc += lnt; + mppc_rem -= lnt; + } + else + { + val = get_istr_x_ni(RUN_PASS, lnt, acc); + } + return val; +} + +SIM_INLINE static int32 get_istr_w_dir (RUN_DECL, int32 acc) +{ + const int32 lnt = L_WORD; + int32 val; + if (likely(mppc_rem >= lnt)) + { + val = * (uint16*) mppc; + PC += lnt; + mppc += lnt; + mppc_rem -= lnt; + } + else + { + val = get_istr_x_ni(RUN_PASS, lnt, acc); + } + return val; +} + +SIM_INLINE static int32 get_istr_l_dir (RUN_DECL, int32 acc) +{ + const int32 lnt = L_LONG; + int32 val; + if (likely(mppc_rem >= lnt)) + { + val = * (uint32*) mppc; + PC += lnt; + mppc += lnt; + mppc_rem -= lnt; + } + else + { + val = get_istr_x_ni(RUN_PASS, lnt, acc); + } + return val; +} + +SIM_INLINE static int32 get_istr_x_dir (RUN_DECL, int32 lnt, int32 acc) +{ + if (likely(mppc_rem >= lnt)) + { + int32 val; + switch (lnt) + { + case L_BYTE: + val = * (t_byte*) mppc; + PC += L_BYTE; + mppc += L_BYTE; + mppc_rem -= L_BYTE; + return val; + + case L_WORD: + val = * (uint16*) mppc; + PC += L_WORD; + mppc += L_WORD; + mppc_rem -= L_WORD; + return val; + + case L_LONG: + val = * (uint32*) mppc; + PC += L_LONG; + mppc += L_LONG; + mppc_rem -= L_LONG; + return val; + + default: + /* never happens, just to suppress false compiler warning */ + return 0; + } + } + else + { + return get_istr_x2 (RUN_PASS, lnt, acc); + } +} + +static int32 get_istr_x2 (RUN_DECL, int32 lnt, int32 acc) +{ + int32 val = 0; + t_byte* valp = (t_byte*) & val; + + if (likely(ppc < 0)) + { + /* get_istr_x2 is called only when mppc_rem < 4 */ +incomplete_again: + switch (mppc_rem) + { + case 3: + *valp++ = *mppc++; + /* fall through */ + case 2: + *valp++ = *mppc++; + /* fall through */ + case 1: + *valp++ = *mppc++; + /* fall through */ + + PC += mppc_rem; + lnt -= mppc_rem; + mppc = NULL; + mppc_rem = 0; + break; + + case 0: + break; + } + + int32 pa = Test (RUN_PASS, PC, RA, NULL); + if (likely(ADDR_IS_MEM(pa))) + { + mppc = (t_byte*) M + pa; + mppc_rem = VA_PAGSIZE - (pa & VA_M_OFF); + if (mppc_rem < lnt) goto incomplete_again; + + switch (lnt) + { + case 4: + *valp++ = *mppc++; + /* fall through */ + case 3: + *valp++ = *mppc++; + /* fall through */ + case 2: + *valp++ = *mppc++; + /* fall through */ + case 1: + *valp++ = *mppc++; + /* fall through */ + + PC += lnt; + mppc_rem -= lnt; + break; + } + + return val; + } + + /* + * Detected crossover from RAM to non-RAM space. + * Should normally never happen within an instruction, but just in case it did, back up. + */ + int32 initial_mppc_rem = (int32) (valp - (t_byte*) & val); + PC -= initial_mppc_rem; + lnt += initial_mppc_rem; + } + + mppc_rem = 0; + mppc = NULL; + + return get_istr_x (RUN_PASS, lnt, acc); +} + +static int32 get_istr_x_ni (RUN_DECL, int32 lnt, int32 acc) +{ + return get_istr_x_dir (RUN_PASS, lnt, acc); +} + +void cpu_branch(RUN_DECL, int32 d) +{ + int32 newPC = PC + d; + + if (mppc_rem && (PC & ~VA_M_OFF) == (newPC & ~VA_M_OFF)) + { + mppc += d; + mppc_rem -= d; + } + else + { + FLUSH_ISTR; + } + + PC = newPC; +} +#endif // VAX_DIRECT_PREFETCH + + +/* + * Instruction loop + */ + +#if defined(__GNUC__) +/* + * Suppress false GCC warning about the following variables being possibly uninitialized: + * + * spec, rn, vfldrp1, brdisp, j, va + * + * We could have initialized them to 0 solely to suppress warning, but since they are inside + * innermost loop, we do not want to do this, for performance sake (yes, measurements show + * it does matter... to a surprisingly greater extent one would have expected). + * + * We could have also moved their declaration out of the loop into "try" block and initialized + * there, but better keep them inside the inner loop to help the optimizer. + * + * Since GCC does not have selective per-variable suppression of messages, we disable messages + * for the whole routine. + */ +# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ >= 40603) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +# else +# pragma message "" +# pragma message " Please ignore compiler's false warning that variables" +# pragma message " spec, rn, vfldrp1, brdisp, j, va" +# pragma message " may be used uninitialized in function sim_instr" +# pragma message "" +# endif +#endif + +t_stat sim_instr (RUN_DECL) +{ +/* + * Unlike with C setjmp/longjmp or C try/catch/finally macro-based exception handling, + * there is no requirement in C++ (ISO/ANSI 98) that automatic variables changed in the "try" block + * and used in the "catch" block should be declared volatile. + * We leave volatile qualifier nevertheless, to avoid the risk of running into possible bugs in a compiler. + */ +sim_try_volatile int32 opc; /* used by sim_catch_all, hence declared volatile */ +sim_try_volatile int32 cc; /* ... */ +int32 acc; /* set by catch (...) */ + +if ((PSL & PSL_MBZ) || /* validate PSL */ + ((PSL & PSL_CM) && BadCmPSL (RUN_PASS, PSL)) || /* validate PSL */ + ((PSL_GETCUR (PSL) != KERN) && /* esu => is, ipl = 0 */ + (PSL & (PSL_IS|PSL_IPL))) || + ((PSL & PSL_IS) && ((PSL & PSL_IPL) == 0))) /* is => ipl > 0 */ +{ + smp_printf("Invalid PSL\n"); + if (sim_log) + fprintf (sim_log, "Invalid PSL\n"); + return SCPE_STOP; +} +cc = PSL & CC_MASK; /* split PSL */ +PSL = PSL & ~CC_MASK; +in_ie = 0; /* not in exc */ +set_map_reg (RUN_PASS); /* set map reg */ +GET_CUR; /* set access mask */ +SET_IRQL; /* eval interrupts */ + +/* Main instruction loop */ +main_loop: + + FLUSH_ISTR; /* clear prefetch */ + +sim_try +{ + for (;;) + { + int32 spec, disp, rn, index, numspec; + int32 vfldrp1, brdisp, flg, mstat; + int32 i, j, r, rh, temp; + uint32 va, iad; + int32 opnd[OPND_SIZE]; /* operand queue */ + + // if (cpu_astop) { + // cpu_astop = 0; + // ABORT_INVALID_SYSOP; + // } + + fault_PC = PC; + recqptr = 0; /* clr recovery q */ + + if (unlikely(cpu_unit->sim_step) && /* check for step condition */ + cpu_unit->sim_step == cpu_unit->sim_instrs) + { + ABORT (SCPE_STEP); + } + + if (unlikely(cpu_unit->cpu_synclk_protect)) + { + if (likely(cpu_unit->cpu_synclk_protect_os)) + cpu_unit->cpu_synclk_protect_os--; + + /* + * we count down cpu_synclk_protect_dev by VAX instruction, not cycle, so sometimes + * it can be excessive, but not by much + */ + if (likely(cpu_unit->cpu_synclk_protect_dev)) + cpu_unit->cpu_synclk_protect_dev--; + + if (unlikely(cpu_unit->cpu_synclk_protect_os == 0 && cpu_unit->cpu_synclk_protect_dev == 0)) + { + cpu_unit->cpu_synclk_protect = FALSE; + check_synclk_pending(RUN_PASS); + } + } + + if (unlikely(weak_read(stop_cpus))) /* stop pending */ + ABORT (SCPE_STOP); + + if (unlikely(sim_interval <= 0)) /* chk clock queue */ + { + temp = sim_process_event (RUN_PASS); + if (temp) + ABORT (temp); + SET_IRQL; /* update interrupts */ + } + else if (unlikely(cpu_unit->cpu_intreg.weak_changed())) /* weak read check: possible change in interrupt register state */ + { + SET_IRQL; /* update interrupts */ + } + + /* Test for non-instruction dispatches, in SRM order + + - trap or interrupt (trpirq != 0) + - PSL set + + If any of these conditions are met, re-dispatch; otherwise, + set PSL from PSL. + */ + + if (trpirq) /* trap or interrupt? */ + { + if (temp = GET_TRAP (trpirq)) /* trap? */ + { + cc = intexc (RUN_PASS, SCB_ARITH, cc, 0, IE_EXC); /* take, clear trap */ + GET_CUR; /* set cur mode */ + in_ie = 1; + Write (RUN_PASS, SP - 4, temp, L_LONG, WA); /* write parameter */ + SP = SP - 4; + in_ie = 0; + SET_IRQL_NOSYNC; /* eval interrupts */ + } + else if (temp = GET_IRQL (trpirq)) /* interrupt? */ + { + int32 vec = 0; /* init vec to suppress false GCC warning */ + if (temp == IPL_HLTPIN) /* console halt? */ + { + // hlt_pin = 0; /* clear intr: in SMP version cleared in the console loop or in con_halt */ + trpirq = 0; /* clear everything */ + cc = con_halt (CON_HLTPIN, cc); /* invoke firmware */ + SET_IRQL; /* eval interrupts */ + continue; /* continue */ + } + else if (temp >= IPL_HMIN) /* hardware req? */ + vec = get_vector (RUN_PASS, temp); /* get vector */ + else if (temp > IPL_SMAX) + ABORT (STOP_UIPL); + else + { + vec = SCB_IPLSOFT + (temp << 2); + SISR = SISR & ~(1u << temp); + } + if (vec) /* take intr */ + cc = intexc (RUN_PASS, vec, cc, temp, IE_INT); + GET_CUR; /* set cur mode */ + SET_IRQL; /* eval interrupts */ + } + else + { + trpirq = 0; /* clear everything */ + SET_IRQL; /* eval interrupts */ + } + continue; + } + + if (PSL & (PSL_CM|PSL_TP|PSW_T)) /* PSL event? */ + { + if (PSL & PSL_TP) /* trace trap? */ + { + PSL = PSL & ~PSL_TP; /* clear */ + cc = intexc (RUN_PASS, SCB_TP, cc, 0, IE_EXC); /* take trap */ + GET_CUR; /* set cur mode */ + continue; + } + if (PSL & PSW_T) /* if T, set TP */ + PSL = PSL | PSL_TP; + if (PSL & PSL_CM) /* compat mode? */ + { + cc = op_cmode (RUN_PASS, cc); /* exec instr */ + continue; /* skip fetch */ + } + } /* end PSL event */ + + if (sim_brk_summ && sim_brk_test (RUN_PASS, (uint32) PC, SWMASK ('E'))) /* breakpoint? */ + { + ABORT (STOP_IBKPT); /* stop simulation */ + } + + if (unlikely(0 == --cpu_unit->syncw_countdown)) /* check synch window */ + { + if ((temp = syncw_checkinterval(RUN_PASS, FALSE)) != SCPE_OK) + ABORT (temp); + } + + cpu_cycle(); /* count cycles */ + cpu_unit->sim_instrs++; /* ... and instructions */ + GET_ISTR_B (opc); /* get opcode */ + if (opc == 0xFD) /* 2 byte op? */ + { + GET_ISTR_B (opc); /* get second byte */ + opc = opc | 0x100; /* flag */ + } + numspec = drom[opc][0]; /* get # specs */ + if (PSL & PSL_FPD) { + if ((numspec & DR_F) == 0) + RSVD_INST_FAULT; + } + else + { + numspec = numspec & DR_NSPMASK; /* get # specifiers */ + + /* Specifier flows. Operands are parsed and placed into queue opnd. + + r.bwl opnd[j] = value of operand + r.q opnd[j:j+1] = value of operand + r.o opnd[j:j+3] = value of operand + a.bwlqo opnd[j] = address of operand + m.bwl opnd[j] = value of operand + m.q opnd[j:j+1] = value of operand + m.o opnd[j:j+3] = value of operand + w.bwlqo opnd[j] = register/memory flag + opnd[j+1] = memory address + + For the last memory specifier, the specifier is in spec, the register + number is in rn, and the effective address is in va. Modify specifiers + (always last) can test spec > reg+PC, as short literal are illegal for + modifiers specifiers, and final index specifiers are always illegal. + */ + + for (i = 1, j = 0; i <= numspec; i++) { /* loop thru specs */ + disp = drom[opc][i]; /* get dispatch */ + if (disp >= BB) { + GET_ISTR_X (brdisp, DR_LNT (disp & 1)); + break; + } + GET_ISTR_B (spec); /* get spec byte */ + rn = spec & RGMASK; /* get reg # */ + disp = (spec & ~RGMASK) | disp; /* merge w dispatch */ + switch (disp) { /* dispatch spec */ + + /* Short literal - only read access permitted */ + + case SH0|RB: case SH0|RW: case SH0|RL: + case SH1|RB: case SH1|RW: case SH1|RL: + case SH2|RB: case SH2|RW: case SH2|RL: + case SH3|RB: case SH3|RW: case SH3|RL: + opnd[j++] = spec; + break; + + case SH0|RQ: case SH1|RQ: case SH2|RQ: case SH3|RQ: + opnd[j++] = spec; + opnd[j++] = 0; + break; + + case SH0|RO: case SH1|RO: case SH2|RO: case SH3|RO: + opnd[j++] = spec; + opnd[j++] = 0; + opnd[j++] = 0; + opnd[j++] = 0; + break; + + case SH0|RF: case SH1|RF: case SH2|RF: case SH3|RF: + opnd[j++] = (spec << 4) | 0x4000; + break; + + case SH0|RD: case SH1|RD: case SH2|RD: case SH3|RD: + opnd[j++] = (spec << 4) | 0x4000; + opnd[j++] = 0; + break; + + case SH0|RG: case SH1|RG: case SH2|RG: case SH3|RG: + opnd[j++] = (spec << 1) | 0x4000; + opnd[j++] = 0; + break; + + case SH0|RH: case SH1|RH: case SH2|RH: case SH3|RH: + opnd[j++] = ((spec & 0x7) << 29) | (0x4000 | ((spec >> 3) & 0x7)); + opnd[j++] = 0; + opnd[j++] = 0; + opnd[j++] = 0; + break; + + /* Register */ + + case GRN|RB: case GRN|MB: + CHECK_FOR_PC; + opnd[j++] = R[rn] & BMASK; + break; + + case GRN|RW: case GRN|MW: + CHECK_FOR_PC; + opnd[j++] = R[rn] & WMASK; + break; + + case GRN|VB: + vfldrp1 = R[(rn + 1) & RGMASK]; + case GRN|WB: case GRN|WW: case GRN|WL: case GRN|WQ: case GRN|WO: + opnd[j++] = rn; + case GRN|RL: case GRN|RF: case GRN|ML: + CHECK_FOR_PC; + opnd[j++] = R[rn]; + break; + + case GRN|RQ: case GRN|RD: case GRN|RG: case GRN|MQ: + CHECK_FOR_SP; + opnd[j++] = R[rn]; + opnd[j++] = R[rn + 1]; + break; + + case GRN|RO: case GRN|RH: case GRN|MO: + CHECK_FOR_AP; + opnd[j++] = R[rn]; + opnd[j++] = R[rn + 1]; + opnd[j++] = R[rn + 2]; + opnd[j++] = R[rn + 3]; + break; + + /* Register deferred, autodecrement */ + + case RGD|VB: + case RGD|WB: case RGD|WW: case RGD|WL: case RGD|WQ: case RGD|WO: + opnd[j++] = OP_MEM; + case RGD|AB: case RGD|AW: case RGD|AL: case RGD|AQ: case RGD|AO: + CHECK_FOR_PC; + va = opnd[j++] = R[rn]; + break; + + case ADC|VB: + case ADC|WB: case ADC|WW: case ADC|WL: case ADC|WQ: case ADC|WO: + opnd[j++] = OP_MEM; + case ADC|AB: case ADC|AW: case ADC|AL: case ADC|AQ: case ADC|AO: + CHECK_FOR_PC; + va = opnd[j++] = R[rn] = R[rn] - DR_LNT (disp); + recq[recqptr++] = RQ_REC (disp, rn); + break; + + case ADC|RB: case ADC|RW: case ADC|RL: case ADC|RF: + R[rn] = R[rn] - (DR_LNT (disp)); + recq[recqptr++] = RQ_REC (disp, rn); + case RGD|RB: case RGD|RW: case RGD|RL: case RGD|RF: + CHECK_FOR_PC; + opnd[j++] = Read (RUN_PASS, va = R[rn], DR_LNT (disp), RA); + break; + + case ADC|RQ: case ADC|RD: case ADC|RG: + R[rn] = R[rn] - 8; + recq[recqptr++] = RQ_REC (disp, rn); + case RGD|RQ: case RGD|RD: case RGD|RG: + CHECK_FOR_PC; + opnd[j++] = Read (RUN_PASS, va = R[rn], L_LONG, RA); + opnd[j++] = Read (RUN_PASS, R[rn] + 4, L_LONG, RA); + break; + + case ADC|RO: case ADC|RH: + R[rn] = R[rn] - 16; + recq[recqptr++] = RQ_REC (disp, rn); + case RGD|RO: case RGD|RH: + CHECK_FOR_PC; + j = ReadOcta (RUN_PASS, va = R[rn], opnd, j, RA); + break; + + case ADC|MB: case ADC|MW: case ADC|ML: + R[rn] = R[rn] - (DR_LNT (disp)); + recq[recqptr++] = RQ_REC (disp, rn); + case RGD|MB: case RGD|MW: case RGD|ML: + CHECK_FOR_PC; + opnd[j++] = Read (RUN_PASS, va = R[rn], DR_LNT (disp), WA); + break; + + case ADC|MQ: + R[rn] = R[rn] - 8; + recq[recqptr++] = RQ_REC (disp, rn); + case RGD|MQ: + CHECK_FOR_PC; + opnd[j++] = Read (RUN_PASS, va = R[rn], L_LONG, WA); + opnd[j++] = Read (RUN_PASS, R[rn] + 4, L_LONG, WA); + break; + + case ADC|MO: + R[rn] = R[rn] - 16; + recq[recqptr++] = RQ_REC (disp, rn); + case RGD|MO: + CHECK_FOR_PC; + j = ReadOcta (RUN_PASS, va = R[rn], opnd, j, WA); + break; + + /* Autoincrement */ + + case AIN|VB: + case AIN|WB: case AIN|WW: case AIN|WL: case AIN|WQ: case AIN|WO: + /* CHECK_FOR_PC; */ + opnd[j++] = OP_MEM; + case AIN|AB: case AIN|AW: case AIN|AL: case AIN|AQ: case AIN|AO: + va = opnd[j++] = R[rn]; + if (rn == nPC) { + if (DR_LNT (disp) >= L_QUAD) { + GET_ISTR_L (temp); + GET_ISTR_L (temp); + if (DR_LNT (disp) == L_OCTA) { + GET_ISTR_L (temp); + GET_ISTR_L (temp); + } + } + else GET_ISTR_X (temp, DR_LNT (disp)); + } + else { + R[rn] = R[rn] + DR_LNT (disp); + recq[recqptr++] = RQ_REC (disp, rn); + } + break; + + case AIN|RB: case AIN|RW: case AIN|RL: case AIN|RF: + va = R[rn]; + if (rn == nPC) { + GET_ISTR_X (opnd[j++], DR_LNT (disp)); + } + else { + opnd[j++] = Read (RUN_PASS, R[rn], DR_LNT (disp), RA); + R[rn] = R[rn] + DR_LNT (disp); + recq[recqptr++] = RQ_REC (disp, rn); + } + break; + + case AIN|RQ: case AIN|RD: case AIN|RG: + va = R[rn]; + if (rn == nPC) { + GET_ISTR_L (opnd[j++]); + GET_ISTR_L (opnd[j++]); + } + else { + opnd[j++] = Read (RUN_PASS, va, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, RA); + R[rn] = R[rn] + 8; + recq[recqptr++] = RQ_REC (disp, rn); + } + break; + + case AIN|RO: case AIN|RH: + va = R[rn]; + if (rn == nPC) { + GET_ISTR_L (opnd[j++]); + GET_ISTR_L (opnd[j++]); + GET_ISTR_L (opnd[j++]); + GET_ISTR_L (opnd[j++]); + } + else { + j = ReadOcta (RUN_PASS, va, opnd, j, RA); + R[rn] = R[rn] + 16; + recq[recqptr++] = RQ_REC (disp, rn); + } + break; + + case AIN|MB: case AIN|MW: case AIN|ML: + va = R[rn]; + if (rn == nPC) { + GET_ISTR_X (opnd[j++], DR_LNT (disp)); + } + else { + opnd[j++] = Read (RUN_PASS, R[rn], DR_LNT (disp), WA); + R[rn] = R[rn] + DR_LNT (disp); + recq[recqptr++] = RQ_REC (disp, rn); + } + break; + + case AIN|MQ: + va = R[rn]; + if (rn == nPC) { + GET_ISTR_L (opnd[j++]); + GET_ISTR_L (opnd[j++]); + } + else { + opnd[j++] = Read (RUN_PASS, va, L_LONG, WA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, WA); + R[rn] = R[rn] + 8; + recq[recqptr++] = RQ_REC (disp, rn); + } + break; + + case AIN|MO: + va = R[rn]; + if (rn == nPC) { + GET_ISTR_L (opnd[j++]); + GET_ISTR_L (opnd[j++]); + GET_ISTR_L (opnd[j++]); + GET_ISTR_L (opnd[j++]); + } + else { + j = ReadOcta (RUN_PASS, va, opnd, j, WA); + R[rn] = R[rn] + 16; + recq[recqptr++] = RQ_REC (disp, rn); + } + break; + + /* Autoincrement deferred */ + + case AID|VB: + case AID|WB: case AID|WW: case AID|WL: case AID|WQ: case AID|WO: + opnd[j++] = OP_MEM; + case AID|AB: case AID|AW: case AID|AL: case AID|AQ: case AID|AO: + if (rn == nPC) { + GET_ISTR_L (va = opnd[j++]); + } + else { + va = opnd[j++] = Read (RUN_PASS, R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + break; + + case AID|RB: case AID|RW: case AID|RL: case AID|RF: + if (rn == nPC) { + GET_ISTR_L (va); + } + else { + va = Read (RUN_PASS, R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + opnd[j++] = Read (RUN_PASS, va, DR_LNT (disp), RA); + break; + + case AID|RQ: case AID|RD: case AID|RG: + if (rn == nPC) { + GET_ISTR_L (va); + } + else { + va = Read (RUN_PASS, R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + opnd[j++] = Read (RUN_PASS, va, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, RA); + break; + + case AID|RO: case AID|RH: + if (rn == nPC) { + GET_ISTR_L (va); + } + else { + va = Read (RUN_PASS, R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + j = ReadOcta (RUN_PASS, va, opnd, j, RA); + break; + + case AID|MB: case AID|MW: case AID|ML: + if (rn == nPC) { + GET_ISTR_L (va); + } + else { + va = Read (RUN_PASS, R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + opnd[j++] = Read (RUN_PASS, va, DR_LNT (disp), WA); + break; + + case AID|MQ: + if (rn == nPC) { + GET_ISTR_L (va); + } + else { + va = Read (RUN_PASS, R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + opnd[j++] = Read (RUN_PASS, va, L_LONG, WA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, WA); + break; + + case AID|MO: + if (rn == nPC) { + GET_ISTR_L (va); + } + else { + va = Read (RUN_PASS, R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + j = ReadOcta (RUN_PASS, va, opnd, j, WA); + break; + + /* Byte displacement */ + + case BDP|VB: + case BDP|WB: case BDP|WW: case BDP|WL: case BDP|WQ: case BDP|WO: + opnd[j++] = OP_MEM; + case BDP|AB: case BDP|AW: case BDP|AL: case BDP|AQ: case BDP|AO: + GET_ISTR_B (temp); + va = opnd[j++] = R[rn] + SXTB (temp); + break; + + case BDP|RB: case BDP|RW: case BDP|RL: case BDP|RF: + GET_ISTR_B (temp); + va = R[rn] + SXTB (temp); + opnd[j++] = Read (RUN_PASS, va, DR_LNT (disp), RA); + break; + + case BDP|RQ: case BDP|RD: case BDP|RG: + GET_ISTR_B (temp); + va = R[rn] + SXTB (temp); + opnd[j++] = Read (RUN_PASS, va, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, RA); + break; + + case BDP|RO: case BDP|RH: + GET_ISTR_B (temp); + va = R[rn] + SXTB (temp); + j = ReadOcta (RUN_PASS, va, opnd, j, RA); + break; + + case BDP|MB: case BDP|MW: case BDP|ML: + GET_ISTR_B (temp); + va = R[rn] + SXTB (temp); + opnd[j++] = Read (RUN_PASS, va, DR_LNT (disp), WA); + break; + + case BDP|MQ: + GET_ISTR_B (temp); + va = R[rn] + SXTB (temp); + opnd[j++] = Read (RUN_PASS, va, L_LONG, WA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, WA); + break; + + case BDP|MO: + GET_ISTR_B (temp); + va = R[rn] + SXTB (temp); + j = ReadOcta (RUN_PASS, va, opnd, j, WA); + break; + + /* Byte displacement deferred */ + + case BDD|VB: + case BDD|WB: case BDD|WW: case BDD|WL: case BDD|WQ: case BDD|WO: + opnd[j++] = OP_MEM; + case BDD|AB: case BDD|AW: case BDD|AL: case BDD|AQ: case BDD|AO: + GET_ISTR_B (temp); + iad = R[rn] + SXTB (temp); + va = opnd[j++] = Read (RUN_PASS, iad, L_LONG, RA); + break; + + case BDD|RB: case BDD|RW: case BDD|RL: case BDD|RF: + GET_ISTR_B (temp); + iad = R[rn] + SXTB (temp); + va = Read (RUN_PASS, iad, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va, DR_LNT (disp), RA); + break; + + case BDD|RQ: case BDD|RD: case BDD|RG: + GET_ISTR_B (temp); + iad = R[rn] + SXTB (temp); + va = Read (RUN_PASS, iad, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, RA); + break; + + case BDD|RO: case BDD|RH: + GET_ISTR_B (temp); + iad = R[rn] + SXTB (temp); + va = Read (RUN_PASS, iad, L_LONG, RA); + j = ReadOcta (RUN_PASS, va, opnd, j, RA); + break; + + case BDD|MB: case BDD|MW: case BDD|ML: + GET_ISTR_B (temp); + iad = R[rn] + SXTB (temp); + va = Read (RUN_PASS, iad, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va, DR_LNT (disp), WA); + break; + + case BDD|MQ: + GET_ISTR_B (temp); + iad = R[rn] + SXTB (temp); + va = Read (RUN_PASS, iad, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va, L_LONG, WA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, WA); + break; + + case BDD|MO: + GET_ISTR_B (temp); + iad = R[rn] + SXTB (temp); + va = Read (RUN_PASS, iad, L_LONG, RA); + j = ReadOcta (RUN_PASS, va, opnd, j, WA); + break; + + /* Word displacement */ + + case WDP|VB: + case WDP|WB: case WDP|WW: case WDP|WL: case WDP|WQ: case WDP|WO: + opnd[j++] = OP_MEM; + case WDP|AB: case WDP|AW: case WDP|AL: case WDP|AQ: case WDP|AO: + GET_ISTR_W (temp); + va = opnd[j++] = R[rn] + SXTW (temp); + break; + + case WDP|RB: case WDP|RW: case WDP|RL: case WDP|RF: + GET_ISTR_W (temp); + va = R[rn] + SXTW (temp); + opnd[j++] = Read (RUN_PASS, va, DR_LNT (disp), RA); + break; + + case WDP|RQ: case WDP|RD: case WDP|RG: + GET_ISTR_W (temp); + va = R[rn] + SXTW (temp); + opnd[j++] = Read (RUN_PASS, va, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, RA); + break; + + case WDP|RO: case WDP|RH: + GET_ISTR_W (temp); + va = R[rn] + SXTW (temp); + j = ReadOcta (RUN_PASS, va, opnd, j, RA); + break; + + case WDP|MB: case WDP|MW: case WDP|ML: + GET_ISTR_W (temp); + va = R[rn] + SXTW (temp); + opnd[j++] = Read (RUN_PASS, va, DR_LNT (disp), WA); + break; + + case WDP|MQ: + GET_ISTR_W (temp); + va = R[rn] + SXTW (temp); + opnd[j++] = Read (RUN_PASS, va, L_LONG, WA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, WA); + break; + + case WDP|MO: + GET_ISTR_W (temp); + va = R[rn] + SXTW (temp); + j = ReadOcta (RUN_PASS, va, opnd, j, WA); + break; + + /* Word displacement deferred */ + + case WDD|VB: + case WDD|WB: case WDD|WW: case WDD|WL: case WDD|WQ: case WDD|WO: + opnd[j++] = OP_MEM; + case WDD|AB: case WDD|AW: case WDD|AL: case WDD|AQ: case WDD|AO: + GET_ISTR_W (temp); + iad = R[rn] + SXTW (temp); + va = opnd[j++] = Read (RUN_PASS, iad, L_LONG, RA); + break; + + case WDD|RB: case WDD|RW: case WDD|RL: case WDD|RF: + GET_ISTR_W (temp); + iad = R[rn] + SXTW (temp); + va = Read (RUN_PASS, iad, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va, DR_LNT (disp), RA); + break; + + case WDD|RQ: case WDD|RD: case WDD|RG: + GET_ISTR_W (temp); + iad = R[rn] + SXTW (temp); + va = Read (RUN_PASS, iad, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, RA); + break; + + case WDD|RO: case WDD|RH: + GET_ISTR_W (temp); + iad = R[rn] + SXTW (temp); + va = Read (RUN_PASS, iad, L_LONG, RA); + j = ReadOcta (RUN_PASS, va, opnd, j, RA); + break; + + case WDD|MB: case WDD|MW: case WDD|ML: + GET_ISTR_W (temp); + iad = R[rn] + SXTW (temp); + va = Read (RUN_PASS, iad, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va, DR_LNT (disp), WA); + break; + + case WDD|MQ: + GET_ISTR_W (temp); + iad = R[rn] + SXTW (temp); + va = Read (RUN_PASS, iad, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va, L_LONG, WA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, WA); + break; + + case WDD|MO: + GET_ISTR_W (temp); + iad = R[rn] + SXTW (temp); + va = Read (RUN_PASS, iad, L_LONG, RA); + j = ReadOcta (RUN_PASS, va, opnd, j, WA); + break; + + /* Longword displacement */ + + case LDP|VB: + case LDP|WB: case LDP|WW: case LDP|WL: case LDP|WQ: case LDP|WO: + opnd[j++] = OP_MEM; + case LDP|AB: case LDP|AW: case LDP|AL: case LDP|AQ: case LDP|AO: + GET_ISTR_L (temp); + va = opnd[j++] = R[rn] + temp; + break; + + case LDP|RB: case LDP|RW: case LDP|RL: case LDP|RF: + GET_ISTR_L (temp); + va = R[rn] + temp; + opnd[j++] = Read (RUN_PASS, va, DR_LNT (disp), RA); + break; + + case LDP|RQ: case LDP|RD: case LDP|RG: + GET_ISTR_L (temp); + va = R[rn] + temp; + opnd[j++] = Read (RUN_PASS, va, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, RA); + break; + + case LDP|RO: case LDP|RH: + GET_ISTR_L (temp); + va = R[rn] + temp; + j = ReadOcta (RUN_PASS, va, opnd, j, RA); + break; + + case LDP|MB: case LDP|MW: case LDP|ML: + GET_ISTR_L (temp); + va = R[rn] + temp; + opnd[j++] = Read (RUN_PASS, va, DR_LNT (disp), WA); + break; + + case LDP|MQ: + GET_ISTR_L (temp); + va = R[rn] + temp; + opnd[j++] = Read (RUN_PASS, va, L_LONG, WA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, WA); + break; + + case LDP|MO: + GET_ISTR_L (temp); + va = R[rn] + temp; + j = ReadOcta (RUN_PASS, va, opnd, j, WA); + break; + + /* Longword displacement deferred */ + + case LDD|VB: + case LDD|WB: case LDD|WW: case LDD|WL: case LDD|WQ: case LDD|WO: + opnd[j++] = OP_MEM; + case LDD|AB: case LDD|AW: case LDD|AL: case LDD|AQ: case LDD|AO: + GET_ISTR_L (temp); + iad = R[rn] + temp; + va = opnd[j++] = Read (RUN_PASS, iad, L_LONG, RA); + break; + + case LDD|RB: case LDD|RW: case LDD|RL: case LDD|RF: + GET_ISTR_L (temp); + iad = R[rn] + temp; + va = Read (RUN_PASS, iad, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va, DR_LNT (disp), RA); + break; + + case LDD|RQ: case LDD|RD: case LDD|RG: + GET_ISTR_L (temp); + iad = R[rn] + temp; + va = Read (RUN_PASS, iad, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, RA); + break; + + case LDD|RO: case LDD|RH: + GET_ISTR_L (temp); + iad = R[rn] + temp; + va = Read (RUN_PASS, iad, L_LONG, RA); + j = ReadOcta (RUN_PASS, va, opnd, j, RA); + break; + + case LDD|MB: case LDD|MW: case LDD|ML: + GET_ISTR_L (temp); + iad = R[rn] + temp; + va = Read (RUN_PASS, iad, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va, DR_LNT (disp), WA); + break; + + case LDD|MQ: + GET_ISTR_L (temp); + iad = R[rn] + temp; + va = Read (RUN_PASS, iad, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, va, L_LONG, WA); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, WA); + break; + + case LDD|MO: + GET_ISTR_L (temp); + iad = R[rn] + temp; + va = Read (RUN_PASS, iad, L_LONG, RA); + j = ReadOcta (RUN_PASS, va, opnd, j, WA); + break; + + /* Index */ + + case IDX|VB: + case IDX|WB: case IDX|WW: case IDX|WL: case IDX|WQ: case IDX|WO: + case IDX|AB: case IDX|AW: case IDX|AL: case IDX|AQ: case IDX|AO: + case IDX|MB: case IDX|MW: case IDX|ML: case IDX|MQ: case IDX|MO: + case IDX|RB: case IDX|RW: case IDX|RL: case IDX|RQ: case IDX|RO: + case IDX|RF: case IDX|RD: case IDX|RG: case IDX|RH: + CHECK_FOR_PC; + index = R[rn] << (disp & DR_LNMASK); + GET_ISTR_B (spec); + rn = spec & RGMASK; + switch (spec & ~RGMASK) { + case ADC: + R[rn] = R[rn] - DR_LNT (disp); + recq[recqptr++] = RQ_REC (ADC | (disp & DR_LNMASK), rn); + case RGD: + CHECK_FOR_PC; + index = index + R[rn]; + break; + + case AIN: + CHECK_FOR_PC; + index = index + R[rn]; + R[rn] = R[rn] + DR_LNT (disp); + recq[recqptr++] = RQ_REC (AIN | (disp & DR_LNMASK), rn); + break; + + case AID: + if (rn == nPC) { + GET_ISTR_L (temp); + } + else { + temp = Read (RUN_PASS, R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + index = temp + index; + break; + + case BDP: + GET_ISTR_B (temp); + index = index + R[rn] + SXTB (temp); + break; + + case BDD: + GET_ISTR_B (temp); + index = index + Read (RUN_PASS, R[rn] + SXTB (temp), L_LONG, RA); + break; + + case WDP: + GET_ISTR_W (temp); + index = index + R[rn] + SXTW (temp); + break; + + case WDD: + GET_ISTR_W (temp); + index = index + Read (RUN_PASS, R[rn] + SXTW (temp), L_LONG, RA); + break; + + case LDP: + GET_ISTR_L (temp); + index = index + R[rn] + temp; + break; + + case LDD: + GET_ISTR_L (temp); + index = index + Read (RUN_PASS, R[rn] + temp, L_LONG, RA); + break; + + default: + RSVD_ADDR_FAULT; /* end case idxspec */ + } + + switch (disp & (DR_ACMASK|DR_SPFLAG|DR_LNMASK)) { /* case acc+lnt */ + case VB: + case WB: case WW: case WL: case WQ: case WO: + opnd[j++] = OP_MEM; + case AB: case AW: case AL: case AQ: case AO: + va = opnd[j++] = index; + break; + + case RB: case RW: case RL: case RF: + opnd[j++] = Read (RUN_PASS, va = index, DR_LNT (disp), RA); + break; + + case RQ: case RD: case RG: + opnd[j++] = Read (RUN_PASS, va = index, L_LONG, RA); + opnd[j++] = Read (RUN_PASS, index + 4, L_LONG, RA); + break; + + case RO: case RH: + j = ReadOcta (RUN_PASS, va = index, opnd, j, RA); + break; + + case MB: case MW: case ML: + opnd[j++] = Read (RUN_PASS, va = index, DR_LNT (disp), WA); + break; + + case MQ: + opnd[j++] = Read (RUN_PASS, va = index, L_LONG, WA); + opnd[j++] = Read (RUN_PASS, index + 4, L_LONG, WA); + break; + + case MO: + j = ReadOcta (RUN_PASS, va = index, opnd, j, WA); + break; + + default: /* all others */ + RSVD_ADDR_FAULT; /* fault */ + break; + } /* end case access/lnt */ + break; /* end index */ + + default: /* all others */ + RSVD_ADDR_FAULT; /* fault */ + break; + } /* end case spec */ + } /* end for */ + } /* end if not FPD */ + + /* Optionally record instruction history */ + + if (unlikely(hst_on)) + { + /* + * Select recording slot in the circular buffer + */ + InstHistory* h = cpu_unit->cpu_hst + (cpu_unit->cpu_hst_index++ % hst_lnt); + + /* + * Do the recoding into the slot + */ + h->isRecorded = FALSE; + if (hst_sync) + { +#if HST_MAKE_STAMP_ISBOOL + if (! hst_make_stamp(& h->stamp)) + { + smp_printf ("\nHistory recording failed: unable to generate sequence stamp\n"); + if (sim_log) + fprintf (sim_log, "History recording failed: unable to generate sequence stamp\n"); + hst_on = FALSE; + goto skipRecording; + // ABORT_INVALID_SYSOP; + } +#else + hst_make_stamp(& h->stamp); +#endif + } + else + { + UINT64_INC(cpu_unit->cpu_hst_stamp); + h->stamp = cpu_unit->cpu_hst_stamp; + } + + h->iPC = fault_PC; + h->iPSL = PSL | cc; + h->opc = opc; + + for (int i = 0; i < j; i++) + h->opnd[i] = opnd[i]; + + int32 lim = PC - fault_PC; + if ((uint32) lim > INST_SIZE) + lim = INST_SIZE; + + for (int i = 0; i < lim; i++) + { + t_value wd; + + if (cpu_ex_run (RUN_PASS, &wd, fault_PC + i, cpu_unit, SWMASK ('V')) == SCPE_OK) + h->inst[i] = (uint8) wd; + else + { + h->inst[0] = h->inst[1] = 0xFF; + break; + } + } + + h->isRecorded = TRUE; + } + +#if HST_MAKE_STAMP_ISBOOL + skipRecording: +#endif + + /* Dispatch to instructions */ + + switch (opc) { + + /* Single operand instructions with dest, write only - CLRx dst.wx + + spec = reg/memory flag + rn = register number + va = virtual address + */ + + case CLRB: + WRITE_B (0); /* store result */ + CC_ZZ1P; /* set cc's */ + break; + + case CLRW: + WRITE_W (0); /* store result */ + CC_ZZ1P; /* set cc's */ + break; + + case CLRL: + { + t_bool b_sys_mask = FALSE; + uint32 old_sys_mask = 0; /* initialize to suppress false GCC warning */ + + /* Trap access to VMS CPU idle mask */ +#ifndef _DEBUG + /* When running under debugger this place can be hit with irrelevant "variable va uninitialized" warning + because for efficiency we order "va" check ahead of "spec" check */ + if (unlikely(va == (uint32) sys_idle_cpu_mask_va) && + sys_idle_cpu_mask_va && mapen && + spec > (GRN | nPC) && + (PSL & PSL_CUR) == 0 && is_os_running(RUN_PASS)) +#else + if (spec > (GRN | nPC) && + unlikely(va == (uint32) sys_idle_cpu_mask_va) && + sys_idle_cpu_mask_va && mapen && + (PSL & PSL_CUR) == 0 && is_os_running(RUN_PASS)) +#endif + { + b_sys_mask = TRUE; + old_sys_mask = Read (RUN_PASS, sys_idle_cpu_mask_va, L_LONG, RA); + } + + WRITE_L (0); /* store result */ + CC_ZZ1P; /* set cc's */ + + /* If bits in VMS CPU idle mask were cleared, wakeup corresponding CPUs */ + if (unlikely(b_sys_mask)) + wakeup_cpus(RUN_PASS, old_sys_mask, 0); + } + break; + + case CLRQ: + WRITE_Q (0, 0); /* store result */ + CC_ZZ1P; /* set cc's */ + break; + + /* Single operand instructions with source, read only - TSTx src.rx + + opnd[0] = source + */ + + case TSTB: + CC_IIZZ_B (op0); /* set cc's */ + break; + + case TSTW: + CC_IIZZ_W (op0); /* set cc's */ + break; + + case TSTL: + CC_IIZZ_L (op0); /* set cc's */ + + if (cc == CC_Z) + { + if ((cpu_idle_mask & VAX_IDLE_ULTOLD) && PSL_GETIPL (PSL) == 0x1 || /* running Old Ultrix or friends at IPL 1? */ + (cpu_idle_mask & VAX_IDLE_QUAD) && PSL_GETIPL (PSL) == 0x0) /* running Quasijarus or friends at IPL 0? */ + { + if ((fault_PC & 0x80000000) && /* in system space? */ + (fault_PC & 0x7fffffff) < 0x4000 && /* in low system space? */ + PC - fault_PC == 6) /* 6 byte instruction? */ + { + cpu_idle (RUN_PASS); /* idle loop */ + } + } + } + + break; + + /* Single operand instructions with source, read/write - op src.mx + + opnd[0] = operand + spec = reg/mem flag + rn = register number + va = operand address + */ + + case INCB: + r = (op0 + 1) & BMASK; /* calc result */ + WRITE_B (r); /* store result */ + CC_ADD_B (r, 1, op0); /* set cc's */ + break; + + case INCW: + r = (op0 + 1) & WMASK; /* calc result */ + WRITE_W (r); /* store result */ + CC_ADD_W (r, 1, op0); /* set cc's */ + break; + + case INCL: + r = (op0 + 1) & LMASK; /* calc result */ + WRITE_L (r); /* store result */ + CC_ADD_L (r, 1, op0); /* set cc's */ + break; + + case DECB: + r = (op0 - 1) & BMASK; /* calc result */ + WRITE_B (r); /* store result */ + CC_SUB_B (r, 1, op0); /* set cc's */ + break; + + case DECW: + r = (op0 - 1) & WMASK; /* calc result */ + WRITE_W (r); /* store result */ + CC_SUB_W (r, 1, op0); /* set cc's */ + break; + + case DECL: + r = (op0 - 1) & LMASK; /* calc result */ + WRITE_L (r); /* store result */ + CC_SUB_L (r, 1, op0); /* set cc's */ + break; + + /* Push instructions - PUSHL src.rl or PUSHAx src.ax + + opnd[0] = source + */ + + case PUSHL: case PUSHAB: case PUSHAW: case PUSHAL: case PUSHAQ: + Write (RUN_PASS, SP - 4, op0, L_LONG, WA); /* push operand */ + SP = SP - 4; /* decr stack ptr */ + CC_IIZP_L (op0); /* set cc's */ + break; + + /* Moves, converts, and ADAWI - op src.rx, dst.wx + + opnd[0] = source + spec = reg/mem flag + rn = register number + va = operand address + */ + + case MOVB: + WRITE_B (op0); /* result */ + CC_IIZP_B (op0); /* set cc's */ + break; + + case MOVW: case MOVZBW: + WRITE_W (op0); /* result */ + CC_IIZP_W (op0); /* set cc's */ + break; + + case MOVL: case MOVZBL: case MOVZWL: + case MOVAB: case MOVAW: case MOVAL: case MOVAQ: + WRITE_L (op0); /* result */ + CC_IIZP_L (op0); /* set cc's */ + break; + + case MCOMB: + r = op0 ^ BMASK; /* compl opnd */ + WRITE_B (r); /* store result */ + CC_IIZP_B (r); /* set cc's */ + break; + + case MCOMW: + r = op0 ^ WMASK; /* compl opnd */ + WRITE_W (r); /* store result */ + CC_IIZP_W (r); /* set cc's */ + break; + + case MCOML: + r = op0 ^ LMASK; /* compl opnd */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + break; + + case MNEGB: + r = (-op0) & BMASK; /* negate opnd */ + WRITE_B (r); /* store result */ + CC_SUB_B (r, op0, 0); /* set cc's */ + break; + + case MNEGW: + r = (-op0) & WMASK; /* negate opnd */ + WRITE_W (r); /* store result */ + CC_SUB_W (r, op0, 0); /* set cc's */ + break; + + case MNEGL: + r = (-op0) & LMASK; /* negate opnd */ + WRITE_L (r); /* store result */ + CC_SUB_L (r, op0, 0); /* set cc's */ + break; + + case CVTBW: + r = SXTBW (op0); /* ext sign */ + WRITE_W (r); /* store result */ + CC_IIZZ_W (r); /* set cc's */ + break; + + case CVTBL: + r = SXTB (op0); /* ext sign */ + WRITE_L (r); /* store result */ + CC_IIZZ_L (r); /* set cc's */ + break; + + case CVTWL: + r = SXTW (op0); /* ext sign */ + WRITE_L (r); /* store result */ + CC_IIZZ_L (r); /* set cc's */ + break; + + case CVTLB: + r = op0 & BMASK; /* set result */ + WRITE_B (r); /* store result */ + CC_IIZZ_B (r); /* initial cc's */ + if ((op0 > 127) || (op0 < -128)) { + V_INTOV; + } + break; + + case CVTLW: + r = op0 & WMASK; /* set result */ + WRITE_W (r); /* store result */ + CC_IIZZ_W (r); /* initial cc's */ + if ((op0 > 32767) || (op0 < -32768)) { + V_INTOV; + } + break; + + case CVTWB: + r = op0 & BMASK; /* set result */ + WRITE_B (r); /* store result */ + CC_IIZZ_B (r); /* initial cc's */ + temp = SXTW (op0); /* cvt op to long */ + if ((temp > 127) || (temp < -128)) { + V_INTOV; + } + break; + + case ADAWI: + /* pass "va" as conditonal to suppress false GCC warning */ + op_adawi (RUN_PASS, opnd, acc, spec, rn, (spec > (GRN | nPC)) ? va : 0, cc /* cc passed by reference*/); + break; + + /* Integer operates, 2 operand, read only - op src1.rx, src2.rx + + opnd[0] = source1 + opnd[1] = source2 + */ + + case CMPB: + CC_CMP_B (op0, op1); /* set cc's */ + break; + + case CMPW: + CC_CMP_W (op0, op1); /* set cc's */ + break; + + case CMPL: + CC_CMP_L (op0, op1); /* set cc's */ + break; + + case BITB: + r = op1 & op0; /* calc result */ + CC_IIZP_B (r); /* set cc's */ + break; + + case BITW: + r = op1 & op0; /* calc result */ + CC_IIZP_W (r); /* set cc's */ + break; + + case BITL: + r = op1 & op0; /* calc result */ + CC_IIZP_L (r); /* set cc's */ + + if (cc == CC_Z) + { + if ((cpu_idle_mask & VAX_IDLE_ULT) && /* running Ultrix or friends? */ + (PSL & PSL_IS) != 0 && /* on IS? */ + PSL_GETIPL (PSL) == 0x18 && /* at IPL 18? */ + (fault_PC & 0x80000000) && /* in system space? */ + PC - fault_PC == 8 && /* 8 byte instruction? */ + (fault_PC & 0x7fffffff) < 0x6000) /* in low system space? */ + { + cpu_idle (RUN_PASS); /* idle loop */ + } + } + + break; + + /* Integer operates, 2 operand read/write, and 3 operand, also MOVQ + op2 src.rx, dst.mx op3 src.rx, src.rx, dst.wx + + opnd[0] = source1 + opnd[1] = source2 + spec = register/memory flag + rn = register number + va = memory address + */ + + case ADDB2: case ADDB3: + r = (op1 + op0) & BMASK; /* calc result */ + WRITE_B (r); /* store result */ + CC_ADD_B (r, op0, op1); /* set cc's */ + break; + + case ADDW2: case ADDW3: + r = (op1 + op0) & WMASK; /* calc result */ + WRITE_W (r); /* store result */ + CC_ADD_W (r, op0, op1); /* set cc's */ + break; + + case ADWC: + r = (op1 + op0 + (cc & CC_C)) & LMASK; /* calc result */ + WRITE_L (r); /* store result */ + CC_ADD_L (r, op0, op1); /* set cc's */ + if ((r == op1) && op0) /* special case */ + cc = cc | CC_C; + break; + + case ADDL2: case ADDL3: + r = (op1 + op0) & LMASK; /* calc result */ + WRITE_L (r); /* store result */ + CC_ADD_L (r, op0, op1); /* set cc's */ + break; + + case SUBB2: case SUBB3: + r = (op1 - op0) & BMASK; /* calc result */ + WRITE_B (r); /* store result */ + CC_SUB_B (r, op0, op1); /* set cc's */ + break; + + case SUBW2: case SUBW3: + r = (op1 - op0) & WMASK; /* calc result */ + WRITE_W (r); /* store result */ + CC_SUB_W (r, op0, op1); /* set cc's */ + break; + + case SBWC: + r = (op1 - op0 - (cc & CC_C)) & LMASK; /* calc result */ + WRITE_L (r); /* store result */ + CC_SUB_L (r, op0, op1); /* set cc's */ + if ((op0 == op1) && r) /* special case */ + cc = cc | CC_C; + break; + + case SUBL2: case SUBL3: + r = (op1 - op0) & LMASK; /* calc result */ + WRITE_L (r); /* store result */ + CC_SUB_L (r, op0, op1); /* set cc's */ + break; + + case MULB2: case MULB3: + temp = SXTB (op0) * SXTB (op1); /* multiply */ + r = temp & BMASK; /* mask to result */ + WRITE_B (r); /* store result */ + CC_IIZZ_B (r); /* set cc's */ + if ((temp > 127) || (temp < -128)) { + V_INTOV; + } + break; + + case MULW2: case MULW3: + temp = SXTW (op0) * SXTW (op1); /* multiply */ + r = temp & WMASK; /* mask to result */ + WRITE_W (r); /* store result */ + CC_IIZZ_W (r); /* set cc's */ + if ((temp > 32767) || (temp < -32768)) { + V_INTOV; + } + break; + + case MULL2: case MULL3: + r = op_emul (RUN_PASS, op0, op1, &rh); /* get 64b result */ + WRITE_L (r); /* store result */ + CC_IIZZ_L (r); /* set cc's */ + if (rh != ((r & LSIGN)? -1: 0)) { /* chk overflow */ + V_INTOV; + } + break; + + case DIVB2: case DIVB3: + if (op0 == 0) { /* div by zero? */ + r = op1; + temp = CC_V; + SET_TRAP (TRAP_DIVZRO); + } + else if ((op0 == BMASK) && (op1 == BSIGN)) { /* overflow? */ + r = op1; + temp = CC_V; + INTOV; + } + else { + r = SXTB (op1) / SXTB (op0); /* ok, divide */ + temp = 0; + } + r = r & BMASK; /* mask to result */ + WRITE_B (r); /* write result */ + CC_IIZZ_B (r); /* set cc's */ + cc = cc | temp; /* error? set V */ + break; + + case DIVW2: case DIVW3: + if (op0 == 0) { /* div by zero? */ + r = op1; + temp = CC_V; + SET_TRAP (TRAP_DIVZRO); + } + else if ((op0 == WMASK) && (op1 == WSIGN)) { /* overflow? */ + r = op1; + temp = CC_V; + INTOV; + } + else { + r = SXTW (op1) / SXTW (op0); /* ok, divide */ + temp = 0; + } + r = r & WMASK; /* mask to result */ + WRITE_W (r); /* write result */ + CC_IIZZ_W (r); /* set cc's */ + cc = cc | temp; /* error? set V */ + break; + + case DIVL2: case DIVL3: + if (op0 == 0) { /* div by zero? */ + r = op1; + temp = CC_V; + SET_TRAP (TRAP_DIVZRO); + } + else if ((uint32) op0 == LMASK && (uint32) op1 == LSIGN) { /* overflow? */ + r = op1; + temp = CC_V; + INTOV; + } + else { + r = op1 / op0; /* ok, divide */ + temp = 0; + } + r = r & LMASK; /* mask to result */ + WRITE_L (r); /* write result */ + CC_IIZZ_L (r); /* set cc's */ + cc = cc | temp; /* error? set V */ + break; + + case BISB2: case BISB3: + r = op1 | op0; /* calc result */ + WRITE_B (r); /* store result */ + CC_IIZP_B (r); /* set cc's */ + break; + + case BISW2: case BISW3: + r = op1 | op0; /* calc result */ + WRITE_W (r); /* store result */ + CC_IIZP_W (r); /* set cc's */ + break; + + case BISL2: case BISL3: + r = op1 | op0; /* calc result */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + break; + + case BICB2: case BICB3: + r = op1 & ~op0; /* calc result */ + WRITE_B (r); /* store result */ + CC_IIZP_B (r); /* set cc's */ + break; + + case BICW2: case BICW3: + r = op1 & ~op0; /* calc result */ + WRITE_W (r); /* store result */ + CC_IIZP_W (r); /* set cc's */ + break; + + case BICL2: case BICL3: + { + t_bool b_sys_mask = FALSE; + uint32 old_sys_mask = 0; /* initialize to suppress false GCC warning */ + + /* Trap access to VMS CPU idle mask */ +#ifndef _DEBUG + /* When running under debugger this place can be hit with irrelevant "variable va uninitialized" warning + because for efficiency we order "va" check ahead of "spec" check */ + if (unlikely(va == (uint32) sys_idle_cpu_mask_va) && + sys_idle_cpu_mask_va && mapen && + spec > (GRN | nPC) && + (PSL & PSL_CUR) == 0 && is_os_running(RUN_PASS)) +#else + if (spec > (GRN | nPC) && + unlikely(va == (uint32) sys_idle_cpu_mask_va) && + sys_idle_cpu_mask_va && mapen && + (PSL & PSL_CUR) == 0 && is_os_running(RUN_PASS)) +#endif + { + b_sys_mask = TRUE; + old_sys_mask = Read (RUN_PASS, sys_idle_cpu_mask_va, L_LONG, RA); + } + + r = op1 & ~op0; /* calc result */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + + /* If bits in VMS CPU idle mask were cleared, wakeup corresponding CPUs */ + if (unlikely(b_sys_mask)) + wakeup_cpus(RUN_PASS, old_sys_mask, r); + } + break; + + case XORB2: case XORB3: + r = op1 ^ op0; /* calc result */ + WRITE_B (r); /* store result */ + CC_IIZP_B (r); /* set cc's */ + break; + + case XORW2: case XORW3: + r = op1 ^ op0; /* calc result */ + WRITE_W (r); /* store result */ + CC_IIZP_W (r); /* set cc's */ + break; + + case XORL2: case XORL3: + r = op1 ^ op0; /* calc result */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + break; + + /* MOVQ - movq src.rq, dst.wq + + opnd[0:1] = source + spec = register/memory flag + rn = register number + va = memory address + + */ + + case MOVQ: + WRITE_Q (op0, op1); /* store result */ + CC_IIZP_Q (op0, op1); + break; + + /* Shifts - op shf.rb,src.rl,dst.wl + + opnd[0] = shift count + opnd[1] = source + spec = register/memory flag + rn = register number + va = memory address + */ + + case ROTL: + j = op0 % 32; /* reduce sc, mod 32 */ + if (j) + r = ((((uint32) op1) << j) | (((uint32) op1) >> (32 - j))) & LMASK; + else r = op1; + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + break; + + case ASHL: + if (op0 & BSIGN) { /* right shift? */ + temp = 0x100 - op0; /* get |shift| */ + if (temp > 31) /* sc > 31? */ + r = (op1 & LSIGN)? LMASK: 0; + else r = op1 >> temp; /* shift */ + WRITE_L (r); /* store result */ + CC_IIZZ_L (r); /* set cc's */ + break; + } + else { + if (op0 > 31) /* sc > 31? */ + r = temp = 0; + else { + r = (((uint32) op1) << op0) & LMASK; /* shift */ + temp = r >> op0; /* shift back */ + } + WRITE_L (r); /* store result */ + CC_IIZZ_L (r); /* set cc's */ + if (op1 != temp) { /* bits lost? */ + V_INTOV; + } + } + break; + + case ASHQ: + r = op_ashq (RUN_PASS, opnd, &rh, &flg); /* do qw shift */ + WRITE_Q (r, rh); /* store results */ + CC_IIZZ_Q (r, rh); /* set cc's */ + if (flg) { /* if ovflo, set */ + V_INTOV; + } + break; + + /* EMUL - emul mplr.rl,mpcn.rl,add.rl,dst.wq + + op0 = multiplier + op1 = multiplicand + op2 = adder + op3:op4 = destination (.wq) + */ + + case EMUL: + r = op_emul (RUN_PASS, op0, op1, &rh); /* calc 64b result */ + r = r + op2; /* add 32b value */ + rh = rh + (((uint32) r) < ((uint32) op2)) - /* into 64b result */ + ((op2 & LSIGN)? 1: 0); + WRITE_Q (r, rh); /* write result */ + CC_IIZZ_Q (r, rh); /* set cc's */ + break; + + /* EDIV - ediv dvr.rl,dvd.rq,quo.wl,rem.wl + + op0 = divisor (.rl) + op1:op2 = dividend (.rq) + op3:op4 = quotient address (.wl) + op5:op6 = remainder address (.wl) + */ + + case EDIV: + if (op5 < 0) /* wtest remainder */ + Read (RUN_PASS, op6, L_LONG, WA); + if (op0 == 0) { /* divide by zero? */ + flg = CC_V; /* set V */ + r = opnd[1]; /* quo = low divd */ + rh = 0; /* rem = 0 */ + SET_TRAP (TRAP_DIVZRO); /* set trap */ + } + else { + r = op_ediv (RUN_PASS, opnd, &rh, &flg); /* extended divide */ + if (flg) { /* if ovf+IV, set trap */ + INTOV; + } + } + if (op3 >= 0) /* store quotient */ + R[op3] = r; + else Write (RUN_PASS, op4, r, L_LONG, WA); + if (op5 >= 0) /* store remainder */ + R[op5] = rh; + else Write (RUN_PASS, op6, rh, L_LONG, WA); + CC_IIZZ_L (r); /* set cc's */ + cc = cc | flg; /* set V if required */ + break; + + /* Control instructions */ + + /* Simple branches and subroutine calls */ + + case BRB: + BRANCHB (brdisp); /* branch */ + if (PC == fault_PC) + { + if (PSL_GETIPL (PSL) == 0x1F) + ABORT (STOP_LOOP); + else + cpu_idle (RUN_PASS); + } + break; + + case BRW: + BRANCHW (brdisp); /* branch */ + if (PC == fault_PC) + { + if (PSL_GETIPL (PSL) == 0x1F) + ABORT (STOP_LOOP); + else + cpu_idle (RUN_PASS); + } + break; + + case BSBB: + Write (RUN_PASS, SP - 4, PC, L_LONG, WA); /* push PC on stk */ + SP = SP - 4; /* decr stk ptr */ + BRANCHB (brdisp); /* branch */ + break; + + case BSBW: + Write (RUN_PASS, SP - 4, PC, L_LONG, WA); /* push PC on stk */ + SP = SP - 4; /* decr stk ptr */ + BRANCHW (brdisp); /* branch */ + break; + + case BGEQ: + if (!(cc & CC_N)) /* br if N = 0 */ + BRANCHB (brdisp); + break; + + case BLSS: + if (cc & CC_N) /* br if N = 1 */ + BRANCHB (brdisp); + break; + + case BNEQ: + if (!(cc & CC_Z)) /* br if Z = 0 */ + BRANCHB (brdisp); + break; + + case BEQL: + if (cc & CC_Z) /* br if Z = 1 */ + { + BRANCHB (brdisp); + + if (mapen == 0 && /* MAPEN off? */ + (PSL & PSL_IS) != 0 && /* on IS? */ + PSL_GETIPL (PSL) == 0x1F && /* at IPL 31 */ + fault_PC == ROM_PC_CHAR_PROMPT) /* Boot ROM Character Prompt */ + { + cpu_idle (RUN_PASS); + } + } + + break; + + case BVC: + if (!(cc & CC_V)) /* br if V = 0 */ + BRANCHB (brdisp); + break; + + case BVS: + if (cc & CC_V) /* br if V = 1 */ + BRANCHB (brdisp); + break; + + case BGEQU: + if (!(cc & CC_C)) /* br if C = 0 */ + BRANCHB (brdisp); + break; + + case BLSSU: + if (cc & CC_C) /* br if C = 1 */ + BRANCHB (brdisp); + break; + + case BGTR: + if (!(cc & (CC_N | CC_Z))) /* br if N | Z = 0 */ + BRANCHB (brdisp); + break; + + case BLEQ: + if (cc & (CC_N | CC_Z)) /* br if N | Z = 1 */ + BRANCHB (brdisp); + break; + + case BGTRU: + if (!(cc & (CC_C | CC_Z))) /* br if C | Z = 0 */ + BRANCHB (brdisp); + break; + + case BLEQU: + if (cc & (CC_C | CC_Z)) /* br if C | Z = 1 */ + BRANCHB (brdisp); + break; + + /* Simple jumps and subroutine calls - op addr.ab + + opnd[0] = address + */ + + case JSB: + Write (RUN_PASS, SP - 4, PC, L_LONG, WA); /* push PC on stk */ + SP = SP - 4; /* decr stk ptr */ + + case JMP: + JUMP (op0); /* jump */ + break; + + case RSB: + temp = Read (RUN_PASS, SP, L_LONG, RA); /* get top of stk */ + SP = SP + 4; /* incr stk ptr */ + JUMP (temp); + break; + + /* SOB instructions - op idx.ml,disp.bb + + opnd[0] = index + spec = register/memory flag + rn = register number + va = memory address + */ + + case SOBGEQ: + r = op0 - 1; /* decr index */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + V_SUB_L (r, 1, op0); /* test for ovflo */ + if (r >= 0) /* if >= 0, branch */ + BRANCHB (brdisp); + break; + + case SOBGTR: + r = op0 - 1; /* decr index */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + V_SUB_L (r, 1, op0); /* test for ovflo */ + if (r > 0) /* if >= 0, branch */ + BRANCHB (brdisp); + break; + + /* AOB instructions - op limit.rl,idx.ml,disp.bb + + opnd[0] = limit + opnd[1] = index + spec = register/memory flag + rn = register number + va = memory address + */ + + case AOBLSS: + r = op1 + 1; /* incr index */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + V_ADD_L (r, 1, op1); /* test for ovflo */ + if (r < op0) /* if < lim, branch */ + BRANCHB (brdisp); + break; + + case AOBLEQ: + r = op1 + 1; /* incr index */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + V_ADD_L (r, 1, op1); /* test for ovflo */ + if (r <= op0) /* if < lim, branch */ + BRANCHB (brdisp); + break; + + /* ACB instructions - op limit.rx,add.rx,index.mx,disp.bw + + opnd[0] = limit + opnd[1] = adder + opnd[2] = index + spec = register/memory flag + rn = register number + va = memory address + */ + + case ACBB: + r = (op2 + op1) & BMASK; /* calc result */ + WRITE_B (r); /* store result */ + CC_IIZP_B (r); /* set cc's */ + V_ADD_B (r, op1, op2); /* test for ovflo */ + if ((op1 & BSIGN)? (SXTB (r) >= SXTB (op0)): (SXTB (r) <= SXTB (op0))) + BRANCHW (brdisp); + break; + + case ACBW: + r = (op2 + op1) & WMASK; /* calc result */ + WRITE_W (r); /* store result */ + CC_IIZP_W (r); /* set cc's */ + V_ADD_W (r, op1, op2); /* test for ovflo */ + if ((op1 & WSIGN)? (SXTW (r) >= SXTW (op0)): (SXTW (r) <= SXTW (op0))) + BRANCHW (brdisp); + break; + + case ACBL: + r = (op2 + op1) & LMASK; /* calc result */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + V_ADD_L (r, op1, op2); /* test for ovflo */ + if ((op1 & LSIGN)? (r >= op0): (r <= op0)) + BRANCHW (brdisp); + break; + + /* CASE instructions - casex sel.rx,base.rx,lim.rx + + opnd[0] = selector + opnd[1] = base + opnd[2] = limit + */ + + case CASEB: + r = (op0 - op1) & BMASK; /* sel - base */ + CC_CMP_B (r, op2); /* r:limit, set cc's */ + if (r > op2) /* r > limit (unsgnd)? */ + JUMP (PC + ((op2 + 1) * 2)); + else { + temp = Read (RUN_PASS, PC + (r * 2), L_WORD, RA); + BRANCHW (temp); + } + break; + + case CASEW: + r = (op0 - op1) & WMASK; /* sel - base */ + CC_CMP_W (r, op2); /* r:limit, set cc's */ + if (r > op2) /* r > limit (unsgnd)? */ + JUMP (PC + ((op2 + 1) * 2)); + else { + temp = Read (RUN_PASS, PC + (r * 2), L_WORD, RA); + BRANCHW (temp); + } + break; + + case CASEL: + r = (op0 - op1) & LMASK; /* sel - base */ + CC_CMP_L (r, op2); /* r:limit, set cc's */ + if (((uint32) r) > ((uint32) op2)) /* r > limit (unsgnd)? */ + JUMP (PC + ((op2 + 1) * 2)); + else { + temp = Read (RUN_PASS, PC + (r * 2), L_WORD, RA); + BRANCHW (temp); + } + break; + + /* Branch on bit instructions - bbxy pos.rl,op.wb,disp.bb + + opnd[0] = position + opnd[1] = register number/memory flag + opnd[2] = memory address, if memory + */ + + case BBS: + if (op_bb_n(RUN_PASS, opnd, acc)) /* br if bit set */ + { + BRANCHB(brdisp); + + if ((PSL & PSL_IS) && /* on IS? */ + PSL_GETIPL(PSL) == 0x3 && /* at IPL 3? */ + (cpu_idle_mask & VAX_IDLE_VMS)) /* running VMS? */ + { + cpu_idle(RUN_PASS); /* idle loop */ + } + } + break; + + case BBC: + if (!op_bb_n (RUN_PASS, opnd, acc)) /* br if bit clr */ + BRANCHB (brdisp); + break; + + case BBSS: case BBSSI: + if (op_bb_x (RUN_PASS, opnd, 1, acc, opc == BBSSI)) /* br if set, set */ + BRANCHB (brdisp); + break; + + case BBCC: case BBCCI: + if (!op_bb_x (RUN_PASS, opnd, 0, acc, opc == BBCCI)) /* br if clr, clr*/ + BRANCHB (brdisp); + break; + + case BBSC: + if (op_bb_x (RUN_PASS, opnd, 0, acc, FALSE)) /* br if clr, set */ + BRANCHB (brdisp); + break; + + case BBCS: + if (!op_bb_x (RUN_PASS, opnd, 1, acc, FALSE)) /* br if set, clr */ + BRANCHB (brdisp); + break; + + case BLBS: + if (op0 & 1) /* br if bit set */ + BRANCHB (brdisp); + break; + + case BLBC: + if ((op0 & 1) == 0) /* br if bit clear */ + BRANCHB (brdisp); + break; + + /* Extract field instructions - ext?v pos.rl,size.rb,base.wb,dst.wl + + opnd[0] = position + opnd[1] = size + opnd[2] = register number/memory flag + opnd[3] = register content/memory address + spec = register/memory flag + rn = register number + va = memory address + */ + + case EXTV: + r = op_extv (RUN_PASS, opnd, vfldrp1, acc); /* get field */ + if (r & byte_sign[op1]) + r = r | ~byte_mask[op1]; + WRITE_L (r); /* store field */ + CC_IIZP_L (r); /* set cc's */ + break; + + case EXTZV: + r = op_extv (RUN_PASS, opnd, vfldrp1, acc); /* get field */ + WRITE_L (r); /* store field */ + CC_IIZP_L (r); /* set cc's */ + break; + + /* Compare field instructions - cmp?v pos.rl,size.rb,base.wb,src2.rl + + opnd[0] = position + opnd[1] = size + opnd[2] = register number/memory flag + opnd[3] = register content/memory address + opnd[4] = source2 + */ + + case CMPV: + r = op_extv (RUN_PASS, opnd, vfldrp1, acc); /* get field */ + if (r & byte_sign[op1]) + r = r | ~byte_mask[op1]; + CC_CMP_L (r, op4); /* set cc's */ + break; + + case CMPZV: + r = op_extv (RUN_PASS, opnd, vfldrp1, acc); /* get field */ + CC_CMP_L (r, op4); /* set cc's */ + break; + + /* Find first field instructions - ff? pos.rl,size.rb,base.wb,dst.wl + + opnd[0] = position + opnd[1] = size + opnd[2] = register number/memory flag + opnd[3] = register content/memory address + spec = register/memory flag + rn = register number + va = memory address + */ + + case FFS: + r = op_extv (RUN_PASS, opnd, vfldrp1, acc); /* get field */ + temp = op_ffs (RUN_PASS, r, op1); /* find first 1 */ + WRITE_L (op0 + temp); /* store result */ + cc = r? 0: CC_Z; /* set cc's */ + break; + + case FFC: + r = op_extv (RUN_PASS, opnd, vfldrp1, acc); /* get field */ + r = r ^ byte_mask[op1]; /* invert bits */ + temp = op_ffs (RUN_PASS, r, op1); /* find first 1 */ + WRITE_L (op0 + temp); /* store result */ + cc = r? 0: CC_Z; /* set cc's */ + break; + + /* Insert field instruction - insv src.rl,pos.rb,size.rl,base.wb + + opnd[0] = source + opnd[1] = position + opnd[2] = size + opnd[3] = register number/memory flag + opnd[4] = register content/memory address + */ + + case INSV: + op_insv (RUN_PASS, opnd, vfldrp1, acc); /* insert field */ + break; + + /* Call and return - call? arg.rx,proc.ab + + opnd[0] = argument + opnd[1] = procedure address + */ + + case CALLS: + cc = op_call (RUN_PASS, opnd, TRUE, acc); + break; + + case CALLG: + cc = op_call (RUN_PASS, opnd, FALSE, acc); + break; + + case RET: + cc = op_ret (RUN_PASS, acc); + break; + + /* Miscellaneous instructions */ + + case HALT: + if (PSL & PSL_CUR) /* not kern? rsvd inst */ + RSVD_INST_FAULT; + else if (cpu_unit->flags & UNIT_CONH) /* halt to console? */ + cc = con_halt (CON_HLTINS, cc); /* enter firmware */ + else { + ABORT (STOP_HALT); /* halt to simulator */ + } + + case NOP: + break; + + case BPT: + SETPC (fault_PC); + cc = intexc (RUN_PASS, SCB_BPT, cc, 0, IE_EXC); + GET_CUR; + break; + + case XFC: + SETPC (fault_PC); + cc = intexc (RUN_PASS, SCB_XFC, cc, 0, IE_EXC); + GET_CUR; + break; + + case BISPSW: + if (opnd[0] & PSW_MBZ) + RSVD_OPND_FAULT; + PSL = PSL | (opnd[0] & ~CC_MASK); + cc = cc | (opnd[0] & CC_MASK); + break; + + case BICPSW: + if (opnd[0] & PSW_MBZ) + RSVD_OPND_FAULT; + PSL = PSL & ~opnd[0]; + cc = cc & ~opnd[0]; + break; + + case MOVPSL: + r = PSL | cc; + WRITE_L (r); + break; + + case PUSHR: + op_pushr (RUN_PASS, opnd, acc); + break; + + case POPR: + op_popr (RUN_PASS, opnd, acc); + break; + + case INDEX: + if ((op0 < op1) || (op0 > op2)) + SET_TRAP (TRAP_SUBSCR); + r = (op0 + op4) * op3; + WRITE_L (r); + CC_IIZZ_L (r); + break; + + /* Queue and interlocked queue */ + + case INSQUE: + cc = op_insque (RUN_PASS, opnd, acc); + break; + + case REMQUE: + cc = op_remque (RUN_PASS, opnd, acc); + break; + + case INSQHI: + cc = op_insqhi (RUN_PASS, opnd, acc); + break; + + case INSQTI: + cc = op_insqti (RUN_PASS, opnd, acc); + break; + + case REMQHI: + cc = op_remqhi (RUN_PASS, opnd, acc); + break; + + case REMQTI: + cc = op_remqti (RUN_PASS, opnd, acc); + break; + + /* String instructions */ + + case MOVC3: case MOVC5: + cc = op_movc (RUN_PASS, opnd, opc & 4, acc); + break; + + case CMPC3: case CMPC5: + cc = op_cmpc (RUN_PASS, opnd, opc & 4, acc); + break; + + case LOCC: case SKPC: + cc = op_locskp (RUN_PASS, opnd, opc & 1, acc); + break; + + case SCANC: case SPANC: + cc = op_scnspn (RUN_PASS, opnd, opc & 1, acc); + break; + + /* Floating point instructions */ + + case TSTF: case TSTD: + r = op_movfd (RUN_PASS, op0); + CC_IIZZ_FP (r); + break; + + case TSTG: + r = op_movg (RUN_PASS, op0); + CC_IIZZ_FP (r); + break; + + case MOVF: + r = op_movfd (RUN_PASS, op0); + WRITE_L (r); + CC_IIZP_FP (r); + break; + + case MOVD: + if ((r = op_movfd (RUN_PASS, op0)) == 0) + op1 = 0; + WRITE_Q (r, op1); + CC_IIZP_FP (r); + break; + + case MOVG: + if ((r = op_movg (RUN_PASS, op0)) == 0) + op1 = 0; + WRITE_Q (r, op1); + CC_IIZP_FP (r); + break; + + case MNEGF: + r = op_mnegfd (RUN_PASS, op0); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case MNEGD: + if ((r = op_mnegfd (RUN_PASS, op0)) == 0) + op1 = 0; + WRITE_Q (r, op1); + CC_IIZZ_FP (r); + break; + + case MNEGG: + if ((r = op_mnegg (RUN_PASS, op0)) == 0) + op1 = 0; + WRITE_Q (r, op1); + CC_IIZZ_FP (r); + break; + + case CMPF: + cc = op_cmpfd (RUN_PASS, op0, 0, op1, 0); + break; + + case CMPD: + cc = op_cmpfd (RUN_PASS, op0, op1, op2, op3); + break; + + case CMPG: + cc = op_cmpg (RUN_PASS, op0, op1, op2, op3); + break; + + case CVTBF: + r = op_cvtifdg (RUN_PASS, SXTB (op0), NULL, opc); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case CVTWF: + r = op_cvtifdg (RUN_PASS, SXTW (op0), NULL, opc); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case CVTLF: + r = op_cvtifdg (RUN_PASS, op0, NULL, opc); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case CVTBD: case CVTBG: + r = op_cvtifdg (RUN_PASS, SXTB (op0), &rh, opc); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case CVTWD: case CVTWG: + r = op_cvtifdg (RUN_PASS, SXTW (op0), &rh, opc); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case CVTLD: case CVTLG: + r = op_cvtifdg (RUN_PASS, op0, &rh, opc); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case CVTFB: case CVTDB: case CVTGB: + r = op_cvtfdgi (RUN_PASS, opnd, &flg, opc) & BMASK; + WRITE_B (r); + CC_IIZZ_B (r); + if (flg) { + V_INTOV; + } + break; + + case CVTFW: case CVTDW: case CVTGW: + r = op_cvtfdgi (RUN_PASS, opnd, &flg, opc) & WMASK; + WRITE_W (r); + CC_IIZZ_W (r); + if (flg) { + V_INTOV; + } + break; + + case CVTFL: case CVTDL: case CVTGL: + case CVTRFL: case CVTRDL: case CVTRGL: + r = op_cvtfdgi (RUN_PASS, opnd, &flg, opc) & LMASK; + WRITE_L (r); + CC_IIZZ_L (r); + if (flg) { + V_INTOV; + } + break; + + case CVTFD: + r = op_movfd (RUN_PASS, op0); + WRITE_Q (r, 0); + CC_IIZZ_FP (r); + break; + + case CVTDF: + r = op_cvtdf (RUN_PASS, opnd); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case CVTFG: + r = op_cvtfg (RUN_PASS, opnd, &rh); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case CVTGF: + r = op_cvtgf (RUN_PASS, opnd); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case ADDF2: case ADDF3: + r = op_addf (RUN_PASS, opnd, FALSE); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case ADDD2: case ADDD3: + r = op_addd (RUN_PASS, opnd, &rh, FALSE); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case ADDG2: case ADDG3: + r = op_addg (RUN_PASS, opnd, &rh, FALSE); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case SUBF2: case SUBF3: + r = op_addf (RUN_PASS, opnd, TRUE); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case SUBD2: case SUBD3: + r = op_addd (RUN_PASS, opnd, &rh, TRUE); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case SUBG2: case SUBG3: + r = op_addg (RUN_PASS, opnd, &rh, TRUE); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case MULF2: case MULF3: + r = op_mulf (RUN_PASS, opnd); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case MULD2: case MULD3: + r = op_muld (RUN_PASS, opnd, &rh); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case MULG2: case MULG3: + r = op_mulg (RUN_PASS, opnd, &rh); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case DIVF2: case DIVF3: + r = op_divf (RUN_PASS, opnd); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case DIVD2: case DIVD3: + r = op_divd (RUN_PASS, opnd, &rh); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case DIVG2: case DIVG3: + r = op_divg (RUN_PASS, opnd, &rh); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case ACBF: + r = op_addf (RUN_PASS, opnd + 1, FALSE); /* add + index */ + temp = op_cmpfd (RUN_PASS, r, 0, op0, 0); /* result : limit */ + WRITE_L (r); /* write result */ + CC_IIZP_FP (r); /* set cc's */ + if ((temp & CC_Z) || ((op1 & FPSIGN)? /* test br cond */ + !(temp & CC_N): (temp & CC_N))) + BRANCHW (brdisp); + break; + + case ACBD: + r = op_addd (RUN_PASS, opnd + 2, &rh, FALSE); + temp = op_cmpfd (RUN_PASS, r, rh, op0, op1); + WRITE_Q (r, rh); + CC_IIZP_FP (r); + if ((temp & CC_Z) || ((op2 & FPSIGN)? /* test br cond */ + !(temp & CC_N): (temp & CC_N))) + BRANCHW (brdisp); + break; + + case ACBG: + r = op_addg (RUN_PASS, opnd + 2, &rh, FALSE); + temp = op_cmpg (RUN_PASS, r, rh, op0, op1); + WRITE_Q (r, rh); + CC_IIZP_FP (r); + if ((temp & CC_Z) || ((op2 & FPSIGN)? /* test br cond */ + !(temp & CC_N): (temp & CC_N))) + BRANCHW (brdisp); + break; + + /* EMODF + + op0 = multiplier + op1 = extension + op2 = multiplicand + op3:op4 = integer destination (int.wl) + op5:op6 = floating destination (flt.wl) + */ + + case EMODF: + r = op_emodf (RUN_PASS, opnd, &temp, &flg); + if (op5 < 0) + Read (RUN_PASS, op6, L_LONG, WA); + if (op3 >= 0) + R[op3] = temp; + else Write (RUN_PASS, op4, temp, L_LONG, WA); + WRITE_L (r); + CC_IIZZ_FP (r); + if (flg) { + V_INTOV; + } + break; + + /* EMODD, EMODG + + op0:op1 = multiplier + op2 = extension + op3:op4 = multiplicand + op5:op6 = integer destination (int.wl) + op7:op8 = floating destination (flt.wq) + */ + + case EMODD: + r = op_emodd (RUN_PASS, opnd, &rh, &temp, &flg); + if (op7 < 0) { + Read (RUN_PASS, op8, L_BYTE, WA); + Read (RUN_PASS, (op8 + 7) & LMASK, L_BYTE, WA); + } + if (op5 >= 0) + R[op5] = temp; + else Write (RUN_PASS, op6, temp, L_LONG, WA); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + if (flg) { + V_INTOV; + } + break; + + case EMODG: + r = op_emodg (RUN_PASS, opnd, &rh, &temp, &flg); + if (op7 < 0) { + Read (RUN_PASS, op8, L_BYTE, WA); + Read (RUN_PASS, (op8 + 7) & LMASK, L_BYTE, WA); + } + if (op5 >= 0) + R[op5] = temp; + else Write (RUN_PASS, op6, temp, L_LONG, WA); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + if (flg) { + V_INTOV; + } + break; + + /* POLY */ + + case POLYF: + op_polyf (RUN_PASS, opnd, acc); + CC_IIZZ_FP (R[0]); + break; + + case POLYD: + op_polyd (RUN_PASS, opnd, acc); + CC_IIZZ_FP (R[0]); + break; + + case POLYG: + op_polyg (RUN_PASS, opnd, acc); + CC_IIZZ_FP (R[0]); + break; + + /* Operating system instructions */ + + case CHMK: case CHME: case CHMS: case CHMU: + cc = op_chm (RUN_PASS, opnd, cc, opc); /* CHMx */ + GET_CUR; /* update cur mode */ + SET_IRQL; /* update intreq */ + break; + + case REI: + cc = op_rei (RUN_PASS, acc); /* REI */ + GET_CUR; /* update cur mode */ + break; + + case LDPCTX: + op_ldpctx (RUN_PASS, acc); + break; + + case SVPCTX: + op_svpctx (RUN_PASS, acc); + break; + + case PROBER: case PROBEW: + cc = (cc & CC_C) | op_probe (RUN_PASS, opnd, opc & 1); + break; + + case MTPR: + cc = (cc & CC_C) | op_mtpr (RUN_PASS, opnd); + break; + + case MFPR: + r = op_mfpr (RUN_PASS, opnd); + WRITE_L (r); + CC_IIZP_L (r); + break; + + /* CIS or emulated instructions */ + + case CVTPL: + case MOVP: case CMPP3: case CMPP4: case CVTLP: + case CVTPS: case CVTSP: case CVTTP: case CVTPT: + case ADDP4: case ADDP6: case SUBP4: case SUBP6: + case MULP: case DIVP: case ASHP: case CRC: + case MOVTC: case MOVTUC: case MATCHC: case EDITPC: + cc = op_cis (RUN_PASS, opnd, cc, opc, acc); + break; + + /* Octaword or reserved instructions */ + + case PUSHAO: case MOVAO: case CLRO: case MOVO: + case TSTH: case MOVH: case MNEGH: case CMPH: + case CVTBH: case CVTWH: case CVTLH: + case CVTHB: case CVTHW: case CVTHL: case CVTRHL: + case CVTFH: case CVTDH: case CVTGH: + case CVTHF: case CVTHD: case CVTHG: + case ADDH2: case ADDH3: case SUBH2: case SUBH3: + case MULH2: case MULH3: case DIVH2: case DIVH3: + case ACBH: case POLYH: case EMODH: +#if defined(_DEBUG) && !defined(FULL_VAX) + /* When running under debugger this place can be hit with irrelevant "variable va uninitialized" + and "variable spec uninitialized" warnings */ + va = 0; + spec = 0; +#endif + cc = op_octa (RUN_PASS, opnd, cc, opc, acc, spec, va); + if (cc & LSIGN) { /* ACBH branch? */ + BRANCHW (brdisp); + cc = cc & CC_MASK; /* mask off flag */ + } + break; + + case 0xFF: + op_reserved_ff (RUN_PASS, acc); + break; + default: + RSVD_INST_FAULT; + break; + } /* end case op */ + } /* end for */ +} /* end try*/ +sim_catch (sim_exception_ABORT, exabort) +{ + t_stat r = handle_abort(RUN_PASS, exabort, cc, acc, opc); + if (r) return r; + + /* + * after break from (abortval < 0) in handle_abort, i.e. exceptions and interrupts, + * rather than stops, go back to main loop + */ + goto main_loop; + +} /* end catch */ +sim_end_try + +ABORT (STOP_UNKNOWN); + +#if defined(USE_C_TRY_CATCH) + /* relax compiler false warning */ + return 0; +#endif +} /* end sim_instr */ + +#if defined(__GNUC__) +# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ >= 40603) +# pragma GCC diagnostic pop +# endif +#endif + + +/* + * Note that ABORT can be thrown while handling previous abort, for example + * if stack is invalid while trying to push exception parameters, or if + * SCB is not readable or SCB vector is corrupt (lower two bits are not 0 or 1). + * We need to handle these nested ABORT's. + */ +static t_stat handle_abort(RUN_DECL, sim_exception_ABORT* exabort, volatile int32& cc, volatile int32& acc, volatile int32& opc) +{ + sim_try + { + int abortval = exabort->code; + if (cpu_unit->cpu_exception_ABORT == NULL && exabort->isAutoDelete()) + { + cpu_unit->cpu_exception_ABORT = exabort; + } + else + { + exabort->checkAutoDelete(); + } + + if (abortval > 0) /* sim stop? */ + { + PSL = PSL | cc; /* put PSL together */ + pcq_r->setqptr(RUN_PASS, pcq_p); /* update pc q ptr */ + return abortval; /* return to SCP */ + } + else if (abortval < 0) /* mm or rsrv or int */ + { + int32 i, delta; + if ((PSL & PSL_FPD) == 0) /* FPD? no recovery */ + { + for (i = 0; i < recqptr; i++) /* unwind inst */ + { + int32 rrn, rlnt; + rrn = RQ_GETRN (recq[i]); /* recover reg # */ + rlnt = DR_LNT (RQ_GETLNT (recq[i])); /* recovery lnt */ + if (recq[i] & RQ_DIR) + R[rrn] = R[rrn] - rlnt; + else + R[rrn] = R[rrn] + rlnt; + } + } + PSL = PSL & ~PSL_TP; /* clear */ + recqptr = 0; /* clear queue */ + delta = PC - fault_PC; /* save delta PC */ + SETPC (fault_PC); /* restore PC */ + switch (-abortval) /* case on abort code */ + { + case SCB_RESIN: /* rsrv inst fault */ + case SCB_RESAD: /* rsrv addr fault */ + case SCB_RESOP: /* rsrv opnd fault */ + if (in_ie) /* in exc? panic */ + ABORT (STOP_INIE); + cc = intexc (RUN_PASS, -abortval, cc, 0, IE_EXC); /* take exception */ + GET_CUR; /* PSL changed */ + break; + + case SCB_CMODE: /* comp mode fault */ + case SCB_ARITH: /* arithmetic fault */ + if (in_ie) /* in exc? panic */ + ABORT (STOP_INIE); + cc = intexc (RUN_PASS, -abortval, cc, 0, IE_EXC); /* take exception */ + GET_CUR; + in_ie = 1; + Write (RUN_PASS, SP - 4, fault_p1, L_LONG, WA); /* write arith param */ + SP = SP - 4; + in_ie = 0; + break; + + case SCB_ACV: /* ACV fault */ + case SCB_TNV: /* TNV fault */ + if (in_ie) /* in exception? */ + { + if (PSL & PSL_IS) /* on is? panic */ + ABORT (STOP_INIE); + cc = intexc (RUN_PASS, SCB_KSNV, cc, 0, IE_SVE); /* ksnv */ + GET_CUR; + } + else + { + cc = intexc (RUN_PASS, -abortval, cc, 0, IE_EXC); /* take exception */ + GET_CUR; + in_ie = 1; + Write (RUN_PASS, SP - 8, fault_p1, L_LONG, WA); /* write mm params */ + Write (RUN_PASS, SP - 4, fault_p2, L_LONG, WA); + SP = SP - 8; + in_ie = 0; + } + break; + + case SCB_MCHK: /* machine check */ + if (in_ie) /* in exc? panic */ + ABORT (STOP_INIE); + cc = machine_check (RUN_PASS, fault_p1, opc, cc, delta); /* system specific */ + in_ie = 0; + GET_CUR; /* PSL changed */ + break; + + case 1: /* interrupt */ + break; /* just proceed */ + + case (-ABORT_INVSYSOP): + return STOP_INVSYSOP; + + default: /* other */ + badabo = abortval; /* save code */ + ABORT (STOP_UNKABO); /* panic */ + } + } + + return 0; + } + sim_catch (sim_exception_ABORT, exabort2) + { + return handle_abort(RUN_PASS, exabort2, cc, acc, opc); + } + sim_end_try + +#if defined(USE_C_TRY_CATCH) + /* relax compiler false warning */ + return 0; +#endif +} + +void cpu_backstep_pc(RUN_DECL) +{ + SETPC(fault_PC); +} + + +/* Prefetch buffer routine + + Prefetch buffer state + + ibufl, ibufh = the prefetch buffer + ibcnt = number of bytes available (0, 4, 8) + ppc = physical PC + + The get_istr routines fetches the indicated number of bytes from + the prefetch buffer. Although it is complicated, it is faster + than calling Read on every byte of the instruction stream. + + If the prefetch buffer has enough bytes, the required bytes are + extracted from the prefetch buffer and returned. If it does not + have enough bytes, enough prefetch words are fetched until there + are. A longword is only prefetched if data is needed from it, + so any translation errors are real. +*/ + +int32 get_istr (RUN_DECL, int32 lnt, int32 acc) +{ + int32 bo = PC & 3; + int32 sc, val, t; + + while (bo + lnt > ibcnt) /* until enuf bytes */ + { + if (ppc < 0 || VA_GETOFF (ppc) == 0) /* PPC inv, xpg? */ + { + ppc = Test (RUN_PASS, (PC + ibcnt) & ~03, RD, &t); /* xlate PC */ + if (ppc < 0) + Read (RUN_PASS, (PC + ibcnt) & ~03, L_LONG, RA); + } + if (ibcnt == 0) /* fill low */ + ibufl = ReadLP (RUN_PASS, ppc); + else + ibufh = ReadLP (RUN_PASS, ppc); /* or high */ + ppc = ppc + 4; /* incr phys PC */ + ibcnt = ibcnt + 4; /* incr ibuf cnt */ + } + + PC = PC + lnt; /* incr PC */ + if (lnt == L_BYTE) /* byte? */ + val = (ibufl >> (bo << 3)) & BMASK; + else if (lnt == L_WORD) /* word? */ + { + if (bo == 3) + val = ((ibufl >> 24) & 0xFF) | ((ibufh & 0xFF) << 8); + else + val = (ibufl >> (bo << 3)) & WMASK; + } + else if (bo) /* unaligned lw? */ + { + sc = bo << 3; + val = (((ibufl >> sc) & align[bo]) | (((uint32) ibufh) << (32 - sc))); + } + else + val = ibufl; /* aligned lw */ + if (bo + lnt >= 4) /* retire ibufl? */ + { + ibufl = ibufh; + ibcnt = ibcnt - 4; + } + return val; +} + +/* + * Process opcode 0xFF. + * + * This routine detects VMS bugchecks (BUGW and BUGL pseudo-instructions) and performs + * the following essential actions: + * + * 1) If directed by the setting in bugcheck_ctrl, shut off instruction history recording, + * so the history leading to the bugcheck (or as close to it as possible) can be easily + * examined, rather than being flushed out of the buffer or lost in overwhelming amount + * of subsequent recording. + * + * 2) If the guest is VMS and VSMP had been loaded, SYNCW-relevant bugchecks will cause + * the dump of CPU and SYNCW state being produced by executing command "CPU INFO". + * Note that various items of other CPUs accessed by dump procedure may be in transient + * state and their latest updates not propagated yet to this VCPU yet via cache coherence + * mechanism. Therefore certain displayed items may have limited validity. + * + * Note that BUGW/BUGL instruction may be not recognized and produce none of the above actions + * if the instuction is located in paged code, crosses page boundary and the second page is not + * currently mapped. This cannot happen for (2), but can happen for (1) in case of bugchecks in + * paged kernel-mode or executive-mode code. + */ +static void op_reserved_ff (RUN_DECL, int32 acc) +{ + t_bool r1 = PSL_GETCUR(PSL) == KERN; + t_bool r2 = PSL_GETCUR(PSL) <= EXEC && (bugcheck_ctrl & 1) != 0 && hst_on; + + t_bool locked = FALSE; + + uint32 bugcode = 0; /* initialize to suppress false GCC warning */ + char wl = 0; /* ... */ + + const uint32 VMS_BUG_BADQHDR = 0x478; + const uint32 VMS_BUG_CPUSANITY = 0x788; + const uint32 VMS_BUG_CPUSPINWAIT = 0x7A0; + const uint32 VMS_SEVERITY_MASK = 0x7; + + const char* bugexpl = ""; + + if (r1 || r2) + { + cpu_database_lock->lock(); + locked = TRUE; + } + + if (r1 || r2) + { + /* + * is_bug_instruction may not work properly for paged code + */ + if (is_bug_instruction(RUN_PASS, acc, fault_PC, & wl, & bugcode)) + { + if (r1 && sim_vsmp_active && sim_vsmp_os == VAXMP_API_OS_VMS) + { + if (wl == 'W') + bugcode &= 0xFFFF; + if ((bugcode & ~VMS_SEVERITY_MASK) == VMS_BUG_BADQHDR) + bugexpl = " (BADQHDR)"; + if ((bugcode & ~VMS_SEVERITY_MASK) == VMS_BUG_CPUSPINWAIT) + bugexpl = " (CPUSPINWAIT)"; + /* VAX MP syncw is not meant to protect against CPUSANITY yet */ + if ((bugcode & ~VMS_SEVERITY_MASK) == VMS_BUG_CPUSANITY && FALSE) + bugexpl = " (CPUSANITY)"; + r1 = bugexpl[0] != '\0'; + } + else + { + r1 = FALSE; + } + } + else + { + r1 = r2 = FALSE; + } + } + + if (r1 || r2) + { + if (r2) + { + /* shut off history recording */ + hst_on = FALSE; + } + + const char* divider = "***************************************************************************"; + + smp_printf("\n\r\n%s\n\n\r", divider); + if (sim_log) + fprintf(sim_log, "\n%s\n\n", divider); + + if (wl == 'W') + { + smp_printf("%s detected OpenVMS bugcheck BUGW %04X%s on CPU%d\n\r", sim_name, bugcode, bugexpl, cpu_unit->cpu_id); + if (sim_log) + fprintf(sim_log, "%s detected OpenVMS bugcheck BUGW %04X%s on CPU%d\n", sim_name, bugcode, bugexpl, cpu_unit->cpu_id); + } + else + { + smp_printf("%s detected OpenVMS bugcheck BUGL %08X%s on CPU%d\n\r", sim_name, bugcode, bugexpl, cpu_unit->cpu_id); + if (sim_log) + fprintf(sim_log, "%s detected OpenVMS bugcheck BUGL %08X%s on CPU%d\n", sim_name, bugcode, bugexpl, cpu_unit->cpu_id); + } + + if (r2) + { + smp_printf("\n\rStopping instruction history recording.\n\r"); + if (sim_log) + fprintf(sim_log, "\nStopping instruction history recording.\n"); + } + + if (r1) + { + smp_printf("\n\rCPU and SYNCW state dump follows. Remember it was taken on the fly,\n\r"); + smp_printf("therefore certain items displayed for other CPUs may have limited validity.\n\n\r"); + if (sim_log) + { + fprintf(sim_log, "\nCPU and SYNCW state dump follow. Remember it was taken on the fly,\n"); + fprintf(sim_log, "therefore certain items displayed for other CPUs may have limited validity.\n\n"); + } + + /* perform "CPU INFO" command */ + cpu_cmd_info(smp_stdout, &cpu_dev, cpu_unit, 0, ""); + if (sim_log) + cpu_cmd_info(sim_log, &cpu_dev, cpu_unit, 0, ""); + + smp_printf("\n\rOpenVMS bugcheck sequence will now be executed.\n\r"); + if (sim_log) + fprintf(sim_log, "\nOpenVMS bugcheck sequence will now be executed.\n"); + } + + smp_printf("\n\r%s\n\n\r", divider); + if (sim_log) + fprintf(sim_log, "\n%s\n\n", divider); + } + + if (locked) + cpu_database_lock->unlock(); + + RSVD_INST_FAULT; +} + +/* + * Detect and decode BUGW/BUGL pseudo-instruction only if it is wholly resident in memory. + * If only partially resident, do not generate page faults and return FALSE; + */ +static t_bool is_bug_instruction(RUN_DECL, int32 acc, int32 va, char* wl, uint32* bug_code) +{ + int32 mstat; + + if (Test (RUN_PASS, va, RA, &mstat) < 0 || + Test (RUN_PASS, va + 3, RA, &mstat) < 0) + { + return FALSE; + } + + if (Read (RUN_PASS, va, L_BYTE, RA) != 0xFF) + return FALSE; + + switch (Read (RUN_PASS, va + 1, L_BYTE, RA)) + { + case 0xFD: + if (Test (RUN_PASS, va + 5, RA, &mstat) < 0) + return FALSE; + *wl = 'L'; + *bug_code = Read (RUN_PASS, va + 2, L_LONG, RA); + return TRUE; + + case 0xFE: + *wl = 'W'; + *bug_code = Read (RUN_PASS, va + 2, L_WORD, RA); + return TRUE; + + default: + return FALSE; + } +} + +/* Read octaword specifier */ + +int32 ReadOcta (RUN_DECL, int32 va, int32 *opnd, int32 j, int32 acc) +{ + opnd[j++] = Read (RUN_PASS, va, L_LONG, acc); + opnd[j++] = Read (RUN_PASS, va + 4, L_LONG, acc); + opnd[j++] = Read (RUN_PASS, va + 8, L_LONG, acc); + opnd[j++] = Read (RUN_PASS, va + 12, L_LONG, acc); + return j; +} + + +static void op_adawi(RUN_DECL, int32 *opnd, int32 acc, int32 spec, int32 rn, int32 va, volatile int32& cc) +{ + int32 r, temp; + int32 pa_op2 = 0; /* initialize to prevent false GCC warning */ + + if (use_native_interlocked) + { + /* + * Handle ADAWI via native host routine if available, + * provided that the destination operand is in VAX main memory space + * (not general-purpose register, and not IO or processor register space) + */ + if (op1 < 0) + { + if (op2 & 1) /* mem? chk align */ + RSVD_OPND_FAULT; + pa_op2 = TestMark (RUN_PASS, op2, WA, NULL); + if (ADDR_IS_MEM(pa_op2)) + { + if (PSL_GETIPL(PSL) >= syncw.ipl_resched) + syncw_enter_ilk(RUN_PASS); + cc = smp_native_adawi(M, pa_op2, (uint16) op0); + if (cc & CC_V) { INTOV; } + return; + } + } + /* + * Fall through if "sum" operand is not in VAX main memory space + */ + } + + sim_try_volatile InterlockedOpLock iop(RUN_PASS); + if (op1 >= 0) + { + temp = R[op1] & WMASK; /* reg? ADDW2 */ + } + else + { + if (op2 & 1) /* mem? chk align */ + RSVD_OPND_FAULT; + /* + * According to "VAX Architecture Reference Manual" and "VAX Architecture Standard", + * ADAWI is the only interlocked instruction allowed on IO space, with interlock + * supported in Unibus space and implementation-dependent status in non-Unibus IO space. + */ + if (use_native_interlocked) + iop.phys_lock(pa_op2); /* base longword will be interlocked by iop.phys_lock(), not target word */ + else + iop.virt_lock(op2, acc); /* base longword will be interlocked by iop.virt_lock(), not target word */ + temp = Read (RUN_PASS, op2, L_WORD, WA); /* ok, ADDW2 */ + } + r = (op0 + temp) & WMASK; + iop.wmb = TRUE; + if (PSL_GETIPL(PSL) >= syncw.ipl_resched) + syncw_enter_ilk(RUN_PASS); + WRITE_W (r); + CC_ADD_W (r, op0, temp); /* set cc's */ +} + + +/* Schedule idle before the next instruction */ +void cpu_idle (RUN_DECL) +{ + /* When SMP is active, sleeping is controlled by paravirtualization module via VAXMP API */ + if (! sim_vsmp_active) + sim_activate_abs (cpu_unit, 0); +} + +t_stat cpu_idle_svc (RUN_SVC_DECL, UNIT *uptr) +{ + if (cpu_unit->cpu_dostop) + return STOP_LOOP; + + // RUN_SVC_CHECK_CANCELLED(uptr); // not required for per-CPU devices + if (sim_idle_enab && !sim_vsmp_active) + return sim_idle(RUN_PASS, TMR_CLK, FALSE, UINT32_MAX); + else + return SCPE_OK; +} + +/* + * Reset current CPU, usually will be called by reset_all for each CPU, 0 to (sim_ncpus - 1). + * + * Can also be called on individual CPU basis by reset_cmd ("reset CPU2"), or when CPU is being + * created (init_cpu_unit_0, cpu_create_cpus). + * + * Can also be called for secondary CPU when it is about to be started and another CPU performs + * pre-staring initialization on it with cpu_start_secondary. + */ + +t_stat cpu_reset (DEVICE *dptr) +{ + RUN_SCOPE; + + cpu_unit->cpu_dostop = FALSE; + + cpu_unit->cpu_synclk_protect = FALSE; + cpu_unit->cpu_synclk_protect_os = 0; + cpu_unit->cpu_synclk_protect_dev = 0; + cpu_unit->cpu_synclk_pending = SynclkNotPending; + + cpu_unit->cpu_context.reset(cpu_unit); + cpu_unit->cpu_intreg.reset(); + + cpu_unit->cpu_con_rei_on = FALSE; + + mem_err = 0; + crd_err = 0; + PSL = PSL_IS | PSL_IPL1F; + SISR = 0; + ASTLVL = 4; + mapen = 0; + FLUSH_ISTR; + cpu_on_clear_mapen(RUN_PASS); + pcq_r = find_reg ("PCQ", NULL, dptr); + if (pcq_r) + pcq_r->setqptr(RUN_PASS, 0); + else + return SCPE_IERR; + + if (M == NULL) + M = (uint32*) calloc_aligned (((uint32) MEMSIZE) >> 2, sizeof (uint32), /*SMP_MAXCACHELINESIZE*/ 512); + if (M == NULL) + return SCPE_MEM; + + /* reset clock queue and release entries back to freelist */ + clock_queue_entry* cqe; + while ((cqe = cpu_unit->clock_queue) != NULL) + { + cpu_unit->clock_queue = cqe->next; + + cqe->next = cpu_unit->clock_queue_freelist; + cpu_unit->clock_queue_freelist = cqe; + } + cpu_unit->cpu_requeue_syswide_pending = FALSE; + + /* mark all SSC timers as inactive */ + cpu_unit->sysd_active_mask = 0; + + cpu_database_lock->lock(); + if (cpu_unit->is_running()) + cpu_unit->cpu_state = CPU_STATE_RUNNABLE; + cpu_running_set.clear(cpu_unit->cpu_id); + sim_mp_active_update(); + cpu_database_lock->unlock(); + + cpu_reevaluate_thread_priority(RUN_PASS); + + if (cpu_unit->is_primary_cpu()) + { + // do this just once for the reset cycle across all CPUs, and only from the primary CPU + hlt_pin = 0; + sim_brk_types = sim_brk_dflt = SWMASK ('E'); + use_native_interlocked = FALSE; + syncw_reset(); + return build_dib_tab (); + } + else + { + syncw_leave_all(RUN_PASS, SYNCW_OVERRIDE_ALL | SYNCW_ENABLE_CPU); + return SCPE_OK; + } +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +{ + RUN_SCOPE; + return cpu_ex_run (RUN_PASS, vptr, exta, uptr, sw); +} + +t_stat cpu_ex_run (RUN_DECL, t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +{ +int32 st; +uint32 addr = (uint32) exta; + +if (vptr == NULL) + return SCPE_ARG; +if (sw & SWMASK ('V')) { + int32 acc = cpu_get_vsw (RUN_PASS, sw); + addr = Test (RUN_PASS, addr, acc, &st); + } +else addr = addr & PAMASK; +if (ADDR_IS_MEM (addr) || ADDR_IS_CDG (addr) || + ADDR_IS_ROM (addr) || ADDR_IS_NVR (addr)) { + *vptr = (uint32) ReadB (RUN_PASS, addr); + return SCPE_OK; + } +return SCPE_NXM; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) +{ +RUN_SCOPE; +int32 st; +uint32 addr = (uint32) exta; + +if (sw & SWMASK ('V')) { + int32 acc = cpu_get_vsw (RUN_PASS, sw); + addr = Test (RUN_PASS, addr, acc, &st); + } +else addr = addr & PAMASK; +if (ADDR_IS_MEM (addr) || ADDR_IS_CDG (addr) || + ADDR_IS_NVR (addr)) { + WriteB (RUN_PASS, addr, (int32) val); + return SCPE_OK; + } +if (ADDR_IS_ROM (addr)) { + rom_wr_B (RUN_PASS, addr, (int32) val); + return SCPE_OK; + } +return SCPE_NXM; +} + +/* Memory allocation */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + RUN_SCOPE; + int32 mc = 0; + uint32 i, clim; + uint32 *nM = NULL; + + if (val <= 0 || val > MAXMEMSIZE_X) + return SCPE_ARG; + for (i = val; i < MEMSIZE; i = i + 4) + mc = mc | M[i >> 2]; + if (mc != 0 && !get_yn ("Really truncate memory [N]?", FALSE)) + return SCPE_OK; + nM = (uint32 *) calloc_aligned (val >> 2, sizeof (uint32), /*SMP_MAXCACHELINESIZE*/ 512); + if (nM == NULL) + return SCPE_MEM; + clim = (uint32) (((uint32) val) < MEMSIZE ? val : MEMSIZE); + for (i = 0; i < clim; i = i + 4) + nM[i >> 2] = M[i >> 2]; + free_aligned ((void*) M); + M = nM; + CPU_UNIT* sv_cpu_unit = cpu_unit; + /* + * replicate new size across all other CPUs and flush prefetch + */ + for (uint32 k = 0; k < sim_ncpus; k++) + { + CPU_UNIT* cpu_unit = cpu_units[k]; + cpu_unit->capac = val; + FLUSH_ISTR; + } + cpu_unit = sv_cpu_unit; + sim_ws_prefaulted = FALSE; + sim_ws_settings_changed = TRUE; + return SCPE_OK; +} + +/* Virtual address translation */ + +t_stat cpu_show_virt (SMP_FILE *of, UNIT *uptr, int32 val, void *desc) +{ +RUN_SCOPE; +t_stat r; +char *cptr = (char *) desc; +uint32 va, pa; +int32 st; +static const char *mm_str[] = { + "Access control violation", + "Length violation", + "Process PTE access control violation", + "Process PTE length violation", + "Translation not valid", + "Internal error", + "Process PTE translation not valid" + }; + +if (cptr) { + va = (uint32) get_uint (cptr, 16, 0xFFFFFFFF, &r); + if (r == SCPE_OK) { + int32 acc = cpu_get_vsw (RUN_PASS, sim_switches); + pa = Test (RUN_PASS, va, acc, &st); + if (st == PR_OK) + fprintf (of, "Virtual %-X = physical %-X\n", va, pa); + else fprintf (of, "Virtual %-X: %s\n", va, mm_str[st]); + return SCPE_OK; + } + } +fprintf (of, "Invalid argument\n"); +return SCPE_OK; +} + +/* Get access mode for examine, deposit, show virtual */ + +int32 cpu_get_vsw (RUN_DECL, int32 sw) +{ +int32 md; + +set_map_reg (RUN_PASS); /* update dyn reg */ +if (sw & SWMASK ('K')) + md = KERN; +else if (sw & SWMASK ('E')) + md = EXEC; +else if (sw & SWMASK ('S')) + md = SUPV; +else if (sw & SWMASK ('U')) + md = USER; +else md = PSL_GETCUR (PSL); +return ACC_MASK (md); +} + +/* Set history */ + +static const char* find_abbrev(const char* cmd, const char** cmd_options); + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + uint32 k, i, lnt = 0; /* init lnt to suppress false GCC warning */ + t_stat r; + char* ptok; + const char* xp; + t_bool sync = TRUE; + t_bool lnt_set = FALSE; + t_bool sync_set = FALSE; + +#if defined (__x86_64__) + smp_check_aligned(& hst_stamp); +#endif + +#if defined (__x86_32__) + smp_check_aligned(& hst_stamp_counter); + smp_check_aligned(& hst_stamp_epoch); +#endif + + if (cptr == NULL) + { + // reset history buffers + if (hst_lnt == 0) + return SCPE_OK; + + for (k = 0; k < sim_ncpus; k++) + { + CPU_UNIT* xcpu = cpu_units[k]; + if (xcpu->cpu_hst == NULL) + { + xcpu->cpu_hst = (InstHistory*) calloc (hst_lnt, sizeof (InstHistory)); + if (xcpu->cpu_hst == NULL) + return SCPE_MEM; + } + for (i = 0; i < hst_lnt; i++) + xcpu->cpu_hst[i].isRecorded = FALSE; + UINT64_SET_ZERO(xcpu->cpu_hst_stamp); + xcpu->cpu_hst_index = 0; + hst_reinit_stamp(); + hst_on = TRUE; + } + + return SCPE_OK; + } + + ptok = strtok (cptr,"/"); + while (ptok) + { + const char* cmd_options[] = { "sync", "unsync", NULL }; + if (xp = find_abbrev(ptok, cmd_options)) + { + if (sync_set) + return SCPE_ARG; + if (0 == strcmp(xp, "sync")) + sync = TRUE; + else if (0 == strcmp(xp, "unsync")) + sync = FALSE; + sync_set = TRUE; + } + else + { + if (lnt_set) + return SCPE_ARG; + lnt = (int32) get_uint (ptok, 10, HIST_MAX, &r); + if (r != SCPE_OK || lnt && lnt < HIST_MIN) + return SCPE_ARG; + lnt_set = TRUE; + } + ptok = strtok (NULL, "/"); + } + + if (! lnt_set) + return SCPE_ARG; + + cpu_free_history (); + + if (lnt) + { + for (k = 0; k < sim_ncpus; k++) + { + CPU_UNIT* xcpu = cpu_units[k]; + xcpu->cpu_hst = (InstHistory*) calloc (lnt, sizeof (InstHistory)); + if (xcpu->cpu_hst == NULL) + { + cpu_free_history (); + return SCPE_MEM; + } + } + + hst_lnt = lnt; + hst_sync = sync; + hst_reinit_stamp(); + hst_on = TRUE; + } + + return SCPE_OK; +} + +static const char* find_abbrev(const char* cmd, const char** cmd_options) +{ + if (! (cmd && *cmd)) + return NULL; + + int cmd_len = (int) strlen(cmd); + int candidate = -1; + for (int k = 0; cmd_options[k] != NULL; k++) + { + int opt_len = (int) strlen(cmd_options[k]); + if (cmd_len <= opt_len) + { + int len = imin(cmd_len, opt_len); +#if defined(_WIN32) + if (0 == strnicmp(cmd, cmd_options[k], len)) +#else + if (0 == strncasecmp(cmd, cmd_options[k], len)) +#endif + { + if (candidate >= 0) + return NULL; + candidate = k; + } + } + } + + if (candidate < 0) + return NULL; + + return cmd_options[candidate]; +} + +static void cpu_free_history () +{ + for (uint32 k = 0; k < sim_ncpus; k++) + { + CPU_UNIT* xcpu = cpu_units[k]; + if (xcpu->cpu_hst != NULL) + { + free (xcpu->cpu_hst); + xcpu->cpu_hst = NULL; + } + UINT64_SET_ZERO(xcpu->cpu_hst_stamp); + xcpu->cpu_hst_index = 0; + } + + hst_lnt = 0; + hst_on = FALSE; +} + +t_bool cpu_stop_history () +{ + if (hst_lnt) + { + cpu_free_history (); + return TRUE; + } + else + { + return FALSE; + } +} + +/* Show history */ + +extern const char *opcode[]; +extern t_value* sim_eval; +extern t_stat fprint_sym (SMP_FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); +typedef struct +{ + InstHistory* pInstHistory; + uint32 cpu_id; +} +HistListEntry; + +static int +#ifdef _WIN32 +__cdecl +#endif +qsort_hle_compare(const void* vp1, const void* vp2) +{ + HistListEntry* h1 = (HistListEntry*) vp1; + HistListEntry* h2 = (HistListEntry*) vp2; + if (UINT64_LT(h1->pInstHistory->stamp, h2->pInstHistory->stamp)) + return -1; + if (UINT64_EQ(h1->pInstHistory->stamp, h2->pInstHistory->stamp)) + return 0; + else + return 1; +} + +t_stat cpu_show_hist (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ + RUN_SCOPE; + int32 max_lnt = 0; /* init max_lnt to suppress false GCC warning */ + t_bool max_lnt_set = FALSE; + t_bool all_cpus = FALSE; + t_bool all_cpus_set = FALSE; + + if (hst_lnt == 0) /* enabled? */ + return SCPE_NOFNC; + + char* cptr = (char*) desc; + + if (cptr) + { + const char* xp; + const char* ptok = strtok (cptr,"/"); + while (ptok) + { + const char* cmd_options[] = { "sync", "unsync", NULL }; + if (xp = find_abbrev(ptok, cmd_options)) + { + if (all_cpus_set) + return SCPE_ARG; + if (0 == strcmp(xp, "sync")) + all_cpus = TRUE; + else if (0 == strcmp(xp, "unsync")) + all_cpus = FALSE; + all_cpus_set = TRUE; + } + else + { + if (max_lnt_set) + return SCPE_ARG; + t_stat r; + max_lnt = (int32) get_uint ((char*) ptok, 10, 0xFFFFFFFF, &r); + if (r != SCPE_OK) + return SCPE_ARG; + max_lnt_set = TRUE; + } + ptok = strtok (NULL, "/"); + } + } + + if (all_cpus && !hst_sync) + { + smp_printf ("Unable to display synchronized instruction history for all CPUs because SYNC option was not requested for recording\n"); + if (sim_log) + fprintf (sim_log, "Unable to display synchronized instruction history for all CPUs because SYNC option was not requested for recording\n"); + return SCPE_NOFNC; + } + + if (max_lnt_set) + { + max_lnt = imin(max_lnt, (int32) (all_cpus ? hst_lnt * sim_ncpus : hst_lnt)); + } + else + { + max_lnt = all_cpus ? hst_lnt * sim_ncpus : hst_lnt; + } + + HistListEntry* hlist = new HistListEntry[all_cpus ? hst_lnt * sim_ncpus : hst_lnt]; + if (hlist == NULL) + return SCPE_MEM; + int avl_lnt = 0; + + for (uint32 k = 0; k < sim_ncpus; k++) + { + CPU_UNIT* xcpu = cpu_units[k]; + if (xcpu->cpu_hst && (all_cpus || xcpu == cpu_unit)) + { + for (uint32 i = 0; i < hst_lnt; i++) + { + if (xcpu->cpu_hst[i].isRecorded) + { + hlist[avl_lnt].pInstHistory = & xcpu->cpu_hst[i]; + hlist[avl_lnt++].cpu_id = xcpu->cpu_id; + } + } + } + } + + fprintf (st, "CPU PC PSL IR\n\n"); + fprintf (st, "--- -------- --------- -------------------------\n\n"); + + if (avl_lnt == 0) + { + delete [] hlist; + return SCPE_OK; + } + + qsort(hlist, avl_lnt, sizeof(HistListEntry), qsort_hle_compare); + + int32 istart = 0; + if (avl_lnt > max_lnt) + istart = avl_lnt - max_lnt; + + for (int32 i = istart; i < avl_lnt ; i++) + { + InstHistory* h = hlist[i].pInstHistory; + + fprintf(st, " %02d %08X %08X| ", hlist[i].cpu_id, h->iPC, h->iPSL); /* CPU ID, PC, PSL */ + + int32 numspec = drom[h->opc][0] & DR_NSPMASK; /* #specifiers */ + + if (opcode[h->opc] == NULL) /* undefined? */ + { + fprintf (st, "%03X (undefined)", h->opc); + } + else if (h->iPSL & PSL_FPD) /* FPD set? */ + { + fprintf (st, "%s FPD set", opcode[h->opc]); + } + else /* normal */ + { + for (int32 i = 0; i < INST_SIZE; i++) + sim_eval[i] = h->inst[i]; + + if ((fprint_sym (st, h->iPC, sim_eval, cpu_unit, SWMASK ('M'))) > 0) + fprintf (st, "%03X (undefined)", h->opc); + + if ((numspec > 1) || + ((numspec == 1) && (drom[h->opc][1] < BB))) + { + if (cpu_show_opnd (st, h, 0)) /* operands; more? */ + { + if (cpu_show_opnd (st, h, 1)) /* 2nd line; more? */ + { + cpu_show_opnd (st, h, 2); /* octa, 3rd/4th */ + cpu_show_opnd (st, h, 3); + } + } + } + } + + /* end line */ + fputc ('\n', st); + } + + delete [] hlist; + + return SCPE_OK; +} + +t_bool cpu_show_opnd (SMP_FILE *st, InstHistory *h, int32 line) +{ + +int32 numspec, i, j, disp; +t_bool more; + +numspec = drom[h->opc][0] & DR_NSPMASK; /* #specifiers */ +fputs ("\n ", st); /* space */ +for (i = 1, j = 0, more = FALSE; i <= numspec; i++) { /* loop thru specs */ + disp = drom[h->opc][i]; /* specifier type */ + if (disp == RG) /* fix specials */ + disp = RQ; + else if (disp >= BB) + break; /* ignore branches */ + else switch (disp & (DR_LNMASK|DR_ACMASK)) { + + case RB: case RW: case RL: /* read */ + case AB: case AW: case AL: case AQ: case AO: /* address */ + case MB: case MW: case ML: /* modify */ + if (line == 0) + fprintf (st, " %08X", h->opnd[j]); + else fputs (" ", st); + j = j + 1; + break; + case RQ: case MQ: /* read, modify quad */ + if (line <= 1) + fprintf (st, " %08X", h->opnd[j + line]); + else fputs (" ", st); + if (line == 0) + more = TRUE; + j = j + 2; + break; + case RO: case MO: /* read, modify octa */ + fprintf (st, " %08X", h->opnd[j + line]); + more = TRUE; + j = j + 4; + break; + case WB: case WW: case WL: case WQ: case WO: /* write */ + if (line == 0) + fprintf (st, " %08X", h->opnd[j + 1]); + else fputs (" ", st); + j = j + 2; + break; + } /* end case */ + } /* end for */ +return more; +} + +struct os_idle { + char *name; + uint32 mask; + }; + +static struct os_idle os_tab[] = { + { "VMS", VAX_IDLE_VMS }, + { "NETBSD", VAX_IDLE_ULTOLD }, + { "ULTRIX", VAX_IDLE_ULT }, + { "ULTRIXOLD", VAX_IDLE_ULTOLD }, + { "OPENBSD", VAX_IDLE_QUAD }, + { "QUASIJARUS", VAX_IDLE_QUAD }, + { "32V", VAX_IDLE_QUAD }, + { "ALL", VAX_IDLE_VMS|VAX_IDLE_ULTOLD|VAX_IDLE_ULT|VAX_IDLE_QUAD }, + { NULL, 0 } + }; + +/* Set and show idle */ + +t_stat cpu_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + char* xarg = NULL; + + if (! (cptr && *cptr)) + return sim_set_idle (uptr, val, NULL, desc); + + const char* ptok = strtok (cptr,"/"); + while (ptok) + { + t_bool tok_sysname = FALSE; + for (uint32 i = 0; os_tab[i].name != NULL; i++) + { + if (strcmp (os_tab[i].name, ptok) == 0) + { + cpu_idle_type = i + 1; + cpu_idle_mask = os_tab[i].mask; + tok_sysname = TRUE; + break; + } + } + if (! tok_sysname) + { + if (xarg) + { + free(xarg); + return SCPE_ARG; + } + else + { + if ((xarg = dupstr(ptok)) == NULL) + return SCPE_MEM; + } + } + ptok = strtok (NULL, "/"); + } + + t_stat res = sim_set_idle (uptr, val, xarg, desc); + if (xarg) free (xarg); + return res; +} + +t_stat cpu_show_idle (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ + if (sim_idle_enab && cpu_idle_type != 0) + fprintf (st, "idle enabled=%s, stability wait = %ds", os_tab[cpu_idle_type - 1].name, sim_idle_stable); + else if (sim_idle_enab) + fprintf (st, "idle enabled, stability wait = %ds", sim_idle_stable); + else + fprintf (st, "idle disabled"); + return SCPE_OK; +} + +/* + * cpu_cmd_info is normally called on the console thread, + * however it can also be called on VCPU thread from op_reserved_ff + */ +t_stat cpu_cmd_info (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ + run_scope_context* rscx = run_scope_context::get_current(); + CPU_UNIT* sv_cpu_unit = rscx->cpu_unit; + CPU_UNIT* cpu_unit; + const char* modes = "KESU"; + const char* cprio; + int tprio; + t_bool none; + char c; + + /* + * Display basic VCPUs state + */ + fprintf(st, "CP MO IP LI THRD ACLK\n"); + fprintf(st, "U# PC PSL DE L# RQ PRIO AIPI\n"); + fprintf(st, "-- -------- -------- -- -- -- ------ ----\n"); + + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + rscx->cpu_unit = cpu_unit = cpu_units[cpu_ix]; + fprintf(st, "%02d ", cpu_unit->cpu_id); + if (cpu_running_set.is_set(cpu_ix) != (cpu_unit->cpu_state == CPU_STATE_RUNNING)) + { + fprintf(st, "**INCONSISTENT**\n"); + continue; + } + + if (cpu_unit->cpu_state != CPU_STATE_RUNNING) + { + fprintf(st, "(%s)\n", cpu_describe_state(cpu_unit)); + continue; + } + + fprintf(st, "%08X %08X ", PC, PSL); + c = modes[PSL_GETCUR(PSL)]; + if (PSL & PSL_IS) c = 'I'; + fprintf(st, "%c%c %2d %2d ", c, modes[PSL_GETPRV(PSL)], PSL_GETIPL(PSL), cpu_unit->cpu_context.highest_irql); + + switch (cpu_unit->cpu_thread_priority) + { + case SIMH_THREAD_PRIORITY_INVALID: + cprio = "INV"; + break; + case SIMH_THREAD_PRIORITY_CPU_RUN: + cprio = "RUN"; + break; + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS: + cprio = "OS "; + break; + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI: + cprio = "OSH"; + break; + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM: + cprio = "VM "; + break; + case SIMH_THREAD_PRIORITY_CPU_CALIBRATION: + cprio = "CAL"; + break; + default: + cprio = "???"; + break; + } + + fprintf(st, "%s", cprio); + tprio = smp_get_thread_os_priority(cpu_unit->cpu_thread); + if (tprio < 0) + fprintf(st, " ??"); + else + fprintf(st, "%3d", tprio); + fprintf(st, " %c%c", cpu_unit->cpu_active_clk_interrupt ? 'C' : ' ', + cpu_unit->cpu_active_ipi_interrupt ? 'I' : ' '); + fprintf(st, "\n"); + } + + fprintf(st, "\n"); + + /* + * Display pending interrupts + */ + fprintf(st, "CP External pending interrupts \n"); + fprintf(st, "U# Local and buffered interrupts \n"); + fprintf(st, "-- --------------------------------------\n"); + + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + rscx->cpu_unit = cpu_unit = cpu_units[cpu_ix]; + if (cpu_unit->cpu_state != CPU_STATE_RUNNING) + continue; + + /* Dispay external interrupts */ + fprintf(st, "%02d XT: ", cpu_unit->cpu_id); + none = TRUE; + cpu_unit->cpu_intreg.show_external(st, none); + if (none) fprintf(st, "(none)"); + fprintf(st, "\n"); + + /* Dispay local and buffered interrupts */ + fprintf(st, " LC: "); + none = TRUE; + if (hlt_pin) + { + fprintf(st, "%s%s", none ? "" : " ", "HLTPIN"); + none = FALSE; + } + if (mem_err) + { + fprintf(st, "%s%s", none ? "" : " ", "MEMERR"); + none = FALSE; + } + if (crd_err) + { + fprintf(st, "%s%s", none ? "" : " ", "CRDERR"); + none = FALSE; + } + + cpu_unit->cpu_intreg.show_local(st, none); + + if (SISR) + { + for (int32 i = IPL_SMAX; i >= 1; i--) + { + if (SISR & (1 << i)) + { + fprintf(st, "%s%d", none ? "" : " ", i); + none = FALSE; + } + } + } + + if (none) fprintf(st, "(none)"); + fprintf(st, "\n"); + } + + rscx->cpu_unit = sv_cpu_unit; + + fprintf(st, "\n"); + syncw_display(st); + + return SCPE_OK; +} + +void InterruptRegister::show_external(SMP_FILE* st, t_bool& none) +{ + if (smp_var(changed)) + show_aux_2(st, "NEW", none); + + show_aux(st, (uint32*) irqs, none); +} + +void InterruptRegister::show_local(SMP_FILE* st, t_bool& none) +{ + show_aux(st, local_irqs, none); +} + +void InterruptRegister::show_aux(SMP_FILE* st, uint32* rqs, t_bool& none) +{ + if (rqs[IPL_IPIRMB] & (1 << INT_V_IPIRMB)) + show_aux_2(st, "IPIRMB", none); + if (rqs[IPL_SECEXIT] & (1 << INT_V_SECEXIT)) + show_aux_2(st, "SECEXIT", none); + if (rqs[IPL_SYNCWSYS] & (1 << INT_V_SYNCWSYS)) + show_aux_2(st, "SYNCWSYS", none); + if (rqs[IPL_SYNCLK] & (1 << INT_V_SYNCLK)) + show_aux_2(st, "SYNCLK", none); + if (rqs[IPL_ASYNC_IO] & (1 << INT_V_ASYNC_IO)) + show_aux_2(st, "ASYNC_IO", none); + if (rqs[IPL_STOP] & (1 << INT_V_STOP)) + show_aux_2(st, "STOP", none); + if (rqs[IPL_CLK] & (1 << INT_V_CLK)) + show_aux_2(st, "CLK", none); + if (rqs[IPL_IPINTR] & (1 << INT_V_IPINTR)) + show_aux_2(st, "IPINTR", none); + if (rqs[IPL_RQ] & (1 << INT_V_RQ)) + show_aux_2(st, "RQ", none); + if (rqs[IPL_RL] & (1 << INT_V_RL)) + show_aux_2(st, "RL", none); + if (rqs[IPL_DZRX] & (1 << INT_V_DZRX)) + show_aux_2(st, "DZRX", none); + if (rqs[IPL_DZTX] & (1 << INT_V_DZTX)) + show_aux_2(st, "DZTX", none); + if (rqs[IPL_TS] & (1 << INT_V_TS)) + show_aux_2(st, "TS", none); + if (rqs[IPL_TQ] & (1 << INT_V_TQ)) + show_aux_2(st, "TQ", none); + if (rqs[IPL_XQ] & (1 << INT_V_XQ)) + show_aux_2(st, "XQ", none); + if (rqs[IPL_RY] & (1 << INT_V_RY)) + show_aux_2(st, "RY", none); + if (rqs[IPL_TTI] & (1 << INT_V_TTI)) + show_aux_2(st, "TTI", none); + if (rqs[IPL_TTO] & (1 << INT_V_TTO)) + show_aux_2(st, "TTO", none); + if (rqs[IPL_PTR] & (1 << INT_V_PTR)) + show_aux_2(st, "PTR", none); + if (rqs[IPL_PTP] & (1 << INT_V_PTP)) + show_aux_2(st, "PTP", none); + if (rqs[IPL_LPT] & (1 << INT_V_LPT)) + show_aux_2(st, "LPT", none); + if (rqs[IPL_CSI] & (1 << INT_V_CSI)) + show_aux_2(st, "CSI", none); + if (rqs[IPL_CSO] & (1 << INT_V_CSO)) + show_aux_2(st, "CSO", none); + if (rqs[IPL_TMR0] & (1 << INT_V_TMR0)) + show_aux_2(st, "TMR0", none); + if (rqs[IPL_TMR1] & (1 << INT_V_TMR1)) + show_aux_2(st, "TMR1", none); + if (rqs[IPL_VHRX] & (1 << INT_V_VHRX)) + show_aux_2(st, "VHRX", none); + if (rqs[IPL_VHTX] & (1 << INT_V_VHTX)) + show_aux_2(st, "VHTX", none); + if (rqs[IPL_QDSS] & (1 << INT_V_QDSS)) + show_aux_2(st, "QDSS", none); + if (rqs[IPL_CR] & (1 << INT_V_CR)) + show_aux_2(st, "CR", none); +} + +void InterruptRegister::show_aux_2(SMP_FILE* st, const char* intr, t_bool& none) +{ + fprintf(st, "%s%s", none ? "" : " ", intr); + none = FALSE; +} + +/* + * API for communication between guest and VAX MP VM + */ + +void op_mtpr_simh (RUN_DECL, int32 va) +{ + int32 acc; + GET_CUR; /* init acc */ + + uint32 args[40]; + int32 op, guest_api_version, k; + int32 argc = 0; /* initialize to suppress false GCC warning */ + uint32 ix; + + /* + * if in kernel mode, require header to be non-paged, this way we avoid paging exception + * when checking the signature in kernel mode and can always pretend MT_SIMH does not exist + * if the signature does not check out, i.e. on random reference can respond with reserved operand fault + * rather than page fault + */ + if (0 == (PSL & PSL_CUR)) + { + int32 mstat; + if (Test (RUN_PASS, va, RA, &mstat) < 0 || + Test (RUN_PASS, va + 11, RA, &mstat) < 0) + { + RSVD_OPND_FAULT; + } + } + + /* + * check request signature + */ + if (Read (RUN_PASS, va, L_LONG, RA) != VAXMP_API_SIGNATURE) + { + if (0 == (PSL & PSL_CUR)) + RSVD_OPND_FAULT; + else + RSVD_INST_FAULT; + } + + op = Read (RUN_PASS, va + 4, L_LONG, RA); + guest_api_version = Read (RUN_PASS, va + 8, L_LONG, RA); + + /* in non-kernel mode accept only QUERY */ + if (0 != (PSL & PSL_CUR) && op != VAXMP_API_OP_QUERY) + RSVD_INST_FAULT; + + switch (op) + { + case VAXMP_API_OP_QUERY: argc = 15; break; + case VAXMP_API_OP_MEMBAR: argc = 3; break; + case VAXMP_API_OP_INIT_SMP: argc = 17; break; + case VAXMP_API_OP_START_CPU: argc = 34; break; + case VAXMP_API_OP_IPINTR: argc = 5; break; + case VAXMP_API_OP_IDLE: argc = 5; break; + case VAXMP_API_OP_IDLE_PULSE: argc = 4; break; + case VAXMP_API_OP_SET_IDLE: argc = 5; break; + case VAXMP_API_OP_GETTIME_VMS: argc = 6; break; + default: RSVD_OPND_FAULT; + } + + /* pre-read argument block, make sure it is in memory and is writable */ + for (k = 0; k < argc; k++) + args[k] = Read (RUN_PASS, va + 4 * k, L_LONG, WA); + + /***************************************************************************** + * * + * VAXMP_API_OP_QUERY -- Query simulator configuration * + * * + * Argument block: * + * * + * 0 R signature (VAXMP_API_SIGNATURE) * + * 1 R request code (VAXMP_API_OP_QUERY) * + * 2 R client API version * + * * + * 3 W response status (1 = OK) * + * 4 W SIMH API version * + * 5 W number of configured VCPUs * + * 6 W mask of configured VCPU IDs * + * 7 W VM brand ('VXMP' = SIMH VAX MP) * + * 8 W VM version (such as 3.8.1.0) * + * 9 W SMP code revision level * + * 10 W SMP options * + * 11 W SMT (Hyper-Threading) slow-down factor multipler * + * 12 W SMT (Hyper-Threading) slow-down factor divider * + * 13 W Host turbo factor * + * 14 W Active synchronization window mask * + * * + *****************************************************************************/ + + if (op == VAXMP_API_OP_QUERY) + { + args[3] = 1; /* response status back to guest */ + args[4] = 1; /* SIMH API version */ + args[5] = sim_ncpus; /* number of configured CPUs */ + args[6] = 0; /* mask of configured CPU IDs */ + for (ix = 0; ix < sim_ncpus; ix++) + args[6] |= (1 << ix); + args[7] = VAXMP_VM_ID; + args[8] = (SIM_MAJOR << 24) | (SIM_MINOR << 16) | (SIM_PATCH << 8) | SIM_DELTA; + args[9] = VSMP_REVISION; + + args[10] = 0; + args[10] |= VAXMP_SMP_OPTION_PORTABLE_INTERLOCK; +#if SMP_NATIVE_INTERLOCKED + args[10] |= VAXMP_SMP_OPTION_NATIVE_INTERLOCK; +#endif + + if (smp_smt_factor_set) + { + args[11] = (uint32) (smp_smt_factor * 100); + args[12] = 100; + } + else if (smp_nsmt_per_core == 2) + { + args[11] = 18; + args[12] = 10; + } + else + { + args[11] = 1; + args[12] = 1; + } + + args[13] = sim_host_turbo; + } + + /***************************************************************************** + * * + * VAXMP_API_OP_MEMBAR -- Execute memory barrier * + * * + * Argument block: * + * * + * 0 R signature (VAXMP_API_SIGNATURE) * + * 1 R request code (VAXMP_API_OP_MEMBAR) * + * 2 R memory barrier to execute: * + * * + * 0 or 3: full memory barrier * + * 1: WMB * + * 2: RMB * + * * + *****************************************************************************/ + + else if (op == VAXMP_API_OP_MEMBAR) + { + /* + * We rely here on design for device handlers (XQ, RQ and TQ) that access shared areas, + * such as UQSSP COMM area and XQ BDL, that performs such access only within the context + * of VCPU threads and not IOP threads. Otherwise MB would have been always required + * here, regardless of whether sim_mp_active is TRUE or not. + */ + if (weak_read(sim_mp_active)) + { + switch (args[2] & 3) + { + case 0: + case 3: + default: + smp_mb(); break; + case 1: + smp_wmb(); break; + case 2: + smp_rmb(); break; + } + } + } + + /***************************************************************************** + * * + * VAXMP_API_OP_INIT_SMP -- Prepare to activate multiprocessing * + * * + * Argument block: * + * * + * 0 R signature (VAXMP_API_SIGNATURE) * + * 1 R request code (VAXMP_API_OP_INIT_SMP) * + * 2 R client API version * + * * + * 3 W response status (1 = OK) * + * 4 R threshold IPL for system critical section * + * 5 R virtual address of CPUs idle mask (VMS SCH$GL_IDLE_CPUS) * + * 6 R guest OS id (VMS = 1) * + * 7 R guest OS version (or 0) * + * 8 R "must" options * + * 9 R "want" options * + * 10 W granted options * + * 11 R syncw on flags (SYNCW_SYS, SYNCW_ILK) * + * 12 R syncw winsize sys * + * 13 R syncw winsize ilk * + * 14 R syncw ipl syslock * + * 15 R syncw ipl resched * + * 16 R idle sleep is enabled (1 or 0) * + * * + *****************************************************************************/ + + else if (op == VAXMP_API_OP_INIT_SMP) + { + uint32 must_options = args[8]; + uint32 want_options = args[9]; + uint32 syncw_on = args[11]; + + args[10] = 0; + + uint32 avopts = VAXMP_SMP_OPTION_PORTABLE_INTERLOCK; +#if SMP_NATIVE_INTERLOCKED + avopts |= VAXMP_SMP_OPTION_NATIVE_INTERLOCK; +#endif + + if (sim_vsmp_active || + 0 != (must_options & ~avopts) || + 0 != (syncw_on & ~(SYNCW_SYS | SYNCW_ILK))) + { + args[3] = 0; /* response status back to guest */ + } + else + { + sim_vsmp_active = TRUE; + + sys_critical_section_ipl = args[4]; + sys_idle_cpu_mask_va = args[5]; + sim_vsmp_idle_sleep = (args[16] != 0); + sim_vsmp_os = args[6]; + + /* process interlock mode options */ +#if SMP_NATIVE_INTERLOCKED + if ((must_options | want_options) & VAXMP_SMP_OPTION_NATIVE_INTERLOCK) + { + use_native_interlocked = TRUE; + args[10] |= VAXMP_SMP_OPTION_NATIVE_INTERLOCK; + } +#endif + + if (! use_native_interlocked) + args[10] |= VAXMP_SMP_OPTION_PORTABLE_INTERLOCK; + + /* process syncw */ + syncw.on = syncw_on; + syncw.vsmp_winsize_sys = args[12]; + syncw.vsmp_winsize_ilk = args[13]; + syncw.ipl_syslock = (int32) args[14]; + syncw.ipl_resched = (int32) args[15]; + syncw_start(RUN_PASS); + syncw_enable_cpu(RUN_PASS, SYNCW_NOLOCK); + syncw_reeval_sys(RUN_PASS); + + /* disable built-in idle sleep detection mechanism, idle sleep is now under the control of guest OS */ + sim_cancel (cpu_unit); + + if (sim_clock_thread_created) + smp_set_thread_priority(sim_clock_thread, SIMH_THREAD_PRIORITY_CLOCK); + + args[3] = 1; /* response status back to guest */ + } + } + + /******************************************************************************* + * * + * VAXMP_API_OP_START_CPU -- Start another CPU * + * * + * Argument block: * + * * + * 0 R signature (VAXMP_API_SIGNATURE) * + * 1 R request code (VAXMP_API_OP_START_CPU) * + * 2 R client API version * + * * + * 3 W response status (1 = OK, 2 = nx CPU ID, 3 = CPU not STANDBY, * + * 4 = invalid HWPCB/SCBB/SBR/SLR/MAPEN values) * + * 4 R CPU ID of the processor to start * + * * + * 5 - 28 R 24 words with register values in VAX hardware PCB format * + * 29 R SCBB * + * 30 R MAPEN * + * 31 R SBR * + * 32 R SLR * + * 33 R ISP * + * * + *******************************************************************************/ + + else if (op == VAXMP_API_OP_START_CPU) + { + cpu_database_lock->lock(); + + /* + * If CPUs stop is pending, do not attempt to start the CPU (since console thread may have + * already calculated number of running CPUs expected to join stop barrier at stop and re-ized + * the barrier object accordingly). Instead throw SCPE_STOP now. When processors are resumed, + * current VCPU will re-execute MFPR instruction, and START_CPU request will be retried. + */ + if (stop_cpus) + { + cpu_database_lock->unlock(); + ABORT (SCPE_STOP); + } + + uint32 cpu_id = args[4]; + uint32* pcb = args + 5; + uint32 scbb = args[29]; + uint32 x_mapen = args[30]; + uint32 sbr = args[31]; + uint32 slr = args[32]; + uint32 isp = args[33]; + + if (cpu_id >= sim_ncpus) + { + /* invalid requested CPU ID */ + args[3] = 2; + } + else if (cpu_running_set.is_set(cpu_id) || cpu_units[cpu_id]->cpu_state != CPU_STATE_STANDBY) + { + /* requested processor is not in STANDBY state */ + args[3] = 3; + } + else if (cpu_start_secondary(RUN_PASS, cpu_units[cpu_id], pcb, scbb, x_mapen, sbr, slr, isp)) + { + /* successfully started processor */ + args[3] = 1; + } + else + { + /* invalid registers values passed as request arguments */ + args[3] = 4; + } + + cpu_database_lock->unlock(); + } + + /***************************************************************************** + * * + * VAXMP_API_OP_IDLE -- Enter idle sleep * + * * + * Argument block: * + * * + * 0 R signature (VAXMP_API_SIGNATURE) * + * 1 R request code (VAXMP_API_OP_IDLE) * + * 2 R client API version * + * * + * 3 W response status (1 = OK) * + * 4 R max tick count to sleep (0 = till next clock tick, * + * 1 = till tick after next, 2 = till second tick after next * + * and so on) * + * * + *****************************************************************************/ + + else if (op == VAXMP_API_OP_IDLE) + { + uint32 maxticks = args[4]; + args[3] = 1; /* response status back to guest */ + + syncw_leave_ilk(RUN_PASS); + + /* syncw_leave_sys is not needed for VMS since idle loop is executing at IPL RESCHED < IPL QUEUEAST */ + // syncw_leave_sys(RUN_PASS); + + /* + * do not process SCPE_STOP here, let it rather be caught at next instruction, + * so that if CPUs are paused to console, exiting console action does not cause + * the CPU to re-enter the sleep immediatelly + */ + (void) sim_idle(RUN_PASS, TMR_CLK, FALSE, maxticks); + } + + /***************************************************************************** + * * + * VAXMP_API_OP_IDLE_PULSE -- Signal idle loop, but do not enter sleep * + * * + * Argument block: * + * * + * 0 R signature (VAXMP_API_SIGNATURE) * + * 1 R request code (VAXMP_API_OP_IDLE_PULSE) * + * 2 R client API version * + * * + * 3 W response status (1 = OK) * + * * + *****************************************************************************/ + + else if (op == VAXMP_API_OP_IDLE_PULSE) + { + uint32 maxticks = args[4]; + args[3] = 1; /* response status back to guest */ + + syncw_leave_ilk(RUN_PASS); + + /* syncw_leave_sys is not needed for VMS since idle loop is executing at IPL RESCHED < IPL QUEUEAST */ + // syncw_leave_sys(RUN_PASS); + } + + /***************************************************************************** + * * + * VAXMP_API_OP_SET_IDLE -- Signal whether guest OS idle loop will be using * + * idle sleep (by issuing VAXMP_API_OP_IDLE calls) * + * * + * Argument block: * + * * + * 0 R signature (VAXMP_API_SIGNATURE) * + * 1 R request code (VAXMP_API_OP_SET_IDLE) * + * 2 R client API version * + * * + * 3 R idle sleep is enabled (1 or 0) * + * 4 W response status (1 = OK) * + * * + *****************************************************************************/ + + else if (op == VAXMP_API_OP_SET_IDLE) + { + sim_vsmp_idle_sleep = (args[3] != 0); + args[4] = 1; /* response status back to guest */ + } + + /***************************************************************************** + * * + * VAXMP_API_OP_IPINTR -- Emit interprocessor interrupt * + * * + * Argument block: * + * * + * 0 R signature (VAXMP_API_SIGNATURE) * + * 1 R request code (VAXMP_API_OP_IPINTR) * + * 2 R client API version * + * * + * 3 W response status (1 = OK) * + * 4 R mask of VCPU IDs to be interrupted * + * * + *****************************************************************************/ + + else if (op == VAXMP_API_OP_IPINTR) + { + uint32 intmask = args[4]; /* ID-mask of CPUs to be interrupted */ + smp_wmb(); /* push out memory changes to targets */ + + /* interrupt all requested targets that are running */ + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + if (intmask & (1 << cpu_ix)) + { + interrupt_ipi(RUN_PASS, cpu_units[cpu_ix]); + } + } + + args[3] = 1; /* response status back to guest */ + } + + /***************************************************************************** + * * + * VAXMP_API_OP_GETTIME_VMS -- Get host system time in VMS format * + * * + * VMS time format is the number of 100-nanosecond intervals * + * since 00:00 o’clock, November 17, 1858 * + * * + * Argument block: * + * * + * 0 R signature (VAXMP_API_SIGNATURE) * + * 1 R request code (VAXMP_API_OP_GETTIME_VMS) * + * 2 R client API version * + * * + * 3 W response status (1 = OK) * + * 4 W low longword of time * + * 5 W high longword of time * + * * + *****************************************************************************/ + + else if (op == VAXMP_API_OP_GETTIME_VMS) + { + sim_os_gettime_vms(args + 4); + args[3] = 1; /* response status back to guest */ + } + + /***************************************************************************** + * Write back argument block, except for the header of first 3 longwords * + * (skip: signature, op-code and guest api version) * + *****************************************************************************/ + + for (k = 3; k < argc; k++) + Write (RUN_PASS, va + 4 * k, args[k], L_LONG, WA); +} + +/* + * Start "xcpu" initializing its context with data from passed "pcb", scbb, x_mapen and sbr/slr arguments. + * Called while holding cpu_database_lock. + */ +static t_bool cpu_start_secondary(RUN_DECL, CPU_UNIT* xcpu, uint32* pcb, uint32 scbb, uint32 x_mapen, uint32 sbr, uint32 slr, uint32 isp) +{ + t_bool valid = TRUE; + int32 opnd[2]; + uint32 t; + + /* + * Temporarily switch to the target VCPU's context. + */ + CPU_UNIT* local_cpu = cpu_unit; + RUN_SCOPE_RSCX_ONLY; + cpu_unit = rscx->cpu_unit = xcpu; + + /* + * Reset target VCPU state. + * + * Note that since we are holding cpu_database_lock, "vm_critical_locks" count in RSCX is non-zero. + * This prevents the code in the invoked "reset" routines from altering current thread's priority. + * Same goes for setting "mapen" below. + * + * Note: we do not reinitialize clock event queue. Therefore if there were any leftover events + * for system-wide devices queued during the previous execution of this VCPU and that had not been + * transferred to the primary VCPU yet (i.e. xcpu->cpu_requeue_syswide_pending == TRUE), these evtns + * would not be lost. Instead of being transferred to the primary VCPU's queue, they will resume + * in the current VCPU's queue again. However we have to reset per-CPU devices. + * + * While resetting them, disable use of synchronization window for target CPU while it is being + * initialized. This prevents clk_dev.reset and other calls from entering target VCPU into SYNCW_SYS + * before it is fully set up and marked runnable. + */ + syncw_leave_all(RUN_PASS, SYNCW_OVERRIDE_ALL | SYNCW_DISABLE_CPU); + cpu_dev.reset(& cpu_dev); + syncw_leave_all(RUN_PASS, SYNCW_OVERRIDE_ALL | SYNCW_DISABLE_CPU); + tlb_dev.reset(& tlb_dev); + sysd_dev.reset(& sysd_dev); + + /* clone initial RTC clock calibration from local VCPU data, must do it before CLK reset */ + sim_rtcn_init_secondary_cpu(cpu_unit, local_cpu, TMR_CLK, 0, TRUE); + clk_dev.reset(& clk_dev); + + tti_dev.reset(& tti_dev); + tto_dev.reset(& tto_dev); + + /* + * Set up target VCPU's state from passed HWPCB and other passed register values. + */ + sim_try + { + /* validate new PSL */ + uint32 newpsl = pcb[19]; + + if ((newpsl & PSL_MBZ) || + (newpsl & PSL_CM) || + PSL_GETCUR(newpsl) != KERN && (newpsl & (PSL_IS|PSL_IPL)) || + (newpsl & PSL_IS) && (newpsl & PSL_IPL) == 0) + { + RSVD_OPND_FAULT; + } + + /* load basic privileged processor registers, in this order */ +#define mtpr(val, prn) opnd[0] = (val); opnd[1] = (prn); op_mtpr(RUN_PASS, opnd); + mtpr(scbb, MT_SCBB); + mtpr(sbr, MT_SBR); + mtpr(slr, MT_SLR); + mtpr(x_mapen, MT_MAPEN); + mtpr(1, MT_TBIA); +#undef mtpr + + IS = isp; + + KSP = pcb[0]; + ESP = pcb[1]; + SSP = pcb[2]; + USP = pcb[3]; + R[0] = pcb[4]; + R[1] = pcb[5]; + R[2] = pcb[6]; + R[3] = pcb[7]; + R[4] = pcb[8]; + R[5] = pcb[9]; + R[6] = pcb[10]; + R[7] = pcb[11]; + R[8] = pcb[12]; + R[9] = pcb[13]; + R[10] = pcb[14]; + R[11] = pcb[15]; + R[12] = pcb[16]; + R[13] = pcb[17]; + PC = pcb[18]; + PSL = newpsl; + + /* validate and set P0BR */ + t = pcb[20]; + ML_PXBR_TEST(t); + P0BR = t & BR_MASK; + + /* P0LR */ + t = pcb[21]; + LP_MBZ84_TEST(t); + ML_LR_TEST(t & LR_MASK); + P0LR = t & LR_MASK; + + /* ASTLVL */ + t = (t >> 24) & AST_MASK; + LP_AST_TEST(t); + ASTLVL = t; + + /* P1BR */ + t = pcb[22]; + ML_PXBR_TEST(t + 0x800000); + P1BR = t & BR_MASK; + + /* P1LR */ + t = pcb[23]; + LP_MBZ92_TEST(t); + ML_LR_TEST(t & LR_MASK); + P1LR = t & LR_MASK; + + /* PME */ + pme = (t >> 31) & 1; + + /* clear TLB */ + zap_tb(RUN_PASS, 1); + + /* reload microcode registers */ + set_map_reg(RUN_PASS); + + /* clone TODR from the primary VCPU */ + todr_reg = primary_todr_reg; + todr_blow = primary_todr_blow; + + /* set up secondary CPU's model-specific registers */ + cpu_setup_secondary_model_specific(RUN_PASS, local_cpu); + } + sim_catch (sim_exception_ABORT, exabort) + { + if (cpu_unit->cpu_exception_ABORT == NULL && exabort->isAutoDelete()) + { + cpu_unit->cpu_exception_ABORT = exabort; + } + else + { + exabort->checkAutoDelete(); + } + + valid = FALSE; + } + sim_end_try + + /* Switch the context back to the local VCPU */ + cpu_unit = rscx->cpu_unit = local_cpu; + + if (valid) + { + /* clear target VCPU's "requeue pending" flag (old entries for syswide devices) */ + xcpu->cpu_requeue_syswide_pending = FALSE; + + /* mark target VCPU as in running state */ + t_bool initial_sim_mp_active = weak_read(sim_mp_active); + cpu_running_set.set(xcpu->cpu_id); + xcpu->cpu_state = CPU_STATE_RUNNING; + sim_mp_active_update(); + + /* + * if starting first secondary, thread priority management is transitioned from disabled + * to possibly enabled (unless host_dedicated is TRUE) and we must propagate primary VCPU + * logical thread priority to host thread priority; see detailed explanation in a comment in cpu_once_a_second() + */ + if (! initial_sim_mp_active) + { + cpu_unit->cpu_thread_priority = SIMH_THREAD_PRIORITY_INVALID; + cpu_reevaluate_thread_priority(RUN_PASS); + } + + /* mark current VCPU as enabled for synchronizaton window */ + syncw_enable_cpu(RUN_PASS, SYNCW_NOLOCK); + if (initial_sim_mp_active == FALSE) + { + /* + * sincw syncw is not maintained while SMP is not active (initial_sim_mp_active == FALSE), + * and thus there is no syncw pre-history at this point, as a precaution activate full syncw + * constraint set on current VCPU + */ + syncw_reeval_sys(RUN_PASS); + syncw_enter_ilk(RUN_PASS); + } + + /* mark target VCPU as enabled for synchronizaton window */ + cpu_unit = rscx->cpu_unit = xcpu; + syncw_reinit_cpu(RUN_PASS, SYNCW_NOLOCK); + syncw_reeval_sys(RUN_PASS); + cpu_unit = rscx->cpu_unit = local_cpu; + + /* set up SYNCLK and clock tracking */ + xcpu->cpu_last_synclk_cycles = XCPU_CURRENT_CYCLES - (CPU_CURRENT_CYCLES - cpu_unit->cpu_last_synclk_cycles); + xcpu->cpu_last_tslice_tick_cycles = XCPU_CURRENT_CYCLES - (CPU_CURRENT_CYCLES - cpu_unit->cpu_last_tslice_tick_cycles); + xcpu->cpu_last_second_tick_cycles = XCPU_CURRENT_CYCLES - (CPU_CURRENT_CYCLES - cpu_unit->cpu_last_second_tick_cycles); + + /* signal "go" to the target VCPU's work thread */ + smp_wmb(); + xcpu->cpu_run_gate->release(1); + + return TRUE; + } + else + { + /* passed arguments were invalid */ + return FALSE; + } +} + +/* + * Called on ROM access. Detect ROM execution. + * + * In case primary processor jumped into ROM while secondary processors were active, + * perform emergency shutdown of secondaries. + * + * This condition should normally never happen, this is just an emergency safeguard + * in case guest OS fails to shut down properly. + */ +void cpu_on_rom_rd(RUN_DECL) +{ + if (weak_read(sim_mp_active) && cpu_unit->is_primary_cpu() && mapen == 0 && ADDR_IS_ROM(PC)) + { + syncw_leave_all(RUN_PASS, SYNCW_OVERRIDE_ALL | SYNCW_DISABLE_CPU); + cpu_shutdown_secondaries(RUN_PASS); + } +} + +/* + * Called when primary is about to shutdown or enter uniprocessor mode. + * + * Perform emerghency shutdown of secondary processors in unlikely case + * guest OS failed to shut them down properly. + * + * Note: it will mark all running CPUs as SYNCW_NOSYNC (i.e. uneligible for waiting or + * being waited on in the inter-processor synchronization window). + */ +void cpu_shutdown_secondaries(RUN_DECL) +{ + /* uniprocessor? */ + if (weak_read(sim_mp_active) == FALSE) + return; + + /* let some time for sim_mp_active to update in case secondaries are being stopped + (not critical for correctness, just performance) */ + sim_os_ms_sleep(3); + + if (weak_read(sim_mp_active) == FALSE) + return; + + smp_printf ("\nPrimary processor in SMP system is about to halt.\n"); + smp_printf ("Forcing emergency shutdown of seconary CPUs...\n"); + if (sim_log) + { + fprintf (sim_log, "Primary processor in SMP system is about to halt.\n"); + fprintf (sim_log, "Forcing emergency shutdown of seconary CPUs...\n"); + } + + cpu_database_lock->lock(); + syncw.on = 0; + /* send non-maskable IPI to stop secondaries */ + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + if (cpu_running_set.is_set(cpu_ix)) + { + interrupt_set_int(cpu_units[cpu_ix], IPL_STOP, INT_V_STOP); + } + } + syncw_wakeup_all_cpus(SYNCW_DISABLE_CPU); + cpu_database_lock->unlock(); + + /* wait until all secondaries are stopped or stop_cpus is set */ + for (;;) + { + sim_os_ms_sleep(3); + cpu_database_lock->lock(); + t_bool doexit = stop_cpus || weak_read(sim_mp_active) == FALSE; + cpu_database_lock->unlock(); + if (doexit) break; + } + + smp_printf ("\nCompleted emergency shutdown of seconary CPUs.\n"); + if (sim_log) + fprintf (sim_log, "Completed emergency shutdown of seconary CPUs.\n"); +} + +/* + * This routine is invoked on each running VCPU once a second, + * more specifically once a second in virtual time scale. + * + * Simulator tries to keep virtual time close to wall time, and normally it is + * more or less close, but can also diverge. + */ +void cpu_once_a_second(RUN_DECL) +{ + /* + * When running in uniprocessor mode with only only one processor active and secondaries not started + * (or all stopped), or when running on a dedicated host, VCPU thread priority is not controlled. + * + * This is an optimization in order to avoid the overhead of "set thread priority" calls to host OS + * that are not essential in this case. + * + * Thread priority control is governed by macro + * + * #define must_control_prio() (sim_mp_active && !sim_host_dedicated) + * + * When must_control_prio() evaluates to TRUE, "set thread priority" calls are executed, otherwise + * they are not. cpu_unit->cpu_thread_priority is still maintained, but actuall underlying base + * priority is not changed and is kept at CPU_RUN level. + * + * The only exception to this is calibration: thread is always allowed to actually raise + * to CALIBRATION level and drop down from it (see cpu_should_actually_change_thread_priority). + * + * Thus, when multiple processor are active, actual VCPU thread priority is subject to change. + * When only the primary is active, it is kept at CPU_RUN level. + * + * One exceptions to this is calibration: processor is allowed to raise to CALIBRATION level + * and drop down from it (in the latter case VCPU is always forced down to CPU_RUN). + * + * Second exception is a dedicated host: if host is marked by user as dedicated to SIMH, + * thread priority is never changed, except that transition to CALIBRATION is still allowed, + * as well as transition from CALIBRATION down. + * + * There is however a number of borderline cases with described governance by must_control_prio() + * that must be taken care about: + * + * 1) When last active secondary is exiting, primary can be at elevated priority and must + * drop it down. This is handled inside SECEXIT interrupt processing. + * + * 2) When starting first secondary, propagate cpu_unit->cpu_thread_priority to actual + * thread priority of the primary (secondary being started will get it automatically). + * This will be performed by any invocation of cpu_reevaluate_thread_priority() on the + * primary that causes any change in priority, i.e. in very short order after + * sim_mp_active changes to TRUE. Just to be super-correct, we perform such invocation + * right in cpu_start_secondary(). + * + * 3) When first secondary is started and (assuming host_dedicated is FALSE) thread priority + * control is enabled, it may take some time before IOP and CLOCK threads get wind of change. + * + * It may be that for a short while when sending interrupts to VCPUs, CLOCK thread will not + * elevate their prority to CRITICAL_VM. This can at most be for the duration of one clock + * cycle (1/100 sec on VAX) and already the next cycle will bump VCPU threads priority up. + * Thus, even apart from low likelyhood of this problem in the first place, its impact is + * very small and not worth bothering about. + * + * It may also be that for a short while when sending interrupts to the primary VCPU thread, + * IOP threasds will not elevate its priority to CRITICAL_VM. However, apart from low likelyhood + * of this problem in the first place, its impact is quite limited. Furthermore, if + * use_clock_thread is TRUE, then even in the worst case the primary will have its priority + * bumped up in at most 1/100 secs even if IOP did not bump it. Thus this problem does not + * look worth bothering about. + * + * 4) When last secondary is being stopped, the primary will get a notification of this via + * SECEXIT interrupt and will drop its priority (except in very unlikely case that calibration + * is active), but it may take some time before IOP and CLOCK threads get wind of change, + * and thus IOP and CLOCK threads may continue for some (typically short) time bumping up + * primary VCPU thread priority. They may do it even after SECEXIT dropped priority and thus + * negate the drop performed in SECEXIT handler. Furthermore, it is possible (though very + * unlikely) for IOP or CLOCK thread to be preempted for some time after it fetched "sim_mp_active" + * but before it acted on it. Thus, CLOCK and IOP threads can theoretically use stale value + * of "sim_mp_active" some extended time after it went FALSE, and still act on it as if it + * was TRUE. This is very unlikely, but possible. + * + * We must safeguard against such a possibility by counteracting it. Even though the event + * is very unlikely, its impact is severe: if left uncounteracted, it may leave primary VCPU + * thread running all the time at CRITICAL_VM level while in uniprocessor mode again. + * + * The easiest solution is simply to reset primary VCPU thread priority periodically to + * CPU_RUN (for some time after going from MP to UniP mode) -- in case thread priority control + * is makerd as "should be disabled" and unless (unlikely) calibration is active. + * + * Such resets can be performed in SYNCLK or CLK interrupt handlers, for instance. But the most + * convenient solution is to place the reset in cpu_once_a_second() routine. + * + */ + + if (!must_control_prio() && !tmr_is_active(RUN_PASS)) + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_RUN); +} + +#define VAX_MP_CPUCTX_METHODS +#include "vax_cpuctx.h" \ No newline at end of file diff --git a/src/VAX/vax_cpu.h b/src/VAX/vax_cpu.h new file mode 100644 index 0000000..d9146a2 --- /dev/null +++ b/src/VAX/vax_cpu.h @@ -0,0 +1,61 @@ +extern int32 op_ashq (RUN_DECL, int32 *opnd, int32 *rh, int32 *flg); +extern int32 op_emul (RUN_DECL, int32 mpy, int32 mpc, int32 *rh); +extern int32 op_ediv (RUN_DECL, int32 *opnd, int32 *rh, int32 *flg); +extern int32 op_bb_n (RUN_DECL, int32 *opnd, int32 acc); +extern int32 op_bb_x (RUN_DECL, int32 *opnd, int32 newb, int32 acc, t_bool interlocked); +extern int32 op_extv (RUN_DECL, int32 *opnd, int32 vfldrp1, int32 acc); +extern int32 op_ffs (RUN_DECL, uint32 fld, int32 size); +extern void op_insv (RUN_DECL, int32 *opnd, int32 vfldrp1, int32 acc); +extern int32 op_call (RUN_DECL, int32 *opnd, t_bool gs, int32 acc); +extern int32 op_ret (RUN_DECL, int32 acc); +extern int32 op_insque (RUN_DECL, int32 *opnd, int32 acc); +extern int32 op_remque (RUN_DECL, int32 *opnd, int32 acc); +extern int32 op_insqhi (RUN_DECL, int32 *opnd, int32 acc); +extern int32 op_insqti (RUN_DECL, int32 *opnd, int32 acc); +extern int32 op_remqhi (RUN_DECL, int32 *opnd, int32 acc); +extern int32 op_remqti (RUN_DECL, int32 *opnd, int32 acc); +extern void op_pushr (RUN_DECL, int32 *opnd, int32 acc); +extern void op_popr (RUN_DECL, int32 *opnd, int32 acc); +extern int32 op_movc (RUN_DECL, int32 *opnd, int32 opc, int32 acc); +extern int32 op_cmpc (RUN_DECL, int32 *opnd, int32 opc, int32 acc); +extern int32 op_locskp (RUN_DECL, int32 *opnd, int32 opc, int32 acc); +extern int32 op_scnspn (RUN_DECL, int32 *opnd, int32 opc, int32 acc); +extern int32 op_chm (RUN_DECL, int32 *opnd, int32 cc, int32 opc); +extern int32 op_rei (RUN_DECL, int32 acc); +extern void op_ldpctx (RUN_DECL, int32 acc); +extern void op_svpctx (RUN_DECL, int32 acc); +extern int32 op_probe (RUN_DECL, int32 *opnd, int32 opc); +extern int32 op_mtpr (RUN_DECL, int32 *opnd); +extern int32 op_mfpr (RUN_DECL, int32 *opnd); +extern int32 op_movfd (RUN_DECL, int32 val); +extern int32 op_movg (RUN_DECL, int32 val); +extern int32 op_mnegfd (RUN_DECL, int32 val); +extern int32 op_mnegg (RUN_DECL, int32 val); +extern int32 op_cmpfd (RUN_DECL, int32 h1, int32 l1, int32 h2, int32 l2); +extern int32 op_cmpg (RUN_DECL, int32 h1, int32 l1, int32 h2, int32 l2); +extern int32 op_cvtifdg (RUN_DECL, int32 val, int32 *rh, int32 opc); +extern int32 op_cvtfdgi (RUN_DECL, int32 *opnd, int32 *flg, int32 opc); +extern int32 op_cvtdf (RUN_DECL, int32 *opnd); +extern int32 op_cvtgf (RUN_DECL, int32 *opnd); +extern int32 op_cvtfg (RUN_DECL, int32 *opnd, int32 *rh); +extern int32 op_cvtgh (RUN_DECL, int32 *opnd, int32 *hflt); +extern int32 op_addf (RUN_DECL, int32 *opnd, t_bool sub); +extern int32 op_addd (RUN_DECL, int32 *opnd, int32 *rh, t_bool sub); +extern int32 op_addg (RUN_DECL, int32 *opnd, int32 *rh, t_bool sub); +extern int32 op_mulf (RUN_DECL, int32 *opnd); +extern int32 op_muld (RUN_DECL, int32 *opnd, int32 *rh); +extern int32 op_mulg (RUN_DECL, int32 *opnd, int32 *rh); +extern int32 op_divf (RUN_DECL, int32 *opnd); +extern int32 op_divd (RUN_DECL, int32 *opnd, int32 *rh); +extern int32 op_divg (RUN_DECL, int32 *opnd, int32 *rh); +extern int32 op_emodf (RUN_DECL, int32 *opnd, int32 *intgr, int32 *flg); +extern int32 op_emodd (RUN_DECL, int32 *opnd, int32 *rh, int32 *intgr, int32 *flg); +extern int32 op_emodg (RUN_DECL, int32 *opnd, int32 *rh, int32 *intgr, int32 *flg); +extern void op_polyf (RUN_DECL, int32 *opnd, int32 acc); +extern void op_polyd (RUN_DECL, int32 *opnd, int32 acc); +extern void op_polyg (RUN_DECL, int32 *opnd, int32 acc); +extern int32 op_cmode (RUN_DECL, int32 cc); +extern int32 op_cis (RUN_DECL, int32 *opnd, int32 cc, int32 opc, int32 acc); +extern int32 op_octa (RUN_DECL, int32 *opnd, int32 cc, int32 opc, int32 acc, int32 spec, int32 va); +extern int32 intexc (RUN_DECL, int32 vec, int32 cc, int32 ipl, int ei); +extern void op_mtpr_simh (RUN_DECL, int32 rqaddr); diff --git a/src/VAX/vax_cpu1.cpp b/src/VAX/vax_cpu1.cpp new file mode 100644 index 0000000..a0cd305 --- /dev/null +++ b/src/VAX/vax_cpu1.cpp @@ -0,0 +1,3307 @@ +/* vax_cpu1.c: VAX complex instructions + + 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"), + 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. + + 25-Nov-11 RMS Added VEC_QBUS test in interrupt handler + 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 + Added mbz check in LDPCTX for 11/780 + 22-Sep-06 RMS Fixed declarations (from Sterling Garwood) + 30-Sep-04 RMS Added conditionals for full VAX + Moved emulation to vax_cis.c + Moved model-specific IPRs to system module + 27-Jan-04 RMS Added device logging support + Fixed EXTxV, INSV double register PC reference fault + 30-Apr-02 RMS Fixed interrupt/exception handler to clear traps + 17-Apr-02 RMS Fixed pos > 31 test in bit fields (should be unsigned) + 14-Apr-02 RMS Fixed prv_mode handling for interrupts (found by Tim Stark) + Fixed PROBEx to mask mode to 2b (found by Kevin Handy) + + This module contains the instruction simulators for + + Field instructions: + - BBS, BBC, BBSSI, BBCCI + - BBSC, BBCC, BBCS, BBSS + - EXTV, EXTZV, CMPV, CMPZV + - FFS, FFC, INSV + + Call/return and push/pop instructions: + - CALLS, CALLG, RET + - PUSHR, POPR + + Queue instructions: + - INSQUE, REMQUE + - INSQHI, INSQTI, REMQHI, REMQTI + + String instructions: + - MOVC3, MOVC5, CMPC3, CMPC5 + - LOCC, SKPC, SCANC, SPANC + + Operating system interface instructions: + - CHMK, CHME, CHMS, CHMU + - PROBER, PROBEW, REI + - MTPR, MFPR + - LDPCTX, SVPCTX + - (interrupt and exception routine) +*/ + +#include "sim_defs.h" +#include "vax_defs.h" + +#include "vax_cpu.h" + +static int32 op_insqti_native (RUN_DECL, int32 *opnd, int32 acc); +static int32 op_insqti_portable (RUN_DECL, int32 *opnd, int32 acc); +static int32 op_remqti_native (RUN_DECL, int32 *opnd, int32 acc); +static int32 op_remqti_portable (RUN_DECL, int32 *opnd, int32 acc); +static int32 op_remqhi_native (RUN_DECL, int32 *opnd, int32 acc); +static int32 op_remqhi_portable (RUN_DECL, int32 *opnd, int32 acc); +static int32 op_remqhi_portable_2 (RUN_DECL, int32 *opnd, int32 acc, sim_try_volatile InterlockedOpLock* iop); +static int32 op_insqhi_native (RUN_DECL, int32 *opnd, int32 acc); +static int32 op_insqhi_portable (RUN_DECL, int32 *opnd, int32 acc); +static int32 op_insqhi_portable_2 (RUN_DECL, int32 *opnd, int32 acc, sim_try_volatile InterlockedOpLock* iop); + + +static const uint8 rcnt[128] = { + 0, 4, 4, 8, 4, 8, 8,12, 4, 8, 8,12, 8,12,12,16, /* 00 - 0F */ + 4, 8, 8,12, 8,12,12,16, 8,12,12,16,12,16,16,20, /* 10 - 1F */ + 4, 8, 8,12, 8,12,12,16, 8,12,12,16,12,16,16,20, /* 20 - 2F */ + 8,12,12,16,12,16,16,20,12,16,16,20,16,20,20,24, /* 30 - 3F */ + 4, 8, 8,12, 8,12,12,16, 8,12,12,16,12,16,16,20, /* 40 - 4F */ + 8,12,12,16,12,16,16,20,12,16,16,20,16,20,20,24, /* 50 - 5F */ + 8,12,12,16,12,16,16,20,12,16,16,20,16,20,20,24, /* 60 - 6F */ +12,16,16,20,16,20,20,24,16,20,20,24,20,24,24,28 /* 70 - 7F */ +}; + +// int32 last_chm = 0; + +extern const uint32 byte_mask[33]; +extern SMP_FILE *sim_deb; +extern DEVICE cpu_dev; + +/* Branch on bit and no modify + Branch on bit and modify + + opnd[0] = position (pos.rl) + opnd[1] = register number/memory flag + opnd[2] = memory address, if memory + Returns bit to be tested +*/ + +int32 op_bb_n (RUN_DECL, int32 *opnd, int32 acc) +{ +int32 pos = opnd[0]; +int32 rn = opnd[1]; +int32 ea, by; + +if (rn >= 0) { /* register? */ + if (((uint32) pos) > 31) /* pos > 31? fault */ + RSVD_OPND_FAULT; + return (R[rn] >> pos) & 1; /* get bit */ + } +ea = opnd[2] + (pos >> 3); /* base byte addr */ +pos = pos & 07; /* pos in byte */ +by = Read (RUN_PASS, ea, L_BYTE, RA); /* read byte */ +return ((by >> pos) & 1); /* get bit */ +} + +int32 op_bb_x (RUN_DECL, int32 *opnd, int32 newb, int32 acc, t_bool interlocked) +{ + // t_bool entering_ilk = FALSE; + + if (interlocked && use_native_interlocked && opnd[1] < 0) + { + int32 pos = opnd[0]; + int32 ea = opnd[2] + (pos >> 3); + int32 pa = TestMark (RUN_PASS, ea, WA, NULL); + if (ADDR_IS_MEM(pa)) + { + /* enter ILK synchronization window for BBSSI */ + if (newb && (syncw.on & SYNCW_ILK) && !(cpu_unit->syncw_active & SYNCW_ILK) && + PSL_GETIPL(PSL) >= syncw.ipl_resched) + { + syncw_enter_ilk(RUN_PASS); + // entering_ilk = TRUE; + } + + /* change the bit */ + int32 bit = smp_native_bb(M, pa, pos & 7, (t_bool) newb); + + /* if the bit was already set, pause in presumed BBSSI spinlock acquisition spin-loop... */ + if (newb && bit) + { + smp_cpu_relax(); + /* + * It is tempting to back out of ILK if BBSSI does not actually flip the bit. + * However this would limit the coverage of interaction cases protected by ILK window. + * In addition, it would also cause thrashing when spinning on OS-level spinlock: + * for each spin cycle, VCPU will be entering and exiting syncw, having to acquire + * cpu_database_lock twice. Therefore avoid it. + */ + // if (FALSE && entering_ilk) + // syncw_leave_ilk(RUN_PASS); + } + + return bit; + } + /* + * BBSSI/BBCCI to a non-memory location is not supported by VAX architecture. + * Could signal an exception, but just fall through to the original SIMH code. + */ + } + + sim_try_volatile InterlockedOpLock iop(RUN_PASS); + + int32 pos = opnd[0]; + int32 rn = opnd[1]; + int32 ea, by, bit; + + t_bool b_sys_mask = FALSE; + uint32 old_sys_mask = 0; /* initialize to suppress false GCC warning */ + int32 sys_pos = 0; /* ... */ + + if (rn >= 0) /* register? */ + { + if (((uint32) pos) > 31) /* pos > 31? fault */ + RSVD_OPND_FAULT; + bit = (R[rn] >> pos) & 1; /* get bit */ + R[rn] = newb? (R[rn] | (1u << pos)): (R[rn] & ~(1u << pos)); + return bit; + } + + /* Trap access to VMS CPU idle mask */ + if (unlikely(opnd[2] == sys_idle_cpu_mask_va) && + sys_idle_cpu_mask_va && mapen && + (PSL & PSL_CUR) == 0 && is_os_running(RUN_PASS) && + pos <= 31) + { + /* in VMS, only BBSC, CLRL and BICL2 will actually clear bits in the idle mask */ + b_sys_mask = TRUE; + old_sys_mask = Read (RUN_PASS, sys_idle_cpu_mask_va, L_LONG, RA); + sys_pos = pos; + } + + ea = opnd[2] + (pos >> 3); /* base byte addr */ + pos = pos & 07; /* pos in byte */ + if (interlocked) iop.virt_lock(ea, acc); /* base longword will be interlocked by iop.lock(), not target byte */ + by = Read (RUN_PASS, ea, L_BYTE, WA); /* read byte */ + bit = (by >> pos) & 1; /* get bit */ + + /* enter ILK synchronization window for BBSSI, but will stay there only if BBSSI actually flips the bit */ + if (interlocked && newb && (syncw.on & SYNCW_ILK) && !(cpu_unit->syncw_active & SYNCW_ILK)) + { + syncw_enter_ilk(RUN_PASS); + // entering_ilk = TRUE; + } + + by = newb ? (by | (1u << pos)) : (by & ~(1u << pos)); /* change bit */ + iop.wmb = TRUE; + Write (RUN_PASS, ea, by, L_BYTE, WA); /* rewrite byte */ + + iop.unlock(); + + /* If bits in VMS CPU idle mask were cleared, wake up corresponding CPUs */ + if (unlikely(b_sys_mask)) + { + uint32 new_sys_mask = newb ? (old_sys_mask | (1u << sys_pos)) : + (old_sys_mask & ~(1u << sys_pos)); + wakeup_cpus(RUN_PASS, old_sys_mask, new_sys_mask); + } + + /* if the bit was already set, pause in presumed BBSSI spinlock acquisition spin-loop... */ + if (interlocked && newb && bit) + { + smp_cpu_relax(); + /* Do NOT back out of ILK (see comment above) */ + // if (FALSE && entering_ilk) + // syncw_leave_ilk(RUN_PASS); + } + + return bit; +} + +/* Extract field + + opnd[0] = position (pos.rl) + opnd[1] = size (size.rb) + opnd[2] = register number/memory flag + opnd[3] = register content/memory address + + If the field is in a register, rn + 1 is in vfldrp1 +*/ + +int32 op_extv (RUN_DECL, int32 *opnd, int32 vfldrp1, int32 acc) +{ +int32 pos = opnd[0]; +int32 size = opnd[1]; +int32 rn = opnd[2]; +uint32 wd = opnd[3]; +int32 ba, wd1 = 0; + +if (size == 0) /* size 0? field = 0 */ + return 0; +if (size > 32) /* size > 32? fault */ + RSVD_OPND_FAULT; +if (rn >= 0) { /* register? */ + if (((uint32) pos) > 31) /* pos > 31? fault */ + RSVD_OPND_FAULT; + if (((pos + size) > 32) && (rn >= nSP)) /* span 2 reg, PC? */ + RSVD_ADDR_FAULT; /* fault */ + if (pos) + wd = (wd >> pos) | (((uint32) vfldrp1) << (32 - pos)); + } +else { + ba = wd + (pos >> 3); /* base byte addr */ + pos = (pos & 07) | ((ba & 03) << 3); /* bit offset */ + ba = ba & ~03; /* lw align base */ + wd = Read (RUN_PASS, ba, L_LONG, RA); /* read field */ + if ((size + pos) > 32) + wd1 = Read (RUN_PASS, ba + 4, L_LONG, RA); + if (pos) + wd = (wd >> pos) | (((uint32) wd1) << (32 - pos)); + } +return wd & byte_mask[size]; +} + +/* Insert field + + opnd[0] = field (src.rl) + opnd[1] = position (pos.rl) + opnd[2] = size (size.rb) + opnd[3] = register number/memory flag + opnd[4] = register content/memory address + + If the field is in a register, rn + 1 is in vfldrp1 +*/ + +void op_insv (RUN_DECL, int32 *opnd, int32 vfldrp1, int32 acc) +{ +uint32 ins = opnd[0]; +int32 pos = opnd[1]; +int32 size = opnd[2]; +int32 rn = opnd[3]; +int32 val, mask, ba, wd, wd1; + +if (size == 0) /* size = 0? done */ + return; +if (size > 32) /* size > 32? fault */ + RSVD_OPND_FAULT; +if (rn >= 0) { /* in registers? */ + if (((uint32) pos) > 31) /* pos > 31? fault */ + RSVD_OPND_FAULT; + if ((pos + size) > 32) { /* span two reg? */ + if (rn >= nSP) /* if PC, fault */ + RSVD_ADDR_FAULT; + mask = byte_mask[pos + size - 32]; /* insert fragment */ + val = ins >> (32 - pos); + R[rn + 1] = (vfldrp1 & ~mask) | (val & mask); + } + mask = byte_mask[size] << pos; /* insert field */ + val = ins << pos; + R[rn] = (R[rn] & ~mask) | (val & mask); + } +else { + ba = opnd[4] + (pos >> 3); /* base byte addr */ + pos = (pos & 07) | ((ba & 03) << 3); /* bit offset */ + ba = ba & ~03; /* lw align base */ + wd = Read (RUN_PASS, ba, L_LONG, WA); /* read field */ + if ((size + pos) > 32) { /* field span lw? */ + wd1 = Read (RUN_PASS, ba + 4, L_LONG, WA); /* read 2nd lw */ + mask = byte_mask[pos + size - 32]; /* insert fragment */ + val = ins >> (32 - pos); + Write (RUN_PASS, ba + 4, (wd1 & ~mask) | (val & mask), L_LONG, WA); + } + mask = byte_mask[size] << pos; /* insert field */ + val = ins << pos; + Write (RUN_PASS, ba, (wd & ~mask) | (val & mask), L_LONG, WA); + } +return; +} + +/* Find first */ + +int32 op_ffs (RUN_DECL, uint32 wd, int32 size) +{ +int32 i; + +for (i = 0; wd; i++, wd = wd >> 1) { + if (wd & 1) + return i; + } +return size; +} + +#define CALL_DV 0x8000 /* DV set */ +#define CALL_IV 0x4000 /* IV set */ +#define CALL_MBZ 0x3000 /* MBZ */ +#define CALL_MASK 0x0FFF /* mask */ +#define CALL_V_SPA 30 /* SPA */ +#define CALL_M_SPA 03 +#define CALL_V_S 29 /* S flag */ +#define CALL_S (1 << CALL_V_S) +#define CALL_V_MASK 16 +#define CALL_PUSH(n) if ((mask >> (n)) & 1) { \ + tsp = tsp - 4; \ + Write (RUN_PASS, tsp, R[n], L_LONG, WA); \ + } +#define CALL_GETSPA(x) (((x) >> CALL_V_SPA) & CALL_M_SPA) +#define RET_POP(n) if ((spamask >> (n + CALL_V_MASK)) & 1) { \ + R[n] = Read (RUN_PASS, tsp, L_LONG, RA); \ + tsp = tsp + 4; \ + } +#define PUSHR_PUSH(n) CALL_PUSH(n) +#define POPR_POP(n) if ((mask >> (n)) & 1) { \ + R[n] = Read (RUN_PASS, SP, L_LONG, RA); \ + SP = SP + 4; \ + } + +/* CALLG, CALLS + + opnd[0] = argument (arg.rx) + opnd[1] = procedure address (adr.ab) + flg = CALLG (0), CALLS (1) + acc = access mask + + These instructions implement a generalized procedure call and return facility. + The principal data structure involved is the stack frame. + CALLS and CALLG build a stack frame in the following format: + + + +---------------------------------------------------------------+ + | condition handler (initially 0) | + +---+-+-+-----------------------+--------------------+----------+ + |SPA|S|0| entry mask<11:0> | saved PSW<15:5> | 0 0 0 0 0| + +---+-+-+-----------------------+--------------------+----------+ + | saved AP | + +---------------------------------------------------------------+ + | saved FP | + +---------------------------------------------------------------+ + | saved PC | + +---------------------------------------------------------------+ + | saved R0 (...) | + +---------------------------------------------------------------+ + . . + . (according to entry mask<11:0>) . + . . + +---------------------------------------------------------------+ + | saved R11 (...) | + +---------------+-----------------------------------------------+ + | #args (CALLS) | (0-3 bytes needed to align stack) | + +---------------+-----------------------------------------------+ + | | 0 0 0 (CALLS) | + +---------------+-----------------------------------------------+ + + RET expects to find this structure based at the frame pointer (FP). + + For CALLG and CALLS, the entry mask specifies the new settings of + DV and IV, and also which registers are to be saved on entry: + + 15 14 13 12 11 0 + +--+--+-----+----------------------------------+ + |DV|IV| MBZ | register mask | + +--+--+-----+----------------------------------+ + + CALLG/CALLS operation: + + read the procedure entry mask + make sure that the stack frame will be accessible + if CALLS, push the number of arguments onto the stack + align the stack to the next lower longword boundary + push the registers specified by the procedure entry mask + push PC, AP, FP, saved SPA/S0/mask/PSW, condition handler + update PC, SP, FP, AP + update PSW traps, clear condition codes +*/ + +int32 op_call (RUN_DECL, int32 *opnd, t_bool gs, int32 acc) +{ +int32 addr = opnd[1]; +int32 mask, stklen, tsp, wd; + +mask = Read (RUN_PASS, addr, L_WORD, RA); /* get proc mask */ +if (mask & CALL_MBZ) /* test mbz */ + RSVD_OPND_FAULT; +stklen = rcnt[mask & 077] + rcnt[(mask >> 6) & 077] + (gs? 24: 20); +Read (RUN_PASS, SP - stklen, L_BYTE, WA); /* wchk stk */ +if (gs) { + Write (RUN_PASS, SP - 4, opnd[0], L_LONG, WA); /* if S, push #arg */ + SP = SP - 4; /* stack is valid */ + } +tsp = SP & ~CALL_M_SPA; /* lw align stack */ +CALL_PUSH (11); /* check mask bits, */ +CALL_PUSH (10); /* push sel reg */ +CALL_PUSH (9); +CALL_PUSH (8); +CALL_PUSH (7); +CALL_PUSH (6); +CALL_PUSH (5); +CALL_PUSH (4); +CALL_PUSH (3); +CALL_PUSH (2); +CALL_PUSH (1); +CALL_PUSH (0); +Write (RUN_PASS, tsp - 4, PC, L_LONG, WA); /* push PC */ +Write (RUN_PASS, tsp - 8, FP, L_LONG, WA); /* push AP */ +Write (RUN_PASS, tsp - 12, AP, L_LONG, WA); /* push FP */ +wd = ((SP & CALL_M_SPA) << CALL_V_SPA) | (gs << CALL_V_S) | + ((mask & CALL_MASK) << CALL_V_MASK) | (PSL & 0xFFE0); +Write (RUN_PASS, tsp - 16, wd, L_LONG, WA); /* push spa/s/mask/psw */ +Write (RUN_PASS, tsp - 20, 0, L_LONG, WA); /* push cond hdlr */ +if (gs) /* update AP */ + AP = SP; +else AP = opnd[0]; +SP = FP = tsp - 20; /* update FP, SP */ +PSL = (PSL & ~(PSW_DV | PSW_FU | PSW_IV)) | /* update PSW */ + ((mask & CALL_DV)? PSW_DV: 0) | + ((mask & CALL_IV)? PSW_IV: 0); +JUMP (addr + 2); /* new PC */ +return 0; /* new cc's */ +} + +int32 op_ret (RUN_DECL, int32 acc) +{ +int32 spamask, stklen, newpc, nargs; +int32 tsp = FP; + +spamask = Read (RUN_PASS, tsp + 4, L_LONG, RA); /* spa/s/mask/psw */ +if (spamask & PSW_MBZ) /* test mbz */ + RSVD_OPND_FAULT; +stklen = rcnt[(spamask >> CALL_V_MASK) & 077] + + rcnt[(spamask >> (CALL_V_MASK + 6)) & 077] + ((spamask & CALL_S)? 23: 19); +Read (RUN_PASS, tsp + stklen, L_BYTE, RA); /* rchk stk end */ +AP = Read (RUN_PASS, tsp + 8, L_LONG, RA); /* restore AP */ +FP = Read (RUN_PASS, tsp + 12, L_LONG, RA); /* restore FP */ +newpc = Read (RUN_PASS, tsp + 16, L_LONG, RA); /* get new PC */ +tsp = tsp + 20; /* update stk ptr */ +RET_POP (0); /* chk mask bits, */ +RET_POP (1); /* pop sel regs */ +RET_POP (2); +RET_POP (3); +RET_POP (4); +RET_POP (5); +RET_POP (6); +RET_POP (7); +RET_POP (8); +RET_POP (9); +RET_POP (10); +RET_POP (11); +SP = tsp + CALL_GETSPA (spamask); /* dealign stack */ +if (spamask & CALL_S) { /* CALLS? */ + nargs = Read (RUN_PASS, SP, L_LONG, RA); /* read #args */ + SP = SP + 4 + ((nargs & BMASK) << 2); /* pop arg list */ + } +PSL = (PSL & ~(PSW_DV | PSW_FU | PSW_IV | PSW_T)) | /* reset PSW */ + (spamask & (PSW_DV | PSW_FU | PSW_IV | PSW_T)); +JUMP (newpc); /* set new PC */ +return spamask & (CC_MASK); /* return cc's */ +} + +/* PUSHR and POPR */ + +void op_pushr (RUN_DECL, int32 *opnd, int32 acc) +{ +int32 mask = opnd[0] & 0x7FFF; +int32 stklen, tsp; + +if (mask == 0) + return; +stklen = rcnt[(mask >> 7) & 0177] + rcnt[mask & 0177] + + ((mask & 0x4000)? 4: 0); +Read (RUN_PASS, SP - stklen, L_BYTE, WA); /* wchk stk end */ +tsp = SP; /* temp stk ptr */ +PUSHR_PUSH (14); /* check mask bits, */ +PUSHR_PUSH (13); /* push sel reg */ +PUSHR_PUSH (12); +PUSHR_PUSH (11); +PUSHR_PUSH (10); +PUSHR_PUSH (9); +PUSHR_PUSH (8); +PUSHR_PUSH (7); +PUSHR_PUSH (6); +PUSHR_PUSH (5); +PUSHR_PUSH (4); +PUSHR_PUSH (3); +PUSHR_PUSH (2); +PUSHR_PUSH (1); +PUSHR_PUSH (0); +SP = tsp; /* update stk ptr */ +return; +} + +void op_popr (RUN_DECL, int32 *opnd, int32 acc) +{ +int32 mask = opnd[0] & 0x7FFF; +int32 stklen; + +if (mask == 0) + return; +stklen = rcnt[(mask >> 7) & 0177] + rcnt[mask & 0177] + + ((mask & 0x4000)? 4: 0); +Read (RUN_PASS, SP + stklen - 1, L_BYTE, RA); /* rchk stk end */ +POPR_POP (0); /* check mask bits, */ +POPR_POP (1); /* pop sel regs */ +POPR_POP (2); +POPR_POP (3); +POPR_POP (4); +POPR_POP (5); +POPR_POP (6); +POPR_POP (7); +POPR_POP (8); +POPR_POP (9); +POPR_POP (10); +POPR_POP (11); +POPR_POP (12); +POPR_POP (13); +if (mask & 0x4000) /* if pop SP, no inc */ + SP = Read (RUN_PASS, SP, L_LONG, RA); +return; +} + + +/* INSQUE + + opnd[0] = entry address (ent.ab) + opnd[1] = predecessor address (pred.ab) + + Condition codes returned to caller on comparison of (ent):(ent+4). + All writes must be checked before any writes are done. + + Pictorially: + + BEFORE AFTER + + P: S P: E W + P+4: (n/a) P+4: (n/a) + + E: --- E: S W + E+4: --- E+4: P W + + S: (n/a) S: (n/a) + S+4: P S+4: E W + + s+4 must be tested with a read modify rather than a probe, as it + might be misaligned. +*/ + +int32 op_insque (RUN_DECL, int32 *opnd, int32 acc) +{ +int32 p = opnd[1]; +int32 e = opnd[0]; +int32 s, cc; + +s = Read (RUN_PASS, p, L_LONG, WA); /* s <- (p), wchk */ +Read (RUN_PASS, s + 4, L_LONG, WA); /* wchk s+4 */ +Read (RUN_PASS, e + 4, L_LONG, WA); /* wchk e+4 */ +Write (RUN_PASS, e, s, L_LONG, WA); /* (e) <- s */ +Write (RUN_PASS, e + 4, p, L_LONG, WA); /* (e+4) <- p */ +Write (RUN_PASS, s + 4, e, L_LONG, WA); /* (s+4) <- ent */ +Write (RUN_PASS, p, e, L_LONG, WA); /* (p) <- e */ +CC_CMP_L (s, p); /* set cc's */ +return cc; +} + +/* REMQUE + + opnd[0] = entry address (ent.ab) + opnd[1:2] = destination address (dst.wl) + + Condition codes returned to caller based on (ent):(ent+4). + All writes must be checked before any writes are done. + + Pictorially: + + BEFORE AFTER + + P: E P: S W + P+4: (n/a) P+4: (n/a) + + E: S W E: S + E+4: P W E+4: P + + S: (n/a) S: (n/a) + S+4: E W S+4: P + +*/ + +int32 op_remque (RUN_DECL, int32 *opnd, int32 acc) +{ +int32 e = opnd[0]; +int32 s, p, cc; + +s = Read (RUN_PASS, e, L_LONG, RA); /* s <- (e) */ +p = Read (RUN_PASS, e + 4, L_LONG, RA); /* p <- (e+4) */ +CC_CMP_L (s, p); /* set cc's */ +if (e != p) { /* queue !empty? */ + Read (RUN_PASS, s + 4, L_LONG, WA); /* wchk (s+4) */ + if (opnd[1] < 0) /* wchk dest */ + Read (RUN_PASS, opnd[2], L_LONG, WA); + Write (RUN_PASS, p, s, L_LONG, WA); /* (p) <- s */ + Write (RUN_PASS, s + 4, p, L_LONG, WA); /* (s+4) <- p */ + } +else cc = cc | CC_V; /* else set v */ +if (opnd[1] >= 0) /* store result */ + R[opnd[1]] = e; +else Write (RUN_PASS, opnd[2], e, L_LONG, WA); +return cc; +} + +/* Interlocked insert instructions + + opnd[0] = entry (ent.ab) + opnd[1] = header (hdr.aq) + + Pictorially: + + BEFORE AFTER INSQHI AFTER INSQTI + + H: A-H H: D-H W H: A-H W for interlock + H+4: C-H H+4: C-H H+4: D-H W + + A: B-A A: B-A A: B-A + A+4: H-A A+4: D-A W A+4: H-A + + B: C-B B: C-B B: C-B + B+4: A-B B+4: A-B B+4: A-B + + C: H-C C: H-C C: D-C W + C+4: B-C C+4: B-C C+4: B-C + + D: --- D: A-D W D: H-D W + D+4: --- D+4: H-D W D+4: C-D W + + Note that the queue header, the entry to be inserted, and all + the intermediate entries that are "touched" in any way must be + QUADWORD aligned. In addition, the header and the entry must + not be equal. +*/ + +int32 op_insqhi (RUN_DECL, int32 *opnd, int32 acc) +{ + if (use_native_interlocked) + return op_insqhi_native(RUN_PASS, opnd, acc); + else + return op_insqhi_portable(RUN_PASS, opnd, acc); +} + +static int32 op_insqhi_native (RUN_DECL, int32 *opnd, int32 acc) +{ + int32 pa_h1, pa_d1, pa_d2, pa_a2; + int32 d = opnd[0]; + int32 h = opnd[1]; + int32 a; + sim_try_volatile t_bool release = FALSE; + sim_try_volatile InterlockedOpLock iop(RUN_PASS, IOP_ILK); + + /* check operands alignment and non-equality */ + if (h == d || ((h | d) & 07)) + RSVD_OPND_FAULT; + + /* + * get physical addresses; note that "d" is quadword-aligned, + * therefore it cannot cross page boundary and hence + * pa_d2 is in the same page as pa_d1 + */ + pa_h1 = TestMark (RUN_PASS, h, WA, NULL); + pa_d1 = TestMark (RUN_PASS, d, WA, NULL); + pa_d2 = pa_d1 + 4; + + /* must be in memory (not IO space): do not need to test pa_d2 */ + if (! (ADDR_IS_MEM(pa_h1) && ADDR_IS_MEM(pa_d1))) + RSVD_OPND_FAULT; + + sim_try + { + /* elevate thread priority if required */ + iop.prio_lock(); + + /* acquire secondary interlock */ + smp_pre_interlocked_mb(); + if (smp_native_bb(M, pa_h1, 0, TRUE)) + { + iop.qxi_busy(); + smp_cpu_relax(); + return CC_C; + } + + release = TRUE; + + /* forward link from header */ + a = h + (ReadLP(RUN_PASS, pa_h1) & ~1); + + if (a & 06) /* check quad align */ + RSVD_OPND_FAULT; + + pa_a2 = TestMark (RUN_PASS, a + 4, WA, NULL); /* make sure it is valid */ + if (!ADDR_IS_MEM(pa_a2)) + RSVD_OPND_FAULT; + + WriteLP(RUN_PASS, pa_a2, d - a); + WriteLP(RUN_PASS, pa_d1, a - d); + WriteLP(RUN_PASS, pa_d2, h - d); + + /* store forward link and release secondary interlock */ + smp_interlocked_uint32* vpa_h1 = (smp_interlocked_uint32*) ((t_byte*) M + pa_h1); + while (! smp_interlocked_cas_done(vpa_h1, weak_read(*vpa_h1), (uint32) (d - h))) ; + release = FALSE; + smp_post_interlocked_mb(); + + return (a == h) ? CC_Z : 0; /* Z = 1 if a = h */ + } + sim_catch_all + { + /* release secondary interlock and re-throw */ + if (release) + { + smp_native_bb(M, pa_h1, 0, FALSE); + smp_post_interlocked_mb(); + } + sim_rethrow; + } + sim_end_try + + sim_noreturn_int32; +} + +static int32 op_insqhi_portable (RUN_DECL, int32 *opnd, int32 acc) +{ + sim_try_volatile InterlockedOpLock iop(RUN_PASS, IOP_ILK); + return op_insqhi_portable_2 (RUN_PASS, opnd, acc, &iop); +} + +static int32 op_insqhi_portable_2 (RUN_DECL, int32 *opnd, int32 acc, sim_try_volatile InterlockedOpLock* iop) +{ + int32 h = opnd[1]; + int32 d = opnd[0]; + int32 a, t; + + if ((h == d) || ((h | d) & 07)) /* h, d quad align? */ + RSVD_OPND_FAULT; + Read (RUN_PASS, d, L_BYTE, WA); /* wchk ent */ + iop->virt_lock(h, acc); + a = Read (RUN_PASS, h, L_LONG, WA); /* a <- (h), wchk */ + if (a & 06) /* chk quad align */ + RSVD_OPND_FAULT; + if (a & 01) /* busy, cc = 0001 */ + { + iop->qxi_busy(); + return CC_C; + } + iop->wmb = TRUE; + Write (RUN_PASS, h, a | 1, L_LONG, WA); /* get interlock */ + a = a + h; /* abs addr of a */ + if (Test (RUN_PASS, a, WA, &t) < 0) /* wtst a, rls if err */ + Write (RUN_PASS, h, a - h, L_LONG, WA); + Write (RUN_PASS, a + 4, d - a, L_LONG, WA); /* (a+4) <- d-a, flt ok */ + Write (RUN_PASS, d, a - d, L_LONG, WA); /* (d) <- a-d */ + Write (RUN_PASS, d + 4, h - d, L_LONG, WA); /* (d+4) <- h-d */ + Write (RUN_PASS, h, d - h, L_LONG, WA); /* (h) <- d-h, rls int */ + + return (a == h)? CC_Z: 0; /* Z = 1 if a = h */ +} + +int32 op_insqti (RUN_DECL, int32 *opnd, int32 acc) +{ + if (use_native_interlocked) + return op_insqti_native(RUN_PASS, opnd, acc); + else + return op_insqti_portable(RUN_PASS, opnd, acc); +} + +static int32 op_insqti_native (RUN_DECL, int32 *opnd, int32 acc) +{ + int32 d = opnd[0]; + int32 h = opnd[1]; + int32 pa_c1, pa_d1, pa_d2, pa_h1, pa_h2; + int32 a, c; + sim_try_volatile t_bool release = FALSE; + sim_try_volatile InterlockedOpLock iop(RUN_PASS, IOP_ILK); + + if (h == d || ((h | d) & 07)) /* h, d quad align? */ + RSVD_OPND_FAULT; + + /* + * get physical addresses; note that "h" and "d" are quadword-aligned, + * therefore it cannot cross page boundary and hence + * pa_h2 is in the same page as pa_h1 + */ + pa_h1 = TestMark (RUN_PASS, h, WA, NULL); + pa_h2 = pa_h1 + 4; + + pa_d1 = TestMark (RUN_PASS, d, WA, NULL); + pa_d2 = pa_d1 + 4; + + /* must be in memory (not IO space): do not need to test pa_h2 and pa_d2 */ + if (! (ADDR_IS_MEM(pa_h1) && ADDR_IS_MEM(pa_d1))) + RSVD_OPND_FAULT; + + sim_try + { + /* elevate thread priority if required */ + iop.prio_lock(); + + /* acquire secondary interlock */ + smp_pre_interlocked_mb(); + if (smp_native_bb(M, pa_h1, 0, TRUE)) + { + iop.qxi_busy(); + smp_cpu_relax(); + return CC_C; + } + + release = TRUE; + + smp_interlocked_uint32* vpa_h1 = (smp_interlocked_uint32*) ((t_byte*) M + pa_h1); + a = ReadLP(RUN_PASS, pa_h1) & ~1; + + if (a & 06) /* chk quad align */ + RSVD_OPND_FAULT; + + if (a == 0) + { + /* queue was empty, perform insqhi */ + WriteLP(RUN_PASS, pa_h2, d - h); + WriteLP(RUN_PASS, pa_d1, h - d); + WriteLP(RUN_PASS, pa_d2, h - d); + + /* store forward link and release secondary interlock */ + while (! smp_interlocked_cas_done(vpa_h1, weak_read(*vpa_h1), (uint32) (d - h))) ; + release = FALSE; + smp_post_interlocked_mb(); + + return CC_Z; + } + else + { + c = ReadLP(RUN_PASS, pa_h2) + h; /* address of previous tail */ + if (c & 07) + RSVD_OPND_FAULT; + pa_c1 = TestMark (RUN_PASS, c, WA, NULL); + if (! ADDR_IS_MEM(pa_c1)) + RSVD_OPND_FAULT; + + WriteLP(RUN_PASS, pa_c1, d - c); + WriteLP(RUN_PASS, pa_d1, h - d); + WriteLP(RUN_PASS, pa_d2, c - d); + WriteLP(RUN_PASS, pa_h2, d - h); + + /* release secondary interlock */ + while (! smp_interlocked_cas_done(vpa_h1, weak_read(*vpa_h1), a)) ; + release = FALSE; + smp_post_interlocked_mb(); + + return 0; + } + } + sim_catch_all + { + /* release secondary interlock and re-throw */ + if (release) + { + smp_native_bb(M, pa_h1, 0, FALSE); + smp_post_interlocked_mb(); + } + sim_rethrow; + } + sim_end_try + + sim_noreturn_int32; +} + +static int32 op_insqti_portable (RUN_DECL, int32 *opnd, int32 acc) +{ + sim_try_volatile InterlockedOpLock iop(RUN_PASS, IOP_ILK); + + int32 h = opnd[1]; + int32 d = opnd[0]; + int32 a, c, t; + + if ((h == d) || ((h | d) & 07)) /* h, d quad align? */ + RSVD_OPND_FAULT; + Read (RUN_PASS, d, L_BYTE, WA); /* wchk ent */ + iop.virt_lock(h, acc); + a = Read (RUN_PASS, h, L_LONG, WA); /* a <- (h), wchk */ + if (a == 0) /* if empty, ins hd */ + return op_insqhi_portable_2 (RUN_PASS, opnd, acc, &iop); + if (a & 06) /* chk quad align */ + RSVD_OPND_FAULT; + if (a & 01) /* busy, cc = 0001 */ + { + iop.qxi_busy(); + return CC_C; + } + iop.wmb = true; + Write (RUN_PASS, h, a | 1, L_LONG, WA); /* acquire interlock */ + c = Read (RUN_PASS, h + 4, L_LONG, RA) + h; /* c <- (h+4) + h */ + if (c & 07) { /* c quad aligned? */ + Write (RUN_PASS, h, a, L_LONG, WA); /* release interlock */ + RSVD_OPND_FAULT; /* fault */ + } + if (Test (RUN_PASS, c, WA, &t) < 0) /* wtst c, rls if err */ + Write (RUN_PASS, h, a, L_LONG, WA); + Write (RUN_PASS, c, d - c, L_LONG, WA); /* (c) <- d-c, flt ok */ + Write (RUN_PASS, d, h - d, L_LONG, WA); /* (d) <- h-d */ + Write (RUN_PASS, d + 4, c - d, L_LONG, WA); /* (d+4) <- c-d */ + Write (RUN_PASS, h + 4, d - h, L_LONG, WA); /* (h+4) <- d-h */ + Write (RUN_PASS, h, a, L_LONG, WA); /* release interlock */ + return 0; /* q >= 2 entries */ +} + +/* Interlocked remove instructions + + opnd[0] = header (hdr.aq) + opnd[1:2] = destination address (dst.al) + + Pictorially: + + BEFORE AFTER REMQHI AFTER REMQTI + + H: A-H H: B-H W H: A-H W for interlock + H+4: C-H H+4: C-H H+4: B-H W + + A: B-A A: B-A R A: B-A + A+4: H-A A+4: H-A A+4: H-A + + B: C-B B: C-B B: H-B W + B+4: A-B B+4: H-B W B+4: A-B + + C: H-C C: H-C C: H-C + C+4: B-C C+4: B-C C+4: B-C R + + Note that the queue header and all the entries that are + "touched" in any way must be QUADWORD aligned. In addition, + the header and the destination must not be equal. +*/ + +int32 op_remqhi (RUN_DECL, int32 *opnd, int32 acc) +{ + if (use_native_interlocked) + return op_remqhi_native(RUN_PASS, opnd, acc); + else + return op_remqhi_portable(RUN_PASS, opnd, acc); +} + +static int32 op_remqhi_native (RUN_DECL, int32 *opnd, int32 acc) +{ + int32 h = opnd[0]; + int32 pa_h1; + int32 ar, a, b = 0; /* init b to suppress false GCC warning */ + sim_try_volatile t_bool release = FALSE; + sim_try_volatile InterlockedOpLock iop(RUN_PASS, IOP_ILK); + + if (h & 07) /* h quad aligned? */ + RSVD_OPND_FAULT; + pa_h1 = TestMark (RUN_PASS, h, WA, NULL); + + /* must be in memory (not IO space) */ + if (! (ADDR_IS_MEM(pa_h1))) + RSVD_OPND_FAULT; + + if (opnd[1] < 0) /* mem destination? */ + { + if (h == opnd[2]) /* hdr = dst? */ + RSVD_OPND_FAULT; + Read (RUN_PASS, opnd[2], L_LONG, WA); /* wchk dst */ + } + + sim_try + { + /* elevate thread priority if required */ + iop.prio_lock(); + + /* acquire secondary interlock */ + smp_pre_interlocked_mb(); + if (smp_native_bb(M, pa_h1, 0, TRUE)) + { + iop.qxi_busy(); + smp_cpu_relax(); + return CC_C | CC_V; + } + + release = TRUE; + + ar = ReadLP(RUN_PASS, pa_h1) & ~1; /* ar <- (h) */ + if (ar & 06) /* a quad aligned? */ + RSVD_OPND_FAULT; + a = ar + h; /* abs addr of a */ + + if (ar) + { + b = Read (RUN_PASS, a, L_LONG, RA) + a; /* b <- (a)+a, flt ok */ + if (b & 07) /* b quad aligned? */ + RSVD_OPND_FAULT; /* fault */ + Write (RUN_PASS, b + 4, h - b, L_LONG, WA); /* (b+4) <- h-b, flt ok */ + + smp_interlocked_uint32* vpa_h1 = (smp_interlocked_uint32*) ((t_byte*) M + pa_h1); + while (! smp_interlocked_cas_done(vpa_h1, weak_read(*vpa_h1), (uint32) (b - h))) ; + smp_post_interlocked_mb(); + release = FALSE; + } + else + { + smp_native_bb(M, pa_h1, 0, FALSE); + smp_post_interlocked_mb(); + release = FALSE; + } + + if (opnd[1] >= 0) /* store result */ + R[opnd[1]] = a; + else + Write (RUN_PASS, opnd[2], a, L_LONG, WA); + + if (ar == 0) /* queue was empty */ + return CC_Z | CC_V; + + return (b == h) ? CC_Z : 0; /* if b = h, queue empty after removal */ + } + sim_catch_all + { + /* release secondary interlock and re-throw */ + if (release) + { + smp_native_bb(M, pa_h1, 0, FALSE); + smp_post_interlocked_mb(); + } + sim_rethrow; + } + sim_end_try + + sim_noreturn_int32; +} + +int32 op_remqhi_portable (RUN_DECL, int32 *opnd, int32 acc) +{ + sim_try_volatile InterlockedOpLock iop(RUN_PASS, IOP_ILK); + return op_remqhi_portable_2 (RUN_PASS, opnd, acc, &iop); +} + +static int32 op_remqhi_portable_2 (RUN_DECL, int32 *opnd, int32 acc, sim_try_volatile InterlockedOpLock* iop) +{ + int32 h = opnd[0]; + int32 ar, a, b = 0, t; /* init b to suppress false GCC warning */ + + if (h & 07) /* h quad aligned? */ + RSVD_OPND_FAULT; + if (opnd[1] < 0) /* mem destination? */ + { + if (h == opnd[2]) /* hdr = dst? */ + RSVD_OPND_FAULT; + Read (RUN_PASS, opnd[2], L_LONG, WA); /* wchk dst */ + } + iop->virt_lock(h, acc); + ar = Read (RUN_PASS, h, L_LONG, WA); /* ar <- (h) */ + if (ar & 06) /* a quad aligned? */ + RSVD_OPND_FAULT; + if (ar & 01) /* busy, cc = 0011 */ + { + iop->qxi_busy(); + return CC_V | CC_C; + } + a = ar + h; /* abs addr of a */ + if (ar) /* queue not empty? */ + { + iop->wmb = true; + Write (RUN_PASS, h, ar | 1, L_LONG, WA); /* acquire interlock */ + if (Test (RUN_PASS, a, RA, &t) < 0) /* read tst a */ + Write (RUN_PASS, h, ar, L_LONG, WA); /* release if error */ + b = Read (RUN_PASS, a, L_LONG, RA) + a; /* b <- (a)+a, flt ok */ + if (b & 07) { /* b quad aligned? */ + Write (RUN_PASS, h, ar, L_LONG, WA); /* release interlock */ + RSVD_OPND_FAULT; /* fault */ + } + /* since b is quad-aligned, b+4 is guaranteed to be in the same page as b */ + if (Test (RUN_PASS, b, WA, &t) < 0) /* write test b and b+4 */ + Write (RUN_PASS, h, ar, L_LONG, WA); /* release if err */ + Write (RUN_PASS, b + 4, h - b, L_LONG, WA); /* (b+4) <- h-b, flt ok */ + Write (RUN_PASS, h, b - h, L_LONG, WA); /* (h) <- b-h, rls int */ + } + if (opnd[1] >= 0) /* store result */ + R[opnd[1]] = a; + else + { + iop->wmb = true; + Write (RUN_PASS, opnd[2], a, L_LONG, WA); + } + if (ar == 0) /* empty, cc = 0110 */ + return CC_Z | CC_V; + return (b == h)? CC_Z: 0; /* if b = h, q empty */ +} + +int32 op_remqti (RUN_DECL, int32 *opnd, int32 acc) +{ + if (use_native_interlocked) + return op_remqti_native(RUN_PASS, opnd, acc); + else + return op_remqti_portable(RUN_PASS, opnd, acc); +} + +static int32 op_remqti_native (RUN_DECL, int32 *opnd, int32 acc) +{ + int32 h = opnd[0]; + int32 ar, b, c, rcc; + int32 pa_h1, pa_h2; + sim_try_volatile t_bool release = FALSE; + sim_try_volatile InterlockedOpLock iop(RUN_PASS, IOP_ILK); + + if (h & 07) /* h quad aligned? */ + RSVD_OPND_FAULT; + + /* + * get physical addresses; since "h" is quad-aligned, + * pa_h1 and pa_h2 are guaranteed to be in the same page + */ + pa_h1 = TestMark (RUN_PASS, h, WA, NULL); + pa_h2 = pa_h1 + 4; + + /* must be in memory (not IO space); + test for pa_h1 also covers pa_h2 too */ + if (! (ADDR_IS_MEM(pa_h1))) + RSVD_OPND_FAULT; + + if (opnd[1] < 0) /* mem destination? */ + { + if (h == opnd[2]) /* hdr = dst? */ + RSVD_OPND_FAULT; + Read (RUN_PASS, opnd[2], L_LONG, WA); /* wchk dst */ + } + + sim_try + { + /* elevate thread priority if required */ + iop.prio_lock(); + + /* acquire secondary interlock */ + smp_pre_interlocked_mb(); + if (smp_native_bb(M, pa_h1, 0, TRUE)) + { + iop.qxi_busy(); + smp_cpu_relax(); + return CC_C | CC_V; + } + + release = TRUE; + + ar = ReadLP(RUN_PASS, pa_h1) & ~1; /* ar <- (h) */ + if (ar & 06) /* a quad aligned? */ + RSVD_OPND_FAULT; + + if (ar) /* queue not empty */ + { + c = ReadLP (RUN_PASS, pa_h2); /* c <- (h+4) */ + if (ar == c) /* single entry ? */ + { + c = ar + h; /* result: abs addr of removed entry */ + WriteLP(RUN_PASS, pa_h2, 0); /* clear header, release interlock */ + smp_interlocked_uint32* vpa_h1 = (smp_interlocked_uint32*) ((t_byte*) M + pa_h1); + while (! smp_interlocked_cas_done(vpa_h1, weak_read(*vpa_h1), 0)) ; + smp_post_interlocked_mb(); + release = FALSE; + rcc = CC_Z; /* result code: queue is empty after removal */ + } + else + { + if (c & 07) /* c quad aligned? */ + RSVD_OPND_FAULT; /* fault */ + c = c + h; /* abs addr of c */ + b = Read (RUN_PASS, c + 4, L_LONG, RA) + c; /* b <- (c+4)+c */ + if (b & 07) /* b quad aligned? */ + RSVD_OPND_FAULT; /* fault */ + Write (RUN_PASS, b, h - b, L_LONG, WA); /* (b) <- h-b */ + WriteLP (RUN_PASS, pa_h2, b - h); /* (h+4) <- b-h */ + smp_native_bb(M, pa_h1, 0, FALSE); /* release interlock */ + smp_post_interlocked_mb(); + release = FALSE; + rcc = 0; /* result code: queue not empty after removal */ + } + } + else + { + smp_native_bb(M, pa_h1, 0, FALSE); /* release interlock */ + smp_post_interlocked_mb(); + release = FALSE; + c = h; /* result address */ + rcc = CC_Z | CC_V; /* result code: queue was empty */ + } + + if (opnd[1] >= 0) /* store result */ + R[opnd[1]] = c; + else + Write (RUN_PASS, opnd[2], c, L_LONG, WA); + + return rcc; + } + sim_catch_all + { + /* release secondary interlock and re-throw */ + if (release) + { + smp_native_bb(M, pa_h1, 0, FALSE); + smp_post_interlocked_mb(); + } + sim_rethrow; + } + sim_end_try + + sim_noreturn_int32; +} + +int32 op_remqti_portable (RUN_DECL, int32 *opnd, int32 acc) +{ + sim_try_volatile InterlockedOpLock iop(RUN_PASS, IOP_ILK); + + int32 h = opnd[0]; + int32 ar, b, c, t; + + if (h & 07) /* h quad aligned? */ + RSVD_OPND_FAULT; + if (opnd[1] < 0) { /* mem destination? */ + if (h == opnd[2]) /* hdr = dst? */ + RSVD_OPND_FAULT; + Read (RUN_PASS, opnd[2], L_LONG, WA); /* wchk dst */ + } + iop.virt_lock(h, acc); + ar = Read (RUN_PASS, h, L_LONG, WA); /* a <- (h) */ + if (ar & 06) /* a quad aligned? */ + RSVD_OPND_FAULT; + if (ar & 01) /* busy, cc = 0011 */ + { + iop.qxi_busy(); + return CC_V | CC_C; + } + if (ar) /* queue not empty */ + { + iop.wmb = true; + Write (RUN_PASS, h, ar | 1, L_LONG, WA); /* acquire interlock */ + c = Read (RUN_PASS, h + 4, L_LONG, RA); /* c <- (h+4) */ + if (ar == c) { /* single entry? */ + Write (RUN_PASS, h, ar, L_LONG, WA); /* release interlock */ + return op_remqhi_portable_2 (RUN_PASS, opnd, acc, &iop); /* treat as remqhi */ + } + if (c & 07) { /* c quad aligned? */ + Write (RUN_PASS, h, ar, L_LONG, WA); /* release interlock */ + RSVD_OPND_FAULT; /* fault */ + } + c = c + h; /* abs addr of c */ + if (Test (RUN_PASS, c + 4, RA, &t) < 0) /* read test c+4 */ + Write (RUN_PASS, h, ar, L_LONG, WA); /* release if error */ + b = Read (RUN_PASS, c + 4, L_LONG, RA) + c; /* b <- (c+4)+c, flt ok */ + if (b & 07) { /* b quad aligned? */ + Write (RUN_PASS, h, ar, L_LONG, WA); /* release interlock */ + RSVD_OPND_FAULT; /* fault */ + } + if (Test (RUN_PASS, b, WA, &t) < 0) /* write test b */ + Write (RUN_PASS, h, ar, L_LONG, WA); /* release if error */ + Write (RUN_PASS, b, h - b, L_LONG, WA); /* (b) <- h-b */ + Write (RUN_PASS, h + 4, b - h, L_LONG, WA); /* (h+4) <- b-h */ + Write (RUN_PASS, h, ar, L_LONG, WA); /* release interlock */ + } + else + c = h; /* empty, result = h */ + if (opnd[1] >= 0) /* store result */ + R[opnd[1]] = c; + else + { + iop.wmb = true; + Write (RUN_PASS, opnd[2], c, L_LONG, WA); + } + if (ar == 0) /* empty, cc = 0110 */ + return CC_Z | CC_V; + return 0; /* q can't be empty */ +} + + +/* String instructions */ + +#define MVC_FRWD 0 /* movc state codes */ +#define MVC_BACK 1 +#define MVC_FILL 3 /* must be 3 */ +#define MVC_M_STATE 3 +#define MVC_V_CC 2 + +/* MOVC3, MOVC5 + + if PSL = 0 and MOVC3, + opnd[0] = length + opnd[1] = source address + opnd[2] = dest address + + if PSL = 0 and MOVC5, + opnd[0] = source length + opnd[1] = source address + opnd[2] = fill + opnd[3] = dest length + opnd[4] = dest address + + if PSL = 1, + R0 = delta-PC/fill/initial move length + R1 = current source address + R2 = current move length + R3 = current dest address + R4 = dstlen - srclen (loop count if fill state) + R5 = cc/state +*/ + +int32 op_movc (RUN_DECL, int32 *opnd, int32 movc5, int32 acc) +{ +int32 i, cc, fill, wd; +int32 j, lnt, mlnt[3]; +static const int32 looplnt[3] = { L_BYTE, L_LONG, L_BYTE }; + +if (PSL & PSL_FPD) { /* FPD set? */ + SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ + fill = STR_GETCHR (R[0]); /* get fill */ + R[2] = R[2] & STR_LNMASK; /* mask lengths */ + if (R[4] > 0) + R[4] = R[4] & STR_LNMASK; + } +else { + R[1] = opnd[1]; /* src addr */ + if (movc5) { /* MOVC5? */ + R[2] = (opnd[0] < opnd[3])? opnd[0]: opnd[3]; + R[3] = opnd[4]; /* dst addr */ + R[4] = opnd[3] - opnd[0]; /* dstlen - srclen */ + fill = opnd[2]; /* set fill */ + CC_CMP_W (opnd[0], opnd[3]); /* set cc's */ + } + else { + R[2] = opnd[0]; /* mvlen = srclen */ + R[3] = opnd[2]; /* dst addr */ + R[4] = fill = 0; /* no fill */ + cc = CC_Z; /* set cc's */ + } + R[0] = STR_PACK (fill, R[2]); /* initial mvlen */ + if (R[2]) { /* any move? */ + if (((uint32) R[1]) < ((uint32) R[3])) { + R[1] = R[1] + R[2]; /* backward, adjust */ + R[3] = R[3] + R[2]; /* addr to end */ + R[5] = MVC_BACK; /* set state */ + } + else R[5] = MVC_FRWD; /* fwd, set state */ + } + else R[5] = MVC_FILL; /* fill, set state */ + R[5] = R[5] | (cc << MVC_V_CC); /* pack with state */ + PSL = PSL | PSL_FPD; /* set FPD */ + } + +/* At this point, + + R0 = delta PC'fill'initial move length + R1 = current src addr + R2 = current move length + R3 = current dst addr + R4 = dst length - src length + R5 = cc'state +*/ + +switch (R[5] & MVC_M_STATE) { /* case on state */ + + case MVC_FRWD: /* move forward */ + mlnt[0] = (4 - R[3]) & 3; /* length to align */ + if (mlnt[0] > R[2]) /* cant exceed total */ + mlnt[0] = R[2]; + mlnt[1] = (R[2] - mlnt[0]) & ~03; /* aligned length */ + mlnt[2] = R[2] - mlnt[0] - mlnt[1]; /* tail */ + for (i = 0; i < 3; i++) { /* head, align, tail */ + lnt = looplnt[i]; /* length for loop */ + for (j = 0; j < mlnt[i]; j = j + lnt, cpu_cycle()) { + wd = Read (RUN_PASS, R[1], lnt, RA); /* read src */ + Write (RUN_PASS, R[3], wd, lnt, WA); /* write dst */ + R[1] = R[1] + lnt; /* inc src addr */ + R[3] = R[3] + lnt; /* inc dst addr */ + R[2] = R[2] - lnt; /* dec move lnt */ + } + } + goto FILL; /* check for fill */ + + case MVC_BACK: /* move backward */ + mlnt[0] = R[3] & 03; /* length to align */ + if (mlnt[0] > R[2]) /* cant exceed total */ + mlnt[0] = R[2]; + mlnt[1] = (R[2] - mlnt[0]) & ~03; /* aligned length */ + mlnt[2] = R[2] - mlnt[0] - mlnt[1]; /* tail */ + for (i = 0; i < 3; i++) { /* head, align, tail */ + lnt = looplnt[i]; /* length for loop */ + for (j = 0; j < mlnt[i]; j = j + lnt, cpu_cycle()) { + wd = Read (RUN_PASS, R[1] - lnt, lnt, RA); /* read src */ + Write (RUN_PASS, R[3] - lnt, wd, lnt, WA); /* write dst */ + R[1] = R[1] - lnt; /* dec src addr */ + R[3] = R[3] - lnt; /* dec dst addr */ + R[2] = R[2] - lnt; /* dec move lnt */ + } + } + R[1] = R[1] + (R[0] & STR_LNMASK); /* final src addr */ + R[3] = R[3] + (R[0] & STR_LNMASK); /* final dst addr */ + + case MVC_FILL: /* fill */ + FILL: + if (R[4] <= 0) /* any fill? */ + break; + R[5] = R[5] | MVC_FILL; /* set state */ + mlnt[0] = (4 - R[3]) & 3; /* length to align */ + if (mlnt[0] > R[4]) /* cant exceed total */ + mlnt[0] = R[4]; + mlnt[1] = (R[4] - mlnt[0]) & ~03; /* aligned length */ + mlnt[2] = R[4] - mlnt[0] - mlnt[1]; /* tail */ + for (i = 0; i < 3; i++) { /* head, align, tail */ + lnt = looplnt[i]; /* length for loop */ + fill = fill & BMASK; /* fill for loop */ + if (lnt == L_LONG) + fill = (((uint32) fill) << 24) | (fill << 16) | (fill << 8) | fill; + for (j = 0; j < mlnt[i]; j = j + lnt, cpu_cycle()) { + Write (RUN_PASS, R[3], fill, lnt, WA); /* write fill */ + R[3] = R[3] + lnt; /* inc dst addr */ + R[4] = R[4] - lnt; /* dec fill lnt */ + } + } + break; + + default: /* bad state */ + RSVD_OPND_FAULT; /* you lose */ + } + +PSL = PSL & ~PSL_FPD; /* clear FPD */ +cc = (R[5] >> MVC_V_CC) & CC_MASK; /* get cc's */ +R[0] = NEG (R[4]); /* set R0 */ +R[2] = R[4] = R[5] = 0; /* clear reg */ +return cc; +} + +/* CMPC3, CMPC5 + + if PSL = 0 and CMPC3, + opnd[0] = length + opnd[1] = source1 address + opnd[2] = source2 address + + if PSL = 0 and CMPC5, + opnd[0] = source1 length + opnd[1] = source1 address + opnd[2] = fill + opnd[3] = source2 length + opnd[4] = source2 address + + if PSL = 1, + R0 = delta-PC/fill/source1 length + R1 = source1 address + R2 = source2 length + R3 = source2 address +*/ + +int32 op_cmpc (RUN_DECL, int32 *opnd, int32 cmpc5, int32 acc) +{ +int32 cc, s1, s2, fill; + +if (PSL & PSL_FPD) { /* FPD set? */ + SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ + fill = STR_GETCHR (R[0]); /* get fill */ + } +else { + R[1] = opnd[1]; /* src1len */ + if (cmpc5) { /* CMPC5? */ + R[2] = opnd[3]; /* get src2 opnds */ + R[3] = opnd[4]; + fill = opnd[2]; + } + else { + R[2] = opnd[0]; /* src2len = src1len */ + R[3] = opnd[2]; + fill = 0; + } + R[0] = STR_PACK (fill, opnd[0]); /* src1len + FPD data */ + PSL = PSL | PSL_FPD; + } +R[2] = R[2] & STR_LNMASK; /* mask src2len */ +for (s1 = s2 = 0; ((R[0] | R[2]) & STR_LNMASK) != 0; cpu_cycle()) { + if (R[0] & STR_LNMASK) /* src1? read */ + s1 = Read (RUN_PASS, R[1], L_BYTE, RA); + else s1 = fill; /* no, use fill */ + if (R[2]) /* src2? read */ + s2 = Read (RUN_PASS, R[3], L_BYTE, RA); + else s2 = fill; /* no, use fill */ + if (s1 != s2) /* src1 = src2? */ + break; + if (R[0] & STR_LNMASK) { /* if src1, decr */ + R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - 1) & STR_LNMASK); + R[1] = R[1] + 1; + } + if (R[2]) { /* if src2, decr */ + R[2] = (R[2] - 1) & STR_LNMASK; + R[3] = R[3] + 1; + } + } +PSL = PSL & ~PSL_FPD; /* clear FPD */ +CC_CMP_B (s1, s2); /* set cc's */ +R[0] = R[0] & STR_LNMASK; /* clear packup */ +return cc; +} + +/* LOCC, SKPC + + if PSL = 0, + opnd[0] = match character + opnd[1] = source length + opnd[2] = source address + + if PSL = 1, + R0 = delta-PC/match/source length + R1 = source address +*/ + +int32 op_locskp (RUN_DECL, int32 *opnd, int32 skpc, int32 acc) +{ +int32 c, match; + +if (PSL & PSL_FPD) { /* FPD set? */ + SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ + match = STR_GETCHR (R[0]); /* get match char */ + } +else { + match = opnd[0]; /* get operands */ + R[0] = STR_PACK (match, opnd[1]); /* src len + FPD data */ + R[1] = opnd[2]; /* src addr */ + PSL = PSL | PSL_FPD; + } +for ( ; (R[0] & STR_LNMASK) != 0; cpu_cycle() ) { /* loop thru string */ + c = Read (RUN_PASS, R[1], L_BYTE, RA); /* get src byte */ + if ((c == match) ^ skpc) /* match & locc? */ + break; + R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - 1) & STR_LNMASK); + R[1] = R[1] + 1; /* incr src1adr */ + } +PSL = PSL & ~PSL_FPD; /* clear FPD */ +R[0] = R[0] & STR_LNMASK; /* clear packup */ +return (R[0]? 0: CC_Z); /* set cc's */ +} + +/* SCANC, SPANC + + if PSL = 0, + opnd[0] = source length + opnd[1] = source address + opnd[2] = table address + opnd[3] = mask + + if PSL = 1, + R0 = delta-PC/char/source length + R1 = source address + R3 = table address +*/ + +int32 op_scnspn (RUN_DECL, int32 *opnd, int32 spanc, int32 acc) +{ +int32 c, t, mask; + +if (PSL & PSL_FPD) { /* FPD set? */ + SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ + mask = STR_GETCHR (R[0]); /* get mask */ + } +else { + R[1] = opnd[1]; /* src addr */ + R[3] = opnd[2]; /* tblad */ + mask = opnd[3]; /* mask */ + R[0] = STR_PACK (mask, opnd[0]); /* srclen + FPD data */ + PSL = PSL | PSL_FPD; + } +for ( ; (R[0] & STR_LNMASK) != 0; cpu_cycle() ) { /* loop thru string */ + c = Read (RUN_PASS, R[1], L_BYTE, RA); /* get byte */ + t = Read (RUN_PASS, R[3] + c, L_BYTE, RA); /* get table ent */ + if (((t & mask) != 0) ^ spanc) /* test vs instr */ + break; + R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - 1) & STR_LNMASK); + R[1] = R[1] + 1; + } +PSL = PSL & ~PSL_FPD; +R[0] = R[0] & STR_LNMASK; /* clear packup */ +R[2] = 0; +return (R[0]? 0: CC_Z); +} + +/* Operating system interfaces */ + +/* Interrupt or exception + + vec = SCB vector + cc = condition codes + ipl = new IPL if interrupt + ei = -1: severe exception + 0: normal exception + 1: interrupt +*/ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); + +int32 intexc (RUN_DECL, int32 vec, int32 cc, int32 ipl, int ei) +{ + int32 oldpsl = PSL | cc; + int32 oldcur = PSL_GETCUR (oldpsl); + int32 oldsp = SP; + int32 newpsl; + int32 newpc; + int32 acc; + +#if 0 + if (FALSE && oldcur == 0 && vec != SCB_MCHK && ei != IE_INT && mapen != 0) + { + SMP_FILE* fd = smp_fopen("kmode-exc.log", "a"); + fprintf(fd, "\n"); + fprintf(fd, "***\n"); + fprintf(fd, "*** Kernel mode exception at 0x%08X\n", PC); + fprintf(fd, "***\n"); + fprintf(fd, "\n"); + cpu_show_hist(fd, cpu_unit, 0, NULL); + cpu_set_hist(cpu_unit, 0, NULL, NULL); + fclose(fd); + } +#endif + + in_ie = 1; /* flag int/exc */ + CLR_TRAPS; /* clear traps */ + + newpc = ReadLP (RUN_PASS, (SCBB + vec) & (PAMASK & ~3)); /* read new PC */ + if (newpc & 2) /* bad flags? */ + ABORT (STOP_ILLVEC); + if (ei == IE_SVE) /* severe? on istk */ + newpc = newpc | 1; + + if (oldpsl & PSL_IS) /* on int stk? */ + { + newpsl = PSL_IS; + } + else + { + STK[oldcur] = SP; /* no, save cur stk */ + if (newpc & 1) /* to int stk? */ + { + newpsl = PSL_IS; /* flag */ + SP = IS; /* new stack */ + } + else + { + newpsl = 0; /* to ker stk */ + SP = KSP; /* new stack */ + } + } + + if (ei == IE_INT) /* if int, new IPL */ + { + if (VAX_QBUS && vec >= VEC_Q) + newpsl |= PSL_IPL17; + else + newpsl |= ipl << PSL_V_IPL; + } + else /* exception or severe exception */ + { + if (newpc & 1) + { + newpsl |= PSL_IPL1F; + } + else + { + newpsl |= oldpsl & PSL_IPL; + } + newpsl |= oldcur << PSL_V_PRV; + } + + PSL = newpsl; + + 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); + + /* + * O/S virtualization assistance. + * Raises thread priority level if required and performs synchronization window management. + * For IE_INT thread priority re-evaluation had already been performed in a preeceding SET_IRQL + * and will be performed again in SET_IRQL when intexc returns. + */ + if (ei == IE_INT) + cpu_on_changed_ipl(RUN_PASS, oldpsl, CHIPL_NO_THRDPRIO); + else + cpu_on_changed_ipl(RUN_PASS, oldpsl, 0); + + acc = ACC_MASK (KERN); /* new mode is kernel */ + Write (RUN_PASS, SP - 4, oldpsl, L_LONG, WA); /* push old PSL */ + Write (RUN_PASS, SP - 8, PC, L_LONG, WA); /* push old PC */ + SP = SP - 8; /* update stk ptr */ + JUMP (newpc & ~3); /* change PC */ + in_ie = 0; /* out of flows */ + return 0; +} + +/* CHMK, CHME, CHMS, CHMU + + opnd[0] = operand +*/ + +int32 op_chm (RUN_DECL, int32 *opnd, int32 cc, int32 opc) +{ +int32 mode = opc & PSL_M_MODE; +int32 cur = PSL_GETCUR (PSL); +int32 tsp, newpc, acc, sta; + +if (PSL & PSL_IS) + ABORT (STOP_CHMFI); +newpc = ReadLP (RUN_PASS, (SCBB + SCB_CHMK + (mode << 2)) & PAMASK); +if (cur < mode) /* only inward */ + mode = cur; +STK[cur] = SP; /* save stack */ +tsp = STK[mode]; /* get new stk */ +acc = ACC_MASK (mode); /* set new mode */ +if (Test (RUN_PASS, fault_p2 = tsp - 1, WA, &sta) < 0) { /* probe stk */ + fault_p1 = MM_WRITE | (sta & MM_EMASK); + ABORT ((sta & 4)? ABORT_TNV: ABORT_ACV); + } +if (Test (RUN_PASS, fault_p2 = tsp - 12, WA, &sta) < 0) { + fault_p1 = MM_WRITE | (sta & MM_EMASK); + ABORT ((sta & 4)? ABORT_TNV: ABORT_ACV); + } +Write (RUN_PASS, tsp - 12, SXTW (opnd[0]), L_LONG, WA); /* push argument */ +Write (RUN_PASS, tsp - 8, PC, L_LONG, WA); /* push PC */ +Write (RUN_PASS, tsp - 4, PSL | cc, L_LONG, WA); /* push PSL */ +SP = tsp - 12; /* set new stk */ +PSL = (mode << PSL_V_CUR) | (PSL & PSL_IPL) | /* set new PSL */ + (cur << PSL_V_PRV); /* IPL unchanged */ +// last_chm = fault_PC; +JUMP (newpc & ~03); /* set new PC */ +return 0; /* cc = 0 */ +} + +/* REI - return from exception or interrupt + +The lengthiest part of the REI instruction is the validity checking of the PSL +popped off the stack. The new PSL is checked against the following eight rules: + +let tmp = new PSL popped off the stack +let PSL = current PSL + +Rule SRM formulation Comment +---- --------------- ------- + 1 tmp<25:24> GEQ PSL<25:24> tmp GEQ PSL + 2 tmp<26> LEQ PSL<26> tmp LEQ PSL + 3 tmp<26> = 1 => tmp<25:24> = 0 tmp = 1 => tmp = ker + 4 tmp<26> = 1 => tmp<20:16> > 0 tmp = 1 => tmp > 0 + 5 tmp<20:16> > 0 => tmp<25:24> = 0 tmp > 0 => tmp = ker + 6 tmp<25:24> LEQ tmp<23:22> tmp LEQ tmp + 7 tmp<20:16> LEQ PSL<20:16> tmp LEQ PSL + 8 tmp<31,29:28,21,15:8> = 0 tmp = 0 + 9 tmp<31> = 1 => tmp = 3, tmp = 3>, tmp = 0 +*/ + +int32 op_rei (RUN_DECL, int32 acc) +{ + int32 oldpsl = PSL; + int32 oldpc = PC; + int32 newpc = Read (RUN_PASS, SP, L_LONG, RA); + int32 newpsl = Read (RUN_PASS, SP + 4, L_LONG, RA); + int32 newcur = PSL_GETCUR (newpsl); + int32 oldcur = PSL_GETCUR (PSL); + int32 newipl, i; + + if ((newpsl & PSL_MBZ) || /* rule 8 */ + (newcur < oldcur)) /* rule 1 */ + RSVD_OPND_FAULT; + + newipl = PSL_GETIPL (newpsl); /* get new ipl */ + + if (newcur) /* to esu, skip 2,4,7 */ + { + if ((newpsl & (PSL_IS | PSL_IPL)) || /* rules 3,5 */ + (newcur > PSL_GETPRV (newpsl))) /* rule 6 */ + RSVD_OPND_FAULT; /* end rei to esu */ + } + else /* to k, skip 3,5,6 */ + { + if ((newpsl & PSL_IS) && /* setting IS? */ + (((PSL & PSL_IS) == 0) || (newipl == 0))) /* test rules 2,4 */ + RSVD_OPND_FAULT; /* else skip 2,4 */ + if (newipl > PSL_GETIPL (PSL)) /* test rule 7 */ + RSVD_OPND_FAULT; + } /* end if kernel */ + + if (newpsl & PSL_CM) /* setting cmode? */ + { + if (BadCmPSL (RUN_PASS, newpsl)) /* validate PSL */ + RSVD_OPND_FAULT; + for (i = 0; i < 7; i++) /* mask R0-R6, PC */ + R[i] = R[i] & WMASK; + newpc = newpc & WMASK; + } + + SP = SP + 8; /* pop stack */ + if (PSL & PSL_IS) /* save stack */ + IS = SP; + 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 = (PSL & PSL_TP) | (newpsl & ~CC_MASK); /* set PSL */ + + if (PSL & PSL_IS) /* set new stack */ + { + SP = IS; + } + else + { + SP = STK[newcur]; /* if ~IS, chk AST */ + if (newcur >= ASTLVL) + { + if (DEBUG_PRI (cpu_dev, LOG_CPU_R)) + fprintf (sim_deb, ">>REI: AST delivered\n"); + SISR = SISR | SISR_2; + } + } + JUMP (newpc); /* set new PC */ + + /* + * When dropping IPL below CLK/IPI level, reset state flags indicating we are in CLK/IPI ISR. + * If CLK or IPI interrupts are pending in cpu_intreg, SET_IRQL below will reinstate the flag(s) to TRUE. + * SET_IRQL will also perform thread prority adjustment according to new state, accounting both for + * leaving CLK/IPI ISR and for any pending CLK/IPI IRQs. + */ + if (newipl < IPL_ABS_CLK) + cpu_unit->cpu_active_clk_interrupt = FALSE; + if (newipl < IPL_ABS_IPINTR) + cpu_unit->cpu_active_ipi_interrupt = FALSE; + + /* + * Check if this is console ROM's CONTNUE REI + */ + if (unlikely(oldpc == ROM_PC_CONTINUE_REI) && cpu_unit->cpu_con_rei_on && + mapen == 1 && CPU_CURRENT_CYCLES == cpu_unit->cpu_con_rei) + { + /* returning from console mode to OS: reenter synchronization window if required */ + syncw_enable_cpu(RUN_PASS); + syncw_reeval_sys(RUN_PASS); + + /* thread priorty reevaluation will be performed by SET_IRQL below */ + cpu_on_changed_ipl(RUN_PASS, oldpsl, CHIPL_NO_THRDPRIO | CHIPL_NO_SYNCW); + } + else + { + /* thread priorty reevaluation will be performed by SET_IRQL below */ + cpu_on_changed_ipl(RUN_PASS, oldpsl, CHIPL_NO_THRDPRIO); + } + cpu_unit->cpu_con_rei_on = FALSE; + + SET_IRQL; /* update intreq */ + + return newpsl & CC_MASK; /* set new cc */ +} + +/* LDCPTX - load process context */ + +void op_ldpctx (RUN_DECL, int32 acc) +{ + int32 newpc, newpsl, pcbpa, t; + + if (PSL & PSL_CUR) /* must be kernel */ + RSVD_INST_FAULT; + + syncw_leave_ilk(RUN_PASS); /* leave ILK synchronization window */ + + pcbpa = PCBB & PAMASK; /* phys address */ + + KSP = ReadLP (RUN_PASS, pcbpa); /* restore stk ptrs */ + ESP = ReadLP (RUN_PASS, pcbpa + 4); + SSP = ReadLP (RUN_PASS, pcbpa + 8); + USP = ReadLP (RUN_PASS, pcbpa + 12); + R[0] = ReadLP (RUN_PASS, pcbpa + 16); /* restore registers */ + R[1] = ReadLP (RUN_PASS, pcbpa + 20); + R[2] = ReadLP (RUN_PASS, pcbpa + 24); + R[3] = ReadLP (RUN_PASS, pcbpa + 28); + R[4] = ReadLP (RUN_PASS, pcbpa + 32); + R[5] = ReadLP (RUN_PASS, pcbpa + 36); + R[6] = ReadLP (RUN_PASS, pcbpa + 40); + R[7] = ReadLP (RUN_PASS, pcbpa + 44); + R[8] = ReadLP (RUN_PASS, pcbpa + 48); + R[9] = ReadLP (RUN_PASS, pcbpa + 52); + R[10] = ReadLP (RUN_PASS, pcbpa + 56); + R[11] = ReadLP (RUN_PASS, pcbpa + 60); + R[12] = ReadLP (RUN_PASS, pcbpa + 64); + R[13] = ReadLP (RUN_PASS, pcbpa + 68); + newpc = ReadLP (RUN_PASS, pcbpa + 72); /* get PC, PSL */ + newpsl = ReadLP (RUN_PASS, pcbpa + 76); + + t = ReadLP (RUN_PASS, pcbpa + 80); + ML_PXBR_TEST (t); /* validate P0BR */ + P0BR = t & BR_MASK; /* restore P0BR */ + t = ReadLP (RUN_PASS, pcbpa + 84); + LP_MBZ84_TEST (t); /* test mbz */ + ML_LR_TEST (t & LR_MASK); /* validate P0LR */ + P0LR = t & LR_MASK; /* restore P0LR */ + t = (t >> 24) & AST_MASK; + LP_AST_TEST (t); /* validate AST */ + ASTLVL = t; /* restore AST */ + t = ReadLP (RUN_PASS, pcbpa + 88); + ML_PXBR_TEST (t + 0x800000); /* validate P1BR */ + P1BR = t & BR_MASK; /* restore P1BR */ + t = ReadLP (RUN_PASS, pcbpa + 92); + LP_MBZ92_TEST (t); /* test MBZ */ + ML_LR_TEST (t & LR_MASK); /* validate P1LR */ + P1LR = t & LR_MASK; /* restore P1LR */ + pme = (t >> 31) & 1; /* restore PME */ + + zap_tb (RUN_PASS, 0); /* clear process TB */ + set_map_reg (RUN_PASS); + if (DEBUG_PRI (cpu_dev, LOG_CPU_P)) + fprintf (sim_deb, ">>LDP: PC=%08x, PSL=%08x, SP=%08x, nPC=%08x, nPSL=%08x, nSP=%08x\n", + PC, PSL, SP, newpc, newpsl, KSP); + if (PSL & PSL_IS) /* if istk, */ + IS = SP; + PSL = PSL & ~PSL_IS; /* switch to kstk */ + SP = KSP - 8; + Write (RUN_PASS, SP, newpc, L_LONG, WA); /* push PC, PSL */ + Write (RUN_PASS, SP + 4, newpsl, L_LONG, WA); +} + +/* SVPCTX - save processor context */ + +void op_svpctx (RUN_DECL, int32 acc) +{ + int32 savpc, savpsl, pcbpa; + + if (PSL & PSL_CUR) /* must be kernel */ + RSVD_INST_FAULT; + + syncw_leave_ilk(RUN_PASS); /* leave ILK synchronization window */ + + savpc = Read (RUN_PASS, SP, L_LONG, RA); /* pop PC, PSL */ + savpsl = Read (RUN_PASS, SP + 4, L_LONG, RA); + + if (DEBUG_PRI (cpu_dev, LOG_CPU_P)) + fprintf (sim_deb, ">>SVP: PC=%08x, PSL=%08x, SP=%08x, oPC=%08x, oPSL=%08x\n", + PC, PSL, SP, savpc, savpsl); + + if (PSL & PSL_IS) /* int stack? */ + SP = SP + 8; + else + { + KSP = SP + 8; /* pop kernel stack */ + SP = IS; /* switch to int stk */ + if ((PSL & PSL_IPL) == 0) /* make IPL > 0 */ + PSL = PSL | PSL_IPL1; + PSL = PSL | PSL_IS; /* set PSL */ + } + + pcbpa = PCBB & PAMASK; + WriteLP (RUN_PASS, pcbpa, KSP); /* save stk ptrs */ + WriteLP (RUN_PASS, pcbpa + 4, ESP); + WriteLP (RUN_PASS, pcbpa + 8, SSP); + WriteLP (RUN_PASS, pcbpa + 12, USP); + WriteLP (RUN_PASS, pcbpa + 16, R[0]); /* save registers */ + WriteLP (RUN_PASS, pcbpa + 20, R[1]); + WriteLP (RUN_PASS, pcbpa + 24, R[2]); + WriteLP (RUN_PASS, pcbpa + 28, R[3]); + WriteLP (RUN_PASS, pcbpa + 32, R[4]); + WriteLP (RUN_PASS, pcbpa + 36, R[5]); + WriteLP (RUN_PASS, pcbpa + 40, R[6]); + WriteLP (RUN_PASS, pcbpa + 44, R[7]); + WriteLP (RUN_PASS, pcbpa + 48, R[8]); + WriteLP (RUN_PASS, pcbpa + 52, R[9]); + WriteLP (RUN_PASS, pcbpa + 56, R[10]); + WriteLP (RUN_PASS, pcbpa + 60, R[11]); + WriteLP (RUN_PASS, pcbpa + 64, R[12]); + WriteLP (RUN_PASS, pcbpa + 68, R[13]); + WriteLP (RUN_PASS, pcbpa + 72, savpc); /* save PC, PSL */ + WriteLP (RUN_PASS, pcbpa + 76, savpsl); +} + +/* PROBER and PROBEW + + opnd[0] = mode + opnd[1] = length + opnd[2] = base address +*/ + +int32 op_probe (RUN_DECL, int32 *opnd, int32 rw) +{ +int32 mode = opnd[0] & PSL_M_MODE; /* mask mode */ +int32 length = opnd[1]; +int32 ba = opnd[2]; +int32 prv = PSL_GETPRV (PSL); +int32 acc, sta, sta1; + +if (prv > mode) /* maximize mode */ + mode = prv; +acc = ACC_MASK (mode) << (rw? TLB_V_WACC: 0); /* set acc mask */ +Test (RUN_PASS, ba, acc, &sta); /* probe */ +switch (sta) { /* case on status */ + + case PR_PTNV: /* pte TNV */ + fault_p1 = MM_PARAM (rw, PR_PTNV); + fault_p2 = ba; + ABORT (ABORT_TNV); /* force TNV */ + + case PR_TNV: case PR_OK: /* TNV or ok */ + break; /* continue */ + + default: /* other */ + return CC_Z; /* lose */ + } + +Test (RUN_PASS, ba + length - 1, acc, &sta1); /* probe end addr */ +switch (sta1) { /* case on status */ + + case PR_PTNV: /* pte TNV */ + fault_p1 = MM_PARAM (rw, PR_PTNV); + fault_p2 = ba + length - 1; + ABORT (ABORT_TNV); /* force TNV */ + + case PR_TNV: case PR_OK: /* TNV or ok */ + break; /* win */ + + default: /* other */ + return CC_Z; /* lose */ + } + +return 0; +} + +/* MTPR - move to processor register + + opnd[0] = data + opnd[1] = register number +*/ + +int32 op_mtpr (RUN_DECL, int32 *opnd) +{ + int32 val = opnd[0]; + int32 prn = opnd[1]; + int32 cc; + t_bool set_irql = TRUE; + t_bool keep_prefetch = FALSE; + + if (prn == MT_SIMH) + { + /* + * SIMH API call by guest. + * This register can be written in user mode for QUERY API only. + * Other SIMH API calls will require kernel mode. + */ + op_mtpr_simh(RUN_PASS, val); + CC_IIZZ_L (val); /* set cc's */ + SET_IRQL; /* update intreq */ + return cc; + } + + if (PSL & PSL_CUR) /* must be kernel */ + RSVD_INST_FAULT; + + if (prn > 63) /* reg# > 63? fault */ + RSVD_OPND_FAULT; + + CC_IIZZ_L (val); /* set cc's */ + + switch (prn) /* case on reg # */ + { + case MT_KSP: /* KSP */ + if (PSL & PSL_IS) /* on IS? store KSP */ + KSP = val; + else + SP = val; /* else store SP */ + set_irql = FALSE; + break; + + case MT_ESP: case MT_SSP: case MT_USP: /* ESP, SSP, USP */ + STK[prn] = val; /* store stack */ + set_irql = FALSE; + break; + + case MT_IS: /* IS */ + if (PSL & PSL_IS) /* on IS? store SP */ + SP = val; + else + IS = val; /* else store IS */ + set_irql = FALSE; + break; + + case MT_P0BR: /* P0BR */ + ML_PXBR_TEST (val); /* validate */ + P0BR = val & BR_MASK; /* lw aligned */ + zap_tb (RUN_PASS, 0); /* clr proc TLB */ + set_map_reg (RUN_PASS); + break; + + case MT_P0LR: /* P0LR */ + ML_LR_TEST (val & LR_MASK); /* validate */ + P0LR = val & LR_MASK; + zap_tb (RUN_PASS, 0); /* clr proc TLB */ + set_map_reg (RUN_PASS); + break; + + case MT_P1BR: /* P1BR */ + ML_PXBR_TEST (val + 0x800000); /* validate */ + P1BR = val & BR_MASK; /* lw aligned */ + zap_tb (RUN_PASS, 0); /* clr proc TLB */ + set_map_reg (RUN_PASS); + break; + + case MT_P1LR: /* P1LR */ + ML_LR_TEST (val & LR_MASK); /* validate */ + P1LR = val & LR_MASK; + zap_tb (RUN_PASS, 0); /* clr proc TLB */ + set_map_reg (RUN_PASS); + break; + + case MT_SBR: /* SBR */ + ML_SBR_TEST (val); /* validate */ + SBR = val & BR_MASK; /* lw aligned */ + zap_tb (RUN_PASS, 1); /* clr entire TLB */ + set_map_reg (RUN_PASS); + break; + + case MT_SLR: /* SLR */ + ML_LR_TEST (val & LR_MASK); /* validate */ + SLR = val & LR_MASK; + zap_tb (RUN_PASS, 1); /* clr entire TLB */ + set_map_reg (RUN_PASS); + break; + + case MT_WHAMI: /* WHAMI */ + WHAMI = val; + set_irql = FALSE; + break; + + case MT_SCBB: /* SCBB */ + ML_PA_TEST (val); /* validate */ + SCBB = val & BR_MASK; /* lw aligned */ + /* set auxiliary variable for fast checks PA_MAY_BE_INSIDE_SCB() */ + cpu_unit->cpu_context.scb_range_pamask = (uint32) SCBB & SCB_RANGE_PAMASK; + break; + + case MT_PCBB: /* PCBB */ + ML_PA_TEST (val); /* validate */ + PCBB = val & BR_MASK; /* lw aligned */ + set_irql = FALSE; + break; + + case MT_IPL: /* IPL */ + { + int32 newipl = val & PSL_M_IPL; + + if (newipl != PSL_GETIPL(PSL)) + { + int32 oldpsl = PSL; + PSL = (PSL & ~PSL_IPL) | (newipl << PSL_V_IPL); + + /* + * When dropping IPL below CLK/IPI level, reset state flags indicating we are in CLK/IPI ISR. + * If CLK or IPI interrupts are pending in cpu_intreg, SET_IRQL below will reinstate the flag(s) to TRUE. + * SET_IRQL will also perform thread prority adjustment according to new state, accounting both for + * leaving CLK/IPI ISR and for any pending CLK/IPI IRQs. + */ + if (newipl < IPL_ABS_CLK) + cpu_unit->cpu_active_clk_interrupt = FALSE; + if (newipl < IPL_ABS_IPINTR) + cpu_unit->cpu_active_ipi_interrupt = FALSE; + + /* thread priorty reevaluation will be performed by SET_IRQL below */ + cpu_on_changed_ipl(RUN_PASS, oldpsl, CHIPL_NO_THRDPRIO); + } + else + { + /* IPL had not changed: no-op */ + set_irql = FALSE; + } + } + break; + + case MT_ASTLVL: /* ASTLVL */ + if (val > AST_MAX) /* > 4? fault */ + RSVD_OPND_FAULT; + ASTLVL = val; + break; + + case MT_SIRR: /* SIRR */ + if (val > 0xF || val == 0) + RSVD_OPND_FAULT; + SISR = SISR | (1 << val); /* set bit in SISR */ + break; + + case MT_SISR: /* SISR */ + SISR = val & SISR_MASK; + break; + + case MT_MAPEN: /* MAPEN */ + { + int32 old_mapen = mapen; + mapen = val & 1; + if (mapen == 0) + { + cpu_on_clear_mapen(RUN_PASS); + } + else + { + if (old_mapen == 0 && PC == ROM_PC_CONTINUE_MAPEN) + { + /* console ROM executing the CONTINUE command */ + cpu_on_rom_continue(RUN_PASS); + cpu_unit->cpu_con_rei = CPU_CURRENT_CYCLES + 1; + cpu_unit->cpu_con_rei_on = TRUE; + } + + cpu_reevaluate_thread_priority(RUN_PASS); + + /* + * Console ROM code contains sequence + * + * MTPR #1, #MT_MAPEN + * REI + * + * REI is fetched thanks to physical prefetch since no virtual mapping for ROM code exists. + * + * Similar sequences relying on physical prefetch going on after enabling MAPEN + * rather than using double mapping may exist as well in some operating systems + * bootstrap code and in some standalone software. + * + * Therefore it is crucial that prefetch is not reset at this point. + */ + keep_prefetch = TRUE; + } + } + /* fall through */ + case MT_TBIA: /* TBIA */ + zap_tb (RUN_PASS, 1, keep_prefetch); /* clr entire TLB */ + set_irql = FALSE; + break; + + case MT_TBIS: /* TBIS */ + zap_tb_ent (RUN_PASS, val); + set_irql = FALSE; + break; + + case MT_TBCHK: /* TBCHK */ + if (chk_tb_ent (RUN_PASS, val)) + cc = cc | CC_V; + set_irql = FALSE; + break; + + case MT_PME: /* PME */ + pme = val & 1; + break; + + default: + WriteIPR (RUN_PASS, prn, val, set_irql); /* others */ + break; + } + + if (set_irql) + { + SET_IRQL; /* update intreq */ + } + + return cc; +} + +int32 op_mfpr (RUN_DECL, int32 *opnd) +{ +int32 prn = opnd[0]; +int32 val; + +if (PSL & PSL_CUR) /* must be kernel */ + RSVD_INST_FAULT; +if (prn > 63) /* reg# > 63? fault */ + RSVD_OPND_FAULT; +switch (prn) { /* case on reg# */ + + case MT_KSP: /* KSP */ + val = (PSL & PSL_IS)? KSP: SP; /* return KSP or SP */ + break; + + case MT_ESP: case MT_SSP: case MT_USP: /* ESP, SSP, USP */ + val = STK[prn]; /* return stk ptr */ + break; + + case MT_IS: /* IS */ + val = (PSL & PSL_IS)? SP: IS; /* return SP or IS */ + break; + + case MT_P0BR: /* P0BR */ + val = P0BR; + break; + + case MT_P0LR: /* P0LR */ + val = P0LR; + break; + + case MT_P1BR: /* P1BR */ + val = P1BR; + break; + + case MT_P1LR: /* P1LR */ + val = P1LR; + break; + + case MT_SBR: /* SBR */ + val = SBR; + break; + + case MT_SLR: /* SLR */ + val = SLR; + break; + + case MT_CPUID: /* CPUID */ + val = ((int32) cpu_unit->cpu_id) & 0xFF; + break; + + case MT_WHAMI: /* WHAMI */ + val = WHAMI; + break; + + case MT_SCBB: /* SCBB */ + val = SCBB; + break; + + case MT_PCBB: /* PCBB */ + val = PCBB; + break; + + case MT_IPL: /* IPL */ + val = PSL_GETIPL (PSL); + break; + + case MT_ASTLVL: /* ASTLVL */ + val = ASTLVL; + break; + + case MT_SISR: /* SISR */ + val = SISR & SISR_MASK; + break; + + case MT_MAPEN: /* MAPEN */ + val = mapen & 1; + break; + + case MT_PME: + val = pme & 1; + break; + + case MT_SIRR: + case MT_TBIA: + case MT_TBIS: + case MT_TBCHK: + RSVD_OPND_FAULT; /* write only */ + + default: /* others */ + val = ReadIPR (RUN_PASS, prn); /* read from SSC */ + break; + } + +return val; +} + +void cpu_on_changed_ipl(RUN_DECL, int32 oldpsl, int32 flags) +{ + int32 old_ipl = PSL_GETIPL(oldpsl); + int32 new_ipl = PSL_GETIPL(PSL); + + if (new_ipl == old_ipl) + return; + + /* + * reevaluate VCPU thread priority if IPL crosses sys_critical_section_ipl level + */ + if (unlikely(0 == (flags & CHIPL_NO_THRDPRIO)) && sys_critical_section_ipl > 0) + { + if (old_ipl < sys_critical_section_ipl && new_ipl >= sys_critical_section_ipl || + old_ipl >= sys_critical_section_ipl && new_ipl < sys_critical_section_ipl) + { + cpu_reevaluate_thread_priority(RUN_PASS); + } + } + + /* + * manage synchronization window transitions + */ + if (0 == (flags & CHIPL_NO_SYNCW)) + { + if (old_ipl < syncw.ipl_syslock && new_ipl >= syncw.ipl_syslock) + { + syncw_enter_sys(RUN_PASS); + } + else if (old_ipl >= syncw.ipl_syslock && new_ipl < syncw.ipl_syslock) + { + if (new_ipl < syncw.ipl_resched) + syncw_leave_all(RUN_PASS, 0); + else + syncw_leave_sys(RUN_PASS); + } + else if (old_ipl >= syncw.ipl_resched && new_ipl < syncw.ipl_resched) + { + syncw_leave_ilk(RUN_PASS); + } + } +} + +/* + * Reevaluate and set proper VCPU thread priority based on VCPU state. + * + * Note that there is currently a race condition between cpu_reevaluate_thread_priority and interrupt_set_int, + * in a sequence like this. Suppose external source is sending an interrupt to VCPUn. The source + * can be a SYNCLK thread, interprocessor interrupt being sent by another procesor, or (for VCPU0) device handler + * executed in the context of another processor: + * + * VCPUn External Source + * (cpu_reevaluate_thread_priority) (interrupt_set_int) + * + * check conditions, incl. interrupts + * found no interrupt + * calculate new thread priority = low + * + * send an interrupt + * set VCPU0 thread priority to CRITICAL_VM + * + * drop thread priority to calculated + * low level + * + * Thus VCPUn priority boosting (for prompt interrupt processing) by external source will be lost + * and it is possible for VCPUn thread to stay at low priority and be preempted despite interrupts + * pending. + * + * If would be easy to fix the issue by using a locking algorithm, but we exactly want to avoid this + * and stick instead to lock-free and wait-free scheme for sending interrupts. It would be nice to + * find lock-free/wait-free solution to this race condition later, if exists. + * + * For now, we just allow this race condition, because: + * + * - It's probability is low (though surely not zero). + * + * - If happens, VCPUn priority will be boosted again by the next interrupt sent to it. + * If SYNCLK thread is used (use_clock_thread is TRUE), this interrupt will be typically sent + * within at most 1/100th of a second. + * + * Therefore we classify the issue as low priority. + * Things would change however if SYNCLK thread was not used. + * + */ +void cpu_reevaluate_thread_priority(RUN_DECL, t_bool synced) +{ + RUN_SCOPE_RSCX_ONLY; + sim_thread_priority_t newprio; + t_bool is_active_clk_interrupt; + t_bool is_active_ipi_interrupt; + + /* + * Reevaluate only if called in the context of target VCPU's thread. + * + * Can also be called on the console thread (as a result of console commands) + * or by another VCPU when that other VCPU is preparing to start this processor + * in cpu_start_secondary. Do not perform reevaluation in these cases. + * Instead, it will be performed when target VCPU's thread starts or resumes + * execution of instruction stream. + */ + if (unlikely(rscx->thread_type != SIM_THREAD_TYPE_CPU) || unlikely(rscx->thread_cpu_id != cpu_unit->cpu_id)) + { + cpu_unit->cpu_thread_priority = SIMH_THREAD_PRIORITY_INVALID; + return; + } + + /* prune recursive calls */ + if (cpu_unit->cpu_inside_reevaluate_thread_priority) + { + cpu_unit->cpu_redo_reevaluate_thread_priority = TRUE; + return; + } + +again: + + if (unlikely(mapen == 0) && cpu_unit->is_primary_cpu()) + { + /* equivalent of is_os_running() */ + cpu_set_thread_priority(RUN_PASS, SIMH_THREAD_PRIORITY_CPU_RUN); + return; + } + + if (unlikely(tmr_is_active(RUN_PASS))) + { + /* check if SSC clocks are still active */ + cpu_set_thread_priority(RUN_PASS, SIMH_THREAD_PRIORITY_CPU_CALIBRATION); + return; + } + + if (unlikely(rscx->vm_critical_locks != 0)) + { + /* + * if code holds VM critical locks, run at elevated priority + */ + cpu_set_thread_priority(RUN_PASS, SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM); + return; + } + + if (unlikely(rscx->os_hi_critical_locks != 0)) + { + /* + * if code holds OS_HI critical locks, run at elevated priority + */ + cpu_set_thread_priority(RUN_PASS, SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI); + return; + } + + if (unlikely(cpu_unit->cpu_active_clk_interrupt || cpu_unit->cpu_active_ipi_interrupt)) + { + /* + * if processing CLK or IPI interrupts, run at elevated priority + */ + cpu_set_thread_priority(RUN_PASS, SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI); + return; + } + + /* + * check if CLK or IPI interrupts are pending + */ + if (! synced) + { + /* + * read_irqs_to_local can recursively call cpu_reevaluate_thread_priority. + * + * This recursion is pruned at the entrance to cpu_reevaluate_thread_priority + * and original invocation of cpu_reevaluate_thread_priority is re-processed. + */ + cpu_unit->cpu_inside_reevaluate_thread_priority = TRUE; + cpu_unit->cpu_redo_reevaluate_thread_priority = FALSE; + + smp_rmb(); + read_irqs_to_local(RUN_PASS); + + int32 hipl = 0; + + if (unlikely(hlt_pin)) /* hlt pin int */ + { + hipl = IPL_HLTPIN; + } + else if (unlikely(mem_err)) /* mem err int */ + { + hipl = IPL_MEMERR; + } + else if (unlikely(crd_err)) /* crd err int */ + { + hipl = IPL_CRDERR; + } + else if (hipl = cpu_unit->cpu_intreg.highest_local_irql()) + { + // use it + } + else if (SISR) + { + for (int32 i = IPL_SMAX; i >= 1; i--) + { + if (SISR & (1 << i)) + { + hipl = i; + break; + } + } + } + + cpu_unit->cpu_context.highest_irql = hipl; + + cpu_unit->cpu_inside_reevaluate_thread_priority = FALSE; + if (cpu_unit->cpu_redo_reevaluate_thread_priority) + { + cpu_unit->cpu_redo_reevaluate_thread_priority = FALSE; + synced = TRUE; + goto again; + } + } + + cpu_unit->cpu_intreg.query_local_clk_ipi(& is_active_clk_interrupt, & is_active_ipi_interrupt); + if (unlikely(is_active_clk_interrupt)) cpu_unit->cpu_active_clk_interrupt = TRUE; + if (unlikely(is_active_ipi_interrupt)) cpu_unit->cpu_active_ipi_interrupt = TRUE; + + if (unlikely(cpu_unit->cpu_active_clk_interrupt || cpu_unit->cpu_active_ipi_interrupt)) + { + /* + * if CLK or IPI interrupts are pending, run at elevated priority + */ + cpu_set_thread_priority(RUN_PASS, SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI); + return; + } + + if (unlikely(cpu_unit->cpu_synclk_pending != SynclkNotPending)) + { + /* + * if inside a period protected from CLK raising, and CLK raising is pending, + * run at elevated priority + */ + cpu_set_thread_priority(RUN_PASS, SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI); + return; + } + + if (likely(sys_critical_section_ipl >= 0)) + { + if (unlikely( + sys_critical_section_ipl > 0 && PSL_GETIPL(PSL) >= sys_critical_section_ipl || + (cpu_unit->cpu_context.highest_irql = cpu_unit->cpu_intreg.highest_local_irql()) >= sys_critical_section_ipl && cpu_unit->cpu_context.highest_irql + /* we do not need to check on the condition commented out below, a check for pending new interrupt, + since we just performed an RMB synchronization above and acquired uptodate IRQ set */ + /* || IPL_HMAX >= sys_critical_section_ipl && cpu_unit->cpu_intreg.cas_changed(1, 1) */ + )) + { + /* + * if inside operating system critical section (or interrupt request is pending with IPL high enough), + * run at elevated priority + */ + newprio = SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS; + } + else + { + /* otherwise run at normal priority */ + newprio = SIMH_THREAD_PRIORITY_CPU_RUN; + } + } + else + { + /* otherwise run at normal priority */ + newprio = SIMH_THREAD_PRIORITY_CPU_RUN; + } + + cpu_set_thread_priority(RUN_PASS, newprio); +} + +/* + * Check whether operating system is running. + * + * The exact meaning of this check is somewhat diffuse, see its actual use. + * + * Current minimum requirement is that the operating system is past initial sysboot phase + * with virtial memory mapping enabled on the primary processor. + * + * Practical check is that we are either running on the secondary processor, + * or on the primary with MAPEN enabled. + */ +t_bool is_os_running(RUN_DECL) +{ + return cpu_unit->is_primary_cpu() ? (mapen != 0) : TRUE; +} + +/* + * Possible write to SCB had been detected. + * + * Cover an omission in VAX Architecture Standard and VMS code that may trigger problems + * when running VMS on host architectures with weak memory model. VMS does not properly synchronize + * writes to the SCB in inter-procesor system. Therefore if CPU2 changes shared SCB vector and CPU0 + * receives an interrupt, old (on undefined) vector can be used. Can happen for example if SYSGEN + * is executed on secondary processor and connects the driver to the interrupt. + * + * Check if written physical address is indeed within SCB range. + * If so, execute write memory barrier to make updated vector available to other processors ASAP. + * + * For interrupt vectors, read memory barrier will be executed by other processors when they + * detect an interrupt, before it is dispatched through the SCB, so valid most recent value + * of the vector will be fetched during interrupt sequence. + * + * However for exceptions and traps dispatch, read memory barrier is not performed. it would be + * inefficient (and unlike for interrupts, is not required by VAX Architechire Standard) + * to execute read memory barrier on every exception/trap. Therefore we do not do it, but we + * do instead try to expedite other processors' cache view update by sending RMB request to them. + * This can matter if kernel mode code such as XDELTA hooks into exception vectors. + * + * Note: a delay in update can stll occur since "lazy" checking for pending interrupts may delay + * execution of remote RMB by up to a few VAX instructions, and also much longer if target processor + * is currently executing interrupt processing. Should the latter ever become an issue, RMB request + * can be moved to high or very high IPL, perhaps even above IPL_POWER, since RMB request interrupt + * is not actually delivered to VAX code and is not visible at VAX code level, but causes only cache + * synchronizaton at SIMH level. + */ +void cpu_scb_written(int32 pa) +{ + /* + * Use RUN_SCOPE instead of RUN_DECL since this routine is invoked extremely infrequently, + * but calls to it are dispersed throughout many places. Therefore try to maintain calling code + * just a little bit more compact by avoiding extra argument. + */ + RUN_SCOPE_RSCX; + + if ((uint32) pa >= (uint32) SCBB && + (uint32) pa < (uint32) SCBB + SCB_SIZE) + { + smp_wmb(); + + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + if (cpu_units[cpu_ix] != cpu_unit || rscx->thread_type != SIM_THREAD_TYPE_CPU) + interrupt_ipi_rmb(RUN_PASS, cpu_units[cpu_ix]); + } + } +} + +static uint32 interrupt_percpu_devs[IPL_HLVL]; +uint32 devs_per_irql[IPL_HLVL]; +extern DEVICE sysd_dev; + +void sim_init_interrupt_info() +{ + memset(interrupt_percpu_devs, 0, sizeof interrupt_percpu_devs); + memset(devs_per_irql, 0, sizeof devs_per_irql); + + DEVICE* dptr; + + for (int i = 0; (dptr = sim_devices[i]) != NULL; i++) + { + DIB* dib = (DIB*) dptr->ctxt; + if (dib == NULL || dib->vnum == 0) continue; + uint32 ix_ipl = IVCL_IPL(dib->vloc); + uint32 dev = IVCL_DEV(dib->vloc); + for (int vn = 0; vn < dib->vnum; vn++, dev++) + { + /* Record highest device index used */ + devs_per_irql[ix_ipl] = imax(devs_per_irql[ix_ipl], dev + 1); + + if (dptr->flags & DEV_PERCPU) + { + interrupt_percpu_devs[ix_ipl] |= (1 << dev); + } + } + } + + devs_per_irql[IPL_IPINTR] = imax(devs_per_irql[IPL_IPINTR], (uint32) INT_V_IPINTR + 1); + devs_per_irql[IPL_IPIRMB] = imax(devs_per_irql[IPL_IPIRMB], (uint32) INT_V_IPIRMB + 1); + devs_per_irql[IPL_SECEXIT] = imax(devs_per_irql[IPL_SECEXIT], (uint32) INT_V_SECEXIT + 1); + devs_per_irql[IPL_SYNCWSYS] = imax(devs_per_irql[IPL_SYNCWSYS], (uint32) INT_V_SYNCWSYS + 1); + devs_per_irql[IPL_SYNCLK] = imax(devs_per_irql[IPL_SYNCLK], (uint32) INT_V_SYNCLK + 1); + devs_per_irql[IPL_ASYNC_IO] = imax(devs_per_irql[IPL_ASYNC_IO], (uint32) INT_V_ASYNC_IO + 1); + devs_per_irql[IPL_STOP] = imax(devs_per_irql[IPL_STOP], (uint32) INT_V_STOP + 1); + + interrupt_percpu_devs[IPL_IPINTR] |= (1 << INT_V_IPINTR); + interrupt_percpu_devs[IPL_IPIRMB] |= (1 << INT_V_IPIRMB); + interrupt_percpu_devs[IPL_SECEXIT] |= (1 << INT_V_SECEXIT); + interrupt_percpu_devs[IPL_SYNCWSYS] |= (1 << INT_V_SYNCWSYS); + interrupt_percpu_devs[IPL_SYNCLK] |= (1 << INT_V_SYNCLK); + interrupt_percpu_devs[IPL_ASYNC_IO] |= (1 << INT_V_ASYNC_IO); + interrupt_percpu_devs[IPL_STOP] |= (1 << INT_V_STOP); +} + +/* raise device interrupt */ +void interrupt_set_int(uint32 ix_ipl, uint32 dev, t_bool force_wakeup) +{ + interrupt_set_int(NULL, ix_ipl, dev, force_wakeup); +} + +void interrupt_set_int(CPU_UNIT* xcpu, uint32 ix_ipl, uint32 dev, t_bool force_wakeup) +{ + RUN_SCOPE_RSCX; + t_bool wakeup = FALSE; + + uint32 ipl = ix_ipl + IPL_HMIN; + + /* determine target VCPU */ + if (interrupt_percpu_devs[ix_ipl] & (1 << dev)) + xcpu = xcpu ? xcpu : cpu_unit; + else + xcpu = &cpu_unit_0; + + /* + * Check for (xcpu->cpu_id == rscx->thread_cpu_id) rather than (xcpu == cpu_unit) + * since while inside cpu_start_secondary() VCPU thread can be temporary bound to the + * context of another VCPU and impersonating it. + */ + t_bool toself = (xcpu->cpu_id == rscx->thread_cpu_id) && (rscx->thread_type == SIM_THREAD_TYPE_CPU); + + /* + * Raise interrupt flag. + * Perform memory barrier if necessary. + * + * If this interrupt was not raised previously in the target CPU + * and is targeted to other (non-current) virtual CPU, wake this CPU up. + */ + if (FALSE == xcpu->cpu_intreg.set_int(ipl, dev, toself) && !toself || force_wakeup) + wakeup = TRUE; + + /* + * Raise target VCPU's thread level priority to reflect pending interrupt + */ + if (sys_critical_section_ipl >= 0 && (int32) ipl >= sys_critical_section_ipl) + { + if (toself) + { + switch (cpu_unit->cpu_thread_priority) + { + case SIMH_THREAD_PRIORITY_CPU_CALIBRATION: + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM: + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI: + break; + default: + cpu_set_thread_priority(RUN_PASS, SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI); + break; + } + } + else if (rscx->thread_type == SIM_THREAD_TYPE_CPU || rscx->thread_type == SIM_THREAD_TYPE_CLOCK) + { + /* + * Really want to raise target CPU thread priority to CRITICAL_OS or CRITICAL_OS_HI here, but the target + * can already be at CRITICAL_VM, so use CRITICAL_VM to avoid undermining the target, but leave the mark + * for the target to re-examine its thread priority ASAP. + * + * We assume that CRITICAL_VM is the highest thread priority target may usually have and it is not likely to be + * at CALIBRATION priority since SSC clocks (associated with CALIBRATION) are generally not used by VMS + * on a MicroVAX after SMP is activated, whereas device interrupts are unlikely during the INIT phase + * when SSC calibration is performed. However SYNCLK are quite possible during CALIBRATION. + * Nevertheless even if it happened, CRITICAL_VM is close to CALIBRATION, so we use CRITICAL_VM here + * instead of CALIBRATION. + * + * We do however make limited attempt to keep the thread at CALIBRATON if it was already at CALIBRATION + * and avoid resetting it to VM. The check below does not have to be perfect. + */ + if (must_control_prio()) + { + if (weak_read(xcpu->sysd_active_mask) == 0) + smp_set_thread_priority(xcpu->cpu_thread, SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM); + else + smp_set_thread_priority(xcpu->cpu_thread, SIMH_THREAD_PRIORITY_CPU_CALIBRATION); + + /* Force target VCPU to re-evaluate its thread priority ASAP. Can be xchg(changed, 1). */ + xcpu->cpu_intreg.cas_changed(0, 1); + } + } + else if (rscx->thread_type == SIM_THREAD_TYPE_CONSOLE) + { + t_bool is_active_clk_interrupt; + t_bool is_active_ipi_interrupt; + + /* console thread has access to xcpu->cpu_thread_priority */ + switch (xcpu->cpu_thread_priority) + { + case SIMH_THREAD_PRIORITY_CPU_CALIBRATION: + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM: + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI: + break; + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS: + xcpu->cpu_intreg.query_local_clk_ipi(& is_active_clk_interrupt, & is_active_ipi_interrupt); + if (is_active_clk_interrupt) xcpu->cpu_active_clk_interrupt = TRUE; + if (is_active_ipi_interrupt) xcpu->cpu_active_ipi_interrupt = TRUE; + if (xcpu->cpu_active_clk_interrupt || xcpu->cpu_active_ipi_interrupt) + { + if (must_control_prio()) + smp_set_thread_priority(xcpu->cpu_thread, SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI); + xcpu->cpu_thread_priority = SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI; + } + break; + default: + if (must_control_prio()) + smp_set_thread_priority(xcpu->cpu_thread, SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS); + xcpu->cpu_thread_priority = SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS; + break; + } + } + } + + if (wakeup) + wakeup_cpu(xcpu); +} + +/* + * Raise interprocessor interrupt on 'xcpu'. + * It is ok to invoke this routine on 'xcpu' than is not running, it is no-op then. + */ +void interrupt_ipi(RUN_DECL, CPU_UNIT* xcpu, t_bool force_wakeup) +{ + interrupt_set_int(xcpu, IPL_IPINTR, INT_V_IPINTR, force_wakeup); +} + +void interrupt_ipi_rmb(RUN_DECL, CPU_UNIT* xcpu) +{ + RUN_SCOPE_RSCX_ONLY; + + if (xcpu->cpu_id == rscx->thread_cpu_id && rscx->thread_type == SIM_THREAD_TYPE_CPU) + { + smp_rmb(); + } + else + { + xcpu->cpu_intreg.set_int(IPL_ABS_IPIRMB, INT_V_IPIRMB, FALSE); + } +} + +/* clear device interrupt */ +void interrupt_clear_int(uint32 ix_ipl, uint32 dev) +{ + RUN_SCOPE_RSCX; + CPU_UNIT* xcpu; + + uint32 ipl = ix_ipl + IPL_HMIN; + + /* determine target VCPU */ + if (interrupt_percpu_devs[ix_ipl] & (1 << dev)) + xcpu = cpu_unit; + else + xcpu = &cpu_unit_0; + + /* + * Check for (xcpu->cpu_id == rscx->thread_cpu_id) rather than (xcpu == cpu_unit) + * since while inside cpu_start_secondary() VCPU thread can be temporary bound to the + * context of another VCPU and impersonating it. + */ + t_bool toself = (xcpu->cpu_id == rscx->thread_cpu_id) && (rscx->thread_type == SIM_THREAD_TYPE_CPU); + + xcpu->cpu_intreg.clear_int(ipl, dev, toself); +} + +/* + * Read/write routines for SIMH REG's of device INT registers, + * encoded as IRDATA_DEV. + */ +t_value reg_irdata_dev_rd(REG* r, uint32 idx) +{ + RUN_SCOPE; + uint32 ivcl = (uint32) (t_addr_val) r->loc; + uint32 ix_ipl = IVCL_IPL(ivcl); + uint32 dev = IVCL_DEV(ivcl); + uint32 ipl = ix_ipl + IPL_HMIN; + + CPU_UNIT* xcpu; + + if (interrupt_percpu_devs[ix_ipl] & (1 << dev)) + xcpu = cpu_unit; + else + xcpu = &cpu_unit_0; + + return xcpu->cpu_intreg.examine_int(ipl, dev) ? 1 : 0; +} + +void reg_irdata_dev_wr(REG* r, uint32 idx, t_value value) +{ + uint32 ivcl = (uint32) (t_addr_val) r->loc; + uint32 ix_ipl = IVCL_IPL(ivcl); + uint32 dev = IVCL_DEV(ivcl); + + if (value & 1) + interrupt_set_int(ix_ipl, dev, TRUE); + else + interrupt_clear_int(ix_ipl, dev); +} + +int reg_irdata_dev_vcpu(REG* r, uint32 idx) +{ + RUN_SCOPE; + uint32 ivcl = (uint32) (t_addr_val) r->loc; + uint32 ix_ipl = IVCL_IPL(ivcl); + uint32 dev = IVCL_DEV(ivcl); + + if (interrupt_percpu_devs[ix_ipl] & (1 << dev)) + return cpu_unit->cpu_id; + else + return -1; +} + +/* + * Read/write routines for SIMH REG's of IPL level interrupts (14-17), + * encoded as IRDATA_LVL. + */ +t_value reg_irdata_lvl_rd(REG* r, uint32 idx) +{ + RUN_SCOPE; + uint32 what_code = (uint32) (t_addr_val) r->loc; + uint32 ipl = what_code & 31; + t_bool curr_cpu = 0 != (what_code & 32); + + if (ipl < IPL_HMIN || ipl > IPL_HMAX) + return 0; + + CPU_UNIT* xcpu = curr_cpu ? cpu_unit : &cpu_unit_0; + + t_value res = 0; + + for (uint32 dev = 0; dev < devs_per_irql[ipl - IPL_HMIN]; dev++) + { + if (xcpu->cpu_intreg.examine_int(ipl, dev)) + res |= (t_value) (1u << dev); + } + + return res; +} + +void reg_irdata_lvl_wr(REG* r, uint32 idx, t_value value) +{ + RUN_SCOPE; + uint32 what_code = (uint32) (t_addr_val) r->loc; + uint32 ipl = what_code & 31; + t_bool curr_cpu = 0 != (what_code & 32); + uint32 ix_ipl = ipl - IPL_HMIN; + + if (ipl < IPL_HMIN || ipl > IPL_HMAX) + return; + + CPU_UNIT* xcpu = curr_cpu ? cpu_unit : &cpu_unit_0; + + for (uint32 dev = 0; dev < devs_per_irql[ipl - IPL_HMIN]; dev++) + { + if (value & (t_value) (1u << dev)) + { + interrupt_set_int(xcpu, ix_ipl, dev); + } + else + { + xcpu->cpu_intreg.clear_int(ipl, dev, FALSE); + } + } +} + +int reg_irdata_lvl_vcpu(REG* r, uint32 idx) +{ + RUN_SCOPE; + return cpu_unit->cpu_id; +} + +t_value reg_sirr_rd(REG* r, uint32 idx) +{ + /* write-only register, return dummy value */ + return 0; +} + +void reg_sirr_wr(REG* r, uint32 idx, t_value value) +{ + RUN_SCOPE; + + if (value >= 1 && value <= 0xF) + { + SISR = SISR | (1 << value); + SET_IRQL; + } +} + + +/* + * Check if CPU may perform idle sleep. + * + * Called by sim_idle right before entering idle sleep. + * If this function returns FALSE, idle sleep is not attempted. + */ +t_bool cpu_may_sleep(RUN_DECL) +{ + /* + * check if received new interrupt + */ + if (cpu_unit->cpu_intreg.cas_changed(1, 1)) + return FALSE; + + /* + * check if *deliverable* interrupt is pending + */ + SET_IRQL; + if (unlikely(GET_IRQL(trpirq) != 0)) + return FALSE; + + /* + * Check if stop is pending + */ + smp_post_interlocked_rmb(); + if (unlikely(weak_read(stop_cpus))) + return FALSE; + + /* + * check if the bit in idle CPU mask had been cleared + */ + if (sys_idle_cpu_mask_va && mapen && is_os_running(RUN_PASS)) + { + int32 acc = ACC_MASK(KERN); + int32 mask = 0; /* initialize to suppress false GCC warning */ + t_bool abort = FALSE; + + sim_try + { + mask = Read (RUN_PASS, sys_idle_cpu_mask_va, L_LONG, RA); + } + sim_catch (sim_exception_ABORT, exabort) + { + if (cpu_unit->cpu_exception_ABORT == NULL && exabort->isAutoDelete()) + { + cpu_unit->cpu_exception_ABORT = exabort; + } + else + { + exabort->checkAutoDelete(); + } + abort = TRUE; + } + sim_end_try + + if (abort) + { + smp_printf ("\nOperating System CPU idle mask is non-readable\n"); + if (sim_log) + fprintf (sim_log, "\nOperating System CPU idle mask is non-readable\n"); + ABORT_INVALID_SYSOP; + } + + if ((mask & (1 << cpu_unit->cpu_id)) == 0) + return FALSE; + } + + /* + * should not have synclk -> clk conversion pending, that would be logical error, + * especially important if (syncw.on & SYNCW_SYS) + */ + if (cpu_unit->cpu_synclk_pending != SynclkNotPending) + panic("cpu_may_sleep: cpu_synclk_pending != SynclkNotPending"); + + return TRUE; +} + +/* + * Wake-up virtual CPU that may be in an idle sleep. + * It is ok to invoke this routine on 'xcpu' than is not sleeping or not running, it is no-op then. + * + * Because of race condition with sim_idle this routine can sometimes produce spurious wakeups + * out of sim_idle sleep. + */ +void wakeup_cpu(CPU_UNIT *xcpu) +{ + if (smp_interlocked_cas_done_var(& xcpu->cpu_sleeping, 1, 1)) + xcpu->cpu_wakeup_event->set(); +} + +/* + * Operating system scheduler had changed "idle CPUs" mask. + * Wake up those CPUs whose "idle" bit had been reset so they can perform their scheduling cycle. + * Called by the CPU that modifies the mask. + */ +void wakeup_cpus(RUN_DECL, uint32 old_idle_mask, uint32 new_idle_mask) +{ + /* calculate wakeup set */ + uint32 wmask = old_idle_mask & ~new_idle_mask; + + /* remove current CPU from the wakeup set, since it is already awake */ + wmask &= ~(1 << cpu_unit->cpu_id); + if (wmask == 0) return; + + /* + * flush out changed content of idle CPUs mask (operating system cell pointed by sys_idle_cpu_mask_va) + * to other CPUs + */ + smp_wmb(); + + /* + * Wakeup CPUs indicated by the change in the idle mask. + * + * Note we do not need to send IPIRMB because idle_sleep and cpu_may_sleep will perform RMB and + * idle CPU mask checking before trying to enter idle sleep and also perform RMB after exiting the sleep. + */ + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + if (wmask & (1 << cpu_ix)) + { + CPU_UNIT* xcpu = cpu_units[cpu_ix]; + wakeup_cpu(xcpu); + } + } +} + +/* + * Beginning and ending interlocked instructions. + * + * Beginning: + * + * Elevate thread priority. This prevents VCPU thread from acquiring a lock on an interlocked + * data item and then being pre-empted for an extended time while holding the lock. + * + * Ending: + * + * Thread priority will be demoted back to the original via cpu_end_interlocked() + * when interlocked instruction completes and interlock is released. + * + */ +void cpu_begin_interlocked(RUN_DECL, volatile sim_thread_priority_t* sv_priority, volatile t_bool* sv_priority_stored) +{ + RUN_SCOPE_RSCX_ONLY; + /* + * See "VAX MP Techincal Overview" chapter "Implementation of VAX interlocked instructions" + * for detailed discussion of what (and why) is done here. + * + * Code bracketed by cpu_begin_interlocked/cpu_end_interlocked may temporarily acquire and release locks with + * non-null criticality level and may call cpu_reevaluate_thread_priority. To prevent thread priority from being + * decreased by by these activities, we have to mark up thread's vm_critical_locks count. + */ + ++rscx->vm_critical_locks; + + // *sv_priority = cpu_unit->cpu_thread_priority; + // *sv_priority_stored = TRUE; + + if ((cpu_unit->cpu_thread_priority == SIMH_THREAD_PRIORITY_INVALID || + cpu_unit->cpu_thread_priority < SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM) && + likely(is_os_running(RUN_PASS))) + { + cpu_set_thread_priority(RUN_PASS, SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM); + } +} + +void cpu_end_interlocked(RUN_DECL, sim_thread_priority_t sv_priority, t_bool sv_priority_stored) +{ + /* + * Restore thread priority. Note that we cannot simply restore priority to sv_priority, + * since priority might have been purposefully altered if interrupt was sent to this processor + * by an external source. + */ + vm_critical_unlock(); +} + +/* internal development/debugging tool */ +t_stat xdev_cmd(int32 flag, char *cptr) +{ + RUN_SCOPE; + char gbuf[CBUFSIZE]; + t_stat r; + + if (*cptr == 0) + return SCPE_2FARG; + + cptr = get_glyph(cptr, gbuf, 0); + + /* XDEV IPI */ + if (streqi(gbuf, "ipi") && cptr && *cptr) + { + uint32 ncpu = (uint32) get_uint(cptr, 10, sim_ncpus - 1, &r); + if (r != SCPE_OK) + return SCPE_ARG; + if (ncpu >= sim_ncpus || cpu_units[ncpu] == NULL) return SCPE_ARG; + interrupt_ipi(RUN_PASS, cpu_units[ncpu]); + return SCPE_OK; + } + else + { + return SCPE_ARG; + } +} + +/* + * Pre-fault memory into working set + */ +void cpu_prefault_memory() +{ + int npages = (int) (cpu_unit_0.capac / VA_PAGSIZE); + volatile t_byte* p = (volatile t_byte*) M; + volatile static t_byte b; + + for (int k = 0; k < npages; k++) + { + b = *p; + p += VA_PAGSIZE; + } +} diff --git a/src/VAX/vax_cpuctx.h b/src/VAX/vax_cpuctx.h new file mode 100644 index 0000000..0b11e90 --- /dev/null +++ b/src/VAX/vax_cpuctx.h @@ -0,0 +1,325 @@ +#ifndef __VAX_MP_CPUCTX_H__ +#define __VAX_MP_CPUCTX_H__ 1 + +// #pragma message ("Loading vax_cpuctx.h") +#include "vax_defs.h" + +#define memzero(x) memset(x, 0, sizeof(x)) + +#define cpu_sim_interval(xcpu) ((xcpu)->cpu_context.r_sim_interval) +#define sim_interval cpu_sim_interval(cpu_unit) +#define R (cpu_unit->cpu_context.r_R) +#define PSL (cpu_unit->cpu_context.r_PSL) +#define STK (cpu_unit->cpu_context.r_STK) +// #ifndef KSP +// # define KSP STK[KERN] +// # define ESP STK[EXEC] +// # define SSP STK[SUPV] +// # define USP STK[USER] +// # define IS STK[4] +// #endif +#define SCBB (cpu_unit->cpu_context.r_SCBB) +#define PCBB (cpu_unit->cpu_context.r_PCBB) +#define P0BR (cpu_unit->cpu_context.r_P0BR) +#define P0LR (cpu_unit->cpu_context.r_P0LR) +#define P1BR (cpu_unit->cpu_context.r_P1BR) +#define P1LR (cpu_unit->cpu_context.r_P1LR) +#define SBR (cpu_unit->cpu_context.r_SBR) +#define SLR (cpu_unit->cpu_context.r_SLR) +#define WHAMI (cpu_unit->cpu_context.r_WHAMI) +#define SISR (cpu_unit->cpu_context.r_SISR) +#define ASTLVL (cpu_unit->cpu_context.r_ASTLVL) +#define mapen (cpu_unit->cpu_context.r_mapen) +#define pme (cpu_unit->cpu_context.r_pme) +#define crd_err (cpu_unit->cpu_context.r_crd_err) +#define mem_err (cpu_unit->cpu_context.r_mem_err) +#define pcq (cpu_unit->cpu_context.r_pcq) +#define pcq_p (cpu_unit->cpu_context.r_pcq_p) +#define mchk_va (cpu_unit->cpu_context.r_mchk_va) +#define mchk_ref (cpu_unit->cpu_context.r_mchk_ref) +#define d_p0br (cpu_unit->cpu_context.r_d_p0br) +#define d_p0lr (cpu_unit->cpu_context.r_d_p0lr) +#define d_p1br (cpu_unit->cpu_context.r_d_p1br) +#define d_p1lr (cpu_unit->cpu_context.r_d_p1lr) +#define d_sbr (cpu_unit->cpu_context.r_d_sbr) +#define d_slr (cpu_unit->cpu_context.r_d_slr) +#define stlb (cpu_unit->cpu_context.r_stlb) +#define ptlb (cpu_unit->cpu_context.r_ptlb) +#define fault_p1 (cpu_unit->cpu_context.r_fault_p1) +#define fault_p2 (cpu_unit->cpu_context.r_fault_p2) +#define fault_PC (cpu_unit->cpu_context.r_fault_PC) +#define trpirq (cpu_unit->cpu_context.r_trpirq) +#define in_ie (cpu_unit->cpu_context.r_in_ie) +#define recq (cpu_unit->cpu_context.r_recq) +#define recqptr (cpu_unit->cpu_context.r_recqptr) +#if VAX_DIRECT_PREFETCH +# define mppc (cpu_unit->cpu_context.r_mppc) +# define mppc_rem (cpu_unit->cpu_context.r_mppc_rem) +#endif +#define ppc (cpu_unit->cpu_context.r_ppc) +#define ibcnt (cpu_unit->cpu_context.r_ibcnt) +#define ibufl (cpu_unit->cpu_context.r_ibufl) +#define ibufh (cpu_unit->cpu_context.r_ibufh) +#define badabo (cpu_unit->cpu_context.r_badabo) +#define cq_scr (cpu_unit->cpu_context.r_cq_scr) +#define cq_dser (cpu_unit->cpu_context.r_cq_dser) +#define cq_mear (cpu_unit->cpu_context.r_cq_mear) +#define cq_sear (cpu_unit->cpu_context.r_cq_sear) +#define cq_ipc (cpu_unit->cpu_context.r_cq_ipc) +#define cdg_dat (cpu_unit->cpu_context.r_cdg_dat) +#define ka_cacr (cpu_unit->cpu_context.r_ka_cacr) +#define CADR (cpu_unit->cpu_context.r_CADR) +#define MSER (cpu_unit->cpu_context.r_MSER) +#define cmctl_reg (cpu_unit->cpu_context.r_cmctl_reg) +#define ssc_bto (cpu_unit->cpu_context.r_ssc_bto) +#define ssc_otp (cpu_unit->cpu_context.r_ssc_otp) +#define tmr_csr (cpu_unit->cpu_context.r_tmr_csr) +#define tmr_tir (cpu_unit->cpu_context.r_tmr_tir) +#define tmr_tir_rtstart (cpu_unit->cpu_context.r_tmr_tir_rtstart) +#define tmr_tnir (cpu_unit->cpu_context.r_tmr_tnir) +#define tmr_tivr (cpu_unit->cpu_context.r_tmr_tivr) +#define tmr_inc (cpu_unit->cpu_context.r_tmr_inc) +#define tmr_sav (cpu_unit->cpu_context.r_tmr_sav) +#define ssc_adsm (cpu_unit->cpu_context.r_ssc_adsm) +#define ssc_adsk (cpu_unit->cpu_context.r_ssc_adsk) +#define tti_csr (cpu_unit->cpu_context.r_tti_csr) +#define tto_csr (cpu_unit->cpu_context.r_tto_csr) +#define tto_buf (cpu_unit->cpu_context.r_tto_buf) +#define clk_csr (cpu_unit->cpu_context.r_clk_csr) +#define todr_reg (cpu_unit->cpu_context.r_todr_reg) +#define todr_blow (cpu_unit->cpu_context.r_todr_blow) + +#define primary_todr_reg (cpu_unit_0.cpu_context.r_todr_reg) +#define primary_todr_blow (cpu_unit_0.cpu_context.r_todr_blow) + +typedef struct +{ + int32 tag; /* tag */ + int32 pte; /* pte */ +} +TLBENT; + +class CPU_CONTEXT +{ +private: + /* first reset? */ + t_bool initial; + +public: + /* emulator runtime */ + int32 r_sim_interval; + + /* processor registers */ + int32 r_R[16]; + int32 r_PSL; + int32 r_STK[5]; + int32 r_SCBB; + int32 r_PCBB; + int32 r_P0BR; + int32 r_P0LR; + int32 r_P1BR; + int32 r_P1LR; + int32 r_SBR; + int32 r_SLR; + int32 r_WHAMI; + int32 r_SISR; + int32 r_ASTLVL; + int32 r_mapen; + int32 r_pme; + int32 r_crd_err; + int32 r_mem_err; + int32 r_pcq[PCQ_SIZE]; + uint32 r_pcq_qptr; + int32 r_pcq_p; + + /* for mcheck */ + int32 r_mchk_va; + int32 r_mchk_ref; + + /* dynamic copies, altered per ucode */ + int32 r_d_p0br; + int32 r_d_p0lr; + int32 r_d_p1br; + int32 r_d_p1lr; + int32 r_d_sbr; + int32 r_d_slr; + + TLBENT r_stlb[VA_TBSIZE]; + TLBENT r_ptlb[VA_TBSIZE]; + + /* fault parameters */ + SIM_ALIGN_32 + int32 r_fault_p1; + int32 r_fault_p2; + int32 r_fault_PC; + + /* interrupt requests */ + int32 r_trpirq; + + /* in exception or interrupt */ + int32 r_in_ie; + + /* instruction recovery queue */ + int32 r_recq[6]; /* recovery queue */ + SIM_ALIGN_32 + int32 r_recqptr; /* recq pointer */ + +#if VAX_DIRECT_PREFETCH + t_byte* r_mppc; /* next memory byte fetch pointer (only if ADDR_IS_MEM) */ + int32 r_mppc_rem; /* remaining memory fetch byte count */ +#endif + int32 r_ppc; /* instruction prefetch ctl */ + int32 r_ibcnt; /* instruction prefetch ctl */ + int32 r_ibufl, r_ibufh; /* instruction prefetch buffer */ + + int32 r_badabo; /* last abort code */ + + /* CQBIC registers: SCR, DSER, MEAR, SEAR and IPC */ + int32 r_cq_scr; + int32 r_cq_dser; + int32 r_cq_mear; + int32 r_cq_sear; + int32 r_cq_ipc; + + int32 r_cdg_dat[CDASIZE >> 2]; /* cache data */ + int32 r_ka_cacr; /* CACR, KA655 cache ctl */ + + /* + * CADR is 1ST LEVEL CACHE STATUS, normal value after operating system startup is comleted: + * CADR: 000000FC + * _D STREAM ENABLED + * _I STREAM ENABLED + * _SET 1 ENABLED + * _SET 2 ENABLED + */ + int32 r_CADR; /* cache disable reg */ + int32 r_MSER; /* mem sys error reg */ + int32 r_cmctl_reg[CMCTLSIZE >> 2]; /* cache memory control registers */ + + int32 r_ssc_bto; /* SSC timeout */ + int32 r_ssc_otp; /* SSC output port */ + int32 r_tmr_csr[2]; /* SSC timers */ + uint32 r_tmr_tir[2]; /* curr interval */ + uint32 r_tmr_tir_rtstart[2]; /* starting value (when delta timer is used) */ + uint32 r_tmr_tnir[2]; /* next interval */ + int32 r_tmr_tivr[2]; /* vector */ + uint32 r_tmr_inc[2]; /* tir increment */ + uint32 r_tmr_sav[2]; /* saved inst cnt */ + int32 r_ssc_adsm[2]; /* addr strobes */ + int32 r_ssc_adsk[2]; + + int32 r_tti_csr; /* control/status */ + int32 r_tto_csr; /* control/status */ + int32 r_tto_buf; /* data buffer */ + int32 r_clk_csr; /* control/status */ + atomic_int32 r_todr_reg; /* TODR register */ + int32 r_todr_blow; /* TODR battery low */ + + uint32 scb_range_pamask; /* auxiliary variable for fast checks by PA_MAY_BE_INSIDE_SCB */ + + int32 highest_irql; /* highest IRQL (IPL) of a pending interrupt */ + + CPU_CONTEXT(); + + void reset(CPU_UNIT* cpu_unit); +}; +#endif + +#ifdef VAX_MP_CPUCTX_METHODS +CPU_CONTEXT::CPU_CONTEXT() +{ + initial = TRUE; + CPU_UNIT* cpu_unit = CPU_UNIT::getBy(this); + cqbic_reset_percpu(RUN_PASS, TRUE); +} + +void CPU_CONTEXT::reset(CPU_UNIT* cpu_unit) +{ + sim_interval = 0; + memzero(R); + PSL = PSL_IS | PSL_IPL1F; + KSP = 0; + ESP = 0; + SSP = 0; + USP = 0; + IS = 0; + SCBB = 0; + PCBB = 0; + P0BR = 0; + P0LR = 0; + P1BR = 0; + P1LR = 0; + SBR = 0; + SLR = 0; + WHAMI = 0; + SISR = 0; + ASTLVL = 4; + mapen = 0; + pme = 0; + crd_err = 0; + mem_err = 0; + memzero(pcq); + r_pcq_qptr = 0; + pcq_p = 0; + mchk_va = 0; + mchk_ref = 0; + d_p0br = 0; + d_p0lr = 0; + d_p1br = 0; + d_p1lr = 0; + d_sbr = 0; + d_slr = 0; + memzero(stlb); + memzero(ptlb); + fault_p1 = 0; + fault_p2 = 0; + fault_PC = 0; + trpirq = 0; + in_ie = 0; + recqptr = 0; +#if VAX_DIRECT_PREFETCH + mppc = NULL; + mppc_rem = 0; +#endif + ppc = -1; + ibcnt = 0; + ibufl = ibufh = 0; + badabo = 0; + + cqbic_reset_percpu(RUN_PASS, FALSE); + + memzero(cdg_dat); + ka_cacr = 0; + CADR = 0; + MSER = 0; + memzero(r_cmctl_reg); + + ssc_bto = 0; + ssc_otp = 0; + + memzero(tmr_csr); + memzero(tmr_tir); + memzero(tmr_tir_rtstart); + memzero(tmr_tnir); + memzero(tmr_tivr); + memzero(tmr_inc); + memzero(tmr_sav); + memzero(ssc_adsm); + memzero(ssc_adsk); + + tti_csr = 0; + tto_csr = 0; + tto_buf = 0; + clk_csr = 0; + todr_reg = 0; + todr_blow = 1; + + scb_range_pamask = 0; + + highest_irql = 0; + + initial = FALSE; + + cpu_on_clear_mapen(RUN_PASS); +} +#endif diff --git a/src/VAX/vax_defs.h b/src/VAX/vax_defs.h new file mode 100644 index 0000000..7e54346 --- /dev/null +++ b/src/VAX/vax_defs.h @@ -0,0 +1,849 @@ +/* vax_defs.h: VAX architecture definitions file + + Copyright (c) 1998-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. + + The author gratefully acknowledges the help of Stephen Shirron, Antonio + Carlini, and Kevin Peterson in providing specifications for the Qbus VAX's + + 09-May-06 RMS Added system PTE ACV error code + 03-May-06 RMS Added EDITPC get/put cc's macros + 03-Nov-05 RMS Added 780 stop codes + 22-Jul-05 RMS Fixed warning from Solaris C (from Doug Gwyn) + 02-Sep-04 RMS Added octa specifier definitions + 30-Aug-04 RMS Added octa, h_floating instruction definitions + 24-Aug-04 RMS Added compatibility mode definitions + 18-Apr-04 RMS Added octa, fp, string definitions + 19-May-03 RMS Revised for new conditional compilation scheme + 14-Jul-02 RMS Added infinite loop message + 30-Apr-02 RMS Added CLR_TRAPS macro +*/ + +// #pragma message ("Loading vax_defs.h") + +#ifndef _VAX_DEFS_H +#define _VAX_DEFS_H 0 + +#ifndef VM_VAX +#define VM_VAX 0 +#endif + +/* Stops and aborts */ + +#define STOP_HALT 1 /* halt */ +#define STOP_IBKPT 2 /* breakpoint */ +#define STOP_CHMFI 3 /* chg mode IS */ +#define STOP_ILLVEC 4 /* illegal vector */ +#define STOP_INIE 5 /* exc in intexc */ +#define STOP_PPTE 6 /* proc pte in Px */ +#define STOP_UIPL 7 /* undefined IPL */ +#define STOP_RQ 8 /* fatal RQ err */ +#define STOP_LOOP 9 /* infinite loop */ +#define STOP_SANITY 10 /* sanity timer exp */ +#define STOP_SWDN 11 /* software done (780) */ +#define STOP_BOOT 12 /* reboot (780) */ +#define STOP_UNKNOWN 13 /* unknown reason */ +#define STOP_UNKABO 14 /* unknown abort */ +#define STOP_INVSYSOP 15 /* invalid system operation */ +#define ABORT_INTR -1 /* interrupt */ +#define ABORT_MCHK (-SCB_MCHK) /* machine check */ +#define ABORT_RESIN (-SCB_RESIN) /* rsvd instruction */ +#define ABORT_RESAD (-SCB_RESAD) /* rsvd addr mode */ +#define ABORT_RESOP (-SCB_RESOP) /* rsvd operand */ +#define ABORT_CMODE (-SCB_CMODE) /* comp mode fault */ +#define ABORT_ARITH (-SCB_ARITH) /* arithmetic trap */ +#define ABORT_ACV (-SCB_ACV) /* access violation */ +#define ABORT_TNV (-SCB_TNV) /* transl not vaid */ +#define ABORT_INVSYSOP (- (0x200 * 30 + 1)) /* illegal system operation */ +#define ABORT(x) throw_sim_exception_ABORT(RUN_PASS, (x)) +#define ABORT_INVALID_SYSOP ABORT (ABORT_INVSYSOP) +#define RSVD_INST_FAULT ABORT (ABORT_RESIN) +#define RSVD_ADDR_FAULT ABORT (ABORT_RESAD) +#define RSVD_OPND_FAULT ABORT (ABORT_RESOP) +#define FLT_OVFL_FAULT fault_p1 = FLT_OVRFLO, ABORT (ABORT_ARITH) +#define FLT_DZRO_FAULT fault_p1 = FLT_DIVZRO, ABORT (ABORT_ARITH) +#define FLT_UNFL_FAULT fault_p1 = FLT_UNDFLO, ABORT (ABORT_ARITH) +#define CMODE_FAULT(cd) fault_p1 = (cd), ABORT (ABORT_CMODE) +#define MACH_CHECK(cd) fault_p1 = (cd), ABORT (ABORT_MCHK) + +/* Recovery queue */ + +#define RQ_RN 0xF /* register */ +#define RQ_V_LNT 4 /* length */ +#define RQ_M_LNT 0x7 /* 0,1,2,3,4 */ +#define RQ_DIR 0x800 /* 0 = -, 1 = + */ +#define RQ_REC(d,r) (((d) << RQ_V_LNT) | (r)) +#define RQ_GETRN(x) ((x) & RQ_RN) +#define RQ_GETLNT(x) (((x) >> RQ_V_LNT) & RQ_M_LNT) + +/* Address space */ + +#define VAMASK 0xFFFFFFFF /* virt addr mask */ +#define PAWIDTH 30 /* phys addr width */ +#define PASIZE (1 << PAWIDTH) /* phys addr size */ +#define PAMASK (PASIZE - 1) /* phys addr mask */ +#define IOPAGE (1 << (PAWIDTH - 1)) /* start of I/O page */ + +/* Architectural constants */ + +#define BMASK 0x000000FF /* byte */ +#define BSIGN 0x00000080 +#define WMASK 0x0000FFFF /* word */ +#define WSIGN 0x00008000 +#define LMASK 0xFFFFFFFF /* longword */ +#define LSIGN 0x80000000 +#define FPSIGN 0x00008000 /* floating point */ +#define L_BYTE 1 /* bytes per */ +#define L_WORD 2 /* data type */ +#define L_LONG 4 +#define L_QUAD 8 +#define L_OCTA 16 +#define NUM_INST 512 /* one byte+two byte */ +#define MAX_SPEC 6 /* max spec/instr */ + +/* Floating point formats */ + +#define FD_V_EXP 7 /* f/d exponent */ +#define FD_M_EXP 0xFF +#define FD_BIAS 0x80 /* f/d bias */ +#define FD_EXP (FD_M_EXP << FD_V_EXP) +#define FD_HB (1 << FD_V_EXP) /* f/d hidden bit */ +#define FD_GUARD (15 - FD_V_EXP) /* # guard bits */ +#define FD_GETEXP(x) (((x) >> FD_V_EXP) & FD_M_EXP) + +#define G_V_EXP 4 /* g exponent */ +#define G_M_EXP 0x7FF +#define G_BIAS 0x400 /* g bias */ +#define G_EXP (G_M_EXP << G_V_EXP) +#define G_HB (1 << G_V_EXP) /* g hidden bit */ +#define G_GUARD (15 - G_V_EXP) /* # guard bits */ +#define G_GETEXP(x) (((x) >> G_V_EXP) & G_M_EXP) + +#define H_V_EXP 0 /* h exponent */ +#define H_M_EXP 0x7FFF +#define H_BIAS 0x4000 /* h bias */ +#define H_EXP (H_M_EXP << H_V_EXP) +#define H_HB (1 << H_V_EXP) /* h hidden bit */ +#define H_GUARD (15 - H_V_EXP) /* # guard bits */ +#define H_GETEXP(x) (((x) >> H_V_EXP) & H_M_EXP) + +/* Memory management modes */ + +#define KERN 0 +#define EXEC 1 +#define SUPV 2 +#define USER 3 + +/* Register and stack aliases */ + +#define nAP 12 +#define nFP 13 +#define nSP 14 +#define nPC 15 +#define AP R[nAP] +#define FP R[nFP] +#define SP R[nSP] +#define PC R[nPC] +#define RGMASK 0xF +#define KSP STK[KERN] +#define ESP STK[EXEC] +#define SSP STK[SUPV] +#define USP STK[USER] +#define IS STK[4] + +/* PSL, PSW, and condition codes */ + +#define PSL_V_CM 31 /* compatibility mode */ +#define PSL_CM (1u << PSL_V_CM) +#define PSL_V_TP 30 /* trace pending */ +#define PSL_TP (1 << PSL_V_TP) +#define PSL_V_FPD 27 /* first part done */ +#define PSL_FPD (1 << PSL_V_FPD) +#define PSL_V_IS 26 /* interrupt stack */ +#define PSL_IS (1 << PSL_V_IS) +#define PSL_V_CUR 24 /* current mode */ +#define PSL_V_PRV 22 /* previous mode */ +#define PSL_M_MODE 0x3 /* mode mask */ +#define PSL_CUR (PSL_M_MODE << PSL_V_CUR) +#define PSL_PRV (PSL_M_MODE << PSL_V_PRV) +#define PSL_V_IPL 16 /* int priority lvl */ +#define PSL_M_IPL 0x1F +#define PSL_IPL (PSL_M_IPL << PSL_V_IPL) +#define PSL_IPL1 (0x01 << PSL_V_IPL) +#define PSL_IPL17 (0x17 << PSL_V_IPL) +#define PSL_IPL1F (0x1F << PSL_V_IPL) +#define PSL_MBZ (0x30200000 | PSW_MBZ) /* must be zero */ +#define PSW_MBZ 0xFF00 /* must be zero */ +#define PSW_DV 0x80 /* dec ovflo enable */ +#define PSW_FU 0x40 /* flt undflo enable */ +#define PSW_IV 0x20 /* int ovflo enable */ +#define PSW_T 0x10 /* trace enable */ +#define CC_N 0x08 /* negative */ +#define CC_Z 0x04 /* zero */ +#define CC_V 0x02 /* overflow */ +#define CC_C 0x01 /* carry */ +#define CC_MASK (CC_N | CC_Z | CC_V | CC_C) +#define CC_V_N 3 /* negative (bit position) */ +#define CC_V_Z 2 /* zero (bit position) */ +#define CC_V_V 1 /* overflow (bit position) */ +#define CC_V_C 0 /* carry (bit position) */ +#define PSL_GETCUR(x) (((x) >> PSL_V_CUR) & PSL_M_MODE) +#define PSL_GETPRV(x) (((x) >> PSL_V_PRV) & PSL_M_MODE) +#define PSL_GETIPL(x) (((x) >> PSL_V_IPL) & PSL_M_IPL) + +/* Software interrupt summary register */ + +#define SISR_MASK 0xFFFE +#define SISR_2 (1 << 2) + +/* AST register */ + +#define AST_MASK 7 +#define AST_MAX 4 + +/* Virtual address */ + +#define VA_N_OFF 9 /* offset size */ +#define VA_PAGSIZE (1u << VA_N_OFF) /* page size */ +#define VA_M_OFF ((1u << VA_N_OFF) - 1) /* offset mask */ +#define VA_V_VPN VA_N_OFF /* vpn start */ +#define VA_N_VPN (31 - VA_N_OFF) /* vpn size */ +#define VA_M_VPN ((1u << VA_N_VPN) - 1) /* vpn mask */ +#define VA_S0 (1u << 31) /* S0 space */ +#define VA_P1 (1u << 30) /* P1 space */ +#define VA_N_TBI 12 /* TB index size */ +#define VA_TBSIZE (1u << VA_N_TBI) /* TB size */ +#define VA_M_TBI ((1u << VA_N_TBI) - 1) /* TB index mask */ +#define VA_GETOFF(x) ((x) & VA_M_OFF) +#define VA_GETVPN(x) (((x) >> VA_V_VPN) & VA_M_VPN) +#define VA_GETTBI(x) ((x) & VA_M_TBI) + +/* PTE */ + +#define PTE_V_V 31 /* valid */ +#define PTE_V (1u << PTE_V_V) +#define PTE_V_ACC 27 /* access */ +#define PTE_M_ACC 0xF +#define PTE_ACC (PTE_M_ACC << PTE_V_ACC) +#define PTE_V_M 26 /* modified */ +#define PTE_M (1u << PTE_V_M) +#define PTE_GETACC(x) (((x) >> PTE_V_ACC) & PTE_M_ACC) + +/* TLB entry */ + +#define TLB_V_RACC 0 /* rd acc field */ +#define TLB_V_WACC 4 /* wr acc field */ +#define TLB_M_ACC 0xF +#define TLB_RACC (TLB_M_ACC << TLB_V_RACC) +#define TLB_WACC (TLB_M_ACC << TLB_V_WACC) +#define TLB_V_M 8 /* m bit */ +#define TLB_M (1u << TLB_V_M) +#define TLB_N_PFN (PAWIDTH - VA_N_OFF) /* ppfn size */ +#define TLB_M_PFN ((1u << TLB_N_PFN) - 1) /* ppfn mask */ +#define TLB_PFN (TLB_M_PFN << VA_V_VPN) + +/* Traps and interrupt requests */ + +#define TIR_V_IRQL 0 /* int request lvl */ +#define TIR_V_TRAP 5 /* trap requests */ +#define TIR_M_TRAP 07 +#define TIR_TRAP (TIR_M_TRAP << TIR_V_TRAP) +#define TRAP_INTOV (1 << TIR_V_TRAP) /* integer overflow */ +#define TRAP_DIVZRO (2 << TIR_V_TRAP) /* divide by zero */ +#define TRAP_FLTOVF (3 << TIR_V_TRAP) /* flt overflow */ +#define TRAP_FLTDIV (4 << TIR_V_TRAP) /* flt/dec div by zero */ +#define TRAP_FLTUND (5 << TIR_V_TRAP) /* flt underflow */ +#define TRAP_DECOVF (6 << TIR_V_TRAP) /* decimal overflow */ +#define TRAP_SUBSCR (7 << TIR_V_TRAP) /* subscript range */ +#define SET_TRAP(x) trpirq = (trpirq & PSL_M_IPL) | (x) +#define CLR_TRAPS trpirq = trpirq & ~TIR_TRAP +#define SET_IRQL trpirq = (trpirq & TIR_TRAP) | eval_int (RUN_PASS, TRUE) +#define SET_IRQL_NOSYNC trpirq = (trpirq & TIR_TRAP) | eval_int (RUN_PASS, FALSE) +#define GET_TRAP(x) (((x) >> TIR_V_TRAP) & TIR_M_TRAP) +#define GET_IRQL(x) (((x) >> TIR_V_IRQL) & PSL_M_IPL) + +/* Floating point fault parameters */ + +#define FLT_OVRFLO 0x8 /* flt overflow */ +#define FLT_DIVZRO 0x9 /* flt div by zero */ +#define FLT_UNDFLO 0xA /* flt underflow */ + +/* Compatability mode fault parameters */ + +#define CMODE_RSVI 0x0 /* reserved instr */ +#define CMODE_BPT 0x1 /* BPT */ +#define CMODE_IOT 0x2 /* IOT */ +#define CMODE_EMT 0x3 /* EMT */ +#define CMODE_TRAP 0x4 /* TRAP */ +#define CMODE_ILLI 0x5 /* illegal instr */ +#define CMODE_ODD 0x6 /* odd address */ + +/* EDITPC suboperators */ + +#define EO_END 0x00 /* end */ +#define EO_END_FLOAT 0x01 /* end float */ +#define EO_CLR_SIGNIF 0x02 /* clear signif */ +#define EO_SET_SIGNIF 0x03 /* set signif */ +#define EO_STORE_SIGN 0x04 /* store sign */ +#define EO_LOAD_FILL 0x40 /* load fill */ +#define EO_LOAD_SIGN 0x41 /* load sign */ +#define EO_LOAD_PLUS 0x42 /* load sign if + */ +#define EO_LOAD_MINUS 0x43 /* load sign if - */ +#define EO_INSERT 0x44 /* insert */ +#define EO_BLANK_ZERO 0x45 /* blank zero */ +#define EO_REPL_SIGN 0x46 /* replace sign */ +#define EO_ADJUST_LNT 0x47 /* adjust length */ +#define EO_FILL 0x80 /* fill */ +#define EO_MOVE 0x90 /* move */ +#define EO_FLOAT 0xA0 /* float */ +#define EO_RPT_MASK 0x0F /* rpt mask */ +#define EO_RPT_FLAG 0x80 /* rpt flag */ + +/* EDITPC R2 packup parameters */ + +#define ED_V_CC 16 /* condition codes */ +#define ED_M_CC 0xFF +#define ED_CC (ED_M_CC << ED_V_CC) +#define ED_V_SIGN 8 /* sign */ +#define ED_M_SIGN 0xFF +#define ED_SIGN (ED_M_SIGN << ED_V_SIGN) +#define ED_V_FILL 0 /* fill */ +#define ED_M_FILL 0xFF +#define ED_FILL (ED_M_FILL << ED_V_FILL) +#define ED_GETCC(x) (((x) >> ED_V_CC) & CC_MASK) +#define ED_GETSIGN(x) (((x) >> ED_V_SIGN) & ED_M_SIGN) +#define ED_GETFILL(x) (((x) >> ED_V_FILL) & ED_M_FILL) +#define ED_PUTCC(r,x) (((r) & ~ED_CC) | (((x) << ED_V_CC) & ED_CC)) +#define ED_PUTSIGN(r,x) (((r) & ~ED_SIGN) | (((x) << ED_V_SIGN) & ED_SIGN)) +#define ED_PUTFILL(r,x) (((r) & ~ED_FILL) | (((x) << ED_V_FILL) & ED_FILL)) + +/* SCB offsets */ + +#define SCB_MCHK 0x04 /* machine chk */ +#define SCB_KSNV 0x08 /* ker stk invalid */ +#define SCB_PWRFL 0x0C /* power fail */ +#define SCB_RESIN 0x10 /* rsvd/priv instr */ +#define SCB_XFC 0x14 /* XFC instr */ +#define SCB_RESOP 0x18 /* rsvd operand */ +#define SCB_RESAD 0x1C /* rsvd addr mode */ +#define SCB_ACV 0x20 /* ACV */ +#define SCB_TNV 0x24 /* TNV */ +#define SCB_TP 0x28 /* trace pending */ +#define SCB_BPT 0x2C /* BPT instr */ +#define SCB_CMODE 0x30 /* comp mode fault */ +#define SCB_ARITH 0x34 /* arith fault */ +#define SCB_CHMK 0x40 /* CHMK */ +#define SCB_CHME 0x44 /* CHME */ +#define SCB_CHMS 0x48 /* CHMS */ +#define SCB_CHMU 0x4C /* CHMU */ +#define SCB_CRDERR 0x54 /* CRD err intr */ +#define SCB_MEMERR 0x60 /* mem err intr */ +#define SCB_IPLSOFT 0x80 /* software intr base */ +#define SCB_IPINTR 0x80 /* interprocessor intr (overlays unused softint 0) */ +#define SCB_INTTIM 0xC0 /* timer intr */ +#define SCB_EMULATE 0xC8 /* emulation */ +#define SCB_EMULFPD 0xCC /* emulation, FPD */ +#define SCB_CSI 0xF0 /* constor input */ +#define SCB_CSO 0xF4 /* constor output */ +#define SCB_TTI 0xF8 /* console input */ +#define SCB_TTO 0xFC /* console output */ +#define SCB_INTR 0x100 /* hardware intr */ + +#define IPL_HLTPIN 0x1F /* halt pin IPL */ +#define IPL_MEMERR 0x1D /* mem err IPL */ +#define IPL_CRDERR 0x1A /* CRD err IPL */ + +/* Interrupt and exception types */ + +#define IE_SVE -1 /* severe exception */ +#define IE_EXC 0 /* normal exception */ +#define IE_INT 1 /* interrupt */ + +/* Decode ROM: opcode entry */ + +#define DR_F 0x80 /* FPD ok flag */ +#define DR_NSPMASK 0x07 /* #specifiers */ +#define DR_V_USPMASK 4 +#define DR_M_USPMASK 0x70 /* #spec, sym_ */ +#define DR_GETNSP(x) ((x) & DR_NSPMASK) +#define DR_GETUSP(x) (((x) >> DR_V_USPMASK) & DR_M_USPMASK) + +/* Decode ROM: specifier entry */ + +#define DR_ACMASK 0x300 /* type */ +#define DR_SPFLAG 0x008 /* special decode */ +#define DR_LNMASK 0x007 /* length mask */ +#define DR_LNT(x) (1 << (x & DR_LNMASK)) /* disp to lnt */ + +/* Decode ROM: length */ + +#define DR_BYTE 0x000 /* byte */ +#define DR_WORD 0x001 /* word */ +#define DR_LONG 0x002 /* long */ +#define DR_QUAD 0x003 /* quad */ +#define DR_OCTA 0x004 /* octa */ + +/* Decode ROM: operand type */ + +#define SH0 0x000 /* short literal */ +#define SH1 0x010 +#define SH2 0x020 +#define SH3 0x030 +#define IDX 0x040 /* indexed */ +#define GRN 0x050 /* register */ +#define RGD 0x060 /* register def */ +#define ADC 0x070 /* autodecrement */ +#define AIN 0x080 /* autoincrement */ +#define AID 0x090 /* autoinc def */ +#define BDP 0x0A0 /* byte disp */ +#define BDD 0x0B0 /* byte disp def */ +#define WDP 0x0C0 /* word disp */ +#define WDD 0x0D0 /* word disp def */ +#define LDP 0x0E0 /* long disp */ +#define LDD 0x0F0 /* long disp def */ + +/* Decode ROM: access type */ + +#define DR_R 0x000 /* read */ +#define DR_M 0x100 /* modify */ +#define DR_A 0x200 /* address */ +#define DR_W 0x300 /* write */ + +/* Decode ROM: access type and length */ + +#define RB (DR_R|DR_BYTE) +#define RW (DR_R|DR_WORD) +#define RL (DR_R|DR_LONG) +#define RQ (DR_R|DR_QUAD) +#define RO (DR_R|DR_OCTA) +#define MB (DR_M|DR_BYTE) +#define MW (DR_M|DR_WORD) +#define ML (DR_M|DR_LONG) +#define MQ (DR_M|DR_QUAD) +#define MO (DR_M|DR_OCTA) +#define AB (DR_A|DR_BYTE) +#define AW (DR_A|DR_WORD) +#define AL (DR_A|DR_LONG) +#define AQ (DR_A|DR_QUAD) +#define AO (DR_A|DR_OCTA) +#define WB (DR_W|DR_BYTE) +#define WW (DR_W|DR_WORD) +#define WL (DR_W|DR_LONG) +#define WQ (DR_W|DR_QUAD) +#define WO (DR_W|DR_OCTA) + +/* Special dispatches. + + vb = variable bit field, treated as wb except for register + rf = f_floating, treated as rl except for short literal + rd = d_floating, treated as rq except for short literal + rg = g_floating, treated as rq except for short literal + rh = h_floating, treated as ro except for short literal + bb = branch byte displacement + bw = branch word displacement + + Length field must be correct +*/ + +#define VB (DR_SPFLAG|WB) /* .vb */ +#define RF (DR_SPFLAG|RL) /* .rf */ +#define RD (DR_SPFLAG|RQ) /* .rd */ +#define RG (DR_SPFLAG|MQ) /* .rg */ +#define RH (DR_SPFLAG|RO) /* .rh */ +#define BB (DR_SPFLAG|WB|6) /* byte branch */ +#define BW (DR_SPFLAG|WB|7) /* word branch */ + +/* Probe results and memory management fault codes */ + +#define PR_ACV 0 /* ACV */ +#define PR_LNV 1 /* length viol */ +#define PR_PACV 2 /* pte ACV (780) */ +#define PR_PLNV 3 /* pte len viol */ +#define PR_TNV 4 /* TNV */ +// #define PR_TB 5 /* impossible */ +#define PR_PTNV 6 /* pte TNV */ +#define PR_OK 7 /* ok */ +#define MM_PARAM(w,p) (((w)? 4: 0) | ((p) & 3)) /* fault param */ + +/* Memory management errors */ + +#define MM_WRITE 4 /* write */ +#define MM_EMASK 3 /* against probe */ + +/* Privileged registers */ + +#define MT_KSP 0 +#define MT_ESP 1 +#define MT_SSP 2 +#define MT_USP 3 +#define MT_IS 4 +#define MT_P0BR 8 +#define MT_P0LR 9 +#define MT_P1BR 10 +#define MT_P1LR 11 +#define MT_SBR 12 +#define MT_SLR 13 +#define MT_CPUID 14 +#define MT_WHAMI 15 +#define MT_PCBB 16 +#define MT_SCBB 17 +#define MT_IPL 18 +#define MT_ASTLVL 19 +#define MT_SIRR 20 +#define MT_SISR 21 +#define MT_ICCS 24 +#define MT_NICR 25 +#define MT_ICR 26 +#define MT_TODR 27 +#define MT_CSRS 28 +#define MT_CSRD 29 +#define MT_CSTS 30 +#define MT_CSTD 31 +#define MT_RXCS 32 +#define MT_RXDB 33 +#define MT_TXCS 34 +#define MT_TXDB 35 +#define MT_MAPEN 56 +#define MT_TBIA 57 +#define MT_TBIS 58 +#define MT_PME 61 +#define MT_SID 62 +#define MT_TBCHK 63 + +/* Privileged processor register for SIMH <-> guest API, this register number is not used by any VAX model */ +#if defined(VM_VAX_MP) +# define MT_SIMH 254 +#endif + +#define BR_MASK 0xFFFFFFFC +#define LR_MASK 0x003FFFFF + +/* Opcodes */ + +enum opcodes { + HALT, NOP, REI, BPT, RET, RSB, LDPCTX, SVPCTX, + CVTPS, CVTSP, INDEX, CRC, PROBER, PROBEW, INSQUE, REMQUE, + BSBB, BRB, BNEQ, BEQL, BGTR, BLEQ, JSB, JMP, + BGEQ, BLSS, BGTRU, BLEQU, BVC, BVS, BGEQU, BLSSU, + ADDP4, ADDP6, SUBP4, SUBP6, CVTPT, MULP, CVTTP, DIVP, + MOVC3, CMPC3, SCANC, SPANC, MOVC5, CMPC5, MOVTC, MOVTUC, + BSBW, BRW, CVTWL, CVTWB, MOVP, CMPP3, CVTPL, CMPP4, + EDITPC, MATCHC, LOCC, SKPC, MOVZWL, ACBW, MOVAW, PUSHAW, + ADDF2, ADDF3, SUBF2, SUBF3, MULF2, MULF3, DIVF2, DIVF3, + CVTFB, CVTFW, CVTFL, CVTRFL, CVTBF, CVTWF, CVTLF, ACBF, + MOVF, CMPF, MNEGF, TSTF, EMODF, POLYF, CVTFD, + ADAWI = 0x58, INSQHI = 0x5C, INSQTI, REMQHI, REMQTI, + ADDD2, ADDD3, SUBD2, SUBD3, MULD2, MULD3, DIVD2, DIVD3, + CVTDB, CVTDW, CVTDL, CVTRDL, CVTBD, CVTWD, CVTLD, ACBD, + MOVD, CMPD, MNEGD, TSTD, EMODD, POLYD, CVTDF, + ASHL = 0x78, ASHQ, EMUL, EDIV, CLRQ, MOVQ, MOVAQ, PUSHAQ, + ADDB2, ADDB3, SUBB2, SUBB3, MULB2, MULB3, DIVB2, DIVB3, + BISB2, BISB3, BICB2, BICB3, XORB2, XORB3, MNEGB, CASEB, + MOVB, CMPB, MCOMB, BITB, CLRB, TSTB, INCB, DECB, + CVTBL, CVTBW, MOVZBL, MOVZBW, ROTL, ACBB, MOVAB, PUSHAB, + ADDW2, ADDW3, SUBW2, SUBW3, MULW2, MULW3, DIVW2, DIVW3, + BISW2, BISW3, BICW2, BICW3, XORW2, XORW3, MNEGW, CASEW, + MOVW, CMPW, MCOMW, BITW, CLRW, TSTW, INCW, DECW, + BISPSW, BICPSW, POPR, PUSHR, CHMK, CHME, CHMS, CHMU, + ADDL2, ADDL3, SUBL2, SUBL3, MULL2, MULL3, DIVL2, DIVL3, + BISL2, BISL3, BICL2, BICL3, XORL2, XORL3, MNEGL, CASEL, + MOVL, CMPL, MCOML, BITL, CLRL, TSTL, INCL, DECL, + ADWC, SBWC, MTPR, MFPR, MOVPSL, PUSHL, MOVAL, PUSHAL, + BBS, BBC, BBSS, BBCS, BBSC, BBCC, BBSSI, BBCCI, + BLBS, BLBC, FFS, FFC, CMPV, CMPZV, EXTV, EXTZV, + INSV, ACBL, AOBLSS, AOBLEQ, SOBGEQ, SOBGTR, CVTLB, CVTLW, + ASHP, CVTLP, CALLG, CALLS, XFC, CVTDH = 0x132, CVTGF = 0x133, + ADDG2 = 0x140, ADDG3, SUBG2, SUBG3, MULG2, MULG3, DIVG2, DIVG3, + CVTGB, CVTGW, CVTGL, CVTRGL, CVTBG, CVTWG, CVTLG, ACBG, + MOVG, CMPG, MNEGG, TSTG, EMODG, POLYG, CVTGH, + ADDH2 = 0x160, ADDH3, SUBH2, SUBH3, MULH2, MULH3, DIVH2, DIVH3, + CVTHB, CVTHW, CVTHL, CVTRHL, CVTBH, CVTWH, CVTLH, ACBH, + MOVH, CMPH, MNEGH, TSTH, EMODH, POLYH, CVTHG, + CLRO = 0x17C, MOVO, MOVAO, PUSHAO, + CVTFH = 0x198, CVTFG = 0x199, + CVTHF = 0x1F6, CVTHD = 0x1F7 }; + +/* Repeated operations */ + +#define SXTB(x) (((x) & BSIGN)? ((x) | ~BMASK): ((x) & BMASK)) +#define SXTW(x) (((x) & WSIGN)? ((x) | ~WMASK): ((x) & WMASK)) +#define SXTBW(x) (((x) & BSIGN)? ((x) | (WMASK - BMASK)): ((x) & BMASK)) +#define SXTL(x) (((x) & LSIGN)? ((x) | ~LMASK): ((x) & LMASK)) +#define INTOV if (PSL & PSW_IV) SET_TRAP (TRAP_INTOV) +#define V_INTOV cc = cc | CC_V; INTOV +#define NEG(x) ((~(x) + 1) & LMASK) + +/* Istream access */ + +// #define VAX_DIRECT_PREFETCH 0 + +#if !defined(VAX_DIRECT_PREFETCH) && (defined(__x86_32__) || defined(__x86_64__)) +# define VAX_DIRECT_PREFETCH 1 +#endif + +#if !defined(VAX_DIRECT_PREFETCH) +# define VAX_DIRECT_PREFETCH 0 +#endif + +#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] = fault_PC +#define GET_ISTR(d,l) d = get_istr (RUN_PASS, l, acc) + +#if 0 +# define GET_ISTR_B(d) d = get_istr (RUN_PASS, L_BYTE, acc) +# define GET_ISTR_W(d) d = get_istr (RUN_PASS, L_WORD, acc) +# define GET_ISTR_L(d) d = get_istr (RUN_PASS, L_LONG, acc) +# define GET_ISTR_X(d, l) d = get_istr (RUN_PASS, (l), acc) +#elif VAX_DIRECT_PREFETCH +# define GET_ISTR_B(d) d = get_istr_b_dir (RUN_PASS, acc) +# define GET_ISTR_W(d) d = get_istr_w_dir (RUN_PASS, acc) +# define GET_ISTR_L(d) d = get_istr_l_dir (RUN_PASS, acc) +# define GET_ISTR_X(d, l) d = get_istr_x_dir (RUN_PASS, (l), acc) +#else +# define GET_ISTR_B(d) d = get_istr_b (RUN_PASS, acc) +# define GET_ISTR_W(d) d = get_istr_w (RUN_PASS, acc) +# define GET_ISTR_L(d) d = get_istr_l (RUN_PASS, acc) +# define GET_ISTR_X(d, l) d = get_istr_x (RUN_PASS, (l), acc) +#endif + +#if VAX_DIRECT_PREFETCH +# define FLUSH_ISTR mppc = NULL, mppc_rem = 0, ibcnt = 0, ppc = -1 +#else +# define FLUSH_ISTR ibcnt = 0, ppc = -1 +#endif + +#if VAX_DIRECT_PREFETCH +void cpu_branch(int32 d); +# define BRANCHB(d) PCQ_ENTRY, cpu_branch(RUN_PASS, SXTB(d)) +# define BRANCHW(d) PCQ_ENTRY, cpu_branch(RUN_PASS, SXTW(d)) +#else +# define BRANCHB(d) PCQ_ENTRY, PC = PC + SXTB (d), FLUSH_ISTR +# define BRANCHW(d) PCQ_ENTRY, PC = PC + SXTW (d), FLUSH_ISTR +#endif +#define JUMP(d) PCQ_ENTRY, PC = (d), FLUSH_ISTR +#define CMODE_JUMP(d) PCQ_ENTRY, PC = (d) +#define SETPC(d) PC = (d), FLUSH_ISTR + +/* Character string instructions */ + +#define STR_V_DPC 24 /* delta PC */ +#define STR_M_DPC 0xFF +#define STR_V_CHR 16 /* char argument */ +#define STR_M_CHR 0xFF +#define STR_LNMASK 0xFFFF /* string length */ +#define STR_GETDPC(x) (((x) >> STR_V_DPC) & STR_M_DPC) +#define STR_GETCHR(x) (((x) >> STR_V_CHR) & STR_M_CHR) +#define STR_PACK(m,x) ((((PC - fault_PC) & STR_M_DPC) << STR_V_DPC) | \ + (((m) & STR_M_CHR) << STR_V_CHR) | ((x) & STR_LNMASK)) + +/* Read and write */ + +#define RA (acc) +#define WA ((acc) << TLB_V_WACC) +#define ACC_MASK(x) (1 << (x)) +#define TLB_ACCR(x) (ACC_MASK (x) << TLB_V_RACC) +#define TLB_ACCW(x) (ACC_MASK (x) << TLB_V_WACC) +#define REF_V 0 +#define REF_P 1 + +/* Condition code macros */ + +#define CC_ZZ1P cc = CC_Z | (cc & CC_C) + +#define CC_IIZZ_B(r) \ + if ((r) & BSIGN) cc = CC_N; \ + else if ((r) == 0) cc = CC_Z; \ + else cc = 0 +#define CC_IIZZ_W(r) \ + if ((r) & WSIGN) cc = CC_N; \ + else if ((r) == 0) cc = CC_Z; \ + else cc = 0 +#define CC_IIZZ_L(r) \ + if ((r) & LSIGN) cc = CC_N; \ + else if ((r) == 0) cc = CC_Z; \ + else cc = 0 +#define CC_IIZZ_Q(rl,rh) \ + if ((rh) & LSIGN) cc = CC_N; \ + else if (((rl) | (rh)) == 0) cc = CC_Z; \ + else cc = 0 +#define CC_IIZZ_FP CC_IIZZ_W + +#define CC_IIZP_B(r) \ + if ((r) & BSIGN) cc = CC_N | (cc & CC_C); \ + else if ((r) == 0) cc = CC_Z | (cc & CC_C); \ + else cc = cc & CC_C +#define CC_IIZP_W(r) \ + if ((r) & WSIGN) cc = CC_N | (cc & CC_C); \ + else if ((r) == 0) cc = CC_Z | (cc & CC_C); \ + else cc = cc & CC_C +#define CC_IIZP_L(r) \ + if ((r) & LSIGN) cc = CC_N | (cc & CC_C); \ + else if ((r) == 0) cc = CC_Z | (cc & CC_C); \ + else cc = cc & CC_C +#define CC_IIZP_Q(rl,rh) \ + if ((rh) & LSIGN) cc = CC_N | (cc & CC_C); \ + else if (((rl) | (rh)) == 0) cc = CC_Z | (cc & CC_C); \ + else cc = cc & CC_C +#define CC_IIZP_O(rl,rm2,rm1,rh) \ + if ((rh) & LSIGN) cc = CC_N | (cc & CC_C); \ + else if (((rl) | (rm2) | (rm1) | (rh)) == 0) cc = CC_Z | (cc & CC_C); \ + else cc = cc & CC_C +#define CC_IIZP_FP CC_IIZP_W + +#define V_ADD_B(r,s1,s2) \ + if (((~(s1) ^ (s2)) & ((s1) ^ (r))) & BSIGN) { V_INTOV; } +#define V_ADD_W(r,s1,s2) \ + if (((~(s1) ^ (s2)) & ((s1) ^ (r))) & WSIGN) { V_INTOV; } +#define V_ADD_L(r,s1,s2) \ + if (((~(s1) ^ (s2)) & ((s1) ^ (r))) & LSIGN) { V_INTOV; } +#define C_ADD(r,s1,s2) \ + if (((uint32) r) < ((uint32) s2)) cc = cc | CC_C + +#define CC_ADD_B(r,s1,s2) \ + CC_IIZZ_B (r); \ + V_ADD_B (r, s1, s2); \ + C_ADD (r, s1, s2) +#define CC_ADD_W(r,s1,s2) \ + CC_IIZZ_W (r); \ + V_ADD_W (r, s1, s2); \ + C_ADD (r, s1, s2) +#define CC_ADD_L(r,s1,s2) \ + CC_IIZZ_L (r); \ + V_ADD_L (r, s1, s2); \ + C_ADD (r, s1, s2) + +#define V_SUB_B(r,s1,s2) \ + if ((((s1) ^ (s2)) & (~(s1) ^ (r))) & BSIGN) { V_INTOV; } +#define V_SUB_W(r,s1,s2) \ + if ((((s1) ^ (s2)) & (~(s1) ^ (r))) & WSIGN) { V_INTOV; } +#define V_SUB_L(r,s1,s2) \ + if ((((s1) ^ (s2)) & (~(s1) ^ (r))) & LSIGN) { V_INTOV; } +#define C_SUB(r,s1,s2) \ + if (((uint32) s2) < ((uint32) s1)) cc = cc | CC_C + +#define CC_SUB_B(r,s1,s2) \ + CC_IIZZ_B (r); \ + V_SUB_B (r, s1, s2); \ + C_SUB (r, s1, s2) +#define CC_SUB_W(r,s1,s2) \ + CC_IIZZ_W (r); \ + V_SUB_W (r, s1, s2); \ + C_SUB (r, s1, s2) +#define CC_SUB_L(r,s1,s2) \ + CC_IIZZ_L (r); \ + V_SUB_L (r, s1, s2); \ + C_SUB (r, s1, s2) + +#define CC_CMP_B(s1,s2) \ + if (SXTB (s1) < SXTB (s2)) cc = CC_N; \ + else if ((s1) == (s2)) cc = CC_Z; \ + else cc = 0; \ + if (((uint32) s1) < ((uint32) s2)) cc = cc | CC_C +#define CC_CMP_W(s1,s2) \ + if (SXTW (s1) < SXTW (s2)) cc = CC_N; \ + else if ((s1) == (s2)) cc = CC_Z; \ + else cc = 0; \ + if (((uint32) s1) < ((uint32) s2)) cc = cc | CC_C +#define CC_CMP_L(s1,s2) \ + if ((s1) < (s2)) cc = CC_N; \ + else if ((s1) == (s2)) cc = CC_Z; \ + else cc = 0; \ + if (((uint32) s1) < ((uint32) s2)) cc = cc | CC_C + +#include "sim_defs.h" + +extern int32 eval_int (RUN_DECL, t_bool sync); + +/* TOY clock ticks per second */ + +#define CLK_TPS 100 + +/* Model dependent definitions */ + +#if defined (VAX_780) +#include "vax780_defs.h" +#else +#include "vaxmod_defs.h" +#endif + +extern DEVICE cpu_dev; +extern DEVICE tlb_dev; +extern DEVICE rom_dev; +extern DEVICE nvr_dev; +extern DEVICE sysd_dev; +extern DEVICE qba_dev; +extern DEVICE tti_dev, tto_dev; +extern DEVICE cr_dev; +extern DEVICE lpt_dev; +extern DEVICE clk_dev; +extern DEVICE rq_dev, rqb_dev, rqc_dev, rqd_dev; +extern DEVICE rl_dev; +extern DEVICE ry_dev; +extern DEVICE ts_dev; +extern DEVICE tq_dev; +extern DEVICE dz_dev; +extern DEVICE csi_dev, cso_dev; +extern DEVICE xq_dev, xqb_dev; +extern DEVICE vh_dev; + +void set_map_reg(RUN_DECL); +void zap_tb(RUN_DECL, int stb, t_bool keep_prefetch = FALSE); +void zap_tb_ent(RUN_DECL, uint32 va); +int32 Test (RUN_DECL, uint32 va, int32 acc, int32 *status); +int32 TestMark (RUN_DECL, uint32 va, int32 acc, int32 *status); +t_bool chk_tb_ent(RUN_DECL, uint32 va); +int32 ReadIPR(RUN_DECL, int32 rg); +void WriteIPR(RUN_DECL, int32 rg, int32 val, t_bool& set_irql); +t_bool BadCmPSL(RUN_DECL, int32 newpsl); +void cpu_setup_secondary_model_specific(RUN_DECL, CPU_UNIT* local_cpu); +void cpu_prefault_memory(); +void cpu_on_rom_rd(RUN_DECL); +void cpu_shutdown_secondaries(RUN_DECL); +void cpu_once_a_second(RUN_DECL); + +/* + * Definitions of the API for communication between guest and VAX MP VM + */ + +#define VAXMP_API_SIGNATURE 0x484D4953 + +#define VAXMP_API_OP_QUERY 1 +#define VAXMP_API_OP_MEMBAR 2 +#define VAXMP_API_OP_INIT_SMP 3 +#define VAXMP_API_OP_START_CPU 4 +#define VAXMP_API_OP_IPINTR 5 +#define VAXMP_API_OP_IDLE 6 +#define VAXMP_API_OP_IDLE_PULSE 7 +#define VAXMP_API_OP_SET_IDLE 8 +#define VAXMP_API_OP_GETTIME_VMS 9 + +#define VAXMP_VM_ID 0x504D5856 /* 'VXMP' */ + +#define VAXMP_SMP_OPTION_PORTABLE_INTERLOCK (1 << 0) +#define VAXMP_SMP_OPTION_NATIVE_INTERLOCK (1 << 1) + +#define VAXMP_API_OS_UNKNOWN 0 +#define VAXMP_API_OS_VMS 1 + +#endif /* _VAX_DEFS_H */ diff --git a/src/VAX/vax_fpa.cpp b/src/VAX/vax_fpa.cpp new file mode 100644 index 0000000..c4c0ab6 --- /dev/null +++ b/src/VAX/vax_fpa.cpp @@ -0,0 +1,1582 @@ +/* vax_fpa.c - VAX f_, d_, g_floating instructions + + Copyright (c) 1998-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. + + 28-May-08 RMS Inlined physical memory routines + 16-May-06 RMS Fixed bug in 32b floating multiply routine + Fixed bug in 64b extended modulus routine + 03-May-06 RMS Fixed POLYD, POLYG to clear R4, R5 + Fixed POLYD, POLYG to set R3 correctly + Fixed POLYD, POLYG to not exit prematurely if arg = 0 + Fixed POLYD, POLYG to do full 64b multiply + Fixed POLYF, POLYD, POLYG to remove truncation on add + Fixed POLYF, POLYD, POLYG to mask mul reslt to 31b/63b/63b + Fixed fp add routine to test for zero via fraction + to support "denormal" argument from POLYF, POLYD, POLYG + (all reported by Tim Stark) + 27-Sep-05 RMS Fixed bug in 32b structure definitions (from Jason Stevens) + 30-Sep-04 RMS Comment and formating changes based on vax_octa.c + 18-Apr-04 RMS Moved format definitions to vax_defs.h + 19-Jun-03 RMS Simplified add algorithm + 16-May-03 RMS Fixed bug in floating to integer convert overflow + Fixed multiple bugs in EMODx + Integrated 32b only code + 05-Jul-02 RMS Changed internal routine names for C library conflict + 17-Apr-02 RMS Fixed bug in EDIV zero quotient + + This module contains the instruction simulators for + + - 64 bit arithmetic (ASHQ, EMUL, EDIV) + - single precision floating point + - double precision floating point, D and G format +*/ + +#include "sim_defs.h" +#include "vax_defs.h" +#include "vax_cpu.h" + +#if defined (USE_INT64) + +#define M64 0xFFFFFFFFFFFFFFFF /* 64b */ +#define FD_FRACW (0xFFFF & ~(FD_EXP | FPSIGN)) +#define FD_FRACL (FD_FRACW | 0xFFFF0000) /* f/d fraction */ +#define G_FRACW (0xFFFF & ~(G_EXP | FPSIGN)) +#define G_FRACL (G_FRACW | 0xFFFF0000) /* g fraction */ +#define UNSCRAM(h,l) (((((t_uint64) (h)) << 48) & 0xFFFF000000000000) | \ + ((((t_uint64) (h)) << 16) & 0x0000FFFF00000000) | \ + ((((t_uint64) (l)) << 16) & 0x00000000FFFF0000) | \ + ((((t_uint64) (l)) >> 16) & 0x000000000000FFFF)) +#define CONCAT(h,l) ((((t_uint64) (h)) << 32) | ((uint32) (l))) + +typedef struct { + int32 sign; + int32 exp; + t_uint64 frac; + } UFP; + +#define UF_NM 0x8000000000000000 /* normalized */ +#define UF_FRND 0x0000008000000000 /* F round */ +#define UF_DRND 0x0000000000000080 /* D round */ +#define UF_GRND 0x0000000000000400 /* G round */ +#define UF_V_NM 63 +#define UF_V_FDHI 40 +#define UF_V_FDLO (UF_V_FDHI - 32) +#define UF_V_GHI 43 +#define UF_V_GLO (UF_V_GHI - 32) +#define UF_GETFDHI(x) (int32) ((((x) >> (16 + UF_V_FDHI)) & FD_FRACW) | \ + (((x) >> (UF_V_FDHI - 16)) & ~0xFFFF)) +#define UF_GETFDLO(x) (int32) ((((x) >> (16 + UF_V_FDLO)) & 0xFFFF) | \ + (((x) << (16 - UF_V_FDLO)) & ~0xFFFF)) +#define UF_GETGHI(x) (int32) ((((x) >> (16 + UF_V_GHI)) & G_FRACW) | \ + (((x) >> (UF_V_GHI - 16)) & ~0xFFFF)) +#define UF_GETGLO(x) (int32) ((((x) >> (16 + UF_V_GLO)) & 0xFFFF) | \ + (((x) << (16 - UF_V_GLO)) & ~0xFFFF)) + +void unpackf (int32 hi, UFP *a); +void unpackd (int32 hi, int32 lo, UFP *a); +void unpackg (int32 hi, int32 lo, UFP *a); +void norm (UFP *a); +int32 rpackfd (RUN_DECL, UFP *a, int32 *rh); +int32 rpackg (RUN_DECL, UFP *a, int32 *rh); +void vax_fadd (UFP *a, UFP *b); +void vax_fmul (UFP *a, UFP *b, t_bool qd, int32 bias, uint32 mhi, uint32 mlo); +void vax_fdiv (RUN_DECL, UFP *b, UFP *a, int32 prec, int32 bias); +void vax_fmod (UFP *a, int32 bias, int32 *intgr, int32 *flg); + +/* Quadword arithmetic shift + + opnd[0] = shift count (cnt.rb) + opnd[1:2] = source (src.rq) + opnd[3:4] = destination (dst.wq) +*/ + +int32 op_ashq (RUN_DECL, int32 *opnd, int32 *rh, int32 *flg) +{ +t_int64 src, r; +int32 sc = opnd[0]; + +src = CONCAT (opnd[2], opnd[1]); /* build src */ +if (sc & BSIGN) { /* right shift? */ + *flg = 0; /* no ovflo */ + sc = 0x100 - sc; /* |shift| */ + if (sc > 63) /* sc > 63? */ + r = (opnd[2] & LSIGN)? -1: 0; + else r = src >> sc; + } +else { + if (sc > 63) { /* left shift */ + r = 0; /* sc > 63? */ + *flg = (src != 0); /* ovflo test */ + } + else { + r = src << sc; /* do shift */ + *flg = (src != (r >> sc)); /* ovflo test */ + } + } +*rh = (int32) ((r >> 32) & LMASK); /* hi result */ +return ((int32) (r & LMASK)); /* lo result */ +} + +/* Extended multiply subroutine */ + +int32 op_emul (RUN_DECL, int32 mpy, int32 mpc, int32 *rh) +{ +t_int64 lmpy = mpy; +t_int64 lmpc = mpc; + +lmpy = lmpy * lmpc; +*rh = (int32) ((lmpy >> 32) & LMASK); +return ((int32) (lmpy & LMASK)); +} + +/* Extended divide + + opnd[0] = divisor (non-zero) + opnd[1:2] = dividend +*/ + +int32 op_ediv (RUN_DECL, int32 *opnd, int32 *rh, int32 *flg) +{ +t_int64 ldvd, ldvr; +int32 quo, rem; + +*flg = CC_V; /* assume error */ +*rh = 0; +ldvr = ((opnd[0] & LSIGN)? -opnd[0]: opnd[0]) & LMASK; /* |divisor| */ +ldvd = CONCAT (opnd[2], opnd[1]); /* 64b dividend */ +if (opnd[2] & LSIGN) /* |dividend| */ + ldvd = -ldvd; +if (((ldvd >> 32) & LMASK) >= ldvr) /* divide work? */ + return opnd[1]; +quo = (int32) (ldvd / ldvr); /* do divide */ +rem = (int32) (ldvd % ldvr); +if ((opnd[0] ^ opnd[2]) & LSIGN) { /* result -? */ + quo = -quo; /* negate */ + if (quo && ((quo & LSIGN) == 0)) /* right sign? */ + return opnd[1]; + } +else if (quo & LSIGN) + return opnd[1]; +if (opnd[2] & LSIGN) /* sign of rem */ + rem = -rem; +*flg = 0; /* no overflow */ +*rh = rem & LMASK; /* set rem */ +return (quo & LMASK); /* return quo */ +} + +/* Compare floating */ + +int32 op_cmpfd (RUN_DECL, int32 h1, int32 l1, int32 h2, int32 l2) +{ +t_uint64 n1, n2; + +if ((h1 & FD_EXP) == 0) { + if (h1 & FPSIGN) + RSVD_OPND_FAULT; + h1 = l1 = 0; + } +if ((h2 & FD_EXP) == 0) { + if (h2 & FPSIGN) + RSVD_OPND_FAULT; + h2 = l2 = 0; + } +if ((h1 ^ h2) & FPSIGN) + return ((h1 & FPSIGN)? CC_N: 0); +n1 = UNSCRAM (h1, l1); +n2 = UNSCRAM (h2, l2); +if (n1 == n2) + return CC_Z; +return (((n1 < n2) ^ ((h1 & FPSIGN) != 0))? CC_N: 0); +} + +int32 op_cmpg (RUN_DECL, int32 h1, int32 l1, int32 h2, int32 l2) +{ +t_uint64 n1, n2; + +if ((h1 & G_EXP) == 0) { + if (h1 & FPSIGN) + RSVD_OPND_FAULT; + h1 = l1 = 0; + } +if ((h2 & G_EXP) == 0) { + if (h2 & FPSIGN) + RSVD_OPND_FAULT; + h2 = l2 = 0; + } +if ((h1 ^ h2) & FPSIGN) + return ((h1 & FPSIGN)? CC_N: 0); +n1 = UNSCRAM (h1, l1); +n2 = UNSCRAM (h2, l2); +if (n1 == n2) + return CC_Z; +return (((n1 < n2) ^ ((h1 & FPSIGN) != 0))? CC_N: 0); +} + +/* Integer to floating convert */ + +int32 op_cvtifdg (RUN_DECL, int32 val, int32 *rh, int32 opc) +{ +UFP a; + +if (val == 0) { + if (rh) + *rh = 0; + return 0; + } +if (val < 0) { + a.sign = FPSIGN; + val = - val; + } +else a.sign = 0; +a.exp = 32 + ((opc & 0x100)? G_BIAS: FD_BIAS); +a.frac = ((t_uint64) val) << (UF_V_NM - 31); +norm (&a); +if (opc & 0x100) + return rpackg (RUN_PASS, &a, rh); +return rpackfd (RUN_PASS, &a, rh); +} + +/* Floating to integer convert */ + +int32 op_cvtfdgi (RUN_DECL, int32 *opnd, int32 *flg, int32 opc) +{ +UFP a; +int32 lnt = opc & 03; +int32 ubexp; +const static t_uint64 maxv[4] = { 0x7F, 0x7FFF, 0x7FFFFFFF, 0x7FFFFFFF }; + +*flg = 0; +if (opc & 0x100) { + unpackg (opnd[0], opnd[1], &a); + ubexp = a.exp - G_BIAS; + } +else { + if (opc & 0x20) + unpackd (opnd[0], opnd[1], &a); + else unpackf (opnd[0], &a); + ubexp = a.exp - FD_BIAS; + } +if ((a.exp == 0) || (ubexp < 0)) + return 0; +if (ubexp <= UF_V_NM) { + a.frac = a.frac >> (UF_V_NM - ubexp); /* leave rnd bit */ + if ((opc & 03) == 03) /* if CVTR, round */ + a.frac = a.frac + 1; + a.frac = a.frac >> 1; /* now justified */ + if (a.frac > (maxv[lnt] + (a.sign? 1: 0))) + *flg = CC_V; + } +else { + *flg = CC_V; /* set overflow */ + if (ubexp > (UF_V_NM + 32)) + return 0; + a.frac = a.frac << (ubexp - UF_V_NM - 1); /* no rnd bit */ + } +return ((int32) ((a.sign? (a.frac ^ LMASK) + 1: a.frac) & LMASK)); +} + +/* Extended modularize + + One of three floating point instructions dropped from the architecture, + EMOD presents two sets of complications. First, it requires an extended + fraction multiply, with precise (and unusual) truncation conditions. + Second, it has two write operands, a dubious distinction it shares + with EDIV. +*/ + +int32 op_emodf (RUN_DECL, int32 *opnd, int32 *intgr, int32 *flg) +{ +UFP a, b; + +unpackf (opnd[0], &a); /* unpack operands */ +unpackf (opnd[2], &b); +a.frac = a.frac | (((t_uint64) opnd[1]) << 32); /* extend src1 */ +vax_fmul (&a, &b, 0, FD_BIAS, 0, LMASK); /* multiply */ +vax_fmod (&a, FD_BIAS, intgr, flg); /* sep int & frac */ +return rpackfd (RUN_PASS, &a, NULL); /* return frac */ +} + +int32 op_emodd (RUN_DECL, int32 *opnd, int32 *flo, int32 *intgr, int32 *flg) +{ +UFP a, b; + +unpackd (opnd[0], opnd[1], &a); /* unpack operands */ +unpackd (opnd[3], opnd[4], &b); +a.frac = a.frac | opnd[2]; /* extend src1 */ +vax_fmul (&a, &b, 1, FD_BIAS, 0, 0); /* multiply */ +vax_fmod (&a, FD_BIAS, intgr, flg); /* sep int & frac */ +return rpackfd (RUN_PASS, &a, flo); /* return frac */ +} + +int32 op_emodg (RUN_DECL, int32 *opnd, int32 *flo, int32 *intgr, int32 *flg) +{ +UFP a, b; + +unpackg (opnd[0], opnd[1], &a); /* unpack operands */ +unpackg (opnd[3], opnd[4], &b); +a.frac = a.frac | (opnd[2] >> 5); /* extend src1 */ +vax_fmul (&a, &b, 1, G_BIAS, 0, 0); /* multiply */ +vax_fmod (&a, G_BIAS, intgr, flg); /* sep int & frac */ +return rpackg (RUN_PASS, &a, flo); /* return frac */ +} + +/* Unpacked floating point routines */ + +void vax_fadd (UFP *a, UFP *b) +{ +int32 ediff; +UFP t; + +if (a->frac == 0) { /* s1 = 0? */ + *a = *b; + return; + } +if (b->frac == 0) /* s2 = 0? */ + return; +if ((a->exp < b->exp) || /* |s1| < |s2|? swap */ + ((a->exp == b->exp) && (a->frac < b->frac))) { + t = *a; + *a = *b; + *b = t; + } +ediff = a->exp - b->exp; /* exp diff */ +if (a->sign ^ b->sign) { /* eff sub? */ + if (ediff) { /* exp diff? */ + if (ediff > 63) /* retain sticky */ + b->frac = M64; + else b->frac = ((-((t_int64) b->frac) >> ediff) | /* denormalize */ + (M64 << (64 - ediff))); /* preserve sign */ + a->frac = a->frac + b->frac; /* add frac */ + } + else a->frac = a->frac - b->frac; /* sub frac */ + norm (a); /* normalize */ + } +else { + if (ediff > 63) /* add */ + b->frac = 0; + else if (ediff) /* denormalize */ + b->frac = b->frac >> ediff; + a->frac = a->frac + b->frac; /* add frac */ + if (a->frac < b->frac) { /* chk for carry */ + a->frac = UF_NM | (a->frac >> 1); /* shift in carry */ + a->exp = a->exp + 1; /* skip norm */ + } + } +return; +} + +/* Floating multiply - 64b * 64b with cross products */ + +void vax_fmul (UFP *a, UFP *b, t_bool qd, int32 bias, uint32 mhi, uint32 mlo) +{ +t_uint64 ah, bh, al, bl, rhi, rlo, rmid1, rmid2; +t_uint64 mask = (((t_uint64) mhi) << 32) | ((t_uint64) mlo); + +if ((a->exp == 0) || (b->exp == 0)) { /* zero argument? */ + a->frac = a->sign = a->exp = 0; /* result is zero */ + return; + } +a->sign = a->sign ^ b->sign; /* sign of result */ +a->exp = a->exp + b->exp - bias; /* add exponents */ +ah = (a->frac >> 32) & LMASK; /* split operands */ +bh = (b->frac >> 32) & LMASK; /* into 32b chunks */ +rhi = ah * bh; /* high result */ +if (qd) { /* 64b needed? */ + al = a->frac & LMASK; + bl = b->frac & LMASK; + rmid1 = ah * bl; + rmid2 = al * bh; + rlo = al * bl; + rhi = rhi + ((rmid1 >> 32) & LMASK) + ((rmid2 >> 32) & LMASK); + rmid1 = rlo + (rmid1 << 32); /* add mid1 to lo */ + if (rmid1 < rlo) /* carry? incr hi */ + rhi = rhi + 1; + rmid2 = rmid1 + (rmid2 << 32); /* add mid2 to lo */ + if (rmid2 < rmid1) /* carry? incr hi */ + rhi = rhi + 1; + } +a->frac = rhi & ~mask; +norm (a); /* normalize */ +return; +} + +/* Floating modulus - there are three cases + + exp <= bias - integer is 0, fraction is input, + no overflow + bias < exp <= bias+64 - separate integer and fraction, + integer overflow may occur + bias+64 < exp - result is integer, fraction is 0 + integer overflow +*/ + +void vax_fmod (UFP *a, int32 bias, int32 *intgr, int32 *flg) +{ +if (a->exp <= bias) /* 0 or <1? int = 0 */ + *intgr = *flg = 0; +else if (a->exp <= (bias + 64)) { /* in range [1,64]? */ + *intgr = (int32) (a->frac >> (64 - (a->exp - bias))); + if ((a->exp > (bias + 32)) || /* test ovflo */ + ((a->exp == (bias + 32)) && + (((uint32) *intgr) > (a->sign? 0x80000000: 0x7FFFFFFF)))) + *flg = CC_V; + else *flg = 0; + if (a->sign) /* -? comp int */ + *intgr = -*intgr; + if (a->exp == (bias + 64)) /* special case 64 */ + a->frac = 0; + else a->frac = a->frac << (a->exp - bias); + a->exp = bias; + } +else { + *intgr = 0; /* out of range */ + a->frac = a->sign = a->exp = 0; /* result 0 */ + *flg = CC_V; /* overflow */ + } +norm (a); /* normalize */ +return; +} + +/* Floating divide + Needs to develop at least one rounding bit. Since the first + divide step can fail, caller should specify 2 more bits than + the precision of the fraction. +*/ + +void vax_fdiv (RUN_DECL, UFP *a, UFP *b, int32 prec, int32 bias) +{ +int32 i; +t_uint64 quo = 0; + +if (a->exp == 0) /* divr = 0? */ + FLT_DZRO_FAULT; +if (b->exp == 0) /* divd = 0? */ + return; +b->sign = b->sign ^ a->sign; /* result sign */ +b->exp = b->exp - a->exp + bias + 1; /* unbiased exp */ +a->frac = a->frac >> 1; /* allow 1 bit left */ +b->frac = b->frac >> 1; +for (i = 0; (i < prec) && b->frac; i++) { /* divide loop */ + quo = quo << 1; /* shift quo */ + if (b->frac >= a->frac) { /* div step ok? */ + b->frac = b->frac - a->frac; /* subtract */ + quo = quo + 1; /* quo bit = 1 */ + } + b->frac = b->frac << 1; /* shift divd */ + } +b->frac = quo << (UF_V_NM - i + 1); /* shift quo */ +norm (b); /* normalize */ +return; +} + +/* Support routines */ + +void unpackf (int32 hi, UFP *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = FD_GETEXP (hi); /* get exponent */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) /* if -, rsvd op */ + { + RUN_SCOPE; + RSVD_OPND_FAULT; + } + r->frac = 0; /* else 0 */ + return; + } +hi = (((hi & FD_FRACW) | FD_HB) << 16) | ((hi >> 16) & 0xFFFF); +r->frac = ((t_uint64) hi) << (32 + UF_V_FDLO); +return; +} + +void unpackd (int32 hi, int32 lo, UFP *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = FD_GETEXP (hi); /* get exponent */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) /* if -, rsvd op */ + { + RUN_SCOPE; + RSVD_OPND_FAULT; + } + r->frac = 0; /* else 0 */ + return; + } +hi = (hi & FD_FRACL) | FD_HB; /* canonical form */ +r->frac = UNSCRAM (hi, lo) << UF_V_FDLO; /* guard bits */ +return; +} + +void unpackg (int32 hi, int32 lo, UFP *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = G_GETEXP (hi); /* get exponent */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) /* if -, rsvd op */ + { + RUN_SCOPE; + RSVD_OPND_FAULT; + } + r->frac = 0; /* else 0 */ + return; + } +hi = (hi & G_FRACL) | G_HB; /* canonical form */ +r->frac = UNSCRAM (hi, lo) << UF_V_GLO; /* guard bits */ +return; +} + +void norm (UFP *r) +{ +int32 i; +const static t_uint64 normmask[5] = { + 0xc000000000000000, 0xf000000000000000, 0xff00000000000000, + 0xffff000000000000, 0xffffffff00000000 + }; +const static int32 normtab[6] = { 1, 2, 4, 8, 16, 32}; + +if (r->frac == 0) { /* if fraction = 0 */ + r->sign = r->exp = 0; /* result is 0 */ + return; + } +while ((r->frac & UF_NM) == 0) { /* normalized? */ + for (i = 0; i < 5; i++) { /* find first 1 */ + if (r->frac & normmask[i]) + break; + } + r->frac = r->frac << normtab[i]; /* shift frac */ + r->exp = r->exp - normtab[i]; /* decr exp */ + } +return; +} + +int32 rpackfd (RUN_DECL, UFP *r, int32 *rh) +{ +if (rh) /* assume 0 */ + *rh = 0; +if (r->frac == 0) /* result 0? */ + return 0; +r->frac = r->frac + (rh? UF_DRND: UF_FRND); /* round */ +if ((r->frac & UF_NM) == 0) { /* carry out? */ + r->frac = r->frac >> 1; /* renormalize */ + r->exp = r->exp + 1; + } +if (r->exp > (int32) FD_M_EXP) /* ovflo? fault */ + FLT_OVFL_FAULT; +if (r->exp <= 0) { /* underflow? */ + if (PSL & PSW_FU) /* fault if fu */ + FLT_UNFL_FAULT; + return 0; /* else 0 */ + } +if (rh) /* get low */ + *rh = UF_GETFDLO (r->frac); +return r->sign | (r->exp << FD_V_EXP) | UF_GETFDHI (r->frac); +} + +int32 rpackg (RUN_DECL, UFP *r, int32 *rh) +{ +*rh = 0; /* assume 0 */ +if (r->frac == 0) /* result 0? */ + return 0; +r->frac = r->frac + UF_GRND; /* round */ +if ((r->frac & UF_NM) == 0) { /* carry out? */ + r->frac = r->frac >> 1; /* renormalize */ + r->exp = r->exp + 1; + } +if (r->exp > (int32) G_M_EXP) /* ovflo? fault */ + FLT_OVFL_FAULT; +if (r->exp <= 0) { /* underflow? */ + if (PSL & PSW_FU) /* fault if fu */ + FLT_UNFL_FAULT; + return 0; /* else 0 */ + } +*rh = UF_GETGLO (r->frac); /* get low */ +return r->sign | (r->exp << G_V_EXP) | UF_GETGHI (r->frac); +} + +#else /* 32b code */ + +#define WORDSWAP(x) ((((x) & WMASK) << 16) | (((x) >> 16) & WMASK)) + +typedef struct { + uint32 lo; + uint32 hi; + } UDP; + +typedef struct { + int32 sign; + int32 exp; + UDP frac; + } UFP; + +#define UF_NM_H 0x80000000 /* normalized */ +#define UF_FRND_H 0x00000080 /* F round */ +#define UF_FRND_L 0x00000000 +#define UF_DRND_H 0x00000000 /* D round */ +#define UF_DRND_L 0x00000080 +#define UF_GRND_H 0x00000000 /* G round */ +#define UF_GRND_L 0x00000400 +#define UF_V_NM 63 + +void unpackf (uint32 hi, UFP *a); +void unpackd (uint32 hi, uint32 lo, UFP *a); +void unpackg (uint32 hi, uint32 lo, UFP *a); +void norm (UFP *a); +int32 rpackfd (RUN_DECL, UFP *a, int32 *rh); +int32 rpackg (RUN_DECL, UFP *a, int32 *rh); +void vax_fadd (UFP *a, UFP *b); +void vax_fmul (UFP *a, UFP *b, t_bool qd, int32 bias, uint32 mhi, uint32 mlo); +void vax_fmod (UFP *a, int32 bias, int32 *intgr, int32 *flg); +void vax_fdiv (RUN_DECL, UFP *b, UFP *a, int32 prec, int32 bias); +void dp_add (UDP *a, const UDP *b); +void dp_inc (UDP *a); +void dp_sub (UDP *a, const UDP *b); +void dp_imul (uint32 a, uint32 b, UDP *r); +void dp_lsh (UDP *a, uint32 sc); +void dp_rsh (UDP *a, uint32 sc); +void dp_rsh_s (UDP *a, uint32 sc, uint32 neg); +void dp_neg (UDP *a); +int32 dp_cmp (UDP *a, UDP *b); + +/* Quadword arithmetic shift + + opnd[0] = shift count (cnt.rb) + opnd[1:2] = source (src.rq) + opnd[3:4] = destination (dst.wq) +*/ + +int32 op_ashq (RUN_DECL, int32 *opnd, int32 *rh, int32 *flg) +{ +UDP r, sr; +uint32 sc = opnd[0]; + +r.lo = opnd[1]; /* get source */ +r.hi = opnd[2]; +*flg = 0; /* assume no ovflo */ +if (sc & BSIGN) /* right shift? */ + dp_rsh_s (&r, 0x100 - sc, r.hi & LSIGN); /* signed right */ +else { + dp_lsh (&r, sc); /* left shift */ + sr = r; /* copy result */ + dp_rsh_s (&sr, sc, sr.hi & LSIGN); /* signed right */ + if ((sr.hi != ((uint32) opnd[2])) || /* reshift != orig? */ + (sr.lo != ((uint32) opnd[1]))) *flg = 1; /* overflow */ + } +*rh = r.hi; /* hi result */ +return r.lo; /* lo result */ +} + +/* Extended multiply subroutine */ + +int32 op_emul (RUN_DECL, int32 mpy, int32 mpc, int32 *rh) +{ +UDP r; +int32 sign = mpy ^ mpc; /* sign of result */ + +if (mpy & LSIGN) /* abs value */ + mpy = -mpy; +if (mpc & LSIGN) + mpc = -mpc; +dp_imul (mpy & LMASK, mpc & LMASK, &r); /* 32b * 32b -> 64b */ +if (sign & LSIGN) /* negative result? */ + dp_neg (&r); +*rh = r.hi; +return r.lo; +} + +/* Extended divide + + opnd[0] = divisor (non-zero) + opnd[1:2] = dividend +*/ + +int32 op_ediv (RUN_DECL, int32 *opnd, int32 *rh, int32 *flg) +{ +UDP dvd; +uint32 i, dvr, quo; + +dvr = opnd[0]; /* get divisor */ +dvd.lo = opnd[1]; /* get dividend */ +dvd.hi = opnd[2]; +*flg = CC_V; /* assume error */ +*rh = 0; +if (dvd.hi & LSIGN) /* |dividend| */ + dp_neg (&dvd); +if (dvr & LSIGN) /* |divisor| */ + dvr = NEG (dvr); +if (dvd.hi >= dvr) /* divide work? */ + return opnd[1]; +for (i = quo = 0; i < 32; i++) { /* 32 iterations */ + quo = quo << 1; /* shift quotient */ + dp_lsh (&dvd, 1); /* shift dividend */ + if (dvd.hi >= dvr) { /* step work? */ + dvd.hi = (dvd.hi - dvr) & LMASK; /* subtract dvr */ + quo = quo + 1; + } + } +if ((opnd[0] ^ opnd[2]) & LSIGN) { /* result -? */ + quo = NEG (quo); /* negate */ + if (quo && ((quo & LSIGN) == 0)) /* right sign? */ + return opnd[1]; + } +else if (quo & LSIGN) + return opnd[1]; +if (opnd[2] & LSIGN) /* sign of rem */ + *rh = NEG (dvd.hi); +else *rh = dvd.hi; +*flg = 0; /* no overflow */ +return quo; /* return quo */ +} + +/* Compare floating */ + +int32 op_cmpfd (RUN_DECL, int32 h1, int32 l1, int32 h2, int32 l2) +{ +UFP a, b; +int32 r; + +unpackd (h1, l1, &a); +unpackd (h2, l2, &b); +if (a.sign != b.sign) + return (a.sign? CC_N: 0); +r = a.exp - b.exp; +if (r == 0) + r = dp_cmp (&a.frac, &b.frac); +if (r < 0) + return (a.sign? 0: CC_N); +if (r > 0) + return (a.sign? CC_N: 0); +return CC_Z; +} + +int32 op_cmpg (RUN_DECL, int32 h1, int32 l1, int32 h2, int32 l2) +{ +UFP a, b; +int32 r; + +unpackg (h1, l1, &a); +unpackg (h2, l2, &b); +if (a.sign != b.sign) + return (a.sign? CC_N: 0); +r = a.exp - b.exp; +if (r == 0) + r = dp_cmp (&a.frac, &b.frac); +if (r < 0) + return (a.sign? 0: CC_N); +if (r > 0) + return (a.sign? CC_N: 0); +return CC_Z; +} + +/* Integer to floating convert */ + +int32 op_cvtifdg (RUN_DECL, int32 val, int32 *rh, int32 opc) +{ +UFP a; + +if (val == 0) { /* zero? */ + if (rh) *rh = 0; /* return true 0 */ + return 0; + } +if (val < 0) { /* negative? */ + a.sign = FPSIGN; /* sign = - */ + val = -val; + } +else a.sign = 0; /* else sign = + */ +a.exp = 32 + ((opc & 0x100)? G_BIAS: FD_BIAS); /* initial exp */ +a.frac.hi = val & LMASK; /* fraction */ +a.frac.lo = 0; +norm (&a); /* normalize */ +if (opc & 0x100) /* pack and return */ + return rpackg (RUN_PASS, &a, rh); +return rpackfd (RUN_PASS, &a, rh); +} + +/* Floating to integer convert */ + +int32 op_cvtfdgi (RUN_DECL, int32 *opnd, int32 *flg, int32 opc) +{ +UFP a; +int32 lnt = opc & 03; +int32 ubexp; +const static uint32 maxv[4] = { 0x7F, 0x7FFF, 0x7FFFFFFF, 0x7FFFFFFF }; + +*flg = 0; +if (opc & 0x100) { /* G? */ + unpackg (opnd[0], opnd[1], &a); /* unpack */ + ubexp = a.exp - G_BIAS; /* unbiased exp */ + } +else { + if (opc & 0x20) /* F or D */ + unpackd (opnd[0], opnd[1], &a); + else unpackf (opnd[0], &a); /* unpack */ + ubexp = a.exp - FD_BIAS; /* unbiased exp */ + } +if ((a.exp == 0) || (ubexp < 0)) /* true zero or frac? */ + return 0; +if (ubexp <= UF_V_NM) { /* exp in range? */ + dp_rsh (&a.frac, UF_V_NM - ubexp); /* leave rnd bit */ + if (lnt == 03) /* if CVTR, round */ + dp_inc (&a.frac); + dp_rsh (&a.frac, 1); /* now justified */ + if ((a.frac.hi != 0) || + (a.frac.lo > (maxv[lnt] + (a.sign? 1: 0)))) + *flg = CC_V; + } +else { + *flg = CC_V; /* always ovflo */ + if (ubexp > (UF_V_NM + 32)) /* in ext range? */ + return 0; + dp_lsh (&a.frac, ubexp - UF_V_NM - 1); /* no rnd bit */ + } +return (a.sign? NEG (a.frac.lo): a.frac.lo); /* return lo frac */ +} + +/* Extended modularize + + One of three floating point instructions dropped from the architecture, + EMOD presents two sets of complications. First, it requires an extended + fraction multiply, with precise (and unusual) truncation conditions. + Second, it has two write operands, a dubious distinction it shares + with EDIV. +*/ + +int32 op_emodf (RUN_DECL, int32 *opnd, int32 *intgr, int32 *flg) +{ +UFP a, b; + +unpackf (opnd[0], &a); /* unpack operands */ +unpackf (opnd[2], &b); +a.frac.hi = a.frac.hi | opnd[1]; /* extend src1 */ +vax_fmul (&a, &b, 0, FD_BIAS, 0, LMASK); /* multiply */ +vax_fmod (&a, FD_BIAS, intgr, flg); /* sep int & frac */ +return rpackfd (RUN_PASS, &a, NULL); /* return frac */ +} + +int32 op_emodd (RUN_DECL, int32 *opnd, int32 *flo, int32 *intgr, int32 *flg) +{ +UFP a, b; + +unpackd (opnd[0], opnd[1], &a); /* unpack operands */ +unpackd (opnd[3], opnd[4], &b); +a.frac.lo = a.frac.lo | opnd[2]; /* extend src1 */ +vax_fmul (&a, &b, 1, FD_BIAS, 0, 0); /* multiply */ +vax_fmod (&a, FD_BIAS, intgr, flg); /* sep int & frac */ +return rpackfd (RUN_PASS, &a, flo); /* return frac */ +} + +int32 op_emodg (RUN_DECL, int32 *opnd, int32 *flo, int32 *intgr, int32 *flg) +{ +UFP a, b; + +unpackg (opnd[0], opnd[1], &a); /* unpack operands */ +unpackg (opnd[3], opnd[4], &b); +a.frac.lo = a.frac.lo | (opnd[2] >> 5); /* extend src1 */ +vax_fmul (&a, &b, 1, G_BIAS, 0, 0); /* multiply */ +vax_fmod (&a, G_BIAS, intgr, flg); /* sep int & frac */ +return rpackg (RUN_PASS, &a, flo); /* return frac */ +} + +/* Unpacked floating point routines */ + +/* Floating add */ + +void vax_fadd (UFP *a, UFP *b) +{ +int32 ediff; +UFP t; + +if ((a->frac.hi == 0) && (a->frac.lo == 0)) { /* s1 = 0? */ + *a = *b; + return; + } +if ((b->frac.hi == 0) && (b->frac.lo == 0)) /* s2 = 0? */ + return; +if ((a->exp < b->exp) || /* |s1| < |s2|? swap */ + ((a->exp == b->exp) && (dp_cmp (&a->frac, &b->frac) < 0))) { + t = *a; + *a = *b; + *b = t; + } +ediff = a->exp - b->exp; /* exp diff */ +if (a->sign ^ b->sign) { /* eff sub? */ + if (ediff) { /* exp diff? */ + dp_neg (&b->frac); /* negate fraction */ + dp_rsh_s (&b->frac, ediff, 1); /* signed right */ + dp_add (&a->frac, &b->frac); /* "add" frac */ + } + else dp_sub (&a->frac, &b->frac); /* a >= b */ + norm (a); /* normalize */ + } +else { + if (ediff) /* add, denormalize */ + dp_rsh (&b->frac, ediff); + dp_add (&a->frac, &b->frac); /* add frac */ + if (dp_cmp (&a->frac, &b->frac) < 0) { /* chk for carry */ + dp_rsh (&a->frac, 1); /* renormalize */ + a->frac.hi = a->frac.hi | UF_NM_H; /* add norm bit */ + a->exp = a->exp + 1; /* skip norm */ + } + } +return; +} + +/* Floating multiply - 64b * 64b with cross products */ + +void vax_fmul (UFP *a, UFP *b, t_bool qd, int32 bias, uint32 mhi, uint32 mlo) +{ +UDP rhi, rlo, rmid1, rmid2; + +if ((a->exp == 0) || (b->exp == 0)) { /* zero argument? */ + a->frac.hi = a->frac.lo = 0; /* result is zero */ + a->sign = a->exp = 0; + return; + } +a->sign = a->sign ^ b->sign; /* sign of result */ +a->exp = a->exp + b->exp - bias; /* add exponents */ +dp_imul (a->frac.hi, b->frac.hi, &rhi); /* high result */ +if (qd) { /* 64b needed? */ + dp_imul (a->frac.hi, b->frac.lo, &rmid1); /* cross products */ + dp_imul (a->frac.lo, b->frac.hi, &rmid2); + dp_imul (a->frac.lo, b->frac.lo, &rlo); /* low result */ + rhi.lo = (rhi.lo + rmid1.hi) & LMASK; /* add hi cross */ + if (rhi.lo < rmid1.hi) /* to low high res */ + rhi.hi = (rhi.hi + 1) & LMASK; + rhi.lo = (rhi.lo + rmid2.hi) & LMASK; + if (rhi.lo < rmid2.hi) + rhi.hi = (rhi.hi + 1) & LMASK; + rlo.hi = (rlo.hi + rmid1.lo) & LMASK; /* add mid1 to low res */ + if (rlo.hi < rmid1.lo) /* carry? incr high res */ + dp_inc (&rhi); + rlo.hi = (rlo.hi + rmid2.lo) & LMASK; /* add mid2 to low res */ + if (rlo.hi < rmid2.lo) /* carry? incr high res */ + dp_inc (&rhi); + } +a->frac.hi = rhi.hi & ~mhi; /* mask fraction */ +a->frac.lo = rhi.lo & ~mlo; +norm (a); /* normalize */ +return; +} + +/* Floating modulus - there are three cases + + exp <= bias - integer is 0, fraction is input, + no overflow + bias < exp <= bias+64 - separate integer and fraction, + integer overflow may occur + bias+64 < exp - result is integer, fraction is 0 + integer overflow +*/ + +void vax_fmod (UFP *a, int32 bias, int32 *intgr, int32 *flg) +{ +UDP ifr; + +if (a->exp <= bias) /* 0 or <1? int = 0 */ + *intgr = *flg = 0; +else if (a->exp <= (bias + 64)) { /* in range [1,64]? */ + ifr = a->frac; + dp_rsh (&ifr, 64 - (a->exp - bias)); /* separate integer */ + if ((a->exp > (bias + 32)) || /* test ovflo */ + ((a->exp == (bias + 32)) && + (ifr.lo > (a->sign? 0x80000000: 0x7FFFFFFF)))) + *flg = CC_V; + else *flg = 0; + *intgr = ifr.lo; + if (a->sign) /* -? comp int */ + *intgr = -*intgr; + dp_lsh (&a->frac, a->exp - bias); /* excise integer */ + a->exp = bias; + } +else { + *intgr = 0; /* out of range */ + a->frac.hi = a->frac.lo = a->sign = a->exp = 0; /* result 0 */ + *flg = CC_V; /* overflow */ + } +norm (a); /* normalize */ +return; +} + +/* Floating divide + Needs to develop at least one rounding bit. Since the first + divide step can fail, caller should specify 2 more bits than + the precision of the fraction. +*/ + +void vax_fdiv (RUN_DECL, UFP *a, UFP *b, int32 prec, int32 bias) +{ +int32 i; +UDP quo = { 0, 0 }; + +if (a->exp == 0) /* divr = 0? */ + FLT_DZRO_FAULT; +if (b->exp == 0) /* divd = 0? */ + return; +b->sign = b->sign ^ a->sign; /* result sign */ +b->exp = b->exp - a->exp + bias + 1; /* unbiased exp */ +dp_rsh (&a->frac, 1); /* allow 1 bit left */ +dp_rsh (&b->frac, 1); +for (i = 0; i < prec; i++) { /* divide loop */ + dp_lsh (&quo, 1); /* shift quo */ + if (dp_cmp (&b->frac, &a->frac) >= 0) { /* div step ok? */ + dp_sub (&b->frac, &a->frac); /* subtract */ + quo.lo = quo.lo + 1; /* quo bit = 1 */ + } + dp_lsh (&b->frac, 1); /* shift divd */ + } +dp_lsh (&quo, UF_V_NM - prec + 1); /* put in position */ +b->frac = quo; +norm (b); /* normalize */ +return; +} + +/* Double precision integer routines */ + +int32 dp_cmp (UDP *a, UDP *b) +{ +if (a->hi < b->hi) /* compare hi */ + return -1; +if (a->hi > b->hi) + return +1; +if (a->lo < b->lo) /* hi =, compare lo */ + return -1; +if (a->lo > b->lo) + return +1; +return 0; /* hi, lo equal */ +} + +void dp_add (UDP *a, const UDP *b) +{ +a->lo = (a->lo + b->lo) & LMASK; /* add lo */ +if (a->lo < b->lo) /* carry? */ + a->hi = a->hi + 1; +a->hi = (a->hi + b->hi) & LMASK; /* add hi */ +return; +} + +void dp_inc (UDP *a) +{ +a->lo = (a->lo + 1) & LMASK; /* inc lo */ +if (a->lo == 0) /* carry? inc hi */ + a->hi = (a->hi + 1) & LMASK; +return; +} + +void dp_sub (UDP *a, const UDP *b) +{ +if (a->lo < b->lo) /* borrow? decr hi */ + a->hi = a->hi - 1; +a->lo = (a->lo - b->lo) & LMASK; /* sub lo */ +a->hi = (a->hi - b->hi) & LMASK; /* sub hi */ +return; +} + +void dp_lsh (UDP *r, uint32 sc) +{ +if (sc > 63) /* > 63? result 0 */ + r->hi = r->lo = 0; +else if (sc > 31) { /* [32,63]? */ + r->hi = (r->lo << (sc - 32)) & LMASK; + r->lo = 0; + } +else if (sc != 0) { + r->hi = ((r->hi << sc) | (r->lo >> (32 - sc))) & LMASK; + r->lo = (r->lo << sc) & LMASK; + } +return; +} + +void dp_rsh (UDP *r, uint32 sc) +{ +if (sc > 63) /* > 63? result 0 */ + r->hi = r->lo = 0; +else if (sc > 31) { /* [32,63]? */ + r->lo = (r->hi >> (sc - 32)) & LMASK; + r->hi = 0; + } +else if (sc != 0) { + r->lo = ((r->lo >> sc) | (r->hi << (32 - sc))) & LMASK; + r->hi = (r->hi >> sc) & LMASK; + } +return; +} + +void dp_rsh_s (UDP *r, uint32 sc, uint32 neg) +{ +dp_rsh (r, sc); /* do unsigned right */ +if (neg && sc) { /* negative? */ + if (sc > 63) /* > 63? result -1 */ + r->hi = r->lo = LMASK; + else { + UDP ones = { LMASK, LMASK }; + dp_lsh (&ones, 64 - sc); /* shift ones */ + r->hi = r->hi | ones.hi; /* or into result */ + r->lo = r->lo | ones.lo; + } + } +return; +} + +void dp_imul (uint32 a, uint32 b, UDP *r) +{ +uint32 ah, bh, al, bl, rhi, rlo, rmid1, rmid2; + +if ((a == 0) || (b == 0)) { /* zero argument? */ + r->hi = r->lo = 0; /* result is zero */ + return; + } +ah = (a >> 16) & WMASK; /* split operands */ +bh = (b >> 16) & WMASK; /* into 16b chunks */ +al = a & WMASK; +bl = b & WMASK; +rhi = ah * bh; /* high result */ +rmid1 = ah * bl; +rmid2 = al * bh; +rlo = al * bl; +rhi = rhi + ((rmid1 >> 16) & WMASK) + ((rmid2 >> 16) & WMASK); +rmid1 = (rlo + (rmid1 << 16)) & LMASK; /* add mid1 to lo */ +if (rmid1 < rlo) /* carry? incr hi */ + rhi = rhi + 1; +rmid2 = (rmid1 + (rmid2 << 16)) & LMASK; /* add mid2 to to */ +if (rmid2 < rmid1) /* carry? incr hi */ + rhi = rhi + 1; +r->hi = rhi & LMASK; /* mask result */ +r->lo = rmid2; +return; +} + +void dp_neg (UDP *r) +{ +r->lo = NEG (r->lo); +r->hi = (~r->hi + (r->lo == 0)) & LMASK; +return; +} + +/* Support routines */ + +void unpackf (uint32 hi, UFP *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = FD_GETEXP (hi); /* get exponent */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) /* if -, rsvd op */ + RSVD_OPND_FAULT; + r->frac.hi = r->frac.lo = 0; /* else 0 */ + return; + } +r->frac.hi = WORDSWAP ((hi & ~(FPSIGN | FD_EXP)) | FD_HB); +r->frac.lo = 0; +dp_lsh (&r->frac, FD_GUARD); +return; +} + +void unpackd (uint32 hi, uint32 lo, UFP *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = FD_GETEXP (hi); /* get exponent */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) /* if -, rsvd op */ + RSVD_OPND_FAULT; + r->frac.hi = r->frac.lo = 0; /* else 0 */ + return; + } +r->frac.hi = WORDSWAP ((hi & ~(FPSIGN | FD_EXP)) | FD_HB); +r->frac.lo = WORDSWAP (lo); +dp_lsh (&r->frac, FD_GUARD); +return; +} + +void unpackg (uint32 hi, uint32 lo, UFP *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = G_GETEXP (hi); /* get exponent */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) /* if -, rsvd op */ + RSVD_OPND_FAULT; + r->frac.hi = r->frac.lo = 0; /* else 0 */ + return; + } +r->frac.hi = WORDSWAP ((hi & ~(FPSIGN | G_EXP)) | G_HB); +r->frac.lo = WORDSWAP (lo); +dp_lsh (&r->frac, G_GUARD); +return; +} + +void norm (UFP *r) +{ +int32 i; +const static uint32 normmask[5] = { + 0xc0000000, 0xf0000000, 0xff000000, 0xffff0000, 0xffffffff + }; +const static int32 normtab[6] = { 1, 2, 4, 8, 16, 32}; + +if ((r->frac.hi == 0) && (r->frac.lo == 0)) { /* if fraction = 0 */ + r->sign = r->exp = 0; /* result is 0 */ + return; + } +while ((r->frac.hi & UF_NM_H) == 0) { /* normalized? */ + for (i = 0; i < 5; i++) { /* find first 1 */ + if (r->frac.hi & normmask[i]) + break; + } + dp_lsh (&r->frac, normtab[i]); /* shift frac */ + r->exp = r->exp - normtab[i]; /* decr exp */ + } +return; +} + +int32 rpackfd (RUN_DECL, UFP *r, int32 *rh) +{ +const static UDP f_round = { UF_FRND_L, UF_FRND_H }; +const static UDP d_round = { UF_DRND_L, UF_DRND_H }; + +if (rh) /* assume 0 */ + *rh = 0; +if ((r->frac.hi == 0) && (r->frac.lo == 0)) /* result 0? */ + return 0; +if (rh) /* round */ + dp_add (&r->frac, &d_round); +else dp_add (&r->frac, &f_round); +if ((r->frac.hi & UF_NM_H) == 0) { /* carry out? */ + dp_rsh (&r->frac, 1); /* renormalize */ + r->exp = r->exp + 1; + } +if (r->exp > (int32) FD_M_EXP) /* ovflo? fault */ + FLT_OVFL_FAULT; +if (r->exp <= 0) { /* underflow? */ + if (PSL & PSW_FU) /* fault if fu */ + FLT_UNFL_FAULT; + return 0; /* else 0 */ + } +dp_rsh (&r->frac, FD_GUARD); /* remove guard */ +if (rh) /* get low */ + *rh = WORDSWAP (r->frac.lo); +return r->sign | (r->exp << FD_V_EXP) | + (WORDSWAP (r->frac.hi) & ~(FD_HB | FPSIGN | FD_EXP)); +} + +int32 rpackg (RUN_DECL, UFP *r, int32 *rh) +{ +const static UDP g_round = { UF_GRND_L, UF_GRND_H }; + +*rh = 0; /* assume 0 */ +if ((r->frac.hi == 0) && (r->frac.lo == 0)) /* result 0? */ + return 0; +dp_add (&r->frac, &g_round); /* round */ +if ((r->frac.hi & UF_NM_H) == 0) { /* carry out? */ + dp_rsh (&r->frac, 1); /* renormalize */ + r->exp = r->exp + 1; + } +if (r->exp > (int32) G_M_EXP) /* ovflo? fault */ +FLT_OVFL_FAULT; +if (r->exp <= 0) { /* underflow? */ + if (PSL & PSW_FU) /* fault if fu */ + FLT_UNFL_FAULT; + return 0; /* else 0 */ + } +dp_rsh (&r->frac, G_GUARD); /* remove guard */ +*rh = WORDSWAP (r->frac.lo); /* get low */ +return r->sign | (r->exp << G_V_EXP) | + (WORDSWAP (r->frac.hi) & ~(G_HB | FPSIGN | G_EXP)); +} + +#endif + +/* Floating point instructions */ + +/* Move/test/move negated floating + + Note that only the high 32b is processed. + If the high 32b is not zero, it is unchanged. +*/ + +int32 op_movfd (RUN_DECL, int32 val) +{ +if (val & FD_EXP) + return val; +if (val & FPSIGN) + RSVD_OPND_FAULT; +return 0; +} + +int32 op_mnegfd (RUN_DECL, int32 val) +{ +if (val & FD_EXP) + return (val ^ FPSIGN); +if (val & FPSIGN) + RSVD_OPND_FAULT; +return 0; +} + +int32 op_movg (RUN_DECL, int32 val) +{ +if (val & G_EXP) + return val; +if (val & FPSIGN) + RSVD_OPND_FAULT; +return 0; +} + +int32 op_mnegg (RUN_DECL, int32 val) +{ +if (val & G_EXP) + return (val ^ FPSIGN); +if (val & FPSIGN) + RSVD_OPND_FAULT; +return 0; +} + +/* Floating to floating convert - F to D is essentially done with MOVFD */ + +int32 op_cvtdf (RUN_DECL, int32 *opnd) +{ +UFP a; + +unpackd (opnd[0], opnd[1], &a); +return rpackfd (RUN_PASS, &a, NULL); +} + +int32 op_cvtfg (RUN_DECL, int32 *opnd, int32 *rh) +{ +UFP a; + +unpackf (opnd[0], &a); +a.exp = a.exp - FD_BIAS + G_BIAS; +return rpackg (RUN_PASS, &a, rh); +} + +int32 op_cvtgf (RUN_DECL, int32 *opnd) +{ +UFP a; + +unpackg (opnd[0], opnd[1], &a); +a.exp = a.exp - G_BIAS + FD_BIAS; +return rpackfd (RUN_PASS, &a, NULL); +} + +/* Floating add and subtract */ + +int32 op_addf (RUN_DECL, int32 *opnd, t_bool sub) +{ +UFP a, b; + +unpackf (opnd[0], &a); /* F format */ +unpackf (opnd[1], &b); +if (sub) /* sub? -s1 */ + a.sign = a.sign ^ FPSIGN; +vax_fadd (&a, &b); /* add fractions */ +return rpackfd (RUN_PASS, &a, NULL); +} + +int32 op_addd (RUN_DECL, int32 *opnd, int32 *rh, t_bool sub) +{ +UFP a, b; + +unpackd (opnd[0], opnd[1], &a); +unpackd (opnd[2], opnd[3], &b); +if (sub) /* sub? -s1 */ + a.sign = a.sign ^ FPSIGN; +vax_fadd (&a, &b); /* add fractions */ +return rpackfd (RUN_PASS, &a, rh); +} + +int32 op_addg (RUN_DECL, int32 *opnd, int32 *rh, t_bool sub) +{ +UFP a, b; + +unpackg (opnd[0], opnd[1], &a); +unpackg (opnd[2], opnd[3], &b); +if (sub) /* sub? -s1 */ + a.sign = a.sign ^ FPSIGN; +vax_fadd (&a, &b); /* add fractions */ +return rpackg (RUN_PASS, &a, rh); /* round and pack */ +} + +/* Floating multiply */ + +int32 op_mulf (RUN_DECL, int32 *opnd) +{ +UFP a, b; + +unpackf (opnd[0], &a); /* F format */ +unpackf (opnd[1], &b); +vax_fmul (&a, &b, 0, FD_BIAS, 0, 0); /* do multiply */ +return rpackfd (RUN_PASS, &a, NULL); /* round and pack */ +} + +int32 op_muld (RUN_DECL, int32 *opnd, int32 *rh) +{ +UFP a, b; + +unpackd (opnd[0], opnd[1], &a); /* D format */ +unpackd (opnd[2], opnd[3], &b); +vax_fmul (&a, &b, 1, FD_BIAS, 0, 0); /* do multiply */ +return rpackfd (RUN_PASS, &a, rh); /* round and pack */ +} + +int32 op_mulg (RUN_DECL, int32 *opnd, int32 *rh) +{ +UFP a, b; + +unpackg (opnd[0], opnd[1], &a); /* G format */ +unpackg (opnd[2], opnd[3], &b); +vax_fmul (&a, &b, 1, G_BIAS, 0, 0); /* do multiply */ +return rpackg (RUN_PASS, &a, rh); /* round and pack */ +} + +/* Floating divide */ + +int32 op_divf (RUN_DECL, int32 *opnd) +{ +UFP a, b; + +unpackf (opnd[0], &a); /* F format */ +unpackf (opnd[1], &b); +vax_fdiv (RUN_PASS, &a, &b, 26, FD_BIAS); /* do divide */ +return rpackfd (RUN_PASS, &b, NULL); /* round and pack */ +} + +int32 op_divd (RUN_DECL, int32 *opnd, int32 *rh) +{ +UFP a, b; + +unpackd (opnd[0], opnd[1], &a); /* D format */ +unpackd (opnd[2], opnd[3], &b); +vax_fdiv (RUN_PASS, &a, &b, 58, FD_BIAS); /* do divide */ +return rpackfd (RUN_PASS, &b, rh); /* round and pack */ +} + +int32 op_divg (RUN_DECL, int32 *opnd, int32 *rh) +{ +UFP a, b; + +unpackg (opnd[0], opnd[1], &a); /* G format */ +unpackg (opnd[2], opnd[3], &b); +vax_fdiv (RUN_PASS, &a, &b, 55, G_BIAS); /* do divide */ +return rpackg (RUN_PASS, &b, rh); /* round and pack */ +} + +/* Polynomial evaluation + The most mis-implemented instruction in the VAX (probably here too). + POLY requires a precise combination of masking versus normalizing + to achieve the desired answer. In particular, the multiply step + is masked prior to normalization. In addition, negative small + fractions must not be treated as 0 during denorm. +*/ + +void op_polyf (RUN_DECL, int32 *opnd, int32 acc) +{ +UFP r, a, c; +int32 deg = opnd[1]; +int32 ptr = opnd[2]; +int32 i, wd, res; + +if (deg > 31) /* degree > 31? fault */ + RSVD_OPND_FAULT; +unpackf (opnd[0], &a); /* unpack arg */ +wd = Read (RUN_PASS, ptr, L_LONG, RD); /* get C0 */ +ptr = ptr + 4; +unpackf (wd, &r); /* unpack C0 */ +res = rpackfd (RUN_PASS, &r, NULL); /* first result */ +for (i = 0; i < deg; i++) { /* loop */ + unpackf (res, &r); /* unpack result */ + vax_fmul (&r, &a, 0, FD_BIAS, 1, LMASK); /* r = r * arg, mask */ + wd = Read (RUN_PASS, ptr, L_LONG, RD); /* get Cnext */ + ptr = ptr + 4; + unpackf (wd, &c); /* unpack Cnext */ + vax_fadd (&r, &c); /* r = r + Cnext */ + res = rpackfd (RUN_PASS, &r, NULL); /* round and pack */ + } +R[0] = res; +R[1] = R[2] = 0; +R[3] = ptr; +return; +} + +void op_polyd (RUN_DECL, int32 *opnd, int32 acc) +{ +UFP r, a, c; +int32 deg = opnd[2]; +int32 ptr = opnd[3]; +int32 i, wd, wd1, res, resh; + +if (deg > 31) /* degree > 31? fault */ + RSVD_OPND_FAULT; +unpackd (opnd[0], opnd[1], &a); /* unpack arg */ +wd = Read (RUN_PASS, ptr, L_LONG, RD); /* get C0 */ +wd1 = Read (RUN_PASS, ptr + 4, L_LONG, RD); +ptr = ptr + 8; +unpackd (wd, wd1, &r); /* unpack C0 */ +res = rpackfd (RUN_PASS, &r, &resh); /* first result */ +for (i = 0; i < deg; i++) { /* loop */ + unpackd (res, resh, &r); /* unpack result */ + vax_fmul (&r, &a, 1, FD_BIAS, 0, 1); /* r = r * arg, mask */ + wd = Read (RUN_PASS, ptr, L_LONG, RD); /* get Cnext */ + wd1 = Read (RUN_PASS, ptr + 4, L_LONG, RD); + ptr = ptr + 8; + unpackd (wd, wd1, &c); /* unpack Cnext */ + vax_fadd (&r, &c); /* r = r + Cnext */ + res = rpackfd (RUN_PASS, &r, &resh); /* round and pack */ + } +R[0] = res; +R[1] = resh; +R[2] = 0; +R[3] = ptr; +R[4] = 0; +R[5] = 0; +return; +} + +void op_polyg (RUN_DECL, int32 *opnd, int32 acc) +{ +UFP r, a, c; +int32 deg = opnd[2]; +int32 ptr = opnd[3]; +int32 i, wd, wd1, res, resh; + +if (deg > 31) /* degree > 31? fault */ + RSVD_OPND_FAULT; +unpackg (opnd[0], opnd[1], &a); /* unpack arg */ +wd = Read (RUN_PASS, ptr, L_LONG, RD); /* get C0 */ +wd1 = Read (RUN_PASS, ptr + 4, L_LONG, RD); +ptr = ptr + 8; +unpackg (wd, wd1, &r); /* unpack C0 */ +res = rpackg (RUN_PASS, &r, &resh); /* first result */ +for (i = 0; i < deg; i++) { /* loop */ + unpackg (res, resh, &r); /* unpack result */ + vax_fmul (&r, &a, 1, G_BIAS, 0, 1); /* r = r * arg */ + wd = Read (RUN_PASS, ptr, L_LONG, RD); /* get Cnext */ + wd1 = Read (RUN_PASS, ptr + 4, L_LONG, RD); + ptr = ptr + 8; + unpackg (wd, wd1, &c); /* unpack Cnext */ + vax_fadd (&r, &c); /* r = r + Cnext */ + res = rpackg (RUN_PASS, &r, &resh); /* round and pack */ + } +R[0] = res; +R[1] = resh; +R[2] = 0; +R[3] = ptr; +R[4] = 0; +R[5] = 0; +return; +} diff --git a/src/VAX/vax_hist.h b/src/VAX/vax_hist.h new file mode 100644 index 0000000..66aaa2b --- /dev/null +++ b/src/VAX/vax_hist.h @@ -0,0 +1,183 @@ +/* + * Primitives used by instruction history recording. + * Generate 64-bit stamps in multiprocessor case. + */ + +#if defined (__x86_64__) +#define HST_MAKE_STAMP_ISBOOL 0 +static smp_interlocked_uint64_var hst_stamp = smp_var_init(0); + +SIM_INLINE static void hst_reinit_stamp() +{ + smp_var(hst_stamp) = 0; +} + +SIM_INLINE static void hst_make_stamp(UINT64* pst) +{ + *pst = smp_interlocked_increment(& smp_var(hst_stamp)); +} + +#endif + +#if defined (__x86_32__) +#define HST_MAKE_STAMP_ISBOOL 1 +static smp_interlocked_uint32_var hst_stamp_counter = smp_var_init(0); +static smp_interlocked_uint32_var hst_stamp_epoch = smp_var_init(0); +extern t_bool have_cmpxchg8b; + +SIM_INLINE static void hst_reinit_stamp() +{ + if (sizeof(hst_stamp_counter) < 8) + panic("Internal error: wrong hst_stamp_counter size"); + memset((void*) & hst_stamp_counter, 0, sizeof hst_stamp_counter); + smp_var(hst_stamp_epoch) = 0; +} + +SIM_INLINE static t_bool hst_make_stamp(UINT64* pst) +{ + if (unlikely(! have_cmpxchg8b)) + goto no_cmpxchg8b; + +#if defined(_WIN32) + { + // COMPILER_BARRIER; + uint32* pctr = (uint32*) & smp_var(hst_stamp_counter); + __asm + { + mov edi, pctr + mov eax, [edi] + mov edx, [edi + 4] +cx_again: + mov ebx, eax + mov ecx, edx + add ebx, 1 + adc ecx, 0 + lock cmpxchg8b [edi] + jnz cx_again + mov edi, pst + mov [edi], ebx + mov [edi + 4], ecx + } + // COMPILER_BARRIER; + } + return TRUE; +#elif defined(__linux) || defined(__APPLE__) + { + // COMPILER_BARRIER; + uint32* pctr = (uint32*) & smp_var(hst_stamp_counter); + __asm__ __volatile__ ("\n\t" + "movl (%%edi), %%eax\n\t" + "movl 4(%%edi), %%edx\n\t" +"1:" + "movl %%eax, %%ebx\n\t" + "movl %%edx, %%ecx\n\t" + "addl $1, %%ebx\n\t" + "adcl $0, %%ecx\n\t" + "lock; cmpxchg8b (%%edi)\n\t" + "jnz 1b\n\t" + "movl %%ebx, (%%esi)\n\t" + "movl %%ecx, 4(%%esi)\n\t" + : /* no outputs */ + : "S" (pst), "D" (pctr) + : "eax", "ebx", "ecx", "edx", /*"esi", "edi",*/ "cc" + ); + // COMPILER_BARRIER; + } + return TRUE; +#else +# error Unimplemented +#endif + +no_cmpxchg8b: + uint32 counter = smp_interlocked_increment_var(& hst_stamp_counter); + uint32 epoch = weak_read_var(hst_stamp_epoch); + + /* + * check safety zones + */ + + // if (counter >= 0xF0000000 || counter <= 0x10000000) + // { + // if (epoch & 1) + // { + // __asm int 3 + // } + // } + // else if (counter >= 0x70000000 && counter <= 0x90000000) + // { + // if (! (epoch & 1)) + // { + // __asm int 3 + // } + // } + + switch (counter >> 28) + { + case 0x0: + case 0x1: + case 0xF: + if (epoch & 1) + return FALSE; + break; + + case 0x7: + case 0x8: + case 0x9: + if (! (epoch & 1)) + return FALSE; + break; + } + + // process epoch decoding and incrementing + + if (counter < 0x80000000) + { + if (counter == 0x40000000) + { + epoch = epoch / 2; + // (epoch, 0) -> (epoch + 1, 1) + if (! smp_interlocked_cas_done_var(& hst_stamp_epoch, 2 * epoch, 2 * (epoch + 1) + 1)) + { + return FALSE; + } + } + else + { + // if (epoch & 1) + // epoch = epoch / 2 - 1; + // else + // epoch = epoch / 2; + epoch = (epoch >> 1) - (epoch & 1); + } + } + else // if (counter >= 0x80000000) + { + if (counter == 0xC0000000) + { + epoch = epoch / 2; + // (epoch + 1, 1) -> (epoch + 1, 0) + if (! smp_interlocked_cas_done_var(& hst_stamp_epoch, 2 * epoch + 1, 2 * epoch)) + { + return FALSE; + } + epoch--; + } + else + { + epoch = epoch / 2 - 1; + } + } + + // compose the stamp + + // t_uint64 stamp = epoch; + // stamp = (epoch << 32) | counter; + // *pst = stamp; + + uint32* pst32 = (uint32*) pst; + pst[0] = counter; + pst[1] = epoch; + + return TRUE; +} +#endif diff --git a/src/VAX/vax_io.cpp b/src/VAX/vax_io.cpp new file mode 100644 index 0000000..a9c7b5a --- /dev/null +++ b/src/VAX/vax_io.cpp @@ -0,0 +1,1308 @@ +/* vax_io.c: VAX 3900 Qbus IO simulator + + Copyright (c) 1998-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. + + qba Qbus adapter + + 28-May-08 RMS Inlined physical memory routines + 25-Jan-08 RMS Fixed declarations (from Mark Pizzolato) + 03-Dec-05 RMS Added SHOW QBA VIRT and ex/dep via map + 05-Oct-05 RMS Fixed bug in autoconfiguration (missing XU) + 25-Jul-05 RMS Revised autoconfiguration algorithm and interface + 30-Sep-04 RMS Revised Qbus interface + Moved mem_err, crd_err interrupts here from vax_cpu.c + 09-Sep-04 RMS Integrated powerup into RESET (with -p) + 05-Sep-04 RMS Added CRD interrupt handling + 28-May-04 RMS Revised I/O dispatching (from John Dundas) + 21-Mar-04 RMS Added RXV21 support + 21-Dec-03 RMS Fixed bug in autoconfigure vector assignment; added controls + 21-Nov-03 RMS Added check for interrupt slot conflict (found by Dave Hittner) + 29-Oct-03 RMS Fixed WriteX declaration (found by Mark Pizzolato) + 19-Apr-03 RMS Added optimized byte and word DMA routines + 12-Mar-03 RMS Added logical name support + 22-Dec-02 RMS Added console halt support + 12-Oct-02 RMS Added autoconfigure support + Added SHOW IO space routine + 29-Sep-02 RMS Added dynamic table support + 07-Sep-02 RMS Added TMSCP and variable vector support +*/ + +#include "sim_defs.h" +#include "vax_defs.h" + +/* + * For more information on CQBIC see "KA655 CPU System Maintenance" manual, pp 1.3, 1.5, 1.8, 1.11. + */ + +/* CQBIC system configuration register */ + +#define CQSCR_POK 0x00008000 /* power ok RO1 */ +#define CQSCR_BHL 0x00004000 /* BHALT enb */ +#define CQSCR_AUX 0x00000400 /* aux mode RONI */ +#define CQSCR_DBO 0x0000000C /* offset NI */ +#define CQSCR_RW (CQSCR_BHL | CQSCR_DBO) +#define CQSCR_MASK (CQSCR_RW | CQSCR_POK | CQSCR_AUX) + +/* CQBIC DMA system error register - W1C */ + +#define CQDSER_BHL 0x00008000 /* BHALT NI */ +#define CQDSER_DCN 0x00004000 /* DC ~OK NI */ +#define CQDSER_MNX 0x00000080 /* master NXM */ +#define CQDSER_MPE 0x00000020 /* master par NI */ +#define CQDSER_SME 0x00000010 /* slv mem err NI */ +#define CQDSER_LST 0x00000008 /* lost err */ +#define CQDSER_TMO 0x00000004 /* no grant NI */ +#define CQDSER_SNX 0x00000001 /* slave NXM */ +#define CQDSER_ERR (CQDSER_MNX | CQDSER_MPE | CQDSER_TMO | CQDSER_SNX) +#define CQDSER_MASK 0x0000C0BD + +/* CQBIC master error address register */ + +#define CQMEAR_MASK 0x00001FFF /* Qbus page */ + +/* CQBIC slave error address register */ + +#define CQSEAR_MASK 0x000FFFFF /* mem page */ + +/* CQBIC map base register */ + +#define CQMBR_MASK 0x1FFF8000 /* 32KB aligned */ + +/* CQBIC IPC register */ + +#define CQIPC_QME 0x00008000 /* Qbus read NXM W1C */ +#define CQIPC_INV 0x00004000 /* CAM inval NIWO */ +#define CQIPC_AHLT 0x00000100 /* aux halt NI */ +#define CQIPC_DBIE 0x00000040 /* dbell int enb NI */ +#define CQIPC_LME 0x00000020 /* local mem enb */ +#define CQIPC_DB 0x00000001 /* doorbell req NI */ +#define CQIPC_W1C CQIPC_QME +#define CQIPC_RW (CQIPC_AHLT | CQIPC_DBIE | CQIPC_LME | CQIPC_DB) +#define CQIPC_MASK (CQIPC_RW | CQIPC_QME ) + +/* CQBIC map entry */ + +#define CQMAP_VLD 0x80000000 /* valid */ +#define CQMAP_PAG 0x000FFFFF /* mem page */ + +/* VAX/VMS definitions */ + +#define IPL_VMS_QUEUEAST 6 /* QUEUEAST, lowest spinlock-holding IPL level */ +#define IPL_VMS_IOPOST 6 /* IOPOST, lowest kernel short-term resource holding level */ + +/* + * SCR, DSER, MEAR, SEAR and IPC were moved to per-CPU context. + * MBR is shared by all CPUs, but is writable by CPU0 only. + */ + +atomic_int32 cq_mbr = 0; /* MBR */ +int32 autcon_enb = 1; /* autoconfig enable */ +uint32 synclk_safe_cycles = 2000; /* minimum VCPU cycles between consequtive CLK events raised by SYNCLK */ +int32 synclk_safe_ipl = IPL_VMS_QUEUEAST; /* minimum IPL level to be protected against starvation by synclk_safe_cycles */ + +extern int32 sim_switches; +extern SMP_FILE *sim_log; + +t_stat dbl_rd (int32 *data, int32 addr, int32 access); +t_stat dbl_wr (int32 data, int32 addr, int32 access); +void cq_merr (int32 pa); +void cq_serr (int32 pa); +t_stat qba_reset (DEVICE *dptr); +t_stat qba_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); +t_stat qba_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); +t_bool qba_map_addr (RUN_DECL, uint32 qa, uint32 *ma); +t_bool qba_map_addr_c (RUN_DECL, uint32 qa, uint32 *ma); +t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_autocon (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat show_iospace (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat qba_show_virt (SMP_FILE *of, UNIT *uptr, int32 val, void *desc); + +/* Qbus adapter data structures + + qba_dev QBA device descriptor + qba_unit QBA units + qba_reg QBA register list +*/ + +DIB qba_dib = { IOBA_DBL, IOLN_DBL, &dbl_rd, &dbl_wr, 0 }; + +UNIT qba_unit UDATA_SINGLE (NULL, 0, 0); +UNIT_TABLE_SINGLE(qba_unit); + +REG qba_reg[] = { + { HRDATA_CPU ("SCR", r_cq_scr, 16) }, + { HRDATA_CPU ("DSER", r_cq_dser, 8) }, + { HRDATA_CPU ("MEAR", r_cq_mear, 13) }, + { HRDATA_CPU ("SEAR", r_cq_sear, 20) }, + { HRDATA_GBL (MBR, cq_mbr, 29) }, + { HRDATA_CPU ("IPC", r_cq_ipc, 16) }, + { IRDATA_LVL (IPL17, 17, 0), REG_RO }, + { IRDATA_LVL (IPL16, 16, 0), REG_RO }, + { IRDATA_LVL (IPL15, 15, 0), REG_RO }, + { IRDATA_LVL (IPL14, 14, 0), REG_RO }, + { FLDATA_GBL (AUTOCON, autcon_enb, 0), REG_HRO }, + { NULL } + }; + +MTAB qba_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "IOSPACE", NULL, + NULL, &show_iospace }, + { MTAB_XTD|MTAB_VDV, 1, "AUTOCONFIG", "AUTOCONFIG", + &set_autocon, &show_autocon }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOAUTOCONFIG", + &set_autocon, NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL, + NULL, &qba_show_virt }, + { 0 } + }; + +DEVICE qba_dev = { + "QBA", qba_unit_table, qba_reg, qba_mod, + 1, 16, CQMAWIDTH, 2, 16, 16, + &qba_ex, &qba_dep, &qba_reset, + NULL, NULL, NULL, + &qba_dib, DEV_QBUS + }; + +/* IO page dispatches */ + +t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md); +t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md); + +/* Interrupt request to interrupt action map */ + +SIM_ALIGN_PTR int32 (* volatile int_ack[IPL_HLVL][32])(); /* int ack routines */ + +/* Interrupt request to vector map */ + +atomic_int32 int_vec[IPL_HLVL][32]; /* int req to vector */ + +/* The KA65x handles errors in I/O space as follows + + - read: set DSER<7>, latch addr in MEAR, machine check + - write: set DSER<7>, latch addr in MEAR, MEMERR interrupt +*/ + +int32 ReadQb (RUN_DECL, uint32 pa) +{ + int32 idx, val; + + idx = (pa & IOPAGEMASK) >> 1; + if (iodispR[idx]) + { + iodispR[idx] (&val, pa, READ); + return val; + } + cq_merr (pa); + MACH_CHECK (MCHK_READ); + return 0; +} + +void WriteQb (RUN_DECL, uint32 pa, int32 val, int32 mode) +{ + int32 idx; + + idx = (pa & IOPAGEMASK) >> 1; + if (iodispW[idx]) + { + iodispW[idx] (val, pa, mode); + return; + } + cq_merr (pa); + mem_err = 1; +} + +/* ReadIO - read I/O space + + Inputs: + pa = physical address + lnt = length (BWLQ) + Output: + longword of data +*/ + +int32 ReadIO (RUN_DECL, uint32 pa, int32 lnt) +{ + int32 iod; + + iod = ReadQb (RUN_PASS, pa); /* wd from Qbus */ + if (lnt < L_LONG) /* bw? position */ + iod = iod << ((pa & 2)? 16: 0); + else + iod = (ReadQb (RUN_PASS, pa + 2) << 16) | iod; /* lw, get 2nd wd */ + + /* + * Checking here for the change in pending interrupts made sense on a uniprocessor version of the emulator. + * In the SMP version, device interrupt may be delivered to another processor. + * Therefore moved the check to instruction loop, except for Qbus memory access errors. + */ + if (mem_err) + { + SET_IRQL; + } + + return iod; +} + +/* WriteIO - write I/O space + + Inputs: + pa = physical address + val = data to write, right justified in 32b longword + lnt = length (BWLQ) + Outputs: + none +*/ + +void WriteIO (RUN_DECL, uint32 pa, int32 val, int32 lnt) +{ + if (lnt == L_BYTE) + WriteQb (RUN_PASS, pa, val, WRITEB); + else if (lnt == L_WORD) + WriteQb (RUN_PASS, pa, val, WRITE); + else + { + WriteQb (RUN_PASS, pa, val & 0xFFFF, WRITE); + WriteQb (RUN_PASS, pa + 2, (val >> 16) & 0xFFFF, WRITE); + } + + /* + * Checking here for the change in pending interrupts made sense on a uniprocessor version of the emulator. + * In the SMP version, device interrupt may be delivered to another processor. + * Therefore moved the check to instruction loop, except for Qbus memory access errors. + */ + if (mem_err) + { + SET_IRQL; + } +} + +/* + * Find highest priority outstanding deliverable interrupt, called by SET_IRQL. + * + * As a side effect, record highest pending interrupt (whether deliverable or not) + * and adjust thread priority. + */ + +int32 eval_int (RUN_DECL, t_bool sync) +{ + RUN_SCOPE_RSCX_ONLY; + + int32 cipl = PSL_GETIPL (PSL); + t_bool was_changed = FALSE; + int32 hipl = 0; + t_bool synced = FALSE; + t_bool nmi = FALSE; + + /* + * Cancel protection period against raising CLK by pending SYNCLK + * if IPL drops below a threshold or goes out of kernel mode. + */ + if (unlikely(cpu_unit->cpu_synclk_protect_os)) + { + t_bool cancel_protection = FALSE; + + if (PSL_GETCUR(PSL)) + { + cancel_protection = TRUE; + } + else if (sys_critical_section_ipl >= 0) + { + cancel_protection = (cipl < sys_critical_section_ipl); + } + else + { + cancel_protection = (cipl < synclk_safe_ipl); + } + + if (cancel_protection) + { + if (cpu_unit->cpu_synclk_pending != SynclkPendingIE1) + { + cpu_unit->cpu_synclk_protect_os = 0; + + if (cpu_unit->cpu_synclk_protect_dev == 0) + { + cpu_unit->cpu_synclk_protect = FALSE; + check_synclk_pending(RUN_PASS); + } + } + } + } + + if (unlikely(cpu_unit->cpu_intreg.weak_changed())) + { + if (was_changed = cpu_unit->cpu_intreg.cas_changed(1, 0)) + { + smp_post_interlocked_rmb(); + read_irqs_to_local(RUN_PASS); + synced = TRUE; + } + } + + if (likely(sync) && !synced) + { + smp_rmb(); + read_irqs_to_local(RUN_PASS); + synced = TRUE; + } + + if (unlikely(hlt_pin)) /* hlt pin int */ + { + hipl = IPL_HLTPIN; + nmi = TRUE; + } + else if (unlikely(mem_err)) /* mem err int */ + { + hipl = IPL_MEMERR; + } + else if (unlikely(crd_err)) /* crd err int */ + { + hipl = IPL_CRDERR; + } + else if (hipl = cpu_unit->cpu_intreg.highest_local_irql()) + { + // use it + } + else if (SISR) + { + for (int32 i = IPL_SMAX; i >= 1; i--) + { + if (SISR & (1 << i)) + { + hipl = i; + break; + } + } + } + + cpu_unit->cpu_context.highest_irql = hipl; + + if (rscx->thread_type == SIM_THREAD_TYPE_CPU) + { + /* + * Interrupt sender may kick target priority to CRITICAL_VM without reflecting it in cpu_unit->cpu_thread_priority. + * Therefore when we receive an interrupt, clear cached record of thread priority in order to force actual + * thread priority change even when new calculated priority evaluates to the priority the thread was recorded + * to hold before. + */ + if (was_changed && must_control_prio()) + cpu_unit->cpu_thread_priority = SIMH_THREAD_PRIORITY_INVALID; + cpu_reevaluate_thread_priority(RUN_PASS, synced); + } + else if (rscx->thread_type == SIM_THREAD_TYPE_CONSOLE) + { + /* + * Called on the console thread, e.g. when writing to SIRR. + * + * Pretty much could do just nothing at all, since reevaluation of thread priority will be performed + * anyway by SET_IRQL at the start of resumed instruction loop. However for a marginal case of starved + * VCPU thread (e.g. trying to break into XDelta while other VCPU threads are hung spinning at elevated IPL) + * boost target thread's priority here. + */ + t_bool is_active_clk_interrupt; + t_bool is_active_ipi_interrupt; + + if (sys_critical_section_ipl >= 0 && (int32) hipl >= sys_critical_section_ipl) + { + /* console thread has access to xcpu->cpu_thread_priority */ + switch (cpu_unit->cpu_thread_priority) + { + case SIMH_THREAD_PRIORITY_CPU_CALIBRATION: + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM: + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI: + break; + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS: + cpu_unit->cpu_intreg.query_local_clk_ipi(& is_active_clk_interrupt, & is_active_ipi_interrupt); + if (is_active_clk_interrupt) cpu_unit->cpu_active_clk_interrupt = TRUE; + if (is_active_ipi_interrupt) cpu_unit->cpu_active_ipi_interrupt = TRUE; + if (cpu_unit->cpu_active_clk_interrupt || cpu_unit->cpu_active_ipi_interrupt) + { + if (must_control_prio()) + smp_set_thread_priority(cpu_unit->cpu_thread, SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI); + cpu_unit->cpu_thread_priority = SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI; + } + break; + default: + if (must_control_prio()) + smp_set_thread_priority(cpu_unit->cpu_thread, SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS); + cpu_unit->cpu_thread_priority = SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS; + break; + } + } + } + + return (hipl > cipl || nmi) ? hipl : 0; +} + +/* Return vector for highest priority hardware interrupt at IPL lvl, + called after previous eval_int */ + +int32 get_vector (RUN_DECL, int32 lvl) +{ + if (lvl == IPL_MEMERR) /* mem error? */ + { + mem_err = 0; + return SCB_MEMERR; + } + else if (lvl == IPL_CRDERR) /* CRD error? */ + { + crd_err = 0; + return SCB_CRDERR; + } + else if (lvl > IPL_HMAX) /* error req lvl? */ + { + ABORT (STOP_UIPL); /* unknown intr */ + } + + uint32 dev; + + if (cpu_unit->cpu_intreg.check_int_atipl_clr(RUN_PASS, (uint32) lvl, & dev)) + { + int32 l = lvl - IPL_HMIN; + + /* + * Execute RMB -- even if requesting device is per-CPU, since there may be + * other interrupts pending, for system-wide devices, and there potentially + * may be a tricky dependence. + */ + smp_post_interlocked_rmb(); + + if (l == IPL_CLK && dev == INT_V_CLK) + { + /* entering CLK ISR */ + cpu_unit->cpu_active_clk_interrupt = TRUE; + } + else if (l == IPL_IPINTR && dev == INT_V_IPINTR) + { + /* entering IPI ISR */ + cpu_unit->cpu_active_ipi_interrupt = TRUE; + } + + int32 (*ack)() = weak_read(int_ack[l][dev]); + if (ack) + return (*ack)(); + else + return weak_read(int_vec[l][dev]); + } + + return 0; +} + +/* + * Copy IRQs from interlocked to local storage. + * Caller must execute RMB before calling read_irqs_to_local. + */ +void read_irqs_to_local(RUN_DECL) +{ + for (;;) + { + cpu_unit->cpu_intreg.copy_irqs_to_local(); + + /* + * process internal non-maskable interrupts + */ + + /* handle IPIRMB and reload */ + if (unlikely(cpu_unit->cpu_intreg.is_local_int(IPL_ABS_IPIRMB, INT_V_IPIRMB))) + { + cpu_unit->cpu_intreg.dismiss_int(RUN_PASS, IPL_ABS_IPIRMB, INT_V_IPIRMB); + smp_post_interlocked_rmb(); + continue; + } + + /* notification: secondary VCPU had stopped */ + if (unlikely(cpu_unit->cpu_intreg.is_local_int(IPL_ABS_SECEXIT, INT_V_SECEXIT))) + { + cpu_unit->cpu_intreg.dismiss_int(RUN_PASS, IPL_ABS_SECEXIT, INT_V_SECEXIT); + + /* + * Transfer pending events for system-wide device from exited secndary (or exited secondaries) + * to the primary. + */ + sim_requeue_syswide_events(RUN_PASS); + + /* + * If last secondary had exited and only the primary remains active, stop managing thread + * priority and drop primary VCPU actual thread priority down to CPU_RUN level. The only + * (extremely unlikely) exception is if calibration is active, in which case priority will + * be dropped later when processor exits CALIBRATION level. + * + * For more detailed explanation of what is done here and why, refer to the comment in cpu_once_a_second(). + */ + if (!must_control_prio() && !tmr_is_active(RUN_PASS)) + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_RUN); + + continue; + } + + /* notification: this VCPU had been entered into synchronization subwindow SYNCW_SYS by another VCPU */ + if (unlikely(cpu_unit->cpu_intreg.is_local_int(IPL_ABS_SYNCWSYS, INT_V_SYNCWSYS))) + { + syncw_process_syncwsys_interrupt(RUN_PASS); + cpu_unit->cpu_intreg.dismiss_int(RUN_PASS, IPL_ABS_SYNCWSYS, INT_V_SYNCWSYS); + continue; + } + + /* handle SYNCLK (sent by clock strobe thread) */ + if (cpu_unit->cpu_intreg.is_local_int(IPL_ABS_SYNCLK, INT_V_SYNCLK)) + { + cpu_unit->cpu_intreg.dismiss_int(RUN_PASS, IPL_ABS_SYNCLK, INT_V_SYNCLK); + + /* record current CPU cycles count at SYNCLK for future delta timing */ + cpu_unit->cpu_last_synclk_cycles = CPU_CURRENT_CYCLES; + + if (cpu_unit->cpu_synclk_protect == FALSE) + { + /* process SYNCLK and set protection period */ + SynclkPending sv_pending = cpu_unit->cpu_synclk_pending; + + if (clk_csr & CSR_IE) + cpu_unit->cpu_synclk_protect_os = synclk_safe_cycles; + cpu_unit->cpu_synclk_protect_dev = (uint32) sim_calculate_device_activity_protection_interval(RUN_PASS); + cpu_unit->cpu_synclk_protect = cpu_unit->cpu_synclk_protect_os && cpu_unit->cpu_synclk_protect_dev; + process_synclk(RUN_PASS, TRUE); + + /* + * Leave cpu_unit->cpu_synclk_pending at previous value. + * Normally it would be NotPending when cpu_synclk_protect is FALSE and will be left NotPending. + * In the "should not happen" case it was set, leave it set, but record current state of CLK CSR_IE. + */ + if (sv_pending != SynclkNotPending) /* should not happen, but handle anyway */ + cpu_unit->cpu_synclk_pending = (clk_csr & CSR_IE) ? SynclkPendingIE1 : SynclkPendingIE0; + } + else if (cpu_unit->cpu_synclk_pending != SynclkNotPending) + { + /* + * Clock interrupt generation from previous SYNCLK signal is already pending (as soon as + * cpu_synclk_protect is counted down), just dismiss newly received signal + * and "lose" new clock interrupt. + */ + cpu_unit->cpu_synclk_pending = (clk_csr & CSR_IE) ? SynclkPendingIE1 : SynclkPendingIE0; + } + else + { + /* + * Still inside the interval protected from raising CLK by SYNCLK. + * Mark SYNCLK processing as pending. + */ + cpu_unit->cpu_synclk_pending = (clk_csr & CSR_IE) ? SynclkPendingIE1 : SynclkPendingIE0; + cpu_reevaluate_thread_priority(RUN_PASS); + } + } + + /* handle ASYNC_IO */ + if (cpu_unit->cpu_intreg.is_local_int(IPL_ABS_ASYNC_IO, INT_V_ASYNC_IO)) + { + cpu_unit->cpu_intreg.dismiss_int(RUN_PASS, IPL_ABS_ASYNC_IO, INT_V_ASYNC_IO); + sim_async_process_io_events(RUN_PASS); + continue; + } + + /* handle emergency STOP request sent by the primary */ + if (cpu_unit->cpu_intreg.is_local_int(IPL_ABS_STOP, INT_V_STOP)) + { + cpu_unit->cpu_intreg.dismiss_int(RUN_PASS, IPL_ABS_STOP, INT_V_STOP); + cpu_unit->cpu_dostop = TRUE; + sim_activate_abs(cpu_unit, 0); + syncw_leave_all(RUN_PASS, SYNCW_OVERRIDE_ALL | SYNCW_DISABLE_CPU); + } + + break; + } +} + +/* + * Check if SYNCLK event is pending, and if so, process it. + */ +t_bool check_synclk_pending(RUN_DECL) +{ + if (cpu_unit->cpu_synclk_pending != SynclkNotPending) + { + t_bool clk_ie = cpu_unit->cpu_synclk_pending == SynclkPendingIE1; + cpu_unit->cpu_synclk_pending = SynclkNotPending; + process_synclk(RUN_PASS, clk_ie); + syncw_reeval_sys(RUN_PASS); + return TRUE; + } + else + { + return FALSE; + } +} + +/* + * Process SYNCLK event. + */ +void process_synclk(RUN_DECL, t_bool clk_ie) +{ + /* schedule events co-scheduled with clock for immediate execution */ + sim_reschedule_cosched(RUN_PASS, RescheduleCosched_OnSynClk); + + /* invoke clock handler */ + clk_svc_ex(RUN_PASS, clk_ie); + + cpu_reevaluate_thread_priority(RUN_PASS); +} + +/* CQBIC registers + + SCR system configuration register + DSER DMA system error register (W1C) + MEAR master error address register (RO) + SEAR slave error address register (RO) + MBR map base register + IPC inter-processor communication register +*/ + +int32 cqbic_rd (RUN_DECL, int32 pa) +{ + int32 rg = (pa - CQBICBASE) >> 2; + + switch (rg) + { + case 0: /* SCR */ + return (cq_scr | CQSCR_POK) & CQSCR_MASK; + + case 1: /* DSER */ + return cq_dser & CQDSER_MASK; + + case 2: /* MEAR */ + return cq_mear & CQMEAR_MASK; + + case 3: /* SEAR */ + return cq_sear & CQSEAR_MASK; + + case 4: /* MBR */ + return cq_mbr & CQMBR_MASK; + } + return 0; +} + +void cqbic_wr (RUN_DECL, int32 pa, int32 val, int32 lnt) +{ + int32 nval, rg = (pa - CQBICBASE) >> 2; + + if (lnt < L_LONG) + { + int32 sc = (pa & 3) << 3; + int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; + int32 t = cqbic_rd (RUN_PASS, pa); + nval = ((val & mask) << sc) | (t & ~(mask << sc)); + val = val << sc; + } + else + { + nval = val; + } + + switch (rg) + { + case 0: /* SCR */ + cq_scr = ((cq_scr & ~CQSCR_RW) | (nval & CQSCR_RW)) & CQSCR_MASK; + break; + + case 1: /* DSER */ + cq_dser = (cq_dser & ~val) & CQDSER_MASK; + if (val & CQDSER_SME) + cq_ipc = cq_ipc & ~CQIPC_QME; + break; + + case 2: case 3: + cq_merr (pa); /* MEAR, SEAR */ + MACH_CHECK (MCHK_WRITE); + break; + + case 4: /* MBR */ + if (! cpu_unit->is_primary_cpu()) + { + smp_printf ("\nNon-primary CPU (CPU%d) attempted to write QBus map base register (CQBIC MBR)\n", cpu_unit->cpu_id); + if (sim_log) + fprintf (sim_log, "Non-primary CPU (CPU%d) attempted to write QBus map base register (CQBIC MBR)\n", cpu_unit->cpu_id); + ABORT_INVALID_SYSOP; + } + /* + * We do not execute memory barrier when writing or reading MBR, that would + * degrade performance during QBus references. + * + * It is presumed MBR will be set by the primary (boot) processor at operating system + * initialization time only, before other processors are started. + * + * In most unlikely case that OS should ever want to change MBR after booting, + * it will do it within proper locking protocol on adapter structure that will ultimately + * use VAX interlocked instuctions and thus cause memory barriers. + */ + cq_mbr = nval & CQMBR_MASK; + break; + } +} + +/* IPC can be read as local register or as Qbus I/O + Because of the W1C */ + +int32 cqipc_rd (RUN_DECL, int32 pa) +{ + return cq_ipc & CQIPC_MASK; /* IPC */ +} + +void cqipc_wr (RUN_DECL, int32 pa, int32 val, int32 lnt) +{ + int32 nval = val; + + if (lnt < L_LONG) + { + int32 sc = (pa & 3) << 3; + nval = val << sc; + } + + cq_ipc = cq_ipc & ~(nval & CQIPC_W1C); /* W1C */ + if ((pa & 3) == 0) /* low byte only */ + cq_ipc = ((cq_ipc & ~CQIPC_RW) | (val & CQIPC_RW)) & CQIPC_MASK; +} + +/* I/O page routines */ + +t_stat dbl_rd (int32 *data, int32 addr, int32 access) +{ + RUN_SCOPE; + *data = cq_ipc & CQIPC_MASK; + return SCPE_OK; +} + +t_stat dbl_wr (int32 data, int32 addr, int32 access) +{ + RUN_SCOPE; + cqipc_wr (RUN_PASS, addr, data, (access == WRITEB)? L_BYTE: L_WORD); + return SCPE_OK; +} + +/* + * CQBIC map read and write (reflects to main memory) + * + * Read error: set DSER<0>, latch slave address, machine check + * Write error: set DSER<0>, latch slave address, memory error interrupt + */ + +int32 cqmap_rd (RUN_DECL, int32 pa) +{ + int32 ma = (pa & CQMAPAMASK) + cq_mbr; /* mem addr */ + + if (ADDR_IS_MEM (ma)) + return M[ma >> 2]; + cq_serr (ma); /* set err */ + MACH_CHECK (MCHK_READ); /* mcheck */ + return 0; +} + +void cqmap_wr (RUN_DECL, int32 pa, int32 val, int32 lnt) +{ + int32 ma = (pa & CQMAPAMASK) + cq_mbr; /* mem addr */ + + if (ADDR_IS_MEM (ma)) + { + if (lnt < L_LONG) + { + int32 sc = (pa & 3) << 3; + int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; + int32 t = M[ma >> 2]; + val = ((val & mask) << sc) | (t & ~(mask << sc)); + } + M[ma >> 2] = val; + } + else + { + cq_serr (ma); /* error */ + mem_err = 1; + } +} + +/* + * CQBIC Qbus memory read and write (reflects to main memory). + * May give master or slave error, depending on where the failure occurs + */ + +int32 cqmem_rd (RUN_DECL, int32 pa) +{ + int32 qa = pa & CQMAMASK; /* Qbus addr */ + uint32 ma; + + if (qba_map_addr (RUN_PASS, qa, &ma)) /* map addr */ + return M[ma >> 2]; + MACH_CHECK (MCHK_READ); /* err? mcheck */ + return 0; +} + +void cqmem_wr (RUN_DECL, int32 pa, int32 val, int32 lnt) +{ + int32 qa = pa & CQMAMASK; /* Qbus addr */ + uint32 ma; + + if (qba_map_addr (RUN_PASS, qa, &ma)) /* map addr */ + { + if (lnt < L_LONG) + { + int32 sc = (pa & 3) << 3; + int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; + int32 t = M[ma >> 2]; + val = ((val & mask) << sc) | (t & ~(mask << sc)); + } + M[ma >> 2] = val; + } + else + { + mem_err = 1; + } +} + +/* Map an address via the translation map */ + +t_bool qba_map_addr (RUN_DECL, uint32 qa, uint32 *ma) +{ + int32 qblk = (qa >> VA_V_VPN); /* Qbus blk */ + int32 qmma = ((qblk << 2) & CQMAPAMASK) + cq_mbr; /* map entry */ + + if (ADDR_IS_MEM (qmma)) /* legit? */ + { + int32 qmap = M[qmma >> 2]; /* get map */ + if (qmap & CQMAP_VLD) /* valid? */ + { + *ma = ((qmap & CQMAP_PAG) << VA_V_VPN) + VA_GETOFF (qa); + if (ADDR_IS_MEM (*ma)) /* legit addr */ + return TRUE; + cq_serr (*ma); /* slave nxm */ + return FALSE; + } + cq_merr (qa); /* master nxm */ + return FALSE; + } + cq_serr (0); /* inv mem */ + return FALSE; +} + +/* Map an address via the translation map - console version (no status changes) */ + +t_bool qba_map_addr_c (RUN_DECL, uint32 qa, uint32 *ma) +{ + int32 qblk = (qa >> VA_V_VPN); /* Qbus blk */ + int32 qmma = ((qblk << 2) & CQMAPAMASK) + cq_mbr; /* map entry */ + + if (ADDR_IS_MEM (qmma)) /* legit? */ + { + int32 qmap = M[qmma >> 2]; /* get map */ + if (qmap & CQMAP_VLD) /* valid? */ + { + *ma = ((qmap & CQMAP_PAG) << VA_V_VPN) + VA_GETOFF (qa); + return TRUE; /* legit addr */ + } + } + return FALSE; +} + +/* Set master error */ + +void cq_merr (int32 pa) +{ + RUN_SCOPE; + if (cq_dser & CQDSER_ERR) + cq_dser = cq_dser | CQDSER_LST; + cq_dser = cq_dser | CQDSER_MNX; /* master nxm */ + cq_mear = (pa >> VA_V_VPN) & CQMEAR_MASK; /* page addr */ +} + +/* Set slave error */ + +void cq_serr (int32 pa) +{ + RUN_SCOPE; + if (cq_dser & CQDSER_ERR) + cq_dser = cq_dser | CQDSER_LST; + cq_dser = cq_dser | CQDSER_SNX; /* slave nxm */ + cq_sear = (pa >> VA_V_VPN) & CQSEAR_MASK; +} + +/* Reset I/O bus */ + +void ioreset_wr (RUN_DECL, int32 data) +{ + /* disrallow writing to IORESET on any processor but the primary */ + if (cpu_unit->is_primary_cpu()) + { + reset_all (sim_device_index (&qba_dev)); /* reset from qba on... */ + } + else + { + smp_printf ("\nNon-primary CPU (CPU%d) attempted to reset QBus and IO devices by writing to IORESET register\n", cpu_unit->cpu_id); + if (sim_log) + fprintf (sim_log, "Non-primary CPU (CPU%d) attempted to reset QBus and IO devices by writing to IORESET register\n", cpu_unit->cpu_id); + ABORT_INVALID_SYSOP; + } +} + +/* Reset CQBIC */ + +t_stat qba_reset (DEVICE *dptr) +{ + RUN_SCOPE; + + if (! cpu_unit->is_primary_cpu()) + { + // QBA reset should be performed only by the primary CPU + smp_printf ("\nNon-primary CPU (CPU%d) attempted to reset QBus\n", cpu_unit->cpu_id); + if (sim_log) + fprintf (sim_log, "Non-primary CPU (CPU%d) attempted to reset QBus\n", cpu_unit->cpu_id); + ABORT_INVALID_SYSOP; + } + + if (sim_switches & SWMASK ('P')) + { + /* Powerup CQBIC */ + if (! smp_check_aligned(& cq_mbr, FALSE) || + ! smp_check_aligned((atomic_int32*) int_vec, FALSE) || + ! smp_check_aligned(& ((DIB*)0)->vec, FALSE)) + { + return SCPE_IERR; + } + cq_mbr = 0; + cqbic_reset_percpu(RUN_PASS, TRUE); + } + else + { + cqbic_reset_percpu(RUN_PASS, FALSE); + } + + /* + * Reset interrupts for all QBus devices. + * + * Note we cannot do generic cpu_unit->cpu_intreg.reset() since that resets + * not only QBus interrupts, but all other interrupts for the primary CPU, + * such as IPIRMB, SECEXIT, SYNCWSYS, ASYNC_IO, CLK, SYNCLK, IPINTR etc., that should be retained. + */ + CLR_INT(RQ); + CLR_INT(RL); + CLR_INT(DZRX); + CLR_INT(DZTX); + CLR_INT(TS); + CLR_INT(TQ); + CLR_INT(XQ); + CLR_INT(RY); + // CLR_INT(TTI); // TTI is not architecturally a QBus device + // CLR_INT(TTO); // TTO is not architecturally a QBus device + // CLR_INT(PTR); // PTR is not a part of VAX MP + // CLR_INT(PTP); // PTP is not a part of VAX MP + CLR_INT(LPT); + CLR_INT(CSI); + CLR_INT(CSO); + CLR_INT(TMR0); + CLR_INT(TMR1); + CLR_INT(VHRX); + CLR_INT(VHTX); + CLR_INT(QDSS); + CLR_INT(CR); + + return SCPE_OK; +} + +void cqbic_reset_percpu(RUN_DECL, t_bool powerup) +{ + if (powerup) + cq_scr = CQSCR_POK; + else + cq_scr = (cq_scr & CQSCR_BHL) | CQSCR_POK; + + cq_dser = cq_mear = cq_sear = cq_ipc = 0; +} + +/* Qbus I/O buffer routines, aligned access + + Map_ReadB - fetch byte buffer from memory + Map_ReadW - fetch word buffer from memory + Map_WriteB - store byte buffer into memory + Map_WriteW - store word buffer into memory +*/ + +int32 Map_ReadB (RUN_DECL, uint32 ba, int32 bc, uint8 *buf) +{ + int32 i; + uint32 ma, dat; + + if ((ba | bc) & 03) /* check alignment */ + { + for (i = ma = 0; i < bc; i++, buf++) /* by bytes */ + { + if ((ma & VA_M_OFF) == 0) /* need map? */ + { + if (!qba_map_addr (RUN_PASS, ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + *buf = ReadB (RUN_PASS, ma); + ma = ma + 1; + } + } + else + { + for (i = ma = 0; i < bc; i = i + 4, buf++) /* by longwords */ + { + if ((ma & VA_M_OFF) == 0) /* need map? */ + { + if (!qba_map_addr (RUN_PASS, ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + dat = ReadL_NA (RUN_PASS, ma); /* get lw */ + *buf++ = dat & BMASK; /* low 8b */ + *buf++ = (dat >> 8) & BMASK; /* next 8b */ + *buf++ = (dat >> 16) & BMASK; /* next 8b */ + *buf = (dat >> 24) & BMASK; + ma = ma + 4; + } + } + return 0; +} + +int32 Map_ReadW (RUN_DECL, uint32 ba, int32 bc, uint16 *buf) +{ + int32 i; + uint32 ma,dat; + + /* + * Multiprocessor note: + * + * Devices that interact with CPU via shared memory structures (XQ, TQ, RQ) need + * to fetch and store certain elements as an atomic operation. This includes words + * in XQ BDL descriptor and words in UQSSP (RQ, TQ) command ring and com-area. + * Map_ReadW must ensure that they are read from memory atomically, as a word, + * by using host "read word" instruction, and without splitting them into multiple byte reads. + * + * Implementation of ReadW_NA/ReadL_NA for x86/x64 ensures it, for other processors + * code for Map_ReadW may need to be revised to ensure atomicity of QBus word transactions, + * whenever required. + * + * Also, should Map_ReadW be optimizied in the future to use memcpy, this optimization + * should not apply for small reads that may be atomic (identified as either with bc == 2 + * or perhaps special flag passed from XQ, TQ and RQ). + */ + + ba = ba & ~01; + bc = bc & ~01; + if ((ba | bc) & 03) /* check alignment */ + { + for (i = ma = 0; i < bc; i = i + 2, buf++) /* by words */ + { + if ((ma & VA_M_OFF) == 0) /* need map? */ + { + if (!qba_map_addr (RUN_PASS, ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + *buf = ReadW_NA (RUN_PASS, ma); + ma = ma + 2; + } + } + else + { + for (i = ma = 0; i < bc; i = i + 4, buf++) /* by longwords */ + { + if ((ma & VA_M_OFF) == 0) /* need map? */ + { + if (!qba_map_addr (RUN_PASS, ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + dat = ReadL_NA (RUN_PASS, ma); /* get lw */ + *buf++ = dat & WMASK; /* low 16b */ + *buf = (dat >> 16) & WMASK; /* high 16b */ + ma = ma + 4; + } + } + return 0; +} + +int32 Map_WriteB (RUN_DECL, uint32 ba, int32 bc, uint8 *buf) +{ + int32 i; + uint32 ma, dat; + + if ((ba | bc) & 03) /* check alignment */ + { + for (i = ma = 0; i < bc; i++, buf++) /* by bytes */ + { + if ((ma & VA_M_OFF) == 0) /* need map? */ + { + if (!qba_map_addr (RUN_PASS, ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + WriteB (RUN_PASS, ma, *buf); + ma = ma + 1; + } + } + else + { + for (i = ma = 0; i < bc; i = i + 4, buf++) /* by longwords */ + { + if ((ma & VA_M_OFF) == 0) /* need map? */ + { + if (!qba_map_addr (RUN_PASS, ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + dat = (uint32) *buf++; /* get low 8b */ + dat = dat | (((uint32) *buf++) << 8); /* merge next 8b */ + dat = dat | (((uint32) *buf++) << 16); /* merge next 8b */ + dat = dat | (((uint32) *buf) << 24); /* merge hi 8b */ + WriteL_NA (RUN_PASS, ma, dat); /* store lw */ + ma = ma + 4; + } + } + return 0; +} + +int32 Map_WriteW (RUN_DECL, uint32 ba, int32 bc, uint16 *buf) +{ + int32 i; + uint32 ma, dat; + + /* + * Multiprocessor note: + * + * Devices that interact with CPU via shared memory structures (XQ, TQ, RQ) need + * to fetch and store certain elements as an atomic operation. This includes words + * in XQ BDL descriptor and words in UQSSP (RQ, TQ) command ring and com-area. + * Map_WriteW must ensure that they are written to memory atomically, as a word, + * by using host "write word" instruction, and without splitting them into multiple byte writes. + * + * Implementation of WriteW_NA/WriteL_NA for x86/x64 ensures it, for other processors + * code for Map_WriteW may need to be revised to ensure atomicity of QBus word transactions, + * whenever required. + * + * Also, should Map_WriteW be optimizied in the future to use memcpy, this optimization + * should not apply for small writes that may be atomic (identified as either with bc == 2 + * or perhaps special flag passed from XQ, TQ and RQ). + */ + + ba = ba & ~01; + bc = bc & ~01; + if ((ba | bc) & 03) /* check alignment */ + { + for (i = ma = 0; i < bc; i = i + 2, buf++) /* by words */ + { + if ((ma & VA_M_OFF) == 0) /* need map? */ + { + if (!qba_map_addr (RUN_PASS, ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + WriteW_NA (RUN_PASS, ma, *buf); + ma = ma + 2; + } + } + else + { + for (i = ma = 0; i < bc; i = i + 4, buf++) /* by longwords */ + { + if ((ma & VA_M_OFF) == 0) /* need map? */ + { + if (!qba_map_addr (RUN_PASS, ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + dat = (uint32) *buf++; /* get low 16b */ + dat = dat | (((uint32) *buf) << 16); /* merge hi 16b */ + WriteL_NA (RUN_PASS, ma, dat); /* store lw */ + ma = ma + 4; + } + } + return 0; +} + +/* Memory examine via map (word only) */ + +t_stat qba_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +{ + RUN_SCOPE; + uint32 qa = (uint32) exta, pa; + + if ((vptr == NULL) || (qa >= CQMSIZE)) + return SCPE_ARG; + if (qba_map_addr_c (RUN_PASS, qa, &pa) && ADDR_IS_MEM (pa)) + { + *vptr = (uint32) ReadW_NA (RUN_PASS, pa); + return SCPE_OK; + } + return SCPE_NXM; +} + +/* Memory deposit via map (word only) */ + +t_stat qba_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) +{ + RUN_SCOPE; + uint32 qa = (uint32) exta, pa; + + if (qa >= CQMSIZE) + return SCPE_ARG; + if (qba_map_addr_c (RUN_PASS, qa, &pa) && ADDR_IS_MEM (pa)) + { + WriteW_NA (RUN_PASS, pa, (int32) val); + return SCPE_OK; + } + return SCPE_NXM; +} + +/* Build dib_tab from device list */ + +t_stat build_dib_tab (void) +{ + int32 i; + DEVICE *dptr; + DIB *dibp; + t_stat r; + + init_ubus_tab (); /* init bus tables */ + for (i = 0; (dptr = sim_devices[i]) != NULL; i++) /* loop thru dev */ + { + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (dibp && !(dptr->flags & DEV_DIS)) /* defined, enabled? */ + { + if (r = build_ubus_tab (dptr, dibp)) /* add to bus tab */ + return r; + } + } + return SCPE_OK; +} + +/* Show QBA virtual address */ + +t_stat qba_show_virt (SMP_FILE *of, UNIT *uptr, int32 val, void *desc) +{ + RUN_SCOPE; + t_stat r; + char *cptr = (char *) desc; + uint32 qa, pa; + + if (cptr) { + qa = (uint32) get_uint (cptr, 16, CQMSIZE - 1, &r); + if (r == SCPE_OK) { + if (qba_map_addr_c (RUN_PASS, qa, &pa)) + fprintf (of, "Qbus %-X = physical %-X\n", qa, pa); + else fprintf (of, "Qbus %-X: invalid mapping\n", qa); + return SCPE_OK; + } + } + fprintf (of, "Invalid argument\n"); + return SCPE_OK; +} diff --git a/src/VAX/vax_ka655x_bin.h b/src/VAX/vax_ka655x_bin.h new file mode 100644 index 0000000..f0f6f2c --- /dev/null +++ b/src/VAX/vax_ka655x_bin.h @@ -0,0 +1,8198 @@ +/* + VAX/vax_ka655x_bin.h produced at Tue Sep 20 04:39:07 2011 + from VAX/ka655x.bin which was last modified at Tue Sep 20 03:24:43 2011 + file size: 131072 (0x20000) +*/ +unsigned char vax_ka655x_bin[] = { +0x11,0x22,0x11,0xFE,0x02,0x03,0x53,0x01,0x31,0x89,0x03,0x00,0x31,0x8B,0x03,0x00, +0x31,0xB5,0x03,0x5A,0xA5,0x00,0x00,0x00,0xF4,0x03,0x04,0x20,0x26,0x04,0x14,0x20, +0x00,0x00,0x04,0x20,0xD0,0x8F,0x00,0x00,0x14,0x20,0x9F,0x00,0x00,0x14,0x20,0xD2, +0x9F,0x30,0x00,0x14,0x20,0x9F,0x02,0x05,0x14,0x20,0xD2,0x0E,0x9F,0x30,0x00,0x14, +0x20,0x7D,0x50,0x9F,0xB2,0x04,0x14,0x20,0xD0,0x8F,0xB2,0x04,0x14,0x20,0x51,0xDB, +0x2A,0xA1,0x44,0xDB,0x2B,0xA1,0x48,0xCA,0x8F,0x80,0x00,0x00,0x00,0x9F,0x00,0x00, +0x08,0x20,0xDB,0x2B,0x50,0xED,0x08,0x06,0x50,0x02,0x13,0x03,0x31,0x0C,0x01,0xED, +0x18,0x03,0x9F,0x00,0x04,0x14,0x20,0x01,0x13,0x03,0x31,0x86,0x00,0xDB,0x21,0x50, +0x8F,0x9F,0x05,0x00,0x04,0x20,0x01,0x02,0x10,0x00,0x07,0x00,0x19,0x00,0x00,0xD0, +0x8F,0x9C,0x1D,0x00,0x00,0x50,0x11,0x10,0xD0,0x8F,0xC4,0x18,0x00,0x00,0x50,0x11, +0x07,0xD0,0x8F,0xF8,0x20,0x00,0x00,0x50,0xF5,0x50,0xFD,0x17,0x9F,0xB1,0x00,0x06, +0x20,0x17,0x9F,0xB7,0x00,0x04,0x20,0x8F,0x9F,0x05,0x00,0x04,0x20,0x01,0x02,0x10, +0x00,0x07,0x00,0x19,0x00,0x00,0xD0,0x8F,0xCE,0x0E,0x00,0x00,0x50,0x11,0x10,0xD0, +0x8F,0x62,0x0C,0x00,0x00,0x50,0x11,0x07,0xD0,0x8F,0x7C,0x10,0x00,0x00,0x50,0xF5, +0x50,0xFD,0xF0,0x04,0x18,0x03,0x9F,0x00,0x04,0x14,0x20,0xD0,0x8F,0xB2,0x04,0x14, +0x20,0x51,0x7D,0xA1,0x20,0xA1,0x44,0x7D,0xA1,0x28,0x61,0xD0,0xA1,0x30,0xA1,0x50, +0x31,0x78,0x00,0xDB,0x20,0x50,0xE1,0x07,0x50,0x13,0xDB,0x21,0x50,0xE1,0x0B,0x50, +0x0C,0xF0,0x03,0x18,0x03,0x9F,0x00,0x04,0x14,0x20,0x31,0x5E,0x00,0xD0,0x8F,0xB2, +0x04,0x14,0x20,0x51,0x7D,0xA1,0x44,0xA1,0x20,0x7D,0x61,0xA1,0x28,0xD0,0xA1,0x50, +0xA1,0x30,0xF0,0x01,0x18,0x03,0x9F,0x00,0x04,0x14,0x20,0x8F,0x9F,0x05,0x00,0x04, +0x20,0x01,0x02,0x10,0x00,0x07,0x00,0x19,0x00,0x00,0xD0,0x8F,0x53,0x0D,0x00,0x00, +0x50,0x11,0x10,0xD0,0x8F,0x25,0x0B,0x00,0x00,0x50,0x11,0x07,0xD0,0x8F,0xD6,0x0E, +0x00,0x00,0x50,0xF5,0x50,0xFD,0x17,0x9F,0x6C,0x01,0x06,0x20,0x17,0x9F,0x72,0x01, +0x04,0x20,0xF0,0x02,0x18,0x03,0x9F,0x00,0x04,0x14,0x20,0xD0,0x8F,0xB2,0x04,0x14, +0x20,0x51,0x7D,0x52,0xA1,0x08,0x7D,0x54,0xA1,0x10,0x7D,0x56,0xA1,0x18,0x7D,0x58, +0xA1,0x20,0x7D,0x5A,0xA1,0x28,0x7D,0x5C,0xA1,0x30,0xDB,0x04,0xA1,0x38,0xDB,0x11, +0xA1,0x4C,0xD0,0xA1,0x44,0xA1,0x3C,0xCB,0x8F,0x00,0xFF,0x00,0x00,0xA1,0x48,0xA1, +0x40,0xED,0x08,0x06,0xA1,0x48,0x03,0x12,0x34,0xD0,0x9F,0x10,0x00,0x14,0x20,0xA1, +0x18,0xD0,0x9F,0x20,0x00,0x14,0x20,0xA1,0x1C,0xD0,0x9F,0x30,0x01,0x14,0x20,0xA1, +0x08,0xD0,0x9F,0x34,0x01,0x14,0x20,0xA1,0x0C,0xD0,0x9F,0x40,0x01,0x14,0x20,0xA1, +0x10,0xD0,0x9F,0x44,0x01,0x14,0x20,0xA1,0x14,0xDB,0x1B,0xA1,0x34,0xD0,0x8F,0x00, +0x55,0xD4,0x00,0x9F,0x10,0x00,0x14,0x20,0x8F,0x9F,0x05,0x00,0x04,0x20,0x01,0x02, +0x07,0x00,0x3B,0x00,0x77,0x00,0x00,0xD0,0x04,0x9F,0x20,0x00,0x14,0x20,0xD0,0x8F, +0x00,0x40,0x08,0x20,0x9F,0x30,0x01,0x14,0x20,0xD0,0x00,0x9F,0x34,0x01,0x14,0x20, +0xD0,0x8F,0x04,0x40,0x08,0x20,0x9F,0x40,0x01,0x14,0x20,0xD0,0x00,0x9F,0x44,0x01, +0x14,0x20,0xD0,0x8F,0x77,0x55,0xD4,0x00,0x51,0x11,0x6E,0xD0,0x04,0x9F,0x20,0x00, +0x14,0x20,0xD0,0x8F,0x00,0x00,0x10,0x20,0x9F,0x30,0x01,0x14,0x20,0xD0,0x8F,0xFC, +0xFF,0x03,0x00,0x9F,0x34,0x01,0x14,0x20,0xD0,0x8F,0x00,0x40,0x08,0x20,0x9F,0x40, +0x01,0x14,0x20,0xD0,0x8F,0xFC,0x0F,0x00,0x00,0x9F,0x44,0x01,0x14,0x20,0xD0,0x8F, +0x33,0x55,0xD4,0x00,0x51,0x11,0x32,0xD0,0x04,0x9F,0x20,0x00,0x14,0x20,0xD0,0x8F, +0x00,0x40,0x08,0x20,0x9F,0x30,0x01,0x14,0x20,0xD0,0x00,0x9F,0x34,0x01,0x14,0x20, +0xD0,0x8F,0x04,0x40,0x08,0x20,0x9F,0x40,0x01,0x14,0x20,0xD0,0x00,0x9F,0x44,0x01, +0x14,0x20,0xD0,0x8F,0x77,0x55,0xD4,0x00,0x51,0xD0,0x51,0x9F,0x10,0x00,0x14,0x20, +0x9A,0x9F,0x04,0x40,0x08,0x20,0x50,0xEF,0x04,0x03,0x50,0x50,0xF0,0x50,0x08,0x03, +0x51,0xF0,0x50,0x0C,0x03,0x51,0xD0,0x51,0x9F,0x10,0x00,0x14,0x20,0xD0,0x8F,0x78, +0x00,0x00,0x00,0x9F,0x0C,0x01,0x14,0x20,0xD0,0x8F,0x7C,0x00,0x00,0x00,0x9F,0x1C, +0x01,0x14,0x20,0x9E,0xEF,0x17,0xAB,0x00,0x00,0x50,0xDA,0x50,0x11,0xD0,0x8F,0x58, +0x07,0x14,0x20,0x5E,0x9E,0xEF,0x06,0xFD,0xFF,0xFF,0x50,0xD1,0x50,0x8F,0x00,0x00, +0x04,0x20,0x1E,0x22,0xD0,0x50,0x5E,0xC2,0xEF,0x13,0xFD,0xFF,0xFF,0x50,0xDB,0x11, +0x51,0x9A,0x8F,0x80,0x52,0xC0,0x50,0x81,0xF5,0x52,0xFA,0x9E,0xEF,0xDF,0xFC,0xFF, +0xFF,0xEF,0xFA,0xFC,0xFF,0xFF,0x31,0xD3,0x00,0x03,0x05,0xD2,0x9F,0x02,0x05,0x14, +0x20,0x9F,0x30,0x00,0x14,0x20,0xDA,0x9F,0xFE,0x04,0x14,0x20,0x11,0x7D,0x9F,0xB2, +0x04,0x14,0x20,0x50,0x7D,0x9F,0xBA,0x04,0x14,0x20,0x52,0x7D,0x9F,0xC2,0x04,0x14, +0x20,0x54,0x7D,0x9F,0xCA,0x04,0x14,0x20,0x56,0x7D,0x9F,0xD2,0x04,0x14,0x20,0x58, +0x7D,0x9F,0xDA,0x04,0x14,0x20,0x5A,0x7D,0x9F,0xE2,0x04,0x14,0x20,0x5C,0xDB,0x25, +0x7E,0xDA,0x8E,0x25,0xDA,0x00,0x39,0xE0,0x0F,0x9F,0xFA,0x04,0x14,0x20,0x08,0xD0, +0x9F,0xEA,0x04,0x14,0x20,0x5E,0x02,0xD0,0x9F,0xEA,0x04,0x14,0x20,0x5E,0x01,0x01, +0xDA,0x01,0x38,0x02,0x30,0x37,0x32,0xD4,0x51,0x05,0xD1,0x50,0x8F,0xFF,0x01,0x00, +0x00,0x14,0x0C,0xDD,0x50,0xFB,0x01,0xEF,0xB0,0x34,0x00,0x00,0x7C,0x50,0x05,0xBB, +0x0C,0x3C,0x60,0x52,0xD0,0xA0,0x04,0x53,0x11,0x06,0x9A,0x83,0x50,0x30,0x87,0x32, +0xF4,0x52,0xF7,0x7C,0x50,0xBA,0x0C,0x05,0xDD,0x51,0xDD,0x8F,0x51,0x00,0x00,0x00, +0x9F,0x9F,0x4C,0x05,0x14,0x20,0xDD,0x50,0xFB,0x04,0xEF,0x47,0x2F,0x00,0x00,0x9F, +0x9F,0x4C,0x05,0x14,0x20,0xFB,0x01,0xEF,0xCF,0x19,0x00,0x00,0x9E,0x9F,0x4C,0x05, +0x14,0x20,0x51,0x05,0x12,0x00,0x00,0x00,0x14,0x04,0x14,0x20,0x90,0x00,0x9F,0x17, +0x05,0x14,0x20,0x8F,0x9F,0x17,0x05,0x14,0x20,0x00,0x0A,0x2A,0x00,0x58,0x00,0x64, +0x00,0x7D,0x00,0x83,0x00,0x92,0x00,0xB4,0x00,0xF2,0x00,0x08,0x01,0x11,0x01,0x1A, +0x01,0x17,0x9F,0x27,0x04,0x04,0x20,0x11,0xFE,0x30,0xB7,0x01,0x90,0x50,0x9F,0x17, +0x05,0x14,0x20,0x11,0xCE,0xD4,0x9F,0x18,0x05,0x14,0x20,0xD4,0x9F,0x06,0x05,0x14, +0x20,0xD4,0x9F,0x0A,0x05,0x14,0x20,0x2C,0x00,0x9F,0x00,0x00,0x00,0x00,0x00,0x08, +0x9F,0x0E,0x05,0x14,0x20,0xD0,0x8F,0x2D,0x04,0x14,0x20,0x9F,0x10,0x04,0x14,0x20, +0x31,0xC6,0xFF,0x30,0xC1,0x30,0x30,0xAD,0x01,0x30,0x4A,0x00,0x31,0xBA,0xFF,0x30, +0xBF,0x30,0x30,0x87,0x30,0x30,0xBC,0x00,0x30,0x25,0x00,0x30,0x38,0x00,0xFB,0x00, +0xEF,0xF5,0x38,0x00,0x00,0x31,0xA1,0xFF,0x30,0x2B,0x00,0x31,0x9B,0xFF,0x30,0x6B, +0x30,0x30,0xA0,0x00,0x30,0x09,0x00,0x30,0x1C,0x00,0x31,0x8C,0xFF,0x31,0x89,0xFF, +0x30,0xC2,0x00,0xD3,0x07,0x50,0x13,0x0D,0xDD,0x8F,0x84,0x00,0x00,0x00,0xFB,0x01, +0xEF,0xA7,0x33,0x00,0x00,0x05,0xE5,0x0B,0x9F,0x00,0x04,0x14,0x20,0x00,0x05,0xDD, +0x8F,0x83,0x00,0x00,0x00,0xFB,0x01,0xEF,0x90,0x33,0x00,0x00,0x9E,0x9F,0xA5,0x05, +0x14,0x20,0x52,0x9E,0x9F,0x14,0x04,0x14,0x20,0xA2,0x04,0xDD,0xA2,0x04,0xFB,0x01, +0xEF,0xD6,0x18,0x00,0x00,0xB0,0x50,0x62,0xDD,0x9F,0x26,0x04,0x14,0x20,0xDD,0x52, +0xFB,0x02,0xEF,0xA9,0x4B,0x00,0x00,0x30,0xE7,0x5A,0x31,0x2C,0xFF,0xDD,0x8F,0x85, +0x00,0x00,0x00,0xFB,0x01,0xEF,0x52,0x33,0x00,0x00,0x30,0x81,0x15,0x30,0xD1,0x5A, +0x31,0x16,0xFF,0x30,0xE2,0x59,0x30,0xC8,0x5A,0x31,0x0D,0xFF,0x30,0x87,0x12,0x30, +0xBF,0x5A,0x31,0x04,0xFF,0x30,0x2C,0x5B,0x30,0xB6,0x5A,0xE8,0x50,0x03,0x31,0xFA, +0xFD,0x31,0xF5,0xFE,0xDD,0x8F,0x8C,0x00,0x00,0x00,0xFB,0x01,0xEF,0x1B,0x33,0x00, +0x00,0xEF,0x08,0x06,0x9F,0xFA,0x04,0x14,0x20,0x7E,0xFB,0x01,0xEF,0x0B,0x33,0x00, +0x00,0xDD,0x9F,0xF6,0x04,0x14,0x20,0x9F,0xEF,0x1B,0x5E,0x00,0x00,0xFB,0x02,0xEF, +0xF8,0x32,0x00,0x00,0x05,0xD4,0x50,0xE1,0x03,0x9F,0x00,0x04,0x14,0x20,0x03,0xC8, +0x01,0x50,0xE1,0x02,0x9F,0x00,0x04,0x14,0x20,0x03,0xC8,0x02,0x50,0xE1,0x0B,0x9F, +0x00,0x04,0x14,0x20,0x03,0xC8,0x04,0x50,0xE1,0x1C,0x9F,0x00,0x04,0x14,0x20,0x03, +0xC8,0x08,0x50,0xD5,0x9F,0x0A,0x05,0x14,0x20,0x13,0x03,0xC8,0x10,0x50,0xEF,0x00, +0x02,0x9F,0x00,0x04,0x14,0x20,0x51,0xF0,0x51,0x05,0x02,0x50,0xEF,0x08,0x06,0x9F, +0xFA,0x04,0x14,0x20,0x51,0x9A,0x41,0xEF,0xD0,0x5D,0x00,0x00,0x51,0xF0,0x51,0x07, +0x02,0x50,0xEF,0x18,0x03,0x9F,0x00,0x04,0x14,0x20,0x51,0xF0,0x51,0x09,0x03,0x50, +0xD0,0x9F,0x04,0x40,0x08,0x20,0x51,0xE1,0x07,0x51,0x07,0xC8,0x8F,0x00,0x10,0x00, +0x00,0x50,0x05,0x30,0x7F,0xFF,0xD0,0x50,0x52,0x9A,0x9F,0x17,0x05,0x14,0x20,0x53, +0x9E,0xEF,0xB6,0x5D,0x00,0x00,0x54,0x91,0x53,0x64,0x12,0x10,0xAB,0xA4,0x02,0x52, +0x50,0xAB,0xA4,0x02,0xA4,0x04,0x51,0xB1,0x50,0x51,0x13,0x05,0xC0,0x06,0x54,0x11, +0xE6,0x9A,0xA4,0x01,0x50,0x05,0x30,0x17,0x38,0x30,0xC5,0x59,0xDD,0x00,0xDD,0x00, +0xDD,0x02,0xFB,0x03,0xEF,0x09,0x3A,0x00,0x00,0xD0,0x50,0x53,0xED,0x02,0x02,0x9F, +0x04,0x40,0x08,0x20,0x00,0x12,0x25,0xED,0x08,0x02,0x9F,0x58,0x07,0x14,0x20,0x03, +0x12,0x1A,0xF0,0x02,0x0D,0x03,0x9F,0x00,0x04,0x14,0x20,0xA8,0x8F,0x00,0x06,0x9F, +0x00,0x04,0x14,0x20,0x94,0x9F,0x02,0x04,0x14,0x20,0x11,0x07,0xFB,0x00,0xEF,0x17, +0x37,0x00,0x00,0xD4,0x52,0xEF,0x04,0x04,0x9F,0x00,0x04,0x14,0x20,0x50,0x13,0x0E, +0xD1,0x50,0x0C,0x1E,0x09,0x91,0x9F,0x02,0x04,0x14,0x20,0x0F,0x1B,0x12,0xF0,0x03, +0x04,0x04,0x9F,0x00,0x04,0x14,0x20,0x94,0x9F,0x02,0x04,0x14,0x20,0xD0,0x01,0x52, +0xDD,0x8F,0x8C,0x00,0x00,0x00,0xFB,0x01,0xEF,0xBF,0x31,0x00,0x00,0x30,0x33,0x15, +0xE1,0x0A,0x9F,0x00,0x04,0x14,0x20,0x23,0xD0,0x9F,0x04,0x40,0x08,0x20,0x50,0xED, +0x00,0x02,0x50,0x01,0x13,0x0E,0xE8,0x52,0x0B,0xED,0x00,0x02,0x9F,0x58,0x07,0x14, +0x20,0x03,0x13,0x07,0xFB,0x00,0xEF,0x2D,0x16,0x00,0x00,0x9E,0xEF,0x2F,0xF9,0xFF, +0xFF,0x50,0xD1,0x50,0x8F,0x00,0x00,0x04,0x20,0x12,0x39,0xDD,0x8F,0x86,0x00,0x00, +0x00,0xFB,0x01,0xEF,0x74,0x31,0x00,0x00,0xDD,0x00,0xDD,0x00,0xDD,0x03,0xFB,0x03, +0xEF,0x3D,0x39,0x00,0x00,0xC9,0x53,0x50,0x51,0x13,0x0C,0xD0,0x8F,0x89,0x00,0x00, +0x00,0x50,0x30,0xDC,0x58,0x11,0x0D,0xDD,0x8F,0x88,0x00,0x00,0x00,0xFB,0x01,0xEF, +0x48,0x31,0x00,0x00,0xDF,0x8F,0x00,0x02,0x00,0x00,0xDD,0x8F,0x80,0x08,0x00,0x00, +0xDD,0x0E,0xFB,0x03,0xEF,0xCF,0x20,0x00,0x00,0xD5,0x50,0x13,0x03,0x30,0xB1,0x58, +0x05,0x00,0x00,0x00,0xB5,0x8F,0xFC,0x03,0xD0,0x9F,0x7C,0x07,0x14,0x20,0x50,0x12, +0x04,0x9A,0x20,0x50,0x04,0xC3,0x8F,0x8C,0x06,0x00,0x00,0x50,0x59,0xD0,0x8F,0x00, +0x19,0x00,0x20,0xA9,0x7C,0x94,0xC9,0x80,0x00,0x90,0x08,0xC9,0x81,0x00,0x90,0x0E, +0xC9,0x82,0x00,0x90,0x01,0xC9,0x83,0x00,0x30,0xF6,0x0E,0xE9,0x50,0x5C,0x30,0x61, +0x00,0xD4,0x56,0x30,0x90,0x00,0xE9,0x50,0x4B,0xB0,0x0B,0xA7,0x04,0x78,0x08,0x56, +0x50,0xA9,0x04,0x50,0x67,0x30,0x7E,0x00,0xE9,0x50,0x39,0xB3,0x8F,0x00,0x04,0x67, +0x12,0x2E,0xD0,0x01,0x50,0xB3,0x8F,0x80,0x00,0xA7,0x06,0x13,0x02,0xD6,0x50,0xD0, +0x50,0x7E,0xD0,0x56,0x7E,0x9A,0xC9,0x80,0x00,0x50,0xC1,0x8F,0x41,0x00,0x00,0x00, +0x50,0x7E,0x9E,0xEF,0xF7,0x5C,0x00,0x00,0x7E,0xFB,0x04,0xEF,0x9C,0x30,0x00,0x00, +0xF2,0x04,0x56,0xAF,0x96,0xC9,0x80,0x00,0x11,0x9E,0x9A,0x00,0x50,0xD0,0xAC,0x04, +0x51,0x04,0xE0,0x01,0xAC,0x04,0x2E,0xCB,0x8F,0x00,0xE0,0xFF,0xFF,0x57,0x50,0xC9, +0x8F,0x00,0xE0,0x03,0x00,0x50,0x7E,0x9A,0xC9,0x80,0x00,0x7E,0x9E,0xEF,0x9E,0x5C, +0x00,0x00,0x50,0xE2,0x00,0xAC,0x04,0x03,0xC0,0x02,0x50,0xD0,0x50,0x7E,0xFB,0x03, +0xEF,0x57,0x30,0x00,0x00,0x05,0xD0,0x8F,0x80,0x69,0x67,0xFF,0x9F,0x18,0x01,0x14, +0x20,0xD0,0x15,0x9F,0x10,0x01,0x14,0x20,0x30,0x60,0x2E,0x9A,0x01,0x50,0x95,0x67, +0x19,0x09,0xE8,0x9F,0x10,0x01,0x14,0x20,0xEF,0xD4,0x50,0x05,0xB5,0x8F,0xFC,0x03, +0xD0,0x9F,0x7C,0x07,0x14,0x20,0x50,0x12,0x04,0x9A,0x20,0x50,0x04,0xC3,0x8F,0x8C, +0x06,0x00,0x00,0x50,0x59,0xBB,0x8F,0x00,0x22,0xC2,0x0A,0x5E,0xB0,0x8F,0x00,0x22, +0xAE,0x08,0x9E,0xEF,0x12,0x00,0x00,0x00,0x6E,0xD0,0x9F,0xAE,0x04,0x14,0x20,0xAE, +0x04,0xD0,0x5E,0x9F,0xAE,0x04,0x14,0x20,0x11,0x07,0x9A,0x8F,0x8D,0x50,0x31,0xE2, +0x00,0xC3,0x04,0x5E,0xA9,0x08,0x9E,0xEF,0xCA,0x04,0x00,0x00,0xA9,0x10,0xEF,0x02, +0x02,0x9F,0x04,0x40,0x08,0x20,0x52,0xB0,0x42,0x9F,0x40,0x1F,0x00,0x20,0xC9,0x84, +0x00,0xA8,0x20,0x42,0x9F,0x40,0x1F,0x00,0x20,0xD0,0x8F,0x68,0x14,0x00,0x20,0xA9, +0x7C,0x94,0xC9,0x80,0x00,0x90,0x04,0xC9,0x81,0x00,0x90,0x1A,0xC9,0x82,0x00,0x90, +0x01,0xC9,0x83,0x00,0x94,0xC9,0x1F,0x04,0x30,0xA6,0x0D,0xE9,0x50,0x34,0x30,0xBB, +0x00,0x9A,0x01,0xA9,0x04,0x30,0x82,0x03,0xE8,0x50,0x19,0xF4,0xA9,0x04,0xF6,0xE0, +0x01,0xAC,0x04,0x13,0x9E,0xEF,0x61,0x5C,0x00,0x00,0x7E,0xFB,0x01,0xEF,0x7A,0x2F, +0x00,0x00,0x11,0x03,0x30,0x1F,0x05,0xB4,0x67,0xD4,0xA9,0x78,0x96,0xC9,0x80,0x00, +0x11,0xC6,0xD0,0x8F,0x40,0x19,0x00,0x20,0xA9,0x7C,0x94,0xC9,0x80,0x00,0x90,0x04, +0xC9,0x81,0x00,0x90,0x1E,0xC9,0x82,0x00,0x90,0x01,0xC9,0x83,0x00,0x90,0x01,0xC9, +0x1F,0x04,0x30,0x4C,0x0D,0xE9,0x50,0x34,0x30,0x61,0x00,0x9A,0x01,0xA9,0x04,0x30, +0x28,0x03,0xE8,0x50,0x19,0xF4,0xA9,0x04,0xF6,0xE0,0x01,0xAC,0x04,0x13,0x9E,0xEF, +0x07,0x5C,0x00,0x00,0x7E,0xFB,0x01,0xEF,0x20,0x2F,0x00,0x00,0x11,0x03,0x30,0xC5, +0x04,0xB4,0x67,0xD4,0xA9,0x78,0x96,0xC9,0x80,0x00,0x11,0xC6,0x9A,0x00,0x50,0xD0, +0xAC,0x04,0x51,0xD0,0xAE,0x04,0x9F,0xAE,0x04,0x14,0x20,0xC0,0x08,0x5E,0xBA,0x8E, +0xEF,0x02,0x02,0x9F,0x04,0x40,0x08,0x20,0x52,0xB0,0xC9,0x84,0x00,0x42,0x9F,0x40, +0x1F,0x00,0x20,0xD0,0xA9,0x78,0x57,0x13,0x02,0xB4,0x67,0x04,0xE0,0x01,0xAC,0x04, +0x5C,0xCB,0x8F,0x00,0xE0,0xFF,0xFF,0x57,0x50,0xC9,0x8F,0x00,0xE0,0x03,0x00,0x50, +0x7E,0x95,0xC9,0x1F,0x04,0x19,0x2D,0x9A,0xC9,0x80,0x00,0x7E,0x9E,0xEF,0x3B,0x5B, +0x00,0x00,0x50,0x95,0xC9,0x1F,0x04,0x13,0x07,0x9E,0xEF,0x52,0x5B,0x00,0x00,0x50, +0xE2,0x00,0xAC,0x04,0x03,0xC0,0x02,0x50,0xD0,0x50,0x7E,0xFB,0x03,0xEF,0x9A,0x2E, +0x00,0x00,0x11,0x19,0x9E,0xEF,0xF7,0x5A,0x00,0x00,0x50,0xE2,0x00,0xAC,0x04,0x03, +0xC0,0x02,0x50,0xD0,0x50,0x7E,0xFB,0x02,0xEF,0x7F,0x2E,0x00,0x00,0x05,0xB5,0x8F, +0xFC,0x03,0xD0,0x9F,0x7C,0x07,0x14,0x20,0x50,0x12,0x04,0x9A,0x20,0x50,0x04,0xC3, +0x8F,0x8C,0x06,0x00,0x00,0x50,0x59,0xBB,0x8F,0x00,0x22,0xC2,0x0A,0x5E,0xB0,0x8F, +0x00,0x22,0xAE,0x08,0x9E,0xEF,0x12,0x00,0x00,0x00,0x6E,0xD0,0x9F,0xAE,0x04,0x14, +0x20,0xAE,0x04,0xD0,0x5E,0x9F,0xAE,0x04,0x14,0x20,0x11,0x07,0x9A,0x8F,0x8D,0x50, +0x31,0xE4,0x00,0xC3,0x04,0x5E,0xA9,0x08,0x9E,0xEF,0xC9,0x06,0x00,0x00,0xA9,0x10, +0x9E,0xEF,0xE7,0x06,0x00,0x00,0xA9,0x14,0xEF,0x02,0x02,0x9F,0x04,0x40,0x08,0x20, +0x52,0xB0,0x42,0x9F,0x40,0x1F,0x00,0x20,0xC9,0x84,0x00,0xA8,0x20,0x42,0x9F,0x40, +0x1F,0x00,0x20,0xD0,0xAC,0x04,0xA9,0x7C,0x94,0xC9,0x80,0x00,0x94,0xC9,0x82,0x00, +0x90,0x8F,0xFF,0xC9,0x1F,0x04,0xD1,0xAC,0x0C,0x01,0x19,0x2E,0x14,0x14,0xD0,0x8F, +0x68,0x14,0x00,0x20,0xA9,0x7C,0x90,0x1A,0xC9,0x82,0x00,0x90,0x00,0xC9,0x1F,0x04, +0x11,0x12,0xD0,0x8F,0x40,0x19,0x00,0x20,0xA9,0x7C,0x90,0x1E,0xC9,0x82,0x00,0x90, +0x01,0xC9,0x1F,0x04,0x90,0xAC,0x04,0xC9,0x80,0x00,0x90,0x04,0xC9,0x81,0x00,0x90, +0x01,0xC9,0x83,0x00,0xD0,0xAC,0x08,0x50,0x12,0x07,0x9E,0xEF,0x8C,0x5F,0x00,0x00, +0x50,0x28,0x06,0x60,0xA9,0x21,0x9E,0xEF,0xA0,0x59,0x00,0x00,0x7E,0xFB,0x01,0xEF, +0x98,0x2D,0x00,0x00,0x30,0x9A,0x0B,0xE9,0x50,0x2C,0xD0,0x01,0xAC,0x04,0x30,0xAB, +0xFE,0x90,0x02,0xC9,0x1F,0x04,0x9A,0x01,0xA9,0x04,0x30,0x6D,0x01,0xE8,0x50,0x06, +0xF4,0xA9,0x04,0xF6,0x11,0x03,0x30,0xE0,0x03,0xB4,0x67,0xD4,0xA9,0x78,0xE5,0x03, +0x9F,0xA5,0x04,0x14,0x20,0x00,0x9E,0xEF,0x79,0x59,0x00,0x00,0x7E,0xFB,0x01,0xEF, +0x58,0x2D,0x00,0x00,0x9A,0x00,0x50,0xD0,0xAE,0x04,0x9F,0xAE,0x04,0x14,0x20,0xC0, +0x08,0x5E,0xBA,0x8E,0xEF,0x02,0x02,0x9F,0x04,0x40,0x08,0x20,0x52,0xB0,0xC9,0x84, +0x00,0x42,0x9F,0x40,0x1F,0x00,0x20,0xD0,0xA9,0x78,0x57,0x13,0x0A,0xB4,0x67,0xE5, +0x03,0x9F,0xA5,0x04,0x14,0x20,0x00,0x04,0xB5,0x8F,0xFC,0x03,0xD0,0x9F,0x7C,0x07, +0x14,0x20,0x50,0x12,0x04,0x9A,0x20,0x50,0x04,0xC3,0x8F,0x8C,0x06,0x00,0x00,0x50, +0x59,0xBB,0x8F,0x00,0x22,0xC2,0x0A,0x5E,0xB0,0x8F,0x00,0x22,0xAE,0x08,0x9E,0xEF, +0x12,0x00,0x00,0x00,0x6E,0xD0,0x9F,0xAE,0x04,0x14,0x20,0xAE,0x04,0xD0,0x5E,0x9F, +0xAE,0x04,0x14,0x20,0x11,0x07,0x9A,0x8F,0x8D,0x50,0x31,0xA4,0x00,0xC3,0x04,0x5E, +0xA9,0x08,0x9E,0xEF,0xBE,0x01,0x00,0x00,0xA9,0x10,0xEF,0x02,0x02,0x9F,0x04,0x40, +0x08,0x20,0x52,0xB0,0x42,0x9F,0x40,0x1F,0x00,0x20,0xC9,0x84,0x00,0xA8,0x20,0x42, +0x9F,0x40,0x1F,0x00,0x20,0xD0,0xAC,0x04,0xA9,0x7C,0x94,0xC9,0x80,0x00,0x94,0xC9, +0x82,0x00,0x90,0x8F,0xFF,0xC9,0x1F,0x04,0xD1,0xAC,0x08,0x01,0x19,0x0E,0xD0,0x8F, +0x10,0x19,0x00,0x20,0xA9,0x7C,0x90,0xAC,0x04,0xC9,0x80,0x00,0x90,0x04,0xC9,0x81, +0x00,0x90,0x04,0xC9,0x83,0x00,0x30,0x88,0x0A,0xE9,0x50,0x42,0xD4,0xAC,0x04,0x30, +0x9A,0xFD,0x30,0x65,0x00,0x88,0x8F,0xF0,0xC9,0x1E,0x04,0x30,0x5A,0x05,0xE9,0x50, +0x28,0x30,0xBC,0x06,0xE9,0x50,0x22,0x9E,0xEF,0x73,0x59,0x00,0x00,0x7E,0xFB,0x01, +0xEF,0x57,0x2C,0x00,0x00,0x30,0xF7,0x05,0xE8,0x50,0x0E,0x9E,0xEF,0x7A,0x59,0x00, +0x00,0x7E,0xFB,0x01,0xEF,0x43,0x2C,0x00,0x00,0xB4,0x67,0xD4,0xA9,0x78,0x9A,0x00, +0x50,0xD0,0xAE,0x04,0x9F,0xAE,0x04,0x14,0x20,0xC0,0x08,0x5E,0xBA,0x8E,0xEF,0x02, +0x02,0x9F,0x04,0x40,0x08,0x20,0x52,0xB0,0xC9,0x84,0x00,0x42,0x9F,0x40,0x1F,0x00, +0x20,0xD0,0xA9,0x78,0x57,0x13,0x02,0xB4,0x67,0x04,0xD0,0x8F,0x00,0x79,0x6C,0xFC, +0xA9,0x0C,0xB4,0x67,0xD0,0x57,0xA9,0x78,0x3E,0xC9,0x88,0x00,0x52,0xB0,0x8F,0x00, +0x84,0x62,0xCB,0x8F,0x00,0xFE,0xFF,0xFF,0x52,0x51,0xC0,0x10,0x51,0xD0,0x51,0xA2, +0x02,0xB0,0x01,0xA2,0x06,0xEF,0x09,0x15,0x52,0x50,0xC8,0x8F,0x00,0x00,0x00,0x80, +0x50,0xD0,0x8F,0x00,0x80,0x08,0x20,0x53,0xD0,0x50,0x83,0xD6,0x50,0xD0,0x50,0x83, +0xD6,0x50,0xD0,0x50,0x63,0xD0,0x0B,0x51,0xD0,0x8F,0x40,0x27,0x58,0xFF,0x9F,0x18, +0x01,0x14,0x20,0xD0,0x15,0x9F,0x10,0x01,0x14,0x20,0x30,0xCE,0x29,0xB0,0xA7,0x02, +0x53,0x19,0x36,0xE0,0x51,0x53,0x09,0xE8,0x9F,0x10,0x01,0x14,0x20,0xEC,0x11,0x29, +0xB0,0x82,0xA7,0x02,0xF3,0x0E,0x51,0xD0,0xD0,0x8F,0xC0,0xBD,0xF0,0xFF,0x9F,0x18, +0x01,0x14,0x20,0xD0,0x15,0x9F,0x10,0x01,0x14,0x20,0x30,0x9E,0x29,0xB5,0xA7,0x02, +0x13,0x0A,0xE8,0x9F,0x10,0x01,0x14,0x20,0xF1,0x31,0x5A,0x00,0x9A,0x04,0x69,0xD0, +0x10,0x50,0xC1,0x8F,0x48,0x00,0x00,0x00,0xC9,0x8A,0x00,0x51,0x9E,0xC9,0x98,0x00, +0x52,0x9E,0xC9,0xDC,0x00,0x53,0xC9,0x8F,0x00,0x00,0x00,0x80,0x51,0x82,0xB0,0x30, +0x63,0xC0,0x34,0x51,0xC0,0x34,0x53,0xF5,0x50,0xEC,0xD0,0x8F,0xFF,0xFF,0xFF,0xFF, +0xC9,0x50,0x04,0xB0,0x30,0xC9,0x1C,0x04,0xC1,0x8F,0x88,0x03,0x00,0x00,0xC9,0x8A, +0x00,0xC9,0xD8,0x00,0x90,0x01,0xC9,0x1E,0x04,0x2C,0x00,0x8F,0x00,0x00,0x30,0xC9, +0x20,0x04,0x9A,0x01,0x50,0x05,0x3C,0x8F,0x54,0x00,0x50,0xD0,0xA9,0x08,0x5E,0xD4, +0x69,0x05,0xB4,0xC9,0x94,0x00,0xC8,0x8F,0x00,0x00,0x00,0x80,0xC9,0xD8,0x00,0xB5, +0x67,0xD0,0xA9,0x0C,0x9F,0x18,0x01,0x14,0x20,0xD0,0x15,0x9F,0x10,0x01,0x14,0x20, +0x30,0x08,0x29,0xB3,0x8F,0x00,0xF8,0xA7,0x02,0x12,0x1B,0xE9,0x9F,0x10,0x01,0x14, +0x20,0x14,0xD5,0xC9,0xD8,0x00,0x19,0xE8,0xBB,0x3C,0x2C,0x00,0x8F,0x00,0x00,0x30, +0xC9,0x20,0x04,0xBA,0x3C,0x05,0x31,0xAD,0xFF,0xB4,0xC9,0x96,0x00,0xD0,0xC9,0x50, +0x04,0x50,0x19,0x0A,0xC8,0x8F,0x00,0x00,0x00,0x80,0x40,0xC9,0x98,0x00,0xD6,0x50, +0xCA,0x8F,0xF0,0xFF,0xFF,0xFF,0x50,0xD0,0x50,0xC9,0x50,0x04,0xDE,0x40,0xC9,0x98, +0x00,0x52,0xC4,0x34,0x50,0x9E,0x40,0xC9,0xE0,0x00,0x58,0xD0,0xA9,0x0C,0x9F,0x18, +0x01,0x14,0x20,0xD0,0x15,0x9F,0x10,0x01,0x14,0x20,0x30,0x9E,0x28,0xB3,0x8F,0x00, +0xF8,0xA7,0x02,0x12,0x1E,0xE5,0x02,0x9F,0xA5,0x04,0x14,0x20,0x05,0x16,0xB9,0x14, +0x11,0xD9,0xE9,0x9F,0x10,0x01,0x14,0x20,0x05,0xD5,0x62,0x19,0xDD,0x05,0x16,0xB9, +0x10,0x11,0xC8,0x31,0x40,0xFF,0x9E,0xC9,0x20,0x04,0x56,0xB4,0xC9,0x86,0x00,0xD0, +0x01,0x66,0xB0,0xC9,0x86,0x00,0xA6,0x04,0x90,0x03,0xA6,0x08,0xB0,0x01,0xA6,0x0A, +0x30,0x2F,0xFF,0x30,0x73,0xFF,0xE0,0x06,0xA8,0x08,0xF8,0x91,0xA8,0x08,0x8F,0x83, +0x13,0x03,0x31,0x11,0xFF,0xB5,0xA8,0x04,0x12,0x06,0xB5,0xC9,0x86,0x00,0x12,0x25, +0xAB,0x8F,0xE0,0xFF,0xA8,0x0A,0x51,0x13,0x10,0xB1,0x04,0x51,0x13,0x0B,0xB1,0x03, +0x51,0x12,0x09,0xB1,0x51,0xA8,0x0A,0x13,0x03,0x30,0x0A,0x00,0xA1,0x01,0xA8,0x04, +0xC9,0x86,0x00,0x11,0xAA,0x05,0xEF,0x00,0x07,0xA8,0x1C,0x7E,0xEF,0x07,0x05,0xA8, +0x1C,0x50,0x13,0x04,0x80,0x8F,0x40,0x50,0x9A,0x50,0x7E,0xEF,0x0C,0x05,0xA8,0x1C, +0x50,0x13,0x04,0x80,0x8F,0x40,0x50,0x9A,0x50,0x7E,0xEF,0x11,0x05,0xA8,0x1C,0x50, +0xC1,0x8F,0x40,0x00,0x00,0x00,0x50,0x7E,0x3C,0xA8,0x04,0x7E,0x9A,0xC9,0x80,0x00, +0x50,0xC1,0x8F,0x41,0x00,0x00,0x00,0x50,0x7E,0x90,0x8F,0x44,0x50,0x95,0xC9,0x1F, +0x04,0x13,0x04,0x90,0x8F,0x4D,0x50,0x9A,0x50,0x7E,0x9E,0xEF,0x65,0x56,0x00,0x00, +0x7E,0xFB,0x08,0xEF,0x94,0x29,0x00,0x00,0x05,0xE5,0x02,0x9F,0xA5,0x04,0x14,0x20, +0x00,0xE2,0x03,0x9F,0xA5,0x04,0x14,0x20,0x00,0x9E,0xC9,0x20,0x04,0x56,0xD0,0x8F, +0x80,0x69,0x67,0xFF,0xA9,0x0C,0x94,0xA9,0x20,0xD0,0x01,0xA9,0x1C,0xD0,0x01,0x66, +0x90,0x01,0xA6,0x08,0x30,0x5B,0xFE,0x30,0x9F,0xFE,0x91,0x69,0x05,0x12,0x36,0x91, +0xA8,0x08,0x8F,0x81,0x12,0x2F,0xD1,0x02,0x68,0x12,0x2A,0xB3,0x1F,0xA8,0x0A,0x12, +0x21,0xD1,0xA8,0x10,0xA9,0x18,0x1B,0x1A,0xD0,0xA8,0x10,0xA9,0x18,0x9A,0x04,0x69, +0xE2,0x02,0x9F,0xA5,0x04,0x14,0x20,0x00,0xD6,0xA9,0x1C,0x14,0x02,0x17,0x9E,0x31, +0xC5,0xFF,0x31,0x11,0xFE,0x93,0x8F,0x80,0xA8,0x08,0x13,0x09,0xB3,0x1F,0xA8,0x0A, +0x13,0x03,0x31,0x01,0xFE,0x91,0xA8,0x08,0x8F,0x81,0x12,0x4E,0x3C,0xA8,0x14,0x50, +0xC4,0x8F,0xC0,0xBD,0xF0,0xFF,0x50,0xD0,0x50,0xA9,0x0C,0x95,0xA9,0x20,0x13,0x23, +0x94,0xA9,0x20,0xD5,0xAC,0x08,0x12,0x12,0x9E,0xEF,0xA1,0x5A,0x00,0x00,0x50,0xD0, +0x07,0x51,0x30,0x4E,0x01,0xD0,0x50,0x55,0x12,0x01,0x05,0x2C,0x55,0xA9,0x27,0x20, +0x06,0xA9,0x21,0xD0,0x01,0x66,0x90,0x03,0xA6,0x08,0xB0,0x01,0xA6,0x0A,0x28,0x06, +0xA9,0x21,0xA6,0x0C,0x30,0xBB,0xFD,0x31,0x5D,0xFF,0x91,0xA8,0x08,0x8F,0x83,0x12, +0x15,0x9A,0xA8,0x0E,0x50,0xC4,0x8F,0xC0,0xBD,0xF0,0xFF,0x50,0xD0,0x50,0xA9,0x0C, +0xD4,0xA9,0x18,0x31,0x41,0xFF,0x91,0xA8,0x08,0x07,0x13,0x14,0x91,0xA8,0x08,0x8F, +0x87,0x12,0x0A,0xD6,0xA9,0x1C,0x14,0x02,0x17,0x9E,0x31,0x2A,0xFF,0x31,0x76,0xFD, +0xB0,0x8F,0x01,0x0C,0x50,0x3C,0xA8,0x0C,0x51,0xD1,0x51,0x16,0x1A,0x31,0x9E,0xA8, +0x0E,0x50,0x95,0xA9,0x20,0x12,0x19,0xEF,0x0C,0x04,0x60,0x53,0x13,0x05,0x91,0x53, +0x06,0x1F,0x03,0x31,0x50,0xFD,0x90,0x53,0xA9,0x20,0xC0,0x02,0x50,0xC2,0x02,0x51, +0x94,0x41,0x60,0xD0,0x50,0x7E,0xFB,0x01,0xEF,0x4F,0x28,0x00,0x00,0xB4,0x50,0xD0, +0x68,0x66,0x90,0x8F,0x87,0xA6,0x08,0xB0,0x50,0xA6,0x0A,0x30,0x34,0xFD,0xE0,0x03, +0xA8,0x0A,0x16,0x91,0xA9,0x20,0x03,0x1F,0x2B,0x1A,0x11,0x94,0xA9,0x20,0x9A,0x8F, +0x8C,0x7E,0xFB,0x01,0xEF,0x23,0x28,0x00,0x00,0x31,0xBB,0xFE,0x9A,0x8F,0x8C,0x7E, +0xFB,0x01,0xEF,0x15,0x28,0x00,0x00,0xD0,0x01,0x66,0x90,0x01,0xA6,0x08,0x30,0x01, +0xFD,0x31,0xA3,0xFE,0x9E,0xEF,0xCA,0x59,0x00,0x00,0x50,0xD0,0x8F,0x51,0x00,0x00, +0x00,0x51,0x30,0x5E,0x00,0xE5,0x02,0x9F,0xA5,0x04,0x14,0x20,0x07,0x90,0x07,0x40, +0xA9,0x27,0xD6,0x50,0xD0,0x50,0x55,0xD4,0x54,0xD0,0x01,0x66,0x90,0x07,0xA6,0x08, +0xD0,0x55,0x50,0xD1,0x50,0x16,0x1B,0x07,0xD0,0x16,0x50,0xB0,0x08,0xA6,0x0A,0xB0, +0x50,0xA6,0x0C,0xBB,0x31,0x28,0x50,0x44,0xA9,0x27,0xA6,0x0E,0xBA,0x31,0xC0,0x50, +0x54,0xC2,0x50,0x55,0xD7,0xA9,0x1C,0x18,0x0D,0xBB,0x30,0x9F,0xEF,0x03,0x00,0x00, +0x00,0x31,0x43,0xFE,0xBA,0x30,0x30,0x99,0xFC,0xD5,0x55,0x12,0xBC,0x94,0xA9,0x20, +0x31,0x34,0xFE,0xDD,0x52,0x9E,0xA9,0x27,0x52,0xDD,0x00,0xDD,0x51,0xDD,0x52,0xDD, +0x50,0xFB,0x04,0xEF,0x4E,0x22,0x00,0x00,0xD0,0x52,0x50,0x9A,0x80,0x51,0x13,0x0E, +0xE1,0x51,0xEF,0x71,0x73,0x00,0x00,0xF3,0x82,0x20,0xA0,0xFF,0x11,0xED,0xC2,0x52, +0x50,0xD7,0x50,0xD0,0x8E,0x52,0x05,0x9A,0x05,0x69,0xD0,0x02,0x66,0x90,0x01,0xA6, +0x08,0xD7,0xA9,0x1C,0x18,0x14,0xD0,0x8E,0x50,0xD0,0x8E,0x51,0x9F,0xEF,0x04,0x00, +0x00,0x00,0xDD,0x51,0x17,0x60,0x9F,0xCF,0xDD,0xFD,0x31,0x35,0xFC,0xD0,0x01,0x66, +0x90,0x07,0xA6,0x08,0xB0,0x01,0xA6,0x0C,0x90,0x07,0xA6,0x0E,0xD7,0xA9,0x1C,0x18, +0x14,0xD0,0x8E,0x50,0xD0,0x8E,0x51,0x9F,0xEF,0x04,0x00,0x00,0x00,0xDD,0x51,0x17, +0x60,0x9F,0xCF,0xB2,0xFD,0x31,0x0A,0xFC,0x9E,0xC9,0x20,0x04,0x56,0xD0,0x01,0x66, +0x9E,0xC9,0x8A,0x04,0x52,0xCB,0x8F,0x00,0xFE,0xFF,0xFF,0x52,0x51,0xC1,0x8F,0x00, +0x06,0x00,0x00,0x51,0xC9,0x54,0x04,0xEF,0x09,0x15,0x52,0x50,0xC8,0x8F,0x00,0x00, +0x00,0x80,0x50,0xD0,0x8F,0x0C,0x80,0x08,0x20,0x53,0xD0,0x50,0x83,0xD6,0x50,0xD0, +0x50,0x63,0x90,0x18,0xA6,0x08,0xD0,0x02,0xA6,0x0C,0xD0,0xC9,0x54,0x04,0xA6,0x10, +0xB0,0x8F,0x47,0x00,0xA6,0x1C,0x90,0x8F,0xFF,0xA6,0x1F,0xD4,0xA6,0x20,0x30,0xB1, +0xFB,0x30,0xF5,0xFB,0xB3,0x1F,0xA8,0x0A,0x13,0x03,0xD4,0x50,0x05,0x91,0xC9,0x8A, +0x04,0x07,0x1E,0x13,0x9A,0xC9,0x8A,0x04,0x7E,0x9E,0xEF,0x81,0x53,0x00,0x00,0x7E, +0xFB,0x02,0xEF,0x95,0x26,0x00,0x00,0x90,0x18,0xA6,0x08,0xD0,0x22,0xA6,0x0C,0xD0, +0xC9,0x54,0x04,0xA6,0x10,0xB0,0x8F,0x42,0x00,0xA6,0x1C,0x90,0x8F,0xFF,0xA6,0x1F, +0xD4,0xA6,0x20,0x30,0x6C,0xFB,0x30,0xB0,0xFB,0xB3,0x1F,0xA8,0x0A,0x12,0x04,0x9A, +0x01,0x50,0x05,0x2C,0x00,0x8F,0x00,0x00,0x22,0xC9,0x8A,0x04,0x31,0x88,0x00,0x9E, +0xC9,0x20,0x04,0x56,0xD0,0x01,0x66,0x9E,0xC9,0x8A,0x04,0x52,0xCB,0x8F,0x00,0xFE, +0xFF,0xFF,0x52,0x51,0xC1,0x8F,0x00,0x06,0x00,0x00,0x51,0xC9,0x54,0x04,0xEF,0x09, +0x15,0x52,0x50,0xC8,0x8F,0x00,0x00,0x00,0x80,0x50,0xD0,0x8F,0x0C,0x80,0x08,0x20, +0x53,0xD0,0x50,0x83,0xD6,0x50,0xD0,0x50,0x63,0x9E,0xC9,0x8A,0x04,0x52,0xB6,0x62, +0x12,0x07,0x30,0x42,0x00,0xE8,0x50,0xF1,0x05,0xD0,0x10,0x53,0xB0,0x8F,0x46,0x4B, +0x50,0xA2,0x82,0x50,0xF5,0x53,0xFA,0xB0,0x50,0x62,0x90,0x19,0xA6,0x08,0xD0,0x22, +0xA6,0x0C,0xD0,0xC9,0x54,0x04,0xA6,0x10,0xB0,0x8F,0x42,0x00,0xA6,0x1C,0x90,0x8F, +0xFF,0xA6,0x1F,0xD4,0xA6,0x20,0x30,0xD9,0xFA,0x30,0x1D,0xFB,0xD4,0x50,0xB3,0x1F, +0xA8,0x0A,0x12,0x02,0xD6,0x50,0x05,0x90,0x19,0xA6,0x08,0xD4,0xA6,0x0C,0xD4,0xA6, +0x10,0xB0,0x8F,0x46,0x00,0xA6,0x1C,0x90,0x8F,0xFF,0xA6,0x1F,0xD4,0xA6,0x20,0x30, +0xB0,0xFA,0x30,0xF4,0xFA,0xD4,0x50,0xB3,0x1F,0xA8,0x0A,0x12,0x02,0xD6,0x50,0x05, +0xD0,0x01,0x56,0x9E,0xC9,0x8E,0x04,0x50,0xAA,0x8F,0x03,0xE0,0x60,0x13,0x07,0xC0, +0x04,0x50,0xF2,0x08,0x56,0xF2,0x9E,0xEF,0xD5,0x52,0x00,0x00,0x7E,0xFB,0x01,0xEF, +0x88,0x25,0x00,0x00,0x30,0xD0,0x02,0x9E,0xEF,0x4F,0x57,0x00,0x00,0x50,0xD0,0x8F, +0x51,0x00,0x00,0x00,0x51,0x30,0xDB,0xFD,0x7F,0xC9,0x82,0x04,0x9F,0xC9,0x61,0x04, +0xDD,0x01,0x7F,0xC9,0x62,0x04,0x9F,0xC9,0x60,0x04,0xDD,0x04,0x9F,0xA9,0x27,0xFB, +0x07,0xEF,0xEC,0x4E,0x00,0x00,0xD1,0x50,0x35,0x12,0x0A,0x9E,0xEF,0xC1,0x52,0x00, +0x00,0x7E,0x31,0x85,0x00,0xD1,0x50,0x34,0x12,0x0A,0x9E,0xEF,0xC4,0x52,0x00,0x00, +0x7E,0x31,0x76,0x00,0x95,0xC9,0x60,0x04,0x13,0xAD,0xDF,0xC9,0x58,0x04,0x7F,0xC9, +0x62,0x04,0x9F,0xEF,0x0A,0x57,0x00,0x00,0xFB,0x03,0xEF,0x33,0x1F,0x00,0x00,0xD1, +0x50,0x01,0x13,0x14,0x19,0x09,0x9E,0xEF,0xAE,0x52,0x00,0x00,0x7E,0x11,0x4B,0x9E, +0xEF,0xBC,0x52,0x00,0x00,0x7E,0x11,0x42,0x9E,0xEF,0x46,0x00,0x00,0x00,0x50,0xD1, +0x50,0xC9,0x58,0x04,0x13,0x2D,0x9E,0xEF,0xA8,0x01,0x00,0x00,0x50,0xD1,0x50,0xC9, +0x58,0x04,0x13,0x1F,0x91,0x01,0xC9,0x60,0x04,0x13,0x09,0x9E,0xEF,0x51,0x52,0x00, +0x00,0x7E,0x11,0x16,0x95,0xC9,0x61,0x04,0x13,0x09,0x9E,0xEF,0x54,0x52,0x00,0x00, +0x7E,0x11,0x07,0x16,0xD9,0x58,0x04,0x31,0x3D,0xFF,0xFB,0x01,0xEF,0xBB,0x24,0x00, +0x00,0x31,0x33,0xFF,0x91,0xC9,0x60,0x04,0x02,0x19,0x14,0xDD,0x07,0xDD,0x00,0xDD, +0x0A,0x7F,0xC9,0x6A,0x04,0xFB,0x04,0xEF,0x69,0x02,0x00,0x00,0xE8,0x50,0x0A,0x9E, +0xEF,0x7D,0x52,0x00,0x00,0x7E,0x31,0x43,0x01,0x90,0x51,0xC9,0x5E,0x04,0x95,0xC9, +0x61,0x04,0x12,0x03,0x31,0x63,0x00,0x9E,0xEF,0x5A,0x56,0x00,0x00,0x7E,0x9A,0xEF, +0x52,0x56,0x00,0x00,0x7E,0x7E,0xC9,0x82,0x04,0x50,0xD0,0x5E,0x51,0x30,0xE6,0x1E, +0xC0,0x08,0x5E,0xD5,0x50,0x12,0x0A,0x9E,0xEF,0x29,0x52,0x00,0x00,0x7E,0x31,0x0B, +0x01,0x91,0xC9,0x60,0x04,0x02,0x13,0x0A,0x9E,0xEF,0xC4,0x51,0x00,0x00,0x7E,0x31, +0xFA,0x00,0xD4,0x53,0x11,0x18,0xDE,0x43,0xC9,0x8A,0x04,0x50,0x91,0xA0,0x02,0xC9, +0x5E,0x04,0x12,0x0A,0x9E,0xEF,0x56,0x52,0x00,0x00,0x7E,0x31,0xDE,0x00,0xF2,0x56, +0x53,0xE4,0x90,0xC9,0x5E,0x04,0xC9,0x8C,0x04,0x05,0x91,0xC9,0x60,0x04,0x03,0x19, +0x21,0xDD,0x8F,0xFC,0xFF,0x03,0x00,0xDD,0x8F,0x08,0xE0,0x03,0x00,0xDD,0x08,0x7F, +0xC9,0x72,0x04,0xFB,0x04,0xEF,0xCB,0x01,0x00,0x00,0xE9,0x50,0x05,0xB3,0x03,0x51, +0x13,0x0A,0x9E,0xEF,0xEC,0x51,0x00,0x00,0x7E,0x31,0xA0,0x00,0xAB,0x8F,0x03,0xE0, +0x51,0xC9,0x5C,0x04,0x91,0xC9,0x60,0x04,0x04,0x19,0x18,0xDD,0x8F,0xFF,0x00,0x00, +0x00,0xDD,0x00,0xDD,0x0A,0x7F,0xC9,0x7A,0x04,0xFB,0x04,0xEF,0x95,0x01,0x00,0x00, +0xE8,0x50,0x0A,0x9E,0xEF,0xD4,0x51,0x00,0x00,0x7E,0x31,0x6F,0x00,0x90,0x51,0xC9, +0x5F,0x04,0x91,0xC9,0x5E,0x04,0xC9,0x8C,0x04,0x12,0x0A,0x9E,0xEF,0xF1,0x51,0x00, +0x00,0x7E,0x31,0x57,0x00,0xD4,0x53,0x11,0x1F,0xDE,0x43,0xC9,0x8A,0x04,0x50,0xB1, +0x60,0xC9,0x5C,0x04,0x12,0x12,0x91,0xA0,0x02,0xC9,0x5E,0x04,0x13,0x35,0x9E,0xEF, +0xEC,0x51,0x00,0x00,0x7E,0x31,0x34,0x00,0xF2,0x56,0x53,0xDD,0xD4,0x53,0x11,0x0E, +0xDE,0x43,0xC9,0x8A,0x04,0x50,0x91,0xA0,0x02,0xC9,0x5E,0x04,0x13,0x15,0xF2,0x56, +0x53,0xEE,0x91,0x56,0x08,0x19,0x0A,0x9E,0xEF,0xF2,0x51,0x00,0x00,0x7E,0x31,0x0B, +0x00,0xD6,0x56,0xD0,0xC9,0x5C,0x04,0x43,0xC9,0x8A,0x04,0x05,0xFB,0x01,0xEF,0x49, +0x23,0x00,0x00,0x05,0x91,0xC9,0x60,0x04,0x02,0x19,0x14,0xDD,0x07,0xDD,0x00,0xDD, +0x0A,0x7F,0xC9,0x6A,0x04,0xFB,0x04,0xEF,0xF9,0x00,0x00,0x00,0xE8,0x50,0x0A,0x9E, +0xEF,0x0D,0x51,0x00,0x00,0x7E,0x31,0x66,0x00,0x90,0x51,0xC9,0x5E,0x04,0x95,0xC9, +0x61,0x04,0x13,0x0A,0x9E,0xEF,0x9A,0x50,0x00,0x00,0x7E,0x31,0xBE,0xFF,0x91,0xC9, +0x60,0x04,0x02,0x13,0x0A,0x9E,0xEF,0x77,0x50,0x00,0x00,0x7E,0x31,0xAD,0xFF,0x91, +0xC9,0x5E,0x04,0xC9,0x8C,0x04,0x12,0x0A,0x9E,0xEF,0x9F,0x51,0x00,0x00,0x7E,0x31, +0x9A,0xFF,0xD4,0x53,0x11,0x0E,0xDE,0x43,0xC9,0x8A,0x04,0x50,0x91,0xA0,0x02,0xC9, +0x5E,0x04,0x13,0x12,0xF2,0x56,0x53,0xEE,0x9E,0xEF,0x99,0x51,0x00,0x00,0x7E,0x31, +0x7A,0xFF,0xD0,0x60,0xA0,0xFC,0xD4,0x80,0xF2,0x56,0x53,0xF6,0xD7,0x56,0x05,0xFB, +0x01,0xEF,0xB6,0x22,0x00,0x00,0x05,0x9E,0xEF,0x8F,0x51,0x00,0x00,0x7E,0xFB,0x01, +0xEF,0xA7,0x22,0x00,0x00,0xD4,0x52,0xD4,0x53,0xDE,0x43,0xC9,0x8A,0x04,0x50,0x91, +0x52,0xA0,0x02,0x12,0x3B,0xD5,0x53,0x12,0x14,0x9A,0xA0,0x02,0x7E,0x9E,0xEF,0x86, +0x51,0x00,0x00,0x7E,0xFB,0x02,0xEF,0x81,0x22,0x00,0x00,0x11,0x27,0x9A,0xA0,0x03, +0x7E,0x3C,0x60,0x51,0xC1,0x8F,0x00,0xE0,0x03,0x00,0x51,0x7E,0x9A,0xA0,0x02,0x7E, +0x9E,0xEF,0x81,0x51,0x00,0x00,0x7E,0xFB,0x04,0xEF,0x5E,0x22,0x00,0x00,0x11,0x04, +0xF2,0x56,0x53,0xB5,0xF2,0x08,0x52,0xAF,0x05,0x9E,0xEF,0x82,0x51,0x00,0x00,0x7E, +0xFB,0x01,0xEF,0x45,0x22,0x00,0x00,0x05,0xD5,0x8E,0xD0,0x01,0x50,0x05,0xD5,0x8E, +0xD4,0x50,0x05,0xB5,0x8F,0x0C,0x00,0xD4,0x51,0xD0,0xAC,0x04,0x50,0x3C,0x60,0x52, +0x13,0x2C,0xD0,0xA0,0x04,0x53,0x9A,0x83,0x50,0xC2,0x30,0x50,0x19,0x20,0xD1,0x50, +0xAC,0x08,0x18,0x1A,0xC4,0xAC,0x08,0x51,0xC0,0x50,0x51,0xF5,0x52,0xE8,0xD1,0x51, +0xAC,0x0C,0x19,0x0A,0xD1,0x51,0xAC,0x10,0x14,0x04,0xD0,0x01,0x50,0x04,0xD4,0x50, +0x04,0xD0,0xA9,0x7C,0x57,0x9A,0xC9,0x80,0x00,0x50,0x13,0x06,0x30,0x07,0x00,0xE9, +0x50,0x03,0x30,0x86,0x00,0x05,0xD0,0x50,0x54,0x9A,0xC9,0x81,0x00,0x53,0x12,0x44, +0x9A,0xC9,0x82,0x00,0x55,0xD7,0x55,0x19,0x55,0x9E,0xEF,0xF1,0x53,0x00,0x00,0x52, +0xD0,0x8F,0x08,0x00,0x00,0x20,0x57,0x30,0x61,0x00,0xE9,0x50,0x47,0xD5,0x55,0x13, +0x11,0x9A,0x62,0x53,0x13,0x38,0xC1,0x01,0x53,0x50,0xC0,0x50,0x57,0xCA,0x53,0x57, +0x11,0xE5,0xD6,0x53,0x11,0x09,0xC0,0x53,0x57,0x30,0x3F,0x00,0xE9,0x50,0x1E,0xF5, +0x54,0xF4,0x11,0x16,0x91,0x54,0xC9,0x83,0x00,0x1F,0x09,0x82,0xC9,0x83,0x00,0x54, +0xD6,0x54,0x11,0xAC,0xC4,0x53,0x54,0xC0,0x54,0x57,0x9A,0x01,0x50,0x05,0x3C,0x8F, +0x08,0x09,0x50,0x05,0xD7,0x55,0x19,0xF6,0xD6,0x52,0x9A,0x62,0x53,0x13,0xEF,0xC1, +0x01,0x53,0x50,0xC0,0x50,0x57,0xCA,0x53,0x57,0x11,0x9C,0xB4,0x7E,0x3F,0x6E,0xDD, +0x8F,0x40,0x40,0x00,0x00,0xDD,0x57,0xFB,0x03,0xEF,0xDC,0x10,0x00,0x00,0xB0,0x8E, +0x58,0xD5,0x50,0x12,0x04,0x9A,0x01,0x50,0x05,0xD4,0x9F,0x06,0x05,0x14,0x20,0x3C, +0x8F,0xBC,0x02,0x50,0x05,0x00,0x00,0x00,0xD1,0x50,0x8F,0x00,0x02,0x00,0x00,0x1F, +0x01,0x05,0xBB,0x1C,0x9E,0xEF,0x66,0x53,0x00,0x00,0x52,0xD0,0x50,0x53,0xEF,0x04, +0x04,0x9F,0x00,0x04,0x14,0x20,0x54,0xB1,0x62,0x53,0x12,0x0C,0x91,0xA2,0x02,0x0C, +0x13,0x24,0x91,0xA2,0x02,0x54,0x13,0x1E,0x9F,0xA2,0x03,0xFB,0x01,0xEF,0x59,0x06, +0x00,0x00,0xC0,0x50,0x52,0xC0,0x04,0x52,0x95,0xA2,0x03,0x12,0xDA,0x9E,0xEF,0x0A, +0x00,0x00,0x00,0x50,0x11,0x04,0x9E,0xA2,0x03,0x50,0xBA,0x1C,0x05,0x00,0x00,0x00, +0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0xDD,0x01,0x91,0xCB,0xC9,0x00,0x02,0x12,0x0E, +0xD0,0xCB,0x89,0x00,0x6E,0x12,0x07,0xD0,0x8E,0x50,0xD0,0x00,0x50,0x05,0xFB,0x01, +0xEF,0x21,0x01,0x00,0x00,0x05,0xBB,0x08,0xC2,0x0A,0x5E,0xB0,0x08,0xAE,0x08,0x9E, +0xEF,0x12,0x00,0x00,0x00,0x6E,0xD0,0x9F,0xAE,0x04,0x14,0x20,0xAE,0x04,0xD0,0x5E, +0x9F,0xAE,0x04,0x14,0x20,0x11,0x10,0xDD,0x8F,0x8D,0x00,0x00,0x00,0xFB,0x01,0xEF, +0x88,0x20,0x00,0x00,0x31,0xC7,0x00,0xD0,0x9F,0xFE,0x04,0x14,0x20,0x50,0xD0,0x9F, +0x08,0x04,0x14,0x20,0xA0,0x28,0xDE,0x7E,0x7E,0xDD,0x8F,0x80,0x08,0x00,0x00,0xDD, +0x0E,0xFB,0x03,0xEF,0xF2,0x0F,0x00,0x00,0xD0,0x8E,0x53,0x7E,0x7E,0x7E,0xDD,0x8F, +0x00,0x21,0x00,0x00,0xDD,0x53,0xFB,0x03,0xEF,0xDD,0x0F,0x00,0x00,0x7D,0x8E,0x50, +0xD0,0x50,0x9F,0xEE,0x04,0x14,0x20,0xCB,0x10,0x51,0x9F,0xF2,0x04,0x14,0x20,0xC1, +0x08,0x53,0x7E,0x9F,0x6E,0xDD,0x8F,0x80,0x08,0x00,0x00,0xDD,0x0E,0xFB,0x03,0xEF, +0xC4,0x0F,0x00,0x00,0xC0,0x04,0x5E,0xDE,0x7E,0x7E,0xDD,0x8F,0x80,0x10,0x00,0x00, +0xDD,0x38,0xFB,0x03,0xEF,0xA1,0x0F,0x00,0x00,0xD0,0x8E,0x54,0xD5,0x54,0x13,0x0D, +0xF0,0x9F,0x2A,0x04,0x14,0x20,0x1B,0x04,0x9F,0x0C,0x04,0x14,0x20,0xDD,0x8F,0x00, +0x40,0x00,0x00,0xD5,0x54,0x13,0x07,0xD0,0x8F,0x00,0x20,0x00,0x00,0x6E,0xDD,0x9F, +0xEE,0x04,0x14,0x20,0xFB,0x02,0xEF,0x8F,0x14,0x00,0x00,0xD7,0x9F,0x04,0x04,0x14, +0x20,0x13,0x1B,0xDD,0x9F,0x04,0x04,0x14,0x20,0xFB,0x01,0xEF,0x36,0x00,0x00,0x00, +0xD0,0xAE,0x04,0x9F,0xAE,0x04,0x14,0x20,0xC0,0x08,0x5E,0xBA,0x8E,0x05,0xD4,0x9F, +0x04,0x04,0x14,0x20,0xCA,0x10,0x9F,0xF2,0x04,0x14,0x20,0xE5,0x1C,0x9F,0x00,0x04, +0x14,0x20,0x00,0xD0,0x00,0x50,0xD0,0xAE,0x04,0x9F,0xAE,0x04,0x14,0x20,0xC0,0x08, +0x5E,0xBA,0x8E,0x05,0xB5,0x8F,0x3C,0x00,0xD0,0x8F,0x00,0x04,0x14,0x20,0x54,0xD0, +0xAC,0x04,0xA4,0x04,0xDE,0x7E,0x7E,0xDD,0x8F,0x80,0x10,0x00,0x00,0xDD,0x11,0xFB, +0x03,0xEF,0x04,0x0F,0x00,0x00,0xD0,0x8E,0x55,0xD0,0xA5,0x28,0xA4,0x08,0xDE,0x7E, +0x7E,0xDD,0x8F,0x80,0x10,0x00,0x00,0xDD,0x38,0xFB,0x03,0xEF,0xEA,0x0E,0x00,0x00, +0xD0,0x8E,0x53,0x9F,0xA4,0x2C,0xFB,0x01,0xEF,0x6A,0x00,0x00,0x00,0xD5,0x50,0x13, +0x01,0x04,0xD0,0x51,0xA5,0x28,0xEF,0x00,0x02,0xA4,0x08,0x50,0xF0,0x50,0x00,0x02, +0xA5,0x28,0x94,0xA4,0x2C,0xD4,0xA4,0x0C,0x94,0xA4,0x2A,0xD5,0x53,0x13,0x14,0xD0, +0x52,0xA4,0x0C,0xEF,0x1B,0x04,0x62,0x50,0x90,0x50,0xA4,0x2A,0xC8,0x8F,0x00,0x00, +0x00,0x78,0x62,0x9E,0xEF,0xB7,0xE6,0xFF,0xFF,0x50,0xD1,0x50,0x8F,0x00,0x00,0x04, +0x20,0x1E,0x0C,0x9E,0xEF,0xA7,0xE6,0xFF,0xFF,0xA5,0x28,0xC8,0x01,0xA5,0x28,0xC8, +0x10,0x9F,0xF2,0x04,0x14,0x20,0xE2,0x1C,0x64,0x00,0x90,0x01,0x9F,0x16,0x05,0x14, +0x20,0xD0,0x00,0x50,0x04,0xB5,0x8F,0x08,0x00,0xDE,0x7E,0x7E,0xDD,0x8F,0x80,0x10, +0x00,0x00,0xDD,0x38,0xFB,0x03,0xEF,0x5F,0x0E,0x00,0x00,0xD0,0x8E,0x51,0x12,0x0A, +0xD0,0x00,0x50,0xD0,0xAC,0x04,0x51,0xD4,0x52,0x04,0xDB,0x0C,0x52,0xDB,0x0D,0x53, +0xEF,0x09,0x15,0xAC,0x04,0x50,0xC8,0x8F,0x00,0x00,0x00,0x80,0x50,0xCB,0x8F,0x00, +0x00,0xE0,0x7F,0x82,0x51,0xD1,0x50,0x51,0x13,0x07,0xF5,0x53,0xF0,0xD0,0x30,0x50, +0x04,0xC2,0x04,0x52,0xDB,0x0C,0x50,0xC3,0x50,0x52,0x51,0x78,0x07,0x51,0x51,0xC8, +0x8F,0x00,0x00,0x00,0x80,0x51,0xCB,0x8F,0x00,0xFE,0xFF,0xFF,0xAC,0x04,0x50,0xC8, +0x50,0x51,0xD0,0x00,0x50,0x04,0x00,0x00,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0xE0, +0x12,0x6B,0x16,0xFA,0xEF,0xB8,0x69,0x00,0x00,0xEF,0x34,0x01,0x00,0x00,0xD1,0x50, +0x8F,0xFF,0xFF,0xFF,0xFF,0x13,0x42,0x11,0x24,0xD0,0x8F,0x58,0x07,0x14,0x20,0x50, +0xDD,0x8F,0x00,0x01,0x00,0x00,0x16,0xEF,0x7B,0x82,0x01,0x00,0x01,0xFB,0x03,0xEF, +0x2B,0x00,0x00,0x00,0xD1,0x50,0x8F,0xFF,0xFF,0xFF,0xFF,0x13,0x1C,0xC1,0x8F,0x00, +0x02,0x00,0x00,0x50,0x7E,0x9F,0x6E,0xDD,0x8F,0x80,0x08,0x00,0x00,0xDD,0x0E,0xFB, +0x03,0xEF,0xB2,0x0D,0x00,0x00,0xD4,0x8E,0x05,0xD0,0x2C,0x50,0x05,0xB5,0x8F,0x1C, +0x00,0x78,0x03,0xAC,0x08,0x52,0xC2,0xAC,0x0C,0x52,0x19,0x2A,0xD0,0xAC,0x04,0x53, +0xD4,0x54,0xD0,0xAC,0x0C,0x50,0xD0,0x54,0x51,0xE1,0x51,0x63,0x07,0xD6,0x51,0xF5, +0x50,0xF7,0x11,0x0D,0xD0,0x51,0x54,0xE0,0x54,0x63,0xE7,0xF3,0x52,0x54,0xF8,0x11, +0x05,0x78,0x09,0x54,0x50,0x04,0xD0,0x8F,0xFF,0xFF,0xFF,0xFF,0x50,0x04,0xE3,0x03, +0x9F,0x00,0x04,0x14,0x20,0x03,0x31,0x89,0x00,0xFB,0xEF,0x12,0x69,0x00,0x00,0xEF, +0x8E,0x00,0x00,0x00,0xD0,0x50,0x52,0x19,0x79,0xE8,0xA2,0x0C,0x75,0xDF,0x8F,0x00, +0x00,0x00,0x00,0xDD,0x8F,0x80,0x10,0x00,0x00,0xDD,0x38,0xFB,0x03,0xEF,0x36,0x0D, +0x00,0x00,0xD5,0x50,0x12,0x5C,0xDF,0x8F,0x00,0x00,0x1F,0x04,0xDD,0x8F,0x80,0x80, +0x00,0x00,0xDD,0x00,0xFB,0x03,0xEF,0x1D,0x0D,0x00,0x00,0xD5,0x50,0x12,0x43,0xEF, +0x08,0x06,0x9F,0xFA,0x04,0x14,0x20,0x9F,0xE2,0x04,0x14,0x20,0x9F,0xC2,0x00,0x02, +0xDF,0x6E,0xDD,0x8F,0x80,0x08,0x00,0x00,0xDD,0x0E,0xFB,0x03,0xEF,0xF7,0x0C,0x00, +0x00,0xD0,0x8E,0x51,0xD5,0x50,0x12,0x1A,0x9F,0xA2,0x04,0xDD,0x8F,0x80,0x08,0x00, +0x00,0xDD,0x0F,0xFB,0x03,0xEF,0xDE,0x0C,0x00,0x00,0xD5,0x50,0x12,0x04,0xD0,0x00, +0x50,0x05,0xD4,0x9F,0x06,0x05,0x14,0x20,0xD0,0x8F,0x84,0x00,0x00,0x00,0x50,0x05, +0xB5,0x8F,0x00,0x00,0xBB,0x8F,0xFC,0x3F,0xC2,0x0A,0x5E,0xB0,0x8F,0xFC,0x3F,0xAE, +0x08,0x9E,0xEF,0x12,0x00,0x00,0x00,0x6E,0xD0,0x9F,0x18,0x05,0x14,0x20,0xAE,0x04, +0xD0,0x5E,0x9F,0x18,0x05,0x14,0x20,0x11,0x1C,0xD0,0x8F,0xFF,0xFF,0xFF,0xFF,0x50, +0xD0,0x9F,0x18,0x05,0x14,0x20,0x5E,0xD0,0xAE,0x04,0x9F,0x18,0x05,0x14,0x20,0xC0, +0x08,0x5E,0xBA,0x8E,0x04,0xD4,0x53,0xD1,0x63,0x53,0x12,0x1A,0xD5,0xA3,0x04,0x13, +0x15,0xD4,0x52,0xD4,0x50,0xD0,0xA3,0x04,0x51,0xC0,0x81,0x52,0xF2,0x1F,0x50,0xF9, +0xD1,0x52,0xA3,0x08,0x13,0x09,0xC0,0x8F,0x00,0x02,0x00,0x00,0x53,0x11,0xD8,0xD0, +0x53,0x50,0xD0,0x9F,0x18,0x05,0x14,0x20,0x5E,0xD0,0xAE,0x04,0x9F,0x18,0x05,0x14, +0x20,0xC0,0x08,0x5E,0xBA,0x8E,0x04,0x00,0xD0,0x00,0x9F,0x08,0x01,0x14,0x20,0xD0, +0x8F,0x95,0x00,0x00,0x80,0x9F,0x00,0x01,0x14,0x20,0x05,0xD0,0x9F,0x04,0x01,0x14, +0x20,0x50,0x05,0xDD,0x8F,0x03,0x00,0x00,0x00,0xDD,0x8F,0x05,0x00,0x00,0x00,0xDD, +0x8F,0x56,0x00,0x00,0x00,0x8F,0x9F,0x04,0x00,0x04,0x20,0x00,0x03,0x08,0x00,0x09, +0x00,0x11,0x00,0x08,0x00,0x00,0xDD,0x8F,0x41,0x00,0x00,0x00,0x11,0x06,0xDD,0x8F, +0x42,0x00,0x00,0x00,0x9F,0xEF,0xD8,0x67,0x00,0x00,0x16,0xEF,0xF1,0x7F,0x01,0x00, +0xD1,0x50,0x8F,0x00,0x00,0x00,0x04,0x15,0x07,0x9E,0xEF,0xB7,0x67,0x00,0x00,0x6E, +0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x9F,0xEF,0x86,0x67,0x00,0x00,0xFB, +0x06,0xEF,0x26,0x1C,0x00,0x00,0x91,0x8F,0x56,0x8F,0x56,0x13,0x13,0xDD,0x8F,0x01, +0x00,0x00,0x00,0x9F,0xEF,0x7B,0x67,0x00,0x00,0xFB,0x02,0xEF,0x0C,0x1C,0x00,0x00, +0xDD,0x8F,0x07,0x00,0x00,0x00,0xDD,0x8F,0x02,0x00,0x00,0x00,0x9F,0xEF,0x66,0x67, +0x00,0x00,0xFB,0x03,0xEF,0xF3,0x1B,0x00,0x00,0x05,0xD0,0x50,0x53,0xBB,0x0E,0xC2, +0x0A,0x5E,0xB0,0x0E,0xAE,0x08,0x9E,0xEF,0x12,0x00,0x00,0x00,0x6E,0xD0,0x9F,0x18, +0x05,0x14,0x20,0xAE,0x04,0xD0,0x5E,0x9F,0x18,0x05,0x14,0x20,0x11,0x05,0xCE,0x01, +0x50,0x11,0x1D,0xCB,0x8F,0x00,0x00,0xFF,0xFF,0x53,0x50,0x78,0x8F,0xFF,0x51,0x51, +0xAC,0x82,0x50,0x78,0x01,0x50,0x50,0xE5,0x10,0x50,0x02,0xB6,0x50,0xF5,0x51,0xF0, +0xD0,0x9F,0x18,0x05,0x14,0x20,0x5E,0xD0,0xAE,0x04,0x9F,0x18,0x05,0x14,0x20,0xC0, +0x08,0x5E,0xBA,0x8E,0x05,0xDD,0x50,0x8F,0x9F,0x05,0x00,0x04,0x20,0x01,0x02,0x10, +0x00,0x07,0x00,0x19,0x00,0x00,0xD0,0x8F,0x7B,0x01,0x00,0x00,0x50,0x11,0x10,0xD0, +0x8F,0x3D,0x01,0x00,0x00,0x50,0x11,0x07,0xD0,0x8F,0xA6,0x01,0x00,0x00,0x50,0xF5, +0x50,0xFD,0xD0,0x8E,0x50,0x05,0xB5,0x8F,0x00,0x00,0xDD,0x8F,0xB8,0x0B,0x00,0x00, +0xDD,0x0F,0xDD,0x01,0xDD,0x8F,0x92,0x00,0x00,0x00,0xFB,0x04,0xEF,0x0D,0x00,0x00, +0x00,0xD5,0x50,0x13,0x03,0xD0,0x05,0x51,0x30,0xFE,0x37,0x04,0xB5,0x8F,0x00,0x08, +0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0xDD,0xAC,0x10,0xDD,0x8F,0x51,0x00,0x00,0x00, +0x9F,0xAB,0x30,0xDD,0xAC,0x04,0xFB,0x04,0xEF,0xE9,0x15,0x00,0x00,0xD1,0x50,0x2D, +0x13,0x3F,0xFA,0xEF,0xB2,0x8F,0x00,0x00,0xEF,0xA5,0x44,0x00,0x00,0xD5,0x50,0x12, +0xD6,0x91,0x01,0xCB,0xC9,0x00,0x12,0xCF,0xDE,0x7E,0x7E,0xDD,0x04,0x9F,0xCB,0x81, +0x00,0xDD,0x0A,0xFB,0x04,0xEF,0x64,0x00,0x00,0x00,0xD0,0x8E,0x51,0xD5,0x50,0x12, +0xB6,0xD1,0x51,0xAC,0x08,0x1F,0xB0,0xD1,0x51,0xAC,0x0C,0x1A,0xAA,0xD0,0x00,0x50, +0x04,0xDD,0x8F,0x8C,0x00,0x00,0x00,0xFB,0x01,0xEF,0xCE,0x1A,0x00,0x00,0xD4,0x51, +0xD0,0x2D,0x50,0x04,0xB5,0x8F,0x04,0x00,0xD0,0xAC,0x04,0x50,0x3C,0x60,0x51,0xD0, +0xA0,0x04,0x50,0x11,0x10,0x9A,0x41,0x60,0x52,0xE1,0x52,0xEF,0xA8,0x66,0x00,0x00, +0x04,0x82,0x20,0x41,0x60,0xF4,0x51,0xED,0x04,0xB5,0x8F,0x00,0x00,0xD0,0xAC,0x04, +0x50,0x95,0x80,0x12,0xFC,0xC2,0xAC,0x04,0x50,0xD7,0x50,0x04,0xB5,0x8F,0x3C,0x00, +0xD0,0xAC,0x08,0x50,0x3C,0x60,0x54,0x12,0x04,0xD0,0x23,0x50,0x04,0xD0,0xA0,0x04, +0x55,0xD4,0x50,0x94,0x40,0xBC,0x10,0xF2,0xAC,0x0C,0x50,0xF7,0x11,0x5C,0xD4,0x50, +0xD4,0x52,0x9A,0x42,0xBC,0x10,0x51,0xC4,0xAC,0x04,0x51,0xC0,0x50,0x51,0x90,0x51, +0x42,0xBC,0x10,0x78,0x8F,0xF8,0x51,0x50,0xF2,0xAC,0x0C,0x52,0xE5,0xD5,0x50,0x12, +0x40,0x90,0x85,0x50,0xE1,0x50,0xEF,0x81,0x66,0x00,0x00,0x39,0x82,0x30,0x50,0x9A, +0x40,0xEF,0xDA,0x65,0x00,0x00,0x50,0x91,0x50,0xAC,0x04,0x1E,0x28,0xD4,0x52,0x9A, +0x42,0xBC,0x10,0x51,0xC0,0x50,0x51,0x90,0x51,0x42,0xBC,0x10,0x78,0x8F,0xF8,0x51, +0x50,0xF2,0xAC,0x0C,0x52,0xE9,0xD5,0x50,0x12,0x07,0xF4,0x54,0xA1,0xD0,0x00,0x50, +0x04,0xD0,0x26,0x50,0x04,0xD0,0x23,0x50,0x04,0x00,0x00,0x00,0xD0,0x00,0x50,0x05, +0xD0,0x00,0x50,0x05,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0xDD,0x6B,0xDD,0xAB,0x14, +0xFB,0x02,0xEF,0x93,0x0E,0x00,0x00,0xD5,0x50,0x13,0x13,0xDD,0x50,0xDD,0x8F,0x8C, +0x00,0x00,0x00,0xFB,0x01,0xEF,0xD2,0x19,0x00,0x00,0xD0,0x8E,0x50,0x05,0xD0,0x51, +0xAB,0x08,0xD0,0xAB,0x14,0xAB,0x2C,0xC0,0xAB,0x08,0xAB,0x14,0xF4,0xAB,0x20,0xCB, +0xD0,0x00,0x50,0x05,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0xE0,0x0A,0x6B,0xB5,0x30, +0xC2,0x00,0xD0,0x50,0xAB,0x28,0x9F,0xAB,0x0C,0xDD,0x6B,0xDD,0xAB,0x14,0xFB,0x03, +0xEF,0x25,0x09,0x00,0x00,0xD5,0x50,0x13,0x01,0x05,0xD0,0x51,0xAB,0x18,0xEF,0x0B, +0x05,0x6B,0x50,0xEA,0x00,0x05,0x50,0x50,0xDF,0xAB,0x18,0x9F,0xEF,0x1B,0x66,0x00, +0x00,0xC0,0x40,0xEF,0x14,0x66,0x00,0x00,0x6E,0xFB,0x02,0xEF,0x6C,0x19,0x00,0x00, +0xFA,0xEF,0xBB,0x64,0x00,0x00,0xEF,0xE3,0x00,0x00,0x00,0xD0,0xAB,0x14,0xAB,0x2C, +0xC0,0xAB,0x28,0xAB,0x14,0xF4,0xAB,0x20,0xAD,0xD0,0x00,0x50,0x05,0xD0,0x8F,0x1C, +0x05,0x14,0x20,0x5B,0x30,0x5D,0x00,0xD0,0x50,0xAB,0x28,0x9A,0xCB,0xC9,0x00,0x53, +0xC2,0x02,0x53,0xE1,0x01,0x6B,0x05,0xC1,0x01,0xAB,0x20,0x53,0xD0,0x02,0x52,0x01, +0x7D,0x42,0xCB,0x81,0x00,0xAB,0x0C,0x9F,0xAB,0x0C,0xDD,0x6B,0xDD,0xAB,0x14,0xFB, +0x03,0xEF,0xB2,0x08,0x00,0x00,0xD5,0x50,0x13,0x01,0x05,0xD6,0x52,0x91,0x52,0xCB, +0xC9,0x00,0x19,0x03,0xD0,0x02,0x52,0xD3,0x8F,0xFF,0x0F,0x00,0x00,0x53,0x12,0x03, +0x30,0x18,0x17,0xD0,0xAB,0x14,0xAB,0x2C,0xC0,0xAB,0x28,0xAB,0x14,0xF5,0x53,0xC0, +0xD0,0x00,0x50,0x05,0xDD,0x5B,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0xEF,0x0B,0x05, +0x6B,0x50,0xEA,0x00,0x05,0x50,0x50,0x8F,0x50,0x00,0x04,0x0B,0x00,0x0B,0x00,0x10, +0x00,0x10,0x00,0x20,0x00,0x00,0xD0,0x01,0x50,0x11,0x12,0xE0,0x03,0x6B,0x06,0xD0, +0xAB,0x08,0x50,0x11,0x08,0xD0,0xAB,0x28,0x50,0x11,0x02,0xD4,0x50,0xD0,0x8E,0x5B, +0x05,0xB5,0x8F,0x00,0x00,0xEF,0x0B,0x05,0xAC,0x08,0x50,0xEA,0x00,0x05,0x50,0x50, +0xDF,0xAC,0x04,0x9F,0xEF,0x47,0x65,0x00,0x00,0xC0,0x40,0xEF,0x40,0x65,0x00,0x00, +0x6E,0xFB,0x02,0xEF,0x84,0x18,0x00,0x00,0xD0,0x00,0x50,0x04,0xB5,0x8F,0x00,0x08, +0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0xEF,0x05,0x05,0x6B,0x50,0xEA,0x00,0x05,0x50, +0x50,0x9F,0xAB,0x0C,0x9F,0xEF,0x57,0x65,0x00,0x00,0xC0,0x40,0xEF,0x50,0x65,0x00, +0x00,0x6E,0xFB,0x02,0xEF,0x53,0x18,0x00,0x00,0xD0,0x00,0x50,0x04,0xD0,0x8F,0x1C, +0x05,0x14,0x20,0x5B,0x30,0x5D,0xFF,0xD0,0x50,0xAB,0x28,0xD0,0xCB,0x89,0x00,0x52, +0xD0,0xCB,0x91,0x00,0x53,0xC1,0x01,0xAB,0x20,0x54,0xD0,0xAB,0x28,0x55,0xD1,0x53, +0x52,0x19,0x10,0xC3,0x01,0x54,0x50,0xC4,0x55,0x50,0xC0,0x50,0x52,0xC0,0x50,0x53, +0xCE,0x55,0x55,0x01,0x9F,0xAB,0x0C,0xDD,0x6B,0xDD,0x52,0xFB,0x03,0xEF,0x98,0x07, +0x00,0x00,0xD5,0x50,0x13,0x01,0x05,0x9F,0xAB,0x0C,0xDD,0x6B,0xDD,0x53,0xFB,0x03, +0xEF,0x93,0x07,0x00,0x00,0xD5,0x50,0x13,0x01,0x05,0xD3,0x8F,0xFF,0x0F,0x00,0x00, +0xAB,0x20,0x12,0x03,0x30,0x04,0x16,0xC0,0x55,0x53,0xC0,0x55,0x52,0xF4,0xAB,0x20, +0xC3,0xD0,0x00,0x50,0x05,0xDD,0x00,0xDD,0x00,0xDD,0x8F,0xAE,0x00,0x00,0x00,0xFB, +0x03,0xEF,0x9C,0x1F,0x00,0x00,0xBB,0x3C,0x9E,0xEF,0x62,0x8D,0x00,0x00,0x50,0xDA, +0x50,0x11,0xDE,0xEF,0xE0,0x64,0x00,0x00,0x52,0x9F,0xC2,0x04,0x00,0xDD,0x8F,0x80, +0x10,0x00,0x00,0xDD,0xC2,0x00,0x00,0xFB,0x03,0xEF,0x3A,0x07,0x00,0x00,0xC0,0x8F, +0x08,0x00,0x00,0x00,0x52,0xD5,0x62,0x12,0xE0,0xD0,0x8F,0x00,0x00,0x1F,0x04,0x9F, +0xF2,0x04,0x14,0x20,0xDB,0x25,0x50,0xDA,0x50,0x25,0x8F,0x9F,0x05,0x00,0x04,0x20, +0x01,0x02,0x07,0x00,0x21,0x00,0x07,0x00,0x00,0x2C,0x00,0x8F,0x00,0x00,0x8F,0x00, +0x80,0x9F,0x00,0x00,0x00,0x10,0x2C,0x00,0x8F,0x00,0x00,0x8F,0x00,0x80,0x9F,0x00, +0x80,0x00,0x10,0xD0,0x8F,0x00,0x55,0xD4,0x00,0x9F,0x10,0x00,0x14,0x20,0x8F,0x9F, +0x05,0x00,0x04,0x20,0x01,0x02,0x07,0x00,0x3B,0x00,0x77,0x00,0x00,0xD0,0x04,0x9F, +0x20,0x00,0x14,0x20,0xD0,0x8F,0x00,0x40,0x08,0x20,0x9F,0x30,0x01,0x14,0x20,0xD0, +0x00,0x9F,0x34,0x01,0x14,0x20,0xD0,0x8F,0x04,0x40,0x08,0x20,0x9F,0x40,0x01,0x14, +0x20,0xD0,0x00,0x9F,0x44,0x01,0x14,0x20,0xD0,0x8F,0x77,0x55,0xD4,0x00,0x51,0x11, +0x6E,0xD0,0x04,0x9F,0x20,0x00,0x14,0x20,0xD0,0x8F,0x00,0x00,0x10,0x20,0x9F,0x30, +0x01,0x14,0x20,0xD0,0x8F,0xFC,0xFF,0x03,0x00,0x9F,0x34,0x01,0x14,0x20,0xD0,0x8F, +0x00,0x40,0x08,0x20,0x9F,0x40,0x01,0x14,0x20,0xD0,0x8F,0xFC,0x0F,0x00,0x00,0x9F, +0x44,0x01,0x14,0x20,0xD0,0x8F,0x33,0x55,0xD4,0x00,0x51,0x11,0x32,0xD0,0x04,0x9F, +0x20,0x00,0x14,0x20,0xD0,0x8F,0x00,0x40,0x08,0x20,0x9F,0x30,0x01,0x14,0x20,0xD0, +0x00,0x9F,0x34,0x01,0x14,0x20,0xD0,0x8F,0x04,0x40,0x08,0x20,0x9F,0x40,0x01,0x14, +0x20,0xD0,0x00,0x9F,0x44,0x01,0x14,0x20,0xD0,0x8F,0x77,0x55,0xD4,0x00,0x51,0xD0, +0x51,0x9F,0x10,0x00,0x14,0x20,0x9A,0x9F,0x04,0x40,0x08,0x20,0x50,0xEF,0x04,0x03, +0x50,0x50,0xF0,0x50,0x08,0x03,0x51,0xF0,0x50,0x0C,0x03,0x51,0xD0,0x51,0x9F,0x10, +0x00,0x14,0x20,0xD0,0x8F,0x78,0x00,0x00,0x00,0x9F,0x0C,0x01,0x14,0x20,0xD0,0x8F, +0x7C,0x00,0x00,0x00,0x9F,0x1C,0x01,0x14,0x20,0x30,0xB2,0x3E,0xD0,0x00,0x50,0xBA, +0x3C,0x05,0xDA,0x01,0x37,0xBB,0x8F,0x80,0x08,0xD0,0x8F,0x2D,0x04,0x14,0x20,0x5B, +0x16,0xBB,0x2C,0xBA,0x8F,0x80,0x08,0xD0,0x00,0x50,0x05,0xD0,0x8F,0x1C,0x05,0x14, +0x20,0x5B,0xD0,0xCB,0x89,0x00,0x9F,0xEE,0x04,0x14,0x20,0x90,0x01,0x9F,0x16,0x05, +0x14,0x20,0xD0,0x00,0x50,0x05,0x90,0x01,0x9F,0x16,0x05,0x14,0x20,0xD0,0x00,0x50, +0x05,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0x30,0x29,0xFD,0xD0,0x50,0x5A,0x9E,0xCB, +0x91,0x00,0x50,0x7D,0x80,0x52,0x7C,0x54,0x91,0xCB,0xC9,0x00,0x04,0x12,0x03,0x7D, +0x80,0x54,0xCA,0x54,0x52,0xCA,0x55,0x53,0x7C,0xCB,0x91,0x00,0xD0,0xAB,0x20,0x58, +0xD0,0xAB,0x14,0x59,0x9F,0xCB,0x91,0x00,0xDD,0x6B,0xDD,0x59,0xFB,0x03,0xEF,0x67, +0x05,0x00,0x00,0xD5,0x50,0x13,0x01,0x05,0xCB,0x54,0xCB,0x91,0x00,0x56,0xCB,0x55, +0xCB,0x95,0x00,0x57,0xD1,0x52,0x56,0x12,0x05,0xD1,0x53,0x57,0x13,0x1A,0xE0,0x13, +0x6B,0x1A,0xC0,0x5A,0x59,0xD3,0x8F,0xFF,0x03,0x00,0x00,0x58,0x12,0x03,0x30,0xCA, +0x13,0xF4,0x58,0xC0,0xD0,0x00,0x50,0x05,0xE0,0x13,0x6B,0xE6,0xD0,0xAB,0x14,0xAB, +0x2C,0xD0,0x59,0xAB,0x14,0x7D,0xCB,0x91,0x00,0xAB,0x0C,0xDD,0x6B,0xDD,0xAB,0x14, +0xFB,0x02,0xCF,0xDE,0xFC,0xFA,0xEF,0xD6,0x60,0x00,0x00,0xCF,0x00,0xFD,0x11,0xC2, +0xFA,0xEF,0x14,0x8A,0x00,0x00,0xEF,0x07,0x3F,0x00,0x00,0xD5,0x50,0x12,0x3E,0x28, +0x8F,0x40,0x00,0x9F,0xA5,0x05,0x14,0x20,0x9F,0x9D,0x05,0x14,0x20,0x97,0x9F,0xE5, +0x05,0x14,0x20,0x30,0x73,0x33,0xD5,0x50,0x12,0x23,0x9E,0xAF,0xD3,0x50,0xD1,0x9F, +0x9D,0x05,0x14,0x20,0x50,0x12,0x04,0xD0,0x22,0x50,0x05,0xD0,0x9F,0x9D,0x05,0x14, +0x20,0x50,0x16,0x60,0xD5,0x50,0x12,0x05,0x30,0x50,0x13,0x11,0xB3,0x05,0xD0,0x8F, +0x1C,0x05,0x14,0x20,0x5B,0xD0,0x02,0x50,0x9E,0xCB,0x91,0x00,0x52,0x9E,0x9F,0x80, +0x07,0x14,0x20,0x53,0x11,0x08,0xD0,0x82,0x83,0xC0,0x04,0x52,0x96,0x50,0x91,0x50, +0xCB,0xC9,0x00,0x19,0xF1,0x9A,0xCB,0xC9,0x00,0x7E,0xC2,0x02,0x6E,0x9A,0xCB,0x89, +0x00,0x7E,0x9A,0xCB,0x8A,0x00,0x7E,0xFB,0x03,0xEF,0xC4,0x1C,0x00,0x00,0x05,0xD0, +0x8F,0x1C,0x05,0x14,0x20,0x5B,0x30,0x07,0x12,0x18,0x04,0xD0,0x2D,0x50,0x05,0x9E, +0xAB,0x30,0x51,0x95,0x61,0x13,0x05,0x80,0x81,0x50,0x11,0xF7,0x95,0x50,0x13,0x12, +0xD0,0x2A,0x50,0x9A,0x50,0x9F,0x0E,0x05,0x14,0x20,0xD0,0x01,0x9F,0x06,0x05,0x14, +0x20,0x05,0xDD,0x8F,0x99,0x00,0x00,0x00,0xFB,0x01,0xEF,0xAD,0x14,0x00,0x00,0xD5, +0xCB,0x91,0x00,0x19,0x0F,0xD0,0xCB,0x89,0x00,0x50,0xD0,0xCB,0x91,0x00,0x51,0x30, +0x27,0x00,0x11,0x12,0xD0,0xCB,0x89,0x00,0x50,0xCB,0x8F,0x00,0x00,0x00,0x80,0xCB, +0x91,0x00,0x51,0x30,0x69,0x00,0xDD,0x50,0xDD,0x8F,0x8C,0x00,0x00,0x00,0xFB,0x01, +0xEF,0x77,0x14,0x00,0x00,0xD0,0x8E,0x50,0x05,0x7D,0x50,0x52,0xD4,0x54,0x11,0x2E, +0x30,0x8D,0x11,0x19,0x46,0x80,0x50,0x54,0xE0,0x1F,0x54,0x20,0x90,0x50,0xCB,0x91, +0x00,0x9F,0xCB,0x91,0x00,0xDD,0x8F,0x20,0x40,0x00,0x00,0xDD,0x52,0xFB,0x03,0xEF, +0xE4,0x03,0x00,0x00,0xD5,0x50,0x13,0x04,0xE2,0x1F,0x54,0x00,0xD6,0x52,0xF4,0x53, +0xCF,0x30,0x5C,0x11,0x19,0x15,0xE1,0x1F,0x54,0x04,0xD0,0x2A,0x50,0x05,0x80,0x50, +0x54,0x13,0x04,0xD0,0x2A,0x50,0x05,0xD0,0x00,0x50,0x05,0xD0,0x2D,0x50,0x05,0x7D, +0x50,0x52,0xD4,0x54,0x11,0x24,0x9F,0xCB,0x91,0x00,0xDD,0x8F,0x20,0x40,0x00,0x00, +0xDD,0x52,0xFB,0x03,0xEF,0x91,0x03,0x00,0x00,0xD5,0x50,0x12,0x1D,0x9A,0xCB,0x91, +0x00,0x50,0x80,0x50,0x54,0x30,0xDF,0x11,0xD6,0x52,0xF4,0x53,0xD9,0xCE,0x54,0x50, +0x9A,0x50,0x50,0x30,0xD1,0x11,0xD0,0x00,0x50,0x05,0xD0,0x2A,0x50,0x05,0x00,0x00, +0xD0,0x9F,0x7C,0x07,0x14,0x20,0x50,0x12,0x04,0x9A,0x20,0x50,0x05,0xC3,0x8F,0x6C, +0x0C,0x00,0x00,0x50,0x59,0x2C,0x00,0x8F,0x00,0x00,0x8F,0x00,0x04,0xA9,0x18,0xD4, +0x58,0x9E,0xEF,0x21,0x61,0x00,0x00,0x7E,0xFB,0x01,0xEF,0xAD,0x13,0x00,0x00,0xDD, +0x00,0xDD,0x8F,0x51,0x00,0x00,0x00,0x9F,0xC9,0x18,0x0C,0x9F,0xEF,0x0E,0x62,0x00, +0x00,0xFB,0x04,0xEF,0x5E,0x0E,0x00,0x00,0xD5,0x50,0x13,0x01,0x05,0x30,0xAA,0x00, +0xE8,0x50,0xDC,0xDF,0xA9,0x08,0x7F,0x69,0x9F,0xEF,0x35,0x90,0x00,0x00,0xFB,0x03, +0xEF,0x8D,0x0D,0x00,0x00,0xD1,0x50,0x01,0x12,0x6C,0xD0,0xA9,0x08,0x50,0xB1,0x50, +0x8F,0x4F,0x00,0x13,0x4C,0xB1,0x50,0x8F,0x50,0x00,0x12,0x09,0xFB,0x00,0xEF,0xE9, +0x00,0x00,0x00,0x11,0xAA,0xD0,0xA9,0x0C,0x51,0xD0,0x50,0x48,0xC9,0x18,0x04,0xD0, +0x51,0x48,0xC9,0x18,0x08,0x3C,0x50,0x50,0xC0,0x51,0x40,0xA9,0x18,0xB1,0x50,0x8F, +0x2A,0x00,0x12,0x15,0xC3,0x01,0x40,0xA9,0x18,0x51,0xD0,0x01,0x40,0xA9,0x18,0x3C, +0x8F,0x48,0x00,0x50,0xC0,0x51,0x40,0xA9,0x18,0xF2,0x8F,0x00,0x01,0x00,0x00,0x58, +0xC2,0x9E,0xEF,0xAD,0x60,0x00,0x00,0x7E,0xFB,0x01,0xEF,0x0D,0x13,0x00,0x00,0x30, +0x00,0x01,0x9A,0x00,0x50,0x05,0x19,0x11,0x9E,0xEF,0xB5,0x60,0x00,0x00,0x7E,0xFB, +0x01,0xEF,0xF6,0x12,0x00,0x00,0x31,0x46,0xFF,0x9E,0xEF,0xBA,0x60,0x00,0x00,0x7E, +0xFB,0x01,0xEF,0xE5,0x12,0x00,0x00,0x31,0x35,0xFF,0xD0,0x01,0xA9,0x0C,0x9E,0xC9, +0x18,0x0C,0x50,0xD0,0x50,0xA9,0x04,0x90,0x80,0x51,0x13,0x4D,0x91,0x51,0x20,0x13, +0x05,0x91,0x51,0x2C,0x12,0xF1,0xBB,0x05,0xD4,0x52,0x9A,0x80,0x51,0x13,0x26,0xE1, +0x51,0xEF,0xD2,0x5E,0x00,0x00,0x1E,0xC4,0x0A,0x52,0x1D,0x19,0xC2,0x30,0x51,0xC0, +0x51,0x52,0x1D,0x11,0x9A,0x80,0x51,0x12,0xE6,0x9A,0x52,0xA9,0x0C,0x13,0x06,0xD1, +0x52,0xA9,0x0C,0x13,0x12,0x9E,0xEF,0x72,0x60,0x00,0x00,0x7E,0xFB,0x01,0xEF,0x89, +0x12,0x00,0x00,0xBA,0x05,0x11,0x0F,0xBA,0x05,0xC2,0xA9,0x04,0x50,0xC3,0x01,0x50, +0x69,0x13,0x03,0xD4,0x50,0x05,0xD0,0x01,0x50,0x05,0xB5,0x8F,0x0C,0x00,0x9E,0xEF, +0x5D,0x60,0x00,0x00,0x7E,0xFB,0x01,0xEF,0x60,0x12,0x00,0x00,0x9E,0xEF,0x11,0x8F, +0x00,0x00,0x52,0xD4,0x53,0xB5,0x62,0x13,0x3A,0xE0,0x1F,0xA2,0x01,0x2A,0xD5,0x53, +0x12,0x0E,0x9A,0x8F,0x8C,0x7E,0xFB,0x01,0xEF,0x3F,0x12,0x00,0x00,0xD0,0x06,0x53, +0x9E,0xA2,0x06,0x7E,0x9A,0xA2,0x05,0x7E,0x9E,0xEF,0x2C,0x60,0x00,0x00,0x7E,0xFB, +0x03,0xEF,0x26,0x12,0x00,0x00,0xD7,0x53,0x9A,0xA2,0x05,0x50,0x9E,0x40,0xA2,0x06, +0x52,0x11,0xC2,0x9E,0xEF,0x1A,0x60,0x00,0x00,0x7E,0xFB,0x01,0xEF,0x0B,0x12,0x00, +0x00,0x04,0xD0,0x8F,0x08,0xE0,0x00,0x00,0xA9,0x10,0xD0,0x8F,0xC0,0x00,0x00,0x00, +0xA9,0x14,0xD0,0x01,0x52,0x9E,0xEF,0xBF,0x91,0x00,0x00,0x50,0xC1,0x42,0xA0,0xFC, +0x50,0x58,0x3C,0xA8,0x01,0x50,0xC0,0x50,0xA9,0x10,0xCA,0x50,0xA9,0x10,0xD0,0x42, +0xA9,0x18,0x53,0x13,0x65,0xC3,0x53,0x42,0xA9,0x18,0x54,0x9A,0xA8,0x04,0x50,0xD1, +0x54,0x50,0x1E,0x07,0x3C,0x44,0xA8,0x06,0x57,0x11,0x06,0x30,0x61,0x00,0xE9,0x50, +0x49,0xD4,0x56,0x95,0x68,0x13,0x19,0x9A,0xA8,0x05,0x51,0xD1,0x54,0x51,0x1E,0x0A, +0xC0,0x50,0x54,0x3C,0x44,0xA8,0x06,0x56,0x11,0x06,0x30,0x59,0x00,0xE9,0x50,0x2A, +0xD0,0x56,0x7E,0xC9,0x8F,0x00,0xE0,0x03,0x00,0x57,0x7E,0x9E,0xEF,0xB8,0x5F,0x00, +0x00,0x7E,0xFB,0x03,0xEF,0x83,0x11,0x00,0x00,0x30,0x6D,0x00,0x9A,0x8F,0x8C,0x7E, +0xFB,0x01,0xEF,0x75,0x11,0x00,0x00,0xF5,0x53,0x9B,0xB5,0xA8,0x01,0x13,0x04,0xC0, +0x02,0xA9,0x10,0xF2,0x8F,0x4E,0x00,0x00,0x00,0x52,0x01,0x05,0x31,0x66,0xFF,0x3C, +0xA8,0x01,0x50,0x13,0x30,0xD0,0xA9,0x10,0x57,0xC0,0x57,0x50,0xD6,0x50,0xD0,0x50, +0xA9,0x10,0x9A,0x01,0x50,0x05,0x9A,0xA8,0x03,0x50,0x13,0x19,0xC1,0x50,0xA9,0x14, +0x56,0xCA,0x50,0x56,0x9A,0x68,0x51,0xC4,0x04,0x51,0xC0,0x56,0x51,0xD0,0x51,0xA9, +0x14,0x9A,0x01,0x50,0x05,0xD0,0x53,0x7E,0x9E,0xEF,0x56,0x5F,0x00,0x00,0x7E,0xFB, +0x02,0xEF,0x16,0x11,0x00,0x00,0xD4,0x50,0x05,0xDD,0x52,0xB1,0x52,0x8F,0x48,0x00, +0x12,0x05,0xB0,0x8F,0x2A,0x00,0x52,0xD4,0x50,0xD0,0x40,0xC9,0x18,0x04,0x51,0xB1, +0x51,0x52,0x13,0x0A,0xF2,0x8F,0x00,0x01,0x00,0x00,0x50,0xED,0x11,0x1B,0xD7,0x40, +0xC9,0x18,0x08,0x12,0x05,0xD4,0x40,0xC9,0x18,0x04,0xDD,0x8F,0xFF,0xFF,0xFF,0x7F, +0xDD,0x51,0xFB,0x02,0xEF,0x06,0x00,0x00,0x00,0xD0,0x8E,0x52,0x05,0xB5,0x8F,0x3C, +0x00,0x9E,0xEF,0x06,0x8D,0x00,0x00,0x52,0x90,0x20,0x53,0xD2,0xAC,0x08,0x54,0xCB, +0x54,0xAC,0x04,0x55,0xB1,0x55,0x8F,0x48,0x00,0x12,0x05,0xB0,0x8F,0x2A,0x00,0x55, +0xB5,0x62,0x13,0x31,0xCB,0x54,0xA2,0x01,0x50,0xD1,0x50,0x55,0x12,0x1C,0x9E,0xA2, +0x06,0x7E,0x9A,0xA2,0x05,0x7E,0x9A,0x53,0x7E,0x9E,0xEF,0xFA,0x5E,0x00,0x00,0x7E, +0xFB,0x04,0xEF,0x85,0x10,0x00,0x00,0x90,0x2F,0x53,0x9A,0xA2,0x05,0x50,0x9E,0x40, +0xA2,0x06,0x52,0x11,0xCB,0x04,0x00,0x00,0xB5,0x8F,0x00,0x00,0xC8,0x8F,0x00,0x00, +0x00,0x80,0xAC,0x08,0x11,0x04,0xB5,0x8F,0x00,0x00,0xBB,0x8F,0xFC,0x37,0xC2,0x0A, +0x5E,0xB0,0x8F,0xFC,0x37,0xAE,0x08,0x9E,0xEF,0x12,0x00,0x00,0x00,0x6E,0xD0,0x9F, +0x18,0x05,0x14,0x20,0xAE,0x04,0xD0,0x5E,0x9F,0x18,0x05,0x14,0x20,0x11,0x15,0xD0, +0x9F,0x18,0x05,0x14,0x20,0x5E,0xD0,0xAE,0x04,0x9F,0x18,0x05,0x14,0x20,0xC0,0x08, +0x5E,0xBA,0x8E,0x04,0xEA,0x0B,0x05,0xAC,0x08,0x50,0xC2,0x0B,0x50,0x9E,0xEF,0xA5, +0x5E,0x00,0x00,0x51,0xC0,0x40,0xEF,0x9D,0x5E,0x00,0x00,0x51,0x16,0x61,0xD0,0x9F, +0x18,0x05,0x14,0x20,0x5E,0xD0,0xAE,0x04,0x9F,0x18,0x05,0x14,0x20,0xC0,0x08,0x5E, +0xBA,0x8E,0x04,0xD1,0xAC,0x04,0x0F,0x1B,0x04,0xD0,0x25,0x50,0x05,0xD1,0xAC,0x04, +0x0E,0x12,0x04,0x30,0x2B,0x00,0x05,0xD0,0xAC,0x04,0x50,0xDE,0x9F,0xB2,0x04,0x14, +0x20,0x51,0xE0,0x1F,0xAC,0x08,0x0D,0xD0,0xBC,0x0C,0x40,0x61,0xD0,0x00,0x50,0xD0, +0xAC,0x04,0x51,0x05,0xD0,0x40,0x61,0xBC,0x0C,0xD0,0x00,0x50,0xD0,0xAC,0x04,0x51, +0x05,0xEF,0x18,0x03,0x9F,0xF2,0x04,0x14,0x20,0x51,0xE1,0x1F,0xAC,0x08,0x03,0x80, +0x05,0x51,0xD0,0x00,0x50,0x8F,0x51,0x00,0x09,0x18,0x00,0x1E,0x00,0x24,0x00,0x2A, +0x00,0x30,0x00,0x3A,0x00,0x40,0x00,0x46,0x00,0x4C,0x00,0x52,0x00,0xD0,0x25,0x50, +0x05,0xDA,0xBC,0x0C,0x00,0x11,0x3E,0xDA,0xBC,0x0C,0x01,0x11,0x38,0xDA,0xBC,0x0C, +0x02,0x11,0x32,0xDA,0xBC,0x0C,0x03,0x11,0x2C,0xD0,0xBC,0x0C,0x9F,0xEA,0x04,0x14, +0x20,0x11,0x22,0xDB,0x00,0xBC,0x0C,0x11,0x1C,0xDB,0x01,0xBC,0x0C,0x11,0x16,0xDB, +0x02,0xBC,0x0C,0x11,0x10,0xDB,0x03,0xBC,0x0C,0x11,0x0A,0xD0,0x9F,0xEA,0x04,0x14, +0x20,0xBC,0x0C,0x11,0x00,0xD0,0x0E,0x51,0x05,0xD0,0x00,0x50,0xD0,0xAC,0x04,0x51, +0xE1,0x11,0xAC,0x08,0x03,0x31,0x8B,0x00,0x8F,0xAC,0x04,0x00,0x3F,0x86,0x00,0x86, +0x00,0x86,0x00,0x86,0x00,0xA1,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86, +0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0xBC, +0x00,0xD7,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86, +0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86, +0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86, +0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86, +0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0xF6,0x00,0x86, +0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0x86,0x00,0xD0,0x25,0x50, +0x31,0x8F,0x00,0xE0,0x1F,0xAC,0x08,0x08,0xDA,0xBC,0x0C,0xAC,0x04,0x31,0x82,0x00, +0xDB,0xAC,0x04,0x50,0xD0,0x50,0xBC,0x0C,0xD0,0x00,0x50,0x31,0x74,0x00,0xE0,0x1F, +0xAC,0x08,0x0B,0xD0,0xBC,0x0C,0x9F,0xEA,0x04,0x14,0x20,0x31,0x64,0x00,0xD0,0x9F, +0xEA,0x04,0x14,0x20,0xBC,0x0C,0x31,0x59,0x00,0xE0,0x1F,0xAC,0x08,0x0B,0xD0,0xBC, +0x0C,0x9F,0xFE,0x04,0x14,0x20,0x31,0x49,0x00,0xD0,0x9F,0xFE,0x04,0x14,0x20,0xBC, +0x0C,0x31,0x3E,0x00,0xE0,0x1F,0xAC,0x08,0x0D,0xF0,0xBC,0x0C,0x10,0x05,0x9F,0xF2, +0x04,0x14,0x20,0x31,0x2C,0x00,0xEF,0x10,0x05,0x9F,0xF2,0x04,0x14,0x20,0xBC,0x0C, +0x31,0x1F,0x00,0xE0,0x1F,0xAC,0x08,0x0D,0xF0,0xBC,0x0C,0x0F,0x01,0x9F,0xFA,0x04, +0x14,0x20,0x31,0x0D,0x00,0xEF,0x0F,0x01,0x9F,0xFA,0x04,0x14,0x20,0xBC,0x0C,0x31, +0x00,0x00,0xD0,0x9F,0x20,0x00,0x14,0x20,0x9F,0x20,0x00,0x14,0x20,0x05,0xE0,0x0F, +0x9F,0xFA,0x04,0x14,0x20,0x04,0x30,0xAE,0x01,0x05,0xEF,0x05,0x05,0xAC,0x08,0x52, +0xCB,0x8F,0xFF,0x01,0x00,0x00,0xAC,0x04,0x50,0xC3,0x01,0x52,0x51,0xC0,0xAC,0x04, +0x51,0xCA,0x8F,0xFF,0x01,0x00,0x00,0x51,0xD1,0x50,0x51,0x12,0x04,0x30,0x1B,0x00, +0x05,0xDD,0x52,0x30,0x15,0x00,0xD0,0x8E,0x52,0xD5,0x50,0x13,0x01,0x05,0xD6,0xAC, +0x04,0xD6,0xAC,0x0C,0xF5,0x52,0xEA,0xD0,0x00,0x50,0x05,0xD0,0xAC,0x04,0x50,0xCB, +0x8F,0x00,0xFE,0xFF,0xFF,0x50,0x54,0xEF,0x09,0x15,0x50,0x55,0xEF,0x1E,0x02,0x50, +0x56,0x8F,0x56,0x00,0x03,0x08,0x00,0x15,0x00,0x22,0x00,0x2F,0x00,0xDB,0x08,0x52, +0xDB,0x09,0x53,0xD1,0x55,0x53,0x1E,0x1C,0x11,0x20,0xDB,0x0A,0x52,0xDB,0x0B,0x53, +0xD1,0x55,0x53,0x1F,0x0F,0x11,0x13,0xDB,0x0C,0x52,0xDB,0x0D,0x53,0xD1,0x55,0x53, +0x1E,0x02,0x11,0x06,0xD0,0x25,0x50,0x31,0x1C,0x01,0xDE,0x45,0x62,0x57,0x91,0x56, +0x02,0x19,0x05,0xD0,0x67,0x58,0x11,0x29,0xD1,0x57,0x8F,0x00,0x00,0x00,0x80,0x1E, +0x06,0xD0,0x25,0x50,0x31,0xFF,0x00,0xDE,0x7E,0x7E,0xDD,0x8F,0x80,0x20,0x02,0x80, +0xDD,0x57,0xFB,0x03,0xCF,0xD3,0xFC,0xD0,0x8E,0x58,0xD5,0x50,0x13,0x03,0x31,0xE5, +0x00,0xE0,0x1F,0x58,0x06,0xD0,0x21,0x50,0x31,0xDB,0x00,0xE0,0x11,0xAC,0x08,0x2C, +0xEF,0x1B,0x04,0x58,0x50,0xEF,0x18,0x02,0x9F,0xF2,0x04,0x14,0x20,0x51,0xF0,0x51, +0x04,0x02,0x50,0xEF,0x1F,0x01,0xAC,0x08,0x51,0xF0,0x51,0x06,0x01,0x50,0xE1,0x50, +0xEF,0xA7,0x5B,0x00,0x00,0x06,0xD0,0x21,0x50,0x31,0xAA,0x00,0xE0,0x1F,0xAC,0x08, +0x2C,0xC8,0x8F,0x00,0x00,0x00,0x04,0x58,0x91,0x56,0x02,0x19,0x05,0xD0,0x58,0x67, +0x11,0x1B,0xDD,0x58,0x9F,0x6E,0xDD,0x8F,0x80,0x20,0x02,0x00,0xDD,0x57,0xFB,0x03, +0xCF,0x75,0xFC,0xC0,0x04,0x5E,0xD5,0x50,0x13,0x03,0x31,0x79,0x00,0xCB,0x8F,0x00, +0x00,0xE0,0xFF,0x58,0x59,0x78,0x09,0x59,0x59,0xC8,0x54,0x59,0xD0,0x59,0x50,0xD0, +0xAC,0x0C,0x51,0xE0,0x1F,0xAC,0x08,0x07,0xD0,0x59,0x51,0xD0,0xAC,0x0C,0x50,0xCB, +0x8F,0xFF,0x01,0x00,0x00,0x59,0x52,0xEF,0x05,0x05,0xAC,0x08,0x53,0xD7,0x53,0xC0, +0x59,0x53,0xCA,0x8F,0xFF,0x01,0x00,0x00,0x53,0xD1,0x52,0x53,0x12,0x14,0xEA,0x05, +0x05,0xAC,0x08,0x52,0x8F,0x52,0x05,0x04,0x0A,0x00,0x0F,0x00,0x14,0x00,0x19,0x00, +0x1E,0x00,0x90,0x60,0x61,0x11,0x19,0xB0,0x60,0x61,0x11,0x14,0xD0,0x60,0x61,0x11, +0x0F,0x7D,0x60,0x61,0x11,0x0A,0x7D,0x60,0x61,0x7D,0xA0,0x08,0xA1,0x08,0x11,0x00, +0xD0,0x50,0x51,0xD0,0x00,0x50,0x05,0xD0,0xAC,0x08,0x50,0xE1,0x04,0x50,0x24,0xE1, +0x0E,0x50,0x20,0xE1,0x07,0x50,0x1C,0xE1,0x1F,0x50,0x0D,0xC8,0x8F,0x00,0x04,0x00, +0x00,0x9F,0x44,0x01,0x08,0x20,0x11,0x0B,0xC8,0x8F,0x83,0x00,0x00,0x00,0x9F,0x44, +0x01,0x08,0x20,0xD0,0xAC,0x04,0x50,0xD0,0xAC,0x0C,0x51,0xE0,0x1F,0xAC,0x08,0x08, +0xD0,0xAC,0x0C,0x50,0xD0,0xAC,0x04,0x51,0xEA,0x05,0x05,0xAC,0x08,0x52,0x8F,0x52, +0x05,0x04,0x0A,0x00,0x0F,0x00,0x14,0x00,0x19,0x00,0x1E,0x00,0x90,0x60,0x61,0x11, +0x19,0xB0,0x60,0x61,0x11,0x14,0xD0,0x60,0x61,0x11,0x0F,0x7D,0x60,0x61,0x11,0x0A, +0x7D,0x60,0x61,0x7D,0xA0,0x08,0xA1,0x08,0x11,0x00,0xE1,0x04,0xAC,0x08,0x0B,0xCA, +0x8F,0x80,0x04,0x00,0x00,0x9F,0x44,0x01,0x08,0x20,0xE1,0x1F,0xAC,0x08,0x03,0xD0, +0x50,0x51,0xD0,0x00,0x50,0xE0,0x1F,0xAC,0x08,0x3D,0xD1,0x51,0x8F,0x00,0x00,0x00, +0x30,0x1F,0x09,0xD1,0x51,0x8F,0xFF,0xFF,0x3F,0x00,0x1B,0x12,0xD1,0x51,0x8F,0x00, +0x00,0x00,0x20,0x1F,0x22,0xD1,0x51,0x8F,0xFF,0x1F,0x00,0x00,0x1A,0x19,0xD0,0x9F, +0x04,0x00,0x08,0x20,0x50,0xE1,0x07,0x50,0x0E,0xC8,0x8F,0x88,0x00,0x00,0x00,0x9F, +0x04,0x00,0x08,0x20,0x9A,0x25,0x50,0x05,0xE0,0x1F,0xAC,0x08,0x1D,0xCB,0x8F,0xFF, +0x00,0xDF,0xCF,0xBC,0x0C,0x50,0x13,0x04,0xD0,0x23,0x50,0x05,0xD0,0xBC,0x0C,0x9F, +0xF2,0x04,0x14,0x20,0xD0,0x00,0x50,0xD4,0x51,0x05,0xD0,0x9F,0xF2,0x04,0x14,0x20, +0xBC,0x0C,0xD0,0x00,0x50,0xD4,0x51,0x05,0xB5,0x8F,0x1C,0x00,0xD0,0xAC,0x04,0x54, +0xCA,0x8F,0xFF,0x07,0xFF,0xFF,0xAC,0x08,0xFA,0x6C,0xEF,0x94,0xF2,0xFF,0xFF,0xDD, +0x00,0x9F,0x6E,0xC9,0x20,0xAC,0x08,0x7E,0xDD,0x54,0xFB,0x03,0xEF,0xB9,0xFA,0xFF, +0xFF,0xD6,0x54,0xD0,0x8E,0x52,0xD5,0x50,0x13,0x04,0xD0,0x01,0x51,0x04,0x91,0x52, +0x8F,0xFD,0x13,0x0C,0x91,0x52,0x8F,0xFF,0x13,0x06,0x91,0x52,0x8F,0xFE,0x12,0x23, +0x78,0x08,0x52,0x52,0xD7,0x5E,0x9F,0x6E,0xC9,0x20,0xAC,0x08,0x7E,0xDD,0x54,0xFB, +0x03,0xEF,0x84,0xFA,0xFF,0xFF,0x90,0x8E,0x52,0xD6,0x54,0xD5,0x50,0x13,0x04,0xD0, +0x02,0x51,0x04,0xDD,0x52,0x9F,0xEF,0x3E,0x65,0x00,0x00,0xB1,0x52,0x8F,0xFF,0x00, +0x1A,0x07,0x9E,0xEF,0x37,0x65,0x00,0x00,0x6E,0xFB,0x02,0xEF,0xCC,0x0A,0x00,0x00, +0x9E,0xEF,0x76,0x59,0x00,0x00,0x53,0xB1,0x52,0xA3,0x03,0x13,0x21,0xEF,0x15,0x03, +0x63,0x50,0x9E,0x43,0xA0,0x05,0x53,0xED,0x15,0x03,0x63,0x00,0x12,0xE9,0x9F,0xEF, +0x13,0x65,0x00,0x00,0xFB,0x01,0xEF,0xA1,0x0A,0x00,0x00,0x31,0x3B,0x01,0x9F,0xA3, +0x05,0xEF,0x15,0x03,0x63,0x7E,0x9F,0xEF,0xE6,0x64,0x00,0x00,0xFB,0x03,0xEF,0x89, +0x0A,0x00,0x00,0xC2,0x0C,0x5E,0xD4,0xAD,0xF8,0x11,0x40,0xDD,0xAC,0x08,0xDF,0xAD, +0xFC,0xC5,0x03,0xAD,0xF8,0x50,0xEF,0x50,0x03,0x63,0x7E,0xDD,0x54,0xFB,0x04,0xEF, +0x12,0x01,0x00,0x00,0xD5,0x50,0x13,0x04,0xD0,0x01,0x51,0x04,0xC0,0x51,0x54,0xC1, +0xAD,0xF8,0x01,0x50,0xED,0x12,0x03,0x63,0x50,0x13,0x0D,0x9F,0xEF,0xBE,0x64,0x00, +0x00,0xFB,0x01,0xEF,0x44,0x0A,0x00,0x00,0xD6,0xAD,0xF8,0xED,0x12,0x03,0x63,0xAD, +0xF8,0x1A,0xB8,0xB1,0x52,0x8F,0xFE,0xFF,0x13,0x36,0xB1,0x52,0x8F,0xFD,0xFF,0x12, +0x5C,0xDE,0x7E,0x7E,0xC9,0x8F,0x80,0x00,0x00,0x00,0xAC,0x08,0x7E,0xDD,0x54,0xFB, +0x03,0xEF,0xA4,0xF9,0xFF,0xFF,0xC0,0x04,0x54,0xD5,0x50,0x13,0x04,0xD0,0x02,0x51, +0x04,0x9F,0xEF,0x8F,0x64,0x00,0x00,0xFB,0x02,0xEF,0xFE,0x09,0x00,0x00,0x11,0x2D, +0xDE,0x7E,0x7E,0xC9,0x8F,0x40,0x00,0x00,0x00,0xAC,0x08,0x7E,0xDD,0x54,0xFB,0x03, +0xEF,0x75,0xF9,0xFF,0xFF,0xC0,0x02,0x54,0xD5,0x50,0x13,0x04,0xD0,0x02,0x51,0x04, +0x9F,0xEF,0x66,0x64,0x00,0x00,0xFB,0x02,0xEF,0xCF,0x09,0x00,0x00,0xDD,0x8F,0x8C, +0x00,0x00,0x00,0xFB,0x01,0xEF,0xC2,0x09,0x00,0x00,0xB1,0x52,0x8F,0x8F,0x00,0x13, +0x11,0xB1,0x52,0x8F,0xAF,0x00,0x13,0x0A,0xB1,0x52,0x8F,0xCF,0x00,0x13,0x03,0x31, +0x47,0x00,0xD0,0x54,0xAD,0xF4,0xC2,0x02,0x5E,0x9F,0x6E,0xC9,0x8F,0x40,0x00,0x00, +0x00,0xAC,0x08,0x7E,0xDD,0x54,0xFB,0x03,0xEF,0x1D,0xF9,0xFF,0xFF,0xD5,0x50,0x13, +0x01,0x04,0xDD,0xAC,0x08,0xDD,0x54,0xFB,0x02,0xEF,0xD5,0xF0,0xFF,0xFF,0x32,0x8E, +0x7E,0xC0,0xAD,0xF4,0x6E,0x9F,0xEF,0xE6,0x63,0x00,0x00,0xFB,0x02,0xEF,0x6A,0x09, +0x00,0x00,0xC0,0x02,0x54,0xF4,0xAD,0xFC,0xBD,0xD0,0x00,0x50,0xC3,0xAC,0x04,0x54, +0x51,0x04,0x00,0x00,0xB5,0x8F,0xFC,0x00,0xD0,0xAC,0x04,0x52,0xD4,0xBC,0x0C,0x8F, +0xAC,0x08,0x00,0x07,0x7C,0x00,0x7C,0x00,0x7C,0x00,0x7C,0x00,0x7C,0x00,0x11,0x00, +0x11,0x00,0x11,0x00,0x00,0xDD,0x00,0x9F,0x6E,0xCB,0x8F,0x00,0x00,0x00,0x80,0xAC, +0x08,0x50,0xC9,0x40,0xEF,0xDB,0x63,0x00,0x00,0xAC,0x10,0x7E,0xDD,0x52,0xFB,0x03, +0xEF,0xA5,0xF8,0xFF,0xFF,0xD0,0x8E,0x51,0xD5,0x50,0x13,0x03,0x31,0xEA,0x02,0x8F, +0xAC,0x08,0x05,0x02,0x07,0x00,0x0C,0x00,0x0F,0x00,0x00,0x98,0x51,0x51,0x11,0x03, +0x32,0x51,0x51,0x9A,0xAC,0x08,0x50,0xC0,0x40,0xEF,0x86,0x63,0x00,0x00,0x52,0xC1, +0x51,0x52,0x7E,0x9F,0xEF,0x81,0x64,0x00,0x00,0xFB,0x02,0xEF,0xDC,0x08,0x00,0x00, +0x9A,0xAC,0x08,0x50,0xD0,0x40,0xEF,0x69,0x63,0x00,0x00,0x51,0xD0,0x00,0x50,0x04, +0x9A,0x00,0x50,0x30,0xA7,0x02,0x9A,0x54,0x53,0x9E,0xEF,0x07,0x64,0x00,0x00,0x50, +0x91,0x53,0x60,0x1F,0x06,0x91,0x53,0xA0,0x01,0x1B,0x05,0xC0,0x04,0x50,0x11,0xF0, +0x9E,0xEF,0xF0,0x63,0x00,0x00,0x51,0x32,0xA0,0x02,0x50,0xC0,0x50,0x51,0x17,0x61, +0x90,0x53,0xBC,0x0C,0xDD,0x53,0x9F,0xEF,0x33,0x64,0x00,0x00,0xFB,0x02,0xEF,0x89, +0x08,0x00,0x00,0x31,0x5A,0x02,0xE0,0x1F,0xAC,0x08,0x1E,0xC8,0x8F,0x00,0x00,0x00, +0x80,0xAC,0x08,0xD6,0xAC,0x04,0xFA,0x6C,0xCF,0x1B,0xFF,0xCA,0x8F,0x00,0x00,0x00, +0x80,0xAC,0x08,0xD7,0xAC,0x04,0xC0,0x51,0x52,0xCB,0x8F,0xF0,0xFF,0xFF,0xFF,0x53, +0x50,0x9E,0xEF,0x2D,0x63,0x00,0x00,0x51,0xC1,0x51,0x40,0x61,0x7E,0x9F,0xEF,0xF4, +0x63,0x00,0x00,0xFB,0x02,0xEF,0x42,0x08,0x00,0x00,0x31,0x13,0x02,0xCB,0x8F,0xF0, +0xFF,0xFF,0xFF,0x53,0x50,0x9E,0xEF,0x09,0x63,0x00,0x00,0x51,0xC1,0x51,0x40,0x61, +0x7E,0x9F,0xEF,0xD5,0x63,0x00,0x00,0xFB,0x02,0xEF,0x1E,0x08,0x00,0x00,0x31,0xEF, +0x01,0xCB,0x8F,0xF0,0xFF,0xFF,0xFF,0x53,0x50,0x9E,0xEF,0xE5,0x62,0x00,0x00,0x51, +0xC1,0x51,0x40,0x61,0x7E,0x9F,0xEF,0xB4,0x63,0x00,0x00,0xFB,0x02,0xEF,0xFA,0x07, +0x00,0x00,0x31,0xCB,0x01,0xCB,0x8F,0xF0,0xFF,0xFF,0xFF,0x53,0x50,0x9E,0xEF,0xC1, +0x62,0x00,0x00,0x51,0xC1,0x51,0x40,0x61,0x7E,0x9F,0xEF,0x95,0x63,0x00,0x00,0xFB, +0x02,0xEF,0xD6,0x07,0x00,0x00,0x31,0xA7,0x01,0xCB,0x8F,0xF0,0xFF,0xFF,0xFF,0x53, +0x50,0x9E,0xEF,0x9D,0x62,0x00,0x00,0x51,0xC1,0x51,0x40,0x61,0x7E,0x9F,0xEF,0x77, +0x63,0x00,0x00,0xFB,0x02,0xEF,0xB2,0x07,0x00,0x00,0x31,0x83,0x01,0x9A,0xAC,0x08, +0x50,0x30,0x89,0x01,0x7D,0x56,0x7E,0x7D,0x54,0x7E,0x9F,0x6E,0x9A,0xAC,0x08,0x50, +0x9E,0xEF,0x5A,0x63,0x00,0x00,0x51,0xC1,0x51,0x40,0x61,0x7E,0xFB,0x02,0xEF,0x89, +0x07,0x00,0x00,0xD0,0x54,0xBC,0x0C,0x31,0x56,0x01,0xCB,0x8F,0xF0,0xFF,0xFF,0xFF, +0x53,0x50,0x9E,0xEF,0x4C,0x62,0x00,0x00,0x51,0xC1,0x51,0x40,0x61,0x7E,0x9F,0xEF, +0x63,0x63,0x00,0x00,0xFB,0x02,0xEF,0x61,0x07,0x00,0x00,0x31,0x32,0x01,0x9A,0x02, +0x50,0x30,0x39,0x01,0xDD,0x54,0x9F,0xEF,0x52,0x63,0x00,0x00,0xFB,0x02,0xEF,0x49, +0x07,0x00,0x00,0x31,0x1A,0x01,0x9A,0x00,0x50,0x30,0x21,0x01,0xCB,0x8F,0xF0,0xFF, +0xFF,0xFF,0x53,0x50,0x9E,0xEF,0x0A,0x62,0x00,0x00,0x51,0xC1,0x51,0x40,0x61,0x7E, +0xDD,0x54,0x9F,0xEF,0x2D,0x63,0x00,0x00,0xE1,0x04,0x53,0x07,0x9E,0xEF,0x2E,0x63, +0x00,0x00,0x6E,0xFB,0x03,0xEF,0x12,0x07,0x00,0x00,0x31,0xE3,0x00,0x9A,0x01,0x50, +0x30,0xEA,0x00,0xCB,0x8F,0xF0,0xFF,0xFF,0xFF,0x53,0x50,0x9E,0xEF,0xD3,0x61,0x00, +0x00,0x51,0xC1,0x51,0x40,0x61,0x7E,0xDD,0x54,0x9F,0xEF,0x0D,0x63,0x00,0x00,0xE1, +0x04,0x53,0x07,0x9E,0xEF,0x0E,0x63,0x00,0x00,0x6E,0xFB,0x03,0xEF,0xDB,0x06,0x00, +0x00,0x31,0xAC,0x00,0x9A,0x02,0x50,0x30,0xB3,0x00,0xCB,0x8F,0xF0,0xFF,0xFF,0xFF, +0x53,0x50,0x9E,0xEF,0x9C,0x61,0x00,0x00,0x51,0xC1,0x51,0x40,0x61,0x7E,0xDD,0x54, +0x9F,0xEF,0xED,0x62,0x00,0x00,0xE1,0x04,0x53,0x07,0x9E,0xEF,0xEE,0x62,0x00,0x00, +0x6E,0xFB,0x03,0xEF,0xA4,0x06,0x00,0x00,0x31,0x75,0x00,0x9A,0x00,0x50,0x30,0x7C, +0x00,0x98,0x54,0x54,0xC1,0x52,0x54,0x7E,0x9F,0xEF,0xDC,0x62,0x00,0x00,0xE1,0x04, +0x53,0x07,0x9E,0xEF,0xD9,0x62,0x00,0x00,0x6E,0xFB,0x03,0xEF,0x7C,0x06,0x00,0x00, +0x31,0x4D,0x00,0x9A,0x01,0x50,0x30,0x54,0x00,0x32,0x54,0x54,0xC1,0x52,0x54,0x7E, +0x9F,0xEF,0xC3,0x62,0x00,0x00,0xE1,0x04,0x53,0x07,0x9E,0xEF,0xC0,0x62,0x00,0x00, +0x6E,0xFB,0x03,0xEF,0x54,0x06,0x00,0x00,0x31,0x25,0x00,0x9A,0x02,0x50,0x30,0x2C, +0x00,0xC1,0x52,0x54,0x7E,0x9F,0xEF,0xAD,0x62,0x00,0x00,0xE1,0x04,0x53,0x07,0x9E, +0xEF,0xAA,0x62,0x00,0x00,0x6E,0xFB,0x03,0xEF,0x2F,0x06,0x00,0x00,0x31,0x00,0x00, +0xC3,0xAC,0x04,0x52,0x51,0xD0,0x00,0x50,0x04,0xD0,0x01,0x51,0x04,0xDD,0x50,0x7C, +0x7E,0x7C,0x7E,0x9F,0x6E,0xC9,0x40,0xEF,0xC8,0x60,0x00,0x00,0xAC,0x10,0x7E,0xDD, +0x52,0xFB,0x03,0xEF,0x92,0xF5,0xFF,0xFF,0xD5,0x50,0x12,0xDD,0x7D,0x8E,0x54,0x7D, +0x8E,0x56,0xD0,0x8E,0x51,0xC0,0x41,0xEF,0x88,0x60,0x00,0x00,0x52,0x05,0x00,0x00, +0xB5,0x8F,0x7C,0x00,0xD4,0x56,0xD0,0xAC,0x04,0x54,0x11,0x45,0xC2,0x08,0x5E,0x9A, +0x64,0x50,0x9B,0x40,0xA4,0x01,0x6E,0x9E,0x40,0xA4,0x02,0xAE,0x04,0xD0,0x5E,0x51, +0xD0,0xAC,0x08,0x50,0x10,0x40,0xC0,0x08,0x5E,0x8F,0x50,0x00,0x02,0x14,0x00,0x07, +0x00,0x0F,0x00,0x00,0xD0,0x01,0x56,0xD0,0x54,0x55,0x11,0x19,0xD6,0x56,0xD0,0x54, +0x55,0x9A,0x64,0x50,0x9E,0x40,0xA4,0x01,0x54,0x9A,0x64,0x50,0x9E,0x40,0xA4,0x01, +0x54,0xB5,0x64,0x12,0xB7,0xD5,0x56,0x13,0x09,0x9A,0x65,0x50,0x28,0x50,0xA5,0x01, +0xBC,0x0C,0xD0,0x56,0x50,0x04,0xBB,0x30,0xB1,0x60,0x61,0x14,0x37,0x7D,0x60,0x52, +0x7D,0x61,0x54,0xB5,0x52,0x13,0x27,0x9A,0x83,0x50,0xE0,0x50,0xEF,0x47,0x51,0x00, +0x00,0x03,0x82,0x20,0x50,0x9A,0x85,0x51,0xE0,0x51,0xEF,0x39,0x51,0x00,0x00,0x03, +0x82,0x20,0x51,0x91,0x50,0x51,0x12,0x0C,0xB7,0x54,0xB7,0x52,0x12,0xD5,0xB5,0x54, +0x13,0x06,0x11,0x09,0xD4,0x50,0x11,0x08,0xD0,0x01,0x50,0x11,0x03,0xD0,0x02,0x50, +0xBA,0x30,0x05,0x00,0xB5,0x8F,0x3C,0x04,0xC2,0x1C,0x5E,0xD0,0x5E,0x5A,0x2C,0x00, +0x9F,0x00,0x00,0x00,0x00,0x00,0x1C,0x6A,0xD4,0x52,0xD4,0x55,0xDD,0xAC,0x04,0xFB, +0x01,0xEF,0x91,0x01,0x00,0x00,0x90,0x00,0x42,0xBC,0x08,0xDD,0xAC,0x10,0xDD,0x5A, +0xFB,0x02,0xEF,0x97,0x08,0x00,0x00,0xD1,0x51,0x2D,0x12,0x04,0xD0,0x51,0x50,0x04, +0x9E,0xEF,0x97,0x61,0x00,0x00,0x51,0xB1,0x50,0xC1,0x00,0x00,0x1F,0x18,0xB1,0x50, +0xC1,0x02,0x00,0x1A,0x11,0x32,0xC1,0x04,0x00,0x51,0x9E,0xEF,0x7D,0x61,0x00,0x00, +0x53,0xC0,0x53,0x51,0x17,0x61,0xC0,0x8F,0x06,0x00,0x00,0x00,0x51,0x11,0xD8,0xD5, +0x52,0x13,0x0A,0xD7,0x52,0x9A,0x42,0xBC,0x08,0x50,0x30,0xAE,0x00,0x31,0xA6,0xFF, +0xD4,0x55,0xDD,0x8F,0x8C,0x00,0x00,0x00,0xFB,0x01,0xEF,0xAD,0x04,0x00,0x00,0xD0, +0x00,0x50,0xD1,0x52,0xAC,0x0C,0x19,0x03,0xD0,0x24,0x50,0x04,0xC3,0x01,0xAC,0x0C, +0x51,0xD1,0x52,0x51,0x18,0x0D,0x90,0x20,0x42,0xBC,0x08,0xD6,0x52,0x9A,0x20,0x50, +0x30,0xC0,0x00,0xD4,0x55,0x31,0x6E,0xFF,0xDD,0x8F,0x8C,0x00,0x00,0x00,0xFB,0x01, +0xEF,0x77,0x04,0x00,0x00,0xDD,0xAC,0x04,0xFB,0x01,0xEF,0xE8,0x00,0x00,0x00,0xDD, +0xAC,0x08,0x9F,0xEF,0x0C,0x00,0x00,0x00,0xFB,0x02,0xEF,0x5D,0x04,0x00,0x00,0xD4, +0x55,0x31,0x42,0xFF,0x25,0x73,0x00,0x9F,0xEF,0x18,0x00,0x00,0x00,0xFB,0x01,0xEF, +0x48,0x04,0x00,0x00,0xD4,0x52,0xDD,0xAC,0x04,0xFB,0x01,0xEF,0xB7,0x00,0x00,0x00, +0xD4,0x55,0x31,0x21,0xFF,0x5E,0x55,0x0D,0x0A,0x00,0xC3,0x01,0xAC,0x0C,0x51,0xD1, +0x52,0x51,0x18,0x0F,0x90,0xAA,0x05,0x42,0xBC,0x08,0xD6,0x52,0x9A,0xAA,0x05,0x50, +0x30,0x50,0x00,0xD4,0x55,0x31,0xFE,0xFE,0x31,0xFB,0xFE,0xD4,0x51,0xE1,0x50,0xEF, +0x88,0x50,0x00,0x00,0x03,0x88,0x01,0x51,0xE1,0x09,0x9F,0x00,0x04,0x14,0x20,0x03, +0x88,0x02,0x51,0xE9,0x55,0x03,0x88,0x04,0x51,0xE0,0x50,0xEF,0x6C,0x50,0x00,0x00, +0x04,0x80,0x8F,0x40,0x50,0xDD,0x50,0x9F,0xEF,0x5B,0x60,0x00,0x00,0x98,0x41,0xEF, +0x54,0x60,0x00,0x00,0x50,0xC0,0x50,0x6E,0xFB,0x02,0xEF,0xCD,0x03,0x00,0x00,0xD0, +0x01,0x55,0x05,0xE0,0x09,0x9F,0x00,0x04,0x14,0x20,0x15,0xE9,0x55,0x12,0xDD,0x50, +0x9F,0xEF,0x55,0x60,0x00,0x00,0xFB,0x01,0xEF,0xAF,0x03,0x00,0x00,0xD0,0x8E,0x50, +0xE1,0x50,0xEF,0x25,0x50,0x00,0x00,0x04,0x30,0x8C,0x01,0x05,0xC1,0x50,0x8F,0x40, +0x00,0x00,0x00,0x7E,0xD0,0x8F,0x5E,0x00,0x00,0x00,0x50,0x30,0x79,0x01,0xD0,0x8E, +0x50,0x30,0x73,0x01,0x05,0xB5,0x8F,0x00,0x00,0xD1,0xAC,0x04,0x8F,0x00,0x02,0x00, +0x00,0x1E,0x08,0xFA,0x6C,0xEF,0x72,0x03,0x00,0x00,0x04,0xDD,0xAC,0x04,0x9F,0xEF, +0x4B,0x60,0x00,0x00,0xFB,0x02,0xEF,0x61,0x03,0x00,0x00,0x04,0xD0,0x8F,0xA5,0x04, +0x14,0x20,0x50,0x94,0x60,0x88,0x02,0x60,0x94,0xA0,0x02,0x94,0xA0,0x03,0x94,0xA0, +0x04,0xD4,0xA0,0x09,0xDD,0x5B,0xD0,0x8F,0x2D,0x04,0x14,0x20,0x5B,0x16,0xBB,0x28, +0xD0,0x8E,0x5B,0xD0,0x00,0x50,0x05,0x30,0x07,0x00,0x30,0xCF,0xFF,0xD0,0x00,0x50, +0x05,0xDD,0x5B,0xD0,0x8F,0x2D,0x04,0x14,0x20,0x5B,0x9E,0xEF,0x14,0x02,0x00,0x00, +0xAB,0x20,0x9E,0xEF,0x15,0x02,0x00,0x00,0xAB,0x24,0x9E,0xEF,0xF0,0x01,0x00,0x00, +0xAB,0x64,0x9E,0xEF,0xF1,0x01,0x00,0x00,0xAB,0x6C,0x9E,0xEF,0x1E,0x00,0x00,0x00, +0xAB,0x28,0x9E,0xEF,0x16,0x00,0x00,0x00,0xAB,0x2C,0xD0,0x8F,0x00,0x04,0x14,0x20, +0x50,0xF0,0x00,0x0D,0x03,0x60,0xAA,0x8F,0x00,0x06,0x60,0xD0,0x8E,0x5B,0x05,0x05, +0xBB,0x0A,0xD0,0x8F,0xA5,0x04,0x14,0x20,0x53,0x95,0xA3,0x04,0x13,0x19,0x97,0xA3, +0x04,0x9A,0xA3,0x02,0x51,0x9A,0x41,0xA3,0x05,0x50,0x96,0x51,0x8B,0x8F,0xFC,0x51, +0xA3,0x02,0xBA,0x0A,0xD5,0x50,0x05,0x30,0x0E,0xE6,0x30,0x93,0x01,0x12,0x16,0x30, +0x19,0xE6,0xD1,0x50,0x8F,0x00,0x87,0x93,0x03,0x19,0xEF,0xD0,0x8F,0x00,0x00,0x00, +0x80,0x50,0xBA,0x0A,0x05,0x30,0x81,0x01,0x9A,0x50,0x50,0xBA,0x0A,0x05,0xBB,0x0C, +0x78,0x04,0x50,0x52,0x78,0x08,0x50,0x53,0xC0,0x53,0x52,0x78,0x09,0x50,0x53,0xC0, +0x53,0x52,0x78,0x0A,0x50,0x53,0xC0,0x53,0x52,0x78,0x0D,0x50,0x53,0xC0,0x53,0x52, +0xD0,0x8F,0xA5,0x04,0x14,0x20,0x53,0x8A,0x01,0x63,0xD5,0x52,0x13,0x03,0x30,0xB7, +0xE5,0x30,0x77,0x00,0xE1,0x03,0x63,0x0C,0xE1,0x02,0x63,0x08,0x90,0x0D,0x50,0xD0, +0x00,0x51,0x11,0x30,0x95,0xA3,0x04,0x12,0x14,0xD5,0x52,0x13,0xE4,0x30,0xAB,0xE5, +0xD1,0x50,0x52,0x19,0xDC,0xD0,0x18,0x50,0xD0,0x2D,0x51,0x11,0x17,0x97,0xA3,0x04, +0x9A,0xA3,0x02,0x51,0x9A,0x41,0xA3,0x05,0x50,0x96,0x51,0x8B,0x8F,0xFC,0x51,0xA3, +0x02,0xD0,0x00,0x51,0xBA,0x0C,0x05,0xBB,0x8F,0x0C,0x08,0xD0,0x50,0x52,0xD0,0x8F, +0xA5,0x04,0x14,0x20,0x53,0xD0,0x8F,0x2D,0x04,0x14,0x20,0x5B,0x30,0x1C,0x00,0xE1, +0x01,0x63,0xF9,0xE0,0x00,0x63,0x0F,0x30,0x11,0x00,0x16,0xBB,0x20,0xE9,0x50,0xF7, +0xD0,0x52,0x51,0x16,0xBB,0x24,0xBA,0x8F,0x0C,0x08,0x05,0xBB,0x8F,0x0C,0x08,0xD0, +0x8F,0xA5,0x04,0x14,0x20,0x53,0xED,0x0D,0x03,0x9F,0x00,0x04,0x14,0x20,0x00,0x13, +0x15,0x30,0xAC,0x00,0xE9,0x50,0x0F,0x30,0xAF,0x00,0xE1,0x0B,0x50,0x08,0x30,0x90, +0xFE,0x90,0x12,0x50,0x11,0x77,0xD0,0x8F,0x2D,0x04,0x14,0x20,0x5B,0x16,0xBB,0x64, +0xE9,0x50,0x28,0x16,0xBB,0x6C,0x91,0x50,0x11,0x12,0x05,0x88,0x02,0x63,0x11,0x1B, +0x91,0x50,0x13,0x12,0x05,0x8A,0x02,0x63,0x11,0x11,0xE1,0x03,0x63,0x0F,0x91,0x50, +0x07,0x13,0x05,0x91,0x50,0x14,0x12,0x05,0x88,0x04,0x63,0x11,0x5E,0x91,0x50,0x0F, +0x12,0x1E,0x8C,0x01,0x63,0xE1,0x00,0x63,0x52,0x9E,0xEF,0x55,0x5E,0x00,0x00,0x52, +0x16,0xBB,0x20,0xE9,0x50,0xFA,0x9A,0x82,0x51,0x13,0x40,0x16,0xBB,0x24,0x11,0xF0, +0x91,0x50,0x03,0x12,0x18,0x8A,0x01,0x63,0x94,0xA3,0x04,0x94,0xA3,0x03,0x94,0xA3, +0x02,0xD5,0xA3,0x09,0x13,0x07,0xD0,0xA3,0x09,0x5E,0xDD,0x6E,0x05,0x91,0xA3,0x04, +0x04,0x18,0x18,0x9A,0xA3,0x03,0x51,0x8A,0x8F,0xFC,0x51,0x90,0x50,0x41,0xA3,0x05, +0x96,0x51,0x8B,0x8F,0xFC,0x51,0xA3,0x03,0x96,0xA3,0x04,0xBA,0x8F,0x0C,0x08,0x05, +0xDB,0x20,0x50,0xEF,0x07,0x01,0x50,0x50,0x05,0xDB,0x21,0x50,0xE1,0x0B,0x50,0x03, +0x90,0x03,0x50,0x05,0xDB,0x22,0x50,0xEF,0x07,0x01,0x50,0x50,0x05,0xDA,0x51,0x23, +0x05,0x00,0x00,0x00,0xD4,0x58,0xD4,0x59,0xD4,0x5A,0x9A,0x8B,0x50,0x91,0x50,0x25, +0x13,0x01,0x05,0xE5,0x03,0x58,0x00,0x91,0x6B,0x2D,0x12,0x05,0xC8,0x08,0x58,0xD6, +0x5B,0xCA,0x03,0x58,0x91,0x6B,0x2A,0x12,0x14,0xD0,0x8C,0x5A,0xC8,0x02,0x58,0xD6, +0x5B,0xD5,0x5A,0x18,0x06,0xCE,0x5A,0x5A,0xC8,0x08,0x58,0x11,0x30,0xD4,0x5A,0xCA, +0x04,0x58,0x91,0x6B,0x30,0x12,0x03,0xC8,0x04,0x58,0x90,0x6B,0x50,0xE1,0x50,0xEF, +0xC4,0x4C,0x00,0x00,0x18,0x78,0x01,0x5A,0x51,0x78,0x03,0x5A,0x5A,0xC0,0x51,0x5A, +0x82,0x30,0x50,0xC0,0x50,0x5A,0xD6,0x5B,0xC8,0x02,0x58,0x11,0xDD,0x91,0x2E,0x6B, +0x12,0x02,0xD6,0x5B,0x91,0x2A,0x6B,0x12,0x0F,0xD0,0x8C,0x59,0x18,0x03,0xCE,0x59, +0x59,0xD6,0x5B,0xC8,0x01,0x58,0x11,0x25,0xD4,0x59,0x90,0x6B,0x50,0xE1,0x50,0xEF, +0x84,0x4C,0x00,0x00,0x18,0x78,0x01,0x59,0x51,0x78,0x03,0x59,0x59,0xC0,0x51,0x59, +0x82,0x30,0x50,0xC0,0x50,0x59,0xD6,0x5B,0xC8,0x01,0x58,0x11,0xDD,0xD1,0x59,0x5A, +0x19,0x03,0xD0,0x59,0x5A,0xC8,0x10,0x58,0x90,0x6B,0x50,0xE0,0x50,0xEF,0x16,0x4C, +0x00,0x00,0x03,0xCA,0x10,0x58,0x9A,0x8B,0x50,0xE1,0x50,0xEF,0x08,0x4C,0x00,0x00, +0x03,0x80,0x20,0x50,0x91,0x50,0x8F,0x72,0x12,0x07,0x78,0x08,0x50,0x50,0x90,0x8B, +0x50,0xDE,0xEF,0x05,0x5D,0x00,0x00,0x51,0xB1,0x50,0x61,0x13,0x08,0xC0,0x04,0x51, +0xD5,0x61,0x12,0xF4,0x05,0x3C,0xA1,0x02,0x50,0x05,0xB5,0x8F,0xFC,0x0F,0xD0,0xAC, +0x04,0x50,0xD1,0x50,0x8F,0x00,0x02,0x00,0x00,0x1E,0x08,0x30,0x18,0x03,0xD0,0x50, +0x5B,0x11,0x03,0xD0,0x50,0x5B,0xC0,0x08,0x5C,0x30,0xE8,0xFE,0xE1,0x0F,0x50,0x2A, +0xAF,0x50,0x8F,0x01,0x80,0x8F,0x11,0x80,0x2D,0x00,0x36,0x00,0x3F,0x00,0x48,0x00, +0x51,0x00,0x51,0x00,0x5A,0x00,0x60,0x00,0x66,0x00,0x6B,0x00,0x70,0x00,0x75,0x00, +0x7A,0x00,0x88,0x00,0x8E,0x00,0x94,0x00,0x9A,0x00,0x95,0x50,0x12,0x01,0x04,0x30, +0x95,0xFD,0x31,0xC4,0xFF,0xD0,0x8C,0x50,0x30,0x8C,0xFD,0x31,0xBB,0xFF,0xD0,0x02, +0x50,0x30,0x8D,0x01,0x31,0xB2,0xFF,0xD0,0x10,0x50,0x30,0x84,0x01,0x31,0xA9,0xFF, +0xD0,0x08,0x50,0x30,0x7B,0x01,0x31,0xA0,0xFF,0xD0,0x0A,0x50,0x30,0x72,0x01,0x31, +0x97,0xFF,0x30,0x43,0x00,0x31,0x91,0xFF,0x30,0x59,0x01,0x31,0x8B,0xFF,0xD0,0x01, +0x50,0x11,0x14,0xD0,0x02,0x50,0x11,0x0F,0xD0,0x04,0x50,0x11,0x0A,0xD0,0x08,0x50, +0x11,0x05,0xD0,0x10,0x50,0x11,0x00,0xC8,0x10,0x58,0x30,0x24,0x02,0x31,0x69,0xFF, +0x30,0x5F,0x02,0x31,0x63,0xFF,0x30,0x59,0x02,0x31,0x5D,0xFF,0x30,0x53,0x02,0x31, +0x57,0xFF,0x30,0x4D,0x02,0x31,0x51,0xFF,0xD0,0x8C,0x57,0xDD,0x57,0xFB,0x01,0xEF, +0x87,0xE4,0xFF,0xFF,0xD0,0x50,0x56,0xEF,0x00,0x04,0x58,0x50,0x8F,0x50,0x00,0x0F, +0x20,0x00,0x2A,0x00,0x47,0x00,0x64,0x00,0x20,0x00,0x2A,0x00,0x47,0x00,0x64,0x00, +0x20,0x00,0x83,0x00,0xA3,0x00,0xBF,0x00,0x20,0x00,0x83,0x00,0xA3,0x00,0xBF,0x00, +0xD0,0x56,0x53,0xD4,0x54,0xD4,0x55,0x31,0xBC,0x00,0xD0,0x56,0x50,0xD0,0x59,0x51, +0x30,0x67,0x02,0xD0,0x50,0x53,0xC3,0x53,0x59,0x50,0xD4,0x51,0x30,0x64,0x02,0xD0, +0x50,0x54,0xD4,0x55,0x31,0x9F,0x00,0xD0,0x56,0x50,0xD0,0x5A,0x51,0x30,0x4A,0x02, +0xD0,0x50,0x53,0xC3,0x53,0x5A,0x50,0xD4,0x51,0x30,0x47,0x02,0xD0,0x50,0x54,0xD4, +0x55,0x31,0x82,0x00,0x7D,0x59,0x50,0x30,0x30,0x02,0xD0,0x56,0x51,0x30,0x2A,0x02, +0xD0,0x50,0x53,0xC3,0x53,0x5A,0x50,0xD4,0x51,0x30,0x27,0x02,0xD0,0x50,0x54,0xD4, +0x55,0x11,0x63,0xD0,0x56,0x50,0xD0,0x59,0xEF,0x33,0xC6,0xFB,0xDF,0x30,0x0A,0x02, +0xD0,0x50,0x53,0xD4,0x54,0xC3,0x53,0x59,0x50,0xD4,0x51,0x30,0x05,0x02,0xD0,0x50, +0x55,0x11,0x43,0xD0,0x56,0x50,0xD0,0x5A,0x51,0x30,0xEE,0x01,0xD0,0x50,0x53,0xD4, +0x54,0xC3,0x53,0x5A,0x50,0xD4,0x51,0x30,0xE9,0x01,0xD0,0x50,0x55,0x11,0x27,0x7D, +0x59,0x50,0x30,0xD5,0x01,0xD0,0x56,0x51,0x30,0xCF,0x01,0xD0,0x50,0x53,0xC3,0x53, +0x59,0x50,0xD4,0x51,0x30,0xCC,0x01,0xD0,0x50,0x54,0xC3,0x59,0x5A,0x50,0xD4,0x51, +0x30,0xC0,0x01,0xD0,0x50,0x55,0x90,0x20,0x50,0xD0,0x54,0x51,0x30,0x44,0x01,0x11, +0x06,0x9A,0x87,0x50,0x30,0x10,0xFC,0xF4,0x53,0xF7,0x90,0x20,0x50,0xD0,0x55,0x51, +0x30,0x30,0x01,0x05,0xD0,0x8C,0x50,0x3C,0x60,0x56,0xD0,0xA0,0x04,0x57,0x31,0xE6, +0xFE,0xBB,0x8F,0xFC,0x00,0x7C,0x56,0x9E,0xEF,0x37,0x5B,0x00,0x00,0x53,0xE0,0x04, +0x58,0x07,0x9E,0xEF,0x3C,0x5B,0x00,0x00,0x53,0xD0,0x8C,0x56,0xD4,0x54,0x7B,0x50, +0x56,0x56,0x52,0x90,0x42,0x63,0x7E,0xD6,0x54,0xD5,0x56,0x12,0xF1,0xD4,0x55,0xD4, +0x56,0xD4,0x57,0xEF,0x00,0x04,0x58,0x50,0x8F,0x50,0x00,0x0F,0x20,0x00,0x20,0x00, +0x20,0x00,0x22,0x00,0x20,0x00,0x28,0x00,0x28,0x00,0x2E,0x00,0x20,0x00,0x38,0x00, +0x38,0x00,0x38,0x00,0x20,0x00,0x3E,0x00,0x3E,0x00,0x44,0x00,0x11,0x2C,0xC3,0x54, +0x5A,0x55,0x11,0x26,0xC3,0x54,0x5A,0x57,0x11,0x20,0xC3,0x59,0x5A,0x55,0xC3,0x54, +0x59,0x57,0x11,0x16,0xC3,0x54,0x5A,0x56,0x11,0x10,0xC3,0x54,0x5A,0x57,0x11,0x0A, +0xC3,0x54,0x59,0x57,0xC3,0x59,0x5A,0x56,0x11,0x00,0x90,0x20,0x50,0xD0,0x55,0x51, +0x30,0x90,0x00,0x90,0x30,0x50,0xD0,0x57,0x51,0x30,0x87,0x00,0x9A,0x8E,0x50,0x30, +0x55,0xFB,0xF5,0x54,0xF7,0x90,0x20,0x50,0xD0,0x56,0x51,0x30,0x75,0x00,0xBA,0x8F, +0xFC,0x00,0x05,0xE0,0x03,0x58,0x12,0xE1,0x01,0x58,0x0E,0xE1,0x00,0x58,0x0A,0x90, +0x20,0x50,0xC3,0x59,0x5A,0x51,0x30,0x5A,0x00,0x05,0xE1,0x01,0x58,0x12,0xE1,0x00, +0x58,0x0E,0xE1,0x03,0x58,0x0A,0x90,0x20,0x50,0xC3,0x59,0x5A,0x51,0x30,0x43,0x00, +0x05,0xBB,0x3C,0xC3,0x01,0x50,0x52,0x9E,0xEF,0x57,0x5A,0x00,0x00,0x53,0xE0,0x04, +0x58,0x07,0x9E,0xEF,0x5C,0x5A,0x00,0x00,0x53,0xD0,0x8C,0x54,0xC0,0x52,0x54,0xD4, +0x55,0xEF,0x04,0x04,0x64,0x50,0x9A,0x40,0x63,0x50,0x30,0xEA,0xFA,0xEF,0x00,0x04, +0x64,0x50,0x9A,0x40,0x63,0x50,0x30,0xDE,0xFA,0xD7,0x54,0xF3,0x52,0x55,0xE2,0xBA, +0x3C,0x05,0x05,0xBB,0x0C,0x7D,0x50,0x52,0x11,0x06,0xD0,0x52,0x50,0x30,0xC7,0xFA, +0xF4,0x53,0xF7,0xBA,0x0C,0x05,0xDD,0x52,0xD0,0x50,0x52,0xD1,0x52,0x8F,0x80,0x00, +0x00,0x00,0x1E,0x26,0xD0,0x3F,0x50,0x30,0xAD,0xFA,0xEF,0x04,0x04,0x52,0x50,0x9A, +0x40,0xEF,0xEE,0x59,0x00,0x00,0x50,0x30,0x9D,0xFA,0xEF,0x00,0x04,0x52,0x50,0x9A, +0x40,0xEF,0xDE,0x59,0x00,0x00,0x50,0x30,0x8D,0xFA,0xD0,0x52,0x50,0x30,0x68,0xDB, +0xD0,0x50,0x52,0x95,0x62,0x12,0x0C,0xD0,0x0D,0x50,0x30,0x7A,0xFA,0xD0,0x0A,0x50, +0x30,0x74,0xFA,0xD0,0x52,0x50,0xD0,0x8E,0x52,0x05,0xD1,0x50,0x51,0x19,0x03,0xD0, +0x51,0x50,0x05,0xD1,0x50,0x51,0x18,0x03,0xD0,0x51,0x50,0x05,0xB5,0x8F,0x3C,0x04, +0xD0,0xAC,0x04,0x5A,0xD0,0xAC,0x08,0x50,0x30,0xD3,0xF9,0xD1,0x51,0x2D,0x12,0x01, +0x04,0x90,0x50,0xAA,0x05,0x30,0x33,0x01,0x8F,0xAA,0x01,0x00,0x0D,0x1D,0x00,0x25, +0x00,0x32,0x00,0x41,0x00,0x47,0x00,0x4D,0x00,0x53,0x00,0x5F,0x00,0x65,0x00,0x6B, +0x00,0x76,0x00,0x7E,0x00,0x8A,0x00,0x95,0x00,0x00,0x9A,0xAA,0x05,0x50,0xD0,0x00, +0x51,0x04,0x9A,0xAA,0x05,0x50,0x90,0xAA,0x02,0xAA,0x01,0xD0,0x00,0x51,0x04,0x9A, +0xAA,0x05,0x50,0x80,0x8F,0x40,0x50,0x90,0x00,0xAA,0x01,0x31,0xB3,0xFF,0x30,0x59, +0x00,0x31,0xA0,0xFF,0x30,0x8D,0x00,0x31,0x9A,0xFF,0x30,0x70,0x00,0x31,0x94,0xFF, +0x9A,0x8F,0x9B,0x50,0x90,0x00,0xAA,0x01,0xD0,0x00,0x51,0x04,0x30,0x3B,0x00,0x31, +0x82,0xFF,0x30,0x58,0x00,0x31,0x7C,0xFF,0x9A,0x1B,0x50,0x90,0x00,0xAA,0x01,0xD0, +0x00,0x51,0x04,0x90,0xAA,0x05,0xAA,0x08,0x31,0x69,0xFF,0x9A,0xAA,0x08,0x50,0x90, +0x00,0xAA,0x01,0xD0,0x00,0x51,0x04,0x88,0x04,0x6A,0x90,0xAA,0x02,0xAA,0x01,0x31, +0x52,0xFF,0x90,0x00,0xAA,0x01,0xD0,0x00,0x51,0x04,0x8A,0x07,0x6A,0x94,0xAA,0x07, +0x94,0xAA,0x18,0x94,0xAA,0x06,0x2C,0x00,0x9F,0x00,0x00,0x00,0x00,0x00,0x10,0xAA, +0x08,0x2C,0x00,0x9F,0x00,0x00,0x00,0x00,0x00,0x03,0xAA,0x19,0x05,0x9A,0xAA,0x18, +0x50,0x91,0x50,0x03,0x19,0x04,0x88,0x01,0x6A,0x05,0x90,0xAA,0x05,0x40,0xAA,0x19, +0x96,0xAA,0x18,0x05,0xE0,0x02,0x6A,0x04,0xE1,0x01,0x6A,0x01,0x05,0x9A,0xAA,0x05, +0x50,0x8F,0x50,0x30,0x0F,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20, +0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x32,0x00,0x33,0x00,0x40,0x00,0x40, +0x00,0x41,0x00,0x41,0x00,0x9A,0xAA,0x07,0x52,0xA4,0x0A,0x42,0xAA,0x08,0x82,0x30, +0x50,0xA0,0x50,0x42,0xAA,0x08,0x05,0x05,0x96,0xAA,0x07,0x91,0xAA,0x07,0x08,0x19, +0x03,0x88,0x02,0x6A,0x05,0x05,0x90,0x50,0xAA,0x06,0x05,0x9E,0xEF,0x73,0x58,0x00, +0x00,0x51,0x91,0xC1,0x00,0x00,0xAA,0x01,0x12,0x1D,0x91,0xAA,0x05,0xC1,0x02,0x00, +0x1F,0x15,0x91,0xAA,0x05,0xC1,0x03,0x00,0x1A,0x0D,0x78,0x08,0xAA,0x01,0xAA,0x01, +0x90,0xC1,0x01,0x00,0xAA,0x01,0x05,0xC0,0x8F,0x04,0x00,0x00,0x00,0x51,0xD1,0x8F, +0xFF,0xFF,0xFF,0xFF,0x61,0x12,0xCB,0x00,0xB5,0x8F,0xFC,0x00,0xD0,0x8F,0x00,0x04, +0x14,0x20,0x57,0xE5,0x09,0x67,0x00,0xE5,0x0A,0x67,0x00,0xC2,0x1C,0x5E,0xD0,0x5E, +0x56,0x2C,0x00,0x9F,0x00,0x00,0x00,0x00,0x00,0x1C,0x66,0x8F,0x9F,0x05,0x00,0x04, +0x20,0x01,0x02,0x10,0x00,0x07,0x00,0x19,0x00,0x00,0xD0,0x8F,0x24,0xBC,0x01,0x00, +0x50,0x11,0x10,0xD0,0x8F,0x7C,0x73,0x01,0x00,0x50,0x11,0x07,0xD0,0x8F,0x88,0xEE, +0x01,0x00,0x50,0xF5,0x50,0xFD,0xDF,0xEF,0xD0,0x58,0x00,0x00,0xFB,0x01,0xEF,0x89, +0xFA,0xFF,0xFF,0xDD,0x8F,0xC8,0x00,0x00,0x00,0xDD,0x56,0xFB,0x02,0xCF,0x0E,0xFE, +0x91,0x8F,0x9B,0x50,0x12,0x43,0x91,0x3F,0xA6,0x06,0x12,0x3D,0x91,0x8F,0x63,0xA6, +0x05,0x12,0x36,0x3C,0xA6,0x08,0x50,0xD1,0x50,0x8F,0x80,0x00,0x00,0x00,0x1E,0x18, +0xE1,0x50,0xEF,0x75,0x58,0x00,0x00,0x04,0xE2,0x09,0x67,0x00,0xE1,0x50,0xEF,0x79, +0x58,0x00,0x00,0x04,0xE2,0x0A,0x67,0x00,0xE0,0x0A,0x67,0x09,0xF0,0x03,0x04,0x04, +0x67,0x90,0x00,0xA7,0x02,0xD0,0x00,0x50,0x04,0xD0,0x2D,0x50,0x04,0x00,0x00,0x00, +0xD2,0x0D,0x9F,0x30,0x00,0x14,0x20,0xE1,0x0F,0x9F,0x00,0x00,0x08,0x20,0xF8,0xD2, +0x0E,0x9F,0x30,0x00,0x14,0x20,0x2C,0x00,0x9F,0x00,0x00,0x00,0x00,0x00,0x8F,0xA4, +0x00,0x9F,0x58,0x07,0x14,0x20,0xF0,0x03,0x00,0x02,0x9F,0x58,0x07,0x14,0x20,0xE1, +0x1F,0x9F,0x10,0x00,0x14,0x20,0x26,0xF0,0x02,0x00,0x02,0x9F,0x58,0x07,0x14,0x20, +0xD0,0x8F,0x00,0x04,0x14,0x20,0x5B,0x2C,0x00,0x9F,0x00,0x00,0x00,0x00,0x00,0x12, +0xAB,0x14,0xD4,0xAB,0x26,0xE2,0x0C,0x9F,0x00,0x04,0x14,0x20,0x00,0xC8,0x8F,0x00, +0x40,0x00,0x00,0x9F,0x00,0x00,0x08,0x20,0xE5,0x02,0x9F,0x00,0x04,0x14,0x20,0x00, +0xE5,0x03,0x9F,0x00,0x04,0x14,0x20,0x00,0xE5,0x0B,0x9F,0x00,0x04,0x14,0x20,0x00, +0xE5,0x1C,0x9F,0x00,0x04,0x14,0x20,0x00,0xF0,0x00,0x00,0x02,0x9F,0x00,0x04,0x14, +0x20,0xE5,0x09,0x9F,0x00,0x04,0x14,0x20,0x00,0xE5,0x0A,0x9F,0x00,0x04,0x14,0x20, +0x00,0xF0,0x00,0x0D,0x03,0x9F,0x00,0x04,0x14,0x20,0x30,0xB9,0xE1,0xD0,0x9F,0x04, +0x40,0x08,0x20,0x50,0xED,0x00,0x02,0x50,0x02,0x13,0x03,0x31,0x27,0x01,0xD0,0x8F, +0x58,0x07,0x14,0x20,0x59,0x90,0x01,0xA9,0x76,0xD2,0x06,0x9F,0x30,0x00,0x14,0x20, +0xD0,0x9F,0x04,0x40,0x08,0x20,0x55,0xEF,0x04,0x03,0x55,0x55,0xD0,0x9F,0x10,0x00, +0x14,0x20,0x51,0xF0,0x55,0x08,0x03,0x51,0xF0,0x55,0x0C,0x03,0x51,0xD0,0x51,0x9F, +0x10,0x00,0x14,0x20,0x9E,0xEF,0x96,0x57,0x00,0x00,0x52,0xC0,0x45,0xEF,0x8E,0x57, +0x00,0x00,0x52,0xD0,0x07,0x53,0x90,0x02,0xA9,0x76,0xDB,0x22,0x50,0xE0,0x07,0x50, +0x03,0x31,0xD5,0x00,0x9A,0x62,0x50,0xDA,0x50,0x23,0xD0,0x45,0xEF,0x4F,0x57,0x00, +0x00,0x51,0x8F,0x9F,0x05,0x00,0x04,0x20,0x01,0x02,0x10,0x00,0x07,0x00,0x19,0x00, +0x00,0xD0,0x8F,0x7B,0x01,0x00,0x00,0x50,0x11,0x10,0xD0,0x8F,0x3D,0x01,0x00,0x00, +0x50,0x11,0x07,0xD0,0x8F,0xA6,0x01,0x00,0x00,0x50,0xF5,0x50,0xFD,0xF4,0x51,0xD2, +0x90,0x03,0xA9,0x76,0xDB,0x20,0x50,0xE0,0x07,0x50,0x03,0x31,0x8B,0x00,0x90,0x04, +0xA9,0x76,0xDB,0x21,0x50,0xD3,0x8F,0x00,0xE8,0x00,0x00,0x50,0x13,0x03,0x31,0x78, +0x00,0x90,0x05,0xA9,0x76,0x91,0x50,0x62,0x13,0x02,0x11,0x6D,0xD6,0x52,0xF5,0x53, +0x85,0x8F,0x9F,0x05,0x00,0x04,0x20,0x01,0x02,0x10,0x00,0x07,0x00,0x19,0x00,0x00, +0xD0,0x8F,0x68,0x59,0x11,0x00,0x50,0x11,0x10,0xD0,0x8F,0xD8,0x82,0x0E,0x00,0x50, +0x11,0x07,0xD0,0x8F,0x50,0x51,0x13,0x00,0x50,0xF5,0x50,0xFD,0x90,0x06,0xA9,0x76, +0xD2,0x03,0x9F,0x30,0x00,0x14,0x20,0x8F,0x9F,0x05,0x00,0x04,0x20,0x01,0x02,0x10, +0x00,0x07,0x00,0x19,0x00,0x00,0xD0,0x8F,0x58,0xEA,0x1C,0x00,0x50,0x11,0x10,0xD0, +0x8F,0x68,0x2F,0x18,0x00,0x50,0x11,0x07,0xD0,0x8F,0x30,0x32,0x20,0x00,0x50,0xF5, +0x50,0xFD,0x31,0xC8,0xFE,0xD0,0x00,0x50,0x05,0x17,0x9F,0x1F,0x40,0x06,0x20,0xD2, +0x06,0x9F,0x30,0x00,0x14,0x20,0x9A,0x9F,0xCE,0x07,0x14,0x20,0x58,0x11,0xF0,0x00, +0xB5,0x8F,0xFC,0x0F,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0xE2,0x0B,0x9F,0x00,0x04, +0x14,0x20,0x00,0x90,0x00,0x9F,0x61,0x07,0x14,0x20,0x94,0x9F,0xA6,0x04,0x14,0x20, +0x90,0xAC,0x04,0x9F,0xCC,0x07,0x14,0x20,0x90,0xAC,0x08,0x9F,0xCD,0x07,0x14,0x20, +0x90,0xAC,0x0C,0x9F,0x63,0x07,0x14,0x20,0xD0,0x8F,0x2D,0x04,0x14,0x20,0x5B,0x16, +0xEF,0xFB,0x81,0x00,0x00,0xE5,0x0B,0x9F,0x00,0x04,0x14,0x20,0x00,0xDD,0x8F,0x8C, +0x00,0x00,0x00,0xFB,0x01,0xEF,0xD2,0xF7,0xFF,0xFF,0x8B,0x8F,0xFC,0x9F,0x61,0x07, +0x14,0x20,0x50,0x9A,0x50,0x50,0x90,0x40,0xEF,0x7B,0x56,0x00,0x00,0x50,0x04,0x9E, +0x9F,0xA6,0x04,0x14,0x20,0x50,0x96,0x60,0x91,0x10,0x60,0x18,0x10,0x90,0x01,0x60, +0xDD,0x8F,0x8C,0x00,0x00,0x00,0xFB,0x01,0xEF,0x9F,0xF7,0xFF,0xFF,0x9F,0x9F,0x62, +0x07,0x14,0x20,0x9F,0xEF,0x53,0x56,0x00,0x00,0xFB,0x02,0xEF,0x8C,0xF7,0xFF,0xFF, +0xD0,0x00,0x50,0x05,0xBB,0x03,0x9F,0x9F,0xD2,0x07,0x14,0x20,0x9F,0x9F,0xD0,0x07, +0x14,0x20,0x9F,0x9F,0xCF,0x07,0x14,0x20,0x9F,0x9F,0xCE,0x07,0x14,0x20,0x9A,0x9F, +0xD1,0x07,0x14,0x20,0x7E,0x9F,0x9F,0xCD,0x07,0x14,0x20,0x9F,0xEF,0x21,0x56,0x00, +0x00,0xFB,0x07,0xEF,0x54,0xF7,0xFF,0xFF,0x91,0x9F,0xD1,0x07,0x14,0x20,0x02,0x19, +0x3D,0x9E,0x9F,0xA8,0x07,0x14,0x20,0x50,0xDD,0x70,0xDD,0x70,0xDD,0x70,0xDD,0x70, +0xDD,0x70,0xDD,0x70,0xDD,0x70,0xDD,0x70,0xDD,0x70,0xDD,0x70,0x9F,0xEF,0x0F,0x56, +0x00,0x00,0xFB,0x0B,0xEF,0x23,0xF7,0xFF,0xFF,0xBA,0x03,0xBB,0x03,0xBB,0x8F,0xFF, +0x03,0x9F,0xEF,0x57,0x56,0x00,0x00,0xFB,0x0B,0xEF,0x0E,0xF7,0xFF,0xFF,0xBA,0x03, +0x05,0xFA,0xEF,0xA2,0x56,0x00,0x00,0xCF,0xD8,0xFE,0xD1,0x00,0x50,0x13,0x08,0xD0, +0x8F,0x84,0x00,0x00,0x00,0x50,0x05,0xD0,0x00,0x50,0x05,0x00,0xD5,0x9F,0x18,0x05, +0x14,0x20,0x12,0x03,0x31,0xC0,0x00,0x8F,0x6E,0x00,0x06,0xBC,0x00,0x11,0x00,0xBC, +0x00,0xBC,0x00,0xBC,0x00,0xBC,0x00,0x63,0x00,0x31,0xAB,0x00,0x8F,0xAE,0x08,0x8F, +0x80,0x03,0x0B,0x00,0xA5,0x00,0x0B,0x00,0xA5,0x00,0x31,0x9A,0x00,0xD0,0x25,0x9F, +0x0A,0x05,0x14,0x20,0xD0,0x9F,0x20,0x00,0x14,0x20,0x50,0xE0,0x1F,0x50,0x07,0xD0, +0x2E,0x9F,0x0A,0x05,0x14,0x20,0xD0,0x9F,0x20,0x00,0x14,0x20,0x9F,0x20,0x00,0x14, +0x20,0xD0,0x9F,0x04,0x00,0x08,0x20,0x9F,0x04,0x00,0x08,0x20,0xD0,0xAE,0x0C,0x9F, +0x0E,0x05,0x14,0x20,0xD0,0x01,0x9F,0x06,0x05,0x14,0x20,0x31,0x2F,0x00,0xD0,0xAE, +0x04,0x52,0x9E,0xEF,0xCB,0xE7,0xFF,0xFF,0x50,0x9E,0xEF,0xDF,0xE7,0xFF,0xFF,0x51, +0xD1,0x52,0x50,0x1F,0x15,0xD1,0x52,0x51,0x1A,0x10,0xD0,0x25,0x9F,0x0A,0x05,0x14, +0x20,0xD4,0x9F,0x06,0x05,0x14,0x20,0x31,0x03,0x00,0x31,0x2A,0x00,0xD0,0x9F,0x18, +0x05,0x14,0x20,0x51,0xD0,0x8E,0x50,0x9A,0x40,0xEF,0x8F,0x02,0x00,0x00,0x50,0xC0, +0x5E,0x50,0xD0,0xA0,0x04,0xA1,0xFC,0xD0,0x61,0xA1,0xF8,0xC3,0x08,0x51,0x5E,0xD0, +0x9F,0x0A,0x05,0x14,0x20,0x50,0x02,0x7D,0x50,0x9F,0xB2,0x04,0x14,0x20,0x7D,0x52, +0x9F,0xBA,0x04,0x14,0x20,0x7D,0x54,0x9F,0xC2,0x04,0x14,0x20,0x7D,0x56,0x9F,0xCA, +0x04,0x14,0x20,0x7D,0x58,0x9F,0xD2,0x04,0x14,0x20,0x7D,0x5A,0x9F,0xDA,0x04,0x14, +0x20,0x7D,0x5C,0x9F,0xE2,0x04,0x14,0x20,0xD0,0x5E,0x9F,0xEA,0x04,0x14,0x20,0xDD, +0x6E,0xD0,0x6E,0x52,0x9F,0xEF,0xB7,0x01,0x00,0x00,0xFB,0x02,0xEF,0xDB,0xF5,0xFF, +0xFF,0xC0,0x8F,0x80,0x01,0x00,0x00,0x6E,0xFB,0x01,0xEF,0xCD,0xF5,0xFF,0xFF,0x9A, +0x42,0xEF,0x17,0x02,0x00,0x00,0x52,0x78,0x8F,0xFE,0x52,0x52,0x11,0x0D,0x9F,0xEF, +0xB2,0x01,0x00,0x00,0xFB,0x02,0xEF,0xB1,0xF5,0xFF,0xFF,0xF4,0x52,0xF0,0x9F,0xEF, +0xB6,0x01,0x00,0x00,0xFB,0x03,0xEF,0xA1,0xF5,0xFF,0xFF,0xDD,0x9F,0xE2,0x04,0x14, +0x20,0xDD,0x9F,0xD2,0x04,0x14,0x20,0xDD,0x9F,0xC2,0x04,0x14,0x20,0xDD,0x9F,0xB2, +0x04,0x14,0x20,0x9F,0xEF,0x8F,0x00,0x00,0x00,0xFB,0x05,0xEF,0x7C,0xF5,0xFF,0xFF, +0xDD,0x9F,0xE6,0x04,0x14,0x20,0xDD,0x9F,0xD6,0x04,0x14,0x20,0xDD,0x9F,0xC6,0x04, +0x14,0x20,0xDD,0x9F,0xB6,0x04,0x14,0x20,0x9F,0xEF,0x9F,0x00,0x00,0x00,0xFB,0x05, +0xEF,0x57,0xF5,0xFF,0xFF,0xDD,0x9F,0xEA,0x04,0x14,0x20,0xDD,0x9F,0xDA,0x04,0x14, +0x20,0xDD,0x9F,0xCA,0x04,0x14,0x20,0xDD,0x9F,0xBA,0x04,0x14,0x20,0x9F,0xEF,0xAF, +0x00,0x00,0x00,0xFB,0x05,0xEF,0x32,0xF5,0xFF,0xFF,0xDD,0x9F,0xDE,0x04,0x14,0x20, +0xDD,0x9F,0xCE,0x04,0x14,0x20,0xDD,0x9F,0xBE,0x04,0x14,0x20,0x9F,0xEF,0xC5,0x00, +0x00,0x00,0xFB,0x04,0xEF,0x13,0xF5,0xFF,0xFF,0xD4,0x52,0x9F,0x6E,0x9F,0xEF,0x3E, +0x01,0x00,0x00,0xFB,0x06,0xEF,0x02,0xF5,0xFF,0xFF,0xF2,0x04,0x52,0xED,0xF0,0x03, +0x00,0x02,0x9F,0x00,0x04,0x14,0x20,0x00,0x0D,0x0A,0x20,0x72,0x30,0x20,0x3D,0x20, +0x25,0x30,0x38,0x58,0x09,0x20,0x72,0x34,0x20,0x3D,0x20,0x25,0x30,0x38,0x58,0x20, +0x20,0x20,0x20,0x20,0x72,0x38,0x20,0x3D,0x20,0x25,0x30,0x38,0x58,0x20,0x20,0x20, +0x20,0x20,0x20,0x41,0x50,0x20,0x3D,0x20,0x25,0x30,0x38,0x58,0x00,0x0D,0x0A,0x20, +0x72,0x31,0x20,0x3D,0x20,0x25,0x30,0x38,0x58,0x09,0x20,0x72,0x35,0x20,0x3D,0x20, +0x25,0x30,0x38,0x58,0x20,0x20,0x20,0x20,0x20,0x72,0x39,0x20,0x3D,0x20,0x25,0x30, +0x38,0x58,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x50,0x20,0x3D,0x20,0x25,0x30,0x38, +0x58,0x00,0x0D,0x0A,0x20,0x72,0x32,0x20,0x3D,0x20,0x25,0x30,0x38,0x58,0x09,0x20, +0x72,0x36,0x20,0x3D,0x20,0x25,0x30,0x38,0x58,0x20,0x20,0x20,0x20,0x72,0x31,0x30, +0x20,0x3D,0x20,0x25,0x30,0x38,0x58,0x20,0x20,0x20,0x20,0x20,0x20,0x53,0x50,0x20, +0x3D,0x20,0x25,0x30,0x38,0x58,0x00,0x0D,0x0A,0x20,0x72,0x33,0x20,0x3D,0x20,0x25, +0x30,0x38,0x58,0x09,0x20,0x72,0x37,0x20,0x3D,0x20,0x25,0x30,0x38,0x58,0x20,0x20, +0x20,0x20,0x72,0x31,0x31,0x20,0x3D,0x20,0x25,0x30,0x38,0x58,0x0D,0x0A,0x0D,0x0A, +0x00,0x0D,0x0A,0x20,0x20,0x20,0x20,0x55,0x6E,0x65,0x78,0x70,0x65,0x63,0x74,0x65, +0x64,0x20,0x65,0x78,0x63,0x65,0x70,0x74,0x69,0x6F,0x6E,0x20,0x2D,0x20,0x28,0x25, +0x30,0x32,0x58,0x29,0x20,0x00,0x09,0x50,0x61,0x72,0x61,0x6D,0x65,0x74,0x65,0x72, +0x20,0x3D,0x20,0x25,0x30,0x38,0x58,0x0D,0x0A,0x00,0x09,0x50,0x43,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x3D,0x20,0x25,0x30,0x38,0x58,0x0D,0x0A,0x09,0x50,0x53, +0x4C,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3D,0x20,0x25,0x30,0x38,0x58,0x0D,0x0A, +0x00,0x25,0x30,0x38,0x58,0x3A,0x09,0x25,0x30,0x38,0x58,0x20,0x25,0x30,0x38,0x58, +0x20,0x25,0x30,0x38,0x58,0x20,0x25,0x30,0x38,0x58,0x0D,0x0A,0x00,0x00,0x14,0x00, +0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x04,0x04, +0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x28, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x08,0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x00,0x00, +0x00,0x00,0x08,0x08,0x1E,0x21,0x01,0x01,0x21,0x1E,0x08,0x08,0x00,0x00,0x00,0x38, +0x44,0x04,0x04,0x04,0x1F,0x04,0x04,0x04,0x6E,0x7B,0x00,0x00,0x00,0x00,0x3E,0x41, +0x41,0x01,0x02,0x04,0x08,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x22,0x22, +0x22,0x14,0x08,0x3E,0x08,0x3E,0x08,0x08,0x00,0x00,0x00,0x00,0x3E,0x41,0x41,0x01, +0x02,0x04,0x08,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x0E,0x11,0x01,0x02,0x0E,0x11, +0x11,0x0E,0x08,0x10,0x11,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x22,0x1C,0x22, +0x22,0x22,0x1C,0x22,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x41,0x59,0x45, +0x45,0x59,0x41,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x20,0x3E,0x21,0x21,0x3E, +0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0x12,0x09,0x12, +0x24,0x00,0x00,0x00,0x00,0x00,0x3E,0x41,0x41,0x01,0x02,0x04,0x08,0x08,0x00,0x08, +0x00,0x00,0x00,0x00,0x00,0x3E,0x41,0x41,0x01,0x02,0x04,0x08,0x08,0x00,0x08,0x00, +0x00,0x00,0x00,0x00,0x3E,0x41,0x41,0x01,0x02,0x04,0x08,0x08,0x00,0x08,0x00,0x00, +0x00,0x00,0x00,0x3E,0x41,0x41,0x01,0x02,0x04,0x08,0x08,0x00,0x08,0x00,0x00,0x00, +0x00,0x0C,0x12,0x12,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x04,0x04,0x04,0x1F,0x04,0x04,0x04,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00, +0x06,0x09,0x04,0x02,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F, +0x08,0x04,0x09,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x41, +0x41,0x01,0x02,0x04,0x08,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x22,0x22,0x22,0x22,0x22,0x3E,0x21,0x40,0x00,0x00,0x00,0x3E,0x27,0x27, +0x27,0x27,0x3E,0x24,0x24,0x24,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x0C,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x41,0x41,0x01,0x02,0x04, +0x08,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x04,0x06,0x04,0x04,0x04,0x0E,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x11,0x11,0x1E,0x00,0x1E,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x12,0x24,0x12, +0x09,0x00,0x00,0x00,0x02,0x03,0x02,0x02,0x27,0x10,0x08,0x24,0x32,0x29,0x7C,0x20, +0x20,0x00,0x00,0x02,0x03,0x02,0x02,0x27,0x10,0x08,0x14,0x2A,0x41,0x30,0x08,0x78, +0x00,0x00,0x00,0x00,0x3E,0x41,0x41,0x01,0x02,0x04,0x08,0x08,0x00,0x08,0x00,0x00, +0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x08,0x04,0x02,0x01,0x21,0x21,0x1E,0x00,0x00, +0x04,0x08,0x10,0x0C,0x12,0x21,0x21,0x21,0x3F,0x21,0x21,0x21,0x00,0x00,0x00,0x08, +0x04,0x02,0x0C,0x12,0x21,0x21,0x21,0x3F,0x21,0x21,0x21,0x00,0x00,0x00,0x0C,0x12, +0x21,0x0C,0x12,0x21,0x21,0x21,0x3F,0x21,0x21,0x21,0x00,0x00,0x00,0x26,0x19,0x00, +0x0C,0x12,0x21,0x21,0x21,0x3F,0x21,0x21,0x21,0x00,0x00,0x00,0x00,0x33,0x00,0x0C, +0x12,0x21,0x21,0x21,0x3F,0x21,0x21,0x21,0x00,0x00,0x00,0x0C,0x12,0x0C,0x0C,0x12, +0x21,0x21,0x21,0x3F,0x21,0x21,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0x0A,0x09, +0x09,0x3F,0x09,0x09,0x09,0x79,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x21,0x01, +0x01,0x01,0x01,0x01,0x21,0x1E,0x08,0x06,0x04,0x08,0x10,0x3F,0x01,0x01,0x01,0x0F, +0x01,0x01,0x01,0x3F,0x00,0x00,0x00,0x08,0x04,0x02,0x3F,0x01,0x01,0x01,0x0F,0x01, +0x01,0x01,0x3F,0x00,0x00,0x00,0x0C,0x12,0x21,0x3F,0x01,0x01,0x01,0x0F,0x01,0x01, +0x01,0x3F,0x00,0x00,0x00,0x00,0x33,0x00,0x3F,0x01,0x01,0x01,0x0F,0x01,0x01,0x01, +0x3F,0x00,0x00,0x00,0x04,0x08,0x10,0x1F,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x1F, +0x00,0x00,0x00,0x04,0x02,0x01,0x1F,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x1F,0x00, +0x00,0x00,0x0C,0x12,0x21,0x1F,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x1F,0x00,0x00, +0x00,0x00,0x33,0x00,0x1F,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x1F,0x00,0x00,0x00, +0x00,0x00,0x3E,0x41,0x41,0x01,0x02,0x04,0x08,0x08,0x00,0x08,0x00,0x00,0x00,0x26, +0x19,0x00,0x21,0x21,0x23,0x25,0x29,0x31,0x21,0x21,0x21,0x00,0x00,0x00,0x04,0x08, +0x10,0x1E,0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x1E,0x00,0x00,0x00,0x04,0x02,0x01, +0x1E,0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x1E,0x00,0x00,0x00,0x0C,0x12,0x21,0x1E, +0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x1E,0x00,0x00,0x00,0x26,0x19,0x00,0x1E,0x21, +0x21,0x21,0x21,0x21,0x21,0x21,0x1E,0x00,0x00,0x00,0x00,0x33,0x00,0x1E,0x21,0x21, +0x21,0x21,0x21,0x21,0x21,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x09,0x09,0x09, +0x39,0x09,0x09,0x09,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x5E,0x21,0x31,0x29,0x29, +0x25,0x23,0x21,0x1E,0x00,0x00,0x00,0x02,0x04,0x08,0x21,0x21,0x21,0x21,0x21,0x21, +0x21,0x21,0x1E,0x00,0x00,0x00,0x10,0x08,0x04,0x21,0x21,0x21,0x21,0x21,0x21,0x21, +0x21,0x1E,0x00,0x00,0x00,0x0C,0x12,0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, +0x1E,0x00,0x00,0x00,0x00,0x33,0x00,0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x1E, +0x00,0x00,0x00,0x00,0x63,0x00,0x41,0x41,0x22,0x14,0x08,0x08,0x08,0x08,0x08,0x00, +0x00,0x00,0x00,0x00,0x3E,0x41,0x41,0x01,0x02,0x04,0x08,0x08,0x00,0x08,0x00,0x00, +0x00,0x00,0x38,0x44,0x42,0x42,0x22,0x3A,0x42,0x42,0x42,0x4A,0x3A,0x02,0x01,0x00, +0x00,0x00,0x04,0x08,0x10,0x00,0x1E,0x20,0x3E,0x21,0x21,0x3E,0x00,0x00,0x00,0x00, +0x00,0x08,0x04,0x02,0x00,0x1E,0x20,0x3E,0x21,0x21,0x3E,0x00,0x00,0x00,0x00,0x00, +0x0C,0x12,0x21,0x00,0x1E,0x20,0x3E,0x21,0x21,0x3E,0x00,0x00,0x00,0x00,0x00,0x00, +0x26,0x19,0x00,0x1E,0x20,0x3E,0x21,0x21,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x33, +0x00,0x00,0x1E,0x20,0x3E,0x21,0x21,0x3E,0x00,0x00,0x00,0x00,0x00,0x0C,0x12,0x0C, +0x00,0x1E,0x20,0x3E,0x21,0x21,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x37,0x48,0x7E,0x09,0x09,0x76,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E, +0x21,0x01,0x01,0x21,0x1E,0x08,0x06,0x00,0x00,0x00,0x04,0x08,0x10,0x00,0x1E,0x21, +0x3F,0x01,0x01,0x1E,0x00,0x00,0x00,0x00,0x00,0x08,0x04,0x02,0x00,0x1E,0x21,0x3F, +0x01,0x01,0x1E,0x00,0x00,0x00,0x00,0x00,0x0C,0x12,0x21,0x00,0x1E,0x21,0x3F,0x01, +0x01,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x00,0x00,0x1E,0x21,0x3F,0x01,0x01, +0x1E,0x00,0x00,0x00,0x00,0x00,0x02,0x04,0x08,0x00,0x04,0x04,0x04,0x04,0x04,0x0E, +0x00,0x00,0x00,0x00,0x00,0x08,0x04,0x02,0x00,0x04,0x04,0x04,0x04,0x04,0x0E,0x00, +0x00,0x00,0x00,0x00,0x0C,0x12,0x21,0x00,0x04,0x04,0x04,0x04,0x04,0x0E,0x00,0x00, +0x00,0x00,0x00,0x00,0x33,0x00,0x00,0x04,0x04,0x04,0x04,0x04,0x0E,0x00,0x00,0x00, +0x00,0x00,0x3E,0x41,0x41,0x01,0x02,0x04,0x08,0x08,0x00,0x08,0x00,0x00,0x00,0x00, +0x00,0x00,0x26,0x19,0x00,0x1F,0x21,0x21,0x21,0x21,0x21,0x00,0x00,0x00,0x00,0x00, +0x04,0x08,0x10,0x00,0x1E,0x21,0x21,0x21,0x21,0x1E,0x00,0x00,0x00,0x00,0x00,0x08, +0x04,0x02,0x00,0x1E,0x21,0x21,0x21,0x21,0x1E,0x00,0x00,0x00,0x00,0x00,0x0C,0x12, +0x21,0x00,0x1E,0x21,0x21,0x21,0x21,0x1E,0x00,0x00,0x00,0x00,0x00,0x19,0x26,0x00, +0x00,0x1E,0x21,0x21,0x21,0x21,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x1B,0x00,0x00, +0x1E,0x21,0x21,0x21,0x21,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36, +0x49,0x79,0x09,0x09,0x76,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5E,0x31,0x29, +0x25,0x23,0x1E,0x01,0x00,0x00,0x00,0x00,0x00,0x02,0x04,0x08,0x00,0x21,0x21,0x21, +0x21,0x21,0x1E,0x00,0x00,0x00,0x00,0x00,0x10,0x08,0x04,0x00,0x21,0x21,0x21,0x21, +0x21,0x1E,0x00,0x00,0x00,0x00,0x00,0x0C,0x12,0x21,0x00,0x21,0x21,0x21,0x21,0x21, +0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x1B,0x00,0x00,0x21,0x21,0x21,0x21,0x21,0x1E, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1B,0x00,0x00,0x21,0x21,0x21,0x21,0x1E,0x20, +0x20,0x3F,0x00,0x00,0x3E,0x41,0x41,0x01,0x02,0x04,0x08,0x08,0x00,0x08,0x00,0x00, +0x00,0x00,0x00,0x3E,0x41,0x41,0x01,0x02,0x04,0x08,0x08,0x00,0x08,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00, +0x24,0x24,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x12,0x12,0x3F,0x12,0x12,0x3F,0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08, +0x3E,0x09,0x09,0x3E,0x48,0x48,0x3E,0x08,0x00,0x00,0x00,0x00,0x00,0x42,0x25,0x25, +0x12,0x08,0x08,0x24,0x52,0x52,0x21,0x00,0x00,0x00,0x00,0x00,0x0E,0x11,0x11,0x11, +0x0E,0x11,0x51,0x21,0x31,0x4E,0x00,0x00,0x00,0x00,0x00,0x38,0x18,0x04,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x08,0x08,0x04,0x04,0x04, +0x04,0x08,0x08,0x10,0x00,0x00,0x00,0x00,0x00,0x04,0x08,0x08,0x10,0x10,0x10,0x10, +0x08,0x08,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x22,0x14,0x7F,0x14,0x22, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x08,0x7F,0x08,0x08,0x08, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x0C, +0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x1C,0x08,0x00, +0x00,0x00,0x00,0x40,0x40,0x20,0x10,0x08,0x04,0x02,0x01,0x01,0x01,0x00,0x00,0x00, +0x00,0x00,0x0C,0x12,0x21,0x21,0x21,0x21,0x21,0x21,0x12,0x0C,0x00,0x00,0x00,0x00, +0x00,0x08,0x0C,0x0A,0x08,0x08,0x08,0x08,0x08,0x08,0x3E,0x00,0x00,0x00,0x00,0x00, +0x3E,0x41,0x41,0x40,0x20,0x10,0x0C,0x02,0x01,0x7F,0x00,0x00,0x00,0x00,0x00,0x7F, +0x40,0x20,0x10,0x38,0x40,0x40,0x40,0x41,0x3E,0x00,0x00,0x00,0x00,0x00,0x20,0x30, +0x28,0x24,0x22,0x21,0x7F,0x20,0x20,0x20,0x00,0x00,0x00,0x00,0x00,0x7F,0x01,0x01, +0x3D,0x43,0x40,0x40,0x40,0x41,0x3E,0x00,0x00,0x00,0x00,0x00,0x3C,0x02,0x01,0x01, +0x3D,0x43,0x41,0x41,0x41,0x3E,0x00,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x20,0x10, +0x08,0x04,0x04,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3E,0x41,0x41,0x41,0x3E,0x41, +0x41,0x41,0x41,0x3E,0x00,0x00,0x00,0x00,0x00,0x3E,0x41,0x41,0x41,0x61,0x5E,0x40, +0x40,0x20,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x1C,0x08,0x00,0x00, +0x08,0x1C,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x1C,0x08,0x00,0x00,0x1C, +0x0C,0x02,0x00,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x02,0x04,0x08,0x10,0x20, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x7F,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x02,0x04,0x08,0x10,0x20,0x20,0x10,0x08,0x04,0x02,0x00,0x00, +0x00,0x00,0x00,0x3E,0x41,0x41,0x40,0x20,0x10,0x08,0x08,0x00,0x08,0x00,0x00,0x00, +0x00,0x00,0x3E,0x41,0x41,0x79,0x45,0x65,0x59,0x01,0x01,0x3E,0x00,0x00,0x00,0x00, +0x00,0x08,0x14,0x22,0x41,0x41,0x41,0x7F,0x41,0x41,0x41,0x00,0x00,0x00,0x00,0x00, +0x3F,0x42,0x42,0x42,0x3E,0x42,0x42,0x42,0x42,0x3F,0x00,0x00,0x00,0x00,0x00,0x3E, +0x41,0x01,0x01,0x01,0x01,0x01,0x01,0x41,0x3E,0x00,0x00,0x00,0x00,0x00,0x3F,0x42, +0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x3F,0x00,0x00,0x00,0x00,0x00,0x7E,0x02,0x02, +0x02,0x1E,0x02,0x02,0x02,0x02,0x7E,0x00,0x00,0x00,0x00,0x00,0x7E,0x02,0x02,0x02, +0x1E,0x02,0x02,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3E,0x41,0x01,0x01,0x01, +0x71,0x41,0x41,0x41,0x3E,0x00,0x00,0x00,0x00,0x00,0x41,0x41,0x41,0x41,0x7F,0x41, +0x41,0x41,0x41,0x41,0x00,0x00,0x00,0x00,0x00,0x3E,0x08,0x08,0x08,0x08,0x08,0x08, +0x08,0x08,0x3E,0x00,0x00,0x00,0x00,0x00,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x21,0x1E,0x00,0x00,0x00,0x00,0x00,0x41,0x21,0x11,0x09,0x07,0x05,0x09,0x11,0x21, +0x41,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x7F, +0x00,0x00,0x00,0x00,0x00,0x41,0x41,0x63,0x55,0x55,0x49,0x49,0x41,0x41,0x41,0x00, +0x00,0x00,0x00,0x00,0x41,0x41,0x43,0x45,0x49,0x51,0x61,0x41,0x41,0x41,0x00,0x00, +0x00,0x00,0x00,0x3E,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x3E,0x00,0x00,0x00, +0x00,0x00,0x3F,0x41,0x41,0x41,0x3F,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00, +0x00,0x3E,0x41,0x41,0x41,0x41,0x41,0x41,0x49,0x51,0x3E,0x40,0x00,0x00,0x00,0x00, +0x3F,0x41,0x41,0x41,0x3F,0x09,0x11,0x21,0x41,0x41,0x00,0x00,0x00,0x00,0x00,0x3E, +0x41,0x01,0x01,0x3E,0x40,0x40,0x40,0x41,0x3E,0x00,0x00,0x00,0x00,0x00,0x7F,0x08, +0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x41,0x41,0x41, +0x41,0x41,0x41,0x41,0x41,0x41,0x3E,0x00,0x00,0x00,0x00,0x00,0x41,0x41,0x41,0x22, +0x22,0x22,0x14,0x14,0x14,0x08,0x00,0x00,0x00,0x00,0x00,0x41,0x41,0x41,0x41,0x49, +0x49,0x49,0x49,0x55,0x22,0x00,0x00,0x00,0x00,0x00,0x41,0x41,0x22,0x14,0x08,0x08, +0x14,0x22,0x41,0x41,0x00,0x00,0x00,0x00,0x00,0x41,0x41,0x22,0x14,0x08,0x08,0x08, +0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x7F,0x40,0x20,0x10,0x08,0x04,0x02,0x01, +0x01,0x7F,0x00,0x00,0x00,0x00,0x00,0x3C,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, +0x3C,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x02,0x04,0x08,0x08,0x10,0x20,0x20,0x40, +0x00,0x00,0x00,0x00,0x00,0x1E,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x1E,0x00, +0x00,0x00,0x00,0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x00,0x00, +0x00,0x00,0x0E,0x0C,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x3E,0x40,0x40,0x7E,0x41,0x61,0x5E,0x00,0x00,0x00,0x00,0x00, +0x01,0x01,0x01,0x3D,0x43,0x41,0x41,0x41,0x43,0x3D,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x3E,0x41,0x01,0x01,0x01,0x41,0x3E,0x00,0x00,0x00,0x00,0x00,0x40,0x40, +0x40,0x5E,0x61,0x41,0x41,0x41,0x61,0x5E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x3E,0x41,0x41,0x7F,0x01,0x01,0x3E,0x00,0x00,0x00,0x00,0x00,0x38,0x44,0x44,0x04, +0x04,0x1F,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5E,0x21, +0x21,0x21,0x1E,0x01,0x3E,0x41,0x41,0x3E,0x00,0x00,0x01,0x01,0x01,0x3D,0x43,0x41, +0x41,0x41,0x41,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x0C,0x08,0x08,0x08, +0x08,0x08,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x38,0x20,0x20,0x20,0x20, +0x20,0x21,0x21,0x21,0x1E,0x00,0x00,0x01,0x01,0x01,0x41,0x31,0x0D,0x03,0x0D,0x31, +0x41,0x00,0x00,0x00,0x00,0x00,0x0C,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x3E, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x37,0x49,0x49,0x49,0x49,0x49,0x41,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3D,0x43,0x41,0x41,0x41,0x41,0x41,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x41,0x41,0x41,0x41,0x41,0x3E,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x3D,0x43,0x41,0x41,0x41,0x43,0x3D,0x01,0x01,0x01,0x00, +0x00,0x00,0x00,0x00,0x5E,0x61,0x41,0x41,0x41,0x61,0x5E,0x40,0x40,0x40,0x00,0x00, +0x00,0x00,0x00,0x39,0x46,0x42,0x02,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x3E,0x41,0x01,0x3E,0x40,0x41,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x04, +0x04,0x3F,0x04,0x04,0x04,0x04,0x44,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x21,0x21,0x21,0x21,0x21,0x21,0x5E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41, +0x41,0x22,0x22,0x14,0x14,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x41, +0x49,0x49,0x49,0x55,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x22,0x14, +0x08,0x14,0x22,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x21,0x21,0x21, +0x21,0x31,0x2E,0x20,0x21,0x1E,0x00,0x00,0x00,0x00,0x00,0x7F,0x20,0x10,0x08,0x04, +0x02,0x7F,0x00,0x00,0x00,0x00,0x00,0x70,0x08,0x08,0x10,0x0C,0x0C,0x10,0x08,0x08, +0x70,0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, +0x00,0x00,0x00,0x00,0x00,0x07,0x08,0x08,0x04,0x18,0x18,0x04,0x08,0x08,0x07,0x00, +0x00,0x00,0x00,0x00,0x46,0x49,0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0x7E,0xCB,0x89,0x00,0x56, +0x91,0xCB,0xC9,0x00,0x02,0x12,0x2D,0x3C,0x66,0x50,0x13,0x28,0xD7,0x50,0x91,0x40, +0xB6,0x04,0x3A,0x12,0x08,0xB7,0x66,0x1A,0x04,0xD0,0x22,0x50,0x05,0x9A,0x40,0xB6, +0x04,0x51,0xE1,0x51,0xEF,0xFF,0x33,0x00,0x00,0x05,0x82,0x20,0x40,0xB6,0x04,0xF4, +0x50,0xEB,0x11,0x15,0x9E,0x9F,0x14,0x04,0x14,0x20,0xA6,0x04,0xDD,0xA6,0x04,0xFB, +0x01,0xEF,0x45,0xCD,0xFF,0xFF,0xB0,0x50,0x66,0xD0,0x9F,0x26,0x04,0x14,0x20,0x57, +0x95,0xCB,0xCA,0x00,0x13,0x0C,0xD0,0xAB,0x1C,0x57,0xE1,0x02,0x6B,0x04,0xD0,0xAB, +0x24,0x57,0xDD,0x57,0xDD,0x56,0xFB,0x02,0xEF,0x03,0x00,0x00,0x00,0x05,0xB5,0x8F, +0x3C,0x08,0xE2,0x02,0x9F,0x00,0x04,0x14,0x20,0x00,0xD0,0x8F,0x58,0x07,0x14,0x20, +0x5B,0xD4,0x50,0x3C,0xAB,0x20,0x51,0xD0,0xAB,0x1C,0x52,0x30,0xAC,0xCB,0xB1,0x50, +0xAB,0x22,0x13,0x08,0x30,0x8A,0xF0,0xD5,0x50,0x13,0x01,0x04,0xD0,0xAC,0x04,0x50, +0xC3,0x8F,0x00,0x02,0x00,0x00,0x9F,0x7C,0x07,0x14,0x20,0x53,0xB0,0x60,0x63,0x9E, +0xA3,0x08,0xA3,0x04,0x2C,0x60,0xB0,0x04,0x00,0x12,0xB3,0x04,0x30,0x3B,0x01,0xEF, +0x02,0x02,0x9F,0x04,0x40,0x08,0x20,0x50,0x3E,0x40,0x9F,0x40,0x1F,0x00,0x20,0x54, +0xAA,0x20,0x64,0x30,0xFC,0xD0,0xDD,0x00,0xDD,0x00,0xDD,0x8F,0xAF,0x00,0x00,0x00, +0xFB,0x03,0xEF,0x1B,0xEF,0xFF,0xFF,0x30,0x6B,0xCF,0xA8,0x8F,0x20,0x01,0x64,0xEF, +0x02,0x02,0x9F,0x04,0x40,0x08,0x20,0x50,0x13,0x14,0xD4,0x52,0x30,0x96,0xCB,0xF3, +0x8F,0x64,0x00,0x00,0x00,0x52,0xF5,0xB0,0x64,0x50,0xE0,0x08,0x50,0xEC,0xD0,0x8F, +0x58,0x07,0x14,0x20,0x5B,0xDD,0x8F,0x00,0x01,0x00,0x00,0x16,0xEF,0xE6,0x4B,0x01, +0x00,0x01,0xFB,0x03,0xEF,0xF6,0xC8,0xFF,0xFF,0xD1,0x50,0x8F,0xFF,0xFF,0xFF,0xFF, +0x12,0x04,0xD0,0x2C,0x50,0x04,0xD0,0x50,0x52,0xC3,0x8F,0x00,0x02,0x00,0x00,0x9F, +0x7C,0x07,0x14,0x20,0x50,0xD0,0x50,0x9F,0xB2,0x04,0x14,0x20,0xB4,0xA0,0x02,0x16, +0xEF,0xBE,0x4C,0x01,0x00,0x01,0x01,0xD0,0xAB,0x1C,0x9F,0xBE,0x04,0x14,0x20,0xD0, +0x9F,0xE6,0x04,0x14,0x20,0x9F,0xC2,0x04,0x14,0x20,0xD0,0xAC,0x08,0x9F,0xC6,0x04, +0x14,0x20,0xD0,0x9F,0xF6,0x04,0x14,0x20,0x9F,0xDA,0x04,0x14,0x20,0xCB,0x8F,0x00, +0xFF,0x00,0x00,0x9F,0xFA,0x04,0x14,0x20,0x9F,0xDE,0x04,0x14,0x20,0xEF,0x08,0x06, +0x9F,0xFA,0x04,0x14,0x20,0x9F,0xE2,0x04,0x14,0x20,0xC1,0x8F,0x00,0x02,0x00,0x00, +0x52,0x7E,0x9F,0x6E,0xDD,0x8F,0x80,0x08,0x00,0x00,0xDD,0x0E,0xFB,0x03,0xEF,0x15, +0xD6,0xFF,0xFF,0xD5,0x50,0x13,0x01,0x04,0xD0,0x8E,0x9F,0xEE,0x04,0x14,0x20,0x28, +0x8F,0xC5,0x3D,0xEF,0xEC,0x0A,0x01,0x00,0xC2,0x00,0x02,0xD4,0x9F,0xB6,0x04,0x14, +0x20,0xD4,0x9F,0xCA,0x04,0x14,0x20,0xD4,0x9F,0xCE,0x04,0x14,0x20,0xD4,0x9F,0xD2, +0x04,0x14,0x20,0xD4,0x9F,0xD6,0x04,0x14,0x20,0xD4,0x9F,0xE6,0x04,0x14,0x20,0x90, +0x01,0x9F,0x16,0x05,0x14,0x20,0xD0,0x00,0x50,0x04,0xED,0x02,0x02,0x9F,0x04,0x40, +0x08,0x20,0x00,0x13,0x0E,0xD0,0x8F,0x50,0x52,0x41,0x30,0xB3,0x04,0xB0,0x04,0x63, +0x31,0x15,0x01,0xC3,0x8F,0x00,0x02,0x00,0x00,0x9F,0x7C,0x07,0x14,0x20,0x53,0xB1, +0x63,0x11,0x1B,0x04,0xD0,0x20,0x50,0x04,0xB5,0x63,0x13,0x03,0x31,0xF9,0x00,0xDD, +0x8F,0x9E,0x00,0x00,0x00,0xFB,0x01,0xEF,0xF0,0xE5,0xFF,0xFF,0xDD,0x8F,0x8C,0x00, +0x00,0x00,0xFB,0x01,0xEF,0xE3,0xE5,0xFF,0xFF,0xDD,0x8F,0x9F,0x00,0x00,0x00,0xFB, +0x01,0xEF,0xD6,0xE5,0xFF,0xFF,0x30,0x23,0x03,0xD5,0x50,0x13,0x09,0xDD,0x50,0xFB, +0x01,0xEF,0xC6,0xE5,0xFF,0xFF,0xDD,0x8F,0x8C,0x00,0x00,0x00,0xFB,0x01,0xEF,0xB9, +0xE5,0xFF,0xFF,0xDD,0x8F,0xA0,0x00,0x00,0x00,0xFB,0x01,0xEF,0xAC,0xE5,0xFF,0xFF, +0xDD,0x8F,0xB8,0x0B,0x00,0x00,0xDD,0x8F,0x51,0x00,0x00,0x00,0xDD,0xA3,0x04,0x9F, +0xEF,0x47,0x45,0x00,0x00,0xFB,0x04,0xEF,0x5A,0xE0,0xFF,0xFF,0xD0,0x50,0x52,0xD1, +0x52,0x2D,0x12,0x0F,0xDD,0x8F,0x8C,0x00,0x00,0x00,0xFB,0x01,0xEF,0x7B,0xE5,0xFF, +0xFF,0x11,0x4D,0xDD,0xA3,0x04,0xFB,0x01,0xEF,0xCE,0xCA,0xFF,0xFF,0xB0,0x50,0x63, +0x13,0x3E,0xD7,0x50,0x91,0x40,0xB3,0x04,0x3A,0x12,0x0F,0xB7,0x63,0x1A,0x0B,0xDD, +0x22,0xFB,0x01,0xEF,0x54,0xE5,0xFF,0xFF,0x11,0x8C,0x9A,0x40,0xB3,0x04,0x51,0xE1, +0x51,0xEF,0x42,0x31,0x00,0x00,0x05,0x82,0x20,0x40,0xB3,0x04,0xF4,0x50,0xEB,0xB1, +0x63,0x11,0x1B,0x1C,0xDD,0x26,0xFB,0x01,0xEF,0x2F,0xE5,0xFF,0xFF,0x31,0x66,0xFF, +0xD0,0x8F,0x58,0x51,0x41,0x30,0xB3,0x04,0xB0,0x04,0x63,0xD1,0x52,0x2D,0x13,0x0B, +0x2C,0x63,0xB3,0x04,0x00,0x12,0x9F,0x14,0x04,0x14,0x20,0xDD,0x8F,0x8C,0x00,0x00, +0x00,0xFB,0x01,0xEF,0x04,0xE5,0xFF,0xFF,0xC3,0x8F,0x00,0x02,0x00,0x00,0x9F,0x7C, +0x07,0x14,0x20,0x53,0xDD,0x53,0xDD,0xAC,0x08,0x9F,0xEF,0xAD,0x44,0x00,0x00,0xFB, +0x03,0xEF,0xE6,0xE4,0xFF,0xFF,0x05,0x00,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0x17, +0xDB,0x89,0x00,0x91,0xCB,0xC9,0x00,0x03,0x13,0x04,0xD0,0x37,0x50,0x05,0xD0,0xCB, +0x91,0x00,0x9F,0x26,0x04,0x14,0x20,0xD0,0x00,0x50,0x05,0x91,0xCB,0xC9,0x00,0x03, +0x18,0x04,0xD0,0x37,0x50,0x05,0x9E,0xCB,0x91,0x00,0x50,0x3C,0x60,0x51,0xC0,0xA0, +0x04,0x51,0x91,0x71,0x3A,0x12,0x08,0xB7,0x60,0x14,0x04,0xD0,0x22,0x50,0x05,0xB1, +0x60,0x11,0x15,0x04,0xD0,0x26,0x50,0x05,0x2C,0x60,0xB0,0x04,0x00,0x12,0x9F,0x14, +0x04,0x14,0x20,0x9E,0x9F,0x14,0x04,0x14,0x20,0x50,0x9A,0x80,0x51,0x13,0x0E,0xE1, +0x51,0xEF,0x72,0x30,0x00,0x00,0xF3,0x82,0x20,0xA0,0xFF,0x11,0xED,0xD0,0x00,0x50, +0x05,0xD0,0xCB,0x91,0x00,0x59,0xE0,0x1B,0x6B,0x08,0xE0,0x1A,0x6B,0x40,0xD0,0x22, +0x50,0x05,0xE0,0x16,0x6B,0x04,0xD0,0x27,0x50,0x05,0xD4,0x57,0xE0,0x19,0x6B,0x16, +0xD1,0x59,0x8F,0x00,0x00,0x00,0x20,0x1F,0x09,0xD1,0x59,0x8F,0xFC,0x1F,0x00,0x20, +0x1B,0x10,0xD0,0x25,0x50,0x05,0xD0,0x03,0x57,0xD1,0x59,0x04,0x1F,0x04,0xD0,0x26, +0x50,0x05,0xDD,0x57,0xDD,0x59,0xFB,0x02,0xEF,0xFD,0xB6,0xFF,0xFF,0x05,0xE0,0x16, +0x6B,0x04,0xD0,0x27,0x50,0x05,0x9E,0xEF,0x94,0xB5,0xFF,0xFF,0x56,0xD4,0x57,0xE0, +0x17,0x6B,0x1C,0xE0,0x18,0x6B,0x16,0xD1,0x59,0x8F,0x00,0x00,0x00,0x20,0x1F,0x09, +0xD1,0x59,0x8F,0xFC,0xFF,0xFF,0x3F,0x1B,0x3B,0xD0,0x25,0x50,0x05,0xD6,0x57,0xD6, +0x57,0xD0,0x08,0x51,0xD0,0x59,0x52,0xD4,0x59,0x9C,0x04,0x52,0x52,0xCB,0x8F,0xF0, +0xFF,0xFF,0xFF,0x52,0x50,0xD1,0x50,0x0A,0x19,0x04,0xD0,0x23,0x50,0x05,0xC4,0x0A, +0x59,0xC0,0x50,0x59,0xF5,0x51,0xE2,0xD1,0x59,0x8F,0xFF,0x00,0x00,0x00,0x1B,0x04, +0xD0,0x26,0x50,0x05,0x91,0xCB,0xC9,0x00,0x03,0x13,0x0B,0x91,0xCB,0xC9,0x00,0x04, +0x13,0x0B,0xD0,0x22,0x50,0x05,0xC2,0x06,0x5E,0xD4,0x58,0x11,0x31,0x9E,0xCB,0x99, +0x00,0x50,0xB1,0x60,0x06,0x15,0x04,0xD0,0x26,0x50,0x05,0xC2,0x06,0x5E,0x2C,0x60, +0xB0,0x04,0x20,0x06,0x6E,0xD0,0x05,0x50,0x9A,0x40,0x6E,0x51,0xE1,0x51,0xEF,0x75, +0x2F,0x00,0x00,0x04,0x82,0x20,0x40,0x6E,0xF4,0x50,0xED,0x9E,0x6E,0x58,0xDD,0x57, +0xDD,0x58,0xDD,0x59,0xFB,0x03,0x66,0xC0,0x06,0x5E,0x05,0x91,0xCB,0xC9,0x00,0x03, +0x13,0x04,0xD0,0x22,0x50,0x05,0xD0,0xCB,0x91,0x00,0x51,0xD1,0x51,0x01,0x1F,0x05, +0xD1,0x51,0x0F,0x1B,0x04,0xD0,0x26,0x50,0x05,0x9E,0xEF,0x1B,0x00,0x00,0x00,0x50, +0x3E,0x41,0xA0,0xFE,0x50,0xF0,0x60,0x04,0x04,0x9F,0x00,0x04,0x14,0x20,0x90,0xA0, +0x01,0x9F,0x02,0x04,0x14,0x20,0xD0,0x00,0x50,0x05,0x01,0x03,0x02,0x06,0x02,0x0A, +0x03,0x04,0x03,0x00,0x04,0x0E,0x05,0x02,0x05,0x0D,0x05,0x09,0x06,0x08,0x07,0x07, +0x08,0x0C,0x09,0x0F,0x0A,0x05,0x0B,0x0B,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0x17, +0xDB,0x89,0x00,0x16,0xEF,0xFC,0xAF,0xFF,0xFF,0xDD,0x50,0xDD,0x0D,0x9F,0xEF,0xB9, +0x42,0x00,0x00,0xFB,0x03,0xEF,0xE2,0xE2,0xFF,0xFF,0x9A,0x00,0x50,0x05,0xDD,0x9F, +0x26,0x04,0x14,0x20,0x9F,0xEF,0xAC,0x42,0x00,0x00,0xFB,0x02,0xEF,0xCB,0xE2,0xFF, +0xFF,0xD0,0x00,0x50,0x05,0x9F,0x9F,0x14,0x04,0x14,0x20,0x9F,0xEF,0x9C,0x42,0x00, +0x00,0xFB,0x02,0xEF,0xB4,0xE2,0xFF,0xFF,0xD0,0x00,0x50,0x05,0xDD,0x02,0x11,0x02, +0xDD,0x00,0xFB,0x01,0xEF,0x75,0xB2,0xFF,0xFF,0xD5,0x50,0x12,0x16,0xDD,0x51,0xFB, +0x01,0xEF,0x70,0xB1,0xFF,0xFF,0xD5,0x50,0x12,0x09,0xDD,0x51,0xFB,0x01,0xEF,0xFB, +0x03,0x00,0x00,0x05,0xDD,0x00,0xFB,0x01,0xEF,0xF1,0x03,0x00,0x00,0x05,0x9A,0x9F, +0x02,0x04,0x14,0x20,0x50,0xD1,0x50,0x10,0x19,0x03,0xD0,0x10,0x50,0x9E,0xEF,0x41, +0x43,0x00,0x00,0x51,0x9A,0x40,0x61,0x50,0xC3,0x50,0x51,0x7E,0x9F,0xEF,0x43,0x43, +0x00,0x00,0xFB,0x02,0xEF,0x53,0xE2,0xFF,0xFF,0xD0,0x00,0x50,0x05,0xD4,0x50,0x95, +0xCB,0xCA,0x00,0x13,0x0A,0xE0,0x14,0x6B,0x04,0xD0,0x27,0x50,0x05,0xD6,0x50,0xDD, +0x50,0xFB,0x01,0xEF,0x4A,0x04,0x00,0x00,0x05,0xDD,0x00,0xFB,0x01,0xEF,0x4A,0x06, +0x00,0x00,0x05,0xDD,0x00,0xFB,0x01,0xEF,0xFA,0xB0,0xFF,0xFF,0x05,0xDD,0x00,0xFB, +0x01,0xEF,0xE8,0xB1,0xFF,0xFF,0x05,0x30,0x89,0xC5,0xD0,0x00,0x50,0x05,0x00,0x00, +0xB5,0x8F,0x00,0x00,0xD0,0xAC,0x08,0x51,0x11,0x16,0x9A,0x61,0x50,0xE1,0x50,0xAC, +0x04,0x0B,0xD3,0xA1,0x01,0xAC,0x04,0x13,0x04,0xD0,0x27,0x50,0x04,0xC0,0x05,0x51, +0x91,0x61,0x1D,0x12,0xE5,0xD0,0x00,0x50,0x04,0xBB,0x8F,0x60,0x0E,0xD0,0x8F,0x1C, +0x05,0x14,0x20,0x5B,0xC2,0x11,0x5E,0xD0,0x5E,0x59,0x95,0xCB,0xC9,0x00,0x12,0x0C, +0x95,0xCB,0xCA,0x00,0x13,0x06,0xD0,0x22,0x50,0x31,0x15,0x02,0x81,0xCB,0xC9,0x00, +0xCB,0xCA,0x00,0x50,0x12,0x0F,0xDE,0xEF,0xB0,0xC7,0xFF,0xFF,0xCB,0x81,0x00,0xD0, +0x00,0x50,0x31,0xFC,0x01,0x30,0x28,0x0A,0xD5,0x50,0x13,0x03,0x31,0xF2,0x01,0xDE, +0x7E,0x7E,0x7F,0xCB,0x81,0x00,0x9F,0xEF,0x18,0x45,0x00,0x00,0xFB,0x03,0xEF,0x9F, +0xDB,0xFF,0xFF,0x9E,0xEF,0x0B,0x45,0x00,0x00,0x5A,0xC0,0x8E,0x5A,0x8F,0x50,0x00, +0x01,0x0A,0x00,0x10,0x00,0xD0,0x36,0x50,0x31,0xC6,0x01,0xD0,0x22,0x50,0x31,0xC0, +0x01,0x9E,0xEF,0x55,0x42,0x00,0x00,0xCB,0x81,0x00,0xC0,0x6A,0xCB,0x81,0x00,0x91, +0xCB,0xC9,0x00,0xAA,0x08,0x18,0x06,0xD0,0x37,0x50,0x31,0xA4,0x01,0x91,0xCB,0xC9, +0x00,0xAA,0x09,0x15,0x06,0xD0,0x35,0x50,0x31,0x96,0x01,0xCB,0xAA,0x04,0x6B,0x50, +0x13,0x06,0xD0,0x27,0x50,0x31,0x89,0x01,0x28,0x09,0xAA,0x0A,0xA9,0x08,0xD4,0x56, +0x11,0x2A,0x7D,0x46,0xCB,0x81,0x00,0x69,0x7C,0x46,0xCB,0x81,0x00,0x8F,0x46,0xA9, +0x08,0x00,0x0B,0x4A,0x00,0x56,0x00,0x78,0x00,0x95,0x00,0x9A,0x00,0x9F,0x00,0xA4, +0x00,0xA9,0x00,0x4D,0x00,0xE4,0x00,0x30,0x01,0xC8,0x00,0x00,0xD6,0x56,0x91,0x56, +0xCB,0xC9,0x00,0x19,0xCD,0x30,0x51,0x01,0xC9,0xAA,0x04,0x8F,0xE0,0xFB,0x00,0x00, +0x50,0xCB,0x50,0x6B,0x50,0x13,0x06,0xD0,0x27,0x50,0x31,0x34,0x01,0xDF,0xEF,0x14, +0x4C,0x00,0x00,0xDD,0x6B,0xFB,0x02,0xCF,0xC8,0xFE,0x31,0x24,0x01,0x31,0xCC,0xFF, +0x7D,0x69,0x46,0xCB,0x81,0x00,0x31,0xC3,0xFF,0x9F,0x69,0xDD,0x56,0xFB,0x02,0xEF, +0x48,0x01,0x00,0x00,0xD5,0x50,0x13,0x03,0x31,0x06,0x01,0x7D,0x46,0xCB,0x81,0x00, +0x50,0xD0,0x50,0xAB,0x14,0x30,0x01,0x01,0x31,0xA1,0xFF,0x7F,0x46,0xCB,0x81,0x00, +0xDD,0xAB,0x08,0x9F,0x69,0xDD,0x10,0xFB,0x04,0xEF,0x00,0xC6,0xFF,0xFF,0xD5,0x50, +0x13,0x03,0x31,0xDC,0x00,0x31,0x84,0xFF,0xD0,0x01,0x50,0x11,0x12,0xD0,0x02,0x50, +0x11,0x0D,0xD0,0x04,0x50,0x11,0x08,0xD0,0x08,0x50,0x11,0x03,0xD0,0x10,0x50,0x7F, +0x46,0xCB,0x81,0x00,0xDD,0x50,0x9F,0x69,0xDD,0x10,0xFB,0x04,0xEF,0xCD,0xC5,0xFF, +0xFF,0xD5,0x50,0x13,0x03,0x31,0xA9,0x00,0x31,0x51,0xFF,0x7F,0x46,0xCB,0x81,0x00, +0xDD,0x04,0x9F,0x69,0xDD,0x0A,0xFB,0x04,0xEF,0xB1,0xC5,0xFF,0xFF,0xD5,0x50,0x13, +0x03,0x31,0x8D,0x00,0x31,0x35,0xFF,0x7C,0x7E,0x9F,0x6E,0x9F,0x69,0x9F,0xEF,0x82, +0x42,0x00,0x00,0xFB,0x03,0xEF,0x38,0xDA,0xFF,0xFF,0x7D,0x8E,0x54,0x8F,0x50,0x00, +0x01,0x0A,0x00,0x10,0x00,0xD0,0x36,0x50,0x31,0x66,0x00,0xD0,0x22,0x50,0x31,0x60, +0x00,0x9E,0xEF,0x5E,0x42,0x00,0x00,0x51,0xC0,0x51,0x54,0x7D,0x54,0x46,0xCB,0x81, +0x00,0x9E,0xEF,0x4E,0x42,0x00,0x00,0x50,0xC0,0x50,0x55,0x28,0x09,0x65,0xA9,0x08, +0x31,0xE9,0xFE,0xDE,0x7E,0x7E,0x9F,0x69,0x9F,0xEF,0xC3,0x42,0x00,0x00,0xFB,0x03, +0xEF,0xED,0xD9,0xFF,0xFF,0xD0,0x8E,0x51,0x8F,0x50,0x00,0x01,0x0A,0x00,0x10,0x00, +0xD0,0x36,0x50,0x31,0x1B,0x00,0xD0,0x22,0x50,0x31,0x15,0x00,0x9E,0xEF,0x9F,0x42, +0x00,0x00,0x52,0xD4,0x53,0xC0,0x51,0x52,0x7D,0x52,0x46,0xCB,0x81,0x00,0x31,0xAB, +0xFE,0xC0,0x11,0x5E,0xBA,0x8F,0x60,0x0E,0x05,0xD3,0x8F,0x00,0xF8,0x00,0x00,0x6B, +0x12,0x0C,0xCB,0x8F,0xFF,0x07,0xFF,0xFF,0xAB,0x04,0x50,0xC8,0x50,0x6B,0xD3,0x8F, +0xE0,0x03,0x00,0x00,0x6B,0x12,0x0C,0xCB,0x8F,0x1F,0xFC,0xFF,0xFF,0xAB,0x04,0x50, +0xC8,0x50,0x6B,0xEF,0x05,0x05,0x6B,0xAB,0x08,0x05,0xB5,0x8F,0x3C,0x08,0xD0,0x8F, +0x1C,0x05,0x14,0x20,0x5B,0xD0,0xAC,0x04,0x54,0xD0,0xAC,0x08,0x55,0x7C,0x44,0xCB, +0x81,0x00,0x7F,0x44,0xCB,0x81,0x00,0xDD,0x04,0xDD,0x55,0xDD,0x10,0xFB,0x04,0xEF, +0xBA,0xC4,0xFF,0xFF,0xD5,0x50,0x12,0x01,0x04,0x7C,0x7E,0x9F,0x6E,0xDD,0x55,0x9F, +0xEF,0xA5,0x43,0x00,0x00,0xFB,0x03,0xEF,0x46,0xD9,0xFF,0xFF,0x7D,0x8E,0x52,0x8F, +0x50,0x00,0x01,0x17,0x00,0x08,0x00,0xD0,0x29,0x50,0x04,0xC8,0x53,0x6B,0xD4,0x53, +0x7D,0x52,0x44,0xCB,0x81,0x00,0xD0,0x00,0x50,0x04,0x30,0x6C,0xFF,0xDE,0x7E,0x7E, +0xDD,0x55,0x9F,0xEF,0xCB,0x4A,0x00,0x00,0xFB,0x03,0xEF,0x13,0xD9,0xFF,0xFF,0xD0, +0x8E,0x51,0xD1,0x50,0x01,0x13,0x04,0xD0,0x29,0x50,0x04,0x8F,0x51,0x00,0x03,0x09, +0x00,0x19,0x00,0x2D,0x00,0x3D,0x00,0x00,0xD0,0xAB,0x14,0x50,0xD4,0x51,0x7D,0x50, +0x44,0xCB,0x81,0x00,0xD0,0x00,0x50,0x04,0x30,0xE9,0xC5,0xC3,0x50,0xAB,0x2C,0x50, +0xD4,0x51,0x7D,0x50,0x44,0xCB,0x81,0x00,0xD0,0x00,0x50,0x04,0xD0,0xAB,0x2C,0x50, +0xD4,0x51,0x7D,0x50,0x44,0xCB,0x81,0x00,0xD0,0x00,0x50,0x04,0x7D,0xAB,0x0C,0x44, +0xCB,0x81,0x00,0xD0,0x00,0x50,0x04,0x00,0x9F,0xEF,0x86,0x4A,0x00,0x00,0xFB,0x01, +0xEF,0x97,0xDE,0xFF,0xFF,0xD0,0x00,0x50,0x05,0x00,0x00,0x00,0xB5,0x8F,0xF0,0x00, +0xD0,0x8F,0x20,0x19,0x00,0x20,0x57,0x30,0xF1,0x04,0xE9,0x50,0x21,0xB0,0x02,0xA7, +0x0E,0x16,0xEF,0xDE,0xC2,0xFF,0xFF,0xB4,0xA7,0x0E,0xD0,0x02,0x56,0x9E,0xEF,0x11, +0x50,0x00,0x00,0x55,0x9E,0xEF,0x2A,0x50,0x00,0x00,0x54,0x30,0x36,0x00,0xD0,0x8F, +0x30,0x19,0x00,0x20,0x57,0x30,0xC3,0x04,0xE9,0x50,0x21,0xB0,0x02,0xA7,0x0E,0x16, +0xEF,0xB0,0xC2,0xFF,0xFF,0xB4,0xA7,0x0E,0xD0,0x02,0x56,0x9E,0xEF,0x08,0x50,0x00, +0x00,0x55,0x9E,0xEF,0x21,0x50,0x00,0x00,0x54,0x30,0x08,0x00,0x9A,0x00,0x50,0xD0, +0xAC,0x04,0x51,0x04,0xE0,0x01,0xAC,0x04,0x12,0xE2,0x00,0xAC,0x04,0x03,0xC0,0x02, +0x55,0xD0,0x55,0x7E,0xFB,0x01,0xEF,0x11,0xDE,0xFF,0xFF,0x9A,0x06,0x50,0xC5,0x05, +0x56,0x51,0xC0,0x51,0x57,0x9A,0x67,0x7E,0xC2,0x56,0x57,0xF5,0x50,0xF7,0xD0,0x54, +0x7E,0x9E,0xEF,0xE7,0x4F,0x00,0x00,0x7E,0xFB,0x08,0xEF,0xED,0xDD,0xFF,0xFF,0x05, +0xB5,0x8F,0xFC,0x03,0xD0,0x8F,0x00,0x01,0x08,0x20,0x57,0xD4,0x58,0xD4,0x59,0xE1, +0x1F,0x67,0x70,0xEF,0x00,0x02,0x67,0x50,0x13,0x69,0xD7,0x50,0xE1,0x02,0x67,0x03, +0xC0,0x02,0x50,0x78,0x50,0x01,0x56,0xEF,0x16,0x04,0x67,0x50,0x78,0x16,0x50,0x55, +0x30,0x7F,0x00,0xD0,0x54,0x7E,0xD0,0x56,0x7E,0x78,0x14,0x56,0x50,0xD7,0x50,0xC1, +0x50,0x55,0x7E,0xD0,0x55,0x7E,0xC3,0x8F,0x00,0x01,0x08,0x20,0x57,0x50,0x78,0x8F, +0xFC,0x50,0x52,0xD0,0x52,0x7E,0x9E,0xEF,0xA8,0x4F,0x00,0x00,0x7E,0xFB,0x05,0xEF, +0x88,0xDD,0xFF,0xFF,0xC0,0x28,0x52,0xE1,0x52,0x9F,0x58,0x07,0x14,0x20,0x0E,0x9E, +0xEF,0xBD,0x4F,0x00,0x00,0x7E,0xFB,0x01,0xEF,0x6F,0xDD,0xFF,0xFF,0xC0,0x56,0x58, +0xC0,0x54,0x59,0xC0,0x10,0x57,0xD1,0x57,0x8F,0x40,0x01,0x08,0x20,0x1F,0x80,0xD0, +0x53,0x7E,0xD0,0x59,0x7E,0x16,0xEF,0xB4,0x41,0x01,0x00,0x01,0x01,0x01,0x01,0xFB, +0x04,0xEF,0x46,0xDD,0xFF,0xFF,0xD5,0xAC,0x04,0x13,0x03,0x30,0x5B,0x00,0x9A,0x00, +0x50,0x04,0x78,0x8F,0xF4,0x55,0x50,0xC0,0x9F,0x74,0x07,0x14,0x20,0x50,0x78,0x06, +0x56,0x51,0xD4,0x54,0xD2,0x80,0x52,0x13,0x0C,0xD5,0x52,0x18,0x02,0xD6,0x54,0x78, +0x01,0x52,0x52,0x12,0xF4,0xF5,0x51,0xEC,0x78,0x14,0x56,0x50,0xC0,0x55,0x50,0xD1, +0x50,0x9F,0x74,0x07,0x14,0x20,0x1F,0x20,0xC1,0x8F,0x00,0x80,0x00,0x00,0x9F,0x7C, +0x07,0x14,0x20,0x51,0xD1,0x55,0x51,0x1E,0x0F,0xC2,0x9F,0x74,0x07,0x14,0x20,0x51, +0x78,0x8F,0xF7,0x51,0x53,0xC2,0x53,0x54,0x05,0xD0,0x9F,0x74,0x07,0x14,0x20,0x52, +0x16,0xEF,0x57,0x41,0x01,0x00,0x01,0x9E,0xEF,0x8F,0x4F,0x00,0x00,0x7E,0xFB,0x01, +0xEF,0xC7,0xDC,0xFF,0xFF,0x78,0x8F,0xF7,0x53,0x7E,0x9E,0x43,0xA2,0xFF,0x7E,0xD0, +0x52,0x7E,0x9E,0xEF,0x5A,0x4F,0x00,0x00,0x7E,0xFB,0x04,0xEF,0xAC,0xDC,0xFF,0xFF, +0x9E,0xEF,0x78,0x4F,0x00,0x00,0x7E,0xFB,0x01,0xEF,0x9E,0xDC,0xFF,0xFF,0xD0,0x20, +0x7E,0x9E,0x43,0xC2,0xFF,0x3F,0x7E,0x9E,0x43,0x62,0x7E,0x9E,0xEF,0x31,0x4F,0x00, +0x00,0x7E,0xFB,0x04,0xEF,0x83,0xDC,0xFF,0xFF,0x9E,0xEF,0x68,0x4F,0x00,0x00,0x7E, +0xFB,0x01,0xEF,0x75,0xDC,0xFF,0xFF,0xD0,0x8F,0x40,0x00,0x00,0x00,0x7E,0x9E,0x43, +0xE2,0xFF,0xBF,0x00,0x00,0x57,0xD0,0x57,0x7E,0x9E,0x43,0xC2,0x00,0x40,0x7E,0x9E, +0xEF,0xFD,0x4E,0x00,0x00,0x7E,0xFB,0x04,0xEF,0x4F,0xDC,0xFF,0xFF,0x78,0x03,0x53, +0x53,0xD4,0x54,0x78,0x8F,0xF7,0x52,0x56,0x78,0x8F,0xF7,0x57,0x57,0x9E,0xEF,0x31, +0x4F,0x00,0x00,0x7E,0xFB,0x01,0xEF,0x31,0xDC,0xFF,0xFF,0xD1,0x54,0x56,0x1F,0x05, +0xD1,0x54,0x57,0x1B,0x3F,0xE0,0x54,0x62,0x3B,0xD0,0x54,0x55,0x11,0x12,0xD1,0x54, +0x56,0x1F,0x05,0xD1,0x54,0x57,0x1B,0x04,0xE1,0x54,0x62,0x04,0x10,0x07,0x11,0x24, +0xD6,0x54,0xF5,0x53,0xE9,0xC3,0x55,0x54,0x7E,0x78,0x09,0x54,0x50,0xC3,0x01,0x50, +0x7E,0x78,0x09,0x55,0x7E,0x9E,0xEF,0x97,0x4E,0x00,0x00,0x7E,0xFB,0x04,0xEF,0xE9, +0xDB,0xFF,0xFF,0x05,0xD6,0x54,0xF5,0x53,0xB2,0x05,0xB5,0x8F,0xFC,0x00,0xEF,0x02, +0x02,0x9F,0x04,0x40,0x08,0x20,0x52,0xB0,0x42,0x9F,0x40,0x1F,0x00,0x20,0x53,0xA8, +0x20,0x42,0x9F,0x40,0x1F,0x00,0x20,0xBB,0x8F,0x08,0x20,0xC2,0x0A,0x5E,0xB0,0x8F, +0x08,0x20,0xAE,0x08,0x9E,0xEF,0x12,0x00,0x00,0x00,0x6E,0xD0,0x9F,0xAE,0x04,0x14, +0x20,0xAE,0x04,0xD0,0x5E,0x9F,0xAE,0x04,0x14,0x20,0x11,0x07,0x9A,0x8F,0x8D,0x50, +0x31,0x76,0x00,0x30,0x92,0x00,0xD0,0x8F,0x00,0x00,0x00,0x20,0x57,0x30,0xAB,0xD9, +0x30,0xF8,0x01,0xE9,0x50,0x03,0x30,0xB3,0x00,0xC0,0x02,0x57,0xD1,0x57,0x8F,0x00, +0x20,0x00,0x20,0x1F,0xE8,0x30,0x8A,0x00,0xD0,0x8F,0x00,0x00,0x00,0x30,0x57,0xD0, +0x8F,0x00,0x80,0x08,0x20,0x56,0xD4,0x55,0x30,0x80,0xD9,0xD0,0x66,0x54,0xD4,0x66, +0x30,0xC8,0x01,0xD0,0x54,0x86,0xE9,0x50,0x09,0xD5,0x55,0x12,0x0E,0xD0,0x57,0x55, +0x11,0x09,0xD5,0x55,0x13,0x05,0x30,0xA6,0x00,0xD4,0x55,0xC0,0x8F,0x00,0x02,0x00, +0x00,0x57,0xD1,0x57,0x8F,0x00,0x00,0x40,0x30,0x1F,0xCD,0xD5,0x55,0x13,0x03,0x30, +0x8D,0x00,0x9A,0x00,0x50,0xD0,0xAC,0x04,0x51,0xD0,0xAE,0x04,0x9F,0xAE,0x04,0x14, +0x20,0xC0,0x08,0x5E,0xBA,0x8E,0xEF,0x02,0x02,0x9F,0x04,0x40,0x08,0x20,0x52,0xB0, +0x53,0x42,0x9F,0x40,0x1F,0x00,0x20,0x04,0x9E,0xEF,0x0C,0x4E,0x00,0x00,0x50,0xE2, +0x00,0xAC,0x04,0x03,0xC0,0x02,0x50,0xD0,0x50,0x7E,0xFB,0x01,0xEF,0xEB,0xDA,0xFF, +0xFF,0x05,0x9E,0xEF,0x0D,0x4E,0x00,0x00,0x50,0xE2,0x00,0xAC,0x04,0x03,0xC0,0x02, +0x50,0xD0,0x50,0x7E,0xFB,0x01,0xEF,0xD1,0xDA,0xFF,0xFF,0x05,0x3C,0x51,0x7E,0xCB, +0x8F,0x00,0xE0,0xFF,0xFF,0x57,0x50,0xC9,0x8F,0x00,0xE0,0x03,0x00,0x50,0x7E,0xD0, +0x57,0x7E,0x9E,0xEF,0xFB,0x4D,0x00,0x00,0x7E,0xFB,0x04,0xEF,0xAC,0xDA,0xFF,0xFF, +0x30,0x36,0x00,0x9A,0x8F,0x8C,0x7E,0xFB,0x01,0xEF,0x9E,0xDA,0xFF,0xFF,0x05,0xC3, +0x01,0x57,0x50,0xCB,0x8F,0x00,0x00,0xC0,0xFF,0x50,0x7E,0xCB,0x8F,0x00,0x00,0xC0, +0xFF,0x55,0x7E,0xC3,0x01,0x57,0x7E,0xD0,0x55,0x7E,0x9E,0xEF,0xD7,0x4D,0x00,0x00, +0x7E,0xFB,0x05,0xEF,0x74,0xDA,0xFF,0xFF,0x05,0xBB,0x8F,0xFC,0x03,0xD0,0x57,0x59, +0xD0,0x8F,0x08,0x00,0x00,0x20,0x55,0xD0,0x8F,0xC0,0x00,0x00,0x00,0x54,0xD0,0x01, +0x52,0x9E,0xEF,0x23,0x5A,0x00,0x00,0x50,0xC1,0x42,0xA0,0xFC,0x50,0x58,0x3C,0xA8, +0x01,0x50,0xC0,0x50,0x55,0xCA,0x50,0x55,0xD4,0x53,0x9A,0xA8,0x04,0x50,0xD1,0x53, +0x50,0x1E,0x14,0x3C,0x43,0xA8,0x06,0x57,0xC8,0x8F,0x00,0x00,0x00,0x20,0x57,0x30, +0x99,0x00,0xE9,0x50,0x54,0x11,0x19,0xD0,0x55,0x57,0x30,0x8E,0x00,0xE9,0x50,0x73, +0x3C,0xA8,0x01,0x50,0x13,0x6D,0xC0,0x57,0x50,0xD6,0x50,0xD0,0x50,0x55,0x11,0x20, +0xD4,0x56,0x95,0x68,0x13,0x33,0x9A,0xA8,0x05,0x51,0xD1,0x53,0x51,0x1E,0x11,0x9A, +0xA8,0x04,0x50,0xC0,0x50,0x53,0x3C,0x43,0xA8,0x06,0x56,0xC2,0x50,0x53,0x11,0x19, +0x9A,0xA8,0x03,0x50,0x13,0x3D,0xC1,0x50,0x54,0x56,0xCA,0x50,0x56,0x9A,0x68,0x51, +0xC4,0x04,0x51,0xC0,0x56,0x51,0xD0,0x51,0x54,0xD1,0x57,0x59,0x12,0x20,0xD0,0x56, +0x7E,0x9E,0xEF,0x3F,0x4D,0x00,0x00,0x7E,0xFB,0x02,0xEF,0xBD,0xD9,0xFF,0xFF,0xDD, +0x8F,0xFF,0xFF,0x00,0x80,0xDD,0x52,0xFB,0x02,0xEF,0xE1,0xC8,0xFF,0xFF,0xD6,0x53, +0x31,0x67,0xFF,0xB5,0xA8,0x01,0x13,0x03,0xC0,0x02,0x55,0xF2,0x8F,0x4E,0x00,0x00, +0x00,0x52,0x05,0xBA,0x8F,0xFC,0x03,0x05,0x31,0x36,0xFF,0xB4,0x7E,0x3F,0x6E,0xDD, +0x8F,0x40,0x40,0x00,0x00,0xDD,0x57,0xFB,0x03,0xEF,0x0C,0xC9,0xFF,0xFF,0xB0,0x8E, +0x51,0xD5,0x50,0x12,0x04,0x9A,0x01,0x50,0x05,0xD4,0x9F,0x06,0x05,0x14,0x20,0x3C, +0x8F,0xBC,0x02,0x50,0x05,0x00,0x00,0x00,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0x30, +0xAC,0x01,0xBB,0x8F,0x00,0x38,0xC2,0x0A,0x5E,0xB0,0x8F,0x00,0x38,0xAE,0x08,0x9E, +0xEF,0x12,0x00,0x00,0x00,0x6E,0xD0,0x9F,0xAE,0x04,0x14,0x20,0xAE,0x04,0xD0,0x5E, +0x9F,0xAE,0x04,0x14,0x20,0x11,0x1C,0xD0,0xAE,0x04,0x9F,0xAE,0x04,0x14,0x20,0xC0, +0x08,0x5E,0xBA,0x8E,0xDD,0x8F,0x8D,0x00,0x00,0x00,0xFB,0x01,0xEF,0x1B,0xD9,0xFF, +0xFF,0x11,0xBF,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0xCB,0x8F,0x1F,0x04,0xFF,0xFF, +0x6B,0xAB,0x04,0xD4,0x9F,0x06,0x05,0x14,0x20,0xD4,0x9F,0x0A,0x05,0x14,0x20,0xDD, +0x00,0xDD,0x8F,0x51,0x00,0x00,0x00,0x9F,0xAB,0x30,0xDD,0x8F,0x99,0x00,0x00,0x00, +0x9E,0xAF,0x85,0x50,0xD1,0x50,0x8F,0x00,0x00,0x04,0x20,0x1E,0x07,0x9E,0xEF,0x45, +0x4D,0x00,0x00,0x6E,0xFB,0x04,0xEF,0x9B,0xD3,0xFF,0xFF,0x30,0x53,0x00,0xE8,0x50, +0xB2,0xD2,0x03,0x9F,0x30,0x00,0x14,0x20,0xFA,0xEF,0x5C,0x4D,0x00,0x00,0xEF,0x4F, +0x02,0x00,0x00,0x30,0x3B,0x00,0xE8,0x50,0x9A,0x30,0xCD,0xF6,0x30,0x32,0x00,0xE8, +0x50,0x91,0x16,0xDB,0x81,0x00,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0x30,0x21,0x00, +0xE8,0x50,0x80,0x95,0x9F,0x16,0x05,0x14,0x20,0x13,0x02,0x11,0x03,0x31,0x73,0xFF, +0xD0,0xAE,0x04,0x9F,0xAE,0x04,0x14,0x20,0xC0,0x08,0x5E,0xBA,0x8E,0xD0,0x00,0x50, +0x05,0xD0,0x50,0x9F,0x0A,0x05,0x14,0x20,0xD5,0x50,0x12,0x07,0xD4,0x9F,0x06,0x05, +0x14,0x20,0x05,0xDD,0x52,0xDD,0x5B,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0x8A,0x01, +0x9F,0xA5,0x04,0x14,0x20,0xDD,0x50,0xFB,0x01,0xEF,0x4E,0xD8,0xFF,0xFF,0xD4,0x52, +0x11,0x14,0xDD,0x42,0x9F,0x0A,0x05,0x14,0x20,0x9F,0xEF,0xB5,0x4C,0x00,0x00,0xFB, +0x02,0xEF,0x36,0xD8,0xFF,0xFF,0xF3,0x9F,0x06,0x05,0x14,0x20,0x52,0xE4,0xDD,0x8F, +0x8C,0x00,0x00,0x00,0xFB,0x01,0xEF,0x21,0xD8,0xFF,0xFF,0xD0,0xAB,0x04,0x6B,0xD0, +0xAB,0x2C,0xAB,0x14,0xD4,0x9F,0x06,0x05,0x14,0x20,0xD0,0x01,0x50,0xD0,0x8E,0x5B, +0xD0,0x8E,0x52,0x05,0xC2,0x04,0x9F,0xEA,0x04,0x14,0x20,0xDF,0x9F,0xF2,0x04,0x14, +0x20,0xDD,0x8F,0x80,0x20,0x02,0x00,0xDD,0x9F,0xEA,0x04,0x14,0x20,0xFB,0x03,0xEF, +0x84,0xC7,0xFF,0xFF,0xD5,0x50,0x12,0x2F,0xC2,0x04,0x9F,0xEA,0x04,0x14,0x20,0xDF, +0x9F,0xEE,0x04,0x14,0x20,0xDD,0x8F,0x80,0x20,0x02,0x00,0xDD,0x9F,0xEA,0x04,0x14, +0x20,0xFB,0x03,0xEF,0x60,0xC7,0xFF,0xFF,0xD5,0x50,0x12,0x0B,0x90,0x01,0x9F,0x16, +0x05,0x14,0x20,0xD0,0x00,0x50,0x05,0x94,0x9F,0x16,0x05,0x14,0x20,0x05,0xDD,0x5B, +0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0xD0,0x8F,0x80,0x40,0x00,0x00,0x6B,0xD0,0x8F, +0x80,0x40,0x00,0x00,0xAB,0x04,0xD4,0xAB,0x14,0xD4,0xAB,0x2C,0xD0,0x04,0xAB,0x08, +0x7C,0xAB,0x0C,0xD4,0xAB,0x20,0x94,0x9F,0x16,0x05,0x14,0x20,0xD0,0x8E,0x5B,0x05, +0xBB,0x8F,0x00,0x08,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0xD4,0x6B,0x2C,0x00,0x9F, +0x00,0x00,0x00,0x00,0x00,0x10,0xAB,0x1C,0x9E,0xCB,0xCB,0x00,0x53,0x90,0xCB,0xCA, +0x00,0x52,0x11,0x12,0xDD,0x53,0xFB,0x01,0xEF,0x16,0x00,0x00,0x00,0xD5,0x50,0x13, +0x02,0x11,0x09,0xC0,0x08,0x53,0xF4,0x52,0xEB,0xD0,0x00,0x50,0xBA,0x8F,0x00,0x08, +0x05,0xB5,0x8F,0xFC,0x08,0xD0,0x8F,0x1C,0x05,0x14,0x20,0x5B,0xD0,0xAC,0x04,0x57, +0xB5,0x67,0x12,0x04,0xD0,0x28,0x50,0x04,0xC2,0x08,0x5E,0xD4,0x56,0x3A,0x3A,0x67, +0xB7,0x04,0x13,0x0F,0xD0,0x5E,0x56,0xC1,0x01,0x51,0xA6,0x04,0xA3,0x01,0x50,0x66, +0xA2,0x50,0x67,0xDD,0x00,0x9F,0x6E,0xDD,0x57,0x9F,0xEF,0x81,0x4A,0x00,0x00,0xFB, +0x03,0xEF,0x0C,0xD1,0xFF,0xFF,0xD0,0x8E,0x55,0x8F,0x50,0x00,0x01,0x08,0x00,0x2D, +0x00,0xD0,0x32,0x50,0x04,0xDD,0x00,0x9F,0x6E,0xDD,0x04,0xDD,0xAC,0x04,0xDD,0x10, +0xFB,0x04,0xEF,0x47,0xBC,0xFF,0xFF,0xD0,0x8E,0x54,0xD5,0x50,0x12,0x08,0xD0,0x54, +0xAB,0x1C,0xC8,0x01,0x6B,0x04,0xD0,0x28,0x50,0x04,0xCB,0x8F,0x00,0x00,0x00,0xF0, +0x55,0x50,0xE2,0x50,0x6B,0x00,0xE0,0x1C,0x55,0x08,0xD5,0x56,0x13,0x04,0xD0,0x31, +0x50,0x04,0xE1,0x1C,0x55,0x0C,0xD5,0x56,0x12,0x04,0xD0,0x33,0x50,0x04,0xB5,0x66, +0x13,0xF8,0xD5,0x56,0x13,0x26,0xDD,0x00,0x9F,0x6E,0xDD,0x04,0xDD,0x56,0xDD,0x10, +0xFB,0x04,0xEF,0xF7,0xBB,0xFF,0xFF,0xD0,0x8E,0x54,0xD5,0x50,0x13,0x01,0x04,0xCB, +0x8F,0x00,0x00,0x00,0xF0,0x55,0x50,0xD0,0x54,0x40,0xAB,0x1C,0xD0,0x00,0x50,0x04, +0xB5,0x8F,0xFC,0x03,0x94,0xBC,0x0C,0x94,0xBC,0x18,0xD0,0xAC,0x1C,0x55,0xD0,0xAC, +0x10,0x56,0xD0,0xAC,0x04,0x58,0xDD,0x58,0xFB,0x01,0xEF,0xAC,0xBB,0xFF,0xFF,0xD0, +0x50,0x59,0x3A,0x21,0x59,0x68,0x94,0x61,0x9A,0x88,0x50,0xD7,0x59,0xE0,0x50,0xEF, +0x98,0x22,0x00,0x00,0xF3,0xD6,0x59,0x95,0x78,0x13,0x62,0x91,0x2F,0x68,0x12,0x19, +0xD6,0x58,0x91,0xBC,0x18,0xAC,0x14,0x18,0x58,0xD0,0x58,0xA5,0x04,0xB4,0x65,0xB4, +0xA5,0x02,0x96,0xBC,0x18,0x94,0x57,0x11,0x16,0x91,0xBC,0x0C,0xAC,0x08,0x18,0x4B, +0xD0,0x58,0xA6,0x04,0xB4,0x66,0xB4,0xA6,0x02,0x96,0xBC,0x0C,0x90,0x01,0x57,0x9A, +0x88,0x50,0xD7,0x59,0xE1,0x50,0xEF,0x6F,0x4A,0x00,0x00,0xF3,0xD6,0x59,0xD7,0x58, +0xE8,0x57,0x0D,0xC3,0xA5,0x04,0x58,0x50,0xB0,0x50,0x65,0xC0,0x08,0x55,0x11,0x0B, +0xC3,0xA6,0x04,0x58,0x50,0xB0,0x50,0x66,0xC0,0x08,0x56,0x11,0x8B,0xD0,0x00,0x50, +0x04,0x94,0xBC,0x0C,0x94,0xBC,0x18,0xD0,0x34,0x50,0x04,0x94,0xBC,0x0C,0x94,0xBC, +0x18,0xD0,0x35,0x50,0x04,0x00,0x00,0x00,0x43,0x43,0x4F,0x4F,0x50,0x50,0x59,0x59, +0x52,0x52,0x49,0x49,0x47,0x47,0x48,0x48,0x54,0x54,0x20,0x20,0x31,0x31,0x39,0x39, +0x38,0x38,0x37,0x37,0x2C,0x2C,0x20,0x20,0x31,0x31,0x39,0x39,0x38,0x38,0x38,0x38, +0x20,0x20,0x44,0x44,0x49,0x49,0x47,0x47,0x49,0x49,0x54,0x54,0x41,0x41,0x4C,0x4C, +0x20,0x20,0x45,0x45,0x51,0x51,0x55,0x55,0x49,0x49,0x50,0x50,0x4D,0x4D,0x45,0x45, +0x4E,0x4E,0x54,0x54,0x20,0x20,0x43,0x43,0x4F,0x4F,0x52,0x52,0x50,0x50,0x4F,0x4F, +0x52,0x52,0x41,0x41,0x54,0x54,0x49,0x49,0x4F,0x4F,0x4E,0x4E,0x20,0x20,0x41,0x41, +0x4C,0x4C,0x4C,0x4C,0x20,0x20,0x52,0x52,0x49,0x49,0x47,0x47,0x48,0x48,0x54,0x54, +0x53,0x53,0x20,0x20,0x52,0x52,0x45,0x45,0x53,0x53,0x45,0x45,0x52,0x52,0x56,0x56, +0x45,0x45,0x44,0x44,0x2E,0x2E,0x43,0x6F,0x70,0x79,0x72,0x69,0x67,0x68,0x74,0x20, +0x31,0x39,0x38,0x38,0x20,0x44,0x69,0x67,0x69,0x74,0x61,0x6C,0x20,0x45,0x71,0x75, +0x69,0x70,0x6D,0x65,0x6E,0x74,0x20,0x43,0x6F,0x72,0x70,0x6F,0x72,0x61,0x74,0x69, +0x6F,0x6E,0x20,0x61,0x6C,0x6C,0x20,0x72,0x69,0x67,0x68,0x74,0x73,0x20,0x72,0x65, +0x73,0x65,0x72,0x76,0x65,0x64,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x50,0x43,0x20,0x3D,0x20,0x25,0x30,0x38,0x58,0x0D,0x0A,0x00,0x03,0x03,0x00,0x01, +0x03,0x03,0x02,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, +0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x01,0x7F,0xFE, +0x80,0x00,0x00,0x02,0x7F,0xF0,0x00,0x06,0x00,0x03,0x67,0xFE,0x08,0x01,0x00,0x04, +0xFF,0xFF,0x00,0x00,0x01,0x05,0xFF,0xFF,0x00,0x00,0x02,0x05,0xFF,0xFF,0x00,0x00, +0x03,0x05,0xFF,0xFF,0x00,0x00,0x04,0x05,0xFF,0xFF,0x00,0x00,0x05,0x06,0x7F,0xE0, +0x00,0x04,0x05,0x08,0x7F,0xE0,0x00,0x14,0x05,0x08,0x7F,0xFE,0x00,0x00,0x05,0x09, +0x77,0xFE,0x08,0x01,0x09,0x0A,0x67,0xFE,0x08,0x01,0x09,0x08,0xFF,0xFF,0x00,0x00, +0x05,0x06,0x00,0xEE,0x80,0x00,0x05,0x06,0x80,0xEE,0x01,0x01,0x07,0x06,0x81,0xEE, +0x10,0x01,0x05,0x06,0x80,0xFE,0x40,0x01,0x05,0x07,0xA0,0xEE,0x00,0x01,0x05,0x07, +0x80,0xEE,0x20,0x11,0x06,0x0A,0xEF,0xFF,0x00,0x00,0x07,0x0A,0xEF,0xFF,0x00,0x00, +0x08,0x0A,0xEF,0xFF,0x00,0x00,0x05,0x08,0xFF,0xFF,0x00,0x00,0x06,0x08,0xFF,0xFF, +0x00,0x00,0x07,0x08,0xFF,0xFF,0x00,0x00,0x08,0x08,0xFF,0xFF,0x00,0x00,0x09,0x08, +0xFF,0xFF,0x00,0x00,0x0A,0x08,0xFF,0xFF,0x00,0x00,0x00,0x00,0x53,0x74,0x61,0x72, +0x74,0x69,0x6E,0x67,0x20,0x44,0x55,0x50,0x20,0x73,0x65,0x72,0x76,0x65,0x72,0x2E, +0x2E,0x2E,0x0D,0x0A,0x00,0x0D,0x0A,0x53,0x74,0x6F,0x70,0x70,0x69,0x6E,0x67,0x20, +0x44,0x55,0x50,0x20,0x73,0x65,0x72,0x76,0x65,0x72,0x2E,0x2E,0x2E,0x0D,0x0A,0x00, +0x0D,0x0A,0x52,0x4C,0x56,0x31,0x32,0x20,0x43,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x6C, +0x65,0x72,0x20,0x25,0x64,0x20,0x28,0x25,0x30,0x36,0x6F,0x29,0x0D,0x0A,0x00,0x2D, +0x44,0x4C,0x25,0x63,0x25,0x64,0x20,0x28,0x52,0x4C,0x30,0x25,0x64,0x29,0x0D,0x0A, +0x00,0x0D,0x0A,0x55,0x51,0x53,0x53,0x50,0x20,0x43,0x6F,0x6E,0x74,0x72,0x6F,0x6C, +0x6C,0x65,0x72,0x20,0x28,0x25,0x30,0x36,0x6F,0x29,0x0D,0x0A,0x00,0x0D,0x0A,0x55, +0x51,0x53,0x53,0x50,0x20,0x44,0x69,0x73,0x6B,0x20,0x43,0x6F,0x6E,0x74,0x72,0x6F, +0x6C,0x6C,0x65,0x72,0x20,0x25,0x64,0x20,0x28,0x25,0x30,0x36,0x6F,0x29,0x0D,0x0A, +0x00,0x0D,0x0A,0x55,0x51,0x53,0x53,0x50,0x20,0x54,0x61,0x70,0x65,0x20,0x43,0x6F, +0x6E,0x74,0x72,0x6F,0x6C,0x6C,0x65,0x72,0x20,0x25,0x64,0x20,0x28,0x25,0x30,0x36, +0x6F,0x29,0x0D,0x0A,0x00,0x2D,0x25,0x63,0x55,0x25,0x63,0x25,0x64,0x20,0x28,0x25, +0x63,0x25,0x63,0x25,0x63,0x25,0x64,0x29,0x0D,0x0A,0x00,0x2D,0x3F,0x0D,0x0A,0x00, +0x54,0x68,0x69,0x73,0x20,0x4B,0x46,0x51,0x53,0x41,0x20,0x63,0x61,0x6E,0x20,0x6F, +0x6E,0x6C,0x79,0x20,0x73,0x75,0x70,0x70,0x6F,0x72,0x74,0x20,0x25,0x64,0x20,0x44, +0x53,0x53,0x49,0x20,0x64,0x65,0x76,0x69,0x63,0x65,0x73,0x21,0x0D,0x0A,0x0A,0x00, +0x50,0x72,0x6F,0x67,0x72,0x61,0x6D,0x6D,0x69,0x6E,0x67,0x20,0x74,0x68,0x65,0x20, +0x4B,0x46,0x51,0x53,0x41,0x2E,0x2E,0x2E,0x0D,0x0A,0x00,0x50,0x72,0x6F,0x67,0x72, +0x61,0x6D,0x6D,0x69,0x6E,0x67,0x20,0x66,0x61,0x69,0x6C,0x65,0x64,0x2E,0x0D,0x0A, +0x00,0x0D,0x0A,0x45,0x6E,0x74,0x65,0x72,0x20,0x53,0x45,0x54,0x2C,0x20,0x43,0x4C, +0x45,0x41,0x52,0x2C,0x20,0x53,0x48,0x4F,0x57,0x2C,0x20,0x48,0x45,0x4C,0x50,0x2C, +0x20,0x45,0x58,0x49,0x54,0x2C,0x20,0x6F,0x72,0x20,0x51,0x55,0x49,0x54,0x0D,0x0A, +0x0A,0x00,0x54,0x6F,0x6F,0x20,0x6D,0x61,0x6E,0x79,0x20,0x66,0x69,0x65,0x6C,0x64, +0x73,0x0D,0x0A,0x00,0x54,0x6F,0x6F,0x20,0x6D,0x61,0x6E,0x79,0x20,0x71,0x75,0x61, +0x6C,0x69,0x66,0x69,0x65,0x72,0x73,0x0D,0x0A,0x00,0x43,0x6F,0x6D,0x6D,0x61,0x6E, +0x64,0x20,0x69,0x73,0x20,0x61,0x6D,0x62,0x69,0x67,0x75,0x6F,0x75,0x73,0x0D,0x0A, +0x00,0x43,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x69,0x73,0x20,0x75,0x6E,0x6B,0x6E, +0x6F,0x77,0x6E,0x0D,0x0A,0x00,0x51,0x75,0x61,0x6C,0x69,0x66,0x69,0x65,0x72,0x20, +0x69,0x73,0x20,0x75,0x6E,0x72,0x65,0x63,0x6F,0x67,0x6E,0x69,0x7A,0x65,0x64,0x0D, +0x0A,0x00,0x4E,0x6F,0x64,0x65,0x20,0x69,0x73,0x20,0x69,0x6E,0x76,0x61,0x6C,0x69, +0x64,0x0D,0x0A,0x00,0x43,0x53,0x52,0x20,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x20, +0x69,0x73,0x20,0x69,0x6E,0x76,0x61,0x6C,0x69,0x64,0x0D,0x0A,0x00,0x4D,0x6F,0x64, +0x65,0x6C,0x20,0x69,0x73,0x20,0x69,0x6E,0x76,0x61,0x6C,0x69,0x64,0x0D,0x0A,0x00, +0x4E,0x6F,0x64,0x65,0x20,0x69,0x73,0x20,0x69,0x6E,0x20,0x75,0x73,0x65,0x20,0x62, +0x79,0x20,0x61,0x20,0x44,0x53,0x53,0x49,0x20,0x64,0x65,0x76,0x69,0x63,0x65,0x0D, +0x0A,0x00,0x4E,0x6F,0x64,0x65,0x20,0x69,0x73,0x20,0x69,0x6E,0x20,0x75,0x73,0x65, +0x20,0x62,0x79,0x20,0x74,0x68,0x65,0x20,0x4B,0x46,0x51,0x53,0x41,0x0D,0x0A,0x00, +0x43,0x53,0x52,0x20,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x20,0x69,0x73,0x20,0x69, +0x6E,0x20,0x75,0x73,0x65,0x20,0x62,0x79,0x20,0x61,0x6E,0x6F,0x74,0x68,0x65,0x72, +0x20,0x44,0x53,0x53,0x49,0x20,0x64,0x65,0x76,0x69,0x63,0x65,0x0D,0x0A,0x00,0x43, +0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x20,0x74,0x61,0x62, +0x6C,0x65,0x20,0x69,0x73,0x20,0x66,0x75,0x6C,0x6C,0x0D,0x0A,0x00,0x4B,0x46,0x51, +0x53,0x41,0x20,0x63,0x61,0x6E,0x27,0x74,0x20,0x62,0x65,0x20,0x64,0x69,0x73,0x61, +0x62,0x6C,0x65,0x64,0x0D,0x0A,0x00,0x4E,0x6F,0x64,0x65,0x20,0x69,0x73,0x20,0x6E, +0x6F,0x74,0x20,0x69,0x6E,0x20,0x75,0x73,0x65,0x0D,0x0A,0x00,0x4E,0x6F,0x64,0x65, +0x20,0x20,0x20,0x43,0x53,0x52,0x20,0x41,0x64,0x64,0x72,0x65,0x73,0x73,0x20,0x20, +0x20,0x4D,0x6F,0x64,0x65,0x6C,0x0D,0x0A,0x00,0x20,0x25,0x64,0x20,0x20,0x20,0x20, +0x20,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x20,0x4B,0x46,0x51,0x53,0x41,0x20,0x2D,0x2D, +0x2D,0x2D,0x2D,0x2D,0x0D,0x0A,0x00,0x20,0x25,0x64,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x25,0x30,0x36,0x6F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x25,0x64,0x0D,0x0A, +0x00,0x43,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x73,0x3A,0x0D,0x0A,0x20,0x20,0x20,0x20, +0x53,0x45,0x54,0x20,0x3C,0x6E,0x6F,0x64,0x65,0x3E,0x20,0x2F,0x4B,0x46,0x51,0x53, +0x41,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x73,0x65,0x74,0x20,0x4B,0x46,0x51,0x53,0x41,0x20,0x44,0x53, +0x53,0x49,0x20,0x6E,0x6F,0x64,0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x0D,0x0A, +0x20,0x20,0x20,0x20,0x53,0x45,0x54,0x20,0x3C,0x6E,0x6F,0x64,0x65,0x3E,0x20,0x3C, +0x43,0x53,0x52,0x5F,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x3E,0x20,0x3C,0x6D,0x6F, +0x64,0x65,0x6C,0x3E,0x20,0x20,0x20,0x20,0x65,0x6E,0x61,0x62,0x6C,0x65,0x20,0x61, +0x20,0x44,0x53,0x53,0x49,0x20,0x64,0x65,0x76,0x69,0x63,0x65,0x0D,0x0A,0x20,0x20, +0x20,0x20,0x43,0x4C,0x45,0x41,0x52,0x20,0x3C,0x6E,0x6F,0x64,0x65,0x3E,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x69,0x73,0x61,0x62,0x6C,0x65,0x20,0x61,0x20, +0x44,0x53,0x53,0x49,0x20,0x64,0x65,0x76,0x69,0x63,0x65,0x0D,0x0A,0x20,0x20,0x20, +0x20,0x53,0x48,0x4F,0x57,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x73,0x68,0x6F,0x77,0x20,0x63,0x75,0x72,0x72,0x65,0x6E, +0x74,0x20,0x63,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x0D, +0x0A,0x20,0x20,0x20,0x20,0x48,0x45,0x4C,0x50,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x72,0x69,0x6E,0x74,0x20,0x74, +0x68,0x69,0x73,0x20,0x74,0x65,0x78,0x74,0x0D,0x0A,0x20,0x20,0x20,0x20,0x45,0x58, +0x49,0x54,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x74,0x68,0x65,0x20,0x4B,0x46, +0x51,0x53,0x41,0x0D,0x0A,0x20,0x20,0x20,0x20,0x51,0x55,0x49,0x54,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x6F,0x6E, +0x27,0x74,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x74,0x68,0x65,0x20,0x4B, +0x46,0x51,0x53,0x41,0x0D,0x0A,0x0A,0x50,0x61,0x72,0x61,0x6D,0x65,0x74,0x65,0x72, +0x73,0x3A,0x0D,0x0A,0x20,0x20,0x20,0x20,0x3C,0x6E,0x6F,0x64,0x65,0x3E,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x74,0x6F, +0x20,0x37,0x0D,0x0A,0x20,0x20,0x20,0x20,0x3C,0x43,0x53,0x52,0x5F,0x61,0x64,0x64, +0x72,0x65,0x73,0x73,0x3E,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x37,0x36,0x30,0x30, +0x31,0x30,0x20,0x74,0x6F,0x20,0x37,0x37,0x37,0x37,0x37,0x34,0x0D,0x0A,0x20,0x20, +0x20,0x20,0x3C,0x6D,0x6F,0x64,0x65,0x6C,0x3E,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x32,0x31,0x20,0x28,0x64,0x69,0x73,0x6B,0x29,0x20, +0x6F,0x72,0x20,0x32,0x32,0x20,0x28,0x74,0x61,0x70,0x65,0x29,0x0D,0x0A,0x00,0x0D, +0x00,0x0E,0x01,0x17,0x6A,0x04,0x20,0x0D,0x0A,0x54,0x61,0x73,0x6B,0x20,0x4E,0x61, +0x6D,0x65,0x3F,0x20,0x00,0x00,0x0E,0x01,0x2C,0x6A,0x04,0x20,0x02,0x00,0x0E,0x01, +0x34,0x6A,0x04,0x20,0x3F,0x20,0x05,0x4B,0x46,0x51,0x53,0x41,0x44,0x49,0x52,0x45, +0x43,0x54,0x04,0xA4,0x13,0x04,0x20,0x03,0x53,0x45,0x54,0x04,0x14,0x15,0x04,0x20, +0x05,0x43,0x4C,0x45,0x41,0x52,0x04,0xA7,0x15,0x04,0x20,0x04,0x53,0x48,0x4F,0x57, +0x04,0x09,0x16,0x04,0x20,0x04,0x48,0x45,0x4C,0x50,0x04,0x18,0x16,0x04,0x20,0x04, +0x45,0x58,0x49,0x54,0x04,0x1E,0x16,0x04,0x20,0x04,0x51,0x55,0x49,0x54,0x00,0x00, +0x07,0x0F,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x0F,0x07,0x07,0x0F,0x07, +0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x0F,0x07,0x03,0x1F,0x0F,0x0F,0x03,0x00,0x00, +0x02,0x00,0x0C,0x20,0x45,0x58,0x54,0x20,0x48,0x4C,0x54,0x0D,0x0A,0x00,0x04,0x00, +0x0C,0x20,0x49,0x53,0x50,0x20,0x45,0x52,0x52,0x0D,0x0A,0x00,0x05,0x00,0x0C,0x20, +0x44,0x42,0x4C,0x20,0x45,0x52,0x52,0x31,0x0D,0x0A,0x00,0x12,0x00,0x0C,0x20,0x44, +0x42,0x4C,0x20,0x45,0x52,0x52,0x32,0x0D,0x0A,0x00,0x13,0x00,0x0C,0x20,0x44,0x42, +0x4C,0x20,0x45,0x52,0x52,0x33,0x0D,0x0A,0x00,0x06,0x00,0x0C,0x20,0x48,0x4C,0x54, +0x20,0x49,0x4E,0x53,0x54,0x0D,0x0A,0x00,0x07,0x00,0x0C,0x20,0x53,0x43,0x42,0x20, +0x45,0x52,0x52,0x33,0x0D,0x0A,0x00,0x08,0x00,0x0C,0x20,0x53,0x43,0x42,0x20,0x45, +0x52,0x52,0x32,0x0D,0x0A,0x00,0x0A,0x00,0x0C,0x20,0x43,0x48,0x4D,0x20,0x46,0x52, +0x20,0x49,0x53,0x54,0x4B,0x0D,0x0A,0x00,0x0B,0x00,0x0C,0x20,0x43,0x48,0x4D,0x20, +0x54,0x4F,0x20,0x49,0x53,0x54,0x4B,0x0D,0x0A,0x00,0x0C,0x00,0x0C,0x20,0x53,0x43, +0x42,0x20,0x52,0x44,0x20,0x45,0x52,0x52,0x0D,0x0A,0x00,0x10,0x00,0x0C,0x20,0x4D, +0x43,0x48,0x4B,0x20,0x41,0x56,0x0D,0x0A,0x00,0x11,0x00,0x0C,0x20,0x4B,0x53,0x50, +0x20,0x41,0x56,0x0D,0x0A,0x00,0x19,0x00,0x0C,0x20,0x50,0x53,0x4C,0x20,0x45,0x58, +0x43,0x35,0x0D,0x0A,0x00,0x1A,0x00,0x0C,0x20,0x50,0x53,0x4C,0x20,0x45,0x58,0x43, +0x36,0x0D,0x0A,0x00,0x1B,0x00,0x0C,0x20,0x50,0x53,0x4C,0x20,0x45,0x58,0x43,0x37, +0x0D,0x0A,0x00,0x1D,0x00,0x0C,0x20,0x50,0x53,0x4C,0x20,0x52,0x45,0x49,0x35,0x0D, +0x0A,0x00,0x1E,0x00,0x0C,0x20,0x50,0x53,0x4C,0x20,0x52,0x45,0x49,0x36,0x0D,0x0A, +0x00,0x1F,0x00,0x0C,0x20,0x50,0x53,0x4C,0x20,0x52,0x45,0x49,0x37,0x0D,0x0A,0x00, +0x40,0x00,0x0C,0x20,0x4E,0x4F,0x53,0x55,0x43,0x48,0x44,0x45,0x56,0x00,0x41,0x00, +0x0C,0x20,0x44,0x45,0x56,0x41,0x53,0x53,0x49,0x47,0x4E,0x00,0x42,0x00,0x0C,0x20, +0x4E,0x4F,0x53,0x55,0x43,0x48,0x46,0x49,0x4C,0x45,0x00,0x43,0x00,0x0C,0x20,0x46, +0x49,0x4C,0x45,0x53,0x54,0x52,0x55,0x43,0x54,0x00,0x44,0x00,0x0C,0x20,0x42,0x41, +0x44,0x43,0x48,0x4B,0x53,0x55,0x4D,0x00,0x45,0x00,0x0C,0x20,0x42,0x41,0x44,0x46, +0x49,0x4C,0x45,0x48,0x44,0x52,0x00,0x46,0x00,0x0C,0x20,0x42,0x41,0x44,0x49,0x52, +0x45,0x43,0x54,0x4F,0x52,0x59,0x00,0x47,0x00,0x0C,0x20,0x46,0x49,0x4C,0x4E,0x4F, +0x54,0x43,0x4E,0x54,0x47,0x00,0x48,0x00,0x0C,0x20,0x45,0x4E,0x44,0x4F,0x46,0x46, +0x49,0x4C,0x45,0x00,0x49,0x00,0x0C,0x20,0x42,0x41,0x44,0x46,0x49,0x4C,0x45,0x4E, +0x41,0x4D,0x45,0x00,0x4A,0x00,0x0C,0x20,0x42,0x55,0x46,0x46,0x45,0x52,0x4F,0x56, +0x46,0x00,0x4B,0x00,0x0C,0x20,0x43,0x54,0x52,0x4C,0x45,0x52,0x52,0x00,0x4C,0x00, +0x0C,0x20,0x44,0x45,0x56,0x49,0x4E,0x41,0x43,0x54,0x00,0x4D,0x00,0x0C,0x20,0x44, +0x45,0x56,0x4F,0x46,0x46,0x4C,0x49,0x4E,0x45,0x00,0x4E,0x00,0x0C,0x20,0x4D,0x45, +0x4D,0x45,0x52,0x52,0x00,0x4F,0x00,0x0C,0x20,0x53,0x43,0x42,0x49,0x4E,0x54,0x00, +0x50,0x00,0x0C,0x20,0x53,0x43,0x42,0x32,0x4E,0x44,0x49,0x4E,0x54,0x00,0x51,0x00, +0x0C,0x20,0x4E,0x4F,0x52,0x4F,0x4D,0x00,0x52,0x00,0x0C,0x20,0x4E,0x4F,0x53,0x55, +0x43,0x48,0x4E,0x4F,0x44,0x45,0x00,0x53,0x00,0x0C,0x20,0x49,0x4E,0x53,0x46,0x4D, +0x41,0x50,0x52,0x45,0x47,0x00,0x54,0x00,0x0C,0x20,0x52,0x45,0x54,0x52,0x59,0x00, +0x55,0x00,0x0C,0x20,0x49,0x56,0x44,0x45,0x56,0x4E,0x41,0x4D,0x00,0x20,0x00,0x0C, +0x20,0x43,0x4F,0x52,0x52,0x50,0x54,0x4E,0x00,0x21,0x00,0x0C,0x20,0x49,0x4C,0x4C, +0x20,0x52,0x45,0x46,0x00,0x22,0x00,0x0C,0x20,0x49,0x4C,0x4C,0x20,0x43,0x4D,0x44, +0x00,0x36,0x00,0x0C,0x20,0x41,0x4D,0x42,0x47,0x20,0x43,0x4D,0x44,0x00,0x37,0x00, +0x0C,0x20,0x49,0x4E,0x53,0x55,0x46,0x20,0x41,0x52,0x47,0x00,0x35,0x00,0x0C,0x20, +0x41,0x52,0x47,0x20,0x4F,0x56,0x45,0x52,0x46,0x00,0x23,0x00,0x0C,0x20,0x49,0x4E, +0x56,0x20,0x44,0x47,0x54,0x00,0x34,0x00,0x0C,0x20,0x51,0x55,0x41,0x4C,0x20,0x4F, +0x56,0x45,0x52,0x46,0x00,0x31,0x00,0x0C,0x20,0x51,0x55,0x41,0x4C,0x20,0x4E,0x4F, +0x56,0x41,0x4C,0x00,0x32,0x00,0x0C,0x20,0x41,0x4D,0x42,0x47,0x20,0x51,0x55,0x41, +0x4C,0x00,0x33,0x00,0x0C,0x20,0x51,0x55,0x41,0x4C,0x20,0x52,0x45,0x51,0x20,0x56, +0x41,0x4C,0x00,0x24,0x00,0x0C,0x20,0x4C,0x54,0x4C,0x00,0x25,0x00,0x0C,0x20,0x49, +0x4C,0x4C,0x20,0x41,0x44,0x52,0x00,0x26,0x00,0x0C,0x20,0x56,0x41,0x4C,0x20,0x54, +0x4F,0x4F,0x20,0x4C,0x52,0x47,0x00,0x27,0x00,0x0C,0x20,0x53,0x57,0x20,0x43,0x4F, +0x4E,0x46,0x00,0x28,0x00,0x0C,0x20,0x55,0x4E,0x4B,0x20,0x53,0x57,0x00,0x29,0x00, +0x0C,0x20,0x55,0x4E,0x4B,0x20,0x53,0x59,0x4D,0x00,0x2A,0x00,0x0C,0x20,0x43,0x48, +0x4B,0x53,0x4D,0x00,0x2B,0x00,0x0C,0x20,0x48,0x4C,0x54,0x45,0x44,0x00,0x2C,0x00, +0x0C,0x20,0x46,0x4E,0x44,0x20,0x45,0x52,0x52,0x00,0x2D,0x00,0x0C,0x20,0x54,0x4D, +0x4F,0x55,0x54,0x00,0x2E,0x00,0x0C,0x20,0x4D,0x45,0x4D,0x20,0x45,0x52,0x52,0x00, +0x2F,0x00,0x0C,0x20,0x55,0x4E,0x58,0x49,0x4E,0x54,0x00,0x30,0x00,0x0C,0x20,0x55, +0x4E,0x49,0x4D,0x50,0x4C,0x45,0x4D,0x45,0x4E,0x54,0x45,0x44,0x00,0x99,0x00,0x0C, +0x3E,0x3E,0x3E,0x00,0x8C,0x00,0x0C,0x0D,0x0A,0x00,0x8D,0x00,0x0C,0x5E,0x43,0x0D, +0x0A,0x00,0x80,0x01,0x0C,0x70,0x61,0x73,0x73,0x69,0x76,0x65,0x20,0x72,0x65,0x6C, +0x65,0x61,0x73,0x65,0x0D,0x0A,0x00,0x81,0x01,0x0C,0x4D,0x61,0x63,0x68,0x69,0x6E, +0x65,0x20,0x63,0x68,0x65,0x63,0x6B,0x0D,0x0A,0x00,0x82,0x01,0x0C,0x4B,0x53,0x50, +0x20,0x69,0x6E,0x76,0x61,0x6C,0x69,0x64,0x0D,0x0A,0x00,0x83,0x01,0x0C,0x50,0x6F, +0x77,0x65,0x72,0x20,0x66,0x61,0x69,0x6C,0x0D,0x0A,0x00,0x84,0x01,0x0C,0x52,0x65, +0x73,0x65,0x72,0x76,0x65,0x64,0x2F,0x70,0x72,0x69,0x76,0x69,0x6C,0x65,0x67,0x65, +0x64,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x63,0x74,0x69,0x6F,0x6E,0x0D,0x0A,0x00, +0x85,0x01,0x0C,0x43,0x75,0x73,0x74,0x6F,0x6D,0x65,0x72,0x20,0x72,0x65,0x73,0x65, +0x72,0x76,0x65,0x64,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x63,0x74,0x69,0x6F,0x6E, +0x0D,0x0A,0x00,0x86,0x01,0x0C,0x52,0x65,0x73,0x65,0x72,0x76,0x65,0x64,0x20,0x6F, +0x70,0x65,0x72,0x61,0x6E,0x64,0x0D,0x0A,0x00,0x87,0x01,0x0C,0x52,0x65,0x73,0x65, +0x72,0x76,0x65,0x64,0x20,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x69,0x6E,0x67,0x20, +0x6D,0x6F,0x64,0x65,0x0D,0x0A,0x00,0x88,0x01,0x0C,0x41,0x63,0x63,0x65,0x73,0x73, +0x20,0x76,0x69,0x6F,0x6C,0x61,0x74,0x69,0x6F,0x6E,0x0D,0x0A,0x00,0x89,0x01,0x0C, +0x54,0x72,0x61,0x6E,0x73,0x6C,0x61,0x74,0x69,0x6F,0x6E,0x20,0x6E,0x6F,0x74,0x20, +0x76,0x61,0x6C,0x69,0x64,0x0D,0x0A,0x00,0x8A,0x01,0x0C,0x54,0x72,0x61,0x63,0x65, +0x20,0x70,0x65,0x6E,0x64,0x69,0x6E,0x67,0x0D,0x0A,0x00,0x8B,0x01,0x0C,0x42,0x72, +0x65,0x61,0x6B,0x70,0x6F,0x69,0x6E,0x74,0x0D,0x0A,0x00,0x8C,0x01,0x0C,0x75,0x6E, +0x75,0x73,0x65,0x64,0x0D,0x0A,0x00,0x8D,0x01,0x0C,0x41,0x72,0x69,0x74,0x68,0x6D, +0x65,0x74,0x69,0x63,0x0D,0x0A,0x00,0x8E,0x01,0x0C,0x75,0x6E,0x75,0x73,0x65,0x64, +0x0D,0x0A,0x00,0x8F,0x01,0x0C,0x75,0x6E,0x75,0x73,0x65,0x64,0x0D,0x0A,0x00,0x90, +0x01,0x0C,0x43,0x48,0x4D,0x4B,0x0D,0x0A,0x00,0x91,0x01,0x0C,0x43,0x48,0x4D,0x45, +0x0D,0x0A,0x00,0x92,0x01,0x0C,0x43,0x48,0x4D,0x53,0x0D,0x0A,0x00,0x93,0x01,0x0C, +0x43,0x48,0x4D,0x55,0x0D,0x0A,0x00,0x94,0x01,0x0C,0x75,0x6E,0x75,0x73,0x65,0x64, +0x0D,0x0A,0x00,0x95,0x01,0x0C,0x43,0x6F,0x72,0x72,0x65,0x63,0x74,0x65,0x64,0x20, +0x72,0x65,0x61,0x64,0x20,0x64,0x61,0x74,0x61,0x0D,0x0A,0x00,0x96,0x01,0x0C,0x75, +0x6E,0x75,0x73,0x65,0x64,0x0D,0x0A,0x00,0x97,0x01,0x0C,0x75,0x6E,0x75,0x73,0x65, +0x64,0x0D,0x0A,0x00,0x98,0x01,0x0C,0x4D,0x65,0x6D,0x6F,0x72,0x79,0x20,0x65,0x72, +0x72,0x6F,0x72,0x0D,0x0A,0x00,0x99,0x01,0x0C,0x75,0x6E,0x75,0x73,0x65,0x64,0x0D, +0x0A,0x00,0x9A,0x01,0x0C,0x75,0x6E,0x75,0x73,0x65,0x64,0x0D,0x0A,0x00,0x9B,0x01, +0x0C,0x75,0x6E,0x75,0x73,0x65,0x64,0x0D,0x0A,0x00,0x9C,0x01,0x0C,0x75,0x6E,0x75, +0x73,0x65,0x64,0x0D,0x0A,0x00,0x9D,0x01,0x0C,0x75,0x6E,0x75,0x73,0x65,0x64,0x0D, +0x0A,0x00,0x9E,0x01,0x0C,0x75,0x6E,0x75,0x73,0x65,0x64,0x0D,0x0A,0x00,0x9F,0x01, +0x0C,0x75,0x6E,0x75,0x73,0x65,0x64,0x0D,0x0A,0x00,0xA0,0x01,0x0C,0x75,0x6E,0x75, +0x73,0x65,0x64,0x0D,0x0A,0x00,0xA1,0x01,0x0C,0x53,0x6F,0x66,0x74,0x77,0x61,0x72, +0x65,0x20,0x6C,0x65,0x76,0x65,0x6C,0x20,0x31,0x0D,0x0A,0x00,0xA2,0x01,0x0C,0x53, +0x6F,0x66,0x74,0x77,0x61,0x72,0x65,0x20,0x6C,0x65,0x76,0x65,0x6C,0x20,0x32,0x0D, +0x0A,0x00,0xA3,0x01,0x0C,0x53,0x6F,0x66,0x74,0x77,0x61,0x72,0x65,0x20,0x6C,0x65, +0x76,0x65,0x6C,0x20,0x33,0x0D,0x0A,0x00,0xA4,0x01,0x0C,0x53,0x6F,0x66,0x74,0x77, +0x61,0x72,0x65,0x20,0x6C,0x65,0x76,0x65,0x6C,0x20,0x34,0x0D,0x0A,0x00,0xA5,0x01, +0x0C,0x53,0x6F,0x66,0x74,0x77,0x61,0x72,0x65,0x20,0x6C,0x65,0x76,0x65,0x6C,0x20, +0x35,0x0D,0x0A,0x00,0xA6,0x01,0x0C,0x53,0x6F,0x66,0x74,0x77,0x61,0x72,0x65,0x20, +0x6C,0x65,0x76,0x65,0x6C,0x20,0x36,0x0D,0x0A,0x00,0xA7,0x01,0x0C,0x53,0x6F,0x66, +0x74,0x77,0x61,0x72,0x65,0x20,0x6C,0x65,0x76,0x65,0x6C,0x20,0x37,0x0D,0x0A,0x00, +0xA8,0x01,0x0C,0x53,0x6F,0x66,0x74,0x77,0x61,0x72,0x65,0x20,0x6C,0x65,0x76,0x65, +0x6C,0x20,0x38,0x0D,0x0A,0x00,0xA9,0x01,0x0C,0x53,0x6F,0x66,0x74,0x77,0x61,0x72, +0x65,0x20,0x6C,0x65,0x76,0x65,0x6C,0x20,0x39,0x0D,0x0A,0x00,0xAA,0x01,0x0C,0x53, +0x6F,0x66,0x74,0x77,0x61,0x72,0x65,0x20,0x6C,0x65,0x76,0x65,0x6C,0x20,0x41,0x0D, +0x0A,0x00,0xAB,0x01,0x0C,0x53,0x6F,0x66,0x74,0x77,0x61,0x72,0x65,0x20,0x6C,0x65, +0x76,0x65,0x6C,0x20,0x42,0x0D,0x0A,0x00,0xAC,0x01,0x0C,0x53,0x6F,0x66,0x74,0x77, +0x61,0x72,0x65,0x20,0x6C,0x65,0x76,0x65,0x6C,0x20,0x43,0x0D,0x0A,0x00,0xAD,0x01, +0x0C,0x53,0x6F,0x66,0x74,0x77,0x61,0x72,0x65,0x20,0x6C,0x65,0x76,0x65,0x6C,0x20, +0x44,0x0D,0x0A,0x00,0xAE,0x01,0x0C,0x53,0x6F,0x66,0x74,0x77,0x61,0x72,0x65,0x20, +0x6C,0x65,0x76,0x65,0x6C,0x20,0x45,0x0D,0x0A,0x00,0xAF,0x01,0x0C,0x53,0x6F,0x66, +0x74,0x77,0x61,0x72,0x65,0x20,0x6C,0x65,0x76,0x65,0x6C,0x20,0x46,0x0D,0x0A,0x00, +0xB0,0x01,0x0C,0x49,0x6E,0x74,0x65,0x72,0x76,0x61,0x6C,0x20,0x74,0x69,0x6D,0x65, +0x72,0x0D,0x0A,0x00,0xB1,0x01,0x0C,0x75,0x6E,0x75,0x73,0x65,0x64,0x0D,0x0A,0x00, +0xB2,0x01,0x0C,0x45,0x6D,0x75,0x6C,0x61,0x74,0x69,0x6F,0x6E,0x20,0x73,0x74,0x61, +0x72,0x74,0x0D,0x0A,0x00,0xB3,0x01,0x0C,0x45,0x6D,0x75,0x6C,0x61,0x74,0x69,0x6F, +0x6E,0x20,0x63,0x6F,0x6E,0x74,0x69,0x6E,0x75,0x65,0x0D,0x0A,0x00,0xB4,0x01,0x0C, +0x75,0x6E,0x75,0x73,0x65,0x64,0x0D,0x0A,0x00,0xB5,0x01,0x0C,0x75,0x6E,0x75,0x73, +0x65,0x64,0x0D,0x0A,0x00,0xB6,0x01,0x0C,0x75,0x6E,0x75,0x73,0x65,0x64,0x0D,0x0A, +0x00,0xB7,0x01,0x0C,0x75,0x6E,0x75,0x73,0x65,0x64,0x0D,0x0A,0x00,0xB8,0x01,0x0C, +0x75,0x6E,0x75,0x73,0x65,0x64,0x0D,0x0A,0x00,0xB9,0x01,0x0C,0x75,0x6E,0x75,0x73, +0x65,0x64,0x0D,0x0A,0x00,0xBA,0x01,0x0C,0x75,0x6E,0x75,0x73,0x65,0x64,0x0D,0x0A, +0x00,0xBB,0x01,0x0C,0x75,0x6E,0x75,0x73,0x65,0x64,0x0D,0x0A,0x00,0xBC,0x01,0x0C, +0x75,0x6E,0x75,0x73,0x65,0x64,0x0D,0x0A,0x00,0xBD,0x01,0x0C,0x75,0x6E,0x75,0x73, +0x65,0x64,0x0D,0x0A,0x00,0xBE,0x01,0x0C,0x43,0x6F,0x6E,0x73,0x6F,0x6C,0x65,0x20, +0x72,0x65,0x63,0x65,0x69,0x76,0x65,0x0D,0x0A,0x00,0xBF,0x01,0x0C,0x43,0x6F,0x6E, +0x73,0x6F,0x6C,0x65,0x20,0x74,0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,0x0D,0x0A,0x00, +0x92,0x00,0x0C,0x20,0x31,0x29,0x20,0x44,0x61,0x6E,0x73,0x6B,0x0D,0x0A,0x20,0x32, +0x29,0x20,0x44,0x65,0x75,0x74,0x73,0x63,0x68,0x20,0x28,0x44,0x65,0x75,0x74,0x73, +0x63,0x68,0x6C,0x61,0x6E,0x64,0x2F,0xD6,0x73,0x74,0x65,0x72,0x72,0x65,0x69,0x63, +0x68,0x29,0x0D,0x0A,0x20,0x33,0x29,0x20,0x44,0x65,0x75,0x74,0x73,0x63,0x68,0x20, +0x28,0x53,0x63,0x68,0x77,0x65,0x69,0x7A,0x29,0x0D,0x0A,0x20,0x34,0x29,0x20,0x45, +0x6E,0x67,0x6C,0x69,0x73,0x68,0x20,0x28,0x55,0x6E,0x69,0x74,0x65,0x64,0x20,0x4B, +0x69,0x6E,0x67,0x64,0x6F,0x6D,0x29,0x0D,0x0A,0x20,0x35,0x29,0x20,0x45,0x6E,0x67, +0x6C,0x69,0x73,0x68,0x20,0x28,0x55,0x6E,0x69,0x74,0x65,0x64,0x20,0x53,0x74,0x61, +0x74,0x65,0x73,0x2F,0x43,0x61,0x6E,0x61,0x64,0x61,0x29,0x0D,0x0A,0x20,0x36,0x29, +0x20,0x45,0x73,0x70,0x61,0xF1,0x6F,0x6C,0x0D,0x0A,0x20,0x37,0x29,0x20,0x46,0x72, +0x61,0x6E,0xE7,0x61,0x69,0x73,0x20,0x28,0x43,0x61,0x6E,0x61,0x64,0x61,0x29,0x0D, +0x0A,0x20,0x38,0x29,0x20,0x46,0x72,0x61,0x6E,0xE7,0x61,0x69,0x73,0x20,0x28,0x46, +0x72,0x61,0x6E,0x63,0x65,0x2F,0x42,0x65,0x6C,0x67,0x69,0x71,0x75,0x65,0x29,0x0D, +0x0A,0x20,0x39,0x29,0x20,0x46,0x72,0x61,0x6E,0xE7,0x61,0x69,0x73,0x20,0x28,0x53, +0x75,0x69,0x73,0x73,0x65,0x29,0x0D,0x0A,0x31,0x30,0x29,0x20,0x49,0x74,0x61,0x6C, +0x69,0x61,0x6E,0x6F,0x0D,0x0A,0x31,0x31,0x29,0x20,0x4E,0x65,0x64,0x65,0x72,0x6C, +0x61,0x6E,0x64,0x73,0x0D,0x0A,0x31,0x32,0x29,0x20,0x4E,0x6F,0x72,0x73,0x6B,0x0D, +0x0A,0x31,0x33,0x29,0x20,0x50,0x6F,0x72,0x74,0x75,0x67,0x75,0xEA,0x73,0x0D,0x0A, +0x31,0x34,0x29,0x20,0x53,0x75,0x6F,0x6D,0x69,0x0D,0x0A,0x31,0x35,0x29,0x20,0x53, +0x76,0x65,0x6E,0x73,0x6B,0x61,0x0D,0x0A,0x20,0x28,0x31,0x2E,0x2E,0x31,0x35,0x29, +0x3A,0x20,0x00,0x83,0x00,0x03,0x4C,0x6F,0x61,0x64,0x69,0x6E,0x67,0x20,0x73,0x79, +0x73,0x74,0x65,0x6D,0x20,0x73,0x6F,0x66,0x74,0x77,0x61,0x72,0x65,0x2E,0x0D,0x0A, +0x00,0x84,0x00,0x03,0x46,0x61,0x69,0x6C,0x75,0x72,0x65,0x2E,0x0D,0x0A,0x00,0x85, +0x00,0x03,0x52,0x65,0x73,0x74,0x61,0x72,0x74,0x69,0x6E,0x67,0x20,0x73,0x79,0x73, +0x74,0x65,0x6D,0x20,0x73,0x6F,0x66,0x74,0x77,0x61,0x72,0x65,0x2E,0x0D,0x0A,0x00, +0x86,0x00,0x03,0x50,0x65,0x72,0x66,0x6F,0x72,0x6D,0x69,0x6E,0x67,0x20,0x6E,0x6F, +0x72,0x6D,0x61,0x6C,0x20,0x73,0x79,0x73,0x74,0x65,0x6D,0x20,0x74,0x65,0x73,0x74, +0x73,0x2E,0x0D,0x0A,0x00,0x88,0x00,0x03,0x54,0x65,0x73,0x74,0x73,0x20,0x63,0x6F, +0x6D,0x70,0x6C,0x65,0x74,0x65,0x64,0x2E,0x0D,0x0A,0x00,0x89,0x00,0x03,0x4E,0x6F, +0x72,0x6D,0x61,0x6C,0x20,0x6F,0x70,0x65,0x72,0x61,0x74,0x69,0x6F,0x6E,0x20,0x6E, +0x6F,0x74,0x20,0x70,0x6F,0x73,0x73,0x69,0x62,0x6C,0x65,0x2E,0x0D,0x0A,0x00,0x9A, +0x00,0x03,0x42,0x6F,0x6F,0x74,0x66,0x69,0x6C,0x65,0x3A,0x20,0x00,0x9D,0x00,0x03, +0x4D,0x65,0x6D,0x6F,0x72,0x79,0x20,0x63,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61, +0x74,0x69,0x6F,0x6E,0x20,0x65,0x72,0x72,0x6F,0x72,0x2E,0x0D,0x0A,0x00,0x9E,0x00, +0x03,0x4E,0x6F,0x20,0x64,0x65,0x66,0x61,0x75,0x6C,0x74,0x20,0x62,0x6F,0x6F,0x74, +0x20,0x64,0x65,0x76,0x69,0x63,0x65,0x20,0x68,0x61,0x73,0x20,0x62,0x65,0x65,0x6E, +0x20,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x2E,0x0D,0x0A,0x00,0x9F,0x00, +0x03,0x41,0x76,0x61,0x69,0x6C,0x61,0x62,0x6C,0x65,0x20,0x64,0x65,0x76,0x69,0x63, +0x65,0x73,0x2E,0x0D,0x0A,0x00,0xA0,0x00,0x03,0x44,0x65,0x76,0x69,0x63,0x65,0x3F, +0x20,0x00,0xA1,0x00,0x03,0x52,0x65,0x74,0x72,0x79,0x69,0x6E,0x67,0x20,0x6E,0x65, +0x74,0x77,0x6F,0x72,0x6B,0x20,0x62,0x6F,0x6F,0x74,0x73,0x74,0x72,0x61,0x70,0x2E, +0x00,0x83,0x00,0x05,0x4C,0x6F,0x67,0x69,0x63,0x69,0x65,0x6C,0x2D,0x73,0x79,0x73, +0x74,0xE8,0x6D,0x65,0x20,0x65,0x6E,0x20,0x63,0x6F,0x75,0x72,0x73,0x20,0x64,0x65, +0x20,0x63,0x68,0x61,0x72,0x67,0x65,0x6D,0x65,0x6E,0x74,0x2E,0x0D,0x0A,0x00,0x84, +0x00,0x05,0x50,0x61,0x6E,0x6E,0x65,0x2E,0x0D,0x0A,0x00,0x85,0x00,0x05,0x52,0x65, +0x64,0xE9,0x6D,0x61,0x72,0x72,0x61,0x67,0x65,0x20,0x64,0x75,0x20,0x6C,0x6F,0x67, +0x69,0x63,0x69,0x65,0x6C,0x2D,0x73,0x79,0x73,0x74,0xE8,0x6D,0x65,0x2E,0x0D,0x0A, +0x00,0x86,0x00,0x05,0x53,0x79,0x73,0x74,0xE8,0x6D,0x65,0x20,0x65,0x6E,0x20,0x63, +0x6F,0x75,0x72,0x73,0x20,0x64,0x65,0x20,0x74,0x65,0x73,0x74,0x2E,0x0D,0x0A,0x00, +0x88,0x00,0x05,0x54,0x65,0x73,0x74,0x73,0x20,0x74,0x65,0x72,0x6D,0x69,0x6E,0xE9, +0x73,0x2E,0x0D,0x0A,0x00,0x89,0x00,0x05,0x45,0x78,0x70,0x6C,0x6F,0x69,0x74,0x61, +0x74,0x69,0x6F,0x6E,0x20,0x64,0x75,0x20,0x73,0x79,0x73,0x74,0xE8,0x6D,0x65,0x20, +0x69,0x6D,0x70,0x6F,0x73,0x73,0x69,0x62,0x6C,0x65,0x2E,0x0D,0x0A,0x00,0x9A,0x00, +0x05,0x46,0x69,0x63,0x68,0x69,0x65,0x72,0x20,0x64,0x65,0x20,0x63,0x68,0x61,0x72, +0x67,0x65,0x6D,0x65,0x6E,0x74,0x3A,0x20,0x00,0x9D,0x00,0x05,0x45,0x72,0x72,0x65, +0x75,0x72,0x20,0x63,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E, +0x20,0x6D,0xE9,0x6D,0x6F,0x69,0x72,0x65,0x2E,0x0D,0x0A,0x00,0x9E,0x00,0x05,0x55, +0x6E,0x69,0x74,0xE9,0x20,0x64,0x65,0x20,0x64,0xE9,0x6D,0x61,0x72,0x72,0x61,0x67, +0x65,0x20,0x70,0x61,0x72,0x20,0x64,0xE9,0x66,0x61,0x75,0x74,0x20,0x69,0x6E,0x63, +0x6F,0x6E,0x6E,0x75,0x65,0x2E,0x0D,0x0A,0x00,0x9F,0x00,0x05,0x55,0x6E,0x69,0x74, +0xE9,0x73,0x20,0x64,0x69,0x73,0x70,0x6F,0x6E,0x69,0x62,0x6C,0x65,0x73,0x3A,0x0D, +0x0A,0x00,0xA0,0x00,0x05,0x55,0x6E,0x69,0x74,0xE9,0x3F,0x20,0x00,0xA1,0x00,0x05, +0x45,0x73,0x73,0x61,0x69,0x20,0x64,0xE9,0x6D,0x61,0x72,0x72,0x61,0x67,0x65,0x20, +0x73,0x75,0x72,0x20,0x72,0xE9,0x73,0x65,0x61,0x75,0x2E,0x00,0x83,0x00,0x04,0x43, +0x61,0x72,0x67,0x61,0x6E,0x64,0x6F,0x20,0x73,0x69,0x73,0x74,0x65,0x6D,0x61,0x20, +0x6F,0x70,0x65,0x72,0x61,0x74,0x69,0x76,0x6F,0x2E,0x0D,0x0A,0x00,0x84,0x00,0x04, +0x46,0x61,0x6C,0x6C,0x6F,0x2E,0x0D,0x0A,0x00,0x85,0x00,0x04,0x52,0x65,0x61,0x72, +0x72,0x61,0x6E,0x63,0x61,0x6E,0x64,0x6F,0x20,0x65,0x6C,0x20,0x73,0x69,0x73,0x74, +0x65,0x6D,0x61,0x2E,0x0D,0x0A,0x00,0x86,0x00,0x04,0x52,0x65,0x61,0x6C,0x69,0x7A, +0x61,0x6E,0x64,0x6F,0x20,0x74,0x65,0x73,0x74,0x20,0x64,0x65,0x6C,0x20,0x73,0x69, +0x73,0x74,0x65,0x6D,0x61,0x2E,0x0D,0x0A,0x00,0x88,0x00,0x04,0x54,0x65,0x73,0x74, +0x20,0x63,0x6F,0x6D,0x70,0x6C,0x65,0x74,0x61,0x64,0x6F,0x2E,0x0D,0x0A,0x00,0x89, +0x00,0x04,0x4F,0x70,0x65,0x72,0x61,0x63,0x69,0x6F,0x6E,0x20,0x6E,0x6F,0x72,0x6D, +0x61,0x6C,0x20,0x69,0x6D,0x70,0x6F,0x73,0x69,0x62,0x6C,0x65,0x2E,0x0D,0x0A,0x00, +0x9A,0x00,0x04,0x20,0x27,0x46,0x69,0x63,0x68,0x65,0x72,0x6F,0x20,0x64,0x65,0x20, +0x62,0x6F,0x6F,0x74,0x3A,0x20,0x00,0x9D,0x00,0x04,0x45,0x72,0x72,0x6F,0x72,0x20, +0x64,0x65,0x20,0x63,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x63,0x69,0xF3,0x6E, +0x20,0x64,0x65,0x20,0x6D,0x65,0x6D,0x6F,0x72,0x69,0x61,0x2E,0x0D,0x0A,0x00,0x9E, +0x00,0x04,0x4E,0x6F,0x20,0x73,0x65,0x20,0x68,0x61,0x20,0x65,0x73,0x70,0x65,0x63, +0x69,0x66,0x69,0x63,0x61,0x64,0x6F,0x20,0x6E,0x69,0x6E,0x67,0xFA,0x6E,0x20,0x64, +0x69,0x73,0x70,0x6F,0x73,0x69,0x74,0x69,0x76,0x6F,0x20,0x64,0x65,0x20,0x61,0x72, +0x72,0x61,0x6E,0x71,0x75,0x65,0x20,0x70,0x6F,0x72,0x20,0x6F,0x6D,0x69,0x73,0x69, +0xF3,0x6E,0x2E,0x0D,0x0A,0x00,0x9F,0x00,0x04,0x44,0x69,0x73,0x70,0x6F,0x73,0x69, +0x74,0x69,0x76,0x6F,0x73,0x20,0x64,0x69,0x73,0x70,0x6F,0x6E,0x69,0x62,0x6C,0x65, +0x73,0x3A,0x0D,0x0A,0x00,0xA0,0x00,0x04,0xBF,0x51,0x75,0xE9,0x20,0x64,0x69,0x73, +0x70,0x6F,0x73,0x69,0x74,0x69,0x76,0x6F,0x3F,0x20,0x00,0xA1,0x00,0x04,0x52,0x65, +0x69,0x6E,0x74,0x65,0x6E,0x74,0x61,0x6E,0x64,0x6F,0x20,0x65,0x6C,0x20,0x61,0x72, +0x72,0x61,0x6E,0x71,0x75,0x65,0x20,0x64,0x65,0x20,0x6C,0x61,0x20,0x72,0x65,0x64, +0x2E,0x00,0x83,0x00,0x02,0x42,0x65,0x74,0x72,0x69,0x65,0x62,0x73,0x73,0x79,0x73, +0x74,0x65,0x6D,0x20,0x77,0x69,0x72,0x64,0x20,0x67,0x65,0x6C,0x61,0x64,0x65,0x6E, +0x2E,0x0D,0x0A,0x00,0x84,0x00,0x02,0x46,0x65,0x68,0x6C,0x65,0x72,0x2E,0x0D,0x0A, +0x00,0x85,0x00,0x02,0x42,0x65,0x74,0x72,0x69,0x65,0x62,0x73,0x73,0x79,0x73,0x74, +0x65,0x6D,0x20,0x77,0x69,0x72,0x64,0x20,0x6E,0x65,0x75,0x20,0x67,0x65,0x73,0x74, +0x61,0x72,0x74,0x65,0x74,0x2E,0x0D,0x0A,0x00,0x86,0x00,0x02,0x53,0x79,0x73,0x74, +0x65,0x6D,0x74,0x65,0x73,0x74,0x20,0x6C,0xE4,0x75,0x66,0x74,0x2E,0x0D,0x0A,0x00, +0x88,0x00,0x02,0x54,0x65,0x73,0x74,0x73,0x20,0x61,0x62,0x67,0x65,0x73,0x63,0x68, +0x6C,0x6F,0x73,0x73,0x65,0x6E,0x2E,0x0D,0x0A,0x00,0x89,0x00,0x02,0x4E,0x6F,0x72, +0x6D,0x61,0x6C,0x65,0x72,0x20,0x42,0x65,0x74,0x72,0x69,0x65,0x62,0x20,0x6E,0x69, +0x63,0x68,0x74,0x20,0x6D,0xF6,0x67,0x6C,0x69,0x63,0x68,0x2E,0x0D,0x0A,0x00,0x9A, +0x00,0x02,0x4C,0x61,0x64,0x65,0x64,0x61,0x74,0x65,0x69,0x3A,0x3A,0x20,0x00,0x9D, +0x00,0x02,0x46,0x65,0x68,0x6C,0x65,0x72,0x20,0x62,0x65,0x69,0x20,0x64,0x65,0x72, +0x20,0x53,0x70,0x65,0x69,0x63,0x68,0x65,0x72,0x6B,0x6F,0x6E,0x66,0x69,0x67,0x75, +0x72,0x61,0x74,0x69,0x6F,0x6E,0x2E,0x0D,0x0A,0x00,0x9E,0x00,0x02,0x53,0x74,0x61, +0x6E,0x64,0x61,0x72,0x64,0x2D,0x55,0x72,0x6C,0x61,0x64,0x65,0x67,0x65,0x72,0xE4, +0x74,0x20,0x77,0x75,0x72,0x64,0x65,0x20,0x6E,0x69,0x63,0x68,0x74,0x20,0x61,0x6E, +0x67,0x65,0x67,0x65,0x62,0x65,0x6E,0x2E,0x0D,0x0A,0x00,0x9F,0x00,0x02,0x56,0x65, +0x72,0x66,0xFC,0x67,0x62,0x61,0x72,0x65,0x20,0x47,0x65,0x72,0xE4,0x74,0x65,0x3A, +0x0D,0x0A,0x00,0xA0,0x00,0x02,0x47,0x65,0x72,0xE4,0x74,0x3F,0x20,0x00,0xA1,0x00, +0x02,0x4C,0x61,0x64,0x65,0x76,0x6F,0x72,0x67,0x61,0x6E,0x67,0x20,0xFC,0x62,0x65, +0x72,0x20,0x4E,0x65,0x74,0x7A,0x77,0x65,0x72,0x6B,0x20,0x6C,0xE4,0x75,0x66,0x74, +0x2E,0x00,0x83,0x00,0x06,0x43,0x61,0x72,0x69,0x63,0x61,0x6D,0x65,0x6E,0x74,0x6F, +0x20,0x73,0x6F,0x66,0x74,0x77,0x61,0x72,0x65,0x20,0x64,0x65,0x6C,0x20,0x73,0x69, +0x73,0x74,0x65,0x6D,0x61,0x2E,0x0D,0x0A,0x00,0x84,0x00,0x06,0x45,0x72,0x72,0x6F, +0x72,0x65,0x2E,0x0D,0x0A,0x00,0x85,0x00,0x06,0x52,0x69,0x61,0x76,0x76,0x69,0x61, +0x6D,0x65,0x6E,0x74,0x6F,0x20,0x73,0x6F,0x66,0x74,0x77,0x61,0x72,0x65,0x20,0x64, +0x69,0x20,0x73,0x69,0x73,0x74,0x65,0x6D,0x61,0x2E,0x0D,0x0A,0x00,0x86,0x00,0x06, +0x45,0x73,0x65,0x63,0x75,0x7A,0x69,0x6F,0x6E,0x65,0x20,0x74,0x65,0x73,0x74,0x20, +0x64,0x65,0x6C,0x20,0x73,0x69,0x73,0x74,0x65,0x6D,0x61,0x2E,0x0D,0x0A,0x00,0x88, +0x00,0x06,0x54,0x65,0x73,0x74,0x20,0x64,0x69,0x61,0x67,0x6E,0x6F,0x73,0x74,0x69, +0x63,0x6F,0x20,0x63,0x6F,0x6D,0x70,0x6C,0x65,0x74,0x61,0x74,0x6F,0x2E,0x0D,0x0A, +0x00,0x89,0x00,0x06,0x4E,0x6F,0x72,0x6D,0x61,0x6C,0x69,0x20,0x6F,0x70,0x65,0x72, +0x61,0x7A,0x69,0x6F,0x6E,0x69,0x20,0x6E,0x6F,0x6E,0x20,0x65,0x73,0x65,0x67,0x75, +0x69,0x62,0x69,0x6C,0x69,0x2E,0x0D,0x0A,0x00,0x9A,0x00,0x06,0x50,0x72,0x6F,0x67, +0x72,0x61,0x6D,0x6D,0x61,0x20,0x64,0x69,0x20,0x61,0x76,0x76,0x69,0x61,0x6D,0x65, +0x6E,0x74,0x6F,0x3A,0x20,0x00,0x9D,0x00,0x06,0x45,0x72,0x72,0x6F,0x72,0x65,0x20, +0x64,0x69,0x20,0x63,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x7A,0x69,0x6F,0x6E, +0x65,0x20,0x64,0x65,0x6C,0x6C,0x61,0x20,0x6D,0x65,0x6D,0x6F,0x72,0x69,0x61,0x2E, +0x0D,0x0A,0x00,0x9E,0x00,0x06,0x55,0x6E,0x69,0x74,0xE0,0x20,0x73,0x74,0x61,0x6E, +0x64,0x61,0x72,0x64,0x20,0x64,0x69,0x20,0x61,0x76,0x76,0x69,0x61,0x6D,0x65,0x6E, +0x74,0x6F,0x20,0x6E,0x6F,0x6E,0x20,0x69,0x6E,0x64,0x69,0x63,0x61,0x74,0x61,0x2E, +0x0D,0x0A,0x00,0x9F,0x00,0x06,0x55,0x6E,0x69,0x74,0xE0,0x20,0x64,0x69,0x73,0x70, +0x6F,0x6E,0x69,0x62,0x69,0x6C,0x69,0x3A,0x0D,0x0A,0x00,0xA0,0x00,0x06,0x55,0x6E, +0x69,0x74,0xE0,0x3F,0x20,0x00,0xA1,0x00,0x06,0x4E,0x75,0x6F,0x76,0x6F,0x20,0x74, +0x65,0x6E,0x74,0x61,0x74,0x69,0x76,0x6F,0x20,0x61,0x76,0x76,0x69,0x6F,0x20,0x73, +0x69,0x73,0x74,0x65,0x6D,0x61,0x20,0x74,0x72,0x61,0x6D,0x69,0x74,0x65,0x20,0x72, +0x65,0x74,0x65,0x2E,0x00,0x83,0x00,0x01,0x49,0x6E,0x64,0x6C,0xE6,0x73,0x65,0x72, +0x20,0x73,0x79,0x73,0x74,0x65,0x6D,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x6D,0x65, +0x6C,0x2E,0x0D,0x0A,0x00,0x84,0x00,0x01,0x46,0x65,0x6A,0x6C,0x2E,0x0D,0x0A,0x00, +0x85,0x00,0x01,0x47,0x65,0x6E,0x73,0x74,0x61,0x72,0x74,0x65,0x72,0x20,0x73,0x79, +0x73,0x74,0x65,0x6D,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x6D,0x65,0x6C,0x2E,0x0D, +0x0A,0x00,0x86,0x00,0x01,0x46,0x6F,0x72,0x65,0x74,0x61,0x67,0x65,0x72,0x20,0x6E, +0x6F,0x72,0x6D,0x61,0x6C,0x20,0x73,0x79,0x73,0x74,0x65,0x6D,0x74,0x65,0x73,0x74, +0x2E,0x0D,0x0A,0x00,0x88,0x00,0x01,0x53,0x79,0x73,0x74,0x65,0x6D,0x74,0x65,0x73, +0x74,0x20,0x61,0x66,0x73,0x6C,0x75,0x74,0x74,0x65,0x74,0x2E,0x0D,0x0A,0x00,0x89, +0x00,0x01,0x4E,0x6F,0x72,0x6D,0x61,0x6C,0x20,0x6B,0xF8,0x72,0x73,0x65,0x6C,0x20, +0x69,0x6B,0x6B,0x65,0x20,0x6D,0x75,0x6C,0x69,0x67,0x2E,0x0D,0x0A,0x00,0x9A,0x00, +0x01,0x53,0x79,0x73,0x74,0x65,0x6D,0x69,0x6E,0x69,0x74,0x69,0x65,0x72,0x69,0x6E, +0x67,0x73,0x66,0x69,0x6C,0x3A,0x20,0x00,0x9D,0x00,0x01,0x48,0x75,0x6B,0x6F,0x6D, +0x6D,0x65,0x6C,0x73,0x65,0x73,0x6B,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74, +0x69,0x6F,0x6E,0x73,0x66,0x65,0x6A,0x6C,0x2E,0x0D,0x0A,0x00,0x9E,0x00,0x01,0x44, +0x65,0x72,0x20,0x65,0x72,0x20,0x69,0x6B,0x6B,0x65,0x20,0x73,0x70,0x65,0x63,0x69, +0x66,0x69,0x63,0x65,0x72,0x65,0x74,0x20,0x6E,0x6F,0x67,0x65,0x6E,0x20,0x65,0x6E, +0x68,0x65,0x64,0x20,0x74,0x69,0x6C,0x20,0x61,0x74,0x20,0x73,0x74,0x61,0x72,0x74, +0x65,0x20,0x66,0x72,0x61,0x2E,0x0D,0x0A,0x00,0x9F,0x00,0x01,0x45,0x6E,0x68,0x65, +0x64,0x65,0x72,0x20,0x64,0x65,0x72,0x20,0x6B,0x61,0x6E,0x20,0x62,0x65,0x6E,0x79, +0x74,0x74,0x65,0x73,0x3A,0x0D,0x0A,0x00,0xA0,0x00,0x01,0x48,0x76,0x69,0x6C,0x6B, +0x65,0x6E,0x20,0x65,0x6E,0x68,0x65,0x64,0x20,0x73,0x6B,0x61,0x6C,0x20,0x62,0x65, +0x6E,0x79,0x74,0x74,0x65,0x73,0x3F,0x20,0x00,0xA1,0x00,0x01,0x50,0x72,0xF8,0x76, +0x65,0x72,0x20,0x69,0x67,0x65,0x6E,0x20,0x61,0x74,0x20,0x73,0x74,0x61,0x72,0x74, +0x65,0x20,0x6F,0x76,0x65,0x72,0x20,0x6E,0x65,0x74,0x76,0xE6,0x72,0x6B,0x65,0x74, +0x2E,0x00,0x83,0x00,0x07,0x53,0x79,0x73,0x74,0x65,0x65,0x6D,0x20,0x77,0x6F,0x72, +0x64,0x74,0x20,0x6F,0x70,0x67,0x65,0x73,0x74,0x61,0x72,0x74,0x2E,0x0D,0x0A,0x00, +0x84,0x00,0x07,0x46,0x6F,0x75,0x74,0x2E,0x0D,0x0A,0x00,0x85,0x00,0x07,0x53,0x79, +0x73,0x74,0x65,0x65,0x6D,0x20,0x77,0x6F,0x72,0x64,0x74,0x20,0x6F,0x70,0x6E,0x69, +0x65,0x75,0x77,0x20,0x6F,0x70,0x67,0x65,0x73,0x74,0x61,0x72,0x74,0x2E,0x0D,0x0A, +0x00,0x86,0x00,0x07,0x53,0x79,0x73,0x74,0x65,0x65,0x6D,0x74,0x65,0x73,0x74,0x73, +0x20,0x77,0x6F,0x72,0x64,0x65,0x6E,0x20,0x75,0x69,0x74,0x67,0x65,0x76,0x6F,0x65, +0x72,0x64,0x2E,0x0D,0x0A,0x00,0x88,0x00,0x07,0x54,0x65,0x73,0x74,0x73,0x20,0x76, +0x6F,0x6C,0x74,0x6F,0x6F,0x69,0x64,0x2E,0x0D,0x0A,0x00,0x89,0x00,0x07,0x4E,0x6F, +0x72,0x6D,0x61,0x6C,0x65,0x20,0x77,0x65,0x72,0x6B,0x69,0x6E,0x67,0x20,0x6E,0x69, +0x65,0x74,0x20,0x6D,0x6F,0x67,0x65,0x6C,0x69,0x6A,0x6B,0x2E,0x0D,0x0A,0x00,0x9A, +0x00,0x07,0x4F,0x70,0x73,0x74,0x61,0x72,0x74,0x62,0x65,0x73,0x74,0x61,0x6E,0x64, +0x3A,0x20,0x00,0x9D,0x00,0x07,0x46,0x6F,0x75,0x74,0x20,0x69,0x6E,0x20,0x67,0x65, +0x68,0x65,0x75,0x67,0x65,0x6E,0x63,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74, +0x69,0x65,0x2E,0x0D,0x0A,0x00,0x9E,0x00,0x07,0x47,0x65,0x65,0x6E,0x20,0x73,0x74, +0x61,0x6E,0x64,0x61,0x61,0x72,0x64,0x20,0x6F,0x70,0x73,0x74,0x61,0x72,0x74,0x2D, +0x61,0x70,0x70,0x61,0x72,0x61,0x61,0x74,0x20,0x67,0x65,0x73,0x70,0x65,0x63,0x69, +0x66,0x69,0x63,0x65,0x65,0x72,0x64,0x2E,0x0D,0x0A,0x00,0x9F,0x00,0x07,0x42,0x65, +0x73,0x63,0x68,0x69,0x6B,0x62,0x61,0x72,0x65,0x20,0x61,0x70,0x70,0x61,0x72,0x61, +0x74,0x65,0x6E,0x3A,0x0D,0x0A,0x00,0xA0,0x00,0x07,0x41,0x70,0x70,0x61,0x72,0x61, +0x61,0x74,0x3F,0x20,0x00,0xA1,0x00,0x07,0x4E,0x65,0x74,0x77,0x65,0x72,0x6B,0x2D, +0x6F,0x70,0x73,0x74,0x61,0x72,0x74,0x70,0x72,0x6F,0x63,0x65,0x64,0x75,0x72,0x65, +0x20,0x77,0x6F,0x72,0x64,0x74,0x20,0x6F,0x70,0x6E,0x69,0x65,0x75,0x77,0x20,0x67, +0x65,0x73,0x74,0x61,0x72,0x74,0x2E,0x00,0x83,0x00,0x0B,0x4C,0x61,0x64,0x64,0x61, +0x72,0x20,0x73,0x79,0x73,0x74,0x65,0x6D,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x2E, +0x0D,0x0A,0x00,0x84,0x00,0x0B,0x46,0x65,0x6C,0x2E,0x0D,0x0A,0x00,0x85,0x00,0x0B, +0x4F,0x6D,0x73,0x74,0x61,0x72,0x74,0x20,0x61,0x76,0x20,0x73,0x79,0x73,0x74,0x65, +0x6D,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x2E,0x0D,0x0A,0x00,0x86,0x00,0x0B,0x4E, +0x6F,0x72,0x6D,0x61,0x6C,0x20,0x73,0x79,0x73,0x74,0x65,0x6D,0x6B,0x6F,0x6E,0x74, +0x72,0x6F,0x6C,0x6C,0x20,0x70,0xE5,0x67,0xE5,0x72,0x2E,0x0D,0x0A,0x00,0x88,0x00, +0x0B,0x53,0x79,0x73,0x74,0x65,0x6D,0x6B,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x6C,0x20, +0x6B,0x6C,0x61,0x72,0x0D,0x0A,0x00,0x89,0x00,0x0B,0x53,0x79,0x73,0x74,0x65,0x6D, +0x65,0x74,0x20,0x6B,0x61,0x6E,0x20,0x65,0x6A,0x20,0x61,0x6E,0x76,0xE4,0x6E,0x64, +0x61,0x73,0x2E,0x0D,0x0A,0x00,0x9A,0x00,0x0B,0x53,0x74,0x61,0x72,0x74,0x66,0x69, +0x6C,0x3A,0x20,0x00,0x9D,0x00,0x0B,0x46,0x65,0x6C,0x20,0x69,0x20,0x6D,0x69,0x6E, +0x6E,0x65,0x73,0x6B,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E, +0x65,0x6E,0x2E,0x0D,0x0A,0x00,0x9E,0x00,0x0B,0x49,0x6E,0x67,0x65,0x6E,0x20,0x73, +0x74,0x61,0x6E,0x64,0x61,0x72,0x64,0x65,0x6E,0x68,0x65,0x74,0x20,0x66,0xF6,0x72, +0x20,0x69,0x6E,0x6D,0x61,0x74,0x6E,0x69,0x6E,0x67,0x73,0x70,0x72,0x6F,0x67,0x72, +0x61,0x6D,0x6D,0x65,0x74,0x20,0x68,0x61,0x72,0x20,0x73,0x70,0x65,0x63,0x69,0x66, +0x69,0x63,0x65,0x72,0x61,0x74,0x73,0x2E,0x0D,0x0A,0x00,0x9F,0x00,0x0B,0x54,0x69, +0x6C,0x6C,0x67,0xE4,0x6E,0x67,0x6C,0x69,0x67,0x61,0x20,0x65,0x6E,0x68,0x65,0x74, +0x65,0x72,0x3A,0x0D,0x0A,0x00,0xA0,0x00,0x0B,0x45,0x6E,0x68,0x65,0x74,0x3F,0x20, +0x00,0xA1,0x00,0x0B,0x49,0x6E,0x6D,0x61,0x74,0x6E,0x69,0x6E,0x67,0x73,0x70,0x72, +0x6F,0x67,0x72,0x61,0x6D,0x6D,0x65,0x74,0x20,0x6B,0xF6,0x72,0x73,0x20,0x69,0x67, +0x65,0x6E,0x2E,0x00,0x83,0x00,0x0A,0x4A,0xE4,0x72,0x6A,0x65,0x73,0x74,0x65,0x6C, +0x6D,0xE4,0x6E,0x20,0x6C,0x61,0x74,0x61,0x75,0x73,0x2E,0x0D,0x0A,0x00,0x84,0x00, +0x0A,0x56,0x69,0x6B,0x61,0x2E,0x0D,0x0A,0x00,0x85,0x00,0x0A,0x55,0x75,0x73,0x69, +0x20,0x6B,0xE4,0x79,0x6E,0x6E,0x69,0x73,0x74,0x79,0x73,0x2E,0x0D,0x0A,0x00,0x86, +0x00,0x0A,0x4A,0xE4,0x72,0x6A,0x65,0x73,0x74,0x65,0x6C,0x6D,0xE4,0x6E,0x20,0x74, +0x65,0x73,0x74,0x61,0x75,0x73,0x2E,0x0D,0x0A,0x00,0x88,0x00,0x0A,0x54,0x65,0x73, +0x74,0x69,0x74,0x20,0x73,0x75,0x6F,0x72,0x69,0x74,0x65,0x74,0x74,0x75,0x2E,0x0D, +0x0A,0x00,0x89,0x00,0x0A,0x4E,0x6F,0x72,0x6D,0x61,0x61,0x6C,0x69,0x20,0x6B,0xE4, +0x79,0x74,0x74,0xF6,0x20,0x65,0x69,0x20,0x6F,0x6C,0x65,0x20,0x6D,0x61,0x68,0x64, +0x6F,0x6C,0x6C,0x69,0x6E,0x65,0x6E,0x2E,0x0D,0x0A,0x00,0x9A,0x00,0x0A,0x4B,0xE4, +0x79,0x6E,0x6E,0x69,0x73,0x74,0x79,0x73,0x74,0x69,0x65,0x64,0x6F,0x73,0x74,0x6F, +0x3A,0x20,0x00,0x9D,0x00,0x0A,0x4D,0x75,0x69,0x73,0x74,0x69,0x6E,0x20,0x6D,0xE4, +0xE4,0x72,0x69,0x74,0x79,0x73,0x76,0x69,0x72,0x68,0x65,0x2E,0x0D,0x0A,0x00,0x9E, +0x00,0x0A,0x4B,0xE4,0x79,0x6E,0x6E,0x69,0x73,0x74,0x79,0x73,0x6C,0x61,0x69,0x74, +0x65,0x74,0x74,0x61,0x20,0x65,0x69,0x20,0x6F,0x6C,0x65,0x20,0x6D,0xE4,0xE4,0x72, +0x69,0x74,0x65,0x74,0x74,0x79,0x2E,0x0D,0x0A,0x00,0x9F,0x00,0x0A,0x4B,0xE4,0x79, +0x74,0x65,0x74,0x74,0xE4,0x76,0x69,0x73,0x73,0xE4,0x20,0x6F,0x6C,0x65,0x76,0x61, +0x74,0x20,0x6C,0x61,0x69,0x74,0x74,0x65,0x65,0x74,0x3A,0x0D,0x0A,0x00,0xA0,0x00, +0x0A,0x4C,0x61,0x69,0x74,0x65,0x3F,0x20,0x00,0xA1,0x00,0x0A,0x56,0x65,0x72,0x6B, +0x6F,0x6E,0x20,0x6B,0xE4,0x79,0x6E,0x6E,0x69,0x73,0x74,0x79,0x73,0x74,0xE4,0x20, +0x79,0x72,0x69,0x74,0x65,0x74,0xE4,0xE4,0x6E,0x20,0x75,0x75,0x64,0x65,0x6C,0x6C, +0x65,0x65,0x6E,0x2E,0x00,0x83,0x00,0x08,0x53,0x79,0x73,0x74,0x65,0x6D,0x70,0x72, +0x6F,0x67,0x72,0x61,0x6D,0x20,0x6C,0x65,0x73,0x65,0x73,0x20,0x69,0x6E,0x6E,0x2E, +0x0D,0x0A,0x00,0x84,0x00,0x08,0x46,0x65,0x69,0x6C,0x2E,0x0D,0x0A,0x00,0x85,0x00, +0x08,0x53,0x79,0x73,0x74,0x65,0x6D,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x73, +0x74,0x61,0x72,0x74,0x65,0x73,0x20,0x70,0xE5,0x20,0x6E,0x79,0x74,0x74,0x2E,0x0D, +0x0A,0x00,0x86,0x00,0x08,0x53,0x74,0x61,0x6E,0x64,0x61,0x72,0x64,0x20,0x73,0x79, +0x73,0x74,0x65,0x6D,0x74,0x65,0x73,0x74,0x65,0x72,0x20,0x75,0x74,0x66,0xF8,0x72, +0x65,0x73,0x2E,0x0D,0x0A,0x00,0x88,0x00,0x08,0x54,0x65,0x73,0x74,0x65,0x72,0x20, +0x75,0x74,0x66,0xF8,0x72,0x74,0x2E,0x0D,0x0A,0x00,0x89,0x00,0x08,0x4E,0x6F,0x72, +0x6D,0x61,0x6C,0x20,0x64,0x72,0x69,0x66,0x74,0x20,0x75,0x6D,0x75,0x6C,0x69,0x67, +0x2E,0x0D,0x0A,0x00,0x9A,0x00,0x08,0x4F,0x70,0x70,0x73,0x74,0x61,0x72,0x74,0x69, +0x6E,0x67,0x73,0x66,0x69,0x6C,0x3A,0x20,0x00,0x9D,0x00,0x08,0x46,0x65,0x69,0x6C, +0x20,0x69,0x20,0x68,0x75,0x6B,0x6F,0x6D,0x6D,0x65,0x6C,0x73,0x65,0x73,0x6B,0x6F, +0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x73,0x6A,0x6F,0x6E,0x65,0x6E,0x2E,0x0D,0x0A, +0x00,0x9E,0x00,0x08,0x53,0x74,0x61,0x6E,0x64,0x61,0x72,0x64,0x20,0x6F,0x70,0x70, +0x73,0x74,0x61,0x72,0x74,0x65,0x6E,0x68,0x65,0x74,0x20,0x65,0x72,0x20,0x69,0x6B, +0x6B,0x65,0x20,0x73,0x70,0x65,0x73,0x69,0x66,0x69,0x73,0x65,0x72,0x74,0x2E,0x0D, +0x0A,0x00,0x9F,0x00,0x08,0x54,0x69,0x6C,0x67,0x6A,0x65,0x6E,0x67,0x65,0x6C,0x69, +0x67,0x65,0x20,0x65,0x6E,0x68,0x65,0x74,0x65,0x72,0x3A,0x0D,0x0A,0x00,0xA0,0x00, +0x08,0x45,0x6E,0x68,0x65,0x74,0x3F,0x20,0x00,0xA1,0x00,0x08,0x4F,0x70,0x70,0x73, +0x74,0x61,0x72,0x74,0x69,0x6E,0x67,0x73,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x6D, +0x65,0x74,0x20,0x6B,0x6A,0xF8,0x72,0x65,0x73,0x20,0x6E,0xE5,0x20,0x6F,0x76,0x65, +0x72,0x20,0x6E,0x65,0x74,0x74,0x76,0x65,0x72,0x6B,0x65,0x74,0x2E,0x00,0x83,0x00, +0x09,0x43,0x61,0x72,0x72,0x65,0x67,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x64,0x6F, +0x20,0x73,0x69,0x73,0x74,0x65,0x6D,0x61,0x2E,0x0D,0x0A,0x00,0x84,0x00,0x09,0x46, +0x61,0x6C,0x68,0x61,0x20,0x64,0x6F,0x20,0x73,0x69,0x73,0x74,0x65,0x6D,0x61,0x2E, +0x0D,0x0A,0x00,0x85,0x00,0x09,0x52,0x65,0x69,0x6E,0x63,0x69,0x61,0x6C,0x69,0x7A, +0x61,0xE7,0xE3,0x6F,0x20,0x64,0x65,0x20,0x73,0x69,0x73,0x74,0x65,0x6D,0x61,0x2E, +0x0D,0x0A,0x00,0x86,0x00,0x09,0x45,0x78,0x65,0x63,0x75,0xE7,0xE3,0x6F,0x20,0x64, +0x65,0x20,0x74,0x65,0x73,0x74,0x65,0x73,0x20,0x67,0x65,0x72,0x61,0x69,0x73,0x20, +0x64,0x6F,0x20,0x73,0x69,0x73,0x74,0x65,0x6D,0x61,0x2E,0x0D,0x0A,0x00,0x88,0x00, +0x09,0x46,0x69,0x6D,0x20,0x64,0x6F,0x73,0x20,0x74,0x65,0x73,0x74,0x65,0x73,0x2E, +0x0D,0x0A,0x00,0x89,0x00,0x09,0x41,0x20,0x63,0x6F,0x72,0x72,0x65,0x6E,0x74,0x65, +0x20,0x6F,0x70,0x65,0x72,0x61,0xE7,0xE3,0x6F,0x20,0x6E,0xE3,0x6F,0x20,0x65,0x20, +0x70,0x6F,0x73,0x73,0x69,0x76,0x65,0x6C,0x2E,0x0D,0x0A,0x00,0x9A,0x00,0x09,0x46, +0x69,0x63,0x68,0x65,0x69,0x72,0x6F,0x20,0x64,0x65,0x20,0x61,0x72,0x72,0x61,0x6E, +0x71,0x75,0x65,0x3A,0x20,0x00,0x9D,0x00,0x09,0x45,0x72,0x72,0x6F,0x20,0x64,0x65, +0x20,0x63,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0xE7,0xE3,0x6F,0x20,0x64,0x65, +0x20,0x6D,0x65,0x6D,0xF3,0x72,0x69,0x61,0x2E,0x0D,0x0A,0x00,0x9E,0x00,0x09,0x44, +0x69,0x73,0x70,0x6F,0x73,0x69,0x74,0x69,0x76,0x6F,0x20,0x64,0x65,0x20,0x61,0x72, +0x72,0x61,0x6E,0x71,0x75,0x65,0x20,0x6E,0xE3,0x6F,0x20,0x65,0x73,0x70,0x65,0x63, +0x69,0x66,0x69,0x63,0x61,0x64,0x6F,0x2E,0x0D,0x0A,0x00,0x9F,0x00,0x09,0x44,0x69, +0x73,0x70,0x6F,0x73,0x69,0x74,0x69,0x76,0x6F,0x73,0x20,0x64,0x69,0x73,0x70,0x6F, +0x6E,0xED,0x76,0x65,0x69,0x73,0x3A,0x0D,0x0A,0x00,0xA0,0x00,0x09,0x44,0x69,0x73, +0x70,0x6F,0x73,0x69,0x74,0x69,0x76,0x6F,0x3F,0x20,0x00,0xA1,0x00,0x09,0x4E,0x6F, +0x76,0x61,0x20,0x74,0x65,0x6E,0x74,0x61,0x74,0x69,0x76,0x61,0x20,0x64,0x65,0x20, +0x61,0x72,0x72,0x61,0x6E,0x71,0x75,0x65,0x20,0x61,0x20,0x70,0x61,0x72,0x74,0x69, +0x72,0x20,0x64,0x61,0x20,0x72,0x65,0x64,0x65,0x2E,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x0D,0x25,0x73,0x2D,0x25,0x63,0x20,0x25,0x63,0x25,0x64, +0x2E,0x25,0x64,0x00,0x2D,0x25,0x64,0x00,0x2C,0x20,0x56,0x4D,0x42,0x20,0x25,0x64, +0x2E,0x25,0x64,0x0D,0x0A,0x00,0x4B,0x41,0x36,0x35,0x35,0x58,0x00,0x41,0x36,0x35, +0x30,0x00,0x4B,0x41,0x36,0x35,0x35,0x00,0x05,0x00,0x03,0x00,0x01,0x00,0x3D,0x01, +0x00,0x00,0x3D,0x01,0x00,0x00,0x7B,0x01,0x00,0x00,0xA6,0x01,0x00,0x00,0x00,0x00, +0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0xFE,0xFF,0xFF,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0xFE,0xFF,0xFF,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x03, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0xFF,0x03,0x7E,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00, +0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00,0x00,0x00,0x00, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x28,0x00,0x00,0x00, +0x31,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x4C,0x00,0x00,0x00, +0x14,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x2F,0x00,0x00,0x00, +0x38,0x00,0x00,0x00,0x20,0x20,0x47,0x20,0x25,0x52,0x6C,0x20,0x00,0x20,0x20,0x49, +0x20,0x25,0x52,0x6C,0x20,0x00,0x20,0x20,0x56,0x20,0x25,0x52,0x6C,0x20,0x00,0x20, +0x20,0x50,0x20,0x25,0x52,0x6C,0x20,0x00,0x20,0x20,0x4D,0x20,0x25,0x52,0x6C,0x20, +0x00,0x14,0x00,0x00,0x00,0x1B,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x29,0x00,0x00, +0x00,0x30,0x00,0x00,0x00,0x25,0x52,0x62,0x0D,0x0D,0x0A,0x00,0x25,0x52,0x77,0x0D, +0x0D,0x0A,0x00,0x25,0x52,0x6C,0x0D,0x0D,0x0A,0x00,0x25,0x52,0x71,0x0D,0x0D,0x0A, +0x00,0x25,0x52,0x6F,0x0D,0x0D,0x0A,0x00,0x12,0x00,0x00,0x00,0x1F,0x00,0x00,0x00, +0x13,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x22,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x45,0x6E,0x74,0x65,0x72,0x20,0x64,0x65, +0x76,0x69,0x63,0x65,0x20,0x63,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69, +0x6F,0x6E,0x2C,0x20,0x48,0x45,0x4C,0x50,0x2C,0x20,0x6F,0x72,0x20,0x45,0x58,0x49, +0x54,0x0D,0x0A,0x00,0x0D,0x0A,0x41,0x64,0x64,0x72,0x65,0x73,0x73,0x2F,0x56,0x65, +0x63,0x74,0x6F,0x72,0x20,0x41,0x73,0x73,0x69,0x67,0x6E,0x6D,0x65,0x6E,0x74,0x73, +0x0D,0x0A,0x00,0x44,0x65,0x76,0x69,0x63,0x65,0x20,0x69,0x73,0x20,0x61,0x6D,0x62, +0x69,0x67,0x75,0x6F,0x75,0x73,0x0D,0x0A,0x00,0x44,0x65,0x76,0x69,0x63,0x65,0x20, +0x69,0x73,0x20,0x75,0x6E,0x6B,0x6E,0x6F,0x77,0x6E,0x0D,0x0A,0x00,0x4E,0x75,0x6D, +0x62,0x65,0x72,0x20,0x69,0x73,0x20,0x69,0x6E,0x76,0x61,0x6C,0x69,0x64,0x0D,0x0A, +0x00,0x44,0x65,0x76,0x69,0x63,0x65,0x73,0x3A,0x00,0x20,0x25,0x2D,0x31,0x32,0x2E, +0x2A,0x73,0x00,0x0D,0x0A,0x4E,0x75,0x6D,0x62,0x65,0x72,0x73,0x3A,0x0D,0x0A,0x20, +0x31,0x20,0x74,0x6F,0x20,0x32,0x35,0x35,0x2C,0x20,0x64,0x65,0x66,0x61,0x75,0x6C, +0x74,0x20,0x69,0x73,0x20,0x31,0x0D,0x0A,0x00,0x2D,0x25,0x30,0x36,0x6F,0x2F,0x25, +0x30,0x33,0x6F,0x00,0x44,0x65,0x76,0x69,0x63,0x65,0x20,0x43,0x53,0x52,0x2F,0x76, +0x65,0x63,0x74,0x6F,0x72,0x20,0x63,0x61,0x6E,0x6E,0x6F,0x74,0x20,0x66,0x6C,0x6F, +0x61,0x74,0x20,0x28,0x25,0x64,0x20,0x6E,0x6F,0x74,0x20,0x63,0x6F,0x6E,0x66,0x69, +0x67,0x75,0x72,0x65,0x64,0x29,0x0D,0x0A,0x00,0x25,0x63,0x25,0x2A,0x73,0x00,0x0F, +0x00,0x0E,0x01,0xD7,0x86,0x04,0x20,0x44,0x65,0x76,0x69,0x63,0x65,0x2C,0x4E,0x75, +0x6D,0x62,0x65,0x72,0x3F,0x20,0x00,0x00,0x7B,0xA1,0xFF,0xFF,0x31,0xA2,0xFF,0xFF, +0x66,0xA3,0xFF,0xFF,0x1F,0xA5,0xFF,0xFF,0xF0,0xA5,0xFF,0xFF,0x8B,0x88,0xCF,0xCC, +0xEF,0xEE,0xEF,0xFF,0x03,0x00,0x0F,0x00,0xEF,0x00,0xEF,0x0F,0x00,0x00,0x80,0x00, +0x00,0x48,0x41,0x4C,0x54,0x00,0x00,0x60,0x01,0x00,0x4E,0x4F,0x50,0x00,0x00,0x60, +0x02,0x00,0x52,0x45,0x49,0x00,0x00,0x60,0x03,0x00,0x42,0x50,0x54,0x00,0x00,0x60, +0x04,0x00,0x52,0x45,0x54,0x00,0x00,0x60,0x05,0x00,0x52,0x53,0x42,0x00,0x00,0xC0, +0x06,0x00,0x4C,0x44,0x50,0x43,0x54,0x58,0x00,0x00,0xC0,0x07,0x00,0x53,0x56,0x50, +0x43,0x54,0x58,0x41,0x00,0xB0,0x08,0x00,0x43,0x56,0x54,0x50,0x53,0x41,0x00,0xB0, +0x09,0x00,0x43,0x56,0x54,0x53,0x50,0x92,0x24,0xB9,0x0A,0x00,0x49,0x4E,0x44,0x45, +0x58,0x50,0x00,0x70,0x0B,0x00,0x43,0x52,0x43,0x08,0x00,0xCC,0x0C,0x00,0x50,0x52, +0x4F,0x42,0x45,0x52,0x08,0x00,0xCC,0x0D,0x00,0x50,0x52,0x4F,0x42,0x45,0x57,0x00, +0x00,0xC8,0x0E,0x00,0x49,0x4E,0x53,0x51,0x55,0x45,0x10,0x00,0xC8,0x0F,0x00,0x52, +0x45,0x4D,0x51,0x55,0x45,0x05,0x00,0x84,0x10,0x00,0x42,0x53,0x42,0x42,0x05,0x00, +0x64,0x11,0x00,0x42,0x52,0x42,0x05,0x00,0x84,0x12,0x00,0x42,0x4E,0x45,0x51,0x05, +0x00,0x84,0x13,0x00,0x42,0x45,0x51,0x4C,0x05,0x00,0x84,0x14,0x00,0x42,0x47,0x54, +0x52,0x05,0x00,0x84,0x15,0x00,0x42,0x4C,0x45,0x51,0x90,0x24,0x65,0x16,0x00,0x4A, +0x53,0x42,0x90,0x24,0x65,0x17,0x00,0x4A,0x4D,0x50,0x05,0x00,0x84,0x18,0x00,0x42, +0x47,0x45,0x51,0x05,0x00,0x84,0x19,0x00,0x42,0x4C,0x53,0x53,0x05,0x00,0xA4,0x1A, +0x00,0x42,0x47,0x54,0x52,0x55,0x05,0x00,0xA4,0x1B,0x00,0x42,0x4C,0x45,0x51,0x55, +0x05,0x00,0x64,0x1C,0x00,0x42,0x56,0x43,0x05,0x00,0x64,0x1D,0x00,0x42,0x56,0x53, +0x05,0x00,0xA4,0x1E,0x00,0x42,0x47,0x45,0x51,0x55,0x05,0x00,0xA4,0x1F,0x00,0x42, +0x4C,0x53,0x53,0x55,0x41,0x00,0xB0,0x20,0x00,0x41,0x44,0x44,0x50,0x34,0x41,0x10, +0xB8,0x21,0x00,0x41,0x44,0x44,0x50,0x36,0x41,0x00,0xB0,0x22,0x00,0x53,0x55,0x42, +0x50,0x34,0x41,0x10,0xB8,0x23,0x00,0x53,0x55,0x42,0x50,0x36,0x01,0x02,0xB4,0x24, +0x00,0x43,0x56,0x54,0x50,0x54,0x41,0x10,0x98,0x25,0x00,0x4D,0x55,0x4C,0x50,0x01, +0x02,0xB4,0x26,0x00,0x43,0x56,0x54,0x54,0x50,0x41,0x10,0x98,0x27,0x00,0x44,0x49, +0x56,0x50,0x01,0x00,0xAC,0x28,0x00,0x4D,0x4F,0x56,0x43,0x33,0x01,0x00,0xAC,0x29, +0x00,0x43,0x4D,0x50,0x43,0x33,0x01,0x00,0xB0,0x2A,0x00,0x53,0x43,0x41,0x4E,0x43, +0x01,0x00,0xB0,0x2B,0x00,0x53,0x50,0x41,0x4E,0x43,0x01,0x02,0xB4,0x2C,0x00,0x4D, +0x4F,0x56,0x43,0x35,0x01,0x02,0xB4,0x2D,0x00,0x43,0x4D,0x50,0x43,0x35,0x01,0x10, +0xB8,0x2E,0x00,0x4D,0x4F,0x56,0x54,0x43,0x01,0x10,0xD8,0x2F,0x00,0x4D,0x4F,0x56, +0x54,0x55,0x43,0x4E,0x92,0x84,0x30,0x00,0x42,0x53,0x42,0x57,0x4E,0x92,0x64,0x31, +0x00,0x42,0x52,0x57,0x51,0x92,0xA8,0x32,0x00,0x43,0x56,0x54,0x57,0x4C,0x41,0x92, +0xA8,0x33,0x00,0x43,0x56,0x54,0x57,0x42,0x01,0x00,0x8C,0x34,0x00,0x4D,0x4F,0x56, +0x50,0x01,0x00,0xAC,0x35,0x00,0x43,0x4D,0x50,0x50,0x33,0x81,0x00,0xAC,0x36,0x00, +0x43,0x56,0x54,0x50,0x4C,0x41,0x00,0xB0,0x37,0x00,0x43,0x4D,0x50,0x50,0x34,0x01, +0x00,0xD0,0x38,0x00,0x45,0x44,0x49,0x54,0x50,0x43,0x41,0x00,0xD0,0x39,0x00,0x4D, +0x41,0x54,0x43,0x48,0x43,0x08,0x00,0x8C,0x3A,0x00,0x4C,0x4F,0x43,0x43,0x08,0x00, +0x8C,0x3B,0x00,0x53,0x4B,0x50,0x43,0x51,0x92,0xC8,0x3C,0x00,0x4D,0x4F,0x56,0x5A, +0x57,0x4C,0x49,0x9C,0x90,0x3D,0x00,0x41,0x43,0x42,0x57,0x49,0x92,0xA8,0x3E,0x00, +0x4D,0x4F,0x56,0x41,0x57,0x49,0x92,0xC4,0x3F,0x00,0x50,0x55,0x53,0x48,0x41,0x57, +0x92,0x24,0xA9,0x40,0x00,0x41,0x44,0x44,0x46,0x32,0x92,0x24,0xAD,0x41,0x00,0x41, +0x44,0x44,0x46,0x33,0x92,0x24,0xA9,0x42,0x00,0x53,0x55,0x42,0x46,0x32,0x92,0x24, +0xAD,0x43,0x00,0x53,0x55,0x42,0x46,0x33,0x92,0x24,0xA9,0x44,0x00,0x4D,0x55,0x4C, +0x46,0x32,0x92,0x24,0xAD,0x45,0x00,0x4D,0x55,0x4C,0x46,0x33,0x92,0x24,0xA9,0x46, +0x00,0x44,0x49,0x56,0x46,0x32,0x92,0x24,0xAD,0x47,0x00,0x44,0x49,0x56,0x46,0x33, +0x82,0x24,0xA9,0x48,0x00,0x43,0x56,0x54,0x46,0x42,0x8A,0x24,0xA9,0x49,0x00,0x43, +0x56,0x54,0x46,0x57,0x92,0x24,0xA9,0x4A,0x00,0x43,0x56,0x54,0x46,0x4C,0x00,0x00, +0xC8,0x4B,0x00,0x43,0x56,0x54,0x52,0x46,0x4C,0x10,0x00,0xA8,0x4C,0x00,0x43,0x56, +0x54,0x42,0x46,0x51,0x92,0xA8,0x4D,0x00,0x43,0x56,0x54,0x57,0x46,0x92,0x24,0xA9, +0x4E,0x00,0x43,0x56,0x54,0x4C,0x46,0x92,0x2C,0x91,0x4F,0x00,0x41,0x43,0x42,0x46, +0x92,0x24,0x89,0x50,0x00,0x4D,0x4F,0x56,0x46,0x92,0x24,0x89,0x51,0x00,0x43,0x4D, +0x50,0x46,0x92,0x24,0xA9,0x52,0x00,0x4D,0x4E,0x45,0x47,0x46,0x92,0x24,0x85,0x53, +0x00,0x54,0x53,0x54,0x46,0x82,0x24,0xB5,0x54,0x00,0x45,0x4D,0x4F,0x44,0x46,0x0A, +0x24,0xAD,0x55,0x00,0x50,0x4F,0x4C,0x59,0x46,0x9A,0x24,0xA9,0x56,0x00,0x43,0x56, +0x54,0x46,0x44,0x49,0x92,0xA8,0x58,0x00,0x41,0x44,0x41,0x57,0x49,0x18,0x00,0xC8, +0x5C,0x00,0x49,0x4E,0x53,0x51,0x48,0x49,0x18,0x00,0xC8,0x5D,0x00,0x49,0x4E,0x53, +0x51,0x54,0x49,0x13,0x00,0xC8,0x5E,0x00,0x52,0x45,0x4D,0x51,0x48,0x49,0x13,0x00, +0xC8,0x5F,0x00,0x52,0x45,0x4D,0x51,0x54,0x49,0xDB,0xB6,0xA9,0x60,0x00,0x41,0x44, +0x44,0x44,0x32,0xDB,0xB6,0xAD,0x61,0x00,0x41,0x44,0x44,0x44,0x33,0xDB,0xB6,0xA9, +0x62,0x00,0x53,0x55,0x42,0x44,0x32,0xDB,0xB6,0xAD,0x63,0x00,0x53,0x55,0x42,0x44, +0x33,0xDB,0xB6,0xA9,0x64,0x00,0x4D,0x55,0x4C,0x44,0x32,0xDB,0xB6,0xAD,0x65,0x00, +0x4D,0x55,0x4C,0x44,0x33,0xDB,0xB6,0xA9,0x66,0x00,0x44,0x49,0x56,0x44,0x32,0xDB, +0xB6,0xAD,0x67,0x00,0x44,0x49,0x56,0x44,0x33,0xC3,0xB6,0xA9,0x68,0x00,0x43,0x56, +0x54,0x44,0x42,0xCB,0xB6,0xA9,0x69,0x00,0x43,0x56,0x54,0x44,0x57,0xD3,0xB6,0xA9, +0x6A,0x00,0x43,0x56,0x54,0x44,0x4C,0xD3,0xB6,0xC9,0x6B,0x00,0x43,0x56,0x54,0x52, +0x44,0x4C,0x18,0x00,0xA8,0x6C,0x00,0x43,0x56,0x54,0x42,0x44,0x59,0x92,0xA8,0x6D, +0x00,0x43,0x56,0x54,0x57,0x44,0x9A,0x24,0xA9,0x6E,0x00,0x43,0x56,0x54,0x4C,0x44, +0xDB,0xBC,0x91,0x6F,0x00,0x41,0x43,0x42,0x44,0xDB,0xB6,0x89,0x70,0x00,0x4D,0x4F, +0x56,0x44,0xDB,0xB6,0x89,0x71,0x00,0x43,0x4D,0x50,0x44,0xDB,0xB6,0xA9,0x72,0x00, +0x4D,0x4E,0x45,0x47,0x44,0xDB,0xB6,0x85,0x73,0x00,0x54,0x53,0x54,0x44,0xC3,0x34, +0xB4,0x74,0x00,0x45,0x4D,0x4F,0x44,0x44,0x0B,0x00,0xAC,0x75,0x00,0x50,0x4F,0x4C, +0x59,0x44,0x13,0x00,0xA8,0x76,0x00,0x43,0x56,0x54,0x44,0x46,0x90,0x00,0x8C,0x78, +0x00,0x41,0x53,0x48,0x4C,0xD8,0xB6,0x8D,0x79,0x00,0x41,0x53,0x48,0x51,0x92,0x06, +0x90,0x7A,0x00,0x45,0x4D,0x55,0x4C,0x9A,0x04,0x90,0x7B,0x00,0x45,0x44,0x49,0x56, +0xDB,0xB6,0x85,0x7C,0x00,0x43,0x4C,0x52,0x51,0xDB,0xB6,0x89,0x7D,0x00,0x4D,0x4F, +0x56,0x51,0xDB,0xB6,0xA9,0x7E,0x00,0x4D,0x4F,0x56,0x41,0x51,0xDB,0xB6,0xC5,0x7F, +0x00,0x50,0x55,0x53,0x48,0x41,0x51,0x00,0x00,0xA8,0x80,0x00,0x41,0x44,0x44,0x42, +0x32,0x00,0x00,0xAC,0x81,0x00,0x41,0x44,0x44,0x42,0x33,0x00,0x00,0xA8,0x82,0x00, +0x53,0x55,0x42,0x42,0x32,0x00,0x00,0xAC,0x83,0x00,0x53,0x55,0x42,0x42,0x33,0x00, +0x00,0xA8,0x84,0x00,0x4D,0x55,0x4C,0x42,0x32,0x00,0x00,0xAC,0x85,0x00,0x4D,0x55, +0x4C,0x42,0x33,0x00,0x00,0xA8,0x86,0x00,0x44,0x49,0x56,0x42,0x32,0x00,0x00,0xAC, +0x87,0x00,0x44,0x49,0x56,0x42,0x33,0x00,0x00,0xA8,0x88,0x00,0x42,0x49,0x53,0x42, +0x32,0x00,0x00,0xAC,0x89,0x00,0x42,0x49,0x53,0x42,0x33,0x00,0x00,0xA8,0x8A,0x00, +0x42,0x49,0x43,0x42,0x32,0x00,0x00,0xAC,0x8B,0x00,0x42,0x49,0x43,0x42,0x33,0x00, +0x00,0xA8,0x8C,0x00,0x58,0x4F,0x52,0x42,0x32,0x00,0x00,0xAC,0x8D,0x00,0x58,0x4F, +0x52,0x42,0x33,0x00,0x00,0xA8,0x8E,0x00,0x4D,0x4E,0x45,0x47,0x42,0x00,0x00,0xAC, +0x8F,0x00,0x43,0x41,0x53,0x45,0x42,0x00,0x00,0x88,0x90,0x00,0x4D,0x4F,0x56,0x42, +0x00,0x00,0x88,0x91,0x00,0x43,0x4D,0x50,0x42,0x00,0x00,0xA8,0x92,0x00,0x4D,0x43, +0x4F,0x4D,0x42,0x00,0x00,0x88,0x93,0x00,0x42,0x49,0x54,0x42,0x00,0x00,0x84,0x94, +0x00,0x43,0x4C,0x52,0x42,0x00,0x00,0x84,0x95,0x00,0x54,0x53,0x54,0x42,0x00,0x00, +0x84,0x96,0x00,0x49,0x4E,0x43,0x42,0x00,0x00,0x84,0x97,0x00,0x44,0x45,0x43,0x42, +0x10,0x00,0xA8,0x98,0x00,0x43,0x56,0x54,0x42,0x4C,0x08,0x00,0xA8,0x99,0x00,0x43, +0x56,0x54,0x42,0x57,0x10,0x00,0xC8,0x9A,0x00,0x4D,0x4F,0x56,0x5A,0x42,0x4C,0x08, +0x00,0xC8,0x9B,0x00,0x4D,0x4F,0x56,0x5A,0x42,0x57,0x90,0x24,0x8D,0x9C,0x00,0x52, +0x4F,0x54,0x4C,0x00,0x0C,0x90,0x9D,0x00,0x41,0x43,0x42,0x42,0x00,0x00,0xA8,0x9E, +0x00,0x4D,0x4F,0x56,0x41,0x42,0x00,0x00,0xC4,0x9F,0x00,0x50,0x55,0x53,0x48,0x41, +0x42,0x49,0x92,0xA8,0xA0,0x00,0x41,0x44,0x44,0x57,0x32,0x49,0x92,0xAC,0xA1,0x00, +0x41,0x44,0x44,0x57,0x33,0x49,0x92,0xA8,0xA2,0x00,0x53,0x55,0x42,0x57,0x32,0x49, +0x92,0xAC,0xA3,0x00,0x53,0x55,0x42,0x57,0x33,0x49,0x92,0xA8,0xA4,0x00,0x4D,0x55, +0x4C,0x57,0x32,0x49,0x92,0xAC,0xA5,0x00,0x4D,0x55,0x4C,0x57,0x33,0x49,0x92,0xA8, +0xA6,0x00,0x44,0x49,0x56,0x57,0x32,0x49,0x92,0xAC,0xA7,0x00,0x44,0x49,0x56,0x57, +0x33,0x49,0x92,0xA8,0xA8,0x00,0x42,0x49,0x53,0x57,0x32,0x49,0x92,0xAC,0xA9,0x00, +0x42,0x49,0x53,0x57,0x33,0x49,0x92,0xA8,0xAA,0x00,0x42,0x49,0x43,0x57,0x32,0x49, +0x92,0xAC,0xAB,0x00,0x42,0x49,0x43,0x57,0x33,0x49,0x92,0xA8,0xAC,0x00,0x58,0x4F, +0x52,0x57,0x32,0x49,0x92,0xAC,0xAD,0x00,0x58,0x4F,0x52,0x57,0x33,0x49,0x92,0xA8, +0xAE,0x00,0x4D,0x4E,0x45,0x47,0x57,0x49,0x92,0xAC,0xAF,0x00,0x43,0x41,0x53,0x45, +0x57,0x49,0x92,0x88,0xB0,0x00,0x4D,0x4F,0x56,0x57,0x49,0x92,0x88,0xB1,0x00,0x43, +0x4D,0x50,0x57,0x49,0x92,0xA8,0xB2,0x00,0x4D,0x43,0x4F,0x4D,0x57,0x49,0x92,0x88, +0xB3,0x00,0x42,0x49,0x54,0x57,0x49,0x92,0x84,0xB4,0x00,0x43,0x4C,0x52,0x57,0x49, +0x92,0x84,0xB5,0x00,0x54,0x53,0x54,0x57,0x49,0x92,0x84,0xB6,0x00,0x49,0x4E,0x43, +0x57,0x49,0x92,0x84,0xB7,0x00,0x44,0x45,0x43,0x57,0x01,0x00,0xC4,0xB8,0x00,0x42, +0x49,0x53,0x50,0x53,0x57,0x01,0x00,0xC4,0xB9,0x00,0x42,0x49,0x43,0x50,0x53,0x57, +0x49,0x92,0x84,0xBA,0x00,0x50,0x4F,0x50,0x52,0x49,0x92,0xA4,0xBB,0x00,0x50,0x55, +0x53,0x48,0x52,0x01,0x00,0x80,0xBC,0x00,0x43,0x48,0x4D,0x4B,0x01,0x00,0x80,0xBD, +0x00,0x43,0x48,0x4D,0x45,0x01,0x00,0x80,0xBE,0x00,0x43,0x48,0x4D,0x53,0x01,0x00, +0x80,0xBF,0x00,0x43,0x48,0x4D,0x55,0x92,0x24,0xA9,0xC0,0x00,0x41,0x44,0x44,0x4C, +0x32,0x92,0x24,0xAD,0xC1,0x00,0x41,0x44,0x44,0x4C,0x33,0x92,0x24,0xA9,0xC2,0x00, +0x53,0x55,0x42,0x4C,0x32,0x92,0x24,0xAD,0xC3,0x00,0x53,0x55,0x42,0x4C,0x33,0x92, +0x24,0xA9,0xC4,0x00,0x4D,0x55,0x4C,0x4C,0x32,0x92,0x24,0xAD,0xC5,0x00,0x4D,0x55, +0x4C,0x4C,0x33,0x92,0x24,0xA9,0xC6,0x00,0x44,0x49,0x56,0x4C,0x32,0x92,0x24,0xAD, +0xC7,0x00,0x44,0x49,0x56,0x4C,0x33,0x92,0x24,0xA9,0xC8,0x00,0x42,0x49,0x53,0x4C, +0x32,0x92,0x24,0xAD,0xC9,0x00,0x42,0x49,0x53,0x4C,0x33,0x92,0x24,0xA9,0xCA,0x00, +0x42,0x49,0x43,0x4C,0x32,0x92,0x24,0xAD,0xCB,0x00,0x42,0x49,0x43,0x4C,0x33,0x92, +0x24,0xA9,0xCC,0x00,0x58,0x4F,0x52,0x4C,0x32,0x92,0x24,0xAD,0xCD,0x00,0x58,0x4F, +0x52,0x4C,0x33,0x92,0x24,0xA9,0xCE,0x00,0x4D,0x4E,0x45,0x47,0x4C,0x92,0x24,0xAD, +0xCF,0x00,0x43,0x41,0x53,0x45,0x4C,0x92,0x24,0x89,0xD0,0x00,0x4D,0x4F,0x56,0x4C, +0x92,0x24,0x89,0xD1,0x00,0x43,0x4D,0x50,0x4C,0x92,0x24,0xA9,0xD2,0x00,0x4D,0x43, +0x4F,0x4D,0x4C,0x92,0x24,0x89,0xD3,0x00,0x42,0x49,0x54,0x4C,0x92,0x24,0x85,0xD4, +0x00,0x43,0x4C,0x52,0x4C,0x92,0x24,0x85,0xD5,0x00,0x54,0x53,0x54,0x4C,0x92,0x24, +0x85,0xD6,0x00,0x49,0x4E,0x43,0x4C,0x92,0x24,0x85,0xD7,0x00,0x44,0x45,0x43,0x4C, +0x92,0x24,0x89,0xD8,0x00,0x41,0x44,0x57,0x43,0x92,0x24,0x89,0xD9,0x00,0x53,0x42, +0x57,0x43,0x92,0x24,0x89,0xDA,0x00,0x4D,0x54,0x50,0x52,0x92,0x24,0x89,0xDB,0x00, +0x4D,0x46,0x50,0x52,0x92,0x24,0xC5,0xDC,0x00,0x4D,0x4F,0x56,0x50,0x53,0x4C,0x92, +0x24,0xA5,0xDD,0x00,0x50,0x55,0x53,0x48,0x4C,0x92,0x24,0xA9,0xDE,0x00,0x4D,0x4F, +0x56,0x41,0x4C,0x92,0x24,0xC5,0xDF,0x00,0x50,0x55,0x53,0x48,0x41,0x4C,0x42,0x25, +0x6D,0xE0,0x00,0x42,0x42,0x53,0x42,0x25,0x6D,0xE1,0x00,0x42,0x42,0x43,0x42,0x25, +0x8D,0xE2,0x00,0x42,0x42,0x53,0x53,0x42,0x25,0x8D,0xE3,0x00,0x42,0x42,0x43,0x53, +0x42,0x25,0x8D,0xE4,0x00,0x42,0x42,0x53,0x43,0x42,0x25,0x8D,0xE5,0x00,0x42,0x42, +0x43,0x43,0x42,0x25,0xAD,0xE6,0x00,0x42,0x42,0x53,0x53,0x49,0x42,0x25,0xAD,0xE7, +0x00,0x42,0x42,0x43,0x43,0x49,0xAA,0x24,0x89,0xE8,0x00,0x42,0x4C,0x42,0x53,0xAA, +0x24,0x89,0xE9,0x00,0x42,0x4C,0x42,0x43,0x02,0x24,0x71,0xEA,0x00,0x46,0x46,0x53, +0x02,0x24,0x71,0xEB,0x00,0x46,0x46,0x43,0x02,0x24,0x91,0xEC,0x00,0x43,0x4D,0x50, +0x56,0x02,0x24,0xB1,0xED,0x00,0x43,0x4D,0x50,0x5A,0x56,0x02,0x24,0x91,0xEE,0x00, +0x45,0x58,0x54,0x56,0x02,0x24,0xB1,0xEF,0x00,0x45,0x58,0x54,0x5A,0x56,0x12,0x20, +0x91,0xF0,0x00,0x49,0x4E,0x53,0x56,0x92,0x2C,0x91,0xF1,0x00,0x41,0x43,0x42,0x4C, +0x42,0x25,0xCD,0xF2,0x00,0x41,0x4F,0x42,0x4C,0x53,0x53,0x42,0x25,0xCD,0xF3,0x00, +0x41,0x4F,0x42,0x4C,0x45,0x51,0xAA,0x24,0xC9,0xF4,0x00,0x53,0x4F,0x42,0x47,0x45, +0x51,0xAA,0x24,0xC9,0xF5,0x00,0x53,0x4F,0x42,0x47,0x54,0x52,0x82,0x24,0xA9,0xF6, +0x00,0x43,0x56,0x54,0x4C,0x42,0x8A,0x24,0xA9,0xF7,0x00,0x43,0x56,0x54,0x4C,0x57, +0x08,0x10,0x90,0xF8,0x00,0x41,0x53,0x48,0x50,0x0A,0x24,0xAD,0xF9,0x00,0x43,0x56, +0x54,0x4C,0x50,0x00,0x00,0xA8,0xFA,0x00,0x43,0x41,0x4C,0x4C,0x47,0x02,0x00,0xA8, +0xFB,0x00,0x43,0x41,0x4C,0x4C,0x53,0x00,0x00,0x60,0xFC,0x00,0x58,0x46,0x43,0x23, +0x00,0xA8,0x32,0xFD,0x43,0x56,0x54,0x44,0x48,0x13,0x00,0xA8,0x33,0xFD,0x43,0x56, +0x54,0x47,0x46,0xDB,0xB6,0xA9,0x40,0xFD,0x41,0x44,0x44,0x47,0x32,0xDB,0xB6,0xAD, +0x41,0xFD,0x41,0x44,0x44,0x47,0x33,0xDB,0xB6,0xA9,0x42,0xFD,0x53,0x55,0x42,0x47, +0x32,0xDB,0xB6,0xAD,0x43,0xFD,0x53,0x55,0x42,0x47,0x33,0xDB,0xB6,0xA9,0x44,0xFD, +0x4D,0x55,0x4C,0x47,0x32,0xDB,0xB6,0xAD,0x45,0xFD,0x4D,0x55,0x4C,0x47,0x33,0xDB, +0xB6,0xA9,0x46,0xFD,0x44,0x49,0x56,0x47,0x32,0xDB,0xB6,0xAD,0x47,0xFD,0x44,0x49, +0x56,0x47,0x33,0xC3,0xB6,0xA9,0x48,0xFD,0x43,0x56,0x54,0x47,0x42,0xCB,0xB6,0xA9, +0x49,0xFD,0x43,0x56,0x54,0x47,0x57,0xD3,0xB6,0xA9,0x4A,0xFD,0x43,0x56,0x54,0x47, +0x4C,0xD3,0xB6,0xC9,0x4B,0xFD,0x43,0x56,0x54,0x52,0x47,0x4C,0xD8,0xB6,0xA9,0x4C, +0xFD,0x43,0x56,0x54,0x42,0x47,0x59,0x92,0xA8,0x4D,0xFD,0x43,0x56,0x54,0x57,0x47, +0x9A,0x24,0xA9,0x4E,0xFD,0x43,0x56,0x54,0x4C,0x47,0xDB,0xBC,0x91,0x4F,0xFD,0x41, +0x43,0x42,0x47,0xDB,0xB6,0x89,0x50,0xFD,0x4D,0x4F,0x56,0x47,0xDB,0xB6,0x89,0x51, +0xFD,0x43,0x4D,0x50,0x47,0xDB,0xB6,0xA9,0x52,0xFD,0x4D,0x4E,0x45,0x47,0x47,0xDB, +0xB6,0x85,0x53,0xFD,0x54,0x53,0x54,0x47,0xCB,0xB4,0xB5,0x54,0xFD,0x45,0x4D,0x4F, +0x44,0x47,0x0B,0xB6,0xAD,0x55,0xFD,0x50,0x4F,0x4C,0x59,0x47,0xE3,0xB6,0xA9,0x56, +0xFD,0x43,0x56,0x54,0x47,0x48,0x24,0x49,0xAA,0x60,0xFD,0x41,0x44,0x44,0x48,0x32, +0x24,0x49,0xAE,0x61,0xFD,0x41,0x44,0x44,0x48,0x33,0x24,0x49,0xAA,0x62,0xFD,0x53, +0x55,0x42,0x48,0x32,0x24,0x49,0xAE,0x63,0xFD,0x53,0x55,0x42,0x48,0x33,0x24,0x49, +0xAA,0x64,0xFD,0x4D,0x55,0x4C,0x48,0x32,0x24,0x49,0xAE,0x65,0xFD,0x4D,0x55,0x4C, +0x48,0x33,0x24,0x49,0xAA,0x66,0xFD,0x44,0x49,0x56,0x48,0x32,0x24,0x49,0xAE,0x67, +0xFD,0x44,0x49,0x56,0x48,0x33,0x04,0x49,0xAA,0x68,0xFD,0x43,0x56,0x54,0x48,0x42, +0x0C,0x49,0xAA,0x69,0xFD,0x43,0x56,0x54,0x48,0x57,0x14,0x49,0xAA,0x6A,0xFD,0x43, +0x56,0x54,0x48,0x4C,0x14,0x49,0xCA,0x6B,0xFD,0x43,0x56,0x54,0x52,0x48,0x4C,0x20, +0x49,0xAA,0x6C,0xFD,0x43,0x56,0x54,0x42,0x48,0x21,0x00,0xA8,0x6D,0xFD,0x43,0x56, +0x54,0x57,0x48,0x22,0x49,0xAA,0x6E,0xFD,0x43,0x56,0x54,0x4C,0x48,0x24,0x4D,0x92, +0x6F,0xFD,0x41,0x43,0x42,0x48,0x24,0x49,0x8A,0x70,0xFD,0x4D,0x4F,0x56,0x48,0x24, +0x49,0x8A,0x71,0xFD,0x43,0x4D,0x50,0x48,0x24,0x49,0xAA,0x72,0xFD,0x4D,0x4E,0x45, +0x47,0x48,0x24,0x49,0x86,0x73,0xFD,0x54,0x53,0x54,0x48,0x0C,0x45,0xB6,0x74,0xFD, +0x45,0x4D,0x4F,0x44,0x48,0x0C,0x48,0xAE,0x75,0xFD,0x50,0x4F,0x4C,0x59,0x48,0x1C, +0x49,0xAA,0x76,0xFD,0x43,0x56,0x54,0x48,0x47,0x24,0x49,0x86,0x7C,0xFD,0x43,0x4C, +0x52,0x4F,0x24,0x49,0x8A,0x7D,0xFD,0x4D,0x4F,0x56,0x4F,0x24,0x49,0xAA,0x7E,0xFD, +0x4D,0x4F,0x56,0x41,0x48,0x24,0x49,0xCA,0x7F,0xFD,0x50,0x55,0x53,0x48,0x41,0x48, +0x22,0x00,0xA8,0x98,0xFD,0x43,0x56,0x54,0x46,0x48,0x1A,0x00,0xA8,0x99,0xFD,0x43, +0x56,0x54,0x46,0x47,0x14,0x49,0xAA,0xF6,0xFD,0x43,0x56,0x54,0x48,0x46,0x1C,0x49, +0xAA,0xF7,0xFD,0x43,0x56,0x54,0x48,0x44,0x92,0x24,0x81,0xFD,0xFF,0x42,0x55,0x47, +0x4C,0x49,0x92,0x80,0xFE,0xFF,0x42,0x55,0x47,0x57,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x25,0x2D,0x38,0x2E,0x2A,0x73,0x00,0x25,0x30,0x34,0x58,0x20,0x00,0x20, +0x20,0x25,0x30,0x32,0x58,0x20,0x00,0x20,0x3F,0x3F,0x3F,0x20,0x0D,0x0A,0x00,0x2C, +0x00,0x20,0x20,0x20,0x20,0x20,0x2E,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x09,0x25, +0x30,0x38,0x58,0x0D,0x0A,0x00,0x23,0x25,0x30,0x38,0x58,0x00,0x23,0x25,0x30,0x34, +0x58,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00, +0x08,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, +0x04,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x80,0x00,0x00,0x00, +0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x00,0x00,0x00,0x40,0x00,0x00,0x00, +0x80,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x46,0x00,0x00,0x00, +0x49,0x00,0x00,0x00,0x4C,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x52,0x00,0x00,0x00, +0x55,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x5B,0x00,0x00,0x00,0x5E,0x00,0x00,0x00, +0x62,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x6C,0x00,0x00,0x00, +0x6F,0x00,0x00,0x00,0x52,0x30,0x00,0x52,0x31,0x00,0x52,0x32,0x00,0x52,0x33,0x00, +0x52,0x34,0x00,0x52,0x35,0x00,0x52,0x36,0x00,0x52,0x37,0x00,0x52,0x38,0x00,0x52, +0x39,0x00,0x52,0x31,0x30,0x00,0x52,0x31,0x31,0x00,0x41,0x50,0x00,0x46,0x50,0x00, +0x53,0x50,0x00,0x50,0x43,0x00,0x00,0x3F,0x1A,0x9C,0x40,0x4F,0x30,0x9C,0x50,0x5F, +0x77,0x9C,0x60,0x6F,0x9B,0x9C,0x70,0x7F,0xBF,0x9C,0x80,0x8E,0xE3,0x9C,0x90,0x9E, +0x34,0x9D,0xA0,0xAE,0x70,0x9D,0xB0,0xBE,0x70,0x9D,0xC0,0xCE,0xA7,0x9D,0xD0,0xDE, +0xA7,0x9D,0xE0,0xEE,0xDE,0x9D,0xF0,0xFE,0xDE,0x9D,0x8F,0x8F,0x07,0x9D,0x9F,0x9F, +0x58,0x9D,0xAF,0xAF,0x15,0x9E,0xBF,0xBF,0x15,0x9E,0xCF,0xCF,0x3D,0x9E,0xDF,0xDF, +0x3D,0x9E,0xEF,0xEF,0x65,0x9E,0xFF,0xFF,0x65,0x9E,0x25,0x30,0x38,0x58,0x00,0x53, +0x5E,0x23,0x25,0x30,0x32,0x58,0x00,0x5B,0x25,0x73,0x5D,0x00,0x25,0x73,0x00,0x28, +0x25,0x73,0x29,0x00,0x2D,0x28,0x25,0x73,0x29,0x00,0x28,0x25,0x73,0x29,0x2B,0x00, +0x14,0x00,0x00,0x00,0x1B,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x29,0x00,0x00,0x00, +0x30,0x00,0x00,0x00,0x49,0x5E,0x23,0x25,0x72,0x62,0x00,0x49,0x5E,0x23,0x25,0x72, +0x77,0x00,0x49,0x5E,0x23,0x25,0x72,0x6C,0x00,0x49,0x5E,0x23,0x25,0x72,0x71,0x00, +0x49,0x5E,0x23,0x25,0x72,0x6F,0x00,0x40,0x28,0x25,0x73,0x29,0x2B,0x00,0x40,0x23, +0x25,0x30,0x38,0x58,0x00,0x42,0x5E,0x25,0x30,0x32,0x58,0x28,0x25,0x73,0x29,0x00, +0x40,0x42,0x5E,0x25,0x30,0x32,0x58,0x28,0x25,0x73,0x29,0x00,0x57,0x5E,0x25,0x30, +0x34,0x58,0x28,0x25,0x73,0x29,0x00,0x40,0x57,0x5E,0x25,0x30,0x34,0x58,0x28,0x25, +0x73,0x29,0x00,0x4C,0x5E,0x25,0x30,0x38,0x58,0x28,0x25,0x73,0x29,0x00,0x40,0x4C, +0x5E,0x25,0x30,0x38,0x58,0x28,0x25,0x73,0x29,0x00,0x42,0x5E,0x25,0x30,0x38,0x58, +0x00,0x40,0x42,0x5E,0x25,0x30,0x38,0x58,0x00,0x57,0x5E,0x25,0x30,0x38,0x58,0x00, +0x40,0x57,0x5E,0x25,0x30,0x38,0x58,0x00,0x4C,0x5E,0x25,0x30,0x38,0x58,0x00,0x40, +0x4C,0x5E,0x25,0x30,0x38,0x58,0x00,0x00,0x17,0x13,0x0C,0x08,0x1F,0x1C,0x0C,0x08, +0x08,0x20,0x08,0x00,0x08,0x08,0x20,0x20,0x08,0x08,0x00,0x5C,0x25,0x63,0x00,0x5C, +0x5E,0x25,0x63,0x00,0x25,0x63,0x00,0x5E,0x25,0x63,0x00,0x5C,0x00,0x08,0x00,0x08, +0x00,0x92,0x9E,0x7F,0x00,0x7F,0x00,0x92,0x9E,0x0D,0x00,0x0D,0x00,0xA3,0x9E,0x09, +0x00,0x09,0x00,0xBF,0x9E,0x20,0x00,0x20,0x00,0xBF,0x9E,0x12,0x00,0x12,0x00,0xDB, +0x9E,0x15,0x00,0x15,0x00,0x0A,0x9F,0x21,0x00,0x7E,0x00,0x2D,0x9F,0xA1,0x00,0xFE, +0x00,0x2D,0x9F,0x00,0x00,0xFF,0xFF,0x4B,0x9F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x25, +0x6D,0x00,0x00,0x00,0x5E,0x4F,0x0D,0x0A,0x00,0x00,0x00,0x00,0x63,0x00,0x01,0x80, +0x62,0x00,0x02,0x80,0x78,0x00,0x03,0x80,0x6F,0x00,0x04,0x80,0x75,0x00,0x05,0x80, +0x64,0x00,0x06,0x80,0x73,0x00,0x07,0x80,0x6D,0x00,0x08,0x80,0x62,0x72,0x09,0x80, +0x77,0x72,0x0A,0x80,0x6C,0x72,0x0B,0x80,0x71,0x72,0x0C,0x80,0x6F,0x72,0x0D,0x80, +0x66,0x72,0x0E,0x80,0x64,0x72,0x0F,0x80,0x67,0x72,0x10,0x80,0x68,0x72,0x11,0x80, +0x00,0x00,0x00,0x00,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42, +0x43,0x44,0x45,0x46,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x61,0x62, +0x63,0x64,0x65,0x66,0x00,0x03,0x9B,0x9B,0x00,0x0A,0x8E,0x8F,0x00,0x07,0x1B,0x1B, +0x00,0x00,0x00,0xFF,0x07,0x07,0x1B,0x1B,0x07,0x00,0x18,0x18,0x07,0x00,0x1A,0x1A, +0x07,0x01,0x00,0x1F,0x07,0x08,0x20,0x2F,0x07,0x03,0x5B,0x5B,0x07,0x02,0x40,0x5F, +0x07,0x09,0x30,0x7E,0x07,0x0C,0x00,0xFF,0x08,0x07,0x1B,0x1B,0x08,0x00,0x18,0x18, +0x08,0x00,0x1A,0x1A,0x08,0x03,0x9B,0x9B,0x08,0x08,0x20,0x2F,0x08,0x09,0x30,0x7E, +0x08,0x01,0x00,0x1F,0x08,0x0C,0x00,0xFF,0x03,0x03,0x9B,0x9B,0x03,0x07,0x1B,0x1B, +0x03,0x00,0x18,0x18,0x03,0x00,0x1A,0x1A,0x03,0x01,0x00,0x1F,0x03,0x04,0x30,0x3F, +0x03,0x05,0x20,0x2F,0x03,0x06,0x40,0x7E,0x03,0x0C,0x00,0xFF,0x04,0x03,0x9B,0x9B, +0x04,0x07,0x1B,0x1B,0x04,0x00,0x18,0x18,0x04,0x00,0x1A,0x1A,0x04,0x01,0x00,0x1F, +0x04,0x04,0x30,0x3F,0x04,0x05,0x20,0x2F,0x04,0x06,0x40,0x7E,0x04,0x0C,0x00,0xFF, +0x05,0x03,0x9B,0x9B,0x05,0x07,0x1B,0x1B,0x05,0x00,0x18,0x18,0x05,0x00,0x1A,0x1A, +0x05,0x01,0x00,0x1F,0x05,0x05,0x20,0x2F,0x05,0x06,0x40,0x7E,0x05,0x0C,0x00,0xFF, +0x0A,0x0B,0x21,0x7E,0x0A,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xD2,0x11,0xE0,0x00, +0x00,0x00,0x00,0xE0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x84,0x80,0x00, +0x00,0x00,0x00,0xE0,0xBF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x1B,0x5B,0x63,0x00, +0x27,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x05,0x00,0x00,0x00, +0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00, +0x20,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x2E,0x00,0x00,0x00,0x35,0x00,0x00,0x00, +0x3C,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x4A,0x00,0x00,0x00,0x51,0x00,0x00,0x00, +0x20,0x20,0x33,0x30,0x30,0x0D,0x0A,0x20,0x20,0x36,0x30,0x30,0x0D,0x0A,0x20,0x31, +0x32,0x30,0x30,0x0D,0x0A,0x20,0x32,0x34,0x30,0x30,0x0D,0x0A,0x20,0x34,0x38,0x30, +0x30,0x0D,0x0A,0x20,0x39,0x36,0x30,0x30,0x0D,0x0A,0x31,0x39,0x32,0x30,0x30,0x0D, +0x0A,0x33,0x38,0x34,0x30,0x30,0x0D,0x0A,0x00,0x00,0x89,0x89,0x25,0x72,0x62,0x2E, +0x2E,0x00,0x0D,0x0A,0x0D,0x0A,0x3F,0x25,0x72,0x62,0x20,0x25,0x31,0x78,0x20,0x25, +0x72,0x62,0x20,0x25,0x72,0x62,0x20,0x25,0x72,0x62,0x20,0x25,0x72,0x77,0x0D,0x0A, +0x00,0x0D,0x0A,0x50,0x31,0x3D,0x25,0x30,0x38,0x58,0x20,0x20,0x50,0x32,0x3D,0x25, +0x30,0x38,0x58,0x20,0x20,0x50,0x33,0x3D,0x25,0x30,0x38,0x58,0x20,0x20,0x50,0x34, +0x3D,0x25,0x30,0x38,0x58,0x20,0x20,0x50,0x35,0x3D,0x25,0x30,0x38,0x58,0x0D,0x0A, +0x50,0x36,0x3D,0x25,0x30,0x38,0x58,0x20,0x20,0x50,0x37,0x3D,0x25,0x30,0x38,0x58, +0x20,0x20,0x50,0x38,0x3D,0x25,0x30,0x38,0x58,0x20,0x20,0x50,0x39,0x3D,0x25,0x30, +0x38,0x58,0x20,0x50,0x31,0x30,0x3D,0x25,0x30,0x38,0x58,0x0D,0x0A,0x00,0x72,0x30, +0x3D,0x25,0x30,0x38,0x58,0x20,0x20,0x72,0x31,0x3D,0x25,0x30,0x38,0x58,0x20,0x20, +0x72,0x32,0x3D,0x25,0x30,0x38,0x58,0x20,0x20,0x72,0x33,0x3D,0x25,0x30,0x38,0x58, +0x20,0x20,0x72,0x34,0x3D,0x25,0x30,0x38,0x58,0x0D,0x0A,0x72,0x35,0x3D,0x25,0x30, +0x38,0x58,0x20,0x20,0x72,0x36,0x3D,0x25,0x30,0x38,0x58,0x20,0x20,0x72,0x37,0x3D, +0x25,0x30,0x38,0x58,0x20,0x20,0x72,0x38,0x3D,0x25,0x30,0x38,0x58,0x20,0x45,0x52, +0x46,0x3D,0x25,0x30,0x38,0x58,0x0D,0x0A,0x00,0x03,0x00,0x00,0x00,0xAD,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x0E,0x01, +0x14,0x98,0x04,0x20,0x5B,0x58,0x51,0x41,0x30,0x5D,0x3A,0x20,0x28,0x42,0x4F,0x4F, +0x54,0x2F,0x52,0x35,0x3A,0x25,0x58,0x20,0x25,0x6D,0x29,0x00,0x25,0x30,0x33,0x32, +0x2E,0x2A,0x62,0x0D,0x0A,0x00,0x25,0x30,0x38,0x58,0x0D,0x0A,0x00,0x25,0x73,0x0D, +0x0A,0x00,0x45,0x6E,0x67,0x6C,0x69,0x73,0x68,0x20,0x28,0x55,0x6E,0x69,0x74,0x65, +0x64,0x20,0x53,0x74,0x61,0x74,0x65,0x73,0x2F,0x43,0x61,0x6E,0x61,0x64,0x61,0x29, +0x00,0x46,0x72,0x61,0x6E,0xE7,0x61,0x69,0x73,0x20,0x28,0x43,0x61,0x6E,0x61,0x64, +0x61,0x29,0x00,0x44,0x61,0x6E,0x73,0x6B,0x00,0x45,0x6E,0x67,0x6C,0x69,0x73,0x68, +0x20,0x28,0x55,0x6E,0x69,0x74,0x65,0x64,0x20,0x4B,0x69,0x6E,0x67,0x64,0x6F,0x6D, +0x29,0x00,0x53,0x75,0x6F,0x6D,0x69,0x00,0x44,0x65,0x75,0x74,0x73,0x63,0x68,0x20, +0x28,0x44,0x65,0x75,0x74,0x73,0x63,0x68,0x6C,0x61,0x6E,0x64,0x2F,0xD6,0x73,0x74, +0x65,0x72,0x72,0x65,0x69,0x63,0x68,0x29,0x00,0x4E,0x65,0x64,0x65,0x72,0x6C,0x61, +0x6E,0x64,0x73,0x00,0x49,0x74,0x61,0x6C,0x69,0x61,0x6E,0x6F,0x00,0x46,0x72,0x61, +0x6E,0xE7,0x61,0x69,0x73,0x20,0x28,0x53,0x75,0x69,0x73,0x73,0x65,0x29,0x00,0x44, +0x65,0x75,0x74,0x73,0x63,0x68,0x20,0x28,0x53,0x63,0x68,0x77,0x65,0x69,0x7A,0x29, +0x00,0x53,0x76,0x65,0x6E,0x73,0x6B,0x61,0x00,0x4E,0x6F,0x72,0x73,0x6B,0x00,0x46, +0x72,0x61,0x6E,0xE7,0x61,0x69,0x73,0x20,0x28,0x46,0x72,0x61,0x6E,0x63,0x65,0x2F, +0x42,0x65,0x6C,0x67,0x69,0x71,0x75,0x65,0x29,0x00,0x45,0x73,0x70,0x61,0xF1,0x6F, +0x6C,0x00,0x50,0x6F,0x72,0x74,0x75,0x67,0x75,0xEA,0x73,0x00,0x55,0x6E,0x6B,0x6E, +0x6F,0x77,0x6E,0x00,0xF2,0x08,0xD3,0xC1,0xBB,0xA2,0x9C,0x7B,0x70,0x67,0x55,0x43, +0x3B,0x35,0x1A,0x12,0x08,0x25,0x73,0x0D,0x0A,0x00,0x00,0x00,0x14,0x85,0xFF,0xFF, +0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6C, +0xC0,0xFF,0xFF,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x08,0x08,0x08,0x08,0x08,0x08, +0x08,0x08,0xC1,0x85,0xFF,0xFF,0xFA,0xFB,0x02,0x00,0x03,0x09,0x00,0x01,0x02,0x02, +0x02,0x02,0x02,0x02,0x02,0x58,0x85,0xFF,0xFF,0xFA,0xFF,0x02,0x00,0x01,0x02,0x00, +0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC1,0x86,0xFF,0xFF,0xEA,0xFB,0x02,0x00, +0x03,0x03,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x2C,0xBA,0xFF,0xFF,0x00, +0x00,0xC0,0x0F,0x02,0x09,0x00,0x09,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x0C,0xBC, +0xFF,0xFF,0x00,0x00,0x10,0x00,0x02,0x02,0x00,0x0A,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x39,0x87,0xFF,0xFF,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0xB6,0x88,0xFF,0xFF,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xD8,0xB6,0xFF,0xFF,0x05,0x00,0x00,0x00,0x01, +0x02,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xCF,0x88,0xFF,0xFF,0x00,0x00, +0x00,0x00,0x02,0x02,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x34,0x7E,0xFF, +0xFF,0x02,0x00,0x00,0x00,0x01,0x02,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0xEA,0x88,0xFF,0xFF,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0xF5,0x88,0xFF,0xFF,0xEA,0xFB,0x0A,0x00,0x03,0x04,0x00,0x01,0x02, +0x02,0x00,0x00,0x00,0x00,0x00,0x94,0x89,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x02,0x09, +0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x23,0x8A,0xFF,0xFF,0x00,0x00,0x00, +0x00,0x03,0x03,0x00,0x05,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x9C,0x80,0xFF,0xFF, +0x00,0x00,0x05,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE2, +0x89,0xFF,0xFF,0x00,0x00,0x00,0x00,0x02,0x09,0x00,0x03,0x05,0x05,0x05,0x05,0x05, +0x05,0x05,0x34,0x8B,0xFF,0xFF,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x08,0xCE,0xB8,0xFF,0xFF,0x68,0x00,0x00,0x00,0x02,0x62, +0x66,0x08,0xCE,0xB8,0xFF,0xFF,0x68,0x00,0x00,0x00,0x03,0x62,0x66,0x6C,0x08,0xCE, +0xB8,0xFF,0xFF,0x68,0x00,0x00,0x00,0x04,0x62,0x66,0x6C,0x67,0x08,0xCE,0xB8,0xFF, +0xFF,0x68,0x00,0x00,0x00,0x05,0x62,0x66,0x6C,0x61,0x67,0x08,0xE6,0xB8,0xFF,0xFF, +0x71,0x00,0x00,0x00,0x04,0x62,0x6F,0x6F,0x74,0x08,0x3C,0xB9,0xFF,0xFF,0x7A,0x00, +0x00,0x00,0x04,0x68,0x6F,0x73,0x74,0x08,0x46,0xBA,0xFF,0xFF,0x83,0x00,0x00,0x00, +0x08,0x6C,0x61,0x6E,0x67,0x75,0x61,0x67,0x65,0x00,0x00,0x00,0x00,0x00,0x09,0x05, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x09,0x05,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x0B,0x00,0x00,0x00,0x00,0x00, +0x00,0x04,0x22,0xBA,0xFF,0xFF,0x07,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x04,0x3D, +0xBA,0xFF,0xFF,0x02,0x62,0x66,0x04,0x3D,0xBA,0xFF,0xFF,0x03,0x62,0x66,0x6C,0x04, +0x3D,0xBA,0xFF,0xFF,0x04,0x62,0x66,0x6C,0x67,0x04,0x3D,0xBA,0xFF,0xFF,0x05,0x62, +0x66,0x6C,0x61,0x67,0x04,0x54,0xBA,0xFF,0xFF,0x04,0x62,0x6F,0x6F,0x74,0x04,0x6F, +0xBA,0xFF,0xFF,0x07,0x64,0x65,0x76,0x69,0x63,0x65,0x73,0x04,0x93,0xBA,0xFF,0xFF, +0x08,0x65,0x74,0x68,0x65,0x72,0x6E,0x65,0x74,0x04,0x9D,0xBA,0xFF,0xFF,0x08,0x6C, +0x61,0x6E,0x67,0x75,0x61,0x67,0x65,0x04,0xCC,0xBA,0xFF,0xFF,0x06,0x6D,0x65,0x6D, +0x6F,0x72,0x79,0x04,0xE8,0xBA,0xFF,0xFF,0x04,0x71,0x62,0x75,0x73,0x04,0xF2,0xBA, +0xFF,0xFF,0x05,0x72,0x6C,0x76,0x31,0x32,0x04,0xFC,0xBA,0xFF,0xFF,0x05,0x75,0x71, +0x73,0x73,0x70,0x04,0x06,0xBB,0xFF,0xFF,0x07,0x76,0x65,0x72,0x73,0x69,0x6F,0x6E, +0x00,0x00,0x00,0x00,0x04,0x68,0xFD,0xFF,0xFF,0x04,0x68,0x61,0x6C,0x74,0x04,0x68, +0xFD,0xFF,0xFF,0x01,0x68,0x04,0x7B,0xFD,0xFF,0xFF,0x04,0x68,0x65,0x6C,0x70,0x04, +0x8E,0xFD,0xFF,0xFF,0x07,0x64,0x65,0x70,0x6F,0x73,0x69,0x74,0x04,0xA1,0xFD,0xFF, +0xFF,0x07,0x65,0x78,0x61,0x6D,0x69,0x6E,0x65,0x04,0xB4,0xFD,0xFF,0xFF,0x04,0x6D, +0x6F,0x76,0x65,0x04,0xC7,0xFD,0xFF,0xFF,0x03,0x73,0x65,0x74,0x04,0xDA,0xFD,0xFF, +0xFF,0x04,0x73,0x68,0x6F,0x77,0x04,0xED,0xFD,0xFF,0xFF,0x04,0x69,0x6E,0x69,0x74, +0x04,0x00,0xFE,0xFF,0xFF,0x05,0x75,0x6E,0x6A,0x61,0x6D,0x04,0x13,0xFE,0xFF,0xFF, +0x04,0x62,0x6F,0x6F,0x74,0x04,0x26,0xFE,0xFF,0xFF,0x05,0x73,0x74,0x61,0x72,0x74, +0x04,0x26,0xFE,0xFF,0xFF,0x01,0x73,0x04,0x39,0xFE,0xFF,0xFF,0x04,0x6E,0x65,0x78, +0x74,0x04,0x4C,0xFE,0xFF,0xFF,0x08,0x63,0x6F,0x6E,0x74,0x69,0x6E,0x75,0x65,0x04, +0x4C,0xFE,0xFF,0xFF,0x01,0x63,0x04,0x5F,0xFE,0xFF,0xFF,0x06,0x73,0x65,0x61,0x72, +0x63,0x68,0x04,0x72,0xFE,0xFF,0xFF,0x06,0x72,0x65,0x70,0x65,0x61,0x74,0x04,0x85, +0xFE,0xFF,0xFF,0x01,0x78,0x04,0x98,0xFE,0xFF,0xFF,0x04,0x66,0x69,0x6E,0x64,0x04, +0xAB,0xFE,0xFF,0xFF,0x04,0x74,0x65,0x73,0x74,0x04,0xBE,0xFE,0xFF,0xFF,0x09,0x63, +0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x65,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x80, +0x08,0x00,0x00,0x02,0x72,0x30,0x08,0x01,0x00,0x00,0x00,0x80,0x08,0x00,0x00,0x02, +0x72,0x31,0x08,0x02,0x00,0x00,0x00,0x80,0x08,0x00,0x00,0x02,0x72,0x32,0x08,0x03, +0x00,0x00,0x00,0x80,0x08,0x00,0x00,0x02,0x72,0x33,0x08,0x04,0x00,0x00,0x00,0x80, +0x08,0x00,0x00,0x02,0x72,0x34,0x08,0x05,0x00,0x00,0x00,0x80,0x08,0x00,0x00,0x02, +0x72,0x35,0x08,0x06,0x00,0x00,0x00,0x80,0x08,0x00,0x00,0x02,0x72,0x36,0x08,0x07, +0x00,0x00,0x00,0x80,0x08,0x00,0x00,0x02,0x72,0x37,0x08,0x08,0x00,0x00,0x00,0x80, +0x08,0x00,0x00,0x02,0x72,0x38,0x08,0x09,0x00,0x00,0x00,0x80,0x08,0x00,0x00,0x02, +0x72,0x39,0x08,0x0A,0x00,0x00,0x00,0x80,0x08,0x00,0x00,0x03,0x72,0x31,0x30,0x08, +0x0B,0x00,0x00,0x00,0x80,0x08,0x00,0x00,0x03,0x72,0x31,0x31,0x08,0x0C,0x00,0x00, +0x00,0x80,0x08,0x00,0x00,0x03,0x72,0x31,0x32,0x08,0x0D,0x00,0x00,0x00,0x80,0x08, +0x00,0x00,0x03,0x72,0x31,0x33,0x08,0x0E,0x00,0x00,0x00,0x80,0x08,0x00,0x00,0x03, +0x72,0x31,0x34,0x08,0x0F,0x00,0x00,0x00,0x80,0x08,0x00,0x00,0x03,0x72,0x31,0x35, +0x08,0x0C,0x00,0x00,0x00,0x80,0x08,0x00,0x00,0x02,0x61,0x70,0x08,0x0D,0x00,0x00, +0x00,0x80,0x08,0x00,0x00,0x02,0x66,0x70,0x08,0x0E,0x00,0x00,0x00,0x80,0x08,0x00, +0x00,0x02,0x73,0x70,0x08,0x0F,0x00,0x00,0x00,0x80,0x08,0x00,0x00,0x02,0x70,0x63, +0x08,0x00,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x07,0x70,0x72,0x24,0x5F,0x6B,0x73, +0x70,0x08,0x01,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x07,0x70,0x72,0x24,0x5F,0x65, +0x73,0x70,0x08,0x02,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x07,0x70,0x72,0x24,0x5F, +0x73,0x73,0x70,0x08,0x03,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x07,0x70,0x72,0x24, +0x5F,0x75,0x73,0x70,0x08,0x04,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x07,0x70,0x72, +0x24,0x5F,0x69,0x73,0x70,0x08,0x08,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x08,0x70, +0x72,0x24,0x5F,0x70,0x30,0x62,0x72,0x08,0x09,0x00,0x00,0x00,0x80,0x10,0x00,0x00, +0x08,0x70,0x72,0x24,0x5F,0x70,0x30,0x6C,0x72,0x08,0x0A,0x00,0x00,0x00,0x80,0x10, +0x00,0x00,0x08,0x70,0x72,0x24,0x5F,0x70,0x31,0x62,0x72,0x08,0x0B,0x00,0x00,0x00, +0x80,0x10,0x00,0x00,0x08,0x70,0x72,0x24,0x5F,0x70,0x31,0x6C,0x72,0x08,0x0C,0x00, +0x00,0x00,0x80,0x10,0x00,0x00,0x07,0x70,0x72,0x24,0x5F,0x73,0x62,0x72,0x08,0x0D, +0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x07,0x70,0x72,0x24,0x5F,0x73,0x6C,0x72,0x08, +0x10,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x08,0x70,0x72,0x24,0x5F,0x70,0x63,0x62, +0x62,0x08,0x11,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x08,0x70,0x72,0x24,0x5F,0x73, +0x63,0x62,0x62,0x08,0x12,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x07,0x70,0x72,0x24, +0x5F,0x69,0x70,0x6C,0x08,0x13,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x09,0x70,0x72, +0x24,0x5F,0x61,0x73,0x74,0x6C,0x76,0x08,0x14,0x00,0x00,0x00,0x80,0x10,0x00,0x00, +0x08,0x70,0x72,0x24,0x5F,0x73,0x69,0x72,0x72,0x08,0x15,0x00,0x00,0x00,0x80,0x10, +0x00,0x00,0x08,0x70,0x72,0x24,0x5F,0x73,0x69,0x73,0x72,0x08,0x18,0x00,0x00,0x00, +0x80,0x10,0x00,0x00,0x08,0x70,0x72,0x24,0x5F,0x69,0x63,0x63,0x72,0x08,0x19,0x00, +0x00,0x00,0x80,0x10,0x00,0x00,0x08,0x70,0x72,0x24,0x5F,0x6E,0x69,0x63,0x72,0x08, +0x1A,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x07,0x70,0x72,0x24,0x5F,0x69,0x63,0x72, +0x08,0x1B,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x08,0x70,0x72,0x24,0x5F,0x74,0x6F, +0x64,0x72,0x08,0x20,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x08,0x70,0x72,0x24,0x5F, +0x72,0x78,0x63,0x73,0x08,0x21,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x08,0x70,0x72, +0x24,0x5F,0x72,0x78,0x64,0x62,0x08,0x22,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x08, +0x70,0x72,0x24,0x5F,0x74,0x78,0x63,0x73,0x08,0x23,0x00,0x00,0x00,0x80,0x10,0x00, +0x00,0x08,0x70,0x72,0x24,0x5F,0x74,0x78,0x64,0x62,0x08,0x24,0x00,0x00,0x00,0x80, +0x10,0x00,0x00,0x08,0x70,0x72,0x24,0x5F,0x74,0x62,0x64,0x72,0x08,0x25,0x00,0x00, +0x00,0x80,0x10,0x00,0x00,0x08,0x70,0x72,0x24,0x5F,0x63,0x61,0x64,0x72,0x08,0x26, +0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x09,0x70,0x72,0x24,0x5F,0x6D,0x63,0x65,0x73, +0x72,0x08,0x27,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x08,0x70,0x72,0x24,0x5F,0x6D, +0x73,0x65,0x72,0x08,0x2A,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x09,0x70,0x72,0x24, +0x5F,0x73,0x61,0x76,0x70,0x63,0x08,0x2B,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x0A, +0x70,0x72,0x24,0x5F,0x73,0x61,0x76,0x70,0x73,0x6C,0x08,0x37,0x00,0x00,0x00,0x80, +0x10,0x00,0x00,0x0B,0x70,0x72,0x24,0x5F,0x69,0x6F,0x72,0x65,0x73,0x65,0x74,0x08, +0x38,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x09,0x70,0x72,0x24,0x5F,0x6D,0x61,0x70, +0x65,0x6E,0x08,0x39,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x08,0x70,0x72,0x24,0x5F, +0x74,0x62,0x69,0x61,0x08,0x3A,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x08,0x70,0x72, +0x24,0x5F,0x74,0x62,0x69,0x73,0x08,0x3E,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0x07, +0x70,0x72,0x24,0x5F,0x73,0x69,0x64,0x08,0x3F,0x00,0x00,0x00,0x80,0x10,0x00,0x00, +0x09,0x70,0x72,0x24,0x5F,0x74,0x62,0x63,0x68,0x6B,0x08,0x00,0x00,0x04,0x20,0x00, +0x40,0x00,0x00,0x03,0x72,0x6F,0x6D,0x08,0x04,0x40,0x08,0x20,0x20,0x40,0x00,0x00, +0x03,0x62,0x64,0x72,0x08,0x00,0x40,0x08,0x20,0x80,0x40,0x00,0x00,0x04,0x63,0x61, +0x63,0x72,0x08,0x00,0x04,0x14,0x20,0x00,0x40,0x00,0x00,0x07,0x73,0x73,0x63,0x5F, +0x72,0x61,0x6D,0x08,0x10,0x00,0x14,0x20,0x80,0x40,0x00,0x00,0x06,0x73,0x73,0x63, +0x5F,0x63,0x72,0x08,0x20,0x00,0x14,0x20,0x80,0x40,0x00,0x00,0x08,0x73,0x73,0x63, +0x5F,0x63,0x64,0x61,0x6C,0x08,0x30,0x00,0x14,0x20,0x80,0x40,0x00,0x00,0x09,0x73, +0x73,0x63,0x5F,0x64,0x6C,0x65,0x64,0x72,0x08,0x30,0x01,0x14,0x20,0x80,0x40,0x00, +0x00,0x0A,0x73,0x73,0x63,0x5F,0x61,0x64,0x30,0x6D,0x61,0x74,0x08,0x34,0x01,0x14, +0x20,0x80,0x40,0x00,0x00,0x0A,0x73,0x73,0x63,0x5F,0x61,0x64,0x30,0x6D,0x73,0x6B, +0x08,0x40,0x01,0x14,0x20,0x80,0x40,0x00,0x00,0x0A,0x73,0x73,0x63,0x5F,0x61,0x64, +0x31,0x6D,0x61,0x74,0x08,0x44,0x01,0x14,0x20,0x80,0x40,0x00,0x00,0x0A,0x73,0x73, +0x63,0x5F,0x61,0x64,0x31,0x6D,0x73,0x6B,0x08,0x00,0x01,0x14,0x20,0x80,0x40,0x00, +0x00,0x08,0x73,0x73,0x63,0x5F,0x74,0x63,0x72,0x30,0x08,0x04,0x01,0x14,0x20,0x80, +0x40,0x00,0x00,0x08,0x73,0x73,0x63,0x5F,0x74,0x69,0x72,0x30,0x08,0x08,0x01,0x14, +0x20,0x80,0x40,0x00,0x00,0x09,0x73,0x73,0x63,0x5F,0x74,0x6E,0x69,0x72,0x30,0x08, +0x0C,0x01,0x14,0x20,0x80,0x40,0x00,0x00,0x09,0x73,0x73,0x63,0x5F,0x74,0x69,0x76, +0x72,0x30,0x08,0x10,0x01,0x14,0x20,0x80,0x40,0x00,0x00,0x08,0x73,0x73,0x63,0x5F, +0x74,0x63,0x72,0x31,0x08,0x14,0x01,0x14,0x20,0x80,0x40,0x00,0x00,0x08,0x73,0x73, +0x63,0x5F,0x74,0x69,0x72,0x31,0x08,0x18,0x01,0x14,0x20,0x80,0x40,0x00,0x00,0x09, +0x73,0x73,0x63,0x5F,0x74,0x6E,0x69,0x72,0x31,0x08,0x1C,0x01,0x14,0x20,0x80,0x40, +0x00,0x00,0x09,0x73,0x73,0x63,0x5F,0x74,0x69,0x76,0x72,0x31,0x08,0x00,0x01,0x08, +0x20,0x80,0x40,0x00,0x00,0x07,0x6D,0x65,0x6D,0x63,0x73,0x72,0x30,0x08,0x04,0x01, +0x08,0x20,0x80,0x40,0x00,0x00,0x07,0x6D,0x65,0x6D,0x63,0x73,0x72,0x31,0x08,0x08, +0x01,0x08,0x20,0x80,0x40,0x00,0x00,0x07,0x6D,0x65,0x6D,0x63,0x73,0x72,0x32,0x08, +0x0C,0x01,0x08,0x20,0x80,0x40,0x00,0x00,0x07,0x6D,0x65,0x6D,0x63,0x73,0x72,0x33, +0x08,0x10,0x01,0x08,0x20,0x80,0x40,0x00,0x00,0x07,0x6D,0x65,0x6D,0x63,0x73,0x72, +0x34,0x08,0x14,0x01,0x08,0x20,0x80,0x40,0x00,0x00,0x07,0x6D,0x65,0x6D,0x63,0x73, +0x72,0x35,0x08,0x18,0x01,0x08,0x20,0x80,0x40,0x00,0x00,0x07,0x6D,0x65,0x6D,0x63, +0x73,0x72,0x36,0x08,0x1C,0x01,0x08,0x20,0x80,0x40,0x00,0x00,0x07,0x6D,0x65,0x6D, +0x63,0x73,0x72,0x37,0x08,0x20,0x01,0x08,0x20,0x80,0x40,0x00,0x00,0x07,0x6D,0x65, +0x6D,0x63,0x73,0x72,0x38,0x08,0x24,0x01,0x08,0x20,0x80,0x40,0x00,0x00,0x07,0x6D, +0x65,0x6D,0x63,0x73,0x72,0x39,0x08,0x28,0x01,0x08,0x20,0x80,0x40,0x00,0x00,0x08, +0x6D,0x65,0x6D,0x63,0x73,0x72,0x31,0x30,0x08,0x2C,0x01,0x08,0x20,0x80,0x40,0x00, +0x00,0x08,0x6D,0x65,0x6D,0x63,0x73,0x72,0x31,0x31,0x08,0x30,0x01,0x08,0x20,0x80, +0x40,0x00,0x00,0x08,0x6D,0x65,0x6D,0x63,0x73,0x72,0x31,0x32,0x08,0x34,0x01,0x08, +0x20,0x80,0x40,0x00,0x00,0x08,0x6D,0x65,0x6D,0x63,0x73,0x72,0x31,0x33,0x08,0x38, +0x01,0x08,0x20,0x80,0x40,0x00,0x00,0x08,0x6D,0x65,0x6D,0x63,0x73,0x72,0x31,0x34, +0x08,0x3C,0x01,0x08,0x20,0x80,0x40,0x00,0x00,0x08,0x6D,0x65,0x6D,0x63,0x73,0x72, +0x31,0x35,0x08,0x40,0x01,0x08,0x20,0x80,0x40,0x00,0x00,0x08,0x6D,0x65,0x6D,0x63, +0x73,0x72,0x31,0x36,0x08,0x44,0x01,0x08,0x20,0x80,0x40,0x00,0x00,0x08,0x6D,0x65, +0x6D,0x63,0x73,0x72,0x31,0x37,0x08,0x40,0x1F,0x00,0x20,0x40,0x40,0x00,0x00,0x05, +0x69,0x70,0x63,0x72,0x30,0x08,0x42,0x1F,0x00,0x20,0x40,0x40,0x00,0x00,0x05,0x69, +0x70,0x63,0x72,0x31,0x08,0x44,0x1F,0x00,0x20,0x40,0x40,0x00,0x00,0x05,0x69,0x70, +0x63,0x72,0x32,0x08,0x46,0x1F,0x00,0x20,0x40,0x40,0x00,0x00,0x05,0x69,0x70,0x63, +0x72,0x33,0x08,0x00,0x00,0x08,0x20,0x80,0x40,0x00,0x00,0x04,0x64,0x73,0x63,0x72, +0x08,0x04,0x00,0x08,0x20,0x80,0x40,0x00,0x00,0x04,0x64,0x73,0x65,0x72,0x08,0x08, +0x00,0x08,0x20,0x80,0x40,0x00,0x00,0x05,0x64,0x6D,0x65,0x61,0x72,0x08,0x0C,0x00, +0x08,0x20,0x80,0x40,0x00,0x00,0x05,0x64,0x73,0x65,0x61,0x72,0x08,0x10,0x00,0x08, +0x20,0x80,0x40,0x00,0x00,0x05,0x71,0x62,0x6D,0x62,0x72,0x08,0x00,0x00,0x00,0x30, +0x80,0x40,0x00,0x00,0x05,0x71,0x62,0x6D,0x65,0x6D,0x08,0x00,0x00,0x00,0x20,0x80, +0x40,0x00,0x00,0x04,0x71,0x62,0x69,0x6F,0x08,0x00,0x00,0x00,0x00,0x80,0x80,0x00, +0x00,0x03,0x70,0x73,0x6C,0x00,0x00,0x00,0x1A,0x00,0x0F,0x00,0x01,0x04,0x00,0x05, +0x00,0x02,0x18,0x00,0x0F,0x00,0x04,0x68,0xBB,0x0F,0x00,0x03,0x00,0x98,0x05,0x00, +0x05,0xC0,0x9B,0x00,0x00,0x06,0x80,0x9B,0x00,0x00,0x07,0x00,0x03,0x00,0x00,0x08, +0x00,0x9A,0x00,0x00,0x09,0x00,0x98,0x00,0x00,0x0B,0x00,0xF0,0x02,0x00,0x0C,0x00, +0xE0,0x00,0x00,0x0D,0x00,0xC0,0x00,0x00,0x0E,0x00,0x80,0x00,0x00,0x0F,0x00,0x00, +0x0A,0x00,0x10,0x00,0x00,0x0E,0x00,0x11,0x00,0x00,0x08,0x00,0x12,0x00,0x00,0x08, +0x00,0x13,0x00,0x00,0x00,0x00,0x0A,0x00,0x98,0x00,0x00,0x14,0x00,0x00,0x00,0x00, +0x16,0x00,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x0B,0x18,0x00,0x00,0x80,0x0A,0x19, +0x00,0x00,0x80,0x01,0x1A,0x00,0x00,0x00,0x0A,0x1B,0x00,0x00,0x80,0x05,0x1D,0x01, +0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x01,0x2B,0x04,0x01,0x00,0x00,0x00,0x01, +0x2D,0x04,0x02,0x00,0x00,0x00,0x01,0x2A,0x04,0x03,0x00,0x00,0x00,0x01,0x40,0x00, +0x00,0x00,0x00,0x00,0x46,0x6F,0x6C,0x6C,0x6F,0x77,0x69,0x6E,0x67,0x20,0x69,0x73, +0x20,0x61,0x20,0x62,0x72,0x69,0x65,0x66,0x20,0x73,0x75,0x6D,0x6D,0x61,0x72,0x79, +0x20,0x6F,0x66,0x20,0x61,0x6C,0x6C,0x20,0x74,0x68,0x65,0x20,0x63,0x6F,0x6D,0x6D, +0x61,0x6E,0x64,0x73,0x20,0x73,0x75,0x70,0x70,0x6F,0x72,0x74,0x65,0x64,0x20,0x62, +0x79,0x20,0x74,0x68,0x65,0x20,0x63,0x6F,0x6E,0x73,0x6F,0x6C,0x65,0x3A,0x0D,0x0A, +0x0D,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x55,0x50,0x50,0x45,0x52,0x43, +0x41,0x53,0x45,0x20,0x20,0x64,0x65,0x6E,0x6F,0x74,0x65,0x73,0x20,0x61,0x20,0x6B, +0x65,0x79,0x77,0x6F,0x72,0x64,0x20,0x74,0x68,0x61,0x74,0x20,0x79,0x6F,0x75,0x20, +0x6D,0x75,0x73,0x74,0x20,0x74,0x79,0x70,0x65,0x20,0x69,0x6E,0x0D,0x0A,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x7C,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x64,0x65,0x6E,0x6F,0x74,0x65,0x73,0x20,0x61,0x6E,0x20,0x4F,0x52,0x20,0x63, +0x6F,0x6E,0x64,0x69,0x74,0x69,0x6F,0x6E,0x0D,0x0A,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x5B,0x5D,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x65,0x6E, +0x6F,0x74,0x65,0x73,0x20,0x6F,0x70,0x74,0x69,0x6F,0x6E,0x61,0x6C,0x20,0x70,0x61, +0x72,0x61,0x6D,0x65,0x74,0x65,0x72,0x73,0x0D,0x0A,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x3C,0x3E,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x65,0x6E, +0x6F,0x74,0x65,0x73,0x20,0x61,0x20,0x66,0x69,0x65,0x6C,0x64,0x20,0x74,0x68,0x61, +0x74,0x20,0x6D,0x75,0x73,0x74,0x20,0x62,0x65,0x20,0x66,0x69,0x6C,0x6C,0x65,0x64, +0x20,0x69,0x6E,0x0D,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x77,0x69,0x74,0x68,0x20,0x61,0x20,0x73, +0x79,0x6E,0x74,0x61,0x63,0x74,0x69,0x63,0x61,0x6C,0x6C,0x79,0x20,0x63,0x6F,0x72, +0x72,0x65,0x63,0x74,0x20,0x76,0x61,0x6C,0x75,0x65,0x0D,0x0A,0x0D,0x0A,0x56,0x61, +0x6C,0x69,0x64,0x20,0x71,0x75,0x61,0x6C,0x69,0x66,0x69,0x65,0x72,0x73,0x3A,0x0D, +0x0A,0x20,0x20,0x20,0x20,0x2F,0x42,0x20,0x2F,0x57,0x20,0x2F,0x4C,0x20,0x2F,0x51, +0x20,0x2F,0x49,0x4E,0x53,0x54,0x52,0x55,0x43,0x54,0x49,0x4F,0x4E,0x0D,0x0A,0x20, +0x20,0x20,0x20,0x2F,0x47,0x20,0x2F,0x49,0x20,0x2F,0x56,0x20,0x2F,0x50,0x20,0x2F, +0x4D,0x0D,0x0A,0x20,0x20,0x20,0x20,0x2F,0x53,0x54,0x45,0x50,0x3A,0x20,0x2F,0x4E, +0x3A,0x20,0x2F,0x4E,0x4F,0x54,0x0D,0x0A,0x20,0x20,0x20,0x20,0x2F,0x57,0x52,0x4F, +0x4E,0x47,0x20,0x2F,0x55,0x0D,0x0A,0x0D,0x0A,0x56,0x61,0x6C,0x69,0x64,0x20,0x63, +0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x73,0x3A,0x0D,0x0A,0x20,0x20,0x20,0x20,0x44,0x45, +0x50,0x4F,0x53,0x49,0x54,0x20,0x5B,0x3C,0x71,0x75,0x61,0x6C,0x69,0x66,0x69,0x65, +0x72,0x73,0x3E,0x5D,0x20,0x3C,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x3E,0x20,0x5B, +0x3C,0x64,0x61,0x74,0x75,0x6D,0x3E,0x20,0x5B,0x3C,0x64,0x61,0x74,0x75,0x6D,0x3E, +0x5D,0x5D,0x0D,0x0A,0x20,0x20,0x20,0x20,0x45,0x58,0x41,0x4D,0x49,0x4E,0x45,0x20, +0x5B,0x3C,0x71,0x75,0x61,0x6C,0x69,0x66,0x69,0x65,0x72,0x73,0x3E,0x5D,0x20,0x5B, +0x3C,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x3E,0x5D,0x0D,0x0A,0x20,0x20,0x20,0x20, +0x4D,0x4F,0x56,0x45,0x20,0x5B,0x3C,0x71,0x75,0x61,0x6C,0x69,0x66,0x69,0x65,0x72, +0x73,0x3E,0x5D,0x20,0x3C,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x3E,0x20,0x3C,0x61, +0x64,0x64,0x72,0x65,0x73,0x73,0x3E,0x0D,0x0A,0x20,0x20,0x20,0x20,0x53,0x45,0x41, +0x52,0x43,0x48,0x20,0x5B,0x3C,0x71,0x75,0x61,0x6C,0x69,0x66,0x69,0x65,0x72,0x73, +0x3E,0x5D,0x20,0x3C,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x3E,0x20,0x3C,0x70,0x61, +0x74,0x74,0x65,0x72,0x6E,0x3E,0x20,0x5B,0x3C,0x6D,0x61,0x73,0x6B,0x3E,0x5D,0x0D, +0x0A,0x20,0x20,0x20,0x20,0x53,0x45,0x54,0x20,0x42,0x46,0x4C,0x47,0x20,0x3C,0x62, +0x6F,0x6F,0x74,0x5F,0x66,0x6C,0x61,0x67,0x73,0x3E,0x0D,0x0A,0x20,0x20,0x20,0x20, +0x53,0x45,0x54,0x20,0x42,0x4F,0x4F,0x54,0x20,0x3C,0x62,0x6F,0x6F,0x74,0x5F,0x64, +0x65,0x76,0x69,0x63,0x65,0x3E,0x5B,0x3A,0x5D,0x0D,0x0A,0x20,0x20,0x20,0x20,0x53, +0x45,0x54,0x20,0x48,0x4F,0x53,0x54,0x2F,0x44,0x55,0x50,0x2F,0x55,0x51,0x53,0x53, +0x50,0x20,0x3C,0x2F,0x44,0x49,0x53,0x4B,0x20,0x7C,0x20,0x2F,0x54,0x41,0x50,0x45, +0x3E,0x20,0x3C,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x6C,0x65,0x72,0x5F,0x6E,0x75, +0x6D,0x62,0x65,0x72,0x3E,0x20,0x5B,0x3C,0x74,0x61,0x73,0x6B,0x3E,0x5D,0x0D,0x0A, +0x20,0x20,0x20,0x20,0x53,0x45,0x54,0x20,0x48,0x4F,0x53,0x54,0x2F,0x44,0x55,0x50, +0x2F,0x55,0x51,0x53,0x53,0x50,0x20,0x3C,0x70,0x68,0x79,0x73,0x69,0x63,0x61,0x6C, +0x5F,0x43,0x53,0x52,0x5F,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x3E,0x20,0x5B,0x3C, +0x74,0x61,0x73,0x6B,0x3E,0x5D,0x0D,0x0A,0x20,0x20,0x20,0x20,0x53,0x45,0x54,0x20, +0x48,0x4F,0x53,0x54,0x2F,0x4D,0x41,0x49,0x4E,0x54,0x45,0x4E,0x41,0x4E,0x43,0x45, +0x2F,0x55,0x51,0x53,0x53,0x50,0x2F,0x53,0x45,0x52,0x56,0x49,0x43,0x45,0x20,0x3C, +0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x6C,0x65,0x72,0x5F,0x6E,0x75,0x6D,0x62,0x65, +0x72,0x3E,0x0D,0x0A,0x20,0x20,0x20,0x20,0x53,0x45,0x54,0x20,0x48,0x4F,0x53,0x54, +0x2F,0x4D,0x41,0x49,0x4E,0x54,0x45,0x4E,0x41,0x4E,0x43,0x45,0x2F,0x55,0x51,0x53, +0x53,0x50,0x20,0x3C,0x70,0x68,0x79,0x73,0x69,0x63,0x61,0x6C,0x5F,0x43,0x53,0x52, +0x5F,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x3E,0x0D,0x0A,0x20,0x20,0x20,0x20,0x53, +0x45,0x54,0x20,0x4C,0x41,0x4E,0x47,0x55,0x41,0x47,0x45,0x20,0x3C,0x6C,0x61,0x6E, +0x67,0x75,0x61,0x67,0x65,0x5F,0x6E,0x75,0x6D,0x62,0x65,0x72,0x3E,0x0D,0x0A,0x20, +0x20,0x20,0x20,0x53,0x48,0x4F,0x57,0x20,0x42,0x46,0x4C,0x47,0x0D,0x0A,0x20,0x20, +0x20,0x20,0x53,0x48,0x4F,0x57,0x20,0x42,0x4F,0x4F,0x54,0x0D,0x0A,0x20,0x20,0x20, +0x20,0x53,0x48,0x4F,0x57,0x20,0x44,0x45,0x56,0x49,0x43,0x45,0x0D,0x0A,0x20,0x20, +0x20,0x20,0x53,0x48,0x4F,0x57,0x20,0x45,0x54,0x48,0x45,0x52,0x4E,0x45,0x54,0x0D, +0x0A,0x20,0x20,0x20,0x20,0x53,0x48,0x4F,0x57,0x20,0x4C,0x41,0x4E,0x47,0x55,0x41, +0x47,0x45,0x0D,0x0A,0x20,0x20,0x20,0x20,0x53,0x48,0x4F,0x57,0x20,0x4D,0x45,0x4D, +0x4F,0x52,0x59,0x20,0x5B,0x2F,0x46,0x55,0x4C,0x4C,0x5D,0x0D,0x0A,0x20,0x20,0x20, +0x20,0x53,0x48,0x4F,0x57,0x20,0x51,0x42,0x55,0x53,0x0D,0x0A,0x20,0x20,0x20,0x20, +0x53,0x48,0x4F,0x57,0x20,0x52,0x4C,0x56,0x31,0x32,0x0D,0x0A,0x20,0x20,0x20,0x20, +0x53,0x48,0x4F,0x57,0x20,0x55,0x51,0x53,0x53,0x50,0x0D,0x0A,0x20,0x20,0x20,0x20, +0x53,0x48,0x4F,0x57,0x20,0x56,0x45,0x52,0x53,0x49,0x4F,0x4E,0x0D,0x0A,0x20,0x20, +0x20,0x20,0x48,0x41,0x4C,0x54,0x0D,0x0A,0x20,0x20,0x20,0x20,0x49,0x4E,0x49,0x54, +0x49,0x41,0x4C,0x49,0x5A,0x45,0x0D,0x0A,0x20,0x20,0x20,0x20,0x55,0x4E,0x4A,0x41, +0x4D,0x0D,0x0A,0x20,0x20,0x20,0x20,0x43,0x4F,0x4E,0x54,0x49,0x4E,0x55,0x45,0x0D, +0x0A,0x20,0x20,0x20,0x20,0x53,0x54,0x41,0x52,0x54,0x20,0x3C,0x61,0x64,0x64,0x72, +0x65,0x73,0x73,0x3E,0x0D,0x0A,0x20,0x20,0x20,0x20,0x52,0x45,0x50,0x45,0x41,0x54, +0x20,0x3C,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x3E,0x0D,0x0A,0x20,0x20,0x20,0x20, +0x58,0x20,0x3C,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x3E,0x20,0x3C,0x63,0x6F,0x75, +0x6E,0x74,0x3E,0x0D,0x0A,0x20,0x20,0x20,0x20,0x46,0x49,0x4E,0x44,0x20,0x5B,0x2F, +0x4D,0x45,0x4D,0x4F,0x52,0x59,0x20,0x7C,0x20,0x2F,0x52,0x50,0x42,0x5D,0x0D,0x0A, +0x20,0x20,0x20,0x20,0x54,0x45,0x53,0x54,0x20,0x5B,0x3C,0x74,0x65,0x73,0x74,0x5F, +0x63,0x6F,0x64,0x65,0x3E,0x20,0x5B,0x3C,0x70,0x61,0x72,0x61,0x6D,0x65,0x74,0x65, +0x72,0x73,0x3E,0x5D,0x5D,0x0D,0x0A,0x20,0x20,0x20,0x20,0x42,0x4F,0x4F,0x54,0x20, +0x5B,0x2F,0x52,0x35,0x3A,0x3C,0x62,0x6F,0x6F,0x74,0x5F,0x66,0x6C,0x61,0x67,0x73, +0x3E,0x20,0x7C,0x20,0x2F,0x3C,0x62,0x6F,0x6F,0x74,0x5F,0x66,0x6C,0x61,0x67,0x73, +0x3E,0x5D,0x20,0x5B,0x3C,0x62,0x6F,0x6F,0x74,0x5F,0x64,0x65,0x76,0x69,0x63,0x65, +0x3E,0x5B,0x3A,0x5D,0x5D,0x0D,0x0A,0x20,0x20,0x20,0x20,0x4E,0x45,0x58,0x54,0x20, +0x5B,0x63,0x6F,0x75,0x6E,0x74,0x5D,0x0D,0x0A,0x20,0x20,0x20,0x20,0x43,0x4F,0x4E, +0x46,0x49,0x47,0x55,0x52,0x45,0x0D,0x0A,0x20,0x20,0x20,0x20,0x48,0x45,0x4C,0x50, +0x0D,0x0A,0x00,0x00,0x0D,0x0A,0x45,0x74,0x68,0x65,0x72,0x6E,0x65,0x74,0x20,0x41, +0x64,0x61,0x70,0x74,0x65,0x72,0x20,0x30,0x20,0x28,0x37,0x37,0x34,0x34,0x34,0x30, +0x29,0x0D,0x0A,0x00,0x2D,0x58,0x51,0x41,0x00,0x0D,0x0A,0x45,0x74,0x68,0x65,0x72, +0x6E,0x65,0x74,0x20,0x41,0x64,0x61,0x70,0x74,0x65,0x72,0x20,0x31,0x20,0x28,0x37, +0x37,0x34,0x34,0x36,0x30,0x29,0x0D,0x0A,0x00,0x2D,0x58,0x51,0x42,0x00,0x25,0x73, +0x30,0x20,0x28,0x25,0x30,0x32,0x58,0x2D,0x25,0x30,0x32,0x58,0x2D,0x25,0x30,0x32, +0x58,0x2D,0x25,0x30,0x32,0x58,0x2D,0x25,0x30,0x32,0x58,0x2D,0x25,0x30,0x32,0x58, +0x29,0x0D,0x0A,0x00,0x4D,0x65,0x6D,0x6F,0x72,0x79,0x20,0x25,0x64,0x3A,0x20,0x25, +0x30,0x38,0x58,0x20,0x74,0x6F,0x20,0x25,0x30,0x38,0x58,0x2C,0x20,0x25,0x64,0x4D, +0x42,0x2C,0x20,0x25,0x64,0x20,0x62,0x61,0x64,0x20,0x70,0x61,0x67,0x65,0x73,0x0D, +0x0A,0x00,0x20,0x2D,0x2D,0x20,0x46,0x44,0x4D,0x20,0x28,0x66,0x61,0x73,0x74,0x20, +0x64,0x69,0x61,0x67,0x6E,0x6F,0x73,0x74,0x69,0x63,0x20,0x6D,0x6F,0x64,0x65,0x29, +0x20,0x6E,0x6F,0x74,0x20,0x77,0x6F,0x72,0x6B,0x69,0x6E,0x67,0x0D,0x0A,0x00,0x0D, +0x0A,0x54,0x6F,0x74,0x61,0x6C,0x20,0x6F,0x66,0x20,0x25,0x64,0x4D,0x42,0x2C,0x20, +0x25,0x64,0x20,0x62,0x61,0x64,0x20,0x70,0x61,0x67,0x65,0x73,0x2C,0x20,0x25,0x64, +0x20,0x72,0x65,0x73,0x65,0x72,0x76,0x65,0x64,0x20,0x70,0x61,0x67,0x65,0x73,0x0D, +0x0A,0x00,0x2D,0x25,0x30,0x38,0x58,0x20,0x74,0x6F,0x20,0x25,0x30,0x38,0x58,0x2C, +0x20,0x25,0x64,0x20,0x70,0x61,0x67,0x65,0x73,0x0D,0x0A,0x00,0x0D,0x0A,0x4D,0x65, +0x6D,0x6F,0x72,0x79,0x20,0x42,0x69,0x74,0x6D,0x61,0x70,0x0D,0x0A,0x00,0x0D,0x0A, +0x43,0x6F,0x6E,0x73,0x6F,0x6C,0x65,0x20,0x53,0x63,0x72,0x61,0x74,0x63,0x68,0x20, +0x41,0x72,0x65,0x61,0x0D,0x0A,0x00,0x0D,0x0A,0x51,0x62,0x75,0x73,0x20,0x4D,0x61, +0x70,0x0D,0x0A,0x00,0x0D,0x0A,0x53,0x63,0x61,0x6E,0x20,0x6F,0x66,0x20,0x42,0x61, +0x64,0x20,0x50,0x61,0x67,0x65,0x73,0x0D,0x0A,0x00,0x0D,0x0A,0x53,0x63,0x61,0x6E, +0x20,0x6F,0x66,0x20,0x51,0x62,0x75,0x73,0x20,0x49,0x2F,0x4F,0x20,0x53,0x70,0x61, +0x63,0x65,0x0D,0x0A,0x00,0x0D,0x0A,0x53,0x63,0x61,0x6E,0x20,0x6F,0x66,0x20,0x51, +0x62,0x75,0x73,0x20,0x4D,0x65,0x6D,0x6F,0x72,0x79,0x20,0x53,0x70,0x61,0x63,0x65, +0x0D,0x0A,0x00,0x2D,0x25,0x30,0x38,0x58,0x20,0x28,0x25,0x30,0x36,0x6F,0x29,0x20, +0x3D,0x20,0x25,0x30,0x34,0x58,0x00,0x2D,0x25,0x30,0x38,0x58,0x20,0x74,0x6F,0x20, +0x25,0x30,0x38,0x58,0x20,0x28,0x25,0x30,0x38,0x6F,0x20,0x74,0x6F,0x20,0x25,0x30, +0x38,0x6F,0x29,0x0D,0x0A,0x00,0x20,0x28,0x25,0x30,0x33,0x6F,0x29,0x00,0x00,0x00, +0x04,0x01,0x00,0x00,0x10,0x01,0x6E,0x04,0x02,0x00,0x00,0x10,0x02,0x72,0x35,0x04, +0x03,0x00,0x00,0x10,0x04,0x73,0x74,0x65,0x70,0x04,0x04,0x00,0x00,0x00,0x05,0x77, +0x72,0x6F,0x6E,0x67,0x04,0x05,0x00,0x00,0x00,0x01,0x62,0x04,0x06,0x00,0x00,0x00, +0x01,0x77,0x04,0x07,0x00,0x00,0x00,0x01,0x6C,0x04,0x08,0x00,0x00,0x00,0x01,0x71, +0x04,0x0A,0x00,0x00,0x00,0x0B,0x69,0x6E,0x73,0x74,0x72,0x75,0x63,0x74,0x69,0x6F, +0x6E,0x04,0x0B,0x00,0x00,0x00,0x01,0x67,0x04,0x0C,0x00,0x00,0x00,0x01,0x69,0x04, +0x0D,0x00,0x00,0x00,0x01,0x76,0x04,0x0E,0x00,0x00,0x00,0x01,0x70,0x04,0x0F,0x00, +0x00,0x00,0x01,0x6D,0x04,0x10,0x00,0x00,0x00,0x03,0x72,0x70,0x62,0x04,0x11,0x00, +0x00,0x00,0x01,0x75,0x04,0x12,0x00,0x00,0x00,0x03,0x6D,0x65,0x6D,0x04,0x13,0x00, +0x00,0x00,0x03,0x6E,0x6F,0x74,0x04,0x14,0x00,0x00,0x00,0x04,0x66,0x75,0x6C,0x6C, +0x04,0x16,0x00,0x00,0x00,0x05,0x75,0x71,0x73,0x73,0x70,0x04,0x17,0x00,0x00,0x00, +0x04,0x64,0x69,0x73,0x6B,0x04,0x18,0x00,0x00,0x00,0x04,0x74,0x61,0x70,0x65,0x04, +0x19,0x00,0x00,0x00,0x07,0x73,0x65,0x72,0x76,0x69,0x63,0x65,0x04,0x1A,0x00,0x00, +0x00,0x03,0x64,0x75,0x70,0x04,0x1B,0x00,0x00,0x00,0x0B,0x6D,0x61,0x69,0x6E,0x74, +0x65,0x6E,0x61,0x6E,0x63,0x65,0x00,0x00,0x04,0x00,0x0E,0x01,0xD0,0xAC,0x04,0x20, +0x72,0x61,0x6D,0x3E,0x20,0x25,0x30,0x38,0x58,0x00,0x01,0x02,0x00,0x00,0x01,0x80, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x4C,0x05, +0x14,0x20,0x09,0x00,0x00,0x00,0xE5,0x05,0x14,0x20,0x9D,0x05,0x14,0x20,0x06,0x00, +0x00,0x00,0xE6,0x05,0x14,0x20,0xE7,0x05,0x14,0x20,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x01,0xB0,0x04,0x20,0x05,0xB0,0x04,0x20,0x09,0xB0,0x04,0x20,0x0D,0xB0,0x04,0x20, +0x11,0xB0,0x04,0x20,0x15,0xB0,0x04,0x20,0x19,0xB0,0x04,0x20,0x1D,0xB0,0x04,0x20, +0x21,0xB0,0x04,0x20,0x25,0xB0,0x04,0x20,0x29,0xB0,0x04,0x20,0x2D,0xB0,0x04,0x20, +0x31,0xB0,0x04,0x20,0x35,0xB0,0x04,0x20,0x39,0xB0,0x04,0x20,0x3D,0xB0,0x04,0x20, +0x40,0xB0,0x04,0x20,0x44,0xB0,0x04,0x20,0x48,0xB0,0x04,0x20,0x4C,0xB0,0x04,0x20, +0x51,0xB0,0x04,0x20,0x55,0xB0,0x04,0x20,0x59,0xB0,0x04,0x20,0x5D,0xB0,0x04,0x20, +0x61,0xB0,0x04,0x20,0x65,0xB0,0x04,0x20,0x69,0xB0,0x04,0x20,0x6D,0xB0,0x04,0x20, +0x71,0xB0,0x04,0x20,0x75,0xB0,0x04,0x20,0x79,0xB0,0x04,0x20,0x7D,0xB0,0x04,0x20, +0x85,0xB0,0x04,0x20,0x89,0xB0,0x04,0x20,0x8D,0xB0,0x04,0x20,0x91,0xB0,0x04,0x20, +0x95,0xB0,0x04,0x20,0x99,0xB0,0x04,0x20,0x9D,0xB0,0x04,0x20,0xA1,0xB0,0x04,0x20, +0xA5,0xB0,0x04,0x20,0xA9,0xB0,0x04,0x20,0xAD,0xB0,0x04,0x20,0xB1,0xB0,0x04,0x20, +0xB5,0xB0,0x04,0x20,0xB9,0xB0,0x04,0x20,0xBD,0xB0,0x04,0x20,0xC1,0xB0,0x04,0x20, +0xC5,0xB0,0x04,0x20,0xC9,0xB0,0x04,0x20,0xCD,0xB0,0x04,0x20,0xD1,0xB0,0x04,0x20, +0xD5,0xB0,0x04,0x20,0xD9,0xB0,0x04,0x20,0xDD,0xB0,0x04,0x20,0xE1,0xB0,0x04,0x20, +0xE5,0xB0,0x04,0x20,0xE9,0xB0,0x04,0x20,0xED,0xB0,0x04,0x20,0xF1,0xB0,0x04,0x20, +0xF5,0xB0,0x04,0x20,0xF9,0xB0,0x04,0x20,0xFD,0xB0,0x04,0x20,0x01,0xB1,0x04,0x20, +0x09,0xB1,0x04,0x20,0x0D,0xB1,0x04,0x20,0x11,0xB1,0x04,0x20,0x15,0xB1,0x04,0x20, +0x19,0xB1,0x04,0x20,0x1D,0xB1,0x04,0x20,0x21,0xB1,0x04,0x20,0x25,0xB1,0x04,0x20, +0x29,0xB1,0x04,0x20,0x2D,0xB1,0x04,0x20,0x31,0xB1,0x04,0x20,0x35,0xB1,0x04,0x20, +0x39,0xB1,0x04,0x20,0x3D,0xB1,0x04,0x20,0x41,0xB1,0x04,0x20,0x45,0xB1,0x04,0x20, +0x49,0xB1,0x04,0x20,0x4D,0xB1,0x04,0x20,0x51,0xB1,0x04,0x20,0x55,0xB1,0x04,0x20, +0x59,0xB1,0x04,0x20,0x5D,0xB1,0x04,0x20,0x61,0xB1,0x04,0x20,0x65,0xB1,0x04,0x20, +0x69,0xB1,0x04,0x20,0x6D,0xB1,0x04,0x20,0x71,0xB1,0x04,0x20,0x75,0xB1,0x04,0x20, +0x79,0xB1,0x04,0x20,0x7D,0xB1,0x04,0x20,0x81,0xB1,0x04,0x20,0x85,0xB1,0x04,0x20, +0x99,0xB1,0x04,0x20,0x9D,0xB1,0x04,0x20,0xA1,0xB1,0x04,0x20,0xA5,0xB1,0x04,0x20, +0xA9,0xB1,0x04,0x20,0xAD,0xB1,0x04,0x20,0xB1,0xB1,0x04,0x20,0xB5,0xB1,0x04,0x20, +0xB9,0xB1,0x04,0x20,0xBD,0xB1,0x04,0x20,0xC1,0xB1,0x04,0x20,0xC5,0xB1,0x04,0x20, +0xC9,0xB1,0x04,0x20,0xCD,0xB1,0x04,0x20,0xD1,0xB1,0x04,0x20,0xD5,0xB1,0x04,0x20, +0xD9,0xB1,0x04,0x20,0xDD,0xB1,0x04,0x20,0xE1,0xB1,0x04,0x20,0xE5,0xB1,0x04,0x20, +0xE9,0xB1,0x04,0x20,0xED,0xB1,0x04,0x20,0xF1,0xB1,0x04,0x20,0xF5,0xB1,0x04,0x20, +0xF9,0xB1,0x04,0x20,0xFD,0xB1,0x04,0x20,0x01,0xB2,0x04,0x20,0x05,0xB2,0x04,0x20, +0x09,0xB2,0x04,0x20,0x0D,0xB2,0x04,0x20,0x11,0xB2,0x04,0x20,0x15,0xB2,0x04,0x20, +0xDD,0x00,0x11,0x7A,0xDD,0x01,0x11,0x76,0xDD,0x02,0x11,0x72,0xDD,0x03,0x11,0x6E, +0xDD,0x04,0x11,0x6A,0xDD,0x05,0x11,0x66,0xDD,0x06,0x11,0x62,0xDD,0x07,0x11,0x5E, +0xDD,0x08,0x11,0x5A,0xDD,0x09,0x11,0x56,0xDD,0x0A,0x11,0x52,0xDD,0x0B,0x11,0x4E, +0xDD,0x0C,0x11,0x4A,0xDD,0x0D,0x11,0x46,0xDD,0x0E,0x11,0x42,0xDD,0x0F,0x11,0x3E, +0xDD,0x10,0x11,0x3A,0xDD,0x11,0x11,0x36,0xDD,0x12,0x11,0x32,0xDD,0x13,0x11,0x2E, +0xDD,0x14,0x11,0x2A,0xDD,0x15,0x11,0x26,0xDD,0x16,0x11,0x22,0xDD,0x17,0x11,0x1E, +0xDD,0x18,0x11,0x1A,0xDD,0x19,0x11,0x16,0xDD,0x1A,0x11,0x12,0xDD,0x1B,0x11,0x0E, +0xDD,0x1C,0x11,0x0A,0xDD,0x1D,0x11,0x06,0xDD,0x1E,0x11,0x02,0xDD,0x1F,0x17,0xEF, +0xE8,0x90,0xFF,0xFF,0xDD,0x20,0x11,0x7A,0xDD,0x21,0x11,0x76,0xDD,0x22,0x11,0x72, +0xDD,0x23,0x11,0x6E,0xDD,0x24,0x11,0x6A,0xDD,0x25,0x11,0x66,0xDD,0x26,0x11,0x62, +0xDD,0x27,0x11,0x5E,0xDD,0x28,0x11,0x5A,0xDD,0x29,0x11,0x56,0xDD,0x2A,0x11,0x52, +0xDD,0x2B,0x11,0x4E,0xDD,0x2C,0x11,0x4A,0xDD,0x2D,0x11,0x46,0xDD,0x2E,0x11,0x42, +0xDD,0x2F,0x11,0x3E,0xDD,0x30,0x11,0x3A,0xDD,0x31,0x11,0x36,0xDD,0x32,0x11,0x32, +0xDD,0x33,0x11,0x2E,0xDD,0x34,0x11,0x2A,0xDD,0x35,0x11,0x26,0xDD,0x36,0x11,0x22, +0xDD,0x37,0x11,0x1E,0xDD,0x38,0x11,0x1A,0xDD,0x39,0x11,0x16,0xDD,0x3A,0x11,0x12, +0xDD,0x3B,0x11,0x0E,0xDD,0x3C,0x11,0x0A,0xDD,0x3D,0x11,0x06,0xDD,0x3E,0x11,0x02, +0xDD,0x3F,0x17,0xEF,0x64,0x90,0xFF,0xFF,0xDD,0x00,0x11,0x7C,0xDD,0x01,0x11,0x78, +0xDD,0x02,0x11,0x74,0xDD,0x03,0x11,0x70,0xDD,0x04,0x11,0x6C,0xDD,0x05,0x11,0x68, +0xDD,0x06,0x11,0x64,0xDD,0x07,0x11,0x60,0xDD,0x08,0x11,0x5C,0xDD,0x09,0x11,0x58, +0xDD,0x0A,0x11,0x54,0xDD,0x0B,0x11,0x50,0xDD,0x0C,0x11,0x4C,0xDD,0x0D,0x11,0x48, +0xDD,0x0E,0x11,0x44,0xDD,0x0F,0x11,0x40,0xDD,0x10,0x11,0x3C,0xDD,0x11,0x11,0x38, +0xDD,0x12,0x11,0x34,0xDD,0x13,0x11,0x30,0xDD,0x14,0x11,0x2C,0xDD,0x15,0x11,0x28, +0xDD,0x16,0x11,0x24,0xDD,0x17,0x11,0x20,0xDD,0x18,0x11,0x1C,0xDD,0x19,0x11,0x18, +0xDD,0x1A,0x11,0x14,0xDD,0x1B,0x11,0x10,0xDD,0x1C,0x11,0x0C,0xDD,0x1D,0x11,0x08, +0xDD,0x1E,0x11,0x04,0xDD,0x1F,0x11,0x00,0xC8,0x8F,0x40,0x00,0x00,0x00,0x6E,0x17, +0xEF,0xD7,0x8F,0xFF,0xFF,0x00,0x00,0x00,0xDD,0x20,0x11,0x7A,0xDD,0x21,0x11,0x76, +0xDD,0x22,0x11,0x72,0xDD,0x23,0x11,0x6E,0xDD,0x24,0x11,0x6A,0xDD,0x25,0x11,0x66, +0xDD,0x26,0x11,0x62,0xDD,0x27,0x11,0x5E,0xDD,0x28,0x11,0x5A,0xDD,0x29,0x11,0x56, +0xDD,0x2A,0x11,0x52,0xDD,0x2B,0x11,0x4E,0xDD,0x2C,0x11,0x4A,0xDD,0x2D,0x11,0x46, +0xDD,0x2E,0x11,0x42,0xDD,0x2F,0x11,0x3E,0xDD,0x30,0x11,0x3A,0xDD,0x31,0x11,0x36, +0xDD,0x32,0x11,0x32,0xDD,0x33,0x11,0x2E,0xDD,0x34,0x11,0x2A,0xDD,0x35,0x11,0x26, +0xDD,0x36,0x11,0x22,0xDD,0x37,0x11,0x1E,0xDD,0x38,0x11,0x1A,0xDD,0x39,0x11,0x16, +0xDD,0x3A,0x11,0x12,0xDD,0x3B,0x11,0x0E,0xDD,0x3C,0x11,0x0A,0xDD,0x3D,0x11,0x06, +0xDD,0x3E,0x11,0x02,0xDD,0x3F,0xC8,0x8F,0x40,0x00,0x00,0x00,0x6E,0x17,0xEF,0x49, +0x8F,0xFF,0xFF,0x01,0x00,0x00,0x00,0x04,0x04,0x40,0x1F,0x42,0x1F,0x44,0x1F,0x46, +0x1F,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x10, +0x19,0x00,0x00,0x00,0x00,0x01,0x00,0x14,0x19,0x00,0x00,0x00,0x00,0x01,0x00,0x18, +0x19,0x00,0x00,0x00,0x00,0x01,0x00,0x1C,0x19,0x01,0x00,0x00,0x00,0x05,0x05,0x4C, +0x1F,0x04,0x08,0x0C,0x08,0x14,0x08,0x1C,0x08,0x80,0x00,0x78,0x00,0x7C,0x00,0xB8, +0x00,0xBC,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x1B,0x20,0x1B,0x40,0x1B,0x60, +0x1B,0x02,0x00,0x00,0x07,0x10,0x00,0x40,0x1D,0x48,0x1D,0x50,0x1D,0x58,0x1D,0x60, +0x1D,0x68,0x1D,0x70,0x1D,0x78,0x1D,0x80,0x1D,0x88,0x1D,0x90,0x1D,0x98,0x1D,0xA0, +0x1D,0xA8,0x1D,0xB0,0x1D,0xB8,0x1D,0x02,0x07,0x00,0x07,0x00,0x00,0x02,0x0F,0x00, +0x07,0x00,0x00,0x02,0x07,0x00,0x07,0x00,0x00,0x02,0x07,0x00,0x07,0x00,0x00,0x02, +0x07,0x00,0x07,0x00,0x00,0x02,0x07,0x00,0x07,0x00,0x00,0x02,0x07,0x00,0x07,0x00, +0x00,0x02,0x07,0x00,0x07,0x00,0x00,0x02,0x07,0x00,0x07,0x00,0x00,0x02,0x07,0x00, +0x07,0x00,0x00,0x02,0x07,0x00,0x07,0x00,0x00,0x02,0x0F,0x00,0x07,0x00,0x00,0x02, +0x07,0x00,0x07,0x00,0x00,0x01,0x07,0x00,0x03,0x01,0x01,0x00,0x19,0x70,0x00,0x01, +0x00,0x00,0x03,0x04,0x01,0x50,0x15,0x54,0x15,0x58,0x15,0x5C,0x15,0x94,0x00,0x02, +0x0F,0x00,0x07,0x01,0x00,0x30,0x11,0x02,0x07,0x00,0x07,0x00,0x00,0x01,0x07,0x00, +0x07,0x00,0x00,0x01,0x07,0x00,0x03,0x01,0x01,0x78,0x1E,0xB4,0x00,0x01,0x07,0x00, +0x03,0x00,0x00,0x01,0x07,0x00,0x03,0x02,0x01,0x08,0x15,0x18,0x15,0x54,0x00,0x02, +0x07,0x00,0x07,0x00,0x00,0x02,0x07,0x00,0x07,0x00,0x00,0x02,0x07,0x00,0x07,0x00, +0x00,0x02,0x0F,0x00,0x07,0x00,0x00,0x01,0x07,0x00,0x03,0x01,0x01,0x48,0x19,0x50, +0x00,0x01,0x00,0x00,0x03,0x04,0x01,0x20,0x19,0x30,0x19,0x50,0x19,0x60,0x19,0x50, +0x00,0x01,0x03,0x00,0x03,0x01,0x01,0x68,0x14,0x6C,0x00,0x08,0x1F,0x00,0x03,0x00, +0x00,0x03,0x0F,0x00,0x07,0x00,0x00,0x01,0x0F,0x00,0x03,0x00,0x00,0x01,0x03,0x00, +0x03,0x01,0x01,0x40,0x19,0xB0,0x00,0x02,0x0F,0x00,0x07,0x00,0x00,0x02,0x00,0x00, +0x07,0x01,0x00,0x40,0x08,0x02,0x0F,0x00,0x07,0x00,0x00,0x06,0x1F,0x00,0x03,0x00, +0x00,0x06,0x1F,0x00,0x03,0x00,0x00,0x02,0x3F,0x00,0x07,0x01,0x00,0x80,0x1E,0x01, +0x07,0x00,0x03,0x00,0x00,0x01,0x00,0x00,0x03,0x01,0x00,0x80,0x1C,0x01,0x0F,0x00, +0x03,0x00,0x00,0x01,0x07,0x00,0x03,0x01,0x00,0x78,0x15,0x01,0x07,0x00,0x03,0x00, +0x00,0x02,0x07,0x00,0x07,0x00,0x00,0x02,0x07,0x00,0x07,0x00,0x00,0x00,0x07,0x00, +0x00,0x00,0x00,0x02,0x07,0x00,0x07,0x01,0x01,0x00,0x1D,0x60,0x00,0x02,0x03,0x00, +0x07,0x01,0x00,0x10,0x11,0x02,0x07,0x00,0x07,0x01,0x00,0x08,0x1D,0x02,0x07,0x00, +0x07,0x01,0x00,0x10,0x1D,0x03,0x00,0x00,0x0F,0x08,0x00,0x00,0x1F,0x02,0x1F,0x04, +0x1F,0x06,0x1F,0x08,0x1F,0x0A,0x1F,0x0C,0x1F,0x0E,0x1F,0x10,0x00,0x00,0x03,0x03, +0x00,0x70,0x08,0x60,0x08,0x50,0x08,0x02,0x0F,0x00,0x07,0x00,0x00,0x01,0x07,0x00, +0x03,0x00,0x00,0x01,0x07,0x00,0x03,0x00,0x00,0x01,0x07,0x00,0x03,0x00,0x00,0x01, +0x07,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00, +0x00,0x01,0x07,0x00,0x07,0x00,0x00,0x02,0x07,0x00,0x07,0x00,0x00,0x00,0x07,0x00, +0x00,0x00,0x00,0x02,0x07,0x00,0x07,0x00,0x00,0x02,0x0F,0x00,0x07,0x00,0x00,0x02, +0x1F,0x00,0x07,0x00,0x00,0x02,0x01,0x00,0x07,0x00,0x00,0x01,0x1F,0x00,0x03,0x00, +0x00,0x02,0x03,0x00,0x07,0x00,0x00,0x02,0x1F,0x00,0x07,0x00,0x00,0x04,0x01,0x00, +0x00,0x00,0x04,0x49,0x50,0x43,0x52,0x04,0x02,0x00,0x00,0x80,0x07,0x4B,0x46,0x51, +0x53,0x41,0x5F,0x30,0x04,0x02,0x00,0x01,0x00,0x08,0x4B,0x46,0x51,0x53,0x41,0x20, +0x23,0x30,0x04,0x03,0x00,0x00,0x80,0x07,0x4B,0x46,0x51,0x53,0x41,0x5F,0x31,0x04, +0x03,0x00,0x01,0x00,0x08,0x4B,0x46,0x51,0x53,0x41,0x20,0x23,0x31,0x04,0x04,0x00, +0x00,0x80,0x07,0x4B,0x46,0x51,0x53,0x41,0x5F,0x32,0x04,0x04,0x00,0x01,0x00,0x08, +0x4B,0x46,0x51,0x53,0x41,0x20,0x23,0x32,0x04,0x05,0x00,0x00,0x80,0x07,0x4B,0x46, +0x51,0x53,0x41,0x5F,0x33,0x04,0x05,0x00,0x01,0x00,0x08,0x4B,0x46,0x51,0x53,0x41, +0x20,0x23,0x33,0x04,0x06,0x00,0x00,0x00,0x05,0x4C,0x50,0x56,0x31,0x31,0x04,0x07, +0x00,0x00,0x00,0x05,0x4B,0x58,0x4A,0x31,0x31,0x04,0x08,0x00,0x00,0x00,0x06,0x44, +0x4C,0x56,0x31,0x31,0x4A,0x04,0x10,0x00,0x00,0x00,0x05,0x44,0x5A,0x51,0x31,0x31, +0x04,0x10,0x00,0x01,0x00,0x05,0x44,0x5A,0x56,0x31,0x31,0x04,0x10,0x00,0x02,0x00, +0x05,0x44,0x46,0x41,0x30,0x31,0x04,0x16,0x00,0x00,0x00,0x05,0x52,0x4C,0x56,0x31, +0x32,0x04,0x17,0x00,0x00,0x00,0x05,0x54,0x53,0x56,0x30,0x35,0x04,0x1B,0x00,0x00, +0x00,0x05,0x52,0x58,0x56,0x32,0x31,0x04,0x1C,0x00,0x00,0x00,0x06,0x44,0x52,0x56, +0x31,0x31,0x57,0x04,0x1D,0x00,0x00,0x00,0x06,0x44,0x52,0x56,0x31,0x31,0x42,0x04, +0x1F,0x00,0x00,0x00,0x05,0x44,0x50,0x56,0x31,0x31,0x04,0x21,0x00,0x00,0x00,0x05, +0x44,0x4D,0x56,0x31,0x31,0x04,0x23,0x00,0x00,0x00,0x05,0x44,0x45,0x4C,0x51,0x41, +0x04,0x23,0x00,0x01,0x00,0x05,0x44,0x45,0x51,0x4E,0x41,0x04,0x23,0x00,0x02,0x00, +0x05,0x44,0x45,0x53,0x51,0x41,0x04,0x23,0x00,0x03,0x80,0x03,0x4C,0x51,0x41,0x04, +0x23,0x00,0x04,0x80,0x03,0x51,0x4E,0x41,0x04,0x23,0x00,0x05,0x80,0x03,0x53,0x51, +0x41,0x04,0x24,0x00,0x00,0x00,0x05,0x52,0x51,0x44,0x58,0x33,0x04,0x24,0x00,0x01, +0x00,0x05,0x4B,0x44,0x41,0x35,0x30,0x04,0x24,0x00,0x02,0x00,0x05,0x52,0x52,0x44, +0x35,0x30,0x04,0x24,0x00,0x03,0x00,0x05,0x52,0x51,0x43,0x32,0x35,0x04,0x24,0x00, +0x04,0x00,0x0A,0x4B,0x46,0x51,0x53,0x41,0x2D,0x44,0x49,0x53,0x4B,0x04,0x24,0x00, +0x05,0x80,0x04,0x52,0x43,0x32,0x35,0x04,0x28,0x00,0x00,0x00,0x05,0x54,0x51,0x4B, +0x35,0x30,0x04,0x28,0x00,0x01,0x00,0x05,0x54,0x51,0x4B,0x37,0x30,0x04,0x28,0x00, +0x02,0x00,0x05,0x54,0x55,0x38,0x31,0x45,0x04,0x28,0x00,0x03,0x00,0x04,0x52,0x56, +0x32,0x30,0x04,0x28,0x00,0x04,0x00,0x0A,0x4B,0x46,0x51,0x53,0x41,0x2D,0x54,0x41, +0x50,0x45,0x04,0x28,0x00,0x05,0x80,0x04,0x54,0x4B,0x35,0x30,0x04,0x28,0x00,0x06, +0x80,0x04,0x54,0x4B,0x37,0x30,0x04,0x29,0x00,0x00,0x00,0x05,0x4B,0x4D,0x56,0x31, +0x31,0x04,0x2A,0x00,0x00,0x00,0x05,0x49,0x45,0x51,0x31,0x31,0x04,0x2B,0x00,0x00, +0x00,0x05,0x44,0x48,0x51,0x31,0x31,0x04,0x2B,0x00,0x01,0x00,0x05,0x44,0x48,0x56, +0x31,0x31,0x04,0x2B,0x00,0x02,0x00,0x05,0x43,0x58,0x41,0x31,0x36,0x04,0x2B,0x00, +0x03,0x00,0x05,0x43,0x58,0x42,0x31,0x36,0x04,0x2B,0x00,0x04,0x00,0x05,0x43,0x58, +0x59,0x30,0x38,0x04,0x2E,0x00,0x00,0x00,0x05,0x56,0x43,0x42,0x30,0x31,0x04,0x2E, +0x00,0x01,0x00,0x04,0x51,0x56,0x53,0x53,0x04,0x30,0x00,0x00,0x00,0x05,0x4C,0x4E, +0x56,0x31,0x31,0x04,0x31,0x00,0x00,0x00,0x05,0x4C,0x4E,0x56,0x32,0x31,0x04,0x31, +0x00,0x01,0x00,0x04,0x51,0x50,0x53,0x53,0x04,0x33,0x00,0x00,0x00,0x05,0x44,0x53, +0x56,0x31,0x31,0x04,0x35,0x00,0x00,0x00,0x06,0x41,0x44,0x56,0x31,0x31,0x43,0x04, +0x36,0x00,0x00,0x00,0x06,0x41,0x41,0x56,0x31,0x31,0x43,0x04,0x37,0x00,0x00,0x00, +0x06,0x41,0x58,0x56,0x31,0x31,0x43,0x04,0x38,0x00,0x00,0x00,0x06,0x4B,0x57,0x56, +0x31,0x31,0x43,0x04,0x39,0x00,0x00,0x00,0x06,0x41,0x44,0x56,0x31,0x31,0x44,0x04, +0x3A,0x00,0x00,0x00,0x06,0x41,0x41,0x56,0x31,0x31,0x44,0x04,0x3B,0x00,0x00,0x00, +0x05,0x56,0x43,0x42,0x30,0x32,0x04,0x3B,0x00,0x01,0x00,0x04,0x51,0x44,0x53,0x53, +0x04,0x3C,0x00,0x00,0x00,0x06,0x44,0x52,0x56,0x31,0x31,0x4A,0x04,0x3D,0x00,0x00, +0x00,0x05,0x44,0x52,0x51,0x33,0x42,0x04,0x3F,0x00,0x00,0x00,0x05,0x56,0x53,0x56, +0x32,0x31,0x04,0x40,0x00,0x00,0x00,0x05,0x49,0x42,0x51,0x30,0x31,0x04,0x41,0x00, +0x00,0x00,0x06,0x49,0x44,0x56,0x31,0x31,0x41,0x04,0x42,0x00,0x00,0x00,0x06,0x49, +0x44,0x56,0x31,0x31,0x42,0x04,0x43,0x00,0x00,0x00,0x06,0x49,0x44,0x56,0x31,0x31, +0x43,0x04,0x44,0x00,0x00,0x00,0x06,0x49,0x44,0x56,0x31,0x31,0x44,0x04,0x45,0x00, +0x00,0x00,0x06,0x49,0x41,0x56,0x31,0x31,0x41,0x04,0x46,0x00,0x00,0x00,0x06,0x49, +0x41,0x56,0x31,0x31,0x42,0x04,0x47,0x00,0x00,0x00,0x04,0x4D,0x49,0x52,0x41,0x04, +0x49,0x00,0x00,0x00,0x05,0x41,0x44,0x51,0x33,0x32,0x04,0x4A,0x00,0x00,0x00,0x05, +0x44,0x54,0x43,0x30,0x34,0x04,0x4B,0x00,0x00,0x00,0x05,0x44,0x45,0x53,0x4E,0x41, +0x04,0x4B,0x00,0x01,0x80,0x03,0x53,0x4E,0x41,0x04,0x4C,0x00,0x00,0x00,0x05,0x49, +0x47,0x51,0x31,0x31,0x04,0x4F,0x00,0x00,0x80,0x04,0x45,0x58,0x49,0x54,0x04,0x50, +0x00,0x00,0x80,0x04,0x48,0x45,0x4C,0x50,0x00,0x00,0xF9,0xF9,0xFF,0xFF,0x0F,0xFA, +0xFF,0xFF,0x17,0xFA,0xFF,0xFF,0x1F,0xFA,0xFF,0xFF,0x27,0xFA,0xFF,0xFF,0x2F,0xFA, +0xFF,0xFF,0x49,0xFA,0xFF,0xFF,0x57,0xFA,0xFF,0xFF,0x7D,0xFA,0xFF,0xFF,0x83,0xFA, +0xFF,0xFF,0x89,0xFA,0xFF,0xFF,0x8F,0xFA,0xFF,0xFF,0x95,0xFA,0xFF,0xFF,0x9B,0xFA, +0xFF,0xFF,0xA1,0xFA,0xFF,0xFF,0xA7,0xFA,0xFF,0xFF,0xAD,0xFA,0xFF,0xFF,0xB3,0xFA, +0xFF,0xFF,0xB9,0xFA,0xFF,0xFF,0xBF,0xFA,0xFF,0xFF,0xC5,0xFA,0xFF,0xFF,0xCB,0xFA, +0xFF,0xFF,0xD5,0xFA,0xFF,0xFF,0xE5,0xFA,0xFF,0xFF,0xED,0xFA,0xFF,0xFF,0xF3,0xFA, +0xFF,0xFF,0xF9,0xFA,0xFF,0xFF,0x03,0xFB,0xFF,0xFF,0x09,0xFB,0xFF,0xFF,0x15,0xFB, +0xFF,0xFF,0x1B,0xFB,0xFF,0xFF,0x21,0xFB,0xFF,0xFF,0x27,0xFB,0xFF,0xFF,0x2D,0xFB, +0xFF,0xFF,0x37,0xFB,0xFF,0xFF,0x47,0xFB,0xFF,0xFF,0x51,0xFB,0xFF,0xFF,0x57,0xFB, +0xFF,0xFF,0x5D,0xFB,0xFF,0xFF,0x63,0xFB,0xFF,0xFF,0x6D,0xFB,0xFF,0xFF,0x73,0xFB, +0xFF,0xFF,0x7B,0xFB,0xFF,0xFF,0x81,0xFB,0xFF,0xFF,0x87,0xFB,0xFF,0xFF,0x8D,0xFB, +0xFF,0xFF,0x95,0xFB,0xFF,0xFF,0x9B,0xFB,0xFF,0xFF,0xA3,0xFB,0xFF,0xFF,0xA9,0xFB, +0xFF,0xFF,0xB1,0xFB,0xFF,0xFF,0xB7,0xFB,0xFF,0xFF,0xBD,0xFB,0xFF,0xFF,0xC3,0xFB, +0xFF,0xFF,0xC9,0xFB,0xFF,0xFF,0xD3,0xFB,0xFF,0xFF,0xDB,0xFB,0xFF,0xFF,0xE3,0xFB, +0xFF,0xFF,0xEB,0xFB,0xFF,0xFF,0x01,0xFC,0xFF,0xFF,0x0D,0xFC,0xFF,0xFF,0x13,0xFC, +0xFF,0xFF,0x19,0xFC,0xFF,0xFF,0x1F,0xFC,0xFF,0xFF,0x25,0xFC,0xFF,0xFF,0x2B,0xFC, +0xFF,0xFF,0x31,0xFC,0xFF,0xFF,0x37,0xFC,0xFF,0xFF,0x3D,0xFC,0xFF,0xFF,0x43,0xFC, +0xFF,0xFF,0x49,0xFC,0xFF,0xFF,0x4F,0xFC,0xFF,0xFF,0x55,0xFC,0xFF,0xFF,0x5B,0xFC, +0xFF,0xFF,0x61,0xFC,0xFF,0xFF,0x67,0xFC,0xFF,0xFF,0x6D,0xFC,0xFF,0xFF,0xC6,0xC6, +0x92,0x19,0x00,0x00,0xC7,0xC7,0x54,0x1A,0x00,0x00,0x34,0x34,0x0E,0x1B,0x00,0x00, +0x33,0x33,0xD6,0x1B,0x00,0x00,0x32,0x32,0x1E,0x1C,0x00,0x00,0x91,0x91,0x42,0x1D, +0x00,0x00,0x90,0x90,0xD2,0x1D,0x00,0x00,0x80,0x80,0x54,0x1E,0x00,0x00,0x60,0x60, +0x7B,0x24,0x00,0x00,0x62,0x62,0xCA,0x27,0x00,0x00,0x63,0x63,0x4A,0x29,0x00,0x00, +0x51,0x51,0xB0,0x2A,0x00,0x00,0x52,0x52,0x9C,0x2C,0x00,0x00,0x53,0x53,0x62,0x2F, +0x00,0x00,0x55,0x55,0x0D,0x31,0x00,0x00,0x5A,0x5A,0x82,0x31,0x00,0x00,0x45,0x45, +0x92,0x32,0x00,0x00,0x46,0x46,0x7A,0x35,0x00,0x00,0x9E,0x9E,0xA4,0x3B,0x00,0x00, +0x81,0x81,0xCA,0x3B,0x00,0x00,0x82,0x82,0x8C,0x3D,0x00,0x00,0xC1,0xC1,0x67,0x3F, +0x00,0x00,0xC2,0xC2,0x2E,0x41,0x00,0x00,0xC5,0xC5,0x9A,0x42,0x00,0x00,0x54,0x54, +0x8B,0x43,0x00,0x00,0x36,0x36,0x0A,0x46,0x00,0x00,0x35,0x35,0x7E,0x4B,0x00,0x00, +0x44,0x44,0x96,0x53,0x00,0x00,0x43,0x43,0xEF,0x53,0x00,0x00,0x41,0x41,0x1E,0x57, +0x00,0x00,0x42,0x42,0x0B,0x59,0x00,0x00,0x31,0x31,0x4E,0x59,0x00,0x00,0x30,0x30, +0x71,0x5F,0x00,0x00,0x4F,0x4F,0xAC,0x63,0x00,0x00,0x4E,0x4E,0x6A,0x65,0x00,0x00, +0x4D,0x4D,0x7F,0x66,0x00,0x00,0x4C,0x4C,0x11,0x68,0x00,0x00,0x4B,0x4B,0xD2,0x6C, +0x00,0x00,0x4A,0x4A,0xAC,0x6E,0x00,0x00,0x49,0x49,0xBF,0x70,0x00,0x00,0x48,0x48, +0x8E,0x76,0x00,0x00,0x47,0x47,0xE5,0x7C,0x00,0x00,0x40,0x40,0x70,0x7E,0x00,0x00, +0x9D,0x9D,0xB6,0x81,0x00,0x00,0x9C,0x9C,0xBA,0x82,0x00,0x00,0x9F,0x9F,0x76,0x88, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x01,0xC0,0x04,0x20,0x05,0xC0,0x04,0x20,0x1D,0xC0,0x04,0x20,0x25,0xC0,0x04,0x20, +0x2D,0xC0,0x04,0x20,0x35,0xC0,0x04,0x20,0x3D,0xC0,0x04,0x20,0x45,0xC0,0x04,0x20, +0x4D,0xC0,0x04,0x20,0x59,0xC0,0x04,0x20,0x65,0xC0,0x04,0x20,0xD1,0x1B,0x04,0x20, +0x75,0xC0,0x04,0x20,0x7D,0xC0,0x04,0x20,0x85,0xC0,0x04,0x20,0x8D,0xC0,0x04,0x20, +0x94,0xC0,0x04,0x20,0x9C,0xC0,0x04,0x20,0xA4,0xC0,0x04,0x20,0xAC,0xC0,0x04,0x20, +0xBD,0xC0,0x04,0x20,0xC5,0xC0,0x04,0x20,0xCD,0xC0,0x04,0x20,0xD5,0xC0,0x04,0x20, +0xDD,0xC0,0x04,0x20,0xE5,0xC0,0x04,0x20,0xED,0xC0,0x04,0x20,0xF5,0xC0,0x04,0x20, +0xFD,0xC0,0x04,0x20,0x05,0xC1,0x04,0x20,0x0D,0xC1,0x04,0x20,0x15,0xC1,0x04,0x20, +0x1D,0xC1,0x04,0x20,0x25,0xC1,0x04,0x20,0x2D,0xC1,0x04,0x20,0x35,0xC1,0x04,0x20, +0x3D,0xC1,0x04,0x20,0x45,0xC1,0x04,0x20,0x4D,0xC1,0x04,0x20,0x55,0xC1,0x04,0x20, +0x5D,0xC1,0x04,0x20,0x65,0xC1,0x04,0x20,0x6D,0xC1,0x04,0x20,0x75,0xC1,0x04,0x20, +0x7D,0xC1,0x04,0x20,0x85,0xC1,0x04,0x20,0x8D,0xC1,0x04,0x20,0x95,0xC1,0x04,0x20, +0x9D,0xC1,0x04,0x20,0xA5,0xC1,0x04,0x20,0xAD,0xC1,0x04,0x20,0xB5,0xC1,0x04,0x20, +0xBD,0xC1,0x04,0x20,0xC5,0xC1,0x04,0x20,0xCD,0xC1,0x04,0x20,0xD5,0xC1,0x04,0x20, +0xDD,0xC1,0x04,0x20,0xE5,0xC1,0x04,0x20,0xED,0xC1,0x04,0x20,0xF5,0xC1,0x04,0x20, +0xFD,0xC1,0x04,0x20,0x05,0xC2,0x04,0x20,0x0D,0xC2,0x04,0x20,0x15,0xC2,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20,0x01,0xC0,0x04,0x20, +0xD4,0x7E,0x11,0x1B,0x90,0xAE,0x04,0x9F,0xB3,0x07,0x14,0x20,0xDE,0xAE,0x04,0x7E, +0xC0,0xAE,0x04,0x6E,0x9A,0x04,0x7E,0x31,0x08,0x02,0x00,0x00,0x9A,0x08,0x7E,0x31, +0xF8,0x01,0x00,0x00,0x9A,0x0C,0x7E,0x31,0xF0,0x01,0x00,0x00,0x9A,0x10,0x7E,0x31, +0xE8,0x01,0x00,0x00,0x9A,0x14,0x7E,0x31,0xE0,0x01,0x00,0x00,0x9A,0x18,0x7E,0x31, +0xD8,0x01,0x00,0x00,0x9A,0x1C,0x7E,0x31,0xD0,0x01,0x00,0x00,0xDE,0xAE,0x08,0x7E, +0x9A,0x20,0x7E,0x31,0xCC,0x01,0x00,0x00,0xDE,0xAE,0x08,0x7E,0x9A,0x24,0x7E,0x31, +0xC0,0x01,0x00,0x00,0x9A,0x28,0x7E,0x31,0xB0,0x01,0x00,0x00,0x9A,0x2C,0x7E,0x31, +0xA8,0x01,0x00,0x00,0x9A,0x30,0x7E,0x31,0xA0,0x01,0x00,0x00,0x9A,0x34,0x7E,0x31, +0x2E,0x00,0x00,0x00,0x9A,0x38,0x7E,0x31,0x90,0x01,0x00,0x00,0x9A,0x3C,0x7E,0x31, +0x88,0x01,0x00,0x00,0x9A,0x8F,0x40,0x7E,0x31,0x15,0x00,0x00,0x9A,0x8F,0x44,0x7E, +0x31,0x0D,0x00,0x00,0x9A,0x8F,0x48,0x7E,0x31,0x05,0x00,0x00,0x9A,0x8F,0x4C,0x7E, +0xD0,0x6E,0x7E,0xDE,0xAE,0x0C,0xAE,0x04,0x31,0x67,0x01,0x00,0x9A,0x8F,0x50,0x7E, +0x31,0x57,0x01,0x00,0x9A,0x8F,0x54,0x7E,0x31,0x4F,0x01,0x00,0x9A,0x8F,0x58,0x7E, +0x31,0x47,0x01,0x00,0x9A,0x8F,0x5C,0x7E,0x31,0x3F,0x01,0x00,0x9A,0x8F,0x60,0x7E, +0x31,0x37,0x01,0x00,0x9A,0x8F,0x64,0x7E,0x31,0x2F,0x01,0x00,0x9A,0x8F,0x68,0x7E, +0x31,0x27,0x01,0x00,0x9A,0x8F,0x6C,0x7E,0x31,0x1F,0x01,0x00,0x9A,0x8F,0x70,0x7E, +0x31,0x17,0x01,0x00,0x9A,0x8F,0x74,0x7E,0x31,0x0F,0x01,0x00,0x9A,0x8F,0x78,0x7E, +0x31,0x07,0x01,0x00,0x9A,0x8F,0x7C,0x7E,0x31,0xFF,0x00,0x00,0x9A,0x8F,0x80,0x7E, +0x31,0xF7,0x00,0x00,0x9A,0x8F,0x84,0x7E,0x31,0xEF,0x00,0x00,0x9A,0x8F,0x88,0x7E, +0x31,0xE7,0x00,0x00,0x9A,0x8F,0x8C,0x7E,0x31,0xDF,0x00,0x00,0x9A,0x8F,0x90,0x7E, +0x31,0xD7,0x00,0x00,0x9A,0x8F,0x94,0x7E,0x31,0xCF,0x00,0x00,0x9A,0x8F,0x98,0x7E, +0x31,0xC7,0x00,0x00,0x9A,0x8F,0x9C,0x7E,0x31,0xBF,0x00,0x00,0x9A,0x8F,0xA0,0x7E, +0x31,0xB7,0x00,0x00,0x9A,0x8F,0xA4,0x7E,0x31,0xAF,0x00,0x00,0x9A,0x8F,0xA8,0x7E, +0x31,0xA7,0x00,0x00,0x9A,0x8F,0xAC,0x7E,0x31,0x9F,0x00,0x00,0x9A,0x8F,0xB0,0x7E, +0x31,0x97,0x00,0x00,0x9A,0x8F,0xB4,0x7E,0x31,0x8F,0x00,0x00,0x9A,0x8F,0xB8,0x7E, +0x31,0x87,0x00,0x00,0x9A,0x8F,0xBC,0x7E,0x31,0x7F,0x00,0x00,0x9A,0x8F,0xC0,0x7E, +0x31,0x77,0x00,0x00,0x9A,0x8F,0xC4,0x7E,0x31,0x6F,0x00,0x00,0x9A,0x8F,0xC8,0x7E, +0x31,0x67,0x00,0x00,0x9A,0x8F,0xCC,0x7E,0x31,0x5F,0x00,0x00,0x9A,0x8F,0xD0,0x7E, +0x31,0x57,0x00,0x00,0x9A,0x8F,0xD4,0x7E,0x31,0x4F,0x00,0x00,0x9A,0x8F,0xD8,0x7E, +0x31,0x47,0x00,0x00,0x9A,0x8F,0xDC,0x7E,0x31,0x3F,0x00,0x00,0x9A,0x8F,0xE0,0x7E, +0x31,0x37,0x00,0x00,0x9A,0x8F,0xE4,0x7E,0x31,0x2F,0x00,0x00,0x9A,0x8F,0xE8,0x7E, +0x31,0x27,0x00,0x00,0x9A,0x8F,0xEC,0x7E,0x31,0x1F,0x00,0x00,0x9A,0x8F,0xF0,0x7E, +0x31,0x17,0x00,0x00,0x9A,0x8F,0xF4,0x7E,0x31,0x0F,0x00,0x00,0x9A,0x8F,0xF8,0x7E, +0x31,0x07,0x00,0x00,0x9A,0x8F,0xFC,0x7E,0x11,0x00,0xD0,0x6E,0x7E,0xDE,0xAE,0x08, +0xAE,0x04,0xD0,0x8F,0x58,0x07,0x14,0x20,0x59,0xD5,0xA9,0x0C,0x13,0x03,0x17,0xB9, +0x0C,0x90,0x02,0xA9,0x79,0x90,0x8F,0xEF,0xA9,0x77,0xDD,0x50,0xD0,0x01,0x50,0xD0, +0x5E,0xA9,0x28,0xD0,0x40,0x6E,0x40,0xA9,0x28,0xF2,0x0A,0x50,0xF6,0xD0,0x8E,0x50, +0xD0,0xAE,0x04,0x5E,0xDE,0xEF,0x02,0x00,0x00,0x00,0x6E,0x02,0xDA,0xA9,0x50,0x11, +0x16,0xD9,0x84,0x00,0xD0,0xA9,0x14,0x5E,0xBA,0x8F,0xE0,0x03,0x05,0x00,0x00,0x00, +0x01,0xBB,0x8F,0xE0,0x03,0xD0,0x8F,0x58,0x07,0x14,0x20,0x59,0xDE,0xEF,0xD6,0x05, +0x00,0x00,0xA9,0x54,0xDE,0xEF,0x4A,0x7E,0xFF,0xFF,0xC9,0x84,0x00,0xDB,0x11,0xA9, +0x50,0xD0,0x5E,0xA9,0x14,0xDE,0xEF,0x65,0xF9,0xFF,0xFF,0x51,0xDA,0x51,0x11,0x95, +0xA9,0x75,0x12,0x05,0x90,0xA9,0x74,0xA9,0x75,0x90,0xA9,0x75,0xA9,0x74,0x9A,0xA9, +0x75,0x53,0x13,0x0C,0xD1,0x53,0x02,0x13,0x07,0xD1,0x53,0x03,0x13,0x02,0x11,0x05, +0x30,0x5E,0x0B,0x11,0x2F,0x91,0x53,0x01,0x12,0x06,0x30,0xEA,0x00,0x31,0xCF,0x00, +0x91,0x8F,0xFE,0x53,0x12,0x06,0x30,0x22,0x01,0x31,0xC3,0x00,0x90,0x02,0xA9,0x79, +0xD4,0x50,0x83,0x8F,0xA0,0x53,0x50,0x1F,0x27,0x12,0x09,0x94,0xA9,0x07,0xD0,0xA9, +0x10,0x50,0x11,0x16,0xD1,0x1F,0x50,0x1F,0x17,0x90,0x50,0xA9,0x07,0xD0,0x40,0xEF, +0x36,0x90,0x00,0x00,0x50,0x12,0x03,0x31,0x95,0x00,0x30,0x71,0x05,0x31,0x8F,0x00, +0x90,0x8F,0xFA,0xA9,0x77,0xDE,0xEF,0x43,0xF6,0xFF,0xFF,0x54,0x95,0x64,0x12,0x39, +0x11,0x26,0x25,0x6E,0x4E,0x6F,0x20,0x73,0x75,0x63,0x68,0x20,0x64,0x69,0x61,0x67, +0x6E,0x6F,0x73,0x74,0x69,0x63,0x2C,0x20,0x54,0x20,0x39,0x45,0x20,0x66,0x6F,0x72, +0x20,0x6C,0x69,0x73,0x74,0x25,0x6E,0x00,0x9F,0xAF,0xD7,0xDD,0x01,0x16,0xEF,0x42, +0x0C,0x00,0x00,0x31,0x49,0x00,0x31,0x46,0x00,0xD6,0x54,0x91,0x53,0x84,0x13,0x04, +0xD5,0x84,0x11,0xB8,0xDF,0xEF,0xF4,0xF5,0xFF,0xFF,0xC1,0x64,0x8E,0x54,0xD0,0x54, +0xA9,0x18,0x32,0xA4,0x02,0x55,0x9E,0x45,0x64,0xC9,0x80,0x00,0xEF,0x04,0x04,0x53, +0x55,0xF0,0x55,0x34,0x04,0x69,0x90,0x53,0xA9,0x0A,0x30,0x7B,0x04,0xCA,0x8F,0x00, +0x00,0x00,0x02,0x69,0x30,0xE2,0x06,0x89,0xA9,0x76,0xA9,0x77,0x50,0x12,0x00,0xDA, +0xA9,0x50,0x11,0xEF,0x30,0x04,0x69,0x7E,0xD0,0x8E,0x9F,0x30,0x00,0x14,0x20,0xDA, +0x1F,0x12,0xBA,0x8F,0xE0,0x03,0x05,0x91,0x8F,0xFF,0xA9,0x77,0x12,0x3C,0xDA,0x1F, +0x12,0xCA,0x8F,0x00,0x00,0x00,0x04,0x69,0xD0,0xA9,0x18,0x54,0xC8,0x8F,0x00,0x00, +0x00,0x40,0x69,0xDE,0xEF,0xCD,0x07,0x00,0x00,0xA9,0x0C,0xC3,0x04,0x5E,0xA9,0x7C, +0x32,0xA4,0x04,0x55,0x94,0xA9,0x77,0x94,0xA9,0x76,0x16,0x45,0x64,0xDA,0x1F,0x12, +0xD4,0xA9,0x0C,0xCA,0x8F,0x00,0x00,0x00,0x40,0x69,0x05,0x11,0x0D,0x25,0x6E,0x20, +0x62,0x69,0x74,0x6D,0x61,0x70,0x3D,0x25,0x6C,0x00,0xDD,0xA9,0x1C,0x9F,0xAF,0xED, +0xDD,0x02,0x16,0xEF,0x7D,0x0B,0x00,0x00,0x11,0x0C,0x2C,0x20,0x6C,0x65,0x6E,0x67, +0x74,0x68,0x3D,0x25,0x77,0x00,0xDD,0xA9,0x20,0x9F,0xAF,0xEE,0xDD,0x02,0x16,0xEF, +0x61,0x0B,0x00,0x00,0x11,0x0E,0x2C,0x20,0x63,0x68,0x65,0x63,0x6B,0x73,0x75,0x6D, +0x3D,0x25,0x77,0x00,0xDD,0xA9,0x22,0x9F,0xAF,0xEC,0xDD,0x02,0x16,0xEF,0x43,0x0B, +0x00,0x00,0x11,0x0D,0x25,0x6E,0x20,0x62,0x75,0x73,0x6D,0x61,0x70,0x3D,0x25,0x6C, +0x00,0xDD,0xA9,0x24,0x9F,0xAF,0xED,0xDD,0x02,0x16,0xEF,0x26,0x0B,0x00,0x00,0x11, +0x13,0x25,0x6E,0x20,0x72,0x65,0x74,0x75,0x72,0x6E,0x5F,0x73,0x74,0x61,0x63,0x6B, +0x3D,0x25,0x6C,0x00,0xDD,0xA9,0x7C,0x9F,0xAF,0xE7,0xDD,0x02,0x16,0xEF,0x03,0x0B, +0x00,0x00,0x11,0x11,0x25,0x6E,0x20,0x73,0x75,0x62,0x74,0x65,0x73,0x74,0x5F,0x70, +0x63,0x3D,0x25,0x6C,0x00,0xDD,0xC9,0x80,0x00,0x9F,0xAF,0xE8,0xDD,0x02,0x16,0xEF, +0xE1,0x0A,0x00,0x00,0x11,0x0E,0x25,0x6E,0x20,0x74,0x69,0x6D,0x65,0x6F,0x75,0x74, +0x3D,0x25,0x6C,0x00,0xDD,0xA9,0x5C,0x9F,0xAF,0xEC,0xDD,0x02,0x16,0xEF,0xC3,0x0A, +0x00,0x00,0x11,0x0B,0x2C,0x20,0x65,0x72,0x72,0x6F,0x72,0x3D,0x25,0x62,0x00,0xDD, +0xA9,0x76,0x9F,0xAF,0xEF,0xDD,0x02,0x16,0xEF,0xA8,0x0A,0x00,0x00,0x11,0x0E,0x2C, +0x20,0x64,0x65,0x5F,0x65,0x72,0x72,0x6F,0x72,0x3D,0x25,0x62,0x00,0xDD,0xA9,0x77, +0x9F,0xAF,0xEC,0xDD,0x02,0x16,0xEF,0x8A,0x0A,0x00,0x00,0x11,0x16,0x25,0x6E,0x20, +0x64,0x65,0x5F,0x65,0x72,0x72,0x6F,0x72,0x5F,0x76,0x65,0x63,0x74,0x6F,0x72,0x3D, +0x25,0x62,0x00,0xDD,0xA9,0x78,0x9F,0xAF,0xE4,0xDD,0x02,0x16,0xEF,0x64,0x0A,0x00, +0x00,0x11,0x13,0x2C,0x20,0x73,0x65,0x76,0x65,0x72,0x69,0x74,0x79,0x5F,0x63,0x6F, +0x64,0x65,0x3D,0x25,0x62,0x00,0xDD,0xA9,0x79,0x9F,0xAF,0xE7,0xDD,0x02,0x16,0xEF, +0x41,0x0A,0x00,0x00,0x11,0x17,0x2C,0x20,0x74,0x6F,0x74,0x61,0x6C,0x5F,0x65,0x72, +0x72,0x6F,0x72,0x5F,0x63,0x6F,0x75,0x6E,0x74,0x3D,0x25,0x77,0x00,0xDD,0xA9,0x7A, +0x9F,0xAF,0xE3,0xDD,0x02,0x16,0xEF,0x1A,0x0A,0x00,0x00,0xBB,0x01,0xD0,0x03,0x50, +0x11,0x15,0x25,0x6E,0x20,0x70,0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x5F,0x65,0x72, +0x72,0x6F,0x72,0x3D,0x25,0x6C,0x00,0xDD,0x40,0xA9,0x64,0x9F,0xAF,0xE4,0xDD,0x02, +0x16,0xEF,0xEF,0x09,0x00,0x00,0xD5,0x50,0x15,0x28,0xD1,0x04,0x06,0x1B,0x09,0xC3, +0x50,0x04,0x7E,0xD1,0x8E,0x06,0x18,0x1A,0xD7,0x50,0x11,0x05,0x2C,0x20,0x25,0x6C, +0x00,0xDD,0x40,0xA9,0x64,0x9F,0xAF,0xF4,0xDD,0x02,0x16,0xEF,0xC5,0x09,0x00,0x00, +0x11,0xD4,0xBA,0x01,0x11,0x18,0x25,0x6E,0x20,0x6C,0x61,0x73,0x74,0x5F,0x65,0x78, +0x63,0x65,0x70,0x74,0x69,0x6F,0x6E,0x5F,0x70,0x63,0x3D,0x25,0x6C,0x00,0xDD,0xC9, +0xA0,0x00,0x9F,0xAF,0xE1,0xDD,0x02,0x16,0xEF,0x98,0x09,0x00,0x00,0x11,0x0C,0x25, +0x6E,0x20,0x66,0x6C,0x61,0x67,0x73,0x3D,0x25,0x6C,0x00,0xDD,0x69,0x9F,0xAF,0xEF, +0xDD,0x02,0x16,0xEF,0x7D,0x09,0x00,0x00,0x11,0x10,0x2C,0x20,0x74,0x65,0x73,0x74, +0x5F,0x66,0x6C,0x61,0x67,0x73,0x3D,0x25,0x77,0x00,0xDD,0xA9,0x04,0x9F,0xAF,0xEA, +0xDD,0x02,0x16,0xEF,0x5D,0x09,0x00,0x00,0x11,0x17,0x25,0x6E,0x20,0x68,0x69,0x67, +0x68,0x65,0x73,0x74,0x5F,0x73,0x65,0x76,0x65,0x72,0x69,0x74,0x79,0x3D,0x25,0x62, +0x00,0xDD,0xA9,0x09,0x9F,0xAF,0xE3,0xDD,0x02,0x16,0xEF,0x36,0x09,0x00,0x00,0xBB, +0x01,0xEF,0x34,0x04,0x69,0x50,0x11,0x12,0x25,0x6E,0x20,0x6C,0x65,0x64,0x5F,0x64, +0x69,0x73,0x70,0x6C,0x61,0x79,0x3D,0x25,0x62,0x00,0xDD,0x50,0x9F,0xAF,0xE9,0xDD, +0x02,0x16,0xEF,0x0E,0x09,0x00,0x00,0xBA,0x01,0x11,0x16,0x25,0x6E,0x20,0x63,0x6F, +0x6E,0x73,0x6F,0x6C,0x65,0x5F,0x64,0x69,0x73,0x70,0x6C,0x61,0x79,0x3D,0x25,0x62, +0x00,0xDD,0xA9,0x0A,0x9F,0xAF,0xE4,0xDD,0x02,0x16,0xEF,0xE6,0x08,0x00,0x00,0x11, +0x15,0x25,0x6E,0x20,0x73,0x61,0x76,0x65,0x5F,0x6D,0x63,0x68,0x6B,0x5F,0x63,0x6F, +0x64,0x65,0x3D,0x25,0x62,0x00,0xDD,0xA9,0x5B,0x9F,0xAF,0xE5,0xDD,0x02,0x16,0xEF, +0xC1,0x08,0x00,0x00,0x11,0x14,0x2C,0x20,0x73,0x61,0x76,0x65,0x5F,0x65,0x72,0x72, +0x5F,0x66,0x6C,0x61,0x67,0x73,0x3D,0x25,0x62,0x00,0xDD,0xA9,0x5A,0x9F,0xAF,0xE6, +0xDD,0x02,0x16,0xEF,0x9D,0x08,0x00,0x00,0x11,0x03,0x25,0x77,0x00,0xDD,0xA9,0x58, +0x9F,0xAF,0xF7,0xDD,0x02,0x16,0xEF,0x8A,0x08,0x00,0x00,0x11,0x12,0x25,0x6E,0x20, +0x70,0x61,0x72,0x61,0x6D,0x65,0x74,0x65,0x72,0x5F,0x31,0x3D,0x25,0x6C,0x00,0xDD, +0xA9,0x28,0x9F,0xAF,0xE8,0xDD,0x02,0x16,0xEF,0x68,0x08,0x00,0x00,0x11,0x07,0x20, +0x20,0x32,0x3D,0x25,0x6C,0x00,0xDD,0xA9,0x2C,0x9F,0xAF,0xF3,0xDD,0x02,0x16,0xEF, +0x51,0x08,0x00,0x00,0x11,0x07,0x20,0x20,0x33,0x3D,0x25,0x6C,0x00,0xDD,0xA9,0x30, +0x9F,0xAF,0xF3,0xDD,0x02,0x16,0xEF,0x3A,0x08,0x00,0x00,0x11,0x07,0x20,0x20,0x34, +0x3D,0x25,0x6C,0x00,0xDD,0xA9,0x34,0x9F,0xAF,0xF3,0xDD,0x02,0x16,0xEF,0x23,0x08, +0x00,0x00,0x11,0x07,0x20,0x20,0x35,0x3D,0x25,0x6C,0x00,0xDD,0xA9,0x38,0x9F,0xAF, +0xF3,0xDD,0x02,0x16,0xEF,0x0C,0x08,0x00,0x00,0x11,0x12,0x25,0x6E,0x20,0x70,0x61, +0x72,0x61,0x6D,0x65,0x74,0x65,0x72,0x5F,0x36,0x3D,0x25,0x6C,0x00,0xDD,0xA9,0x3C, +0x9F,0xAF,0xE8,0xDD,0x02,0x16,0xEF,0xEA,0x07,0x00,0x00,0x11,0x07,0x20,0x20,0x37, +0x3D,0x25,0x6C,0x00,0xDD,0xA9,0x40,0x9F,0xAF,0xF3,0xDD,0x02,0x16,0xEF,0xD3,0x07, +0x00,0x00,0x11,0x07,0x20,0x20,0x38,0x3D,0x25,0x6C,0x00,0xDD,0xA9,0x44,0x9F,0xAF, +0xF3,0xDD,0x02,0x16,0xEF,0xBC,0x07,0x00,0x00,0x11,0x07,0x20,0x20,0x39,0x3D,0x25, +0x6C,0x00,0xDD,0xA9,0x48,0x9F,0xAF,0xF3,0xDD,0x02,0x16,0xEF,0xA5,0x07,0x00,0x00, +0x11,0x07,0x20,0x31,0x30,0x3D,0x25,0x6C,0x00,0xDD,0xA9,0x4C,0x9F,0xAF,0xF3,0xDD, +0x02,0x16,0xEF,0x8E,0x07,0x00,0x00,0x05,0xBB,0x03,0x9A,0xA9,0x0B,0x58,0x32,0xA4, +0x08,0x55,0xC0,0x54,0x55,0x9A,0x85,0x57,0xDE,0xA9,0x28,0x50,0x90,0x57,0xA9,0x0B, +0x13,0x33,0x95,0x85,0x12,0x08,0xD5,0x58,0x14,0x24,0xD4,0x60,0x11,0x20,0x9A,0xA5, +0xFF,0x51,0xC0,0x03,0x55,0xD5,0x58,0x14,0x0E,0x91,0x02,0x51,0x19,0x05,0xD0,0x65, +0x60,0x11,0x04,0xD0,0xA5,0x08,0x60,0x9C,0x02,0x51,0x51,0xC0,0x51,0x55,0xD5,0x80, +0xD7,0x58,0xF5,0x57,0xCD,0xBA,0x03,0x05,0xD0,0x8F,0x58,0x07,0x14,0x20,0x59,0xD0, +0x52,0xC9,0x84,0x00,0xD0,0x51,0xA9,0x50,0xDE,0xEF,0x92,0xF3,0xFF,0xFF,0x53,0xDA, +0x53,0x11,0xD0,0x5E,0xA9,0x14,0x30,0x05,0x00,0xDA,0xA9,0x50,0x11,0x05,0xD0,0x8F, +0x58,0x07,0x14,0x20,0x59,0xF0,0x00,0x01,0x01,0xA9,0x04,0x9A,0x60,0x53,0x12,0x01, +0x05,0xD4,0x51,0x91,0x53,0x8F,0xC0,0x1E,0x45,0x83,0x8F,0xA0,0x53,0x51,0x1F,0x3E, +0x12,0x07,0xD0,0xA9,0x10,0x50,0x31,0xD5,0xFF,0x91,0x51,0xA9,0x07,0x12,0x0B,0xD0, +0x41,0xEF,0x84,0x8A,0x00,0x00,0x50,0x31,0xC4,0xFF,0xC1,0x01,0x50,0x7E,0x98,0xA9, +0x07,0x7E,0x90,0x51,0xA9,0x07,0xD0,0x41,0xEF,0x6D,0x8A,0x00,0x00,0x50,0x30,0xAD, +0xFF,0xF6,0x8E,0xA9,0x07,0xD0,0x8E,0x50,0xE1,0x01,0xA9,0x04,0xA1,0x05,0x9A,0xA0, +0x01,0x52,0xDE,0xEF,0x76,0xF0,0xFF,0xFF,0x54,0x95,0x64,0x12,0x06,0xDE,0xA0,0x02, +0x50,0x11,0x98,0xD6,0x54,0x91,0x52,0x84,0x13,0x04,0xD5,0x84,0x11,0xEB,0xDF,0xEF, +0x5A,0xF0,0xFF,0xFF,0xC1,0x64,0x8E,0x54,0xD0,0x54,0xA9,0x18,0x32,0xA4,0x02,0x55, +0x9E,0x45,0x64,0xC9,0x80,0x00,0x90,0x52,0xA9,0x74,0x90,0x52,0xA9,0x75,0xF0,0xA0, +0x05,0x34,0x04,0x69,0x90,0xA0,0x06,0xA9,0x0A,0xEF,0x34,0x04,0x69,0x7E,0xCD,0x8E, +0x0F,0x9F,0x30,0x00,0x14,0x20,0x90,0xA9,0x0A,0x51,0x13,0x14,0xBB,0x1F,0xDB,0x11, +0x7E,0xDA,0xA9,0x50,0x11,0x16,0x9F,0x9F,0x40,0x04,0x20,0xDA,0x8E,0x11,0xBA,0x1F, +0x94,0xA9,0x77,0xE1,0x00,0xA0,0x02,0x18,0xE0,0x00,0x64,0x14,0xE0,0x07,0xA0,0x02, +0x29,0xED,0x10,0x02,0x69,0x03,0x12,0x08,0x16,0xEF,0x7D,0x72,0x00,0x00,0x11,0x4C, +0xE1,0x01,0xA0,0x02,0x15,0xE0,0x01,0x64,0x11,0xE0,0x07,0xA0,0x02,0x0C,0xED,0x12, +0x02,0x69,0x03,0x12,0x05,0x30,0xB5,0x05,0x11,0x32,0x93,0x03,0xA0,0x02,0x13,0x0A, +0xE1,0x00,0xA0,0x02,0x22,0xE1,0x01,0xA0,0x02,0x1D,0xE1,0x02,0xA0,0x02,0x1D,0xEF, +0x14,0x03,0x9F,0x10,0x00,0x14,0x20,0x55,0x78,0x55,0x8F,0x00,0x10,0x00,0x00,0x55, +0xC0,0x55,0xC9,0x80,0x00,0x11,0x05,0x90,0x8F,0xFB,0xA9,0x77,0xEF,0x00,0x02,0xA0, +0x04,0x55,0x90,0x55,0xA9,0x79,0x32,0xA4,0x08,0x55,0xC0,0x54,0x55,0x9A,0x85,0x57, +0xDE,0xA0,0x07,0x56,0xDE,0xA9,0x28,0x53,0x90,0x57,0xA9,0x0B,0x13,0x26,0x95,0x85, +0x12,0x04,0xD4,0x83,0x11,0x1B,0x9A,0xA5,0xFF,0x58,0xC0,0x03,0x55,0xD1,0x01,0x58, +0x12,0x05,0xD0,0x85,0x83,0x11,0x0A,0xD0,0x86,0x83,0x9C,0x02,0x58,0x58,0xC0,0x58, +0x55,0xF5,0x57,0xDA,0x9A,0xA0,0x03,0x57,0xD0,0x57,0x58,0x91,0x8F,0xFB,0xA9,0x77, +0x12,0x14,0xBB,0x1F,0xDB,0x11,0x7E,0xDA,0xA9,0x50,0x11,0x16,0xD9,0x84,0x00,0xDA, +0x8E,0x11,0xBA,0x1F,0x11,0x21,0xCA,0x8F,0x00,0x00,0x00,0x02,0x69,0xE0,0x03,0xA0, +0x04,0x07,0xC8,0x8F,0x00,0x00,0x00,0x02,0x69,0xE1,0x07,0xA0,0x02,0x03,0x30,0xB8, +0x34,0x30,0x35,0x00,0x30,0x13,0x35,0x89,0xA9,0x76,0xA9,0x77,0x51,0x13,0x14,0xE1, +0x02,0xA0,0x04,0x07,0xF0,0x01,0x01,0x01,0xA9,0x04,0x05,0x91,0x01,0x57,0x12,0x03, +0x31,0xD6,0xFF,0x91,0x02,0x57,0x12,0x03,0x31,0xCE,0xFF,0x91,0x03,0x57,0x1A,0x03, +0xF5,0x58,0xED,0xD0,0x56,0x50,0x31,0x05,0xFE,0xBB,0x8F,0xFF,0x01,0x94,0xA9,0x76, +0x90,0x8F,0xFF,0xA9,0x77,0xDA,0x1F,0x12,0xC3,0x04,0x5E,0xA9,0x7C,0xCA,0x8F,0x00, +0x00,0x00,0x20,0x69,0xCA,0x8F,0x00,0x00,0x00,0x40,0x69,0xCA,0x8F,0x00,0x00,0x00, +0x04,0x69,0xD4,0xC9,0xA0,0x00,0x8C,0x01,0x9F,0x30,0x00,0x14,0x20,0x8C,0x01,0x9F, +0x30,0x00,0x14,0x20,0xDE,0xEF,0xEC,0x00,0x00,0x00,0xA9,0x0C,0x16,0xD9,0x80,0x00, +0xDA,0x1F,0x12,0xD4,0xA9,0x0C,0x95,0xA9,0x76,0x13,0x4E,0xF0,0x9F,0x30,0x00,0x14, +0x20,0x30,0x04,0x69,0x8C,0x04,0x9F,0x30,0x00,0x14,0x20,0x8C,0x04,0x9F,0x30,0x00, +0x14,0x20,0xE0,0x19,0x69,0x41,0x30,0x71,0x34,0x30,0x62,0x07,0xBB,0x1F,0xDB,0x11, +0x7E,0xDA,0xA9,0x50,0x11,0xD0,0xC9,0x84,0x00,0x7E,0xD0,0xA9,0x58,0x59,0x16,0x9E, +0xD0,0x8F,0x58,0x07,0x14,0x20,0x59,0xDA,0x8E,0x11,0xBA,0x1F,0xE1,0x00,0xA9,0x04, +0x16,0x16,0xEF,0x2B,0x71,0x00,0x00,0x11,0x0E,0x8C,0x02,0x9F,0x30,0x00,0x14,0x20, +0x8C,0x02,0x9F,0x30,0x00,0x14,0x20,0xD0,0xA9,0x18,0x54,0xC8,0x8F,0x00,0x00,0x00, +0x40,0x69,0xDE,0xEF,0x6E,0x00,0x00,0x00,0xA9,0x0C,0xC3,0x04,0x5E,0xA9,0x7C,0x32, +0xA4,0x04,0x55,0x16,0x45,0x64,0xDA,0x1F,0x12,0xD4,0xA9,0x0C,0xCA,0x8F,0x00,0x00, +0x00,0x40,0x69,0x91,0x8F,0xFD,0xA9,0x77,0x12,0x19,0xE0,0x19,0x69,0x15,0x30,0xF9, +0x33,0xBB,0x1F,0xDB,0x11,0x7E,0xDA,0xA9,0x50,0x11,0x16,0xD9,0x84,0x00,0xDA,0x8E, +0x11,0xBA,0x1F,0x91,0x8F,0xFF,0xA9,0x77,0x12,0x03,0x94,0xA9,0x77,0x89,0xA9,0x76, +0xA9,0x77,0x50,0x13,0x1C,0xB6,0xA9,0x7A,0xD4,0x50,0xD0,0x40,0xA9,0x68,0x40,0xA9, +0x64,0xF2,0x04,0x50,0xF5,0x91,0xA9,0x79,0xA9,0x09,0x1B,0x05,0x90,0xA9,0x79,0xA9, +0x09,0xBA,0x8F,0xFF,0x01,0x05,0xBB,0x8F,0xFF,0x03,0xD0,0x8F,0x58,0x07,0x14,0x20, +0x59,0xD0,0xBE,0x2C,0xC9,0xA0,0x00,0xD4,0xA9,0x0C,0xE0,0x1E,0x69,0x5A,0xE0,0x1A, +0x69,0x50,0xD0,0xA9,0x18,0x54,0x32,0xA4,0x06,0x55,0x13,0x52,0xC0,0x54,0x55,0xD0, +0x55,0x54,0xE1,0x1D,0x69,0x48,0x9A,0x85,0x52,0xB1,0x85,0xAE,0x28,0x13,0x08,0xB5, +0x85,0xF5,0x52,0xF5,0x31,0x37,0x00,0x32,0x65,0x52,0xD0,0x01,0xA9,0x5C,0xDD,0xAE, +0x2C,0xC8,0x8F,0x00,0x00,0x00,0x04,0x69,0xDE,0xAF,0xAB,0xA9,0x0C,0x16,0x42,0x64, +0xCA,0x8F,0x00,0x00,0x00,0x04,0x69,0xD5,0x8E,0xBA,0x8F,0xFF,0x03,0xDE,0xBE,0x04, +0x5E,0x02,0x90,0x8F,0xFC,0x56,0x11,0x0A,0x90,0x8F,0xFD,0x56,0x11,0x04,0x90,0x8F, +0xFE,0x56,0x90,0x56,0xA9,0x77,0x90,0xAE,0x28,0xA9,0x78,0x91,0x8F,0x60,0xA9,0x75, +0x12,0x14,0xD0,0x8F,0xA0,0x03,0x02,0x00,0xA9,0x5C,0x16,0xEF,0x67,0x02,0x00,0x00, +0xDA,0x00,0x22,0xDA,0x00,0x20,0xDE,0xEF,0x14,0x00,0x00,0x00,0xBE,0x2C,0xBA,0x8F, +0xFF,0x03,0xDE,0xBE,0x04,0x5E,0xDB,0x12,0x7E,0xF0,0x8E,0x10,0x05,0xAE,0x04,0x02, +0xD0,0xA9,0x7C,0x5E,0x05,0x11,0x07,0x25,0x6E,0x54,0x65,0x73,0x74,0x00,0x9F,0xAF, +0xF6,0xDD,0x01,0x16,0xEF,0x1C,0x03,0x00,0x00,0x11,0x2B,0x25,0x6E,0x23,0x20,0x20, +0x20,0x41,0x64,0x64,0x72,0x65,0x73,0x73,0x20,0x20,0x20,0x4E,0x61,0x6D,0x65,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x50,0x61,0x72,0x61,0x6D, +0x65,0x74,0x65,0x72,0x73,0x00,0x9F,0xAF,0xD2,0xDD,0x01,0x16,0xEF,0xE4,0x02,0x00, +0x00,0x11,0x03,0x25,0x6E,0x00,0x9F,0xAF,0xFA,0xDD,0x01,0x16,0xEF,0xD4,0x02,0x00, +0x00,0xD0,0x8F,0x4B,0x00,0x00,0x00,0x50,0x11,0x02,0x5F,0x00,0x9F,0xAF,0xFB,0xDD, +0x01,0x16,0xEF,0xBE,0x02,0x00,0x00,0xF5,0x50,0xEE,0xDB,0x11,0x50,0x11,0x09,0x25, +0x6E,0x20,0x20,0x20,0x20,0x25,0x6C,0x00,0xDD,0x50,0x9F,0xAF,0xF2,0xDD,0x02,0x16, +0xEF,0xA0,0x02,0x00,0x00,0x11,0x09,0x20,0x20,0x44,0x65,0x5F,0x53,0x43,0x42,0x00, +0x9F,0xAF,0xF4,0xDD,0x01,0x16,0xEF,0x8A,0x02,0x00,0x00,0xD0,0x8F,0x00,0x00,0x04, +0x20,0x53,0xDE,0xEF,0x46,0xEC,0xFF,0xFF,0x51,0x95,0x61,0x12,0x01,0x05,0x90,0x81, +0x50,0x90,0x81,0x50,0x11,0x07,0x25,0x6E,0x25,0x62,0x20,0x20,0x00,0xDD,0x50,0x9F, +0xAF,0xF4,0xDD,0x02,0x16,0xEF,0x5B,0x02,0x00,0x00,0xDF,0xEF,0x1E,0xEC,0xFF,0xFF, +0xC1,0x81,0x8E,0x54,0xB0,0x54,0x7E,0xB1,0x8E,0x53,0x1E,0x07,0xD0,0x8F,0x00,0x00, +0x05,0x20,0x53,0xD0,0x53,0x7E,0xB4,0x6E,0xF0,0x00,0x10,0x10,0x54,0xC8,0x8E,0x54, +0xB0,0x54,0x53,0x11,0x05,0x25,0x6C,0x20,0x20,0x00,0xDD,0x54,0x9F,0xAF,0xF6,0xDD, +0x02,0x16,0xEF,0x1E,0x02,0x00,0x00,0xDE,0xA4,0x0A,0x50,0x11,0x05,0x25,0x61,0x20, +0x09,0x00,0xDD,0x50,0x9F,0xAF,0xF6,0xDD,0x02,0x16,0xEF,0x06,0x02,0x00,0x00,0x32, +0xA4,0x08,0x55,0xC0,0x54,0x55,0xD0,0x55,0x56,0x9A,0x85,0x57,0x12,0x03,0x31,0x78, +0xFF,0x9A,0x85,0x50,0x12,0x2A,0x11,0x02,0x2A,0x00,0x9F,0xAF,0xFB,0xDD,0x01,0x16, +0xEF,0xE0,0x01,0x00,0x00,0xD1,0x57,0x01,0x1B,0x13,0x95,0x65,0x13,0x0F,0x11,0x02, +0x20,0x00,0x9F,0xAF,0xFB,0xDD,0x01,0x16,0xEF,0xC8,0x01,0x00,0x00,0x31,0x22,0x00, +0x32,0x85,0x58,0xC0,0x56,0x58,0x11,0x04,0x25,0x61,0x20,0x00,0xDD,0x58,0x9F,0xAF, +0xF7,0xDD,0x02,0x16,0xEF,0xAC,0x01,0x00,0x00,0x78,0x02,0x50,0x50,0xD6,0x50,0xC0, +0x50,0x55,0xF5,0x57,0x03,0x31,0x21,0xFF,0x31,0xA6,0xFF,0x00,0x00,0x00,0x00,0xBB, +0x1F,0xDB,0x11,0x7E,0xDA,0xA9,0x50,0x11,0x16,0xD9,0x84,0x00,0xDA,0x8E,0x11,0xBA, +0x1F,0x05,0x00,0x02,0x03,0x0A,0x0B,0x02,0x10,0x10,0x1A,0x1B,0x03,0x12,0x11,0x0C, +0x01,0xDE,0xAF,0xEE,0x51,0x91,0x81,0xA9,0x75,0x13,0x05,0xC0,0x04,0x51,0x11,0xF5, +0xD0,0x9F,0x04,0x40,0x08,0x20,0x50,0xED,0x00,0x02,0x50,0x03,0x12,0x10,0xED,0x04, +0x03,0x50,0x04,0x12,0x04,0x9A,0x61,0x50,0x05,0x9A,0xA1,0x01,0x50,0x05,0xED,0x02, +0x02,0x9F,0x04,0x40,0x08,0x20,0x00,0x12,0x1D,0x9A,0xA1,0x03,0x50,0xDD,0xA9,0x0C, +0xDE,0xEF,0x16,0x00,0x00,0x00,0xA9,0x0C,0xB5,0x9F,0x00,0x1F,0x00,0x20,0xD0,0x8E, +0xA9,0x0C,0xD5,0x50,0x12,0x04,0x9A,0xA1,0x02,0x50,0x05,0x00,0xD0,0x9F,0x04,0x00, +0x08,0x20,0x7E,0xCA,0x8F,0x7F,0xFF,0xFF,0xFF,0x6E,0xD0,0x8E,0x9F,0x04,0x00,0x08, +0x20,0xD5,0x8E,0xC0,0x06,0xBE,0x00,0xD4,0x50,0xD0,0x6E,0x5E,0x02,0xEF,0x00,0x02, +0x9F,0x04,0x40,0x08,0x20,0x50,0x05,0xF0,0x00,0x1F,0x01,0xA9,0x5C,0xED,0x0C,0x02, +0x69,0x03,0x12,0x1D,0xD0,0x0F,0x9F,0x18,0x01,0x14,0x20,0xD0,0x8F,0x11,0x00,0x00, +0x80,0x9F,0x10,0x01,0x14,0x20,0xD1,0x9F,0x14,0x01,0x14,0x20,0xA9,0x5C,0x1F,0xF6, +0x05,0xDE,0xAF,0xD3,0xAE,0xFC,0xED,0x11,0x0F,0xAE,0xFC,0x8F,0x02,0x10,0x00,0x00, +0x13,0x06,0x17,0x9F,0xEE,0xCE,0x06,0x20,0x17,0x9F,0xEE,0xCE,0x04,0x20,0xC2,0x02, +0xA9,0x5C,0x14,0xFA,0x05,0xC8,0x8F,0x00,0x00,0x00,0x20,0x69,0x05,0xCA,0x8F,0x00, +0x00,0x00,0x20,0x69,0x05,0xD4,0x56,0xD4,0x57,0xD0,0x01,0x52,0xD0,0x02,0x53,0xD0, +0x52,0x54,0xCA,0x56,0x54,0xCA,0x57,0x54,0x13,0x18,0xD0,0x54,0x61,0xD0,0x61,0x55, +0xCA,0x57,0x55,0xD1,0x55,0x54,0x13,0x04,0xD0,0x01,0x53,0x05,0xD2,0x52,0x52,0xF5, +0x53,0xDD,0x78,0x01,0x52,0x52,0x12,0xD4,0xD4,0x61,0xD4,0x53,0x05,0xBB,0x3F,0xD0, +0x8F,0xBF,0x85,0x00,0x00,0x54,0x78,0x8F,0xF7,0x54,0x50,0xD6,0x50,0xD0,0xA9,0x1C, +0x51,0x13,0x30,0xCE,0x01,0x52,0xD6,0x52,0xD4,0x53,0xE1,0x52,0x61,0xF8,0xD6,0x52, +0xF2,0x50,0x53,0xF6,0xC2,0x50,0x52,0x78,0x09,0x52,0x7E,0x28,0x54,0xCF,0x8B,0xFE, +0xBE,0x00,0xDE,0xCF,0x85,0xFE,0x55,0xC3,0x55,0xC9,0x80,0x00,0x55,0xC1,0x8E,0x55, +0xC9,0x80,0x00,0xBA,0x3F,0x05,0xBB,0x20,0x9A,0x85,0x50,0x13,0x05,0x30,0x6C,0x02, +0x11,0xF6,0xBA,0x20,0x05,0xDD,0x58,0xC1,0x04,0x5E,0x58,0xBB,0x8F,0xFF,0x00,0xD0, +0xA8,0x08,0x52,0xDE,0xA8,0x0C,0x54,0x9A,0x82,0x50,0x13,0x11,0x91,0x25,0x50,0x12, +0x07,0x9A,0x82,0x50,0x10,0x21,0x11,0xEF,0x30,0x41,0x02,0x11,0xEA,0xD0,0xA8,0x04, +0x54,0xDE,0x44,0xA8,0x04,0x54,0xD0,0x68,0x64,0xD0,0x54,0x68,0xBA,0x8F,0xFF,0x00, +0xD0,0x8E,0x58,0xD0,0x6E,0x5E,0x05,0x8F,0x50,0x8F,0x61,0x16,0x9A,0x00,0x3B,0x00, +0x2E,0x00,0x68,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00, +0x2E,0x00,0x45,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00, +0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x40,0x00,0x9A,0x0D,0x50,0x30,0xEC,0x01, +0x9A,0x0A,0x50,0x30,0xE6,0x01,0x05,0x9A,0x08,0x56,0x11,0x08,0x9A,0x10,0x56,0x11, +0x03,0x9A,0x20,0x56,0xD0,0x84,0x55,0xC2,0x04,0x56,0x18,0x01,0x05,0xEF,0x56,0x04, +0x55,0x50,0x91,0x09,0x50,0x19,0x05,0xC0,0x30,0x50,0x11,0x03,0xC0,0x37,0x50,0x30, +0xBA,0x01,0x11,0xE3,0xDD,0x58,0xD0,0x84,0x55,0xD4,0x56,0xD0,0x8F,0x00,0xCA,0x9A, +0x3B,0x57,0xD4,0x58,0x7B,0x57,0x55,0x50,0x55,0x12,0x08,0xE8,0x58,0x05,0xD1,0x01, +0x57,0x12,0x0A,0xC0,0x30,0x50,0x30,0x93,0x01,0xE2,0x00,0x58,0x00,0xC6,0x0A,0x57, +0x12,0xE2,0xD0,0x8E,0x58,0x05,0xD0,0x84,0x55,0x30,0x0A,0xFF,0x05,0xDD,0x58,0xC1, +0x04,0x5E,0x58,0xBB,0x8F,0xFF,0x00,0xD0,0xA8,0x08,0x52,0xDE,0xA8,0x0C,0x54,0xD0, +0x84,0x57,0x9A,0x82,0x50,0x13,0x11,0x91,0x25,0x50,0x12,0x07,0x9A,0x82,0x50,0x10, +0x21,0x11,0xEF,0x30,0x56,0x01,0x11,0xEA,0xD0,0xA8,0x04,0x54,0xDE,0x44,0xA8,0x04, +0x54,0xD0,0x68,0x64,0xD0,0x54,0x68,0xBA,0x8F,0xFF,0x00,0xD0,0x8E,0x58,0xD0,0x6E, +0x5E,0x05,0x8F,0x50,0x8F,0x61,0x16,0xAD,0x00,0x3B,0x00,0x2E,0x00,0x86,0x00,0x2E, +0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x47,0x00,0x2E, +0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E,0x00,0x2E, +0x00,0x2E,0x00,0x41,0x00,0x9A,0x0D,0x50,0x30,0x01,0x01,0x9A,0x0A,0x50,0x30,0xFB, +0x00,0x05,0x10,0x10,0x90,0x51,0x94,0x05,0x10,0x0A,0xB0,0x51,0x94,0x05,0x10,0x04, +0xD0,0x51,0x94,0x05,0xBB,0x0C,0x30,0x72,0x00,0xD0,0x57,0x52,0x9A,0x82,0x53,0xD4, +0x51,0xD5,0x53,0x12,0x03,0xBA,0x0C,0x05,0xC4,0x10,0x51,0xD7,0x53,0x9A,0x82,0x50, +0x91,0x39,0x50,0x19,0x08,0xC2,0x30,0x50,0xC0,0x50,0x51,0x11,0xE4,0xC2,0x37,0x50, +0x91,0x10,0x50,0x14,0x03,0xC2,0x20,0x50,0xC0,0x50,0x51,0x11,0xD4,0xBB,0x0C,0x30, +0x39,0x00,0xD0,0x57,0x52,0x9A,0x82,0x53,0xD4,0x51,0xD5,0x53,0x12,0x06,0xD0,0x51, +0x94,0xBA,0x0C,0x05,0xC4,0x0A,0x51,0xD7,0x53,0x9A,0x82,0x50,0xC2,0x30,0x50,0xC0, +0x50,0x51,0x11,0xE6,0x30,0x14,0x00,0x9A,0x67,0x56,0xBB,0x3C,0x28,0x56,0xA7,0x01, +0xB4,0x00,0xBA,0x3C,0x94,0x46,0xB4,0x00,0xD5,0x84,0x05,0xBB,0x0C,0xD0,0x57,0x52, +0x95,0x82,0x30,0x50,0x00,0x91,0x50,0x8F,0x7F,0x13,0x1F,0xD0,0x50,0x53,0x91,0x53, +0x03,0x13,0x37,0x91,0x53,0x0D,0x13,0x32,0x30,0x51,0x00,0x91,0x53,0x20,0x13,0x2A, +0x91,0x53,0x09,0x13,0x25,0x90,0x53,0x82,0x11,0xD8,0xC3,0x57,0x52,0x50,0xD1,0x01, +0x50,0x13,0xCF,0x95,0x72,0x9A,0x08,0x50,0x30,0x31,0x00,0x9A,0x20,0x50,0x30,0x2B, +0x00,0x9A,0x08,0x50,0x30,0x25,0x00,0x31,0xB8,0xFF,0xC3,0x57,0x52,0x50,0x83,0x01, +0x50,0x67,0xBA,0x0C,0x05,0xBB,0x1E,0xD4,0x50,0xDB,0x11,0x7E,0xDA,0xA9,0x50,0x11, +0x16,0x9F,0xCE,0x35,0x04,0x20,0xDA,0x8E,0x11,0xBA,0x1E,0x05,0xBB,0x1E,0xDB,0x11, +0x7E,0xDA,0xA9,0x50,0x11,0x91,0x0D,0x50,0x12,0x05,0x94,0xA9,0x08,0x11,0x1F,0x91, +0x09,0x50,0x12,0x1A,0x90,0x20,0x50,0x93,0x07,0xA9,0x08,0x13,0x20,0x16,0x9F,0x47, +0x36,0x04,0x20,0x16,0x9F,0x7F,0x35,0x04,0x20,0x96,0xA9,0x08,0x11,0xE6,0x96,0xA9, +0x08,0x16,0x9F,0x47,0x36,0x04,0x20,0x16,0x9F,0x7F,0x35,0x04,0x20,0xDA,0x8E,0x11, +0xBA,0x1E,0x05,0x90,0x0D,0x50,0x10,0xB4,0x90,0x0A,0x50,0x10,0xAF,0x05,0xBB,0x03, +0xDB,0x27,0x50,0xF0,0x50,0x13,0x02,0xA9,0x58,0xEF,0x04,0x03,0x50,0x50,0xF0,0x50, +0x15,0x03,0xA9,0x58,0xD0,0x9F,0x40,0x01,0x08,0x20,0x51,0xEF,0x1D,0x03,0x51,0x50, +0xF0,0x50,0x0F,0x03,0xA9,0x58,0xEF,0x16,0x04,0x51,0x50,0xF0,0x50,0x0B,0x04,0xA9, +0x58,0xEF,0x07,0x02,0x51,0x50,0xF0,0x50,0x09,0x02,0xA9,0x58,0xEF,0x1E,0x02,0x9F, +0x20,0x00,0x14,0x20,0x50,0xF0,0x50,0x07,0x02,0xA9,0x58,0xD0,0x9F,0x04,0x00,0x08, +0x20,0x51,0xEF,0x07,0x01,0x51,0x50,0xF0,0x50,0x06,0x01,0xA9,0x58,0xEF,0x02,0x04, +0x51,0x50,0xF0,0x50,0x02,0x04,0xA9,0x58,0xF0,0x51,0x01,0x01,0xA9,0x58,0xD0,0x8F, +0x40,0x1F,0x00,0x20,0x51,0xE1,0x0A,0x9F,0x00,0x00,0x08,0x20,0x10,0xEF,0x01,0x03, +0x9F,0x00,0x00,0x08,0x20,0x50,0x78,0x01,0x50,0x50,0xC8,0x50,0x51,0xB0,0x61,0x50, +0xEF,0x0F,0x01,0x50,0x50,0xF0,0x50,0x00,0x01,0xA9,0x58,0xBA,0x03,0x05,0x00,0x00, +0x03,0x80,0x2C,0x00,0xC1,0x00,0x00,0x00,0x16,0x00,0x53,0x53,0x43,0x5F,0x70,0x6F, +0x77,0x65,0x72,0x75,0x70,0x00,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x20,0x00,0x21,0x00,0x22,0x80,0x00,0x00,0x01,0x14,0x20,0x08,0xE3,0x18,0x69,0x03, +0x31,0x89,0x00,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0xDE,0xAF,0xE4,0x56,0x90,0x01,0xA9, +0x76,0x9A,0x86,0x51,0x13,0x14,0x9A,0x86,0x52,0xDB,0x51,0x50,0xD1,0x51,0x21,0x12, +0x02,0x94,0x50,0xD1,0x52,0x50,0x12,0x68,0x11,0xE3,0x90,0x02,0xA9,0x76,0xD0,0x8F, +0x00,0x01,0x14,0x20,0xA9,0x28,0xD0,0x08,0x52,0xD0,0xB9,0x28,0xA9,0x2C,0x11,0x09, +0x12,0x4E,0xC0,0x04,0xA9,0x28,0xF5,0x52,0xF0,0xDE,0xA9,0x30,0x51,0xD0,0x8F,0xB2, +0x04,0x14,0x20,0x52,0xD0,0xA2,0x10,0x81,0xD0,0xA2,0x14,0x81,0xD0,0xA2,0x08,0x81, +0xD0,0xA2,0x0C,0x81,0xD0,0xA2,0x1C,0x81,0xD0,0xA2,0x18,0x81,0xD2,0xA2,0x50,0x61, +0x90,0x03,0xA9,0x76,0xD0,0x71,0x50,0xEF,0x00,0x1F,0x50,0x50,0x12,0x12,0x90,0x04, +0xA9,0x76,0xD0,0x05,0x54,0xD5,0x71,0x12,0x07,0xF5,0x54,0xF9,0x94,0xA9,0x76,0x05, +0x05,0x05,0x03,0x80,0x21,0x00,0xB7,0x00,0x1C,0x00,0x18,0x00,0x43,0x42,0x54,0x43, +0x52,0x20,0x74,0x69,0x6D,0x65,0x6F,0x75,0x74,0x00,0x03,0x00,0x00,0x00,0x01,0x04, +0x00,0x92,0x00,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x01,0xA9,0x76,0xDE,0xEF,0x42, +0x00,0x00,0x00,0xA9,0x30,0xD4,0xA9,0x28,0xD0,0x8F,0x20,0x00,0x14,0x20,0x50,0xD0, +0x8F,0x14,0x00,0x08,0x20,0x51,0xC8,0x8F,0x00,0x00,0x00,0xC0,0x60,0xD0,0x60,0xA9, +0x2C,0x90,0x02,0xA9,0x76,0xEF,0x00,0x1E,0xA9,0x2C,0x52,0x13,0x52,0x90,0x03,0xA9, +0x76,0xEF,0x1E,0x02,0xA9,0x2C,0x52,0x12,0x46,0x90,0x04,0xA9,0x76,0xF0,0x01,0x1D, +0x01,0x69,0xD0,0x61,0x52,0xF0,0x00,0x1D,0x01,0x69,0x90,0x05,0xA9,0x76,0xD1,0x01, +0xA9,0x28,0x12,0x2B,0x90,0x06,0xA9,0x76,0xD0,0x60,0xA9,0x2C,0xED,0x1E,0x02,0xA9, +0x2C,0x03,0x12,0x1B,0x90,0x07,0xA9,0x76,0xC8,0x8F,0x00,0x00,0x00,0xC0,0x60,0xD0, +0x60,0xA9,0x2C,0xEF,0x1E,0x02,0xA9,0x2C,0x52,0x12,0x04,0x94,0xA9,0x76,0x05,0x05, +0xD6,0xA9,0x28,0xD0,0xA9,0x30,0xBE,0x04,0x05,0x05,0x00,0x00,0x00,0x80,0x34,0x00, +0xC6,0x00,0x00,0x00,0x19,0x00,0x52,0x4F,0x4D,0x20,0x6C,0x6F,0x67,0x69,0x63,0x20, +0x74,0x65,0x73,0x74,0x00,0x01,0x00,0x00,0x55,0x55,0x55,0x55,0xAA,0xAA,0xAA,0xAA, +0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08, +0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x01,0xA9,0x76,0xDE,0xAF,0xDB,0x52,0xD0,0x82, +0x53,0xD0,0x82,0x54,0xC1,0x53,0x54,0x55,0xD6,0x55,0x12,0x75,0x90,0x02,0xA9,0x76, +0xD2,0x53,0x56,0xD1,0x56,0x54,0x12,0x69,0x90,0x03,0xA9,0x76,0xD0,0x82,0x51,0x12, +0x60,0x90,0x04,0xA9,0x76,0xD2,0x51,0x50,0xD0,0x82,0x51,0xD1,0x51,0x50,0x12,0x51, +0x90,0x05,0xA9,0x76,0xD0,0x01,0x50,0x9A,0x82,0x51,0x91,0x50,0x51,0x12,0x42,0xF3, +0x08,0x50,0xF4,0x90,0x06,0xA9,0x76,0xC2,0x05,0x52,0x3C,0x62,0x51,0xB1,0x51,0x8F, +0x04,0x05,0x12,0x2D,0x90,0x07,0xA9,0x76,0xD4,0x50,0x9E,0xEF,0x9C,0xC5,0x00,0x00, +0x51,0x9E,0xEF,0xE9,0x2A,0xFF,0xFF,0x52,0xC2,0x52,0x51,0x78,0x8F,0xFE,0x51,0x51, +0xC0,0x82,0x50,0xF4,0x51,0xFA,0x90,0x08,0xA9,0x76,0xD5,0x50,0x01,0x01,0x94,0xA9, +0x76,0x05,0x05,0x00,0x03,0x80,0x1A,0x00,0x44,0x00,0x00,0x00,0x18,0x00,0x43,0x4D, +0x43,0x54,0x4C,0x5F,0x70,0x6F,0x77,0x65,0x72,0x75,0x70,0x00,0x01,0x00,0xE3,0x16, +0x69,0x02,0x11,0x1F,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0xD0,0x8F,0x00,0x01,0x08,0x20, +0x51,0xD0,0x12,0x52,0x90,0x01,0xA9,0x76,0xD0,0x61,0x50,0x12,0x0A,0xC0,0x04,0x51, +0xF5,0x52,0xF5,0x94,0xA9,0x76,0x05,0x05,0x05,0x00,0x00,0x00,0x02,0x00,0x34,0x00, +0xF6,0x00,0x00,0x00,0x15,0x00,0x43,0x4D,0x43,0x54,0x4C,0x20,0x72,0x65,0x67,0x73, +0x00,0x0A,0x01,0x12,0x00,0x01,0x00,0x01,0x08,0x20,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x4D,0x45,0x4D,0x43,0x53,0x52,0x30,0x5F,0x61,0x64,0x64,0x72,0x00, +0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x01,0xA9,0x76,0xD4,0xA9,0x4C,0xC1,0x02,0xA9, +0x28,0x51,0xDE,0xA9,0x2C,0x50,0xD0,0x10,0x52,0xB0,0x61,0x80,0xC0,0x04,0x51,0xF5, +0x52,0xF7,0xD0,0xA1,0x02,0x60,0xF0,0x01,0x1F,0x01,0x60,0x90,0x02,0xA9,0x76,0xD4, +0x50,0xD0,0xA9,0x28,0x51,0xD0,0x8F,0xE0,0xFF,0x3F,0x7C,0x56,0xD0,0x1F,0x57,0x30, +0x17,0xF9,0x13,0x03,0x31,0x7A,0x00,0xD0,0x50,0x61,0xC0,0x8F,0x00,0x00,0x40,0x00, +0x50,0xC0,0x04,0x51,0xD0,0xA9,0x28,0x7E,0xC0,0x3C,0x6E,0xD1,0x51,0x8E,0x1B,0xD5, +0x90,0x03,0xA9,0x76,0xD0,0x8F,0x80,0x01,0x00,0xE0,0x61,0xD0,0x61,0x56,0xD3,0x8F, +0x80,0x01,0x00,0xE0,0x56,0x12,0x4A,0x90,0x04,0xA9,0x76,0xC0,0x04,0x51,0xD0,0x8F, +0x00,0xE9,0xFF,0xFF,0x56,0x30,0xCF,0xF8,0x12,0x37,0xD0,0x01,0x61,0xD0,0x61,0x50, +0x90,0x05,0xA9,0x76,0xD1,0x50,0x01,0x12,0x28,0x90,0x06,0xA9,0x76,0xD0,0xA9,0x28, +0x51,0xD4,0x52,0xD0,0x10,0x53,0xD0,0x61,0x50,0xB4,0x50,0xD1,0x50,0x52,0x12,0x11, +0xC0,0x04,0x51,0xC0,0x8F,0x00,0x00,0x40,0x00,0x52,0xF5,0x53,0xE9,0x94,0xA9,0x76, +0x05,0x05,0xE1,0x1F,0xA9,0x4C,0x28,0xBB,0x0F,0xD0,0xA9,0x28,0x51,0xDE,0xA9,0x2C, +0x50,0xD0,0x10,0x52,0x3C,0x80,0x53,0x78,0x10,0x53,0x53,0xD0,0x53,0x81,0xF5,0x52, +0xF3,0xD0,0x80,0xA1,0x04,0xCA,0x8F,0xFF,0xDF,0xFF,0xFF,0xA1,0x04,0xBA,0x0F,0x05, +0x03,0x80,0x1B,0x00,0x8F,0x00,0x00,0x00,0x18,0x00,0x43,0x51,0x42,0x49,0x43,0x5F, +0x70,0x6F,0x77,0x65,0x72,0x75,0x70,0x00,0x02,0x00,0x00,0xE3,0x17,0x69,0x02,0x11, +0x69,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0xD0,0x8F,0x00,0x00,0x08,0x20,0x51,0x90,0x01, +0xA9,0x76,0xD0,0x61,0xA9,0x28,0xD0,0xA9,0x28,0xA9,0x2C,0xAA,0x8F,0x80,0x54,0xA9, +0x28,0xD1,0xA9,0x28,0x8F,0x00,0x80,0x00,0x00,0x12,0x43,0x90,0x02,0xA9,0x76,0xD0, +0xA1,0x04,0xA9,0x28,0xD0,0xA9,0x28,0xA9,0x2C,0x8A,0x8F,0x80,0xA9,0x28,0x12,0x2E, +0xD0,0x8F,0x40,0x1F,0x00,0x20,0x52,0xE1,0x0A,0x61,0x15,0xEF,0x02,0x02,0x9F,0x04, +0x40,0x08,0x20,0x54,0xF0,0x54,0x01,0x03,0x61,0x78,0x01,0x54,0x54,0xC0,0x54,0x52, +0x90,0x03,0xA9,0x76,0x3C,0x62,0xA9,0x28,0x12,0x04,0x94,0xA9,0x76,0x05,0x05,0x05, +0x00,0x00,0x18,0x00,0x81,0x00,0x00,0x00,0x16,0x00,0x43,0x51,0x42,0x49,0x43,0x20, +0x72,0x65,0x67,0x73,0x20,0x00,0x01,0x00,0x90,0x01,0xA9,0x76,0x30,0xAE,0xF7,0xD0, +0x8F,0x10,0x00,0x08,0x20,0x51,0xD0,0x8F,0xFF,0x7F,0x00,0xE0,0x56,0x30,0xA7,0xF7, +0x12,0x4E,0x90,0x02,0xA9,0x76,0xD0,0x9F,0x08,0x00,0x08,0x20,0x50,0x90,0x03,0xA9, +0x76,0xD0,0x9F,0x0C,0x00,0x08,0x20,0x50,0x90,0x04,0xA9,0x76,0xD0,0x9F,0x04,0x00, +0x08,0x20,0x50,0x90,0x05,0xA9,0x76,0xD0,0x8F,0x40,0x1F,0x00,0x20,0x50,0xE1,0x0A, +0x9F,0x00,0x00,0x08,0x20,0x10,0xEF,0x01,0x03,0x9F,0x00,0x00,0x08,0x20,0x51,0x78, +0x01,0x51,0x51,0xC8,0x51,0x50,0xD0,0x50,0xA9,0x28,0xB4,0x60,0x94,0xA9,0x76,0x05, +0x05,0x05,0x0A,0x00,0x2C,0x00,0xDD,0x05,0x18,0x00,0x21,0x00,0x43,0x51,0x42,0x49, +0x43,0x5F,0x6D,0x65,0x6D,0x6F,0x72,0x79,0x20,0x00,0x02,0x04,0x00,0x03,0x06,0x60, +0x00,0x0B,0x06,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x01, +0xA9,0x76,0xD4,0xA9,0x28,0xDA,0x19,0x12,0xD2,0x00,0xA9,0x30,0xF0,0x00,0x1D,0x01, +0x69,0xD0,0xA9,0x24,0x9F,0x10,0x00,0x08,0x20,0x12,0x03,0x31,0x90,0x05,0x90,0x02, +0xA9,0x76,0xD0,0x8F,0x00,0x80,0x08,0x20,0x51,0xD0,0x8F,0x00,0x00,0xF0,0x7F,0x56, +0x16,0xEF,0xF1,0xF6,0xFF,0xFF,0xD0,0x54,0xA9,0x48,0xD0,0x55,0xA9,0x44,0xD5,0x53, +0x12,0x63,0x90,0x03,0xA9,0x76,0xD0,0x8F,0x00,0x80,0x08,0x20,0x51,0xD4,0x52,0xD0, +0x52,0x42,0x61,0xF2,0x8F,0x00,0x20,0x00,0x00,0x52,0xF4,0x90,0x04,0xA9,0x76,0xD0, +0x8F,0x00,0x80,0x08,0x20,0x51,0xD4,0x52,0xD0,0x42,0x61,0xA9,0x44,0xD1,0x52,0xA9, +0x44,0x12,0x2E,0xF2,0x8F,0x00,0x20,0x00,0x00,0x52,0xED,0x90,0x05,0xA9,0x76,0xD0, +0xA9,0x24,0x53,0xD0,0x8F,0x00,0x80,0x08,0x20,0x51,0xD4,0x52,0xD0,0x42,0x63,0xA9, +0x44,0xD1,0x52,0xA9,0x44,0x12,0x0A,0xF2,0x8F,0x00,0x20,0x00,0x00,0x52,0xED,0x11, +0x07,0xD0,0x52,0xA9,0x48,0x31,0x06,0x05,0x90,0x06,0xA9,0x76,0xF0,0x01,0x1D,0x01, +0x69,0xD0,0x8F,0x00,0x00,0x00,0x30,0x53,0xD0,0x8F,0x00,0x80,0x08,0x20,0x51,0xD4, +0x52,0xD0,0xA9,0x1C,0x54,0x16,0xEF,0x4E,0xC4,0x00,0x00,0x01,0x01,0xD1,0x8F,0x00, +0x20,0x00,0x00,0x55,0x14,0x07,0xD0,0x8F,0x00,0x20,0x00,0x00,0x55,0x94,0xA9,0x28, +0x3C,0x8F,0xBD,0xC0,0x9F,0x04,0x00,0x08,0x20,0x95,0x63,0x95,0xA9,0x28,0x12,0x03, +0x31,0xF7,0x00,0xC3,0x8F,0x00,0x00,0x00,0x30,0x53,0x56,0x78,0x8F,0xF7,0x56,0x56, +0x90,0x07,0xA9,0x76,0xD0,0x9F,0x08,0x00,0x08,0x20,0xA9,0x4C,0xD1,0xA9,0x4C,0x56, +0x12,0x93,0x90,0x08,0xA9,0x76,0xD0,0x9F,0x04,0x00,0x08,0x20,0xA9,0x34,0x3C,0x8F, +0xBD,0xC0,0x9F,0x04,0x00,0x08,0x20,0x91,0xA9,0x34,0x8F,0x80,0x13,0x03,0x31,0x7D, +0x04,0xD0,0x8F,0x40,0x1F,0x00,0x20,0x50,0xE1,0x0A,0x9F,0x00,0x00,0x08,0x20,0x14, +0xEF,0x01,0x03,0x9F,0x00,0x00,0x08,0x20,0x50,0x78,0x01,0x50,0x50,0xC8,0x8F,0x40, +0x1F,0x00,0x20,0x50,0x3C,0x8F,0xBD,0xC0,0x9F,0x04,0x00,0x08,0x20,0x94,0xA9,0x29, +0xDA,0x1F,0x12,0x90,0x09,0xA9,0x76,0xB4,0x63,0xB5,0x60,0x90,0x0A,0xA9,0x76,0x95, +0xA9,0x29,0x12,0x66,0x90,0x0B,0xA9,0x76,0xDA,0x19,0x12,0x91,0xA9,0x29,0x01,0x12, +0x59,0x90,0x0C,0xA9,0x76,0xD0,0x9F,0x04,0x00,0x08,0x20,0xA9,0x34,0x91,0xA9,0x34, +0x8F,0x80,0x12,0x46,0xDA,0x1F,0x12,0x94,0xA9,0x29,0x90,0x0D,0xA9,0x76,0x94,0xA3, +0x01,0xB5,0x60,0x90,0x0E,0xA9,0x76,0x95,0xA9,0x29,0x12,0x2E,0x90,0x0F,0xA9,0x76, +0xDA,0x19,0x12,0x91,0xA9,0x29,0x01,0x12,0x21,0x90,0x10,0xA9,0x76,0xD0,0x9F,0x04, +0x00,0x08,0x20,0xA9,0x34,0x3C,0x8F,0xBD,0xC0,0x9F,0x04,0x00,0x08,0x20,0x91,0xA9, +0x34,0x8F,0x88,0x12,0x05,0x11,0x06,0x31,0x03,0xFF,0x31,0xD1,0x03,0xE1,0x52,0x64, +0x09,0xC9,0x8F,0x00,0x00,0x00,0x80,0x52,0x42,0x61,0xC0,0x8F,0x00,0x02,0x00,0x00, +0x53,0xF2,0x55,0x52,0xE2,0x90,0x11,0xA9,0x76,0xF0,0x00,0x1D,0x01,0x69,0xD0,0x8F, +0x00,0x00,0x00,0x30,0x53,0xD4,0x58,0xD0,0x8F,0x00,0x80,0x08,0x20,0x51,0xD4,0x52, +0xD3,0x8F,0x00,0x00,0x00,0x80,0x42,0x61,0x13,0x24,0xCB,0x8F,0xC0,0xFF,0xFF,0xFF, +0x53,0x56,0xDE,0x46,0x63,0x46,0x63,0xDE,0x46,0x63,0xA9,0x48,0xD5,0x46,0x63,0xD0, +0x46,0x68,0xA9,0x44,0xD1,0xA9,0x48,0xA9,0x44,0x13,0x03,0x31,0x02,0x01,0xC0,0x8F, +0x00,0x02,0x00,0x00,0x53,0xC0,0x8F,0x00,0x02,0x00,0x00,0x58,0xF2,0x55,0x52,0xC0, +0x90,0x12,0xA9,0x76,0xD0,0x8F,0x00,0x80,0x08,0x20,0x51,0xD4,0x52,0xD0,0xA9,0x1C, +0x54,0x16,0xEF,0xB2,0xC2,0x00,0x00,0x01,0x01,0xD1,0x55,0x8F,0x00,0x20,0x00,0x00, +0x1B,0x07,0xD0,0x8F,0x00,0x20,0x00,0x00,0x55,0xD0,0x8F,0x00,0x00,0x00,0x30,0x57, +0xD3,0x8F,0x00,0x00,0x00,0x80,0x42,0x61,0x12,0x1A,0xC0,0x8F,0x00,0x02,0x00,0x00, +0x57,0xF2,0x55,0x52,0xEB,0xE0,0x0A,0x9F,0x00,0x00,0x08,0x20,0x20,0x90,0x13,0xA9, +0x76,0x31,0x9C,0x00,0xD0,0x42,0x61,0x58,0xD0,0x52,0xA9,0x2C,0x16,0xEF,0x67,0xC2, +0x00,0x00,0x01,0x01,0xD1,0x8F,0x00,0x20,0x00,0x00,0x55,0x19,0x03,0x31,0xEA,0x02, +0xC3,0x8F,0x00,0x20,0x00,0x00,0x55,0x55,0x78,0x8F,0xF7,0x55,0x55,0xC0,0x8F,0x00, +0x04,0x00,0x00,0x54,0xD0,0x8F,0x00,0x00,0x40,0x00,0x56,0xD4,0x50,0xEA,0x50,0x20, +0x64,0x50,0x12,0x0B,0xD1,0x50,0x8F,0x00,0x02,0x00,0x00,0x18,0x38,0x11,0xEE,0x78, +0x09,0x50,0x50,0xC0,0x56,0x50,0x78,0x8F,0xF7,0x50,0x42,0x61,0xC8,0x8F,0x00,0x00, +0x00,0x80,0x42,0x61,0xD4,0x60,0xD0,0x50,0x67,0x90,0x14,0xA9,0x76,0xD0,0x67,0xA9, +0x44,0xD1,0x50,0xA9,0x44,0x12,0x25,0x90,0x15,0xA9,0x76,0xD0,0x60,0xA9,0x44,0xD1, +0x50,0xA9,0x44,0x12,0x17,0xC0,0x8F,0x00,0x00,0x04,0x00,0x56,0xC0,0x8F,0x40,0x00, +0x00,0x00,0x54,0xF5,0x55,0xA5,0xD0,0x58,0x42,0x61,0x11,0x07,0xD0,0x50,0xA9,0x48, +0x31,0x6B,0x02,0x90,0x16,0xA9,0x76,0xD0,0x9F,0x0C,0x00,0x08,0x20,0x50,0x31,0x59, +0x02,0x01,0x3C,0x8F,0xBD,0xC0,0x9F,0x04,0x00,0x08,0x20,0xD0,0xA9,0x2C,0x52,0x78, +0x09,0x52,0x57,0xC0,0x8F,0x00,0x00,0x00,0x30,0x57,0xD0,0x8F,0x00,0x80,0x08,0x20, +0x51,0xD0,0x42,0x61,0x58,0xD0,0x8F,0x00,0x00,0x06,0x80,0x42,0x61,0xF0,0x01,0x1D, +0x01,0x69,0x94,0xA9,0x28,0xD5,0x67,0xD0,0x58,0x42,0x61,0x95,0xA9,0x28,0x13,0xB0, +0x90,0x18,0xA9,0x76,0xD0,0x8F,0x00,0x00,0x06,0x00,0xA9,0x48,0xD0,0x9F,0x0C,0x00, +0x08,0x20,0xA9,0x40,0xD1,0xA9,0x40,0xA9,0x48,0x12,0x95,0x90,0x19,0xA9,0x76,0xF0, +0x00,0x1D,0x01,0x69,0xD0,0x9F,0x04,0x00,0x08,0x20,0xA9,0x34,0xD0,0x01,0xA9,0x48, +0x91,0xA9,0x34,0x01,0x12,0x17,0xD0,0x01,0x9F,0x04,0x00,0x08,0x20,0x90,0x1A,0xA9, +0x76,0xD0,0x9F,0x04,0x00,0x08,0x20,0xA9,0x44,0xE9,0xA9,0x44,0x03,0x31,0xCE,0x01, +0x31,0xC7,0x01,0x01,0xCB,0x8F,0x00,0x00,0x00,0x30,0x57,0x58,0xD0,0x03,0x55,0xD2, +0x00,0x9F,0x40,0x01,0x08,0x20,0xD0,0x8F,0x44,0x01,0x08,0x20,0x53,0xCA,0x8F,0xFF, +0x1F,0x00,0x00,0x63,0xD0,0x63,0x54,0x90,0x8F,0xBC,0x54,0xD0,0x54,0x63,0xD0,0x58, +0xA9,0x30,0xD0,0x55,0x68,0xF0,0x01,0x1D,0x01,0x69,0xD5,0x68,0xF0,0x00,0x1D,0x01, +0x69,0xD0,0xA3,0xFC,0x54,0xED,0x1D,0x03,0x54,0x04,0x13,0x0D,0x78,0x01,0x55,0x55, +0x12,0xBD,0x90,0x1C,0xA9,0x76,0x31,0x75,0x01,0x90,0x1D,0xA9,0x76,0xD2,0x00,0x9F, +0x40,0x01,0x08,0x20,0xF0,0x01,0x1D,0x01,0x69,0xD5,0x67,0xF0,0x00,0x1D,0x01,0x69, +0xCA,0x8F,0xFF,0x1F,0x00,0x00,0x9F,0x44,0x01,0x08,0x20,0xD4,0xB9,0x30,0xD2,0x00, +0xA9,0x30,0xD0,0x9F,0x04,0x00,0x08,0x20,0xA9,0x34,0xD0,0x9F,0x40,0x01,0x08,0x20, +0xA9,0x3C,0xD4,0xA9,0x38,0xE0,0x0A,0x9F,0x00,0x00,0x08,0x20,0x08,0x3C,0x9F,0x40, +0x1F,0x00,0x20,0xA9,0x38,0xD0,0x10,0x9F,0x04,0x00,0x08,0x20,0xD2,0x00,0x9F,0x40, +0x01,0x08,0x20,0x90,0x1E,0xA9,0x76,0x91,0xA9,0x34,0x10,0x12,0x34,0x90,0x1F,0xA9, +0x76,0xE0,0x0A,0x9F,0x00,0x00,0x08,0x20,0x05,0xE1,0x0F,0xA9,0x38,0x23,0xCB,0x8F, +0xFF,0x01,0x00,0xE0,0x58,0xA9,0x48,0xC8,0x8F,0x00,0x01,0x00,0x80,0xA9,0x48,0xF0, +0xA9,0x3C,0x00,0x07,0xA9,0x48,0x90,0x20,0xA9,0x76,0xD1,0xA9,0x3C,0xA9,0x48,0x13, +0x03,0x31,0xDA,0x00,0x90,0x21,0xA9,0x76,0xD0,0x8F,0x40,0x1F,0x00,0x20,0x57,0xE1, +0x0A,0x9F,0x00,0x00,0x08,0x20,0x10,0xEF,0x01,0x03,0x9F,0x00,0x00,0x08,0x20,0x50, +0x78,0x01,0x50,0x50,0xC8,0x50,0x57,0xD0,0x8F,0x00,0x80,0x08,0x20,0x58,0xD0,0x8F, +0x00,0x00,0x00,0x30,0x56,0xD0,0x8F,0x00,0x20,0x00,0x00,0x54,0xD4,0x51,0xB0,0x8F, +0x00,0x40,0x67,0xD0,0x88,0x52,0xE1,0x1F,0x52,0x1A,0xEF,0x00,0x14,0x52,0x53,0x78, +0x09,0x53,0x53,0xD0,0x53,0x55,0xC8,0x56,0x53,0xD0,0x51,0x65,0xB5,0x63,0xF2,0x12, +0x51,0xE1,0x11,0x09,0xF5,0x54,0xDC,0x90,0x22,0xA9,0x76,0x11,0x66,0xD0,0x8F,0x00, +0x80,0x08,0x20,0x58,0xD0,0xA9,0x24,0x55,0x90,0x01,0xA9,0x2A,0xD4,0x51,0xD5,0x85, +0xD0,0x88,0x52,0xE1,0x1F,0x52,0xF7,0xD5,0x51,0x13,0x03,0xD7,0xA5,0xFC,0xF2,0x12, +0x51,0xEC,0xD0,0xA9,0x24,0x55,0xD4,0x51,0xD0,0x85,0x52,0xE1,0x1F,0x52,0xF9,0xD1, +0x51,0x02,0x1F,0x32,0xEF,0x00,0x14,0x52,0x53,0xD6,0x53,0x78,0x09,0x53,0x53,0xC8, +0x56,0x53,0xD0,0x63,0xA9,0x44,0xD0,0x51,0xA9,0x48,0xC3,0x01,0x51,0x50,0x90,0x23, +0xA9,0x76,0xD1,0x50,0xA9,0x44,0x13,0x0B,0x90,0x24,0xA9,0x76,0xD1,0xA9,0x44,0xA9, +0x48,0x13,0x03,0x31,0x08,0x00,0xF2,0x12,0x51,0xBE,0x94,0xA9,0x76,0x05,0x05,0xCA, +0x8F,0xFF,0x1F,0x00,0x00,0x9F,0x44,0x01,0x08,0x20,0xE0,0x1F,0xA9,0x30,0x03,0xD4, +0xB9,0x30,0xE9,0xA9,0x2A,0x26,0xBB,0x07,0xD0,0x8F,0x00,0x80,0x08,0x20,0x50,0xD4, +0x51,0xD0,0x41,0x60,0x52,0xE1,0x1F,0x52,0x09,0xF0,0x51,0x00,0x14,0x52,0xD0,0x52, +0x41,0x60,0xF2,0x8F,0x00,0x20,0x00,0x00,0x51,0xE7,0xBA,0x07,0x05,0x96,0xA9,0x28, +0xC0,0x02,0xBE,0x04,0x05,0x96,0xA9,0x29,0x05,0x00,0x00,0x5E,0x00,0x15,0x03,0x1A, +0x00,0x23,0x00,0x43,0x6F,0x6E,0x73,0x6F,0x6C,0x65,0x20,0x73,0x65,0x72,0x69,0x61, +0x6C,0x20,0x00,0x02,0xF8,0x00,0x2B,0x03,0xFC,0x00,0x23,0x03,0x08,0x03,0x27,0x00, +0x02,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x03,0x32,0x00, +0x02,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x73,0x74,0x61,0x72,0x74,0x5F,0x62,0x61,0x75,0x64,0x00,0x65,0x6E, +0x64,0x5F,0x62,0x61,0x75,0x64,0x00,0xF0,0x02,0x02,0x02,0x69,0xD0,0x8F,0xA0,0x03, +0x02,0x00,0xA9,0x5C,0x16,0xEF,0x5D,0xF0,0xFF,0xFF,0xD0,0xA9,0x28,0xA9,0x34,0x78, +0xA9,0x34,0x8F,0x2C,0x01,0x00,0x00,0x50,0xD4,0x52,0xD0,0x8F,0x20,0x4E,0x00,0x00, +0x51,0xD6,0x52,0xC2,0x50,0x51,0x14,0xF9,0x78,0x0A,0x52,0x52,0xD0,0x52,0xA9,0x44, +0x78,0x01,0x52,0xA9,0x5C,0x16,0xEF,0x2C,0xF0,0xFF,0xFF,0x90,0x01,0xA9,0x76,0xF0, +0xA9,0x34,0x0C,0x03,0x9F,0x10,0x00,0x14,0x20,0x90,0x02,0xA9,0x76,0xDB,0x20,0x50, +0xDB,0x21,0x50,0xDB,0x22,0x50,0xDB,0x23,0x50,0x31,0x0B,0x02,0x01,0x16,0xEF,0x5A, +0xF0,0xFF,0xFF,0xDA,0x04,0x22,0xD0,0x52,0xA9,0x5C,0x16,0xEF,0xF7,0xEF,0xFF,0xFF, +0xDB,0x21,0xA9,0x30,0x94,0x51,0x90,0x04,0xA9,0x76,0xDB,0x22,0xA9,0x30,0xE1,0x07, +0xA9,0x30,0x36,0x90,0x05,0xA9,0x76,0xDA,0x51,0x23,0xD0,0x52,0xA9,0x5C,0x16,0xEF, +0xD3,0xEF,0xFF,0xFF,0xDB,0x20,0xA9,0x30,0xE1,0x07,0xA9,0x30,0x1C,0xDB,0x21,0xA9, +0x30,0xB3,0x8F,0x00,0xE8,0xA9,0x30,0x12,0x10,0x91,0x51,0xA9,0x30,0x12,0x0A,0x95, +0x51,0x12,0x09,0x90,0x8F,0xFF,0x51,0x11,0xBD,0x31,0xD9,0x01,0x90,0x06,0xA9,0x76, +0xDB,0x22,0xA9,0x30,0xE1,0x07,0xA9,0x30,0xF0,0x90,0x07,0xA9,0x76,0xDA,0x8F,0xAA, +0x00,0x00,0x00,0x23,0xD0,0x52,0xA9,0x5C,0x16,0xEF,0x89,0xEF,0xFF,0xFF,0xDB,0x20, +0xA9,0x30,0xE1,0x07,0xA9,0x30,0xD2,0x90,0x08,0xA9,0x76,0xDA,0x8F,0xA9,0x00,0x00, +0x00,0x23,0xD0,0x52,0xA9,0x5C,0x16,0xEF,0x6B,0xEF,0xFF,0xFF,0xDB,0x20,0xA9,0x30, +0xE1,0x07,0xA9,0x30,0xB4,0x90,0x09,0xA9,0x76,0xDA,0x8F,0x55,0x00,0x00,0x00,0x23, +0xD0,0x52,0xA9,0x5C,0x16,0xEF,0x4D,0xEF,0xFF,0xFF,0xDB,0x20,0xA9,0x30,0xE1,0x07, +0xA9,0x30,0x65,0x90,0x0A,0xA9,0x76,0xDB,0x21,0xA9,0x30,0x91,0x8F,0xAA,0xA9,0x30, +0x12,0x56,0x90,0x0B,0xA9,0x76,0xB3,0x8F,0x00,0xC0,0xA9,0x30,0x12,0x4A,0x90,0x0C, +0xA9,0x76,0xDB,0x21,0xA9,0x30,0x91,0x8F,0x55,0xA9,0x30,0x12,0x3B,0x90,0x0D,0xA9, +0x76,0x91,0x8F,0xC0,0xA9,0x31,0x12,0x30,0x90,0x0E,0xA9,0x76,0x16,0xEF,0x53,0xEF, +0xFF,0xFF,0xDA,0x14,0x12,0xD4,0xA9,0x38,0xD4,0xA9,0x40,0xDA,0x8F,0x44,0x00,0x00, +0x00,0x22,0xDB,0x22,0xA9,0x30,0x93,0x8F,0x40,0xA9,0x30,0x13,0x0B,0x90,0x0F,0xA9, +0x76,0xD5,0xA9,0x38,0x12,0x02,0x11,0x03,0x31,0x0A,0x01,0x90,0x10,0xA9,0x76,0xDA, +0x13,0x12,0xD1,0x01,0xA9,0x38,0x12,0xF0,0x90,0x11,0xA9,0x76,0xD1,0x14,0xA9,0x40, +0x12,0xE6,0x90,0x12,0xA9,0x76,0xDA,0x04,0x22,0xDA,0x13,0x12,0xDA,0x8F,0x40,0x00, +0x00,0x00,0x20,0xDB,0x20,0xA9,0x30,0x93,0x8F,0x40,0xA9,0x30,0x13,0xCA,0x90,0x13, +0xA9,0x76,0xD4,0xA9,0x3C,0xD4,0xA9,0x40,0xDA,0x8F,0xAA,0x00,0x00,0x00,0x23,0xD0, +0x52,0xA9,0x5C,0x16,0xEF,0x8E,0xEE,0xFF,0xFF,0xDB,0x20,0xA9,0x30,0xE1,0x07,0xA9, +0x30,0xA6,0x90,0x14,0xA9,0x76,0xD1,0x01,0xA9,0x3C,0x12,0x9C,0x90,0x15,0xA9,0x76, +0xD1,0x14,0xA9,0x40,0x12,0x92,0x90,0x16,0xA9,0x76,0xDB,0x21,0xA9,0x30,0x91,0x8F, +0xAA,0xA9,0x30,0x12,0x0D,0x90,0x17,0xA9,0x76,0xDB,0x20,0xA9,0x30,0xE1,0x07,0xA9, +0x30,0x03,0x31,0x80,0x00,0xDE,0xAF,0xFD,0x51,0xD1,0x51,0x8F,0x00,0x00,0x04,0x20, +0x1F,0x41,0xED,0x10,0x03,0x9F,0x10,0x00,0x14,0x20,0x03,0x12,0x0B,0xD1,0x51,0x8F, +0xFF,0xFF,0x04,0x20,0x1A,0x2D,0x11,0x09,0xD1,0x51,0x8F,0xFF,0xFF,0x05,0x20,0x1A, +0x22,0x90,0x18,0xA9,0x76,0xDA,0x05,0x22,0x78,0x01,0x52,0xA9,0x5C,0x16,0xEF,0x14, +0xEE,0xFF,0xFF,0x90,0x19,0xA9,0x76,0xDB,0x21,0x50,0xD1,0x8F,0x00,0xA8,0x00,0x00, +0x50,0x12,0x32,0x90,0x1A,0xA9,0x76,0xDA,0x00,0x22,0xDA,0x00,0x20,0xF3,0xA9,0x2C, +0xA9,0x34,0x02,0x11,0x03,0x31,0x97,0xFD,0xF0,0x03,0x02,0x02,0x69,0xEF,0x04,0x03, +0x9F,0x04,0x40,0x08,0x20,0xA9,0x34,0xF0,0xA9,0x34,0x0C,0x03,0x9F,0x10,0x00,0x14, +0x20,0x94,0xA9,0x76,0x05,0xDA,0x00,0x22,0xDA,0x00,0x20,0xEF,0x04,0x03,0x9F,0x04, +0x40,0x08,0x20,0x7E,0xF0,0x8E,0x0C,0x03,0x9F,0x10,0x00,0x14,0x20,0x05,0x78,0x01, +0xA9,0x44,0xA9,0x5C,0xD6,0xA9,0x5C,0x16,0xEF,0xAA,0xED,0xFF,0xFF,0xDA,0x00,0x22, +0xDA,0x00,0x20,0xEF,0x04,0x03,0x9F,0x04,0x40,0x08,0x20,0x7E,0xF0,0x8E,0x0C,0x03, +0x9F,0x10,0x00,0x14,0x20,0x05,0xD6,0xA9,0x38,0xDB,0x12,0xA9,0x40,0x05,0xD6,0xA9, +0x3C,0xDB,0x12,0xA9,0x40,0x05,0x00,0x00,0x02,0x00,0x68,0x00,0x76,0x01,0x18,0x00, +0x1D,0x00,0x63,0x6F,0x6E,0x73,0x6F,0x6C,0x65,0x20,0x51,0x44,0x53,0x53,0x20,0x00, +0x01,0x04,0x00,0x5F,0x01,0x08,0x02,0x22,0x00,0x02,0x00,0x00,0x00,0x00,0x01,0x00, +0x00,0x00,0x01,0x33,0x00,0x02,0x00,0x00,0x00,0x00,0x01,0x3F,0x00,0x02,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6D,0x61,0x72,0x6B,0x5F,0x6E,0x6F,0x74,0x5F, +0x70,0x72,0x65,0x73,0x65,0x6E,0x74,0x00,0x73,0x65,0x6C,0x66,0x74,0x65,0x73,0x74, +0x5F,0x72,0x30,0x00,0x73,0x65,0x6C,0x66,0x74,0x65,0x73,0x74,0x5F,0x72,0x31,0x00, +0x90,0x01,0xA9,0x76,0xF0,0x01,0x08,0x02,0x69,0xED,0x02,0x02,0x9F,0x04,0x40,0x08, +0x20,0x00,0x12,0x05,0xD5,0xA9,0x28,0x13,0x03,0x31,0xED,0x00,0xF0,0x01,0x1D,0x01, +0x69,0xDE,0xEF,0x0B,0x00,0x00,0x00,0xA9,0x44,0xD4,0xA9,0x3C,0xB5,0x9F,0x00,0x1F, +0x00,0x20,0xD5,0xA9,0x3C,0x13,0x03,0x31,0xCF,0x00,0xF0,0x00,0x1D,0x01,0x69,0xD5, +0xA9,0x1C,0x12,0x07,0x90,0x02,0xA9,0x76,0x31,0xC2,0x00,0xC3,0x8F,0x00,0x10,0x00, +0x00,0xA9,0x1C,0x56,0x90,0x03,0xA9,0x76,0xEF,0x09,0x11,0x56,0x55,0xED,0x55,0x08, +0xB9,0x1C,0x8F,0xFF,0x00,0x00,0x00,0x13,0x10,0xC2,0x8F,0x00,0x02,0x00,0x00,0x56, +0x1A,0xE6,0x90,0x04,0xA9,0x76,0x31,0x94,0x00,0xD0,0x56,0xA9,0x40,0xD0,0xA9,0x24, +0x9F,0x10,0x00,0x08,0x20,0xD0,0xA9,0x40,0xAB,0x68,0xB0,0x8F,0x00,0x04,0xAB,0x68, +0x90,0x05,0xA9,0x76,0xD0,0x8F,0x00,0xFE,0x08,0x20,0x50,0xD4,0x80,0xD1,0x50,0x8F, +0x00,0x00,0x09,0x20,0x1F,0xF5,0xD0,0x8F,0x40,0x42,0x0F,0x00,0xA9,0x5C,0x90,0x06, +0xA9,0x76,0xD0,0x8F,0x00,0x1F,0x00,0x20,0xAB,0x3C,0xD0,0x8F,0x00,0x00,0x3F,0x30, +0xAB,0x58,0xB0,0x3F,0xBB,0x3C,0xD0,0x5E,0xA9,0x34,0xC1,0x8F,0x00,0x08,0x00,0x00, +0xAB,0x68,0x5E,0xDB,0x11,0x7E,0xDA,0xA9,0x50,0x11,0x16,0xEF,0x38,0x78,0x00,0x00, +0xDA,0x8E,0x11,0xD0,0xA9,0x34,0x5E,0x7D,0x50,0xA9,0x2C,0x90,0x07,0xA9,0x76,0xD1, +0x02,0x50,0x13,0x19,0xF0,0x03,0x08,0x02,0x69,0xD1,0x01,0x50,0x13,0x0B,0x90,0x08, +0xA9,0x76,0xF0,0x02,0x08,0x02,0x69,0x11,0x04,0x94,0xA9,0x76,0x05,0x05,0x05,0xD6, +0xA9,0x3C,0xD0,0xA9,0x44,0xBE,0x04,0x05,0x02,0x00,0x5F,0x00,0x5C,0x01,0x19,0x00, +0x1E,0x00,0x51,0x44,0x53,0x53,0x20,0x73,0x65,0x6C,0x66,0x2D,0x74,0x65,0x73,0x74, +0x00,0x01,0x04,0x00,0x44,0x01,0x09,0x01,0x1F,0x00,0x02,0x00,0x00,0x00,0x00,0x01, +0x29,0x00,0x02,0x00,0x00,0x00,0x00,0x01,0x35,0x00,0x02,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x69,0x6E,0x70,0x75,0x74,0x5F,0x63,0x73,0x72,0x00,0x73, +0x65,0x6C,0x66,0x74,0x65,0x73,0x74,0x5F,0x72,0x30,0x00,0x73,0x65,0x6C,0x66,0x74, +0x65,0x73,0x74,0x5F,0x72,0x31,0x00,0x90,0x01,0xA9,0x76,0xD1,0x07,0xA9,0x28,0x18, +0x10,0xC3,0x8F,0x00,0x1F,0x00,0x20,0xA9,0x28,0x50,0x78,0x8F,0xFF,0x50,0x50,0x11, +0x14,0xD0,0xA9,0x28,0x50,0x78,0x01,0xA9,0x28,0xA9,0x28,0xC1,0xA9,0x28,0x8F,0x00, +0x1F,0x00,0x20,0xA9,0x28,0x90,0x02,0xA9,0x76,0x16,0xEF,0xB6,0xEB,0xFF,0xFF,0xD4, +0xA9,0x40,0xDE,0xEF,0x05,0x00,0x00,0x00,0xA9,0x48,0xB5,0xB9,0x28,0xD5,0xA9,0x40, +0x13,0x03,0x31,0xAE,0x00,0x78,0x09,0x50,0x51,0xC3,0x51,0x8F,0x00,0xFE,0x08,0x20, +0x51,0xD4,0x52,0xD4,0x42,0x61,0xF2,0x8F,0x80,0x00,0x00,0x00,0x52,0xF5,0x90,0x03, +0xA9,0x76,0xA3,0x50,0x3F,0xB9,0x28,0x78,0x10,0x50,0x51,0xC3,0x51,0x8F,0x00,0x00, +0x3F,0x30,0xA9,0x34,0xD5,0xA9,0x1C,0x12,0x07,0x90,0x04,0xA9,0x76,0x31,0x73,0x00, +0x16,0xEF,0xDF,0xB8,0x00,0x00,0x01,0x01,0x01,0x91,0x76,0x8F,0xFF,0x13,0x0D,0xD1, +0x56,0xA9,0x1C,0x1A,0xF4,0x90,0x05,0xA9,0x76,0x31,0x57,0x00,0xC2,0xA9,0x1C,0x56, +0x78,0x0C,0x56,0x56,0x28,0x8F,0x00,0x01,0x6B,0x66,0xDD,0x5B,0xD0,0x56,0x5B,0xA1, +0x8F,0x00,0x06,0x56,0xAB,0x68,0x90,0x06,0xA9,0x76,0xD0,0xA9,0x28,0xAB,0x3C,0xD0, +0xA9,0x34,0xAB,0x58,0xD0,0x5E,0xA9,0x38,0xC1,0x8F,0x00,0x0A,0x00,0x00,0xAB,0x68, +0x5E,0xDB,0x11,0x7E,0xDA,0xA9,0x50,0x11,0x16,0xEF,0xBA,0x76,0x00,0x00,0xDA,0x8E, +0x11,0xD0,0xA9,0x38,0x5E,0xD0,0x8E,0x5B,0x7D,0x50,0xA9,0x2C,0xD5,0x50,0x13,0x03, +0x94,0xA9,0x76,0x05,0x05,0xD6,0xA9,0x40,0xD0,0xA9,0x48,0xBE,0x04,0x05,0x00,0x00, +0x1F,0x00,0xCD,0x01,0x10,0x00,0x19,0x00,0x43,0x46,0x50,0x41,0x09,0x00,0x02,0x34, +0x00,0xC2,0x01,0x18,0x00,0xD4,0x01,0x05,0x00,0x00,0x00,0x00,0x00,0x90,0x01,0xA9, +0x76,0xD4,0x51,0x50,0x8F,0x7A,0x45,0x00,0x00,0x50,0xD4,0xA9,0x28,0x40,0x50,0xA9, +0x28,0xD6,0x51,0xD1,0x04,0x51,0x15,0x13,0x4F,0x8F,0x7A,0xC5,0x00,0x00,0x8F,0x7A, +0xC5,0x00,0x00,0x50,0xE7,0xFF,0x53,0xA9,0x28,0x13,0x03,0x31,0x56,0x00,0x90,0x02, +0xA9,0x76,0x4A,0x8F,0x8F,0x40,0xFF,0xFF,0xA9,0x2C,0xD1,0x01,0xA9,0x2C,0x12,0x44, +0x90,0x03,0xA9,0x76,0x4A,0x8F,0x8F,0xC0,0xFF,0xFF,0xA9,0x2C,0xD1,0x8F,0xFF,0xFF, +0xFF,0xFF,0xA9,0x2C,0x12,0x2E,0x90,0x04,0xA9,0x76,0x4E,0x8F,0x78,0x56,0x34,0x12, +0xA9,0x28,0xD1,0x8F,0x91,0x4E,0xB4,0xA2,0xA9,0x28,0x12,0x18,0x90,0x05,0xA9,0x76, +0x4E,0x8F,0x87,0xA9,0xCB,0xED,0xA9,0x2C,0xD1,0x8F,0x91,0xCE,0xB4,0xA2,0xA9,0x2C, +0x12,0x02,0x11,0x03,0x31,0x23,0x01,0x90,0x06,0xA9,0x76,0xFD,0x4E,0x8F,0x87,0xA9, +0xCB,0xED,0xA9,0x28,0xD1,0x8F,0xD2,0xC1,0x56,0x34,0xA9,0x28,0x12,0xE6,0xD1,0x8F, +0x00,0x79,0x00,0x00,0xA9,0x2C,0x12,0xDC,0x90,0x07,0xA9,0x76,0x6E,0x8F,0x89,0x67, +0x45,0x23,0xA9,0x28,0xD1,0x8F,0x0D,0x4F,0x9E,0x15,0xA9,0x28,0x12,0xC6,0xD1,0x8F, +0x00,0x24,0x00,0x00,0xA9,0x2C,0x12,0xBC,0x90,0x08,0xA9,0x76,0x72,0xA9,0x28,0xA9, +0x28,0xD1,0x8F,0x0D,0xCF,0x9E,0x15,0xA9,0x28,0x12,0xA9,0xD1,0x8F,0x00,0x24,0x00, +0x00,0xA9,0x2C,0x12,0x9F,0x90,0x09,0xA9,0x76,0x4E,0x8F,0x78,0x56,0x34,0x12,0xA9, +0x28,0xD2,0xA9,0x28,0xA9,0x2C,0x47,0xA9,0x2C,0xA9,0x28,0xA9,0x28,0xD1,0x8F,0x9C, +0xDD,0x18,0x69,0xA9,0x28,0x12,0x0D,0x90,0x0A,0xA9,0x76,0x42,0xA9,0x28,0xA9,0x28, +0x12,0x02,0x11,0x03,0x31,0x93,0x00,0x90,0x0B,0xA9,0x76,0xD4,0xA9,0x34,0x16,0xEF, +0xA1,0xE9,0xFF,0xFF,0x50,0x8F,0x00,0x80,0x00,0x00,0xA9,0x28,0xD5,0xA9,0x34,0x13, +0xE3,0x90,0x0C,0xA9,0x76,0xD0,0x0A,0xA9,0x38,0xD4,0xA9,0x30,0xB8,0x8F,0x40,0x00, +0x47,0x22,0x8F,0x80,0x00,0x00,0x00,0xA9,0x28,0xD5,0xA9,0x30,0x13,0xC6,0x90,0x0D, +0xA9,0x76,0xD4,0xA9,0x30,0xB9,0x8F,0x40,0x00,0xD0,0x01,0xA9,0x28,0x47,0x22,0x8F, +0x80,0x00,0x00,0x00,0xA9,0x28,0xD5,0xA9,0x30,0x12,0xA9,0x90,0x0E,0xA9,0x76,0xD5, +0xA9,0x28,0x12,0xA0,0x90,0x0F,0xA9,0x76,0xD0,0x08,0xA9,0x38,0xD4,0xA9,0x30,0x45, +0x22,0x8F,0x80,0x7F,0x00,0x00,0xA9,0x28,0xD5,0xA9,0x30,0x13,0x1D,0x90,0x10,0xA9, +0x76,0xD0,0x09,0xA9,0x38,0xD4,0xA9,0x30,0x47,0x8F,0x00,0x00,0x00,0x00,0x08,0xA9, +0x28,0xD5,0xA9,0x30,0x13,0x04,0x94,0xA9,0x76,0x05,0x05,0xB9,0x8F,0x40,0x00,0x05, +0xD0,0xAE,0x04,0x50,0xD1,0xA9,0x38,0x70,0x12,0x03,0xD6,0xA9,0x30,0xC0,0x09,0xBE, +0x04,0x05,0xD6,0xA9,0x34,0xC0,0x08,0xBE,0x04,0x05,0x00,0x00,0x5C,0x00,0x81,0x02, +0x16,0x00,0x1F,0x00,0x50,0x72,0x6F,0x67,0x20,0x74,0x69,0x6D,0x65,0x72,0x20,0x00, +0x02,0x78,0x00,0x99,0x02,0x7C,0x00,0xA2,0x02,0x05,0x03,0x24,0x00,0x02,0x00,0x00, +0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x30,0x00,0x02,0x01,0x00, +0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x77,0x68,0x69, +0x63,0x68,0x5F,0x74,0x69,0x6D,0x65,0x72,0x00,0x77,0x61,0x69,0x74,0x5F,0x74,0x69, +0x6D,0x65,0x5F,0x75,0x73,0x00,0x90,0x01,0xA9,0x76,0xC1,0xA9,0x28,0x0A,0xA9,0x34, +0xC0,0xA9,0x28,0xA9,0x34,0xF0,0x02,0xA9,0x34,0x02,0x69,0x9C,0x04,0xA9,0x28,0x51, +0xC1,0x51,0x8F,0x00,0x01,0x14,0x20,0x58,0x9A,0x8F,0x78,0xA9,0x38,0xD5,0xA9,0x28, +0x13,0x05,0x9A,0x8F,0x7C,0xA9,0x38,0x90,0x02,0xA9,0x76,0xD0,0x8F,0x80,0x00,0x00, +0x80,0x68,0xD5,0x68,0x12,0x56,0x90,0x03,0xA9,0x76,0xD0,0x58,0x51,0xD0,0x8F,0xBB, +0xFF,0xFF,0xFF,0x57,0xD0,0x8F,0x0A,0xFF,0xFF,0x7F,0x56,0x16,0xEF,0x58,0xE8,0xFF, +0xFF,0x12,0x39,0x90,0x04,0xA9,0x76,0xDE,0xA8,0x08,0x51,0x16,0xEF,0x44,0xE8,0xFF, +0xFF,0x12,0x29,0xD2,0xA9,0x28,0x52,0xEF,0x00,0x01,0x52,0x52,0x9C,0x04,0x52,0x52, +0xC0,0x8F,0x00,0x01,0x14,0x20,0x52,0x90,0x05,0xA9,0x76,0xD2,0x00,0xA8,0x08,0xD0, +0xA8,0x08,0x50,0xD1,0x50,0x8F,0xFF,0xFF,0xFF,0xFF,0x13,0x03,0x31,0x8B,0x01,0x90, +0x06,0xA9,0x76,0xD4,0xA2,0x08,0xD0,0xA8,0x08,0x50,0xD1,0x50,0x8F,0xFF,0xFF,0xFF, +0xFF,0x12,0xE9,0xD4,0xA8,0x08,0x90,0x07,0xA9,0x76,0xDE,0xA8,0x0C,0x51,0xD2,0x8F, +0xFC,0x03,0x00,0x00,0x56,0x16,0xEF,0xEC,0xE7,0xFF,0xFF,0x12,0xCF,0xD2,0xA9,0x28, +0x52,0xEF,0x00,0x01,0x52,0x52,0x9C,0x04,0x52,0x52,0xC0,0x8F,0x00,0x01,0x14,0x20, +0x52,0x90,0x08,0xA9,0x76,0x3C,0x8F,0xFC,0x03,0xA8,0x0C,0xD0,0xA8,0x0C,0x50,0xD1, +0x50,0x8F,0xFC,0x03,0x00,0x00,0x12,0xA4,0x90,0x09,0xA9,0x76,0xD4,0xA2,0x0C,0xD0, +0xA8,0x0C,0x50,0xD1,0x50,0x8F,0xFC,0x03,0x00,0x00,0x12,0x50,0xD4,0xA8,0x0C,0x90, +0x0A,0xA9,0x76,0xD4,0xA8,0x08,0xC8,0x10,0x68,0xD5,0xA8,0x04,0x12,0x3E,0x90,0x0B, +0xA9,0x76,0xCE,0x02,0xA8,0x08,0xC8,0x10,0x68,0xD1,0x8F,0xFE,0xFF,0xFF,0xFF,0xA8, +0x04,0x12,0x29,0x90,0x0C,0xA9,0x76,0xC8,0x20,0x68,0xD1,0x8F,0xFF,0xFF,0xFF,0xFF, +0xA8,0x04,0x12,0x18,0x90,0x0D,0xA9,0x76,0xD5,0x68,0x12,0x10,0x90,0x0E,0xA9,0x76, +0xC8,0x20,0x68,0xD1,0x8F,0x80,0x00,0x00,0x00,0x68,0x13,0x03,0x31,0xCB,0x00,0x90, +0x0F,0xA9,0x76,0xCE,0x01,0xA8,0x08,0xD0,0x10,0x68,0xD0,0x20,0x68,0xE1,0x1F,0x68, +0xEB,0x31,0xAC,0x00,0x01,0xDA,0x0D,0x12,0xD4,0xA9,0x30,0xD0,0xA9,0x38,0xA8,0x0C, +0xCE,0x8F,0xF4,0x01,0x00,0x00,0xA8,0x08,0xD0,0x8F,0xF4,0x01,0x00,0x00,0xA9,0x5C, +0x16,0xEF,0x0F,0xE7,0xFF,0xFF,0xD0,0x8F,0xD1,0x00,0x00,0x80,0x68,0x16,0xEF,0xB4, +0xE6,0xFF,0xFF,0xD1,0x01,0xA9,0x30,0x13,0x03,0x31,0xB0,0xFF,0x90,0x11,0xA9,0x76, +0xD4,0xA9,0x30,0xD0,0x8F,0xF4,0x01,0x00,0x00,0xA9,0x5C,0xD0,0x68,0x68,0x16,0xEF, +0x93,0xE6,0xFF,0xFF,0xD1,0x01,0xA9,0x30,0x12,0x60,0x90,0x12,0xA9,0x76,0xD1,0x8F, +0xC1,0x00,0x00,0x00,0x68,0x12,0x53,0x90,0x13,0xA9,0x76,0xD0,0xA8,0x04,0x53,0x14, +0x49,0xD1,0x8F,0x0C,0xFE,0xFF,0xFF,0x53,0x14,0x40,0x90,0x14,0xA9,0x76,0xD4,0xA9, +0x30,0xD0,0xA9,0x38,0xA8,0x0C,0xCE,0xA9,0x2C,0xA8,0x08,0xD0,0xA9,0x2C,0xA9,0x5C, +0xD0,0x8F,0xD5,0x00,0x00,0x80,0x68,0x16,0xEF,0x4A,0xE6,0xFF,0xFF,0xD1,0x01,0xA9, +0x30,0x12,0x17,0x90,0x15,0xA9,0x76,0xD1,0x8F,0xC4,0x00,0x00,0x00,0x68,0x12,0x0A, +0xF0,0x03,0xA9,0x34,0x02,0x69,0x94,0xA9,0x76,0x05,0x05,0x9C,0x04,0xA9,0x28,0x51, +0xC1,0x51,0x8F,0x00,0x01,0x14,0x20,0x58,0xD4,0xA8,0x08,0xD0,0x8F,0x90,0x00,0x00, +0x80,0x68,0xD0,0x8F,0x78,0x00,0x00,0x00,0x9F,0x0C,0x01,0x14,0x20,0xD0,0x8F,0x7C, +0x00,0x00,0x00,0x9F,0x1C,0x01,0x14,0x20,0x05,0xD5,0xA9,0x28,0x12,0x0D,0xD6,0xA9, +0x30,0x05,0xD5,0xA9,0x28,0x13,0x04,0xD6,0xA9,0x30,0x05,0xD0,0x3F,0xA9,0x30,0x05, +0x03,0x00,0x58,0x00,0x9F,0x01,0x00,0x00,0x15,0x00,0x54,0x4F,0x59,0x20,0x63,0x6C, +0x6F,0x63,0x6B,0x20,0x00,0x05,0x03,0x24,0x00,0x02,0x01,0x00,0x00,0x00,0xFF,0xFF, +0xFF,0xFF,0x14,0x00,0x00,0x00,0x03,0x39,0x00,0x02,0x02,0x00,0x00,0x00,0xE8,0x03, +0x00,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x72,0x65,0x70,0x65,0x61,0x74,0x5F, +0x74,0x65,0x73,0x74,0x5F,0x32,0x35,0x30,0x6D,0x73,0x5F,0x65,0x61,0x00,0x54,0x6F, +0x6C,0x65,0x72,0x61,0x6E,0x63,0x65,0x00,0x90,0x01,0xA9,0x76,0xF0,0x02,0x04,0x02, +0x69,0xD4,0xA9,0x30,0xD4,0xA9,0x38,0x90,0x02,0xA9,0x76,0xDB,0x1B,0x50,0xD0,0x9F, +0x10,0x00,0x14,0x20,0xA9,0x34,0xED,0x00,0x02,0x69,0x02,0x13,0x05,0xF0,0x03,0x00, +0x02,0x69,0xE1,0x1F,0xA9,0x34,0x0C,0x90,0x03,0xA9,0x76,0xDA,0x01,0x1B,0xF0,0x02, +0x00,0x02,0x69,0x90,0x04,0xA9,0x76,0xDB,0x1B,0x50,0x12,0x03,0xDA,0x01,0x1B,0x90, +0x05,0xA9,0x76,0xDB,0x1B,0x50,0xC1,0x01,0x50,0x54,0xD0,0x8F,0xA0,0x86,0x01,0x00, +0x53,0xDB,0x1B,0x51,0xD1,0x51,0x54,0x1E,0x07,0xF5,0x53,0xF5,0x11,0x41,0x01,0x01, +0x90,0x06,0xA9,0x76,0xD0,0x8F,0x10,0x01,0x14,0x20,0x51,0xD4,0xA1,0x08,0xD0,0x8F, +0x40,0x0D,0x03,0x00,0x53,0xD0,0x8F,0x40,0x42,0x0F,0x00,0x56,0xD0,0x8F,0x11,0x00, +0x00,0x80,0x57,0xDB,0x1B,0x52,0xC0,0x02,0x52,0xC1,0x0A,0x52,0x54,0xD0,0x57,0x61, +0xDB,0x1B,0x50,0xD1,0x50,0x52,0x1E,0x0A,0xF5,0x53,0xF5,0x90,0x07,0xA9,0x76,0x31, +0x9C,0x00,0xD0,0xA1,0x04,0x58,0x01,0x01,0xDB,0x1B,0x50,0xD1,0x50,0x54,0x1E,0x09, +0xF5,0x56,0xF5,0x90,0x08,0xA9,0x76,0x11,0xE6,0xC3,0x58,0xA1,0x04,0x58,0xD0,0x8F, +0x00,0x00,0x00,0x80,0x61,0xC0,0x04,0x58,0xD0,0x8F,0xA0,0x86,0x01,0x00,0x57,0xD1, +0x58,0x57,0x1F,0x06,0xC3,0x57,0x58,0x56,0x11,0x04,0xC3,0x58,0x57,0x56,0xEF,0x00, +0x1F,0xA9,0x30,0x50,0xD1,0x56,0x50,0x1B,0x15,0xD0,0x56,0xA9,0x30,0xF0,0x00,0x1F, +0x01,0xA9,0x30,0xD1,0x58,0x57,0x1E,0x06,0xF0,0x01,0x1F,0x01,0xA9,0x30,0xC3,0xA9, +0x2C,0x57,0x56,0xC1,0xA9,0x2C,0x57,0x57,0xD1,0x58,0x56,0x1E,0x07,0x90,0x09,0xA9, +0x76,0x31,0x2A,0x00,0xD1,0x58,0x57,0x1B,0x07,0x90,0x0A,0xA9,0x76,0x31,0x1E,0x00, +0xD6,0xA9,0x38,0xD1,0xA9,0x38,0xA9,0x28,0x1E,0x03,0x31,0x12,0xFF,0xE1,0x1F,0xA9, +0x34,0x03,0xDA,0x00,0x1B,0xF0,0x03,0x04,0x02,0x69,0x94,0xA9,0x76,0x05,0x05,0xD0, +0x8F,0x00,0x00,0x00,0x80,0x9F,0x10,0x01,0x14,0x20,0x05,0x00,0x00,0x21,0x00,0x6C, +0x00,0x1A,0x00,0x1F,0x00,0x49,0x6E,0x74,0x65,0x72,0x76,0x61,0x6C,0x20,0x74,0x69, +0x6D,0x65,0x72,0x20,0x00,0x01,0xC0,0x00,0x56,0x00,0x01,0x00,0x90,0x01,0xA9,0x76, +0xF0,0x02,0x0E,0x02,0x69,0xD4,0xA9,0x28,0xD0,0x8F,0x20,0x4E,0x00,0x00,0xA9,0x5C, +0x16,0xEF,0x4F,0xE4,0xFF,0xFF,0xDA,0x16,0x12,0xDA,0x8F,0x40,0x00,0x00,0x00,0x18, +0x16,0xEF,0xF1,0xE3,0xFF,0xFF,0xD5,0xA9,0x28,0x12,0x18,0x90,0x02,0xA9,0x76,0xDA, +0x15,0x12,0xD5,0xA9,0x28,0x13,0x0C,0xF0,0x03,0x0E,0x02,0x69,0x94,0xA9,0x76,0xDA, +0x00,0x18,0x05,0xDA,0x00,0x18,0x05,0xDA,0x00,0x18,0x05,0xD6,0xA9,0x28,0x05,0x00, +0x00,0x80,0x7F,0x00,0x0B,0x01,0x5C,0x00,0x1A,0x00,0x56,0x41,0x58,0x20,0x43,0x4D, +0x43,0x54,0x4C,0x20,0x43,0x44,0x41,0x4C,0x20,0x00,0x03,0x02,0x1E,0x00,0x02,0x00, +0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x35,0x00,0x02,0x00,0x00,0x00,0x00,0xFF, +0xFF,0xFF,0xFF,0x10,0x27,0x00,0x00,0x00,0x64,0x6F,0x6E,0x74,0x5F,0x72,0x65,0x70, +0x6F,0x72,0x74,0x5F,0x6D,0x65,0x6D,0x6F,0x72,0x79,0x5F,0x62,0x61,0x64,0x00,0x72, +0x65,0x70,0x65,0x61,0x74,0x5F,0x63,0x6F,0x75,0x6E,0x74,0x00,0x01,0x04,0x00,0x05, +0x00,0xDE,0xEF,0x8C,0x00,0x00,0x00,0xBE,0x04,0x05,0x05,0x00,0x00,0x00,0x00,0xFF, +0xFF,0xFF,0xFF,0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,0x00,0x00,0x00,0x00,0x90, +0x01,0xA9,0x76,0xD4,0x51,0xD0,0x8F,0x40,0x01,0x08,0x20,0x57,0xCA,0x8F,0xFF,0x1F, +0x00,0x00,0xA7,0x04,0xD4,0x56,0xDA,0x00,0x25,0xDA,0x8F,0xE0,0x00,0x00,0x00,0x25, +0x90,0x02,0xA9,0x76,0xD0,0xA9,0x1C,0x54,0x13,0x53,0x90,0x03,0xA9,0x76,0xE9,0x64, +0x48,0xDE,0xAF,0xB6,0x53,0x9A,0x83,0x55,0x90,0x04,0xA9,0x76,0xD0,0x83,0x52,0xD2, +0x00,0x67,0xF0,0x01,0x1D,0x01,0x69,0x01,0x01,0x01,0xD0,0x52,0x61,0xD0,0x61,0x50, +0xF0,0x00,0x1D,0x01,0x69,0xD1,0x50,0x52,0x12,0x23,0xE1,0x02,0xA9,0x04,0x08,0x90, +0x05,0xA9,0x76,0xE0,0x1D,0x67,0x16,0xF5,0x55,0xD2,0xF2,0xA9,0x2C,0x56,0xC2,0x94, +0xA9,0x76,0x05,0x90,0x06,0xA9,0x76,0x11,0x04,0xE8,0xA9,0x28,0xF2,0xD0,0x67,0x58, +0xDB,0x27,0xA9,0x30,0xDA,0x00,0x27,0xDA,0x00,0x25,0x05,0xDA,0x00,0x25,0x05,0x00, +0x02,0x80,0x6D,0x00,0xB6,0x02,0x00,0x00,0x1A,0x00,0x63,0x61,0x63,0x68,0x65,0x5F, +0x6D,0x65,0x6D,0x5F,0x63,0x71,0x62,0x69,0x63,0x00,0x07,0x03,0x35,0x00,0x02,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x03,0x40,0x00,0x02,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x03,0x49,0x00,0x02,0x04, +0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x04,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x73, +0x74,0x61,0x72,0x74,0x5F,0x61,0x64,0x64,0x72,0x00,0x65,0x6E,0x64,0x5F,0x61,0x64, +0x64,0x72,0x00,0x61,0x64,0x64,0x72,0x5F,0x69,0x6E,0x63,0x72,0x00,0x31,0x30,0x02, +0x01,0xCA,0x03,0xA9,0x2C,0xCA,0x03,0xA9,0x30,0x12,0x08,0xD0,0x8F,0x00,0x02,0x00, +0x00,0xA9,0x30,0xD4,0xA9,0x40,0xD4,0xA9,0x3C,0x90,0x01,0xA9,0x76,0xDA,0x00,0x25, +0xDA,0x00,0x25,0xDA,0x00,0x27,0x90,0x02,0xA9,0x76,0xD5,0xA9,0x24,0x12,0x03,0x31, +0x05,0x02,0x90,0x03,0xA9,0x76,0xD5,0xA9,0x1C,0x12,0x03,0x31,0xF9,0x01,0x90,0x04, +0xA9,0x76,0xD0,0x8F,0x00,0x80,0x08,0x20,0x55,0xE0,0x1F,0x65,0x1B,0xC0,0x04,0x55, +0xD1,0x55,0x8F,0x00,0x00,0x09,0x20,0x1F,0xF0,0xE0,0x0A,0x9F,0x00,0x00,0x08,0x20, +0x03,0x31,0xD3,0x01,0x94,0xA9,0x76,0x05,0xD0,0x65,0xA9,0x40,0xD0,0xA9,0x28,0x51, +0x16,0xEF,0x42,0xB0,0x00,0x00,0x01,0x01,0xD1,0x51,0x53,0x1E,0x17,0xEF,0x09,0x11, +0x51,0x52,0xE0,0x52,0xB9,0x1C,0x1C,0xC0,0x8F,0x00,0x02,0x00,0x00,0x51,0xD1,0x51, +0xA9,0x2C,0x1F,0xE4,0xD5,0xA9,0x3C,0x13,0x03,0x31,0x90,0x01,0x90,0x05,0xA9,0x76, +0x31,0x94,0x01,0xD6,0xA9,0x3C,0xF0,0x52,0x00,0x11,0x65,0xEF,0x00,0x09,0x51,0x53, +0xEF,0x02,0x0D,0x55,0x54,0xF0,0x54,0x09,0x0D,0x53,0xC0,0x8F,0x00,0x00,0x00,0x30, +0x53,0xCB,0x0F,0x51,0x58,0x90,0x06,0xA9,0x76,0x7C,0x68,0x7C,0xA8,0x08,0x90,0x07, +0xA9,0x76,0xD0,0x8F,0x21,0x43,0x21,0x43,0x63,0xD0,0x63,0x56,0xD1,0x56,0x8F,0x21, +0x43,0x21,0x43,0x12,0x2D,0x90,0x08,0xA9,0x76,0xD0,0x61,0x57,0xD1,0x57,0x8F,0x21, +0x43,0x21,0x43,0x12,0x1D,0x90,0x09,0xA9,0x76,0xDA,0x8F,0xD0,0x00,0x00,0x00,0x25, +0xDA,0x00,0x27,0xD0,0x61,0x57,0xDB,0x27,0x50,0xD1,0x57,0x8F,0x21,0x43,0x21,0x43, +0x13,0x03,0x31,0x22,0x01,0x90,0x0A,0xA9,0x76,0x93,0x8F,0x60,0x50,0x12,0xF3,0x90, +0x0B,0xA9,0x76,0x95,0x50,0x13,0xEB,0x90,0x0C,0xA9,0x76,0xDA,0x8F,0xD0,0x00,0x00, +0x00,0x25,0xDA,0x00,0x27,0xD0,0x61,0x52,0xD0,0x63,0x56,0xDB,0x27,0x50,0xD1,0x56, +0x52,0x13,0x03,0x31,0xF1,0x00,0x90,0x0D,0xA9,0x76,0x93,0x8F,0x60,0x50,0x12,0xF3, +0x90,0x0E,0xA9,0x76,0x95,0x50,0x13,0xEB,0xD0,0x61,0x52,0xDB,0x27,0x50,0x90,0x0F, +0xA9,0x76,0x95,0x50,0x12,0xDD,0x90,0x10,0xA9,0x76,0xD1,0x56,0x52,0x12,0xD4,0xDA, +0x8F,0xD0,0x00,0x00,0x00,0x25,0xDA,0x00,0x27,0x7C,0x68,0x7C,0xA8,0x08,0xD5,0x68, +0xD5,0xA8,0x08,0xD0,0x61,0x57,0xDB,0x27,0x50,0x90,0x11,0xA9,0x76,0x95,0x50,0x12, +0x25,0x90,0x12,0xA9,0x76,0xD5,0x57,0x12,0x1D,0xD0,0x8F,0x68,0x24,0x68,0x24,0x63, +0xD0,0x63,0x56,0xD0,0x61,0x54,0xDB,0x27,0x50,0x90,0x13,0xA9,0x76,0xD1,0x54,0x8F, +0x68,0x24,0x68,0x24,0x13,0x03,0x31,0x7E,0x00,0x90,0x14,0xA9,0x76,0x91,0x50,0x8F, +0x80,0x12,0xF3,0xD0,0x51,0x58,0xE0,0x02,0x58,0x04,0xE3,0x02,0x58,0x04,0xE4,0x02, +0x58,0x00,0xD0,0x8F,0x68,0x24,0x68,0x24,0x63,0xD0,0x63,0x56,0xD0,0x68,0x57,0xDB, +0x27,0x50,0x90,0x15,0xA9,0x76,0x91,0x50,0x8F,0x80,0x12,0x3D,0x90,0x16,0xA9,0x76, +0xD5,0x57,0x12,0x35,0xD0,0x51,0x58,0xE0,0x03,0x58,0x04,0xE3,0x03,0x58,0x04,0xE4, +0x03,0x58,0x00,0xD0,0x68,0x57,0xDB,0x27,0x50,0x90,0x17,0xA9,0x76,0x95,0x50,0x12, +0x18,0x90,0x18,0xA9,0x76,0xD5,0x57,0x12,0x10,0xC0,0xA9,0x30,0x51,0xD1,0x51,0xA9, +0x2C,0x1F,0x03,0x31,0x6E,0xFE,0x31,0x47,0xFE,0x31,0x0B,0x00,0xD0,0xA9,0x40,0x65, +0xD4,0xA9,0x40,0x94,0xA9,0x76,0x05,0xDB,0x25,0xA9,0x34,0xDB,0x27,0xA9,0x38,0xDA, +0x00,0x27,0xDA,0x00,0x25,0x05,0xD5,0xA9,0x40,0x13,0x04,0xD0,0xA9,0x40,0x65,0xED, +0x14,0x02,0x69,0x02,0x13,0x10,0xF0,0x02,0x14,0x02,0x69,0x91,0x00,0xA9,0x76,0x12, +0x05,0xF0,0x03,0x14,0x02,0x69,0xC8,0x8F,0x00,0x00,0x00,0xC0,0x9F,0x20,0x00,0x14, +0x20,0xDA,0x00,0x27,0xDA,0x00,0x25,0x05,0x03,0x80,0x42,0x00,0xC5,0x05,0x19,0x00, +0x1E,0x00,0x43,0x61,0x63,0x68,0x65,0x31,0x5F,0x64,0x69,0x61,0x67,0x5F,0x6D,0x64, +0x00,0x01,0x04,0x00,0x03,0x06,0x0A,0x03,0x1A,0x00,0x02,0x08,0x00,0x00,0x00,0x00, +0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x61,0x64,0x64,0x72,0x5F,0x69,0x6E,0x63,0x72,0x00,0xDE,0xAF,0xFD,0xC9,0x80,0x00, +0x90,0x01,0xA9,0x76,0xD0,0x9F,0x20,0x00,0x14,0x20,0xA9,0x44,0xD0,0x0A,0x9F,0x20, +0x00,0x14,0x20,0xD0,0x8F,0x00,0x00,0x00,0x04,0xA9,0x30,0xD0,0x01,0xA9,0x2C,0x30, +0xBB,0xDF,0x90,0x02,0xA9,0x76,0x9A,0x8F,0xFC,0x55,0xDA,0x55,0x25,0xDB,0x25,0x50, +0xDA,0x03,0x25,0xDB,0x25,0x51,0xDA,0x00,0x25,0xDA,0x00,0x25,0x90,0x03,0xA9,0x76, +0xD1,0x50,0x55,0x12,0x18,0x90,0x04,0xA9,0x76,0xD0,0x51,0x50,0xD1,0x50,0x0F,0x12, +0x0C,0x90,0x05,0xA9,0x76,0xDB,0x25,0x50,0xD1,0x50,0x0C,0x13,0x03,0x31,0xFE,0x04, +0x31,0xF7,0x04,0x01,0x30,0x56,0x05,0x7D,0x01,0x52,0xD0,0x02,0x54,0x90,0x07,0xA9, +0x76,0xC8,0x01,0x58,0xDA,0x58,0x25,0x7D,0x51,0x61,0xD5,0x61,0xD0,0x53,0xA1,0x04, +0xD5,0xA1,0x04,0xCA,0x01,0x58,0xDA,0x58,0x25,0x7D,0x61,0x55,0xDB,0x25,0x57,0xDA, +0x00,0x25,0x90,0x08,0xA9,0x76,0xDB,0x27,0x50,0x13,0x03,0x31,0xC0,0x04,0x90,0x09, +0xA9,0x76,0xD0,0x55,0x50,0xD1,0x50,0x52,0x12,0xF1,0x90,0x0A,0xA9,0x76,0xD0,0x56, +0x50,0xD1,0x50,0x53,0x12,0xE5,0xD2,0x52,0x52,0xD2,0x53,0x53,0xF5,0x54,0xAE,0x79, +0x01,0x52,0x52,0x12,0xA5,0x90,0x0B,0xA9,0x76,0x30,0xF1,0x04,0xC8,0x01,0x58,0xDA, +0x58,0x25,0x7C,0x61,0xD5,0x61,0xD4,0xA1,0x04,0xD2,0x00,0x81,0xD2,0x00,0x81,0xD0, +0x07,0x54,0x90,0x54,0x71,0xF4,0x54,0xFA,0xCA,0x01,0x58,0xDA,0x58,0x25,0x7D,0x61, +0x55,0xDB,0x25,0x57,0xDA,0x00,0x25,0xDB,0x27,0x50,0x13,0x03,0x31,0x5F,0x04,0x90, +0x0C,0xA9,0x76,0xD0,0x55,0x50,0xD1,0x50,0x8F,0x00,0x01,0x02,0x03,0x12,0xED,0x90, +0x0D,0xA9,0x76,0xD0,0x56,0x50,0xD1,0x50,0x8F,0x04,0x05,0x06,0x07,0x12,0xDD,0x90, +0x0E,0xA9,0x76,0x30,0x97,0x04,0xB3,0x8F,0xFF,0x01,0x51,0x13,0x03,0x31,0xF2,0x00, +0xD0,0x8F,0xAA,0xAA,0xAA,0xAA,0x52,0xD0,0x01,0x54,0xC8,0x01,0x58,0xDA,0x58,0x25, +0x7D,0x51,0x61,0xD5,0x81,0xD0,0x52,0x81,0xB3,0x8F,0xFF,0x01,0x51,0x12,0xF1,0xC2, +0x8F,0x00,0x02,0x00,0x00,0x51,0xD2,0x52,0x53,0xD0,0x02,0x54,0xD0,0x61,0x55,0xDB, +0x25,0x57,0xDB,0x27,0x50,0x12,0x2D,0xD6,0x54,0xD1,0x55,0x52,0x12,0x26,0xD6,0x54, +0xD0,0xA1,0x04,0x56,0xDB,0x25,0x57,0xDB,0x27,0x50,0x12,0x18,0xD6,0x54,0xD1,0x56, +0x52,0x12,0x11,0xD6,0x54,0xD0,0x53,0x81,0xD0,0x53,0x81,0xB3,0x8F,0xFF,0x01,0x51, +0x12,0xC7,0x11,0x03,0x31,0xC7,0x03,0xC2,0x8F,0x00,0x02,0x00,0x00,0x51,0xD0,0x8F, +0xF7,0x7F,0x1F,0x07,0x52,0xD0,0x07,0x54,0xD0,0x61,0x55,0xDB,0x25,0x57,0xDB,0x27, +0x50,0x12,0xE1,0xD6,0x54,0xD1,0x55,0x53,0x12,0x65,0xD6,0x54,0xD0,0xA1,0x04,0x56, +0xDB,0x25,0x57,0xDB,0x27,0x50,0x12,0xCC,0xD6,0x54,0xD1,0x56,0x53,0x12,0x50,0xD0, +0x52,0x81,0xD0,0x52,0x81,0xB3,0x8F,0xFF,0x01,0x51,0x12,0xC9,0xC2,0x8F,0x00,0x02, +0x00,0x00,0x51,0xCA,0x01,0x58,0xDA,0x58,0x25,0xD0,0x0B,0x54,0xD0,0x61,0x55,0xDB, +0x25,0x57,0xDB,0x27,0x50,0x12,0x9D,0xD6,0x54,0xD1,0x55,0x52,0x12,0x21,0xD6,0x54, +0xD0,0xA1,0x04,0x56,0xDB,0x25,0x57,0xDB,0x27,0x50,0x12,0x88,0xD6,0x54,0xD1,0x56, +0x52,0x12,0x0C,0xC0,0x08,0x51,0xB3,0x8F,0xFF,0x01,0x51,0x12,0xCC,0x11,0x03,0x31, +0x3C,0x03,0x90,0x0F,0xA9,0x76,0x30,0x94,0x03,0xC8,0x01,0x58,0xDA,0x58,0x25,0x7C, +0x61,0xD5,0x61,0xD4,0xA1,0x04,0x7C,0xA1,0x08,0xD5,0xA1,0x08,0xD4,0xA1,0x0C,0x90, +0x10,0xA9,0x76,0xC8,0x02,0x58,0xDA,0x58,0x25,0xD0,0x01,0xA1,0x0C,0x11,0x00,0xCA, +0x03,0x58,0xDA,0x58,0x25,0xD4,0xA9,0x34,0xD4,0xA9,0x4C,0xDE,0xEF,0x0F,0x00,0x00, +0x00,0xA9,0x48,0xF0,0x01,0x1D,0x01,0x69,0x90,0x11,0xA9,0x76,0xD0,0xA1,0x0C,0x52, +0xF0,0x00,0x1D,0x01,0x69,0xDB,0x27,0x50,0xDB,0x25,0x57,0x90,0x12,0xA9,0x76,0xD1, +0xA9,0x34,0x01,0x12,0x9A,0x90,0x13,0xA9,0x76,0xED,0x00,0x07,0x50,0x11,0x12,0x8F, +0x90,0x14,0xA9,0x76,0xD1,0x57,0x0C,0x12,0x86,0xDA,0x58,0x25,0xDA,0x00,0x27,0xDE, +0xEF,0x10,0x00,0x00,0x00,0xA9,0x48,0xD4,0xA9,0x34,0xF0,0x01,0x1D,0x01,0x69,0x90, +0x15,0xA9,0x76,0xD5,0x61,0xF0,0x00,0x1D,0x01,0x69,0x90,0x16,0xA9,0x76,0xDB,0x27, +0x50,0x93,0x8F,0x7F,0x50,0x13,0x03,0x31,0x7F,0x00,0x90,0x17,0xA9,0x76,0xD1,0xA9, +0x34,0x01,0x12,0x75,0x30,0xE6,0x02,0xC8,0x01,0x58,0xDA,0x58,0x25,0x7C,0x61,0xD5, +0x61,0xD4,0xA1,0x04,0xC8,0x02,0x58,0xDA,0x58,0x25,0xD0,0x01,0x61,0x11,0x00,0xCA, +0x02,0x58,0xDA,0x58,0x25,0xD0,0x02,0xA1,0x04,0x11,0x00,0xCA,0x03,0x58,0xDA,0x58, +0x25,0xD4,0xA9,0x34,0xDE,0xEF,0x11,0x00,0x00,0x00,0xA9,0x48,0xDA,0x00,0x27,0xF0, +0x01,0x1D,0x01,0x69,0x90,0x18,0xA9,0x76,0xD0,0x61,0x52,0xF0,0x00,0x1D,0x01,0x69, +0xDB,0x27,0x50,0xDB,0x25,0x57,0x90,0x19,0xA9,0x76,0xD1,0xA9,0x34,0x01,0x12,0x19, +0x90,0x1A,0xA9,0x76,0xED,0x00,0x07,0x50,0x12,0x12,0x0E,0x90,0x1B,0xA9,0x76,0xD1, +0x57,0x0C,0x12,0x05,0xDA,0x00,0x27,0x11,0x03,0x31,0x12,0x02,0xDE,0xEF,0x0C,0x02, +0x00,0x00,0xA9,0x48,0x90,0x1C,0xA9,0x76,0x30,0x62,0x02,0xB3,0x8F,0xFF,0x03,0x51, +0x13,0x03,0x31,0xEC,0x00,0x3C,0x8F,0x00,0x02,0x51,0xD4,0x54,0xD0,0x8F,0xFF,0x01, +0x00,0xFC,0x52,0xC8,0x01,0x58,0xDA,0x58,0x25,0xD0,0x02,0x56,0xC1,0x54,0x51,0x53, +0xC8,0x8F,0x00,0x00,0x00,0x04,0x53,0x7D,0x52,0x63,0xD5,0x83,0xD4,0x83,0xC0,0x08, +0x54,0xD2,0x51,0x51,0xCA,0x52,0x51,0xF5,0x56,0xE2,0x78,0x01,0x51,0x51,0xE1,0x1A, +0x51,0xD7,0xCA,0x01,0x58,0xDA,0x58,0x25,0x3C,0x8F,0x00,0x02,0x51,0xD4,0x54,0xD0, +0x02,0x56,0xC1,0x54,0x51,0x53,0xC8,0x8F,0x00,0x00,0x00,0x04,0x53,0x90,0x1D,0xA9, +0x76,0xD0,0x63,0x55,0xDB,0x27,0x50,0x12,0x1F,0x90,0x1E,0xA9,0x76,0xD1,0x53,0x55, +0x12,0x16,0xC0,0x08,0x54,0xD2,0x51,0x51,0xCA,0x52,0x51,0xF5,0x56,0xD4,0x78,0x01, +0x51,0x51,0xE1,0x1A,0x51,0xC9,0x11,0x03,0x31,0x73,0x01,0x90,0x1F,0xA9,0x76,0xD0, +0x8F,0x00,0x00,0x00,0x04,0x51,0xDE,0xEF,0x0F,0x00,0x00,0x00,0xA9,0x48,0xD0,0x02, +0x54,0xF0,0x01,0x1D,0x01,0x69,0xD4,0xA9,0x34,0xD5,0x61,0xD1,0xA9,0x34,0x01,0x12, +0xD7,0xC0,0x10,0x51,0xB3,0x8F,0xFF,0x01,0x51,0x12,0xEB,0xF0,0x00,0x1D,0x01,0x69, +0x90,0x20,0xA9,0x76,0xD0,0x8F,0x00,0xFE,0xFF,0x07,0x51,0xDE,0xEF,0x10,0x00,0x00, +0x00,0xA9,0x48,0xD0,0x02,0x54,0xF0,0x01,0x1D,0x01,0x69,0xD4,0xA9,0x34,0xD5,0xA1, +0x08,0xD1,0xA9,0x34,0x01,0x12,0xA1,0xC0,0x10,0x51,0xB3,0x8F,0xFF,0x01,0x51,0x12, +0xEA,0xF0,0x00,0x1D,0x01,0x69,0xDA,0x00,0x25,0x91,0xA9,0x2C,0x02,0x1E,0x06,0x96, +0xA9,0x2C,0x31,0xBA,0xFB,0xD0,0x01,0xA9,0x2C,0x90,0x21,0xA9,0x76,0x30,0x6D,0xDB, +0x30,0x4A,0x01,0xD0,0xA9,0x30,0x51,0xB3,0x8F,0xFF,0x03,0x51,0x13,0x03,0x31,0xC7, +0x00,0xC8,0x01,0x58,0xDA,0x58,0x25,0xD0,0x01,0x54,0xD0,0x02,0x56,0x7D,0x50,0x61, +0xD5,0x81,0xD4,0x81,0xB3,0x8F,0xFF,0x01,0x51,0x12,0xF2,0xD0,0x02,0x54,0xDA,0x8F, +0x91,0x00,0x00,0x00,0x25,0xD0,0x02,0x56,0xB3,0x8F,0xFF,0x03,0x51,0x12,0xDE,0xD0, +0x03,0x54,0x9A,0x8F,0xD0,0x52,0xDA,0x52,0x25,0xD0,0x03,0x56,0xC2,0x8F,0x00,0x04, +0x00,0x00,0x51,0xD0,0x04,0x54,0xD0,0x61,0x55,0xD6,0x54,0xDB,0x25,0x57,0xDB,0x27, +0x50,0x12,0x29,0xD6,0x54,0xD1,0x51,0x55,0x12,0x22,0xC0,0x04,0x51,0xD0,0x61,0x55, +0xD6,0x54,0xDB,0x25,0x57,0xDB,0x27,0x50,0x12,0x12,0xD6,0x54,0xD5,0x55,0x12,0x0C, +0xC0,0x04,0x51,0xB3,0x8F,0xFF,0x03,0x51,0x12,0xC9,0x11,0x03,0x31,0x5F,0x00,0x30, +0xD3,0xDA,0x90,0x22,0xA9,0x76,0xD0,0xA9,0x30,0x51,0xDA,0x52,0x25,0xDE,0xEF,0x10, +0x00,0x00,0x00,0xA9,0x48,0xF0,0x01,0x1D,0x01,0x69,0x90,0x23,0xA9,0x76,0xD4,0xA9, +0x34,0xD5,0x61,0x90,0x24,0xA9,0x76,0xDB,0x25,0x57,0xDB,0x27,0x50,0xD1,0xA9,0x34, +0x01,0x12,0x2B,0xC0,0x08,0x51,0xB3,0x8F,0xFF,0x03,0x51,0x12,0xDD,0xF0,0x00,0x1D, +0x01,0x69,0xD4,0xA9,0x34,0xDA,0x00,0x25,0xC0,0xA9,0x28,0xA9,0x30,0xD1,0xA9,0x30, +0x8F,0x00,0x00,0x00,0x08,0x1E,0x03,0x31,0xC1,0xFA,0x94,0xA9,0x76,0x05,0xF0,0x00, +0x1D,0x01,0x69,0xD0,0xA9,0x44,0x9F,0x20,0x00,0x14,0x20,0xDB,0x25,0xA9,0x3C,0xDB, +0x27,0xA9,0x38,0xDA,0x00,0x25,0xDA,0x00,0x25,0xDA,0x00,0x27,0x05,0xD0,0xA9,0x44, +0x9F,0x20,0x00,0x14,0x20,0xC8,0x8F,0x00,0x00,0x00,0xC0,0x9F,0x20,0x00,0x14,0x20, +0xED,0x14,0x02,0x69,0x02,0x13,0x10,0xF0,0x02,0x14,0x02,0x69,0x91,0x00,0xA9,0x76, +0x12,0x05,0xF0,0x03,0x14,0x01,0x69,0xDB,0x25,0xAE,0xFC,0x9A,0xAE,0xFC,0xA9,0x40, +0xDB,0x27,0xAE,0xFC,0x90,0xAE,0xFC,0xA9,0x41,0x30,0xB7,0xFF,0x05,0xDA,0x00,0x25, +0xDA,0x00,0x25,0xD0,0x10,0x58,0xF0,0xA9,0x2C,0x06,0x02,0x58,0xDA,0x00,0x27,0xD0, +0xA9,0x30,0x51,0x05,0xD0,0xBE,0x04,0xA9,0x4C,0xD6,0xA9,0x34,0xD0,0xA9,0x48,0xBE, +0x04,0x05,0x00,0x00,0x18,0x00,0x25,0x00,0x00,0x00,0x16,0x00,0x4C,0x69,0x73,0x74, +0x20,0x64,0x69,0x61,0x67,0x73,0x20,0x00,0x01,0x00,0x90,0x01,0xA9,0x76,0x16,0xEF, +0x41,0xD7,0xFF,0xFF,0x94,0xA9,0x76,0x05,0x00,0x00,0x3C,0x00,0xA6,0x01,0x19,0x00, +0x1E,0x00,0x4D,0x53,0x43,0x50,0x2D,0x51,0x42,0x55,0x53,0x20,0x74,0x65,0x73,0x74, +0x00,0x01,0x08,0x00,0x9A,0x01,0x07,0x03,0x17,0x00,0x02,0x00,0x00,0x00,0x20,0xFF, +0x1F,0x00,0x20,0x68,0x14,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x50,0x5F, +0x63,0x73,0x72,0x00,0x90,0x01,0xA9,0x76,0xDB,0x11,0xA9,0x38,0xC1,0x02,0xA9,0x28, +0xA9,0x30,0x9A,0x01,0xA9,0x3C,0x90,0x02,0xA9,0x76,0xDA,0x13,0x12,0xB4,0xB9,0x28, +0xD0,0x8F,0xE0,0x01,0x00,0x00,0xA9,0x5C,0x16,0xEF,0x19,0xD9,0xFF,0xFF,0xDA,0x1F, +0x12,0x3C,0xB9,0x30,0x50,0x90,0x03,0xA9,0x76,0xD0,0x01,0xA9,0x40,0xEF,0x0B,0x04, +0x50,0x51,0x91,0x51,0x01,0x12,0x42,0x90,0x04,0xA9,0x76,0xE0,0x0F,0x50,0x3A,0x90, +0x05,0xA9,0x76,0xE1,0x09,0x50,0x32,0xE1,0x08,0x50,0x2E,0x90,0x06,0xA9,0x76,0xD0, +0x8F,0x00,0xC0,0x00,0x00,0x51,0xB0,0x51,0xB9,0x30,0x3C,0x8F,0x10,0x27,0x52,0xD0, +0x8F,0xE0,0x01,0x00,0x00,0xA9,0x5C,0x16,0xEF,0xCA,0xD8,0xFF,0xFF,0x3C,0xB9,0x30, +0x50,0xB1,0x50,0x51,0x13,0x06,0xF4,0x52,0xE6,0x31,0x3C,0x00,0x90,0x07,0xA9,0x76, +0x9A,0x8F,0x04,0x51,0xB0,0x41,0xEF,0xE5,0x00,0x00,0x00,0xB9,0x30,0xD0,0x8F,0x10, +0x27,0x00,0x00,0x52,0xD0,0x8F,0xE0,0x01,0x00,0x00,0xA9,0x5C,0x16,0xEF,0x95,0xD8, +0xFF,0xFF,0xB1,0xB9,0x30,0x41,0xEF,0xC5,0x00,0x00,0x00,0x13,0x06,0xF4,0x52,0xE4, +0x31,0x05,0x00,0xF4,0x51,0xCE,0x11,0x0A,0xF4,0xA9,0x3C,0x03,0x31,0x8D,0x00,0x31, +0x44,0xFF,0x90,0x08,0xA9,0x76,0x9A,0x01,0xA9,0x3C,0x16,0xEF,0xB5,0xD8,0xFF,0xFF, +0xDA,0x13,0x12,0xC3,0x8F,0x00,0x02,0x00,0x00,0xA9,0x38,0x50,0xDA,0x50,0x11,0x90, +0x09,0xA9,0x76,0xB4,0xB9,0x28,0xD0,0x8F,0xE0,0x01,0x00,0x00,0xA9,0x5C,0x16,0xEF, +0x43,0xD8,0xFF,0xFF,0x3C,0xB9,0x30,0x50,0xE0,0x0F,0x50,0x39,0x90,0x0A,0xA9,0x76, +0xEF,0x0B,0x04,0x50,0x51,0x91,0x51,0x01,0x12,0x2B,0x90,0x0B,0xA9,0x76,0xE0,0x0A, +0x50,0x23,0x90,0x0C,0xA9,0x76,0x3C,0x8F,0x82,0x80,0x51,0xB0,0x51,0xB9,0x30,0xD4, +0xA9,0x34,0xD0,0x8F,0x00,0x3E,0x49,0x00,0xA9,0x5C,0x16,0xEF,0x07,0xD8,0xFF,0xFF, +0xD5,0xA9,0x34,0x12,0x0A,0xF4,0xA9,0x3C,0x03,0x31,0x10,0x00,0x31,0xA0,0xFF,0x94, +0xA9,0x76,0xDA,0xA9,0x38,0x11,0x3C,0xB9,0x30,0xA9,0x2C,0x05,0xDA,0xA9,0x38,0x11, +0x3C,0xB9,0x30,0xA9,0x2C,0xD5,0xA9,0x40,0x13,0x03,0xB4,0xB9,0x28,0x05,0xDA,0xA9, +0x38,0x11,0xD5,0xA9,0x40,0x13,0x03,0xB4,0xB9,0x28,0x05,0x9A,0x01,0xA9,0x34,0x05, +0xAA,0xAA,0x00,0x00,0x55,0x55,0xF0,0xF0,0xFF,0xFF,0x00,0x80,0x43,0x00,0xDA,0x01, +0x35,0x00,0x14,0x00,0x44,0x45,0x4C,0x51,0x41,0x20,0x20,0x20,0x20,0x00,0x05,0x02, +0x11,0x00,0x02,0x00,0x00,0x00,0x00,0xFF,0x1F,0x00,0x20,0x00,0x00,0x00,0x00,0x64, +0x65,0x76,0x69,0x63,0x65,0x5F,0x6E,0x75,0x6D,0x5F,0x61,0x64,0x64,0x72,0x00,0x01, +0x04,0x00,0x05,0x00,0xDE,0xEF,0x8E,0x01,0x00,0x00,0xBE,0x04,0x05,0xDE,0xAF,0xFD, +0xC9,0x80,0x00,0x90,0x01,0xA9,0x76,0xD4,0xA9,0x38,0xD0,0x8F,0x20,0x19,0x00,0x20, +0xA9,0x2C,0xD5,0xA9,0x28,0x13,0x2B,0xC0,0x10,0xA9,0x2C,0xD1,0xA9,0x28,0x01,0x13, +0x21,0xCB,0x0F,0xA9,0x28,0xA9,0x2C,0xD1,0xA9,0x28,0x8F,0x00,0x00,0x00,0x20,0x1F, +0x0A,0xD1,0xA9,0x28,0x8F,0x00,0x20,0x00,0x20,0x1F,0x07,0x90,0x02,0xA9,0x76,0x31, +0x46,0x01,0xF0,0x01,0x1D,0x01,0x69,0x90,0x03,0xA9,0x76,0xD0,0xA9,0x2C,0x51,0xB5, +0x81,0x90,0x04,0xA9,0x76,0xB5,0x81,0xB5,0x81,0xB5,0x81,0xB5,0x81,0xB5,0x61,0xF0, +0x00,0x1D,0x01,0x69,0xC0,0x0C,0xA9,0x2C,0xD0,0xA9,0x2C,0x51,0xB0,0x02,0xA1,0x02, +0xB4,0xA1,0x02,0x90,0x05,0xA9,0x76,0x3C,0xA1,0x02,0xA9,0x34,0x3C,0x61,0xA9,0x30, +0xE1,0x0C,0xA9,0x34,0xBA,0x90,0x06,0xA9,0x76,0xA8,0x01,0x61,0x3C,0xA1,0x02,0xA9, +0x34,0x3C,0x61,0xA9,0x30,0xAA,0x01,0x61,0xB3,0x01,0xA9,0x30,0x12,0x1A,0x11,0x0A, +0x25,0x6E,0x44,0x45,0x51,0x4E,0x41,0x25,0x6E,0x00,0x9F,0xAF,0xF3,0xDD,0x01,0x16, +0xEF,0xB0,0xD7,0xFF,0xFF,0x31,0xD1,0x00,0xA8,0x8F,0x00,0x80,0x61,0xD0,0x8F,0xA0, +0x86,0x01,0x00,0xA9,0x5C,0x16,0xEF,0xAC,0xD6,0xFF,0xFF,0x3C,0xA1,0x02,0xA9,0x34, +0x3C,0x61,0xA9,0x30,0xB3,0x8F,0x00,0x80,0xA9,0x30,0x12,0x3B,0x11,0x2B,0x25,0x6E, +0x44,0x45,0x4C,0x51,0x41,0x2C,0x20,0x66,0x6F,0x72,0x63,0x65,0x64,0x20,0x74,0x6F, +0x20,0x44,0x45,0x51,0x4E,0x41,0x20,0x6D,0x6F,0x64,0x65,0x20,0x76,0x69,0x61,0x20, +0x73,0x77,0x69,0x74,0x63,0x68,0x25,0x6E,0x00,0x9F,0xAF,0xD2,0xDD,0x01,0x16,0xEF, +0x51,0xD7,0xFF,0xFF,0x31,0x72,0x00,0x11,0x18,0x25,0x6E,0x44,0x45,0x4C,0x51,0x41, +0x20,0x69,0x6E,0x20,0x44,0x45,0x4C,0x51,0x41,0x20,0x6D,0x6F,0x64,0x65,0x25,0x6E, +0x00,0x9F,0xAF,0xE5,0xDD,0x01,0x16,0xEF,0x29,0xD7,0xFF,0xFF,0x90,0x07,0xA9,0x76, +0xA8,0x8F,0x00,0x20,0x61,0xD0,0x8F,0xB8,0x0B,0x00,0x00,0x53,0x3C,0x8F,0x10,0x27, +0xA9,0x5C,0x16,0xEF,0x1F,0xD6,0xFF,0xFF,0x3C,0x61,0xA9,0x30,0xE1,0x0D,0xA9,0x30, +0x0E,0xF5,0x53,0xE8,0x90,0x08,0xA9,0x76,0x3C,0xA1,0x02,0xA9,0x34,0x11,0x19,0x3C, +0xA1,0x02,0xA9,0x34,0x3C,0x61,0xA9,0x30,0xEF,0x0A,0x03,0xA9,0x30,0xA9,0x38,0x90, +0x09,0xA9,0x76,0xD5,0xA9,0x38,0x13,0x01,0x05,0xB0,0x02,0xA1,0x02,0xB4,0xA1,0x02, +0x94,0xA9,0x76,0x05,0x05,0x00,0x80,0x2C,0x00,0xC6,0x01,0x00,0x00,0x16,0x00,0x53, +0x53,0x43,0x20,0x52,0x41,0x4D,0x20,0x20,0x20,0x20,0x00,0x01,0x00,0x00,0x04,0x14, +0x20,0x00,0x05,0x14,0x20,0x00,0x06,0x14,0x20,0x00,0x07,0x14,0x20,0x00,0x00,0x00, +0x00,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x01,0xA9,0x76,0xD0,0x8F,0x00,0x04,0x14, +0x20,0x51,0x3C,0x8F,0x00,0x01,0x53,0x90,0x02,0xA9,0x76,0xD5,0x61,0xB5,0x61,0x95, +0x81,0x95,0x81,0xB5,0x61,0x95,0x81,0x95,0x81,0xF5,0x53,0xEF,0x90,0x03,0xA9,0x76, +0x3C,0x8F,0x00,0x01,0x53,0xD0,0x8F,0x00,0x04,0x14,0x20,0x51,0xD0,0x61,0x52,0xD0, +0x52,0x61,0xD0,0x61,0x50,0xD1,0x50,0x52,0x12,0x5C,0xC0,0x04,0x51,0xF5,0x53,0xEC, +0xD0,0x8F,0x00,0x04,0x14,0x20,0x51,0x90,0x04,0xA9,0x76,0x7D,0x61,0x56,0xD4,0x52, +0xD2,0x00,0x53,0x7D,0x52,0x61,0x7D,0x61,0x54,0x7D,0x56,0x61,0x90,0x05,0xA9,0x76, +0xD1,0x52,0x54,0x12,0x31,0x90,0x06,0xA9,0x76,0xD1,0x53,0x55,0x12,0x28,0xD0,0x01, +0x52,0xD0,0x02,0x50,0x90,0x07,0xA9,0x76,0xD2,0x52,0x53,0xD0,0x52,0x61,0xD0,0x53, +0xA1,0x04,0x7D,0x61,0x54,0x7D,0x56,0x61,0x90,0x08,0xA9,0x76,0xD1,0x52,0x54,0x12, +0x05,0xD1,0x53,0x55,0x13,0x03,0x31,0x8D,0x00,0xD2,0x52,0x52,0xF5,0x50,0xD5,0x78, +0x01,0x52,0x52,0x12,0xCC,0x90,0x09,0xA9,0x76,0xD2,0x00,0x81,0xD2,0x00,0x81,0xD0, +0x08,0x52,0x90,0x52,0x71,0xF5,0x52,0xFA,0x7D,0x61,0x54,0x7D,0x56,0x61,0x90,0x0A, +0xA9,0x76,0xD0,0x8F,0x01,0x02,0x03,0x04,0x52,0xD0,0x8F,0x05,0x06,0x07,0x08,0x53, +0xD1,0x54,0x52,0x12,0xC1,0xD1,0x55,0x53,0x12,0xBC,0x90,0x0B,0xA9,0x76,0xD0,0x8F, +0x88,0x99,0xAA,0xBB,0x52,0xD0,0x8F,0xCC,0xDD,0xEE,0xFF,0x53,0x7D,0x52,0x61,0xB0, +0x8F,0x22,0x11,0xA1,0x03,0x7D,0x61,0x54,0x7D,0x56,0x61,0x90,0x0C,0xA9,0x76,0xD0, +0x8F,0x88,0x99,0xAA,0x22,0x52,0xD0,0x8F,0x11,0xDD,0xEE,0xFF,0x53,0xD1,0x52,0x54, +0x12,0x14,0xD1,0x53,0x55,0x12,0x0F,0xC0,0x08,0x51,0xD1,0x51,0x8F,0x00,0x08,0x14, +0x20,0x1E,0x04,0x31,0x25,0xFF,0x05,0x90,0x0D,0xA9,0x76,0xDE,0xCF,0xAE,0xFE,0x50, +0xD0,0x90,0x55,0xD0,0x90,0x56,0xD0,0x90,0x57,0xD0,0x90,0x58,0xDE,0xCF,0x9D,0xFE, +0x50,0xD0,0x04,0x90,0xD0,0x03,0x90,0xD0,0x02,0x90,0xD0,0x01,0x90,0xDE,0xCF,0x8C, +0xFE,0x50,0xD0,0x90,0x51,0xD0,0x90,0x52,0xD0,0x90,0x53,0xD0,0x90,0x54,0xDE,0xCF, +0x7B,0xFE,0x50,0xD0,0x55,0x90,0xD0,0x56,0x90,0xD0,0x57,0x90,0xD0,0x58,0x90,0x90, +0x0E,0xA9,0x76,0xD1,0x51,0x04,0x12,0xAE,0xD1,0x52,0x03,0x12,0xA9,0xD1,0x53,0x02, +0x12,0xA4,0xD1,0x54,0x01,0x12,0x9F,0x94,0xA9,0x76,0x05,0x05,0x01,0x80,0x18,0x00, +0x6B,0x01,0x00,0x00,0x16,0x00,0x53,0x53,0x43,0x20,0x52,0x41,0x4D,0x20,0x41,0x4C, +0x4C,0x00,0x01,0x00,0x90,0x01,0xA9,0x76,0xED,0x10,0x02,0x69,0x03,0x13,0x03,0x31, +0x44,0x01,0x90,0x02,0xA9,0x76,0xD4,0x55,0xD0,0x8F,0x00,0x00,0x00,0x10,0x56,0xD0, +0x56,0x50,0xD0,0x8F,0x00,0x04,0x14,0x20,0x58,0xD0,0x58,0x51,0x3C,0x8F,0x00,0x01, +0x57,0xD0,0x57,0x52,0xD0,0x81,0x80,0xF5,0x52,0xFA,0xD0,0x58,0x51,0xD0,0x56,0x50, +0xD0,0x57,0x52,0xD0,0x01,0x55,0xD0,0x61,0x61,0xD0,0x81,0xC0,0x00,0x04,0xC0,0x04, +0x50,0xF5,0x52,0xF2,0xD0,0x58,0x51,0xD0,0x56,0x50,0xD0,0x57,0x52,0x90,0x03,0xA9, +0x76,0xD0,0xC0,0x00,0x04,0x55,0xD0,0x80,0x53,0xD1,0x53,0x55,0x13,0x03,0x31,0xE5, +0x00,0xC0,0x04,0x51,0xF5,0x52,0xEA,0x90,0x04,0xA9,0x76,0xD0,0x57,0x52,0xD0,0x58, +0x51,0xCB,0x8F,0x00,0xF0,0xFF,0xFF,0x51,0x53,0xD0,0x53,0x81,0xC0,0x04,0x53,0xF5, +0x52,0xF7,0xD0,0x57,0x52,0xD0,0x58,0x51,0xD0,0x81,0x80,0xF5,0x52,0xFA,0x90,0x05, +0xA9,0x76,0xD0,0x57,0x52,0xD0,0x8F,0xAA,0xAA,0xAA,0xAA,0x53,0xD0,0x58,0x51,0xD0, +0x53,0x81,0xF5,0x52,0xFA,0xD0,0x57,0x52,0xD2,0x53,0x54,0xD0,0x58,0x51,0xD0,0x61, +0x80,0xD0,0x54,0x81,0xF5,0x52,0xF7,0x90,0x06,0xA9,0x76,0xD0,0x57,0x52,0xD0,0x58, +0x51,0xD0,0x61,0x80,0xF5,0x52,0xFA,0x90,0x07,0xA9,0x76,0xD0,0x57,0x52,0xD0,0x58, +0x51,0xD0,0x56,0x50,0xD0,0x80,0x81,0xF5,0x52,0xFA,0xD0,0x57,0x52,0xD0,0x58,0x51, +0xCB,0x8F,0x00,0xF0,0xFF,0xFF,0x51,0x53,0x90,0x08,0xA9,0x76,0xD0,0x80,0x55,0xD1, +0x53,0x55,0x12,0x52,0xC0,0x04,0x51,0xC0,0x04,0x53,0xF5,0x52,0xEF,0xD0,0x57,0x52, +0xD0,0x58,0x51,0xD2,0x54,0x53,0xD0,0x80,0x55,0x90,0x09,0xA9,0x76,0xD1,0x55,0x54, +0x13,0x34,0x90,0x0A,0xA9,0x76,0xD1,0x55,0x53,0x12,0x2B,0xC0,0x04,0x51,0xF5,0x52, +0xE5,0xD0,0x57,0x52,0xD0,0x58,0x51,0xD0,0x80,0x55,0x90,0x0B,0xA9,0x76,0xD1,0x55, +0x53,0x13,0x13,0x90,0x0C,0xA9,0x76,0xD1,0x55,0x54,0x12,0x0A,0xC0,0x04,0x51,0xF5, +0x52,0xE5,0x94,0xA9,0x76,0x05,0x05,0x05,0x00,0x00,0x16,0x00,0xE9,0x00,0x00,0x00, +0x14,0x00,0x53,0x53,0x43,0x20,0x72,0x65,0x67,0x73,0x20,0x00,0x01,0x00,0x90,0x01, +0xA9,0x76,0xD0,0x8F,0x00,0x00,0x14,0x20,0x50,0xD0,0x60,0x51,0xD1,0x50,0x51,0x12, +0x48,0x90,0x02,0xA9,0x76,0xD0,0x9F,0x30,0x01,0x14,0x20,0x50,0xD1,0x50,0x8F,0x00, +0x40,0x08,0x20,0x12,0x34,0x90,0x03,0xA9,0x76,0xD0,0x9F,0x34,0x01,0x14,0x20,0x50, +0xD1,0x50,0x00,0x12,0x24,0x90,0x04,0xA9,0x76,0xD0,0x9F,0x40,0x01,0x14,0x20,0x50, +0xD1,0x50,0x8F,0x04,0x40,0x08,0x20,0x12,0x10,0x90,0x05,0xA9,0x76,0xD0,0x9F,0x44, +0x01,0x14,0x20,0x50,0xD1,0x50,0x00,0x13,0x17,0x31,0x72,0x00,0xFF,0xFF,0xFF,0x00, +0x00,0x00,0x00,0x00,0xAA,0xAA,0xAA,0x00,0x55,0x55,0x55,0x00,0x04,0x00,0x00,0x00, +0x90,0x06,0xA9,0x76,0xDE,0xAF,0xE5,0x54,0xC8,0x8F,0x00,0x00,0x00,0xC0,0x9F,0x20, +0x00,0x14,0x20,0xD0,0x84,0x50,0xD0,0x50,0x9F,0x20,0x00,0x14,0x20,0xD0,0x9F,0x20, +0x00,0x14,0x20,0x51,0xD1,0x50,0x51,0x12,0x35,0xD1,0x50,0x04,0x12,0xE5,0x90,0x07, +0xA9,0x76,0xD0,0x8F,0x77,0x50,0xD4,0x00,0x50,0xEF,0x04,0x03,0x9F,0x04,0x40,0x08, +0x20,0x54,0xF0,0x54,0x0C,0x03,0x50,0xEF,0x00,0x1F,0x9F,0x10,0x00,0x14,0x20,0x51, +0xF0,0x00,0x08,0x03,0x51,0xD1,0x51,0x50,0x12,0x04,0x94,0xA9,0x76,0x05,0x10,0x01, +0x05,0xD0,0x04,0x9F,0x20,0x00,0x14,0x20,0x05,0x07,0x80,0x30,0x00,0xC3,0x01,0x18, +0x00,0x29,0x00,0x56,0x69,0x72,0x74,0x75,0x61,0x6C,0x20,0x6D,0x6F,0x64,0x65,0x20, +0x00,0x04,0x20,0x00,0xAF,0x01,0x24,0x00,0xC4,0x01,0x4C,0x00,0xD7,0x01,0x40,0x00, +0xEB,0x01,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0xD0,0x8F,0xF8,0x41,0x00,0x10,0xA9, +0x30,0xD0,0x8F,0xFC,0x41,0x00,0x10,0xA9,0x34,0x90,0x01,0xA9,0x76,0x16,0xEF,0xC6, +0x01,0x00,0x00,0xDA,0x00,0x38,0x90,0x02,0xA9,0x76,0xDA,0x00,0x39,0xD0,0x8F,0x10, +0x00,0x08,0xA0,0xB9,0x30,0xD0,0x8F,0x11,0x00,0x08,0xA0,0xB9,0x34,0xDA,0x01,0x38, +0x11,0x00,0xD0,0x8F,0x00,0xFC,0x1F,0x20,0x57,0xD0,0x8F,0x00,0xFE,0x1F,0x20,0x58, +0xD4,0x67,0xD4,0x68,0xDE,0xAF,0xEB,0x56,0xDA,0x56,0x3F,0x1C,0x12,0xDA,0x57,0x3F, +0x1C,0x0D,0xDA,0x58,0x3F,0x1C,0x08,0xDA,0x58,0x3A,0xDA,0x58,0x3F,0x1C,0x03,0x31, +0x26,0x01,0x90,0x03,0xA9,0x76,0x16,0xEF,0x71,0xD1,0xFF,0xFF,0xDA,0x00,0x38,0xD0, +0x8F,0x10,0x00,0x08,0xA0,0xB9,0x30,0xD0,0x8F,0x11,0x00,0x08,0xA0,0xB9,0x34,0xDA, +0x01,0x38,0xDE,0xAF,0xFD,0x56,0xD0,0x8F,0xF9,0xFD,0x1F,0x20,0x57,0xD0,0x09,0x58, +0x28,0x58,0x66,0x67,0x90,0x04,0xA9,0x76,0xDA,0x00,0x38,0xDE,0xAF,0xE4,0x56,0xD0, +0x8F,0xF9,0x21,0x00,0x10,0x57,0x91,0x86,0x87,0x12,0x4B,0xF5,0x58,0xF8,0x90,0x05, +0xA9,0x76,0xE5,0x1A,0xB9,0x30,0x3F,0xE5,0x1A,0xB9,0x34,0x3A,0x90,0x06,0xA9,0x76, +0xDA,0x8F,0xFF,0x0F,0x10,0x00,0x09,0xD4,0xA9,0x28,0xDA,0x00,0x39,0xDA,0x01,0x38, +0x16,0xEF,0xFF,0xD0,0xFF,0xFF,0xDE,0xAF,0xA9,0x56,0xD0,0x8F,0xF9,0xFD,0x1F,0x20, +0x57,0xD0,0x09,0x58,0x28,0x58,0x66,0x67,0x90,0x07,0xA9,0x76,0xE1,0x00,0xA9,0x28, +0x05,0xE0,0x02,0xA9,0x28,0x03,0x31,0x8F,0x00,0x90,0x08,0xA9,0x76,0xDA,0x00,0x38, +0xDA,0x8F,0x00,0x10,0x10,0x00,0x09,0xD0,0x8F,0x11,0x00,0x08,0x20,0xB9,0x34,0xD4, +0xA9,0x2C,0xDA,0x00,0x39,0xDA,0x01,0x38,0x16,0xEF,0xB7,0xD0,0xFF,0xFF,0xDE,0xCF, +0x60,0xFF,0x56,0xD0,0x8F,0xF9,0xFD,0x1F,0x20,0x57,0xD0,0x09,0x58,0x28,0x58,0x66, +0x67,0x90,0x09,0xA9,0x76,0xD5,0xA9,0x2C,0x13,0xBC,0x90,0x0A,0xA9,0x76,0xDA,0x00, +0x38,0xD0,0x8F,0x11,0x00,0x08,0x98,0xB9,0x34,0xD4,0xA9,0x28,0xDA,0x00,0x39,0xDA, +0x01,0x38,0x16,0xEF,0x7D,0xD0,0xFF,0xFF,0xDE,0xCF,0x26,0xFF,0x56,0xD0,0x8F,0xF9, +0xFD,0x1F,0x20,0x57,0xD0,0x09,0x58,0x28,0x58,0x66,0x67,0x90,0x0B,0xA9,0x76,0xE0, +0x00,0xA9,0x28,0x05,0xE0,0x02,0xA9,0x28,0x03,0x31,0x0C,0x00,0x11,0x03,0x31,0x07, +0x00,0xDA,0x00,0x38,0x94,0xA9,0x76,0x05,0xDA,0x00,0x38,0x05,0xDA,0x00,0x38,0x05, +0xDE,0xBE,0x04,0x50,0xD0,0xA0,0xF8,0xA9,0x28,0xCA,0x8F,0x00,0x00,0x00,0x08,0xA0, +0x04,0xC0,0x04,0x60,0x05,0xDE,0xBE,0x04,0x50,0xD6,0xA9,0x2C,0xCA,0x8F,0x00,0x00, +0x00,0x08,0xA0,0x04,0xC0,0x04,0x60,0x05,0xDE,0xBE,0x04,0x50,0xC8,0x8F,0x00,0x00, +0xC0,0x03,0xA0,0x04,0xDE,0xA0,0x08,0x51,0xDA,0x51,0x03,0x05,0xDE,0xBE,0x04,0x50, +0xCA,0x8F,0x00,0x00,0xC0,0x03,0xA0,0x04,0x05,0xBB,0x07,0xD4,0x50,0xD0,0x8F,0x01, +0x00,0x08,0xA0,0x51,0xD0,0x8F,0x00,0x00,0x00,0x10,0x52,0xD0,0x51,0x82,0xD6,0x51, +0xF3,0x20,0x50,0xF7,0xDA,0x8F,0x00,0x80,0xFF,0x0F,0x0C,0xDA,0x8F,0x20,0x20,0x00, +0x00,0x0D,0xD4,0x50,0xD0,0x8F,0x00,0x00,0x10,0xA0,0x51,0xD0,0x8F,0x00,0x02,0x00, +0x10,0x52,0xD0,0x51,0x82,0xD6,0x51,0xF2,0x8F,0x00,0x10,0x00,0x00,0x50,0xF3,0xDA, +0x8F,0x00,0x00,0x00,0x80,0x08,0xDA,0x8F,0x00,0x10,0x10,0x00,0x09,0xC8,0x8F,0x00, +0x00,0x00,0x80,0x69,0xDA,0x01,0x38,0xBA,0x07,0x05,0xDA,0x00,0x38,0xCA,0x8F,0x00, +0x00,0x00,0x80,0x69,0x05,0x00,0x00,0x00,0x01,0x80,0x77,0x00,0xE5,0x04,0x18,0x00, +0x21,0x00,0x63,0x61,0x63,0x68,0x65,0x32,0x5F,0x6D,0x65,0x6D,0x6F,0x72,0x79,0x00, +0x02,0x04,0x00,0x30,0x05,0x54,0x00,0x39,0x05,0x0A,0x03,0x38,0x00,0x02,0x00,0x00, +0x00,0x00,0xFC,0xFF,0xFF,0x03,0x00,0x00,0x01,0x00,0x03,0x43,0x00,0x02,0x00,0x00, +0x00,0x00,0xFC,0xFF,0xFF,0x03,0xFC,0xFF,0x02,0x00,0x03,0x4C,0x00,0x02,0x04,0x00, +0x00,0x00,0xFC,0xFF,0xFF,0x03,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x73,0x74,0x61,0x72,0x74,0x5F,0x61,0x64,0x64,0x72,0x00,0x65,0x6E,0x64,0x5F, +0x61,0x64,0x64,0x72,0x00,0x61,0x64,0x64,0x72,0x5F,0x69,0x6E,0x63,0x72,0x00,0x31, +0x46,0x04,0x01,0x01,0x01,0xCA,0x03,0xA9,0x30,0xD0,0x8F,0x2C,0x04,0x05,0x20,0xA9, +0x34,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x01,0xA9,0x76,0xD5,0xA9,0x1C,0x12,0x03, +0x31,0x29,0x04,0x90,0x02,0xA9,0x76,0xD0,0xA9,0x28,0x56,0xB4,0x56,0xD0,0xA9,0x2C, +0x58,0xB0,0x8F,0xFC,0xFF,0x58,0xDA,0x10,0x25,0xDA,0x00,0x27,0xD0,0x20,0x9F,0x00, +0x40,0x08,0x20,0xD0,0x56,0x57,0xB0,0x8F,0xFC,0xFF,0x57,0xD0,0x56,0xA9,0x48,0xD0, +0x56,0x50,0xCA,0x07,0x50,0xEF,0x09,0x17,0x50,0xAE,0xFC,0xE1,0xAE,0xFC,0xB9,0x1C, +0x0A,0xC1,0x04,0x50,0x51,0xD0,0x50,0x60,0xD0,0x51,0x61,0xC0,0xA9,0x30,0x56,0xD1, +0x56,0x57,0x1B,0xD7,0xD1,0x56,0x58,0x1B,0xCA,0x90,0x03,0xA9,0x76,0xD0,0xA9,0x28, +0x52,0xB4,0x52,0xC0,0x8F,0x00,0x00,0x00,0x10,0x52,0xD0,0x52,0x57,0xB0,0x8F,0xFC, +0xFF,0x57,0xD0,0x21,0x9F,0x00,0x40,0x08,0x20,0xD0,0x52,0x50,0xCA,0x07,0x50,0xC1, +0x04,0x50,0x51,0x7D,0x50,0x60,0xC0,0xA9,0x30,0x52,0xD1,0x52,0x57,0x1B,0xEA,0x11, +0x03,0x31,0x98,0x03,0xD0,0xA9,0x28,0x52,0xB4,0x52,0x90,0x04,0xA9,0x76,0x30,0xD7, +0x03,0x90,0x05,0xA9,0x76,0xD5,0xA9,0x4C,0x12,0xE7,0x90,0x06,0xA9,0x76,0xD5,0x50, +0x12,0xDF,0xD0,0xA9,0x28,0x56,0xB4,0x56,0xD0,0x56,0x58,0xB0,0x8F,0xFC,0xFF,0x58, +0xD0,0xA9,0x28,0x54,0xB4,0x54,0xC0,0x8F,0x00,0x00,0x00,0x10,0x54,0xD0,0x56,0xA9, +0x48,0xEF,0x09,0x17,0x56,0xAE,0xFC,0xE1,0xAE,0xFC,0xB9,0x1C,0x1D,0xD0,0x10,0x9F, +0x00,0x40,0x08,0x20,0x90,0x07,0xA9,0x76,0xD0,0x66,0x55,0xD4,0x9F,0x00,0x40,0x08, +0x20,0x90,0x08,0xA9,0x76,0xD1,0x55,0x54,0x12,0x97,0xC0,0xA9,0x30,0x54,0xC0,0xA9, +0x30,0x56,0xD1,0x56,0x58,0x1B,0xC6,0xD0,0xA9,0x28,0x56,0xB4,0x56,0xD0,0xA9,0x2C, +0x58,0xB0,0x8F,0xFC,0xFF,0x58,0xD0,0xA9,0x30,0x53,0xD3,0x07,0x53,0x13,0x06,0xCA, +0x07,0x53,0xC0,0x08,0x53,0xC2,0x04,0x53,0x90,0x09,0xA9,0x76,0x30,0x73,0x0B,0xDA, +0x00,0x27,0xD0,0x56,0x57,0xB0,0x8F,0xFC,0xFF,0x57,0xD0,0x56,0xA9,0x48,0xEF,0x09, +0x17,0x56,0xAE,0xFC,0x90,0x0A,0xA9,0x76,0xE1,0xAE,0xFC,0xB9,0x1C,0x46,0xDA,0x8F, +0x50,0x00,0x00,0x00,0x25,0xD0,0x10,0x9F,0x00,0x40,0x08,0x20,0x90,0x0B,0xA9,0x76, +0xD0,0x66,0x55,0xDA,0x00,0x25,0xD4,0x9F,0x00,0x40,0x08,0x20,0x90,0x0C,0xA9,0x76, +0x30,0x57,0x03,0x13,0x03,0x31,0xB4,0x02,0x90,0x0D,0xA9,0x76,0x30,0x19,0x09,0xD0, +0x04,0xA9,0x4C,0x30,0x50,0x09,0xD5,0xA9,0x4C,0x12,0x73,0x90,0x0E,0xA9,0x76,0xD1, +0x55,0x56,0x12,0x6A,0xC0,0x04,0x56,0xD0,0x56,0x54,0xD0,0x56,0xA9,0x48,0xEF,0x09, +0x17,0x56,0xAE,0xFC,0x90,0x0F,0xA9,0x76,0xE1,0xAE,0xFC,0xB9,0x1C,0x3B,0x90,0x10, +0xA9,0x76,0xD0,0x66,0x52,0xD4,0x66,0xDA,0x10,0x25,0xD0,0x10,0x9F,0x00,0x40,0x08, +0x20,0xD0,0x66,0x55,0xD4,0x9F,0x00,0x40,0x08,0x20,0xD0,0x52,0x66,0x90,0x11,0xA9, +0x76,0x30,0xC4,0x08,0xD0,0x04,0xA9,0x4C,0x30,0xFB,0x08,0xD5,0xA9,0x4C,0x12,0x1E, +0x90,0x12,0xA9,0x76,0xD1,0x55,0x56,0x12,0x15,0xC0,0x53,0x56,0xD1,0x56,0x57,0x1A, +0x03,0x31,0x46,0xFF,0xD1,0x56,0x58,0x1A,0x03,0x31,0x36,0xFF,0x11,0x03,0x31,0x2B, +0x02,0xD0,0x54,0x56,0xD0,0xA9,0x28,0x58,0xB4,0x58,0xD0,0xA9,0x30,0x53,0xD3,0x07, +0x53,0x13,0x06,0xCA,0x07,0x53,0xC0,0x08,0x53,0xC2,0x04,0x53,0x90,0x13,0xA9,0x76, +0x30,0x7F,0x0A,0xDA,0x00,0x27,0xD0,0x56,0x57,0xB4,0x57,0xD0,0x56,0xA9,0x48,0xEF, +0x09,0x17,0x56,0xAE,0xFC,0x90,0x14,0xA9,0x76,0xE1,0xAE,0xFC,0xB9,0x1C,0x46,0xDA, +0x8F,0x50,0x00,0x00,0x00,0x25,0xD0,0x10,0x9F,0x00,0x40,0x08,0x20,0x90,0x15,0xA9, +0x76,0xD0,0x66,0x55,0xDA,0x00,0x25,0xD4,0x9F,0x00,0x40,0x08,0x20,0x90,0x16,0xA9, +0x76,0x30,0x66,0x02,0x13,0x03,0x31,0xC3,0x01,0x90,0x17,0xA9,0x76,0x30,0x28,0x08, +0xD0,0x04,0xA9,0x4C,0x30,0x5F,0x08,0xD5,0xA9,0x4C,0x12,0x6C,0x90,0x18,0xA9,0x76, +0xD1,0x55,0x56,0x12,0x63,0xC2,0x04,0x56,0xD0,0x56,0xA9,0x48,0xEF,0x09,0x17,0x56, +0xAE,0xFC,0x90,0x19,0xA9,0x76,0xE1,0xAE,0xFC,0xB9,0x1C,0x37,0xD0,0x66,0x52,0xD4, +0x66,0xDA,0x10,0x25,0xD0,0x10,0x9F,0x00,0x40,0x08,0x20,0xD0,0x66,0x55,0xD4,0x9F, +0x00,0x40,0x08,0x20,0xD0,0x52,0x66,0x90,0x1A,0xA9,0x76,0x30,0xDA,0x07,0xD0,0x04, +0xA9,0x4C,0x30,0x11,0x08,0xD5,0xA9,0x4C,0x12,0x1E,0x90,0x1B,0xA9,0x76,0xD1,0x55, +0x56,0x12,0x15,0xC2,0x53,0x56,0xD1,0x56,0x57,0x19,0x03,0x31,0x4D,0xFF,0xD1,0x56, +0x58,0x19,0x03,0x31,0x40,0xFF,0x11,0x03,0x31,0x41,0x01,0xD0,0xA9,0x28,0x56,0xB4, +0x56,0xC0,0x8F,0x00,0x00,0x01,0x00,0x56,0xD0,0xA9,0x30,0x53,0xD3,0x07,0x53,0x13, +0x06,0xCA,0x07,0x53,0xC0,0x08,0x53,0xDA,0x00,0x27,0xD0,0x8F,0x2C,0x04,0x05,0x20, +0xA9,0x34,0xD0,0x56,0x57,0xB0,0x8F,0xF8,0xFF,0x57,0x94,0xA9,0x38,0x94,0xA9,0x39, +0xD4,0xA9,0x44,0xD0,0x56,0xA9,0x48,0xEF,0x09,0x17,0x56,0xAE,0xFC,0x90,0x1C,0xA9, +0x76,0xE1,0xAE,0xFC,0xB9,0x1C,0x68,0x16,0xEF,0xB8,0xCB,0xFF,0xFF,0xDA,0x8F,0x50, +0x00,0x00,0x00,0x25,0xD0,0x10,0x9F,0x00,0x40,0x08,0x20,0xD0,0x56,0x66,0xD0,0x66, +0x55,0xDA,0x00,0x25,0xD4,0x9F,0x00,0x40,0x08,0x20,0x16,0xEF,0x9D,0xCB,0xFF,0xFF, +0x90,0x1D,0xA9,0x76,0x30,0x63,0x01,0x12,0x43,0x90,0x1E,0xA9,0x76,0x91,0x00,0xA9, +0x38,0x12,0x39,0x90,0x1F,0xA9,0x76,0x91,0x00,0xA9,0x39,0x12,0x2F,0xD3,0x20,0xA9, +0x44,0x12,0x03,0x30,0x12,0x07,0x90,0x20,0xA9,0x76,0xD0,0x04,0xA9,0x4C,0x30,0x45, +0x07,0xD5,0xA9,0x4C,0x12,0x16,0x90,0x21,0xA9,0x76,0xD1,0x55,0x56,0x12,0x0D,0xC0, +0x53,0x56,0xD1,0x56,0x57,0x1A,0x03,0x31,0x76,0xFF,0x11,0x03,0x31,0x7D,0x00,0xD0, +0xA9,0x28,0x56,0xB4,0x56,0xD0,0x56,0x57,0xB0,0x8F,0xF8,0xFF,0x57,0xEF,0x09,0x17, +0x56,0xAE,0xFC,0x90,0x22,0xA9,0x76,0xE1,0xAE,0xFC,0xB9,0x1C,0x4B,0x16,0xEF,0x22, +0xCB,0xFF,0xFF,0xD0,0x56,0xA9,0x48,0xD0,0x66,0x55,0x16,0xEF,0x1D,0xCB,0xFF,0xFF, +0x90,0x23,0xA9,0x76,0x30,0xE3,0x00,0x12,0x3C,0x90,0x24,0xA9,0x76,0x91,0x00,0xA9, +0x38,0x12,0x32,0x90,0x25,0xA9,0x76,0x91,0x00,0xA9,0x39,0x12,0x28,0x90,0x26,0xA9, +0x76,0x30,0x94,0x06,0xD4,0xA9,0x4C,0x30,0xCC,0x06,0xD5,0xA9,0x4C,0x12,0x16,0x90, +0x27,0xA9,0x76,0xD1,0x55,0x56,0x12,0x0D,0xC0,0x53,0x56,0xD1,0x56,0x57,0x1A,0x03, +0x31,0x9A,0xFF,0x11,0x03,0x31,0x04,0x00,0x94,0xA9,0x76,0x05,0x30,0x69,0x06,0xDB, +0x25,0x7E,0x9A,0x6E,0xA9,0x40,0xDB,0x27,0x6E,0x90,0x6E,0xA9,0x41,0xD5,0x8E,0xDA, +0x00,0x25,0xD0,0x20,0x9F,0x00,0x40,0x08,0x20,0xDA,0x19,0x12,0x05,0xED,0x10,0x02, +0x69,0x02,0x13,0x10,0xF0,0x02,0x10,0x02,0x69,0x91,0x00,0xA9,0x76,0x12,0x05,0xF0, +0x03,0x10,0x02,0x69,0xDA,0x00,0x27,0xD0,0x20,0x9F,0x00,0x40,0x08,0x20,0x30,0x31, +0x08,0xDA,0x00,0x25,0xDA,0x19,0x12,0x05,0xD0,0x52,0x53,0xB0,0x8F,0xFC,0xFF,0x53, +0xD4,0x50,0xD4,0x51,0xD0,0x52,0xA9,0x48,0x30,0x0D,0x06,0xD4,0xA9,0x4C,0x30,0x45, +0x06,0xD5,0xA9,0x4C,0x12,0x19,0xD3,0x8F,0x00,0x00,0x04,0x00,0xA9,0x44,0x12,0x04, +0xD6,0x51,0x11,0x02,0xD6,0x50,0xC0,0xA9,0x30,0x52,0xD1,0x52,0x53,0x1B,0xD5,0x05, +0x96,0xA9,0x38,0xD0,0xA9,0x34,0xBE,0x04,0x05,0x96,0xA9,0x39,0x30,0xD9,0x05,0x30, +0xE0,0x07,0xD0,0x20,0x9F,0x00,0x40,0x08,0x20,0x05,0xBB,0x01,0xDB,0x27,0x50,0xDA, +0x00,0x27,0xD3,0x8F,0x60,0x00,0x00,0x00,0x50,0xBA,0x01,0x05,0x01,0x80,0x78,0x00, +0x91,0x05,0x19,0x00,0x22,0x00,0x43,0x61,0x63,0x68,0x32,0x5F,0x69,0x6E,0x74,0x65, +0x67,0x72,0x74,0x79,0x00,0x02,0x04,0x00,0xD0,0x07,0x54,0x00,0xD9,0x07,0x0A,0x03, +0x38,0x00,0x02,0x00,0x00,0x00,0x10,0xF8,0xFF,0xFF,0x13,0x00,0x00,0x00,0x10,0x03, +0x43,0x00,0x02,0x00,0x00,0x00,0x10,0xF8,0xFF,0xFF,0x13,0x00,0x20,0x01,0x10,0x03, +0x4C,0x00,0x02,0x08,0x00,0x00,0x00,0xF8,0xFF,0xFF,0x03,0x00,0x20,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x61,0x72,0x74,0x5F,0x61,0x64,0x64,0x72, +0x00,0x65,0x6E,0x64,0x5F,0x61,0x64,0x64,0x72,0x00,0x61,0x64,0x64,0x72,0x5F,0x69, +0x6E,0x63,0x72,0x00,0x31,0xF1,0x04,0x01,0x01,0xCB,0x07,0xA9,0x28,0xA9,0x28,0xCB, +0x07,0xA9,0x2C,0xA9,0x2C,0xCB,0x07,0xA9,0x30,0xA9,0x30,0x16,0xEF,0x8C,0xC9,0xFF, +0xFF,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x01,0xA9,0x76,0x30,0x08,0x07,0xC8,0x04, +0x9F,0x00,0x40,0x08,0x20,0xD0,0x01,0x54,0xD0,0x56,0xA9,0x48,0xD2,0x8F,0x8B,0x2E, +0x8B,0x2E,0x57,0xD0,0x08,0x52,0xD0,0x02,0x53,0x90,0x02,0xA9,0x76,0xD0,0x54,0x66, +0x90,0x03,0xA9,0x76,0xD0,0x66,0x51,0xDB,0x27,0x50,0x30,0xEB,0x04,0x90,0x04,0xA9, +0x76,0x93,0x8F,0x60,0x50,0x12,0x4B,0xDA,0x00,0x27,0x90,0x05,0xA9,0x76,0xD1,0x51, +0x54,0x12,0x3F,0x91,0x03,0x9F,0x05,0x00,0x04,0x20,0x12,0x14,0xEF,0x00,0x04,0x57, +0x50,0x90,0x06,0xA9,0x76,0xEF,0x18,0x04,0xA9,0x44,0x58,0x91,0x58,0x50,0x12,0x22, +0xD2,0x54,0x54,0xF5,0x53,0xB3,0x78,0x01,0x54,0x54,0xF5,0x52,0xA9,0x78,0x8F,0xFC, +0x57,0x57,0xD5,0x54,0x12,0x9D,0xC0,0xA9,0x30,0x56,0xD1,0x56,0xA9,0x2C,0x1B,0x85, +0x11,0x03,0x31,0x47,0x04,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x07,0xA9,0x76,0x30, +0x74,0x06,0xC8,0x04,0x9F,0x00,0x40,0x08,0x20,0x90,0x08,0xA9,0x76,0xD2,0x00,0x86, +0xD2,0x00,0x86,0x90,0x09,0xA9,0x76,0xD0,0x07,0x51,0x90,0x51,0x76,0xF4,0x51,0xFA, +0x90,0x0A,0xA9,0x76,0x7D,0x66,0x52,0xD0,0x56,0xA9,0x48,0x30,0xA1,0x06,0x12,0x26, +0x90,0x0B,0xA9,0x76,0xD1,0x52,0x8F,0x00,0x01,0x02,0x03,0x12,0x19,0x90,0x0C,0xA9, +0x76,0xD1,0x53,0x8F,0x04,0x05,0x06,0x07,0x12,0x0C,0xC0,0xA9,0x30,0x56,0xD1,0x56, +0xA9,0x2C,0x1B,0xB5,0x11,0x03,0x31,0xE3,0x03,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90, +0x0D,0xA9,0x76,0x30,0x10,0x06,0xF0,0x00,0x00,0x03,0x56,0xD0,0x56,0x58,0xF0,0x8F, +0xF8,0xFF,0x00,0x00,0x00,0x10,0x58,0xD1,0x58,0xA9,0x2C,0x1B,0x04,0xD0,0xA9,0x2C, +0x58,0xC2,0x56,0x58,0xC0,0x08,0x58,0x78,0x8F,0xFD,0x58,0x58,0xD0,0x8F,0xAA,0xAA, +0xAA,0xAA,0x57,0xD0,0x57,0x51,0xD0,0x51,0x52,0xD0,0x56,0x50,0xD0,0x58,0x55,0x90, +0x0E,0xA9,0x76,0x7D,0x51,0x80,0xF5,0x55,0xFA,0x11,0x03,0x31,0x8E,0x03,0x78,0x8F, +0xFF,0x58,0x58,0xC8,0x04,0x9F,0x00,0x40,0x08,0x20,0xD2,0x57,0x51,0xD0,0x56,0x50, +0xD0,0x58,0x55,0x90,0x0F,0xA9,0x76,0xD0,0x50,0xA9,0x48,0xD0,0x60,0x53,0xDB,0x27, +0x52,0x30,0xB4,0x03,0x90,0x10,0xA9,0x76,0x93,0x8F,0x60,0x52,0x12,0xCD,0xDA,0x00, +0x27,0x90,0x11,0xA9,0x76,0xD1,0x53,0x57,0x12,0xC1,0x91,0x03,0x9F,0x05,0x00,0x04, +0x20,0x12,0x0F,0x90,0x12,0xA9,0x76,0xEF,0x18,0x04,0xA9,0x44,0x54,0xD1,0x54,0x05, +0x12,0xA9,0x90,0x13,0xA9,0x76,0xD0,0x51,0x80,0xF5,0x55,0xB7,0xD0,0x58,0x55,0xD0, +0x56,0x50,0xD0,0x8F,0x7F,0x7F,0x7F,0x7F,0x51,0xD2,0x57,0x57,0x90,0x14,0xA9,0x76, +0xD0,0x50,0xA9,0x48,0xD0,0x60,0x53,0xDB,0x27,0x52,0x30,0x5B,0x03,0x90,0x15,0xA9, +0x76,0x93,0x8F,0x60,0x52,0x13,0x03,0x31,0x02,0x03,0xDA,0x00,0x27,0x90,0x16,0xA9, +0x76,0xD1,0x53,0x57,0x12,0xF1,0x91,0x03,0x9F,0x05,0x00,0x04,0x20,0x12,0x0F,0x90, +0x17,0xA9,0x76,0xEF,0x18,0x04,0xA9,0x44,0x54,0xD1,0x54,0x05,0x12,0xD9,0x90,0x18, +0xA9,0x76,0xD0,0x51,0x80,0xF5,0x55,0xB4,0xD0,0x58,0x55,0xD0,0x56,0x50,0x90,0x19, +0xA9,0x76,0xD0,0x50,0xA9,0x48,0xD0,0x60,0x53,0xDB,0x27,0x52,0x30,0x09,0x03,0x90, +0x1A,0xA9,0x76,0x93,0x8F,0x60,0x52,0x12,0x38,0xDA,0x00,0x27,0x90,0x1B,0xA9,0x76, +0xD1,0x53,0x51,0x12,0x2C,0x91,0x03,0x9F,0x05,0x00,0x04,0x20,0x12,0x0F,0x90,0x1C, +0xA9,0x76,0xEF,0x18,0x04,0xA9,0x44,0x54,0xD1,0x54,0x0A,0x12,0x14,0xC0,0x04,0x50, +0xF5,0x55,0xBB,0xD0,0x50,0x56,0xD1,0x56,0xA9,0x2C,0x1A,0x03,0x31,0xAC,0xFE,0x11, +0x03,0x31,0x78,0x02,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x1D,0xA9,0x76,0x30,0xA5, +0x04,0x7D,0x8F,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x50,0x30,0xC0,0x00,0xD3, +0x20,0xA9,0x44,0x12,0x03,0x30,0xA0,0x02,0x90,0x1E,0xA9,0x76,0x30,0xD7,0x02,0xD1, +0x8F,0x00,0x00,0x08,0x00,0xA9,0x4C,0x12,0x34,0x90,0x1F,0xA9,0x76,0xDB,0x27,0x55, +0xCA,0x8F,0x80,0x00,0x00,0x00,0x55,0xD5,0x55,0x12,0x22,0x90,0x20,0xA9,0x76,0xDB, +0x25,0x55,0xD1,0x1C,0x55,0x12,0x16,0x90,0x21,0xA9,0x76,0x91,0x01,0xA9,0x38,0x1F, +0x0C,0x90,0x22,0xA9,0x76,0x91,0x01,0xA9,0x39,0x12,0x02,0x11,0x03,0x31,0x0C,0x02, +0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x23,0xA9,0x76,0x30,0x39,0x04,0x7D,0x8F,0x01, +0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x50,0x30,0x54,0x00,0xD3,0x20,0xA9,0x44,0x12, +0x03,0x30,0x34,0x02,0x90,0x24,0xA9,0x76,0x30,0x6B,0x02,0xD1,0x8F,0x00,0x00,0x08, +0x00,0xA9,0x4C,0x12,0x34,0x90,0x25,0xA9,0x76,0xDB,0x27,0x55,0xCA,0x8F,0x80,0x00, +0x00,0x00,0x55,0xD5,0x55,0x12,0x22,0x90,0x26,0xA9,0x76,0xDB,0x25,0x55,0xD1,0x1C, +0x55,0x12,0x16,0x90,0x27,0xA9,0x76,0x91,0x01,0xA9,0x38,0x1F,0x0C,0x90,0x28,0xA9, +0x76,0x91,0x01,0xA9,0x39,0x12,0x02,0x11,0x03,0x31,0xA0,0x01,0x31,0x64,0x00,0x94, +0xA9,0x38,0x94,0xA9,0x39,0xDA,0x19,0x12,0xD4,0xA9,0x44,0xD0,0x04,0xA9,0x4C,0xDE, +0xEF,0x3D,0x00,0x00,0x00,0xA9,0x34,0xD0,0x03,0x9F,0x00,0x40,0x08,0x20,0xD0,0x56, +0xA9,0x48,0x90,0x01,0xA9,0x3A,0x7D,0x50,0x66,0xCA,0x8F,0x00,0x00,0x00,0x10,0x56, +0xCA,0x02,0x9F,0x00,0x40,0x08,0x20,0xCA,0x01,0x9F,0x00,0x40,0x08,0x20,0x16,0xEF, +0x01,0xC6,0xFF,0xFF,0xD0,0x10,0x9F,0x00,0x40,0x08,0x20,0x90,0x02,0xA9,0x3A,0xD0, +0x66,0x58,0xD4,0x9F,0x00,0x40,0x08,0x20,0x16,0xEF,0xEF,0xC5,0xFF,0xFF,0x90,0x03, +0xA9,0x3A,0x05,0x90,0x29,0xA9,0x76,0x30,0x6C,0x03,0x94,0xA9,0x39,0xD4,0x52,0x7D, +0x8F,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0xAE,0xF8,0xE9,0x52,0x06,0x94,0x42, +0xAE,0xF8,0x11,0x05,0x90,0x01,0x42,0xAE,0xF8,0x7D,0xAE,0xF8,0x50,0x94,0xA9,0x38, +0xDE,0xEF,0x1C,0x00,0x00,0x00,0xA9,0x34,0xD0,0x23,0x9F,0x00,0x40,0x08,0x20,0x7D, +0x50,0x66,0xD0,0x21,0x9F,0x00,0x40,0x08,0x20,0xF0,0x01,0x1D,0x01,0x69,0x7D,0x66, +0xAE,0xF8,0xD4,0x9F,0x00,0x40,0x08,0x20,0xF0,0x00,0x1D,0x01,0x69,0x90,0x2A,0xA9, +0x76,0xDB,0x27,0x55,0x8A,0x8F,0x80,0x55,0x91,0x55,0x8F,0x60,0x13,0x03,0x31,0xCB, +0x00,0x90,0x2B,0xA9,0x76,0x91,0xA9,0x38,0x01,0x12,0xF3,0xDA,0x00,0x27,0xD6,0x52, +0xD1,0x52,0x08,0x1E,0x03,0x31,0x87,0xFF,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x2C, +0xA9,0x76,0x30,0xE1,0x02,0x7D,0x8F,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x50, +0x90,0x2D,0xA9,0x76,0x7D,0x50,0x60,0xC0,0x8F,0x08,0x00,0x01,0x00,0x50,0xD1,0x50, +0x8F,0xF8,0x1F,0xFF,0x13,0x1B,0xE9,0x7D,0x8F,0x00,0x00,0x00,0x10,0x00,0x00,0x00, +0x00,0x50,0x90,0x2E,0xA9,0x76,0x7D,0x60,0x52,0xD0,0x50,0xA9,0x48,0x90,0x2F,0xA9, +0x76,0x30,0xFB,0x02,0x12,0x24,0x90,0x30,0xA9,0x76,0xD1,0x52,0x50,0x12,0x1B,0x90, +0x31,0xA9,0x76,0xD1,0x53,0x00,0x12,0x12,0xC0,0x8F,0x08,0x00,0x01,0x00,0x50,0xD1, +0x50,0x8F,0xF8,0x1F,0xFF,0x13,0x1B,0xCA,0x11,0x03,0x31,0x3F,0x00,0x7D,0x8F,0x00, +0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x50,0xD0,0x50,0xA9,0x48,0x90,0x32,0xA9,0x76, +0x30,0xBC,0x02,0x12,0xE5,0x90,0x33,0xA9,0x76,0x30,0x6C,0x00,0xD0,0x04,0xA9,0x4C, +0x30,0xA3,0x00,0xD5,0xA9,0x4C,0x12,0xD2,0xC0,0x8F,0x08,0x00,0x01,0x00,0x50,0xD1, +0x50,0x8F,0xF8,0x1F,0xFF,0x13,0x1B,0xD0,0x94,0xA9,0x76,0x05,0x30,0x49,0x00,0xDB, +0x25,0x7E,0x9A,0x6E,0xA9,0x40,0xDB,0x27,0x6E,0x90,0x6E,0xA9,0x41,0xD5,0x8E,0xDA, +0x00,0x25,0xD0,0x20,0x9F,0x00,0x40,0x08,0x20,0xDA,0x19,0x12,0x05,0xED,0x10,0x02, +0x69,0x02,0x13,0x10,0xF0,0x02,0x10,0x02,0x69,0x91,0x00,0xA9,0x76,0x12,0x05,0xF0, +0x03,0x10,0x02,0x69,0xDA,0x00,0x27,0xD0,0x20,0x9F,0x00,0x40,0x08,0x20,0x30,0x11, +0x02,0xDA,0x00,0x25,0xDA,0x19,0x12,0x05,0xBB,0x01,0xD0,0x8F,0x00,0x00,0x00,0x38, +0x9F,0x30,0x01,0x14,0x20,0xD0,0x8F,0xFC,0xFF,0xFF,0x03,0x9F,0x34,0x01,0x14,0x20, +0xEF,0x00,0x1A,0xA9,0x48,0x50,0xC0,0x8F,0x00,0x00,0x00,0x38,0x50,0xD0,0x60,0xA9, +0x44,0xD0,0x00,0x9F,0x34,0x01,0x14,0x20,0xD0,0x8F,0x00,0x40,0x08,0x20,0x9F,0x30, +0x01,0x14,0x20,0xBA,0x01,0x05,0xBB,0x1C,0xEF,0x10,0x0A,0xA9,0x48,0x52,0xD4,0x54, +0xD0,0x01,0x53,0xD3,0x53,0x52,0x13,0x02,0xD6,0x54,0x78,0x01,0x53,0x53,0xD1,0x53, +0x8F,0x00,0x02,0x00,0x00,0x1B,0xEC,0xD4,0x52,0xD3,0x01,0x54,0x12,0x02,0xD6,0x52, +0xEF,0x16,0x01,0xA9,0x44,0x53,0xD1,0x53,0x52,0x13,0x08,0xC8,0x8F,0x00,0x00,0x40, +0x00,0xA9,0x4C,0xD3,0x04,0xA9,0x4C,0x13,0x3A,0xEF,0x13,0x01,0xA9,0x44,0x53,0xD1, +0x53,0x52,0x13,0x08,0xC8,0x8F,0x00,0x00,0x08,0x00,0xA9,0x4C,0xEF,0x10,0x0A,0xA9, +0x48,0x52,0xEF,0x08,0x0A,0xA9,0x44,0x53,0xCC,0x52,0x53,0xF0,0x53,0x08,0x0A,0xA9, +0x4C,0xD3,0x8F,0x00,0x00,0x04,0x00,0xA9,0x44,0x13,0x08,0xC8,0x8F,0x00,0x00,0x04, +0x00,0xA9,0x4C,0xEF,0x08,0x0B,0xA9,0x44,0x52,0xD4,0x54,0xD0,0x01,0x53,0xD3,0x53, +0x52,0x13,0x02,0xD6,0x54,0x78,0x01,0x53,0x53,0xD1,0x53,0x8F,0x00,0x04,0x00,0x00, +0x1B,0xEC,0xD4,0x52,0xD3,0x01,0x54,0x12,0x02,0xD6,0x52,0xD3,0x04,0xA9,0x4C,0x13, +0x07,0xCA,0x04,0xA9,0x4C,0x31,0x15,0x00,0xEF,0x13,0x01,0xA9,0x44,0x53,0xD1,0x53, +0x52,0x13,0x0A,0xF0,0x8F,0xFF,0x0F,0x00,0x00,0x08,0x0C,0xA9,0x4C,0xEF,0x13,0x01, +0xA9,0x44,0x53,0xC0,0x53,0x54,0xEF,0x00,0x01,0x54,0x52,0xEF,0x17,0x01,0xA9,0x44, +0x53,0xD1,0x53,0x52,0x13,0x08,0xC8,0x8F,0x00,0x00,0x80,0x00,0xA9,0x4C,0xD3,0x20, +0xA9,0x44,0x13,0x0E,0xD3,0x10,0xA9,0x44,0x13,0x08,0xC8,0x10,0xA9,0x4C,0xC8,0x20, +0xA9,0x4C,0xD3,0x01,0xA9,0x44,0x13,0x1C,0xD3,0x10,0xA9,0x44,0x13,0x08,0xC8,0x01, +0xA9,0x4C,0xC8,0x10,0xA9,0x4C,0xD3,0x20,0xA9,0x44,0x13,0x08,0xC8,0x01,0xA9,0x4C, +0xC8,0x20,0xA9,0x4C,0xD5,0xA9,0x4C,0x13,0x03,0x31,0x77,0x00,0xED,0x14,0x02,0xA9, +0x44,0x00,0x12,0x10,0xD3,0x8F,0x00,0x00,0x04,0x00,0xA9,0x44,0x12,0x06,0xF0,0x03, +0x14,0x02,0xA9,0x4C,0xED,0x14,0x02,0xA9,0x44,0x00,0x13,0x27,0xD3,0x10,0xA9,0x44, +0x13,0x21,0xD3,0x8F,0x00,0x00,0x04,0x00,0xA9,0x44,0x12,0x17,0xEF,0x08,0x0A,0xA9, +0x44,0x53,0xEF,0x10,0x0A,0xA9,0x48,0x52,0xD1,0x53,0x52,0x12,0x06,0xF0,0x03,0x14, +0x02,0xA9,0x4C,0xED,0x14,0x02,0xA9,0x44,0x03,0x12,0x10,0xD3,0x10,0xA9,0x44,0x13, +0x0A,0xF0,0x03,0x14,0x02,0xA9,0x4C,0xC8,0x10,0xA9,0x4C,0xED,0x14,0x02,0xA9,0x44, +0x03,0x13,0x10,0xD3,0x10,0xA9,0x44,0x12,0x0A,0xF0,0x03,0x14,0x02,0xA9,0x4C,0xC8, +0x10,0xA9,0x4C,0xEF,0x02,0x02,0xA9,0x44,0x52,0xD5,0x52,0x13,0x06,0xF0,0x03,0x02, +0x02,0xA9,0x4C,0xBA,0x1C,0x05,0xD0,0x21,0x9F,0x00,0x40,0x08,0x20,0xDA,0x10,0x25, +0xDA,0x00,0x27,0xD0,0xA9,0x28,0x56,0x30,0x08,0x00,0xD0,0x21,0x9F,0x00,0x40,0x08, +0x20,0x05,0xD0,0x20,0x9F,0x00,0x40,0x08,0x20,0xD0,0x8F,0x00,0x00,0x00,0x10,0x50, +0x7D,0x8F,0xEF,0xCD,0xAB,0x89,0x67,0x45,0x23,0x01,0x80,0xD1,0x50,0x8F,0xF8,0xFF, +0x00,0x10,0x1B,0xEC,0x05,0x96,0xA9,0x38,0xD0,0xA9,0x34,0xBE,0x04,0x05,0x96,0xA9, +0x39,0x30,0xC4,0xFD,0x30,0xCB,0xFF,0xD0,0x20,0x9F,0x00,0x40,0x08,0x20,0x05,0xBB, +0x01,0xDB,0x27,0x50,0xDA,0x00,0x27,0xD3,0x8F,0x60,0x00,0x00,0x00,0x50,0xBA,0x01, +0x05,0x00,0x00,0x00,0x02,0x80,0x40,0x00,0x18,0x03,0x3B,0x00,0x17,0x00,0x43,0x61, +0x63,0x68,0x65,0x5F,0x6D,0x65,0x6D,0x6F,0x72,0x79,0x00,0x0A,0x03,0x1A,0x00,0x02, +0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x61,0x64,0x64,0x72,0x5F,0x69,0x6E,0x63,0x72,0x00,0x01, +0x04,0x00,0xC5,0x02,0x31,0x93,0x02,0x01,0x01,0x01,0x90,0x01,0xA9,0x76,0xD4,0xA9, +0x30,0xD5,0xA9,0x1C,0x12,0x31,0x90,0x02,0xA9,0x76,0x31,0x81,0x02,0x03,0x80,0x18, +0x00,0xBF,0x02,0xE2,0xFF,0xBE,0xFF,0x43,0x61,0x63,0x68,0x65,0x31,0x5F,0x43,0x61, +0x63,0x68,0x65,0x32,0x00,0x31,0x62,0x02,0x01,0x01,0x01,0x90,0x03,0xA9,0x76,0xD0, +0x8F,0x00,0x00,0x00,0x10,0xA9,0x30,0xCA,0x8F,0xFF,0x03,0x00,0x00,0xA9,0x28,0x12, +0x06,0x3C,0x8F,0x00,0x04,0xA9,0x28,0xF0,0x01,0x1D,0x01,0x69,0xD4,0xA9,0x4C,0xD4, +0xA9,0x2C,0xD0,0x20,0x9F,0x00,0x40,0x08,0x20,0x90,0x01,0xA9,0x34,0xDA,0x00,0x25, +0xDA,0x00,0x25,0xD0,0xA9,0x30,0x51,0x30,0x86,0x02,0x13,0x03,0x31,0xF6,0x01,0x90, +0x04,0xA9,0x76,0xDE,0xCF,0x17,0x02,0xA9,0x48,0x3C,0x8F,0x00,0x01,0x52,0xD4,0x54, +0xD0,0x54,0x61,0xD6,0x54,0xC0,0x04,0x51,0xF5,0x52,0xF5,0x90,0x05,0xA9,0x76,0xD0, +0xA9,0x30,0x51,0x3C,0x8F,0x00,0x01,0x52,0xD4,0x54,0xD0,0x61,0x50,0xD1,0x54,0x50, +0x12,0x6A,0xC0,0x04,0x51,0xD6,0x54,0xF5,0x52,0xF0,0x30,0xF4,0x01,0xD0,0xA9,0x30, +0x51,0x9A,0x8F,0x40,0x52,0xD4,0x54,0x90,0x06,0xA9,0x76,0xD0,0x61,0x50,0xDB,0x27, +0x53,0x91,0x53,0x8F,0x80,0x12,0x45,0x90,0x07,0xA9,0x76,0xD1,0x54,0x50,0x12,0x3C, +0xD6,0x54,0xC0,0x04,0x51,0x90,0x08,0xA9,0x76,0xD0,0x61,0x50,0xDB,0x27,0x53,0x12, +0x2B,0x90,0x09,0xA9,0x76,0xD1,0x54,0x50,0x12,0x22,0x90,0x0A,0xA9,0x76,0xD0,0x71, +0x50,0xD7,0x54,0xDB,0x27,0x53,0x12,0x14,0x90,0x0B,0xA9,0x76,0xD1,0x54,0x50,0x12, +0x0B,0xC0,0x08,0x51,0xC0,0x02,0x54,0xF5,0x52,0xAD,0x11,0x03,0x31,0x7F,0x01,0x90, +0x0C,0xA9,0x76,0xD0,0xA9,0x30,0x51,0x3C,0x8F,0x00,0x01,0x52,0xD0,0x51,0x61,0xC0, +0x04,0x51,0xF5,0x52,0xF7,0x30,0x79,0x01,0x9A,0x8F,0x40,0x52,0x90,0x0D,0xA9,0x76, +0xD0,0x71,0x50,0xDB,0x27,0x53,0x91,0x53,0x8F,0x80,0x12,0x3E,0x90,0x0E,0xA9,0x76, +0xD1,0x51,0x50,0x12,0x35,0x90,0x0F,0xA9,0x76,0xD0,0x71,0x50,0xDB,0x27,0x53,0x12, +0x29,0x90,0x10,0xA9,0x76,0xD1,0x51,0x50,0x12,0x20,0x90,0x11,0xA9,0x76,0xC0,0x04, +0x51,0xD0,0x61,0x50,0xDB,0x27,0x53,0x12,0x11,0x90,0x12,0xA9,0x76,0xD1,0x51,0x50, +0x12,0x08,0xC2,0x04,0x51,0xF5,0x52,0xB4,0x11,0x03,0x31,0x11,0x01,0x90,0x13,0xA9, +0x76,0xC2,0x8F,0x00,0x02,0x00,0x00,0x51,0x9A,0x8F,0x80,0x52,0xD4,0x81,0xF5,0x52, +0xFB,0x9A,0x8F,0x80,0x52,0x90,0x14,0xA9,0x76,0xD0,0x61,0x50,0xDB,0x27,0x53,0x12, +0x35,0x90,0x15,0xA9,0x76,0xD1,0x51,0x50,0x12,0x2C,0xC0,0x04,0x51,0xF5,0x52,0xE5, +0xC2,0x8F,0x00,0x04,0x00,0x00,0x51,0xDA,0x58,0x25,0x9A,0x8F,0x80,0x52,0x90,0x16, +0xA9,0x76,0xD0,0x61,0x50,0xDB,0x27,0x53,0x91,0x8F,0x80,0x53,0x12,0x08,0xC0,0x08, +0x51,0xF5,0x52,0xEA,0x11,0x03,0x31,0xB5,0x00,0xD0,0xA9,0x30,0x51,0x9A,0x8F,0x40, +0x52,0xC8,0x02,0x58,0xDA,0x58,0x25,0xCA,0x02,0x58,0x90,0x17,0xA9,0x76,0xD0,0x61, +0x50,0xC0,0x08,0x51,0xF5,0x52,0xF3,0x90,0x18,0xA9,0x76,0xC2,0x8F,0x00,0x02,0x00, +0x00,0x51,0xD4,0xA9,0x38,0xDE,0xCF,0x05,0x00,0xA9,0x48,0xD0,0x61,0x50,0x90,0x19, +0xA9,0x76,0xDB,0x27,0x53,0xDB,0x25,0x54,0xD1,0xA9,0x38,0x01,0x12,0xB8,0x90,0x1A, +0xA9,0x76,0xD1,0x53,0x11,0x12,0xAF,0xDA,0x00,0x27,0x90,0x1B,0xA9,0x76,0xD1,0x54, +0x0C,0x12,0xA3,0xDE,0xCF,0x57,0x00,0xA9,0x48,0x9A,0x8F,0x40,0x52,0xDA,0x58,0x25, +0x90,0x1C,0xA9,0x76,0xD0,0x61,0x50,0xDB,0x27,0x53,0x91,0x53,0x8F,0x80,0x12,0x3E, +0xC0,0x08,0x51,0xF5,0x52,0xEA,0xDA,0x00,0x25,0x91,0xA9,0x34,0x03,0x1E,0x06,0x96, +0xA9,0x34,0x31,0xF8,0xFD,0xC0,0xA9,0x28,0xA9,0x30,0xD0,0xA9,0x30,0x51,0xCA,0x8F, +0x00,0x00,0x00,0x10,0x51,0xD1,0x51,0x8F,0x00,0x00,0x00,0x04,0x1E,0x03,0x31,0xD8, +0xFD,0x90,0x1D,0xA9,0x76,0xD5,0xA9,0x2C,0x13,0x04,0x94,0xA9,0x76,0x05,0xDB,0x25, +0xAE,0xFC,0x9A,0xAE,0xFC,0xA9,0x44,0xDB,0x27,0xAE,0xFC,0x90,0xAE,0xFC,0xA9,0x45, +0x05,0xDA,0x00,0x25,0xD0,0x10,0x58,0xF0,0xA9,0x34,0x06,0x02,0x58,0xDA,0x58,0x25, +0xDA,0x00,0x27,0x05,0xD0,0xBE,0x04,0xA9,0x4C,0xD0,0xA9,0x48,0xBE,0x04,0xD5,0xA9, +0x38,0x13,0x05,0xDE,0xAF,0xC8,0xBE,0x04,0xD6,0xA9,0x38,0x05,0xED,0x14,0x02,0x69, +0x02,0x13,0x10,0xF0,0x02,0x14,0x02,0x69,0x91,0x00,0xA9,0x76,0x12,0x05,0xF0,0x03, +0x14,0x02,0x69,0x30,0xA8,0xFF,0xDA,0x00,0x27,0xDA,0x00,0x25,0xDA,0x00,0x25,0x05, +0xBB,0x0C,0xE1,0x1C,0x51,0x05,0xD6,0xA9,0x2C,0x11,0x25,0x3C,0xA9,0x20,0x53,0x78, +0x0C,0x53,0x53,0xD1,0x51,0x53,0x1E,0x2C,0xEF,0x09,0x11,0x51,0x52,0xE1,0x52,0xB9, +0x1C,0x12,0xD6,0x52,0xE1,0x52,0xB9,0x1C,0x0B,0xD6,0xA9,0x2C,0xD0,0x51,0xA9,0x30, +0xD4,0x50,0x11,0x13,0xC0,0x8F,0x00,0x04,0x00,0x00,0x51,0xD1,0x51,0x8F,0x00,0x00, +0x00,0x04,0x1F,0xCF,0xD2,0x00,0x50,0xBA,0x0C,0x05,0x00,0x00,0x03,0x80,0x1A,0x00, +0x7C,0x01,0x00,0x00,0x16,0x00,0x42,0x6F,0x61,0x72,0x64,0x20,0x52,0x65,0x73,0x65, +0x74,0x00,0x03,0x00,0x00,0x00,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0xE1,0x0A,0x9F,0x00, +0x00,0x08,0x20,0x1D,0x90,0x01,0xA9,0x76,0xD0,0x8F,0x00,0x80,0x08,0x20,0x51,0xD4, +0x50,0xCA,0x8F,0x00,0x00,0x00,0x80,0x40,0x61,0xF2,0x8F,0x00,0x20,0x00,0x00,0x50, +0xF0,0x90,0x02,0xA9,0x76,0xD0,0x8F,0xA0,0x86,0x01,0x00,0xA9,0x5C,0x16,0xEF,0xD4, +0xBD,0xFF,0xFF,0xDA,0x00,0x20,0xDA,0x00,0x22,0xD0,0x8F,0x00,0x00,0x14,0x20,0x9F, +0x00,0x00,0x14,0x20,0xD0,0x8F,0x00,0x40,0x08,0x20,0x9F,0x30,0x01,0x14,0x20,0xD0, +0x00,0x9F,0x34,0x01,0x14,0x20,0xD0,0x8F,0x04,0x40,0x08,0x20,0x9F,0x40,0x01,0x14, +0x20,0xD0,0x00,0x9F,0x44,0x01,0x14,0x20,0xD0,0x8F,0x77,0x50,0xD4,0x00,0x50,0xD0, +0x50,0x9F,0x10,0x00,0x14,0x20,0xEF,0x04,0x03,0x9F,0x04,0x40,0x08,0x20,0x51,0xF0, +0x51,0x0C,0x03,0x50,0xD0,0x50,0x9F,0x10,0x00,0x14,0x20,0xC9,0x8F,0x00,0x00,0x00, +0xC0,0x04,0x9F,0x20,0x00,0x14,0x20,0xD0,0x8F,0x40,0x01,0x08,0x20,0x50,0xD2,0x00, +0x80,0xD0,0x60,0x51,0xCA,0x8F,0xFF,0xDF,0xFF,0xFF,0x51,0xED,0x06,0x02,0x9F,0x00, +0x40,0x08,0x20,0x03,0x12,0x07,0x3C,0x8F,0x00,0x20,0x51,0x11,0x0D,0xED,0x06,0x02, +0x9F,0x00,0x40,0x08,0x20,0x02,0x12,0x02,0xD4,0x51,0xD0,0x51,0x60,0xDA,0x00,0x27, +0x9A,0x8F,0xBD,0x9F,0x04,0x00,0x08,0x20,0xD4,0x51,0xE1,0x0A,0x9F,0x00,0x00,0x08, +0x20,0x09,0xEF,0x02,0x02,0x9F,0x04,0x40,0x08,0x20,0x51,0xF0,0x51,0x01,0x03,0x9F, +0x00,0x00,0x08,0x20,0xDA,0x00,0x18,0xD0,0x8F,0x00,0x01,0x14,0x20,0x50,0xD0,0x8F, +0x80,0x00,0x00,0x80,0x60,0xD0,0x00,0xA0,0x08,0xD0,0x10,0x60,0xC0,0x0C,0x50,0x9A, +0x8F,0x78,0x80,0xD0,0x8F,0x80,0x00,0x00,0x80,0x60,0xD0,0x00,0xA0,0x08,0xD0,0x10, +0x60,0x9A,0x8F,0x7C,0xA0,0x0C,0xDA,0x00,0x25,0xDA,0x00,0x25,0xD0,0x20,0x9F,0x00, +0x40,0x08,0x20,0x90,0x03,0xA9,0x76,0xD0,0x8F,0x00,0x00,0x00,0x10,0x50,0x7D,0x00, +0x80,0xD1,0x50,0x8F,0xF8,0xFF,0x00,0x10,0x1B,0xF4,0xD0,0x20,0x9F,0x00,0x40,0x08, +0x20,0x30,0x05,0x00,0x94,0xA9,0x76,0x05,0x05,0xD4,0xA9,0x30,0xD0,0x8F,0x40,0x0D, +0x03,0x00,0x50,0xD2,0x00,0xA9,0x2C,0xD0,0xA9,0x0C,0xA9,0x28,0xDE,0xEF,0x18,0x00, +0x00,0x00,0xA9,0x0C,0xDA,0x01,0x12,0x01,0xC2,0x02,0x50,0xF5,0x50,0xFA,0xDA,0x1F, +0x12,0xD0,0xA9,0x28,0xA9,0x0C,0xDA,0x00,0x27,0x05,0xD6,0xA9,0x30,0x91,0xA9,0x2C, +0x8F,0xFF,0x12,0x06,0x90,0x6E,0xA9,0x2C,0x11,0x25,0x91,0xA9,0x2D,0x8F,0xFF,0x12, +0x06,0x90,0x6E,0xA9,0x2D,0x11,0x18,0x91,0xA9,0x2E,0x8F,0xFF,0x12,0x06,0x90,0x6E, +0xA9,0x2E,0x11,0x0B,0x91,0xA9,0x2F,0x8F,0xFF,0x12,0x04,0x90,0x6E,0xA9,0x2F,0xDE, +0xAF,0xB6,0xBE,0x04,0xDE,0xBE,0x04,0x5E,0x02,0x03,0x80,0x1A,0x00,0x41,0x00,0x00, +0x00,0x29,0xFE,0x43,0x68,0x65,0x63,0x6B,0x5F,0x66,0x6F,0x72,0x5F,0x69,0x6E,0x74, +0x72,0x73,0x00,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x04,0xA9,0x76,0x30,0x69,0xFF, +0xD5,0xA9,0x30,0x13,0x11,0x9A,0xA9,0x2C,0x50,0x12,0x06,0x90,0x05,0xA9,0x76,0x11, +0x04,0x90,0x50,0xA9,0x76,0x05,0x94,0xA9,0x76,0x05,0x05,0x00,0x02,0x80,0x24,0x00, +0xEE,0x01,0x7B,0x05,0x19,0x00,0x4D,0x45,0x4D,0x5F,0x53,0x65,0x74,0x75,0x70,0x5F, +0x43,0x53,0x52,0x73,0x00,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x01,0xA9,0x76,0x30,0xAF,0x02,0x30,0xB6,0x04, +0xD4,0xA9,0x28,0xD0,0x08,0x53,0xC8,0x8F,0x00,0x08,0x00,0x00,0x67,0xF5,0x53,0xF6, +0xD4,0x53,0xD0,0x8F,0x00,0x01,0x08,0x20,0x51,0xD4,0x52,0x90,0x02,0xA9,0x76,0xD0, +0x20,0x61,0xD0,0x61,0x50,0x90,0x03,0xA9,0x76,0xEF,0x00,0x02,0x50,0x54,0xD1,0x54, +0x03,0x12,0x02,0xD6,0x54,0xD0,0x04,0x55,0x90,0x04,0xA9,0x76,0xD4,0x61,0xD5,0x54, +0x13,0x2A,0xED,0x02,0x04,0x50,0x05,0x12,0x23,0xD0,0x8F,0x00,0x00,0x00,0x80,0x58, +0xF0,0x53,0x16,0x04,0x58,0xD0,0x58,0x61,0x91,0x03,0x9F,0x05,0x00,0x04,0x20,0x12, +0x07,0xE0,0x26,0x69,0x03,0x31,0x11,0x00,0xD7,0x54,0xD6,0x53,0xC0,0x04,0x51,0xF5, +0x55,0xC6,0xF2,0x04,0x52,0xA5,0x31,0x76,0x00,0x90,0x05,0xA9,0x76,0xBB,0x0D,0xD0, +0x61,0x50,0xEF,0x00,0x03,0x50,0x50,0xD1,0x50,0x06,0x12,0x59,0xCB,0x8F,0xFF,0xFF, +0x3F,0xFC,0x61,0x50,0xDE,0xEF,0x4B,0x00,0x00,0x00,0xA9,0x4C,0xF0,0x01,0x1D,0x01, +0x69,0xD4,0x60,0xD0,0x60,0x52,0xD2,0x52,0x53,0xD0,0x53,0xE0,0x00,0x00,0x10,0x00, +0xD0,0x53,0xE0,0x00,0x00,0x20,0x00,0xD0,0x53,0xE0,0x00,0x00,0x30,0x00,0xD1,0x60, +0x53,0x12,0x22,0xD3,0x0C,0x51,0x13,0x02,0xD7,0x53,0xCA,0x0F,0x51,0xD4,0x81,0xD4, +0x81,0xD4,0x81,0xD4,0x81,0xF0,0x00,0x1D,0x01,0x69,0xBA,0x0D,0xF0,0x01,0x52,0x01, +0xA9,0x28,0x31,0x8D,0xFF,0xF0,0x00,0x1D,0x01,0x69,0xBA,0x0D,0x31,0x79,0xFF,0x95, +0xA9,0x28,0x13,0x3E,0xBB,0x1F,0xDB,0x11,0xA9,0x2C,0xDA,0xA9,0x50,0x11,0xDD,0x8F, +0x8C,0x00,0x00,0x00,0xFB,0x01,0xEF,0x71,0x24,0xFF,0xFF,0xDD,0x8F,0x8C,0x00,0x00, +0x00,0xFB,0x01,0xEF,0x64,0x24,0xFF,0xFF,0xDD,0x8F,0x9D,0x00,0x00,0x00,0xFB,0x01, +0xEF,0x57,0x24,0xFF,0xFF,0xDA,0xA9,0x2C,0x11,0xBA,0x1F,0x90,0x06,0xA9,0x76,0x31, +0x87,0x00,0xD4,0x52,0xD0,0x8F,0x00,0x01,0x08,0x20,0x51,0xD0,0x61,0x50,0xD3,0x03, +0x50,0x13,0x0B,0x90,0x07,0xA9,0x76,0xED,0x02,0x04,0x50,0x05,0x12,0x6B,0xC0,0x04, +0x51,0xD1,0x51,0x8F,0x40,0x01,0x08,0x20,0x1F,0xE1,0xD4,0x53,0xD0,0x8F,0x00,0x01, +0x08,0x20,0x51,0xD0,0x61,0x50,0xEF,0x00,0x02,0x61,0x54,0x13,0x16,0xD1,0x54,0x03, +0x13,0x1B,0xD1,0x54,0x01,0x12,0x07,0xD3,0x0C,0x51,0x13,0x11,0x11,0x05,0xD3,0x08, +0x51,0x13,0x0A,0x90,0x08,0xA9,0x76,0xE0,0x1F,0x50,0x2E,0x11,0x15,0x90,0x09,0xA9, +0x76,0xE1,0x1F,0x50,0x24,0x90,0x0A,0xA9,0x76,0xED,0x16,0x04,0x50,0x53,0x12,0x19, +0xD6,0x53,0xC0,0x04,0x51,0xD1,0x51,0x8F,0x40,0x01,0x08,0x20,0x1F,0xB5,0x90,0x0B, +0xA9,0x76,0xD5,0x53,0x13,0x03,0x31,0xA1,0x01,0x05,0x30,0x70,0x25,0x31,0x3E,0x03, +0x0A,0x02,0x3F,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x03,0x49,0x00, +0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x04,0x03,0x51,0x00, +0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x02,0x5A,0x00, +0x02,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73, +0x74,0x61,0x72,0x74,0x5F,0x61,0x64,0x64,0x00,0x65,0x6E,0x64,0x5F,0x61,0x64,0x64, +0x00,0x61,0x64,0x64,0x5F,0x69,0x6E,0x63,0x72,0x00,0x63,0x6F,0x6E,0x74,0x5F,0x6F, +0x6E,0x5F,0x65,0x72,0x72,0x00,0xF0,0x00,0x09,0x01,0xA9,0x34,0xD0,0xA9,0x28,0x52, +0x13,0x10,0xD1,0x52,0x04,0x1A,0x0B,0xD0,0xA9,0x2C,0x51,0x13,0x05,0xD1,0x51,0x04, +0x1B,0x03,0x31,0x67,0x00,0xD0,0x8F,0xF0,0x00,0x08,0x20,0x50,0xC0,0x10,0x50,0xF5, +0x52,0xFA,0xD0,0x60,0x52,0xE1,0x1F,0x52,0x37,0xCA,0x8F,0xFF,0xFF,0x3F,0xFC,0x52, +0xD0,0x52,0xA9,0x28,0xD0,0x8F,0xF0,0x00,0x08,0x20,0x50,0xC0,0x10,0x50,0xF5,0x51, +0xFA,0xD0,0x60,0x52,0xEF,0x00,0x02,0x52,0x53,0x13,0x15,0xD1,0x53,0x03,0x13,0x02, +0xD7,0x53,0x78,0x02,0x53,0x53,0xC0,0x53,0x50,0xD0,0x60,0x52,0xE0,0x1F,0x52,0x08, +0xF0,0x01,0x09,0x01,0xA9,0x34,0x11,0x60,0xCA,0x8F,0xFF,0xFF,0x3F,0xFC,0x52,0xC0, +0x8F,0x00,0x00,0x40,0x00,0x52,0xD0,0x52,0xA9,0x2C,0x11,0x4C,0xCA,0x07,0xA9,0x28, +0xCA,0x07,0xA9,0x2C,0xCA,0x03,0xA9,0x30,0x12,0x04,0xD0,0x04,0xA9,0x30,0xF0,0x00, +0x09,0x01,0xA9,0x34,0xEF,0x16,0x05,0xA9,0x28,0x50,0xD0,0x8F,0x00,0x01,0x08,0x20, +0x57,0xE1,0x1F,0x67,0x07,0xED,0x16,0x04,0x67,0x50,0x13,0x1C,0xC0,0x04,0x57,0xD1, +0x57,0x8F,0x40,0x01,0x08,0x20,0x1F,0xE9,0xD6,0x50,0xED,0x16,0x05,0xA9,0x2C,0x50, +0x1A,0xD8,0xF0,0x01,0x09,0x01,0xA9,0x34,0xF0,0x00,0x1D,0x01,0x69,0xCA,0x8F,0xFE, +0xFD,0xFF,0xFF,0xA9,0x34,0xD2,0x00,0xA9,0x3C,0xD4,0xA9,0x40,0xD4,0xA9,0x44,0xD4, +0xA9,0x48,0xD4,0xA9,0x4C,0xDA,0x00,0x25,0xDA,0x00,0x25,0xDA,0x8F,0xE0,0x00,0x00, +0x00,0x25,0xD0,0x8F,0x40,0x01,0x08,0x20,0x57,0xD2,0x00,0x87,0x3C,0x8F,0x00,0x20, +0x58,0xED,0x06,0x02,0x9F,0x00,0x40,0x08,0x20,0x03,0x13,0x17,0xD4,0x58,0xED,0x06, +0x02,0x9F,0x00,0x40,0x08,0x20,0x02,0x13,0x0A,0xD0,0x67,0x58,0xCA,0x8F,0xFF,0xDF, +0xFF,0xFF,0x58,0xD0,0x58,0x67,0xDA,0x19,0x12,0x05,0xDA,0x00,0x25,0x94,0xA9,0x76, +0x05,0xF0,0x01,0x0A,0x01,0xA9,0x34,0xF0,0x00,0x1D,0x01,0x69,0xBB,0x02,0xCA,0x8F, +0xFF,0x1F,0x00,0x00,0x9F,0x44,0x01,0x08,0x20,0xD5,0xA9,0x1C,0x13,0x17,0xEF,0x09, +0x14,0x51,0x51,0xE1,0x51,0xB9,0x1C,0x06,0xF0,0x00,0x51,0x01,0xB9,0x1C,0xD2,0x00, +0x9F,0x40,0x01,0x08,0x20,0xBA,0x02,0xF0,0x00,0x0A,0x01,0xA9,0x34,0x05,0x30,0x8C, +0x01,0xD2,0x00,0x9F,0x40,0x01,0x08,0x20,0xCA,0x8F,0xFF,0x1F,0x00,0x00,0x9F,0x44, +0x01,0x08,0x20,0x30,0xAB,0xFF,0xE1,0x00,0xA9,0x34,0x1C,0xC0,0x8F,0x00,0x02,0x00, +0x00,0x51,0xAA,0x8F,0xFF,0x01,0x51,0xD1,0xA9,0x30,0x8F,0x00,0x02,0x00,0x00,0x1B, +0x05,0x3C,0x8F,0x00,0x02,0x53,0x05,0xD5,0x8E,0x05,0xF0,0x00,0x1D,0x01,0x69,0x30, +0xF6,0x23,0xD1,0x51,0xA9,0x2C,0x1E,0x2B,0xBB,0x24,0x3C,0xA9,0x36,0x52,0xD1,0x51, +0xA9,0x2C,0x1E,0x1D,0xEF,0x16,0x04,0x51,0x53,0xE0,0x53,0x52,0x19,0xC0,0x8F,0x00, +0x00,0x40,0x00,0x51,0xCA,0x8F,0xFF,0xFF,0x3F,0x00,0x51,0xD1,0x51,0xA9,0x2C,0x1F, +0xDD,0xBA,0x24,0xB9,0x04,0x05,0xD0,0xA9,0x1C,0x54,0x13,0x21,0xB4,0x54,0xD0,0x8F, +0x00,0x00,0x01,0x00,0x53,0xD0,0x54,0x55,0xD1,0x51,0x54,0x1F,0x10,0xC0,0x53,0x54, +0xD0,0x54,0x55,0xD1,0x51,0x54,0x1E,0x05,0xD0,0x54,0x51,0x11,0xB1,0xF0,0x00,0x0C, +0x04,0xA9,0x34,0xBB,0x04,0xCB,0x8F,0xFF,0xFF,0x3F,0xFC,0x51,0x53,0xF0,0x01,0x1F, +0x01,0x53,0xD0,0x8F,0x00,0x01,0x08,0x20,0x52,0xCB,0x8F,0xFF,0xFF,0x3F,0x7C,0x62, +0x7E,0xD1,0x8E,0x53,0x13,0x0E,0xC0,0x04,0x52,0xD1,0x52,0x8F,0x40,0x01,0x08,0x20, +0x1F,0xE7,0x11,0x0E,0xEF,0x04,0x02,0x52,0x52,0xC0,0x0C,0x52,0xF0,0x01,0x52,0x01, +0xA9,0x34,0xBA,0x04,0xC1,0x8F,0x00,0x00,0x40,0x00,0x51,0x54,0xCA,0x8F,0xFF,0xFF, +0x3F,0x00,0x54,0x30,0x81,0x23,0xD1,0x51,0x55,0x1E,0x08,0xD1,0x54,0x55,0x1B,0x03, +0xD0,0x55,0x54,0xD1,0x54,0xA9,0x2C,0x1B,0x04,0xD0,0xA9,0x2C,0x54,0xBA,0x24,0xD0, +0xA9,0x30,0x53,0xB8,0x04,0x05,0xBB,0x06,0xB4,0xA9,0x36,0xD0,0x8F,0x00,0x01,0x08, +0x20,0x51,0xE1,0x1F,0x61,0x0B,0xEF,0x16,0x04,0x61,0x52,0xF0,0x01,0x52,0x01,0xA9, +0x36,0xD5,0x81,0xE1,0x06,0x51,0xEB,0xBA,0x06,0x05,0xD0,0xA9,0x1C,0x52,0xD4,0x50, +0x3C,0xA9,0x20,0x51,0x78,0x8F,0xFF,0x51,0x51,0xAC,0x82,0x50,0x78,0x01,0x50,0x50, +0xE5,0x10,0x50,0x02,0xB6,0x50,0xF5,0x51,0xF0,0xB0,0x50,0xA9,0x22,0x05,0xD2,0x00, +0xA9,0x3C,0x91,0xA9,0x77,0x8F,0xFE,0x12,0x03,0x30,0x21,0x00,0xCA,0x8F,0xFF,0x1F, +0x00,0x00,0x9F,0x44,0x01,0x08,0x20,0xD2,0x00,0x9F,0x40,0x01,0x08,0x20,0xDA,0x00, +0x25,0xDA,0x00,0x27,0xE0,0x1F,0xA9,0x3C,0x03,0xD4,0xB9,0x3C,0x05,0xDB,0x27,0xAE, +0xFC,0x90,0xAE,0xFC,0xA9,0x43,0xD0,0x9F,0x40,0x01,0x08,0x20,0xA9,0x44,0xB0,0x9F, +0x44,0x01,0x08,0x20,0xA9,0x40,0x05,0x01,0x04,0x00,0x05,0x00,0xD0,0xBE,0x04,0xA9, +0x48,0xD0,0xA9,0x4C,0xBE,0x04,0x05,0x94,0xA9,0x42,0xF0,0x00,0x06,0x02,0xA9,0x34, +0xE1,0x1D,0x9F,0x40,0x01,0x08,0x20,0x04,0x90,0x01,0xA9,0x42,0xBB,0x18,0xD0,0x20, +0x53,0xD0,0xAE,0x0C,0x54,0x01,0x01,0x01,0xE9,0x54,0x03,0x96,0xA9,0x42,0x78,0x8F, +0xFF,0x54,0x54,0xF5,0x53,0xF2,0xBA,0x18,0x91,0xA9,0x42,0x02,0x1F,0x08,0xF0,0x01, +0x07,0x01,0xA9,0x34,0x11,0x0B,0x95,0xA9,0x42,0x13,0x06,0xF0,0x01,0x06,0x01,0xA9, +0x34,0xD0,0x8E,0x6E,0x05,0xCA,0x8F,0x80,0xFF,0xFF,0xFF,0xAE,0x04,0x31,0xBC,0xFF, +0xF0,0x01,0x0A,0x01,0xA9,0x34,0xF0,0x00,0x1D,0x01,0x69,0xBB,0x03,0xEF,0x0C,0x04, +0xA9,0x34,0x50,0xD4,0x51,0xE8,0x50,0x0B,0xD6,0x51,0x78,0x8F,0xFF,0x50,0x50,0x13, +0x15,0x11,0xF2,0xD0,0xA9,0x1C,0x50,0xB4,0x50,0xC0,0x8F,0x00,0x40,0x00,0x00,0x50, +0xC0,0x00,0x50,0xD6,0x41,0x60,0xBA,0x03,0xF0,0x00,0x0A,0x01,0xA9,0x34,0x05,0x02, +0x80,0x3A,0x00,0x0F,0xFF,0x58,0xFF,0x15,0x00,0x4D,0x45,0x4D,0x5F,0x42,0x69,0x74, +0x6D,0x61,0x70,0x00,0x0A,0x00,0x00,0x00,0x02,0x16,0x00,0x02,0x00,0x00,0x00,0x00, +0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6D,0x61,0x72,0x6B,0x5F,0x48, +0x61,0x72,0x64,0x5F,0x53,0x42,0x45,0x73,0x00,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90, +0x01,0xA9,0x76,0xD4,0xA9,0x1C,0xB4,0xA9,0x20,0xB4,0xA9,0x22,0xD4,0xA9,0x24,0xF0, +0x02,0x12,0x02,0x69,0xF0,0x00,0x22,0x01,0x69,0xE9,0xA9,0x34,0x05,0xF0,0x01,0x22, +0x01,0x69,0x30,0x57,0xFC,0xD4,0xA9,0x38,0x30,0x5B,0xFE,0xE1,0x09,0xA9,0x34,0x05, +0x90,0x02,0xA9,0x76,0x05,0xD0,0x8F,0x00,0x00,0x00,0x04,0x51,0xD0,0x0F,0x52,0xE0, +0x52,0xA9,0x36,0x14,0xC2,0x8F,0x00,0x00,0x40,0x00,0x51,0xF4,0x52,0xF1,0x90,0x03, +0xA9,0x76,0xF0,0x02,0x12,0x02,0x69,0x05,0xD0,0x51,0xA9,0x30,0x90,0x04,0xA9,0x76, +0xDE,0xCF,0x6C,0x03,0xA9,0x4C,0xD2,0x00,0xA7,0xFC,0xC2,0x8F,0x00,0x00,0x02,0x00, +0xA9,0x30,0x1A,0x02,0x11,0xDC,0xD0,0xA9,0x30,0x51,0x3C,0xA9,0x36,0x52,0xEF,0x16, +0x04,0x51,0x53,0xE1,0x53,0x52,0xD5,0xD0,0x01,0x56,0xD0,0x02,0x52,0xF0,0x01,0x1D, +0x01,0x69,0x90,0x05,0xA9,0x76,0xD2,0x56,0x54,0xD0,0x56,0x61,0xD0,0x54,0xA1,0x04, +0x90,0x06,0xA9,0x76,0xD0,0x61,0x50,0xD0,0xA1,0x04,0x53,0x90,0x07,0xA9,0x76,0xD1, +0x50,0x56,0x12,0x24,0x90,0x08,0xA9,0x76,0xD1,0x54,0x53,0x12,0x1B,0x90,0x09,0xA9, +0x76,0xE1,0x1D,0xA7,0xFC,0x04,0xE0,0x22,0x69,0x0E,0xD2,0x56,0x56,0xF5,0x52,0xC2, +0x78,0x01,0x56,0x56,0x12,0xB4,0x11,0x03,0x31,0xF5,0x02,0x90,0x0A,0xA9,0x76,0xD4, +0x61,0x90,0x0B,0xA9,0x76,0xD0,0x61,0x50,0x90,0x0C,0xA9,0x76,0xD5,0x50,0x12,0xE8, +0xE1,0x22,0x69,0x09,0x90,0x0D,0xA9,0x76,0xE0,0x1D,0xA7,0xFC,0xDB,0xD2,0x00,0x55, +0x90,0x0E,0xA9,0x76,0xD0,0x55,0x61,0x90,0x0F,0xA9,0x76,0xD0,0x61,0x50,0x90,0x10, +0xA9,0x76,0xD1,0x50,0x55,0x12,0xC1,0xE1,0x22,0x69,0x09,0x90,0x11,0xA9,0x76,0xE0, +0x1D,0xA7,0xFC,0xB4,0x90,0x12,0xA9,0x76,0xD2,0x00,0x61,0xD2,0x00,0xA1,0x04,0x90, +0x13,0xA9,0x76,0x90,0x07,0xA1,0x07,0x90,0x06,0xA1,0x06,0x90,0x05,0xA1,0x05,0x90, +0x04,0xA1,0x04,0x90,0x03,0xA1,0x03,0x90,0x02,0xA1,0x02,0x90,0x01,0xA1,0x01,0x90, +0x00,0x61,0x90,0x14,0xA9,0x76,0xD0,0x8F,0x00,0x01,0x02,0x03,0x56,0xD0,0x61,0x50, +0x90,0x15,0xA9,0x76,0xD1,0x50,0x56,0x12,0x5B,0x90,0x16,0xA9,0x76,0xD0,0x8F,0x04, +0x05,0x06,0x07,0x56,0xD0,0xA1,0x04,0x50,0x90,0x17,0xA9,0x76,0xD1,0x50,0x56,0x12, +0x43,0x90,0x18,0xA9,0x76,0xB4,0xA1,0x03,0x90,0x19,0xA9,0x76,0xD0,0x8F,0x00,0x01, +0x02,0x00,0x56,0xD0,0x61,0x50,0x90,0x1A,0xA9,0x76,0xD1,0x50,0x56,0x12,0x25,0x90, +0x1B,0xA9,0x76,0xD0,0x8F,0x00,0x05,0x06,0x07,0x56,0xD0,0xA1,0x04,0x50,0x90,0x1C, +0xA9,0x76,0xD1,0x50,0x56,0x12,0x0D,0xE1,0x22,0x69,0x0C,0x90,0x1D,0xA9,0x76,0xE1, +0x1D,0xA7,0xFC,0x03,0x31,0x09,0x02,0x90,0x1E,0xA9,0x76,0x3C,0x8F,0x00,0x80,0x56, +0xD0,0x51,0x58,0x2C,0x00,0x8F,0xAA,0x8F,0xAA,0x56,0x68,0xC0,0x56,0x58,0x2C,0x00, +0x8F,0xAA,0x8F,0xAA,0x56,0x68,0xC0,0x56,0x58,0x2C,0x00,0x8F,0xAA,0x8F,0xAA,0x56, +0x68,0xC0,0x56,0x58,0x2C,0x00,0x8F,0xAA,0x8F,0xAA,0x56,0x68,0x90,0x1F,0xA9,0x76, +0xD0,0xA9,0x30,0x51,0xD0,0x8F,0xAA,0xAA,0xAA,0xAA,0x56,0xD0,0x8F,0x55,0x55,0x55, +0x45,0x52,0x3C,0x8F,0x00,0x80,0x53,0x01,0xD0,0x61,0x50,0xD1,0x50,0x56,0x12,0xA4, +0xD0,0x52,0x81,0xF5,0x53,0xF2,0xE1,0x22,0x69,0x0C,0x90,0x20,0xA9,0x76,0xE1,0x1D, +0xA7,0xFC,0x03,0x31,0x9A,0x01,0x90,0x21,0xA9,0x76,0xD0,0xA9,0x30,0x51,0xD0,0x52, +0x56,0xD0,0x8F,0x55,0x55,0x55,0x55,0x52,0x3C,0x8F,0x00,0x80,0x53,0x01,0x01,0x01, +0xD0,0x61,0x50,0xD1,0x50,0x56,0x12,0xDB,0xD0,0x52,0x81,0xF5,0x53,0xF2,0xE1,0x22, +0x69,0x09,0x90,0x22,0xA9,0x76,0xE0,0x1D,0xA7,0xFC,0xC8,0x90,0x23,0xA9,0x76,0xD4, +0x51,0xD4,0x55,0x3C,0x8F,0x00,0x40,0x52,0xDE,0xCF,0x4A,0x00,0xA9,0x4C,0xD0,0xA9, +0x30,0x50,0xC1,0x8F,0x00,0x00,0x02,0x00,0x50,0x53,0xD1,0x51,0x50,0x1F,0x0E,0xD1, +0x51,0x53,0x1E,0x09,0xC0,0x8F,0x00,0x00,0x02,0x00,0x51,0x11,0xED,0xEF,0x16,0x04, +0x51,0x56,0xE0,0x56,0xA9,0x36,0x19,0xC0,0x8F,0x00,0x00,0x40,0x00,0x51,0xCA,0x8F, +0xFF,0xFF,0x3F,0x00,0x51,0xD1,0x51,0x8F,0x00,0x00,0x00,0x04,0x1F,0xCC,0x11,0x17, +0xD4,0x61,0xD2,0x00,0xA1,0x04,0xC0,0x52,0x51,0xD1,0x51,0x8F,0x00,0x00,0x00,0x04, +0x1F,0xB8,0x11,0x03,0x31,0xF9,0x00,0xDE,0xCF,0xF5,0x00,0xA9,0x4C,0xD0,0xA9,0x30, +0x51,0x3C,0x8F,0x00,0x80,0x53,0xD0,0x8F,0x55,0x55,0x55,0x55,0x56,0x01,0x01,0x01, +0x90,0x24,0xA9,0x76,0xD0,0x61,0x50,0xD1,0x50,0x56,0x12,0xD8,0xF5,0x53,0xF1,0xE1, +0x22,0x69,0x09,0x90,0x25,0xA9,0x76,0xE0,0x1D,0xA7,0xFC,0xC8,0x90,0x26,0xA9,0x76, +0xC0,0x8F,0x00,0x00,0x01,0x00,0xA9,0x30,0xD0,0xA9,0x30,0x56,0x2C,0x00,0x8F,0x00, +0x00,0x8F,0x00,0x80,0x66,0xC0,0x8F,0x00,0x80,0x00,0x00,0x56,0x2C,0x00,0x8F,0x00, +0x00,0x8F,0x00,0x80,0x66,0xC1,0x8F,0x00,0x80,0x00,0x00,0xA9,0x30,0x51,0x3C,0xA9, +0x36,0x53,0xD0,0x10,0x52,0x3C,0x8F,0x00,0x40,0x54,0x78,0x01,0x53,0x53,0xE0,0x10, +0x53,0x0A,0xC2,0x8F,0x00,0x04,0x00,0x00,0x54,0xF5,0x52,0xEE,0xD0,0x51,0x52,0xC2, +0x8F,0x00,0x40,0x00,0x00,0x51,0xC2,0x54,0x51,0xD0,0x52,0xA9,0x24,0xB0,0x54,0xA9, +0x20,0xD0,0x51,0xA9,0x1C,0x2C,0x00,0x8F,0xFF,0x8F,0xFF,0xA9,0x20,0x61,0x3C,0xA9, +0x20,0x54,0x78,0x8F,0xF7,0x54,0x54,0xD0,0x54,0x55,0x78,0x8F,0xFD,0x54,0x54,0xCA, +0x8F,0xF8,0xFF,0xFF,0xFF,0x55,0xC0,0x0C,0x54,0xD0,0xA9,0x1C,0x51,0xEF,0x10,0x0A, +0x51,0x52,0xD6,0x52,0x78,0x04,0x52,0x52,0xC0,0x52,0x51,0x94,0x71,0xF5,0x54,0xFB, +0xCE,0x55,0x55,0x9A,0x71,0x52,0x78,0x55,0x52,0x52,0x90,0x52,0x61,0x30,0xA3,0x1D, +0x30,0xE7,0xFA,0xB2,0xA9,0x22,0xA9,0x22,0xF0,0x03,0x12,0x02,0x69,0x31,0x5A,0xF9, +0xD0,0x50,0xA9,0x2C,0xD0,0x56,0xA9,0x28,0xD0,0x51,0xA9,0x3C,0x30,0x1E,0xFB,0x90, +0xA9,0x3A,0xA9,0x3B,0x90,0xA9,0x39,0xA9,0x3A,0x90,0xA9,0x38,0xA9,0x39,0x90,0xA9, +0x76,0xA9,0x38,0xD2,0x00,0xA7,0xFC,0x31,0x62,0xFC,0x02,0x80,0x45,0x00,0xD4,0xFA, +0x00,0x00,0x96,0xF7,0x4D,0x45,0x4D,0x5F,0x44,0x61,0x74,0x61,0x00,0x01,0xFF,0xFF, +0xFF,0xFF,0x7F,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0xAA,0xAA,0xAA,0xAA,0x2A,0x01, +0x55,0x55,0x55,0x55,0x55,0x27,0x01,0x00,0x00,0x00,0x00,0xFF,0x08,0xF0,0xF0,0xF0, +0xF0,0x70,0x10,0x00,0xFF,0x00,0xFF,0x00,0x14,0x00,0xFC,0x0F,0xC0,0x7F,0x00,0x31, +0xE8,0xF8,0x01,0x01,0x01,0x90,0x01,0xA9,0x76,0x30,0xAA,0xF7,0x30,0x37,0xFA,0xE1, +0x09,0xA9,0x34,0x05,0x90,0x02,0xA9,0x76,0x05,0xD0,0xA9,0x28,0x51,0xCA,0x07,0xA9, +0x30,0x12,0x04,0xD0,0x08,0xA9,0x30,0x30,0x40,0xF9,0x13,0x03,0x31,0xBB,0xF8,0xA8, +0x8F,0x80,0x04,0x58,0xDE,0xAF,0x96,0xA9,0x48,0xF0,0x00,0x08,0x01,0xA9,0x34,0x90, +0xB9,0x48,0xA9,0x43,0x91,0xA9,0x43,0x8F,0xFF,0x12,0x11,0xD6,0xA9,0x48,0xD3,0x8F, +0x00,0x00,0x04,0x00,0x51,0x12,0x0A,0x90,0xB9,0x48,0xA9,0x43,0x95,0xA9,0x43,0x12, +0x03,0x31,0x8B,0x00,0x90,0x03,0xA9,0x76,0xD0,0xA9,0x48,0x52,0xD0,0xA2,0x01,0x55, +0x9A,0xA2,0x05,0x56,0xD2,0x56,0x52,0xD2,0x55,0xA9,0x4C,0xF0,0x01,0x07,0x01,0x58, +0xF0,0x56,0x00,0x07,0x58,0xD0,0x58,0x67,0xD0,0x55,0x61,0xF0,0x52,0x00,0x07,0x58, +0xD0,0x58,0x67,0xD0,0xA9,0x4C,0xA1,0x04,0x90,0x04,0xA9,0x76,0x94,0x58,0xD0,0x58, +0x67,0xF0,0x56,0x00,0x07,0x58,0xD0,0x61,0x50,0xD1,0x50,0x55,0x12,0x60,0x90,0x05, +0xA9,0x76,0xD1,0x67,0x58,0x12,0x57,0x90,0x06,0xA9,0x76,0x94,0x58,0xD0,0x58,0x67, +0xF0,0x52,0x00,0x07,0x58,0xD0,0xA1,0x04,0x50,0xD1,0x50,0xA9,0x4C,0x12,0x4E,0x90, +0x07,0xA9,0x76,0xD1,0x67,0x58,0x12,0x45,0x79,0x01,0x55,0x55,0xE5,0x07,0x56,0x03, +0xC8,0x01,0x55,0x97,0xA9,0x43,0x1A,0x8C,0xC0,0x06,0xA9,0x48,0x31,0x50,0xFF,0xCA, +0x8F,0xFF,0x00,0x00,0x00,0x67,0x7C,0x61,0xE1,0x08,0xA9,0x34,0x03,0x30,0x40,0xFA, +0xC0,0x53,0x51,0xD1,0x51,0x54,0x1F,0x03,0x31,0x1C,0xFF,0x31,0x21,0xFF,0xD0,0x55, +0xA9,0x38,0xCB,0x8F,0x80,0xFF,0xFF,0xFF,0x56,0xA9,0x3C,0x11,0x0E,0xD0,0xA9,0x4C, +0xA9,0x38,0xCB,0x8F,0x80,0xFF,0xFF,0xFF,0x52,0xA9,0x3C,0xB0,0x67,0xA9,0x40,0xE1, +0x00,0xA9,0x34,0x34,0x90,0x08,0xA9,0x76,0xCD,0x50,0xA9,0x38,0x7E,0x30,0xA7,0xF9, +0xE0,0x07,0xA9,0x34,0x23,0xCD,0xA9,0x3C,0xA9,0x40,0x7E,0x30,0xE7,0xF9,0xE0,0x07, +0xA9,0x34,0x15,0xE2,0x08,0xA9,0x34,0x03,0x31,0xE4,0xFE,0xE0,0x22,0x69,0x09,0xF0, +0x00,0x08,0x01,0xA9,0x34,0x31,0x80,0xFF,0x30,0xC3,0xF7,0x94,0x58,0xA8,0x8F,0x80, +0x04,0x58,0xD2,0x00,0xA7,0xFC,0x11,0x8B,0x02,0x80,0x13,0x00,0x16,0xF9,0x5F,0xF9, +0xD8,0xF5,0x4D,0x45,0x4D,0x5F,0x42,0x79,0x74,0x65,0x00,0xDE,0xAF,0xFD,0xC9,0x80, +0x00,0x90,0x01,0xA9,0x76,0x30,0x1E,0xF6,0xE1,0x09,0xA9,0x34,0x05,0x90,0x02,0xA9, +0x76,0x05,0x30,0xA1,0xF8,0xD0,0xA9,0x28,0x51,0x30,0xBE,0xF7,0x13,0x03,0x31,0x39, +0xF7,0x90,0x03,0xA9,0x76,0xDE,0xCF,0xCB,0x00,0xA9,0x4C,0xC1,0x08,0x51,0x50,0xD1, +0x50,0x54,0x1F,0x03,0x31,0x9A,0x00,0xF0,0x00,0x08,0x01,0xA9,0x34,0xD2,0x00,0xA7, +0xFC,0xF0,0x01,0x1D,0x01,0x69,0x90,0x04,0xA9,0x76,0xD2,0x00,0x61,0x90,0x03,0xA1, +0x03,0x90,0x02,0xA1,0x02,0x90,0x01,0xA1,0x01,0x90,0x00,0x61,0xD0,0x61,0x50,0x90, +0x05,0xA9,0x76,0xD0,0x8F,0x00,0x01,0x02,0x03,0x52,0xD1,0x50,0x52,0x12,0x70,0x90, +0x06,0xA9,0x76,0xD2,0x00,0x61,0xD2,0x00,0xA1,0x04,0x3C,0x8F,0x04,0x05,0x52,0xB0, +0x52,0xA1,0x03,0x3C,0xA1,0x03,0x50,0x90,0x07,0xA9,0x76,0xD1,0x50,0x52,0x12,0x4F, +0x90,0x08,0xA9,0x76,0x7D,0x61,0x55,0xD0,0x55,0x50,0xD0,0x8F,0xFF,0xFF,0xFF,0x04, +0x52,0xD1,0x50,0x52,0x12,0x13,0x90,0x09,0xA9,0x76,0xD0,0x56,0x50,0xD0,0x8F,0x05, +0xFF,0xFF,0xFF,0x52,0xD1,0x50,0x52,0x13,0x02,0x11,0x39,0x90,0x0A,0xA9,0x76,0xE0, +0x1D,0xA7,0xFC,0x1B,0xF0,0x00,0x1D,0x01,0x69,0xE1,0x08,0xA9,0x34,0x03,0x30,0xDF, +0xF8,0xC0,0x53,0x51,0xD1,0x51,0x54,0x1E,0x03,0x31,0x45,0xFF,0x31,0x3A,0xFF,0xE1, +0x00,0xA9,0x34,0x10,0xE3,0x08,0xA9,0x34,0x11,0xE0,0x22,0x69,0x07,0xF0,0x00,0x1D, +0x01,0x69,0x11,0xDD,0x30,0xA7,0xF6,0x31,0xDA,0xFF,0x31,0x40,0xFF,0x02,0x80,0x16, +0x00,0x01,0xF8,0x4A,0xF8,0xC3,0xF4,0x4D,0x45,0x4D,0x5F,0x41,0x64,0x64,0x72,0x65, +0x73,0x73,0x00,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x01,0xA9,0x76,0x30,0x06,0xF5, +0x30,0x93,0xF7,0xE1,0x09,0xA9,0x34,0x05,0x90,0x02,0xA9,0x76,0x05,0xD0,0xA9,0x28, +0x51,0x30,0xA6,0xF6,0x12,0x11,0x90,0x03,0xA9,0x76,0xD0,0x51,0x61,0xC0,0x53,0x51, +0xD1,0x51,0x54,0x1F,0xF5,0x11,0xEA,0x90,0x04,0xA9,0x76,0xD0,0xA9,0x28,0x51,0xDE, +0xCF,0x26,0x00,0xA9,0x4C,0x30,0x82,0xF6,0x13,0x03,0x31,0x6F,0x00,0xF0,0x01,0x1D, +0x01,0x69,0xD0,0x61,0x50,0xD1,0x50,0x51,0x12,0x15,0xC0,0x53,0x51,0xD1,0x51,0x54, +0x1F,0xF0,0xF0,0x00,0x1D,0x01,0x69,0x11,0xDC,0x90,0x05,0xA9,0x76,0x11,0x04,0x90, +0x06,0xA9,0x76,0xF0,0x00,0x1D,0x01,0x69,0xD0,0x9F,0x40,0x01,0x08,0x20,0xA9,0x44, +0xB0,0x9F,0x44,0x01,0x08,0x20,0xA9,0x40,0x30,0xC6,0xF5,0xE1,0x00,0xA9,0x34,0x1B, +0xF0,0x01,0x1D,0x01,0x69,0xD1,0x53,0x8F,0x00,0x02,0x00,0x00,0x1B,0xBC,0x3C,0x8F, +0x00,0x02,0xA9,0x30,0xD2,0x00,0xA7,0xFC,0x31,0x72,0xFF,0x05,0xFF,0xFF,0xFF,0xFF, +0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,0x00,0x00,0x00,0x00,0x90,0x07,0xA9,0x76, +0xD0,0xA9,0x28,0x51,0xD3,0x8F,0xFF,0xFF,0x0F,0x00,0x51,0x13,0x0E,0xC0,0x8F,0x00, +0x00,0x10,0x00,0x51,0xCA,0x8F,0xFF,0xFF,0x0F,0x00,0x51,0x30,0xEC,0xF5,0x13,0x03, +0x31,0x67,0xF5,0xDE,0xCF,0x3A,0x00,0xA9,0x4C,0x90,0x08,0xA9,0x76,0xCA,0x8F,0xFF, +0xFF,0x0F,0x00,0x51,0xC8,0x8F,0x00,0xF8,0x0F,0x00,0x51,0xD1,0x51,0x54,0x1E,0x34, +0xDE,0xAF,0xA9,0x56,0xD2,0x00,0xA7,0xFC,0xF0,0x01,0x1D,0x01,0x69,0xD0,0x66,0x55, +0xD0,0x55,0x61,0xD0,0x61,0x50,0xD1,0x50,0x55,0x13,0x0C,0x90,0x09,0xA9,0x76,0x11, +0x3E,0x90,0x0A,0xA9,0x76,0x11,0x38,0xF0,0x00,0x1D,0x01,0x69,0xD5,0x86,0x12,0xD4, +0xE0,0x02,0x51,0x15,0xCA,0x8F,0xFF,0xFF,0x0F,0x00,0x51,0xC8,0x8F,0xFC,0x07,0x00, +0x00,0x51,0xD1,0x51,0x54,0x1E,0x02,0x11,0xB7,0xCA,0x8F,0xFF,0xFF,0x0F,0x00,0x51, +0xC0,0x8F,0x00,0x00,0x10,0x00,0x51,0xD1,0x51,0x54,0x1F,0x91,0x31,0x7C,0xFF,0xF0, +0x00,0x1D,0x01,0x69,0xD0,0x9F,0x40,0x01,0x08,0x20,0xA9,0x44,0xB0,0x9F,0x44,0x01, +0x08,0x20,0xA9,0x40,0x30,0xDA,0xF4,0xE1,0x00,0xA9,0x34,0x02,0x11,0xCB,0x05,0x02, +0x80,0x21,0x00,0x73,0xF6,0x18,0x00,0x31,0xF3,0x4D,0x45,0x4D,0x5F,0x45,0x43,0x43, +0x5F,0x45,0x72,0x72,0x6F,0x72,0x00,0x02,0x04,0x00,0x78,0x04,0x54,0x00,0x98,0x04, +0x31,0xA7,0xF4,0x01,0x01,0x01,0x90,0x01,0xA9,0x76,0x30,0x69,0xF3,0xC2,0x04,0x57, +0x30,0xF3,0xF5,0xE1,0x09,0xA9,0x34,0x05,0x90,0x02,0xA9,0x76,0x05,0xD0,0xA9,0x28, +0x51,0x30,0x06,0xF5,0x13,0x03,0x31,0x81,0xF4,0xD0,0x51,0x55,0xF0,0x00,0x08,0x01, +0xA9,0x34,0xC1,0x8F,0x00,0x08,0x00,0x00,0x55,0x58,0xD1,0x58,0x54,0x1B,0x06,0x31, +0x55,0x03,0x31,0xB6,0x00,0xE1,0x00,0xA9,0x34,0xF8,0xE0,0x22,0x69,0xF4,0xD0,0xA7, +0x04,0x58,0xF0,0x8F,0xBC,0x04,0x00,0x00,0x00,0x0D,0x58,0xD0,0x58,0xA7,0x04,0xD0, +0x02,0x61,0xCA,0x8F,0x80,0x00,0x00,0x00,0xA7,0x04,0xD1,0x61,0x02,0x12,0x70,0x91, +0xA7,0x04,0x3C,0x12,0x6A,0xC8,0x8F,0x80,0x00,0x00,0x00,0xA7,0x04,0xD0,0x02,0xC1, +0x00,0x02,0xCA,0x8F,0x80,0x00,0x00,0x00,0xA7,0x04,0xD1,0xC1,0x00,0x02,0x02,0x12, +0x4E,0x91,0xA7,0x04,0x3C,0x12,0x48,0xC8,0x8F,0x80,0x00,0x00,0x00,0xA7,0x04,0xD0, +0x03,0xC1,0x00,0x04,0xCA,0x8F,0x80,0x00,0x00,0x00,0xA7,0x04,0xD1,0xC1,0x00,0x04, +0x03,0x12,0x2C,0x91,0xA7,0x04,0x3C,0x12,0x26,0xC8,0x8F,0x80,0x00,0x00,0x00,0xA7, +0x04,0xD0,0x03,0xC1,0x00,0x06,0xCA,0x8F,0x80,0x00,0x00,0x00,0xA7,0x04,0xD1,0xC1, +0x00,0x06,0x03,0x12,0x0A,0x91,0xA7,0x04,0x3C,0x12,0x04,0x10,0x07,0x11,0x1C,0x10, +0x03,0x31,0xB3,0x02,0xCA,0x8F,0xFF,0x1F,0x00,0x00,0xA7,0x04,0xD4,0x61,0xD4,0xC1, +0x00,0x02,0xD4,0xC1,0x00,0x04,0xD4,0xC1,0x00,0x06,0x05,0xF0,0x00,0x08,0x01,0xA9, +0x34,0xF0,0x01,0x1D,0x01,0x69,0xDA,0x19,0x12,0xD2,0x00,0x67,0xD0,0x51,0x55,0xD0, +0x51,0xA9,0x3C,0xDE,0xEF,0x28,0x00,0x00,0x00,0xA9,0x4C,0xD0,0xA7,0x04,0x58,0x90, +0x8F,0xBC,0x58,0xAA,0x8F,0x00,0x10,0x58,0xD0,0x58,0xA7,0x04,0xD0,0x02,0x61,0xA8, +0x8F,0x00,0x10,0x58,0x94,0x58,0xD0,0x58,0xA7,0x04,0xD0,0x02,0xA9,0x38,0xD5,0x61, +0x01,0xD0,0x02,0x61,0xB4,0xA9,0x38,0xEF,0x09,0x14,0x67,0x58,0xEF,0x1D,0x03,0x67, +0x50,0xD2,0x00,0xA9,0x3C,0x90,0x03,0xA9,0x76,0x91,0xA9,0x3B,0x01,0x12,0x1D,0x90, +0x04,0xA9,0x76,0xED,0x09,0x14,0x51,0x58,0x12,0x12,0x90,0x05,0xA9,0x76,0xD1,0x50, +0x01,0x12,0x09,0xC0,0x8F,0x00,0x02,0x00,0x00,0x51,0x11,0x03,0x31,0x7F,0x02,0xD0, +0x51,0xA9,0x3C,0xDE,0xEF,0x28,0x00,0x00,0x00,0xA9,0x4C,0xD0,0xA7,0x04,0x58,0x90, +0x8F,0xBC,0x58,0xAA,0x8F,0x00,0x10,0x58,0xD0,0x58,0xA7,0x04,0xD0,0x02,0x61,0xA8, +0x8F,0x00,0x10,0x58,0x94,0x58,0xD0,0x58,0xA7,0x04,0xD0,0x02,0xA9,0x38,0xD5,0x61, +0x01,0xD0,0x02,0x61,0xB4,0xA9,0x38,0xEF,0x09,0x14,0x67,0x58,0xEF,0x1D,0x03,0x67, +0x50,0xD2,0x00,0xA9,0x3C,0x90,0x06,0xA9,0x76,0x91,0xA9,0x3B,0x01,0x12,0xAD,0x90, +0x07,0xA9,0x76,0xD1,0x50,0x01,0x12,0xA4,0x90,0x08,0xA9,0x76,0xEF,0x09,0x14,0x55, +0x50,0xD1,0x50,0x58,0x12,0x96,0x11,0x03,0x31,0x13,0x02,0xD0,0x03,0x52,0xC0,0x8F, +0x00,0x02,0x00,0x00,0x51,0xD0,0x51,0xA9,0x3C,0xDE,0xEF,0x28,0x00,0x00,0x00,0xA9, +0x4C,0xD0,0xA7,0x04,0x58,0x90,0x8F,0xBC,0x58,0xAA,0x8F,0x00,0x10,0x58,0xD0,0x58, +0xA7,0x04,0xD0,0x03,0x61,0xA8,0x8F,0x00,0x10,0x58,0x94,0x58,0xD0,0x58,0xA7,0x04, +0xD0,0x03,0xA9,0x38,0xD5,0x61,0x01,0xD0,0x03,0x61,0xB4,0xA9,0x38,0xEF,0x09,0x14, +0x67,0x58,0xEF,0x1D,0x03,0x67,0x50,0xD2,0x00,0xA9,0x3C,0x90,0x09,0xA9,0x76,0xB1, +0xA9,0x3A,0x01,0x12,0xA3,0x90,0x0A,0xA9,0x76,0xED,0x09,0x14,0x51,0x58,0x12,0x98, +0x90,0x0B,0xA9,0x76,0xD1,0x50,0x05,0x12,0x8F,0xC0,0x8F,0x00,0x02,0x00,0x00,0x51, +0xD0,0x51,0xA9,0x3C,0xDE,0xEF,0x28,0x00,0x00,0x00,0xA9,0x4C,0xD0,0xA7,0x04,0x58, +0x90,0x8F,0xBC,0x58,0xAA,0x8F,0x00,0x10,0x58,0xD0,0x58,0xA7,0x04,0xD0,0x03,0x61, +0xA8,0x8F,0x00,0x10,0x58,0x94,0x58,0xD0,0x58,0xA7,0x04,0xD0,0x03,0xA9,0x38,0xD5, +0x61,0x01,0xD0,0x03,0x61,0xB4,0xA9,0x38,0xEF,0x09,0x14,0x67,0x58,0xEF,0x1D,0x03, +0x67,0x50,0xD2,0x00,0xA9,0x3C,0x90,0x0C,0xA9,0x76,0x91,0xA9,0x3A,0x01,0x12,0x2C, +0x90,0x0D,0xA9,0x76,0xD1,0x50,0x07,0x12,0x23,0x90,0x0E,0xA9,0x76,0xC3,0x8F,0x00, +0x02,0x00,0x00,0x51,0x50,0xEF,0x09,0x14,0x50,0x50,0xD1,0x50,0x58,0x12,0x0D,0xCA, +0x8F,0xFF,0x1F,0x00,0x00,0xA7,0x04,0xD2,0x00,0x67,0x11,0x03,0x31,0x1F,0x01,0xD0, +0x55,0x51,0xD1,0x53,0x8F,0x00,0x02,0x00,0x00,0x1B,0x0C,0xD3,0x8F,0xFF,0xFF,0x0F, +0x00,0x51,0x13,0x03,0x31,0x93,0x00,0xDE,0xEF,0xA8,0x00,0x00,0x00,0x56,0xD2,0x00, +0x67,0xD0,0xA7,0x04,0x58,0xF0,0x8F,0x00,0x04,0x00,0x00,0x00,0x0D,0x58,0x90,0x0F, +0xA9,0x76,0x8D,0x66,0x8F,0xBC,0x58,0xD0,0x58,0xA7,0x04,0x90,0x10,0xA9,0x76,0xD4, +0x61,0xE1,0x00,0xA9,0x34,0x17,0xE0,0x22,0x69,0x13,0xF0,0x00,0x07,0x01,0x58,0xD0, +0x58,0xA7,0x04,0xD5,0x61,0x12,0x47,0x91,0xA7,0x04,0x58,0x12,0x41,0x90,0x11,0xA9, +0x76,0xCA,0x8F,0xFF,0x1F,0x00,0x00,0xA7,0x04,0xDE,0xEF,0x11,0x00,0x00,0x00,0xA9, +0x4C,0xD0,0x03,0xA9,0x38,0x90,0x12,0xA9,0x76,0xF0,0x01,0x1D,0x01,0x69,0xD5,0x61, +0xF0,0x00,0x1D,0x01,0x69,0x90,0x13,0xA9,0x76,0x9A,0x67,0x50,0x9A,0x66,0x52,0x91, +0x50,0x52,0x12,0x13,0x90,0x14,0xA9,0x76,0x91,0xA9,0x3A,0x01,0x12,0x09,0xD6,0x56, +0x95,0x66,0x13,0x06,0x31,0x77,0xFF,0x31,0x74,0x00,0xF0,0x00,0x1D,0x01,0x69,0xE1, +0x08,0xA9,0x34,0x03,0x30,0x69,0xF3,0xC0,0x53,0x51,0xD1,0x51,0x54,0x1F,0x03,0x31, +0x7F,0xFC,0x31,0x84,0xFC,0x03,0x05,0x06,0x07,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, +0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x1B,0x1D,0x1E,0x21,0x22,0x23,0x24,0x25, +0x26,0x27,0x28,0x2B,0x2D,0x2E,0x30,0x33,0x35,0x36,0x39,0x3A,0x3C,0x3F,0x41,0x42, +0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,0x51,0x52, +0x53,0x54,0x55,0x56,0x57,0x59,0x5A,0x5C,0x5F,0x60,0x61,0x62,0x63,0x64,0x65,0x66, +0x67,0x69,0x6A,0x6C,0x6F,0x71,0x72,0x74,0x77,0x78,0x7B,0x7D,0x7E,0x00,0xE1,0x00, +0xA9,0x34,0x0B,0xE2,0x08,0xA9,0x34,0x06,0xD0,0x55,0x51,0x31,0xF3,0xFC,0xDA,0x1F, +0x12,0xE1,0x00,0xA9,0x34,0x03,0xD0,0x55,0x51,0x30,0xD2,0xF0,0x30,0xCF,0xF0,0x30, +0xCC,0xF0,0x30,0xC9,0xF0,0x31,0x72,0xFF,0xD0,0x51,0xA9,0x3C,0xDE,0xEF,0x28,0x00, +0x00,0x00,0xA9,0x4C,0xD0,0xA7,0x04,0x58,0x90,0x8F,0xBC,0x58,0xAA,0x8F,0x00,0x10, +0x58,0xD0,0x58,0xA7,0x04,0xD0,0x52,0x61,0xA8,0x8F,0x00,0x10,0x58,0x94,0x58,0xD0, +0x58,0xA7,0x04,0xD0,0x52,0xA9,0x38,0xD5,0x61,0x01,0xD0,0x52,0x61,0xB4,0xA9,0x38, +0xEF,0x09,0x14,0x67,0x58,0xEF,0x1D,0x03,0x67,0x50,0xD2,0x00,0xA9,0x3C,0x05,0xB1, +0x03,0xA9,0x38,0x12,0x0E,0x95,0xA9,0x3A,0x12,0x09,0x96,0xA9,0x3A,0xD0,0xA9,0x4C, +0xBE,0x04,0x05,0xD0,0xBE,0x04,0xA9,0x48,0xDE,0xCF,0x72,0xFF,0xBE,0x04,0x05,0xB1, +0x02,0xA9,0x38,0x12,0x09,0x95,0xA9,0x3B,0x12,0x04,0x96,0xA9,0x3B,0x05,0x11,0xE3, +0x02,0x80,0x1E,0x00,0xB2,0xF1,0x19,0x00,0x70,0xEE,0x4D,0x45,0x4D,0x5F,0x4D,0x61, +0x73,0x6B,0x64,0x5F,0x45,0x72,0x72,0x73,0x00,0x01,0x04,0x00,0xA1,0x01,0x31,0xE9, +0xEF,0x01,0x01,0x01,0x90,0x01,0xA9,0x76,0x30,0xAB,0xEE,0xC2,0x04,0x57,0x30,0x35, +0xF1,0xE1,0x09,0xA9,0x34,0x05,0x90,0x02,0xA9,0x76,0x05,0xD0,0xA9,0x28,0x51,0x30, +0x48,0xF0,0x13,0x03,0x31,0xC3,0xEF,0xF0,0x00,0x08,0x01,0xA9,0x34,0xF0,0x00,0x1D, +0x01,0x69,0x90,0x03,0xA9,0x76,0xD0,0x51,0xA9,0x3C,0xF0,0x8F,0xBC,0x04,0x00,0x00, +0x00,0x0D,0x58,0xD0,0x58,0xA7,0x04,0xE1,0x00,0xA9,0x34,0x5E,0xE0,0x22,0x69,0x5A, +0xD0,0x03,0x61,0xCA,0x8F,0x80,0x00,0x00,0x00,0xA7,0x04,0xD1,0x61,0x03,0x12,0x39, +0x91,0xA7,0x04,0x3C,0x12,0x33,0xC8,0x8F,0x80,0x00,0x00,0x00,0xA7,0x04,0x3C,0x8F, +0x00,0x04,0x61,0xCA,0x8F,0x80,0x00,0x00,0x00,0xA7,0x04,0xD1,0x61,0x8F,0x00,0x04, +0x00,0x00,0x12,0x15,0x91,0xA7,0x04,0x3C,0x12,0x0F,0x9A,0x0A,0x61,0xD1,0x61,0x0A, +0x12,0x07,0x91,0xA7,0x04,0x8F,0x7E,0x13,0x11,0xF0,0x00,0x00,0x0D,0x58,0xD0,0x58, +0xA7,0x04,0xD4,0x61,0x31,0xCA,0x00,0x31,0xDD,0x00,0xF0,0x01,0x1D,0x01,0x69,0xF0, +0x8F,0xBC,0x00,0x00,0x00,0x00,0x0D,0x58,0xD0,0x58,0xA7,0x04,0x3C,0x8F,0x00,0x04, +0x61,0xD2,0x00,0x67,0x90,0x04,0xA9,0x76,0x94,0x58,0xD0,0x58,0xA7,0x04,0x90,0x0A, +0x61,0x90,0x05,0xA9,0x76,0xCB,0x8F,0x7F,0xFE,0xFF,0x1F,0x67,0x50,0xD1,0x50,0x8F, +0x00,0x00,0x00,0x20,0x12,0xC1,0xD2,0x00,0x67,0x90,0x06,0xA9,0x76,0xD0,0x61,0x50, +0xD1,0x0A,0x50,0x12,0xB2,0x90,0x07,0xA9,0x76,0xCB,0x8F,0x7F,0xFE,0xFF,0x1F,0x67, +0x50,0x12,0xA4,0x90,0x08,0xA9,0x76,0xD2,0x00,0x67,0x90,0x8F,0xBC,0x58,0xD0,0x58, +0xA7,0x04,0xD0,0x03,0x61,0x90,0x09,0xA9,0x76,0x94,0x58,0xD0,0x58,0xA7,0x04,0xD0, +0x03,0xA9,0x38,0x90,0x0B,0xA1,0x03,0xB4,0xA9,0x38,0x90,0x0A,0xA9,0x76,0x91,0xA9, +0x3A,0x01,0x12,0x53,0x90,0x0B,0xA9,0x76,0xCB,0x8F,0x7F,0xFE,0xFF,0x1F,0x67,0x50, +0xD1,0x50,0x8F,0x00,0x00,0x00,0x80,0x12,0x3E,0x90,0x0C,0xA9,0x76,0xC8,0x8F,0x00, +0x04,0x00,0x00,0xA7,0x04,0xD0,0x61,0x50,0xD1,0x03,0x50,0x12,0x2A,0x90,0x0D,0xA9, +0x76,0xD0,0x58,0xA7,0x04,0xD0,0x00,0x61,0xD2,0x00,0xA9,0x3C,0xF0,0x00,0x1D,0x01, +0x69,0xE1,0x08,0xA9,0x34,0x03,0x30,0xC7,0xF0,0xC0,0x53,0x51,0xD1,0x51,0x54,0x1F, +0x03,0x31,0x9B,0xFE,0x31,0xA0,0xFE,0xE1,0x00,0xA9,0x34,0x08,0xE2,0x08,0xA9,0x34, +0x03,0x31,0x99,0xFE,0x30,0x97,0xEE,0x31,0xE2,0xFF,0xB1,0x03,0xA9,0x38,0x12,0x0F, +0x95,0xA9,0x3A,0x12,0x0A,0x96,0xA9,0x3A,0xDE,0xCF,0x7B,0xFF,0xBE,0x04,0x05,0xD0, +0xBE,0x04,0xA9,0x48,0xDE,0xAF,0xD0,0xBE,0x04,0x05,0x02,0x80,0x19,0x00,0xD8,0xEF, +0x1D,0xF0,0x96,0xEC,0x4D,0x45,0x4D,0x5F,0x43,0x6F,0x72,0x72,0x65,0x63,0x74,0x69, +0x6F,0x6E,0x00,0x31,0x14,0xEE,0x01,0x01,0x01,0x90,0x01,0xA9,0x76,0x30,0xD6,0xEC, +0x30,0x63,0xEF,0xE1,0x09,0xA9,0x34,0x05,0x90,0x02,0xA9,0x76,0x05,0xC2,0x04,0x57, +0xD0,0xA9,0x28,0x51,0xDE,0xCF,0xB7,0x01,0xA9,0x4C,0x31,0x20,0x00,0x58,0x1C,0x1A, +0x5E,0x1F,0x5B,0x5D,0x19,0x68,0x2C,0x2A,0x6E,0x2F,0x6B,0x6D,0x29,0x70,0x34,0x32, +0x76,0x37,0x73,0x75,0x31,0x38,0x7C,0x7A,0x3E,0x7F,0x3B,0x3D,0x79,0x30,0x4A,0xEE, +0x13,0x03,0x31,0xC5,0xED,0xF0,0x00,0x08,0x01,0xA9,0x34,0xF0,0x01,0x1D,0x01,0x69, +0x3C,0x8F,0xBC,0x00,0x58,0xD2,0x00,0x67,0xDE,0xAF,0xC2,0x56,0xD0,0x01,0x52,0xD2, +0x00,0x55,0xD0,0x51,0xA9,0x3C,0x90,0x03,0xA9,0x76,0xD2,0x00,0x67,0xD2,0x55,0x55, +0xD0,0x58,0xA7,0x04,0xD0,0x52,0x61,0xE1,0x00,0xA9,0x34,0x1D,0xE0,0x22,0x69,0x19, +0xF0,0x00,0x07,0x01,0x58,0xF0,0x01,0x0A,0x01,0x58,0xD0,0x58,0xA7,0x04,0xD1,0x61, +0x52,0x12,0x66,0x91,0xA7,0x04,0x58,0x12,0x60,0xCA,0x8F,0x80,0x04,0x00,0x00,0xA7, +0x04,0x90,0x04,0xA9,0x76,0xD0,0x61,0x50,0x90,0x05,0xA9,0x76,0xD1,0x55,0x50,0x12, +0x45,0x90,0x06,0xA9,0x76,0xD0,0x67,0x50,0xCA,0x8F,0x7F,0xFE,0xFF,0x1F,0x50,0xD1, +0x8F,0x00,0x00,0x00,0x20,0x50,0x12,0x2E,0x90,0x07,0xA9,0x76,0xCB,0x8F,0xFF,0x01, +0x00,0xE0,0x51,0xA9,0x38,0xD0,0x67,0x50,0xCA,0x8F,0xFF,0x01,0x00,0xE0,0x50,0xD1, +0x50,0xA9,0x38,0x12,0x11,0x90,0x08,0xA9,0x76,0xD0,0x67,0x50,0xEF,0x00,0x07,0x50, +0x50,0x91,0x66,0x50,0x13,0x03,0x31,0xD6,0x00,0xD2,0x52,0x52,0xD5,0x55,0x12,0x03, +0x31,0x63,0xFF,0xD6,0x56,0x78,0x01,0x52,0x52,0x12,0xF5,0x90,0x09,0xA9,0x76,0xD3, +0x8F,0x80,0x01,0x00,0xC0,0x67,0x12,0xDE,0xD2,0x00,0x67,0x90,0x0A,0xA9,0x76,0xD4, +0x52,0x3C,0x8F,0xBC,0x00,0x58,0xEF,0x52,0x01,0x58,0x55,0xD2,0x55,0x55,0xF0,0x55, +0x52,0x01,0x58,0xD0,0x58,0xA7,0x04,0xD4,0x61,0xE1,0x00,0xA9,0x34,0x1C,0xE0,0x22, +0x69,0x18,0xF0,0x00,0x07,0x01,0x58,0xF0,0x01,0x0A,0x01,0x58,0xD0,0x58,0xA7,0x04, +0xD5,0x61,0x12,0x45,0x91,0xA7,0x04,0x58,0x12,0x3F,0xCA,0x8F,0x80,0x04,0x00,0x00, +0xA7,0x04,0x90,0x0B,0xA9,0x76,0xD0,0x61,0x50,0x90,0x0C,0xA9,0x76,0xD5,0x50,0x12, +0x5E,0x90,0x0D,0xA9,0x76,0xD0,0x67,0x50,0xE1,0x1D,0x50,0x53,0x90,0x0E,0xA9,0x76, +0xD3,0x8F,0x80,0x01,0x00,0xC0,0x50,0x12,0x46,0x90,0x0F,0xA9,0x76,0xD4,0x55,0xF0, +0x01,0x52,0x01,0x55,0x91,0x50,0x55,0x12,0x36,0xD2,0x00,0x67,0xD6,0x52,0xD1,0x52, +0x07,0x1E,0x03,0x31,0x7B,0xFF,0xCA,0x8F,0xFF,0x1F,0x00,0x00,0xA7,0x04,0xD4,0x61, +0xD2,0x00,0xA9,0x3C,0xF0,0x00,0x1D,0x01,0x69,0xE1,0x08,0xA9,0x34,0x03,0x30,0x9F, +0xEE,0xC0,0x53,0x51,0xD1,0x51,0x54,0x1F,0x03,0x31,0x71,0xFE,0x31,0x76,0xFE,0xE1, +0x00,0xA9,0x34,0x08,0xE2,0x08,0xA9,0x34,0x03,0x31,0x6F,0xFE,0x30,0x6F,0xEC,0x31, +0xE2,0xFF,0xD0,0xBE,0x04,0xA9,0x48,0xDE,0xAF,0xE5,0xBE,0x04,0x05,0x02,0x80,0x2E, +0x00,0x17,0x05,0x0A,0xEE,0x18,0x00,0x4D,0x45,0x4D,0x5F,0x46,0x44,0x4D,0x5F,0x4C, +0x6F,0x67,0x69,0x63,0x00,0x0A,0x00,0x00,0x00,0x02,0xC5,0xEA,0x02,0x00,0x00,0x00, +0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xDE,0xAF,0xFD,0xC9,0x80, +0x00,0x90,0x01,0xA9,0x76,0x01,0x01,0x01,0x01,0x31,0xDE,0xEB,0x30,0x2D,0xEB,0xD0, +0x02,0xA9,0x38,0xD4,0xA9,0x3C,0xDB,0x1B,0x50,0x12,0x05,0xD0,0x0B,0x50,0x11,0x0C, +0x93,0x0F,0x50,0x12,0x07,0x78,0x8F,0xFC,0x50,0x50,0x11,0xF4,0xEF,0x00,0x04,0x50, +0xA9,0x2C,0xD0,0x8F,0x00,0x01,0x08,0x20,0x51,0xD4,0x50,0xD0,0x10,0x53,0xD0,0x61, +0x54,0xED,0x02,0x03,0x54,0x05,0x12,0x2A,0xEF,0x00,0x02,0x54,0x56,0x13,0x23,0xD1, +0x56,0x03,0x12,0x02,0xD6,0x56,0x90,0x02,0xA9,0x76,0xEF,0x02,0x04,0x51,0x52,0xEF, +0x02,0x02,0x51,0x55,0xD1,0x55,0x56,0x1E,0x09,0xE1,0x1F,0x54,0x1B,0xF0,0x01,0x52, +0x01,0x50,0xC0,0x04,0x51,0xF5,0x53,0xC6,0xD0,0x50,0xA9,0x30,0xD1,0x50,0x01,0x12, +0x03,0x31,0x66,0xEB,0xE8,0xA9,0x30,0x03,0x31,0x49,0x04,0xDE,0xCF,0x45,0x04,0xA9, +0x4C,0xF0,0x01,0x1D,0x01,0x69,0xD0,0x8F,0x00,0x01,0x08,0x20,0xA9,0x28,0xD2,0x00, +0xA7,0xFC,0x90,0x03,0xA9,0x76,0xEF,0x02,0x04,0xA9,0x28,0x53,0xE0,0x53,0xA9,0x30, +0x11,0xC0,0x04,0xA9,0x28,0xD1,0xA9,0x28,0x8F,0x40,0x01,0x08,0x20,0x1F,0xE3,0x31, +0x7C,0x00,0xD0,0xB9,0x28,0x56,0xCA,0x8F,0xFF,0xFF,0x3F,0xFC,0x56,0x2C,0x00,0x8F, +0x00,0x00,0x8F,0x00,0x04,0x66,0xC1,0x04,0x56,0x51,0xD0,0x8F,0xAA,0xAA,0xAA,0xAA, +0x81,0xD0,0x8F,0x55,0x55,0x55,0x55,0x81,0xD2,0x00,0x81,0xEF,0x02,0x04,0xA9,0x28, +0x50,0x13,0xBE,0xEF,0x02,0x02,0xA9,0x28,0x50,0x78,0x07,0x50,0x50,0xC0,0x50,0x51, +0xEF,0x04,0x02,0xA9,0x28,0x50,0xC1,0x50,0xA9,0x2C,0x52,0xD2,0x00,0x81,0xF5,0x52, +0xFA,0xD0,0x8F,0x00,0xFC,0xFF,0xFF,0x81,0xD0,0x8F,0x00,0x00,0xF0,0xFF,0x81,0xD0, +0x8F,0x00,0x00,0x00,0xC0,0x81,0xC1,0x8F,0x00,0x02,0x00,0x00,0x56,0x51,0x9A,0x8F, +0x80,0x52,0xD2,0x00,0x81,0xF5,0x52,0xFA,0x31,0x76,0xFF,0x31,0x96,0x03,0xD0,0x8F, +0x00,0x01,0x08,0x20,0xA9,0x28,0xEF,0x02,0x04,0xA9,0x28,0x53,0xE0,0x53,0xA9,0x30, +0x11,0xC0,0x04,0xA9,0x28,0xD1,0xA9,0x28,0x8F,0x40,0x01,0x08,0x20,0x1F,0xE7,0x31, +0xFB,0x00,0xD0,0xB9,0x28,0x56,0xCA,0x8F,0xFF,0xFF,0x3F,0xFC,0x56,0xD0,0x56,0x51, +0x90,0x04,0xA9,0x76,0xD5,0x81,0x12,0xC3,0x90,0x05,0xA9,0x76,0xD1,0x81,0x8F,0xAA, +0xAA,0xAA,0xAA,0x12,0xB6,0x90,0x06,0xA9,0x76,0xD1,0x81,0x8F,0x55,0x55,0x55,0x55, +0x12,0xA9,0x90,0x07,0xA9,0x76,0xD1,0x81,0x8F,0xFF,0xFF,0xFF,0xFF,0x12,0x9C,0xEF, +0x02,0x04,0xA9,0x28,0x50,0x12,0x03,0x31,0x9C,0x00,0xEF,0x02,0x02,0xA9,0x28,0x50, +0x78,0x07,0x50,0x50,0xC0,0x51,0x50,0xD1,0x51,0x50,0x1E,0x0B,0x90,0x08,0xA9,0x76, +0xD5,0x81,0x13,0xF3,0x31,0x7C,0x00,0xEF,0x04,0x02,0xA9,0x28,0x50,0xC1,0x50,0xA9, +0x2C,0x52,0x90,0x09,0xA9,0x76,0xD1,0x81,0x8F,0xFF,0xFF,0xFF,0xFF,0x12,0x64,0xF5, +0x52,0xF4,0x90,0x0A,0xA9,0x76,0xD1,0x81,0x8F,0x00,0xFC,0xFF,0xFF,0x12,0x54,0x90, +0x0B,0xA9,0x76,0xD1,0x81,0x8F,0x00,0x00,0xF0,0xFF,0x12,0x47,0x90,0x0C,0xA9,0x76, +0xD1,0x81,0x8F,0x00,0x00,0x00,0xC0,0x12,0x3A,0xC1,0x8F,0x00,0x02,0x00,0x00,0x56, +0x53,0x90,0x0D,0xA9,0x76,0xD5,0x81,0x12,0x2A,0xD1,0x51,0x53,0x1F,0xF7,0x9A,0x8F, +0x80,0x52,0x90,0x0E,0xA9,0x76,0xD1,0x81,0x8F,0xFF,0xFF,0xFF,0xFF,0x12,0x14,0xF5, +0x52,0xF0,0x90,0x0F,0xA9,0x76,0xD3,0x8F,0x00,0x00,0x00,0xE0,0xA7,0xFC,0x12,0x03, +0x31,0x0E,0xFF,0x31,0x8E,0x02,0x90,0x10,0xA9,0x76,0xC1,0x8F,0x00,0x04,0x00,0x00, +0x56,0x53,0xD5,0x81,0x12,0xED,0xD1,0x51,0x53,0x1F,0xF7,0x11,0xD5,0x90,0x11,0xA9, +0x76,0xD4,0x51,0x9A,0x8F,0x7C,0x52,0x30,0x80,0x02,0x90,0x12,0xA9,0x76,0xD5,0x81, +0x12,0x40,0x90,0x13,0xA9,0x76,0xD1,0x81,0x8F,0xAA,0xAA,0xAA,0xAA,0x12,0x33,0x90, +0x14,0xA9,0x76,0xD1,0x81,0x8F,0x55,0x55,0x55,0x55,0x12,0x26,0x90,0x15,0xA9,0x76, +0xD1,0x81,0x8F,0xFF,0xFF,0xFF,0xFF,0x12,0x19,0x90,0x16,0xA9,0x76,0xD5,0x81,0x12, +0x11,0xF5,0x52,0xF9,0x90,0x17,0xA9,0x76,0xD3,0x8F,0x00,0x00,0x00,0xE0,0xA7,0xFC, +0x13,0x03,0x31,0x1F,0x02,0x90,0x18,0xA9,0x76,0xD0,0x8F,0x00,0x01,0x08,0x20,0xA9, +0x28,0x31,0x1C,0x00,0xEF,0x02,0x04,0xA9,0x28,0x53,0xE0,0x53,0xA9,0x30,0x11,0xC0, +0x10,0xA9,0x28,0xD1,0xA9,0x28,0x8F,0x40,0x01,0x08,0x20,0x1F,0xE7,0x31,0xA8,0x00, +0xD0,0xB9,0x28,0x51,0xCA,0x8F,0xFF,0xFF,0x3F,0xFC,0x51,0x30,0xAD,0x01,0xEF,0x04, +0x02,0xA9,0x28,0x50,0xC1,0x50,0xA9,0x2C,0x51,0x78,0x02,0x50,0x56,0xEF,0x56,0x04, +0xA9,0x30,0x56,0xD4,0x58,0xE9,0x56,0x2A,0xD6,0x58,0x78,0x8F,0xFF,0x56,0x56,0x11, +0xF4,0xEF,0x04,0x02,0xA9,0x28,0xAE,0xFC,0xC0,0x0C,0xAE,0xFC,0xF0,0x01,0xAE,0xFC, +0x01,0xA9,0x34,0xE0,0x00,0xA9,0x34,0x03,0x31,0x6C,0x01,0x90,0xA9,0x76,0xA9,0x39, +0x11,0x9D,0xEF,0x04,0x02,0xA9,0x28,0x50,0x12,0x02,0xD7,0x58,0xD0,0x58,0x56,0x13, +0x44,0xD4,0x50,0xC0,0x51,0x50,0xF5,0x56,0xFA,0x90,0x19,0xA9,0x76,0xD1,0x50,0x52, +0x12,0xBF,0xD0,0x58,0x56,0xD6,0x50,0xF5,0x56,0xFB,0x90,0x1A,0xA9,0x76,0xD1,0x50, +0x53,0x12,0xAE,0xD0,0x58,0x56,0xD6,0x50,0xF5,0x56,0xFB,0x90,0x1B,0xA9,0x76,0xD1, +0x50,0x54,0x12,0x9D,0xD0,0x58,0x56,0xD6,0x50,0xF5,0x56,0xFB,0x90,0x1C,0xA9,0x76, +0xD1,0x50,0x55,0x12,0x8C,0x31,0x47,0xFF,0x30,0x5F,0x01,0xD0,0x02,0x53,0x3C,0x8F, +0x00,0x02,0x51,0x9A,0x8F,0x80,0x52,0x90,0x1D,0xA9,0x76,0xD5,0x81,0x13,0x03,0x31, +0x32,0x01,0xF5,0x52,0xF6,0xF5,0x53,0xE6,0x90,0x1E,0xA9,0x76,0xD3,0x8F,0x00,0x00, +0x00,0xE0,0xA7,0xFC,0x12,0xE9,0x90,0x1F,0xA9,0x76,0xD0,0x8F,0x00,0x01,0x08,0x20, +0xA9,0x28,0x31,0x1C,0x00,0xEF,0x02,0x04,0xA9,0x28,0x53,0xE0,0x53,0xA9,0x30,0x11, +0xC0,0x10,0xA9,0x28,0xD1,0xA9,0x28,0x8F,0x40,0x01,0x08,0x20,0x1F,0xE7,0x31,0x6A, +0x00,0xD0,0xB9,0x28,0x51,0xCA,0x8F,0xFF,0xFF,0x3F,0xFC,0x51,0x30,0xAC,0x00,0xD1, +0xA9,0x28,0x8F,0x00,0x01,0x08,0x20,0x12,0x08,0xED,0x00,0x04,0xA9,0x30,0x01,0x13, +0xCF,0x9A,0x8F,0x80,0x51,0x90,0x20,0xA9,0x76,0xD1,0x52,0x51,0x12,0x1B,0x90,0x21, +0xA9,0x76,0xD1,0x53,0x51,0x12,0x12,0x90,0x22,0xA9,0x76,0xD1,0x54,0x51,0x12,0x09, +0x90,0x23,0xA9,0x76,0xD1,0x55,0x51,0x13,0xA7,0xEF,0x04,0x02,0xA9,0x28,0xAE,0xFC, +0xC0,0x0C,0xAE,0xFC,0xF0,0x01,0xAE,0xFC,0x01,0xA9,0x34,0xE0,0x00,0xA9,0x34,0x03, +0x31,0x54,0x00,0x90,0xA9,0x76,0xA9,0x39,0x31,0x85,0xFF,0xEF,0x0C,0x04,0xA9,0x34, +0x50,0x13,0x0E,0x97,0xA9,0x38,0x13,0x11,0xF0,0x50,0x00,0x04,0xA9,0x3C,0x31,0xFC, +0xFD,0xF0,0x00,0x28,0x04,0x69,0x31,0x81,0xE7,0xF0,0x50,0x04,0x04,0xA9,0x3C,0xD2, +0x50,0x50,0xD2,0xA9,0x3C,0x51,0xEF,0x00,0x04,0x50,0x50,0xEF,0x00,0x04,0x51,0x51, +0xC8,0x50,0x51,0xD2,0x51,0x51,0xEF,0x00,0x04,0x51,0x51,0x13,0xD4,0xF0,0x51,0x28, +0x04,0x69,0xE0,0x00,0xA9,0x34,0xCF,0x30,0x23,0xE9,0x05,0xCA,0x8F,0x00,0x02,0x00, +0x00,0x67,0xC8,0x8F,0x00,0x04,0x00,0x00,0x67,0xD0,0x61,0x50,0xEF,0x00,0x0A,0x50, +0x52,0xEF,0x0A,0x0A,0x50,0x53,0xEF,0x14,0x0A,0x50,0x54,0xEF,0x1E,0x02,0x50,0x55, +0xEF,0x00,0x07,0x67,0x50,0x78,0x02,0x50,0x50,0xC8,0x50,0x55,0xCA,0x8F,0x00,0x04, +0x00,0x00,0x67,0x05,0x90,0xA9,0x76,0xA9,0x39,0x30,0x45,0x00,0xE1,0x00,0xA9,0x34, +0xB6,0x31,0x06,0xE7,0x30,0x3A,0x00,0x31,0xA4,0xE8,0xBB,0x04,0xCA,0x8F,0x00,0x02, +0x00,0x00,0x9F,0x44,0x01,0x08,0x20,0xD0,0x8F,0x00,0x01,0x08,0x20,0x52,0xC8,0x20, +0x62,0xC8,0x20,0xA2,0x10,0xC8,0x20,0xA2,0x20,0xC8,0x20,0xA2,0x30,0xC8,0x8F,0x00, +0x02,0x00,0x00,0xA2,0x44,0xBA,0x04,0x05,0xBB,0x8F,0xFF,0x00,0xD0,0x01,0x56,0x11, +0x06,0xBB,0x8F,0xFF,0x00,0xD4,0x56,0xCA,0x8F,0xFF,0x1F,0x00,0x00,0x9F,0x44,0x01, +0x08,0x20,0xC8,0x8F,0x00,0x04,0x00,0x00,0x9F,0x44,0x01,0x08,0x20,0xD4,0x55,0xD0, +0x01,0x54,0xD0,0x8F,0x04,0x01,0x08,0x20,0x53,0xD0,0x03,0x50,0x11,0x03,0xD0,0x04, +0x50,0xD0,0x53,0x52,0xD0,0x82,0x57,0xE0,0x1F,0x57,0x24,0xF5,0x50,0xF3,0x78,0x01, +0x54,0x54,0xC0,0x10,0x53,0xE1,0x04,0x54,0xE5,0xCA,0x8F,0xFF,0x1F,0x00,0x00,0x9F, +0x44,0x01,0x08,0x20,0xE9,0x56,0x03,0xD0,0x55,0x6E,0xBA,0x8F,0xFF,0x00,0x05,0xCA, +0x8F,0xFF,0xFF,0x3F,0xFC,0x57,0xD5,0x67,0x12,0x0D,0xED,0x00,0x07,0x9F,0x44,0x01, +0x08,0x20,0x00,0x12,0x02,0x11,0xC7,0xC8,0x54,0x55,0x11,0xC2,0x02,0x80,0x76,0x00, +0x07,0x06,0x71,0x00,0x19,0x00,0x4D,0x45,0x4D,0x5F,0x41,0x64,0x64,0x72,0x5F,0x73, +0x68,0x72,0x74,0x73,0x00,0x0A,0x02,0xDA,0xE4,0x01,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x04,0x03,0xE4,0xE4,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00, +0x00,0x04,0x00,0x02,0xF5,0xE4,0x02,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03, +0x4E,0x00,0x02,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xAA,0xAA,0xAA,0xAA,0x03, +0x53,0x00,0x02,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x55,0x55,0x55,0x55,0x00, +0x00,0x00,0x00,0x70,0x61,0x74,0x32,0x00,0x70,0x61,0x74,0x33,0x00,0x01,0x04,0x00, +0xCF,0xE7,0x31,0xD5,0xE5,0x01,0x01,0x01,0x90,0x01,0xA9,0x76,0x31,0x33,0x02,0xDE, +0xAF,0xFD,0xC9,0x80,0x00,0xDD,0xA9,0x3C,0x30,0x8B,0xE4,0xD0,0x8E,0xA9,0x3C,0x30, +0x14,0xE7,0xE1,0x09,0xA9,0x34,0x05,0x90,0x02,0xA9,0x76,0x05,0x90,0x03,0xA9,0x76, +0xF0,0x00,0x1D,0x01,0x69,0xB4,0xA9,0x28,0xB4,0xA9,0x2C,0xD0,0xA9,0x28,0x51,0x30, +0x18,0xE6,0x12,0x29,0x3C,0x8F,0x00,0x08,0x53,0xD0,0x8F,0x55,0x55,0x55,0x45,0x52, +0xD0,0x52,0x81,0xD0,0x52,0x81,0xD0,0x52,0x81,0xD0,0x52,0x81,0xD0,0x52,0x81,0xD0, +0x52,0x81,0xD0,0x52,0x81,0xD0,0x52,0x81,0xF5,0x53,0xE5,0x11,0xD2,0x90,0x04,0xA9, +0x76,0xDE,0xCF,0x4A,0x00,0xA9,0x4C,0xD0,0xA9,0x28,0x51,0xD0,0x8F,0x55,0x55,0x55, +0x45,0x52,0xD0,0xA9,0x38,0x55,0x30,0xD1,0xE5,0x13,0x03,0x31,0x7F,0x00,0xD0,0x8F, +0x40,0x01,0x08,0x20,0x56,0xD2,0x00,0x66,0xF0,0x01,0x1D,0x01,0x69,0x3C,0x8F,0x80, +0x00,0x54,0x01,0x01,0xD0,0x61,0x50,0xD1,0x50,0x52,0x12,0x19,0xE0,0x1D,0x66,0x24, +0xD0,0x55,0x61,0xC0,0x04,0x51,0xF5,0x54,0xEB,0xB5,0x51,0x12,0xD1,0x11,0xC7,0x90, +0x05,0xA9,0x76,0x11,0x3E,0x90,0x06,0xA9,0x76,0xD1,0x50,0x55,0x12,0x35,0x90,0x07, +0xA9,0x76,0x11,0x2F,0x90,0x08,0xA9,0x76,0xE1,0x00,0xA9,0x34,0x26,0xD2,0x00,0x66, +0xE0,0x22,0x69,0x02,0x11,0xCA,0x90,0x09,0xA9,0x76,0xD0,0x52,0x61,0xD0,0x61,0x50, +0xD1,0x50,0x52,0x12,0x0E,0xE0,0x1D,0x66,0x0A,0x30,0x34,0xE7,0xF0,0x01,0x1D,0x01, +0x69,0x11,0xAD,0x30,0x18,0xE5,0xF0,0x01,0x1D,0x01,0x69,0x11,0xAC,0x90,0x0A,0xA9, +0x76,0xDE,0xCF,0x46,0x00,0xA9,0x4C,0xD0,0xA9,0x28,0x51,0xD0,0xA9,0x38,0x52,0xD0, +0xA9,0x3C,0x55,0x30,0x34,0xE5,0x13,0x03,0x31,0x7E,0x00,0xD0,0x8F,0x40,0x01,0x08, +0x20,0x56,0xD2,0x00,0x66,0xF0,0x01,0x1D,0x01,0x69,0x3C,0x8F,0x80,0x00,0x54,0x01, +0xD0,0x61,0x50,0xD1,0x50,0x52,0x12,0x19,0xE0,0x1D,0x66,0x24,0xD0,0x55,0x61,0xC0, +0x04,0x51,0xF5,0x54,0xEB,0xB5,0x51,0x12,0xD2,0x11,0xC8,0x90,0x0B,0xA9,0x76,0x11, +0x3E,0x90,0x0C,0xA9,0x76,0xD1,0x50,0x55,0x12,0x35,0x90,0x0D,0xA9,0x76,0x11,0x2F, +0x90,0x0E,0xA9,0x76,0xE1,0x00,0xA9,0x34,0x26,0xD2,0x00,0x66,0xE0,0x22,0x69,0x02, +0x11,0xCA,0x90,0x0F,0xA9,0x76,0xD0,0x52,0x61,0xD0,0x61,0x50,0xD1,0x50,0x52,0x12, +0x0E,0xE0,0x1D,0x66,0x0A,0x30,0x98,0xE6,0xF0,0x01,0x1D,0x01,0x69,0x11,0xAD,0x30, +0x7C,0xE4,0xF0,0x01,0x1D,0x01,0x69,0x11,0xAC,0x90,0x10,0xA9,0x76,0xDE,0xCF,0x43, +0x00,0xA9,0x4C,0xD0,0xA9,0x28,0x51,0xD0,0xA9,0x3C,0x52,0xD0,0xA9,0x38,0x55,0x30, +0x98,0xE4,0x13,0x03,0x31,0x13,0xE4,0xD0,0x8F,0x40,0x01,0x08,0x20,0x56,0xD2,0x00, +0x66,0xF0,0x01,0x1D,0x01,0x69,0x3C,0x8F,0x80,0x00,0x54,0x01,0xD0,0x61,0x50,0xD1, +0x50,0x52,0x12,0x16,0xE0,0x1D,0x66,0x21,0xC0,0x04,0x51,0xF5,0x54,0xEE,0xB5,0x51, +0x12,0xD5,0x11,0xCB,0x90,0x11,0xA9,0x76,0x11,0x3E,0x90,0x12,0xA9,0x76,0xD1,0x50, +0x55,0x12,0x35,0x90,0x13,0xA9,0x76,0x11,0x2F,0x90,0x14,0xA9,0x76,0xE1,0x00,0xA9, +0x34,0x26,0xD2,0x00,0x66,0xE0,0x22,0x69,0x02,0x11,0xCD,0x90,0x15,0xA9,0x76,0xD0, +0x52,0x61,0xD0,0x61,0x50,0xD1,0x50,0x52,0x12,0x0E,0xE0,0x1D,0x66,0x0A,0x30,0xFF, +0xE5,0xF0,0x01,0x1D,0x01,0x69,0x11,0xB0,0x30,0xE3,0xE3,0xF0,0x01,0x1D,0x01,0x69, +0x11,0xAC,0xE1,0x00,0xA9,0x34,0x2E,0xE0,0x26,0x69,0x2A,0xED,0x28,0x04,0x69,0x00, +0x12,0x23,0xD5,0xA9,0x28,0x12,0x1E,0xD1,0xA9,0x2C,0x8F,0x00,0x00,0x00,0x04,0x12, +0x14,0xD1,0xA9,0x38,0x8F,0xAA,0xAA,0xAA,0xAA,0x12,0x0A,0xD1,0xA9,0x3C,0x8F,0x55, +0x55,0x55,0x55,0x13,0x03,0x31,0x97,0xFD,0x30,0xB1,0xE2,0x30,0xB8,0xE4,0xE1,0x09, +0xA9,0x34,0x05,0x90,0x16,0xA9,0x76,0x05,0xD0,0xA9,0x1C,0xA9,0x28,0xCA,0x8F,0xFF, +0xFF,0xC0,0xFF,0xA9,0x28,0xF0,0x00,0x1D,0x01,0x69,0xD4,0x51,0x30,0x3B,0xFC,0xD1, +0x51,0x8F,0x00,0x00,0x40,0x00,0x1F,0x03,0x31,0x52,0x00,0xD0,0x51,0xA9,0x3C,0xD1, +0x51,0xA9,0x28,0x13,0x1D,0x90,0x17,0xA9,0x76,0xD0,0x8F,0x55,0x55,0x55,0x45,0x52, +0xC8,0x8F,0x00,0x02,0x00,0x00,0x67,0x01,0xD0,0x52,0x81,0xB5,0x51,0x12,0xF9,0x31, +0xCD,0xFF,0x90,0x18,0xA9,0x76,0x30,0x38,0xFC,0x30,0xCF,0x02,0x13,0x02,0x11,0xBC, +0xD0,0x51,0x56,0xD0,0x8F,0x00,0x40,0x00,0x00,0x50,0xD0,0x52,0x81,0xF5,0x50,0xFA, +0xD0,0x56,0x51,0xC0,0x8F,0x00,0x00,0x40,0x00,0x51,0x31,0xDC,0xFF,0x90,0x19,0xA9, +0x76,0xC3,0x04,0x57,0x56,0xF0,0x01,0x1D,0x01,0x69,0x30,0x04,0xFC,0xD4,0x51,0x30, +0xC8,0xFB,0xD0,0x8F,0x55,0x55,0x55,0x45,0x54,0xD0,0x8F,0xAA,0xAA,0xAA,0xAA,0x52, +0xD1,0x51,0x8F,0x00,0x00,0x40,0x00,0x1F,0x03,0x31,0xB2,0x00,0xD0,0x51,0xA9,0x3C, +0xD1,0x51,0xA9,0x28,0x13,0x03,0x31,0x5F,0x00,0x30,0xD5,0xFB,0x30,0x6C,0x02,0x12, +0xCE,0xD2,0x00,0x66,0xDE,0xCF,0x46,0x00,0xA9,0x4C,0x3C,0x8F,0x80,0x00,0x55,0x9A, +0x8F,0x80,0x53,0x90,0x1A,0xA9,0x76,0x01,0xD1,0x61,0x54,0x12,0x31,0xE1,0x22,0x69, +0x1B,0xE1,0x1D,0x66,0x17,0xD2,0x00,0x66,0xD0,0x54,0x61,0xD1,0x61,0x54,0x12,0x1E, +0xE0,0x1D,0x66,0x1A,0x30,0xB9,0xE4,0xF0,0x01,0x1D,0x01,0x69,0xD0,0x52,0x81,0xF5, +0x53,0xD6,0xF5,0x55,0xCA,0xC0,0x8F,0x00,0x00,0x3F,0x00,0x51,0x11,0xAE,0x30,0x8D, +0xE2,0xF0,0x01,0x1D,0x01,0x69,0x11,0xEA,0x90,0x1B,0xA9,0x76,0xC8,0x8F,0x00,0x02, +0x00,0x00,0x67,0xDE,0xCF,0x20,0x00,0xA9,0x4C,0x9A,0x8F,0x80,0x53,0x01,0x01,0x01, +0xD1,0x61,0x54,0x12,0x12,0xD0,0x52,0x81,0xF5,0x53,0xF5,0xB5,0x51,0x12,0xEA,0x30, +0x46,0xFB,0x12,0x03,0x31,0x48,0xFF,0x30,0x10,0xFB,0xD0,0xA9,0x3C,0x51,0x01,0x01, +0xD0,0x54,0x81,0xB5,0x51,0x12,0xF9,0xD0,0xA9,0x3C,0x51,0x31,0x5B,0xFF,0x90,0x1C, +0xA9,0x76,0xF0,0x01,0x1D,0x01,0x69,0x30,0x27,0xFB,0xD4,0x51,0x30,0xEB,0xFA,0xD0, +0x8F,0xAA,0xAA,0xAA,0xAA,0x54,0xD0,0x8F,0x55,0x55,0x55,0x55,0x52,0xD1,0x51,0x8F, +0x00,0x00,0x40,0x00,0x1F,0x03,0x31,0xB1,0x00,0xD0,0x51,0xA9,0x3C,0xD1,0x51,0xA9, +0x28,0x13,0x03,0x31,0x5E,0x00,0x30,0xF8,0xFA,0x30,0x8F,0x01,0x12,0xCE,0xD2,0x00, +0x66,0xDE,0xCF,0x45,0x00,0xA9,0x4C,0x3C,0x8F,0x80,0x00,0x55,0x9A,0x8F,0x80,0x53, +0x90,0x1D,0xA9,0x76,0xD1,0x61,0x54,0x12,0x31,0xE1,0x22,0x69,0x1B,0xE1,0x1D,0x66, +0x17,0xD2,0x00,0x66,0xD0,0x54,0x61,0xD1,0x61,0x54,0x12,0x1E,0xE0,0x1D,0x66,0x1A, +0x30,0xDD,0xE3,0xF0,0x01,0x1D,0x01,0x69,0xD0,0x52,0x81,0xF5,0x53,0xD6,0xF5,0x55, +0xCB,0xC0,0x8F,0x00,0x00,0x3F,0x00,0x51,0x11,0xAF,0x30,0xB1,0xE1,0xF0,0x01,0x1D, +0x01,0x69,0x11,0xEA,0x90,0x1E,0xA9,0x76,0xC8,0x8F,0x00,0x02,0x00,0x00,0x67,0xDE, +0xCF,0x20,0x00,0xA9,0x4C,0x9A,0x8F,0x80,0x53,0x01,0x01,0x01,0xD1,0x61,0x54,0x12, +0x12,0xD0,0x52,0x81,0xF5,0x53,0xF5,0xB5,0x51,0x12,0xEA,0x30,0x6A,0xFA,0x12,0x03, +0x31,0x49,0xFF,0x30,0x34,0xFA,0xD0,0xA9,0x3C,0x51,0x01,0x01,0xD0,0x54,0x81,0xB5, +0x51,0x12,0xF9,0xD0,0xA9,0x3C,0x51,0x31,0x5C,0xFF,0x90,0x1F,0xA9,0x76,0xF0,0x01, +0x1D,0x01,0x69,0x30,0x4B,0xFA,0xD4,0x51,0x30,0x0F,0xFA,0xD0,0x8F,0x55,0x55,0x55, +0x55,0x54,0xD1,0x51,0x8F,0x00,0x00,0x40,0x00,0x1F,0x06,0x30,0x33,0xFA,0x31,0xF9, +0xE0,0xD0,0x51,0xA9,0x3C,0xD1,0x51,0xA9,0x28,0x13,0x03,0x31,0x5E,0x00,0x30,0x20, +0xFA,0x30,0xB7,0x00,0x12,0xD2,0xD2,0x00,0x66,0xDE,0xCF,0x45,0x00,0xA9,0x4C,0x3C, +0x8F,0x80,0x00,0x55,0x9A,0x8F,0x80,0x53,0x90,0x20,0xA9,0x76,0xD1,0x61,0x54,0x12, +0x31,0xE1,0x22,0x69,0x1B,0xE1,0x1D,0x66,0x17,0xD2,0x00,0x66,0xD0,0x54,0x61,0xD1, +0x61,0x54,0x12,0x1E,0xE0,0x1D,0x66,0x1A,0x30,0x05,0xE3,0xF0,0x01,0x1D,0x01,0x69, +0xC0,0x04,0x51,0xF5,0x53,0xD6,0xF5,0x55,0xCB,0xC0,0x8F,0x00,0x00,0x3F,0x00,0x51, +0x11,0xAF,0x30,0xD9,0xE0,0xF0,0x01,0x1D,0x01,0x69,0x11,0xEA,0x90,0x21,0xA9,0x76, +0xC8,0x8F,0x00,0x02,0x00,0x00,0x67,0xDE,0xCF,0x20,0x00,0xA9,0x4C,0x9A,0x8F,0x80, +0x53,0x01,0x01,0x01,0xD1,0x61,0x54,0x12,0x12,0xC0,0x04,0x51,0xF5,0x53,0xF5,0xB5, +0x51,0x12,0xEA,0x30,0x92,0xF9,0x12,0x03,0x31,0x4D,0xFF,0xD0,0xA9,0x3C,0x51,0x31, +0x6C,0xFF,0x30,0x5C,0xE0,0x30,0x25,0xE2,0xD2,0x00,0xA7,0xFC,0x05,0xD5,0x8E,0x30, +0x7F,0xF9,0x05,0x30,0x7B,0xF9,0xCA,0x8F,0x00,0x04,0x00,0x00,0x9F,0x44,0x01,0x08, +0x20,0xDA,0x00,0x25,0xDA,0x00,0x25,0xDA,0x00,0x27,0x05,0xD1,0x51,0x8F,0x00,0x00, +0x00,0x04,0x1E,0x21,0xD0,0xA9,0x1C,0x50,0xB4,0x50,0xD1,0x51,0x50,0x13,0x0A,0xEF, +0x16,0x04,0x51,0x50,0xE0,0x50,0xA9,0x36,0x09,0xC0,0x8F,0x00,0x00,0x40,0x00,0x51, +0x11,0xD9,0xB8,0x04,0x05,0xD0,0xA9,0x3C,0x51,0xC0,0x8F,0x00,0x00,0x01,0x00,0x51, +0xB9,0x04,0x05,0x02,0x80,0x80,0x00,0x9B,0xE1,0xE4,0xE1,0x16,0x00,0x4D,0x45,0x4D, +0x5F,0x52,0x65,0x66,0x72,0x65,0x73,0x68,0x00,0x0A,0x02,0x4E,0x00,0x01,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x04,0x03,0x54,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x04,0x00,0x00,0x00,0x04,0x03,0x58,0x00,0x02,0x04,0x00,0x00,0x00,0x00,0x00, +0x10,0x00,0x00,0x00,0x01,0x00,0x02,0xA1,0xDE,0x02,0x00,0x00,0x00,0x00,0x01,0x00, +0x00,0x00,0x03,0x5D,0x00,0x02,0x01,0x00,0x00,0x00,0x10,0x0E,0x00,0x00,0x14,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x74,0x61,0x72,0x74,0x00,0x65,0x6E,0x64, +0x00,0x69,0x6E,0x63,0x72,0x00,0x74,0x69,0x6D,0x65,0x5F,0x73,0x65,0x63,0x6F,0x6E, +0x64,0x73,0x00,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x01,0xA9,0x76,0x30,0x36,0xDE, +0x30,0xC3,0xE0,0xCA,0x0F,0xA9,0x28,0xCA,0x07,0xA9,0x30,0x12,0x04,0xD0,0x08,0xA9, +0x30,0xE1,0x09,0xA9,0x34,0x05,0x90,0x02,0xA9,0x76,0x05,0xF0,0x00,0x1D,0x01,0x69, +0xD0,0xA9,0x28,0x51,0x30,0xC3,0xDF,0x12,0x19,0x90,0x03,0xA9,0x76,0xD4,0x55,0xD2, +0x55,0x56,0x01,0x01,0x7D,0x55,0x61,0xC0,0x53,0x51,0xD1,0x51,0x54,0x1F,0xF5,0x31, +0xE2,0xFF,0x90,0x04,0xA9,0x76,0x78,0x14,0xA9,0x38,0xA9,0x5C,0x16,0xEF,0x85,0x97, +0xFF,0xFF,0xF0,0x01,0x1D,0x01,0x69,0xDE,0xCF,0x8E,0x00,0xA9,0x4C,0x90,0x05,0xA9, +0x76,0xD0,0xA9,0x28,0x51,0x30,0x82,0xDF,0x13,0x03,0x31,0xFD,0xDE,0xD4,0x50,0xD2, +0x50,0x52,0x01,0x01,0xF0,0x01,0x1D,0x01,0x69,0x7D,0x61,0x55,0xF0,0x00,0x1D,0x01, +0x69,0xD1,0x50,0x55,0x12,0x53,0xD1,0x52,0x56,0x12,0x54,0xE0,0x1D,0xA7,0xFC,0x0A, +0xC0,0x53,0x51,0xD1,0x51,0x54,0x1F,0xDC,0x11,0xCB,0x90,0x06,0xA9,0x76,0xE1,0x00, +0xA9,0x34,0x46,0xD2,0x00,0xA7,0xFC,0xF0,0x01,0x1D,0x01,0x69,0xD0,0x50,0x61,0xD0, +0x52,0xA1,0x04,0x7D,0x61,0x55,0xF0,0x00,0x1D,0x01,0x69,0xD1,0x50,0x55,0x12,0x29, +0xD1,0x52,0x56,0x12,0x24,0xE0,0x1D,0xA7,0xFC,0x05,0x30,0xF3,0xE0,0x11,0xC1,0xE0, +0x22,0x69,0x16,0xD2,0x00,0xA7,0xFC,0x11,0xB7,0x90,0x07,0xA9,0x76,0x11,0x0A,0x90, +0x08,0xA9,0x76,0x11,0x04,0x90,0x09,0xA9,0x76,0x30,0xC2,0xDE,0xF0,0x00,0x1D,0x01, +0x69,0xD1,0x53,0xA9,0x30,0x13,0x9C,0xD0,0x53,0xA9,0x30,0x31,0x1D,0xFF,0x02,0x80, +0x76,0x00,0x10,0xE0,0x00,0x00,0x19,0x00,0x4D,0x45,0x4D,0x5F,0x43,0x6F,0x75,0x6E, +0x74,0x5F,0x45,0x72,0x72,0x73,0x00,0x0A,0x02,0x34,0x00,0x02,0x01,0x00,0x00,0x00, +0x04,0x00,0x00,0x00,0x03,0x40,0x00,0x02,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00, +0x04,0x00,0x00,0x00,0x03,0x4B,0x00,0x02,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x69,0x72,0x73,0x74, +0x5F,0x62,0x6F,0x61,0x72,0x64,0x00,0x4C,0x61,0x73,0x74,0x5F,0x62,0x6F,0x61,0x72, +0x64,0x00,0x53,0x6F,0x66,0x74,0x5F,0x65,0x72,0x72,0x73,0x5F,0x61,0x6C,0x6C,0x6F, +0x77,0x65,0x64,0x00,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x01,0xA9,0x76,0xD0,0xA9, +0x30,0x58,0xD4,0xA9,0x30,0xD4,0xA9,0x34,0xD4,0xA9,0x38,0xD4,0xA9,0x3C,0xD4,0xA9, +0x40,0xD4,0xA9,0x44,0xD0,0xA9,0x1C,0xA9,0x48,0x90,0x02,0xA9,0x76,0xED,0x12,0x02, +0x69,0x03,0x13,0x03,0x31,0xFC,0x00,0x90,0x03,0xA9,0x76,0xCA,0x8F,0xFF,0x1F,0x00, +0x00,0x9F,0x44,0x01,0x08,0x20,0x30,0x31,0xDF,0xD0,0xA9,0x28,0x52,0xD0,0x8F,0xF0, +0x00,0x08,0x20,0x53,0x90,0x04,0xA9,0x76,0xC0,0x10,0x53,0xF5,0x52,0xF6,0xD0,0x04, +0x52,0xD0,0x63,0x54,0xE1,0x1F,0x54,0x5C,0xD4,0x56,0xEF,0x16,0x04,0x54,0x55,0x13, +0x08,0xA0,0x8F,0x00,0x04,0x56,0xF5,0x55,0xF8,0xB1,0x56,0xA9,0x20,0x1E,0x45,0xC1, +0x56,0xA9,0x1C,0x51,0x30,0xAD,0x00,0xEF,0x16,0x04,0x54,0x54,0xED,0x16,0x04,0xA9, +0x1C,0x54,0x12,0x1A,0xD0,0xA9,0x1C,0x51,0x3C,0xA9,0x20,0x54,0x78,0x8F,0xF7,0x54, +0x54,0xA0,0x8F,0x60,0x00,0x54,0xD1,0x54,0x50,0x1A,0x03,0xC2,0x54,0x50,0xC0,0x50, +0xA9,0x30,0xC0,0x8F,0x00,0x20,0x00,0x00,0xA9,0x34,0xEF,0x04,0x03,0x53,0x55,0xC0, +0x50,0x45,0xA9,0x38,0xD5,0x83,0xF5,0x52,0x98,0xEF,0x04,0x03,0x53,0x52,0xD1,0x52, +0xA9,0x2C,0x1F,0x8A,0xF0,0x03,0x12,0x02,0x69,0xD0,0xA9,0x1C,0x50,0xB4,0x50,0xC0, +0x8F,0x00,0x40,0x00,0x00,0x50,0xC0,0x00,0x50,0xD0,0x58,0xAE,0xFC,0xD4,0xA9,0x4C, +0xD0,0x80,0x55,0xD0,0x80,0x56,0xD0,0x80,0x57,0xD0,0x80,0x58,0xD0,0xAE,0xFC,0x50, +0xC0,0x55,0xA9,0x4C,0xC0,0x56,0xA9,0x4C,0xC0,0x57,0xA9,0x4C,0xC0,0x58,0xA9,0x4C, +0xE1,0x22,0x69,0x13,0xD1,0x50,0x8F,0xFF,0xFF,0xFF,0xFF,0x13,0x0A,0x90,0x05,0xA9, +0x76,0xD1,0xA9,0x4C,0x50,0x1A,0x0C,0x90,0x06,0xA9,0x76,0xD5,0xA9,0x30,0x12,0x03, +0x31,0xC7,0xDC,0x05,0xBB,0x8F,0x5C,0x00,0xD0,0x04,0x53,0xD4,0x50,0xD2,0x00,0x57, +0xE0,0x23,0x69,0x2C,0x3C,0x8F,0x40,0x00,0x52,0xD4,0x54,0xD0,0x20,0x55,0xCD,0x57, +0x81,0x56,0x13,0x11,0xEA,0x54,0x55,0x56,0x54,0x13,0x0A,0xD6,0x50,0xD6,0x54,0xC3, +0x54,0x20,0x55,0x12,0xEF,0xF5,0x52,0xE1,0xF5,0x53,0xD5,0xBA,0x8F,0x5C,0x00,0x05, +0x91,0x03,0x9F,0x05,0x00,0x04,0x20,0x13,0xCB,0xDE,0xEF,0x21,0x00,0x00,0x00,0x54, +0x9A,0x84,0x55,0x91,0x55,0x8F,0xFF,0x13,0x0E,0xC3,0xA9,0x1C,0x51,0x52,0xED,0x08, +0x06,0x52,0x55,0x12,0xEB,0x11,0xAD,0xC0,0x8F,0x00,0x01,0x00,0x00,0x51,0x11,0xC8, +0x00,0x02,0x04,0x06,0x09,0x0B,0x0D,0x0F,0xFF,0x00,0x01,0x08,0x20,0x00,0x00,0x00, +0x00,0x14,0x01,0x08,0x20,0x00,0x00,0x40,0x00,0x28,0x01,0x08,0x20,0x00,0x00,0x80, +0x00,0x3C,0x01,0x08,0x20,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x23, +0x01,0x69,0xE1,0x26,0x69,0x5C,0xBB,0x0F,0xD0,0x8F,0x00,0x01,0x08,0x20,0x51,0xD4, +0x50,0xD0,0x40,0x61,0x52,0xEF,0x00,0x05,0x52,0x53,0x91,0x17,0x53,0x13,0x0E,0x91, +0x03,0x9F,0x05,0x00,0x04,0x20,0x13,0x38,0x91,0x16,0x53,0x12,0x33,0xD0,0x40,0x61, +0x52,0xED,0x00,0x05,0x52,0x53,0x12,0x28,0xF2,0x10,0x50,0xF1,0xF0,0x01,0x23,0x01, +0x69,0xD4,0x50,0xD4,0x40,0x61,0xF2,0x10,0x50,0xF9,0xDE,0xAF,0x8C,0x51,0xD0,0x81, +0x50,0x13,0x0D,0xC9,0x8F,0x00,0x00,0x00,0x80,0x81,0x52,0xD0,0x52,0x60,0x11,0xEE, +0xBA,0x0F,0x05,0xE1,0x23,0x69,0x40,0x91,0x03,0x9F,0x05,0x00,0x04,0x20,0x13,0x37, +0xD0,0x0F,0x58,0xDE,0xCF,0x59,0xFF,0x52,0x91,0x62,0x8F,0xFF,0x13,0x07,0x91,0x82, +0x58,0x13,0x21,0x11,0xF3,0xD4,0x50,0xD0,0x58,0x53,0x13,0x0A,0xC0,0x8F,0x00,0x01, +0x00,0x00,0x50,0xF5,0x53,0xF6,0xC1,0x50,0xA9,0x1C,0x56,0x2C,0x00,0x8F,0x00,0x00, +0x8F,0x00,0x01,0x66,0xF4,0x58,0xCC,0x05,0xE1,0x23,0x69,0x3A,0x91,0x03,0x9F,0x05, +0x00,0x04,0x20,0x13,0x31,0xBB,0x0D,0xCB,0x8F,0xFF,0xFF,0x0F,0x00,0x51,0x53,0xDE, +0xCF,0x0D,0xFF,0x50,0xEF,0x14,0x07,0x53,0x52,0x91,0x60,0x52,0x13,0x16,0x91,0x80, +0x8F,0xFF,0x12,0xF5,0xC0,0x8F,0x00,0x00,0x10,0x00,0x53,0xD0,0x53,0x51,0xD1,0x51, +0xA9,0x2C,0x1F,0xDB,0xBA,0x0D,0x05,0xE1,0x23,0x69,0x18,0x91,0x03,0x9F,0x05,0x00, +0x04,0x20,0x13,0x0F,0xC1,0x8F,0x00,0x00,0x10,0x00,0x51,0x54,0xCA,0x8F,0xFF,0xFF, +0x0F,0x00,0x54,0x05,0x00,0x00,0x71,0x00,0xD6,0x00,0x00,0x00,0x14,0x00,0x55,0x74, +0x69,0x6C,0x69,0x74,0x69,0x65,0x73,0x00,0x04,0x02,0x31,0x00,0x02,0x00,0x00,0x00, +0x00,0x02,0x00,0x00,0x00,0x02,0x3F,0x00,0x02,0x00,0x00,0x00,0x00,0x01,0x00,0x00, +0x00,0x02,0x48,0x00,0x02,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x52,0x00, +0x02,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x45,0x78,0x70,0x6E,0x64,0x5F,0x65, +0x72,0x72,0x5F,0x6D,0x73,0x67,0x00,0x67,0x65,0x74,0x5F,0x6D,0x6F,0x64,0x65,0x00, +0x69,0x6E,0x69,0x74,0x5F,0x4C,0x45,0x44,0x73,0x00,0x63,0x6C,0x72,0x5F,0x70,0x73, +0x5F,0x63,0x6E,0x74,0x00,0x90,0x01,0xA9,0x76,0xE9,0xA9,0x2C,0x17,0xF0,0x00,0x06, +0x01,0xA9,0x04,0xED,0x00,0x02,0x9F,0x04,0x40,0x08,0x20,0x03,0x12,0x06,0xF0,0x01, +0x06,0x01,0xA9,0x04,0xE9,0xA9,0x30,0x0F,0xF0,0x0F,0x30,0x04,0x69,0xE0,0x06,0xA9, +0x04,0x05,0xF0,0x0C,0x30,0x04,0x69,0xD1,0xA9,0x34,0x01,0x12,0x06,0x94,0xC9,0xA7, +0x00,0x11,0x0A,0xD1,0xA9,0x34,0x02,0x12,0x04,0x96,0xC9,0xA7,0x00,0xD1,0xA9,0x28, +0x01,0x12,0x08,0xF0,0x01,0x00,0x01,0xA9,0x04,0x11,0x0C,0xD1,0xA9,0x28,0x02,0x12, +0x06,0xF0,0x00,0x00,0x01,0xA9,0x04,0x94,0xA9,0x76,0x05,0xBB,0x3F,0xB0,0x8F,0xBF, +0x85,0x50,0x28,0x50,0xEF,0x02,0x92,0xFF,0xFF,0x9F,0x00,0x00,0x00,0x10,0xDE,0xEF, +0xF7,0x91,0xFF,0xFF,0x50,0xC3,0x50,0xC9,0x80,0x00,0x50,0xC1,0x8F,0x00,0x00,0x00, +0x10,0x50,0xC9,0x80,0x00,0xBA,0x3F,0x05,0x03,0x80,0x1A,0x00,0xBB,0x05,0x00,0x00, +0x18,0x00,0x4C,0x69,0x73,0x74,0x20,0x43,0x50,0x55,0x20,0x72,0x65,0x67,0x73,0x00, +0x01,0x00,0xDE,0xAF,0xFD,0xC9,0x80,0x00,0x90,0x01,0xA9,0x76,0x10,0x07,0x94,0xA9, +0x76,0x05,0x10,0x01,0x05,0xBB,0x03,0xDB,0x1B,0x51,0x11,0x09,0x54,0x4F,0x59,0x20, +0x20,0x3D,0x25,0x6C,0x00,0xDD,0x51,0x9F,0xAF,0xF2,0xDD,0x02,0x16,0xEF,0x33,0x93, +0xFF,0xFF,0xDB,0x18,0x51,0x11,0x0B,0x20,0x20,0x49,0x43,0x43,0x53,0x20,0x3D,0x25, +0x6C,0x00,0xDD,0x51,0x9F,0xAF,0xF0,0xDD,0x02,0x16,0xEF,0x16,0x93,0xFF,0xFF,0x11, +0x0B,0x25,0x6E,0x54,0x43,0x52,0x30,0x20,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x00,0x01, +0x14,0x20,0x9F,0xAF,0xEC,0xDD,0x02,0x16,0xEF,0xF8,0x92,0xFF,0xFF,0x11,0x0B,0x20, +0x20,0x54,0x49,0x52,0x30,0x20,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x04,0x01,0x14,0x20, +0x9F,0xAF,0xEC,0xDD,0x02,0x16,0xEF,0xDA,0x92,0xFF,0xFF,0x11,0x0B,0x20,0x20,0x54, +0x4E,0x49,0x52,0x30,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x08,0x01,0x14,0x20,0x9F,0xAF, +0xEC,0xDD,0x02,0x16,0xEF,0xBC,0x92,0xFF,0xFF,0x11,0x0B,0x20,0x20,0x54,0x49,0x56, +0x52,0x30,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x0C,0x01,0x14,0x20,0x9F,0xAF,0xEC,0xDD, +0x02,0x16,0xEF,0x9E,0x92,0xFF,0xFF,0x11,0x0B,0x25,0x6E,0x54,0x43,0x52,0x31,0x20, +0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x10,0x01,0x14,0x20,0x9F,0xAF,0xEC,0xDD,0x02,0x16, +0xEF,0x80,0x92,0xFF,0xFF,0x11,0x0B,0x20,0x20,0x54,0x49,0x52,0x31,0x20,0x3D,0x25, +0x6C,0x00,0xDD,0x9F,0x14,0x01,0x14,0x20,0x9F,0xAF,0xEC,0xDD,0x02,0x16,0xEF,0x62, +0x92,0xFF,0xFF,0x11,0x0B,0x20,0x20,0x54,0x4E,0x49,0x52,0x31,0x3D,0x25,0x6C,0x00, +0xDD,0x9F,0x18,0x01,0x14,0x20,0x9F,0xAF,0xEC,0xDD,0x02,0x16,0xEF,0x44,0x92,0xFF, +0xFF,0x11,0x0B,0x20,0x20,0x54,0x49,0x56,0x52,0x31,0x3D,0x25,0x6C,0x00,0xDD,0x9F, +0x1C,0x01,0x14,0x20,0x9F,0xAF,0xEC,0xDD,0x02,0x16,0xEF,0x26,0x92,0xFF,0xFF,0xDB, +0x20,0x51,0x11,0x0B,0x25,0x6E,0x52,0x58,0x43,0x53,0x20,0x3D,0x25,0x6C,0x00,0xDD, +0x51,0x9F,0xAF,0xF0,0xDD,0x02,0x16,0xEF,0x09,0x92,0xFF,0xFF,0xDB,0x21,0x51,0x11, +0x0B,0x20,0x20,0x52,0x58,0x44,0x42,0x20,0x3D,0x25,0x6C,0x00,0xDD,0x51,0x9F,0xAF, +0xF0,0xDD,0x02,0x16,0xEF,0xEC,0x91,0xFF,0xFF,0xDB,0x22,0x51,0x11,0x0B,0x20,0x20, +0x54,0x58,0x43,0x53,0x20,0x3D,0x25,0x6C,0x00,0xDD,0x51,0x9F,0xAF,0xF0,0xDD,0x02, +0x16,0xEF,0xCF,0x91,0xFF,0xFF,0xDB,0x23,0x51,0x11,0x0B,0x20,0x20,0x54,0x58,0x44, +0x42,0x20,0x3D,0x25,0x6C,0x00,0xDD,0x51,0x9F,0xAF,0xF0,0xDD,0x02,0x16,0xEF,0xB2, +0x91,0xFF,0xFF,0xDB,0x27,0x51,0x11,0x0B,0x25,0x6E,0x4D,0x53,0x45,0x52,0x20,0x3D, +0x25,0x6C,0x00,0xDD,0x51,0x9F,0xAF,0xF0,0xDD,0x02,0x16,0xEF,0x95,0x91,0xFF,0xFF, +0xDB,0x25,0x51,0x11,0x0B,0x20,0x20,0x43,0x41,0x44,0x52,0x20,0x3D,0x25,0x6C,0x00, +0xDD,0x51,0x9F,0xAF,0xF0,0xDD,0x02,0x16,0xEF,0x78,0x91,0xFF,0xFF,0x11,0x0B,0x20, +0x20,0x43,0x41,0x43,0x52,0x20,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x00,0x40,0x08,0x20, +0x9F,0xAF,0xEC,0xDD,0x02,0x16,0xEF,0x5A,0x91,0xFF,0xFF,0x11,0x0B,0x25,0x6E,0x42, +0x44,0x52,0x20,0x20,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x04,0x40,0x08,0x20,0x9F,0xAF, +0xEC,0xDD,0x02,0x16,0xEF,0x3C,0x91,0xFF,0xFF,0x11,0x0B,0x20,0x20,0x44,0x4C,0x45, +0x44,0x52,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x30,0x00,0x14,0x20,0x9F,0xAF,0xEC,0xDD, +0x02,0x16,0xEF,0x1E,0x91,0xFF,0xFF,0x11,0x0B,0x20,0x20,0x53,0x53,0x43,0x43,0x52, +0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x10,0x00,0x14,0x20,0x9F,0xAF,0xEC,0xDD,0x02,0x16, +0xEF,0x00,0x91,0xFF,0xFF,0x11,0x0B,0x20,0x20,0x43,0x42,0x54,0x43,0x52,0x3D,0x25, +0x6C,0x00,0xDD,0x9F,0x20,0x00,0x14,0x20,0x9F,0xAF,0xEC,0xDD,0x02,0x16,0xEF,0xE2, +0x90,0xFF,0xFF,0x11,0x0B,0x25,0x6E,0x53,0x43,0x52,0x20,0x20,0x3D,0x25,0x6C,0x00, +0xDD,0x9F,0x00,0x00,0x08,0x20,0x9F,0xAF,0xEC,0xDD,0x02,0x16,0xEF,0xC4,0x90,0xFF, +0xFF,0x11,0x0B,0x20,0x20,0x44,0x53,0x45,0x52,0x20,0x3D,0x25,0x6C,0x00,0xDD,0x9F, +0x04,0x00,0x08,0x20,0x9F,0xAF,0xEC,0xDD,0x02,0x16,0xEF,0xA6,0x90,0xFF,0xFF,0x11, +0x0B,0x20,0x20,0x51,0x42,0x45,0x41,0x52,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x08,0x00, +0x08,0x20,0x9F,0xAF,0xEC,0xDD,0x02,0x16,0xEF,0x88,0x90,0xFF,0xFF,0x11,0x0B,0x20, +0x20,0x44,0x45,0x41,0x52,0x20,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x0C,0x00,0x08,0x20, +0x9F,0xAF,0xEC,0xDD,0x02,0x16,0xEF,0x6A,0x90,0xFF,0xFF,0x11,0x0B,0x25,0x6E,0x51, +0x42,0x4D,0x42,0x52,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x10,0x00,0x08,0x20,0x9F,0xAF, +0xEC,0xDD,0x02,0x16,0xEF,0x4C,0x90,0xFF,0xFF,0xD0,0x8F,0x40,0x1F,0x00,0x20,0x51, +0xE1,0x0A,0x9F,0x00,0x00,0x08,0x20,0x10,0xEF,0x01,0x03,0x9F,0x00,0x00,0x08,0x20, +0x50,0x78,0x01,0x50,0x50,0xC8,0x50,0x51,0x3C,0x61,0x50,0x11,0x0B,0x20,0x20,0x49, +0x50,0x43,0x52,0x6E,0x3D,0x25,0x77,0x00,0xDD,0x50,0x9F,0xAF,0xF0,0xDD,0x02,0x16, +0xEF,0x10,0x90,0xFF,0xFF,0x11,0x03,0x25,0x6E,0x00,0x9F,0xAF,0xFA,0xDD,0x01,0x16, +0xEF,0x00,0x90,0xFF,0xFF,0x11,0x19,0x25,0x6E,0x4D,0x45,0x4D,0x5F,0x46,0x52,0x55, +0x20,0x31,0x20,0x20,0x4D,0x45,0x4D,0x43,0x53,0x52,0x5F,0x30,0x3D,0x25,0x6C,0x00, +0xDD,0x9F,0x00,0x01,0x08,0x20,0x9F,0xAF,0xDE,0xDD,0x02,0x16,0xEF,0xD4,0x8F,0xFF, +0xFF,0x11,0x08,0x20,0x20,0x20,0x31,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x04,0x01,0x08, +0x20,0x9F,0xAF,0xEF,0xDD,0x02,0x16,0xEF,0xB9,0x8F,0xFF,0xFF,0x11,0x08,0x20,0x20, +0x20,0x32,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x08,0x01,0x08,0x20,0x9F,0xAF,0xEF,0xDD, +0x02,0x16,0xEF,0x9E,0x8F,0xFF,0xFF,0x11,0x08,0x20,0x20,0x20,0x33,0x3D,0x25,0x6C, +0x00,0xDD,0x9F,0x0C,0x01,0x08,0x20,0x9F,0xAF,0xEF,0xDD,0x02,0x16,0xEF,0x83,0x8F, +0xFF,0xFF,0x11,0x19,0x25,0x6E,0x4D,0x45,0x4D,0x5F,0x46,0x52,0x55,0x20,0x32,0x20, +0x20,0x4D,0x45,0x4D,0x43,0x53,0x52,0x5F,0x34,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x10, +0x01,0x08,0x20,0x9F,0xAF,0xDE,0xDD,0x02,0x16,0xEF,0x57,0x8F,0xFF,0xFF,0x11,0x08, +0x20,0x20,0x20,0x35,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x14,0x01,0x08,0x20,0x9F,0xAF, +0xEF,0xDD,0x02,0x16,0xEF,0x3C,0x8F,0xFF,0xFF,0x11,0x08,0x20,0x20,0x20,0x36,0x3D, +0x25,0x6C,0x00,0xDD,0x9F,0x18,0x01,0x08,0x20,0x9F,0xAF,0xEF,0xDD,0x02,0x16,0xEF, +0x21,0x8F,0xFF,0xFF,0x11,0x08,0x20,0x20,0x20,0x37,0x3D,0x25,0x6C,0x00,0xDD,0x9F, +0x1C,0x01,0x08,0x20,0x9F,0xAF,0xEF,0xDD,0x02,0x16,0xEF,0x06,0x8F,0xFF,0xFF,0x11, +0x19,0x25,0x6E,0x4D,0x45,0x4D,0x5F,0x46,0x52,0x55,0x20,0x33,0x20,0x20,0x4D,0x45, +0x4D,0x43,0x53,0x52,0x5F,0x38,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x20,0x01,0x08,0x20, +0x9F,0xAF,0xDE,0xDD,0x02,0x16,0xEF,0xDA,0x8E,0xFF,0xFF,0x11,0x08,0x20,0x20,0x20, +0x39,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x24,0x01,0x08,0x20,0x9F,0xAF,0xEF,0xDD,0x02, +0x16,0xEF,0xBF,0x8E,0xFF,0xFF,0x11,0x08,0x20,0x20,0x31,0x30,0x3D,0x25,0x6C,0x00, +0xDD,0x9F,0x28,0x01,0x08,0x20,0x9F,0xAF,0xEF,0xDD,0x02,0x16,0xEF,0xA4,0x8E,0xFF, +0xFF,0x11,0x08,0x20,0x20,0x31,0x31,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x2C,0x01,0x08, +0x20,0x9F,0xAF,0xEF,0xDD,0x02,0x16,0xEF,0x89,0x8E,0xFF,0xFF,0x11,0x19,0x25,0x6E, +0x4D,0x45,0x4D,0x5F,0x46,0x52,0x55,0x20,0x34,0x20,0x20,0x4D,0x45,0x4D,0x43,0x53, +0x52,0x31,0x32,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x30,0x01,0x08,0x20,0x9F,0xAF,0xDE, +0xDD,0x02,0x16,0xEF,0x5D,0x8E,0xFF,0xFF,0x11,0x08,0x20,0x20,0x31,0x33,0x3D,0x25, +0x6C,0x00,0xDD,0x9F,0x34,0x01,0x08,0x20,0x9F,0xAF,0xEF,0xDD,0x02,0x16,0xEF,0x42, +0x8E,0xFF,0xFF,0x11,0x08,0x20,0x20,0x31,0x34,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x38, +0x01,0x08,0x20,0x9F,0xAF,0xEF,0xDD,0x02,0x16,0xEF,0x27,0x8E,0xFF,0xFF,0x11,0x08, +0x20,0x20,0x31,0x35,0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x3C,0x01,0x08,0x20,0x9F,0xAF, +0xEF,0xDD,0x02,0x16,0xEF,0x0C,0x8E,0xFF,0xFF,0x11,0x19,0x25,0x6E,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x4D,0x45,0x4D,0x43,0x53,0x52,0x31,0x36, +0x3D,0x25,0x6C,0x00,0xDD,0x9F,0x40,0x01,0x08,0x20,0x9F,0xAF,0xDE,0xDD,0x02,0x16, +0xEF,0xE0,0x8D,0xFF,0xFF,0x11,0x08,0x20,0x20,0x31,0x37,0x3D,0x25,0x6C,0x00,0xDD, +0x9F,0x44,0x01,0x08,0x20,0x9F,0xAF,0xEF,0xDD,0x02,0x16,0xEF,0xC5,0x8D,0xFF,0xFF, +0xBA,0x03,0x05,0x05,0x00,0x00,0x1F,0x00,0xB6,0x06,0x00,0x00,0x18,0x00,0x43,0x72, +0x65,0x61,0x74,0x65,0x20,0x73,0x63,0x72,0x69,0x70,0x74,0x00,0x06,0x00,0x00,0x00, +0x00,0x00,0x00,0x90,0x01,0xA9,0x76,0x11,0x09,0x25,0x6E,0x53,0x50,0x3D,0x25,0x6C, +0x20,0x00,0xDD,0x5E,0x9F,0xAF,0xF2,0xDD,0x02,0x16,0xEF,0x86,0x8D,0xFF,0xFF,0x11, +0x31,0x25,0x6E,0x43,0x72,0x65,0x61,0x74,0x65,0x20,0x73,0x63,0x72,0x69,0x70,0x74, +0x20,0x69,0x6E,0x20,0x3F,0x5B,0x30,0x3D,0x53,0x53,0x43,0x2C,0x31,0x3D,0x44,0x69, +0x61,0x67,0x5F,0x52,0x41,0x4D,0x2C,0x32,0x3D,0x52,0x41,0x4D,0x5D,0x20,0x3A,0x25, +0x62,0x00,0xDF,0xA9,0x28,0xDF,0xA9,0x3C,0x9F,0xAF,0xC6,0xDD,0x03,0x16,0xEF,0x2A, +0x8E,0xFF,0xFF,0x95,0xA9,0x28,0x12,0x0D,0xD0,0x8F,0xE0,0x07,0x14,0x20,0x50,0xD0, +0x18,0xA9,0x2C,0x11,0x26,0x91,0x01,0xA9,0x28,0x12,0x11,0xD0,0x8F,0x00,0xFC,0x00, +0x10,0x50,0xD0,0x8F,0x00,0x04,0x00,0x00,0xA9,0x2C,0x11,0x0F,0xD0,0x8F,0x00,0x10, +0x00,0x00,0x50,0xD0,0x8F,0x00,0x08,0x00,0x00,0xA9,0x2C,0x9A,0xA9,0x28,0xA9,0x34, +0xD0,0x50,0xA9,0x10,0x11,0x16,0x25,0x6E,0x53,0x63,0x72,0x69,0x70,0x74,0x20,0x73, +0x74,0x61,0x72,0x74,0x73,0x20,0x61,0x74,0x20,0x25,0x6C,0x00,0xDD,0x50,0x9F,0xAF, +0xE5,0xDD,0x02,0x16,0xEF,0xDC,0x8C,0xFF,0xFF,0xD0,0x50,0xA9,0x30,0x11,0x11,0x25, +0x6E,0x20,0x25,0x64,0x20,0x62,0x79,0x74,0x65,0x73,0x20,0x6C,0x65,0x66,0x74,0x00, +0xDD,0xA9,0x2C,0x9F,0xAF,0xE9,0xDD,0x02,0x16,0xEF,0xB7,0x8C,0xFF,0xFF,0x11,0x17, +0x25,0x6E,0x4E,0x65,0x78,0x74,0x20,0x74,0x65,0x73,0x74,0x20,0x6E,0x75,0x6D,0x62, +0x65,0x72,0x20,0x3A,0x25,0x62,0x00,0xDF,0xA9,0x28,0xDF,0xA9,0x3C,0x9F,0xAF,0xE0, +0xDD,0x03,0x16,0xEF,0x75,0x8D,0xFF,0xFF,0x9A,0xA9,0x28,0x53,0x12,0x06,0x94,0x80, +0x94,0xA9,0x76,0x05,0x91,0x3F,0xA9,0x3D,0x12,0x0C,0xBB,0x01,0x16,0xEF,0x43,0x89, +0xFF,0xFF,0xBA,0x01,0x11,0x93,0x91,0x53,0x8F,0xA0,0x1F,0x27,0x91,0x53,0x8F,0xBF, +0x1A,0x21,0x11,0x0A,0x20,0x2D,0x20,0x73,0x63,0x72,0x69,0x70,0x74,0x00,0x9F,0xAF, +0xF3,0xDD,0x01,0x16,0xEF,0x4C,0x8C,0xFF,0xFF,0x90,0x53,0xA9,0x28,0x30,0x3B,0x05, +0x31,0x66,0xFF,0xD0,0x53,0x52,0xDE,0xEF,0x02,0x76,0xFF,0xFF,0x51,0x95,0x61,0x12, +0x26,0x11,0x16,0x25,0x6E,0x4E,0x6F,0x20,0x73,0x75,0x63,0x68,0x20,0x64,0x69,0x61, +0x67,0x6E,0x6F,0x73,0x74,0x69,0x63,0x21,0x00,0x9F,0xAF,0xE7,0xDD,0x01,0x16,0xEF, +0x11,0x8C,0xFF,0xFF,0x31,0x32,0xFF,0xD6,0x51,0x91,0x53,0x81,0x13,0x05,0xC0,0x04, +0x51,0x11,0xCA,0xDF,0xEF,0xC5,0x75,0xFF,0xFF,0xC1,0x61,0x8E,0x54,0x90,0x53,0xA9, +0x28,0x30,0xE7,0x04,0x90,0x53,0xA9,0x28,0x30,0xE0,0x04,0xDE,0xA4,0x0A,0x52,0x95, +0xA9,0x34,0x13,0x50,0x91,0x01,0xA9,0x34,0x12,0x06,0xE1,0x00,0x64,0x46,0x11,0x04, +0xE1,0x01,0x64,0x40,0x11,0x24,0x25,0x6E,0x44,0x69,0x61,0x67,0x6E,0x6F,0x73,0x74, +0x69,0x63,0x2F,0x73,0x63,0x72,0x69,0x70,0x74,0x20,0x69,0x6E,0x63,0x6F,0x6D,0x70, +0x61,0x74,0x69,0x62,0x69,0x6C,0x69,0x74,0x79,0x00,0x9F,0xAF,0xD9,0xDD,0x01,0x16, +0xEF,0xA0,0x8B,0xFF,0xFF,0xC2,0xA9,0x30,0x50,0xC0,0x50,0xA9,0x2C,0xD0,0xA9,0x30, +0x50,0x31,0xB5,0xFE,0x11,0x07,0x25,0x6E,0x25,0x61,0x3E,0x3E,0x00,0xDD,0x52,0x9F, +0xAF,0xF4,0xDD,0x02,0x16,0xEF,0x7B,0x8B,0xFF,0xFF,0x11,0x11,0x52,0x75,0x6E,0x20, +0x66,0x72,0x6F,0x6D,0x20,0x3F,0x5B,0x30,0x3D,0x52,0x4F,0x4D,0x00,0x9F,0xAF,0xEC, +0xDD,0x01,0x16,0xEF,0x5D,0x8B,0xFF,0xFF,0xE0,0x00,0x64,0x19,0x11,0x0C,0x2C,0x31, +0x3D,0x44,0x69,0x61,0x67,0x5F,0x52,0x41,0x4D,0x00,0x9F,0xAF,0xF1,0xDD,0x01,0x16, +0xEF,0x40,0x8B,0xFF,0xFF,0xE0,0x01,0x64,0x14,0x11,0x07,0x2C,0x32,0x3D,0x52,0x41, +0x4D,0x00,0x9F,0xAF,0xF6,0xDD,0x01,0x16,0xEF,0x28,0x8B,0xFF,0xFF,0x11,0x14,0x2C, +0x33,0x3D,0x66,0x61,0x73,0x74,0x65,0x73,0x74,0x20,0x70,0x6F,0x73,0x73,0x69,0x62, +0x6C,0x65,0x00,0x9F,0xAF,0xE9,0xDD,0x01,0x16,0xEF,0x07,0x8B,0xFF,0xFF,0x11,0x09, +0x5D,0x20,0x28,0x30,0x29,0x3A,0x25,0x62,0x00,0xDF,0xA9,0x28,0xDF,0xA9,0x3C,0x9F, +0xAF,0xEE,0xDD,0x03,0x16,0xEF,0xD3,0x8B,0xFF,0xFF,0x30,0xDE,0x03,0x88,0x04,0xA0, +0xFF,0x95,0xA0,0xFF,0x12,0x66,0xE0,0x0F,0x64,0x62,0x11,0x07,0x25,0x6E,0x25,0x61, +0x3E,0x3E,0x00,0xDD,0x52,0x9F,0xAF,0xF4,0xDD,0x02,0x16,0xEF,0xC5,0x8A,0xFF,0xFF, +0x11,0x2F,0x41,0x64,0x64,0x72,0x65,0x73,0x73,0x69,0x6E,0x67,0x20,0x6D,0x6F,0x64, +0x65,0x3F,0x20,0x5B,0x30,0x3D,0x70,0x68,0x79,0x73,0x69,0x63,0x61,0x6C,0x2C,0x31, +0x3D,0x76,0x69,0x72,0x74,0x75,0x61,0x6C,0x5D,0x20,0x28,0x30,0x29,0x3A,0x25,0x62, +0x00,0xDF,0xA9,0x28,0xDF,0xA9,0x3C,0x9F,0xAF,0xC8,0xDD,0x03,0x16,0xEF,0x6B,0x8B, +0xFF,0xFF,0x95,0xA9,0x28,0x13,0x05,0x88,0x8F,0x80,0xA0,0xFF,0x11,0x07,0x25,0x6E, +0x25,0x61,0x3E,0x3E,0x00,0xDD,0x52,0x9F,0xAF,0xF4,0xDD,0x02,0x16,0xEF,0x63,0x8A, +0xFF,0xFF,0x11,0x37,0x52,0x65,0x70,0x65,0x61,0x74,0x3F,0x20,0x5B,0x30,0x3D,0x6E, +0x6F,0x2C,0x31,0x3D,0x6F,0x6E,0x20,0x65,0x72,0x72,0x6F,0x72,0x2C,0x32,0x3D,0x66, +0x6F,0x72,0x65,0x76,0x65,0x72,0x2C,0x3E,0x32,0x3D,0x63,0x6F,0x75,0x6E,0x74,0x3C, +0x46,0x46,0x5D,0x20,0x28,0x30,0x29,0x3A,0x25,0x62,0x00,0xDF,0xA9,0x28,0xDF,0xA9, +0x3C,0x9F,0xAF,0xC0,0xDD,0x03,0x16,0xEF,0x01,0x8B,0xFF,0xFF,0x30,0x0C,0x03,0x11, +0x00,0x11,0x07,0x25,0x6E,0x25,0x61,0x3E,0x3E,0x00,0xDD,0x52,0x9F,0xAF,0xF4,0xDD, +0x02,0x16,0xEF,0xFE,0x89,0xFF,0xFF,0x94,0x53,0x11,0x22,0x45,0x72,0x72,0x6F,0x72, +0x20,0x73,0x65,0x76,0x65,0x72,0x69,0x74,0x79,0x20,0x3F,0x20,0x5B,0x30,0x2C,0x31, +0x2C,0x32,0x2C,0x33,0x5D,0x20,0x28,0x32,0x29,0x3A,0x25,0x62,0x00,0xDF,0xA9,0x28, +0xDF,0xA9,0x3C,0x9F,0xAF,0xD5,0xDD,0x03,0x16,0xEF,0xAF,0x8A,0xFF,0xFF,0x95,0xA9, +0x3C,0x12,0x04,0x90,0x02,0xA9,0x28,0xF0,0xA9,0x28,0x00,0x02,0x53,0x11,0x07,0x25, +0x6E,0x25,0x61,0x3E,0x3E,0x00,0xDD,0x52,0x9F,0xAF,0xF4,0xDD,0x02,0x16,0xEF,0xA2, +0x89,0xFF,0xFF,0x11,0x2D,0x43,0x6F,0x6E,0x73,0x6F,0x6C,0x65,0x20,0x65,0x72,0x72, +0x6F,0x72,0x20,0x72,0x65,0x70,0x6F,0x72,0x74,0x3F,0x20,0x5B,0x30,0x3D,0x6E,0x6F, +0x6E,0x65,0x2C,0x31,0x3D,0x66,0x75,0x6C,0x6C,0x5D,0x20,0x28,0x31,0x29,0x3A,0x25, +0x62,0x00,0xDF,0xA9,0x28,0xDF,0xA9,0x3C,0x9F,0xAF,0xCA,0xDD,0x03,0x16,0xEF,0x4A, +0x8A,0xFF,0xFF,0x95,0xA9,0x3C,0x12,0x04,0x90,0x01,0xA9,0x28,0xF0,0xA9,0x28,0x03, +0x01,0x53,0x11,0x07,0x25,0x6E,0x25,0x61,0x3E,0x3E,0x00,0xDD,0x52,0x9F,0xAF,0xF4, +0xDD,0x02,0x16,0xEF,0x3D,0x89,0xFF,0xFF,0x11,0x2A,0x53,0x74,0x6F,0x70,0x20,0x73, +0x63,0x72,0x69,0x70,0x74,0x20,0x6F,0x6E,0x20,0x65,0x72,0x72,0x6F,0x72,0x3F,0x20, +0x5B,0x30,0x3D,0x4E,0x4F,0x2C,0x31,0x3D,0x59,0x45,0x53,0x5D,0x20,0x28,0x31,0x29, +0x3A,0x25,0x62,0x00,0xDF,0xA9,0x28,0xDF,0xA9,0x3C,0x9F,0xAF,0xCD,0xDD,0x03,0x16, +0xEF,0xE8,0x89,0xFF,0xFF,0x95,0xA9,0x3C,0x12,0x04,0x90,0x01,0xA9,0x28,0xF0,0xA9, +0x28,0x02,0x01,0x53,0x90,0x53,0xA9,0x28,0x30,0xE0,0x01,0x11,0x07,0x25,0x6E,0x25, +0x61,0x3E,0x3E,0x00,0xDD,0x52,0x9F,0xAF,0xF4,0xDD,0x02,0x16,0xEF,0xD4,0x88,0xFF, +0xFF,0xEF,0x04,0x04,0xA1,0xFF,0x58,0x11,0x13,0x4C,0x45,0x44,0x20,0x6F,0x6E,0x20, +0x65,0x6E,0x74,0x72,0x79,0x20,0x28,0x25,0x62,0x29,0x3A,0x00,0xDD,0x58,0x9F,0xAF, +0xE8,0xDD,0x02,0x16,0xEF,0xAC,0x88,0xFF,0xFF,0x11,0x03,0x25,0x62,0x00,0xDF,0xA9, +0x28,0xDF,0xA9,0x3C,0x9F,0xAF,0xF4,0xDD,0x03,0x16,0xEF,0x7E,0x89,0xFF,0xFF,0x95, +0xA9,0x28,0x12,0x04,0x90,0x58,0xA9,0x28,0x30,0x80,0x01,0x11,0x07,0x25,0x6E,0x25, +0x61,0x3E,0x3E,0x00,0xDD,0x52,0x9F,0xAF,0xF4,0xDD,0x02,0x16,0xEF,0x74,0x88,0xFF, +0xFF,0x11,0x17,0x43,0x6F,0x6E,0x73,0x6F,0x6C,0x65,0x20,0x6F,0x6E,0x20,0x65,0x6E, +0x74,0x72,0x79,0x20,0x28,0x25,0x62,0x29,0x3A,0x00,0xDD,0xA1,0xFF,0x9F,0xAF,0xE3, +0xDD,0x02,0x16,0xEF,0x4D,0x88,0xFF,0xFF,0x11,0x03,0x25,0x62,0x00,0xDF,0xA9,0x28, +0xDF,0xA9,0x3C,0x9F,0xAF,0xF4,0xDD,0x03,0x16,0xEF,0x1F,0x89,0xFF,0xFF,0x95,0xA9, +0x28,0x12,0x05,0x90,0xA1,0xFF,0xA9,0x28,0x30,0x20,0x01,0x32,0xA4,0x08,0x55,0xC0, +0x54,0x55,0xD0,0x55,0x56,0x9A,0x85,0x57,0x12,0x03,0x31,0x3C,0xFB,0x95,0x85,0x12, +0x03,0x31,0xFD,0x00,0x91,0x01,0xA5,0xFF,0x12,0x3B,0x32,0x85,0x58,0xC0,0x56,0x58, +0x95,0x85,0x11,0x07,0x25,0x6E,0x25,0x61,0x3E,0x3E,0x00,0xDD,0x52,0x9F,0xAF,0xF4, +0xDD,0x02,0x16,0xEF,0xED,0x87,0xFF,0xFF,0x11,0x09,0x20,0x25,0x61,0x20,0x3A,0x20, +0x25,0x6C,0x00,0xDD,0x85,0xDD,0x58,0x9F,0xAF,0xF0,0xDD,0x03,0x16,0xEF,0xD3,0x87, +0xFF,0xFF,0x31,0xBC,0x00,0x32,0x85,0x58,0xC0,0x56,0x58,0x95,0x85,0x11,0x07,0x25, +0x6E,0x25,0x61,0x3E,0x3E,0x00,0xDD,0x52,0x9F,0xAF,0xF4,0xDD,0x02,0x16,0xEF,0xB2, +0x87,0xFF,0xFF,0x11,0x10,0x20,0x25,0x61,0x20,0x3A,0x20,0x25,0x6C,0x20,0x2D,0x20, +0x25,0x6C,0x20,0x3F,0x00,0xDD,0xA5,0x04,0xDD,0x65,0xDD,0x58,0x9F,0xAF,0xE6,0xDD, +0x04,0x16,0xEF,0x8E,0x87,0xFF,0xFF,0x91,0x02,0xA5,0xFC,0x13,0x42,0xD0,0x0C,0x53, +0x11,0x06,0x28,0x25,0x6C,0x29,0x20,0x00,0xDD,0xA5,0x08,0x9F,0xAF,0xF4,0xDD,0x02, +0x16,0xEF,0x6F,0x87,0xFF,0xFF,0x11,0x03,0x25,0x6C,0x00,0xDF,0xA9,0x28,0xDF,0xA9, +0x3C,0x9F,0xAF,0xF4,0xDD,0x03,0x16,0xEF,0x41,0x88,0xFF,0xFF,0x95,0xA9,0x3C,0x12, +0x27,0xD0,0xA5,0x08,0xA9,0x28,0x30,0x52,0x00,0xC0,0x53,0x55,0x31,0x32,0x00,0x11, +0x03,0x25,0x6C,0x00,0xDF,0xA9,0x28,0xDF,0xA9,0x3C,0x9F,0xAF,0xF4,0xDD,0x03,0x16, +0xEF,0x18,0x88,0xFF,0xFF,0xD0,0x08,0x53,0xD1,0xA9,0x28,0x65,0x1F,0x10,0xD1,0xA9, +0x28,0xA5,0x04,0x1A,0x09,0x30,0x23,0x00,0xC0,0x53,0x55,0x31,0x03,0x00,0x31,0x4C, +0xFF,0xF5,0x57,0x03,0x31,0x32,0xFA,0x31,0xF3,0xFE,0x05,0xC3,0x01,0xA9,0x2C,0xA9, +0x38,0x19,0x19,0xD7,0xA9,0x2C,0x90,0xA9,0x28,0x80,0x05,0xC3,0x04,0xA9,0x2C,0xA9, +0x38,0x19,0x09,0xC2,0x04,0xA9,0x2C,0xD0,0xA9,0x28,0x80,0x05,0x11,0x2A,0x25,0x6E, +0x4E,0x6F,0x20,0x73,0x70,0x61,0x63,0x65,0x2C,0x20,0x62,0x61,0x63,0x6B,0x75,0x70, +0x20,0x74,0x6F,0x20,0x70,0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,0x64,0x69,0x61, +0x67,0x6E,0x6F,0x73,0x74,0x69,0x63,0x00,0x9F,0xAF,0xD3,0xDD,0x01,0x16,0xEF,0xB2, +0x86,0xFF,0xFF,0xC2,0xA9,0x30,0x50,0xC0,0x50,0xA9,0x2C,0xD0,0xA9,0x30,0x50,0xDE, +0xCF,0xC6,0xF9,0x6E,0x05,0xBD,0x00,0xB0,0x01,0x62,0x04,0x00,0x0E,0x06,0x62,0x01, +0x00,0x00,0x00,0x01,0x52,0x04,0x00,0x0E,0x0C,0x52,0x00,0x00,0x00,0x00,0x0A,0x00, +0x00,0x00,0x01,0x52,0x04,0x00,0x0E,0x0C,0x52,0x01,0x00,0x00,0x00,0x0A,0x00,0x00, +0x00,0x01,0x53,0x04,0x00,0x0E,0x0C,0x53,0x0C,0x00,0x00,0x00,0x14,0x00,0x00,0x00, +0x01,0xC1,0x04,0x00,0x0E,0x0C,0xC1,0x01,0x34,0x04,0x00,0x0E,0x0C,0x34,0x01,0x91, +0x04,0x00,0x0E,0x07,0x91,0x01,0x33,0x04,0x00,0x0E,0x09,0x33,0x01,0xC5,0x04,0x00, +0x0E,0x0C,0xC5,0x01,0x55,0x04,0x00,0x0E,0x0B,0x55,0x01,0x46,0x04,0x00,0x0E,0x0B, +0x46,0x00,0x00,0x00,0x04,0x01,0x35,0x04,0x00,0x0E,0x05,0x35,0x00,0x00,0x00,0x10, +0x00,0x20,0x01,0x10,0x00,0x20,0x00,0x00,0x01,0x43,0x04,0x00,0x0E,0x0B,0x43,0x00, +0x00,0x40,0x00,0x01,0x90,0x04,0x00,0x0E,0x07,0x90,0x01,0x32,0x04,0x00,0x0E,0x09, +0x32,0x01,0xC7,0x04,0x00,0x0E,0x0C,0xC7,0x01,0x51,0x04,0x00,0x0E,0x0A,0x51,0x01, +0x54,0x04,0x00,0x0E,0x0B,0x54,0x01,0x34,0x84,0x00,0x0E,0x0B,0x34,0x01,0xC5,0x84, +0x00,0x0E,0x0B,0xC5,0x00,0xA2,0xA5,0x00,0x01,0x9D,0x04,0x00,0x0E,0x0C,0x00,0x00, +0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0xA6, +0x00,0x01,0x31,0x04,0x00,0x0E,0x08,0x31,0x01,0x49,0x04,0x00,0x0E,0x08,0x49,0x01, +0x00,0x00,0x00,0x01,0xC2,0x00,0x00,0x0E,0x0C,0xC2,0x01,0x30,0x04,0x00,0x0E,0x08, +0x30,0x01,0x00,0x00,0x00,0x01,0x4F,0x07,0x00,0x0E,0x08,0x4F,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x4E,0x07,0x00, +0x0E,0x08,0x4E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01, +0x00,0x00,0x00,0x01,0x4D,0x07,0x00,0x0E,0x08,0x4D,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x4C,0x07,0x00,0x0E,0x08, +0x4C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00, +0x00,0x01,0x4B,0x07,0x00,0x0E,0x08,0x4B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, +0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x4A,0x07,0x00,0x0E,0x08,0x4A,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01, +0x48,0x07,0x00,0x0E,0x08,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00, +0x00,0x00,0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,0x01,0x47,0x07,0x00,0x0E,0x08, +0x47,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x40,0x00,0x00,0x01,0x00,0x00, +0x00,0x0A,0x00,0x00,0x00,0x01,0x40,0x04,0x00,0x0E,0x08,0x40,0x01,0x00,0x00,0x00, +0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x44,0x04,0x00,0x0E,0x0B,0x44,0x00, +0x00,0x00,0x01,0x01,0x36,0x04,0x00,0x0E,0x05,0x36,0x00,0x00,0x01,0x00,0xFC,0xFF, +0x02,0x00,0x00,0x02,0x00,0x00,0x01,0x80,0x07,0x00,0x0E,0x07,0x80,0x01,0x45,0x04, +0x00,0x0E,0x07,0x45,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00, +0x01,0x5A,0x06,0x00,0x0E,0x09,0x5A,0x00,0x00,0x00,0x00,0x10,0x27,0x00,0x00,0x01, +0x41,0x04,0x00,0x0E,0x0C,0x41,0x01,0x9D,0x04,0x00,0x0E,0x0C,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0xA2,0xA5, +0xA6,0x00,0x01,0x4F,0x07,0x00,0x0E,0x08,0x4F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x04,0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x4E,0x07,0x00,0x0E,0x08,0x4E, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x00, +0x01,0x4D,0x07,0x00,0x0E,0x08,0x4D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00, +0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x4C,0x07,0x00,0x0E,0x08,0x4C,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x01,0x4B, +0x07,0x00,0x0E,0x08,0x4B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x01, +0x00,0x01,0x00,0x00,0x00,0x01,0x4A,0x07,0x00,0x0E,0x08,0x4A,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x01,0x48,0x07,0x00, +0x0E,0x08,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0xAA, +0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,0x01,0x47,0x07,0x00,0x0E,0x08,0x47,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x1E,0x00, +0x00,0x00,0x01,0x40,0x04,0x00,0x0A,0x08,0x40,0x01,0x00,0x00,0x00,0x04,0x00,0x00, +0x00,0x01,0x00,0x00,0x00,0x01,0x80,0x07,0x00,0x0A,0x07,0x80,0x01,0x41,0x04,0x00, +0x0E,0x0C,0x41,0x00,0x01,0x31,0x04,0x00,0x0E,0x08,0x31,0x01,0x49,0x04,0x00,0x0E, +0x08,0x49,0x01,0x00,0x00,0x00,0x01,0x30,0x04,0x00,0x0E,0x08,0x30,0x01,0x00,0x00, +0x00,0xA7,0x00,0x01,0x4F,0x07,0x00,0x0E,0x08,0x4F,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x4E,0x07,0x00,0x0E,0x08, +0x4E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x00,0x00, +0x00,0x01,0x4D,0x07,0x00,0x0E,0x08,0x4D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, +0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x4C,0x07,0x00,0x0E,0x08,0x4C,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x01, +0x4B,0x07,0x00,0x0E,0x08,0x4B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00, +0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x4A,0x07,0x00,0x0E,0x08,0x4A,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x48,0x07, +0x00,0x0E,0x08,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00, +0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,0x01,0x47,0x07,0x00,0x0E,0x08,0x47,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x0A, +0x00,0x00,0x00,0x01,0x40,0x04,0x00,0x0A,0x08,0x40,0x01,0x00,0x00,0x00,0x04,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x41,0x04,0x00,0x0A,0x0C,0x41,0x00,0xBA,0xBC, +0xA1,0x00,0xBB,0xA1,0x00,0xBC,0xA1,0x00,0xAE,0x01,0x30,0x00,0x00,0x02,0x08,0x00, +0x00,0x00,0x00,0x00,0x01,0x4F,0x00,0x00,0x02,0x08,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x4E,0x00,0x00,0x02, +0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00, +0x00,0x00,0x01,0x4D,0x00,0x00,0x02,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x4C,0x00,0x00,0x02,0x08,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00, +0x01,0x4B,0x00,0x00,0x02,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00, +0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x4A,0x00,0x00,0x02,0x08,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x48, +0x00,0x00,0x02,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00, +0x00,0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,0x01,0x40,0x00,0x00,0x01,0x08,0x00, +0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x01,0x80,0x00,0x00, +0x02,0x07,0x00,0x01,0x41,0x00,0x00,0x02,0x0C,0x00,0x00,0x01,0x31,0x00,0x00,0x02, +0x08,0x00,0x01,0x41,0x00,0x00,0x02,0x0C,0x00,0x00,0x01,0x80,0x00,0x00,0x02,0x07, +0x00,0x01,0x41,0x00,0x00,0x02,0x0C,0x00,0x00,0x01,0x9D,0x04,0x00,0x0E,0x0C,0x00, +0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00, +0x01,0x42,0x04,0x00,0x0E,0x0B,0x00,0x01,0xC6,0x04,0x00,0x0E,0x0C,0x00,0x01,0x60, +0x00,0x00,0x0E,0x06,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0xA2,0xA5, +0x00,0xA2,0x00,0x01,0x9D,0x04,0x00,0x06,0x0C,0x00,0x00,0x00,0x00,0x00,0x01,0x00, +0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x42,0x04,0x00,0x06,0x0B, +0x00,0x01,0xC6,0x04,0x00,0x06,0x0C,0x00,0x01,0x60,0x00,0x00,0x06,0x06,0x00,0x00, +0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x62,0x04,0x00,0x06,0x06,0x00,0x01,0x00, +0x00,0x00,0x01,0x52,0x04,0x00,0x06,0x0C,0x00,0x00,0x00,0x00,0x00,0x0A,0x00,0x00, +0x00,0x01,0x52,0x04,0x00,0x06,0x0C,0x00,0x01,0x00,0x00,0x00,0x0A,0x00,0x00,0x00, +0x01,0x53,0x04,0x00,0x06,0x0C,0x00,0x0C,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x01, +0xC1,0x04,0x00,0x06,0x0C,0x00,0x01,0x34,0x04,0x00,0x06,0x0C,0x00,0x01,0x91,0x04, +0x00,0x06,0x07,0x00,0x01,0x33,0x04,0x00,0x06,0x09,0x00,0x01,0xC5,0x04,0x00,0x06, +0x0C,0x00,0x01,0x55,0x04,0x00,0x06,0x0B,0x00,0x01,0x46,0x04,0x00,0x06,0x0B,0x00, +0x00,0x00,0x00,0x04,0x01,0x35,0x04,0x00,0x06,0x05,0x00,0x00,0x00,0x00,0x10,0x00, +0x20,0x01,0x10,0x00,0x20,0x00,0x00,0x01,0x43,0x04,0x00,0x06,0x0B,0x00,0x00,0x00, +0x40,0x00,0x01,0x90,0x04,0x00,0x06,0x07,0x00,0x01,0x32,0x04,0x00,0x06,0x09,0x00, +0x01,0xC7,0x04,0x00,0x06,0x0C,0x00,0x01,0x51,0x04,0x00,0x06,0x0A,0x00,0x01,0x31, +0x04,0x00,0x06,0x08,0x00,0x01,0x49,0x04,0x00,0x06,0x08,0x00,0x01,0x00,0x00,0x00, +0x01,0xC2,0x00,0x00,0x06,0x0C,0x00,0x01,0x30,0x04,0x00,0x06,0x08,0x00,0x01,0x00, +0x00,0x00,0x01,0x4F,0x07,0x00,0x06,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x4E,0x07,0x00,0x06,0x08,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00, +0x01,0x4D,0x07,0x00,0x06,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00, +0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x4C,0x07,0x00,0x06,0x08,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x4B, +0x07,0x00,0x06,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04, +0x00,0x01,0x00,0x00,0x00,0x01,0x4A,0x07,0x00,0x06,0x08,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x48,0x07,0x00, +0x06,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0xAA, +0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,0x01,0x47,0x07,0x00,0x06,0x08,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x40,0x00,0x00,0x01,0x00,0x00,0x00,0x0A,0x00, +0x00,0x00,0x01,0x40,0x04,0x00,0x06,0x08,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00, +0x00,0x01,0x00,0x00,0x00,0x01,0x44,0x04,0x00,0x06,0x0B,0x00,0x00,0x00,0x00,0x01, +0x01,0x36,0x04,0x00,0x06,0x05,0x00,0x00,0x00,0x01,0x00,0xFC,0xFF,0x02,0x00,0x00, +0x02,0x00,0x00,0x01,0x80,0x07,0x00,0x06,0x07,0x00,0x01,0x34,0x84,0x00,0x06,0x0B, +0x00,0x01,0xC5,0x84,0x00,0x06,0x0B,0x00,0x01,0x45,0x04,0x00,0x06,0x07,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x5A,0x06,0x00,0x06, +0x09,0x00,0x00,0x00,0x00,0x00,0x10,0x27,0x00,0x00,0x01,0x41,0x04,0x00,0x06,0x0C, +0x00,0x01,0x9D,0x04,0x00,0x06,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x01,0x9D,0x00,0x00,0x0A,0x0C,0x00, +0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x01,0x42,0x00,0x00,0x0A,0x0B,0x00,0x01,0xC6,0x00,0x00,0x0A,0x0C,0x00,0x01,0x60, +0x00,0x00,0x0A,0x06,0x00,0x05,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x01,0x62,0x00, +0x00,0x0A,0x06,0x00,0x01,0x00,0x00,0x00,0x00,0x01,0x9D,0x00,0x00,0x0A,0x0C,0x00, +0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x01,0x42,0x00,0x00,0x0A,0x0B,0x00,0x01,0xC6,0x00,0x00,0x0A,0x0C,0x00,0x01,0x60, +0x00,0x00,0x0A,0x06,0x00,0x05,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x01,0x91,0x00, +0x00,0x0A,0x07,0x00,0x01,0x90,0x00,0x00,0x0A,0x07,0x00,0x01,0x33,0x00,0x00,0x0A, +0x09,0x00,0x01,0x32,0x00,0x00,0x0A,0x09,0x00,0x01,0x31,0x00,0x00,0x0A,0x08,0x00, +0x01,0x49,0x00,0x00,0x0A,0x08,0x00,0x01,0x00,0x00,0x00,0x01,0x30,0x00,0x00,0x0E, +0x08,0x00,0x00,0x00,0x00,0x00,0x01,0x62,0x00,0x00,0x0E,0x06,0x00,0x00,0x00,0x00, +0x00,0x00,0x01,0x91,0x00,0x00,0x0A,0x07,0x40,0x01,0x90,0x00,0x00,0x0A,0x07,0x39, +0x01,0x33,0x00,0x00,0x0A,0x09,0x38,0x01,0x32,0x00,0x00,0x0A,0x09,0x37,0x01,0x31, +0x00,0x00,0x0A,0x08,0x36,0x01,0x49,0x00,0x00,0x0A,0x08,0x35,0x01,0x00,0x00,0x00, +0x01,0x30,0x00,0x00,0x0E,0x08,0x34,0x00,0x00,0x00,0x00,0x00,0x01,0x52,0x00,0x00, +0x0A,0x0C,0x33,0x00,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x01,0x52,0x00,0x00,0x0A, +0x0C,0x32,0x01,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x01,0x53,0x00,0x00,0x0A,0x0C, +0x31,0x02,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x01,0xC1,0x00,0x00,0x0A,0x0C,0x30, +0x01,0x34,0x00,0x00,0x0A,0x0C,0x29,0x01,0xC5,0x00,0x00,0x0A,0x0C,0x28,0x01,0x55, +0x00,0x00,0x0A,0x0B,0x27,0x01,0x51,0x00,0x00,0x0A,0x0A,0x26,0x01,0xC7,0x00,0x00, +0x0A,0x0C,0x25,0x01,0x46,0x00,0x00,0x0A,0x0B,0x24,0x00,0x00,0x00,0x04,0x01,0x35, +0x00,0x00,0x0A,0x05,0x23,0x00,0x00,0x00,0x10,0x00,0x20,0x01,0x10,0x00,0x20,0x00, +0x00,0x01,0x43,0x00,0x00,0x0A,0x0B,0x22,0x00,0x00,0x00,0x01,0x01,0xC2,0x00,0x00, +0x0A,0x0C,0x21,0x01,0x4F,0x00,0x00,0x0A,0x08,0x20,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x4E,0x00,0x00,0x0A,0x08, +0x19,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00, +0x00,0x01,0x4D,0x00,0x00,0x0A,0x08,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, +0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x4C,0x00,0x00,0x0A,0x08,0x17,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01, +0x4B,0x00,0x00,0x0A,0x08,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00, +0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x4A,0x00,0x00,0x0A,0x08,0x15,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01,0x48,0x00, +0x00,0x0A,0x08,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00, +0xAA,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x55,0x01,0x47,0x00,0x00,0x0A,0x08,0x13,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x40,0x00,0x00,0x01,0x00,0x00,0x00,0x01, +0x00,0x00,0x00,0x01,0x40,0x00,0x00,0x09,0x08,0x12,0x01,0x00,0x00,0x00,0x04,0x00, +0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x01,0x44,0x00,0x00,0x0A,0x0B,0x11,0x00,0x00,0x00, +0x01,0x01,0x36,0x00,0x00,0x0A,0x05,0x10,0x00,0x00,0x01,0x00,0xFC,0xFF,0x02,0x00, +0x00,0x02,0x00,0x00,0x01,0x80,0x00,0x00,0x0A,0x07,0x09,0x01,0x54,0x00,0x00,0x0A, +0x0B,0x08,0x01,0x34,0x80,0x00,0x0A,0x0B,0x07,0x01,0xC5,0x80,0x00,0x0A,0x0B,0x06, +0x01,0x45,0x00,0x00,0x0A,0x07,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00, +0x00,0x04,0x00,0x01,0x5A,0x02,0x00,0x0A,0x09,0x04,0x01,0x00,0x00,0x00,0xE8,0x03, +0x00,0x00,0x01,0x41,0x00,0x00,0x0A,0x0C,0x03,0x00,0x00,0x00,0x00,0x00,0xF5,0x48, +0x05,0x20,0xF7,0x48,0x05,0x20,0xB5,0x49,0x05,0x20,0xB8,0x49,0x05,0x20,0xD1,0x49, +0x05,0x20,0x2E,0x4B,0x05,0x20,0x32,0x4B,0x05,0x20,0x14,0x4C,0x05,0x20,0x33,0x4C, +0x05,0x20,0x0E,0x4D,0x05,0x20,0x12,0x4D,0x05,0x20,0x15,0x4D,0x05,0x20,0x18,0x4D, +0x05,0x20,0xEB,0x4D,0x05,0x20,0xFA,0x4D,0x05,0x20,0x09,0x4E,0x05,0x20,0x3E,0x4E, +0x05,0x20,0x41,0x4E,0x05,0x20,0x43,0x4E,0x05,0x20,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x89,0x50,0x05,0x20,0xC9,0x50,0x05,0x20,0x42,0x51,0x05,0x20,0x7C,0x51, +0x05,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x7F,0x7F,0x7F, +0x0D,0x0D,0x0D,0x0D,0x09,0x09,0x09,0x09,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x60,0x1E,0x7E,0x60,0x31,0x00,0x21,0x31,0x32,0x00,0x40,0x32,0x33,0x1B,0x23,0x33, +0x34,0x1C,0x24,0x34,0x35,0x1D,0x25,0x35,0x36,0x1E,0x5E,0x36,0x37,0x1F,0x26,0x37, +0x38,0x7F,0x2A,0x38,0x39,0x00,0x28,0x39,0x30,0x00,0x29,0x30,0x2D,0x00,0x5F,0x2D, +0x3D,0x00,0x2B,0x3D,0x71,0x11,0x51,0x51,0x77,0x17,0x57,0x57,0x65,0x05,0x45,0x45, +0x72,0x12,0x52,0x52,0x74,0x14,0x54,0x54,0x79,0x19,0x59,0x59,0x75,0x15,0x55,0x55, +0x69,0x09,0x49,0x49,0x6F,0x0F,0x4F,0x4F,0x70,0x10,0x50,0x50,0x5B,0x1B,0x7B,0x5B, +0x5D,0x1D,0x7D,0x5D,0x61,0x01,0x41,0x41,0x73,0x13,0x53,0x53,0x64,0x04,0x44,0x44, +0x66,0x06,0x46,0x46,0x67,0x07,0x47,0x47,0x68,0x08,0x48,0x48,0x6A,0x0A,0x4A,0x4A, +0x6B,0x0B,0x4B,0x4B,0x6C,0x0C,0x4C,0x4C,0x3B,0x00,0x3A,0x3B,0x27,0x00,0x22,0x27, +0x5C,0x1C,0x7C,0x5C,0x3C,0x00,0x3E,0x3C,0x7A,0x1A,0x5A,0x5A,0x78,0x18,0x58,0x58, +0x63,0x03,0x43,0x43,0x76,0x16,0x56,0x56,0x62,0x02,0x42,0x42,0x6E,0x0E,0x4E,0x4E, +0x6D,0x0D,0x4D,0x4D,0x2C,0x00,0x2C,0x2C,0x2E,0x00,0x2E,0x2E,0x2F,0x1F,0x3F,0x2F, +0x00,0x00,0x00,0x00,0x00,0x00,0xB0,0x00,0x32,0x00,0x22,0x32,0x33,0x1B,0x40,0x33, +0x36,0x1E,0x26,0x36,0x37,0x1F,0x2F,0x37,0x38,0x7F,0x28,0x38,0x39,0x00,0x29,0x39, +0x30,0x00,0x3D,0x30,0x2B,0x00,0x3F,0x2B,0x5D,0x00,0x5B,0x5D,0xF8,0x00,0xD8,0xD8, +0x23,0x00,0x5C,0x23,0x27,0x00,0x2A,0x27,0x2C,0x00,0x3B,0x2C,0x2E,0x00,0x3A,0x2E, +0x2D,0x00,0x5F,0x2D,0x26,0x00,0x31,0x26,0xE9,0x00,0x32,0xE9,0x22,0x1B,0x33,0x22, +0x27,0x1C,0x34,0x27,0x28,0x1D,0x35,0x28,0x5B,0x1E,0x36,0x5B,0x5D,0x1F,0x37,0x5D, +0x21,0x7F,0x38,0x21,0xE7,0x00,0x39,0xE7,0xE0,0x00,0x30,0xE0,0x29,0x00,0xB0,0x29, +0x2D,0x00,0x5F,0x2D,0x7A,0x1A,0x5A,0x5A,0x24,0x00,0x2A,0x24,0x71,0x11,0x51,0x51, +0x6D,0x0D,0x4D,0x4D,0x5C,0x00,0x25,0x5C,0x23,0x00,0x40,0x23,0x77,0x17,0x57,0x57, +0x2C,0x00,0x3F,0x2C,0x3B,0x00,0x2E,0x3B,0x3A,0x00,0x2F,0x3A,0x3D,0x00,0x2B,0x3D, +0x33,0x1B,0x2F,0x33,0x36,0x1E,0x3F,0x36,0x23,0x00,0x40,0x23,0x2C,0x00,0x27,0x2C, +0xE9,0x00,0xC9,0xC9,0x37,0x1F,0x27,0x37,0xBD,0x00,0xBC,0xBD,0x40,0x00,0x5C,0x40, +0x5D,0x00,0x5B,0x5D,0x3B,0x00,0x2B,0x3B,0x3A,0x00,0x2A,0x3A,0x23,0x00,0x5C,0x23, +0xF6,0x00,0xD6,0xD6,0xE4,0x00,0xC4,0xC4,0x5D,0x00,0x5B,0x5D,0x23,0x00,0x27,0x23, +0x2B,0x00,0x2A,0x2B,0x33,0x1B,0xA7,0x33,0xDF,0x00,0x3F,0xDF,0x79,0x19,0x59,0x59, +0x7A,0x1A,0x5A,0x5A,0x31,0x00,0xBC,0x31,0x32,0x00,0x5B,0x32,0x33,0x1B,0x5D,0x33, +0x36,0x1E,0x23,0x36,0x30,0x00,0x5C,0x30,0x27,0x00,0x22,0x27,0x2F,0x00,0x3A,0x2F, +0x2A,0x00,0x21,0x2A,0x2D,0x00,0x5F,0x2D,0x40,0x1C,0x7C,0x40,0x2C,0x00,0x3F,0x2C, +0x2E,0x00,0x3B,0x2E,0x2B,0x00,0x3D,0x2B,0x40,0x00,0x31,0x40,0x23,0x00,0x32,0x23, +0x5F,0x1E,0x36,0x5F,0xE8,0x1F,0x37,0xE8,0x00,0x7F,0x38,0x00,0x5B,0x00,0x39,0x5B, +0x5D,0x00,0x30,0x5D,0x2D,0x00,0x2B,0x2D,0xEC,0x00,0x3D,0xEC,0x24,0x00,0x26,0x24, +0xF9,0x00,0x25,0xF9,0x2A,0x00,0x5C,0x2A,0xF2,0x00,0x21,0xF2,0x21,0x00,0xB0,0x21, +0x31,0x00,0x2B,0x31,0x33,0x1B,0x2A,0x33,0x34,0x1C,0x40,0x34,0x27,0x00,0x3F,0x27, +0x7A,0x1A,0x5A,0x5A,0xE8,0x00,0x5C,0xE8,0xE0,0x00,0x5D,0xE0,0xE9,0x00,0x5B,0xE9, +0x24,0x00,0x23,0x24,0x79,0x19,0x59,0x59,0xFC,0x00,0x5C,0xFC,0xF6,0x00,0x5B,0xF6, +0xE4,0x00,0x5D,0xE4,0xE6,0x00,0xC6,0xC6,0x61,0x01,0x41,0x41,0xF1,0x00,0xD1,0xD1, +0xE7,0x00,0x00,0xE7,0x40,0x00,0x5C,0x40,0x5D,0x00,0x5B,0x5D,0x5C,0x1C,0x7C,0x5C, +0x36,0x1E,0x22,0x36,0x2E,0x00,0x3A,0x2E,0x27,0x00,0x60,0x27,0xE7,0x00,0xC7,0xC7, +0x7E,0x00,0x5E,0x7E,0x5B,0x1B,0x7B,0x5B,0x00,0x01,0x02,0x05,0x06,0x12,0x1E,0x2B, +0x35,0x07,0x13,0x1F,0x2C,0x2A,0x35,0x08,0x14,0x20,0x2D,0x35,0x09,0x15,0x21,0x2E, +0x03,0x04,0x0A,0x16,0x22,0x2F,0x35,0x0B,0x17,0x23,0x30,0x35,0x0C,0x18,0x24,0x31, +0x35,0x0D,0x19,0x25,0x32,0x35,0x0E,0x1A,0x26,0x33,0x35,0x0F,0x1B,0x35,0x27,0x34, +0x35,0x11,0x1D,0x29,0x35,0x10,0x1C,0x28,0x35,0x35,0x35,0x35,0x00,0x01,0x02,0x35, +0x46,0x9B,0x54,0x58,0x35,0x47,0x52,0x1F,0x2C,0x2A,0x35,0x48,0x14,0x20,0x2D,0x35, +0x49,0x15,0x21,0x2E,0x03,0x04,0x4A,0x16,0x22,0x2F,0x35,0x4B,0x17,0x23,0x30,0x35, +0x4C,0x18,0x24,0x59,0x35,0x4D,0x19,0x25,0x5A,0x35,0x4E,0x1A,0x26,0x5B,0x35,0x4F, +0x1B,0x35,0x55,0x5C,0x35,0x51,0x53,0x57,0x35,0x50,0x35,0x56,0x35,0x35,0x35,0x35, +0x00,0x01,0x02,0x36,0x06,0x12,0x1E,0x2B,0x35,0x37,0x13,0x1F,0x2C,0x2A,0x35,0x5D, +0x14,0x20,0x2D,0x35,0x09,0x15,0x21,0x2E,0x03,0x04,0x0A,0x16,0x22,0x2F,0x35,0x5E, +0x17,0x23,0x30,0x35,0x0C,0x18,0x24,0x31,0x35,0x0D,0x19,0x25,0x60,0x35,0x0E,0x1A, +0x26,0x33,0x35,0x0F,0x1B,0x35,0x27,0x61,0x35,0x11,0x5F,0x29,0x35,0x10,0x3F,0x35, +0x35,0x35,0x35,0x35,0x00,0x01,0x02,0x36,0x06,0x12,0x1E,0x2B,0x35,0x37,0x13,0x1F, +0x2C,0x2A,0x35,0x38,0x14,0x20,0x2D,0x35,0x09,0x15,0x21,0x2E,0x03,0x04,0x0A,0x16, +0x22,0x2F,0x35,0x39,0x17,0x23,0x30,0x35,0x3A,0x18,0x24,0x31,0x35,0x3B,0x19,0x25, +0x43,0x35,0x3C,0x1A,0x26,0x44,0x35,0x3D,0x1B,0x35,0x40,0x45,0x35,0x35,0x35,0x42, +0x35,0x3E,0x3F,0x41,0x35,0x35,0x35,0x35,0x00,0x01,0x02,0x36,0x06,0x12,0x1E,0x2B, +0x35,0x37,0x13,0x1F,0x2C,0x2A,0x35,0x08,0x14,0x20,0x2D,0x35,0x09,0x15,0x21,0x2E, +0x03,0x04,0x0A,0x16,0x22,0x2F,0x35,0x39,0x17,0x23,0x30,0x35,0x62,0x18,0x24,0x31, +0x35,0x3B,0x19,0x25,0x32,0x35,0x3C,0x1A,0x26,0x33,0x35,0x3D,0x1B,0x35,0x66,0x34, +0x35,0x63,0x65,0x35,0x35,0x10,0x64,0x67,0x35,0x35,0x35,0x35,0x00,0x01,0x02,0x36, +0x06,0x12,0x1E,0x2B,0x35,0x37,0x13,0x1F,0x2C,0x2A,0x35,0x38,0x14,0x20,0x2D,0x35, +0x09,0x15,0x21,0x2E,0x03,0x04,0x0A,0x16,0x22,0x2F,0x35,0x39,0x17,0x23,0x30,0x35, +0x3A,0x18,0x24,0x31,0x35,0x3B,0x19,0x25,0x43,0x35,0x3C,0x1A,0x26,0x44,0x35,0x3D, +0x1B,0x35,0x69,0x45,0x35,0x35,0x68,0x42,0x35,0x3E,0x3F,0x6A,0x35,0x35,0x35,0x35, +0x00,0x01,0x02,0x35,0x06,0x12,0x1E,0x70,0x35,0x37,0x13,0x1F,0x2C,0x2A,0x35,0x6E, +0x14,0x20,0x2D,0x35,0x09,0x15,0x21,0x2E,0x03,0x04,0x0A,0x16,0x22,0x2F,0x35,0x39, +0x71,0x23,0x30,0x35,0x3A,0x18,0x24,0x31,0x35,0x3B,0x19,0x25,0x43,0x35,0x3C,0x1A, +0x26,0x44,0x35,0x3D,0x1B,0x35,0x69,0x45,0x35,0x35,0x6D,0x6C,0x35,0x6F,0x64,0x6B, +0x35,0x35,0x35,0x35,0x00,0x01,0x02,0x36,0x72,0x12,0x1E,0x2B,0x35,0x73,0x13,0x1F, +0x2C,0x2A,0x35,0x74,0x14,0x20,0x2D,0x35,0x09,0x15,0x21,0x2E,0x03,0x04,0x0A,0x16, +0x22,0x2F,0x35,0x75,0x17,0x23,0x30,0x35,0x0C,0x18,0x24,0x31,0x35,0x3B,0x19,0x25, +0x7C,0x35,0x3C,0x1A,0x26,0x7D,0x35,0x76,0x1B,0x35,0x7A,0x7E,0x35,0x78,0x79,0x7B, +0x35,0x77,0x35,0x35,0x35,0x35,0x35,0x35,0x00,0x01,0x02,0x35,0x7F,0x12,0x1E,0x58, +0x35,0x80,0x52,0x1F,0x2C,0x2A,0x35,0x48,0x14,0x20,0x2D,0x35,0x49,0x15,0x21,0x2E, +0x03,0x04,0x4A,0x16,0x22,0x2F,0x35,0x81,0x17,0x23,0x30,0x35,0x82,0x18,0x24,0x59, +0x35,0x83,0x19,0x25,0x5A,0x35,0x84,0x1A,0x26,0x5B,0x35,0x85,0x1B,0x35,0x55,0x8B, +0x35,0x86,0x88,0x8A,0x35,0x50,0x87,0x89,0x35,0x35,0x35,0x35,0x00,0x01,0x02,0x8C, +0x8D,0x12,0x1E,0x96,0x35,0x37,0x13,0x1F,0x2C,0x2A,0x35,0x8E,0x14,0x20,0x2D,0x35, +0x8F,0x15,0x21,0x2E,0x03,0x04,0x0A,0x16,0x22,0x2F,0x35,0x39,0x91,0x23,0x30,0x35, +0x3A,0x18,0x24,0x31,0x35,0x3B,0x19,0x25,0x43,0x35,0x3C,0x1A,0x26,0x44,0x35,0x3D, +0x1B,0x35,0x94,0x45,0x35,0x35,0x35,0x95,0x35,0x90,0x92,0x93,0x35,0x35,0x35,0x35, +0x00,0x01,0x02,0x8C,0x8D,0x12,0x1E,0x96,0x35,0x37,0x13,0x1F,0x2C,0x2A,0x35,0x8E, +0x14,0x20,0x2D,0x35,0x8F,0x15,0x21,0x2E,0x03,0x04,0x0A,0x16,0x22,0x2F,0x35,0x39, +0x91,0x23,0x30,0x35,0x3A,0x18,0x24,0x31,0x35,0x3B,0x19,0x25,0x43,0x35,0x3C,0x1A, +0x26,0x44,0x35,0x3D,0x1B,0x35,0x98,0x45,0x35,0x35,0x35,0x95,0x35,0x90,0x97,0x99, +0x35,0x35,0x35,0x35,0x00,0x01,0x02,0x36,0x06,0x12,0x1E,0x2B,0x35,0x37,0x13,0x1F, +0x2C,0x2A,0x35,0x38,0x14,0x20,0x2D,0x35,0x09,0x15,0x21,0x2E,0x03,0x04,0x0A,0x16, +0x22,0x2F,0x35,0x39,0x17,0x23,0x30,0x35,0x3A,0x18,0x24,0x31,0x35,0x3B,0x19,0x25, +0x43,0x35,0x3C,0x1A,0x26,0x44,0x35,0x3D,0x1B,0x35,0x69,0x45,0x35,0x35,0x68,0x42, +0x35,0x3E,0x3F,0x6A,0x35,0x35,0x35,0x35,0x00,0x01,0x02,0x36,0x06,0x12,0x1E,0x2B, +0x35,0x37,0x13,0x1F,0x2C,0x2A,0x35,0x38,0x14,0x20,0x2D,0x35,0x09,0x15,0x21,0x2E, +0x03,0x04,0x0A,0x16,0x22,0x2F,0x35,0x39,0x17,0x23,0x30,0x35,0x3A,0x18,0x24,0x31, +0x35,0x3B,0x19,0x25,0x43,0x35,0x3C,0x1A,0x26,0x44,0x35,0x3D,0x1B,0x35,0x9A,0x45, +0x35,0x35,0x35,0x42,0x35,0x3E,0x3F,0x41,0x35,0x35,0x35,0x35,0x00,0x01,0x02,0x35, +0x46,0x9B,0x54,0x58,0x35,0x47,0x52,0x1F,0x2C,0x2A,0x35,0x48,0x14,0x20,0x2D,0x35, +0x49,0x15,0x21,0x2E,0x03,0x04,0x4A,0x16,0x22,0x2F,0x35,0x4B,0x17,0x23,0x30,0x35, +0x4C,0x18,0x24,0x59,0x35,0x4D,0x19,0x25,0x5A,0x35,0x4E,0x1A,0x26,0x5B,0x35,0x4F, +0x1B,0x35,0x55,0x5C,0x35,0x51,0x53,0x57,0x35,0x50,0x35,0x56,0x35,0x35,0x35,0x35, +0x00,0x01,0x02,0x9E,0x06,0x12,0x1E,0x2B,0x35,0x37,0x13,0x1F,0x2C,0x2A,0x35,0x08, +0x14,0x20,0x2D,0x35,0x09,0x15,0x21,0x2E,0x03,0x04,0x0A,0x16,0x22,0x2F,0x35,0x39, +0x17,0x23,0x30,0x35,0x3A,0x18,0x24,0x31,0x35,0x3B,0x19,0x25,0x43,0x35,0x3C,0x1A, +0x26,0x44,0x35,0x3D,0x1B,0x35,0x9C,0x45,0x35,0x9F,0x6D,0x9D,0x35,0x90,0x35,0x35, +0x35,0x35,0x35,0x35,0x00,0x01,0x02,0xA0,0x06,0x12,0x1E,0x2B,0x35,0x07,0x13,0x1F, +0x2C,0x2A,0x35,0x08,0x14,0x20,0x2D,0x35,0x09,0x15,0x21,0x2E,0x03,0x04,0x0A,0x16, +0x22,0x2F,0x35,0xA1,0x17,0x23,0x30,0x35,0x0C,0x18,0x24,0x31,0x35,0x0D,0x19,0x25, +0x43,0x35,0x0E,0x1A,0x26,0xA2,0x35,0x0F,0x1B,0x35,0xA4,0x34,0x35,0x11,0x1D,0xA6, +0x35,0x10,0xA3,0xA5,0x35,0x35,0x35,0x35,0x88,0x90,0x98,0xA0,0xA8,0xB6,0xB8,0xC0, +0xC8,0xD0,0xD8,0xE0,0xE8,0xF0,0x00,0x00,0xBB,0x8F,0xFC,0x03,0x9E,0xEF,0xDE,0xEF, +0xFE,0xFF,0xAB,0x40,0x9E,0xEF,0x36,0xEA,0xFE,0xFF,0xAB,0x44,0x9E,0xEF,0x77,0x01, +0x00,0x00,0xAB,0x48,0x94,0xAB,0x54,0xD4,0xAB,0x38,0x9E,0xEF,0xF5,0xC1,0xFE,0xFF, +0xAB,0x5C,0x31,0x0A,0x00,0x44,0x49,0x41,0x47,0x30,0x20,0x20,0x20,0x20,0x20,0xD4, +0xAB,0x4C,0xD4,0xAB,0x50,0xD0,0xAB,0x58,0x59,0xD0,0x59,0x52,0x9A,0xA9,0x04,0x51, +0x3C,0x8F,0xFF,0xFF,0x50,0x16,0xEF,0x6F,0xC1,0xFE,0xFF,0xD5,0x50,0x12,0x5C,0x9A, +0xA9,0x05,0x50,0xC0,0x06,0x50,0xC0,0x59,0x50,0xD0,0x59,0x52,0xD0,0x60,0x51,0x3C, +0x8F,0xFF,0xFF,0x50,0x16,0xEF,0x50,0xC1,0xFE,0xFF,0xD5,0x50,0x12,0x3D,0x9E,0xAF, +0xB4,0x50,0xD0,0x59,0x51,0x16,0xEF,0x35,0x00,0x00,0x00,0xE9,0x50,0x2D,0xFB,0x00, +0x61,0xD1,0x01,0x50,0x12,0x25,0x9E,0xEF,0x88,0x00,0x00,0x00,0xAB,0x28,0x9E,0xEF, +0x93,0x00,0x00,0x00,0xAB,0x2C,0x9E,0xEF,0x9D,0x00,0x00,0x00,0xAB,0x64,0x9E,0xEF, +0xA0,0x00,0x00,0x00,0xAB,0x6C,0x11,0x03,0xD0,0x06,0x51,0xBA,0x8F,0xFC,0x03,0x05, +0xBB,0x8F,0xE0,0x01,0x7D,0x50,0x55,0x9A,0xA6,0x04,0x57,0xC0,0x56,0x57,0x3C,0x87, +0x58,0x12,0x05,0xD0,0x01,0x51,0x11,0x45,0xD1,0x67,0x65,0x12,0x0E,0xD1,0xA7,0x04, +0xA5,0x04,0x12,0x07,0xB1,0xA7,0x08,0xA5,0x08,0x13,0x0B,0xC0,0x1A,0x57,0xF5,0x58, +0xE7,0xD0,0x07,0x51,0x11,0x27,0xC1,0xA7,0x0E,0x56,0x52,0xD0,0xA7,0x0A,0x51,0x3C, +0x8F,0xFF,0xFF,0x50,0x16,0xEF,0xC0,0xC0,0xFE,0xFF,0xD5,0x50,0x13,0x05,0xD0,0x03, +0x51,0x11,0x0A,0xC1,0xA7,0x12,0x56,0x51,0xD0,0x01,0x50,0x11,0x02,0xD4,0x50,0xBA, +0x8F,0xE0,0x01,0x05,0xBB,0x8F,0x82,0x00,0xB0,0x3F,0xBB,0x3C,0x9A,0x01,0x51,0x16, +0xBB,0x30,0xBA,0x8F,0x82,0x00,0x05,0xBB,0x8F,0x82,0x00,0xB0,0x3F,0xBB,0x3C,0xD4, +0x51,0x16,0xBB,0x30,0xBA,0x8F,0x82,0x00,0x05,0x16,0xBB,0x1C,0xE9,0x50,0x04,0x90, +0x51,0xAB,0x55,0x05,0x90,0xAB,0x55,0x50,0x05,0xBB,0x8F,0x8C,0x00,0xD0,0x8F,0x00, +0xF8,0x03,0x00,0x52,0xC0,0xAB,0x58,0x52,0xD4,0x53,0xB0,0x53,0x82,0xF2,0x8F,0xF0, +0x00,0x00,0x00,0x53,0xF5,0x16,0xBB,0x28,0xBA,0x8F,0x8C,0x00,0x05,0xD5,0x51,0x12, +0x0A,0xDD,0x57,0x16,0xBB,0x2C,0xD0,0x8E,0x57,0x11,0x0D,0xD1,0x51,0x01,0x12,0x08, +0xDD,0x57,0x16,0xBB,0x28,0xD0,0x8E,0x57,0x05,0xBB,0x0C,0x91,0x8F,0xAF,0x52,0x12, +0x07,0x88,0x02,0xAB,0x54,0x31,0x8D,0x00,0x91,0x8F,0xAE,0x52,0x12,0x07,0x88,0x04, +0xAB,0x54,0x31,0x80,0x00,0x91,0x8F,0xB0,0x52,0x12,0x20,0x8C,0x08,0xAB,0x54,0x93, +0x08,0xAB,0x54,0x13,0x09,0x9E,0xEF,0x6F,0x00,0x00,0x00,0x50,0x11,0x07,0x9E,0xEF, +0x69,0x00,0x00,0x00,0x50,0x16,0xBB,0x34,0x31,0x5A,0x00,0x91,0x8F,0xB3,0x52,0x12, +0x07,0x8A,0x06,0xAB,0x54,0x31,0x4D,0x00,0x91,0x52,0x8F,0xBC,0x1F,0x47,0xC2,0x8F, +0xBC,0x00,0x00,0x00,0x52,0xEA,0x00,0x04,0xAB,0x54,0x51,0x13,0x02,0x11,0x02,0xD4, +0x51,0xD0,0x8F,0x00,0x04,0x14,0x20,0x53,0x9A,0xA3,0x02,0x53,0x9E,0xEF,0xA6,0xF9, +0xFF,0xFF,0x7E,0xC4,0x8F,0x44,0x00,0x00,0x00,0x53,0xC0,0x8E,0x53,0x9A,0x42,0x63, +0x53,0x78,0x02,0x53,0x53,0xC0,0x53,0x51,0x9E,0xEF,0xEE,0xF6,0xFF,0xFF,0x52,0x9A, +0x41,0x62,0x51,0x12,0x02,0xD4,0x50,0xBA,0x0C,0x05,0x13,0x84,0x00,0x11,0x84,0x00, +0xA7,0x00,0x00,0x00,0x31,0x11,0x02,0x00,0x56,0x45,0x52,0x53,0x07,0x02,0x00,0x0A, +0x04,0x53,0x59,0x53,0x30,0x00,0x00,0x00,0x00,0x00,0x13,0x5B,0x53,0x59,0x53,0x45, +0x58,0x45,0x5D,0x53,0x59,0x53,0x42,0x4F,0x4F,0x54,0x2E,0x45,0x58,0x45,0x16,0x5B, +0x53,0x59,0x53,0x4D,0x41,0x49,0x4E,0x54,0x5D,0x44,0x49,0x41,0x47,0x42,0x4F,0x4F, +0x54,0x2E,0x45,0x58,0x45,0x00,0x00,0x00,0x44,0x55,0x41,0x00,0xFF,0xFF,0x00,0x11, +0x1A,0x00,0x00,0x00,0x68,0x14,0x00,0x20,0xA2,0x07,0x00,0x00,0x44,0x4A,0x41,0x00, +0xFF,0xFF,0x00,0x11,0x1A,0x00,0x00,0x00,0x68,0x14,0x00,0x20,0x8E,0x07,0x00,0x00, +0x44,0x41,0x41,0x00,0xFF,0xFF,0x00,0x11,0x1A,0x00,0x00,0x00,0x68,0x14,0x00,0x20, +0x7A,0x07,0x00,0x00,0x44,0x4C,0x41,0x00,0x03,0x00,0x00,0x02,0x0E,0x00,0x00,0x00, +0x00,0x19,0x00,0x20,0x66,0x07,0x00,0x00,0x4D,0x55,0x41,0x00,0xFF,0xFF,0x00,0x12, +0x1E,0x00,0x00,0x00,0x40,0x19,0x00,0x20,0x52,0x07,0x00,0x00,0x58,0x51,0x41,0x00, +0x00,0x00,0x00,0x60,0x00,0x10,0x01,0x25,0x20,0x19,0x00,0x20,0xE8,0x06,0x00,0x00, +0x50,0x52,0x41,0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x49,0x06,0x00,0x00,0x00,0x00,0x08,0x09,0x40,0x48,0x08,0x41,0x10,0x09,0x42,0xC0, +0x08,0x43,0x08,0x08,0x44,0x10,0x08,0x45,0x28,0x08,0x46,0xAC,0x02,0x47,0x70,0x08, +0x48,0x18,0x08,0x49,0x01,0x06,0x4A,0x54,0x00,0x4B,0xD4,0x20,0x4C,0x84,0x00,0x4D, +0x00,0x80,0x4E,0x08,0x80,0x4F,0x10,0x80,0x50,0x18,0x80,0x51,0x8C,0x02,0x52,0x44, +0x03,0x53,0x32,0x0E,0x54,0x44,0x01,0x55,0x00,0x00,0x0D,0x0A,0x0A,0x20,0x20,0x32, +0x2E,0x2E,0x00,0x0D,0x0A,0x20,0x20,0x31,0x2E,0x2E,0x00,0x30,0x2E,0x2E,0x0D,0x0A, +0x0A,0x00,0x0D,0x0A,0x2D,0x00,0x0D,0x0A,0x42,0x6F,0x6F,0x74,0x20,0x44,0x65,0x76, +0x69,0x63,0x65,0x3A,0x20,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x01,0x00,0x60,0x01,0xAB,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2C, +0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x0F,0x07,0x07,0x07,0x07,0x07,0x07,0x07, +0x07,0x07,0x0F,0x07,0x07,0x0F,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x0F,0x07, +0x03,0x1F,0x0F,0x0F,0x03,0x00,0x00,0x00,0xE1,0x0A,0x9F,0x00,0x00,0x08,0x20,0x14, +0xD0,0x8F,0x50,0x52,0x41,0x30,0xAF,0xA9,0xDE,0xAF,0xC4,0x50,0x9A,0x04,0x60,0xDE, +0xAF,0x9F,0xA0,0x04,0x9E,0xCF,0x90,0x3B,0x57,0xC0,0x8F,0xFF,0x01,0x00,0x00,0x57, +0xCA,0x8F,0xFF,0x01,0x00,0x00,0x57,0x9E,0xC7,0x00,0x04,0x57,0x9A,0x8F,0xFF,0x59, +0xDE,0xCF,0xBD,0x08,0x77,0xF4,0x59,0xF8,0xD4,0xCF,0x50,0xFF,0x9E,0xCF,0x4D,0x07, +0xA7,0x04,0x9E,0xCF,0x0F,0x09,0xA7,0x60,0xD5,0x8F,0x88,0x99,0x05,0x20,0x13,0x0E, +0x9E,0xCF,0x34,0x3A,0xC7,0xC8,0x00,0x9E,0xCF,0x61,0x3A,0xC7,0xCC,0x00,0xDA,0x57, +0x11,0xDB,0x3E,0x58,0x78,0x8F,0xE8,0x58,0x58,0x90,0x58,0xCF,0x57,0x12,0xD5,0x8F, +0x00,0x00,0x00,0x00,0x13,0x27,0xBB,0x8F,0xFF,0x0F,0xD0,0xAB,0x34,0x52,0x13,0x0D, +0xD0,0xA2,0x2C,0x51,0x13,0x07,0xD0,0x5B,0x59,0xFA,0x6C,0x41,0x62,0xBA,0x8F,0xFF, +0x0F,0xD0,0xAB,0x20,0x51,0x7D,0xAB,0x44,0x52,0x7D,0xAB,0x2C,0x54,0xE1,0x09,0x55, +0x01,0x00,0xD0,0x5E,0x56,0x9E,0xC7,0x00,0x0A,0x5E,0xD5,0x8F,0xD4,0x8C,0x05,0x20, +0x13,0x1E,0x9E,0xEF,0x1D,0x2D,0x00,0x00,0xA7,0x2C,0x9E,0xEF,0x75,0x2D,0x00,0x00, +0xA7,0x28,0x9E,0xCF,0x09,0x00,0xEF,0x5D,0x27,0x00,0x00,0xE1,0x05,0x55,0x01,0x03, +0x9E,0xC6,0x00,0xFE,0x56,0x7D,0x50,0xA6,0x1C,0x7D,0x52,0xA6,0x24,0x7D,0x54,0xA6, +0x2C,0xD5,0x8F,0x00,0x00,0x00,0x00,0x13,0x32,0x9F,0xCF,0xC4,0xFE,0xDD,0x11,0x9F, +0xCF,0x23,0xFE,0xFB,0x03,0xCF,0x12,0x11,0xDE,0xCF,0xD3,0xFE,0x50,0x9A,0xCF,0xB0, +0xFE,0x60,0x9E,0xCF,0xAB,0xFE,0xA0,0x04,0xD0,0x50,0xA6,0x1C,0x2C,0x60,0xCF,0xA1, +0xFE,0x00,0x12,0xCF,0x9B,0xFE,0xE5,0x08,0xA6,0x30,0x00,0x7D,0x5A,0xA6,0x10,0xD0, +0x56,0x66,0xD0,0x56,0xCF,0x61,0xFE,0xD0,0x5C,0xA6,0x18,0xD4,0xA6,0x04,0xD4,0xA6, +0x0C,0xCE,0x01,0xA6,0x08,0x9E,0xCF,0x53,0x11,0xA6,0x34,0x2C,0x00,0x6E,0x00,0x8F, +0xDC,0x00,0xA6,0x38,0xD0,0x56,0x5B,0xD0,0x57,0xCB,0xB0,0x00,0xD0,0x8F,0x00,0x78, +0x08,0x20,0xAB,0x5C,0x90,0x8F,0x81,0xCB,0x90,0x00,0x90,0x28,0xCB,0x91,0x00,0xD0, +0xCF,0x89,0xFC,0xCB,0x10,0x01,0x9E,0xCF,0xBE,0xFD,0x5C,0xD0,0x5E,0x5A,0xD0,0x57, +0x59,0x7D,0xAB,0x24,0x52,0xD4,0xAB,0x4C,0xD4,0xCB,0x04,0x01,0x7D,0x52,0xAB,0x44, +0x78,0x03,0x52,0x52,0xD4,0xAC,0x0C,0xC3,0x01,0x52,0xAC,0x10,0x9E,0xCB,0xBC,0x00, +0x58,0xD0,0x52,0x68,0xD4,0xA8,0x04,0x9A,0x20,0x55,0x7C,0x56,0xD4,0x50,0xD4,0x57, +0x7D,0xAB,0x24,0x52,0x92,0x83,0x50,0x12,0x07,0xF5,0x52,0xF8,0x11,0x10,0x01,0x01, +0xD4,0x51,0xE1,0x51,0x50,0x02,0xD6,0x57,0xF3,0x07,0x51,0xF6,0x11,0xEB,0x7D,0xAB, +0x24,0x52,0x78,0x03,0x52,0xAB,0x4C,0xD0,0x57,0xCB,0x04,0x01,0xC2,0x57,0xAB,0x4C, +0x11,0x00,0xD4,0x52,0x7D,0xAB,0x24,0x50,0x9E,0x40,0x61,0x51,0x9A,0x71,0x50,0x12, +0x05,0xC0,0x08,0x52,0x11,0xF6,0x9A,0x07,0x51,0xE0,0x51,0x50,0x05,0xD6,0x52,0xF4, +0x51,0xF7,0xC2,0x52,0xCB,0x04,0x01,0xD0,0xAB,0x5C,0x57,0x9E,0xC7,0x00,0x08,0x57, +0xD4,0x51,0x3C,0x8F,0x44,0x03,0x50,0xD5,0x87,0x19,0x1E,0xF3,0x8F,0xFD,0x1F,0x00, +0x00,0x51,0xF4,0x3C,0x8F,0x00,0x20,0x51,0xD0,0xAB,0x1C,0x52,0xD1,0x8F,0x50,0x52, +0x41,0x30,0xB2,0x04,0x13,0x0B,0x30,0x7E,0x05,0xD5,0x87,0x18,0xEB,0xD5,0x87,0x18, +0xE7,0xD0,0x51,0xCF,0x7A,0x10,0x90,0x0D,0x9F,0x30,0x00,0x14,0x20,0x7C,0x7E,0x9F, +0xCF,0xB7,0xFC,0xFB,0x03,0xCF,0xC2,0x0F,0x7D,0xAB,0x44,0xAC,0x14,0xE0,0x03,0xAB, +0x30,0x65,0xE1,0x08,0xAB,0x30,0x26,0x7C,0x7E,0x9A,0x8F,0x8C,0x7E,0xFB,0x03,0xCF, +0xA8,0x0F,0x7C,0x7E,0x9A,0x8F,0x8C,0x7E,0xFB,0x03,0xCF,0x9D,0x0F,0x9F,0xAB,0x68, +0xDD,0x27,0x9A,0x8F,0x9A,0x7E,0xFB,0x03,0xCF,0x8F,0x0F,0x11,0x3A,0x9E,0xCF,0x79, +0xFB,0x57,0xE1,0x04,0xAB,0x30,0x05,0x9E,0xCF,0x83,0xFB,0x57,0xEF,0x1C,0x04,0xAB, +0x30,0x56,0xD1,0x56,0x09,0x15,0x03,0xC0,0x07,0x56,0x80,0x56,0xCF,0x55,0xFB,0x9A, +0x67,0x50,0x90,0x50,0xAB,0x68,0x28,0x50,0xA7,0x01,0xAB,0x6E,0x90,0x8F,0x5B,0xAB, +0x69,0xD0,0xCF,0x3C,0xFB,0xAB,0x6A,0xD0,0xAB,0x1C,0x55,0x13,0x0E,0xD0,0xA5,0x04, +0x56,0x3C,0x65,0x55,0x13,0x05,0x91,0x66,0x20,0x14,0x16,0xD0,0x8F,0x58,0x51,0x41, +0x30,0xAB,0x1C,0xD4,0xAB,0x20,0x31,0x7C,0x00,0x3C,0x8F,0x44,0x01,0x50,0x31,0x98, +0x01,0x3A,0x24,0x55,0x66,0x13,0x61,0xC3,0x50,0x55,0x57,0xD1,0x57,0x08,0x14,0xE9, +0xD5,0x57,0x13,0xE5,0x91,0x8F,0x49,0xA1,0x02,0x12,0xDE,0xC3,0x01,0x50,0x55,0x7D, +0x56,0x53,0x91,0x63,0x39,0x14,0x2B,0x91,0x83,0x30,0x19,0x26,0xF5,0x54,0xF3,0xD1, +0x57,0x01,0x14,0x14,0x83,0x30,0x66,0xCF,0xB3,0xFC,0x91,0xCF,0xAF,0xFC,0x08,0x18, +0x07,0xE2,0x04,0xAC,0x2C,0x1D,0x11,0x1B,0xD0,0x8F,0x8C,0x02,0x00,0x00,0x50,0x31, +0x47,0x01,0xDD,0x55,0x2C,0x57,0x66,0x20,0x08,0xAC,0x34,0xD0,0x8E,0x55,0xE2,0x03, +0xAC,0x2C,0x00,0xC0,0x57,0x56,0xD6,0x56,0xD1,0x55,0x08,0x14,0x8C,0x7C,0xAB,0x1C, +0x28,0x55,0x66,0xAB,0x1C,0x7D,0xAB,0x1C,0xCF,0x56,0xFC,0x95,0xCF,0x54,0xFC,0x12, +0x0B,0xD0,0x8F,0x41,0x30,0x00,0x00,0xCF,0x49,0xFC,0x11,0x0B,0x95,0xCF,0x44,0xFC, +0x12,0x05,0xD0,0x30,0xCF,0x3D,0xFC,0xD0,0xCF,0x3A,0xFC,0x50,0x83,0x30,0xCF,0x33, +0xFC,0x51,0x19,0x32,0x91,0x51,0x09,0x14,0x2D,0x9A,0x51,0x51,0x9A,0x04,0x52,0x95, +0x50,0x13,0x1B,0x82,0x30,0x50,0x19,0x1E,0x91,0x50,0x09,0x14,0x19,0x9A,0x50,0x53, +0xC4,0x0A,0x51,0xC0,0x53,0x51,0x9C,0x8F,0xF8,0x50,0x50,0xF5,0x52,0xE1,0x3C,0x51, +0x52,0xD1,0x51,0x52,0x13,0x08,0x3C,0x8F,0x48,0x08,0x50,0x31,0xBB,0x00,0xB0,0x52, +0xCF,0xEA,0xFB,0x9E,0xCF,0x61,0xFA,0x57,0xB4,0xAB,0x64,0xED,0x00,0x18,0xCF,0xE0, +0xFB,0x67,0x13,0x1B,0xB5,0xA7,0x08,0x13,0x07,0xB1,0xCF,0xD4,0xFB,0x67,0x13,0x0F, +0xC0,0x14,0x57,0xB5,0x67,0x12,0xE1,0x3C,0x8F,0x48,0x08,0x50,0x31,0x8A,0x00,0xB1, +0xCF,0xBA,0xFB,0xA7,0x04,0x1A,0xE9,0xB0,0xCF,0xB2,0xFB,0xA7,0x04,0xB0,0xCF,0xAC, +0xFB,0xAB,0x64,0x94,0xAB,0x67,0x90,0x0D,0x9F,0x30,0x00,0x14,0x20,0xD4,0xCF,0xA4, +0xFB,0x9E,0xCF,0x9F,0xFB,0x51,0x3C,0xAB,0x64,0x50,0xD4,0x54,0x3C,0x8F,0x10,0x27, +0x55,0xC7,0x55,0x50,0x53,0xC5,0x55,0x53,0x52,0xC0,0x52,0x54,0x13,0x04,0x81,0x30, +0x53,0x81,0xC2,0x52,0x50,0x18,0x03,0xC0,0x54,0x50,0xC6,0x0A,0x55,0xC3,0x01,0x55, +0x53,0x12,0xDE,0x81,0x30,0x50,0x81,0x9E,0xC9,0x00,0x0A,0x5A,0x90,0xA7,0x07,0xAB, +0x66,0x90,0x04,0xCF,0x9A,0xF9,0x16,0x47,0xB7,0x10,0xE9,0x50,0x03,0x31,0x1C,0x00, +0xBB,0x8F,0xFF,0x0F,0xD0,0xAB,0x34,0x52,0xD0,0xA2,0x2C,0x51,0x13,0x07,0xD0,0x5B, +0x59,0xFA,0x6C,0x41,0x62,0xBA,0x8F,0xFF,0x0F,0x30,0x2B,0x03,0xE0,0x08,0xAB,0x30, +0x16,0x91,0x8F,0x60,0xAB,0x66,0x13,0x0F,0x91,0x8F,0x63,0xAB,0x66,0x13,0x08,0x80, +0x05,0xAB,0x68,0x90,0x2E,0xAB,0x6E,0x90,0x0E,0x9F,0x30,0x00,0x14,0x20,0x7C,0x7E, +0x9F,0xCF,0x5F,0xFA,0xFB,0x03,0xCF,0x61,0x0D,0x83,0x8F,0x40,0xCF,0x04,0xFB,0xCB, +0x08,0x01,0x9E,0xC9,0x00,0x0A,0x5E,0x9E,0xC9,0x00,0x04,0x57,0x9A,0x8F,0xFF,0x54, +0xDE,0xCF,0x35,0x04,0x77,0xF4,0x54,0xF8,0x8A,0x0C,0x9F,0x00,0x04,0x14,0x20,0xE1, +0x09,0xAB,0x30,0x01,0x00,0x90,0x0F,0x9F,0x30,0x00,0x14,0x20,0x7C,0x7E,0x9F,0xCF, +0x29,0xFA,0xFB,0x03,0xCF,0x23,0x0D,0x17,0x65,0x9E,0xAF,0x74,0xCF,0x9D,0xFA,0xD4, +0x53,0xD0,0xAB,0x5C,0x54,0xD5,0x43,0xC4,0x00,0x08,0x19,0x64,0x78,0x09,0x53,0x51, +0xC8,0x8F,0x00,0x00,0x00,0x30,0x51,0xB1,0x61,0x18,0x12,0x54,0x30,0xF1,0x03,0xE9, +0x50,0x4E,0xDD,0x59,0xD0,0xA1,0x08,0x59,0x30,0x0D,0x04,0x78,0x09,0x59,0x56,0xD0, +0x8E,0x59,0xE9,0x50,0x4A,0x7C,0x7E,0x9F,0xCF,0xE7,0xF9,0xFB,0x03,0xCF,0xDA,0x0C, +0x7C,0x7E,0x9F,0xCF,0x7B,0xFA,0xFB,0x03,0xCF,0xCF,0x0C,0xD0,0x5A,0x53,0x3C,0x8F, +0x00,0xFE,0x58,0xD1,0x58,0x56,0x15,0x03,0xD0,0x56,0x58,0x28,0x58,0x61,0x63,0xC2, +0x58,0x56,0x14,0xEF,0xC1,0x5A,0xAA,0x10,0x55,0x3C,0x01,0x50,0x7C,0xAB,0x34,0x05, +0xF1,0x8F,0xFF,0x1F,0x00,0x00,0x20,0x53,0x8B,0xFF,0x3C,0x8F,0x18,0x80,0x50,0xD4, +0xCF,0x19,0xFA,0x05,0x30,0xA6,0x02,0x7C,0x7E,0x9F,0xCF,0x95,0xF9,0xFB,0x03,0xCF, +0x88,0x0C,0x7C,0x7E,0x9F,0xCF,0x29,0xFA,0xFB,0x03,0xCF,0x7D,0x0C,0xD4,0xAC,0x54, +0xBB,0x8F,0x00,0x07,0x90,0xA7,0x0B,0xCF,0xF6,0xF9,0xD0,0xAB,0x34,0x52,0xD0,0x5B, +0x59,0x16,0x42,0xB2,0x18,0x9A,0x02,0x59,0x10,0x05,0xBA,0x8F,0x00,0x07,0x05,0x90, +0x0E,0x9F,0x30,0x00,0x14,0x20,0xD0,0x5A,0x58,0x16,0xCF,0xD6,0x03,0xE8,0x50,0x06, +0x3C,0x8F,0x54,0x00,0x50,0x05,0x9A,0x01,0x50,0x05,0x30,0x50,0x02,0xDD,0x59,0xD0, +0xAB,0x34,0x52,0xD0,0x5B,0x59,0x16,0x42,0xB2,0x18,0xD0,0xA2,0x1C,0x51,0x13,0x07, +0xFA,0x6C,0x41,0x62,0xE9,0x50,0x25,0xBB,0x8F,0xC0,0x03,0x90,0x04,0xCF,0x00,0xF8, +0x7C,0x7E,0x9F,0xCF,0x1C,0xF9,0xFB,0x03,0xCF,0x0F,0x0C,0x7C,0x7E,0x9F,0xCF,0xB0, +0xF9,0xFB,0x03,0xCF,0x04,0x0C,0x10,0x08,0xBA,0x8F,0xC0,0x03,0xD0,0x8E,0x59,0x05, +0xE0,0x01,0xAC,0x2C,0x1E,0xE1,0x03,0xAB,0x30,0x03,0x31,0x8A,0x00,0x7D,0xCF,0x5F, +0xF9,0xCF,0x08,0xF9,0x12,0x0A,0x30,0xA8,0x29,0x7D,0xCF,0xFF,0xF8,0xCF,0x50,0xF9, +0x30,0xD2,0x29,0x9F,0xAB,0x6E,0xE1,0x08,0xAB,0x30,0x08,0x9E,0xAB,0x69,0x6E,0x94, +0xCF,0xAD,0xF7,0x9A,0xAB,0x68,0x7E,0xDE,0x7E,0x56,0xDD,0xAC,0x2C,0xD4,0x7E,0x7C, +0x7E,0xDF,0xAB,0x3C,0xDF,0x6A,0xDF,0xCA,0x00,0x02,0xDF,0xA6,0x04,0xDF,0x66,0xFB, +0x09,0xCF,0x45,0x2A,0xC0,0x0C,0x5E,0xE8,0x50,0x78,0xE0,0x01,0xAC,0x2C,0x34,0xB1, +0x50,0x8F,0x10,0x09,0x12,0x11,0x95,0xCF,0x76,0xF7,0x13,0x27,0x94,0xCF,0x70,0xF7, +0x91,0x30,0xCF,0x6F,0xF7,0x13,0xAC,0xB1,0x50,0x8F,0xC0,0x08,0x13,0x19,0xB1,0x50, +0x8F,0x10,0x08,0x13,0x12,0xB1,0x50,0x8F,0x28,0x08,0x13,0x0B,0xB1,0x50,0x8F,0x08, +0x08,0x13,0x04,0xC8,0x02,0x50,0x05,0xD4,0x58,0xD0,0x01,0x59,0xD0,0x5A,0x56,0x30, +0x85,0x00,0xE9,0x50,0xF1,0x3C,0x8F,0xC0,0x08,0x50,0x9A,0xAA,0x02,0x52,0x91,0xAA, +0x03,0x01,0x12,0xE2,0x3E,0x42,0x6A,0x51,0x30,0x35,0x02,0xE9,0x50,0xD5,0x9C,0x10, +0xAA,0x04,0x58,0xD0,0xA1,0x08,0x59,0xC0,0xA1,0x0C,0x5A,0xC1,0x5A,0xA1,0x10,0x55, +0x11,0x47,0xD0,0x5A,0x55,0x7D,0xAB,0x3C,0x58,0xE0,0x01,0xAC,0x2C,0x0A,0xD5,0x58, +0x12,0x06,0x3C,0x8F,0xAC,0x02,0x50,0x05,0xE1,0x06,0xAB,0x30,0x2C,0xD0,0x5A,0x56, +0xD0,0x01,0x59,0x10,0x32,0xE9,0x50,0x7E,0x7D,0xAB,0x3C,0x58,0x7D,0x59,0x52,0x30, +0x4E,0x29,0xD0,0x51,0xCB,0xA0,0x00,0xC3,0x51,0x52,0x59,0xC0,0x51,0x58,0x3C,0xAA, +0x02,0x51,0xC0,0x5A,0x51,0x9E,0x4A,0x61,0x55,0x7D,0xAB,0x44,0xAC,0x14,0xD0,0x5A, +0x56,0x30,0xF4,0x01,0xE9,0x50,0x4F,0x90,0x0E,0x9F,0x30,0x00,0x14,0x20,0x9A,0x8F, +0x7F,0x57,0xD1,0x57,0x59,0x15,0x03,0xD0,0x59,0x57,0xDD,0x5B,0xDD,0x00,0x3C,0x21, +0x7E,0xDD,0x58,0x9C,0x09,0x57,0x7E,0xDD,0x56,0xC0,0xAE,0x04,0x56,0xC0,0x57,0x58, +0xFB,0x06,0xCF,0x6D,0x0B,0xE9,0x50,0x1E,0xC2,0x57,0x59,0x14,0xCA,0xE1,0x01,0xAC, +0x2C,0x14,0xDD,0x00,0xDD,0x5B,0xDD,0x00,0x3C,0x24,0x7E,0xDD,0x00,0xDD,0x00,0xDD, +0x00,0xFB,0x07,0xCF,0xB0,0x32,0x05,0x00,0x02,0x00,0x00,0x00,0xD5,0xCF,0xFC,0xF7, +0x13,0x0D,0xD0,0xAE,0x04,0x50,0xC0,0x8E,0x5E,0xD0,0xCF,0xEF,0xF7,0x6E,0x02,0x3C, +0x8F,0x08,0x80,0x50,0x30,0x00,0x00,0xBB,0x8F,0xFF,0x0F,0x9E,0xCF,0x35,0xF6,0x5B, +0xAA,0x8F,0xFF,0x01,0x5B,0x9E,0xCB,0x00,0xFE,0x5B,0xD4,0xCF,0xCE,0xF7,0x9E,0xCF, +0xF4,0xF6,0x51,0xCA,0x03,0x50,0xB5,0x61,0x13,0x2F,0xB1,0x81,0x50,0x13,0x04,0xD6, +0x51,0x11,0xF3,0x7C,0x7E,0x9A,0x8F,0x8C,0x7E,0xFB,0x03,0xCF,0x2C,0x0A,0x7C,0x7E, +0x9A,0x61,0x7E,0xFB,0x03,0xCF,0x22,0x0A,0x95,0xCF,0xC5,0xF7,0x13,0x0B,0x7C,0x7E, +0x9F,0xCF,0xBB,0xF7,0xFB,0x03,0xCF,0x11,0x0A,0xBA,0x8F,0xFF,0x0F,0x00,0xD0,0x6B, +0x5E,0x9E,0xCE,0x00,0x02,0x5E,0x7D,0xAB,0x1C,0x50,0x7D,0xAB,0x24,0x52,0x7D,0xAB, +0x2C,0x54,0xD0,0xAB,0x18,0x5C,0x7D,0xAB,0x10,0x5A,0x31,0xC7,0xF5,0xD0,0xA7,0x0C, +0x51,0xE1,0x13,0x51,0x09,0xB0,0x8F,0x81,0x00,0xCB,0xA1,0x00,0x11,0x2E,0xB0,0x28, +0xCB,0xA1,0x00,0x83,0x8F,0x41,0xCF,0x7A,0xF7,0x50,0x13,0x05,0x10,0x23,0xE9,0x50, +0x12,0x9E,0xAF,0x07,0xCF,0x45,0xF7,0xD4,0x50,0xB5,0x61,0xD4,0xCF,0x3D,0xF7,0xD5, +0x50,0x13,0x09,0x3C,0x8F,0x48,0x08,0x50,0xD0,0x8E,0x51,0x05,0xD0,0x51,0xAB,0x54, +0x05,0xBB,0x3C,0x9A,0x50,0x54,0x9A,0xA7,0x09,0x53,0x12,0x4A,0x9A,0xA7,0x08,0x55, +0xD7,0x55,0x19,0x5E,0xD1,0x54,0x07,0x1A,0x59,0x9E,0xAF,0x5A,0xCF,0x0D,0xF7,0x9E, +0xCF,0x54,0xF7,0x52,0xD0,0x8F,0x08,0x00,0x00,0x20,0x51,0xB5,0x61,0xD5,0x55,0x13, +0x11,0x9A,0x62,0x53,0x13,0x3C,0xC1,0x01,0x53,0x50,0xC0,0x50,0x51,0xCA,0x53,0x51, +0x11,0xE9,0xD6,0x53,0x9E,0xAF,0x28,0xCF,0xE2,0xF6,0x11,0x05,0xC0,0x53,0x51,0xB5, +0x61,0xF5,0x54,0xF8,0x11,0x12,0x91,0x54,0xA7,0x0A,0x15,0x06,0xC2,0xA7,0x0A,0x54, +0x11,0xAA,0xC4,0x53,0x54,0xC0,0x54,0x51,0xD4,0xCF,0xC0,0xF6,0x9A,0x01,0x50,0xBA, +0x3C,0x05,0xD4,0x50,0x11,0xF9,0xD7,0x55,0x19,0xF8,0xD6,0x52,0x9A,0x62,0x53,0x13, +0xF1,0xC1,0x01,0x53,0x50,0xC0,0x50,0x51,0xCA,0x53,0x51,0x11,0x9E,0x00,0x00,0x00, +0x3C,0x8F,0x08,0x80,0x50,0x30,0xAF,0xFE,0x3C,0x8F,0x10,0x80,0x50,0x30,0xA7,0xFE, +0xD4,0x50,0xB1,0x61,0x18,0x12,0x20,0x81,0x18,0xA1,0x02,0x52,0x92,0x52,0x52,0x91, +0x52,0xA1,0x03,0x12,0x12,0xC1,0xA1,0x08,0xA1,0x0C,0x52,0xC0,0xA1,0x10,0x52,0xD1, +0x52,0xA1,0x14,0x12,0x02,0xD6,0x50,0x05,0x7D,0x51,0x7E,0x3C,0x8F,0x01,0x06,0x50, +0x9C,0x17,0x5B,0x52,0xDE,0xA2,0x7F,0x52,0x9C,0x17,0x5A,0x51,0xC0,0x59,0x51,0x11, +0x05,0xE1,0x52,0xBC,0x18,0x07,0xF2,0x51,0x52,0xF7,0xD0,0x01,0x50,0x7D,0x8E,0x51, +0x05,0x00,0x00,0x00,0xD0,0xCF,0x34,0xF6,0x6E,0x13,0x95,0xD0,0x8F,0x81,0x00,0x00, +0x00,0x50,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78, +0x56,0x34,0x12,0xBB,0x1E,0x30,0x86,0x07,0xE9,0x50,0x45,0x90,0x59,0xAF,0xEE,0xAA, +0x8F,0x00,0x20,0xAF,0xE5,0x7D,0x8F,0xAB,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0xAF, +0xC3,0x9A,0x08,0x51,0xC8,0x08,0x54,0x30,0x95,0x00,0xE9,0x50,0xE2,0xA8,0x8F,0x00, +0x20,0xAF,0xC7,0xE0,0x0E,0x50,0x08,0xAA,0x8F,0x00,0x40,0xAF,0xBD,0x11,0x06,0xA8, +0x8F,0x00,0x40,0xAF,0xB5,0x7D,0xAF,0x94,0xAF,0x9A,0x30,0x09,0x00,0xE9,0x50,0xBF, +0x30,0xE5,0x07,0xBA,0x1E,0x05,0x94,0xAF,0xA3,0xD0,0x8F,0x00,0x87,0x93,0x03,0xAF, +0x8F,0x9A,0x08,0x51,0xC8,0x04,0x54,0x30,0xAF,0x00,0xE9,0x50,0x51,0x91,0xAF,0x8C, +0xA2,0x01,0x12,0x48,0x30,0xD1,0x04,0xE9,0x50,0x44,0x96,0xCF,0x7E,0xFF,0x9A,0x0A, +0x51,0xC8,0x8F,0x05,0x00,0x10,0x00,0x54,0x30,0x8E,0x00,0xE9,0x50,0x30,0x83,0xA2, +0x01,0xCF,0x68,0xFF,0x51,0x13,0x07,0x91,0x51,0x01,0x13,0xE2,0x11,0x1E,0xE0,0x02, +0x54,0xD2,0x30,0xCF,0x04,0xE9,0x50,0x16,0xD4,0x54,0x96,0xCF,0x4E,0xFF,0x9A,0x0A, +0x51,0x30,0x65,0x00,0xE9,0x50,0x07,0x9A,0x01,0x50,0x11,0x02,0xD4,0x50,0x05,0xD0, +0x8F,0x00,0x12,0x7A,0x00,0xCF,0x28,0xFF,0xA8,0x8F,0x00,0x40,0xCF,0x2B,0xFF,0xD0, +0x54,0x7E,0x30,0x44,0x00,0xE8,0x50,0x3E,0xD0,0x8E,0x54,0xE1,0x0E,0xCF,0x1A,0xFF, +0x09,0xAA,0x8F,0x00,0x40,0xCF,0x12,0xFF,0x11,0xE5,0x7C,0x7E,0x9A,0x8F,0x8C,0x7E, +0xFB,0x03,0xCF,0x85,0x07,0x7C,0x7E,0x9A,0x8F,0xA1,0x7E,0xFB,0x03,0xCF,0x7A,0x07, +0xD1,0x8F,0x00,0x1C,0x4E,0x0E,0xCF,0xE7,0xFE,0x1F,0xBD,0xC0,0x8F,0x00,0x09,0x3D, +0x00,0xCF,0xDC,0xFE,0x11,0xB2,0xD5,0x8E,0x05,0x7D,0xCF,0xC7,0xFE,0xCF,0xBC,0xFE, +0xAA,0x8F,0x00,0x40,0x51,0xE1,0x0E,0xCF,0xD0,0xFE,0x05,0xA8,0x8F,0x00,0x40,0x51, +0x30,0x0B,0x00,0xE9,0x50,0x07,0xD5,0x54,0x13,0x03,0x30,0x8A,0x00,0x05,0x7D,0x51, +0x7E,0x9E,0xAA,0x10,0x53,0x30,0xEF,0x01,0x9E,0xCF,0x20,0x00,0x7E,0x8B,0x8F,0xF0, +0x51,0x50,0x8F,0x50,0x07,0x0F,0xA9,0x02,0x5F,0x02,0x12,0x00,0x9E,0x02,0x05,0x03, +0x12,0x00,0xC7,0x03,0xD5,0x03,0xE0,0x03,0xD4,0x50,0x11,0x57,0x9E,0xAA,0x10,0x50, +0xC2,0x50,0x53,0x9E,0xCF,0x8A,0xF4,0x50,0xE0,0x0E,0x51,0x07,0xB0,0xCF,0x78,0xFE, +0x52,0x11,0x03,0x3C,0x53,0x52,0x90,0x52,0xA0,0x03,0x9C,0x8F,0xF8,0x52,0x52,0x90, +0x52,0xA0,0x02,0x9C,0x08,0x52,0x52,0xD0,0xCF,0x41,0xFE,0xA0,0x04,0xB0,0xCF,0x3F, +0xFE,0xA0,0x08,0x9A,0x04,0x7E,0xDD,0x5B,0xDD,0x00,0x3C,0x20,0x7E,0x9F,0xCF,0x50, +0xF4,0xDD,0x53,0x9F,0xAA,0x10,0xFB,0x06,0xCF,0x87,0x07,0xE8,0x50,0x03,0xF5,0x6E, +0xE5,0xD5,0x8E,0x7D,0x8E,0x51,0x05,0xD0,0x51,0x7E,0x30,0xBC,0x04,0x9E,0xCF,0x30, +0xF4,0x50,0xB4,0x60,0xDD,0x5B,0xDD,0x00,0x3C,0x21,0x7E,0x9F,0xCF,0x22,0xF4,0x3C, +0x8F,0x08,0x04,0x7E,0x9F,0xCA,0x10,0x02,0xFB,0x06,0xCF,0x55,0x07,0xE9,0x50,0x06, +0x30,0x0D,0x00,0xE8,0x50,0x06,0x30,0xAE,0x04,0xE8,0x50,0xD1,0xD0,0x8E,0x51,0x05, +0xD0,0x51,0x7E,0x9E,0xCA,0x10,0x02,0x52,0x9E,0xCF,0xF5,0xF3,0x51,0x7D,0xA1,0x04, +0xCF,0xC9,0xFD,0x9A,0xA1,0x02,0x53,0x9C,0x08,0x53,0x53,0x90,0xA1,0x03,0x53,0xB1, +0x8F,0xDC,0x05,0x53,0x1F,0x48,0x9C,0x10,0x53,0x50,0xA8,0x8F,0x00,0x40,0x50,0xB1, +0x8F,0xAA,0xAA,0x82,0x12,0x15,0xD1,0x8F,0x03,0x08,0x00,0x2B,0x82,0x12,0x54,0x9A, +0x82,0x53,0x9C,0x08,0x53,0x53,0x90,0x82,0x53,0x11,0x23,0x90,0x62,0x53,0x8A,0x10, +0x53,0x91,0x8F,0xAF,0x53,0x12,0x05,0x9A,0x1E,0x51,0x11,0x09,0x91,0x8F,0xE3,0x53, +0x12,0x31,0x9A,0x1F,0x51,0xE8,0xA2,0xFF,0x2A,0xB5,0x72,0x31,0xB7,0x00,0xB1,0x8F, +0x00,0x90,0x53,0x13,0x30,0xE0,0x0E,0x50,0x07,0x3C,0x82,0x50,0x9C,0x10,0x50,0x50, +0x91,0x1F,0x62,0x1F,0x0E,0xB1,0x8F,0x02,0x60,0x53,0x13,0x54,0xB1,0x8F,0x01,0x60, +0x53,0x13,0x7F,0xD0,0xCF,0x55,0xFD,0x51,0xC0,0x01,0xC1,0xA0,0x00,0xD8,0x00,0xC1, +0xA4,0x00,0x31,0x8C,0x00,0xE0,0x0E,0x50,0x0C,0x9C,0x8F,0xF0,0x50,0x50,0xC2,0x0E, +0x50,0x9C,0x10,0x50,0x50,0x3C,0x62,0x51,0xA1,0x08,0x51,0x62,0x9E,0x41,0xA2,0x02, +0x51,0xB1,0x02,0x81,0x12,0x6B,0xE8,0x61,0x68,0x7D,0x61,0xCF,0x0E,0xFD,0x9E,0xCF, +0x2F,0xF3,0x51,0xB0,0x8F,0x00,0x90,0xA1,0x02,0xB4,0x61,0x9A,0x1D,0x51,0x11,0x45, +0x91,0x05,0x62,0x12,0x0A,0x9A,0x07,0x51,0xA8,0x8F,0x00,0x80,0x51,0x11,0x36,0x91, +0x09,0x62,0x12,0x05,0x9A,0x0B,0x51,0x11,0x2C,0x91,0x06,0x62,0x12,0x95,0xE0,0x0D, +0xCF,0xF7,0xFC,0x2D,0xD0,0x8F,0x00,0x12,0x7A,0x00,0xCF,0xE3,0xFC,0x30,0x79,0x03, +0x11,0x1F,0x9A,0x62,0x53,0xE0,0x53,0x54,0x03,0x31,0x77,0xFF,0x9C,0x53,0x01,0x54, +0x90,0x01,0x50,0x11,0x0E,0xE1,0x0E,0x50,0x05,0xA8,0x8F,0x00,0x40,0x51,0x30,0x0D, +0xFE,0xD4,0x50,0xD0,0x8E,0x51,0x05,0xD0,0x50,0x7E,0x9A,0x51,0x50,0xE1,0x50,0xCF, +0x63,0x00,0x07,0xB0,0x8F,0x01,0x60,0xCF,0xAE,0xFC,0xE1,0x50,0xCF,0x5A,0x00,0x07, +0xB0,0x8F,0x02,0x60,0xCF,0xA1,0xFC,0xE1,0x50,0xCF,0x51,0x00,0x07,0xB0,0x8F,0x00, +0x90,0xCF,0x94,0xFC,0x9E,0xCF,0x99,0xF2,0x50,0xB4,0x60,0xE0,0x0E,0x51,0x0A,0x91, +0x1D,0x51,0x13,0x2D,0x9B,0x01,0x60,0x11,0x28,0x91,0x1E,0x51,0x14,0x0D,0x90,0xA2, +0x01,0x83,0x90,0x01,0x83,0x90,0xA2,0x02,0x83,0x11,0x16,0xB0,0x8F,0xAA,0xAA,0x83, +0xD0,0x8F,0x03,0x08,0x00,0x2B,0x83,0x90,0xCF,0x5E,0xFC,0x83,0x90,0xCF,0x58,0xFC, +0x83,0xD0,0x8E,0x50,0x05,0x1F,0x55,0x10,0x00,0xE0,0xAA,0x0A,0x00,0x00,0x00,0x00, +0x35,0x00,0x00,0x00,0xC0,0x90,0x08,0x83,0x90,0xCF,0x42,0xFC,0x83,0x90,0x04,0x83, +0xE0,0x0E,0x51,0x04,0x90,0x01,0xA3,0xFF,0x9B,0xCF,0x31,0xFC,0x83,0xE1,0x04,0xAB, +0x30,0x05,0x90,0x8F,0xFE,0xA3,0xFF,0xE1,0x08,0xAB,0x30,0x12,0x90,0xAB,0x68,0xA3, +0xFF,0xBB,0x37,0x9B,0xAB,0x68,0x50,0x28,0x50,0xAB,0x69,0x63,0xBA,0x37,0x94,0x83, +0x30,0x1C,0x00,0x05,0x90,0x0A,0x83,0x90,0xCF,0x01,0xFC,0x83,0x94,0x83,0x05,0x90, +0x07,0x83,0x94,0x83,0xB4,0x83,0xE1,0x0F,0x51,0x05,0xB0,0xA2,0x02,0xA3,0xFE,0xB0, +0x01,0x83,0x90,0x03,0x83,0x90,0x04,0x83,0xB4,0x83,0xB0,0x02,0x83,0x90,0x02,0x83, +0xB0,0x8F,0x59,0x00,0x83,0xB0,0x07,0x83,0x90,0x06,0x83,0xD0,0xCF,0xA5,0xFB,0x83, +0xB0,0xCF,0xA4,0xFB,0x83,0xB0,0x8F,0x64,0x00,0x83,0x90,0x01,0x83,0x90,0xCF,0xBD, +0xFB,0x83,0xB0,0x8F,0x90,0x01,0x83,0x90,0x01,0x83,0x90,0x01,0x83,0xB0,0x8F,0x91, +0x01,0x83,0x90,0x02,0x83,0xB0,0x8F,0x06,0x04,0x83,0x05,0xBB,0x37,0xD0,0x51,0x50, +0xD0,0xCF,0x88,0xFB,0x51,0x90,0x0B,0x83,0xB0,0xA2,0x01,0x83,0xE1,0x0E,0x50,0x09, +0x28,0x8F,0xC8,0x00,0x61,0x63,0x31,0xA1,0x00,0xB5,0xA1,0x02,0x12,0x0F,0xD5,0xA1, +0x04,0x12,0x0A,0xD5,0xA1,0x08,0x12,0x05,0xD5,0xA1,0x0C,0x13,0x05,0xB2,0x00,0x83, +0x11,0x03,0xB0,0x61,0x83,0xD0,0x51,0x52,0xD0,0x09,0x54,0xD5,0xA2,0x14,0x13,0x05, +0xD2,0x00,0x83,0x11,0x04,0xD0,0xA2,0x10,0x83,0xC0,0x08,0x52,0xF5,0x54,0xEC,0xC0, +0x10,0x52,0xD0,0x06,0x54,0xD0,0x01,0x55,0x7C,0xC1,0xC8,0x00,0xB4,0x50,0x7D,0x62, +0x62,0x13,0x03,0xA8,0x55,0x50,0xC0,0x82,0xC1,0xC8,0x00,0xD8,0x82,0xC1,0xCC,0x00, +0x9C,0x01,0x55,0x55,0xF5,0x54,0xE7,0xB5,0xC1,0xCA,0x00,0x12,0x06,0xD5,0xC1,0xCC, +0x00,0x13,0x05,0xB2,0x00,0x83,0x11,0x05,0xB0,0xC1,0xC8,0x00,0x83,0xB0,0x50,0x83, +0xD0,0x03,0x54,0xD1,0x08,0x55,0x12,0xBD,0xD0,0x51,0x52,0xD0,0x04,0x54,0xB5,0xC2, +0xA2,0x00,0x12,0x06,0xD5,0xC2,0xA4,0x00,0x13,0x05,0xB2,0x00,0x83,0x11,0x05,0xB0, +0xC2,0xA0,0x00,0x83,0xC0,0x08,0x52,0xF5,0x54,0xE4,0xBA,0x37,0x05,0xBB,0x37,0x78, +0x8F,0xF0,0x50,0x51,0x28,0x51,0x62,0x63,0xBA,0x37,0x05,0x90,0x8F,0x81,0x83,0x90, +0x01,0x83,0x90,0x00,0x83,0x05,0xBB,0x37,0x78,0x8F,0xF0,0x50,0x51,0xC2,0x03,0x51, +0x28,0x51,0xA2,0x03,0x63,0xBA,0x37,0x05,0xBB,0x3E,0xB4,0x50,0x78,0x8F,0xF0,0x50, +0x51,0xC2,0x06,0x51,0x15,0x19,0x9E,0xCA,0x00,0x08,0x53,0xC0,0xA2,0x02,0x53,0x30, +0xD3,0x00,0xE9,0x50,0x0A,0x28,0x51,0xA2,0x06,0x63,0x9A,0x01,0x50,0x11,0x02,0xD4, +0x50,0xBA,0x3E,0x05,0xBB,0x0E,0x7C,0xAC,0x34,0x7C,0xAC,0x24,0x7C,0xAC,0x4C,0xD4, +0x59,0xB4,0x50,0x9C,0x8F,0xF0,0x50,0x53,0x9E,0xA2,0x02,0x52,0xC2,0x02,0x53,0x15, +0x20,0x9A,0x82,0x50,0x9A,0x82,0x51,0x8F,0x50,0x00,0x06,0x1D,0x00,0x27,0x00,0x42, +0x00,0x52,0x00,0x5A,0x00,0x62,0x00,0x62,0x00,0xC0,0x51,0x52,0xC2,0x51,0x53,0x14, +0xDB,0x31,0x51,0x00,0xD4,0x50,0x11,0x58,0xD0,0x03,0x51,0xD0,0xA2,0xFF,0x59,0x31, +0x43,0x00,0x9E,0xAC,0x34,0x50,0x10,0x5C,0xB0,0x51,0xAB,0x68,0xD1,0x51,0x08,0x1B, +0x04,0xB0,0x08,0xAB,0x68,0x9E,0xAB,0x6A,0x50,0x10,0x49,0x11,0xCC,0x9E,0xAC,0x24, +0x50,0x10,0x30,0x9E,0xAB,0x28,0x50,0x7C,0x60,0x10,0x28,0x11,0xBC,0x9E,0xAC,0x44, +0x50,0x10,0x31,0x11,0xB4,0x9E,0xAC,0x3C,0x50,0x10,0x18,0x11,0xAC,0x9E,0xAC,0x4C, +0x50,0x10,0x21,0x11,0xA4,0x9E,0xCA,0x00,0x08,0x55,0xC0,0x59,0x55,0x9A,0x01,0x50, +0xBA,0x0E,0x05,0xD1,0x51,0x02,0x12,0x0C,0xD0,0x8F,0xAA,0x00,0x04,0x00,0x60,0x3C, +0x62,0xA0,0x04,0x05,0xBB,0x3F,0xD1,0x51,0x08,0x1B,0x03,0x9A,0x08,0x51,0x28,0x51, +0x62,0x60,0xBA,0x3F,0x05,0xBB,0x32,0xD1,0x53,0x58,0x1F,0x04,0xC1,0x53,0x51,0x58, +0x78,0x8F,0xF7,0x53,0x55,0xC0,0x8F,0xFF,0x01,0x00,0x00,0x51,0x78,0x8F,0xF7,0x51, +0x51,0xC7,0x08,0x55,0x54,0xCA,0x8F,0xF8,0xFF,0xFF,0xFF,0x55,0x3C,0x8F,0x01,0x06, +0x50,0xB0,0x54,0x7E,0xA1,0x51,0x55,0x7E,0xA6,0x08,0x6E,0xA0,0x8E,0x6E,0xB1,0x8E, +0xAB,0x44,0x14,0x12,0xC0,0xAB,0x48,0x54,0xEC,0x55,0x51,0x64,0x8F,0xFF,0xFF,0xFF, +0xFF,0x12,0x03,0x9A,0x01,0x50,0xBA,0x32,0x05,0xD0,0x51,0x7E,0xD0,0x8F,0x00,0x00, +0x14,0x20,0x51,0xD4,0xC1,0x00,0x01,0xD2,0xCF,0x55,0xF9,0xC1,0x08,0x01,0xD0,0x15, +0xC1,0x00,0x01,0xD0,0x8E,0x51,0x05,0xD0,0x51,0x7E,0xD0,0x8F,0x00,0x00,0x14,0x20, +0x51,0xD0,0xA1,0x6C,0x50,0xD0,0xCF,0x33,0xF9,0x51,0x11,0x18,0xC0,0x01,0x61,0xD8, +0x00,0xA1,0x04,0xD8,0x00,0xA1,0x08,0xD8,0x00,0xA1,0x0C,0xC0,0x8F,0x64,0x00,0x00, +0x00,0xCF,0x20,0xF9,0xD1,0x50,0xCF,0x1B,0xF9,0x1E,0xE1,0xD0,0x8F,0x00,0x00,0x14, +0x20,0x51,0xE1,0x07,0xC1,0x10,0x01,0x0C,0xC8,0x8F,0x80,0x00,0x00,0x00,0xC1,0x10, +0x01,0x30,0x1A,0x00,0xE1,0x07,0xC1,0x00,0x01,0x0D,0xC8,0x8F,0x80,0x00,0x00,0x00, +0xC1,0x00,0x01,0xD4,0x50,0x11,0x03,0x9A,0x01,0x50,0xD0,0x8E,0x51,0x05,0x7D,0x50, +0x7E,0x7D,0x8F,0xAB,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0xCF,0xBE,0xF8,0x9A,0x07, +0x51,0xA8,0x8F,0x00,0x40,0x51,0x30,0x15,0xFA,0xAA,0x8F,0x00,0x40,0x51,0x30,0x0D, +0xFA,0x7D,0x52,0x7E,0xD0,0x8F,0x00,0x00,0x14,0x20,0x51,0xD0,0xC1,0x14,0x01,0x51, +0xCB,0x8F,0xE0,0xFF,0xFF,0xFF,0x51,0x50,0x7D,0xCF,0x88,0xF8,0x52,0xC2,0x51,0x52, +0x9C,0x50,0x52,0x52,0xC0,0x51,0x53,0x9C,0x50,0x53,0x53,0xCA,0x8F,0x00,0x00,0x00, +0xF8,0x53,0x7B,0x8F,0x00,0x1C,0x4E,0x0E,0x52,0x51,0x50,0xC0,0x8F,0x00,0x38,0x9C, +0x1C,0x50,0x7D,0x8E,0x52,0xD0,0x8F,0x00,0x00,0x14,0x20,0x51,0xD4,0xC1,0x10,0x01, +0xD2,0x50,0xC1,0x18,0x01,0xD0,0x11,0xC1,0x10,0x01,0x7D,0x8E,0x50,0x05,0xBB,0x3E, +0xC8,0x20,0xAC,0x2C,0xB4,0xCF,0x62,0xF8,0xD0,0x8F,0x00,0x00,0x14,0x20,0x51,0xD4, +0xC1,0x10,0x01,0xD2,0x8F,0x00,0x38,0x9C,0x1C,0xC1,0x18,0x01,0xD0,0x11,0xC1,0x10, +0x01,0xD5,0xA1,0x6C,0x13,0x09,0xA8,0x8F,0x00,0x10,0xCF,0x3D,0xF8,0x11,0x04,0x9A, +0x01,0xA1,0x6C,0xD0,0xA1,0x6C,0xCF,0x2B,0xF8,0xC0,0x8F,0x64,0x00,0x00,0x00,0xCF, +0x22,0xF8,0xD0,0xAB,0x34,0x52,0xD0,0xA2,0x1C,0x51,0x13,0x12,0xBB,0x8F,0x00,0x02, +0xD0,0x5B,0x59,0xFA,0x6C,0x41,0x62,0xBA,0x8F,0x00,0x02,0xE8,0x50,0x04,0xD4,0x50, +0x11,0x43,0x90,0xCF,0x0A,0xEE,0xCF,0x05,0xF8,0x91,0x25,0xCF,0x00,0xF8,0x12,0x0A, +0xD5,0xAC,0x48,0x12,0x05,0x90,0x05,0xCF,0xF4,0xF7,0x7D,0xAC,0x3C,0xCF,0xC4,0xF7, +0xD0,0xAC,0x44,0xCF,0xD6,0xF7,0x2C,0x00,0x8F,0x00,0x00,0x8F,0x08,0x04,0xCA,0x10, +0x02,0xD0,0xCF,0xC7,0xF7,0x51,0x2C,0x00,0x8F,0x00,0x00,0x8F,0xC8,0x00,0x61,0x30, +0xDC,0xFE,0x9A,0x01,0x50,0xBA,0x3E,0x05,0xBB,0x3F,0xD0,0x8F,0x00,0x00,0x14,0x20, +0x51,0xD4,0xC1,0x00,0x01,0xD4,0xC1,0x08,0x01,0xD4,0xC1,0x10,0x01,0xD4,0xC1,0x18, +0x01,0xE0,0x0C,0xCF,0xA4,0xF7,0x03,0xD4,0xA1,0x6C,0xD0,0xAB,0x34,0x52,0xD0,0xA2, +0x2C,0x51,0x13,0x0F,0xBB,0x8F,0x00,0x02,0xD0,0x5B,0x59,0xFA,0x6C,0x41,0x62,0xBA, +0x8F,0x00,0x02,0xCA,0x20,0xAC,0x2C,0xBA,0x3F,0x05,0x3C,0x00,0xDD,0x51,0x7C,0x7E, +0xD0,0xAC,0x04,0x50,0xD1,0x50,0x8F,0xFF,0x00,0x00,0x00,0x1B,0x15,0xD0,0x50,0x51, +0xD0,0x5E,0x50,0xD0,0x51,0xA0,0x04,0x95,0x81,0x12,0xFC,0xD7,0x51,0xC3,0xA0,0x04, +0x51,0x60,0x95,0xAC,0x08,0x12,0x08,0x16,0x9F,0x0C,0x00,0x06,0x20,0x11,0x31,0xD4, +0x51,0x16,0x9F,0x10,0x00,0x06,0x20,0xD0,0x51,0x53,0xD0,0x50,0x52,0xE1,0x06,0x63, +0x03,0x8A,0x20,0x63,0xD6,0x53,0xF5,0x52,0xF4,0x9A,0xAC,0x08,0x52,0xD1,0x50,0x52, +0x1B,0x03,0xD0,0x52,0x50,0xD0,0xAC,0x0C,0x52,0x90,0x50,0x82,0x28,0x50,0x61,0x62, +0x3C,0x01,0x50,0xC0,0x08,0x5E,0xD0,0x8E,0x51,0x04,0xBB,0x1E,0xD4,0x50,0x16,0x9F, +0x08,0x00,0x06,0x20,0x8A,0x8F,0x80,0x50,0xBA,0x1E,0x05,0x00,0x46,0x00,0x00,0x00, +0xF5,0x00,0x00,0x00,0xF0,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x0D,0x00,0xF2,0xFF, +0x53,0x14,0x00,0x00,0x02,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x80,0x01,0x01,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00, +0x00,0x00,0xFC,0x0F,0xD0,0xAC,0x18,0x59,0xDB,0x38,0x51,0xD0,0x41,0xA9,0x5C,0x53, +0xD0,0xAC,0x04,0x5A,0x3C,0xAC,0x08,0x58,0x12,0x04,0x9C,0x10,0x01,0x58,0xEF,0x00, +0x09,0x5A,0x57,0x9E,0x47,0xC8,0xFF,0x03,0x57,0x78,0x8F,0xF7,0x57,0x57,0xD0,0xAC, +0x0C,0x5B,0xD0,0xA9,0x50,0x51,0xE0,0x1F,0x5A,0x03,0xDB,0x08,0x51,0xEF,0x09,0x15, +0x5A,0x52,0x91,0xA9,0x66,0x20,0x1F,0x0C,0x91,0xA9,0x66,0x8F,0x60,0x1F,0x02,0x11, +0x03,0x31,0x49,0x00,0xC1,0x02,0xCF,0x87,0xFF,0x54,0xDE,0x44,0xC3,0x00,0x08,0x54, +0x9E,0x82,0x55,0xE9,0xAC,0x14,0x0B,0xD0,0x45,0x61,0x55,0xCA,0x8F,0x00,0x00,0xE0, +0xFF,0x55,0xC9,0x55,0xCF,0x79,0xFF,0x84,0xD7,0x57,0x14,0xE4,0xEF,0x00,0x09,0x5A, +0x5A,0xCA,0x8F,0x00,0x00,0x00,0x80,0x74,0xC1,0x02,0xCF,0x53,0xFF,0x50,0x9C,0x09, +0x50,0x50,0xC8,0x50,0x5A,0xDB,0x38,0x50,0xD0,0x40,0xA9,0x54,0x57,0xDD,0x0A,0xD0, +0x5B,0x55,0xD0,0xA9,0x34,0x50,0x16,0x40,0xB0,0x08,0xE8,0x50,0x03,0xF5,0x6E,0xEF, +0x04,0xFC,0x00,0xD0,0xAC,0x0C,0x57,0xD0,0xAC,0x04,0x52,0xD0,0x52,0xA7,0x50,0xD0, +0xAC,0x08,0x53,0xD0,0x53,0xA7,0x60,0xEF,0x09,0x15,0xA7,0x5C,0x54,0xD0,0x08,0x55, +0xEF,0x09,0x15,0x53,0x50,0xDE,0x40,0x62,0x51,0x10,0x20,0xD0,0x10,0x55,0xCB,0x8F, +0xFF,0x1F,0x00,0x00,0xA7,0x54,0x54,0x9C,0x17,0x54,0x54,0x10,0x0E,0x3C,0xA7,0x54, +0x50,0x9E,0x43,0xE0,0x00,0x30,0xFF,0xFF,0xA7,0x58,0x04,0xC9,0x8F,0x00,0x00,0x00, +0x90,0x54,0x81,0xD6,0x54,0xF5,0x55,0xF3,0x05,0x00,0x00,0x00,0x0C,0x44,0x4C,0x44, +0x52,0x49,0x56,0x45,0x52,0x2E,0x45,0x58,0x45,0x00,0x00,0x16,0xEF,0x30,0x01,0x00, +0x00,0xB4,0xA9,0x1E,0xD0,0x01,0x50,0x04,0xD4,0x50,0xF0,0xA9,0x64,0x08,0x02,0x50, +0xB0,0x0B,0xA7,0x04,0xA9,0x04,0x50,0x67,0x30,0x0E,0x01,0x3C,0xA7,0x06,0x56,0xED, +0x00,0x05,0x56,0x1D,0x12,0xEE,0xB3,0x01,0x67,0x13,0xE9,0xA9,0x08,0x50,0x67,0x30, +0xF7,0x00,0x18,0x03,0x31,0xD9,0x00,0xAB,0x3F,0xA7,0x06,0x51,0xC4,0x02,0x55,0xD4, +0x56,0x7B,0x8F,0x50,0x00,0x00,0x00,0x55,0x56,0x54,0xD4,0x55,0x7B,0x28,0x54,0x55, +0x54,0x78,0x07,0x56,0x56,0xF0,0x55,0x06,0x01,0x56,0xB1,0x56,0x51,0x13,0x2E,0xAA, +0x8F,0x7F,0x00,0x51,0xAB,0x8F,0x7F,0x00,0x56,0x52,0xA2,0x52,0x51,0x13,0x08,0x1E, +0x06,0xAE,0x51,0x51,0xA8,0x04,0x51,0xF0,0x55,0x04,0x01,0x51,0xA9,0x01,0x51,0xA7, +0x04,0xA9,0x06,0x50,0x67,0x30,0xA1,0x00,0x18,0x03,0x31,0x83,0x00,0xF0,0x54,0x00, +0x06,0x56,0xB0,0x56,0xA7,0x04,0xB0,0x5A,0xA7,0x02,0x9C,0x8F,0xF0,0x5A,0x51,0xB0, +0x51,0xA7,0x08,0xB0,0x58,0x52,0xA3,0x54,0x28,0x51,0xA4,0x8F,0x00,0x01,0x51,0xB1, +0x52,0x51,0x1B,0x03,0xB0,0x51,0x52,0xA6,0x02,0x52,0xAE,0x52,0xA7,0x06,0xB0,0x0C, +0x51,0xD1,0xAC,0x10,0x20,0x12,0x03,0xB0,0x0A,0x51,0xA9,0x50,0x51,0x67,0x30,0x58, +0x00,0x19,0x3D,0xA4,0x02,0x52,0xA2,0x52,0x58,0x13,0x24,0xAB,0x8F,0x7F,0x00,0xA7, +0x04,0x51,0xA9,0x3F,0xA7,0x04,0x56,0xB6,0x56,0xEF,0x06,0x01,0x56,0x55,0xD4,0x54, +0xB0,0xA7,0x08,0x5A,0x9C,0x10,0x5A,0x5A,0xB0,0xA7,0x02,0x5A,0x31,0x65,0xFF,0x3C, +0xAC,0x08,0x51,0x12,0x07,0xD0,0x8F,0x00,0x80,0x00,0x00,0x51,0x3C,0x01,0x50,0x05, +0x3C,0xAC,0x08,0x58,0x12,0x07,0xD0,0x8F,0x40,0x1F,0x00,0x00,0x58,0xEF,0x00,0x09, +0xAC,0x04,0x5A,0x3C,0x8F,0x54,0x00,0x50,0x05,0xB3,0x8F,0x80,0x80,0x67,0x13,0xF9, +0x05,0xBB,0x8F,0xFF,0x00,0xD0,0xA9,0x34,0x54,0xDB,0x38,0x55,0xD5,0x55,0x13,0x2B, +0xD0,0xA9,0x60,0x53,0xCA,0x8F,0xFF,0x01,0x00,0x00,0x53,0xD0,0xA9,0x50,0x56,0xEF, +0x09,0x15,0x53,0x57,0xDE,0x47,0x66,0x52,0xDD,0x62,0xD0,0x8F,0x00,0x0A,0x10,0x90, +0x62,0xDA,0x53,0x3A,0xDE,0xC3,0x00,0x01,0x51,0x11,0x07,0xD0,0x8F,0x00,0x01,0x14, +0x20,0x51,0xD4,0xA1,0x08,0xD0,0x8F,0x20,0x4E,0x00,0x00,0x7E,0xD0,0x11,0x61,0xF5, +0x6E,0xFD,0xD0,0xA1,0x04,0x50,0xD4,0x61,0xC7,0x50,0x8F,0x60,0xEA,0x00,0x00,0xA4, +0x42,0xD6,0xA4,0x42,0xD4,0xA1,0x08,0xD0,0x8F,0x20,0x4E,0x00,0x00,0x50,0xD0,0x11, +0x61,0xB3,0x8F,0x00,0x80,0xEF,0x12,0x00,0x00,0x00,0x12,0x10,0xC1,0xA9,0x34,0x8F, +0x42,0x00,0x00,0x00,0x6E,0xD0,0xBE,0x00,0x6E,0xF5,0x6E,0xFD,0xF5,0x50,0xE2,0xD0, +0xA1,0x04,0x50,0xD4,0x61,0xD5,0x8E,0xC7,0x50,0x8F,0x40,0x0D,0x03,0x00,0xA4,0x3E, +0xD6,0xA4,0x3E,0xD5,0x55,0x13,0x06,0xD0,0x8E,0x62,0xDA,0x53,0x3A,0xBA,0x8F,0xFF, +0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x0C,0x54,0x55,0x44,0x52,0x49,0x56,0x45,0x52,0x2E,0x45,0x58,0x45,0x0C, +0x50,0x55,0x44,0x52,0x49,0x56,0x45,0x52,0x2E,0x45,0x58,0x45,0x4D,0x55,0xFC,0x01, +0xDB,0x38,0x50,0xD0,0x40,0xA9,0x54,0x57,0xD0,0xA9,0x34,0x51,0x16,0xEF,0xA8,0x04, +0x00,0x00,0xE8,0x50,0x1C,0x94,0xA1,0x38,0xD0,0x03,0xAC,0x2C,0xCB,0x8F,0x00,0x00, +0xFC,0xFF,0x57,0xAC,0x24,0xB0,0x8F,0x00,0x80,0xAC,0x28,0xB0,0x8F,0xFC,0x01,0xA9, +0x1E,0xD0,0xA1,0x24,0x53,0xD0,0x40,0xA9,0x5C,0x52,0xDE,0x43,0xC2,0x00,0x08,0x54, +0x9E,0xCF,0x78,0x01,0x52,0xCB,0x8F,0x00,0xFE,0xFF,0xFF,0x52,0x51,0x78,0x09,0x53, +0xA2,0x02,0xC8,0x51,0xA2,0x02,0xC0,0x10,0xA2,0x02,0xEF,0x09,0x15,0x52,0x52,0xE9, +0x50,0x0C,0xD0,0x42,0xB9,0x50,0x52,0xCA,0x8F,0x00,0x00,0xE0,0xFF,0x52,0xC9,0xAF, +0x75,0x52,0x84,0xD6,0x52,0xC9,0xAF,0x6E,0x52,0x64,0xD0,0x8F,0x80,0xC3,0xC9,0x01, +0xCF,0x57,0xFF,0xE3,0x00,0xCF,0x56,0xFF,0x11,0xB3,0x8F,0x00,0xF8,0xA7,0x02,0x12, +0x09,0x30,0x19,0x01,0xE9,0x50,0x03,0x31,0xBF,0x00,0xD0,0x02,0x58,0x9E,0xCF,0x1B, +0x01,0x53,0xD0,0x0B,0x52,0xB4,0x67,0xD0,0xA9,0x34,0x51,0xC5,0x8F,0x80,0x84,0x1E, +0x00,0xA1,0x3E,0x51,0x3C,0x01,0x50,0xD4,0x7E,0xB0,0xA7,0x02,0x54,0x19,0x19,0xE0, +0x52,0x54,0x15,0xC1,0xA9,0x34,0x8F,0x42,0x00,0x00,0x00,0x6E,0xD0,0xBE,0x00,0x6E, +0xF5,0x6E,0xFD,0xF5,0x51,0xE3,0xD4,0x50,0xD5,0x8E,0xE8,0x50,0x0D,0xF5,0x58,0xBD, +0x3C,0x8F,0xD4,0x20,0x50,0x04,0x00,0x00,0x00,0x80,0xB5,0x54,0x19,0xEF,0xB0,0x83, +0xA7,0x02,0xF3,0x0E,0x52,0xB1,0xD0,0xA9,0x34,0x51,0xC5,0x8F,0xA0,0x86,0x01,0x00, +0xA1,0x3E,0x51,0x3C,0x01,0x50,0xD4,0x7E,0xB5,0xA7,0x02,0x13,0x15,0xC1,0xA9,0x34, +0x8F,0x42,0x00,0x00,0x00,0x6E,0xD0,0xBE,0x00,0x6E,0xF5,0x6E,0xFD,0xF5,0x51,0xE8, +0xD4,0x50,0xD5,0x8E,0xC1,0x8F,0x94,0x00,0x00,0x00,0xCF,0x91,0x00,0xCF,0x9C,0x00, +0xC1,0x8F,0xD4,0x00,0x00,0x00,0xCF,0x85,0x00,0xCF,0x94,0x00,0xC1,0x14,0xCF,0x7D, +0x00,0xCF,0x90,0x00,0xC1,0x8F,0x54,0x00,0x00,0x00,0xCF,0x71,0x00,0xCF,0x88,0x00, +0xD4,0xCF,0x82,0xFE,0x10,0x57,0xE9,0x50,0x4E,0x3C,0xA2,0x10,0x50,0xC4,0x8F,0x80, +0x1A,0x06,0x00,0x50,0xD1,0x50,0xCF,0x71,0xFE,0x15,0x05,0xD0,0x50,0xCF,0x6A,0xFE, +0x30,0x69,0x01,0xD0,0x01,0x62,0x3C,0xA9,0x64,0xA2,0x04,0x9B,0x09,0xA2,0x08,0x30, +0x51,0x02,0xD4,0xCF,0x4C,0xFE,0xEF,0x16,0x05,0xA2,0x1C,0x51,0x78,0x08,0x51,0x51, +0xEF,0x1B,0x05,0xA2,0x1C,0x54,0xA8,0x8F,0x40,0x40,0x51,0xA9,0x51,0x54,0xCF,0x5B, +0xFE,0x88,0x18,0x50,0xE8,0x50,0x05,0x3C,0x8F,0x84,0x00,0x50,0x04,0xD4,0x54,0x30, +0x2A,0x01,0xD0,0x01,0x62,0x9B,0x04,0xA2,0x08,0x31,0x17,0x02,0x00,0x89,0x00,0x00, +0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x00,0x01,0x01, +0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x00,0x01,0x01, +0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x9E,0xCF,0x00,0xFF, +0x52,0xE9,0xCF,0xF1,0xFC,0x05,0x9E,0xCF,0x36,0xFF,0x52,0xDD,0x52,0x9A,0x07,0x50, +0x7C,0x82,0xF5,0x50,0xFB,0xD0,0x8E,0x52,0x90,0x20,0xA2,0x0B,0x05,0x10,0xDD,0xD1, +0xAC,0x10,0x21,0x13,0x03,0x31,0x81,0x00,0xBB,0x8F,0x00,0x05,0xD4,0x54,0xB1,0x6C, +0x07,0x1F,0x04,0xD0,0xAC,0x1C,0x54,0xD5,0x55,0x19,0x12,0x9B,0x25,0xA2,0x08,0xBB, +0x8F,0x70,0x00,0x30,0x79,0x00,0xBA,0x8F,0x70,0x00,0xE9,0x50,0x53,0x91,0x02,0xCF, +0xA6,0xFC,0x12,0x06,0x30,0xA3,0x00,0xE9,0x50,0x40,0x10,0xA0,0x9B,0x21,0xA2,0x08, +0x3C,0x8F,0x00,0x02,0xA2,0x0C,0xD1,0x58,0x8F,0x00,0x02,0x00,0x00,0x18,0x04,0xD0, +0x58,0xA2,0x0C,0xD0,0x5A,0xA2,0x10,0x30,0x10,0x01,0xE9,0x50,0x23,0xC0,0x8F,0x00, +0x02,0x00,0x00,0x5A,0xC2,0x8F,0x00,0x02,0x00,0x00,0x58,0x14,0xC0,0x95,0xCF,0x67, +0xFC,0x13,0x07,0x10,0x65,0xE9,0x50,0x02,0x11,0xF3,0xF0,0xA2,0x0C,0x10,0x10,0x50, +0x94,0xCF,0x54,0xFC,0xBA,0x8F,0x00,0x05,0x04,0x9B,0x25,0xA2,0x08,0xD1,0xAC,0x10, +0x24,0x12,0x06,0xA8,0x02,0xA2,0x0A,0x11,0x3A,0xD1,0xAC,0x10,0x02,0x12,0x18,0xA8, +0x04,0xA2,0x0A,0xC2,0xCF,0x2B,0xFC,0x55,0x12,0x0D,0xD5,0x54,0x13,0x05,0xD0,0xCF, +0x20,0xFC,0x64,0x3C,0x01,0x50,0x05,0x9E,0xA2,0x10,0x51,0xD1,0xAC,0x10,0x25,0x13, +0x04,0x9E,0xA2,0x0C,0x51,0xD5,0x55,0x18,0x07,0xCE,0x55,0x55,0xA8,0x08,0xA2,0x0A, +0xD0,0x55,0x61,0x30,0x94,0x00,0xE8,0x50,0x01,0x05,0x95,0xCF,0xFA,0xFB,0x12,0x01, +0x00,0x9E,0xCF,0xE9,0xFD,0x52,0xE9,0xCF,0xED,0xFB,0x05,0x9E,0xCF,0xE3,0xFD,0x52, +0x96,0xCF,0xE3,0xFB,0xD0,0xA9,0x34,0x51,0xC5,0xCF,0xDE,0xFB,0xA1,0x3E,0x51,0x3C, +0x01,0x50,0xD4,0x7E,0xB5,0x62,0x18,0x15,0xC1,0xA9,0x34,0x8F,0x42,0x00,0x00,0x00, +0x6E,0xD0,0xBE,0x00,0x6E,0xF5,0x6E,0xFD,0xF5,0x51,0xE9,0xD4,0x50,0xD5,0x8E,0x97, +0xCF,0xB5,0xFB,0xE9,0x50,0x7E,0xA8,0x8F,0x00,0x80,0x62,0x9E,0xCF,0x31,0xFE,0x52, +0xE8,0xCF,0xA3,0xFB,0x05,0x9E,0xCF,0x67,0xFE,0x52,0xD0,0xA2,0x1C,0xCF,0x92,0xFB, +0xD5,0x54,0x13,0x05,0xD0,0xCF,0x8A,0xFB,0x64,0xEF,0x00,0x05,0xA2,0x0A,0x50,0x13, +0x15,0xD1,0x01,0x50,0x13,0x10,0xD1,0x10,0x50,0x13,0x0B,0xD1,0x0E,0x50,0x12,0x44, +0x3C,0x8F,0x70,0x08,0x50,0x05,0x3C,0x01,0x50,0x05,0xB0,0xA7,0x02,0x50,0x19,0x34, +0x9E,0xCF,0x62,0xFD,0x51,0x9E,0xCF,0x55,0xFD,0x52,0xE9,0xCF,0x58,0xFB,0x0A,0x9E, +0xCF,0x57,0xFD,0x51,0x9E,0xCF,0x4A,0xFD,0x52,0x96,0xCF,0x49,0xFB,0x96,0xCF,0x47, +0xFB,0xA8,0x8F,0x00,0x80,0x61,0xA8,0x8F,0x00,0x80,0x62,0xB0,0x67,0x50,0xB0,0xA7, +0x02,0x50,0x18,0xC2,0x3C,0x8F,0x54,0x00,0x50,0x05,0x80,0x00,0xDB,0x38,0x50,0xD0, +0x40,0xA9,0x54,0x57,0xB4,0x67,0xD0,0x01,0x50,0x04,0xBB,0x8F,0xFF,0x00,0xD0,0xA9, +0x34,0x54,0xDB,0x38,0x55,0xD5,0x55,0x13,0x2B,0xD0,0xA9,0x60,0x53,0xCA,0x8F,0xFF, +0x01,0x00,0x00,0x53,0xD0,0xA9,0x50,0x56,0xEF,0x09,0x15,0x53,0x57,0xDE,0x47,0x66, +0x52,0xDD,0x62,0xD0,0x8F,0x00,0x0A,0x10,0x90,0x62,0xDA,0x53,0x3A,0xDE,0xC3,0x00, +0x01,0x51,0x11,0x07,0xD0,0x8F,0x00,0x01,0x14,0x20,0x51,0xD4,0xA1,0x08,0xD0,0x8F, +0x20,0x4E,0x00,0x00,0x7E,0xD0,0x11,0x61,0xF5,0x6E,0xFD,0xD0,0xA1,0x04,0x50,0xD4, +0x61,0xC7,0x50,0x8F,0x60,0xEA,0x00,0x00,0xA4,0x42,0xD6,0xA4,0x42,0xD4,0xA1,0x08, +0xD0,0x8F,0x20,0x4E,0x00,0x00,0x50,0xD0,0x11,0x61,0xB3,0x8F,0x00,0x80,0xEF,0x12, +0x00,0x00,0x00,0x12,0x10,0xC1,0xA9,0x34,0x8F,0x42,0x00,0x00,0x00,0x6E,0xD0,0xBE, +0x00,0x6E,0xF5,0x6E,0xFD,0xF5,0x50,0xE2,0xD0,0xA1,0x04,0x50,0xD4,0x61,0xD5,0x8E, +0xC7,0x50,0x8F,0x40,0x0D,0x03,0x00,0xA4,0x3E,0xD6,0xA4,0x3E,0xD5,0x55,0x13,0x06, +0xD0,0x8E,0x62,0xDA,0x53,0x3A,0xBA,0x8F,0xFF,0x00,0x05,0x0C,0x44,0x55,0x44,0x52, +0x49,0x56,0x45,0x52,0x2E,0x45,0x58,0x45,0x0C,0x50,0x55,0x44,0x52,0x49,0x56,0x45, +0x52,0x2E,0x45,0x58,0x45,0x44,0x55,0x00,0x00,0x00,0x00,0xFC,0x01,0xDB,0x38,0x50, +0xD0,0x40,0xA9,0x54,0x57,0xD0,0xA9,0x34,0x51,0x16,0xEF,0xC0,0x02,0x00,0x00,0xE8, +0x50,0x1C,0x94,0xA1,0x38,0xD0,0x01,0xAC,0x2C,0xCB,0x8F,0x00,0x00,0xFC,0xFF,0x57, +0xAC,0x24,0xB0,0x8F,0x00,0x80,0xAC,0x28,0xB0,0x8F,0xFC,0x01,0xA9,0x1E,0xD0,0xA1, +0x24,0x53,0xD0,0x40,0xA9,0x5C,0x52,0xDE,0x43,0xC2,0x00,0x08,0x54,0x9E,0xCF,0x58, +0x01,0x52,0xCB,0x8F,0x00,0xFE,0xFF,0xFF,0x52,0x51,0x78,0x09,0x53,0xA2,0x02,0xC8, +0x51,0xA2,0x02,0xC0,0x10,0xA2,0x02,0xEF,0x09,0x15,0x52,0x52,0xE9,0x50,0x0C,0xD0, +0x42,0xB9,0x50,0x52,0xCA,0x8F,0x00,0x00,0xE0,0xFF,0x52,0xC9,0xAF,0x63,0x52,0x84, +0xD6,0x52,0xC9,0xAF,0x5C,0x52,0x64,0xB3,0x8F,0x00,0xF8,0xA7,0x02,0x12,0x06,0x30, +0xFE,0x00,0xE8,0x50,0x79,0xD0,0x02,0x58,0x9E,0xCF,0x0D,0x01,0x53,0xD0,0x0B,0x52, +0xB4,0x67,0xD0,0xA9,0x34,0x51,0xC5,0x8F,0x40,0x42,0x0F,0x00,0xA1,0x3E,0x51,0x3C, +0x01,0x50,0xD4,0x7E,0xB0,0xA7,0x02,0x54,0x19,0x19,0xE0,0x52,0x54,0x15,0xC1,0xA9, +0x34,0x8F,0x42,0x00,0x00,0x00,0x6E,0xD0,0xBE,0x00,0x6E,0xF5,0x6E,0xFD,0xF5,0x51, +0xE3,0xD4,0x50,0xD5,0x8E,0xE8,0x50,0x0D,0xF5,0x58,0xBD,0x3C,0x8F,0xD4,0x20,0x50, +0x04,0x00,0x00,0x00,0x80,0xB5,0x54,0x19,0xEF,0xB0,0x83,0xA7,0x02,0xF3,0x0E,0x52, +0xB1,0xC1,0x8F,0x4C,0x00,0x00,0x00,0xCF,0xB1,0x00,0xCF,0xBC,0x00,0xC1,0x0C,0xCF, +0xA9,0x00,0xCF,0xB8,0x00,0x30,0x88,0x00,0xE8,0x50,0x03,0x31,0x5F,0x00,0x9A,0x12, +0xCF,0xF4,0xFE,0xD0,0x01,0x65,0x3C,0xA9,0x64,0xA5,0x04,0x9A,0x09,0xA5,0x08,0x30, +0x3C,0x01,0xE9,0x50,0x0E,0x88,0x08,0x50,0xE1,0x07,0xCF,0xE6,0x00,0x44,0x88,0x10, +0x50,0x11,0x3F,0xB1,0x54,0x03,0x12,0x35,0xD7,0xCF,0xCB,0xFE,0x19,0x2F,0xD0,0xA9, +0x34,0x51,0xC5,0x8F,0x40,0x42,0x0F,0x00,0xA1,0x3E,0x51,0x3C,0x01,0x50,0xD4,0x7E, +0xD5,0x54,0x13,0x15,0xC1,0xA9,0x34,0x8F,0x42,0x00,0x00,0x00,0x6E,0xD0,0xBE,0x00, +0x6E,0xF5,0x6E,0xFD,0xF5,0x51,0xE9,0xD4,0x50,0xD5,0x8E,0x11,0xA6,0x3C,0x8F,0x84, +0x00,0x50,0xEF,0x16,0x05,0xCF,0xA9,0x00,0x51,0x78,0x08,0x51,0x51,0xEF,0x1B,0x05, +0xCF,0x9E,0x00,0x52,0xA8,0x8F,0x40,0x40,0x51,0xA9,0x51,0x52,0xCF,0x76,0xFE,0x04, +0x9E,0xCF,0x31,0x00,0x55,0xD0,0x01,0x65,0xD4,0xA5,0x04,0x9A,0x04,0xA5,0x08,0x7C, +0xA5,0x0C,0x7C,0xA5,0x14,0x31,0xB6,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x01, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x3C,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x90,0x21,0xAF,0x88,0xD1,0xAC,0x10,0x20,0x12,0x05,0x90,0x22,0xCF,0x7D,0xFF, +0xD0,0x55,0xAF,0x8D,0xD0,0x58,0xCF,0x78,0xFF,0xD0,0x5A,0xCF,0x77,0xFF,0xD0,0x8F, +0x80,0x96,0x98,0x00,0x52,0xB0,0xA7,0x02,0x54,0x19,0x5E,0xA8,0x8F,0x00,0x80,0xCF, +0x4D,0xFF,0xA8,0x8F,0x00,0x80,0xCF,0x42,0xFF,0xB0,0x67,0x54,0xB0,0xA7,0x02,0x54, +0x19,0x47,0xD0,0xA9,0x34,0x51,0xC5,0x52,0xA1,0x3E,0x51,0x3C,0x01,0x50,0xD4,0x7E, +0xB5,0xCF,0x27,0xFF,0x18,0x15,0xC1,0xA9,0x34,0x8F,0x42,0x00,0x00,0x00,0x6E,0xD0, +0xBE,0x00,0x6E,0xF5,0x6E,0xFD,0xF5,0x51,0xE7,0xD4,0x50,0xD5,0x8E,0xE9,0x50,0x19, +0xA8,0x8F,0x00,0x80,0xCF,0x04,0xFF,0xEF,0x00,0x05,0xCF,0x52,0xFF,0x54,0x13,0x05, +0xB1,0x01,0x54,0x12,0x04,0x3C,0x01,0x50,0x05,0x3C,0x8F,0x54,0x00,0x50,0x05,0x80, +0x00,0xDB,0x38,0x50,0xD0,0x40,0xA9,0x54,0x57,0xB4,0x67,0xD0,0x01,0x50,0x04,0xBB, +0x8F,0xFF,0x00,0xD0,0xA9,0x34,0x54,0xDB,0x38,0x55,0xD5,0x55,0x13,0x2B,0xD0,0xA9, +0x60,0x53,0xCA,0x8F,0xFF,0x01,0x00,0x00,0x53,0xD0,0xA9,0x50,0x56,0xEF,0x09,0x15, +0x53,0x57,0xDE,0x47,0x66,0x52,0xDD,0x62,0xD0,0x8F,0x00,0x0A,0x10,0x90,0x62,0xDA, +0x53,0x3A,0xDE,0xC3,0x00,0x01,0x51,0x11,0x07,0xD0,0x8F,0x00,0x01,0x14,0x20,0x51, +0xD4,0xA1,0x08,0xD0,0x8F,0x20,0x4E,0x00,0x00,0x7E,0xD0,0x11,0x61,0xF5,0x6E,0xFD, +0xD0,0xA1,0x04,0x50,0xD4,0x61,0xC7,0x50,0x8F,0x60,0xEA,0x00,0x00,0xA4,0x42,0xD6, +0xA4,0x42,0xD4,0xA1,0x08,0xD0,0x8F,0x20,0x4E,0x00,0x00,0x50,0xD0,0x11,0x61,0xB3, +0x8F,0x00,0x80,0xEF,0x12,0x00,0x00,0x00,0x12,0x10,0xC1,0xA9,0x34,0x8F,0x42,0x00, +0x00,0x00,0x6E,0xD0,0xBE,0x00,0x6E,0xF5,0x6E,0xFD,0xF5,0x50,0xE2,0xD0,0xA1,0x04, +0x50,0xD4,0x61,0xD5,0x8E,0xC7,0x50,0x8F,0x40,0x0D,0x03,0x00,0xA4,0x3E,0xD6,0xA4, +0x3E,0xD5,0x55,0x13,0x06,0xD0,0x8E,0x62,0xDA,0x53,0x3A,0xBA,0x8F,0xFF,0x00,0x05, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x64,0x68,0x6C,0x0C,0x58,0x51,0x44,0x52,0x49,0x56,0x45,0x52,0x2E,0x45,0x58,0x45, +0x58,0x51,0xFC,0x0D,0xD0,0x8F,0x8A,0x0D,0x00,0x00,0xA9,0x38,0x94,0xCF,0x92,0x06, +0xDB,0x38,0x50,0xE8,0x50,0x09,0xE1,0x05,0xAC,0x2C,0x04,0x96,0xCF,0x83,0x06,0xB4, +0xA9,0x1E,0xDD,0x03,0x10,0x09,0xE8,0x50,0x03,0xF5,0x6E,0xF8,0xD5,0x8E,0x04,0x94, +0xCF,0x70,0x06,0xDB,0x38,0x50,0xD0,0x40,0xA9,0x54,0x57,0xB0,0x02,0xA7,0x0E,0xE9, +0xCF,0x5F,0x06,0x13,0x9A,0x01,0xAC,0x48,0x9B,0x01,0xA7,0x0C,0xE0,0x00,0xA7,0x0C, +0x03,0xD4,0xAC,0x48,0xB4,0xA7,0x0C,0xD0,0xA9,0x34,0x51,0x16,0xCF,0x8A,0x05,0xD0, +0xA1,0x24,0x53,0xD0,0x40,0xA9,0x5C,0x52,0xDE,0x43,0xC2,0x00,0x08,0x54,0x9E,0xCF, +0x5E,0xFF,0x52,0xCB,0x8F,0x00,0xFE,0xFF,0xFF,0x52,0x51,0x78,0x09,0x53,0xCF,0x23, +0x06,0xC8,0x51,0xCF,0x1E,0x06,0xEF,0x09,0x15,0x52,0x55,0xE9,0x50,0x0C,0xD0,0x45, +0xB9,0x50,0x55,0xCA,0x8F,0x00,0x00,0xE0,0xFF,0x55,0xC9,0xCF,0x33,0x02,0x55,0x84, +0xD6,0x55,0xC9,0xCF,0x2B,0x02,0x55,0x64,0x2C,0x00,0x6E,0x00,0x20,0x62,0x9E,0xCF, +0xE6,0x05,0x52,0x90,0x67,0x82,0x90,0xA7,0x02,0x82,0x90,0xA7,0x04,0x82,0x90,0xA7, +0x06,0x82,0x90,0xA7,0x08,0x82,0x90,0xA7,0x0A,0x82,0xE9,0xCF,0xD4,0x05,0x0B,0x7D, +0xA2,0xFA,0xAC,0x3C,0x9E,0xCF,0xD4,0x05,0xAC,0x44,0x9E,0xCF,0x9C,0x0A,0x56,0x2C, +0x00,0x6E,0x00,0x8F,0x80,0x00,0xA6,0x10,0xD0,0x02,0x53,0xD0,0x06,0x50,0xC0,0x10, +0x56,0x9E,0xCF,0xA3,0x05,0x52,0xD0,0x07,0x51,0xD6,0x56,0x90,0x62,0x86,0xF5,0x51, +0xFA,0xD6,0x52,0xF5,0x50,0xF0,0xF5,0x53,0xE2,0x9E,0xCF,0x8B,0x05,0x52,0x7C,0x50, +0x78,0x01,0x51,0x51,0xD1,0x51,0x8F,0xFF,0xFF,0x00,0x00,0x1B,0x07,0xC2,0x8F,0xFF, +0xFF,0x00,0x00,0x51,0x90,0x82,0x7E,0x90,0x82,0x7E,0x3C,0x8E,0x53,0xC0,0x53,0x51, +0xD1,0x51,0x8F,0xFF,0xFF,0x00,0x00,0x1B,0x07,0xC2,0x8F,0xFF,0xFF,0x00,0x00,0x51, +0xF3,0x02,0x50,0xCC,0xD1,0x51,0x8F,0xFF,0xFF,0x00,0x00,0x12,0x02,0xD4,0x51,0xAA, +0x02,0xA7,0x0E,0xB0,0x8F,0x00,0x02,0xA7,0x0E,0x90,0xA7,0x02,0x7E,0x90,0x67,0x7E, +0xB1,0x8E,0x51,0x13,0x06,0x3C,0x8F,0x54,0x00,0x50,0x05,0xFA,0x6C,0xCF,0xB4,0x00, +0xE9,0x50,0xF2,0x9E,0xCF,0x05,0x06,0x56,0x2C,0x00,0x6E,0x8F,0x55,0x8F,0x0E,0x04, +0x66,0xD0,0xCF,0x13,0x05,0x66,0xB0,0xCF,0x12,0x05,0xA6,0x04,0xFA,0x6C,0xCF,0x88, +0x00,0xE9,0x50,0x7F,0x9E,0xCF,0x48,0xFE,0x5A,0xE0,0x0E,0xAA,0x08,0x75,0x2D,0x00, +0x6E,0x8F,0x55,0x8F,0x00,0x04,0xCF,0xE1,0x05,0x12,0x68,0x11,0x15,0xAA,0x8F,0x00, +0x03,0xA7,0x0E,0xC0,0x02,0x66,0xFA,0x6C,0xAF,0x5F,0xE9,0x50,0x56,0xE1,0x0B,0xAA, +0x08,0x51,0x96,0xCF,0xDD,0x04,0xFA,0x6C,0xAF,0x5A,0xE9,0x50,0x46,0xB0,0x8F,0x00, +0x03,0xA7,0x0E,0x2C,0x00,0x6E,0x8F,0xAA,0x8F,0x08,0x04,0xA6,0x06,0xD0,0xCF,0xB7, +0x04,0x66,0xB0,0xCF,0xB6,0x04,0xA6,0x04,0xFA,0x6C,0xAF,0x2D,0xE9,0x50,0x24,0xE0, +0x0E,0xAA,0x08,0x1F,0x2D,0x00,0x6E,0x8F,0xAA,0x8F,0x00,0x04,0xCF,0x8B,0x05,0x12, +0x12,0x96,0xCF,0x9E,0x04,0xFA,0x6C,0xAF,0x1B,0xE9,0x50,0x07,0xB0,0x8F,0x00,0x01, +0xA7,0x0E,0x05,0x3C,0x8F,0x54,0x00,0x50,0x05,0xFC,0x0F,0x10,0x59,0xB0,0x8F,0x00, +0xA0,0x50,0x11,0x17,0xFC,0x0F,0x10,0x3C,0x9A,0xCF,0x77,0x04,0x50,0x9A,0x40,0xCF, +0xBE,0xFD,0x50,0xC0,0x50,0x58,0xB0,0x8F,0x00,0xB0,0x50,0x30,0xFB,0x00,0xC0,0x10, +0xCF,0x61,0x04,0xFA,0x6C,0xAF,0x11,0xC2,0x10,0xCF,0x58,0x04,0xE9,0x50,0x08,0x9E, +0xCF,0x7D,0xFD,0x5A,0x31,0x4D,0x01,0x04,0xFC,0x0F,0x10,0x1A,0x9E,0xCF,0x80,0xFD, +0x5A,0x31,0x09,0x02,0x9E,0xCF,0x22,0x09,0x56,0x3C,0x8F,0x80,0x00,0x58,0x3C,0x02, +0x52,0x3C,0x02,0x51,0x11,0x10,0x9E,0xCF,0x02,0x05,0x56,0x3C,0x8F,0x0E,0x04,0x58, +0x3C,0x04,0x52,0x3C,0x04,0x51,0xDB,0x38,0x50,0xD0,0x40,0xA9,0x5C,0x53,0xD0,0xA9, +0x34,0x54,0xC1,0x52,0xA4,0x24,0x57,0xDE,0x47,0xC3,0x00,0x08,0x54,0xEF,0x09,0x15, +0x56,0x55,0xE9,0x50,0x0C,0xD0,0x45,0xB9,0x50,0x55,0xCA,0x8F,0x00,0x00,0xE0,0xFF, +0x55,0xC9,0xAF,0x1D,0x55,0x84,0xD6,0x55,0xF5,0x51,0xF6,0xCA,0xAF,0x13,0x64,0xEF, +0x00,0x09,0x56,0x52,0x78,0x09,0x57,0x57,0xC8,0x57,0x52,0xD0,0x40,0xA9,0x54,0x57, +0x05,0x00,0x00,0x00,0x80,0xD0,0xAC,0x04,0x56,0xB4,0xCF,0xF5,0xFC,0xD1,0x20,0xAC, +0x10,0x13,0x03,0x31,0x63,0x01,0xB0,0x02,0xA7,0x0E,0xD0,0x56,0x50,0xAA,0x02,0xA7, +0x0E,0xB0,0x8F,0x01,0x01,0xA7,0x0E,0xE9,0x65,0x03,0xB0,0x58,0x76,0xB0,0xA5,0x02, +0x76,0xB0,0xCF,0x97,0x03,0x76,0xD0,0xCF,0x8E,0x03,0x76,0xB0,0xA5,0x08,0x76,0xD0, +0xA5,0x04,0x76,0xC2,0x56,0x50,0xC0,0x50,0x58,0xC3,0x50,0x5A,0x52,0xEF,0x09,0x15, +0x52,0x50,0xED,0x09,0x15,0x5A,0x50,0x13,0x03,0x31,0x17,0x01,0xB1,0x58,0x3C,0x1E, +0x03,0x3C,0x3C,0x58,0xB0,0x8F,0x00,0xA0,0x50,0xC3,0x0E,0x58,0xCF,0x69,0x03,0xE9, +0x56,0x11,0xA8,0x8F,0x40,0x00,0x50,0xE8,0x58,0x11,0xA8,0x8F,0x80,0x00,0x50,0xD6, +0x58,0x11,0x08,0xE9,0x58,0x05,0xA8,0x8F,0x80,0x00,0x50,0x9E,0xCF,0x71,0xFC,0x5A, +0xD6,0x58,0x78,0x8F,0xFF,0x58,0x58,0xB0,0x8F,0x00,0x80,0xAA,0x08,0xAE,0x58,0xAA, +0x06,0xB0,0x52,0xAA,0x04,0xB4,0xAA,0x0A,0x78,0x8F,0xF0,0x52,0x52,0xA9,0x50,0x52, +0xAA,0x02,0xB0,0xCF,0x1E,0x03,0xA7,0x08,0xB0,0xCF,0x1A,0x03,0xA7,0x0A,0xB3,0x8F, +0x00,0x01,0xA7,0x0E,0x13,0x0D,0xB3,0x8F,0x00,0x02,0xA7,0x0E,0x12,0x05,0xE1,0x0C, +0xAA,0x02,0x01,0x05,0xD0,0xA9,0x34,0x51,0xC5,0x8F,0x80,0x1A,0x06,0x00,0xA1,0x3E, +0x51,0x3C,0x01,0x50,0xD4,0x7E,0xB5,0xAA,0x0A,0x12,0x15,0xC1,0xA9,0x34,0x8F,0x42, +0x00,0x00,0x00,0x6E,0xD0,0xBE,0x00,0x6E,0xF5,0x6E,0xFD,0xF5,0x51,0xE8,0xD4,0x50, +0xD5,0x8E,0xE8,0x50,0x03,0x31,0x6B,0x00,0x9E,0xCF,0xD0,0x02,0x50,0xB3,0x8F,0x00, +0x0A,0xAA,0x08,0x12,0x44,0xE1,0x08,0xAA,0x08,0x0A,0xC0,0x01,0xC0,0xC0,0x00,0xD8, +0x00,0xC0,0xC4,0x00,0xC0,0x01,0xA0,0x28,0xD8,0x00,0xA0,0x2C,0xC0,0xCF,0xA8,0x02, +0xA0,0x18,0xD8,0x00,0xA0,0x1C,0xEF,0x04,0x04,0xAA,0x08,0x54,0x13,0x17,0xD1,0x54, +0x01,0x13,0x0A,0xC0,0x01,0xA0,0x50,0xD8,0x00,0xA0,0x54,0x11,0x08,0xC0,0x01,0xA0, +0x48,0xD8,0x00,0xA0,0x4C,0x9A,0x01,0x50,0x04,0xE1,0x09,0xAA,0x08,0x08,0xC0,0x01, +0xA0,0x58,0xD8,0x00,0xA0,0x5C,0xE1,0x0B,0xAA,0x08,0x08,0xC0,0x01,0xA0,0x60,0xD8, +0x00,0xA0,0x64,0x3C,0x8F,0x54,0x00,0x50,0x04,0x9A,0x0E,0x50,0xE9,0x65,0x03,0xC0, +0x02,0x50,0xC2,0x50,0x56,0xC0,0x50,0x58,0xC3,0x50,0x5A,0x52,0xEF,0x09,0x15,0x52, +0x50,0xED,0x09,0x15,0x5A,0x50,0x12,0xDB,0x9E,0xCF,0x64,0xFB,0x5A,0x78,0x8F,0xFF, +0x58,0x58,0x78,0x8F,0xF0,0x52,0x54,0xD0,0x8F,0x60,0xE3,0x16,0x00,0xCF,0x1E,0x02, +0xE9,0xCF,0x1E,0x02,0x09,0xD0,0x8F,0x80,0x1A,0x06,0x00,0xCF,0x10,0x02,0xDD,0x32, +0xB0,0x8F,0x00,0x80,0xAA,0x08,0xAE,0x58,0xAA,0x06,0xB0,0x52,0xAA,0x04,0xB0,0x01, +0xAA,0x0A,0xA9,0x8F,0x00,0x80,0x54,0xAA,0x02,0xB0,0xCF,0xF7,0x01,0xA7,0x04,0xB0, +0xCF,0xF3,0x01,0xA7,0x06,0xD0,0xA9,0x34,0x51,0xC5,0xCF,0xE1,0x01,0xA1,0x3E,0x51, +0x3C,0x01,0x50,0xD4,0x7E,0x91,0xAA,0x0A,0xAA,0x0B,0x13,0x15,0xC1,0xA9,0x34,0x8F, +0x42,0x00,0x00,0x00,0x6E,0xD0,0xBE,0x00,0x6E,0xF5,0x6E,0xFD,0xF5,0x51,0xE6,0xD4, +0x50,0xD5,0x8E,0xE8,0x50,0x03,0x31,0x5A,0xFF,0xD0,0xA9,0x34,0x50,0xC7,0xA0,0x3E, +0x51,0xCF,0xAA,0x01,0xB3,0x8F,0x00,0x58,0xAA,0x08,0x12,0x08,0xE1,0x0D,0xAA,0x08, +0x3C,0x31,0xC4,0x00,0xE0,0x0B,0xAA,0x08,0x87,0x9E,0xCF,0x9F,0x01,0x50,0xE1,0x01, +0xAA,0x08,0x0A,0xC0,0x01,0xC0,0x88,0x00,0xD8,0x00,0xC0,0x8C,0x00,0xE1,0x02,0xAA, +0x08,0x0A,0xC0,0x01,0xC0,0x90,0x00,0xD8,0x00,0xC0,0x94,0x00,0xE1,0x00,0xAA,0x08, +0x40,0xC0,0x01,0xC0,0xA8,0x00,0xD8,0x00,0xC0,0xAC,0x00,0x11,0x34,0xE9,0xCF,0x61, +0x01,0x07,0xB0,0xA6,0x0C,0xA5,0x02,0x11,0x31,0xB1,0xA5,0x02,0xA6,0x0C,0x12,0x12, +0xE8,0xA5,0x04,0x26,0xD1,0xA5,0x04,0xA6,0x06,0x12,0x07,0xB1,0xA5,0x08,0xA6,0x0A, +0x13,0x22,0x9E,0xCF,0x46,0x01,0x50,0xC0,0x01,0xC0,0xA0,0x00,0xD8,0x00,0xC0,0xA4, +0x00,0xF5,0x6E,0x03,0x31,0xCC,0xFE,0x31,0x16,0xFF,0xD0,0xA6,0x06,0xA5,0x04,0xB0, +0xA6,0x0A,0xA5,0x08,0xAB,0x8F,0xFF,0xF8,0xAA,0x08,0x50,0x90,0xAA,0x0A,0x50,0xA0, +0x2E,0x50,0x9E,0xCF,0x16,0x01,0x58,0xC0,0x01,0xA8,0x20,0xD8,0x00,0xA8,0x24,0xC0, +0x50,0xA8,0x10,0xD8,0x00,0xA8,0x14,0xE9,0x66,0x10,0xC0,0x01,0xA8,0x38,0xD8,0x00, +0xA8,0x3C,0xC0,0x50,0xA8,0x30,0xD8,0x00,0xA8,0x34,0xA0,0x0E,0x50,0xE9,0x65,0x04, +0xB0,0xA6,0x0E,0x50,0x78,0x10,0x50,0x50,0xB0,0x01,0x50,0x04,0x80,0x00,0xDB,0x38, +0x50,0xD0,0x40,0xA9,0x54,0x57,0xB0,0x02,0xA7,0x0E,0xE9,0xCF,0xC4,0x00,0x06,0xB0, +0x8F,0x00,0x80,0xA7,0x0C,0x3C,0x01,0x50,0x04,0xBB,0x8F,0xFF,0x00,0xD0,0xA9,0x34, +0x54,0xDB,0x38,0x55,0xD5,0x55,0x13,0x2B,0xD0,0xA9,0x60,0x53,0xCA,0x8F,0xFF,0x01, +0x00,0x00,0x53,0xD0,0xA9,0x50,0x56,0xEF,0x09,0x15,0x53,0x57,0xDE,0x47,0x66,0x52, +0xDD,0x62,0xD0,0x8F,0x00,0x0A,0x10,0x90,0x62,0xDA,0x53,0x3A,0xDE,0xC3,0x00,0x01, +0x51,0x11,0x07,0xD0,0x8F,0x00,0x01,0x14,0x20,0x51,0xD4,0xA1,0x08,0xD0,0x8F,0x20, +0x4E,0x00,0x00,0x7E,0xD0,0x11,0x61,0xF5,0x6E,0xFD,0xD0,0xA1,0x04,0x50,0xD4,0x61, +0xC7,0x50,0x8F,0x60,0xEA,0x00,0x00,0xA4,0x42,0xD6,0xA4,0x42,0xD4,0xA1,0x08,0xD0, +0x8F,0x20,0x4E,0x00,0x00,0x50,0xD0,0x11,0x61,0xB3,0x8F,0x00,0x80,0xCF,0x12,0x00, +0x12,0x10,0xC1,0xA9,0x34,0x8F,0x42,0x00,0x00,0x00,0x6E,0xD0,0xBE,0x00,0x6E,0xF5, +0x6E,0xFD,0xF5,0x50,0xE4,0xD0,0xA1,0x04,0x50,0xD4,0x61,0xD5,0x8E,0xC7,0x50,0x8F, +0x40,0x0D,0x03,0x00,0xA4,0x3E,0xD6,0xA4,0x3E,0xD5,0x55,0x13,0x06,0xD0,0x8E,0x62, +0xDA,0x53,0x3A,0xBA,0x8F,0xFF,0x00,0x05,0xFF,0xFF,0x02,0x00,0x00,0x00,0x00,0x00, +0xF6,0x01,0x00,0x00,0x04,0xEE,0xFF,0xFF,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, +0xFF,0xFF,0x12,0x00,0x00,0x00,0x00,0x00,0x99,0x05,0x00,0x00,0xD2,0xEF,0xFF,0xFF, +0x2B,0x03,0x00,0x00,0x10,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x2C,0x00,0x00,0x00, +0xD8,0x04,0x00,0x00,0x2A,0x00,0x00,0x00,0xFF,0xFF,0x11,0x00,0x00,0x00,0x00,0x00, +0xA5,0x03,0x00,0x00,0x43,0xF5,0xFF,0xFF,0x56,0x02,0x00,0x00,0x00,0x00,0x00,0x00, +0x0D,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0xE4,0x02,0x00,0x00,0x1A,0x00,0x00,0x00, +0xFF,0xFF,0x60,0x00,0x00,0x00,0x00,0x00,0xC8,0x06,0x00,0x00,0xC0,0xF8,0xFF,0xFF, +0x05,0x03,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x00, +0xFC,0x05,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xBB,0x8F,0x7E,0x00, +0x10,0x5D,0xBA,0x8F,0x7E,0x00,0xD0,0xA9,0x34,0x50,0x17,0x40,0xB0,0x08,0xBB,0x8F, +0xFE,0x00,0x10,0x4B,0x9E,0x45,0xB5,0x0C,0x56,0x9E,0xCF,0x3F,0xED,0x54,0xC3,0x54, +0x56,0x57,0x13,0x36,0x28,0xA5,0x08,0x66,0x64,0x9E,0xCF,0xDF,0xEB,0x54,0xC2,0x57, +0xA4,0x08,0xC2,0x57,0xA4,0x0C,0xD5,0xA4,0x20,0x13,0x04,0xC2,0x57,0xA4,0x20,0xD5, +0xA4,0x1C,0x13,0x04,0xC2,0x57,0xA4,0x1C,0xD5,0xA4,0x2C,0x13,0x04,0xC2,0x57,0xA4, +0x2C,0xD5,0xA4,0x30,0x13,0x04,0xC2,0x57,0xA4,0x30,0xBA,0x8F,0xFE,0x00,0x05,0xDE, +0xCF,0xF5,0xFE,0x55,0x9A,0xA9,0x66,0x53,0x9A,0xCF,0xD9,0xEB,0x54,0x3C,0x8F,0x50, +0x01,0x56,0x32,0x65,0x50,0x13,0x7B,0x19,0x05,0xD1,0x50,0x54,0x12,0x17,0x32,0xA5, +0x02,0x50,0x19,0x05,0xD1,0x50,0x53,0x12,0x0C,0xD0,0xA5,0x04,0x50,0x13,0x0F,0x16, +0x40,0x65,0xE8,0x50,0x09,0xC0,0xA5,0x08,0x56,0xC0,0x28,0x55,0x11,0xD4,0xDE,0xCF, +0x6A,0xEB,0x54,0xC1,0x8F,0x50,0x01,0x00,0x00,0xA5,0x08,0xA9,0x38,0xC1,0x56,0xA5, +0x10,0xA4,0x08,0xC1,0x56,0xA5,0x14,0xA4,0x0C,0xD4,0xA4,0x1C,0xD0,0xA5,0x1C,0x51, +0x13,0x05,0xC1,0x56,0x51,0xA4,0x1C,0xD4,0xA4,0x20,0xD0,0xA5,0x18,0x51,0x13,0x05, +0xC1,0x56,0x51,0xA4,0x20,0xD4,0xA4,0x2C,0xD0,0xA5,0x20,0x51,0x13,0x05,0xC1,0x56, +0x51,0xA4,0x2C,0xD4,0xA4,0x30,0xD0,0xA5,0x24,0x51,0x13,0x05,0xC1,0x56,0x51,0xA4, +0x30,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, +0x2E,0x2C,0x2B,0x20,0x40,0x2A,0x25,0x2D,0x5B,0x09,0x0A,0x0D,0x2F,0x22,0x3D,0x1B, +0x53,0x3B,0x50,0x51,0x27,0x52,0x47,0x48,0x58,0x0A,0x0D,0x65,0x68,0x3F,0x0A,0x0D, +0x00,0x00,0x00,0x11,0x13,0x9E,0xAF,0xF1,0x54,0x30,0xD7,0x01,0xD0,0x5D,0x5E,0x9E, +0xAB,0xB4,0x59,0x94,0x69,0x30,0x95,0x02,0x10,0x02,0x11,0xFC,0x30,0xFA,0x01,0x3A, +0x58,0x29,0xAF,0xAC,0x13,0xDF,0xC3,0x50,0x29,0x50,0xAF,0x50,0x10,0x18,0x80,0x03, +0xD2,0x00,0x39,0x02,0x39,0x02,0x39,0x02,0x39,0x02,0x39,0x02,0x42,0x02,0xB7,0x02, +0x4E,0x02,0x29,0x01,0xBB,0x00,0x8A,0x00,0x85,0x00,0x70,0x02,0x5E,0x02,0xD2,0x02, +0x92,0x02,0xA0,0x05,0x8D,0x03,0x58,0x05,0xA5,0x03,0x41,0x00,0x47,0x00,0xD3,0x03, +0xB1,0x50,0x10,0x18,0xA0,0xC4,0x10,0x56,0xC0,0x50,0x56,0xC8,0x04,0x6A,0x05,0x9C, +0x1F,0x01,0x54,0x11,0x07,0xD0,0x8F,0x00,0x00,0xFE,0x7F,0x54,0x10,0x06,0xD0,0x54, +0x56,0x9F,0xAF,0xE7,0xE5,0x07,0x6A,0x03,0xCE,0x56,0x56,0x10,0x06,0xD4,0x56,0x94, +0xAB,0xFF,0x05,0x8F,0xAB,0xFF,0x00,0x04,0x17,0x00,0x17,0x00,0x0A,0x00,0x0F,0x00, +0x13,0x00,0x78,0x56,0x57,0x57,0x05,0xC4,0x56,0x57,0x05,0xC6,0x56,0x57,0x05,0xC0, +0x56,0x57,0x05,0x88,0x02,0x6A,0x11,0x03,0x8A,0x02,0x6A,0x10,0x46,0xE0,0x08,0x6A, +0x06,0xD0,0xAB,0x04,0x6B,0x11,0x04,0xD0,0xAB,0xE8,0x6B,0xEF,0x0F,0x01,0x6A,0x50, +0xF0,0x50,0x1F,0x01,0x6A,0x30,0x86,0x00,0xE1,0x09,0x6A,0x1A,0xD1,0xAB,0xEC,0x6B, +0x15,0x14,0x10,0x76,0x11,0xF6,0x31,0x1C,0xFF,0x10,0x18,0xE5,0x00,0x6A,0x0A,0xE1, +0x08,0x6A,0x03,0x30,0xA4,0x04,0x31,0xB4,0x01,0xE1,0x08,0x6A,0xF9,0x31,0xA5,0x01, +0x30,0x68,0xFF,0xE5,0x02,0x6A,0x16,0x30,0x7A,0xFF,0x9A,0xAB,0xFC,0x50,0xE2,0x50, +0xAA,0x01,0xD3,0xD0,0x57,0x40,0xAB,0xE8,0x96,0xAB,0xFC,0x7C,0x56,0x05,0xE0,0x1F, +0x6A,0x1D,0x8F,0xAB,0xFE,0x00,0x02,0x06,0x00,0x0C,0x00,0x12,0x00,0x9A,0xBB,0x00, +0xAB,0x04,0x05,0x3C,0xBB,0x00,0xAB,0x04,0x05,0xD0,0xBB,0x00,0xAB,0x04,0x05,0xDB, +0x6B,0xAB,0x04,0x05,0xD0,0x01,0x51,0xD5,0x6A,0x19,0x05,0x9C,0xAB,0xFE,0x51,0x51, +0xC0,0x51,0x6B,0x05,0x04,0x0C,0x1C,0x30,0x8F,0xFF,0x10,0xE8,0x10,0x2B,0x10,0xBE, +0xC8,0x01,0x6A,0x9A,0xAB,0xFE,0x51,0x9A,0x41,0xAF,0xE9,0x52,0xD0,0xAB,0x04,0x53, +0xE0,0x01,0x6A,0x04,0x10,0x53,0x11,0x0E,0xD0,0x53,0xAB,0x08,0x78,0x51,0x01,0x52, +0x94,0x42,0xAB,0x08,0x10,0x59,0x31,0x81,0x00,0x30,0x83,0x00,0x9E,0xAB,0x18,0x53, +0xC3,0x53,0x6B,0x53,0x19,0x12,0xC6,0x04,0x53,0xD1,0x53,0x0F,0x14,0x0A,0x9A,0x8F, +0x72,0x50,0x10,0x52,0xD4,0x52,0x11,0x13,0xD0,0x6B,0x53,0xD0,0x1C,0x52,0xD5,0x6A, +0x18,0x09,0x9A,0x8F,0x70,0x50,0x10,0x3E,0xD0,0x04,0x52,0x10,0x0C,0x9A,0x2F,0x50, +0x11,0x34,0xD4,0x52,0x11,0x03,0xD0,0x1C,0x52,0x9E,0xAB,0x08,0x54,0xEF,0x52,0x04, +0x53,0x51,0x90,0x41,0xCF,0xF9,0xFD,0x84,0xC2,0x04,0x52,0x18,0xF0,0x94,0x64,0x9E, +0xAB,0x08,0x54,0x9A,0x84,0x50,0x13,0x04,0x10,0x0C,0x11,0xF7,0x05,0x9A,0x8F,0x5C, +0x50,0x11,0x03,0x9A,0x58,0x50,0x9A,0x50,0x7E,0x7C,0x7E,0xDE,0xAE,0x08,0x7E,0xFB, +0x03,0xEF,0xF4,0xE6,0xFF,0xFF,0xD0,0x8E,0x50,0x05,0x9A,0x20,0x50,0x11,0xE7,0x9A, +0x0D,0x50,0x10,0xE2,0x9A,0x0A,0x50,0x11,0xDD,0x9A,0x89,0x58,0x13,0x01,0x05,0x9E, +0xAB,0xB4,0x59,0x30,0x44,0xE7,0xD0,0x50,0x58,0x91,0x58,0x8F,0x7F,0x12,0x0F,0xE2, +0x06,0x6A,0x02,0x10,0xB8,0x9A,0x79,0x58,0x13,0xE5,0x10,0xB7,0x11,0xE5,0xE5,0x06, +0x6A,0x02,0x10,0xA9,0xE1,0x06,0x58,0x03,0x8A,0x20,0x58,0x10,0xA6,0x90,0x58,0x89, +0x3A,0x58,0x08,0xCF,0x93,0xFD,0x13,0xCB,0x91,0x0D,0x58,0x12,0x02,0x10,0xB0,0x94, +0x69,0x9E,0xAB,0xB4,0x59,0x11,0xB2,0x30,0x1A,0xFE,0x83,0x12,0x50,0xAB,0xFF,0x05, +0xD5,0x56,0x13,0x03,0x30,0x0D,0xFE,0x8C,0x8F,0x80,0x6A,0x05,0xD0,0xAB,0x04,0x6B, +0xEF,0x0F,0x01,0x6A,0x50,0xF0,0x50,0x1F,0x01,0x6A,0x11,0x0F,0xD0,0x01,0x51,0xD5, +0x6A,0x19,0x05,0x9C,0xAB,0xFE,0x51,0x51,0xC2,0x51,0x6B,0x31,0xBE,0xFE,0x30,0x62, +0xFE,0xE1,0x08,0x6A,0x05,0xD0,0xAB,0xE8,0xAB,0x04,0x30,0xB6,0xFE,0xCA,0x8F,0x80, +0xFF,0xFF,0x00,0x6A,0x94,0xAB,0xFC,0x7C,0x56,0x05,0x58,0x50,0x49,0x47,0x45,0x42, +0x8A,0x01,0x6A,0x30,0x3D,0xFE,0x30,0x50,0xFF,0x3A,0x58,0x06,0xAF,0xEC,0xAF,0x50, +0x01,0x05,0x38,0x00,0xEE,0x02,0xD2,0x00,0xEF,0x00,0xDB,0x00,0x1D,0x01,0x31,0x24, +0xFD,0x43,0x4C,0x57,0x42,0x30,0x31,0xFF,0x3A,0x58,0x04,0xAF,0xF4,0x13,0xEF,0xE0, +0x02,0x50,0x09,0x83,0x01,0x50,0xAB,0xFE,0x8A,0x02,0x6A,0x05,0x88,0x02,0x6A,0x05, +0xF0,0x01,0x03,0x02,0x6A,0xE5,0x0F,0x6A,0x00,0x04,0xE1,0x08,0x6A,0x56,0xE0,0x09, +0x6A,0x10,0xD0,0x01,0x52,0xD5,0x42,0xCF,0xFA,0xFB,0x13,0x11,0xF3,0x08,0x52,0xF5, +0x11,0xBC,0xD0,0xAB,0xEC,0x52,0x13,0xED,0xD1,0x08,0x52,0x19,0xB1,0xD4,0x42,0xCF, +0x0A,0xFC,0xD4,0x42,0xCF,0x25,0xFC,0xD0,0xAB,0xE8,0x50,0x13,0x03,0x90,0x60,0x60, +0xE1,0x0A,0x6A,0x0C,0xD0,0xAB,0xF0,0x42,0xCF,0xF1,0xFB,0x13,0x03,0xD5,0xBB,0xF0, +0xE1,0x0B,0x6A,0x07,0xD0,0xAB,0xF4,0x42,0xCF,0x01,0xFC,0xD0,0x50,0x42,0xCF,0xB3, +0xFB,0x31,0x49,0xFF,0xD0,0x01,0x55,0xD0,0x45,0xCF,0xA8,0xFB,0x58,0x13,0x2E,0xD0, +0x55,0x53,0x30,0x9A,0xFE,0x30,0x4A,0xFE,0x30,0x8F,0xFE,0xD0,0x58,0x53,0x30,0x45, +0xFE,0x30,0x86,0xFE,0xD0,0x45,0xCF,0xB3,0xFB,0x53,0x13,0x03,0x30,0x37,0xFE,0xD0, +0x45,0xCF,0xC8,0xFB,0x53,0x13,0x06,0x30,0x70,0xFE,0x30,0x29,0xFE,0xF3,0x08,0x55, +0xC6,0x31,0x6B,0xFE,0xE1,0x08,0x6A,0x05,0xD0,0xAB,0xE8,0xAB,0x54,0x04,0xDE,0x6B, +0x51,0xE1,0x1F,0x6A,0x18,0xE2,0x0F,0x6A,0x14,0x11,0x12,0xDE,0xAB,0x04,0x51,0x11, +0x0C,0xDE,0xAB,0x54,0x51,0xE1,0x08,0x6A,0x04,0xD0,0xAB,0xE8,0x61,0xD0,0x61,0x56, +0x31,0x98,0xFC,0xDE,0xAB,0x18,0x55,0x10,0x02,0x11,0xF5,0x30,0x3B,0xFE,0x3A,0x58, +0x10,0xCF,0xEC,0xFB,0x13,0x2D,0xC3,0x50,0x10,0x50,0xDE,0x40,0x65,0x56,0x05,0xE1, +0x09,0x6A,0x20,0xEF,0x00,0x04,0xAB,0xEC,0x51,0xDE,0x41,0xCF,0x82,0xFB,0x51,0x11, +0xC4,0xDE,0xCF,0x7B,0xFB,0x55,0x10,0xD3,0xD0,0x66,0x56,0x11,0xC3,0x00,0x00,0x00, +0xDA,0x0F,0x26,0x31,0xEF,0xFB,0xDA,0x1F,0x12,0x7D,0x50,0xCF,0xAE,0xFA,0x9E,0xCF, +0xB2,0xFA,0x51,0x7D,0x52,0x81,0x7D,0x54,0x81,0x7D,0x56,0x81,0x7D,0x58,0x81,0x7D, +0x5A,0x81,0x7D,0x5C,0x81,0x9E,0xAE,0x0C,0x81,0x7D,0xAE,0x04,0x81,0xD4,0x81,0xD4, +0x81,0x9E,0xCF,0x6F,0xFA,0x5B,0x9E,0xAB,0xE4,0x5A,0x9E,0xAB,0xB4,0x59,0x94,0x69, +0xDB,0x11,0x50,0x9E,0xCF,0x69,0xFB,0x51,0xD0,0xA0,0x04,0x81,0x9E,0xAF,0xB1,0xA0, +0x04,0xD0,0xA0,0x18,0x81,0x9E,0xAF,0xA8,0xA0,0x18,0xD0,0xA0,0x20,0x81,0x9E,0xAF, +0x9F,0xA0,0x20,0xD0,0xA0,0x24,0x81,0x9E,0xAF,0x96,0xA0,0x24,0x31,0x2E,0xFE,0x7D, +0xAB,0x54,0xAE,0x04,0xDB,0x11,0x50,0x9E,0xCF,0x35,0xFB,0x51,0xD0,0x81,0xA0,0x04, +0xD0,0x81,0xA0,0x18,0xD0,0x81,0xA0,0x20,0xD0,0x81,0xA0,0x24,0x9E,0xAB,0x20,0x51, +0x7D,0x81,0x52,0x7D,0x81,0x54,0x7D,0x81,0x56,0x7D,0x81,0x58,0x7D,0x81,0x5A,0x7D, +0x81,0x5C,0x7D,0xCF,0x16,0xFA,0x50,0x05,0x20,0x62,0x72,0x6B,0x20,0x61,0x74,0x20, +0x00,0x00,0x00,0x00,0x30,0x4F,0xFF,0x30,0xAC,0x00,0xD5,0x53,0x13,0x03,0x88,0x18, +0x6A,0x30,0x6A,0x00,0xE0,0x04,0xAB,0x58,0x38,0xD0,0x53,0x55,0x30,0x30,0xFD,0x30, +0xE0,0xFC,0x9E,0xAF,0xD3,0x54,0x30,0xFA,0xFC,0xD0,0xAB,0x54,0x53,0x30,0xD6,0xFC, +0x30,0x17,0xFD,0xD0,0x45,0xCF,0x44,0xFA,0x51,0x13,0x06,0xD0,0x51,0x6B,0x30,0x5B, +0xFC,0xD0,0x45,0xCF,0x56,0xFA,0x51,0x13,0x03,0xD0,0x51,0x59,0xFA,0x6C,0xCF,0xF0, +0xFA,0x10,0x40,0xE5,0x03,0x6A,0x09,0xE2,0x04,0xAB,0x58,0x00,0xE2,0x05,0x6A,0x00, +0x30,0x5C,0xFF,0x02,0x30,0xEF,0xFE,0xE5,0x05,0x6A,0x04,0xCA,0x10,0xAB,0x58,0x10, +0x0D,0xE4,0x04,0x6A,0xDC,0xD0,0xAB,0x54,0x6B,0x30,0x20,0xFC,0x11,0xCE,0xD0,0x08, +0x51,0xD0,0x41,0xCF,0xCE,0xF9,0x50,0x13,0x06,0x90,0x41,0xCF,0xE9,0xF9,0x60,0xF5, +0x51,0xEF,0x05,0xD0,0x08,0x51,0xD0,0x41,0xCF,0xB9,0xF9,0x50,0x13,0x14,0x90,0x60, +0x41,0xCF,0xD3,0xF9,0x93,0x18,0x6A,0x13,0x06,0xD1,0x50,0xAB,0x54,0x13,0x03,0x90, +0x03,0x60,0xF5,0x51,0xE1,0x05,0xD0,0x08,0x53,0xD1,0xAB,0x54,0x43,0xCF,0x94,0xF9, +0x13,0x03,0xF5,0x53,0xF4,0x05,0xD0,0x6B,0x55,0x30,0x8D,0xFC,0x91,0x27,0x58,0x13, +0x05,0x90,0x58,0x85,0x11,0xF3,0xD0,0x55,0x6B,0x05,0xE0,0x1F,0x6A,0x1D,0x8F,0xAB, +0xFE,0x00,0x02,0x06,0x00,0x0C,0x00,0x12,0x00,0x90,0xAB,0xE8,0xBB,0x00,0x05,0xB0, +0xAB,0xE8,0xBB,0x00,0x05,0xD0,0xAB,0xE8,0xBB,0x00,0x05,0xDA,0xAB,0xE8,0x6B,0x05, +0xE1,0x08,0x6A,0x09,0xD0,0xAB,0xE8,0x59,0x12,0x03,0x31,0x3F,0xFA,0x05,0xE2,0x0F, +0x6A,0x00,0x05,0x61,0x41,0x30,0x7A,0x5A,0x39,0x57,0x37,0x30,0x3C,0x00,0xD0,0x0A, +0x52,0x11,0x0C,0x3C,0x00,0xD0,0x08,0x52,0x11,0x05,0x3C,0x00,0xD0,0x10,0x52,0x7C, +0x50,0x7C,0x53,0xD7,0x53,0x11,0x40,0x9A,0x43,0xBC,0x08,0x55,0xD5,0x53,0x12,0x0E, +0x91,0x55,0x2B,0x13,0x32,0x91,0x55,0x2D,0x12,0x04,0xD6,0x54,0x11,0x29,0xD4,0x51, +0x91,0x55,0x41,0xAF,0xBE,0x1F,0x07,0x91,0x55,0x41,0xAF,0xBA,0x1B,0x06,0xF2,0x03, +0x51,0xEE,0x11,0x26,0x82,0x41,0xAF,0xB1,0x55,0xD1,0x55,0x52,0x1E,0x1C,0x7A,0x52, +0x50,0x55,0x50,0xD5,0x51,0x12,0x13,0xF2,0xAC,0x04,0x53,0xBB,0xE9,0x54,0x03,0xCE, +0x50,0x50,0xD0,0x50,0xBC,0x0C,0xD0,0x01,0x50,0x04,0xD4,0x50,0x04,0x00,0x00,0xDD, +0xAC,0x04,0xD0,0x6E,0x50,0xD0,0xA0,0x34,0x50,0xDD,0x00,0xDD,0xAC,0x10,0xDD,0xAC, +0x08,0xDD,0xAC,0x14,0xDF,0xBC,0x0C,0xFB,0x06,0x40,0xB0,0x00,0x04,0x9A,0x39,0x00, +0x00,0x00,0x08,0x40,0x00,0x00,0x10,0x00,0x00,0xCD,0x1C,0x00,0x00,0x00,0x04,0x40, +0x00,0x00,0x08,0x00,0x00,0x67,0x0E,0x00,0x00,0x80,0x02,0x40,0x00,0x00,0x04,0x00, +0x00,0x34,0x07,0x00,0x00,0x00,0x02,0x40,0x00,0x00,0x03,0x00,0x00,0x9A,0x03,0x00, +0x00,0x00,0x01,0x20,0x00,0x00,0x02,0x00,0x00,0xCD,0x01,0x00,0x00,0x00,0x01,0x10, +0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0xDD,0x55,0x7C,0xCF,0x55,0xCF,0xDE,0xAF,0xA3,0x50,0x7D,0x80,0x51,0x13,0x1F, +0xD0,0x80,0x55,0xD1,0xAB,0x4C,0x51,0x19,0xF2,0x3C,0x52,0x50,0x78,0x8F,0xF0,0x52, +0x51,0x78,0x8F,0xFF,0x51,0x54,0x10,0x37,0x19,0x05,0x7D,0x52,0xCF,0x2D,0xCF,0xD0, +0x8E,0x55,0x05,0x10,0xCC,0xD0,0xCF,0x23,0xCF,0x52,0x13,0x22,0xC2,0x04,0x5E,0xD0, +0x5E,0x50,0xC3,0x02,0x52,0x7E,0xDD,0x0D,0x78,0x09,0xCF,0x13,0xCF,0x7E,0x78,0x09, +0x52,0x7E,0xD4,0x7E,0xDD,0x50,0xFB,0x06,0xCF,0xE4,0x01,0xC0,0x04,0x5E,0x05,0x9C, +0x17,0x5B,0x52,0xC0,0x52,0x50,0xD0,0x50,0x53,0xC0,0x52,0x55,0xD1,0x55,0x50,0x19, +0x1E,0xE0,0x50,0xBB,0x48,0x0D,0xC3,0x53,0x50,0x52,0xD1,0x52,0x54,0x18,0x10,0xC1, +0x01,0x50,0x53,0xD6,0x50,0xF5,0x51,0xE4,0xC3,0x53,0x50,0x52,0xD1,0x52,0x54,0x05, +0x3C,0xA3,0x04,0x50,0x13,0x0D,0x9E,0x43,0x60,0x51,0x10,0x19,0x9E,0x43,0xA0,0x04, +0x51,0x10,0x12,0x3C,0xA3,0x08,0x50,0x13,0x07,0x9E,0x43,0xA0,0x20,0x51,0x10,0x05, +0x9A,0xA3,0x10,0x51,0x05,0xC3,0x01,0x61,0x51,0x19,0x08,0xD1,0x52,0x51,0x15,0x03, +0xD0,0x51,0x52,0x05,0x08,0x5B,0x53,0x59,0x53,0x45,0x58,0x45,0x5D,0x00,0x09,0x53, +0x59,0x53,0x43,0x4F,0x4D,0x4D,0x4F,0x4E,0x04,0xFC,0x08,0xD1,0x6C,0x09,0x19,0x13, +0xE1,0x01,0xAC,0x24,0x0E,0x3C,0x8F,0xC0,0x08,0x50,0x9E,0xCF,0x5D,0x07,0x52,0x13, +0xE7,0x17,0x62,0xD0,0xCF,0xC1,0xCE,0xBC,0x04,0xC2,0x8F,0x58,0x00,0x00,0x00,0x5E, +0xDE,0xAD,0xE0,0xAD,0xDC,0xD4,0xAD,0xD0,0x2C,0x00,0x6E,0x00,0x18,0xAD,0xA8,0xD1, +0x6C,0x08,0x19,0x09,0xD0,0xAC,0x20,0xAD,0xD0,0xE8,0xAD,0xD0,0x13,0xDE,0xCF,0x3B, +0xCE,0x5B,0x13,0x0E,0xD0,0xCF,0x38,0xCE,0x5B,0x13,0x07,0xB1,0x6B,0x01,0x13,0x02, +0xD4,0x5B,0xDE,0xCF,0x8E,0xFF,0x57,0x9A,0x87,0x56,0xD6,0x57,0xC2,0x02,0x56,0xD0, +0xAC,0x08,0x50,0x13,0x27,0x7D,0x60,0x52,0x3A,0x3A,0x52,0x63,0x13,0x07,0x9E,0xA1, +0x01,0x53,0x9E,0x70,0x52,0x91,0x8F,0x5B,0x63,0x13,0x05,0x91,0x3C,0x63,0x12,0x1D, +0x81,0x02,0x83,0x50,0xD7,0x52,0x3A,0x50,0x52,0x63,0x12,0x03,0x31,0xB0,0x03,0xD0, +0x53,0x57,0xC3,0x53,0x51,0x56,0x9E,0x70,0x52,0xDE,0xA1,0x01,0x53,0x7C,0x7E,0xD1, +0x6C,0x07,0x19,0x04,0x7D,0xAC,0x18,0x6E,0xDF,0xAD,0xFA,0x7D,0xAC,0x10,0x7E,0xDD, +0xAC,0x0C,0xDF,0xAD,0xD4,0xDD,0xBC,0x04,0xDD,0x06,0xD5,0x5B,0x13,0x07,0xDE,0xAB, +0x18,0xAE,0x0C,0x11,0x0B,0xFA,0x6E,0xCF,0xA3,0x01,0xE8,0x50,0x03,0x31,0x8E,0x00, +0x7D,0x52,0xAD,0xC0,0x7D,0x56,0xAD,0xC8,0xDE,0xAD,0xA8,0x58,0xE0,0x01,0xAD,0xD0, +0x0F,0xDE,0xCF,0x5B,0xCC,0x68,0xD0,0x68,0xA8,0x08,0xDE,0xCF,0x00,0xFF,0xA8,0x0C, +0xB0,0x04,0xAD,0xFA,0xD0,0x04,0xAD,0xFC,0xD0,0x88,0x51,0x13,0x11,0x9A,0x81,0x50, +0x13,0x0C,0x7D,0x50,0x56,0x30,0xF9,0x00,0x7D,0xAD,0xC8,0x56,0x11,0x03,0x30,0xF0, +0x00,0x7D,0x50,0xAD,0xD4,0xDF,0x6E,0xDD,0x5B,0xFB,0x02,0xCF,0xCF,0x01,0xE8,0x50, +0x17,0xD1,0x50,0x8F,0x10,0x09,0x00,0x00,0x12,0x34,0xD5,0x88,0x12,0x30,0xD5,0x68, +0x13,0x2C,0x7D,0xAD,0xC0,0x52,0x11,0xB8,0x7C,0xAD,0xE0,0xB4,0xAD,0xE8,0xD5,0x68, +0x12,0xB6,0xD5,0x56,0x14,0xC8,0x7D,0x52,0xAD,0xD4,0xD4,0x52,0xD5,0xAD,0xD4,0x14, +0xC4,0xD1,0x6C,0x07,0x19,0x03,0xC0,0x02,0x6E,0xFA,0x6E,0xCF,0xEF,0x03,0x04,0x3C, +0x0C,0x7D,0xAC,0x0C,0x5A,0xC3,0x8F,0x18,0x02,0x00,0x00,0x5A,0x50,0x19,0x5A,0xB0, +0x01,0x6B,0x3C,0x8F,0x18,0x02,0xAB,0x04,0x3C,0x8F,0x18,0x02,0xAB,0x08,0xC5,0x24, +0xAC,0x14,0x51,0xC2,0x51,0x50,0x19,0x41,0xC1,0x8F,0x18,0x02,0x00,0x00,0x51,0xAB, +0x0C,0xD0,0xAB,0x0C,0xAB,0x10,0x78,0x09,0xAC,0x18,0x51,0xD1,0x51,0x50,0x15,0x08, +0xCB,0x8F,0xFF,0x01,0x00,0x00,0x50,0x51,0xC1,0x51,0xAB,0x10,0xAB,0x14,0xD0,0xCF, +0x26,0xCD,0xBC,0x04,0xDF,0xAB,0x18,0xD4,0x7E,0xDD,0xBC,0x04,0xFB,0x03,0xCF,0xAC, +0x00,0xE9,0x50,0x08,0x7D,0x5A,0xCF,0xB3,0xCC,0xD0,0x01,0x50,0x04,0x00,0x00,0xD0, +0xCF,0xAD,0xCC,0x50,0xD0,0xA0,0x08,0xA0,0x0C,0xD0,0xA0,0x10,0xA0,0x14,0xD0,0xA0, +0x10,0xCF,0x98,0xCC,0xD0,0x01,0x50,0x04,0xD1,0x50,0x03,0x15,0x03,0x31,0x4F,0x02, +0xB0,0x8F,0x30,0x30,0x82,0x90,0x30,0x82,0x11,0x03,0x90,0x71,0x72,0xF4,0x50,0xFA, +0x05,0x3A,0x2E,0x56,0x67,0xC3,0x01,0x50,0x56,0xC3,0x57,0x51,0x50,0xD0,0x57,0x51, +0x9E,0x40,0xA1,0x01,0x57,0xBB,0x07,0xD1,0x50,0x09,0x15,0x03,0x31,0x20,0x02,0x3A, +0x2C,0x50,0x61,0x13,0x39,0xDD,0x50,0xC3,0x50,0xAE,0x04,0x50,0xDE,0xAD,0xEA,0x52, +0x10,0xB6,0xC3,0x01,0x8E,0x50,0xC1,0x8E,0x8E,0x51,0xDE,0xAD,0xED,0x52,0x10,0xA8, +0xBA,0x04,0xD0,0x06,0x50,0x9E,0x40,0xAD,0xEA,0x51,0xD0,0x8F,0x2E,0x44,0x49,0x52, +0x81,0xB0,0x8F,0x3B,0x31,0x61,0x9E,0xAD,0xEA,0x51,0xC0,0x06,0x50,0x05,0xBB,0x38, +0x28,0xAE,0x0C,0xBE,0x10,0xAD,0xEA,0xBA,0x38,0xBA,0x07,0x11,0xD8,0x1C,0x00,0xD0, +0xAC,0x0C,0x53,0x9C,0x09,0x01,0x7E,0x3C,0x21,0x7E,0xDD,0x53,0xDD,0x01,0xDD,0xAC, +0x04,0xDD,0x05,0xFA,0x6E,0xCF,0x25,0xFC,0xE9,0x50,0x5B,0xD0,0x53,0x51,0x3C,0x1D, +0x50,0x30,0xB0,0x04,0xD0,0x53,0x51,0x30,0xA5,0x04,0x91,0xA3,0x0D,0x02,0x12,0x47, +0xD0,0xA3,0x18,0x51,0x3C,0xA3,0x20,0x50,0xD0,0xA3,0x1C,0x54,0xA5,0x04,0xA3,0x0E, +0x54,0xA0,0x50,0x54,0xC1,0x50,0x51,0xAE,0x08,0xFB,0x8E,0xCF,0xEF,0xFB,0xE9,0x50, +0x25,0xD0,0x53,0x51,0xD4,0x7E,0xDD,0x8F,0x01,0x00,0x01,0x00,0xD0,0x5E,0x50,0x30, +0x3D,0x04,0xB0,0x54,0xC3,0xFE,0x01,0x78,0x8F,0xF0,0x54,0x54,0x13,0x05,0xE2,0x0A, +0xA3,0x06,0x00,0x3C,0x01,0x50,0x04,0x3C,0x8F,0xC0,0x08,0x50,0x04,0xFC,0x0F,0xC2, +0x30,0x5E,0x2C,0x00,0x6E,0x00,0x30,0x6E,0xD4,0x5B,0xD1,0x6C,0x02,0x12,0x08,0xD0, +0xAC,0x04,0x5B,0xD0,0xAC,0x08,0x5C,0xD5,0x5B,0x13,0x66,0x28,0x06,0xBC,0x18,0x6E, +0x7D,0xBC,0x08,0x50,0xD1,0x50,0x06,0x15,0x25,0xD1,0x50,0x0F,0x14,0x20,0xC1,0x50, +0x51,0x52,0xB1,0x8F,0x3B,0x31,0x72,0x12,0x15,0xD1,0x8F,0x2E,0x44,0x49,0x52,0x72, +0x12,0x0C,0xC2,0x06,0x50,0x90,0x50,0xAD,0xD6,0x28,0x50,0x61,0xAD,0xD7,0xC1,0xAB, +0x04,0x5B,0x58,0xC1,0xAB,0x08,0x5B,0x59,0x11,0x1E,0x29,0x10,0xAD,0xD0,0x68,0x12, +0x06,0xD0,0x58,0xAD,0xF4,0x11,0x1E,0xD1,0x50,0x0A,0x14,0x09,0xD0,0x58,0xAD,0xF4, +0x95,0xAD,0xD6,0x13,0x10,0xC0,0x24,0x58,0xD1,0x58,0x59,0x1F,0xDD,0xD0,0xAD,0xF4, +0x58,0x13,0x3E,0x11,0x0F,0xD5,0xA8,0x1E,0x13,0x0A,0x28,0x06,0xA8,0x1E,0xBC,0x18, +0x3C,0x01,0x50,0x04,0x7D,0xA8,0x10,0xAD,0xE0,0xD0,0xA8,0x18,0xAD,0xE8,0xB0,0xA8, +0x1C,0xAD,0xEC,0xC3,0x01,0xAD,0xE4,0x56,0x3C,0xAD,0xE0,0x57,0x3C,0xAD,0xEC,0xAD, +0xFC,0xC1,0xAD,0xE8,0x5B,0xAD,0xF8,0xC0,0x56,0x57,0x31,0xA4,0x00,0x31,0x89,0x00, +0x04,0xFA,0x6C,0xCF,0x97,0x01,0xE9,0x50,0xF7,0xD0,0xAC,0x10,0x55,0xC3,0x01,0xBC, +0x14,0x56,0x19,0xE9,0xDE,0xA5,0x14,0x54,0x9C,0x10,0xA4,0x08,0x57,0xB5,0xA4,0x0C, +0x12,0x02,0xD7,0x57,0xC1,0x01,0x56,0xAD,0xE4,0xB0,0x57,0xAD,0xE0,0x90,0x01,0xAD, +0xE2,0xD5,0x5B,0x13,0x4F,0xC3,0xAB,0x10,0xAB,0x14,0x52,0x78,0x8F,0xF7,0x52,0x52, +0x13,0x42,0xD1,0x52,0x57,0x15,0x03,0xD0,0x57,0x52,0xD0,0xAB,0x10,0x53,0x78,0x09, +0x52,0x7E,0x3C,0x21,0x7E,0x9F,0x43,0x6B,0xDD,0xAD,0xE4,0xDD,0xAC,0x04,0xFB,0x05, +0xCF,0x9A,0xFA,0xE9,0x50,0x1E,0xD0,0x52,0xAD,0xFC,0x9E,0x43,0x6B,0xAD,0xF8,0x95, +0xAD,0xD6,0x12,0x10,0xB0,0x52,0xAD,0xEC,0xD0,0x53,0xAD,0xE8,0x78,0x09,0x52,0x51, +0xC0,0x51,0xAB,0x10,0xC0,0x56,0x57,0x11,0x0C,0x3C,0x8F,0x28,0x08,0x50,0x04,0x3C, +0x8F,0x18,0x08,0x50,0x04,0xE1,0x0D,0xA5,0x34,0xEF,0xB1,0x8F,0x02,0x08,0x64,0x12, +0xE8,0x7D,0xBC,0x08,0x58,0xD4,0x5A,0x7D,0x58,0x53,0x3A,0x2E,0x53,0x64,0x13,0x07, +0x9E,0x70,0x53,0x9E,0xA1,0x01,0x54,0x3A,0x3B,0x53,0x64,0x12,0x06,0x3A,0x2E,0x53, +0x64,0x13,0x32,0xC2,0x50,0x58,0xDF,0x7E,0x9F,0xA1,0x01,0x9F,0x70,0xFB,0x03,0xCF, +0xBA,0xF9,0xE9,0x50,0xBA,0xD0,0x8E,0x5A,0x11,0x1B,0x9A,0xA5,0x05,0x54,0x2D,0x58, +0x69,0x00,0x54,0xA5,0x06,0x13,0x1D,0x19,0x10,0x3C,0x65,0x50,0x9E,0x40,0xA5,0x02, +0x55,0xB5,0x65,0x14,0xE5,0xF3,0x57,0x56,0x06,0x3C,0x8F,0x10,0x09,0x50,0x04,0x30, +0x6D,0x00,0x11,0xED,0xD6,0x54,0xCA,0x01,0x54,0x9E,0x44,0xA5,0x06,0x53,0x3C,0x65, +0x50,0x9E,0x40,0xA5,0x02,0x55,0xD5,0x5A,0x13,0x11,0xB1,0x5A,0x63,0x13,0x0C,0x1A, +0xD8,0xC0,0x08,0x53,0xD1,0x53,0x55,0x1F,0xF1,0x11,0xC6,0x7D,0xA3,0x02,0x56,0x3C, +0x57,0x57,0x28,0x06,0xA3,0x02,0xBC,0x18,0xD5,0x5B,0x13,0x2C,0x95,0xAD,0xD6,0x12, +0x07,0xD5,0xAD,0xF4,0x12,0x22,0x11,0x08,0xD0,0x56,0xAD,0xEE,0xB0,0x57,0xAD,0xF2, +0xD0,0xAB,0x08,0x58,0xC1,0x24,0x58,0x50,0xD1,0x50,0xAB,0x0C,0x14,0x0A,0xD0,0x50, +0xAB,0x08,0x28,0x24,0xAD,0xD0,0x48,0x6B,0x3C,0x01,0x50,0x04,0x31,0x1A,0xFF,0xD5, +0xAD,0xFC,0x13,0x0E,0xD7,0xAD,0xFC,0xD0,0xAD,0xF8,0x55,0xDE,0xC5,0x00,0x02,0xAD, +0xF8,0x05,0xD0,0xAC,0x10,0x55,0x9C,0x09,0x01,0x7E,0x3C,0x21,0x7E,0xDF,0x65,0xDD, +0x56,0xDD,0xAC,0x04,0xFB,0x05,0xCF,0x64,0xF9,0xE8,0x50,0xE5,0x04,0x7C,0x00,0xCE, +0x01,0x7E,0xD4,0x7E,0x7C,0x7E,0xD1,0x6C,0x08,0x19,0x0D,0xD0,0xAC,0x20,0x50,0x13, +0x07,0x7D,0x60,0xAD,0xF0,0xD4,0xBC,0x1C,0x7D,0xAC,0x0C,0x52,0x7D,0xAC,0x14,0x54, +0x7D,0x65,0x7E,0xD0,0x5E,0x55,0x7E,0x7E,0x56,0x7C,0x64,0xD7,0x64,0x3C,0x65,0x7E, +0xE1,0x0A,0xA2,0x06,0x05,0x90,0xA5,0x05,0xAE,0x02,0xD0,0x8E,0x50,0x12,0x03,0x31, +0x6D,0x00,0x3C,0xC2,0xFE,0x01,0x51,0xC0,0x51,0x50,0xD6,0xAD,0xFC,0xDF,0x62,0xDF, +0x63,0xDD,0x50,0xDD,0xAC,0x04,0xFB,0x04,0xCF,0x6D,0x00,0xE9,0x50,0x62,0xE0,0x1F, +0xAD,0xFC,0x5D,0xD0,0x55,0x50,0xD0,0x53,0x51,0x30,0x53,0x01,0xD0,0xAC,0x0C,0x52, +0xDF,0xAD,0xF0,0xDF,0xAD,0xF8,0xDD,0x56,0xDD,0x53,0xFB,0x04,0xCF,0xDC,0x00,0xD0, +0xAD,0xF8,0x51,0x13,0x16,0xC0,0x51,0xBC,0x1C,0xD1,0x51,0xAD,0xF0,0x15,0x04,0xD0, +0xAD,0xF0,0x51,0xC0,0x51,0xAD,0xF4,0xC2,0x51,0xAD,0xF0,0xD2,0x64,0x51,0x12,0x03, +0xD0,0x66,0x64,0xC0,0xA6,0x04,0xA4,0x04,0x7D,0xA3,0x0E,0x65,0x31,0x7E,0xFF,0xD5, +0xAD,0xFC,0x15,0x09,0x7D,0xBC,0x18,0x65,0xE3,0x1F,0xAD,0xFC,0xEF,0x3C,0x01,0x50, +0x04,0x3C,0x00,0x3C,0x20,0x7E,0x11,0x05,0x3C,0x00,0x3C,0x21,0x7E,0xD0,0xAC,0x10, +0x55,0x10,0x31,0xC3,0x01,0xAC,0x08,0x53,0x10,0x3B,0xD1,0x53,0x50,0x19,0x0E,0xC2, +0x50,0x53,0xD1,0x54,0x55,0x1F,0xF1,0x3C,0x8F,0x70,0x08,0x50,0x04,0x9C,0x09,0x01, +0x7E,0xDD,0xAD,0xFC,0xDD,0xAC,0x0C,0xC1,0x53,0x51,0x7E,0xDD,0xAC,0x04,0xFB,0x05, +0xCF,0x5A,0xF8,0x04,0x9A,0xA5,0x01,0x50,0x3E,0x40,0x65,0x54,0x9A,0xA5,0x3A,0x55, +0x3E,0x45,0x64,0x55,0x05,0xEF,0x0E,0x02,0x64,0x50,0xAF,0x50,0x00,0x02,0x14,0x00, +0x1D,0x00,0x30,0x00,0x9C,0x10,0x84,0x50,0xF0,0x00,0x1E,0x02,0x50,0xD0,0x84,0x51, +0x11,0x19,0xCE,0x01,0x51,0xC0,0x02,0x54,0xD4,0x50,0x05,0xD0,0x84,0x50,0xEF,0x08, +0x06,0x50,0x51,0x79,0x10,0x50,0x50,0x9A,0xA4,0xFC,0x50,0xD6,0x50,0x05,0x3C,0x84, +0x50,0xEF,0x00,0x0E,0x50,0x50,0xD0,0x84,0x51,0x11,0xF0,0xFC,0x00,0x7C,0x56,0xD1, +0x6C,0x04,0x19,0x0F,0xD0,0xAC,0x10,0x50,0x13,0x09,0x7D,0x60,0x56,0xCA,0x07,0x56, +0xD4,0xBC,0x0C,0xD0,0xAC,0x04,0x55,0x9A,0xA5,0x34,0x50,0xEF,0x07,0x01,0x50,0x7E, +0x30,0x81,0xFF,0xD4,0x53,0x11,0x29,0x30,0x8B,0xFF,0xD5,0x53,0x12,0x0A,0xD5,0x50, +0x13,0x1E,0xE9,0x6E,0x03,0xD0,0x51,0x6E,0xC0,0x50,0x53,0xD5,0x56,0x13,0x09,0xD0, +0x50,0x87,0xD0,0x51,0x87,0xC2,0x08,0x56,0xD5,0x57,0x13,0x04,0xC0,0x08,0xBC,0x0C, +0xD1,0x54,0x55,0x1F,0xD2,0xBA,0x04,0x7D,0x52,0xBC,0x08,0x3C,0x01,0x50,0x04,0x91, +0xA1,0x07,0x02,0x12,0x1E,0x3C,0xA1,0x0C,0x7E,0xD0,0xA1,0x08,0x7E,0xB5,0x6E,0x13, +0x18,0xD1,0x80,0x8E,0x12,0x0D,0xD5,0x6E,0x19,0x05,0xB1,0x60,0x6E,0x12,0x04,0xBA, +0x01,0x11,0x0C,0x3C,0x8F,0x10,0x08,0x50,0x04,0x3C,0x8F,0x10,0x09,0x50,0x04,0x3C, +0x8F,0xFF,0x00,0x50,0xD4,0x52,0xA0,0x81,0x52,0xF5,0x50,0xFA,0xB1,0x52,0x61,0x12, +0x01,0x05,0x3C,0x8F,0x08,0x08,0x50,0x04,0x00,0x00,0x00,0x00,0x48,0x44,0x52,0x31, +0x48,0x44,0x52,0x32,0x30,0x30,0x35,0x31,0x32,0xFC,0x08,0xD0,0xCF,0x69,0xC7,0xBC, +0x04,0xD0,0xAC,0x08,0x50,0x13,0x27,0x7D,0x60,0x52,0x3A,0x3A,0x52,0x63,0x13,0x07, +0x9E,0xA1,0x01,0x53,0x9E,0x70,0x52,0x91,0x8F,0x5B,0x63,0x13,0x05,0x91,0x3C,0x63, +0x12,0x1B,0x81,0x02,0x83,0x50,0xD7,0x52,0x3A,0x50,0x52,0x63,0x12,0x08,0x3C,0x8F, +0x18,0x08,0x50,0x31,0x8B,0x00,0x9E,0xA1,0x01,0x53,0x9E,0x70,0x52,0x3A,0x3B,0x52, +0x63,0x13,0x04,0xC3,0x53,0x51,0x52,0x7D,0x52,0x56,0xD0,0xBC,0x04,0x5B,0x3C,0x01, +0x50,0xE0,0x03,0xAB,0x30,0x6B,0x30,0xCE,0x00,0xE9,0x50,0x65,0x30,0x65,0x01,0xE9, +0x50,0x5F,0xDF,0xAF,0x83,0xDD,0xBC,0x04,0xDD,0x00,0x3C,0x25,0x7E,0xDD,0x01,0xDD, +0x00,0xDD,0x00,0xFB,0x07,0xCF,0x9E,0x01,0xD0,0xAC,0x14,0x52,0xD0,0xCF,0x68,0xFF, +0x62,0xDF,0xCF,0x63,0xFF,0xDD,0xBC,0x04,0xDD,0x00,0x3C,0x25,0x7E,0xDD,0x01,0xDD, +0x00,0xDD,0x00,0xFB,0x07,0xCF,0x7E,0x01,0xC3,0x62,0xCF,0x4B,0xFF,0xA2,0x04,0xD7, +0xA2,0x04,0xD1,0x6C,0x07,0x19,0x1A,0xD0,0xAC,0x1C,0x53,0x13,0x14,0xD1,0x08,0x63, +0x14,0x0F,0xD0,0x08,0xBC,0x18,0xD0,0xA3,0x04,0x53,0xD0,0xA2,0x04,0x83,0xD0,0x62, +0x63,0x04,0xDF,0xCF,0x22,0xFF,0xDD,0xBC,0x04,0xDD,0x00,0x3C,0x26,0x7E,0xCE,0x01, +0x7E,0xDD,0x00,0xDD,0x00,0xFB,0x07,0xCF,0x3C,0x01,0xD0,0xAC,0x14,0x52,0xD0,0xCF, +0x06,0xFF,0x62,0xDF,0xCF,0x01,0xFF,0xDD,0xBC,0x04,0xDD,0x00,0x3C,0x25,0x7E,0xDD, +0x01,0xDD,0x00,0xDD,0x00,0xFB,0x07,0xCF,0x1C,0x01,0xC3,0x62,0xCF,0xE9,0xFE,0xA2, +0x04,0xD7,0xA2,0x04,0xD1,0x6C,0x07,0x19,0x1A,0xD0,0xAC,0x1C,0x53,0x13,0x14,0xD1, +0x08,0x63,0x14,0x0F,0xD0,0x08,0xBC,0x18,0xD0,0xA3,0x04,0x53,0xD0,0xA2,0x04,0x83, +0xD0,0x62,0x63,0x3C,0x01,0x50,0x04,0xD0,0xAC,0x10,0x52,0xDF,0xCF,0xB9,0xFE,0x9C, +0x09,0x01,0x7E,0x3C,0x21,0x7E,0xDF,0x62,0xCE,0x01,0x7E,0xDD,0xBC,0x04,0xFB,0x06, +0xCF,0x02,0x01,0xE9,0x50,0x71,0x78,0x8F,0xF0,0x50,0x51,0xB1,0x51,0x8F,0x50,0x00, +0x13,0x35,0xB1,0x51,0x8F,0x00,0x02,0x12,0x65,0x31,0x66,0xFF,0xD0,0xAC,0x10,0x52, +0xDF,0xCF,0x84,0xFE,0x9C,0x09,0x01,0x7E,0x3C,0x21,0x7E,0xDF,0x62,0xCE,0x01,0x7E, +0xDD,0xBC,0x04,0xFB,0x06,0xCF,0xCD,0x00,0xE9,0x50,0x3C,0x78,0x8F,0xF0,0x50,0x51, +0xB1,0x51,0x8F,0x50,0x00,0x12,0x37,0xD1,0x62,0xCF,0x60,0xFE,0x12,0xCE,0x29,0x56, +0x67,0xA2,0x04,0x13,0x19,0xDF,0xCF,0x4F,0xFE,0xDD,0xBC,0x04,0xDD,0x00,0x3C,0x25, +0x7E,0xDD,0x03,0xDD,0x00,0xDD,0x00,0xFB,0x07,0xCF,0x6A,0x00,0x11,0xAE,0x91,0x20, +0x63,0x12,0xE2,0x3C,0x01,0x50,0x05,0xB1,0x8F,0x70,0x08,0x50,0x12,0x05,0x3C,0x8F, +0xC0,0x08,0x50,0x05,0xD0,0xAC,0x10,0x52,0xDF,0xCF,0x1C,0xFE,0x9C,0x09,0x01,0x7E, +0x3C,0x21,0x7E,0xDF,0x62,0xCE,0x01,0x7E,0xDD,0xBC,0x04,0xFB,0x06,0xCF,0x65,0x00, +0xE9,0x50,0xD4,0x78,0x8F,0xF0,0x50,0x51,0xB1,0x51,0x8F,0x50,0x00,0x12,0xCF,0xD1, +0x62,0xCF,0xFC,0xFD,0x12,0xC8,0x91,0x8F,0x46,0xA2,0x04,0x12,0xC1,0xD0,0x52,0x54, +0x29,0x05,0xA2,0x05,0xCF,0xED,0xFD,0x12,0xB5,0x29,0x05,0xA4,0x0A,0xCF,0xE4,0xFD, +0x12,0xAC,0x3C,0x01,0x50,0x05,0xFC,0x0F,0xD0,0xAC,0x18,0x59,0xD0,0xAC,0x0C,0x5B, +0xD0,0xAC,0x1C,0x54,0xDB,0x38,0x51,0xD0,0x41,0xA9,0x5C,0x53,0xD0,0x41,0xA9,0x54, +0x57,0xDD,0x05,0xD0,0x5B,0x55,0xD0,0xA9,0x34,0x50,0x16,0x40,0xB0,0x08,0xE8,0x50, +0x03,0xF5,0x6E,0xEF,0x04,0x00,0x00,0xDD,0xAC,0x18,0xDD,0xAC,0x04,0xD0,0x6E,0x50, +0xD0,0xA0,0x34,0x50,0xDD,0x00,0xDD,0xAC,0x10,0xDD,0xAC,0x08,0xDD,0xAC,0x14,0xDF, +0xBC,0x0C,0xFB,0x07,0x40,0xB0,0x00,0x04,0x8F,0x6E,0x29,0x12,0x39,0x00,0x26,0x00, +0x26,0x00,0x26,0x00,0x4C,0x00,0x26,0x00,0x26,0x00,0x26,0x00,0x26,0x00,0x26,0x00, +0x26,0x00,0x26,0x00,0x26,0x00,0x26,0x00,0x26,0x00,0x26,0x00,0x26,0x00,0x68,0x00, +0x26,0x00,0xDD,0x8F,0x01,0x00,0x00,0x00,0xDD,0x0D,0x00,0x00,0xDD,0x8F,0x02,0x00, +0x00,0x00,0xDD,0x04,0x00,0x3C,0xAE,0x08,0x50,0xD0,0xAE,0x0C,0x51,0xD0,0xAE,0x10, +0x53,0x9F,0xCF,0x33,0x00,0x31,0x3C,0x00,0x9C,0x10,0xAE,0x10,0x50,0xB0,0xAE,0x08, +0x50,0xD0,0xAE,0x0C,0x51,0x3C,0xAE,0x14,0x52,0xD0,0xAE,0x18,0x53,0x9F,0xCF,0x17, +0x00,0x31,0x41,0x00,0x9C,0x10,0xAE,0x08,0x50,0xB0,0xAE,0x0C,0x50,0xD0,0xAE,0x10, +0x51,0x9F,0xCF,0x03,0x00,0x31,0x7F,0x00,0xDC,0x7E,0xF0,0x8E,0x00,0x04,0xAE,0x2C, +0xC0,0x28,0x5E,0x02,0x3C,0x50,0x50,0x13,0x0D,0xDD,0x5A,0x91,0x83,0x81,0x12,0x0B, +0xF5,0x50,0xF8,0xD0,0x8E,0x5A,0xD4,0x52,0xD5,0x50,0x05,0xD0,0x8E,0x5A,0xD0,0x50, +0x52,0x91,0x71,0x73,0x05,0xDD,0x5A,0xDD,0x54,0x78,0x8F,0xF0,0x50,0x54,0x3C,0x50, +0x50,0x13,0x28,0x3C,0x52,0x52,0x13,0x14,0x91,0x81,0x83,0x12,0x32,0xF5,0x50,0x09, +0xD7,0x52,0x12,0x1C,0xBA,0x8F,0x10,0x04,0x05,0xF5,0x52,0xEC,0x91,0x81,0x54,0x12, +0x05,0xF5,0x50,0xF8,0x11,0xEE,0x91,0x71,0x54,0x11,0x17,0x3C,0x52,0x52,0x13,0xE4, +0x91,0x54,0x83,0x12,0x05,0xF5,0x52,0xF8,0x11,0xDA,0x91,0x54,0x73,0x11,0x03,0x91, +0x71,0x73,0xBA,0x8F,0x10,0x04,0x05,0xDD,0x5A,0xDD,0x52,0x78,0x8F,0xF0,0x50,0x52, +0x3C,0x50,0x50,0x13,0x08,0x91,0x52,0x81,0x13,0x0A,0xF5,0x50,0xF8,0xBA,0x8F,0x04, +0x04,0xD5,0x50,0x05,0xD7,0x51,0x11,0xF5,0xFF,0xFF,0xFF,0xFF,0xC4,0xC9,0x44,0x24, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0xBB,0x00,0xC2,0x12,0x5E,0xB0,0x00,0xAE,0x08,0x9E,0xAF,0x23,0xAE,0x00,0xD0, +0x9F,0x18,0x05,0x14,0x20,0xAE,0x04,0xD0,0x9F,0x06,0x05,0x14,0x20,0xAE,0x0A,0xD0, +0x9F,0x0A,0x05,0x14,0x20,0xAE,0x0E,0xD0,0x5E,0x9F,0x18,0x05,0x14,0x20,0x11,0x19, +0x3C,0x9F,0x78,0x07,0x14,0x20,0x50,0xD4,0x9F,0x06,0x05,0x14,0x20,0xD4,0x9F,0x0A, +0x05,0x14,0x20,0x78,0x0C,0x50,0x50,0x11,0x07,0xD0,0x9F,0x48,0x01,0x08,0x20,0x50, +0xD0,0x9F,0x18,0x05,0x14,0x20,0x5E,0xD0,0xAE,0x04,0x9F,0x18,0x05,0x14,0x20,0xD0, +0xAE,0x0A,0x9F,0x06,0x05,0x14,0x20,0xD0,0xAE,0x0E,0x9F,0x0A,0x05,0x14,0x20,0xC0, +0x10,0x5E,0xBA,0x8E,0x05,0x3C,0x9F,0x78,0x07,0x14,0x20,0x56,0x12,0x11,0xD0,0x50, +0x7E,0x16,0xEF,0x7A,0xFF,0xFF,0xFF,0x78,0x8F,0xF4,0x50,0x56,0xD0,0x8E,0x50,0xC0, +0x9F,0x74,0x07,0x14,0x20,0x56,0x05,0xDD,0x9F,0x74,0x07,0x14,0x20,0xD0,0xAE,0x04, +0x7E,0x3C,0x9F,0x78,0x07,0x14,0x20,0xAE,0x08,0x12,0x12,0xD0,0x50,0x7E,0x16,0xEF, +0x4D,0xFF,0xFF,0xFF,0x78,0x8F,0xF4,0x50,0xAE,0x0C,0xD0,0x8E,0x50,0x01,0x05,0x9E, +0xEF,0x0A,0x0E,0xFF,0xFF,0x7E,0xD0,0xAE,0x04,0x7E,0xD0,0x50,0x7E,0x16,0xEF,0x2E, +0xFF,0xFF,0xFF,0x78,0x8F,0xEC,0x50,0xAE,0x0C,0xD0,0x8E,0x50,0x05,0x3C,0x9F,0x78, +0x07,0x14,0x20,0x53,0x12,0x11,0xD0,0x50,0x7E,0x16,0xEF,0x12,0xFF,0xFF,0xFF,0x78, +0x8F,0xF4,0x50,0x53,0xD0,0x8E,0x50,0x05,0x01,0x3C,0x9F,0x78,0x07,0x14,0x20,0x55, +0x12,0x11,0xD0,0x50,0x7E,0x16,0xEF,0xF6,0xFE,0xFF,0xFF,0x78,0x8F,0xF4,0x50,0x55, +0xD0,0x8E,0x50,0x78,0x03,0x55,0x55,0x05,0x3C,0x9F,0x78,0x07,0x14,0x20,0x53,0x12, +0x11,0xD0,0x50,0x7E,0x16,0xEF,0xD7,0xFE,0xFF,0xFF,0x78,0x8F,0xF4,0x50,0x53,0xD0, +0x8E,0x50,0x78,0x0C,0x53,0x53,0x05,0xD0,0xAB,0x1C,0x52,0x3C,0xAB,0x20,0x51,0x12, +0x0C,0x78,0x8F,0xF4,0x9F,0x48,0x01,0x08,0x20,0x51,0x31,0xF0,0x00,0xD1,0x51,0x8F, +0x00,0x40,0x00,0x00,0x19,0xF4,0x16,0xEF,0xA5,0xFE,0xFF,0xFF,0x78,0x8F,0xF4,0x50, +0x51,0xD1,0x51,0x8F,0x00,0x40,0x00,0x00,0x13,0xE0,0xD0,0x9F,0x48,0x01,0x08,0x20, +0x51,0xC3,0x8F,0x00,0xC0,0x00,0x00,0x51,0x53,0xC0,0x8F,0x00,0x40,0x00,0x00,0x52, +0xD4,0x50,0x90,0x40,0x62,0x40,0x63,0xF2,0x8F,0x00,0xC0,0x00,0x00,0x50,0xF3,0x9E, +0xC3,0x00,0x40,0xAB,0x24,0x9E,0xC3,0x00,0x40,0x9F,0x10,0x00,0x08,0x20,0xC3,0x8F, +0x00,0x02,0x00,0x00,0xAB,0x24,0x51,0x9E,0xA1,0x08,0xA1,0x04,0x78,0x8F,0xF4,0x9F, +0x48,0x01,0x08,0x20,0x51,0xC3,0x51,0x53,0x52,0xD0,0x52,0xAB,0x1C,0x78,0x8F,0xF7, +0x9F,0x48,0x01,0x08,0x20,0x51,0x78,0x8F,0xF4,0x9F,0x48,0x01,0x08,0x20,0x50,0xC0, +0x8F,0xFF,0xC1,0x00,0x00,0x50,0x78,0x8F,0xF7,0x50,0x50,0xC2,0x50,0x51,0xD4,0x50, +0xDD,0x51,0x78,0x8F,0xFB,0x51,0x51,0xD0,0x8F,0xFF,0xFF,0xFF,0xFF,0x40,0x62,0xF2, +0x51,0x50,0xF4,0x78,0x05,0x50,0x50,0xD0,0x8E,0x51,0xD1,0x50,0x51,0x18,0x08,0xE2, +0x50,0x62,0x00,0xF2,0x51,0x50,0xF8,0x78,0x8F,0xF7,0x9F,0x48,0x01,0x08,0x20,0x51, +0xE5,0x50,0x62,0x00,0xF2,0x51,0x50,0xF8,0xD4,0x50,0x78,0x8F,0xF4,0x9F,0x48,0x01, +0x08,0x20,0x51,0x16,0x9F,0x6A,0x1C,0x04,0x20,0xB0,0x50,0xAB,0x22,0xB4,0xAB,0x20, +0x78,0x8F,0xF4,0x9F,0x48,0x01,0x08,0x20,0x51,0xD0,0xAB,0x1C,0x52,0x17,0xEF,0x54, +0xFE,0xFF,0xFF,0x3C,0x9F,0x78,0x07,0x14,0x20,0x9F,0xBA,0x04,0x14,0x20,0x12,0x15, +0xD0,0x50,0x7E,0x16,0xEF,0xA8,0xFD,0xFF,0xFF,0x78,0x8F,0xF4,0x50,0x9F,0xBA,0x04, +0x14,0x20,0xD0,0x8E,0x50,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,}; diff --git a/src/VAX/vax_mmu.cpp b/src/VAX/vax_mmu.cpp new file mode 100644 index 0000000..f603f1f --- /dev/null +++ b/src/VAX/vax_mmu.cpp @@ -0,0 +1,848 @@ +/* vax_mmu.c - VAX memory management + + Copyright (c) 1998-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. + + 21-Jul-08 RMS Removed inlining support + 28-May-08 RMS Inlined physical memory routines + 29-Apr-07 RMS Added address masking for system page table reads + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 30-Sep-04 RMS Comment and formating changes + 19-Sep-03 RMS Fixed upper/lower case linkage problems on VMS + 01-Jun-03 RMS Fixed compilation problem with USE_ADDR64 + + This module contains the instruction simulators for + + Read - read virtual + Write - write virtual + ReadL(P) - read aligned physical longword (physical context) + WriteL(P) - write aligned physical longword (physical context) + ReadB(W) - read aligned physical byte (word) + WriteB(W) - write aligned physical byte (word) + Test - test acccess + + zap_tb - clear TB + zap_tb_ent - clear TB entry + chk_tb_ent - check TB entry + set_map_reg - set up working map registers +*/ + +#include "sim_defs.h" +#include "vax_defs.h" + +extern const uint32 align[4]; + +static const int32 insert[4] = { + 0x00000000, 0x000000FF, 0x0000FFFF, 0x00FFFFFF +}; +static const int32 cvtacc[16] = { 0, 0, + TLB_ACCW (KERN)+TLB_ACCR (KERN), + TLB_ACCR (KERN), + TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCW (SUPV)+TLB_ACCW (USER)+ + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER), + TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCR (KERN)+TLB_ACCR (EXEC), + TLB_ACCW (KERN)+TLB_ACCR (KERN)+TLB_ACCR (EXEC), + TLB_ACCR (KERN)+TLB_ACCR (EXEC), + TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCW (SUPV)+ + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV), + TLB_ACCW (KERN)+TLB_ACCW (EXEC)+ + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV), + TLB_ACCW (KERN)+TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV), + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV), + TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCW (SUPV)+ + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER), + TLB_ACCW (KERN)+TLB_ACCW (EXEC)+ + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER), + TLB_ACCW (KERN)+ + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER), + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER) +}; + +t_stat tlb_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat tlb_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat tlb_reset (DEVICE *dptr); + +static TLBENT fill (RUN_DECL, uint32 va, int32 acc, int32 *stat); +static void Write_Uncommon (RUN_DECL, uint32 va, int32 pa, int32 pa1_pagebase, int32 val, int32 lnt, int32 acc); + +/* TLB data structures + + tlb_dev pager device descriptor + tlb_unit pager units + pager_reg pager register list +*/ + +UNIT* tlb_unit[] = { + UDATA (NULL, UNIT_FIX, VA_TBSIZE * 2), + UDATA (NULL, UNIT_FIX, VA_TBSIZE * 2) +}; + +REG tlb_reg[] = { + { NULL } +}; + +DEVICE tlb_dev = { + "TLB", tlb_unit, tlb_reg, NULL, + 2, 16, VA_N_TBI * 2, 1, 16, 32, + &tlb_ex, &tlb_dep, &tlb_reset, + NULL, NULL, NULL, + NULL, DEV_PERCPU +}; + + +/* Read and write virtual + + These routines logically fall into three phases: + + 1. Look up the virtual address in the translation buffer, calling + the fill routine on a tag mismatch or access mismatch (invalid + tlb entries have access = 0 and thus always mismatch). The + fill routine handles all errors. If the resulting physical + address is aligned, do an aligned physical read or write. + 2. Test for unaligned across page boundaries. If cross page, look + up the physical address of the second page. If not cross page, + the second physical address is the same as the first. + 3. Using the two physical addresses, do an unaligned read or + write, with three cases: unaligned long, unaligned word within + a longword, unaligned word crossing a longword boundary. + + Note that these routines do not handle quad or octa references. +*/ + +/* Read virtual + + Inputs: + va = virtual address + lnt = length code (BWL) + acc = access code (KESU) + Output: + returned data, right justified in 32b longword +*/ + +int32 Read (RUN_DECL, uint32 va, int32 lnt, int32 acc) +{ + int32 vpn, off, tbi, pa; + int32 pa1, bo, sc, wl, wh; + TLBENT xpte; + + mchk_va = va; + + if (mapen) /* mapping on? */ + { + vpn = VA_GETVPN (va); /* get vpn, offset */ + off = VA_GETOFF (va); + tbi = VA_GETTBI (vpn); + xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ + if (((xpte.pte & acc) == 0) || (xpte.tag != vpn) || + ((acc & TLB_WACC) && ((xpte.pte & TLB_M) == 0))) + xpte = fill (RUN_PASS, va, acc, NULL); /* fill if needed */ + pa = (xpte.pte & TLB_PFN) | off; /* get phys addr */ + } + else + { + pa = va & PAMASK; + off = VA_GETOFF (va); /* unused, only to suppress false GCC warning */ + } + + if ((pa & (lnt - 1)) == 0) /* aligned? */ + { + if (lnt >= L_LONG) /* long, quad? */ + return ReadL (RUN_PASS, pa); + else if (lnt == L_WORD) /* word? */ + return ReadW (RUN_PASS, pa); + else + return ReadB (RUN_PASS, pa); /* byte */ + } + + if (mapen && (uint32) (off + lnt) > VA_PAGSIZE) /* cross page? */ + { + vpn = VA_GETVPN (va + lnt); /* vpn 2nd page */ + tbi = VA_GETTBI (vpn); + xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ + if (((xpte.pte & acc) == 0) || (xpte.tag != vpn) || + ((acc & TLB_WACC) && ((xpte.pte & TLB_M) == 0))) + xpte = fill (RUN_PASS, va + lnt, acc, NULL); /* fill if needed */ + pa1 = (xpte.pte & TLB_PFN) | VA_GETOFF (va + 4); + } + else + { + pa1 = (pa + 4) & PAMASK; /* not cross page */ + } + + bo = pa & 3; + + if (lnt >= L_LONG) /* lw unaligned? */ + { + sc = bo << 3; + wl = ReadL_NA (RUN_PASS, pa); /* read both lw */ + wh = ReadL_NA (RUN_PASS, pa1); /* extract */ + return ((((wl >> sc) & align[bo]) | (wh << (32 - sc))) & LMASK); + } + else if (bo == 1) + { + return ((ReadL_NA (RUN_PASS, pa) >> 8) & WMASK); + } + else + { + wl = ReadL_NA (RUN_PASS, pa); /* word cross lw */ + wh = ReadL_NA (RUN_PASS, pa1); /* read, extract */ + return (((wl >> 24) & 0xFF) | ((wh & 0xFF) << 8)); + } +} + +/* Write virtual + + Inputs: + va = virtual address + val = data to be written, right justified in 32b lw + lnt = length code (BWL) + acc = access code (KESU) + Output: + none +*/ + +void Write (RUN_DECL, uint32 va, int32 val, int32 lnt, int32 acc) +{ + int32 vpn, off, tbi, pa, pa1; + TLBENT xpte; + + mchk_va = va; + if (mapen) + { + vpn = VA_GETVPN (va); + off = VA_GETOFF (va); + tbi = VA_GETTBI (vpn); + xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ + if ((xpte.pte & acc) == 0 || xpte.tag != vpn || (xpte.pte & TLB_M) == 0) + { + xpte = fill (RUN_PASS, va, acc, NULL); + } + pa = (xpte.pte & TLB_PFN) | off; + } + else + { + pa = va & PAMASK; + off = VA_GETOFF (va); /* unused, only to suppress false GCC warning */ + } + + /* + * lnt can be L_WORD or L_LONG; or perhaps L_QUAD/L_OCTA but meaining here L_LONG + */ + if (lnt > L_LONG) + lnt = L_LONG; + + if ((pa & (lnt - 1)) == 0) /* aligned? */ + { + if (lnt >= L_LONG) /* long, quad? */ + WriteL (RUN_PASS, pa, val); + else if (lnt == L_WORD) /* word? */ + WriteW (RUN_PASS, pa, val); + else + WriteB (RUN_PASS, pa, val); /* byte */ + return; + } + + /* + * Non-aligned write: lnt can be L_WORD or L_LONG only + */ + +#if 0 + /* + * Original uniprocessor SIMH code that works regardless of the host endianness, + * but only in uniprocessor (single-threaded) case. + */ + + int32 bo, sc, wl, wh; + + if (mapen && (uint32) (off + lnt) > VA_PAGSIZE) + { + vpn = VA_GETVPN (va + 4); + tbi = VA_GETTBI (vpn); + xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ + if ((xpte.pte & acc) == 0 || xpte.tag != vpn || (xpte.pte & TLB_M) == 0) + { + xpte = fill (RUN_PASS, va + lnt, acc, NULL); + } + pa1 = (xpte.pte & TLB_PFN) | VA_GETOFF (va + 4); + } + else + { + pa1 = (pa + 4) & PAMASK; + } + + bo = pa & 3; + wl = ReadL_NA (RUN_PASS, pa); + + if (lnt >= L_LONG) + { + sc = bo << 3; + wh = ReadL_NA (RUN_PASS, pa1); + wl = (wl & insert[bo]) | ((val << sc) & LMASK); + wh = (wh & ~insert[bo]) | ((val >> (32 - sc)) & insert[bo]); + WriteL_NA (RUN_PASS, pa, wl); + WriteL_NA (RUN_PASS, pa1, wh); + } + else if (bo == 1) + { + wl = (wl & 0xFF0000FF) | (val << 8); + WriteL_NA (RUN_PASS, pa, wl); + } + else + { + wh = ReadL_NA (RUN_PASS, pa1); + wl = (wl & 0x00FFFFFF) | ((val & 0xFF) << 24); + wh = (wh & 0xFFFFFF00) | ((val >> 8) & 0xFF); + WriteL_NA (RUN_PASS, pa, wl); + WriteL_NA (RUN_PASS, pa1, wh); + } +#elif defined(__x86_32__) || defined(__x86_64__) + /* + * SMP case: though this is not formally required by VAX Architecture Standard, + * but nevertheless to be on the safe side do our best to + * avoid interfering with data adjacent to write region but outside it. + * This is what byte masking on SBI and other memory buses actually do. + * In x86/x64 case the simplest approach is non-aligned hardware write, wherever possible, + * on other machines that do not allow non-aligned access and/or may have + * different endianness than VAX, a series of split word/byte writes would be required. + */ + if (mapen && (uint32) (off + lnt) > VA_PAGSIZE) + { + vpn = VA_GETVPN (va + lnt - 1); + tbi = VA_GETTBI (vpn); + xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ + if ((xpte.pte & acc) == 0 || xpte.tag != vpn || (xpte.pte & TLB_M) == 0) + { + xpte = fill (RUN_PASS, va + lnt - 1, acc, NULL); + } + pa1 = xpte.pte & TLB_PFN; + if (!ADDR_IS_MEM(pa) || !ADDR_IS_MEM(pa1)) + { + Write_Uncommon (RUN_PASS, va, pa, pa1, val, lnt, acc); + return; + } + uint32 xval = (uint32) val; // just make it unsigned for less casts and braces below + if (lnt == L_WORD) + { + refoff(t_byte, M, pa) = (uint8) xval; + refoff(t_byte, M, pa1) = (uint8) (xval >> 8); + } + else switch ((off + lnt) & (VA_PAGSIZE - 1)) + { + /* writing a longword, how many bytes stick out into another page? */ + case 1: + /* three bytes on this (pa) side, one byte on the other (pa1) side */ + // byte to pa + // word to pa + 1 + // byte to pa1 + refoff(t_byte, M, pa) = (t_byte) xval; + refoff(uint16, M, pa + 1) = (uint16) (xval >> 8); + refoff(t_byte, M, pa1) = (t_byte) (xval >> 24); + break; + case 2: + /* two bytes on this (pa) side, two bytes on the other (pa1) side */ + // word to pa + // word to pa1 + refoff(uint16, M, pa) = (uint16) xval; + refoff(uint16, M, pa1) = (uint16) (xval >> 16); + break; + case 3: + /* one byte on this (pa) side, three bytes on the other (pa1) side */ + // byte to pa + // word to pa1 + // byte to pa1 + 2 + refoff(t_byte, M, pa) = (t_byte) xval; + refoff(uint16, M, pa1) = (uint16) (xval >> 8); + refoff(t_byte, M, pa1 + 2) = (t_byte) (xval >> 24); + break; + } + } + else /* datum does not cross page boundary */ + { + if (ADDR_IS_MEM(pa)) + { + /* + * Datum does not cross virtual page boundary, and hence is contiguous in physical memory. + * On x86/x64 store it with non-aligned hardware-level write. + */ + if (lnt == L_WORD) + { + refoff(uint16, M, pa) = (uint16) val; + } + else + { + refoff(uint32, M, pa) = (uint32) val; + } + } + else + { + Write_Uncommon (RUN_PASS, va, pa, -1, val, lnt, acc); + } + } +#else +# error Unimplemented +#endif +} + +/* + * Handles unaligned writes that are not wholly within memory region. + */ +static void Write_Uncommon (RUN_DECL, uint32 va, int32 pa, int32 pa1_pagebase, int32 val, int32 lnt, int32 acc) +{ + int32 bo, sc, wl, wh, pa1; + + if (mapen && VA_GETOFF(va) + lnt > VA_PAGSIZE) + { + pa1 = pa1_pagebase | VA_GETOFF (va + 4); + } + else + { + pa1 = (pa + 4) & PAMASK; + } + + bo = pa & 3; + wl = ReadL_NA (RUN_PASS, pa); + + if (lnt >= L_LONG) + { + sc = bo << 3; + wh = ReadL_NA (RUN_PASS, pa1); + wl = (wl & insert[bo]) | ((val << sc) & LMASK); + wh = (wh & ~insert[bo]) | ((val >> (32 - sc)) & insert[bo]); + WriteL_NA (RUN_PASS, pa, wl); + WriteL_NA (RUN_PASS, pa1, wh); + } + else if (bo == 1) + { + wl = (wl & 0xFF0000FF) | (val << 8); + WriteL_NA (RUN_PASS, pa, wl); + } + else + { + wh = ReadL_NA (RUN_PASS, pa1); + wl = (wl & 0x00FFFFFF) | ((val & 0xFF) << 24); + wh = (wh & 0xFFFFFF00) | ((val >> 8) & 0xFF); + WriteL_NA (RUN_PASS, pa, wl); + WriteL_NA (RUN_PASS, pa1, wh); + } +} + +/* + * Test access to a byte (VAX PROBEx) + * + * If "status" is not NULL, does not raise memory access exceptions (including page or + * page table not valid faults, and access violation). + * Instead on exception just returns condition in "*status" and return value as -1. + * On succes will return physical address corresponding to "va". + * + * If "status" is NULL, will raise and throw via ABORT any memory access exceptions + * encountered during access to memory location. + * If returns regular way, page is resident, access to "va" requested by "acc" is enabled. + * Return value is physical address corresponding to "va". + * + * Note that this routine may refill TLB entry and evict its previous content. + * Therefore it is potentially possible for the sequence + * + * Test (va1) + * Test (va2) + * Read (va1) or Write(va1) + * + * to fail if second Test evicts TLB entry for va1, and Read/Write then discovers + * that PTE for va1 had been modified compared to previously cached in TLB (or is not + * accessible anymore) and does not allow access to va1. + */ +int32 Test (RUN_DECL, uint32 va, int32 acc, int32 *status) +{ + int32 vpn, off, tbi; + TLBENT xpte; + + if (status) + *status = PR_OK; /* assume ok */ + + if (mapen) /* mapping on? */ + { + vpn = VA_GETVPN (va); /* get vpn, off */ + off = VA_GETOFF (va); + tbi = VA_GETTBI (vpn); + xpte = (va & VA_S0) ? stlb[tbi]: ptlb[tbi]; /* access tlb */ + if ((xpte.pte & acc) && xpte.tag == vpn) /* TB hit, acc ok? */ + return (xpte.pte & TLB_PFN) | off; + xpte = fill (RUN_PASS, va, acc, status); /* fill TB */ + if (status == NULL || *status == PR_OK) + return (xpte.pte & TLB_PFN) | off; + else + return -1; + } + return va & PAMASK; /* ret phys addr */ +} + +/* + * TestMark is identical to Test routine, except it will always mark PTE + * as Modified if write access is requested, whereas Test may mark + * sometimes but not always + */ +int32 TestMark (RUN_DECL, uint32 va, int32 acc, int32 *status) +{ + int32 vpn, off, tbi; + TLBENT xpte; + + if (status) + *status = PR_OK; /* assume ok */ + + if (mapen) /* mapping on? */ + { + vpn = VA_GETVPN (va); /* get vpn, off */ + off = VA_GETOFF (va); + tbi = VA_GETTBI (vpn); + xpte = (va & VA_S0) ? stlb[tbi]: ptlb[tbi]; /* access tlb */ + + if (acc & TLB_WACC) + { + if ((xpte.pte & acc) && xpte.tag == vpn && (xpte.pte & TLB_M)) + return (xpte.pte & TLB_PFN) | off; + } + else + { + if ((xpte.pte & acc) && xpte.tag == vpn) /* TB hit, acc ok? */ + return (xpte.pte & TLB_PFN) | off; + } + + xpte = fill (RUN_PASS, va, acc, status); + + if (status == NULL || *status == PR_OK) + return (xpte.pte & TLB_PFN) | off; + else + return -1; + } + + return va & PAMASK; /* ret phys addr */ +} + + + +/* TLB fill + + This routine fills the TLB after a tag or access mismatch, or + on a write if pte = 0. It fills the TLB and returns the + pte to the caller. On an error, it aborts directly to the + fault handler in the CPU. + + If called from map (VAX PROBEx), the error status is returned + to the caller, and no fault occurs. +*/ + +#define MM_ERR(param) { \ + if (stat) { \ + *stat = param; \ + return zero_pte; \ + } \ + fault_p1 = MM_PARAM (acc & TLB_WACC, param); \ + fault_p2 = va; \ + ABORT ((param & PR_TNV)? ABORT_TNV: ABORT_ACV); } + +static TLBENT fill (RUN_DECL, uint32 va, int32 acc, int32 *stat) +{ + int32 ptidx = (((uint32) va) >> 7) & ~03; + int32 tlbpte, ptead, pte, tbi, vpn; + static const TLBENT zero_pte = { 0, 0 }; + + if (va & VA_S0) /* system space? */ + { + if (ptidx >= d_slr) /* system */ + MM_ERR (PR_LNV); + ptead = (d_sbr + ptidx) & PAMASK; + } + else + { + if (va & VA_P1) /* P1? */ + { + if (ptidx < d_p1lr) + MM_ERR (PR_LNV); + ptead = d_p1br + ptidx; + } + else /* P0 */ + { + if (ptidx >= d_p0lr) + MM_ERR (PR_LNV); + ptead = d_p0br + ptidx; + } + if ((ptead & VA_S0) == 0) + ABORT (STOP_PPTE); /* ppte must be sys */ + vpn = VA_GETVPN (ptead); /* get vpn, tbi */ + tbi = VA_GETTBI (vpn); + if (stlb[tbi].tag != vpn) /* in sys tlb? */ + { + ptidx = (((uint32) ptead) >> 7) & ~0x3; /* xlate like sys */ + if (ptidx >= d_slr) + MM_ERR (PR_PLNV); + pte = ReadLP (RUN_PASS, (d_sbr + ptidx) & PAMASK); /* get system pte */ +#if defined (VAX_780) + if ((pte & PTE_ACC) == 0) /* spte ACV? */ + MM_ERR (PR_PACV); +#endif + if ((pte & PTE_V) == 0) /* spte TNV? */ + MM_ERR (PR_PTNV); + stlb[tbi].tag = vpn; /* set stlb tag */ + stlb[tbi].pte = cvtacc[PTE_GETACC (pte)] | + ((pte << VA_N_OFF) & TLB_PFN); /* set stlb data */ + } + ptead = (stlb[tbi].pte & TLB_PFN) | VA_GETOFF (ptead); + } + pte = ReadL (RUN_PASS, ptead); /* read pte */ + tlbpte = cvtacc[PTE_GETACC (pte)] | /* cvt access */ + ((pte << VA_N_OFF) & TLB_PFN); /* set addr */ + if ((tlbpte & acc) == 0) /* chk access */ + MM_ERR (PR_ACV); + if ((pte & PTE_V) == 0) /* check valid */ + MM_ERR (PR_TNV); + if (acc & TLB_WACC) /* write? */ + { + if ((pte & PTE_M) == 0) + { + WriteL (RUN_PASS, ptead, pte | PTE_M); + smp_wmb(); + } + tlbpte = tlbpte | TLB_M; /* set M */ + } + vpn = VA_GETVPN (va); + tbi = VA_GETTBI (vpn); + if ((va & VA_S0) == 0) /* process space? */ + { + ptlb[tbi].tag = vpn; /* store tlb ent */ + ptlb[tbi].pte = tlbpte; + return ptlb[tbi]; + } + stlb[tbi].tag = vpn; /* system space */ + stlb[tbi].pte = tlbpte; /* store tlb ent */ + return stlb[tbi]; +} + +/* Utility routines */ + +void set_map_reg (RUN_DECL) +{ + d_p0br = P0BR & ~03; + d_p1br = (P1BR - 0x800000) & ~03; /* VA<30> >> 7 */ + d_sbr = (SBR - 0x1000000) & ~03; /* VA<31> >> 7 */ + d_p0lr = (P0LR << 2); + d_p1lr = (P1LR << 2) + 0x800000; /* VA<30> >> 7 */ + d_slr = (SLR << 2) + 0x1000000; /* VA<31> >> 7 */ +} + +/* Zap process (0) or whole (1) tb */ + +void zap_tb (RUN_DECL, int stb, t_bool keep_prefetch) +{ + uint32 i; + + for (i = 0; i < VA_TBSIZE; i++) + { + ptlb[i].tag = ptlb[i].pte = -1; + if (stb) + stlb[i].tag = stlb[i].pte = -1; + } + +#if VAX_DIRECT_PREFETCH + /* kludge: invalidate mppc/mppc_rem and ppc/ibcnt */ + if (! keep_prefetch) + { + FLUSH_ISTR; + } +#endif +} + +/* Zap single tb entry corresponding to va */ + +void zap_tb_ent (RUN_DECL, uint32 va) +{ + int32 tbi = VA_GETTBI (VA_GETVPN (va)); + + if (va & VA_S0) + stlb[tbi].tag = stlb[tbi].pte = -1; + else + ptlb[tbi].tag = ptlb[tbi].pte = -1; + +#if VAX_DIRECT_PREFETCH + /* kludge: invalidate mppc/mppc_rem and ppc/ibcnt */ + FLUSH_ISTR; +#endif +} + +/* Check for tlb entry corresponding to va */ + +t_bool chk_tb_ent (RUN_DECL, uint32 va) +{ + int32 vpn = VA_GETVPN (va); + int32 tbi = VA_GETTBI (vpn); + TLBENT xpte; + + xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; + if (xpte.tag == vpn) + return TRUE; + return FALSE; +} + +/* TLB examine */ + +t_stat tlb_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ + RUN_SCOPE; + int32 tlbn = sim_unit_index (uptr); + uint32 idx = (uint32) addr >> 1; + + if (idx >= VA_TBSIZE) + return SCPE_NXM; + if (addr & 1) + *vptr = (uint32) (tlbn ? stlb[idx].pte: ptlb[idx].pte); + else + *vptr = (uint32) (tlbn ? stlb[idx].tag: ptlb[idx].tag); + return SCPE_OK; +} + +/* TLB deposit */ + +t_stat tlb_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ + RUN_SCOPE; + int32 tlbn = sim_unit_index (uptr); + uint32 idx = (uint32) addr >> 1; + + if (idx >= VA_TBSIZE) + return SCPE_NXM; + if (addr & 1) + { + if (tlbn) + stlb[idx].pte = (int32) val; + else + ptlb[idx].pte = (int32) val; + } + else + { + if (tlbn) + stlb[idx].tag = (int32) val; + else + ptlb[idx].tag = (int32) val; + } + return SCPE_OK; +} + +/* TLB reset */ + +t_stat tlb_reset (DEVICE *dptr) +{ + RUN_SCOPE; + uint32 i; + + for (i = 0; i < VA_TBSIZE; i++) + stlb[i].tag = ptlb[i].tag = stlb[i].pte = ptlb[i].pte = -1; + return SCPE_OK; +} + +/* + * Reading non-memory space + */ + +int32 ReadB_nomem (RUN_DECL, uint32 pa) +{ + int32 dat; + + mchk_ref = REF_V; + if (ADDR_IS_IO (pa)) + dat = ReadIO (RUN_PASS, pa, L_BYTE); + else + dat = ReadReg (RUN_PASS, pa, L_BYTE); + + return ((dat >> ((pa & 3) << 3)) & BMASK); +} + +int32 ReadW_nonmem (RUN_DECL, uint32 pa) +{ + int32 dat; + + mchk_ref = REF_V; + if (ADDR_IS_IO (pa)) + dat = ReadIO (RUN_PASS, pa, L_WORD); + else + dat = ReadReg (RUN_PASS, pa, L_WORD); + + return ((dat >> ((pa & 2)? 16: 0)) & WMASK); +} + +int32 ReadL_nonmem (RUN_DECL, uint32 pa) +{ + mchk_ref = REF_V; + if (ADDR_IS_IO (pa)) + return ReadIO (RUN_PASS, pa, L_LONG); + else + return ReadReg (RUN_PASS, pa, L_LONG); +} + +int32 ReadLP_nonmem (RUN_DECL, uint32 pa) +{ + mchk_va = pa; + mchk_ref = REF_P; + if (ADDR_IS_IO (pa)) + return ReadIO (RUN_PASS, pa, L_LONG); + else + return ReadReg (RUN_PASS, pa, L_LONG); +} + +/* + * Writing non-memory space + */ + +void WriteB_nomem (RUN_DECL, uint32 pa, int32 val) +{ + mchk_ref = REF_V; + if (ADDR_IS_IO (pa)) + WriteIO (RUN_PASS, pa, val, L_BYTE); + else + WriteReg (RUN_PASS, pa, val, L_BYTE); +} + +void WriteW_nonmem (RUN_DECL, uint32 pa, int32 val) +{ + mchk_ref = REF_V; + if (ADDR_IS_IO (pa)) + WriteIO (RUN_PASS, pa, val, L_WORD); + else + WriteReg (RUN_PASS, pa, val, L_WORD); +} + +void WriteL_nonmem (RUN_DECL, uint32 pa, int32 val) +{ + mchk_ref = REF_V; + if (ADDR_IS_IO (pa)) + WriteIO (RUN_PASS, pa, val, L_LONG); + else + WriteReg (RUN_PASS, pa, val, L_LONG); +} + +void WriteLP_nomem (RUN_DECL, uint32 pa, int32 val) +{ + mchk_va = pa; + mchk_ref = REF_P; + if (ADDR_IS_IO (pa)) + WriteIO (RUN_PASS, pa, val, L_LONG); + else + WriteReg (RUN_PASS, pa, val, L_LONG); +} diff --git a/src/VAX/vax_mmu.h b/src/VAX/vax_mmu.h new file mode 100644 index 0000000..4fc388e --- /dev/null +++ b/src/VAX/vax_mmu.h @@ -0,0 +1,311 @@ +/* + * vax_mmu.h - prototypes and inline basic functions for physical memory access + */ + +#define refoff(type, base, offset) (* (type*) (((t_byte*)(base)) + (offset))) + +extern volatile uint32 *M; + +int32 ReadIO (RUN_DECL, uint32 pa, int32 lnt); +void WriteIO (RUN_DECL, uint32 pa, int32 val, int32 lnt); + +int32 ReadReg (RUN_DECL, uint32 pa, int32 lnt); +void WriteReg (RUN_DECL, uint32 pa, int32 val, int32 lnt); + +int32 ReadB_nomem (RUN_DECL, uint32 pa); +int32 ReadW_nonmem (RUN_DECL, uint32 pa); +int32 ReadL_nonmem (RUN_DECL, uint32 pa); +int32 ReadLP_nonmem (RUN_DECL, uint32 pa); + +void WriteB_nomem (RUN_DECL, uint32 pa, int32 val); +void WriteW_nonmem (RUN_DECL, uint32 pa, int32 val); +void WriteL_nonmem (RUN_DECL, uint32 pa, int32 val); +void WriteLP_nomem (RUN_DECL, uint32 pa, int32 val); + +#if 0 +# define MEM_REF_ASSERT(cond) do { if (! (cond)) sim_DebugBreak(); } while (0) +#else +# define MEM_REF_ASSERT(cond) +#endif + +/* Read aligned physical (in virtual context, unless indicated) + + Inputs: + pa = physical address, naturally aligned + Output: + returned data, right justified in 32b longword +*/ + +SIM_INLINE static int32 ReadB (RUN_DECL, uint32 pa) +{ + if (ADDR_IS_MEM (pa)) + { +#if defined(__x86_32__) || defined(__x86_64__) + return (int32) refoff(uint8, M, pa); +#else + int32 dat = M[pa >> 2]; + return ((dat >> ((pa & 3) << 3)) & BMASK); +#endif + } + else + { + return ReadB_nomem (RUN_PASS, pa); + } +} + +/* + * Read word. + * Assumes aligned address. + */ +SIM_INLINE static int32 ReadW (RUN_DECL, uint32 pa) +{ + MEM_REF_ASSERT((pa & 1) == 0); + + if (ADDR_IS_MEM (pa)) + { +#if defined(__x86_32__) || defined(__x86_64__) + return (int32) refoff(uint16, M, pa); +#else + int32 dat = M[pa >> 2]; + return ((dat >> ((pa & 2)? 16: 0)) & WMASK); +#endif + } + else + { + return ReadW_nonmem (RUN_PASS, pa); + } +} + +/* + * Read longword. + * Assumes aligned address. + */ +SIM_INLINE static int32 ReadL (RUN_DECL, uint32 pa) +{ + MEM_REF_ASSERT((pa & 3) == 0); + + if (ADDR_IS_MEM (pa)) + { +#if defined(__x86_32__) || defined(__x86_64__) + return refoff(uint32, M, pa); +#else + return M[pa >> 2]; +#endif + } + else + { + return ReadL_nonmem (RUN_PASS, pa); + } +} + +/* + * Read longword, physical access. + * Assumes aligned address. + */ +SIM_INLINE static int32 ReadLP (RUN_DECL, uint32 pa) +{ + MEM_REF_ASSERT((pa & 3) == 0); + + if (ADDR_IS_MEM (pa)) + { +#if defined(__x86_32__) || defined(__x86_64__) + return refoff(uint32, M, pa); +#else + return M[pa >> 2]; +#endif + } + else + { + return ReadLP_nonmem (RUN_PASS, pa); + } +} + +/* + * ReadL, but memory address argument may be pointing inside the longword, + * ignore its lowest 2 bits + */ +SIM_INLINE static int32 ReadL_NA (RUN_DECL, uint32 pa) +{ + if (ADDR_IS_MEM (pa)) + { +#if defined(__x86_32__) || defined(__x86_64__) + return refoff(uint32, M, pa & ~0x3); +#else + return M[pa >> 2]; +#endif + } + else + { + return ReadL_nonmem(RUN_PASS, pa); + } +} + +/* Write aligned physical (in virtual context, unless indicated) + + Inputs: + pa = physical address, naturally aligned + val = data to be written, right justified in 32b longword + Output: + none +*/ + +SIM_INLINE static void WriteB (RUN_DECL, uint32 pa, int32 val) +{ + if (ADDR_IS_MEM (pa)) + { +#if 0 + /* + * Original uniprocessor SIMH code that works regardless of the host endianness, + * but only in uniprocessor (single-threaded) case. + */ + int32 id = pa >> 2; + int32 sc = (pa & 3) << 3; + int32 mask = 0xFF << sc; + M[id] = (M[id] & ~mask) | (val << sc); +#elif defined(__x86_32__) || defined(__x86_64__) + /* SMP case: atomic access to byte without interfering with neighbor bytes */ + refoff(t_byte, M, pa) = (t_byte) val; +#else +# error Unimplemented +#endif + } + else + { + WriteB_nomem (RUN_PASS, pa, val); + } +} + +SIM_INLINE static void WriteW (RUN_DECL, uint32 pa, int32 val) +{ + MEM_REF_ASSERT((pa & 1) == 0); + + if (ADDR_IS_MEM (pa)) + { +#if 0 + /* + * Original uniprocessor SIMH code that works regardless of the host endianness, + * but only in uniprocessor (single-threaded) case. + */ + int32 id = pa >> 2; + M[id] = (pa & 2)? (M[id] & 0xFFFF) | (val << 16): + (M[id] & ~0xFFFF) | val; +#elif defined(__x86_32__) || defined(__x86_64__) + /* SMP case: atomic access to aligned word without interfering with neighbor words */ + refoff(uint16, M, pa) = (uint16) val; +#else +# error Unimplemented +#endif + } + else + { + WriteW_nonmem (RUN_PASS, pa, val); + } +} + +SIM_INLINE static void WriteL (RUN_DECL, uint32 pa, int32 val) +{ + MEM_REF_ASSERT((pa & 3) == 0); + + if (ADDR_IS_MEM (pa)) + { +#if defined(__x86_32__) || defined(__x86_64__) + refoff(uint32, M, pa) = (uint32) val; +#else + M[pa >> 2] = val; +#endif + if (unlikely(PA_MAY_BE_INSIDE_SCB(pa))) + cpu_scb_written(pa); + } + else + { + WriteL_nonmem (RUN_PASS, pa, val); + } +} + +SIM_INLINE static void WriteLP (RUN_DECL, uint32 pa, int32 val) +{ + MEM_REF_ASSERT((pa & 3) == 0); + + if (ADDR_IS_MEM (pa)) + { +#if defined(__x86_32__) || defined(__x86_64__) + refoff(uint32, M, pa) = (uint32) val; +#else + M[pa >> 2] = val; +#endif + } + else + { + WriteLP_nomem (RUN_PASS, pa, val); + } +} + +/* + * WriteL, but memory address argument may be pointing inside the longword, + * ignore its lowest 2 bits + */ +SIM_INLINE static void WriteL_NA (RUN_DECL, uint32 pa, int32 val) +{ + if (ADDR_IS_MEM (pa)) + { +#if defined(__x86_32__) || defined(__x86_64__) + refoff(uint32, M, pa & ~0x3) = (uint32) val; +#else + M[pa >> 2] = val; +#endif + } + else + { + WriteL_nonmem (RUN_PASS, pa, val); + } +} + +/* + * ReadW, but memory address argument may be pointing inside the word, + * ignore its lowest bit + */ +SIM_INLINE static int32 ReadW_NA (RUN_DECL, uint32 pa) +{ + if (ADDR_IS_MEM (pa)) + { +#if defined(__x86_32__) || defined(__x86_64__) + return (int32) refoff(uint16, M, pa & ~0x1); +#else + int32 dat = M[pa >> 2]; + return ((dat >> ((pa & 2)? 16: 0)) & WMASK); +#endif + } + else + { + return ReadW_nonmem (RUN_PASS, pa); + } +} + +/* + * WriteW, but memory address argument may be pointing inside the word, + * ignore its lowest bit + */ +SIM_INLINE static void WriteW_NA (RUN_DECL, uint32 pa, int32 val) +{ + if (ADDR_IS_MEM (pa)) + { +#if 0 + /* + * Original uniprocessor SIMH code that works regardless of the host endianness, + * but only in uniprocessor (single-threaded) case. + */ + int32 id = pa >> 2; + M[id] = (pa & 2)? (M[id] & 0xFFFF) | (val << 16): + (M[id] & ~0xFFFF) | val; +#elif defined(__x86_32__) || defined(__x86_64__) + /* SMP case: atomic access to aligned word without interfering with neighbor words */ + refoff(uint16, M, pa & ~0x1) = (uint16) val; +#else +# error Unimplemented +#endif + } + else + { + WriteW_nonmem (RUN_PASS, pa, val); + } +} diff --git a/src/VAX/vax_octa.cpp b/src/VAX/vax_octa.cpp new file mode 100644 index 0000000..ac7928e --- /dev/null +++ b/src/VAX/vax_octa.cpp @@ -0,0 +1,1243 @@ +/* vax_octa.c - VAX octaword and h_floating instructions + + Copyright (c) 2004-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. + + This module simulates the VAX h_floating instruction set. + + 28-May-08 RMS Inlined physical memory routines + 10-May-06 RMS Fixed bug in reported VA on faulting cross-page write + 03-May-06 RMS Fixed MNEGH to test negated sign, clear C + Fixed carry propagation in qp_inc, qp_neg, qp_add + Fixed pack routines to test for zero via fraction + Fixed ACBH to set cc's on result + Fixed POLYH to set R3 correctly + Fixed POLYH to not exit prematurely if arg = 0 + Fixed POLYH to mask mul reslt to 127b + Fixed fp add routine to test for zero via fraction + to support "denormal" argument from POLYH + Fixed EMODH to concatenate 15b of 16b extension + (all reported by Tim Stark) + 15-Jul-04 RMS Cloned from 32b VAX floating point implementation +*/ + +#include "sim_defs.h" +#include "vax_defs.h" + +#if defined (FULL_VAX) + +extern int32 Test (RUN_DECL, uint32 va, int32 acc, int32 *status); + +#define WORDSWAP(x) ((((x) & WMASK) << 16) | (((x) >> 16) & WMASK)) + +typedef struct { + uint32 f0; /* low */ + uint32 f1; + uint32 f2; + uint32 f3; /* high */ + } UQP; + +typedef struct { + int32 sign; + int32 exp; + UQP frac; + } UFPH; + +#define UH_NM_H 0x80000000 /* normalized */ +#define UH_FRND 0x00000080 /* F round */ +#define UH_DRND 0x00000080 /* D round */ +#define UH_GRND 0x00000400 /* G round */ +#define UH_HRND 0x00004000 /* H round */ +#define UH_V_NM 127 + +int32 op_tsth (int32 val); +int32 op_cmph (int32 *hf1, int32 *hf2); +int32 op_cvtih (int32 val, int32 *hf); +int32 op_cvthi (int32 *hf, int32 *flg, int32 opc); +int32 op_cvtfdh (int32 vl, int32 vh, int32 *hf); +int32 op_cvtgh (int32 vl, int32 vh, int32 *hf); +int32 op_cvthfd (int32 *hf, int32 *vh); +int32 op_cvthg (int32 *hf, int32 *vh); +int32 op_addh (int32 *opnd, int32 *hf, t_bool sub); +int32 op_mulh (int32 *opnd, int32 *hf); +int32 op_divh (int32 *opnd, int32 *hf); +int32 op_emodh (int32 *opnd, int32 *hflt, int32 *intgr, int32 *flg); +void op_polyh (int32 *opnd, int32 acc); +void h_write_b (int32 spec, int32 va, int32 val, int32 acc); +void h_write_w (int32 spec, int32 va, int32 val, int32 acc); +void h_write_l (int32 spec, int32 va, int32 val, int32 acc); +void h_write_q (int32 spec, int32 va, int32 vl, int32 vh, int32 acc); +void h_write_o (int32 spec, int32 va, const int32 *val, int32 acc); +void vax_hadd (UFPH *a, UFPH *b); +void vax_hmul (UFPH *a, UFPH *b, uint32 mlo); +void vax_hmod (UFPH *a, int32 *intgr, int32 *flg); +void vax_hdiv (UFPH *a, UFPH *b); +uint32 qp_add (UQP *a, const UQP *b); +uint32 qp_sub (UQP *a, const UQP *b); +void qp_inc (UQP *a); +void qp_lsh (UQP *a, uint32 sc); +void qp_rsh (UQP *a, uint32 sc); +void qp_rsh_s (UQP *a, uint32 sc, uint32 neg); +void qp_neg (UQP *a); +int32 qp_cmp (UQP *a, UQP *b); +void h_unpackfd (int32 hi, int32 lo, UFPH *a); +void h_unpackg (int32 hi, int32 lo, UFPH *a); +void h_unpackh (int32 *hflt, UFPH *a); +void h_normh (UFPH *a); +int32 h_rpackfd (RUN_DECL, UFPH *a, int32 *rl); +int32 h_rpackg (RUN_DECL, UFPH *a, int32 *rl); +int32 h_rpackh (UFPH *a, int32 *hflt); + +const static int32 z_octa[4] = { 0, 0, 0, 0 }; + +/* Octaword instructions */ + +int32 op_octa (RUN_DECL, int32 *opnd, int32 cc, int32 opc, int32 acc, int32 spec, int32 va) +{ +int32 r, rh, temp, flg; +int32 r_octa[4]; + +switch (opc) { + +/* PUSHAO + + opnd[0] = src.ao +*/ + + case PUSHAO: + Write (RUN_PASS, SP - 4, opnd[0], L_LONG, WA); /* push operand */ + SP = SP - 4; /* decr stack ptr */ + CC_IIZP_L (opnd[0]); /* set cc's */ + break; + +/* MOVAO + + opnd[0] = src.ro + opnd[1:2] = dst.wl + spec = last specifier + va = address if last specifier is memory +*/ + + case MOVAO: + h_write_l (spec, va, opnd[0], acc); /* write operand */ + CC_IIZP_L (opnd[0]); /* set cc's */ + break; + +/* CLRO + + opnd[0:1] = dst.wl + spec = last specifier + va = address if last specifier is memory +*/ + + case CLRO: + h_write_o (spec, va, z_octa, acc); /* write 0's */ + CC_ZZ1P; /* set cc's */ + break; + +/* TSTH + + opnd[0:3] = src.rh +*/ + + case TSTH: + r = op_tsth (opnd[0]); /* test for 0 */ + CC_IIZZ_FP (r); /* set cc's */ + break; + +/* MOVO, MOVH, MNEGH + + opnd[0:3] = src.ro + opnd[4:5] = dst.wo + spec = last specifier + va = address if last specifier is memory +*/ + + case MOVO: + h_write_o (spec, va, opnd, acc); /* write src */ + CC_IIZP_O (opnd[0], opnd[1], opnd[2], opnd[3]); /* set cc's */ + break; + + case MOVH: + if (r = op_tsth (opnd[0])) { /* test for 0 */ + h_write_o (spec, va, opnd, acc); /* nz, write result */ + CC_IIZP_FP (r); /* set cc's */ + } + else { /* zero */ + h_write_o (spec, va, z_octa, acc); /* write 0 */ + cc = (cc & CC_C) | CC_Z; /* set cc's */ + } + break; + + case MNEGH: + if (r = op_tsth (opnd[0])) { /* test for 0 */ + opnd[0] = opnd[0] ^ FPSIGN; /* nz, invert sign */ + h_write_o (spec, va, opnd, acc); /* write result */ + CC_IIZZ_FP (opnd[0]); /* set cc's */ + } + else { /* zero */ + h_write_o (spec, va, z_octa, acc); /* write 0 */ + cc = CC_Z; /* set cc's */ + } + break; + +/* CMPH + + opnd[0:3] = src1.rh + opnd[4:7] = src2.rh +*/ + + case CMPH: + cc = op_cmph (opnd + 0, opnd + 4); /* set cc's */ + break; + +/* CVTBH, CVTWH, CVTLH + + opnd[0] = src.rx + opnd[1:2] = dst.wh + spec = last specifier + va = address if last specifier is memory +*/ + + case CVTBH: + r = op_cvtih (SXTB (opnd[0]), r_octa); /* convert */ + h_write_o (spec, va, r_octa, acc); /* write reslt */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case CVTWH: + r = op_cvtih (SXTW (opnd[0]), r_octa); /* convert */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case CVTLH: + r = op_cvtih (opnd[0], r_octa); /* convert */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + +/* CVTHB, CVTHW, CVTHL, CVTRHL + + opnd[0:3] = src.rh + opnd[4:5] = dst.wx + spec = last specifier + va = address if last specifier is memory +*/ + + case CVTHB: + r = op_cvthi (opnd, &flg, opc) & BMASK; /* convert */ + h_write_b (spec, va, r, acc); /* write result */ + CC_IIZZ_B (r); /* set cc's */ + if (flg) { + V_INTOV; + } + break; + + case CVTHW: + r = op_cvthi (opnd, &flg, opc) & WMASK; /* convert */ + h_write_w (spec, va, r, acc); /* write result */ + CC_IIZZ_W (r); /* set cc's */ + if (flg) { + V_INTOV; + } + break; + + case CVTHL: case CVTRHL: + r = op_cvthi (opnd, &flg, opc) & LMASK; /* convert */ + h_write_l (spec, va, r, acc); /* write result */ + CC_IIZZ_L (r); /* set cc's */ + if (flg) { + V_INTOV; + } + break; + +/* CVTFH + + opnd[0] = src.rf + opnd[1:2] = dst.wh + spec = last specifier + va = address if last specifier is memory +*/ + + case CVTFH: + r = op_cvtfdh (opnd[0], 0, r_octa); /* convert */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + +/* CVTDH, CVTGH + + opnd[0:1] = src.rx + opnd[2:3] = dst.wh + spec = last specifier + va = address if last specifier is memory +*/ + + case CVTDH: + r = op_cvtfdh (opnd[0], opnd[1], r_octa); /* convert */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case CVTGH: + r = op_cvtgh (opnd[0], opnd[1], r_octa); /* convert */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + +/* CVTHF, CVTHD, CVTHG + + opnd[0:3] = src.rh + opnd[4:5] = dst.wx + spec = last specifier + va = address if last specifier is memory +*/ + + case CVTHF: + r = op_cvthfd (opnd, NULL); /* convert */ + h_write_l (spec, va, r, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case CVTHD: + r = op_cvthfd (opnd, &rh); /* convert */ + h_write_q (spec, va, r, rh, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case CVTHG: + r = op_cvthg (opnd, &rh); /* convert */ + h_write_q (spec, va, r, rh, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + +/* ADDH2, SUBH2, MULH2, DIVH2 + + op[0:3] = src.rh + op[4:7] = dst.mh + spec = last specifier + va = address if last specifier is memory + + ADDH3, SUBH3, MULH3, DIVH3 + + op[0:3] = src1.rh + op[4:7] = src2.rh + op[8:9] = dst.wh + spec = last specifier + va = address if last specifier is memory +*/ + + + case ADDH2: case ADDH3: + r = op_addh (opnd, r_octa, FALSE); /* add */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case SUBH2: case SUBH3: + r = op_addh (opnd, r_octa, TRUE); /* subtract */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case MULH2: case MULH3: + r = op_mulh (opnd, r_octa); /* multiply */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case DIVH2: case DIVH3: + r = op_divh (opnd, r_octa); /* divide */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + +/* ACBH + + opnd[0:3] = limit.rh + opnd[4:7] = add.rh + opnd[8:11] = index.mh + spec = last specifier + va = last va + brdest = branch destination +*/ + + case ACBH: + r = op_addh (opnd + 4, r_octa, FALSE); /* add + index */ + CC_IIZP_FP (r); /* set cc's */ + temp = op_cmph (r_octa, opnd); /* result : limit */ + h_write_o (spec, va, r_octa, acc); /* write 2nd */ + if ((temp & CC_Z) || ((opnd[4] & FPSIGN)? /* test br cond */ + !(temp & CC_N): (temp & CC_N))) + cc = cc | LSIGN; /* hack for branch */ + break; + +/* POLYH + + opnd[0:3] = arg.rh + opnd[4] = deg.rb + opnd[5] = table.ah +*/ + + case POLYH: + op_polyh (opnd, acc); /* eval polynomial */ + CC_IIZZ_FP (R[0]); /* set cc's */ + break; + +/* EMODH + + opnd[0:3] = multiplier + opnd[4] = extension + opnd[5:8] = multiplicand + opnd[9:10] = integer destination (int.wl) + opnd[11:12] = floating destination (flt.wh) + spec = last specifier + va = address if last specifier is memory +*/ + + case EMODH: + r = op_emodh (opnd, r_octa, &temp, &flg); /* extended mod */ + if (opnd[11] < 0) { /* 2nd memory? */ + Read (RUN_PASS, opnd[12], L_BYTE, WA); /* prove write */ + Read (RUN_PASS, (opnd[12] + 15) & LMASK, L_BYTE, WA); + } + if (opnd[9] >= 0) /* store 1st */ + R[opnd[9]] = temp; + else Write (RUN_PASS, opnd[10], temp, L_LONG, WA); + h_write_o (spec, va, r_octa, acc); /* write 2nd */ + CC_IIZZ_FP (r); /* set cc's */ + if (flg) { + V_INTOV; + } + break; + + default: + RSVD_INST_FAULT; + } + +return cc; +} + +/* Test h_floating + + Note that only the high 32b is processed. + If the high 32b is not zero, the rest of the fraction is unchanged. */ + +int32 op_tsth (int32 val) +{ +if (val & H_EXP) /* non-zero? */ + return val; +if (val & FPSIGN) /* reserved? */ + RSVD_OPND_FAULT; +return 0; /* clean 0 */ +} + +/* Compare h_floating */ + +int32 op_cmph (int32 *hf1, int32 *hf2) +{ +UFPH a, b; +int32 r; + +h_unpackh (hf1, &a); /* unpack op1 */ +h_unpackh (hf2, &b); /* unpack op2 */ +if (a.sign != b.sign) /* opp signs? */ + return (a.sign? CC_N: 0); +if (a.exp != b.exp) /* cmp exp */ + r = a.exp - b.exp; +else r = qp_cmp (&a.frac, &b.frac); /* if =, cmp frac */ +if (r < 0) /* !=, maybe set N */ + return (a.sign? 0: CC_N); +if (r > 0) + return (a.sign? CC_N: 0); +return CC_Z; /* =, set Z */ +} + +/* Integer to h_floating convert */ + +int32 op_cvtih (int32 val, int32 *hf) +{ +UFPH a; + +if (val == 0) { /* zero? */ + hf[0] = hf[1] = hf[2] = hf[3] = 0; /* result is 0 */ + return 0; + } +if (val < 0) { /* negative? */ + a.sign = FPSIGN; /* sign = - */ + val = -val; + } +else a.sign = 0; /* else sign = + */ +a.exp = 32 + H_BIAS; /* initial exp */ +a.frac.f3 = val & LMASK; /* fraction hi */ +a.frac.f2 = a.frac.f1 = a.frac.f0 = 0; +h_normh (&a); /* normalize */ +return h_rpackh (&a, hf); /* round and pack */ +} + +/* H_floating to integer convert */ + +int32 op_cvthi (int32 *hf, int32 *flg, int32 opc) +{ +UFPH a; +int32 lnt = opc & 03; +int32 ubexp; +const static uint32 maxv[4] = { 0x7F, 0x7FFF, 0x7FFFFFFF, 0x7FFFFFFF }; + +*flg = 0; /* clear ovflo */ +h_unpackh (hf, &a); /* unpack */ +ubexp = a.exp - H_BIAS; /* unbiased exp */ +if ((a.exp == 0) || (ubexp < 0)) /* true zero or frac? */ + return 0; +if (ubexp <= UH_V_NM) { /* exp in range? */ + qp_rsh (&a.frac, UH_V_NM - ubexp); /* leave rnd bit */ + if (lnt == 03) /* if CVTR, round */ + qp_inc (&a.frac); + qp_rsh (&a.frac, 1); /* now justified */ + if (a.frac.f3 || a.frac.f2 || a.frac.f1 || + (a.frac.f0 > (maxv[lnt] + (a.sign? 1: 0)))) + *flg = CC_V; + } +else { + *flg = CC_V; /* always ovflo */ + if (ubexp > (UH_V_NM + 32)) /* in ext range? */ + return 0; + qp_lsh (&a.frac, ubexp - UH_V_NM - 1); /* no rnd bit */ + } +return (a.sign? NEG (a.frac.f0): a.frac.f0); /* return lo frac */ +} + +/* Floating to floating convert - F/D to H, G to H, H to F/D, H to G */ + +int32 op_cvtfdh (int32 vl, int32 vh, int32 *hflt) +{ +UFPH a; + +h_unpackfd (vl, vh, &a); /* unpack f/d */ +a.exp = a.exp - FD_BIAS + H_BIAS; /* if nz, adjust exp */ +return h_rpackh (&a, hflt); /* round and pack */ +} + +int32 op_cvtgh (int32 vl, int32 vh, int32 *hflt) +{ +UFPH a; + +h_unpackg (vl, vh, &a); /* unpack g */ +a.exp = a.exp - G_BIAS + H_BIAS; /* if nz, adjust exp */ +return h_rpackh (&a, hflt); /* round and pack */ +} + +int32 op_cvthfd (int32 *hflt, int32 *rh) +{ +UFPH a; + +h_unpackh (hflt, &a); /* unpack h */ +a.exp = a.exp - H_BIAS + FD_BIAS; /* if nz, adjust exp */ +return h_rpackfd (RUN_PASS, &a, rh); /* round and pack */ +} + +int32 op_cvthg (int32 *hflt, int32 *rh) +{ +UFPH a; + +h_unpackh (hflt, &a); /* unpack h */ +a.exp = a.exp - H_BIAS + G_BIAS; /* if nz, adjust exp */ +return h_rpackg (RUN_PASS, &a, rh); /* round and pack */ +} + +/* Floating add and subtract */ + +int32 op_addh (int32 *opnd, int32 *hflt, t_bool sub) +{ +UFPH a, b; + +h_unpackh (&opnd[0], &a); /* unpack s1, s2 */ +h_unpackh (&opnd[4], &b); +if (sub) /* sub? -s1 */ + a.sign = a.sign ^ FPSIGN; +vax_hadd (&a, &b); /* do add */ +return h_rpackh (&a, hflt); /* round and pack */ +} + +/* Floating multiply */ + +int32 op_mulh (int32 *opnd, int32 *hflt) +{ +UFPH a, b; + +h_unpackh (&opnd[0], &a); /* unpack s1, s2 */ +h_unpackh (&opnd[4], &b); +vax_hmul (&a, &b, 0); /* do multiply */ +return h_rpackh (&a, hflt); /* round and pack */ +} + +/* Floating divide */ + +int32 op_divh (int32 *opnd, int32 *hflt) +{ +UFPH a, b; + +h_unpackh (&opnd[0], &a); /* unpack s1, s2 */ +h_unpackh (&opnd[4], &b); +vax_hdiv (&a, &b); /* do divide */ +return h_rpackh (&b, hflt); /* round and pack */ +} + +/* Polynomial evaluation + + The most mis-implemented instruction in the VAX (probably here too). + POLY requires a precise combination of masking versus normalizing + to achieve the desired answer. In particular, both the multiply + and add steps are masked prior to normalization. In addition, + negative small fractions must not be treated as 0 during denorm. */ + +void op_polyh (int32 *opnd, int32 acc) +{ +UFPH r, a, c; +int32 deg = opnd[4]; +int32 ptr = opnd[5]; +int32 i, wd[4], res[4]; + +if (deg > 31) /* deg > 31? fault */ + RSVD_OPND_FAULT; +h_unpackh (&opnd[0], &a); /* unpack arg */ +wd[0] = Read (RUN_PASS, ptr, L_LONG, RD); /* get C0 */ +wd[1] = Read (RUN_PASS, ptr + 4, L_LONG, RD); +wd[2] = Read (RUN_PASS, ptr + 8, L_LONG, RD); +wd[3] = Read (RUN_PASS, ptr + 12, L_LONG, RD); +ptr = ptr + 16; /* adv ptr */ +h_unpackh (wd, &r); /* unpack C0 */ +h_rpackh (&r, res); /* first result */ +for (i = 0; i < deg; i++) { /* loop */ + h_unpackh (res, &r); /* unpack result */ + vax_hmul (&r, &a, 1); /* r = r * arg */ + wd[0] = Read (RUN_PASS, ptr, L_LONG, RD); /* get Cn */ + wd[1] = Read (RUN_PASS, ptr + 4, L_LONG, RD); + wd[2] = Read (RUN_PASS, ptr + 8, L_LONG, RD); + wd[3] = Read (RUN_PASS, ptr + 12, L_LONG, RD); + ptr = ptr + 16; + h_unpackh (wd, &c); /* unpack Cnext */ + vax_hadd (&r, &c); /* r = r + Cnext */ + h_rpackh (&r, res); /* round and pack */ + } +R[0] = res[0]; /* result */ +R[1] = res[1]; +R[2] = res[2]; +R[3] = res[3]; +R[4] = 0; +R[5] = ptr; +return; +} + +/* Extended modularize + + EMOD presents two sets of complications. First, it requires an extended + fraction multiply, with precise (and unusual) truncation conditions. + Second, it has two write operands, a dubious distinction it shares + with EDIV. */ + +int32 op_emodh (int32 *opnd, int32 *hflt, int32 *intgr, int32 *flg) +{ +UFPH a, b; + +h_unpackh (&opnd[0], &a); /* unpack operands */ +h_unpackh (&opnd[5], &b); +a.frac.f0 = a.frac.f0 | (opnd[4] >> 1); /* extend src1 */ +vax_hmul (&a, &b, 0); /* multiply */ +vax_hmod (&a, intgr, flg); /* sep int & frac */ +return h_rpackh (&a, hflt); /* round and pack frac */ +} + +/* Unpacked floating point routines */ + +/* Floating add */ + +void vax_hadd (UFPH *a, UFPH *b) +{ +int32 ediff; +UFPH t; + +if ((a->frac.f3 == 0) && (a->frac.f2 == 0) && /* s1 = 0? */ + (a->frac.f1 == 0) && (a->frac.f0 == 0)) { + *a = *b; /* result is s2 */ + return; + } +if ((b->frac.f3 == 0) && (b->frac.f2 == 0) && /* s2 = 0? */ + (b->frac.f1 == 0) && (b->frac.f0 == 0)) + return; +if ((a->exp < b->exp) || /* |s1| < |s2|? */ + ((a->exp == b->exp) && (qp_cmp (&a->frac, &b->frac) < 0))) { + t = *a; /* swap */ + *a = *b; + *b = t; + } +ediff = a->exp - b->exp; /* exp diff */ +if (a->sign ^ b->sign) { /* eff sub? */ + qp_neg (&b->frac); /* negate fraction */ + if (ediff) /* denormalize */ + qp_rsh_s (&b->frac, ediff, 1); + qp_add (&a->frac, &b->frac); /* "add" frac */ + h_normh (a); /* normalize */ + } +else { + if (ediff) /* add, denormalize */ + qp_rsh (&b->frac, ediff); + if (qp_add (&a->frac, &b->frac)) { /* add frac, carry? */ + qp_rsh (&a->frac, 1); /* renormalize */ + a->frac.f3 = a->frac.f3 | UH_NM_H; /* add norm bit */ + a->exp = a->exp + 1; /* incr exp */ + } + } +return; +} + +/* Floating multiply - 128b * 128b */ + +void vax_hmul (UFPH *a, UFPH *b, uint32 mlo) +{ +int32 i, c; +UQP accum = { 0, 0, 0, 0 }; + +if ((a->exp == 0) || (b->exp == 0)) { /* zero argument? */ + a->frac.f0 = a->frac.f1 = 0; /* result is zero */ + a->frac.f2 = a->frac.f3 = 0; + a->sign = a->exp = 0; + return; + } +a->sign = a->sign ^ b->sign; /* sign of result */ +a->exp = a->exp + b->exp - H_BIAS; /* add exponents */ +for (i = 0; i < 128; i++) { /* quad precision */ + if (a->frac.f0 & 1) /* mplr low? add */ + c = qp_add (&accum, &b->frac); + else c = 0; + qp_rsh (&accum, 1); /* shift result */ + if (c) /* add carry out */ + accum.f3 = accum.f3 | UH_NM_H; + qp_rsh (&a->frac, 1); /* shift mplr */ + } +a->frac = accum; /* result */ +a->frac.f0 = a->frac.f0 & ~mlo; /* mask low frac */ +h_normh (a); /* normalize */ +return; +} + +/* Floating modulus - there are three cases + + exp <= bias - integer is 0, fraction is input, + no overflow + bias < exp <= bias+128 - separate integer and fraction, + integer overflow may occur + bias+128 < exp - result is integer, fraction is 0 + integer overflow +*/ + +void vax_hmod (UFPH *a, int32 *intgr, int32 *flg) +{ +UQP ifr; + +if (a->exp <= H_BIAS) /* 0 or <1? int = 0 */ + *intgr = *flg = 0; +else if (a->exp <= (H_BIAS + 128)) { /* in range? */ + ifr = a->frac; + qp_rsh (&ifr, 128 - (a->exp - H_BIAS)); /* separate integer */ + if ((a->exp > (H_BIAS + 32)) || /* test ovflo */ + ((a->exp == (H_BIAS + 32)) && + (ifr.f0 > (a->sign? 0x80000000: 0x7FFFFFFF)))) + *flg = CC_V; + else *flg = 0; + *intgr = ifr.f0; + if (a->sign) /* -? comp int */ + *intgr = -*intgr; + qp_lsh (&a->frac, a->exp - H_BIAS); /* excise integer */ + a->exp = H_BIAS; + } +else { + *intgr = 0; /* out of range */ + a->frac.f0 = a->frac.f1 = 0; /* result 0 */ + a->frac.f2 = a->frac.f3 = 0; + a->sign = a->exp = 0; + *flg = CC_V; /* overflow */ + } +h_normh (a); /* normalize */ +return; +} + +/* Floating divide + + Carried out to 128 bits, although fewer are required */ + +void vax_hdiv (UFPH *a, UFPH *b) +{ +int32 i; +UQP quo = { 0, 0, 0, 0 }; + +if (a->exp == 0) /* divr = 0? */ + FLT_DZRO_FAULT; +if (b->exp == 0) /* divd = 0? */ + return; +b->sign = b->sign ^ a->sign; /* result sign */ +b->exp = b->exp - a->exp + H_BIAS + 1; /* unbiased exp */ +qp_rsh (&a->frac, 1); /* allow 1 bit left */ +qp_rsh (&b->frac, 1); +for (i = 0; i < 128; i++) { /* divide loop */ + qp_lsh (&quo, 1); /* shift quo */ + if (qp_cmp (&b->frac, &a->frac) >= 0) { /* div step ok? */ + qp_sub (&b->frac, &a->frac); /* subtract */ + quo.f0 = quo.f0 + 1; /* quo bit = 1 */ + } + qp_lsh (&b->frac, 1); /* shift divd */ + } +b->frac = quo; +h_normh (b); /* normalize */ +return; +} + +/* Quad precision integer routines */ + +int32 qp_cmp (UQP *a, UQP *b) +{ +if (a->f3 < b->f3) /* compare hi */ + return -1; +if (a->f3 > b->f3) + return +1; +if (a->f2 < b->f2) /* hi =, compare mid1 */ + return -1; +if (a->f2 > b->f2) + return +1; +if (a->f1 < b->f1) /* mid1 =, compare mid2 */ + return -1; +if (a->f1 > b->f1) + return +1; +if (a->f0 < b->f0) /* mid2 =, compare lo */ + return -1; +if (a->f0 > b->f0) + return +1; +return 0; /* all equal */ +} + +uint32 qp_add (UQP *a, const UQP *b) +{ +uint32 cry1, cry2, cry3, cry4; + +a->f0 = (a->f0 + b->f0) & LMASK; /* add lo */ +cry1 = (a->f0 < b->f0); /* carry? */ +a->f1 = (a->f1 + b->f1 + cry1) & LMASK; /* add mid2 */ +cry2 = (a->f1 < b->f1) || (cry1 && (a->f1 == b->f1)); /* carry? */ +a->f2 = (a->f2 + b->f2 + cry2) & LMASK; /* add mid1 */ +cry3 = (a->f2 < b->f2) || (cry2 && (a->f2 == b->f2)); /* carry? */ +a->f3 = (a->f3 + b->f3 + cry3) & LMASK; /* add hi */ +cry4 = (a->f3 < b->f3) || (cry3 && (a->f3 == b->f3)); /* carry? */ +return cry4; /* return carry out */ +} + +void qp_inc (UQP *a) +{ +a->f0 = (a->f0 + 1) & LMASK; /* inc lo */ +if (a->f0 == 0) { /* propagate carry */ + a->f1 = (a->f1 + 1) & LMASK; + if (a->f1 == 0) { + a->f2 = (a->f2 + 1) & LMASK; + if (a->f2 == 0) { + a->f3 = (a->f3 + 1) & LMASK; + } + } + } +return; +} + +uint32 qp_sub (UQP *a, const UQP *b) +{ +uint32 brw1, brw2, brw3, brw4; + +brw1 = (a->f0 < b->f0); /* borrow? */ +a->f0 = (a->f0 - b->f0) & LMASK; /* sub lo */ +brw2 = (a->f1 < b->f1) || (brw1 && (a->f1 == b->f1)); /* borrow? */ +a->f1 = (a->f1 - b->f1 - brw1) & LMASK; /* sub mid1 */ +brw3 = (a->f2 < b->f2) || (brw2 && (a->f2 == b->f2)); /* borrow? */ +a->f2 = (a->f2 - b->f2 - brw2) & LMASK; /* sub mid2 */ +brw4 = (a->f3 < b->f3) || (brw3 && (a->f3 == b->f3)); /* borrow? */ +a->f3 = (a->f3 - b->f3 - brw3) & LMASK; /* sub high */ +return brw4; +} + +void qp_neg (UQP *a) +{ +uint32 cryin; + +cryin = 1; +a->f0 = (~a->f0 + cryin) & LMASK; +if (a->f0 != 0) + cryin = 0; +a->f1 = (~a->f1 + cryin) & LMASK; +if (a->f1 != 0) + cryin = 0; +a->f2 = (~a->f2 + cryin) & LMASK; +if (a->f2 != 0) + cryin = 0; +a->f3 = (~a->f3 + cryin) & LMASK; +return; +} + +void qp_lsh (UQP *r, uint32 sc) +{ +if (sc >= 128) /* > 127? result 0 */ + r->f3 = r->f2 = r->f1 = r->f0 = 0; +else if (sc >= 96) { /* [96,127]? */ + r->f3 = (r->f0 << (sc - 96)) & LMASK; + r->f2 = r->f1 = r->f0 = 0; + } +else if (sc > 64) { /* [65,95]? */ + r->f3 = ((r->f1 << (sc - 64)) | (r->f0 >> (96 - sc))) & LMASK; + r->f2 = (r->f0 << (sc - 64)) & LMASK; + r->f1 = r->f0 = 0; + } +else if (sc == 64) { /* [64]? */ + r->f3 = r->f1; + r->f2 = r->f0; + r->f1 = r->f0 = 0; + } +else if (sc > 32) { /* [33,63]? */ + r->f3 = ((r->f2 << (sc - 32)) | (r->f1 >> (64 - sc))) & LMASK; + r->f2 = ((r->f1 << (sc - 32)) | (r->f0 >> (64 - sc))) & LMASK; + r->f1 = (r->f0 << (sc - 32)) & LMASK; + r->f0 = 0; + } +else if (sc == 32) { /* [32]? */ + r->f3 = r->f2; + r->f2 = r->f1; + r->f1 = r->f0; + r->f0 = 0; + } +else if (sc != 0) { /* [31,1]? */ + r->f3 = ((r->f3 << sc) | (r->f2 >> (32 - sc))) & LMASK; + r->f2 = ((r->f2 << sc) | (r->f1 >> (32 - sc))) & LMASK; + r->f1 = ((r->f1 << sc) | (r->f0 >> (32 - sc))) & LMASK; + r->f0 = (r->f0 << sc) & LMASK; + } +return; +} + +void qp_rsh (UQP *r, uint32 sc) +{ +if (sc >= 128) /* > 127? result 0 */ + r->f3 = r->f2 = r->f1 = r->f0 = 0; +else if (sc >= 96) { /* [96,127]? */ + r->f0 = (r->f3 >> (sc - 96)) & LMASK; + r->f1 = r->f2 = r->f3 = 0; + } +else if (sc > 64) { /* [65,95]? */ + r->f0 = ((r->f2 >> (sc - 64)) | (r->f3 << (96 - sc))) & LMASK; + r->f1 = (r->f3 >> (sc - 64)) & LMASK; + r->f2 = r->f3 = 0; + } +else if (sc == 64) { /* [64]? */ + r->f0 = r->f2; + r->f1 = r->f3; + r->f2 = r->f3 = 0; + } +else if (sc > 32) { /* [33,63]? */ + r->f0 = ((r->f1 >> (sc - 32)) | (r->f2 << (64 - sc))) & LMASK; + r->f1 = ((r->f2 >> (sc - 32)) | (r->f3 << (64 - sc))) & LMASK; + r->f2 = (r->f3 >> (sc - 32)) & LMASK; + r->f3 = 0; + } +else if (sc == 32) { /* [32]? */ + r->f0 = r->f1; + r->f1 = r->f2; + r->f2 = r->f3; + r->f3 = 0; + } +else if (sc != 0) { /* [31,1]? */ + r->f0 = ((r->f0 >> sc) | (r->f1 << (32 - sc))) & LMASK; + r->f1 = ((r->f1 >> sc) | (r->f2 << (32 - sc))) & LMASK; + r->f2 = ((r->f2 >> sc) | (r->f3 << (32 - sc))) & LMASK; + r->f3 = (r->f3 >> sc) & LMASK; + } +return; +} + +void qp_rsh_s (UQP *r, uint32 sc, uint32 neg) +{ +qp_rsh (r, sc); /* do unsigned right */ +if (neg && sc) { /* negative? */ + if (sc >= 128) + r->f0 = r->f1 = r->f2 = r->f3 = LMASK; /* > 127? result -1 */ + else { + UQP ones = { LMASK, LMASK, LMASK, LMASK }; + qp_lsh (&ones, 128 - sc); /* shift ones */ + r->f0 = r->f0 | ones.f0; /* or into result */ + r->f1 = r->f1 | ones.f1; + r->f2 = r->f2 | ones.f2; + r->f3 = r->f3 | ones.f3; + } + } +return; +} + +/* Support routines */ + +void h_unpackfd (int32 hi, int32 lo, UFPH *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = FD_GETEXP (hi); /* get exponent */ +r->frac.f0 = r->frac.f1 = 0; /* low bits 0 */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) /* if -, rsvd op */ + RSVD_OPND_FAULT; + r->frac.f2 = r->frac.f3 = 0; /* else 0 */ + return; + } +r->frac.f3 = WORDSWAP ((hi & ~(FPSIGN | FD_EXP)) | FD_HB); +r->frac.f2 = WORDSWAP (lo); +qp_lsh (&r->frac, FD_GUARD); +return; +} + +void h_unpackg (int32 hi, int32 lo, UFPH *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = G_GETEXP (hi); /* get exponent */ +r->frac.f0 = r->frac.f1 = 0; /* low bits 0 */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) /* if -, rsvd op */ + RSVD_OPND_FAULT; + r->frac.f2 = r->frac.f3 = 0; /* else 0 */ + return; + } +r->frac.f3 = WORDSWAP ((hi & ~(FPSIGN | G_EXP)) | G_HB); +r->frac.f2 = WORDSWAP (lo); +qp_lsh (&r->frac, G_GUARD); +return; +} + +void h_unpackh (int32 *hflt, UFPH *r) +{ +r->sign = hflt[0] & FPSIGN; /* get sign */ +r->exp = H_GETEXP (hflt[0]); /* get exponent */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) /* if -, rsvd op */ + RSVD_OPND_FAULT; + r->frac.f0 = r->frac.f1 = 0; /* else 0 */ + r->frac.f2 = r->frac.f3 = 0; + return; + } +r->frac.f3 = WORDSWAP ((hflt[0] & ~(FPSIGN | H_EXP)) | H_HB); +r->frac.f2 = WORDSWAP (hflt[1]); +r->frac.f1 = WORDSWAP (hflt[2]); +r->frac.f0 = WORDSWAP (hflt[3]); +qp_lsh (&r->frac, H_GUARD); +return; +} + +void h_normh (UFPH *r) +{ +int32 i; +const static uint32 normmask[5] = { + 0xc0000000, 0xf0000000, 0xff000000, 0xffff0000, 0xffffffff }; +const static int32 normtab[6] = { 1, 2, 4, 8, 16, 32}; + +if ((r->frac.f0 == 0) && (r->frac.f1 == 0) && + (r->frac.f2 == 0) && (r->frac.f3 == 0)) { /* if fraction = 0 */ + r->sign = r->exp = 0; /* result is 0 */ + return; + } +while ((r->frac.f3 & UH_NM_H) == 0) { /* normalized? */ + for (i = 0; i < 5; i++) { /* find first 1 */ + if (r->frac.f3 & normmask[i]) + break; + } + qp_lsh (&r->frac, normtab[i]); /* shift frac */ + r->exp = r->exp - normtab[i]; /* decr exp */ + } +return; +} + +int32 h_rpackfd (RUN_DECL, UFPH *r, int32 *rh) +{ +const static UQP f_round = { 0, 0, 0, UH_FRND }; +const static UQP d_round = { 0, 0, UH_DRND, 0 }; + +if (rh) /* assume 0 */ + *rh = 0; +if ((r->frac.f3 == 0) && (r->frac.f2 == 0)) /* frac = 0? done */ + return 0; +qp_add (&r->frac, rh? &d_round: &f_round); +if ((r->frac.f3 & UH_NM_H) == 0) { /* carry out? */ + qp_rsh (&r->frac, 1); /* renormalize */ + r->exp = r->exp + 1; + } +if (r->exp > (int32) FD_M_EXP) /* ovflo? fault */ + FLT_OVFL_FAULT; +if (r->exp <= 0) { /* underflow? */ + if (PSL & PSW_FU) /* fault if fu */ + FLT_UNFL_FAULT; + return 0; /* else 0 */ + } +qp_rsh (&r->frac, FD_GUARD); /* remove guard */ +if (rh) + *rh = WORDSWAP (r->frac.f2); +return r->sign | (r->exp << FD_V_EXP) | + (WORDSWAP (r->frac.f3) & ~(FD_HB | FPSIGN | FD_EXP)); +} + +int32 h_rpackg (RUN_DECL, UFPH *r, int32 *rh) +{ +const static UQP g_round = { 0, 0, UH_GRND, 0 }; + +*rh = 0; /* assume 0 */ +if ((r->frac.f3 == 0) && (r->frac.f2 == 0)) /* frac = 0? done */ + return 0; +qp_add (&r->frac, &g_round); /* round */ +if ((r->frac.f3 & UH_NM_H) == 0) { /* carry out? */ + qp_rsh (&r->frac, 1); /* renormalize */ + r->exp = r->exp + 1; + } +if (r->exp > (int32) G_M_EXP) /* ovflo? fault */ + FLT_OVFL_FAULT; +if (r->exp <= 0) { /* underflow? */ + if (PSL & PSW_FU) /* fault if fu */ + FLT_UNFL_FAULT; + return 0; /* else 0 */ + } +qp_rsh (&r->frac, G_GUARD); /* remove guard */ +*rh = WORDSWAP (r->frac.f2); /* get low */ +return r->sign | (r->exp << G_V_EXP) | + (WORDSWAP (r->frac.f3) & ~(G_HB | FPSIGN | G_EXP)); +} + +int32 h_rpackh (UFPH *r, int32 *hflt) +{ +const static UQP h_round = { UH_HRND, 0, 0, 0 }; + +hflt[0] = hflt[1] = hflt[2] = hflt[3] = 0; /* assume 0 */ +if ((r->frac.f3 == 0) && (r->frac.f2 == 0) && /* frac = 0? done */ + (r->frac.f1 == 0) && (r->frac.f0 == 0)) + return 0; +if (qp_add (&r->frac, &h_round)) { /* round, carry out? */ + qp_rsh (&r->frac, 1); /* renormalize */ + r->exp = r->exp + 1; + } +if (r->exp > (int32) H_M_EXP) /* ovflo? fault */ + FLT_OVFL_FAULT; +if (r->exp <= 0) { /* underflow? */ + if (PSL & PSW_FU) /* fault if fu */ + FLT_UNFL_FAULT; + return 0; /* else 0 */ + } +qp_rsh (&r->frac, H_GUARD); /* remove guard */ +hflt[0] = r->sign | (r->exp << H_V_EXP) | + (WORDSWAP (r->frac.f3) & ~(H_HB | FPSIGN | H_EXP)); +hflt[1] = WORDSWAP (r->frac.f2); +hflt[2] = WORDSWAP (r->frac.f1); +hflt[3] = WORDSWAP (r->frac.f0); +return hflt[0]; +} + +void h_write_b (int32 spec, int32 va, int32 val, int32 acc) +{ +int32 rn; + +if (spec > (GRN | nPC)) + Write (RUN_PASS, va, val, L_BYTE, WA); +else { + rn = spec & 0xF; + R[rn] = (R[rn] & ~BMASK) | val; + } +return; +} + +void h_write_w (int32 spec, int32 va, int32 val, int32 acc) +{ +int32 rn; + +if (spec > (GRN | nPC)) + Write (RUN_PASS, va, val, L_WORD, WA); +else { + rn = spec & 0xF; + R[rn] = (R[rn] & ~WMASK) | val; + } +return; +} + +void h_write_l (int32 spec, int32 va, int32 val, int32 acc) +{ +if (spec > (GRN | nPC)) + Write (RUN_PASS, va, val, L_LONG, WA); +else R[spec & 0xF] = val; +return; +} + +void h_write_q (int32 spec, int32 va, int32 vl, int32 vh, int32 acc) +{ +int32 rn, mstat; + +if (spec > (GRN | nPC)) { + if ((Test (RUN_PASS, va + 7, WA, &mstat) >= 0) || + (Test (RUN_PASS, va, WA, &mstat) < 0)) + Write (RUN_PASS, va, vl, L_LONG, WA); + Write (RUN_PASS, va + 4, vh, L_LONG, WA); + } +else { + rn = spec & 0xF; + if (rn >= nSP) + RSVD_ADDR_FAULT; + R[rn] = vl; + R[rn + 1] = vh; + } +return; +} + +void h_write_o (int32 spec, int32 va, const int32 *val, int32 acc) +{ +int32 rn, mstat; + +if (spec > (GRN | nPC)) { + if ((Test (RUN_PASS, va + 15, WA, &mstat) >= 0) || + (Test (RUN_PASS, va, WA, &mstat) < 0)) + Write (RUN_PASS, va, val[0], L_LONG, WA); + Write (RUN_PASS, va + 4, val[1], L_LONG, WA); + Write (RUN_PASS, va + 8, val[2], L_LONG, WA); + Write (RUN_PASS, va + 12, val[3], L_LONG, WA); + } +else { + rn = spec & 0xF; + if (rn >= nAP) + RSVD_ADDR_FAULT; + R[rn] = val[0]; + R[rn + 1] = val[1]; + R[rn + 2] = val[2]; + R[rn + 3] = val[3]; + } +return; +} + +#else + +int32 op_octa (RUN_DECL, int32 *opnd, int32 cc, int32 opc, int32 acc, int32 spec, int32 va) +{ +RSVD_INST_FAULT; +return cc; +} + +#endif diff --git a/src/VAX/vax_stddev.cpp b/src/VAX/vax_stddev.cpp new file mode 100644 index 0000000..8c3c841 --- /dev/null +++ b/src/VAX/vax_stddev.cpp @@ -0,0 +1,563 @@ +/* vax_stddev.c: VAX 3900 standard I/O devices + + Copyright (c) 1998-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. + + tti terminal input + tto terminal output + clk 100Hz and TODR clock + + 17-Aug-08 RMS Resync TODR on any clock reset + 18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock + 17-Oct-06 RMS Synced keyboard poll to real-time clock for idling + 22-Nov-05 RMS Revised for new terminal processing routines + 09-Sep-04 RMS Integrated powerup into RESET (with -p) + 28-May-04 RMS Removed SET TTI CTRL-C + 29-Dec-03 RMS Added console backpressure support + 25-Apr-03 RMS Revised for extended file support + 02-Mar-02 RMS Added SET TTI CTRL-C + 22-Dec-02 RMS Added console halt capability + 01-Nov-02 RMS Added 7B/8B capability to terminal + 12-Sep-02 RMS Removed paper tape, added variable vector support + 30-May-02 RMS Widened POS to 32b + 30-Apr-02 RMS Automatically set TODR to VMS-correct value during boot +*/ + +#include "sim_defs.h" +#include "vax_defs.h" +#include + +#define TTICSR_IMP (CSR_DONE + CSR_IE) /* terminal input */ +#define TTICSR_RW (CSR_IE) +#define TTIBUF_ERR 0x8000 /* error */ +#define TTIBUF_OVR 0x4000 /* overrun */ +#define TTIBUF_FRM 0x2000 /* framing error */ +#define TTIBUF_RBR 0x0400 /* receive break */ +#define TTOCSR_IMP (CSR_DONE + CSR_IE) /* terminal output */ +#define TTOCSR_RW (CSR_IE) +#define CLKCSR_IMP (CSR_IE) /* real-time clock */ +#define CLKCSR_RW (CSR_IE) +#define CLK_DELAY 5000 /* 100 Hz */ + +extern int32 sim_switches; + +int32 clk_tps = CLK_TPS; /* ticks/second */ +atomic_int32_var tmxr_poll = atomic_var_init(CLK_DELAY * TMXR_MULT); /* term mux poll */ +atomic_int32_var tmr_poll = atomic_var_init(CLK_DELAY); /* pgm timer poll */ + +t_stat tti_svc (RUN_SVC_DECL, UNIT *uptr); +t_stat tto_svc (RUN_SVC_DECL, UNIT *uptr); +t_stat clk_svc (RUN_SVC_DECL, UNIT *uptr); +t_stat tti_reset (DEVICE *dptr); +t_stat tto_reset (DEVICE *dptr); +t_stat clk_reset (DEVICE *dptr); +t_stat todr_resync (void); + +extern int32 sysd_hlt_enb (void); + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit descriptor + tti_reg TTI register list +*/ + +#define TTI_TYPEAHEAD_SIZE 256 +static sim_ring_buffer tti_typeahead; +AUTO_INIT_LOCK(tti_typeahead_lock, SIM_LOCK_CRITICALITY_OS_HI, DEVLOCK_SPINWAIT_CYCLES); + +DIB tti_dib = { 0, 0, NULL, NULL, 1, IVCL (TTI), SCB_TTI, { NULL } }; + +UNIT tti_unit UDATA_SINGLE_WAIT (&tti_svc, UNIT_IDLE|TT_MODE_8B, 0, 0); +UNIT_TABLE_SINGLE(tti_unit); + +REG tti_reg[] = { + { HRDATA_GBL (BUF, tti_unit.buf, 16) }, + { HRDATA_CPU ("CSR", r_tti_csr, 16) }, + { IRDATA_DEV (INT, IVCL (TTI)) }, + { FLDATA_CPU ("DONE", r_tti_csr, CSR_V_DONE) }, + { FLDATA_CPU ("IE", r_tti_csr, CSR_V_IE) }, + { DRDATA_GBL (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA_GBL (TIME, tti_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tti_mod[] = { + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { 0 } + }; + +DEVICE tti_dev = { + "TTI", tti_unit_table, tti_reg, tti_mod, + 1, 10, 31, 1, 16, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL, + &tti_dib, DEV_PERCPU + }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit descriptor + tto_reg TTO register list +*/ + +DIB tto_dib = { 0, 0, NULL, NULL, 1, IVCL (TTO), SCB_TTO, { NULL } }; + +UNIT tto_unit UDATA_SINGLE_WAIT (&tto_svc, TT_MODE_8B, 0, SERIAL_OUT_WAIT); +UNIT_TABLE_SINGLE(tto_unit); + +REG tto_reg[] = { + { HRDATA_CPU ("BUF", r_tto_buf, 8) }, + { HRDATA_CPU ("CSR", r_tto_csr, 16) }, + { IRDATA_DEV (INT, IVCL (TTO)) }, + { FLDATA_CPU ("DONE", r_tto_csr, CSR_V_DONE) }, + { FLDATA_CPU ("IE", r_tto_csr, CSR_V_IE) }, + { DRDATA_GBL (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA_GBL (TIME, tto_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tto_mod[] = { + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec }, + { 0 } + }; + +DEVICE tto_dev = { + "TTO", tto_unit_table, tto_reg, tto_mod, + 1, 10, 31, 1, 16, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL, + &tto_dib, DEV_PERCPU + }; + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit descriptor + clk_reg CLK register list +*/ + +DIB clk_dib = { 0, 0, NULL, NULL, 1, IVCL (CLK), SCB_INTTIM, { NULL } }; + +UNIT clk_unit UDATA_SINGLE_WAIT (&clk_svc, UNIT_IDLE, 0, CLK_DELAY); +UNIT_TABLE_SINGLE(clk_unit); + +REG clk_reg[] = { + { HRDATA_CPU ("CSR", r_clk_csr, 16) }, + { IRDATA_DEV (INT, IVCL (CLK)) }, + { FLDATA_CPU ("IE", r_clk_csr, CSR_V_IE) }, + { DRDATA_CPU ("TODR", r_todr_reg, 32), PV_LEFT }, + { FLDATA_CPU ("BLOW", r_todr_blow, 0) }, + { DRDATA_GBL (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA_GBL (POLL, atomic_var(tmr_poll), 24), REG_NZ + PV_LEFT + REG_HRO }, + { DRDATA_GBL (TPS, clk_tps, 8), REG_NZ + PV_LEFT }, + { NULL } + }; + +MTAB clk_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec }, + { 0 } + }; + +DEVICE clk_dev = { + "CLK", clk_unit_table, clk_reg, clk_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL, + &clk_dib, DEV_PERCPU + }; + +/* Clock and terminal MxPR routines + + iccs_rd/wr interval timer + todr_rd/wr time of year clock + rxcs_rd/wr input control/status + rxdb_rd input buffer + txcs_rd/wr output control/status + txdb_wr output buffer +*/ + +int32 iccs_rd (RUN_DECL) +{ + return (clk_csr & CLKCSR_IMP); +} + +int32 todr_rd (RUN_DECL) +{ + if (cpu_unit->is_primary_cpu()) + return todr_reg; + else + return weak_read(primary_todr_reg); +} + +int32 rxcs_rd (RUN_DECL) +{ + return (tti_csr & TTICSR_IMP); +} + +int32 rxdb_rd (RUN_DECL) +{ + /* only the primary processor receives console input */ + if (cpu_unit->is_primary_cpu()) + { + int32 t = tti_unit.buf; /* char + error */ + + tti_csr = tti_csr & ~CSR_DONE; /* clr done */ + tti_unit.buf = tti_unit.buf & 0377; /* clr errors */ + CLR_INT (TTI); + return t; + } + else + { + /* non-primary CPU tries to read RXDB: not expected */ + tti_csr = tti_csr & ~CSR_DONE; /* clr done */ + CLR_INT (TTI); + return CSR_ERR; + } +} + +int32 txcs_rd (RUN_DECL) +{ + return (tto_csr & TTOCSR_IMP); +} + +void iccs_wr (RUN_DECL, int32 data) +{ + if ((data & CSR_IE) == 0) + { + CLR_INT (CLK); + syncw_reeval_sys(RUN_PASS); + } + clk_csr = (clk_csr & ~CLKCSR_RW) | (data & CLKCSR_RW); +} + +void todr_wr (RUN_DECL, int32 data) +{ + if (cpu_unit->is_primary_cpu()) + { + todr_reg = data; + if (data) + todr_blow = 0; + } +} + +void rxcs_wr (RUN_DECL, int32 data) +{ + if ((data & CSR_IE) == 0) + CLR_INT (TTI); + else if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + SET_INT (TTI); + tti_csr = (tti_csr & ~TTICSR_RW) | (data & TTICSR_RW); +} + +void txcs_wr (RUN_DECL, int32 data) +{ + if ((data & CSR_IE) == 0) + CLR_INT (TTO); + else if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + SET_INT (TTO); + tto_csr = (tto_csr & ~TTOCSR_RW) | (data & TTOCSR_RW); +} + +void txdb_wr (RUN_DECL, int32 data) +{ + tto_buf = data & 0377; + tto_csr = tto_csr & ~CSR_DONE; + CLR_INT (TTO); + sim_activate (&tto_unit, tto_unit.wait); +} + +/* Terminal input routines + + tti_svc process event (character ready) + tti_reset process reset +*/ + +t_stat tti_svc (RUN_SVC_DECL, UNIT *uptr) +{ + // RUN_SVC_CHECK_CANCELLED(uptr); // not required for per-CPU devices + int32 c; + + /* Since console input is available only for the primary CPU, + this routine is never executed by secondaries */ + + /* continue poll */ + if (use_clock_thread) + { + sim_activate_clk_cosched (uptr, 1); + } + else + { + int32 x_tmr_poll = weak_read_var(tmr_poll); + sim_activate (uptr, KBD_WAIT (uptr->wait, x_tmr_poll)); + } + + /* skip if tti_unit.buf is not empty and still has not been consumed */ + if (tti_csr & CSR_DONE) + return SCPE_OK; + + AUTO_LOCK_NM(ta_autolock, tti_typeahead_lock); + if (! tti_typeahead.get(& c)) + { + ta_autolock.unlock(); + c = sim_poll_kbd (FALSE); /* poll Telnet connection if any */ + } + else + { + ta_autolock.unlock(); + } + + if (c < SCPE_KFLAG) /* no char or error? */ + return c; + if (c & SCPE_BREAK) /* break? */ + { + if (sysd_hlt_enb() && smp_hlt_enb(& hlt_pin)) /* if enabled, halt */ + hlt_pin = 1; + tti_unit.buf = TTIBUF_ERR | TTIBUF_FRM | TTIBUF_RBR; + } + else + { + tti_unit.buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags)); + } + uptr->pos = uptr->pos + 1; + tti_csr = tti_csr | CSR_DONE; + if (tti_csr & CSR_IE) + SET_INT (TTI); + return SCPE_OK; +} + +t_stat tti_reset (DEVICE *dptr) +{ + RUN_SCOPE; + tti_csr = 0; + CLR_INT (TTI); + /* console input is available only for the primary CPU */ + if (cpu_unit->is_primary_cpu()) + { + tti_unit.buf = 0; + + if (use_clock_thread) + { + sim_activate_clk_cosched_abs (&tti_unit, 1); + } + else + { + int32 x_tmr_poll = weak_read_var(tmr_poll); + sim_activate_abs (&tti_unit, KBD_WAIT (tti_unit.wait, x_tmr_poll)); + } + } + return SCPE_OK; +} + +t_bool tti_rcv_char(int32 c) +{ + AUTO_LOCK(tti_typeahead_lock); + + if ((c & SCPE_BREAK) && sysd_hlt_enb()) + { + if (! smp_hlt_enb()) + return FALSE; + tti_typeahead.clear(); + } + + if (tti_typeahead.put(c)) + { + wakeup_cpu(& cpu_unit_0); + return TRUE; + } + else + { + return FALSE; + } +} + +void tti_clear_pending_typeahead() +{ + AUTO_LOCK(tti_typeahead_lock); + tti_typeahead.clear(); +} + +/* Terminal output routines + + tto_svc process event (character typed) + tto_reset process reset +*/ + +t_stat tto_svc (RUN_SVC_DECL, UNIT *uptr) +{ + // RUN_SVC_CHECK_CANCELLED(uptr); // not required for per-CPU devices + int32 c; + t_stat r; + + c = sim_tt_outcvt (tto_buf, TT_GET_MODE (uptr->flags)); + if (c >= 0) + { + if ((r = sim_putchar_s (c)) != SCPE_OK) /* output; error? */ + { + sim_activate (uptr, uptr->wait); /* retry */ + return (r == SCPE_STALL) ? SCPE_OK : r; /* !stall? report */ + } + } + tto_csr = tto_csr | CSR_DONE; + if (tto_csr & CSR_IE) + SET_INT (TTO); + // uptr->pos = uptr->pos + 1; /* non-essential counter, not worth locking */ + return SCPE_OK; +} + +t_stat tto_reset (DEVICE *dptr) +{ + RUN_SCOPE; + tto_buf = 0; + tto_csr = CSR_DONE; + CLR_INT (TTO); + sim_cancel (&tto_unit); /* deactivate unit */ + return SCPE_OK; +} + +/* Clock routines + + clk_svc process event (clock tick) + clk_reset process reset + todr_powerup powerup for TODR (get date from system) +*/ + +t_stat clk_svc (RUN_SVC_DECL, UNIT *uptr) +{ + // RUN_SVC_CHECK_CANCELLED(uptr); // not required for per-CPU devices + return clk_svc_ex(RUN_PASS, TRUE); +} + +/* + * clk_svc_ex is invoked: + * + * * if SYNCLK clock is enabled (use_clock_thread is TRUE), + * then by read_irqs_to_local when SYNCLK interrupt is received + * + * * if SYNCLK clock is not enabled (use_clock_thread is FALSE), + * then by clk_svc on expiration of clock tick interval + * + */ +t_stat clk_svc_ex (RUN_DECL, t_bool clk_ie) +{ + int32 t; + + if ((clk_csr & CSR_IE) && clk_ie) + { + SET_INT (CLK); + } + else if (!use_clock_thread && cpu_unit->cpu_thread_priority == SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM) + { + /* + * When clock strobe thread is not used, sim_idle leaves thread priority elevated in case clock event + * is about to happen, so the event may get processed promptly. If clock interrupt processing is + * enabled via CSR_IE, priority will be dropped during interrupt delivery when it performs + * cpu_reevaluate_thread_priority. If CLK interrupt signalling is disabled however by the CSR, + * drop priority here. + */ + cpu_reevaluate_thread_priority(RUN_PASS); + } + + t = sim_rtcn_calb (RUN_PASS, clk_tps, TMR_CLK); /* calibrate clock */ + sim_activate (&clk_unit, t); /* reactivate unit */ + + if (cpu_unit->is_primary_cpu()) + { + if (!todr_blow) /* incr TODR */ + todr_reg = todr_reg + 1; + } + + return SCPE_OK; +} + +/* TODR resync routine */ + +t_stat todr_resync (RUN_DECL) +{ + uint32 base; + time_t curr; + struct tm *ctm; + + curr = time (NULL); /* get curr time */ + if (curr == (time_t) -1) /* error? */ + return SCPE_NOFNC; + ctm = localtime (&curr); /* decompose */ + if (ctm == NULL) /* error? */ + return SCPE_NOFNC; + base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */ + ctm->tm_hour) * 60) + + ctm->tm_min) * 60) + + ctm->tm_sec; + todr_reg = (base * 100) + 0x10000000; /* cvt to VAX form */ + todr_blow = 0; + return SCPE_OK; +} + +/* Reset routine */ + +t_stat clk_reset (DEVICE *dptr) +{ + RUN_SCOPE; + int32 t; + + if (cpu_unit->is_primary_cpu()) + todr_resync (RUN_PASS); /* resync clock */ + + clk_csr = 0; + CLR_INT (CLK); + syncw_reeval_sys(RUN_PASS); + + t = sim_rtcn_init (RUN_PASS, clk_unit.wait, TMR_CLK); /* init timer */ + sim_activate_abs (&clk_unit, t); /* activate unit */ + + return SCPE_OK; +} + +/* + * Estimate of cycles till next expected SYNCLK event + */ +int32 synclk_expected_next(RUN_DECL) +{ + uint32 period = (uint32) weak_read(atomic_var(tmr_poll)); + uint32 delta = CPU_CURRENT_CYCLES - cpu_unit->cpu_last_synclk_cycles; + if (delta >= period) + { + period = 1; + } + else + { + period -= delta; + const uint32 max_period = INT32_MAX / 4; /* avoid overflows */ + if (period >= max_period) + period = max_period; + } + + return (int32) period; +} \ No newline at end of file diff --git a/src/VAX/vax_sys.cpp b/src/VAX/vax_sys.cpp new file mode 100644 index 0000000..70e252a --- /dev/null +++ b/src/VAX/vax_sys.cpp @@ -0,0 +1,1473 @@ +/* vax_sys.c: VAX simulator interface + + Copyright (c) 1998-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. + + 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) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 15-Sep-04 RMS Fixed bugs in character display and parse + 30-Sep-04 RMS Fixed bugs in parsing indirect displacement modes + Added compatibility mode support + 04-Sep-04 RMS Added octa instruction support + 02-Sep-04 RMS Fixed parse branch return status + 13-Jul-04 RMS Fixed bad block routine + 16-Jun-04 RMS Added DHQ11 support + 21-Mar-04 RMS Added RXV21 support + 06-May-03 RMS Added support for second DELQA + 12-Oct-02 RMS Added multiple RQ controller support + 10-Oct-02 RMS Added DELQA support + 21-Sep-02 RMS Extended symbolic ex/mod to all byte devices + 06-Sep-02 RMS Added TMSCP support + 14-Jul-02 RMS Added infinite loop message +*/ + +#include "sim_defs.h" +#include "vax_defs.h" +#include + +#if defined (FULL_VAX) +#define ODC(x) (x) +#else +#define ODC(x) ((x) << DR_V_USPMASK) +#endif + +extern REG cpu_reg[]; +extern int32 saved_PC; +extern int32 sim_switches; + +t_stat fprint_sym_m (SMP_FILE *of, uint32 addr, t_value *val); +int32 fprint_sym_qoimm (SMP_FILE *of, t_value *val, int32 vp, int32 lnt); +t_stat parse_char (char *cptr, t_value *val, int32 lnt); +t_stat parse_sym_m (char *cptr, uint32 addr, t_value *val); +int32 parse_brdisp (char *cptr, uint32 addr, t_value *val, + int32 vp, int32 lnt, t_stat *r); +int32 parse_spec (char *cptr, uint32 addr, t_value *val, + int32 vp, int32 disp, t_stat *r); +char *parse_rnum (char *cptr, int32 *rn); +int32 parse_sym_qoimm (int32 *lit, t_value *val, int32 vp, + int lnt, int32 minus); + +extern t_stat fprint_sym_cm (SMP_FILE *of, t_addr addr, t_value *bytes, int32 sw); +extern t_stat parse_sym_cm (char *cptr, t_addr addr, t_value *bytes, 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 +*/ + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 60; + +const char *sim_stop_messages[] = { + "Unknown error", + // STOP_HALT + "HALT instruction", + // STOP_IBKPT + "Breakpoint", + // STOP_CHMFI + "CHMx on interrupt stack", + // STOP_ILLVEC + "Invalid SCB vector", + // STOP_INIE + "Exception in interrupt or exception", + // STOP_PPTE + "Process PTE in P0 or P1 space", + // STOP_UIPL + "Interrupt at undefined IPL", + // STOP_RQ + "Fatal RQDX3 error", + // STOP_LOOP + "Infinite loop", + // STOP_SANITY + "Sanity timer expired", + // STOP_SWDN + "Software done", + // STOP_BOOT + "Reboot request failed", + // STOP_UNKNOWN + "Unknown error", + // STOP_UNKABO + "Unknown abort code", + // STOP_INVSYSOP + "Invalid system operation" + }; + + +/* Dispatch/decoder table + + The first entry contains: + - FPD legal flag (DR_F) + - number of specifiers for decode bits 2:0> + - number of specifiers for unimplemented instructions bits<6:4> + */ + +extern const uint16 drom[NUM_INST][MAX_SPEC + 1]; + +const uint16 drom[NUM_INST][MAX_SPEC + 1] = { + { 0, 0, 0, 0, 0, 0, 0 }, /* HALT */ + { 0, 0, 0, 0, 0, 0, 0 }, /* NOP */ + { 0, 0, 0, 0, 0, 0, 0 }, /* REI */ + { 0, 0, 0, 0, 0, 0, 0 }, /* BPT */ + { 0, 0, 0, 0, 0, 0, 0 }, /* RET */ + { 0, 0, 0, 0, 0, 0, 0 }, /* RSB */ + { 0, 0, 0, 0, 0, 0, 0 }, /* LDPCTX */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SVPCTX */ + { 4+DR_F, RW, AB, RW, AB, 0, 0 }, /* CVTPS */ + { 4+DR_F, RW, AB, RW, AB, 0, 0 }, /* CVTSP */ + { 6, RL, RL, RL, RL, RL, WL }, /* INDEX */ + { 4+DR_F, AB, RL, RW, AB, 0, 0 }, /* CRC */ + { 3, RB, RW, AB, 0, 0, 0 }, /* PROBER */ + { 3, RB, RW, AB, 0, 0, 0 }, /* PROBEW */ + { 2, AB, AB, 0, 0, 0, 0 }, /* INSQUE */ + { 2, AB, WL, 0, 0, 0, 0 }, /* REMQUE */ + { 1, BB, 0, 0, 0, 0, 0 }, /* BSBB */ + { 1, BB, 0, 0, 0, 0, 0 }, /* BRB */ + { 1, BB, 0, 0, 0, 0, 0 }, /* BNEQ */ + { 1, BB, 0, 0, 0, 0, 0 }, /* BEQL */ + { 1, BB, 0, 0, 0, 0, 0 }, /* BGTR */ + { 1, BB, 0, 0, 0, 0, 0 }, /* BLEQ */ + { 1, AB, 0, 0, 0, 0, 0 }, /* JSB */ + { 1, AB, 0, 0, 0, 0, 0 }, /* JMP */ + { 1, BB, 0, 0, 0, 0, 0 }, /* BGEQ */ + { 1, BB, 0, 0, 0, 0, 0 }, /* BLSS */ + { 1, BB, 0, 0, 0, 0, 0 }, /* BGTRU */ + { 1, BB, 0, 0, 0, 0, 0 }, /* BLEQU */ + { 1, BB, 0, 0, 0, 0, 0 }, /* BVC */ + { 1, BB, 0, 0, 0, 0, 0 }, /* BVS */ + { 1, BB, 0, 0, 0, 0, 0 }, /* BCC */ + { 1, BB, 0, 0, 0, 0, 0 }, /* BCS */ + { 4+DR_F, RW, AB, RW, AB, 0, 0 }, /* ADDP4 */ + { 6+DR_F, RW, AB, RW, AB, RW, AB }, /* ADDP6 */ + { 4+DR_F, RW, AB, RW, AB, 0, 0 }, /* SUBP4 */ + { 6+DR_F, RW, AB, RW, AB, RW, AB }, /* SUBP6 */ + { 5+DR_F, RW, AB, AB, RW, AB, 0 }, /* CVTPT */ + { 6+DR_F, RW, AB, RW, AB, RW, AB }, /* MULP6 */ + { 5+DR_F, RW, AB, AB, RW, AB, 0 }, /* CVTTP */ + { 6+DR_F, RW, AB, RW, AB, RW, AB }, /* DIVP6 */ + { 3+DR_F, RW, AB, AB, 0, 0, 0 }, /* MOVC3 */ + { 3+DR_F, RW, AB, AB, 0, 0, 0 }, /* CMPC3 */ + { 4+DR_F, RW, AB, AB, RB, 0, 0 }, /* SCANC */ + { 4+DR_F, RW, AB, AB, RB, 0, 0 }, /* SPANC */ + { 5+DR_F, RW, AB, RB, RW, AB, 0 }, /* MOVC5 */ + { 5+DR_F, RW, AB, RB, RW, AB, 0 }, /* CMPC5 */ + { 6+DR_F, RW, AB, RB, AB, RW, AB }, /* MOVTC */ + { 6+DR_F, RW, AB, RB, AB, RW, AB }, /* MOVTUC */ + { 1, BW, 0, 0, 0, 0, 0 }, /* BSBW */ + { 1, BW, 0, 0, 0, 0, 0 }, /* BRW */ + { 2, RW, WL, 0, 0, 0, 0 }, /* CVTWL */ + { 2, RW, WB, 0, 0, 0, 0 }, /* CVTWB */ + { 3+DR_F, RW, AB, AB, 0, 0, 0 }, /* MOVP */ + { 3+DR_F, RW, AB, AB, 0, 0, 0 }, /* CMPP3 */ + { 3+DR_F, RW, AB, WL, 0, 0, 0 }, /* CVTPL */ + { 4+DR_F, RW, AB, RW, AB, 0, 0 }, /* CMPP4 */ + { 4+DR_F, RW, AB, AB, AB, 0, 0 }, /* EDITPC */ + { 4+DR_F, RW, AB, RW, AB, 0, 0 }, /* MATCHC */ + { 3+DR_F, RB, RW, AB, 0, 0, 0 }, /* LOCC */ + { 3+DR_F, RB, RW, AB, 0, 0, 0 }, /* SKPC */ + { 2, RW, WL, 0, 0, 0, 0 }, /* MOVZWL */ + { 4, RW, RW, MW, BW, 0, 0 }, /* ACBW */ + { 2, AW, WL, 0, 0, 0, 0 }, /* MOVAW */ + { 1, AW, 0, 0, 0, 0, 0 }, /* PUSHAW */ + { 2, RF, ML, 0, 0, 0, 0 }, /* ADDF2 */ + { 3, RF, RF, WL, 0, 0, 0 }, /* ADDF3 */ + { 2, RF, ML, 0, 0, 0, 0 }, /* SUBF2 */ + { 3, RF, RF, WL, 0, 0, 0 }, /* SUBF3 */ + { 2, RF, ML, 0, 0, 0, 0 }, /* MULF2 */ + { 3, RF, RF, WL, 0, 0, 0 }, /* MULF3 */ + { 2, RF, ML, 0, 0, 0, 0 }, /* DIVF2 */ + { 3, RF, RF, WL, 0, 0, 0 }, /* DIVF3 */ + { 2, RF, WB, 0, 0, 0, 0 }, /* CVTFB */ + { 2, RF, WW, 0, 0, 0, 0 }, /* CVTFW */ + { 2, RF, WL, 0, 0, 0, 0 }, /* CVTFL */ + { 2, RF, WL, 0, 0, 0, 0 }, /* CVTRFL */ + { 2, RB, WL, 0, 0, 0, 0 }, /* CVTBF */ + { 2, RW, WL, 0, 0, 0, 0 }, /* CVTWF */ + { 2, RL, WL, 0, 0, 0, 0 }, /* CVTLF */ + { 4, RF, RF, ML, BW, 0, 0 }, /* ACBF */ + { 2, RF, WL, 0, 0, 0, 0 }, /* MOVF */ + { 2, RF, RF, 0, 0, 0, 0 }, /* CMPF */ + { 2, RF, WL, 0, 0, 0, 0 }, /* MNEGF */ + { 1, RF, 0, 0, 0, 0, 0 }, /* TSTF */ + { 5, RF, RB, RF, WL, WL, 0 }, /* EMODF */ + { 3, RF, RW, AB, 0, 0, 0 }, /* POLYF */ + { 2, RF, WQ, 0, 0, 0, 0 }, /* CVTFD */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 2, RW, WW, 0, 0, 0, 0 }, /* ADAWI */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 2, AB, AQ, 0, 0, 0, 0 }, /* INSQHI */ + { 2, AB, AQ, 0, 0, 0, 0 }, /* INSQTI */ + { 2, AQ, WL, 0, 0, 0, 0 }, /* REMQHI */ + { 2, AQ, WL, 0, 0, 0, 0 }, /* REMQTI */ + { 2, RD, MQ, 0, 0, 0, 0 }, /* ADDD2 */ + { 3, RD, RD, WQ, 0, 0, 0 }, /* ADDD3 */ + { 2, RD, MQ, 0, 0, 0, 0 }, /* SUBD2 */ + { 3, RD, RD, WQ, 0, 0, 0 }, /* SUBD3 */ + { 2, RD, MQ, 0, 0, 0, 0 }, /* MULD2 */ + { 3, RD, RD, WQ, 0, 0, 0 }, /* MULD3 */ + { 2, RD, MQ, 0, 0, 0, 0 }, /* DIVD2 */ + { 3, RD, RD, WQ, 0, 0, 0 }, /* DIVD3 */ + { 2, RD, WB, 0, 0, 0, 0 }, /* CVTDB */ + { 2, RD, WW, 0, 0, 0, 0 }, /* CVTDW */ + { 2, RD, WL, 0, 0, 0, 0 }, /* CVTDL */ + { 2, RD, WL, 0, 0, 0, 0 }, /* CVTRDL */ + { 2, RB, WQ, 0, 0, 0, 0 }, /* CVTBD */ + { 2, RW, WQ, 0, 0, 0, 0 }, /* CVTWD */ + { 2, RL, WQ, 0, 0, 0, 0 }, /* CVTLD */ + { 4, RD, RD, MQ, BW, 0, 0 }, /* ACBD */ + { 2, RD, WQ, 0, 0, 0, 0 }, /* MOVD */ + { 2, RD, RD, 0, 0, 0, 0 }, /* CMPD */ + { 2, RD, WQ, 0, 0, 0, 0 }, /* MNEGD */ + { 1, RD, 0, 0, 0, 0, 0 }, /* TSTD */ + { 5, RD, RB, RD, WL, WQ, 0 }, /* EMODD */ + { 3, RD, RW, AB, 0, 0, 0 }, /* POLYD */ + { 2, RD, WL, 0, 0, 0, 0 }, /* CVTDF */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 3, RB, RL, WL, 0, 0, 0 }, /* ASHL */ + { 3, RB, RQ, WQ, 0, 0, 0 }, /* ASHQ */ + { 4, RL, RL, RL, WQ, 0, 0 }, /* EMUL */ + { 4, RL, RQ, WL, WL, 0, 0 }, /* EDIV */ + { 1, WQ, 0, 0, 0, 0, 0 }, /* CLRQ */ + { 2, RQ, WQ, 0, 0, 0, 0 }, /* MOVQ */ + { 2, AQ, WL, 0, 0, 0, 0 }, /* MOVAQ */ + { 1, AQ, 0, 0, 0, 0, 0 }, /* PUSHAQ */ + { 2, RB, MB, 0, 0, 0, 0 }, /* ADDB2 */ + { 3, RB, RB, WB, 0, 0, 0 }, /* ADDB3 */ + { 2, RB, MB, 0, 0, 0, 0 }, /* SUBB2 */ + { 3, RB, RB, WB, 0, 0, 0 }, /* SUBB3 */ + { 2, RB, MB, 0, 0, 0, 0 }, /* MULB2 */ + { 3, RB, RB, WB, 0, 0, 0 }, /* MULB3 */ + { 2, RB, MB, 0, 0, 0, 0 }, /* DIVB2 */ + { 3, RB, RB, WB, 0, 0, 0 }, /* DIVB3 */ + { 2, RB, MB, 0, 0, 0, 0 }, /* BISB2 */ + { 3, RB, RB, WB, 0, 0, 0 }, /* BISB3 */ + { 2, RB, MB, 0, 0, 0, 0 }, /* BICB2 */ + { 3, RB, RB, WB, 0, 0, 0 }, /* BICB3 */ + { 2, RB, MB, 0, 0, 0, 0 }, /* XORB2 */ + { 3, RB, RB, WB, 0, 0, 0 }, /* XORB3 */ + { 2, RB, WB, 0, 0, 0, 0 }, /* MNEGB */ + { 3, RB, RB, RB, 0, 0, 0 }, /* CASEB */ + { 2, RB, WB, 0, 0, 0, 0 }, /* MOVB */ + { 2, RB, RB, 0, 0, 0, 0 }, /* CMPB */ + { 2, RB, WB, 0, 0, 0, 0 }, /* MCOMB */ + { 2, RB, RB, 0, 0, 0, 0 }, /* BITB */ + { 1, WB, 0, 0, 0, 0, 0 }, /* CLRB */ + { 1, RB, 0, 0, 0, 0, 0 }, /* TSTB */ + { 1, MB, 0, 0, 0, 0, 0 }, /* INCB */ + { 1, MB, 0, 0, 0, 0, 0 }, /* DECB */ + { 2, RB, WL, 0, 0, 0, 0 }, /* CVTBL */ + { 2, RB, WW, 0, 0, 0, 0 }, /* CVTBW */ + { 2, RB, WL, 0, 0, 0, 0 }, /* MOVZBL */ + { 2, RB, WW, 0, 0, 0, 0 }, /* MOVZBW */ + { 3, RB, RL, WL, 0, 0, 0 }, /* ROTL */ + { 4, RB, RB, MB, BW, 0, 0 }, /* ACBB */ + { 2, AB, WL, 0, 0, 0, 0 }, /* MOVAB */ + { 1, AB, 0, 0, 0, 0, 0 }, /* PUSHAB */ + { 2, RW, MW, 0, 0, 0, 0 }, /* ADDW2 */ + { 3, RW, RW, WW, 0, 0, 0 }, /* ADDW3 */ + { 2, RW, MW, 0, 0, 0, 0 }, /* SUBW2 */ + { 3, RW, RW, WW, 0, 0, 0 }, /* SUBW3 */ + { 2, RW, MW, 0, 0, 0, 0 }, /* MULW2 */ + { 3, RW, RW, WW, 0, 0, 0 }, /* MULW3 */ + { 2, RW, MW, 0, 0, 0, 0 }, /* DIVW2 */ + { 3, RW, RW, WW, 0, 0, 0 }, /* DIVW3 */ + { 2, RW, MW, 0, 0, 0, 0 }, /* BISW2 */ + { 3, RW, RW, WW, 0, 0, 0 }, /* BISW3 */ + { 2, RW, MW, 0, 0, 0, 0 }, /* BICW2 */ + { 3, RW, RW, WW, 0, 0, 0 }, /* BICW3 */ + { 2, RW, MW, 0, 0, 0, 0 }, /* XORW2 */ + { 3, RW, RW, WW, 0, 0, 0 }, /* XORW3 */ + { 2, RW, WW, 0, 0, 0, 0 }, /* MNEGW */ + { 3, RW, RW, RW, 0, 0, 0 }, /* CASEW */ + { 2, RW, WW, 0, 0, 0, 0 }, /* MOVW */ + { 2, RW, RW, 0, 0, 0, 0 }, /* CMPW */ + { 2, RW, WW, 0, 0, 0, 0 }, /* MCOMW */ + { 2, RW, RW, 0, 0, 0, 0 }, /* BITW */ + { 1, WW, 0, 0, 0, 0, 0 }, /* CLRW */ + { 1, RW, 0, 0, 0, 0, 0 }, /* TSTW */ + { 1, MW, 0, 0, 0, 0, 0 }, /* INCW */ + { 1, MW, 0, 0, 0, 0, 0 }, /* DECW */ + { 1, RW, 0, 0, 0, 0, 0 }, /* BISPSW */ + { 1, RW, 0, 0, 0, 0, 0 }, /* BICPSW */ + { 1, RW, 0, 0, 0, 0, 0 }, /* POPR */ + { 1, RW, 0, 0, 0, 0, 0 }, /* PUSHR */ + { 1, RW, 0, 0, 0, 0, 0 }, /* CHMK */ + { 1, RW, 0, 0, 0, 0, 0 }, /* CHME */ + { 1, RW, 0, 0, 0, 0, 0 }, /* CHMS */ + { 1, RW, 0, 0, 0, 0, 0 }, /* CHMU */ + { 2, RL, ML, 0, 0, 0, 0 }, /* ADDL2 */ + { 3, RL, RL, WL, 0, 0, 0 }, /* ADDL3 */ + { 2, RL, ML, 0, 0, 0, 0 }, /* SUBL2 */ + { 3, RL, RL, WL, 0, 0, 0 }, /* SUBL3 */ + { 2, RL, ML, 0, 0, 0, 0 }, /* MULL2 */ + { 3, RL, RL, WL, 0, 0, 0 }, /* MULL3 */ + { 2, RL, ML, 0, 0, 0, 0 }, /* DIVL2 */ + { 3, RL, RL, WL, 0, 0, 0 }, /* DIVL3 */ + { 2, RL, ML, 0, 0, 0, 0 }, /* BISL2 */ + { 3, RL, RL, WL, 0, 0, 0 }, /* BISL3 */ + { 2, RL, ML, 0, 0, 0, 0 }, /* BICL2 */ + { 3, RL, RL, WL, 0, 0, 0 }, /* BICL3 */ + { 2, RL, ML, 0, 0, 0, 0 }, /* XORL2 */ + { 3, RL, RL, WL, 0, 0, 0 }, /* XORL3 */ + { 2, RL, WL, 0, 0, 0, 0 }, /* MNEGL */ + { 3, RL, RL, RL, 0, 0, 0 }, /* CASEL */ + { 2, RL, WL, 0, 0, 0, 0 }, /* MOVL */ + { 2, RL, RL, 0, 0, 0, 0 }, /* CMPL */ + { 2, RL, WL, 0, 0, 0, 0 }, /* MCOML */ + { 2, RL, RL, 0, 0, 0, 0 }, /* BITL */ + { 1, WL, 0, 0, 0, 0, 0 }, /* CLRL */ + { 1, RL, 0, 0, 0, 0, 0 }, /* TSTL */ + { 1, ML, 0, 0, 0, 0, 0 }, /* INCL */ + { 1, ML, 0, 0, 0, 0, 0 }, /* DECL */ + { 2, RL, ML, 0, 0, 0, 0 }, /* ADWC */ + { 2, RL, ML, 0, 0, 0, 0 }, /* SBWC */ + { 2, RL, RL, 0, 0, 0, 0 }, /* MTPR */ + { 2, RL, WL, 0, 0, 0, 0 }, /* MFPR */ + { 1, WL, 0, 0, 0, 0, 0 }, /* MOVPSL */ + { 1, RL, 0, 0, 0, 0, 0 }, /* PUSHL */ + { 2, AL, WL, 0, 0, 0, 0 }, /* MOVAL */ + { 1, AL, 0, 0, 0, 0, 0 }, /* PUSHAL */ + { 3, RL, VB, BB, 0, 0, 0 }, /* BBS */ + { 3, RL, VB, BB, 0, 0, 0 }, /* BBC */ + { 3, RL, VB, BB, 0, 0, 0 }, /* BBSS */ + { 3, RL, VB, BB, 0, 0, 0 }, /* BBCS */ + { 3, RL, VB, BB, 0, 0, 0 }, /* BBSC */ + { 3, RL, VB, BB, 0, 0, 0 }, /* BBCC */ + { 3, RL, VB, BB, 0, 0, 0 }, /* BBSSI */ + { 3, RL, VB, BB, 0, 0, 0 }, /* BBCCI */ + { 2, RL, BB, 0, 0, 0, 0 }, /* BLBS */ + { 2, RL, BB, 0, 0, 0, 0 }, /* BLBC */ + { 4, RL, RB, VB, WL, 0, 0 }, /* FFS */ + { 4, RL, RB, VB, WL, 0, 0 }, /* FFC */ + { 4, RL, RB, VB, RL, 0, 0 }, /* CMPV */ + { 4, RL, RB, VB, RL, 0, 0 }, /* CMPZV */ + { 4, RL, RB, VB, WL, 0, 0 }, /* EXTV */ + { 4, RL, RB, VB, WL, 0, 0 }, /* EXTZV */ + { 4, RL, RL, RB, VB, 0, 0 }, /* INSV */ + { 4, RL, RL, ML, BW, 0, 0 }, /* ACBL */ + { 3, RL, ML, BB, 0, 0, 0 }, /* AOBLSS */ + { 3, RL, ML, BB, 0, 0, 0 }, /* AOBLEQ */ + { 2, ML, BB, 0, 0, 0, 0 }, /* SOBGEQ */ + { 2, ML, BB, 0, 0, 0, 0 }, /* SOBGTR */ + { 2, RL, WB, 0, 0, 0, 0 }, /* CVTLB */ + { 2, RL, WW, 0, 0, 0, 0 }, /* CVTLW */ + { 6+DR_F, RB, RW, AB, RB, RW, AB }, /* ASHP */ + { 3+DR_F, RL, RW, AB, 0, 0, 0 }, /* CVTLP */ + { 2, AB, AB, 0, 0, 0, 0 }, /* CALLG */ + { 2, RL, AB, 0, 0, 0, 0 }, /* CALLS */ + { 0, 0, 0, 0, 0, 0, 0 }, /* XFC */ + { 0, 0, 0, 0, 0, 0, 0 }, /* 0FD */ + { 0, 0, 0, 0, 0, 0, 0 }, /* 0FE */ + { 0, 0, 0, 0, 0, 0, 0 }, /* 0FF */ + { 0, 0, 0, 0, 0, 0, 0 }, /* 100-10F */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* 110-11F */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* 120-12F */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* 130-13F */ + { 0, 0, 0, 0, 0, 0, 0 }, + { ODC(2), RD, WO, 0, 0, 0, 0 }, /* CVTDH */ + { 2, RG, WL, 0, 0, 0, 0 }, /* CVTGF */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 2, RG, MQ, 0, 0, 0, 0 }, /* ADDG2 */ + { 3, RG, RG, WQ, 0, 0, 0 }, /* ADDG3 */ + { 2, RG, MQ, 0, 0, 0, 0 }, /* SUBG2 */ + { 3, RG, RG, WQ, 0, 0, 0 }, /* SUBG3 */ + { 2, RG, MQ, 0, 0, 0, 0 }, /* MULG2 */ + { 3, RG, RG, WQ, 0, 0, 0 }, /* MULG3 */ + { 2, RG, MQ, 0, 0, 0, 0 }, /* DIVG2 */ + { 3, RG, RG, WQ, 0, 0, 0 }, /* DIVG3 */ + { 2, RG, WB, 0, 0, 0, 0 }, /* CVTGB */ + { 2, RG, WW, 0, 0, 0, 0 }, /* CVTGW */ + { 2, RG, WL, 0, 0, 0, 0 }, /* CVTGL */ + { 2, RG, WL, 0, 0, 0, 0 }, /* CVTRGL */ + { 2, RB, WQ, 0, 0, 0, 0 }, /* CVTBG */ + { 2, RW, WQ, 0, 0, 0, 0 }, /* CVTWG */ + { 2, RL, WQ, 0, 0, 0, 0 }, /* CVTLG */ + { 4, RG, RG, MQ, BW, 0, 0 }, /* ACBG */ + { 2, RG, WQ, 0, 0, 0, 0 }, /* MOVG */ + { 2, RG, RG, 0, 0, 0, 0 }, /* CMPG */ + { 2, RG, WQ, 0, 0, 0, 0 }, /* MNEGG */ + { 1, RG, 0, 0, 0, 0, 0 }, /* TSTG */ + { 5, RG, RW, RG, WL, WQ, 0 }, /* EMODG */ + { 3, RG, RW, AB, 0, 0, 0 }, /* POLYG */ + { ODC(2), RG, WO, 0, 0, 0, 0 }, /* CVTGH */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { ODC(2), RH, MO, 0, 0, 0, 0 }, /* ADDH2 */ + { ODC(3), RH, RH, WO, 0, 0, 0 }, /* ADDH3 */ + { ODC(2), RH, MO, 0, 0, 0, 0 }, /* SUBH2 */ + { ODC(3), RH, RH, WO, 0, 0, 0 }, /* SUBH3 */ + { ODC(2), RH, MO, 0, 0, 0, 0 }, /* MULH2 */ + { ODC(3), RH, RH, WO, 0, 0, 0 }, /* MULH3 */ + { ODC(2), RH, MO, 0, 0, 0, 0 }, /* DIVH2 */ + { ODC(3), RH, RH, WO, 0, 0, 0 }, /* DIVH3 */ + { ODC(2), RH, WB, 0, 0, 0, 0 }, /* CVTHB */ + { ODC(2), RH, WW, 0, 0, 0, 0 }, /* CVTHW */ + { ODC(2), RH, WL, 0, 0, 0, 0 }, /* CVTHL */ + { ODC(2), RH, WL, 0, 0, 0, 0 }, /* CVTRHL */ + { ODC(2), RB, WO, 0, 0, 0, 0 }, /* CVTBH */ + { ODC(2), RW, WO, 0, 0, 0, 0 }, /* CVTWH */ + { ODC(2), RL, WO, 0, 0, 0, 0 }, /* CVTLH */ + { ODC(4), RH, RH, MO, BW, 0, 0 }, /* ACBH */ + { ODC(2), RH, RO, 0, 0, 0, 0 }, /* MOVH */ + { ODC(2), RH, RH, 0, 0, 0, 0 }, /* CMPH */ + { ODC(2), RH, WO, 0, 0, 0, 0 }, /* MNEGH */ + { ODC(1), RH, 0, 0, 0, 0, 0 }, /* TSTH */ + { ODC(5), RH, RW, RH, WL, WO, 0 }, /* EMODH */ + { ODC(3), RH, RW, AB, 0, 0, 0 }, /* POLYH */ + { ODC(2), RH, WQ, 0, 0, 0, 0 }, /* CVTHG */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { 0, 0, 0, 0, 0, 0, 0 }, /* reserved */ + { ODC(1), WO, 0, 0, 0, 0, 0 }, /* CLRO */ + { ODC(2), RO, RO, 0, 0, 0, 0 }, /* MOVO */ + { ODC(2), AO, WL, 0, 0, 0, 0 }, /* MOVAO*/ + { ODC(1), AO, 0, 0, 0, 0, 0 }, /* PUSHAO*/ + { 0, 0, 0, 0, 0, 0, 0 }, /* 180-18F */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* 190-19F */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { ODC(2), RF, WO, 0, 0, 0, 0 }, /* CVTFH */ + { 2, RF, WQ, 0, 0, 0, 0 }, /* CVTFG */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* 1A0-1AF */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* 1B0-1BF */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* 1C0-1CF */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* 1D0-1DF */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* 1E0-1EF */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* 1F0-1FF */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { ODC(2), RH, WL, 0, 0, 0, 0 }, /* CVTHF */ + { ODC(2), RH, WQ, 0, 0, 0, 0 }, /* CVTHD */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 } +}; + +/* Opcode mnemonics table */ + +const char *opcode[] = { +"HALT", "NOP", "REI", "BPT", "RET", "RSB", "LDPCTX", "SVPCTX", +"CVTPS", "CVTSP", "INDEX", "CRC", "PROBER", "PROBEW", "INSQUE", "REMQUE", +"BSBB", "BRB", "BNEQ", "BEQL", "BGTR", "BLEQ", "JSB", "JMP", +"BGEQ", "BLSS", "BGTRU", "BLEQU", "BVC", "BVS", "BGEQU", "BLSSU", +"ADDP4", "ADDP6", "SUBP4", "SUBP6", "CVTPT", "MULP", "CVTTP", "DIVP", +"MOVC3", "CMPC3", "SCANC", "SPANC", "MOVC5", "CMPC5", "MOVTC", "MOVTUC", +"BSBW", "BRW", "CVTWL", "CVTWB", "MOVP", "CMPP3", "CVTPL", "CMPP4", +"EDITPC", "MATCHC", "LOCC", "SKPC", "MOVZWL", "ACBW", "MOVAW", "PUSHAW", +"ADDF2", "ADDF3", "SUBF2", "SUBF3", "MULF2", "MULF3", "DIVF2", "DIVF3", +"CVTFB", "CVTFW", "CVTFL", "CVTRFL", "CVTBF", "CVTWF", "CVTLF", "ACBF", +"MOVF", "CMPF", "MNEGF", "TSTF", "EMODF", "POLYF", "CVTFD", NULL, +"ADAWI", NULL, NULL, NULL, "INSQHI", "INSQTI", "REMQHI", "REMQTI", +"ADDD2", "ADDD3", "SUBD2", "SUBD3", "MULD2", "MULD3", "DIVD2", "DIVD3", +"CVTDB", "CVTDW", "CVTDL", "CVTRDL", "CVTBD", "CVTWD", "CVTLD", "ACBD", +"MOVD", "CMPD", "MNEGD", "TSTD", "EMODD", "POLYD", "CVTDF", NULL, +"ASHL", "ASHQ", "EMUL", "EDIV", "CLRQ", "MOVQ", "MOVAQ", "PUSHAQ", +"ADDB2", "ADDB3", "SUBB2", "SUBB3", "MULB2", "MULB3", "DIVB2", "DIVB3", +"BISB2", "BISB3", "BICB2", "BICB3", "XORB2", "XORB3", "MNEGB", "CASEB", +"MOVB", "CMPB", "MCOMB", "BITB", "CLRB", "TSTB", "INCB", "DECB", +"CVTBL", "CVTBW", "MOVZBL", "MOVZBW", "ROTL", "ACBB", "MOVAB", "PUSHAB", +"ADDW2", "ADDW3", "SUBW2", "SUBW3", "MULW2", "MULW3", "DIVW2", "DIVW3", +"BISW2", "BISW3", "BICW2", "BICW3", "XORW2", "XORW3", "MNEGW", "CASEW", +"MOVW", "CMPW", "MCOMW", "BITW", "CLRW", "TSTW", "INCW", "DECW", +"BISPSW", "BICPSW", "POPR", "PUSHR", "CHMK", "CHME", "CHMS", "CHMU", +"ADDL2", "ADDL3", "SUBL2", "SUBL3", "MULL2", "MULL3", "DIVL2", "DIVL3", +"BISL2", "BISL3", "BICL2", "BICL3", "XORL2", "XORL3", "MNEGL", "CASEL", +"MOVL", "CMPL", "MCOML", "BITL", "CLRL", "TSTL", "INCL", "DECL", +"ADWC", "SBWC", "MTPR", "MFPR", "MOVPSL", "PUSHL", "MOVAL", "PUSHAL", +"BBS", "BBC", "BBSS", "BBCS", "BBSC", "BBCC", "BBSSI", "BBCCI", +"BLBS", "BLBC", "FFS", "FFC", "CMPV", "CMPZV", "EXTV", "EXTZV", +"INSV", "ACBL", "AOBLSS", "AOBLEQ", "SOBGEQ", "SOBGTR", "CVTLB", "CVTLW", +"ASHP", "CVTLP", "CALLG", "CALLS", "XFC", NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 100 - 11F */ +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 120 - 13F */ +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, "CVTDH", "CVTGF", NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +"ADDG2", "ADDG3", "SUBG2", "SUBG3", "MULG2", "MULG3", "DIVG2", "DIVG3", +"CVTGB", "CVTGW", "CVTGL", "CVTRGL", "CVTBG", "CVTWG", "CVTLG", "ACBG", +"MOVG", "CMPG", "MNEGG", "TSTG", "EMODG", "POLYG", "CVTGH", NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +"ADDH2", "ADDH3", "SUBH2", "SUBH3", "MULH2", "MULH3", "DIVH2", "DIVH3", +"CVTHB", "CVTHW", "CVTHL", "CVTRHL", "CVTBH", "CVTWH", "CVTLH", "ACBH", +"MOVH", "CMPH", "MNEGH", "TSTH", "EMODH", "POLYH", "CVTHG", NULL, +NULL, NULL, NULL, NULL, "CLRO", "MOVO", "MOVAO", "PUSHAO", +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 180 - 19F */ +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +"CVTFH", "CVTFG", NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 1A0 - 1BF */ +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 1C0 - 1DF */ +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 1E0 - 1FF */ +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, "CVTHF", "CVTHD", +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +const char *altcod[] = { +"CLRF", "CLRD", "CLRG", "CLRH", "MOVAF", "MOVAD", "MOVAG", "MOVAH", +"PUSHAF", "PUSHAD", "PUSHAG", "PUSHAH", "BNEQU", "BEQLU", "BCC", "BCS", +NULL +}; + +const int32 altop[] = { + 0xD4, 0x7C, 0x7C, 0x17C, 0xDE, 0x7E, 0x7E, 0x17E, + 0xDF, 0x7F, 0x7F, 0x17F, 0x12, 0x13, 0x1E, 0x1F + }; + +const char* regname[] = { + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", + "R8", "R9", "R10", "R11", "AP", "FP", "SP", "PC" + }; + +#define GETNUM(d,n) for (k = d = 0; k < n; k++) \ + d = d | (((int32) val[vp++]) << (k * 8)) + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = values to decode + *uptr = pointer to unit + sw = switches + Outputs: + return = if >= 0, error code + if < 0, number of extra bytes retired +*/ + +t_stat fprint_sym (SMP_FILE *of, t_addr exta, t_value *val, UNIT *uptr, int32 sw) +{ +RUN_SCOPE; +uint32 addr = (uint32) exta; +int32 c, k, num, vp, lnt, rdx; +t_stat r; +DEVICE *dptr; + +if (uptr == NULL) /* anon = CPU */ + uptr = &cpu_unit_0; +if ((sw & SIM_SW_STOP) && (PSL & PSL_CM)) /* stop in CM? */ + sw = sw | SWMASK ('P'); /* force CM print */ +dptr = find_dev_from_unit (uptr); /* find dev */ +if (dptr == NULL) + return SCPE_IERR; +if (dptr->dwidth != 8) /* byte dev only */ + return SCPE_ARG; +if (sw & SWMASK ('B')) /* get length */ + lnt = 1; +else if (sw & SWMASK ('W')) + lnt = 2; +else if (sw & SWMASK ('L')) + lnt = 4; +else lnt = uptr->is_cpu() ? 4: 1; +if (sw & SWMASK ('D')) /* get radix */ + rdx = 10; +else if (sw & SWMASK ('O')) + rdx = 8; +else if (sw & SWMASK ('H')) + rdx = 16; +else rdx = dptr->dradix; +if ((sw & SWMASK ('A')) || (sw & SWMASK ('C'))) { /* char format? */ + for (vp = lnt - 1; vp >= 0; vp--) { + c = (int32) val[vp] & 0x7F; + fprintf (of, (c < 0x20)? "<%02X>": "%c", c); + } + return -(lnt - 1); /* return # chars */ + } + +if ((sw & (SWMASK ('P') | SWMASK ('R'))) && /* cmode or rad50? */ + uptr->is_cpu()) { + r = fprint_sym_cm (of, addr, val, sw); /* decode inst */ + if (r <= 0) + return r; + } + +if ((sw & SWMASK ('M')) && uptr->is_cpu()) { /* inst format? */ + r = fprint_sym_m (of, addr, val); /* decode inst */ + if (r <= 0) + return r; + } + +vp = 0; /* init ptr */ +GETNUM (num, lnt); /* get number */ +fprint_val (of, (uint32) num, rdx, lnt * 8, PV_RZRO); +return -(vp - 1); +} + +/* Symbolic decode for -m + + Inputs: + of = output stream + addr = current PC + *val = values to decode + Outputs: + return = if >= 0, error code + if < 0, number of extra bytes retired +*/ + +t_stat fprint_sym_m (SMP_FILE *of, uint32 addr, t_value *val) +{ +int32 i, k, vp, inst, numspec; +int32 num, spec, rn, disp, index; + +vp = 0; /* init ptr */ +inst = (int32) val[vp++]; /* get opcode */ +if (inst == 0xFD) /* 2 byte op? */ + inst = 0x100 | (int32) val[vp++]; +if (opcode[inst] == NULL) /* defined? */ + return SCPE_ARG; +numspec = DR_GETNSP (drom[inst][0]); /* get # spec */ +if (numspec == 0) + numspec = DR_GETUSP (drom[inst][0]); +fprintf (of, "%s", opcode[inst]); /* print name */ +for (i = 0; i < numspec; i++) { /* loop thru spec */ + fputc (i? ',': ' ', of); /* separator */ + disp = drom[inst][i + 1]; /* get drom value */ + if (disp == BB) { /* byte br disp? */ + GETNUM (num, 1); + fprintf (of, "%-X", SXTB (num) + addr + vp); + } + else if (disp == BW) { /* word br disp? */ + GETNUM (num, 2); + fprintf (of, "%-X", SXTW (num) + addr + vp); + } + else { + spec = (int32) val[vp++]; /* get specifier */ + if ((spec & 0xF0) == IDX) { /* index? */ + index = spec; /* copy, get next */ + spec = (int32) val[vp++]; + } + else index = 0; + rn = spec & 0xF; /* get reg # */ + switch (spec & 0xF0) { /* case on mode */ + + case SH0: case SH1: case SH2: case SH3: /* s^# */ + fprintf (of, "#%-X", spec); + break; + + case GRN: /* Rn */ + fprintf (of, "%-s", regname[rn]); + break; + + case RGD: /* (Rn) */ + fprintf (of, "(%-s)", regname[rn]); + break; + + case ADC: /* -(Rn) */ + fprintf (of, "-(%-s)", regname[rn]); + break; + + case AIN: /* (Rn)+, #n */ + if (rn != nPC) + fprintf (of, "(%-s)+", regname[rn]); + else { + if (DR_LNT (disp) == L_OCTA) + vp = fprint_sym_qoimm (of, val, vp, 4); + else if (DR_LNT (disp) == L_QUAD) + vp = fprint_sym_qoimm (of, val, vp, 2); + else { + GETNUM (num, DR_LNT (disp)); + fprintf (of, "#%-X", num); + } + } + break; + + case AID: /* @(Rn)+, @#n */ + if (rn != nPC) + fprintf (of, "@(%-s)+", regname[rn]); + else { + GETNUM (num, 4); + fprintf (of, "@#%-X", num); + } + break; + + case BDD: /* @b^d(r),@b^n */ + fputc ('@', of); + case BDP: /* b^d(r), b^n */ + GETNUM (num, 1); + if (rn == nPC) + fprintf (of, "%-X", addr + vp + SXTB (num)); + else if (num & BSIGN) fprintf (of, "-%-X(%-s)", + -num & BMASK, regname[rn]); + else fprintf (of, "%-X(%-s)", num, regname[rn]); + break; + + case WDD: /* @w^d(r),@w^n */ + fputc ('@', of); + case WDP: /* w^d(r), w^n */ + GETNUM (num, 2); + if (rn == nPC) + fprintf (of, "%-X", addr + vp + SXTW (num)); + else if (num & WSIGN) fprintf (of, "-%-X(%-s)", + -num & WMASK, regname[rn]); + else fprintf (of, "%-X(%-s)", num, regname[rn]); + break; + + case LDD: /* @l^d(r),@l^n */ + fputc ('@', of); + case LDP: /* l^d(r),l^n */ + GETNUM (num, 4); + if (rn == nPC) + fprintf (of, "%-X", addr + vp + num); + else if (num & LSIGN) fprintf (of, "-%-X(%-s)", + -num, regname[rn]); + else fprintf (of, "%-X(%-s)", num, regname[rn]); + break; + } /* end case */ + if (index) + fprintf (of, "[%-s]", regname[index & 0xF]); + } /* end else */ + } /* end for */ +return -(vp - 1); +} + +/* Symbolic decode, quad/octa immediates + + Inputs: + *of = output stream + *val = pointer to input values + vp = current index into val + lnt = number of longwords in immediate + Outputs: + vp = updated index into val +*/ + +int32 fprint_sym_qoimm (SMP_FILE *of, t_value *val, int32 vp, int32 lnt) +{ +int32 i, k, startp, num[4]; + +for (i = 0; i < lnt; i++) { + GETNUM (num[lnt - 1 - i], 4); + } +for (i = startp = 0; i < lnt; i++) { + if (startp) + fprintf (of, "%08X", num[i]); + else if (num[i] || (i == (lnt - 1))) { + fprintf (of, "#%-X", num[i]); + startp = 1; + } + } +return vp; +} + +#define PUTNUM(d,n) for (k = 0; k < n; k++) val[vp++] = (d >> (k * 8)) & 0xFF + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym (char *cptr, t_addr exta, UNIT *uptr, t_value *val, int32 sw) +{ +uint32 addr = (uint32) exta; +int32 k, rdx, lnt, num, vp; +t_stat r; +DEVICE *dptr; +const static uint32 maxv[5] = { 0, 0xFF, 0xFFFF, 0, 0xFFFFFFFF }; + +if (uptr == NULL) /* anon = CPU */ + uptr = &cpu_unit_0; +dptr = find_dev_from_unit (uptr); /* find dev */ +if (dptr == NULL) + return SCPE_IERR; +if (dptr->dwidth != 8) /* byte dev only */ + return SCPE_ARG; +if (sw & SWMASK ('B')) /* get length */ + lnt = 1; +else if (sw & SWMASK ('W')) + lnt = 2; +else if (sw & SWMASK ('L')) + lnt = 4; +else lnt = uptr->is_cpu() ? 4: 1; +if (sw & SWMASK ('D')) /* get radix */ + rdx = 10; +else if (sw & SWMASK ('O')) + rdx = 8; +else if (sw & SWMASK ('H')) + rdx = 16; +else rdx = dptr->dradix; + +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) /* ASCII char? */ + return parse_char (cptr, val, lnt); +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) /* ASCII string? */ + return parse_char (cptr, val, sim_emax); + +if ((sw & (SWMASK ('P') | SWMASK ('R'))) && /* cmode or rad50? */ + uptr->is_cpu()) { + r = parse_sym_cm (cptr, addr, val, sw); /* try to parse */ + if (r <= 0) + return r; + } + +if (uptr->is_cpu()) { /* cpu only */ + r = parse_sym_m (cptr, addr, val); /* try to parse inst */ + if (r <= 0) + return r; + } + +num = (int32) get_uint (cptr, rdx, maxv[lnt], &r); /* get number */ +if (r != SCPE_OK) + return r; +vp = 0; +PUTNUM (num, lnt); /* store */ +return -(lnt - 1); +} + +/* Character input for -a or -c + + Inputs: + *cptr = pointer to input string + addr = current PC + *val = pointer to output values + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_char (char *cptr, t_value *val, int32 lnt) +{ +int32 vp; + +if (*cptr == 0) + return SCPE_ARG; +vp = 0; +while ((vp < lnt) && *cptr) { /* get chars */ + val[vp++] = *cptr++; + } +return -(vp - 1); /* return # chars */ +} + +/* Symbolic input for -m + + Inputs: + *cptr = pointer to input string + addr = current PC + *val = pointer to output values + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym_m (char *cptr, uint32 addr, t_value *val) +{ +int32 i, numspec, disp, opc, vp; +t_stat r; +char gbuf[CBUFSIZE]; + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0, opc = -1; (i < NUM_INST) && (opc < 0); i++) { + if (opcode[i] && strcmp (gbuf, opcode[i]) == 0) + opc = i; + } +if (opc < 0) { /* check alternates */ + for (i = 0; altcod[i] && (opc < 0); i++) { + if (strcmp (gbuf, altcod[i]) == 0) + opc = altop[i]; + } + } +if (opc < 0) + return SCPE_ARG; /* undefined? */ +vp = 0; +if (opc >= 0x100) /* 2 byte? */ + val[vp++] = 0xFD; +val[vp++] = opc & 0xFF; /* store opcode */ +numspec = DR_GETNSP (drom[opc][0]); /* get # specifiers */ +if (numspec == 0) + numspec = DR_GETUSP (drom[opc][0]); +for (i = 1; i <= numspec; i++) { /* loop thru specs */ + if (i == numspec) + cptr = get_glyph (cptr, gbuf, 0); + else cptr = get_glyph (cptr, gbuf, ','); /* get specifier */ + disp = drom[opc][i]; /* get drom value */ + if (disp == BB) + vp = parse_brdisp (gbuf, addr, val, vp, 0, &r); + else if (disp == BW) + vp = parse_brdisp (gbuf, addr, val, vp, 1, &r); + else vp = parse_spec (gbuf, addr, val, vp, disp, &r); + if (r != SCPE_OK) + return r; + } +if (*cptr != 0) + return SCPE_ARG; +return -(vp - 1); +} + +/* Parse a branch displacement + + Inputs: + cptr = pointer to input buffer + addr = current address + val = pointer to output array + vp = current pointer in output array + lnt = length (0 = byte, 1 = word) + r = pointer to status + Outputs: + vp = updated output pointer +*/ + +int32 parse_brdisp (char *cptr, uint32 addr, t_value *val, int32 vp, + int32 lnt, t_stat *r) +{ +int32 k, dest, num; + +dest = (int32) get_uint (cptr, 16, 0xFFFFFFFF, r); /* get value */ +num = dest - (addr + vp + lnt + 1); /* compute offset */ +if ((num > (lnt? 32767: 127)) || (num < (lnt? -32768: -128))) + *r = SCPE_ARG; +else { + PUTNUM (num, lnt + 1); /* store offset */ + *r = SCPE_OK; + } +return vp; +} + +/* Parse a specifier + + Inputs: + cptr = pointer to input buffer + addr = current address + val = pointer to output array + vp = current pointer in output array + disp = specifier dispatch + r = pointer to status + Outputs: + vp = updated output pointer +*/ + +#define SP_IND 0x200 /* indirect */ +#define SP_V_FORCE 6 +#define SP_FS 0x040 /* S^ */ +#define SP_FI 0x080 /* I^ */ +#define SP_FB 0x0C0 /* B^ */ +#define SP_FW 0x100 /* W^ */ +#define SP_FL 0x140 /* L^ */ +#define SP_LIT 0x020 /* # */ +#define SP_PLUS 0x010 /* plus */ +#define SP_MINUS 0x008 /* minus */ +#define SP_NUM 0x004 /* number */ +#define SP_IDX 0x002 /* (Rn) */ +#define SP_POSTP 0x001 /* trailing + */ +#define M1C(c,v) if (*cptr == c) {\ + cptr++; \ + fl = fl | v; \ + } +#define SPUTNUM(v,d) if (fl & SP_MINUS) \ + v = -v; \ + PUTNUM (v, d) +#define PARSE_LOSE { \ + *r = SCPE_ARG; \ + return vp; \ + } +#define SEL_LIM(p,m,u) ((fl & SP_PLUS)? (p): ((fl & SP_MINUS)? (m): (u))) + +int32 parse_spec (char *cptr, uint32 addr, t_value *val, int32 vp, int32 disp, t_stat *r) +{ +int32 i, k, litsize, index, rn = 0; /* init rn to suppress false GCC warning */ +int32 num, dispsize, mode; +int32 lit[4] = { 0 }; +int32 fl = 0; +char c, *tptr; +const char *force[] = { "S^", "I^", "B^", "W^", "L^", NULL }; + +*r = SCPE_OK; /* assume ok */ +M1C ('@', SP_IND); /* look for @ */ +if (tptr = parse_rnum (cptr, &rn)) { /* look for Rn */ + if (*cptr == '[') { /* look for [Rx] */ + cptr = parse_rnum (++cptr, &index); + if ((cptr == NULL) || (*cptr++ != ']')) + PARSE_LOSE; + val[vp++] = index | IDX; + } + else val[vp++] = rn | GRN | (fl? 1: 0); /* Rn or @Rn */ + if (*tptr != 0) /* must be done */ + *r = SCPE_ARG; + return vp; + } +for (i = 0; force[i]; i++) { /* look for x^ */ + if (strncmp (cptr, force[i], 2) == 0) { + cptr = cptr + 2; + fl = fl | ((i + 1) << SP_V_FORCE); + break; + } + } +M1C ('#', SP_LIT); /* look for # */ +M1C ('+', SP_PLUS); /* look for + */ +M1C ('-', SP_MINUS); /* look for - */ +for (litsize = 0;; cptr++) { /* look for mprec int */ + c = *cptr; + if ((c < '0') || (c > 'F') || ((c > '9') && (c < 'A'))) + break; + num = (c <= '9')? c - '0': c - 'A' + 10; + fl = fl | SP_NUM; + for (i = 3; i >= 0; i--) { + lit[i] = lit[i] << 4; + if (i > 0) + lit[i] = lit[i] | ((lit[i - 1] >> 28) & 0xF); + else lit[i] = lit[i] | num; + if (lit[i] && (i > litsize)) + litsize = i; + } + } +if (*cptr == '(') { /* look for (Rn) */ + cptr = parse_rnum (++cptr, &rn); + if ((cptr == NULL) || (*cptr++ != ')')) + PARSE_LOSE; + fl = fl | SP_IDX; + } +M1C ('+', SP_POSTP); /* look for + */ +if (*cptr == '[') { /* look for [Rx] */ + cptr = parse_rnum (++cptr, &index); + if ((cptr == NULL) || (*cptr++ != ']')) + PARSE_LOSE; + val[vp++] = index | IDX; + } +switch (fl) { /* case on state */ + + case SP_FS|SP_LIT|SP_NUM: /* S^#n */ + case SP_FS|SP_LIT|SP_PLUS|SP_NUM: /* S^#+n */ + if ((litsize > 0) || (lit[0] & ~0x3F)) + PARSE_LOSE; + val[vp++] = lit[0]; + break; + + case SP_IDX: /* (Rn) */ + val[vp++] = rn | RGD; + break; + + case SP_MINUS|SP_IDX: /* -(Rn) */ + val[vp++] = rn | ADC; + break; + + case SP_IDX|SP_POSTP: /* (Rn)+ */ + val[vp++] = rn | AIN; + break; + + case SP_LIT|SP_NUM: /* #n */ + case SP_LIT|SP_PLUS|SP_NUM: /* #+n */ + if ((litsize == 0) && ((lit[0] & ~0x3F) == 0)) { + val[vp++] = lit[0]; + break; + } /* fall thru */ + case SP_LIT|SP_MINUS|SP_NUM: /* #-n */ + case SP_FI|SP_LIT|SP_NUM: /* I^#n */ + case SP_FI|SP_LIT|SP_PLUS|SP_NUM: /* I^#+n */ + case SP_FI|SP_LIT|SP_MINUS|SP_NUM: /* I^#-n */ + val[vp++] = nPC | AIN; + disp = disp & DR_LNMASK; + switch (disp) { /* case spec lnt */ + case 00: /* check fit */ + if ((litsize > 0) || (lit[0] < 0) || + (lit[0] > SEL_LIM (0x7F, 0x80, 0xFF))) + PARSE_LOSE; + SPUTNUM (lit[0], 1); /* store */ + break; + case 01: /* check fit */ + if ((litsize > 0) || (lit[0] < 0) || + (lit[0] > SEL_LIM (0x7FFF, 0x8000, 0xFFFF))) + PARSE_LOSE; + SPUTNUM (lit[0], 2); + break; + case 02: /* check 1 lw */ + if (litsize > 0) + PARSE_LOSE; + SPUTNUM (lit[0], 4); + break; + case 03: /* check 2 lw */ + if (litsize > 1) + PARSE_LOSE; + vp = parse_sym_qoimm (lit, val, vp, 2, fl & SP_MINUS); + break; + case 04: + vp = parse_sym_qoimm (lit, val, vp, 4, fl & SP_MINUS); + break; + } /* end case lnt */ + break; + + case SP_IND|SP_IDX|SP_POSTP: /* @(Rn)+ */ + val[vp++] = rn | AID; + break; + + case SP_IND|SP_LIT|SP_NUM: /* @#n */ + if (litsize > 0) + PARSE_LOSE; + val[vp++] = nPC | AID; + PUTNUM (lit[0], 4); + break; + + case SP_NUM|SP_IDX: /* d(rn) */ + case SP_PLUS|SP_NUM|SP_IDX: /* +d(rn) */ + case SP_MINUS|SP_NUM|SP_IDX: /* -d(rn) */ + case SP_IND|SP_NUM|SP_IDX: /* @d(rn) */ + case SP_IND|SP_PLUS|SP_NUM|SP_IDX: /* @+d(rn) */ + case SP_IND|SP_MINUS|SP_NUM|SP_IDX: /* @-d(rn) */ + if (litsize > 0) + PARSE_LOSE; + dispsize = 4; /* find fit for */ + mode = LDP; /* displacement */ + if (lit[0] >= 0) { + if (lit[0] <= SEL_LIM (0x7F, 0x80, 0xFF)) { + dispsize = 1; + mode = BDP; + } + else if (lit[0] <= SEL_LIM (0x7FFF, 0x8000, 0xFFFF)) { + dispsize = 2; + mode = WDP; + } + } + val[vp++] = mode | rn | ((fl & SP_IND)? 0x10: 0); + SPUTNUM (lit[0], dispsize); + break; + + case SP_FB|SP_NUM|SP_IDX: /* B^d(rn) */ + case SP_FB|SP_PLUS|SP_NUM|SP_IDX: /* B^+d(rn) */ + case SP_FB|SP_MINUS|SP_NUM|SP_IDX: /* B^-d(rn) */ + case SP_IND|SP_FB|SP_NUM|SP_IDX: /* @B^d(rn) */ + case SP_IND|SP_FB|SP_PLUS|SP_NUM|SP_IDX: /* @B^+d(rn) */ + case SP_IND|SP_FB|SP_MINUS|SP_NUM|SP_IDX: /* @B^-d(rn) */ + if ((litsize > 0) || (lit[0] < 0) || + (lit[0] > SEL_LIM (0x7F, 0x80, 0xFF))) + PARSE_LOSE; + val[vp++] = rn | BDP | ((fl & SP_IND)? 0x10: 0); + SPUTNUM (lit[0], 1); + break; + + case SP_FW|SP_NUM|SP_IDX: /* W^d(rn) */ + case SP_FW|SP_PLUS|SP_NUM|SP_IDX: /* W^+d(rn) */ + case SP_FW|SP_MINUS|SP_NUM|SP_IDX: /* W^-d(rn) */ + case SP_IND|SP_FW|SP_NUM|SP_IDX: /* @W^d(rn) */ + case SP_IND|SP_FW|SP_PLUS|SP_NUM|SP_IDX: /* @W^+d(rn) */ + case SP_IND|SP_FW|SP_MINUS|SP_NUM|SP_IDX: /* @W^-d(rn) */ + if ((litsize > 0) || (lit[0] < 0) || + (lit[0] > SEL_LIM (0x7FFF, 0x8000, 0xFFFF))) + PARSE_LOSE; + val[vp++] = rn | WDP | ((fl & SP_IND)? 0x10: 0); + SPUTNUM (lit[0], 2); + break; + + case SP_FL|SP_NUM|SP_IDX: /* L^d(rn) */ + case SP_FL|SP_PLUS|SP_NUM|SP_IDX: /* L^+d(rn) */ + case SP_FL|SP_MINUS|SP_NUM|SP_IDX: /* L^-d(rn) */ + case SP_IND|SP_FL|SP_NUM|SP_IDX: /* @L^d(rn) */ + case SP_IND|SP_FL|SP_PLUS|SP_NUM|SP_IDX: /* @L^+d(rn) */ + case SP_IND|SP_FL|SP_MINUS|SP_NUM|SP_IDX: /* @L^-d(rn) */ + if ((litsize > 0) || (lit[0] < 0)) + PARSE_LOSE; + val[vp++] = rn | LDP | ((fl & SP_IND)? 0x10: 0); + SPUTNUM (lit[0], 4); + break; + + case SP_NUM: /* n */ + case SP_IND|SP_NUM: /* @n */ + if (litsize > 0) + PARSE_LOSE; + num = lit[0] - (addr + vp + 2); /* fit in byte? */ + if ((num >= -128) && (num <= 127)) { + mode = BDP; + dispsize = 1; + } + else { + num = lit[0] - (addr + vp + 3); /* fit in word? */ + if ((num >= -32768) && (num <= 32767)) { + mode = WDP; + dispsize = 2; + } + else { + num = lit[0] - (addr + vp + 5); /* no, use lw */ + mode = LDP; + dispsize = 4; + } + } + val[vp++] = mode | nPC | ((fl & SP_IND)? 1: 0); + PUTNUM (num, dispsize); + break; + + case SP_FB|SP_NUM: /* B^n */ + case SP_IND|SP_FB|SP_NUM: /* @B^n */ + num = lit[0] - (addr + vp + 2); + if ((litsize > 0) || (num > 127) || (num < -128)) + PARSE_LOSE; + val[vp++] = nPC | BDP | ((fl & SP_IND)? 1: 0); + PUTNUM (num, 1); + break; + + case SP_FW|SP_NUM: /* W^n */ + case SP_IND|SP_FW|SP_NUM: /* @W^n */ + num = lit[0] - (addr + vp + 3); + if ((litsize > 0) || (num > 32767) || (num < -32768)) + PARSE_LOSE; + val[vp++] = nPC | WDP | ((fl & SP_IND)? 1: 0); + PUTNUM (num, 2); + break; + + case SP_FL|SP_NUM: /* L^n */ + case SP_IND|SP_FL|SP_NUM: /* @L^n */ + num = lit[0] - (addr + vp + 5); + if (litsize > 0) + PARSE_LOSE; + val[vp++] = nPC | LDP | ((fl & SP_IND)? 1: 0); + PUTNUM (num, 4); + break; + + default: + PARSE_LOSE; + } /* end case */ + +if (*cptr != 0) /* must be done */ + *r = SCPE_ARG; +return vp; +} + +char *parse_rnum (char *cptr, int32 *rn) +{ +int32 i, lnt; +t_value regnum; +char *tptr; + +for (i = 15; i >= 0; i--) { /* chk named reg */ + lnt = (int) strlen (regname[i]); + if (strncmp (cptr, regname[i], lnt) == 0) { + *rn = i; + return cptr + lnt; + } + } +if (*cptr++ != 'R') /* look for R */ + return NULL; +regnum = strtotv (cptr, &tptr, 10); /* look for reg # */ +if ((cptr == tptr) || (regnum > 15)) + return NULL; +*rn = (int32) regnum; +return tptr; +} + +int32 parse_sym_qoimm (int32 *lit, t_value *val, int32 vp, int lnt, int32 minus) +{ +int32 i, k, prev; + +for (i = prev = 0; i < lnt; i++) { + if (minus) + prev = lit[i] = ~lit[i] + (prev == 0); + PUTNUM (lit[i], 4); + } +return vp; +} + diff --git a/src/VAX/vax_syscm.cpp b/src/VAX/vax_syscm.cpp new file mode 100644 index 0000000..6e05b87 --- /dev/null +++ b/src/VAX/vax_syscm.cpp @@ -0,0 +1,689 @@ +/* vax_syscm.c: PDP-11 compatibility mode symbolic decode and parse + + 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"), + 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. + + 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 +*/ + +#include "sim_defs.h" +#include "vax_defs.h" +#include + +/* Symbol tables */ +/* Warning: for literals, the class number MUST equal the field width!! */ + +#define I_V_CL 18 /* class bits */ +#define I_M_CL 017 /* class mask */ +#define I_V_NPN 0 /* no operands */ +#define I_V_REG 1 /* reg */ +#define I_V_SOP 2 /* operand */ +#define I_V_3B 3 /* 3b literal */ +#define I_V_RSOP 4 /* reg, operand */ +#define I_V_BR 5 /* cond branch */ +#define I_V_6B 6 /* 6b literal */ +#define I_V_SOB 7 /* reg, disp */ +#define I_V_8B 8 /* 8b literal */ +#define I_V_DOP 9 /* double operand */ +#define I_V_CCC 10 /* CC clear */ +#define I_V_CCS 11 /* CC set */ +#define I_V_SOPR 12 /* operand, reg */ +#define I_NPN (I_V_NPN << I_V_CL) +#define I_REG (I_V_REG << I_V_CL) +#define I_SOP (I_V_SOP << I_V_CL) +#define I_3B (I_V_3B << I_V_CL) +#define I_6B (I_V_6B << I_V_CL) +#define I_BR (I_V_BR << I_V_CL) +#define I_8B (I_V_8B << I_V_CL) +#define I_RSOP (I_V_RSOP << I_V_CL) +#define I_SOB (I_V_SOB << I_V_CL) +#define I_DOP (I_V_DOP << I_V_CL) +#define I_CCC (I_V_CCC << I_V_CL) +#define I_CCS (I_V_CCS << I_V_CL) +#define I_SOPR (I_V_SOPR << I_V_CL) + +static const int32 masks[] = { + 0177777, 0177770, 0177700, 0177770, + 0177000, 0177400, 0177700, 0177000, + 0177400, 0170000, 0177777, 0177777, + 0177000 + }; + +static const char *opcode[] = { +"HALT","WAIT","RTI","BPT", +"IOT","RESET","RTT","MFPT", +"JMP","RTS","SPL", +"NOP","CLC","CLV","CLV CLC", +"CLZ","CLZ CLC","CLZ CLV","CLZ CLV CLC", +"CLN","CLN CLC","CLN CLV","CLN CLV CLC", +"CLN CLZ","CLN CLZ CLC","CLN CLZ CLC","CCC", +"NOP","SEC","SEV","SEV SEC", +"SEZ","SEZ SEC","SEZ SEV","SEZ SEV SEC", +"SEN","SEN SEC","SEN SEV","SEN SEV SEC", +"SEN SEZ","SEN SEZ SEC","SEN SEZ SEC","SCC", +"SWAB","BR","BNE","BEQ", +"BGE","BLT","BGT","BLE", +"JSR", +"CLR","COM","INC","DEC", +"NEG","ADC","SBC","TST", +"ROR","ROL","ASR","ASL", +"MARK","MFPI","MTPI","SXT", +"CSM", "TSTSET","WRTLCK", +"MOV","CMP","BIT","BIC", +"BIS","ADD", +"MUL","DIV","ASH","ASHC", +"XOR", +"FADD","FSUB","FMUL","FDIV", +"L2DR", +"MOVC","MOVRC","MOVTC", +"LOCC","SKPC","SCANC","SPANC", +"CMPC","MATC", +"ADDN","SUBN","CMPN","CVTNL", +"CVTPN","CVTNP","ASHN","CVTLN", +"L3DR", +"ADDP","SUBP","CMPP","CVTPL", +"MULP","DIVP","ASHP","CVTLP", +"MOVCI","MOVRCI","MOVTCI", +"LOCCI","SKPCI","SCANCI","SPANCI", +"CMPCI","MATCI", +"ADDNI","SUBNI","CMPNI","CVTNLI", +"CVTPNI","CVTNPI","ASHNI","CVTLNI", +"ADDPI","SUBPI","CMPPI","CVTPLI", +"MULPI","DIVPI","ASHPI","CVTLPI", +"SOB", +"BPL","BMI","BHI","BLOS", +"BVC","BVS","BCC","BCS", +"BHIS","BLO", /* encode only */ +"EMT","TRAP", +"CLRB","COMB","INCB","DECB", +"NEGB","ADCB","SBCB","TSTB", +"RORB","ROLB","ASRB","ASLB", +"MTPS","MFPD","MTPD","MFPS", +"MOVB","CMPB","BITB","BICB", +"BISB","SUB", +NULL +}; + +static const int32 opc_val[] = { +0000000+I_NPN, 0000001+I_NPN, 0000002+I_NPN, 0000003+I_NPN, +0000004+I_NPN, 0000005+I_NPN, 0000006+I_NPN, 0000007+I_NPN, +0000100+I_SOP, 0000200+I_REG, 0000230+I_3B, +0000240+I_CCC, 0000241+I_CCC, 0000242+I_CCC, 0000243+I_NPN, +0000244+I_CCC, 0000245+I_NPN, 0000246+I_NPN, 0000247+I_NPN, +0000250+I_CCC, 0000251+I_NPN, 0000252+I_NPN, 0000253+I_NPN, +0000254+I_NPN, 0000255+I_NPN, 0000256+I_NPN, 0000257+I_CCC, +0000260+I_CCS, 0000261+I_CCS, 0000262+I_CCS, 0000263+I_NPN, +0000264+I_CCS, 0000265+I_NPN, 0000266+I_NPN, 0000267+I_NPN, +0000270+I_CCS, 0000271+I_NPN, 0000272+I_NPN, 0000273+I_NPN, +0000274+I_NPN, 0000275+I_NPN, 0000276+I_NPN, 0000277+I_CCS, +0000300+I_SOP, 0000400+I_BR, 0001000+I_BR, 0001400+I_BR, +0002000+I_BR, 0002400+I_BR, 0003000+I_BR, 0003400+I_BR, +0004000+I_RSOP, +0005000+I_SOP, 0005100+I_SOP, 0005200+I_SOP, 0005300+I_SOP, +0005400+I_SOP, 0005500+I_SOP, 0005600+I_SOP, 0005700+I_SOP, +0006000+I_SOP, 0006100+I_SOP, 0006200+I_SOP, 0006300+I_SOP, +0006400+I_6B, 0006500+I_SOP, 0006600+I_SOP, 0006700+I_SOP, +0007000+I_SOP, 0007200+I_SOP, 0007300+I_SOP, +0010000+I_DOP, 0020000+I_DOP, 0030000+I_DOP, 0040000+I_DOP, +0050000+I_DOP, 0060000+I_DOP, +0070000+I_SOPR, 0071000+I_SOPR, 0072000+I_SOPR, 0073000+I_SOPR, +0074000+I_RSOP, +0075000+I_REG, 0075010+I_REG, 0075020+I_REG, 0075030+I_REG, +0076020+I_REG, +0076030+I_NPN, 0076031+I_NPN, 0076032+I_NPN, +0076040+I_NPN, 0076041+I_NPN, 0076042+I_NPN, 0076043+I_NPN, +0076044+I_NPN, 0076045+I_NPN, +0076050+I_NPN, 0076051+I_NPN, 0076052+I_NPN, 0076053+I_NPN, +0076054+I_NPN, 0076055+I_NPN, 0076056+I_NPN, 0076057+I_NPN, +0076060+I_REG, +0076070+I_NPN, 0076071+I_NPN, 0076072+I_NPN, 0076073+I_NPN, +0076074+I_NPN, 0076075+I_NPN, 0076076+I_NPN, 0076077+I_NPN, +0076130+I_NPN, 0076131+I_NPN, 0076132+I_NPN, +0076140+I_NPN, 0076141+I_NPN, 0076142+I_NPN, 0076143+I_NPN, +0076144+I_NPN, 0076145+I_NPN, +0076150+I_NPN, 0076151+I_NPN, 0076152+I_NPN, 0076153+I_NPN, +0076154+I_NPN, 0076155+I_NPN, 0076156+I_NPN, 0076157+I_NPN, +0076170+I_NPN, 0076171+I_NPN, 0076172+I_NPN, 0076173+I_NPN, +0076174+I_NPN, 0076175+I_NPN, 0076176+I_NPN, 0076177+I_NPN, +0077000+I_SOB, +0100000+I_BR, 0100400+I_BR, 0101000+I_BR, 0101400+I_BR, +0102000+I_BR, 0102400+I_BR, 0103000+I_BR, 0103400+I_BR, +0103000+I_BR, 0103400+I_BR, +0104000+I_8B, 0104400+I_8B, +0105000+I_SOP, 0105100+I_SOP, 0105200+I_SOP, 0105300+I_SOP, +0105400+I_SOP, 0105500+I_SOP, 0105600+I_SOP, 0105700+I_SOP, +0106000+I_SOP, 0106100+I_SOP, 0106200+I_SOP, 0106300+I_SOP, +0106400+I_SOP, 0106500+I_SOP, 0106600+I_SOP, 0106700+I_SOP, +0110000+I_DOP, 0120000+I_DOP, 0130000+I_DOP, 0140000+I_DOP, +0150000+I_DOP, 0160000+I_DOP, +-1 +}; + +static const char *rname [] = { + "R0", "R1", "R2", "R3", "R4", "R5", "SP", "PC" + }; + +static const char r50_to_asc[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789"; + +/* Specifier decode + + Inputs: + *of = output stream + addr = current PC + spec = specifier + nval = next word + flag = TRUE if decoding for CPU + iflag = TRUE if decoding integer instruction + Outputs: + count = -number of extra words retired +*/ + +int32 fprint_spec (SMP_FILE *of, t_addr addr, int32 spec, int32 nval) +{ +int32 reg, mode; +static const int32 rgwd[8] = { 0, 0, 0, 0, 0, 0, -1, -1 }; +static const int32 pcwd[8] = { 0, 0, -1, -1, 0, 0, -1, -1 }; + +reg = spec & 07; +mode = ((spec >> 3) & 07); +switch (mode) { + + case 0: + fprintf (of, "%s", rname[reg]); + break; + + case 1: + fprintf (of, "(%s)", rname[reg]); + break; + + case 2: + if (reg != 7) + fprintf (of, "(%s)+", rname[reg]); + else fprintf (of, "#%-X", nval); + break; + + case 3: + if (reg != 7) + fprintf (of, "@(%s)+", rname[reg]); + else fprintf (of, "@#%-X", nval); + break; + + case 4: + fprintf (of, "-(%s)", rname[reg]); + break; + + case 5: + fprintf (of, "@-(%s)", rname[reg]); + break; + + case 6: + if (reg != 7) + fprintf (of, "%-X(%s)", nval, rname[reg]); + 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", (int32)((nval + addr + 4) & 0177777)); + break; + } /* end case */ + +return ((reg == 07)? pcwd[mode]: rgwd[mode]); +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = values to decode + *uptr = pointer to unit + sw = switches + Outputs: + return = if >= 0, error code + if < 0, number of extra words retired +*/ + +t_stat fprint_sym_cm (SMP_FILE *of, t_addr addr, t_value *bytes, int32 sw) +{ +int32 i, j, c1, c2, c3, inst, srcm, srcr, dstm, dstr; +int32 l8b, brdisp, wd1; +uint32 val[3]; + +for (i = j = 0; i < 3; i++, j = j + 2) + val[i] = (int32) (bytes[j] | (bytes[j + 1] << 8)); + +if (sw & SWMASK ('R')) { /* radix 50? */ + if (val[0] > 0174777) /* max value */ + return SCPE_ARG; + c3 = val[0] % 050; + c2 = (val[0] / 050) % 050; + c1 = val[0] / (050 * 050); + fprintf (of, "%c%c%c", r50_to_asc[c1], + r50_to_asc[c2], r50_to_asc[c3]); + return -1; + } +if (!(sw & SWMASK ('P')) || (addr & 1) || (addr > WMASK)) + return SCPE_ARG; + +inst = val[0]; /* inst */ +wd1 = 0; +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_CL) & I_M_CL; /* get class */ + if ((opc_val[i] & 0177777) == (inst & masks[j])) { /* match? */ + srcm = (inst >> 6) & 077; /* opr fields */ + srcr = srcm & 07; + dstm = inst & 077; + dstr = dstm & 07; + l8b = inst & 0377; + switch (j) { /* case on class */ + + case I_V_NPN: case I_V_CCC: case I_V_CCS: /* no operands */ + fprintf (of, "%s", opcode[i]); + break; + + case I_V_REG: /* reg */ + fprintf (of, "%s %-s", opcode[i], rname[dstr]); + break; + + case I_V_SOP: /* sop */ + fprintf (of, "%s ", opcode[i]); + wd1 = fprint_spec (of, addr, dstm, val[1]); + break; + + case I_V_3B: /* 3b */ + fprintf (of, "%s %-X", opcode[i], dstr); + break; + + case I_V_6B: /* 6b */ + fprintf (of, "%s %-X", opcode[i], dstm); + break; + + 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); + break; + + case I_V_8B: /* 8b */ + fprintf (of, "%s %-X", opcode[i], l8b); + break; + + case I_V_SOB: /* sob */ + fprintf (of, "%s %s,", opcode[i], rname[srcr]); + brdisp = (dstm * 2) - 2; + fprintf (of, "%-X", (addr - brdisp) & 0177777); + break; + + case I_V_RSOP: /* rsop */ + fprintf (of, "%s %s,", opcode[i], rname[srcr]); + wd1 = fprint_spec (of, addr, dstm, val[1]); + break; + + case I_V_SOPR: /* sopr */ + fprintf (of, "%s ", opcode[i]); + wd1 = fprint_spec (of, addr, dstm, val[1]); + fprintf (of, ",%s", rname[srcr]); + break; + + case I_V_DOP: /* dop */ + fprintf (of, "%s ", opcode[i]); + wd1 = fprint_spec (of, addr, srcm, val[1]); + fprintf (of, ","); + wd1 += fprint_spec (of, addr - wd1 - wd1, dstm, + val[1 - wd1]); + break; + } /* end case */ + + return ((wd1 * 2) - 1); + } /* end if */ + } /* end for */ +return SCPE_ARG; /* no match */ +} + +#define A_PND 100 /* # seen */ +#define A_MIN 040 /* -( seen */ +#define A_PAR 020 /* (Rn) seen */ +#define A_REG 010 /* Rn seen */ +#define A_PLS 004 /* + seen */ +#define A_NUM 002 /* number seen */ +#define A_REL 001 /* relative addr seen */ + +/* Register number + + Inputs: + *cptr = pointer to input string + mchar = character to match after register name + Outputs: + rnum = 0..7 if a legitimate register + < 0 if error +*/ + +int32 get_reg (char *cptr, char mchar) +{ +int32 i; + +if (*(cptr + 2) != mchar) + return -1; +for (i = 0; i < 8; i++) { + if (strncmp (cptr, rname[i], 2) == 0) + return i; + } +return -1; +} + +/* Number or memory address + + Inputs: + *cptr = pointer to input string + *dptr = pointer to output displacement + *pflag = pointer to accumulating flags + Outputs: + cptr = pointer to next character in input string + NULL if parsing error + + Flags: 0 (no result), A_NUM (number), A_REL (relative) +*/ + +char *get_addr (char *cptr, int32 *dptr, int32 *pflag) +{ +int32 val, minus; +char *tptr; + +minus = 0; + +if (*cptr == '.') { /* relative? */ + *pflag = *pflag | A_REL; + cptr++; + } +if (*cptr == '+') { /* +? */ + *pflag = *pflag | A_NUM; + cptr++; + } +if (*cptr == '-') { /* -? */ + *pflag = *pflag | A_NUM; + minus = 1; + cptr++; + } +errno = 0; +val = strtoul (cptr, &tptr, 16); +if (cptr == tptr) { /* no number? */ + if (*pflag == (A_REL + A_NUM)) /* .+, .-? */ + return NULL; + *dptr = 0; + return cptr; + } +if (errno || (*pflag == A_REL)) /* .n? */ + return NULL; +*dptr = (minus? -val: val) & 0177777; +*pflag = *pflag | A_NUM; +return tptr; +} + +/* Specifier decode + + Inputs: + *cptr = pointer to input string + addr = current PC + n1 = 0 if no extra word used + -1 if extra word used in prior decode + *sptr = pointer to output specifier + *dptr = pointer to output displacement + Outputs: + status = = -1 extra word decoded + = 0 ok + = +1 error +*/ + +t_stat get_spec (char *cptr, int32 addr, int32 n1, int32 *sptr, int32 *dptr) +{ +int32 reg, indir, pflag, disp; + +indir = 0; /* no indirect */ +pflag = 0; + +if (*cptr == '@') { /* indirect? */ + indir = 010; + cptr++; + } +if (*cptr == '#') { /* literal? */ + pflag = pflag | A_PND; + cptr++; + } +if (strncmp (cptr, "-(", 2) == 0) { /* autodecrement? */ + pflag = pflag | A_MIN; + cptr++; + } +else if ((cptr = get_addr (cptr, &disp, &pflag)) == NULL) + return 1; +if (*cptr == '(') { /* register index? */ + pflag = pflag | A_PAR; + if ((reg = get_reg (cptr + 1, ')')) < 0) + return 1; + cptr = cptr + 4; + if (*cptr == '+') { /* autoincrement? */ + pflag = pflag | A_PLS; + cptr++; + } + } +else if ((reg = get_reg (cptr, 0)) >= 0) { + pflag = pflag | A_REG; + cptr = cptr + 2; + } +if (*cptr != 0) /* all done? */ + return 1; + +switch (pflag) { /* case on syntax */ + + case A_REG: /* Rn, @Rn */ + *sptr = indir + reg; + return 0; + + case A_PAR: /* (Rn), @(Rn) */ + if (indir) { /* @(Rn) = @0(Rn) */ + *sptr = 070 + reg; + *dptr = 0; + return -1; + } + else *sptr = 010 + reg; + return 0; + + case A_PAR+A_PLS: /* (Rn)+, @(Rn)+ */ + *sptr = 020 + indir + reg; + return 0; + + case A_MIN+A_PAR: /* -(Rn), @-(Rn) */ + *sptr = 040 + indir + reg; + return 0; + + case A_NUM+A_PAR: /* d(Rn), @d(Rn) */ + *sptr = 060 + indir + reg; + *dptr = disp; + return -1; + + case A_PND+A_REL: case A_PND+A_REL+A_NUM: /* #.+n, @#.+n */ + disp = (disp + addr) & 0177777; /* fall through */ + case A_PND+A_NUM: /* #n, @#n */ + *sptr = 027 + indir; + *dptr = disp; + return -1; + + case A_REL: case A_REL+A_NUM: /* .+n, @.+n */ + *sptr = 067 + indir; + *dptr = (disp - 4 + (2 * n1)) & 0177777; + return -1; + + case A_NUM: /* n, @n */ + *sptr = 067 + indir; + *dptr = (disp - addr - 4 + (2 * n1)) & 0177777; + return -1; + + default: + return 1; + } /* end case */ +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym_cm (char *cptr, t_addr addr, t_value *bytes, int32 sw) +{ +int32 d, i, j, reg, spec, n1, n2, disp, pflag; +int32 val[3]; +int32 ad32 = (int32) addr; +t_stat r; +char *tptr, gbuf[CBUFSIZE]; + +if (sw & SWMASK ('R')) /* radix 50 */ + return SCPE_ARG; +if (!(sw & SWMASK ('P')) || (ad32 & 1) || (ad32 > WMASK)) + return SCPE_ARG; + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +n1 = n2 = pflag = 0; +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) + return SCPE_ARG; +val[0] = opc_val[i] & 0177777; /* get value */ +j = (opc_val[i] >> I_V_CL) & I_M_CL; /* get class */ + +switch (j) { /* case on class */ + + case I_V_NPN: /* no operand */ + break; + + case I_V_REG: /* register */ + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((reg = get_reg (gbuf, 0)) < 0) + return SCPE_ARG; + val[0] = val[0] | reg; + break; + + case I_V_3B: case I_V_6B: case I_V_8B: /* xb literal */ + cptr = get_glyph (cptr, gbuf, 0); /* get literal */ + d = (int32) get_uint (gbuf, 16, (1 << j) - 1, &r); + if (r != SCPE_OK) + return SCPE_ARG; + val[0] = val[0] | d; /* put in place */ + break; + + case I_V_BR: /* cond br */ + cptr = get_glyph (cptr, gbuf, 0); /* get address */ + tptr = get_addr (gbuf, &disp, &pflag); /* parse */ + if ((tptr == NULL) || (*tptr != 0)) + return SCPE_ARG; + if ((pflag & A_REL) == 0) + disp = (disp - ad32) & 0177777; + if ((disp & 1) || (disp > 0400) && (disp < 0177402)) + return SCPE_ARG; + val[0] = val[0] | (((disp - 2) >> 1) & 0377); + break; + + case I_V_SOB: /* sob */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((reg = get_reg (gbuf, 0)) < 0) + return SCPE_ARG; + val[0] = val[0] | (reg << 6); + cptr = get_glyph (cptr, gbuf, 0); /* get address */ + tptr = get_addr (gbuf, &disp, &pflag); /* parse */ + if ((tptr == NULL) || (*tptr != 0)) + return SCPE_ARG; + if ((pflag & A_REL) == 0) + disp = (disp - ad32) & 0177777; + if ((disp & 1) || ((disp > 2) && (disp < 0177604))) + return SCPE_ARG; + val[0] = val[0] | (((2 - disp) >> 1) & 077); + break; + + case I_V_RSOP: /* reg, sop */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((reg = get_reg (gbuf, 0)) < 0) + return SCPE_ARG; + val[0] = val[0] | (reg << 6); /* fall through */ + case I_V_SOP: /* sop */ + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((n1 = get_spec (gbuf, ad32, 0, &spec, &val[1])) > 0) + return SCPE_ARG; + val[0] = val[0] | spec; + break; + + case I_V_SOPR: /* dop, reg */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((n1 = get_spec (gbuf, ad32, 0, &spec, &val[1])) > 0) + return SCPE_ARG; + val[0] = val[0] | spec; + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((reg = get_reg (gbuf, 0)) < 0) + return SCPE_ARG; + val[0] = val[0] | (reg << 6); + break; + + case I_V_DOP: /* double op */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((n1 = get_spec (gbuf, ad32, 0, &spec, &val[1])) > 0) + return SCPE_ARG; + val[0] = val[0] | (spec << 6); + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((n2 = get_spec (gbuf, ad32, n1, &spec, &val[1 - n1])) > 0) + return SCPE_ARG; + val[0] = val[0] | spec; + break; + + case I_V_CCC: case I_V_CCS: /* cond code oper */ + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0) ; i++) ; + if ((((opc_val[i] >> I_V_CL) & I_M_CL) != j) || + (opcode[i] == NULL)) + return SCPE_ARG; + val[0] = val[0] | (opc_val[i] & 0177777); + } + break; + + default: + return SCPE_ARG; + } + +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +for (i = j = 0; i < 3; i++, j = j + 2) { + bytes[j] = val[i] & BMASK; + bytes[j + 1] = (val[i] >> 8) & BMASK; + } +return ((2 * (n1 + n2)) - 1); +} diff --git a/src/VAX/vax_sysdev.cpp b/src/VAX/vax_sysdev.cpp new file mode 100644 index 0000000..353e173 --- /dev/null +++ b/src/VAX/vax_sysdev.cpp @@ -0,0 +1,2181 @@ +/* vax_sysdev.c: VAX 3900 system-specific logic + + 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"), + 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. + + This module contains the CVAX chip and VAX 3900 system-specific registers + and devices. + + rom bootstrap ROM (no registers) + nvr non-volatile ROM (no registers) + csi console storage input + 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) + 30-Sep-04 RMS Moved CADR, MSER, CONPC, CONPSL, machine_check, cpu_boot, + con_halt here from vax_cpu.c + Moved model-specific IPR's here from vax_cpu1.c + 09-Sep-04 RMS Integrated powerup into RESET (with -p) + Added model-specific registers and routines from CPU + 23-Jan-04 MP Added extended physical memory support (Mark Pizzolato) + 07-Jun-03 MP Added calibrated delay to ROM reads (Mark Pizzolato) + Fixed calibration problems interval timer (Mark Pizzolato) + 12-May-03 RMS Fixed compilation warnings from VC.Net + 23-Apr-03 RMS Revised for 32b/64b t_addr + 19-Aug-02 RMS Removed unused variables (found by David Hittner) + Allowed NVR to be attached to file + 30-May-02 RMS Widened POS to 32b + 28-Feb-02 RMS Fixed bug, missing end of table (found by Lars Brinkhoff) +*/ + +#include "sim_defs.h" +#include "vax_defs.h" + +#ifndef DONT_USE_INTERNAL_ROM +# include "vax_ka655x_bin.h" +#endif + +#define UNIT_V_NODELAY (UNIT_V_UF + 0) /* ROM access equal to RAM access */ +#define UNIT_NODELAY (1u << UNIT_V_NODELAY) + +/* Console storage control/status */ + +#define CSICSR_IMP (CSR_DONE + CSR_IE) /* console input */ +#define CSICSR_RW (CSR_IE) +#define CSOCSR_IMP (CSR_DONE + CSR_IE) /* console output */ +#define CSOCSR_RW (CSR_IE) + +/* CMCTL configuration registers */ + +#define CMCNF_VLD 0x80000000 /* addr valid */ +#define CMCNF_BA 0x1FF00000 /* base addr */ +#define CMCNF_LOCK 0x00000040 /* lock NI */ +#define CMCNF_SRQ 0x00000020 /* sig req WO */ +#define CMCNF_SIG 0x0000001F /* signature */ +#define CMCNF_RW (CMCNF_VLD | CMCNF_BA) /* read/write */ +#define CMCNF_MASK (CMCNF_RW | CMCNF_SIG) +#define MEM_BANK (1 << 22) /* bank size 4MB */ +#define MEM_SIG (0x17) /* ECC, 4 x 4MB */ + +/* CMCTL error register */ + +#define CMERR_RDS 0x80000000 /* uncorr err NI */ +#define CMERR_FRQ 0x40000000 /* 2nd RDS NI */ +#define CMERR_CRD 0x20000000 /* CRD err NI */ +#define CMERR_PAG 0x1FFFFC00 /* page addr NI */ +#define CMERR_DMA 0x00000100 /* DMA err NI */ +#define CMERR_BUS 0x00000080 /* bus err NI */ +#define CMERR_SYN 0x0000007F /* syndrome NI */ +#define CMERR_W1C (CMERR_RDS | CMERR_FRQ | CMERR_CRD | \ + CMERR_DMA | CMERR_BUS) + +/* CMCTL control/status register */ + +#define CMCSR_PMI 0x00002000 /* PMI speed NI */ +#define CMCSR_CRD 0x00001000 /* enb CRD int NI */ +#define CMCSR_FRF 0x00000800 /* force ref WONI */ +#define CMCSR_DET 0x00000400 /* dis err NI */ +#define CMCSR_FDT 0x00000200 /* fast diag NI */ +#define CMCSR_DCM 0x00000080 /* diag mode NI */ +#define CMCSR_SYN 0x0000007F /* syndrome NI */ +#define CMCSR_MASK (CMCSR_PMI | CMCSR_CRD | CMCSR_DET | \ + CMCSR_FDT | CMCSR_DCM | CMCSR_SYN) + +/* KA655 boot/diagnostic register */ + +#define BDR_BRKENB 0x00000080 /* break enable */ + +/* KA655 cache control register */ + +#define CACR_DRO 0x00FFFF00 /* diag bits RO */ +#define CACR_V_DPAR 24 /* data parity */ +#define CACR_FIXED 0x00000040 /* fixed bits */ +#define CACR_CPE 0x00000020 /* parity err W1C */ +#define CACR_CEN 0x00000010 /* enable */ +#define CACR_DPE 0x00000004 /* disable par NI */ +#define CACR_WWP 0x00000002 /* write wrong par NI */ +#define CACR_DIAG 0x00000001 /* diag mode */ +#define CACR_W1C (CACR_CPE) +#define CACR_RW (CACR_CEN | CACR_DPE | CACR_WWP | CACR_DIAG) + +/* + * For purposes of SSC (System Support Chip) see "KA655 CPU System Maintenance" manual, p. 1.10 + */ + +/* SSC base register */ + +#define SSCBASE_MBO 0x20000000 /* must be one */ +#define SSCBASE_RW 0x1FFFFC00 /* base address */ + +/* SSC configuration register */ + +#define SSCCNF_BLO 0x80000000 /* batt low W1C */ +#define SSCCNF_IVD 0x08000000 /* int dsbl NI */ +#define SSCCNF_IPL 0x03000000 /* int IPL NI */ +#define SSCCNF_ROM 0x00F70000 /* ROM param NI */ +#define SSCCNF_CTLP 0x00008000 /* ctrl P enb */ +#define SSCCNF_BAUD 0x00007700 /* baud rates NI */ +#define SSCCNF_ADS 0x00000077 /* addr strb NI */ +#define SSCCNF_W1C SSCCNF_BLO +#define SSCCNF_RW 0x0BF7F777 + +/* SSC timeout register */ + +#define SSCBTO_BTO 0x80000000 /* timeout W1C */ +#define SSCBTO_RWT 0x40000000 /* read/write W1C */ +#define SSCBTO_INTV 0x00FFFFFF /* interval NI */ +#define SSCBTO_W1C (SSCBTO_BTO | SSCBTO_RWT) +#define SSCBTO_RW SSCBTO_INTV + +/* SSC output port */ + +#define SSCOTP_MASK 0x0000000F /* output port */ + +/* SSC timer control/status */ + +#define TMR_CSR_ERR 0x80000000 /* error W1C */ +#define TMR_CSR_DON 0x00000080 /* done W1C */ +#define TMR_CSR_IE 0x00000040 /* int enb */ +#define TMR_CSR_SGL 0x00000020 /* single WO */ +#define TMR_CSR_XFR 0x00000010 /* xfer WO */ +#define TMR_CSR_STP 0x00000004 /* stop */ +#define TMR_CSR_RUN 0x00000001 /* run */ +#define TMR_CSR_W1C (TMR_CSR_ERR | TMR_CSR_DON) +#define TMR_CSR_RW (TMR_CSR_IE | TMR_CSR_STP | TMR_CSR_RUN) + +/* SSC timer intervals */ + +#define TMR_INC 10000 /* usec/interval */ + +/* SSC timer vector */ + +#define TMR_VEC_MASK 0x000003FC /* vector */ + +/* SSC address strobes */ + +#define SSCADS_MASK 0x3FFFFFFC /* match or mask */ + +#define sysd_activating(tmr) do { cpu_unit->sysd_active_mask |= (1 << (tmr)); } while (0) +#define sysd_deactivating(tmr) do { cpu_unit->sysd_active_mask &= ~(1 << (tmr)); } while (0) + +extern UNIT clk_unit; +extern int32 sim_switches; + +uint32 *rom = NULL; /* boot ROM */ +uint32 *nvr = NULL; /* non-volatile mem */ +int32 conpc, conpsl; /* console reg */ +int32 csi_csr = 0; /* control/status */ +int32 cso_csr = 0; /* control/status */ +int32 ka_bdr = BDR_BRKENB; /* KA655 boot diag */ +int32 ssc_base = SSCBASE; /* SSC base */ +int32 ssc_cnf = 0; /* SSC conf */ +static uint32 rom_delay = 0; + +t_stat rom_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); +t_stat rom_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); +t_stat rom_reset (DEVICE *dptr); +t_stat nvr_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); +t_stat nvr_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); +t_stat nvr_reset (DEVICE *dptr); +t_stat nvr_attach (UNIT *uptr, char *cptr); +t_stat nvr_detach (UNIT *uptr); +t_stat csi_reset (DEVICE *dptr); +t_stat cso_reset (DEVICE *dptr); +t_stat cso_svc (RUN_SVC_DECL, UNIT *uptr); +t_stat tmr_svc (RUN_SVC_DECL, UNIT *uptr); +t_stat sysd_reset (DEVICE *dptr); + +int32 rom_rd (RUN_DECL, int32 pa); +int32 nvr_rd (RUN_DECL, int32 pa); +void nvr_wr (RUN_DECL, int32 pa, int32 val, int32 lnt); +int32 csrs_rd (RUN_DECL); +int32 csrd_rd (RUN_DECL); +int32 csts_rd (RUN_DECL); +void csrs_wr (RUN_DECL, int32 dat); +void csts_wr (RUN_DECL, int32 dat); +void cstd_wr (RUN_DECL, int32 dat); +int32 cmctl_rd (RUN_DECL, int32 pa); +void cmctl_wr (RUN_DECL, int32 pa, int32 val, int32 lnt); +int32 ka_rd (RUN_DECL, int32 pa); +void ka_wr (RUN_DECL, int32 pa, int32 val, int32 lnt); +int32 cdg_rd (RUN_DECL, int32 pa); +void cdg_wr (RUN_DECL, int32 pa, int32 val, int32 lnt); +int32 ssc_rd (RUN_DECL, int32 pa); +void ssc_wr (RUN_DECL, int32 pa, int32 val, int32 lnt); +int32 tmr_tir_rd (RUN_DECL, int32 tmr, t_bool interp); +void tmr_csr_wr (RUN_DECL, int32 tmr, int32 val); +void tmr_sched (RUN_DECL, int32 tmr, t_bool is_realtime); +void tmr_incr (RUN_DECL, int32 tmr, uint32 inc, t_bool is_realtime); +int32 tmr0_inta (void); +int32 tmr1_inta (void); +static int32 tmr_tir_rd_realtime (RUN_DECL, int32 tmr); +static void tmr_csr_wr_realtime (RUN_DECL, int32 tmr, int32 val); +static void tmr_adjust_thread_priority(RUN_DECL, t_bool force_high = FALSE); +int32 parity (int32 val, int32 odd); +t_stat sysd_powerup (RUN_DECL); + +extern int32 intexc (RUN_DECL, int32 vec, int32 cc, int32 ipl, int ei); +extern int32 cqmap_rd (RUN_DECL, int32 pa); +extern void cqmap_wr (RUN_DECL, int32 pa, int32 val, int32 lnt); +extern int32 cqipc_rd (RUN_DECL, int32 pa); +extern void cqipc_wr (RUN_DECL, int32 pa, int32 val, int32 lnt); +extern int32 cqbic_rd (RUN_DECL, int32 pa); +extern void cqbic_wr (RUN_DECL, int32 pa, int32 val, int32 lnt); +extern int32 cqmem_rd (RUN_DECL, int32 pa); +extern void cqmem_wr (RUN_DECL, int32 pa, int32 val, int32 lnt); +extern int32 iccs_rd (RUN_DECL); +extern int32 todr_rd (RUN_DECL); +extern int32 rxcs_rd (RUN_DECL); +extern int32 rxdb_rd (RUN_DECL); +extern int32 txcs_rd (RUN_DECL); +extern void iccs_wr (RUN_DECL, int32 dat); +extern void todr_wr (RUN_DECL, int32 dat); +extern void rxcs_wr (RUN_DECL, int32 dat); +extern void txcs_wr (RUN_DECL, int32 dat); +extern void txdb_wr (RUN_DECL, int32 dat); +extern void ioreset_wr (RUN_DECL, int32 dat); +extern uint32 sim_os_msec(); + +/* + * For more information on ROM see "KA655 CPU System Maintenance" manual, p. 1.11. + */ + +/* ROM data structures + + rom_dev ROM device descriptor + rom_unit ROM units + rom_reg ROM register list +*/ + +UNIT rom_unit UDATA_SINGLE (NULL, UNIT_FIX+UNIT_BINK, ROMSIZE); +UNIT_TABLE_SINGLE(rom_unit); + +REG rom_reg[] = { + { NULL } + }; + +MTAB rom_mod[] = { + { UNIT_NODELAY, UNIT_NODELAY, "fast access", "NODELAY", NULL }, + { UNIT_NODELAY, 0, "1usec calibrated access", "DELAY", NULL }, + { 0 } + }; + +DEVICE rom_dev = { + "ROM", rom_unit_table, rom_reg, rom_mod, + 1, 16, ROMAWIDTH, 4, 16, 32, + &rom_ex, &rom_dep, &rom_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* NVR data structures + + nvr_dev NVR device descriptor + nvr_unit NVR units + nvr_reg NVR register list +*/ + +UNIT nvr_unit UDATA_SINGLE (NULL, UNIT_FIX+UNIT_BINK, NVRSIZE); +UNIT_TABLE_SINGLE(nvr_unit); + +REG nvr_reg[] = { + { NULL } + }; + +DEVICE nvr_dev = { + "NVR", nvr_unit_table, nvr_reg, NULL, + 1, 16, NVRAWIDTH, 4, 16, 32, + &nvr_ex, &nvr_dep, &nvr_reset, + NULL, &nvr_attach, &nvr_detach, + NULL, 0 + }; + +/* CSI data structures + + csi_dev CSI device descriptor + csi_unit CSI unit descriptor + csi_reg CSI register list +*/ + +DIB csi_dib = { 0, 0, NULL, NULL, 1, IVCL (CSI), SCB_CSI, { NULL } }; + +UNIT csi_unit UDATA_SINGLE_WAIT (NULL, 0, 0, KBD_POLL_WAIT); +UNIT_TABLE_SINGLE(csi_unit); + +REG csi_reg[] = { + { ORDATA_GBL (BUF, csi_unit.buf, 8) }, + { ORDATA_GBL (CSR, csi_csr, 16) }, + { IRDATA_DEV (INT, IVCL (CSI)) }, + { FLDATA_GBL (DONE, csi_csr, CSR_V_DONE) }, + { FLDATA_GBL (IE, csi_csr, CSR_V_IE) }, + { DRDATA_GBL (POS, csi_unit.pos, 32), PV_LEFT }, + { DRDATA_GBL (TIME, csi_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } + }; + +MTAB csi_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec }, + { 0 } + }; + +DEVICE csi_dev = { + "CSI", csi_unit_table, csi_reg, csi_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &csi_reset, + NULL, NULL, NULL, + &csi_dib, 0 + }; + +/* CSO data structures + + cso_dev CSO device descriptor + cso_unit CSO unit descriptor + cso_reg CSO register list +*/ + +DIB cso_dib = { 0, 0, NULL, NULL, 1, IVCL (CSO), SCB_CSO, { NULL } }; + +UNIT cso_unit UDATA_SINGLE_WAIT (&cso_svc, UNIT_SEQ+UNIT_ATTABLE, 0, SERIAL_OUT_WAIT); +UNIT_TABLE_SINGLE(cso_unit); + +REG cso_reg[] = { + { ORDATA_GBL (BUF, cso_unit.buf, 8) }, + { ORDATA_GBL (CSR, cso_csr, 16) }, + { IRDATA_DEV (INT, IVCL (CSO)) }, + { FLDATA_GBL (DONE, cso_csr, CSR_V_DONE) }, + { FLDATA_GBL (IE, cso_csr, CSR_V_IE) }, + { DRDATA_GBL (POS, cso_unit.pos, 32), PV_LEFT }, + { DRDATA_GBL (TIME, cso_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB cso_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec }, + { 0 } + }; + +DEVICE cso_dev = { + "CSO", cso_unit_table, cso_reg, cso_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &cso_reset, + NULL, NULL, NULL, + &cso_dib, 0 + }; + +/* SYSD data structures + + sysd_dev SYSD device descriptor + sysd_unit SYSD units + sysd_reg SYSD register list +*/ + +DIB sysd_dib = { + 0, 0, NULL, NULL, + 2, IVCL (TMR0), 0, { &tmr0_inta, &tmr1_inta } + }; + +UNIT* sysd_unit[] = { + UDATA (&tmr_svc, 0, 0), + UDATA (&tmr_svc, 0, 0) + }; + +REG sysd_reg[] = { + { HRDATA_CPU ("CADR", r_CADR, 8) }, + { HRDATA_CPU ("MSER", r_MSER, 8) }, + { HRDATA_GBL (CONPC, conpc, 32) }, + { HRDATA_GBL (CONPSL, conpsl, 32) }, + { BRDATA_CPU ("CMCSR", r_cmctl_reg, 16, 32, CMCTLSIZE >> 2) }, + { HRDATA_CPU ("CACR", r_ka_cacr, 8) }, + { HRDATA_GBL (BDR, ka_bdr, 8) }, + { HRDATA_GBL (BASE, ssc_base, 29) }, + { HRDATA_GBL (CNF, ssc_cnf, 32) }, + { HRDATA_CPU ("BTO", r_ssc_bto, 32) }, + { HRDATA_CPU ("OTP", r_ssc_otp, 4) }, + { HRDATA_CPU ("TCSR0", r_tmr_csr[0], 32) }, + { HRDATA_CPU ("TIR0", r_tmr_tir[0], 32) }, + { HRDATA_CPU ("TNIR0", r_tmr_tnir[0], 32) }, + { HRDATA_CPU ("TIVEC0", r_tmr_tivr[0], 9) }, + { HRDATA_CPU ("TINC0", r_tmr_inc[0], 32) }, + { HRDATA_CPU ("TSAV0", r_tmr_sav[0], 32) }, + { HRDATA_CPU ("TCSR1", r_tmr_csr[1], 32) }, + { HRDATA_CPU ("TIR1", r_tmr_tir[1], 32) }, + { HRDATA_CPU ("TNIR1", r_tmr_tnir[1], 32) }, + { HRDATA_CPU ("TIVEC1", r_tmr_tivr[1], 9) }, + { HRDATA_CPU ("TINC1", r_tmr_inc[1], 32) }, + { HRDATA_CPU ("TSAV1", r_tmr_sav[1], 32) }, + { HRDATA_CPU ("ADSM0", r_ssc_adsm[0], 32) }, + { HRDATA_CPU ("ADSK0", r_ssc_adsk[0], 32) }, + { HRDATA_CPU ("ADSM1", r_ssc_adsm[1], 32) }, + { HRDATA_CPU ("ADSK1", r_ssc_adsk[1], 32) }, + { BRDATA_CPU ("CDGDAT", r_cdg_dat, 16, 32, CDASIZE >> 2) }, + { NULL } + }; + +DEVICE sysd_dev = { + "SYSD", sysd_unit, sysd_reg, NULL, + 2, 16, 16, 1, 16, 8, + NULL, NULL, &sysd_reset, + NULL, NULL, NULL, + &sysd_dib, DEV_PERCPU + }; + +AUTO_INIT_DEVLOCK(sysd_lock); +AUTO_INIT_DEVLOCK(csio_lock); + +/* ROM: read only memory - stored in a buffered file + Register space access routines see ROM twice + + ROM access has been 'regulated' to about 1Mhz to avoid issues + with testing the interval timers in self-test. Specifically, + the VAX boot ROM (ka655.bin) contains code which presumes that + the VAX runs at a particular slower speed when code is running + from ROM (which is not cached). These assumptions are built + into instruction based timing loops. As the host platform gets + much faster than the original VAX, the assumptions embedded in + these code loops are no longer valid. + + Code has been added to the ROM implementation to limit CPU speed + to about 500K instructions per second. This heads off any future + issues with the embedded timing loops. +*/ + +int32 rom_swapb(int32 val) +{ + return ((val << 24) & 0xff000000) | (( val << 8) & 0xff0000) | + ((val >> 8) & 0xff00) | ((val >> 24) & 0xff); +} + +int32 rom_read_delay (int32 val) +{ + uint32 i, l = rom_delay; + volatile int32 loopval = 0; + + if (rom_unit.flags & UNIT_NODELAY) + return val; + + /* Calibrate the loop delay factor when first used. + Do this 4 times to and use the largest value computed. */ + + if (rom_delay == 0) + { + uint32 ts, te, c = 10000, samples = 0; + while (1) + { + c = c * 2; + te = sim_os_msec(); + while (te == (ts = sim_os_msec ())); /* align on ms tick */ + + /* This is merely a busy wait with some "work" that won't get optimized + away by a good compiler. loopval always is zero. To avoid smart compilers, + the loopval variable is referenced in the function arguments so that the + function expression is not loop invariant. It also must be referenced + by subsequent code or to avoid the whole computation being eliminated. */ + + for (i = 0; i < c; i++) + loopval |= (loopval + ts) ^ rom_swapb (rom_swapb (loopval + ts)); + te = sim_os_msec (); + if ((te - ts) < 50) /* sample big enough? */ + continue; + if (rom_delay < (loopval + (c / (te - ts) / 1000) + 1)) + rom_delay = loopval + (c / (te - ts) / 1000) + 1; + if (++samples >= 4) + break; + c = c / 2; + } + if (rom_delay < 5) + rom_delay = 5; + } + + for (i = 0; i < l; i++) + loopval |= (loopval + val) ^ rom_swapb (rom_swapb (loopval + val)); + return val + loopval; +} + +int32 rom_rd (RUN_DECL, int32 pa) +{ + /* + * Kludge: Detect that primary processor jumped into ROM while secondary + * processors were active, and perform emergency shutdown of secondaries. + * + * This condition should normally never happen, this is just an emergency safeguard + * in case guest OS fails to shut down properly. + * + * Logically this should be done in main instruction loop, but it would be + * inefficient to place it there. + */ + cpu_on_rom_rd(RUN_PASS); + + int32 rg = ((pa - ROMBASE) & ROMAMASK) >> 2; + return rom_read_delay (rom[rg]); +} + +void rom_wr_B (RUN_DECL, int32 pa, int32 val) +{ + int32 rg = ((pa - ROMBASE) & ROMAMASK) >> 2; + int32 sc = (pa & 3) << 3; + + rom[rg] = ((val & 0xFF) << sc) | (rom[rg] & ~(0xFF << sc)); +} + +/* ROM examine */ + +t_stat rom_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +{ + RUN_SCOPE; + uint32 addr = (uint32) exta; + + if ((vptr == NULL) || (addr & 03)) + return SCPE_ARG; + if (addr >= ROMSIZE) + return SCPE_NXM; + *vptr = rom[addr >> 2]; + return SCPE_OK; +} + +/* ROM deposit */ + +t_stat rom_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) +{ + RUN_SCOPE; + uint32 addr = (uint32) exta; + + if (addr & 03) + return SCPE_ARG; + if (addr >= ROMSIZE) + return SCPE_NXM; + rom[addr >> 2] = (uint32) val; + return SCPE_OK; +} + +/* ROM reset */ + +t_stat rom_reset (DEVICE *dptr) +{ + RUN_SCOPE; + if (rom == NULL) + rom = (uint32*) calloc (ROMSIZE >> 2, sizeof (uint32)); + if (rom == NULL) + return SCPE_MEM; + return SCPE_OK; +} + +/* NVR: non-volatile RAM - stored in a buffered file */ + +int32 nvr_rd (RUN_DECL, int32 pa) +{ + int32 rg = (pa - NVRBASE) >> 2; + return nvr[rg]; +} + +void nvr_wr (RUN_DECL, int32 pa, int32 val, int32 lnt) +{ + if (! cpu_unit->is_primary_cpu()) + { + /* On MicroVAX 3900, only boot ROM firmware and early stages of VMS SYSBOOT write to NVR */ + smp_printf ("\nNon-primary CPU (CPU%d) attempted to write NVR\n", cpu_unit->cpu_id); + if (sim_log) + fprintf (sim_log, "Non-primary CPU (CPU%d) attempted to write NVR\n", cpu_unit->cpu_id); + ABORT_INVALID_SYSOP; + } + + int32 rg = (pa - NVRBASE) >> 2; + + if (lnt < L_LONG) /* byte or word? */ + { + int32 sc = (pa & 3) << 3; /* merge */ + int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; + nvr[rg] = ((val & mask) << sc) | (nvr[rg] & ~(mask << sc)); + } + else + { + nvr[rg] = val; + } +} + +/* NVR examine */ + +t_stat nvr_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +{ + RUN_SCOPE; + uint32 addr = (uint32) exta; + + if ((vptr == NULL) || (addr & 03)) + return SCPE_ARG; + if (addr >= NVRSIZE) + return SCPE_NXM; + *vptr = nvr[addr >> 2]; + return SCPE_OK; +} + +/* NVR deposit */ + +t_stat nvr_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) +{ + RUN_SCOPE; + uint32 addr = (uint32) exta; + + if (addr & 03) + return SCPE_ARG; + if (addr >= NVRSIZE) + return SCPE_NXM; + nvr[addr >> 2] = (uint32) val; + return SCPE_OK; +} + +/* NVR reset */ + +t_stat nvr_reset (DEVICE *dptr) +{ + RUN_SCOPE; + AUTO_LOCK(sysd_lock); + sim_bind_devunits_lock(&nvr_dev, sysd_lock); + if (nvr == NULL) + { + nvr = (uint32*) calloc (NVRSIZE >> 2, sizeof (uint32)); + nvr_unit.filebuf = nvr; + ssc_cnf = ssc_cnf | SSCCNF_BLO; + } + if (nvr == NULL) + return SCPE_MEM; + return SCPE_OK; +} + +/* NVR attach */ + +t_stat nvr_attach (UNIT *uptr, char *cptr) +{ + t_stat r; + + uptr->flags = uptr->flags | (UNIT_ATTABLE | UNIT_BUFABLE); + r = attach_unit (uptr, cptr); + if (r != SCPE_OK) + uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); + else + { + uptr->hwmark = (uint32) uptr->capac; + ssc_cnf = ssc_cnf & ~SSCCNF_BLO; + } + return r; +} + +/* NVR detach */ + +t_stat nvr_detach (UNIT *uptr) +{ + t_stat r; + + r = detach_unit (uptr); + if ((uptr->flags & UNIT_ATT) == 0) + uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); + return r; +} + +/* CSI: console storage input */ + +int32 csrs_rd (RUN_DECL) +{ + AUTO_LOCK(csio_lock); + return (csi_csr & CSICSR_IMP); +} + +int32 csrd_rd (RUN_DECL) +{ + AUTO_LOCK(csio_lock); + csi_csr = csi_csr & ~CSR_DONE; + CLR_INT (CSI); + return (csi_unit.buf & 0377); +} + +void csrs_wr (RUN_DECL, int32 data) +{ + AUTO_LOCK(csio_lock); + if ((data & CSR_IE) == 0) + CLR_INT (CSI); + else if ((csi_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + SET_INT (CSI); + csi_csr = (csi_csr & ~CSICSR_RW) | (data & CSICSR_RW); +} + +t_stat csi_reset (DEVICE *dptr) +{ + AUTO_LOCK(csio_lock); + sim_bind_devunits_lock(&csi_dev, csio_lock); + csi_unit.buf = 0; + csi_csr = 0; + CLR_INT (CSI); + return SCPE_OK; +} + +/* CSO: console storage output */ + +int32 csts_rd (RUN_DECL) +{ + AUTO_LOCK(csio_lock); + return (cso_csr & CSOCSR_IMP); +} + +void csts_wr (RUN_DECL, int32 data) +{ + AUTO_LOCK(csio_lock); + if ((data & CSR_IE) == 0) + CLR_INT (CSO); + else if ((cso_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + SET_INT (CSO); + cso_csr = (cso_csr & ~CSOCSR_RW) | (data & CSOCSR_RW); +} + +void cstd_wr (RUN_DECL, int32 data) +{ + AUTO_LOCK(csio_lock); + cso_unit.buf = data & 0377; + cso_csr = cso_csr & ~CSR_DONE; + CLR_INT (CSO); + sim_activate (&cso_unit, cso_unit.wait); +} + +t_stat cso_svc (RUN_SVC_DECL, UNIT *uptr) +{ + AUTO_LOCK(csio_lock); + RUN_SVC_CHECK_CANCELLED(uptr); + cso_csr = cso_csr | CSR_DONE; + if (cso_csr & CSR_IE) + SET_INT (CSO); + if ((cso_unit.flags & UNIT_ATT) == 0) + return SCPE_OK; + if (putc (cso_unit.buf, cso_unit.fileref) == EOF) + { + smp_perror ("CSO I/O error"); + clearerr (cso_unit.fileref); + return SCPE_IOERR; + } + cso_unit.pos = cso_unit.pos + 1; + return SCPE_OK; +} + +t_stat cso_reset (DEVICE *dptr) +{ + AUTO_LOCK(csio_lock); + sim_bind_devunits_lock(&cso_dev, csio_lock); + cso_unit.buf = 0; + cso_csr = CSR_DONE; + CLR_INT (CSO); + sim_cancel (&cso_unit); /* deactivate unit */ + return SCPE_OK; +} + +/* SYSD: SSC access mechanisms and devices + + - IPR space read/write routines + - register space read/write routines + - SSC local register read/write routines + - SSC console storage UART + - SSC timers + - CMCTL local register read/write routines +*/ + +/* Read/write IPR register space + + These routines implement the SSC's response to IPR's which are + sent off the CPU chip for processing. +*/ + +int32 ReadIPR (RUN_DECL, int32 rg) +{ + int32 val; + + switch (rg) + { + case MT_ICCS: /* ICCS */ + val = iccs_rd (RUN_PASS); + break; + + case MT_CSRS: /* CSRS */ + val = csrs_rd (RUN_PASS); + break; + + case MT_CSRD: /* CSRD */ + val = csrd_rd (RUN_PASS); + break; + + case MT_CSTS: /* CSTS */ + val = csts_rd (RUN_PASS); + break; + + case MT_CSTD: /* CSTD */ + val = 0; + break; + + case MT_RXCS: /* RXCS */ + val = rxcs_rd (RUN_PASS); + break; + + case MT_RXDB: /* RXDB */ + val = rxdb_rd (RUN_PASS); + break; + + case MT_TXCS: /* TXCS */ + val = txcs_rd (RUN_PASS); + break; + + case MT_TXDB: /* TXDB */ + val = 0; + break; + + case MT_TODR: /* TODR */ + val = todr_rd (RUN_PASS); + break; + + case MT_CADR: /* CADR */ + val = CADR & 0xFF; + break; + + case MT_MSER: /* MSER */ + val = MSER & 0xFF; + break; + + case MT_CONPC: /* console PC */ + if (! cpu_unit->is_primary_cpu()) + { + // On MicroVAX 3900, CONPC and CONPSL are intened for use by firmware only -- that executes on the primary CPU. + // VMS does not access SAVPC/SAVPSL except on Calypso (VAX 9CC) processors. + smp_printf ("\nNon-primary CPU (CPU%d) attempted to read CONPC\n", cpu_unit->cpu_id); + if (sim_log) + fprintf (sim_log, "Non-primary CPU (CPU%d) attempted to read CONPC\n", cpu_unit->cpu_id); + ABORT_INVALID_SYSOP; + } + val = conpc; + break; + + case MT_CONPSL: /* console PSL */ + if (! cpu_unit->is_primary_cpu()) + { + // On MicroVAX 3900, CONPC and CONPSL are intened for use by firmware only -- that executes on the primary CPU. + // VMS does not access SAVPC/SAVPSL except on Calypso (VAX 9CC) processors. + smp_printf ("\nNon-primary CPU (CPU%d) attempted to read CONPSL\n", cpu_unit->cpu_id); + if (sim_log) + fprintf (sim_log, "Non-primary CPU (CPU%d) attempted to read CONPSL\n", cpu_unit->cpu_id); + ABORT_INVALID_SYSOP; + } + val = conpsl; + break; + + case MT_SID: /* SID */ + val = CVAX_SID | CVAX_UREV; + break; + + default: + ssc_bto = ssc_bto | SSCBTO_BTO; /* set BTO */ + val = 0; + break; + } + + return val; +} + +void WriteIPR (RUN_DECL, int32 rg, int32 val, t_bool& set_irql) +{ + switch (rg) + { + case MT_ICCS: /* ICCS */ + iccs_wr (RUN_PASS, val); + break; + + case MT_TODR: /* TODR */ + todr_wr (RUN_PASS, val); + break; + + case MT_CSRS: /* CSRS */ + csrs_wr (RUN_PASS, val); + break; + + case MT_CSRD: /* CSRD */ + break; + + case MT_CSTS: /* CSTS */ + csts_wr (RUN_PASS, val); + break; + + case MT_CSTD: /* CSTD */ + cstd_wr (RUN_PASS, val); + break; + + case MT_RXCS: /* RXCS */ + rxcs_wr (RUN_PASS, val); + break; + + case MT_RXDB: /* RXDB */ + break; + + case MT_TXCS: /* TXCS */ + txcs_wr (RUN_PASS, val); + break; + + case MT_TXDB: /* TXDB */ + txdb_wr (RUN_PASS, val); + break; + + case MT_CADR: /* CADR */ + CADR = (val & CADR_RW) | CADR_MBO; + set_irql = FALSE; + break; + + case MT_MSER: /* MSER */ + MSER = MSER & MSER_HM; + set_irql = FALSE; + break; + + case MT_IORESET: /* IORESET */ + ioreset_wr (RUN_PASS, val); + break; + + case MT_SID: + case MT_CONPC: + case MT_CONPSL: /* halt reg */ + RSVD_OPND_FAULT; + + default: + ssc_bto = ssc_bto | SSCBTO_BTO; /* set BTO */ + break; + } +} + +/* Read/write I/O register space + + These routines are the 'catch all' for address space map. Any + address that doesn't explicitly belong to memory, I/O, or ROM + is given to these routines for processing. +*/ + +struct reglink { /* register linkage */ + uint32 low; /* low addr */ + uint32 high; /* high addr */ + int32 (*read)(RUN_DECL, int32 pa); /* read routine */ + void (*write)(RUN_DECL, int32 pa, int32 val, int32 lnt); /* write routine */ + }; + +struct reglink regtable[] = { + { CQMAPBASE, CQMAPBASE+CQMAPSIZE, &cqmap_rd, &cqmap_wr }, + { ROMBASE, ROMBASE+ROMSIZE+ROMSIZE, &rom_rd, NULL }, + { NVRBASE, NVRBASE+NVRSIZE, &nvr_rd, &nvr_wr }, + { CMCTLBASE, CMCTLBASE+CMCTLSIZE, &cmctl_rd, &cmctl_wr }, + { SSCBASE, SSCBASE+SSCSIZE, &ssc_rd, &ssc_wr }, + { KABASE, KABASE+KASIZE, &ka_rd, &ka_wr }, + { CQBICBASE, CQBICBASE+CQBICSIZE, &cqbic_rd, &cqbic_wr }, + { CQIPCBASE, CQIPCBASE+CQIPCSIZE, &cqipc_rd, &cqipc_wr }, + { CQMBASE, CQMBASE+CQMSIZE, &cqmem_rd, &cqmem_wr }, + { CDGBASE, CDGBASE+CDGSIZE, &cdg_rd, &cdg_wr }, + { 0, 0, NULL, NULL } + }; + +/* ReadReg - read register space + + Inputs: + pa = physical address + lnt = length (BWLQ) - ignored + Output: + longword of data +*/ + +int32 ReadReg (RUN_DECL, uint32 pa, int32 lnt) +{ + struct reglink *p; + + for (p = ®table[0]; p->low != 0; p++) + { + if ((pa >= p->low) && (pa < p->high) && p->read) + return p->read (RUN_PASS, pa); + } + ssc_bto = ssc_bto | SSCBTO_BTO | SSCBTO_RWT; + MACH_CHECK (MCHK_READ); + return 0; +} + +/* WriteReg - write register space + + Inputs: + pa = physical address + val = data to write, right justified in 32b longword + lnt = length (BWLQ) + Outputs: + none +*/ + +void WriteReg (RUN_DECL, uint32 pa, int32 val, int32 lnt) +{ + struct reglink *p; + + for (p = ®table[0]; p->low != 0; p++) + { + if ((pa >= p->low) && (pa < p->high) && p->write) + { + p->write (RUN_PASS, pa, val, lnt); + return; + } + } + ssc_bto = ssc_bto | SSCBTO_BTO | SSCBTO_RWT; + MACH_CHECK (MCHK_WRITE); +} + +/* + * For more information on CMCTL see "KA655 CPU System Maintenance" manual, pp 1.5, 1.9, 1.10, B.1-B.5. + */ + +/* CMCTL registers + + CMCTL00 - 15 configure memory banks 00 - 15. Note that they are + here merely to entertain the firmware; the actual configuration + of memory is unaffected by the settings here. + + CMCTL16 - error status register + + CMCTL17 - control/diagnostic status register + + The CMCTL registers are cleared at power up. +*/ + +int32 cmctl_rd (RUN_DECL, int32 pa) +{ + int32 rg = (pa - CMCTLBASE) >> 2; + + switch (rg) + { + default: /* config reg */ + return cmctl_reg[rg] & CMCNF_MASK; + + case 16: /* err status */ + return cmctl_reg[rg]; + + case 17: /* csr */ + return cmctl_reg[rg] & CMCSR_MASK; + + case 18: /* KA655X ext mem */ + if (MEMSIZE > MAXMEMSIZE) /* more than 128MB? */ + return ((int32) MEMSIZE); + MACH_CHECK (MCHK_READ); + } + + return 0; +} + +void cmctl_wr (RUN_DECL, int32 pa, int32 val, int32 lnt) +{ + int32 i, rg = (pa - CMCTLBASE) >> 2; + + if (lnt < L_LONG) /* LW write only */ + { + int32 sc = (pa & 3) << 3; /* shift data to */ + val = val << sc; /* proper location */ + } + + switch (rg) + { + default: /* config reg */ + if (val & CMCNF_SRQ) /* sig request? */ + { + int32 rg_g = rg & ~3; /* group of 4 */ + for (i = rg_g; i < (rg_g + 4); i++) + { + cmctl_reg[i] = cmctl_reg[i] & ~CMCNF_SIG; + if (ADDR_IS_MEM (i * MEM_BANK)) + cmctl_reg[i] = cmctl_reg[i] | MEM_SIG; + } + } + cmctl_reg[rg] = (cmctl_reg[rg] & ~CMCNF_RW) | (val & CMCNF_RW); + break; + + case 16: /* err status */ + cmctl_reg[rg] = cmctl_reg[rg] & ~(val & CMERR_W1C); + break; + + case 17: /* csr */ + cmctl_reg[rg] = val & CMCSR_MASK; + break; + + case 18: + MACH_CHECK (MCHK_WRITE); + } +} + +/* KA655 registers */ + +int32 ka_rd (RUN_DECL, int32 pa) +{ + int32 rg = (pa - KABASE) >> 2; + + switch (rg) + { + case 0: /* CACR */ + return ka_cacr; + + case 1: /* BDR */ + return ka_bdr; + } + + return 0; +} + +void ka_wr (RUN_DECL, int32 pa, int32 val, int32 lnt) +{ + int32 rg = (pa - KABASE) >> 2; + + if ((rg == 0) && ((pa & 3) == 0)) /* lo byte only */ + { + ka_cacr = (ka_cacr & ~(val & CACR_W1C)) | CACR_FIXED; + ka_cacr = (ka_cacr & ~CACR_RW) | (val & CACR_RW); + } +} + +int32 sysd_hlt_enb (void) +{ + return ka_bdr & BDR_BRKENB; +} + +/* Cache diagnostic space */ + +int32 cdg_rd (RUN_DECL, int32 pa) +{ + int32 t, row = CDG_GETROW (pa); + + t = cdg_dat[row]; + ka_cacr = ka_cacr & ~CACR_DRO; /* clear diag */ + ka_cacr = ka_cacr | + (parity ((t >> 24) & 0xFF, 1) << (CACR_V_DPAR + 3)) | + (parity ((t >> 16) & 0xFF, 0) << (CACR_V_DPAR + 2)) | + (parity ((t >> 8) & 0xFF, 1) << (CACR_V_DPAR + 1)) | + (parity (t & 0xFF, 0) << CACR_V_DPAR); + return t; +} + +void cdg_wr (RUN_DECL, int32 pa, int32 val, int32 lnt) +{ + int32 row = CDG_GETROW (pa); + + if (lnt < L_LONG) /* byte or word? */ + { + int32 sc = (pa & 3) << 3; /* merge */ + int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; + int32 t = cdg_dat[row]; + val = ((val & mask) << sc) | (t & ~(mask << sc)); + } + cdg_dat[row] = val; /* store data */ +} + +int32 parity (int32 val, int32 odd) +{ + for ( ; val != 0; val = val >> 1) + { + if (val & 1) + odd = odd ^ 1; + } + return odd; +} + +/* SSC registers - byte/word merges done in WriteReg */ + +int32 ssc_rd (RUN_DECL, int32 pa) +{ + int32 rg = (pa - SSCBASE) >> 2; + + switch (rg) + { + case 0x00: /* base reg */ + { + AUTO_LOCK(sysd_lock); + return ssc_base; + } + + case 0x04: /* conf reg */ + { + AUTO_LOCK(sysd_lock); + return ssc_cnf; + } + + case 0x08: /* bus timeout */ + return ssc_bto; + + case 0x0C: /* output port */ + return ssc_otp & SSCOTP_MASK; + + case 0x1B: /* TODR */ + return todr_rd (RUN_PASS); + + case 0x1C: /* CSRS */ + return csrs_rd (RUN_PASS); + + case 0x1D: /* CSRD */ + return csrd_rd (RUN_PASS); + + case 0x1E: /* CSTS */ + return csts_rd (RUN_PASS); + + case 0x20: /* RXCS */ + return rxcs_rd (RUN_PASS); + + case 0x21: /* RXDB */ + return rxdb_rd (RUN_PASS); + + case 0x22: /* TXCS */ + return txcs_rd (RUN_PASS); + + case 0x40: /* T0CSR */ + return tmr_csr[0]; + + case 0x41: /* T0INT */ + return tmr_tir_rd (RUN_PASS, 0, FALSE); + + case 0x42: /* T0NI */ + return tmr_tnir[0]; + + case 0x43: /* T0VEC */ + return tmr_tivr[0]; + + case 0x44: /* T1CSR */ + return tmr_csr[1]; + + case 0x45: /* T1INT */ + return tmr_tir_rd (RUN_PASS, 1, FALSE); + + case 0x46: /* T1NI */ + return tmr_tnir[1]; + + case 0x47: /* T1VEC */ + return tmr_tivr[1]; + + case 0x4C: /* ADS0M */ + return ssc_adsm[0]; + + case 0x4D: /* ADS0K */ + return ssc_adsk[0]; + + case 0x50: /* ADS1M */ + return ssc_adsm[1]; + + case 0x51: /* ADS1K */ + return ssc_adsk[1]; + } + + return 0; +} + +void ssc_wr (RUN_DECL, int32 pa, int32 val, int32 lnt) +{ + int32 rg = (pa - SSCBASE) >> 2; + + if (lnt < L_LONG) /* byte or word? */ + { + int32 sc = (pa & 3) << 3; /* merge */ + int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; + int32 t = ssc_rd (RUN_PASS, pa); + val = ((val & mask) << sc) | (t & ~(mask << sc)); + } + + switch (rg) + { + case 0x00: /* base reg */ + { + if (! cpu_unit->is_primary_cpu()) + { + smp_printf ("\nNon-primary CPU (CPU%d) attempted to write SSCBASE\n", cpu_unit->cpu_id); + if (sim_log) + fprintf (sim_log, "Non-primary CPU (CPU%d) attempted to write SSCBASE\n", cpu_unit->cpu_id); + ABORT_INVALID_SYSOP; + } + AUTO_LOCK(sysd_lock); + ssc_base = (val & SSCBASE_RW) | SSCBASE_MBO; + } + break; + + case 0x04: /* conf reg */ + { + AUTO_LOCK(sysd_lock); + ssc_cnf = ssc_cnf & ~(val & SSCCNF_W1C); + ssc_cnf = (ssc_cnf & ~SSCCNF_RW) | (val & SSCCNF_RW); + } + break; + + case 0x08: /* bus timeout */ + ssc_bto = ssc_bto & ~(val & SSCBTO_W1C); + ssc_bto = (ssc_bto & ~SSCBTO_RW) | (val & SSCBTO_RW); + break; + + case 0x0C: /* output port */ + ssc_otp = val & SSCOTP_MASK; + break; + + case 0x1B: /* TODR */ + todr_wr (RUN_PASS, val); + break; + + case 0x1C: /* CSRS */ + csrs_wr (RUN_PASS, val); + break; + + case 0x1E: /* CSTS */ + csts_wr (RUN_PASS, val); + break; + + case 0x1F: /* CSTD */ + cstd_wr (RUN_PASS, val); + break; + + case 0x20: /* RXCS */ + rxcs_wr (RUN_PASS, val); + break; + + case 0x22: /* TXCS */ + txcs_wr (RUN_PASS, val); + break; + + case 0x23: /* TXDB */ + txdb_wr (RUN_PASS, val); + break; + + case 0x40: /* T0CSR */ + tmr_csr_wr (RUN_PASS, 0, val); + break; + + case 0x42: /* T0NI */ + tmr_tnir[0] = val; + break; + + case 0x43: /* T0VEC */ + tmr_tivr[0] = val & TMR_VEC_MASK; + break; + + case 0x44: /* T1CSR */ + tmr_csr_wr (RUN_PASS, 1, val); + break; + + case 0x46: /* T1NI */ + tmr_tnir[1] = val; + break; + + case 0x47: /* T1VEC */ + tmr_tivr[1] = val & TMR_VEC_MASK; + break; + + case 0x4C: /* ADS0M */ + ssc_adsm[0] = val & SSCADS_MASK; + break; + + case 0x4D: /* ADS0K */ + ssc_adsk[0] = val & SSCADS_MASK; + break; + + case 0x50: /* ADS1M */ + ssc_adsm[1] = val & SSCADS_MASK; + break; + + case 0x51: /* ADS1K */ + ssc_adsk[1] = val & SSCADS_MASK; + break; + } +} + +/* + * VAX MP version of programmable timers is a hack upon the original uniprocessor SIMH hack. + * + * SSC timers are used in two sitiations. + * + * First is pre-boot ROM firmware tests. They play no functional significance for actual + * booting and execution of operating system after control is transferred to VMB and then + * to VMS (or Unix). + * + * The only goal with regard to these tests is to make them pass without annoying + * (but harmless) messages. Accuracy of values they measure is of no great significance. + * + * In VAX MP (both SYNCLK and non-SYNCLK modes) we use original SIMH code to process timers + * during ROM tests, except for Test 31 we return hard-coded known good values in SYNCLK + * mode, to suppress message caused by very tight tolerance margins on this test. + * + * ROM tests can be discriminated by MAPEN=0 and PC in ROM range. + * + * Second kind of use is the use by VMB and operating system. SSC timers are used by VMB, + * VMS SYSBOOT/INIT, and are also used during system shutdown or crash when IO system is + * recalibrated for reading in the bugcheck handling code and writing dump. This use can be + * discriminated by MAPEN=1 or PC in RAM range. Only timer 0 is used (never timer 1) + * and the pattern of access is as follows: + * + * TNIR0 = 0 + * TCR0 = RUN | XFR + * (calibrated loop) + * read TIR0 + * TCR0 = 0 + * + * Interrupts are not used (and counter does not overflow). + * + * We try to use host system timers to provide actual calibration data for this sequence. + */ + +/* Programmable timers + + The SSC timers, which increment at 1Mhz, cannot be accurately + simulated due to the overhead that would be required for 1M + clock events per second. Instead, a gross hack is used. When + a timer is started, the clock interval is inspected. + + if (int < 0 and small) then testing timer, count instructions. + Small is determined by when the requested interval is less + than the size of a 100hz system clock tick. + if (int >= 0 or large) then counting a real interval, schedule + clock events at 100Hz using calibrated line clock delay + and when the remaining time value gets small enough, behave + like the small case above. + + If the interval register is read, then its value between events + is interpolated using the current instruction count versus the + count when the most recent event started, the result is scaled + to the calibrated system clock, unless the interval being timed + is less than a calibrated system clock tick (or the calibrated + clock is running very slowly) at which time the result will be + the elapsed instruction count. + + The powerup TOY Test sometimes fails its tolerance test. This was + due to varying system load causing varying calibration values to be + used at different times while referencing the TIR. While timing long + intervals, we now synchronize the stepping (and calibration) of the + system tick with the opportunity to reference the value. This gives + precise tolerance measurement values (when interval timers are used + to measure the system clock), regardless of other load issues on the + host system which might cause varying values of the system clock's + calibration factor. +*/ + +int32 tmr_tir_rd (RUN_DECL, int32 tmr, t_bool interp) +{ + if (! cpu_unit->is_primary_cpu()) + { + /* + * Only boot ROM firmware, VMS SYSBOOT and EXE$INIT, and final phase of VMS shutdown access SSC clocks. + * All these phases are performed on the primary processor, so secondaries are not expected to access SSC clocks. + * On some VAX processors SSC clock is also accessed by VMS machine check handler, but not on the 650. + * SMP$SETUP_CPU in some SYSLOAxxx may also call EXE$INI_TIMWAIT calibration, but not for VAX MP based SMP + * which copies calibration data for the secondaries from the primary CPU. + * + * VAX MP SMP paravirtualization module (VSMP.EXE for VMS) can also call EXE$INI_TIMWAIT before enabling SMP + * to ensure that calibration is stable, and that loops were not undercalibrated, but this will be performed + * on the primary processor as well. + * + * VAX MP SSC clock implementation is per-CPU, so we could enable SSC clock use by the secondaries if ever needed. + * So far keep it disabled just to make sure nothing unintended goes on. + */ + smp_printf ("\nNon-primary CPU (CPU%d) attempted to access SSC clock (reading TIR%d)\n", cpu_unit->cpu_id, tmr); + if (sim_log) + fprintf (sim_log, "Non-primary CPU (CPU%d) attempted to access SSC clock (reading TIR%d)\n", cpu_unit->cpu_id, tmr); + ABORT_INVALID_SYSOP; + } + + t_bool rom_test = (mapen == 0) && ADDR_IS_ROM(PC); + if (! rom_test) + return tmr_tir_rd_realtime(RUN_PASS, tmr); + + uint32 delta; + int32 x_tmr_poll = weak_read_var(tmr_poll); + int32 res; + + if (interp || (tmr_csr[tmr] & TMR_CSR_RUN)) /* interp, running? */ + { + delta = sim_grtime (RUN_PASS) - tmr_sav[tmr]; /* delta inst */ + if ((tmr_inc[tmr] == TMR_INC) && /* scale large int */ + (x_tmr_poll > TMR_INC)) + delta = (uint32) ((((double) delta) * TMR_INC) / x_tmr_poll); + if (delta >= tmr_inc[tmr]) + delta = tmr_inc[tmr] - 1; + res = tmr_tir[tmr] + delta; + } + else + { + res = tmr_tir[tmr]; + } + + /* + * ROM Test 31 tends to fail when SYNCLK is used because its tolerances are + * very tight (tighter than 0.15%). Just put out know good values for it, + * to suppress annoying (but harmless) message about test failure. + */ + if (tmr == 1 && mapen == 0 && use_clock_thread) + { + if (PC == ROM_PC_TEST31_TIR1_RD_A) + res = 0x4E15; + else if (PC == ROM_PC_TEST31_TIR1_RD_B) + res = 0x1D4B5; + } + + return res; +} + +void tmr_csr_wr (RUN_DECL, int32 tmr, int32 val) +{ + if (tmr < 0 || tmr > 1) + return; + + if (! cpu_unit->is_primary_cpu()) + { + /* + * Only boot ROM firmware, VMS SYSBOOT and EXE$INIT, and final phase of VMS shutdown access SSC clocks. + * All these phases are performed on the primary processor, so secondaries are not expected to access SSC clocks. + * On some VAX processors SSC clock is also accessed by VMS machine check handler, but not on the 650. + * SMP$SETUP_CPU in some SYSLOAxxx may also call EXE$INI_TIMWAIT for calibration, but not under VAX MP based SMP + * which copies calibration data for the secondaries from the primary CPU. + * + * VAX MP SMP paravirtualization module (VSMP.EXE for VMS) can also call EXE$INI_TIMWAIT before enabling SMP + * to ensure that calibration is stable, and that loops were not undercalibrated, but this will be performed + * on the primary processor as well. + * + * VAX MP SSC clock implementation is per-CPU, so we could enable SSC clock use by the secondaries if ever needed. + * So far keep it disabled just to make sure nothing unintended goes on. + */ + smp_printf ("\nNon-primary CPU (CPU%d) attempted to access SSC clock (writing CSR%d)\n", cpu_unit->cpu_id, tmr); + if (sim_log) + fprintf (sim_log, "Non-primary CPU (CPU%d) attempted to access SSC clock (writing CSR%d)\n", cpu_unit->cpu_id, tmr); + ABORT_INVALID_SYSOP; + } + + /* do not elevate thread priority for ROM tests */ + t_bool rom_test = (mapen == 0) && ADDR_IS_ROM(PC); + t_bool is_realtime = !rom_test; + + if (is_realtime) + { + tmr_csr_wr_realtime (RUN_PASS, tmr, val); + return; + } + + if ((val & TMR_CSR_RUN) == 0) /* clearing run? */ + { + sim_cancel (sysd_unit[tmr]); /* cancel timer */ + sysd_deactivating(tmr); /* note as inactive */ + if (tmr_csr[tmr] & TMR_CSR_RUN) /* run 1 -> 0? */ + tmr_tir[tmr] = tmr_tir_rd (RUN_PASS, tmr, TRUE); /* update itr */ + } + tmr_csr[tmr] = tmr_csr[tmr] & ~(val & TMR_CSR_W1C); /* W1C csr */ + tmr_csr[tmr] = (tmr_csr[tmr] & ~TMR_CSR_RW) | /* new r/w */ + (val & TMR_CSR_RW); + if (val & TMR_CSR_XFR) /* xfr set? */ + tmr_tir[tmr] = tmr_tnir[tmr]; + if (val & TMR_CSR_RUN) /* run? */ + { + if (is_realtime) + tmr_adjust_thread_priority(RUN_PASS, TRUE); + if (val & TMR_CSR_XFR) /* new tir? */ + { + sim_cancel (sysd_unit[tmr]); /* stop prev */ + sysd_deactivating(tmr); /* note as inactive */ + } + if (!sim_is_active (sysd_unit[tmr])) /* not running? */ + tmr_sched (RUN_PASS, tmr, is_realtime); /* activate */ + } + else if (val & TMR_CSR_SGL) /* single step? */ + { + tmr_incr (RUN_PASS, tmr, 1, is_realtime); /* incr tmr */ + if (tmr_tir[tmr] == 0) /* if ovflo, */ + tmr_tir[tmr] = tmr_tnir[tmr]; /* reload tir */ + } + if ((tmr_csr[tmr] & (TMR_CSR_DON | TMR_CSR_IE)) != /* update int */ + (TMR_CSR_DON | TMR_CSR_IE)) + { + if (tmr) + CLR_INT (TMR1); + else + CLR_INT (TMR0); + } + + if (is_realtime) + tmr_adjust_thread_priority(RUN_PASS); +} + +/* Unit service */ + +t_stat tmr_svc (RUN_SVC_DECL, UNIT *uptr) +{ + // RUN_SVC_CHECK_CANCELLED(uptr); // not required for per-CPU devices + int32 tmr = sim_unit_index (uptr); /* get timer # */ + + uint32 sv_sysd_active_mask = cpu_unit->sysd_active_mask; + t_bool is_realtime = 0 != (sv_sysd_active_mask & (1 << tmr)); + sysd_deactivating(tmr); /* note as inactive */ + + tmr_incr (RUN_PASS, tmr, tmr_inc[tmr], is_realtime); /* incr timer */ + + if ((sv_sysd_active_mask == 0) != (cpu_unit->sysd_active_mask == 0)) + tmr_adjust_thread_priority(RUN_PASS); + + return SCPE_OK; +} + +/* + * SSC timers are used by the operating system to calibrate processor loops for various critical + * time-waits, including spinlocks timeout and device access delays. + * + * If the thread is paused during loop calibration, loop will be under-calibrated to a smaller + * number of cycles, resulting in shorter subsequent waits, and hence unstable system. + * + * To avoid this, boost current thread priority while the loop is beng calibrated, + * i.e. SSC clocks are active or about to be activated. + */ +static void tmr_adjust_thread_priority(RUN_DECL, t_bool force_high) +{ + if (force_high || tmr_is_active(RUN_PASS)) + { + /* raise thread priority */ + RUN_SCOPE_RSCX_ONLY; + cpu_set_thread_priority(RUN_RSCX_PASS, SIMH_THREAD_PRIORITY_CPU_CALIBRATION); + } + else + { + /* lower thread priority */ + cpu_reevaluate_thread_priority(RUN_PASS); + } +} + +/* Timer increment */ + +void tmr_incr (RUN_DECL, int32 tmr, uint32 inc, t_bool is_realtime) +{ + uint32 new_tir = tmr_tir[tmr] + inc; /* add incr */ + + if (new_tir < tmr_tir[tmr]) /* ovflo? */ + { + tmr_tir[tmr] = 0; /* now 0 */ + + if (tmr_csr[tmr] & TMR_CSR_DON) /* done? set err */ + tmr_csr[tmr] = tmr_csr[tmr] | TMR_CSR_ERR; + else + tmr_csr[tmr] = tmr_csr[tmr] | TMR_CSR_DON; /* set done */ + + if (tmr_csr[tmr] & TMR_CSR_STP) /* stop? */ + tmr_csr[tmr] = tmr_csr[tmr] & ~TMR_CSR_RUN; /* clr run */ + + if (tmr_csr[tmr] & TMR_CSR_RUN) /* run? */ + { + tmr_tir[tmr] = tmr_tnir[tmr]; /* reload */ + tmr_sched (RUN_PASS, tmr, is_realtime); /* reactivate */ + } + if (tmr_csr[tmr] & TMR_CSR_IE) /* set int req */ + { + if (tmr) + SET_INT (TMR1); + else + SET_INT (TMR0); + } + } + else + { + tmr_tir[tmr] = new_tir; /* no, upd tir */ + if (tmr_csr[tmr] & TMR_CSR_RUN) /* still running? */ + tmr_sched (RUN_PASS, tmr, is_realtime); /* reactivate */ + } +} + +/* Timer scheduling */ + +void tmr_sched (RUN_DECL, int32 tmr, t_bool is_realtime) +{ + int32 clk_time = sim_is_active (&clk_unit) - 1; + int32 tmr_time; + + tmr_sav[tmr] = sim_grtime (RUN_PASS); /* save intvl base */ + + if (tmr_tir[tmr] > (0xFFFFFFFFu - TMR_INC)) /* short interval? */ + { + tmr_inc[tmr] = (~tmr_tir[tmr] + 1); /* inc = interval */ + tmr_time = tmr_inc[tmr]; + } + else + { + tmr_inc[tmr] = TMR_INC; /* usec/interval */ + tmr_time = weak_read_var(tmr_poll); + } + + if (tmr_time == 0) + tmr_time = 1; + + if (tmr_inc[tmr] == TMR_INC && tmr_time > clk_time) + { + /* Align scheduled event to be identical to the event for the next clock + tick. This lets us always see a consistent calibrated value, both for + this scheduling, AND for any query of the current timer register that + may happen in tmr_tir_rd (). This presumes that sim_activate will + queue the interval timer behind the event for the clock tick. */ + + tmr_inc[tmr] = (uint32) (((double) clk_time * TMR_INC) / weak_read_var(tmr_poll)); + tmr_time = clk_time; + } + + sim_activate (sysd_unit[tmr], tmr_time); + + if (is_realtime) + sysd_activating(tmr); /* note as active */ +} + +int32 tmr0_inta (void) +{ + RUN_SCOPE; + return tmr_tivr[0]; +} + +int32 tmr1_inta (void) +{ + RUN_SCOPE; + return tmr_tivr[1]; +} + +static void tmr_csr_wr_realtime (RUN_DECL, int32 tmr, int32 val) +{ + t_bool unexpected = FALSE; + + if (tmr != 0 || val != 0 && val != (TMR_CSR_XFR | TMR_CSR_RUN)) + unexpected = TRUE; + else if ((val & TMR_CSR_RUN) && (tmr_csr[tmr] & TMR_CSR_RUN)) + unexpected = TRUE; + + if (unexpected) + { + smp_printf ("\nUnexpected request to SSC clock (tmr=%d, TCR=%08X, write=%08X)\n", tmr, tmr_csr[tmr], val); + if (sim_log) + fprintf (sim_log, "Unexpected request to SSC clock (tmr=%d, TCR=%08X, write=%08X)\n", tmr, tmr_csr[tmr], val); + ABORT_INVALID_SYSOP; + } + + if (val & TMR_CSR_XFR) + tmr_tir[tmr] = tmr_tnir[tmr]; + + if (val & TMR_CSR_RUN) + { + tmr_adjust_thread_priority(RUN_PASS, TRUE); + tmr_csr[tmr] |= TMR_CSR_RUN; + sysd_activating(tmr); + tmr_tir_rtstart[tmr] = tmr_tir[tmr]; + cpu_unit->cpu_ssc_delta_timer[tmr]->begin(RUN_PASS); + } + else + { + uint32 sv_sysd_active_mask = cpu_unit->sysd_active_mask; + tmr_csr[tmr] &= ~TMR_CSR_RUN; + sysd_deactivating(tmr); + if ((sv_sysd_active_mask == 0) != (cpu_unit->sysd_active_mask == 0)) + cpu_reevaluate_thread_priority(RUN_PASS); + } +} + +static int32 tmr_tir_rd_realtime (RUN_DECL, int32 tmr) +{ + if (tmr != 0) + { + smp_printf ("\nUnexpected request to SSC clock (tmr=%d)\n", tmr); + if (sim_log) + fprintf (sim_log, "Unexpected request to SSC clock (tmr=%d)\n", tmr); + ABORT_INVALID_SYSOP; + } + + if (tmr_csr[tmr] & TMR_CSR_RUN) + { + sim_delta_timer* dt = cpu_unit->cpu_ssc_delta_timer[tmr]; + dt->sample(RUN_PASS); + uint32 usec = dt->us_since_start(RUN_PASS); + uint32 tir = (uint32) tmr_tir_rtstart[tmr] + usec; + /* real measurments do not incur overflow */ + if (tir > (uint32) tmr_tir[tmr]) + tmr_tir[tmr] = tir; + } + + return tmr_tir[tmr]; +} + +/* Machine check */ + +int32 machine_check (RUN_DECL, int32 p1, int32 opc, int32 cc, int32 delta) +{ + int32 i, st1, st2, p2, hsir, acc; + + if (p1 & 0x80) /* mref? set v/p */ + p1 = p1 + mchk_ref; + p2 = mchk_va + 4; /* save vap */ + for (i = hsir = 0; i < 16; i++) { /* find hsir */ + if ((SISR >> i) & 1) + hsir = i; + } + st1 = ((((uint32) opc) & 0xFF) << 24) | + (hsir << 16) | + ((CADR & 0xFF) << 8) | + (MSER & 0xFF); + st2 = 0x00C07000 + (delta & 0xFF); + cc = intexc (RUN_PASS, SCB_MCHK, cc, 0, IE_SVE); /* take exception */ + acc = ACC_MASK (KERN); /* in kernel mode */ + in_ie = 1; + SP = SP - 20; /* push 5 words */ + Write (RUN_PASS, SP, 16, L_LONG, WA); /* # bytes */ + Write (RUN_PASS, SP + 4, p1, L_LONG, WA); /* mcheck type */ + Write (RUN_PASS, SP + 8, p2, L_LONG, WA); /* address */ + Write (RUN_PASS, SP + 12, st1, L_LONG, WA); /* state 1 */ + Write (RUN_PASS, SP + 16, st2, L_LONG, WA); /* state 2 */ + in_ie = 0; + return cc; +} + +t_bool smp_hlt_enb(atomic_int32* set_on) +{ + int nRunning = 0; + + /* + * MicroVAX 3900 firmware ROM is not SMP-aware, + * therefore we cannot perform halt on active multiprocessor configuration + */ + + cpu_database_lock->lock(); + + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + if (cpu_running_set.is_set(cpu_ix)) + { + if (++nRunning > 1) break; + } + } + + if (nRunning > 1) + { + cpu_database_lock->unlock(); + return FALSE; + } + + /* flag: halt/stop pending */ + if (set_on) + *set_on = 1; + + cpu_database_lock->unlock(); + + return TRUE; +} + +/* saved values of variables while interrupted into ROM console mode */ +static int32 con_sys_idle_cpu_mask_va = 0; +static int32 con_sys_critical_section_ipl = -1; +static t_bool con_sim_vsmp_active = FALSE; +static uint32 con_syncw_on = 0; +static t_bool con_use_native_interlocked = FALSE; + +/* Console entry */ +int32 con_halt (int32 code, int32 cc) +{ + int32 temp; + RUN_SCOPE; + + /* + * MicroVAX 3900 firmware ROM is not SMP-aware, so if active SMP configuration is running, + * never transfer control to ROM. + * + * Execute HALT to SimH console instead if HALT instruction was executed. + * + * Halt by HALT PIN (such as raised by Ctrl/P) is disabled if multiple processors are active, + * so it should never happen, but if ever did, just ignore it, clear hlt_pin and continue running. + */ + if (! smp_hlt_enb()) + { + if (code == CON_HLTINS) + { + ABORT (STOP_HALT); + } + else // code == CON_HLTPIN + { + /* + * Should never happen, but if it ever did just ignore and clear halt pin. + */ + hlt_pin = 0; + return PSL & CC_MASK; + } + } + + /* + * Single-CPU case only at this point, should be primary processor. + * + * Secondary can happen here only because of the bug in the simulator + * or if the operating system reassigned primary CPU. + */ + if (! cpu_unit->is_primary_cpu()) + { + smp_printf ("\nHALT on the secondary processor\n"); + if (sim_log) + fprintf (sim_log, "HALT on the secondary processor\n"); + ABORT_INVALID_SYSOP; + } + + hlt_pin = 0; + + conpc = PC; /* save PC */ + conpsl = ((PSL | cc) & 0xFFFF00FF) | CON_HLTINS; /* PSL, param */ + temp = (PSL >> PSL_V_CUR) & 0x7; /* get is'cur */ + if (temp > 4) /* invalid? */ + conpsl = conpsl | CON_BADPSL; + else + STK[temp] = SP; /* save stack */ + if (mapen) /* mapping on? */ + conpsl = conpsl | CON_MAPON; + mapen = 0; /* turn off map */ + SP = IS; /* set SP from IS */ + PSL = PSL_IS | PSL_IPL1F; /* PSL = 41F0000 */ + JUMP (ROMBASE); /* PC = 20040000 */ + + /* save paravirtualization state */ + con_sys_idle_cpu_mask_va = sys_idle_cpu_mask_va; + con_sys_critical_section_ipl = sys_critical_section_ipl; + con_sim_vsmp_active = sim_vsmp_active; + con_syncw_on = syncw.on; + con_use_native_interlocked = use_native_interlocked; + + /* reset paravirtualization state */ + cpu_on_clear_mapen(RUN_PASS); /* will also re-evaluate thread priority */ + + return 0; /* new cc = 0 */ +} + +/* + * Called when MAPEN is cleared + */ +void cpu_on_clear_mapen(RUN_DECL) +{ + /* safety check */ + if (mapen) return; + + /* local CPU is leaving OS context */ + cpu_unit->cpu_active_clk_interrupt = FALSE; + cpu_unit->cpu_active_ipi_interrupt = FALSE; + + /* after operating system shutdown or when entering console mode... */ + if (cpu_unit->is_primary_cpu() && mapen == 0) + { + /* reset the pointer to idle CPUs mask */ + sys_idle_cpu_mask_va = 0; + + /* reset critical section ipl */ + sys_critical_section_ipl = -1; + + /* disable synchronization window */ + syncw_leave_all(RUN_PASS, SYNCW_OVERRIDE_ALL | SYNCW_DISABLE_CPU); + syncw.on = 0; + + /* disable use of native interlock */ + use_native_interlocked = FALSE; + + /* reset "ready for multiprocessing" state */ + if (sim_vsmp_active) + { + sim_vsmp_active = FALSE; + if (sim_clock_thread_created) + smp_set_thread_priority(sim_clock_thread, SIMH_THREAD_PRIORITY_CLOCK); + } + } + + cpu_reevaluate_thread_priority(RUN_PASS); +} + +/* + * Called when console executes the CONTINUE command. + * + * After MTPR #1, #MT_MAPEN is called. + * Right before REI is executed. + */ +void cpu_on_rom_continue(RUN_DECL) +{ + /* restore paravirtualization state */ + sys_idle_cpu_mask_va = con_sys_idle_cpu_mask_va; + sys_critical_section_ipl = con_sys_critical_section_ipl; + sim_vsmp_active = con_sim_vsmp_active; + syncw.on = con_syncw_on; + use_native_interlocked = con_use_native_interlocked; + + con_sys_idle_cpu_mask_va = 0; + con_sys_critical_section_ipl = -1; + con_sim_vsmp_active = FALSE; + con_syncw_on = 0; + con_use_native_interlocked = FALSE; + + /* + * Could execute here + * syncw_enable_cpu(RUN_PASS); + * syncw_reeval_sys(RUN_PASS); + * to reenter syncw, however they will be executed by op_rei which is next instruction. + */ + + if (sim_vsmp_active && sim_clock_thread_created) + smp_set_thread_priority(sim_clock_thread, SIMH_THREAD_PRIORITY_CLOCK); + + /* + * Both MTPR and REI will call SET_IRQL that will reevaluate thread priority + * and also values of + * + * cpu_unit->cpu_active_clk_interrupt + * cpu_unit->cpu_active_ipi_interrupt + * + * if needed. + */ +} + +/* Bootstrap */ + +t_stat cpu_boot (int32 unitno, DEVICE *dptr) +{ + extern t_stat load_cmd (int32 flag, char *cptr); + extern SMP_FILE *sim_log; + + if (unitno != 0) + { + smp_printf ("Only primary CPU (cpu0) can boot\n"); + if (sim_log) + fprintf (sim_log, "Only primary CPU (cpu0) can boot\n"); + return SCPE_NOFNC; + } + + RUN_SCOPE; + t_stat r; + + SETPC(ROMBASE); + PSL = PSL_IS | PSL_IPL1F; + conpc = 0; + conpsl = PSL_IS | PSL_IPL1F | CON_PWRUP; + if (rom == NULL) + return SCPE_IERR; + if (*rom == 0) /* no boot? */ + { + smp_printf ("Loading boot code from ka655x.bin\n"); + if (sim_log) + fprintf (sim_log, "Loading boot code from ka655x.bin\n"); + r = load_cmd (0, "-R ka655x.bin"); + if (r != SCPE_OK) + { +#ifndef DONT_USE_INTERNAL_ROM + SMP_FILE *f; + + if (f = sim_fopen ("ka655x.bin", "wb")) + { + smp_printf ("Saving boot code to ka655x.bin\n"); + if (sim_log) + fprintf (sim_log, "Saving boot code to ka655x.bin\n"); + sim_fwrite (vax_ka655x_bin, sizeof(vax_ka655x_bin[0]), sizeof(vax_ka655x_bin)/sizeof(vax_ka655x_bin[0]), f); + fclose (f); + smp_printf ("Loading boot code from ka655x.bin\n"); + if (sim_log) + fprintf (sim_log, "Loading boot code from ka655x.bin\n"); + r = load_cmd (0, "-R ka655x.bin"); + } +#endif + return r; + } + } + sysd_powerup(RUN_PASS); + syncw_leave_all(RUN_PASS, SYNCW_OVERRIDE_ALL | SYNCW_ENABLE_CPU); + syncw.on = 0; + return SCPE_OK; +} + +/* SYSD reset */ + +t_stat sysd_reset (DEVICE *dptr) +{ + int32 i; + RUN_SCOPE; + + if (sim_switches & SWMASK ('P')) + sysd_powerup (RUN_PASS); /* powerup? */ + + for (i = 0; i < 2; i++) + { + tmr_csr[i] = tmr_tnir[i] = tmr_tir[i] = 0; + tmr_inc[i] = tmr_sav[i] = 0; + sim_cancel (sysd_unit[i]); + sysd_deactivating(i); + } + + if (cpu_unit->is_primary_cpu()) + { + AUTO_LOCK(csio_lock); + csi_csr = 0; + csi_unit.buf = 0; + sim_cancel (&csi_unit); + CLR_INT (CSI); + cso_csr = CSR_DONE; + cso_unit.buf = 0; + sim_cancel (&cso_unit); + CLR_INT (CSO); + } + + return SCPE_OK; +} + +/* SYSD powerup */ + +t_stat sysd_powerup (RUN_DECL) +{ + int32 i; + + if (cpu_unit->is_primary_cpu()) + { + // do this just once for the reset cycle across all CPUs, and only from the primary CPU + AUTO_LOCK(sysd_lock); + ssc_base = SSCBASE; + ssc_cnf = ssc_cnf & SSCCNF_BLO; + } + + for (i = 0; i < (CMCTLSIZE >> 2); i++) + { + cmctl_reg[i] = 0; + } + + for (i = 0; i < 2; i++) + { + tmr_tivr[i] = 0; + ssc_adsm[i] = ssc_adsk[i] = 0; + } + + ka_cacr = 0; + ssc_bto = 0; + ssc_otp = 0; + + return SCPE_OK; +} + +/* + * Set up secondary CPU's model-specific registers before that CPU is started. + * + * cpu_unit points to target secondary CPU. + * local_cpu is the CPU that is setting up the secondary for starting it. + */ +void cpu_setup_secondary_model_specific(RUN_DECL, CPU_UNIT* local_cpu) +{ + /* clone cache control registers from the local VCPU */ + CADR = local_cpu->cpu_context.r_CADR; + ka_cacr = local_cpu->cpu_context.r_ka_cacr; + + /* clone SSC BTO setting from the local VCPU */ + ssc_bto = local_cpu->cpu_context.r_ssc_bto & SSCBTO_INTV; + + /* clone settings of SSC timer interrupt vectors from the local VCPU */ + tmr_tivr[0] = local_cpu->cpu_context.r_tmr_tivr[0]; + tmr_tivr[1] = local_cpu->cpu_context.r_tmr_tivr[1]; + + /* clone CMCTL registers */ + memcpy(cmctl_reg, local_cpu->cpu_context.r_cmctl_reg, sizeof(cmctl_reg)); +} \ No newline at end of file diff --git a/src/VAX/vax_syslist.cpp b/src/VAX/vax_syslist.cpp new file mode 100644 index 0000000..e3c916b --- /dev/null +++ b/src/VAX/vax_syslist.cpp @@ -0,0 +1,133 @@ +/* vax_syslist.c: VAX simulator interface + + Copyright (c) 1998-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. + + 17-Oct-06 RMS Re-ordered device list + 17-May-06 RMS Added CR11/CD11 support (from John Dundas) + 01-Oct-2004 RMS Cloned from vax_sys.c +*/ + +#include "sim_defs.h" +#include "vax_defs.h" + +char sim_name[] = "VAX MP"; + +extern int32 sim_switches; +extern void WriteB (RUN_DECL, uint32 pa, int32 val); +extern void rom_wr_B (RUN_DECL, int32 pa, int32 val); + +/* when adding extra QBus device to sim_devices, + be sure to also include it in the Qbus interrupt reset list + in qba_reset */ +DEVICE *sim_devices[] = +{ + &cpu_dev, + &tlb_dev, + &rom_dev, + &nvr_dev, + &sysd_dev, + &qba_dev, + &clk_dev, + &tti_dev, + &tto_dev, + &csi_dev, + &cso_dev, + &dz_dev, + &vh_dev, + &cr_dev, + &lpt_dev, + &rl_dev, + &rq_dev, + &rqb_dev, + &rqc_dev, + &rqd_dev, + &ry_dev, + &ts_dev, + &tq_dev, + &xq_dev, + &xqb_dev, + NULL +}; + +int sim_device_index (DEVICE* dptr) +{ + for (int k = 0; ; k++) + { + if (sim_devices[k] == NULL) + panic("Unable to find requested device in sim_dev_index"); + if (sim_devices[k] == dptr) + return k; + } +} + +/* Binary loader + + The binary loader handles absolute system images, that is, system + images linked /SYSTEM. These are simply a byte stream, with no + origin or relocation information. + + -r load ROM + -n load NVR + -o for memory, specify origin +*/ + +t_stat sim_load (RUN_DECL, SMP_FILE *fileref, char *cptr, char *fnam, int flag) +{ +t_stat r; +int32 i; +uint32 origin, limit; +extern int32 ssc_cnf; +#define SSCCNF_BLO 0x80000000 + +if (flag) /* dump? */ + return SCPE_ARG; +if (sim_switches & SWMASK ('R')) { /* ROM? */ + origin = ROMBASE; + limit = ROMBASE + ROMSIZE; + } +else if (sim_switches & SWMASK ('N')) { /* NVR? */ + origin = NVRBASE; + limit = NVRBASE + NVRSIZE; + ssc_cnf = ssc_cnf & ~SSCCNF_BLO; + } +else { + origin = 0; /* memory */ + limit = (uint32) cpu_unit->capac; + if (sim_switches & SWMASK ('O')) { /* origin? */ + origin = (int32) get_uint (cptr, 16, 0xFFFFFFFF, &r); + if (r != SCPE_OK) + return SCPE_ARG; + } + } +while ((i = getc (fileref)) != EOF) { /* read byte stream */ + if (origin >= limit) /* NXM? */ + return SCPE_NXM; + if (sim_switches & SWMASK ('R')) /* ROM? */ + rom_wr_B (RUN_PASS, origin, i); /* not writeable */ + else WriteB (RUN_PASS, origin, i); /* store byte */ + origin = origin + 1; + } +return SCPE_OK; +} + diff --git a/src/VAX/vaxmod_defs.h b/src/VAX/vaxmod_defs.h new file mode 100644 index 0000000..b36c94c --- /dev/null +++ b/src/VAX/vaxmod_defs.h @@ -0,0 +1,601 @@ +/* vaxmod_defs.h: VAX model-specific definitions file + + 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"), + 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. + + + 11-Dec-11 RMS Moved all Qbus devices to BR4; deleted RP definitions + 25-Nov-11 RMS Added VEC_QBUS definition + 29-Apr-07 RMS Separated checks for PxBR and SBR + 17-May-06 RMS Added CR11/CD11 support + 10-May-06 RMS Added NOP'd reserved operand checking macros + 05-Oct-05 RMS Added XU definitions for autoconfigure + 15-Jun-05 RMS Added QDSS support + 12-Sep-04 RMS Removed map_address prototype + 16-Jun-04 RMS Added DHQ11 support + 21-Mar-04 RMS Added RXV21 support + 25-Jan-04 RMS Removed local debug logging support + RMS,MP Added "KA655X" support + 29-Dec-03 RMS Added Q18 definition for PDP11 compatibility + 22-Dec-02 RMS Added BDR halt enable definition + 11-Nov-02 RMS Added log bits for XQ + 10-Oct-02 RMS Added DEQNA/DELQA, multiple RQ, autoconfigure support + 29-Sep-02 RMS Revamped bus support macros + 06-Sep-02 RMS Added TMSCP support + 14-Jul-02 RMS Added additional console halt codes + 28-Apr-02 RMS Fixed DZV vector base and number of lines + + This file covers the KA65x ("Mayfair") series of CVAX-based Qbus systems. + The simulator defines an extended physical memory variant of the KA655, + called the KA655X. It has a maximum memory size of 512MB instead of 64MB. + + System memory map + + 0000 0000 - 03FF FFFF main memory (KA655) + 0400 0000 - 0FFF FFFF reserved (KA655), main memory (KA655X) + 1000 0000 - 13FF FFFF cache diagnostic space (KA655), main memory (KA655X) + 1400 0000 - 1FFF FFFF reserved (KA655), main memory (KA655X) + + 2000 0000 - 2000 1FFF Qbus I/O page + 2000 2000 - 2003 FFFF reserved + 2004 0000 - 2005 FFFF ROM space, halt protected + 2006 0000 - 2007 FFFF ROM space, halt unprotected + 2008 0000 - 201F FFFF Local register space + 2020 0000 - 2FFF FFFF reserved + 3000 0000 - 303F FFFF Qbus memory space + 3400 0000 - 3FFF FFFF reserved +*/ + +// #pragma message ("Loading vaxmod_defs.h") + +#ifdef FULL_VAX /* subset VAX */ +#undef FULL_VAX +#endif + +#ifndef _VAXMOD_DEFS_H_ +#define _VAXMOD_DEFS_H_ 1 + +/* Microcode constructs */ + +#define CVAX_SID (10 << 24) /* system ID */ +#define CVAX_UREV 6 /* ucode revision */ +#define CON_HLTPIN 0x0200 /* external CPU halt */ +#define CON_PWRUP 0x0300 /* powerup code */ +#define CON_HLTINS 0x0600 /* HALT instruction */ +#define CON_BADPSL 0x4000 /* invalid PSL flag */ +#define CON_MAPON 0x8000 /* mapping on flag */ +#define MCHK_TBM_P0 0x05 /* PPTE in P0 */ +#define MCHK_TBM_P1 0x06 /* PPTE in P1 */ +#define MCHK_M0_P0 0x07 /* PPTE in P0 */ +#define MCHK_M0_P1 0x08 /* PPTE in P1 */ +#define MCHK_INTIPL 0x09 /* invalid ireq */ +#define MCHK_READ 0x80 /* read check */ +#define MCHK_WRITE 0x82 /* write check */ + +/* SCB size */ + +#define SCB_SIZE (0x200 * 2) /* 2 pages for QBus MicroVAXen */ + +/* Machine specific IPRs */ + +#define MT_CADR 37 +#define MT_MSER 39 +#define MT_CONPC 42 +#define MT_CONPSL 43 +#define MT_IORESET 55 + +/* Memory system error register */ + +#define MSER_HM 0x80 /* hit/miss */ +#define MSER_CPE 0x40 /* CDAL par err */ +#define MSER_CPM 0x20 /* CDAL mchk */ + +/* Cache disable register */ + +#define CADR_RW 0xF3 +#define CADR_MBO 0x0C + +/* Memory */ + +#define MAXMEMWIDTH 26 /* max mem, std KA655 */ +#define MAXMEMSIZE (1 << MAXMEMWIDTH) /* max mem size */ +#define MAXMEMWIDTH_X 29 /* max mem, KA655X */ +#define MAXMEMSIZE_X (1 << MAXMEMWIDTH_X) +#define INITMEMSIZE (1 << 24) /* initial memory size */ +#define MEMSIZE ((uint32) (cpu_unit->capac)) /* cast it to uint32, to enable compiler optimization, + even when "capac" is int64 (USE_ADDR64/USE_INT64) */ +#define ADDR_IS_MEM(x) likely(((uint32) (x)) < MEMSIZE) + +/* Cache diagnostic space */ + +#define CDAAWIDTH 16 /* cache dat addr width */ +#define CDASIZE (1u << CDAAWIDTH) /* cache dat length */ +#define CDAMASK (CDASIZE - 1) /* cache dat mask */ +#define CTGAWIDTH 10 /* cache tag addr width */ +#define CTGSIZE (1u << CTGAWIDTH) /* cache tag length */ +#define CTGMASK (CTGSIZE - 1) /* cache tag mask */ +#define CDGSIZE (CDASIZE * CTGSIZE) /* diag addr length */ +#define CDGBASE 0x10000000 /* diag addr base */ +#define CDG_GETROW(x) (((x) & CDAMASK) >> 2) +#define CDG_GETTAG(x) (((x) >> CDAAWIDTH) & CTGMASK) +#define CTG_V (1u << (CTGAWIDTH + 0)) /* tag valid */ +#define CTG_WP (1u << (CTGAWIDTH + 1)) /* wrong parity */ +#define ADDR_IS_CDG(x) ((((uint32) (x)) >= CDGBASE) && \ + (((uint32) (x)) < (CDGBASE + CDGSIZE))) + +/* Qbus I/O registers */ + +#define IOPAGEAWIDTH 13 /* IO addr width */ +#define IOPAGESIZE (1u << IOPAGEAWIDTH) /* IO page length */ +#define IOPAGEMASK (IOPAGESIZE - 1) /* IO addr mask */ +#define IOPAGEBASE 0x20000000 /* IO page base */ +#define ADDR_IS_IO(x) ((((uint32) (x)) >= IOPAGEBASE) && \ + (((uint32) (x)) < (IOPAGEBASE + IOPAGESIZE))) + +/* Read only memory - appears twice */ + +#define ROMAWIDTH 17 /* ROM addr width */ +#define ROMSIZE (1u << ROMAWIDTH) /* ROM length */ +#define ROMAMASK (ROMSIZE - 1) /* ROM addr mask */ +#define ROMBASE 0x20040000 /* ROM base */ +#define ADDR_IS_ROM(x) ((((uint32) (x)) >= ROMBASE) && \ + (((uint32) (x)) < (ROMBASE + ROMSIZE + ROMSIZE))) +#define ROM_PC_CONTINUE_MAPEN (ROMBASE + 0x393) /* MTPR MAPEN instruction for ROM CONTINUE command */ +#define ROM_PC_CONTINUE_REI (ROMBASE + 0x394) /* MTPR REI instruction for ROM CONTINUE command */ +#define ROM_PC_TEST31_TIR1_RD_A (ROMBASE + 0xE9C5) /* ROM Test 31 instructon to read TIR1 1st time */ +#define ROM_PC_TEST31_TIR1_RD_B (ROMBASE + 0xE9DD) /* ROM Test 31 instructon to read TIR1 2nd time */ +#define ROM_PC_CHAR_PROMPT (ROMBASE + 0x361B) /* Boot ROM Character Prompt */ + +/* Local register space */ + +#define REGAWIDTH 19 /* REG addr width */ +#define REGSIZE (1u << REGAWIDTH) /* REG length */ +#define REGBASE 0x20080000 /* REG addr base */ + +/* KA655 board registers */ + +#define KAAWIDTH 3 /* KA reg width */ +#define KASIZE (1u << KAAWIDTH) /* KA reg length */ +#define KABASE (REGBASE + 0x4000) /* KA650 addr base */ + +/* CQBIC registers */ + +#define CQBICSIZE (5 << 2) /* 5 registers */ +#define CQBICBASE (REGBASE) /* CQBIC addr base */ +#define CQMAPASIZE 15 /* map addr width */ +#define CQMAPSIZE (1u << CQMAPASIZE) /* map length */ +#define CQMAPAMASK (CQMAPSIZE - 1) /* map addr mask */ +#define CQMAPBASE (REGBASE + 0x8000) /* map addr base */ +#define CQIPCSIZE 2 /* 2 bytes only */ +#define CQIPCBASE (REGBASE + 0x1F40) /* ipc reg addr */ + +/* CMCTL registers */ + +// #define CMCTLSIZE (18 << 2) /* 18 registers */ +#define CMCTLSIZE (19 << 2) /* KA655X extra reg */ +#define CMCTLBASE (REGBASE + 0x100) /* CMCTL addr base */ + +/* SSC registers */ + +#define SSCSIZE 0x150 /* SSC size */ +#define SSCBASE 0x20140000 /* SSC base */ + +/* Non-volatile RAM - 1KB long */ + +#define NVRAWIDTH 10 /* NVR addr width */ +#define NVRSIZE (1u << NVRAWIDTH) /* NVR length */ +#define NVRAMASK (NVRSIZE - 1) /* NVR addr mask */ +#define NVRBASE 0x20140400 /* NVR base */ +#define ADDR_IS_NVR(x) ((((uint32) (x)) >= NVRBASE) && \ + (((uint32) (x)) < (NVRBASE + NVRSIZE))) + +/* CQBIC Qbus memory space (seen from CVAX) */ + +#define CQMAWIDTH 22 /* Qmem addr width */ +#define CQMSIZE (1u << CQMAWIDTH) /* Qmem length */ +#define CQMAMASK (CQMSIZE - 1) /* Qmem addr mask */ +#define CQMBASE 0x30000000 /* Qmem base */ + +/* Machine specific reserved operand tests (all NOPs) */ + +#define ML_PA_TEST(r) +#define ML_LR_TEST(r) +#define ML_SBR_TEST(r) +#define ML_PXBR_TEST(r) +#define LP_AST_TEST(r) +#define LP_MBZ84_TEST(r) +#define LP_MBZ92_TEST(r) + +/* Qbus I/O modes */ + +#define READ 0 /* PDP-11 compatibility */ +#define WRITE (L_WORD) +#define WRITEB (L_BYTE) + +/* Common CSI flags */ + +#define CSR_V_GO 0 /* go */ +#define CSR_V_IE 6 /* interrupt enable */ +#define CSR_V_DONE 7 /* done */ +#define CSR_V_BUSY 11 /* busy */ +#define CSR_V_ERR 15 /* error */ +#define CSR_GO (1u << CSR_V_GO) +#define CSR_IE (1u << CSR_V_IE) +#define CSR_DONE (1u << CSR_V_DONE) +#define CSR_BUSY (1u << CSR_V_BUSY) +#define CSR_ERR (1u << CSR_V_ERR) + +/* Timers */ + +#define TMR_CLK 0 /* 100Hz clock */ + +/* I/O system definitions */ + +#define DZ_MUXES 4 /* max # of DZV muxes */ +#define DZ_LINES 4 /* lines per DZV mux */ +#define VH_MUXES 4 /* max # of DHQ muxes */ +#define DLX_LINES 16 /* max # of KL11/DL11's */ +#define DCX_LINES 16 /* max # of DC11's */ +#define MT_MAXFR (1 << 16) /* magtape max rec */ +#define AUTO_LNT 34 /* autoconfig ranks */ + +#define DEV_V_UBUS (DEV_V_UF + 0) /* Unibus */ +#define DEV_V_QBUS (DEV_V_UF + 1) /* Qbus */ +#define DEV_V_Q18 (DEV_V_UF + 2) /* Qbus, mem <= 256KB */ +#define DEV_V_FLTA (DEV_V_UF + 3) /* flt addr */ +#define DEV_UBUS (1u << DEV_V_UBUS) +#define DEV_QBUS (1u << DEV_V_QBUS) +#define DEV_Q18 (1u << DEV_V_Q18) +#define DEV_FLTA (1u << DEV_V_FLTA) + +#define UNIBUS FALSE /* 22b only */ + +#define DEV_RDX 16 /* default device radix */ + +/* Device information block */ + +#define VEC_DEVMAX 4 /* max device vec */ + +typedef struct { + uint32 ba; /* base addr */ + uint32 lnt; /* length */ + t_stat (*rd)(int32 *dat, int32 ad, int32 md); + t_stat (*wr)(int32 dat, int32 ad, int32 md); + int32 vnum; /* vectors: number */ + int32 vloc; /* locator */ + atomic_int32 vec; /* value */ + int32 (*ack[VEC_DEVMAX])(void); /* ack routine */ + } DIB; + +/* I/O page layout - RQB,RQC,RQD float based on number of DZ's */ + +#define IOBA_DZ (IOPAGEBASE + 000100) /* DZ11 */ +#define IOLN_DZ 010 +#define IOBA_RQB (IOPAGEBASE + 000334 + (020 * (DZ_MUXES / 2))) +#define IOLN_RQB 004 +#define IOBA_RQC (IOPAGEBASE + IOBA_RQB + IOLN_RQB) +#define IOLN_RQC 004 +#define IOBA_RQD (IOPAGEBASE + IOBA_RQC + IOLN_RQC) +#define IOLN_RQD 004 +#define IOBA_VH (IOPAGEBASE + 000440) /* DHQ11 */ +#define IOLN_VH 020 +#define IOBA_RQ (IOPAGEBASE + 012150) /* RQDX3 */ +#define IOLN_RQ 004 +#define IOBA_TS (IOPAGEBASE + 012520) /* TS11 */ +#define IOLN_TS 004 +#define IOBA_RL (IOPAGEBASE + 014400) /* RL11 */ +#define IOLN_RL 012 +#define IOBA_XQ (IOPAGEBASE + 014440) /* DEQNA/DELQA */ +#define IOLN_XQ 020 +#define IOBA_XQB (IOPAGEBASE + 014460) /* 2nd DEQNA/DELQA */ +#define IOLN_XQB 020 +#define IOBA_TQ (IOPAGEBASE + 014500) /* TMSCP */ +#define IOLN_TQ 004 +#define IOBA_XU (IOPAGEBASE + 014510) /* DEUNA/DELUA */ +#define IOLN_XU 010 +#define IOBA_CR (IOPAGEBASE + 017160) /* CD/CR/CM */ +#define IOLN_CR 010 +#define IOBA_RX (IOPAGEBASE + 017170) /* RXV11 */ +#define IOLN_RX 004 +#define IOBA_RY (IOPAGEBASE + 017170) /* RXV21 */ +#define IOLN_RY 004 +#define IOBA_QDSS (IOPAGEBASE + 017400) /* QDSS */ +#define IOLN_QDSS 002 +#define IOBA_DBL (IOPAGEBASE + 017500) /* doorbell */ +#define IOLN_DBL 002 +#define IOBA_LPT (IOPAGEBASE + 017514) /* LP11 */ +#define IOLN_LPT 004 +#define IOBA_PTR (IOPAGEBASE + 017550) /* PC11 reader */ +#define IOLN_PTR 004 +#define IOBA_PTP (IOPAGEBASE + 017554) /* PC11 punch */ +#define IOLN_PTP 004 + +/* + * The KA65x maintains 4 separate hardware IPL levels, IPL 17 to IPL 14 + * however, DEC Qbus controllers all interrupt on IPL 14 + * Within each IPL, priority is right to left + */ + +#if defined(VM_VAX_MP) +/* + * === IMPORTANT !!! === + * + * Current VAX MP multi-tick idle sleep code assumes that CLK is the highest-priority async interrupt. + + * When multiple CLK interrupts are delivered back-to-back after multi-tick idle sleep, current VAX MP code + * relies on this assumption to deliver multiple CLK interrupts in a row (signalling next CLK interrupt + * right after the previous one is dismissed via REI) before any other async interrupt can be delivered. + * + * The code, as currently implemented, performs back-to-back sequnce without blocking non-CLK interrupts + * during the sequence. It is important that once back-to-back sequence is started, no other async interrupt + * deliverable to VAX code intervenes in it. Otherwise OS ISR for this other interrupt may set up a timer-based + * timeout event that will be signalled expired before some (e.g. fork-level) processing associated with it + * may have a chance to take place, likely with disastorous and certainly incorrect consequences. + * + * Right now the protection against this scenario is attained by the mere fact that CLK is the highest + * priority async interrupt and no other async interrupt can intervene in the sequence before all + * pending CLK interrupts in the whole back-to-back are processed. + * + * Should an async interrupt source be defined with a priority above CLK, back-to-back code should be revised + * to block the processing of IRQ's from non-CLK devices till back-to-back CLK sequence completes. + * + * The only exceptions are IPIRMB, SECEXIT, SYNCWSYS, SYNCLK, ASYNC_IO and STOP that are not delivered to VAX code. + * + * IPIRMB should have highest precedence of all interrupts, followed by SECEXIT followed by SYNCWSYS, SYNCLK, ASYNC_IO and STOP. + */ +#endif + + +/* IPL 17 */ + +#define INT_V_IPIRMB 0 /* special non-maskable interprocessor interrupt: + not delivered to VAX code, only processed internally by SIMH + and is dismissed internally by SIMH, causes target + virtual processor to execute immediate read memory barrier */ + +#define INT_V_SECEXIT 1 /* special non-maskable interprocessor interrupt: + not delivered to VAX code, only processed internally by SIMH + and is dismissed internally by SIMH, sent by a secondary processor + to the primary when secondary is being stopped to cause primary + to transfer pending clock queue event entries for system-wide devices + from the secondary to the primary and perform other cleanup */ + +#define INT_V_SYNCWSYS 2 /* special non-maskable interrupt sent by one VCPU to another + when former enters the latter into SYNCW_SYS synchronization + window because it notices the latter VCPU has system-critical + interrupts pending but is not in SYNCW_SYS yet because it did not + process those interrupts yet, perhaps because the thread is depressed; + SYNCWSYS is not delivered to VAX code, only processed internally by SIMH + and is dismissed internally by SIMH */ + +#define INT_V_SYNCLK 3 /* special non-maskable interrupt sent by clock strobe thread to VCPUs: + not delivered to VAX code, only processed internally by SIMH + and is dismissed internally by SIMH */ + +#define INT_V_ASYNC_IO 4 /* special non-maskable interrupt sent by IOP thread to the primary VCPU + when an event is posted on asynch IO completion queue: + not delivered to VAX code, only processed internally by SIMH + and is dismissed internally by SIMH */ + +#define INT_V_STOP 5 /* special non-maskable interrupt sent by the primary CPU to the + secondary CPUs causing them to perform emergency stop */ + +/* IPL 16 */ + +#define INT_V_CLK 0 /* clock (keep CLK above all other external interrupts !!!, + except for special internal SIMH interrupts) */ +#define INT_V_IPINTR 1 /* interprocessor interrupt: + should have higher numeric value (lower delivery priority) + than CLK interrupt */ +/* IPL 15 */ + +/* IPL 14 - devices through RY are IPL 15 on Unibus systems */ + +#define INT_V_RQ 0 /* RQDX3 */ +#define INT_V_RL 1 /* RLV12/RL02 */ +#define INT_V_DZRX 2 /* DZ11 */ +#define INT_V_DZTX 3 +#define INT_V_TS 4 /* TS11/TSV05 */ +#define INT_V_TQ 5 /* TMSCP */ +#define INT_V_XQ 6 /* DEQNA/DELQA */ +#define INT_V_RY 7 /* RXV21 */ + +#define INT_V_TTI 8 /* console */ +#define INT_V_TTO 9 +#define INT_V_PTR 10 /* PC11 */ +#define INT_V_PTP 11 +#define INT_V_LPT 12 /* LP11 */ +#define INT_V_CSI 13 /* SSC cons UART */ +#define INT_V_CSO 14 +#define INT_V_TMR0 15 /* SSC timers */ +#define INT_V_TMR1 16 +#define INT_V_VHRX 17 /* DHQ11 */ +#define INT_V_VHTX 18 +#define INT_V_QDSS 19 /* QDSS */ +#define INT_V_CR 20 + +#define INT_IPIRMB (1u << INT_V_IPIRMB) +#define INT_SECEXIT (1u << INT_V_SECEXIT) +#define INT_SYNCWSYS (1u << INT_V_SYNCWSYS) +#define INT_ASYNC_IO (1u << INT_V_ASYNC_IO) +#define INT_STOP (1u << INT_V_STOP) +#define INT_CLK (1u << INT_V_CLK) +#define INT_IPINTR (1u << INT_V_IPINTR) +#define INT_RQ (1u << INT_V_RQ) +#define INT_RL (1u << INT_V_RL) +#define INT_DZRX (1u << INT_V_DZRX) +#define INT_DZTX (1u << INT_V_DZTX) +#define INT_TS (1u << INT_V_TS) +#define INT_TQ (1u << INT_V_TQ) +#define INT_XQ (1u << INT_V_XQ) +#define INT_RY (1u << INT_V_RY) +#define INT_TTI (1u << INT_V_TTI) +#define INT_TTO (1u << INT_V_TTO) +#define INT_PTR (1u << INT_V_PTR) +#define INT_PTP (1u << INT_V_PTP) +#define INT_LPT (1u << INT_V_LPT) +#define INT_CSI (1u << INT_V_CSI) +#define INT_CSO (1u << INT_V_CSO) +#define INT_TMR0 (1u << INT_V_TMR0) +#define INT_TMR1 (1u << INT_V_TMR1) +#define INT_VHRX (1u << INT_V_VHRX) +#define INT_VHTX (1u << INT_V_VHTX) +#define INT_QDSS (1u << INT_V_QDSS) +#define INT_CR (1u << INT_V_CR) + +#define IPL_IPIRMB (0x17 - IPL_HMIN) +#define IPL_SECEXIT (0x17 - IPL_HMIN) +#define IPL_SYNCWSYS (0x17 - IPL_HMIN) +#define IPL_SYNCLK (0x17 - IPL_HMIN) +#define IPL_ASYNC_IO (0x17 - IPL_HMIN) +#define IPL_STOP (0x17 - IPL_HMIN) +#define IPL_CLK (0x16 - IPL_HMIN) /* relative IPL */ +#define IPL_IPINTR (0x16 - IPL_HMIN) +#define IPL_RQ (0x14 - IPL_HMIN) +#define IPL_RL (0x14 - IPL_HMIN) +#define IPL_DZRX (0x14 - IPL_HMIN) +#define IPL_DZTX (0x14 - IPL_HMIN) +#define IPL_TS (0x14 - IPL_HMIN) +#define IPL_TQ (0x14 - IPL_HMIN) +#define IPL_XQ (0x14 - IPL_HMIN) +#define IPL_RY (0x14 - IPL_HMIN) +#define IPL_TTI (0x14 - IPL_HMIN) +#define IPL_TTO (0x14 - IPL_HMIN) +#define IPL_PTR (0x14 - IPL_HMIN) +#define IPL_PTP (0x14 - IPL_HMIN) +#define IPL_LPT (0x14 - IPL_HMIN) +#define IPL_CSI (0x14 - IPL_HMIN) +#define IPL_CSO (0x14 - IPL_HMIN) +#define IPL_TMR0 (0x14 - IPL_HMIN) +#define IPL_TMR1 (0x14 - IPL_HMIN) +#define IPL_VHRX (0x14 - IPL_HMIN) +#define IPL_VHTX (0x14 - IPL_HMIN) +#define IPL_QDSS (0x14 - IPL_HMIN) +#define IPL_CR (0x14 - IPL_HMIN) + +#define IPL_ABS_IPIRMB (IPL_IPIRMB + IPL_HMIN) +#define IPL_ABS_SECEXIT (IPL_SECEXIT + IPL_HMIN) +#define IPL_ABS_SYNCWSYS (IPL_SYNCWSYS + IPL_HMIN) +#define IPL_ABS_SYNCLK (IPL_SYNCLK + IPL_HMIN) +#define IPL_ABS_ASYNC_IO (IPL_ASYNC_IO + IPL_HMIN) +#define IPL_ABS_STOP (IPL_STOP + IPL_HMIN) +#define IPL_ABS_CLK (IPL_CLK + IPL_HMIN) +#define IPL_ABS_IPINTR (IPL_IPINTR + IPL_HMIN) + +#define IPL_HMAX 0x17 /* highest hwre level */ +#define IPL_HMIN 0x14 /* lowest hwre level */ +#define IPL_HLVL (IPL_HMAX - IPL_HMIN + 1) /* # hardware levels */ +#define IPL_SMAX 0xF /* highest swre level */ + +/* Device vectors */ + +#define VAX_QBUS 1 /* QBus based machine */ + +#define VEC_Q 0x200 /* Qbus vector offset */ + +#define VEC_PTR (VEC_Q + 0070) +#define VEC_PTP (VEC_Q + 0074) +#define VEC_XQ (VEC_Q + 0120) +#define VEC_XU (VEC_Q + 0120) +#define VEC_RQ (VEC_Q + 0154) +#define VEC_RL (VEC_Q + 0160) +#define VEC_LPT (VEC_Q + 0200) +#define VEC_TS (VEC_Q + 0224) +#define VEC_CR (VEC_Q + 0230) +#define VEC_TQ (VEC_Q + 0260) +#define VEC_RX (VEC_Q + 0264) +#define VEC_RY (VEC_Q + 0264) +#define VEC_DZRX (VEC_Q + 0300) +#define VEC_DZTX (VEC_Q + 0304) +#define VEC_VHRX (VEC_Q + 0310) +#define VEC_VHTX (VEC_Q + 0314) + +/* Interrupt macros and functions */ + +#define IVCL(dv) ((IPL_##dv * 32) + INT_V_##dv) +#define IVCL_IPL(ivcl) ((ivcl) >> 5) +#define IVCL_DEV(ivcl) ((ivcl) & 31) +#define SET_INT(dv) interrupt_set_int(IPL_##dv, INT_V_##dv) +#define CLR_INT(dv) interrupt_clear_int(IPL_##dv, INT_V_##dv) +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* cond error return */ + +void interrupt_set_int(uint32 ix_ipl, uint32 dev, t_bool force_wakeup = FALSE); +void interrupt_set_int(CPU_UNIT* xcpu, uint32 ix_ipl, uint32 dev, t_bool force_wakeup = FALSE); +void interrupt_clear_int(uint32 ipl, uint32 dev); +void interrupt_ipi(RUN_DECL, CPU_UNIT* xcpu, t_bool force_wakeup = FALSE); +void interrupt_ipi_rmb(RUN_DECL, CPU_UNIT* xcpu); + +/* Logging */ + +#define LOG_CPU_I 0x1 /* intexc */ +#define LOG_CPU_R 0x2 /* REI */ +#define LOG_CPU_P 0x4 /* context */ + +/* Function prototypes for virtual memory interface */ + +int32 Read (RUN_DECL, uint32 va, int32 lnt, int32 acc); +void Write (RUN_DECL, uint32 va, int32 val, int32 lnt, int32 acc); + +/* Function prototypes for I/O */ + +int32 Map_ReadB (RUN_DECL, uint32 ba, int32 bc, uint8 *buf); +int32 Map_ReadW (RUN_DECL, uint32 ba, int32 bc, uint16 *buf); +int32 Map_WriteB (RUN_DECL, uint32 ba, int32 bc, uint8 *buf); +int32 Map_WriteW (RUN_DECL, uint32 ba, int32 bc, uint16 *buf); + +int32 synclk_expected_next(RUN_DECL); +void cqbic_reset_percpu(RUN_DECL, t_bool powerup); +t_bool tti_rcv_char(int32 c); +void tti_clear_pending_typeahead(); +t_bool smp_hlt_enb(atomic_int32* set_on = NULL); +void cpu_on_clear_mapen(RUN_DECL); +void cpu_on_rom_continue(RUN_DECL); +void cpu_on_changed_ipl(RUN_DECL, int32 oldpsl, int32 flags); +void cpu_reevaluate_thread_priority(RUN_DECL, t_bool synced = FALSE); +t_bool check_synclk_pending(RUN_DECL); +void process_synclk(RUN_DECL, t_bool clk_ie); +t_bool is_os_running(RUN_DECL); +void cpu_scb_written(int32 pa); +void read_irqs_to_local(RUN_DECL); +t_stat clk_svc_ex (RUN_DECL, t_bool clk_ie); + +/* mask for quick-checking whether physcial address might fall into SCB */ +#define SCB_RANGE_PAMASK (~(SCB_SIZE * 4 - 1)) +#define PA_MAY_BE_INSIDE_SCB(pa) (((pa) & SCB_RANGE_PAMASK) == cpu_unit->cpu_context.scb_range_pamask) + +/* flags for cpu_on_changed_ipl */ +#define CHIPL_NO_THRDPRIO (1 << 0) +#define CHIPL_NO_SYNCW (1 << 1) + +#include "pdp11_io_lib.h" + +extern int32 clk_tps; +extern atomic_int32_var tmr_poll; +extern atomic_int32_var tmxr_poll; +extern atomic_int32 hlt_pin; +extern int32 sys_idle_cpu_mask_va; +extern int32 sys_critical_section_ipl; +extern uint32 synclk_safe_cycles; +extern int32 synclk_safe_ipl; + +#endif diff --git a/src/VAX/vmb.exe b/src/VAX/vmb.exe new file mode 100644 index 0000000..5f7cb8f Binary files /dev/null and b/src/VAX/vmb.exe differ diff --git a/src/VAX_MP/VAX_MP.sln b/src/VAX_MP/VAX_MP.sln new file mode 100644 index 0000000..01fa3b6 --- /dev/null +++ b/src/VAX_MP/VAX_MP.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VAX_MP", "VAX_MP.vcproj", "{C22FDFBC-0F56-4CAB-8476-99A43EEC1A8F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C22FDFBC-0F56-4CAB-8476-99A43EEC1A8F}.Debug|Win32.ActiveCfg = Debug|Win32 + {C22FDFBC-0F56-4CAB-8476-99A43EEC1A8F}.Debug|Win32.Build.0 = Debug|Win32 + {C22FDFBC-0F56-4CAB-8476-99A43EEC1A8F}.Debug|x64.ActiveCfg = Debug|x64 + {C22FDFBC-0F56-4CAB-8476-99A43EEC1A8F}.Debug|x64.Build.0 = Debug|x64 + {C22FDFBC-0F56-4CAB-8476-99A43EEC1A8F}.Release|Win32.ActiveCfg = Release|Win32 + {C22FDFBC-0F56-4CAB-8476-99A43EEC1A8F}.Release|Win32.Build.0 = Release|Win32 + {C22FDFBC-0F56-4CAB-8476-99A43EEC1A8F}.Release|x64.ActiveCfg = Release|x64 + {C22FDFBC-0F56-4CAB-8476-99A43EEC1A8F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/VAX_MP/VAX_MP.vcproj b/src/VAX_MP/VAX_MP.vcproj new file mode 100644 index 0000000..9a2972c --- /dev/null +++ b/src/VAX_MP/VAX_MP.vcproj @@ -0,0 +1,645 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/VAX_MP/docs/aux_docs/DELQA.pdf b/src/VAX_MP/docs/aux_docs/DELQA.pdf new file mode 100644 index 0000000..3bd5d3d Binary files /dev/null and b/src/VAX_MP/docs/aux_docs/DELQA.pdf differ diff --git a/src/VAX_MP/docs/aux_docs/DEQNA.pdf b/src/VAX_MP/docs/aux_docs/DEQNA.pdf new file mode 100644 index 0000000..2be72fd Binary files /dev/null and b/src/VAX_MP/docs/aux_docs/DEQNA.pdf differ diff --git a/src/VAX_MP/docs/aux_docs/KA650.pdf b/src/VAX_MP/docs/aux_docs/KA650.pdf new file mode 100644 index 0000000..1b7857f Binary files /dev/null and b/src/VAX_MP/docs/aux_docs/KA650.pdf differ diff --git a/src/VAX_MP/docs/aux_docs/KA655-Maintenance.pdf b/src/VAX_MP/docs/aux_docs/KA655-Maintenance.pdf new file mode 100644 index 0000000..5750e03 Binary files /dev/null and b/src/VAX_MP/docs/aux_docs/KA655-Maintenance.pdf differ diff --git a/src/VAX_MP/docs/aux_docs/Memory consistency models for shared-memory multiprocessors (WRL-95-9).pdf b/src/VAX_MP/docs/aux_docs/Memory consistency models for shared-memory multiprocessors (WRL-95-9).pdf new file mode 100644 index 0000000..87d57bc Binary files /dev/null and b/src/VAX_MP/docs/aux_docs/Memory consistency models for shared-memory multiprocessors (WRL-95-9).pdf differ diff --git a/src/VAX_MP/docs/aux_docs/UQSSP.pdf b/src/VAX_MP/docs/aux_docs/UQSSP.pdf new file mode 100644 index 0000000..27a9b08 Binary files /dev/null and b/src/VAX_MP/docs/aux_docs/UQSSP.pdf differ diff --git a/src/VAX_MP/docs/aux_docs/VAX-Arch-Std-Jan90.pdf b/src/VAX_MP/docs/aux_docs/VAX-Arch-Std-Jan90.pdf new file mode 100644 index 0000000..162934a Binary files /dev/null and b/src/VAX_MP/docs/aux_docs/VAX-Arch-Std-Jan90.pdf differ diff --git a/src/VMS_STRESS/CC/hw.c b/src/VMS_STRESS/CC/hw.c new file mode 100644 index 0000000..6df1af3 --- /dev/null +++ b/src/VMS_STRESS/CC/hw.c @@ -0,0 +1,21 @@ +/* + * HW.C - Hello World program for ST-CC stress test + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + printf("Hello, World!\n"); + return SS$_NORMAL; +} diff --git a/src/VMS_STRESS/CC/launch.com b/src/VMS_STRESS/CC/launch.com new file mode 100644 index 0000000..0b9ed99 --- /dev/null +++ b/src/VMS_STRESS/CC/launch.com @@ -0,0 +1,79 @@ +$! +$! LAUNCH.COM - launch specified number of stress subprocesses, +$! then stop them all on Ctrl/Y +$! +$!SET VERIFY +$ TNAME = "ST_CC" +$ SAY := WRITE SYS$OUTPUT +$ IF P1 .EQS. "" .OR. F$TYPE(P1) .NES. "INTEGER" THEN GOTO USAGE +$ NPROCS = F$INTEGER(P1) +$ IF NPROCS .LE. 0 .OR. NPROCS .GT. 99 THEN GOTO BAD_NPROCS +$ PROC_FILE = F$ENVIRONMENT("PROCEDURE") +$ TEST_DIR = F$PARSE(PROC_FILE,,,"DEVICE","SYNTAX_ONLY") + - + F$PARSE(PROC_FILE,,,"DIRECTORY","SYNTAX_ONLY") +$ RUN_FILE = TEST_DIR + "RUN.COM" +$ SV_PRIO = F$GETJPI("", "PRIB") +$ SV_DEF = F$ENVIRONMENT("DEFAULT") +$ SAY "" +$ SAY "*** Starting ''NPROCS' ''TNAME' processes ..." +$ SAY "" +$ ON CONTROL_Y THEN GOTO ON_CTRL_Y +$ SET PROCESS/PRIO=8 +$ SET DEFAULT 'TEST_DIR' +$ GOSUB CLEAN +$ CREATE/DIRECTORY [.TEMP] +$! +$! +$ NP = 1 +$START_LOOP: +$ SPAWN /LOG/NOWAIT/PROCESS='TNAME'_'NP' @'RUN_FILE' 'NP' +$ NP = NP + 1 +$ IF NP .LE. NPROCS THEN GOTO START_LOOP +$ SAY "" +$ SAY "*** Press CTRL/Y to terminate started processes ***" +$ SAY "*** Waiting for CTRL/Y to be pressed ..." +$ SAY "" +$! +$! +$WAIT_LOOP: +$ WAIT 20:00 +$ GOTO WAIT_LOOP +$! +$! +$ON_CTRL_Y: +$ SAY "*** Terminating ''TNAME' processes ..." +$ NP = 1 +$STOP_LOOP: +$ ON WARNING THEN GOTO NOPROCESS +$ ON ERROR THEN GOTO NOPROCESS +$ ON SEVERE_ERROR THEN GOTO NOPROCESS +$ SET PROCESS/PRIO=4 'TNAME'_'NP' +$ STOP 'TNAME'_'NP' +$NOPROCESS: +$ ON WARNING THEN EXIT +$ ON ERROR THEN EXIT +$ ON SEVERE_ERROR THEN EXIT +$ NP = NP + 1 +$ IF NP .LE. NPROCS THEN GOTO STOP_LOOP +$ SAY "*** Removing temporary files ..." +$ SAY "" +$ WAIT 0:0:2 +$ GOSUB CLEAN +$ SET PROCESS/PRIO='SV_PRIO' +$ SET DEFAULT 'SV_DEF' +$ EXIT +$! +$! +$USAGE: +$ SAY "Usage: @LAUNCH [NPROCS]" +$ EXIT +$BAD_NPROCS: +$ SAY "NPROCS should be in 1 ... 99 range" +$ EXIT +$! +$! +$CLEAN: +$ IF F$SEARCH("[.TEMP.*]*.*;*") .NES. "" THEN DELETE/NOLOG [.TEMP.*]*.*;* +$ IF F$SEARCH("[.TEMP]*.*;*") .NES. "" THEN DELETE/NOLOG [.TEMP]*.*;* +$ IF F$SEARCH("TEMP.DIR;") .NES. "" THEN DELETE/NOLOG TEMP.DIR;* +$ RETURN \ No newline at end of file diff --git a/src/VMS_STRESS/CC/run.com b/src/VMS_STRESS/CC/run.com new file mode 100644 index 0000000..968ef36 --- /dev/null +++ b/src/VMS_STRESS/CC/run.com @@ -0,0 +1,17 @@ +$! +$! RUN.COM - run CC test +$! +$ SET NOCONTROL=Y +$ SET PROCESS/PRIO=2 +$ PROC_FILE = F$ENVIRONMENT("PROCEDURE") +$ CREATE/DIRECTORY/NOLOG [.TEMP.'P1'] +$ SET DEFAULT [.TEMP.'P1'] +$LOOP: +$ COPY [-.-]HW.C []*.* +$ CC/LIST HW.C + SYS$LIBRARY:SYS$STARLET_C.TLB/LIBRARY +$ PURGE/NOLOG +$ LINK/MAP HW.OBJ +$ PURGE/NOLOG +$ DEFINE/USER SYS$OUTPUT NLA0: +$ RUN HW.EXE +$ GOTO LOOP \ No newline at end of file diff --git a/src/VMS_STRESS/CPU_HOG/build.com b/src/VMS_STRESS/CPU_HOG/build.com new file mode 100644 index 0000000..18f365a --- /dev/null +++ b/src/VMS_STRESS/CPU_HOG/build.com @@ -0,0 +1,3 @@ +$ MACRO CPU_HOG +$ LINK CPU_HOG +$ PURGE/NOLOG \ No newline at end of file diff --git a/src/VMS_STRESS/CPU_HOG/cpu_hog.mar b/src/VMS_STRESS/CPU_HOG/cpu_hog.mar new file mode 100644 index 0000000..8623063 --- /dev/null +++ b/src/VMS_STRESS/CPU_HOG/cpu_hog.mar @@ -0,0 +1,13 @@ + .TITLE CPU_HOG + + .PSECT $CODE LONG, SHR, NOWRT, PIC, EXE + .ENTRY MAIN, ^M<> +LOOP: + INCL VAR + CLRL R0 + BRB LOOP + + .PSECT $DATA LONG, NOSHR, WRT, PIC, NOEXE +VAR: .BLKL 1 + + .END MAIN \ No newline at end of file diff --git a/src/VMS_STRESS/CPU_HOG/launch.com b/src/VMS_STRESS/CPU_HOG/launch.com new file mode 100644 index 0000000..320960c --- /dev/null +++ b/src/VMS_STRESS/CPU_HOG/launch.com @@ -0,0 +1,64 @@ +$! +$! LAUNCH.COM - launch specified number of stress subprocesses, +$! then stop them all on Ctrl/Y +$! +$!SET VERIFY +$ TNAME = "CPU_HOG" +$ SAY := WRITE SYS$OUTPUT +$ IF P1 .EQS. "" .OR. F$TYPE(P1) .NES. "INTEGER" THEN GOTO USAGE +$ NPROCS = F$INTEGER(P1) +$ IF NPROCS .LE. 0 .OR. NPROCS .GT. 99 THEN GOTO BAD_NPROCS +$ PROC_FILE = F$ENVIRONMENT("PROCEDURE") +$ RUN_FILE = F$PARSE(PROC_FILE,,,"DEVICE","SYNTAX_ONLY") + - + F$PARSE(PROC_FILE,,,"DIRECTORY","SYNTAX_ONLY") + - + "RUN.COM" +$ SV_PRIO = F$GETJPI("", "PRIB") +$ SAY "" +$ SAY "*** Starting ''NPROCS' ''TNAME' processes ..." +$ SAY "" +$ ON CONTROL_Y THEN GOTO ON_CTRL_Y +$ SET PROCESS/PRIO=8 +$! +$! +$ NP = 1 +$START_LOOP: +$ SPAWN /LOG/NOWAIT/PROCESS='TNAME'_'NP' @'RUN_FILE' 'NP' +$ NP = NP + 1 +$ IF NP .LE. NPROCS THEN GOTO START_LOOP +$ SAY "" +$ SAY "*** Press CTRL/Y to terminate started processes ***" +$ SAY "*** Waiting for CTRL/Y to be pressed ..." +$ SAY "" +$! +$! +$WAIT_LOOP: +$ WAIT 20:00 +$ GOTO WAIT_LOOP +$! +$! +$ON_CTRL_Y: +$ SAY "*** Terminating ''TNAME' processes ..." +$ SAY "" +$ NP = 1 +$STOP_LOOP: +$ ON WARNING THEN GOTO NOPROCESS +$ ON ERROR THEN GOTO NOPROCESS +$ ON SEVERE_ERROR THEN GOTO NOPROCESS +$ SET PROCESS/PRIO=4 'TNAME'_'NP' +$ STOP 'TNAME'_'NP' +$NOPROCESS: +$ ON WARNING THEN EXIT +$ ON ERROR THEN EXIT +$ ON SEVERE_ERROR THEN EXIT +$ NP = NP + 1 +$ IF NP .LE. NPROCS THEN GOTO STOP_LOOP +$ SET PROCESS/PRIO='SV_PRIO' +$ EXIT +$! +$! +$USAGE: +$ SAY "Usage: @LAUNCH [NPROCS]" +$ EXIT +$BAD_NPROCS: +$ SAY "NPROCS should be in 1 ... 99 range" +$ EXIT diff --git a/src/VMS_STRESS/CPU_HOG/run.com b/src/VMS_STRESS/CPU_HOG/run.com new file mode 100644 index 0000000..4124068 --- /dev/null +++ b/src/VMS_STRESS/CPU_HOG/run.com @@ -0,0 +1,10 @@ +$! +$! RUN.COM - run CPU_HOG image +$! +$ SET NOCONTROL=Y +$ SET PROCESS/PRIO=1 +$ PROC_FILE = F$ENVIRONMENT("PROCEDURE") +$ RUN_FILE = F$PARSE(PROC_FILE,,,"DEVICE","SYNTAX_ONLY") + - + F$PARSE(PROC_FILE,,,"DIRECTORY","SYNTAX_ONLY") + - + "CPU_HOG.EXE" +$ RUN 'RUN_FILE' \ No newline at end of file diff --git a/src/VMS_STRESS/FILE/build.com b/src/VMS_STRESS/FILE/build.com new file mode 100644 index 0000000..d75d971 --- /dev/null +++ b/src/VMS_STRESS/FILE/build.com @@ -0,0 +1,11 @@ +$ IF P1 .EQS. "DEBUG" +$ THEN +$ CC := CC /DEBUG=ALL/NOOPTIMIZE/LIST/MACHINE_CODE +$ LINK := LINK/DEBUG +$ ELSE +$ CC := CC /NODEBUG/OPTIMIZE/LIST/MACHINE_CODE +$ LINK := LINK/NOTRACE +$ ENDIF +$ CC FILE.C + SYS$LIBRARY:SYS$STARLET_C.TLB/LIBRARY +$ LINK FILE.OBJ +$ FILE :== $SYS$DISK:[]FILE \ No newline at end of file diff --git a/src/VMS_STRESS/FILE/file.c b/src/VMS_STRESS/FILE/file.c new file mode 100644 index 0000000..aa4a85b --- /dev/null +++ b/src/VMS_STRESS/FILE/file.c @@ -0,0 +1,546 @@ +/* + * FILE.C - program for ST-FILE stress test + * + * Executes a loop filling file with generated content, then reading it back and checking + * if content is valid. + * + * Usage: FILE {RMS|QIO} proc# [passes] + * + * RMS mode uses RMS for IO thus passing through RMS and FCP/volume caches + * QIO uses directed IO bypassing cache (IO$_READVBLK|IO$M_NOVCACHE and IO$_WRITEVBLK|IO$M_NOVCACHE) + * CRT mode does not work: fseek seems not to position properly and somehow garbles file content + * + * proc# is a process number + * + * passes is a number of passes to execute before terminating, if 0 or omitted then infinite + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*************************************************************************************** +* Helper macros, type definitions etc. * +***************************************************************************************/ + +#ifndef FALSE +# define FALSE 0 +#endif + +#ifndef TRUE +# define TRUE 1 +#endif + +#ifndef MAX_PATH +# define MAX_PATH 512 +#endif + +#define CHECK(cond) do { if (! (cond)) goto cleanup; } while (0) + +#define check(cond) do { if (! (cond)) { status = SS$_ABORT; goto cleanup; } } while (0) +#define check_vms(st) do { if (! $VMS_STATUS_SUCCESS(st)) goto cleanup; } while (0) +#define check_vms_status(st) do { status = (st); if (! $VMS_STATUS_SUCCESS(status)) goto cleanup; } while (0) +#define fail(st) do { status = (st); goto cleanup; } while (0) +#define vms_success(st) $VMS_STATUS_SUCCESS(st) + +#define streqi(s1, s2) (0 == strcasecmp((s1), (s2))) +#define countof(a) (sizeof(a) / sizeof((a)[0])) + +typedef unsigned int uint32; +typedef unsigned short uint16; +typedef unsigned char byte_t; +typedef unsigned int bool_t; + +typedef struct __iosb +{ + uint16 iosb$w_status; + uint16 iosb$w_count; + uint32 iosb$l_extended; +} +IOSB; + +typedef struct __VA_RANGE +{ + void* start; + void* end; +} +VA_RANGE; + +typedef struct dsc$descriptor DESC; + +static void mkdesc(DESC* dsc, const char* s) +{ + dsc->dsc$w_length = (uint16) strlen((char*) s); + dsc->dsc$a_pointer = (char*) s; + dsc->dsc$b_dtype = DSC$K_DTYPE_T; + dsc->dsc$b_class = DSC$K_CLASS_S; +} + +static inline uint32 hash32(uint32 key) +{ + key += key << 12; + key ^= key >> 22; + key += key << 4; + key ^= key >> 9; + key += key << 10; + key ^= key >> 2; + key += key << 7; + key ^= key >> 12; + return key; +} + +/*************************************************************************************** +* Configuration parameters * +***************************************************************************************/ + +/* blocks to write and read in a single iteration */ +#define NBLOCKS 128 + +/*************************************************************************************** +* Module-global data * +***************************************************************************************/ + +#define IO_METHOD_RMS 0 +#define IO_METHOD_QIO 1 +#define IO_METHOD_CRT 2 + +static uint32 io_method = IO_METHOD_RMS; +static uint32 process_number; +static uint32 random_id; +static uint32 block_seq = 1; +byte_t* pblocks = NULL; /* buffer of NBLOCKS pagelets */ +byte_t* ppage = NULL; /* buffer of one pagelet */ +static uint32 tm_start[2]; +static uint32 pid; +static FILE* fp = NULL; +static char work_file_name[MAX_PATH]; +uint32 passes = 0; + +static struct FAB fab; +static struct RAB rab; +static bool_t rms_open = FALSE; +static bool_t qio_open = FALSE; +static uint32 efn = 0; + +/*************************************************************************************** +* Local function prototypes * +***************************************************************************************/ + +static void usage(); +static void fill_next_block(byte_t* pb, uint32 blkindex); +void create_file_conditional(); +static void close_reopen_file(); +static void close_file(); +static void delete_file(); +static void write_block(long offset, byte_t* pb); +static void read_block(long offset, byte_t* pb); +static void bad(const char* msg); +static void bad_st(const char* msg, uint32 status); +static void baderr(const char* msg); +FILE* do_fopen(const char* mode); + +/*************************************************************************************** +* Main routine * +***************************************************************************************/ + +int main(int argc, char** argv) +{ + uint32 status; + uint32 nblocks; + uint32 pass; + int k; + char c; + + /* + * Parse arguments + */ + + if (argc != 3 && argc != 4) + usage(); + + if (streqi(argv[1], "rms")) + io_method = IO_METHOD_RMS; + else if (streqi(argv[1], "qio")) + io_method = IO_METHOD_QIO; + else if (streqi(argv[1], "crt")) + io_method = IO_METHOD_CRT; + else + usage(); + + if (1 != sscanf(argv[2], "%ud%c", &process_number, & c)) + usage(); + + if (argc == 4) + { + if (1 != sscanf(argv[3], "%ud%c", &passes, & c)) + usage(); + } + + /* + * Initialize aux data + */ + status = lib$get_ef(& efn); + if (! vms_success(status)) + bad_st("unable to allocate event flag", status); + + /* + * Initialize randomness + */ + check_vms_status(sys$gettim(& tm_start)); + pid = getpid(); + random_id = hash32(tm_start[0]) ^ hash32(pid); + random_id = hash32(random_id) ^ hash32(process_number); + + /* + * Allocate read-write buffers + */ + nblocks = NBLOCKS + 1; + check_vms_status(lib$get_vm_page(&nblocks, &ppage)); + pblocks = ppage + 512; + + /* + * Perform main loop + */ + sprintf(work_file_name, "temp%d.tmp;1", process_number); + delete_file(); + for (pass = 0; ; pass++) + { + /* create or open file if necessary */ + create_file_conditional(); + + /* generate data blocks */ + for (k = 0; k < NBLOCKS; k++) + fill_next_block(pblocks + 512 * k, k); + + /* write sequence of NBLOCKS blocks */ + for (k = 0; k < NBLOCKS; k++) + write_block(k * 512, pblocks + 512 * k); + + /* every second time: close/reopen file */ + if ((pass % 2) == 0) + close_reopen_file(); + + /* read blocks in reverse order and compare */ + for (k = NBLOCKS - 1; k >= 0; k--) + { + read_block(k * 512, ppage); + if (0 != memcmp(ppage, pblocks + 512 * k, 512)) + { + uint32* pr = (uint32*) ppage; + uint32* pw = (uint32*) (pblocks + 512 * k); + fprintf(stderr, "Read/write data mismatch: process %d, pass %d, blk %d\n wr: (%X, %X, %X)\n rd: (%X, %X, %X)\n", + process_number, pass, k, pw[0], pw[1], pw[2], pr[0], pr[1], pr[2]); + exit(SS$_ABORT | STS$M_INHIB_MSG); + } + } + + /* after each 10th time close and delete file and re-create it later */ + if ((pass % 10) == 0) + { + close_file(); + delete_file(); + } + + /* after specified number of iterations exit and delete file */ + if (passes && pass + 1 == passes) + { + close_file(); + delete_file(); + break; + } + } + + return SS$_NORMAL; + +cleanup: + exit(status); +} + +static void fill_next_block(byte_t* pb, uint32 blkindex) +{ + uint32* px = (uint32*) pb; + int k; + + *px++ = (process_number << 16) | blkindex; + *px++ = block_seq++; + for (k = 0; k < 512/sizeof(uint32) - 2; k++) + { + *px++ = random_id; + random_id = hash32((random_id + tm_start[0] + process_number) ^ pid); + } +} + +static void usage() +{ + fprintf(stderr, "usage: FILE {RMS|QIO} proc# [passes]\n"); + exit(SS$_INVARG | STS$M_INHIB_MSG); +} + +void create_file_conditional() +{ + uint32 status; + + if (io_method == IO_METHOD_CRT) + { + if (fp == NULL) + { + fp = do_fopen("rb+"); + if (fp == NULL) + { + if (fp = do_fopen("wb")) + { + fclose(fp); + fp = do_fopen("rb+"); + } + } + if (fp == NULL) + baderr("unable to create or open file"); + } + if (fseek(fp, 0, SEEK_SET)) + baderr("unable to rewind file"); + } + else if (io_method == IO_METHOD_RMS) + { + if (! rms_open) + { + fab = cc$rms_fab; + fab.fab$l_fna = work_file_name; + fab.fab$b_fns = strlen(work_file_name); + fab.fab$l_alq = NBLOCKS; + fab.fab$b_fac = FAB$M_BIO|FAB$M_GET|FAB$M_PUT; + fab.fab$v_cif = 1; + fab.fab$w_gbc = 2; + fab.fab$w_mrs = 512; + fab.fab$b_rfm = FAB$C_FIX; + fab.fab$v_nil = 1; + + status = sys$create(&fab); + if (! vms_success(status)) + bad_st("unable to create or open work file", status); + + rab = cc$rms_rab; + rab.rab$l_fab = & fab; + rab.rab$w_rsz = 512; + rab.rab$l_rbf = (char*) ppage; + rab.rab$w_usz = 512; + rab.rab$l_ubf = (char*) pblocks; + rab.rab$b_mbf = 1; + rab.rab$b_rac = RAB$C_RFA; + rab.rab$v_bio = 1; + rab.rab$v_wbh = 1; + + status = sys$connect(&rab); + if (! vms_success(status)) + bad_st("unable to create or open work file (sys$connect failed)", status); + + rms_open = TRUE; + } + else + { + status = sys$rewind(&rab); + if (! vms_success(status)) + bad_st("unable to position in work file (sys$rewind failed)", status); + } + } + else + { + if (! qio_open) + { + fab = cc$rms_fab; + fab.fab$l_fna = work_file_name; + fab.fab$b_fns = strlen(work_file_name); + fab.fab$l_alq = NBLOCKS; + fab.fab$b_fac = FAB$M_BIO|FAB$M_GET|FAB$M_PUT; + fab.fab$v_cif = 1; + fab.fab$w_gbc = 2; + fab.fab$w_mrs = 512; + fab.fab$b_rfm = FAB$C_FIX; + fab.fab$v_nil = 1; + fab.fab$v_ufo = 1; + + status = sys$create(&fab); + if (! vms_success(status)) + bad_st("unable to create or open work file", status); + + qio_open = TRUE; + } + } +} + +FILE* do_fopen(const char* mode) +{ + char alq[32]; + sprintf(alq, "alq=%d", NBLOCKS); + return fopen(work_file_name, mode, alq, "rfm=fix", "mrs=512", "ctx=bin", "fop=sup", "rop=asy,syncsts,wbh", "gbc=2", "mbf=1", "shr=nil"); + // return fopen(work_file_name, mode, "rfm=fix", "mrs=512"); +} + +static void close_reopen_file() +{ + close_file(); + create_file_conditional(); +} + +static void close_file() +{ + uint32 status; + + if (io_method == IO_METHOD_CRT) + { + if (fp) + { + if (fclose(fp)) + baderr("unable to close file"); + fp = NULL; + } + } + else if (io_method == IO_METHOD_RMS) + { + if (rms_open) + { + status = sys$disconnect(&rab); + if (! vms_success(status)) + bad_st("unable close work file (sys$disconnect failed)", status); + status = sys$close(&fab); + if (! vms_success(status)) + bad_st("unable to close work file (sys$close failed)", status); + rms_open = FALSE; + } + } + else + { + if (qio_open) + { + sys$dassgn(fab.fab$l_stv); + qio_open = FALSE; + } + } +} + +static void delete_file() +{ + close_file(); + remove(work_file_name); + if (0 == access(work_file_name, F_OK)) + bad("was unable to delete work file"); +} + +static void write_block(long offset, byte_t* pb) +{ + uint32 status; + + if (io_method == IO_METHOD_CRT) + { + if (fwrite(pb, 1, 512, fp) != 512 || fflush(fp) || fsync(fileno(fp))) + baderr("write to file failed"); + } + else if (io_method == IO_METHOD_RMS) + { + rab.rab$l_rbf = (char*) pb; + rab.rab$w_rsz = 512; + rab.rab$l_bkt = offset / 512 + 1; + status = sys$write(& rab); + if (! vms_success(status)) + bad_st("unable to write work file (sys$write failed)", status); + status = sys$flush(& rab); + if (! vms_success(status)) + bad_st("unable to write work file (sys$flush failed)", status); + } + else + { + IOSB iosb; + uint32 vbn = offset / 512 + 1; + + status = sys$qiow(efn, fab.fab$l_stv, IO$_WRITEVBLK|IO$M_NOVCACHE, &iosb, NULL, 0, pb, 512, vbn, 0, 0, 0); + if (! vms_success(status)) + bad_st("unable to write work file (sys$qio failed)", status); + if (! vms_success(iosb.iosb$w_status)) + bad_st("unable to write work file (sys$qio failed)", iosb.iosb$w_status); + if (iosb.iosb$w_count != 512) + baderr("incomplete block write"); + } +} + +static void read_block(long offset, byte_t* pb) +{ + uint32 status; + + if (io_method == IO_METHOD_CRT) + { + int count; + if (fseek(fp, offset, SEEK_SET)) + baderr("unable to position in file"); + count = fread(pb, 1, 512, fp); + if (count != 512) + { + if (count < 0) + { + baderr("reading from file failed"); + } + else + { + fprintf(stderr, "Error: reading from file failed (read %d instead of 512)\n", count); + exit(SS$_ABORT | STS$M_INHIB_MSG); + } + } + } + else if (io_method == IO_METHOD_RMS) + { + rab.rab$l_ubf = (char*) pb; + rab.rab$w_usz = 512; + rab.rab$l_bkt = offset / 512 + 1; + status = sys$read(&rab); + if (! vms_success(status)) + bad_st("unable to read work file (sys$read failed)", status); + if (rab.rab$w_rsz != 512) + baderr("incomplete block read"); + if (rab.rab$l_rbf != (char*) pb) + memcpy(pb, (void*) rab.rab$l_rbf, 512); + } + else + { + IOSB iosb; + uint32 vbn = offset / 512 + 1; + + status = sys$qiow(efn, fab.fab$l_stv, IO$_READVBLK|IO$M_NOVCACHE, &iosb, NULL, 0, pb, 512, vbn, 0, 0, 0); + if (! vms_success(status)) + bad_st("unable to read work file (sys$qio failed)", status); + if (! vms_success(iosb.iosb$w_status)) + bad_st("unable to read work file (sys$qio failed)", iosb.iosb$w_status); + if (iosb.iosb$w_count != 512) + baderr("incomplete block read"); + } +} + +static void bad(const char* msg) +{ + fprintf(stderr, "Error: %s\n", msg); + exit(SS$_ABORT | STS$M_INHIB_MSG); +} + +static void bad_st(const char* msg, uint32 status) +{ + fprintf(stderr, "Error: %s\n", msg); + exit(status); +} + +static void baderr(const char* msg) +{ + fprintf(stderr, "Error: "); + perror(msg); + exit(SS$_ABORT | STS$M_INHIB_MSG); +} diff --git a/src/VMS_STRESS/FILE/launch.com b/src/VMS_STRESS/FILE/launch.com new file mode 100644 index 0000000..cc934fc --- /dev/null +++ b/src/VMS_STRESS/FILE/launch.com @@ -0,0 +1,93 @@ +$! +$! LAUNCH.COM - launch specified number of stress subprocesses, +$! then stop them all on Ctrl/Y +$! +$!SET VERIFY +$ TNAME = "ST_FILE" +$ SAY := WRITE SYS$OUTPUT +$! +$ IF P1 .EQS. "" .OR. F$TYPE(P1) .NES. "INTEGER" THEN GOTO USAGE +$ NPROCS = F$INTEGER(P1) +$ IF NPROCS .LE. 0 .OR. NPROCS .GT. 99 THEN GOTO BAD_NPROCS +$! +$ IF (P2 .NES. "RMS") .AND. (P2 .NES. "QIO") THEN GOTO USAGE +$ MODE = P2 +$! +$ INST_ID = P3 +$! +$ IF P4 .NES. "" .AND. F$TYPE(P4) .NES. "INTEGER" THEN GOTO USAGE +$ NPASSES = P4 +$! +$ PROC_FILE = F$ENVIRONMENT("PROCEDURE") +$ TEST_DIR = F$PARSE(PROC_FILE,,,"DEVICE","SYNTAX_ONLY") + - + F$PARSE(PROC_FILE,,,"DIRECTORY","SYNTAX_ONLY") +$ RUN_FILE = TEST_DIR + "RUN.COM" +$ SV_PRIO = F$GETJPI("", "PRIB") +$ SV_DEF = F$ENVIRONMENT("DEFAULT") +$ SAY "" +$ SAY "*** Starting ''NPROCS' ''TNAME' processes ..." +$ SAY "" +$ ON CONTROL_Y THEN GOTO ON_CTRL_Y +$ SET PROCESS/PRIO=8 +$ SET DEFAULT 'TEST_DIR' +$ GOSUB CLEAN +$ IF F$SEARCH("TEMP.DIR") .EQS. "" THEN CREATE/DIRECTORY/NOLOG [.TEMP] +$! +$! +$ NP = 1 +$START_LOOP: +$ SPAWN /LOG/NOWAIT/PROCESS='TNAME'_'INST_ID''NP' @'RUN_FILE' 'MODE' 'NP' 'NPASSES' +$ NP = NP + 1 +$ IF NP .LE. NPROCS THEN GOTO START_LOOP +$ SAY "" +$ SAY "*** Press CTRL/Y to terminate started processes ***" +$ SAY "*** Waiting for CTRL/Y to be pressed ..." +$ SAY "" +$! +$! +$WAIT_LOOP: +$ WRITE SYS$OUTPUT " Timestamp: " + F$TIME() +$ WAIT 1:00:00 +$ GOTO WAIT_LOOP +$! +$! +$ON_CTRL_Y: +$ SAY "*** Terminating ''TNAME' processes ..." +$ NP = 1 +$STOP_LOOP: +$ ON WARNING THEN GOTO NOPROCESS +$ ON ERROR THEN GOTO NOPROCESS +$ ON SEVERE_ERROR THEN GOTO NOPROCESS +$ SET PROCESS/PRIO=4 'TNAME'_'INST_ID''NP' +$ STOP 'TNAME'_'INST_ID''NP' +$NOPROCESS: +$ ON WARNING THEN EXIT +$ ON ERROR THEN EXIT +$ ON SEVERE_ERROR THEN EXIT +$ NP = NP + 1 +$ IF NP .LE. NPROCS THEN GOTO STOP_LOOP +$ SAY "*** Removing temporary files ..." +$ SAY "" +$ WAIT 0:0:2 +$ GOSUB CLEAN +$ SET PROCESS/PRIO='SV_PRIO' +$ SET DEFAULT 'SV_DEF' +$ EXIT +$! +$! +$USAGE: +$ SAY "Usage: @LAUNCH NPROCS {RMS|QIO} [INST_ID] [NPASSES]" +$ SAY "" +$ SAY " INST_ID can be A-Z and is used for naming processes, so multiple instances" +$ SAY " of the test could run in parallel on different disk devices." +$ EXIT +$BAD_NPROCS: +$ SAY "NPROCS should be in 1 ... 99 range" +$ EXIT +$! +$! +$CLEAN: +$ IF F$SEARCH("[.TEMP.*]*.*;*") .NES. "" THEN DELETE/NOLOG [.TEMP.*]*.*;* +$ IF F$SEARCH("[.TEMP]*.*;*") .NES. "" THEN DELETE/NOLOG [.TEMP]*.*;* +$ IF F$SEARCH("TEMP.DIR;") .NES. "" THEN DELETE/NOLOG TEMP.DIR;* +$ RETURN \ No newline at end of file diff --git a/src/VMS_STRESS/FILE/run.com b/src/VMS_STRESS/FILE/run.com new file mode 100644 index 0000000..6c94fc8 --- /dev/null +++ b/src/VMS_STRESS/FILE/run.com @@ -0,0 +1,11 @@ +$! +$! RUN.COM - run FILE test +$! +$ SET NOCONTROL=Y +$ SET PROCESS/PRIO=3 +$ IF F$SEARCH("TEMP.DIR") .EQS. "" THEN CREATE/DIRECTORY/NOLOG [.TEMP] +$ SET DEFAULT [.TEMP] +$ FILE := $SYS$DISK:[-]FILE.EXE +$LOOP: +$ FILE 'P1' 'P2' 'P3' +$ GOTO LOOP \ No newline at end of file diff --git a/src/VMS_STRESS/NUMTIM/build.com b/src/VMS_STRESS/NUMTIM/build.com new file mode 100644 index 0000000..8dce43d --- /dev/null +++ b/src/VMS_STRESS/NUMTIM/build.com @@ -0,0 +1,2 @@ +$ CC NUMTIM.C + SYS$LIBRARY:SYS$STARLET_C.TLB/LIBRARY +$ LINK NUMTIM.OBJ \ No newline at end of file diff --git a/src/VMS_STRESS/NUMTIM/numtim.c b/src/VMS_STRESS/NUMTIM/numtim.c new file mode 100644 index 0000000..1c7dd12 --- /dev/null +++ b/src/VMS_STRESS/NUMTIM/numtim.c @@ -0,0 +1,33 @@ +/* + * NUMTIM.C + * + * Perform SYS$NUMTIM test + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + unsigned short t[7]; + int status; + + status = sys$numtim (t, NULL); + if (! $VMS_STATUS_SUCCESS(status)) + exit(status); + + printf("%d-%d-%d %02d:%02d:%02d.%02d\n", t[2], t[1], t[0], t[3], t[4], t[5], t[6]); + + return SS$_NORMAL; +} diff --git a/src/VMS_STRESS/PAGE/build.com b/src/VMS_STRESS/PAGE/build.com new file mode 100644 index 0000000..70a6a66 --- /dev/null +++ b/src/VMS_STRESS/PAGE/build.com @@ -0,0 +1,11 @@ +$ IF P1 .EQS. "DEBUG" +$ THEN +$ CC := CC /DEBUG=ALL/NOOPTIMIZE/LIST/MACHINE_CODE +$ LINK := LINK/DEBUG +$ ELSE +$ CC := CC /NODEBUG/OPTIMIZE/LIST/MACHINE_CODE +$ LINK := LINK/NOTRACE +$ ENDIF +$ CC PAGE.C + SYS$LIBRARY:SYS$STARLET_C.TLB/LIBRARY +$ LINK PAGE.OBJ +$ PAGE :== $SYS$DISK:[]PAGE \ No newline at end of file diff --git a/src/VMS_STRESS/PAGE/launch.com b/src/VMS_STRESS/PAGE/launch.com new file mode 100644 index 0000000..5843ffb --- /dev/null +++ b/src/VMS_STRESS/PAGE/launch.com @@ -0,0 +1,123 @@ +$! +$! LAUNCH.COM - launch specified number of stress subprocesses, +$! then stop them all on Ctrl/Y +$! +$! When running this test you may either run a scenario that exercises "soft" paging +$! between process space and modified list, or "hard" paging with IO to/from paging file. +$! +$! In the latter case you want to reduce the size both of physical memory avalable and of +$! modified list. +$! +$! Size of physical memory can be reduced either with SIMH configuration or with SYSGEN +$! parameter PHYSICALPAGES. +$! +$! To reduce modified-list size, reduce MPW_HILIMIT, MPW_LOLIMIT and associated +$! MPW_WAITLIMIT and MPW_LOWAITLIMIT, for example: +$! +$! USE CURRENT +$! WR SAFE.PAR +$! SET PHYSICALPAGES 30000 +$! SET MPW_LOLIMIT 50 +$! SET MPW_HILIMIT 300 +$! SET MPW_WAITLIMIT 300 +$! SET MPW_LOWAITLIMIT 200 +$! SET WSMAX 2048 +$! WR PAGETEST.PAR +$! +$! then use SYSBOOT to start system with these parameters +$! (remember to SET WRITESYS 0 when executing SYSBOOT!) +$! and run: +$! +$! @LAUNCH 20 2000 +$! +$! yelding demand of 40,000 memory pages exceeding free list and causing the pages +$! to be both written to page file and read from it. +$! +$! Note: We observed TCPIP services for OpenVMS crash system under tight memory +$! configuration of 28,000 pages. Crash happened in 50-second periodic TQE +$! queued by TCPIP (invalid TQE$L_FPC). +$! +$!SET VERIFY +$ TNAME = "ST_PAGE" +$ SAY := WRITE SYS$OUTPUT +$! +$ IF P1 .EQS. "" .OR. F$TYPE(P1) .NES. "INTEGER" THEN GOTO USAGE +$ NPROCS = F$INTEGER(P1) +$ IF NPROCS .LE. 0 .OR. NPROCS .GT. 99 THEN GOTO BAD_NPROCS +$! +$ IF P2 .EQS. "" .OR. F$TYPE(P2) .NES. "INTEGER" THEN GOTO USAGE +$ NPAGES = F$INTEGER(P2) +$ IF NPAGES .LE. 0 THEN GOTO BAD_NPAGES +$! +$ PROC_FILE = F$ENVIRONMENT("PROCEDURE") +$ TEST_DIR = F$PARSE(PROC_FILE,,,"DEVICE","SYNTAX_ONLY") + - + F$PARSE(PROC_FILE,,,"DIRECTORY","SYNTAX_ONLY") +$ RUN_FILE = TEST_DIR + "RUN.COM" +$ SV_PRIO = F$GETJPI("", "PRIB") +$ SV_DEF = F$ENVIRONMENT("DEFAULT") +$ SAY "" +$ SAY "*** Starting ''NPROCS' ''TNAME' processes ..." +$ SAY "" +$ ON CONTROL_Y THEN GOTO ON_CTRL_Y +$ SET PROCESS/PRIO=8 +$ SET DEFAULT 'TEST_DIR' +$!GOSUB CLEAN +$!IF F$SEARCH("TEMP.DIR") .EQS. "" THEN CREATE/DIRECTORY/NOLOG [.TEMP] +$! +$! +$ NP = 1 +$START_LOOP: +$ SPAWN /LOG/NOWAIT/PROCESS='TNAME'_'NP' @'RUN_FILE' 'NP' 'NPAGES' +$ NP = NP + 1 +$ IF NP .LE. NPROCS THEN GOTO START_LOOP +$ SAY "" +$ SAY "*** Press CTRL/Y to terminate started processes ***" +$ SAY "*** Waiting for CTRL/Y to be pressed ..." +$ SAY "" +$! +$! +$WAIT_LOOP: +$ WAIT 20:00 +$ GOTO WAIT_LOOP +$! +$! +$ON_CTRL_Y: +$ SAY "*** Terminating ''TNAME' processes ..." +$ NP = 1 +$STOP_LOOP: +$ ON WARNING THEN GOTO NOPROCESS +$ ON ERROR THEN GOTO NOPROCESS +$ ON SEVERE_ERROR THEN GOTO NOPROCESS +$ SET PROCESS/PRIO=4 'TNAME'_'NP' +$ STOP 'TNAME'_'NP' +$NOPROCESS: +$ ON WARNING THEN EXIT +$ ON ERROR THEN EXIT +$ ON SEVERE_ERROR THEN EXIT +$ NP = NP + 1 +$ IF NP .LE. NPROCS THEN GOTO STOP_LOOP +$!SAY "*** Removing temporary files ..." +$!SAY "" +$ WAIT 0:0:2 +$!GOSUB CLEAN +$ SET PROCESS/PRIO='SV_PRIO' +$ SET DEFAULT 'SV_DEF' +$ EXIT +$! +$! +$USAGE: +$ SAY "Usage: @LAUNCH NPROCS NPAGES" +$ EXIT +$BAD_NPROCS: +$ SAY "NPROCS should be in 1 ... 99 range" +$ EXIT +$BAD_NPAGES: +$ SAY "NPAGES should be above 0" +$ EXIT +$! +$! +$!CLEAN: +$!IF F$SEARCH("[.TEMP.*]*.*;*") .NES. "" THEN DELETE/NOLOG [.TEMP.*]*.*;* +$!IF F$SEARCH("[.TEMP]*.*;*") .NES. "" THEN DELETE/NOLOG [.TEMP]*.*;* +$!IF F$SEARCH("TEMP.DIR;") .NES. "" THEN DELETE/NOLOG TEMP.DIR;* +$!RETURN \ No newline at end of file diff --git a/src/VMS_STRESS/PAGE/page.c b/src/VMS_STRESS/PAGE/page.c new file mode 100644 index 0000000..39f3e40 --- /dev/null +++ b/src/VMS_STRESS/PAGE/page.c @@ -0,0 +1,262 @@ +/* + * PAGE.C - program for ST-PAGE stress test + * + * Executes a loop filling virtual memory pages at random with data, then verifying content some time later + * and overwriting the content again. Causes high paging rate and stress-tests paging system to verify its + * integrity. + * + * Usage: PAGE proc# npages + * + * proc# is a process number + * + * npages is the number of virtual pages to use in stress buffer + * + * ToDo: + * + * 1) Implement test to page global memory section, in addition to private pages. + * 50% of paging can go to global section, 50% can go to private pages. + * + * 2) Implement test to page global memory section actually shared by process instances. + * Each instance should write to and read back its data from a slot in each of the section's pages. + * The slot is defined by process index. Thus there can be up to 512 / 4 = 128 process instances, + * or even more if word or byte reads/writes are used instead of longword reads/writes. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*************************************************************************************** +* Helper macros, type definitions etc. * +***************************************************************************************/ + +#ifndef FALSE +# define FALSE 0 +#endif + +#ifndef TRUE +# define TRUE 1 +#endif + +#ifndef MAX_PATH +# define MAX_PATH 512 +#endif + +#define PAGESIZE 512 + +#define CHECK(cond) do { if (! (cond)) goto cleanup; } while (0) + +#define check(cond) do { if (! (cond)) { status = SS$_ABORT; goto cleanup; } } while (0) +#define check_vms(st) do { if (! $VMS_STATUS_SUCCESS(st)) goto cleanup; } while (0) +#define check_vms_status(st) do { status = (st); if (! $VMS_STATUS_SUCCESS(status)) goto cleanup; } while (0) +#define fail(st) do { status = (st); goto cleanup; } while (0) +#define vms_success(st) $VMS_STATUS_SUCCESS(st) + +#define streqi(s1, s2) (0 == strcasecmp((s1), (s2))) +#define countof(a) (sizeof(a) / sizeof((a)[0])) + +typedef unsigned int uint32; +typedef unsigned short uint16; +typedef unsigned char byte_t; +typedef unsigned int bool_t; + +typedef struct __VA_RANGE +{ + void* start; + void* end; +} +VA_RANGE; + +typedef struct dsc$descriptor DESC; + +static void mkdesc(DESC* dsc, const char* s) +{ + dsc->dsc$w_length = (uint16) strlen((char*) s); + dsc->dsc$a_pointer = (char*) s; + dsc->dsc$b_dtype = DSC$K_DTYPE_T; + dsc->dsc$b_class = DSC$K_CLASS_S; +} + +static inline uint32 hash32(uint32 key) +{ + key += key << 12; + key ^= key >> 22; + key += key << 4; + key ^= key >> 9; + key += key << 10; + key ^= key >> 2; + key += key << 7; + key ^= key >> 12; + return key; +} + +/*************************************************************************************** +* Module-global data * +***************************************************************************************/ + +static uint32 process_number; +static uint32 npages; +static uint32 random_id; +static uint32 tm_start[2]; +static uint32 pid; +static byte_t* ppages = NULL; +static uint32* ptrack = NULL; + +/*************************************************************************************** +* Local function prototypes * +***************************************************************************************/ + +static void usage(); +static void bad(const char* msg); +static void bad_st(const char* msg, uint32 status); +static void baderr(const char* msg); +static uint32 mkrand(); + +/*************************************************************************************** +* Main routine * +***************************************************************************************/ + +int main(int argc, char** argv) +{ + uint32 status; + int k; + char c; + uint32* pp; + uint32 val; + + /* + * Parse arguments + */ + if (argc != 3) + usage(); + + if (1 != sscanf(argv[1], "%ud%c", &process_number, & c)) + usage(); + + if (1 != sscanf(argv[2], "%ud%c", &npages, & c)) + usage(); + + /* + * Initialize randomness + */ + check_vms_status(sys$gettim(& tm_start)); + pid = getpid(); + random_id = hash32(tm_start[0]) ^ hash32(pid); + random_id = hash32(random_id) ^ hash32(process_number); + + /* + * Allocate pages + */ + check_vms_status(lib$get_vm_page(&npages, &ppages)); + + /* + * Allocate tracking buffer + */ + k = (npages * sizeof(uint32)) / PAGESIZE + 1; + check_vms_status(lib$get_vm_page(&k, &ptrack)); + memset(ptrack, 0, npages * sizeof(uint32)); + + /* + * Perform main loop + */ + for (;;) + { + /* get page index at random */ + uint32 index = mkrand() % npages; + + uint32* page = (uint32*) (ppages + PAGESIZE * index); + + /* check if page was filled and if so verify its content */ + if (val = ptrack[index]) + { + pp = page; +#define CHKVAL if (val != *pp++) goto badvalue + for (k = 0; k < PAGESIZE / (32 * sizeof(uint32)); k++) + { + CHKVAL; CHKVAL; CHKVAL; CHKVAL; + CHKVAL; CHKVAL; CHKVAL; CHKVAL; + CHKVAL; CHKVAL; CHKVAL; CHKVAL; + CHKVAL; CHKVAL; CHKVAL; CHKVAL; + CHKVAL; CHKVAL; CHKVAL; CHKVAL; + CHKVAL; CHKVAL; CHKVAL; CHKVAL; + CHKVAL; CHKVAL; CHKVAL; CHKVAL; + CHKVAL; CHKVAL; CHKVAL; CHKVAL; + } + } + + /* generate new filler value and fill the page */ + val = ptrack[index] = mkrand(); + pp = page; + for (k = 0; k < PAGESIZE / (32 * sizeof(uint32)); k++) + { + *pp++ = val; *pp++ = val; *pp++ = val; *pp++ = val; + *pp++ = val; *pp++ = val; *pp++ = val; *pp++ = val; + *pp++ = val; *pp++ = val; *pp++ = val; *pp++ = val; + *pp++ = val; *pp++ = val; *pp++ = val; *pp++ = val; + *pp++ = val; *pp++ = val; *pp++ = val; *pp++ = val; + *pp++ = val; *pp++ = val; *pp++ = val; *pp++ = val; + *pp++ = val; *pp++ = val; *pp++ = val; *pp++ = val; + *pp++ = val; *pp++ = val; *pp++ = val; *pp++ = val; + } + } + + return SS$_NORMAL; + +cleanup: + exit(status); + +badvalue: + fprintf(stderr, "Memory consistency verification failed, expected %08X, found %08X\n", val, pp[-1]); +} + +static uint32 mkrand() +{ + uint32 status; + + for (;;) + { + random_id = hash32((random_id + tm_start[0] + process_number) ^ pid); + if (random_id) return random_id; + check_vms_status(sys$gettim(& tm_start)); + } + +cleanup: + exit(status); +} + +static void usage() +{ + fprintf(stderr, "usage: PAGE proc# npages\n"); + exit(SS$_INVARG | STS$M_INHIB_MSG); +} + +static void bad(const char* msg) +{ + fprintf(stderr, "Error: %s\n", msg); + exit(SS$_ABORT | STS$M_INHIB_MSG); +} + +static void bad_st(const char* msg, uint32 status) +{ + fprintf(stderr, "Error: %s\n", msg); + exit(status); +} + +static void baderr(const char* msg) +{ + fprintf(stderr, "Error: "); + perror(msg); + exit(SS$_ABORT | STS$M_INHIB_MSG); +} diff --git a/src/VMS_STRESS/PAGE/run.com b/src/VMS_STRESS/PAGE/run.com new file mode 100644 index 0000000..a1e07c0 --- /dev/null +++ b/src/VMS_STRESS/PAGE/run.com @@ -0,0 +1,8 @@ +$! +$! RUN.COM - run PAGE test +$! +$ SET NOCONTROL=Y +$ SET WORKING_SET /LIMIT=200/QUOTA=200/EXTENT=300 +$ SET PROCESS/PRIO=2 +$ PAGE := $SYS$DISK:[]PAGE.EXE +$ PAGE 'P1' 'P2' diff --git a/src/VMS_STRESS/build_all.com b/src/VMS_STRESS/build_all.com new file mode 100644 index 0000000..20d12e2 --- /dev/null +++ b/src/VMS_STRESS/build_all.com @@ -0,0 +1,9 @@ +$ SET DEFAULT [.NUMTIM] +$ @BUILD +$ SET DEFAULT [-.FILE] +$ @BUILD +$ SET DEFAULT [-.PAGE] +$ @BUILD +$ SET DEFAULT [-.CPU_HOG] +$ @BUILD +$ SET DEFAULT [-] \ No newline at end of file diff --git a/src/VMS_STRESS/create_stress_user.com b/src/VMS_STRESS/create_stress_user.com new file mode 100644 index 0000000..54dbed6 --- /dev/null +++ b/src/VMS_STRESS/create_stress_user.com @@ -0,0 +1,9 @@ +$ SV_DEF = F$ENVIRONMENT("DEFAULT") +$ SET DEFAULT SYS$SYSTEM +$ RUN AUTHORIZE +COPY SYSTEM STRESS /UIC=[1,30] /ACCOUNT=STRESS /ADD_IDENTIFIER - + /PASSWORD=TEST /PWDMINIMUM=4 /NOPWDEXPIRED - + /PRCLM=50 /FILLM=2000 /BIOLM=500 /DIOLM=500 /ASTLM=500 - + /ENQLM=2000 /TQELM=100 /BYTLM=500000 /JTQUOTA=10000 - + /PGFLQUOTA=400000 +$ SET DEFAULT 'SV_DEF' \ No newline at end of file diff --git a/src/VMS_VSMP/build.com b/src/VMS_VSMP/build.com new file mode 100644 index 0000000..98f5943 --- /dev/null +++ b/src/VMS_VSMP/build.com @@ -0,0 +1,25 @@ +$ IF P1 .EQS. "DEBUG" +$ THEN +$ MACRO := MACRO /DEBUG/LIST/SHOW=BINARY +$ CC := CC /DEBUG=ALL/NOOPTIMIZE/LIST/MACHINE_CODE +$ LINK := LINK/DEBUG +$ ELSE +$ MACRO := MACRO /NODEBUG/LIST/SHOW=BINARY +$ CC := CC /NODEBUG/OPTIMIZE/LIST/MACHINE_CODE +$ LINK := LINK/NOTRACE +$ ENDIF +$! +$ CC VSMP.C + SYS$LIBRARY:SYS$STARLET_C.TLB/LIBRARY +$ LIBRARY/CREATE/MACRO X.MLB XBRANCH.MAR + SYS_DEFS.MAR + SIMHDEF.MAR +$ MACRO KLOAD.MAR + X.MLB/LIBRARY +$ MACRO SMPCORE.MAR + X.MLB/LIBRARY +$ MACRO STARTCPU.MAR + X.MLB/LIBRARY +$ MACRO VIRTCONS.MAR + X.MLB/LIBRARY +$ MACRO TIMESYNC.MAR + X.MLB/LIBRARY +$ MACRO DYNPATCH.MAR + X.MLB/LIBRARY +$ MESSAGE/LIST VSMP_MSG +$ LINK/MAP/FULL VSMP.OBJ + KLOAD.OBJ + VSMP_MSG.OBJ + TIMESYNC.OBJ + - + SMPCORE.OBJ + STARTCPU.OBJ + VIRTCONS.OBJ + DYNPATCH.OBJ + - + SYS$SYSTEM:SYS.STB /SELECTIVE_SEARCH + SYS$INPUT/OPTIONS + COLLECT=KLOAD_CLUSTER, KLOAD_AAA, KLOAD_DATA, KLOAD_DATA_PAGE, KLOAD_CODE, KLOAD_ZZZ +$ VSMP :== $SYS$DISK:[]VSMP \ No newline at end of file diff --git a/src/VMS_VSMP/devices.com b/src/VMS_VSMP/devices.com new file mode 100644 index 0000000..f844d17 --- /dev/null +++ b/src/VMS_VSMP/devices.com @@ -0,0 +1,117 @@ +$! +$! DEVICES.COM - set proper CPU affinity for devices supported by VAX MP +$! as required for virtual multiprocessing +$! +$ PROC_FILE = F$ENVIRONMENT("PROCEDURE") +$ PROC_DIR = F$PARSE(PROC_FILE,,,"DEVICE","SYNTAX_ONLY") + - + F$PARSE(PROC_FILE,,,"DIRECTORY","SYNTAX_ONLY") +$ VSMP :== $'PROC_DIR'VSMP +$ SAY := WRITE SYS$OUTPUT +$! +$ DC_DISK = 1 +$ DC_TAPE = 2 +$ DC_CARD = 65 +$ DC_TERM = 66 +$ DC_LP = 67 +$! +$ DT_CR11 = 1 +$ DT_LP11 = 1 +$ DT_RL01 = 9 +$ DT_RL02 = 10 +$ DT_RX02 = 11 +$ DT_RX04 = 12 +$ DT_TS11 = 4 +$ DT_MW_TSV05 = 14 +$! +$ DEV = "_CRA0" +$ IF F$GETDVI(DEV, "EXISTS") +$ THEN +$ IF (F$GETDVI(DEV, "REMOTE_DEVICE") .EQS. "FALSE") .AND. - + (F$GETDVI(DEV, "DEVCLASS") .EQ. DC_CARD) .AND. - + (F$GETDVI(DEV, "DEVTYPE") .EQ. DT_CR11) +$ THEN +$ CALL SETAF DEV +$ ENDIF +$ ENDIF +$! +$ DEV = "_LPA0" +$ IF F$GETDVI(DEV, "EXISTS") +$ THEN +$ IF (F$GETDVI(DEV, "REMOTE_DEVICE") .EQS. "FALSE") .AND. - + (F$GETDVI(DEV, "DEVCLASS") .EQ. DC_LP) .AND. - + (F$GETDVI(DEV, "DEVTYPE") .EQ. DT_LP11) +$ THEN +$ CALL SETAF DEV +$ ENDIF +$ ENDIF +$! +$ DEV = "_DLA0" +$ IF F$GETDVI(DEV, "EXISTS") +$ THEN +$ IF (F$GETDVI(DEV, "REMOTE_DEVICE") .EQS. "FALSE") .AND. - + (F$GETDVI(DEV, "DEVCLASS") .EQ. DC_DISK) .AND. - + ((F$GETDVI(DEV, "DEVTYPE") .EQ. DT_RL01) .OR. (F$GETDVI(DEV, "DEVTYPE") .EQ. DT_RL02)) +$ THEN +$ CALL SETAF DEV +$ ENDIF +$ ENDIF +$! +$ DEV = "_DYA0" +$ IF F$GETDVI(DEV, "EXISTS") +$ THEN +$ IF (F$GETDVI(DEV, "REMOTE_DEVICE") .EQS. "FALSE") .AND. - + (F$GETDVI(DEV, "DEVCLASS") .EQ. DC_DISK) .AND. - + ((F$GETDVI(DEV, "DEVTYPE") .EQ. DT_RX02) .OR. (F$GETDVI(DEV, "DEVTYPE") .EQ. DT_RX04)) +$ THEN +$ CALL SETAF DEV +$ ENDIF +$ ENDIF +$! +$ DEV = "_MSA0" +$ IF F$GETDVI(DEV, "EXISTS") +$ THEN +$ IF (F$GETDVI(DEV, "REMOTE_DEVICE") .EQS. "FALSE") .AND. - + (F$GETDVI(DEV, "DEVCLASS") .EQ. DC_TAPE) .AND. - + ((F$GETDVI(DEV, "DEVTYPE") .EQ. DT_TS11) .OR. (F$GETDVI(DEV, "DEVTYPE") .EQ. DT_MW_TSV05)) +$ THEN +$ CALL SETAF DEV +$ ENDIF +$ ENDIF +$! +$ CALL TMUX "TXA" +$ CALL TMUX "TXB" +$ CALL TMUX "TXC" +$ CALL TMUX "TXD" +$ CALL TMUX "TXE" +$ CALL TMUX "TXF" +$ CALL TMUX "TXG" +$ CALL TMUX "TXH" +$! +$ CALL TMUX "TTA" +$ CALL TMUX "TTB" +$ CALL TMUX "TTC" +$ CALL TMUX "TTD" +$ CALL TMUX "TTE" +$ CALL TMUX "TTF" +$ CALL TMUX "TTG" +$ CALL TMUX "TTH" +$! +$ EXIT +$! +$TMUX: SUBROUTINE +$ DEV = "_" + P1 + "0" +$ IF F$GETDVI(DEV, "EXISTS") +$ THEN +$ IF (F$GETDVI(DEV, "REMOTE_DEVICE") .EQS. "FALSE") .AND. - + (F$GETDVI(DEV, "DEVCLASS") .EQ. DC_TERM) +$ THEN +$ CALL SETAF DEV +$ ENDIF +$ ENDIF +$ ENDSUBROUTINE +$! +$SETAF: SUBROUTINE +$ ADEV = F$EXTRACT(1, 3, DEV) +$!SAY "Setting affinity for " + ADEV + " ..." +$ VSMP SET AFFINITY 'ADEV'* /CPU=PRIMARY +$ ENDSUBROUTINE diff --git a/src/VMS_VSMP/dynpatch.mar b/src/VMS_VSMP/dynpatch.mar new file mode 100644 index 0000000..0a8245b --- /dev/null +++ b/src/VMS_VSMP/dynpatch.mar @@ -0,0 +1,2474 @@ + .TITLE DYNPATCH + .IDENT /V1.00/ + +;; +;; Kernel-mode loadable code for SIMH VMS virtual SMP utility. +;; DynPatch -- apply patches to running system code loaded in memory. +;; +;; Tested with OpenVMS VAX version 7.3. +;; +;; Module: dynpatch.mar +;; Version: 1.0 +;; Author: Sergey Oboguev (oboguev@yahoo.com) +;; Created: 22-Dec-2011 +;; Revision History: +;; none +;; + .LIBRARY "SYS$LIBRARY:LIB" + + SYS_DEFS ; VMS defines + XBRANCH ; Extended branch instructions + SIMHDEF ; SIMH API definitions + + $CRBDEF ; channel request block + $IRPDEF ; IO request packet + $LDRIMGDEF ; loadable image descriptor + $OPDEF ; VAX opcode definitions + $PTEDEF ; page table entry + $VADEF ; virtual address layout + $VBSDEF ; virtual balance slot flags + $VECDEF ; interrupt vector in CRB + + PATCH_ID_XDELTA == 0 ; patch identifiers + PATCH_ID_CHSEP == 1 ; ... + PATCH_ID_RESCHED == 2 ; ... + PATCH_ID_NUMTIM == 3 ; ... + PATCH_ID_LOCKRTRY == 4 ; ... + PATCH_ID_XQTIMXMT == 5 ; ... + PATCH_ID_UCBTMO == 6 ; ... + PATCH_ID_CRBTMO == 7 ; ... + PATCH_ID_XQTX1 == 8 ; ... + PATCH_ID_XQTX2 == 9 ; ... + PATCH_ID_XQTX3 == 10 ; ... + PATCH_ID_XQTX4 == 11 ; ... + PATCH_ID_XQTX5 == 12 ; ... + PATCH_ID_XQTX6 == 13 ; ... + PATCH_ID_XQTX7 == 14 ; ... + PATCH_ID_XQTX8 == 15 ; ... + PATCH_ID_XQTX9 == 16 ; ... + PATCH_ID_XQTX10 == 17 ; ... + PATCH_ID_XQRX1 == 18 ; ... + PATCH_ID_XQRX2 == 19 ; ... + PATCH_ID_XQRX3 == 20 ; ... + PATCH_ID_XQRX4 == 21 ; ... + PATCH_ID_MFYCAP == 22 ; ... + PATCH_ID_PU1 == 23 ; ... + PATCH_ID_PU2 == 24 ; ... + PATCH_ID_PU3 == 25 ; ... + PATCH_ID_PU4 == 26 ; ... + PATCH_ID_PU5 == 27 ; ... + PATCH_ID_PU6 == 28 ; ... + PATCH_ID_PU7 == 29 ; ... + + PATCH_ID_END_OF_LIST == 255 ; patch list terminator + + PATCH_DESC_ID = 0 ; patch descriptor layout + PATCH_DESC_FLAGS = 1 ; ... + PATCH_DESC_LOOKUP_BEGIN = 4 ; ... + PATCH_DESC_LOOKUP_END = 8 ; ... + PATCH_DESC_TRG_BEGIN = 12 ; ... + PATCH_DESC_TRG_END = 16 ; ... + PATCH_DESC_REPL_BEGIN = 20 ; ... + PATCH_DESC_REPL_END = 24 ; ... + PATCH_DESC_LOOKUP_START = 28 ; ... + PATCH_DESC_LOOKUP_BEFORE = 32 ; ... + PATCH_DESC_LOOKUP_AFTER = 36 ; ... + PATCH_DESC_LOOKUP_FOUND = 40 ; ... + PATCH_DESC_ERROR_STATUS = 44 ; ... + PATCH_DESC_SIZE = 48 ; ... + + PD_V_FILL_NOP = 0 ; fill the remainder of replaced areas with NOPs + PD_V_FILL_HALT = 1 ; fill the remainder of replaced areas with HALTs + ; if neither is set, filled with BPTs + PD_V_NOVERIFY = 2 ; complex pattern, do not verify it by memcmp match + PD_V_NOAPPLY = 3 ; do not apply patch + + PD_M_FILL_NOP = <1@PD_V_FILL_NOP> + PD_M_FILL_HALT = <1@PD_V_FILL_HALT> + PD_M_NOVERIFY = <1@PD_V_NOVERIFY> + PD_M_NOAPPLY = <1@PD_V_NOAPPLY> + + ; + ; Macro to emit patch descriptor + ; + .MACRO PATCH_DESC ID, ERROR_STATUS, - + FLAGS=0, - + LOOKUP_BEGIN, LOOKUP_END, - + TRG_BEGIN, TRG_END, - + REPL_BEGIN, REPL_END, - + LOOKUP_START, LOOKUP_BEFORE, LOOKUP_AFTER + + $$$DESC = . + .BYTE ID + .BYTE FLAGS + .BYTE 0, 0 + + .IF NB, + .LONG LOOKUP_BEGIN - $$$DESC + .LONG LOOKUP_END - $$$DESC + .IFF + .LONG 0, 0 + .ENDC + + .LONG TRG_BEGIN - $$$DESC + .LONG TRG_END - $$$DESC + .LONG REPL_BEGIN - $$$DESC + .LONG REPL_END - $$$DESC + + .IF NB, + .ADDRESS G^LOOKUP_START + .IFF + .LONG 0 + .ENDC + + .IF NB, + .LONG LOOKUP_BEFORE + .IFF + .LONG 0 + .ENDC + + .IF NB, + .LONG LOOKUP_AFTER + .IFF + .LONG 0 + .ENDC + + .LONG 0 + .LONG ERROR_STATUS + + .ENDM PATCH_DESC + + .MACRO ANDL3 A, B, C, TEMP + .IF NB, + MCOML A, TEMP + BICL3 TEMP, B, C + .IFF + MCOML A, -(SP) + BICL3 (SP)+, B, C + .ENDC + .ENDM ANDL3 + + .MACRO MEMBAR ?L +;;; +;;; Version using BBSSI causes VAX MP to perform thread priority elevation/demotion +;;; and is thus more costly than direct service call to the simulator. However +;;; the use of direct service call implies MEMBAR should not be called unless the +;;; machine is known to be VAX MP, rather than regular SIMH VAX or other simulator +;;; or even real VAX. +;;; +;;; BBSSI #0, -4(SP), L +;;;L: .BLKB 0 +;;; + CLRL -(SP) ; 0 = full memory barrier + PUSHL S^#VAXMP_API_OP_MEMBAR ; request code + PUSHL #VAXMP_API_SIGNATURE ; request block signature + MTPR SP, #PR$_SIMH ; signal to SIMH + ADDL S^#<3 * 4>, SP ; pop request block off the stack + .ENDM + + .MACRO DEF_PATCH ID +PATCHDESC_'ID': + PATCH_DESC PATCH_ID_'ID', - + ERROR_STATUS=VSMP_MSG_'ID'_P, - + FLAGS=, - + LOOKUP_BEGIN='ID'_LOOK, - + LOOKUP_END='ID'_LOOK_E, - + TRG_BEGIN='ID'_TRG, - + TRG_END='ID'_TRG_E, - + REPL_BEGIN='ID'_REPL, - + REPL_END='ID'_REPL_E + .ENDM + + ; + ; Pieces of DEQNA/DELQA buffer descriptor layout and CSR structure + ; + QNA_XMT$W_FLAG = 0 + QNA_XMT$W_ADDRHI = 2 + QNA_XMT$W_ADDR = 4 + QNA_XMT$W_LEN = 6 + QNA_XMT$W_STS = 8 + QNA_XMT$W_TDR = 10 + QNA_XMT$C_LENGTH = 12 + ; + QNA_XMT$V_FLAG_LAST = 15 + QNA_XMT$V_DSC_VALID = 15 + QNA_XMT$V_DSC_CHAIN = 14 + QNA_XMT$V_STS_LAST = 15 + QNA_XMT$V_STS_ERR = 14 + QNA_XMT$V_DSC_EOM = 13 + QNA_XMT$V_DSC_SETUP = 12 + ; + QNA_XMT$M_FLAG_LAST = <1 @ QNA_XMT$V_FLAG_LAST> + QNA_XMT$M_DSC_VALID = <1 @ QNA_XMT$V_DSC_VALID> + QNA_XMT$M_DSC_CHAIN = <1 @ QNA_XMT$V_DSC_CHAIN> + QNA_XMT$M_DSC_EOM = <1 @ QNA_XMT$V_DSC_EOM> + QNA_XMT$M_DSC_SETUP = <1 @ QNA_XMT$V_DSC_SETUP> + ; + QNA_RCV$W_FLAG = QNA_XMT$W_FLAG + QNA_RCV$W_ADDRHI = QNA_XMT$W_ADDRHI + QNA_RCV$W_ADDR = QNA_XMT$W_ADDR + QNA_RCV$W_LEN = QNA_XMT$W_LEN + QNA_RCV$W_STS = QNA_XMT$W_STS + QNA_RCV$W_LENB = QNA_XMT$W_STS + 2 + QNA_RCV$C_LENGTH = QNA_XMT$C_LENGTH + ; + QNA_RCV$V_DSC_VALID = QNA_XMT$V_DSC_VALID + QNA_RCV$V_DSC_CHAIN = QNA_XMT$V_DSC_CHAIN + QNA_RCV$V_FLAG_LAST = QNA_XMT$V_FLAG_LAST + QNA_RCV$V_STS_LAST = 15 + QNA_RCV$V_STS_ERR = 14 + ; + QNA_RCV$M_DSC_VALID = <1 @ QNA_RCV$V_DSC_VALID> + QNA_RCV$M_DSC_CHAIN = <1 @ QNA_RCV$V_DSC_CHAIN> + QNA_RCV$M_FLAG_LAST = <1 @ QNA_RCV$V_FLAG_LAST> + ; + QNA_CSR$W_CSR = ^XE + QNA_CSR$M_XMTINV = ^X10 + QNA_CSR$M_RCVENA = ^X1 + + +;;*********************************************************************************** +;; Kernel-resident part that is loaded into nonpaged memory -- data +;;*********************************************************************************** + + .PSECT KLOAD_DATA QUAD, PIC, EXE, NOSHR, WRT +SV_PTE1:.BLKL ; saved code page PTEs while patching these pages +SV_PTE2:.BLKL ; ... + ; + ; list of patch descriptors + ; +PATCHDESC_LIST: + ; + ; XDelta uses WHAMI macro that reads current CPU ID in a machine-specific way. + ; + ; XDelta uses the result to interlock XDT ownership between processors and also + ; to determine whether current processor is primary and can perform console IO. + ; + ; Since existing VMS WHAMI macro does not cover VAX MP multiprocessor, we need + ; to patch XDelta (if it is loaded) before starting the multiprocessing, so + ; WHAMI code returns correct value. + ; + ; On OpenVMS 7.3 target is findable at SYSTEM_DEBUG+00E86. + ; +PATCHDESC_XDELTA: + PATCH_DESC PATCH_ID_XDELTA, - + ERROR_STATUS=VSMP_MSG_XDELTA_P, - + FLAGS=0, - + LOOKUP_BEGIN=XDT_LOOK, - ; for offset calculations only + LOOKUP_END=XDT_LOOK_E, - ; ... not used for actual lookup + TRG_BEGIN=XDT_TRG, - + TRG_END=XDT_TRG_E, - + REPL_BEGIN=XDT_REPL, - + REPL_END=XDT_REPL_E + ; + ; Note: real lookup for XDELTA patch target area is performed by patch-specific + ; LOOKUP_PATCH_XDELTA routine utilizing extended XDT_PAT_LOOK pattern, rather than + ; XDT_LOOK pattern. XDT_LOOK is recorded in the descriptor merely to indicate + ; offset between the pattern and target patch area. + ; + ; + ; When a process is made computable, SCH$CHSEP signals all idle CPUs and makes + ; them attempt rescheduling. This is acceptable on a real VAX or on VAX MP with + ; idle sleep disabled, but it is wastefull to wake up all virtual processors + ; every time a single VMS process becomes computable. + ; + ; CHSEP patch applies more intelligent algorithm for wakeups. + ; + ; On OpenVMS 7.3 lookup target is findable at PROCESS_MANAGEMENT+06EB6, + ; patch replacement target (TRG) at PROCESS_MANAGEMENT+06ED2. + ; +PATCHDESC_CHSEP: + ;; CHSEP patch LOOKUP_START is SCH$CHSEP (internal address) + ^xDD + PATCH_DESC PATCH_ID_CHSEP, - + ERROR_STATUS=VSMP_MSG_CHSEP_P, - + FLAGS=PD_M_FILL_NOP, - + LOOKUP_BEGIN=CHSEP_LOOK, - + LOOKUP_END=CHSEP_LOOK_E, - + TRG_BEGIN=CHSEP_TRG, - + TRG_END=CHSEP_TRG_E, - + REPL_BEGIN=CHSEP_REPL, - + REPL_END=CHSEP_REPL_E, - + LOOKUP_BEFORE=50, - + LOOKUP_AFTER=50 + ; + ; When a process is reentered in COM queue, SCH$RESCHED signals all idle CPUs + ; and makes them attempt rescheduling. This is acceptable on a real VAX or + ; on VAX MP with idle sleep disabled, but it is wastefull to wake up all + ; virtual processors every time a single VMS process becomes computable. + ; + ; RESCHED patch applies more intelligent algorithm for wakeups. + ; + ; On OpenVMS 7.3 lookup target is findable at PROCESS_MANAGEMENT+00EE6, + ; patch replacement target (TRG) at PROCESS_MANAGEMENT+00EE9. + ; +PATCHDESC_RESCHED: + ;; RESCHED patch LOOKUP_START is SCH$RESCHED (internal address) + ^xA2 + PATCH_DESC PATCH_ID_RESCHED, - + ERROR_STATUS=VSMP_MSG_RESCHED_P, - + FLAGS=PD_M_FILL_NOP, - + LOOKUP_BEGIN=RESCHED_LOOK, - + LOOKUP_END=RESCHED_LOOK_E, - + TRG_BEGIN=RESCHED_TRG, - + TRG_END=RESCHED_TRG_E, - + REPL_BEGIN=RESCHED_REPL, - + REPL_END=RESCHED_REPL_E, - + LOOKUP_BEFORE=80, - + LOOKUP_AFTER=80 + ; + ; EXE$NUMTIM accesses EXE$GQ_SYSTIME directly without acquiring HWCLK spinlock + ; and with weak consistency checks. + ; + ; EXE$GQ_SYSTIME should be accessed while holding HWCLK lock. Otherwise reading + ; it won't be correct on processors with weaker memory consistency model. + ; + ; On OpenVMS 7.3 target is findable at MESSAGE_ROUTINES+05373. + ; +PATCHDESC_NUMTIM: + ;; NUMTIM patch LOOKUP_START is MESSAGE_ROUTINES + ^x5373 + ;; could also read GST from MESSAGE_ROUTINES.EXE to locate + ;; the offset of EXE$NUMTIM + 9 + PATCH_DESC PATCH_ID_NUMTIM, - + ERROR_STATUS=VSMP_MSG_NUMTIM_P, - + FLAGS=PD_M_FILL_NOP, - + LOOKUP_BEGIN=NUMTIM_LOOK, - + LOOKUP_END=NUMTIM_LOOK_E, - + TRG_BEGIN=NUMTIM_TRG, - + TRG_END=NUMTIM_TRG_E, - + REPL_BEGIN=NUMTIM_REPL, - + REPL_END=NUMTIM_REPL_E, - + LOOKUP_BEFORE=400, - + LOOKUP_AFTER=400 + ; + ; MFYCAP patches SCH$ADD_CPU_CAP/SCH$REMOVE_CPU_CAP to enusure waking up idle CPUs + ; after capability was modified (original VMS code wakes them up before modification) + ; +PATCHDESC_MFYCAP: + ;; in OpenVMS 7.3 MFYCAP patch is located at target + ^XCD, + ;; where target is pointed by SCH$REMOVE_CPU_CAP: .WORD ^M<...>, JMP @#target + PATCH_DESC PATCH_ID_MFYCAP, - + ERROR_STATUS=VSMP_MSG_MFYCAP_P, - + FLAGS=PD_M_FILL_NOP, - + LOOKUP_BEGIN=MFYCAP_LOOK, - + LOOKUP_END=MFYCAP_LOOK_E, - + TRG_BEGIN=MFYCAP_TRG, - + TRG_END=MFYCAP_TRG_E, - + REPL_BEGIN=MFYCAP_REPL, - + REPL_END=MFYCAP_REPL_E, - + LOOKUP_BEFORE=150, - + LOOKUP_AFTER=150 + ; + ; + ; +PATCHDESC_UCBTMO: + ;; UCBTMO patch lookup area is at EXE$TIMEOUT (internal address)+^XCB + PATCH_DESC PATCH_ID_UCBTMO, - + ERROR_STATUS=VSMP_MSG_UCBTMO_P, - + FLAGS=PD_M_FILL_NOP, - + LOOKUP_BEGIN=UCBTMO_LOOK, - + LOOKUP_END=UCBTMO_LOOK_E, - + TRG_BEGIN=UCBTMO_TRG, - + TRG_END=UCBTMO_TRG_E, - + REPL_BEGIN=UCBTMO_REPL, - + REPL_END=UCBTMO_REPL_E, - + LOOKUP_BEFORE=150, - + LOOKUP_AFTER=150 +PATCHDESC_CRBTMO: + ;; CRBTMO patch lookup area is at EXE$TIMEOUT (internal address)+^X66 + PATCH_DESC PATCH_ID_CRBTMO, - + ERROR_STATUS=VSMP_MSG_CRBTMO_P, - + FLAGS=PD_M_FILL_NOP, - + LOOKUP_BEGIN=CRBTMO_LOOK, - + LOOKUP_END=CRBTMO_LOOK_E, - + TRG_BEGIN=CRBTMO_TRG, - + TRG_END=CRBTMO_TRG_E, - + REPL_BEGIN=CRBTMO_REPL, - + REPL_END=CRBTMO_REPL_E, - + LOOKUP_BEFORE=90, - + LOOKUP_AFTER=150 + ; + ; Patches for XQDRIVER, except for XQTIMXMT patch which is handled separately. + ; Purpose for these patches is described in SIMH file PDP11_XQ.CPP, + ; VAX MP Technical Overview and VAX MP OpenVMS User Guide. + ; In short, provide memory barriers during interaction between DEQNA/DELQA controller + ; and XQDRIVER performed via buffer descriptor list (Rx BDL and Tx BDL). + ; + DEF_PATCH XQTX1 + DEF_PATCH XQTX2 + DEF_PATCH XQTX3 + DEF_PATCH XQTX4 + DEF_PATCH XQTX5 + DEF_PATCH XQTX6 + DEF_PATCH XQTX7 + DEF_PATCH XQTX8 + DEF_PATCH XQTX9 + DEF_PATCH XQTX10 + DEF_PATCH XQRX1 + DEF_PATCH XQRX2 + DEF_PATCH XQRX3 + DEF_PATCH XQRX4 + ; + ; Patches for PUDRIVER. + ; Purpose for these patches is described in SIMH file PDP11_RQ.CPP. + ; In short, provide memory barriers during interaction between RQ/TQ handlers + ; and PUDRIVER performed via comm area (cmd/rsp rings and cmd/rsp interrupt indicators). + ; + DEF_PATCH PU1 + DEF_PATCH PU2 + DEF_PATCH PU3 + DEF_PATCH PU4 + DEF_PATCH PU5 + DEF_PATCH PU6 + DEF_PATCH PU7 + ; + ; list terminator + ; + .BYTE PATCH_ID_END_OF_LIST ; end-of-patch-list marker + ; + ; end of patch descriptors list + ; + +;;; +;;; XDELTA source template (located and matched in loaded XDelta) +;;; +XDT_LOOK: +XDT_TRG: + MOVL (SP),-(SP) + CASEB G^EXE$GB_CPUTYPE, #0, #0 +XDT_TRG_E = . - 2 +XDT_LOOK_E = XDT_TRG_E + +;;; +;;; XDELTA replacement code (replaces XDT_TRG in place) +;;; +XDT_REPL: + MOVL (SP), -(SP) + MFPR S^#PR$_CPUID, 4(SP) + RSB +XDT_REPL_E: + +;;; +;;; CHSEP source template (located and matched in loaded executive image) +;;; + .ENABLE LOCAL_BLOCK +CHSEP_LOOK: +10$: BBSS R0, G^SCH$GL_COMQS, 20$ +20$: MOVW R1, PCB$W_STATE(R4) + MOVAQ G^SCH$AQ_COMT[R0], R2 + INSQUE (R4), @(R2)+ + POPR #^M + RSB +CHSEP_TRG: +30$: BBC #CPB$V_IMPLICIT_AFFINITY, - + PCB$L_CAPABILITY(R4), 40$ + BBSC PCB$L_AFFINITY(R4), - + G^SCH$GL_IDLE_CPUS, 10$ +40$: BICL R5, G^SCH$GL_IDLE_CPUS +CHSEP_TRG_E: + BRB 10$ +CHSEP_LOOK_E: + .DISABLE LOCAL_BLOCK + +;;; +;;; CHSEP replacement code (replaces CHSEP_TRG in place) +;;; +CHSEP_REPL: + JSB @#^X80001234 ; will be JSB @#CHSEP_HANDLER +CHSEP_REPL_E: + +;;; +;;; RESCHED source template (located and matched in loaded executive image) +;;; +RESCHED_LOOK: + INSQUE (R1),@(R2)+ +RESCHED_TRG: + CLRL G^SCH$GL_IDLE_CPUS + CLRL G^VBSS$GL_STALLED_CPUS + BICL #VBS$M_SKP_VBS, G^VBSS$GL_FLAGS +RESCHED_TRG_E: +RESCHED_LOOK_E: + +;;; +;;; RESCHED replacement code (replaces RESCHED_TRG in place) +;;; +RESCHED_REPL: + JSB @#^X80001234 ; will be JSB @#RESCHED_HANDLER +RESCHED_REPL_E: + +;;; +;;; NUMTIM source template (located and matched in loaded executive image) +;;; + .ENABLE LOCAL_BLOCK +NUMTIM_LOOK: +NUMTIM_TRG: +1$: MOVQ G^EXE$GQ_SYSTIME, R1 + CMPL G^EXE$GQ_SYSTIME, R1 + BNEQ 1$ + CMPL G^EXE$GQ_SYSTIME+4, R2 + BNEQ 1$ +NUMTIM_TRG_E: +NUMTIM_LOOK_E: + .DISABLE LOCAL_BLOCK + +;;; +;;; NUMTIM replacement code (replaces NUMTIM_TRG in place) +;;; +NUMTIM_REPL: + JSB @#^X80001234 ; will be JSB @#NUMTIM_HANDLER +NUMTIM_REPL_E: + + .ALIGN LONG +XQ_XMT_TIMEOUT: ; XQDRIVER's XMT timeout value (in seconds) + .BLKL ; ... + + +;;; +;;; MFYCAP source template (located and matched in loaded executive image) +;;; +MFYCAP_LOOK: + MOVL #SS$_NORMAL, R0 +MFYCAP_TRG: + UNLOCK MUTEX=SMP$GL_CPU_MUTEX, PRESERVE=YES, SHARE=YES +MFYCAP_TRG_E: + RET +MFYCAP_LOOK_E: + +;;; +;;; MFYCAP replacement code (replaces MFYCAP_TRG in place) +;;; +MFYCAP_REPL: + JSB @#^X80001234 ; will be JSB @#MFYCAP_HANDLER +MFYCAP_REPL_E: + + +;;; +;;; UCBTMO source template (located and matched in loaded executive image) +;;; + .ENABLE LOCAL_BLOCK +UCBTMO_LOOK: + BBC #5, UCB$B_FLCK(R5), 10$ + FORKLOCK LOCK=UCB$B_FLCK(R5) +10$: DEVICELOCK LOCKADDR=UCB$L_DLCK(R5) + SETIPL #IPL$_POWER, ENVIRON=UNIPROCESSOR + BBC #UCB$V_TIM, UCB$W_STS(R5), 20$ +UCBTMO_TRG: + CMPL UCB$L_DUETIM(R5), G^EXE$GL_ABSTIM +UCBTMO_TRG_E: + BGTRU 20$ + BICW #, UCB$W_STS(R5) + BISW #UCB$M_TIMOUT, UCB$W_STS(R5) + SETIPL UCB$B_DIPL(R5), ENVIRON=UNIPROCESSOR + MOVQ UCB$L_FR3(R5),R3 + MOVL UCB$L_FPC(R5),R2 + CVTWL -(R2), -(SP) + ADDL (SP)+, R2 + JSB (R2) +20$: DEVICEUNLOCK LOCKADDR=UCB$L_DLCK(R5), CONDITION=RESTORE +UCBTMO_LOOK_E: + .DISABLE LOCAL_BLOCK + +;;; +;;; UCBTMO replacement code +;;; +UCBTMO_REPL: + JSB @#^XFFFFFFFF ; will be JSB @#UCBTMO_HANDLER +UCBTMO_REPL_E: + +;;; +;;; CRBTMO source template (located and matched in loaded executive image) +;;; + .ENABLE LOCAL_BLOCK +CRBTMO_LOOK: + MOVAL G^IOC$GL_CRBTMOUT, R6 +10$: MOVL (R6), R6 + BEQL 30$ + FORKLOCK LOCK=CRB$B_FLCK-CRB$L_TIMELINK(R6) +CRBTMO_TRG: + CMPL CRB$L_DUETIME-CRB$L_TIMELINK(R6), G^EXE$GL_ABSTIM +CRBTMO_TRG_E: + BGTRU 20$ + MOVAL -CRB$L_TIMELINK(R6), R3 + MNEGL #1, CRB$L_DUETIME(R3) + JSB @CRB$L_TOUTROUT(R3) +20$: FORKUNLOCK LOCK=CRB$B_FLCK-CRB$L_TIMELINK(R6), NEWIPL=#IPL$_TIMER + BRB 10$ +CRBTMO_LOOK_E: + BRW CRBTMO_DUMMY_1 +30$: BRW CRBTMO_DUMMY_2 +CRBTMO_DUMMY_1: +CRBTMO_DUMMY_2: + .DISABLE LOCAL_BLOCK + +;;; +;;; CRBTMO replacement code +;;; +CRBTMO_REPL: + JSB @#^XFFFFFFFF ; will be JSB @#CRBTMO_HANDLER +CRBTMO_REPL_E: + +;;; +;;; XQTX1 patch source template, located and matched in loaded XQDRIVER +;;; Module [PHV_LAN.SRC]DEQNA.MAR, routine UNMAP_XMTBUF +;;; OpenVMS 7.3 location XQDRIVER + ^X1556 +;;; +XQTX1_LOOK:: + MOVL #1, R0 +XQTX1_TRG: + EXTZV #QNA_XMT$V_STS_ERR, #2, QNA_XMT$W_STS(R6), R2 +XQTX1_TRG_E: + CASEB R2, #0, #3 +XQTX1_LOOK_E:: + +;;; +;;; XQTX2 patch source template, located and matched in loaded XQDRIVER +;;; Module [PHV_LAN.SRC]DEQNA.MAR, routine UNMAP_XMTBUF +;;; OpenVMS 7.3 location XQDRIVER + ^X1574 +;;; +XQTX2_LOOK:: + MOVL #10, R5 +XQTX2_TRG: + EXTZV #QNA_XMT$V_STS_ERR, #2, QNA_XMT$W_STS(R6), R2 +XQTX2_TRG_E: + CASEB R2, #0, #3 +XQTX2_LOOK_E:: + +;;; +;;; XQTX3 patch source template, located and matched in loaded XQDRIVER +;;; Module [PHV_LAN.SRC]DEQNA.MAR, routine UNMAP_XMTBUF +;;; OpenVMS 7.3 location XQDRIVER + ^X159A +;;; +XQTX3_LOOK:: + INCL R0 + MOVL R6, R5 + MOVL ^X0A20(R4)[R1],R6 +XQTX3_TRG: + EXTZV #QNA_XMT$V_STS_ERR, #2, QNA_XMT$W_STS(R6), R2 +XQTX3_TRG_E: + CASEB R2, #0, #2 +XQTX3_LOOK_E:: + +; +; "1" in the mask means to ignore the difference in corresponding byte. +; This way pattern lookup algorithm adapts to variability (across system versions) +; of structure field offsets encoded into instructions and also branch offsets +; as long as these offsets remain in the same size (word/byte) category. +; +XQTX3_LOOK_MASK:: + .BYTE 0[2] ; INCL + .BYTE 0[3] ; MOVL + .BYTE 0, 0, 0, 1, 1, 0 ; MOVL + .BYTE 0[6] ; EXTZV + .BYTE 0[4] ; CASEB +XQTX3_LOOK_MASK_E:: + +XQTX1_REPL: XQTX2_REPL: XQTX3_REPL: + JSB @#^X80001234 ; will be JSB @#XQTX123_HANDLER +XQTX1_REPL_E: XQTX2_REPL_E: XQTX3_REPL_E: + +;;; +;;; XQTX4 patch source template, located and matched in loaded XQDRIVER +;;; Module [PHV_LAN.SRC]DEQNA.MAR, routine UNMAP_XMTBUF +;;; OpenVMS 7.3 location XQDRIVER + ^X15E6 +;;; +XQTX4_LOOK:: + DEVICELOCK LOCKADDR=UCB$L_DLCK(R5), SAVIPL=-(SP), PRESERVE=YES + DSBINT #IPL$_MEGA, ENVIRON=UNIPROCESSOR +XQTX4_TRG: + BBC #QNA_XMT$V_STS_LAST, QNA_XMT$W_STS(R6), XQTX4_LOOK + BBS #QNA_XMT$V_STS_ERR, QNA_XMT$W_STS(R6), XQTX4_LOOK +XQTX4_TRG_E: + BITW #QNA_CSR$M_XMTINV, QNA_CSR$W_CSR(R2) + BEQL XQTX4_LOOK + ENBINT +XQTX4_LOOK_E:: + +XQTX4_LOOK_MASK:: + ASSUME EQ 37 + .BYTE 0[37] ; DEVICELOCK + DSBINT + .BYTE 0[4], 1 ; BBC + .BYTE 0[4], 1 ; BBS + .BYTE 0[4] ; BITW + .BYTE 0, 1 ; BEQL + .BYTE 0[3] ; ENBINT +XQTX4_LOOK_MASK_E:: + +XQTX4_REPL: + JSB @#^X80001234 ; will be JSB @#XQTX4_HANDLER +XQTX4_REPL_E: + +;;; +;;; XQRX1 patch source template, located and matched in loaded XQDRIVER +;;; Module [PHV_LAN.SRC]DEQNA.MAR, routine NEXTMSG +;;; OpenVMS 7.3 location XQDRIVER + ^X12EB +;;; + .ENABLE LOCAL_BLOCK +XQRX1_LOOK:: + MOVZBL ^X08A0(R4), R6 + MOVL ^X09A0(R4)[R6], R6 +XQRX1_TRG: + BBC #QNA_RCV$V_STS_LAST, QNA_RCV$W_STS(R6), 10$ + BBC #QNA_RCV$V_STS_ERR, QNA_RCV$W_STS(R6), XQRX1_LOOK +10$: CMPB QNA_RCV$W_LENB(R6), QNA_RCV$W_LENB+1(R6) +XQRX1_TRG_E: + BNEQ XQRX1_LOOK + MOVL G^EXE$GL_ABSTIM_TICS, ^X96(R4) +XQRX1_LOOK_E:: + +XQRX1_LOOK_MASK:: + .BYTE 0, 0, 1, 1, 0 ; MOVZBL + .BYTE 0, 0, 0, 1, 1, 0 ; MOVL + .BYTE 0[5] ; BBC + .BYTE 0[4], 1 ; BBC + .BYTE 0[5] ; CMPB + .BYTE 0, 1 ; BNEQ + .BYTE 0[7], 1, 1 ; MOVL +XQRX1_LOOK_MASK_E:: + .DISABLE LOCAL_BLOCK + +XQRX1_REPL: + JSB @#^X80001234 ; will be JSB @#XQRX1_HANDLER +XQRX1_REPL_E: + +;;; +;;; XQRX2/XQTX5 patch source template, located and matched in loaded XQDRIVER +;;; Module [PHV_LAN.SRC]DEQNA.MAR, routine INIT_DEQNA +;;; OpenVMS 7.3 location XQDRIVER + ^XADA +;;; + .ENABLE LOCAL_BLOCK +XQRX2_LOOK:: +XQTX5_LOOK:: + MOVZBL ^X810(R4), R7 +10$: MOVW #QNA_RCV$M_FLAG_LAST, QNA_RCV$W_FLAG(R1) + CLRW QNA_RCV$W_STS(R1) +XQRX2_TRG: + BICW #, QNA_RCV$W_ADDRHI(R1) +XQRX2_TRG_E: + MNEGW R0, QNA_RCV$W_LEN(R1) + ADDL #QNA_RCV$C_LENGTH, R1 + SOBGTR R7, 10$ + MOVAB ^XB10(R4), R1 + MOVZBL ^X810(R4), R7 +20$: MOVW #QNA_XMT$M_FLAG_LAST, QNA_XMT$W_FLAG(R1) + CLRW QNA_XMT$W_STS(R1) +XQTX5_TRG: + BICW #, QNA_XMT$W_ADDRHI(R1) +XQTX5_TRG_E: + ADDL #QNA_XMT$C_LENGTH, R1 + SOBGTR R7, 20$ +XQRX2_LOOK_E:: +XQTX5_LOOK_E:: + +XQRX2_LOOK_MASK:: +XQTX5_LOOK_MASK:: + .BYTE 0, 0, 1, 1, 0 ; MOVZBL + .BYTE 0[5] ; MOVW + .BYTE 0[3] ; CLRW + .BYTE 0[6] ; BICW + .BYTE 0[4] ; MNEGW + .BYTE 0[3] ; ADDL + .BYTE 0[3] ; SOBGTR + .BYTE 0, 0, 1, 1, 0 ; MOVAB + .BYTE 0, 0, 1, 1, 0 ; MOVZBL + .BYTE 0[5] ; MOVW + .BYTE 0[3] ; CLRW + .BYTE 0[6] ; BICW + .BYTE 0[3] ; ADDL + .BYTE 0[3] ; SOBGTR +XQRX2_LOOK_MASK_E:: +XQTX5_LOOK_MASK_E:: + .DISABLE LOCAL_BLOCK + +XQRX2_REPL: + JSB @#^X80001234 ; will be JSB @#XQRX2_HANDLER +XQRX2_REPL_E: +XQTX5_REPL: + JSB @#^X80001234 ; will be JSB @#XQTX5_HANDLER +XQTX5_REPL_E: + +;;; +;;; XQTX6 patch source template, located and matched in loaded XQDRIVER +;;; Module [PHV_LAN.SRC]DEQNA.MAR, routine QNA_XMIT +;;; OpenVMS 7.3 location XQDRIVER + ^XE89 +;;; + .ENABLE LOCAL_BLOCK +XQTX6_LOOK:: + BSBW XQTX6_LOOK +XQTX6_TRG: + BISW #, R8 + MOVW R8, QNA_XMT$W_ADDRHI(R6) +XQTX6_TRG_E: + POPR #^M + BISW #QNA_XMT$M_DSC_VALID, R8 + BRW XQTX6_LOOK +XQTX6_LOOK_E:: + +XQTX6_LOOK_MASK:: + .BYTE 0, 1, 1 ; BSBW + .BYTE 0[5] ; BISW + .BYTE 0[4] ; MOVW + .BYTE 0[4] ; POPR + .BYTE 0[5] ; BISW + .BYTE 0, 1, 1 ; BRW +XQTX6_LOOK_MASK_E:: + .DISABLE LOCAL_BLOCK + +XQTX6_REPL: + JSB @#^X80001234 ; will be JSB @#XQTX6_HANDLER +XQTX6_REPL_E: + +;;; +;;; XQTX7/XQTX8 patch source template, located and matched in loaded XQDRIVER +;;; Module [PHV_LAN.SRC]DEQNA.MAR, routine UNMAP_XMTBUF +;;; OpenVMS 7.3 location XQDRIVER + ^X16A2 +;;; + .ENABLE LOCAL_BLOCK +XQTX7_LOOK:: +XQTX8_LOOK:: + INCB ^X880(R4) + BICB ^X882(R4), ^X870(R4) +XQTX7_TRG: + BICW #QNA_XMT$M_DSC_VALID, QNA_XMT$W_ADDRHI(R5) +XQTX7_TRG_E: + DECB ^XD0(R4) + BEQL 10$ + MOVB ^X0D51(R4), ^X0D50(R4) +10$: INCB ^X880(R4) + BICB ^X890(R4), ^X0880(R4) +XQTX8_TRG: + BICW #QNA_XMT$M_DSC_VALID, QNA_XMT$W_ADDRHI(R6) +XQTX8_TRG_E: + CMPB #DYN$C_VCRP, IRP$B_TYPE(R3) +XQTX7_LOOK_E:: +XQTX8_LOOK_E:: + +XQTX7_LOOK_MASK:: +XQTX8_LOOK_MASK:: + .BYTE 0, 0, 1, 1 ; INCB + .BYTE 0, 0, 1, 1, 0, 1, 1 ; BICB + .BYTE 0[6] ; BICW + .BYTE 0, 0, 1, 1 ; DECB + .BYTE 0[2] ; BEQL + .BYTE 0, 0, 1, 1, 0, 1, 1 ; MOVB + .BYTE 0, 0, 1, 1 ; INCB + .BYTE 0, 0, 1, 1, 0, 1, 1 ; BICB + .BYTE 0[6] ; BICW + .BYTE 0[5] ; CMPB +XQTX7_LOOK_MASK_E:: +XQTX8_LOOK_MASK_E:: + .DISABLE LOCAL_BLOCK + + +XQTX7_REPL: + JSB @#^X80001234 ; will be JSB @#XQTX7_HANDLER +XQTX7_REPL_E: +XQTX8_REPL: + JSB @#^X80001234 ; will be JSB @#XQTX8_HANDLER +XQTX8_REPL_E: + +;;; +;;; XQTX9 patch source template, located and matched in loaded XQDRIVER +;;; Module [PHV_LAN.SRC]DEQNA.MAR, routine XMT_COMPLETE +;;; OpenVMS 7.3 location XQDRIVER + ^X1485 +;;; + .ENABLE LOCAL_BLOCK +XQTX9_LOOK:: + MOVL R0, R1 + ASHL #16, R0, R0 + MOVW #SS$_NORMAL, R0 +XQTX9_TRG: + MOVL G^EXE$GL_ABSTIM_TICS, ^XA8(R4) + BBSC #QNA_XMT$V_DSC_SETUP, QNA_XMT$W_ADDRHI(R6), XQTX9_LOOK +XQTX9_TRG_E: + BSBW XQTX9_LOOK + JSB @^X720(R4) +XQTX9_LOOK_E:: + +XQTX9_LOOK_MASK:: + .BYTE 0[3] ; MOVL + .BYTE 0[4] ; ASHL + .BYTE 0[3] ; MOVW + .BYTE 0[7], 1, 1 ; MOVL + .BYTE 0[4], 1 ; BBSC + .BYTE 0, 1, 1 ; BSBW + .BYTE 0, 0, 1, 1 ; JSB +XQTX9_LOOK_MASK_E:: + .DISABLE LOCAL_BLOCK + +XQTX9_REPL: + JSB @#^X80001234 ; will be JSB @#XQTX9_HANDLER +XQTX9_REPL_E: + +;;; +;;; XQTX10 patch source template, located and matched in loaded XQDRIVER +;;; Module [PHV_LAN.SRC]DEQNA.MAR, routine QNA_XMIT +;;; OpenVMS 7.3 location XQDRIVER + ^XEF3 +;;; + .ENABLE LOCAL_BLOCK +XQTX10_LOOK:: + BISW #QNA_XMT$M_DSC_SETUP, R8 + MOVL ^X0870(R4), R2 + BICW #QNA_CSR$M_RCVENA, QNA_CSR$W_CSR(R2) +XQTX10_TRG: + DEVICELOCK LOCKADDR=UCB$L_DLCK(R5), - + SAVIPL=-(SP), - + PRESERVE=NO + MOVW R8, QNA_XMT$W_ADDRHI(R6) +XQTX10_TRG_E: +XQTX10_LOOK_E:: + +XQTX10_LOOK_MASK:: + .BYTE 0[5] ; BISW + .BYTE 0, 0, 1, 1, 0 ; MOVL + .BYTE 0[4] ; BICW + ASSUME EQ 30 + .BYTE 0[26] ; DEVICELOCK + .BYTE 0[4] ; MOVW +XQTX10_LOOK_MASK_E:: + .DISABLE LOCAL_BLOCK + +XQTX10_REPL: + JSB @#^X80001234 ; will be JSB @#XQTX10_HANDLER +XQTX10_REPL_E: + +;;; +;;; XQRX3 patch source template, located and matched in loaded XQDRIVER +;;; Module [PHV_LAN.SRC]DEQNA.MAR, routine QNA_START_RECEIVE +;;; OpenVMS 7.3 location XQDRIVER + ^XCDB +;;; + .ENABLE LOCAL_BLOCK +XQRX3_LOOK:: + MOVL (R7), (R1) + JSB G^IOC$LOADALTMAP + BLBS R0, 10$ + BRW XQRX3_LOOK +10$: + MOVQ (SP)+, R3 + MOVW #1, QNA_RCV$W_LENB(R6) +XQRX3_TRG: + BISW #QNA_RCV$M_DSC_VALID, QNA_RCV$W_ADDRHI(R6) +XQRX3_TRG_E: +XQRX3_LOOK_E:: + +XQRX3_LOOK_MASK:: + .BYTE 0[4] ; MOVL + .BYTE 0[6] ; JSB + .BYTE 0[3] ; BLBS + .BYTE 0, 1, 1 ; BRW + .BYTE 0[3] ; MOVQ + .BYTE 0[4] ; MOVW + .BYTE 0[6] ; BISW +XQRX3_LOOK_MASK_E:: + .DISABLE LOCAL_BLOCK + +XQRX3_REPL: + JSB @#^X80001234 ; will be JSB @#XQRX3_HANDLER +XQRX3_REPL_E: + +;;; +;;; XQRX4 patch source template, located and matched in loaded XQDRIVER +;;; Module [PHV_LAN.SRC]DEQNA.MAR, routine NEXTMSG +;;; OpenVMS 7.3 location XQDRIVER + ^X1307 +;;; + .ENABLE LOCAL_BLOCK +XQRX4_LOOK:: + MOVL G^EXE$GL_ABSTIM_TICS, ^X110(R4) + REMQUE @^X250(R4), R2 + BVS XQRX4_LOOK + DECB ^X1CE(R4) + INCB ^X8A0(R4) +XQRX4_TRG: + BICB ^X8A6(R4), ^X8A0(R4) + BBCC #QNA_RCV$V_DSC_VALID, QNA_RCV$W_ADDRHI(R6), 10$ +10$: +XQRX4_TRG_E: + BBC #QNA_RCV$V_STS_LAST, QNA_RCV$W_STS(R6), XQRX4_LOOK +XQRX4_LOOK_E:: + +XQRX4_LOOK_MASK:: + .BYTE 0[7], 1, 1 ; MOVL + .BYTE 0, 0, 1, 1, 0 ; REMQUE + .BYTE 0, 1 ; BVS + .BYTE 0, 0, 1, 1 ; DECB + .BYTE 0, 0, 1, 1 ; INCB + .BYTE 0, 0, 1, 1, 0, 1, 1 ; BICB + .BYTE 0[5] ; BBCC + .BYTE 0[4], 1 ; BBC +XQRX4_LOOK_MASK_E:: + .DISABLE LOCAL_BLOCK + +XQRX4_REPL: + JSB @#^X80001234 ; will be JSB @#XQRX4_HANDLER +XQRX4_REPL_E: + +;;; +;;; PU1 patch source template, located and matched in loaded PUDRIVER +;;; Module [DRIVER.SRC]PUDRIVER.MAR, routine PU$INT +;;; OpenVMS 7.3 location PUDRIVER + ^X2D40 +;;; +PU1_LOOK:: + MOVL @(SP)+, R3 + MOVL IDB$L_OWNER(R3), R5 +PU1_DLCK: + DEVICELOCK LOCKADDR=UCB$L_DLCK(R5), - + CONDITION=NOSETIPL, PRESERVE=NO +PU1_DLCK_E: + MOVL ^X100(R5), R4 + MOVL ^X300(R4), R3 +PU1_RCSR: + READ_CSR @^XDA(R5), 188(R5), LENGTH=WORD +PU1_RCSR_E: +PU1_TRG: + MOVL ^X400(R4), R0 +PU1_LOOK_BEQL: + BEQL PU1_LOOK_E +PU1_TRG_E: +PU1_LOOK_E:: + + ; + ; READ_CSR_SA_UDASA_MASK is likely to depend on VMS version + ; + .MACRO READ_CSR_SA_UDASA_MASK + RCSR_SA_UDASA_MASK = . + .BYTE 0[8] ; BBC + .BYTE 0[2] ; CLRL + .BYTE 0[2] ; PUSHL + .BYTE 0[6] ; PUSHL + .BYTE 0[2] ; PUSHL + .BYTE 0, 0, 1, 1 ; PUSHAL + .BYTE 0[7] ; CALLS + .BYTE 0, 0, 0, 1, 1 ; CVTLW + .BYTE 0[2] ; BRB + .BYTE 0, 0, 1, 1, 0, 1, 1 ; MOVW + RCSR_SA_UDASA_MASK_E = . + .ENDM + +PU1_LOOK_MASK:: + .BYTE 0[3] ; MOVL + .BYTE 0[4] ; MOVL + ASSUME EQ 17 + .BYTE 0[17] ; DEVICELOCK + .BYTE 0, 0, 1, 1, 0 ; MOVL + .BYTE 0, 0, 1, 1, 0 ; MOVL + READ_CSR_SA_UDASA_MASK ; READ_CSR + ASSUME EQ + .BYTE 0, 0, 1, 1, 0 ; MOVL + .BYTE 0, 1 ; BEQL +PU1_LOOK_MASK_E:: + +PU1_REPL: + JSB @#^X80001234 ; will be JSB @#PU1_HANDLER +PU1_REPL_E: + +;;; +;;; PU2 patch source template, located and matched in loaded PUDRIVER +;;; Module [DRIVER.SRC]PUDRIVER.MAR, routine INSERT_IN_RRING +;;; OpenVMS 7.3 location PUDRIVER + ^X2842 +;;; +PU2_LOOK:: + EXTZV #0,^X3FA(R4),^X28C(R4),R0 + MOVL R2,^X31E(R4)[R0] + MOVB R0,6(R2) + MOVB #1,7(R2) + SUBL3 #^X12,^X3C2(R4),^X12(R2) +PU2_TRG: + MOVL ^XA(R2),^X412(R4)[R0] +PU2_TRG_E: + ADAWI #1,^X28A(R4) + INCB ^X28C(R4) + RSB +PU2_LOOK_E:: + +PU2_LOOK_MASK:: + .BYTE 0, 0, 0, 1, 1, 0, 1, 1, 0 ; EXTZV + .BYTE 0, 0, 0, 0, 1, 1 ; MOVL + .BYTE 0, 0, 0, 1 ; MOVB + .BYTE 0, 0, 0, 1 ; MOVB + .BYTE 0, 1, 0, 1, 1, 0, 1 ; SUBL3 + .BYTE 0, 0, 1, 0, 0, 1, 1 ; MOVL + .BYTE 0, 0, 0, 1, 1 ; ADAWI + .BYTE 0, 0, 1, 1 ; INCB + .BYTE 0 ; RSB +PU2_LOOK_MASK_E:: + +PU2_REPL: + JSB @#^X80001234 ; will be JSB @#PU2_HANDLER +PU2_REPL_E: + +;;; +;;; PU3 patch source template, located and matched in loaded PUDRIVER +;;; Module [DRIVER.SRC]PUDRIVER.MAR, routine POLL_RSPRING +;;; OpenVMS 7.3 location PUDRIVER + ^X3066 +;;; + + .ENABLE LOCAL_BLOCK +PU3_LOOK:: + JSB G^EXE$IOFORK + BICW #3,UCB$W_DEVSTS(R5) + TSTW ^XBC(R5) + BLSS PU3_LOOK + BBS #5,UCB$W_DEVSTS(R5),PU3_LOOK + BBS #UCB$V_POWER,UCB$W_STS(R5),PU3_LOOK + BBCC #0,^XEC(R5),10$ + BSBW PU3_LOOK +10$: + CLRW ^XEC(R5) + TSTW ^X28C(R4) + BNEQ 30$ +20$: + RSB +30$: +PU3_TRG: + EXTZV #0,^X3FA(R4),^X28C(R4),R0 + TSTL ^X412(R4)[R0] +PU3_TRG_E: + BLSS 20$ +PU3_LOOK_E:: + .DISABLE LOCAL_BLOCK + +PU3_LOOK_MASK:: + .BYTE 0[6] ; JSB (IOFORK) + .BYTE 0, 1, 0, 0 ; BICW + .BYTE 0, 0, 1, 1 ; TSTW + .BYTE 0, 1 ; BLSS + .BYTE 0, 1, 0, 0, 1 ; BBS + .BYTE 0[4], 1 ; BBS + .BYTE 0, 0, 0, 1, 1, 0 ; BBCC + .BYTE 0, 1, 1 ; BSBW + .BYTE 0, 0, 1, 1 ; CLRW + .BYTE 0, 0, 1, 1 ; TSTW + .BYTE 0, 0 ; BNEQ + .BYTE 0 ; RSB + .BYTE 0, 0, 0, 1, 1, 0, 1, 1, 0 ; EXTZV + .BYTE 0, 0, 0, 1, 1 ; TSTL + .BYTE 0, 0 ; BLSS +PU3_LOOK_MASK_E:: + +PU3_REPL: + JSB @#^X80001234 ; will be JSB @#PU3_HANDLER +PU3_REPL_E: + +;;; +;;; PU4 patch source template, located and matched in loaded PUDRIVER +;;; Module [DRIVER.SRC]PUDRIVER.MAR, routine PU$SA_POLL +;;; OpenVMS 7.3 location PUDRIVER + ^X3176 +;;; + +PU4_LOOK:: + MOVL CRB$L_AUXSTRUC(R3),R4 + BSBW PU4_LOOK + MOVL ^XFA(R4),R5 + MOVL ^X266(R4),R0 +PU4_RCSR: + READ_CSR @^XDA(R5), 188(R5), LENGTH=WORD +PU4_RCSR_E: + BLSS PU4_LOOK +PU4_TRG: + EXTZV #0,^X3FA(R4),^X28C(R4),R0 + TSTL ^X412(R4)[R0] +PU4_TRG_E: + BLSS PU4_LOOK +PU4_LOOK_E:: + +PU4_LOOK_MASK:: + .BYTE 0[4] ; MOVL + .BYTE 0, 1, 1 ; BSBW + .BYTE 0, 0, 1, 1, 0 ; MOVL + .BYTE 0, 0, 1, 1, 0 ; MOVL + READ_CSR_SA_UDASA_MASK ; READ_CSR + ASSUME EQ + .BYTE 0, 1 ; BLSS + .BYTE 0, 0, 0, 1, 1, 0, 1, 1, 0 ; EXTZV + .BYTE 0, 0, 0, 1, 1 ; TSTL + .BYTE 0, 1 ; BLSS +PU4_LOOK_MASK_E:: + +PU4_REPL: + JSB @#^X80001234 ; will be JSB @#PU4_HANDLER +PU4_REPL_E: + +;;; +;;; PU5 patch source template, located and matched in loaded PUDRIVER +;;; Module [DRIVER.SRC]PUDRIVER.MAR, routine POLL_CMDRING +;;; OpenVMS 7.3 location PUDRIVER + ^X2CFC +;;; + + .ENABLE LOCAL_BLOCK +PU5_LOOK:: +1$: TSTB ^X38C(R4) + BEQL 20$ +PU5_TRG: + EXTZV #0,^X4FA(R4),^X38A(R4),R2 + TSTL @^X4CA(R4)[R2] +PU5_TRG_E: + BLSS 20$ + MOVL ^X39A(R4)[R2],R2 + PUSHL R2 + INCB ^X38A(R4) + DECB ^X38C(R4) + REMQUE @^X378(R4),R2 + BVS 10$ + BSBW PU5_LOOK +10$: + POPL R2 + BSBW PU5_LOOK + BRB 1$ +20$: + MOVZWL #SS$_NORMAL,R0 + RSB +PU5_LOOK_E:: + .DISABLE LOCAL_BLOCK + +PU5_LOOK_MASK:: + .BYTE 0, 0, 1, 1 ; TSTB + .BYTE 0, 0 ; BEQL + .BYTE 0, 0, 0, 1, 1, 0, 1, 1, 0 ; EXTZV + .BYTE 0, 0, 0, 1, 1 ; TSTL + .BYTE 0, 0 ; BLSS + .BYTE 0, 0, 0, 1, 1, 0 ; MOVL + .BYTE 0, 0 ; PUSHL + .BYTE 0, 0, 1, 1 ; INCB + .BYTE 0, 0, 1, 1 ; DECB + .BYTE 0, 0, 1, 1, 0 ; REMQUE + .BYTE 0, 0 ; BVS + .BYTE 0, 1, 1 ; BSBW + .BYTE 0, 0, 0 ; POPL + .BYTE 0, 1, 1 ; BSBW + .BYTE 0, 0 ; BRB + .BYTE 0, 0, 0 ; MOVZWL + .BYTE 0 ; RSB +PU5_LOOK_MASK_E:: + +PU5_REPL: + JSB @#^X80001234 ; will be JSB @#PU5_HANDLER +PU5_REPL_E: + +;;; +;;; PU6 patch source template, located and matched in loaded PUDRIVER +;;; Module [DRIVER.SRC]PUDRIVER.MAR, routine INSERT_IN_CRING (ENABLE_COMMAND_START) +;;; OpenVMS 7.3 location PUDRIVER + ^X294B +;;; + +PU6_LOOK:: + EXTZV #0,^X4FA(R4),^X38A(R4),R0 + MOVL R2,^X39A(R4)[R0] + MOVZBW R0,^X6(R2) +PU6_TRG: + MOVL ^XE(R2),@^X4CA(R4)[R0] +PU6_TRG_E: + MOVL ^X368(R4),R1 + PUSHL R5 + MOVL ^XFA(R4),R5 + BLBS ^X386(R4),PU6_LOOK +PU6_LOOK_E:: + +PU6_LOOK_MASK:: + .BYTE 0, 0, 0, 1, 1, 0, 1, 1, 0 ; EXTZV + .BYTE 0, 0, 0, 0, 1, 1 ; MOVL + .BYTE 0, 0, 0, 1 ; MOVZBW + .BYTE 0, 0, 1, 0, 0, 1, 1 ; MOVL + .BYTE 0, 0, 1, 1, 0 ; MOVL + .BYTE 0, 0 ; PUSHL + .BYTE 0, 0, 1, 1, 0 ; MOVL + .BYTE 0, 0, 1, 1, 1 ; BLBS +PU6_LOOK_MASK_E:: + +PU6_REPL: + JSB @#^X80001234 ; will be JSB @#PU6_HANDLER +PU6_REPL_E: + +;;; +;;; PU7 patch source template, located and matched in loaded PUDRIVER +;;; Module [DRIVER.SRC]PUDRIVER.MAR, routine CRING_FULL +;;; OpenVMS 7.3 location PUDRIVER + ^X29DA +;;; + +PU7_LOOK:: + INSQUE (R2),@^X37A(R4) + BSBW PU7_LOOK + MOVZWL #SS$_NORMAL,R0 + RSB + BSBW PU7_LOOK +PU7_TRG: + MOVL ^XA(R2),@^X4CA(R4)[R0] +PU7_TRG_E: + BRW PU7_LOOK +PU7_LOOK_E:: + +PU7_LOOK_MASK:: + .BYTE 0, 0, 0, 1, 1 ; INSQUE + .BYTE 0, 1, 1 ; BSBW + .BYTE 0, 0, 0 ; MOVZWL + .BYTE 0 ; RSB + .BYTE 0, 1, 1 ; BSBW + .BYTE 0, 0, 1, 0, 0, 1, 1 ; MOVL + .BYTE 0, 1, 1 ; BRW +PU7_LOOK_MASK_E:: + +PU7_REPL: + JSB @#^X80001234 ; will be JSB @#PU7_HANDLER +PU7_REPL_E: + +;;; +;;; General instruction templates +;;; + +JMP_INSTR: + JMP @#^X80001234 ; JMP G^xxx instruction template +JSB_INSTR: + JSB @#^X80001234 ; JSB G^xxx instruction template + + +;;*********************************************************************************** +;; Kernel-resident part that is loaded into nonpaged memory -- code +;;*********************************************************************************** + + .PSECT KLOAD_CODE QUAD, PIC, EXE, SHR, NOWRT +;+ +; +; Check if dynamic patches can be applied. +; +; Inputs: +; 4(AP) = nopatch mask +; IPL = 31 +; +; Outputs: +; Status in R0. +; +;- + ARG_NOPATCH = 4 +; + .ENTRY CHECK_DYNPATCHES, ^M + .ENABLE LOCAL_BLOCK + MOVAB PATCHDESC_LIST, R7 ; get patch list start address +10$: + MOVZBL PATCH_DESC_ID(R7), R0 ; get patch id + CMPL R0, #PATCH_ID_END_OF_LIST ; end of list? + BEQL 120$ ; eql - reached end, finish + BBS R0, ARG_NOPATCH(AP), 100$ ; is this patch disabled? + TSTL PATCH_DESC_LOOKUP_FOUND(R7) ; was this patch located? + BEQL 150$ ; eql - no + ; + ; verify located patch matches "trg" + ; + BBS #PD_V_NOVERIFY, - ; skip if complex pattern + PATCH_DESC_FLAGS(R7), 100$ ; ... + ADDL3 R7, PATCH_DESC_TRG_BEGIN(R7), R0 ; trg start + ADDL3 R7, PATCH_DESC_TRG_END(R7), R1 ; trg length + SUBL R0, R1 ; ... + MOVL PATCH_DESC_LOOKUP_FOUND(R7), R2 ; located patch pointer to "lookup" + ADDL PATCH_DESC_TRG_BEGIN(R7), R2 ; advance from "lookup" to "trg" + SUBL PATCH_DESC_LOOKUP_BEGIN(R7), R2 ; ... + CMPC3 R1, (R0), (R2) ; compare pattern with located data + BNEQ 150$ ; neq - mismatch +100$: + ADDL #PATCH_DESC_SIZE, R7 ; go check next patch in the list + BRB 10$ ; ... +120$: + MOVZBL #SS$_NORMAL, R0 ; all list had been processed + RET ; return success status to caller +150$: + MOVL PATCH_DESC_ERROR_STATUS(R7), R0 ; return error status + RET ; to the caller + .DISABLE LOCAL_BLOCK + +;+ +; +; Apply dynamic patches. +; +; Inputs: +; 4(AP) = nopatch mask +; IPL = 31 +; Uniprocessor environment +; +; Outputs: +; Patches applied. +; +;- + ARG_NOPATCH = 4 +; + .ENTRY APPLY_DYNPATCHES, ^M + .ENABLE LOCAL_BLOCK + ; + ; fixup replacing patch code + ; + MOVAB CHSEP_HANDLER, - ; fix JSB instruction + CHSEP_REPL + 2 ; ... + MOVAB RESCHED_HANDLER, - ; fix JSB instruction + RESCHED_REPL + 2 ; ... + MOVAB NUMTIM_HANDLER, - ; fix JSB instruction + NUMTIM_REPL + 2 ; ... + MOVAB MFYCAP_HANDLER, - ; fix JSB instruction + MFYCAP_REPL + 2 ; ... + MOVAB UCBTMO_HANDLER, - ; fix JSB instruction + UCBTMO_REPL + 2 ; ... + MOVAB CRBTMO_HANDLER, - ; fix JSB instruction + CRBTMO_REPL + 2 ; ... + MOVAB XQTX123_HANDLER, - ; fix JSB instruction + XQTX1_REPL + 2 ; ... + MOVAB XQTX4_HANDLER, - ; fix JSB instruction + XQTX4_REPL + 2 ; ... + MOVAB XQTX5_HANDLER, - ; fix JSB instruction + XQTX5_REPL + 2 ; ... + MOVAB XQTX6_HANDLER, - ; fix JSB instruction + XQTX6_REPL + 2 ; ... + MOVAB XQTX7_HANDLER, - ; fix JSB instruction + XQTX7_REPL + 2 ; ... + MOVAB XQTX8_HANDLER, - ; fix JSB instruction + XQTX8_REPL + 2 ; ... + MOVAB XQTX9_HANDLER, - ; fix JSB instruction + XQTX9_REPL + 2 ; ... + MOVAB XQTX10_HANDLER, - ; fix JSB instruction + XQTX10_REPL + 2 ; ... + MOVAB XQRX1_HANDLER, - ; fix JSB instruction + XQRX1_REPL + 2 ; ... + MOVAB XQRX2_HANDLER, - ; fix JSB instruction + XQRX2_REPL + 2 ; ... + MOVAB XQRX3_HANDLER, - ; fix JSB instruction + XQRX3_REPL + 2 ; ... + MOVAB XQRX4_HANDLER, - ; fix JSB instruction + XQRX4_REPL + 2 ; ... + MOVAB PU1_HANDLER, - ; fix JSB instruction + PU1_REPL + 2 ; ... + MOVAB PU2_HANDLER, - ; fix JSB instruction + PU2_REPL + 2 ; ... + MOVAB PU3_HANDLER, - ; fix JSB instruction + PU3_REPL + 2 ; ... + MOVAB PU4_HANDLER, - ; fix JSB instruction + PU4_REPL + 2 ; ... + MOVAB PU5_HANDLER, - ; fix JSB instruction + PU5_REPL + 2 ; ... + MOVAB PU6_HANDLER, - ; fix JSB instruction + PU6_REPL + 2 ; ... + MOVAB PU7_HANDLER, - ; fix JSB instruction + PU7_REPL + 2 ; ... + ; + ; process patch list + ; + MOVAB PATCHDESC_LIST, R7 ; patch list start address +10$: + MOVZBL PATCH_DESC_ID(R7), R0 ; get patch id + CMPL R0, #PATCH_ID_END_OF_LIST ; end of list? + XBEQL 120$ ; eql - reached the end, finish + XBBS R0, ARG_NOPATCH(AP), 100$ ; is this patch disabled? + XBBS #PD_V_NOAPPLY, - ; descriptor marked "noapply"? + PATCH_DESC_FLAGS(R7), 100$ ; bs - skip it + ; + ; R4 = replacement code address + ; R6 = replacement code size + ; R8 = target code first VA (VA1) + ; R9 = target code last VA (VA2), including both target-replacement and target-fill areas + ; and pointing to the last byte of target-fill area if any, + ; otherwise last byte of target-replacement area + ; + ADDL3 R7, PATCH_DESC_REPL_BEGIN(R7), R4 ; replacement begin address + ADDL3 R7, PATCH_DESC_REPL_END(R7), R6 ; replacement end address + SUBL R4, R6 ; replacement size + MOVL PATCH_DESC_LOOKUP_FOUND(R7), R8 ; target base address + ADDL PATCH_DESC_TRG_BEGIN(R7), R8 ; ... + SUBL PATCH_DESC_LOOKUP_BEGIN(R7), R8 ; ... + MOVL PATCH_DESC_LOOKUP_FOUND(R7), R9 ; target end address + ADDL PATCH_DESC_TRG_END(R7), R9 ; ... + SUBL PATCH_DESC_LOOKUP_BEGIN(R7), R9 ; ... + DECL R9 ; inclusive only + ; + ; save code page PTEs for VA1 and VA2 + ; (note that it can be the same page) + ; + MOVL G^LDR$GL_SPTBASE, R1 ; get SPT base + EXTZV #VA$V_VPG, #VA$S_SVPN, R8, R0 ; extract VA1 SVPN + MOVL (R1)[R0], SV_PTE1 ; save VA1 SPTE + EXTZV #VA$V_VPG, #VA$S_SVPN, R9, R0 ; extract VA2 SVPN + MOVL (R1)[R0], SV_PTE2 ; save VA2 SPTE + ; + ; change PTEs and invalidate TLB entries + ; + EXTZV #VA$V_VPG, #VA$S_SVPN, R8, R0 ; extract VA1 SVPN + MOVAL (R1)[R0], R0 ; address of VA1 SPTE + INSV #>, - ; set PTE protection = URKW + #PTE$V_PROT, #PTE$S_PROT, (R0) ; ... + MTPR R8, S^#PR$_TBIS ; invalidate TLB + ; + EXTZV #VA$V_VPG, #VA$S_SVPN, R9, R0 ; extract VA2 SVPN + MOVAL (R1)[R0], R0 ; address of VA2 SPTE + INSV #>, - ; set PTE protection = URKW + #PTE$V_PROT, #PTE$S_PROT, (R0) ; ... + MTPR R9, S^#PR$_TBIS ; invalidate TLB + ; + ; copy patch + ; + MOVC3 R6, (R4), (R8) ; copy patch code to target area + ; + ; fill remaining area (if its size > 0) with NOPs or HALTs or BPTs + ; + SUBL3 PATCH_DESC_TRG_BEGIN(R7), - ; calculate "trg" length + PATCH_DESC_TRG_END(R7), R0 ; ... + SUBL R6, R0 ; excess over "repl" + BLEQ 30$ ; leq - do not pad + ADDL3 R6, R8, R1 ; padding start address + MOVB #OP$_NOP, R2 ; select the filler + BBS #PD_V_FILL_NOP, - ; ... + PATCH_DESC_FLAGS(R7), 20$ ; ... + MOVB #OP$_HALT, R2 ; ... + BBS #PD_V_FILL_HALT, - ; ... + PATCH_DESC_FLAGS(R7), 20$ ; ... + MOVB #OP$_BPT, R2 ; ... +20$: + MOVC5 #0, (SP), R2, R0, (R1) ; do pad filling +30$: + ; + ; restore PTEs + ; + MOVL G^LDR$GL_SPTBASE, R1 ; restore SPTE for VA1 + EXTZV #VA$V_VPG, #VA$S_SVPN, R8, R0 ; ... + MOVL SV_PTE1, (R1)[R0] ; ... + MTPR R8, S^#PR$_TBIS ; invalidate TLB + ; + EXTZV #VA$V_VPG, #VA$S_SVPN, R9, R0 ; restore SPTE for VA2 + MOVL SV_PTE2, (R1)[R0] ; ... + MTPR R9, S^#PR$_TBIS ; invalidate TLB +100$: + ADDL #PATCH_DESC_SIZE, R7 ; go process next patch + BRW 10$ ; ... +120$: + RET ; reached the end, return + .DISABLE LOCAL_BLOCK + + +;+ +; +; Store address of patch pattern. +; +; bool_t set_patchdesc_lookup_found(uint32 patch_id, void* addr); +; +;- + ARG_PATCH_ID = 4 + ARG_ADDR = 8 +; + .ENTRY SET_PATCHDESC_LOOKUP_FOUND, ^M + .ENABLE LOCAL_BLOCK + MOVAB PATCHDESC_LIST, R7 ; get patch list start address +10$: + MOVZBL PATCH_DESC_ID(R7), R0 ; get patch id + CMPL R0, ARG_PATCH_ID(AP) ; matches? + BEQL 20$ ; eql - go store + CMPL R0, #PATCH_ID_END_OF_LIST ; end of list? + BEQL 100$ ; eql - reached end, finish + ADDL #PATCH_DESC_SIZE, R7 ; go check next patch in the list + BRB 10$ ; ... +20$: + MOVL ARG_ADDR(AP), - ; save found location to patch table + PATCH_DESC_LOOKUP_FOUND(R7) ; ... + MOVZBL #1, R0 ; return success status + RET ; to the caller +100$: + CLRL R0 ; return failure status + RET ; to the caller + .DISABLE LOCAL_BLOCK + + +;;*********************************************************************************** +;; Handler for CHSEP patch (fixes SCH$CHSEP) +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK +CHSEP_HANDLER: + ; + ; if idle sleep is disabled, use standard VMS algorithm + ; + ; R5 = PCB$L_CURRENT_AFFINITY(R4) + ; + CMPB IDLE_CTRL, #SIM_K_IDLE_ON ; is idle sleep enabled? + BNEQ 70$ ; neq - go use standard VMS algorithm + ; + ; use VSMP specific algorithm - signal only one eligible CPU + ; + BBC #CPB$V_IMPLICIT_AFFINITY, - ; is implicit affinity involved? + PCB$L_CAPABILITY(R4), 10$ ; bc - no + BBSC PCB$L_AFFINITY(R4), - ; yes, see if CPU is idle + G^SCH$GL_IDLE_CPUS, 100$ ; bs - signal the CPU +10$: + PUSHL R0 ; preserve working register + ANDL3 R5, G^SCH$GL_IDLE_CPUS, R0 ; get mask of eligible *and* idle CPUs + FFS #0, #32, R0, R0 ; find first CPU ID in the mask + BEQL 20$ ; eql - should never happen + ASHL R0, #1, R0 ; convert to mask + BICL R0, G^SCH$GL_IDLE_CPUS ; signal one eligible CPUs + POPL R0 ; restore saved register + RSB ; rejoin SCH$CHSEP +20$: + POPL R0 ; restore saved register + BRB 80$ ; use old algorithm + ; + ; VMS standard algorithm - signal all eligible CPUs + ; +70$: + BBC #CPB$V_IMPLICIT_AFFINITY, - ; is implicit affinity involved? + PCB$L_CAPABILITY(R4), 80$ ; bc - no + BBSC PCB$L_AFFINITY(R4), - ; yes, see if CPU is idle + G^SCH$GL_IDLE_CPUS, 100$ ; bs - signal the CPU +80$: BICL R5, G^SCH$GL_IDLE_CPUS ; signal all eligible CPUs +100$: RSB ; rejoin SCH$CHSEP + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Handler for RESCHED patch (fixes SCH$RESCHED) +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK +RESCHED_HANDLER: + ; + ; if idle sleep is disabled, use standard VMS algorithm + ; + CMPB IDLE_CTRL, #SIM_K_IDLE_ON ; is idle sleep enabled? + BNEQ 300$ ; neq - go use standard VMS algorithm + ; + ; use VSMP specific algorithm - signal only one eligible CPU + ; + PUSHL R0 ; save working register + MOVL PCB$L_CURRENT_AFFINITY(R1), R0 ; get affinity of the descheduled process + BITL R0, G^SCH$GL_IDLE_CPUS ; any idle CPUs good for it? + BEQL 290$ ; eql - none, go use standard VMS code + BBS #CPB$V_IMPLICIT_AFFINITY, - ; is implicit affinity involved? + PCB$L_CAPABILITY(R1), 100$ ; bs - yes +10$: + ANDL3 PCB$L_CURRENT_AFFINITY(R1), - ; get mask of eligible *and* idle CPUs + G^SCH$GL_IDLE_CPUS, R0 ; ... + FFS #0, #32, R0, R0 ; find first CPU ID in the mask + BEQL 290$ ; eql - should never happen + ASHL R0, #1, R0 ; convert to mask + BICL R0, G^SCH$GL_IDLE_CPUS ; signal to the CPU + BICL R0, G^VBSS$GL_STALLED_CPUS ; remove it from the stalled mask +20$: + BICL #VBS$M_SKP_VBS, G^VBSS$GL_FLAGS ; enable VBS scheduling + POPL R0 ; restore working register + RSB ; rejoin SCH$RESCHED +100$: + BBSC PCB$L_AFFINITY(R1), - ; signal CPU if available + G^SCH$GL_IDLE_CPUS, 110$ ; bs - was available + BRB 10$ ; else go signal first eligible +110$: + BBCC PCB$L_AFFINITY(R1), - ; remove signalled CPU from + G^VBSS$GL_STALLED_CPUS, 20$ ; the stalled CPUs set + BRB 20$ ; join common exit + ; + ; rejoin VMS standard algorithm + ; +290$: POPL R0 ; restore working register + ; + ; VMS standard algorithm - signal all CPUs + ; +300$: + CLRL G^SCH$GL_IDLE_CPUS ; signal all CPUs + CLRL G^VBSS$GL_STALLED_CPUS ; no stalled CPUs + BICL #VBS$M_SKP_VBS, G^VBSS$GL_FLAGS ; enable VBS scheduling + RSB ; rejoin SCH$RESCHED + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Handler for NUMTIM patch (fixes EXE$NUMTIM) +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK +NUMTIM_HANDLER: + PUSHL R0 ; save scratch register + CLRQ -(SP) ; allocate buffer for time on stack + PUSHL SP ; build arglist for CMKRNL + PUSHL #1 ; ... + MOVL SP, R0 ; address of arglist + $CMKRNL_S ROUTIN=NUMTIM_K, ARGLST=(R0) ; call to get time + BLBC R0, 100$ ; lbc - unexpected, failed to switch to kernel, + ; go use old code + ADDL #8, SP ; remove arglist off the stack + MOVQ (SP)+, R1 ; move time to R1, R2 + POPL R0 ; restore scratch register + RSB ; rejoin EXE$NUMTIM + ; + ; comes here if failed to switch to kernel mode + ; (should never happen, since we are in exec mode) + ; +100$: + ADDL #16, SP ; remove arglist and time buffer off the stack + POPL R0 ; restore scratch register + ; + ; read system time the original NUMTIM (bad) way + ; +105$: + MOVQ G^EXE$GQ_SYSTIME, R1 ; read SYSTIME + CMPL G^EXE$GQ_SYSTIME, R1 ; re-read if had changed + BNEQ 105$ ; ... + CMPL G^EXE$GQ_SYSTIME+4, R2 ; ... + BNEQ 105$ ; ... + RSB ; rejoin EXE$NUMTIM + + .ENTRY NUMTIM_K, ^M<> + READ_SYSTIME @4(AP) ; read time the proper way + MOVZBL #SS$_NORMAL, R0 ; return success status + RET ; to the caller + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Handler for MFYCAP patch (fixes SCH$ADD_CPU_CAP/SCH$REMOVE_CPU_CAP) +;;*********************************************************************************** + +MFYCAP_HANDLER: + MEMBAR + CLRL G^SCH$GL_IDLE_CPUS + UNLOCK MUTEX=SMP$GL_CPU_MUTEX, PRESERVE=YES, SHARE=YES + RSB + +;;*********************************************************************************** +;; Handler for UCBTMO patch (currently a placeholder) +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK +UCBTMO_HANDLER: + CMPL UCB$L_DUETIM(R5), G^EXE$GL_ABSTIM + RSB + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Handler for CRBTMO patch (currently a placeholder) +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK +CRBTMO_HANDLER: + CMPL CRB$L_DUETIME-CRB$L_TIMELINK(R6), G^EXE$GL_ABSTIM + RSB + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Handler for patches XQTX1, XQTX2, XQTX3 +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK +XQTX123_HANDLER: + MOVW QNA_XMT$W_STS(R6), R2 + EXTZV #QNA_XMT$V_STS_ERR, #2, R2, R2 + MEMBAR + RSB + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Handler for XQTX4 patch +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK +XQTX4_HANDLER: + PUSHL R0 + MOVW QNA_XMT$W_STS(R6), R0 + MEMBAR + BBC #QNA_XMT$V_STS_LAST, R0, 10$ + BBS #QNA_XMT$V_STS_ERR, R0, 10$ + POPL R0 + RSB +10$: + POPL R0 + ADDL #4, SP +XQTX4_HANDLER_JMP:: + JMP @#^X80001234 + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Handlers for patches XQTX5, XQTX6, XQTX7, XQTX8, XQTX9, XQTX10 +;;*********************************************************************************** + +XQTX5_HANDLER: + BICW #, QNA_XMT$W_ADDRHI(R1) + MEMBAR + RSB + +XQTX6_HANDLER: + BISW #, R8 + MEMBAR + MOVW R8, QNA_XMT$W_ADDRHI(R6) + RSB + +XQTX7_HANDLER: + BICW #QNA_XMT$M_DSC_VALID, QNA_XMT$W_ADDRHI(R5) + MEMBAR + RSB + +XQTX8_HANDLER: + BICW #QNA_XMT$M_DSC_VALID, QNA_XMT$W_ADDRHI(R6) + MEMBAR + RSB + + .ENABLE LOCAL_BLOCK +XQTX9_HANDLER: +XQTX9_HANDLER_MOVL:: + .BYTE OP$_BPT[9] ; space for MOVL instruction + BBSC #QNA_XMT$V_DSC_SETUP, QNA_XMT$W_ADDRHI(R6), 10$ + MEMBAR ; really redundant since bit was not changed, but just to be safe... + RSB +10$: + MEMBAR + ADDL #4, SP +XQTX9_HANDLER_JMP:: + JMP @#80001234 ; jump address will be set + .DISABLE LOCAL_BLOCK + +XQTX10_HANDLER: + PUSHL (SP) + DEVICELOCK LOCKADDR=UCB$L_DLCK(R5), - + SAVIPL=-(SP), - + PRESERVE=NO + MOVL (SP), 8(SP) + ADDL #4, SP + MEMBAR ; really redundant after DEVICELOCK + MOVW R8, QNA_XMT$W_ADDRHI(R6) + MEMBAR ; probably unneeded, but just to be safe + RSB + +;;*********************************************************************************** +;; Handler for XQRX1 patch +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK +XQRX1_HANDLER: + PUSHL R0 + MOVW QNA_RCV$W_STS(R6), R0 + MEMBAR + BBC #QNA_RCV$V_STS_LAST, R0, 20$ + BBC #QNA_RCV$V_STS_ERR, R0, 50$ +20$: + POPL R0 + CMPB QNA_RCV$W_LENB(R6), QNA_RCV$W_LENB+1(R6) + RSB +50$: + POPL R0 + TSTB #1 + RSB + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Handler for patches XQRX2, XQRX3 and XQRX4 +;;*********************************************************************************** + +XQRX2_HANDLER: + BICW #, QNA_RCV$W_ADDRHI(R1) + MEMBAR + RSB + +XQRX3_HANDLER: + MEMBAR + BISW #QNA_RCV$M_DSC_VALID, QNA_RCV$W_ADDRHI(R6) + RSB + + .ENABLE LOCAL_BLOCK +XQRX4_HANDLER: +XQRX4_HANDLER_BICB:: + .BYTE OP$_BPT[7] ; space for BICB instruction + BBCC #QNA_RCV$V_DSC_VALID, QNA_RCV$W_ADDRHI(R6), 10$ +10$: + MEMBAR + RSB + .DISABLE LOCAL_BLOCK + +;+ +; +; Apply XQTIMXMT patch to XQDRIVER code +; +; void patch_xqdrv_instr(void* addr_mov_xmt_tmo, uint32 xqtimeout); +; +;- + ARG_ADDR_MOV_XMT_TMO = 4 + ARG_XQTIMEOUT = 8 +; + .ENTRY PATCH_XQDRV_INSTR, ^M<> + MOVL ARG_XQTIMEOUT(AP), - ; store timeout + XQ_XMT_TIMEOUT ; ... + MOVL ARG_ADDR_MOV_XMT_TMO(AP), R0 ; + MOVW 3(R0), XQPATCH_INS_1 + 7 ; copy target offset in MOVB #QNA$C_XMT_TMO, LSB$G_QNA_TIMXMT+1(R4) + MOVL 5(R0), XQPATCH_INS_2 ; copy second instruction (7 bytes long) + MOVW <5 + 4>(R0), XQPATCH_INS_2 + 4 ; i.e. MOVW #QNA$C_SID_MINTIM, LSB$G_QNA_TIMSID+2(R4) + MOVB <5 + 6>(R0), XQPATCH_INS_2 + 6 ; ... + DSBINT #IPL$_POWER, -(SP), - ; block interrupts while we are patching XQDRIVER code + ENVIRON=UNIPROCESSOR ; ... + MOVW JSB_INSTR, (R0) ; replace XQDRIVER's two instructions with + MOVAB XQPATCH_INS_1, 2(R0) ; JSB @#XQPATCH_INS_1 + MOVL #^X01010101, 6(R0) ; and NOPs + MOVW #^X0101, 10(R0) ; ... + JSB FLUSH_INSTRUCTION_STREAM ; flush instructon stream after patching the code + ENBINT (SP)+ ; restore IPL + RET ; return to the caller + ; + ; XQDRIVER's SUB_START_CTRL_TIMER will jump here + ; +XQPATCH_INS_1: + MOVB L^XQ_XMT_TIMEOUT, W^1024(R4) +XQPATCH_INS_2: + .BLKB 7 + RSB + + +;;*********************************************************************************** +;; Handler for patch PU1 +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK + ; + ; note that preceeding READ_CSR in PUDRIVER code just provided memory barrier + ; +PU1_HANDLER: +10$: + .BLKB 5 ; SETUP_PATCH_PU1 will copy MOVL PDT$W_CMDINT(R4),R0 into here + BEQL 20$ + MEMBAR + RSB +20$: + ADDL #4, SP +30$: + JMP @#^XFFFFFFFF ; SETUP_PATCH_PU1 will store actual destination in here + + .ENTRY SETUP_PATCH_PU1, ^M + MOVL 4(AP), R2 ; address of the area in PUDRIVER matching PU1_LOOK + ASSUME EQ 5 + MOVL (R2), 10$ ; copy instruction MOVL PDT$W_CMDINT(R4),R0 + MOVB (R2), 10$ + 4 ; ... + MOVAB (R2), R0 ; address of BEQL instruction + CVTBL 1(R0), R1 ; calculate target address of BEQL + ADDL R0, R1 ; ... + ADDL #2, R1 ; ... + MOVL R1, 30$ + 2 ; store it in PU1_HANDLER code + RET ; return (void) to the caller + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Handler for patch PU2 +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK +PU2_HANDLER: + MEMBAR +10$: + .BLKB 7 ; space for MOVL UDAB$L_DESCRIP(R2),PDT$L_RSPRING(R4)[R0] + ; + ; note that ADAWI executed right after RSB will also provide a memory barrier + ; + RSB + + .ENTRY SETUP_PATCH_PU2, ^M + MOVL 4(AP), R2 ; address of the area in PUDRIVER matching PU2_LOOK + ASSUME EQ 7 + MOVL (R2), 10$ ; copy instruction MOVL UDAB$L_DESCRIP(R2),PDT$L_RSPRING(R4)[R0] + MOVW (R2), 10$ + 4 ; ... + MOVB (R2), 10$ + 6 ; ... + RET ; return (void) to the caller + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Handler for patch PU3 +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK +PU3_HANDLER: + ; + ; SETUP_PATCH_PU3 will copy in the space below instructions + ; EXTZV #0,PDT$B_RINGEXP(R4),PDT$B_RPOLLINX(R4),R0 + ; TSTL PDT$L_RSPRING(R4)[R0] + ; +10$: .BLKB 14 ; space for EXTZV and TSTL + BLSS 20$ ; LSS - return LSS + MEMBAR ; execute memory barrier + TSTB #0 ; return GEQ +20$: + RSB ; return to the caller + + .ENTRY SETUP_PATCH_PU3, ^M + MOVL 4(AP), R2 ; address of the area in PUDRIVER matching PU3_LOOK + ASSUME EQ 14 + MOVL (R2), 10$ ; copy instructions EXTZV and TSTL (9 + 5 = 14 bytes) + MOVL (R2), 10$ + 4 ; ... + MOVL (R2), 10$ + 8 ; ... + MOVW (R2), 10$ + 12 ; ... + RET ; return (void) to the caller + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Handler for patch PU4 +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK +PU4_HANDLER: + ; + ; SETUP_PATCH_PU4 will copy in the space below instructions + ; EXTZV #0,PDT$B_RINGEXP(R4),PDT$B_RPOLLINX(R4),R0 + ; TSTL PDT$L_RSPRING(R4)[R0] + ; +10$: .BLKB 14 ; space for EXTZV and TSTL + BLSS 20$ ; LSS - return LSS + MEMBAR ; execute memory barrier + TSTB #0 ; return GEQ +20$: + RSB ; return to the caller + + .ENTRY SETUP_PATCH_PU4, ^M + MOVL 4(AP), R2 ; address of the area in PUDRIVER matching PU4_LOOK + ASSUME EQ 14 + MOVL (R2), 10$ ; copy instructions EXTZV and TSTL (9 + 5 = 14 bytes) + MOVL (R2), 10$ + 4 ; ... + MOVL (R2), 10$ + 8 ; ... + MOVW (R2), 10$ + 12 ; ... + RET ; return (void) to the caller + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Handler for patch PU5 +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK +PU5_HANDLER: + ; + ; SETUP_PATCH_PU5 will copy in the space below instructions + ; EXTZV #0,PDT$B_RINGEXP(R4),PDT$B_CPOLLINX(R4),R2 + ; TSTL @PDT$L_CMDRING(R4)[R2] + ; +10$: .BLKB 14 ; space for EXTZV and TSTL + BLSS 20$ ; LSS - return LSS + MEMBAR ; execute memory barrier + TSTB #0 ; return GEQ +20$: + RSB ; return to the caller + + .ENTRY SETUP_PATCH_PU5, ^M + MOVL 4(AP), R2 ; address of the area in PUDRIVER matching PU5_LOOK + ASSUME EQ 14 + MOVL (R2), 10$ ; copy instructions EXTZV and TSTL (9 + 5 = 14 bytes) + MOVL (R2), 10$ + 4 ; ... + MOVL (R2), 10$ + 8 ; ... + MOVW (R2), 10$ + 12 ; ... + RET ; return (void) to the caller + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Handler for patch PU6 +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK +PU6_HANDLER: + MEMBAR + ; + ; SETUP_PATCH_PU6 will copy in the space below instruction + ; MOVL UDAB$L_DESCRIP(R2),@PDT$L_CMDRING(R4)[R0] + ; +10$: .BLKB 7 ; space for MOVL + RSB ; return to the caller + ; note that subsequent memory barrier will also be performed by READ_CSR right after returning + + .ENTRY SETUP_PATCH_PU6, ^M + MOVL 4(AP), R2 ; address of the area in PUDRIVER matching PU6_LOOK + ASSUME EQ 7 + MOVL (R2), 10$ ; copy MOVL instruction + MOVW (R2), 10$ + 4 ; ... + MOVB (R2), 10$ + 6 ; ... + RET ; return (void) to the caller + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Handler for patch PU7 +;;*********************************************************************************** + + .ENABLE LOCAL_BLOCK +PU7_HANDLER: + MEMBAR + ; + ; SETUP_PATCH_PU7 will copy in the space below instruction + ; MOVL UDAB$L_DESCRIP(R2),@PDT$L_CMDRING(R4)[R0] + ; +10$: .BLKB 7 ; space for MOVL + RSB ; return to the caller + ; note that subsequent memory barrier will also be performed by READ_CSR right after returning + + .ENTRY SETUP_PATCH_PU7, ^M + MOVL 4(AP), R2 ; address of the area in PUDRIVER matching PU7_LOOK + ASSUME EQ 7 + MOVL (R2), 10$ ; copy MOVL instruction + MOVW (R2), 10$ + 4 ; ... + MOVB (R2), 10$ + 6 ; ... + RET ; return (void) to the caller + .DISABLE LOCAL_BLOCK + +;;*********************************************************************************** +;; Utility routines to assist loader, not relocated into the resident image +;;*********************************************************************************** + + .PSECT $CODE LONG, SHR, NOWRT, PIC, EXE +;+ +; +; Lock/unlock required system pages in memory (system working set). +; Used to lock pageable executive pages that need to be patched. +; +; void k_lock_system_pages(uint32 nopatch); +; void k_unlock_system_pages(uint32 nopatch); +; +; Pages should be locked before patching. +; They can be unlocked only if patching had not been applied. +; +;- + ARG_NOPATCH = 4 +; + .ENTRY K_LOCK_SYSTEM_PAGES, ^M + .ENABLE LOCAL_BLOCK + BBS #PATCH_ID_NUMTIM, - ; bs - skip NUMTIM patch + ARG_NOPATCH(AP), 10$ ; ... + MOVAB PATCHDESC_NUMTIM, R7 ; address of descriptor + SUBL3 PATCH_DESC_TRG_BEGIN(R7), - ; size of target area + PATCH_DESC_TRG_END(R7), R1 ; ... + MOVL PATCH_DESC_LOOKUP_FOUND(R7), R0 ; located patch pointer to "lookup" + BEQL 10$ ; unexpected: patch not found + ADDL PATCH_DESC_TRG_BEGIN(R7), R0 ; advance from "lookup" to "trg" + SUBL PATCH_DESC_LOOKUP_BEGIN(R7), R0 ; ... + JSB G^MMG$LOCK_SYSTEM_PAGES_CALL ; lock system pages +10$: + RET ; return to the caller + .DISABLE LOCAL_BLOCK + + .ENTRY K_UNLOCK_SYSTEM_PAGES, ^M + .ENABLE LOCAL_BLOCK + BBS #PATCH_ID_NUMTIM, - ; bs - skip NUMTIM patch + ARG_NOPATCH(AP), 10$ ; ... + MOVAB PATCHDESC_NUMTIM, R7 ; address of descriptor + SUBL3 PATCH_DESC_TRG_BEGIN(R7), - ; size of target area + PATCH_DESC_TRG_END(R7), R1 ; ... + MOVL PATCH_DESC_LOOKUP_FOUND(R7), R0 ; located patch pointer to "lookup" + BEQL 10$ ; unexpected: patch not found + ADDL PATCH_DESC_TRG_BEGIN(R7), R0 ; advance from "lookup" to "trg" + SUBL PATCH_DESC_LOOKUP_BEGIN(R7), R0 ; ... + JSB G^MMG$UNLOCK_SYSTEM_PAGES_CALL ; unlock system pages +10$: + RET ; return to the caller + .DISABLE LOCAL_BLOCK + + +;+ +; +; Locate loadable executive image. +; Should be called in kernel mode at IPL <= ASTDEL. +; +; uint32 k_locate_ldr_img(int namelen, char* name, uint32* range, uint32* is_valid); +; +; Returns at caller's IPL. +; Always return success status. +; +; If image is not present, range will be set to {0,0} and *is_valid to false. +; If image is present, range will be set to its whole range, and *is_valid to +; ether true or false, depending on whether the image had been initialized. +; +;- + ARG_NAMELEN = 4 + ARG_NAME = 8 + ARG_RANGE = 12 + ARG_ISVALID = 16 +; + .ENABLE LOCAL_BLOCK + .ENTRY K_LOCATE_LDR_IMG, ^M + SAVIPL -(SP) ; save caller's IPL + MOVL G^CTL$GL_PCB, R4 ; lock image list mutex + MOVAB G^EXE$GL_BASIMGMTX, R0 ; ... + JSB G^SCH$LOCKR ; ... + MOVAB G^LDR$GQ_IMAGE_LIST, R6 ; image listhead address + MOVL R6, R7 ; ... +10$: + MOVL (R7), R7 ; get next image block + BGEQ 50$ ; geq - null or invalid pointer + CMPL R7, R6 ; end of list? + BEQL 50$ ; eql - end scan + CMPB LDRIMG$B_IMGNAMLEN(R7), - ; compare image name length + ARG_NAMELEN(AP) ; ... + BNEQ 10$ ; neq - go try next block + MOVZBL ARG_NAMELEN(AP), -(SP) ; compare image name + PUSHL ARG_NAME(AP) ; ... + PUSHAB LDRIMG$T_IMGNAM(R7) ; ... + CALLS #3, K_STREQI_CNT ; ... + BLBC R0, 10$ ; lbc - does not match - go try next + MOVL ARG_RANGE(AP), R0 ; found the block, copy address range + MOVL LDRIMG$L_BASE(R7), (R0) ; ... + MOVL LDRIMG$L_BASE(R7), 4(R0) ; ... + MOVL LDRIMG$L_PAGE_COUNT(R7), R1 ; ... + ASHL #9, R1, R1 ; ... + DECL R1 ; ... + ADDL R1, 4(R0) ; ... + EXTZV #LDRIMG$V_VALID, #1, - ; copy "valid" flag + LDRIMG$L_FLAGS(R7), - ; to the caller's location + @ARG_ISVALID(AP) ; ... +20$: + MOVL G^CTL$GL_PCB, R4 ; unlock the mutex + MOVAB G^EXE$GL_BASIMGMTX, R0 ; ... + JSB G^SCH$UNLOCK ; ... + SETIPL (SP)+, - ; restore caller's IPL + ENVIRON=UNIPROCESSOR ; ... + MOVZBL #SS$_NORMAL, R0 ; always return success status + RET ; to the caller +50$: + MOVL ARG_RANGE(AP), R0 ; image not found: clear range + CLRQ (R0) ; ... + CLRL @ARG_ISVALID(AP) ; and "valid" flag + BRB 20$ ; return to the caller + .DISABLE LOCAL_BLOCK + +;+ +; +; Lookup patches. Check if required patch areas are present and findable and sets up +; internal table with addresses of code to be patched. +; +; Called in user mode. +; +; uint32 lookup_patches(uint32 nopatch); +; +; Return VMS-structured status. +; +; Note that some patches are found elsewhere, such as in prepare_xqdrv_patches(). +; +;- + ARG_NOPATCH = 4 +; + .ENABLE LOCAL_BLOCK + .ENTRY LOOKUP_PATCHES, ^M + BBS #PATCH_ID_XDELTA, - ; check if XDELTA patch is disabled + ARG_NOPATCH(AP), 10$ ; bs - skip it + MOVL #VSMP_MSG_XDELTA_P, R2 ; assume error status code + CALLS #0, LOOKUP_PATCH_XDELTA ; lookup patch + XBLBC R0, 310$ ; lbc - return error +10$: + BBS #PATCH_ID_CHSEP, - ; check if CHSEP patch is disabled + ARG_NOPATCH(AP), 20$ ; bs - skip it + PUSHL #^X00DD ; offset past transfer address + PUSHAB G^SCH$CHSEP ; transfer vector + PUSHAB PATCHDESC_CHSEP ; descriptor address + CALLS #3, LOOKUP_PATCH_D ; lookup CHSEP pach + XBLBC R0, 300$ ; lbc - return error +20$: + BBS #PATCH_ID_RESCHED, - ; check if RESCHED patch is disabled + ARG_NOPATCH(AP), 30$ ; bs - skip it + PUSHL #^X00A2 ; offset past transfer address + PUSHAB G^SCH$RESCHED ; transfer vector + PUSHAB PATCHDESC_RESCHED ; descriptor address + CALLS #3, LOOKUP_PATCH_D ; lookup RESCHED pach + XBLBC R0, 300$ ; lbc - return error +30$: + BBS #PATCH_ID_NUMTIM, - ; check if NUMTIM patch is disabled + ARG_NOPATCH(AP), 40$ ; bs - skip it + MOVL #VSMP_MSG_NUMTIM_P, R2 ; assume error + TSTL MSG_RTNS_RANGE ; check if message_routines.exe is located + XBEQL 310$ ; eql - return error + ADDL3 #^X5373, MSG_RTNS_RANGE, -(SP) ; load approx. address + PUSHAB PATCHDESC_NUMTIM ; descriptor address + CALLS #2, LOOKUP_PATCH_DA ; lookup NUMTIM pach + XBLBC R0, 300$ ; lbc - return error +40$: + BBS #PATCH_ID_UCBTMO, - ; check if UCBTMO patch is disabled + ARG_NOPATCH(AP), 50$ ; bs - skip it + MOVL #VSMP_MSG_UCBTMO_P, R2 ; assume error + CMPW G^EXE$TIMEOUT, JMP_INSTR ; check EXE$TIMEOUT vector is JMP @# + XBNEQ 310$ ; neq - unexpected + ADDL3 #^XCB, G^EXE$TIMEOUT+2, -(SP) ; load approx. address + PUSHAB PATCHDESC_UCBTMO ; descriptor address + CALLS #2, LOOKUP_PATCH_DA ; lookup NUMTIM pach + XBLBC R0, 300$ ; lbc - return error +50$: + BBS #PATCH_ID_CRBTMO, - ; check if CRBTMO patch is disabled + ARG_NOPATCH(AP), 60$ ; bs - skip it + MOVL #VSMP_MSG_CRBTMO_P, R2 ; assume error + CMPW G^EXE$TIMEOUT, JMP_INSTR ; check EXE$TIMEOUT vector is JMP @# + BNEQ 310$ ; neq - unexpected + ADDL3 #^X66, G^EXE$TIMEOUT+2, -(SP) ; load approx. address + PUSHAB PATCHDESC_CRBTMO ; descriptor address + CALLS #2, LOOKUP_PATCH_DA ; lookup NUMTIM pach + XBLBC R0, 300$ ; lbc - return error +60$: + BBS #PATCH_ID_MFYCAP, - ; check if MFYCAP patch is disabled + ARG_NOPATCH(AP), 70$ ; bs - skip it + MOVL #VSMP_MSG_MFYCAP_P, R2 ; assume error + CMPW G^SCH$REMOVE_CPU_CAP + 2, - ; check SCH$REMOVE_CPU_CAP vector is .WORD ^M<...>, JMP @# + JMP_INSTR ; ... + BNEQ 310$ ; neq - unexpected + ADDL3 #^XCD, G^SCH$REMOVE_CPU_CAP+4, -(SP) ; load approx. address + PUSHAB PATCHDESC_MFYCAP ; descriptor address + CALLS #2, LOOKUP_PATCH_DA ; lookup NUMTIM pach + XBLBC R0, 300$ ; lbc - return error +70$: + MOVZBL #SS$_NORMAL, R0 ; return success status +300$: + RET ; to the caller +310$: + MOVL R2, R0 ; return error status + RET ; to the caller + .DISABLE LOCAL_BLOCK + +;+ +; +; Lookup XDelta patch area +; +;- + .ENTRY LOOKUP_PATCH_XDELTA, ^M<> + .ENABLE LOCAL_BLOCK + ; + ; set up exception handling for possible exceptions + ; while comparing the code + ; + MOVAB 20$, LOOKUP_PATCH_EXC_RESUME ; set up ACCVIO handler + MOVAB LOOKUP_PATCH_EXC_HANDLER, (FP) ; ... + ; + ; get address of XDELBPT routine + ; + MOVAB G^XDT$BPT, R0 ; XDELBPT should be pointed + CMPW (R0)+, JMP_INSTR ; by XDT$BPT as JMP #@XDELBPT + BNEQ 20$ ; ... + MOVL (R0), R0 ; fetch XDELBPT address + CLRL (FP) ; reset condition handler + ; + ; the code for XDELTA_WHAMI precedes XDELBPT, look it up + ; + SUBL #, R0 ; approximate location + PUSHAB XDT_TRG_E ; end of lookup pattern + PUSHAB XDT_TRG ; start of lookup pattern + PUSHL #10 ; max offset after + PUSHL #10 ; max offset before + PUSHL R0 ; approximate address + CALLS #5, LOOKUP_CODE ; lookup the code + BLBC R0, 20$ ; lbc - not found + MOVL LOOKUP_CODE_ADDR, - ; save found location to patch table + PATCHDESC_XDELTA + PATCH_DESC_LOOKUP_FOUND +10$: + MOVZBL #SS$_NORMAL, R0 ; return success status + RET ; to the caller +20$: + MOVL #VSMP_MSG_XDELTA_P, R0 ; return error status code + RET ; to the caller + .DISABLE LOCAL_BLOCK + + .ENTRY LOOKUP_PATCH_EXC_HANDLER, ^M<> ; condition handler invoked on exception during code search + .ENABLE LOCAL_BLOCK + MOVL CHF$L_SIGARGLST(AP), R0 ; get signal array pointer + CMPL CHF$L_SIG_ARGS(R0), #5 ; check if ACCVIO + BNEQ 10$ ; ... + CMPL CHF$L_SIG_NAME(R0), - ; ... + #SS$_ACCVIO ; ... + BNEQ 10$ ; ... + MOVL LOOKUP_PATCH_EXC_RESUME, 16(R0) ; modify PC to resume execution at + MOVZWL #SS$_CONTINUE, R0 ; resume executuion at error handler + RET ; ... +10$: + MOVZWL #SS$_RESIGNAL, R0 ; resignal unexpected exception + RET ; ... + .DISABLE LOCAL_BLOCK + +;+ +; +; Lookup patch area. +; Called in user mode. +; +; Arguments: +; 4(AP) = patch descriptor address +; 8(AP) = transfer vector (must be JMP@# or JSB@# structured) +; 12(AP) = offset from the transfer vector target, as approximate position +; +;- + ARG_DESC = 4 + ARG_XFER_VEC = 8 + ARG_XFER_OFFSET = 12 +; + .ENABLE LOCAL_BLOCK + .ENTRY LOOKUP_PATCH_D, ^M + MOVL ARG_DESC(AP), R7 ; patch descriptor address + MOVL ARG_XFER_VEC(AP), R0 ; extract transfer vector's + CMPW (R0), JMP_INSTR ; ... target address + BEQL 10$ ; ... + CMPW (R0), JSB_INSTR ; ... + BNEQ 110$ ; neq - bad or unexpected vector +10$: + MOVL 2(R0), R0 ; target address + ADDL3 ARG_XFER_OFFSET(AP), R0, - ; add offset and store in the descriptor + PATCH_DESC_LOOKUP_START(R7) ; as starting point for code lookups +20$: + ADDL3 R7, PATCH_DESC_LOOKUP_END(R7), -(SP) ; end of pattern + ADDL3 R7, PATCH_DESC_LOOKUP_BEGIN(R7), -(SP) ; start of pattern + PUSHL PATCH_DESC_LOOKUP_AFTER(R7) ; lookup range + PUSHL PATCH_DESC_LOOKUP_BEFORE(R7) ; ... + PUSHL PATCH_DESC_LOOKUP_START(R7) ; lookup start + CALLS #5, LOOKUP_CODE ; lookup the code + BLBC R0, 110$ ; lbc - not found + MOVL LOOKUP_CODE_ADDR, - ; save found location to patch table + PATCH_DESC_LOOKUP_FOUND(R7) ; ... + MOVZBL #SS$_NORMAL, R0 ; return success status + RET ; to the caller +110$: + MOVL PATCH_DESC_ERROR_STATUS(R7), R0 ; return error status + RET ; to the caller + +; +; Alternate entry point. +; Arguments: +; 4(AP) = patch descriptor address +; 8(AP) = starting address for lookup +; + ARG_DESC = 4 + ARG_START = 8 +; + .ENTRY LOOKUP_PATCH_DA, ^M + MOVL ARG_DESC(AP), R7 ; patch descriptor address + MOVL ARG_START(AP), R0 ; starting lookup address + MOVL R0, PATCH_DESC_LOOKUP_START(R7) ; store in the descriptor + ; as starting point for code lookups + BRB 20$ ; join common code + .DISABLE LOCAL_BLOCK + + +;;; +;;; XDELTA patch lookup template (preliminary) +;;; + +XDT_PAT_LOOK: + MOVL (SP),-(SP) + WHAMI 4(SP) + RSB + .ASCIZ /STEPOVER/ + .ASCIZ / BRK AT / +XDT_PAT_LOOK_E: + + .PSECT $DATA LONG, NOSHR, WRT, PIC, NOEXE + +LOOKUP_PATCH_EXC_RESUME: ; execution resume address for + .BLKL ; ... LOOKUP_PATCH_EXC_HANDLER + +LOOKUP_CODE_ADDR:: ; located address retured by + .BLKL ; ... LOOKUP_CODE + +XDELTA_RANGE:: ; address range of XDelta + .LONG 0, 0 + +MSG_RTNS_RANGE:: ; address range of MESSAGE_ROUTINES.EXE + .LONG 0, 0 + + +;;; +;;; XQTIMXMT patch source template +;;; for code located in XQDRIVER routine SUB_START_CTRL_TIMER +;;; in module [PGV_LAN.SRC]DEQNA.MAR +;;; + X_QNA$C_XMT_TMO = 5 ; these values may change across XQDRIVER and VMS versions + X_LSB$G_QNA_TIMXMT = ^XC4E ; and matter here only approximately, as falling either in + X_QNA$C_SID_MINTIM = ^X1E0 ; "word offset" or "byte offset" category or, for QNA$C_XMT_TMO + X_LSB$G_QNA_TIMSID = ^XC50 ; and QNA$C_SID_MINTIM, as being either a "short immediate + X_LSB$G_QNA_TQE = ^XC54 ; operand" (with value <= 63) or above 63 + X_QNA$C_TQE_DELTA = 10000000 + +XQDRV_PAT_1:: ;; located approximately at XQDRIVER + ^X1AD3 + MOVB #X_QNA$C_XMT_TMO, X_LSB$G_QNA_TIMXMT+1(R4) + MOVW #X_QNA$C_SID_MINTIM, X_LSB$G_QNA_TIMSID+2(R4) + MOVAL X_LSB$G_QNA_TQE(R4),R5 + MOVL #<!TQE$C_LENGTH>, TQE$W_SIZE(R5) + MOVQ #X_QNA$C_TQE_DELTA, TQE$Q_DELTA(R5) + MOVQ R3,TQE$L_FR3(R5) + MOVAL W^X_QNA$CTRL_TIMER_EXP, TQE$L_FPC(R5) +XQDRV_PAT_2:: ;; located approximately at XQDRIVER + ^X1B02 + MOVB #TQE$C_SSREPT, TQE$L_RQPID(R5) + MOVB TQE$L_RQPID(R5), TQE$B_RQTYPE(R5) + READ_SYSTIME R0 + ADDL TQE$Q_DELTA(R5),R0 + ADWC TQE$Q_DELTA+4(R5),R1 + JSB G^EXE$INSTIMQ +XQDRV_PAT_2E == . + 2 + MOVW X_LSB$G_QNA_TIMSID+2(R4), X_LSB$G_QNA_TIMSID(R4) +X_QNA$CTRL_TIMER_EXP = . + + .END diff --git a/src/VMS_VSMP/kload.mar b/src/VMS_VSMP/kload.mar new file mode 100644 index 0000000..366fbaf --- /dev/null +++ b/src/VMS_VSMP/kload.mar @@ -0,0 +1,1437 @@ + .TITLE KLOAD + .IDENT /V1.00/ + +;; +;; Kernel-mode loadable code for SIMH VMS virtual SMP utility. +;; Root part: loads resident image to kernel, interfaces with command line utility. +;; +;; Tested with OpenVMS VAX version 7.3. +;; +;; Module: kload.mar +;; Version: 1.0 +;; Author: Sergey Oboguev (oboguev@yahoo.com) +;; Created: 10-Dec-2011 +;; Revision History: +;; none +;; + .LIBRARY "SYS$LIBRARY:LIB" + + SYS_DEFS ; common VMS definitions + $KA650DEF ; KA650 CPU specific definitions + $CRBDEF GLOBAL ; channel request block (make GLOBAL for importing from C) + $DCDEF ; device classes and types + $DDBDEF GLOBAL ; device data block (make GLOBAL for importing from C) + $DPTDEF GLOBAL ; driver prologue table (make GLOBAL for importing from C) + $IRPDEF ; IO request packet + $SBDEF GLOBAL ; VAXcluster node system descriptor block (GLOBAL for C) + $PTEDEF ; page table entry + + XBRANCH ; Extended branch instructions + SIMHDEF ; SIMH API definitions + + ; + ; VAX MP will elevate VCPU thread priority when the thread is executing + ; at IPL SYS_CRITICAL and above + ; + IPL$_SYS_CRITICAL == IPL$_QUEUEAST + + ; + ; IPL for VAX MP interprocessor interrupts + ; + IPL$_IPINTR = 22 + + .PSECT $CODE LONG, SHR, NOWRT, PIC, EXE + +;;*********************************************************************************** +;; Utility routines to assist loader +;;*********************************************************************************** + +;+ +; +; Write address of SIMH VAX MP API request block to VAX MP communication register. +; Should be called in user mode. +; +; void mtpr_simh(unit32* args); +; +; If request is disallowed will throw exception. +; +;- + ARG_ADDR = 4 +; + .ENTRY MTPR_SIMH, ^M<> + MTPR ARG_ADDR(AP), #PR$_SIMH + RET + +;+ +; +; Check whether multiprocessing system image is loaded and if any drivers not +; modified for SMP are loaded on the system. +; +; uint32 k_check_smp_enabled(uint32* enabled, uint32* unmod_driver); +; +; Returns VMS-structured status. +; If successful, will boolean responses to locations pointed by "enabled" +; and "unmod_driver". +; +;- + ARG_ENABLED = 4 + ARG_UNMOD_DRIVER = 8 +; + .ENTRY K_CHECK_SMP_ENABLED, ^M<> + EXTZV #SMP$V_ENABLED, #1, G^SMP$GL_FLAGS, @ARG_ENABLED(AP) + EXTZV #SMP$V_UNMOD_DRIVER, #1, G^SMP$GL_FLAGS, @ARG_UNMOD_DRIVER(AP) + MOVZBL #SS$_NORMAL, R0 + RET + +;+ +; +; Allocate a block of non-paged pool upto 63KB in size. +; +; uint32 kmalloc_kb64(uint32 size, void** paddr); +; +; Returns VMS-structured status. +; If successful, will return the address of the loaded block to *paddr. +; +;- + ARG_SIZE = 4 + ARG_PADDR = 8 +; + .ENABLE LOCAL_BLOCK + .ENTRY KMALLOC_KB64, ^M + CLRL @ARG_PADDR(AP) ; clear for failure + MOVL ARG_SIZE(AP), R1 ; allocate block of requested size + JSB G^EXE$ALONONPAGED ; ... + BLBC R0, 10$ ; failed? return error status + MOVL R2, @ARG_PADDR(AP) ; store address + MOVW R1, PCB$W_SIZE(R2) ; store allocated block size in the block +10$: + RET ; return to the caller + .DISABLE LOCAL_BLOCK + +;+ +; +; Deallocate a block to non-paged pool allocated with kmalloc_kb64. +; +; void kfree_kb64(void* p) +; +;- + ARG_ADDR = 4 +; + .ENTRY KFREE_KB64, ^M + MOVL ARG_ADDR(AP), R0 ; get block address + CLRB PCB$B_TYPE(R0) ; clear block type (MSB = 0 indicates non-shared memory) + JSB G^EXE$DEANONPAGED ; deallocate + RET ; return to the caller + +;+ +; +; Allocate a block of non-paged pool of arbitrary size. +; +; uint32 kmalloc_anysize(uint32 reqsize, void** paddr, uint32* pblksize); +; +; "reqsize" - requested size of the block to allocate. +; +; Returns VMS-structured status. +; If successful, will return the address of the loaded block to *paddr and the +; actual size of allocated block to *pblksize. +; +;- + ARG_SIZE = 4 + ARG_PADDR = 8 + ARG_PBLKSIZE = 12 +; + .ENABLE LOCAL_BLOCK + .ENTRY KMALLOC_ANYSIZE, ^M + CLRL @ARG_PADDR(AP) ; clear for failure + MOVL ARG_SIZE(AP), R1 ; allocate block of requested size + JSB G^EXE$ALONONPAGED ; ... + BLBC R0, 10$ ; failed? return error status + MOVL R2, @ARG_PADDR(AP) ; store address + MOVL R1, @ARG_PBLKSIZE(AP) ; store allocated block size +10$: + RET ; return to the caller + .DISABLE LOCAL_BLOCK + +;+ +; +; Deallocate a block to non-paged pool allocated with kmalloc_anysize. +; +; void kfree_anysize(void* p, uint32 blksize) +; +;- + ARG_ADDR = 4 + ARG_BLKSIZE = 8 +; + .ENTRY KFREE_ANYSIZE, ^M + MOVL ARG_ADDR(AP), R0 ; get block address + MOVL ARG_BLKSIZE(AP), R1 ; get block size + JSB G^EXE$DEANONPGDSIZ ; deallocate + RET ; return to the caller + +;+ +; +; Flush instruction stream after executable code was modified. +; +; void kload_flush_instruction_stream(); +; +;- + .ENABLE LOCAL_BLOCK + .ENTRY KLOAD_FLUSH_INSTRUCTION_STREAM, ^M<> + MOVPSL -(SP) ; execute REI to flush instruction stream + MOVAB 10$, -(SP) ; ... + REI ; ... +10$: + RET ; return to the caller + .DISABLE LOCAL_BLOCK + +;+ +; +; Copy memory block. +; +; void kmemcpy(void* dst, const void* src, uint32 size); +; +; "size" is in bytes. +; "src" and "dst" should not overlap, otherwise results are unpredictable. +; +;- + ARG_DST = 4 + ARG_SRC = 8 + ARG_SIZE = 12 +; + .ENABLE LOCAL_BLOCK + .ENTRY KMEMCPY, ^M + MOVL ARG_SIZE(AP), R6 ; load arguments + MOVL ARG_SRC(AP), R1 ; ... + MOVL ARG_DST(AP), R3 ; ... +10$: + CMPL R6, #^XFFF0 ; fits 64K block? + BLEQU 20$ ; lequ - yes, move last part + MOVC #^XFFF0, (R1), (R3) ; move first or next part + SUBL #^XFFF0, R6 ; reduce remaining counter + BRB 10$ ; next iteration +20$: + MOVC R6, (R1), (R3) ; move last segment + RET ; return to the caller + .DISABLE LOCAL_BLOCK + +;+ +; +; Verify if already loaded kernel-resident block is compatible with the current SMP.EXE image. +; +; int kload_verify_compatible(void* addr); +; +; Argument is the address of the resident block. +; Returns VMS-structured status. +; +;- + ARG_ADDR = 4 +; + .ENABLE LOCAL_BLOCK + .ENTRY KLOAD_VERIFY_COMPATIBLE, ^M<> + MOVL ARG_ADDR(AP), R0 ; get block address + MOVAB KLOAD_START, R1 ; get prototype address in the current image + MOVAB KVC_HANDLER, (FP) ; establish condition handler to catch invalid + ; (non-readable) resident block address + ; + ; Compare basic header fields + ; + CMPL 12(R0), 12(R1) ; compare signature + BNEQ 10$ ; ... + CMPL 16(R0), 16(R1) ; ... + BNEQ 10$ ; ... + CMPL 20(R0), 20(R1) ; compare interface version + BNEQ 10$ ; ... + CMPL 24(R0), 24(R1) ; compare implementation version + BNEQ 10$ ; ... + ; + ; Verify that offsets of routines exported by the loaded resident image from system space + ; match present executable image + ; + MOVAB (R0), R0 + MOVAB (R1), R1 + CMPL (R0)+, (R1)+ ; kcall_onload + BNEQ 10$ ; ... + CMPL (R0)+, (R1)+ ; kcall_get_idle + BNEQ 10$ ; ... + CMPL (R0)+, (R1)+ ; kcall_set_idle + BNEQ 10$ ; ... + CMPL (R0)+, (R1)+ ; kcall_get_timesync + BNEQ 10$ ; ... + CMPL (R0)+, (R1)+ ; kcall_set_timesync + BNEQ 10$ ; ... + ; + ; Finished checking + ; + CLRL (FP) ; cancel condition handler + MOVZBL #SS$_NORMAL, R0 ; return status: compatible + RET ; ... +10$: + MOVL #VSMP_MSG_VERSION_MISMATCH, R0 ; return error status: loaded image mismatches current VSMP executable + RET ; ... + + .ENTRY KVC_HANDLER, ^M<> ; condition handler invoked if resident block is unreadable + MOVL CHF$L_SIGARGLST(AP), R0 ; get signal array pointer + CMPL CHF$L_SIG_ARGS(R0), #5 ; check if ACCVIO + BNEQ 20$ ; ... + CMPL CHF$L_SIG_NAME(R0), - ; ... + #SS$_ACCVIO ; ... + BNEQ 20$ ; ... + MOVAB 10$, 16(R0) ; modify PC to resume execution at + MOVZWL #SS$_CONTINUE, R0 ; resume executuion at error handler + RET ; ... +20$: + BUG_CHECK INVEXCEPTN,FATAL ; invalid exception + .DISABLE LOCAL_BLOCK + +;+ +; +; Lock IO database for reading or writing. +; +; void iodb_lock_rd(); +; void iodb_lock_wr(); +; +; These functions must be called at IPL <= ASTDEL and return at IPL ASTDEL. +; +;- + .ENTRY IODB_LOCK_RD, ^M + MOVL G^CTL$GL_PCB, R4 + JSB G^SCH$IOLOCKR + RET + + .ENTRY IODB_LOCK_WR, ^M + MOVL G^CTL$GL_PCB, R4 + JSB G^SCH$IOLOCKW + RET + +;+ +; +; Unlock IO database. +; +; void iodb_unlock(uint32 ipl); +; +; These functions must be called at IPL = ASTDEL. +; May ponentially also be called at any level that may acquire SCHED spinlock, +; but currently this code is pageable, so ASTDEL is the highest caller's IPL allowed. +; +; At exit resets IPL to "ipl". +; +;- + ARG_IPL = 4 +; + .ENTRY IODB_UNLOCK, ^M + MOVL G^CTL$GL_PCB, R4 + JSB G^SCH$IOUNLOCK + MTPR ARG_IPL(AP), S^#PR$_IPL + RET + + +;+ +; +; Get address of local system descriptor block (SB). +; +; void* get_localsb_addr(); +; +;- + .ENTRY GET_LOCALSB_ADDR, ^M<> + MOVL G^SCS$AR_LOCALSB, R0 + RET + + +;;*********************************************************************************** +;; Loader part locked in the process working set +;;*********************************************************************************** + +KLOAD_WSLOCK_START:: ; start of section locked into working set + +;+ +; +; Calibrate processor loops. +; +; uint32 kload_calibrate_sysloops(uint32* tenusec, uint32* ubdelay) +; +; Values of EXE$GL_TENUSEC, EXE$GL_UBDELAY, CPU$L_TENUSEC and CPU$L_UBDELAY are restored +; at the exit of call to the values at entrance. +; +; Must be called before SMP is activated. +; +;- + TENUSEC = 4 + UBDELAY = 8 +; + .ENTRY KLOAD_CALIBRATE_SYSLOOPS, ^M + + FIND_CPU_DATA R1 ; locate CPU database + + DSBINT ENVIRON=UNIPROCESSOR ; disable interrupts + + PUSHL G^EXE$GL_TENUSEC ; save previous calibration + PUSHL G^EXE$GL_UBDELAY ; ... + JSB G^EXE_INI_TIMWAIT ; perform calibration using private version of EXE$INI_TIMWAIT + + MOVL G^EXE$GL_TENUSEC, R2 ; save new calibration + MOVL G^EXE$GL_UBDELAY, R3 ; ... + + POPL G^EXE$GL_UBDELAY ; restore previous calibration + POPL G^EXE$GL_TENUSEC ; ... + MOVL G^EXE$GL_UBDELAY, CPU$L_UBDELAY(R1) ; ... + MOVL G^EXE$GL_TENUSEC, CPU$L_TENUSEC(R1) ; ... + + ENBINT ; re-enable interrupts + + MOVL R2, @TENUSEC(AP) ; return measured values + MOVL R3, @UBDELAY(AP) ; ... + MOVZBL #SS$_NORMAL, R0 ; ... + RET ; to the caller + +;+ +; +; Apply new calibration of processor loops. +; +; uint32 kload_apply_calibration(uint32 tenusec, uint32 ubdelay); +; +; Changes values of EXE$GL_TENUSEC, EXE$GL_UBDELAY, CPU$L_TENUSEC and CPU$L_UBDELAY. +; +; Must be called before SMP is activated. +; +;- + TENUSEC = 4 + UBDELAY = 8 +; + .ENTRY KLOAD_APPLY_CALIBRATION, ^M + MOVL TENUSEC(AP), R2 ; get parameters + MOVL UBDELAY(AP), R3 ; ... + FIND_CPU_DATA R1 ; locate CPU database + DSBINT ENVIRON=UNIPROCESSOR ; disable interrupts + + MOVL R2, G^EXE$GL_TENUSEC ; apply new calibration + MOVL R2, CPU$L_TENUSEC(R1) ; ... + + MOVL R3, G^EXE$GL_UBDELAY ; ... + MOVL R3, CPU$L_UBDELAY(R1) ; ... + + ENBINT ; re-enable interrupts + + MOVZBL #SS$_NORMAL, R0 ; return + RET ; to the caller + + +;+ +; +; EXE_INI_TIMWAIT - compute TIMEWAIT loop values +; +; This routine is a modified private variant of EXE$INI_TIMWAIT (its version for KA650). +; Unlike the original EXE$INI_TIMWAIT it does not call INI$CACHE to reinitialize the cache. +; +; This private variant is needed because SIMH implementation of MicroVAX 3900 simulator +; uses a hack whereby it can provide 512MB memory configuration. Unfortunately memory +; region above 256MB overlaps KA650 CDG space. This works fine, as long as the operating system +; does not try to use CDG space after the system had been booted. In case of VMS it does +; not do that natively, but if we called EXE$INI_TIMWAIT (that in turn calls INI$CACHE) +; this is exactly what would have happened: 64KB memory region slightly above 256 MB boundary +; would have been cleared, wiping out all the data in these pages. +; +; Therefore we cannot call regular system copy of EXE$INI_TIMWAIT to recalibrate loops and +; have to use a private modified copy of this routine instead. +; +; EXE_INI_TIMWAIT computes and sets valus of cells CPU$L_TENUSEC and CPU$L_UBDELAY that provide +; calibration data for busy-wait macros (such as TIMEWAIT and TIMEDWAIT) and other busy-wait loops +; that spin for a certain pre-defined number of cycles instead of reading the processor clock. +; Instead, calculated number of iteration cycles of calibrated loops translates to a required delay. +; +; CPU$L_UBDELAY is the number of times a calibrated loop must be executed for 3 usec delay. +; Delaying by 3 usec between references to QBus or Unibus space (such as by bit-test instruction +; inside TIMEWAIT macros or indirectly via TIMEDWAIT) prevents excessively high frequency of +; polling references to the bus space that can saturate it. CPU$L_UBDELAY driven loop introduces +; 3 usec delay between such references to the bus inside TIMEWAIT bit-test loop. +; +; CPU$L_TENUSEC is the number of times a calibrated loop must be executed for 10 usec delay. +; +; Note that actual busy-wait loops may (and commonly will) take more time to execute each iteration +; than the calibration loop. They can have more instructions per iteration. Also SMP busy-wait loops +; typically use BBSSI or other interlocked instruction in place of BITW in the prototype loop +; which is much more expensive on VAX MP than BITW. The purpose of calibration loop is to provide +; not exact timing for target loops, but minimal guaranteed timing. +; +; Also note that since modern host processors that SIMH executes on are much faster than Mayfair VAXen, +; the number of iterations during UBDELAY calibration is increased approximately 20-fold compared +; to EXE$INI_TIMWAIT in order to obtain more precise and stable calibration reading. +; +; Inputs: +; +; Interval timers. +; Kernel mode, memory management enabled, IPL >= ASTDEL +; +; Outputs: +; +; R0 - Destroyed. +; +; CPU$L_TENUSEC - set to appropriate value to make TIMEWAIT and TIMEDWAIT +; macros loop for 10 usec +; +; CPU$L_UBDELAY - set to appropriate value to make TIMEWAIT and TIMEDWAIT +; macros loop for 3 usec +; +;- + + ; + ; set up and start real-time timer for loop calibration + ; + .MACRO BEGIN_TMR + CLRL KA650$L_TNIR0(R2) ; reset "timer next interval" register + MOVL #, - ; start timer, do not signal interrupts + KA650$L_TCR0(R2) ; using initial timer run value in TNIR0 + .ENDM BEGIN_TMR + + ; + ; stop real-time timer and compute calibration + ; + .MACRO END_TMR NCYC, CALIBR + MOVL KA650$L_TIR0(R2), R0 ; read total time it took to execute the loop (usecs) + CLRL KA650$L_TCR0(R2) ; stop timer + DIVL3 R0, NCYC, CALIBR ; calculate number of times to execute the loop + INCL CALIBR ; ... for specified (3 usec or 10 usec) delay + .ENDM END_TMR + + .ENABLE LOCAL_BLOCK + ; + ; routine to set up time-wait calibration data + ; +EXE_INI_TIMWAIT: + PUSHL R1 ; save scratch registers + PUSHL R2 ; ... + FIND_CPU_DATA R1 ; locate per-CPU database + + ;;; BSBW INI$CACHE ; do **not** reinitialize the cache + + MOVL G^EXE$GL_CPUNODSP, R2 ; virtual address of CPU node private space + PUSHL #400000 ; number of times to execute calibration loop + BEGIN_TMR ; reset and start real-time clock timer +; +; Start of UBDELAY loop being calibrated. +; UBDELAY loop is executed to provide delay for 3 usec intervals (or multiple of 3 usec intervals). +; +10$: SOBGTR (SP), 10$ ; delay loop body +; +; End of UBDELAY calibration loop. +; + END_TMR #1200000, CPU$L_UBDELAY(R1) ; stop timer and calculate number of times to execute + ; ... UBDELAY loop for 3 usec delay +; +; Calibration of UBDELAY loop is completed. +; Now set up for calibration of TENUSEC loop. +; + MOVL #20000, R0 ; number of times to execute calibrated loop + BEGIN_TMR ; reset and start real-time clock timer + +; +; Start of TENUSEC loop being calibrated. +; TENUSEC loop is executed to delay for 10 usec (or multiple of 10 usec) intervals. +; +20$: BITW #^X4000, 900$ ; arbitrary BITx instruction to simulate loop pattern + BNEQ 40$ ; arbitrary conditional branch instruction to simulate loop pattern + MOVL CPU$L_UBDELAY(R1), (SP) ; refill 3 ussec delay loop iteration count +30$: SOBGTR (SP), 30$ ; delay 3 usec +40$: SOBGTR R0, 20$ ; go next outer loop iteration +; +; End of TENUSEC calibration loop. +; + END_TMR #200000, CPU$L_TENUSEC(R1) ; stop timer and calculate number of times to execute + ; ... TENUSEC loop for 10 usec delay + + MOVL CPU$L_TENUSEC(R1), G^EXE$GL_TENUSEC ; store CPU calibration data to system-wide locations + MOVL CPU$L_UBDELAY(R1), G^EXE$GL_UBDELAY ; ... + + TSTL (SP)+ ; pop delay-loop counter off the stack + + POPL R2 ; restore scratch registers + POPL R1 ; ... + RSB ; return to the caller + + .ALIGN LONG +900$: + .LONG 0 ; cell referenced by calibration loop bit-test instruction + .DISABLE LOCAL_BLOCK + +;+ +; +; Enable/disable process deletion. +; +; void kload_set_process_deletable(bool_t deletable) +; +; If "deletable" is TRUE, process is set deletable. +; If "deletable" is FALSE, process is set undeletable. +; +;- + ARG_DELETABLE = 4 +; + .ENABLE LOCAL_BLOCK + .ENTRY KLOAD_SET_PROCESS_DELETABLE, ^M + MOVZBL ARG_DELETABLE(AP), R2 ; get flag value from paged area + LOCK LOCKNAME=SCHED, - ; synchonize access to the PCB + SAVIPL=-(SP), - ; ... + PRESERVE=NO ; ... + MOVL G^CTL$GL_PCB,R4 ; Get current PCB address + TSTL R2 ; check requested action + BEQL 10$ ; eql - disable delete + BICL #PCB$M_NODELET,- ; enable process deletion + PCB$L_STS(R4) ; ... + BRB 20$ +10$: + BISL #PCB$M_NODELET,- ; disable process deletion + PCB$L_STS(R4) ; ... +20$: + UNLOCK LOCKNAME=SCHED, - ; release SCHED spinlock and revert IPL + PRESERVE=NO, - ; ... do not preserve R0 + NEWIPL=(SP)+ ; ... + RET + .DISABLE LOCAL_BLOCK + + +;+ +; +; Set UCB's affinity. +; +; void k_set_ucb_affinity(void* ucb, uint32 affinity); +; +; Called and returns at IPL = ASTDEL with IODB lock held. +; +;- + ARG_UCB = 4 + ARG_AFFINITY = 8 +; + .ENTRY K_SET_UCB_AFFINITY, ^M + MOVL ARG_UCB(AP), R5 + FORKLOCK - + LOCK=UCB$B_FLCK(R5), - ; lock fork level access to UCB + SAVIPL=-(SP), - ; save current IPL + PRESERVE=NO ; don't save R0 + MOVL ARG_AFFINITY(AP), - ; set unit affinity + UCB$L_AFFINITY(R5) ; ... + FORKUNLOCK - ; unlock fork lock + LOCK=UCB$B_FLCK(R5), - ; ... + NEWIPL=(SP)+, - ; restore previous IPL + PRESERVE=NO ; ... + RET ; return to the caller + + +;+ +; +; Extend fork-to-primary pool size to at least "npages". +; Limit is 255 pages. +; Called at IPL 0 to ASTDEL before VSMP is started. +; +; uint32 k_extend_pfork_pool(int npages); +; +; Returns status code. +; +;- + ARG_NPAGES = 4 +; + .ENABLE LOCAL_BLOCK + .ENTRY K_EXTEND_PFORK_POOL, ^M + MOVL ARG_NPAGES(AP), R6 ; limit page count to 255 + CMPL R6, #255 ; ... to be storable in + BLEQU 10$ ; ... SMP$GB_PFORK_POOL_SIZE + MOVZBL #255, R6 ; ... +10$: + LOCK LOCKNAME=MMG, - ; acquire MMG spinlock + LOCKIPL=IPL$_SYNCH, - ; ... + SAVIPL=-(SP) ; ... and store caller's IPL + CMPB R6, G^SMP$GB_PFORK_POOL_SIZE ; is existing pool already large enough? + BLEQU 200$ ; lequ - yes, just exit with success status + MOVL R6, R2 ; allocate required number of SPTEs + JSB G^LDR$ALLOC_PT_SYNC ; ... + BLBC R0, 210$ ; lbc - return error to the caller + SUBL3 G^LDR$GL_SPTBASE, R1, R3 ; calculate base address + ASHL #<9-2>, R3, R7 ; and initalize buffer pointer + BISL #^X80000000, R7 ; ... + MOVL R7, R8 ; set up SVA of current page for the loop below +30$: + PUSHL R1 ; preserve R1 + JSB G^MMG$ALLOCPFN ; allocate one PFN + POPL R1 ; restore R1 + TSTL R0 ; was PFN successfully allocated? + BLEQ 220$ ; leq - no + BISL3 #, R0, (R1)+ ; map page + MTPR R8, S^#PR$_TBIS ; invalidate TLB entry + ADDL #512, R8 ; advance to SVA of next page + SOBGTR R2,30$ ; go get PFN for next page +; +; Set pool address and initialize the listhed. Listhead contains a FLINK and +; a blocksize as the first 2 longwords of each block. +; + DSBINT #IPL$_POWER, -(SP), - ; block interrupts while we are switching pool + ENVIRON=UNIPROCESSOR ; ... + MOVB R6, G^SMP$GB_PFORK_POOL_SIZE ; store pool size (page count) + MOVL R7, G^SMP$GL_PFORK_POOL ; set pool address + CLRL (R7)+ ; zero FLINK (single block on free list) + MULL3 R6, #512, (R7) ; and set the block size + ENBINT (SP)+ ; restore IPL +200$: + MOVZBL #SS$_NORMAL, R0 ; return status = success +210$: + UNLOCK LOCKNAME=MMG, - ; unlock MMG lock + NEWIPL=(SP)+, - ; ... restore caller's IPL + PRESERVE=YES ; ... preserve R0 + RET ; return to the caller +220$: + MOVL #VSMP_MSG_ALLOCPFN, R0 ; return error code + BRB 210$ ; ... + .DISABLE LOCAL_BLOCK + +;+ +; +; Disable driver unloading. +; +; void pin_driver(void* dpt); +; +; Argument "dpt" is the address of the driver's prologue table. +; +;- + ARG_DPT = 4 +; + .ENTRY PIN_DRIVER, ^M<> + MOVL ARG_DPT(AP), R0 ; driver DPT address + BISB #DPT$M_NOUNLOAD, - ; set NOUNLOAD flag + DPT$B_FLAGS(R0) ; ... + MOVZBL #SS$_NORMAL, R0 ; return success code + RET ; just in case + +;+ +; +; Change transmit timeout of XQ device. +; +; void set_xq_xmt_timeout(void* ucb, uint32 lsb_xmt_tmo, uint32 timeout); +; +; Entrance IPL is ASTDEL (but may be any). +; Returs at caller's original IPL. +; +; Arguments: +; +; "ucb" - UCB of any XQ unit for this conroller, typically XQx0 +; "lsb_xmt_tmo" - offset of LSB$G_QNA_TIMXMT in XQDRIVER's LSB structure +; "timeout" - timeout to set, in seconds, must be between 5 and 255 +; +;+ + ARG_UCB = 4 + ARG_LSB_XMT_TMO = 8 + ARG_TIMEOUT = 12 +; + .ENABLE LOCAL_BLOCK + .ENTRY SET_XQ_XMT_TIMEOUT, ^M + DSBINT #IPL$_POWER, -(SP), - ; block interrupts while we are changing system structures + ENVIRON=UNIPROCESSOR ; ... + MOVL ARG_UCB(AP), R0 ; get UCB address + CMPB UCB$B_DEVTYPE(R0), #DT$_DEQNA ; skip if not DEQNA or DELQA + BEQL 10$ ; ... + CMPB UCB$B_DEVTYPE(R0), #DT$_XQ_DELQA ; ... + BNEQ 100$ ; ... +10$: + MOVL UCB$L_CRB(R0), R0 ; get CRB address + BGEQ 100$ ; nx - should never happen + MOVL CRB$L_AUXSTRUC(R0), R0 ; get LSB address + BGEQ 100$ ; null - was not allocated yet + CMPB IRP$B_TYPE(R0), #DYN$C_CDB ; check block type matches used for LSB + BNEQ 100$ ; ... + MOVZWL IRP$W_SIZE(R0), R1 ; check XMT_TMO fits inside this LSB + SUBL #2, R1 ; ... + CMPL ARG_LSB_XMT_TMO(AP), R1 ; ... + BGTR 100$ ; ... + ADDL ARG_LSB_XMT_TMO(AP), R0 ; address of LSB$G_QNA_TIMXMT, 2 bytes + ; + ; LSB$G_QNA_TIMXMT is two bytes. + ; Low byte is 0 if timeout is currently not active, otherwise countdown timer. + ; High byte is timeout refill value, copied to low byte when timeout is started. + ; + ; High byte will be 0 if this LSB had not been initialzied yet. + ; In this case, do not set timeout in it. Enlarged timeout value will be set in this LSB later + ; by XQPATCH_INS_1 when associated link will be initialzied. That it will be set, is insured by + ; calling patch_xqdrv_instr before invoking set_xq_xmt_timeout. + ; + TSTB 1(R0) ; timeout not initialized yet in this LSB? + BEQL 100$ ; eql - not initialized, skip this LSB + CMPB 1(R0), ARG_TIMEOUT(AP) ; new timeout must be larger than old "refill" + BGEQU 100$ ; geq - do not change it + SUBB3 1(R0), ARG_TIMEOUT(AP), R1 ; calc insrease delta + MOVZBL R1, R1 ; ... + MOVZBL (R0), R2 ; calc new "active" timeout + BEQL 30$ ; eql - inactive, leave it + ADDL R1, R2 ; ... + CMPL R2, #255 ; must not exceed 255 + BLEQ 20$ ; ... + MOVZBL #255, R2 ; ... +20$: + MOVB R2, (R0) ; set current "active" timeout +30$: + MOVB ARG_TIMEOUT(AP), 1(R0) ; set current "refill" timeout +100$: + ENBINT (SP)+ ; restore caller's IPL + MOVZBL #SS$_NORMAL, R0 ; return success code + RET ; just in case + .DISABLE LOCAL_BLOCK + +;+ +; Break to debugger +;- + .ENTRY DBG_BRK, ^M<> + BPT + RET + +KLOAD_WSLOCK_END == . - 1 ; end of section locked into working set + +;;*********************************************************************************** +;; Kernel-resident part that is loaded into nonpaged memory -- header +;;*********************************************************************************** + + .PSECT KLOAD_AAA QUAD, PIC, EXE, SHR, NOWRT +KLOAD_START:: + ;; start of loadable part header, first part is standard VMS block header + .LONG 0 ; standard VMS block header + .LONG 0 ; ... + .WORD 0 ; ... block size (left 0) + .BYTE DYN$C_SPECIAL ; ... block type + .BYTE 0 ; ... + ;; end of VMS block header, followed by specific image id header + .ASCII "VSMP$LDR" ; signature of the header of the loadable image + .LONG 1 ; version of the interface + .LONG 1 ; version of the implementation + .LONG 0 ; unload routine or nil +KLOAD_BLKSIZE:: + .LONG 0 ; size of the block +EXPORTS_OFFSET_TABLE: + .LONG KCALL_ONLOAD - KLOAD_START + .LONG KCALL_GET_IDLE - KLOAD_START + .LONG KCALL_SET_IDLE - KLOAD_START + .LONG KCALL_GET_TIMESYNC - KLOAD_START + .LONG KCALL_SET_TIMESYNC - KLOAD_START + ;; end of loadable part header + +;;*********************************************************************************** +;; Kernel-resident part that is loaded into nonpaged memory -- data +;;*********************************************************************************** + + .PSECT KLOAD_DATA QUAD, PIC, EXE, NOSHR, WRT +KERROR_CAUSE:: + .BLKL ; error cause returned to the caller + +SYNCW_ON:: .LONG 0 ; synchronization window control +SYNCW_WINSIZE_SYS:: .LONG 0 ; ... +SYNCW_WINSIZE_ILK:: .LONG 0 ; ... +SMP_MUST_OPTIONS:: .LONG 0 ; SIMH SMP options control +SMP_WANT_OPTIONS:: .LONG 0 ; ... + +PROC_IDLE_OLD: + .BLKL ; saved address of old EXE$PROC_IDLE handler + +IDLE_CTRL:: ; idle loop control + .BLKB ; SIM_K_IDLE_ON/SIM_K_IDLE_OFF/SIM_K_IDLE_NEVER + +CRLF: + .BYTE ^X0D, ^X0A, ^X00 ; console print line postiflx + +JMP_INSTR: + JMP @#^X80001234 ; JMP G^xxx instruction template + +NX_JMP_INSTR: + JMP G^EXE$LOAD_ERROR_JSB ; JMP template for non-existing instruction vector (6 bytes) + +NX_JSB_INSTR: + JSB G^EXE$LOAD_ERROR_JSB ; JSB template for non-existing instruction vector + RSB ; ... (7 bytes including RSB) + +;;*********************************************************************************** +;; Kernel-resident part that is loaded into nonpaged memory -- code +;;*********************************************************************************** + + .PSECT KLOAD_CODE QUAD, PIC, EXE, SHR, NOWRT +;+ +; +; Called to initialize at loading time. +; +; uint32 kcall_onload(uint32 cpu_mask, uint32 idle, uint32 timesync, uint32 nopatch); +; +; Argument "idle" is initial idle status: SIM_K_IDLE_ON/SIM_K_IDLE_OFF/SIM_K_IDLE_NEVER. +; Returns VMS-structured status. +; +; This routine is executed in uniprocessor configuration, before secondary processors +; are configured into system yet. +; +; Entrance IPL must be 0 to ASTDEL. +; Returns at the caller's original IPL. +; +;- + ARG_CPU_MASK = 4 + ARG_IDLE = 8 + ARG_TIMESYNC = 12 + ARG_NOPATCH = 16 +; + .ENABLE LOCAL_BLOCK + .ENTRY KCALL_ONLOAD, ^M<> + CLRL KERROR_CAUSE ; no error cause yet + MOVB ARG_IDLE(AP), IDLE_CTRL ; store idle control setting + MOVB ARG_TIMESYNC(AP), TIMESYNC_CTRL ; store TIMESYNC control setting + DSBINT #IPL$_POWER, -(SP), - ; block interrupts while we are patching system code + ENVIRON=UNIPROCESSOR ; ... + PUSHL ARG_NOPATCH(AP) ; check if dynamic patches can be applied + CALLS #1, CHECK_PATCHES ; ... + XBLBC R0, 200$ ; lbc - cannot, return error + ; + ; Check if idle patch can be performed + ; + CMPB IDLE_CTRL, #SIM_K_IDLE_NEVER ; will run idle? + XBEQL 100$ ; eql - never, do not patch idle code + ; + ; Check that SCH$CUR_TO_COM is not present. + ; Routine SCH$CUR_TO_COM was deleted in 1991 and used to contain "CLRL G^SCH$GL_IDLE_CPUS" + ; that we may have wanted to handle intelligently (except in IDLE NEVER mode) if it were present + ; since it wakes up all the CPUs to schedule just one process. Even in IDLE NEVER mode, + ; and likewise on real hardware VAXen, it would have caused a bank run on the SCHED lock. + ; + MOVL #VSMP_MSG_LDR_SCH_CUR_TO_COM, - ; assume error + KERROR_CAUSE ; ... + CMPL G^SCH$CUR_TO_COM, NX_JMP_INSTR ; check SCH$CUR_TO_COM aganst non-loaded JMP vector + BNEQ 20$ ; ... + CMPW G^SCH$CUR_TO_COM + 4, NX_JMP_INSTR + 4 ; ... + BEQL 30$ ; ... +20$: + CMPL G^SCH$CUR_TO_COM, NX_JSB_INSTR ; check SCH$CUR_TO_COM aganst non-loaded JSB/RSB vector + XBNEQ 300$ ; ... + CMPW G^SCH$CUR_TO_COM + 4, NX_JSB_INSTR + 4 ; ... + XBNEQ 300$ ; ... + CMPB G^SCH$CUR_TO_COM + 6, NX_JSB_INSTR + 6 ; ... + XBNEQ 300$ ; ... +30$: + ; + ; Hook into EXE$PROC_IDLE + ; + MOVL #VSMP_MSG_LDR_EXE_PROC_IDLE, - ; assume error + KERROR_CAUSE ; ... + CMPW G^EXE$PROC_IDLE, JMP_INSTR ; patch EXE$PROC_IDLE vector + XBNEQ 300$ ; ... neq - unexpected structure + MOVL G^EXE$PROC_IDLE+2, - ; ... save old routine address + PROC_IDLE_OLD ; ... + MOVAB PROC_IDLE, - ; ... set new address + G^EXE$PROC_IDLE + 2 ; ... +100$: + ; + ; Signal to virtual machine monitor (SIMH) + ; + CLRL -(SP) ; idle sleep enabled/disabled status + CMPB IDLE_CTRL, #SIM_K_IDLE_ON ; ... + BNEQ 110$ ; ... + MOVZBL #1, (SP) ; ... +110$: + PUSHL #IPL$_RESCHED ; synchronization window control + PUSHL #IPL$_QUEUEAST ; ... + PUSHL SYNCW_WINSIZE_ILK ; ... + PUSHL SYNCW_WINSIZE_SYS ; ... + PUSHL SYNCW_ON ; ... + CLRL -(SP) ; placeholder for granted options + PUSHL SMP_WANT_OPTIONS ; requested options + PUSHL SMP_MUST_OPTIONS ; ... + CLRL -(SP) ; guest OS version (0 = not specific) + PUSHL #1 ; guest OS id (1 = VMS) + PUSHAB G^SCH$GL_IDLE_CPUS ; address of cpu idle mask + PUSHL #IPL$_SYS_CRITICAL ; sys critical section level + CLRL -(SP) ; response status placeholder + PUSHL #1 ; guest API version + PUSHL #VAXMP_API_OP_INIT_SMP ; request code + PUSHL #VAXMP_API_SIGNATURE ; request block signature + MTPR SP, #PR$_SIMH ; signal to SIMH + MOVL 12(SP), R0 ; save response + ADDL #<16*4>, SP ; remove argument block off the stack + CMPL R0, #1 ; check for successful response + BEQL 190$ ; ok - go to successful exit + ; + ; Simulator rejected SMP initiation + ; + MOVB #SIM_K_IDLE_NEVER, IDLE_CTRL ; shut off idle loop control + MOVB #SIM_K_TIMESYNC_OFF, - ; ... and TimeSync + TIMESYNC_CTRL ; ... + MOVL R0, KERROR_CAUSE ; return error code + MOVL #VSMP_MSG_VM_REFUSED, R0 ; ... + BRB 200$ ; ... +190$: + FIND_CPU_DATA R1 ; locate CPU database + MTPR R1, S^#PR$_WHAMI ; set up WHAMI register for the primary + PUSHL ARG_NOPATCH(AP) ; apply dynamic patches + CALLS #1, APPLY_PATCHES ; ... + BSBW VSMP$SETUP_SMP ; set up SMP environment + ; + ; Increase count of interlocked instruction retries to at least that hardwired + ; in $INSQHI/$INSQTI/$REMQHI/$REMQTI macros, i.e. 900000, unless it is already + ; set even higher and unless increasng it is disabled by NOPATCH option. + ; + CMPL G^EXE$GL_LOCKRTRY, #900000 ; already higher? + BGEQ 195$ ; geq - skip + BBS #PATCH_ID_LOCKRTRY, - ; bs - change is disabled + ARG_NOPATCH(AP), 195$ ; ... + MOVL #900000, G^EXE$GL_LOCKRTRY ; set to number used by $INSQHI and co. +195$: + MOVL ARG_CPU_MASK(AP), - ; set mask of available CPUs + G^SMP$GL_CPUCONF ; ... + CLRL KERROR_CAUSE ; no error + MOVZBL #SS$_NORMAL, R0 ; returns success code +200$: + ; + ; common return handler + ; + BLBC R0, 210$ ; if about to perform successful return, activate TimeSync TQE + SETIPL #IPL$_TIMER, - ; ... drop IPL to TIMER lock + ENVIRON=UNIPROCESSOR ; ... + PUSHL R0 ; ... preserve register across the call + BSBW ACTIVATE_TIMESYNC_TQE ; ... set up and activate TQE + POPL R0 ; ... restore register +210$: ; ... + ENBINT (SP)+ ; restore caller's IPL + RET ; return to the caller +300$: + MOVL #VSMP_MSG_LDR_VERIFY, R0 ; common pre-load verification error return + BRB 200$ ; ... + .DISABLE LOCAL_BLOCK + + +;+ +; +; Get and set current idle control. +; +; uint32 kcall_get_idle(uint32* idle); +; uint32 kcall_set_idle(uint32 idle); +; +; Caller is responsible for validating the supplied "idle" value. +; Returns VMS-structured status. +; +;- + ARG_IDLE = 4 +; + .ENTRY KCALL_GET_IDLE, ^M<> + MOVZBL IDLE_CTRL, @ARG_IDLE(AP) ; return the value + MOVZBL #SS$_NORMAL, R0 ; return success status + RET ; ... + + .ENABLE LOCAL_BLOCK + .ENTRY KCALL_SET_IDLE, ^M<> + LOCK LOCKNAME=SCHED, - ; synchronize access to SCH$GL_IDLE_CPUS + SAVIPL=-(SP), - ; ... + PRESERVE=NO ; ... + MOVB ARG_IDLE(AP), IDLE_CTRL ; store new setting + CMPB IDLE_CTRL, #SIM_K_IDLE_ON ; if enabling idle, just return + BEQL 10$ ; ... + CLRL G^SCH$GL_IDLE_CPUS ; if disabling idle, wakeup all other CPUs + CLRL G^VBSS$GL_STALLED_CPUS ; ... and clear the mask of CPUs stalled for RBS +10$: + UNLOCK LOCKNAME=SCHED,- ; release SCHED spinlock and revert IPL + PRESERVE=NO,- ; ... + NEWIPL=(SP)+ ; ... + ; + ; notify the simulator + ; + CLRL -(SP) ; response status placeholder + CLRL -(SP) ; idle sleep enabled/disabled status + CMPB IDLE_CTRL, #SIM_K_IDLE_ON ; ... + BNEQ 20$ ; ... + MOVZBL #1, (SP) ; ... +20$: + PUSHL #1 ; guest API version + PUSHL #VAXMP_API_OP_SET_IDLE ; request code + PUSHL #VAXMP_API_SIGNATURE ; request block signature + MTPR SP, #PR$_SIMH ; signal to SIMH + ADDL #<5*4>, SP ; remove argument block off the stack + MOVZBL #SS$_NORMAL, R0 ; return success status + RET ; to the caller + .DISABLE LOCAL_BLOCK + +;+ +; +; Output string to console. +; +; void kprint(const char* s); +; void kprint_crlf(const char* s); +; +; Prints string to console. +; The latter version adds CR/LF at the end. +; +; Print hexadecimal: +; +; void kprint_x8_value(uint32 val); +; +; Print prefix followed by hexadecimal: +; +; void kprint_x8(const char* s, uint32 val); +; +; Print prefix followed by hexadecimal and CR-LF: +; +; void kprint_x8_crlf(const char* s, uint32 val); +; +;- + ARG_STR = 4 +; + .ENTRY KPRINT, ^M + MOVL ARG_STR(AP), R1 + CLRL R11 + JSB G^EXE$OUTZSTRING + RET + + .ENTRY KPRINT_CRLF, ^M + CALLG (AP), KPRINT + PUSHAB CRLF + CALLS #1, KPRINT + RET +; + ARG_VAL = 4 +; + .ENTRY KPRINT_X8_VALUE, ^M + MOVL ARG_VAL(AP), R1 + CLRL R11 + JSB G^EXE$OUTHEX + RET +; + ARG_STR = 4 + ARG_VAL = 8 +; + .ENTRY KPRINT_X8, ^M + PUSHL ARG_STR(AP) + CALLS #1, KPRINT + PUSHL ARG_VAL(AP) + CALLS #1, KPRINT_X8_VALUE + RET +; + ARG_STR = 4 + ARG_VAL = 8 +; + .ENTRY KPRINT_X8_CRLF, ^M + PUSHL ARG_STR(AP) + CALLS #1, KPRINT + PUSHL ARG_VAL(AP) + CALLS #1, KPRINT_X8_VALUE + PUSHAB CRLF + CALLS #1, KPRINT + RET + +;+ +; +; Idle loop handler. +; Called by SCH$RESCHED idle loop. +; +; Inputs: +; R3 points to CPU database +; R1 contains CPU$L_PHY_CPUID(R3) +; locks not held +; IPL = RESCHED +; +; Output: +; Preserve registers except R0. +; The value of R0 at exit (LBS/LBC) controls loop flow, see the source for SCH$RESCHED. +; This routine always returns R0 LBC, to avoid the loop shifting to the next stage +; where EXE$PROC_IDLE is no longer polled. +; +;- + .ENABLE LOCAL_BLOCK +PROC_IDLE: + PUSHL R6 ; save registers + PUSHL R7 ; ... + MOVL G^EXE$GL_ABSTIM_TICS, R6 ; get tick count at the start of this idle wait +10$: + JSB @PROC_IDLE_OLD ; call previous handler if any + XBLBC R0, 110$ ; LBC - return + XBBC #CPB$V_RUN, - ; run capability present? + CPU$L_CAPABILITY(R3), 100$ ; ... + XBBC CPU$L_PHY_CPUID(R3), - ; is processor still idle? + G^SCH$GL_IDLE_CPUS, 100$ ; ... + TSTL G^SCH$GL_TBSH ; is class scheduler present? + BEQL 20$ ; ... + JSB G^SCH$CLASS_IDLE ; call class scheduler + XBBC #CPB$V_RUN, - ; run capability present? + CPU$L_CAPABILITY(R3), 100$ ; ... + XBBC CPU$L_PHY_CPUID(R3), - ; is processor still idle? + G^SCH$GL_IDLE_CPUS, 100$ ; ... +20$: + CMPB IDLE_CTRL, #SIM_K_IDLE_ON ; is SIMH-level sleep enabled + BNEQ 30$ ; neq - no, do not attempt it + ; + ; call VMM to enter sleep mode + ; + BSBW CALC_IDLE_SLEEP_TICKS ; calculate ticks to sleep -> R0 + PUSHL R0 ; ticks to sleep + CLRL -(SP) ; response status placeholder + PUSHL #1 ; guest API version + PUSHL #VAXMP_API_OP_IDLE ; request code + PUSHL #VAXMP_API_SIGNATURE ; request block signature + MTPR SP, #PR$_SIMH ; signal to SIMH + ADDL #<5*4>, SP ; remove argument block off the stack + BRB 90$ +30$: + ; + ; call VMM to signal idle loop pulse... does not cause sleep, + ; but can be used by VMM to reset synchronization window + ; + CLRL -(SP) ; response status placeholder + PUSHL #1 ; guest API version + PUSHL #VAXMP_API_OP_IDLE_PULSE ; request code + PUSHL #VAXMP_API_SIGNATURE ; request block signature + MTPR SP, #PR$_SIMH ; signal to SIMH + ADDL #<4*4>, SP ; remove argument block off the stack +90$: + ;; + ;; as a safety net, once every 1/2 seconds mark current CPU as non-idle + ;; and cause it to re-attempt scheduling + ;; + ;; this may come helpful for example when class scheduler is started or fails, + ;; since routine make_tbs_jobs_computable in [SYS.SRC]SYSSCHED.B32 called in these + ;; cases does not clear SCH$GL_IDLE_CPUS + ;; + SUBL3 R6, G^EXE$GL_ABSTIM_TICS, R7 ; get number of ticks passed since PROC_IDLE was called + CMPL R7, #50 ; more than 1/2 second? + XBLSSU 10$ ; lssu - no + LOCK LOCKNAME=SCHED, - ; mark this processor as not idle to re-attemp scheduling + LOCKIPL=#IPL$_SYNCH ; ... + BICL CPU$L_CPUID_MASK(R3), - ; ... + G^SCH$GL_IDLE_CPUS ; ... + UNLOCK LOCKNAME=SCHED, - ; ... + NEWIPL=#IPL$_RESCHED ; ... +100$: + CLRL R0 ; status: do not shift to the next idle loop phase +110$: + POPL R7 ; restore registers + POPL R6 ; ... + RSB ; ... + .DISABLE LOCAL_BLOCK + +;+ +; +; Calculate number of ticks for VAX MP simulator idle sleep +; +; Inputs: +; R3 points to CPU database +; IPL = RESCHED +; +; Output: +; Preserve all registers except R0. +; The value of R0 at exit contains number of ticks to sleep. +; 0 => can sleep till the end of current clock tick (i.e. next HWCLK interrupt) +; 1 => till the end of current tick and then for one full tick after it +; 2 => till the end of current tick and then for two full ticks after it +; . . . . +; N => till the end of current tick and then for N full ticks after it +; +; For the primary CPU always returns 0. +; Primary CPU maintains time for all other VCPUs and performs other system-wide activity +; that other VCPUs depend on, and it cannot be allowed to sleep multiple ticks. +; +; Secondary VCPUs can sleep multiple ticks. Primary limitation is that SMP sanity check +; should not be triggerred. +; +; On every hardware clock interrupt each processor reinitializes its CPU$W_SANITY_TIMER +; to the value of SGN$GW_SMP_SANITY_CNT (SYSGEN SMP_SANITY_CNT parameter, with default value 300). +; +; Every SGN$GW_SMP_TICK_CNT ticks (SYSGEN parameter SMP_TICK_CNT, default value 30) +; previous processor in the SMP sanity monitoring chain substracts SGN$GW_SMP_TICK_CNT from +; this processor's CPU$W_SANITY_TIMER. If the result is below zero, bugcheck is generated. +; +; Theoretically idling secondary CPU can be allowed to sleep up to (CPU$W_SANITY_TIMER - 1) ticks, +; i.e. under default value of parameters for almost 300 seconds. In practice much shorter idle +; sleep period time must be used. If we allowed long sleep intervals close to SGN$GW_SMP_SANITY_CNT, +; previous processor in the chain could wake up from sleep and jump at us performing multiple +; back-to-back clock interrupts (sent by VAX MP in back-to-back fashion) and substract +; close SGN$GW_SMP_SANITY_CNT from our CPU$W_SANITY_TIMER, putting the system on the threshold +; of bugcheck. To prevent this from happenning and maintain sufficient safety margin, we limit +; the duration of idle sleep to at most a quarter of the maximum sanity interval. +; +; Furthermore, to prevent any unexpected and unintended effects we limit secondaries idle sleep +; duraton to at most 1/2 second (50 ticks). This provides very low overhead, and there is no +; inscentive to use longer sleep intervals. +; +; We employ algorithm that calculates idle sleep time (R ticks) as follows: +; +; RMAX = SGN$GW_SMP_SANITY_CNT / 4 ; maximum idle sleep time +; RMAX = min(50, RMAX) ; but not more than 0.5 second +; SAFE_MARGIN = max(RMAX, 100) ; want to have at least this many ticks left +; ; after worst-case substraction, to provide +; ; safrty margn against host scheduler delaying +; ; execution of our VCPU thread and hence delaying +; ; processing the clock interrupt that would refill +; ; our CPU$W_SANITY_TIMER +; R = thiscpu.CPU$W_SANITY_TIMER - SGN$GW_SMP_TICK_CNT ; SGN$GW_SMP_TICK_CNT can be substracted any time +; R -= RMAX ; RMAX can be sustracted any time +; R -= SAFE_MARGIN ; want to have SAFE_MARGIN left +; R = min(R, RMAX) ; do not sleep longer than RMAX +; R = max(0, R) ; not less than 0 +; +; Under regular circumstances this algorithm will usually produce a value of R close to 50 ticks (0.5 seconds). +; +;- + .ENABLE LOCAL_BLOCK +CALC_IDLE_SLEEP_TICKS: + ASSUME CPB$V_PRIMARY EQ 0 + BLBC CPU$L_CAPABILITY(R3), 10$ ; if LBC, this is a secondary CPU + CLRL R0 ; primary - return 0 + RSB ; to the caller +10$: + PUSHL R1 ; preserve scratch registers + PUSHL R2 ; ... + MOVZWL G^SGN$GW_SMP_SANITY_CNT, R1 ; R1 = RMAX + DIVL #4, R1 ; ... + CMPL R1, #50 ; ... + BLEQ 20$ ; ... + MOVZBL #50, R1 ; ... +20$: + MOVL R1, R2 ; R2 = SAFE_MARGIN + CMPL R2, #100 ; ... + BGEQ 30$ ; ... + MOVZBL #100, R2 ; ... +30$: + MOVZWL CPU$W_SANITY_TIMER(R3), R0 ; R = CPU$W_SANITY_TIMER - SGN$GW_SMP_TICK_CNT + MOVZWL G^SGN$GW_SMP_TICK_CNT, -(SP) ; ... + SUBL (SP)+, R0 ; ... + SUBL R1, R0 ; R -= RMAX + SUBL R2, R0 ; R -= SAFE_MARGIN + CMPL R0, R1 ; R = min(R, RMAX) + BLEQ 40$ ; ... + MOVL R1, R0 ; ... +40$: + TSTL R0 ; R = max(0, R) + BGEQ 50$ ; ... + CLRL R0 ; ... +50$: + POPL R2 ; restore scratch registers + POPL R1 ; ... + RSB ; return to the caller + .DISABLE LOCAL_BLOCK + +;+ +; +; Check if dynamic patches can be applied. +; +; Inputs: +; 4(AP) = nopatch mask +; IPL = 31 +; +; Outputs: +; Status in R0. +; Sub-status in KERROR_CAUSE. +; +;- + .ENTRY CHECK_PATCHES, ^M<> + .ENABLE LOCAL_BLOCK + MOVL #VSMP_MSG_LDR_SMP_JMPVEC, - ; assume error + KERROR_CAUSE ; ... + MOVAB SMP_JMPVEC, R0 ; check SMP vector first +10$: + MOVL (R0), R1 ; get address of next vectored routine + BEQL 20$ ; eql - end of list + CMPW (R1), JMP_INSTR ; check if JMP @# + BNEQ 100$ ; neq - unexpected content + ADDL #<3*4>, R0 ; next vector + BRB 10$ ; go check next entry in the table +20$: + PUSHL 4(AP) ; check dynpatches + CALLS #1, CHECK_DYNPATCHES ; ... + MOVL R0, KERROR_CAUSE ; save error status + BLBC R0, 100$ ; lbc - take error exit + MOVZBL #SS$_NORMAL, R0 ; sussessful return to the caller + RET ; ... +100$: + MOVL #VSMP_MSG_LDR_VERIFY, R0 ; return error to the caller + RET ; ... + .DISABLE LOCAL_BLOCK + +;+ +; +; Apply dynamic patches. +; +; Inputs: +; 4(AP) = nopatch mask +; IPL = 31 +; +;- + .ENTRY APPLY_PATCHES, ^M + .ENABLE LOCAL_BLOCK + MOVAB SMP_JMPVEC, R0 ; apply SMP vector first +10$: + MOVL (R0)+, R1 ; get address of next vectored routine + BEQL 30$ ; eql - end of list + ; + ; see if "store old value" is present + ; + TSTL 4(R0) ; saving old vector address value requested? + BEQL 20$ ; eql - no, skip + MOVAB 4(R0), R2 ; address of save location + ADDL (R2), R2 ; ... + MOVL 2(R1), (R2) ; save old JMP@# vector transfer address +20$: + ADDL3 R0, (R0), 2(R1) ; address of replacement code in JMP @# + ADDL #8, R0 ; next vector + BRB 10$ ; go check next entry in the table +30$: + PUSHL 4(AP) ; apply dynpatches + CALLS #1, APPLY_DYNPATCHES ; ... + JSB FLUSH_INSTRUCTION_STREAM ; flush instructon stream after patching the code + RET + .DISABLE LOCAL_BLOCK + +;+ +; +; FLUSH_INSTRUCTION_STREAM - after instruction patching +; +;- + .ENABLE LOCAL_BLOCK +FLUSH_INSTRUCTION_STREAM:: + MOVPSL -(SP) ; execute REI to flush instruction stream + MOVAB 10$, -(SP) ; ... + REI ; ... +10$: + RSB ; return to the caller + .DISABLE LOCAL_BLOCK + + + .MACRO JMPVEC SRC, DST, SAVE + .ADDRESS SRC + .LONG DST - . + .IF NB SAVE + .LONG SAVE - . + .IFF + .LONG 0 + .ENDC + .ENDM JMPVEC + + .ALIGN LONG +SMP_JMPVEC: + JMPVEC EXE$INIPROCREG, VSMP$INIPROCREG + JMPVEC SMP$INTPROC, VSMP$INTPROC + JMPVEC SMP$INTALL, VSMP$INTALL + JMPVEC SMP$INTALL_BIT, VSMP$INTALL_BIT + JMPVEC SMP$INTALL_ACQ, VSMP$INTALL_ACQ + JMPVEC SMP$INTALL_BIT_ACQ, VSMP$INTALL_BIT_ACQ + JMPVEC SMP$SETUP_CPU, VSMP$SETUP_CPU + JMPVEC SMP$SETUP_SMP, VSMP$SETUP_SMP + JMPVEC SMP$START_CPU, VSMP$START_CPU + JMPVEC SMP$STOP_CPU, VSMP$STOP_CPU + JMPVEC SMP$SHOW_CPU, VSMP$SHOW_CPU + JMPVEC SMP$HALT_CPU, VSMP$HALT_CPU + JMPVEC SMP$CONTROLP_CPUS, VSMP$CONTROLP_CPUS + JMPVEC SMP$INVALID_SINGLE, VSMP$INVALID_SINGLE + JMPVEC SMP$VIRTCONS_SERVER, VSMP$VIRTCONS_SERVER + JMPVEC SMP$SELECT_PRIMARY, VSMP$SELECT_PRIMARY + JMPVEC EXE$READ_TODR, VSMP$READ_TODR + JMPVEC EXE$WRITE_TODR, VSMP$WRITE_TODR + JMPVEC CON$OWNCTY, VCON$OWNCTY + JMPVEC CON$RELEASECTY, VCON$RELEASECTY + JMPVEC CON$GETCHAR, VCON$GETCHAR, VCON$OLD_GETCHAR + JMPVEC CON$PUTCHAR, VCON$PUTCHAR, VCON$OLD_PUTCHAR + .LONG 0 ;; end of vector list + +;;*********************************************************************************** +;; End of kernel-resident part loaded into nonpaged memory +;;*********************************************************************************** + +;; All KLOAD_DATA and KLOAD_CODE from all source files go into here + + .PSECT KLOAD_ZZZ QUAD, PIC, EXE, NOSHR, WRT +KLOAD_END == . - 1 ; end of loadable code + .BLKB 1 ; force the section to appear in the linker map + +;;*********************************************************************************** +;; Ended kernel-resident part loaded into nonpaged memory +;;*********************************************************************************** + + .END diff --git a/src/VMS_VSMP/simhdef.mar b/src/VMS_VSMP/simhdef.mar new file mode 100644 index 0000000..2e99b89 --- /dev/null +++ b/src/VMS_VSMP/simhdef.mar @@ -0,0 +1,41 @@ +;; +;; SIMH VAX MP API and related definitions +;; +;; Module: simhdef.mar +;; Version: 1.0 +;; Author: Sergey Oboguev +;; Created: 11-Dec-2011 +;; Revision History: +;; none +;; + + .MACRO SIMHDEF + + PR$_SIMH = 254 ; register for communication between VAX MP VM and guest system + + SIM_K_IDLE_OFF = 0 ; CPU idle sleep is disabled + SIM_K_IDLE_ON = 1 ; CPU idle sleep is enabled + SIM_K_IDLE_NEVER = 2 ; CPU idle sleep is disabled and cannot be enabled + + SIM_K_TIMESYNC_OFF = 0 ; TimeSync (syncing system time from host) is disabled + SIM_K_TIMESYNC_ON = 1 ; TimeSync is enabled + + VAXMP_API_SIGNATURE = ^X484D4953 ; VAX MP API request block signature + + ; + ; VAX MP API request codes + ; + VAXMP_API_OP_QUERY = 1 + VAXMP_API_OP_MEMBAR = 2 + VAXMP_API_OP_INIT_SMP = 3 + VAXMP_API_OP_START_CPU = 4 + VAXMP_API_OP_IPINTR = 5 + VAXMP_API_OP_IDLE = 6 + VAXMP_API_OP_IDLE_PULSE = 7 + VAXMP_API_OP_SET_IDLE = 8 + VAXMP_API_OP_GETTIME_VMS = 9 + + .MACRO SIMHDEF + .ENDM SIMHDEF + + .ENDM SIMHDEF diff --git a/src/VMS_VSMP/smpcore.mar b/src/VMS_VSMP/smpcore.mar new file mode 100644 index 0000000..49e3902 --- /dev/null +++ b/src/VMS_VSMP/smpcore.mar @@ -0,0 +1,1130 @@ + .TITLE SMPCORE + .IDENT /V1.00/ + +;; +;; Kernel-mode loadable code for SIMH VMS virtual SMP utility. +;; SMP core part -- provide replacement of SMP$xxx routines supplied by SYSLOA. +;; +;; This module defines SMP-capable version of SMP$xxx routines that override uniprocessor +;; version of those routines supplied by SYSLOA650. When VSMP is activated, CPULOA vectors +;; are changed to point to new routines instead of their SYSLOA650 versions. +;; +;; Two of the routines, VSMP$SETUP_CPU and VSMT$START_CPU are located +;; in a separate file STARTCPU.MAR. +;; +;; Tested with OpenVMS VAX version 7.3. +;; +;; Module: smpcore.mar +;; Version: 1.0 +;; Author: Sergey Oboguev (oboguev@yahoo.com) +;; Created: 10-Dec-2011 +;; Revision History: +;; none +;; + .LIBRARY "SYS$LIBRARY:LIB" + + SYS_DEFS ; common VMS definitions + XBRANCH ; extended branch instructions + $CLUBDEF ; cluster block structure + $VPFLAGSDEF ; vector processor flags + + XBRANCH ; Extended branch instructions + SIMHDEF ; SIMH API definitions + + CPU$V_PING == 27 ; bit-code for "ping" interprocessor request + + ; + ; Optimized version of BBCCI: try non-interlocked first + ; + .MACRO BBCCI_NI BIT, FIELD, L + BBC BIT, FIELD, L + BBCCI BIT, FIELD, L + .ENDM BBCCI_NI + + ; + ; Perform vector/scalar processors memory synchronization + ; + .MACRO VECTOR_MEMORY_SYNC ?L + ASSUME EXE$V_VP_PRESENT EQ 0 + BLBC G^EXE$GL_VP_FLAGS, L ; check if vector processor is present + MFPR #PR$_VMAC, -4(SP) ; perform scalar/vector memory sync +L: .BLKB 0 + .ENDM VECTOR_MEMORY_SYNC + +;;*********************************************************************************** +;; Kernel-resident part that is loaded into nonpaged memory -- data +;;*********************************************************************************** + + .PSECT KLOAD_DATA QUAD, PIC, EXE, NOSHR, WRT + +;;*********************************************************************************** +;; Kernel-resident part that is loaded into nonpaged memory -- code +;;*********************************************************************************** + + .PSECT KLOAD_CODE QUAD, PIC, EXE, SHR, NOWRT +;++ +; +; SMP$INTPROC - interrupt a specific processor +; +; Inputs: +; +; R0 = The physical CPU ID of the processor to be interrupted +; IPL is ASTDEL or above +; +;-- + +VSMP$INTPROC:: ; interrupt the specified processor + ROTL R0, #1, -(SP) ; target CPU mask + CLRL -(SP) ; status placeholder + PUSHL #1 ; guest API version + PUSHL #VAXMP_API_OP_IPINTR ; request code + PUSHL #VAXMP_API_SIGNATURE ; request block signature + MTPR SP, #PR$_SIMH ; signal to SIMH + ADDL #<5*4>, SP ; remove request block off the stack + RSB ; return to the caller + + +;++ +; +; SMP$INTALL - interrupt all processors in the active set +; +; Inputs: +; +; IPL is ASTDEL or above +; +;-- + .ENABLE LOCAL_BLOCK +VSMP$INTALL:: ; interrupt all processors + PUSHL R1 ; save register + FIND_CPU_DATA R1 ; locate CPU database + ROTL CPU$L_PHY_CPUID(R1), #1, R1 ; this CPU ID mask + BICL3 R1, G^SMP$GL_ACTIVE_CPUS, R1 ; mask of active CPUs less current + BEQL 10$ ; eql - there are none + ; + ; Build SIMH request block and perform the request + ; + PUSHL R1 ; target CPU IDs mask + CLRL -(SP) ; status placeholder + PUSHL #1 ; guest API version + PUSHL #VAXMP_API_OP_IPINTR ; request code + PUSHL #VAXMP_API_SIGNATURE ; request block signature + MTPR SP, #PR$_SIMH ; signal to SIMH + ADDL #<5*4>, SP ; remove request block off the stack +10$: + POPL R1 ; restore register + RSB ; return to the caller + .DISABLE LOCAL_BLOCK + + +;++ +; +; SMP$INTALL_BIT - interrupt all processors in the active set with work request bit +; +; Inputs: +; +; R2 = work request bit to set +; IPL is ASTDEL or above +; +;-- + .ENABLE LOCAL_BLOCK +VSMP$INTALL_BIT:: + PUSHR #^M ; save registers + FIND_CPU_DATA R1 ; locate CPU database + ROTL CPU$L_PHY_CPUID(R1), #1, R1 ; this CPU ID mask + BICL3 R1, G^SMP$GL_ACTIVE_CPUS, R1 ; mask of active CPUs less current + BEQL 100$ ; eql - there are none + ; + ; Set work bit in CPU DB's of all destination processors + ; + MOVL R1, R4 ; save interrupt destination mask + MOVAL G^SMP$GL_CPU_DATA, R3 ; address of CPU data base vector +20$: + FFS #0, #32, R1, R0 ; find next CPU ID + BEQL 40$ ; eql - finished + BBCC R0, R1, 30$ ; clear this CPU's ID in the mask +30$: MOVL (R3)[R0], R0 ; target CPUs data area + BBSSI R2, CPU$L_WORK_REQ(R0), 20$ ; set work request bit + BRB 20$ ; ... + ; + ; Build SIMH request block and perform the request + ; +40$: PUSHL R4 ; target CPU IDs mask + CLRL -(SP) ; status placeholder + PUSHL #1 ; guest API version + PUSHL #VAXMP_API_OP_IPINTR ; request code + PUSHL #VAXMP_API_SIGNATURE ; request block signature + MTPR SP, #PR$_SIMH ; signal to SIMH + ADDL #<5*4>, SP ; remove request block off the stack +100$: + POPR #^M ; restore registers + RSB ; return to the caller + .DISABLE LOCAL_BLOCK + + +;++ +; +; SMP$INTALL_ACQ - acquire CPU bitmask mutex and interrupt all processors +; +; Inputs: +; +; IPL is ASTDEL or above +; +;-- + +VSMP$INTALL_ACQ:: + LOCK MUTEX=SMP$GL_CPU_MUTEX, - ; prevent concurrent changes to CPU bitmask + SHARE=YES ; ... + BSBW VSMP$INTALL ; interrupt other CPUs + UNLOCK MUTEX=SMP$GL_CPU_MUTEX, - ; release CPU bitmask + SHARE=YES ; ... + RSB ; return to the caller + + +;++ +; +; SMP$INTALL_BIT_ACQ - acquire CPU bitmask mutex and interrupt all processors +; with work request bit +; +; Inputs: +; +; R2 = work request bit to set +; IPL is ASTDEL or above +; +;-- + +VSMP$INTALL_BIT_ACQ:: + LOCK MUTEX=SMP$GL_CPU_MUTEX, - ; prevent concurrent changes to CPU bitmask + SHARE=YES ; ... + BSBW VSMP$INTALL_BIT ; interrupt other CPUs with work request bit + UNLOCK MUTEX=SMP$GL_CPU_MUTEX, - ; release CPU bitmask + SHARE=YES ; ... + RSB ; return to the caller + + +;++ +; +; SMP$STOP_CPU - CPU-specific kernel mode code for DCL STOP /CPU command +; +; Inputs: +; +; R2 = CPU database address of the target processor +; +;-- + +VSMP$STOP_CPU:: + .ENABLE LOCAL_BLOCK + CMPB CPU$B_STATE(R2), - ; did secondary processor refuse to start? + #CPU$C_BOOT_REJECTED ; ... + BNEQ 10$ ; no - exit + MOVB #CPU$C_STOPPED, CPU$B_STATE(R2) ; yes, reset state +10$: + MOVZBL #SS$_NORMAL,R0 ; load success code + RSB ; and return to caller + .DISABLE LOCAL_BLOCK + + +;++ +; +; SMP$SHOW_CPU - CPU-specific kernel mode code for DCL SHOW /CPU command, +; not called for KA650/KA655 +; +;-- + +VSMP$SHOW_CPU:: + RSB + + +;++ +; +; SMP$HALT_CPU - CPU-specific code to halt the secondary processor +; +; Inputs: +; +; R3 = CPU database address of the current processor +; (NB: may be R2 in earlier VMS versions) +; Executing on the secondary processor that is to be halted +; +;-- + +VSMP$HALT_CPU:: + SETIPL #IPL$_POWER, - ; disable interrupts + ENVIRON=UNIPROCESSOR ; ... + HALT ; HALT (can also do BRB to self) + +;++ +; +; SMP$CONTROLP_CPUS - Determine, if possible, whether any CPUs in the active +; set have been halted via CTRL-P/HALT. If the console does not provide this +; data, assume that some CPU is halted and return the active set. +; +; Inputs: +; +; None. +; +; Outputs: +; +; R0 = Bitmask of CPUs halted via CTRL-P/HALT anded with the active set. +; If cannot determine which CPUs are halted, return just the active mask. +; +;-- + +VSMP$CONTROLP_CPUS:: + ;; MOVL G^SMP$GL_ACTIVE_CPUS, R0 ; mask for all active CPUs + CLRL R0 ; SIMH MicroVAX 39x0 disables console when SMP is active + RSB ; ... + + +;++ +; +; SMP$INVALID_SINGLE - respond to a TBIS IPI request +; +; Inputs: +; +; R1 = CPU database address +; SMP$GL_INVALID = address for which to perform invalidate +; +; Outputs: +; +; R0, R2 destroyed. +; +;- + +VSMP$INVALID_SINGLE:: + .ENABLE LOCAL_BLOCK + MOVL G^SMP$GL_INVALID, R2 ; save the address to invalidate + VECTOR_MEMORY_SYNC ; perfrorm vector/scalar processors memory sync + ; + ; Raise the flag indicating this CPU had received the request + ; + XSSBI CPU$L_PHY_CPUID(R1), G^SMP$GL_ACK_MASK + ; + ; Wait here till the interrupting CPU modifies the PTE and says each target CPU + ; to proceed by asserting each target's TBACK flag + ; +10$: BUSYWAIT TIME=G^SGN$GL_SMP_SPINWAIT, - + INST1=, - + INST2=, - + DONLBL=20$ + BBCCI #CPU$V_TBACK, - ; check TBACK interlocked and reset it for next request + CPU$L_WORK_REQ(R1), 10$ ; ... (if TBACK was clear when accessed interlocked, go spin again) + MTPR R2, S^#PR$_TBIS ; invalidate TLB entry for the requested page + RSB ; return to the caller + +100$: XDELTA_WAIT ; check if should temporarily enter benign state due to XDelta + RSB ; return back into busy-wait loop + .DISABLE LOCAL_BLOCK + + +;++ +; +; SMP$SELECT_PRIMARY - verify that the selected CPU (or one of selected CPUs) is a valid candidate to become primary +; +; Inputs: +; +; R5 = Bitmask of eligible CPUs +; IPL = RESCHED +; +; Outputs: +; +; R0 = LBS indicates that CPU represented in R2 is eligible to become primary CPU +; = LBC indicates that no CPUs are eligible to become primary +; R2 = CPU node id of CPU that will become primary +; +; R1 is destroyed and all other registers are preserved +; +;- + +VSMP$SELECT_PRIMARY:: + CLRL R0 ; VAX MP does not support switching primary processor + RSB ; ... + + +;++ +; +; SMP$SETUP_SMP - set up overall SMP environment +; +; This routine is called as the last stage of VSMP LOAD command. +; +; Inputs: +; +; IPL = 31 +; +; SGN$GL_SMP_CPUS = Bitmask of potential CPU nodes that are allowed to be booted +; +; SMP$GL_CPUCONF = CPU configuration bitmask +; +; Outputs: +; +; Affinity for the PRIMARY CPU is established for the OPA0: device. +; +; Multiprocessor environment established +; +;-- + + .ENABLE LOCAL_BLOCK +VSMP$SETUP_SMP:: + PUSHR #^M ; preserve scratch registers + FIND_CPU_DATA R1 ; locate primary CPU's per-CPU database + ; + ; Set affinity of OPA0's UCB to the primary CPU + ; + MOVL G^OPA$AR_UCB0, R0 ; address of OPA0 UCB + CLRL UCB$L_AFFINITY(R0) ; set console affinity to the primary CPU + ; + ; Initialize the IPINT IPL to the correct value (IPL 22 for VAX MP) + ; + MOVZBL #IPL$_IPINTR, G^EXE$GL_IPINT_IPL ; store interprocessor interrupt + ; + ; Adjust the virtual console spinlock (same level as IPINT IPL) + ; + MOVL G^SMP$AR_SPNLKVEC, R0 ; address of spinlock vector + MOVZBL #SPL$C_VIRTCONS, R1 ; VIRTCONS spinlock index + MOVL (R0)[R1], R0 ; address of VIRTCONS spinlock + MOVL G^EXE$GL_IPINT_IPL, R1 ; new IPL for VIRTCONS spinlock + JSB G^SMP$ADJUST_IPL ; modify the spinlock IPL + ; + ; Adjust the INVALIDATE spinlock (one level less than IPINT IPL) + ; + MOVL G^SMP$AR_SPNLKVEC, R0 ; address of spinlock vector + MOVZBL #SPL$C_INVALIDATE, R1 ; INVALIDATE spinlock index + MOVL (R0)[R1], R0 ; address of INVALIDATE spinlock + SUBL3 #1, G^EXE$GL_IPINT_IPL, R1 ; new IPL for INVALIDATE spinlock + JSB G^SMP$ADJUST_IPL ; modify the spinlock IPL + ; + ; Set up the HWCLK IPL appropriate for current processor type (IPL 22 for VAX MP) + ; + MOVL G^SMP$AR_SPNLKVEC, R0 ; address of spinlock vector + MOVZBL #SPL$C_HWCLK, R1 ; HWCLK spinlock index + MOVL (R0)[R1], R0 ; address of HWCLK spinlock + MOVZWL #22, R1 ; proper IPL to set for that spinlock + JSB G^SMP$ADJUST_IPL ; modify the spinlock IPL + ; + ; Update OPA0 DIPL and device spinlock IPL to IPINT IPL + ; + ; ToDo: This is likely to be excessive. Rethink whether synchronization + ; with VIRTCONS requires it and if not, then use IPL 20 for OPA0. + ; + MOVL G^OPA$AR_IDB, R0 ; address of OPA0 IDB + MOVL IDB$L_SPL(R0), R0 ; ... and of device spinlock + MOVB G^EXE$GL_IPINT_IPL, - ; adjust its IPL + SPL$B_IPL(R0) ; ... + MOVL G^OPA$AR_UCB0, R0 ; address of OPA0 UCB + MOVB G^EXE$GL_IPINT_IPL, - ; adjust IPL in the UCB + UCB$B_DIPL(R0) ; ... + ; + ; Establish the interprocessor interrupt vector in the SCB + ; + MOVL G^EXE$GL_SCB, R0 ; address of SCB + MOVAL W^VSMP$INTSR!1, ^X80(R0) ; set vector, interrupts taken on IS + ; + ; The following bit in EXE$GL_SYS_SPECIFIC area turns on workaround code that + ; is disabled in production versions of OpenVMS and was (presumably) designed + ; as a workaround for microcode or circuitry bug in early pre-production + ; VAX 6000-400 (Rigel) multiprocessors. + ; + ; When this flag is set and page fault is completed on a system page, VMS will + ; execute INVALIDATE_TB on that PTE, broadcasting the change and performing local + ; TBIS. If this flag is not set or page is a process space page, invalidation will + ; not be performed, in a presumption (apparently) that TLBs should not hold PTEs + ; with valid bit off. + ; + ; VAX MP currently does not use this workaround, but its existence is mentioned here + ; just in case it might (though extremely unlikely -- and it should not) come in handy + ; if VAX MP was ever ported to a host platform with very weak memory model. + ; + ;;;BISL #1, G^EXE$GL_SYS_SPECIFIC+8 ; turn on "rigel mp workaround" + + POPR #^M ; restore saved scratch registers + RSB ; return to the caller + .DISABLE LOCAL_BLOCK + + +;++ +; +; VSMP$READ_TODR - replacement for EXE$READ_TODR +; +; This routine reads system-wide TIME-OF-DAY clock (TODR) maintained in the primary processor's TODR. +; It is callable on any CPU in the multiprocessor system and will return a valid system TODR value. +; If necessary, this routine will utilize interprocessor interrupts to coordinate a TODR value exchange +; between the primary CPU and a calling secondary CPU. +; +; Inputs: +; +; None +; +; Outputs: +; +; R0 = TODR value +; All other registers preserved +; +;-- + .ENABLE LOCAL_BLOCK +VSMP$READ_TODR:: +; +; Regular VMS SMP code elevates during READ_TODR to IPL=RESCHED to block rescheduling +; and switching current execution context to another CPU. +; +; Under VAX MP we elevate higher, to SYS_CRITICAL IPL, to additionally cause VAX MP VCPU +; thread priority elevation in order to avoid priority inversion on VCPU threads, with +; regular-priority thread (prone to pre-emption) blocking higher priority thread +; and making it spin uselessly. This could have happened if we acquired SMP$V_TODR +; interlock while staying merely at RESCHED, which does not cause VAX MP VCPU thread +; priority elevation. To induce thread priority elevation we must raise at least to +; SYS_CRITICAL. +; + SAVIPL ; save caller's IPL + CMPL (SP), #IPL$_SYS_CRITICAL ; if necessary, elevate to the level that + BGEQ 10$ ; ... 1) blocks us from rescheduling to another CPU + MTPR #IPL$_SYS_CRITICAL, S^#PR$_IPL ; ... 2) elevates VAX MP VCPU thread priority +; +; Check whether we are on the primary CPU and TODR is available locally, +; or if we are on the secondary CPU and system TODR is not available locally and we +; must request TODR value from the primary +; +10$: + MOVQ R1, -(SP) ; save scratch registers + FIND_CPU_DATA R1 ; locate current CPU's per-CPU database + ASSUME CPB$V_PRIMARY EQ 0 + BLBC CPU$L_CAPABILITY(R1), 20$ ; if LBC, this is a secondary CPU + JSB G^EXE$READP_LOCAL_TODR ; TODR is locally available to the primary + BRW 50$ ; common exit to caller +; +; We are on a secondary CPU and must request primary CPU for system TODR value. +; +; To communicate with the primary, we must first interlock access to the global cells and flags +; used to communicate with the primary reserving them against other secondaries also tryng either +; to get TODR from the primary or set system TODR on the primary, and also against concurrent +; time updates on the primary CPU itself. +; +; Note that we may be called at high IPL and therefore must avoid acquiring HWCLK spinlock. +; +20$: BUSYWAIT TIME=G^SGN$GL_SMP_SPINWAIT, - ; spin until SMP$V_TODR is cleared, indicating + DONLBL=30$, - ; ... no other TODR operation is in progress + INST1= + BBSSI #SMP$V_TODR, G^SMP$GL_FLAGS, 20$ ; try to interlock access to the primary CPU for TODR operation + ; + ; Here we have interlocked against other concurrent TODR operations and reserved access + ; to the global cells and flags required for TODR operation + ; + CLRL G^SMP$GL_PROPOSED_TODR ; indicate TODR read request for the primary + IPINT_CPU UPDTODR, G^SMP$GL_PRIMID ; interrupt primary CPU with a work request for TODR read + + BUSYWAIT TIME=G^SGN$GL_SMP_SPINWAIT, - ; wait for the primary to acknowledge TODR read request + DONLBL=40$, - + INST1= + XCCBI #SMP$V_TODR_ACK, G^SMP$GL_FLAGS ; acknowledge receiving primary's response +; +; We have received acknowledgement from the primary that our read request had been served. +; Primary had written its TODR value into SMP$GL_NEW_TODR for this CPU to read. +; + MOVL G^SMP$GL_NEW_TODR, R0 ; read TODR value reported by the primary + XCCBI #SMP$V_TODR, G^SMP$GL_FLAGS ; release interlock on TODR requests +50$: + MOVQ (SP)+, R1 ; restore scratch registers + ENBINT ; restore to caller's original IPL + RSB ; return to the caller, with TODR in R0 + .DISABLE LOCAL_BLOCK + + +;++ +; +; VSMP$WRITE_TODR - replacement for EXE$WRITE_TODR +; +; This routine writes system-wide TIME-OF-DAY clock (TODR) maintained in the primary processor's TODR. +; It is callable on any CPU in the multiprocessor system and will return a valid system TODR value. +; If necessary, this routine will utilize interprocessor interrupts to coordinate a TODR value exchange +; between the primary CPU and a calling secondary CPU. +; +; Inputs: +; +; R0 = value to be written into TODR +; +; Outputs: +; +; R0 = Updated to contain new TODR, adjusted for delays while performing write operation +; in case this routne is called on a secondary CPU. +; +; New time value written into TODR. +; All other registers preserved. +; +;-- + .ENABLE LOCAL_BLOCK +VSMP$WRITE_TODR:: +; +; Regular VMS SMP code elevates during READ_TODR to IPL=RESCHED to block rescheduling +; and switching current execution context to another CPU. +; +; Under VAX MP we elevate higher, to SYS_CRITICAL IPL, to additionally cause VAX MP VCPU +; thread priority elevation in order to avoid priority inversion on VCPU threads, with +; regular-priority thread (prone to pre-emption) blocking higher priority thread +; and making it spin uselessly. This could have happened if we acquired SMP$V_TODR +; interlock while staying merely at RESCHED, which does not cause VAX MP VCPU thread +; priority elevation. To induce thread priority elevation we must raise at least to +; SYS_CRITICAL. +; + SAVIPL ; save caller's IPL + CMPL (SP), #IPL$_SYS_CRITICAL ; if necessary, elevate to the level that + BGEQ 10$ ; ... 1) blocks us from rescheduling to another CPU + MTPR #IPL$_SYS_CRITICAL, S^#PR$_IPL ; ... 2) elevates VAX MP VCPU thread priority +; +; Check whether we are on the primary CPU and TODR is available locally, +; or if we are on the secondary CPU and system TODR is not available locally and we +; must request TODR value from the primary +; +10$: + PUSHR #^M ; save scratch registers. + FIND_CPU_DATA R1 ; locate per-CPU database for local CPU + ASSUME CPB$V_PRIMARY EQ 0 + BLBC CPU$L_CAPABILITY(R1), 30$ ; if LBC, this is a secondary CPU + JSB G^EXE$WRITEP_LOCAL_TODR ; TODR is locally available to the primary + BRW 50$ ; common exit to the caller +; +; We are on a secondary CPU and must request primary CPU to update system TODR value. +; +; To communicate with the primary, we must first interlock access to the global cells and flags +; used to communicate with the primary reserving them against other secondaries also tryng either +; to get TODR from the primary or set system TODR on the primary, and also against concurrent +; time updates on the primary CPU itself. +; +; Note that we may be called at high IPL and therefore must avoid acquiring HWCLK spinlock. +; +30$: BUSYWAIT TIME=G^SGN$GL_SMP_SPINWAIT, - ; check for concurrent TODR operation + DONLBL=35$, - + INST1= + BBSSI #SMP$V_TODR, G^SMP$GL_FLAGS, 30$ ; check again, this time in interlocked fashion + + MOVL R0, G^SMP$GL_PROPOSED_TODR ; note proposed TODR value + IPINT_CPU UPDTODR, G^SMP$GL_PRIMID ; and interrupt the primary to set it + +40$: BUSYWAIT TIME=G^SGN$GL_SMP_SPINWAIT, - + DONLBL=45$, - + INST1= + BBCCI #SMP$V_TODR_ACK, - ; verify that request is complete + G^SMP$GL_FLAGS, 40$ ; ... + MOVL G^SMP$GL_NEW_TODR, R0 ; R0 = adjusted new TODR value + XCCBI #SMP$V_TODR, G^SMP$GL_FLAGS ; release interlock on TODR requests +50$: + POPR #^M ; restore saved caller's registers + ENBINT ; restore to caller's original IPL + RSB ; return to the caller + .DISABLE LOCAL_BLOCK + + +;++ +; +; SMP$INTSR - interprocessor interrupt service routine +; +; SMP$INTSR is invoked in response to an interprocessor interrupt. +; +; The interprocessor interrupt is sent in the events listed below. +; +; Note that multiple IP requests to the same processor may occur concurrently or +; nearly-simultaneously, in which case multple IP interrupts will coalesce in just one +; IP interrupt dispatching and one ISR invocation, therefore ISR must examine all of +; IP work request bits and process multiple pending requests in a single pass. +; +; Note that IP interrupt is cleared *before* we start examining work request bits, +; so if another IP work request is sent while we examining work bits in this ISR, +; then this CPU will receive new IP interrupt and will reexamine work bits again, +; so work requests never get lost. +; +; Also note that since both sending IP interrupt and receivng interrupt perform memory +; barriers on VAX MP, we can use non-interlocked instructions to examine work request +; bits (since paring memory barriers guarantees memory updates propagation), but still +; must use interlocked instructions to clear work request bits. +; +; Interprocessor interrupt request is sent in the following events: +; +; - XDELTA. Another processor had entered XDELTA and requesting all other processor +; in active set to temporarily pause for the duration of XDELTA session. +; +; - VIRTCONS. Secondary processor requests primary to perform virtual console service, +; i.e. reading or writing console registers on behalf of this secondary. +; +; - BUGCHECK processing. Secondary processor that triggered a bugcheck +; requests primary to perform bugcheck processing. Then either the primary +; or originating secondary request all other processors in the active set +; to perform bugcheck processing. +; +; - TBIS. CPU changing TLB entry (typically for system page) requests all +; other CPUs to invalidate their TLB entry for this page. +; +; - IOPOST. Other processor entered an entry in IOPOST queue of this processor +; and requests it to perform IOPOST processing. +; +; - TBIA. Other CPU performed massive change of PTEs and requests other CPUs +; to invalidate all of their TLB entries. +; +; - UPDATE ASTLVL. Other CPU queued AST to the process executing on this processor +; and requests this processor to reevaluate it's current process' ASTLVL register. +; +; - WORK FQP. Other processed queued fork entry into the fork queue of this CPU +; (for secondaries or primary, to queues in CPU-specific area; for primary only, +; additionally into global fork queue) and requests fork processing to be +; performed on this CPU. +; +; - TODR. Secondary processor requesting prmary to read or set the value of +; its TODR register. +; +; - QLOST. Signals that VAXcluster (VMScluster) quorum had been lost. All processors +; in the active set must stay (spin) at IPL$_IOPOST or higher until the quorum is +; restored. +; +; - RESCHED. A process whose priority and affinity allows to contend for this CPU +; had become computable. Execute rescheduling of this CPU to possibly preempt +; current process and schedule new one to be executed on this CPU. +; +; - INV_ISTREAM. Executable system code or global code had been changed by other +; processor, which requests all other processors in the active set to invalidate +; their instruction prefetch streams. +; +; - PING (not VMS standard, VAX MP specific). Sent by a processor to ping +; other processors. As a side effect, ensures memory changes performed by the +; sender of this IP request are propagated to the recipients. +; +; - CPU type-specific IP requests. Currently none for VAX MP, except PING which +; is however handled in main ISR stream. +; +;-- + .ENABLE LOCAL_BLOCK + .ALIGN LONG ; longword aligned for SCB vector address +VSMP$INTSR:: ; interrupt service routine entry point + PUSHR #^M ; save scratch registers + PUSHAB VSMP_INTSR_EXIT ; push ISR exit point address so RSB returns to it + FIND_CPU_DATA R1, ISTACK=YES ; locate per-CPU database + +;************************************************************************************************ +; +; XDELTA work request handler. +; +; When CPU enters XDELTA, it requests all other processors in the active state to temporarily +; suspend their execution, so debugging session won't be affected by any processors performing +; concurrent execution while debugging is in progress. +; +; Therefore the processor that enters XDELTA requests all other CPUs in the active set to +; suspend their execution and enter benign state and stay spinning in benign state until the +; CPU executing XDELTA exits XDELTA and allows other CPUs to leave benign state and resume +; execution. +; +;************************************************************************************************ + + XDELTA_WAIT ; check if should temporarily enter benign state due to XDelta + +;************************************************************************************************ +; +; Virtual console work request handler. +; +; Vitual console requests are sent by secondary CPUs to the primary, therefore only primary +; needs to process them. On the primary, check whether virtual console mode service had been +; requested by a secondary and if so perform it for this secondary. +; +;************************************************************************************************ + + ASSUME CPB$C_PRIMARY EQ 0 + BLBC CPU$L_CAPABILITY(R1), DO_BUGCHK ; is this the primary CPU? + BSBW VSMP$VIRTCONS_SERVER ; lbs - primary - check for virtual console + ; ... service requests from the secondaries + +;************************************************************************************************ +; +; Bugcheck work request handler. +; +; If system bugcheck had been requested by another processor, perform CPUEXIT bugcheck +; on this processor. This IP requeset is initially sent by secondary processor that triggered +; original bugcheck to the primary, then either primary or originating secondary will send +; bugcheck exit request to all the secondaries in the active set. +; +;************************************************************************************************ + +DO_BUGCHK: + BBC #CPU$V_BUGCHK, - ; bugcheck code is responsible for clearing + CPU$L_WORK_REQ(R1), DO_INV_TBS ; ... work request bit + +ENTER_BUGCHECK: + SETIPL ENVIRON=UNIPROCESSOR ; disable interrupts +; +; Bugcheck processing had been requested. Restore the stack to pre-ISR state (except for +; interrupt PC/PSL) and enter bugcheck handler by signalling BUG_CHECK on the local processor. +; +; For secondary processors, bugcheck handler will save CPU context to an area in per-CPU database +; entry and enter infinite spin loop (e.g. BRB self) until the processor is restarted. +; +; On the primary processor bugcheck handler will perform other activities, including writing +; system crash dump, printing console message etc. +; + TSTL (SP)+ ; pop ISR exit point address from stack + POPR #^M ; restore saved scratch registers + BUG_CHECK CPUEXIT, FATAL ; enter BUGCHECK handler + +;************************************************************************************************ +; +; TBIS work request handler. +; +; Perform TBIS request to invalidate TLB entry (typically for system space address). +; +; When interrupting processor requests INV_TBS service, it acquires INVALIDATE spinlock, +; stores the address to be invalidated in SMP$GL_INVALID and sends IP request INV_TBS to +; target processors (typically all processors in the active set). +; +;************************************************************************************************ + +DO_INV_TBS: + BBCCI_NI #CPU$V_INV_TBS, - ; check for INV_TBS request + CPU$L_WORK_REQ(R1), - ; ... + RECHECK_BENIGN ; ... + BSBW VSMP$INVALID_SINGLE ; service INV_TBS request + +;************************************************************************************************ +; +; Re-check for pending benign state request. +; +;************************************************************************************************ + +RECHECK_BENIGN: + BBC #SMP$V_BENIGN, - ; if benign state is not requested, proceed to + G^SMP$GL_FLAGS, DO_IOPOST ; ... checking next work request +; +; CPUs had been requested to enter into benign state. +; Enter this CPU into benign state and spin in benign state loop. +; +; While spinning in the benign state loop, serve XDELTA, TBIS and BUGCHECK requests +; that may be issued by XDELTA (directly and indirectly) while in a benign state. +; Also keep checking for benign state being lifted, at which point this CPU can exit +; benign state and resume regular processing. +; + XSSBI CPU$L_PHY_CPUID(R1), - ; enter this CPU into benign state set + G^XDT$GL_BENIGN_CPUS ; ... +10$: + XDELTA_WAIT ; check if should serve XDELTA IP request + ; ... and temporarily wait in XDELTA benign wait code + ; + ; if we have spinned in benign state inside XDELTA_WAIT and resumed, + ; we may have left benign state, but in any event need to re-check + ; for INV_TBS again + ; + BBCCI_NI #CPU$V_INV_TBS, - ; recheck for INV_TBS + CPU$L_WORK_REQ(R1), 20$ ; ... + BSBW VSMP$INVALID_SINGLE ; service INV_TBS request +20$: + BBS #CPU$V_BUGCHK, - ; if bugcheck request is pending + CPU$L_WORK_REQ(R1), - ; ... go process it + ENTER_BUGCHECK ; ... and we will never come back + BBS #SMP$V_BENIGN, - ; if still under benign state, go back into + G^SMP$GL_FLAGS, 10$ ; ... benign state server loop +; +; leave benign state +; + XCCBI CPU$L_PHY_CPUID(R1), - ; remove this CPU from benign state set + G^XDT$GL_BENIGN_CPUS ; ... and proceed to checking next work request + +;************************************************************************************************ +; +; IOPOST work request handler. +; +; If IOPOST processing was requested by another CPU, signal IOPOST on this CPU. +; +;************************************************************************************************ + +DO_IOPOST: + BBCCI_NI #CPU$V_IOPOST, - ; check for IOPOST IP request + CPU$L_WORK_REQ(R1), 60$ ; ... + SOFTINT #IPL$_IOPOST ; raise local IOPOST processing request +60$: + TSTL CPU$L_WORK_REQ(R1) ; any work request bits left? + BEQL 70$ ; eql - none, so skip other checks + +;************************************************************************************************ +; +; TBIA work request handler. +; +; Perform TBIA request to invalidate all TLB entres on this CPU. +; +;************************************************************************************************ + +DO_INV_TBA: + BBCCI_NI #CPU$V_INV_TBA, - ; check for INV_TBA request + CPU$L_WORK_REQ(R1), DO_UPDASTLVL ; ... + VECTOR_MEMORY_SYNC ; perfrorm vector/scalar processors memory sync + MTPR #0, S^#PR$_TBIA ; invalidate TLB + TSTL CPU$L_WORK_REQ(R1) ; any work request bits left? + BNEQ DO_UPDASTLVL ; neq - continue with other requests +70$: + RSB ; else skip other checks + +;************************************************************************************************ +; +; Update ASTLVL work request handler. +; +; Update ASTLVL for the process currently executing on this CPU. +; +; This work request bit is set and IP request is sent when a process or system code executing +; on another CPU queues an AST to the process currently executing on this CPU (in SCH$QAST). +; Other CPU does not have direct access to this CPU's ASTLVL register, so it has to request +; this CPU to update its ASTLVL. +; +; To service the request we must force the process that is current on local CPU to examine its +; AST queue. We do this by forcing the current process ASTLVL register to 0 (kernel-mode AST) +; to cause AST interrupt being executed ASAP. In AST interrupt handler the process will +; re-examine its AST queue and recalculate new correct value for ASTLVL. +; +; We cannot just recalculate or set ASTLVL directly since IP ISR is executing at high IPL and +; thus cannot obtain properly synchronized access to necessary data structures. In particular, +; we cannot just read PHD$B_ASTLVL since concurrent multiprocessor access to PHD$L_P0LRASTL +; longword (in SCH$QAST and in SYSCREDEL) may result in a corrupt value being read by local +; CPU from PHD$B_ASTLVL. +; +;************************************************************************************************ + +DO_UPDASTLVL: + BBCCI_NI #CPU$V_UPDASTLVL, - ; check if UPDASTLVL request is pending + CPU$L_WORK_REQ(R1), DO_WORK_FQP ; ... + MOVL CPU$L_CURPCB(R1), R4 ; get current PCB address + MOVL PCB$L_PHD(R4), R2 ; get PHD address + CLRB PHD$B_ASTLVL(R2) ; clear ASTLVL in PHD + MTPR #0, S^#PR$_ASTLVL ; CPU ASTLVL = KERNEL, force ASTDEL interrupt + + TSTL CPU$L_WORK_REQ(R1) ; any work request bits left? + BNEQ DO_WORK_FQP ; neq - continue with other requests + RSB ; else skip other checks + +;************************************************************************************************ +; +; CPU work packet request handler. +; +; This IP is received when there is work packet in local CPU-specific work request queue. +; On the primary CPU also check for a packet on the system-wide primary work queue. +; Remove the packet from the queue and fork it on the local CPU. +; +;************************************************************************************************ + +DO_WORK_FQP: + BBCCI_NI #CPU$V_WORK_FQP, - ; check if FQP request is pending + CPU$L_WORK_REQ(R1), DO_UPDTODR ; ... + + CMPL CPU$L_PHY_CPUID(R1), - ; is this the primary CPU? + G^SMP$GL_PRIMID ; ... + BNEQ 110$ ; neq - secondary, check only per-CPU work queue +100$: + $REMQHI G^SMP$GQ_PRIMARY_WORKQ, R5 ; dequeue work queue entry + BVS 110$ ; bvs - queue is empty + MOVQ FKB$L_FR3(R5), R3 ; restore R3 and R4 from fork block + PUSHAB B^100$ ; fork return addr = dequeue next request + PUSHL FKB$L_FPC(R5) ; address of fork routine + JMP G^EXE$FORK ; fork it + ; + ; in ISR context returns to 100$ + ; +110$: + $REMQHI CPU$Q_WORK_FQFL(R1), R5 ; dequeue work queue entry + BVS 130$ ; bvs - queue is empty + MOVQ FKB$L_FR3(R5), R3 ; restore R3 and R4 from fork block + PUSHAB B^110$ ; fork return addr = dequeue next request + PUSHL FKB$L_FPC(R5) ; address of fork routine + JMP G^EXE$FORK ; fork it + ; + ; in ISR context returns to 110$ + ; +120$: RSB ; RSB to VSMP_INTSR_EXIT + +130$: TSTL CPU$L_WORK_REQ(R1) ; any work request bits left? + BEQL 120$ ; eql - none, skip other checks + +;************************************************************************************************ +; +; UPDTODR work request handler. +; +; Check for interprocessor request to read or update system TODR. +; +; System TODR is maintained by the primary CPU. Secondary CPUs needing to read, update or +; init system TODR do it by sending IP request to the primary. +; +; The folowing data cells and flags are used for TODR control: +; +; SMP$GL_PROPOSED_TODR = identifies requested TODR operation +; -1 => READ WATCH CHIP request (for SCORPIO/8SS CPUs only) +; 0 => read TODR request +; other values => write TODR request +; +; SMP$V_TODR in SMP$GL_FLAGS = bit that interlocks access to SMP$GL_PROPOSED_TODR +; and TODR IP service in general. Secondary CPU requesting TODR +; service performs BBSSI on this flag to reserve temporary exclusive +; access to TODR service and its data cells. When secondary CPU +; requesting TODR IP service receives a response (by observing that +; SMP$V_TODR_ACK had been set by the primary) and reads +; SMP$GL_NEW_TODR, secondary clears SMP$V_TODR_ACK and then +; clears SMP$V_TODR. +; +; SMP$V_TODR_ACK in SMP$GL_FLAGS = this bit is set by the primary after it had processed +; UPDTODR IP request. It is noted by secondary CPU that requested +; the service and gets cleared by this secondary CPU. +; +; SMP$GL_NEW_TODR = value of TODR returned by the primary. Filled by the primary right +; before it sets SMP$V_TODR_ACK +; +;************************************************************************************************ + +DO_UPDTODR: + BBCCI_NI #CPU$V_UPDTODR, - ; check if UPDTODR request is pending + CPU$L_WORK_REQ(R1), DO_QLOST ; ... + CMPL CPU$L_PHY_CPUID(R1), - ; are we on the primary CPU? + G^SMP$GL_PRIMID ; ... + BNEQ 150$ ; no, ignore request + JSB G^SMP$UPDTODR ; process update TODR request + +150$: TSTL CPU$L_WORK_REQ(R1) ; any work request bits left? + BNEQ DO_QLOST ; neq - continue with other requests + RSB ; else skip other checks + +;************************************************************************************************ +; +; Cluster quorum lost work request handler. +; +; While the quorum is lost, processors in the active set should execute at IPL$_IOPOST +; or higher IPL until VAXcluster (VMScluster) quorum is restored again. On receipt of this +; request make this processor spin at IPL=IOPOST until the quorum is restored. +; +; In VMS 7.3 QLOST IP request is no longer used, but it was used in earlier versions. +; +;************************************************************************************************ + +DO_QLOST: + BBCCI_NI #CPU$V_QLOST, - ; check if QLOST request is pending + CPU$L_WORK_REQ(R1), DO_RESCHED ; ... + MOVL G^CLU$GL_CLUB, R0 ; get CLUB address + BBS #CLUB$V_QUORUM, - ; check if quorum had been regained + CLUB$L_FLAGS(R0), 200$ ; ... +; +; Set up fork block for spinning at IPL$_IOPOST +; + MOVAB CPU$L_QLOST_FQFL(R1), R5 ; address of FKB in CPU specific area + MOVAB B^210$, FKB$L_FPC(R5) ; fork PC address + MOVB S^#DYN$C_FRK, FKB$B_TYPE(R5) ; block type = fork + INSQUE (R5), @CPU$L_PSBL(R1) ; queue fork block to IOPOST queue + SOFTINT #IPL$_IOPOST ; signal IOPOST interrupt +200$: + TSTL CPU$L_WORK_REQ(R1) ; any work request bits left? + BNEQ DO_RESCHED ; neq - proceeed with checks + RSB ; eql - none more, skip other checks +; +; The code below is executed as fork processing routine. +; It will keep resubmitting fork request until the quorum is regained. +; +210$: MOVL G^CLU$GL_CLUB, R0 ; get CLUB address + BBS #CLUB$V_QUORUM, - ; check if quorum had been regained + CLUB$L_FLAGS(R0), 220$ ; ... +; +; Quorum is still missing. Queue fork block to the back of IOPOST queue for local CPU +; and request IOPOST interrupt. +; + FIND_CPU_DATA R1 ; locate per-CPU database + INSQUE (R5), @CPU$L_PSBL(R1) ; queue fork block to IOPOST queue + SOFTINT #IPL$_IOPOST ; signal IOPOST interrupt +220$: + RSB ; return to fork dispatcher + +;************************************************************************************************ +; +; RESCHED work request handler. +; +; A process that can execute on this CPU had become computable and at the time of becoming +; computable had priority >= this CPU's current process. Request rescheduling to be performed. +; +;************************************************************************************************ + +DO_RESCHED: + BBCCI_NI #CPU$V_RESCHED, - ; check if RESCHED IP request is pending + CPU$L_WORK_REQ(R1), DO_INV_ISTREAM ; ... + SOFTINT #IPL$_RESCHED ; request local CPU to perform a re-scheduling + + TSTL CPU$L_WORK_REQ(R1) ; any work request bits left? + BEQL 500$ ; eql - none more, skip other checks + +;************************************************************************************************ +; +; INV_ISTREAM work request handler. +; +; This IP s requested by another CPU when it modifies code and instruction prefetch has to be +; flushed on other CPUs. Instruction prefetch will be flushed by REI from this IP interrupt +; processing. +; +;************************************************************************************************ + +DO_INV_ISTREAM: + BBCCI_NI #CPU$V_INV_ISTREAM, - ; check if INV_ISTREAM IP request is pending + CPU$L_WORK_REQ(R1), DO_PING ; ... + + TSTL CPU$L_WORK_REQ(R1) ; any work request bits left? + BEQL 500$ ; eql - none more, skip other checks + +;************************************************************************************************ +; +; PING CPU request handler (VAX MP specific, not a part of VMS IPI protocol). +; +; Just acknowledge we had seen the request. +; +; No longer actually used even on VAX MP, but does not harm to be left here since +; this code is never reached (because SPEC_IPINT request set is empty). +; +;************************************************************************************************ + +DO_PING: + BBCCI_NI #CPU$V_PING, - ; check if PING IP request is pending + CPU$L_WORK_REQ(R1), 270$ ; ... + + BBSSI CPU$L_PHY_CPUID(R1), - ; acknowledge the request + G^SMP$GL_ACK_MASK, 250$ ; ... +250$: + TSTL CPU$L_WORK_REQ(R1) ; any work request bits left? + BEQL 500$ ; eql - none more, skip other checks + +;************************************************************************************************ +; +; CPU-specific IP request handler. +; +; Check for and process CPU-specific requests. This is done at the end of IP ISR processing in +; assumption that CPU-specific requests will be relatively infrequent. +; +;************************************************************************************************ + +270$: BSBW VSMP$SPEC_IPINT ; call CPU-specific service routine + +500$: RSB ; finished processing IP interrupt, RSB to VSMP_INTSR_EXIT + +;************************************************************************************************ +; +; Dismiss this IP interrupt. All possible reasons for the IP interrupt have been checked +; and processed by now. +; +; If any new work requests bits were raised while this interrupt service routine was already +; in progress, setting of these bits will also be followed by signalling another IP to this CPU +; sent by the requestor of these bits and this ISR will be invoked again, so new work request +; bits will not be missed. +; +;************************************************************************************************ + +VSMP_INTSR_EXIT: + POPR #^M ; restore saved scratch registers + REI ; and exit the interrupt + .DISABLE LOCAL_BLOCK + + +;++ +; +; VSMP$SPEC_IPINT - CPU specific IP interrupt service routine +; +; Determine which CPU specific work request bit is set and dispatch +; to the appropriate routine. +; +; Inputs: +; +; R1 = address of this CPU's per-CPU database +; +; Outputs: +; +; CPU specific work request bits are cleared +; +;-- +VSMP$SPEC_IPINT:: + RSB + + .END diff --git a/src/VMS_VSMP/startcpu.mar b/src/VMS_VSMP/startcpu.mar new file mode 100644 index 0000000..977a949 --- /dev/null +++ b/src/VMS_VSMP/startcpu.mar @@ -0,0 +1,930 @@ + .TITLE STARTCPU + .IDENT /V1.00/ + +;; +;; Kernel-mode loadable code for SIMH VMS virtual SMP utility. +;; +;; SMP START CPU part +;; +;; This module defines SMP-capable version of SMP$SETUP_CPU and SMP$START_CPU routines +;; that override uniprocessor version of those routines supplied by SYSLOA650. When VSMP is +;; activated, CPULOA vectors are changed to point to new routines instead of their SYSLOA650 +;; versions. +;; +;; Tested with OpenVMS VAX version 7.3. +;; +;; Module: startcpu.mar +;; Version: 1.0 +;; Author: Sergey Oboguev (oboguev@yahoo.com) +;; Created: 10-Dec-2011 +;; Revision History: +;; none +;; + .LIBRARY "SYS$LIBRARY:LIB" + + SYS_DEFS ; common VMS definitions + $VPFLAGSDEF ; vector processor flags + + $PFNDEF ; PFN array state codes + $PTEDEF ; page table entry + $VADEF ; virtual address break-up + + $KA650DEF ; KA650-specific definitions + $PR650DEF ; ... + + XBRANCH ; Extended branch instructions + SIMHDEF ; SIMH API definitions + + CR = ^X0D + LF = ^X0A + + RESTART_PWR_UP = 3 ; powerup restart code (not really meaningful for KA650) + +;;*********************************************************************************** +;; Kernel-resident part that is loaded into nonpaged memory -- data +;;*********************************************************************************** + + .PSECT KLOAD_DATA QUAD, PIC, EXE, NOSHR, WRT +; +; Messages that may be issued when CPUs begin executing or fails to be started +; +BOOT_DONE: .ASCIC \%SMP-I-CPUBOOTED, CPU #\ +BOOT_DONE2: .ASCIC \ has joined the PRIMARY CPU in multiprocessor operation\ + +UNSTART_UNK: .ASCIC \%SMP-E-UNSTART, Unable to start CPU #\ +UNSTART_UNK2: .ASCIC \ , error reason unidentified\ + +UNSTART_CPUID: .ASCIC \%SMP-E-UNSTART, Unable to start CPU #\ +UNSTART_CPUID2: .ASCIC \ , CPU ID is not recognized by VAX MP\ + +UNSTART_NRDY: .ASCIC \%SMP-E-UNSTART, Unable to start CPU #\ +UNSTART_NRDY2: .ASCIC \ , CPU is not in VAX MP STANDBY state\ + +UNSTART_IVREG: .ASCIC \%SMP-E-UNSTART, Unable to start CPU #\ +UNSTART_IVREG2: .ASCIC \ , invalid startup HWPCB/SCBB/SBR/SLR/MAPEN values\ + +;;*********************************************************************************** +;; Kernel-resident part that is loaded into nonpaged memory -- code +;;*********************************************************************************** + + .PSECT KLOAD_CODE QUAD, PIC, EXE, SHR, NOWRT +;++ +; +; EXE$INIPROCREG - perform initialization of interval timer and CPU-dependent registers +; +; VSMP$INIPROCREG is vectored through JSB G^EXE$INIPROCREG. +; +; INIPROCREG is called by the powerfail restart code for all CPUs that were members +; of the SMP active set prior to the powerfailure. +; +; INIPROCREG is also called by START/CPU for secondary CPUs only. +; +; Inputs: +; +; none +; +; Outputs: +; +; none +;- + +VSMP$INIPROCREG:: + .ENABLE LOCAL_BLOCK + MOVQ R0, -(SP) ; save scratch registers + MOVL G^EXE$GL_CPUNODSP, R0 ; get node-specific area address +; +; enable or disable CRD interrupts, according to the CRD-enable flag +; + EXTZV S^#EXE$V_CRDENABLE, #1, - ; get CRD-enable bit + G^EXE$GB_CRD_CONTROL, R1 ; ... + INSV R1,- ; insert CRD-enable bit to proper field + #KA650$V_MEM17_CRDENB, #1, - ; ... + KA650$L_MEM17(R0) ; ... +; +; clear the reboot code in the console mailbox, for primary processor only +; + MFPR S^#PR$_CPUID, R1 ; get CPU node ID + CMPL R1, G^SMP$GL_PRIMID ; primary? + BNEQ 10$ ; neq - secondary, skip NVR change + ASSUME KA650$V_HALT_ACTION EQ 0 + ASSUME KA650$S_HALT_ACTION EQ 2 + BICW #3, KA650$B_NVR(R0) ; clear reboot code in the console mailbox +10$: +; +; enable clock interrupts and clear processor-specific error registers +; + MTPR #^X40, S^#PR$_ICCS ; enable clock interrupts + BSBB EXE_CLEAR_ERRORS ; clear processor-specific error registers + MOVQ (SP)+, R0 ; restore scratch registers + RSB ; return + .DISABLE LOCAL_BLOCK + + +;++ +; +; EXE_CLEAR_ERRORS +; +; This routine is specific to the KA650 (Mayfair) processor, and clears all the KA650 +; error flags. It replicates SYSLOA650 EXE$CLEAR_ERRORS with no functonal changes for +; multiprocessor case. The only reason we do not just call the original is because +; EXE$CLEAR_ERRORS is not exposed as system vectored routine. +; +; Inputs: +; +; None +; +; Outputs: +; +; Mayfair error flags are cleared. +; All general-purpose register values are preserved. +; +;-- +EXE_CLEAR_ERRORS:: + MOVQ R0, -(SP) ; save scratch registers + MOVL G^EXE$GL_CPUNODSP, R0 ; get node-specific area address + + MFPR #PR650$_MSER, R1 ; reinit MSER and clear errors + MTPR R1,#PR650$_MSER ; ... + MOVB KA650$L_CACR(R0), - ; reinit CACR and clear errors + KA650$L_CACR(R0) ; ... (can only write low byte of CACR) + MOVL KA650$L_MEM16(R0), - ; reinit MEMCSR16 and clear errors + KA650$L_MEM16(R0) ; ... + MOVL KA650$L_CBTCR(R0), - ; reinit CBTCR and clear errors + KA650$L_CBTCR(R0) ; ... + MOVL KA650$L_DSER(R0), - ; reinit DSER and clear QBus errors + KA650$L_DSER(R0) ; ... + + MOVQ (SP)+, R0 ; restore scratch registers + RSB + + +;++ +; +; SMP$START_CPU - CPU-specific kernel mode routine that was intended for DCL START /CPU command, +; however this routine is not actually ever invoked +; +;-- + +VSMP$START_CPU:: + RSB + + +;++ +; +; SMP$SETUP_CPU - add designated CPU to the SMP enviornment +; +; Called by START/CPU command to establish the context for the target CPU +; if it does not exist yet and bring the processor online as a member of +; the multiprocessor system. +; +; Current CPU is the primary CPU. +; +; Inputs: +; +; R9 = CPU ID of the processor being brought online +; +; Caller IPL and state should be low enough to allow this routine to acquire +; MMG spninlock and, better yet, be at IPL 0 to ASTDEL. This routine can bugcheck +; if caller's IPL is above ASTDEL and there is no free memory pages allocatable +; (if caller's IPL is 0...ASTDEL, it will wait for memory to become available). +; +; DCL START/CPU command invokes this routine at IPL 0. +; +; Outputs: +; +; R0 = SS$_WASSET - CPU already running +; SS$_BADPARAM - specified CPU is not available for booting +; +; Other success/failure status codes may also be returned +; +; Note however that R0 unfortunately cannot be used to pass meaningful status +; to be displayed by DCL START/CPU command: as long as SMP$SETUP_CPU returns +; a failure status, any failure status, DCL START/CPU (SMPUTIL.MAR) will always +; convert it to SS$_NOSUCHCPU. +; +; The only way to display a meaningful message is by printing it to console +; using SMP$WRITE_OPA0 or similar interface. +; +;-- + +VSMP$SETUP_CPU:: + .ENABLE LOCAL_BLOCK + BBC R9, G^SMP$GL_CPUCONF, 20$ ; bc - specified CPU does not exist + PUSHR #^M ; save scratch registers + LOCK LOCKNAME=MMG, - ; lock MMG data structures + LOCKIPL=IPL$_SYNCH, - ; ... + PRESERVE=NO, - ; ... + SAVIPL=R7 ; ... + ; + ; see if per-CPU database already exists or it is the first reference to the processor + ; and database has to be created + ; + MOVAL G^SMP$GL_CPU_DATA, R1 ; get address of per-CPU database + MOVL (R1)[R9], R6 ; ... + BEQL 30$ ; eql - none exists yet + CMPB #CPU$C_INIT, - ; is CPU ready for restart? + CPU$B_STATE(R6) ; ... + BNEQ 10$ ; neq - not ready, cannot restart it + XCCBI R9, G^SMP$GL_BUG_DONE ; clear BUG DONE flag for this CPU + BRW RESTART_CPU ; go restart this CPU +10$: + MOVZBL #SS$_WASSET, R0 ; return success indicator "already started" +15$: + UNLOCK LOCKNAME=MMG, - ; release the lock + PRESERVE=YES, - ; ... + CONDITION=RESTORE, - ; ... + NEWIPL=R7 ;.. + POPR #^M ; restore scratch registers + RSB ; return to the caller +20$: + MOVZBL #SS$_BADPARAM, R0 ; return status for invalid CPU ID + RSB ; ... +30$: + XCCBI R9, G^SMP$GL_BUG_DONE ; clear BUG_DONE flag for this CPU + BSBW ALLOC_PER_CPU_DATA ; allocate per-CPU data area + BLBC R0, 15$ ; lbc - failed, return error status to the caller + BSBW INIT_PER_CPU_DATA ; initialize per-CPU data area + MOVL R6, G^SMP$GL_CPU_DATA[R9] ; store VA of new per-CPU database in the system vector +RESTART_CPU: + MFPR #PR$_SCBB, CPU$L_SCBB(R6) ; set SCB address in the CPU block + ; + ; set up argument block for SIMH "START CPU" function + ; + SUBL #<34*4>, SP ; space for the block + MOVL SP, R0 ; ... + MOVL #VAXMP_API_SIGNATURE, (R0)+ ; signature code + MOVL #VAXMP_API_OP_START_CPU, (R0)+ ; function code + MOVZBL #1, (R0)+ ; guest API version + CLRL (R0)+ ; response status placeholder + MOVL R9, (R0)+ ; CPU ID + CLRQ (R0)+ ; KSP, ESP = 0 + CLRQ (R0)+ ; SSP, USP = 0 + CLRQ (R0)+ ; R0, R1 = 0 + CLRQ (R0)+ ; R2, R3 = 0 + CLRQ (R0)+ ; R4, R5 = 0 + CLRL (R0)+ ; R6 = 0 + MOVL R6, (R0)+ ; R7 = virtual address of per-CPU database + CLRQ (R0)+ ; R8, R9 = 0 + CLRQ (R0)+ ; R10, R11 = 0 + CLRQ (R0)+ ; AP, FP = 0 + MOVAB CPU_START_VIRTUAL, (R0)+ ; PC + MOVL #>, (R0)+ ; PSL: IPL=31, Interrupt Stack + CLRL (R0)+ ; P0BR = 0 + MOVL #<4@24>, (R0)+ ; ASTLVL = 4, P0LR = 0 + CLRL (R0)+ ; P1BR = 0 + CLRL (R0)+ ; PME, P1LR = 0 + MFPR S^#PR$_SCBB, (R0)+ ; SCBB to set + MOVZBL #1, (R0)+ ; MAPEN to set + MFPR S^#PR$_SBR, (R0)+ ; SBR to set + MFPR S^#PR$_SLR, (R0)+ ; SLR to set + MOVL CPU$L_INTSTK(R6), (R0)+ ; ISP to set + MTPR SP, #PR$_SIMH ; signal to SIMH + MOVL 12(SP), R0 ; save SIMH response status code + ADDL #<34*4>, SP ; remove request block off the stack + CASE R0, <50$,51$,52$,53$,54$>, TYPE=L ; dispatch on return status +50$: + MOVAB UNSTART_UNK, R1 ; print message "error unknown" + MOVAB UNSTART_UNK2, R2 ; and return error status + BRB 70$ ; to caller +51$: + MOVZBL #SS$_NORMAL, R0 ; successfully started CPU + BRW 15$ ; return success status to caller +52$: + MOVAB UNSTART_CPUID, R1 ; print error message + MOVAB UNSTART_CPUID2, R2 ; "CPU ID rejected by VAX MP" + BRB 70$ ; and return error status to caller +53$: + MOVAB UNSTART_NRDY, R1 ; print error message + MOVAB UNSTART_NRDY2, R2 ; "CPU is not in VAX MP STANDBY state" + BRB 70$ ; and return error status to caller +54$: + MOVAB UNSTART_IVREG, R1 ; print error message + MOVAB UNSTART_IVREG2, R2 ; "invalid startup HWPCB/SCBB ... values" +70$: + JSB G^SMP$WRITE_OPA0 ; print out the message + MOVZWL #SS$_ABORT, R0 ; return error status + BRW 15$ ; to the caller + .DISABLE LOCAL_BLOCK + + +;+ +; +; ALLOC_PER_CPU_DATA - allocate virtual address range and memory pages for per-CPU data +; +; Target CPU has never been previously booted into this incarnation of the system (since +; the system's last reboot) and its per-CPU data area does not exist yet. Allocate and +; initialize a per-CPU area for this CPU before starting it and requesting it +; to join the multiprocessing system. (Initialization is performed by separate call to +; INIT_PER_CPU_DATA.) +; +; Allocate PFNs and SPTEs per-CPU data structures. Layout should look like this +; (high addresses at the top, lower addresses at the bottom): +; +; +------------------------------------------------+ +; | | +; | Guard page | +; | | +; |------------------------------------------------| +; | | +; | Interrupt stack | +; | (SGN$GW_ISPPGCT pages) | +; | | +; |------------------------------------------------| +; | | +; | Guard page | +; | | +; |------------------------------------------------| +; | | +; | Boot stack page | +; | (double-mapped via P0PT) | +; | | +; |------------------------------------------------| +; | | +; | Guard page | +; | | +; |------------------------------------------------| +; | | +; | per-CPU DB | +; | (CPU$C_PAGECNT pages) | +; | | +; +------------------------------------------------+ <-- aligned +; +; The bottom of this structure must be aligned to a power of two address +; as reflected in SMP$GL_BASE_MSK, which is used by FIND_CPU_DATA. +; +; Under VAX MP we have a luxory of convenience to start CPU right off with virtual memory +; mapping enabled. This obviates the need for setting up and use of real-mode page table page, +; real-mode stack and related varables: +; +; CPU$L_REALSTACK +; CPU$L_P0PT_PAGE +; SMP$GL_P0PT_MAP +; +; They are still set but are not actually used under VAX MP. +; +; This need is obviated for the purposes of starting/stopping processor during SMP management +; operation only. However these locations are also utilized when power failure restart is handled. +; +; However neither VAX MP nor uniprocessor SIMH VAX currenly do not implement powerfail restarts +; (even for simulated uniprocessor VAX models), so the need is mute as well. +; +; Furthermore, MicroVAX 3900 boot ROM is not SMP-aware and cannot supports SMP powerfailure restarts +; even if such capability were implemented by SIMH for uniprocessor models. +; +; Furthermore, if we wished to implement powerfail restarts, there is an issue of VAX 3900 CDG +; (cache diagnostic) space overlapping extended memory range when expanded 512 MB SIMH VAX memory mode +; is used, so this issue would have to be addressed (presumably either by disabling cache reinitialization +; by OpenVMS during the restarts and also intercepting console ROM CDG access and voiding it during +; the restart -- or perhaps simply by taking PFNs in the overlapped/conflicting range off the PFN +; free list during bootstrap). +; +; Thus if powerfailure recovery support was ever implemented for SIMH VAX and VAX MP in the future, +; these issues would have to be addressed, and use of P0PT setup can be addresses as well at that time. +; +; Inputs: +; +; MMG spinlock is held +; R7 = caller caller's IPL +; R9 = CPU ID +; +; Outputs: +; +; R0 = status +; R6 = (if LBS R0) base virtual addreess of allocated per-CPU area, with proper alignment +; R11 = (if LBS R0) total page count in the area +; destroys R0-R5, R8, R10 +; +;- + .ENABLE LOCAL_BLOCK +ALLOC_PER_CPU_DATA: + ; + ; calculate R11 = actual page count for per-CPU area + ; + MOVZWL G^SGN$GW_ISPPGCT, R11 ; size of interrupt stack plus ... + ADDL #, R11 ; ... per-CPU page count + 3 guard pages + 1 boot stack page + ; + ; Calculate per-CPU area base mask implied by this page count. + ; For this, calculate R2 = next higher power of 2 value (just above R11), i.e. + ; value that will round down to base of per-CPU data area. + ; + MOVZBL S^#8, R2 ; initial power of 2 (start with 8 pages as the minimum) +10$: + CMPL R2, R11 ; will per-CPU area fit into that number of virtual pages? + BGEQU 50$ ; br if will fit + ASHL S^#1, R2, R2 ; next power of 2 + CMPL R2, #^X10000 ; number of pages way too high? + BLEQU 10$ ; br if not too high yet +30$: + BUG_CHECK INCONSTATE, FATAL ; crash if insane interrupt stack size or mask mismatch +50$: + PUSHL R2 ; save page count + ASHL #VA$V_VPG, R2, R2 ; convert to byte count + DECL R2 ; get modulo number + CMPL R2, G^SMP$GL_BASE_MSK ; compare with pre-calculated base mask + BNEQ 30$ ; neq - miscalculated granularity + POPL R2 ; get back page count +; +; At this point +; +; R11 = size (page count) of per-CPU database entry +; R2 = R11 rounded up to the next higher power of 2 +; +; Preallocate (2 * R2 - 1) SPTEs to ensure we can achieve the alignment of the structure being created, +; regardless of the alignment of the allocated virtual space. +; +; Calculate R4 = SPTE alignment mask = (4 * rounded-up page count) - 1 +; Calculate R2 = number of SPTEs to allocate = (2 * rounded-up page count) - 1 +; + ASHL S^#2, R2, R4 ; SPTE bytes for rounded-up page count + DECL R4 ; SPTE alignment bitmask + ASHL S^#1, R2, R2 ; calculate number of SPTEs to allocate + DECL R2 ; ... +; +; If VBSS is enabled, count in SPTEs required for VBSS +; + BLBC G^MMG$GB_VBSS_ENABLE, 60$ ; skip if VBSS is disabled + ADDL #CPU$K_VBS_SPTES, R2 ; count in SPTEs needed for VBSS +60$: + JSB G^LDR$ALLOC_PT ; allocate required SPTEs + BLBS R0, 70$ ; br if success + RSB ; SPTE allocation failed, return error to the caller +70$: +; +; We have allocated a range of system virtual address space of sufficient size to hold per-CPU database +; area aligned within this rage at required alignment. Perform per-CPU area alignment now, i.e. calculate +; base location for per-CPU area within the allocated virtual address space. Sections of virtual address +; space left over below or above the new per-CPU area are unused and can be released back to the system. +; +; At this point: +; +; R1 = starting address of allocated array of SPTEs +; R2 = number of requested (and allocated) SPTEs (including VBSS window) +; R4 = SPTE alignment mask +; R11 = count of pages actually needed for per-CPU area (excluding VBSS window) +; +; Calculate R4 = address of per-CPU area's SPTEs = (R1 + R4) & ~R4 +; + ADDL3 R1, R4, R3 ; compute base SVA of SPTE block to map per-CPU area + BICL3 R4, R3, R4 ; ... +; +; Calculate R5 = number of excess SPTEs allocated that should now be released +; + SUBL3 R11, R2, R5 ; number of excess allocated SPTEs to release +; +; At this point: +; +; R1 = starting address of the whole allocated array of SPTEs +; R2 = total number of allocated SPTEs (including VBSS window) +; R4 = base address of aligned SPTEs block that will map per-CPU area +; R5 = number of excess SPTEs to release +; R11 = count of pages actually needed for per-CPU area (excluding VBSS window) +; +; Release excess SPTEs, section below per-CPU area. +; + SUBL3 R1, R4, R2 ; unused SPTE bytes below per-CPU area + DIVL #4, R2 ; convert to virtual page (SPTEs) count + SUBL R2, R5 ; unused virtual page (SPTEs) count above per-CPU area + JSB G^LDR$DEALLOC_PT ; release unused bottom SPTEs back to the system +; +; Release excess SPTEs, section above per-CPU area. +; + ASHL S^#2, R11, R1 ; R1 = starting address of SPTEs to be released + ADDL R4, R1 ; ... (just above per-CPU area SPTEs) + MOVL R5, R2 ; SPTE count to release + JSB G^LDR$DEALLOC_PT ; release unused top SPTEs back to the system, + ; including excess SPTEs allocated for VBSS window +; +; We have allocated virtual address space for per-CPU database, including SPTEs for per-CPU area itself, +; interrupt stack, boot stack and stack guard pages. Now allocate physical pages for them and map virtual +; pages to physical. +; +; In case of KA650 based VAX MP we can allocate these pages anywhere in the machine's physical memory. +; +; However just in case if VAX MP ever in the future implements emulation of higher-end CPUs, physical pages +; for the first page of per-CPU database and boot stack page must be allocated from low 512 MB of physical memory +; since these two pages may be accessed by by the code executing with memory management disabled. When memory management +; is disabled, on machines with 30-bit physical address mode only low 1 GB or memory is accessible (see +; "VAX Architecture Reference Manual" 2nd ed., section 4.2.1 "Memory management disabled"). OpenVMS does not +; have a routine to allocate from low 1 GB, however it has routine (MMG$ALLOCPFN_LOW) to allocate from low +; 512 MB which we are using below. +; +; Note that none of this is applicable to KA650 which is limited to 512 MB anyway even in expanded version. +; +; Furthermore, low-memory allocation is required only in two cases, none of which currently applies: +; +; (1) Powerfail restart, which SIMH/VAX MP currently does not implement. +; or +; +; (2) Starting secondary CPUs initially with MAPEN=0 instead of starting them in virtual mode. +; Currently VSMP starts secondary processors in virtual mode right away (MAPEN=1). +; +; All other pages for per-CPU data area, including the interrupt stack pages and per-CPU DB pages except +; the first one, may be allocated from any part of memory in any event. +; +; R4 = SPTE address mapping the start of per-CPU area +; R11 = number of pages (SPTEs) in the whole per-CPU area, +; including CPU DB, boot stack, interrupt stack and three guard pages +; R7 = VSMP$SETUP_CPU caller's IPL +; R9 = CPU ID for the target processor +; + SUBL3 G^LDR$GL_SPTBASE, R4, R1 ; byte offset in SPT to starting per-CPU ares's SPTE + MULL #<512/4>, R1 ; convert to offset from the base of S0 space + BISL3 #VA$M_SYSTEM, R1, R10 ; SVA of per-CPU area base + BSBW ALLOC_PHY_PAGE_LOW ; allocate physical page for first CPU DB page (use low 512 MB) + MOVL R10, R2 ; set up virtual address for INVALIDATE_TB + MOVZBL #CPU$C_PAGECNT, R3 ; number of pages in per-CPU database + BRB 90$ ; enter loop to map the page and then map the remainder of per-CPU DB +80$: BSBW ALLOC_PHY_PAGE ; allocate physical memory page (from any part of memory) + ; + ; Map the new per-CPU database pages. + ; + ; Also invalidate TLB for the allocated page. The latter is likely to be redundant + ; since pages corresponding to "available" SPTEs must have been set "invalid" earlier + ; and thus had been flushed from the TLBs, but just to be on the safe side... + ; +90$: INVALIDATE_TB R2, - ; change PTE and invalidate TLB entries + INST1=,R0,(R4)+> + ADDL #512, R2 ; advance to next page address + SOBGTR R3, 80$ ; loop until all per-CPU DB pages are done + ; + ; Bottom (stack-top, stack overflow) guard page for boot stack + ; + INVALIDATE_TB R2, - ; change PTE and invalidate TLB entry + INST1= ; ... + ADDL #512, R2 ; advance to next page address + ; + ; Boot stack page + ; + BSBW ALLOC_PHY_PAGE_LOW ; allocate physical page (from low 512 MB) + INVALIDATE_TB R2, - ; change PTE and invalidate TLB entry + INST1=,R0,(R4)+> + ADDL #512, R2 ; advance to next page address + ; + ; Dual-map boot stack page so that it can also be used as a temporary P0 page table + ; page. It needs to be mapped at a high SVA to guarantee that entire P0 page table will + ; be within S0 space while memory management is being enabled. + ; + ; Again, this is not currenly relevant for VAX MP but may become relevant if code that + ; executes with memory management disabled is ever enabled, as had been discussed above. + ; + ; SMP$GL_P0PT_MAP points to an array of virtual pages used as P0 page table pages when + ; memory management is being enabling. This array holds a page per each CPU. + ; These virtual pages dual-map each CPU's boot stack page located in its per-CPU area. + ; + ; Note also that SYSBOOT reserves one SPTE for each of CPU$C_MAX_CPUS potential processors, + ; regardless of whether the processor is present at boot time or not. + ; + ASHL #VA$V_VPG, R9, R1 ; calculate virtual address of P0PT page + ADDL G^SMP$GL_P0PT_MAP, R1 ; ... + EXTZV #VA$V_SVPN, #VA$S_SVPN, R1, R0 ; SPTE index of P0PT page (index of the page in S0 region) + MOVL G^LDR$GL_SPTBASE, R3 ; SPT base address + INVALIDATE_TB R1, - ; change PTE and invalidate TLB entry + INST1= + ; + ; Bottom (stack-top, stack overflow) guard page for interrupt stack + ; + INVALIDATE_TB R2, - ; change PTE and invalidate TLB entry + INST1= ; ... + ADDL #512, R2 ; advance to next page address + ; + ; Interrupt stack pages + ; + MOVZWL G^SGN$GW_ISPPGCT, R3 ; page count for interrupt stack +100$: + BSBW ALLOC_PHY_PAGE ; allocate physical page + INVALIDATE_TB R2, - ; change PTE and invalidate TLB entries + INST1=,R0,(R4)+> + ADDL #512, R2 ; advance to next page address + SOBGTR R3, 100$ ; loop until interrupt stack is done + ; + ; Top (stack underrun) guard page for interrupt stack + ; + INVALIDATE_TB R2, - ; change PTE and invalidate TLB entries + INST1= ; ... + ; + ; Successful return to the caller + ; + MOVL R10, R6 ; base virtual address of allocated per-CPU data area + MOVZBL #SS$_NORMAL, R0 ; return success status + RSB ; to the caller + .DISABLE LOCAL_BLOCK + + +;+ +; +; INIT_PER_CPU_DATA - initialize new per-CPU data area after allocation +; +; Inputs: +; +; MMG spinlock is held +; R6 = base virtual addreess of allocated per-CPU area, with proper alignment +; R7 = caller caller's IPL +; R9 = CPU ID +; +; Outputs: +; +; destroys R0-R5 +; +;- + .ENABLE LOCAL_BLOCK +INIT_PER_CPU_DATA: + MOVC5 #0, (SP), #0, #CPU$C_LENGTH, (R6) ; zero out the whole per-CPU DB area + ASSUME CPU$W_SIZE+2 EQ CPU$B_TYPE + ASSUME CPU$B_TYPE+1 EQ CPU$B_SUBTYPE + MOVL #<!!CPU$C_LENGTH>, - + CPU$W_SIZE(R6) ; initialize the structure header + + MOVL R6, CPU$L_PERCPUVA(R6) ; store own virtual address + MOVB #CPU$C_INIT, CPU$B_STATE(R6) ; set initial state of this CPU + + MOVL R9, CPU$L_PHY_CPUID(R6) ; store CPU ID + ASHL R9, #1, CPU$L_CPUID_MASK(R6) ; store CPU ID as bitmask + + MOVL #RESTART_PWR_UP, CPU$L_SAVED_AP(R6) ; set restart code = POWERUP + ; (not really meaningful for KA650) + MOVL #BUG$_CPUCEASED, CPU$L_BUGCODE(R6) ; init BUGCHECK code + MOVL G^SCH$AR_NULLPCB, CPU$L_CURPCB(R6) ; current PCB = NULL process + + MOVL G^EXE$GL_TENUSEC, CPU$L_TENUSEC(R6) ; set busywait loop calibration counters + MOVL G^EXE$GL_UBDELAY, CPU$L_UBDELAY(R6) ; ... + + MOVAB CPU$L_PSFL(R6), CPU$L_PSFL(R6) ; init IOPOST queue + MOVAB CPU$L_PSFL(R6), CPU$L_PSBL(R6) ; ... + + MOVZBL #CPU$K_NUM_SWIQS, R1 ; number of fork queues to initialize + MOVAB CPU$Q_SWIQFL(R6), R0 ; address of first fork queue +10$: + MOVL R0, (R0) ; initialize the queue + MOVL R0, 4(R0) ; ... + ADDL #8, R0 ; advance to next queue + SOBGTR R1, 10$ ; loop to init next queue + + ASHL #VA$V_VPG, R9, CPU$L_P0PT_PAGE(R6) ; virtual address of P0PT page + ADDL G^SMP$GL_P0PT_MAP, CPU$L_P0PT_PAGE(R6) ; ... + + MOVZWL G^SGN$GW_ISPPGCT, R0 ; page count for interrupt stack + ASHL #VA$V_VPG, R0, R0 ; byte size of interrupt stack + ADDL3 #, R6, R1 ; address of the top of interrupt stack + ADDL3 R1, R0, CPU$L_INTSTK(R6) ; address of the base of interrupt stack + + ADDL3 #, R6, R0 ; address inside the boot stack + EXTZV #VA$V_SVPN, #VA$S_SVPN, R0, R0 ; SVPN of the boot stack page + MOVL G^LDR$GL_SPTBASE, R1 ; SPT base address + MOVL (R1)[R0], R0 ; SPTE of the boot stack page + EXTZV #PTE$V_PFN, #PTE$S_PFN, R0, R0 ; PFN of the boot stack page + ASHL #VA$V_VPG, R0, R0 ; physical address of the boot stack page + ADDL3 #512, R0, CPU$L_REALSTACK(R6) ; stack pointer of empty boot stack +; +; Initialize per CPU reserved SPTEs and SVAs for VBSS. We are "almost" certain of being able to +; allocate needed SPTEs by earlier successfull allocation and subsequent deallocation of required number +; of SPTEs in ALLOC_PER_CPU_DATA, so released SPTEs most likely should be still available. However since +; we may have released MMG lock in ALLOC_PHY_PAGE/WAIT_FREE_PAGE, when allocating memory pages for per-CPU +; data area, there is a small chance that these SPTEs may be gone and we may crash here. May want do redo +; the code later so SPTEs initially allocated for VBSS are not released in the first place. +; + BLBC G^MMG$GB_VBSS_ENABLE, 40$ ; skip if VBSS is not enabled + MOVL #1,R2 ; allocate one SPTE + JSB G^LDR$ALLOC_PT ; ... + BLBS R0,20$ ; lbs - allocation success + BUG_CHECK INCONSTATE, FATAL ; crash if unable to obtain the SPTE +20$: + MOVL R1, CPU$L_VBS_CP_SPTE(R6) ; store SVA of allocated SPTE + SUBL3 G^LDR$GL_SPTBASE, R1, R0 ; byte offset into SPT for this SPTE + ASHL #, R0, R0 ; SVA mapped by allocated SPTE + BISL3 #VA$M_SYSTEM, R0, - ; ... + CPU$L_VBS_CP_SVA(R6) ; store computed mapped SVA + ; + ; SPTEs for mapping VBS page + ; + MOVL #2, R2 ; allocate two contiguous SPTEs + JSB G^LDR$ALLOC_PT ; ... + BLBS R0, 30$ ; lbs - allocation success + BUG_CHECK INCONSTATE, FATAL ; crash if was unable to obtain the SPTEs +30$: + MOVL R1, CPU$L_VBS_MAP_SPTE(R6) ; store SVA of first allocated SPTE + SUBL3 G^LDR$GL_SPTBASE, R1, R0 ; byte offset into SPT for first SPTE + ASHL #, R0, R0 ; SVA mapped by this SPTE + BISL3 #VA$M_SYSTEM, R0, - ; ... + CPU$L_VBS_MAP_SVA(R6) ; store starting mapped SVA +40$: + RSB ; return to the caller + .DISABLE LOCAL_BLOCK + + +;+ +; +; ALLOC_PHY_PAGE - Allocate PFN (physical page) +; ALLOC_PHY_PAGE_LOW - Allocate PFN (physical page) from low 512 MB of memory +; +; This routine will wait if a page is not immediatelly available and if top caller's IPL +; (as passed in R7) is not above ASTDEL. Otherwise it will generate a bugcheck. +; +; Inputs: +; +; MMG spinlock is held +; R4 = SVA of PTE that will map the page to be allocated +; R7 = caller's original IPL before acquisition of MMG +; +; Outputs: +; +; R0 = allocated PFN +; All other registers are preserved +; +;- + + .ENABLE LOCAL_BLOCK +ALLOC_PHY_PAGE: + PUSHR #^M ; preserve working registers + CLRL R8 ; 0 = performing ALLOC_PHY_PAGE +ALLOC_PHY_PAGE_REENTER: + JSB G^MMG$ALLOCPFN ; try to allocate a page + BLSS 20$ ; lss - no free pages available now + BRB 10$ ; join common code + +ALLOC_PHY_PAGE_LOW: + PUSHR #^M ; preserve working registers + MOVZBL #1, R8 ; 1 = performing ALLOC_PHY_PAGE_LOW +ALLOC_PHY_PAGE_LOW_REENTER: + JSB G^MMG$ALLOCPFN_LOW ; try to allocate a page + BLSS 20$ ; lss - no free pages available now +10$: + CMPL R0, G^MMG$GL_MAXPFN ; is this page in PFN database? + BGTR 15$ ; gtr - no, not in PFN database + DECL G^PFN$GL_PHYPGCNT ; decrement remaning fluid page count + MOVL G^PFN$AW_REFCNT, R2 ; increment page reference count + INCW (R2)[R0] ; ... + MOVL G^PFN$AB_TYPE, R2 ; page type = SYSTEM + MOVB #PFN$C_SYSTEM, (R2)[R0] ; ... + MOVL G^PFN$AB_STATE, R2 ; page state = ACTIVE + MOVB #PFN$C_ACTIVE, (R2)[R0] ; ... + MOVL G^PFN$AL_PTE, R2 ; store PTE back pointer + MOVL R4,(R2)[R0] ; ... +15$: + POPR #^M ; restore working registers + RSB ; success, return the allocated page +20$: + CMPL R7, #IPL$_ASTDEL ; can we wait for a page to be freed by another process? + BLEQU WAIT_FREE_PAGE ; leq - yes, wait for free page + BUG_CHECK INCONSTATE, FATAL ; unable to enter wait state: caller's original IPL was too high +; +; Wait for free page to become available +; +WAIT_FREE_PAGE: + LOCK LOCKNAME=SCHED, - ; lock the scheduler database + PRESERVE=NO, - ; ... + LOCKIPL=#IPL$_SYNCH ; ... + MOVAQ G^SCH$GQ_FPGWQ, R0 ; we will be waiting on free page wait queue + MOVL G^CTL$GL_PCB, R4 ; current PCB address + JSB G^MMG$PGFLTWAIT ; enter calling process in free page wait state + MOVPSL -(SP) ; build waiting PSL + INSV #IPL$_ASTDEL, - ; process will be waiting at IPL=ASTDEL + #PSL$V_IPL, #PSL$S_IPL, (SP) ; ... + JSB G^MMG$SVPCTX ; enter wait state and invoke the scheduler +; +; At this point the calling process is placed in FPG wait state and the scheduler is invoked +; to select another process for execution. +; +; Note that MMG$SVPCTX will release MMG and SCHED spinlocks. +; +; Execution will be resumed below when a free page or pages have become available. +; Resuming at IPL = ASTDEL, and with no spinlocks held. +; + LOCK LOCKNAME=MMG, - ; reacquire MMG spinlock the caller held + LOCKIPL=#IPL$_SYNCH, - ; ... + PRESERVE=NO ; ... + TSTL R8 ; check which function was called + XBEQL ALLOC_PHY_PAGE_REENTER ; eql - branch back inside ALLOC_PHY_PAGE + BRW ALLOC_PHY_PAGE_LOW_REENTER ; else branch back inside ALLOC_PHY_PAGE_LOW + .DISABLE LOCAL_BLOCK + + +;++ +; +; CPU_START_VIRTUAL - new CPU receives control here +; +; Inputs: +; +; PSL: IPL=31, Interrupt Stack +; MAPEN = 1 +; SBR, SLR and SCBB same as on initiator processor +; R7 = virtual address of per-CPU database +; +;-- + .ENABLE LOCAL_BLOCK +CPU_START_VIRTUAL: + MTPR #1, #PR$_TBIA ; clear TLB cache + MOVL CPU$L_INTSTK(R7), SP ; load interrupt stack + MTPR R7, S^#PR$_WHAMI ; store address of CPU$ + MFPR S^#PR$_SID, CPU$L_SID(R7) ; store SID + CLRL CPU$L_WORK_REQ(R7) ; clear IPI work request mask + ; + ; Temporarily join the override set while the processor is being initialized + ; + XSSBI CPU$L_PHY_CPUID(R7), - ; list as a member of override set + G^SMP$GL_OVERRIDE ; ... + READ_SYSTIME CPU$Q_BOOT_TIME(R7) ; record boot time + JSB G^EXE$INIPROCREG ; execute processor type specific initialization + MOVB #CPU$C_BOOTED, CPU$B_STATE(R7) ; CPU now is in BOOTED state + ; + ; wait for multiprocessing "go" bit + ; (set after INIT or POWERFAIL completion by the primary) + ; under VAX MP, this is essentially just a placeholder + ; +10$: BBC #SMP$V_START_CPU, G^SMP$GL_FLAGS, 10$ + ; + ; send message to console about successfull booting + ; + MOVAB W^BOOT_DONE, R1 ; first part of the message + MOVAB W^BOOT_DONE2, R2 ; second part of the message + MOVL R7, R6 ; CPU DB address + JSB G^SMP$WRITE_OPA0 ; Print out the message + ; + ; If repeat powerfail is pending, let it happen here. + ; Since VAX MP does not implement power failure simulation, + ; under VAX MP this is essentially just a placeholder. + ; + SETIPL #, - ; allow pending powerfail interrupts + ENVIRON=UNIPROCESSOR ; to occur before we join the active set + NOP ; ... + NOP ; ... + SETIPL #IPL$_POWER, - ; disable all interrupts again + ENVIRON=UNIPROCESSOR ; ... while we are joining multiprocessing system +; +; Transition this CPU into active set. +; +; Note: XDelta cannot be used in the section of code below while CPU mutex is held. +; No breakpoints in here, no stepping through this section. +; + LOCK MUTEX=SMP$GL_CPU_MUTEX, - ; Lock CPU mutex while changing CPU state to active + SHARE=NO, - ; ... + PRESERVE=NO ; ... + MOVB #CPU$C_RUN, CPU$B_STATE(R7) ; CPU is now going into RUNNING state + XSSBI CPU$L_PHY_CPUID(R7), - ; join active set + G^SMP$GL_ACTIVE_CPUS ; ... + MOVL R7, R2 ; initialize CPU sanity timer fields + JSB G^SMP$INIT_SANITY ; ... + UNLOCK MUTEX=SMP$GL_CPU_MUTEX, - ; release CPU mutex + SHARE=NO, - ; ... + PRESERVE=NO ; ... +; +; XDelta can be used again. +; + XDELTA_WAIT ; if XDELTA is active, temporarily enter bening state + ; + ; Set CPU initial capabilities to default + ; + LOCK LOCKNAME=SCHED, - ; acquire SCHED lock + PRESERVE=NO ; ... + CLRL -(SP) ; do not need previous value to be returned + PUSHL G^SCH$GL_DEFAULT_CPU_CAP ; use default capabilities + PUSHL CPU$L_PHY_CPUID(R7) ; current CPU ID + CALLS #3, G^SCH$ADD_CPU_CAP ; set up initial capabilities + UNLOCK LOCKNAME=SCHED, - ; release SCHED lock + PRESERVE=NO ; ... + ; + ; Remove this CPU from the override set. + ; Spinlock acquire and release requests will be now subject to checks. + ; + XCCBI CPU$L_PHY_CPUID(R7), - + G^SMP$GL_OVERRIDE + ; + ; Mark this CPU as idle + ; + XSSBI CPU$L_PHY_CPUID(R7), - + G^SCH$GL_IDLE_CPUS + ; + ; Lower IPL to SYNCH + ; + PUSHL #> + PUSHAB B^100$ + REI +100$: + ; + ; Send dummy IPI request to other CPUs to ensure active set mask change had been propagated + ; to them and other processors start considering this one a member of the active set. + ; + ; Sending and receiving IPI request makes mutually coupled memory barriers to be executed + ; and mask changes propagated across the multiprocessor system. By the time IP_ACK_WAIT + ; completes we'll know the update to masks had been propagated and is visible to all processors + ; in a multiprocessor system. + ; + ; An easy way may be to do IP_ACK_WAIT PING, however spin loops in VSMP$INTSR, + ; VSMP$VIRTCONS_SERVER and, most importantly, in SMP$ACQUIRE/SMP$ACQUIREL/SMP$ACQNOIPL + ; are not set up to process PING. They are currently set up only to procss INV_TBS. + ; + ; Therefore send out dummy INV_TBS. We do this by executing INVALIDATE_TB. + ; We could use any system space address for INVALIDATE_TB. Per-CPU DB address is as + ; good as any. + ; + FIND_CPU_DATA R2 ; use per-CPU CPU database address for INVALIDATE_TB + INVALIDATE_TB R2, INST1= ; INVALIDATE_TB internally executes IP_ACK_WAIT INV_TBS + + ; + ; Transfer control to the scheduler to join the VMS multiprocessing system + ; + FIND_CPU_DATA R2 ; get per-CPU CPU database address in R2 for SCH$SCHED + ; (required for earlier versions of OpenVMS, but not for 7.3) + JMP G^SCH$SCHED ; transfer this CPU's control to the scheduler + .DISABLE LOCAL_BLOCK + + .END diff --git a/src/VMS_VSMP/sys_defs.mar b/src/VMS_VSMP/sys_defs.mar new file mode 100644 index 0000000..320e2aa --- /dev/null +++ b/src/VMS_VSMP/sys_defs.mar @@ -0,0 +1,49 @@ +;; +;; Importing various VMS definitons +;; +;; Module: sys_defs.mar +;; Version: 1.0 +;; Author: Sergey Oboguev +;; Created: 11-Dec-2011 +;; Revision History: +;; none +;; + + .MACRO SYS_DEFS + + $ACBDEF ; AST control block + $ARBDEF ; access rights block + $CHFDEF ; exception handling structures + $CPBDEF ; CPU capabilities + $CPUDEF ; per-CPU database + $DYNDEF GLOBAL ; dynamic structures codes (GLOBAL exports to C) + $FKBDEF ; fork block + $IDBDEF ; interrupt dispatch block + $IPLDEF ; interrupt priority levels + $PCBDEF ; software process control block + $PHDDEF ; process header + $PRDEF ; processor registers + $PRIDEF ; priority boost classes + $PRVDEF ; privileges + $PSLDEF ; processor status longword + $SPLDEF ; spinlock structure + $SPLCODDEF ; spinlock indexes and SMP flags + $TQEDEF ; timer queue entry + $UCBDEF GLOBAL ; unit control block + $SSDEF ; system services status codes + + ; + ; Check if should enter into benign wait state because of XDelta + ; + .MACRO XDELTA_WAIT ?L + BBS #15, G^XDT$GW_OWNER_ID, L ; if bs - no CPU in XDELTA + DSBINT ENVIRON=UNIPROCESSOR ; disable interrupts + JSB G^XDT$CPU_WAIT ; enter a benign wait state + ENBINT ; CPU had left XDELTA, restore IPL +L: .BLKB 0 ; ... and resume execution + .ENDM XDELTA_WAIT + + .MACRO SYS_DEFS + .ENDM SYS_DEFS + + .ENDM SYS_DEFS diff --git a/src/VMS_VSMP/timesync.mar b/src/VMS_VSMP/timesync.mar new file mode 100644 index 0000000..adfc538 --- /dev/null +++ b/src/VMS_VSMP/timesync.mar @@ -0,0 +1,344 @@ + .TITLE TIMESYNC + .IDENT /V1.00/ + +;; +;; Kernel-mode loadable code for SIMH VMS virtual SMP utility. +;; Timesync part -- synchronize guest time with host. +;; +;; Tested with OpenVMS VAX version 7.3. +;; +;; Module: timesync.mar +;; Version: 1.0 +;; Author: Sergey Oboguev (oboguev@yahoo.com) +;; Created: 10-Dec-2011 +;; Revision History: +;; none +;; + .LIBRARY "SYS$LIBRARY:LIB" + + SYS_DEFS ; VMS defines + XBRANCH ; Extended branch instructions + SIMHDEF ; SIMH API definitions + +;;*********************************************************************************** +;; Kernel-resident part that is loaded into nonpaged memory -- data +;;*********************************************************************************** + + .PSECT KLOAD_DATA QUAD, PIC, EXE, NOSHR, WRT +TIMESYNC_CTRL:: ; TimeSync control + .BLKB ; SIM_K_TIMESYNC_ON/SIM_K_TIMESYNC_OFF + + .ALIGN QUAD +TIMESYNC_TQE: ; TimeSync TQE + .BLKB TQE$C_LENGTH + + .ALIGN QUAD +TIMESYNC_ACB: ; TimeSync ACB queued to server process + .BLKB ACB$C_LENGTH + + .ALIGN LONG +TIMESYNC_ACB_ACTIVE: ; interlock when TimeSync ACB is queued + .LONG 0 + +;;*********************************************************************************** +;; Kernel-resident part that is loaded into nonpaged memory -- code +;;*********************************************************************************** + + .PSECT KLOAD_CODE QUAD, PIC, EXE, SHR, NOWRT + .ALIGN LONG + +;+ +; +; Get and set current TIMESYNC control. +; +; uint32 kcall_get_timesync(uint32* timesync); +; uint32 kcall_set_timesync(uint32 timesync); +; +; Returns VMS-structured status. +; +;- + ARG_TIMESYNC = 4 +; + .ENTRY KCALL_GET_TIMESYNC, ^M<> + MOVZBL TIMESYNC_CTRL, @ARG_TIMESYNC(AP) ; return the value + MOVZBL #SS$_NORMAL, R0 ; return success status + RET ; ... + + .ENTRY KCALL_SET_TIMESYNC, ^M<> + MOVB ARG_TIMESYNC(AP), TIMESYNC_CTRL ; store the value + MOVZBL #SS$_NORMAL, R0 ; return success status + RET ; ... + +;+ +; +; Set up and activate TimeSync TQE +; +; Called at IPL$_TIMER or lower. +; +;- + .ENABLE LOCAL_BLOCK +ACTIVATE_TIMESYNC_TQE:: + PUSHR #^M ; save registers + ; + ; Initialize ACB + ; + MOVAB TIMESYNC_ACB, R5 + MOVW #ACB$C_LENGTH, ACB$W_SIZE(R5) ; init header + MOVB #DYN$C_ACB, ACB$B_TYPE(R5) ; ... + MOVB #, - ; kernel mode AST, non-deletable ACB + ACB$B_RMOD(R5) ; ... + CLRL ACB$L_ASTPRM(R5) ; no AST parameter + CLRL ACB$L_KAST(R5) ; no special KAST + ; + ; Initialize TQE + ; + MOVAB TIMESYNC_TQE, R5 ; address of TQE + MOVW #TQE$C_LENGTH, TQE$W_SIZE(R5) ; init header + MOVB #DYN$C_TQE, TQE$B_TYPE(R5) ; ... + MOVB #TQE$C_SSREPT, TQE$B_RQTYPE(R5) ; ... type = repeatable system subroutine + MOVAB TIMESYNC_SUBR, TQE$L_FPC(R5) ; address of timer subroutine + CLRL TQE$L_FR3(R5) ; R3/R4 passed to subroutine + CLRL TQE$L_FR4(R5) ; ... + MOVL #<5 * 10 * 1000 * 1000>, - ; repeat interval = 5 seconds + TQE$Q_DELTA(R5) ; ... + CLRL TQE$Q_DELTA+4(R5) ; ... + ; + ; Queue TQE + ; + READ_SYSTIME R0 ; get current time + ADDL TQE$Q_DELTA(R5), R0 ; ... plus delta + ADWC TQE$Q_DELTA+4(R5), R1 ; ... for next scheduled timer due event + JSB G^EXE$INSTIMQ ; insert TQE in system queue + POPR #^M ; restore registers + RSB ; return to the caller + .DISABLE LOCAL_BLOCK + +;+ +; +; TimeSync timer subroutine. +; +; Invoked every 5 seconds at IPL$_TIMER holding no spinlocks to check +; whether system time should be synchronized from the host. +; +; Pseudocode for TIMESYNC_SUBR: +; +; lock HWCLK +; get host time +; if (exe$gl_timeadjust != 0, i.e. adjustment is active) goto skip +; if (KAST is active) goto skip +; if (guest >= host - 1sec && guest <= host + 1sec) goto skip +; nticks = 450 /* 4.5 seconds */ +; if (guest < host) +; { +; tickdelta = (host - guest) / nticks +; if (tickdelta.hi || tickdelta.lo > 0xFFFFFFFF - 100000) goto settime +; exe$gl_timeadjust = nticks +; exe$gl_ticklength = tickdelta + 100000 +; } +; else +; { +; if (guest - host > nticks * 95000) goto settime +; tickdelta = (guest - host) / nticks +; exe$gl_timeadjust = nticks +; exe$gl_ticklength = 100000 - tickdelta +; } +; skip: +; unlock HWCLK +; return +; +; settime: +; time has to be adjusted by SYS$SETIME executed in process context +; +; Maximum adjustments performed by the "short" version are for guest lags up to 53.7 hours behind +; the host and for guest running ahead of the host up to few seconds. Longer adjustments are performed +; by the "long" version executed via KAST in process context and executing SYS$SETIME. +; +; Error Logger is used as the process providing context. (Using swapper would have been ideal, but +; swapper provides limited process context and executing SYS$SETIME within swapper context crashes +; the system). +; +; Note that the "short" version does *not* change TODR and associated system values (EXE$GL_TODR, +; SMP$GL_TODR, EXE$GQ_TODCBASE and the ring buffer in SYSSETIME). They are changed only by the "long" +; version of adjustment performed via SYS$SETIME. +; +; Note that VMS executes TQEs (including this routine) on the primary processor. +; +; ToDo: Rethink what happens if ERL process used as the server is stopped or crashes. +; Is it possible for ACB to be left marked busy? +; +;- + TS_NTICKS = 450 ; "small" adjustment is performed over 4.5 seconds + TS_TOLERANCE = <10 * 1000 * 1000> ; do not adjust if within 1 sec (10^7 of 100ns units) + UINTS_PER_TICK = 100000 ; number of 100ns units per tick + UINTS_PER_TICK_LOW = 950000 ; number slightly less than 100ns units per tick +; + .ENABLE LOCAL_BLOCK +TIMESYNC_SUBR: + CMPB TIMESYNC_CTRL, - ; check it TimeSync is enabled + #SIM_K_TIMESYNC_ON ; ... + XBNEQ 310$ ; neq - disabled, just exit + PUSHR #^M ; save registers + MOVL SP, R11 ; ... + MOVPSL R9 ; ... + ; + ; Acquire HWCLK + ; + LOCK LOCKNAME=HWCLK, - ; acquire HWCLK spinlock and elevate IPL + SAVIPL=R10, - ; ... save IPL to R10 + PRESERVE=NO ; ... + ; + ; Query VMM for host time + ; + CLRQ -(SP) ; set up argument block + CLRL -(SP) ; ... + PUSHL #1 ; ... + PUSHL #VAXMP_API_OP_GETTIME_VMS ; ... + PUSHL #VAXMP_API_SIGNATURE ; ... + MTPR SP, #PR$_SIMH ; query VMM + ADDL #<3*4>, SP ; remove query block header off the stack + TSTL (SP)+ ; check VMM response status + XBEQL 400$ ; eql - failure, just exit + ; + ; See if adjustment is already underway or set-time KAST is active + ; + TSTL G^EXE$GL_TIMEADJUST ; see if adjustment is already underway + XBNEQ 400$ ; neq - underway - just exit + ; + ; noniterlocked check for KAST is ok: even if propagation of flag reset from another processor + ; is delayed, we will merely skip this cycle and perform adjustment at the next cycle (but + ; right after KAST reset guest time should be already in sync with host anyway) + ; + TSTL TIMESYNC_ACB_ACTIVE ; see if KAST may be active + XBNEQ 400$ ; neq - active - just exit + MOVQ (SP), R0 ; host time + MOVQ G^EXE$GQ_SYSTIME, R2 ; guest time + BICPSW #PSL$M_IV ; disable exceptions on integer overflow (for EDIV below) + CMPL R3, R1 ; compare quest vs. host + BLSSU 30$ ; ... + BGTRU 130$ ; ... + CMPL R2, R0 ; ... + XBEQL 400$ ; ... eql - exit + BGTRU 130$ ; ... + ; + ; guest < host + ; +30$: + SUBL R2, R0 ; calculate (host - guest) + SBWC R3, R1 ; ... + TSTL R1 ; check if within tolerance + BNEQ 31$ ; ... neq - out of tolerance + CMPL R0, #TS_TOLERANCE ; ... + XBLSSU 400$ ; ... lssu - within tolerance, exit +31$: + EDIV #TS_NTICKS, R0, R1, R0 ; (host - guest) / nticks + BVS DO_SETIME ; overflow - result is large - use set-time + CMPL R1, #<<^XFFFFFFFF> - UINTS_PER_TICK> ; is result large? + BGTRU DO_SETIME ; gtru - yes - use set-time + ADDL3 #UINTS_PER_TICK, R1, - ; set size of tick delta + G^EXE$GL_TICKLENGTH ; (systime increment on each tick) + MOVZWL #TS_NTICKS, G^EXE$GL_TIMEADJUST ; for this number of ticks + BRW 400$ ; restore registers and return + ; + ; guest > host + ; +130$: + SUBL R0, R2 ; calculate (guest - host) + SBWC R1, R3 ; ... + TSTL R3 ; check if within tolerance + BNEQ DO_SETIME ; ... neq - out of tolerance and "short" correction + CMPL R2, #TS_TOLERANCE ; ... + XBLSSU 400$ ; ... lssu - within tolerance, exit + CMPL R2, # ; beyond the threshold of "small" adjustment? + BGTRU DO_SETIME ; gtru - yes - use set-time + EDIV #TS_NTICKS, R2, R1, R0 ; calculate (guest - host) / nticks + SUBL3 R1, #UINTS_PER_TICK, - ; set size of tick delta + G^EXE$GL_TICKLENGTH ; (systime increment on each tick) + MOVZWL #TS_NTICKS, G^EXE$GL_TIMEADJUST ; for this number of ticks + BRB 400$ ; restore registers and return + ; + ; Host/guest time divergence is large, perform adjustment via SYS$SETIME + ; +DO_SETIME: + UNLOCK LOCKNAME=HWCLK, - ; release HWCLK spinlock and revert IPL + PRESERVE=NO, - ; ... + NEWIPL=R10 ; ... + TSTL G^ERL$GL_ERLPID ; check if error logger was started + BEQL 300$ ; eql - not yet, exit + BBSSI #0, TIMESYNC_ACB_ACTIVE, 300$ ; check if ACB is busy and mark busy + MOVAB TIMESYNC_ACB, R5 ; load ACB address + MOVAB TIMESYNC_AST, ACB$L_AST(R5) ; set AST routine + MOVL G^ERL$GL_ERLPID, ACB$L_PID(R5) ; set target process PID + MOVZBL #PRI$_TIMER, R2 ; set priority increment + JSB G^SCH$QAST ; fire off AST to the server process + BLBS R0, 300$ ; queued successfully? + XCCBI #0, TIMESYNC_ACB_ACTIVE ; if not - mark ACB inactive +300$: + BICW #^C, R9 ; restore original state of PSL IV flag + BISPSW R9 ; ... + MOVL R11, SP ; restore registers + POPR #^M ; ... +310$: + RSB ; return to the caller +400$: + UNLOCK LOCKNAME=HWCLK, - ; release HWCLK spinlock and revert IPL + PRESERVE=NO, - ; ... + NEWIPL=R10 ; ... + BRB 300$ ; restore registers and return + .DISABLE LOCAL_BLOCK + +;+ +; +; TimeSync AST subroutine. +; +; Invoked when divergence between guest time and host time exceeds the threshold of correctable +; within TIMESYNC_SUBR (i.e. either the lag of up to 53.7 hours or getting ahead by ~ 5 seconds +; relative to the host) and therefore full SYS$SETIME correction is required. +; +;- + TIMESYNC_PRV = ; privileges required for $SETIME + + .ENTRY TIMESYNC_AST, ^M + .ENABLE LOCAL_BLOCK + ; + ; Enable privileges required for $SETIME + ; + MOVL G^CTL$GL_PCB, R4 ; get PCB address + MOVL PCB$L_ARB(R4), R5 ; get ARB address + MOVQ ARB$Q_PRIV(R5), -(SP) ; save ARB privilege mask on the stack + MOVL PCB$L_PHD(R4), R7 ; fetch PHD address + MOVQ PHD$Q_PRIVMSK(R7), -(SP) ; save PHD privilege mask on the stack + BISL #TIMESYNC_PRV, ARB$Q_PRIV(R5) ; enable privileges + BISL #TIMESYNC_PRV, PHD$Q_PRIVMSK(R7) ; ... + ; + ; Query VMM for host time + ; + CLRQ -(SP) ; set up argument block + CLRL -(SP) ; ... + PUSHL #1 ; ... + PUSHL #VAXMP_API_OP_GETTIME_VMS ; ... + PUSHL #VAXMP_API_SIGNATURE ; ... + MTPR SP, #PR$_SIMH ; query VMM + ADDL #<3*4>, SP ; remove query block header off the stack + TSTL (SP)+ ; check VMM response status + BEQL 200$ ; eql - failure, skip SETIME + ; + ; Set time + ; + PUSHL SP ; push address of reported time + CALLS #1, G^SYS$SETIME ; set time +200$: + ADDL #<2*4>, SP ; remove time off the stack + ; + ; Restore privileges + ; +300$: + MOVQ (SP)+, PHD$Q_PRIVMSK(R7) ; restore PHD privilege mask + MOVQ (SP)+, ARB$Q_PRIV(R5) ; restore ARB privilege mask + ; + ; Mark ACB inactive and return + ; + XCCBI #0, TIMESYNC_ACB_ACTIVE ; mark ACB inactive + RET ; return to the caller + .DISABLE LOCAL_BLOCK + + .END diff --git a/src/VMS_VSMP/virtcons.mar b/src/VMS_VSMP/virtcons.mar new file mode 100644 index 0000000..ca0fe3b --- /dev/null +++ b/src/VMS_VSMP/virtcons.mar @@ -0,0 +1,561 @@ + .TITLE VIRTCONS + .IDENT /V1.00/ + +;; +;; Kernel-mode loadable code for SIMH VMS virtual SMP utility. +;; Virtual console part -- provide replacement of SMP-related CON$xxx routines supplied by SYSLOA. +;; +;; This module defines SMP-capable version of CON$xxx and other vitual console related routines +;; that override uniprocessor version of those routine supplied by SYSLOA650 (and defined in +;; [SYSLOA.SRC]OPDRIVER.MAR). When VSMP is activated, CPULOA vectors are changed to point to new +; routines instead of their SYSLOA650 version. +;; +;; Tested with OpenVMS VAX version 7.3. +;; +;; Module: virtcons.mar +;; Version: 1.0 +;; Author: Sergey Oboguev (oboguev@yahoo.com) +;; Created: 16-Dec-2011 +;; Revision History: +;; none +;; + .LIBRARY "SYS$LIBRARY:LIB" + + SYS_DEFS ; common VMS definitions + $VPFLAGSDEF ; vector processor flags + + XBRANCH ; Extended branch instructions + SIMHDEF ; SIMH API definitions + + .MACRO DO_SET VAR + XSSBI #0, VAR + .ENDM DO_SET + + .MACRO DO_CLR VAR + XCCBI #0, VAR + .ENDM DO_CLR + + .MACRO IF_SET VAR, L + ADAWI #0, VAR ; non-interlocked version would be + BNEQ L ; BLBS VAR, L + .ENDM IF_SET + + .MACRO IF_CLR VAR, L + ADAWI #0, VAR ; non-interlocked version would be + BEQL L ; BLBC VAR, L + .ENDM IF_CLR + +;++ +; +; In VAX multiprocessing system secondary processors do not have direct access to console +; and have to rely on the primary CPU to read from the console and write to the console +; using primary's console IO registers. +; +; (Albeit on VAX MP secondaries can actully write to console using their own console +; registers, but they still cannot read from the console.) +; +; To perform console IO, secondary processors have to perform interprocessor requests to +; the primary that executes console IO on behalf of the requesting secondaries by writing to +; and reading from the primary's console registers and reporting results back to requesting +; secondaries. This is called virtual console mode. +; +; Virtual console is composed of the protocol, set of virtcons virtual registers (VCREG_RXxxx +; and VCREG_TXxxx) and VIRTCONS spinlock. Access to virtual console and its data cells is guarded +; by VIRTCONS spinlock. Any secondary processor that needs to execute virtual console IO must +; acquire VIRTCONS spinlock first. VCREG registers mirror fields in VAX console registers. +; +; Virtual console protocol is as follows: +; +; 1) SECONDARY acquires VIRTCONS spinlock and issues IP interrupt to the primary CPU. +; SECONDARY normally does it by calling CON$OWNCTY. +; +; 2) When PRIMARY receives IP interrupt, it checks if VIRTCONS spinlock is busy, +; and if so enters virtual console mode. It then performs the following loop: +; +; while (VIRTCONS is busy) +; { +; /* if console reading is requested */ +; if (VCREG_RXIE != 0) +; { +; VCREG_RXDB = get character from console +; VCREG_RXDONE = 1 +; wait for the secondary to clear VCREG_RXDONE +; } +; +; /* if console writing is requested */ +; if (VCREG_TXIE != 0) +; { +; write to console character (VCREG_TXDB) +; VCREG_TXDONE = 1 +; wait for the secondary to clear VCREG_TXDONE +; } +; } +; +; Access to RXIE, TXIE, RXDONE and TXDONE is performed using interlocked instructions. +; +; In particular, testing for value change is done with ADAWI #0, VAR. We could have also +; used non-interlocked instruction such as TSTB or BLBC for testing value change. The advantage +; of using interlocked instruction (ADAWI) for testing is shorter memory update propagation time +; between processors, which could be a tad longer (perhaps few to a few dozen VAX instruction +; cycles) in case if non-interlocked instruction was used. The disadvantage is that polling on +; a variable in a loop using interlocked instruction hogs down the bus by flooding it with cache +; coherence messages. We still go for interlocked testing because (1) there is only one hogger +; on each VAR at a time, so hogging may be not too bad (2) because there is only one processor +; polling for each VAR, all references to it could in fact stay intra CPU that owns particular +; cache line and any cross-processor exchange occur only when the value is actually changed, +; (3) console IO is typcally not high volume in general, (4) it is better for console IO to +; complete as fast as possible so processor can go their own ways, (5) it is better for +; interprocessor exchange to that matter to complete as fast as possible. +; +; When getting character from physical console, primary will spin until PR$_RXCS.RDY is set, +; then read PR$_RXDB. +; +; 3) To write character to the console, SECONDARY performs: +; +; VCREG_TXDB = character +; VCREG_TXIE = 1 +; wait for VXREG_TXDONE +; VCREG_TXIE = 0 +; VCREG_TXDONE = 0 +; +; 4) To read character from the console, SECONDARY performs: +; +; VCREG_RXIE = 1 +; wait for VXREG_RXDONE +; get character from VCREG_RXDB +; VCREG_RXIE = 0 +; VCREG_RXDONE = 0 +; +; 5) At the end of transfer, SECONDARY must release VIRTCONS spinlock. +; SECONDARY normally does it by calling CON$RELEASECTY. +; +; Virtual registers thus logically mirror functions of physical console registers: +; +; VCREG_RXDB = PR$_RXDB +; VCREG_RXIE = PR$_RXCS.IE +; VCREG_RXDONE = PR$_RXCS.RDY +; +; VCREG_TXDB = PR$_TXDB +; VCREG_TXIE = PR$_TXCS.IE +; VCREG_TXDONE = PR$_TXCS.RDY +; +;-- + +;;*********************************************************************************** +;; Kernel-resident part that is loaded into nonpaged memory -- data +;;*********************************************************************************** + + .PSECT KLOAD_DATA QUAD, PIC, EXE, NOSHR, WRT + +VCON$OLD_PUTCHAR:: .BLKL ; address of old (SYSLOA650's) CON$PUTCHAR +VCON$OLD_GETCHAR:: .BLKL ; address of old (SYSLOA650's) CON$GETCHAR + +; +; When CON$OWNCTY and CON$RELEASECTY are called on the primary CPU, +; +; OWNCTY will (or may) save original TXCS/RXCS to VC_SV_TXCS/RXCS +; RELEASECTY will (or may) restore TXCS/RXCS from VC_SV_TXCS/RXCS +; +; If OWNCTY/RELEASECTY are called on the secondary CPU, primary will save its +; TXCS/RXCS to VC_SV_TXCS/RXCS when entering VSMP$VIRTCONS_SERVER and restore +; TXCS/RXCS back to saved values when exiting VSMP$VIRTCONS_SERVER. +; +; Saving and restoring TXCS/RCXS preserves physical console's TXCS.IE and RXCS.IE. +; It may also save and restore other flags if this CPU type uses serial IO for console +; instead of standard internal processor registers (but VAX MP based on MicroVAX 3900 +; i.e. KA650 uses the latter). +; +; In either case, be it secondary or primary, OWNCTY will record caller's IPL at entrance +; to virtual console mode to VC_SV_IPL and elevate to VIRTCONS IPL. RELEASECTY will restore +; IPL back to the level previously recorded to VC_SV_IPL. +; + .ALIGN LONG + +VC_SV_IPL:: .LONG 0 ; recorded IPL at entry to VIRTCONS client mode (at CON$OWNCTY) +VC_SV_TXCS:: .LONG 0 ; recorded TXCS (used on the primary only) +VC_SV_RXCS:: .LONG 0 ; recorded RXCS (used on the primary only) + +; +; Virtual registers for console data +; +VCREG_TXDB:: .LONG 0 ; write buffer +VCREG_RXDB:: .LONG 0 ; read data + + .PSECT KLOAD_DATA_PAGE PAGE, PIC, EXE, NOSHR, WRT +; +; Below virtual console registers cells are accessed with ADAWI instruction and therefore +; must be properly aligned (to a word boundary each). In addition, keep them isolated +; to a separate host CPU cache line: TXIE/RXIE in one cache line, TXDONE/RXDONE in another +; cache line from everything else. We do not know host cache line size, so use 512 bytes +; as hopefully sufficient. +; + .ALIGN PAGE + +VCREG_TXIE:: .WORD 0 ; write requested +VCREG_RXIE:: .WORD 0 ; read requested + + .ALIGN PAGE + +VCREG_TXDONE:: .WORD 0 ; write serviced +VCREG_RXDONE:: .WORD 0 ; read serviced + + .ALIGN PAGE + .BLKB 0 + +;;*********************************************************************************** +;; Kernel-resident part that is loaded into nonpaged memory -- code +;;*********************************************************************************** + + .PSECT KLOAD_CODE QUAD, PIC, EXE, SHR, NOWRT + +; +; Macro to get current CPU's physical node ID +; + .MACRO GET_CPU_NODE_ID REG + MFPR S^#PR$_CPUID, REG + .ENDM GET_CPU_NODE_ID + + +;++ +; +; VCON$PUTCHAR - put a character to the console terminal +; +; Output a character to the console terminal using non-interrupt driven output. +; +; This routine is vectored via JSB G^CON$PUTCHAR. +; +; Inputs: +; +; R0 (low byte) = character to be output to the console. +; Access to console previously established by calling CON$OWNCTY. +; +; Outputs: +; +; Character had been written to the OPA0: console terminal. +; All registers preserved. +; Access to console must be relinquished by calling CON$RELEASECTY. +; +;-- + .ENABLE LOCAL_BLOCK +VCON$PUTCHAR:: + MOVL R1,-(SP) ; save scratch register + GET_CPU_NODE_ID R1 ; get current CPU node ID + CMPL R1, G^SMP$GL_PRIMID ; are we in the primary CPU? + BEQL 20$ ; eql - primary CPU +; +; On a secondary CPU -- perform console IO by communicating with the primary CPU +; and requesting it to perform IO to its console on our behalf +; + FIND_CPU_DATA R1 ; locate CPU database + MOVL R0, VCREG_TXDB ; output to virtual register + DO_SET VCREG_TXIE ; signal write request + BUSYWAIT TIME=G^SGN$GL_SMP_SPINWAIT, - ; wait till primary signals + DONLBL=10$, - ; ... + INST1= ; ... that data had been transmitted + DO_CLR VCREG_TXIE ; clear write request + DO_CLR VCREG_TXDONE ; clear ready + MOVL (SP)+, R1 ; restore scratch register + RSB ; return to the caller +; +; On the primary CPU -- perform console IO via direct access to physical console +; +20$: JSB @VCON$OLD_PUTCHAR ; call the original SYSLOAD650 routine + MOVL (SP)+, R1 ; restore scratch register + RSB ; return to the caller + .DISABLE LOCAL_BLOCK + + +;++ +; +; VCON$GETCHAR - get a character from the console terminal +; +; Get a character from the console using polled I/O. +; +; This routine is vectored via JSB G^CON$GETCHAR. +; +; Inputs: +; +; Access to console previously established by calling CON$OWNCTY. +; +; Outputs: +; +; R0 = character read. +; Access to console must be relinquished by calling CON$RELEASECTY. +; +;-- + .ENABLE LOCAL_BLOCK +VCON$GETCHAR:: + GET_CPU_NODE_ID R0 ; get CPU node ID + CMPL R0, G^SMP$GL_PRIMID ; are we the primary? + BEQL 20$ ; eql - yes, primary +; +; On a secondary CPU -- perform console IO by communicating with the primary CPU +; and requesting it to perform IO to its console on our behalf +; + PUSHL R1 ; preserve scratch register + DO_SET VCREG_RXIE ; signal read request + FIND_CPU_DATA R0 ; locate CPU database + BUSYWAIT TIME=G^SGN$GL_SMP_SPINWAIT, - ; wait till primary signals + DONLBL=10$, - ; ... + CPUBASE=R0, - ; ... + INST1= ; ... that read data is ready + MOVL VCREG_RXDB, R0 + DO_CLR VCREG_RXIE ; clear read request + DO_CLR VCREG_RXDONE ; clear ready + MOVL (SP)+, R1 ; restore scratch register + RSB +; +; On the primary CPU -- perform console IO via direct access to physical console +; +20$: JMP @VCON$OLD_GETCHAR ; call the original SYSLOAD650 routine + ; ... will return to the caller + .DISABLE LOCAL_BLOCK + + +;++ +; +; VCON$OWNCTY - reserve access to console terminal +; +; Set up for performing non-interrupt driven I/O to the console. +; +; Reserve exclusive ownership to the console by acquiring VIRTCONS spinlock, save previous +; console terminal state, disable interrupt-driven I/O, activate virtual console mode and +; (if called on secondary CPU) send IP request to the primary CPU to enter virtual console +; server mode. +; +; This routine is vectored via JSB G^CON$OWNCTY. +; +; Proper calling sequence is: +; +; CON$OWNCTY +; series of calls to CON$PUTCHAR and/or CON$GETCHAR +; CON$RELEASECTY +; +; Inputs: +; +; Caller IPL must be <= console IPL, or caller must be a member of the override set. +; +; Outputs: +; +; VIRTCONS spinlock is acquired. +; Virtual console mode is entered. +; VC_SV_IPL = saved IPL of the caller. +; All processor registers preserved. +; +; If called on the primary CPU, additionally: +; +; VC_SV_TXCS = value to be restored to PR$_TXCS when releasing the console +; VC_SV_RXCS = value to be restored to PR$_RXCS when releasing the console +; +; If called on the secondary, primary's TXCS and RXCS will be saved and restored +; by the primary's virtual console server loop. +; +;-- + .ENABLE LOCAL_BLOCK +VCON$OWNCTY:: + MOVQ R0, -(SP) ; save scratch registers + LOCK LOCKNAME=VIRTCONS, - ; reserve access to virtual console + PRESERVE=NO, - ; ... by acquiring VIRTCONS spinlock + SAVIPL=VC_SV_IPL ; ... also save caller's IPL to VC_SV_IPL +; +; Check if we are on the primary CPU +; + GET_CPU_NODE_ID R0 ; get CPU node ID + CMPL R0, G^SMP$GL_PRIMID ; are we on the primary CPU? + BEQL 10$ ; eql - yes, primary +; +; We are on the secondary CPU +; + CLRL VCREG_TXDB ; initialize virtcons registers + CLRW VCREG_TXIE ; ... + CLRW VCREG_TXDONE ; ... + + CLRL VCREG_RXDB ; ... + CLRW VCREG_RXIE ; ... + CLRW VCREG_RXDONE ; ... + + IPINT_CPU CPU=G^SMP$GL_PRIMID ; request primary CPU to enter virtual console server mode + + MOVQ (SP)+,R0 ; restore scratch registers + RSB ; return to the caller +; +; We are on the primary CPU +; +10$: + JSB G^CON$SAVE_CTY ; save original console state: call save routine + MOVL R0, VC_SV_TXCS ; store its results + MOVL R1, VC_SV_RXCS ; ... + MOVQ (SP)+, R0 ; restore scratch registers + RSB ; return to the caller + .DISABLE LOCAL_BLOCK + + +;++ +; +; VCON$RELEASECTY - release console terminal +; +; Relinquish exclusive use of the console previously obtained via CON$OWNCTY. +; +; This routine is vectored via JSB G^CON$RELEASECTY. +; +; Inputs: +; +; VIRTCONS spinlock is held. +; VC_SV_IPL = IPL previously saved at entrance to CON$OWNCTY. +; +; Outputs: +; +; VIRTCONS spinlock is released. +; IPL = VC_SV_IPL +; All processor registers preserved. +; +; If called on the primary CPU, additionally: +; +; PR$_TXCS = VC_SV_TXCS +; PR$_RXCS = VC_SV_RXCS +; +; If called on the secondary, primary will exit console server state. +; Primary's TXCS and RXCS will be restored by the primary when it is exiting +; virtual console server mode. +; +;-- + .ENABLE LOCAL_BLOCK +VCON$RELEASECTY:: + MOVQ R0, -(SP) ; save scratch registers +; +; Check if we are on the primary CPU +; + GET_CPU_NODE_ID R0 ; get CPU node ID + CMPL R0, G^SMP$GL_PRIMID ; are we on the primary CPU? + BNEQ 10$ ; neq - skip restore cty + MOVL VC_SV_RXCS, R0 ; restore console state + MOVL VC_SV_TXCS, R1 ; ... + JSB G^CON$RESTORE_CTY ; ... +10$: + UNLOCK LOCKNAME=VIRTCONS, - ; relase virtual console spinlock + PRESERVE=NO, - ; ... + NEWIPL=VC_SV_IPL ; ... and revert to IPL of OWNCTY + MOVQ (SP)+, R0 ; restore scratch registers + RSB ; return to the caller + .DISABLE LOCAL_BLOCK + + +;++ +; +; SMP$VIRTCONS_SERVER - service the virtual console IP request +; +; This routine is invoked by interprocessor interrupt ISR. +; It is invoked only on the primary CPU. +; +; Inputs: +; +; R1 = CPU database address +; +; Outputs: +; +; R0, R2 destroyed. +; +;-- + .ENABLE LOCAL_BLOCK +VSMP$VIRTCONS_SERVER:: +; +; Check if VIRTCONS spinlock is owned. If it is owned, then virtual console service had +; been requested by secondary processor. If the spinlock is not owned, there is no request +; for virtual console, in this case just exit. +; +; Note that IPINT performs write memory barrier on the sending processor side, and receiving +; any interrupt performs read memory barrier on receiving processor side, so "weak" (plain +; read) check is sufficient. +; + MOVZBL #SPL$C_VIRTCONS, R0 ; get address of VIRTCONS spinlock + MOVL G^SMP$AR_SPNLKVEC, R2 ; ... + MOVL (R2)[R0], R0 ; ... + TSTW SPL$W_OWN_CNT(R0) ; check whether the spinlock is owned + BGEQ 10$ ; geq - yes + RSB ; no - return +; +; Set up for the virtual console server loop. +; +; Save the state of physical console (primarily for RCXS.IE and TXCS.IE). +; Console will be restored to this state when exiting virtual console server mode. +; +10$: PUSHL R1 ; save register + JSB G^CON$SAVE_CTY ; disable console interrupts + ; ... and get TXCS/RXCS registers + MOVL R0, VC_SV_TXCS ; save TXCS value + MOVL R1, VC_SV_RXCS ; save RXCS value +; +; Enter virtual console server loop. +; While inside the loop: +; +; Check for XDELTA being active on another CPU. +; If XDELTA is active, temporarily enter bening state until resumed from the debugger. +; +; Service TBIS IP interrupt. When another CPU sends this request, it expects ACK, +; so process the request to provide needed ACK to the requesting processor. +; +; Service BUGCHECK IP interrupt. +; +; Service virtual read and write request. +; +; Check for virtual console mode been exited. +; +20$: + MOVL (SP), R1 ; R1 = CPU database address +; +; Process any pending XDELTA, BUGCHECK or TBIS requests. +; + XDELTA_WAIT ; if XDelta is active, temporarily enter bening state + BBC #CPU$V_BUGCHK, - ; has bugcheck been requested? + CPU$L_WORK_REQ(R1), 30$ ; ... + BUG_CHECK CPUEXIT,FATAL ; join the bugcheck processing, never returns +30$: + BBCCI #CPU$V_INV_TBS, - ; has another processor requested TBIS? + CPU$L_WORK_REQ(R1), 40$ ; ... + JSB G^SMP$INVALID_SINGLE ; process the request +; +; Check if virtual console read had been requested. +; +40$: + IF_CLR VCREG_RXIE, 50$ ; vc read requested? + ; ... clr - no, proceed with other checks + BSBW VCON$GETCHAR ; read character from physical console + MOVL R0, VCREG_RXDB ; store read character in virtual register + DO_SET VCREG_RXDONE ; signal vc client that read data is available + BUSYWAIT TIME=G^SGN$GL_SMP_SPINWAIT, - ; wait for secondary to ack receiving data + DONLBL=45$, - ; ... + INST1= ; ... +; +; Check if virtual console write had been requested. +; +50$: + IF_CLR VCREG_TXIE, 60$ ; vc write requested? + ; ... clr - no, proceed with other checks + MOVL VCREG_TXDB, R0 ; character to write + BSBW VCON$PUTCHAR ; write to physical console + DO_SET VCREG_TXDONE ; signal to vc client: writing done + BUSYWAIT TIME=G^SGN$GL_SMP_SPINWAIT, - ; wait for secondary to ack + DONLBL=55$, - ; ... + INST1= ; ... +; +; Check if secondary exited virtual console mode. +; +60$: + MOVZBL #SPL$C_VIRTCONS, R0 ; get address of VIRTCONS spinlock + MOVL G^SMP$AR_SPNLKVEC, R2 ; ... + MOVL (R2)[R0], R0 ; ... + TSTW SPL$W_OWN_CNT(R0) ; checked whether spinlock is still owned + XBGEQ 20$ ; geq - yes, continue virtcons server loop +; +; VIRTCONS spinlock was released by the secondary. Exit virtual console mode and +; restore the state of the physical console interface to the previously saved state +; before entering virtcons server loop. +; + MOVL VC_SV_TXCS, R0 ; get saved TXCS/RXCS + MOVL VC_SV_RXCS, R1 ; ... + JSB G^CON$RESTORE_CTY ; restore physical console state + MOVL (SP)+, R1 ; resore scratch register + RSB ; return back to the caller + .DISABLE LOCAL_BLOCK + + .END diff --git a/src/VMS_VSMP/vsmp$load.com b/src/VMS_VSMP/vsmp$load.com new file mode 100644 index 0000000..4733d69 --- /dev/null +++ b/src/VMS_VSMP/vsmp$load.com @@ -0,0 +1,31 @@ +$! +$! VSMP$LOAD.COM - start VAX MP virtual multiprocessing +$! +$ PROC_FILE = F$ENVIRONMENT("PROCEDURE") +$ PROC_DIR = F$PARSE(PROC_FILE,,,"DEVICE","SYNTAX_ONLY") + - + F$PARSE(PROC_FILE,,,"DIRECTORY","SYNTAX_ONLY") +$ VSMP :== $'PROC_DIR'VSMP +$ SAY := WRITE SYS$OUTPUT +$ SS_NORMAL = 1 +$! +$ VSMP_MSG_SYS_NOT_VAXMP = %XF1B800C +$ DEFINE/USER SYS$OUTPUT _NLA0: +$ DEFINE/USER SYS$ERROR _NLA0: +$ ON WARNING THEN CONTINUE +$ VSMP QUERY +$ EX_STATUS = $STATUS +$ IF (F$TRNLNM("SYS$OUTPUT",,,,,"ACCESS_MODE") .EQS. "USER") THEN DEASSIGN/USER SYS$OUTPUT +$ IF (F$TRNLNM("SYS$ERROR",,,,,"ACCESS_MODE") .EQS. "USER") THEN DEASSIGN/USER SYS$ERROR +$ ON WARNING THEN EXIT SS_NORMAL +$ IF EX_STATUS .EQ. VSMP_MSG_SYS_NOT_VAXMP +$ THEN +$ SAY "VSMP multiprocessing cannot be activated because not running on VAX MP" +$ EXIT SS_NORMAL +$ ENDIF +$! +$ @'PROC_DIR'DEVICES.COM +$! +$ ON WARNING THEN CONTINUE +$ VSMP LOAD 'VSMP$LOAD_OPTIONS' 'P1' 'P2' 'P3' 'P4' 'P5' 'P6' 'P7' 'P8' +$ ON WARNING THEN EXIT SS_NORMAL +$ EXIT SS_NORMAL \ No newline at end of file diff --git a/src/VMS_VSMP/vsmp.c b/src/VMS_VSMP/vsmp.c new file mode 100644 index 0000000..cc4b5f5 --- /dev/null +++ b/src/VMS_VSMP/vsmp.c @@ -0,0 +1,3392 @@ +/* + * VSMP.C + * + * Activate and manage virtual SMP for OpenVMS when executing on SIMH VAX MP simulator + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SYI$_NODE_SYSTEMID +# define SYI$_NODE_SYSTEMID 4307 +#endif + +/*************************************************************************************** +* Status codes * +***************************************************************************************/ + +globalvalue unsigned int VSMP_MSG_SYNTAX; +globalvalue unsigned int VSMP_MSG_MISLINKED; +globalvalue unsigned int VSMP_MSG_MISBUILT; +globalvalue unsigned int VSMP_MSG_SYS_NOT_VAXMP; +globalvalue unsigned int VSMP_MSG_ALREADY_SMP; +globalvalue unsigned int VSMP_MSG_ALREADY_LOADED; +globalvalue unsigned int VSMP_MSG_NOT_LOADED; +globalvalue unsigned int VSMP_MSG_VERSION_MISMATCH; +globalvalue unsigned int VSMP_MSG_VMS_NOT_MULTI; +globalvalue unsigned int VSMP_MSG_CALIBR_UNSTABLE; +globalvalue unsigned int VSMP_MSG_LDR_VERIFY; +globalvalue unsigned int VSMP_MSG_LDR_SCH_CUR_TO_COM; +globalvalue unsigned int VSMP_MSG_LDR_EXE_PROC_IDLE; +globalvalue unsigned int VSMP_MSG_VM_REFUSED; +globalvalue unsigned int VSMP_MSG_IDLE_NEVER; +globalvalue unsigned int VSMP_MSG_UNMOD_DRV; +globalvalue unsigned int VSMP_MSG_INVOPTVAL; +globalvalue unsigned int VSMP_MSG_IVPATCHID; +globalvalue unsigned int VSMP_MSG_XDTINVALID; +globalvalue unsigned int VSMP_MSG_MSGRTNSLDR; +globalvalue unsigned int VSMP_MSG_XQTIMXMT_P; +globalvalue unsigned int VSMP_MSG_UNSUPPXQ; +globalvalue unsigned int VSMP_MSG_XQTIMXMT_C; +globalvalue unsigned int VSMP_MSG_XQTX1_P; +globalvalue unsigned int VSMP_MSG_XQTX2_P; +globalvalue unsigned int VSMP_MSG_XQTX3_P; +globalvalue unsigned int VSMP_MSG_XQTX4_P; +globalvalue unsigned int VSMP_MSG_XQTX5_P; +globalvalue unsigned int VSMP_MSG_XQTX6_P; +globalvalue unsigned int VSMP_MSG_XQTX7_P; +globalvalue unsigned int VSMP_MSG_XQTX8_P; +globalvalue unsigned int VSMP_MSG_XQTX9_P; +globalvalue unsigned int VSMP_MSG_XQTX10_P; +globalvalue unsigned int VSMP_MSG_XQRX1_P; +globalvalue unsigned int VSMP_MSG_XQRX2_P; +globalvalue unsigned int VSMP_MSG_XQRX3_P; +globalvalue unsigned int VSMP_MSG_XQRX4_P; +globalvalue unsigned int VSMP_MSG_PU1_P; +globalvalue unsigned int VSMP_MSG_PU2_P; +globalvalue unsigned int VSMP_MSG_PU3_P; +globalvalue unsigned int VSMP_MSG_PU4_P; +globalvalue unsigned int VSMP_MSG_PU5_P; +globalvalue unsigned int VSMP_MSG_PU6_P; +globalvalue unsigned int VSMP_MSG_PU7_P; +globalvalue unsigned int VSMP_MSG_NONATIVE; +globalvalue unsigned int VSMP_MSG_SYNCWIDLEOFF; +globalvalue unsigned int VSMP_MSG_IVPARSET; +globalvalue unsigned int VSMP_MSG_IVLOCKRTRY; +globalvalue unsigned int VSMP_MSG_VLOW_SW_SYS; +globalvalue unsigned int VSMP_MSG_VLOW_SW_ILK; +globalvalue unsigned int VSMP_MSG_LOW_SW_SYS; +globalvalue unsigned int VSMP_MSG_LOW_SW_ILK; +globalvalue unsigned int VSMP_MSG_ADV_SW_SYS; +globalvalue unsigned int VSMP_MSG_ADV_SW_ILK; +globalvalue unsigned int VSMP_MSG_SYS_LESS_ILK; +globalvalue unsigned int VSMP_MSG_ADV_ISW_SYS; +globalvalue unsigned int VSMP_MSG_UNSTART; +globalvalue unsigned int VSMP_MSG_SPWHIGH; +globalvalue unsigned int VSMP_MSG_ADV_SPW_HIGH; +globalvalue unsigned int VSMP_MSG_LSPWHIGH; +globalvalue unsigned int VSMP_MSG_ADV_LSPW_HIGH; + +globalvalue unsigned int VSMP_MSG_CALIBRATING; +globalvalue unsigned int VSMP_MSG_CALIBRATED; +globalvalue unsigned int VSMP_MSG_CALIBRETRY; +globalvalue unsigned int VSMP_MSG_LOADED; + +/*************************************************************************************** +* Helper macros, type definitions etc. * +***************************************************************************************/ + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#define EXTERN globalref +#define INLINE __inline + +#define CHECK(cond) do { if (! (cond)) goto cleanup; } while (0) + +#define check(cond) do { if (! (cond)) { status = SS$_ABORT; goto cleanup; } } while (0) +#define check_vms(st) do { if (! $VMS_STATUS_SUCCESS(st)) goto cleanup; } while (0) +#define check_vms_status(st) do { status = (st); if (! $VMS_STATUS_SUCCESS(status)) goto cleanup; } while (0) +#define fail(st) do { status = (st); goto cleanup; } while (0) + +#define streqi(s1, s2) (0 == strcasecmp((s1), (s2))) + +#define memzero(m) memset(&m, 0, sizeof(m)) +#define memzero_ar(m) memset(m, 0, sizeof(m)) + +#define offset_ptr(type, base, offset) ((type*) ((byte_t*)(base) + (offset))) +#define offset_ref(type, base, offset) (* offset_ptr(type, (base), (offset))) + +#define countof(a) (sizeof(a) / sizeof((a)[0])) + +typedef unsigned int uint32; +typedef unsigned short uint16; +typedef unsigned char byte_t; +typedef unsigned int bool_t; + +typedef struct __iosb +{ + uint16 iosb$w_status; + uint16 iosb$w_count; + uint32 iosb$l_extended; +} +IOSB; + +typedef struct __lksb +{ + uint16 lksb$w_status; + uint16 lksb$w_reserved; + uint32 lksb$l_lock_id; + byte_t lksb$b_value_blk[64]; +} +LKSB; + +typedef struct __ILE3 +{ + uint16 ile3$w_length; + uint16 ile3$w_code; + void* ile3$ps_bufaddr; + uint16* ile3$ps_retlen_addr ; +} +ILE3; + +typedef struct __VA_RANGE +{ + void* start; + void* end; +} +VA_RANGE; + +typedef struct dsc$descriptor DESC; + +static int kstrlen(const char* p); + +static void mkdesc(DESC* dsc, const char* s) +{ + dsc->dsc$w_length = (uint16) kstrlen((char*) s); + dsc->dsc$a_pointer = (char*) s; + dsc->dsc$b_dtype = DSC$K_DTYPE_T; + dsc->dsc$b_class = DSC$K_CLASS_S; +} + +static void ile3_make(ILE3* ile, uint16 code, uint16 length, void* bufaddr, uint16* retlen) +{ + ile->ile3$w_code = code; + ile->ile3$w_length = length; + ile->ile3$ps_bufaddr = bufaddr; + ile->ile3$ps_retlen_addr = retlen; +} + +static void ile3_term(ILE3* ile) +{ + ile->ile3$w_code = 0; + ile->ile3$w_length = 0; + ile->ile3$ps_bufaddr = NULL; + ile->ile3$ps_retlen_addr = NULL; +} + +INLINE static int imin(int a, int b) +{ + return (a < b) ? a : b; +} + +INLINE static int imax(int a, int b) +{ + return (a > b) ? a : b; +} + +INLINE static int uimin(uint32 a, uint32 b) +{ + return (a < b) ? a : b; +} + +INLINE static int uimax(uint32 a, uint32 b) +{ + return (a > b) ? a : b; +} + +/* call routine at relocated address inside the kernel-resident copy of the loadable code */ +#define kcall_reloc(rtype, routine) ((rtype) ((char*) kaddress + ((char*) (rtype) (routine) - (char*) &kload_start))) +#define RCALL(rtype, routine) (*kcall_reloc(rtype, routine)) + +/* access data at relocated address inside the kernel-resident copy of the loadable code */ +#define kdata_reloc(dtype, dname) (* (dtype*) ((char*) kaddress + ((char*) (dtype*) (&(dname)) - (char*) &kload_start))) +#define kdata_reloc_ptr(ptype, ptr) ((ptype) ((char*) kaddress + ((char*) (ptr) - (char*) &kload_start))) + +typedef uint32 (*kcall_onload_t)(uint32 cpu_mask, uint32 idle, uint32 timesync, uint32 nopatch); +typedef uint32 (*kcall_get_idle_t)(uint32* idle); +typedef uint32 (*kcall_set_idle_t)(uint32 idle); +typedef uint32 (*kcall_get_timesync_t)(uint32* timesync); +typedef uint32 (*kcall_set_timesync_t)(uint32 timesync); +typedef void (*patch_xqdrv_instr_t)(void* addr_mov_xmt_tmo, uint32 xqtimeout); +typedef bool_t (*lookup_code_checker_t)(const byte_t* xaddr, void* arg); +typedef bool_t (*set_patchdesc_lookup_found_t)(uint32 patch_id, const void* addr); +typedef void (*setup_patch_t)(const void* addr); + +#define SIM_K_IDLE_OFF 0 +#define SIM_K_IDLE_ON 1 +#define SIM_K_IDLE_NEVER 2 + +#define SIM_K_TIMESYNC_OFF 0 +#define SIM_K_TIMESYNC_ON 1 + +#define MAXARGSIZE 64 + +#define XQ_MIN_TIMEOUT 5 +#define XQ_MAX_TIMEOUT 255 +#define XQ_DEF_TIMEOUT 255 + +/*************************************************************************************** +* SIMH VAX MP API definitions * +***************************************************************************************/ + +#define VAXMP_API_SIGNATURE 0x484D4953 + +#define VAXMP_API_OP_QUERY 1 +#define VAXMP_API_OP_IDLE 2 + +#define VAXMP_VM_ID 0x504D5856 /* 'VXMP' */ + +#define VAXMP_SMP_OPTION_PORTABLE_INTERLOCK (1 << 0) +#define VAXMP_SMP_OPTION_NATIVE_INTERLOCK (1 << 1) + +#define SYNCW_SYS (1 << 0) +#define SYNCW_ILK (1 << 1) + +/*************************************************************************************** +* Module-global data * +***************************************************************************************/ + +/* SIMH QUERY data */ +static volatile bool_t simh_present = FALSE; +static uint32 simh_api_version; +static uint32 simh_ncpus; +static uint32 simh_cpu_mask; +static uint32 simh_vm_id; +static uint32 simh_vm_version; +static uint32 simh_smp_revision; +static uint32 simh_smp_options; +static uint32 simh_smp_multiplier; +static uint32 simh_smp_divider; +static uint32 simh_host_turbo_factor; +static uint32 simh_syncw; + +static byte_t* kaddress = 0; /* address of resident image */ + +static uint32 exe_tenusec; /* recalculated loop calibration counters */ +static uint32 exe_ubdelay; /* ... */ + +static uint32 kerror_cause_usr = 0; /* cause of error (user-mode copy of kerror_cause, + copied from kerror_cause in resident image in S0) */ + +static uint32 kargs_none[1] = { 0 }; + +static uint32 smp_idle = SIM_K_IDLE_ON; +static uint32 smp_timesync = SIM_K_TIMESYNC_ON; + +static int pfork_pool_pages = 1; + +static uint32 nopatch = 0; /* NOPATCH flags */ +static uint32 xqtimeout = XQ_DEF_TIMEOUT; + +static void* xqdrv_dpt = NULL; /* XQDRIVER DPT location */ +static uint32 xqdrv_size = 0; /* XQDRIVER size */ +static byte_t xqdrv_orig_xmt_tmo = 0; +static void* xqdrv_addr_xmt_tmo = NULL; +static uint32 xqdrv_lsb_xmt_tmo = 0; + +static void* pudrv_dpt = NULL; /* PUDRIVER DPT location */ +static uint32 pudrv_size = 0; /* PUDRIVER size */ + +static uint32 syncw_sys_pct = 66; /* SYS synchronization window size (percent of max) */ +static uint32 syncw_ilk_pct = 66; /* ILK synchronization window size (percent of max) */ + +static bool_t use_native_interlock = FALSE; + +/*************************************************************************************** +* External references * +***************************************************************************************/ + +EXTERN byte_t kload_start; +EXTERN byte_t kload_end; +EXTERN byte_t kload_blksize; +EXTERN byte_t kload_wslock_start; +EXTERN byte_t kload_wslock_end; +EXTERN uint32 kerror_cause; +EXTERN uint32 xdelta_range[2]; +EXTERN uint32 msg_rtns_range[2]; +EXTERN const byte_t* lookup_code_addr; + +EXTERN uint32 syncw_on; /* SYNCW_SYS, SYNCW_ILK */ +EXTERN uint32 syncw_winsize_sys; /* SYS synchronization window size (cycles) */ +EXTERN uint32 syncw_winsize_ilk; /* ILK synchronization window size (cycles) */ + +EXTERN uint32 smp_must_options; +EXTERN uint32 smp_want_options; + +/*************************************************************************************** +* External references to system data * +***************************************************************************************/ + +EXTERN uint32 exe$gl_lockrtry; +EXTERN uint32 sgn$gl_smp_lngspinwait; +EXTERN uint32 sgn$gl_smp_spinwait; +EXTERN uint32 exe$gl_time_control; + +/*************************************************************************************** +* Dynamic patch IDs * +***************************************************************************************/ + +globalvalue unsigned int PATCH_ID_XDELTA; +globalvalue unsigned int PATCH_ID_CHSEP; +globalvalue unsigned int PATCH_ID_RESCHED; +globalvalue unsigned int PATCH_ID_NUMTIM; +globalvalue unsigned int PATCH_ID_MFYCAP; +globalvalue unsigned int PATCH_ID_LOCKRTRY; +globalvalue unsigned int PATCH_ID_XQTIMXMT; +globalvalue unsigned int PATCH_ID_UCBTMO; +globalvalue unsigned int PATCH_ID_CRBTMO; +globalvalue unsigned int PATCH_ID_XQTX1; +globalvalue unsigned int PATCH_ID_XQTX2; +globalvalue unsigned int PATCH_ID_XQTX3; +globalvalue unsigned int PATCH_ID_XQTX4; +globalvalue unsigned int PATCH_ID_XQTX5; +globalvalue unsigned int PATCH_ID_XQTX6; +globalvalue unsigned int PATCH_ID_XQTX7; +globalvalue unsigned int PATCH_ID_XQTX8; +globalvalue unsigned int PATCH_ID_XQTX9; +globalvalue unsigned int PATCH_ID_XQTX10; +globalvalue unsigned int PATCH_ID_XQRX1; +globalvalue unsigned int PATCH_ID_XQRX2; +globalvalue unsigned int PATCH_ID_XQRX3; +globalvalue unsigned int PATCH_ID_XQRX4; +globalvalue unsigned int PATCH_ID_PU1; +globalvalue unsigned int PATCH_ID_PU2; +globalvalue unsigned int PATCH_ID_PU3; +globalvalue unsigned int PATCH_ID_PU4; +globalvalue unsigned int PATCH_ID_PU5; +globalvalue unsigned int PATCH_ID_PU6; +globalvalue unsigned int PATCH_ID_PU7; + +static uint32 xq_all_patches_mask; +static uint32 pu_all_patches_mask; + +/*************************************************************************************** +* Local function prototypes * +***************************************************************************************/ + +static void verify_linking_integrity(); +static void usage(); +static uint32 cmd_query(int argc, char** argv); +static uint32 cmd_load(int argc, char** argv); +static uint32 cmd_show(int argc, char** argv); +static uint32 cmd_set_affinity(int argc, char** argv); +static uint32 cmd_show_affinity(int argc, char** argv); +static uint32 cmd_set(int argc, char** argv); +static void query_simh(); +void mtpr_simh(uint32* args); +static uint32 query_simh_exc_handler(struct chf$signal_array* sig, struct chf$mech_array* mech); +static bool_t is_keyword(const char* s, const char* kwd, int len); +static bool_t split_key_value(const char* arg, char* key, size_t keysize, char* value, size_t valuesize); +static bool_t is_qualifier(const char* s, const char* kwd, int len, const char** pqvalue); +static uint32 parse_nopatch(const char* value); +static void check_smp_enabled(bool_t fatal); +static void perform_calibration(); +static void verify_calibration(); +static void apply_calibration(); +static bool_t is_calibration_stable(const uint32* m, int nsamples, int* pmax); +static uint32 k_load_resident_image(); +static uint32 locate_xqdriver(); +static uint32 prepare_xqdrv_patches(); +static uint32 prepare_xqdrv_timxmt_patch(); +static uint32 apply_xqdrv_timxmt_patch(int stage); +static uint32 locate_pudriver(); +static uint32 prepare_pudrv_patches(); +static void inv_opt_val(const char* optname); +static void validate_syncw_parameters(); +static void print_msg(uint32 status); +static void print_msg_1(uint32 status, uint32 arg); +static void print_2msgs(uint32 st1, uint32 st2); +static void print_3msgs_val(uint32 st1, uint32 st2, uint32 st3, uint32 val); +static void check_resident_image_loaded(); +static void check_resident_image_not_loaded(); +static uint32 k_check_resident_image_loaded(); +static void lock_nonpaged(); +static void acquire_node_lock(); +static void release_node_lock(); +static void exit_handler(uint32 status); +static char* fmt_x8(char* buf, uint32 value); +static char* fmt_x2(char* buf, uint32 value); +static char* fmt_s(char* buf, const char* value); +static void kprint_prefix_x8_crlf(const char* prefix, uint32 value); +static bool_t parse_address(const char* sa, byte_t** paddr); +static uint32 k_gather_device_affinity(uint32* buffer, uint32 bufsize, uint32* overflow); +static uint32 k_set_device_affinity(const char* pattern, uint32 affinity); +static void k_strupr(char *s); +static void k_itoa(char* s, int v); +static bool_t k_match_pattern(const char* pattern, const char* name, int namelen, uint32 unit, bool_t endstar); +static bool_t k_streq(const char* s1, const char* s2); +static uint32 parse_udec(const char* sv, const char* optname); +static uint32 lookup_code_match(const byte_t* xaddr, const byte_t* pstart, const byte_t* pend); +static uint32 accvio_handler(struct chf$signal_array* sig, struct chf$mech_array* mech); +static uint32 safe_read(const byte_t* addr, byte_t* pvalue, uint32 count); +uint32 lookup_code_ex(const byte_t* xaddr, int max_before, int max_after, const byte_t* pstart, const byte_t* pend, + const byte_t* mask, const byte_t* mask_e, lookup_code_checker_t checker, void* checker_arg); +static uint32 lookup_code_match_ex(const byte_t* xaddr, const byte_t* pstart, const byte_t* pend, + const byte_t* mask, lookup_code_checker_t checker, void* checker_arg); +static void* locate_driver(const char* drivername); +uint32 lookup_in_xqdrv(uint32 errstatus, int initial, const byte_t* pstart, const byte_t* pend); +uint32 lookup_in_xqdrv_ex(uint32 errstatus, int initial, const byte_t* pstart, const byte_t* pend, + const byte_t* mask, const byte_t* mask_e, lookup_code_checker_t checker, void* checker_arg); +uint32 lookup_in_pudrv(uint32 errstatus, int initial, const byte_t* pstart, const byte_t* pend); +uint32 lookup_in_pudrv_ex(uint32 errstatus, int initial, const byte_t* pstart, const byte_t* pend, + const byte_t* mask, const byte_t* mask_e, lookup_code_checker_t checker, void* checker_arg); +uint32 lookup_in_drv(void* dpt, uint32 drv_size, uint32 errstatus, int initial, const byte_t* pstart, const byte_t* pend); +uint32 lookup_in_drv_ex(void* dpt, uint32 drv_size, uint32 errstatus, int initial, const byte_t* pstart, const byte_t* pend, + const byte_t* mask, const byte_t* mask_e, lookup_code_checker_t checker, void* checker_arg); + +/*************************************************************************************** +* Exported functions prototypes * +***************************************************************************************/ + +uint32 lookup_code(const byte_t* xaddr, int max_before, int max_after, const byte_t* pstart, const byte_t* pend); +bool_t k_streqi_cnt(const char* s1, const char* s2, int len); + +/*************************************************************************************** +* Functions in KLOAD and DYNPATCH * +***************************************************************************************/ + +// uint32 kmalloc_kb64(uint32 size, void** p); +// void kfree_kb64(void* p); +uint32 kmalloc_anysize(uint32 reqsize, void** paddr, uint32* pblksize); +void kfree_anysize(void* p, uint32 blksize); +uint32 kload_verify_compatible(void* addr); +uint32 kload_calibrate_sysloops(uint32* tenusec, uint32* ubdelay); +uint32 kload_apply_calibration(uint32 tenusec, uint32 ubdelay); +uint32 k_check_smp_enabled(uint32* enabled, uint32* unmod_driver); +void kload_set_process_deletable(bool_t deletable); +void kload_flush_instruction_stream(); +uint32 kcall_onload(uint32 idle, uint32 timesync); +uint32 kcall_get_idle(uint32* idle); +uint32 kcall_get_timesync(uint32* timesync); +uint32 kcall_set_idle(uint32 idle); +uint32 kcall_set_timesync(uint32 timesync); +void kprint(const char* s); +void kprint_crlf(const char* s); +void kmemcpy(void* dst, const void* src, uint32 size); +void iodb_lock_rd(); +void iodb_lock_wr(); +void iodb_unlock(uint32 ipl); +void* get_localsb_addr(); +void k_set_ucb_affinity(void* ucb, uint32 affinity); +uint32 k_extend_pfork_pool(int npages); +uint32 k_locate_ldr_img(int namelen, char* name, uint32* range, uint32* is_valid); +uint32 lookup_patches(uint32 nopatch); +void k_lock_system_pages(uint32 nopatch); +void k_unlock_system_pages(uint32 nopatch); +void pin_driver(void* dpt); +void patch_xqdrv_instr(void* addr_mov_xmt_tmo, uint32 xqtimeout); +void set_xq_xmt_timeout(void* ucb, uint32 lsb_xmt_tmo, uint32 timeout); +bool_t set_patchdesc_lookup_found(uint32 patch_id, const void* addr); +void setup_patch_pu1(const void* addr); +void setup_patch_pu2(const void* addr); +void setup_patch_pu3(const void* addr); +void setup_patch_pu4(const void* addr); +void setup_patch_pu5(const void* addr); +void setup_patch_pu6(const void* addr); +void setup_patch_pu7(const void* addr); +void dbg_brk(); + +/*************************************************************************************** +* Main routine * +***************************************************************************************/ + +int main(int argc, char** argv) +{ + xq_all_patches_mask = (1 << PATCH_ID_XQTIMXMT) | + (1 << PATCH_ID_XQTX1) | + (1 << PATCH_ID_XQTX2) | + (1 << PATCH_ID_XQTX3) | + (1 << PATCH_ID_XQTX4) | + (1 << PATCH_ID_XQTX5) | + (1 << PATCH_ID_XQTX6) | + (1 << PATCH_ID_XQTX7) | + (1 << PATCH_ID_XQTX8) | + (1 << PATCH_ID_XQTX9) | + (1 << PATCH_ID_XQTX10) | + (1 << PATCH_ID_XQRX1) | + (1 << PATCH_ID_XQRX2) | + (1 << PATCH_ID_XQRX3) | + (1 << PATCH_ID_XQRX4); + + pu_all_patches_mask = (1 << PATCH_ID_PU1) | + (1 << PATCH_ID_PU2) | + (1 << PATCH_ID_PU3) | + (1 << PATCH_ID_PU4) | + (1 << PATCH_ID_PU5) | + (1 << PATCH_ID_PU6) | + (1 << PATCH_ID_PU7); + + verify_linking_integrity(); + + /* Check if running on SIMH VAX MP */ + query_simh(); + if (! simh_present) + return VSMP_MSG_SYS_NOT_VAXMP; + + if (argc == 1) usage(); + + if (is_keyword(argv[1], "QUERY", 1)) + { + return cmd_query(argc - 2, argv + 2); + } + else if (is_keyword(argv[1], "LOAD", 2)) + { + return cmd_load(argc - 2, argv + 2); + } + else if (is_keyword(argv[1], "SET", 2)) + { + return cmd_set(argc - 2, argv + 2); + } + else if (is_keyword(argv[1], "SHOW", 2)) + { + return cmd_show(argc - 2, argv + 2); + } + else + { + usage(); + } +} + +/*************************************************************************************** +* Display usage help * +***************************************************************************************/ + +static void usage() +{ + print_msg(VSMP_MSG_SYNTAX); + fprintf(stderr, "\n"); + fprintf(stderr, "VSMP usage:\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " VSMP QUERY\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " VSMP LOAD [IDLE=ON|OFF|NEVER] [TIMESYNC=ON|OFF] [NOPATCH=(list)]\n"); + fprintf(stderr, " [XQTIMEOUT=] [INTERLOCK=PORTABLE|NATIVE]\n"); + fprintf(stderr, " [SYNCW=(SYS|ILK|SYS,ILK|ALL|NONE)]\n"); + fprintf(stderr, " [SYNCW_SYS=pct] [SYNCW_ILK=pct]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " VSMP SET [IDLE=ON|OFF] [TIMESYNC=ON|OFF]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " VSMP SHOW [IDLE] [TIMESYNC]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " VSMP SET AFFINITY { device | device-pattern }\n"); + fprintf(stderr, " /CPU={PRIMARY|ALL}\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " VSMP SHOW AFFINITY { device | device-pattern | /ALL }\n"); + fprintf(stderr, "\n"); + exit(VSMP_MSG_SYNTAX | STS$M_INHIB_MSG); +} + +/*************************************************************************************** +* Display QUERY data * +***************************************************************************************/ + +static uint32 cmd_query(int argc, char** argv) +{ + uint32 smp_options; + + if (argc != 0) usage(); + + printf("VSMP tool version: 1.0\n"); + printf("SIMH VAX MP simulator API version: %d\n", simh_api_version); + printf("Number of virtual processors configured: %d\n", simh_ncpus); + printf("Processor IDs mask: %08X\n", simh_cpu_mask); + printf("Advised host SMT slow-down factor: %g\n", (double) simh_smp_multiplier / (double) simh_smp_divider); + printf("Advised host turbo factor: %d%%\n", simh_host_turbo_factor); + printf("vSMP revision level: %d\n", simh_smp_revision); + printf("vSMP options: %X\n", simh_smp_options); + + smp_options = simh_smp_options; + if (smp_options & VAXMP_SMP_OPTION_PORTABLE_INTERLOCK) + { + printf(" portable interlock\n"); + smp_options &= ~VAXMP_SMP_OPTION_PORTABLE_INTERLOCK; + } + if (smp_options & VAXMP_SMP_OPTION_NATIVE_INTERLOCK) + { + printf(" native interlock\n"); + smp_options &= ~VAXMP_SMP_OPTION_NATIVE_INTERLOCK; + } + if (smp_options) + { + printf(" unknown %X\n", smp_options); + } + + printf("VAX virtual machine provider: "); + if (simh_vm_id == VAXMP_VM_ID) + { + printf("SIMH VAX MP"); + } + else + { + printf("%c%c%c%c", + simh_vm_id & 0xFF, + (simh_vm_id >> 8) & 0xFF, + (simh_vm_id >> 16) & 0xFF, + (simh_vm_id >> 24) & 0xFF); + } + + printf(" %d.%d", (simh_vm_version >> 24) & 0xFF, (simh_vm_version >> 16) & 0xFF); + if (simh_vm_version & 0xFFFF); + { + printf(".%d", (simh_vm_version >> 8) & 0xFF); + if (simh_vm_version & 0xFF) + printf("-%d", simh_vm_version & 0xFF); + } + printf("\n"); + printf("Synchronization window(s) active: "); + switch (simh_syncw & (SYNCW_SYS | SYNCW_ILK)) + { + case (SYNCW_SYS | SYNCW_ILK): + printf("SYS,ILK\n"); + break; + case SYNCW_SYS: + printf("SYS\n"); + break; + case SYNCW_ILK: + printf("ILK\n"); + break; + case 0: + printf("none\n"); + break; + } + + return SS$_NORMAL; +} + +/*************************************************************************************** +* Query for SIMH VAX MP presence and configuration * +***************************************************************************************/ + +static void query_simh() +{ + simh_present = FALSE; + uint32 args[15]; + args[0] = VAXMP_API_SIGNATURE; + args[1] = VAXMP_API_OP_QUERY; + args[2] = 1; /* guest API version */ + args[3] = 0; /* response status, none yet */ + lib$establish(query_simh_exc_handler); + mtpr_simh(args); + lib$revert(); + simh_present = (args[3] == 1); + simh_api_version = args[4]; + simh_ncpus = args[5]; + simh_cpu_mask = args[6]; + simh_vm_id = args[7]; + simh_vm_version = args[8]; + simh_smp_revision = args[9]; + simh_smp_options = args[10]; + simh_smp_multiplier = args[11]; + simh_smp_divider = args[12]; + simh_host_turbo_factor = args[13]; + simh_syncw = args[14]; +} + +static uint32 query_simh_exc_handler(struct chf$signal_array* sig, struct chf$mech_array* mech) +{ + if (sig->chf$l_sig_name == SS$_UNWIND || + sig->chf$l_sig_name == SS$_OPCDEC) + { + return sys$unwind(NULL, NULL); + } + else + { + return SS$_RESIGNAL; + } +} + +/*************************************************************************************** +* Load resident module * +***************************************************************************************/ + +static uint32 cmd_load(int argc, char** argv) +{ + uint32 status; + int ak; + char key[MAXARGSIZE]; + char value[MAXARGSIZE]; + double f; + uint32 kargs[5]; + static const char* xdelta_name = "SYSTEM_DEBUG.EXE"; + static const char* msg_rtns_name = "MESSAGE_ROUTINES.EXE"; + + /* parse arguments */ + for (ak = 0; ak < argc; ak++) + { + if (! split_key_value(argv[ak], key, countof(key), value, countof(value))) + usage(); + + if (is_keyword(key, "IDLE", 2)) + { + if (is_keyword(value, "ON", 2)) + smp_idle = SIM_K_IDLE_ON; + else if (is_keyword(value, "OFF", 3)) + smp_idle = SIM_K_IDLE_OFF; + else if (is_keyword(value, "NEVER", 3)) + smp_idle = SIM_K_IDLE_NEVER; + else + usage(); + } + else if (is_keyword(key, "TIMESYNC", 2)) + { + if (is_keyword(value, "ON", 2)) + smp_timesync = SIM_K_TIMESYNC_ON; + else if (is_keyword(value, "OFF", 3)) + smp_timesync = SIM_K_TIMESYNC_OFF; + else + usage(); + } + else if (is_keyword(key, "NOPATCH", 3)) + { + nopatch |= parse_nopatch(value); + } + else if (is_keyword(key, "XQTIMEOUT", 2)) + { + xqtimeout = parse_udec(value, "XQTIMEOUT"); + if (! (xqtimeout >= XQ_MIN_TIMEOUT && xqtimeout <= XQ_MAX_TIMEOUT)) + inv_opt_val("XQTIMEOUT"); + } + else if (is_keyword(key, "INTERLOCK", 3)) + { + if (is_keyword(value, "PORTABLE", 1)) + use_native_interlock = FALSE; + else if (is_keyword(value, "NATIVE", 1)) + use_native_interlock = TRUE; + else + inv_opt_val("INTERLOCK"); + } + else if (is_keyword(key, "SYNCW", -1)) + { + /* + * SYNCW=(SYS,ILK) or SYS or ILK or ALL or NONE + */ + char* list; + char* tok; + int k; + + syncw_on = 0; + + list = strdup(value); + if (list == NULL) exit(SS$_INSFMEM); + k_strupr(list); + + if (*list == '(') + { + list++; + k = strlen(list); + if (k == 0 || list[k - 1] != ')') + usage(); + list[k - 1] = '\0'; + } + + tok = strtok(list, ","); + while (tok) + { + if (is_keyword(tok, "SYS", 1)) + { + syncw_on |= SYNCW_SYS; + } + else if (is_keyword(tok, "ILK", 1)) + { + syncw_on |= SYNCW_ILK; + } + else if (is_keyword(tok, "ALL", 1)) + { + syncw_on |= SYNCW_SYS | SYNCW_ILK; + } + else if (is_keyword(tok, "NONE", 1)) + { + syncw_on = 0; + } + else + { + inv_opt_val("SYNCW"); + } + tok = strtok(NULL, ","); + } + } + else if (is_keyword(key, "SYNCW_SYS", -1)) + { + /* + * percentage of maximum window width to use as safe window size + * default = 66% + * range = 10-90% + */ + syncw_sys_pct = parse_udec(value, "SYNCW_SYS"); + if (! (syncw_sys_pct >= 10 && syncw_sys_pct <= 90)) + inv_opt_val("SYNCW_SYS"); + } + else if (is_keyword(key, "SYNCW_ILK", -1)) + { + /* + * percentage of maximum window width to use as safe window size + * default = 66% + * range = 10-90% + */ + syncw_ilk_pct = parse_udec(value, "SYNCW_ILK"); + if (! (syncw_ilk_pct >= 10 && syncw_ilk_pct <= 90)) + inv_opt_val("SYNCW_ILK"); + } + else + { + usage(); + } + } + + /* validate parameter combination */ + validate_syncw_parameters(); + + /* begin pre-load preparations */ + check_smp_enabled(TRUE); + lock_nonpaged(); + + /* check if XDelta is loaded */ + if (0 == (nopatch & (1 << PATCH_ID_XDELTA))) + { + uint32 xdelta_is_valid = 0; + kargs[0] = 4; + kargs[1] = strlen(xdelta_name); + kargs[2] = (uint32) (char*) xdelta_name; + kargs[3] = (uint32) (uint32*) xdelta_range; + kargs[4] = (uint32) & xdelta_is_valid; + check_vms_status(sys$cmkrnl(k_locate_ldr_img, kargs)); + if (xdelta_range[0] && !xdelta_is_valid) exit(VSMP_MSG_XDTINVALID); + } + + /* adjust nopatch arguments */ + if (smp_idle == SIM_K_IDLE_NEVER) + { + nopatch |= 1 << PATCH_ID_CHSEP; + nopatch |= 1 << PATCH_ID_RESCHED; + } + if (xdelta_range[0] == 0) + { + nopatch |= 1 << PATCH_ID_XDELTA; + } + + /* locate message_routines.exe */ + if (0 == (nopatch & (1 << PATCH_ID_NUMTIM))) + { + uint32 msg_rtns_is_valid = 0; + kargs[0] = 4; + kargs[1] = strlen(msg_rtns_name); + kargs[2] = (uint32) (char*) msg_rtns_name; + kargs[3] = (uint32) (uint32*) msg_rtns_range; + kargs[4] = (uint32) & msg_rtns_is_valid; + check_vms_status(sys$cmkrnl(k_locate_ldr_img, kargs)); + if (msg_rtns_range[0] == 0 || !msg_rtns_is_valid) exit(VSMP_MSG_MSGRTNSLDR); + } + + /* continue pre-load preparations */ + acquire_node_lock(); + check_resident_image_not_loaded(); + check_vms_status(lookup_patches(nopatch)); + + /* continue pre-load preparations */ + perform_calibration(); + verify_calibration(); + apply_calibration(); + + /* + * Calculate number of PFORK (fork-to-primary) pool pages required. These pages are used to store messages + * for printing to console emitted by high-IPL code during SMP state transitons (used by routines + * SMP$WRITE_OPA0 and SMP$FORK_TO_PRIMARY). Guestimate of 5.5 pages per processor makes it ample. + */ + f = simh_ncpus * 5.5; + pfork_pool_pages = (int) ceil(f); + if (f - pfork_pool_pages >= 0.5) + pfork_pool_pages++; + if (pfork_pool_pages > 255) + pfork_pool_pages = 255; + + /* + * calculate SYNCW SYS window size + */ + if (syncw_on & SYNCW_SYS) + { + uint32 spinwait = uimin(sgn$gl_smp_lngspinwait, sgn$gl_smp_spinwait); + /* translating loop count to instruction count is complicated, but the tightest loop + is just sobgtr instruction, so map as 1:1 */ + double fws = (double) spinwait * (double) exe_tenusec * (double) exe_ubdelay * (double) syncw_sys_pct / 100.0; + /* avoid overflows */ + if (fws > (double) 0x7F000000) fws = (double) 0x7F000000; + syncw_winsize_sys = floor(fws); + + /* check that window size is reasonable */ + if (syncw_winsize_sys < 40000) + { + print_2msgs(VSMP_MSG_VLOW_SW_SYS, VSMP_MSG_ADV_SW_SYS); + exit(VSMP_MSG_VLOW_SW_SYS | STS$M_INHIB_MSG); + } + else if (syncw_winsize_sys < 200000) + { + print_2msgs(VSMP_MSG_LOW_SW_SYS, VSMP_MSG_ADV_SW_SYS); + } + } + else + { + syncw_winsize_sys = 0; + } + + /* + * calculate SYNCW ILK window size (see VAX MP Techinical Overview for details) + */ + if (syncw_on & SYNCW_ILK) + { + /* number of loops hardwired in $INSQHI etc. macros */ + const uint32 hardwired_nloops = 900000; + if (exe$gl_lockrtry >= 0x80000000) exit(VSMP_MSG_IVLOCKRTRY); + uint32 nloops = (nopatch & (1 << PATCH_ID_LOCKRTRY)) ? uimin(exe$gl_lockrtry, hardwired_nloops) + : hardwired_nloops; + /* there are at minimum three instructions in $INSQHI etc. loops */ + double fws = (double) nloops * 3 * (double) syncw_ilk_pct / 100.0; + /* avoid overflows */ + if (fws > (double) 0x7F000000) fws = (double) 0x7F000000; + syncw_winsize_ilk = floor(fws); + + /* check that window size is reasonable */ + if (syncw_winsize_ilk < 40000) + { + print_2msgs(VSMP_MSG_VLOW_SW_ILK, VSMP_MSG_ADV_SW_ILK); + exit(VSMP_MSG_VLOW_SW_ILK | STS$M_INHIB_MSG); + } + else if (syncw_winsize_ilk < 200000) + { + print_2msgs(VSMP_MSG_LOW_SW_ILK, VSMP_MSG_ADV_SW_ILK); + } + } + else + { + syncw_winsize_ilk = 0; + } + + if ((syncw_on & (SYNCW_SYS|SYNCW_ILK)) == (SYNCW_SYS|SYNCW_ILK) && syncw_winsize_sys < syncw_winsize_ilk) + print_2msgs(VSMP_MSG_SYS_LESS_ILK, VSMP_MSG_ADV_ISW_SYS); + + /* set requested SMP options */ + smp_must_options = use_native_interlock ? VAXMP_SMP_OPTION_NATIVE_INTERLOCK + : VAXMP_SMP_OPTION_PORTABLE_INTERLOCK; + smp_want_options = 0; + + /* perform loading */ + kerror_cause_usr = 0; + status = sys$cmkrnl(k_load_resident_image, kargs_none); + if (status == VSMP_MSG_LDR_VERIFY && kerror_cause_usr) + { + print_2msgs(status, kerror_cause_usr); + exit(status | STS$M_INHIB_MSG); + } + else + { + check_vms(status); + print_msg(VSMP_MSG_LOADED); + return SS$_NORMAL; + } + +cleanup: + exit(status); +} + +static uint32 parse_nopatch(const char* value) +{ + char* list; + char* tok; + int k; + uint32 nopatch = 0; + + list = strdup(value); + if (list == NULL) exit(SS$_INSFMEM); + k_strupr(list); + + if (*list == '(') + { + list++; + k = strlen(list); + if (k == 0 || list[k - 1] != ')') + usage(); + list[k - 1] = '\0'; + } + + tok = strtok(list, ","); + while (tok) + { + if (is_keyword(tok, "XDELTA", 2)) + nopatch |= (1 << PATCH_ID_XDELTA); + else if (is_keyword(tok, "CHSEP", 3)) + nopatch |= (1 << PATCH_ID_CHSEP); + else if (is_keyword(tok, "RESCHED", 3)) + nopatch |= (1 << PATCH_ID_RESCHED); + else if (is_keyword(tok, "NUMTIM", 3)) + nopatch |= (1 << PATCH_ID_NUMTIM); + else if (is_keyword(tok, "MFYCAP", 4)) + nopatch |= (1 << PATCH_ID_MFYCAP); + else if (is_keyword(tok, "LOCKRTRY", 3)) + nopatch |= (1 << PATCH_ID_LOCKRTRY); + else if (is_keyword(tok, "XQTIMXMT", 4)) + nopatch |= (1 << PATCH_ID_XQTIMXMT); + else if (is_keyword(tok, "UCBTMO", 3)) + nopatch |= (1 << PATCH_ID_UCBTMO); + else if (is_keyword(tok, "CRBTMO", 3)) + nopatch |= (1 << PATCH_ID_CRBTMO); + else if (is_keyword(tok, "XQTX1", -1)) + nopatch |= (1 << PATCH_ID_XQTX1); + else if (is_keyword(tok, "XQTX2", 0)) + nopatch |= (1 << PATCH_ID_XQTX2); + else if (is_keyword(tok, "XQTX3", 0)) + nopatch |= (1 << PATCH_ID_XQTX3); + else if (is_keyword(tok, "XQTX4", 0)) + nopatch |= (1 << PATCH_ID_XQTX4); + else if (is_keyword(tok, "XQTX5", 0)) + nopatch |= (1 << PATCH_ID_XQTX5); + else if (is_keyword(tok, "XQTX6", 0)) + nopatch |= (1 << PATCH_ID_XQTX6); + else if (is_keyword(tok, "XQTX7", 0)) + nopatch |= (1 << PATCH_ID_XQTX7); + else if (is_keyword(tok, "XQTX8", 0)) + nopatch |= (1 << PATCH_ID_XQTX8); + else if (is_keyword(tok, "XQTX9", 0)) + nopatch |= (1 << PATCH_ID_XQTX9); + else if (is_keyword(tok, "XQTX10", -1)) + nopatch |= (1 << PATCH_ID_XQTX10); + else if (is_keyword(tok, "XQRX1", 0)) + nopatch |= (1 << PATCH_ID_XQRX1); + else if (is_keyword(tok, "XQRX2", 0)) + nopatch |= (1 << PATCH_ID_XQRX2); + else if (is_keyword(tok, "XQRX3", 0)) + nopatch |= (1 << PATCH_ID_XQRX3); + else if (is_keyword(tok, "XQRX4", 0)) + nopatch |= (1 << PATCH_ID_XQRX4); + else if (is_keyword(tok, "PU1", 0)) + nopatch |= (1 << PATCH_ID_PU1); + else if (is_keyword(tok, "PU2", 0)) + nopatch |= (1 << PATCH_ID_PU2); + else if (is_keyword(tok, "PU3", 0)) + nopatch |= (1 << PATCH_ID_PU3); + else if (is_keyword(tok, "PU4", 0)) + nopatch |= (1 << PATCH_ID_PU4); + else if (is_keyword(tok, "PU5", 0)) + nopatch |= (1 << PATCH_ID_PU5); + else if (is_keyword(tok, "PU6", 0)) + nopatch |= (1 << PATCH_ID_PU6); + else if (is_keyword(tok, "PU7", 0)) + nopatch |= (1 << PATCH_ID_PU7); + else + { + print_msg_1(VSMP_MSG_IVPATCHID, (uint32) tok); + exit(VSMP_MSG_IVPATCHID | STS$M_INHIB_MSG); + } + tok = strtok(NULL, ","); + } + + /* + * CRBTMO and UCBTMO are not currently utilized, so disable them + */ + nopatch |= (1 << PATCH_ID_UCBTMO); + nopatch |= (1 << PATCH_ID_CRBTMO); + + return nopatch; +} + +static void validate_syncw_parameters() +{ + uint32 time_control = exe$gl_time_control & 0x6; + bool_t valid = FALSE; + + if (use_native_interlock && 0 == (simh_smp_options & VAXMP_SMP_OPTION_NATIVE_INTERLOCK)) + { + print_msg(VSMP_MSG_NONATIVE); + use_native_interlock = FALSE; + } + + if (smp_idle == SIM_K_IDLE_NEVER && (syncw_on & SYNCW_ILK)) + { + print_msg(VSMP_MSG_SYNCWIDLEOFF); + smp_idle = SIM_K_IDLE_OFF; + } + + if (use_native_interlock) + { + /* native */ + if (syncw_on == (SYNCW_SYS|SYNCW_ILK)) + { + valid = (time_control == 2 || time_control == 6); + } + else if (syncw_on == SYNCW_ILK) + { + valid = (time_control == 6); + } + } + else + { + /* portable */ + if (syncw_on == (SYNCW_SYS|SYNCW_ILK)) + { + valid = (time_control == 2 || time_control == 6); + } + else if (syncw_on == SYNCW_SYS) + { + valid = (time_control == 2 || time_control == 6); + } + else if (syncw_on == SYNCW_ILK) + { + valid = (time_control == 6); + } + else if (syncw_on == 0) + { + valid = (time_control == 6); + } + } + + if (! valid) + { + char msg[256]; + char* pmsg; + + sprintf(msg, "INTERLOCK=%s, SYNCW=", use_native_interlock ? "NATIVE" : "PORTABLE"); + pmsg = msg + strlen(msg); + switch (syncw_on) + { + case (SYNCW_SYS | SYNCW_ILK): + sprintf(pmsg, "(SYS,ILK)"); + break; + case SYNCW_SYS: + sprintf(pmsg, "SYS"); + break; + case SYNCW_ILK: + sprintf(pmsg, "ILK"); + break; + case 0: + sprintf(pmsg, "NONE"); + break; + } + sprintf(msg + strlen(msg), ", SYSGEN TIME_CONTROL=%d", time_control); + + print_msg_1(VSMP_MSG_IVPARSET, (uint32) msg); + exit(VSMP_MSG_IVPARSET | STS$M_INHIB_MSG); + } +} + +/*************************************************************************************** +* Set control parameters * +***************************************************************************************/ + +static uint32 cmd_set(int argc, char** argv) +{ + uint32 status; + uint32 kargs[2]; + int ak; + char key[MAXARGSIZE]; + char value[MAXARGSIZE]; + bool_t set_idle = FALSE; + bool_t set_timesync = FALSE; + uint32 new_idle; + uint32 new_timesync; + uint32 old_idle; + + /* parse arguments */ + if (argc == 0) usage(); + + if (is_keyword(argv[0], "AFFINITY", 2)) + { + return cmd_set_affinity(argc - 1, argv + 1); + } + + for (ak = 0; ak < argc; ak++) + { + if (! split_key_value(argv[ak], key, countof(key), value, countof(value))) + usage(); + + if (is_keyword(key, "IDLE", 2)) + { + set_idle = TRUE; + + if (is_keyword(value, "ON", 2)) + new_idle = SIM_K_IDLE_ON; + else if (is_keyword(value, "OFF", 3)) + new_idle = SIM_K_IDLE_OFF; + else if (is_keyword(value, "NEVER", 3)) + new_idle = SIM_K_IDLE_NEVER; + else + usage(); + } + else if (is_keyword(key, "TIMESYNC", 2)) + { + set_timesync = TRUE; + + if (is_keyword(value, "ON", 2)) + new_timesync = SIM_K_TIMESYNC_ON; + else if (is_keyword(value, "OFF", 3)) + new_timesync = SIM_K_TIMESYNC_OFF; + else + usage(); + } + else + { + usage(); + } + } + + /* prepare */ + lock_nonpaged(); + acquire_node_lock(); + check_resident_image_loaded(); + + /* verify IDLE value */ + if (set_idle) + { + kargs[0] = 1; + kargs[1] = (uint32) & old_idle; + check_vms_status(sys$cmkrnl(kcall_reloc(kcall_get_idle_t, kcall_get_idle), kargs)); + if (old_idle == new_idle) set_idle = FALSE; + if (set_idle && old_idle == SIM_K_IDLE_NEVER) + exit(VSMP_MSG_IDLE_NEVER); + } + + /* set IDLE */ + if (set_idle) + { + kargs[0] = 1; + kargs[1] = new_idle; + check_vms_status(sys$cmkrnl(kcall_reloc(kcall_set_idle_t, kcall_set_idle), kargs)); + } + + /* set TIMESYNC */ + if (set_timesync) + { + kargs[0] = 1; + kargs[1] = new_timesync; + check_vms_status(sys$cmkrnl(kcall_reloc(kcall_set_timesync_t, kcall_set_timesync), kargs)); + } + + return SS$_NORMAL; + +cleanup: + exit(status); +} + +/*************************************************************************************** +* Show parameters and status * +***************************************************************************************/ + +static uint32 cmd_show(int argc, char** argv) +{ + uint32 status; + uint32 kargs[2]; + int ak; + char key[MAXARGSIZE]; + char value[MAXARGSIZE]; + bool_t show_idle = FALSE; + bool_t show_timesync = FALSE; + + /* parse arguments */ + if (argc == 0) + { + show_idle = TRUE; + show_timesync = TRUE; + } + else if (is_keyword(argv[0], "AFFINITY", 2)) + { + return cmd_show_affinity(argc - 1, argv + 1); + } + else for (ak = 0; ak < argc; ak++) + { + if (is_keyword(argv[ak], "IDLE", 2)) + show_idle = TRUE; + else if (is_keyword(argv[ak], "TIMESYNC", 2)) + show_timesync = TRUE; + else + usage(); + } + + /* prepare */ + lock_nonpaged(); + acquire_node_lock(); + check_resident_image_loaded(); + + if (show_idle) + { + kargs[0] = 1; + kargs[1] = (uint32) & smp_idle; + check_vms_status(sys$cmkrnl(kcall_reloc(kcall_get_idle_t, kcall_get_idle), kargs)); + switch (smp_idle) + { + case SIM_K_IDLE_OFF: + printf("IDLE is OFF\n"); + break; + case SIM_K_IDLE_ON: + printf("IDLE is ON\n"); + break; + case SIM_K_IDLE_NEVER: + printf("IDLE is NEVER\n"); + break; + } + } + + if (show_timesync) + { + kargs[0] = 1; + kargs[1] = (uint32) & smp_timesync; + check_vms_status(sys$cmkrnl(kcall_reloc(kcall_get_timesync_t, kcall_get_timesync), kargs)); + switch (smp_timesync) + { + case SIM_K_TIMESYNC_OFF: + printf("TIMESYNC is OFF\n"); + break; + case SIM_K_TIMESYNC_ON: + printf("TIMESYNC is ON\n"); + break; + } + } + + return SS$_NORMAL; + +cleanup: + exit(status); +} + +/*************************************************************************************** +* Set device affinity * +***************************************************************************************/ + +#define DDB_NAME_STR_SIZE 31 + +static uint32 cmd_set_affinity(int argc, char** argv) +{ + char* pattern = NULL; + uint32 status; + uint32 kargs[3]; + int ak; + uint32 cmd_set_affinity_value; + bool_t cmd_set_affinity_value_set = FALSE; + + globalvalue uint32 ddb$s_name_str; + + /* check size is in range */ + if (DDB_NAME_STR_SIZE < ddb$s_name_str) + exit(VSMP_MSG_MISBUILT); + + /* parse the arguments */ + for (ak = 0; ak < argc; ak++) + { + char* arg = argv[ak]; + if (arg[0] == '/') + { + const char* qvalue; + if (is_qualifier(arg, "CPUS", 1, &qvalue)) + { + if (qvalue && is_keyword(qvalue, "PRIMARY", 1)) + { + cmd_set_affinity_value = 0; + cmd_set_affinity_value_set = TRUE; + } + else if (qvalue && is_keyword(qvalue, "ALL", 1)) + { + cmd_set_affinity_value = 0xFFFFFFFF; + cmd_set_affinity_value_set = TRUE; + } + else + { + usage(); + } + } + else + { + usage(); + } + } + else + { + if (pattern) + usage(); + else + pattern = arg; + } + } + + /* validate the arguments */ + if (! cmd_set_affinity_value_set) + usage(); + + if (pattern == NULL || *pattern == '\0') + usage(); + + pattern = strdup(pattern); + if (pattern == NULL) exit(SS$_INSFMEM); + k_strupr(pattern); + + check_smp_enabled(FALSE); + lock_nonpaged(); + + kargs[0] = 2; + kargs[1] = (uint32) pattern; + kargs[2] = cmd_set_affinity_value; + check_vms_status(sys$cmkrnl(k_set_device_affinity, kargs)); + + return SS$_NORMAL; + +cleanup: + exit(status); +} + +static uint32 k_set_device_affinity(const char* pattern, uint32 affinity) +{ + globalvalue uint32 sb$l_ddb; + globalvalue uint32 ddb$l_link; + globalvalue uint32 ddb$b_name_len; + globalvalue uint32 ddb$t_name_str; + globalvalue uint32 ddb$l_ucb; + globalvalue uint32 ucb$l_link; + globalvalue uint32 ucb$w_unit; + globalvalue uint32 ucb$l_affinity; + + void* sb; + void* ddb; + void* ucb; + int match_count = 0; + char namebuf[DDB_NAME_STR_SIZE + 10]; + uint32 status; + + /* lock IO database, returns here at IPL ASTDEL */ + iodb_lock_wr(); + + /* get SB for local system */ + sb = get_localsb_addr(); + + /* enumerate DDBs */ + for (ddb = offset_ref(void*, sb, sb$l_ddb); ddb != NULL; ddb = offset_ref(void*, ddb, ddb$l_link)) + { + uint32 dnamelen = offset_ref(byte_t, ddb, ddb$b_name_len); + char* dname = offset_ptr(char, ddb, ddb$t_name_str); + kmemcpy(namebuf, dname, dnamelen); + namebuf[dnamelen] = 0; + if (k_streq(namebuf, "MBA")) continue; + if (k_streq(namebuf, "NLA")) continue; + + /* enumerate UCBs */ + for (ucb = offset_ref(void*, ddb, ddb$l_ucb); ucb != NULL; ucb = offset_ref(void*, ucb, ucb$l_link)) + { + if (k_match_pattern(pattern, dname, dnamelen, offset_ref(uint16, ucb, ucb$w_unit), FALSE)) + { + k_set_ucb_affinity(ucb, affinity); + match_count++; + } + } + } + + /* unlock IO database, reset IPL to 0 */ + iodb_unlock(0); + + status = SS$_NORMAL; + + if (match_count == 0) + status = SS$_NOSUCHDEV; + + return status; +} + +/*************************************************************************************** +* Display device affinity * +***************************************************************************************/ + +typedef struct __ucb_info +{ + uint32 unit; + uint32 affinity; +} +ucb_info; + +typedef struct __ddb_info +{ + uint32 namelen; + char name[DDB_NAME_STR_SIZE + 1]; +} +ddb_info; + +static uint32 cmd_show_affinity(int argc, char** argv) +{ + char* pattern = NULL; + uint32 status; + uint32 bufsize; + uint32* buffer = NULL; + uint32* bp; + uint32 kargs[4]; + uint32 overflow; + uint32 mark; + ddb_info* p_ddb_info; + ucb_info* p_ucb_info; + bool_t header = TRUE; + + globalvalue uint32 ddb$s_name_str; + + /* check size is in range */ + if (DDB_NAME_STR_SIZE < ddb$s_name_str) + exit(VSMP_MSG_MISBUILT); + + if (argc == 0) + { + pattern = "*"; + } + else if (argc != 1) + { + usage(); + } + else if (is_keyword(argv[0], "/ALL", 2)) + { + pattern = "*"; + } + else + { + pattern = argv[0]; + } + + pattern = strdup(pattern); + if (pattern == NULL) exit(SS$_INSFMEM); + k_strupr(pattern); + + check_smp_enabled(FALSE); + lock_nonpaged(); + + for (bufsize = 16 * 1024; ; bufsize = bufsize * 2) + { + buffer = (uint32*) malloc(bufsize); + if (buffer == NULL) exit(SS$_INSFMEM); + kargs[0] = 3; + kargs[1] = (uint32) buffer; + kargs[2] = bufsize; + kargs[3] = (uint32) & overflow; + overflow = FALSE; + check_vms_status(sys$cmkrnl(k_gather_device_affinity, kargs)); + if (! overflow) break; + free (buffer); + } + + for (bp = buffer; (mark = *bp++) != 'E'; ) + { + if (mark == 'D') + { + p_ddb_info = (ddb_info*) bp; + bp = offset_ptr(uint32, bp, sizeof(ddb_info)); + } + else // mark == 'U' + { + char namebuf[DDB_NAME_STR_SIZE + 10]; + + p_ucb_info = (ucb_info*) bp; + bp = offset_ptr(uint32, bp, sizeof(ucb_info)); + if (streqi(p_ddb_info->name, "MBA")) continue; + if (streqi(p_ddb_info->name, "NLA")) continue; + if (k_match_pattern(pattern, p_ddb_info->name, p_ddb_info->namelen, p_ucb_info->unit, TRUE)) + { + if (header) + { + printf("Local device affinity to CPUs:\n\n"); + header = FALSE; + } + + sprintf(namebuf, "%s%d", p_ddb_info->name, p_ucb_info->unit); + printf(" %-8s \t", namebuf); + if (p_ucb_info->affinity == 0xFFFFFFFF) + { + printf("ALL\n"); + } + else if (p_ucb_info->affinity == 0) + { + printf("PRIMARY\n"); + } + else + { + bool_t first = TRUE; + for (int k = 0; k <= 31; k++) + { + printf(first ? "%02d" : " %02d", k); + first = FALSE; + } + printf("\n"); + } + } + } + } + + return SS$_NORMAL; + +cleanup: + exit(status); +} + +/* + * Gather device affinity information from IO database into the output buffer, + * as a sequence of D-records (representing data from DDBs) and U-records (data from UCBs) + * terminated by E-record (The End). + * + * If buffer size is not sufficient, *p_overflow is set to TRUE, and caller should call + * again with a larger buffer. + */ +static uint32 k_gather_device_affinity(uint32* buffer, uint32 bufsize, uint32* p_overflow) +{ + globalvalue uint32 sb$l_ddb; + globalvalue uint32 ddb$l_link; + globalvalue uint32 ddb$b_name_len; + globalvalue uint32 ddb$t_name_str; + globalvalue uint32 ddb$l_ucb; + globalvalue uint32 ucb$l_link; + globalvalue uint32 ucb$w_unit; + globalvalue uint32 ucb$l_affinity; + + void* sb; + void* ddb; + void* ucb; + ddb_info* p_ddb_info; + ucb_info* p_ucb_info; + + *p_overflow = FALSE; + + /* lock IO database, returns here at IPL ASTDEL */ + iodb_lock_rd(); + + /* get SB for local system */ + sb = get_localsb_addr(); + + /* enumerate DDBs */ + for (ddb = offset_ref(void*, sb, sb$l_ddb); ddb != NULL; ddb = offset_ref(void*, ddb, ddb$l_link)) + { + /* check enough space for D-mark, DDB info and E-mark */ + if (bufsize < sizeof(uint32) + sizeof(ddb_info) + sizeof(uint32)) + goto overflow; + + /* emit D-mark and DDB info */ + *buffer++ = 'D'; + p_ddb_info = (ddb_info*) buffer; + p_ddb_info->namelen = offset_ref(byte_t, ddb, ddb$b_name_len); + kmemcpy(p_ddb_info->name, offset_ptr(byte_t, ddb, ddb$t_name_str), p_ddb_info->namelen); + p_ddb_info->name[p_ddb_info->namelen] = 0; + + /* advance buffer */ + buffer = offset_ptr(uint32, buffer, sizeof(ddb_info)); + bufsize -= sizeof(uint32) + sizeof(ddb_info); + + /* enumerate UCBs */ + for (ucb = offset_ref(void*, ddb, ddb$l_ucb); ucb != NULL; ucb = offset_ref(void*, ucb, ucb$l_link)) + { + /* check enough space for U-mark, UCB info and E-mark */ + if (bufsize < sizeof(uint32) + sizeof(ucb_info) + sizeof(uint32)) + goto overflow; + + /* emit U-mark and UCB info */ + *buffer++ = 'U'; + p_ucb_info = (ucb_info*) buffer; + p_ucb_info->unit = offset_ref(uint16, ucb, ucb$w_unit); + p_ucb_info->affinity = offset_ref(uint32, ucb, ucb$l_affinity); + + /* advance buffer */ + buffer = offset_ptr(uint32, buffer, sizeof(ucb_info)); + bufsize -= sizeof(uint32) + sizeof(ucb_info); + } + } + + /* emit E-mark */ + *buffer = 'E'; + + /* unlock IO database, reset IPL to 0 */ + iodb_unlock(0); + + return SS$_NORMAL; + + /* return in case of buffer overflow */ +overflow: + *p_overflow = TRUE; + iodb_unlock(0); + return SS$_NORMAL; +} + +/* + * Check if device unit (described by "name/namelen" and "unit" number) matches the supplied pattern. + * If "endstar" is true, there is an implied "*" at the end of the pattern argument unless the pattern contains a digit, + * i.e. pattern "XQA" matches device "XQA2", but pattern "XQA0" does not match it. + */ +static bool_t k_match_pattern(const char* pattern, const char* name, int namelen, uint32 unit, bool_t endstar) +{ + char uname[DDB_NAME_STR_SIZE + 10]; + const char* unp = uname; + const char* pp = pattern; + char c; + + kmemcpy(uname, name, namelen); + k_itoa(uname + namelen, unit); + + while (c = *pp++) + { + if (c >= '0' && c <= '9') + endstar = FALSE; + + if (c == '*') + { + while (*pp == '*') pp++; + if (*pp) + return FALSE; + return TRUE; + } + + if (c != *unp++) + return FALSE; + } + + if (*unp == '\0' || endstar) + return TRUE; + + return FALSE; +} + +/*************************************************************************************** +* Perform processor calibration before statring SMP until the reading is stable * +***************************************************************************************/ + +#define CALIBR_MIN_SAMPLES 25 +#define CALIBR_MAX_SAMPLES 200 +#define CALIBR_TOLERANCE_RANGE 0.15 +#define CALIBR_MIN_WITHIN_TR 5 + +/* + * Perform CPU busy-wait loops calibration multiple times to eliminate the effect of possible + * concurrent load on host system that may result in the under-calibration. + */ +static void perform_calibration() +{ + int status; + int nsamples; + int ns; + int pass = 1; + uint32 tenusec[CALIBR_MAX_SAMPLES]; + uint32 ubdelay[CALIBR_MAX_SAMPLES]; + uint32 mul[CALIBR_MAX_SAMPLES]; + + print_msg(VSMP_MSG_CALIBRATING); + +again: + + for (nsamples = 0; nsamples < CALIBR_MAX_SAMPLES; ) + { + uint32 kargs[3]; + kargs[0] = 2; + kargs[1] = (uint32) & tenusec[nsamples]; + kargs[2] = (uint32) & ubdelay[nsamples]; + check_vms_status(sys$cmkrnl(kload_calibrate_sysloops, kargs)); + mul[nsamples] = tenusec[nsamples] * ubdelay[nsamples]; + nsamples++; + if (nsamples >= CALIBR_MIN_SAMPLES && is_calibration_stable(mul, nsamples, & ns)) + { + exe_ubdelay = ubdelay[ns]; + /* + * Busy-wait loops are two-layered: + * + * TENUSEC controls outer loop. + * UBDEALY control inner loop. + * + * On SMT/Hyperthreaded host processors measured refults may be affected by load running on the same + * core during calibration and slowing down the calibration loop, and may therefore result in the + * undercalibration of the loop. + * + * Adjust TENUSEC for SMT slow-down factor and host turbo factor advised by VMM. + */ + double f = (double) tenusec[ns] * (double) simh_smp_multiplier / (double) simh_smp_divider; + f *= (double) simh_host_turbo_factor / 100.0; + exe_tenusec = (int) floor(f); + if (f - exe_tenusec > 0.5) exe_tenusec++; + print_msg_1(VSMP_MSG_CALIBRATED, nsamples); + return; + } + } + + /* + * Sometimes calibraton fails due to spurious reasons, + * such as external load or perhaps cache flush/cold/warm effects. + * Try one more time before giving up. + */ + if (pass++ <= 1) + { + print_msg(VSMP_MSG_CALIBRETRY); + goto again; + } + + /* was unable to attain stable calibration */ + status = VSMP_MSG_CALIBR_UNSTABLE; + +cleanup: + exit(status); +} + +static bool_t is_calibration_stable(const uint32* m, int nsamples, int* pmax) +{ + int maxm = 0; + int in_tolerance_range = 0; + int k; + + /* find maximum */ + for (k = 0; k < nsamples; k++) + { + if (k == 0 || m[k] > maxm) + { + maxm = m[k]; + *pmax = k; + } + } + + /* count samples close to maximum */ + for (k = 0; k < nsamples; k++) + { + if (k != *pmax && m[k] >= maxm * (1.0 - CALIBR_TOLERANCE_RANGE)) + { + if (++in_tolerance_range >= CALIBR_MIN_WITHIN_TR) + return TRUE; + } + } + + return FALSE; +} + +/* + * Verify that calibration would not cause overflow: + * + * TENUSEC * UBDELAY * SPINWAIT <= 0x7FFFFFFF + * TENUSEC * UBDELAY * LNGSPINWAIT <= 0x7FFFFFFF + * + * Note that we cannot just knock down SPINWAIT/LNGSPINWAIT value in case overflow is detected, + * because by now they had been copied in numerous spinlocks, including spinlocks outside of + * well-known spinlocks and IO devices spinlocks. Thus, in base VMS alone there is internal + * CRDERR spinlock, log manager spinlocks and there are may be various internal private spinlocks + * in various laryered privileged components and drivers that we cannot know about. + * + * Therefore we cannot adjust SPINWAIT/LNGSPINWAIT values dynamically as we cannot be aware of + * all locations where the values had been copied by now. Therefore the only available recourse + * is to issue user an advisory message to reduce SPINWAIT/LNGSPINWAIT and to reboot. + */ +static void verify_calibration() +{ + uint32 maxwait = 0x7FFFFFFF / (exe_tenusec * exe_ubdelay); + uint32 status = SS$_NORMAL; + + uint32 twait = (uint32) floor(maxwait * 0.95); + twait -= twait % 1000; + + if (sgn$gl_smp_spinwait > maxwait) + { + print_3msgs_val(VSMP_MSG_UNSTART, VSMP_MSG_SPWHIGH, VSMP_MSG_ADV_SPW_HIGH, twait); + status = VSMP_MSG_SPWHIGH; + } + + if (sgn$gl_smp_lngspinwait > maxwait) + { + print_3msgs_val(VSMP_MSG_UNSTART, VSMP_MSG_LSPWHIGH, VSMP_MSG_ADV_LSPW_HIGH, twait); + status = VSMP_MSG_LSPWHIGH; + } + + if (status != SS$_NORMAL) + exit(status | STS$M_INHIB_MSG); +} + +static void apply_calibration() +{ + int status; + uint32 kargs[3]; + kargs[0] = 2; + kargs[1] = exe_tenusec; + kargs[2] = exe_ubdelay; + check_vms_status(sys$cmkrnl(kload_apply_calibration, kargs)); + return; + +cleanup: + exit(status); +} + +/*************************************************************************************** +* Check whether SMP is enabled and VMS multiprocesing system image is loaded * +***************************************************************************************/ + +static void check_smp_enabled(bool_t fatal) +{ + uint32 smp_enabled = FALSE; + uint32 smp_unmod_drv = FALSE; + uint32 kargs[3]; + kargs[0] = 2; + kargs[1] = (uint32) &smp_enabled; + kargs[2] = (uint32) &smp_unmod_drv; + int status = sys$cmkrnl(k_check_smp_enabled, kargs); + if (! $VMS_STATUS_SUCCESS(status)) exit(status); + + if (smp_enabled) + { + if (smp_unmod_drv) + { + if (fatal) + { + exit(VSMP_MSG_UNMOD_DRV); + } + else + { + uint32 status = (VSMP_MSG_UNMOD_DRV & ~STS$M_SEVERITY) | (STS$K_WARNING << STS$V_SEVERITY); + print_msg(status); + } + } + } + else if (fatal) + { + print_msg(VSMP_MSG_VMS_NOT_MULTI); + fprintf(stderr, "\n"); + fprintf(stderr, "VSMP: OpenVMS multiprocessing system image is not loaded.\n"); + fprintf(stderr, " Please change the value of SYSGEN (SYSBOOT) parameter\n"); + fprintf(stderr, " MULTIPROCESSING to 2 and reboot.\n"); + exit(VSMP_MSG_VMS_NOT_MULTI | STS$M_INHIB_MSG); + } + else + { + uint32 status = (VSMP_MSG_VMS_NOT_MULTI & ~STS$M_SEVERITY) | (STS$K_INFO << STS$V_SEVERITY); + print_msg(status); + exit(status | STS$M_INHIB_MSG); + } +} + +/********************************************************************************************** +* Check whether VSMP resident image is loaded and loaded version matches current executable * +**********************************************************************************************/ + +static $DESCRIPTOR(lnm_table, "LNM$SYSTEM_TABLE"); +static $DESCRIPTOR(lnm_name, "VSMP$KERNEL_LOAD_ADDRESS"); + +static void check_resident_image_loaded() +{ + uint32 kargs[1]; + kargs[0] = 0; + int status = sys$cmkrnl(k_check_resident_image_loaded, kargs); + if (! $VMS_STATUS_SUCCESS(status)) exit(status); + if (status != SS$_WASSET) exit(VSMP_MSG_NOT_LOADED); +} + +static void check_resident_image_not_loaded() +{ + uint32 kargs[1]; + kargs[0] = 0; + int status = sys$cmkrnl(k_check_resident_image_loaded, kargs); + if (! $VMS_STATUS_SUCCESS(status)) exit(status); + if (status != SS$_WASCLR) exit(VSMP_MSG_ALREADY_LOADED); +} + +static uint32 k_check_resident_image_loaded() +{ + uint32 status; + ILE3 item_list[3]; + char lnm_value[256]; + uint16 lnm_length = 0; + int lnm_max_index = -1; + + /* + * Address of the loaded kernel-resident code is stored in system-wide logical name. + * Read logical name value. + */ + + ile3_make(&item_list[0], LNM$_STRING, sizeof(lnm_value) - 1, lnm_value, & lnm_length); + ile3_make(&item_list[1], LNM$_MAX_INDEX, 4, &lnm_max_index, NULL); + ile3_term(&item_list[2]); + + status = sys$trnlnm(NULL, &lnm_table, &lnm_name, & PSL$C_KERNEL, &item_list); + if (status == SS$_NOLOGNAM) + return SS$_WASCLR; + + check_vms(status); + + /* logical name does exists, check and parse it */ + if (lnm_max_index != 0 || lnm_length >= sizeof(lnm_value)) + fail(SS$_IVLOGNAM); + lnm_value[lnm_length] = 0; + if (! parse_address(lnm_value, & kaddress)) + fail(SS$_IVLOGNAM); + if (! ((uint32) kaddress >= 0x80000000 && (uint32) kaddress < 0xC0000000)) + fail(SS$_IVLOGNAM); + + /* verify if resident image is compatible with current executable */ + check_vms_status(kload_verify_compatible(kaddress)); + return SS$_WASSET; + +cleanup: + + return status; +} + +/*************************************************************************************** +* Load resident image into the kernel * +***************************************************************************************/ + +/* + * Load resident part into the kernel. + * Returns VMS-structured status. + * On success, sets 'kaddress' variable to the address of the loaded resident part. + * On failure, sets kaddress to NULL. + */ +static uint32 k_load_resident_image() +{ + uint32 status; + ILE3 item_list[3]; + char lnm_value[256]; + uint32 lnm_attributes; + uint32 kload_size; + bool_t did_kmalloc = FALSE; + bool_t did_crelnm = FALSE; + bool_t did_lock_system_pages = FALSE; + bool_t did_disable_delete_process = FALSE; + bool_t did_lock_iodb = FALSE; + uint32 blksize; + char* xp; + int k; + + kaddress = 0; + + /* disable process deletion while we are holding resources */ + kload_set_process_deletable(FALSE); + did_disable_delete_process = TRUE; + + /* lock select system pages in memory (system working set), as required for patches */ + k_lock_system_pages(nopatch); + did_lock_system_pages = TRUE; + + /* extend fork-to-primary pool if required */ + check_vms_status(k_extend_pfork_pool(pfork_pool_pages)); + + /* allocate block from non-paged pool and copy the resident code into it */ + kload_size = &kload_end - &kload_start + 1; + check_vms_status(kmalloc_anysize(kload_size, (void**) & kaddress, & blksize)); + did_kmalloc = TRUE; + + /* copy content into the allocated block, preserve block size in the block header */ + kmemcpy(kaddress, &kload_start, kload_size); + kdata_reloc(uint32, kload_blksize) = blksize; + + /* flush instruction prefetch (obviously excessive, especially for SIMH) */ + kload_flush_instruction_stream(); + + /* store the address of allocated block in system-wide logical name */ + xp = fmt_x8(lnm_value, (uint32) kaddress); + *xp = '\0'; + lnm_attributes = LNM$M_TERMINAL; + ile3_make(&item_list[0], LNM$_ATTRIBUTES, 4, &lnm_attributes, NULL); + ile3_make(&item_list[1], LNM$_STRING, kstrlen(lnm_value), lnm_value, NULL); + ile3_term(&item_list[2]); + check_vms_status(sys$crelnm(&LNM$M_NO_ALIAS, &lnm_table, &lnm_name, &PSL$C_KERNEL, &item_list)); + did_crelnm = TRUE; + + /* any XQDRIVER patches to apply? */ + if ((nopatch & xq_all_patches_mask) != xq_all_patches_mask) + { + /* lock IO database, returns here at IPL ASTDEL */ + if (! did_lock_iodb) + { + iodb_lock_wr(); + did_lock_iodb = TRUE; + } + + /* locate XQDRIVER */ + check_vms_status(locate_xqdriver()); + + /* if XQDRIVER is not loaded, disable all patches for it */ + if (xqdrv_dpt == 0) + { + nopatch |= xq_all_patches_mask; + } + else + { + check_vms_status(prepare_xqdrv_patches()); + } + } + + /* any PUDRIVER patches to apply? */ + if ((nopatch & pu_all_patches_mask) != pu_all_patches_mask) + { + /* lock IO database, returns here at IPL ASTDEL */ + if (! did_lock_iodb) + { + iodb_lock_wr(); + did_lock_iodb = TRUE; + } + + /* locate PUDRIVER */ + check_vms_status(locate_pudriver()); + + /* if PUDRIVER is not loaded, disable all patches for it */ + if (pudrv_dpt == 0) + { + nopatch |= pu_all_patches_mask; + } + else + { + check_vms_status(prepare_pudrv_patches()); + } + } + + /* invoke "onload" handler in the loaded image */ + // kprint_prefix_x8_crlf("** kaddress: ", (uint32) kaddress); + // kprint_prefix_x8_crlf("** kcall_reloc(kcall_onload): ", (uint32) kcall_reloc(kcall_onload_t, kcall_onload)); + status = RCALL(kcall_onload_t, kcall_onload)(simh_cpu_mask, smp_idle, smp_timesync, nopatch); + kerror_cause_usr = kdata_reloc(uint32, kerror_cause); + // kprint_prefix_x8_crlf("** onload status: ", status); + check_vms_status(status); + +cleanup: + + if ($VMS_STATUS_SUCCESS(status) || status == VSMP_MSG_VM_REFUSED) + { + if ((nopatch & xq_all_patches_mask) != xq_all_patches_mask && xqdrv_dpt) + pin_driver(xqdrv_dpt); + + if (0 == (nopatch & (1 << PATCH_ID_XQTIMXMT))) + apply_xqdrv_timxmt_patch(1); + + if ((nopatch & pu_all_patches_mask) != pu_all_patches_mask && pudrv_dpt) + pin_driver(pudrv_dpt); + } + + if (did_lock_iodb) + iodb_unlock(0); + + if (! $VMS_STATUS_SUCCESS(status) && status != VSMP_MSG_VM_REFUSED) + { + if (did_kmalloc) + kfree_anysize(kaddress, blksize); + if (did_crelnm) + sys$dellnm(&lnm_table, &lnm_name, &PSL$C_KERNEL); + if (did_lock_system_pages) + k_unlock_system_pages(nopatch); + kaddress = NULL; + } + + if (did_disable_delete_process) + kload_set_process_deletable(TRUE); + + return status; +} + +/*************************************************************************************** +* Locate XQDRIVER and PUDRIVER if loaded * +***************************************************************************************/ + +static uint32 locate_xqdriver() +{ + void* dpt = locate_driver("XQDRIVER"); + + if (dpt != NULL) + { + globalvalue uint32 dpt$w_size; + + /* store DPT address */ + xqdrv_dpt = dpt; + xqdrv_size = offset_ref(uint16, dpt, dpt$w_size); + } + + return SS$_NORMAL; +} + +static uint32 locate_pudriver() +{ + void* dpt = locate_driver("PUDRIVER"); + + if (dpt != NULL) + { + globalvalue uint32 dpt$w_size; + + /* store DPT address */ + pudrv_dpt = dpt; + pudrv_size = offset_ref(uint16, dpt, dpt$w_size); + } + + return SS$_NORMAL; +} + +static void* locate_driver(const char* drivername) +{ + byte_t namelen = (byte_t) kstrlen(drivername); + void* dpt; + + globalvalue uint32 dpt$l_flink; + globalvalue uint32 dpt$t_name; + globalref uint32 ioc$gl_dptlist; + + /* + * Enumerate DPTs to find the driver + */ + for (dpt = (void*) ioc$gl_dptlist; dpt != &ioc$gl_dptlist; dpt = offset_ref(void*, dpt, dpt$l_flink)) + { + byte_t* p = offset_ptr(byte_t, dpt, dpt$t_name); + if (*p == namelen && k_streqi_cnt((const char*) p + 1, drivername, namelen)) + { + return dpt; + } + } + + return NULL; +} + +/*************************************************************************************** +* Prepare patches for XQDRIVER * +***************************************************************************************/ + +static bool_t xqrxtx_checker(const byte_t* xa, void* arg); + +static uint32 prepare_xqdrv_patches() +{ + uint32 status; + uint32 patch_id; + + if (0 == (nopatch & (1 << PATCH_ID_XQTX1))) + { + EXTERN byte_t xqtx1_look, xqtx1_look_e; + check_vms_status(lookup_in_xqdrv(VSMP_MSG_XQTX1_P, 0x1556, &xqtx1_look, &xqtx1_look_e)); + status = VSMP_MSG_XQTX1_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_XQTX1, lookup_code_addr)); + } + + if (0 == (nopatch & (1 << PATCH_ID_XQTX2))) + { + EXTERN byte_t xqtx2_look, xqtx2_look_e; + check_vms_status(lookup_in_xqdrv(VSMP_MSG_XQTX2_P, 0x1574, &xqtx2_look, &xqtx2_look_e)); + status = VSMP_MSG_XQTX2_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_XQTX2, lookup_code_addr)); + } + + if (0 == (nopatch & (1 << PATCH_ID_XQTX3))) + { + EXTERN byte_t xqtx3_look, xqtx3_look_e, xqtx3_look_mask, xqtx3_look_mask_e; + check_vms_status(lookup_in_xqdrv_ex(VSMP_MSG_XQTX3_P, 0x159A, &xqtx3_look, &xqtx3_look_e, &xqtx3_look_mask, &xqtx3_look_mask_e, NULL, NULL)); + status = VSMP_MSG_XQTX3_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_XQTX3, lookup_code_addr)); + } + + if (0 == (nopatch & (1 << PATCH_ID_XQTX4))) + { + EXTERN byte_t xqtx4_look, xqtx4_look_e, xqtx4_look_mask, xqtx4_look_mask_e; + EXTERN byte_t xqtx4_handler_jmp; + const byte_t* p; + int offset; + + patch_id = PATCH_ID_XQTX4; + /* if lookup fails, one possible cause is that DEVICELOCK size might have changed, so mask size mismatches lookup pattern size */ + check_vms_status(lookup_in_xqdrv_ex(VSMP_MSG_XQTX4_P, 0x15E6, &xqtx4_look, &xqtx4_look_e, &xqtx4_look_mask, &xqtx4_look_mask_e, xqrxtx_checker, &patch_id)); + status = VSMP_MSG_XQTX4_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_XQTX4, lookup_code_addr)); + + /* calculate target of BBC instruction at lookup_code_addr + 37 */ + p = lookup_code_addr; + offset = 0xFF & (uint32) p[41]; + if (offset & 0x80) offset |= 0xFFFFFF00; + + /* store as jump address in replacement patch data */ + *kdata_reloc_ptr(const void**, &xqtx4_handler_jmp + 2) = p + 42 + offset; + } + + if (0 == (nopatch & (1 << PATCH_ID_XQTX5))) + { + EXTERN byte_t xqtx5_look, xqtx5_look_e, xqtx5_look_mask, xqtx5_look_mask_e; + check_vms_status(lookup_in_xqdrv_ex(VSMP_MSG_XQTX5_P, 0xADA, &xqtx5_look, &xqtx5_look_e, &xqtx5_look_mask, &xqtx5_look_mask_e, NULL, NULL)); + status = VSMP_MSG_XQTX5_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_XQTX5, lookup_code_addr)); + } + + if (0 == (nopatch & (1 << PATCH_ID_XQTX6))) + { + EXTERN byte_t xqtx6_look, xqtx6_look_e, xqtx6_look_mask, xqtx6_look_mask_e; + check_vms_status(lookup_in_xqdrv_ex(VSMP_MSG_XQTX6_P, 0xE89, &xqtx6_look, &xqtx6_look_e, &xqtx6_look_mask, &xqtx6_look_mask_e, NULL, NULL)); + status = VSMP_MSG_XQTX6_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_XQTX6, lookup_code_addr)); + } + + if (0 == (nopatch & (1 << PATCH_ID_XQTX7))) + { + EXTERN byte_t xqtx7_look, xqtx7_look_e, xqtx7_look_mask, xqtx7_look_mask_e; + check_vms_status(lookup_in_xqdrv_ex(VSMP_MSG_XQTX7_P, 0x16A2, &xqtx7_look, &xqtx7_look_e, &xqtx7_look_mask, &xqtx7_look_mask_e, NULL, NULL)); + status = VSMP_MSG_XQTX7_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_XQTX7, lookup_code_addr)); + } + + if (0 == (nopatch & (1 << PATCH_ID_XQTX8))) + { + EXTERN byte_t xqtx8_look, xqtx8_look_e, xqtx8_look_mask, xqtx8_look_mask_e; + check_vms_status(lookup_in_xqdrv_ex(VSMP_MSG_XQTX8_P, 0x16A2, &xqtx8_look, &xqtx8_look_e, &xqtx8_look_mask, &xqtx8_look_mask_e, NULL, NULL)); + status = VSMP_MSG_XQTX8_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_XQTX8, lookup_code_addr)); + } + + if (0 == (nopatch & (1 << PATCH_ID_XQTX9))) + { + EXTERN byte_t xqtx9_look, xqtx9_look_e, xqtx9_look_mask, xqtx9_look_mask_e, xqtx9_handler_movl, xqtx9_handler_jmp; + const byte_t* p; + int offset; + + check_vms_status(lookup_in_xqdrv_ex(VSMP_MSG_XQTX9_P, 0x1485, &xqtx9_look, &xqtx9_look_e, &xqtx9_look_mask, &xqtx9_look_mask_e, NULL, NULL)); + status = VSMP_MSG_XQTX9_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_XQTX9, lookup_code_addr)); + kmemcpy(kdata_reloc_ptr(byte_t*, &xqtx9_handler_movl), lookup_code_addr + 10, 9); + + /* calculate target of BBC instruction at lookup_code_addr + 19 */ + p = lookup_code_addr; + offset = 0xFF & (uint32) p[23]; + if (offset & 0x80) offset |= 0xFFFFFF00; + + /* store as jump address in replacement patch data */ + *kdata_reloc_ptr(const void**, &xqtx9_handler_jmp + 2) = p + 24 + offset; + } + + if (0 == (nopatch & (1 << PATCH_ID_XQTX10))) + { + EXTERN byte_t xqtx10_look, xqtx10_look_e, xqtx10_look_mask, xqtx10_look_mask_e; + /* if lookup fails, one possible cause is that DEVICELOCK size might have changed, so mask size mismatches lookup pattern size */ + check_vms_status(lookup_in_xqdrv_ex(VSMP_MSG_XQTX10_P, 0xEF3, &xqtx10_look, &xqtx10_look_e, &xqtx10_look_mask, &xqtx10_look_mask_e, NULL, NULL)); + status = VSMP_MSG_XQTX10_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_XQTX10, lookup_code_addr)); + } + + + if (0 == (nopatch & (1 << PATCH_ID_XQRX1))) + { + EXTERN byte_t xqrx1_look, xqrx1_look_e, xqrx1_look_mask, xqrx1_look_mask_e; + patch_id = PATCH_ID_XQRX1; + check_vms_status(lookup_in_xqdrv_ex(VSMP_MSG_XQRX1_P, 0x12EB, &xqrx1_look, &xqrx1_look_e, &xqrx1_look_mask, &xqrx1_look_mask_e, xqrxtx_checker, &patch_id)); + status = VSMP_MSG_XQRX1_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_XQRX1, lookup_code_addr)); + } + + if (0 == (nopatch & (1 << PATCH_ID_XQRX2))) + { + EXTERN byte_t xqrx2_look, xqrx2_look_e, xqrx2_look_mask, xqrx2_look_mask_e; + check_vms_status(lookup_in_xqdrv_ex(VSMP_MSG_XQRX2_P, 0xADA, &xqrx2_look, &xqrx2_look_e, &xqrx2_look_mask, &xqrx2_look_mask_e, NULL, NULL)); + status = VSMP_MSG_XQRX2_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_XQRX2, lookup_code_addr)); + } + + if (0 == (nopatch & (1 << PATCH_ID_XQRX3))) + { + EXTERN byte_t xqrx3_look, xqrx3_look_e, xqrx3_look_mask, xqrx3_look_mask_e; + check_vms_status(lookup_in_xqdrv_ex(VSMP_MSG_XQRX3_P, 0xCDB, &xqrx3_look, &xqrx3_look_e, &xqrx3_look_mask, &xqrx3_look_mask_e, NULL, NULL)); + status = VSMP_MSG_XQRX3_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_XQRX3, lookup_code_addr)); + } + + if (0 == (nopatch & (1 << PATCH_ID_XQRX4))) + { + EXTERN byte_t xqrx4_look, xqrx4_look_e, xqrx4_look_mask, xqrx4_look_mask_e, xqrx4_handler_bicb; + check_vms_status(lookup_in_xqdrv_ex(VSMP_MSG_XQRX4_P, 0x1307, &xqrx4_look, &xqrx4_look_e, &xqrx4_look_mask, &xqrx4_look_mask_e, NULL, NULL)); + status = VSMP_MSG_XQRX4_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_XQRX4, lookup_code_addr)); + kmemcpy(kdata_reloc_ptr(byte_t*, &xqrx4_handler_bicb), lookup_code_addr + 24, 7); + } + + if (0 == (nopatch & (1 << PATCH_ID_XQTIMXMT))) + { + check_vms_status(prepare_xqdrv_timxmt_patch()); + check_vms_status(apply_xqdrv_timxmt_patch(0)); + } + + return SS$_NORMAL; + +cleanup: + + return status; +} + +static bool_t xqrxtx_checker(const byte_t* xa, void* arg) +{ + uint32 patch_id = * (uint32*) arg; + + if (patch_id == PATCH_ID_XQTX4) + { + /* check BBC and BBS point to the same target address + and BNEQ also points to the same address */ + return ((byte_t)(xa[41] - xa[46]) == 5 && + (byte_t)(xa[46] - xa[52]) == 6); + } + else if (patch_id == PATCH_ID_XQRX1) + { + /* check second BBC and BNEQ point to the same target address */ + return ((byte_t)(xa[27] - xa[20]) == 0xF9); + } + else + { + return FALSE; + } +} + +/*************************************************************************************** +* Prepare patch for XQDRIVER XMT timeout * +***************************************************************************************/ + +/* + * Entered and left at IPL ASTDEL with IODB mutex held. + */ +static uint32 prepare_xqdrv_timxmt_patch() +{ + const uint32 pat_2_startoff = 0x1B02; + int max_before; + int max_after; + const byte_t* pat1_addr; + byte_t pat1[7]; + uint32 status; + + EXTERN byte_t xqdrv_pat_1; + EXTERN byte_t xqdrv_pat_2; + EXTERN byte_t xqdrv_pat_2e; + const byte_t* pxqdrv_pat_1 = &xqdrv_pat_1; + + /* + * Locate instruction that sets timeout in XQ LSB. + * Begin by searching code pattern located in XQDRIVER's routine SUB_START_CTRL_TIMER (module [PHV_LAN.SRC]DEQNA.MAR). + */ + CHECK(pat_2_startoff + (&xqdrv_pat_2e - &xqdrv_pat_2) + 4 < xqdrv_size); + + max_before = 0x800; + max_after = xqdrv_size - (&xqdrv_pat_2e - &xqdrv_pat_2) - 4; + if (max_after > 0x800) + max_after = 0x800; + check_vms_status(lookup_code(offset_ptr(byte_t, xqdrv_dpt, pat_2_startoff), max_before, max_after, &xqdrv_pat_2, &xqdrv_pat_2e)); + + /* + * Back off from XQDRV_PAT_2 to XQDRV_PAT_1 and see if active XQDRIVER code + * there contains expected instruction MOVB S^#VAL, W^OFFSET(R4) + */ + pat1_addr = lookup_code_addr - (&xqdrv_pat_2 - &xqdrv_pat_1); + check_vms_status(safe_read(pat1_addr, pat1, 5)); + CHECK(pat1[0] == pxqdrv_pat_1[0]); + CHECK(pat1[2] == pxqdrv_pat_1[2]); + + /* extract and store value of original timeout (5 seconds in VMS 7.3) */ + xqdrv_orig_xmt_tmo = pat1[1]; + + /* store address of XQDRIVER MOVB instruction that sets timeout */ + xqdrv_addr_xmt_tmo = (void*) pat1_addr; + + /* store the value of LSB$G_QNA_TIMXMT (offset into LSB) */ + xqdrv_lsb_xmt_tmo = (((uint32) pat1[4]) << 8) | (pat1[3]); + CHECK(xqdrv_lsb_xmt_tmo); + xqdrv_lsb_xmt_tmo--; + CHECK(xqdrv_lsb_xmt_tmo >= 0x20 && xqdrv_lsb_xmt_tmo <= 0xFFF0); + + /* + * Next instruction should be MOVW #VAL, W^OFFSET2(R4) + */ + check_vms_status(safe_read(pat1_addr + 5, pat1, 7)); + CHECK(pat1[0] == pxqdrv_pat_1[5 + 0]); + CHECK(pat1[1] == pxqdrv_pat_1[5 + 1]); + CHECK(pat1[4] == pxqdrv_pat_1[5 + 4]); + + return SS$_NORMAL; + +cleanup: + xqdrv_orig_xmt_tmo = 0; + xqdrv_addr_xmt_tmo = NULL; + xqdrv_lsb_xmt_tmo = 0; + return VSMP_MSG_XQTIMXMT_P; +} + +/*************************************************************************************** +* Apply patch for XQDRIVER XMT timeout * +***************************************************************************************/ + +/* + * Entered and left at IPL ASTDEL with IODB mutex held. + * Called two times. Sequence: + * + * prepare_xqdrv_timxmt_patch(); + * apply_xqdrv_timxmt_patch(stage = 0); + * kcall_onload + * apply_xqdrv_timxmt_patch(stage = 1); + * + * At stage 0 perform pre-patch validation. + * + * At stage 1 perform data modification. + * When called for stage 1, returned status value is ignored. Must not fail. + * + */ +static uint32 apply_xqdrv_timxmt_patch(int stage) +{ + void* sb; + void* ddb; + void* ucb; + void* crb; + void* lsb; + uint32 status; + int pass; + byte_t curr_xmt_tmo; + + globalvalue uint32 sb$l_ddb; + globalvalue uint32 ddb$l_link; + globalvalue uint32 ddb$l_ucb; + globalvalue uint32 ucb$b_type; + globalvalue uint32 ucb$w_size; + globalvalue uint32 ucb$b_devtype; + globalvalue uint32 ucb$l_crb; + globalvalue uint32 ddb$b_drvnam_len; + globalvalue uint32 ddb$t_drvnam_str; + globalvalue uint32 crb$l_auxstruc; + globalvalue uint32 DYN$C_CDB; + + /* if XQDRIVER is not loaded, nothing to do */ + if (xqdrv_dpt == NULL) + return SS$_NORMAL; + + if (xqtimeout < xqdrv_orig_xmt_tmo) + xqtimeout = xqdrv_orig_xmt_tmo; + if (xqtimeout > 255) + xqtimeout = 255; + + /* get SB for local system */ + sb = get_localsb_addr(); + + if (stage == 1) + { + /* disable driver unloading -- already done by the caller */ + // pin_driver(xqdrv_dpt); + + /* + * Patch XQDRIVER instructions. + * + * MOVB instruction that sets timeout value uses immed short operand (S^#5) and is too short + * to be patched in place, therefore we need to replace two insructions by JSB to a resident + * location, for this we need to elevate IPL to POWER, but this code is pageable, so we need to + * call non-pageable code to do this. + */ + RCALL(patch_xqdrv_instr_t, patch_xqdrv_instr)(xqdrv_addr_xmt_tmo, xqtimeout); + } + + /* scan all local devices in the system that are connected to XQDRIVER */ + for (ddb = offset_ref(void*, sb, sb$l_ddb); ddb != NULL; ddb = offset_ref(void*, ddb, ddb$l_link)) + { + byte_t* pname = offset_ptr(byte_t, ddb, ddb$b_drvnam_len); + if (*pname == 8 && k_streqi_cnt((const char*) pname + 1, "XQDRIVER", 8)) + { + CHECK(ucb = offset_ref(void*, ddb, ddb$l_ucb)); + if (stage == 0) + { + /* check device is DEQNA or DELQA, not DEQTA */ + if (offset_ref(byte_t, ucb, ucb$b_devtype) != DT$_DEQNA && + offset_ref(byte_t, ucb, ucb$b_devtype) != DT$_XQ_DELQA) + { + return VSMP_MSG_UNSUPPXQ; + } + /* LSB address is stored in CRB$_AUXSTRUC */ + CHECK(crb = offset_ref(void*, ucb, ucb$l_crb)); + if (lsb = offset_ref(void*, crb, crb$l_auxstruc)) + { + CHECK(offset_ref(byte_t, lsb, ucb$b_type) == DYN$C_CDB); + CHECK(offset_ref(uint16, lsb, ucb$w_size) >= xqdrv_lsb_xmt_tmo + 2); + curr_xmt_tmo = offset_ref(byte_t, lsb, xqdrv_lsb_xmt_tmo + 1); + if (curr_xmt_tmo) + { + CHECK(curr_xmt_tmo == xqdrv_orig_xmt_tmo); + if (xqtimeout < curr_xmt_tmo) + xqtimeout = curr_xmt_tmo; + } + else + { + /* This LSB has not been initialized yet, skip it. */ + } + } + } + else + { + set_xq_xmt_timeout(ucb, xqdrv_lsb_xmt_tmo, xqtimeout); + } + } + } + + return SS$_NORMAL; + +cleanup: + + return VSMP_MSG_XQTIMXMT_C; +} + +/*************************************************************************************** +* Prepare patches for PUDRIVER * +***************************************************************************************/ + +static uint32 prepare_pudrv_patches() +{ + uint32 status; + uint32 patch_id; + + if (0 == (nopatch & (1 << PATCH_ID_PU1))) + { + EXTERN byte_t pu1_look, pu1_look_e, pu1_look_mask, pu1_look_mask_e; + /* if lookup fails, one possible cause is that READ_CSR size might have changed, so mask size mismatches lookup pattern size */ + check_vms_status(lookup_in_pudrv_ex(VSMP_MSG_PU1_P, 0x2D40, &pu1_look, &pu1_look_e, &pu1_look_mask, &pu1_look_mask_e, NULL, NULL)); + status = VSMP_MSG_PU1_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_PU1, lookup_code_addr)); + RCALL(setup_patch_t, setup_patch_pu1)(lookup_code_addr); + } + + if (0 == (nopatch & (1 << PATCH_ID_PU2))) + { + EXTERN byte_t pu2_look, pu2_look_e, pu2_look_mask, pu2_look_mask_e; + check_vms_status(lookup_in_pudrv_ex(VSMP_MSG_PU2_P, 0x2842, &pu2_look, &pu2_look_e, &pu2_look_mask, &pu2_look_mask_e, NULL, NULL)); + status = VSMP_MSG_PU2_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_PU2, lookup_code_addr)); + RCALL(setup_patch_t, setup_patch_pu2)(lookup_code_addr); + } + + if (0 == (nopatch & (1 << PATCH_ID_PU3))) + { + EXTERN byte_t pu3_look, pu3_look_e, pu3_look_mask, pu3_look_mask_e; + check_vms_status(lookup_in_pudrv_ex(VSMP_MSG_PU3_P, 0x3066, &pu3_look, &pu3_look_e, &pu3_look_mask, &pu3_look_mask_e, NULL, NULL)); + status = VSMP_MSG_PU3_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_PU3, lookup_code_addr)); + RCALL(setup_patch_t, setup_patch_pu3)(lookup_code_addr); + } + + if (0 == (nopatch & (1 << PATCH_ID_PU4))) + { + EXTERN byte_t pu4_look, pu4_look_e, pu4_look_mask, pu4_look_mask_e; + check_vms_status(lookup_in_pudrv_ex(VSMP_MSG_PU4_P, 0x3176, &pu4_look, &pu4_look_e, &pu4_look_mask, &pu4_look_mask_e, NULL, NULL)); + status = VSMP_MSG_PU4_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_PU4, lookup_code_addr)); + RCALL(setup_patch_t, setup_patch_pu4)(lookup_code_addr); + } + + if (0 == (nopatch & (1 << PATCH_ID_PU5))) + { + EXTERN byte_t pu5_look, pu5_look_e, pu5_look_mask, pu5_look_mask_e; + check_vms_status(lookup_in_pudrv_ex(VSMP_MSG_PU5_P, 0x2CFC, &pu5_look, &pu5_look_e, &pu5_look_mask, &pu5_look_mask_e, NULL, NULL)); + status = VSMP_MSG_PU5_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_PU5, lookup_code_addr)); + RCALL(setup_patch_t, setup_patch_pu5)(lookup_code_addr); + } + + if (0 == (nopatch & (1 << PATCH_ID_PU6))) + { + EXTERN byte_t pu6_look, pu6_look_e, pu6_look_mask, pu6_look_mask_e; + check_vms_status(lookup_in_pudrv_ex(VSMP_MSG_PU6_P, 0x294B, &pu6_look, &pu6_look_e, &pu6_look_mask, &pu6_look_mask_e, NULL, NULL)); + status = VSMP_MSG_PU6_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_PU6, lookup_code_addr)); + RCALL(setup_patch_t, setup_patch_pu6)(lookup_code_addr); + } + + if (0 == (nopatch & (1 << PATCH_ID_PU7))) + { + EXTERN byte_t pu7_look, pu7_look_e, pu7_look_mask, pu7_look_mask_e; + check_vms_status(lookup_in_pudrv_ex(VSMP_MSG_PU7_P, 0x29DA, &pu7_look, &pu7_look_e, &pu7_look_mask, &pu7_look_mask_e, NULL, NULL)); + status = VSMP_MSG_PU7_P; + CHECK(RCALL(set_patchdesc_lookup_found_t, set_patchdesc_lookup_found)(PATCH_ID_PU7, lookup_code_addr)); + RCALL(setup_patch_t, setup_patch_pu7)(lookup_code_addr); + } + + return SS$_NORMAL; + +cleanup: + + return status; +} + +/*************************************************************************************** +* Lock non-paged code into working set * +***************************************************************************************/ + +static void lock_nonpaged() +{ + VA_RANGE va_range; + uint32 status; + + /* lock page(s) of loader code that is invoked at elevated IPL */ + va_range.start = &kload_wslock_start; + va_range.end = &kload_wslock_end; + check_vms_status(sys$lkwset(&va_range, NULL, NULL)); + return; + +cleanup: + exit(status); +} + +/*************************************************************************************** +* Verify image linking integrity * +***************************************************************************************/ + +#define check_kloadable(x) CHECK((void*) (&(x)) >= (void*) &kload_start && (void*) (&(x)) <= (void*) &kload_end) +#define check_kloadable_routine(x) CHECK((void*) (x) >= (void*) &kload_start && (void*) (x) <= (void*) &kload_end) + +static void verify_linking_integrity() +{ + EXTERN byte_t timesync_ctrl; + EXTERN uint32 activate_timesync_tqe; + + EXTERN uint32 vcon$old_putchar; + EXTERN uint32 vcon$old_getchar; + EXTERN uint32 vc_sv_txcs; + EXTERN uint32 vc_sv_rxcs; + EXTERN uint32 vc_sv_ipl; + EXTERN uint32 vcreg_txdb; + EXTERN uint16 vcreg_txie; + EXTERN uint16 vcreg_txdone; + EXTERN uint32 vcreg_rxdb; + EXTERN uint16 vcreg_rxie; + EXTERN uint16 vcreg_rxdone; + + /* declaration of routine addresses only */ + void vsmp$intproc(); + void vsmp$intall(); + void vsmp$intall_bit(); + void vsmp$intall_acq(); + void vsmp$intall_bit_acq(); + void vsmp$stop_cpu(); + void vsmp$show_cpu(); + void vsmp$halt_cpu(); + void vsmp$controlp_cpus(); + void vsmp$invalid_single(); + void vsmp$select_primary(); + void vsmp$setup_smp(); + void vsmp$read_todr(); + void vsmp$write_todr(); + void vsmp$intsr(); + void vsmp$spec_ipint(); + void vsmp$iniprocreg(); + void exe_clear_errors(); + void vsmp$start_cpu(); + void vsmp$setup_cpu(); + void vcon$putchar(); + void vcon$getchar(); + void vcon$owncty(); + void vcon$releasecty(); + void vsmp$virtcons_server(); + + check_kloadable(kerror_cause); + check_kloadable(timesync_ctrl); + check_kloadable(activate_timesync_tqe); + check_kloadable(vcon$old_putchar); + check_kloadable(vcon$old_getchar); + + check_kloadable(vc_sv_txcs); + check_kloadable(vc_sv_rxcs); + check_kloadable(vc_sv_ipl); + check_kloadable(vcreg_txdb); + check_kloadable(vcreg_txie); + check_kloadable(vcreg_txdone); + check_kloadable(vcreg_rxdb); + check_kloadable(vcreg_rxie); + check_kloadable(vcreg_rxdone); + + check_kloadable_routine(kcall_onload); + check_kloadable_routine(kcall_get_idle); + check_kloadable_routine(kcall_set_idle); + check_kloadable_routine(kprint); + check_kloadable_routine(kcall_get_timesync); + check_kloadable_routine(kcall_set_timesync); + check_kloadable_routine(set_patchdesc_lookup_found); + check_kloadable_routine(patch_xqdrv_instr); + check_kloadable_routine(setup_patch_pu1); + check_kloadable_routine(setup_patch_pu2); + check_kloadable_routine(setup_patch_pu3); + check_kloadable_routine(setup_patch_pu4); + check_kloadable_routine(setup_patch_pu5); + check_kloadable_routine(setup_patch_pu6); + check_kloadable_routine(setup_patch_pu7); + + check_kloadable_routine(vsmp$intproc); + check_kloadable_routine(vsmp$intall); + check_kloadable_routine(vsmp$intall_bit); + check_kloadable_routine(vsmp$intall_acq); + check_kloadable_routine(vsmp$intall_bit_acq); + check_kloadable_routine(vsmp$stop_cpu); + check_kloadable_routine(vsmp$show_cpu); + check_kloadable_routine(vsmp$halt_cpu); + check_kloadable_routine(vsmp$controlp_cpus); + check_kloadable_routine(vsmp$invalid_single); + check_kloadable_routine(vsmp$select_primary); + check_kloadable_routine(vsmp$setup_smp); + check_kloadable_routine(vsmp$read_todr); + check_kloadable_routine(vsmp$write_todr); + check_kloadable_routine(vsmp$intsr); + check_kloadable_routine(vsmp$spec_ipint); + check_kloadable_routine(vsmp$iniprocreg); + check_kloadable_routine(exe_clear_errors); + check_kloadable_routine(vsmp$start_cpu); + check_kloadable_routine(vsmp$setup_cpu); + check_kloadable_routine(vcon$putchar); + check_kloadable_routine(vcon$getchar); + check_kloadable_routine(vcon$owncty); + check_kloadable_routine(vcon$releasecty); + check_kloadable_routine(vsmp$virtcons_server); + + return; + +cleanup: + exit(VSMP_MSG_MISLINKED); +} + +#undef check_kloadable +#undef check_kloadable_routine + +/*************************************************************************************** +* Acquire/Release lock preventing concurrent execution of other VSMP instances * + * on this node * +***************************************************************************************/ + +static bool_t enq_held = FALSE; /* set if holding ENQ lock */ +static LKSB lksb; +static uint32 exit_handler_blk[4]; +uint32 exit_status; + +static void acquire_node_lock() +{ + ILE3 item_list[3]; + IOSB iosb; + DESC desc; + byte_t sysid[6]; + uint16 retlen; + int status = SS$_ABORT; + char* xp; + char lockname[sizeof("VSMP$VAXMP_KLOAD_112233445566") + 4]; + + /* + * declare exit handler to release lock: this is redundant in user mode, + * as user-mode locks will be released on image rundown + */ + exit_handler_blk[0] = 0; + exit_handler_blk[1] = (uint32) exit_handler; + exit_handler_blk[2] = 1; + exit_handler_blk[3] = (uint32) & exit_status; + sys$dclexh(exit_handler_blk); + + /* get node ID */ + ile3_make(&item_list[0], SYI$_NODE_SYSTEMID, 6, sysid, & retlen); + ile3_term(&item_list[1]); + + check_vms_status(sys$getsyiw(0, NULL, NULL, &item_list, &iosb, NULL, 0)); + check_vms_status(iosb.iosb$w_status); + check(retlen == 6); + + /* build lock name and acquire the lock */ + xp = fmt_s(lockname, "VSMP$VAXMP_KLOAD_"); + xp = fmt_x2(xp, sysid[5]); + xp = fmt_x2(xp, sysid[4]); + xp = fmt_x2(xp, sysid[3]); + xp = fmt_x2(xp, sysid[2]); + xp = fmt_x2(xp, sysid[1]); + xp = fmt_x2(xp, sysid[0]); + *xp = '\0'; + mkdesc(&desc, lockname); + check_vms_status(sys$enqw(0, LCK$K_EXMODE, &lksb, LCK$M_SYSTEM, &desc, 0, NULL, 0, NULL, PSL$C_KERNEL, 0, NULL)); + check_vms_status(lksb.lksb$w_status); + + enq_held = TRUE; + + return; + +cleanup: + exit(status); +} + +static void exit_handler(uint32 status) +{ + release_node_lock(); +} + +static void release_node_lock() +{ + if (enq_held) + { + sys$deq(lksb.lksb$l_lock_id, NULL, PSL$C_KERNEL, 0); + enq_held = FALSE; + } +} + +/*************************************************************************************** +* Utility routines * +***************************************************************************************/ + +/* try to match command line argument to a known keyword */ +static bool_t is_keyword(const char* s, const char* kwd, int len) +{ + int k; + + if (len == 0) + { + len = strlen(kwd); + } + else if (len < 0) + { + len = strlen(kwd); + if (len != strlen(s)) + return FALSE; + } + + for (k = 0; ; k++) + { + if (s[k] == '\0' && kwd[k] == '\0') break; + if (s[k] == '\0' && k >= len) break; + if (toupper(s[k]) != toupper(kwd[k])) + return FALSE; + } + + return TRUE; +} + +static bool_t split_key_value(const char* arg, char* key, size_t keysize, char* value, size_t valuesize) +{ + char* xp = key; + const char* p = arg; + + while (*p && *p != '=') + { + if (xp - key == keysize - 1) + return FALSE; + *xp++ = *p++; + } + *xp = '\0'; + + if (*p++ != '=') + return FALSE; + + xp = value; + while (*p) + { + if (xp - value == valuesize - 1) + return FALSE; + *xp++ = *p++; + } + *xp = '\0'; + + return TRUE; +} + +/* try to match command line argument to a known qualifier and get its value (if any) */ +static bool_t is_qualifier(const char* s, const char* kwd, int len, const char** pqvalue) +{ + int k; + + *pqvalue = NULL; + + if (*s++ != '/') + return FALSE; + + for (k = 0; ; k++) + { + if (s[k] == '\0' || s[k] == '=') + { + if (kwd[k] == '\0' || k >= len) break; + } + if (toupper(s[k]) != toupper(kwd[k])) + return FALSE; + } + + if (s[k] == '\0') + return TRUE; + + if (s[k] != '=') + return FALSE; + + if (s[k + 1] == '\0') + return FALSE; + + *pqvalue = & s[k + 1]; + return TRUE; +} + +static void inv_opt_val(const char* optname) +{ + print_msg_1(VSMP_MSG_INVOPTVAL, (uint32) optname); + exit(VSMP_MSG_INVOPTVAL | STS$M_INHIB_MSG); +} + +static void print_msg(uint32 status) +{ + uint32 msgvec[] = { 0x000F0002, status, 0x000F0000 }; + sys$putmsg(msgvec); +} + +static void print_msg_1(uint32 status, uint32 arg) +{ + uint32 nargs = 1; + uint32 msgvec[] = { 0x000F0000 | (nargs + 2), status, 0x000F0000 | nargs, arg }; + sys$putmsg(msgvec); +} + +static void print_2msgs(uint32 st1, uint32 st2) +{ + uint32 msgvec[] = { 0x000F0004, st1, 0x000F0000, st2, 0x000F0000 }; + sys$putmsg(msgvec); +} + +static void print_3msgs_val(uint32 st1, uint32 st2, uint32 st3, uint32 val) +{ + uint32 nargs = 1; + uint32 msgvec[] = { 0x000F0000 | (nargs + 6), st1, 0x000F0000, st2, 0x000F0000, st3, 0x000F0000 | nargs, val }; + sys$putmsg(msgvec); +} + +static bool_t parse_address(const char* sa, byte_t** paddr) +{ + int k, status; + uint32 res = 0; + check(kstrlen(sa) == 8); + unsigned char c; + while (c = (unsigned char) *sa++) + { + if (c >= '0' && c <= '9') + res = (res << 4) + (c - '0'); + else if (c >= 'A' && c <= 'F') + res = (res << 4) + (c - 'A') + 10; + else if (c >= 'a' && c <= 'f') + res = (res << 4) + (c - 'a') + 10; + else + check(FALSE); + } + + *paddr = (byte_t*) res; + return TRUE; + +cleanup: + *paddr = NULL; + return FALSE; +} + +static int kstrlen(const char* p) +{ + const char* ep = p; + while (*ep++) ; + return ep - p - 1; +} + +static char* fmt_s(char* bp, const char* value) +{ + while (*bp++ = *value++) + ; + return bp; +} + +const static char* xdigits = "0123456789ABCDEF"; + +static char* fmt_x2(char* bp, uint32 value) +{ + *bp++ = xdigits[(value >> 4) & 0xF]; + *bp++ = xdigits[value & 0xF]; + return bp; +} + +static char* fmt_x8(char* bp, uint32 value) +{ + bp = fmt_x2(bp, (value >> 24) & 0xFF); + bp = fmt_x2(bp, (value >> 16) & 0xFF); + bp = fmt_x2(bp, (value >> 8) & 0xFF); + bp = fmt_x2(bp, value & 0xFF); + return bp; +} + +static void kprint_prefix_x8_crlf(const char* prefix, uint32 value) +{ + char buf[9]; + fmt_x8(buf, value); + buf[8] = '\0'; + kprint(prefix); + kprint_crlf(buf); +} + +/* uppercase the string */ +static void k_strupr(char *s) +{ + char c; + while (c = *s) + { + if (c >= 'a' && c <= 'z') + c += 'A' - 'a'; + *s++ = c; + } +} + +/* convert "v" into output buffer as nul-terminated decimal string */ +static void k_itoa(char* s, int v) +{ + if (v == 0) + { + *s++ = '0'; + } + else + { + char buf[20]; + char* bp = buf; + while (v) + { + *bp++ = '0' + (v % 10); + v /= 10; + } + bp--; + while (bp >= buf) + { + *s++ = *bp--; + } + + } + *s++ = '\0'; +} + +static bool_t k_streq(const char* s1, const char* s2) +{ + for (;;) + { + if (*s1 == '\0' && *s2 == '\0') + return TRUE; + if (*s1++ != *s2++) + return FALSE; + } +} + +/* + * This function is called by MACRO code + * + * Compare string "s1" and "s2" upto "len" characters. + */ +bool_t k_streqi_cnt(const char* s1, const char* s2, int len) +{ + int k; + + for (k = 0; k < len; k++) + { + char c1 = *s1++; + char c2 = *s2++; + if (c1 >= 'a' && c1 <= 'z') c1 = c1 - 'a' + 'A'; + if (c2 >= 'a' && c2 <= 'z') c2 = c2 - 'a' + 'A'; + if (c1 != c2) return FALSE; + if (c1 == 0) return TRUE; + } + + return TRUE; +} + +/* parse unsigned decimal number */ +static uint32 parse_udec(const char* sv, const char* optname) +{ + uint32 res = 0; + uint32 ores = 0; + char c; + + CHECK(*sv); + while (c = *sv++) + { + CHECK(c >= '0' && c <= '9'); + CHECK(res <= 0xFFFFFFFF / 10); + res *= 10; + ores = res; + res += c - '0'; + CHECK(res >= ores); + } + + return res; + +cleanup: + inv_opt_val(optname); +} + +/* + * This function is called by MACRO code + * + * Look up code identified by pattern in range "pstart ... pend" around "xaddr", + * limited to offsets upto "max_before" and "max_after" bytes around "xaddr". + */ +uint32 lookup_code(const byte_t* xaddr, int max_before, int max_after, const byte_t* pstart, const byte_t* pend) +{ + int maxoff; + int off; + + maxoff = max_before; + if (max_after > maxoff) maxoff = max_after; + + if ($VMS_STATUS_SUCCESS(lookup_code_match(xaddr, pstart, pend))) + { + lookup_code_addr = xaddr; + return SS$_NORMAL; + } + + for (off = 1; off <= maxoff; off++) + { + if (off <= max_before && $VMS_STATUS_SUCCESS(lookup_code_match(xaddr - off, pstart, pend))) + { + lookup_code_addr = xaddr - off; + return SS$_NORMAL; + } + + if (off <= max_after && $VMS_STATUS_SUCCESS(lookup_code_match(xaddr + off, pstart, pend))) + { + lookup_code_addr = xaddr + off; + return SS$_NORMAL; + } + } + + return SS$_IDMISMATCH; +} + +static uint32 lookup_code_match(const byte_t* xaddr, const byte_t* pstart, const byte_t* pend) +{ + lib$establish(accvio_handler); + while (pstart != pend) + { + if (*pstart++ != *xaddr++) + return SS$_IDMISMATCH; + } + lib$revert(); + return SS$_NORMAL; +} + +/* + * lookup_code_ex is similar to lookup_code, however it will ignore difference in pattern bytes indicated + * by corresponding mask byte != 0, and will also require "checker" routine to confirm potential match. + */ +uint32 lookup_code_ex(const byte_t* xaddr, int max_before, int max_after, const byte_t* pstart, const byte_t* pend, + const byte_t* mask, const byte_t* mask_e, lookup_code_checker_t checker, void* checker_arg) +{ + int maxoff; + int off; + + /* check mask size validity */ + if (mask && mask_e && mask_e - mask != pend - pstart) + return SS$_IDMISMATCH; + + maxoff = max_before; + if (max_after > maxoff) maxoff = max_after; + + if ($VMS_STATUS_SUCCESS(lookup_code_match_ex(xaddr, pstart, pend, mask, checker, checker_arg))) + { + lookup_code_addr = xaddr; + return SS$_NORMAL; + } + + for (off = 1; off <= maxoff; off++) + { + if (off <= max_before && $VMS_STATUS_SUCCESS(lookup_code_match_ex(xaddr - off, pstart, pend, mask, checker, checker_arg))) + { + lookup_code_addr = xaddr - off; + return SS$_NORMAL; + } + + if (off <= max_after && $VMS_STATUS_SUCCESS(lookup_code_match_ex(xaddr + off, pstart, pend, mask, checker, checker_arg))) + { + lookup_code_addr = xaddr + off; + return SS$_NORMAL; + } + } + + return SS$_IDMISMATCH; +} + +static uint32 lookup_code_match_ex(const byte_t* xaddr, const byte_t* pstart, const byte_t* pend, + const byte_t* mask, lookup_code_checker_t checker, void* checker_arg) +{ + const byte_t* xa = xaddr; + lib$establish(accvio_handler); + while (pstart != pend) + { + if ((mask == NULL || *mask == 0) && *pstart != *xaddr) + return SS$_IDMISMATCH; + pstart++; + xaddr++; + if (mask) mask++; + } + if (checker && !(*checker)(xa, checker_arg)) + return SS$_IDMISMATCH; + lib$revert(); + return SS$_NORMAL; +} + +static uint32 safe_read(const byte_t* addr, byte_t* pvalue, uint32 count) +{ + lib$establish(accvio_handler); + while (count--) + *pvalue++ = *addr++; + lib$revert(); + return SS$_NORMAL; +} + + +static uint32 accvio_handler(struct chf$signal_array* sig, struct chf$mech_array* mech) +{ + if (sig->chf$l_sig_name == SS$_ACCVIO) + { + return lib$sig_to_ret(sig, mech); + } + else + { + return SS$_RESIGNAL; + } +} + +/* + * Lookup helpers for the code patterns in XQDRIVER, PUDRIVER and any driver + */ +uint32 lookup_in_xqdrv(uint32 errstatus, int initial, const byte_t* pstart, const byte_t* pend) +{ + return lookup_in_drv(xqdrv_dpt, xqdrv_size, errstatus, initial, pstart, pend); +} + +uint32 lookup_in_xqdrv_ex(uint32 errstatus, int initial, const byte_t* pstart, const byte_t* pend, + const byte_t* mask, const byte_t* mask_e, lookup_code_checker_t checker, void* checker_arg) +{ + return lookup_in_drv_ex(xqdrv_dpt, xqdrv_size, errstatus, initial, pstart, pend, mask, mask_e, checker, checker_arg); +} + +uint32 lookup_in_pudrv(uint32 errstatus, int initial, const byte_t* pstart, const byte_t* pend) +{ + return lookup_in_drv(pudrv_dpt, pudrv_size, errstatus, initial, pstart, pend); +} + +uint32 lookup_in_pudrv_ex(uint32 errstatus, int initial, const byte_t* pstart, const byte_t* pend, + const byte_t* mask, const byte_t* mask_e, lookup_code_checker_t checker, void* checker_arg) +{ + return lookup_in_drv_ex(pudrv_dpt, pudrv_size, errstatus, initial, pstart, pend, mask, mask_e, checker, checker_arg); +} + +uint32 lookup_in_drv(void* dpt, uint32 drv_size, uint32 errstatus, int initial, const byte_t* pstart, const byte_t* pend) +{ + return lookup_in_drv_ex(dpt, drv_size, errstatus, initial, pstart, pend, NULL, NULL, NULL, NULL); +} + +uint32 lookup_in_drv_ex(void* dpt, uint32 drv_size, uint32 errstatus, int initial, const byte_t* pstart, const byte_t* pend, + const byte_t* mask, const byte_t* mask_e, lookup_code_checker_t checker, void* checker_arg) +{ + int max_before = 0x800; + int max_after = 0x800; + int patlen = pend - pstart; + uint32 status; + + if (mask && mask_e && mask_e - mask != patlen) + return errstatus; + + CHECK(dpt && drv_size && drv_size >= patlen); + if (initial + patlen > drv_size) + { + initial = drv_size - patlen; + CHECK(initial >= 0); + } + + max_after = imin(max_after, drv_size - (initial + patlen)); + max_before = imin(max_before, initial); + if (mask == NULL && checker == NULL) + check_vms_status(lookup_code(offset_ptr(byte_t, dpt, initial), max_before, max_after, pstart, pend)); + else + check_vms_status(lookup_code_ex(offset_ptr(byte_t, dpt, initial), max_before, max_after, pstart, pend, mask, mask_e, checker, checker_arg)); + return SS$_NORMAL; + +cleanup: + return errstatus; +} diff --git a/src/VMS_VSMP/vsmp_msg.msg b/src/VMS_VSMP/vsmp_msg.msg new file mode 100644 index 0000000..a1ad8f4 --- /dev/null +++ b/src/VMS_VSMP/vsmp_msg.msg @@ -0,0 +1,102 @@ + .TITLE Error and Warning Messages for virtual SMP control utility + .IDENT /V-1.0/ + + .FACILITY VSMP,1819 /PREFIX=VSMP_MSG_ + +! +! Falal error messages +! +.SEVERITY FATAL +.BASE 1 + SYS_NOT_VAXMP + VMS_NOT_MULTI + +.BASE 20 + MISLINKED + MISBUILT + SYNTAX + ALREADY_SMP + ALREADY_LOADED + NOT_LOADED + VERSION_MISMATCH + CALIBR_UNSTABLE + LDR_VERIFY + LDR_SCH_CUR_TO_COM + LDR_EXE_PROC_IDLE + LDR_SMP_JMPVEC + VM_REFUSED + IDLE_NEVER + UNMOD_DRV + ALLOCPFN + INVOPTVAL /FAO=1 + IVPATCHID /FAO=1 + XDTINVALID + MSGRTNSLDR + XDELTA_P + CHSEP_P + RESCHED_P + NUMTIM_P + MFYCAP_P + CRBTMO_P + UCBTMO_P + XQTIMXMT_P + UNSUPPXQ + XQTIMXMT_C + XQTX1_P + XQTX2_P + XQTX3_P + XQTX4_P + XQTX5_P + XQTX6_P + XQTX7_P + XQTX8_P + XQTX9_P + XQTX10_P + XQRX1_P + XQRX2_P + XQRX3_P + XQRX4_P + PU1_P + PU2_P + PU3_P + PU4_P + PU5_P + PU6_P + PU7_P + IVPARSET /FAO=1 + IVLOCKRTRY + VLOW_SW_SYS + VLOW_SW_ILK + UNSTART + SPWHIGH + LSPWHIGH + + +! +! Informational messages +! +.SEVERITY INFORMATIONAL +.BASE 100 + + CALIBRATING + CALIBRATED /FAO=1 + CALIBRETRY + LOADED + ADV_SW_SYS + ADV_SW_ILK + ADV_ISW_SYS + ADV_SPW_HIGH /FAO=1 + ADV_LSPW_HIGH /FAO=1 + +! +! Warning messages +! +.SEVERITY WARNING +.BASE 200 + + NONATIVE + SYNCWIDLEOFF + LOW_SW_SYS + LOW_SW_ILK + SYS_LESS_ILK + .END diff --git a/src/VMS_VSMP/xbranch.mar b/src/VMS_VSMP/xbranch.mar new file mode 100644 index 0000000..a15e037 --- /dev/null +++ b/src/VMS_VSMP/xbranch.mar @@ -0,0 +1,113 @@ +;; +;; Extended branch commands +;; +;; Module: xbranch.mar +;; Version: 1.0 +;; Facility: TCP/IP stack for VAX/VMS +;; Author: Sergey Oboguev +;; Created: 12-Dec-1989 +;; Revision History: +;; none +;; + + .MACRO XBRANCH + + +;; +;; eXtended Branch if EQual +;; + .MACRO XBEQL LABEL, ?L + BNEQ L + BRW LABEL +L: .BLKB 0 + .ENDM XBEQL + + +;; +;; eXtended Branch if Not EQual +;; + .MACRO XBNEQ LABEL, ?L + BEQL L + BRW LABEL +L: .BLKB 0 + .ENDM XBNEQ + +;; +;; eXtended Branch if Greater or EQual +;; + .MACRO XBGEQ LABEL, ?L + BLSS L + BRW LABEL +L: .BLKB 0 + .ENDM XBGEQ + +;; +;; eXtended Branch if LeSS Unsigned +;; + .MACRO XBLSSU LABEL, ?L + BGEQU L + BRW LABEL +L: .BLKB 0 + .ENDM XBLSSU + +;; +;; eXtended Branch if Low Bit is Clear +;; + .MACRO XBLBC FLAG, LABEL, ?L + BLBS FLAG, L + BRW LABEL +L: .BLKB 0 + .ENDM XBLBC + + +;; +;; eXtended Branch if Bit is Clear +;; + .MACRO XBBC BIT, FLAGS, LABEL, ?L + BBS BIT, FLAGS, L + BRW LABEL +L: .BLKB 0 + .ENDM XBBC + + +;; +;; eXtended Branch if Bit is Set +;; + .MACRO XBBS BIT, FLAGS, LABEL, ?L + BBC BIT, FLAGS, L + BRW LABEL +L: .BLKB 0 + .ENDM XBBS + + +;; +;; Bit Set (non-interlocked) pseudoinstruction +;; + .MACRO XSSB BIT, FIELD, ?L + BBSS BIT, FIELD, L +L: .BLKB 0 + .ENDM XSSB + + +;; +;; Bit Set (interlocked) pseudoinstruction +;; + .MACRO XSSBI BIT, FIELD, ?L + BBSSI BIT, FIELD, L +L: .BLKB 0 + .ENDM XSSBI + + +;; +;; Bit Clear (interlocked) pseudoinstruction +;; + .MACRO XCCBI BIT, FIELD, ?L + BBCCI BIT, FIELD, L +L: .BLKB 0 + .ENDM XCCBI + + + .MACRO XBRANCH + .ENDM XBRANCH + + .ENDM XBRANCH diff --git a/src/check-config.sh b/src/check-config.sh new file mode 100755 index 0000000..2ed505d --- /dev/null +++ b/src/check-config.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +excode=0 + +case "$1" in +'CONFIG=x86-dbg') +;; +'CONFIG=x86-rel') +;; +'CONFIG=x64-dbg') +;; +'CONFIG=x64-rel') +;; +*) +echo 'Error: Invalid value of CONFIG' +echo ' Valid values are: x86-dbg, x86-rel, x64-dbg, x64-rel' +excode=1 +;; +esac + +case "$2" in +'USE_NETWORK=0') +;; +'USE_NETWORK=1') +;; +*) +echo 'Error: Invalid value of USE_NETWORK' +echo ' Valid values are: 0, 1' +excode=1 +;; +esac + +if [ "$OSTYPE" = "" ] +then +echo 'Error: Missing value of OSTYPE' +echo ' Did you remember to "export OSTYPE" in your shell?' +excode=1 +fi + +exit $excode \ No newline at end of file diff --git a/src/d2u.sh b/src/d2u.sh new file mode 100755 index 0000000..51a0330 --- /dev/null +++ b/src/d2u.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +# +# When modifying this file, remember to save it in Unix (LF) format, +# rather than DOS (CR-LF) format. +# +# If it is unable to run becuase it was converted to CR-LF format, execute +# on Linux: +# +# dos2unix d2u.sh +# +# on OS X: +# +# perl -p -e 's/(\r\n|\n|\r)/\n/g' d2u.sh >d2u.tmp +# mv -f d2u.tmp d2u.sh +# + +if [ "`which perl`" = "" ] && [ "`which dos2unix`" = "" ] +then + echo "Error: Neither dos2unix nor perl installed" + exit 1 +fi + +if [ "$1" = "" ] +then + find . -name "*.cpp" -exec $0 {} \; + find . -name "*.c" -exec $0 {} \; + find . -name "*.h" -exec $0 {} \; + find . -name "*.com" -exec $0 {} \; + find . -name "*.mar" -exec $0 {} \; + find . -name "*.txt" -exec $0 {} \; + find . -name "*.asm" -exec $0 {} \; + find . -name "*.ini" -exec $0 {} \; + find . -name "*.msg" -exec $0 {} \; + $0 ./Makefile + $0 ./makefile2 + find . \( -name "*.sh" -and -not -name "d2u.sh" \) -exec $0 {} \; + find . -name "*.sh" -exec chmod a+x {} \; + find . -name "*.COM" -exec $0 {} \; + find . -name "*.C" -exec $0 {} \; + find . -name "*.H" -exec $0 {} \; + find . -name "*.MSG" -exec $0 {} \; + find . -name "*.MAR" -exec $0 {} \; + find . -name "*.MSG" -exec $0 {} \; + find . -name "*.TXT" -exec $0 {} \; +elif [ "`which dos2unix`" != "" ] +then + chmod o+w $1 + dos2unix $1 +else + echo Converting $1 + perl -p -e 's/(\r\n|\n|\r)/\n/g' $1 >$1.d2u.tmp + touch -r $1 $1.d2u.tmp + mv -f $1.d2u.tmp $1 +fi diff --git a/src/linux-tap.sh b/src/linux-tap.sh new file mode 100755 index 0000000..dc850bd --- /dev/null +++ b/src/linux-tap.sh @@ -0,0 +1,242 @@ +#!/bin/sh + +# +# Create tap device owned by specified user and bridge it to the Ethernet interface +# For usage invoke as ./linux-tap.sh help +# + +if [ "`which /usr/sbin/brctl`" = "" ] +then + echo "Error: brctl is not installed" + exit 1 +fi + +if [ "`which /usr/sbin/tunctl`" = "" ] +then + echo "Error: tunctl is not installed" + exit 1 +fi + +if [ "`which gawk`" = "" ] +then + echo "Error: gawk is not installed" + exit 1 +fi + +VERB=$1 + +case "$VERB" in +'create') + # + ############################################################################################ + # + BRDEV=$2 + ETHDEV=$3 + TAPDEV=$4 + USERID=$5 + # + echo BRDEV=$BRDEV + echo ETHDEV=$ETHDEV + echo TAPDEV=$TAPDEV + echo USERID=$USERID + if [ "$TAPDEV" = "" ] || [ "$ETHDEV" = "" ] || [ "$BRDEV" = "" ] || [ "$USERID" = "" ] + then + echo "Error: missing argument" + exit 1 + fi + # + ############################################################################################ + # + HOSTIP=`/sbin/ifconfig $ETHDEV | grep "inet addr" | gawk -- '{ print $2 }' | gawk -F : -- '{ print $2 }'` + HOSTNETMASK=`/sbin/ifconfig $ETHDEV | grep "inet addr" | gawk -- '{ print $4 }' | gawk -F : -- '{ print $2 }'` + HOSTBCASTADDR=`/sbin/ifconfig $ETHDEV | grep "inet addr" | gawk -- '{ print $3 }' | gawk -F : -- '{ print $2 }'` + HOSTDEFAULTGATEWAY=`/sbin/route -n | grep ^0.0.0.0 | gawk -- '{ print $2 }'` + # + ############################################################################################ + # + echo HOSTIP=$HOSTIP + echo HOSTNETMASK=$HOSTNETMASK + echo HOSTBCASTADDR=$HOSTBCASTADDR + echo HOSTDEFAULTGATEWAY=$HOSTDEFAULTGATEWAY + if [ "$HOSTIP" = "" ] || [ "$HOSTNETMASK" = "" ] || [ "$HOSTBCASTADDR" = "" ] || [ "$HOSTDEFAULTGATEWAY" = "" ] + then + echo "Error: missing network configuration data" + exit 1 + fi + # + ############################################################################################ + # + /usr/sbin/tunctl -t $TAPDEV -u $USERID + /sbin/ifconfig $TAPDEV up + # + ############################################################################################ + # + # Now convert $ETHDEV to a bridge and bridge it with the TAP interface + # + /usr/sbin/brctl addbr $BRDEV + /usr/sbin/brctl addif $BRDEV $ETHDEV + /usr/sbin/brctl setfd $BRDEV 0 + /sbin/ifconfig $ETHDEV 0.0.0.0 + /sbin/ifconfig $BRDEV $HOSTIP netmask $HOSTNETMASK broadcast $HOSTBCASTADDR up + # + # Set the default route to the $BRDEV interface + # + /sbin/route add -net 0.0.0.0/0 gw $HOSTDEFAULTGATEWAY + # + # Bridge in the tap device + # + /usr/sbin/brctl addif $BRDEV $TAPDEV + /sbin/ifconfig $TAPDEV 0.0.0.0 + ;; + + +addtap) + # + ############################################################################################ + # + BRDEV=$2 + TAPDEV=$3 + USERID=$4 + # + echo BRDEV=$BRDEV + echo TAPDEV=$TAPDEV + echo USERID=$USERID + if [ "$TAPDEV" = "" ] || [ "$BRDEV" = "" ] || [ "$USERID" = "" ] + then + echo "Error: missing argument" + exit 1 + fi + # + ############################################################################################ + # + /usr/sbin/tunctl -t $TAPDEV -u $USERID + /sbin/ifconfig $TAPDEV up + /usr/sbin/brctl addif $BRDEV $TAPDEV + /sbin/ifconfig $TAPDEV 0.0.0.0 + ;; + + +deltap) + # + ############################################################################################ + # + BRDEV=$2 + TAPDEV=$3 + # + echo BRDEV=$BRDEV + echo TAPDEV=$TAPDEV + if [ "$TAPDEV" = "" ] || [ "$BRDEV" = "" ] + then + echo "Error: missing argument" + exit 1 + fi + # + ############################################################################################ + # + /sbin/ifconfig $TAPDEV down + /usr/sbin/brctl delif $BRDEV $TAPDEV + /usr/sbin/tunctl -d $TAPDEV + ;; + +destroy) + # + ############################################################################################ + # + BRDEV=$2 + ETHDEV=$3 + TAPDEV=$4 + # + echo TAPDEV=$TAPDEV + echo ETHDEV=$ETHDEV + echo BRDEV=$BRDEV + if [ "$TAPDEV" = "" ] || [ "$ETHDEV" = "" ] || [ "$BRDEV" = "" ] + then + echo "Error: missing argument" + exit 1 + fi + /sbin/ifconfig $BRDEV + # + ############################################################################################ + # + HOSTIP=`/sbin/ifconfig $BRDEV | grep "inet addr" | gawk -- '{ print $2 }' | gawk -F : -- '{ print $2 }'` + HOSTNETMASK=`/sbin/ifconfig $BRDEV | grep "inet addr" | gawk -- '{ print $4 }' | gawk -F : -- '{ print $2 }'` + HOSTBCASTADDR=`/sbin/ifconfig $BRDEV | grep "inet addr" | gawk -- '{ print $3 }' | gawk -F : -- '{ print $2 }'` + HOSTDEFAULTGATEWAY=`/sbin/route -n | grep ^0.0.0.0 | gawk -- '{ print $2 }'` + # + ############################################################################################ + # + echo HOSTIP=$HOSTIP + echo HOSTNETMASK=$HOSTNETMASK + echo HOSTBCASTADDR=$HOSTBCASTADDR + echo HOSTDEFAULTGATEWAY=$HOSTDEFAULTGATEWAY + if [ "$HOSTIP" = "" ] || [ "$HOSTNETMASK" = "" ] || [ "$HOSTBCASTADDR" = "" ] || [ "$HOSTDEFAULTGATEWAY" = "" ] + then + echo "Warning: missing network configuration data" + exit 1 + fi + # + /sbin/ifconfig $BRDEV down + /usr/sbin/brctl delbr $BRDEV + /sbin/ifconfig $TAPDEV down + /usr/sbin/tunctl -d $TAPDEV + # + if [ "$HOSTIP" = "" ] || [ "$HOSTNETMASK" = "" ] || [ "$HOSTBCASTADDR" = "" ] + then + echo "Warning: Unable to configure $ETHDEV because configuration data is missing" + echo " Execute manually: ifconfig $ETHDEV netmask broadcast up" + else + /sbin/ifconfig $ETHDEV $HOSTIP netmask $HOSTNETMASK broadcast $HOSTBCASTADDR up + fi + # + # Set the default route to the $ETHDEV interface + # + /sbin/route add -net 0.0.0.0/0 gw $HOSTDEFAULTGATEWAY + ;; + + +*) + if [ "$VERB" != "help" ] && [ "$VERB" != "" ] + then + echo "Error: Invalid command verb." + echo "" + fi + + echo "Usage:" + echo "" + echo " To create the bridge, create tap interface and add it to the bridge: " + echo "" + echo " Execute: $0 create BRDEV ETHDEV TAPDEV USERID" + echo "" + echo " Example: $0 create br0 eth0 tap0 vaxuser" + echo "" + echo " To create additional tap interface and add it to the bridge: " + echo "" + echo " Execute: $0 addtap BRDEV TAPDEV USERID" + echo "" + echo " Example: $0 addtap br0 tap2 vaxuser2" + echo " $0 addtap br0 tap9 vaxuser9" + echo "" + echo " To destroy addtional tap interface (beyond the last): " + echo "" + echo " Execute: $0 deltap BRDEV TAPDEV" + echo "" + echo " Example: $0 deltap br0 tap1" + echo "" + echo " To destroy the bridge and the only remaining tap interface: " + echo "" + echo " Execute: $0 destroy BRDEV ETHDEV TAPDEV" + echo " Optionally: ifconfig ETHDEV netmask broadcast up" + echo "" + echo " Example: $0 create br0 eth0 tap0" + echo " Optionally: ifconfig eth0 ..." + echo "" + echo " To check current host network configuration, use commands:" + echo "" + echo " brctl show" + echo " brctl show BRDEV (e.g. brctl show br0)" + echo " brctl showmacs BRDEV (e.g. brctl showmacs br0)" + echo " ifconfig" + echo " route -n" + echo "" + ;; +esac diff --git a/src/makefile2 b/src/makefile2 new file mode 100644 index 0000000..a18f13e --- /dev/null +++ b/src/makefile2 @@ -0,0 +1,238 @@ +# +# makefile for VAX MP for Linux, OS X and Unix +# +# To build VAX MP invoke as: +# +# make CONFIG=... USE_NETWORK={1|0} USE_SHARED={1|0} USE_TAP_NETWORK={1|0} USE_VDE_NETWORK={1|0} +# +# where possible values for CONFIG are: +# +# x86-dbg for 32-bit i86 debug build +# x86-rel 32-bit i86 release build +# x64-dbg 64-bit x86 debug build +# x64-rel 64-bit x86 release build +# +# To clean the target, invoke +# +# make CONFIG=... clean +# +# To rebuild dependency list before building, invoke as +# +# make CONFIG=... depend +# +# Suggested sequence for clean rebuild is: +# +# make CONFIG=... clean +# make CONFIG=... depend +# make CONFIG=... USE_NETWORK={1|0} +# +# or +# +# make CONFIG=... rebuild +# +# This file assumes that the value for parameters are set appropriately +# and does not verify them. +# +# Parent shell should export OSTYPE variable. +# + +SHELL = /bin/sh +VAX = vax_mp + +ifeq ($(CXX),) + CXX = g++ +endif + +ifeq (SunOS,$(shell uname)) + TEST = /bin/test +else + TEST = test +endif + +PDP11D = PDP11 +VAXD = VAX +EXE = + +CFLAGS_M = +CFLAGS_D = -DVM_VAX -D VM_VAX_MP -DUSE_INT64 -DUSE_ADDR64 -U__STRICT_ANSI__ +CFLAGS_I = -I. -I$(VAXD) -I$(PDP11D) +CFLAGS_W = -Wall -Weffc++ -Wundef -Wextra -Wshadow -Wconversion -Wlogical-op -Winline -Wunsafe-loop-optimizations -Wabi -Wno-invalid-offsetof +# disable warnings for minor issues that better be cleaned up some day +CFLAGS_W += -Wno-unused-parameter -Wno-shadow -Wno-write-strings -Wno-effc++ -Wno-missing-field-initializers -Wno-conversion +CFLAGS_W += -Wno-unused-variable -Wno-unused-function -Wno-parentheses -Wno-unsafe-loop-optimizations -Wno-unused-result +CFLAGS_O = +CFLAGS_G = +LD_LIBS = +LDFLAGS = -static-libstdc++ -shared-libgcc +HAVE_DLOPEN = + +# under OS X we use GCC 4.7 that has -Wno-unused-but-set-variable +# under Linux at this time we are still using GCC 4.4/4.5 that does not have this option +ifneq (,$(findstring darwin,$(OSTYPE))) + CFLAGS_W += -Wno-unused-but-set-variable +endif + +ifneq (,$(findstring linux,$(OSTYPE))) + HAVE_DLOPEN = so +endif + +ifneq (,$(findstring darwin,$(OSTYPE))) + HAVE_DLOPEN = dylib +endif + +ifneq ($(HAVE_DLOPEN),) + CFLAGS_D += -DHAVE_DLOPEN=$(HAVE_DLOPEN) + LD_LIBS += -ldl +endif + +ifeq ($(USE_SHARED),1) + ifeq ($(HAVE_DLOPEN),) + USE_SHARED = 0 + USE_NETWORK = 1 + endif +endif + +ifeq ($(USE_NETWORK),1) + CFLAGS_D += -DUSE_NETWORK + CFLAGS_I += -isystem /usr/local/include + ifeq (usrlib,$(shell if $(TEST) -e /usr/lib/libpcap.a; then echo usrlib; fi)) + LD_LIBS += /usr/lib/libpcap.a + else + LD_LIBS += /usr/local/lib/libpcap.a + endif +endif + +ifeq ($(USE_SHARED),1) + CFLAGS_D += -DUSE_SHARED + CFLAGS_I += -isystem /usr/local/include + LD_LIBS += -lpcap -ldl +endif + +ifeq ($(USE_TAP_NETWORK),1) + ifneq (,$(findstring darwin,$(OSTYPE))) + CFLAGS_D += -DUSE_TAP_NETWORK -DUSE_BSDTUNTAP + else + CFLAGS_D += -DUSE_TAP_NETWORK + endif +endif + +ifeq ($(USE_VDE_NETWORK),1) + CFLAGS_D += -DUSE_VDE_NETWORK + LD_LIBS += -lvdeplug +endif + +ifeq ($(CONFIG),x86-dbg) + CFLAGS_M += -m32 -march=pentium + CFLAGS_G += -g -ggdb -g3 + CFLAGS_O = -O0 + BIN = $(CONFIG) +endif + +ifeq ($(CONFIG),x64-dbg) + CFLAGS_M += -m64 + CFLAGS_G += -g -ggdb -g3 + CFLAGS_O = -O0 + BIN = $(CONFIG) +endif + +ifeq ($(CONFIG),x86-rel) + CFLAGS_M += -m32 -march=pentium + CFLAGS_O = -O2 -flto -finline-functions -fgcse-after-reload -fpredictive-commoning -fipa-cp-clone + #CFLAGS_O = -O3 -flto -fno-unswitch-loops -fno-tree-vectorize + LDFLAGS = -flto -fwhole-program $(CFLAGS_O) + BIN = $(CONFIG) +endif + +ifeq ($(CONFIG),x64-rel) + CFLAGS_M += -m64 + CFLAGS_O = -O2 -flto -finline-functions -fgcse-after-reload -fpredictive-commoning -fipa-cp-clone + #CFLAGS_O = -O3 -flto -fno-unswitch-loops -fno-tree-vectorize + LDFLAGS = -flto -fwhole-program $(CFLAGS_O) + BIN = $(CONFIG) +endif + +CFLAGS_O += -fno-unsafe-loop-optimizations -fno-strict-overflow + +OSTYPE_PROCESSED = no + +ifneq (,$(findstring linux,$(OSTYPE))) + CFLAGS_D += -D_GNU_SOURCE + LD_LIBS += -lrt -lpthread + OSTYPE_PROCESSED = yes +endif + +ifneq (,$(findstring solaris,$(OSTYPE))) + CFLAGS_D += -D_GNU_SOURCE + LD_LIBS += -lm -lsocket -lnsl -lrt -lpthread + OSTYPE_PROCESSED = yes +endif + +ifneq (,$(findstring darwin,$(OSTYPE))) + CFLAGS_D += -D_GNU_SOURCE + LD_LIBS += -lpthread + OSTYPE_PROCESSED = yes +endif + +ifeq (no,$(OSTYPE_PROCESSED)) + CFLAGS_D += -D_GNU_SOURCE + LD_LIBS += -lrt -lpthread +endif + +CXXFLAGS = -c $(CFLAGS_M) $(CFLAGS_D) $(CFLAGS_I) $(CFLAGS_W) $(CFLAGS_O) $(CFLAGS_G) +CMMFLAGS = -MM $(CFLAGS_M) $(CFLAGS_D) $(CFLAGS_I) $(CFLAGS_W) $(CFLAGS_O) $(CFLAGS_G) + +SIM_SRC := $(wildcard *.cpp) +PDP11_SRC := $(wildcard $(PDP11D)/*.cpp) +VAX_SRC := $(wildcard $(VAXD)/*.cpp) +ALL_SRC := $(SIM_SRC) $(PDP11_SRC) $(VAX_SRC) +SIM_H := $(wildcard *.h) +PDP11_H := $(wildcard $(PDP11D)/*.h) +VAX_H := $(wildcard $(VAXD)/*.h) +ALL_H := $(SIM_H) $(PDP11_H) $(VAX_H) +OBJS := $(addprefix $(BIN)/,$(patsubst %.cpp,%.o,$(notdir $(ALL_SRC)))) + +.PHONY : all clean rebuild depend $(VAX) ; + +all: $(VAX) ; + +$(VAX): $(BIN)/$(VAX)$(EXE) ; + +depend: $(BIN)-depend.mk ; + +rebuild: clean depend $(VAX) ; + +clean: + -rm -rf $(BIN) + +$(BIN)/$(VAX)$(EXE): $(BIN) $(OBJS) + @echo Linking $(BIN)/$(VAX)$(EXE) ... + @$(CXX) $(LDFLAGS) $(OBJS) $(LD_LIBS) -o $(BIN)/$(VAX)$(EXE) + +$(BIN): + mkdir -p $(BIN) + +$(BIN)/%.o: %.cpp + @echo Compiling $< ... + @$(CXX) $(CXXFLAGS) -c $< -o $@ + +$(BIN)/%.o: $(PDP11D)/%.cpp + @echo Compiling $< ... + @$(CXX) $(CXXFLAGS) -c $< -o $@ + +$(BIN)/%.o: $(VAXD)/%.cpp + @echo Compiling $< ... + @$(CXX) $(CXXFLAGS) -c $< -o $@ + +#.cpp.o: +# $(CXX) $(CXXFLAGS) -c $< -o $@ + +$(BIN)-depend.mk: $(ALL_SRC) $(ALL_H) + @echo Building dependencies list ... + @mkdir -p $(BIN) + @-rm -f $@ + @-rm -f $@.tmp + @$(CXX) $(CMMFLAGS) $(ALL_SRC) >$@.tmp + @sed -e's/^\([a-zA-Z0-9]\)/$(BIN)\/\1/' <$@.tmp >$@ + @rm -f $@.tmp + +include $(BIN)-depend.mk diff --git a/src/mk.sh b/src/mk.sh new file mode 100755 index 0000000..a2a2fe3 --- /dev/null +++ b/src/mk.sh @@ -0,0 +1,151 @@ +#!/bin/sh + +# +# Convenience driver for VAX MP makefile. +# +# Invoke as: +# +# mk.sh {x86|x64} {dbg|rel} {net|shr|nonet} [tap] [vde] [all vax_mp clean depend rebuild] +# +# Multiple targets can be specified for the given configuration. +# + +CONF_ARCH="" +CONF_BUILD="" +CONF_NET="" +CONF_TAP="" +CONF_VDE="" +CONF_TARGETS="" +badargs="false" + +for arg in $* +do + + case "$arg" in + 'x86' | 'x64') + if [ "$CONF_ARCH" = "" ] || [ "$CONF_ARCH" = "$arg" ] + then + CONF_ARCH="$arg" + else + echo "Error: Conflicting arguments: $CONF_ARCH and $arg" + badargs="true" + fi + ;; + 'dbg' | 'rel') + if [ "$CONF_BUILD" = "" ] || [ "$CONF_BUILD" = "$arg" ] + then + CONF_BUILD="$arg" + else + echo "Error: Conflicting arguments: $CONF_BUILD and $arg" + badargs="true" + fi + ;; + 'net' | 'nonet' | 'shr') + if [ "$CONF_NET" = "" ] || [ "$CONF_NET" = "$arg" ] + then + CONF_NET="$arg" + else + echo "Error: Conflicting arguments: $CONF_NET and $arg" + badargs="true" + fi + ;; + 'tap') + CONF_TAP="true" + ;; + 'vde') + CONF_VDE="true" + ;; + 'all' | 'vax_mp' | 'clean' | 'depend' | 'rebuild') + if [ "$CONF_TARGETS" = "" ] + then + CONF_TARGETS="$arg" + else + CONF_TARGETS="$CONF_TARGETS $arg" + fi + ;; + *) + echo "Error: Invalid argument: $arg" + badargs="true" + ;; + esac + +done + +if [ "$CONF_ARCH" = "" ] +then + echo "Error: Missing target architecture parameter" + badargs="true" +fi + +if [ "$CONF_BUILD" = "" ] +then + echo "Error: Missing target build parameter" + badargs="true" +fi + +if [ "$CONF_NET" = "" ] +then + echo "Error: Missing target network parameter, specify one of NONET, NET or SHR" + badargs="true" +fi + +if [ "$badargs" = "true" ] +then + echo "Usage: mk.sh {x86|x64} {dbg|rel} {net|shr|nonet} [tap] [vde]" + echo " [all vax_mp clean depend rebuild]" + echo "" + echo " x86|x64 select target host processor architecture" + echo " dbg|rel select release or debug build" + echo " net|shr|nonet select network support: static or dynamic library, or none" + echo " tap support TAP devices for host-VM networking (Linux, OS X)" + echo " vde support VDE networking (Linux)" + exit 1 +fi + +if [ "$CONF_NET" = "net" ] +then + USE_NETWORK=1 + USE_SHARED=0 +fi + +if [ "$CONF_NET" = "shr" ] +then + USE_NETWORK=0 + USE_SHARED=1 +fi + +if [ "$CONF_NET" = "nonet" ] +then + USE_NETWORK=0 + USE_SHARED=0 +fi + +if [ "$CONF_TAP" = "true" ] && [ "$USE_NETWORK" = "0" ] && [ "$USE_SHARED" = "0" ] +then + echo "Error: TAP requires NET or SHR options" + exit 1 +fi + +if [ "$CONF_VDE" = "true" ] && [ "$USE_NETWORK" = "0" ] && [ "$USE_SHARED" = "0" ] +then + echo "Error: VDE requires NET or SHR options" + exit 1 +fi + +if [ "$CONF_TAP" = "true" ] +then + USE_TAP_NETWORK=1 +else + USE_TAP_NETWORK=0 +fi + +if [ "$CONF_VDE" = "true" ] +then + USE_VDE_NETWORK=1 +else + USE_VDE_NETWORK=0 +fi + +export OSTYPE + +make CONFIG=$CONF_ARCH-$CONF_BUILD USE_NETWORK=$USE_NETWORK USE_SHARED=$USE_SHARED USE_TAP_NETWORK=$USE_TAP_NETWORK USE_VDE_NETWORK=$USE_VDE_NETWORK $CONF_TARGETS diff --git a/src/scp.cpp b/src/scp.cpp new file mode 100644 index 0000000..26afc67 --- /dev/null +++ b/src/scp.cpp @@ -0,0 +1,8230 @@ +/* scp.c: simulator control program + + 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"), + 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. + + 25-Sep-11 MP Added the ability for a simulator built with + SIM_ASYNCH_IO to change whether I/O is actually done + asynchronously by the new scp command SET ASYNCH and + SET NOASYNCH + 22-Sep-11 MP Added signal catching of SIGHUP and SIGTERM to cause + simulator STOP. This allows an externally signalled + event (i.e. system shutdown, or logoff) to signal a + running simulator of these events and to allow + reasonable actions to be taken. This will facilitate + running a simulator as a 'service' on *nix platforms, + given a sufficiently flexible simulator .ini file. + 20-Apr-11 MP Added expansion of %STATUS% and %TSTATUS% in do command + arguments. STATUS is the numeric value of the last + command error status and TSTATUS is the text message + relating to the last command error status + 17-Apr-11 MP Changed sim_rest to defer attaching devices until after + device register contents have been restored since some + attach activities may reference register contained info. + 29-Jan-11 MP Adjusted sim_debug to: + - include the simulator timestamp (sim_gtime) + as part of the prefix for each line of output + SPO: not merged into VAX MP + - write complete lines at a time (avoid asynch I/O issues). + 05-Jan-11 MP Added Asynch I/O support + 22-Jan-11 MP Added SET ON, SET NOON, ON, GOTO and RETURN command support + (SPO: not merged from 3.8.2 to VAX MP) + 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 + 05-Sep-08 JDB "detach_all" ignores error status returns if shutting down + 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) + 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 + Fixed bug in get_sim_opt + Fixed bug in restoration with changed memory size + 08-Mar-07 JDB Fixed breakpoint actions in DO command file processing + 30-Jan-07 RMS Fixed bugs in get_ipaddr + 17-Oct-06 RMS Added idle support + 04-Oct-06 JDB DO cmd failure now echoes cmd unless -q + 14-Jul-06 RMS Added sim_activate_abs + 02-Jun-06 JDB Fixed do_cmd to exit nested files on assertion failure + Added -E switch to do_cmd to exit on any error + 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) + 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) + 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) + Revised interface to fprint_sym, fparse_sym + 07-Feb-05 RMS Added ASSERT command (from 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) + 15-Oct-04 RMS Fixed HELP to suppress duplicate descriptions + 27-Sep-04 RMS Fixed comma-separation options in set (from 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) + 12-Jul-04 RMS Fixed problem ATTACHing to read only files + (found by 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 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 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) + 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) + Added u5, u6 fields + Added logical name support + 03-Mar-03 RMS Added sim_fsize + 27-Feb-03 RMS Fixed bug in multiword deposits to files + 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) + 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) + 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) + 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 quiet mode, DO with parameters, GUI interface, + extensible commands (from Brian Knittel) + Added device enable/disable commands + 14-Jul-02 RMS Fixed exit bug in do, added -v switch (from 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) + 26-Feb-02 RMS Fixed initialization bugs in do_cmd, get_aval + (found by 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) + 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 + 24-Nov-01 RMS Added unit-based registers + 16-Nov-01 RMS Added DO command + 28-Oct-01 RMS Added relative range addressing + 08-Oct-01 RMS Added SHOW VERSION + 30-Sep-01 RMS Relaxed attach test in BOOT + 27-Sep-01 RMS Added queue count routine, fixed typo in ex/mod + 17-Sep-01 RMS Removed multiple console support + 07-Sep-01 RMS Removed conditional externs on function prototypes + 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) + 27-May-01 RMS Added multiple console support + 16-May-01 RMS Added logging + 15-May-01 RMS Added features from Tim Litt + 12-May-01 RMS Fixed missing return in disable_cmd + 25-Mar-01 RMS Added ENABLE/DISABLE + 14-Mar-01 RMS Revised LOAD/DUMP interface (again) + 05-Mar-01 RMS Added clock calibration support + 05-Feb-01 RMS Fixed bug, DETACH buffered unit with hwmark = 0 + 04-Feb-01 RMS Fixed bug, RESTORE not using device's attach routine + 21-Jan-01 RMS Added relative time + 22-Dec-00 RMS Fixed find_device for devices ending in numbers + 08-Dec-00 RMS V2.5a changes + 30-Oct-00 RMS Added output file option to examine + 11-Jul-99 RMS V2.5 changes + 13-Apr-99 RMS Fixed handling of 32b addresses + 04-Oct-98 RMS V2.4 changes + 20-Aug-98 RMS Added radix commands + 05-Jun-98 RMS Fixed bug in ^D handling for UNIX + 10-Apr-98 RMS Added switches to all commands + 26-Oct-97 RMS Added search capability + 25-Jan-97 RMS Revised data types + 23-Jan-97 RMS Added bi-endian I/O + 06-Sep-96 RMS Fixed bug in variable length IEXAMINE + 16-Jun-96 RMS Changed interface to parse/print_sym + 06-Apr-96 RMS Added error checking in reset all + 07-Jan-96 RMS Added register buffers in save/restore + 11-Dec-95 RMS Fixed ordering bug in save/restore + 22-May-95 RMS Added symbolic input + 13-Apr-95 RMS Added symbolic printouts +*/ + +/* Macros and data structures */ + +#include "sim_defs.h" +#include "sim_rev.h" +#include +#include + +#if defined(__linux) || defined(_WIN32) +# include +#endif + +#if defined(__linux) || defined(__APPLE__) +# include +#endif + +#include +#include + +#if defined(HAVE_DLOPEN) /* Dynamic Readline support */ +#include +#endif + +#define EX_D 0 /* deposit */ +#define EX_E 1 /* examine */ +#define EX_I 2 /* interactive */ +#define SCH_OR 0 /* search logicals */ +#define SCH_AND 1 +#define SCH_XOR 2 +#define SCH_E 0 /* search booleans */ +#define SCH_N 1 +#define SCH_G 2 +#define SCH_L 3 +#define SCH_EE 4 +#define SCH_NE 5 +#define SCH_GE 6 +#define SCH_LE 7 +#define SSH_ST 0 /* set */ +#define SSH_SH 1 /* show */ +#define SSH_CL 2 /* clear */ + +#define MAX_DO_NEST_LVL 10 /* DO cmd nesting level */ +#define SRBSIZ 1024 /* save/restore buffer */ +#define SIM_BRK_INILNT 4096 /* bpt tbl length */ +#define SIM_BRK_ALLTYP 0xFFFFFFFF + +/* + * CLOCKS & TIME KEEPING LEGEND + * ============================ + * + * Essential elements are as follows. + * + * sim_interval - interval countdown counter till next clock queue check + * + * - per-CPU + * - decremented by 1 by regular instructions + * - decremented multiple times by complex instructions (MOVC3/5, SCANC etc.) + * - decremented by idle sleep by (sleep_time * instructions_per_second) + * + * noqueue_time - time till next queue check if there are entries in the queue + * + * - per-CPU + * - used when clock_queue == NULL, + * - otherwise use clock_queue->time + * - initialized to NOQUEUE_WAIT (10000), decreased by UPDATE_SIM_TIME + * + * sim_time, sim_rtime - time on this virtual processor + * + * - per-CPU + * - updated by UPDATE_SIM_TIME + * - read via sim_gtime, sim_grtime + * - sim_gtime is used for synchronicity window management (see below) + * - sim_grtime used by SYSD clock + * + * - there is no notion of single "global time" shared by all processors, each processor has its own private time view, + * just like in a real hardware multiprocessor system CPU clocks can drift (because of hardware drift and because + * some clock tick interrupts can be blocked and skipped when handling higher-priority interrupts), + * however VAX MP processors keep within a certain synchronicity window with respect to each other's time; + * if some processor gets ahead of the synchronicity window, it stalls its own execution till other processors + * catch up and all processors fall back into allowed synchronicity window limits. + * + * SYSD clocks - per-CPU theoretically, but access by non-primary processors is disallowed + * + * - used only by firmware, SYSBOOT and INIT and final phase of shutdown + * + * UPDATE_SIM_TIME is called as + * + * UPDATE_SIM_TIME (cpu_unit->clock_queue->time); + * + * or + * UPDATE_SIM_TIME (cpu_unit->noqueue_time); + * + * after some time passes and sim_interval decreases from its original value + * of cpu_unit->clock_queue->time or cpu_unit->noqueue_time. + * + * This macro can *only* be executed either on a local processor or from the console thread + * while the processor is paused. + * + */ + +#define UPDATE_SIM_TIME(x) \ + do { \ + cpu_unit->sim_time += (x) - sim_interval; \ + cpu_unit->sim_rtime += (uint32) ((x) - sim_interval); \ + (x) = sim_interval; \ + } while (0) + +#define UPDATE_CPU_SIM_TIME() \ + if (cpu_unit->clock_queue != NULL) \ + { \ + UPDATE_SIM_TIME (cpu_unit->clock_queue->time); \ + } \ + else \ + { \ + UPDATE_SIM_TIME (cpu_unit->noqueue_time); \ + } + +#define SZ_D(dp) (size_map[((dp)->dwidth + CHAR_BIT - 1) / CHAR_BIT]) +#define SZ_R(rp) \ + (size_map[((rp)->width + (rp)->offset + CHAR_BIT - 1) / CHAR_BIT]) +#if defined (USE_INT64) +#define SZ_LOAD(sz,v,mb,j) \ + if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j)); \ + else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \ + else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + ((uint32) j)); \ + else v = *(((t_uint64 *) mb) + ((uint32) j)); +#define SZ_STORE(sz,v,mb,j) \ + if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v; \ + else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \ + else if (sz == sizeof (uint32)) *(((uint32 *) mb) + ((uint32) j)) = (uint32) v; \ + else *(((t_uint64 *) mb) + ((uint32) j)) = v; +#else +#define SZ_LOAD(sz,v,mb,j) \ + if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j)); \ + else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \ + else v = *(((uint32 *) mb) + ((uint32) j)); +#define SZ_STORE(sz,v,mb,j) \ + if (sz == sizeof (uint8)) *(((uint8 *) mb) + ((uint32) j)) = (uint8) v; \ + else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \ + else *(((uint32 *) mb) + ((uint32) j)) = v; +#endif +#define GET_SWITCHES(cp) \ + if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW +#define GET_RADIX(val,dft) \ + if (sim_switches & SWMASK ('O')) val = 8; \ + else if (sim_switches & SWMASK ('D')) val = 10; \ + else if (sim_switches & SWMASK ('H')) val = 16; \ + else val = dft; + +/* VM interface */ + +extern REG *sim_PC; +extern const char *sim_stop_messages[]; +extern t_stat sim_load (RUN_DECL, SMP_FILE *ptr, char *cptr, char *fnam, int32 flag); +extern int32 sim_emax; +extern t_stat fprint_sym (SMP_FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); +extern t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, + int32 sw); + +/* The per-simulator init routine is a weak global that defaults to NULL + The other per-simulator pointers can be overrriden by the init routine */ + +void (*sim_vm_init) (void); +char* (*sim_vm_read) (char *ptr, int32 size, SMP_FILE *stream) = NULL; +void (*sim_vm_post) (t_bool from_scp) = NULL; +CTAB *sim_vm_cmd = NULL; +void (*sim_vm_fprint_addr) (SMP_FILE *st, DEVICE *dptr, t_addr addr) = NULL; +t_addr (*sim_vm_parse_addr) (DEVICE *dptr, char *cptr, char **tptr) = NULL; + +/* Prototypes */ + +/* Set and show command processors */ + +t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat ssh_break (SMP_FILE *st, char *cptr, int32 flg); +t_stat show_cmd_fi (SMP_FILE *ofile, int32 flag, char *cptr); +t_stat show_config (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_queue (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_time (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_mod_names (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_show_commands (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_log_names (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_dev_radix (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_dev_debug (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_dev_logicals (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_dev_modifiers (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_dev_show_commands (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_version (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_break (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_on (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_device (SMP_FILE *st, DEVICE *dptr, int32 flag); +t_stat show_unit (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag); +t_stat show_all_mods (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg); +t_stat show_one_mod (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, char *cptr, int32 flag); +t_stat sim_check_console (int32 sec); +t_stat sim_save (SMP_FILE *sfile); +t_stat sim_rest (SMP_FILE *rfile); + +/* cpu commands */ + +t_stat cpu_cmd (int32 flag, char *ptr); +t_stat cpu_cmd_multiprocessor (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat cpu_cmd_id (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat cpu_cmd_smt (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); + +/* Breakpoint package */ + +t_stat sim_brk_init (void); +t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, char *act); +t_stat sim_brk_clr (t_addr loc, int32 sw); +t_stat sim_brk_clrall (int32 sw); +t_stat sim_brk_show (SMP_FILE *st, t_addr loc, int32 sw); +t_stat sim_brk_showall (SMP_FILE *st, int32 sw); +void sim_brk_clract (void); +void sim_brk_npc (RUN_DECL, uint32 cnt); +BRKTAB *sim_brk_new (t_addr loc); +static t_bool sim_brk_is_action_pending (); +sim_cstream* sim_brk_get_action_script (); +const char* sim_brk_end_action_script (sim_cstream* script); + +/* Commands support routines */ + +SCHTAB *get_search (char *cptr, int32 radix, SCHTAB *schptr); +int32 test_search (t_value val, SCHTAB *schptr); +char *get_glyph_gen (char *iptr, char *optr, char mchar, t_bool uc); +int32 get_switches (char *cptr); +char *get_sim_sw (char *cptr); +t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr); +t_value get_rval (REG *rptr, uint32 idx); +void put_rval (REG *rptr, uint32 idx, t_value val); +t_value strtotv (char *inptr, char **endptr, uint32 radix); +void fprint_help (SMP_FILE *st); +void fprint_stopped (SMP_FILE *st); +void fprint_capac (SMP_FILE *st, DEVICE *dptr, UNIT *uptr); +char *read_line (char *ptr, int32 size, SMP_FILE *stream); +char *read_line_p (char *prompt, char *ptr, int32 size, SMP_FILE *stream); +REG *find_reg_glob (char *ptr, char **optr, DEVICE **gdptr); +char *sim_trim_endspc (char *cptr); + +/* Forward references */ + +static void sim_wait_debugger(int* pargc, char* argv[]); +t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, char *cptr); +t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr); +t_bool qdisable (DEVICE *dptr); +t_stat attach_err (UNIT *uptr, t_stat stat); +t_stat detach_all (int32 start_device, t_bool shutdown); +t_stat assign_device (DEVICE *dptr, char *cptr); +t_stat deassign_device (DEVICE *dptr); +t_stat ssh_break_one (SMP_FILE *st, int32 flg, t_addr lo, int32 cnt, char *aptr); +t_stat run_boot_prep (void); +t_stat exdep_reg_loop (SMP_FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr, + REG *lowr, REG *highr, uint32 lows, uint32 highs); +t_stat ex_reg (SMP_FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx); +t_stat dep_reg (int32 flag, char *cptr, REG *rptr, uint32 idx); +t_stat exdep_addr_loop (SMP_FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr, + t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr); +t_stat ex_addr (SMP_FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr); +t_stat dep_addr (int32 flag, char *cptr, t_addr addr, DEVICE *dptr, + UNIT *uptr, int32 dfltinc); +void sub_args (char *instr, char *tmpbuf, int32 maxstr, char *do_arg[]); +static void print_prompt (SMP_FILE* fp, const char* component = NULL); +static void make_prompt (char* bp, const char* component = NULL); +void fprint_stopped_instr_or_state (RUN_DECL, SMP_FILE *st, const char* msg, REG *pc, DEVICE *dptr); +void fprint_stopped_instr (RUN_DECL, SMP_FILE *st, const char* msg, REG *pc, DEVICE *dptr); +static t_stat run_cmd_core (RUN_DECL, int32 runcmd); +void int_handler (int signal); +static clock_queue_entry* reverse_cqe_list(clock_queue_entry* list); +static void setup_cotimed_cqe_list(clock_queue_entry* list, int32 nticks); +static void insert_cotimed_cqe_list(RUN_DECL, clock_queue_entry* list, int32 newtime); +void sim_reevaluate_noncpu_thread_priority(run_scope_context* rscx); +t_stat set_on (int32 flag, char *cptr); +t_stat set_asynch (int32 flag, char *cptr); +t_stat sim_set_asynch (int32 flag, char *cptr); +t_stat sim_set_environment (int32 flag, char *cptr); +t_bool sim_cpu_has_syswide_events(RUN_DECL); + +/* Global data */ + +DEVICE *sim_dflt_dev = NULL; +CPU_UNIT *sim_dflt_cpu = NULL; +run_scope_context* sim_dflt_rscx = NULL; +int32 sim_switches = 0; +SMP_FILE *sim_ofile = NULL; +SCHTAB *sim_schptr = FALSE; +DEVICE *sim_dfdev = NULL; +UNIT *sim_dfunit = NULL; +int32 sim_opt_out = 0; +uint32 sim_brk_summ = 0; +uint32 sim_brk_types = 0; +uint32 sim_brk_dflt = 0; +BRKTAB *sim_brk_tab = NULL; +int32 sim_brk_ent = 0; +int32 sim_brk_lnt = 0; +atomic_int32 sim_brk_ins = 0; +t_bool sim_brk_continue = FALSE; +typedef sim_cstream* sim_cstream_ptr_t; +static sim_stack sim_brk_action_stack; +int32 sim_quiet = 0; +int32 sim_step = 0; +atomic_int32 stop_cpus = 0; +t_value *sim_eval = NULL; +SMP_FILE *sim_log = NULL; /* log file */ +SMP_FILE *sim_deb = NULL; /* debug file */ +SMP_FILEREF *sim_log_ref = NULL; /* log file file reference */ +SMP_FILEREF *sim_deb_ref = NULL; /* debug file file reference */ +static int32 sim_do_echo = 0; /* the echo status of the currently open do file */ +static t_stat sim_last_cmd_stat; /* command status */ +static SCHTAB sim_stab; +t_bool sim_vcpu_per_core = FALSE; /* VCPU affinity control: if set true, try to assign at most + one VCPU per single host PCPU core */ +t_bool sim_vsmp_active = FALSE; /* virtual multiprocessing initialized (VSMP had called VAXMP_API_OP_INIT_SMP) */ +uint32 sim_vsmp_os = 0; /* guest OS ID (valid only if sim_vsmp_active is TRUE) */ +t_bool sim_vsmp_idle_sleep = FALSE; /* TRUE if idle sleep is enabled (VSMP SET IDLE=ON), valid only if sim_vsmp_active is TRUE */ +smp_affinity_kind_t sim_vcpu_affinity = SMP_AFFINITY_ALL; /* VCPU affinity */ +int32 sim_units_percpu = 0; /* number of per-CPU units in the system */ +int32 sim_units_global = 0; /* number of global units in the system */ +static clock_queue_entry_info* sim_requeue_info = NULL; /* data buffer used by sim_requeue_syswide_events */ +t_bool sim_asynch_enabled = FALSE; +extern UNIT sim_throt_unit; +t_bool sim_ttrun_mode = FALSE; +on_init_call* on_init_call::head = NULL; +smp_lock* cpu_database_lock = NULL; +smp_semaphore* cpu_attention = NULL; +smp_barrier* cpu_pause_sync_barrier = NULL; +smp_semaphore* cpu_clock_run_gate = NULL; +t_bool sim_clock_thread_created = FALSE; +t_bool use_clock_thread = USE_CLOCK_THREAD; +/* + * If use_native_interlocked is true, use host native interlocked instructions to simulate target machine's + * interlocked instructions. If false, use portable interlock + * + * Do not enable this flag unless you understand well the ramification and, in particular, that synchronization + * window may need to be extended to non-kernel processor modes and low IPLs in kernel mode. + * + * See "VAX MP Techincal Overview", chapters "Synchronization window" and "Implementation of VAX interlocked + * instructions" for more details. + */ +uint32 use_native_interlocked = FALSE; +smp_thread_t sim_clock_thread = SMP_THREAD_NULL; +uint32 cpu_cycles_mark[SIM_MAX_CPUS]; +uint32 cpu_cycles_sleepexit[SIM_MAX_CPUS]; +cpu_set cpu_waitset[SIM_MAX_CPUS]; +cpu_set cpu_running_set; +/* + * sim_mp_active is FALSE when only one processor is active. + * + * Use is as follows: + * + * Value is changed only either by VCPU thread holding cpu_database_lock or by console thread while VCPUs are paused. + * + * weak_read(sim_mp_active) == FALSE means that only primary VCPU is active, + * it is guaranted that no other VCPUs are running + * + * weak_read(sim_mp_active) == TRUE means _almost_ always that multiple VCPUs are running, + * however very occassionally it can also be TRUE if secondary just stopped but the change + * in sim_mp_active value performed by secondary shutdown had not propagated to the primary yet. + * + * In other words, weak_read(sim_mp_active) value of FALSE is reliable, but value of TRUE is not. + * This is sufficient for the purposes of taking uniprocessor "fast paths" in the code that provides + * optimized behavior for uniprocessor execution. + * + * Note that described rule applies only within VCPU threads, and also within console thread but only when + * VCPU threads are paused. The rule is not valid within CLOCK or IOP threads or console thread when VCPU threads + * are running. + * + */ +atomic_bool sim_mp_active = FALSE; + +t_bool sim_ws_prefaulted = FALSE; /* if TRUE, memory had been pre-faulted */ +t_bool sim_ws_settings_changed = FALSE; /* if TRUE, working set settings below had been changed */ +t_bool sim_ws_lock = FALSE; /* if TRUE, lock all pages in working set */ +uint32 sim_ws_min = 0; /* minimum working set size (MB) */ +uint32 sim_ws_max = 0; /* maximum working set size (MB) */ +uint32 sim_host_turbo = 120; /* host CPU turbo factor (max cpu freq / min cpu freq) */ +t_bool sim_host_dedicated = FALSE; /* if TRUE, host is wholly dedicated to running the simulator, + therefore do not perform VCPU thread priority managemenet */ + +#if defined USE_INT64 +static const char *sim_si64 = "64b data"; +#else +static const char *sim_si64 = "32b data"; +#endif +#if defined USE_ADDR64 +static const char *sim_sa64 = "64b addresses"; +#else +static const char *sim_sa64 = "32b addresses"; +#endif +#if defined (USE_NETWORK) || defined (USE_SHARED) +static const char *sim_snet = "Ethernet support"; +#else +static const char *sim_snet = "no Ethernet"; +#endif + +/* Tables and strings */ + +const char save_vercur[] = "M3.5"; +const char save_ver32[] = "V3.2"; +const char save_ver30[] = "V3.0"; +const struct scp_error +{ + char *code; + char *message; +} +scp_errors[SCPE_MAX_ERR - SCPE_BASE + 1] = +{ + {"NXM", "Address space exceeded"}, + {"UNATT", "Unit not attached"}, + {"IOERR", "I/O error"}, + {"CSUM", "Checksum error"}, + {"FMT", "Format error"}, + {"NOATT", "Unit not attachable"}, + {"OPENERR", "File open error"}, + {"MEM", "Memory exhausted"}, + {"ARG", "Invalid argument"}, + {"STEP", "Step expired"}, + {"UNK", "Unknown command"}, + {"RO", "Read only argument"}, + {"INCOMP", "Command not completed"}, + {"STOP", "Simulation stopped"}, + {"EXIT", "Goodbye"}, + {"TTIERR", "Console input I/O error"}, + {"TTOERR", "Console output I/O error"}, + {"EOF", "End of file"}, + {"REL", "Relocation error"}, + {"NOPARAM", "No settable parameters"}, + {"ALATT", "Unit already attached"}, + {"TIMER", "Hardware timer error"}, + {"SIGERR", "SIGINT handler setup error"}, + {"TTYERR", "Console terminal setup error"}, + {"SUB", "Subscript out of range"}, + {"NOFNC", "Command not allowed"}, + {"UDIS", "Unit disabled"}, + {"NORO", "Read only operation not allowed"}, + {"INVSW", "Invalid switch"}, + {"MISVAL", "Missing value"}, + {"2FARG", "Too few arguments"}, + {"2MARG", "Too many arguments"}, + {"NXDEV", "Non-existent device"}, + {"NXUN", "Non-existent unit"}, + {"NXREG", "Non-existent register"}, + {"NXPAR", "Non-existent parameter"}, + {"NEST", "Nested DO command limit exceeded"}, + {"IERR", "Internal error"}, + {"MTRLNT", "Invalid magtape record length"}, + {"LOST", "Console Telnet connection lost"}, + {"TTMO", "Console Telnet connection timed out"}, + {"STALL", "Console Telnet output stall"}, + {"AFAIL", "Assertion failed"}, + {"SWSTP", "Unable to perform STEP since CPU is constrained by the synchronization window"}, +}; + +const size_t size_map[] = { sizeof (int8), + sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32) +#if defined (USE_INT64) + , sizeof (t_int64), sizeof (t_int64), sizeof (t_int64), sizeof (t_int64) +#endif +}; + +const t_value width_mask[] = { 0, + 0x1, 0x3, 0x7, 0xF, + 0x1F, 0x3F, 0x7F, 0xFF, + 0x1FF, 0x3FF, 0x7FF, 0xFFF, + 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, + 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, + 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, + 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, + 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF +#if defined (USE_INT64) + , 0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF, + 0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF, + 0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF, + 0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF, + 0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF, + 0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF, + 0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF, + 0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF, + 0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF, + 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF +#endif + }; + +static CTAB cmd_table[] = { + { "RESET", &reset_cmd, 0, + "r{eset} {ALL|} reset simulator\n" }, + { "EXAMINE", &exdep_cmd, EX_E, + "e{xamine} examine memory or registers\n" }, + { "IEXAMINE", &exdep_cmd, EX_E+EX_I, + "ie{xamine} interactive examine memory or registers\n" }, + { "DEPOSIT", &exdep_cmd, EX_D, + "d{eposit} deposit in memory or registers\n" }, + { "IDEPOSIT", &exdep_cmd, EX_D+EX_I, + "id{eposit} interactive deposit in memory or registers\n" }, + { "EVALUATE", &eval_cmd, 0, + "ev{aluate} evaluate symbolic expression\n" }, + { "RUN", &run_cmd, RU_RUN, + "ru{n} {new PC} reset and start simulation\n" }, + { "GO", &run_cmd, RU_GO, + "go {new PC} start simulation\n" }, + { "STEP", &run_cmd, RU_STEP, + "s{tep} {n} simulate n instructions\n" }, + { "CONT", &run_cmd, RU_CONT, + "c{ont} continue simulation\n" }, + { "BOOT", &run_cmd, RU_BOOT, + "b{oot} bootstrap unit\n" }, + { "BREAK", &brk_cmd, SSH_ST, + "br{eak} set breakpoints\n" }, + { "NOBREAK", &brk_cmd, SSH_CL, + "nobr{eak} clear breakpoints\n" }, + { "ATTACH", &attach_cmd, 0, + "at{tach} attach file to simulated unit\n" }, + { "DETACH", &detach_cmd, 0, + "det{ach} detach file from simulated unit\n" }, + { "ASSIGN", &assign_cmd, 0, + "as{sign} assign logical name for device\n" }, + { "DEASSIGN", &deassign_cmd, 0, + "dea{ssign} deassign logical name for device\n" }, + { "SAVE", &save_cmd, 0, + "sa{ve} save simulator to file\n" }, + { "RESTORE", &restore_cmd, 0, + "rest{ore}|ge{t} restore simulator from file\n" }, + { "GET", &restore_cmd, 0, NULL }, + { "LOAD", &load_cmd, 0, + "l{oad} {} load binary file\n" }, + { "DUMP", &load_cmd, 1, + "du(mp) {} dump binary file\n" }, + { "EXIT", &exit_cmd, 0, + "exi{t}|q{uit}|by{e} exit from simulation\n" }, + { "QUIT", &exit_cmd, 0, NULL }, + { "BYE", &exit_cmd, 0, NULL }, + { "SET", &set_cmd, 0, + "set console arg{,arg...} set console options\n" + "set console WRU specify console drop to simh char\n" + "set console BRK specify console Break character\n" + "set console DEL specify console delete char\n" + "set console PCHAR specify console printable chars\n" + "set console TELNET=port specify console telnet port\n" + "set console TELNET=LOG specify console telnet logging\n" + "set console TELNET=NOLOG disables console telnet logging\n" + "set console TELNET=BUFFERED[=bufsize]\n" + " specify console telnet buffering\n" + "set console TELNET=NOBUFFERED\n" + " disables console telnet buffering\n" + "set console TELNET=UNBUFFERED\n" + " disables console telnet buffering\n" + "set console NOTELNET disable console telnet\n" + "set console LOG enable console logging\n" + "set console NOLOG disable console logging\n" + "set console DEBUG enable console debugging\n" + "set console NODEBUG disable console debugging\n" + "set break set breakpoints\n" + "set nobreak clear breakpoints\n" + "set throttle x{M|K|%%} set simulation rate\n" + "set nothrottle set simulation rate to maximum\n" + "set asynch enable asynchronous I/O\n" + "set noasynch disable asynchronous I/O\n" + "set environment name=val set environment variable\n" + "set OCT|DEC|HEX set device display radix\n" + "set ENABLED enable device\n" + "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 (see show modifiers)\n" + "set ENABLED enable unit\n" + "set DISABLED disable unit\n" + "set arg{,arg...} set unit parameters (see show modifiers)\n" + // "set on enables error checking after command execution\n" + // "set noon disables error checking after command execution\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 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" + "sh{ow} th{rottle} show simulation rate\n" + "sh{ow} a{synch} show asynchronouse I/O state\n" + "sh{ow} ve{rsion} show simulator version\n" + "sh{ow} 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" }, + { "CPU", &cpu_cmd, 0, + "cpu multi{processor} create virtual CPUs\n" + "cpu id select CPU for current context\n" + "cpu id show currently selected CPU\n" + "cpu smt override SMT slow-down factor\n" + "cpu info detailed CPU state information\n" }, +#if 1 + { "XDEV", &xdev_cmd, 0, NULL }, /* internal development/debugging tool */ +#endif + { "PERF", &perf_cmd, 0, + "perf on [counter] enable performance counter(s)\n" + "perf off [counter] disable performance counter(s)\n" + "perf reset [counter] reset performance counter(s)\n" + "perf show [counter] display performance counter(s)\n" }, + { "DO", &do_cmd, 1, + "do {arg,arg...} process command file\n" }, + { "ECHO", &echo_cmd, 0, + "echo display \n" }, + { "ASSERT", &assert_cmd, 0, + "assert {} test simulator state against condition\n" }, + { "HELP", &help_cmd, 0, + "h{elp} type this message\n" + "h{elp} type help for command\n" }, + { "!", &spawn_cmd, 0, + "! execute local command interpreter\n" + "! execute local host command\n" }, + { NULL, NULL, 0 } + }; + +#if defined(_WIN32) +static int setenv(const char *envname, const char *envval, int overwrite) +{ + char* envstr = (char*) malloc(strlen(envname) + strlen(envval) + 2); + int r; + if (envstr == NULL) + { + r = -1; + } + else + { + sprintf(envstr, "%s=%s", envname, envval); + r = _putenv(envstr); + free(envstr); + } + return r; +} +#endif + +/* Main command loop */ + +static int main_exec (int argc, char *argv[]); + +int main (int argc, char *argv[]) +{ + sim_try_volatile int vol_argc = argc; /* relax compiler warning */ + sim_wait_debugger(&argc, argv); + +#if defined(USE_C_TRY_CATCH) + /* + * Note that sim_seh_frame::init() uses TLS, but we call it ahead of init_threads + * in assumption that TLS is always ready to use and does not require initialization. + */ + if (! sim_seh_frame::init()) + { + fprintf(stderr, "Unable to initialize SEH framework\n"); + return 0; + } +#endif + + sim_try + { + const char* ptname = "CONSOLE"; + + smp_check_aligned(& sim_brk_ins); + smp_check_aligned(& stop_cpus); + smp_check_aligned(& hlt_pin); + smp_check_aligned(& tmr_poll); + smp_check_aligned(& tmxr_poll); + + init_threads_core(); + sim_timer_init(); + init_threads_ext(); + + smp_stdin = smp_file_wrap(stdin); + smp_stdout = smp_file_wrap(stdout); + smp_stderr = smp_file_wrap(stderr); + + cpu_database_lock = smp_lock::create(smp_spinwait_min_us, 1000, 10000); + cpu_database_lock->set_criticality(SIM_LOCK_CRITICALITY_VM); + cpu_attention = smp_semaphore::create(0); + cpu_clock_run_gate = smp_semaphore::create(0); + cpu_pause_sync_barrier = smp_barrier::create(2); + cpu_cycles_per_second_lock = smp_lock::create(smp_spinwait_min_us, 1000, 3000); + cpu_cycles_per_second_lock->set_criticality(SIM_LOCK_CRITICALITY_VM); + + perf_register_object("cpu_database_lock", cpu_database_lock); + perf_register_object("cpu_cycles_per_second_lock", cpu_cycles_per_second_lock); + + on_init_call::invoke_all(); + +#if defined(__linux) + /* Linux System Monitor tool displays main thread's name as the name of the whole process */ + static char tname[128]; + sprintf(tname, "%s CONSOLE", sim_name); + ptname = tname; +#endif + + smp_set_thread_name(ptname); + } + sim_catch (sim_exception_SimError, exc) + { + /* message here to stderr, not smp_stderr yet */ + fprintf (stderr, "Error: %s\n", exc->get_message()); + exc->checkAutoDelete(); + return 0; + } + sim_end_try + + sim_try + { + return main_exec (vol_argc, argv); + } + sim_catch (sim_exception_SimError, exc) + { + fprintf (smp_stderr, "Error: %s\n", exc->get_message()); + exc->checkAutoDelete(); + return 0; + } + sim_end_try +} + +static int main_exec (int argc, char *argv[]) +{ + char cbuf[CBUFSIZE], gbuf[CBUFSIZE], *cptr; + int32 i, sw; + t_bool lookswitch; + t_stat stat; + CTAB *cmdp; + DEVICE *dptr; + + for (i = 0; (dptr = sim_devices[i]) != NULL; i++) + { + dptr->a_reset_count = 0; + for (uint32 k = 0; k < dptr->numunits; k++) + { + dptr->units[k]->device = dptr; + dptr->units[k]->unitno = k; + if (dptr->flags & DEV_PERCPU) + sim_units_percpu++; + else + sim_units_global++; + } + } + + sim_init_interrupt_info(); + + sim_dflt_cpu = & cpu_unit_0; + sim_dflt_rscx = new run_scope_context(&cpu_unit_0, SIM_THREAD_TYPE_CONSOLE); + sim_dflt_rscx->set_current(); + syncw_init(); + init_cpu_unit_0(); + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CONSOLE_PAUSED); + memzero(cpu_cycles_mark); + memzero(cpu_cycles_sleepexit); + +#if defined (__MWERKS__) && defined (macintosh) + argc = ccommand (&argv); +#endif + + *cbuf = 0; /* init arg buffer */ + sim_switches = 0; /* init switches */ + lookswitch = TRUE; + for (i = 1; i < argc; i++) /* loop thru args */ + { + if (argv[i] == NULL) /* paranoia */ + continue; + if ((*argv[i] == '-') && lookswitch) /* switch? */ + { + if ((sw = get_switches (argv[i])) < 0) + { + fprintf (smp_stderr, "Invalid switch %s\n", argv[i]); + return 0; + } + sim_switches = sim_switches | sw; + } + else + { + if ((strlen (argv[i]) + strlen (cbuf) + 1) >= CBUFSIZE) + { + fprintf (smp_stderr, "Argument string too long\n"); + return 0; + } + if (*cbuf) /* concat args */ + strcat (cbuf, " "); + strcat (cbuf, argv[i]); + lookswitch = FALSE; /* no more switches */ + } + } + sim_quiet = sim_switches & SWMASK ('Q'); /* -q means quiet */ + + if (sim_vm_init != NULL) /* call once only */ + (*sim_vm_init)(); + sim_finit (); /* init fio package */ + setenv ("SIM_NAME", sim_name, 1); /* publish simulator name */ + stop_cpus = 0; + sim_log = NULL; + if (sim_emax <= 0) + sim_emax = 1; + + if ((stat = sim_ttinit ()) != SCPE_OK) + { + fprintf (smp_stderr, "Fatal terminal initialization error\n%s\n", + sim_error_text (stat)); + return 0; + } + if ((sim_requeue_info = (clock_queue_entry_info*) calloc(sim_units_global + 1, sizeof(clock_queue_entry_info))) == NULL) + { + fprintf (smp_stderr, "Unable to allocate memory for internal data buffer\n"); + return 0; + } + if ((sim_eval = (t_value *) calloc (sim_emax, sizeof (t_value))) == NULL) + { + fprintf (smp_stderr, "Unable to allocate examine buffer\n"); + return 0; + } + if ((stat = reset_all_p (0)) != SCPE_OK) + { + fprintf (smp_stderr, "Fatal simulator initialization error\n%s\n", + sim_error_text (stat)); + return 0; + } + if ((stat = sim_brk_init ()) != SCPE_OK) + { + fprintf (smp_stderr, "Fatal breakpoint table initialization error\n%s\n", + sim_error_text (stat)); + return 0; + } + if (!sim_quiet) + { + smp_printf ("\n"); + show_version (smp_stdout, NULL, NULL, 0, NULL); + } + if (sim_dflt_dev == NULL) /* if no default */ + sim_dflt_dev = sim_devices[0]; + + if (*cbuf) /* cmd file arg? */ + stat = do_cmd (0, cbuf); /* proc cmd file */ + else if (*argv[0]) /* sim name arg? */ + { + char nbuf[PATH_MAX + 7], *np; /* "path.ini" */ + nbuf[0] = '"'; /* starting " */ + strncpy (nbuf + 1, argv[0], PATH_MAX + 1); /* copy sim name */ + if (np = match_ext (nbuf, "EXE")) /* remove .exe */ + *np = 0; + strcat (nbuf, ".ini\""); /* add .ini" */ + stat = do_cmd (-1, nbuf); /* proc cmd file */ + } + + while (stat != SCPE_EXIT) /* in case exit */ + { + sim_try + { + cptr = NULL; + + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CONSOLE_PAUSED); + + stat = process_brk_actions (0, & cptr); + if (stat == SCPE_EXIT) break; + + if (cptr != NULL) + { + /* if sim_brk_end_action_script returned "cont" */ + print_prompt (smp_stdout, "brk"); + fprintf(smp_stdout, "%s\n", cptr); + } + else + { + if (sim_vm_read != NULL) /* sim routine? */ + { + print_prompt (smp_stdout); /* prompt */ + cptr = (*sim_vm_read) (cbuf, CBUFSIZE, smp_stdin); + } + else + { + char pmt[32]; + make_prompt (pmt); + cptr = read_line_p (pmt, cbuf, CBUFSIZE, smp_stdin); /* read command line */ + } + } + + if (cptr == NULL) /* EOF? */ + { + if (sim_ttisatty()) + continue; /* ignore tty EOF */ + else + break; /* otherwise exit */ + } + + if (*cptr == 0) /* ignore blank */ + continue; + sub_args (cbuf, gbuf, CBUFSIZE, argv); + if (sim_log) /* log cmd */ + { + print_prompt (sim_log); + fprintf (sim_log, "%s\n", cptr); + } + cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ + sim_switches = 0; /* init switches */ + if (cmdp = find_cmd (gbuf)) /* lookup command */ + { + sim_dflt_rscx->cpu_unit = sim_dflt_cpu; + stat = cmdp->action (cmdp->arg, cptr); /* if found, exec */ + sim_dflt_rscx->cpu_unit = sim_dflt_cpu; + } + else + stat = SCPE_UNK; + if (stat >= SCPE_BASE) /* error? */ + { + smp_printf ("%s\n", sim_error_text (stat)); + if (sim_log) + fprintf (sim_log, "%s\n", sim_error_text (stat)); + } + if (sim_vm_post != NULL) + (*sim_vm_post) (TRUE); + + if (stat != SCPE_OK || cmdp->action != &echo_cmd) + sim_last_cmd_stat = stat; /* save command error status */ + } + sim_catch (sim_exception_SimError, exc) + { + /* last chance handler for out-of-memory and similar critical conditions + during console command execution, without losing the simulator's general state: + reset pending actions, issue message and drop to console prompt */ + sim_brk_action_stack.reset(); + sim_brk_clract(); + sim_brk_continue = FALSE; + fprintf (smp_stderr, "\nSevere error while handling commands: %s\n", exc->get_message()); + if (sim_log) + fprintf (sim_log, "\nSevere error while handling commands: %s\n", exc->get_message()); + exc->checkAutoDelete(); + } + sim_end_try + } + + detach_all (0, TRUE); /* close files */ + sim_set_deboff (0, NULL); /* close debug */ + sim_set_logoff (0, NULL); /* close log */ + sim_set_notelnet (0, NULL); /* close Telnet */ + sim_ttclose (); /* close console */ + return 0; +} + +t_stat process_new_brk_actions (int flag) +{ + if (sim_brk_is_action_pending ()) + return process_brk_actions (flag, NULL); + else + return SCPE_OK; +} + +t_stat process_brk_actions (int flag, char** ppcmd) +{ + sim_cstream* brk_script = sim_brk_get_action_script (); + t_stat stat = SCPE_OK; + char* dummy; + + if (brk_script) + { + if (ppcmd == NULL) + ppcmd = &dummy; + + int32 sv_sim_switches = sim_switches; + sim_switches |= SWMASK ('V'); + sim_switches &= ~SWMASK ('E'); + + stat = do_script (flag, brk_script, "brk"); + *ppcmd = (char*) sim_brk_end_action_script (brk_script); + + int32 rmask = SWMASK ('V') | SWMASK ('E'); + sv_sim_switches &= ~rmask; + sv_sim_switches |= sv_sim_switches & rmask; + } + + return stat; +} + +static void print_prompt (SMP_FILE* fp, const char* component) +{ + char pmt[32]; + make_prompt (pmt, component); + fprintf(fp, "%s", pmt); +} + +static void make_prompt (char* bp, const char* component) +{ + if (component == NULL) + component = "sim"; + + if (sim_dflt_dev != NULL) + { + if (sim_dflt_dev == &cpu_dev && sim_ncpus == 1) + { + sprintf (bp, "%s> ", component); + } + else if (sim_dflt_dev == &cpu_dev && sim_ncpus != 1) + { + sprintf (bp, "%s-cpu%d> ", component, sim_dflt_cpu->cpu_id); + } + else + { + sprintf (bp, "%s-%s> ", component, sim_dflt_dev->name); + } + } + else if (sim_ncpus != 1) + { + sprintf (bp, "%s-cpu%d> ", component, sim_dflt_cpu->cpu_id); + } + else + { + sprintf (bp, "%s> ", component); + } +} + +void check_select_cpu(UNIT* uptr) +{ + if (uptr && uptr->is_cpu()) + { + sim_dflt_rscx->cpu_unit = (CPU_UNIT*) uptr; + } +} + +/* Find command routine */ + +CTAB *find_cmd (char *gbuf) +{ +CTAB *cmdp = NULL; + +if (sim_vm_cmd) /* try ext commands */ + cmdp = find_ctab (sim_vm_cmd, gbuf); +if (cmdp == NULL) /* try regular cmds */ + cmdp = find_ctab (cmd_table, gbuf); +return cmdp; +} + +/* Exit command */ + +t_stat exit_cmd (int32 flag, char *cptr) +{ +return SCPE_EXIT; +} + +/* Help command */ + +void fprint_help (SMP_FILE *st) +{ +CTAB *cmdp; + +for (cmdp = sim_vm_cmd; cmdp && (cmdp->name != NULL); cmdp++) { + if (cmdp->help) + fputs (cmdp->help, st); + } +for (cmdp = cmd_table; cmdp && (cmdp->name != NULL); cmdp++) { + if (cmdp->help && (!sim_vm_cmd || !find_ctab (sim_vm_cmd, cmdp->name))) + fputs (cmdp->help, st); + } +return; +} + +t_stat help_cmd (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +CTAB *cmdp; + +GET_SWITCHES (cptr); +if (*cptr) { + cptr = get_glyph (cptr, gbuf, 0); + if (*cptr) + return SCPE_2MARG; + if (cmdp = find_cmd (gbuf)) { + fputs (cmdp->help, smp_stdout); + if (sim_log) + fputs (cmdp->help, sim_log); + } + else return SCPE_ARG; + } +else { + fprint_help (smp_stdout); + if (sim_log) + fprint_help (sim_log); + } +return SCPE_OK; +} + +/* Spawn command */ + +t_stat spawn_cmd (int32 flag, char *cptr) +{ +t_stat status; +if ((cptr == NULL) || (strlen (cptr) == 0)) + cptr = getenv("SHELL"); +if ((cptr == NULL) || (strlen (cptr) == 0)) + cptr = getenv("ComSpec"); +#if defined (VMS) +if ((cptr == NULL) || (strlen (cptr) == 0)) + cptr = "SPAWN/INPUT=SYS$COMMAND:"; +#endif +fflush(smp_stdout); /* flush stdout */ +if (sim_log) /* flush log if enabled */ + fflush (sim_log); +status = system (cptr); +#if defined (VMS) +printf ("\n"); +#endif + +return status; +} + +/* Echo command */ + +t_stat echo_cmd (int32 flag, char *cptr) +{ +puts (cptr); +if (sim_log) + fprintf (sim_log, "%s\n", cptr); +return SCPE_OK; +} + +/* Do command + + Syntax: DO {-E} {-V} {...} + + -E causes all command errors to be fatal; without it, only EXIT and ASSERT + failure will stop a command file. + + -V causes commands to be echoed before execution. + + Note that SCPE_STEP ("Step expired") is considered a note and not an error + and so does not abort command execution when using -E. + + Inputs: + flag = caller and nesting level indicator + fcptr = filename and optional arguments, space-separated + Outputs: + status = error status + + The "flag" input value indicates the source of the call, as follows: + + -1 = initialization file (no error if not found) + 0 = command line file + 1 = "DO" command + >1 = nested "DO" command +*/ + +#define SCPE_DOFAILED 0040000 /* fail in DO, not subproc */ + +t_stat do_cmd (int32 flag, char *fcptr) +{ + char *cptr, cbuf[CBUFSIZE], gbuf[CBUFSIZE], *c, quote, *do_arg[10]; + SMP_FILE *fpin; + CTAB *cmdp; + int32 echo = sim_do_echo, nargs, errabort; + t_bool interactive, isdo, staying; + t_stat stat; + char *ocptr; + + stat = SCPE_OK; + staying = TRUE; + interactive = (flag > 0); /* issued interactively? */ + if (interactive) /* get switches */ + { + GET_SWITCHES (fcptr); + } + if (sim_switches & SWMASK ('V')) /* -v means echo */ + echo = 1; + errabort = sim_switches & SWMASK ('E'); /* -e means abort on error */ + + c = fcptr; + for (nargs = 0; nargs < 10; ) /* extract arguments */ + { + while (isspace (*c)) /* skip blanks */ + c++; + if (*c == 0) /* all done? */ + do_arg [nargs++] = NULL; /* null argument */ + else + { + if (*c == '\'' || *c == '"') /* quoted string? */ + quote = *c++; + else quote = 0; + do_arg[nargs++] = c; /* save start */ + while (*c && (quote ? (*c != quote) : !isspace (*c))) + c++; + if (*c) /* term at quote/spc */ + *c++ = 0; + } + } + + if (nargs <= 0 || do_arg[0] == NULL) /* need at least 1 */ + return SCPE_2FARG; + if ((fpin = smp_fopen (do_arg[0], "r")) == NULL) /* file failed to open? */ + { + if (flag == 0) /* cmd line file? */ + fprintf (smp_stderr, "Can't open file %s\n", do_arg[0]); + if (flag > 1) + return SCPE_OPENERR | SCPE_DOFAILED; /* return failure with flag */ + else + return SCPE_OPENERR; /* return failure */ + } + if (flag < 1) /* start at level 1 */ + flag = 1; + + do + { + stat = process_new_brk_actions (flag); /* process break actions if pending in the CPUs */ + if (stat == SCPE_EXIT) + return stat; + + ocptr = cptr = read_line (cbuf, CBUFSIZE, fpin); /* get cmd line */ + sub_args (cbuf, gbuf, CBUFSIZE, do_arg); /* substitute args */ + if (cptr == NULL) /* EOF? */ + { + stat = SCPE_OK; /* set good return */ + break; + } + if (*cptr == 0) /* ignore blank */ + continue; + if (echo) /* echo if -v */ + smp_printf("do> %s\n", cptr); + if (echo && sim_log) + fprintf (sim_log, "do> %s\n", cptr); + cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ + sim_switches = 0; /* init switches */ + isdo = FALSE; + sim_do_echo = echo; + if (cmdp = find_cmd (gbuf)) /* lookup command */ + { + isdo = (cmdp->action == &do_cmd); + if (isdo) /* DO command? */ + { + if (flag >= MAX_DO_NEST_LVL) /* nest too deep? */ + stat = SCPE_NEST; + else + { + sim_dflt_rscx->cpu_unit = sim_dflt_cpu; + stat = do_cmd (flag + 1, cptr); /* exec DO cmd */ + sim_dflt_rscx->cpu_unit = sim_dflt_cpu; + } + } + else + { + sim_dflt_rscx->cpu_unit = sim_dflt_cpu; + stat = cmdp->action (cmdp->arg, cptr); /* exec other cmd */ + sim_dflt_rscx->cpu_unit = sim_dflt_cpu; + } + } + else + { + stat = SCPE_UNK; /* bad cmd given */ + } + staying = (stat != SCPE_EXIT) && /* decide if staying */ + (stat != SCPE_AFAIL) && + (!errabort || (stat < SCPE_BASE) || (stat == SCPE_STEP)); + + if (stat != SCPE_OK || cmdp->action != &echo_cmd) + sim_last_cmd_stat = stat; /* save command error status */ + + if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) && /* error from cmd? */ + (stat != SCPE_STEP)) + { + if (!echo && !sim_quiet && /* report if not echoing */ + (!isdo || (stat & SCPE_DOFAILED))) /* and not from DO return */ + { + smp_printf("%s> %s\n", do_arg[0], ocptr); + if (sim_log) + fprintf (sim_log, "%s> %s\n", do_arg[0], ocptr); + } + stat = stat & ~SCPE_DOFAILED; /* remove possible flag */ + } + if ((staying || !interactive) && /* report error if staying */ + (stat >= SCPE_BASE) && !isdo) /* or in cmdline file */ + { + smp_printf ("%s\n", sim_error_text (stat)); + if (sim_log) + fprintf (sim_log, "%s\n", sim_error_text (stat)); + } + if (sim_vm_post != NULL) + (*sim_vm_post) (TRUE); + } + while (staying); + + fclose (fpin); /* close file */ + sim_do_echo = 0; + + return stat; +} + +t_stat do_script (int32 flag, sim_cstream* script, const char* prompt) +{ + char *cptr, cbuf[CBUFSIZE], gbuf[CBUFSIZE]; + CTAB *cmdp; + int32 echo, errabort; + t_bool isdo, staying; + t_stat stat; + char *ocptr; + + stat = SCPE_OK; + staying = TRUE; + echo = sim_switches & SWMASK ('V'); /* -v means echo */ + errabort = sim_switches & SWMASK ('E'); /* -e means abort on error */ + + do + { + stat = process_new_brk_actions (flag); /* process break actions if pending in the CPUs */ + if (stat == SCPE_EXIT) + return stat; + + ocptr = cptr = script->read_line (cbuf, CBUFSIZE); /* get cmd line */ + if (cptr == NULL) /* EOF? */ + { + stat = SCPE_OK; /* set good return */ + break; + } + if (*cptr == 0) /* ignore blank */ + continue; + if (echo) /* echo if -v */ + { + print_prompt (smp_stdout, prompt); + smp_printf("%s\n", cptr); + } + if (echo && sim_log) + { + print_prompt (sim_log, prompt); + fprintf(sim_log, "%s\n", cptr); + } + cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ + sim_switches = 0; /* init switches */ + isdo = FALSE; + if (cmdp = find_cmd (gbuf)) /* lookup command */ + { + isdo = (cmdp->action == &do_cmd); + if (isdo) /* DO command? */ + { + if (flag >= MAX_DO_NEST_LVL) /* nest too deep? */ + stat = SCPE_NEST; + else + { + sim_dflt_rscx->cpu_unit = sim_dflt_cpu; + stat = do_cmd (flag + 1, cptr); /* exec DO cmd */ + sim_dflt_rscx->cpu_unit = sim_dflt_cpu; + } + } + else + { + sim_dflt_rscx->cpu_unit = sim_dflt_cpu; + stat = cmdp->action (cmdp->arg, cptr); /* exec other cmd */ + sim_dflt_rscx->cpu_unit = sim_dflt_cpu; + } + } + else + { + stat = SCPE_UNK; /* bad cmd given */ + } + staying = (stat != SCPE_EXIT) && /* decide if staying */ + (stat != SCPE_AFAIL) && + (!errabort || (stat < SCPE_BASE) || (stat == SCPE_STEP)); + if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) && /* error from cmd? */ + (stat != SCPE_STEP)) + { + if (!echo && !sim_quiet && /* report if not echoing */ + (!isdo || (stat & SCPE_DOFAILED))) /* and not from DO return */ + { + print_prompt (smp_stdout, prompt); + smp_printf("%s\n", ocptr); + if (sim_log) + { + print_prompt (sim_log, prompt); + fprintf(sim_log, "%s\n", ocptr); + } + } + stat = stat & ~SCPE_DOFAILED; /* remove possible flag */ + } + if (stat >= SCPE_BASE) + { + smp_printf ("%s\n", sim_error_text (stat)); + if (sim_log) + fprintf (sim_log, "%s\n", sim_error_text (stat)); + } + if (sim_vm_post != NULL) + (*sim_vm_post) (TRUE); + } + while (staying); + + return stat; +} + +/* Substitute_args - replace %n tokens in 'instr' with the do command's arguments + and other enviroment variables + + Calling sequence + instr = input string + tmpbuf = temp buffer + maxstr = min (len (instr), len (tmpbuf)) + do_arg[10] = arguments + + Token "%0" represents the command file name. + + The input sequence "\%" represents a literal "%", and "\\" represents a + literal "\". All other character combinations are rendered literally. + + Omitted parameters result in null-string substitutions. +*/ + +void sub_args (char *instr, char *tmpbuf, int32 maxstr, char *do_arg[]) +{ +char *ip, *op, *ap, *oend = tmpbuf + maxstr - 2; + +for (ip = instr, op = tmpbuf; *ip && (op < oend); ) { + if ((ip [0] == '\\') && /* literal escape? */ + ((ip [1] == '%') || (ip [1] == '\\'))) { /* and followed by '%' or '\'? */ + ip++; /* skip '\' */ + *op++ = *ip++; /* copy escaped char */ + } + else + if (*ip == '%') { /* sub? */ + if ((ip[1] >= '0') && (ip[1] <= ('9'))) { /* %n = sub */ + ap = do_arg[ip[1] - '0']; + ip = ip + 2; + } + else { /* environment variable */ + char gbuf[CBUFSIZE]; + + ap = NULL; + get_glyph_gen (ip+1, gbuf, '%', FALSE); + ip += 1 + strlen (gbuf); + if (*ip == '%') ++ip; + ap = getenv(gbuf); + if (!ap) { + static char rbuf[CBUFSIZE]; + time_t now; + struct tm *tmnow; + + time(&now); + tmnow = localtime(&now); + if (!strcmp ("DATE", gbuf)) { + sprintf (rbuf, "%4d/%02d/%02d", tmnow->tm_year+1900, tmnow->tm_mon+1, tmnow->tm_mday); + ap = rbuf; + } + else if (!strcmp ("TIME", gbuf)) { + sprintf (rbuf, "%02d:%02d:%02d", tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec); + ap = rbuf; + } + else if (!strcmp ("CTIME", gbuf)) { + strcpy (rbuf, ctime(&now)); + rbuf[strlen (rbuf)-1] = '\0'; /* remove trailing \n */ + ap = rbuf; + } + else if (!strcmp ("STATUS", gbuf)) { + sprintf (rbuf, "%08X", sim_last_cmd_stat); + ap = rbuf; + } + else if (!strcmp ("TSTATUS", gbuf)) { + sprintf (rbuf, "%s", sim_error_text (sim_last_cmd_stat)); + ap = rbuf; + } + } + } + if (ap) { /* non-null arg? */ + while (*ap && (op < oend)) /* copy the argument */ + *op++ = *ap++; + } + } + else + *op++ = *ip++; /* literal character */ + } +*op = 0; /* term buffer */ +strcpy (instr, tmpbuf); +return; +} + +/* Assert command + + Syntax: ASSERT {} {} + + If is not specified, CPU is assumed. is expressed in the radix + specified for . and are the same as that + allowed for examine and deposit search specifications. */ + +t_stat assert_cmd (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE], *gptr, *tptr; +REG *rptr; +uint32 idx; +t_value val; +t_stat r; + +cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_DFT, cptr, &r); /* get sw, default */ +if (*cptr == 0) /* must be more */ + return SCPE_2FARG; +cptr = get_glyph (cptr, gbuf, 0); /* get register */ +rptr = find_reg (gbuf, &gptr, sim_dfdev); /* parse register */ +if (!rptr) /* not there */ + return SCPE_NXREG; +if (*gptr == '[') { /* subscript? */ + if (rptr->depth <= 1) /* array register? */ + return SCPE_ARG; + idx = (uint32) strtotv (++gptr, &tptr, 10); /* convert index */ + if ((gptr == tptr) || (*tptr++ != ']')) + return SCPE_ARG; + gptr = tptr; /* update */ + } +else idx = 0; /* not array */ +if (idx >= rptr->depth) /* validate subscript */ + return SCPE_SUB; +if (*gptr != 0) /* more? must be search */ + get_glyph (gptr, gbuf, 0); +else { + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + cptr = get_glyph (cptr, gbuf, 0); /* get search cond */ + } +if (*cptr != 0) /* must be done */ + return SCPE_2MARG; +if (!get_search (gbuf, rptr->radix, &sim_stab)) /* parse condition */ + return SCPE_MISVAL; +val = get_rval (rptr, idx); /* get register value */ +if (test_search (val, &sim_stab)) /* test condition */ + return SCPE_OK; +return SCPE_AFAIL; /* condition fails */ +} + +/* Set command */ + +static CTAB set_glob_tab[] = { + { "CONSOLE", &sim_set_console, 0 }, + { "BREAK", &brk_cmd, SSH_ST }, + { "NOBREAK", &brk_cmd, SSH_CL }, + { "TELNET", &sim_set_telnet, 0 }, /* deprecated */ + { "NOTELNET", &sim_set_notelnet, 0 }, /* deprecated */ + { "LOG", &sim_set_logon, 0 }, /* deprecated */ + { "NOLOG", &sim_set_logoff, 0 }, /* deprecated */ + { "DEBUG", &sim_set_debon, 0 }, /* deprecated */ + { "NODEBUG", &sim_set_deboff, 0 }, /* deprecated */ + { "THROTTLE", &sim_set_throt, 1 }, + { "NOTHROTTLE", &sim_set_throt, 0 }, + { "ASYNCH", &sim_set_asynch, 1 }, + { "NOASYNCH", &sim_set_asynch, 0 }, + { "ENV", &sim_set_environment, 1 }, + { NULL, NULL, 0 } + }; + +static C1TAB set_dev_tab[] = { + { "OCTAL", &set_dev_radix, 8 }, + { "DECIMAL", &set_dev_radix, 10 }, + { "HEX", &set_dev_radix, 16 }, + { "ENABLED", &set_dev_enbdis, 1 }, + { "DISABLED", &set_dev_enbdis, 0 }, + { "DEBUG", &set_dev_debug, 1 }, + { "NODEBUG", &set_dev_debug, 0 }, + { NULL, NULL, 0 } + }; + +static C1TAB set_unit_tab[] = { + { "ENABLED", &set_unit_enbdis, 1 }, + { "DISABLED", &set_unit_enbdis, 0 }, + { NULL, NULL, 0 } + }; + + +/* Set asynch/noasynch routine */ + +t_stat sim_set_asynch (int32 flag, char *cptr) +{ + if (cptr && (*cptr != 0)) /* now eol? */ + return SCPE_2MARG; + if (flag == sim_asynch_enabled) /* already set correctly? */ + return SCPE_OK; + sim_asynch_enabled = flag; + if (1) + { + uint32 i, j; + DEVICE *dptr; + UNIT *uptr; + + /* Call unit flush routines to report asynch status change to device layer */ + for (i = 1; (dptr = sim_devices[i]) != NULL; i++) /* flush attached files */ + { + for (j = 0; j < dptr->numunits; j++) /* if not buffered in mem */ + { + uptr = dptr->units[j]; + if ((uptr->flags & UNIT_ATT) && /* attached, */ + !(uptr->flags & UNIT_BUF) && /* not buffered, */ + (uptr->fileref)) /* real file, */ + if (uptr->io_flush) /* unit specific flush routine */ + uptr->io_flush (uptr); + } + } + } + if (!sim_quiet) + smp_printf ("Asynchronous I/O %sabled\n", sim_asynch_enabled ? "en" : "dis"); + if (sim_log) + fprintf (sim_log, "Asynchronous I/O %sabled\n", sim_asynch_enabled ? "en" : "dis"); + return SCPE_OK; +} + +/* Show asynch routine */ + +t_stat sim_show_asynch (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ + if (cptr && (*cptr != 0)) + return SCPE_2MARG; + fprintf (st, "Asynchronous I/O is %sabled\n", (sim_asynch_enabled) ? "en" : "dis"); + return SCPE_OK; +} + +/* Set environment routine */ + +t_stat sim_set_environment (int32 flag, char *cptr) +{ + char varname[CBUFSIZE]; + + if ((!cptr) || (*cptr == 0)) /* now eol? */ + return SCPE_2FARG; + cptr = get_glyph_gen (cptr, varname, '=', FALSE); /* get environment variable name */ + setenv(varname, cptr, 1); + return SCPE_OK; +} + +t_stat set_cmd (int32 flag, char *cptr) +{ + int32 lvl; + t_stat r; + char gbuf[CBUFSIZE], *cvptr, *svptr; + DEVICE *dptr; + UNIT *uptr; + MTAB *mptr; + CTAB *gcmdp; + C1TAB *ctbr, *glbr; + + GET_SWITCHES (cptr); /* get switches */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + cptr = get_glyph (cptr, gbuf, 0); /* get glob/dev/unit */ + + if (dptr = find_dev (gbuf)) /* device match? */ + { + if (dptr == &cpu_dev && sim_dflt_cpu) + uptr = sim_dflt_cpu; /* selected CPU unit */ + else + uptr = dptr->units[0]; /* unit 0 */ + ctbr = set_dev_tab; /* global table */ + lvl = MTAB_VDV; /* device match */ + } + else if (dptr = find_unit (gbuf, &uptr)) /* unit match? */ + { + if (uptr == NULL) /* invalid unit */ + return SCPE_NXUN; + ctbr = set_unit_tab; /* global table */ + lvl = MTAB_VUN; /* unit match */ + } + else if (gcmdp = find_ctab (set_glob_tab, gbuf)) /* global? */ + return gcmdp->action (gcmdp->arg, cptr); /* do the rest */ + else return SCPE_NXDEV; /* no match */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + + check_select_cpu(uptr); + + while (*cptr != 0) /* do all mods */ + { + cptr = get_glyph (svptr = cptr, gbuf, ','); /* get modifier */ + if (cvptr = strchr (gbuf, '=')) /* = value? */ + *cvptr++ = 0; + for (mptr = dptr->modifiers; mptr && mptr->mask != 0; mptr++) + { + if ((mptr->mstring) && /* match string */ + (MATCH_CMD (gbuf, mptr->mstring) == 0)) /* matches option? */ + { + if (mptr->mask & MTAB_XTD) /* extended? */ + { + if ((lvl & mptr->mask) == 0) + return SCPE_ARG; + if ((lvl & MTAB_VUN) && (uptr->flags & UNIT_DIS)) + return SCPE_UDIS; /* unit disabled? */ + if (mptr->valid) /* validation rtn? */ + { + if (cvptr && (mptr->mask & MTAB_NC)) + { + get_glyph_nc (svptr, gbuf, ','); + if (cvptr = strchr (gbuf, '=')) + *cvptr++ = 0; + } + r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc); + if (r != SCPE_OK) + return r; + } + else if (!mptr->desc) /* value desc? */ + break; + // else if (mptr->mask & MTAB_VAL) { /* take a value? */ + // if (!cvptr) return SCPE_MISVAL; /* none? error */ + // r = dep_reg (0, cvptr, (REG *) mptr->desc, 0); + // if (r != SCPE_OK) return r; + // } + else if (cvptr) /* = value? */ + return SCPE_ARG; + else + *((int32 *) mptr->desc) = mptr->match; + } /* end if xtd */ + else /* old style */ + { + if (cvptr) /* = value? */ + return SCPE_ARG; + if (uptr->flags & UNIT_DIS) /* disabled? */ + return SCPE_UDIS; + if ((mptr->valid) && /* invalid? */ + ((r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc)) != SCPE_OK)) + return r; + uptr->flags = (uptr->flags & ~(mptr->mask)) | + (mptr->match & mptr->mask); /* set new value */ + if (uptr->is_cpu()) + cpu_sync_flags((CPU_UNIT*) uptr); + } /* end else xtd */ + break; /* terminate for */ + } /* end if match */ + } /* end for */ + + if (!mptr || (mptr->mask == 0)) /* no match? */ + { + if (glbr = find_c1tab (ctbr, gbuf)) /* global match? */ + { + r = glbr->action (dptr, uptr, glbr->arg, cvptr); /* do global */ + if (r != SCPE_OK) + return r; + } + else if (!dptr->modifiers) /* no modifiers? */ + return SCPE_NOPARAM; + else + return SCPE_NXPAR; + } /* end if no mat */ + } /* end while */ + return SCPE_OK; /* done all */ +} + +/* Match CTAB/CTAB1 name */ + +CTAB *find_ctab (CTAB *tab, const char *gbuf) +{ +for (; tab->name != NULL; tab++) { + if (MATCH_CMD (gbuf, tab->name) == 0) + return tab; + } +return NULL; +} + +C1TAB *find_c1tab (C1TAB *tab, const char *gbuf) +{ +for (; tab->name != NULL; tab++) { + if (MATCH_CMD (gbuf, tab->name) == 0) + return tab; + } +return NULL; +} + +/* Set device data radix routine */ + +t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (cptr) + return SCPE_ARG; +dptr->dradix = flag & 037; +return SCPE_OK; +} + +/* Set device enabled/disabled routine */ + +t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ + UNIT *up; + uint32 i; + + if (cptr) + return SCPE_ARG; + + if ((dptr->flags & DEV_DISABLE) == 0) /* allowed? */ + return SCPE_NOFNC; + + if (dptr->flags & DEV_PERCPU) /* safety check */ + return SCPE_NOFNC; + + if (flag) /* enable? */ + { + if ((dptr->flags & DEV_DIS) == 0) /* already enb? ok */ + return SCPE_OK; + dptr->flags = dptr->flags & ~DEV_DIS; /* no, enable */ + } + else + { + if (dptr->flags & DEV_DIS) /* already dsb? ok */ + return SCPE_OK; + for (i = 0; i < dptr->numunits; i++) /* check units */ + { + up = dptr->units[i]; /* att or active? */ + if ((up->flags & UNIT_ATT) || sim_is_active (up)) + return SCPE_NOFNC; /* can't do it */ + } + dptr->flags = dptr->flags | DEV_DIS; /* disable */ + } + + return reset_dev_allcpus (dptr); /* reset device */ +} + +/* Set unit enabled/disabled routine */ + +t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ + if (cptr) + return SCPE_ARG; + if (!(uptr->flags & UNIT_DISABLE)) /* allowed? */ + return SCPE_NOFNC; + if (flag) /* enb? enable */ + uptr->flags = uptr->flags & ~UNIT_DIS; + else { + if ((uptr->flags & UNIT_ATT) || /* dsb */ + sim_is_active (uptr)) /* more tests */ + return SCPE_NOFNC; + uptr->flags = uptr->flags | UNIT_DIS; /* disable */ + } + return SCPE_OK; +} + +/* Set device debug enabled/disabled routine */ + +t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +DEBTAB *dep; + +if ((dptr->flags & DEV_DEBUG) == 0) + return SCPE_NOFNC; +if (cptr == NULL) { /* no arguments? */ + dptr->dctrl = flag; /* disable/enable w/o table */ + if (flag && dptr->debflags) { /* enable with table? */ + for (dep = dptr->debflags; dep->name != NULL; dep++) + dptr->dctrl = dptr->dctrl | dep->mask; /* set all */ + } + return SCPE_OK; + } +if (dptr->debflags == NULL) /* must have table */ + return SCPE_ARG; +while (*cptr) { + cptr = get_glyph (cptr, gbuf, ';'); /* get debug flag */ + for (dep = dptr->debflags; dep->name != NULL; dep++) { + if (strcmp (dep->name, gbuf) == 0) { /* match? */ + if (flag) + dptr->dctrl = dptr->dctrl | dep->mask; + else dptr->dctrl = dptr->dctrl & ~dep->mask; + break; + } + } /* end for */ + if (dep->mask == 0) /* no match? */ + return SCPE_ARG; + } /* end while */ +return SCPE_OK; +} + +/* Show command */ + +t_stat show_cmd (int32 flag, char *cptr) +{ +t_stat r; + +cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_OF, cptr, &r); /* get sw, ofile */ +if (!cptr) /* error? */ + return r; +if (sim_ofile) { /* output file? */ + r = show_cmd_fi (sim_ofile, flag, cptr); /* do show */ + fclose (sim_ofile); + } +else { + r = show_cmd_fi (smp_stdout, flag, cptr); /* no, stdout, log */ + if (sim_log) + show_cmd_fi (sim_log, flag, cptr); + } +return r; +} + +static SHTAB show_glob_tab[] = { + { "CONFIGURATION", &show_config, 0 }, + { "DEVICES", &show_config, 1 }, + { "QUEUE", &show_queue, 0 }, + { "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 }, + { "LOG", &sim_show_log, 0 }, /* deprecated */ + { "TELNET", &sim_show_telnet, 0 }, /* deprecated */ + { "DEBUG", &sim_show_debug, 0 }, /* deprecated */ + { "THROTTLE", &sim_show_throt, 0 }, + { "ASYNCH", &sim_show_asynch, 0 }, + { NULL, NULL, 0 } + }; + +static SHTAB show_dev_tab[] = { + { "RADIX", &show_dev_radix, 0 }, + { "DEBUG", &show_dev_debug, 0 }, + { "MODIFIERS", &show_dev_modifiers, 0 }, + { "NAMES", &show_dev_logicals, 0 }, + { "SHOW", &show_dev_show_commands, 0 }, + { NULL, NULL, 0 } + }; + +static SHTAB show_unit_tab[] = { + { NULL, NULL, 0 } + }; + + +t_stat show_cmd_fi (SMP_FILE *ofile, int32 flag, char *cptr) +{ + int32 lvl; + char gbuf[CBUFSIZE], *cvptr; + DEVICE *dptr; + UNIT *uptr; + MTAB *mptr; + SHTAB *shtb, *shptr; + + GET_SWITCHES (cptr); /* get switches */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (shptr = find_shtab (show_glob_tab, gbuf)) /* global? */ + return shptr->action (ofile, NULL, NULL, shptr->arg, cptr); + + if (dptr = find_dev (gbuf)) /* device match? */ + { + if (dptr == &cpu_dev && sim_dflt_cpu) + uptr = sim_dflt_cpu; /* selected CPU unit */ + else + uptr = dptr->units[0]; /* unit 0 */ + shtb = show_dev_tab; /* global table */ + lvl = MTAB_VDV; /* device match */ + } + else if (dptr = find_unit (gbuf, &uptr)) /* unit match? */ + { + if (uptr == NULL) /* invalid unit */ + return SCPE_NXUN; + if (uptr->flags & UNIT_DIS) /* disabled? */ + return SCPE_UDIS; + shtb = show_unit_tab; /* global table */ + lvl = MTAB_VUN; /* unit match */ + } + else + return SCPE_NXDEV; /* no match */ + + if (*cptr == 0) /* now eol? */ + { + return (lvl == MTAB_VDV)? + show_device (ofile, dptr, 0): + show_unit (ofile, dptr, uptr, -1); + } + + if (dptr->modifiers == NULL) /* any modifiers? */ + return SCPE_NOPARAM; + + check_select_cpu(uptr); + + while (*cptr != 0) /* do all mods */ + { + cptr = get_glyph (cptr, gbuf, ','); /* get modifier */ + if (cvptr = strchr (gbuf, '=')) /* = value? */ + *cvptr++ = 0; + for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) + { + if (((mptr->mask & MTAB_XTD)? /* right level? */ + (mptr->mask & lvl): (MTAB_VUN & lvl)) && + ((mptr->disp && mptr->pstring && /* named disp? */ + (MATCH_CMD (gbuf, mptr->pstring) == 0)) + // || + // ((mptr->mask & MTAB_VAL) && /* named value? */ + // mptr->mstring && + // (MATCH_CMD (gbuf, mptr->mstring) == 0))) + )) + { + if (cvptr && !(mptr->mask & MTAB_SHP)) + return SCPE_ARG; + show_one_mod (ofile, dptr, uptr, mptr, cvptr, 1); + break; + } /* end if */ + } /* end for */ + + if (mptr->mask == 0) /* no match? */ + { + if (shptr = find_shtab (shtb, gbuf)) /* global match? */ + shptr->action (ofile, dptr, uptr, shptr->arg, cptr); + else + return SCPE_ARG; + } /* end if */ + } /* end while */ + return SCPE_OK; +} + +SHTAB *find_shtab (SHTAB *tab, const char *gbuf) +{ + for (; tab->name != NULL; tab++) { + if (MATCH_CMD (gbuf, tab->name) == 0) + return tab; + } + return NULL; +} + +/* Show device and unit */ + +t_stat show_device (SMP_FILE *st, DEVICE *dptr, int32 flag) +{ + uint32 j, udbl, ucnt; + UNIT *uptr; + + fprintf (st, "%s", sim_dname (dptr)); /* print dev name */ + if (qdisable (dptr)) { /* disabled? */ + fprintf (st, ", disabled\n"); + return SCPE_OK; + } + for (j = ucnt = udbl = 0; j < dptr->numunits; j++) { /* count units */ + uptr = dptr->units[j]; + if (uptr->flags & UNIT_DISABLE) + udbl++; + if (!(uptr->flags & UNIT_DIS)) + ucnt++; + } + check_select_cpu(dptr->units[0]); + show_all_mods (st, dptr, dptr->units[0], MTAB_VDV); /* show dev mods */ + if (dptr->numunits == 0) + fprintf (st, "\n"); + else { + if (udbl && (ucnt == 0)) + fprintf (st, ", all units disabled\n"); + else if (ucnt > 1) + fprintf (st, ", %d units\n", ucnt); + else if (flag) + fprintf (st, "\n"); + } + if (flag) /* dev only? */ + return SCPE_OK; + for (j = 0; j < dptr->numunits; j++) { /* loop thru units */ + uptr = dptr->units[j]; + if ((uptr->flags & UNIT_DIS) == 0) + show_unit (st, dptr, uptr, ucnt); + } + return SCPE_OK; +} + +t_stat show_unit (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag) +{ + check_select_cpu(uptr); + int32 u = sim_unit_index (uptr); + + if (flag > 1) + fprintf (st, " %s%d", sim_dname (dptr), u); + else if (flag < 0) + fprintf (st, "%s%d", sim_dname (dptr), u); + if (uptr->flags & UNIT_FIX) { + fprintf (st, ", "); + fprint_capac (st, dptr, uptr); + } + if (uptr->flags & UNIT_ATT) { + fprintf (st, ", attached to %s", uptr->filename); + if (uptr->flags & UNIT_RO) + fprintf (st, ", read only"); + } + else if (uptr->flags & UNIT_ATTABLE) + fprintf (st, ", not attached"); + show_all_mods (st, dptr, uptr, MTAB_VUN); /* show unit mods */ + if (uptr->is_cpu()) + { + CPU_UNIT* cpu_unit = (CPU_UNIT*) uptr; + fprintf (st, ", %s", cpu_describe_state(cpu_unit)); + } + fprintf (st, "\n"); + return SCPE_OK; +} + +void fprint_capac (SMP_FILE *st, DEVICE *dptr, UNIT *uptr) +{ + t_addr kval = (uptr->flags & UNIT_BINK)? 1024: 1000; + t_addr mval = kval * kval; + t_addr psize = uptr->capac; + char scale, width; + + if ((dptr->dwidth / dptr->aincr) > 8) + width = 'W'; + else width = 'B'; + if (uptr->capac < (kval * 10)) + scale = 0; + else if (uptr->capac < (mval * 10)) { + scale = 'K'; + psize = psize / kval; + } + else { + scale = 'M'; + psize = psize / mval; + } + fprint_val (st, (t_value) psize, 10, T_ADDR_W, PV_LEFT); + if (scale) + fputc (scale, st); + fputc (width, st); + return; +} + +/* Show processors */ + +t_stat show_version (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ + int32 vmaj = SIM_MAJOR, vmin = SIM_MINOR, vpat = SIM_PATCH, vdelt = SIM_DELTA; + + if (cptr && (*cptr != 0)) + return SCPE_2MARG; + fprintf (st, "%s simulator V%d.%d-%d", sim_name, vmaj, vmin, vpat); + if (vdelt) + fprintf (st, "(%d)", vdelt); + if (flag) + fprintf (st, " [%s, %s, %s]", sim_si64, sim_sa64, sim_snet); + fprintf (st, "\n"); + + +#if defined(VM_VAX_MP) + t_bool print_nl = FALSE; + +# if defined(VSMP_REVISION) + fprintf(st, "vSMP revision: %d", VSMP_REVISION); + print_nl = TRUE; +# endif + + const char* ilock = "portable only"; +# if SMP_NATIVE_INTERLOCKED + ilock = use_native_interlocked ? "native/portable" : "portable/native"; +# endif + fprintf(st, "%sprimary interlock: %s", print_nl ? ", " : "", ilock); + print_nl = TRUE; + + if (print_nl) fprintf(st, "\n"); +#endif + + smp_show_thread_priority_info(st); + + return SCPE_OK; +} + +t_stat show_config (SMP_FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) +{ + int32 i; + DEVICE *dptr; + + if (cptr && (*cptr != 0)) + return SCPE_2MARG; + fprintf (st, "%s simulator configuration\n\n", sim_name); + for (i = 0; (dptr = sim_devices[i]) != NULL; i++) + show_device (st, dptr, flag); + return SCPE_OK; +} + +t_stat show_log_names (SMP_FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) +{ + int32 i; + DEVICE *dptr; + + if (cptr && (*cptr != 0)) + return SCPE_2MARG; + for (i = 0; (dptr = sim_devices[i]) != NULL; i++) + show_dev_logicals (st, dptr, NULL, 1, cptr); + return SCPE_OK; +} + +t_stat show_dev_logicals (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ + if (dptr->lname) + fprintf (st, "%s -> %s\n", dptr->lname, dptr->name); + else if (!flag) + fputs ("no logical name assigned\n", st); + return SCPE_OK; +} + +t_stat show_queue (SMP_FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) +{ + /* + * Unlike SIMH 3.8 we do not display asynch IO queue here since it is flushed on Ctrl/E before + * control is passed to the console, so when show_queue is invoked, asynch IO queue will be empty. + */ + + DEVICE *dptr; + int32 accum; + + if (cptr && *cptr) + return SCPE_2MARG; + + for (uint32 k = 0; k < sim_ncpus; k++) + { + CPU_UNIT* cpu_unit = cpu_units[k]; + clock_queue_entry* cqe; + + if (sim_ncpus == 1) + { + fprintf(st, "%s", sim_name); + } + else + { + fprintf(st, "CPU%d", cpu_unit->cpu_id); + } + + if (!cpu_unit->is_running() && !cpu_unit->is_primary_cpu()) + { + fprintf (st, " %s\n", cpu_describe_state(cpu_unit)); + continue; + } + + if (cpu_unit->clock_queue == NULL) + { + fprintf (st, " event queue empty, time = %.0f\n", cpu_unit->sim_time); + continue; + } + + fprintf (st, " event queue status, time = %.0f\n", cpu_unit->sim_time); + accum = 0; + t_bool clk_printed = FALSE; + + for (cqe = cpu_unit->clock_queue; cqe != NULL; cqe = cqe->next) + { + if (cqe->clk_cosched && cpu_unit->clk_active && !clk_printed) + { + fprintf (st, " CLK at next SYNCLK tick\n"); + clk_printed = TRUE; + } + + if (cqe->uptr == &sim_throt_unit) + { + fprintf (st, " Throttle timer"); + } + else if ((dptr = find_dev_from_unit (cqe->uptr)) != NULL) + { + fprintf (st, " %s", sim_dname (dptr)); + if (dptr->numunits > 1) fprintf (st, " unit %d", sim_unit_index (cqe->uptr)); + } + else + { + fprintf (st, " Unknown"); + } + + // ToDo: sort by clk_cosched + if (cqe->clk_cosched == 0) + fprintf (st, " at %d", accum + cqe->time); + else if (cqe->clk_cosched == 1) + fprintf (st, " at next CLK tick"); + else + fprintf (st, " after %d CLK ticks", cqe->clk_cosched); + + if (! (IS_PERCPU_UNIT(cqe->uptr) || cqe->uptr->clock_queue_cpu == cpu_unit)) + { + if (cqe->uptr->clock_queue_cpu) + fprintf (st, " [invalidated by CPU%02d]", cqe->uptr->clock_queue_cpu->cpu_id); + else + fprintf (st, " [invalidated by CPUxx]"); + } + + fprintf(st, "\n"); + + accum = accum + cqe->time; + } + + if (use_clock_thread && cpu_unit->clk_active && !clk_printed) + fprintf (st, " CLK at next SYNCLK tick\n"); + } + + return SCPE_OK; +} + +t_stat show_time (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ + RUN_SCOPE; + if (cptr && *cptr) + return SCPE_2MARG; + fprintf (st, "Time:\t%.0f\n", cpu_unit->sim_time); + return SCPE_OK; +} + +t_stat show_break (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ + t_stat r; + + if (cptr && (*cptr != 0)) + r = ssh_break (st, cptr, 1); /* more? */ + else r = sim_brk_showall (st, sim_switches); + return r; +} + +t_stat show_dev_radix (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ + fprintf (st, "Radix=%d\n", dptr->dradix); + return SCPE_OK; +} + +t_stat show_dev_debug (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +int32 any = 0; +DEBTAB *dep; + +if (dptr->flags & DEV_DEBUG) { + if (dptr->dctrl == 0) + fputs ("Debugging disabled", st); + else if (dptr->debflags == NULL) + fputs ("Debugging enabled", st); + else { + fputs ("Debug=", st); + for (dep = dptr->debflags; dep->name != NULL; dep++) { + if (dptr->dctrl & dep->mask) { + if (any) + fputc (';', st); + fputs (dep->name, st); + any = 1; + } + } + } + fputc ('\n', st); + return SCPE_OK; + } +else return SCPE_NOFNC; +} + +/* Show modifiers */ + +t_stat show_mod_names (SMP_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_modifiers (st, dptr, NULL, flag, cptr); + return SCPE_OK; +} + +t_stat show_dev_modifiers (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ + int32 any, enb; + MTAB *mptr; + DEBTAB *dep; + + any = enb = 0; + if (dptr->modifiers) { + for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) { + if (mptr->mstring) { + if (strcmp (mptr->mstring, "ENABLED") == 0) + enb = 1; + if (any++) + fprintf (st, ", %s", mptr->mstring); + else fprintf (st, "%s\t%s", sim_dname (dptr), mptr->mstring); + } + } + } + if (dptr->flags & DEV_DEBUG) { + if (any++) + fprintf (st, ", DEBUG, NODEBUG"); + else fprintf (st, "%s\tDEBUG, NODEBUG", sim_dname (dptr)); + } + if (!enb && (dptr->flags & DEV_DISABLE)) { + if (any++) + fprintf (st, ", ENABLED, DISABLED"); + else fprintf (st, "%s\tENABLED, DISABLED", sim_dname (dptr)); + } + 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, "\n"); + } + return SCPE_OK; +} + +t_stat show_all_mods (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag) +{ + MTAB *mptr; + + if (dptr->modifiers == NULL) + return SCPE_OK; + for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) { + if (mptr->pstring && ((mptr->mask & MTAB_XTD)? + ((mptr->mask & flag) && !(mptr->mask & MTAB_NMO)): + ((MTAB_VUN & flag) && ((uptr->flags & mptr->mask) == mptr->match)))) { + fputs (", ", st); + show_one_mod (st, dptr, uptr, mptr, NULL, 0); + } + } + return SCPE_OK; +} + +t_stat show_one_mod (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, + char *cptr, int32 flag) +{ + //t_value val; + + if (mptr->disp) + mptr->disp (st, uptr, mptr->match, cptr ? cptr: mptr->desc); + //else if ((mptr->mask & MTAB_XTD) && (mptr->mask & MTAB_VAL)) { + // REG *rptr = (REG *) mptr->desc; + // fprintf (st, "%s=", mptr->pstring); + // val = get_rval (rptr, 0); + // fprint_val (st, val, rptr->radix, rptr->width, + // rptr->flags & REG_FMT); + // } + else + fputs (mptr->pstring, st); + if (flag && !((mptr->mask & MTAB_XTD) && (mptr->mask & MTAB_NMO))) + fputc ('\n', st); + return SCPE_OK; +} + +/* Show show commands */ + +t_stat show_show_commands (SMP_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 (SMP_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, "sh{ow} %s\t%s", sim_dname (dptr), mptr->pstring); + } + } + if (any) + fprintf (st, "\n"); + return SCPE_OK; +} + +static SHTAB cpu_cmd_tab[] = { + { "MULTIPROCESSOR", &cpu_cmd_multiprocessor, 0 }, + { "ID", &cpu_cmd_id, 0 }, + { "SMT", &cpu_cmd_smt, 0 }, + { "INFO", &cpu_cmd_info, 0 }, + { NULL, NULL, 0 } +}; + +t_stat cpu_cmd (int32 flag, char *cptr) +{ + char gbuf[CBUFSIZE]; + SHTAB* shptr; + + // GET_SWITCHES (cptr); /* get switches */ + + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + + if (shptr = find_shtab (cpu_cmd_tab, gbuf)) + return shptr->action (sim_ofile ? sim_ofile : smp_stdout, NULL, NULL, shptr->arg, cptr); + + return SCPE_ARG; +} + +t_stat cpu_cmd_multiprocessor (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ + t_stat r; + + if (! (cptr && *cptr)) + return SCPE_2FARG; + uint32 ncpus = (uint32) get_uint (cptr, 10, SIM_MAX_CPUS, &r); + if (r != SCPE_OK) + return SCPE_ARG; + if (ncpus == sim_ncpus) return SCPE_OK; + if (ncpus < sim_ncpus || ncpus > SIM_MAX_CPUS) return SCPE_ARG; + + /* + * Check if SMT level is acceptable + */ + if (smp_smt_factor_set) + { + smp_printf("Using override SMT (Hyper-Threading) slow-down factor %g\n", smp_smt_factor); + if (sim_log) + fprintf(sim_log, "Using override SMT (Hyper-Threading) slow-down factor %g\n", smp_smt_factor); + } + else if (smp_nsmt_per_core <= 0) + { + smp_printf("%s was unable to query the number of SMT (Hyper-Threading) units per core on the host system\n", sim_name); + if (sim_log) + fprintf(sim_log, "%s was unable to query the number of SMT (Hyper-Threading) units per core on the host system\n", sim_name); + return SCPE_AFAIL; + } + else if (smp_nsmt_per_core > 2) + { + smp_printf("This version of %s is not designed for host systems with more than 2-way SMT (Hyper-Threading) per core\n", sim_name); + if (sim_log) + fprintf(sim_log, "This version of %s is not designed for host systems with more than 2-way SMT (Hyper-Threading) per core\n", sim_name); + return SCPE_NOFNC; + } + + if (ncpus > (uint32) smp_ncpus) + { + smp_printf("Warning!!! Number of configured virtual processors (%d) exceeds the number of\n", ncpus); + smp_printf(" physical or logical processors on the host system (%d).\n", smp_ncpus); + smp_printf(" Configured %s system may be unstable or not runnable.\n", sim_name); + if (sim_log) + { + fprintf(sim_log, "Warning!!! Number of configured virtual processors (%d) exceeds the number of\n", ncpus); + fprintf(sim_log, " physical or logical processors on the host system (%d).\n", smp_ncpus); + fprintf(sim_log, " Configured %s system may be unstable or not runnable.\n", sim_name); + } + } + + if (cpu_stop_history ()) + { + smp_printf("Warning: CPU MULTI[PROCESSOR] command disabled CPU HISTORY recording, reenable if required\n"); + if (sim_log) + fprintf (sim_log, "Warning: CPU MULTI[PROCESSOR] command disabled CPU HISTORY recording, reenable if required\n"); + } + + /* + * Check if process is able to execute in desired priority range. + * + * We must provide guest operating system with capability to properly calibrate its timing loops, + * such as TENUSEC and UBADELAY based loops in case of VMS. This is essential even for the uniprocessor case + * but is truly critical for SMP mode. + * + * Priority elevation is also essential for: + * + * * lock holders preemtion avoidance and loss of efficiency due to busy-waiting while spinlock holder + * is pre-empted; + * + * * similary, to avoid convoying problem if VM-critical lock holder thread is pre-empted; + * + * * avoidance of pre-emption of VCPU thread while it is processing IPI request, and loss of efficiency + * while requesting processor is waiting for a response to IPI synchronous request; + * + * * prompt and timely processing of clock interrupts to maintain satisfactory synchronicty in the system + * and also to avoid significant system time drifts. + * + */ + t_bool ok_prio = smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_RUN) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CONSOLE_RUN) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CONSOLE_PAUSED) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CLOCK) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_CALIBRATION) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_RUN) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CONSOLE_RUN) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CONSOLE_PAUSED) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CLOCK) && + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_CALIBRATION); + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CONSOLE_PAUSED); + if (! ok_prio) + { + smp_printf("Warning!!! %s is unable to control thread priority. It is strongly recommened to configure host system\n", sim_name); + smp_printf(" as described in %s manual, otherwise guest operating system may be unstable in SMP mode.\n", sim_name); + if (sim_log) + { + fprintf(sim_log, "Warning!!! %s is unable to control thread priority. It is strongly recommened to configure host system\n", sim_name); + fprintf(sim_log, " as described in %s manual, otherwise guest operating system may be unstable in SMP mode.\n", sim_name); + } + } + + return cpu_create_cpus(ncpus) ? SCPE_OK : SCPE_AFAIL; +} + +t_stat cpu_cmd_id (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ + if (cptr == NULL || *cptr == '\0') + { + smp_printf ("Current default CPU: cpu%d\n", sim_dflt_cpu->cpu_id); + if (sim_log) + fprintf (sim_log, "Current default CPU: cpu%d\n", sim_dflt_cpu->cpu_id); + } + else + { + t_stat r; + uint32 ncpu = (uint32) get_uint (cptr, 10, sim_ncpus - 1, &r); + if (r != SCPE_OK) + return SCPE_ARG; + if (ncpu >= sim_ncpus || cpu_units[ncpu] == NULL) return SCPE_ARG; + sim_dflt_rscx->cpu_unit = sim_dflt_cpu = cpu_units[ncpu]; + } + + return SCPE_OK; +} + +t_stat cpu_cmd_smt (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ + if (cptr == NULL || *cptr == '\0') + { + if (smp_nsmt_per_core > 0) + { + smp_printf ("Detected SMT (Hyper-Threading) units per host processor core: %d\n", smp_nsmt_per_core); + if (sim_log) + fprintf (sim_log, "Detected SMT (Hyper-Threading) units per host processor core: %d\n", smp_nsmt_per_core); + } + else + { + smp_printf ("Detected SMT (Hyper-Threading) units per host processor core: unknown\n"); + if (sim_log) + fprintf (sim_log, "Detected SMT (Hyper-Threading) units per host processor core: unknown\n"); + } + + if (smp_smt_factor_set) + { + smp_printf ("Override SMT (Hyper-Threading) slow-down factor: %g\n", smp_smt_factor); + if (sim_log) + fprintf (sim_log, "Override SMT (Hyper-Threading) slow-down factor: %g\n", smp_smt_factor); + } + } + else + { + t_stat r = get_double(cptr, & smp_smt_factor); + if (r != SCPE_OK) + return r; + smp_smt_factor_set = TRUE; + } + + return SCPE_OK; +} + +/* Breakpoint commands */ + +t_stat brk_cmd (int32 flg, char *cptr) +{ + GET_SWITCHES (cptr); /* get switches */ + return ssh_break (NULL, cptr, flg); /* call common code */ +} + +t_stat ssh_break (SMP_FILE *st, char *cptr, int32 flg) +{ + if (sim_brk_types == 0) + return SCPE_NOFNC; + DEVICE* dptr = sim_dflt_dev; + if (dptr == NULL) + return SCPE_IERR; + UNIT* uptr = dptr->units[0]; + if (uptr == NULL) + return SCPE_IERR; + + char gbuf[CBUFSIZE], *tptr, *t1ptr, *aptr; + t_stat r; + t_addr lo, hi, max = uptr->capac - 1; + int32 cnt; + + if (aptr = strchr (cptr, ';')) /* ;action? */ + { + if (flg != SSH_ST) /* only on SET */ + return SCPE_ARG; + *aptr++ = 0; /* separate strings */ + } + + if (*cptr == 0) /* no argument? */ + { + lo = (t_addr) get_rval (sim_PC, 0); /* use PC */ + return ssh_break_one (st, flg, lo, 0, aptr); + } + + while (*cptr) + { + cptr = get_glyph (cptr, gbuf, ','); + tptr = get_range (dptr, gbuf, &lo, &hi, dptr->aradix, max, 0); + if (tptr == NULL) + return SCPE_ARG; + if (*tptr == '[') + { + cnt = (int32) strtotv (tptr + 1, &t1ptr, 10); + if ((tptr == t1ptr) || (*t1ptr != ']') || (flg != SSH_ST)) + return SCPE_ARG; + tptr = t1ptr + 1; + } + else + { + cnt = 0; + } + if (*tptr != 0) + return SCPE_ARG; + if (lo == 0 && hi == max) + { + if (flg == SSH_CL) + sim_brk_clrall (sim_switches); + else if (flg == SSH_SH) + sim_brk_showall (st, sim_switches); + else return SCPE_ARG; + } + else + { + for ( ; lo <= hi; lo = lo + 1) + { + r = ssh_break_one (st, flg, lo, cnt, aptr); + if (r != SCPE_OK) + return r; + } + } + } + + return SCPE_OK; +} + +t_stat ssh_break_one (SMP_FILE *st, int32 flg, t_addr lo, int32 cnt, char *aptr) +{ +switch (flg) { + + case SSH_ST: + return sim_brk_set (lo, sim_switches, cnt, aptr); + break; + + case SSH_CL: + return sim_brk_clr (lo, sim_switches); + break; + + case SSH_SH: + return sim_brk_show (st, lo, sim_switches); + break; + + default: + return SCPE_ARG; + } +} + +/* Reset command and routines + + re[set] reset all devices + re[set] all reset all devices + re[set] device reset specific device +*/ + +#if defined (VM_VAX) +extern DEVICE qba_dev; +#endif + +t_stat reset_cmd (int32 flag, char *cptr) +{ + RUN_SCOPE; + char gbuf[CBUFSIZE]; + DEVICE *dptr; + + GET_SWITCHES (cptr); /* get switches */ + if (*cptr == 0) /* reset(cr) */ + return (reset_all (0)); + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (*cptr != 0) /* now eol? */ + return SCPE_2MARG; + if (strcmp (gbuf, "ALL") == 0) + return (reset_all (0)); + dptr = find_dev (gbuf); /* locate device */ + if (dptr == NULL) /* found it? */ + return SCPE_NXDEV; + + if (dptr == &cpu_dev) + { + // if per-unit reset were provided, should call reset_all(0) for CPU0, + // for any other CPUs should call reset_cpu_and_its_devices(cpu_unit) + return reset_all(0); + } + else if (dptr->flags & DEV_PERCPU) /* allowed? */ + { + return SCPE_NOFNC; + } +#if defined (VM_VAX) + // we could rather emit this error message directly from qba_reset, + // but the only path it can "legally" happen starts here, so better catch it early + else if (dptr == &qba_dev && !cpu_unit->is_primary_cpu()) + { + smp_printf ("QBA can be reset only by the primary CPU (cpu0)\n"); + if (sim_log) + fprintf (sim_log, "QBA can be reset only by the primary CPU (cpu0)\n"); + return SCPE_NOFNC; + } +#endif + else + { + return reset_dev_thiscpu (dptr); + } +} + +/* Reset devices start..end + + Inputs: + start = number of starting device + Outputs: + status = error status +*/ + +t_stat reset_all (uint32 start) +{ + DEVICE *dptr; + t_stat reason = SCPE_OK; + + for (uint32 i = 0; i < start; i++) + { + if (sim_devices[i] == NULL) + return SCPE_IERR; + } + + if (start == 0) + { + for (uint32 k = 0; k < sim_ncpus; k++) + { + reason = reset_cpu_and_its_devices (cpu_units[k]); + if (reason != SCPE_OK) break; + } + } + else for (uint32 i = start; (dptr = sim_devices[i]) != NULL; i++) + { + if (dptr->flags & DEV_PERCPU) + reason = reset_dev_allcpus (dptr); + else + reason = reset_dev_thiscpu (dptr); + + if (reason != SCPE_OK) + break; + } + + return reason; +} + +/* Reset to powerup state + + Inputs: + start = number of starting device + Outputs: + status = error status +*/ + +t_stat reset_all_p (uint32 start) +{ + t_stat r; + int32 old_sw = sim_switches; + + sim_switches = SWMASK ('P'); + r = reset_all (start); + sim_switches = old_sw; + return r; +} + +/* reset device, will reset per-CPU device only on the current CPU, not all CPUs */ +t_stat reset_dev_thiscpu (DEVICE* dptr) +{ + if (dptr->reset != NULL) + return dptr->reset (dptr); + else + return SCPE_OK; +} + +/* reset device, will reset per-CPU device on all CPUs */ +t_stat reset_dev_allcpus (DEVICE* dptr) +{ + t_stat res = SCPE_OK; + + if (dptr->reset != NULL) + { + if (dptr->flags & DEV_PERCPU) + { + run_scope_context* rscx = run_scope_context::get_current(); + CPU_UNIT* sv_cpu_unit = rscx->cpu_unit; + + for (uint32 k = 0; k < sim_ncpus; k++) + { + rscx->cpu_unit = cpu_units[k]; + t_stat rc = dptr->reset (dptr); + if (rc != SCPE_OK && res == SCPE_OK) + res = rc; + } + + rscx->cpu_unit = sv_cpu_unit; + } + else + { + res = dptr->reset (dptr); + } + } + + return res; +} + + +/* Load and dump commands + + lo[ad] filename {arg} load specified file + du[mp] filename {arg} dump to specified file +*/ + +t_stat load_cmd (int32 flag, char *cptr) +{ + RUN_SCOPE; + char gbuf[CBUFSIZE]; + SMP_FILE *loadfile; + t_stat reason; + + GET_SWITCHES (cptr); /* get switches */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */ + loadfile = sim_fopen (gbuf, flag? "wb": "rb"); /* open for wr/rd */ + if (loadfile == NULL) + return SCPE_OPENERR; + GET_SWITCHES (cptr); /* get switches */ + reason = sim_load (RUN_PASS, loadfile, cptr, gbuf, flag); /* load or dump */ + fclose (loadfile); + return reason; +} + +/* Attach command + + at[tach] unit file attach specified unit to file +*/ + +t_stat attach_cmd (int32 flag, char *cptr) +{ + char gbuf[CBUFSIZE]; + DEVICE *dptr; + UNIT *uptr; + t_stat r; + + GET_SWITCHES (cptr); /* get switches */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + GET_SWITCHES (cptr); /* get switches */ + if (*cptr == 0) /* now eol? */ + return SCPE_2FARG; + dptr = find_unit (gbuf, &uptr); /* locate unit */ + if (dptr == NULL) /* found dev? */ + return SCPE_NXDEV; + if (uptr == NULL) /* valid unit? */ + return SCPE_NXUN; + if (uptr->flags & UNIT_ATT) { /* already attached? */ + r = scp_detach_unit (dptr, uptr); /* detach it */ + if (r != SCPE_OK) /* error? */ + return r; + } + sim_trim_endspc (cptr); /* trim trailing spc */ + return scp_attach_unit (dptr, uptr, cptr); /* attach */ +} + +/* Call device-specific or file-oriented attach unit routine */ + +t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, char *cptr) +{ + if (dptr->attach != NULL) /* device routine? */ + return dptr->attach (uptr, cptr); /* call it */ + return attach_unit (uptr, cptr); /* no, std routine */ +} + +/* Attach unit to file */ + +t_stat attach_unit (UNIT *uptr, char *cptr) +{ + DEVICE *dptr; + + if (uptr->flags & UNIT_DIS) /* disabled? */ + return SCPE_UDIS; + if (!(uptr->flags & UNIT_ATTABLE)) /* not attachable? */ + return SCPE_NOATT; + if ((dptr = find_dev_from_unit (uptr)) == NULL) + return SCPE_NOATT; + if (dptr->flags & DEV_RAWONLY) /* raw mode only? */ + return SCPE_NOFNC; + uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc name buf */ + if (uptr->filename == NULL) + return SCPE_MEM; + strncpy (uptr->filename, cptr, CBUFSIZE); /* save name */ + if (sim_switches & SWMASK ('R')) { /* read only? */ + if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */ + return attach_err (uptr, SCPE_NORO); /* no, error */ + uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */ + if (uptr->fileref == NULL) /* open fail? */ + return attach_err (uptr, SCPE_OPENERR); /* yes, error */ + uptr->flags = uptr->flags | UNIT_RO; /* set rd only */ + if (!sim_quiet) + smp_printf ("%s: unit is read only\n", sim_dname (dptr)); + } + else { /* normal */ + uptr->fileref = sim_fopen (cptr, "rb+"); /* open r/w */ + if (uptr->fileref == NULL) { /* open fail? */ + if ((errno == EROFS) || (errno == EACCES)) { /* read only? */ + if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */ + return attach_err (uptr, SCPE_NORO); /* no error */ + uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */ + if (uptr->fileref == NULL) /* open fail? */ + return attach_err (uptr, SCPE_OPENERR); /* yes, error */ + uptr->flags = uptr->flags | UNIT_RO; /* set rd only */ + if (!sim_quiet) + smp_printf ("%s: unit is read only\n", sim_dname (dptr)); + } + else { /* doesn't exist */ + if (sim_switches & SWMASK ('E')) /* must exist? */ + return attach_err (uptr, SCPE_OPENERR); /* yes, error */ + uptr->fileref = sim_fopen (cptr, "wb+"); /* open new file */ + if (uptr->fileref == NULL) /* open fail? */ + return attach_err (uptr, SCPE_OPENERR); /* yes, error */ + if (!sim_quiet) smp_printf ("%s: creating new file\n", sim_dname (dptr)); + } + } /* end if null */ + } /* end else */ + if (uptr->flags & UNIT_BUFABLE) { /* buffer? */ + uint32 cap = ((uint32) uptr->capac) / dptr->aincr; /* effective size */ + if (uptr->flags & UNIT_MUSTBUF) /* dyn alloc? */ + uptr->filebuf = calloc (cap, SZ_D (dptr)); /* allocate */ + if (uptr->filebuf == NULL) /* no buffer? */ + return attach_err (uptr, SCPE_MEM); /* error */ + if (!sim_quiet) smp_printf ("%s: buffering file in memory\n", sim_dname (dptr)); + uptr->hwmark = (uint32) sim_fread (uptr->filebuf, /* read file */ + SZ_D (dptr), cap, uptr->fileref); + uptr->flags = uptr->flags | UNIT_BUF; /* set buffered */ + } + uptr->flags = uptr->flags | UNIT_ATT; + uptr->pos = 0; + return SCPE_OK; +} + +t_stat attach_err (UNIT *uptr, t_stat stat) +{ + free (uptr->filename); + uptr->filename = NULL; + return stat; +} + +/* Detach command + + det[ach] all detach all units + det[ach] unit detach specified unit +*/ + +t_stat detach_cmd (int32 flag, char *cptr) +{ + char gbuf[CBUFSIZE]; + DEVICE *dptr; + UNIT *uptr; + + GET_SWITCHES (cptr); /* get switches */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (*cptr != 0) /* now eol? */ + return SCPE_2MARG; + if (strcmp (gbuf, "ALL") == 0) + return (detach_all (0, FALSE)); + dptr = find_unit (gbuf, &uptr); /* locate unit */ + if (dptr == NULL) /* found dev? */ + return SCPE_NXDEV; + if (uptr == NULL) /* valid unit? */ + return SCPE_NXUN; + return scp_detach_unit (dptr, uptr); /* detach */ +} + +/* Detach devices start..end + + Inputs: + start = number of starting device + shutdown = TRUE if simulator shutting down + Outputs: + status = error status + + Note that during shutdown, detach routines for non-attachable devices + will be called. These routines can implement simulator shutdown. Error + returns during shutdown are ignored. +*/ + +t_stat detach_all (int32 start, t_bool shutdown) +{ + uint32 i, j; + DEVICE *dptr; + UNIT *uptr; + t_stat r; + + if ((start < 0) || (start > 1)) + return SCPE_IERR; + for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ + for (j = 0; j < dptr->numunits; j++) { /* loop thru units */ + uptr = dptr->units[j]; + if ((uptr->flags & UNIT_ATT) || /* attached? */ + (shutdown && dptr->detach && /* shutdown, spec rtn, */ + !(uptr->flags & UNIT_ATTABLE))) { /* !attachable? */ + r = scp_detach_unit (dptr, uptr); /* detach unit */ + + if ((r != SCPE_OK) && !shutdown) /* error and not shutting down? */ + return r; /* bail out now with error status */ + } + } + } + return SCPE_OK; +} + +/* Call device-specific or file-oriented detach unit routine */ + +t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr) +{ + if (dptr->detach != NULL) /* device routine? */ + return dptr->detach (uptr); + return detach_unit (uptr); /* no, standard */ +} + +/* Detach unit from file */ + +t_stat detach_unit (UNIT *uptr) +{ + DEVICE *dptr; + + if (uptr == NULL) + return SCPE_IERR; + if (!(uptr->flags & UNIT_ATTABLE)) /* attachable? */ + return SCPE_NOATT; + if (!(uptr->flags & UNIT_ATT)) /* attached? */ + return SCPE_OK; + if ((dptr = find_dev_from_unit (uptr)) == NULL) + return SCPE_OK; + if (uptr->flags & UNIT_BUF) { + uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr; + if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { + if (!sim_quiet) + smp_printf ("%s: writing buffer to file\n", sim_dname (dptr)); + rewind (uptr->fileref); + sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref); + if (ferror (uptr->fileref)) + smp_perror ("I/O error"); + } + if (uptr->flags & UNIT_MUSTBUF) { /* dyn alloc? */ + free (uptr->filebuf); /* free buf */ + uptr->filebuf = NULL; + } + uptr->flags = uptr->flags & ~UNIT_BUF; + } + uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_RO); + free (uptr->filename); + uptr->filename = NULL; + if (fclose (uptr->fileref) == EOF) + return SCPE_IOERR; + return SCPE_OK; +} + +/* Assign command + + as[sign] device name assign logical name to device +*/ + +t_stat assign_cmd (int32 flag, char *cptr) +{ + char gbuf[CBUFSIZE]; + DEVICE *dptr; + + GET_SWITCHES (cptr); /* get switches */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + GET_SWITCHES (cptr); /* get switches */ + if (*cptr == 0) /* now eol? */ + return SCPE_2FARG; + dptr = find_dev (gbuf); /* locate device */ + if (dptr == NULL) /* found dev? */ + return SCPE_NXDEV; + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (*cptr != 0) /* must be eol */ + return SCPE_2MARG; + if (find_dev (gbuf)) /* name in use */ + return SCPE_ARG; + deassign_device (dptr); /* release current */ + return assign_device (dptr, gbuf); +} + +t_stat assign_device (DEVICE *dptr, char *cptr) +{ + dptr->lname = (char *) calloc (CBUFSIZE, sizeof (char)); + if (dptr->lname == NULL) + return SCPE_MEM; + strncpy (dptr->lname, cptr, CBUFSIZE); + return SCPE_OK; +} + +/* Deassign command + + dea[ssign] device deassign logical name +*/ + +t_stat deassign_cmd (int32 flag, char *cptr) +{ + char gbuf[CBUFSIZE]; + DEVICE *dptr; + + GET_SWITCHES (cptr); /* get switches */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (*cptr != 0) /* now eol? */ + return SCPE_2MARG; + dptr = find_dev (gbuf); /* locate device */ + if (dptr == NULL) /* found dev? */ + return SCPE_NXDEV; + return deassign_device (dptr); +} + +t_stat deassign_device (DEVICE *dptr) +{ + if (dptr->lname) + free (dptr->lname); + dptr->lname = NULL; + return SCPE_OK; +} + +/* Get device display name */ + +const char *sim_dname (DEVICE *dptr) +{ + return (dptr->lname ? dptr->lname : dptr->name); +} + +/* Save command + + sa[ve] filename save state to specified file +*/ + +t_stat save_cmd (int32 flag, char *cptr) +{ +#if 1 + smp_printf ("Save/Restore functionality is not implemented yet in VAX MP simulator\n"); + if (sim_log) + fprintf (sim_log, "Save/Restore functionality is not implemented yet in VAX MP simulator\n"); + return SCPE_NOFNC; +#else + SMP_FILE *sfile; + t_stat r; + GET_SWITCHES (cptr); /* get switches */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + sim_trim_endspc (cptr); + if ((sfile = sim_fopen (cptr, "wb")) == NULL) + return SCPE_OPENERR; + r = sim_save (sfile); + fclose (sfile); + return r; +#endif +} + +t_stat sim_save (SMP_FILE *sfile) +{ +// ToDo: reimplement for VAX MP, accounting for additional fields in UNIT, CPU_UNIT, +// multiple CPUs, CPU database and other global variables +RUN_SCOPE; +void *mbuf; +int32 l, t; +uint32 i, j; +t_addr k, high; +t_value val; +t_stat r; +t_bool zeroflg; +size_t sz; +DEVICE *dptr; +UNIT *uptr; +REG *rptr; + +#define WRITE_I(xx) sim_fwrite (&(xx), sizeof (xx), 1, sfile) + +fprintf (sfile, "%s\n%s\n%s\n%s\n%s\n%.0f\n", + save_vercur, /* [V2.5] save format */ + sim_name, /* sim name */ + sim_si64, sim_sa64, sim_snet, /* [V3.5] options */ + cpu_unit->sim_time); /* [V3.2] sim time (ToDo) */ +WRITE_I (cpu_unit->sim_rtime); /* [V2.6] sim rel time (ToDo) */ + +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru devices */ + fputs (dptr->name, sfile); /* device name */ + fputc ('\n', sfile); + if (dptr->lname) /* [V3.0] logical name */ + fputs (dptr->lname, sfile); + fputc ('\n', sfile); + WRITE_I (dptr->flags); /* [V2.10] flags */ + for (j = 0; j < dptr->numunits; j++) { + uptr = dptr->units[j]; + t = sim_is_active (uptr); /* ToDo: redo for SMP */ + WRITE_I (j); /* unit number */ + WRITE_I (t); /* activation time */ + WRITE_I (uptr->u3); /* unit specific */ + WRITE_I (uptr->u4); + WRITE_I (uptr->u5); /* [V3.0] more unit */ + WRITE_I (uptr->u6); + WRITE_I (uptr->flags); /* [V2.10] flags */ + WRITE_I (uptr->capac); /* [V3.5] capacity */ + if (uptr->flags & UNIT_ATT) + fputs (uptr->filename, sfile); + fputc ('\n', sfile); + if (((uptr->flags & (UNIT_FIX + UNIT_ATTABLE)) == UNIT_FIX) && + (dptr->examine != NULL) && + ((high = uptr->capac) != 0)) { /* memory-like unit? */ + WRITE_I (high); /* [V2.5] write size */ + sz = SZ_D (dptr); + if ((mbuf = calloc (SRBSIZ, sz)) == NULL) { + fclose (sfile); + return SCPE_MEM; + } + for (k = 0; k < high; ) { /* loop thru mem */ + zeroflg = TRUE; + for (l = 0; (l < SRBSIZ) && (k < high); l++, + k = k + (dptr->aincr)) { /* check for 0 block */ + r = dptr->examine (&val, k, uptr, SIM_SW_REST); + if (r != SCPE_OK) + return r; + if (val) zeroflg = FALSE; + SZ_STORE (sz, val, mbuf, l); + } /* end for l */ + if (zeroflg) { /* all zero's? */ + l = -l; /* invert block count */ + WRITE_I (l); /* write only count */ + } + else { + WRITE_I (l); /* block count */ + sim_fwrite (mbuf, sz, l, sfile); + } + } /* end for k */ + free (mbuf); /* dealloc buffer */ + } /* end if mem */ + else { /* no memory */ + high = 0; /* write 0 */ + WRITE_I (high); + } /* end else mem */ + } /* end unit loop */ + t = -1; /* end units */ + WRITE_I (t); /* write marker */ + for (rptr = dptr->registers; (rptr != NULL) && /* loop thru regs */ + (rptr->name != NULL); rptr++) { + fputs (rptr->name, sfile); /* name */ + fputc ('\n', sfile); + WRITE_I (rptr->depth); /* [V2.10] depth */ + for (j = 0; j < rptr->depth; j++) { /* loop thru values */ + val = get_rval (rptr, j); /* get value */ + WRITE_I (val); /* store */ + } + } + fputc ('\n', sfile); /* end registers */ + } +fputc ('\n', sfile); /* end devices */ +return (ferror (sfile))? SCPE_IOERR: SCPE_OK; /* error during save? */ +} + +/* Restore command + + re[store] filename restore state from specified file +*/ + +t_stat restore_cmd (int32 flag, char *cptr) +{ +#if 1 + smp_printf ("Save/Restore functionality is not implemented yet in VAX MP simulator\n"); + if (sim_log) + fprintf (sim_log, "Save/Restore functionality is not implemented yet in VAX MP simulator\n"); + return SCPE_NOFNC; +#else + SMP_FILE *rfile; + t_stat r; + + GET_SWITCHES (cptr); /* get switches */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + sim_trim_endspc (cptr); + if ((rfile = sim_fopen (cptr, "rb")) == NULL) + return SCPE_OPENERR; + r = sim_rest (rfile); + fclose (rfile); + return r; +#endif +} + +t_stat sim_rest (SMP_FILE *rfile) +{ +// ToDo: reimplement for VAX MP, accounting for additional fields in UNIT, CPU_UNIT, +// multiple CPUs, CPU database and other global variables +RUN_SCOPE; +char buf[CBUFSIZE]; +void *mbuf; +int32 j, blkcnt, limit, unitno, time, flg; +uint32 us, depth; +t_addr k, high, old_capac; +t_value val, mask; +t_stat r; +size_t sz; +t_bool v35, v32; +DEVICE *dptr; +UNIT *uptr; +REG *rptr; + +#define READ_S(xx) if (read_line ((xx), CBUFSIZE, rfile) == NULL) \ + return SCPE_IOERR; +#define READ_I(xx) if (sim_fread (&xx, sizeof (xx), 1, rfile) == 0) \ + return SCPE_IOERR; + +READ_S (buf); /* [V2.5+] read version */ +v35 = v32 = FALSE; +if (strcmp (buf, save_vercur) == 0) /* version 3.5? */ + v35 = v32 = TRUE; +else if (strcmp (buf, save_ver32) == 0) /* version 3.2? */ + v32 = TRUE; +else if (strcmp (buf, save_ver30) != 0) { /* version 3.0? */ + smp_printf ("Invalid file version: %s\n", buf); + return SCPE_INCOMP; + } +READ_S (buf); /* read sim name */ +if (strcmp (buf, sim_name)) { /* name match? */ + smp_printf ("Wrong system type: %s, not %s\n", buf, sim_name); + return SCPE_INCOMP; + } +if (v35) { /* [V3.5+] options */ + READ_S (buf); /* integer size */ + if (strcmp (buf, sim_si64) != 0) { + smp_printf ("Incompatible integer size, save file = %s\n", buf); + return SCPE_INCOMP; + } + READ_S (buf); /* address size */ + if (strcmp (buf, sim_sa64) != 0) { + smp_printf ("Incompatible address size, save file = %s\n", buf); + return SCPE_INCOMP; + } + READ_S (buf); /* Ethernet */ + } +if (v32) { /* [V3.2+] time as string */ + READ_S (buf); + sscanf (buf, "%lf", &cpu_unit->sim_time); + } +else READ_I (cpu_unit->sim_time); /* sim time (ToDo)*/ +READ_I (cpu_unit->sim_rtime); /* [V2.6+] sim rel time (ToDo) */ + +for ( ;; ) { /* device loop */ + READ_S (buf); /* read device name */ + if (buf[0] == 0) /* last? */ + break; + if ((dptr = find_dev (buf)) == NULL) { /* locate device */ + smp_printf ("Invalid device name: %s\n", buf); + return SCPE_INCOMP; + } + READ_S (buf); /* [V3.0+] logical name */ + deassign_device (dptr); /* delete old name */ + if ((buf[0] != 0) && + ((r = assign_device (dptr, buf)) != SCPE_OK)) + return r; + READ_I (flg); /* [V2.10+] ctlr flags */ + if (!v32) + flg = ((flg & DEV_UFMASK_31) << (DEV_V_UF - DEV_V_UF_31)) | + (flg & ~DEV_UFMASK_31); /* [V3.2+] flags moved */ + dptr->flags = (dptr->flags & ~DEV_RFLAGS) | /* restore ctlr flags */ + (flg & DEV_RFLAGS); + for ( ;; ) { /* unit loop */ + sim_switches = SIM_SW_REST; /* flag rstr, clr RO */ + READ_I (unitno); /* unit number */ + if (unitno < 0) /* end units? */ + break; + if ((uint32) unitno >= dptr->numunits) { /* too big? */ + smp_printf ("Invalid unit number: %s%d\n", sim_dname (dptr), unitno); + return SCPE_INCOMP; + } + READ_I (time); /* event time */ + uptr = dptr->units[unitno]; + sim_cancel (uptr); + if (time > 0) + sim_activate (uptr, time - 1); + READ_I (uptr->u3); /* device specific */ + READ_I (uptr->u4); + READ_I (uptr->u5); /* [V3.0+] more dev spec */ + READ_I (uptr->u6); + READ_I (flg); /* [V2.10+] unit flags */ + old_capac = uptr->capac; /* save current capacity */ + if (v35) { /* [V3.5+] capacity */ + READ_I (uptr->capac); + } + if (!v32) + flg = ((flg & UNIT_UFMASK_31) << (UNIT_V_UF - UNIT_V_UF_31)) | + (flg & ~UNIT_UFMASK_31); /* [V3.2+] flags moved */ + uptr->flags = (uptr->flags & ~UNIT_RFLAGS) | + (flg & UNIT_RFLAGS); /* restore */ + READ_S (buf); /* attached file */ + if ((uptr->flags & UNIT_ATT) && /* unit currently attached? */ + !(dptr->flags & DEV_NET)) { /* and not a net device? */ + r = scp_detach_unit (dptr, uptr); /* detach it */ + if (r != SCPE_OK) + return r; + } + if ((buf[0] != '\0') && /* unit to be reattached? */ + !(dptr->flags & DEV_NET) && /* and not a net device? */ + ((uptr->flags & UNIT_ATTABLE) || /* and unit is attachable */ + (dptr->attach != NULL))) { /* or VM attach routine provided? */ + uptr->flags = uptr->flags & ~UNIT_DIS; /* ensure device is enabled */ + if (flg & UNIT_RO) /* [V2.10+] saved flgs & RO? */ + sim_switches |= SWMASK ('R'); /* RO attach */ + r = scp_attach_unit (dptr, uptr, buf); /* reattach unit */ + if (r != SCPE_OK) + return r; + } + READ_I (high); /* memory capacity */ + if (high > 0) { /* [V2.5+] any memory? */ + if (((uptr->flags & (UNIT_FIX + UNIT_ATTABLE)) != UNIT_FIX) || + (dptr->deposit == NULL)) { + smp_printf ("Can't restore memory: %s%d\n", sim_dname (dptr), unitno); + return SCPE_INCOMP; + } + if (high != old_capac) { /* size change? */ + uptr->capac = old_capac; /* temp restore old */ + if ((dptr->flags & DEV_DYNM) && + ((dptr->msize == NULL) || + (dptr->msize (uptr, (int32) high, NULL, NULL) != SCPE_OK))) { + smp_printf ("Can't change memory size: %s%d\n", + sim_dname (dptr), unitno); + return SCPE_INCOMP; + } + uptr->capac = high; /* new memory size */ + smp_printf ("Memory size changed: %s%d = ", sim_dname (dptr), unitno); + fprint_capac (smp_stdout, dptr, uptr); + smp_printf ("\n"); + } + sz = SZ_D (dptr); /* allocate buffer */ + if ((mbuf = calloc (SRBSIZ, sz)) == NULL) + return SCPE_MEM; + for (k = 0; k < high; ) { /* loop thru mem */ + READ_I (blkcnt); /* block count */ + if (blkcnt < 0) /* compressed? */ + limit = -blkcnt; + else limit = (int32) sim_fread (mbuf, sz, blkcnt, rfile); + if (limit <= 0) /* invalid or err? */ + return SCPE_IOERR; + for (j = 0; j < limit; j++, k = k + (dptr->aincr)) { + if (blkcnt < 0) /* compressed? */ + val = 0; + else SZ_LOAD (sz, val, mbuf, j); /* saved value */ + r = dptr->deposit (val, k, uptr, SIM_SW_REST); + if (r != SCPE_OK) + return r; + } /* end for j */ + } /* end for k */ + free (mbuf); /* dealloc buffer */ + } /* end if high */ + } /* end unit loop */ + for ( ;; ) { /* register loop */ + READ_S (buf); /* read reg name */ + if (buf[0] == 0) /* last? */ + break; + READ_I (depth); /* [V2.10+] depth */ + if ((rptr = find_reg (buf, NULL, dptr)) == NULL) { + smp_printf ("Invalid register name: %s %s\n", sim_dname (dptr), buf); + for (us = 0; us < depth; us++) { /* skip values */ + READ_I (val); + } + continue; + } + if (depth != rptr->depth) /* [V2.10+] mismatch? */ + smp_printf ("Register depth mismatch: %s %s, file = %d, sim = %d\n", + sim_dname (dptr), buf, depth, rptr->depth); + mask = width_mask[rptr->width]; /* get mask */ + for (us = 0; us < depth; us++) { /* loop thru values */ + READ_I (val); /* read value */ + if (val > mask) /* value ok? */ + smp_printf ("Invalid register value: %s %s\n", sim_dname (dptr), buf); + else if (us < rptr->depth) /* in range? */ + put_rval (rptr, us, val); + } + } + } /* end device loop */ +return SCPE_OK; +} + +/* Run, go, cont, step commands + + ru[n] [new PC] reset and start simulation + go [new PC] start simulation + co[nt] start simulation + s[tep] [step limit] start simulation for 'limit' instructions + b[oot] device bootstrap from device and start simulation +*/ + +t_stat run_cmd (int32 flag, char *cptr) +{ + RUN_SCOPE; + char *tptr, gbuf[CBUFSIZE]; + uint32 i, j; + int32 unitno; + t_value pcv; + t_stat r; + DEVICE *dptr; + UNIT *uptr; + + if (sim_brk_is_in_action ()) + { + const char* verb; + t_bool can = FALSE; + + switch (flag) + { + case RU_RUN: verb = "RUN"; break; + case RU_GO: verb = "GO"; break; + case RU_BOOT: verb = "BOOT"; break; + case RU_STEP: verb = "STEP"; can = TRUE; break; + case RU_CONT: verb = "CONTINUE"; can = TRUE; break; + default: verb = "the"; can = TRUE; break; + } + + if (! can) + { + smp_printf ("Cannot execute %s command from breakpoint action or script\n", verb); + if (sim_log) + fprintf (sim_log, "Cannot execute %s command from breakpoint action or script\n", verb); + return SCPE_ARG; + } + } + + if (sim_ws_settings_changed) + sim_ws_setup(); + + GET_SWITCHES (cptr); /* get switches */ + sim_step = 0; + if (flag == RU_RUN || flag == RU_GO) /* run or go */ + { + switch (cpu_unit->cpu_state) + { + case CPU_STATE_RUNNABLE: + case CPU_STATE_RUNNING: + break; + default: + smp_printf ("Cannot execute %s command since cpu%d is in %s state\n", flag == RU_RUN ? "RUN" : "GO", cpu_unit->cpu_id, cpu_describe_state(cpu_unit)); + if (sim_log) + fprintf (sim_log, "Cannot execute %s command since cpu%d is in %s state\n", flag == RU_RUN ? "RUN" : "GO", cpu_unit->cpu_id, cpu_describe_state(cpu_unit)); + return SCPE_ARG; + } + + if (*cptr != 0) /* argument? */ + { + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (*cptr != 0) /* should be end */ + return SCPE_2MARG; + if (sim_vm_parse_addr) /* address parser? */ + pcv = sim_vm_parse_addr (sim_dflt_dev, gbuf, &tptr); + else pcv = strtotv (gbuf, &tptr, sim_PC->radix);/* parse PC */ + if ((tptr == gbuf) || (*tptr != 0) || /* error? */ + (pcv > width_mask[sim_PC->width])) + return SCPE_ARG; + put_rval (sim_PC, 0, pcv); + } + + if ((flag == RU_RUN) && /* run? */ + ((r = run_boot_prep ()) != SCPE_OK)) /* reset sim */ + { + return r; + } + } + else if (flag == RU_STEP) /* step */ + { + switch (cpu_unit->cpu_state) + { + case CPU_STATE_RUNNABLE: + case CPU_STATE_RUNNING: + break; + default: + smp_printf ("Cannot execute STEP command since cpu%d is in %s state\n", cpu_unit->cpu_id, cpu_describe_state(cpu_unit)); + if (sim_log) + fprintf (sim_log, "Cannot execute STEP command since cpu%d is in %s state\n", cpu_unit->cpu_id, cpu_describe_state(cpu_unit)); + return SCPE_ARG; + } + + if (*cptr != 0) /* argument? */ + { + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (*cptr != 0) /* should be end */ + return SCPE_2MARG; + sim_step = (int32) get_uint (gbuf, 10, INT_MAX, &r); + if (r != SCPE_OK || sim_step <= 0) /* error? */ + return SCPE_ARG; + } + else + { + sim_step = 1; + } + } + else if (flag == RU_BOOT) /* boot */ + { + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (*cptr != 0) /* should be end */ + return SCPE_2MARG; + dptr = find_unit (gbuf, &uptr); /* locate unit */ + if (dptr == NULL) /* found dev? */ + return SCPE_NXDEV; + if (uptr == NULL) /* valid unit? */ + return SCPE_NXUN; + if (dptr->boot == NULL) /* can it boot? */ + return SCPE_NOFNC; + if (uptr->flags & UNIT_DIS) /* disabled? */ + return SCPE_UDIS; + if ((uptr->flags & UNIT_ATTABLE) && /* if attable, att? */ + !(uptr->flags & UNIT_ATT)) + return SCPE_UNATT; + unitno = sim_unit_index (uptr); /* recover unit# */ + if (uptr->is_cpu() && uptr != &cpu_unit_0) + { + smp_printf ("Only primary CPU (cpu0) can boot\n"); + if (sim_log) + fprintf (sim_log, "Only primary CPU (cpu0) can boot\n"); + return SCPE_NOFNC; + } + if (uptr->is_cpu()) /* set as current cpu */ + { + run_scope_context* rscx = run_scope_context::get_current(); + rscx->cpu_unit = cpu_unit = (CPU_UNIT*) uptr; + } + if ((r = run_boot_prep ()) != SCPE_OK) /* reset sim */ + return r; + if ((r = dptr->boot (unitno, dptr)) != SCPE_OK) /* boot device */ + return r; + } + else if (flag == RU_CONT) + { + if (sim_brk_is_in_action ()) + { + smp_printf ("CONTINUE command inside breakpoint action: postponed till the end of action\n"); + if (sim_log) + fprintf (sim_log, "CONTINUE command inside breakpoint action: postponed till the end of action\n"); + sim_brk_continue = TRUE; + return SCPE_OK; + } + } + else + { + return SCPE_IERR; + } + + for (i = 1; (dptr = sim_devices[i]) != NULL; i++) /* reposition all */ + { + for (j = 0; j < dptr->numunits; j++) /* seq devices */ + { + uptr = dptr->units[j]; + if ((uptr->flags & (UNIT_ATT + UNIT_SEQ)) == (UNIT_ATT + UNIT_SEQ)) + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); + } + } + + if ((r = run_cmd_core (RUN_PASS, flag)) != SCPE_OK) + return r; + + if (sim_log) /* flush console log */ + fflush (sim_log); + if (sim_deb) /* flush debug log */ + fflush (sim_deb); + for (i = 1; (dptr = sim_devices[i]) != NULL; i++) /* flush attached files */ + { + for (j = 0; j < dptr->numunits; j++) /* if not buffered in mem */ + { + uptr = dptr->units[j]; + if ((uptr->flags & UNIT_ATT) && /* attached, */ + !(uptr->flags & UNIT_BUF) && /* not buffered, */ + (uptr->fileref)) /* real file, */ + { + if (uptr->io_flush) /* unit specific flush routine */ + { + uptr->io_flush (uptr); + } + else if (!(uptr->flags & UNIT_RAW) && /* not raw, */ + !(uptr->flags & UNIT_RO)) /* not read only? */ + { + fflush (uptr->fileref); + } + } + } + } + + sim_async_process_io_events_for_console(); + +#if defined (VMS) + smp_printf ("\n"); +#endif + fprint_stopped (smp_stdout); /* print msg */ + if (sim_log) /* log if enabled */ + fprint_stopped (sim_log); + return SCPE_OK; +} + +/* Common setup for RUN or BOOT */ + +t_stat run_boot_prep (void) +{ + /* reset queue and time */ + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + CPU_UNIT* cpu_unit = cpu_units[cpu_ix]; + // sim_interval = 0; // redundant: will be reset in all CPU units by reset_all + cpu_unit->noqueue_time = 0; + cpu_unit->sim_time = cpu_unit->sim_rtime = 0; + } + + /* reset_all will also reset clock event queues on all CPUs */ + return reset_all (0); +} + +static t_stat run_cmd_core (RUN_DECL, int32 runcmd) +{ + run_scope_context* rscx = run_scope_context::get_current(); + CPU_UNIT* current_cpu_unit = cpu_unit; + t_stat r; + + stop_cpus = 0; + hlt_pin = 0; + + if (use_clock_thread && !sim_clock_thread_created) + { + sim_try + { + smp_create_thread(sim_clock_thread_proc, NULL, & sim_clock_thread); + sim_clock_thread_created = TRUE; + } + sim_catch (sim_exception_SimError, exc) + { + fprintf(smp_stderr, "\nFailed to create CLOCK thread: %s\n", exc->get_message()); + return SCPE_IERR; + } + sim_end_try + } + + fflush(smp_stdout); /* flush stdout */ + if (sim_log) /* flush log if enabled */ + fflush (sim_log); + + if (signal (SIGINT, int_handler) == SIG_ERR) /* set WRU */ + { + return SCPE_SIGERR; + } + +#ifdef SIGHUP + if (signal (SIGHUP, int_handler) == SIG_ERR) /* set WRU */ + return SCPE_SIGERR; +#endif + + if (signal (SIGTERM, int_handler) == SIG_ERR) /* set WRU */ + return SCPE_SIGERR; + + if (sim_ttrun () != SCPE_OK) /* set console mode */ + { + sim_ttcmd (); + return SCPE_TTYERR; + } + + sim_ttrun_mode = TRUE; + + if ((r = sim_check_console (30)) != SCPE_OK) /* check console, error? */ + { + sim_ttcmd (); + sim_ttrun_mode = FALSE; + return r; + } + if ((r = build_dib_tab ()) != SCPE_OK) /* build, chk dib_tab */ + { + sim_ttcmd (); + sim_ttrun_mode = FALSE; + return r; + } + + /************************************************************ + * set up VCPUs affinity * + ************************************************************/ + + if (sim_vcpu_per_core) + { + if (smp_can_alloc_per_core(sim_ncpus)) + { + sim_vcpu_affinity = SMP_AFFINITY_PER_CORE; + } + else + { + sim_vcpu_affinity = SMP_AFFINITY_ALL; + sim_vcpu_per_core = FALSE; + smp_printf("Warning!!! %s is unable to assign VCPUs to distinct PCPU cores.\n", sim_name); + if (sim_log) + fprintf(sim_log, "Warning!!! %s is unable to assign VCPUs to distinct PCPU cores.\n", sim_name); + } + } + else + { + sim_vcpu_affinity = SMP_AFFINITY_ALL; + } + + /************************************************************ + * prepare to launch CPUs * + ************************************************************/ + + cpu_unit->sim_step = (uint32) sim_step; /* set step counter */ + cpu_unit->sim_instrs = 0; /* ... */ + cpu_unit->cpu_state = CPU_STATE_RUNNING; /* mark CPU state change */ + cpu_running_set.set(cpu_unit->cpu_id); /* ... */ + sim_mp_active_update(); /* ... */ + sim_brk_clract(); /* defang actions */ + syncw_resuming(); /* reset syncw wait data */ + + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + rscx->cpu_unit = cpu_unit = cpu_units[cpu_ix]; + if (cpu_unit->cpu_state != CPU_STATE_RUNNING) continue; + if (sim_step && cpu_unit != current_cpu_unit) continue; + sim_throt_sched (); /* set throttle */ + sim_rtcn_init_all (RUN_PASS, runcmd == RU_CONT || runcmd == RU_STEP) ; /* re-init clocks */ + } + rscx->cpu_unit = cpu_unit = current_cpu_unit; + + cpu_attention->clear(); + cpu_pause_sync_barrier->set_count(sim_ncpus + 1 + (use_clock_thread ? 1 : 0)); /* all threads incl. self */ + smp_wmb(); // redundant before locking primitives, but let it be + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CONSOLE_RUN); + + /************************************************************ + * launch CPUs and optional clock thread * + ************************************************************/ + + cpu_database_lock->lock(); + + if (use_clock_thread) + cpu_clock_run_gate->release(1); + + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + CPU_UNIT* xcpu = cpu_units[cpu_ix]; + if (xcpu->cpu_state != CPU_STATE_RUNNING) continue; + if (sim_step && xcpu != current_cpu_unit) continue; + xcpu->cpu_run_gate->release(1); + } + + cpu_database_lock->unlock(); + + /************************************************************ + * run console loop during CPUs execution * + ************************************************************/ + + smp_pollable_synch_object* objs[2]; + objs[0] = cpu_attention; + objs[1] = smp_pollable_console_keyboard::get(); + + while (! weak_read(stop_cpus)) + { + int wres = smp_wait_any(objs, 2, -1); + + if (wres == 1) /* cpu_attention */ + break; + + if (wres == 2) /* console keyboard */ + { + for (;;) + { + r = sim_os_poll_kbd (); + if (r == SCPE_OK) /* no keyboard input */ + { + break; + } + else if (r == SCPE_STOP) /* Ctrl/E */ + { + stop_cpus = 1; + break; + } + else + { + sim_con_rcv_char ((int32) r); + } + } + } + } + + /************************************************************ + * pause CPUs for pending console action * + ************************************************************/ + + stop_cpus = 1; + smp_wmb(); + + cpu_database_lock->lock(); + + int nbarrier = 1; /* count self */ + + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + CPU_UNIT* xcpu = cpu_units[cpu_ix]; + if (xcpu->cpu_state != CPU_STATE_RUNNING) continue; + if (sim_step && xcpu != current_cpu_unit) continue; + wakeup_cpu(xcpu); + /* + * Wakeup CPUs out of sync window sleep. Do not reset syncw.cpu[].waitset + * nor xcpu->syncw_wait_cpu_id since they will be used by console commands + * to display syncw state information. + */ + xcpu->syncw_wait_event->set(); + nbarrier++; + } + + cpu_pause_sync_barrier->set_count(nbarrier + (use_clock_thread ? 1 : 0)); + + cpu_database_lock->unlock(); + cpu_pause_sync_barrier->wait(); + + /************************************************************ + * CPUs had been paused * + ************************************************************/ + + smp_mb(); /* redundant after sync primitives, but let it be */ + + cpu_unit->sim_step = 0; /* reset step pending indicator */ + + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CONSOLE_PAUSED); + + sim_ttcmd (); /* restore console */ + sim_ttrun_mode = FALSE; + + signal (SIGINT, SIG_DFL); /* cancel WRU */ +#ifdef SIGHUP + signal (SIGHUP, SIG_DFL); /* cancel WRU */ +#endif + signal (SIGTERM, SIG_DFL); /* cancel WRU */ + + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + rscx->cpu_unit = cpu_unit = cpu_units[cpu_ix]; + if (cpu_unit->cpu_state != CPU_STATE_RUNNING) continue; + + sim_throt_cancel (); /* cancel throttle */ + UPDATE_CPU_SIM_TIME(); /* update sim time */ + } + rscx->cpu_unit = cpu_unit = current_cpu_unit; + + // drain console keyboard typeahead + while (sim_os_poll_kbd () != SCPE_OK) {} + + // clear pending tti typeahead + tti_clear_pending_typeahead(); + + return SCPE_OK; +} + +SMP_THREAD_ROUTINE_DECL sim_cpu_work_thread_proc (void* arg) +{ + CPU_UNIT* cpu_unit = (CPU_UNIT*) arg; + char tname[8]; + smp_affinity_kind_t affinity = SMP_AFFINITY_ALL; + + sim_try + { + smp_thread_init(); + + run_scope_context* rscx = new run_scope_context(cpu_unit, SIM_THREAD_TYPE_CPU, cpu_unit->cpu_thread); + rscx->thread_cpu_id = cpu_unit->cpu_id; + rscx->set_current(); + + if (cpu_unit->is_primary_cpu()) + cpu_set_thread_priority(RUN_PASS, SIMH_THREAD_PRIORITY_CPU_RUN); + else + cpu_set_thread_priority(RUN_PASS, SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI); + + sprintf(tname, "CPU%02d", cpu_unit->cpu_id); + smp_set_thread_name(tname); + + for (;;) + { + cpu_unit->cpu_run_gate->wait(); + + smp_rmb(); /* redundant after sync primitive, but let it be */ + + if (affinity != sim_vcpu_affinity) + { + affinity = sim_vcpu_affinity; + smp_set_affinity(cpu_unit->cpu_thread, affinity); + } + + /* + * reevaluate thread priority either on initial VCPU start to match VCPU state + * or when resuming from console to reflect any changes done by console commands + */ + cpu_unit->cpu_thread_priority = SIMH_THREAD_PRIORITY_INVALID; + cpu_reevaluate_thread_priority(RUN_PASS); + if (! must_control_prio()) + { + /* + * If VCPU thread priority should not be controlled, always set it to CPU_RUN level. + * Priority is not controlled for performance reasons when executing in uniprocessor + * mode (only one VCPU is active) or when executing on a dedicated host machine. + * + * Note that even when priority is not controlled, it is still controlled for calibration purposes, + * i.e. thread priority is allowed to raise to CALIBRATION level and drop down from it. + * Therefore normally we should not knock down prority of a thread that is at CALIBRATION level, + * however if VCPU thread was at CALIBRATION level and was interrupted by the console, + * pending calibration is as good as a dead fish, so no useful point trying to care about + * maintaining CALIBRATION level here. Therefore always set it CPU_RUN unconditionally. + */ + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_RUN); + } + + /* check if should re-enter sync window sleep */ + cpu_unit->cpu_stop_code = syncw_checkinterval(RUN_PASS, TRUE); + + if (cpu_unit->cpu_stop_code == SCPE_OK) + cpu_unit->cpu_stop_code = sim_instr(RUN_PASS); + + smp_wmb(); /* redundant before sync primitive, but let it be */ + + t_bool join_console = TRUE; + + if (cpu_unit->is_primary_cpu() && (cpu_unit->cpu_stop_code == STOP_HALT || cpu_unit->cpu_stop_code == STOP_LOOP)) + { + /* + * Halting primary processor because it executed HALT instructions or infinite non-interruptable loop. + * All secondaries should have been shut down by operating system by now. + * In case guest OS failed, perform emergency force-shutdown of secondaries. + */ + syncw_leave_all(RUN_PASS, SYNCW_OVERRIDE_ALL | SYNCW_DISABLE_CPU); + cpu_stopping_ips_rate_update(RUN_PASS); + cpu_shutdown_secondaries(RUN_PASS); + } + + if (cpu_unit->is_secondary_cpu() && (cpu_unit->cpu_stop_code == STOP_HALT || cpu_unit->cpu_stop_code == STOP_LOOP)) + { + /* + * Halting secondary processor because it executed HALT instructions or infinite non-interruptable loop + */ + cpu_database_lock->lock(); + + smp_rmb(); /* redundant after locking primitive, but let it be */ + + if (stop_cpus) + { + /* + * Pause-to-console request is pending. + * + * Cannot stop the CPU right now because console thread already expects this CPU thread to join the barrier. + * Will join the barrier and stop CPU later, when instruction that caused stop condition + * (HALT or non-interruptable infinite branch to self) will be re-executed when this CPU is resumed. + */ + if (cpu_unit->cpu_stop_code == STOP_HALT) + cpu_backstep_pc(RUN_PASS); + + cpu_unit->cpu_stop_code = SCPE_STOP; + } + else + { + join_console = FALSE; + cpu_unit->cpu_state = CPU_STATE_STANDBY; + cpu_running_set.clear(cpu_unit->cpu_id); + sim_mp_active_update(); + syncw_leave_all(RUN_PASS, SYNCW_OVERRIDE_ALL | SYNCW_DISABLE_CPU); + cpu_stopping_ips_rate_update(RUN_PASS); + + /* update sim time */ + UPDATE_CPU_SIM_TIME(); + + /* + * If there are events for system-wide devices pending in this CPU's event queue, + * ask the primary CPU to transfer them over to the primary CPU's queue. + */ + if (sim_cpu_has_syswide_events(RUN_PASS)) + cpu_unit->cpu_requeue_syswide_pending = TRUE; + + /* notify the primary that secondary is shutting down */ + interrupt_set_int(&cpu_unit_0, IPL_SECEXIT, INT_V_SECEXIT); + } + + cpu_database_lock->unlock(); + + if (! join_console) + cpu_set_thread_priority(RUN_PASS, SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI); + } + + if (join_console) + { + cpu_attention->release(); + cpu_pause_sync_barrier->wait(); + } + } + } + sim_catch (sim_exception_SimError, exc) + { + fprintf(smp_stderr, "\nFatal error in %s simulator, unexpected exception while executing CPU%d\n", sim_name, cpu_unit->cpu_id); + fprintf(smp_stderr, "Exception cause: %s\n", exc->get_message()); + fprintf(smp_stderr, "Terminating the simulator abnormally...\n"); + exit(1); + } + sim_end_try + +#if defined(USE_C_TRY_CATCH) + /* relax compiler false warning */ + SMP_THREAD_ROUTINE_END; +#endif +} + +SMP_THREAD_ROUTINE_DECL sim_clock_thread_proc (void* arg) +{ + sim_try + { + smp_thread_init(); + + run_scope_context* rscx = new run_scope_context(NULL, SIM_THREAD_TYPE_CLOCK, sim_clock_thread); + rscx->set_current(); + + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CLOCK); + smp_set_thread_name("CLOCK"); + + uint32 ms = 1000 / clk_tps; + cpu_set synclk_set; + + for (;;) + { + cpu_clock_run_gate->wait(); + for (;;) + { + /* sleep one tick */ + sim_os_ms_sleep(ms); + + /* broadcast clock strobe interrupts (SYNCLK) */ + cpu_database_lock->lock(); + synclk_set = cpu_running_set; + cpu_database_lock->unlock(); + + for (uint32 ix = 0; ix < sim_ncpus; ix++) + { + if (synclk_set.is_set(ix)) + { + interrupt_set_int(cpu_units[ix], IPL_SYNCLK, INT_V_SYNCLK); + } + } + + if (weak_read(stop_cpus)) break; + } + cpu_pause_sync_barrier->wait(); + } + } + sim_catch (sim_exception_SimError, exc) + { + fprintf(smp_stderr, "\nFatal error in %s simulator, unexpected exception while executing clock thread\n", sim_name); + fprintf(smp_stderr, "Exception cause: %s\n", exc->get_message()); + fprintf(smp_stderr, "Terminating the simulator abnormally...\n"); + exit(1); + } + sim_end_try + +#if defined(USE_C_TRY_CATCH) + /* relax compiler false warning */ + SMP_THREAD_ROUTINE_END; +#endif +} + +SIM_INLINE static t_bool cpu_should_actually_change_thread_priority(RUN_DECL, sim_thread_priority_t prio, sim_thread_priority_t* actprio) +{ + if (must_control_prio()) + return TRUE; + + /* + * If thread priority control is disabled, let through only calls that raise to CALIBRATION level + * and drop down from it + */ + + if (unlikely(prio == SIMH_THREAD_PRIORITY_CPU_CALIBRATION)) + { + /* allow to raise to CALIBRATION */ + return TRUE; + } + + if (cpu_unit->cpu_thread_priority == SIMH_THREAD_PRIORITY_CPU_CALIBRATION && prio < cpu_unit->cpu_thread_priority) + { + /* allow to drop down from CALIBRATION -- drop down always to RUN */ + *actprio = SIMH_THREAD_PRIORITY_CPU_RUN; + return TRUE; + } + + if (cpu_unit->cpu_thread_priority == SIMH_THREAD_PRIORITY_INVALID) + { + /* current priority is unknown -- always force to RUN */ + *actprio = SIMH_THREAD_PRIORITY_CPU_RUN; + return TRUE; + } + + return FALSE; +} + +void cpu_set_thread_priority(RUN_DECL, sim_thread_priority_t prio) +{ + if (cpu_unit->cpu_thread_priority != prio) + { + sim_thread_priority_t actprio = prio; + + if (cpu_should_actually_change_thread_priority(RUN_PASS, prio, &actprio)) + { + cpu_unit->cpu_thread_priority = prio; + smp_set_thread_priority(actprio); + } + else + { + cpu_unit->cpu_thread_priority = prio; + } + } +} + +void cpu_set_thread_priority(RUN_RSCX_DECL, sim_thread_priority_t prio) +{ + if (cpu_unit->cpu_id == rscx->thread_cpu_id && + rscx->thread_type == SIM_THREAD_TYPE_CPU && + cpu_unit->cpu_thread_priority != prio) + { + sim_thread_priority_t actprio = prio; + + if (cpu_should_actually_change_thread_priority(RUN_PASS, prio, &actprio)) + { + cpu_unit->cpu_thread_priority = prio; + smp_set_thread_priority(actprio); + } + else + { + cpu_unit->cpu_thread_priority = prio; + } + } +} + + +/* Print stopped message */ + +void fprint_stopped (SMP_FILE *st) +{ + fprint_stopped_gen (st, sim_PC, sim_dflt_dev); +} + +const char* sim_stop_code_message(t_stat stop_code) +{ + if (stop_code >= SCPE_BASE) + return sim_error_text (stop_code); + else + return sim_stop_messages[stop_code]; +} + +void fprint_stopped_gen (SMP_FILE *st, REG *pc, DEVICE *dptr) +{ + RUN_SCOPE_RSCX; + const char* msg; + + /* handle "STEP" command case */ + if (sim_step && cpu_unit->cpu_stop_code == SCPE_STEP) + { + /* if step = 1 (common case), prevailing chances are that nothing had been output + to console by the command, so no need for newline */ + if (sim_step != 1) + fprintf (st, "\n"); + + msg = (sim_step == 1) ? NULL : sim_stop_code_message(cpu_unit->cpu_stop_code); + fprint_stopped_instr(RUN_PASS, st, msg, pc, dptr); + return; + } + + CPU_UNIT* sv_cpu_unit = rscx->cpu_unit; + t_stat prev_stop_code = SCPE_OK; /* init to suppress false GCC warning */ + t_bool printed = FALSE; + + fprintf (st, "\n"); + + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + rscx->cpu_unit = cpu_unit = cpu_units[cpu_ix]; + + if (cpu_unit->cpu_state != CPU_STATE_RUNNABLE && + cpu_unit->cpu_state != CPU_STATE_RUNNING) + { + msg = NULL; + fprint_stopped_instr_or_state(RUN_PASS, st, msg, pc, dptr); + printed = TRUE; + continue; + } + + t_stat stop_code = cpu_unit->cpu_stop_code; + msg = sim_stop_code_message(stop_code); + + if (stop_code == SCPE_STOP || stop_code == SCPE_SIGERR) + { + if (printed && stop_code == prev_stop_code) + msg = NULL; + } + + fprint_stopped_instr_or_state(RUN_PASS, st, msg, pc, dptr); + prev_stop_code = stop_code; + printed = TRUE; + } + + /* if nothing had been printed, print for the primary VCPU */ + if (! printed) + { + rscx->cpu_unit = cpu_unit = & cpu_unit_0; + msg = sim_stop_code_message(cpu_unit->cpu_stop_code); + fprint_stopped_instr_or_state (RUN_PASS, st, msg, pc, dptr); + } + + rscx->cpu_unit = sv_cpu_unit; +} + +void fprint_stopped_instr_or_state (RUN_DECL, SMP_FILE *st, const char* msg, REG *pc, DEVICE *dptr) +{ + if (cpu_unit->cpu_state == CPU_STATE_RUNNABLE || + cpu_unit->cpu_state == CPU_STATE_RUNNING) + { + fprint_stopped_instr (RUN_PASS, st, msg, pc, dptr); + } + else + { + if (msg != NULL) + fprintf (st, "%s\n", msg); + + if (sim_ncpus != 1) + fprintf (st, "CPU %02d ", cpu_unit->cpu_id); + + fprintf (st, "%s\n", cpu_describe_state(cpu_unit)); + } +} + +void fprint_stopped_instr (RUN_DECL, SMP_FILE *st, const char* msg, REG *pc, DEVICE *dptr) +{ + int32 i; + t_stat r = 0; + t_addr k; + + if (msg != NULL) + fprintf (st, "%s\n", msg); + + if (sim_ncpus != 1) + fprintf (st, "CPU %02d ", cpu_unit->cpu_id); + + fprintf (st, "%s: ", pc->name); + + t_value pcval = get_rval (pc, 0); + + if (sim_vm_fprint_addr) + sim_vm_fprint_addr (st, dptr, (t_addr) pcval); + else + fprint_val (st, pcval, pc->radix, pc->width, pc->flags & REG_FMT); + + if (dptr != NULL && dptr->examine != NULL) + { + for (i = 0; i < sim_emax; i++) + sim_eval[i] = 0; + + for (i = 0, k = (t_addr) pcval; i < sim_emax; i++, k = k + dptr->aincr) + { + if ((r = dptr->examine (&sim_eval[i], k, dptr == &cpu_dev ? cpu_unit : dptr->units[0], SWMASK ('V'))) != SCPE_OK) + break; + } + + if (r == SCPE_OK || i > 0) + { + fprintf (st, " "); + if (fprint_sym (st, (t_addr) pcval, sim_eval, NULL, SWMASK('M')|SIM_SW_STOP) > 0) + fprint_val (st, sim_eval[0], dptr->dradix, dptr->dwidth, PV_RZRO); + } + } + + fprintf (st, "\n"); + +} + +/* Signal handler for ^C signal - set stop simulation flag */ + +void int_handler (int sig) +{ + stop_cpus = 1; +} + +/* Examine/deposit commands + + ex[amine] [modifiers] list examine + de[posit] [modifiers] list val deposit + ie[xamine] [modifiers] list interactive examine + id[eposit] [modifiers] list interactive deposit + + modifiers + @filename output file + -letter(s) switches + devname'n device name and unit number + [{&|^}value]{=|==|!|!=|>|>=|<|<=} value search specification + + list list of addresses and registers + addr[:addr|-addr] address range + ALL all addresses + register[:register|-register] register range + STATE all registers +*/ + +t_stat exdep_cmd (int32 flag, char *cptr) +{ + char gbuf[CBUFSIZE], *gptr, *tptr; + int32 opt; + t_addr low, high; + t_stat reason; + DEVICE *tdptr; + REG *lowr, *highr; + SMP_FILE *ofile; + + opt = CMD_OPT_SW|CMD_OPT_SCH|CMD_OPT_DFT; /* options for all */ + if (flag == EX_E) /* extra for EX */ + opt = opt | CMD_OPT_OF; + cptr = get_sim_opt (opt, cptr, &reason); /* get cmd options */ + if (!cptr) /* error? */ + return reason; + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + if (sim_dfunit == NULL) /* got a unit? */ + return SCPE_NXUN; + check_select_cpu(sim_dfunit); + cptr = get_glyph (cptr, gbuf, 0); /* get list */ + if ((flag == EX_D) && (*cptr == 0)) /* deposit needs more */ + return SCPE_2FARG; + ofile = sim_ofile? sim_ofile: smp_stdout; /* no ofile? use stdout */ + + for (gptr = gbuf, reason = SCPE_OK; + (*gptr != 0) && (reason == SCPE_OK); gptr = tptr) + { + tdptr = sim_dfdev; /* working dptr */ + if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) + { + tptr = gptr + strlen ("STATE"); + if (*tptr && (*tptr++ != ',')) + return SCPE_ARG; + if ((lowr = sim_dfdev->registers) == NULL) + return SCPE_NXREG; + for (highr = lowr; highr->name != NULL; highr++) ; + sim_switches = sim_switches | SIM_SW_HIDE; + reason = exdep_reg_loop (ofile, sim_schptr, flag, cptr, + lowr, --highr, 0, 0); + continue; + } + + if ((lowr = find_reg (gptr, &tptr, tdptr)) || /* local reg or */ + (!(sim_opt_out & CMD_OPT_DFT) && /* no dflt, global? */ + (lowr = find_reg_glob (gptr, &tptr, &tdptr)))) + { + low = high = 0; + if ((*tptr == '-') || (*tptr == ':')) + { + highr = find_reg (tptr + 1, &tptr, tdptr); + if (highr == NULL) + return SCPE_NXREG; + } + else + { + highr = lowr; + if (*tptr == '[') + { + if (lowr->depth <= 1) + return SCPE_ARG; + tptr = get_range (NULL, tptr + 1, &low, &high, + 10, lowr->depth - 1, ']'); + if (tptr == NULL) + return SCPE_ARG; + } + } + if (*tptr && (*tptr++ != ',')) + return SCPE_ARG; + reason = exdep_reg_loop (ofile, sim_schptr, flag, cptr, + lowr, highr, (uint32) low, (uint32) high); + continue; + } + + tptr = get_range (sim_dfdev, gptr, &low, &high, sim_dfdev->aradix, + (((sim_dfunit->capac == 0) || (flag == EX_E))? 0: + sim_dfunit->capac - sim_dfdev->aincr), 0); + if (tptr == NULL) + return SCPE_ARG; + if (*tptr && (*tptr++ != ',')) + return SCPE_ARG; + reason = exdep_addr_loop (ofile, sim_schptr, flag, cptr, low, high, + sim_dfdev, sim_dfunit); + } + + if (sim_ofile) /* close output file */ + fclose (sim_ofile); + + return reason; +} + +/* Loop controllers for examine/deposit + + exdep_reg_loop examine/deposit range of registers + exdep_addr_loop examine/deposit range of addresses +*/ + +t_stat exdep_reg_loop (SMP_FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr, + REG *lowr, REG *highr, uint32 lows, uint32 highs) +{ + t_stat reason; + uint32 idx; + t_value val; + REG *rptr; + + if (lowr == NULL || highr == NULL) + return SCPE_IERR; + if (lowr > highr) + return SCPE_ARG; + for (rptr = lowr; rptr <= highr; rptr++) + { + if ((sim_switches & SIM_SW_HIDE) && + (rptr->flags & REG_HIDDEN)) + continue; + + for (idx = lows; idx <= highs; idx++) + { + if (idx >= rptr->depth) + return SCPE_SUB; + val = get_rval (rptr, idx); + if (schptr && !test_search (val, schptr)) + continue; + if (flag != EX_D) + { + reason = ex_reg (ofile, val, flag, rptr, idx); + if (reason != SCPE_OK) + return reason; + if (sim_log && ofile == smp_stdout) + ex_reg (sim_log, val, flag, rptr, idx); + } + if (flag != EX_E) + { + reason = dep_reg (flag, cptr, rptr, idx); + if (reason != SCPE_OK) + return reason; + } + } + } + + return SCPE_OK; +} + +t_stat exdep_addr_loop (SMP_FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr, + t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr) +{ +t_addr i, mask; +t_stat reason; + +if (uptr->flags & UNIT_DIS) /* disabled? */ + return SCPE_UDIS; +mask = (t_addr) width_mask[dptr->awidth]; +if ((low > mask) || (high > mask) || (low > high)) + return SCPE_ARG; +for (i = low; i <= high; ) { /* all paths must incr!! */ + reason = get_aval (i, dptr, uptr); /* get data */ + if (reason != SCPE_OK) /* return if error */ + return reason; + if (schptr && !test_search (sim_eval[0], schptr)) + i = i + dptr->aincr; /* sch fails, incr */ + else { /* no sch or success */ + if (flag != EX_D) { /* ex, ie, or id? */ + reason = ex_addr (ofile, flag, i, dptr, uptr); + if (reason > SCPE_OK) + return reason; + if (sim_log && (ofile == smp_stdout)) + ex_addr (sim_log, flag, i, dptr, uptr); + } + else reason = 1 - dptr->aincr; /* no, dflt incr */ + if (flag != EX_E) { /* ie, id, or d? */ + reason = dep_addr (flag, cptr, i, dptr, uptr, reason); + if (reason > SCPE_OK) + return reason; + } + i = i + (1 - reason); /* incr */ + } + } +return SCPE_OK; +} + +/* Examine register routine + + Inputs: + ofile = output stream + val = current register value + flag = type of ex/mod command (ex, iex, idep) + rptr = pointer to register descriptor + idx = index + Outputs: + return = error status +*/ + +t_stat ex_reg (SMP_FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx) +{ + RUN_SCOPE; + int32 rdx; + + if (rptr == NULL) + return SCPE_IERR; + + if (sim_ncpus != 1) + { + int rcpuid = -1; + if (rptr->locinfo.loctype == REG_LOCTYPE_CPU) + { + rcpuid = cpu_unit->cpu_id; + } + else if (rptr->locinfo.loctype == REG_LOCTYPE_DYN) + { + if (rptr->locinfo.get_vcpu) + rcpuid = (*rptr->locinfo.get_vcpu)(rptr, idx); + } + + if (rcpuid >= 0) + { + fprintf (ofile, "[cpu%d]\t", rcpuid); + } + else + { + fprintf (ofile, "\t"); + } + } + + if (rptr->depth > 1) + fprintf (ofile, "%s[%d]:\t", rptr->name, idx); + else + fprintf (ofile, "%s:\t", rptr->name); + if (!(flag & EX_E)) + return SCPE_OK; + GET_RADIX (rdx, rptr->radix); + if ((rptr->flags & REG_VMAD) && sim_vm_fprint_addr) + sim_vm_fprint_addr (ofile, sim_dflt_dev, (t_addr) val); + else if (!(rptr->flags & REG_VMIO) || + fprint_sym (ofile, rdx, &val, NULL, sim_switches | SIM_SW_REG) > 0) + { + fprint_val (ofile, val, rdx, rptr->width, rptr->flags & REG_FMT); + } + if (flag & EX_I) + fprintf (ofile, "\t"); + else + fprintf (ofile, "\n"); + return SCPE_OK; +} + +/* Get register value + + Inputs: + rptr = pointer to register descriptor + idx = index + Outputs: + return = register value +*/ + +t_value get_rval (REG *rptr, uint32 idx) +{ + RUN_SCOPE; + size_t sz = SZ_R (rptr); + t_value val; + + if (rptr->locinfo.loctype == REG_LOCTYPE_DYN) + { + val = (*rptr->locinfo.get_rvalue)(rptr, idx); + return val & width_mask[rptr->width]; + } + + if (rptr->depth > 1 && (rptr->flags & REG_CIRC)) + { + idx = idx + rptr->getqptr(RUN_PASS); + if (idx >= rptr->depth) idx = idx - rptr->depth; + } + + if (rptr->depth > 1 && (rptr->flags & REG_UNIT)) + { + void* vptr = rptr->getloc_unit_idx(RUN_PASS, idx); +#if defined (USE_INT64) + if (sz <= sizeof (uint32)) + val = *((uint32 *) vptr); + else + val = *((t_uint64 *) vptr); +#else + val = *((uint32 *) vptr); +#endif + } + else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) && + (sz == sizeof (uint8))) + val = *(((uint8 *) rptr->getloc(RUN_PASS)) + idx); + else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) && + (sz == sizeof (uint16))) + val = *(((uint16 *) rptr->getloc(RUN_PASS)) + idx); +#if defined (USE_INT64) + else if (sz <= sizeof (uint32)) + val = *(((uint32 *) rptr->getloc(RUN_PASS)) + idx); + else + val = *(((t_uint64 *) rptr->getloc(RUN_PASS)) + idx); +#else + else + val = *(((uint32 *) rptr->getloc(RUN_PASS)) + idx); +#endif + + val = (val >> rptr->offset) & width_mask[rptr->width]; + + return val; +} + +/* Deposit register routine + + Inputs: + flag = type of deposit (normal/interactive) + cptr = pointer to input string + rptr = pointer to register descriptor + idx = index + Outputs: + return = error status +*/ + +t_stat dep_reg (int32 flag, char *cptr, REG *rptr, uint32 idx) +{ + t_stat r; + t_value val, mask; + int32 rdx; + char *tptr, gbuf[CBUFSIZE]; + + if (cptr == NULL || rptr == NULL) + return SCPE_IERR; + if (rptr->flags & REG_RO) + return SCPE_RO; + if (flag & EX_I) + { + cptr = read_line (gbuf, CBUFSIZE, smp_stdin); + if (sim_log) + fprintf (sim_log, (cptr? "%s\n": "\n"), cptr); + if (cptr == NULL) /* force exit */ + return 1; + if (*cptr == 0) /* success */ + return SCPE_OK; + } + mask = width_mask[rptr->width]; + GET_RADIX (rdx, rptr->radix); + if ((rptr->flags & REG_VMAD) && sim_vm_parse_addr) /* address form? */ + { + val = sim_vm_parse_addr (sim_dflt_dev, cptr, &tptr); + if (tptr == cptr || *tptr != 0 || val > mask) + return SCPE_ARG; + } + else if (!(rptr->flags & REG_VMIO) || /* dont use sym? */ + parse_sym (cptr, rdx, NULL, &val, sim_switches | SIM_SW_REG) > SCPE_OK) + { + val = get_uint (cptr, rdx, mask, &r); + if (r != SCPE_OK) + return SCPE_ARG; + } + if ((rptr->flags & REG_NZ) && (val == 0)) + return SCPE_ARG; + put_rval (rptr, idx, val); + return SCPE_OK; +} + +/* Put register value + + Inputs: + rptr = pointer to register descriptor + idx = index + val = new value + mask = mask + Outputs: + none +*/ + +void put_rval (REG *rptr, uint32 idx, t_value val) +{ + RUN_SCOPE; + size_t sz; + t_value mask; + +#define PUT_RVAL(sz,rp,id,v,m) \ + *(((sz *) rp->getloc(RUN_PASS)) + id) = \ + (*(((sz *) rp->getloc(RUN_PASS)) + id) & \ + ~((m) << (rp)->offset)) | ((v) << (rp)->offset) + + if (rptr == sim_PC) + sim_brk_npc (RUN_PASS, 0); + sz = SZ_R (rptr); + mask = width_mask[rptr->width]; + + if (rptr->locinfo.loctype == REG_LOCTYPE_DYN) + { + (*rptr->locinfo.set_rvalue)(rptr, idx, val & mask); + return; + } + + if (rptr->depth > 1 && (rptr->flags & REG_CIRC)) + { + idx = idx + rptr->getqptr(RUN_PASS); + if (idx >= rptr->depth) + idx = idx - rptr->depth; + } + if (rptr->depth > 1 && (rptr->flags & REG_UNIT)) + { + void* vptr = rptr->getloc_unit_idx(RUN_PASS, idx); +#if defined (USE_INT64) + if (sz <= sizeof (uint32)) + *((uint32 *) vptr) = (*((uint32 *) vptr) & + ~(((uint32) mask) << rptr->offset)) | + (((uint32) val) << rptr->offset); + else *((t_uint64 *) vptr) = (*((t_uint64 *) vptr) + & ~(mask << rptr->offset)) | (val << rptr->offset); +#else + *((uint32 *) vptr) = (*((uint32 *) vptr) & + ~(((uint32) mask) << rptr->offset)) | + (((uint32) val) << rptr->offset); +#endif + } + else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) && + (sz == sizeof (uint8))) + PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask); + else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) && + (sz == sizeof (uint16))) + PUT_RVAL (uint16, rptr, idx, (uint32) val, (uint32) mask); +#if defined (USE_INT64) + else if (sz <= sizeof (uint32)) + PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask); + else PUT_RVAL (t_uint64, rptr, idx, val, mask); +#else + else PUT_RVAL (uint32, rptr, idx, val, mask); +#endif +} + +/* Examine address routine + + Inputs: (sim_eval is an implicit argument) + ofile = output stream + flag = type of ex/mod command (ex, iex, idep) + addr = address to examine + dptr = pointer to device + uptr = pointer to unit + Outputs: + return = if > 0, error status + if <= 0,-number of extra addr units retired +*/ + +t_stat ex_addr (SMP_FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr) +{ +t_stat reason; +int32 rdx; + +if (sim_vm_fprint_addr) + sim_vm_fprint_addr (ofile, dptr, addr); +else fprint_val (ofile, addr, dptr->aradix, dptr->awidth, PV_LEFT); +fprintf (ofile, ":\t"); +if (!(flag & EX_E)) + return (1 - dptr->aincr); + +GET_RADIX (rdx, dptr->dradix); +if ((reason = fprint_sym (ofile, addr, sim_eval, uptr, sim_switches)) > 0) { + fprint_val (ofile, sim_eval[0], rdx, dptr->dwidth, PV_RZRO); + reason = 1 - dptr->aincr; + } +if (flag & EX_I) + fprintf (ofile, "\t"); +else fprintf (ofile, "\n"); +return reason; +} + +/* Get address routine + + Inputs: + flag = type of ex/mod command (ex, iex, idep) + addr = address to examine + dptr = pointer to device + uptr = pointer to unit + Outputs: (sim_eval is an implicit output) + return = error status +*/ + +t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr) +{ +int32 i; +t_value mask; +t_addr j, loc; +size_t sz; +t_stat reason = SCPE_OK; + +if ((dptr == NULL) || (uptr == NULL)) + return SCPE_IERR; +mask = width_mask[dptr->dwidth]; +for (i = 0; i < sim_emax; i++) + sim_eval[i] = 0; +for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr->aincr) { + if (dptr->examine != NULL) { + reason = dptr->examine (&sim_eval[i], j, uptr, sim_switches); + if (reason != SCPE_OK) + break; + } + else { + if (!(uptr->flags & UNIT_ATT)) + return SCPE_UNATT; + if (uptr->flags & UNIT_RAW) + return SCPE_NOFNC; + if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) { + reason = SCPE_NXM; + break; + } + sz = SZ_D (dptr); + loc = j / dptr->aincr; + if (uptr->flags & UNIT_BUF) { + SZ_LOAD (sz, sim_eval[i], uptr->filebuf, loc); + } + else { + sim_fseek (uptr->fileref, (t_addr) (sz * loc), SEEK_SET); + sim_fread (&sim_eval[i], sz, 1, uptr->fileref); + if ((feof (uptr->fileref)) && + !(uptr->flags & UNIT_FIX)) { + reason = SCPE_EOF; + break; + } + else if (ferror (uptr->fileref)) { + clearerr (uptr->fileref); + reason = SCPE_IOERR; + break; + } + } + } + sim_eval[i] = sim_eval[i] & mask; + } +if ((reason != SCPE_OK) && (i == 0)) + return reason; +return SCPE_OK; +} + +/* Deposit address routine + + Inputs: + flag = type of deposit (normal/interactive) + cptr = pointer to input string + addr = address to examine + dptr = pointer to device + uptr = pointer to unit + dfltinc = value to return on cr input + Outputs: + return = if > 0, error status + if <= 0, -number of extra address units retired +*/ + +t_stat dep_addr (int32 flag, char *cptr, t_addr addr, DEVICE *dptr, + UNIT *uptr, int32 dfltinc) +{ +int32 i, count, rdx; +t_addr j, loc; +t_stat r, reason; +t_value mask; +size_t sz; +char gbuf[CBUFSIZE]; + +if (dptr == NULL) + return SCPE_IERR; +if (flag & EX_I) { + cptr = read_line (gbuf, CBUFSIZE, smp_stdin); + if (sim_log) + fprintf (sim_log, (cptr? "%s\n": "\n"), cptr); + if (cptr == NULL) /* force exit */ + return 1; + if (*cptr == 0) /* success */ + return dfltinc; + } +if (uptr->flags & UNIT_RO) /* read only? */ + return SCPE_RO; +mask = width_mask[dptr->dwidth]; + +GET_RADIX (rdx, dptr->dradix); +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; + +for (i = 0, j = addr; i < count; i++, j = j + dptr->aincr) { + sim_eval[i] = sim_eval[i] & mask; + if (dptr->deposit != NULL) { + r = dptr->deposit (sim_eval[i], j, uptr, sim_switches); + if (r != SCPE_OK) + return r; + } + else { + if (!(uptr->flags & UNIT_ATT)) + return SCPE_UNATT; + if (uptr->flags & UNIT_RAW) + return SCPE_NOFNC; + if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) + return SCPE_NXM; + sz = SZ_D (dptr); + loc = j / dptr->aincr; + if (uptr->flags & UNIT_BUF) { + SZ_STORE (sz, sim_eval[i], uptr->filebuf, loc); + if (loc >= uptr->hwmark) + uptr->hwmark = (uint32) loc + 1; + } + else { + sim_fseek (uptr->fileref, (t_addr) (sz * loc), SEEK_SET); + sim_fwrite (&sim_eval[i], sz, 1, uptr->fileref); + if (ferror (uptr->fileref)) { + clearerr (uptr->fileref); + return SCPE_IOERR; + } + } + } + } +return reason; +} + +/* Evaluate command */ + +t_stat eval_cmd (int32 flg, char *cptr) +{ +DEVICE *dptr = sim_dflt_dev; +int32 i, rdx, a, lim; +t_stat r; + +GET_SWITCHES (cptr); +GET_RADIX (rdx, dptr->dradix); +for (i = 0; i < sim_emax; i++) +sim_eval[i] = 0; +if (*cptr == 0) + return SCPE_2FARG; +if ((r = parse_sym (cptr, 0, dptr->units[0], sim_eval, sim_switches)) > 0) { + sim_eval[0] = get_uint (cptr, rdx, width_mask[dptr->dwidth], &r); + if (r != SCPE_OK) + return r; + } +lim = 1 - r; +for (i = a = 0; a < lim; ) { + smp_printf ("%d:\t", a); + if ((r = fprint_sym (smp_stdout, a, &sim_eval[i], dptr->units[0], sim_switches)) > 0) + r = fprint_val (smp_stdout, sim_eval[i], rdx, dptr->dwidth, PV_RZRO); + smp_printf ("\n"); + if (sim_log) { + fprintf (sim_log, "%d\t", i); + if ((r = fprint_sym (sim_log, a, &sim_eval[i], dptr->units[0], sim_switches)) > 0) + r = fprint_val (sim_log, sim_eval[i], rdx, dptr->dwidth, PV_RZRO); + fprintf (sim_log, "\n"); + } + if (r < 0) + a = a + 1 - r; + else a = a + dptr->aincr; + i = a / dptr->aincr; + } +return SCPE_OK; +} + +/* String processing routines + + read_line read line + + Inputs: + 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 (char *cptr, int32 size, SMP_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, SMP_FILE *stream) +{ + char *tptr; +#if defined(HAVE_DLOPEN) + static int initialized = 0; + static char *(*p_readline)(const char *) = NULL; + static void (*p_add_history)(const char *) = NULL; + + if (!initialized) + { + initialized = 1; + void *handle; + +#define __STR_QUOTE(tok) #tok +#define __STR(tok) __STR_QUOTE(tok) + handle = dlopen("libncurses." __STR(HAVE_DLOPEN), RTLD_NOW|RTLD_GLOBAL); + handle = dlopen("libcurses." __STR(HAVE_DLOPEN), RTLD_NOW|RTLD_GLOBAL); + handle = dlopen("libreadline." __STR(HAVE_DLOPEN), RTLD_NOW|RTLD_GLOBAL); + if (handle) + { + p_readline = (char *(*)(const char *)) dlsym(handle, "readline"); + p_add_history = (void (*)(const char *)) dlsym(handle, "add_history"); + } + } + + if (prompt) /* interactive? */ + { + char *tmpc; + + if (p_readline) + { + char *tmpc = p_readline (prompt); /* get cmd line */ + if (tmpc == NULL) /* bad result? */ + cptr = NULL; + else + { + strncpy (cptr, tmpc, size); /* copy result */ + free (tmpc); /* free temp */ + } + } + else + { + smp_printf ("%s", prompt); /* display prompt */ + cptr = fgets (cptr, size, stream); /* get cmd line */ + } + } + else + { + cptr = fgets (cptr, size, stream); /* get cmd line */ + } +#else + if (prompt) /* interactive? */ + smp_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 */ + } + for (tptr = cptr; tptr < (cptr + size); tptr++) /* remove cr or nl */ + { + if (*tptr == '\n' || *tptr == '\r' || + tptr == cptr + size - 1) /* str max length? */ + { + *tptr = 0; /* terminate */ + break; + } + } + while (isspace (*cptr)) /* trim leading spc */ + cptr++; + if (*cptr == ';') /* ignore comment */ + { + char pmt[32]; + make_prompt (pmt, "do"); + if (sim_do_echo) /* echo comments if -v */ + smp_printf("%s%s\n", pmt, cptr); + if (sim_do_echo && sim_log) + fprintf (sim_log, "%s%s\n", pmt, cptr); + *cptr = 0; + } + +#if defined (HAVE_DLOPEN) + if (prompt && p_add_history && *cptr) /* Save non blank lines in history */ + p_add_history (cptr); +#endif + + return cptr; +} + +/* get_glyph get next glyph (force upper case) + get_glyph_nc get next glyph (no conversion) + get_glyph_gen get next glyph (general case) + + Inputs: + iptr = pointer to input string + optr = pointer to output string + mchar = optional end of glyph character + flag = TRUE for convert to upper case (_gen only) + Outputs + result = pointer to next character in input string +*/ + +char *get_glyph_gen (char *iptr, char *optr, char mchar, t_bool uc) +{ + while (!isspace(*iptr) && *iptr && *iptr != mchar) + { + if (islower (*iptr) && uc) + *optr = toupper (*iptr); + else + *optr = *iptr; + iptr++; optr++; + } + *optr = 0; + if (mchar && *iptr == mchar) /* skip terminator */ + iptr++; + while (isspace (*iptr)) /* absorb spaces */ + iptr++; + return iptr; +} + +char *get_glyph (char *iptr, char *optr, char mchar) +{ + return get_glyph_gen (iptr, optr, mchar, TRUE); +} + +char *get_glyph_nc (char *iptr, char *optr, char mchar) +{ + return get_glyph_gen (iptr, optr, mchar, FALSE); +} + +/* Trim trailing spaces from a string + + Inputs: + cptr = pointer to string + Outputs: + cptr = pointer to string +*/ + +char *sim_trim_endspc (char *cptr) +{ +char *tptr; + +tptr = cptr + strlen (cptr); +while ((--tptr >= cptr) && isspace (*tptr)) + *tptr = 0; +return cptr; +} + +/* get_yn yes/no question + + Inputs: + cptr = pointer to question + deflt = default answer + Outputs: + result = true if yes, false if no +*/ + +t_stat get_yn (char *ques, t_stat deflt) +{ +char cbuf[CBUFSIZE], *cptr; + +printf ("%s ", ques); +cptr = read_line (cbuf, CBUFSIZE, smp_stdin); +if ((cptr == NULL) || (*cptr == 0)) + return deflt; +if ((*cptr == 'Y') || (*cptr == 'y')) + return TRUE; +return FALSE; +} + +/* get_uint unsigned number + + Inputs: + cptr = pointer to input string + radix = input radix + max = maximum acceptable value + *status = pointer to error status + Outputs: + val = value +*/ + +t_value get_uint (char *cptr, uint32 radix, t_value max, t_stat *status) +{ + t_value val; + char *tptr; + + *status = SCPE_OK; + val = strtotv (cptr, &tptr, radix); + if (cptr == tptr || val > max) + *status = SCPE_ARG; + else + { + while (isspace (*tptr)) tptr++; + if (*tptr) + *status = SCPE_ARG; + } + return val; +} + +/* get_uint unsigned number + + Inputs: + cptr = pointer to input string + *status = pointer to error status + Outputs: + val = value +*/ + +t_stat get_double(char *cptr, double* pval) +{ + double val = 0; + + if (*cptr != '.' && !isdigit(*cptr)) + return SCPE_ARG; + + while (isdigit(*cptr)) + val = val * 10 + (*cptr++ - '0'); + + if (*cptr == '.') + { + cptr++; + double scale = 0.1; + while (isdigit(*cptr)) + { + val += (*cptr++ - '0') * scale; + scale *= 0.1; + } + } + + while (isspace(*cptr)) cptr++; + + if (*cptr) return SCPE_ARG; + + *pval = val; + + return SCPE_OK; +} + +/* get_range range specification + + Inputs: + dptr = pointer to device (NULL if none) + cptr = pointer to input string + *lo = pointer to low result + *hi = pointer to high result + aradix = radix + max = default high value + term = terminating character, 0 if none + Outputs: + tptr = input pointer after processing + NULL if error +*/ + +char *get_range (DEVICE *dptr, char *cptr, t_addr *lo, t_addr *hi, + uint32 rdx, t_addr max, char term) +{ +char *tptr; + +if (max && strncmp (cptr, "ALL", strlen ("ALL")) == 0) { /* ALL? */ + tptr = cptr + strlen ("ALL"); + *lo = 0; + *hi = max; + } +else { + if (dptr && sim_vm_parse_addr) /* get low */ + *lo = sim_vm_parse_addr (dptr, cptr, &tptr); + else *lo = (t_addr) strtotv (cptr, &tptr, rdx); + if (cptr == tptr) /* error? */ + return NULL; + if ((*tptr == '-') || (*tptr == ':')) { /* range? */ + cptr = tptr + 1; + if (dptr && sim_vm_parse_addr) /* get high */ + *hi = sim_vm_parse_addr (dptr, cptr, &tptr); + else *hi = (t_addr) strtotv (cptr, &tptr, rdx); + if (cptr == tptr) + return NULL; + if (*lo > *hi) + return NULL; + } + else if (*tptr == '/') { /* relative? */ + cptr = tptr + 1; + *hi = (t_addr) strtotv (cptr, &tptr, rdx); /* get high */ + if ((cptr == tptr) || (*hi == 0)) + return NULL; + *hi = *lo + *hi - 1; + } + else *hi = *lo; + } +if (term && (*tptr++ != term)) + return NULL; +return tptr; +} + +/* get_ipaddr IP address:port + + Inputs: + cptr = pointer to input string + Outputs: + ipa = pointer to IP address (may be NULL), 0 = none + ipp = pointer to IP port (may be NULL), 0 = none + result = status +*/ + +t_stat get_ipaddr (char *cptr, uint32 *ipa, uint32 *ipp) +{ +char gbuf[CBUFSIZE]; +char *addrp, *portp, *octetp; +uint32 i, addr, port, octet; +t_stat r; + +if ((cptr == NULL) || (*cptr == 0)) + return SCPE_ARG; +strncpy (gbuf, cptr, CBUFSIZE); +addrp = gbuf; /* default addr */ +if (portp = strchr (gbuf, ':')) /* x:y? split */ + *portp++ = 0; +else if (strchr (gbuf, '.')) /* x.y...? */ + portp = NULL; +else { + portp = gbuf; /* port only */ + addrp = NULL; /* no addr */ + } +if (portp) { /* port string? */ + if (ipp == NULL) /* not wanted? */ + return SCPE_ARG; + port = (int32) get_uint (portp, 10, 65535, &r); + if ((r != SCPE_OK) || (port == 0)) + return SCPE_ARG; + } +else port = 0; +if (addrp) { /* addr string? */ + if (ipa == NULL) /* not wanted? */ + return SCPE_ARG; + for (i = addr = 0; i < 4; i++) { /* four octets */ + octetp = strchr (addrp, '.'); /* find octet end */ + if (octetp != NULL) /* split string */ + *octetp++ = 0; + else if (i < 3) /* except last */ + return SCPE_ARG; + octet = (int32) get_uint (addrp, 10, 255, &r); + if (r != SCPE_OK) + return SCPE_ARG; + addr = (addr << 8) | octet; + addrp = octetp; + } + if (((addr & 0377) == 0) || ((addr & 0377) == 255)) + return SCPE_ARG; + } +else addr = 0; +if (ipp) /* return req values */ + *ipp = port; +if (ipa) + *ipa = addr; +return SCPE_OK; +} + +/* Find_device find device matching input string + + Inputs: + cptr = pointer to input string + Outputs: + result = pointer to device +*/ + +DEVICE *find_dev (char *cptr) +{ + int32 i; + DEVICE *dptr; + + for (i = 0; (dptr = sim_devices[i]) != NULL; i++) + { + if ((strcmp (cptr, dptr->name) == 0) || + (dptr->lname && + (strcmp (cptr, dptr->lname) == 0))) + { + return dptr; + } + } + return NULL; +} + +DEVICE *find_dev (DIB* dibp) +{ + DEVICE *dptr; + + if (dibp == NULL) + return NULL; + + for (int i = 0; (dptr = sim_devices[i]) != NULL; i++) + { + if (dptr->ctxt == dibp) + return dptr; + } + return NULL; +} + +/* Find_unit find unit matching input string + + Inputs: + cptr = pointer to input string + uptr = pointer to unit pointer + Outputs: + result = pointer to device (null if no dev) + *iptr = pointer to unit (null if nx unit) +*/ + +DEVICE *find_unit (char *cptr, UNIT **uptr) +{ + uint32 i, u; + const char* nptr; + char* tptr; + t_stat r; + DEVICE *dptr; + + if (uptr == NULL) /* arg error? */ + return NULL; + + if (dptr = find_dev (cptr)) /* exact match? */ + { + if (qdisable (dptr)) /* disabled? */ + return NULL; + if (dptr == &cpu_dev && sim_dflt_cpu) + *uptr = sim_dflt_cpu; /* selected CPU unit */ + else + *uptr = dptr->units[0]; /* unit 0 */ + return dptr; + } + + for (i = 0; (dptr = sim_devices[i]) != NULL; i++) /* base + unit#? */ + { + if (dptr->numunits && /* any units? */ + (((nptr = dptr->name) && + (strncmp (cptr, nptr, strlen (nptr)) == 0)) || + ((nptr = dptr->lname) && + (strncmp (cptr, nptr, strlen (nptr)) == 0)))) + { + tptr = cptr + strlen (nptr); + if (isdigit (*tptr)) + { + if (qdisable (dptr)) /* disabled? */ + return NULL; + u = (uint32) get_uint (tptr, 10, dptr->numunits - 1, &r); + if (r != SCPE_OK) /* error? */ + *uptr = NULL; + else + *uptr = dptr->units[u]; + return dptr; + } + } + } + + return NULL; +} + +/* Find_dev_from_unit find device for unit + + Inputs: + uptr = pointer to unit + Outputs: + result = pointer to device +*/ + +DEVICE *find_dev_from_unit (UNIT *uptr) +{ + return uptr ? uptr->device : NULL; +} + +/* Test for disabled device */ + +t_bool qdisable (DEVICE *dptr) +{ + return (dptr->flags & DEV_DIS? TRUE: FALSE); +} + +/* find_reg_glob find globally unique register + + Inputs: + cptr = pointer to input string + optr = pointer to output pointer (can be null) + gdptr = pointer to global device + Outputs: + result = pointer to register, NULL if error + *optr = pointer to next character in input string + *gdptr = pointer to device where found +*/ + +REG *find_reg_glob (char *cptr, char **optr, DEVICE **gdptr) +{ +int32 i; +DEVICE *dptr; +REG *rptr, *srptr = NULL; + +for (i = 0; (dptr = sim_devices[i]) != 0; i++) { /* all dev */ + if (dptr->flags & DEV_DIS) /* skip disabled */ + continue; + if (rptr = find_reg (cptr, optr, dptr)) { /* found? */ + if (srptr) /* ambig? err */ + return NULL; + srptr = rptr; /* save reg */ + *gdptr = dptr; /* save unit */ + } + } +return srptr; +} + +/* find_reg find register matching input string + + Inputs: + cptr = pointer to input string + optr = pointer to output pointer (can be null) + dptr = pointer to device + Outputs: + result = pointer to register, NULL if error + *optr = pointer to next character in input string +*/ + +REG *find_reg (char *cptr, char **optr, DEVICE *dptr) +{ +char *tptr; +REG *rptr; +size_t slnt; + +if ((cptr == NULL) || (dptr == NULL) || (dptr->registers == NULL)) + return NULL; +tptr = cptr; +do { + tptr++; + } while (isalnum (*tptr) || (*tptr == '*') || (*tptr == '_')); +slnt = (unsigned int) (tptr - cptr); +for (rptr = dptr->registers; rptr->name != NULL; rptr++) { + if ((slnt == strlen (rptr->name)) && + (strncmp (cptr, rptr->name, slnt) == 0)) { + if (optr != NULL) + *optr = tptr; + return rptr; + } + } +return NULL; +} + +/* get_switches get switches from input string + + Inputs: + cptr = pointer to input string + Outputs: + sw = switch bit mask + 0 if no switches, -1 if error +*/ + +int32 get_switches (char *cptr) +{ +int32 sw; + +if (*cptr != '-') + return 0; +sw = 0; +for (cptr++; (isspace (*cptr) == 0) && (*cptr != 0); cptr++) { + if (isalpha (*cptr) == 0) + return -1; + sw = sw | SWMASK (toupper (*cptr)); + } +return sw; +} + +/* get_sim_sw accumulate sim_switches + + Inputs: + cptr = pointer to input string + Outputs: + ptr = pointer to first non-string glyph + NULL if error +*/ + +char *get_sim_sw (char *cptr) +{ +int32 lsw; +char gbuf[CBUFSIZE]; + +while (*cptr == '-') { /* while switches */ + cptr = get_glyph (cptr, gbuf, 0); /* get switch glyph */ + lsw = get_switches (gbuf); /* parse */ + if (lsw <= 0) /* invalid? */ + return NULL; + sim_switches = sim_switches | lsw; /* accumulate */ + } +return cptr; +} + +/* get_sim_opt get simulator command options + + Inputs: + opt = command options + cptr = pointer to input string + Outputs: + ptr = pointer to next glypsh, NULL if error + *stat = error status +*/ + +char *get_sim_opt (int32 opt, char *cptr, t_stat *st) +{ +int32 t; +char *svptr, gbuf[CBUFSIZE]; +DEVICE *tdptr; +UNIT *tuptr; + +sim_switches = 0; /* no switches */ +sim_ofile = NULL; /* no output file */ +sim_schptr = NULL; /* no search */ +sim_stab.logic = SCH_OR; /* default search params */ +sim_stab.boolop = SCH_GE; +sim_stab.mask = 0; +sim_stab.comp = 0; +sim_dfdev = sim_dflt_dev; +if (sim_dfdev == &cpu_dev) +{ + sim_dfunit = sim_dflt_cpu; +} +else +{ + sim_dfunit = sim_dfdev->units[0]; +} +sim_opt_out = 0; /* no options yet */ +*st = SCPE_OK; +while (*cptr) { /* loop through modifiers */ + svptr = cptr; /* save current position */ + if ((opt & CMD_OPT_OF) && (*cptr == '@')) { /* output file spec? */ + if (sim_ofile) { /* already got one? */ + fclose (sim_ofile); /* one per customer */ + *st = SCPE_ARG; + return NULL; + } + cptr = get_glyph_nc (cptr + 1, gbuf, 0); + sim_ofile = sim_fopen (gbuf, "a"); /* open for append */ + if (sim_ofile == NULL) { /* open failed? */ + *st = SCPE_OPENERR; + return NULL; + } + sim_opt_out |= CMD_OPT_OF; /* got output file */ + continue; + } + cptr = get_glyph (cptr, gbuf, 0); + if ((t = get_switches (gbuf)) != 0) { /* try for switches */ + if (t < 0) { /* err if bad switch */ + *st = SCPE_INVSW; + return NULL; + } + sim_switches = sim_switches | t; /* or in new switches */ + } + else if ((opt & CMD_OPT_SCH) && /* if allowed, */ + get_search (gbuf, sim_dfdev->dradix, &sim_stab)) { /* try for search */ + sim_schptr = &sim_stab; /* set search */ + sim_opt_out |= CMD_OPT_SCH; /* got search */ + } + else if ((opt & CMD_OPT_DFT) && /* default allowed? */ + ((sim_opt_out & CMD_OPT_DFT) == 0) && /* none yet? */ + (tdptr = find_unit (gbuf, &tuptr)) && /* try for default */ + (tuptr != NULL)) { + sim_dfdev = tdptr; /* set as default */ + sim_dfunit = tuptr; + sim_opt_out |= CMD_OPT_DFT; /* got default */ + } + else return svptr; /* not rec, break out */ + } +return cptr; +} + +/* Match file extension + + Inputs: + fnam = file name + ext = extension, without period + Outputs: + cp = pointer to final '.' if match, NULL if not +*/ + +char *match_ext (char *fnam, char *ext) +{ +char *pptr, *fptr, *eptr; + +if ((fnam == NULL) || (ext == NULL)) /* bad arguments? */ + return NULL; +pptr = strrchr (fnam, '.'); /* find last . */ +if (pptr) { /* any? */ + for (fptr = pptr + 1, eptr = ext; /* match characters */ +#if defined (VMS) /* VMS: stop at ; or null */ + (*fptr != 0) && (*fptr != ';'); +#else + *fptr != 0; /* others: stop at null */ +#endif + fptr++, eptr++) { + if (toupper (*fptr) != toupper (*eptr)) + return NULL; + } + if (*eptr != 0) /* ext exhausted? */ + return NULL; + } +return pptr; +} + +/* Get search specification + + Inputs: + cptr = pointer to input string + radix = radix for numbers + schptr = pointer to search table + Outputs: + return = NULL if error + schptr if valid search specification +*/ + +SCHTAB *get_search (char *cptr, int32 radix, SCHTAB *schptr) +{ +int32 c; +int32 logop, cmpop; +t_value logval, cmpval; +char *sptr, *tptr; +const char logstr[] = "|&^", cmpstr[] = "=!><"; + +logval = cmpval = 0; +if (*cptr == 0) /* check for clause */ + return NULL; +for (logop = cmpop = -1; c = *cptr++; ) { /* loop thru clauses */ + if (sptr = (char*) strchr (logstr, c)) { /* check for mask */ + logop = (int) (sptr - logstr); + logval = strtotv (cptr, &tptr, radix); + if (cptr == tptr) + return NULL; + cptr = tptr; + } + else if (sptr = (char*) strchr (cmpstr, c)) { /* check for boolop */ + cmpop = (int) (sptr - cmpstr); + if (*cptr == '=') { + cmpop = cmpop + (int) strlen (cmpstr); + cptr++; + } + cmpval = strtotv (cptr, &tptr, radix); + if (cptr == tptr) + return NULL; + cptr = tptr; + } + else return NULL; + } /* end for */ +if (logop >= 0) { + schptr->logic = logop; + schptr->mask = logval; + } +if (cmpop >= 0) { + schptr->boolop = cmpop; + schptr->comp = cmpval; + } +return schptr; +} + +/* Test value against search specification + + Inputs: + val = value to test + schptr = pointer to search table + Outputs: + return = 1 if value passes search criteria, 0 if not +*/ + +int32 test_search (t_value val, SCHTAB *schptr) +{ +if (schptr == NULL) return 0; + +switch (schptr->logic) { /* case on logical */ + + case SCH_OR: + val = val | schptr->mask; + break; + + case SCH_AND: + val = val & schptr->mask; + break; + + case SCH_XOR: + val = val ^ schptr->mask; + break; + } + +switch (schptr->boolop) { /* case on comparison */ + + case SCH_E: case SCH_EE: + return (val == schptr->comp); + + case SCH_N: case SCH_NE: + return (val != schptr->comp); + + case SCH_G: + return (val > schptr->comp); + + case SCH_GE: + return (val >= schptr->comp); + + case SCH_L: + return (val < schptr->comp); + + case SCH_LE: + return (val <= schptr->comp); + } + +return 0; +} + +/* Radix independent input/output package + + strtotv - general radix input routine + + Inputs: + inptr = string to convert + endptr = pointer to first unconverted character + radix = radix for input + Outputs: + value = converted value + + On an error, the endptr will equal the inptr. +*/ + +t_value strtotv (char *inptr, char **endptr, uint32 radix) +{ +int32 nodigit; +t_value val; +uint32 c, digit; + +*endptr = inptr; /* assume fails */ +if ((radix < 2) || (radix > 36)) + return 0; +while (isspace (*inptr)) /* bypass white space */ + inptr++; +val = 0; +nodigit = 1; +for (c = *inptr; isalnum(c); c = *++inptr) { /* loop through char */ + if (islower (c)) + c = toupper (c); + if (isdigit (c)) /* digit? */ + digit = c - (uint32) '0'; + else if (radix <= 10) /* stop if not expected */ + break; + else digit = c + 10 - (uint32) 'A'; /* convert letter */ + if (digit >= radix) /* valid in radix? */ + return 0; + val = (val * radix) + digit; /* add to value */ + nodigit = 0; + } +if (nodigit) /* no digits? */ + return 0; +*endptr = inptr; /* result pointer */ +return val; +} + +/* fprint_val - general radix printing routine + + Inputs: + stream = stream designator + val = value to print + radix = radix to print + width = width to print + format = leading zeroes format + Outputs: + status = error status +*/ + +t_stat fprint_val (SMP_FILE *stream, t_value val, uint32 radix, + uint32 width, uint32 format) +{ +#define MAX_WIDTH ((int) (CHAR_BIT * sizeof (t_value))) +t_value owtest, wtest; +int32 d, digit, ndigits; +char dbuf[MAX_WIDTH + 1]; + +for (d = 0; d < MAX_WIDTH; d++) + dbuf[d] = (format == PV_RZRO)? '0': ' '; +dbuf[MAX_WIDTH] = 0; +d = MAX_WIDTH; +do { + d = d - 1; + digit = (int32) (val % radix); + val = val / radix; + dbuf[d] = (digit <= 9)? '0' + digit: 'A' + (digit - 10); + } while ((d > 0) && (val != 0)); + +if (format != PV_LEFT) { + wtest = owtest = radix; + ndigits = 1; + while ((wtest < width_mask[width]) && (wtest >= owtest)) { + owtest = wtest; + wtest = wtest * radix; + ndigits = ndigits + 1; + } + if ((MAX_WIDTH - ndigits) < d) + d = MAX_WIDTH - ndigits; + } +if (fputs (&dbuf[d], stream) == EOF) + return SCPE_IOERR; +return SCPE_OK; +} + +/* Event queue package + + sim_activate add entry to event queue + sim_cancel remove entry from event queue + sim_process_event process entries on event queue + sim_is_active see if entry is on event queue + sim_atime return absolute time for an entry + sim_gtime return global time + + Asynchronous events are set up by queueing a unit data structure + to the event queue with a timeout (in simulator units, relative + to the current time). Each simulator 'times' these events by + counting down interval counter sim_interval. When this reaches + zero the simulator calls sim_process_event to process the event + and to see if further events need to be processed, or sim_interval + reset to count the next one. + + The event queue is maintained in clock order; entry timeouts are + RELATIVE to the time in the previous entry. + + sim_process_event - process event + + Inputs: + none + Outputs: + reason = reason code returned by any event processor, + or 0 (SCPE_OK) if no exceptions +*/ + +t_stat sim_process_event (RUN_DECL) +{ + t_stat reason; + t_bool elevated = FALSE; + t_bool check_elevate = TRUE; + + if (weak_read(stop_cpus)) /* stop CPU? */ + return SCPE_STOP; + + if (cpu_unit->clock_queue == NULL) /* queue empty? */ + { + UPDATE_SIM_TIME (cpu_unit->noqueue_time); /* update sim time */ + sim_interval = cpu_unit->noqueue_time = NOQUEUE_WAIT; /* flag queue empty */ + return SCPE_OK; + } + + RUN_SCOPE_RSCX_ONLY; + + UPDATE_SIM_TIME (cpu_unit->clock_queue->time); /* update sim time */ + + do + { + clock_queue_entry* cqe = cpu_unit->clock_queue; /* get first */ + + if (unlikely(cqe->clk_cosched)) + { + /* + * change time on all cosched entries + */ + sim_reschedule_cosched(RUN_PASS, RescheduleCosched_RequeueOnProcessEvent); + if (sim_interval) + return SCPE_OK; + cqe = cpu_unit->clock_queue; + if (unlikely(cqe->clk_cosched)) + panic("Unexpected prematurely expired CLK COSCHED entry in the clock queue"); + } + + UNIT *uptr = cqe->uptr; + + cpu_unit->clock_queue = cqe->next; /* remove from active list */ + + cqe->next = cpu_unit->clock_queue_freelist; /* place on freelist */ + cpu_unit->clock_queue_freelist = cqe; + + if (cpu_unit->clock_queue != NULL) + sim_interval = cpu_unit->clock_queue->time; + else + sim_interval = cpu_unit->noqueue_time = NOQUEUE_WAIT; + + /* + * We are about to call device handler which is likely to acquire device lock + * as a part of event processing. + * + * We elevate thread priority to avoid acquiring device lock at low thread priority + * and thus to reduce probability of lock holder preemption. + * + * Exception is made for per-CPU devices since they normally do not acquire shared locks. + * However some per-CPU devices (for VAX MP, TTI and TTO) do acquire shared locks, + * and for these devices thread priority must be elevated too. It would have been possible + * for them to raise and lower thread priority internally, however we try to minimize + * overall number of thread priority changes to at most one pair per loop. + */ + if (!elevated && check_elevate) + { + if (unlikely(!is_os_running(RUN_PASS)) || + cpu_unit->cpu_thread_priority != SIMH_THREAD_PRIORITY_INVALID && + cpu_unit->cpu_thread_priority >= SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI) + { + check_elevate = FALSE; + } + else + { + t_bool device_match = !IS_PERCPU_UNIT(uptr); +#if defined(VM_VAX_MP) + if (! device_match) + { + extern DEVICE tti_dev, tto_dev; + if (uptr->device == &tti_dev || uptr->device == &tto_dev) + device_match = TRUE; + } +#endif + if (device_match && rscx->os_hi_critical_locks == 0) + { + rscx->os_hi_critical_locks++; + cpu_reevaluate_thread_priority(RUN_PASS); + elevated = TRUE; + } + } + } + + if (uptr->action != NULL) + reason = uptr->action (RUN_PASS, uptr); + else + reason = SCPE_OK; + } + while (reason == SCPE_OK && sim_interval == 0); + + /* + * Reset thread priority. Note that we cannot simply restore priority to that at the + * entrance to sim_process_event, since priority might have been purposefully altered + * by events like: + * + * - event handler changing priority, e.g. in case of VAX SSC clock expiration + * - event handler sending interrupt to this processor + * - interrupt was sent to this processor by external source + * + * In addition, event handler may temporary change priority inside itself by acquiring + * and releasing locks with non-null criticality level. + * + */ + if (elevated && --rscx->os_hi_critical_locks == 0) + cpu_reevaluate_thread_priority(RUN_PASS); + + return reason; +} + +/* + * sim_activate - activate (queue) event + * + * If device is system-wide (not per-CPU), caller should hold device lock. + * + * Inputs: + * uptr = pointer to unit + * event_time = relative timeout (only valid if nticks == 0) + * nticks = number of clock ticks for cosched activation (>=1), 1 = next tick + * Outputs: + * reason = result (SCPE_OK if ok) + */ + +#define CLK_COSCHED_DUMMY_TIME (INT32_MAX / 4) + +t_stat sim_activate (UNIT *uptr, int32 event_time, int32 nticks) +{ + RUN_SCOPE; + + if (event_time < 0) + return SCPE_IERR; + + if (nticks) + event_time = CLK_COSCHED_DUMMY_TIME; + + if (uptr == &clk_unit && use_clock_thread) + { + cpu_unit->clk_active = TRUE; + return SCPE_OK; + } + + clock_queue_entry* cqe; + clock_queue_entry* prev; + int32 accum; + + if (sim_is_active (uptr)) /* already active? */ + { + return SCPE_OK; + } + + UPDATE_CPU_SIM_TIME(); /* update sim time */ + + prev = NULL; + accum = 0; + for (cqe = cpu_unit->clock_queue; cqe != NULL; cqe = cqe->next) + { + if (event_time < accum + cqe->time) + break; + accum = accum + cqe->time; + prev = cqe; + } + + /* allocate entry off free list and fill it */ + cqe = cpu_unit->clock_queue_freelist; + if (cqe == NULL) + { + panic("Unable to allocate clock queue entry"); + } + cpu_unit->clock_queue_freelist = cqe->next; + cqe->time = event_time - accum; + cqe->uptr = uptr; + cqe->clk_cosched = nticks; + + /* insert it */ + if (prev == NULL) /* insert at head */ + { + cqe->next = cpu_unit->clock_queue; + cpu_unit->clock_queue = cqe; + } + else + { + cqe->next = prev->next; /* insert at prev */ + prev->next = cqe; + } + + if (cqe->next != NULL) + cqe->next->time -= cqe->time; + + sim_interval = cpu_unit->clock_queue->time; + + if (! IS_PERCPU_UNIT(uptr)) + uptr->clock_queue_cpu = cpu_unit; + + return SCPE_OK; +} + +/* + * sim_activate_abs - activate (queue) event even if event already scheduled + * + * If device is system-wide (not per-CPU), caller should hold device lock. + * + * Inputs: + * uptr = pointer to unit + * event_time = relative timeout + * Outputs: + * reason = result (SCPE_OK if ok) + */ + +t_stat sim_activate_abs (UNIT *uptr, int32 event_time) +{ + sim_cancel (uptr); + return sim_activate (uptr, event_time); +} + +t_stat sim_activate_clk_cosched (UNIT *uptr, int32 nticks) +{ + if (use_clock_thread) + { + /* + * Mark it as "clk_cosched" and time-mark for remote future. + * Once SYNCLK event is received (nticks-th time), it will reschedule all "clk_cosched" entries for immediate execution. + */ + return sim_activate (uptr, 0, nticks); + } + else + { + int32 t = sim_is_active (&clk_unit); + if (t) + t = t - 1 + (nticks - 1) * weak_read_var(tmr_poll); + else + t = nticks * weak_read_var(tmr_poll); + return sim_activate (uptr, t); + } +} + +t_stat sim_activate_clk_cosched_abs (UNIT *uptr, int32 nticks) +{ + sim_cancel (uptr); + return sim_activate_clk_cosched (uptr, nticks); +} + +/* + * sim_cancel - cancel (dequeue) event + * + * If device is system-wide (not per-CPU), caller should hold device lock. + * + * Inputs: + * uptr = pointer to unit + * Outputs: + * reason = result (SCPE_OK if ok) + */ + +t_stat sim_cancel (UNIT *uptr) +{ + RUN_SCOPE; + + if (uptr == &clk_unit && use_clock_thread) + { + if (cpu_unit->clk_active) + { + cpu_unit->clk_active = FALSE; + /* + * convert all pending co-sched entries to regular ones with scheduled execution time + * at estimated next clock tick + */ + sim_reschedule_cosched(RUN_PASS, RescheduleCosched_OnCancelClock); + } + return SCPE_OK; + } + + if (IS_PERCPU_UNIT(uptr)) + { + /* + * Per-CPU unit, queue entry is always kept on local processor, + * will dequeue the entry below. + */ + } + else if (uptr->clock_queue_cpu == cpu_unit) + { + /* + * System-wide unit queued on local processor. + * Mark unit not queued, then dequeue clock queue entry below. + */ + uptr->clock_queue_cpu = NULL; + } + else + { + /* + * System-wide unit queued on remote processor. + * Mark entry not queued. + */ + uptr->clock_queue_cpu = NULL; + return SCPE_OK; + } + + if (cpu_unit->clock_queue == NULL) + { + return SCPE_OK; + } + + UPDATE_SIM_TIME (cpu_unit->clock_queue->time); /* update sim time */ + + clock_queue_entry* cqe = NULL; + clock_queue_entry* prev = NULL; + + for (cqe = cpu_unit->clock_queue; cqe != NULL; cqe = cqe->next) + { + if (cqe->uptr == uptr) + { + if (prev == NULL) + { + cpu_unit->clock_queue = cqe->next; + } + else + { + prev->next = cqe->next; + } + break; + } + prev = cqe; + } + + if (cqe != NULL) + { + if (cqe->next != NULL) + cqe->next->time += cqe->time; + + /* release queue entry */ + cqe->next = cpu_unit->clock_queue_freelist; + cpu_unit->clock_queue_freelist = cqe; + + if (cpu_unit->clock_queue != NULL) + sim_interval = cpu_unit->clock_queue->time; + else + sim_interval = cpu_unit->noqueue_time = NOQUEUE_WAIT; + } + + return SCPE_OK; +} + +/* + * sim_is_active - test for entry in queue, return activation time + * + * If device is system-wide (not per-CPU), caller should hold device lock. + * + * Inputs: + * uptr = pointer to unit + * Outputs: + * result = 0 if inactive + * otherwise if active: + * for per-CPU devices: absolute activation time + 1 + * for system-wide (non per-CPU) units queued on the local CPU: absolute activation time + 1 + * for system-wide (non per-CPU) units queued on the remote CPU: 1 + * + * Note that for system-wide devices queued on remote CPU this function will return 1, + * not activation time value. + */ + +int32 sim_is_active (UNIT *uptr) +{ + RUN_SCOPE; + + if (uptr == &clk_unit && use_clock_thread) + { + if (cpu_unit->clk_active) + return synclk_expected_next(RUN_PASS) + 1; + else + return 0; + } + + if (! IS_PERCPU_UNIT(uptr)) + { + if (uptr->clock_queue_cpu == NULL) + return 0; + if (uptr->clock_queue_cpu != cpu_unit) + return 1; + } + + int32 accum = 0; + + for (clock_queue_entry *cqe = cpu_unit->clock_queue; cqe != NULL; cqe = cqe->next) + { + if (cqe == cpu_unit->clock_queue) + { + if (sim_interval > 0) + accum += sim_interval; + } + else + { + accum += cqe->time; + } + + if (cqe->uptr == uptr) + { + if (cqe->clk_cosched) + { + /* implies use_clock_thread, provide just an estimate / boolean flag */ + return synclk_expected_next(RUN_PASS) + (cqe->clk_cosched - 1) * weak_read_var(tmr_poll) + 1; + } + else + { + return accum + 1; + } + } + } + + return 0; +} + +/* + * Take all CLK cosched entries out of the queue and requque them back according to 'how'. + * Updates sim_interval. + */ +void sim_reschedule_cosched(RUN_DECL, RescheduleCoschedHow how) +{ + if (cpu_unit->clock_queue == NULL) + return; + + UPDATE_SIM_TIME (cpu_unit->clock_queue->time); + + clock_queue_entry* cosched_list = NULL; + clock_queue_entry* unlinked_list = NULL; + clock_queue_entry* cosched_later_list = NULL; + clock_queue_entry* unlinked_later_list = NULL; + clock_queue_entry* cqe; + clock_queue_entry* xqe; + clock_queue_entry* prev = NULL; + + /* + * move all clk_cosched entries from CPU clock_queue to unlinked_list/unlinked_later_list + */ + for (cqe = cpu_unit->clock_queue; cqe; ) + { + if (cqe->clk_cosched) + { + xqe = cqe; + + if (cqe->next) + cqe->next->time += cqe->time; + + if (prev == NULL) + cpu_unit->clock_queue = cqe->next; + else + prev->next = cqe->next; + + cqe = cqe->next; + + if (how == RescheduleCosched_OnSynClk && xqe->clk_cosched > 1) + { + xqe->next = unlinked_later_list; + unlinked_later_list = xqe; + } + else + { + xqe->next = unlinked_list; + unlinked_list = xqe; + } + } + else + { + prev = cqe; + cqe = cqe->next; + } + } + + switch (how) + { + case RescheduleCosched_RequeueOnProcessEvent: + if (unlinked_list) + { + /* move CLK-coscheduled entries towards the tail of the queue */ + cosched_list = reverse_cqe_list(unlinked_list); + setup_cotimed_cqe_list(cosched_list, -1); + insert_cotimed_cqe_list(RUN_PASS, cosched_list, CLK_COSCHED_DUMMY_TIME); + } + break; + + case RescheduleCosched_OnCancelClock: + if (unlinked_list) + { + /* convert CLK-coscheduled entries to regular entries scheduled at estimated clock expiration time */ + cosched_list = reverse_cqe_list(unlinked_list); + int32 till_next_tick = synclk_expected_next(RUN_PASS); + int32 tick_length = weak_read_var(tmr_poll); + while (cqe = cosched_list) + { + cosched_list = cqe->next; + cqe->next = NULL; + int32 newtime = till_next_tick + tick_length * (cqe->clk_cosched - 1); + cqe->clk_cosched = 0; + insert_cotimed_cqe_list(RUN_PASS, cqe, newtime); + } + } + break; + + case RescheduleCosched_OnSynClk: + if (unlinked_list) + { + /* convert entries with (clk_cosched == 1) to regular entries scheduled for immediate processing */ + cosched_list = reverse_cqe_list(unlinked_list); + setup_cotimed_cqe_list(cosched_list, 0); + insert_cotimed_cqe_list(RUN_PASS, cosched_list, 0); + } + if (unlinked_later_list) + { + /* for entries with (clk_cosched > 1) decerement clk_cosched and requeue them far end of the queue */ + cosched_later_list = reverse_cqe_list(unlinked_later_list); + for (cqe = cosched_later_list; cqe; cqe = cqe->next) + { + cqe->clk_cosched--; + cqe->time = 0; + } + insert_cotimed_cqe_list(RUN_PASS, cosched_later_list, CLK_COSCHED_DUMMY_TIME); + } + break; + } + + sim_interval = cpu_unit->clock_queue->time; +} + +static clock_queue_entry* reverse_cqe_list(clock_queue_entry* list) +{ + clock_queue_entry* result = NULL; + clock_queue_entry* cqe; + + while ((cqe = list) != NULL) + { + list = cqe->next; + cqe->next = result; + result = cqe; + } + + return result; +} + +static void setup_cotimed_cqe_list(clock_queue_entry* list, int32 nticks) +{ + for (clock_queue_entry* cqe = list; cqe; cqe = cqe->next) + { + if (nticks >= 0) + cqe->clk_cosched = nticks; + cqe->time = 0; + } +} + +static void insert_cotimed_cqe_list(RUN_DECL, clock_queue_entry* list, int32 newtime) +{ + clock_queue_entry* prev = NULL; + clock_queue_entry* last; + clock_queue_entry* cqe; + + if (list == NULL) + return; + + for (cqe = list; cqe; cqe = cqe->next) + last = cqe; + + /* insert entries at newtime */ + + int32 accum = 0; + + for (cqe = cpu_unit->clock_queue; cqe != NULL; cqe = cqe->next) + { + if (newtime < accum + cqe->time) + break; + accum = accum + cqe->time; + prev = cqe; + } + + if (prev == NULL) + { + /* insert at head */ + last->next = cpu_unit->clock_queue; + cpu_unit->clock_queue = list; + cpu_unit->clock_queue->time = newtime; + + if (last->next) + last->next->time -= newtime; + } + else + { + /* insert at prev */ + last->next = prev->next; + prev->next = list; + list->time = newtime - accum; + if (last->next) + last->next->time -= list->time; + } +} + +/* + * Drop entries off the front of clock queue that had been migrated to another processor. + * Updates sim_interval. + */ +void sim_flush_migrated_clock_queue_entries(RUN_DECL) +{ + for (;;) + { + /* + * check if front element had been migrated + */ + + clock_queue_entry* cqe = cpu_unit->clock_queue; + if (cqe == NULL) + break; + UNIT* uptr = cqe->uptr; + if (IS_PERCPU_UNIT(uptr) || uptr->clock_queue_cpu == cpu_unit) + break; + + /* + * remove front element + */ + + /* update sim time */ + UPDATE_SIM_TIME (cqe->time); + + cpu_unit->clock_queue = cqe->next; + + if (cqe->next != NULL) + cqe->next->time += cqe->time; + + /* update remaining interval count */ + if (cpu_unit->clock_queue != NULL) + sim_interval = cpu_unit->clock_queue->time; + else + sim_interval = cpu_unit->noqueue_time = NOQUEUE_WAIT; + + /* release queue entry */ + cqe->next = cpu_unit->clock_queue_freelist; + cpu_unit->clock_queue_freelist = cqe; + } +} + +/* + * Called when SYNCLK is received. + * + * Calculate interval of protection for device activity during which a subsequent SYNCLK + * will not be processed immediately if received, but instead will be postponed till after + * the end of interval. + */ +int32 sim_calculate_device_activity_protection_interval(RUN_DECL) +{ + int32 accum = 0; + int32 res = 0; + + for (clock_queue_entry *cqe = cpu_unit->clock_queue; cqe != NULL; cqe = cqe->next) + { + if (cqe == cpu_unit->clock_queue) + { + if (sim_interval > 0) + accum += sim_interval; + } + else + { + accum += cqe->time; + } + + if ((uint32) accum > synclk_safe_cycles) + break; + + if (cqe->clk_cosched || cqe->uptr == cpu_unit || cqe->uptr == &sim_throt_unit) + continue; + + res = accum; + } + + return res; +} + +/* + * Check if current CPU has pending events for system-wide (non-percpu) devices + */ +t_bool sim_cpu_has_syswide_events(RUN_DECL) +{ + for (clock_queue_entry *cqe = cpu_unit->clock_queue; cqe != NULL; cqe = cqe->next) + { + UNIT* uptr = cqe->uptr; + if (!IS_PERCPU_UNIT(uptr) && uptr->clock_queue_cpu == cpu_unit) + return TRUE; + } + return FALSE; +} + +/* + * Called on the primary processor after a secondary had been shut down. + * Requeue any pending events for system-wide devices from halted secondary VCPU's event queue + * (that is no longer processed) by moving them to the primary's queue. + */ +void sim_requeue_syswide_events(RUN_DECL) +{ + int nentries; + t_bool locked = FALSE; + + if (!cpu_unit->is_primary_cpu()) + panic("sim_requeue_syswide_events called on secondary CPU"); + + /* scan all secondary CPUs and handle those who requested requeueing service */ + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + /* (re)acquire cpu db lock and process next CPU */ + if (! locked) + { + cpu_database_lock->lock(); + locked = TRUE; + } + + if (cpu_running_set.is_set(cpu_ix)) + continue; + CPU_UNIT* xcpu = cpu_units[cpu_ix]; + if (xcpu == cpu_unit || !xcpu->cpu_requeue_syswide_pending || xcpu->cpu_state != CPU_STATE_STANDBY) + continue; + + /* copy matching clock event queue entries info to temporary buffer */ + nentries = 0; + int32 qtime = cpu_sim_interval(xcpu); + for (clock_queue_entry *cqe = xcpu->clock_queue; cqe != NULL; cqe = cqe->next) + { + UNIT* uptr = cqe->uptr; + if (cqe != xcpu->clock_queue) + qtime += cqe->time; + if (! IS_PERCPU_UNIT(uptr)) + { + sim_requeue_info[nentries].uptr = cqe->uptr; + sim_requeue_info[nentries].time = qtime; + sim_requeue_info[nentries].clk_cosched = cqe->clk_cosched; + nentries++; + } + } + xcpu->cpu_requeue_syswide_pending = FALSE; + if (nentries == 0) continue; + + /* temporarily release cpu db lock */ + cpu_database_lock->unlock(); + locked = FALSE; + + /* process entries */ + for (int k = 0; k < nentries; k++) + { + clock_queue_entry_info* cq = sim_requeue_info + k; + UNIT* uptr = cq->uptr; + if (uptr->lock) uptr->lock->lock(); + if (uptr->clock_queue_cpu == xcpu) + { + if (cq->clk_cosched) + sim_activate_clk_cosched (cq->uptr, cq->clk_cosched); + else + sim_activate (cq->uptr, cq->time); + } + if (uptr->lock) uptr->lock->unlock(); + } + } + + if (locked) + cpu_database_lock->unlock(); +} + +/* sim_gtime - return global time + sim_grtime - return global time with rollover + + Inputs: none + Outputs: + time = global time +*/ + +double sim_gtime (RUN_DECL) +{ + UPDATE_CPU_SIM_TIME(); + return cpu_unit->sim_time; +} + +uint32 sim_grtime (RUN_DECL) +{ + UPDATE_CPU_SIM_TIME(); + return cpu_unit->sim_rtime; +} + +/* + * Bind specified lock for all units of the device + */ +void sim_bind_devunits_lock(DEVICE* dptr, smp_lock* lock) +{ + for (uint32 k = 0; k < dptr->numunits; k++) + dptr->units[k]->lock = lock; +} + +/* Breakpoint package. This module replaces the VM-implemented one + instruction breakpoint capability. + + Breakpoints are stored in table sim_brk_tab, which is ordered by address for + efficient binary searching. A breakpoint consists of a four entry structure: + + addr address of the breakpoint + type types of breakpoints set on the address + a bit mask representing letters A-Z + cnt number of iterations before breakp is taken + action pointer command string to be executed + when break is taken + + sim_brk_summ is a summary of the types of breakpoints that are currently set (it + is the bitwise OR of all the type fields). A simulator need only check for + a breakpoint of type X if bit SWMASK('X') is set in sim_brk_sum. + + The package contains the following public routines: + + sim_brk_init initialize + sim_brk_set set breakpoint + sim_brk_clr clear breakpoint + sim_brk_clrall clear all breakpoints + sim_brk_show show breakpoint + sim_brk_showall show all breakpoints + sim_brk_test test for breakpoint + sim_brk_npc PC has been changed + sim_brk_clract clear pending actions in CPUs + + Initialize breakpoint system. +*/ + +t_stat sim_brk_init (void) +{ + RUN_SCOPE; + sim_brk_lnt = SIM_BRK_INILNT; + sim_brk_tab = (BRKTAB *) calloc (sim_brk_lnt, sizeof (BRKTAB)); + if (sim_brk_tab == NULL) + return SCPE_MEM; + sim_brk_ent = sim_brk_ins = 0; + // sim_brk_act = NULL; + sim_brk_npc (RUN_PASS, 0); + return SCPE_OK; +} + +/* Search for a breakpoint in the sorted breakpoint table */ + +BRKTAB *sim_brk_fnd (t_addr loc) +{ + int32 lo, hi, p; + BRKTAB *bp; + + if (sim_brk_ent == 0) /* table empty? */ + { + sim_brk_ins = 0; /* insrt at head */ + return NULL; /* sch fails */ + } + lo = 0; /* initial bounds */ + hi = sim_brk_ent - 1; + + do + { + p = (lo + hi) >> 1; /* probe */ + bp = sim_brk_tab + p; /* table addr */ + if (loc == bp->addr) /* match? */ + return bp; + else if (loc < bp->addr) /* go down? p is upper */ + hi = p - 1; + else + lo = p + 1; /* go up? p is lower */ + } + while (lo <= hi); + + if (loc < bp->addr) /* insrt before or */ + sim_brk_ins = p; + else + sim_brk_ins = p + 1; /* after last sch */ + return NULL; +} + +/* Insert a breakpoint */ + +BRKTAB *sim_brk_new (t_addr loc) +{ + int32 i, t; + BRKTAB *bp, *newp; + + if (sim_brk_ins < 0) + return NULL; + if (sim_brk_ent >= sim_brk_lnt) /* out of space? */ + { + t = sim_brk_lnt + SIM_BRK_INILNT; /* new size */ + newp = (BRKTAB *) calloc (t, sizeof (BRKTAB)); /* new table */ + if (newp == NULL) /* can't extend */ + return NULL; + for (i = 0; i < sim_brk_lnt; i++) /* copy table */ + *(newp + i) = *(sim_brk_tab + i); + free (sim_brk_tab); /* free old table */ + sim_brk_tab = newp; /* new base, lnt */ + sim_brk_lnt = t; + } + if (sim_brk_ins != sim_brk_ent) /* move needed? */ + { + for (bp = sim_brk_tab + sim_brk_ent; + bp > sim_brk_tab + sim_brk_ins; bp--) + *bp = *(bp - 1); + } + bp = sim_brk_tab + sim_brk_ins; + bp->addr = loc; + bp->typ = 0; + bp->cnt = 0; + bp->act = NULL; + sim_brk_ent = sim_brk_ent + 1; + return bp; +} + +/* Set a breakpoint of type sw */ + +t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, char *act) +{ + BRKTAB *bp; + + if (sw == 0) sw = sim_brk_dflt; + if ((sim_brk_types & sw) == 0) + return SCPE_NOFNC; + bp = sim_brk_fnd (loc); /* present? */ + if (!bp) /* no, allocate */ + bp = sim_brk_new (loc); + if (!bp) /* still no? mem err */ + return SCPE_MEM; + bp->typ = sw; /* set type */ + bp->cnt = ncnt; /* set count */ + if (bp->act != NULL && act != NULL) /* replace old action? */ + { + free (bp->act); /* deallocate */ + bp->act = NULL; /* now no action */ + } + if (act != NULL && *act != 0) /* new action? */ + { + char *newp = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc buf */ + if (newp == NULL) /* mem err? */ + return SCPE_MEM; + strncpy (newp, act, CBUFSIZE); /* copy action */ + bp->act = newp; /* set pointer */ + } + sim_brk_summ = sim_brk_summ | sw; + return SCPE_OK; +} + +/* Clear a breakpoint */ + +t_stat sim_brk_clr (t_addr loc, int32 sw) +{ + BRKTAB *bp = sim_brk_fnd (loc); + + if (!bp) /* not there? ok */ + return SCPE_OK; + if (sw == 0) + sw = SIM_BRK_ALLTYP; + bp->typ = bp->typ & ~sw; + if (bp->typ) /* clear all types? */ + return SCPE_OK; + if (bp->act != NULL) /* deallocate action */ + free (bp->act); + for ( ; bp < (sim_brk_tab + sim_brk_ent - 1); bp++) /* erase entry */ + *bp = *(bp + 1); + sim_brk_ent = sim_brk_ent - 1; /* decrement count */ + sim_brk_summ = 0; /* recalc summary */ + for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); bp++) + sim_brk_summ = sim_brk_summ | bp->typ; + return SCPE_OK; +} + +/* Clear all breakpoints */ + +t_stat sim_brk_clrall (int32 sw) +{ + BRKTAB *bp; + + if (sw == 0) sw = SIM_BRK_ALLTYP; + for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); ) + { + if (bp->typ & sw) + sim_brk_clr (bp->addr, sw); + else bp++; + } + return SCPE_OK; +} + +/* Show a breakpoint */ + +t_stat sim_brk_show (SMP_FILE *st, t_addr loc, int32 sw) +{ + BRKTAB *bp = sim_brk_fnd (loc); + DEVICE *dptr; + int32 i, any; + + if (sw == 0) + sw = SIM_BRK_ALLTYP; + if (!bp || (!(bp->typ & sw))) + return SCPE_OK; + dptr = sim_dflt_dev; + if (dptr == NULL) + return SCPE_OK; + if (sim_vm_fprint_addr) + sim_vm_fprint_addr (st, dptr, loc); + else fprint_val (st, loc, dptr->aradix, dptr->awidth, PV_LEFT); + fprintf (st, ":\t"); + for (i = any = 0; i < 26; i++) { + if ((bp->typ >> i) & 1) { + if (any) + fprintf (st, ", "); + fputc (i + 'A', st); + any = 1; + } + } + if (bp->cnt > 0) + fprintf (st, " [%d]", bp->cnt); + if (bp->act != NULL) + fprintf (st, "; %s", bp->act); + fprintf (st, "\n"); + return SCPE_OK; +} + +/* Show all breakpoints */ + +t_stat sim_brk_showall (SMP_FILE *st, int32 sw) +{ + BRKTAB *bp; + + if (sw == 0) + sw = SIM_BRK_ALLTYP; + for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); bp++) + { + if (bp->typ & sw) + sim_brk_show (st, bp->addr, sw); + } + return SCPE_OK; +} + +/* Test for breakpoint */ + +uint32 sim_brk_test (RUN_DECL, t_addr loc, uint32 btyp) +{ + uint32 spc = (btyp >> SIM_BKPT_V_SPC) & (SIM_BKPT_N_SPC - 1); + BRKTAB* bp = sim_brk_fnd (loc); + + if (bp && (btyp & bp->typ)) /* in table, type match? */ + { + /* previous location? */ + if (cpu_unit->sim_brk_pend[spc] && (loc == cpu_unit->sim_brk_ploc[spc])) + { + return 0; + } + if (--bp->cnt > 0) /* count > 0? */ + { + return 0; + } + bp->cnt = 0; /* reset count */ + cpu_unit->sim_brk_ploc[spc] = loc; /* save location */ + cpu_unit->sim_brk_pend[spc] = TRUE; /* don't do twice */ + cpu_unit->sim_brk_act = bp->act; /* set up actions */ + return (btyp & bp->typ); + } + cpu_unit->sim_brk_pend[spc] = FALSE; + return 0; +} + +static t_bool sim_brk_is_action_pending () +{ + for (uint32 k = 0; k < sim_ncpus; k++) + { + if (cpu_units[k]->sim_brk_act) + return TRUE; + } + return FALSE; +} + +t_bool sim_brk_is_in_action () +{ + return sim_brk_action_stack.depth() != 0; +} + +/* Get next pending action, if any */ + +sim_cstream* sim_brk_get_action_script () +{ + if (sim_brk_is_action_pending ()) + { + if (sim_brk_action_stack.depth() >= 8) + { + smp_printf ("Breakpoint action script exceeded safety depth limit, ignoring breakpoint action commands\n"); + if (sim_log) + fprintf (sim_log, "Breakpoint action script exceeded safety depth limit, ignoring breakpoint action commands\n"); + sim_brk_continue = FALSE; + return NULL; + } + + /* + * build script of the structure: + * + * CPU ID n1 + * commands from action for n1 + * .... + * CPU ID nx + * commands from action for nx + * CPU ID original + * + */ + sim_cstream* sim_try_volatile script = NULL; + char* sim_try_volatile xact = NULL; + sim_try + { + t_bool restore_cpu = FALSE; + script = new sim_cstream(); + char cmd[100]; + for (uint32 k = 0; k < sim_ncpus; k++) + { + CPU_UNIT* xcpu = cpu_units[k]; + if (xcpu->sim_brk_act) + { + t_bool did_setcpu = FALSE; + /* accumulate action commands */ + if (xact) free(xact); + xact = dupstr_exc(xcpu->sim_brk_act); + char* cp = strtok(xact, ";"); + while (cp) + { + while (isspace(*cp)) cp++; + if (*cp) + { + if (!did_setcpu && sim_ncpus > 1) + { + sprintf(cmd, "CPU ID %d\n", xcpu->cpu_id); + script->append(cmd); + did_setcpu = TRUE; + restore_cpu = TRUE; + } + script->append(cp); + script->append('\n'); + } + cp = strtok(NULL, ";"); + } + } + } + + if (restore_cpu) + { + sprintf(cmd, "CPU ID %d\n", sim_dflt_cpu->cpu_id); + script->append(cmd); + } + + sim_brk_action_stack.push((sim_cstream_ptr_t) script); + sim_brk_clract (); + if (xact) + { + free(xact); + xact = NULL; + } + } + sim_catch (sim_exception_SimError, exc) + { + fprintf (smp_stderr, "Error while processing breakpoint actions: %s\n", exc->get_message()); + exc->checkAutoDelete(); + delete script; + if (xact) free(xact); + sim_brk_continue = FALSE; + return NULL; + } + sim_end_try + } + + if (sim_brk_action_stack.depth() == 0) + return NULL; + + return sim_brk_action_stack.peek(); +} + +const char* sim_brk_end_action_script (sim_cstream* script) +{ + if (sim_brk_action_stack.depth() != 0 && + sim_brk_action_stack.peek() == script) + { + sim_brk_action_stack.pop(); + } + else + { + panic("Internal consistency check failure: unmatched breakpoint script stack"); + } + + if (sim_brk_action_stack.depth() == 0 && sim_brk_continue) + { + sim_brk_continue = FALSE; + return "cont"; + } + + return NULL; +} + +/* Clear pending actions */ + +void sim_brk_clract (void) +{ + for (uint32 k = 0; k < sim_ncpus; k++) + { + CPU_UNIT* cpu_unit = cpu_units[k]; + cpu_unit->sim_brk_act = NULL; + } +} + +/* New PC */ + +void sim_brk_npc (RUN_DECL, uint32 cnt) +{ + uint32 i; + + if (cnt == 0 || cnt > SIM_BKPT_N_SPC) + cnt = SIM_BKPT_N_SPC; + + for (i = 0; i < cnt; i++) + { + cpu_unit->sim_brk_pend[i] = FALSE; + cpu_unit->sim_brk_ploc[i] = 0; + } +} + +#if 0 +/* Clear breakpoint space (unused routine) */ +void sim_brk_clrspc (RUN_DECL, uint32 spc) +{ + if (spc < SIM_BKPT_N_SPC) + { + cpu_unit->sim_brk_pend[spc] = FALSE; + cpu_unit->sim_brk_ploc[spc] = 0; + } +} +#endif + +volatile t_bool sim_dbg_wait = FALSE; +static void dbg_wait_int_handler(int sig); + +static void sim_wait_debugger(int* pargc, char* argv[]) +{ + t_bool cancel_sigint_handler = FALSE; + + for (int k = 0; k < *pargc; k++) + { + if (streqi(argv[k], "--dbg-wait")) + { + sim_dbg_wait = TRUE; + for (int j = k + 1; j < *pargc; j++) + argv[j - 1] = argv[j]; + k--; + (*pargc)--; + } + } + + if (sim_dbg_wait) + { + signal(SIGINT, dbg_wait_int_handler); + cancel_sigint_handler = TRUE; + fprintf(stderr, "Waiting for debugger to connect, press Ctrl/C to resume...\n"); + } + +#if defined(_WIN32) + while (sim_dbg_wait) + _sleep(250); +#else + while (sim_dbg_wait) + sleep(1); +#endif + + if (cancel_sigint_handler) + { + signal(SIGINT, SIG_DFL); + fprintf(stderr, "\nResuming...\n"); + } +} + +static void dbg_wait_int_handler(int sig) +{ + sim_dbg_wait = FALSE; +} + +/* Message Text */ + +AUTO_TLS(sim_error_text_msg_key); +const char *sim_error_text (t_stat stat) +{ + static char s_msgbuf[64]; + + stat &= ~(SCPE_KFLAG|SCPE_BREAK); /* remove any flags */ + if (stat == SCPE_OK) + return "No Error"; + if (stat >= SCPE_BASE && stat <= SCPE_MAX_ERR) + return scp_errors[stat - SCPE_BASE].message; + char* msgbuf = (char*) tls_get_value(sim_error_text_msg_key); + if (msgbuf == NULL) + { + msgbuf = (char*) malloc(sizeof(s_msgbuf)); + tls_set_value(sim_error_text_msg_key, msgbuf); + } + if (msgbuf == NULL) msgbuf = s_msgbuf; + sprintf(msgbuf, "Error %d", stat); + return msgbuf; +} + +t_stat sim_string_to_stat (char *cptr, t_stat *stat) +{ + char gbuf[CBUFSIZE]; + int32 cond; + + *stat = SCPE_ARG; + cptr = get_glyph (cptr, gbuf, 0); + if (0 == memcmp("SCPE_", gbuf, 5)) + strcpy (gbuf, gbuf + 5); /* skip leading SCPE_ */ + for (cond = 0; cond <= SCPE_MAX_ERR - SCPE_BASE; cond++) + { + if (0 == strcmp(scp_errors[cond].code, gbuf)) + { + cond += SCPE_BASE; + break; + } + } + if (cond > SCPE_MAX_ERR - SCPE_BASE) /* not found? */ + { + if (0 == (cond = strtol(gbuf, NULL, 0))) /* try explicit number */ + return SCPE_ARG; + } + if (cond > SCPE_MAX_ERR) + return SCPE_ARG; + *stat = cond; + return SCPE_OK; +} + +/* Debug printout routines, from Dave Hittner */ + +const char* debug_bstates = "01_^"; +const char* debug_fmt = "DBG> %s %s: "; +int32 debug_unterm = 0; + +/* Finds debug phrase matching bitmask from from device DEBTAB table */ + +static char* get_dbg_verb (uint32 dbits, DEVICE* dptr) +{ + static char* debtab_none = "DEBTAB_ISNULL"; + static char* debtab_nomatch = "DEBTAB_NOMATCH"; + int32 offset = 0; + + if (dptr->debflags == 0) + return debtab_none; + + /* Find matching words for bitmask */ + + while (dptr->debflags[offset].name && (offset < 32)) + { + if (dptr->debflags[offset].mask & dbits) + return dptr->debflags[offset].name; + offset++; + } + return debtab_nomatch; +} + +/* Prints standard debug prefix unless previous call unterminated */ + +static void sim_debug_prefix (uint32 dbits, DEVICE* dptr) +{ + if (!debug_unterm) + { + char* debug_type = get_dbg_verb (dbits, dptr); + fprintf(sim_deb, debug_fmt, dptr->name, debug_type); + } +} + +/* Prints state of a register: bit translation + state (0,1,_,^) + indicating the state and transition of the bit. States: + 0=steady(0->0), 1=steady(1->1), _=falling(1->0), ^=rising(0->1) */ + +void sim_debug_u16(uint32 dbits, DEVICE* dptr, const char* const* bitdefs, + uint16 before, uint16 after, int terminate) +{ + if (sim_deb && (dptr->dctrl & dbits)) + { + int32 i; + + sim_debug_prefix(dbits, dptr); /* print prefix if required */ + for (i = 15; i >= 0; i--) /* print xlation, transition */ + { + int off = ((after >> i) & 1) + (((before ^ after) >> i) & 1) * 2; + fprintf(sim_deb, "%s%c ", bitdefs[i], debug_bstates[off]); + } + if (terminate) + fprintf(sim_deb, "\r\n"); + debug_unterm = terminate ? 0 : 1; /* set unterm for next */ + } +} + +#if defined (_WIN32) +#define vsnprintf _vsnprintf +#endif +#if defined (__DECC) && defined (__VMS) && (defined (__VAX) || (__CRTL_VER <= 70311000)) +#define NO_vsnprintf +#endif +#if defined( NO_vsnprintf) +#define STACKBUFSIZE 16384 +#else +#define STACKBUFSIZE 2048 +#endif + +/* Inline debugging - will print debug message if debug file is + set and the bitmask matches the current device debug options. + Extra returns are added for un*x systems, since the output + device is set into 'raw' mode when the cpu is booted, + and the extra returns don't hurt any other systems. + + Callers should be calling sim_debug() which is a macro + defined in scp.h which evaluates the action condition before + incurring call overhead. */ + +void _sim_debug (uint32 dbits, DEVICE* dptr, const char* fmt, ...) +{ + if (sim_deb && (dptr->dctrl & dbits)) + { + char stackbuf[STACKBUFSIZE]; + int32 bufsize = sizeof(stackbuf); + char *buf = stackbuf; + va_list arglist; + int32 i, j, len; + + char* debug_type = get_dbg_verb (dbits, dptr); + buf[bufsize-1] = '\0'; + // sim_debug_prefix(dbits, dptr); /* print prefix if required */ + + while (1) /* format passed string, args */ + { + va_start (arglist, fmt); +#if defined(NO_vsnprintf) +#if defined(HAS_vsprintf_void) + + /* Note, this could blow beyond the buffer, and we couldn't tell */ + /* That is a limitation of the C runtime library available on this platform */ + + vsprintf (buf, fmt, arglist); + for (len = 0; len < bufsize-1; len++) + if (buf[len] == 0) break; +#else + len = vsprintf (buf, fmt, arglist); +#endif /* HAS_vsprintf_void */ +#else /* NO_vsnprintf */ +#if defined(HAS_vsnprintf_void) + vsnprintf (buf, bufsize-1, fmt, arglist); + for (len = 0; len < bufsize-1; len++) + if (buf[len] == 0) break; +#else + len = vsnprintf (buf, bufsize-1, fmt, arglist); +#endif /* HAS_vsnprintf_void */ +#endif /* NO_vsnprintf */ + va_end (arglist); + + /* If it didn't fit into the buffer, then grow it and try again */ + + if (len < 0 || len >= bufsize - 1) + { + if (buf != stackbuf) + free (buf); + bufsize = bufsize * 2; + buf = (char *) malloc (bufsize); + if (buf == NULL) /* out of memory */ + return; + buf[bufsize-1] = '\0'; + continue; + } + break; + } + + /* Output the formatted data expanding newlines where they exist */ + + for (i = j = 0; i < len; ++i) + { + if ('\n' == buf[i]) + { + if (i > j) + { + if (debug_unterm) + fprintf (sim_deb, "%.*s\r\n", i-j, &buf[j]); + else /* print prefix when required */ + fprintf (sim_deb, "DBG> %s %s: %.*s\r\n", dptr->name, debug_type, i-j, &buf[j]); + debug_unterm = 0; + } + j = i + 1; + } + } + if (i > j) + fwrite (&buf[j], 1, i-j, sim_deb); + + /* Set unterminated flag for next time */ + + debug_unterm = (len && (buf[len-1]=='\n')) ? 0 : 1; + if (buf != stackbuf) + free (buf); + } +} + +enum perf_object_kind +{ + PERF_OBJECT_NONE = 0, + PERF_OBJECT_SMP_LOCK = 1 +}; + +class perf_object +{ +public: + perf_object_kind kind; + const char* name; +private: + void* object; + t_bool copied_name; + +public: + perf_object() + { kind = PERF_OBJECT_NONE; name = NULL; copied_name = FALSE; object = NULL; } + void set(const char* name, t_bool copied_name, smp_lock* object) + { this->kind = PERF_OBJECT_SMP_LOCK; this->name = name; this->copied_name = copied_name; this->object = object; } + void unset() + { kind = PERF_OBJECT_NONE; if (name && copied_name) free((void*)name); name = NULL; copied_name = FALSE; object = NULL; } + smp_lock* get_smp_lock() + { return kind == PERF_OBJECT_SMP_LOCK ? (smp_lock*) object : NULL; } + void* get_object() + { return object; } +}; + +#define MAX_PERF_OBJECTS 200 +static perf_object perf_objects[MAX_PERF_OBJECTS]; +int perf_objects_count = 0; +t_bool perf_objects_overflow = FALSE; + +void perf_register_object(const char* name, smp_lock* object, t_bool copyname) +{ + if (copyname) + { + name = dupstr(name); + if (name == NULL) + { + if (smp_stderr == NULL) + fprintf(stderr, "%s: Warning: Not enough memory to register performance object\n", sim_name); + else + fprintf(smp_stderr, "%s: Warning: Not enough memory to register performance object\n", sim_name); + return; + } + } + + if (perf_objects_count < MAX_PERF_OBJECTS) + { + perf_objects[perf_objects_count++].set(name, copyname, object); + } + else if (! perf_objects_overflow) + { + if (smp_stderr == NULL) + fprintf(stderr, "%s: Warning: Performance objects list overflow\n", sim_name); + else + fprintf(smp_stderr, "%s: Warning: Performance objects list overflow\n", sim_name); + perf_objects_overflow = TRUE; + } +} + +perf_object* perf_find_object(const char* name) +{ + for (int k = 0; k < perf_objects_count; k++) + { + perf_object* po = perf_objects + k; + if (po->kind != PERF_OBJECT_NONE && po->name && streqi(po->name, name)) + return po; + } + return NULL; +} + +void perf_unregister_object(smp_lock* object) +{ + if (object == NULL) + return; + + for (int k = 0; k < perf_objects_count; k++) + { + perf_object* po = perf_objects + k; + if (po->kind != PERF_OBJECT_NONE && po->get_object() == object) + po->unset(); + } +} + +typedef enum __tag_perf_cmd_verb +{ + PERF_CMD_VERB_NONE = 0, + PERF_CMD_VERB_ON = 1, + PERF_CMD_VERB_OFF = 2, + PERF_CMD_VERB_RESET = 3, + PERF_CMD_VERB_SHOW = 4 +} +perf_cmd_verb; + +t_stat perf_cmd (int32 flag, char *cptr) +{ + char gbuf[CBUFSIZE]; + perf_cmd_verb verb = PERF_CMD_VERB_NONE; + perf_object* xpo = NULL; + + cptr = get_glyph (cptr, gbuf, 0); + + if (streqi(gbuf, "SHOW")) + verb = PERF_CMD_VERB_SHOW; + else if (streqi(gbuf, "ON")) + verb = PERF_CMD_VERB_ON; + else if (streqi(gbuf, "OFF")) + verb = PERF_CMD_VERB_OFF; + else if (streqi(gbuf, "RESET")) + verb = PERF_CMD_VERB_RESET; + else if (streqi(gbuf, "")) + verb = PERF_CMD_VERB_NONE; + else + return SCPE_ARG; + + if (verb == PERF_CMD_VERB_NONE) + { + verb = PERF_CMD_VERB_SHOW; + } + else + { + cptr = get_glyph (cptr, gbuf, 0); + if (gbuf[0]) + { + xpo = perf_find_object(gbuf); + if (xpo == NULL) return SCPE_ARG; + } + } + + for (int k = 0; k < perf_objects_count; k++) + { + perf_object* po = perf_objects + k; + + if (po->kind == PERF_OBJECT_NONE || po->name == NULL) + break; + + if (xpo && po != xpo) + continue; + + smp_lock* pcs = po->get_smp_lock(); + + switch (verb) + { + case PERF_CMD_VERB_ON: + if (pcs) pcs->set_perf_collect(TRUE); + break; + + case PERF_CMD_VERB_OFF : + if (pcs) pcs->set_perf_collect(FALSE); + break; + + case PERF_CMD_VERB_RESET : + if (pcs) pcs->perf_reset(); + break; + + case PERF_CMD_VERB_SHOW: + if (pcs) + { + pcs->perf_show(smp_stdout, po->name); + if (sim_log) pcs->perf_show(sim_log, po->name); + } + break; + + case PERF_CMD_VERB_NONE: + /* cannot happen; include here just to suppress false GCC warning */ + break; + } + } + + return SCPE_OK; +} + +void* malloc_aligned(size_t size, size_t alignment) +{ +#if defined(_WIN32) + void* p = _aligned_malloc(size, alignment); +#elif defined(__GNUC__) + void* p = NULL; + if (posix_memalign(&p, alignment, size)) + p = NULL; + /* GNU LIBC requires alignment to be multiple of sizeof(void*), + hence alignment=4 will fail with GNU LIBC on x64 systems */ + if (p == NULL && alignment < sizeof(void*)) + p = malloc(size); +#else +# error Unimplemented +#endif + if ((alignment - 1) & (t_addr_val) p) + { + free_aligned(p); + p = NULL; + } + return p; +} + +void* calloc_aligned (size_t num, size_t elsize, size_t alignment) +{ + size_t xsize = num * elsize; + void* p = malloc_aligned(xsize, alignment); + if (p) memset(p, 0, xsize); + return p; +} + +void free_aligned(void* p) +{ +#if defined(_WIN32) + return _aligned_free(p); +#elif defined(__GNUC__) + free(p); +#else +# error Unimplemented +#endif +} + +void* operator_new_aligned(size_t size, size_t alignment) +{ + void* p = malloc_aligned(size, alignment); + if (! p) panic("Unable to allocate aligned memory"); + return p; +} + +void operator_delete_aligned(void* p) +{ + if (p) free_aligned(p); +} + +void throw_sim_exception_ABORT(RUN_DECL, t_stat code) +{ + sim_exception_ABORT* sa; + if ((sa = cpu_unit->cpu_exception_ABORT) == NULL) + { + sa = new sim_exception_ABORT(code, TRUE); + } + else + { + cpu_unit->cpu_exception_ABORT = NULL; + sa->code = code; + } + sim_throw(sa); +} + +void panic(const char* cause) +{ + sim_throw(new sim_exception_SimError(cause)); +} + +t_value ws_min_rd(REG* r, uint32 idx) +{ + return sim_ws_min; +} + +void ws_min_wr(REG* r, uint32 idx, t_value value) +{ + sim_ws_min = (uint32) value; + sim_ws_lock = (sim_ws_min != 0 || sim_ws_max != 0); + sim_ws_settings_changed = TRUE; +} + +t_value ws_max_rd(REG* r, uint32 idx) +{ + return sim_ws_max; +} + +void ws_max_wr(REG* r, uint32 idx, t_value value) +{ + sim_ws_max = (uint32) value; + sim_ws_lock = (sim_ws_min != 0 || sim_ws_max != 0); + sim_ws_settings_changed = TRUE; +} + +t_value ws_lock_rd(REG* r, uint32 idx) +{ + return sim_ws_lock; +} + +void ws_lock_wr(REG* r, uint32 idx, t_value value) +{ + sim_ws_lock = (t_bool) value; + if (! sim_ws_lock) + sim_ws_min = sim_ws_max = 0; + sim_ws_settings_changed = TRUE; +} + +/* Called when locking a critical object */ +void critical_lock(sim_lock_criticality_t criticality) +{ + RUN_SCOPE_RSCX_ONLY; + + if (rscx->thread_type == SIM_THREAD_TYPE_CPU) + { + CPU_UNIT* cpu_unit = rscx->cpu_unit; + + switch (criticality) + { + case SIM_LOCK_CRITICALITY_VM: + if (++rscx->vm_critical_locks == 1 && must_control_prio()) + cpu_reevaluate_thread_priority(RUN_PASS); + break; + + case SIM_LOCK_CRITICALITY_OS_HI: + if (++rscx->os_hi_critical_locks == 1 && must_control_prio()) + cpu_reevaluate_thread_priority(RUN_PASS); + break; + + case SIM_LOCK_CRITICALITY_NONE: + break; + } + } + else if (rscx->thread_type == SIM_THREAD_TYPE_CONSOLE || + rscx->thread_type == SIM_THREAD_TYPE_IOP) + { + switch (criticality) + { + case SIM_LOCK_CRITICALITY_VM: + if (++rscx->vm_critical_locks == 1 && must_control_prio()) + sim_reevaluate_noncpu_thread_priority(rscx); + break; + + case SIM_LOCK_CRITICALITY_OS_HI: + if (++rscx->os_hi_critical_locks == 1 && must_control_prio()) + sim_reevaluate_noncpu_thread_priority(rscx); + break; + + case SIM_LOCK_CRITICALITY_NONE: + break; + } + } + + /* + * We do not adjust priority for CLOCK thread since it is always + * executing at very high priority (and is meant to). + */ +} + +/* Called when unlocking a critical object */ +void critical_unlock(sim_lock_criticality_t criticality) +{ + RUN_SCOPE_RSCX_ONLY; + + if (rscx->thread_type == SIM_THREAD_TYPE_CPU) + { + CPU_UNIT* cpu_unit = rscx->cpu_unit; + + switch (criticality) + { + case SIM_LOCK_CRITICALITY_VM: + if (--rscx->vm_critical_locks == 0 && must_control_prio()) + cpu_reevaluate_thread_priority(RUN_PASS); + break; + + case SIM_LOCK_CRITICALITY_OS_HI: + if (--rscx->os_hi_critical_locks == 0 && must_control_prio()) + cpu_reevaluate_thread_priority(RUN_PASS); + break; + + case SIM_LOCK_CRITICALITY_NONE: + break; + } + } + else if (rscx->thread_type == SIM_THREAD_TYPE_CONSOLE || + rscx->thread_type == SIM_THREAD_TYPE_IOP) + { + switch (criticality) + { + case SIM_LOCK_CRITICALITY_VM: + if (--rscx->vm_critical_locks == 0 && must_control_prio()) + sim_reevaluate_noncpu_thread_priority(rscx); + break; + + case SIM_LOCK_CRITICALITY_OS_HI: + if (--rscx->os_hi_critical_locks == 0 && must_control_prio()) + sim_reevaluate_noncpu_thread_priority(rscx); + break; + + case SIM_LOCK_CRITICALITY_NONE: + break; + } + } + + /* + * We do not adjust priority for CLOCK thread since it is always + * executing at very high priority (and is meant to). + */ +} + +void sim_reevaluate_noncpu_thread_priority(run_scope_context* rscx) +{ + sim_thread_priority_t prio; + + if (rscx->vm_critical_locks) + prio = SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM; + else if (rscx->os_hi_critical_locks) + prio = SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI; + else + { + prio = rscx->bprio; + if (unlikely(prio == SIMH_THREAD_PRIORITY_INVALID)) + return; + } + + if (prio != rscx->cprio) + { + rscx->reevaluating_prio = TRUE; + smp_set_thread_priority(prio); + rscx->reevaluating_prio = FALSE; + + rscx->cprio = prio; + } +} + + +/* + * Update sim_mp_active from cpu_running_set. + * Must be called either by any VCPU thread holding cpu_database_lock or console thread when VCPUs are paused. + */ +void sim_mp_active_update() +{ + int ncpus = 0; + + for (uint32 cpu_ix = 0; cpu_ix < sim_ncpus; cpu_ix++) + { + if (cpu_running_set.is_set(cpu_ix)) + { + if (++ncpus >= 2) + { + sim_mp_active = TRUE; + return; + } + } + } + + sim_mp_active = FALSE; +} diff --git a/src/scp.h b/src/scp.h new file mode 100644 index 0000000..2a6577a --- /dev/null +++ b/src/scp.h @@ -0,0 +1,213 @@ +/* scp.h: simulator control program headers + + 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. + + 05-Dec-10 MP Added macro invocation of sim_debug + 09-Aug-06 JDB Added assign_device and deassign_device + 14-Jul-06 RMS Added sim_activate_abs + 06-Jan-06 RMS Added fprint_stopped_gen + Changed arg type in sim_brk_test + 07-Feb-05 RMS Added ASSERT command + 09-Sep-04 RMS Added reset_all_p + 14-Feb-04 RMS Added debug prototypes (from Dave Hittner) + 02-Jan-04 RMS Split out from SCP +*/ + +#ifndef _SIM_SCP_H_ +#define _SIM_SCP_H_ 0 + +/* run_cmd parameters */ + +#define RU_RUN 0 /* run */ +#define RU_GO 1 /* go */ +#define RU_STEP 2 /* step */ +#define RU_CONT 3 /* continue */ +#define RU_BOOT 4 /* boot */ + +/* get_sim_opt parameters */ + +#define CMD_OPT_SW 001 /* switches */ +#define CMD_OPT_OF 002 /* output file */ +#define CMD_OPT_SCH 004 /* search */ +#define CMD_OPT_DFT 010 /* defaults */ + +typedef enum +{ + RescheduleCosched_RequeueOnProcessEvent = 1, + RescheduleCosched_OnCancelClock = 2, + RescheduleCosched_OnSynClk = 3 +} +RescheduleCoschedHow; + +/* Command processors */ + +t_stat reset_cmd (int32 flag, char *ptr); +t_stat exdep_cmd (int32 flag, char *ptr); +t_stat eval_cmd (int32 flag, char *ptr); +t_stat load_cmd (int32 flag, char *ptr); +t_stat run_cmd (int32 flag, char *ptr); +t_stat attach_cmd (int32 flag, char *ptr); +t_stat detach_cmd (int32 flag, char *ptr); +t_stat assign_cmd (int32 flag, char *ptr); +t_stat deassign_cmd (int32 flag, char *ptr); +t_stat save_cmd (int32 flag, char *ptr); +t_stat restore_cmd (int32 flag, char *ptr); +t_stat exit_cmd (int32 flag, char *ptr); +t_stat set_cmd (int32 flag, char *ptr); +t_stat show_cmd (int32 flag, char *ptr); +t_stat perf_cmd (int32 flag, char *ptr); +t_stat cpu_cmd (int32 flag, char *ptr); +t_stat brk_cmd (int32 flag, char *ptr); +t_stat do_cmd (int32 flag, char *ptr); +t_stat do_script (int32 flag, sim_cstream* script, const char* prompt); +t_stat assert_cmd (int32 flag, char *ptr); +t_stat help_cmd (int32 flag, char *ptr); +t_stat spawn_cmd (int32 flag, char *ptr); +t_stat echo_cmd (int32 flag, char *ptr); +t_stat process_new_brk_actions (int flag); +t_stat process_brk_actions (int flag, char** ppcmd); + +/* Utility routines */ + +t_stat sim_process_event (RUN_DECL); +t_stat sim_activate (UNIT *uptr, int32 interval, int32 nticks = 0); +t_stat sim_activate_abs (UNIT *uptr, int32 interval); +t_stat sim_activate_clk_cosched (UNIT *uptr, int32 nticks); +t_stat sim_activate_clk_cosched_abs (UNIT *uptr, int32 nticks); +t_stat sim_cancel (UNIT *uptr); +int32 sim_is_active (UNIT *uptr); +void sim_asynch_activate (UNIT *uptr, int32 interval); +void sim_asynch_activate_abs (UNIT *uptr, int32 interval); +void sim_reschedule_cosched(RUN_DECL, RescheduleCoschedHow how); +void sim_flush_migrated_clock_queue_entries(RUN_DECL); +int32 sim_calculate_device_activity_protection_interval(RUN_DECL); +void sim_requeue_syswide_events(RUN_DECL); +void sim_async_process_io_events(RUN_DECL, t_bool* any = NULL, t_bool current_only = FALSE); +void sim_async_post_io_event(UNIT* uptr); +void sim_async_process_io_events_for_console(); +double sim_gtime (RUN_DECL); +uint32 sim_grtime (RUN_DECL); +void sim_bind_devunits_lock(DEVICE *dptr, smp_lock* lock); +t_stat attach_unit (UNIT *uptr, char *cptr); +t_stat detach_unit (UNIT *uptr); +t_stat assign_device (DEVICE *dptr, char *cptr); +t_stat deassign_device (DEVICE *dptr); +t_stat reset_all (uint32 start_device); +t_stat reset_all_p (uint32 start_device); +const char *sim_dname (DEVICE *dptr); +t_stat get_yn (char *ques, t_stat deflt); +char *get_sim_opt (int32 opt, char *cptr, t_stat *st); +char *get_glyph (char *iptr, char *optr, char mchar); +char *get_glyph_nc (char *iptr, char *optr, char mchar); +t_value get_uint (char *cptr, uint32 radix, t_value max, t_stat *status); +t_stat get_double(char *cptr, double* pval); +char *get_range (DEVICE *dptr, char *cptr, t_addr *lo, t_addr *hi, + uint32 rdx, t_addr max, char term); +t_stat get_ipaddr (char *cptr, uint32 *ipa, uint32 *ipp); +t_value strtotv (char *cptr, char **endptr, uint32 radix); +t_stat fprint_val (SMP_FILE *stream, t_value val, uint32 rdx, uint32 wid, uint32 fmt); +CTAB *find_cmd (char *gbuf); +DEVICE *find_dev (char *ptr); +DEVICE *find_dev (DIB* dibp); +DEVICE *find_unit (char *ptr, UNIT **uptr); +DEVICE *find_dev_from_unit (UNIT *uptr); +REG *find_reg (char *ptr, char **optr, DEVICE *dptr); +CTAB *find_ctab (CTAB *tab, const char *gbuf); +C1TAB *find_c1tab (C1TAB *tab, const char *gbuf); +SHTAB *find_shtab (SHTAB *tab, const char *gbuf); +BRKTAB *sim_brk_fnd (t_addr loc); +uint32 sim_brk_test (RUN_DECL, t_addr bloc, uint32 btyp); +void sim_brk_clrspc (RUN_DECL, uint32 spc); +char *match_ext (char *fnam, char *ext); +const char *sim_error_text (t_stat stat); +t_stat sim_string_to_stat (char *cptr, t_stat *cond); +void sim_debug_u16 (uint32 dbits, DEVICE* dptr, const char* const* bitdefs, uint16 before, uint16 after, int terminate); + +#if defined (__DECC) && defined (__VMS) && (defined (__VAX) || (__DECC_VER < 60590001)) +# define CANT_USE_MACRO_VA_ARGS 1 +#endif + +#ifdef CANT_USE_MACRO_VA_ARGS +# define _sim_debug sim_debug + void sim_debug (uint32 dbits, DEVICE* dptr, const char* fmt, ...); +#else + void _sim_debug (uint32 dbits, DEVICE* dptr, const char* fmt, ...); +# define sim_debug(dbits, dptr, ...) if (unlikely(sim_deb != NULL) && ((dptr)->dctrl & dbits)) _sim_debug (dbits, dptr, __VA_ARGS__) +#endif + +void fprint_stopped_gen (SMP_FILE *st, REG *pc, DEVICE *dptr); +char* read_line (char *ptr, int32 size, SMP_FILE *stream); +SMP_THREAD_ROUTINE_DECL sim_cpu_work_thread_proc (void* arg); +SMP_THREAD_ROUTINE_DECL sim_clock_thread_proc (void* arg); +void sim_ws_setup(); +void sim_prefault_memory(); +t_stat xdev_cmd(int32 flag, char *ptr); + +/* SIM <-> CPU routines */ +t_stat sim_instr(RUN_DECL); +void cpu_backstep_pc(RUN_DECL); +void init_cpu_unit_0(); +void sim_init_interrupt_info(); +t_bool cpu_create_cpus(uint32 ncpus); +t_stat cpu_cmd_info (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +void cpu_sync_flags(CPU_UNIT* uptr); +void sim_mp_active_update(); + +void cpu_update_cycles_per_second(RUN_DECL, uint32 ips, t_bool valid, uint32 os_msec); +uint32 cpu_get_cycles_per_second(RUN_DECL); +void cpu_stopping_ips_rate_update(RUN_DECL); + +/* CPU state database */ + +extern smp_lock* cpu_database_lock; +extern uint32 cpu_cycles_mark[SIM_MAX_CPUS]; +extern uint32 cpu_cycles_sleepexit[SIM_MAX_CPUS]; +extern cpu_set cpu_waitset[SIM_MAX_CPUS]; +extern cpu_set cpu_running_set; +extern atomic_bool sim_mp_active; + +/* CPU cycles per second rate */ + +extern smp_lock* cpu_cycles_per_second_lock; +extern atomic_uint32_var cpu_cycles_per_second; + +/* Synchronization objects to control CPUs <-> console interaction */ + +extern smp_semaphore* cpu_attention; +extern smp_barrier* cpu_pause_sync_barrier; +extern smp_semaphore* cpu_clock_run_gate; +extern t_bool sim_ttrun_mode; +extern t_bool use_clock_thread; +extern t_bool sim_clock_thread_created; +extern smp_thread_t sim_clock_thread; + +/* VCPU affinity control */ + +extern t_bool sim_vcpu_per_core; + +/* other globals */ +extern SMP_FILE* sim_deb; +extern uint32 sim_vsmp_os; + +#endif diff --git a/src/sim_aio.cpp b/src/sim_aio.cpp new file mode 100644 index 0000000..90e6e5a --- /dev/null +++ b/src/sim_aio.cpp @@ -0,0 +1,367 @@ +/* + * sim_aio.cpp: support for asynchrnonous IO + */ + +#include "sim_defs.h" + +/****************************************************************************************** +* aio_context helper class for async disk and tape IO, * +* both disk_context and tape_context derive from aio_context * +******************************************************************************************/ + +aio_context::aio_context(UNIT* uptr) +{ + dptr = NULL; + dbit = 0; + this->uptr = uptr; + + asynch_io = FALSE; + io_thread = SMP_THREAD_NULL; + io_thread_created = FALSE; + io_event = NULL; + io_flush_ack = NULL; + io_do_flush = FALSE; +} + +aio_context::~aio_context() +{ + asynch_uninit(); +} + +void aio_context::asynch_init(smp_thread_routine_t start_routine, void* arg) +{ + if (io_event == NULL) + io_event = smp_simple_semaphore::create(0); + else + io_event->clear(); + + if (io_flush_ack == NULL) + io_flush_ack = smp_event::create(); + + if (! io_thread_created) + { + smp_create_thread(start_routine, arg, &io_thread); + io_thread_created = TRUE; + } +} + +/* + * asynch_uninit will be called either via sim_disk_unload by a VCPU thread holding uptr->lock + * (perhaps sometimes console thread holding uptr->lock if manually writing to UQSSP registers) + * or by console thread doing detach when all VCPU threads are paused. + * In either case mutual exclusion between callers is observed. + */ +void aio_context::asynch_uninit() +{ + if (io_thread_created) + { + asynch_io = FALSE; + io_event_signal(); + smp_wait_thread(io_thread); + io_thread_created = FALSE; + } + + asynch_io = FALSE; + delete io_event; io_event = NULL; + delete io_flush_ack; io_flush_ack = NULL; +} + +void aio_context::thread_loop() +{ + for (;;) + { + io_event->wait(); + volatile t_bool was_asynch_io = asynch_io; + volatile t_bool was_flush = io_do_flush; + io_do_flush = FALSE; + if (has_request()) + { + /* after seeing operation code set, issue rmb to ensure + request paramaters are locally visible on this CPU */ + smp_rmb(); + perform_request(); + } + if (was_flush) + { + perform_flush(); + io_flush_ack->set(); + } + if (! was_asynch_io) + break; + } +} + +/* + * flush() will be normally called either by a VCPU thread holding uptr->lock + * or by console thread doing io_flush when all VCPU threads are paused + * or by console thread manually writing to UQSSP registers when all VCPU threads are paused + * (and also holding uptr->lock, in addition). + * In either case mutual exclusion between callers is observed. + */ +void aio_context::flush() +{ + if (asynch_io) + { + io_flush_ack->clear(); + io_do_flush = TRUE; + io_event_signal(); + io_flush_ack->wait(); + } + else + { + perform_flush(); + } +} + +/****************************************************************************************** +* AIO event queue * +******************************************************************************************/ + +/* + * Events are posed to AIO event queue by IO processing (IOP) threads on completion of + * asynchronous IO request. + * + * Events are fetched from the queue and processed by primary VCPU's thread, since on VAX + * only primrary processor is handling the interrupts (under VMS anyway), therefore it would + * be wasteful to spread AIO event handling to other CPUs so they almost immediatelly re-post + * device interrupts to the primary processor. It is much more efficient to handle all of + * AIO postprocessing within the context of primary processor only. + * + * Ocassionally requests can be fetched and processed also on the console thread, but + * within the context of primary processor as well -- when all VCPUs are suspended and + * simulator is entering console mode or executing in the console mode. + * + * Thus we have multiple producers (IOP threads) but only one consumer (either primary + * VCPU thread or console thread, when VCPU threads are paused by the console). + * + * To reduce overhead and avoid preemption/convoying issues, we implement AIO event + * queue as lock-free queue. + * + * In our particular case (single consumer and multiple producers) we can get by with + * CAS or LL/SC instructions. + * + * If there were multiple consumers, we'd have to use lock-free queue such as described + * in Herlihy & Shavit's "The Art of Multiprocessor's Programming" (2008), pp. 230-237 + * or Michael & Scott's "Simple, Fast and Practical Non-Blocking and Blocking Concurrent + * Queue Algorithms" (1996). These algorithms requuire CAS2 or LL/SC and cannot be + * implemented with CAS alone. DCAS abstraction for various platforms is handily + * defined in libldfs sources (libldfs.org, file abstraction_dcas.c). + * + * On host machines where CAS (or LL/SC) instructons are not available, AIO event queue + * would have to be implemented as lockable queue, using something like + * + * AUTO_INIT_LOCK(sim_asynch_queue_lock, SIM_LOCK_CRITICALITY_NONE, 200); + * + * and + * + * sim_asynch_queue_lock->lock(); + * sim_asynch_queue_lock->unlock(); + * + * Current implementaton uses CAS. + * + * There is no ABA problem on inserting side because: + * + * - UNIT object being inserted is "owned" by inserting IOP thread + * - value of head pointer being replaced is confirmed atomically by CAS + * - no other field is modified + * + * There is no ABA problem on removal side because: + * + * - inserion and removal happens only at the head, so a_next link of + * head entry is not modified by IOP thread + * - head pointer value cannot cycle ABA except through dequeueing, + * by dequeueing is done only by single retrieval thread which is + * synchronized to itself + * + * Interlocked header insertion/retrieval works as LIFO. To maintain fairness + * and prevent saturation of the queue by single device, we convert it to FIFO + * after the entries had been removed off the interlocked queue. Retrieval side + * maintains list private to retrieval thread. After copying all the entries + * from the interlocked LIFO list, it puts them on private LIFO list. Double + * LIFO ordering creates FIFO. + * + * Note that current AIO queue code assumes that device handler can use + * either sim_async_post_io_event or sim_asynch_activate[_abs], but not both + * for the same UNIT. + * + */ + +#if !defined(VM_VAX_MP) +# error review code: AIO_SIGNAL_CPU sends to primary CPU +#endif + +/* events are put into sim_asynch_queue by IOP threads, retrieved by primary VCPU thread */ +static smp_interlocked_addr_val_var sim_asynch_queue = smp_var_init(0); + +/* "aqueue" is accessed by primary VCPU thread only */ +static UNIT* aqueue = NULL; + +static void init_aio_data() +{ + smp_check_aligned(& sim_asynch_queue); +} +ON_INIT_INVOKE(init_aio_data); + +t_bool sim_async_io_queue_isempty() +{ + return smp_interlocked_cas_done_var(& sim_asynch_queue, (t_addr_val) 0, (t_addr_val) 0); +} + +/* Insert new entry at the head, in LIFO order */ +#define AIO_INSERT_QUEUE(uptr) \ + do \ + { \ + (uptr)->a_next = (UNIT*) smp_var(sim_asynch_queue); \ + } \ + while (! smp_interlocked_cas_done_var(& sim_asynch_queue, (t_addr_val) (uptr)->a_next, (t_addr_val) (uptr))) + +#define AIO_SIGNAL_CPU() \ + interrupt_set_int(&cpu_unit_0, IPL_ASYNC_IO, INT_V_ASYNC_IO) + +void sim_async_post_io_event(UNIT* uptr) +{ + smp_pre_interlocked_wmb(); + AIO_INSERT_QUEUE(uptr); + AIO_SIGNAL_CPU(); +} + +static t_stat sim_async_activate_thunk(UNIT *uptr, int32 interval) +{ + return sim_activate(uptr, interval); +} + +void sim_asynch_activate(UNIT *uptr, int32 interval) +{ + t_bool signal = FALSE; + + uptr->lock->lock(); + + if (uptr->a_activate_call == NULL) + { + uptr->a_activate_call = sim_async_activate_thunk; + uptr->a_sim_interval = interval; + AIO_INSERT_QUEUE(uptr); + signal = TRUE; + } + + uptr->lock->unlock(); + + if (signal) + AIO_SIGNAL_CPU(); +} + +void sim_asynch_activate_abs(UNIT *uptr, int32 interval) +{ + uptr->lock->lock(); + + t_bool onqueue = (uptr->a_activate_call != NULL); + uptr->a_activate_call = sim_activate_abs; + uptr->a_sim_interval = interval; + if (! onqueue) AIO_INSERT_QUEUE(uptr); + + uptr->lock->unlock(); + + if (! onqueue) AIO_SIGNAL_CPU(); +} + +/* + * will normally be invoked at thread priority level VM_CRITICAL, + * boosted up by sent interrupt and before interrupt processing + * recalculates thread priority down + */ +void sim_async_process_io_events(RUN_DECL, t_bool* pany, t_bool current_only) +{ +#if !defined(VM_VAX_MP) +# error review code: assumes primary CPU context +#endif + t_bool any = FALSE; + + for (;;) + { + UNIT* aq = NULL; + UNIT* uptr; + + /* + * Dequeue entries from AIO queue and transfer them to local queue "aq", + * reversing order of entries from LIFO to FIFO. + */ + for (;;) + { + t_addr_val qe = smp_var(sim_asynch_queue); + uptr = (UNIT*) qe; + if (uptr == NULL) break; + if (smp_interlocked_cas_done_var(& sim_asynch_queue, qe, (t_addr_val) uptr->a_next)) + { + smp_post_interlocked_rmb(); + uptr->a_next = aq; + aq = uptr; + } + } + + /* + * One's first impulse is to process entries directly off "aq", but here is the problem: + * a_check_completion can cause device reset, which in turn can call again sim_async_process_io_events, + * recursively. This second recursive call should be able to drain events that we just picked off the queue. + * + * Therefore we put events fetched into "aq" on a static thread-local queue "aqueue" and process + * events off this queue, which will be available to recursive invocations of this routine as well. + */ + if (aqueue == NULL) + { + aqueue = aq; + } + else + { + /* maintain FIFO order */ + uptr = aq; + while (uptr->a_next) + uptr = uptr->a_next; + uptr->a_next = aq; + } + + /* Now process events off "aqueue" */ + while (aqueue != NULL) + { + uptr = aqueue; + aqueue = uptr->a_next; + uptr->a_next = NULL; + any = TRUE; + + uptr->lock->lock(); + if (uptr->a_check_completion) + (*uptr->a_check_completion)(uptr); + if (uptr->a_activate_call) + { + (*uptr->a_activate_call)(uptr, uptr->a_sim_interval); + uptr->a_activate_call = NULL; + } + uptr->lock->unlock(); + } + + /* anything left? */ + if (current_only || sim_async_io_queue_isempty()) + break; + } + + if (pany) + *pany = any; +} + +/* + * Called by console thread when VCPUs are paused to process pending async IO events + * and to flush async IO queue. + */ +void sim_async_process_io_events_for_console() +{ +#if !defined(VM_VAX_MP) +# error review code: assumes events are processed in the primary CPU context +#endif + /* + * VAX-specific: do it in the context of primary CPU + */ + RUN_SCOPE_RSCX; + CPU_UNIT* sv_cpu_unit = rscx->cpu_unit; + rscx->cpu_unit = cpu_unit = &cpu_unit_0; + sim_async_process_io_events(RUN_PASS); + rscx->cpu_unit = sv_cpu_unit; +} diff --git a/src/sim_barriers.cpp b/src/sim_barriers.cpp new file mode 100644 index 0000000..1eb673b --- /dev/null +++ b/src/sim_barriers.cpp @@ -0,0 +1,585 @@ +/* + * Memory barrier functions. Placed into a separate file so compiler cannot know their content + * when compilng other modules and will treat them as a compiler barrier too. + */ + +#if defined(_WIN32) +# define _WIN32_WINNT 0x0403 +# include +# include +#endif + +#if defined(__linux) || defined(__APPLE__) +# include +# include +#endif + +#include "sim_defs.h" + +/* + * For information on x86 and x64 shared memory synchronization see + * + * "Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 3 (3A & 3B): System Programming Guide" + * (order number: 325384-039US), sections 8.* and 19.34, + * at http://www.intel.com/Assets/PDF/manual/325384.pdf (hereafter P686) + * + * "Intel Architecture Software Developer's Manual, Volume 3: System Programming" + * (order number 243192), sections 7.1.2 and 7.2.2, + * at http://communities.intel.com/servlet/JiveServlet/downloadBody/5061-102-1-8118/Pentium_SW_Developers_Manual_Vol3_SystemProgramming.pdf + * (hereafter P686) + * + * P686, section 8.1.2: + * + * "For the P6 family processors, locked operations serialize all outstanding load and store operations (that is, + * wait for them to complete). This rule is also true for the Pentium 4 and Intel Xeon processors, with one exception. + * Load operations that reference weakly ordered memory types (such as the WC memory type) may not be serialized." + * + * P686, section 8.2.2: + * + * The Pentium and Intel486 processors follow the processor-ordered memory model; + * however, they operate as strongly-ordered processors under most circumstances. + * Reads and writes always appear in programmed order at the system bus—except for + * the following situation where processor ordering is exhibited. Read misses are + * permitted to go ahead of buffered writes on the system bus when all the buffered + * writes are cache hits and, therefore, are not directed to the same address being + * accessed by the read miss. + * + * The Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium 4, and P6 family processors + * also use a processor-ordered memory-ordering model that can be further + * defined as “write ordered with store-buffer forwarding.” This model can be characterized + * as follows. + * In a single-processor system for memory regions defined as write-back cacheable, + * the memory-ordering model respects the following principles + * + * - Reads are not reordered with other reads. + * - Writes are not reordered with older reads. + * - Writes to memory are not reordered with other writes, with the following exceptions [...] + * + * Reads may be reordered with older writes to different locations but not with older + * writes to the same location. + * + * - Reads or writes cannot be reordered with I/O instructions, locked instructions, or + * serializing instructions. + * - Reads cannot pass earlier LFENCE and MFENCE instructions. + * - Writes cannot pass earlier LFENCE, SFENCE, and MFENCE instructions. + * - LFENCE instructions cannot pass earlier reads. + * - SFENCE instructions cannot pass earlier writes. + * - MFENCE instructions cannot pass earlier reads or writes. + * + * In a multiple-processor system, the following ordering principles apply: + * + * - Individual processors use the same ordering principles as in a single-processor + * system. + * - Writes by a single processor are observed in the same order by all processors. + * - Writes from an individual processor are NOT ordered with respect to the writes + * from other processors. + * - Memory ordering obeys causality (memory ordering respects transitive + * visibility). + * - Any two stores are seen in a consistent order by processors other than those + * performing the stores + * - Locked instructions have a total order. + * + * The processor-ordering model described in this section is virtually identical to that + * used by the Pentium and Intel486 processors. The only enhancements in the Pentium + * 4, Intel Xeon, and P6 family processors are: + * + * - Added support for speculative reads, while still adhering to the ordering + * principles above. + * - Store-buffer forwarding, when a read passes a write to the same memory + * location. + * - Out of order store from long string store and string move operations + * + * P686, section 8.2.5: + * + * Locking operations typically operate like I/O operations in that they wait + * for all previous instructions to complete and for all buffered writes to drain to memory. + * + * On older processors that do not have LFENCE, LOCK prefix stalls reads. + * + * P686, section 19.34: + * + * During a locked bus cycle, the Intel486 processor will always access external + * memory, it will never look for the location in the on-chip cache. All data pending in + * the Intel486 processor's store buffers will be written to memory before a locked cycle + * is allowed to proceed to the external bus. Thus, the locked bus cycle can be used for + * eliminating the possibility of reordering read cycles on the Intel486 processor. The + * Pentium processor does check its cache on a read-modify-write access and, if the + * cache line has been modified, writes the contents back to memory before locking the + * bus. + * + * The P6 family processors write to their cache on a read-modify-write operation + * (if the access does not split across a cache line) and does not write back to system + * memory. If the access does split across a cache line, it locks the bus and accesses + * system memory. + * + * P586, section 7.1.2: + * + * For the P6 family processors, locked operations serialize all outstanding load and store operations + * (that is, wait for them to complete). + * + * In a nutshell, most x86 writes are strongly ordered and WMB can be no-op except for writes by + * non-temporal SSE instructions, and except for some clones that provide out-of-order stroring and that + * should use "LOCK; some-instruction" for WMB. + * + * However reads can be reorderd on x86, so RMB and MB should expand to "LOCK; some-instuction". + * CPUs that have SSE and applications that use SSE non-temporal instructions should use lfence, sfence, mfence. + * + */ + +#if defined (__x86_32__) || defined (__x86_64__) +static t_bool have_lfence = FALSE; +static t_bool have_sfence = FALSE; +static t_bool have_mfence = FALSE; +static t_bool have_PentiumPro = FALSE; +static t_bool have_oostore = FALSE; +t_bool have_x86_xaddl = FALSE; +t_bool have_x86_cmpxchgl = FALSE; +t_bool have_pentium = FALSE; +t_bool have_cmpxchg8b = FALSE; +#endif + +/* + * SMP_X86_USING_NONTEMPORAL indicates whether compiler-generated code or runtime libraries might + * utilize non-temporal SSE/3DNow instructions without proper LFENCE/SFENCE/MFENCE termination, + * and hence if such termination should be supplied by VAX MP as a part of memory barriers. + * + * If application (including runtime libraries) uses SSE/3DNow non-temporal instructions without + * proper termination, memory barriers should be defined as follows: + * + * rmb = barrier() + lfence + * wmb = barrier() + sfence + * mb = barrier() + mfence + * + * If application does not use SSE/3DNow non-temporal instructions (and assuming processor does not have + * OOStore mode or it is not enabled and processor is not PentiumPro), memory barriers can be defined + * in a more lightweight fashion, as follows: + * + * rmb = barrier() + * wmb = barrier() + * mb = barrier() + lock-addl + * + * No additional hardware primitive is required for ordering in wmb since x86/x64 orders regular writes + * (except for non-temporal instructions and for OOStore case). + * + * No additional hardware primitive is required for ordering in rmb since x86/x64 orders regular reads + * (except for non-temporal instructions and for PentiumPro case). + * + * Some x86 clone processors from 3rd party manufacturers (but not Intel nor AMD) used to have OOStore mode + * enable-able via a setting. For these processors: + * + * wmb = barrier() + lock-addl + * mb = barrier() + lock-addl + * + * PentiumPro has a bug in cache coherency that can cause it effectively reorder loads in a multiprocessing + * system. As a workaround for this bug, rmb should be defined on PentiumPro as: + * + * rmb = barrier() + lock-addl + * + * Note that MB is _not_ equivalent to the sum of RMB and WMB: the latter order loads-to-loads + * and stores-to-stores, but not load-to-stores and stores-to-loads. Intel x86/x64 is an example + * of processor where the distinction matters. OOStore mode and PentiumPro bug aside, x86/x64 + * processors do not locally reorder loads with other loads, and do not locally reorder stores + * with other stores, but loads can be reordered with older stores to a different (not same!) location + * unless memory barrier is issued in between. + * + * For a summary see Paul McKenney, "Is Parallel Programming Hard, And, If So, What Can You Do About It?", + * chapter "Memory-Barrier Instructions for Specific CPUs", subsection on x86. + * + * Also see a list of documents on x86-TSO model in "VAX MP Techincal Overview", Appendix A. + * + ****************************************************************************************************** + * + * Linux behavior corresponds to SMP_X86_USING_NONTEMPORAL = TRUE since Linux has to support + * context switching between user-level applications that may utilize non-temporal instructions. + * Whether this indicates any possible use of non-temporal instructions in the kernel is unclear. + * + * See Linux kernel sources: + * + * arch/x86/include/asm/system.h + * arch/um/sys-i386/shared/sysdep/system.h + * arch/um/sys-x86_64/shared/sysdep/system.h + * + * In Windows DDK, KeMemoryBarrier (full MB) is defined as: + * + * on x86: [lock] xchg barrier, eax + * + * on x64: lock or dword ptr [rsp], 0 ; __faststorefence + * lfence + * + * Microsoft documentation for __faststorefence says: + * + * "On the x64 platform, this routine generates an instruction that is a faster store fence + * than the sfence instruction. Use this intrinsic instead of _mm_sfence on the x64 platform." + * + */ + +#if defined (__x86_32__) + static void smp_mb_init_x86_32(); +#endif + +#if defined(_WIN32) && defined(__x86_32__) +# define ASM_MFENCE __asm mfence +# define ASM_LFENCE __asm lfence +# define ASM_SFENCE __asm sfence +#elif defined(_WIN32) && defined(__x86_64__) + void __faststorefence(void); + void _mm_lfence(void); + void _mm_mfence(void); + void _mm_sfence(void); +# pragma intrinsic(__faststorefence) +# pragma intrinsic(_mm_lfence) +# pragma intrinsic(_mm_mfence) +# pragma intrinsic(_mm_sfence) +# define ASM_MFENCE _mm_mfence(); +# define ASM_LFENCE _mm_lfence(); +# define ASM_SFENCE _mm_sfence(); +#elif defined(__GNUC__) +// http://gcc.gnu.org/onlinedocs/gcc-4.6.1/gcc.pdf, section 6.41 and following +// http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html +// http://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html +// http://www.osdever.net/tutorials/view/a-brief-tutorial-on-gcc-inline-asm +// http://asm.sourceforge.net//articles/linasm.html +// http://www.cims.nyu.edu/cgi-systems/info2html?%28gcc%29Extended%2520Asm +# define ASM_MFENCE __asm__ __volatile__ ("mfence":::"memory", "cc") +# define ASM_LFENCE __asm__ __volatile__ ("lfence":::"memory", "cc") +# define ASM_SFENCE __asm__ __volatile__ ("sfence":::"memory", "cc") +#endif + +#if defined(_WIN32) && defined(__x86_32__) +# define ASM_LOCKED_BARRIER __asm lock or dword ptr [esp], 0 +#endif + +#if defined(_WIN64) && defined(__x86_64__) +# define ASM_LOCKED_BARRIER __faststorefence(); +#endif + +#if defined(__GNUC__) && defined(__x86_32__) +# define ASM_LOCKED_BARRIER __asm__ __volatile__ ("lock; orl $0,0(%%esp)":::"memory", "cc") +#endif + +#if defined(__GNUC__) && defined(__x86_64__) +# define ASM_LOCKED_BARRIER __asm__ __volatile__ ("lock; orq $0,0(%%rsp)":::"memory", "cc") +#endif + +#if defined (__x86_32__) || defined (__x86_64__) +static void smp_xmb_noop() +{ + COMPILER_BARRIER; +} +static void smp_xmb_lfence() +{ + COMPILER_BARRIER; + ASM_LFENCE; + COMPILER_BARRIER; +} +static void smp_xmb_sfence() +{ + COMPILER_BARRIER; + ASM_SFENCE; + COMPILER_BARRIER; +} +static void smp_xmb_mfence() +{ + COMPILER_BARRIER; + ASM_MFENCE; + COMPILER_BARRIER; +} +static void smp_xmb_locked() +{ + COMPILER_BARRIER; + ASM_LOCKED_BARRIER; + COMPILER_BARRIER; +} + +void (*smp_rmb_p)() = NULL; +void (*smp_wmb_p)() = NULL; +void (*smp_mb_p)() = NULL; + +static void smp_xmb_init_wmb() +{ + if (SMP_X86_USING_NONTEMPORAL && have_sfence) + smp_wmb_p = smp_xmb_sfence; + else if (have_oostore) + smp_wmb_p = smp_xmb_locked; + else + smp_wmb_p = smp_xmb_noop; +} + +static void smp_xmb_init_rmb() +{ + if (SMP_X86_USING_NONTEMPORAL && have_lfence) + smp_rmb_p = smp_xmb_lfence; + else if (have_PentiumPro) + smp_rmb_p = smp_xmb_locked; + else + smp_rmb_p = smp_xmb_noop; +} + +static void smp_xmb_init_mb() +{ + if (SMP_X86_USING_NONTEMPORAL && have_mfence) + smp_mb_p = smp_xmb_mfence; + else + smp_mb_p = smp_xmb_locked; +} + +void smp_mb_init() +{ +#if defined (__x86_64__) + have_lfence = have_sfence = have_mfence = TRUE; + have_x86_xaddl = have_x86_cmpxchgl = TRUE; + have_pentium = TRUE; +#elif defined (__x86_32__) + smp_mb_init_x86_32(); +#else +# error Unimplemented +#endif + + /* initialize barrier indirection vectors */ + smp_xmb_init_rmb(); + smp_xmb_init_wmb(); + smp_xmb_init_mb(); + + /* for single-processor case, reset all to no-op's */ + if (smp_ncpus == 1) + { + smp_wmb_p = smp_xmb_noop; + smp_rmb_p = smp_xmb_noop; + smp_mb_p = smp_xmb_noop; + } + + /* if barriers somehow crash, try to catch it early */ + smp_rmb(); + smp_wmb(); + smp_mb(); +} +#endif + +#if defined (__x86_32__) +/* see for details Intel Architecture Manual (CPUID instruction) as well as: + * http://www.amd.com/us-en/Processors/DevelopWithAMD/0,,30_2252_2272_2274,00.html + * http://www.codeproject.com/KB/system/camel.aspx + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcsample/html/vcsamCPUIDDetermineCPUCapabilities.asp + */ + +typedef struct +{ + SIM_ALIGN_32 uint32 eax; + SIM_ALIGN_32 uint32 ebx; + SIM_ALIGN_32 uint32 ecx; + SIM_ALIGN_32 uint32 edx; +} +x86_regs; + +#define streq(s1, s2) (0 == strcmp((s1), (s2))) + +#if defined(_WIN32) +// NB: should switch from custom code to MSVC intrinsics _cpuid(), _cpuidex() +static t_bool xcpuid(x86_regs* regs) +{ + // use SEH here so we can disable C++ monitoring for processor exceptions other than explicit throw + // and use /EHsc for more optimized compilation instead of /EHsca + BOOL res = FALSE; + __try + { + __asm + { + mov esi, regs + mov eax, [esi]x86_regs.eax + mov ebx, [esi]x86_regs.ebx + mov ecx, [esi]x86_regs.ecx + mov edx, [esi]x86_regs.edx + cpuid + mov [esi]x86_regs.eax, eax + mov [esi]x86_regs.ebx, ebx + mov [esi]x86_regs.ecx, ecx + mov [esi]x86_regs.edx, edx + } + res = TRUE; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } + return res; +} +#elif defined(__linux) || defined(__APPLE__) +static jmp_buf xcpuid_jmpbuf; +static void xcpuid_sig_handler(int sig) +{ + longjmp(xcpuid_jmpbuf, 1); +} + +static void xcpuid_signal_all(sighandler_t handler) +{ + signal(SIGILL, handler); + // signal(SIGSEGV, handler); +} + +static t_bool xcpuid(x86_regs* regs) +{ + volatile t_bool res = FALSE; + + if (setjmp(xcpuid_jmpbuf) == 0) + { + xcpuid_signal_all(xcpuid_sig_handler); + __asm__ __volatile__ ("\n\t" + "movl 0(%%esi), %%eax\n\t" + "movl 4(%%esi), %%ebx\n\t" + "movl 8(%%esi), %%ecx\n\t" + "movl 12(%%esi), %%edx\n\t" + "cpuid\n\t" + "movl %%eax, 0(%%esi)\n\t" + "movl %%ebx, 4(%%esi)\n\t" + "movl %%ecx, 8(%%esi)\n\t" + "movl %%edx, 12(%%esi)\n\t" + : /* no outputs */ + : "S" (regs) + : "eax", "ebx", "ecx", "edx", /*"esi",*/ "cc" + ); + res = TRUE; + } + else + { + res = FALSE; + } + xcpuid_signal_all(SIG_DFL); + return res; +} +#endif + +static void smp_mb_init_x86_32() +{ + x86_regs regs; + t_bool have_mmx = FALSE; + t_bool have_sse = FALSE; + t_bool have_sse2 = FALSE; + t_bool have_amd_sfence = FALSE; + uint32 chipfamily = 0; + uint32 chipmodel = 0; + int maxcode; + char cpuname[13]; + t_bool extsupport = FALSE; + uint32 maxextlevel = 0; + t_bool isIntel = FALSE; + t_bool isAMD = FALSE; + + regs.eax = 0; + if (xcpuid(& regs)) + { + maxcode = regs.eax; + memcpy(cpuname, & regs.ebx, 4); + memcpy(cpuname + 4, & regs.edx, 4); + memcpy(cpuname + 8, & regs.ecx, 4); + cpuname[12] = 0; + } + else + { + maxcode = 0; + cpuname[0] = 0; + } + + if (maxcode == 0) + { + // CPUID is unavailable: i386, early versions of i486, Cyrix/IBM M1 and below, NexGen 586. + // Could try to differentiate between them as in FreeBSD: http://people.freebsd.org/~kato/cpuident.html + // but probably is not worth the effort. Just assume as safe fallback it can be one of the clones with out-of-order store. + have_oostore = TRUE; + return; + } + + have_x86_xaddl = have_x86_cmpxchgl = TRUE; + + if (maxcode >= 1) + { + regs.eax = 1; + if (! xcpuid(& regs)) + panic("Unexpected failure in execution of CPUID instruction"); + if (regs.edx & (1 << 8)) have_cmpxchg8b = TRUE; + // if (regs.edx & (1 << 15)) have_cmov = TRUE; + // if (regs.edx & (1 << 19)) have_clflush = TRUE; + if (regs.edx & (1 << 23)) have_mmx = TRUE; + if (regs.edx & (1 << 25)) have_sse = TRUE; + if (regs.edx & (1 << 26)) have_sse2 = TRUE; + // if (regs.ecx & (1 << 0)) have_sse3 = TRUE; + // if (regs.edx & (1 << 28)) have_hyperthreading = TRUE; + + chipfamily = (regs.eax >> 8) & 0xF; + if (chipfamily == 0xF) chipfamily |= ((regs.eax >> 20) & 0xFF) << 4; + + chipmodel = (regs.eax >> 4) & 0xF; + if (chipmodel == 0xF) chipmodel |= ((regs.eax >> 16) & 0xF) << 4; + } + + // see "Intel Processor Identification and the CPUID Instruction, Application Note 485" + // at http://web.archive.org/web/20090205235617/http://intel.com/Assets/PDF/appnote/241618.pdf + have_pentium = (chipfamily >= 5); + + extsupport = TRUE; + + if (streq(cpuname, "GenuineIntel")) + { + isIntel = TRUE; + extsupport = (chipfamily >= 0xF); + } + else if (streq(cpuname, "AuthenticAMD") || streq(cpuname, "AMD ISBETTER")) + { + isAMD = TRUE; + extsupport = (chipfamily > 5) || (chipfamily == 5 && chipmodel >= 6); + } + else if (streq(cpuname, "CyrixInstead")) + { + extsupport = (chipfamily > 6) || (chipfamily == 5 && chipmodel >= 4) || (chipfamily == 6 && chipmodel >= 5); + } + else if (streq(cpuname, "CentaurHauls")) + { + // IDT + extsupport = (chipfamily > 5) || (chipfamily == 5 && chipmodel >= 8); + } + else if (streq(cpuname, "TransmetaCPU") || streq(cpuname, "GenuineTMx86")) + { + extsupport = (chipfamily >= 5); + } + + /* Some other known maker codes: + * + * "AMDisbetter!" - early engineering samples of AMD K5 processor + * "Geode by NSC" - National Semiconductor + * "NexGenDriven" - NexGen + * "RiseRiseRise" - Rise + * "SiS SiS SiS " - SiS + * "UMC UMC UMC " - UMC + * "VIA VIA VIA " - VIA + */ + + if (extsupport) + { + regs.eax = 0x80000000; + if (xcpuid(& regs)) + { + maxextlevel = regs.eax; + } + else + { + maxextlevel = 0x80000000; + } + } + + if (isAMD && have_mmx && maxextlevel >= 0x80000001) + { + // "AMD Extensions to the 3DNow! and MMX Instruction Sets Manual" + // http://support.amd.com/us/Processor_TechDocs/22466.pdf + regs.eax = 0x80000001; + if (xcpuid(& regs)) + have_amd_sfence = (0 != (regs.edx & (1 << 22))); + else + have_amd_sfence = FALSE; + } + + if (have_sse || have_amd_sfence) have_sfence = TRUE; + if (have_sse2) have_lfence = have_mfence = TRUE; + have_PentiumPro = isIntel && chipfamily == 6 && chipmodel == 1; + + // some x86 clones, such as Cyrix and Centaur, can do out-of-order stores + have_oostore = !isIntel && !isAMD; +} +#endif diff --git a/src/sim_console.cpp b/src/sim_console.cpp new file mode 100644 index 0000000..f379292 --- /dev/null +++ b/src/sim_console.cpp @@ -0,0 +1,1638 @@ +/* sim_console.c: simulator console I/O library + + 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"), + 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. + + 07-Dec-11 MP Added sim_ttisatty to support reasonable behaviour (i.e. + avoid in infinite loop) in the main command input + loop when EOF is detected and input is coming from + a file (or a null device: /dev/null or NUL:) This may + happen when a simulator is running in a background + process. + 17-Apr-11 MP Cleaned up to support running in a background/detached + process + 20-Jan-11 MP Fixed support for BREAK key on Windows to account + for/ignore other keyboard Meta characters. + 18-Jan-11 MP Added log file reference count support + 17-Jan-11 MP Added support for a "Buffered" behaviors which include: + - If Buffering is enabled and Telnet is enabled, a + telnet connection is not required for simulator + operation (instruction execution). + - If Buffering is enabled, all console output is + written to the buffer at all times (deleting the + oldest buffer contents on overflow). + - when a connection is established on the console + telnet port, the whole contents of the Buffer is + presented on the telnet session and connection + will then proceed as if the connection had always + been there. + This concept allows a simulator to run in the background + and when needed a console session to be established. + The "when needed" case usually will be interested in + what already happened before looking to address what + to do, hence the buffer contents being presented. + 28-Dec-10 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 + 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_set_cons_buff - set console buffered + sim_set_cons_unbuff -set console unbuffered + sim_set_cons_log - set console log + sim_set_cons_nolog - set console nolog + sim_show_cons_buff - show console buffered + sim_show_cons_log - show console log + 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_ttisatty - called to determine if running interactively + 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 */ +AUTO_INIT_LOCK(sim_con_lock, /* lock for sim_con_ldsc and sim_con_tmxr */ + SIM_LOCK_CRITICALITY_OS_HI, + DEVLOCK_SPINWAIT_CYCLES); + +extern int32 sim_quiet; +extern SMP_FILE *sim_log, *sim_deb; +extern SMP_FILEREF *sim_log_ref, *sim_deb_ref; + +/* 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_cons_log, 0 }, + { "TELNET", &sim_show_telnet, 0 }, + { "DEBUG", &sim_show_debug, 0 }, + { "BUFFERED", &sim_show_cons_buff, 0 }, + { NULL, NULL, 0 } + }; + +static CTAB set_con_telnet_tab[] = { + { "LOG", &sim_set_cons_log, 0 }, + { "NOLOG", &sim_set_cons_nolog, 0 }, + { "BUFFERED", &sim_set_cons_buff, 0 }, + { "NOBUFFERED", &sim_set_cons_unbuff, 0 }, + { "UNBUFFERED", &sim_set_cons_unbuff, 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 (SMP_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 (SMP_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 (SMP_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]; +t_stat r; + +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 */ +r = sim_open_logfile (gbuf, FALSE, &sim_log, &sim_log_ref); /* open log */ +if (r != SCPE_OK) /* error? */ + return r; +if (!sim_quiet) + smp_printf ("Logging to file \"%s\"\n", + sim_logfile_name (sim_log, sim_log_ref)); +fprintf (sim_log, "Logging to file \"%s\"\n", + sim_logfile_name (sim_log, sim_log_ref)); /* 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) + smp_printf ("Log file closed\n"); +fprintf (sim_log, "Log file closed\n"); +sim_close_logfile (&sim_log_ref); /* close log */ +sim_log = NULL; +return SCPE_OK; +} + +/* Show log status */ + +t_stat sim_show_log (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) + return SCPE_2MARG; +if (sim_log) + fprintf (st, "Logging enabled to \"%s\"\n", + sim_logfile_name (sim_log, sim_log_ref)); +else fprintf (st, "Logging disabled\n"); +return SCPE_OK; +} + +/* Set debug routine */ + +t_stat sim_set_debon (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +t_stat r; + +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; +r = sim_open_logfile (gbuf, FALSE, &sim_deb, &sim_deb_ref); + +if (r != SCPE_OK) + return r; +if (!sim_quiet) + smp_printf ("Debug output to \"%s\"\n", + sim_logfile_name (sim_deb, sim_deb_ref)); +if (sim_log) + fprintf (sim_log, "Debug output to \"%s\"\n", + sim_logfile_name (sim_deb, sim_deb_ref)); +return SCPE_OK; +} + +/* Set nodebug routine */ + +t_stat sim_set_deboff (int32 flag, char *cptr) +{ +t_stat r; + +if (cptr && (*cptr != 0)) /* now eol? */ + return SCPE_2MARG; +if (sim_deb == NULL) /* no log? */ + return SCPE_OK; +r = sim_close_logfile (&sim_deb_ref); +sim_deb = NULL; +if (!sim_quiet) + smp_printf ("Debug output disabled\n"); +if (sim_log) + fprintf (sim_log, "Debug output disabled\n"); +return SCPE_OK; +} + +/* Show debug routine */ + +t_stat sim_show_debug (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) + return SCPE_2MARG; +if (sim_deb) + fprintf (st, "Debug output enabled to \"%s\"\n", + sim_logfile_name (sim_deb, sim_deb_ref)); +else fprintf (st, "Debug output disabled\n"); +return SCPE_OK; +} + +/* SET CONSOLE command */ + +/* Set console to Telnet port (and parameters) */ + +t_stat sim_set_telnet (int32 flg, 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 (isdigit (*gbuf)) { + if (sim_con_tmxr.master) /* already open? */ + sim_set_notelnet (0, NULL); /* close first */ + return tmxr_open_master (&sim_con_tmxr, gbuf); /* open master socket */ + } + else + if (ctptr = find_ctab (set_con_telnet_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; +} + +/* 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 (SMP_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; +} + +/* Set console to Buffering */ + +t_stat sim_set_cons_buff (int32 flg, char *cptr) +{ +char cmdbuf[CBUFSIZE]; + +sprintf(cmdbuf, "BUFFERED%c%s", cptr ? '=' : '\0', cptr ? cptr : ""); +return tmxr_open_master (&sim_con_tmxr, cmdbuf); /* open master socket */ +} + +/* Set console to NoBuffering */ + +t_stat sim_set_cons_unbuff (int32 flg, char *cptr) +{ +char cmdbuf[CBUFSIZE]; + +sprintf(cmdbuf, "UNBUFFERED%c%s", cptr ? '=' : '\0', cptr ? cptr : ""); +return tmxr_open_master (&sim_con_tmxr, cmdbuf); /* open master socket */ +} + +/* Set console to Logging */ + +t_stat sim_set_cons_log (int32 flg, char *cptr) +{ +char cmdbuf[CBUFSIZE]; + +sprintf(cmdbuf, "LOG%c%s", cptr ? '=' : '\0', cptr ? cptr : ""); +return tmxr_open_master (&sim_con_tmxr, cmdbuf); /* open master socket */ +} + +/* Set console to NoLogging */ + +t_stat sim_set_cons_nolog (int32 flg, char *cptr) +{ +char cmdbuf[CBUFSIZE]; + +sprintf(cmdbuf, "NOLOG%c%s", cptr ? '=' : '\0', cptr ? cptr : ""); +return tmxr_open_master (&sim_con_tmxr, cmdbuf); /* open master socket */ +} + +t_stat sim_show_cons_log (SMP_FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) + return SCPE_2MARG; +if (sim_con_tmxr.ldsc->txlog) + fprintf (st, "Log File being written to %s\n", sim_con_tmxr.ldsc->txlogname); +else + fprintf (st, "No Logging\n"); +return SCPE_OK; +} + +t_stat sim_show_cons_buff (SMP_FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) + return SCPE_2MARG; +if (!sim_con_tmxr.buffered) + fprintf (st, "Unbuffered\n"); +else + fprintf (st, "Buffer Size = %d\n", sim_con_tmxr.buffered); +return SCPE_OK; +} + +/* Log File Open/Close/Show Support */ + +/* Open log file */ + +t_stat sim_open_logfile (char *filename, t_bool binary, SMP_FILE **pf, SMP_FILEREF **pref) +{ +char *tptr, gbuf[CBUFSIZE]; + +if ((filename == NULL) || (*filename == 0)) /* too few arguments? */ + return SCPE_2FARG; +tptr = get_glyph (filename, gbuf, 0); +if (*tptr != 0) /* now eol? */ + return SCPE_2MARG; +sim_close_logfile (pref); +*pf = NULL; +if (strcmp (gbuf, "LOG") == 0) { /* output to log? */ + if (sim_log == NULL) /* any log? */ + return SCPE_ARG; + *pf = sim_log; + *pref = sim_log_ref; + if (*pref) + ++(*pref)->refcount; + } +else if (strcmp (gbuf, "DEBUG") == 0) { /* output to debug? */ + if (sim_deb == NULL) /* any debug? */ + return SCPE_ARG; + *pf = sim_deb; + *pref = sim_deb_ref; + if (*pref) + ++(*pref)->refcount; + } +else if (strcmp (gbuf, "STDOUT") == 0) { /* output to stdout? */ + *pf = smp_stdout; + *pref = NULL; + } +else if (strcmp (gbuf, "STDERR") == 0) { /* output to stderr? */ + *pf = smp_stderr; + *pref = NULL; + } +else { + *pref = (SMP_FILEREF*) calloc (1, sizeof(**pref)); + if (!*pref) + return SCPE_MEM; + get_glyph_nc (filename, gbuf, 0); /* reparse */ + strncpy ((*pref)->name, gbuf, sizeof((*pref)->name)-1); + *pf = smp_fopen (gbuf, (binary ? "ab" : "a")); /* open file */ + if (*pf == NULL) { /* error? */ + free (*pref); + *pref = NULL; + return SCPE_OPENERR; + } + (*pref)->file = *pf; + (*pref)->refcount = 1; /* need close */ + } +return SCPE_OK; +} + +/* Close log file */ + +t_stat sim_close_logfile (SMP_FILEREF **pref) +{ +if (NULL == *pref) + return SCPE_OK; +(*pref)->refcount = (*pref)->refcount - 1; +if ((*pref)->refcount > 0) + return SCPE_OK; +fclose ((*pref)->file); +free (*pref); +*pref = NULL; +return SCPE_OK; +} + +/* Show logfile support routine */ + +const char *sim_logfile_name (SMP_FILE *st, SMP_FILEREF *ref) +{ +if (!st) + return ""; +if (st == smp_stdout) + return "STDOUT"; +if (st == smp_stderr) + return "STDERR"; +if (!ref) + return ""; +return ref->name; +} + +/* 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 || sim_con_ldsc.txbfd) /* connected or buffered? */ + { + tmxr_poll_rx (&sim_con_tmxr); /* poll (check disconn) */ + if (sim_con_ldsc.conn || sim_con_ldsc.txbfd) /* still connected? */ + { + if (! sim_con_ldsc.conn) + { + smp_printf ("Running with Buffered Console\r\n"); /* print transition */ + fflush (smp_stdout); + if (sim_log) /* log file? */ + { + fprintf (sim_log, "Running with Buffered Console\n"); + fflush (sim_log); + } + } + 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 */ + { + smp_printf ("Running\r\n"); /* print transition */ + fflush (smp_stdout); + if (sim_log) /* log file? */ + { + fprintf (sim_log, "Running\n"); + fflush (sim_log); + } + } + return SCPE_OK; /* ready to proceed */ + } + c = sim_os_poll_kbd (); /* check for stop char */ + if (c == SCPE_STOP || weak_read(stop_cpus)) + return SCPE_STOP; + if ((i % 10) == 0) /* Status every 10 sec */ + { + smp_printf ("Waiting for console Telnet connection\r\n"); + fflush (smp_stdout); + if (sim_log) /* log file? */ + { + fprintf (sim_log, "Waiting for console Telnet connection\n"); + fflush (sim_log); + } + } + sim_os_sleep (1); /* wait 1 second */ + } + return SCPE_TTMO; /* timed out */ +} + +/* Poll for character */ + +t_stat sim_poll_kbd (t_bool use_console) +{ + int32 c; + + if (use_console) + { + c = sim_os_poll_kbd (); /* get character */ + if (c == SCPE_STOP || sim_con_tmxr.master == 0) /* ^E or not Telnet? */ + return c; /* in-window */ + } + else + { + if (sim_con_tmxr.master == 0) /* not Telnet? */ + return SCPE_OK; + } + + AUTO_LOCK(sim_con_lock); + + if (sim_con_ldsc.conn == 0) /* no Telnet conn? */ + { + if (! sim_con_ldsc.txbfd) /* unbuffered? */ + return SCPE_LOST; /* connection lost */ + if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */ + sim_con_ldsc.rcve = 1; /* rcv enabled */ + else /* fall through to poll reception */ + return SCPE_OK; /* unconnected and buffered - nothing to receive */ + } + 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) +{ + AUTO_LOCK(sim_con_lock); + if (sim_con_tmxr.master == 0) /* not Telnet? */ + { + if (sim_log) /* log file? */ + fputc (c, sim_log); + return sim_os_putchar (c); /* in-window version */ + } + if (sim_log && !sim_con_ldsc.txlog) /* log file, but no line log? */ + fputc (c, sim_log); + if (sim_con_ldsc.conn == 0) /* no Telnet conn? */ + { + if (! sim_con_ldsc.txbfd) /* unbuffered? */ + return SCPE_LOST; /* connection lost */ + if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */ + sim_con_ldsc.rcve = 1; /* rcv enabled */ + } + 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) +{ + AUTO_LOCK(sim_con_lock); + t_stat r; + + if (sim_con_tmxr.master == 0) /* not Telnet? */ + { + if (sim_log) /* log file? */ + fputc (c, sim_log); + return sim_os_putchar (c); /* in-window version */ + } + if (sim_log && !sim_con_ldsc.txlog) /* log file, but no line log? */ + fputc (c, sim_log); + if (sim_con_ldsc.conn == 0) /* no Telnet conn? */ + { + if (!sim_con_ldsc.txbfd) /* non-buffered Telnet conn? */ + return SCPE_LOST; /* lost */ + if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */ + sim_con_ldsc.rcve = 1; /* rcv enabled */ + } + 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; +} + +/* process character received from console keyboard, + can be (c | SCPE_KFLAG) or SCPE_BREAK */ +void sim_con_rcv_char (int32 c) +{ + if (sim_con_tmxr.master == 0 || (c & SCPE_BREAK)) + { + /* no Telnet or BRK (Ctrl/P), forward input to CPU typeahead buffer */ + if (! tti_rcv_char(c)) + { + /* TTI typeahead was full or BRK HALT was rejected: bell */ + sim_os_putchar(7); + } + } +} + +/* 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_bool sim_ttisatty (void) +{ +return isatty (fileno (stdin)); +} + +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 = NULL; +static HANDLE std_output = NULL; +static DWORD saved_mode; + +static BOOL WINAPI +ControlHandler(DWORD dwCtrlType) +{ + extern void int_handler(int sig); + DWORD Mode; + + 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 + /* fall through */ + + 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 && std_input != INVALID_HANDLE_VALUE) /* Not Background process? */ + GetConsoleMode (std_input, &saved_mode); /* Save Mode */ + + return SCPE_OK; +} + +t_stat sim_ttrun (void) +{ + if (std_input && std_input != INVALID_HANDLE_VALUE) /* Not Background process? */ + { + 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); + } + + return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ + if (sim_log) + { + fflush (sim_log); + _setmode (_fileno (sim_log), _O_TEXT); + } + + if (std_input && std_input != INVALID_HANDLE_VALUE && /* Not Background process? */ + !SetConsoleMode(std_input, saved_mode)) /* Restore Normal mode */ + { + return SCPE_TTYERR; + } + + return SCPE_OK; +} + +t_stat sim_ttclose (void) +{ + return SCPE_OK; +} + +t_bool sim_ttisatty (void) +{ + DWORD Mode; + return std_input && std_input != INVALID_HANDLE_VALUE && GetConsoleMode (std_input, &Mode); +} + +t_stat sim_os_poll_kbd (void) +{ + int c; + + if (std_input == NULL || std_input == INVALID_HANDLE_VALUE) /* No keyboard for background processes */ + return SCPE_OK; + + DWORD ev_count = 0; + GetNumberOfConsoleInputEvents(std_input, &ev_count); + + /* ToDo: megre changes from 3.8.2 */ + + if (!_kbhit ()) + { + /* + * Drain non-keyboard events and also keyboard events not consumed by kbhit/getch, + * so console handle is unmarked as signalled. Remove only those events that were + * present in the input queue before kbhit. + */ + while (ev_count) + { + INPUT_RECORD ir[10]; + DWORD nread; + DWORD nrd = 10; + if (ev_count < nrd) nrd = ev_count; + ReadConsoleInput(std_input, ir, nrd, & nread); + ev_count -= nrd; + } + + 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) +{ + DWORD unused; + + if (c != 0177) + WriteConsoleA(std_output, &c, 1, &unused, NULL); + return SCPE_OK; +} + +class smp_pollable_console_keyboard_impl : public smp_pollable_console_keyboard +{ +private: + HANDLE hConsoleInput; +public: + smp_pollable_console_keyboard_impl() + { + hConsoleInput = GetStdHandle(STD_INPUT_HANDLE); + } + virtual ~smp_pollable_console_keyboard_impl() + { + if (hConsoleInput != INVALID_HANDLE_VALUE) + CloseHandle(hConsoleInput); + } + smp_pollable_handle_t pollable_handle() + { + return hConsoleInput; + } + virtual const char* pollable_handle_op() + { + return "h"; + } + + void clear() {} + void wait() {} + t_bool trywait() { return FALSE; } + void release(int count = 1) {} +}; + +smp_pollable_console_keyboard* smp_pollable_console_keyboard::instance = NULL; +smp_pollable_console_keyboard* smp_pollable_console_keyboard::get() +{ + if (instance == NULL) + instance = new smp_pollable_console_keyboard_impl(); + return instance; +} + +/* 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_bool sim_ttisatty (void) +{ +return 1; +} + +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__) + smp_putchar (c); +#else + putch (c); +#endif + fflush (smp_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(smp_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_bool sim_ttisatty (void) +{ +return 1; +} + +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) { + smp_putchar (c); + fflush (smp_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; + return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ + 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_bool sim_ttisatty (void) +{ + return isatty (0); +} + +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 = out; + write (1, &c, 1); + return SCPE_OK; +} + +/* POSIX UNIX routines, from Leendert Van Doorn */ + +#else + +#include +#include + +static struct termios cmdtty, runtty; + +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; + return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ + if (!isatty (fileno (stdin))) /* skip if !tty */ + return SCPE_OK; + if (tcsetattr (0, TCSAFLUSH, &cmdtty) < 0) + return SCPE_TTIERR; + return SCPE_OK; +} + +t_bool sim_ttisatty(void) +{ + return isatty (fileno (stdin)); +} + +t_stat sim_ttclose (void) +{ + return sim_ttcmd (); +} + +t_stat sim_os_poll_kbd (void) +{ + DECL_RESTARTABLE(rc); + unsigned char buf[1]; + + switch (smp_wait(smp_pollable_console_keyboard::get(), 0)) + { + case 0: return SCPE_OK; + case -1: return SCPE_STOP; + default: break; + } + + DO_RESTARTABLE(rc, read (0, buf, 1)); + if (rc == -1 && errno == EWOULDBLOCK) return SCPE_OK; + if (rc == 0) return SCPE_OK; + if (rc != 1) return SCPE_STOP; + 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) +{ + DECL_RESTARTABLE(rc); + char c = out; + DO_RESTARTABLE(rc, write (1, &c, 1)); + return SCPE_OK; +} + +class smp_pollable_console_keyboard_impl : public smp_pollable_console_keyboard +{ +public: + smp_pollable_console_keyboard_impl() {} + virtual ~smp_pollable_console_keyboard_impl() {} + smp_pollable_handle_t pollable_handle() + { return 0; } + virtual const char* pollable_handle_op() + { return "r"; } + void clear() {} + void wait() {} + t_bool trywait() { return FALSE; } + void release(int count = 1) {} +}; + +smp_pollable_console_keyboard* smp_pollable_console_keyboard::instance = NULL; +smp_pollable_console_keyboard* smp_pollable_console_keyboard::get() +{ + if (instance == NULL) + instance = new smp_pollable_console_keyboard_impl(); + return instance; +} + +#endif diff --git a/src/sim_console.h b/src/sim_console.h new file mode 100644 index 0000000..b9e7916 --- /dev/null +++ b/src/sim_console.h @@ -0,0 +1,93 @@ +/* sim_console.h: simulator console I/O library headers + + 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. + + 17-Jan-11 MP Added buffered line capabilities + 22-Jun-06 RMS Implemented SET/SHOW PCHAR + 22-Nov-05 RMS Added central input/output conversion support + 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy + 28-May-04 RMS Added SET/SHOW CONSOLE + 02-Jan-04 RMS Removed timer routines, added Telnet console routines +*/ + +#ifndef _SIM_CONSOLE_H_ +#define _SIM_CONSOLE_H_ 0 + +#define TTUF_V_MODE (UNIT_V_UF + 0) +#define TTUF_W_MODE 2 +#define TTUF_MODE_7B 0 +#define TTUF_MODE_8B 1 +#define TTUF_MODE_UC 2 +#define TTUF_MODE_7P 3 +#define TTUF_KSR (1u << TTUF_W_MODE) +#define TTUF_M_MODE ((1u << TTUF_W_MODE) - 1) +#define TTUF_V_UF (TTUF_V_MODE + TTUF_W_MODE) +#define TT_MODE (TTUF_M_MODE << TTUF_V_MODE) +#define TT_MODE_7B (TTUF_MODE_7B << TTUF_V_MODE) +#define TT_MODE_8B (TTUF_MODE_8B << TTUF_V_MODE) +#define TT_MODE_UC (TTUF_MODE_UC << TTUF_V_MODE) +#define TT_MODE_7P (TTUF_MODE_7P << TTUF_V_MODE) +#define TT_MODE_KSR (TT_MODE_UC) +#define TT_GET_MODE(x) (((x) >> TTUF_V_MODE) & TTUF_M_MODE) + +t_stat sim_set_console (int32 flag, char *cptr); +t_stat sim_set_kmap (int32 flag, char *cptr); +t_stat sim_set_telnet (int32 flag, char *cptr); +t_stat sim_set_notelnet (int32 flag, char *cptr); +t_stat sim_set_logon (int32 flag, char *cptr); +t_stat sim_set_logoff (int32 flag, char *cptr); +t_stat sim_set_debon (int32 flag, char *cptr); +t_stat sim_set_deboff (int32 flag, char *cptr); +t_stat sim_set_cons_buff (int32 flg, char *cptr); +t_stat sim_set_cons_unbuff (int32 flg, char *cptr); +t_stat sim_set_cons_log (int32 flg, char *cptr); +t_stat sim_set_cons_nolog (int32 flg, char *cptr); +t_stat sim_set_pchar (int32 flag, char *cptr); +t_stat sim_show_console (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_kmap (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_telnet (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_log (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_debug (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_pchar (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_cons_buff (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_cons_log (SMP_FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_check_console (int32 sec); +t_stat sim_open_logfile (char *filename, t_bool binary, SMP_FILE **pf, SMP_FILEREF **pref); +t_stat sim_close_logfile (SMP_FILEREF **pref); +const char *sim_logfile_name (SMP_FILE *st, SMP_FILEREF *ref); +t_stat sim_poll_kbd (t_bool use_console); +t_stat sim_putchar (int32 c); +t_stat sim_putchar_s (int32 c); +t_stat sim_ttinit (void); +t_stat sim_ttrun (void); +t_stat sim_ttcmd (void); +t_stat sim_ttclose (void); +t_bool sim_ttisatty (void); +t_stat sim_os_poll_kbd (void); +t_stat sim_os_putchar (int32 out); +int32 sim_tt_inpcvt (int32 c, uint32 mode); +int32 sim_tt_outcvt (int32 c, uint32 mode); +void sim_con_rcv_char (int32 c); + +#endif diff --git a/src/sim_defs.h b/src/sim_defs.h new file mode 100644 index 0000000..e8fdc95 --- /dev/null +++ b/src/sim_defs.h @@ -0,0 +1,1833 @@ +/* sim_defs.h: simulator definitions + + 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. + + 05-Jan-11 MP Added Asynch I/O support (SPO: modified for VAX MP) + 18-Jan-11 MP Added log file reference count support + 21-Jul-08 RMS Removed inlining support + 28-May-08 RMS Added inlining support + 28-Jun-07 RMS Added IA64 VMS support (from Norm Lastovica) + 18-Jun-07 RMS Added UNIT_IDLE flag + 18-Mar-07 RMS Added UNIT_TEXT flag + 07-Mar-07 JDB Added DEBUG_PRJ macro + 18-Oct-06 RMS Added limit check for clock synchronized keyboard waits + 13-Jul-06 RMS Guarantee CBUFSIZE is at least 256 + 07-Jan-06 RMS Added support for breakpoint spaces + Added REG_FIT flag + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 11-Mar-05 RMS Moved 64b data type definitions outside USE_INT64 + 07-Feb-05 RMS Added assertion fail stop + 05-Nov-04 RMS Added support for SHOW opt=val + 20-Oct-04 RMS Converted all base types to typedefs + 21-Sep-04 RMS Added switch to flag stop message printout + 06-Feb-04 RMS Moved device and unit user flags fields (V3.2) + RMS Added REG_VMAD + 29-Dec-03 RMS Added output stall status + 15-Jun-03 RMS Added register flag REG_VMIO + 23-Apr-03 RMS Revised for 32b/64b t_addr + 14-Mar-03 RMS Lengthened default serial output wait + 31-Mar-03 RMS Added u5, u6 fields + 18-Mar-03 RMS Added logical name support + Moved magtape definitions to sim_tape.h + Moved breakpoint definitions from scp.c + 03-Mar-03 RMS Added sim_fsize + 08-Feb-03 RMS Changed sim_os_sleep to void, added match_ext + 05-Jan-03 RMS Added hidden switch definitions, device dyn memory support, + parameters for function pointers, case sensitive SET support + 22-Dec-02 RMS Added break flag + 08-Oct-02 RMS Increased simulator error code space + Added Telnet errors + Added end of medium support + Added help messages to CTAB + Added flag and context fields to DEVICE + Added restore flag masks + Revised 64b definitions + 02-May-02 RMS Removed log status codes + 22-Apr-02 RMS Added magtape record length error + 30-Dec-01 RMS Generalized timer package, added circular arrays + 07-Dec-01 RMS Added breakpoint package + 01-Dec-01 RMS Added read-only unit support, extended SET/SHOW features, + improved error messages + 24-Nov-01 RMS Added unit-based registers + 27-Sep-01 RMS Added queue count prototype + 17-Sep-01 RMS Removed multiple console support + 07-Sep-01 RMS Removed conditional externs on function prototypes + 31-Aug-01 RMS Changed int64 to t_int64 for Windoze + 17-Jul-01 RMS Added additional function prototypes + 27-May-01 RMS Added multiple console support + 15-May-01 RMS Increased string buffer size + 25-Feb-01 RMS Revisions for V2.6 + 15-Oct-00 RMS Editorial revisions for V2.5 + 11-Jul-99 RMS Added unsigned int data types + 14-Apr-99 RMS Converted t_addr to unsigned + 04-Oct-98 RMS Additional definitions for V2.4 + + The interface between the simulator control package (SCP) and the + simulator consists of the following routines and data structures + + sim_name simulator name string + sim_devices[] array of pointers to simulated devices + sim_PC pointer to saved PC register descriptor + sim_interval simulator interval to next event + sim_stop_messages[] array of pointers to stop messages + sim_instr() instruction execution routine + sim_load() binary loader routine + sim_emax maximum number of words in an instruction + + In addition, the simulator must supply routines to print and parse + architecture specific formats + + print_sym print symbolic output + parse_sym parse symbolic input +*/ + +// #pragma message ("Loading sim_defs.h") + +#ifndef _SIM_DEFS_H_ +#define _SIM_DEFS_H_ 0 + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(___llvm__) +# message "LLVM compiler is suboptimal for SIMH. Use regular GCC if possible." +#endif + +#if defined(__linux) +# define __STDC_LIMIT_MACROS +# define __STDC_FORMAT_MACROS +# include +# include +#elif defined(__APPLE__) +# include +# include +#endif + +#if defined(_WIN32) && !defined(PRIu64) +# define PRIu64 "I64u" +#endif + +#ifdef _WIN32 +# pragma warning(disable:4996) +#endif + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +#ifndef __ORDER_LITTLE_ENDIAN__ +# define __ORDER_LITTLE_ENDIAN__ 1234 +#endif + +#ifndef __ORDER_BIG_ENDIAN__ +# define __ORDER_BIG_ENDIAN__ 4321 +#endif + +#if !defined(UINT64_MAX) && defined(_UI64_MAX) +# define UINT64_MAX _UI64_MAX +#endif + +#if !defined(UINT32_MAX) && defined(UINT_MAX) +# define UINT32_MAX UINT_MAX +#endif + +#if !defined(INT32_MAX) && defined(INT_MAX) +# define INT32_MAX INT_MAX +#endif + +/* suppress compiler warning message when code does not have return statement + after invoking a function that never returns */ +#define never_returns_bool_t return FALSE +#define never_returns_int32 return 0 + +#ifndef __SIZEOF_POINTER__ +# if defined(_WIN64) +# define __SIZEOF_POINTER__ 8 +# elif defined(_WIN32) +# define __SIZEOF_POINTER__ 4 +# else +# error Unimplemented +# endif +#endif + +#ifdef _WIN32 +# define SIM_INLINE __forceinline +#elif defined(__GNUC__) +# define SIM_INLINE inline __attribute__ ((__always_inline__)) +#else +# define SIM_INLINE inline +#endif + +#if defined(__GNUC__) +# define likely(x) __builtin_expect((x), TRUE) +# define unlikely(x) __builtin_expect((x), FALSE) +#else +# define likely(x) (x) +# define unlikely(x) (x) +#endif + +#if !defined(USE_CLOCK_THREAD) +# if defined(_WIN32) || defined (__linux) || defined(__APPLE__) +# define USE_CLOCK_THREAD TRUE +# else +# define USE_CLOCK_THREAD FALSE +# endif +#endif + +#if defined(_WIN32) +# define streqi(a, b) (0 == stricmp((a), (b))) +#elif defined(__linux) || defined(__APPLE__) +# include +# define streqi(a, b) (0 == strcasecmp((a), (b))) +#endif + +#if (PATH_MAX >= 128) +# define CBUFSIZE (128 + PATH_MAX) /* string buf size */ +#else +# define CBUFSIZE 256 +#endif + +/* thread-interlocked file access */ +class smp_file_critical_section; + +class SMP_FILE +{ +public: + FILE* stream; + smp_file_critical_section* lock_cs; +}; + +extern SMP_FILE* smp_stdin; +extern SMP_FILE* smp_stdout; +extern SMP_FILE* smp_stderr; +extern SMP_FILE* sim_log; + +struct sim_smp_fileref +{ + char name[CBUFSIZE]; /* file name */ + SMP_FILE *file; /* file handle */ + int refcount; /* reference count */ +}; +typedef struct sim_smp_fileref SMP_FILEREF; + +/* forward declarations */ +class sim_unit; +typedef sim_unit UNIT; +class CPU_UNIT; +class run_scope_context; +#define RUN_DECL CPU_UNIT* cpu_unit +#define RUN_PASS cpu_unit +#define RUN_PASS_NULL NULL +#define RUN_RSCX_DECL CPU_UNIT* cpu_unit, run_scope_context* rscx +#define RUN_RSCX_PASS cpu_unit, rscx + +int smp_printf(const char* fmt, ...); +int fprintf(SMP_FILE* stream, const char* fmt, ...); +int fflush(SMP_FILE* stream); +int fscanf(SMP_FILE* stream, const char* fmt, ...); +int fgetc(SMP_FILE *stream); +char *fgets(char *string, int n, SMP_FILE *stream); +size_t fread(void *buffer, size_t size, size_t count, SMP_FILE *stream); +int fputc(int c, SMP_FILE *stream); +int fputs(const char *string, SMP_FILE *stream); +size_t fwrite(const void *buffer, size_t size, size_t count, SMP_FILE *stream); +int fseek(SMP_FILE* stream, long offset, int origin); +int fsetpos(SMP_FILE *stream, const fpos_t *pos); +void rewind(SMP_FILE *stream); +int vfprintf(SMP_FILE *stream, const char *format, va_list argptr); +long ftell(SMP_FILE* stream); +int ferror(SMP_FILE* stream); +int feof(SMP_FILE* stream); +int getc(SMP_FILE *stream); +int putc(int c, SMP_FILE *stream); +int setvbuf(SMP_FILE *stream, char *buffer, int mode, size_t size); +void clearerr(SMP_FILE *stream); +int fclose(SMP_FILE *stream); +int fgetpos(SMP_FILE *stream, fpos_t *pos); +void smp_perror(const char *string); +int _fileno(SMP_FILE* stream); +int smp_putchar(int c); +SMP_FILE *smp_fopen(const char* filename, const char* mode); +SMP_FILE *smp_fopen64(const char* filename, const char* mode); +SMP_FILE *smp_file_wrap(FILE* fp); +#if defined (__linux) +int fseeko64(SMP_FILE *sfd, off64_t offset, int whence); +off64_t ftello64(SMP_FILE *sfd); +#elif defined (__APPLE__) || defined (__FreeBSD__) +int fseeko(SMP_FILE *sfd, off_t offset, int whence); +off_t ftello(SMP_FILE *sfd); +#endif +void panic(const char* cause); +void* operator_new_aligned(size_t size, size_t alignment); +void operator_delete_aligned(void* p); + +/* alignment and padding for structures and their fields */ +#if defined(_WIN32) +# define SIM_ALIGN_8 __declspec(align(1)) +# define SIM_ALIGN_16 __declspec(align(2)) +# define SIM_ALIGN_32 __declspec(align(4)) +# define SIM_ALIGN_64 __declspec(align(8)) +#elif defined(__GNUC__) +# define SIM_ALIGN_8 __attribute__ ((__aligned__ (1))) __attribute__ ((packed)) +# define SIM_ALIGN_16 __attribute__ ((__aligned__ (2))) +# define SIM_ALIGN_32 __attribute__ ((__aligned__ (4))) +# define SIM_ALIGN_64 __attribute__ ((__aligned__ (8))) +#else +# error Unsupported compiler +#endif + +/* Length specific integer declarations */ + +#if defined (VMS) +# include +#else +typedef signed char int8; +typedef signed short int16; +typedef signed int int32; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +#endif + +typedef int t_stat; /* status */ +typedef int t_bool; /* boolean */ +typedef unsigned char t_byte; + +/* 64b integers */ + +#if defined (__GNUC__) /* GCC */ +typedef signed long long t_int64; +typedef unsigned long long t_uint64; +#elif defined (_WIN32) /* Windows */ +typedef signed __int64 t_int64; +typedef unsigned __int64 t_uint64; +#elif (defined (__ALPHA) || defined (__ia64)) && defined (VMS) /* 64b VMS */ +typedef signed __int64 t_int64; +typedef unsigned __int64 t_uint64; +#elif defined (__ALPHA) && defined (__unix__) /* Alpha UNIX */ +typedef signed long t_int64; +typedef unsigned long t_uint64; +#else /* default */ +#define t_int64 signed long long +#define t_uint64 unsigned long long +#endif /* end 64b */ + +#if defined (USE_INT64) /* 64b data */ +typedef t_int64 t_svalue; /* signed value */ +typedef t_uint64 t_value; /* value */ +#else /* 32b data */ +typedef int32 t_svalue; +typedef uint32 t_value; +#endif /* end 64b data */ + +#if __SIZEOF_POINTER__ == 8 +# define SIM_ALIGN_PTR SIM_ALIGN_64 + typedef t_int64 t_addr_off; + typedef t_uint64 t_addr_val; +#else +# define SIM_ALIGN_PTR SIM_ALIGN_32 + typedef int32 t_addr_off; + typedef uint32 t_addr_val; +#endif + +#if defined (USE_INT64) && defined (USE_ADDR64) /* 64b address */ + typedef t_uint64 t_addr; +# define T_ADDR_W 64 +# define SIM_ALIGN_T_ADDR SIM_ALIGN_64 +#else /* 32b address */ + typedef uint32 t_addr; +# define T_ADDR_W 32 +# define SIM_ALIGN_T_ADDR SIM_ALIGN_32 +#endif /* end 64b address */ + +#if defined (USE_INT64) + typedef t_uint64 UINT64; +# define UINT64_EQ(a, b) ((a) == (b)) +# define UINT64_LT(a, b) ((a) < (b)) +# define UINT64_LE(a, b) ((a) <= (b)) +# define UINT64_GT(a, b) ((a) > (b)) +# define UINT64_GE(a, b) ((a) >= (b)) +# define UINT64_SET_ZERO(a) do {(a) = 0; } while (0) +# define UINT64_IS_ZERO(a) ((a) == 0) +# define UINT64_INC(a) (++(a)) +# define UINT64_ZERO_VALUE 0 +# define UINT64_ISMAX(a) ((a) == UINT64_MAX) +# define UINT64_TO_DOUBLE(a) ((double) (a)) +# define UINT64_FROM_UINT32(a64, b32) do { (a64) = (UINT64) (uint32) (b32); } while (0) +# define UINT64_LT_UINT32(a64, b32) ((a64) < (b32)) +# define UINT64_MUL_UINT32(a64, b32) do { (a64) *= (b32); } while (0) +# define UINT64_DIV_UINT32(a64, b32) do { (a64) /= (b32); } while (0) +# define UINT64_TO_UINT32(a64, b32, v) do { if ((a64) <= UINT32_MAX) { (b32) = (uint32) (a64); (v) = TRUE; } else { (v) = FALSE; } } while (0) +#else + typedef struct { uint32 lo; uint32 hi; } UINT64; +# define UINT64_EQ(a, b) ((a).hi == (b).hi && (a).lo == (b).lo) +# define UINT64_GT(a, b) ((a).hi > (b).hi || (a).hi == (b).hi && (a).lo > (b).lo) +# define UINT64_GE(a, b) ((a).hi > (b).hi || (a).hi == (b).hi && (a).lo >= (b).lo) +# define UINT64_LT(a, b) ((a).hi < (b).hi || (a).hi == (b).hi && (a).lo < (b).lo) +# define UINT64_LE(a, b) ((a).hi < (b).hi || (a).hi == (b).hi && (a).lo <= (b).lo) +# define UINT64_SET_ZERO(a) do {(a).hi = 0; (a).lo = 0; } while (0) +# define UINT64_INC(a) do { if ((a).lo++ == 0) (a).hi++; } while (0) +# define UINT64_ZERO_VALUE { 0, 0 } +# define UINT64_IS_ZERO(a) ((a).lo == 0 && (a).hi == 0) +# define UINT64_ISMAX(a) ((a).lo == UINT32_MAX && (a).hi == UINT32_MAX) +# define UINT64_TO_DOUBLE(a) (4294967296.0 * (double) a.hi + (double) a.lo) +# define UINT64_FROM_UINT32(a64, b32) do { (a64).hi = 0; (a64).lo = (b32); } while (0) +# define UINT64_LT_UINT32(a64, b32) ((a64).hi == 0 && (a64).lo < (b32).lo) +# error Implement UINT64_MUL_UINT32 and UINT64_DIV_UINT32 +# define UINT64_TO_UINT32(a64, b32, v) do { if ((a64).hi == 0) { (b32) = (a64).lo; (v) = TRUE; } else { (v) = FALSE; } } while (0) +#endif + +/* min/max helpers */ + +static uint32 SIM_INLINE imax(uint32 a, uint32 b) +{ + return (a > b) ? a : b; +} + +static int32 SIM_INLINE imax(int32 a, int32 b) +{ + return (a > b) ? a : b; +} + +static uint32 SIM_INLINE imin(uint32 a, uint32 b) +{ + return (a < b) ? a : b; +} + +static int32 SIM_INLINE imin(int32 a, int32 b) +{ + return (a < b) ? a : b; +} + + +/* System independent definitions */ + +#define FLIP_SIZE (1 << 16) /* flip buf size */ +#if !defined (PATH_MAX) /* usually in limits */ +#define PATH_MAX 512 +#endif + +/* Breakpoint spaces definitions */ + +#define SIM_BKPT_N_SPC 64 /* max number spaces */ +#define SIM_BKPT_V_SPC 26 /* location in arg */ + +/* Extended switch definitions (bits >= 26) */ + +#define SIM_SW_HIDE (1u << 26) /* enable hiding */ +#define SIM_SW_REST (1u << 27) /* attach/restore */ +#define SIM_SW_REG (1u << 28) /* register value */ +#define SIM_SW_STOP (1u << 29) /* stop message */ + +/* Simulator status codes + + 0 ok + 1 - (SCPE_BASE - 1) simulator specific + SCPE_BASE - n general +*/ + +#define SCPE_OK 0 /* normal return */ +#define SCPE_BASE 64 /* base for messages */ +#define SCPE_NXM (SCPE_BASE + 0) /* nxm */ +#define SCPE_UNATT (SCPE_BASE + 1) /* no file */ +#define SCPE_IOERR (SCPE_BASE + 2) /* I/O error */ +#define SCPE_CSUM (SCPE_BASE + 3) /* loader cksum */ +#define SCPE_FMT (SCPE_BASE + 4) /* loader format */ +#define SCPE_NOATT (SCPE_BASE + 5) /* not attachable */ +#define SCPE_OPENERR (SCPE_BASE + 6) /* open error */ +#define SCPE_MEM (SCPE_BASE + 7) /* alloc error */ +#define SCPE_ARG (SCPE_BASE + 8) /* argument error */ +#define SCPE_STEP (SCPE_BASE + 9) /* step expired */ +#define SCPE_UNK (SCPE_BASE + 10) /* unknown command */ +#define SCPE_RO (SCPE_BASE + 11) /* read only */ +#define SCPE_INCOMP (SCPE_BASE + 12) /* incomplete */ +#define SCPE_STOP (SCPE_BASE + 13) /* sim stopped */ +#define SCPE_EXIT (SCPE_BASE + 14) /* sim exit */ +#define SCPE_TTIERR (SCPE_BASE + 15) /* console tti err */ +#define SCPE_TTOERR (SCPE_BASE + 16) /* console tto err */ +#define SCPE_EOF (SCPE_BASE + 17) /* end of file */ +#define SCPE_REL (SCPE_BASE + 18) /* relocation error */ +#define SCPE_NOPARAM (SCPE_BASE + 19) /* no parameters */ +#define SCPE_ALATT (SCPE_BASE + 20) /* already attached */ +#define SCPE_TIMER (SCPE_BASE + 21) /* hwre timer err */ +#define SCPE_SIGERR (SCPE_BASE + 22) /* signal err */ +#define SCPE_TTYERR (SCPE_BASE + 23) /* tty setup err */ +#define SCPE_SUB (SCPE_BASE + 24) /* subscript err */ +#define SCPE_NOFNC (SCPE_BASE + 25) /* func not imp */ +#define SCPE_UDIS (SCPE_BASE + 26) /* unit disabled */ +#define SCPE_NORO (SCPE_BASE + 27) /* rd only not ok */ +#define SCPE_INVSW (SCPE_BASE + 28) /* invalid switch */ +#define SCPE_MISVAL (SCPE_BASE + 29) /* missing value */ +#define SCPE_2FARG (SCPE_BASE + 30) /* too few arguments */ +#define SCPE_2MARG (SCPE_BASE + 31) /* too many arguments */ +#define SCPE_NXDEV (SCPE_BASE + 32) /* nx device */ +#define SCPE_NXUN (SCPE_BASE + 33) /* nx unit */ +#define SCPE_NXREG (SCPE_BASE + 34) /* nx register */ +#define SCPE_NXPAR (SCPE_BASE + 35) /* nx parameter */ +#define SCPE_NEST (SCPE_BASE + 36) /* nested DO */ +#define SCPE_IERR (SCPE_BASE + 37) /* internal error */ +#define SCPE_MTRLNT (SCPE_BASE + 38) /* tape rec lnt error */ +#define SCPE_LOST (SCPE_BASE + 39) /* Telnet conn lost */ +#define SCPE_TTMO (SCPE_BASE + 40) /* Telnet conn timeout */ +#define SCPE_STALL (SCPE_BASE + 41) /* Telnet conn stall */ +#define SCPE_AFAIL (SCPE_BASE + 42) /* assert failed */ +#define SCPE_SWSTP (SCPE_BASE + 43) /* cannot step because syncw limit reached */ + +#define SCPE_MAX_ERR (SCPE_BASE + 43) /* Maximum SCPE Error Value */ + +#define SCPE_KFLAG 0010000 /* tti data flag */ +#define SCPE_BREAK 0020000 /* tti break flag */ + +/* Print value format codes */ + +#define PV_RZRO 0 /* right, zero fill */ +#define PV_RSPC 1 /* right, space fill */ +#define PV_LEFT 2 /* left justify */ + +/* Default timing parameters */ + +#define KBD_POLL_WAIT 5000 /* keyboard poll */ +#define KBD_MAX_WAIT 500000 +#define SERIAL_IN_WAIT 100 /* serial in time */ +#define SERIAL_OUT_WAIT 100 /* serial output */ +#define NOQUEUE_WAIT 10000 /* min check time */ +#define KBD_LIM_WAIT(x) (((x) > KBD_MAX_WAIT)? KBD_MAX_WAIT: (x)) +#define KBD_WAIT(w,s) ((w)? w: KBD_LIM_WAIT (s)) + +/* Convert switch letter to bit mask */ + +#define SWMASK(x) (1u << (((int) (x)) - ((int) 'A'))) + +/* String match */ + +#define MATCH_CMD(ptr,cmd) strncmp ((ptr), (cmd), strlen (ptr)) + +/* Utility classes and functions */ +#include "sim_util.h" + +/* Threading primitives */ +#include "sim_threads.h" + +/* + * try/catch: define USE_C_TRY_CATCH to use try/catch constructs based on setjmp/longjmp + * rather than native C/C++ try-catch. On some systems this may yield better performance, + * however we do not observe this with MSVC/Windows and Linux/GCC/GLIBC. + */ +// #define USE_C_TRY_CATCH +#include "sim_try.h" + +/* Threading primitives, part 2 */ +#include "sim_threads2.h" + +/* Break-to-debugger helper */ + +#if defined(_WIN32) && defined(__x86_32__) +# define sim_DebugBreak() do { __asm {int 3} ; } while (0) +#elif defined(__GNUC__) && (defined(__x86_32__) || defined(__x86_64__)) +# define sim_DebugBreak() do { __asm__("int3"); } while (0) +#else +extern void sim_DebugBreak(); +#endif + +/* Helpers for execution/CPU context */ + +extern CPU_UNIT cpu_unit_0; + +class run_scope_context +{ +public: + CPU_UNIT* cpu_unit; /* currently selected VCPU context */ + sim_thread_type_t thread_type; /* thread type: CPU, IOP, CONSOLE */ + smp_thread_t thread_handle; /* thread handle or SMP_THREAD_NULL */ + t_bool thread_handle_valid; /* true if thread_handle is valid */ + uint32 thread_cpu_id; /* for CPU threads only: thread's original context CPU ID */ + int vm_critical_locks; /* count of VM critical locks held by this thread */ + int os_hi_critical_locks; /* count of OS_HI critical locks held by this thread */ + + /* the following cells are used only for CONSOLE and IOP threads */ + sim_thread_priority_t bprio; /* thread base priority */ + sim_thread_priority_t cprio; /* thread current priority */ + t_bool reevaluating_prio; /* inside sim_reevaluate_noncpu_thread_priority */ + + run_scope_context() + { + cpu_unit = NULL; + thread_type = SIM_THREAD_TYPE_CONSOLE; + thread_handle = SMP_THREAD_NULL; + thread_handle_valid = FALSE; + thread_cpu_id = (uint32) -1; + vm_critical_locks = 0; + os_hi_critical_locks = 0; + bprio = SIMH_THREAD_PRIORITY_INVALID; + cprio = SIMH_THREAD_PRIORITY_INVALID; + reevaluating_prio = FALSE; + } + + run_scope_context(CPU_UNIT* cpu_unit) + { + this->cpu_unit = cpu_unit; + this->thread_type = SIM_THREAD_TYPE_CONSOLE; + thread_handle = SMP_THREAD_NULL; + thread_handle_valid = FALSE; + thread_cpu_id = (uint32) -1; + vm_critical_locks = 0; + os_hi_critical_locks = 0; + bprio = SIMH_THREAD_PRIORITY_INVALID; + cprio = SIMH_THREAD_PRIORITY_INVALID; + reevaluating_prio = FALSE; + } + + run_scope_context(CPU_UNIT* cpu_unit, sim_thread_type_t thread_type) + { + this->cpu_unit = cpu_unit; + this->thread_type = thread_type; + thread_handle = SMP_THREAD_NULL; + thread_handle_valid = FALSE; + thread_cpu_id = (uint32) -1; + vm_critical_locks = 0; + os_hi_critical_locks = 0; + bprio = SIMH_THREAD_PRIORITY_INVALID; + cprio = SIMH_THREAD_PRIORITY_INVALID; + reevaluating_prio = FALSE; + } + + run_scope_context(CPU_UNIT* cpu_unit, sim_thread_type_t thread_type, smp_thread_t thread_handle) + { + this->cpu_unit = cpu_unit; + this->thread_type = thread_type; + this->thread_handle = thread_handle; + thread_handle_valid = TRUE; + thread_cpu_id = (uint32) -1; + vm_critical_locks = 0; + os_hi_critical_locks = 0; + bprio = SIMH_THREAD_PRIORITY_INVALID; + cprio = SIMH_THREAD_PRIORITY_INVALID; + reevaluating_prio = FALSE; + } + + void set_current(); + void set_thread_type(sim_thread_type_t thread_type) { this->thread_type = thread_type; } + static void set_current(run_scope_context* rscx); + static run_scope_context* get_current(); + + void setting_priority(sim_thread_priority_t prio) + { + if (this != NULL && + (thread_type == SIM_THREAD_TYPE_CONSOLE || thread_type == SIM_THREAD_TYPE_IOP) && + !reevaluating_prio) + { + cprio = bprio = prio; + } + } +}; + +#define RUN_SCOPE \ + CPU_UNIT* cpu_unit = NULL; \ + do \ + { \ + run_scope_context* rscx = run_scope_context::get_current(); \ + cpu_unit = rscx ? rscx->cpu_unit : NULL; \ + } \ + while (0); +#define RUN_SCOPE_RSCX \ + run_scope_context* rscx = run_scope_context::get_current(); \ + CPU_UNIT* cpu_unit = rscx ? rscx->cpu_unit : NULL; +#define RUN_SCOPE_RSCX_ONLY \ + run_scope_context* rscx = run_scope_context::get_current(); + +#define RUN_SVC_DECL RUN_DECL + +#define sim_ncpus (cpu_dev.numunits) /* number of processors configured in the system */ + +class sim_reg; + +/* Device data structure */ + +class sim_device { +public: + const char *name; /* name */ + sim_unit **units; /* units (array of pointers to UNIT, size numunits) */ + sim_reg *registers; /* registers */ + struct sim_mtab *modifiers; /* modifiers */ + SIM_ALIGN_32 uint32 numunits; /* #units */ + uint32 aradix; /* address radix */ + uint32 awidth; /* address width */ + uint32 aincr; /* addr increment */ + uint32 dradix; /* data radix */ + uint32 dwidth; /* data width */ + t_stat (*examine)(t_value *v, t_addr a, sim_unit *up, + int32 sw); /* examine routine */ + t_stat (*deposit)(t_value v, t_addr a, sim_unit *up, + int32 sw); /* deposit routine */ + t_stat (*reset)(sim_device *dp);/* reset routine */ + t_stat (*boot)(int32 u, sim_device *dp); + /* boot routine */ + t_stat (*attach)(sim_unit *up, char *cp); + /* attach routine */ + t_stat (*detach)(sim_unit *up); /* detach routine */ + void *ctxt; /* context */ + uint32 flags; /* flags */ + uint32 dctrl; /* debug control */ + struct sim_debtab *debflags; /* debug flags */ + t_stat (*msize)(sim_unit *up, int32 v, char *cp, void *dp); + /* mem size routine */ + char *lname; /* logical name */ + uint32 a_reset_count; /* number of resets on this device (used by ASYNCH_IO) */ +}; +typedef sim_device DEVICE; + +extern DEVICE *sim_devices[]; + +/* Device flags */ + +#define DEV_V_DIS 0 /* dev disabled */ +#define DEV_V_DISABLE 1 /* dev disable-able */ +#define DEV_V_DYNM 2 /* mem size dynamic */ +#define DEV_V_NET 3 /* network attach */ +#define DEV_V_DEBUG 4 /* debug capability */ +#define DEV_V_RAW 5 /* raw supported */ +#define DEV_V_RAWONLY 6 /* only raw supported */ +#define DEV_V_PERCPU 7 /* device and its units are per-CPU */ +#define DEV_V_UF_31 12 /* user flags, V3.1 */ +#define DEV_V_UF 16 /* user flags */ +#define DEV_V_RSV 31 /* reserved */ + +#define DEV_DIS (1 << DEV_V_DIS) +#define DEV_DISABLE (1 << DEV_V_DISABLE) +#define DEV_DYNM (1 << DEV_V_DYNM) +#define DEV_NET (1 << DEV_V_NET) +#define DEV_DEBUG (1 << DEV_V_DEBUG) +#define DEV_RAW (1 << DEV_V_RAW) +#define DEV_RAWONLY (1 << DEV_V_RAWONLY) +#define DEV_PERCPU (1 << DEV_V_PERCPU) + +#define DEV_UFMASK_31 (((1u << DEV_V_RSV) - 1) & ~((1u << DEV_V_UF_31) - 1)) +#define DEV_UFMASK (((1u << DEV_V_RSV) - 1) & ~((1u << DEV_V_UF) - 1)) +#define DEV_RFLAGS (DEV_UFMASK|DEV_DIS) /* restored flags */ + +/* Unit data structure + + Parts of the unit structure are device specific, that is, they are + not referenced by the simulator control package and can be freely + used by device simulators. Fields starting with 'buf', and flags + starting with 'UF', are device specific. The definitions given here + are for a typical sequential device. +*/ + +/* Unit flags */ +#define UNIT_V_UF_31 12 /* dev spec, V3.1 */ +#define UNIT_V_UF 16 /* device specific */ +#define UNIT_V_RSV 31 /* reserved!! */ + +#define UNIT_ATTABLE 000001 /* attachable */ +#define UNIT_RO 000002 /* read only */ +#define UNIT_FIX 000004 /* fixed capacity */ +#define UNIT_SEQ 000010 /* sequential */ +#define UNIT_ATT 000020 /* attached */ +#define UNIT_BINK 000040 /* K = power of 2 */ +#define UNIT_BUFABLE 000100 /* bufferable */ +#define UNIT_MUSTBUF 000200 /* must buffer */ +#define UNIT_BUF 000400 /* buffered */ +#define UNIT_ROABLE 001000 /* read only ok */ +#define UNIT_DISABLE 002000 /* disable-able */ +#define UNIT_DIS 004000 /* disabled */ +#define UNIT_RAW 010000 /* raw mode */ +#define UNIT_TEXT 020000 /* text mode */ +#define UNIT_IDLE 040000 /* idle eligible */ +#define UNIT_ISCPU 0100000 /* is CPU */ + +#define UNIT_UFMASK_31 (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF_31) - 1)) +#define UNIT_UFMASK (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF) - 1)) +#define UNIT_RFLAGS (UNIT_UFMASK|UNIT_DIS) /* restored flags */ + +/* + * You can subclass sim_unit (as we do for CPU_UNIT), however keep in mind that C++ is not Java. + * In C++ a pointer to a derived class and a pointer to a base class, for the same object, are not guaranteed + * to hold the same address in memory. Often they do, but sometimes they don't, for example when using + * multiple inheritance or virtual inheritance or when Base class does not have virtual functions and vtable, + * but Derived does. In these cases + * + * (void*) (Base*) p != (void*) (Derived*) p + * + * and this can land you in trouble with SIMH code (which is originally a C code and does not expect C++ tricks), + * so you want to avoid it. + * + * Avoid using multiple inheritance or virtual inheritance. Also if you declare a virtual function in a + * subclass of sim_unit, make sure sim_unit declares some virtual function too, so both sim_unit and all its + * subclasses do have vtable and thus for all classes involved fields are uniformly offset from the start of the object + * by the size of vtable pointer. + * + * Finally, you can include consistency check in the initialization code similar to + * + * if ((void*) (CPU_UNIT*) &cpu_unit_0 != (void*) (UNIT*) &cpu_unit_0) + * panic("Broken assumption: CPU_UNIT casts to UNIT with address change"); + * + * You have been warned. + * + */ +class sim_unit +{ +public: + t_stat (*action)(RUN_SVC_DECL, sim_unit *up); /* action routine */ + char *filename; /* open file name */ + SMP_FILE *fileref; /* file reference */ + void *filebuf; /* memory buffer */ + uint32 hwmark; /* high water mark */ + uint32 flags; /* flags */ + t_addr capac; /* capacity */ + t_addr pos; /* file position */ + int32 buf; /* buffer */ + int32 wait; /* wait */ + int32 u3; /* device specific */ + int32 u4; /* device specific */ + int32 u5; /* device specific */ + int32 u6; /* device specific */ + void (*io_flush)(sim_unit* up); /* io flush routine */ + uint32 io_starttime; /* async I/O start time */ + uint32 io_startcpu; /* async I/O start cpu id */ + void *up7; /* device specific */ + void *up8; /* device specific */ + int32 unitno; /* unit number */ + DEVICE* device; /* backpointer to DEVICE */ + CPU_UNIT* clock_queue_cpu; /* pointer to CPU that has this unit in its clock queue, + can be accessed for read or write only when holding device lock, + clock_queue_cpu is not used per-CPU devices */ + smp_lock* lock; /* lock used to lock the unit (incl. for clock queue operations) or NULL */ + + sim_unit* a_next; /* next asynch active */ + void (*a_check_completion)(sim_unit*); + t_stat (*a_activate_call)(sim_unit*, int32); + int32 a_sim_interval; + + sim_unit(t_stat (*action)(RUN_SVC_DECL, sim_unit *up), uint32 flags, t_addr capacity) + { + init_unit_object(action, flags, capacity); + } + + sim_unit(t_stat (*action)(RUN_SVC_DECL, sim_unit *up), uint32 flags, t_addr capacity, int32 wait) + { + init_unit_object(action, flags, capacity); + this->wait = wait; + } + + t_bool is_cpu() + { + return (flags & UNIT_ISCPU) != 0; + } + + void init_unit_object(t_stat (*action)(RUN_SVC_DECL, sim_unit *up), uint32 flags, t_addr capacity) + { + this->action = action; + this->filename = NULL; + this->fileref = NULL; + this->filebuf = NULL; + this->hwmark = 0; + this->flags = flags; + this->capac = capacity; + this->pos = 0; + this->buf = 0; + this->wait = 0; + this->u3 = 0; + this->u4 = 0; + this->u5 = 0; + this->u6 = 0; + this->io_flush = NULL; + this->io_starttime = 0; + this->up7 = 0; + this->up8 = 0; + this->unitno = 0; + this->device = NULL; + this->clock_queue_cpu = NULL; + this->lock = NULL; + this->a_check_completion = NULL; + this->a_activate_call = NULL; + } +}; + +#define sim_unit_index(uptr) ((uptr)->unitno) + +// old: #define UDATA(act,fl,cap) UNIT(NULL,act,NULL,NULL,NULL,0,0,(fl),(cap),0,0) +#define UDATA(act,fl,cap) new UNIT((act), (fl), (cap)) +#define UDATA_SINGLE(act,fl,cap) ((act), (fl), (cap)) +#define UDATA_SINGLE_WAIT(act,fl,cap,wait) ((act), (fl), (cap), (wait)) +#define UNIT_TABLE_SINGLE(udesc) UNIT* udesc##_table [] = {& udesc}; + +#define IS_PERCPU_UNIT(uptr) ((uptr) == &sim_throt_unit || ((uptr)->device->flags & DEV_PERCPU)) +extern UNIT sim_throt_unit; + +/* + * check if unit clock queue entry had been cancelled, + * should be called by xx_svc after acquiring device lock + */ +#define RUN_SVC_CHECK_CANCELLED(uptr) \ + do { \ + if (! IS_PERCPU_UNIT(uptr)) \ + { \ + if ((uptr)->clock_queue_cpu != cpu_unit) \ + return SCPE_OK; \ + (uptr)->clock_queue_cpu = NULL; \ + } \ + } while (0) + +/* Register data structure */ + +class sim_reg; +typedef sim_reg REG; + +class sim_reg +{ +public: + char *name; /* name */ + struct + { + char loctype; /* location type */ + SIM_ALIGN_32 + int32 loc_ix1; /* for REG_LOCTYPE_GBL_UNIT, loc_unit_index */ + t_addr loc_a1; /* for REG_LOCTYPE_GBL_UNIT, loc_unit_fielf_offset */ + t_value (*get_rvalue)(REG* r, uint32 idx); /* routines for REG_LOCTYPE_DYN */ + void (*set_rvalue)(REG* r, uint32 idx, t_value value); /* ... */ + int (*get_vcpu)(REG* r, uint32 idx); /* ... */ + } locinfo; + SIM_ALIGN_32 volatile void* loc; /* location */ + uint32 radix; /* radix */ + uint32 width; /* width */ + uint32 offset; /* starting bit */ + uint32 depth; /* save depth */ + uint32 flags; /* flags */ + uint32 qptr; /* circ q ptr (for loc == REG_LOCTYPE_GBL) */ + t_addr qptr_offset; /* circ q ptr (for loc == REG_LOCTYPE_CPU) */ + + void* getloc(RUN_DECL); + void* getloc_unit_idx(RUN_DECL, uint32 idx); + void setqptr(RUN_DECL, uint32 qptr); + uint32 getqptr(RUN_DECL); +}; + +#define REG_FMT 00003 /* see PV_x */ +#define REG_RO 00004 /* read only */ +#define REG_HIDDEN 00010 /* hidden */ +#define REG_NZ 00020 /* must be non-zero */ +#define REG_UNIT 00040 /* in unit struct */ +#define REG_CIRC 00100 /* circular array */ +#define REG_VMIO 00200 /* use VM data print/parse */ +#define REG_VMAD 00400 /* use VM addr print/parse */ +#define REG_FIT 01000 /* fit access to size */ +#define REG_HRO (REG_RO | REG_HIDDEN) /* hidden, read only */ + +#define REG_LOCTYPE_DYN 'D' /* register is read and writtem via locinfo.get_rvalue and locinfo.set_rvalue */ +#define REG_LOCTYPE_GBL 'G' /* register location: global, loc is direct address */ +#define REG_LOCTYPE_CPU 'C' /* register location: per-cpu, loc is byte offset in CPU_UNIT.cpu_context */ +#define REG_LOCTYPE_GBL_UNIT 'U' /* register location: located in UNIT descriptor itself, + loc is the address of UNIT* pointers array, i.e. of type UNIT**, + loc_unit_index is the index into the array, + loc_unit_fielf_offset is byte offset into UNIT structure */ + +/* Command tables, base and alternate formats */ + +struct sim_ctab +{ + const char *name; /* name */ + t_stat (*action)(int32 flag, char *cptr); + /* action routine */ + int32 arg; /* argument */ + const char *help; /* help string */ +}; + +struct sim_c1tab +{ + const char *name; /* name */ + t_stat (*action)(sim_device *dptr, sim_unit *uptr, + int32 flag, char *cptr); /* action routine */ + int32 arg; /* argument */ + const char *help; /* help string */ +}; + +struct sim_shtab +{ + const char *name; /* name */ + t_stat (*action)(SMP_FILE *st, sim_device *dptr, + sim_unit *uptr, int32 flag, char *cptr); + int32 arg; /* argument */ + const char *help; /* help string */ +}; + +/* Modifier table - only extended entries have disp, reg, or flags */ + +struct sim_mtab +{ + uint32 mask; /* mask */ + uint32 match; /* match */ + const char *pstring; /* print string */ + const char *mstring; /* match string */ + t_stat (*valid)(sim_unit *up, int32 v, char *cp, void *dp); + /* validation routine */ + t_stat (*disp)(SMP_FILE *st, sim_unit *up, int32 v, void *dp); + /* display routine */ + void *desc; /* value descriptor */ + /* REG * if MTAB_VAL */ + /* int * if not */ +}; + +#define MTAB_XTD (1u << UNIT_V_RSV) /* ext entry flag */ +#define MTAB_VDV 001 /* valid for dev */ +#define MTAB_VUN 002 /* valid for unit */ +#define MTAB_VAL 004 /* takes a value */ +#define MTAB_NMO 010 /* only if named */ +#define MTAB_NC 020 /* no UC conversion */ +#define MTAB_SHP 040 /* show takes parameter */ + +/* Search table */ + +struct sim_schtab +{ + int32 logic; /* logical operator */ + int32 boolop; /* boolean operator */ + t_value mask; /* mask for logical */ + t_value comp; /* comparison for boolean */ +}; + +/* Breakpoint table */ + +struct sim_brktab +{ + t_addr addr; /* address */ + int32 typ; /* mask of types */ + int32 cnt; /* proceed count */ + char *act; /* action string */ +}; + +/* Debug table */ + +struct sim_debtab +{ + char *name; /* control name */ + uint32 mask; /* control bit */ +}; + +#define DEBUG_PRS(d) (sim_deb && d.dctrl) +#define DEBUG_PRD(d) (sim_deb && d->dctrl) +#define DEBUG_PRI(d,m) (sim_deb && (d.dctrl & (m))) +#define DEBUG_PRJ(d,m) (sim_deb && (d->dctrl & (m))) + +t_value reg_irdata_dev_rd(REG* r, uint32 idx); +void reg_irdata_dev_wr(REG* r, uint32 idx, t_value value); +int reg_irdata_dev_vcpu(REG* r, uint32 idx); + +t_value reg_irdata_lvl_rd(REG* r, uint32 idx); +void reg_irdata_lvl_wr(REG* r, uint32 idx, t_value value); +int reg_irdata_lvl_vcpu(REG* r, uint32 idx); +t_value ws_min_rd(REG* r, uint32 idx); +void ws_min_wr(REG* r, uint32 idx, t_value value); +t_value ws_max_rd(REG* r, uint32 idx); +void ws_max_wr(REG* r, uint32 idx, t_value value); +t_value ws_lock_rd(REG* r, uint32 idx); +void ws_lock_wr(REG* r, uint32 idx, t_value value); + +/* The following macros define structure contents */ + +#define xxDATA_CPU_OFF(field) ((void*) & ((CPU_UNIT*)0)->cpu_context.field) +#define xxDATA_CPU_ARROFF(field) ((void*) ((CPU_UNIT*)0)->cpu_context.field) +#define xxDATA_UNIT_OFF(field) ((void*) & ((UNIT*)0)->field) +#define HRDATA_CPU(nm,loc,wd) nm, {REG_LOCTYPE_CPU}, xxDATA_CPU_OFF(loc), 16, (wd), 0, 1 +// #define ORDATA_CPU(nm,loc,wd) nm, {REG_LOCTYPE_CPU}, xxDATA_CPU_OFF(loc), 8, (wd), 0, 1 +#define DRDATA_CPU(nm,loc,wd) nm, {REG_LOCTYPE_CPU}, xxDATA_CPU_OFF(loc), 10, (wd), 0, 1 +#define FLDATA_CPU(nm,loc,pos) nm, {REG_LOCTYPE_CPU}, xxDATA_CPU_OFF(loc), 2, 1, (pos), 1 +// #define GRDATA_CPU(nm,loc,rdx,wd,pos) nm, {REG_LOCTYPE_CPU}, xxDATA_CPU_OFF(loc), (rdx), (wd), (pos), 1 +#define BRDATA_CPU(nm,loc,rdx,wd,dep) nm, {REG_LOCTYPE_CPU}, xxDATA_CPU_ARROFF(loc), (rdx), (wd), 0, (dep) +#define BRDATA_CPU_QPTR(qptr_loc) 0, (t_addr) xxDATA_CPU_OFF(qptr_loc) +#define HRDATA_DYN(nm,wd,rd,wr,vcpu) nm, {REG_LOCTYPE_DYN, 0, 0, rd, wr, vcpu}, 0, 16, (wd), 0, 1 + +#if defined (__STDC__) || defined (_WIN32) +#define HRDATA_GBL(nm,loc,wd) #nm, {REG_LOCTYPE_GBL}, &(loc), 16, (wd), 0, 1 +#define HRDATA_GBL_RDX(nm,loc,wd,rdx) #nm, {REG_LOCTYPE_GBL}, &(loc), (rdx), (wd), 0, 1 +#define ORDATA_GBL(nm,loc,wd) #nm, {REG_LOCTYPE_GBL}, &(loc), 8, (wd), 0, 1 +#define DRDATA_GBL(nm,loc,wd) #nm, {REG_LOCTYPE_GBL}, &(loc), 10, (wd), 0, 1 +#define FLDATA_GBL(nm,loc,pos) #nm, {REG_LOCTYPE_GBL}, &(loc), 2, 1, (pos), 1 +#define GRDATA_GBL(nm,loc,rdx,wd,pos) #nm, {REG_LOCTYPE_GBL}, &(loc), (rdx), (wd), (pos), 1 +#define BRDATA_GBL(nm,loc,rdx,wd,dep) #nm, {REG_LOCTYPE_GBL}, (loc), (rdx), (wd), 0, (dep) +#define URDATA_GBL(nm,uarrloc,udx,ufn,rdx,wd,off,dep,fl) \ + #nm, {REG_LOCTYPE_GBL_UNIT, (udx), (t_addr) xxDATA_UNIT_OFF(ufn)}, (uarrloc), (rdx), (wd), (off), (dep), ((fl) | REG_UNIT) +#define IRDATA_DEV(nm, ivcl) #nm, {REG_LOCTYPE_DYN, 0, 0, reg_irdata_dev_rd, reg_irdata_dev_wr, reg_irdata_dev_vcpu}, (void*) (ivcl), 2, 1, 0, 1 +#define IRDATA_LVL(nm, ipl, curr_cpu) #nm, {REG_LOCTYPE_DYN, 0, 0, reg_irdata_lvl_rd, reg_irdata_lvl_wr, reg_irdata_lvl_vcpu}, (void*) (((curr_cpu) << 5) | ipl), 16, 32, 0, 1 +#else +#define HRDATA_GBL(nm,loc,wd) "nm", {REG_LOCTYPE_GBL}, &(loc), 16, (wd), 0, 1 +#define HRDATA_GBL_RDX(nm,loc,wd,rdx) "nm", {REG_LOCTYPE_GBL}, &(loc), (rdx), (wd), 0, 1 +#define ORDATA_GBL(nm,loc,wd) "nm", {REG_LOCTYPE_GBL}, &(loc), 8, (wd), 0, 1 +#define DRDATA_GBL(nm,loc,wd) "nm", {REG_LOCTYPE_GBL}, &(loc), 10, (wd), 0, 1 +#define FLDATA_GBL(nm,loc,pos) "nm", {REG_LOCTYPE_GBL}, &(loc), 2, 1, (pos), 1 +#define GRDATA_GBL(nm,loc,rdx,wd,pos) "nm", {REG_LOCTYPE_GBL}, &(loc), (rdx), (wd), (pos), 1 +#define BRDATA_GBL(nm,loc,rdx,wd,dep) "nm", {REG_LOCTYPE_GBL}, (loc), (rdx), (wd), 0, (dep) +#define URDATA_GBL(nm,uarrloc,udx,ufn,rdx,wd,off,dep,fl) \ + "nm", {REG_LOCTYPE_GBL_UNIT, (udx), (t_addr) xxDATA_UNIT_OFF(ufn)}, (uarrloc), (rdx), (wd), (off), (dep), ((fl) | REG_UNIT) +#define IRDATA_DEV(nm, ivcl) "nm", {REG_LOCTYPE_DYN, 0, 0, reg_irdata_dev_rd, reg_irdata_dev_wr, reg_irdata_dev_vcpu}, (void*) (ivcl), 2, 1, 0, 1 +#define IRDATA_LVL(nm, ipl, curr_cpu) "nm", {REG_LOCTYPE_DYN, 0, 0, reg_irdata_lvl_rd, reg_irdata_lvl_wr, reg_irdata_lvl_vcpu}, (void*) (((curr_cpu) << 5) | ipl), 16, 32, 0, 1 +#endif + +/* Typedefs for principal structures */ + +typedef struct sim_ctab CTAB; +typedef struct sim_c1tab C1TAB; +typedef struct sim_shtab SHTAB; +typedef struct sim_mtab MTAB; +typedef struct sim_schtab SCHTAB; +typedef struct sim_brktab BRKTAB; +typedef struct sim_debtab DEBTAB; + +/* Forward declaration */ + +class InstHistory; + +/* Exception declarations */ + +enum +{ + sim_exception_typeid = 0, + sim_exception_SimError_typeid = 1, + sim_exception_ABORT_typeid = 2 +}; + +class sim_exception +{ +private: + t_bool autoDelete; + uint32 typemask; + +public: + sim_exception() + { + autoDelete = FALSE; + typemask = (1 << sim_exception_typeid); + } + + sim_exception(t_bool autoDelete) + { + this->autoDelete = autoDelete; + typemask = (1 << sim_exception_typeid); + } + + virtual ~sim_exception() + { + } + + void checkAutoDelete() + { + if (autoDelete) delete this; + } + + t_bool isAutoDelete() + { + return autoDelete; + } + + void setAutoDelete(t_bool autoDelete) + { + this->autoDelete = autoDelete; + } + + void setType(int etypeid) + { + typemask |= (1 << etypeid); + } + + t_bool isType(int etypeid) + { + return (typemask & (1 << etypeid)) != 0; + } +}; + +class sim_exception_ABORT : public sim_exception +{ +public: + t_stat code; + sim_exception_ABORT(t_stat code) + { + setType(sim_exception_ABORT_typeid); + this->code = code; + } + + sim_exception_ABORT(t_stat code, t_bool autoDelete) : sim_exception(autoDelete) + { + setType(sim_exception_ABORT_typeid); + this->code = code; + } +}; + +class sim_exception_SimError : public sim_exception +{ +private: + char* msg; + static char* nomem_msg; + sim_exception_SimError(); +public: + sim_exception_SimError(const char* msg) : sim_exception(TRUE) + { + setType(sim_exception_SimError_typeid); + if ((this->msg = dupstr(msg)) == NULL) + this->msg = nomem_msg; + } + + ~sim_exception_SimError() + { + if (msg && msg != nomem_msg) free(msg); + } + + const char* get_message() + { + return msg ? msg : "Unknown error"; + } +}; + +/* clock queue entry */ + +class clock_queue_entry +{ +public: + clock_queue_entry* next; /* link to next entry in the active event queue or in free list */ + UNIT* uptr; /* unit waiting for time event */ + int32 time; /* time out */ + int32 clk_cosched; /* if 0, scheduled at 'time' + if 1, coscheduled with next clock tick + if 2, with clock tick after it, and so on */ +}; + +typedef struct __tag_clock_queue_entry_info +{ + UNIT* uptr; /* unit waiting for time event */ + int32 time; /* time out */ + int32 clk_cosched; /* if 0, scheduled at 'time' + if 1, coscheduled with next clock tick + if 2, with clock tick after it, and so on */ +} +clock_queue_entry_info; + + +/* maximum number of CPUs supported */ +#define SIM_MAX_CPUS 32 + +#if defined (VM_VAX) +#include "vax_cpuctx.h" +#else +class CPU_CONTEXT +{ +public: +}; +#endif + +/* timer declarations */ + +#include "sim_timer.h" + +/* CPU states */ +enum +{ + CPU_STATE_STANDBY = 0, + CPU_STATE_RUNNABLE = 1, + CPU_STATE_RUNNING = 2 +}; + +typedef enum __SynclkPending +{ + SynclkNotPending = 0, + SynclkPendingIE0 = 1, + SynclkPendingIE1 = 2 +} +SynclkPending; + +/* CPU UNIT definition */ + +#define NO_CPU_ID ((uint32) -1) + +class SIM_ALIGN_CACHELINE CPU_UNIT : public UNIT +{ +public: + uint8 cpu_id; /* this CPU ID, ranges from 0 to (SIM_MAX_CPUS - 1) */ + uint8 cpu_state; /* one of CPU_STATE_xxx states */ + + /* current priority of the thread for this VCPU */ + sim_thread_priority_t cpu_thread_priority; + + /* clock queue control */ + SIM_ALIGN_PTR clock_queue_entry* clock_queue; /* active clock queue */ + SIM_ALIGN_PTR clock_queue_entry* clock_queue_freelist; /* lookaside allocation list for clock queue entries */ + + /* time bookkeeping */ + SIM_ALIGN_64 double sim_time; /* per-CPU "global" time */ + SIM_ALIGN_32 uint32 sim_rtime; /* per-CPU "global" time with rollover */ + SIM_ALIGN_32 int32 noqueue_time; /* interval till next clock queue check if there is no entries in the queue */ + + /* step control */ + SIM_ALIGN_32 uint32 sim_step; + + /* instruction counter towards sim_step */ + SIM_ALIGN_32 uint32 sim_instrs; + + /* CPU cycles accrued */ + atomic_uint32_var cpu_adv_cycles; + + /* CPU context */ + SIM_ALIGN_32 CPU_CONTEXT cpu_context; + + /* records pending device interrupts */ + /*SIM_ALIGN_CACHELINE*/ + InterruptRegister cpu_intreg; + + /* cached exception object entry, to avoid allocating each every time */ + SIM_ALIGN_PTR sim_exception_ABORT* cpu_exception_ABORT; + + /* CPU stop code */ + t_stat cpu_stop_code; + + /* flag to cause CPU shutdown */ + t_bool cpu_dostop; + + /* instruction stream history */ + SIM_ALIGN_PTR InstHistory* cpu_hst; + SIM_ALIGN_64 UINT64 cpu_hst_stamp; + uint32 cpu_hst_index; + + /* breakpoint package */ + t_bool sim_brk_pend[SIM_BKPT_N_SPC]; + SIM_ALIGN_T_ADDR t_addr sim_brk_ploc[SIM_BKPT_N_SPC]; + SIM_ALIGN_PTR char* sim_brk_act; + + /* clk_unit is active, used only if use_clock_thread is TRUE */ + t_bool clk_active; + + /* value of cpu_adv_cycles at last SYNCLK event, used only if use_clock_thread is TRUE */ + uint32 cpu_last_synclk_cycles; + + /* value of cpu_adv_cycles at last clock slice tick, used only if use_clock_thread is TRUE */ + uint32 cpu_last_tslice_tick_cycles; + + /* value of cpu_adv_cycles at last second tick, used only if use_clock_thread is TRUE */ + uint32 cpu_last_second_tick_cycles; + + /* clocks calibration */ + int32 cpu_rtc_ticks[SIM_NTIMERS]; /* ticks */ + int32 cpu_rtc_hz[SIM_NTIMERS]; /* tick rate */ + uint32 cpu_rtc_rtime[SIM_NTIMERS]; /* real time */ + uint32 cpu_rtc_vtime[SIM_NTIMERS]; /* virtual time */ + uint32 cpu_rtc_nxintv[SIM_NTIMERS]; /* next interval */ + int32 cpu_rtc_based[SIM_NTIMERS]; /* base delay */ + int32 cpu_rtc_currd[SIM_NTIMERS]; /* current delay */ + int32 cpu_rtc_initd[SIM_NTIMERS]; /* initial delay */ + uint32 cpu_rtc_elapsed[SIM_NTIMERS]; /* sec since init */ + uint32 cpu_idle_sleep_us; /* usecs in voluntary sleep */ + uint32 cpu_idle_sleep_cycles; /* cycles in idle sleep */ + + /* SSC delta timers */ + sim_delta_timer* cpu_ssc_delta_timer[2]; + + /* mask of active SSC clocks (accounts only for clocks sampled real time, not ROM tests) */ + uint32 sysd_active_mask; + + /* clock interrupt is being processed or pending */ + t_bool cpu_active_clk_interrupt; + + /* inter-processor interrupt is being processed or pending */ + t_bool cpu_active_ipi_interrupt; + + /* countdown timers for number of cycles or instructions to allow before raising next CLK interrupt, + used only if SYNCLK clock strobe thread is used */ + uint32 cpu_synclk_protect_os; + uint32 cpu_synclk_protect_dev; + /* union of cpu_synclk_protect_os and cpu_synclk_protect_dev */ + t_bool cpu_synclk_protect; + + /* when set to SynclkPendingIE0, SYNCLK interrupt had been received, and per-tick activities such as + execution of clock queue entries coscheduled with CLK and clock recalibraton should be performed + when cpu_synclk_protect expires; + when set to SynclkPendingIE1, in addition CLK should be raised when cpu_synclk_protect expires */ + SynclkPending cpu_synclk_pending; + + /* recursion control for cpu_reevaluate_thread_priority */ + t_bool cpu_inside_reevaluate_thread_priority; + t_bool cpu_redo_reevaluate_thread_priority; + + /* CPU thread and "run" gate */ + smp_semaphore* cpu_run_gate; + smp_thread_t cpu_thread; + t_bool cpu_thread_created; + + /* cpu idle sleep / cpu_wakeup handshake */ + smp_interlocked_uint32_var cpu_sleeping; + smp_event* cpu_wakeup_event; + + /* TRUE if this secondary CPU wants syswide device events pending in its event queue to be transferred to the primary + (raised by the secondary when it is shutting down, cleared after the primary transfers events to its own queue) */ + t_bool cpu_requeue_syswide_pending; + + /* slave copy of syncw.cpu[].active, moved here to avoid frequent references to master copy that would cause + interprocessor cache interference, replacing them by references to a private copy as much as possible; + + access to syncw.cpu[].active is protected by cpu_database_lock; + access to syncw_active is unprotected and limited to owning VCPU and console thread (the latter only when VCPUs are paused); + + note that syncw_active "lags" behind the master copy: if other VCPU detects this CPU has IPI or CLK interrupts + pending, but is not in SYS synchronization window yet, it will enter this CPU into SYS synchronization window + and set SYNCW_SYS in syncw.cpu[].active, but not in syncw_active; SYNCW_SYS will be set in syncw_active some + time later and only by this VCPU, when it tries to change its state; thus if SYNCW_SYS is set in syncw_active, + VCPU is always guaranteed to be active in SYS window; however if it is not set, then syncw.cpu[].active + must be consulted */ + uint32 syncw_active; + + /* countdown counters for syncw position checking; unlike cpu_adv_cycles, syncw countdown is not (counter)advanced + * by idle sleep, and counts (down) instructions, not cycles, i.e. complex instructions may change cpu_adv_cycles + * by multiple units, but will change syncw countdown only by one unit + */ + uint32 syncw_countdown; + uint32 syncw_countdown_start; + uint32 syncw_countdown_sys; + uint32 syncw_countdown_ilk; + + /* when this VCPU waits in syncw on other VCPU, it waits for this event to be signalled */ + smp_event* syncw_wait_event; + + /* when this VCPU waits in syncw on other VCPU, the latter's id is recorded here, otherwise NO_CPU_ID */ + uint32 syncw_wait_cpu_id; + + /* hack for detecting console ROM's CONTINUE sequence (MAPEN-REI) */ + uint32 cpu_con_rei; + t_bool cpu_con_rei_on; + + /* pad to ensure that per-CPU data does not interfere with shared data caching */ + t_byte pad[SMP_MAXCACHELINESIZE]; + +public: + CPU_UNIT(); + + static void* operator new(size_t size) { return operator_new_aligned(size, SMP_MAXCACHELINESIZE); } + static void operator delete(void* p) { operator_delete_aligned(p); } + + t_bool is_primary_cpu() + { + return (this == &cpu_unit_0); + } + + t_bool is_secondary_cpu() + { + return (this != &cpu_unit_0); + } + + t_bool is_running() + { + return cpu_state == CPU_STATE_RUNNING; + } + + void init(uint8 cpu_id, uint8 cpu_state); + + void init_clock_queue(); + + static CPU_UNIT* getBy(CPU_CONTEXT* ctxt); +}; + +inline CPU_UNIT* CPU_UNIT::getBy(CPU_CONTEXT* ctxt) +{ + t_addr_off ctxt_offset = (t_addr_off) & ((CPU_UNIT*)0)->cpu_context; + return (CPU_UNIT*) ((t_byte*) ctxt - ctxt_offset); +} + +#define CPU_CURRENT_CYCLES atomic_var(cpu_unit->cpu_adv_cycles) +#define XCPU_CURRENT_CYCLES atomic_var(xcpu->cpu_adv_cycles) +#define cpu_cycle() sim_interval--, CPU_CURRENT_CYCLES++ + +/* control VCPU thread priority if more than one VCPU is currently active and host is not a dedicated machine */ +#define must_control_prio() (sim_mp_active && !sim_host_dedicated) + +static SIM_INLINE t_bool tmr_is_active(RUN_DECL) +{ + return 0 != cpu_unit->sysd_active_mask; +} + +extern CPU_UNIT cpu_unit_0; +extern CPU_UNIT* cpu_units[SIM_MAX_CPUS]; +extern UNIT* cpu_units_as_units[SIM_MAX_CPUS]; +extern DEVICE cpu_dev; +extern int32 sim_units_percpu; /* number of per-CPU units in the system */ +extern int32 sim_units_global; /* number of global units in the system */ + +#if SIM_MAX_CPUS <= 32 +class cpu_set +{ +private: + uint32 mask; +public: + cpu_set() { mask = 0; } + void set(uint32 ix) { mask |= 1 << (ix & 0x1F); } + void clear(uint32 ix) { mask &= ~(1 << (ix & 0x1F)); } + void clear_all() { mask = 0; } + t_bool is_set(uint32 ix) const { return 0 != (mask & (1 << (ix & 0x1F))); } + t_bool is_clear(uint32 ix) const { return 0 == (mask & (1 << (ix & 0x1F))); } + t_bool is_any_set() const { return mask != 0; } + void op_or(const cpu_set* x) { mask |= x->mask; } + uint32 count_set(uint32 ix1, uint32 ix2) const + { + uint32 count = 0; + for (uint32 ix = ix1; ix <= ix2; ix++) + if (is_set(ix)) count++; + return count; + } +}; +#elif SIM_MAX_CPUS <= 64 +/* could do via uint64 as well */ +class cpu_set +{ +private: + uint32 mask[2]; +public: + cpu_set() { mask[0] = mask[1] = 0; } + void set(uint32 ix) { mask[ix % 32] |= 1 << (ix & 0x1F); } + void clear(uint32 ix) { mask[ix % 32] &= ~(1 << (ix & 0x1F)); } + void clear_all() { mask[0] = mask[1] = 0; } + t_bool is_set(uint32 ix) const { return 0 != (mask[ix % 32] & (1 << (ix & 0x1F))); } + t_bool is_clear(uint32 ix) const { return 0 == (mask[ix % 32] & (1 << (ix & 0x1F))); } + t_bool is_any_set() const { return (mask[0] | mask[1]) != 0; } + void op_or(const cpu_set* x) { mask[0] |= x->mask[0]; mask[1] |= x->mask[1]; } + uint32 count_set(uint32 ix1, uint32 ix2) const + { + uint32 count = 0; + for (uint32 ix = ix1; ix <= ix2; ix++) + if (is_set(ix)) count++; + return count; + } +}; +#else +class cpu_set +{ +private: + uint32 mask[(SIM_MAX_CPUS + 31) / 32]; +public: + cpu_set() { memzero(mask); } + void set(uint32 ix) { mask[ix >> 5] |= 1 << (ix & 0x1F); } + void clear(uint32 ix); { mask[ix >> 5] &= ~(1 << (ix & 0x1F)); } + void clear_all() { memzero(mask); } + t_bool is_set(uint32 ix) const { 0 != (mask[ix >> 5] & (1 << (ix & 0x1F))); } + t_bool is_clear(uint32 ix) const { 0 == (mask[ix >> 5] & (1 << (ix & 0x1F))); } + t_bool is_any_set() const + { + for (uint32 k = 0; k < sizeof(mask) / sizeof(mask[0]); k++) + if (mask[k]) + return TRUE; + return FALSE; + } + void op_or(const cpu_set* x) + { + for (uint32 k = 0; k < sizeof(mask) / sizeof(mask[0]); k++) + mask[k] |= x->mask[k]; + } + uint32 count_set(uint32 ix1, uint32 ix2) const + { + uint32 count = 0; + for (uint32 ix = ix1; ix <= ix2; ix++) + if (is_set(ix)) count++; + return count; + } +}; +#endif + +inline void* sim_reg::getloc(RUN_DECL) +{ + switch (locinfo.loctype) + { + case REG_LOCTYPE_GBL: + return (void*) loc; + + case REG_LOCTYPE_CPU: + return ((char*) cpu_unit) + (t_addr) loc; + + case REG_LOCTYPE_GBL_UNIT: + { + UNIT** uap = (UNIT**) loc; + UNIT* unit = uap[locinfo.loc_ix1]; + return ((char*) unit) + (t_addr) locinfo.loc_a1; + } + + default: + return NULL; + } +} + +inline void* sim_reg::getloc_unit_idx(RUN_DECL, uint32 idx) +{ + if (locinfo.loctype != REG_LOCTYPE_GBL_UNIT) + panic("Invalid reference to register: register is not GBL_UNIT"); + + UNIT** uap = (UNIT**) loc; + UNIT* unit = uap[idx]; + return ((char*) unit) + (t_addr) locinfo.loc_a1; +} + +inline void sim_reg::setqptr(RUN_DECL, uint32 qptr) +{ + switch (locinfo.loctype) + { + case REG_LOCTYPE_GBL: this->qptr = qptr; + return; + case REG_LOCTYPE_CPU: * (uint32*) (((char*) cpu_unit) + qptr_offset) = qptr; + return; + default: return; + } +} + +inline uint32 sim_reg::getqptr(RUN_DECL) +{ + switch (locinfo.loctype) + { + case REG_LOCTYPE_GBL: return qptr; + case REG_LOCTYPE_CPU: return * (uint32*) (((char*) cpu_unit) + qptr_offset); + default: return 0; + } +} + +#if defined(VM_VAX) +/* Function prototypes and inline code for physical memory interface */ +# include "vax_mmu.h" +#endif + +class aio_context +{ +public: + DEVICE* dptr; /* device for unit (access to debug flags) */ + UNIT* uptr; /* unit */ + uint32 dbit; /* debugging bit */ + t_bool asynch_io; /* Asynchronous Interrupt scheduling enabled */ + smp_thread_t io_thread; /* I/O thread handle */ + t_bool io_thread_created; /* ... */ + smp_simple_semaphore* io_event; /* sleep event for IOP worker thread */ + smp_event* io_flush_ack; /* signal "flushing completed" */ + t_bool io_do_flush; /* flag requesting IOP thread to perform flush */ + t_stat io_status; /* IO request comption status */ + uint32 io_reset_count; /* copy of DEVICE.a_reset_count */ + +public: + aio_context(UNIT* uptr); + virtual ~aio_context(); + void asynch_init(smp_thread_routine_t start_routine, void* arg); + void asynch_uninit(); + void thread_loop(); + virtual void perform_request() = 0; + virtual t_bool has_request() = 0; + void flush(); + virtual void perform_flush() = 0; + void io_event_signal() { io_event->release(); } + +private: + aio_context(); +}; + +/* Function prototypes */ + +#include "scp.h" +#include "sim_console.h" +#include "sim_fio.h" +void cpu_set_thread_priority(RUN_DECL, sim_thread_priority_t prio); +void cpu_set_thread_priority(RUN_RSCX_DECL, sim_thread_priority_t prio); +void* malloc_aligned(size_t size, size_t alignment); +void* calloc_aligned (size_t num, size_t elsize, size_t alignment); +void free_aligned(void* p); +const char* cpu_describe_state(CPU_UNIT* cpu_unit); +t_stat reset_cpu_and_its_devices(CPU_UNIT* cpu_unit); +t_stat reset_dev_thiscpu (DEVICE* dptr); +t_stat reset_dev_allcpus (DEVICE* dptr); +int sim_device_index (DEVICE* dptr); +t_bool cpu_may_sleep(RUN_DECL); +void wakeup_cpu(CPU_UNIT *xcpu); +void wakeup_cpus(RUN_DECL, uint32 old_idle_mask, uint32 new_idle_mask); +void cpu_begin_interlocked(RUN_DECL, volatile sim_thread_priority_t* sv_priority, volatile t_bool* sv_priority_stored); +void cpu_end_interlocked(RUN_DECL, sim_thread_priority_t sv_priority, t_bool sv_priority_stored); +void debug_out(const char* x); +void throw_sim_exception_ABORT(RUN_DECL, t_stat x); +t_bool cpu_stop_history (); +t_bool sim_brk_is_in_action (); +void perf_register_object(const char* name, smp_lock* object, t_bool copyname = FALSE); +void perf_unregister_object(smp_lock* object); +t_value reg_sirr_rd(REG* r, uint32 idx); +void reg_sirr_wr(REG* r, uint32 idx, t_value value); + + +/* initialization helper definitions */ + +class on_init_call +{ +public: + static on_init_call* head; + +protected: + on_init_call* next; + void (*routine)(); + virtual void invoke() + { + (*routine)(); + } + void insert() + { + this->next = head; + head = this; + } + +public: + on_init_call(void (*routine)()) + { + this->routine = routine; + insert(); + } + + static void invoke_all() + { + for (on_init_call* cp = head; cp; cp = cp->next) + cp->invoke(); + } +}; + +class on_init_smp_lock : public on_init_call +{ +protected: + smp_lock** ppcs; + sim_lock_criticality_t criticality; + uint32 cycles; + const char* name; + + void invoke() + { + *ppcs = smp_lock::create(cycles); + if (criticality != SIM_LOCK_CRITICALITY_NONE) + (*ppcs)->set_criticality(criticality); + perf_register_object(name, *ppcs); + } + +public: + on_init_smp_lock(smp_lock** ppcs, sim_lock_criticality_t criticality, uint32 cycles, const char* name) : on_init_call(NULL) + { + this->ppcs = ppcs; + this->criticality = criticality; + this->cycles = cycles; + this->name = name; + } +}; + +class on_init_tls : public on_init_call +{ +protected: + smp_tls_key* pkey; + + void invoke() + { + if (! smp_tls_alloc(pkey)) + panic("Unable to initialize TLS variable"); + } + +public: + on_init_tls(smp_tls_key* pkey) : on_init_call(NULL) + { + this->pkey = pkey; + } +}; + +#define AUTO_TLS(key) \ + static smp_tls_key key; \ + static on_init_tls __oninit_tls_##key(& key) + +#define ON_INIT_INVOKE(routine) \ + static on_init_call __oninit_call_##routine(routine) + +#define AUTO_INIT_LOCK(lockname, criticality, spinwait_cycles) \ + static smp_lock* lockname = NULL; \ + static on_init_smp_lock __oninit_smp_lock_##lockname(& lockname, criticality, spinwait_cycles, #lockname) + +#define DEVLOCK_SPINWAIT_CYCLES 5000 +#define AUTO_INIT_DEVLOCK(lockname) \ + AUTO_INIT_LOCK(lockname, SIM_LOCK_CRITICALITY_NONE, DEVLOCK_SPINWAIT_CYCLES) + +/* auto-locking helpers */ +class auto_lock : public sim_try_auto_destructable +{ + smp_lock* pcs; + t_bool pcs_locked; + +public: + SIM_INLINE auto_lock(smp_lock* pcs, t_bool dolock = TRUE) + { + this->pcs = pcs; + pcs_locked = FALSE; + if (dolock) lock(); + onConstructor(); + } + SIM_INLINE ~auto_lock() + { + onDestroy(FALSE); + } + SIM_INLINE void onDestroy(t_bool unregistered) + { + if (onDestructor(unregistered)) + unlock(); + } + SIM_INLINE void unlock() + { + if (pcs_locked) + { + pcs->unlock(); + pcs_locked = FALSE; + } + } + SIM_INLINE void lock() + { + if (!pcs_locked) + { + pcs->lock(); + pcs_locked = TRUE; + } + } +}; + +#define AUTO_LOCK(lockname) auto_lock auto_lock_##lockname(lockname) +#define AUTO_LOCK_NM(autoname, lockname) auto_lock autoname(lockname) + +#include "sim_syncw.h" + +/* globals */ +extern char sim_name[]; +extern uint32 sim_idle_stable; +extern const uint32 sim_taddr_64; +extern atomic_int32 stop_cpus; +extern t_bool sim_asynch_enabled; +extern t_bool sim_brk_continue; +extern t_bool sim_vsmp_active; +extern t_bool sim_vsmp_idle_sleep; +extern t_bool sim_ws_prefaulted; +extern t_bool sim_ws_settings_changed; +extern t_bool sim_ws_lock; +extern uint32 sim_ws_min; +extern uint32 sim_ws_max; +extern uint32 sim_host_turbo; +extern t_bool sim_host_dedicated; +extern uint32 use_native_interlocked; + +#endif diff --git a/src/sim_disk.cpp b/src/sim_disk.cpp new file mode 100644 index 0000000..aedfeaf --- /dev/null +++ b/src/sim_disk.cpp @@ -0,0 +1,3404 @@ +/* sim_disk.c: simulator disk support library + + Copyright (c) 2011, Mark Pizzolato + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of Mark Pizzolato shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Mark Pizzolato. + + + + This is the place which hides processing of various disk formats, + as well as OS-specific direct hardware access. + + 25-Jan-11 MP Initial Implemementation + +Public routines: + + sim_disk_attach attach disk unit + sim_disk_detach detach disk unit + sim_disk_rdsect read disk sectors + sim_disk_rdsect_a read disk sectors asynchronously + sim_disk_wrsect write disk sectors + sim_disk_wrsect_a write disk sectors asynchronously + sim_disk_unload unload or detach a disk as needed + sim_disk_reset reset device + sim_disk_wrp TRUE if write protected + sim_disk_isavailable TRUE if available for I/O + sim_disk_size get disk size + sim_disk_set_fmt set disk format + sim_disk_show_fmt show disk format + sim_disk_set_capac set disk capacity + sim_disk_show_capac show disk capacity + sim_disk_set_async enable asynchronous operation + sim_disk_clr_async disable asynchronous operation + sim_disk_data_trace debug support + +Internal routines: + + sim_os_disk_open_raw platform specific open raw device + sim_os_disk_close_raw platform specific close raw device + sim_os_disk_size_raw platform specific raw device size + sim_os_disk_unload_raw platform specific disk unload/eject + sim_os_disk_rdsect platform specific read sectors + sim_os_disk_wrsect platform specific write sectors + + sim_vhd_disk_open platform independent open virtual disk file + sim_vhd_disk_create platform independent create virtual disk file + sim_vhd_disk_create_diff platform independent create differencing virtual disk file + sim_vhd_disk_close platform independent close virtual disk file + sim_vhd_disk_size platform independent virtual disk size + sim_vhd_disk_rdsect platform independent read virtual disk sectors + sim_vhd_disk_wrsect platform independent write virtual disk sectors + + +*/ + +#if defined(_WIN32) +# include +#endif + +#include "sim_defs.h" +#include "sim_disk.h" + +#include +#include + +extern SMP_FILE* sim_log; /* log file */ +extern int32 sim_switches; +extern int32 sim_quiet; +extern int32 sim_end; + +#define DOP_DONE 0 /* close */ +#define DOP_RSEC 1 /* sim_disk_rdsect_a */ +#define DOP_WSEC 2 /* sim_disk_wrsect_a */ +#define DOP_IAVL 3 /* sim_disk_isavailable_a */ + + +class disk_context : public aio_context +{ +public: + disk_context(UNIT* uptr) : aio_context(uptr) + { + io_dop = DOP_DONE; + callback = NULL; + } + void perform_flush(); + static void perform_flush(UNIT* uptr); + t_bool has_request() { return io_dop != DOP_DONE; } + void perform_request(); + +public: + uint32 sector_size; /* Disk Sector Size (of the pseudo disk) */ + uint32 xfer_element_size; /* Disk Bus Transfer size (1 - byte, 2 - word, 4 - longword) */ + uint32 storage_sector_size;/* Sector size of the containing storage */ + uint32 removable; /* Removable device flag */ + uint32 auto_format; /* Format determined dynamically */ +#if defined _WIN32 + HANDLE disk_handle; /* OS specific Raw device handle */ +#endif + int io_dop; + uint8 *buf; + t_seccnt *rsects; + t_seccnt sects; + t_lba lba; + DISK_PCALLBACK callback; +}; + +#define disk_ctx up8 /* Field in Unit structure which points to the disk_context */ + +static void aio_panic(); +#define AIO_CALLSETUP \ +disk_context* ctx = (disk_context*) uptr->disk_ctx; \ + \ +if ((!callback) || !ctx->asynch_io) + +/* caller of AIO_CALL will be holding uptr->lock */ +#define AIO_CALL(op, _lba, _buf, _rsects, _sects, _callback) \ + if (ctx->asynch_io) \ + { \ + disk_context* ctx = (disk_context*) uptr->disk_ctx; \ + \ + sim_debug (ctx->dbit, ctx->dptr, \ + "sim_disk AIO_CALL(op=%d, unit=%d, lba=0x%X, sects=%d)\n",\ + op, sim_unit_index(uptr), _lba, _sects); \ + \ + if (unlikely(NULL != ctx->callback)) \ + aio_panic(); /* gross error */ \ + ctx->lba = _lba; \ + ctx->buf = _buf; \ + ctx->sects = _sects; \ + ctx->rsects = _rsects; \ + ctx->callback = _callback; \ + ctx->io_reset_count = uptr->device->a_reset_count; \ + smp_wmb(); \ + ctx->io_dop = op; \ + ctx->io_event_signal(); \ + } \ + else \ + if (_callback) \ + (_callback) (uptr, r); + + +SMP_THREAD_ROUTINE_DECL _disk_io(void* arg) +{ + UNIT* volatile uptr = (UNIT*)arg; + disk_context* ctx = (disk_context*) uptr->disk_ctx; + char tname[16]; + + sim_try + { + sim_debug (ctx->dbit, ctx->dptr, "_disk_io(unit=%d) starting\n", sim_unit_index(uptr)); + + smp_thread_init(); + + run_scope_context* rscx = new run_scope_context(NULL, SIM_THREAD_TYPE_IOP, ctx->io_thread); + rscx->set_current(); + + smp_set_thread_priority(SIMH_THREAD_PRIORITY_IOP); + sprintf(tname, "IOP_%s%d", uptr->device->name, sim_unit_index(uptr)); + smp_set_thread_name(tname); + + ctx->thread_loop(); + } + sim_catch (sim_exception_SimError, exc) + { + fprintf(smp_stderr, "\nFatal error in %s simulator, unexpected exception while executing disk IOP thread\n", sim_name); + fprintf(smp_stderr, "Exception cause: %s\n", exc->get_message()); + fprintf(smp_stderr, "Terminating the simulator abnormally...\n"); + exit(1); + } + sim_end_try + + sim_debug (ctx->dbit, ctx->dptr, "_disk_io(unit=%d) exiting\n", sim_unit_index(uptr)); + + SMP_THREAD_ROUTINE_END; +} + +void disk_context::perform_request() +{ + switch (io_dop) + { + case DOP_RSEC: + io_status = sim_disk_rdsect (uptr, lba, buf, rsects, sects); + break; + case DOP_WSEC: + io_status = sim_disk_wrsect (uptr, lba, buf, rsects, sects); + break; + case DOP_IAVL: + io_status = sim_disk_isavailable (uptr); + break; + } + io_dop = DOP_DONE; + sim_async_post_io_event(uptr); +} + +/* This routine is called in the context of the main simulator thread before + processing events for any unit. It is only called when an asynchronous + thread has called sim_activate() to activate a unit. The job of this + routine is to put the unit in proper condition to digest what may have + occurred in the asynchrcondition thread. + + Since disk processing only handles a single I/O at a time to a + particular disk device (due to using stdio for the SimH Disk format + and stdio doesn't have an atomic seek+(read|write) operation), + we have the opportunity to possibly detect improper attempts to + issue multiple concurrent I/O requests. */ +static void _disk_completion_dispatch (UNIT *uptr) +{ + disk_context* ctx = (disk_context*) uptr->disk_ctx; + DISK_PCALLBACK callback = ctx->callback; + + sim_debug (ctx->dbit, ctx->dptr, "_disk_completion_dispatch(unit=%d, dop=%d, callback=%p)\n", sim_unit_index(uptr), ctx->io_dop, ctx->callback); + + if (ctx->io_dop != DOP_DONE) + aio_panic(); /* horribly wrong, stop */ + + if (ctx->callback && ctx->io_dop == DOP_DONE) + { + ctx->callback = NULL; + if (ctx->io_reset_count == uptr->device->a_reset_count) + callback (uptr, ctx->io_status); + } +} + +static void aio_panic() +{ + panic("Unexpected fatal error in disk AIO subsystem"); +} + +/* Forward declarations */ + +static t_stat sim_vhd_disk_implemented (void); +static SMP_FILE* sim_vhd_disk_open (const char *rawdevicename, const char *openmode); +static SMP_FILE* sim_vhd_disk_create (const char *szVHDPath, t_addr desiredsize); +static SMP_FILE* sim_vhd_disk_create_diff (const char *szVHDPath, const char *szParentVHDPath); +static int sim_vhd_disk_close (SMP_FILE* f); +static void sim_vhd_disk_flush (SMP_FILE* f); +static t_addr sim_vhd_disk_size (SMP_FILE* f); +static t_stat sim_vhd_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects); +static t_stat sim_vhd_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects); +static t_stat sim_vhd_disk_set_dtype (SMP_FILE* f, const char *dtype); +static const char *sim_vhd_disk_get_dtype (SMP_FILE* f); +static t_stat sim_os_disk_implemented_raw (void); +static SMP_FILE* sim_os_disk_open_raw (const char *rawdevicename, const char *openmode); +static int sim_os_disk_close_raw (SMP_FILE* f); +static void sim_os_disk_flush_raw (SMP_FILE* f); +static t_addr sim_os_disk_size_raw (SMP_FILE* f); +static t_stat sim_os_disk_unload_raw (SMP_FILE* f); +static t_bool sim_os_disk_isavailable_raw (SMP_FILE* f); +static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects); +static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects); +static t_stat sim_os_disk_info_raw (SMP_FILE* f, uint32 *sector_size, uint32 *removable); +static t_stat sim_disk_pdp11_bad_block (UNIT *uptr, int32 sec); + +struct sim_disk_fmt { + char *name; /* name */ + int32 uflags; /* unit flags */ + int32 fmtval; /* Format type value */ + t_stat (*impl_fnc)(void); /* Implemented Test Function */ + }; + +static struct sim_disk_fmt fmts[DKUF_N_FMT] = { + { "SIMH", 0, DKUF_F_STD, NULL}, + { "RAW", 0, DKUF_F_RAW, sim_os_disk_implemented_raw}, + { "VHD", 0, DKUF_F_VHD, sim_vhd_disk_implemented}, + { NULL, 0, 0} + }; + +/* Set disk format */ + +t_stat sim_disk_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + uint32 f; + + if (uptr == NULL) + return SCPE_IERR; + if (cptr == NULL) + return SCPE_ARG; + for (f = 0; f < DKUF_N_FMT && fmts[f].name; f++) { + if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) { + if ((fmts[f].impl_fnc) && (fmts[f].impl_fnc() != SCPE_OK)) + return SCPE_NOFNC; + uptr->flags = (uptr->flags & ~DKUF_FMT) | + (fmts[f].fmtval << DKUF_V_FMT) | fmts[f].uflags; + return SCPE_OK; + } + } + return SCPE_ARG; +} + +/* Show disk format */ + +t_stat sim_disk_show_fmt (SMP_FILE* st, UNIT *uptr, int32 val, void *desc) +{ + int32 i, f = DK_GET_FMT (uptr); + + for (i = 0; i < (int32) DKUF_N_FMT; i++) + if (fmts[i].fmtval == f) { + fprintf (st, "%s format", fmts[i].name); + return SCPE_OK; + } + fprintf (st, "invalid format"); + return SCPE_OK; +} + +/* Set disk capacity */ + +t_stat sim_disk_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + t_addr cap; + t_stat r; + + if ((cptr == NULL) || (*cptr == 0)) + return SCPE_ARG; + if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; + cap = (t_addr) get_uint (cptr, 10, sim_taddr_64? 2000000: 2000, &r); + if (r != SCPE_OK) + return SCPE_ARG; + uptr->capac = cap * ((t_addr) 1000000); + return SCPE_OK; +} + +/* Show disk capacity */ + +t_stat sim_disk_show_capac (SMP_FILE* st, UNIT *uptr, int32 val, void *desc) +{ + if (uptr->capac) { + if (uptr->capac >= (t_addr) 1000000) + fprintf (st, "capacity=%dMB", (uint32) (uptr->capac / ((t_addr) 1000000))); + else if (uptr->capac >= (t_addr) 1000) + fprintf (st, "capacity=%dKB", (uint32) (uptr->capac / ((t_addr) 1000))); + else fprintf (st, "capacity=%dB", (uint32) uptr->capac); + } + else fprintf (st, "undefined capacity"); + return SCPE_OK; +} + +/* Test for available */ + +t_bool sim_disk_isavailable (UNIT *uptr) +{ +if (!(uptr->flags & UNIT_ATT)) /* attached? */ + return FALSE; +switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_STD: /* SIMH format */ + return TRUE; + case DKUF_F_VHD: /* VHD format */ + return TRUE; + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + return sim_os_disk_isavailable_raw (uptr->fileref); + break; + default: + return FALSE; + } +} + +t_bool sim_disk_isavailable_a (UNIT *uptr, DISK_PCALLBACK callback) +{ +t_bool r = FALSE; +AIO_CALLSETUP + r = sim_disk_isavailable (uptr); +AIO_CALL(DOP_IAVL, 0, NULL, NULL, 0, callback); +return r; +} + +/* Test for write protect */ + +t_bool sim_disk_wrp (UNIT *uptr) +{ +return (uptr->flags & DKUF_WRP)? TRUE: FALSE; +} + +/* Get Disk size */ + +t_addr sim_disk_size (UNIT *uptr) +{ +switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_STD: /* SIMH format */ + return sim_fsize_ex (uptr->fileref); + case DKUF_F_VHD: /* VHD format */ + return sim_vhd_disk_size (uptr->fileref); + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + return sim_os_disk_size_raw (uptr->fileref); + break; + default: + return (t_addr)-1; + } +} + +/* Enable asynchronous operation */ + +t_stat sim_disk_set_async (UNIT *uptr, int latency) +{ + disk_context* ctx = (disk_context*) uptr->disk_ctx; + + if (ctx->asynch_io = sim_asynch_enabled) + { + uptr->a_check_completion = _disk_completion_dispatch; + ctx->asynch_io = FALSE; + ctx->asynch_init(_disk_io, (void*) uptr); + ctx->asynch_io = TRUE; + } + return SCPE_OK; +} + +/* Disable asynchronous operation */ + +t_stat sim_disk_clr_async (UNIT *uptr) +{ + disk_context* ctx = (disk_context*) uptr->disk_ctx; + + /* make sure device exists */ + if (!ctx) return SCPE_UNATT; + + if (ctx->asynch_io) + ctx->asynch_uninit(); + + return SCPE_OK; +} + +/* Read Sectors */ + +static t_stat _sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) +{ +t_addr da; +uint32 err, tbc; +size_t i; +disk_context* ctx = (disk_context*) uptr->disk_ctx; + +sim_debug (ctx->dbit, ctx->dptr, "_sim_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", sim_unit_index(uptr), lba, sects); + +da = ((t_addr)lba) * ctx->sector_size; +tbc = sects * ctx->sector_size; +if (sectsread) + *sectsread = 0; +err = sim_fseek (uptr->fileref, da, SEEK_SET); /* set pos */ +if (!err) { + i = sim_fread (buf, ctx->xfer_element_size, tbc/ctx->xfer_element_size, uptr->fileref); + if (i < tbc/ctx->xfer_element_size) /* fill */ + memset (&buf[i*ctx->xfer_element_size], 0, tbc-(i*ctx->xfer_element_size)); + err = ferror (uptr->fileref); + if ((!err) && (sectsread)) + *sectsread = (t_seccnt)((i*ctx->xfer_element_size+ctx->sector_size-1)/ctx->sector_size); + } +return err; +} + +t_stat sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) +{ +t_stat r; +disk_context* ctx = (disk_context*) uptr->disk_ctx; +t_seccnt sread; + +sim_debug (ctx->dbit, ctx->dptr, "sim_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", sim_unit_index(uptr), lba, sects); + +if ((sects == 1) && /* Single sector reads */ + (lba >= uptr->capac/ctx->sector_size)) { /* beyond the end of the disk */ + memset (buf, '\0', ctx->sector_size); /* are bad block management efforts - zero buffer */ + if (sectsread) + *sectsread = 1; + return SCPE_OK; /* return success */ + } + +if ((0 == (ctx->sector_size & (ctx->storage_sector_size - 1))) || /* Sector Aligned & whole sector transfers */ + ((0 == ((lba*ctx->sector_size) & (ctx->storage_sector_size - 1))) && + (0 == ((sects*ctx->sector_size) & (ctx->storage_sector_size - 1))))) { + switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_STD: /* SIMH format */ + return _sim_disk_rdsect (uptr, lba, buf, sectsread, sects); + case DKUF_F_VHD: /* VHD format */ + r = sim_vhd_disk_rdsect (uptr, lba, buf, &sread, sects); + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + r = sim_os_disk_rdsect (uptr, lba, buf, &sread, sects); + break; + default: + return SCPE_NOFNC; + } + if (sectsread) + *sectsread = sread; + if (r != SCPE_OK) + return r; + sim_buf_swap_data (buf, ctx->xfer_element_size, (sread * ctx->sector_size) / ctx->xfer_element_size); + return r; + } +else { /* Unaligned and/or partial sector transfers */ + uint8 *tbuf = (uint8*) malloc (sects*ctx->sector_size + 2*ctx->storage_sector_size); + t_lba sspsts = ctx->storage_sector_size/ctx->sector_size; /* sim sectors in a storage sector */ + t_lba tlba = lba & ~(sspsts - 1); + t_seccnt tsects = sects + (lba - tlba); + + tsects = (tsects + (sspsts - 1)) & ~(sspsts - 1); + if (sectsread) + *sectsread = 0; + if (tbuf == NULL) + return SCPE_MEM; + switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_STD: /* SIMH format */ + r = _sim_disk_rdsect (uptr, tlba, tbuf, &sread, tsects); + break; + case DKUF_F_VHD: /* VHD format */ + r = sim_vhd_disk_rdsect (uptr, tlba, tbuf, &sread, tsects); + if (r == SCPE_OK) + sim_buf_swap_data (tbuf, ctx->xfer_element_size, (sread * ctx->sector_size) / ctx->xfer_element_size); + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + r = sim_os_disk_rdsect (uptr, tlba, tbuf, &sread, tsects); + if (r == SCPE_OK) + sim_buf_swap_data (tbuf, ctx->xfer_element_size, (sread * ctx->sector_size) / ctx->xfer_element_size); + break; + default: + free (tbuf); + return SCPE_NOFNC; + } + if (r == SCPE_OK) { + memcpy (buf, tbuf + ((lba - tlba) * ctx->sector_size), sects * ctx->sector_size); + if (sectsread) { + *sectsread = sread - (lba - tlba); + if (*sectsread > sects) + *sectsread = sects; + } + } + free (tbuf); + return r; + } +} + +t_stat sim_disk_rdsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects, DISK_PCALLBACK callback) +{ +t_stat r = SCPE_OK; +AIO_CALLSETUP + r = sim_disk_rdsect (uptr, lba, buf, sectsread, sects); +AIO_CALL(DOP_RSEC, lba, buf, sectsread, sects, callback); +return r; +} + +/* Write Sectors */ + +static t_stat _sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) +{ +t_addr da; +uint32 err, tbc; +size_t i; +disk_context* ctx = (disk_context*) uptr->disk_ctx; + +sim_debug (ctx->dbit, ctx->dptr, "_sim_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", sim_unit_index(uptr), lba, sects); + +da = ((t_addr)lba) * ctx->sector_size; +tbc = sects * ctx->sector_size; +if (sectswritten) + *sectswritten = 0; +err = sim_fseek (uptr->fileref, da, SEEK_SET); /* set pos */ +if (!err) { + i = sim_fwrite (buf, ctx->xfer_element_size, tbc/ctx->xfer_element_size, uptr->fileref); + err = ferror (uptr->fileref); + if ((!err) && (sectswritten)) + *sectswritten = (t_seccnt)((i*ctx->xfer_element_size+ctx->sector_size-1)/ctx->sector_size); + } +return err; +} + +t_stat sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) +{ +disk_context* ctx = (disk_context*) uptr->disk_ctx; +uint32 f = DK_GET_FMT (uptr); +t_stat r; +uint8 *tbuf = NULL; + +sim_debug (ctx->dbit, ctx->dptr, "sim_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", sim_unit_index(uptr), lba, sects); + +if (f == DKUF_F_STD) + return _sim_disk_wrsect (uptr, lba, buf, sectswritten, sects); +if ((0 == (ctx->sector_size & (ctx->storage_sector_size - 1))) || /* Sector Aligned & whole sector transfers */ + ((0 == ((lba*ctx->sector_size) & (ctx->storage_sector_size - 1))) && + (0 == ((sects*ctx->sector_size) & (ctx->storage_sector_size - 1))))) { + + if (sim_end || (ctx->xfer_element_size == sizeof (char))) + switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_VHD: /* VHD format */ + return sim_vhd_disk_wrsect (uptr, lba, buf, sectswritten, sects); + case DKUF_F_RAW: /* Raw Physical Disk Access */ + return sim_os_disk_wrsect (uptr, lba, buf, sectswritten, sects); + default: + return SCPE_NOFNC; + } + + tbuf = (uint8*) malloc (sects * ctx->sector_size); + if (NULL == tbuf) + return SCPE_MEM; + sim_buf_copy_swapped (tbuf, buf, ctx->xfer_element_size, (sects * ctx->sector_size) / ctx->xfer_element_size); + + switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_VHD: /* VHD format */ + r = sim_vhd_disk_wrsect (uptr, lba, tbuf, sectswritten, sects); + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + r = sim_os_disk_wrsect (uptr, lba, tbuf, sectswritten, sects); + break; + default: + r = SCPE_NOFNC; + break; + } + } +else { /* Unaligned and/or partial sector transfers */ + t_lba sspsts = ctx->storage_sector_size/ctx->sector_size; /* sim sectors in a storage sector */ + t_lba tlba = lba & ~(sspsts - 1); + t_seccnt tsects = sects + (lba - tlba); + + tbuf = (uint8*) malloc (sects*ctx->sector_size + 2*ctx->storage_sector_size); + tsects = (tsects + (sspsts - 1)) & ~(sspsts - 1); + if (sectswritten) + *sectswritten = 0; + if (tbuf == NULL) + return SCPE_MEM; + /* Partial Sector writes require a read-modify-write sequence for the partial sectors */ + if ((lba & (sspsts - 1)) || + (sects < sspsts)) + switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_VHD: /* VHD format */ + sim_vhd_disk_rdsect (uptr, tlba, tbuf, NULL, sspsts); + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + sim_os_disk_rdsect (uptr, tlba, tbuf, NULL, sspsts); + break; + default: + r = SCPE_NOFNC; + break; + } + if ((tsects > sspsts) && + ((sects + lba - tlba) & (sspsts - 1))) + switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_VHD: /* VHD format */ + sim_vhd_disk_rdsect (uptr, tlba + tsects - sspsts, + tbuf + (tsects - sspsts) * ctx->sector_size, + NULL, sspsts); + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + sim_os_disk_rdsect (uptr, tlba + tsects - sspsts, + tbuf + (tsects - sspsts) * ctx->sector_size, + NULL, sspsts); + break; + default: + r = SCPE_NOFNC; + break; + } + sim_buf_copy_swapped (tbuf + (lba & (sspsts - 1)) * ctx->sector_size, + buf, ctx->xfer_element_size, (sects * ctx->sector_size) / ctx->xfer_element_size); + switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_VHD: /* VHD format */ + r = sim_vhd_disk_wrsect (uptr, tlba, tbuf, sectswritten, tsects); + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + r = sim_os_disk_wrsect (uptr, tlba, tbuf, sectswritten, tsects); + break; + default: + r = SCPE_NOFNC; + break; + } + if ((r == SCPE_OK) && sectswritten) { + *sectswritten -= (lba - tlba); + if (*sectswritten > sects) + *sectswritten = sects; + } + } +free (tbuf); +return r; +} + +t_stat sim_disk_wrsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects, DISK_PCALLBACK callback) +{ +t_stat r = SCPE_OK; +AIO_CALLSETUP + r = sim_disk_wrsect (uptr, lba, buf, sectswritten, sects); +AIO_CALL(DOP_WSEC, lba, buf, sectswritten, sects, callback); +return r; +} + +t_stat sim_disk_unload (UNIT *uptr) +{ +switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_STD: /* Simh */ + case DKUF_F_VHD: /* VHD format */ + return sim_disk_detach (uptr); + case DKUF_F_RAW: /* Raw Physical Disk Access */ + return sim_os_disk_unload_raw (uptr->fileref); /* remove/eject disk */ + break; + default: + return SCPE_NOFNC; + } +} + +static void _sim_disk_io_flush (UNIT *uptr) +{ + disk_context* ctx = (disk_context*) uptr->disk_ctx; + if (ctx) + ctx->flush(); + else + disk_context::perform_flush(uptr); +} + +void disk_context::perform_flush() +{ + perform_flush(uptr); +} + +void disk_context::perform_flush(UNIT* uptr) +{ + switch (DK_GET_FMT (uptr)) /* case on format */ + { + case DKUF_F_STD: /* Simh */ + fflush (uptr->fileref); + break; + case DKUF_F_VHD: /* Virtual Disk */ + sim_vhd_disk_flush (uptr->fileref); + break; + case DKUF_F_RAW: /* Physical */ + sim_os_disk_flush_raw (uptr->fileref); + break; + } +} + +static t_stat _err_return (UNIT *uptr, t_stat stat) +{ + free (uptr->filename); + uptr->filename = NULL; + + delete (disk_context*) uptr->disk_ctx; + uptr->disk_ctx = NULL; + + return stat; +} + +/* + * Reset disk: + * + * Caller must be either console thread or VCPU thread holding the lock for the device. + * It is assumed that all units on the device share the same lock. + */ +t_stat sim_disk_reset (DEVICE* dptr) +{ + RUN_SCOPE_RSCX; + UNIT* uptr; + t_bool any_async = FALSE; + uint32 k; + + if (rscx->thread_type == SIM_THREAD_TYPE_CONSOLE) + { + /* console thread can reset devices (all VCPUs are paused) */ + } + else if (rscx->thread_type == SIM_THREAD_TYPE_CPU) + { + for (k = 0; k < dptr->numunits; k++) + { + uptr = dptr->units[k]; + if (uptr->flags & UNIT_ATT) + { + disk_context* ctx = (disk_context*) uptr->disk_ctx; + if (ctx && ctx->asynch_io) + { + any_async = TRUE; + break; + } + } + } + + /* + * On a multiprocessor VAX with asynchronous IO enabled, asynchronous IO completion is handled + * by the primary processor that fetches units with AIO events from AIO event queue. Resetting + * controller requires flushing all entries pending in async queue. To do it on a secondary CPU, + * we'd have to send IPI to the primary and wait for the response. However primary may already + * being stopped by the console, so console code responsible for pausing VCPUs would have to + * check for pending flushing request and execute it (in fact it does, but we'd have to wait + * either for the primary VCPU response or AIO queue going empty). + * + * More seriously, we are holding device lock, so the primary may go deadlocked with us if we + * try to wait for it. On the other hand, we cannot release the lock (which may even have + * acquisition depth > 1), even temporarily. Also, primary can already be right at this point + * blocked inside uptr->lock(). + * + * It may be possible to design a scheme to handle this situation, however it appears that + * resetting controller by the secondary CPU is an exremely unlikely event in the first place. + * We may implement handling of this case if it ever becomes a problem. + * For now just abort the simulator if it is encountered. ToDo. + */ + if (any_async && !cpu_unit->is_primary_cpu()) + panic("Disk controller device reset attempted by a secondary CPU"); + } + else + { + panic("sim_disk_reset: invalid thread type"); + } + + dptr->a_reset_count++; + + for (k = 0; k < dptr->numunits; k++) + { + uptr = dptr->units[k]; + if ((uptr->flags & UNIT_ATT) && !(uptr->flags & UNIT_BUF) && uptr->fileref) + if (uptr->io_flush) + uptr->io_flush(uptr); + } + + if (any_async) + { + if (rscx->thread_type == SIM_THREAD_TYPE_CONSOLE) + sim_async_process_io_events_for_console(); + else + sim_async_process_io_events(RUN_PASS, NULL, TRUE); + } + + for (k = 0; k < dptr->numunits; k++) + { + uptr = dptr->units[k]; + sim_cancel(uptr); + } + + return SCPE_OK; +} + + +t_stat sim_disk_attach (UNIT *uptr, char *cptr, size_t sector_size, size_t xfer_element_size, t_bool dontautosize, uint32 dbit, const char *dtype, uint32 pdp11tracksize) +{ +disk_context* ctx; +DEVICE *dptr; +SMP_FILE* (*open_function)(const char *filename, const char *mode) = sim_fopen; +SMP_FILE* (*create_function)(const char *filename, t_addr desiredsize) = NULL; +t_addr (*size_function)(SMP_FILE* file); +t_stat (*storage_function)(SMP_FILE* file, uint32 *sector_size, uint32 *removable) = NULL; +t_bool created = FALSE; +t_bool auto_format = FALSE; +t_addr capac; + +if (uptr->flags & UNIT_DIS) /* disabled? */ + return SCPE_UDIS; +if (!(uptr->flags & UNIT_ATTABLE)) /* not attachable? */ + return SCPE_NOATT; +if ((dptr = find_dev_from_unit (uptr)) == NULL) + return SCPE_NOATT; +if (sim_switches & SWMASK ('F')) { /* format spec? */ + char gbuf[CBUFSIZE]; + cptr = get_glyph (cptr, gbuf, 0); /* get spec */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + if (sim_disk_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK) + return SCPE_ARG; + } +if (sim_switches & SWMASK ('D')) { /* create difference disk? */ + char gbuf[CBUFSIZE]; + SMP_FILE* vhd; + + sim_switches = sim_switches & ~(SWMASK ('D')); + cptr = get_glyph_nc (cptr, gbuf, 0); /* get spec */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + vhd = sim_vhd_disk_create_diff (gbuf, cptr); + if (vhd) { + sim_vhd_disk_close (vhd); + return sim_disk_attach (uptr, gbuf, sector_size, xfer_element_size, dontautosize, dbit, dtype, pdp11tracksize); + } + return SCPE_ARG; + } +if (sim_switches & SWMASK ('C')) { /* create vhd disk & copy contents? */ + char gbuf[CBUFSIZE]; + SMP_FILE* vhd; + int saved_sim_switches = sim_switches; + int32 saved_sim_quiet = sim_quiet; + t_stat r; + + sim_switches = sim_switches & ~(SWMASK ('C')); + cptr = get_glyph_nc (cptr, gbuf, 0); /* get spec */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + sim_switches |= SWMASK ('R') | SWMASK ('E'); + sim_quiet = TRUE; + /* First open the source of the copy operation */ + r = sim_disk_attach (uptr, cptr, sector_size, xfer_element_size, dontautosize, dbit, dtype, pdp11tracksize); + sim_quiet = saved_sim_quiet; + if (r != SCPE_OK) { + sim_switches = saved_sim_switches; + return r; + } + if (!sim_quiet) + smp_printf ("%s%d: creating new virtual disk '%s'\n", sim_dname (dptr), sim_unit_index(uptr), gbuf); + vhd = sim_vhd_disk_create (gbuf, uptr->capac); + if (!vhd) { + if (!sim_quiet) + smp_printf ("%s%d: can't create virtual disk '%s'\n", sim_dname (dptr), sim_unit_index(uptr), gbuf); + return SCPE_OPENERR; + } + else { + uint8 *copy_buf = (uint8*) malloc (1024*1024); + t_lba lba; + t_seccnt sectors_per_buffer = (t_seccnt)((1024*1024)/sector_size); + t_lba total_sectors = (t_lba)(uptr->capac/sector_size); + t_seccnt sects = sectors_per_buffer; + + if (!copy_buf) { + remove (gbuf); + return SCPE_MEM; + } + for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) { + if (!sim_quiet) + smp_printf ("%s%d: Copied %dMB. %d%% complete.\r", sim_dname (dptr), sim_unit_index(uptr), (int)(((t_addr)lba*sector_size)/1000000), (int)((lba*100)/total_sectors)); + sects = sectors_per_buffer; + if (lba + sects > total_sectors) + sects = total_sectors - lba; + r = sim_disk_rdsect (uptr, lba, copy_buf, NULL, sects); + if (r == SCPE_OK) { + uint32 saved_unit_flags = uptr->flags; + SMP_FILE* save_unit_fileref = uptr->fileref; + + sim_disk_set_fmt (uptr, 0, "VHD", NULL); + uptr->fileref = vhd; + r = sim_disk_wrsect (uptr, lba, copy_buf, NULL, sects); + uptr->fileref = save_unit_fileref; + uptr->flags = saved_unit_flags; + } + } + if (!sim_quiet) + smp_printf ("\n%s%d: Copied %dMB. Done.\n", sim_dname (dptr), sim_unit_index(uptr), (int)(((t_addr)lba*sector_size)/1000000)); + free (copy_buf); + created = TRUE; + sim_vhd_disk_close (vhd); + sim_disk_detach (uptr); + strcpy (cptr, gbuf); + sim_disk_set_fmt (uptr, 0, "VHD", NULL); + sim_switches = saved_sim_switches; + /* fall through and open/return the newly created & copied vhd */ + } + } +switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_STD: /* SIMH format */ + if (NULL == (uptr->fileref = sim_vhd_disk_open (cptr, "rb"))) { + open_function = sim_fopen; + size_function = sim_fsize_ex; + break; + } + sim_disk_set_fmt (uptr, 0, "VHD", NULL); /* set file format to VHD */ + sim_vhd_disk_close (uptr->fileref); /* close vhd file*/ + auto_format = TRUE; + uptr->fileref = NULL; + /* Fall through to normal VHD processing */ + case DKUF_F_VHD: /* VHD format */ + open_function = sim_vhd_disk_open; + create_function = sim_vhd_disk_create; + size_function = sim_vhd_disk_size; + break; + case DKUF_F_RAW: /* Raw Physical Disk Access */ + open_function = sim_os_disk_open_raw; + size_function = sim_os_disk_size_raw; + storage_function = sim_os_disk_info_raw; + break; + default: + return SCPE_IERR; + } +uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char));/* alloc name buf */ +uptr->disk_ctx = ctx = new disk_context(uptr); +if (uptr->filename == NULL || uptr->disk_ctx == NULL) + return _err_return (uptr, SCPE_MEM); +strncpy (uptr->filename, cptr, CBUFSIZE); /* save name */ +ctx->sector_size = (uint32)sector_size; /* save sector_size */ +ctx->xfer_element_size = (uint32)xfer_element_size; /* save xfer_element_size */ +ctx->dptr = dptr; /* save DEVICE pointer */ +ctx->dbit = dbit; /* save debug bit */ +ctx->auto_format = auto_format; /* save that we auto selected format */ +ctx->storage_sector_size = (uint32)sector_size; /* Default */ +if (sim_switches & SWMASK ('R')) { /* read only? */ + if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */ + return _err_return (uptr, SCPE_NORO); /* no, error */ + uptr->fileref = open_function (cptr, "rb"); /* open rd only */ + if (uptr->fileref == NULL) /* open fail? */ + return _err_return (uptr, SCPE_OPENERR); /* yes, error */ + uptr->flags = uptr->flags | UNIT_RO; /* set rd only */ + if (!sim_quiet) + smp_printf ("%s%d: unit is read only\n", sim_dname (dptr), sim_unit_index(uptr)); + } +else { /* normal */ + uptr->fileref = open_function (cptr, "rb+"); /* open r/w */ + if (uptr->fileref == NULL) { /* open fail? */ + if ((errno == EROFS) || (errno == EACCES)) { /* read only? */ + if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */ + return _err_return (uptr, SCPE_NORO); /* no error */ + uptr->fileref = open_function (cptr, "rb"); /* open rd only */ + if (uptr->fileref == NULL) /* open fail? */ + return _err_return (uptr, SCPE_OPENERR);/* yes, error */ + uptr->flags = uptr->flags | UNIT_RO; /* set rd only */ + if (!sim_quiet) + smp_printf ("%s%d: unit is read only\n", sim_dname (dptr), sim_unit_index(uptr)); + } + else { /* doesn't exist */ + if (sim_switches & SWMASK ('E')) /* must exist? */ + return _err_return (uptr, SCPE_OPENERR); /* yes, error */ + if (create_function) + uptr->fileref = create_function (cptr, uptr->capac);/* create new file */ + else + uptr->fileref = open_function (cptr, "wb+");/* open new file */ + if (uptr->fileref == NULL) /* open fail? */ + return _err_return (uptr, SCPE_OPENERR);/* yes, error */ + if (!sim_quiet) + smp_printf ("%s%d: creating new file\n", sim_dname (dptr), sim_unit_index(uptr)); + created = TRUE; + } + } /* end if null */ + } /* end else */ +if (DK_GET_FMT (uptr) == DKUF_F_VHD) { + if ((created) && dtype) + sim_vhd_disk_set_dtype (uptr->fileref, dtype); + if (dtype && strcmp (dtype, sim_vhd_disk_get_dtype (uptr->fileref))) { + char cmd[32]; + + sprintf (cmd, "%s%d %s", dptr->name, sim_unit_index(uptr), sim_vhd_disk_get_dtype (uptr->fileref)); + set_cmd (0, cmd); + } + } +uptr->flags = uptr->flags | UNIT_ATT; +uptr->pos = 0; + +/* Get Device attributes if they are available */ +if (storage_function) + storage_function (uptr->fileref, &ctx->storage_sector_size, &ctx->removable); + +if (created) { + t_stat r = SCPE_OK; + uint8 *secbuf = (uint8*) calloc (1, ctx->sector_size); /* alloc temp sector buf */ + + /* + On a newly created disk, we write a zero sector to the last and the + first sectors. This serves 3 purposes: + 1) it avoids strange allocation delays writing newly allocated + storage at the end of the disk during simulator operation + 2) it allocates storage for the whole disk at creation time to + avoid strange failures which may happen during simulator execution + if the containing disk is full + 3) it leaves a Sinh Format disk at the intended size so it may + subsequently be autosized with the correct size. + */ + if (secbuf == NULL) + r = SCPE_MEM; + if (r == SCPE_OK) + r = sim_disk_wrsect (uptr, (t_lba)((uptr->capac - ctx->sector_size)/ctx->sector_size), secbuf, NULL, 1); /* Write Last Sector */ + if (r == SCPE_OK) + r = sim_disk_wrsect (uptr, (t_lba)(0), secbuf, NULL, 1); /* Write First Sector */ + free (secbuf); + if (r != SCPE_OK) { + sim_disk_detach (uptr); /* report error now */ + remove (cptr); /* remove the create file */ + return SCPE_OPENERR; + } + if (pdp11tracksize) + sim_disk_pdp11_bad_block (uptr, pdp11tracksize); + } + +capac = size_function (uptr->fileref); +if (capac && (capac != (t_addr)-1)) + if (dontautosize) { + if ((capac < uptr->capac) && (DKUF_F_STD != DK_GET_FMT (uptr))) { + if (!sim_quiet) { + smp_printf ("%s%d: non expandable disk %s is smaller than simulated device (", sim_dname (dptr), sim_unit_index(uptr), cptr); + fprint_val (smp_stdout, capac, 10, T_ADDR_W, PV_LEFT); + smp_printf (" < "); + fprint_val (smp_stdout, uptr->capac, 10, T_ADDR_W, PV_LEFT); + smp_printf (")\n"); + } + } + } + else + if ((capac > uptr->capac) || (DKUF_F_STD != DK_GET_FMT (uptr))) + uptr->capac = capac; + +sim_disk_set_async (uptr, 0); +uptr->io_flush = _sim_disk_io_flush; + +return SCPE_OK; +} + +t_stat sim_disk_detach (UNIT *uptr) +{ +if (uptr == NULL) + return SCPE_IERR; + +disk_context* ctx = (disk_context*) uptr->disk_ctx; +int (*close_function)(SMP_FILE* f) = NULL; +SMP_FILE* fileref = uptr->fileref; +DEVICE *dptr; +t_bool auto_format; + +switch (DK_GET_FMT (uptr)) { /* case on format */ + case DKUF_F_STD: /* Simh */ + close_function = fclose; + break; + case DKUF_F_VHD: /* Virtual Disk */ + close_function = sim_vhd_disk_close; + break; + case DKUF_F_RAW: /* Physical */ + close_function = sim_os_disk_close_raw; + break; + } +if (!(uptr->flags & UNIT_ATTABLE)) /* attachable? */ + return SCPE_NOATT; +if (!(uptr->flags & UNIT_ATT)) /* attached? */ + return SCPE_OK; +if ((dptr = find_dev_from_unit (uptr)) == NULL) + return SCPE_OK; +auto_format = ctx->auto_format; + +if (uptr->io_flush) + uptr->io_flush (uptr); /* flush buffered data */ + +sim_disk_clr_async (uptr); + +uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_RO | UNIT_RAW); +free (uptr->filename); +uptr->filename = NULL; +uptr->fileref = NULL; +free (uptr->disk_ctx); +uptr->disk_ctx = NULL; +uptr->io_flush = NULL; +if (auto_format) + sim_disk_set_fmt (uptr, 0, "SIMH", NULL); /* restore file format */ +if (close_function (fileref) == EOF) + return SCPE_IOERR; +return SCPE_OK; +} + +/* Factory bad block table creation routine + + This routine writes a DEC standard 044 compliant bad block table on the + last track of the specified unit. The bad block table consists of 10 + repetitions of the same table, formatted as follows: + + words 0-1 pack id number + words 2-3 cylinder/sector/surface specifications + : + words n-n+1 end of table (-1,-1) + + Inputs: + uptr = pointer to unit + sec = number of sectors per surface + Outputs: + sta = status code +*/ + +t_stat sim_disk_pdp11_bad_block (UNIT *uptr, int32 sec) +{ +disk_context* ctx = (disk_context*) uptr->disk_ctx; +DEVICE *dptr; +int32 i; +t_addr da; +int32 wds = ctx->sector_size/sizeof (uint16); +uint16 *buf; + +if ((sec < 2) || (wds < 16)) + return SCPE_ARG; +if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; +if (uptr->flags & UNIT_RO) + return SCPE_RO; +if ((dptr = find_dev_from_unit (uptr)) == NULL) + return SCPE_NOATT; +if ((dptr->dwidth / dptr->aincr) <= 8) /* Must be Word oriented Capacity */ + return SCPE_IERR; +if (!get_yn ("Overwrite last track? [N]", FALSE)) + return SCPE_OK; +if ((buf = (uint16 *) malloc (wds * sizeof (uint16))) == NULL) + return SCPE_MEM; +buf[0] = buf[1] = 012345u; +buf[2] = buf[3] = 0; +for (i = 4; i < wds; i++) + buf[i] = 0177777u; +da = uptr->capac - (sec * wds); +for (i = 0; (i < sec) && (i < 10); i++, da += wds) + if (sim_disk_wrsect (uptr, (t_lba)(da/wds), (uint8*)buf, NULL, 1)) { + free (buf); + return SCPE_IOERR; + } +free (buf); +return SCPE_OK; +} + +void sim_disk_data_trace(UNIT *uptr, const uint8 *data, size_t lba, size_t len, const char* txt, int detail, uint32 reason) +{ +disk_context* ctx = (disk_context*) uptr->disk_ctx; + +if (ctx->dptr->dctrl & reason) { + sim_debug (reason, ctx->dptr, "%s%d %s lbn: %08X len: %08X\n", ctx->dptr->name, sim_unit_index(uptr), txt, lba, len); + if (detail) { + size_t i, same, group, sidx, oidx; + char outbuf[80], strbuf[18]; + static const char hex[] = "0123456789ABCDEF"; + + for (i=same=0; i 0) && (0 == memcmp (&data[i], &data[i-16], 16))) { + ++same; + continue; + } + if (same > 0) { + sim_debug (reason, ctx->dptr, "%04X thru %04X same as above\n", i-(16*same), i-1); + same = 0; + } + group = (((len - i) > 16) ? 16 : (len - i)); + for (sidx=oidx=0; sidx>4)&0xf]; + outbuf[oidx++] = hex[data[i+sidx]&0xf]; + if (isprint (data[i+sidx])) + strbuf[sidx] = data[i+sidx]; + else + strbuf[sidx] = '.'; + } + outbuf[oidx] = '\0'; + strbuf[sidx] = '\0'; + sim_debug (reason, ctx->dptr, "%04X%-48s %s\n", i, outbuf, strbuf); + } + if (same > 0) + sim_debug (reason, ctx->dptr, "%04X thru %04X same as above\n", i-(16*same), len-1); + } + } +} + + +/* OS Specific RAW Disk I/O support */ + +#if defined _WIN32 + +static void _set_errno_from_status (DWORD dwStatus) +{ +switch (dwStatus) { + case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: + case ERROR_INVALID_DRIVE: case ERROR_NO_MORE_FILES: + case ERROR_BAD_NET_NAME: case ERROR_BAD_NETPATH: + case ERROR_BAD_PATHNAME: case ERROR_FILENAME_EXCED_RANGE: + errno = ENOENT; + return; + case ERROR_INVALID_ACCESS: case ERROR_INVALID_DATA: + case ERROR_INVALID_FUNCTION: case ERROR_INVALID_PARAMETER: + case ERROR_NEGATIVE_SEEK: + errno = EINVAL; + return; + case ERROR_ARENA_TRASHED: case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_INVALID_BLOCK: case ERROR_NOT_ENOUGH_QUOTA: + errno = ENOMEM; + return; + case ERROR_TOO_MANY_OPEN_FILES: + errno = EMFILE; + return; + case ERROR_ACCESS_DENIED: case ERROR_CURRENT_DIRECTORY: + case ERROR_LOCK_VIOLATION: case ERROR_NETWORK_ACCESS_DENIED: + case ERROR_CANNOT_MAKE: case ERROR_FAIL_I24: + case ERROR_DRIVE_LOCKED: case ERROR_SEEK_ON_DEVICE: + case ERROR_NOT_LOCKED: case ERROR_LOCK_FAILED: + errno = EACCES; + return; + case ERROR_ALREADY_EXISTS: case ERROR_FILE_EXISTS: + errno = EEXIST; + return; + case ERROR_INVALID_HANDLE: case ERROR_INVALID_TARGET_HANDLE: + case ERROR_DIRECT_ACCESS_HANDLE: + errno = EBADF; + return; + case ERROR_DIR_NOT_EMPTY: + errno = ENOTEMPTY; + return; + case ERROR_BAD_ENVIRONMENT: + errno = E2BIG; + return; + case ERROR_BAD_FORMAT: + errno = ENOEXEC; + return; + case ERROR_NOT_SAME_DEVICE: + errno = EXDEV; + return; + case ERROR_BROKEN_PIPE: + errno = EPIPE; + return; + case ERROR_DISK_FULL: + errno = ENOSPC; + return; + case ERROR_WAIT_NO_CHILDREN: case ERROR_CHILD_NOT_COMPLETE: + errno = ECHILD; + return; + case ERROR_NO_PROC_SLOTS: case ERROR_MAX_THRDS_REACHED: + case ERROR_NESTING_NOT_ALLOWED: + errno = EAGAIN; + return; + } +if ((dwStatus >= ERROR_WRITE_PROTECT) && (dwStatus <= ERROR_SHARING_BUFFER_EXCEEDED)) { + errno = EACCES; + return; + } +if ((dwStatus >= ERROR_INVALID_STARTING_CODESEG) && (dwStatus <= ERROR_INFLOOP_IN_RELOC_CHAIN)) { + errno = ENOEXEC; + return; + } +errno = EINVAL; +} +#include +struct _device_type { + int32 Type; + char *desc; + } DeviceTypes[] = { + {FILE_DEVICE_8042_PORT, "8042_PORT"}, + {FILE_DEVICE_ACPI, "ACPI"}, + {FILE_DEVICE_BATTERY, "BATTERY"}, + {FILE_DEVICE_BEEP, "BEEP"}, +#ifdef FILE_DEVICE_BLUETOOTH + {FILE_DEVICE_BLUETOOTH, "BLUETOOTH"}, +#endif + {FILE_DEVICE_BUS_EXTENDER, "BUS_EXTENDER"}, + {FILE_DEVICE_CD_ROM, "CD_ROM"}, + {FILE_DEVICE_CD_ROM_FILE_SYSTEM, "CD_ROM_FILE_SYSTEM"}, + {FILE_DEVICE_CHANGER, "CHANGER"}, + {FILE_DEVICE_CONTROLLER, "CONTROLLER"}, +#ifdef FILE_DEVICE_CRYPT_PROVIDER + {FILE_DEVICE_CRYPT_PROVIDER, "CRYPT_PROVIDER"}, +#endif + {FILE_DEVICE_DATALINK, "DATALINK"}, + {FILE_DEVICE_DFS, "DFS"}, + {FILE_DEVICE_DFS_FILE_SYSTEM, "DFS_FILE_SYSTEM"}, + {FILE_DEVICE_DFS_VOLUME, "DFS_VOLUME"}, + {FILE_DEVICE_DISK, "DISK"}, + {FILE_DEVICE_DISK_FILE_SYSTEM, "DISK_FILE_SYSTEM"}, + {FILE_DEVICE_DVD, "DVD"}, + {FILE_DEVICE_FILE_SYSTEM, "FILE_SYSTEM"}, +#ifdef FILE_DEVICE_FIPS + {FILE_DEVICE_FIPS, "FIPS"}, +#endif + {FILE_DEVICE_FULLSCREEN_VIDEO, "FULLSCREEN_VIDEO"}, +#ifdef FILE_DEVICE_INFINIBAND + {FILE_DEVICE_INFINIBAND, "INFINIBAND"}, +#endif + {FILE_DEVICE_INPORT_PORT, "INPORT_PORT"}, + {FILE_DEVICE_KEYBOARD, "KEYBOARD"}, + {FILE_DEVICE_KS, "KS"}, + {FILE_DEVICE_KSEC, "KSEC"}, + {FILE_DEVICE_MAILSLOT, "MAILSLOT"}, + {FILE_DEVICE_MASS_STORAGE, "MASS_STORAGE"}, + {FILE_DEVICE_MIDI_IN, "MIDI_IN"}, + {FILE_DEVICE_MIDI_OUT, "MIDI_OUT"}, + {FILE_DEVICE_MODEM, "MODEM"}, + {FILE_DEVICE_MOUSE, "MOUSE"}, + {FILE_DEVICE_MULTI_UNC_PROVIDER, "MULTI_UNC_PROVIDER"}, + {FILE_DEVICE_NAMED_PIPE, "NAMED_PIPE"}, + {FILE_DEVICE_NETWORK, "NETWORK"}, + {FILE_DEVICE_NETWORK_BROWSER, "NETWORK_BROWSER"}, + {FILE_DEVICE_NETWORK_FILE_SYSTEM, "NETWORK_FILE_SYSTEM"}, + {FILE_DEVICE_NETWORK_REDIRECTOR, "NETWORK_REDIRECTOR"}, + {FILE_DEVICE_NULL, "NULL"}, + {FILE_DEVICE_PARALLEL_PORT, "PARALLEL_PORT"}, + {FILE_DEVICE_PHYSICAL_NETCARD, "PHYSICAL_NETCARD"}, + {FILE_DEVICE_PRINTER, "PRINTER"}, + {FILE_DEVICE_SCANNER, "SCANNER"}, + {FILE_DEVICE_SCREEN, "SCREEN"}, + {FILE_DEVICE_SERENUM, "SERENUM"}, + {FILE_DEVICE_SERIAL_MOUSE_PORT, "SERIAL_MOUSE_PORT"}, + {FILE_DEVICE_SERIAL_PORT, "SERIAL_PORT"}, + {FILE_DEVICE_SMARTCARD, "SMARTCARD"}, + {FILE_DEVICE_SMB, "SMB"}, + {FILE_DEVICE_SOUND, "SOUND"}, + {FILE_DEVICE_STREAMS, "STREAMS"}, + {FILE_DEVICE_TAPE, "TAPE"}, + {FILE_DEVICE_TAPE_FILE_SYSTEM, "TAPE_FILE_SYSTEM"}, + {FILE_DEVICE_TERMSRV, "TERMSRV"}, + {FILE_DEVICE_TRANSPORT, "TRANSPORT"}, + {FILE_DEVICE_UNKNOWN, "UNKNOWN"}, + {FILE_DEVICE_VDM, "VDM"}, + {FILE_DEVICE_VIDEO, "VIDEO"}, + {FILE_DEVICE_VIRTUAL_DISK, "VIRTUAL_DISK"}, +#ifdef FILE_DEVICE_VMBUS + {FILE_DEVICE_VMBUS, "VMBUS"}, +#endif + {FILE_DEVICE_WAVE_IN, "WAVE_IN"}, + {FILE_DEVICE_WAVE_OUT, "WAVE_OUT"}, +#ifdef FILE_DEVICE_WPD + {FILE_DEVICE_WPD, "WPD"}, +#endif + {0, NULL}}; + +static const char *_device_type_name (int DeviceType) +{ +int i; + +for (i=0; DeviceTypes[i].desc; i++) + if (DeviceTypes[i].Type == DeviceType) + return DeviceTypes[i].desc; +return "Unknown"; +} + +static t_stat sim_os_disk_implemented_raw (void) +{ +return SCPE_OK; +} + +static SMP_FILE* sim_os_disk_open_raw (const char *rawdevicename, const char *openmode) +{ +HANDLE Handle; +DWORD DesiredAccess = 0; + +if (strchr (openmode, 'r')) + DesiredAccess |= GENERIC_READ; +if (strchr (openmode, 'w') || strchr (openmode, '+')) + DesiredAccess |= GENERIC_WRITE; +Handle = CreateFileA (rawdevicename, DesiredAccess, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS|FILE_FLAG_WRITE_THROUGH, NULL); +if (Handle == INVALID_HANDLE_VALUE) { + _set_errno_from_status (GetLastError ()); + return NULL; + } +return (SMP_FILE* )Handle; +} + +static int sim_os_disk_close_raw (SMP_FILE* f) +{ +if (!CloseHandle ((HANDLE)f)) { + _set_errno_from_status (GetLastError ()); + return EOF; + } +return 0; +} + +static void sim_os_disk_flush_raw (SMP_FILE* f) +{ +FlushFileBuffers ((HANDLE)f); +} + +static t_addr sim_os_disk_size_raw (SMP_FILE* Disk) +{ +DWORD IoctlReturnSize; +LARGE_INTEGER Size; +WINBASEAPI BOOL WINAPI GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize); + +if (GetFileSizeEx((HANDLE)Disk, &Size)) + return (t_addr)(Size.QuadPart); +#ifdef IOCTL_STORAGE_READ_CAPACITY +if (1) { + STORAGE_READ_CAPACITY S; + + ZeroMemory (&S, sizeof (S)); + S.Version = sizeof (STORAGE_READ_CAPACITY); + if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_STORAGE_READ_CAPACITY, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &S, /* output buffer */ + (DWORD) sizeof(S), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + return (t_addr)(S.DiskLength.QuadPart); + } +#endif +#ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY_EX +if (1) { + DISK_GEOMETRY_EX G; + + ZeroMemory (&G, sizeof (G)); + if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &G, /* output buffer */ + (DWORD) sizeof(G), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + return (t_addr)(G.DiskSize.QuadPart); + } +#endif +#ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY +if (1) { + DISK_GEOMETRY G; + + if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_DISK_GET_DRIVE_GEOMETRY, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &G, /* output buffer */ + (DWORD) sizeof(G), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + return (t_addr)(G.Cylinders.QuadPart*G.TracksPerCylinder*G.SectorsPerTrack*G.BytesPerSector); + } +#endif +_set_errno_from_status (GetLastError ()); +return (t_addr)-1; +} + +static t_stat sim_os_disk_unload_raw (SMP_FILE* Disk) +{ +#ifdef IOCTL_STORAGE_EJECT_MEDIA +DWORD BytesReturned; +uint32 Removable = FALSE; + +sim_os_disk_info_raw (Disk, NULL, &Removable); +if (Removable) { + if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */ + IOCTL_STORAGE_EJECT_MEDIA, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + NULL, /* lpOutBuffer */ + 0, /* nOutBufferSize */ + (LPDWORD) &BytesReturned, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) { /* OVERLAPPED structure */ + _set_errno_from_status (GetLastError ()); + return SCPE_IOERR; + } + } +return SCPE_OK; +#else +return SCPE_NOFNC; +#endif +} + +static t_bool sim_os_disk_isavailable_raw (SMP_FILE* Disk) +{ +#ifdef IOCTL_STORAGE_EJECT_MEDIA +DWORD BytesReturned; +uint32 Removable = FALSE; + +sim_os_disk_info_raw (Disk, NULL, &Removable); +if (Removable) { + if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */ + IOCTL_STORAGE_CHECK_VERIFY, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + NULL, /* lpOutBuffer */ + 0, /* nOutBufferSize */ + (LPDWORD) &BytesReturned, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) { /* OVERLAPPED structure */ + _set_errno_from_status (GetLastError ()); + return FALSE; + } + } +#endif +return TRUE; +} + +static t_stat sim_os_disk_info_raw (SMP_FILE* Disk, uint32 *sector_size, uint32 *removable) +{ +DWORD IoctlReturnSize; +#ifndef __GNUC__ +STORAGE_DEVICE_NUMBER Device; + +ZeroMemory (&Device, sizeof (Device)); +if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_STORAGE_GET_DEVICE_NUMBER, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &Device, /* output buffer */ + (DWORD) sizeof(Device), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + smp_printf ("Device OK - Type: %s, Number: %d\n", _device_type_name (Device.DeviceType), Device.DeviceNumber); +#endif + +if (sector_size) + *sector_size = 512; +if (removable) + *removable = 0; +#ifdef IOCTL_STORAGE_READ_CAPACITY +if (1) { + STORAGE_READ_CAPACITY S; + + ZeroMemory (&S, sizeof (S)); + S.Version = sizeof (STORAGE_READ_CAPACITY); + if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_STORAGE_READ_CAPACITY, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &S, /* output buffer */ + (DWORD) sizeof(S), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + if (sector_size) + *sector_size = S.BlockLength; + } +#endif +#ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY_EX +if (1) { + DISK_GEOMETRY_EX G; + + ZeroMemory (&G, sizeof (G)); + if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &G, /* output buffer */ + (DWORD) sizeof(G), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + if (sector_size) + *sector_size = G.Geometry.BytesPerSector; + } +#endif +#ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY +if (1) { + DISK_GEOMETRY G; + + if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_DISK_GET_DRIVE_GEOMETRY, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &G, /* output buffer */ + (DWORD) sizeof(G), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + if (sector_size) + *sector_size = G.BytesPerSector; + } +#endif +#ifdef IOCTL_STORAGE_GET_HOTPLUG_INFO +if (1) { + STORAGE_HOTPLUG_INFO H; + + ZeroMemory (&H, sizeof (H)); + if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ + IOCTL_STORAGE_GET_HOTPLUG_INFO, /* dwIoControlCode */ + NULL, /* lpInBuffer */ + 0, /* nInBufferSize */ + (LPVOID) &H, /* output buffer */ + (DWORD) sizeof(H), /* size of output buffer */ + (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ + (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ + if (removable) + *removable = H.MediaRemovable; + } +#endif +if (removable && *removable) + smp_printf ("Removable Device\n"); +return SCPE_OK; +} + +static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) +{ +OVERLAPPED pos; +disk_context* ctx = (disk_context*) uptr->disk_ctx; +long long addr; + +sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", sim_unit_index(uptr), lba, sects); + +addr = ((long long)lba) * ctx->sector_size; +memset (&pos, 0, sizeof (pos)); +pos.Offset = (DWORD)addr; +pos.OffsetHigh = (DWORD)(addr >> 32); +if (ReadFile ((HANDLE)(uptr->fileref), buf, sects * ctx->sector_size, (LPDWORD)sectsread, &pos)) { + if (sectsread) + *sectsread /= ctx->sector_size; + return SCPE_OK; + } +_set_errno_from_status (GetLastError ()); +return SCPE_IOERR; +} + +static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) +{ +OVERLAPPED pos; +disk_context* ctx = (disk_context*) uptr->disk_ctx; +long long addr; + +sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", sim_unit_index(uptr), lba, sects); + +addr = ((long long)lba) * ctx->sector_size; +memset (&pos, 0, sizeof (pos)); +pos.Offset = (DWORD)addr; +pos.OffsetHigh = (DWORD)(addr >> 32); +if (WriteFile ((HANDLE)(uptr->fileref), buf, sects * ctx->sector_size, (LPDWORD)sectswritten, &pos)) { + if (sectswritten) + *sectswritten /= ctx->sector_size; + return SCPE_OK; + } +_set_errno_from_status (GetLastError ()); +return SCPE_IOERR; +} + +#elif defined (__linux) || defined (__sun__) || defined(__APPLE__) + +#include +#include +#include +#include + +static t_stat sim_os_disk_implemented_raw (void) +{ +return SCPE_OK; +} + +static SMP_FILE* sim_os_disk_open_raw (const char *rawdevicename, const char *openmode) +{ +int fd; +int mode = 0; + +if (strchr (openmode, 'r') && (strchr (openmode, '+') || strchr (openmode, 'w'))) + mode = O_RDWR; +else + if (strchr (openmode, 'r')) + mode = O_RDONLY; +#ifdef O_LARGEFILE +mode |= O_LARGEFILE; +#endif +#ifdef O_DSYNC +mode |= O_DSYNC; +#endif +return (SMP_FILE* )((long)open (rawdevicename, mode, 0)); +} + +static int sim_os_disk_close_raw (SMP_FILE* f) +{ +return close ((int)((long)f)); +} + +static void sim_os_disk_flush_raw (SMP_FILE* f) +{ +fsync ((int)((long)f)); +} + +static t_addr sim_os_disk_size_raw (SMP_FILE* f) +{ +#if defined(__APPLE__) +struct stat statb; + +if (sizeof(statb.st_size) != sizeof(t_addr) || fstat ((int)((long)f), &statb)) + return (t_addr)-1; +return (t_addr)statb.st_size; +#else +struct stat64 statb; + +if (fstat64 ((int)((long)f), &statb)) + return (t_addr)-1; +return (t_addr)statb.st_size; +#endif +} + +static t_stat sim_os_disk_unload_raw (SMP_FILE* f) +{ +return SCPE_IOERR; +} + +static t_bool sim_os_disk_isavailable_raw (SMP_FILE* Disk) +{ +return TRUE; +} + +static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) +{ +disk_context* ctx = (disk_context*) uptr->disk_ctx; +off_t addr; +ssize_t bytesread; + +sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", sim_unit_index(uptr), lba, sects); + +addr = ((off_t)lba) * ctx->sector_size; +bytesread = pread((int)((long)uptr->fileref), buf, sects * ctx->sector_size, addr); +if (bytesread < 0) { + if (sectsread) + *sectsread = 0; + return SCPE_IOERR; + } +if (sectsread) + *sectsread = bytesread / ctx->sector_size; +return SCPE_OK; +} + +static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) +{ +disk_context* ctx = (disk_context*) uptr->disk_ctx; +off_t addr; +ssize_t byteswritten; + +sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", sim_unit_index(uptr), lba, sects); + +addr = ((off_t)lba) * ctx->sector_size; +byteswritten = pwrite((int)((long)uptr->fileref), buf, sects * ctx->sector_size, addr); +if (byteswritten < 0) { + if (sectswritten) + *sectswritten = 0; + return SCPE_IOERR; + } +if (sectswritten) + *sectswritten = byteswritten / ctx->sector_size; +return SCPE_OK; +} + +static t_stat sim_os_disk_info_raw (SMP_FILE* f, uint32 *sector_size, uint32 *removable) +{ +if (sector_size) + *sector_size = 512; +if (removable) + *removable = 0; +return SCPE_OK; +} + +#else +/*============================================================================*/ +/* Non-implemented versions */ +/*============================================================================*/ + +static t_stat sim_os_disk_implemented_raw (void) +{ +return SCPE_NOFNC; +} + +static SMP_FILE* sim_os_disk_open_raw (const char *rawdevicename, const char *openmode) +{ +return NULL; +} + +static int sim_os_disk_close_raw (SMP_FILE* f) +{ +return EOF; +} + +static void sim_os_disk_flush_raw (SMP_FILE* f) +{ +} + +static t_addr sim_os_disk_size_raw (SMP_FILE* f) +{ +return (t_addr)-1; +} + +static t_stat sim_os_disk_unload_raw (SMP_FILE* f) +{ +return SCPE_NOFNC; +} + +static t_bool sim_os_disk_isavailable_raw (SMP_FILE* Disk) +{ +return FALSE; +} + +static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) +{ +return SCPE_NOFNC; +} + +static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) +{ +return SCPE_NOFNC; +} + +static t_stat sim_os_disk_info_raw (SMP_FILE* f, uint32 *sector_size, uint32 *removable) +{ +return SCPE_NOFNC; +} + +#endif + +/* OS Independent Disk Virtual Disk (VHD) I/O support */ + +#if (defined (VMS) && !(defined (__ALPHA) || defined (__ia64))) +#define DONT_DO_VHD_SUPPORT /* VAX/VMS compilers don't have 64 bit integers */ +#endif + +#if defined (DONT_DO_VHD_SUPPORT) + +/*============================================================================*/ +/* Non-implemented version */ +/* This is only for hody systems which don't have 64 bit integer types */ +/*============================================================================*/ + +static t_stat sim_vhd_disk_implemented (void) +{ +return SCPE_NOFNC; +} + +static SMP_FILE* sim_vhd_disk_open (const char *rawdevicename, const char *openmode) +{ +return NULL; +} + +static SMP_FILE* sim_vhd_disk_create (const char *szVHDPath, t_addr desiredsize) +{ +return NULL; +} + +static SMP_FILE* sim_vhd_disk_create_diff (const char *szVHDPath, const char *szParentVHDPath) +{ +return NULL; +} + +static int sim_vhd_disk_close (SMP_FILE* f) +{ +return -1; +} + +static void sim_vhd_disk_flush (SMP_FILE* f) +{ +} + +static t_addr sim_vhd_disk_size (SMP_FILE* f) +{ +return (t_addr)-1; +} + +static t_stat sim_vhd_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) +{ +return SCPE_IOERR; +} + +static t_stat sim_vhd_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) +{ +return SCPE_IOERR; +} + +static t_stat sim_vhd_disk_set_dtype (SMP_FILE* f, const char *dtype) +{ +return SCPE_NOFNC; +} + +static const char *sim_vhd_disk_get_dtype (SMP_FILE* f) +{ +return NULL; +} + +#else + +/*++ + This code follows the details specified in the "Virtual Hard Disk Image + Format Specification", Version 1.0 October 11, 2006. +--*/ + +typedef t_uint64 uint64; +typedef t_int64 int64; + +typedef struct _VHD_Footer { + /* + Cookies are used to uniquely identify the original creator of the hard disk + image. The values are case-sensitive. Microsoft uses the “conectix” string + to identify this file as a hard disk image created by Microsoft Virtual + Server, Virtual PC, and predecessor products. The cookie is stored as an + eight-character ASCII string with the “c” in the first byte, the “o” in + the second byte, and so on. + */ + char Cookie[8]; + /* + This is a bit field used to indicate specific feature support. The following + table displays the list of features. + Any fields not listed are reserved. + + Feature Value: + No features enabled 0x00000000 + Temporary 0x00000001 + Reserved 0x00000002 + + No features enabled. + The hard disk image has no special features enabled in it. + Temporary. + This bit is set if the current disk is a temporary disk. A + temporary disk designation indicates to an application that + this disk is a candidate for deletion on shutdown. + Reserved. + This bit must always be set to 1. + All other bits are also reserved and should be set to 0. + */ + uint32 Features; + /* + This field is divided into a major/minor version and matches the version of + the specification used in creating the file. The most-significant two bytes + are for the major version. The least-significant two bytes are the minor + version. This must match the file format specification. For the current + specification, this field must be initialized to 0x00010000. + The major version will be incremented only when the file format is modified + in such a way that it is no longer compatible with older versions of the + file format. + */ + uint32 FileFormatVersion; + /* + This field holds the absolute byte offset, from the beginning of the file, + to the next structure. This field is used for dynamic disks and differencing + disks, but not fixed disks. For fixed disks, this field should be set to + 0xFFFFFFFF. + */ + uint64 DataOffset; + /* + This field stores the creation time of a hard disk image. This is the number + of seconds since January 1, 2000 12:00:00 AM in UTC/GMT. + */ + uint32 TimeStamp; + /* + This field is used to document which application created the hard disk. The + field is a left-justified text field. It uses a single-byte character set. + If the hard disk is created by Microsoft Virtual PC, "vpc " is written in + this field. If the hard disk image is created by Microsoft Virtual Server, + then "vs " is written in this field. + Other applications should use their own unique identifiers. + */ + char CreatorApplication[4]; + /* + This field holds the major/minor version of the application that created + the hard disk image. Virtual Server 2004 sets this value to 0x00010000 and + Virtual PC 2004 sets this to 0x00050000. + */ + uint32 CreatorVersion; + /* + This field stores the type of host operating system this disk image is + created on. + Host OS type Value + Windows 0x5769326B (Wi2k) + Macintosh 0x4D616320 (Mac ) + */ + uint8 CreatorHostOS[4]; + /* + This field stores the size of the hard disk in bytes, from the perspective + of the virtual machine, at creation time. This field is for informational + purposes. + */ + uint64 OriginalSize; + /* + This field stores the current size of the hard disk, in bytes, from the + perspective of the virtual machine. + This value is same as the original size when the hard disk is created. + This value can change depending on whether the hard disk is expanded. + */ + uint64 CurrentSize; + /* + This field stores the cylinder, heads, and sectors per track value for the + hard disk. + Disk Geometry field Size (bytes) + Cylinder 2 + Heads 1 + Sectors per track/cylinder 1 + + When a hard disk is configured as an ATA hard disk, the CHS values (that is, + Cylinder, Heads, Sectors per track) are used by the ATA controller to + determine the size of the disk. When the user creates a hard disk of a + certain size, the size of the hard disk image in the virtual machine is + smaller than that created by the user. This is because CHS value calculated + from the hard disk size is rounded down. The pseudo-code for the algorithm + used to determine the CHS values can be found in the appendix of this + document. + */ + uint32 DiskGeometry; + /* + Disk Type field Value + None 0 + Reserved (deprecated) 1 + Fixed hard disk 2 + Dynamic hard disk 3 + Differencing hard disk 4 + Reserved (deprecated) 5 + Reserved (deprecated) 6 + */ + uint32 DiskType; + /* + This field holds a basic checksum of the hard disk footer. It is just a + one’s complement of the sum of all the bytes in the footer without the + checksum field. + If the checksum verification fails, the Virtual PC and Virtual Server + products will instead use the header. If the checksum in the header also + fails, the file should be assumed to be corrupt. The pseudo-code for the + algorithm used to determine the checksum can be found in the appendix of + this document. + */ + uint32 Checksum; + /* + Every hard disk has a unique ID stored in the hard disk. This is used to + identify the hard disk. This is a 128-bit universally unique identifier + (UUID). This field is used to associate a parent hard disk image with its + differencing hard disk image(s). + */ + uint8 UniqueID[16]; + /* + This field holds a one-byte flag that describes whether the system is in + saved state. If the hard disk is in the saved state the value is set to 1. + Operations such as compaction and expansion cannot be performed on a hard + disk in a saved state. + */ + uint8 SavedState; + /* + This field contains zeroes. It is 427 bytes in size. + */ + uint8 Reserved1[11]; + /* + This field is an extension to the VHD spec and includes a simh drive type + name as a nul terminated string. + */ + uint8 DriveType[16]; + /* + This field contains zeroes. It is 400 bytes in size. + */ + uint8 Reserved[400]; + } VHD_Footer; + +/* +For dynamic and differencing disk images, the “Data Offset” field within +the image footer points to a secondary structure that provides additional +information about the disk image. The dynamic disk header should appear on +a sector (512-byte) boundary. +*/ +typedef struct _VHD_DynamicDiskHeader { + /* + This field holds the value "cxsparse". This field identifies the header. + */ + char Cookie[8]; + /* + This field contains the absolute byte offset to the next structure in the + hard disk image. It is currently unused by existing formats and should be + set to 0xFFFFFFFF. + */ + uint64 DataOffset; + /* + This field stores the absolute byte offset of the Block Allocation Table + (BAT) in the file. + */ + uint64 TableOffset; + /* + This field stores the version of the dynamic disk header. The field is + divided into Major/Minor version. The least-significant two bytes represent + the minor version, and the most-significant two bytes represent the major + version. This must match with the file format specification. For this + specification, this field must be initialized to 0x00010000. + The major version will be incremented only when the header format is + modified in such a way that it is no longer compatible with older versions + of the product. + */ + uint32 HeaderVersion; + /* + This field holds the maximum entries present in the BAT. This should be + equal to the number of blocks in the disk (that is, the disk size divided + by the block size). + */ + uint32 MaxTableEntries; + /* + A block is a unit of expansion for dynamic and differencing hard disks. It + is stored in bytes. This size does not include the size of the block bitmap. + It is only the size of the data section of the block. The sectors per block + must always be a power of two. The default value is 0x00200000 (indicating a + block size of 2 MB). + */ + uint32 BlockSize; + /* + This field holds a basic checksum of the dynamic header. It is a one’s + complement of the sum of all the bytes in the header without the checksum + field. + If the checksum verification fails the file should be assumed to be corrupt. + */ + uint32 Checksum; + /* + This field is used for differencing hard disks. A differencing hard disk + stores a 128-bit UUID of the parent hard disk. For more information, see + “Creating Differencing Hard Disk Images” later in this paper. + */ + uint8 ParentUniqueID[16]; + /* + This field stores the modification time stamp of the parent hard disk. This + is the number of seconds since January 1, 2000 12:00:00 AM in UTC/GMT. + */ + uint32 ParentTimeStamp; + /* + This field should be set to zero. + */ + uint32 Reserved0; + /* + This field contains a Unicode string (UTF-16) of the parent hard disk + filename. + */ + char ParentUnicodeName[512]; + /* + These entries store an absolute byte offset in the file where the parent + locator for a differencing hard disk is stored. This field is used only for + differencing disks and should be set to zero for dynamic disks. + */ + struct VHD_ParentLocator { + /* + The platform code describes which platform-specific format is used for the + file locator. For Windows, a file locator is stored as a path (for example. + “c:\disksimages\ParentDisk.vhd”). On a Macintosh system, the file locator + is a binary large object (blob) that contains an “alias.” The parent locator + table is used to support moving hard disk images across platforms. + Some current platform codes include the following: + Platform Code Description + None (0x0) + Wi2r (0x57693272) [deprecated] + Wi2k (0x5769326B) [deprecated] + W2ru (0x57327275) Unicode pathname (UTF-16) on Windows relative to the differencing disk pathname. + W2ku (0x57326B75) Absolute Unicode (UTF-16) pathname on Windows. + Mac (0x4D616320) (Mac OS alias stored as a blob) + MacX(0x4D616358) A file URL with UTF-8 encoding conforming to RFC 2396. + */ + uint8 PlatformCode[4]; + /* + This field stores the number of 512-byte sectors needed to store the parent + hard disk locator. + */ + uint32 PlatformDataSpace; + /* + This field stores the actual length of the parent hard disk locator in bytes. + */ + uint32 PlatformDataLength; + /* + This field must be set to zero. + */ + uint32 Reserved; + /* + This field stores the absolute file offset in bytes where the platform + specific file locator data is stored. + */ + uint64 PlatformDataOffset; + /* + This field stores the absolute file offset in bytes where the platform + specific file locator data is stored. + */ + } ParentLocatorEntries[8]; + /* + This must be initialized to zeroes. + */ + char Reserved[256]; + } VHD_DynamicDiskHeader; + +#define VHD_BAT_FREE_ENTRY (0xFFFFFFFF) +#define VHD_DATA_BLOCK_ALIGNMENT ((uint64)4096) /* Optimum when underlying storage has 4k sectors */ + +static char *VHD_DiskTypes[] = + { + "None", /* 0 */ + "Reserved (deprecated)", /* 1 */ + "Fixed hard disk", /* 2 */ +#define VHD_DT_Fixed 2 + "Dynamic hard disk", /* 3 */ +#define VHD_DT_Dynamic 3 + "Differencing hard disk", /* 4 */ +#define VHD_DT_Differencing 4 + "Reserved (deprecated)", /* 5 */ + "Reserved (deprecated)", /* 6 */ + }; + +static uint32 NtoHl(uint32 value); + +static uint64 NtoHll(uint64 value); + +typedef struct VHD_IOData *VHDHANDLE; + +static t_stat ReadFilePosition(SMP_FILE* File, void *buf, size_t bufsize, size_t *bytesread, uint64 position) +{ +uint32 err = sim_fseek (File, (t_addr)position, SEEK_SET); +size_t i; + +if (!err) { + i = fread (buf, 1, bufsize, File); + err = ferror (File); + if ((!err) && bytesread) + *bytesread = i; + } +return (err ? SCPE_IOERR : SCPE_OK); +} + +static t_stat WriteFilePosition(SMP_FILE* File, void *buf, size_t bufsize, size_t *byteswritten, uint64 position) +{ +uint32 err = sim_fseek (File, (t_addr)position, SEEK_SET); +size_t i; + +if (!err) { + i = fwrite (buf, 1, bufsize, File); + err = ferror (File); + if ((!err) && byteswritten) + *byteswritten = i; + } +return (err ? SCPE_IOERR : SCPE_OK); +} + +static uint32 +CalculateVhdFooterChecksum(void *data, + size_t size) +{ +uint32 sum = 0; +uint8 *c = (uint8 *)data; + +while (size--) + sum += *c++; +return ~sum; +} + +#if defined (__ALPHA) || defined (__ia64) || defined (VMS) || defined(__x86_32__) || defined(__x86_64__) +#ifndef __BYTE_ORDER__ +#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ +#endif +#endif +#ifndef __BYTE_ORDER__ +#define __BYTE_ORDER__ UNKNOWN +#endif +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +static uint32 +NtoHl(uint32 value) +{ +uint8 *l = (uint8 *)&value; +return l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24); +} + +static uint64 +NtoHll(uint64 value) +{ +uint8 *l = (uint8 *)&value; +uint64 highresult = l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24); +uint32 lowresult = l[7] | (l[6]<<8) | (l[5]<<16) | (l[4]<<24); +return (highresult << 32) | lowresult; +} +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +static uint32 +NtoHl(uint32 value) +{ +return value; +} + +static uint64 +NtoHll(uint64 value) +{ +return value; +} +#else +static uint32 +NtoHl(uint32 value) +{ +uint8 *l = (uint8 *)&value; + +if (sim_end) + return l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24); +return value; +} + +static uint64 +NtoHll(uint64 value) +{ +uint8 *l = (uint8 *)&value; + +if (sim_end) { + uint64 highresult = l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24); + uint32 lowresult = l[7] | (l[6]<<8) | (l[5]<<16) | (l[4]<<24); + return (highresult << 32) | lowresult; + } +return value; +} +#endif + +static +int +GetVHDFooter(const char *szVHDPath, + VHD_Footer *sFooter, + VHD_DynamicDiskHeader *sDynamic, + uint32 **aBAT, + uint32 *ModifiedTimeStamp, + char *szParentVHDPath, + size_t ParentVHDPathSize) +{ +SMP_FILE* File = NULL; +uint64 position; +uint32 sum, saved_sum; +int Return = 0; +VHD_Footer sHeader; +struct stat statb; + +if (sFooter) + memset(sFooter, '\0', sizeof(*sFooter)); +if (sDynamic) + memset(sDynamic, '\0', sizeof(*sDynamic)); +if (aBAT) + *aBAT = NULL; +File = sim_fopen (szVHDPath, "rb"); +if (!File) { + Return = errno; + goto Return_Cleanup; + } +if (ModifiedTimeStamp) + if (stat (szVHDPath, &statb)) { + Return = errno; + goto Return_Cleanup; + } + else + *ModifiedTimeStamp = NtoHl ((uint32)(statb.st_mtime-946684800)); +position = sim_fsize_ex (File); +if (((int64)position) == -1) { + Return = errno; + goto Return_Cleanup; + } +position -= sizeof(*sFooter); +if (ReadFilePosition(File, + sFooter, + sizeof(*sFooter), + NULL, + position)) { + Return = errno; + goto Return_Cleanup; + } +saved_sum = NtoHl(sFooter->Checksum); +sFooter->Checksum = 0; +sum = CalculateVhdFooterChecksum(sFooter, sizeof(*sFooter)); +sFooter->Checksum = NtoHl(saved_sum); +if ((sum != saved_sum) || (memcmp("conectix", sFooter->Cookie, sizeof(sFooter->Cookie)))) { + Return = EINVAL; /* File Corrupt */ + goto Return_Cleanup; + } +if (ReadFilePosition(File, + &sHeader, + sizeof(sHeader), + NULL, + (uint64)0)) { + Return = errno; + goto Return_Cleanup; + } +if ((NtoHl(sFooter->DiskType) != VHD_DT_Dynamic) && + (NtoHl(sFooter->DiskType) != VHD_DT_Differencing) && + (NtoHl(sFooter->DiskType) != VHD_DT_Fixed)) { + Return = EINVAL; /* File Corrupt */ + goto Return_Cleanup; + } +if (((NtoHl(sFooter->DiskType) == VHD_DT_Dynamic) || + (NtoHl(sFooter->DiskType) == VHD_DT_Differencing)) && + memcmp(sFooter, &sHeader, sizeof(sHeader))) { + Return = EINVAL; /* File Corrupt */ + goto Return_Cleanup; + } +if ((sDynamic) && + ((NtoHl(sFooter->DiskType) == VHD_DT_Dynamic) || + (NtoHl(sFooter->DiskType) == VHD_DT_Differencing))) { + if (ReadFilePosition(File, + sDynamic, + sizeof (*sDynamic), + NULL, + NtoHll (sFooter->DataOffset))) { + Return = errno; + goto Return_Cleanup; + } + saved_sum = NtoHl (sDynamic->Checksum); + sDynamic->Checksum = 0; + sum = CalculateVhdFooterChecksum (sDynamic, sizeof(*sDynamic)); + sDynamic->Checksum = NtoHl (saved_sum); + if ((sum != saved_sum) || (memcmp ("cxsparse", sDynamic->Cookie, sizeof (sDynamic->Cookie)))) { + Return = errno; + goto Return_Cleanup; + } + if (aBAT) + { + *aBAT = (uint32*) malloc(512*((sizeof(**aBAT)*NtoHl(sDynamic->MaxTableEntries)+511)/512)); + if (ReadFilePosition(File, + *aBAT, + sizeof (**aBAT)*NtoHl(sDynamic->MaxTableEntries), + NULL, + NtoHll (sDynamic->TableOffset))) { + Return = EINVAL; /* File Corrupt */ + goto Return_Cleanup; + } + } + if (szParentVHDPath && ParentVHDPathSize) { + VHD_Footer sParentFooter; + + memset (szParentVHDPath, '\0', ParentVHDPathSize); + if (NtoHl (sFooter->DiskType) == VHD_DT_Differencing) + { + size_t i, j; + + for (j=0; j<8; ++j) + { + uint8 *Pdata; + char ParentName[256]; + char CheckPath[256]; + uint32 ParentModificationTime; + + if ('\0' == sDynamic->ParentLocatorEntries[j].PlatformCode[0]) + continue; + memset (ParentName, '\0', sizeof(ParentName)); + memset (CheckPath, '\0', sizeof(CheckPath)); + Pdata = (uint8*) calloc (1, NtoHl(sDynamic->ParentLocatorEntries[j].PlatformDataSpace)+1); + if (!Pdata) + continue; + if (ReadFilePosition(File, + Pdata, + NtoHl (sDynamic->ParentLocatorEntries[j].PlatformDataSpace), + NULL, + NtoHll (sDynamic->ParentLocatorEntries[j].PlatformDataOffset))) { + free (Pdata); + continue; + } + for (i=0; iParentLocatorEntries[j].PlatformDataLength); i+=2) + if ((Pdata[i] == '\0') && (Pdata[i+1] == '\0')) { + ParentName[i/2] = '\0'; + break; + } + else + ParentName[i/2] = Pdata[i] ? Pdata[i] : Pdata[i+1]; + free (Pdata); + if (0 == memcmp (sDynamic->ParentLocatorEntries[j].PlatformCode, "W2ku", 4)) + strncpy (CheckPath, ParentName, sizeof (CheckPath)-1); + else + if (0 == memcmp (sDynamic->ParentLocatorEntries[j].PlatformCode, "W2ru", 4)) { + const char *c; + + if ((c = strrchr (szVHDPath, '/')) || (c = strrchr (szVHDPath, '\\'))) + memcpy (CheckPath, szVHDPath, c-szVHDPath+1); + strncpy (CheckPath+strlen(CheckPath), ParentName, sizeof (CheckPath)-(strlen (CheckPath)+1)); + } + if ((0 == GetVHDFooter(CheckPath, + &sParentFooter, + NULL, + NULL, + &ParentModificationTime, + NULL, + 0)) && + (0 == memcmp (sDynamic->ParentUniqueID, sParentFooter.UniqueID, sizeof (sParentFooter.UniqueID))) && + (sDynamic->ParentTimeStamp == ParentModificationTime)) + { + strncpy (szParentVHDPath, CheckPath, ParentVHDPathSize); + break; + } + } + if (!szParentVHDPath) + Return = EINVAL; /* File Corrupt */ + } + } + } +Return_Cleanup: +if (File) + fclose(File); +if (aBAT && (0 != Return)) { + free (*aBAT); + *aBAT = NULL; + } +return errno = Return; +} + +struct VHD_IOData { + VHD_Footer Footer; + VHD_DynamicDiskHeader Dynamic; + uint32 *BAT; + SMP_FILE* File; + char ParentVHDPath[512]; + struct VHD_IOData *Parent; + }; + +static t_stat sim_vhd_disk_implemented (void) +{ +return SCPE_OK; +} + +static t_stat sim_vhd_disk_set_dtype (SMP_FILE* f, const char *dtype) +{ +VHDHANDLE hVHD = (VHDHANDLE)f; +int Status = 0; + +memset (hVHD->Footer.DriveType, '\0', sizeof hVHD->Footer.DriveType); +memcpy (hVHD->Footer.DriveType, dtype, ((1+strlen (dtype)) < sizeof (hVHD->Footer.DriveType)) ? (1+strlen (dtype)) : sizeof (hVHD->Footer.DriveType)); +hVHD->Footer.Checksum = 0; +hVHD->Footer.Checksum = NtoHl (CalculateVhdFooterChecksum (&hVHD->Footer, sizeof(hVHD->Footer))); + +if (NtoHl (hVHD->Footer.DiskType) == VHD_DT_Fixed) { + if (WriteFilePosition(hVHD->File, + &hVHD->Footer, + sizeof(hVHD->Footer), + NULL, + NtoHll (hVHD->Footer.CurrentSize))) + Status = errno; + goto Cleanup_Return; + } +else { + uint64 position; + + position = sim_fsize_ex (hVHD->File); + if (((int64)position) == -1) { + Status = errno; + goto Cleanup_Return; + } + position -= sizeof(hVHD->Footer); + /* Update both copies on a dynamic disk */ + if (WriteFilePosition(hVHD->File, + &hVHD->Footer, + sizeof(hVHD->Footer), + NULL, + (uint64)0)) { + Status = errno; + goto Cleanup_Return; + } + if (WriteFilePosition(hVHD->File, + &hVHD->Footer, + sizeof(hVHD->Footer), + NULL, + position)) { + Status = errno; + goto Cleanup_Return; + } + } +Cleanup_Return: +if (Status) + return SCPE_IOERR; +return SCPE_OK; +} + +static const char *sim_vhd_disk_get_dtype (SMP_FILE* f) +{ +VHDHANDLE hVHD = (VHDHANDLE)f; + +return (char *)(&hVHD->Footer.DriveType[0]); +} + +static SMP_FILE* sim_vhd_disk_open (const char *szVHDPath, const char *DesiredAccess) + { + VHDHANDLE hVHD = (VHDHANDLE) calloc (1, sizeof(*hVHD)); + int Status; + + if (!hVHD) + return (SMP_FILE* )hVHD; + if (Status = GetVHDFooter (szVHDPath, + &hVHD->Footer, + &hVHD->Dynamic, + &hVHD->BAT, + NULL, + hVHD->ParentVHDPath, + sizeof (hVHD->ParentVHDPath))) + goto Cleanup_Return; + if (NtoHl (hVHD->Footer.DiskType) == VHD_DT_Differencing) { + hVHD->Parent = (VHDHANDLE)sim_vhd_disk_open (hVHD->ParentVHDPath, "rb"); + if (!hVHD->Parent) { + Status = errno; + goto Cleanup_Return; + } + } + if (hVHD->Footer.SavedState) { + Status = EAGAIN; /* Busy */ + goto Cleanup_Return; + } + hVHD->File = sim_fopen (szVHDPath, DesiredAccess); + if (!hVHD->File) { + Status = errno; + goto Cleanup_Return; + } +Cleanup_Return: + if (Status) { + free (hVHD->BAT); + free (hVHD); + hVHD = NULL; + } + errno = Status; + return (SMP_FILE* )hVHD; + } + +static int sim_vhd_disk_close (SMP_FILE* f) +{ +VHDHANDLE hVHD = (VHDHANDLE)f; + +if (NULL != hVHD) { + if (hVHD->Parent) + sim_vhd_disk_close ((SMP_FILE* )hVHD->Parent); + free (hVHD->BAT); + if (hVHD->File) { + fflush (hVHD->File); + fclose (hVHD->File); + } + free (hVHD); + return 0; + } +return -1; +} + +static void sim_vhd_disk_flush (SMP_FILE* f) +{ +VHDHANDLE hVHD = (VHDHANDLE)f; + +if ((NULL != hVHD) && (hVHD->File)) + fflush (hVHD->File); +} + +static t_addr sim_vhd_disk_size (SMP_FILE* f) +{ +VHDHANDLE hVHD = (VHDHANDLE)f; + +return (t_addr)(NtoHll (hVHD->Footer.CurrentSize)); +} + +#include +#include +static void +_rand_uuid_gen (void *uuidaddr) +{ +int i; +uint8 *b = (uint8 *)uuidaddr; +uint32 timenow = (uint32)time (NULL); + +memcpy (uuidaddr, &timenow, sizeof (timenow)); +srand ((unsigned)timenow); +for (i=4; i<16; i++) { + b[i] = (uint8)rand(); + } +} + +#if defined (_WIN32) +static void +uuid_gen (void *uuidaddr) +{ +static +RPC_STATUS +(RPC_ENTRY *UuidCreate_c) (void *); + +if (!UuidCreate_c) { + HINSTANCE hDll; + hDll = LoadLibraryA("rpcrt4.dll"); + UuidCreate_c = (RPC_STATUS (RPC_ENTRY *) (void*)) GetProcAddress(hDll, "UuidCreate"); + } +if (UuidCreate_c) + UuidCreate_c(uuidaddr); +else + _rand_uuid_gen (uuidaddr); +} +#elif defined (HAVE_DLOPEN) +#include + +static void +uuid_gen (void *uuidaddr) +{ +void (*uuid_generate_c) (void *) = NULL; +void *handle; + +#define __STR_QUOTE(tok) #tok +#define __STR(tok) __STR_QUOTE(tok) + handle = dlopen("libuuid." __STR(HAVE_DLOPEN), RTLD_NOW|RTLD_GLOBAL); + if (handle) + uuid_generate_c = (void (*)(void *)) dlsym(handle, "uuid_generate"); +if (uuid_generate_c) + uuid_generate_c(uuidaddr); +else + _rand_uuid_gen (uuidaddr); + if (handle) + dlclose(handle); +} +#else +static void +uuid_gen (void *uuidaddr) +{ +_rand_uuid_gen (uuidaddr); +} +#endif + +static VHDHANDLE +CreateVirtualDisk(const char *szVHDPath, + uint32 SizeInSectors, + uint32 BlockSize, + t_bool bFixedVHD) +{ +VHD_Footer Footer; +VHD_DynamicDiskHeader Dynamic; +uint32 *BAT = NULL; +time_t now; +uint32 i; +SMP_FILE* File = NULL; +uint32 Status = 0; +uint32 BytesPerSector = 512; +uint64 SizeInBytes = ((uint64)SizeInSectors)*BytesPerSector; +uint32 MaxTableEntries; +VHDHANDLE hVHD = NULL; + +if (SizeInBytes > ((uint64)(1024*1024*1024))*2040) { + Status = EFBIG; + goto Cleanup_Return; + } +if (File = sim_fopen (szVHDPath, "rb")) { + fclose (File); + File = NULL; + Status = EEXIST; + goto Cleanup_Return; + } +File = sim_fopen (szVHDPath, "wb"); +if (!File) { + Status = errno; + goto Cleanup_Return; + } + +memset (&Footer, 0, sizeof(Footer)); +memcpy (Footer.Cookie, "conectix", 8); +Footer.Features = NtoHl (0x00000002);; +Footer.FileFormatVersion = NtoHl (0x00010000);; +Footer.DataOffset = NtoHll (bFixedVHD ? ((long long)-1) : (long long)(sizeof(Footer))); +time (&now); +Footer.TimeStamp = NtoHl ((uint32)(now-946684800)); +memcpy (Footer.CreatorApplication, "simh", 4); +Footer.CreatorVersion = NtoHl (0x00010000); +memcpy (Footer.CreatorHostOS, "Wi2k", 4); +Footer.OriginalSize = NtoHll (SizeInBytes); +Footer.CurrentSize = NtoHll (SizeInBytes); +uuid_gen (Footer.UniqueID); +Footer.DiskType = NtoHl (bFixedVHD ? VHD_DT_Fixed : VHD_DT_Dynamic); +Footer.DiskGeometry = NtoHl (0xFFFF10FF); +if (1) { /* CHS Calculation */ + uint32 totalSectors = (uint32)(SizeInBytes/BytesPerSector);/* Total data sectors present in the disk image */ + uint32 cylinders; /* Number of cylinders present on the disk */ + uint32 heads; /* Number of heads present on the disk */ + uint32 sectorsPerTrack; /* Sectors per track on the disk */ + uint32 cylinderTimesHeads; /* Cylinders x heads */ + + if (totalSectors > 65535 * 16 * 255) + totalSectors = 65535 * 16 * 255; + + if (totalSectors >= 65535 * 16 * 63) { + sectorsPerTrack = 255; + heads = 16; + cylinderTimesHeads = totalSectors / sectorsPerTrack; + } + else { + sectorsPerTrack = 17; + cylinderTimesHeads = totalSectors / sectorsPerTrack; + + heads = (cylinderTimesHeads + 1023) / 1024; + + if (heads < 4) + heads = 4; + if (cylinderTimesHeads >= (heads * 1024) || heads > 16) + { + sectorsPerTrack = 31; + heads = 16; + cylinderTimesHeads = totalSectors / sectorsPerTrack; + } + if (cylinderTimesHeads >= (heads * 1024)) + { + sectorsPerTrack = 63; + heads = 16; + cylinderTimesHeads = totalSectors / sectorsPerTrack; + } + } + cylinders = cylinderTimesHeads / heads; + Footer.DiskGeometry = NtoHl ((cylinders<<16)|(heads<<8)|sectorsPerTrack); + } +Footer.Checksum = NtoHl (CalculateVhdFooterChecksum(&Footer, sizeof(Footer))); + +if (bFixedVHD) { + if (WriteFilePosition(File, + &Footer, + sizeof(Footer), + NULL, + SizeInBytes)) + Status = errno; + goto Cleanup_Return; + } + +/* Dynamic Disk */ +memset (&Dynamic, 0, sizeof(Dynamic)); +memcpy (Dynamic.Cookie, "cxsparse", 8); +Dynamic.DataOffset = NtoHll (0xFFFFFFFFFFFFFFFFLL); +Dynamic.TableOffset = NtoHll ((uint64)(BytesPerSector*((sizeof(Dynamic)+sizeof(Footer)+BytesPerSector-1)/BytesPerSector))); +Dynamic.HeaderVersion = NtoHl (0x00010000); +if (0 == BlockSize) + BlockSize = 2*1024*1024; +Dynamic.BlockSize = NtoHl (BlockSize); +MaxTableEntries = (uint32)((SizeInBytes+BlockSize-1)/BlockSize); +Dynamic.MaxTableEntries = NtoHl (MaxTableEntries); +Dynamic.Checksum = NtoHl (CalculateVhdFooterChecksum(&Dynamic, sizeof(Dynamic))); +BAT = (uint32*) malloc (BytesPerSector*((MaxTableEntries*sizeof(*BAT)+BytesPerSector-1)/BytesPerSector)); +memset (BAT, 0, BytesPerSector*((MaxTableEntries*sizeof(*BAT)+BytesPerSector-1)/BytesPerSector)); +for (i=0; iDynamic.TableOffset)+BytesPerSector*((NtoHl (hVHD->Dynamic.MaxTableEntries)*sizeof(*hVHD->BAT)+BytesPerSector-1)/BytesPerSector); +hVHD->Dynamic.Checksum = 0; +RelativeParentVHDPath = (char*) calloc (1, BytesPerSector+1); +FullParentVHDPath = (char*) calloc (1, BytesPerSector+1); +RelativeParentVHDPathBuffer = (char*) calloc (1, BytesPerSector); +FullParentVHDPathBuffer = (char*) calloc (1, BytesPerSector); +FullVHDPath = (char*) calloc (1, BytesPerSector+1); +ExpandToFullPath (szParentVHDPath, FullParentVHDPath, BytesPerSector); +for (i=0; i < strlen (FullParentVHDPath); i++) + hVHD->Dynamic.ParentUnicodeName[i*2+1] = FullParentVHDPath[i]; +for (i=0; i < strlen (FullParentVHDPath); i++) + FullParentVHDPathBuffer[i*2] = FullParentVHDPath[i]; +ExpandToFullPath (szVHDPath, FullVHDPath, BytesPerSector); +for (i=0, RelativeMatch=UpDirectories=0; iDynamic.ParentTimeStamp = ParentTimeStamp; +memcpy (hVHD->Dynamic.ParentUniqueID, ParentFooter.UniqueID, sizeof (hVHD->Dynamic.ParentUniqueID)); +hVHD->Dynamic.ParentLocatorEntries[7].PlatformDataSpace = NtoHl (BytesPerSector); +hVHD->Dynamic.ParentLocatorEntries[7].PlatformDataOffset = NtoHll (LocatorPosition+LocatorsWritten*BytesPerSector); +++LocatorsWritten; +hVHD->Dynamic.ParentLocatorEntries[6].PlatformDataSpace = NtoHl (BytesPerSector); +hVHD->Dynamic.ParentLocatorEntries[6].PlatformDataOffset = NtoHll (LocatorPosition+LocatorsWritten*BytesPerSector); +++LocatorsWritten; +if (RelativeMatch) { + memcpy (hVHD->Dynamic.ParentLocatorEntries[5].PlatformCode, "W2ru", 4); + hVHD->Dynamic.ParentLocatorEntries[5].PlatformDataSpace = NtoHl (BytesPerSector); + hVHD->Dynamic.ParentLocatorEntries[5].PlatformDataLength = NtoHl ((uint32)(2*strlen(RelativeParentVHDPath))); + hVHD->Dynamic.ParentLocatorEntries[5].Reserved = 0; + hVHD->Dynamic.ParentLocatorEntries[5].PlatformDataOffset = NtoHll (LocatorPosition+LocatorsWritten*BytesPerSector); + ++LocatorsWritten; + } +memcpy (hVHD->Dynamic.ParentLocatorEntries[4].PlatformCode, "W2ku", 4); +hVHD->Dynamic.ParentLocatorEntries[4].PlatformDataSpace = NtoHl (BytesPerSector); +hVHD->Dynamic.ParentLocatorEntries[4].PlatformDataLength = NtoHl ((uint32)(2*strlen(FullParentVHDPath))); +hVHD->Dynamic.ParentLocatorEntries[4].Reserved = 0; +hVHD->Dynamic.ParentLocatorEntries[4].PlatformDataOffset = NtoHll (LocatorPosition+LocatorsWritten*BytesPerSector); +++LocatorsWritten; +hVHD->Dynamic.Checksum = NtoHl (CalculateVhdFooterChecksum (&hVHD->Dynamic, sizeof(hVHD->Dynamic))); +hVHD->Footer.Checksum = 0; +hVHD->Footer.DiskType = NtoHl (VHD_DT_Differencing); +memcpy (hVHD->Footer.DriveType, ParentFooter.DriveType, sizeof (hVHD->Footer.DriveType)); +hVHD->Footer.Checksum = NtoHl (CalculateVhdFooterChecksum (&hVHD->Footer, sizeof(hVHD->Footer))); + +if (WriteFilePosition (hVHD->File, + &hVHD->Footer, + sizeof (hVHD->Footer), + NULL, + 0)) { + Status = errno; + goto Cleanup_Return; + } +if (WriteFilePosition (hVHD->File, + &hVHD->Dynamic, + sizeof (hVHD->Dynamic), + NULL, + NtoHll (hVHD->Footer.DataOffset))) { + Status = errno; + goto Cleanup_Return; + } +LocatorsWritten = 0; +if (RelativeMatch) { + if (WriteFilePosition (hVHD->File, + RelativeParentVHDPath, + BytesPerSector, + NULL, + LocatorPosition+LocatorsWritten*BytesPerSector)) { + Status = errno; + goto Cleanup_Return; + } + ++LocatorsWritten; + } +if (WriteFilePosition (hVHD->File, + FullParentVHDPath, + BytesPerSector, + NULL, + LocatorPosition+LocatorsWritten*BytesPerSector)) { + Status = errno; + goto Cleanup_Return; + } +++LocatorsWritten; +if (RelativeMatch) { + if (WriteFilePosition (hVHD->File, + RelativeParentVHDPathBuffer, + BytesPerSector, + NULL, + LocatorPosition+LocatorsWritten*BytesPerSector)) { + Status = errno; + goto Cleanup_Return; + } + ++LocatorsWritten; + } +if (WriteFilePosition (hVHD->File, + FullParentVHDPathBuffer, + BytesPerSector, + NULL, + LocatorPosition+LocatorsWritten*BytesPerSector)) { + Status = errno; + goto Cleanup_Return; + } +++LocatorsWritten; +if (WriteFilePosition (hVHD->File, + &hVHD->Footer, + sizeof(hVHD->Footer), + NULL, + LocatorPosition+LocatorsWritten*BytesPerSector)) { + Status = errno; + goto Cleanup_Return; + } + +Cleanup_Return: +free (RelativeParentVHDPath); +free (FullParentVHDPath); +free (RelativeParentVHDPathBuffer); +free (FullParentVHDPathBuffer); +free (FullVHDPath); +sim_vhd_disk_close ((SMP_FILE* )hVHD); +hVHD = NULL; +if (Status) { + if (EEXIST != Status) + remove (szVHDPath); + } +else { + hVHD = (VHDHANDLE)sim_vhd_disk_open (szVHDPath, "rb+"); + if (!hVHD) + Status = errno; + } +errno = Status; +return hVHD; +} + +static SMP_FILE* sim_vhd_disk_create (const char *szVHDPath, t_addr desiredsize) +{ +return (SMP_FILE* )CreateVirtualDisk (szVHDPath, (uint32)(desiredsize/512), 0, (sim_switches & SWMASK ('X'))); +} + +static SMP_FILE* sim_vhd_disk_create_diff (const char *szVHDPath, const char *szParentVHDPath) +{ +return (SMP_FILE* )CreateDifferencingVirtualDisk (szVHDPath, szParentVHDPath); +} + +static t_stat +ReadVirtualDiskSectors(VHDHANDLE hVHD, + uint8 *buf, + t_seccnt sects, + t_seccnt *sectsread, + uint32 SectorSize, + t_lba lba) +{ +uint64 BlockOffset = ((uint64)lba)*SectorSize; +uint32 BlocksRead = 0; +uint32 SectorsInRead; +size_t BytesRead = 0; + +if (!hVHD || (hVHD->File == NULL)) { + errno = EBADF; + return SCPE_IOERR; + } +if ((BlockOffset + sects*SectorSize) > (uint64)NtoHll (hVHD->Footer.CurrentSize)) { + errno = ERANGE; + return SCPE_IOERR; + } +if (NtoHl (hVHD->Footer.DiskType) == VHD_DT_Fixed) { + if (ReadFilePosition(hVHD->File, + buf, + sects*SectorSize, + &BytesRead, + BlockOffset)) { + if (sectsread) + *sectsread = (t_seccnt)(BytesRead/SectorSize); + return SCPE_IOERR; + } + if (sectsread) + *sectsread /= SectorSize; + return SCPE_OK; + } +/* We are now dealing with a Dynamically expanding or differencing disk */ +while (sects) { + uint32 SectorsPerBlock = NtoHl (hVHD->Dynamic.BlockSize)/SectorSize; + uint64 BlockNumber = lba/SectorsPerBlock; + uint32 BitMapBytes = (7+(NtoHl (hVHD->Dynamic.BlockSize)/SectorSize))/8; + uint32 BitMapSectors = (BitMapBytes+SectorSize-1)/SectorSize; + + SectorsInRead = 1; + if (hVHD->BAT[BlockNumber] == VHD_BAT_FREE_ENTRY) { + if (!hVHD->Parent) + memset (buf, 0, SectorSize); + else { + SectorsInRead = SectorsPerBlock - lba%SectorsPerBlock; + if (SectorsInRead > sects) + SectorsInRead = sects; + if (ReadVirtualDiskSectors(hVHD->Parent, + buf, + SectorsInRead, + NULL, + SectorSize, + lba)) { + if (sectsread) + *sectsread = BlocksRead; + return FALSE; + } + } + } + else { + BlockOffset = SectorSize*((uint64)(NtoHl (hVHD->BAT[BlockNumber]) + lba%SectorsPerBlock + BitMapSectors)); + SectorsInRead = SectorsPerBlock - lba%SectorsPerBlock; + if (SectorsInRead > sects) + SectorsInRead = sects; + if (ReadFilePosition(hVHD->File, + buf, + SectorsInRead*SectorSize, + NULL, + BlockOffset)) { + if (sectsread) + *sectsread = BlocksRead; + return SCPE_IOERR; + } + } + sects -= SectorsInRead; + buf = (uint8 *)(((char *)buf) + SectorSize*SectorsInRead); + lba += SectorsInRead; + BlocksRead += SectorsInRead; + } +if (sectsread) + *sectsread = BlocksRead; +return SCPE_OK; +} + +static t_stat sim_vhd_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) +{ +VHDHANDLE hVHD = (VHDHANDLE)uptr->fileref; +disk_context* ctx = (disk_context*) uptr->disk_ctx; + +return ReadVirtualDiskSectors(hVHD, buf, sects, sectsread, ctx->sector_size, lba); +} + +static t_bool +BufferIsZeros(void *Buffer, size_t BufferSize) +{ +size_t i; +char *c = (char *)Buffer; + +for (i=0; iFile) { + errno = EBADF; + return SCPE_IOERR; + } +if ((BlockOffset + sects*SectorSize) > (uint64)NtoHll(hVHD->Footer.CurrentSize)) { + errno = ERANGE; + return SCPE_IOERR; + } +if (NtoHl(hVHD->Footer.DiskType) == VHD_DT_Fixed) { + if (WriteFilePosition(hVHD->File, + buf, + sects*SectorSize, + &BytesWritten, + BlockOffset)) { + if (sectswritten) + *sectswritten = (t_seccnt)(BytesWritten/SectorSize); + return SCPE_IOERR; + } + if (sectswritten) + *sectswritten /= SectorSize; + return SCPE_OK; + } +/* We are now dealing with a Dynamically expanding or differencing disk */ +while (sects) { + uint32 SectorsPerBlock = NtoHl(hVHD->Dynamic.BlockSize)/SectorSize; + uint64 BlockNumber = lba/SectorsPerBlock; + uint32 BitMapBytes = (7+(NtoHl(hVHD->Dynamic.BlockSize)/SectorSize))/8; + uint32 BitMapSectors = (BitMapBytes+SectorSize-1)/SectorSize; + + if (BlockNumber >= NtoHl(hVHD->Dynamic.MaxTableEntries)) { + if (sectswritten) + *sectswritten = BlocksWritten; + return SCPE_EOF; + } + SectorsInWrite = 1; + if (hVHD->BAT[BlockNumber] == VHD_BAT_FREE_ENTRY) { + void *BitMap = NULL; + void *BlockData = NULL; + + if (!hVHD->Parent && BufferIsZeros(buf, SectorSize)) + goto IO_Done; + /* Need to allocate a new Data Block. */ + BlockOffset = sim_fsize_ex (hVHD->File); + if (((int64)BlockOffset) == -1) + return SCPE_IOERR; + BitMap = malloc(BitMapSectors*SectorSize); + memset(BitMap, 0xFF, BitMapBytes); + BlockOffset -= sizeof(hVHD->Footer); + + /* align the data portion of the block to the desired alignment */ + /* compute the address of the data portion of the block */ + BlockOffset += BitMapSectors*SectorSize; + /* round up this address to the desired alignment */ + BlockOffset += VHD_DATA_BLOCK_ALIGNMENT-1; + BlockOffset &= ~(VHD_DATA_BLOCK_ALIGNMENT-1); + /* the actual block address is the beginning of the block bitmap */ + BlockOffset -= BitMapSectors*SectorSize; + + hVHD->BAT[BlockNumber] = NtoHl((uint32)(BlockOffset/SectorSize)); + if (WriteFilePosition(hVHD->File, + BitMap, + BitMapSectors*SectorSize, + NULL, + BlockOffset)) { + free (BitMap); + return SCPE_IOERR; + } + free(BitMap); + BitMap = NULL; + BlockOffset += SectorSize * (SectorsPerBlock + BitMapSectors); + if (WriteFilePosition(hVHD->File, + &hVHD->Footer, + sizeof(hVHD->Footer), + NULL, + BlockOffset)) + goto Fatal_IO_Error; + if (WriteFilePosition(hVHD->File, + hVHD->BAT, + SectorSize*((sizeof(*hVHD->BAT)*NtoHl(hVHD->Dynamic.MaxTableEntries)+511)/512), + NULL, + NtoHll(hVHD->Dynamic.TableOffset))) + goto Fatal_IO_Error; + if (hVHD->Parent) + { /* Need to populate data block contents from parent VHD */ + BlockData = malloc(SectorsPerBlock*SectorSize); + if (ReadVirtualDiskSectors(hVHD->Parent, + (uint8*) BlockData, + SectorsPerBlock, + NULL, + SectorSize, + (lba/SectorsPerBlock)*SectorsPerBlock)) + goto Fatal_IO_Error; + if (WriteVirtualDiskSectors(hVHD, + (uint8*) BlockData, + SectorsPerBlock, + NULL, + SectorSize, + (lba/SectorsPerBlock)*SectorsPerBlock)) + goto Fatal_IO_Error; + free(BlockData); + } + continue; +Fatal_IO_Error: + free (BitMap); + free (BlockData); + fclose (hVHD->File); + hVHD->File = NULL; + return SCPE_IOERR; + } + else { + BlockOffset = 512*((uint64)(NtoHl(hVHD->BAT[BlockNumber]) + lba%SectorsPerBlock + BitMapSectors)); + SectorsInWrite = SectorsPerBlock - lba%SectorsPerBlock; + if (SectorsInWrite > sects) + SectorsInWrite = sects; + if (WriteFilePosition(hVHD->File, + buf, + SectorsInWrite*SectorSize, + NULL, + BlockOffset)) { + if (sectswritten) + *sectswritten = BlocksWritten; + return SCPE_IOERR; + } + } +IO_Done: + sects -= SectorsInWrite; + buf = (uint8 *)(((char *)buf) + SectorsInWrite*SectorSize); + lba += SectorsInWrite; + BlocksWritten += SectorsInWrite; + } +if (sectswritten) + *sectswritten = BlocksWritten; +return SCPE_OK; +} + +static t_stat sim_vhd_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) +{ +VHDHANDLE hVHD = (VHDHANDLE)uptr->fileref; +disk_context* ctx = (disk_context*) uptr->disk_ctx; + +return WriteVirtualDiskSectors(hVHD, buf, sects, sectswritten, ctx->sector_size, lba); +} +#endif diff --git a/src/sim_disk.h b/src/sim_disk.h new file mode 100644 index 0000000..245fc8d --- /dev/null +++ b/src/sim_disk.h @@ -0,0 +1,87 @@ +/* sim_disk.h: simulator disk support library definitions + + Copyright (c) 2011, Mark Pizzolato + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of Robert M Supnik and + Mark Pizzolato shall not be used in advertising or otherwise to promote + the sale, use or other dealings in this Software without prior written + authorization from Robert M Supnik and Mark Pizzolato. + + 25-Jan-11 MP Initial Implemementation +*/ + +#ifndef _SIM_DISK_H_ +#define _SIM_DISK_H_ 0 + +/* SIMH/Disk format */ + +typedef uint32 t_seccnt; /* disk sector count */ +typedef uint32 t_lba; /* disk logical block address */ + +/* Unit flags */ + +#define DKUF_V_WLK (UNIT_V_UF + 12) /* write locked */ +#define DKUF_V_FMT (UNIT_V_UF + 13) /* disk file format */ +#define DKUF_W_FMT 3 /* 3b of formats */ +#define DKUF_N_FMT (1u << DKUF_W_FMT) /* number of formats */ +#define DKUF_M_FMT ((1u << DKUF_W_FMT) - 1) +#define DKUF_F_STD 0 /* SIMH format */ +#define DKUF_F_RAW 1 /* Raw Physical Disk Access */ +#define DKUF_F_VHD 2 /* VHD format */ +#define DKUF_V_UF (DKUF_V_FMT + DKUF_W_FMT) +#define DKUF_WLK (1u << DKUF_V_WLK) +#define DKUF_FMT (DKUF_M_FMT << DKUF_V_FMT) +#define DKUF_WRP (DKUF_WLK | UNIT_RO) + +#define DK_F_STD (DKUF_F_STD << DKUF_V_FMT) +#define DK_F_RAW (DKUF_F_RAW << DKUF_V_FMT) +#define DK_F_VHD (DKUF_F_VHD << DKUF_V_FMT) + +#define DK_GET_FMT(u) (((u)->flags >> DKUF_V_FMT) & DKUF_M_FMT) + +/* Return status codes */ + +#define DKSE_OK 0 /* no error */ + +typedef void (*DISK_PCALLBACK)(UNIT *unit, t_stat status); + +/* Prototypes */ + +t_stat sim_disk_attach (UNIT *uptr, char *cptr, size_t sector_size, size_t xfer_element_size, t_bool dontautosize, uint32 debugbit, const char *drivetype, uint32 pdp11_tracksize); +t_stat sim_disk_detach (UNIT *uptr); +t_stat sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects); +t_stat sim_disk_rdsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects, DISK_PCALLBACK callback); +t_stat sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects); +t_stat sim_disk_wrsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects, DISK_PCALLBACK callback); +t_stat sim_disk_unload (UNIT *uptr); +t_stat sim_disk_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sim_disk_show_fmt (SMP_FILE* st, UNIT *uptr, int32 val, void *desc); +t_stat sim_disk_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sim_disk_show_capac (SMP_FILE* st, UNIT *uptr, int32 val, void *desc); +t_stat sim_disk_set_asynch (UNIT *uptr, int latency); +t_stat sim_disk_clr_asynch (UNIT *uptr); +t_bool sim_disk_isavailable (UNIT *uptr); +t_bool sim_disk_isavailable_a (UNIT *uptr, DISK_PCALLBACK callback); +t_bool sim_disk_wrp (UNIT *uptr); +t_addr sim_disk_size (UNIT *uptr); +void sim_disk_data_trace (UNIT *uptr, const uint8 *data, size_t lba, size_t len, const char* txt, int detail, uint32 reason); +t_stat sim_disk_reset (DEVICE *dptr); + +#endif diff --git a/src/sim_ether.cpp b/src/sim_ether.cpp new file mode 100644 index 0000000..3596da2 --- /dev/null +++ b/src/sim_ether.cpp @@ -0,0 +1,3341 @@ +/* sim_ether.c: OS-dependent network routines + ------------------------------------------------------------------------------ + Copyright (c) 2002-2007, 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"), + 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 + THE AUTHOR 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 the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + This ethernet simulation is based on the PCAP and WinPcap packages. + + PCAP/WinPcap was chosen as the basis for network code since it is the most + "universal" of the various network packages available. Using this style has + allowed rapid network development for the major SIMH platforms. Developing + a network package specifically for SIMH was rejected due to the time required; + the advantage would be a more easily compiled and integrated code set. + + There are various problems associated with use of ethernet networking, which + would be true regardless of the network package used, since there are no + universally accepted networking methods. The most serious of these is getting + the proper networking package loaded onto the system, since most environments + do not come with the network interface packages loaded. + + The second most serious network issue relates to security. The network + simulation needs to simulate operating system level functionality (packet + driving). However, the host network programming interfaces tend to operate at + the user level of functionality, so getting to the full functionality of + the network interface usually requires that the person executing the + network code be a privileged user of the host system. See the PCAP/WinPcap + documentation for the appropriate host platform if unprivileged use of + 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 + + ------------------------------------------------------------------------------ + + Supported/Tested Platforms: + + Windows(NT,2K,XP,2K3,Vista,Win7) WinPcap V3.0+ + Linux libpcap at least 0.9 + OpenBSD,FreeBSD,NetBSD libpcap at least 0.9 + MAC OS/X libpcap at least 0.9 + Solaris Sparc libpcap at least 0.9 + Solaris Intel libpcap at least 0.9 + AIX ?? + HP/UX ?? + Compaq Tru64 Unix ?? + VMS Alpha/Itanium VMS only, needs VMS libpcap + + WinPcap is available from: + http://winpcap.polito.it/ + libpcap for VMS is available from: + http://simh.trailing-edge.com/sources/vms-pcap.zip + libpcap for other Unix platforms is available at: + NOTE: As of the release of this version of sim_ether.c ALL current + *nix platforms ship with a sufficiently new version of + libpcap, and ALL provide a libpcap-dev package for developing + libpcap based applications. The OS vendor supplied version + of libpcap AND the libpcap-dev components are preferred for + proper operation of both simh AND other applications on the + host system which use libpcap. + Current Version: http://www.tcpdump.org/daily/libpcap-current.tar.gz + Released Version: http://www.tcpdump.org/release/ + + When necessary (see NOTE above about vendor supplied libpcap), + we've gotten the tarball, unpacked, built and installed it with: + gzip -dc libpcap-current.tar.gz | tar xvf - + cd libpcap-directory-name + ./configure + make + make install + Note: The "make install" step generally will have to be done as root. + This will install libpcap in /usr/local/lib and /usr/local/include + The current simh makefile will do the right thing to locate and + reference the OS provided libpcap or the one just installed. + + + Note: Building for the platforms indicated above, with the indicated libpcap, + should automatically leverage the appropriate mechanisms contained here. + Things are structured so that it is likely to work for any other as yet + untested platform. If it works for you, please let the author know so we + can update the table above. If it doesn't work, then the following #define + variables can influence the operation on an untested platform. + + USE_BPF - Determines if this code leverages a libpcap/WinPcap + provided bpf packet filtering facility. All tested + environments have bpf facilities that work the way we + need them to. However a new one might not. undefine + this variable to let this code do its own filtering. + USE_SETNONBLOCK - Specifies whether the libpcap environment's non-blocking + semantics are to be leveraged. This helps to manage the + varying behaviours of the kernel packet facilities + leveraged by libpcap. + USE_READER_THREAD - Specifies that packet reading should be done in the + context of a separate thread. The Posix threading + APIs are used. This option is less efficient than the + default non-threaded approach, but it exists since some + platforms don't want to work with nonblocking libpcap + semantics. OpenBSD and NetBSD either don't have pthread + APIs available, or they are too buggy to be useful. + 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. 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. + USE_VDE_NETWORK - Specifies that support for vde networking should be + included. This can be leveraged, along with OS bridging + capabilities to share a single LAN interface. It also + can allow a simulator to have useful networking + functionality when running without root access. This + allows device names of the form vde:/tmp/switch to be + specified at open time. This functionality is only + available on *nix platforms since the vde api isn't + available on Windows. + + NEED_PCAP_SENDPACKET + - Specifies that you are using an older version of libpcap + which doesn't provide a pcap_sendpacket API. + + NOTE: Changing these defines is done in either sim_ether.h OR on the global + compiler command line which builds all of the modules included in a + simulator. + + ------------------------------------------------------------------------------ + + Modification history: + + 17-Nov-11 MP Added dynamic loading of libpcap on *nix platforms + 30-Oct-11 MP Added support for vde (Virtual Distributed Ethernet) networking + 29-Oct-11 MP Added support for integrated Tap networking interfaces on OSX + 12-Aug-11 MP Cleaned up payload length determination + Fixed race condition detecting reflections when threaded + reading and writing is enabled + 18-Apr-11 MP Fixed race condition with self loopback packets in + multithreaded environments + 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 + 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) + 25-Mar-04 MP Revised comments and minor #defines to deal with updated + libpcap which now provides pcap_sendpacket on all platforms. + 04-Feb-04 MP Returned success/fail status from eth_write to support + determining if the current libpcap connection can successfully + write packets. + Added threaded approach to reading packets since + this works better on some platforms (solaris intel) than the + inconsistently implemented non-blocking read approach. + 04-Feb-04 DTH Converted ETH_DEBUG to sim_debug + 13-Jan-04 MP tested and fixed on OpenBSD, NetBS and FreeBSD. + 09-Jan-04 MP removed the BIOCSHDRCMPLT ioctl() for OS/X + 05-Jan-04 DTH Added eth_mac_scan + 30-Dec-03 DTH Cleaned up queue routines, added no network support message + 26-Dec-03 DTH Added ethernet show and queue functions from pdp11_xq + 15-Dec-03 MP polished generic libpcap support. + 05-Dec-03 DTH Genericized eth_devices() and #ifdefs + 03-Dec-03 MP Added Solaris support + 02-Dec-03 DTH Corrected decnet fix to use reflection counting + 01-Dec-03 DTH Added BPF source filtering and reflection counting + 28-Nov-03 DTH Rewrote eth_devices using universal pcap_findalldevs() + 25-Nov-03 DTH Verified DECNET_FIX, reversed ifdef to mainstream code + 19-Nov-03 MP Fixed BPF functionality on Linux/BSD. + 17-Nov-03 DTH Added xBSD simplification + 14-Nov-03 DTH Added #ifdef DECNET_FIX for problematic duplicate detection code + 13-Nov-03 DTH Merged in __FreeBSD__ support + 21-Oct-03 MP Added enriched packet dumping for debugging + 20-Oct-03 MP Added support for multiple ethernet devices on VMS + 20-Sep-03 Ankan Add VMS support (Alpha only) + 29-Sep-03 MP Changed separator character in eth_fmt_mac to be ":" to + format ethernet addresses the way the BPF compile engine + wants to see them. + Added BPF support to filter packets + Added missing smp_printf in eth_close + 07-Jun-03 MP Added WIN32 support for DECNET duplicate address detection. + 06-Jun-03 MP Fixed formatting of Ethernet Protocol Type in eth_packet_trace + 30-May-03 DTH Changed WIN32 to _WIN32 for consistency + 07-Mar-03 MP Fixed Linux implementation of PacketGetAdapterNames to also + work on Red Hat 6.2-sparc and Debian 3.0r1-sparc. + 03-Mar-03 MP Changed logging to be consistent on stdout and sim_log + 01-Feb-03 MP Changed type of local variables in eth_packet_trace to + conform to the interface needs of eth_mac_fmt wich produces + char data instead of unsigned char data. Suggested by the + DECC compiler. + 15-Jan-03 DTH Corrected PacketGetAdapterNames parameter2 datatype + 26-Dec-02 DTH Merged Mark Pizzolato's enhancements with main source + Added networking documentation + Changed _DEBUG to ETH_DEBUG + 20-Dec-02 MP Added display of packet CRC to the eth_packet_trace. + This helps distinguish packets with identical lengths + and protocols. + 05-Dec-02 MP With the goal of draining the input buffer more rapidly + changed eth_read to call pcap_dispatch repeatedly until + either a timeout returns nothing or a packet allowed by + the filter is seen. This more closely reflects how the + pcap layer will work when the filtering is actually done + by a bpf filter. + 31-Oct-02 DTH Added USE_NETWORK conditional + Reworked not attached test + Added OpenBSD support (from Federico Schwindt) + Added ethX detection simplification (from Megan Gentry) + Removed sections of temporary code + Added parameter validation + 23-Oct-02 DTH Beta 5 released + 22-Oct-02 DTH Added all_multicast and promiscuous support + Fixed not attached behavior + 21-Oct-02 DTH Added NetBSD support (from Jason Thorpe) + Patched buffer size to make sure entire packet is read in + Made 'ethX' check characters passed as well as length + Corrected copyright again + 16-Oct-02 DTH Beta 4 released + Corrected copyright + 09-Oct-02 DTH Beta 3 released + Added pdp11 write acceleration (from Patrick Caulfield) + 08-Oct-02 DTH Beta 2 released + Integrated with 2.10-0p4 + Added variable vector and copyrights + 04-Oct-02 DTH Added linux support (from Patrick Caulfield) + 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11 + 24-Sep-02 DTH Finished eth_devices, eth_getname + 18-Sep-02 DTH Callbacks implemented + 13-Sep-02 DTH Basic packet read/write written + 20-Aug-02 DTH Created Sim_Ether for O/S independant ethernet implementation + + ------------------------------------------------------------------------------ +*/ + +#include +#include "sim_ether.h" +#include "sim_sock.h" + +extern SMP_FILE *sim_log; + + +/*============================================================================*/ +/* OS-independant ethernet routines */ +/*============================================================================*/ + +t_stat eth_mac_scan (ETH_MAC* mac, char* 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; + + if ((6 != sscanf(strmac, "%x:%x:%x:%x:%x:%x", &a0, &a1, &a2, &a3, &a4, &a5)) && + (6 != sscanf(strmac, "%x.%x.%x.%x.%x.%x", &a0, &a1, &a2, &a3, &a4, &a5)) && + (6 != sscanf(strmac, "%x-%x-%x-%x-%x-%x", &a0, &a1, &a2, &a3, &a4, &a5))) + return SCPE_ARG; + if ((a0 > 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 */ + !memcmp(newmac, ones, sizeof(ETH_MAC)) || /* broadcast */ + (newmac[0] & 0x01) /* multicast */ + ) + return SCPE_ARG; + + /* new mac is OK, copy into passed mac */ + memcpy (*mac, newmac, sizeof(ETH_MAC)); + return SCPE_OK; +} + +void eth_mac_fmt(ETH_MAC* mac, char* buff) +{ + uint8* m = (uint8*) mac; + sprintf(buff, "%02X:%02X:%02X:%02X:%02X:%02X", m[0], m[1], m[2], m[3], m[4], m[5]); + return; +} + +static void eth_panic_mem() +{ + // ToDo: all refernces to eth_panic_mem need to be eliminated + // and replaced with proper error handling + panic("Ethernet interface: out of memory"); +} + +static const uint32 crcTable[256] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, + 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, + 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, + 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, + 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, + 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, + 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, + 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, + 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, + 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, + 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, + 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, + 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, + 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, + 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, + 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, + 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, + 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, + 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +uint32 eth_crc32(uint32 crc, const void* vbuf, size_t len) +{ + const uint32 mask = 0xFFFFFFFF; + 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); +} + +int eth_get_packet_crc32_data(const uint8 *msg, int len, uint8 *crcdata) +{ + 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(crcdata, &ncrc, size); /* append crc to packet */ + crc_len = len + size; /* set packet crc length */ + } else { + 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) +{ + dev->need_crc = need_crc; +} + +void eth_packet_trace_ex(ETH_DEV* dev, const uint8 *msg, int len, const char* txt, int detail, uint32 reason) +{ + 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(reason, dev->dptr, "%s dst: %s src: %s proto: 0x%04X len: %d crc: %X\n", + txt, dst, src, ntohs(*proto), len, crc); + if (detail) { + int i, same, group, sidx, oidx; + char outbuf[80], strbuf[18]; + static const char hex[] = "0123456789ABCDEF"; + for (i=same=0; i 0) && (0 == memcmp(&msg[i], &msg[i-16], 16))) { + ++same; + continue; + } + if (same > 0) { + 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)); + for (sidx=oidx=0; sidx>4)&0xf]; + outbuf[oidx++] = hex[msg[i+sidx]&0xf]; + if (isprint(msg[i+sidx])) + strbuf[sidx] = msg[i+sidx]; + else + strbuf[sidx] = '.'; + } + outbuf[oidx] = '\0'; + strbuf[sidx] = '\0'; + sim_debug(reason, dev->dptr, "%04X%-48s %s\n", i, outbuf, strbuf); + } + if (same > 0) + 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, 0, dev->dbit); +} + +char* eth_getname(int number, char* name) +{ + ETH_LIST list[ETH_MAX_DEVICE]; + int count = eth_devices(ETH_MAX_DEVICE, list); + + if (number >= count || number < 0) + return 0; + strcpy(name, list[number].name); + return name; +} + +char* eth_getname_bydesc(char* desc, char* name) +{ + ETH_LIST list[ETH_MAX_DEVICE]; + int count = eth_devices(ETH_MAX_DEVICE, list); + int i; + size_t j=strlen(desc); + + for (i=0; i s2) + return 1; + if (s1 == 0) return 0; + } + return 0; +} + +char* eth_getname_byname(char* name, char* temp) +{ + ETH_LIST list[ETH_MAX_DEVICE]; + int count = eth_devices(ETH_MAX_DEVICE, list); + size_t n; + int i, found; + + found = 0; + n = strlen(name); + for (i=0; ireflections = -1; /* not established yet */ +#if defined(USE_READER_THREAD) + dev->reader_thread = SMP_THREAD_NULL; + dev->writer_thread = SMP_THREAD_NULL; +#endif +} + +t_stat eth_show (SMP_FILE* st, UNIT* uptr, int32 val, void* desc) +{ + ETH_LIST list[ETH_MAX_DEVICE]; + int number = eth_devices(ETH_MAX_DEVICE, list); + + fprintf(st, "ETH devices:\n"); + if (number == -1) + fprintf(st, " network support not available in simulator\n"); + else + if (number == 0) + fprintf(st, " no network devices are available\n"); + else { + size_t min, len; + int i; + for (i=0, min=0; i min) min = len; + for (i=0; iitem) { + 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"; + smp_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; +} + +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 = 0; +} + +void ethq_remove(ETH_QUE* que) +{ + struct eth_item* item = &que->item[que->head]; + + if (que->count) { + memset(item, 0, sizeof(struct eth_item)); + if (++que->head == que->max) + que->head = 0; + que->count--; + } +} + +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; + + /* if queue empty, set pointers to beginning */ + if (!que->count) { + que->head = 0; + que->tail = -1; + } + + /* find new tail of the circular queue */ + if (++que->tail == que->max) + que->tail = 0; + if (++que->count > que->max) { + que->count = que->max; + /* lose oldest packet */ + if (++que->head == que->max) + que->head = 0; + que->loss++; + } + if (que->count > que->high) + que->high = que->count; + + /* set information in (new) tail item */ + item = &que->item[que->tail]; + item->type = type; + 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) +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;} +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* 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;} +void eth_show_dev (SMP_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__) +# include +# include +#endif /* xBSD */ + +#ifdef _WIN32 +# define WPCAP +#endif +#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 */ + +#ifdef USE_VDE_NETWORK +# include +#endif /* USE_VDE_NETWORK */ + +/* Allows windows to look up user-defined adapter names */ +#if defined(_WIN32) +# include +#endif + +AUTO_TLS(x_pcap_inited); +AUTO_INIT_LOCK(x_pcap_inited_lock, SIM_LOCK_CRITICALITY_NONE, 5000); + +static int lib_loaded = 0; /* 0=not loaded, 1=loaded, 2=library load failed, 3=Func load failed */ + +/* define pointers to pcap functions needed */ +static void (*p_pcap_close) (pcap_t *); +static int (*p_pcap_compile) (pcap_t *, struct bpf_program *, const char *, int, bpf_u_int32); +static int (*p_pcap_datalink) (pcap_t *); +static int (*p_pcap_dispatch) (pcap_t *, int, pcap_handler, u_char *); +static int (*p_pcap_findalldevs) (pcap_if_t **, char *); +static void (*p_pcap_freealldevs) (pcap_if_t *); +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 *); +#ifdef _WIN32 +static int (*p_pcap_setmintocopy) (pcap_t* handle, int); +static HANDLE (*p_pcap_getevent) (pcap_t *); +#else +#ifdef MUST_DO_SELECT +static int (*p_pcap_get_selectable_fd) (pcap_t *); +#endif +static int (*p_pcap_fileno) (pcap_t *); +#endif +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 const char* (*p_pcap_lib_version) (void); +static int (*p_pcap_setnonblock) (pcap_t *p, int nonblock, char *errbuf); + +static char* no_pcap = +#ifdef _WIN32 + "wpcap load failure"; +#else + "libpcap load failure"; +#endif + +#ifdef HAVE_DLOPEN +# include +#endif + +#if defined(USE_SHARED) && (defined(_WIN32) || defined(HAVE_DLOPEN)) +/* Dynamic DLL loading technique and modified source comes from + Etherial/WireShark capture_pcap.c */ + +/* Dynamic DLL load variables */ +#ifdef _WIN32 +static HINSTANCE hLib = 0; /* handle to DLL */ +#else +static void *hLib = 0; /* handle to Library */ +#endif +static const char* lib_name = +#ifdef _WIN32 + "wpcap.dll"; +#else +# define __STR_QUOTE(tok) #tok +# define __STR(tok) __STR_QUOTE(tok) + "libpcap." __STR(HAVE_DLOPEN); +#endif + +/* load function pointer from DLL */ +void load_function(char* function, void** func_ptr) { +#ifdef _WIN32 + *func_ptr = GetProcAddress(hLib, function); +#else + *func_ptr = dlsym(hLib, function); +#endif + if (*func_ptr == 0) { + char* msg = "Eth: Failed to find function '%s' in %s\r\n"; + + smp_printf (msg, function, lib_name); + if (sim_log) fprintf (sim_log, msg, function, lib_name); + lib_loaded = 3; + } +} + +/* load wpcap.dll as required */ +static void load_pcap_dll() +{ +#ifdef _WIN32 + hLib = LoadLibraryA(lib_name); +#else + hLib = dlopen(lib_name, RTLD_NOW); +#endif + +#if defined(__APPLE__) + if (hLib == 0) + { + lib_name = "/usr/lib/libpcap.A.dylib"; + hLib = dlopen(lib_name, RTLD_NOW); + } +#endif + + if (hLib == 0) + { + /* failed to load DLL */ + char* msg = "Eth: Failed to load %s\r\n"; + char* msg2 = +#ifdef _WIN32 + "Eth: You must install WinPcap 4.x to use networking\r\n"; +#else + "Eth: You must install libpcap to use networking\r\n"; +#endif + smp_printf (msg, lib_name); + smp_printf (msg2); + if (sim_log) + { + fprintf (sim_log, msg, lib_name); + fprintf (sim_log, msg2); + } + lib_loaded = 2; + return; + } + else + { + /* DLL loaded OK */ + lib_loaded = 1; + } + + /* 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); +#ifdef _WIN32 + load_function("pcap_setmintocopy", (void**) &p_pcap_setmintocopy); + load_function("pcap_getevent", (void**) &p_pcap_getevent); +#else +#ifdef MUST_DO_SELECT + load_function("pcap_get_selectable_fd", (void**) &p_pcap_get_selectable_fd); +#endif + load_function("pcap_fileno", (void**) &p_pcap_fileno); +#endif + +#ifdef USE_SETNONBLOCK + load_function("pcap_setnonblock", (void**) &p_pcap_setnonblock); +#endif + + if (lib_loaded == 1) + { + /* log successful load */ + const char* version = p_pcap_lib_version(); + smp_printf("%s\n", version); + if (sim_log) + fprintf(sim_log, "%s\n", version); + } +} +#else // USE_SHARED +static void load_pcap_dll() +{ + p_pcap_close = & pcap_close; + p_pcap_compile = & pcap_compile; + p_pcap_datalink = & pcap_datalink; + p_pcap_dispatch = & pcap_dispatch; + p_pcap_findalldevs = & pcap_findalldevs; + p_pcap_freealldevs = & pcap_freealldevs; + p_pcap_freecode = & pcap_freecode; + p_pcap_geterr = & pcap_geterr; + p_pcap_lookupnet = & pcap_lookupnet; + p_pcap_open_live = & pcap_open_live; +#ifdef _WIN32 + p_pcap_setmintocopy = & pcap_setmintocopy; + p_pcap_getevent = & pcap_getevent; +#else +#ifdef MUST_DO_SELECT + p_pcap_get_selectable_fd = & pcap_get_selectable_fd; +#endif + p_pcap_fileno = & pcap_fileno; +#endif + p_pcap_sendpacket = & pcap_sendpacket; + p_pcap_setfilter = & pcap_setfilter; + p_pcap_lib_version = & pcap_lib_version; +#ifdef USE_SETNONBLOCK + p_pcap_setnonblock = & pcap_setnonblock; +#endif + lib_loaded = 1; +} +#endif // USE_SHARED + +#define X_PCAP_INITED_NO ((void*) 0) +#define X_PCAP_INITED_YES ((void*) 1) + +static t_bool check_pcap_version(); + +static int load_pcap(void) +{ + if (X_PCAP_INITED_NO == tls_get_value(x_pcap_inited)) + { + AUTO_LOCK(x_pcap_inited_lock); + + switch(lib_loaded) + { + case 0: + load_pcap_dll(); + if (lib_loaded == 1 && !check_pcap_version()) + lib_loaded = 3; + break; + default: + break; + } + tls_set_value(x_pcap_inited, X_PCAP_INITED_YES); + return (lib_loaded == 1) ? 1 : 0; + } + else + { + return (lib_loaded == 1) ? 1 : 0; + } +} + +#if defined(_WIN32) +#define CHECK(cond) do { if (! (cond)) goto cleanup; } while (0) +static t_bool check_pcap_version() +{ + const char* version = p_pcap_lib_version(); + const char* pv = version; + int majver = 0; + t_bool done = FALSE; + static const char* prefix = "WinPcap version "; + CHECK(strlen(pv) > strlen(prefix)); + CHECK(0 == strncmp(pv, prefix, strlen(prefix))); + pv += strlen(prefix); + CHECK(isdigit(*pv)); + while (isdigit(*pv)) + { + majver = majver * 10 + (*pv++ - '0'); + } + CHECK(*pv == '.' || *pv == ' '); + /* for threading we need version 3.0 or later, but the code in this file is written for 4.x */ + CHECK(majver >= 4); + done = TRUE; + +cleanup: + + if (! done) + { + smp_printf ("Version 4.0 or later of PCAP is required, installed version: ", version); + if (sim_log) + { + fprintf (sim_log, "Version 4.0 or later of PCAP is required, installed version: ", version); + } + } + + return done; +} +#else +static t_bool check_pcap_version() +{ + return TRUE; +} +#endif + +/* define functions with dynamic revectoring */ +static void x_pcap_close(pcap_t* a) { + if (load_pcap() != 0) { + p_pcap_close(a); + } +} + +AUTO_INIT_DEVLOCK(pcap_compile_lock); + +/* OpenBSD has an ancient declaration of pcap_compile which doesn't have a const in the bpf string argument */ +#if defined(__OpenBSD__) +static int x_pcap_compile(pcap_t* a, struct bpf_program* b, char* c, int d, bpf_u_int32 e) { +#else +static int x_pcap_compile(pcap_t* a, struct bpf_program* b, const char* c, int d, bpf_u_int32 e) { +#endif + if (load_pcap() != 0) + { + /* pcap_compile is not thread-safe */ + AUTO_LOCK(pcap_compile_lock); + return p_pcap_compile(a, b, c, d, e); + } else { + return 0; + } +} + +static int x_pcap_datalink(pcap_t* a) { + if (load_pcap() != 0) { + return p_pcap_datalink(a); + } else { + return 0; + } +} + +static int x_pcap_dispatch(pcap_t* a, int b, pcap_handler c, u_char* d) { + if (load_pcap() != 0) { + return p_pcap_dispatch(a, b, c, d); + } else { + return 0; + } +} + +static int x_pcap_findalldevs(pcap_if_t** a, char* b) { + if (load_pcap() != 0) { + return p_pcap_findalldevs(a, b); + } else { + *a = 0; + strcpy(b, no_pcap); + return -1; + } +} + +static void x_pcap_freealldevs(pcap_if_t* a) { + if (load_pcap() != 0) { + p_pcap_freealldevs(a); + } +} + +static void x_pcap_freecode(struct bpf_program* a) { + if (load_pcap() != 0) { + p_pcap_freecode(a); + } +} + +static char* x_pcap_geterr(pcap_t* a) { + if (load_pcap() != 0) { + return p_pcap_geterr(a); + } else { + return (char*) 0; + } +} + +static int x_pcap_lookupnet(const char* a, bpf_u_int32* b, bpf_u_int32* c, char* d) { + if (load_pcap() != 0) { + return p_pcap_lookupnet(a, b, c, d); + } else { + return 0; + } +} + +static pcap_t* x_pcap_open_live(const char* a, int b, int c, int d, char* e) { + if (load_pcap() != 0) { + return p_pcap_open_live(a, b, c, d, e); + } else { + return (pcap_t*) 0; + } +} + +#ifdef _WIN32 +static int x_pcap_setmintocopy(pcap_t* a, int b) { + if (load_pcap() != 0) { + return p_pcap_setmintocopy(a, b); + } else { + return 0; + } +} + +static HANDLE x_pcap_getevent(pcap_t* a) { + if (load_pcap() != 0) { + return p_pcap_getevent(a); + } else { + return (HANDLE) 0; + } +} + +#else +#ifdef MUST_DO_SELECT +static int x_pcap_get_selectable_fd(pcap_t* a) { + if (load_pcap() != 0) { + return p_pcap_get_selectable_fd(a); + } else { + return 0; + } +} +#endif + +static int x_pcap_fileno(pcap_t * a) { + if (load_pcap() != 0) { + return p_pcap_fileno(a); + } else { + return 0; + } +} +#endif + +static int x_pcap_sendpacket(pcap_t* a, const u_char* b, int c) { + if (load_pcap() != 0) { + return p_pcap_sendpacket(a, b, c); + } else { + return 0; + } +} + +static int x_pcap_setfilter(pcap_t* a, struct bpf_program* b) { + if (load_pcap() != 0) { + /* it is not quite clear whether setfilter is thread-safe */ + AUTO_LOCK(pcap_compile_lock); + return p_pcap_setfilter(a, b); + } else { + return 0; + } +} + +#ifdef USE_SETNONBLOCK +static int x_pcap_setnonblock(pcap_t *p, int nonblock, char *errbuf) +{ + if (load_pcap() != 0) { + return p_pcap_setnonblock(p, nonblock, errbuf); + } else { + return 0; + } +} +#endif + + +/* Some platforms have always had pcap_sendpacket */ +#if defined(_WIN32) || defined(__VMS) +# define HAS_PCAP_SENDPACKET 1 +#else +/* The latest libpcap and WinPcap all have pcap_sendpacket */ +# if !defined(NEED_PCAP_SENDPACKET) +# define HAS_PCAP_SENDPACKET 1 +# endif +#endif + +#if !defined(HAS_PCAP_SENDPACKET) +/* libpcap has no function to write a packet, so we need to implement + pcap_sendpacket() for compatibility with the WinPcap base code. + Return value: 0=Success, -1=Failure */ +static int x_pcap_sendpacket(pcap_t* handle, const u_char* msg, int len) +{ +#if defined(__linux) + return (send(pcap_fileno(handle), msg, len, 0) == len)? 0 : -1; +#else + return (write(pcap_fileno(handle), msg, len) == len)? 0 : -1; +#endif /* linux */ +} +#endif /* !HAS_PCAP_SENDPACKET */ + +#ifdef _WIN32 +//#include +//#include + +/* extracted from WinPcap's Packet32.h */ +struct _PACKET_OID_DATA { + uint32 Oid; ///< OID code. See the Microsoft DDK documentation or the file ntddndis.h + ///< for a complete list of valid codes. + uint32 Length; ///< Length of the data field + uint8 Data[1]; ///< variable-lenght field that contains the information passed to or received + ///< from the adapter. +}; +typedef struct _PACKET_OID_DATA PACKET_OID_DATA, *PPACKET_OID_DATA; +typedef void **LPADAPTER; +#define OID_802_3_CURRENT_ADDRESS 0x01010102 /* Extracted from ntddmdis.h */ + +typedef LPADAPTER (*PacketOpenAdapter_t)(PCHAR AdapterName); +typedef VOID (*PacketCloseAdapter_t)(LPADAPTER lpAdapter); +typedef BOOLEAN (*PacketRequest_t)(LPADAPTER AdapterObject,BOOLEAN Set,PPACKET_OID_DATA OidData); + +static int x_pcap_mac_if_win32(char *AdapterName, unsigned char MACAddress[6]) +{ + LPADAPTER lpAdapter; + PPACKET_OID_DATA OidData; + int Status; + int ReturnValue; +#ifdef _WIN32 + HINSTANCE hDll; /* handle to DLL */ +#else + static void *hDll = NULL; /* handle to Library */ + typedef int BOOLEAN; +#endif + typedef LPADAPTER (*PacketOpenAdapter_proc_t)(char *AdapterName); + typedef void (*PacketCloseAdapter_proc_t)(LPADAPTER lpAdapter); + typedef int (*PacketRequest_proc_t)(LPADAPTER AdapterObject,BOOLEAN Set,PPACKET_OID_DATA OidData); + + PacketOpenAdapter_proc_t p_PacketOpenAdapter; + PacketCloseAdapter_proc_t p_PacketCloseAdapter; + PacketRequest_proc_t p_PacketRequest; + +#ifdef _WIN32 + hDll = LoadLibraryA("packet.dll"); + p_PacketOpenAdapter = (PacketOpenAdapter_proc_t) GetProcAddress(hDll, "PacketOpenAdapter"); + p_PacketCloseAdapter = (PacketCloseAdapter_proc_t) GetProcAddress(hDll, "PacketCloseAdapter"); + p_PacketRequest = (PacketRequest_proc_t) GetProcAddress(hDll, "PacketRequest"); +#else + hDll = dlopen("packet.dll", RTLD_NOW); + p_PacketOpenAdapter = (PacketOpenAdapter_proc_t) dlsym(hDll, "PacketOpenAdapter"); + p_PacketCloseAdapter = (PacketCloseAdapter_proc_t) dlsym(hDll, "PacketCloseAdapter"); + p_PacketRequest = (PacketRequest_proc_t) dlsym(hDll, "PacketRequest"); +#endif + + /* Open the selected adapter */ + + lpAdapter = p_PacketOpenAdapter(AdapterName); + + if (!lpAdapter || lpAdapter == (void*) -1 || *lpAdapter == (void*) -1) { +#ifdef _WIN32 + FreeLibrary(hDll); +#else + dlclose(hDll); +#endif + return -1; + } + + /* Allocate a buffer to get the MAC adress */ + + OidData = (PPACKET_OID_DATA) malloc(6 + sizeof(PACKET_OID_DATA)); + if (OidData == NULL) { + p_PacketCloseAdapter(lpAdapter); +#ifdef _WIN32 + FreeLibrary(hDll); +#else + dlclose(hDll); +#endif + 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); +#ifdef _WIN32 + FreeLibrary(hDll); +#else + dlclose(hDll); +#endif + 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 (!x_pcap_mac_if_win32(devname, dev->host_nic_phy_hw_addr)) + dev->have_host_nic_phy_addr = 1; +#else + if (1) + { + char command[1024]; + FILE *f; + int i; + static const char *patterns[] = + { + "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]", + "egrep [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]", + NULL + }; + + if (0 == strncmp("vde:", devname, 4)) + return; + memset(command, 0, sizeof(command)); + for (i = 0; patterns[i] && 0 == dev->have_host_nic_phy_addr; ++i) + { + snprintf(command, sizeof(command)-1, "ifconfig %s | %s >NIC.hwaddr", devname, patterns[i]); + system(command); + if (f = fopen("NIC.hwaddr", "r")) + { + while (0 == dev->have_host_nic_phy_addr) + { + 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; + } + } + else + { + break; + } + } + 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) + +static void * +_eth_reader(void *arg) +{ +ETH_DEV* volatile dev = (ETH_DEV*)arg; +int status = 0; /* initialize to suppress false GCC warning */ +#if defined(_WIN32) +HANDLE hWait = x_pcap_getevent ((pcap_t*)dev->handle); +#else +int sel_ret; +int do_select = 0; +int select_fd = 0; /* initialize to suppress false GCC warning */ + +switch (dev->eth_api) { + case ETH_API_PCAP: +#if defined(MUST_DO_SELECT) + do_select = 1; + select_fd = x_pcap_get_selectable_fd((pcap_t *)dev->handle); +#endif + break; + case ETH_API_TAP: + case ETH_API_VDE: + do_select = 1; + select_fd = dev->fd_handle; + break; + } +#endif + +while (dev->handle) { +#if defined(_WIN32) + if (WAIT_OBJECT_0 == WaitForSingleObject (hWait, 250)) { +#else + fd_set setl; + struct timeval timeout; + + if (do_select) { + FD_ZERO(&setl); + FD_SET(select_fd, &setl); + timeout.tv_sec = 0; + timeout.tv_usec = 250*1000; + sel_ret = select(1+select_fd, &setl, NULL, NULL, &timeout); + } + else + sel_ret = 1; + if (sel_ret < 0 && errno != EINTR) break; + if (sel_ret > 0) { +#endif /* _WIN32 */ + if (!dev->handle) + break; + /* dispatch read request queue available packets */ + switch (dev->eth_api) { + case ETH_API_PCAP: + status = x_pcap_dispatch ((pcap_t*)dev->handle, -1, &_eth_callback, (u_char*)dev); + break; +#ifdef USE_TAP_NETWORK + case ETH_API_TAP: + if (1) { + 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.caplen = header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } + else + status = 0; + } + break; +#endif /* USE_TAP_NETWORK */ +#ifdef USE_VDE_NETWORK + case ETH_API_VDE: + if (1) { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; + + memset(&header, 0, sizeof(header)); + len = vde_recv((VDECONN *)dev->handle, buf, sizeof(buf), 0); + if (len > 0) { + status = 1; + header.caplen = header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } + else + status = 0; + } + break; +#endif /* USE_VDE_NETWORK */ + } + if (status > 0 && dev->asynch_io) { + int wakeup_needed; + + dev->lock->lock(); + wakeup_needed = (dev->read_queue.count != 0); + dev->lock->unlock(); + if (wakeup_needed) { + sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); + sim_asynch_activate_abs (dev->dptr->units[0], dev->asynch_io_latency); + } + } + } + } + +return NULL; +} + +static void* +_eth_writer(void* arg) +{ +ETH_DEV* volatile dev = (ETH_DEV*) arg; +struct eth_write_request* request; + +dev->writer_lock->lock(); +while (dev->handle) +{ + /* + * ToDo: consider whether this sequence can be optimized by leveraging directly pthreads better + * (would it actually translate to tighter futex sequence calls?) or SignalObjectAndWait + */ + dev->writer_lock->unlock(); + dev->writer_cond->wait_and_clear(); + dev->writer_lock->lock(); + while (request = dev->write_requests) + { + /* Pull buffer off request list */ + dev->write_requests = request->next; + dev->writer_lock->unlock(); + + dev->write_status = _eth_write(dev, &request->packet, NULL); + + dev->writer_lock->lock(); + /* Put buffer on free buffer list */ + request->next = dev->write_buffers; + dev->write_buffers = request; + } +} +dev->writer_lock->unlock(); + +return NULL; +} + +SMP_THREAD_ROUTINE_DECL _eth_reader_main(void* arg) +{ + ETH_DEV* dev = (ETH_DEV*) arg; + char tname[16]; + + sim_try + { + sim_debug(dev->dbit, dev->dptr, "Ethernet Reader Thread Starting\n"); + + smp_thread_init(); + + run_scope_context* rscx = new run_scope_context(NULL, SIM_THREAD_TYPE_IOP, dev->reader_thread); + rscx->set_current(); + + smp_set_thread_priority(SIMH_THREAD_PRIORITY_IOP); + sprintf(tname, "IOP_%s_RD", dev->dptr->name); + smp_set_thread_name(tname); + + _eth_reader(dev); + } + sim_catch (sim_exception_SimError, exc) + { + fprintf(smp_stderr, "\nFatal error in %s simulator, unexpected exception while executing network reader thread\n", sim_name); + fprintf(smp_stderr, "Exception cause: %s\n", exc->get_message()); + fprintf(smp_stderr, "Terminating the simulator abnormally...\n"); + exit(1); + } + sim_end_try + + sim_debug(dev->dbit, dev->dptr, "Ethernet Reader Thread Exiting\n"); + + SMP_THREAD_ROUTINE_END; +} + +SMP_THREAD_ROUTINE_DECL _eth_writer_main(void* arg) +{ + ETH_DEV* dev = (ETH_DEV*) arg; + char tname[16]; + + sim_try + { + sim_debug(dev->dbit, dev->dptr, "Ethernet Writer Thread Starting\n"); + + smp_thread_init(); + + run_scope_context* rscx = new run_scope_context(NULL, SIM_THREAD_TYPE_IOP, dev->writer_thread); + rscx->set_current(); + + smp_set_thread_priority(SIMH_THREAD_PRIORITY_IOP); + sprintf(tname, "IOP_%s_WR", dev->dptr->name); + smp_set_thread_name(tname); + + _eth_writer(dev); + } + sim_catch (sim_exception_SimError, exc) + { + fprintf(smp_stderr, "\nFatal error in %s simulator, unexpected exception while executing network writer thread\n", sim_name); + fprintf(smp_stderr, "Exception cause: %s\n", exc->get_message()); + fprintf(smp_stderr, "Terminating the simulator abnormally...\n"); + exit(1); + } + sim_end_try + + sim_debug(dev->dbit, dev->dptr, "Ethernet Writer Thread Exiting\n"); + + SMP_THREAD_ROUTINE_END; +} + +#endif + +t_stat eth_set_async (ETH_DEV *dev, int latency) +{ +#if !defined(USE_READER_THREAD) +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 = TRUE; +dev->lock->lock(); +wakeup_needed = (dev->read_queue.count != 0); +dev->lock->unlock(); +if (wakeup_needed) { + sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); + sim_activate_abs (dev->dptr->units[0], dev->asynch_io_latency); + } +#endif +return SCPE_OK; +} + +t_stat eth_clr_async (ETH_DEV *dev) +{ +#if !defined(USE_READER_THREAD) +return SCPE_NOFNC; +#else +/* make sure device exists */ +if (!dev) return SCPE_UNATT; + +dev->asynch_io = FALSE; +return SCPE_OK; +#endif +} + +static void eth_register_perf_object(ETH_DEV* dev, smp_lock* object, const char* subname) +{ + char perfname[128]; + sprintf(perfname, "%s.ether.%s", dev->dptr->name, subname); + perf_register_object(perfname, object, TRUE); +} + +t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) +{ +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); + +/* translate name of type "ethX" to real device name */ +if ((strlen(name) == 4) + && (tolower(name[0]) == 'e') + && (tolower(name[1]) == 't') + && (tolower(name[2]) == 'h') + && isdigit(name[3]) + ) { + num = atoi(&name[3]); + savname = eth_getname(num, temp); + if (savname == 0) /* didn't translate */ + return SCPE_OPENERR; + } +else { + /* are they trying to use device description? */ + savname = eth_getname_bydesc(name, temp); + if (savname == 0) { /* didn't translate */ + /* probably is not ethX and has no description */ + savname = eth_getname_byname(name, temp); + if (savname == 0) /* didn't translate */ + savname = name; + } + } + +/* attempt to connect device */ +memset(errbuf, 0, sizeof(errbuf)); +if (0 == strncmp("tap:", savname, 4)) { + 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"; + smp_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); + } +#if defined(__APPLE__) + if (1) { + struct ifreq ifr; + int s; + + memset (&ifr, 0, sizeof(ifr)); + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, savname, sizeof(ifr.ifr_name)); + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) { + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) >= 0) { + ifr.ifr_flags |= IFF_UP; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr)) { + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + close(tun); + } + } + close(s); + } + } +#endif + } + 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 (0 == errbuf[0]) { + dev->eth_api = ETH_API_TAP; + dev->handle = (void *)1; /* Flag used to indicated open */ + } + } +else + if (0 == strncmp("vde:", savname, 4)) { +#if defined(USE_VDE_NETWORK) + struct vde_open_args voa; + + memset(&voa, 0, sizeof(voa)); + if (!strcmp(savname, "vde:vdedevice")) { + msg = "Eth: Must specify actual vde device name (i.e. vde:/tmp/switch)\r\n"; + smp_printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; + } + if (!(dev->handle = (void*) vde_open(savname+4, "simh", &voa))) + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + else { + dev->eth_api = ETH_API_VDE; + dev->fd_handle = vde_datafd((VDECONN*)dev->handle); + } +#else + strncpy(errbuf, "No support for vde: network devices", sizeof(errbuf)-1); +#endif /* !defined(__linux) && !defined(USE_BSDTUNTAP) */ + } + else { + dev->handle = (void*) x_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"; + smp_printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; + } + dev->eth_api = ETH_API_PCAP; + } +if (errbuf[0]) { + msg = "Eth: open error - %s\r\n"; + smp_printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; + } +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 = (char*) malloc(strlen(savname)+1); +if (! dev->name) eth_panic_mem(); +strcpy(dev->name, savname); + +/* save debugging information */ +dev->dptr = dptr; +dev->dbit = 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->eth_api == ETH_API_PCAP) { + int one = 1; + ioctl(x_pcap_fileno((pcap_t*) dev->handle), BIOCSHDRCMPLT, &one); + } +#endif /* xBSD */ + +#if defined(USE_READER_THREAD) +if (1) +{ +#if defined(_WIN32) + x_pcap_setmintocopy ((pcap_t*) dev->handle, 0); +#endif + ethq_init (&dev->read_queue, 200); /* initialize FIFO queue */ + dev->asynch_io_latency = 0; + dev->lock = smp_lock::create(1000); + eth_register_perf_object(dev, dev->lock, "lock"); + dev->writer_lock = smp_lock::create(500); + eth_register_perf_object(dev, dev->writer_lock, "writer_lock"); + dev->self_lock = smp_lock::create(500); + eth_register_perf_object(dev, dev->self_lock, "self_lock"); + dev->writer_cond = smp_event::create(); + if (! dev->reader_thread_created) + { + smp_create_thread(_eth_reader_main, (void*) dev, &dev->reader_thread); + dev->reader_thread_created = TRUE; + } + if (! dev->writer_thread_created) + { + smp_create_thread(_eth_writer_main, (void*) dev, &dev->writer_thread); + dev->writer_thread_created = TRUE; + } +} +#else /* !defined(USE_READER_THREAD) */ +#ifdef USE_SETNONBLOCK +/* set ethernet device non-blocking so pcap_dispatch() doesn't hang */ +if ((dev->eth_api == ETH_API_PCAP) && (x_pcap_setnonblock ((pcap_t*) dev->handle, 1, errbuf) == -1)) { + msg = "Eth: Failed to set non-blocking: %s\r\n"; + smp_printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + } +#endif +#endif /* !defined(USE_READER_THREAD */ +#if defined(__APPLE__) +if (dev->eth_api == ETH_API_PCAP) { + /* 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(x_pcap_fileno((pcap_t*) dev->handle), BIOCIMMEDIATE, &v); + } +#endif +return SCPE_OK; +} + +t_stat eth_close(ETH_DEV* dev) +{ +char* msg = "Eth: closed %s\r\n"; +pcap_t *pcap; +int pcap_fd; + +/* make sure device exists */ +if (!dev) return SCPE_UNATT; + +/* close the device */ +pcap_fd = dev->fd_handle; +pcap = (pcap_t *)dev->handle; +dev->handle = NULL; +dev->fd_handle = 0; +dev->have_host_nic_phy_addr = 0; + +#if defined(USE_READER_THREAD) +if (dev->reader_thread_created) +{ + smp_wait_thread(dev->reader_thread); + dev->reader_thread_created = FALSE; +} +dev->writer_cond->set(); +if (dev->writer_thread_created) +{ + smp_wait_thread(dev->writer_thread); + dev->writer_thread_created = FALSE; +} +perf_unregister_object(dev->lock); delete dev->lock; dev->lock = NULL; +perf_unregister_object(dev->self_lock); delete dev->self_lock; dev->self_lock = NULL; +perf_unregister_object(dev->writer_lock); delete dev->writer_lock; dev->writer_lock = NULL; +if (1) { + struct eth_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 + +switch (dev->eth_api) { + case ETH_API_PCAP: + x_pcap_close(pcap); + break; +#ifdef USE_TAP_NETWORK + case ETH_API_TAP: + close(pcap_fd); + break; +#endif +#ifdef USE_VDE_NETWORK + case ETH_API_VDE: + vde_close((VDECONN*)pcap); + break; +#endif + } +printf (msg, dev->name); +if (sim_log) fprintf (sim_log, msg, dev->name); + +/* clean up the mess */ +free(dev->name); +eth_zero(dev); + +return SCPE_OK; +} + +t_stat eth_check_address_conflict (ETH_DEV* dev, + ETH_MAC* const mac) +{ +ETH_PACK send, recv; +t_stat status; +int responses = 0; +char mac_string[32]; + +eth_mac_fmt(mac, mac_string); +sim_debug(dev->dbit, dev->dptr, "Determining Address Conflict for MAC address: %s\n", mac_string); + +/* The process of checking address conflicts is used in two ways: + 1) to determine the behavior of the currently running packet + delivery facility regarding whether it may receive copies + of every packet sent (and how many). + 2) to verify if a MAC address which this facility is planning + to use as the source address of packets is already in use + by some other node on the local network + Case #1, doesn't require (and explicitly doesn't want) any + interaction or response from other systems on the LAN so + therefore no considerations regarding switch packet forwarding + are important. Meanwhile, Case #2 does require responses from + other components on the LAN to provide useful functionality. + The original designers of this mechanism did this when essentially + all LANs were single collision domains (i.e. ALL nodes which might + be affected by an address conflict were physically present on a single + Ethernet cable which might have been extended by a couple of repeaters). + Since that time, essentially no networks are single collision domains. + Thick and thinwire Ethernet cables don’t exist and very few networks + even have hubs. Today, essentially all LANs are deployed using one + or more layers of network switches. In a switched LAN environment, the + switches on the LAN ‘learn’ which ports on the LAN source traffic from + which MAC addresses and then forward traffic destined for particular + MAC address to the appropriate ports. If a particular MAC address is + already in use somewhere on the LAN, then the switches ‘know’ where + it is. The host based test using the loopback protocol is poorly + designed to detect this condition. This test is performed by the host + first changing the device’s Physical MAC address to the address which + is to be tested, and then sending a loopback packet FROM AND TO this + MAC address with a loopback reply to be sent by a system which may be + currently using the MAC address. If no reply is received, then the + MAC address is presumed to be unused. The sending of this packet will + result in its delivery to the right system since the switch port/MAC + address tables know where to deliver packets destined to this MAC + address, however the response it generates won’t be delivered to the + system performing the test since the switches on the LAN won’t know + about the local port being the right target for packets with this MAC + address. A better test design to detect these conflicts would be for + the testing system to send a loopback packet FROM the current physical + MAC address (BEFORE changing it) TO the MAC address being tested with + the loopback response coming to the current physical MAC address of + the device. If a response is received, then the address is in use and + the attempt to change the device’s MAC address should fail. Since we + can’t change the software running in these simulators to implement this + better conflict detection approach, we can still ‘do the right thing’ + in the sim_ether layer. We’re already handling the loopback test + packets specially since we always had to avoid receiving the packets + which were being sent, but needed to allow for the incoming loopback + packets to be properly dealt with. We can extend this current special + handling to change outgoing ‘loopback to self’ packets to have source + AND loopback destination addresses in the packets to be the host NIC’s + physical address. The switch network will already know the correct + MAC/port relationship for the host NIC’s physical address, so loopback + response packets will be delivered as needed. + + Code in _eth_write and _eth_callback provide the special handling to + perform the described loopback packet adjustments, and code in + eth_filter_hash makes sure that the loopback response packets are received. + + */ + +/* 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 */ +send.msg[14] = 0; /* Offset */ +send.msg[16] = 2; /* Forward */ +memcpy(&send.msg[18], mac, sizeof(ETH_MAC)); /* Forward Destination */ +send.msg[24] = 1; /* Reply */ + +eth_filter(dev, 1, (ETH_MAC *)mac, 0, 0); + +/* send the packet */ +status = _eth_write (dev, &send, NULL); +if (status != SCPE_OK) { + const char *msg = (dev->eth_api == ETH_API_PCAP) ? + "Eth: Error Transmitting packet: %s\r\n" + "You may need to run as root, or install a libpcap version\r\n" + "which is at least 0.9 from your OS vendor or www.tcpdump.org\r\n" : + "Eth: Error Transmitting packet: %s\r\n" + "You may need to run as root.\r\n"; + smp_printf(msg, strerror(errno)); + if (sim_log) fprintf (sim_log, msg, strerror(errno)); + return status; + } + +sim_os_ms_sleep (300); /* time for a conflicting host to respond */ + +/* empty the read queue and count the responses */ +do { + memset (&recv, 0, sizeof(ETH_PACK)); + status = eth_read (dev, &recv, NULL); + if (((0 == memcmp(send.msg+12, recv.msg+12, 2)) && /* Protocol Match */ + (0 == memcmp(send.msg, recv.msg+6, 6)) && /* Source Match */ + (0 == memcmp(send.msg+6, recv.msg, 6))) || /* Destination Match */ + (0 == memcmp(send.msg, recv.msg, 14))) /* Packet Match (Reflection) */ + 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; +} + +static +t_stat _eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) +{ +int status = 1; /* default to failure */ + +/* make sure device exists */ +if (!dev) return SCPE_UNATT; + +/* make sure packet exists */ +if (!packet) return SCPE_ARG; + +/* make sure packet is acceptable length */ +if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) { + int loopback_self_frame = LOOPBACK_SELF_FRAME(packet->msg, packet->msg); + + eth_packet_trace (dev, packet->msg, packet->len, "writing"); + + /* record sending of loopback packet (done before actual send to avoid race conditions with receiver) */ + if (loopback_self_frame) { + if (dev->have_host_nic_phy_addr) { + memcpy(&packet->msg[6], dev->host_nic_phy_hw_addr, sizeof(ETH_MAC)); + memcpy(&packet->msg[18], dev->host_nic_phy_hw_addr, sizeof(ETH_MAC)); + } +#ifdef USE_READER_THREAD + dev->self_lock->lock(); +#endif + dev->loopback_self_sent += dev->reflections; + dev->loopback_self_sent_total++; +#ifdef USE_READER_THREAD + dev->self_lock->unlock(); +#endif + } + + /* dispatch write request (synchronous; no need to save write info to dev) */ + switch (dev->eth_api) { + case ETH_API_PCAP: + status = x_pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len); + break; +#ifdef USE_TAP_NETWORK + case ETH_API_TAP: + status = ((packet->len == write(dev->fd_handle, (void *)packet->msg, packet->len)) ? 0 : -1); + break; +#endif +#ifdef USE_VDE_NETWORK + case ETH_API_VDE: + status = vde_send((VDECONN*)dev->handle, (void *)packet->msg, packet->len, 0); + if ((status == packet->len) || (status == 0)) + status = 0; + else + if ((status == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) + status = 0; + else + status = 1; + break; +#endif + } + /* On error, correct loopback bookkeeping */ + if ((status != 0) && loopback_self_frame) { +#ifdef USE_READER_THREAD + dev->self_lock->lock(); +#endif + dev->loopback_self_sent -= dev->reflections; + dev->loopback_self_sent_total--; +#ifdef USE_READER_THREAD + dev->self_lock->unlock(); +#endif + } + + } /* if packet->len */ + +/* call optional write callback function */ +if (routine) + (routine)(status); + +return ((status == 0) ? SCPE_OK : SCPE_IOERR); +} + +t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) +{ +#ifdef USE_READER_THREAD +struct eth_write_request *request; +int write_queue_size = 1; + +/* make sure device exists */ +if (!dev) return SCPE_UNATT; + +/* Get a buffer */ +dev->writer_lock->lock(); +if (request = dev->write_buffers) + dev->write_buffers = request->next; +dev->writer_lock->unlock(); +if (!request) +{ + request = (struct eth_write_request*) malloc(sizeof(*request)); + if (! request) eth_panic_mem(); +} +/* 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) */ +dev->writer_lock->lock(); +request->next = NULL; +if (dev->write_requests) { + struct eth_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; +dev->writer_lock->unlock(); + +/* Awaken writer thread to perform actual write */ +dev->writer_cond->set(); + +/* 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; + smp_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))) { + smp_printf("Inconsistent Computed Hash:\n"); + smp_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]); + smp_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]); + } +else { + smp_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]); + smp_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 (0xFFF) + 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*) (void*) endbytes; + + /* modified version of the above code to relax GCC warning */ + union + { + uint8 b[2]; + uint16 w; + } + endbytes; + + endbytes.b[0] = *((uint8 *)buffer); + endbytes.b[1] = 0; + barrier(); + cksum += endbytes.w; + } + + /* 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 orig_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 Checksums 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; + /* When we're performing LSO (Large Send Offload), we're given a + 'template' header which may not include a value being populated + in the IP header length (which is only 16 bits). + We process as payload everything which isn't known header data. */ + payload_len = len - (14 + 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.caplen = 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 + if (1) { + /* Debugging is easier if we read packets directly with pcap + (i.e. we can use Wireshark to verify packet contents) + we don't want to do this all the time for 2 reasons: + 1) sending through pcap involves kernel transitions and + 2) if the current system reflects sent packets, the + recieving side will receive and process 2 copies of + any packets sent this way. */ + 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; + eth_packet_trace_ex (dev, ((u_char *)IP)-14, len, "Fragmenting Jumbo TCP segment", 1, dev->dbit); + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_tcp_flags = ntohs(TCP->data_offset_and_flags); + /* When we're performing LSO (Large Send Offload), we're given a + 'template' header which may not include a value being populated + in the IP header length (which is only 16 bits). + We process as payload everything which isn't known header data. */ + payload_len = len - (14 + 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) { + TCP->data_offset_and_flags = htons(orig_tcp_flags&~(TCP_PSH_FLAG|TCP_FIN_FLAG|TCP_RST_FLAG)); + IP->total_len = htons(mtu_payload + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + } + else { + TCP->data_offset_and_flags = htons(orig_tcp_flags); + 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.caplen = header.len = 14 + ntohs(IP->total_len); + eth_packet_trace_ex (dev, ((u_char *)IP)-14, header.len, "reading TCP segment", 1, dev->dbit); +#if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET + if (1) { + /* Debugging is easier if we read packets directly with pcap + (i.e. we can use Wireshark to verify packet contents) + we don't want to do this all the time for 2 reasons: + 1) sending through pcap involves kernel transitions and + 2) if the current system reflects sent packets, the + recieving side will receive and process 2 copies of + any packets sent this way. */ + 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 Checksums 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; +int to_me = 0; +int from_me = 0; +int i; +int bpf_used = 0; + +if ((dev->have_host_nic_phy_addr) && + (LOOPBACK_PHYSICAL_RESPONSE(dev->host_nic_phy_hw_addr, dev->physical_addr, data))) { + u_char *datacopy = (u_char*) malloc(header->len); + if (! datacopy) eth_panic_mem(); + memcpy(datacopy, data, header->len); + memcpy(datacopy, dev->physical_addr, sizeof(ETH_MAC)); + memcpy(datacopy+18, dev->physical_addr, sizeof(ETH_MAC)); + _eth_callback(info, header, datacopy); + free(datacopy); + return; +} + +switch (dev->eth_api) { + case ETH_API_PCAP: +#ifdef USE_BPF + bpf_used = 1; + 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); + break; +#endif /* USE_BPF */ + case ETH_API_TAP: + case ETH_API_VDE: + bpf_used = 0; + to_me = 0; + 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; + } + + /* all multicast mode? */ + if (dev->all_multicast && (data[0] & 0x01)) to_me = 1; + + /* 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); + break; + } + +/* detect reception of loopback packet to our physical address */ +if ((LOOPBACK_SELF_FRAME(dev->physical_addr, data)) || + (dev->have_host_nic_phy_addr && + LOOPBACK_PHYSICAL_REFLECTION(dev->host_nic_phy_hw_addr, data))) { +#ifdef USE_READER_THREAD + dev->self_lock->lock(); +#endif + 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; + } + else + if (!bpf_used) + from_me = 0; +#ifdef USE_READER_THREAD + dev->self_lock->unlock(); +#endif + } + +if (bpf_used ? to_me : (to_me && !from_me)) { + if (header->len > ETH_MIN_JUMBO_FRAME) { + if (header->len <= header->caplen) /* Whole Frame captured? */ + _eth_fix_ip_jumbo_offload(dev, data, header->len); + else + ++dev->jumbo_truncated; + return; + } +#if defined(USE_READER_THREAD) + if (1) { + int crc_len = 0; + uint8 crc_data[4]; + uint32 len = header->len; + u_char* moved_data = NULL; + + if (header->len < ETH_MIN_PACKET) { /* Pad runt packets before CRC append */ + moved_data = (u_char*) malloc(ETH_MIN_PACKET); + if (! moved_data) eth_panic_mem(); + memcpy(moved_data, data, len); + memset(moved_data + len, 0, ETH_MIN_PACKET-len); + len = ETH_MIN_PACKET; + data = moved_data; + } + + /* 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); + else + crc_data[0] = 0; /* initialize to suppress false GCC warning */ + + eth_packet_trace (dev, data, len, "rcvqd"); + + dev->lock->lock(); + ethq_insert_data(&dev->read_queue, 2, data, 0, len, crc_len, crc_data, 0); + dev->lock->unlock(); + free(moved_data); + } +#else /* !USE_READER_THREAD */ + /* 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) + 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"); + + /* call optional read callback function */ + if (dev->read_callback) + (dev->read_callback)(0); +#endif + } +} + +int eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) +{ +int status = 1; /* initialize to suppress false GCC warning */ + +/* make sure device exists */ + +if (!dev) return 0; + +/* make sure packet exists */ +if (!packet) return 0; + +packet->len = 0; +#if !defined(USE_READER_THREAD) +/* set read packet */ +dev->read_packet = packet; + +/* set optional callback routine */ +dev->read_callback = routine; + +/* dispatch read request to either receive a filtered packet or timeout */ +do { + switch (dev->eth_api) { + case ETH_API_PCAP: + status = x_pcap_dispatch((pcap_t*)dev->handle, 1, &_eth_callback, (u_char*)dev); + break; +#ifdef USE_TAP_NETWORK + case ETH_API_TAP: + if (1) { + 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.caplen = header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } + else + status = 0; + } + break; +#endif /* USE_TAP_NETWORK */ +#ifdef USE_VDE_NETWORK + case ETH_API_VDE: + if (1) { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; + + memset(&header, 0, sizeof(header)); + len = vde_recv((VDECONN*)dev->handle, buf, sizeof(buf), 0); + if (len > 0) { + status = 1; + header.caplen = header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } + else + status = 0; + } + break; +#endif /* USE_VDE_NETWORK */ + } + } while ((status) && (0 == packet->len)); + +#else /* USE_READER_THREAD */ + + status = 0; + dev->lock->lock(); + if (dev->read_queue.count > 0) { + ETH_ITEM* item = &dev->read_queue.item[dev->read_queue.head]; + packet->len = item->packet.len; + 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); + } + dev->lock->unlock(); + if (status && routine) + routine(0); +#endif + +return status; +} + +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[114+66*ETH_FILTER_MAX]; +char errbuf[PCAP_ERRBUF_SIZE]; +char mac[20]; +char* buf2; +t_stat status; +#ifdef USE_BPF +struct bpf_program bpf; +char* msg; +#endif + +/* make sure device exists */ +if (!dev) return SCPE_UNATT; + +/* filter count OK? */ +if ((addr_count < 0) || (addr_count > ETH_FILTER_MAX)) + return SCPE_ARG; +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)); +dev->addr_count = addr_count; + +/* store other flags */ +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"); + for (i = 0; i < addr_count; i++) { + char mac[20]; + eth_mac_fmt(&dev->filter_address[i], mac); + sim_debug(dev->dbit, dev->dptr, " Addr[%d]: %s\n", i, mac); + } + if (dev->all_multicast) + sim_debug(dev->dbit, dev->dptr, "All Multicast\n"); + if (dev->promiscuous) + sim_debug(dev->dbit, dev->dptr, "Promiscuous\n"); + } + +/* setup BPF filters and other fields to minimize packet delivery */ +strcpy(buf, ""); + +/* construct destination filters - since the real ethernet interface was set + into promiscuous mode by eth_open(), we need to filter out the packets that + our simulated interface doesn't want. */ +if (!dev->promiscuous) { + 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); + } + 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 + by systems where WinPcap and libpcap cause packet reflections. Note that + some systems do not reflect packets at all. This *assumes* that the + simulated NIC will not send out packets with multicast source fields. */ +if ((addr_count > 0) && (dev->reflections > 0)) { + if (strlen(buf) > 0) + sprintf(&buf[strlen(buf)], " and "); + sprintf (&buf[strlen(buf)], "not ("); + buf2 = &buf[strlen(buf)]; + for (i = 0; i < addr_count; i++) { + if (dev->filter_address[i][0] & 0x01) continue; /* skip multicast addresses */ + eth_mac_fmt(&dev->filter_address[i], mac); + if (!strstr(buf2, mac)) /* eliminate duplicates */ + sprintf(&buf2[strlen(buf2)], "%s(ether src %s)", (*buf2) ? " or " : "", mac); + } + sprintf (&buf[strlen(buf)], ")"); + } +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 (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); + if (dev->have_host_nic_phy_addr) { + eth_mac_fmt(&dev->host_nic_phy_hw_addr, mac); + sprintf(&buf[strlen(buf)], "or ((ether dst %s) and (ether proto 0x9000))", 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 a required argument for compiling. The value, + in our case isn't actually interesting since the filters we generate + aren't referencing IP fields, networks or values */ +if ((dev->eth_api == ETH_API_PCAP) && (x_pcap_lookupnet((const char*) dev->name, &bpf_subnet, &bpf_netmask, errbuf)<0)) + bpf_netmask = 0; + +#ifdef USE_BPF +if (dev->eth_api == ETH_API_PCAP) { + /* compile filter string */ + if ((status = x_pcap_compile((pcap_t*) dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { + sprintf(errbuf, "%s", x_pcap_geterr((pcap_t*) dev->handle)); + msg = "Eth: pcap_compile error: %s\r\n"; + smp_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"; + smp_printf (msg, buf); + if (sim_log) fprintf (sim_log, msg, buf); + } + else { + /* apply compiled filter string */ + if ((status = x_pcap_setfilter((pcap_t*) dev->handle, &bpf)) < 0) { + sprintf(errbuf, "%s", x_pcap_geterr((pcap_t*) dev->handle)); + msg = "Eth: pcap_setfilter error: %s\r\n"; + smp_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 = x_pcap_setnonblock ((pcap_t*) dev->handle, 1, errbuf); +#endif /* USE_SETNONBLOCK */ + } + x_pcap_freecode(&bpf); + } +#ifdef USE_READER_THREAD + dev->lock->lock(); + ethq_clear (&dev->read_queue); /* Empty FIFO Queue when filter list changes */ + dev->lock->unlock(); +#endif + } +#endif /* USE_BPF */ + +return SCPE_OK; +} + +/* + The libpcap provided API pcap_findalldevs() on most platforms, will + leverage the getifaddrs() API if it is available in preference to + alternate platform specific methods of determining the interface list. + + A limitation of getifaddrs() is that it returns only interfaces which + have associated addresses. This may not include all of the interesting + interfaces that we are interested in since a host may have dedicated + interfaces for a simulator, which is otherwise unused by the host. + + One could hand craft the the build of libpcap to specifically use + alternate methods to implement pcap_findalldevs(). However, this can + get tricky, and would then result in a sort of deviant libpcap. + + This routine exists to allow platform specific code to validate and/or + extend the set of available interfaces to include any that are not + returned by pcap_findalldevs. + +*/ +int eth_host_devices(int used, int max, ETH_LIST* list) +{ +pcap_t* conn; +int i, j, datalink = DLT_EN10MB; /* init datalink to suppress false GCC warning */ +char errbuf[PCAP_ERRBUF_SIZE]; + +for (i=0; i sizeof(regval))) { + RegCloseKey (reghnd); + continue; + } + /* registry value seems OK, finish up and replace description */ + RegCloseKey (reghnd ); + sprintf (list[i].desc, "%s", regval); + } + } /* for */ +#endif + +#ifdef USE_TAP_NETWORK +if (used < max) { +#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 +#ifdef USE_VDE_NETWORK +if (used < max) { + sprintf(list[used].name, "%s", "vde:device"); + sprintf(list[used].desc, "%s", "Integrated VDE support"); + ++used; + } +#endif + +return used; +} + +int eth_devices(int max, ETH_LIST* list) +{ +int i = 0; +#ifndef DONT_USE_PCAP_FINDALLDEVS +pcap_if_t* alldevs = NULL; +pcap_if_t* dev; +char errbuf[PCAP_ERRBUF_SIZE]; + +memset(list, 0, max*sizeof(*list)); +errbuf[0] = '\0'; +/* retrieve the device list */ +if (x_pcap_findalldevs(&alldevs, errbuf) == -1) { + char* msg = "Eth: error in pcap_findalldevs: %s\r\n"; + smp_printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + } +else { + if (alldevs == NULL && errbuf[0]) + { + char* msg = "Eth: warning: %s\r\n"; + smp_printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + } + /* copy device list into the passed structure */ + for (i=0, dev=alldevs; dev && (i < max); dev=dev->next, ++i) { + if ((dev->flags & PCAP_IF_LOOPBACK) || (!strcmp("any", dev->name))) continue; + strncpy(list[i].name, dev->name, sizeof(list[i].name)-1); + if (dev->description) + strncpy(list[i].desc, dev->description, sizeof(list[i].desc)-1); + else + strncpy(list[i].desc, "No description available", sizeof(list[i].desc)-1); + } + + /* free device list */ + x_pcap_freealldevs(alldevs); + } +#endif + +/* Add any host specific devices and/or validate those already found */ +i = eth_host_devices(i, max, list); + +/* return device count */ +return i; +} + +void eth_show_dev (SMP_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 (dev->jumbo_truncated) + fprintf(st, " Jumbo Truncated: %d\n", dev->jumbo_truncated); +#if defined(USE_READER_THREAD) +fprintf(st, " Asynch Interrupts: %s\n", dev->asynch_io ? "Enabled" : "Disabled"); +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/src/sim_ether.h b/src/sim_ether.h new file mode 100644 index 0000000..179cbe0 --- /dev/null +++ b/src/sim_ether.h @@ -0,0 +1,313 @@ +/* sim_ether.h: OS-dependent network information + ------------------------------------------------------------------------------ + + Copyright (c) 2002-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"), + 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 + THE AUTHOR 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 the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + Modification history: + + 17-Nov-11 MP Added dynamic loading of libpcap on *nix platforms + 30-Oct-11 MP Added support for vde (Virtual Distributed Ethernet) networking + 18-Apr-11 MP Fixed race condition with self loopback packets in + multithreaded environments + 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 + 05-Jan-04 DTH Added eth_mac_scan + 26-Dec-03 DTH Added ethernet show and queue functions from pdp11_xq + 23-Dec-03 DTH Added status to packet + 01-Dec-03 DTH Added reflections, tweaked decnet fix items + 25-Nov-03 DTH Verified DECNET_FIX, reversed ifdef to mainstream code + 14-Nov-03 DTH Added #ifdef DECNET_FIX for problematic duplicate detection code + 07-Jun-03 MP Added WIN32 support for DECNET duplicate address detection. + 05-Jun-03 DTH Added used to struct eth_packet + 01-Feb-03 MP Changed some uint8 strings to char* to reflect usage + 22-Oct-02 DTH Added all_multicast and promiscuous support + 21-Oct-02 DTH Corrected copyright again + 16-Oct-02 DTH Fixed copyright + 08-Oct-02 DTH Integrated with 2.10-0p4, added variable vector and copyrights + 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11 + 15-Aug-02 DTH Started XQ simulation + + ------------------------------------------------------------------------------ +*/ + +#ifndef _SIM_ETHER_H +#define _SIM_ETHER_H + +#include "sim_defs.h" + +// quick development-time plug +#ifndef DONT_USE_READER_THREAD +//# define DONT_USE_READER_THREAD +#endif + +/* make common BSD code a bit easier to read in this file */ +/* OS/X seems to define and compile using one of these BSD types */ +#if defined(__NetBSD__) || defined (__OpenBSD__) || defined (__FreeBSD__) +# define xBSD 1 +#endif + +#if !defined(__FreeBSD__) && !defined(_WIN32) && !defined(VMS) && !defined(__APPLE__) +# define USE_SETNONBLOCK 1 +#endif + +#if (((defined(__sun__) && defined(__i386__)) || defined(__linux) || defined(__APPLE__)) && !defined(DONT_USE_READER_THREAD)) +# define USE_READER_THREAD 1 +#endif + +#if defined(DONT_USE_READER_THREAD) && defined(USE_READER_THREAD) +# undef USE_READER_THREAD +#endif + +/* make common winpcap code a bit easier to read in this file */ +#if defined(_WIN32) || defined(VMS) +# define PCAP_READ_TIMEOUT -1 +#else +# define PCAP_READ_TIMEOUT 1 +#endif + +/* set related values to have correct relationships */ +#if defined (USE_READER_THREAD) +# if defined (USE_SETNONBLOCK) +# undef USE_SETNONBLOCK +# endif /* USE_SETNONBLOCK */ +# undef PCAP_READ_TIMEOUT +# define PCAP_READ_TIMEOUT 15 +# if (!defined (xBSD) && !defined(_WIN32) && !defined(VMS)) || defined (USE_TAP_NETWORK) || defined (USE_VDE_NETWORK) +# define MUST_DO_SELECT 1 +# endif +#endif /* USE_READER_THREAD */ + +/* give priority to USE_NETWORK over USE_SHARED */ +#if defined(USE_NETWORK) && defined(USE_SHARED) +# undef USE_SHARED +#endif + +/* USE_SHARED only works on Windows or if HAVE_DLOPEN */ +#if defined(USE_SHARED) && !defined(_WIN32) && !defined(HAVE_DLOPEN) +# undef USE_SHARED +#endif + +/* + USE_BPF is defined to let this code leverage the libpcap/OS kernel provided + BPF packet filtering. This generally will enhance performance. It may not + be available in some environments and/or it may not work correctly, so + undefining this will still provide working code here. +*/ +#define USE_BPF 1 + +/* structure declarations */ + +#define ETH_PROMISC 1 /* promiscuous mode = true */ +#define ETH_TIMEOUT -1 /* read timeout in milliseconds (immediate) */ +#define ETH_FILTER_MAX 20 /* maximum address filters */ +#define ETH_DEV_NAME_MAX 256 /* maximum device name size */ +#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 65536 /* maximum ethernet jumbo frame size (or Offload Segment Size) */ +#define ETH_MAX_DEVICE 20 /* maximum ethernet devices */ +#define ETH_CRC_SIZE 4 /* ethernet CRC 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 LOOPBACK_SELF_FRAME(phy_mac, msg) \ + (((msg)[12] == 0x90) && ((msg)[13] == 0x00) && \ + ((msg)[14] == 0x00) && ((msg)[15] == 0x00) && \ + ((msg)[16] == 0x02) && ((msg)[17] == 0x00) && \ + ((msg)[24] == 0x01) && ((msg)[25] == 0x00) && \ + (memcmp(phy_mac, (msg), 6) == 0) && \ + (memcmp(phy_mac, (msg)+6, 6) == 0) && \ + (memcmp(phy_mac, (msg)+18, 6) == 0)) + +#define LOOPBACK_PHYSICAL_RESPONSE(host_phy, phy_mac, msg) \ + (((msg)[12] == 0x90) && ((msg)[13] == 0x00) && \ + ((msg)[14] == 0x08) && ((msg)[15] == 0x00) && \ + ((msg)[16] == 0x02) && ((msg)[17] == 0x00) && \ + ((msg)[24] == 0x01) && ((msg)[25] == 0x00) && \ + (memcmp(host_phy, (msg)+18, 6) == 0) && \ + (memcmp(host_phy, (msg), 6) == 0) && \ + (memcmp(phy_mac, (msg)+6, 6) == 0)) + +#define LOOPBACK_PHYSICAL_REFLECTION(host_phy, msg) \ + (((msg)[12] == 0x90) && ((msg)[13] == 0x00) && \ + ((msg)[14] == 0x00) && ((msg)[15] == 0x00) && \ + ((msg)[16] == 0x02) && ((msg)[17] == 0x00) && \ + ((msg)[24] == 0x01) && ((msg)[25] == 0x00) && \ + (memcmp(host_phy, (msg)+6, 6) == 0) && \ + (memcmp(host_phy, (msg)+18, 6) == 0)) + +struct eth_packet { + uint8 msg[ETH_FRAME_SIZE]; /* ethernet frame (message) */ + int len; /* packet length without CRC */ + int used; /* bytes processed (used in packet chaining) */ + int status; /* transmit/receive status */ + int crc_len; /* packet length with CRC */ +}; + +struct eth_item { + int type; /* receive (0=setup, 1=loopback, 2=normal) */ + struct eth_packet packet; +}; + +struct eth_queue { + int max; + int count; + int head; + int tail; + int loss; + int high; + struct eth_item* item; +}; + +struct eth_list { + char name[ETH_DEV_NAME_MAX]; + char desc[ETH_DEV_DESC_MAX]; +}; + +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; +typedef struct eth_queue ETH_QUE; +typedef struct eth_item ETH_ITEM; + +#if defined (USE_READER_THREAD) +struct eth_write_request +{ + struct eth_write_request* next; + ETH_PACK packet; +}; +#endif + +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 eth_api; /* Designator for which API is being used to move packets */ +#define ETH_API_PCAP 0 /* Pcap API in use */ +#define ETH_API_TAP 1 /* tun/tap API in use */ +#define ETH_API_VDE 2 /* VDE API in use */ + ETH_PCALLBACK read_callback; /* read callback function */ + ETH_PCALLBACK write_callback; /* write callback function */ + ETH_PACK* read_packet; /* read 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 */ + 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 */ + uint32 jumbo_truncated; /* Giant Frames too big for capture buffer - 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) */ +#if defined (USE_READER_THREAD) + t_bool asynch_io; /* Asynchronous Interrupt scheduling enabled */ + int32 asynch_io_latency; + ETH_QUE read_queue; + smp_thread_t reader_thread; + t_bool reader_thread_created; + smp_thread_t writer_thread; + t_bool writer_thread_created; + smp_lock* lock; + smp_lock* writer_lock; + smp_lock* self_lock; + smp_event* writer_cond; + struct eth_write_request* write_requests; + int write_queue_peak; + struct eth_write_request* write_buffers; + t_stat write_status; +#endif +}; + +typedef struct eth_device ETH_DEV; + +/* prototype declarations*/ + +t_stat eth_open (ETH_DEV* dev, char* name, /* open ethernet interface */ + DEVICE* dptr, uint32 dbit); +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 */ +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* 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 header+crc */ +void eth_packet_trace_ex (ETH_DEV* dev, const uint8 *msg, int len, const char* txt, int detail, uint32 reason); /* trace ethernet packet */ +t_stat eth_show (SMP_FILE* st, UNIT* uptr, /* show ethernet devices */ + int32 val, void* desc); +void eth_show_dev (SMP_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 */ + +t_stat ethq_init (ETH_QUE* que, int max); /* initialize FIFO queue */ +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/src/sim_fio.cpp b/src/sim_fio.cpp new file mode 100644 index 0000000..3e71303 --- /dev/null +++ b/src/sim_fio.cpp @@ -0,0 +1,382 @@ +/* sim_fio.c: simulator file 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. + + 03-Jun-11 MP Simplified VMS 64b support and made more portable + 02-Feb-11 MP Added sim_fsize_ex and sim_fsize_name_ex returning t_addr + Added export of sim_buf_copy_swapped and sim_buf_swap_data + 28-Jun-07 RMS Added VMS IA64 support (from Norm Lastovica) + 10-Jul-06 RMS Fixed linux conditionalization (from Chaskiel Grundman) + 15-May-06 RMS Added sim_fsize_name + 21-Apr-06 RMS Added FreeBSD large file support (from Mark Martinec) + 19-Nov-05 RMS Added OS/X large file support (from Peter Schorn) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 17-Jul-04 RMS Fixed bug in optimized sim_fread (reported by Scott Bailey) + 26-May-04 RMS Optimized sim_fread (suggested by John Dundas) + 02-Jan-04 RMS Split out from SCP + + This library includes: + + sim_finit - initialize package + sim_fopen - open file + sim_fread - endian independent read (formerly fxread) + sim_write - endian independent write (formerly fxwrite) + sim_fseek - extended (>32b) seek (formerly fseek_ext) + sim_fsize - get file size + sim_fsize_name - get file size of named file + sim_fsize_ex - get file size as a t_addr + sim_fsize_name_ex - get file size as a t_addr of named file + sim_buf_copy_swapped - copy data swapping elements along the way + sim_buf_swap_data - swap data elements inplace in buffer + + sim_fopen and sim_fseek are OS-dependent. The other routines are not. + sim_fsize is always a 32b routine (it is used only with small capacity random + access devices like fixed head disks and DECtapes). +*/ + +#include "sim_defs.h" + +// static unsigned char sim_flip[FLIP_SIZE]; +int32 sim_end = 1; /* 1 = little */ +AUTO_TLS(flip_tls_key); + +/* OS-independent, endian independent binary I/O package + + For consistency, all binary data read and written by the simulator + is stored in little endian data order. That is, in a multi-byte + data item, the bytes are written out right to left, low order byte + to high order byte. On a big endian host, data is read and written + from high byte to low byte. Consequently, data written on a little + endian system must be byte reversed to be usable on a big endian + system, and vice versa. + + These routines are analogs of the standard C runtime routines + fread and fwrite. If the host is little endian, or the data items + are size char, then the calls are passed directly to fread or + fwrite. Otherwise, these routines perform the necessary byte swaps. + Sim_fread swaps in place, sim_fwrite uses an intermediate buffer. +*/ + +int32 sim_finit (void) +{ + union {int32 i; char c[sizeof (int32)]; } end_test; + + end_test.i = 1; /* test endian-ness */ + sim_end = end_test.c[0]; + return sim_end; +} + +void sim_buf_swap_data (void *bptr, size_t size, size_t count) +{ + uint32 j; + int32 k; + unsigned char by, *sptr, *dptr; + + if (sim_end || (count == 0) || (size == sizeof (char))) + return; + for (j = 0, dptr = sptr = (unsigned char *) bptr; /* loop on items */ + j < count; j++) { + for (k = (int32)(size - 1); k >= (((int32) size + 1) / 2); k--) { + by = *sptr; /* swap end-for-end */ + *sptr++ = *(dptr + k); + *(dptr + k) = by; + } + sptr = dptr = dptr + size; /* next item */ + } +} + +size_t sim_fread (void *bptr, size_t size, size_t count, SMP_FILE *fptr) +{ + size_t c; + + if ((size == 0) || (count == 0)) /* check arguments */ + return 0; + c = fread (bptr, size, count, fptr); /* read buffer */ + if (sim_end || (size == sizeof (char)) || (c == 0)) /* le, byte, or err? */ + return c; /* done */ + sim_buf_swap_data (bptr, size, count); + return c; +} + +void sim_buf_copy_swapped (void *dbuf, void *sbuf, size_t size, size_t count) +{ + size_t j; + int32 k; + unsigned char *sptr = (unsigned char *)sbuf; + unsigned char *dptr = (unsigned char *)dbuf; + + if (sim_end || (size == sizeof (char))) + { + memcpy (dptr, sptr, size * count); + return; + } + for (j = 0; j < count; j++) { /* loop on items */ + for (k = (int32)(size - 1); k >= 0; k--) + *(dptr + k) = *sptr++; + dptr = dptr + size; + } +} + +size_t sim_fwrite (void *bptr, size_t size, size_t count, SMP_FILE *fptr) +{ + size_t c, nelem, nbuf, lcnt, total; + int32 i; + unsigned char *sptr; + + if (size == 0 || count == 0) /* check arguments */ + return 0; + if (sim_end || size == sizeof (char)) /* le or byte? */ + return fwrite (bptr, size, count, fptr); /* done */ + + t_byte* sim_flip = (t_byte*) tls_get_value(flip_tls_key); + if (sim_flip == NULL) + { + sim_flip = (t_byte*) malloc(FLIP_SIZE); + if (sim_flip == NULL) + { + smp_printf ("\nUnable to allocate I/O buffer: out of memory\n"); + if (sim_log) + fprintf (sim_log, "Unable to allocate I/O buffer: out of memory\n"); + return 0; + } + tls_set_value(flip_tls_key, sim_flip); + } + + nelem = FLIP_SIZE / size; /* elements in buffer */ + nbuf = count / nelem; /* number buffers */ + lcnt = count % nelem; /* count in last buf */ + if (lcnt) + nbuf = nbuf + 1; + else + lcnt = nelem; + total = 0; + sptr = (unsigned char *) bptr; /* init input ptr */ + for (i = (int32) nbuf; i > 0; i--) /* loop on buffers */ + { + c = (i == 1) ? lcnt: nelem; + sim_buf_copy_swapped (sim_flip, sptr, size, c); + sptr = sptr + size * count; + c = fwrite (sim_flip, size, c, fptr); + if (c == 0) + return total; + total = total + c; + } + return total; +} + +/* Forward Declaration */ + +static t_addr _sim_ftell (SMP_FILE *st); + +/* Get file size */ + +t_addr sim_fsize_ex (SMP_FILE *fp) +{ + t_addr pos, sz; + + if (fp == NULL) + return 0; + pos = _sim_ftell (fp); + sim_fseek (fp, 0, SEEK_END); + sz = _sim_ftell (fp); + sim_fseek (fp, pos, SEEK_SET); + return sz; +} + +t_addr sim_fsize_name_ex (char *fname) +{ + SMP_FILE *fp; + t_addr sz; + + if ((fp = sim_fopen (fname, "rb")) == NULL) + return 0; + sz = sim_fsize_ex (fp); + fclose (fp); + return sz; +} + +uint32 sim_fsize_name (char *fname) +{ + return (uint32)(sim_fsize_name_ex (fname)); +} + +uint32 sim_fsize (SMP_FILE *fp) +{ + return (uint32)(sim_fsize_ex (fp)); +} + +/* OS-dependent routines */ + +/* Optimized file open */ + +SMP_FILE *sim_fopen (const char *file, const char *mode) +{ +#if defined (VMS) + return smp_fopen (file, mode, "ALQ=32", "DEQ=4096", + "MBF=6", "MBC=127", "FOP=cbt,tef", "ROP=rah,wbh", "CTX=stm"); +#elif defined (USE_INT64) && defined (USE_ADDR64) && defined (__linux) + return smp_fopen64 (file, mode); +#else + return smp_fopen (file, mode); +#endif +} + +/* Long seek */ + +#if defined (USE_INT64) && defined (USE_ADDR64) + +/* 64b VMS */ + +#if (defined (__ALPHA) || defined (__ia64)) && defined (VMS) && (__DECC_VER >= 60590001) +#define _SIM_IO_FSEEK_EXT_ 1 + +int sim_fseek (SMP_FILE *st, t_addr offset, int whence) +{ + return fseeko (st, (off_t)offset, whence); +} + +static t_addr _sim_ftell (SMP_FILE *st) +{ + return (t_addr)(ftello (st)); +} + +#endif + +/* Alpha UNIX - natively 64b */ + +#if defined (__ALPHA) && defined (__unix__) /* Alpha UNIX */ +#define _SIM_IO_FSEEK_EXT_ 1 + +int sim_fseek (SMP_FILE *st, t_addr offset, int whence) +{ + return fseek (st, offset, whence); +} + +static t_addr _sim_ftell (SMP_FILE *st) +{ + return (t_addr)(ftell (st)); +} + +#endif + +/* Windows */ + +#if defined (_WIN32) +#define _SIM_IO_FSEEK_EXT_ 1 +#include + +int sim_fseek (SMP_FILE *st, t_addr offset, int whence) +{ + fpos_t fileaddr; + struct _stati64 statb; + + switch (whence) + { + case SEEK_SET: + fileaddr = offset; + break; + + case SEEK_END: + if (_fstati64 (_fileno (st), &statb)) + return (-1); + fileaddr = statb.st_size + offset; + break; + case SEEK_CUR: + if (fgetpos (st, &fileaddr)) + return (-1); + fileaddr = fileaddr + offset; + break; + + default: + errno = EINVAL; + return (-1); + } + + return fsetpos (st, &fileaddr); +} + +static t_addr _sim_ftell (SMP_FILE *st) +{ + fpos_t fileaddr; + if (fgetpos (st, &fileaddr)) + return (-1); + return (t_addr) fileaddr; +} + +#endif /* end Windows */ + +/* Linux */ + +#if defined (__linux) +#define _SIM_IO_FSEEK_EXT_ 1 + +int sim_fseek (SMP_FILE *st, t_addr xpos, int origin) +{ + return fseeko64(st, xpos, origin); +} + +static t_addr _sim_ftell (SMP_FILE *st) +{ + return (t_addr) ftello64(st); +} + +#endif /* end Linux with LFS */ + +/* Apple OS/X */ + +#if defined (__APPLE__) || defined (__FreeBSD__) +#define _SIM_IO_FSEEK_EXT_ 1 + +int sim_fseek (SMP_FILE *st, t_addr xpos, int origin) +{ + return fseeko(st, xpos, origin); +} + +static t_addr _sim_ftell (SMP_FILE *st) +{ + return (t_addr) ftello(st); +} + +#endif /* end Apple OS/X */ + +#endif /* end 64b seek defs */ + +/* Default: no OS-specific routine has been defined */ + +#if !defined (_SIM_IO_FSEEK_EXT_) +#define _SIM_IO_FSEEK_EXT_ 0 + +int sim_fseek (SMP_FILE *st, t_addr xpos, int origin) +{ + return fseek (st, (int32) xpos, origin); +} + +static t_addr _sim_ftell (SMP_FILE *st) +{ + return (t_addr) ftell(st); +} + +#endif + +const uint32 sim_taddr_64 = _SIM_IO_FSEEK_EXT_; \ No newline at end of file diff --git a/src/sim_fio.h b/src/sim_fio.h new file mode 100644 index 0000000..99ff0e9 --- /dev/null +++ b/src/sim_fio.h @@ -0,0 +1,51 @@ +/* sim_fio.h: simulator file I/O library headers + + 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 + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 02-Feb-11 MP Added sim_fsize_ex and sim_fsize_name_ex returning t_addr + Added export of sim_buf_copy_swapped and sim_buf_swap_data + 15-May-06 RMS Added sim_fsize_name + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 02-Jan-04 RMS Split out from SCP +*/ + +#ifndef _SIM_FIO_H_ +#define _SIM_FIO_H_ 0 + +#define FLIP_SIZE (1 << 16) /* flip buf size */ +#define fxread(a,b,c,d) sim_fread (a, b, c, d) +#define fxwrite(a,b,c,d) sim_fwrite (a, b, c, d) + +int32 sim_finit (void); +SMP_FILE *sim_fopen (const char *file, const char *mode); +int sim_fseek (SMP_FILE *st, t_addr offset, int whence); +size_t sim_fread (void *bptr, size_t size, size_t count, SMP_FILE *fptr); +size_t sim_fwrite (void *bptr, size_t size, size_t count, SMP_FILE *fptr); +uint32 sim_fsize (SMP_FILE *fptr); +uint32 sim_fsize_name (char *fname); +t_addr sim_fsize_ex (SMP_FILE *fptr); +t_addr sim_fsize_name_ex (char *fname); +void sim_buf_swap_data (void *bptr, size_t size, size_t count); +void sim_buf_copy_swapped (void *dptr, void *bptr, size_t size, size_t count); +#endif diff --git a/src/sim_rev.h b/src/sim_rev.h new file mode 100644 index 0000000..d27b411 --- /dev/null +++ b/src/sim_rev.h @@ -0,0 +1,2459 @@ +/* sim_rev.h: simulator revisions and current rev level + + 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. +*/ + +#ifndef _SIM_REV_H_ +#define _SIM_REV_H_ 0 + +#define SIM_MAJOR 3 +#define SIM_MINOR 8 +#define SIM_PATCH 3 +#define SIM_DELTA 0 + +#if defined(VM_VAX_MP) +# define VSMP_REVISION 1 +#endif + +/* V3.8 revision history + +patch date module(s) and fix(es) + + 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 (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: + - 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 (Walter F Mueller) + + pdp11_rl.c: + - added debug support + + 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 + + pdp8_sys.c: + - added link to FPP + + vax_cpu.c: + - 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) + - 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) + + 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 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): + - 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 + - Moved non-existent memory checks to WritePW + - Fixed mp_dms_jmp to accept lower bound, check write protection + - Corrected DMS violation register set conditions + - Refefined ABORT to pass address, moved def to hp2100_cpu.h + - Combined dms and dms_io routines + - JSB to 0/1 with W5 out and fence = 0 erroneously causes MP abort + - Unified I/O slot dispatch by adding DIBs for CPU, MP, and DMA + - Rewrote device I/O to model backplane signals + - EDT no longer passes DMA channel + - Added SET CPU IDLE/NOIDLE, idle detection for DOS/RTE + - Breakpoints on interrupt trap cells now work + + hp2100_cpu0.c (from 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): + - 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): + - 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): + - 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): + - Moved microcode function prototypes to hp2100_cpu1.h + - Removed option-present tests (now in UIG dispatchers) + + hp2100_cpu5.c (from 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): + - 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): + - Rewrote device I/O to model backplane signals + + hp2100_baci.c (from 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): + - Added REG_FIT to register variables < 32-bit size + + hp2100_dr.c (from Dave Bryan): + - Revised drc_boot to use ibl_copy + + hp2100_fp1.c (from Dave Bryan): + - Quieted bogus gcc warning in fp_exec + + hp2100_ipl.c (from 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): + - Changed CTIME register width to match documentation + + hp2100_mpx.c (from Dave Bryan): + - Implemented 12792C eight-channel terminal multiplexer + + hp2100_ms.c (from Dave Bryan): + - Revised to use AR instead of saved_AR in boot + + hp2100_mt.c (from Dave Bryan): + - Fixed missing flag after CLR command + - Moved write enable and format commands from MTD to MTC + + hp2100_mux.c (from 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): + - Implemented 12620A/12936A Privileged Interrupt Fences + + hp2100_sys.c (from 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 + - Added PIF device + - Moved fmt_char() function from hp2100_baci.c + - Added MPX device + + hp2100_cpu.h (from 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): + - Moved microcode function prototypes here + + hp2100_defs.h (from Dave Bryan): + - Added POLL_FIRST to indicate immediate connection attempt + - Rearranged declarations with hp2100_cpu.h + - Added PIF device + - Declared fmt_char() function + - 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) + + 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) + + 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) + + pdp11_dz.c: + - added MTAB_NC modifier on SET LOG command (found by Walter Mueller) + + pdp11_io.c, vax_io.c, vax780_uba.c: + - revised to use PDP-11 I/O library + + pdp11_io_lib.c: + - created common library for Unibus/Qbus support routines + + pdp11_cis.c, vax_cis.c: + - fixed bug in ASHP left overflow calc (Word/NibbleLShift) + - fixed bug in DIVx (LntDstr calculation) + + sds_lp.c: + - fixed loss of carriage control position on space op + + vax_stddev.c, vax780_stddev.c + - 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) + + sim_tmxr.c + - worked around Telnet negotiation problem with QCTerm (from Dave Bryan) + + gri_defs.h, gri_cpu.c, gri_sys.c: + - added GRI-99 support + + hp2100_baci.c (from Dave Bryan): + - Implemented 12966A Buffered Asynchronous Communications Interface simulator + + hp2100_cpu.c (from 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, + mp_mevff clear on interrupt with I/O instruction in trap cell + - Removed DBI support from 1000-M (was temporary for RTE-6/VM) + - Enabled EMA and VIS, added EMA, VIS, and SIGNAL debug flags + - Enabled SIGNAL instructions, SIG debug flag + - Fixed single stepping through interrupts + + hp2100_cpu0.c (from 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): + - 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): + - Fixed unsigned divide bug in .DDI + - Fixed unsigned multiply bug in .DMP + - Added implementation of DBI self-test + + hp2100_cpu4.c (from Dave Bryan): + - Fixed B register return bug in /CMRT + + hp2100_cpu5.c (from Holger Veit): + - Implemented RTE-6/VM Virtual Memory Area firmware + - Implemented RTE-IV Extended Memory Area firmware + + hp2100_cpu6.c (from Dave Bryan): + - Implemented RTE-6/VM OS accelerator firmware + + hp2100_cpu7.c (from Holger Veit): + - Implemented Vector Instruction Set and SIGNAL/1000 firmware + + hp2100_ds.c (from Dave Bryan): + - Corrected and verified ioCRS action + - Corrected DPTR register definition from FLDATA_GBL to DRDATA_GBL + + hp2100_fp.c (from Mark Pizzolato) + - Corrected fp_unpack mantissa high-word return + + hp2100_fp1.c (from 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): + - Sync mux poll with console poll for idle compatibility + + hp2100_stddev.c (from 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 + - Changed TTY console poll to 10 msec. real time + - Synchronized CLK with TTY if set for 10 msec. + - Added UNIT_IDLE to TTY and CLK + - Removed redundant control char handling definitions + - Changed TTY output wait from 100 to 200 for MSU BASIC + + hp2100_sys.c (from 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): + - 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): + - 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): + - Added BACI device + - Added 16/32-bit unsigned-to-signed conversions + - Changed TMR_MUX to TMR_POLL for idle support + - Added POLLMODE, sync_poll() declaration + - 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): + - added support for third-party 64KW memory + + nova_clk.c (from Bruce Ray): + - renamed to RTC, to match DG literature + + nova_cpu.c (from Bruce Ray): + - added support for third-party 64KW memory + - added Nova 3 "secret" instructions + - added CPU history support + + nova_dkp.c (from 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): + - 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): + - renamed to MTA, to match DG literature + - changed bootstrap code to DG official sequence + + nova_plt.c, nova_pt.c (from Bruce Ray): + - added 7B/8B support + + nova_sys.c (from 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) + + pdp11_cpumod.h, pdp11_cpumod.c: + - fixed write behavior of 11/70 MBRK, LOSIZE, HISIZE (found by Walter Mueller) + - added support to set default state of KDJ11B,E clock control register + + pdp11_dc.c: + - added support for DC11 + + pdp11_defs.h: + - added KE, KG, RC, DC support + - renamed DL11 devices + + pdp11_dl.c: + - renamed devices to DLI/DLO, to match DC11 + - added modem control + + pdp11_io.c: + - added autoconfigure support for DC11 + + pdp11_ke.c: + - added support for KE11A + + pdp11_kg.c (from John Dundas): + - added support for KG11A + + pdp11_rc.c (from John Dundas): + - added support for RC11 + + pdp11_sys.c: + - modified to allow -A, -B use with 8b devices + - added KE, KG, RC, DC support + - renamed DL11 devices + + vax_cmode.c, vax_io.c, vax780_uba.c: + - fixed declarations (from Mark Pizzolato) + + V3.7 revision history + + 3 02-Sep-07 scp.c: + - fixed bug in SET THROTTLE command + + pdp10_cpu.c: + - fixed non-portable usage in SHOW HISTORY routine + + pdp11_ta.c: + - forward op at BOT skips initial file gap + + pdp8_ct.c: + - forward op at BOT skips initial file gap + - fixed handling of BEOT + + 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) + - added dynamic loading of wpcap.dll; + - corrected exceed max index bug in ethX lookup + - corrected failure to look up ethernet device names in + the registry on Windows XP x64 + + sim_timer.c: + - fixed idle timer event selection algorithm + + h316_lp.c: + - fixed loss of last print line (from Theo Engel) + + h316_mt.c: + - fixed bug in write without stop (from Theo Engel) + + h316_stddev.c: + - fixed bug in clock increment (from 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) + - remove restriction on load-mode binary tape operations + + pdp1_cpu.c: + - fixed typo in SBS clear (from 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) + + vax_cpu.c: + - revised idle detector + + 1 14-May-07 scp.c: + - modified sim_instr invocation to call sim_rtcn_init_all + - fixed bug in get_sim_opt (reported by Don North) + - 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) + + all CPU's with clocks: + - removed clock initialization (now done in SCP) + + hp2100_cpu.c (from Dave Bryan): + - EDT passes input flag and DMA channel in dat parameter + + hp2100_ipl.c (from Dave Bryan): + - IPLI EDT delays DMA completion interrupt for TSB + + hp2100_mux.c (from 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 + - fixed to set "muxl_ibuf" in response to a transmit interrupt + - changed "mux_xbuf", "mux_rbuf" declarations from 8 to 16 bits + - fixed to set "mux_rchp" when a line break is received + - fixed incorrect "odd_par" table values + - reversed test in "RCV_PAR" to return "LIL_PAR" on odd parity + - rixed mux reset (ioCRS) to clear port parameters + - fixed to use PUT_DCH instead of PUT_CCH for data channel status + - added DIAG/TERM modifiers to implement diagnostic mode + + pdp11_cpumod.c: + - changed memory size routine to work with RESTORE + + pdp11_hk.c: + - NOP and DCLR (at least) do not check drive type + - MR2 and MR3 only updated on NOP + + pdp10_tu.c, pdp11_tu.c: + - TMK sets FCE only on read (found by Naoki Hamada) + + pdp11_xu.c: + - added missing FC_RMAL command + - cleared multicast on write + + vax_moddefs.h, vax_cpu1.c: + - separated PxBR and SBR mbz checks + + vax780_defs.h + - separated PxBR and SBR mbz checks + - modified mbz checks to reflect 780 microcode patches + (found by Naoki Hamada) + + vax_mmu.c: + - added address masking to all SBR-based memory reads + + 0 30-Jan-07 scp.c: + - implemented throttle commands + - added -e to control error processing in DO command files + (from Dave Bryan) + + sim_console.c: + - fixed handling of non-printable characters in KSR mode + + sim_tape.c: + - fixed bug in reverse operations for P7B-format tapes + - fixed bug in reverse operations across erase gaps + + sim_timer.c: + - added throttle support + - added idle support (based on work by Mark Pizzolato) + + gri_stddev.c, h316_stddev.c, pdp18b_tt1.c + - 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): + - 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 + controllers, and 21xx binary loader protection + - fixed DMS self-test instruction execution on 1000-M + - fixed indirect interrupt holdoff logic + + hp2100_ds.c: + - fixed REQUEST STATUS to clear status-1 (from Dave Bryan) + + hp2100_fp1.c: + - Added Floating Point Processor (from Dave Bryan) + + hp2100_lps.c: + - fixed diag-mode CLC response + + i7094_cpu.c: + - fixed new bug in halt IO wait loop + - added IFT, EFT expanded core test instructions + + id16_cpu.c, id32_cpu.c: + - removed separate multiplexor clock + - added idle support + + id_pas.c: + - synced multiplexor poll to real-time clock + + id_tt.c, id_ttp.c: + - fixed handling of non-printable characters in KSR mode + - synced keyboard poll to real-time clock + + id_uvc.c: + - changed line-time clock to be free-running + + pdp1_cpu.c: + - added 16-channel sequence break system (API) support + - added PDP-1D support + + pdp1_clk.c: + - first release + + pdp1_dcs.c: + - first release + + pdp1_stddev.c: + - separated TTI, TTO for API support + + pdp1_sys.c: + - added PDP-1D, 16-channel SBS, clock, DCS support + - fixed bugs in character input, block loader + + pdp10_cpu.c: + - added idle support + + pdp10_defs.h, pdp10_sys.c: + - added CR support + + pdp10_fe.c, pdp10_tim.c: + - synced keyboard poll to real-time clock + + pdp11_cr.c: + - revised for PDP-10 compatibility (CD11 only) + + pdp11_cpu.c: + - added idle support + - fixed bug in ASH -32 C value + + pdp11_rf.c: + - fixed unit mask (found by John Dundas) + + pdp11_stddev.c, vax_stddev.c, vax780_stddev.c: + - synced keyboard poll to real-time clock + - added clock coscheduling support + + pdp11_ta.c: + - first release + + pdp11_vh.c: + - synced service poll to real-time clock + - changed device to be off by default + + pdp11_dz.c, pdp11_xq.c, pdp11_xu.c: + - synced service poll to real-time clock + + pdp11_sys.c: + - fixed operand order in EIS instructions (found by W.F.J. Mueller) + - added TA11 support + + pdp18b_cpu.c: + - fixed incorrect value of PC on instruction fetch mem mmgt error + - fixed PDP-15 handling of mem mmgt traps (sets API 3) + - fixed PDP-15 handling of CAL API 4 (sets only if 0-3 inactive) + - fixed PDP-15 CAF to clear memory management mode register + - fixed boundary test in KT15/XVM (reported by Andrew Warkentin) + - added XVM RDCLK instruction + - added idle support and infinite loop detection + + pdp18b_rf.c: + - fixed bug, DSCD does not clear function register + + pdp18b_stddev.c: + - added PDP-15 program-selectable duplex handling instruction + - fixed PDP-15 handling of reader out-of-tape + - fixed handling of non-printable characters in KSR mode + - added XVM RDCLK instruction + - changed real-time clock to be free running + - synced keyboard poll to real-time clock + + pdp18b_tt1.c + - fixed handling of non-printable characters in KSR mode + + pdp18b_sys.c: + - added XVM RDCLK instruction + + pdp8_cpu.c: + - fixed SC value after DVI overflow (found by Don North) + - added idle support and infinite loop detection + + pdp8_ct.c: + - first release + + pdp8_clk.c: + - changed real-time clock to be free running + + pdp8_sys.c: + - added TA8E support + - added ability to disambiguate overlapping IOT definitions + + pdp8_tt.c: + - fixed handling of non-printable characters in KSR mode + - synced keyboard poll to real-time clock + + vax_cpu.c, vax_cpu1.c: + - added idle support + + vax_syscm.c: + - fixed operand order in EIS instructions (found by W.F.J. Mueller) + + + V3.6 revision history + + 1 25-Jul-06 sim_console.c: + - implemented SET/SHOW PCHAR + + all DECtapes: + - fixed conflict in ATTACH switches + + hp2100_ms.c (from Dave Bryan): + - added CAPACITY as alternate for REEL + - fixed EOT test for unlimited reel size + + i1620_cd.c (from Tom McBride): + - fixed card reader fgets call + - fixed card reader boot sequence + + i7094_cd.c: + - fixed problem with 80 column full cards + + i7094_cpu.c: + - fixed bug in halt IO wait loop + + i7094_sys.c: + - added binary loader (courtesy of Dave Pitt) + + pdp1_cpu.c: + - fixed bugs in MUS and DIV + + pdp11_cis.c: + - added interrupt tests to character instructions + - added 11/44 stack probe test to MOVCx (only) + + pdp11_dl.c: + - first release + + pdp11_rf.c: + - first release + + pdp11_stddev.c: + - added UC support to TTI, TTO + + pdp18b_cpu.c: + - fixed RESET to clear AC, L, and MQ + + pdp18b_dt.c: + - fixed checksum calculation bug for Type 550 + + pdp18b_fpp.c: + - fixed bugs in left shift, multiply + + pdp18b_stddev.c: + - fixed Baudot letters/figures inversion for PDP-4 + - fixed letters/figures tracking for PDP-4 + - fixed PDP-4/PDP-7 default terminal to be local echo + + pdp18b_sys.c: + - added Fiodec, Baudot display + - generalized LOAD to handle HRI, RIM, and BIN files + + pdp8_ttx.c: + - fixed bug in DETACH routine + + 0 15-May-06 scp.c: + - revised save file format to save options, unit capacity + + sim_tape.c, sim_tape.h: + - added support for finite reel size + - fixed bug in P7B write record + + most magtapes: + - added support for finite reel size + + h316_cpu.c: fixed bugs in LLL, LRL (found by Theo Engel) + + h316_lp.c: fixed bug in blanks backscanning (found by Theo Engel) + + h316_stddev.c: fixed bugs in punch state handling (found by Theo Engel) + + i1401_cpu.c: fixed bug in divide (reported by Van Snyder) + + i16_cpu.c: fixed bug in DH (found by Mark Hittinger) + + i32_cpu.c: + - fixed bug in DH (found by 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_idc.c: + - fixed WD/WH handling (found by Davis Johnson) + - fixed bug, nop command should be ignored (found by Davis Johnson) + + nova_cpu.c: fixed bug in DIVS (found by Mark Hittinger) + + pdp11_cis.c: (all reported by John Dundas) + - fixed bug in decode table + - fixed bug in ASHP + - fixed bug in write decimal string with mmgt enabled + - fixed bug in 0-length strings in multiply/divide + + pdp11_cpu.c: fixed order of operand fetching in XOR for SDSD models + + pdp11_cr.c: added CR11/CD11 support + + pdp11_tc.c: + - fixed READ to set extended data bits in TCST (found by Alan Frisbie) + + vax780_fload.c: added FLOAD command + + vax780_sbi.c: fixed writes to ACCS + + vax780_stddev.c: revised timer logic for EVKAE (reported by Tim Stark) + + vax_cis.c: (all reported by Tim Stark) + - fixed MOVTC, MOVTUC to preserve cc's through page faults + - fixed MOVTUC to stop on translated == escape + - fixed CVTPL to set registers before destination reg write + - fixed CVTPL to set correct cc bit on overflow + - fixed EDITPC to preserve cc's through page faults + - fixed EDITPC EO$BLANK_ZERO count, cc test + - fixed EDITPC EO$INSERT to insert fill instead of blank + - fixed EDITPC EO$LOAD_PLUS/MINUS to skip character + + vax_cpu.c: + - added KESU capability to virtual examine + - fixed bugs in virtual examine + - rewrote CPU history function for improved usability + (bugs below reported by Tim Stark) + - fixed fault cleanup to clear PSL + - fixed ADAWI r-mode to preserve dst<31:16> + - fixed ACBD/G to test correct operand + - fixed access checking on modify-class specifiers + - fixed branch address calculation in CPU history + - fixed bug in reported VA on faulting cross-page write + + vax_cpu1.c: (all reported by Tim Stark) + - added access check on system PTE for 11/780 + - added mbz check in LDPCTX for 11/780 + + vax_cmode.c: (all reported by Tim Stark) + - fixed omission of SXT + - fixed order of operand fetching in XOR + + vax_fpa.c: (all reported by Tim Stark) + - fixed POLYD, POLYG to clear R4, R5 + - fixed POLYD, POLYG to set R3 correctly + - fixed POLYD, POLYG to not exit prematurely if arg = 0 + - fixed POLYD, POLYG to do full 64b multiply + - fixed POLYF, POLYD, POLYG to remove truncation on add + - fixed POLYF, POLYD, POLYG to mask mul reslt to 31b/63b/63b + - fixed fp add routine to test for zero via fraction + to support "denormal" argument from POLYF, POLYD, POLYG + - fixed bug in 32b floating multiply routine + - fixed bug in 64b extended modulus routine + + vax_mmu.c: + - added access check on system PTE for 11/780 + + vax_octa.c: (all reported by Tim Stark) + - fixed MNEGH to test negated sign, clear C + - fixed carry propagation in qp_inc, qp_neg, qp_add + - fixed pack routines to test for zero via fraction + - fixed ACBH to set cc's on result + - fixed POLYH to set R3 correctly + - fixed POLYH to not exit prematurely if arg = 0 + - fixed POLYH to mask mul reslt to 127b + - fixed fp add routine to test for zero via fraction + to support "denormal" argument from POLYH + - fixed EMODH to concatenate 15b of 16b extension + - fixed bug in reported VA on faulting cross-page write + + + V3.5 revision history + +patch date module(s) and fix(es) + + 2 07-Jan-06 scp.c: + - added breakpoint spaces + - added REG_FIT support + + sim_console.c: added ASCII character processing routines + + sim_tape.c, sim_tape.h: + - added write support for P7B format + - fixed bug in write forward (found by 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, + pdp11_dz.c, pdp18b_stddev.c, pdp18b_tt1.c, vax_stddev, + gri_stddev.c: + - revised to support new character handling routines + + pdp10_rp.c: fixed DCLR not to clear disk address + + pdp11_hk.c: fixed overlapped seek interaction with NOP, etc + + pdp11_rh.c: added enable/disable routine + + pdp11_rq.c, pdp11_tm.c, pdp11_tq.c, pdp11_ts.c + - widened address display to 64b when USE_ADDR64 + + pdp11_rp.c: + - fixed DCLR not to clear disk address + - fixed device enable/disable logic to include Massbus adapter + - widened address display to 64b when USE_ADDR64 + + pdp11_tu.c: + - fixed device enable/disable logic to include Massbus adapter + - widened address display to 64b when USE_ADDR64 + - changed default adapter to TM03 (for VMS) + + pdp8_df.c, pdp8_dt.c, pdp8_rf.c: + - fixed unaligned access bug (found by Doug Carman) + + pdp8_rl.c: fixed IOT 61 decoding bug (found by 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) + + i1401_cpu.c: added control for old/new character encodings + + i1401_cd.c, i1401_lpt.c, i1401_tty.c: + - changed character encodings to be consistent with 7094 + - changed column binary format to be consistent with 7094 + - added choice of business or Fortran set for output encoding + + i1401_sys.c: changed WM character to ` under new encodings + + i1620_cd.c, i1620_lpt.c, i1620_tty.c: + - changed character encodings to be consistent with 7094 + + pdp10_cpu.c: changed MOVNI to eliminate gcc warning + + pdp11_io.c: fixed bug in autoconfiguration (missing XU) + + vax_io.c: fixed bug in autoconfiguration (missing XU) + + vax_fpa.c: fixed bug in 32b structure definitions (from 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++ + + all instruction histories: fixed reversed arguments to calloc + + scp.c: revised to trim trailing spaces on file inputs + + sim_sock.c: fixed SIGPIPE error on Unix + + sim_ether.c: added Windows user-defined adapter names (from Timothe Litt) + + sim_tape.c: fixed misallocation of TPC map array + + sim_tmxr.c: added support for SET DISCONNECT + + hp2100_mux.c: added SET MUXLn DISCONNECT + + i1401_cpu.c: + - fixed SSB-SSG clearing on RESET (reported by Ralph Reinke) + - removed error stops in MCE + + i1401_cd.c: fixed read, punch to ignore modifier on 1, 4 char inst + (reported by Van Snyder) + + id_pas.c: + - fixed bug in SHOW CONN/STATS + - added SET PASLn DISCONNECT + + pdp10_ksio.c: revised for new autoconfiguration interface + + pdp11_cpu.c: replaced WAIT clock queue check with API call + + pdp11_cpumod.c: added additional 11/60 registers + + pdp11_io.c: revised autoconfiguration algorithm and interface + + pdp11_dz.c: revised for new autoconfiguration interface + + pdp11_vh.c: + - revised for new autoconfiguration interface + - fixed bug in vector display routine + + pdp11_xu.c: fixed runt packet processing (found by Tim Chapman) + + pdp18b_cpu.c, pdp18b_sys.c: + - removed spurious AAS instruction + + pdp18b_tt1.c: + - fixed bug in SHOW CONN/STATS + - fixed bug in SET LOG/NOLOG + - added SET TTOXn DISCONNECT + + pdp8_ttx.c: + - fixed bug in SHOW CONN/STATS + - fixed bug in SET LOG/NOLOG + - added SET TTOXn DISCONNECT + + sds_mux.c: + - fixed bug in SHOW CONN/STATS + - added SET MUXLn DISCONNECT + + vaxmod_defs.h: added QDSS support + + vax_io.c: revised autoconfiguration algorithm and interface + + V3.4 revision history + + 0 01-May-04 scp.c: + - fixed ASSERT code + - revised syntax for SET DEBUG (from 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) + + many: revised detach routines to test for attached state + + hp2100_cpu.c: reorganized CPU options (from Dave Bryan) + + hp2100_cpu1.c: reorganized EIG routines (from Dave Bryan) + + hp2100_fp1.c: added FFP support (from Dave Bryan) + + id16_cpu.c: + - fixed bug in show history routine (from 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) + - revised examine/deposit to do words rather than bytes + + id16_sys.c, id32_sys: + - revised examine/deposit to do words rather than bytes + + pdp10_tu.c: + - fixed bug, ERASE and WREOF should not clear done (reported + by Rich Alderson) + - fixed error reporting + + pdp11_tu.c: fixed error reporting + + V3.3 revision history + + 2 08-Mar-05 scp.c: added ASSERT command (from Dave Bryan) + + h316_defs.h: fixed IORETURN macro + + h316_mt.c: fixed error reporting from OCP (found by Philipp Hachtmann) + + h316_stddev.c: fixed bug in OCP '0001 (found by Philipp Hachtmann) + + hp2100_cpu.c: split out EAU and MAC instructions + + hp2100_cpu1.c: (from Dave Bryan) + - fixed missing MPCK on JRS target + - removed EXECUTE instruction (is NOP in actual microcode) + + hp2100_fp: (from 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) + + 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) + + pdp11_rq: added RA60, RA71, RA81 disks + + pdp11_ry: fixed bug in boot code (reported by Graham Toal) + + vax_cpu.c: fixed initial state of cpu_extmem + + 1 05-Jan-05 h316_cpu.c: fixed bug in DIV + + h316_stddev.c: + - fixed bug in SKS '104 (reported by Philipp Hachtmann) + - fixed bug in SKS '504 + - adder reader/punch ASCII file support + - added Teletype reader/punch support + + h316_dp.c: fixed bug in skip on !seeking + + h316_mt.c: fixed bug in DMA/DMC support + + 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 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) + + hp2100_ds.c: first release + + hp2100_lps.c (all changes from Dave Bryan) + - added restart when set online, etc. + - fixed col count for non-printing chars + + hp2100_lpt.c (all changes from Dave Bryan) + - added restart when set online, etc. + + hp2100_sys.c (all changes from Dave Bryan): + - added STOP_OFFLINE, STOP_PWROFF messages + + i1401_sys.c: added address argument support (from 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_tm.c, pdp11_ts.c: added read-only file support + + sds_mt.c: added read-only file support + + 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) + - changed HELP to suppress duplicate command displays + + sim_console.c: + - moved SET/SHOW DEBUG under CONSOLE hierarchy + + hp2100_cpu.c: (all fixes by Dave Bryan) + - moved MP into its own device; added MP option jumpers + - modified DMA to allow disabling + - modified SET CPU 2100/2116 to truncate memory > 32K + - added -F switch to SET CPU to force memory truncation + - fixed S-register behavior on 2116 + - fixed LIx/MIx behavior for DMA on 2116 and 2100 + - fixed LIx/MIx behavior for empty I/O card slots + - modified WRU to be REG_HRO + - added BRK and DEL to save console settings + - fixed use of "unsigned int16" in cpu_reset + + hp2100_dp.c: (all fixes by Dave Bryan) + - fixed enable/disable from either device + - fixed ANY ERROR status for 12557A interface + - fixed unattached drive status for 12557A interface + - status cmd without prior STC DC now completes (12557A) + - OTA/OTB CC on 13210A interface also does CLC CC + - fixed RAR model + - fixed seek check on 13210 if sector out of range + + hp2100_dq.c: (all fixes by Dave Bryan) + - fixed enable/disable from either device + - shortened xtime from 5 to 3 (drive avg 156KW/second) + - fixed not ready/any error status + - fixed RAR model + + hp2100_dr.c: (all fixes by Dave Bryan) + - fixed enable/disable from either device + - fixed sector return in status word + - provided protected tracks and "Writing Enabled" status bit + - fixed DMA last word write, incomplete sector fill value + - added "parity error" status return on writes for 12606 + - added track origin test for 12606 + - added SCP test for 12606 + - fixed 12610 SFC operation + - added "Sector Flag" status bit + - added "Read Inhibit" status bit for 12606 + - fixed current-sector determination + - added TRACKPROT modifier + + hp2100_ipl.c, hp2100_ms.c: (all fixes by Dave Bryan) + - fixed enable/disable from either device + + hp2100_lps.c: (all fixes by Dave Bryan) + - added SET OFFLINE/ONLINE, POWEROFF/POWERON + - fixed status returns for error conditions + - fixed handling of non-printing characters + - fixed handling of characters after column 80 + - improved timing model accuracy for RTE + - added fast/realistic timing + - added debug printouts + + hp2100_lpt.c: (all fixes by Dave Bryan) + - added SET OFFLINE/ONLINE, POWEROFF/POWERON + - fixed status returns for error conditions + - fixed TOF handling so form remains on line 0 + + hp2100_stddev.c (all fixes by Dave Bryan) + - added paper tape loop mode, DIAG/READER modifiers to PTR + - added PV_LEFT to PTR TRLLIM register + - modified CLK to permit disable + + hp2100_sys.c: (all fixes by Dave Bryan) + - added memory protect device + - fixed display of CCA/CCB/CCE instructions + + i1401_cpu.c: added =n to SHOW HISTORY + + id16_cpu.c: added instruction history + + id32_cpu.c: added =n to SHOW HISTORY + + pdp10_defs.h: revised Unibus DMA API's + + pdp10_ksio.c: revised Unibus DMA API's + + pdp10_lp20.c: revised Unibus DMA API's + + pdp10_rp.c: replicated register state per drive + + pdp10_tu.c: + - fixed to set FCE on short record + - fixed to return bit<15> in drive type + - fixed format specification, 1:0 are don't cares + - implemented write check + - TMK is cleared by new motion command, not DCLR + - DONE is set on data transfers, ATA on non data transfers + + pdp11_defs.h: + - revised Unibus/Qbus DMA API's + - added CPU type and options flags + + pdp11_cpumod.h, pdp11_cpumod.c: + - new routines for setting CPU type and options + + pdp11_io.c: revised Unibus/Qbus DMA API's + + all PDP-11 DMA peripherals: + - revised Unibus/Qbus DMA API's + + pdp11_hk.c: CS2 OR must be zero for M+ + + pdp11_rh.c, pdp11_rp.c, pdp11_tu.c: + - split Massbus adapter from controllers + - replicated RP register state per drive + - added TM02/TM03 with TE16/TU45/TU77 drives + + pdp11_rq.c, pdp11_tq.c: + - provided different default timing for PDP-11, VAX + - revised to report CPU bus type in stage 1 + - revised to report controller type reflecting bus type + - added -L switch (LBNs) to RAUSER size specification + + pdp15_cpu.c: added =n to SHOW HISTORY + + pdp15_fpp.c: + - fixed URFST to mask low 9b of fraction + - fixed exception PC setting + + pdp8_cpu.c: added =n to SHOW HISTORY + + vax_defs.h: + - added octaword, compatibility mode support + + vax_moddefs.h: + - revised Unibus/Qbus DMA API's + + vax_cpu.c: + - moved processor-specific code to vax_sysdev.c + - added =n to SHOW HISTORY + + vax_cpu1.c: + - moved processor-specific IPR's to vax_sysdev.c + - moved emulation trap to vax_cis.c + - added support for compatibility mode + + vax_cis.c: new full VAX CIS instruction emulator + + vax_octa.c: new full VAX octaword and h_floating instruction emulator + + vax_cmode.c: new full VAX compatibility mode instruction emulator + + vax_io.c: + - revised Unibus/Qbus DMA API's + + vax_io.c, vax_stddev.c, vax_sysdev.c: + - integrated powerup into RESET (with -p) + + vax_sys.c: + - fixed bugs in parsing indirect displacement modes + - fixed bugs in displaying and parsing character data + + vax_syscm.c: added display and parse for compatibility mode + + vax_syslist.c: + - split from vax_sys.c + - removed PTR, PTP + + V3.2 revision history + + 3 03-Sep-04 scp.c: + - added ECHO command (from Dave Bryan) + - qualified RESTORE detach with SIM_SW_REST + + sim_console: added OS/2 EMX fixes (from Holger Veit) + + sim_sock.h: added missing definition for OS/2 (from Holger Veit) + + hp2100_cpu.c: changed error stops to report PC not PC + 1 + (from Dave Bryan) + + hp2100_dp.c: functional and timing fixes (from 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_ms.c: functional and timing fixes (from Dave Bryan) + - fixed erroneous execution of rejected command + - fixed erroneous execution of select-only command + - fixed erroneous execution of clear command + - fixed odd byte handling for read + - fixed spurious odd byte status on 13183A EOF + - modified handling of end of medium + - added detailed timing, with fast and realistic modes + - added reel sizes to simulate end of tape + - added debug printouts + + hp2100_mt.c: modified handling of end of medium (from Dave Bryan) + + hp2100_stddev.c: added tab to control char set (from Dave Bryan) + + pdp11_rq.c: VAX controllers luns start at 0 (from 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) + + sim_console.c: revised Windows console code (from Dave Bryan) + + sim_fio.c: fixed problem in big-endian read + (reported by Scott Bailey) + + gri_cpu.c: updated MSR, EAO functions + + hp_stddev.c: generalized handling of control char echoing + (from 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) + - SBT increments B after store + - DMS console map must check dms_enb + - SFS x,C and SFC x,C work + - MP violation clears automatically on interrupt + - SFS/SFC 5 is not gated by protection enabled + - DMS enable does not disable mem prot checks + - DMS status inconsistent at simulator halt + - Examine/deposit are checking wrong addresses + - Physical addresses are 20b not 15b + - Revised DMS to use memory rather than internal format + - Added instruction printout to HALT message + - Added M and T internal registers + - Added N, S, and U breakpoints + Revised IBL facility to conform to microcode + Added DMA EDT I/O pseudo-opcode + Separated DMA SRQ (service request) from FLG + + all HP2100 peripherals: + - revised to make SFS x,C and SFC x,C work + - revised to separate SRQ from FLG + + all HP2100 IBL bootable peripherals: + - revised boot ROMs to use IBL facility + - revised SR values to preserve SR<5:3> + + hp2100_lps.c, hp2100_lpt.c: fixed timing + + hp2100_dp.c: fixed interpretation of SR<0> + + 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) + + hp2100_stddev.c: + - fixed input behavior during typeout for RTE-IV + - suppressed nulls on TTY output for RTE-IV + + hp2100_sys.c: added SFS x,C and SFC x,C to print/parse routines + + pdp10_fe.c, pdp11_stddev.c, pdp18b_stddev.c, pdp8_tt.c, vax_stddev.c: + - removed SET TTI CTRL-C option + + pdp11_tq.c: + - fixed bug in reporting write protect (reported by Lyle Bickley) + - fixed TK70 model number and media ID (found by Robert Schaffrath) + + pdp11_vh.c: added DHQ11 support (from John Dundas) + + pdp11_io.c, vax_io.c: fixed DHQ11 autoconfigure (from John Dundas) + + pdp11_sys.c, vax_sys.c: added DHQ11 support (from John Dundas) + + vax_cpu.c: fixed bug in DIVBx, DIVWx (reported by Peter Trimmel) + + 0 04-Apr-04 scp.c: + - added sim_vm_parse_addr and sim_vm_fprint_addr + - added REG_VMAD + - moved console logging to SCP + - changed sim_fsize to use descriptor rather than name + - added global device/unit show modifiers + - added device debug support (Dave Hittner) + - moved device and unit flags, updated save format + + sim_ether.c: + - further generalizations (Dave Hittner, Mark Pizzolato) + + sim_tmxr.h, sim_tmxr.c: + - added tmxr_linemsg + - changed TMXR definition to support variable number of lines + + sim_libraries: + - new console library (sim_console.h, sim_console.c) + - new file I/O library (sim_fio.h, sim_fio.c) + - new timer library (sim_timer.h, sim_timer.c) + + all terminal multiplexors: revised for tmxr library changes + + all DECtapes: + - added STOP_EOR to enable end-of-reel stop + - revised for device debug support + + all variable-sized devices: revised for sim_fsize change + + eclipse_cpu.c, nova_cpu.c: fixed device enable/disable support + (found by Bruce Ray) + + nova_defs.h, nova_sys.c, nova_qty.c: + - added QTY and ALM support (Bruce Ray) + + id32_cpu.c, id_dp.c: revised for device debug support + + lgp: added LGP-30 [LGP-21] simulator + + pdp1_sys.c: fixed bug in LOAD (found by Mark Crispin) + + pdp10_mdfp.c: + - fixed bug in floating unpack + - fixed bug in FIXR (found by Philip Stone, fixed by Chris Smith) + + pdp11_dz.c: added per-line logging + + pdp11_rk.c: + - added formatting support + - added address increment inhibit support + - added transfer overrun detection + + pdp11_hk.c, pdp11_rp.c: revised for device debug support + + pdp11_rq.c: fixed bug in interrupt control (found by Tom Evans) + + pdp11_ry.c: added VAX support + + pdp11_tm.c, pdp11_tq.c, pdp11_ts.c: revised for device debug support + + pdp11_xu.c: replaced stub with real implementation (Dave Hittner) + + pdp18b_cpu.c: + - fixed bug in XVM g_mode implementation + - fixed bug in PDP-15 indexed address calculation + - fixed bug in PDP-15 autoindexed address calculation + + pdp18b_fpp.c: fixed bugs in instruction decode + + pdp18b_stddev.c: + - fixed clock response to CAF + - fixed bug in hardware read-in mode bootstrap + + pdp18b_sys.c: fixed XVM instruction decoding errors + + pdp18b_tt1.c: added support for 1-16 additional terminals + + vax_moddef.h, vax_cpu.c, vax_sysdev.c: + - added extended physical memory support (Mark Pizzolato) + - added RXV21 support + + vax_cpu1.c: + - added PC read fault in EXTxV + - fixed PC write fault in INSV + + V3.1 revision history + + 0 29-Dec-03 sim_defs.h, scp.c: added output stall status + + all console emulators: added output stall support + + sim_ether.c (Dave Hittner, Mark Pizzolato, Anders Ahgren): + - added Alpha/VMS support + - added FreeBSD, Mac OS/X support + - added TUN/TAP support + - added DECnet duplicate address detection + + all memory buffered devices (fixed head disks, floppy disks): + - cleaned up buffer copy code + + all DECtapes: + - fixed reverse checksum in read all + - added DECtape off reel message + - simplified timing + + eclipse_cpu.c (Charles Owen): + - added floating point support + - added programmable interval timer support + - bug fixes + + h316_cpu.c: + - added instruction history + - added DMA/DMC support + - added device ENABLE/DISABLE support + - change default to HSA option included + + h316_dp.c: added moving head disk support + + h316_fhd.c: added fixed head disk support + + h316_mt.c: added magtape support + + h316_sys.c: added new device support + + nova_dkp.c (Charles Owen): + - fixed bug in flag clear sequence + - added diagnostic mode support for disk sizing + +` nova_mt.c (Charles Owen): + - 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) + + i1620_cpu.c: fixed bug in branch digit (found by Dave Babcock) + + pdp1_drm.c: + - added parallel drum support + - fixed bug in serial drum instructin decoding + + pdp1_sys.c: added parallel drum support, mnemonics + + pdp11_cpu.c: + - added autoconfiguration controls + - added support for 18b-only Qbus devices + - cleaned up addressing/bus definitions + + pdp11_rk.c, pdp11_ry.c, pdp11_tm.c, pdp11_hk.c: + - added Q18 attribute + + pdp11_io.c: + - added autoconfiguration controls + - fixed bug in I/O configuration (found by Dave Hittner) + + pdp11_rq.c: + - revised MB->LBN conversion for greater accuracy + - fixed bug with multiple RAUSER drives + + pdp11_tc.c: changed to be off by default (base config is Qbus) + + pdp11_xq.c (Dave Hittner, Mark Pizzolato): + - fixed second controller interrupts + - fixed bugs in multicast and promiscuous setup + + pdp18b_cpu.c: + - added instruction history + - fixed PDP-4,-7,-9 autoincrement bug + - change PDP-7,-9 default to API option included + + pdp8_defs.h, pdp8_sys.c: + - added DECtape off reel message + - added support for TSC8-75 (ETOS) option + - added support for TD8E controller + + pdp8_cpu.c: added instruction history + + pdp8_rx.c: + - fixed bug in RX28 read status (found by Charles Dickman) + - fixed double density write + + pdp8_td.c: added TD8E controller + + pdp8_tsc.c: added TSC8-75 option + + vax_cpu.c: + - revised instruction history for dynamic sizing + - added autoconfiguration controls + + vax_io.c: + - added autoconfiguration controls + - fixed bug in I/O configuration (found by Dave Hittner) + + id16_cpu.c: revised instruction decoding + + id32_cpu.c: + - revised instruction decoding + - added instruction history + + V3.0 revision history + + 2 15-Sep-03 scp.c: + - fixed end-of-file problem in dep, idep + - fixed error on trailing spaces in dep, idep + + pdp1_stddev.c + - fixed system hang if continue after PTR error + - added PTR start/stop functionality + - added address switch functionality to PTR BOOT + + pdp1_sys.c: added multibank capability to LOAD + + pdp18b_cpu.c: + - fixed priorities in PDP-15 API (PI between 3 and 4) + - fixed sign handling in PDP-15 unsigned mul/div + - fixed bug in CAF, must clear API subsystem + + i1401_mt.c: + - fixed tape read end-of-record handling based on real 1401 + - added diagnostic read (space forward) + + i1620_cpu.c + - fixed bug in immediate index add (found by Michael Short) + + 1 27-Jul-03 pdp1_cpu.c: updated to detect indefinite I/O wait + + pdp1_drm.c: fixed incorrect logical, missing activate, break + + pdp1_lp.c: + - fixed bugs in instruction decoding, overprinting + - updated to detect indefinite I/O wait + + pdp1_stddev.c: + - changed RIM loader to be "hardware" + - updated to detect indefinite I/O wait + + pdp1_sys.c: added block loader format support to LOAD + + pdp10_rp.c: fixed bug in read header + + pdp11_rq: fixed bug in user disk size (found by Chaskiel M Grundman) + + pdp18b_cpu.c: + - added FP15 support + - added XVM support + - added EAE support to the PDP-4 + - added PDP-15 "re-entrancy ECO" + - fixed memory protect/skip interaction + - fixed CAF to only reset peripherals + + pdp18b_fpp.c: added FP15 + + pdp18b_lp.c: fixed bug in Type 62 overprinting + + pdp18b_rf.c: fixed bug in set size routine + + pdp18b_stddev.c: + - increased PTP TIME for PDP-15 operating systems + - added hardware RIM loader for PDP-7, PDP-9, PDP-15 + + pdp18b_sys.c: added FP15, KT15, XVM instructions + + pdp8b_df.c, pdp8_rf.c: fixed bug in set size routine + + hp2100_dr.c: + - fixed drum sizes + - fixed variable capacity interaction with SAVE/RESTORE + + i1401_cpu.c: revised fetch to model hardware more closely + + ibm1130: fixed bugs found by APL 1130 + + nova_dsk.c: fixed bug in set size routine + + altairz80: fixed bug in real-time clock on Windows host + + 0 15-Jun-03 scp.c: + - added ASSIGN/DEASSIGN + - changed RESTORE to detach files + - added u5, u6 unit fields + - added USE_ADDR64 support + - changed some structure fields to unsigned + + scp_tty.c: added extended file seek + + sim_sock.c: fixed calling sequence in stubs + + sim_tape.c: + - added E11 and TPC format support + - added extended file support + + sim_tmxr.c: fixed bug in SHOW CONNECTIONS + + all magtapes: + - added multiformat support + - added extended file support + + i1401_cpu.c: + - fixed mnemonic, instruction lengths, and reverse + scan length check bug for MCS + - fixed MCE bug, BS off by 1 if zero suppress + - fixed chaining bug, D lost if return to SCP + - fixed H branch, branch occurs after continue + - added check for invalid 8 character MCW, LCA + + i1401_mt.c: fixed load-mode end of record response + + nova_dsk.c: fixed variable size interaction with restore + + pdp1_dt.c: fixed variable size interaction with restore + + pdp10_rp.c: fixed ordering bug in attach + + pdp11_cpu.c: + - fixed bug in MMR1 update (found by Tim Stark) + - fixed bug in memory size table + + pdp11_lp.c, pdp11_rq.c: added extended file support + + pdp11_rl.c, pdp11_rp.c, pdp11_ry.c: fixed ordering bug in attach + + pdp11_tc.c: fixed variable size interaction with restore + + pdp11_xq.c: + - corrected interrupts on IE state transition (code by Tom Evans) + - added interrupt clear on soft reset (first noted by Bob Supnik) + - removed interrupt when setting XL or RL (multiple people) + - added SET/SHOW XQ STATS + - added SHOW XQ FILTERS + - added ability to split received packet into multiple buffers + - added explicit runt & giant packet processing + + vax_fpa.c: + - fixed integer overflow bug in CVTfi + - fixed multiple bugs in EMODf + + 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) + + pdp1_dt.c: fixed variable size interaction with restore + + pdp18b_dt.c: fixed variable size interaction with restore + + pdp18b_mt.c: fixed bug in MTTR + + pdp18b_rf.c: fixed variable size interaction with restore + + pdp8_df.c, pdp8_rf.c: fixed variable size interaction + with restore + + pdp8_dt.c: fixed variable size interaction with restore + + pdp8_mt.c: fixed bug in SKTR + + hp2100_dp.c,hp2100_dq.c: + - fixed bug in read status (13210A controller) + - fixed bug in seek completion + + id_pt.c: fixed type declaration (found by Mark Pizzolato) + + gri_cpu.c: fixed bug in SC queue pointer management + + V2.10 revision history + + 4 03-Mar-03 scp.c + - added .ini startup file capability + - added multiple breakpoint actions + - added multiple switch evaluation points + - fixed bug in multiword deposits to file + + sim_tape.c: magtape simulation library + + h316_stddev.c: added set line frequency command + + hp2100_mt.c, hp2100_ms.c: revised to use magtape library + + i1401_mt.c: revised to use magtape library + + id_dp.c, id_idc.c: fixed cylinder overflow on writes + + id_mt.c: + - fixed error handling to stop selector channel + - revised to use magtape library + + id16_sys.c, id32_sys.c: added relative addressing support + + id_uvc.c: + - added set frequency command to line frequency clock + - improved calibration algorithm for precision clock + + nova_clk.c: added set line frequency command + + nova_dsk.c: fixed autosizing algorithm + + nova_mt.c: revised to use magtape library + + pdp10_tu.c: revised to use magtape library + + pdp11_cpu.c: fixed bug in MMR1 update (found by Tim Stark) + + pdp11_stddev.c + - added set line frequency command + - added set ctrl-c command + + pdp11_rq.c: + - fixed ordering problem in queue process + - fixed bug in vector calculation for VAXen + - added user defined drive support + + pdp11_ry.c: fixed autosizing algorithm + + pdp11_tm.c, pdp11_ts.c: revised to use magtape library + + pdp11_tq.c: + - fixed ordering problem in queue process + - fixed overly restrictive test for bad modifiers + - fixed bug in vector calculation for VAXen + - added variable controller, user defined drive support + - revised to use magtape library + + pdp18b_cpu.c: fixed three EAE bugs (found by Hans Pufal) + + pdp18b_mt.c: + - fixed bugs in BOT error handling, interrupt handling + - revised to use magtape library + + pdp18b_rf.c: + - removed 22nd bit from disk address + - fixed autosizing algorithm + + pdp18b_stddev.c: + - added set line frequency command + - added set ctrl-c command + + pdp18b_sys.c: fixed FMTASC printouts (found by Hans Pufal) + + pdp8_clk.c: added set line frequency command + + pdp8_df.c, pdp8_rf.c, pdp8_rx.c: fixed autosizing algorithm + + pdp8_mt.c: + - fixed bug in BOT error handling + - revised to use magtape library + + pdp8_tt.c: added set ctrl-c command + + sds_cpu.c: added set line frequency command + + sds_mt.c: revised to use magtape library + + vax_stddev.c: added set ctrl-c command + + 3 06-Feb-03 scp.c: + - added dynamic extension of the breakpoint table + - added breakpoint actions + + hp2100_cpu.c: fixed last cycle bug in DMA output (found by + Mike Gemeny) + + hp2100_ipl.c: individual links are full duplex (found by + Mike Gemeny) + + pdp11_cpu.c: changed R, SP to track PSW respectively + + pdp18b_defs.h, pdp18b_sys.c: added RB09 fixed head disk, + LP09 printer + + pdp18b_rf.c: + - fixed IOT decoding (found by Hans Pufal) + - fixed address overrun logic + - added variable number of platters and autosizing + + pdp18b_rf.c: + - fixed IOT decoding + - fixed bug in command initiation + + pdp18b_rb.c: new RB09 fixed head disk + + pdp18b_lp.c: new LP09 line printer + + pdp8_df.c: added variable number of platters and autosizing + + pdp8_rf.c: added variable number of platters and autosizing + + nova_dsk.c: added variable number of platters and autosizing + + id16_cpu.c: fixed bug in SETM, SETMR (found by 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) + + sim_tmxr.c: + - fixed bugs in IAC+IAC handling (from Mark Pizzolato) + - added IAC+BRK handling (from Mark Pizzolato) + + sim_sock.c: + - added use count for Windows start/stop + - added sim_connect_sock + + pdp1_defs.h, pdp1_cpu.c, pdp1_sys.c, pdp1_drm.c: + added Type 24 serial drum + + pdp18_defs.h: added PDP-4 drum support + + hp2100_cpu.c: added 21MX IOP support + + hp2100_ipl.c: added HP interprocessor link support + + pdp11_tq.c: fixed bug in transfer end packet length + + pdp11_xq.c: + - added VMScluster support (thanks to Mark Pizzolato) + - added major performance enhancements (thanks to Mark Pizzolato) + - added local packet processing + - added system id broadcast + + 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) + + all terminals and multiplexors: added BREAK support + + 1 21-Nov-02 pdp1_stddev.c: changed typewriter to half duplex + (found by Derek Peschel) + + pdp10_tu.c: + - fixed bug in bootstrap (reported by Michael Thompson) + - fixed bug in read (reported by Harris Newman) + + 0 15-Nov-02 SCP and libraries + scp.c: + - added Telnet console support + - 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) + - added quiet mode, DO with parameters, GUI interface, + extensible commands (from Brian Knittel) + - added DEVICE context and flags + - added central device enable/disable support + - modified SAVE/GET to save and restore flags + - modified boot routine calling sequence + scp_tty.c: + - removed VT emulation support + - added sim_os_sleep, renamed sim_poll_kbd, sim_putchar + sim_tmxr.c: + - 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) + + all magtapes: + - added support for end of medium + - cleaned up BOT handling + + all DECtapes: added support for RT11 image file format + + most terminals and multiplexors: + - added support for 7b vs 8b character processing + + PDP-1 + pdp1_cpu.c, pdp1_sys.c, pdp1_dt.c: added PDP-1 DECtape support + + PDP-8 + pdp8_cpu.c, all peripherals: + - added variable device number support + - added new device enabled/disable support + pdp8_rx.c: added RX28/RX02 support + + PDP-11 + pdp11_defs.h, pdp11_io.c, pdp11_sys.c, all peripherals: + - added variable vector support + - added new device enable/disable support + - added autoconfiguration support + all bootstraps: modified to support variable addresses + dec_mscp.h, pdp11_tq.c: added TK50 support + pdp11_rq.c: + - added multicontroller support + - fixed bug in HBE error log packet + - fixed bug in ATP processing + 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_pclk.c: added KW11P support + pdp11_ts.c: + - fixed bug in CTL decoding + - fixed bug in extended status XS0_MOT + pdp11_stddev.c: removed paper tape to its own module + + PDP-18b + pdp18b_cpu.c, all peripherals: + - added variable device number support + - added new device enabled/disabled support + + VAX + dec_dz.h: fixed bug in number of boards calculation + vax_moddefs.h, vax_io.c, vax_sys.c, all peripherals: + - added variable vector support + - added new device enable/disable support + - added autoconfiguration support + vax_sys.c: + - generalized examine/deposit + - added TMSCP, multiple RQDX3, DEQNA/DELQA support + 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) + + PDP-10 + pdp10_defs.h, pdp10_ksio.c, all peripherals: + - added variable vector support + - added new device enable/disable support + pdp10_defs.h, pdp10_ksio.c: added support for standard PDP-11 + peripherals, added RX211 support + pdp10_pt.c: rewritten to reference common implementation + + Nova, Eclipse: + nova_cpu.c, eclipse_cpu.c, all peripherals: + - added new device enable/disable support + + HP2100 + hp2100_cpu: + - fixed bugs in the EAU, 21MX, DMS, and IOP instructions + - fixed bugs in the memory protect and DMS functions + - created new options to enable/disable EAU, MPR, DMS + - added new device enable/disable support + hp2100_fp.c: + - recoded to conform to 21MX microcode algorithms + hp2100_stddev.c: + - fixed bugs in TTY reset, OTA, time base generator + - revised BOOT support to conform to RBL loader + - added clock calibration + hp2100_dp.c: + - changed default to 13210A + - added BOOT support + hp2100_dq.c: + - finished incomplete functions, fixed head switching + - added BOOT support + hp2100_ms.c: + - fixed bugs found by diagnostics + - added 13183 support + - added BOOT support + hp2100_mt.c: + - fixed bugs found by diagnostics + - disabled by default + hp2100_lpt.c: implemented 12845A controller + hp2100_lps.c: + - renamed 12653A controller + - added diagnostic mode for MPR, DCPC diagnostics + - disabled by default + + IBM 1620: first release + + 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) + + i1401_dp.c: reworked address generation and checking + + vax_cpu.c: added infinite loop detection and halt to + boot ROM option (from Mark Pizzolato) + + vax_fpa.c: changed function names to prevent conflict + with C math library + + pdp11_cpu.c: fixed bug in MMR0 update logic (from + John Dundas) + + pdp18b_stddev.c: added "ASCII mode" for reader and + punch (from Hans Pufal) + + gri_*.c: added GRI-909 simulator + + scp.c: added DO echo, DO exit (from 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) + + scp_tty.c, sim_vt.h, sim_vt.c: added VTxxx emulation + support for Windows (from Fischer Franz) + + sim_sock.c: added OS/2 support (from Holger Veit) + + pdp11_cpu.c: fixed bugs (from 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) + o 5 and 7 character H, 7 character doesn't branch + o 8 character NOP + o 1401-like memory dump + + i1401_dp.c: added 1311 disk + + 9 04-May-02 pdp11_rq: fixed bug in polling routine + + 8 03-May-02 scp.c: + - changed LOG/NOLOG to SET LOG/NOLOG + - added SHOW LOG + - added SET VT/NOVT and SHOW VT for VT emulation + + sim_sock.h: changed VMS stropt.h include to ioctl.h + + vax_cpu.c + - added TODR powerup routine to set date, time on boot + - fixed exception flows to clear trap request + - fixed register logging in autoincrement indexed + + vax_stddev.c: added TODR powerup routine + + 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) + + pdp11_cpu.c: fixed bugs, added features (from John Dundas + and Wolfgang Helbig) + - added HTRAP and BPOK to maintenance register + - added trap on kernel HALT if MAINT set + - fixed red zone trap, clear odd address and nxm traps + - fixed RTS SP, don't increment restored SP + - fixed TSTSET, write dst | 1 rather than prev R0 | 1 + - fixed DIV, set N=0,Z=1 on div by zero (J11, 11/70) + - fixed DIV, set set N=Z=0 on overfow (J11, 11/70) + - fixed ASH, ASHC, count = -32 used implementation- + dependent 32 bit right shift + - fixed illegal instruction test to detect 000010 + - fixed write-only page test + + pdp11_rp.c: fixed SHOW ADDRESS command + + vaxmod_defs.h: fixed DZ vector base and number of lines + + dec_dz.h: + - fixed interrupt acknowledge routines + - fixed SHOW ADDRESS command + + all magtape routines: added test for badly formed + record length (suggested by Jonathan Engdahl) + + 6 18-Apr-02 vax_cpu.c: fixed CASEL condition codes + + vax_cpu1.c: fixed vfield pos > 31 test to be unsigned + + 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) + + 4 1-Apr-02 pdp11_rq.c: fixed bug, reset cleared write protect status + + pdp11_ts.c: fixed bug in residual frame count after space + + 3 15-Mar-02 pdp11_defs.h: changed default model to KDJ11A (11/73) + + pdp11_rq.c: adjusted delays for M+ timing bugs + + hp2100_cpu.c, pdp10_cpu.c, pdp11_cpu.c: tweaked abort + code for ANSI setjmp/longjmp compliance + + hp2100_cpu.c, hp2100_fp.c, hp2100_stddev.c, hp2100_sys.c: + revised to allocate memory dynamically + + 2 01-Mar-02 pdp11_cpu.c: + - fixed bugs in CPU registers + - fixed double operand evaluation order for M+ + + pdp11_rq.c: added delays to initialization for + RSX11M+ prior to V4.5 + + 1 20-Feb-02 scp.c: fixed bug in clock calibration when (real) + time runs backwards + + pdp11_rq.c: fixed bug in host timeout logic + + pdp11_ts.c: fixed bug in message header logic + + pdp18b_defs.h, pdp18b_dt.c, pdp18b_sys.c: added + PDP-7 DECtape support + + hp2100_cpu.c: + - added floating point and DMS + - fixed bugs in DIV, ASL, ASR, LBT, SBT, CBT, CMW + + hp2100_sys.c: added floating point, DMS + + hp2100_fp.c: added floating point + + ibm1130: added Brian Knittel's IBM 1130 simulator + + 0 30-Jan-02 scp.c: + - generalized timer package for multiple timers + - added circular register arrays + - fixed bugs, line spacing in modifier display + - added -e switch to attach + - moved device enable/disable to simulators + + scp_tty.c: VAX specific fix (from Robert Alan Byer) + + sim_tmxr.c, sim_tmxr.h: + - added tmxr_fstats, tmxr_dscln + - renamed tmxr_fstatus to tmxr_fconns + + sim_sock.c, sim_sock.h: added VMS support (from + Robert Alan Byer) + + pdp_dz.h, pdp18b_tt1.c, nova_tt1.c: + - added SET DISCONNECT + - added SHOW STATISTICS + + pdp8_defs.h: fixed bug in interrupt enable initialization + + pdp8_ttx.c: rewrote as unified multiplexor + + pdp11_cpu.c: fixed calc_MMR1 macro (found by Robert Alan Byer) + + pdp11_stddev.c: fixed bugs in KW11L (found by 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) + + 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) + + hp2100_mt.c: fixed bug on write of last character + + hp2100_dq,dr,ms,mux.c: added new disk, magtape, and terminal + multiplexor controllers + + i1401_cd.c, i1401_mt.c: new zero footprint bootstraps + (from Van Snyder) + + i1401_sys.c: fixed symbolic display of H, NOP with no trailing + word mark (found by Van Snyder) + + most CPUs: + - replaced OLDPC with PC queue + - implemented device enable/disable locally + + V2.8 revision history + +5 25-Dec-01 scp.c: fixed bug in DO command (found by John Dundas) + + pdp10_cpu.c: + - moved trap-in-progress to separate variable + - cleaned up declarations + - cleaned up volatile state for GNU C longjmp + + pdp11_cpu.c: cleaned up declarations + + pdp11_rq.c: added RA-class disks + +4 17-Dec-01 pdp11_rq.c: added delayed processing of packets + +3 16-Dec-01 pdp8_cpu.c: + - mode A EAE instructions didn't clear GTF + - ASR shift count > 24 mis-set GTF + - effective shift count == 32 didn't work + +2 07-Dec-01 scp.c: added breakpoint package + + all CPU's: revised to use new breakpoint package + +1 05-Dec-01 scp.c: fixed bug in universal register name logic + +0 30-Nov-01 Reorganized simh source and documentation tree + + scp: Added DO command, universal registers, extended + SET/SHOW logic + + pdp11: overhauled PDP-11 for DMA map support, shared + sources with VAX, dynamic buffer allocation + + 18b pdp: overhauled interrupt structure + + pdp8: added RL8A + + pdp10: fixed two ITS-related bugs (found by Dave Conroy) + + V2.7 revision history + +patch date module(s) and fix(es) + +15 23-Oct-01 pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: fixed bugs + error interrupt handling + + pdp10_defs.h, pdp10_ksio.c, pdp10_fe.c, pdp10_fe.c, + pdp10_rp.c, pdp10_tu.c: reworked I/O page interface + 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 + 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) + +12 15-Oct-01 pdp11_defs.h, pdp11_cpu.c, pdp11_tc.c, pdp11_ts.c, + pdp11_rp.c: added operations logging + +11 8-Oct-01 scp.c: added sim_rev.h include and version print + + pdp11_cpu.c: fixed bug in interrupt acknowledge, + multiple outstanding interrupts caused the lowest + rather than the highest to be acknowledged + +10 7-Oct-01 pdp11_stddev.c: added monitor bits (CSR<7>) for full + KW11L compatibility, needed for RSTS/E autoconfiguration + +9 6-Oct-01 pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: rewrote interrupt + logic from RH11/RH70 schematics, to mimic hardware quirks + + dec_dz.c: fixed bug in carrier detect logic, carrier + detect was being cleared on next modem poll + +8 4-Oct-01 pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: undid edit of + 28-Sep-01; real problem was level-sensitive nature of + CS1_SC, but CS1_SC can only trigger an interrupt if + DONE is set + +7 2-Oct-01 pdp11_rp.c, pdp10_rp.c: CS1_SC is evaluated as a level- + sensitive, rather than an edge-sensitive, input to + interrupt request + +6 30-Sep-01 pdp11_rp.c, pdp10_rp.c: separated out CS1<5:0> to per- + drive registers + + pdp10_tu.c: based on above, cleaned up handling of + non-existent formatters, fixed non-data transfer + commands clearing DONE + +5 28-Sep-01 pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: controller should + interrupt if ATA or SC sets when IE is set, was + interrupting only if DON = 1 as well + +4 27-Sep-01 pdp11_ts.c: + - NXM errors should return TC4 or TC5; were returning TC3 + - extended features is part of XS2; was returned in XS3 + - extended characteristics (fifth) word needed for RSTS/E + + pdp11_tc.c: stop, stop all do cause an interrupt + + dec_dz.h: scanner should find a ready output line, even + if there are no connections; needed for RSTS/E autoconfigure + + scp.c: + - added routine sim_qcount for 1130 + - added "simulator exit" detach routine for 1130 + + sim_defs.h: added header for sim_qcount + +3 20-Sep-01 pdp11_ts.c: boot code binary was incorrect + +2 19-Sep-01 pdp18b_cpu.c: EAE should interpret initial count of 00 + as 100 + + scp.c: modified Macintosh support + +1 17-Sep-01 pdp8_ttx.c: new module for PDP-8 multi-terminal support + + pdp18b_tt1.c: modified to use sim_tmxr library + + nova_tt1.c: modified to use sim_tmxr library + + dec_dz.h: added autodisconnect support + + scp.c: removed old multiconsole support + + sim_tmxr.c: modified calling sequence for sim_putchar_ln + + sim_sock.c: added Macintosh sockets support +*/ + +#endif diff --git a/src/sim_smp_file.cpp b/src/sim_smp_file.cpp new file mode 100644 index 0000000..2edb03c --- /dev/null +++ b/src/sim_smp_file.cpp @@ -0,0 +1,293 @@ +/* + * Thread-safe file I/O + */ + +#define SIM_THREADS_H_FULL_INCLUDE +#include "sim_defs.h" + +SMP_FILE* smp_stdin = NULL; +SMP_FILE* smp_stdout = NULL; +SMP_FILE* smp_stderr = NULL; + +class smp_file_critical_section : public smp_lock_impl +{ +public: + // If there is an access to file from concurrent threads, + // give it just enough spin-wait time to format the string and enter characters in FILE memory buffer (e.g. when writing log files), + // but hibernate at OS level for actual physical file IO. + // This produces an estimate of spin-wait cycles used below. + smp_file_critical_section() + { + init(5000); + } +}; + +// class SMP_FILE +// { +// public: +// SMP_FILE* stream; +// smp_file_critical_section* lock_cs; +// }; + +#define DynLock(sfd) \ + sfd->lock_cs->lock(); \ + DynUnlocker _dyn_unlocker(sfd->lock_cs); + +class DynUnlocker +{ +private: + smp_lock* lock; +public: + DynUnlocker(smp_lock* lock) + { + this->lock = lock; + } + ~DynUnlocker() + { + lock->unlock(); + } +}; + +SMP_FILE* smp_file_wrap(FILE* fp) +{ + SMP_FILE* sfd = new SMP_FILE(); + sfd->stream = fp; + sfd->lock_cs = new smp_file_critical_section(); + return sfd; +} + +SMP_FILE* smp_fopen(const char* filename, const char* mode) +{ + FILE* fd = fopen(filename, mode); + return (fd == NULL) ? NULL : smp_file_wrap(fd); +} + +#if defined (USE_INT64) && defined (USE_ADDR64) && defined (__linux) +SMP_FILE* smp_fopen64(const char* filename, const char* mode) +{ + FILE* fd = fopen64(filename, mode); + return (fd == NULL) ? NULL : smp_file_wrap(fd); +} +#endif + +int fclose(SMP_FILE* sfd) +{ + sfd->lock_cs->lock(); + int res = fclose(sfd->stream); + sfd->lock_cs->unlock(); + delete sfd->lock_cs; + delete sfd; + return res; +} + +void smp_perror(const char* string) +{ + if (smp_stderr) smp_stderr->lock_cs->lock(); + perror(string); + if (smp_stderr) smp_stderr->lock_cs->unlock(); +} + +int smp_putchar(int c) +{ + smp_stdout->lock_cs->lock(); + int res = putchar(c); + smp_stdout->lock_cs->unlock(); + return res; +} + +int smp_printf(const char* fmt, ...) +{ + size_t len; + va_list va; + va_start(va, fmt); + if (smp_stdout) smp_stdout->lock_cs->lock(); + if (sim_ttrun_mode && fmt[0] == '\n') + printf("\r"); + int res = vfprintf(stdout, fmt, va); + if (sim_ttrun_mode && (len = strlen(fmt)) && fmt[len - 1] == '\n') + printf("\r"); + if (smp_stdout) smp_stdout->lock_cs->unlock(); + va_end(va); + return res; +} + +int fflush(SMP_FILE* sfd) +{ + DynLock(sfd); + return fflush(sfd->stream); +} + +int fgetc(SMP_FILE* sfd) +{ + DynLock(sfd); + return fgetc(sfd->stream); +} + +char* fgets(char* string, int n, SMP_FILE* sfd) +{ + DynLock(sfd); + return fgets(string, n, sfd->stream); +} + +size_t fread(void* buffer, size_t size, size_t count, SMP_FILE* sfd) +{ + DynLock(sfd); + return fread(buffer, size, count, sfd->stream); +} + +int fputc(int c, SMP_FILE* sfd) +{ + DynLock(sfd); + return fputc(c, sfd->stream); +} + +int fputs(const char* string, SMP_FILE* sfd) +{ + DynLock(sfd); + return fputs(string, sfd->stream); +} + +size_t fwrite(const void* buffer, size_t size, size_t count, SMP_FILE* sfd) +{ + DynLock(sfd); + return fwrite(buffer, size, count, sfd->stream); +} + +int fseek(SMP_FILE* sfd, long offset, int origin) +{ + DynLock(sfd); + return fseek(sfd->stream, offset, origin); +} + +int fsetpos(SMP_FILE* sfd, const fpos_t* pos) +{ + DynLock(sfd); + return fsetpos(sfd->stream, pos); +} + +void rewind(SMP_FILE* sfd) +{ + DynLock(sfd); + return rewind(sfd->stream); +} + +int vfprintf(SMP_FILE* sfd, const char* format, va_list argptr) +{ + DynLock(sfd); + return vfprintf(sfd->stream, format, argptr); +} + +long ftell(SMP_FILE* sfd) +{ + DynLock(sfd); + return ftell(sfd->stream); +} + +int ferror(SMP_FILE* sfd) +{ + DynLock(sfd); + return ferror(sfd->stream); +} + +int feof(SMP_FILE* sfd) +{ + DynLock(sfd); + return feof(sfd->stream); +} + +int getc(SMP_FILE* sfd) +{ + DynLock(sfd); + return getc(sfd->stream); +} + +int putc(int c, SMP_FILE* sfd) +{ + DynLock(sfd); + return putc(c, sfd->stream); +} + +void clearerr(SMP_FILE* sfd) +{ + DynLock(sfd); + return clearerr(sfd->stream); +} + +int fgetpos(SMP_FILE* sfd, fpos_t* pos) +{ + DynLock(sfd); + return fgetpos(sfd->stream, pos); +} + +int _fileno(SMP_FILE* sfd) +{ + DynLock(sfd); +#if defined(_WIN32) + return _fileno(sfd->stream); +#else + return fileno(sfd->stream); +#endif +} + +int setvbuf(SMP_FILE *sfd, char *buffer, int mode, size_t size) +{ + DynLock(sfd); + return setvbuf(sfd->stream, buffer, mode, size); +} + +int fprintf(SMP_FILE* sfd, const char* fmt, ...) +{ + size_t len; + DynLock(sfd); + va_list va; + va_start(va, fmt); + if (sim_ttrun_mode && sfd == smp_stdout && fmt[0] == '\n') + printf("\r"); + int res = vfprintf(sfd->stream, fmt, va); + if (sim_ttrun_mode && sfd == smp_stdout && (len = strlen(fmt)) && fmt[len - 1] == '\n') + printf("\r"); + va_end(va); + return res; +} + +#if defined (__linux) +int fseeko64(SMP_FILE *sfd, off64_t offset, int whence) +{ + DynLock(sfd); + return fseeko64(sfd->stream, offset, whence); +} + +off64_t ftello64(SMP_FILE *sfd) +{ + DynLock(sfd); + return ftello64(sfd->stream); +} +#endif + +#if defined (__APPLE__) || defined (__FreeBSD__) +int fseeko(SMP_FILE *sfd, off_t offset, int whence) +{ + DynLock(sfd); + return fseeko(sfd->stream, offset, whence); +} + +off_t ftello(SMP_FILE *sfd) +{ + DynLock(sfd); + return ftello(sfd->stream); +} +#endif + +#if 0 +// vfscanf is not implemented by Windows CRT. +// However, fscanf is not used by SIMH, so just leave it out. +int fscanf(SMP_FILE* sfd, const char* fmt, ...) +{ + DynLock(sfd); + va_list va; + va_start(va, fmt); + int res = vfscanf(sfd->stream, fmt, va); + va_end(va); + return res; +} +#endif \ No newline at end of file diff --git a/src/sim_sock.cpp b/src/sim_sock.cpp new file mode 100644 index 0000000..ce5c220 --- /dev/null +++ b/src/sim_sock.cpp @@ -0,0 +1,336 @@ +/* sim_sock.c: OS-dependent socket routines + + 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. + + 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) + 09-Jan-04 RMS Fixed typing problem in Alpha Unix (found by Tim Chapman) + 17-Apr-03 RMS Fixed non-implemented version of sim_close_sock + (found by Mark Pizzolato) + 17-Dec-02 RMS Added sim_connect_socket, sim_create_socket + 08-Oct-02 RMS Revised for .NET compatibility + 22-Aug-02 RMS Changed calling sequence for sim_accept_conn + 22-May-02 RMS Added OS2 EMX support from Holger Veit + 06-Feb-02 RMS Added VMS support from Robert Alan Byer + 16-Sep-01 RMS Added Macintosh support from Peter Schorn + 02-Sep-01 RMS Fixed UNIX bugs found by Mirian Lennox and Tom Markson +*/ + +#include "sim_defs.h" +#include "sim_sock.h" +#include + +/* OS dependent routines + + sim_master_sock create master socket + sim_accept_conn accept connection + sim_read_sock read from socket + sim_write_sock write from socket + sim_close_sock close socket + sim_setnonblock set socket non-blocking + sim_msg_sock send message to socket +*/ + +int32 sim_sock_cnt = 0; +#if defined (_WIN32) +AUTO_INIT_DEVLOCK(sim_sock_cnt_lock); +#endif + +/* First, all the non-implemented versions */ + +#if defined (__OS2__) && !defined (__EMX__) + +SOCKET sim_master_sock (int32 port) +{ +return INVALID_SOCKET; +} + +SOCKET sim_connect_sock (int32 ip, int32 port) +{ +return INVALID_SOCKET; +} + +SOCKET sim_accept_conn (SOCKET master, uint32 *ipaddr) +{ +return INVALID_SOCKET; +} + +int32 sim_read_sock (SOCKET sock, char *buf, int32 nbytes) +{ +return -1; +} + +int32 sim_write_sock (SOCKET sock, const char *msg, int32 nbytes) +{ +return 0; +} + +void sim_close_sock (SOCKET sock, t_bool master) +{ +return; +} + +int32 sim_setnonblock (SOCKET sock) +{ +return SOCKET_ERROR; +} + +#else /* endif unimpl */ + +/* UNIX, Win32, Macintosh, VMS, OS2 (Berkeley socket) routines */ + +SOCKET sim_err_sock (SOCKET s, char *emsg, int32 flg) +{ +int32 err = WSAGetLastError (); + +printf ("Sockets: %s error %d\n", emsg, err); +sim_close_sock (s, flg); +return INVALID_SOCKET; +} + +SOCKET sim_create_sock (void) +{ + SOCKET newsock; + int32 err; + +#if defined (_WIN32) + { + AUTO_LOCK(sim_sock_cnt_lock); + if (sim_sock_cnt == 0) + { + WORD wVersionRequested = MAKEWORD (1, 1); + WSADATA wsaData; + err = WSAStartup (wVersionRequested, &wsaData); /* start Winsock */ + if (err != 0) + { + smp_printf ("Winsock: startup error %d\n", err); + return INVALID_SOCKET; + } + } + sim_sock_cnt = sim_sock_cnt + 1; + } +#endif + +#if defined (SIGPIPE) + signal (SIGPIPE, SIG_IGN); /* no pipe signals */ +#endif + + newsock = socket (AF_INET, SOCK_STREAM, 0); /* create socket */ + if (newsock == INVALID_SOCKET) /* socket error? */ + { + err = WSAGetLastError (); + smp_printf ("Sockets: socket error %d\n", err); + return INVALID_SOCKET; + } + return newsock; +} + +SOCKET sim_master_sock (int32 port) +{ +SOCKET newsock; +struct sockaddr_in name; +int32 sta; + +newsock = sim_create_sock (); /* create socket */ +if (newsock == INVALID_SOCKET) /* socket error? */ + return newsock; + +name.sin_family = AF_INET; /* name socket */ +name.sin_port = htons ((unsigned short) port); /* insert port */ +name.sin_addr.s_addr = htonl (INADDR_ANY); /* insert addr */ + +sta = bind (newsock, (struct sockaddr *) &name, sizeof (name)); +if (sta == SOCKET_ERROR) /* bind error? */ + return sim_err_sock (newsock, "bind", 1); +sta = sim_setnonblock (newsock); /* set nonblocking */ +if (sta == SOCKET_ERROR) /* fcntl error? */ + return sim_err_sock (newsock, "fcntl", 1); +sta = listen (newsock, 1); /* listen on socket */ +if (sta == SOCKET_ERROR) /* listen error? */ + return sim_err_sock (newsock, "listen", 1); +return newsock; /* got it! */ +} + +SOCKET sim_connect_sock (int32 ip, int32 port) +{ +SOCKET newsock; +struct sockaddr_in name; +int32 sta; + +newsock = sim_create_sock (); /* create socket */ +if (newsock == INVALID_SOCKET) /* socket error? */ + return newsock; + +name.sin_family = AF_INET; /* name socket */ +name.sin_port = htons ((unsigned short) port); /* insert port */ +name.sin_addr.s_addr = htonl (ip); /* insert addr */ + +sta = sim_setnonblock (newsock); /* set nonblocking */ +if (sta == SOCKET_ERROR) /* fcntl error? */ + return sim_err_sock (newsock, "fcntl", 1); +sta = connect (newsock, (struct sockaddr *) &name, sizeof (name)); +if ((sta == SOCKET_ERROR) && + (WSAGetLastError () != WSAEWOULDBLOCK) && + (WSAGetLastError () != WSAEINPROGRESS)) + return sim_err_sock (newsock, "connect", 1); + +return newsock; /* got it! */ +} + +SOCKET sim_accept_conn (SOCKET master, uint32 *ipaddr) +{ +int32 sta, err; +#if defined (macintosh) || defined (__linux) || \ + defined (__APPLE__) || defined (__OpenBSD__) || \ + defined(__NetBSD__) || defined(__FreeBSD__) +socklen_t size; +#elif defined (_WIN32) || defined (__EMX__) ||\ + (defined (__ALPHA) && defined (__unix__)) +int size; +#else +size_t size; +#endif +SOCKET newsock; +struct sockaddr_in clientname; + +if (master == 0) /* not attached? */ + return INVALID_SOCKET; +size = sizeof (clientname); +newsock = accept (master, (struct sockaddr *) &clientname, &size); +if (newsock == INVALID_SOCKET) { /* error? */ + err = WSAGetLastError (); + if (err != WSAEWOULDBLOCK) + smp_printf ("Sockets: accept error %d\n", err); + return INVALID_SOCKET; + } +if (ipaddr != NULL) + *ipaddr = ntohl (clientname.sin_addr.s_addr); + +sta = sim_setnonblock (newsock); /* set nonblocking */ +if (sta == SOCKET_ERROR) /* fcntl error? */ + return sim_err_sock (newsock, "fcntl", 0); +return newsock; +} + +int32 sim_check_conn (SOCKET sock, t_bool rd) +{ +fd_set rw_set, er_set; +fd_set *rw_p = &rw_set; +fd_set *er_p = &er_set; +struct timeval tz; + +timerclear (&tz); +FD_ZERO (rw_p); +FD_ZERO (er_p); +FD_SET (sock, rw_p); +FD_SET (sock, er_p); +if (rd) + select ((int) sock + 1, rw_p, NULL, er_p, &tz); +else select ((int) sock + 1, NULL, rw_p, er_p, &tz); +if (FD_ISSET (sock, rw_p)) + return 1; +if (FD_ISSET (sock, er_p)) + return -1; +return 0; +} + +int32 sim_read_sock (SOCKET sock, char *buf, int32 nbytes) +{ +int32 rbytes, err; + +rbytes = recv (sock, buf, nbytes, 0); +if (rbytes == 0) /* disconnect */ + return -1; +if (rbytes == SOCKET_ERROR) { + err = WSAGetLastError (); + if (err == WSAEWOULDBLOCK) /* no data */ + return 0; + smp_printf ("Sockets: read error %d\n", err); + return -1; + } +return rbytes; +} + +int32 sim_write_sock (SOCKET sock, const char *msg, int32 nbytes) +{ +return send (sock, msg, nbytes, 0); +} + +void sim_close_sock (SOCKET sock, t_bool master) +{ +#if defined (_WIN32) +closesocket (sock); +if (master) { + AUTO_LOCK(sim_sock_cnt_lock); + sim_sock_cnt = sim_sock_cnt - 1; + if (sim_sock_cnt <= 0) { + WSACleanup (); + sim_sock_cnt = 0; + } + } +#else +close (sock); +#endif +return; +} + +#if defined (_WIN32) /* Windows */ +int32 sim_setnonblock (SOCKET sock) +{ +unsigned long non_block = 1; + +return ioctlsocket (sock, FIONBIO, &non_block); /* set nonblocking */ +} + +#elif defined (VMS) /* VMS */ +int32 sim_setnonblock (SOCKET sock) +{ +int non_block = 1; + +return ioctl (sock, FIONBIO, &non_block); /* set nonblocking */ +} + +#else /* Mac, Unix, OS/2 */ +int32 sim_setnonblock (SOCKET sock) +{ +int32 fl, sta; + +fl = fcntl (sock, F_GETFL,0); /* get flags */ +if (fl == -1) + return SOCKET_ERROR; +sta = fcntl (sock, F_SETFL, fl | O_NONBLOCK); /* set nonblock */ +if (sta == -1) + return SOCKET_ERROR; +#if !defined (macintosh) && !defined (__EMX__) /* Unix only */ +sta = fcntl (sock, F_SETOWN, getpid()); /* set ownership */ +if (sta == -1) + return SOCKET_ERROR; +#endif +return 0; +} + +#endif /* endif !Win32 && !VMS */ + +#endif /* end else !implemented */ diff --git a/src/sim_sock.h b/src/sim_sock.h new file mode 100644 index 0000000..9e1643b --- /dev/null +++ b/src/sim_sock.h @@ -0,0 +1,88 @@ +/* sim_sock.h: OS-dependent socket routines header file + + 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. + + 04-Jun-08 RMS Addes sim_create_sock, for IBM 1130 + 14-Apr-05 RMS Added WSAEINPROGRESS (from Tim Riker) + 20-Aug-04 HV Added missing definition for OS/2 (from Holger Veit) + 22-Oct-03 MP Changed WIN32 winsock include to use winsock2.h to + avoid a conflict if sim_sock.h and sim_ether.h get + included by the same module. + 20-Mar-03 RMS Added missing timerclear definition for VMS (from + Robert Alan Byer) + 15-Feb-03 RMS Added time.h for EMX (from Holger Veit) + 17-Dec-02 RMS Added sim_connect_sock + 08-Oct-02 RMS Revised for .NET compatibility + 20-Aug-02 RMS Changed calling sequence for sim_accept_conn + 30-Apr-02 RMS Changed VMS stropts include to ioctl + 06-Feb-02 RMS Added VMS support from Robert Alan Byer + 16-Sep-01 RMS Added Macintosh support from Peter Schorn +*/ + +#ifndef _SIM_SOCK_H_ +#define _SIM_SOCK_H_ 0 + +#if defined (_WIN32) /* Windows */ +#undef INT_PTR /* hack, hack */ +#include + +#elif !defined (__OS2__) || defined (__EMX__) /* VMS, Mac, Unix, OS/2 EMX */ +#define WSAGetLastError() errno /* Windows macros */ +#define SOCKET int32 +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEINPROGRESS EINPROGRESS +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#include /* for fcntl, getpid */ +#include /* for sockets */ +#include +#include +#include /* for sockaddr_in */ +#include +#include /* for EMX */ +#endif + +#if defined (VMS) /* VMS unique */ +#include /* for ioctl */ +#if !defined (timerclear) +#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 +#endif +#endif +#if defined(__EMX__) /* OS/2 unique */ +#if !defined (timerclear) +#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 +#endif +#endif + +SOCKET sim_master_sock (int32 port); +SOCKET sim_connect_sock (int32 ip, int32 port); +SOCKET sim_create_sock (void); +SOCKET sim_accept_conn (SOCKET master, uint32 *ipaddr); +int32 sim_check_conn (SOCKET sock, t_bool rd); +int32 sim_read_sock (SOCKET sock, char *buf, int32 nbytes); +int32 sim_write_sock (SOCKET sock, const char *msg, int32 nbytes); +void sim_close_sock (SOCKET sock, t_bool master); +int32 sim_setnonblock (SOCKET sock); + +#endif diff --git a/src/sim_syncw.cpp b/src/sim_syncw.cpp new file mode 100644 index 0000000..710290a --- /dev/null +++ b/src/sim_syncw.cpp @@ -0,0 +1,1338 @@ +#include "sim_defs.h" +#include "vax_defs.h" + +/* + * This file implements optional interprocessor synchronization window. For detailed description of what and why + * is performed here refer to "VAX MP Technical Overview", chapters "Interprocessor synchronization window" and + * "Implementation of VAX interlocked instructions". + * + * ToDo: Implement performance improvements suggested in "VAX MP Technical Overview", chapter "Interprocessor + * synchronization window". + */ + +/* + * Minimum number of quants (slices) to divide synchronization window into. During execution of VM (VAX) instruction + * stream, VCPU calls syncw_checkinterval() every (window size / SYNCW_QUANTS) VM instructon cycles or more often. + */ +#define SYNCW_QUANTS 10 + +/* + * Maximum size of syncw scale quant, in VM instruction cycles + */ +#define MAX_QUANT_SIZE (1000 * 1000) + +/* + * Maximum delay for inter-VCPU memory updates propagation, in VM instruction cycles. + * See the explanation below. + * + * As a rough estimate, at VM performance of 20-40 virtual MIPS it translates to 5-10 usec, + * which should be ample for updates propagation through cache coherency protocol. + * + * Alternatively, instead of guestimating this value, we could have raised and cleared + * SYNCLK, CLK and IPINTR under the protection of a lock, but it is desirable to avoid + * the overhead and serialization this would have introduced. + */ +#define INTERCPU_DELAY 200 + +/* + * Default base value for syncw.cpu[].pos, when VCPU initially enters itself into SYNCW. + * + * As explained below, VCPUs may be entered in SYNCW by other VCPUs at position + * (entering_VCPU.pos - INTERCPU_DELAY), + * so we need to reserve space at the start of the scale for enough INTERCPU_DELAY decrements. + */ +#define SYNCW_BASE_POS (20 * SIM_MAX_CPUS * INTERCPU_DELAY) + +syncw_data syncw; +smp_interlocked_uint32_var syncw_sys_active[SIM_MAX_CPUS]; +uint32 interrupt_reeval_syncw_sys[IPL_HLVL]; + +#define SYNCW_SYS_ILK (SYNCW_SYS | SYNCW_ILK) + +#define syncw_wakeup_waitset(ix) do { if (unlikely(syncw.cpu[ix].waitset.is_any_set())) syncw_wakeup(& syncw.cpu[ix].waitset, SYNCW_NOLOCK); } while (0) +#define syncw_wakeup_wakeset() do { if (unlikely(wakeset.is_any_set())) syncw_wakeup(& wakeset, SYNCW_NOLOCK); } while (0) + +#define syncw_process_external_syncw_sys(cx) \ + do { \ + if (unlikely((syncw.cpu[cx].active ^ cpu_unit->syncw_active) & SYNCW_SYS) && (syncw.cpu[cx].active & SYNCW_SYS)) \ + syncw_accept_external_syncw_sys(RUN_PASS); \ + } while (0) + +static void syncw_reinit(t_bool reset); +static void syncw_wakeup(cpu_set* waitset, uint32 flags); +static void syncw_accept_external_syncw_sys(RUN_DECL); +static void syncw_adjust_pos_all(uint32 delta); +static int strwidth(char* fmt, ...); +static uint32 higher_or_equal_multiple(uint32 q, uint32 d); + +/* + * Called once at simulator startup + */ +void syncw_init() +{ + syncw_reinit(FALSE); + + /* + * When fetching these interrupts, VCPU must enter SYNCW_SYS. + * If making changes to this code (i.e. value of interrupt_reeval_syncw_sys mask), + * be sure to also review InterruptRegister::query_syncw_sys(). + */ + memzero(interrupt_reeval_syncw_sys); + interrupt_reeval_syncw_sys[IPL_SYNCLK] |= (1 << INT_V_SYNCLK); + interrupt_reeval_syncw_sys[IPL_CLK] |= (1 << INT_V_CLK); + interrupt_reeval_syncw_sys[IPL_IPINTR] |= (1 << INT_V_IPINTR); +} + + +/* + * Called when primary CPU is reset, such as at "BOOT CPU" command + */ +void syncw_reset() +{ + syncw_reinit(TRUE); +} + + +/* + * Common code for syncw_init and syncw_reset + */ +static void syncw_reinit(t_bool reset) +{ + uint32 ix; + + if (cpu_database_lock) + cpu_database_lock->lock(); + + syncw.on = 0; + syncw.vsmp_winsize_sys = syncw.winsize_sys = 0; + syncw.vsmp_winsize_ilk = syncw.winsize_ilk = 0; + syncw.ipl_syslock = 6; + syncw.ipl_resched = 3; + syncw.checkinterval_sys = 0; + syncw.checkinterval_ilk = 0; + syncw.checkinterval_none = 2000000; + + for (ix = 0; ix < SIM_MAX_CPUS; ix++) + { + syncw.cpu[ix].active = 0; + syncw.cpu[ix].pos = SYNCW_BASE_POS; + syncw.cpu[ix].waitset.clear_all(); + smp_check_aligned(& syncw_sys_active[ix]); + if (reset) + { + /* primary VCPU reset, such as during BOOT CPU command */ + syncw_sys_active_do_clear(ix); + } + else + { + /* simulator startup */ + smp_var(syncw_sys_active[ix]) = 0; + } + } + + for (ix = 0; ix < sim_ncpus; ix++) + { + CPU_UNIT* xcpu = cpu_units[ix]; + xcpu->syncw_active = 0; + xcpu->syncw_countdown_start = xcpu->syncw_countdown = syncw.checkinterval_none; + xcpu->syncw_countdown_sys = 0; + xcpu->syncw_countdown_ilk = 0; + if (reset) xcpu->syncw_wait_event->set(); + xcpu->syncw_wait_cpu_id = NO_CPU_ID; + } + + if (cpu_database_lock) + cpu_database_lock->unlock(); +} + + +/* + * Called when VSMP gets loaded and initializes for actual use of synchronization window + */ +void syncw_start(RUN_DECL) +{ + /* + * We need to slash winsize_sys and winsize_ilk in half compared to the values reported by VSMP and + * based on maximum number of wait-cycles allowed. Here is why. Consider syncw scale that limits maximum + * relative "drift" between the processors to SW. Suppose there are two processors A and B active + * on the scale. B initially is near the top of the scale and A is near the base of the scale. + * + * top | B ^ + * | | | + * | | | + * | | (SW cycles) + * | | | + * | | | + * base A | v + * + * A performs interaction event (such as sending IPI to B or trying to acquire a spinlock held by B) + * and begins spin-waiting for B. B's thread is preempted and makes no progress. Syncw scale will allow A + * to make forward progress until it gets "SW" cycles ahead of "B": + * + * top A | ^ + * | | | + * | | | + * | | (SW cycles) + * | | | + * | | | + * base | B v + * + * Thus until A is suspended by syncw scale, it will be able to make forward progress by (2 * SW) + * cycles, which must not exceed winsize value reported by VSMP: 2 * SW <= vsmp_winsize_xxx. + * + * Yielding: SW <= vsmp_winsize_xxx / 2. + */ + + if (syncw.on & SYNCW_SYS) + { + /* note: VSMP is also responsible to limit requested winsize_sys to below 0x80000000 */ + syncw.winsize_sys = imin(syncw.vsmp_winsize_sys / 2, (uint32) 0x7F000000); + syncw.checkinterval_sys = syncw.winsize_sys / SYNCW_QUANTS; + if (syncw.checkinterval_sys == 0) + syncw.checkinterval_sys = 1; + if (syncw.checkinterval_sys > MAX_QUANT_SIZE) + syncw.checkinterval_sys = MAX_QUANT_SIZE; + + /* VSMP is responsible for never allowing such a low value of winsize_sys */ + if (syncw.checkinterval_sys <= INTERCPU_DELAY) + { + syncw.checkinterval_sys = INTERCPU_DELAY + 1; + syncw.winsize_sys = syncw.checkinterval_sys * SYNCW_QUANTS; + smp_printf("\n*** %s: SYNCW SYS size is too low, safe operation is impossible\n\n", sim_name); + if (sim_log) + fprintf(sim_log, "\n*** %s: SYNCW SYS size is too low, safe operation is impossible\n\n", sim_name); + } + } + else + { + syncw.checkinterval_sys = 0; + } + + if (syncw.on & SYNCW_ILK) + { + /* note: VSMP is also responsible to limit requested winsize_ilk to below 0x80000000 */ + syncw.winsize_ilk = imin(syncw.vsmp_winsize_ilk / 2, (uint32) 0x7F000000); + syncw.checkinterval_ilk = syncw.winsize_ilk / SYNCW_QUANTS; + if (syncw.checkinterval_ilk == 0) + syncw.checkinterval_ilk = 1; + if (syncw.checkinterval_ilk > MAX_QUANT_SIZE) + syncw.checkinterval_ilk = MAX_QUANT_SIZE; + } + else + { + syncw.checkinterval_ilk = 0; + } + + if ((syncw.on & SYNCW_SYS_ILK) == SYNCW_SYS_ILK) + { + if (syncw.winsize_ilk < syncw.winsize_sys) + { + syncw.winsize_sys = syncw.winsize_ilk; + syncw.checkinterval_sys = syncw.checkinterval_ilk; + } + else if (syncw.winsize_ilk > syncw.winsize_sys) + { + syncw.winsize_ilk = syncw.winsize_sys; + syncw.checkinterval_ilk = syncw.checkinterval_sys; + } + } + + uint32 winsize; + + switch (syncw.on & SYNCW_SYS_ILK) + { + case SYNCW_SYS_ILK: + winsize = imin(syncw.winsize_sys, syncw.winsize_ilk); + syncw.quant = imin(syncw.checkinterval_sys, syncw.checkinterval_ilk); + break; + case SYNCW_SYS: + winsize = syncw.winsize_sys; + syncw.quant = syncw.checkinterval_sys; + break; + case SYNCW_ILK: + winsize = syncw.winsize_ilk; + syncw.quant = syncw.checkinterval_ilk; + break; + case 0: + default: + winsize = 0; + syncw.quant = 0; + break; + } + + /* + * "maxdrift" is the maximum number of cycles VCPUs are allowed to drift apart, + * as checked in syncw_checkinterval(). "maxdrift" is calculated by shaving off + * the followng value off winsize: + * + * quant + higher_or_equal_multiple(quant, (uint32) INTERCPU_DELAY) + 4 + * + * First part ("quant") is because VCPU won't have a chance to re-check driftng conditions + * for another "quant" cycles. + * + * Second part (imax...) is the maximum number of cycles that may pass between another VCPU + * is sent CLK/SYNCLK/IPINTR interrupt and time the condition is noticed by this VCPU and that + * other VCPU is entered by this VCPU in synchronization window. All this time this VCPU may + * keep running, and thus pre-accumulate drift even before the condition is noticed. + * Second member in the sum reflects the maximum amount of this possible pre-accumulated drift + * before another VCPU is forced by this (or other VCPU) into SYNCW_SYS. + * It is the first multiple of "quant" higher or equal than INTERCPU_DELAY. + * In practice it normally be just one "quant". + * + * Final part (4) is a safeguard to avoid rounding problems and effects of ">=" vs ">" and + * also because in some busy-wait loop structures VCPU may signal entrance into SYNCW an + * instruction or two later after actual busy-wait loop starts. + * + */ + if (syncw.on & SYNCW_SYS_ILK) + { + uint32 reserve = syncw.quant + higher_or_equal_multiple(syncw.quant, (uint32) INTERCPU_DELAY) + 4; + syncw.maxdrift = winsize - reserve; + } + else + { + syncw.maxdrift = 0; + } + + syncw.seq = 0; +} + +/* calculate first multiple of "q" >= "d", but not lesser than "q" */ +static uint32 higher_or_equal_multiple(uint32 q, uint32 d) +{ + if (d == 0) + return q; + + for (uint32 v = q * (d / q); ; v += q) + { + if (v >= d) + return v; + } +} + +/* + * Called when secondary is about to start. + * Note that current thread may be another VCPU's thread. + * "Flags" argument may contain SYNCW_NOLOCK. + */ +void syncw_reinit_cpu(RUN_DECL, uint32 flags) +{ + uint32 cx = cpu_unit->cpu_id; + + if (! (flags & SYNCW_NOLOCK)) + cpu_database_lock->lock(); + + cpu_unit->syncw_active = 0; + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown = syncw.checkinterval_none; + cpu_unit->syncw_wait_cpu_id = NO_CPU_ID; + syncw.cpu[cx].active = 0; + syncw_sys_active_do_clear(cx); + syncw_wakeup_waitset(cx); + + if (! (flags & SYNCW_NOLOCK)) + cpu_database_lock->unlock(); +} + + +/* + * Calculate position when entering syncw. + * Calculate new position to be at the bottom of the range i.e. min(all other VCPUs in syncw), + * excluding "cx" from consideration. Note: "cx" can also be NO_CPU_ID. + * If no other VCPUs in syncw, default to SYNCW_BASE_POS. + */ +SIM_INLINE static uint32 syncw_entry_pos(uint32 cx) +{ + t_bool found = FALSE; + uint32 pos = SYNCW_BASE_POS; + + for (uint32 ix = 0; ix < sim_ncpus; ix++) + { + if (syncw.cpu[ix].active & SYNCW_SYS_ILK) + { + if (ix == cx) continue; + if (cpu_running_set.is_clear(ix)) continue; + if (syncw.cpu[ix].active & SYNCW_NOSYNC) continue; + + if (found) + { + if (syncw.cpu[ix].pos < pos) + pos = syncw.cpu[ix].pos; + } + else + { + pos = syncw.cpu[ix].pos; + found = TRUE; + } + } + } + + return pos; +} + + +/* + * Enter SYS synchronization window. + * Actual routne for syncw_enter_sys. + * Called by syncw_enter_sys macro after checking for preliminary conditions. + */ +void syncw_doenter_sys(RUN_DECL) +{ + uint32 cx = cpu_unit->cpu_id; + uint32 old_active; + + cpu_database_lock->lock(); + + /* + * We could check other VCPUs here for SYNCLK, CLK or IPINTR pendng and enter them into SYS window + * if they were not there yet. However this would create an extra overhead, and the assumption is + * that most stays in synchronization window are shorter than the duration of check interval. + * Therefore we check other VCPUs only in syncw_checkinterval, but not in syncw_doenter_xxx. + * Effectively we trade off a quant of syncw scale space in favor of better performance. + */ + + /* process possible setting of our SYNCW_SYS by other VCPU */ + syncw_process_external_syncw_sys(cx); + + if (unlikely(syncw.cpu[cx].active & SYNCW_NOSYNC)) + goto exit; + + if (unlikely(syncw.cpu[cx].active & SYNCW_SYS)) + goto exit; + + if (syncw.cpu[cx].active & SYNCW_ILK) + { + // keep current syncw.cpu[cx].pos + } + else + { + syncw.cpu[cx].pos = syncw_entry_pos(cx); + } + + old_active = syncw.cpu[cx].active & SYNCW_SYS_ILK; + syncw.cpu[cx].active |= SYNCW_SYS; + syncw_sys_active_do_set(cx); + cpu_database_lock->unlock(); + + cpu_unit->syncw_active = old_active | SYNCW_SYS; + cpu_unit->syncw_countdown_sys = syncw.checkinterval_sys; + if (old_active & SYNCW_ILK) + { + cpu_unit->syncw_countdown_ilk -= cpu_unit->syncw_countdown_start - cpu_unit->syncw_countdown; + cpu_unit->syncw_countdown = imin(cpu_unit->syncw_countdown_sys, cpu_unit->syncw_countdown_ilk); + } + else + { + cpu_unit->syncw_countdown = cpu_unit->syncw_countdown_sys; + } + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown; + + return; + +exit: + + cpu_unit->syncw_active = syncw.cpu[cx].active & SYNCW_SYS_ILK; + + cpu_database_lock->unlock(); +} + + +/* + * Leave SYS synchronization window. + * Actual routne for syncw_leave_sys. + * Called by syncw_leave_sys macro after checking for preliminary conditions. + */ +void syncw_doleave_sys(RUN_DECL) +{ + uint32 cx = cpu_unit->cpu_id; + cpu_set wakeset; + + cpu_database_lock->lock(); + + /* process possible setting of our SYNCW_SYS by other VCPU */ + syncw_process_external_syncw_sys(cx); + + t_bool leave_sys = FALSE; + + if (syncw.cpu[cx].active & SYNCW_SYS) + { + leave_sys = TRUE; + if (0 == (syncw.on & SYNCW_SYS) || weak_read(sim_mp_active) == FALSE || (syncw.cpu[cx].active & SYNCW_NOSYNC)) + { + /* leave regardless of any pending interrupts */ + } + else if (cpu_unit->cpu_synclk_pending == SynclkPendingIE1 || cpu_unit->cpu_intreg.query_syncw_sys()) + { + /* stay in SYNCW_SYS */ + leave_sys = FALSE; + } + + if (leave_sys) + { + syncw.cpu[cx].active &= ~SYNCW_SYS; + syncw_sys_active_do_clear(cx); + + wakeset = syncw.cpu[cx].waitset; + syncw.cpu[cx].waitset.clear_all(); + } + } + + cpu_unit->syncw_active = syncw.cpu[cx].active & SYNCW_SYS_ILK; + + cpu_database_lock->unlock(); + + if (leave_sys) + { + syncw_wakeup_wakeset(); + + if (cpu_unit->syncw_active & SYNCW_ILK) + { + cpu_unit->syncw_countdown_ilk -= cpu_unit->syncw_countdown_start - cpu_unit->syncw_countdown; + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown = cpu_unit->syncw_countdown_ilk; + } + else + { + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown = syncw.checkinterval_none; + } + } +} + + +/* + * Enter ILK synchronization window. + * Actual routne for syncw_enter_ilk. + * Called by syncw_enter_ilk macro after checking for preliminary conditions. + */ +t_bool syncw_doenter_ilk(RUN_DECL) +{ + uint32 cx = cpu_unit->cpu_id; + uint32 old_active; + + cpu_database_lock->lock(); + + /* process possible setting of our SYNCW_SYS by other VCPU */ + syncw_process_external_syncw_sys(cx); + + if (unlikely(syncw.cpu[cx].active & SYNCW_NOSYNC)) + goto exit; + + if (syncw.cpu[cx].active & SYNCW_SYS) + { + // keep current syncw.cpu[cx].pos + } + else + { + syncw.cpu[cx].pos = syncw_entry_pos(cx); + } + old_active = syncw.cpu[cx].active & SYNCW_SYS_ILK; + syncw.cpu[cx].active |= SYNCW_ILK; + cpu_database_lock->unlock(); + + cpu_unit->syncw_active = old_active | SYNCW_ILK; + cpu_unit->syncw_countdown_ilk = syncw.checkinterval_ilk; + if (old_active & SYNCW_SYS) + { + cpu_unit->syncw_countdown_sys -= cpu_unit->syncw_countdown_start - cpu_unit->syncw_countdown; + cpu_unit->syncw_countdown = imin(cpu_unit->syncw_countdown_sys, cpu_unit->syncw_countdown_ilk); + } + else + { + cpu_unit->syncw_countdown = cpu_unit->syncw_countdown_ilk; + } + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown; + + return TRUE; + +exit: + + cpu_unit->syncw_active = syncw.cpu[cx].active & SYNCW_SYS_ILK; + + cpu_database_lock->unlock(); + + return FALSE; +} + + +/* + * Leave ILK synchronization window. + * Actual routne for syncw_leave_ilk. + * Called by syncw_leave_ilk macro after checking for preliminary conditions. + */ +void syncw_doleave_ilk(RUN_DECL) +{ + uint32 cx = cpu_unit->cpu_id; + cpu_set wakeset; + + cpu_database_lock->lock(); + + /* process possible setting of our SYNCW_SYS by other VCPU */ + syncw_process_external_syncw_sys(cx); + + syncw.cpu[cx].active &= ~SYNCW_ILK; + cpu_unit->syncw_active = syncw.cpu[cx].active & SYNCW_SYS_ILK; + wakeset = syncw.cpu[cx].waitset; + syncw.cpu[cx].waitset.clear_all(); + cpu_database_lock->unlock(); + + syncw_wakeup_wakeset(); + + if (cpu_unit->syncw_active & SYNCW_SYS) + { + cpu_unit->syncw_countdown_sys -= cpu_unit->syncw_countdown_start - cpu_unit->syncw_countdown; + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown = cpu_unit->syncw_countdown_sys; + } + else + { + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown = syncw.checkinterval_none; + } +} + + +/* + * Leave both SYS and ILK synchronization windows. + * "Flags" argument can contain SYNCW_DISABLE_CPU and SYNCW_ENABLE_CPU. + * + * SYNCW_DISABLE_CPU disables use of synchronization window for this CPU, i.e. this CPU will no + * longer wait for other VCPUs, and other VCPUs will no longer wait for this VCPU. + * + * SYNCW_ENABLE_CPU reenables the use of synchronization window for this CPU. + */ +void syncw_leave_all(RUN_DECL, uint32 flags) +{ + uint32 cx = cpu_unit->cpu_id; + cpu_set wakeset; + + cpu_database_lock->lock(); + + /* process possible setting of our SYNCW_SYS by other VCPU */ + syncw_process_external_syncw_sys(cx); + + if (flags & SYNCW_DISABLE_CPU) + syncw.cpu[cx].active |= SYNCW_NOSYNC; + + if (flags & SYNCW_ENABLE_CPU) + syncw.cpu[cx].active &= ~SYNCW_NOSYNC; + + uint32 old_active = syncw.cpu[cx].active & SYNCW_SYS_ILK; + t_bool leave_sys = FALSE; + + if (syncw.cpu[cx].active & SYNCW_SYS) + { + leave_sys = TRUE; + if ((flags & SYNCW_OVERRIDE_ALL) || 0 == (syncw.on & SYNCW_SYS) || weak_read(sim_mp_active) == FALSE || (syncw.cpu[cx].active & SYNCW_NOSYNC)) + { + /* leave regardless of any pending interrupts */ + } + else if (cpu_unit->cpu_synclk_pending == SynclkPendingIE1 || cpu_unit->cpu_intreg.query_syncw_sys()) + { + /* stay in SYNCW_SYS */ + leave_sys = FALSE; + } + + if (leave_sys) + { + syncw.cpu[cx].active &= ~SYNCW_SYS; + syncw_sys_active_do_clear(cx); + } + } + + syncw.cpu[cx].active &= ~SYNCW_ILK; + + cpu_unit->syncw_active = syncw.cpu[cx].active & SYNCW_SYS_ILK; + + if (cpu_unit->syncw_active != old_active || (flags & SYNCW_DISABLE_CPU)) + { + wakeset = syncw.cpu[cx].waitset; + syncw.cpu[cx].waitset.clear_all(); + } + + cpu_database_lock->unlock(); + + syncw_wakeup_wakeset(); + + if (cpu_unit->syncw_active & SYNCW_SYS) + { + if (old_active & SYNCW_ILK) + { + cpu_unit->syncw_countdown_sys -= cpu_unit->syncw_countdown_start - cpu_unit->syncw_countdown; + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown = cpu_unit->syncw_countdown_sys; + } + } + else + { + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown = syncw.checkinterval_none; + } +} + + +/* + * Called when this VCPU receives SYNCWSYS interrupt from another VCPU. + * This interrupt is sent when one VCPU enters another VCPU in SYS window, + * so target VCPU sets up its local syncw data correspondingly. + */ +void syncw_process_syncwsys_interrupt(RUN_DECL) +{ + uint32 cx = cpu_unit->cpu_id; + cpu_database_lock->lock(); + syncw_process_external_syncw_sys(cx); + cpu_database_lock->unlock(); +} + + +/* + * Other VCPU has entered this VCPU into SYNCW_SYS by marking it as (syncw.cpu[].active & SYNCW_SYS), + * setting syncw.cpu[].pos for it and sending SYNCWSYS interrupt. Once this VCPU notices this event, + * it should make appropriate changes in its local syncw-related structures as well, including + * cpu_unit->countdown_xxx fields and cpu_unit->syncw_active. + * + * This routine is called under the protection of cpu_database_lock and only when SYNCW_SYS is set + * in syncw.cpu[].active but not set in cpu_unit->syncw_active. + * + * It is unlikely, but marginally possible that signalling VCPU entered this VCPU into SYNCW_SYS + * improperly, because it saw one of SYNCLK, CLK or IPINTR interrupts pending in its stale memory + * view, after they already had been already cleared. Therefore recheck the condition for the + * interrupts and if it does not hold true anymore then leave SYNCW_SYS. + */ +static void syncw_accept_external_syncw_sys(RUN_DECL) +{ + uint32 cx = cpu_unit->cpu_id; + t_bool noenter = FALSE; + + cpu_unit->cpu_intreg.dismiss_int(RUN_PASS, IPL_ABS_SYNCWSYS, INT_V_SYNCWSYS); + + if (unlikely(syncw.cpu[cx].active & SYNCW_NOSYNC)) + noenter = TRUE; + + if (unlikely((syncw.on & SYNCW_SYS) == 0)) + noenter = TRUE; + + if (PSL_GETIPL(PSL) >= syncw.ipl_syslock || + cpu_unit->cpu_synclk_pending == SynclkPendingIE1 || + cpu_unit->cpu_intreg.query_syncw_sys()) + { + /* must proceeed with entering into SYNCW_SYS */ + } + else + { + noenter = TRUE; + } + + if (unlikely(noenter)) + { + syncw.cpu[cx].active &= ~SYNCW_SYS; + syncw_sys_active_do_clear(cx); + syncw_wakeup_waitset(cx); + return; + } + + /* + * INTERCPU_DELAY is a presumed maximum time for memory updates propagation in MP system, + * i.e. maximum time before one processor notices IPI, CLK or SYNCLK sent to another VCPU + * by any other VCPU or clock thread + */ + cpu_unit->syncw_countdown_sys = syncw.checkinterval_sys - INTERCPU_DELAY; + cpu_unit->syncw_active |= SYNCW_SYS; + if (cpu_unit->syncw_active & SYNCW_ILK) + { + cpu_unit->syncw_countdown_ilk -= cpu_unit->syncw_countdown_start - cpu_unit->syncw_countdown; + cpu_unit->syncw_countdown = imin(cpu_unit->syncw_countdown_sys, cpu_unit->syncw_countdown_ilk); + } + else + { + cpu_unit->syncw_countdown = cpu_unit->syncw_countdown_sys; + } + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown; +} + + +/* + * Reenable the use of synchronization window for this CPU + */ +void syncw_enable_cpu(RUN_DECL, uint32 flags) +{ + if (flags & SYNCW_NOLOCK) + { + syncw.cpu[cpu_unit->cpu_id].active &= ~SYNCW_NOSYNC; + } + else + { + cpu_database_lock->lock(); + syncw.cpu[cpu_unit->cpu_id].active &= ~SYNCW_NOSYNC; + cpu_database_lock->unlock(); + } +} + + +/* + * Reevaluate appropriate SYNCW_SYS state ("in" or "out") for current VCPU + * and either enter SYNCW_SYS or leave SYNCW_SYS window. + */ +void syncw_reeval_sys(RUN_DECL) +{ + if (PSL_GETIPL(PSL) >= syncw.ipl_syslock || + cpu_unit->cpu_synclk_pending == SynclkPendingIE1 || + cpu_unit->cpu_intreg.query_syncw_sys()) + { + syncw_enter_sys(RUN_PASS); + } + else + { + syncw_leave_sys(RUN_PASS); + } +} + + +/* + * Wake up all VCPUs out of synchronization window waiting. + * Called when primary performs emergency shutdown of secondaries. + * + * "Flags" may include SYNCW_DISABLE_CPU to disable further use of syncw for all VCPUs, + * so all VCPUs may resume unconstrained by syncw and make their best attempt to shut down. + */ +void syncw_wakeup_all_cpus(uint32 flags) +{ + cpu_database_lock->lock(); + for (uint32 ix = 0; ix < sim_ncpus; ix++) + { + if (cpu_running_set.is_set(ix)) + { + if (flags & SYNCW_DISABLE_CPU) + syncw.cpu[ix].active |= SYNCW_NOSYNC; + syncw.cpu[ix].waitset.clear_all(); + cpu_units[ix]->syncw_wait_event->set(); + } + } + cpu_database_lock->unlock(); +} + + + +/* + * Wake up all VCPUs in the waitset. + * Clear waitset. + * "Flags" can include SYNCW_NOLOCK. + */ +static void syncw_wakeup(cpu_set* waitset, uint32 flags) +{ + if (! (flags & SYNCW_NOLOCK)) + cpu_database_lock->lock(); + + for (uint32 ix = 0; ix < sim_ncpus; ix++) + { + if (waitset->is_set(ix)) + { + waitset->clear(ix); + cpu_units[ix]->syncw_wait_event->set(); + } + } + + if (! (flags & SYNCW_NOLOCK)) + cpu_database_lock->unlock(); +} + + +/* + * VCPUs are about to start or resume execution as a result of console commands + * BOOT, CONTINUE, STEP, RUN or GO. Initialize wait data or reset wait data left + * by prior execution. + */ +void syncw_resuming() +{ + for (uint32 ix = 0; ix < sim_ncpus; ix++) + { + CPU_UNIT* xcpu = cpu_units[ix]; + syncw.cpu[ix].waitset.clear_all(); + xcpu->syncw_wait_cpu_id = NO_CPU_ID; + } +} + + +/* + * If "resuming" is FALSE: called by main instruction loop when syncw_countdown reaches zero. + * + * If "resuming" is TRUE: called when VCPU thread is resumed after suspension to console, + * before entering main instruction loop. In this case countdowns should not be recalculated + * and syncw.cpu[].pos should not be advanced. + */ +t_stat syncw_checkinterval(RUN_DECL, t_bool resuming) +{ + uint32 cx = cpu_unit->cpu_id; + uint32 ix; + uint32 delta; + + if (0 == (SYNCW_SYS_ILK & weak_read(syncw.cpu[cx].active))) + { + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown = syncw.checkinterval_none; + return SCPE_OK; + } + + cpu_set wakeset; + + cpu_database_lock->lock(); + syncw.seq++; + + /* + * set TRUE to check if other VCPUs may need to be entered in SYS window; + * will be set TRUE if SYS quant had expired or if resuming VCPU from console suspension + */ + t_bool syscheck_other_vcpus = FALSE; + + if (resuming) + { + delta = 0; + if (syncw.on & SYNCW_SYS) + syscheck_other_vcpus = TRUE; + } + else + { + /* record the number of cycles this VCPU advanced forward through the synchronization window */ + delta = (cpu_unit->syncw_active & SYNCW_SYS_ILK) ? cpu_unit->syncw_countdown_start - cpu_unit->syncw_countdown + : 0; + + /* refill countdown */ + switch (cpu_unit->syncw_active & SYNCW_SYS_ILK) + { + case SYNCW_SYS_ILK: + if ((cpu_unit->syncw_countdown_sys -= delta) == 0) + { + cpu_unit->syncw_countdown_sys = syncw.checkinterval_sys; + syscheck_other_vcpus = TRUE; + } + if ((cpu_unit->syncw_countdown_ilk -= delta) == 0) + cpu_unit->syncw_countdown_ilk = syncw.checkinterval_ilk; + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown = imin(cpu_unit->syncw_countdown_sys, cpu_unit->syncw_countdown_ilk); + break; + case SYNCW_SYS: + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown = cpu_unit->syncw_countdown_sys = syncw.checkinterval_sys; + syscheck_other_vcpus = TRUE; + break; + case SYNCW_ILK: + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown = cpu_unit->syncw_countdown_ilk = syncw.checkinterval_ilk; + break; + case 0: + default: + cpu_unit->syncw_countdown_start = cpu_unit->syncw_countdown = syncw.checkinterval_none; + break; + } + } + + /* process possible setting of our SYNCW_SYS by other VCPU */ + syncw_process_external_syncw_sys(cx); + + /* if any of syncw-relevant interrupts are pending, enter sys window */ + if (cpu_unit->cpu_synclk_pending == SynclkPendingIE1 || cpu_unit->cpu_intreg.query_syncw_sys()) + { + if (! (syncw.cpu[cx].active & SYNCW_NOSYNC)) + syncw_enter_sys(RUN_PASS); + } + + /* check if gone out of all windows or if syncw's had been disabled by emergency shutdown */ + if (unlikely(0 == (cpu_unit->syncw_active & syncw.on & SYNCW_SYS_ILK))) + { + syncw_leave_all(RUN_PASS, 0); + if (0 == (cpu_unit->syncw_active & SYNCW_SYS_ILK)) + { + cpu_database_lock->unlock(); + return SCPE_OK; + } + } + + /* + * syncw_checkinterval performs the check on all other processors to see if any of them have SYNCLK, CLK or IPINTR + * interrupts pending, but not in SYNCW_SYS yet. If so, syncw_checkinterval will enter such processors into SYNCW_SYS. + * + * Those processors will be entered in SYNCW_SYS at position equal to minimum of positions of any other processor + * already active either in SYNCW_SYS or SYNCW_ILK. For the purposes of this calculalation, position for current + * processor (in case it is active in synchronization window) will assumed to be that at previous call to + * syncw_checkinterval, i.e. the value *before* advancement by this invocation of syncw_checkinterval. The latter + * is required since SYNCLK/CLK/IPINTR might have been signalled to the processor being entered at any time + * during current VCPU's check interval, including at it's very beginning. + * + * Furthermore, since it takes time for other processor's SYNCLK/CLK/IPINTR to propagate before these signals + * become visible by this processor, some value to cover propagation delay will be substracted from computed + * min(all VCPU pos). Macro INTERCPU_DELAY defines the number of VM (VAX) instruction cycles that is presumed to + * be safely larger than any inter-cpu memory update propagation delay in any host system. In practice in + * contemporary low-NUMA systems propagation delay in most cases is under 1000 host CPU cycles and thus will not + * exceeed few VM cycles at most. We use for INTERCPU_DELAY the value of 200 VM cycles that should amply cover + * any propagation delay. + * + * Thus, when a VCPU1 is entered into SYNCW_SYS by VCPU2 because the latter observed VCPU1 having SYNCLK, CLK + * or IPINTR set, but VCPU1 not in SYNCW_SYS yet, VCPU2 will enter VCPU1 in SYNCW_SYS at position + * + * VCPU1.pos = min(VCPUx.pos) - INTERCPU_DELAY + * + * Where "x" is all VCPUs active either in SYS or ILK windows. If "x" includes current VCPU, its pos + * used in calculations will be one before advancement to next quant. + * + * If VCPU1 was already active in SYNCW_ILK, it will remain at its existing position, since interrupts + * could not have been signalled much before it entered SYNCW_ILK. + * + * This check is performed only on SYS intervals, not ILK intervals. + * It is also performed on VCPU thread resumption from console assumung SYNCW_SYS is enabled in syncw.on. + */ + + if (syscheck_other_vcpus) + { + uint32 pos = 0; + t_bool pos_valid = FALSE; + + for (ix = 0; ix < sim_ncpus; ix++) + { + /* skip CPUs that should not be checked */ + if (cpu_running_set.is_clear(ix)) continue; + if (ix == cx) continue; + if (syncw.cpu[ix].active & SYNCW_SYS) continue; + if (syncw.cpu[ix].active & SYNCW_NOSYNC) continue; + + /* check if xcpu should be entered in sys window */ + CPU_UNIT* xcpu = cpu_units[ix]; + if (xcpu->cpu_intreg.query_syncw_sys() && (syncw.on & SYNCW_SYS)) + { + /* if not in ILK yet, should calculate position for entering */ + if (! (syncw.cpu[ix].active & SYNCW_ILK)) + { + /* if multiple CPUs are being entered into sys window, calculate position just once for all of them */ + if (! pos_valid) + { + pos = syncw_entry_pos(NO_CPU_ID); + /* if target position is too low, move all VCPUs up */ + if (pos < INTERCPU_DELAY) + { + syncw_adjust_pos_all(SYNCW_BASE_POS + INTERCPU_DELAY); + pos += SYNCW_BASE_POS + INTERCPU_DELAY; + } + pos -= INTERCPU_DELAY; + + pos_valid = TRUE; + } + /* set xcpu's calculated position */ + syncw.cpu[ix].pos = pos; + } + + /* mark as entered in sys window */ + syncw.cpu[ix].active |= SYNCW_SYS; + syncw_sys_active_do_set(ix); + + /* send SYNCWSYS interrupt to xcpu to cause xcpu update its syncw_countdown_xxx */ + interrupt_set_int(xcpu, IPL_SYNCWSYS, INT_V_SYNCWSYS); + } + } + } + + /* + * Advance position. + * Note : pos should be advanced by "countdown", not VCPU cycle counters since the latter is also advanced by idle sleep + */ + syncw.cpu[cx].pos += delta; + + /* wakeup waiters if any */ + if (likely(delta)) + { + wakeset = syncw.cpu[cx].waitset; + syncw.cpu[cx].waitset.clear_all(); + } + + /* prevent overflow */ + if (unlikely(syncw.cpu[cx].pos > 0xE0000000)) + { + /* bump all VCPU's pos down towards SYNCW_BASE_POS */ + uint32 minpos = syncw_entry_pos(NO_CPU_ID); + syncw_adjust_pos_all(SYNCW_BASE_POS - minpos); + } + + /* + * see if we are being blocked from making further forward progress by any VCPU lagging behind, + * if so, enter wait for it + */ + for (;;) + { + /* do not wait if we have been disabled from synchronization window */ + if (syncw.cpu[cx].active & SYNCW_NOSYNC) + break; + + t_bool proceed = TRUE; + + for (ix = 0; ix < sim_ncpus; ix++) + { + /* skip irrelevant VCPUs */ + if (cpu_running_set.is_clear(ix)) continue; + if (ix == cx) continue; + if (! (syncw.cpu[ix].active & SYNCW_SYS_ILK)) continue; + if (syncw.cpu[ix].active & SYNCW_NOSYNC) continue; + + if (syncw.cpu[cx].pos > syncw.cpu[ix].pos && syncw.cpu[cx].pos - syncw.cpu[ix].pos > syncw.maxdrift) + { + /* enter wait on VCPU ix */ + cpu_unit->syncw_wait_cpu_id = ix; + syncw.cpu[ix].waitset.set(cx); + syncw.cpu[cx].seq = syncw.seq; + + /* is console stopping VCPUs? */ + if (unlikely(weak_read(stop_cpus))) + { + syncw.cpu[cx].waitset.op_or(& wakeset); + cpu_database_lock->unlock(); + return SCPE_STOP; + } + + /* if must enter sleep and STEP mode is on, return condition code (that will print to console) */ + if (unlikely(cpu_unit->sim_step)) + { + syncw.cpu[cx].waitset.op_or(& wakeset); + cpu_database_lock->unlock(); + return SCPE_SWSTP; + } + + /* sleep */ + cpu_unit->syncw_wait_event->clear(); + cpu_database_lock->unlock(); + syncw_wakeup_wakeset(); + cpu_unit->syncw_wait_event->wait(); + cpu_database_lock->lock(); + syncw.seq++; + + /* when here, had been resumed either by other VCPU or by the console */ + + /* might have been entered into SYNCW_SYS while sleeping: process it */ + syncw_process_external_syncw_sys(cx); + + /* if any of syncw-relevant interrupts are now pending, enter into sys window */ + if ((syncw.cpu[cx].active & (SYNCW_NOSYNC | SYNCW_SYS)) == 0) + { + if (cpu_unit->cpu_intreg.query_syncw_sys()) + syncw_enter_sys(RUN_PASS); + } + + if (unlikely(cpu_unit->syncw_active & ~syncw.on & SYNCW_SYS_ILK)) + { + /* if SYS had been disabled system-wide, shut it down for this VCPU too */ + if ((cpu_unit->syncw_active & SYNCW_SYS) && !(syncw.on & SYNCW_SYS)) + syncw_leave_sys(RUN_PASS); + + /* if ILK had been disabled system-wide, shut it down for this VCPU too */ + if ((cpu_unit->syncw_active & SYNCW_ILK) && !(syncw.on & SYNCW_ILK)) + syncw_leave_ilk(RUN_PASS); + } + + /* is console stopping VCPUs? */ + if (unlikely(stop_cpus)) + { + /* + * SIMH console requested VCPUs to pause and simulator to enter console mode. + * Note that since console raises stop_cpus signal, then locks cpu_database_lock, wakes up CPUs and + * unlocks cpu_database_lock, stop_cpus change is guaranteed to be visible to VCPUs after they aquire + * cpu_database_lock, since lock performs memory barrier and ensures memory update visibility + * propagation of stop_cpus. + * + * Do not reset syncw_wait_cpu_id and waitset, since their data will be used by "CPU INFO" command + * to display syncw state. They will be reset by the console when the console calls syncw_resuming() + * before resuming the execution of VCPUs. + */ + cpu_database_lock->unlock(); + return SCPE_STOP; + } + + /* mark as not waiting */ + cpu_unit->syncw_wait_cpu_id = NO_CPU_ID; + syncw.cpu[ix].waitset.clear(cx); + + /* if out of any window now, resume VCPU execution */ + if ((unlikely(cpu_unit->syncw_active & SYNCW_SYS_ILK) == 0)) + break; + + /* go recalc syncw constraint again */ + proceed = FALSE; + break; + } + } + + if (proceed) break; + } + + cpu_database_lock->unlock(); + + syncw_wakeup_wakeset(); + + return SCPE_OK; +} + +/* + * Adjust positions of all CPUs in syncw by "delta". + * Although "delta" is uint32, it can be negavite for downward adjustments. + */ +static void syncw_adjust_pos_all(uint32 delta) +{ + for (uint32 ix = 0; ix < sim_ncpus; ix++) + { + if (syncw.cpu[ix].active & SYNCW_SYS_ILK) + syncw.cpu[ix].pos += delta; + } +} + +/* + * Display syncw state. + * + * This routine is normally called on the console thread, however it can also be called + * on VCPU thread from op_reserved_ff when processing BUGW/BUGL bugcheck. In the latter + * case cpu_database_lock is held by the caller. + */ +void syncw_display(SMP_FILE* st) +{ + RUN_SCOPE_RSCX_ONLY; + + t_bool nactive = 0; + uint32 minpos = 0; /* initialize to suppress false GCC warning */ + const char* vs; + int w1 = 0, w2 = 0; + + /* Display current primary interlock mode */ + fprintf(st, "Interlock mode: %s\n", use_native_interlocked ? "native" : "portable"); + + /* Display syncw settings */ + switch (syncw.on & SYNCW_SYS_ILK) + { + case SYNCW_SYS_ILK: + vs = "SYS,ILK"; + w1 = imax(strwidth("%u", syncw.checkinterval_sys), strwidth("%u", syncw.checkinterval_ilk)); + w2 = imax(strwidth("%u", syncw.winsize_sys), strwidth("%u", syncw.winsize_ilk)); + break; + case SYNCW_SYS: + vs = "SYS"; + w1 = strwidth("%u", syncw.checkinterval_sys); + w2 = strwidth("%u", syncw.winsize_sys); + break; + case SYNCW_ILK: + w1 = strwidth("%u", syncw.checkinterval_ilk); + w2 = strwidth("%u", syncw.winsize_ilk); + vs = "ILK"; + break; + case 0: + default: + vs = "none"; + break; + } + fprintf(st, "Active synchronization windows: %s\n", vs); + if ((syncw.on & SYNCW_SYS_ILK) == SYNCW_SYS_ILK && + syncw.checkinterval_sys == syncw.checkinterval_ilk && + syncw.winsize_sys == syncw.winsize_ilk) + { + fprintf(st, "SYS and ILK check interval / window size: %*u / %*u\n", w1, syncw.checkinterval_sys, w2, syncw.winsize_sys); + } + else + { + if (syncw.on & SYNCW_SYS) + fprintf(st, "SYS check interval / window size: %*u / %*u\n", w1, syncw.checkinterval_sys, w2, syncw.winsize_sys); + if (syncw.on & SYNCW_ILK) + fprintf(st, "ILK check interval / window size: %*u / %*u\n", w1, syncw.checkinterval_ilk, w2, syncw.winsize_ilk); + } + + if (syncw.on & SYNCW_SYS_ILK) + fprintf(st, "Synchronization window quant / maxdrift: %d / %d\n", syncw.quant, syncw.maxdrift); + + /* Find minimum position in syncw */ + for (uint32 ix = 0; ix < sim_ncpus; ix++) + { + if (cpu_running_set.is_clear(ix)) continue; + if (syncw.cpu[ix].active & SYNCW_NOSYNC) continue; + if (syncw.cpu[ix].active & SYNCW_SYS_ILK) + { + if (nactive == 0) + { + minpos = syncw.cpu[ix].pos; + } + else + { + if (syncw.cpu[ix].pos < minpos) + minpos = syncw.cpu[ix].pos; + } + nactive++; + } + } + + if (nactive == 0) + { + fprintf(st, "\n"); + fprintf(st, "No CPUs currently active in synchronization window\n"); + } + else + { + fprintf(st, "\n"); + fprintf(st, "CPUs active in synchronization window:\n"); + fprintf(st, "\n"); + fprintf(st, "CP active rel wait wt\n"); + fprintf(st, "U# in pos seq# on waited by\n"); + fprintf(st, "-- ------- ---------- -------- -- ---------------------------\n"); + } + + /* Display per-CPU state of VCPUs active in syncw */ + for (uint32 ix = 0; ix < sim_ncpus; ix++) + { + CPU_UNIT* xcpu = cpu_units[ix]; + if (cpu_running_set.is_clear(ix)) continue; + if (syncw.cpu[ix].active & SYNCW_NOSYNC) continue; + + switch (syncw.cpu[ix].active & SYNCW_SYS_ILK) + { + case SYNCW_SYS_ILK: + vs = "SYS,ILK"; break; + case SYNCW_SYS: + vs = "SYS"; break; + case SYNCW_ILK: + vs = "ILK"; break; + default: + continue; + } + + fprintf(st, "%02d %7s %10d ", ix, vs, syncw.cpu[ix].pos - minpos); + volatile uint32 xcid = xcpu->syncw_wait_cpu_id; + if (xcid == NO_CPU_ID) + fprintf(st, " "); + else + fprintf(st, "%02d %08X ", xcid, syncw.cpu[ix].seq); + + t_bool first_waiter = TRUE; + for (uint32 k = 0; k < sim_ncpus; k++) + { + if (syncw.cpu[ix].waitset.is_set(k)) + { + fprintf(st, "%s%02d", first_waiter ? "" : ", "); + first_waiter = FALSE; + } + } + fprintf(st, "\n"); + } + + /* Perform consistency checks */ + for (uint32 ix = 0; ix < sim_ncpus; ix++) + { + CPU_UNIT* xcpu = cpu_units[ix]; + if (cpu_running_set.is_clear(ix)) continue; + if (syncw.cpu[ix].active & SYNCW_NOSYNC) continue; + + if (syncw.cpu[ix].active & SYNCW_SYS_ILK) + { + if (syncw.cpu[ix].pos < SYNCW_BASE_POS) + fprintf(st, "*** Warning: syncw pos is below SYNCW_BASE_POS (CPU%d)\n", ix); + } + + if (syncw_sys_active_is_set(ix) != (0 != (syncw.cpu[ix].active & SYNCW_SYS))) + fprintf(st, "*** Warning: syncw_sys_active_is_set mismatches (active & SYNCW_SYS) for CPU%d [%d/%d]\n", ix, syncw_sys_active_is_set(ix), 0 != (syncw.cpu[ix].active & SYNCW_SYS)); + + if ((syncw.cpu[ix].active & SYNCW_ILK) != (xcpu->syncw_active & SYNCW_ILK) && rscx->thread_type == SIM_THREAD_TYPE_CONSOLE) + fprintf(st, "*** Warning: (syncw_active & SYNCW_ILK) mismatches (active & SYNCW_ILK) for CPU%d [%d/%d]\n", ix, 0 != (syncw.cpu[ix].active & SYNCW_ILK), 0 != (xcpu->syncw_active & SYNCW_ILK)); + } +} + +/* + * get width of printf-formatted string + */ +static int strwidth(char* fmt, ...) +{ + char buf[1025]; + + va_list args; + va_start(args, fmt); + vsnprintf(buf, sizeof(buf) - 1, fmt, args); + va_end(args); + buf[1024] = '\0'; + return (int) strlen(buf); +} diff --git a/src/sim_syncw.h b/src/sim_syncw.h new file mode 100644 index 0000000..a275cae --- /dev/null +++ b/src/sim_syncw.h @@ -0,0 +1,168 @@ +#ifndef __SYNCW_H_INCLUDED__ +#define __SYNCW_H_INCLUDED__ + +typedef struct +{ + uint32 active; /* can keep SYNCW_SYS, SYNCW_ILK, SYNCW_NOSYNC */ + uint32 pos; /* current position in syncw */ + cpu_set waitset; /* set of VCPUs waiting on this VCPU */ + uint32 seq; /* wait sequence number, for debugging only */ +} +syncw_cpu_data; + +typedef struct SIM_ALIGN_CACHELINE +{ + uint32 on; + uint32 vsmp_winsize_sys; /* vsmp_winsize_xxx are kept for record only and are not actively used */ + uint32 vsmp_winsize_ilk; /* ... */ + uint32 winsize_sys; /* constraining SYS window size, i.e. maximum drift between VCPUs in SYS window */ + uint32 winsize_ilk; /* constraining ILK window size, i.e. maximum drift between VCPUs in ILK window */ + uint32 maxdrift; /* max inter-CPU drift that should cause forward VCPU to pause */ + int32 ipl_syslock; /* IPLs used to detect SYS and ILK entry/leave conditions */ + int32 ipl_resched; /* ... */ + uint32 checkinterval_sys; /* compare drifts in SYS window every checkinterval_sys cycles */ + uint32 checkinterval_ilk; /* compare drifts in SYS window every checkinterval_ilk cycles */ + uint32 checkinterval_none; /* refill value for cpu_unit->syncw_countdown when it is not active in any sync window */ + uint32 quant; /* scale quant size */ + uint32 seq; /* event sequence number used for diagnostics only */ + t_byte pad1[SMP_MAXCACHELINESIZE - sizeof(uint32) * 8]; + syncw_cpu_data cpu[SIM_MAX_CPUS]; /* access to cpu[] data is protected by cpu_database_lock */ + t_byte pad2[SMP_MAXCACHELINESIZE]; +} +syncw_data; + +extern syncw_data syncw; + +extern uint32 interrupt_reeval_syncw_sys[IPL_HLVL]; + +/* + * Array syncw_sys_active[] is the primary store for each VCPU's SYNCW_SYS active flag. + * + * The value of each entry is either 1 or 0. + * + * The entries are set, reset and checked using interlocked operations -- in order to avoid unnecessary + * locking of cpu_database_lock. + * + * Entry is: + * + * Set only under the protection of cpu_database_lock, either by corresponsing VCPU or + * by any other VCPU noticing that this VCPU has SYNCLK or IPI interrupts pending + * but is not in SYNCW_SYS yet. + * + * Reset only under the protection of cpu_database_lock by VCPU itself. + * + * Checked with interlocked instructions only; holding cpu_database_lock is not required for checking. + * + * There are two slave copies of syncw_sys_active[] kept for cheaper access under special valid only + * under special restrictive conditions. + * + * One copy is replicated to (cpu[].active & SYNCW_SYS) that can be accessed only under the protecton of + * cpu_database_lock and when accessed under such protection always accurately reflects syncw_sys_active[]. + * + * Another slave copy is kept in (cpu_unit->syncw_active & SYNCW_SYS). When (cpu_unit->syncw_active & SYNCW_SYS) + * is set, it is guaranteed that (cpu[].active & SYNCW_SYS) and syncw_sys_active[] are set too. + * When (cpu_unit->syncw_active & SYNCW_SYS) is cleared, the state of other two flags is unknown and + * should be consulted using appropriate access procedures: interlocked access for syncw_sys_active[] + * or access to (cpu[].active & SYNCW_SYS) while holding cpu_database_lock. + */ +extern smp_interlocked_uint32_var syncw_sys_active[SIM_MAX_CPUS]; +#define syncw_sys_active_is_set(ix) smp_interlocked_cas_done_var(& syncw_sys_active[ix], 1, 1) +#define syncw_sys_active_is_cleared(ix) smp_interlocked_cas_done_var(& syncw_sys_active[ix], 0, 0) +#define syncw_sys_active_do_set(ix) smp_interlocked_cas_done_var(& syncw_sys_active[ix], 0, 1) +#define syncw_sys_active_do_clear(ix) smp_interlocked_cas_done_var(& syncw_sys_active[ix], 1, 0) + +/* + * SYNCW_NOSYNC if set in syncw.cpu[].active designates that this CPU is excluded from synchronization. + * It does not participate in progress scale comparison and cannot be entered in any synchronization window. + */ +#define SYNCW_SYS (1 << 0) +#define SYNCW_ILK (1 << 1) +#define SYNCW_NOSYNC (1 << 2) + +#define SYNCW_OVERRIDE_ALL (1 << 0) +#define SYNCW_DISABLE_CPU (1 << 1) +#define SYNCW_ENABLE_CPU (1 << 2) +#define SYNCW_NOLOCK (1 << 3) + +void syncw_init(); +void syncw_reset(); +void syncw_start(RUN_DECL); + +#define syncw_need_enter_sys(cpu_unit) \ + ((syncw.on & SYNCW_SYS) && !(cpu_unit->syncw_active & SYNCW_SYS) && \ + weak_read(sim_mp_active) && syncw_sys_active_is_cleared((cpu_unit)->cpu_id)) + +#define syncw_enter_sys(cpu_unit) \ + do \ + { \ + if (syncw_need_enter_sys(cpu_unit)) \ + syncw_doenter_sys(cpu_unit); \ + } \ + while (0) + +#define syncw_enter_ilk(cpu_unit) \ + do \ + { \ + if ((syncw.on & SYNCW_ILK) && !(cpu_unit->syncw_active & SYNCW_ILK) && weak_read(sim_mp_active)) \ + syncw_doenter_ilk(cpu_unit); \ + } \ + while (0) + +#define syncw_leave_sys(cpu_unit) \ + do \ + { \ + if ((cpu_unit->syncw_active & SYNCW_SYS) || syncw_sys_active_is_set((cpu_unit)->cpu_id)) \ + syncw_doleave_sys(cpu_unit); \ + } \ + while (0) + +#define syncw_leave_ilk(cpu_unit) \ + do \ + { \ + if (cpu_unit->syncw_active & SYNCW_ILK) \ + syncw_doleave_ilk(cpu_unit); \ + } \ + while (0) + +void syncw_doenter_sys(RUN_DECL); +void syncw_doleave_sys(RUN_DECL); + +t_bool syncw_doenter_ilk(RUN_DECL); +void syncw_doleave_ilk(RUN_DECL); + +void syncw_leave_all(RUN_DECL, uint32 flags); + +void syncw_wakeup_all_cpus(uint32 flags); + +t_stat syncw_checkinterval(RUN_DECL, t_bool resuming); + +void syncw_reinit_cpu(RUN_DECL, uint32 flags = 0); + +void syncw_enable_cpu(RUN_DECL, uint32 flags = 0); + +void syncw_reeval_sys(RUN_DECL); + +void syncw_resuming(); + +void syncw_process_syncwsys_interrupt(RUN_DECL); + +/* + * Try to enter ILK. + * If SYNCW_ILK state changed as the result of the call, return TRUE. + * Otherwise return FALSE. + */ +SIM_INLINE static t_bool syncw_ifenter_ilk(RUN_DECL) +{ + if ((syncw.on & SYNCW_ILK) && !(cpu_unit->syncw_active & SYNCW_ILK) && weak_read(sim_mp_active)) + { + return syncw_doenter_ilk(RUN_PASS); + } + else + { + return FALSE; + } +} + +void syncw_display(SMP_FILE* st); + +#endif diff --git a/src/sim_tape.cpp b/src/sim_tape.cpp new file mode 100644 index 0000000..0fd71ee --- /dev/null +++ b/src/sim_tape.cpp @@ -0,0 +1,1935 @@ +/* sim_tape.c: simulator tape support 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. + + Ultimately, this will be a place to hide processing of various tape formats, + as well as OS-specific direct hardware access. + + 23-Jan-12 MP Added support for Logical EOT detection while positioning + 05-Feb-11 MP Refactored to prepare for SIM_ASYNC_IO support + Added higher level routines: + sim_tape_wreomrw - erase remainder of tape & rewind + sim_tape_sprecsf - skip records + sim_tape_spfilef - skip files + sim_tape_sprecsr - skip records rev + sim_tape_spfiler - skip files rev + sim_tape_position - general purpose position + These routines correspond to natural tape operations + and will align better when physical tape support is + included here. + 08-Jun-08 JDB Fixed signed/unsigned warning in sim_tape_set_fmt + 23-Jan-07 JDB Fixed backspace over gap at BOT + 22-Jan-07 RMS Fixed bug in P7B format read reclnt rev (found by Rich Cornwell) + 15-Dec-06 RMS Added support for small capacity tapes + 30-Aug-06 JDB Added erase gap support + 14-Feb-06 RMS Added variable tape capacity + 23-Jan-06 JDB Fixed odd-byte-write problem in sim_tape_wrrecf + 17-Dec-05 RMS Added write support for Paul Pierce 7b format + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 02-May-05 RMS Added support for Pierce 7b format + 28-Jul-04 RMS Fixed bug in writing error records (found by Dave Bryan) + RMS Fixed incorrect error codes (found by Dave Bryan) + 05-Jan-04 RMS Revised for file I/O library + 25-Apr-03 RMS Added extended file support + 28-Mar-03 RMS Added E11 and TPC format support + + Public routines: + + sim_tape_attach attach tape unit + sim_tape_detach detach tape unit + sim_tape_rdrecf read tape record forward + sim_tape_rdrecr read tape record reverse + sim_tape_wrrecf write tape record forward + sim_tape_sprecf space tape record forward + sim_tape_sprecr space tape record reverse + sim_tape_wrtmk write tape mark + sim_tape_wreom erase remainder of tape + sim_tape_wreomrw erase remainder of tape & rewind + sim_tape_wrgap write erase gap + sim_tape_sprecsf space records forward + sim_tape_spfilef space files forward + sim_tape_sprecsr space records reverse + sim_tape_spfiler space files reverse + sim_tape_position generalized position + sim_tape_rewind rewind + sim_tape_reset reset device + sim_tape_bot TRUE if at beginning of tape + sim_tape_eot TRUE if at or beyond end of tape + sim_tape_wrp TRUE if write protected + sim_tape_set_fmt set tape format + sim_tape_show_fmt show tape format + sim_tape_set_capac set tape capacity + sim_tape_show_capac show tape capacity + sim_tape_set_async enable asynchronous operation + sim_tape_clr_async disable asynchronous operation +*/ + +#include "sim_defs.h" +#include "sim_tape.h" +#include + +struct sim_tape_fmt { + char *name; /* name */ + int32 uflags; /* unit flags */ + t_addr bot; /* bot test */ + }; + +static struct sim_tape_fmt fmts[MTUF_N_FMT] = { + { "SIMH", 0, sizeof (t_mtrlnt) - 1 }, + { "E11", 0, sizeof (t_mtrlnt) - 1 }, + { "TPC", UNIT_RO, sizeof (t_tpclnt) - 1 }, + { "P7B", 0, 0 }, +/* { "TPF", UNIT_RO, 0 }, */ + { NULL, 0, 0 } + }; + +extern int32 sim_switches; + +t_stat sim_tape_ioerr (UNIT *uptr); +t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat); +uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map); +t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map); + +#define TOP_DONE 0 /* close */ +#define TOP_RDRF 1 /* sim_tape_rdrecf_a */ +#define TOP_RDRR 2 /* sim_tape_rdrecr_a */ +#define TOP_WREC 3 /* sim_tape_wrrecf_a */ +#define TOP_WTMK 4 /* sim_tape_wrtmk_a */ +#define TOP_WEOM 5 /* sim_tape_wreom_a */ +#define TOP_WEMR 6 /* sim_tape_wreomrw_a */ +#define TOP_WGAP 7 /* sim_tape_wrgap_a */ +#define TOP_SPRF 8 /* sim_tape_sprecf_a */ +#define TOP_SRSF 9 /* sim_tape_sprecsf_a */ +#define TOP_SPRR 10 /* sim_tape_sprecr_a */ +#define TOP_SRSR 11 /* sim_tape_sprecsr_a */ +#define TOP_SPFF 12 /* sim_tape_spfilef */ +#define TOP_SFRF 13 /* sim_tape_spfilebyrecf */ +#define TOP_SPFR 14 /* sim_tape_spfiler */ +#define TOP_SFRR 15 /* sim_tape_spfilebyrecr */ +#define TOP_RWND 16 /* sim_tape_rewind_a */ +#define TOP_POSN 17 /* sim_tape_position_a */ + +class tape_context : public aio_context +{ +public: + tape_context(UNIT* uptr) : aio_context(uptr) + { + io_top = TOP_DONE; + callback = NULL; + } + void perform_flush(); + static void perform_flush(UNIT* uptr); + t_bool has_request() { return io_top != TOP_DONE; } + void perform_request(); + +public: + int io_top; + uint8 *buf; + uint32 *bc; + uint32 *fc; + uint32 vbc; + uint32 max; + uint32 gaplen; + uint32 bpi; + uint32 *objupdate; + TAPE_PCALLBACK callback; +}; + +#define tape_ctx up8 /* Field in Unit structure which points to the tape_context */ + +static void aio_panic(); +#define AIO_CALLSETUP \ +tape_context* ctx = (tape_context*) uptr->tape_ctx; \ + \ +if ((!callback) || !ctx->asynch_io) + +/* caller of AIO_CALL will be holding uptr->lock */ +#define AIO_CALL(op, _buf, _bc, _fc, _max, _vbc, _gaplen, _bpi, _obj, _callback)\ + if (ctx->asynch_io) \ + { \ + tape_context* ctx = (tape_context*) uptr->tape_ctx; \ + \ + sim_debug (ctx->dbit, ctx->dptr, \ + "sim_tape AIO_CALL(op=%d, unit=%d)\n", op, sim_unit_index(uptr)); \ + \ + if (unlikely(NULL != ctx->callback)) \ + aio_panic(); /* gross error */ \ + ctx->buf = _buf; \ + ctx->bc = _bc; \ + ctx->fc = _fc; \ + ctx->max = _max; \ + ctx->vbc = _vbc; \ + ctx->gaplen = _gaplen; \ + ctx->bpi = _bpi; \ + ctx->objupdate = _obj; \ + ctx->callback = _callback; \ + ctx->io_reset_count = uptr->device->a_reset_count; \ + smp_wmb(); \ + ctx->io_top = op; \ + ctx->io_event_signal(); \ + } \ + else \ + if (_callback) \ + (_callback) (uptr, r); + + +SMP_THREAD_ROUTINE_DECL _tape_io(void* arg) +{ + UNIT* volatile uptr = (UNIT*)arg; + tape_context* ctx = (tape_context*) uptr->tape_ctx; + char tname[16]; + + sim_try + { + sim_debug (ctx->dbit, ctx->dptr, "_tape_io(unit=%d) starting\n", sim_unit_index(uptr)); + + smp_thread_init(); + + run_scope_context* rscx = new run_scope_context(NULL, SIM_THREAD_TYPE_IOP, ctx->io_thread); + rscx->set_current(); + + smp_set_thread_priority(SIMH_THREAD_PRIORITY_IOP); + sprintf(tname, "IOP_%s%d", uptr->device->name, sim_unit_index(uptr)); + smp_set_thread_name(tname); + + ctx->thread_loop(); + } + sim_catch (sim_exception_SimError, exc) + { + fprintf(smp_stderr, "\nFatal error in %s simulator, unexpected exception while executing tape IOP thread\n", sim_name); + fprintf(smp_stderr, "Exception cause: %s\n", exc->get_message()); + fprintf(smp_stderr, "Terminating the simulator abnormally...\n"); + exit(1); + } + sim_end_try + + sim_debug (ctx->dbit, ctx->dptr, "_tape_io(unit=%d) exiting\n", sim_unit_index(uptr)); + + SMP_THREAD_ROUTINE_END; +} + +void tape_context::perform_request() +{ + switch (io_top) + { + case TOP_RDRF: + io_status = sim_tape_rdrecf (uptr, buf, bc, max); + break; + case TOP_RDRR: + io_status = sim_tape_rdrecr (uptr, buf, bc, max); + break; + case TOP_WREC: + io_status = sim_tape_wrrecf (uptr, buf, vbc); + break; + case TOP_WTMK: + io_status = sim_tape_wrtmk (uptr); + break; + case TOP_WEOM: + io_status = sim_tape_wreom (uptr); + break; + case TOP_WEMR: + io_status = sim_tape_wreomrw (uptr); + break; + case TOP_WGAP: + io_status = sim_tape_wrgap (uptr, gaplen, bpi); + break; + case TOP_SPRF: + io_status = sim_tape_sprecf (uptr, bc); + break; + case TOP_SRSF: + io_status = sim_tape_sprecsf (uptr, vbc, bc); + break; + case TOP_SPRR: + io_status = sim_tape_sprecr (uptr, bc); + break; + case TOP_SRSR: + io_status = sim_tape_sprecsr (uptr, vbc, bc); + break; + case TOP_SPFF: + io_status = sim_tape_spfilef (uptr, vbc, bc); + break; + case TOP_SFRF: + io_status = sim_tape_spfilebyrecf (uptr, vbc, bc, fc, max); + break; + case TOP_SPFR: + io_status = sim_tape_spfiler (uptr, vbc, bc); + break; + case TOP_SFRR: + io_status = sim_tape_spfilebyrecr (uptr, vbc, bc, fc); + break; + case TOP_RWND: + io_status = sim_tape_rewind (uptr); + break; + case TOP_POSN: + io_status = sim_tape_position (uptr, vbc, gaplen, bc, bpi, fc, objupdate); + break; + } + io_top = TOP_DONE; + sim_async_post_io_event(uptr); +} + +/* This routine is called in the context of the main simulator thread before + processing events for any unit. It is only called when an asynchronous + thread has called sim_activate() to activate a unit. The job of this + routine is to put the unit in proper condition to digest what may have + occurred in the asynchrcondition thread. + + Since tape processing only handles a single I/O at a time to a + particular tape device, we have the opportunity to possibly detect + improper attempts to issue multiple concurrent I/O requests. */ +static void _tape_completion_dispatch (UNIT *uptr) +{ + tape_context* ctx = (tape_context*) uptr->tape_ctx; + TAPE_PCALLBACK callback = ctx->callback; + + sim_debug (ctx->dbit, ctx->dptr, "_tape_completion_dispatch(unit=%d, top=%d, callback=%p)\n", sim_unit_index(uptr), ctx->io_top, ctx->callback); + + if (ctx->io_top != TOP_DONE) + aio_panic(); /* horribly wrong, stop */ + + if (ctx->callback && ctx->io_top == TOP_DONE) + { + ctx->callback = NULL; + if (ctx->io_reset_count == uptr->device->a_reset_count) + callback (uptr, ctx->io_status); + } +} + +static void aio_panic() +{ + panic("Unexpected fatal error in tape AIO subsystem"); +} + + +/* Enable asynchronous operation */ + +t_stat sim_tape_set_async (UNIT *uptr, int latency) +{ + tape_context* ctx = (tape_context*) uptr->tape_ctx; + + if (ctx->asynch_io = sim_asynch_enabled) + { + uptr->a_check_completion = _tape_completion_dispatch; + ctx->asynch_io = FALSE; + ctx->asynch_init(_tape_io, (void*) uptr); + ctx->asynch_io = TRUE; + } + return SCPE_OK; +} + +/* Disable asynchronous operation */ + +t_stat sim_tape_clr_async (UNIT *uptr) +{ + tape_context* ctx = (tape_context*) uptr->tape_ctx; + + /* make sure device exists */ + if (!ctx) return SCPE_UNATT; + + if (ctx->asynch_io) + ctx->asynch_uninit(); + + return SCPE_OK; +} + +static void _sim_tape_io_flush (UNIT *uptr) +{ + tape_context* ctx = (tape_context*) uptr->tape_ctx; + if (ctx) + ctx->flush(); + else + tape_context::perform_flush(uptr); +} + +void tape_context::perform_flush() +{ + perform_flush(uptr); +} + +void tape_context::perform_flush(UNIT* uptr) +{ + fflush (uptr->fileref); +} + +/* Attach tape unit */ + +t_stat sim_tape_attach (UNIT *uptr, char *cptr) +{ +return sim_tape_attach_ex (uptr, cptr, 0); +} + +t_stat sim_tape_attach_ex (UNIT *uptr, char *cptr, uint32 dbit) +{ +tape_context* ctx; +uint32 objc; +DEVICE *dptr; +char gbuf[CBUFSIZE]; +t_stat r; + +if ((dptr = find_dev_from_unit (uptr)) == NULL) + return SCPE_NOATT; +if (sim_switches & SWMASK ('F')) { /* format spec? */ + cptr = get_glyph (cptr, gbuf, 0); /* get spec */ + if (*cptr == 0) /* must be more */ + return SCPE_2FARG; + if (sim_tape_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK) + return SCPE_ARG; + } +r = attach_unit (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) /* error? */ + return r; +switch (MT_GET_FMT (uptr)) { /* case on format */ + + case MTUF_F_TPC: /* TPC */ + objc = sim_tape_tpc_map (uptr, NULL); /* get # objects */ + if (objc == 0) { /* tape empty? */ + sim_tape_detach (uptr); + return SCPE_FMT; /* yes, complain */ + } + uptr->filebuf = calloc (objc + 1, sizeof (t_addr)); + if (uptr->filebuf == NULL) { /* map allocated? */ + sim_tape_detach (uptr); + return SCPE_MEM; /* no, complain */ + } + uptr->hwmark = objc + 1; /* save map size */ + sim_tape_tpc_map (uptr, (t_addr *) uptr->filebuf); /* fill map */ + break; + + default: + break; + } + +uptr->tape_ctx = ctx = new tape_context(uptr); +ctx->dptr = dptr; /* save DEVICE pointer */ +ctx->dbit = dbit; /* save debug bit */ + +sim_tape_rewind (uptr); + +sim_tape_set_async (uptr, 0); +uptr->io_flush = _sim_tape_io_flush; + +return SCPE_OK; +} + +/* Detach tape unit */ + +t_stat sim_tape_detach (UNIT *uptr) +{ +uint32 f = MT_GET_FMT (uptr); +t_stat r; + +sim_tape_clr_async (uptr); + +r = detach_unit (uptr); /* detach unit */ +if (r != SCPE_OK) + return r; +switch (f) { /* case on format */ + + case MTUF_F_TPC: /* TPC */ + if (uptr->filebuf) /* free map */ + free (uptr->filebuf); + uptr->filebuf = NULL; + uptr->hwmark = 0; + break; + + default: + break; + } + +sim_tape_rewind (uptr); +delete (tape_context*) uptr->tape_ctx; +uptr->tape_ctx = NULL; +uptr->io_flush = NULL; +return SCPE_OK; +} + +void sim_tape_data_trace(UNIT *uptr, const uint8 *data, size_t len, const char* txt, int detail, uint32 reason) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; + +if (ctx->dptr->dctrl & reason) { + sim_debug (reason, ctx->dptr, "%s%d %s len: %08X\n", ctx->dptr->name, sim_unit_index(uptr), txt, len); + if (detail) { + size_t i, same, group, sidx, oidx; + char outbuf[80], strbuf[18]; + static const char hex[] = "0123456789ABCDEF"; + + for (i=same=0; i 0) && (0 == memcmp (&data[i], &data[i-16], 16))) { + ++same; + continue; + } + if (same > 0) { + sim_debug (reason, ctx->dptr, "%04X thru %04X same as above\n", i-(16*same), i-1); + same = 0; + } + group = (((len - i) > 16) ? 16 : (len - i)); + for (sidx=oidx=0; sidx>4)&0xf]; + outbuf[oidx++] = hex[data[i+sidx]&0xf]; + if (isprint (data[i+sidx])) + strbuf[sidx] = data[i+sidx]; + else + strbuf[sidx] = '.'; + } + outbuf[oidx] = '\0'; + strbuf[sidx] = '\0'; + sim_debug (reason, ctx->dptr, "%04X%-48s %s\n", i, outbuf, strbuf); + } + if (same > 0) + sim_debug (reason, ctx->dptr, "%04X thru %04X same as above\n", i-(16*same), len-1); + } + } +} + +/* Read record length forward (internal routine) + + Inputs: + uptr = pointer to tape unit + bc = pointer to returned record length + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + read error unchanged, PNU set + end of file/medium unchanged, PNU set + tape mark updated + data record updated, sim_fread will read record forward + + See notes at "sim_tape_wrgap" regarding erase gap implementation. +*/ + +t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc) +{ +uint8 c; +t_bool all_eof; +uint32 f = MT_GET_FMT (uptr); +t_mtrlnt sbc; +t_tpclnt tpcbc; + +MT_CLR_PNU (uptr); +if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + return MTSE_UNATT; +sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set tape pos */ +switch (f) { /* switch on fmt */ + + case MTUF_F_STD: case MTUF_F_E11: + do { + sim_fread (bc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */ + sbc = MTR_L (*bc); /* save rec lnt */ + if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); /* pos not upd */ + return sim_tape_ioerr (uptr); + } + if (feof (uptr->fileref) || (*bc == MTR_EOM)) { /* eof or eom? */ + MT_SET_PNU (uptr); /* pos not upd */ + return MTSE_EOM; + } + uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* spc over rec lnt */ + if (*bc == MTR_TMK) /* tape mark? */ + return MTSE_TMK; + if (*bc == MTR_FHGAP) { /* half gap? */ + uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* half space fwd */ + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* resync */ + } + else if (*bc != MTR_GAP) + uptr->pos = uptr->pos + sizeof (t_mtrlnt) + /* spc over record */ + ((f == MTUF_F_STD)? ((sbc + 1) & ~1): sbc); + } + while ((*bc == MTR_GAP) || (*bc == MTR_FHGAP)); + break; + + case MTUF_F_TPC: + sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref); + *bc = tpcbc; /* save rec lnt */ + if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); /* pos not upd */ + return sim_tape_ioerr (uptr); + } + if (feof (uptr->fileref)) { /* eof? */ + MT_SET_PNU (uptr); /* pos not upd */ + return MTSE_EOM; + } + uptr->pos = uptr->pos + sizeof (t_tpclnt); /* spc over reclnt */ + if (tpcbc == TPC_TMK) /* tape mark? */ + return MTSE_TMK; + uptr->pos = uptr->pos + ((tpcbc + 1) & ~1); /* spc over record */ + break; + + case MTUF_F_P7B: + for (sbc = 0, all_eof = 1; ; sbc++) { /* loop thru record */ + sim_fread (&c, sizeof (uint8), 1, uptr->fileref); + if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); /* pos not upd */ + return sim_tape_ioerr (uptr); + } + if (feof (uptr->fileref)) { /* eof? */ + if (sbc == 0) /* no data? eom */ + return MTSE_EOM; + break; /* treat like eor */ + } + if ((sbc != 0) && (c & P7B_SOR)) /* next record? */ + break; + if ((c & P7B_DPAR) != P7B_EOF) + all_eof = 0; + } + *bc = sbc; /* save rec lnt */ + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */ + uptr->pos = uptr->pos + sbc; /* spc over record */ + if (all_eof) /* tape mark? */ + return MTSE_TMK; + break; + + default: + return MTSE_FMT; + } + +return MTSE_OK; +} + +/* Read record length reverse (internal routine) + + Inputs: + uptr = pointer to tape unit + bc = pointer to returned record length + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + beginning of tape unchanged + read error unchanged + end of file unchanged + end of medium updated + tape mark updated + data record updated, sim_fread will read record forward + + See notes at "sim_tape_wrgap" regarding erase gap implementation. +*/ + +t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc) +{ +uint8 c; +t_bool all_eof; +uint32 f = MT_GET_FMT (uptr); +t_addr ppos; +t_mtrlnt sbc; +t_tpclnt tpcbc; + +MT_CLR_PNU (uptr); +if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + return MTSE_UNATT; +if (sim_tape_bot (uptr)) /* at BOT? */ + return MTSE_BOT; +switch (f) { /* switch on fmt */ + + case MTUF_F_STD: case MTUF_F_E11: + do { + sim_fseek (uptr->fileref, uptr->pos - sizeof (t_mtrlnt), SEEK_SET); + sim_fread (bc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */ + sbc = MTR_L (*bc); + if (ferror (uptr->fileref)) /* error? */ + return sim_tape_ioerr (uptr); + if (feof (uptr->fileref)) /* eof? */ + return MTSE_EOM; + uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* spc over rec lnt */ + if (*bc == MTR_EOM) /* eom? */ + return MTSE_EOM; + if (*bc == MTR_TMK) /* tape mark? */ + return MTSE_TMK; + if ((*bc & MTR_M_RHGAP) == MTR_RHGAP) { /* half gap? */ + uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* half space rev */ + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* resync */ + } + else if (*bc != MTR_GAP) { + uptr->pos = uptr->pos - sizeof (t_mtrlnt) - /* spc over record */ + ((f == MTUF_F_STD)? ((sbc + 1) & ~1): sbc); + sim_fseek (uptr->fileref, uptr->pos + sizeof (t_mtrlnt), SEEK_SET); + } + else if (sim_tape_bot (uptr)) /* backed into BOT? */ + return MTSE_BOT; + } + while ((*bc == MTR_GAP) || (*bc == MTR_RHGAP)); + break; + + case MTUF_F_TPC: + ppos = sim_tape_tpc_fnd (uptr, (t_addr *) uptr->filebuf); /* find prev rec */ + sim_fseek (uptr->fileref, ppos, SEEK_SET); /* position */ + sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref); + *bc = tpcbc; /* save rec lnt */ + if (ferror (uptr->fileref)) /* error? */ + return sim_tape_ioerr (uptr); + if (feof (uptr->fileref)) /* eof? */ + return MTSE_EOM; + uptr->pos = ppos; /* spc over record */ + if (*bc == MTR_TMK) /* tape mark? */ + return MTSE_TMK; + sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET); + break; + + case MTUF_F_P7B: + for (sbc = 1, all_eof = 1; (t_addr) sbc <= uptr->pos ; sbc++) { + sim_fseek (uptr->fileref, uptr->pos - sbc, SEEK_SET); + sim_fread (&c, sizeof (uint8), 1, uptr->fileref); + if (ferror (uptr->fileref)) /* error? */ + return sim_tape_ioerr (uptr); + if (feof (uptr->fileref)) /* eof? */ + return MTSE_EOM; + if ((c & P7B_DPAR) != P7B_EOF) + all_eof = 0; + if (c & P7B_SOR) /* start of record? */ + break; + } + uptr->pos = uptr->pos - sbc; /* update position */ + *bc = sbc; /* save rec lnt */ + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */ + if (all_eof) /* tape mark? */ + return MTSE_TMK; + break; + + default: + return MTSE_FMT; + } + +return MTSE_OK; +} + +/* Read record forward + + Inputs: + uptr = pointer to tape unit + buf = pointer to buffer + bc = pointer to returned record length + max = maximum record size + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + read error unchanged, PNU set + end of file/medium unchanged, PNU set + invalid record unchanged, PNU set + tape mark updated + data record updated + data record error updated +*/ + +t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; +uint32 f = MT_GET_FMT (uptr); +t_mtrlnt i, tbc, rbc; +t_addr opos; +t_stat st; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecf(unit=%d, buf=%p, max=%d)\n", sim_unit_index(uptr), buf, max); + +opos = uptr->pos; /* old position */ +if (st = sim_tape_rdlntf (uptr, &tbc)) /* read rec lnt */ + return st; +*bc = rbc = MTR_L (tbc); /* strip error flag */ +if (rbc > max) { /* rec out of range? */ + MT_SET_PNU (uptr); + uptr->pos = opos; + return MTSE_INVRL; + } +i = (t_mtrlnt) sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */ +if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); + uptr->pos = opos; + return sim_tape_ioerr (uptr); + } +for ( ; i < rbc; i++) /* fill with 0's */ + buf[i] = 0; +if (f == MTUF_F_P7B) /* p7b? strip SOR */ + buf[0] = buf[0] & P7B_DPAR; +return (MTR_F (tbc)? MTSE_RECE: MTSE_OK); +} + +t_stat sim_tape_rdrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback) +{ +t_stat r = SCPE_OK; +AIO_CALLSETUP + r = sim_tape_rdrecf (uptr, buf, bc, max); +AIO_CALL(TOP_RDRF, buf, bc, NULL, max, 0, 0, 0, NULL, callback); +return r; +} + + +/* Read record reverse + + Inputs: + uptr = pointer to tape unit + buf = pointer to buffer + bc = pointer to returned record length + max = maximum record size + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + read error unchanged + end of file unchanged + end of medium updated + invalid record unchanged + tape mark updated + data record updated + data record error updated +*/ + +t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; +uint32 f = MT_GET_FMT (uptr); +t_mtrlnt i, rbc, tbc; +t_stat st; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecr(unit=%d, buf=%p, max=%d)\n", sim_unit_index(uptr), buf, max); + +if (st = sim_tape_rdlntr (uptr, &tbc)) /* read rec lnt */ + return st; +*bc = rbc = MTR_L (tbc); /* strip error flag */ +if (rbc > max) /* rec out of range? */ + return MTSE_INVRL; +i = (t_mtrlnt) sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */ +if (ferror (uptr->fileref)) /* error? */ + return sim_tape_ioerr (uptr); +for ( ; i < rbc; i++) /* fill with 0's */ + buf[i] = 0; +if (f == MTUF_F_P7B) /* p7b? strip SOR */ + buf[0] = buf[0] & P7B_DPAR; +return (MTR_F (tbc)? MTSE_RECE: MTSE_OK); +} + +t_stat sim_tape_rdrecr_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback) +{ +t_stat r = SCPE_OK; +AIO_CALLSETUP + r = sim_tape_rdrecr (uptr, buf, bc, max); +AIO_CALL(TOP_RDRR, buf, bc, NULL, max, 0, 0, 0, NULL, callback); +return r; +} + +/* Write record forward + + Inputs: + uptr = pointer to tape unit + buf = pointer to buffer + bc = record length + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + write protect unchanged + write error unchanged, PNU set + data record updated +*/ + +t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; +uint32 f = MT_GET_FMT (uptr); +t_mtrlnt sbc; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrrecf(unit=%d, buf=%p, bc=%d)\n", sim_unit_index(uptr), buf, bc); + +MT_CLR_PNU (uptr); +sbc = MTR_L (bc); +if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + return MTSE_UNATT; +if (sim_tape_wrp (uptr)) /* write prot? */ + return MTSE_WRP; +if (sbc == 0) /* nothing to do? */ + return MTSE_OK; +sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */ +switch (f) { /* case on format */ + + case MTUF_F_STD: /* standard */ + sbc = MTR_L ((bc + 1) & ~1); /* pad odd length */ + case MTUF_F_E11: /* E11 */ + sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref); + sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref); + sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref); + if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); + return sim_tape_ioerr (uptr); + } + uptr->pos = uptr->pos + sbc + (2 * sizeof (t_mtrlnt)); /* move tape */ + break; + + case MTUF_F_P7B: /* Pierce 7B */ + buf[0] = buf[0] | P7B_SOR; /* mark start of rec */ + sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref); + sim_fwrite (buf, sizeof (uint8), 1, uptr->fileref); /* delimit rec */ + if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); + return sim_tape_ioerr (uptr); + } + uptr->pos = uptr->pos + sbc; /* move tape */ + break; + } + +return MTSE_OK; +} + +t_stat sim_tape_wrrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt bc, TAPE_PCALLBACK callback) +{ +t_stat r = SCPE_OK; +AIO_CALLSETUP + r = sim_tape_wrrecf (uptr, buf, bc); +AIO_CALL(TOP_WREC, buf, 0, NULL, 0, bc, 0, 0, NULL, callback); +return r; +} + +/* Write metadata forward (internal routine) */ + +t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat) +{ +MT_CLR_PNU (uptr); +if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + return MTSE_UNATT; +if (sim_tape_wrp (uptr)) /* write prot? */ + return MTSE_WRP; +sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */ +sim_fwrite (&dat, sizeof (t_mtrlnt), 1, uptr->fileref); +if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); + return sim_tape_ioerr (uptr); + } +uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* move tape */ +return MTSE_OK; +} + +/* Write tape mark */ + +t_stat sim_tape_wrtmk (UNIT *uptr) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrtmk(unit=%d)\n", sim_unit_index(uptr)); +if (MT_GET_FMT (uptr) == MTUF_F_P7B) { /* P7B? */ + uint8 buf = P7B_EOF; /* eof mark */ + return sim_tape_wrrecf (uptr, &buf, 1); /* write char */ + } +return sim_tape_wrdata (uptr, MTR_TMK); +} + +t_stat sim_tape_wrtmk_a (UNIT *uptr, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_wrtmk (uptr); +AIO_CALL(TOP_WTMK, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback); +return r; +} + +/* Write end of medium */ + +t_stat sim_tape_wreom (UNIT *uptr) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wreom(unit=%d)\n", sim_unit_index(uptr)); +if (MT_GET_FMT (uptr) == MTUF_F_P7B) /* cant do P7B */ + return MTSE_FMT; +return sim_tape_wrdata (uptr, MTR_EOM); +} + +t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_wreom (uptr); +AIO_CALL(TOP_WEOM, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback); +return r; +} + +/* Write end of medium-rewind */ + +t_stat sim_tape_wreomrw (UNIT *uptr) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; +t_stat r; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wreomrw(unit=%d)\n", sim_unit_index(uptr)); +if (MT_GET_FMT (uptr) == MTUF_F_P7B) /* cant do P7B */ + return MTSE_FMT; +r = sim_tape_wrdata (uptr, MTR_EOM); +if (r == MTSE_OK) + r = sim_tape_rewind (uptr); +return r; +} + +t_stat sim_tape_wreomrw_a (UNIT *uptr, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_wreomrw (uptr); +AIO_CALL(TOP_WEMR, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback); +return r; +} + +/* Write erase gap + + Inputs: + uptr = pointer to tape unit + gaplen = length of gap in tenths of an inch + bpi = current recording density in bytes per inch + + Outputs: + status = operation status + + exit condition position + ------------------ ------------------ + unit unattached unchanged + unsupported format unchanged + write protected unchanged + read error unchanged, PNU set + write error unchanged, PNU set + gap written updated + + + An erase gap is represented in the tape image file by a special metadata + value. This value is chosen so that it is still recognizable even if it has + been "cut in half" by a subsequent data overwrite that does not end on a + metadatum-sized boundary. In addition, a range of metadata values are + reserved for detection in the reverse direction. Erase gaps are supported + only in SIMH tape format. + + This implementation supports erasing gaps in the middle of a populated tape + image and will always produce a valid image. It also produces valid images + when overwriting gaps with data records, with one exception: a data write + that leaves only two bytes of gap remaining will produce an invalid tape. + This limitation is deemed acceptable, as it is analogous to the existing + limitation that data records cannot overwrite other data records without + producing an invalid tape. + + Because SIMH tape images do not carry physical parameters (e.g., recording + density), overwriting a tape image file containing gap metadata is + problematic if the density setting is not the same as that used during + recording. There is no way to establish a gap of a certain length + unequivocally in an image file, so this implementation establishes a gap of a + certain number of bytes that reflect the desired gap length at the bpi used + during writing. + + To write an erase gap, the implementation uses one of two approaches, + depending on whether or not the current tape position is at EOM. Erasing at + EOM presents no special difficulties; gap metadata markers are written for + the prescribed number of bytes. If the tape is not at EOM, then erasing must + take into account the existing record structure to ensure that a valid tape + image is maintained. + + The general approach is to erase for the nominal number of bytes but to + increase that length, if necessary, to ensure that a partially overwritten + data record at the end of the gap can be altered to maintain validity. + Because the smallest legal tape record requires space for two metadata + markers plus two data bytes, an erasure that would leave less than that + is increased to consume the entire record. Otherwise, the final record is + truncated appropriately. + + When reading in either direction, gap metadata markers are ignored (skipped) + until a record length header, EOF marker, EOM marker, or physical EOF is + encountered. Thus, tape images containing gap metadata are transparent to + the calling simulator. + + The permissibility of data record lengths that are not multiples of the + metadatum size presents a difficulty when reading. If such an "odd length" + record is written over a gap, half of a metadata marker will exist + immediately after the trailing record length. + + This condition is detected when reading forward by the appearance of a + "reversed" marker. The value appears reversed because the value is made up + of half of one marker and half of the next. This is handled by seeking + forward two bytes to resync (the stipulation above that the overwrite cannot + leave only two bytes of gap means that at least one "whole" metadata marker + will follow). Reading in reverse presents a more complex problem, because + half of the marker is from the preceding trailing record length marker and + therefore could be any of a range of values. However, that range is + restricted by the SIMH tape specification requirement that record length + metadata values must have bits 30:24 set to zero. This allows unambiguous + detection of the condition. + + The value chosen for gap metadata and the values reserved for "half-gap" + detection are: + + 0xFFFFFFFE - primary gap value + 0xFFFEFFFF - reserved (indicates half-gap in forward reads) + 0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads) + 0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads) + */ + +t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen, uint32 bpi) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; +t_stat st; +t_mtrlnt meta, sbc, new_len, rec_size; +t_addr gap_pos = uptr->pos; +uint32 file_size, marker_count; +uint32 format = MT_GET_FMT (uptr); +uint32 gap_alloc = 0; /* gap allocated from tape */ +int32 gap_needed = (gaplen * bpi) / 10; /* gap remainder still needed */ +const uint32 meta_size = sizeof (t_mtrlnt); /* bytes per metadatum */ +const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* smallest data record size */ + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%p, bpi=%d)\n", sim_unit_index(uptr), gaplen, bpi); + +MT_CLR_PNU (uptr); + +if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + return MTSE_UNATT; +if (format != MTUF_F_STD) /* not SIMH fmt? */ + return MTSE_FMT; +if (sim_tape_wrp (uptr)) /* write protected? */ + return MTSE_WRP; + +file_size = sim_fsize (uptr->fileref); /* get file size */ +sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */ + +/* Read tape records and allocate to gap until amount required is consumed. + + Read next metadatum from tape: + - EOF or EOM: allocate remainder of bytes needed. + - TMK or GAP: allocate sizeof(metadatum) bytes. + - Reverse GAP: allocate sizeof(metadatum) / 2 bytes. + - Data record: see below. + + Loop until bytes needed = 0. +*/ + +do { + sim_fread (&meta, meta_size, 1, uptr->fileref); /* read metadatum */ + + if (ferror (uptr->fileref)) { /* read error? */ + uptr->pos = gap_pos; /* restore original position */ + MT_SET_PNU (uptr); /* position not updated */ + return sim_tape_ioerr (uptr); /* translate error */ + } + else + uptr->pos = uptr->pos + meta_size; /* move tape over datum */ + + if (feof (uptr->fileref) || (meta == MTR_EOM)) { /* at eof or eom? */ + gap_alloc = gap_alloc + gap_needed; /* allocate remainder */ + gap_needed = 0; + } + + else if ((meta == MTR_GAP) || (meta == MTR_TMK)) { /* gap or tape mark? */ + gap_alloc = gap_alloc + meta_size; /* allocate marker space */ + gap_needed = gap_needed - meta_size; /* reduce requirement */ + } + + else if (meta == MTR_FHGAP) { /* half gap? */ + uptr->pos = uptr->pos - meta_size / 2; /* backup to resync */ + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */ + gap_alloc = gap_alloc + meta_size / 2; /* allocate marker space */ + gap_needed = gap_needed - meta_size / 2; /* reduce requirement */ + } + + else if (uptr->pos + + MTR_L (meta) + meta_size > file_size) { /* rec len out of range? */ + gap_alloc = gap_alloc + gap_needed; /* presume overwritten tape */ + gap_needed = 0; /* allocate remainder */ + } + +/* Allocate a data record: + - Determine record size in bytes (including metadata) + - If record size - bytes needed < smallest allowed record size, + allocate entire record to gap, else allocate needed amount and + truncate data record to reflect remainder. +*/ + else { /* data record */ + sbc = MTR_L (meta); /* get record data length */ + rec_size = ((sbc + 1) & ~1) + meta_size * 2; /* overall size in bytes */ + + if (rec_size < gap_needed + min_rec_size) { /* rec too small? */ + uptr->pos = uptr->pos - meta_size + rec_size; /* position past record */ + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* move tape */ + gap_alloc = gap_alloc + rec_size; /* allocate record */ + gap_needed = gap_needed - rec_size; /* reduce requirement */ + } + + else { /* record size OK */ + uptr->pos = uptr->pos - meta_size + gap_needed; /* position to end of gap */ + new_len = MTR_F (meta) | (sbc - gap_needed); /* truncate to new len */ + st = sim_tape_wrdata (uptr, new_len); /* write new rec len */ + + if (st != MTSE_OK) { /* write OK? */ + uptr->pos = gap_pos; /* restore orig pos */ + return st; /* PNU was set by wrdata */ + } + + uptr->pos = uptr->pos + sbc - gap_needed; /* position to end of data */ + st = sim_tape_wrdata (uptr, new_len); /* write new rec len */ + + if (st != MTSE_OK) { /* write OK? */ + uptr->pos = gap_pos; /* restore orig pos */ + return st; /* PNU was set by wrdata */ + } + + gap_alloc = gap_alloc + gap_needed; /* allocate remainder */ + gap_needed = 0; + } + } + } +while (gap_needed > 0); + +uptr->pos = gap_pos; /* reposition to gap start */ + +if (gap_alloc & (meta_size - 1)) { /* gap size "odd?" */ + st = sim_tape_wrdata (uptr, MTR_FHGAP); /* write half gap marker */ + if (st != MTSE_OK) { /* write OK? */ + uptr->pos = gap_pos; /* restore orig pos */ + return st; /* PNU was set by wrdata */ + } + uptr->pos = uptr->pos - meta_size / 2; /* realign position */ + gap_alloc = gap_alloc - 2; /* decrease gap to write */ + } + +marker_count = gap_alloc / meta_size; /* count of gap markers */ + +do { + st = sim_tape_wrdata (uptr, MTR_GAP); /* write gap markers */ + if (st != MTSE_OK) { /* write OK? */ + uptr->pos = gap_pos; /* restore orig pos */ + return st; /* PNU was set by wrdata */ + } + } +while (--marker_count > 0); + +return MTSE_OK; +} + +t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, uint32 bpi, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_wrgap (uptr, gaplen, bpi); +AIO_CALL(TOP_RDRR, NULL, NULL, NULL, 0, 0, gaplen, bpi, NULL, callback); +return r; +} + +/* Space record forward + + Inputs: + uptr = pointer to tape unit + bc = pointer to size of record skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + read error unchanged, PNU set + end of file/medium unchanged, PNU set + tape mark updated + data record updated + data record error updated +*/ + +t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; +t_stat st; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecf(unit=%d)\n", sim_unit_index(uptr)); + +st = sim_tape_rdlntf (uptr, bc); /* get record length */ +*bc = MTR_L (*bc); +return st; +} + +t_stat sim_tape_sprecf_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_sprecf (uptr, bc); +AIO_CALL(TOP_SPRF, NULL, bc, NULL, 0, 0, 0, 0, NULL, callback); +return r; +} + +/* Space records forward + + Inputs: + uptr = pointer to tape unit + count = count of records to skip + skipped = pointer to number of records actually skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + read error unchanged, PNU set + end of file/medium unchanged, PNU set + tape mark updated + data record updated + data record error updated +*/ + +t_stat sim_tape_sprecsf (UNIT *uptr, uint32 count, uint32 *skipped) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; +t_stat st; +t_mtrlnt tbc; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecsf(unit=%d, count=%d)\n", sim_unit_index(uptr), count); + +*skipped = 0; +while (*skipped < count) { /* loopo */ + st = sim_tape_sprecf (uptr, &tbc); /* spc rec */ + if (st != MTSE_OK) + return st; + *skipped = *skipped + 1; /* # recs skipped */ + } +return MTSE_OK; +} + +t_stat sim_tape_sprecsf_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_sprecsf (uptr, count, skipped); +AIO_CALL(TOP_SRSF, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback); +return r; +} + +/* Space record reverse + + Inputs: + uptr = pointer to tape unit + bc = pointer to size of records skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + beginning of tape unchanged + read error unchanged + end of file unchanged + end of medium updated + tape mark updated + data record updated +*/ + +t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; +t_stat st; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecr(unit=%d)\n", sim_unit_index(uptr)); + +if (MT_TST_PNU (uptr)) { + MT_CLR_PNU (uptr); + *bc = 0; + return MTSE_OK; + } +st = sim_tape_rdlntr (uptr, bc); /* get record length */ +*bc = MTR_L (*bc); +return st; +} + +t_stat sim_tape_sprecr_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_sprecr (uptr, bc); +AIO_CALL(TOP_SPRR, NULL, bc, NULL, 0, 0, 0, 0, NULL, callback); +return r; +} + +/* Space records reverse + + Inputs: + uptr = pointer to tape unit + count = count of records to skip + skipped = pointer to number of records actually skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + beginning of tape unchanged + read error unchanged + end of file unchanged + end of medium updated + tape mark updated + data record updated +*/ + +t_stat sim_tape_sprecsr (UNIT *uptr, uint32 count, uint32 *skipped) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; +t_stat st; +t_mtrlnt tbc; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecsr(unit=%d, count=%d)\n", sim_unit_index(uptr), count); + +*skipped = 0; +while (*skipped < count) { /* loopo */ + st = sim_tape_sprecr (uptr, &tbc); /* spc rec rev */ + if (st != MTSE_OK) + return st; + *skipped = *skipped + 1; /* # recs skipped */ + } +return MTSE_OK; +} + +t_stat sim_tape_sprecsr_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_sprecsr (uptr, count, skipped); +AIO_CALL(TOP_SRSR, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback); +return r; +} + +/* Space files forward by record + + Inputs: + uptr = pointer to tape unit + count = count of files to skip + skipped = pointer to number of files actually skipped + recsskipped = pointer to number of records skipped + check_leot = flag to detect and stop skip between two successive tape marks + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + read error unchanged, PNU set + end of file/medium unchanged, PNU set + tape mark updated + data record updated + data record error updated +*/ + +t_stat sim_tape_spfilebyrecf (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; +t_stat st; +t_bool last_tapemark = FALSE; +uint32 filerecsskipped; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecf(unit=%d, count=%d, check_leot=%d)\n", sim_unit_index(uptr), count, check_leot); + +if (check_leot) { + t_mtrlnt rbc; + + st = sim_tape_rdlntr (uptr, &rbc); + last_tapemark = (MTSE_TMK == st); + if ((st == MTSE_OK) || (st == MTSE_TMK)) + sim_tape_rdlntf (uptr, &rbc); + } +*skipped = 0; +*recsskipped = 0; +while (*skipped < count) { /* loopo */ + while (1) { + st = sim_tape_sprecsf (uptr, 0x1ffffff, &filerecsskipped);/* spc recs */ + *recsskipped += filerecsskipped; + if (st != MTSE_OK) + break; + } + if (st == MTSE_TMK) { + *skipped = *skipped + 1; /* # files skipped */ + if (check_leot && (filerecsskipped == 0) && last_tapemark) { + uint32 filefileskipped; + sim_tape_spfilebyrecr (uptr, 1, &filefileskipped, &filerecsskipped); + *skipped = *skipped - 1; /* adjust # files skipped */ + return MTSE_LEOT; + } + last_tapemark = TRUE; + } + else + return st; + } +return MTSE_OK; +} + +t_stat sim_tape_spfilebyrecf_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_spfilebyrecf (uptr, count, skipped, recsskipped, check_leot); +AIO_CALL(TOP_SFRF, NULL, skipped, recsskipped, check_leot, count, 0, 0, NULL, callback); +return r; +} + +/* Space files forward + + Inputs: + uptr = pointer to tape unit + count = count of files to skip + skipped = pointer to number of files actually skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + read error unchanged, PNU set + end of file/medium unchanged, PNU set + tape mark updated + data record updated + data record error updated +*/ + +t_stat sim_tape_spfilef (UNIT *uptr, uint32 count, uint32 *skipped) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; +uint32 totalrecsskipped; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilef(unit=%d, count=%d)\n", sim_unit_index(uptr), count); + +return sim_tape_spfilebyrecf (uptr, count, skipped, &totalrecsskipped, FALSE); +} + +t_stat sim_tape_spfilef_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_spfilef (uptr, count, skipped); +AIO_CALL(TOP_SPFF, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback); +return r; +} + +/* Space files reverse by record + + Inputs: + uptr = pointer to tape unit + count = count of files to skip + skipped = pointer to number of files actually skipped + recsskipped = pointer to number of records skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + beginning of tape unchanged + read error unchanged + end of file unchanged + end of medium updated + tape mark updated + data record updated +*/ + +t_stat sim_tape_spfilebyrecr (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; +t_stat st; +uint32 filerecsskipped; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecr(unit=%d, count=%d)\n", sim_unit_index(uptr), count); + +*skipped = 0; +*recsskipped = 0; +while (*skipped < count) { /* loopo */ + while (1) { + st = sim_tape_sprecsr (uptr, 0x1ffffff, &filerecsskipped);/* spc recs rev */ + *recsskipped += filerecsskipped; + if (st != MTSE_OK) + break; + } + if (st == MTSE_TMK) + *skipped = *skipped + 1; /* # files skipped */ + else + return st; + } +return MTSE_OK; +} + +t_stat sim_tape_spfilebyrecr_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_spfilebyrecr (uptr, count, skipped, recsskipped); +AIO_CALL(TOP_SPFR, NULL, skipped, recsskipped, 0, count, 0, 0, NULL, callback); +return r; +} + +/* Space files reverse + + Inputs: + uptr = pointer to tape unit + count = count of files to skip + skipped = pointer to number of files actually skipped + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + beginning of tape unchanged + read error unchanged + end of file unchanged + end of medium updated + tape mark updated + data record updated +*/ + +t_stat sim_tape_spfiler (UNIT *uptr, uint32 count, uint32 *skipped) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; +uint32 totalrecsskipped; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfiler(unit=%d, count=%d)\n", sim_unit_index(uptr), count); + +return sim_tape_spfilebyrecr (uptr, count, skipped, &totalrecsskipped); +} + +t_stat sim_tape_spfiler_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_spfiler (uptr, count, skipped); +AIO_CALL(TOP_SPFR, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback); +return r; +} + +/* Rewind tape */ + +t_stat sim_tape_rewind (UNIT *uptr) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; + +if (uptr->flags & UNIT_ATT) + sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rewind(unit=%d)\n", sim_unit_index(uptr)); +uptr->pos = 0; +MT_CLR_PNU (uptr); +return MTSE_OK; +} + +t_stat sim_tape_rewind_a (UNIT *uptr, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_rewind (uptr); +AIO_CALL(TOP_RWND, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback); +return r; +} + +/* Position Tape */ + +t_stat sim_tape_position (UNIT *uptr, uint8 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped) +{ +tape_context* ctx = (tape_context*) uptr->tape_ctx; +t_stat r = MTSE_OK; + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_position(unit=%d, flags=0x%X, recs=%d, files=%d)\n", sim_unit_index(uptr), flags, recs, files); + +*recsskipped = *filesskipped = *objectsskipped = 0; +if (flags & MTPOS_M_REW) + r = sim_tape_rewind (uptr); +if (r != MTSE_OK) + return r; +if (flags & MTPOS_M_OBJ) { + uint32 objs = recs; + uint32 skipped; + uint32 objsremaining = objs; + + while (*objectsskipped < objs) { /* loopo */ + if (flags & MTPOS_M_REV) /* reverse? */ + r = sim_tape_sprecsr (uptr, objsremaining, &skipped); + else + r = sim_tape_sprecsf (uptr, objsremaining, &skipped); + objsremaining = objsremaining - (skipped + ((r == MTSE_TMK) ? 1 : 0)); + if ((r == MTSE_TMK) || (r == MTSE_OK)) + *objectsskipped = *objectsskipped + skipped + ((r == MTSE_TMK) ? 1 : 0); + else + return r; + } + r = MTSE_OK; + } +else { + uint32 fileskiprecs; + + if (flags & MTPOS_M_REV) /* reverse? */ + r = sim_tape_spfilebyrecr (uptr, files, filesskipped, &fileskiprecs); + else + r = sim_tape_spfilebyrecf (uptr, files, filesskipped, &fileskiprecs, (flags & MTPOS_M_DLE)); + if (r != MTSE_OK) + return r; + if (flags & MTPOS_M_REV) /* reverse? */ + r = sim_tape_sprecsr (uptr, recs, recsskipped); + else + r = sim_tape_sprecsf (uptr, recs, recsskipped); + if (r == MTSE_TMK) + *filesskipped = *filesskipped + 1; + *objectsskipped = fileskiprecs + *filesskipped + *recsskipped; + } +return r; +} + +t_stat sim_tape_position_a (UNIT *uptr, uint8 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped, TAPE_PCALLBACK callback) +{ +t_stat r = MTSE_OK; +AIO_CALLSETUP + r = sim_tape_position (uptr, flags, recs, recsskipped, files, filesskipped, objectsskipped); +AIO_CALL(TOP_POSN, NULL, recsskipped, filesskipped, 0, flags, recs, files, objectsskipped, callback); +return r; +} + +/* + * Reset tape: + * + * Caller must be either console thread or VCPU thread holding the lock for the device. + * It is assumed that all units on the device share the same lock. + */ +t_stat sim_tape_reset (DEVICE* dptr) +{ + RUN_SCOPE_RSCX; + UNIT* uptr; + t_bool any_async = FALSE; + uint32 k; + + if (rscx->thread_type == SIM_THREAD_TYPE_CONSOLE) + { + /* console thread can reset devices (all VCPUs are paused) */ + } + else if (rscx->thread_type == SIM_THREAD_TYPE_CPU) + { + for (k = 0; k < dptr->numunits; k++) + { + uptr = dptr->units[k]; + if (uptr->flags & UNIT_ATT) + { + tape_context* ctx = (tape_context*) uptr->tape_ctx; + if (ctx && ctx->asynch_io) + { + any_async = TRUE; + break; + } + } + } + + /* + * On a multiprocessor VAX with asynchronous IO enabled, asynchronous IO completion is handled + * by the primary processor that fetches units with AIO events from AIO event queue. Resetting + * controller requires flushing all entries pending in async queue. To do it on a secondary CPU, + * we'd have to send IPI to the primary and wait for the response. However primary may already + * being stopped by the console, so console code responsible for pausing VCPUs would have to + * check for pending flushing request and execute it (in fact it does, but we'd have to wait + * either for the primary VCPU response or AIO queue going empty). + * + * More seriously, we are holding device lock, so the primary may go deadlocked with us if we + * try to wait for it. On the other hand, we cannot release the lock (which may even have + * acquisition depth > 1), even temporarily. Also, primary can already be right at this point + * blocked inside uptr->lock(). + * + * It may be possible to design a scheme to handle this situation, however it appears that + * resetting controller by the secondary CPU is an exremely unlikely event in the first place. + * We may implement handling of this case if it ever becomes a problem. + * For now just abort the simulator if it is encountered. ToDo. + */ + if (any_async && !cpu_unit->is_primary_cpu()) + panic("Tape controller device reset attempted by a secondary CPU"); + } + else + { + panic("sim_tape_reset: invalid thread type"); + } + + dptr->a_reset_count++; + + for (k = 0; k < dptr->numunits; k++) + { + uptr = dptr->units[k]; + if ((uptr->flags & UNIT_ATT) && !(uptr->flags & UNIT_BUF) && uptr->fileref) + if (uptr->io_flush) + uptr->io_flush(uptr); + } + + if (any_async) + { + if (rscx->thread_type == SIM_THREAD_TYPE_CONSOLE) + sim_async_process_io_events_for_console(); + else + sim_async_process_io_events(RUN_PASS, NULL, TRUE); + } + + for (k = 0; k < dptr->numunits; k++) + { + uptr = dptr->units[k]; + sim_cancel(uptr); + if (uptr->flags & UNIT_ATTABLE) + MT_CLR_PNU (uptr); + } + + return SCPE_OK; +} + +/* Test for BOT */ + +t_bool sim_tape_bot (UNIT *uptr) +{ +uint32 f = MT_GET_FMT (uptr); + +return (uptr->pos <= fmts[f].bot)? TRUE: FALSE; +} + +/* Test for end of tape */ + +t_bool sim_tape_eot (UNIT *uptr) +{ +return (uptr->capac && (uptr->pos >= uptr->capac))? TRUE: FALSE; +} + +/* Test for write protect */ + +t_bool sim_tape_wrp (UNIT *uptr) +{ +return (uptr->flags & MTUF_WRP)? TRUE: FALSE; +} + +/* Process I/O error */ + +t_stat sim_tape_ioerr (UNIT *uptr) +{ +smp_perror ("Magtape library I/O error"); +clearerr (uptr->fileref); +return MTSE_IOERR; +} + +/* Set tape format */ + +t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 f; + +if (uptr == NULL) + return SCPE_IERR; +if (cptr == NULL) + return SCPE_ARG; +for (f = 0; f < MTUF_N_FMT; f++) { + if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) { + uptr->flags = (uptr->flags & ~MTUF_FMT) | + (f << MTUF_V_FMT) | fmts[f].uflags; + return SCPE_OK; + } + } +return SCPE_ARG; +} + +/* Show tape format */ + +t_stat sim_tape_show_fmt (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 f = MT_GET_FMT (uptr); + +if (fmts[f].name) + fprintf (st, "%s format", fmts[f].name); +else fprintf (st, "invalid format"); +return SCPE_OK; +} + +/* Map a TPC format tape image */ + +uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map) +{ +t_addr tpos; +t_tpclnt bc; +size_t i; +uint32 objc; + +if ((uptr == NULL) || (uptr->fileref == NULL)) + return 0; +for (objc = 0, tpos = 0;; ) { + sim_fseek (uptr->fileref, tpos, SEEK_SET); + i = sim_fread (&bc, sizeof (t_tpclnt), 1, uptr->fileref); + if (i == 0) + break; + if (map) + map[objc] = tpos; + objc++; + tpos = tpos + ((bc + 1) & ~1) + sizeof (t_tpclnt); + } +if (map) map[objc] = tpos; +return objc; +} + +/* Find the preceding record in a TPC file */ + +t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map) +{ +uint32 lo, hi, p; + + +if (map == NULL) + return 0; +lo = 0; +hi = uptr->hwmark - 1; +do { + p = (lo + hi) >> 1; + if (uptr->pos == map[p]) + return ((p == 0)? map[p]: map[p - 1]); + else if (uptr->pos < map[p]) + hi = p - 1; + else lo = p + 1; + } +while (lo <= hi); +return ((p == 0)? map[p]: map[p - 1]); +} + +/* Set tape capacity */ + +t_stat sim_tape_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +t_addr cap; +t_stat r; + +if ((cptr == NULL) || (*cptr == 0)) + return SCPE_ARG; +if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; +cap = (t_addr) get_uint (cptr, 10, sim_taddr_64? 2000000: 2000, &r); +if (r != SCPE_OK) + return SCPE_ARG; +uptr->capac = cap * ((t_addr) 1000000); +return SCPE_OK; +} + +/* Show tape capacity */ + +t_stat sim_tape_show_capac (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (uptr->capac) { + if (uptr->capac >= (t_addr) 1000000) + fprintf (st, "capacity=%dMB", (uint32) (uptr->capac / ((t_addr) 1000000))); + else if (uptr->capac >= (t_addr) 1000) + fprintf (st, "capacity=%dKB", (uint32) (uptr->capac / ((t_addr) 1000))); + else fprintf (st, "capacity=%dB", (uint32) uptr->capac); + } +else fprintf (st, "unlimited capacity"); +return SCPE_OK; +} diff --git a/src/sim_tape.h b/src/sim_tape.h new file mode 100644 index 0000000..f22ffd6 --- /dev/null +++ b/src/sim_tape.h @@ -0,0 +1,172 @@ +/* sim_tape.h: simulator tape support library definitions + + 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. + + 23-Jan-12 MP Added support for Logical EOT detection while positioning + 05-Feb-11 MP Add Asynch I/O support + 30-Aug-06 JDB Added erase gap support + 14-Feb-06 RMS Added variable tape capacity + 17-Dec-05 RMS Added write support for Paul Pierce 7b format + 02-May-05 RMS Added support for Paul Pierce 7b format +*/ + +#ifndef _SIM_TAPE_H_ +#define _SIM_TAPE_H_ 0 + +/* SIMH/E11 tape format */ + +typedef uint32 t_mtrlnt; /* magtape rec lnt */ + +#define MTR_TMK 0x00000000 /* tape mark */ +#define MTR_EOM 0xFFFFFFFF /* end of medium */ +#define MTR_GAP 0xFFFFFFFE /* primary gap */ +#define MTR_FHGAP 0xFFFEFFFF /* fwd half gap (overwrite) */ +#define MTR_RHGAP 0xFFFF0000 /* rev half gap (overwrite) */ +#define MTR_M_RHGAP (~0x000080FF) /* range mask for rev gap */ +#define MTR_MAXLEN 0x00FFFFFF /* max len is 24b */ +#define MTR_ERF 0x80000000 /* error flag */ +#define MTR_F(x) ((x) & MTR_ERF) /* record error flg */ +#define MTR_L(x) ((x) & ~MTR_ERF) /* record length */ + +/* TPC tape format */ + +typedef uint16 t_tpclnt; /* magtape rec lnt */ + +/* P7B tape format */ + +#define P7B_SOR 0x80 /* start of record */ +#define P7B_PAR 0x40 /* parity */ +#define P7B_DATA 0x3F /* data */ +#define P7B_DPAR (P7B_PAR|P7B_DATA) /* data and parity */ +#define P7B_EOF 0x0F /* eof character */ + +#define TPC_TMK 0x0000 /* tape mark */ + +/* Unit flags */ + +#define MTUF_V_PNU (UNIT_V_UF + 0) /* position not upd */ +#define MTUF_V_WLK (UNIT_V_UF + 1) /* write locked */ +#define MTUF_V_FMT (UNIT_V_UF + 2) /* tape file format */ +#define MTUF_W_FMT 3 /* 3b of formats */ +#define MTUF_N_FMT (1u << MTUF_W_FMT) /* number of formats */ +#define MTUF_M_FMT ((1u << MTUF_W_FMT) - 1) +#define MTUF_F_STD 0 /* SIMH format */ +#define MTUF_F_E11 1 /* E11 format */ +#define MTUF_F_TPC 2 /* TPC format */ +#define MTUF_F_P7B 3 /* P7B format */ +#define MUTF_F_TDF 4 /* TDF format */ +#define MTUF_V_UF (MTUF_V_FMT + MTUF_W_FMT) +#define MTUF_PNU (1u << MTUF_V_PNU) +#define MTUF_WLK (1u << MTUF_V_WLK) +#define MTUF_FMT (MTUF_M_FMT << MTUF_V_FMT) +#define MTUF_WRP (MTUF_WLK | UNIT_RO) + +#define MT_F_STD (MTUF_F_STD << MTUF_V_FMT) +#define MT_F_E11 (MTUF_F_E11 << MTUF_V_FMT) +#define MT_F_TPC (MTUF_F_TPC << MTUF_V_FMT) +#define MT_F_P7B (MTUF_F_P7B << MTUF_V_FMT) +#define MT_F_TDF (MTUF_F_TDF << MTUF_V_FMT) + +#define MT_SET_PNU(u) (u)->flags = (u)->flags | MTUF_PNU +#define MT_CLR_PNU(u) (u)->flags = (u)->flags & ~MTUF_PNU +#define MT_TST_PNU(u) ((u)->flags & MTUF_PNU) +#define MT_GET_FMT(u) (((u)->flags >> MTUF_V_FMT) & MTUF_M_FMT) + +/* sim_tape_position Position Flags */ +#define MTPOS_V_REW 3 +#define MTPOS_M_REW (1u << MTPOS_V_REW) /* Rewind First */ +#define MTPOS_V_REV 2 +#define MTPOS_M_REV (1u << MTPOS_V_REV) /* Reverse Direction */ +#define MTPOS_V_OBJ 1 +#define MTPOS_M_OBJ (1u << MTPOS_V_OBJ) /* Objects vs Records/Files */ +#define MTPOS_V_DLE 4 +#define MTPOS_M_DLE (1u << MTPOS_V_DLE) /* Detect LEOT */ +/* Return status codes */ + +#define MTSE_OK 0 /* no error */ +#define MTSE_TMK 1 /* tape mark */ +#define MTSE_UNATT 2 /* unattached */ +#define MTSE_IOERR 3 /* IO error */ +#define MTSE_INVRL 4 /* invalid rec lnt */ +#define MTSE_FMT 5 /* invalid format */ +#define MTSE_BOT 6 /* beginning of tape */ +#define MTSE_EOM 7 /* end of medium */ +#define MTSE_RECE 8 /* error in record */ +#define MTSE_WRP 9 /* write protected */ +#define MTSE_LEOT 10 /* Logical End Of Tape */ + +typedef void (*TAPE_PCALLBACK)(UNIT *unit, t_stat status); + +/* Prototypes */ + +t_stat sim_tape_attach_ex (UNIT *uptr, char *cptr, uint32 dbit); +t_stat sim_tape_attach (UNIT *uptr, char *cptr); +t_stat sim_tape_detach (UNIT *uptr); +t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max); +t_stat sim_tape_rdrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback); +t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max); +t_stat sim_tape_rdrecr_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback); +t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc); +t_stat sim_tape_wrrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt bc, TAPE_PCALLBACK callback); +t_stat sim_tape_wrtmk (UNIT *uptr); +t_stat sim_tape_wrtmk_a (UNIT *uptr, TAPE_PCALLBACK callback); +t_stat sim_tape_wreom (UNIT *uptr); +t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback); +t_stat sim_tape_wreomrw (UNIT *uptr); +t_stat sim_tape_wreomrw_a (UNIT *uptr, TAPE_PCALLBACK callback); +t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen, uint32 bpi); +t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, uint32 bpi, TAPE_PCALLBACK callback); +t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc); +t_stat sim_tape_sprecf_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback); +t_stat sim_tape_sprecsf (UNIT *uptr, uint32 count, uint32 *skipped); +t_stat sim_tape_sprecsf_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback); +t_stat sim_tape_spfilef (UNIT *uptr, uint32 count, uint32 *skipped); +t_stat sim_tape_spfilef_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback); +t_stat sim_tape_spfilebyrecf (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot); +t_stat sim_tape_spfilebyrecf_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot, TAPE_PCALLBACK callback); +t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc); +t_stat sim_tape_sprecr_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback); +t_stat sim_tape_sprecsr (UNIT *uptr, uint32 count, uint32 *skipped); +t_stat sim_tape_sprecsr_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback); +t_stat sim_tape_spfiler (UNIT *uptr, uint32 count, uint32 *skipped); +t_stat sim_tape_spfiler_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback); +t_stat sim_tape_spfilebyrecr (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped); +t_stat sim_tape_spfilebyrecr_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, TAPE_PCALLBACK callback); +t_stat sim_tape_rewind (UNIT *uptr); +t_stat sim_tape_rewind_a (UNIT *uptr, TAPE_PCALLBACK callback); +t_stat sim_tape_position (UNIT *uptr, uint8 flags, uint32 recs, uint32 *recskipped, uint32 files, uint32 *fileskipped, uint32 *objectsskipped); +t_stat sim_tape_position_a (UNIT *uptr, uint8 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped, TAPE_PCALLBACK callback); +t_stat sim_tape_reset (DEVICE *dptr); +t_bool sim_tape_bot (UNIT *uptr); +t_bool sim_tape_wrp (UNIT *uptr); +t_bool sim_tape_eot (UNIT *uptr); +t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sim_tape_show_fmt (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat sim_tape_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sim_tape_show_capac (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat sim_tape_set_asynch (UNIT *uptr, int latency); +t_stat sim_tape_clr_asynch (UNIT *uptr); +void sim_tape_data_trace (UNIT *uptr, const uint8 *data, size_t len, const char* txt, int detail, uint32 reason); + +#endif diff --git a/src/sim_threads.cpp b/src/sim_threads.cpp new file mode 100644 index 0000000..3cab64b --- /dev/null +++ b/src/sim_threads.cpp @@ -0,0 +1,5128 @@ +/* + * Threading support, other than memory barrier functions. + * + * Cross-platform code at the top, system-specific below. + */ + +/* ============================================ Cross-platofrm part ============================================ */ + +#define SIM_THREADS_H_FULL_INCLUDE +#include "sim_defs.h" + +#if defined(__linux) || defined(__APPLE__) +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#if defined(__linux) +# include +#endif + +#if defined(__APPLE__) +# include +#endif + +#include + +void smp_mb_init(); + +#define InterlockedOpLock_NCS 64 +static smp_lock_impl InterlockedOpLock_CS[InterlockedOpLock_NCS]; +int smp_ncpus = 0; +int smp_nsmt_per_core = 0; +t_bool smp_smt_factor_set = FALSE; +double smp_smt_factor = 1.0; +uint32 smp_spinwait_min_us = 20; +static t_bool smp_init_info(); +static int smp_get_cpu_count(); +static int smp_get_smt_per_core(); + +#if defined(__linux) || defined(__APPLE__) +static void smp_set_thread_priority_init(); +#endif + +#if defined(__linux) +static t_bool check_nptl(); +#endif + +#if SMP_NATIVE_INTERLOCKED +static void smp_native_self_test(); +#endif + +#define CHECK(cond) do { if (! (cond)) goto cleanup; } while (0) + + +// Hash function "jenkin32" +// See http://baagoe.org/en/wiki/Avalanche_on_integer_hash_functions +// Good hash function provides close to equal influence of every input bit to each output bit +SIM_INLINE static int32 hash32(int32 key) +{ + key += key << 12; + key ^= (uint32) key >> 22; + key += key << 4; + key ^= (uint32) key >> 9; + key += key << 10; + key ^= (uint32) key >> 2; + key += key << 7; + key ^= (uint32) key >> 12; + return key; +} + +// Hash function "blend" +// See http://baagoe.org/en/wiki/Avalanche_on_integer_hash_functions +// SIM_INLINE static int32 hash32(int32 key) +// { +// static const uint32 table[] = +// { +// 2945918480, 3438006206, 2844659691, 2655754164, 2149826949, 1264591814, 2178756406, 1108394547, +// 1403771681, 4048350178, 1677843536, 2048500138, 1297136353, 2024434236, 2509406493, 316199015, +// 2393194003, 1874105163, 3983215261, 3439390297, 1584819634, 1478947895, 3540658215, 883689796, +// 719502645, 4247659682, 822370394, 2772803220, 757936898, 1719845855, 3502055134, 656881580, +// 3374324640, 3124330056, 347211968, 1599773202, 496963709, 3110458943, 523394089, 2497946217, +// 3335010626, 3404214538, 1888883598, 2976003771, 3939254910, 3003897015, 2182581168, 1671343576, +// 2348764985, 4084950097, 4178729868, 3317058044, 2046252615, 422121877, 3563333935, 1337552614, +// 1104797690, 2548923623, 451955747, 1164810435, 2927935617, 2554484595, 2619888961, 2857541770, +// 1795365133, 2811945112, 1649331294, 1450688335, 671939448, 902285340, 3681429487, 2447096243, +// 483718424, 2421517520, 3091631718, 605345797, 1410878973, 220137172, 865307884, 1182838007, +// 253388584, 3277440191, 3371578720, 1916241651, 1728483986, 989513374, 456916081, 92768634, +// 3206340126, 4202615644, 1531539748, 396485535, 4026082661, 1231485340, 2199300060, 4157798005, +// 3777044224, 2287933036, 3595694247, 1551094359, 543261433, 1500897547, 66453467, 2872504971, +// 300503158, 1128807093, 1014568889, 1947989870, 3587123916, 1980336113, 1762764873, 2133813317, +// 3411418623, 820424661, 409564493, 2275920411, 3347611752, 936714661, 1064327833, 2468481999, +// 1631358254, 2007544357, 4005061284, 912950047, 2910109411, 3474310600, 1899265939, 1973156029, +// 2318969741, 2892813695, 2713929178, 3242518237, 16594153, 171984074, 591497231, 954543444, +// 1354554019, 3235356530, 1476151471, 842338040, 118293459, 2224653319, 101169288, 2759980018, +// 4116230445, 2992490774, 1324564348, 3868192635, 3517975959, 1818706027, 1432941044, 1213399497, +// 2380031540, 2822674933, 1362982528, 3182785362, 1082775942, 3191502910, 2733095942, 1622141137, +// 1518776481, 3745984642, 2104654362, 3774118182, 3265330586, 1700027821, 4073411392, 3085708053, +// 3918811649, 726486672, 2965415373, 384468241, 1840089642, 3907032068, 1387173604, 68103926, +// 3034885741, 1568156840, 2359606024, 1931753687, 1144194828, 3833410097, 4035832398, 2413482260, +// 187017860, 3660353746, 3718880698, 149932000, 2670783638, 2310246279, 798536622, 3887744889, +// 3961328305, 3951836255, 2613399833, 157182278, 326923407, 3536393481, 2097043788, 2589739568, +// 21642499, 4287699182, 2748556443, 3653154024, 2073938870, 3048969709, 366991318, 968877428, +// 3731838026, 2532488498, 2244928169, 239328683, 1204032763, 3632622590, 44459504, 4272061216, +// 513128292, 1791826543, 995878081, 4213921898, 653353816, 2572115655, 282075229, 2257250062, +// 1855746134, 1277086999, 688409481, 212966434, 1242468837, 1745001301, 3302366320, 2119695939, +// 4176389798, 1056449218, 3065488184, 3808772962, 3692233925, 2793013347, 4139938488, 577855961, +// 3458902478, 1032048172, 3616243041, 3141528977, 3855022780, 780134715, 752775146, 568410459, +// 631440003, 2687590202, 2458928836, 3170567485, 4241558647, 2646717995, 3798169427, 4106894283 +// }; +// key = 3783973111 * key; +// key ^= table[(uint32) key >> 24]; +// return key ^ table[key & 255]; +// } + +// Hash function by Thomas Wang +// http://www.concentric.net/~ttwang/tech/inthash.htm +// SIM_INLINE static int32 hash32(int32 key) +// { +// key = ~key + (key << 15); // key = (key << 15) - key - 1; +// key = key ^ ((uint32) key >> 12); +// key = key + (key << 2); +// key = key ^ ((uint32) key >> 4); +// key = key * 2057; // key = (key + (key << 3)) + (key << 11); +// key = key ^ ((uint32) key >> 16); +// return key; +// } + +/* + * InterlockedOpLock_SpinCount logics: + * + * When USE_SIMH_SMP_LOCK is not defined on Windows CriticalSection is used instead, spin-wait loop is 5 instructions, + * one of which is PAUSE: + * + * loop: pause + * cmp dword ptr own_count[edx], -1 + * je try_acquire + * dec dword ptr [esp] + * jne loop + * + * with little refernces to memory and a delay introduced by pause, on some processors. + * + * When USE_SIMH_SMP_LOCK is used, spin-wait loop compiles to 11 instructions (both by MSVC and GCC) one of which is PAUSE. + * + * Latency of PAUSE instruction can range widely, from virtualy no latency (NOP) on old processors to a latency equivalent + * to roughly about 50 regular instructions or so on newer processors. + * + * By comparison, a locked core of single VAX interlocked instructuion such as INSQHI can translate into few hundred + * instructions on the host system, with many references to memory for PTE, TLB, local variables etc. + * + * This suggests a rather high value for InterlockedOpLock_SpinCount. + * + * ToDo: Consider implementing self-learning auto-adaptive spin-wait critical section that + * + * 1) takes certain number of initial cycles to sample, limitng these initial waits by rather high maximum; + * + * 2) after the sampling throws away outliers and sets spin-wait cycle count based on resulting sampled vaues] + * (e.g. average multipled by 2); + * + * 3) and then adjusts spinwait dynamically based on success/failure, based by growing by a small margin + * on failure and shrinking on success, within max and min limits. + * + * More specfically, possible heurstics of general-purpose self-learnig auto-adaptable lock can be: + * + * - smp_lock can collect statistics over certain number of lock cycles, similar to what smp_lock + * does now for PERF command, but delta-statistics, rather than continuously running one. For example, + * statistics can be kept over recent 1000 and/or 10000 lock cycles. + * + * - Based on acquired statistics, adjust spin count to target very low (but non-zero) percentage of lock + * acquisitions resulting in going to OS-level blocking wait, such as 0.1% or 0.01%. + * + * - If recent blocking wait rate was too low (well below the target), then tighten spin count. + * If it was too high (well above the target), then loosen spin count. + * + * - Tighten slow, loosen fast. + * Tighten spin count by a small amount, gradually, based on delta-statistics taken over many acquisitions. + * However if a contention burst comes in and blocking wait rate goes high, detect it quickly (based on smaller + * sampling interval, i.e. number of recent acquisitions) and loosen spin count quickly by larger amount. + * + * - If lock contention has bursty pattern, detect it and cope with it. After having to back the tightening off + * by large amount for several times, give up on tightening and allow spin count to stay large. + * + * - Never increase spin count beyond ~ 1/2 of the cost of rescheduling wait. + * + * - Never drop spin count too low. Account for variations in cache/main memory access times etc. + * Specifically, it does not make sense to set spin count below 10. + * Also it does not make sense to set spin count below 1-5% of rescheduling cost. + * + */ + +static inline uint32 ROUNDUP(uint32 n, uint32 r) +{ + return ((n + r - 1) / r) * r; +} + +/* give it a plenty of margin */ +#define InterlockedOpLock_SpinCount 4000 + +#if defined(_WIN32) + static DWORD run_scope_key = -1; +#elif (defined(__linux) || defined(__APPLE__)) && (defined(__x86_32__) || defined(__x86_64__)) + static pthread_key_t run_scope_key; +#endif + +/* + * Routines to verify memory alignment of variables used for interlocked and atomic + * operations. If these variables are misplaced somehow, e.g. due to a bug in the + * compiler or linker, and their misplacement goes unnoticed, it could be a silent killer. + */ + +t_bool check_aligned(void* p, uint32 alignment, t_bool dothrow) +{ + if (0 == ((t_addr_val)(alignment - 1) & (t_addr_val) p)) + { + return TRUE; + } + else if (dothrow) + { + panic("Internal error: Data alignment check failed"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} + +t_bool smp_check_aligned(const smp_interlocked_uint32* p, t_bool dothrow) +{ + if (0 == (3 & (t_addr_val) p)) + { + return TRUE; + } + else if (dothrow) + { + panic("Internal error: Data alignment check failed"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} + +t_bool smp_check_aligned(const smp_interlocked_int32* p, t_bool dothrow) +{ + if (0 == (3 & (t_addr_val) p)) + { + return TRUE; + } + else if (dothrow) + { + panic("Internal error: Data alignment check failed"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} + +#if 0 +// from the compiler's viewpoint, atomic_int32 is the same as smp_interlocked_int32 +t_bool smp_check_aligned(const atomic_int32* p, t_bool dothrow) +{ + if (0 == (3 & (t_addr_val) p)) + { + return TRUE; + } + else if (dothrow) + { + panic("Internal error: Data alignment check failed"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} +#endif + +t_bool smp_check_aligned(const smp_interlocked_uint32_var* p, t_bool dothrow) +{ + if ((SMP_MAXCACHELINESIZE - 1) & (t_addr_val) p) + { + if (dothrow) + { + panic("Internal error: Data alignment check failed"); + never_returns_bool_t; + } + else + { + return FALSE; + } + } + + return smp_check_aligned(smp_var_p(p), dothrow); +} + +t_bool smp_check_aligned(const smp_interlocked_int32_var* p, t_bool dothrow) +{ + if ((SMP_MAXCACHELINESIZE - 1) & (t_addr_val) p) + { + if (dothrow) + { + panic("Internal error: Data alignment check failed"); + never_returns_bool_t; + } + else + { + return FALSE; + } + } + + return smp_check_aligned(smp_var_p(p), dothrow); +} + +t_bool smp_check_aligned(const atomic_int32_var* p, t_bool dothrow) +{ + if ((SMP_MAXCACHELINESIZE - 1) & (t_addr_val) p) + { + if (dothrow) + { + panic("Internal error: Data alignment check failed"); + never_returns_bool_t; + } + else + { + return FALSE; + } + } + + return smp_check_aligned(atomic_var_p(p), dothrow); +} + +t_bool smp_check_aligned(const atomic_uint32_var* p, t_bool dothrow) +{ + if ((SMP_MAXCACHELINESIZE - 1) & (t_addr_val) p) + { + if (dothrow) + { + panic("Internal error: Data alignment check failed"); + never_returns_bool_t; + } + else + { + return FALSE; + } + } + + return smp_check_aligned(atomic_var_p(p), dothrow); +} + +#if defined (__x86_64__) +/* not truly cross-platform, but for any processor with 64-bit interlocked operations */ +t_bool smp_check_aligned(const smp_interlocked_uint64* p, t_bool dothrow) +{ + if (0 == (7 & (t_addr_val) p)) + { + return TRUE; + } + else if (dothrow) + { + panic("Internal error: Data alignment check failed"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} + +t_bool smp_check_aligned(const smp_interlocked_uint64_var* p, t_bool dothrow) +{ + if ((SMP_MAXCACHELINESIZE - 1) & (t_addr_val) p) + { + if (dothrow) + { + panic("Internal error: Data alignment check failed"); + never_returns_bool_t; + } + else + { + return FALSE; + } + } + + return smp_check_aligned(smp_var_p(p), dothrow); +} +#endif + +smp_semaphore* smp_semaphore::create(int initial_open_count, t_bool dothrow) +{ + smp_semaphore_impl* sem = new smp_semaphore_impl(); + if (! sem->init(initial_open_count, dothrow)) + { + delete sem; + sem = NULL; + } + return sem; +} + +smp_barrier* smp_barrier::create(int initial_count, t_bool dothrow) +{ + smp_barrier_impl* bar = new smp_barrier_impl(); + if (! bar->init(initial_count, dothrow)) + { + delete bar; + bar = NULL; + } + return bar; +} + +smp_mutex* smp_mutex::create(t_bool dothrow) +{ + smp_mutex_impl* mtx = new smp_mutex_impl(); + if (! mtx->init(dothrow)) + { + delete mtx; + mtx = NULL; + } + return mtx; +} + +smp_condvar* smp_condvar::create(t_bool dothrow) +{ + smp_condvar_impl* cv = new smp_condvar_impl(); + if (! cv->init(dothrow)) + { + delete cv; + cv = NULL; + } + return cv; +} + +smp_event* smp_event::create(t_bool dothrow) +{ + smp_event_impl* ev = new smp_event_impl(); + if (! ev->init(dothrow)) + { + delete ev; + ev = NULL; + } + return ev; +} + +#if SMP_NATIVE_INTERLOCKED == 0 +int32 smp_native_adawi(volatile void* memory, int32 pa_sum, uint16 addend) +{ + panic("Native support for interlocked instructions is unavailable"); + return 0; +} + +t_bool smp_native_bb(volatile void* memory, int32 pa, int32 bit, t_bool set) +{ + panic("Native support for interlocked instructions is unavailable"); + return 0; +} +#endif + +/********************************** init_threads **********************************/ + +#if defined(__x86_32__) || defined(__x86_64__) +extern t_bool have_x86_xaddl; +extern t_bool have_x86_cmpxchgl; +extern t_bool have_pentium; +#endif + +void init_threads_core() +{ +#if defined(_WIN32) + // assumptions by interlocked primitives + if (sizeof(LONG) != sizeof(uint32)) + panic("Broken assumption: LONG != uint32"); + if (sizeof(long) != sizeof(smp_interlocked_uint32)) + panic("Broken assumption: sizeof(long) != sizeof(smp_interlocked_uint32)"); +#endif + +#if defined(__linux) + if (! check_nptl()) + panic("System is not configured to use NPTL (New Posix Threads) library"); +#endif + + if (! smp_init_info()) + panic("Unable to acquire information about processors on the host system"); + smp_ncpus = smp_get_cpu_count(); + smp_nsmt_per_core = smp_get_smt_per_core(); + + smp_mb_init(); + +#if defined(__GNUC__) && (defined(__x86_32__) || defined(__x86_64__)) + // many gcc __sync_* builtins require x86 instructions not available before i486 + if (!have_x86_xaddl || !have_x86_cmpxchgl) + panic("Requires i486 or later processor"); +#elif defined(__WIN32__) + // WIN32 code uses CMPXCHG + if (! have_x86_cmpxchgl) + panic("Requires i486 or later processor"); +#endif + +#if defined(_WIN32) + if ((run_scope_key = TlsAlloc()) == 0xFFFFFFFF) + panic("Unable to initialize thread-local storage"); +#elif defined(__linux) || defined(__APPLE__) + if (pthread_key_create(& run_scope_key, NULL)) + panic("Unable to initialize thread-local storage"); +#else +# error Unimplemented +#endif + +#if defined(__GNUC__) && defined(__x86_32__) && !defined(__tune_i386__) && !defined(__tune_i486__) + // If the code was not compiled for i386/i486, and current processor is not Pentium, exit + if (! have_pentium) + panic("Requires Pentium or later processor"); +#endif + +#if defined(__linux) || defined(__APPLE__) + smp_set_thread_priority_init(); +#endif + +#if SMP_NATIVE_INTERLOCKED + // self-test primitives used for native-mode implementation of VAX interlocked instructions + smp_native_self_test(); +#endif + + run_scope_context::set_current(NULL); +} + +void init_threads_ext() +{ + if (smp_ncpus > 1) + smp_lock::calibrate(); + + for (int k = 0; k < InterlockedOpLock_NCS; k++) + { + /* initialize the lock */ + InterlockedOpLock_CS[k].init(InterlockedOpLock_SpinCount); + + /* register the lock as managed performance object */ + char lock_name[50]; + sprintf(lock_name, "interlock_%d", k); + perf_register_object(dupstr(lock_name), & InterlockedOpLock_CS[k]); + } + + smp_wmb(); +} + +/********************************** InterlockedOpLock **********************************/ + +InterlockedOpLock::InterlockedOpLock(RUN_DECL, uint32 flags) +{ + lock_nesting_count = 0; + lock_index = -1; + wmb = FALSE; + this->cpu_unit = cpu_unit; + sv_priority_stored = FALSE; + this->flags = flags; + entered_temp_ilk = FALSE; + onConstructor(); +} + +InterlockedOpLock::~InterlockedOpLock() +{ + onDestroy(FALSE); +} + +void InterlockedOpLock::onDestroy(t_bool unregistered) +{ + if (onDestructor(unregistered)) + { + if (lock_nesting_count > 0) + { + lock_nesting_count = 1; + unlock(); + } + } +} + +void InterlockedOpLock::virt_lock(int32 vaddr, int32 acc) sim_try_volatile +{ + /* + * We rely here on design for device handlers (XQ, RQ and TQ) that access shared areas, + * such as UQSSP COMM area and XQ BDL, that performs such access only within the context + * of VCPU threads and not IOP threads. Otherwise MB would have been always required + * both here and in unlock(), regardless of whether sim_mp_active is TRUE or not. + */ + if (unlikely(weak_read(sim_mp_active) == FALSE)) + { + // smp_mb(); + return; + } + + int32 pa_ilock = TestMark (RUN_PASS, vaddr, WA, NULL); + phys_lock(pa_ilock); +} + +void InterlockedOpLock::phys_lock(int32 addr) sim_try_volatile +{ + /* + * We rely here on design for device handlers (XQ, RQ and TQ) that access shared areas, + * such as UQSSP COMM area and XQ BDL, that performs such access only within the context + * of VCPU threads and not IOP threads. Otherwise MB would have been always required + * both here and in unlock(), regardless of whether sim_mp_active is TRUE or not. + */ + if (unlikely(weak_read(sim_mp_active) == FALSE)) + { + // smp_mb(); + return; + } + + if (lock_nesting_count++ == 0) + { + cpu_begin_interlocked(RUN_PASS, & sv_priority, & sv_priority_stored); + + if (flags & IOP_ILK) + entered_temp_ilk = syncw_ifenter_ilk(RUN_PASS); + + // interlock on base longword of the address + addr &= ~0x3; + + // compute hash function on addr and select critical section from the array + lock_index = hash32((uint32) addr >> 2) & (InterlockedOpLock_NCS - 1); + + InterlockedOpLock_CS[lock_index].lock(); + + // do not need to execute smp_mb because "lock" above executes full memory barrier + // smp_mb(); + } +} + +void InterlockedOpLock::prio_lock() sim_try_volatile +{ + if (unlikely(weak_read(sim_mp_active) == FALSE)) + return; + + if (lock_nesting_count++ == 0) + { + cpu_begin_interlocked(RUN_PASS, & sv_priority, & sv_priority_stored); + + if (flags & IOP_ILK) + entered_temp_ilk = syncw_ifenter_ilk(RUN_PASS); + } +} + +void InterlockedOpLock::qxi_busy() sim_try_volatile +{ + /* + * INSQHI, INSQTI, REMQHI or REMQTI tried to acquire secondary interlock, + * but queue header was busy: stay in SYNCW-ILK is IPL >= RESCHED + */ + + if (entered_temp_ilk && PSL_GETIPL(PSL) >= syncw.ipl_resched) + entered_temp_ilk = FALSE; +} + +void InterlockedOpLock::unlock() sim_try_volatile +{ + if (lock_nesting_count && 0 == --lock_nesting_count) + { + /* + * do not need to execute smp_mb because the "unlock" below executes full memory barrier for virt_lock + * and phys_lock cases; whereas for prio_lock case a barrier is not required since the code calling prio_lock + * (native-mode implementations of interlocked instructions) takes care about issuing barriers itself + */ + // if (wmb) smp_mb(); + + if (entered_temp_ilk) + { + syncw_leave_ilk(RUN_PASS); + entered_temp_ilk = FALSE; + } + + if (lock_index != -1) + { + InterlockedOpLock_CS[lock_index].unlock(); + lock_index = -1; + // wmb = FALSE; + } + + cpu_end_interlocked(RUN_PASS, sv_priority, sv_priority_stored); + sv_priority_stored = FALSE; + } +} + +/************************** smp_lock **************************/ + +smp_lock* smp_lock::create(uint32 cycles, t_bool dothrow) +{ + smp_lock* cs = new smp_lock_impl(); + if (! cs->init(cycles, dothrow)) + { + delete cs; + cs = NULL; + } + return cs; +} + +smp_lock* smp_lock::create(uint32 us, uint32 min_cycles, uint32 max_cycles, t_bool dothrow) +{ + smp_lock* cs = new smp_lock_impl(); + if (! cs->init(us, min_cycles, max_cycles, dothrow)) + { + delete cs; + cs = NULL; + } + return cs; +} + +/********************** InterruptRegister **********************/ + +InterruptRegister::InterruptRegister() +{ + lo_ipl = 0; + hi_ipl = 0; + devs_per_ipl = NULL; + smp_var(changed) = TRUE; + irqs = NULL; + local_irqs = NULL; +} + +InterruptRegister::~InterruptRegister() +{ + if (irqs) + free_aligned((void*) irqs); + if (local_irqs) + free(local_irqs); +} + +void InterruptRegister::init(uint32 lo_ipl, uint32 hi_ipl, const uint32* devs_per_ipl) +{ + check_aligned(this, SMP_MAXCACHELINESIZE); + smp_check_aligned(& changed); + if (lo_ipl > hi_ipl) + panic("Unable to initialize InterruptRegister: invalid parameters"); + for (uint32 k = 0; k < hi_ipl - lo_ipl + 1; k++) + { + if (devs_per_ipl[k] > 32) + panic("Unable to initialize InterruptRegister: invalid parameters"); + } + if (irqs) + free_aligned((void*) irqs); + if (local_irqs) + free(local_irqs); + + uint32 alloc_size = (hi_ipl - lo_ipl + 1) * sizeof(uint32); + if (NULL == (local_irqs = (uint32*) malloc(alloc_size))) + panic("Unable to initialize InterruptRegister: memory allocation failed"); + + /* + * It may be possible to allocate a mask for each interrupt level in its own cache line container, + * but is probably not worth it + */ + alloc_size = (hi_ipl - lo_ipl + 1) * sizeof(smp_interlocked_uint32); + alloc_size = ROUNDUP(alloc_size, SMP_MAXCACHELINESIZE); + if (NULL == (irqs = (smp_interlocked_uint32*) malloc_aligned(alloc_size, SMP_MAXCACHELINESIZE))) + panic("Unable to initialize InterruptRegister: aligned memory allocation failed"); + + this->lo_ipl = lo_ipl; + this->hi_ipl = hi_ipl; + this->devs_per_ipl = devs_per_ipl; + + reset(); +} + +/* reset all pending interrupts */ +void InterruptRegister::reset() +{ + for (uint32 ipl = lo_ipl; ipl <= hi_ipl; ipl++) + { + irqs[ipl - lo_ipl] = 0; + local_irqs[ipl - lo_ipl] = 0; + } + smp_var(changed) = TRUE; +} + +/* raise pending interrupt */ +t_bool InterruptRegister::set_int(uint32 ipl, uint32 dev, t_bool toself) +{ + if (! toself) smp_pre_interlocked_wmb(); + + t_bool res = smp_test_set_bit(& irqs[ipl - lo_ipl], dev); + +#if !defined(__x86_32__) && !defined(__x86_64__) + if (! toself) smp_post_interlocked_wmb(); +#endif + + smp_interlocked_cas_done_var(& changed, 0, 1); // can be just xchg(1) as well + + if (toself) + local_irqs[ipl - lo_ipl] |= (1 << dev); + + return res; +} + +/* clear pending interrupt */ +t_bool InterruptRegister::clear_int(uint32 ipl, uint32 dev, t_bool toself) +{ + if (! toself) smp_pre_interlocked_wmb(); + + t_bool res = smp_test_clear_bit(& irqs[ipl - lo_ipl], dev); + +#if !defined(__x86_32__) && !defined(__x86_64__) + if (! toself) smp_post_interlocked_wmb(); +#endif + + smp_interlocked_cas_done_var(& changed, 0, 1); // can be just xchg(1) as well + + if (toself) + local_irqs[ipl - lo_ipl] &= ~(1 << dev); + + return res; +} + +/* check if interrupt is marked pending in local buffer */ +t_bool InterruptRegister::is_local_int(uint32 ipl, uint32 dev) +{ + return 0 != (local_irqs[ipl - lo_ipl] & (1 << dev)); +} + +/* dismiss interrupt on local processor */ +void InterruptRegister::dismiss_int(RUN_DECL, uint32 ipl, uint32 dev) +{ + if (interrupt_reeval_syncw_sys[ipl - lo_ipl] & (1 << dev)) + syncw_enter_sys(RUN_PASS); + smp_test_clear_bit(& irqs[ipl - lo_ipl], dev); + local_irqs[ipl - lo_ipl] &= ~(1 << dev); +} + +/* copy irqs to local_irqs, usually will be executed after memory barrier */ +void InterruptRegister::copy_irqs_to_local() +{ + for (uint32 ipl = lo_ipl; ipl <= hi_ipl; ipl++) + { + local_irqs[ipl - lo_ipl] = weak_read(irqs[ipl - lo_ipl]); + } +} + +/* find highest irql pending in local_irqs */ +int32 InterruptRegister::highest_local_irql() +{ + for (uint32 ipl = hi_ipl; ipl >= lo_ipl; ipl--) + { + if (local_irqs[ipl - lo_ipl]) + return (int32) ipl; + } + return 0; +} + +void InterruptRegister::query_local_clk_ipi(t_bool* is_active_clk_interrupt, t_bool* is_active_ipi_interrupt) +{ + *is_active_clk_interrupt = (local_irqs[IPL_CLK] & INT_CLK) ? TRUE : FALSE; + *is_active_ipi_interrupt = (local_irqs[IPL_IPINTR] & INT_IPINTR) ? TRUE : FALSE; +} + +/* examine if device interrupts is pending, called from console with all CPUs paused and memory barriers already executed */ +t_bool InterruptRegister::examine_int(uint32 ipl, uint32 dev) +{ + return 0 != (weak_read(irqs[ipl - lo_ipl]) & (1 << dev)); +} + +/* + * Check if there is any interrupt pending at exact "ipl" level using previously read + * local_irqs. + * + * If yes, return requesting device index to "*int_dev" and return TRUE as method value, + * and clear the request bit both in irqs and local_irqs. If the bit in irqs had been + * already cleared, consider interrupt request as cancelled and disregard it. + * + * If there are no interrupts pending at "ipl", method returns FALSE. + * + * Must be called by local processor only. + */ +t_bool InterruptRegister::check_int_atipl_clr(RUN_DECL, uint32 ipl, uint32* int_dev) +{ + if (ipl < lo_ipl || ipl > hi_ipl) + return FALSE; + + uint32 dev; + + smp_interlocked_uint32* pintr = & irqs[ipl - lo_ipl]; + uint32* plocal = & local_irqs[ipl - lo_ipl]; + uint32 local = *plocal; + + for (dev = 0; dev < devs_per_ipl[ipl - lo_ipl]; dev++) + { + if (local & (1 << dev)) + { + if (interrupt_reeval_syncw_sys[ipl - lo_ipl] & (1 << dev)) + syncw_enter_sys(RUN_PASS); + *plocal &= ~(1 << dev); + if (smp_test_clear_bit(pintr, dev)) + { + *int_dev = dev; + return TRUE; + } + } + } + + return FALSE; +} + +/* check if any of syncw_sys relevant interrupts are pending */ +#if defined(VM_VAX_MP) && (IPL_CLK == IPL_SYNCLK) && (IPL_CLK == IPL_IPINTR) +SIM_INLINE t_bool InterruptRegister::query_syncw_sys() +{ + return 0 != (interrupt_reeval_syncw_sys[IPL_CLK] & weak_read(irqs[IPL_CLK])); +} +#else +t_bool InterruptRegister::query_syncw_sys() +{ + for (uint32 ipl = lo_ipl; ipl <= hi_ipl; ipl++) + { + if (interrupt_reeval_syncw_sys[ipl - lo_ipl] & weak_read(irqs[ipl - lo_ipl])) + return TRUE; + } + + return FALSE; +} +#endif + +#if 0 +/* + * This routine is not currently used. + * + * Examine if interrupts at level "ipl" are pending, + * intended to be called from console with all CPUs paused and memory barriers already executed. + */ +t_bool InterruptRegister::examine_int(uint32 ipl) +{ + return 0 != weak_read(irqs[ipl - lo_ipl]); +} + +/* + * This routine is not currently used. + * + * Check if there is any interrupt pending that can be delivered at "ipl" level. + * Clear "changed" flag, execute memory barrier, but do not reset the pending interrupt. + */ +t_bool InterruptRegister::check_int(uint32 ipl, uint32* int_ipl) +{ + smp_interlocked_cas_var_done(& changed, 1, 0); // can be just xchg(0) as well + smp_post_interlocked_rmb(); + + if (ipl >= hi_ipl) + return FALSE; + + uint32 llpl = imax(lo_ipl, ipl + 1); + + smp_interlocked_uint32* pintr = & irqs[hi_ipl - lo_ipl]; + + for (uint32 cpl = hi_ipl; cpl >= llpl; cpl--, pintr--) + { + if (0 != weak_read(*pintr)) + { + *int_ipl = cpl; + return TRUE; + } + } + + return FALSE; +} + +/* + * This routine is not currently used. + * + * Check if any interrupt is pending that will interrupt executiion at level "ipl" and if so, retrive and clear it. + * + * This method begins by weak (lazy) reading of interrupt bits data, without using memory barriers. + * This means that even if other threads set the interrupt, it may not be propagated to the cache memory + * of the CPU executing current thread yet. + * In virtual terms, this interrupt may not be visible right away to current processor. + * + * This is fine, since interrupt reguster is intended for asynchronous interrupts, such as external device interrupts or + * interprocessor (IPI) interrupts. Being asynchronous by their nature, and not timed exactly to instructuion stream, it is fine if + * they get noticed by the target CPU with slight delay, such as a delay caused by cache coherence / update propagation protocol. + * + * In practical terms, it may take about one or two VAX instructions in the simulator for the host memory cache coherence protocol + * to complete and update to be propagated. This is perfectly fine. + * + * On the other hand, set_int() executed by local processor _will_ be noticed immediatelly, since local cache is always self-coherent. + * Therefore IPI interrupt sent by the processor to itself or any interrupt cause by per-CPU device or any other interrupt-generating + * code executing within the context of current emulator thread _will_ be visible to that thread immediatelly. + * + * Thus, logically asynchronous interrupts may get noticed at very slight delay, but logically synchronous interrupts are always + * noticed right away. + * + */ + +t_bool InterruptRegister::check_int(uint32 ipl, uint32* int_ipl, uint32* int_dev) +{ +#if (IPL_HMAX - IPL_HMIN != 3) +# error change shortcut code below +#endif + + // NB: may need to reset "changed", and synchronize with SET_IRQL and eval_int + + if (hi_ipl - lo_ipl == 3) + { + // most usual case: no pending interrupts at all + if (weak_read(irqs[0]) == 0 && + weak_read(irqs[1]) == 0 && + weak_read(irqs[2]) == 0 && + weak_read(irqs[3]) == 0) + { + return FALSE; + } + } + + if (ipl >= hi_ipl) + return FALSE; + + uint32 cpl, dev; + + uint32 llpl = imax(lo_ipl, ipl + 1); + + smp_interlocked_uint32* pintr = & irqs[hi_ipl - lo_ipl]; + + for (cpl = hi_ipl; cpl >= llpl; cpl--, pintr--) + { + if (weak_read(*pintr)) + { + for (dev = 0; dev < devs_per_ipl[cpl - lo_ipl]; dev++) + { + if (smp_test_clear_bit(pintr, dev)) + { + *int_ipl = cpl; + *int_dev = dev; + return TRUE; + } + } + } + } + + return FALSE; +} +#endif + +/* ============================================ Windows ============================================ */ + +#if defined(_WIN32) + +/********************** Windows -- create thread **********************/ + +t_bool smp_create_thread(smp_thread_routine_t start_routine, void* arg, smp_thread_t* thread_handle, t_bool dothrow) +{ + if (thread_handle) + *thread_handle = SMP_THREAD_NULL; + + if (sizeof(smp_thread_t) != sizeof(HANDLE)) + panic("Internal error: smp_thread_t misdeclared"); + + uintptr_t res = _beginthreadex(NULL, 0, start_routine, arg, thread_handle ? CREATE_SUSPENDED : 0, NULL); + + if (res == 0) + { + if (dothrow) + panic("Unable to create thread"); + return FALSE; + } + + if (thread_handle) + { + *thread_handle = (HANDLE) res; + if (ResumeThread(*thread_handle) != 1) + { + *thread_handle = SMP_THREAD_NULL; + if (dothrow) + panic("Unable to create thread"); + return FALSE; + } + } + + return TRUE; +} + +/********************** Windows -- wait thread **********************/ + +t_bool smp_wait_thread(smp_thread_t thread_handle, t_value* exitcode, t_bool dothrow) +{ + if (WaitForSingleObject(thread_handle, INFINITE) == WAIT_OBJECT_0) + { + DWORD dw = 0; + if (GetExitCodeThread(thread_handle, & dw)) + { + if (exitcode) + *exitcode = dw; + return TRUE; + } + } + + if (dothrow) + panic("Internal error: unable to complete worker thread"); + + return FALSE; +} + +/********************** Windows -- set thread priority **********************/ + +t_bool smp_set_thread_priority(sim_thread_priority_t prio) +{ + run_scope_context::get_current()->setting_priority(prio); + return smp_set_thread_priority(GetCurrentThread(), prio); +} + +int smp_get_thread_os_priority(smp_thread_t thread_th) +{ + DWORD pclass = GetPriorityClass(GetCurrentProcess()); + int tprio = GetThreadPriority(thread_th); + + if (pclass == 0 || tprio == THREAD_PRIORITY_ERROR_RETURN) + return -1; + + /* + * See priorities table in "Scheduling priorities" + * http://msdn.microsoft.com/en-us/library/windows/desktop/ms685100%28v=VS.85%29.aspx + */ + + switch (pclass) + { + case IDLE_PRIORITY_CLASS: + switch (tprio) + { + case THREAD_PRIORITY_IDLE: return 1; + case THREAD_PRIORITY_LOWEST: return 2; + case THREAD_PRIORITY_BELOW_NORMAL: return 3; + case THREAD_PRIORITY_NORMAL: return 4; + case THREAD_PRIORITY_ABOVE_NORMAL: return 5; + case THREAD_PRIORITY_HIGHEST: return 6; + case THREAD_PRIORITY_TIME_CRITICAL: return 15; + } + break; + + case BELOW_NORMAL_PRIORITY_CLASS: + switch (tprio) + { + case THREAD_PRIORITY_IDLE: return 1; + case THREAD_PRIORITY_LOWEST: return 4; + case THREAD_PRIORITY_BELOW_NORMAL: return 5; + case THREAD_PRIORITY_NORMAL: return 6; + case THREAD_PRIORITY_ABOVE_NORMAL: return 7; + case THREAD_PRIORITY_HIGHEST: return 8; + case THREAD_PRIORITY_TIME_CRITICAL: return 15; + } + break; + + case NORMAL_PRIORITY_CLASS: + switch (tprio) + { + case THREAD_PRIORITY_IDLE: return 1; + case THREAD_PRIORITY_LOWEST: return 6; + case THREAD_PRIORITY_BELOW_NORMAL: return 7; + case THREAD_PRIORITY_NORMAL: return 8; + case THREAD_PRIORITY_ABOVE_NORMAL: return 9; + case THREAD_PRIORITY_HIGHEST: return 10; + case THREAD_PRIORITY_TIME_CRITICAL: return 15; + } + break; + + case ABOVE_NORMAL_PRIORITY_CLASS: + switch (tprio) + { + case THREAD_PRIORITY_IDLE: return 1; + case THREAD_PRIORITY_LOWEST: return 8; + case THREAD_PRIORITY_BELOW_NORMAL: return 9; + case THREAD_PRIORITY_NORMAL: return 10; + case THREAD_PRIORITY_ABOVE_NORMAL: return 11; + case THREAD_PRIORITY_HIGHEST: return 12; + case THREAD_PRIORITY_TIME_CRITICAL: return 15; + } + break; + + case HIGH_PRIORITY_CLASS: + switch (tprio) + { + case THREAD_PRIORITY_IDLE: return 1; + case THREAD_PRIORITY_LOWEST: return 11; + case THREAD_PRIORITY_BELOW_NORMAL: return 12; + case THREAD_PRIORITY_NORMAL: return 13; + case THREAD_PRIORITY_ABOVE_NORMAL: return 14; + case THREAD_PRIORITY_HIGHEST: return 15; + case THREAD_PRIORITY_TIME_CRITICAL: return 15; + } + break; + + case REALTIME_PRIORITY_CLASS: + switch (tprio) + { + case THREAD_PRIORITY_IDLE: return 16; + case THREAD_PRIORITY_LOWEST: return 22; + case THREAD_PRIORITY_BELOW_NORMAL: return 23; + case THREAD_PRIORITY_NORMAL: return 24; + case THREAD_PRIORITY_ABOVE_NORMAL: return 25; + case THREAD_PRIORITY_HIGHEST: return 26; + case THREAD_PRIORITY_TIME_CRITICAL: return 31; + } + break; + } + + return -1; +} + +t_bool smp_set_thread_priority(smp_thread_t thread_th, sim_thread_priority_t prio) +{ + int nPriority; + BOOL bDisablePriorityBoost = FALSE; + + switch (prio) + { + /* CPU thread -- regular instruction stream execution */ + case SIMH_THREAD_PRIORITY_CPU_RUN: + nPriority = THREAD_PRIORITY_BELOW_NORMAL; + bDisablePriorityBoost = TRUE; + break; + + /* console thread -- while CPUs are paused */ + case SIMH_THREAD_PRIORITY_CONSOLE_PAUSED: + nPriority = THREAD_PRIORITY_NORMAL; + bDisablePriorityBoost = FALSE; + break; + + /* console thread -- while CPUs are running */ + case SIMH_THREAD_PRIORITY_CONSOLE_RUN: + // nPriority = THREAD_PRIORITY_ABOVE_NORMAL; + // bDisablePriorityBoost = FALSE; + /* make it the same as OS_HI, so console could preempt VCPU threads, + could even make it higher than OS_HI */ + nPriority = THREAD_PRIORITY_HIGHEST; + bDisablePriorityBoost = TRUE; + break; + + /* + * IO processing thread: make thread priority identical with CRITICAL_OS to avoid assigning criticality + * setting to locks for IOP structures, so locking can be done without the overhead of elevating/demoting thread + * priority, but at the same time also avoiding priority inversion when CPU's thread running at CRITIAL_OS + * (a priority at which OS will typically access IO device) would have to wait for the lock held by IOP thread + * executing at a lower priority and possibly preempted. Downside of this choice being that high IO rate may + * also saturate host system computationally and take precedence over host UI enviroment both in terms of + * high-priority computational load and IO requests queued at high-priority. + */ + case SIMH_THREAD_PRIORITY_IOP: + /* fall through to CRITICAL_OS */ + + /* CPU thread -- kernel is in a critical section (holding spinlocks etc.) */ + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS: + nPriority = THREAD_PRIORITY_ABOVE_NORMAL; + bDisablePriorityBoost = TRUE; + break; + + /* CPU thread -- kernel is processing CLK or IPI interrupt, + or any of these interrupts is pending */ + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI: + nPriority = THREAD_PRIORITY_HIGHEST; + bDisablePriorityBoost = TRUE; + break; + + /* CPU thread -- VM is in a critical section (holding lock etc.) */ + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM: + nPriority = THREAD_PRIORITY_TIME_CRITICAL; + bDisablePriorityBoost = TRUE; + break; + + /* CPU thread -- processor loops calibration using SSC clock */ + case SIMH_THREAD_PRIORITY_CPU_CALIBRATION: + nPriority = THREAD_PRIORITY_TIME_CRITICAL; + bDisablePriorityBoost = TRUE; + break; + + /* clock strobing thread */ + case SIMH_THREAD_PRIORITY_CLOCK: + if (sim_vsmp_active) + { + nPriority = THREAD_PRIORITY_TIME_CRITICAL; + bDisablePriorityBoost = TRUE; + } + else + { + nPriority = THREAD_PRIORITY_HIGHEST; + bDisablePriorityBoost = TRUE; + } + break; + + default: + return FALSE; + } + + HANDLE hThread = thread_th; + t_bool res = (FALSE != SetThreadPriority(hThread, nPriority)); + if (res == FALSE && nPriority == THREAD_PRIORITY_TIME_CRITICAL) + { + nPriority = THREAD_PRIORITY_HIGHEST; + bDisablePriorityBoost = FALSE; + res = (FALSE != SetThreadPriority(hThread, nPriority)); + } + res = res && (0 != SetThreadPriorityBoost(hThread, bDisablePriorityBoost)); + return res; +} + +/* per-thread initializaion */ +t_bool smp_thread_init() +{ + return TRUE; +} + +/* print thread priority allocation etc. */ +void smp_show_thread_priority_info(SMP_FILE* st) +{ +} + +/********************** Windows -- set thread name **********************/ + +const DWORD MS_VC_EXCEPTION=0x406D1388; + +#pragma pack(push,8) +typedef struct tagTHREADNAME_INFO +{ + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +} THREADNAME_INFO; +#pragma pack(pop) + +void smp_set_thread_name(const char* name) +{ + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = (DWORD) -1; + info.dwFlags = 0; + + __try + { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*) &info); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +} + +/********************** Windows -- TLS **********************/ + +t_bool smp_tls_alloc(smp_tls_key* key) +{ + uint32 /*DWORD*/ dw = TlsAlloc(); + return (*key = dw) != 0xFFFFFFFF; +} + +void tls_set_value(const smp_tls_key& key, void* value) +{ + TlsSetValue(key, value); +} + +void* tls_get_value(const smp_tls_key& key) +{ + return TlsGetValue(key); +} + +/************************ Windows -- smp_lock ************************/ + +#if !defined(USE_SIMH_SMP_LOCK) +smp_lock_impl::smp_lock_impl() +{ + criticality = SIM_LOCK_CRITICALITY_NONE; + m_inited = FALSE; +} + +t_bool smp_lock_impl::init(uint32 cycles, t_bool dothrow) +{ + if (m_inited) return TRUE; + + if (! smp_check_aligned((smp_interlocked_uint32*) & m_cs, dothrow)) + return FALSE; + + if (cycles == 0 || smp_ncpus <= 1) + { + InitializeCriticalSection(& m_cs); + m_inited = TRUE; + } + else + { + m_inited = InitializeCriticalSectionAndSpinCount(& m_cs, cycles); + } + + if (!m_inited && dothrow) + panic("Unable to initialize critical section"); + + return m_inited; +} + +smp_lock_impl::~smp_lock_impl() +{ + if (m_inited) + DeleteCriticalSection(& m_cs); +} + +void smp_lock_impl::set_spin_count(uint32 cycles) +{ + SetCriticalSectionSpinCount(&m_cs, (smp_ncpus > 1) ? cycles : 0); +} + +void smp_lock_impl::lock() +{ + if (criticality != SIM_LOCK_CRITICALITY_NONE) + critical_lock(criticality); + + EnterCriticalSection(& m_cs); +} + +void smp_lock_impl::unlock() +{ + LeaveCriticalSection(& m_cs); + + if (criticality != SIM_LOCK_CRITICALITY_NONE) + critical_unlock(criticality); +} +#endif + +/********************** Windows -- smp_semaphore **********************/ + +smp_semaphore_impl::smp_semaphore_impl() +{ + hSemaphore = NULL; + if (sizeof(smp_pollable_handle_t) != sizeof(HANDLE)) + panic("Internal error: smp_pollable_handle_t misdeclared"); +} + +smp_semaphore_impl::~smp_semaphore_impl() +{ + if (hSemaphore != NULL) + CloseHandle(hSemaphore); +} + +t_bool smp_semaphore_impl::init(int initial_open_count, t_bool dothrow) +{ + const int max_count = 0x3FFFFFFF; + if (NULL != (hSemaphore = CreateSemaphore(NULL, initial_open_count, max_count, NULL))) + { + smp_wmb(); + return TRUE; + } + else if (dothrow) + { + panic("Unable to initialize semaphore"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} + +smp_pollable_handle_t smp_semaphore_impl::pollable_handle() +{ + return hSemaphore; +} + +const char* smp_semaphore_impl::pollable_handle_op() +{ + return "h"; +} + +void smp_semaphore_impl::release(int count) +{ + if (! ReleaseSemaphore(hSemaphore, count, NULL)) + panic("Unable to release semaphore"); +} + +void smp_semaphore_impl::clear() +{ + // could do "binary search" if count was expected to be high, but in SIMH it is usually just zero + for (;;) + { + switch (WaitForSingleObject(hSemaphore, 0)) + { + case WAIT_OBJECT_0: + break; + case WAIT_TIMEOUT: + return; + default: + panic("Unexpected failure trying to clear semaphore"); + } + } +} + +void smp_semaphore_impl::wait() +{ + if (WAIT_OBJECT_0 != WaitForSingleObject(hSemaphore, INFINITE)) + panic("Unable to acquire semaphore"); +} + +t_bool smp_semaphore_impl::trywait() +{ + switch (WaitForSingleObject(hSemaphore, 0)) + { + case WAIT_OBJECT_0: + return TRUE; + case WAIT_TIMEOUT: + return FALSE; + default: + panic("Unexpected failure trying to acquire semaphore"); + never_returns_bool_t; + } +} + +/********************** Windows -- smp_barrier **********************/ + +smp_barrier_impl::smp_barrier_impl() +{ + hMutex = NULL; + hEvent = NULL; + barrier_count = 0; + waiting_count = 0; + for (int k = 0; k < SMP_BARRIER_CYCLES; k++) + hOldEvents[k] = NULL; +} + +smp_barrier_impl::~smp_barrier_impl() +{ + dealloc(); +} + +void smp_barrier_impl::dealloc() +{ + if (hMutex != NULL) + { + CloseHandle(hMutex); + hMutex = NULL; + } + + if (hEvent != NULL) + { + CloseHandle(hEvent); + hEvent = NULL; + } + + for (int k = 0; k < SMP_BARRIER_CYCLES; k++) + { + if (hOldEvents[k] != NULL) + CloseHandle(hOldEvents[k]); + hOldEvents[k] = NULL; + } +} + +t_bool smp_barrier_impl::init(int initial_count, t_bool dothrow) +{ + barrier_count = initial_count; + + hMutex = CreateMutex(NULL, FALSE, NULL); + t_bool ok = hMutex != NULL; + + hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + ok = ok && (hEvent != NULL); + + for (int k = 0; k < SMP_BARRIER_CYCLES; k++) + { + hOldEvents[k] = CreateEvent(NULL, TRUE, FALSE, NULL); + ok = ok && (hOldEvents[k] != NULL); + } + + if (ok) + { + smp_wmb(); + return TRUE; + } + else + { + dealloc(); + smp_wmb(); + + if (dothrow) + { + panic("Unable to initialize barrier"); + never_returns_bool_t; + } + else + { + return FALSE; + } + } +} + +void smp_barrier_impl::wait() +{ + if (WAIT_OBJECT_0 != WaitForSingleObject(hMutex, INFINITE)) + panic("Unable to acquire barrier mutex"); + + /* + * Note that we cannot ResetEvent here. Othwerwise if a waiting thread is removed from the wait state + * by kernel APC and re-enters wait state after the event is cleared, the thread will remain stalled. + * + * SetEvent followed by ResetEvent, and likewise PulseEvent are inherently unreliable in Windows. + * If waiting thread is removed from wait state by kernel APC and re-enters wait state after the event + * is cleared, the thread will remain stalled. + * + * This is fundamental design flaw in Windows. See documentation for PulseEvent for more details. + * + * Windows should have linked any thread inside SetEvent to the event, so once the thread returns + * from kernel APC it would know the event had been signalled in the meanwhile and treat the event + * as signalled. But Windows does not, according to the note in PulseEvent documentation. + * + * The workaround is to keep a rotating list of events for several generations of the barrier use. + * + * The application should _not_ re-create the whole barrier object, as it would destroy the old event + * and make the thread resuming after KAPC to possibly fail in waiting on the event -- unless + * SignalObjectAndWait internally bumps reference count and keeps it across post-KAPC restarts, + * which we do not know if it does. + */ + + // if (waiting_count == 0) + // cannot ResetEvent(hEvent); + + if (++waiting_count >= barrier_count) + { + waiting_count = 0; + SetEvent(hEvent); + rotate_events(); + ReleaseMutex(hMutex); + } + else + { + if (WAIT_OBJECT_0 != SignalObjectAndWait(hMutex, hEvent, INFINITE, FALSE)) + panic("Unable to wait on the barrier"); + } +} + +t_bool smp_barrier_impl::trywait() +{ + if (WAIT_OBJECT_0 != WaitForSingleObject(hMutex, INFINITE)) + panic("Unable to acquire barrier mutex"); + /* See the note above for wait() */ + if (waiting_count >= barrier_count - 1) + { + waiting_count = 0; + SetEvent(hEvent); + rotate_events(); + ReleaseMutex(hMutex); + return TRUE; + } + else + { + ReleaseMutex(hMutex); + return FALSE; + } +} + +void smp_barrier_impl::rotate_events() +{ + HANDLE hOldestEvent = hOldEvents[SMP_BARRIER_CYCLES - 1]; + for (int k = SMP_BARRIER_CYCLES - 1; k >= 1; k--) + hOldEvents[k] = hOldEvents[k - 1]; + hOldEvents[0] = hEvent; + hEvent = hOldestEvent; + ResetEvent(hEvent); +} + +t_bool smp_barrier_impl::set_count(int count, t_bool dothrow) +{ + if (WAIT_OBJECT_0 != WaitForSingleObject(hMutex, INFINITE)) + panic("Unable to acquire barrier mutex"); + + barrier_count = count; + + if (waiting_count >= barrier_count) + { + waiting_count = 0; + SetEvent(hEvent); + rotate_events(); + } + + ReleaseMutex(hMutex); + return TRUE; +} + +void smp_barrier_impl::clear() +{ + panic("Operation undefined: barrier.clear"); +} + +void smp_barrier_impl::release(int count) +{ + panic("Operation undefined: barrier.release"); +} + +/********************** Windows -- smp_mutex **********************/ + +smp_mutex_impl::smp_mutex_impl() +{ + hMutex = NULL; + criticality = SIM_LOCK_CRITICALITY_NONE; +} + +smp_mutex_impl::~smp_mutex_impl() +{ + if (hMutex) + CloseHandle(hMutex); +} + +t_bool smp_mutex_impl::init(t_bool dothrow) +{ + if (NULL != (hMutex = CreateMutex(NULL, FALSE, NULL))) + { + return TRUE; + } + else if (dothrow) + { + panic("Unable to create mutex"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} + +void smp_mutex_impl::set_criticality(sim_lock_criticality_t criticality) +{ + this->criticality = criticality; +} + +void smp_mutex_impl::lock() +{ + if (criticality != SIM_LOCK_CRITICALITY_NONE) + critical_lock(criticality); + + if (WAIT_OBJECT_0 != WaitForSingleObject(hMutex, INFINITE)) + panic("Unable to acquire mutex"); +} + +void smp_mutex_impl::unlock() +{ + if (! ReleaseMutex(hMutex)) + panic("Unable to release mutex"); + + if (criticality != SIM_LOCK_CRITICALITY_NONE) + critical_unlock(criticality); +} + +t_bool smp_mutex_impl::trylock() +{ + if (criticality != SIM_LOCK_CRITICALITY_NONE) + critical_lock(criticality); + + switch (WaitForSingleObject(hMutex, 0)) + { + case WAIT_OBJECT_0: + return TRUE; + + case WAIT_TIMEOUT: + if (criticality != SIM_LOCK_CRITICALITY_NONE) + critical_unlock(criticality); + return FALSE; + + default: + panic("Unable to acquire mutex"); + never_returns_bool_t; + } +} + +/********************** Windows -- smp_condvar **********************/ + +smp_condvar_impl::smp_condvar_impl() +{ + hEvent = NULL; +} + +smp_condvar_impl::~smp_condvar_impl() +{ + if (hEvent) + CloseHandle(hEvent); +} + +t_bool smp_condvar_impl::init(t_bool dothrow) +{ + if (NULL != (CreateEvent(NULL, TRUE, FALSE, NULL))) + { + return TRUE; + } + else if (dothrow) + { + panic("Unable to create condition variable"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} + +void smp_condvar_impl::prepareForWait() +{ + if (! ResetEvent(hEvent)) + panic("Unable to reset condition variable"); +} + +void smp_condvar_impl::wait(smp_mutex* mutex, t_bool reacquire_mutex) +{ + smp_mutex_impl* mi = (smp_mutex_impl*) mutex; + if (WAIT_OBJECT_0 != SignalObjectAndWait(mi->hMutex, hEvent, INFINITE, FALSE)) + panic("Unable to wait on condition variable"); + if (reacquire_mutex) + mutex->lock(); +} + +void smp_condvar_impl::signal(smp_mutex* mutex) +{ + if (! SetEvent(hEvent)) + panic("Unable to signal condition variable"); +} + +/********************** Windows -- smp_event **********************/ + +smp_event_impl::smp_event_impl() +{ + delta_timer = NULL; + hEvent = NULL; +} + +smp_event_impl::~smp_event_impl() +{ + if (delta_timer) + delete delta_timer; + if (hEvent) + CloseHandle(hEvent); +} + +t_bool smp_event_impl::init(t_bool dothrow) +{ + if (NULL != (delta_timer = sim_delta_timer::create(dothrow)) && + NULL != (hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + return TRUE; + } + else if (dothrow) + { + panic("Unable to create event variable"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} + +void smp_event_impl::set() +{ + SetEvent(hEvent); +} + +void smp_event_impl::clear() +{ + ResetEvent(hEvent); +} + +void smp_event_impl::wait() +{ + switch (WaitForSingleObject(hEvent, INFINITE)) + { + case WAIT_OBJECT_0: + return; + default: + panic("Unexpected failure waiting for event variable"); + } +} + +t_bool smp_event_impl::trywait() +{ + switch (WaitForSingleObject(hEvent, 0)) + { + case WAIT_OBJECT_0: + return TRUE; + case WAIT_TIMEOUT: + return FALSE; + default: + panic("Unexpected failure waiting for event variable"); + never_returns_bool_t; + } +} + +t_bool smp_event_impl::timed_wait(uint32 usec, uint32* p_actual_usec) +{ + t_bool res; + + RUN_SCOPE_RSCX; + + if (rscx->thread_type != SIM_THREAD_TYPE_CPU) + cpu_unit = NULL; + + if (p_actual_usec) + delta_timer->begin(RUN_PASS); + + uint32 msec = (usec + 500) / 1000; + switch (WaitForSingleObject(hEvent, msec)) + { + case WAIT_OBJECT_0: + res = TRUE; + break; + case WAIT_TIMEOUT: + res = FALSE; + break; + default: + panic("Unexpected failure waiting for event variable"); + never_returns_bool_t; + } + + if (p_actual_usec) + { + delta_timer->sample(RUN_PASS); + *p_actual_usec = delta_timer->us_since_start(RUN_PASS); + } + + return res; +} + +void smp_event_impl::wait_and_clear() +{ + wait(); + clear(); +} + +/********************** Windows -- run_scope_context **********************/ + +void run_scope_context::set_current() +{ + TlsSetValue(run_scope_key, this); +} + +void run_scope_context::set_current(run_scope_context* rscx) +{ + TlsSetValue(run_scope_key, rscx); +} + +run_scope_context* run_scope_context::get_current() +{ + return (run_scope_context*) TlsGetValue(run_scope_key); +} + +/********************** Windows -- smp_get_cpu_count **********************/ + +static int smp_get_cpu_count() +{ + SYSTEM_INFO sysinfo; + GetSystemInfo(& sysinfo); + return (int) sysinfo.dwNumberOfProcessors; +} + +/********************** Windows -- smp_init_info **********************/ + +static int smp_ncores = 0; +static DWORD_PTR smp_core_cpu_mask = 0; +static DWORD_PTR smp_all_cpu_mask = 0; +static int smp_smt_per_core = -1; + +/* + * Gather host system multiprocessor configuration. + * + * ToDo: may later want to switch to Portable Hardware Locality (hwloc) library. + * http://www.open-mpi.org/projects/hwloc + */ +static t_bool smp_init_info() +{ + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION pBuffer = NULL; + DWORD length; + + /* reset data to the "unknown" state */ + smp_ncores = 0; + smp_core_cpu_mask = 0; + smp_smt_per_core = -1; + smp_all_cpu_mask = 0xFFFFFFFF; + if (sizeof(smp_all_cpu_mask) > 32) + { + /* do it weird way to relax false compiler warning */ + smp_all_cpu_mask = smp_all_cpu_mask << 16; + smp_all_cpu_mask = (smp_all_cpu_mask << 16) | 0xFFFFFFFF; + } + + /* keep enlarging buffer until processor information fits into it */ + for (int k = 32 ;; k = k * 2) + { + size_t size = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) * k; + length = (DWORD) size; + pBuffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION) malloc(size); + if (pBuffer == NULL) + { + return FALSE; + } + + if (GetLogicalProcessorInformation(pBuffer, & length)) + { + break; + } + else + { + free(pBuffer); + pBuffer = NULL; + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + return FALSE; + } + } + } + + /* parse retrieved data */ + int max_per_core = 0; + int nentries = (int) (length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)); + + for (int k = 0; k < nentries; k++) + { + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION p = pBuffer + k; + if (p->Relationship == RelationProcessorCore && p->ProcessorCore.Flags == 1) + { + /* per-core record */ + int masksize = sizeof(p->ProcessorMask) * 8; + ULONG_PTR mask = 1; + int per_core = 0; + t_bool found_core_cpu = FALSE; + + for (int i = 0; i < masksize; i++) + { + if (p->ProcessorMask & mask) + { + per_core++; + + if (! found_core_cpu) + { + found_core_cpu = TRUE; + smp_ncores++; + smp_core_cpu_mask |= mask; + } + } + mask = mask << 1; + } + + if (per_core > max_per_core) + max_per_core = per_core; + } + } + + free(pBuffer); + + smp_smt_per_core = max_per_core; + + return TRUE; +} + +/********************** Windows -- smp_get_smt_per_core **********************/ + +/* + * get number of SMT/HyperThreading processor units per core, + * in case of error return -1 + */ +static int smp_get_smt_per_core() +{ + return smp_smt_per_core; +} + +/********************** Windows -- smp_set_affinity **********************/ + +t_bool smp_can_alloc_per_core(int nthreads) +{ + return smp_core_cpu_mask && smp_ncores >= nthreads; +} + +void smp_set_affinity(smp_thread_t thread_th, smp_affinity_kind_t how) +{ + if (how == SMP_AFFINITY_PER_CORE) + { + SetThreadAffinityMask(GetCurrentThread(), smp_core_cpu_mask); + } + else + { + SetThreadAffinityMask(GetCurrentThread(), smp_all_cpu_mask); + } +} + +/********************** Windows -- smp_wait_xxx **********************/ + +int smp_wait(smp_pollable_synch_object* object, int ms) +{ + return smp_wait_any(&object, 1, ms); +} + +int smp_wait_any(smp_pollable_synch_object** objects, int nobjects, int ms) +{ + if (nobjects < 0) + return -1; + + if (nobjects == 0) + return 0; + + HANDLE* handles; + HANDLE hh[64]; + + if (nobjects <= 64) + { + handles = hh; + } + else + { + handles = (HANDLE*) _alloca(sizeof(HANDLE) * nobjects); + if (handles == NULL) return -1; + } + + for (int k = 0; k < nobjects; k++) + handles[k] = objects[k]->pollable_handle(); + + DWORD dwMilliseconds = (ms < 0) ? INFINITE : (DWORD) ms; + DWORD dwRes = WaitForMultipleObjects((DWORD) nobjects, handles, FALSE, dwMilliseconds); + + if (dwRes == WAIT_TIMEOUT) + return 0; + + if (dwRes >= WAIT_OBJECT_0 && dwRes <= WAIT_OBJECT_0 + (DWORD) (nobjects - 1)) + return (int) (dwRes - WAIT_OBJECT_0 + 1); + + if (dwRes >= WAIT_ABANDONED_0 && dwRes <= WAIT_ABANDONED_0 + (DWORD) (nobjects - 1)) + return (int) (dwRes - WAIT_OBJECT_0 + 1); + + return -1; +} + +/********************** Windows -- debug helper **********************/ + +void debug_out(const char* x) +{ + OutputDebugStringA(x); +} + +#endif + +/* ========================================= Linux/OSX OS level semaphores ========================================= */ + +/* + * OS X allows only named semaphores. + * sem_init and sem_destroy are declared in header files but return error "Function is not implemented". + */ +#if defined(__linux) +int os_sem_init(sem_t** ppsem, sem_t* holder, unsigned int initial_value) +{ + int rc = sem_init(holder, 0, initial_value); + *ppsem = (rc == 0) ? holder : NULL; + return rc; +} + +int os_sem_destroy(sem_t** ppsem, sem_t* holder) +{ + int rc = 0; + if (*ppsem) + { + rc = sem_destroy(*ppsem); + if (rc == 0) *ppsem = NULL; + } + return rc; +} +#elif defined(__APPLE__) +int os_sem_init(sem_t** ppsem, char* name, unsigned int initial_value) +{ + /* OS X limits semaphore name to 30 characters max */ + static smp_interlocked_uint32 seq = 0; + sprintf(name, "SIMH_%08X_%08X_%07X", (int) geteuid(), (int) getpid(), smp_interlocked_increment(&seq)); + + (void) sem_unlink(name); /* delete persistent semaphore e.g. after system crash-reboot */ + sem_t* sem = sem_open(name, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR, initial_value); + + if (sem == SEM_FAILED) + { + perror("\nSIMH error: sem_open faled"); + *ppsem = NULL; + return -1; + } + else + { + *ppsem = sem; + return 0; + } +} + +int os_sem_destroy(sem_t** ppsem, char* name) +{ + int rc = 0; + if (*ppsem) + { + if (name[0]) + { + (void) sem_unlink(name); + name[0] = 0; + } + rc = sem_close(*ppsem); + } + return rc; +} +#endif + +/* ========================================== Linux/OSX all architectures ========================================== */ + +#if defined(__linux) || defined(__APPLE__) + +static int rest_setpriority(int which, int who, int prio) +{ + DECL_RESTARTABLE(rc); + DO_RESTARTABLE(rc, setpriority(which, who, prio)); + return rc; +} + +static int rest_setrlimit(int resource, const struct rlimit *rlim) +{ + DECL_RESTARTABLE(rc); + DO_RESTARTABLE(rc, setrlimit(resource, rlim)); + return rc; +} + +static int rest_pthread_create(pthread_t *thread_id, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) +{ + DECL_RESTARTABLE(rc); + DO_RVAL_RESTARTABLE(rc, pthread_create(thread_id, attr, start_routine, arg)); + return rc; +} + +static int rest_pthread_join(pthread_t thread_id, void **retval) +{ + DECL_RESTARTABLE(rc); + DO_RVAL_RESTARTABLE(rc, pthread_join(thread_id, retval)); + return rc; +} + +static int rest_pthread_mutex_init(pthread_mutex_t* __restrict mutex, const pthread_mutexattr_t* __restrict attr) +{ + DECL_RESTARTABLE(rc); + DO_RVAL_RESTARTABLE(rc, pthread_mutex_init(mutex, attr)); + return rc; +} + +static int rest_pthread_mutex_lock(pthread_mutex_t *mutex) +{ + DECL_RESTARTABLE(rc); + DO_RVAL_RESTARTABLE(rc, pthread_mutex_lock(mutex)); + return rc; +} + +static int rest_pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + DECL_RESTARTABLE(rc); + DO_RVAL_RESTARTABLE(rc, pthread_mutex_trylock(mutex)); + return rc; +} + +static int rest_pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + DECL_RESTARTABLE(rc); + DO_RVAL_RESTARTABLE(rc, pthread_mutex_unlock(mutex)); + return rc; +} + +static int rest_pthread_setschedparam(pthread_t thread_id, int policy, const struct sched_param *param) +{ + DECL_RESTARTABLE(rc); + DO_RVAL_RESTARTABLE(rc, pthread_setschedparam(thread_id, policy, param)); + return rc; +} + +static int rest_pthread_getschedparam(pthread_t thread_id, int *policy, struct sched_param *param) +{ + DECL_RESTARTABLE(rc); + DO_RVAL_RESTARTABLE(rc, pthread_getschedparam(thread_id, policy, param)); + return rc; +} + +static int rest_pthread_cond_init(pthread_cond_t* __restrict cond, const pthread_condattr_t* __restrict attr) +{ + DECL_RESTARTABLE(rc); + DO_RVAL_RESTARTABLE(rc, pthread_cond_init(cond, attr)); + return rc; +} + +static int rest_pthread_cond_wait(pthread_cond_t* __restrict cond, pthread_mutex_t* __restrict mutex) +{ + DECL_RESTARTABLE(rc); + DO_RVAL_RESTARTABLE(rc, pthread_cond_wait(cond, mutex)); + return rc; +} + +static int rest_pthread_cond_timedwait(pthread_cond_t* __restrict cond, pthread_mutex_t* __restrict mutex, const struct timespec* __restrict abstime) +{ + DECL_RESTARTABLE(rc); + DO_RVAL_RESTARTABLE(rc, pthread_cond_timedwait(cond, mutex, abstime)); + return rc; +} + +static int rest_pthread_cond_signal(pthread_cond_t *cond) +{ + DECL_RESTARTABLE(rc); + DO_RVAL_RESTARTABLE(rc, pthread_cond_signal(cond)); + return rc; +} + +static int rest_pthread_cond_broadcast(pthread_cond_t *cond) +{ + DECL_RESTARTABLE(rc); + DO_RVAL_RESTARTABLE(rc, pthread_cond_broadcast(cond)); + return rc; +} + +/********************** Linux/OSX -- create thread **********************/ + +static SMP_THREAD_ROUTINE_DECL smp_create_thread_starter(void* pxargs); + +t_bool smp_create_thread(smp_thread_routine_t start_routine, void* arg, smp_thread_t* thread_handle, t_bool dothrow) +{ + smp_event* event = NULL; + smp_event* event_ack = NULL; + void* xargs[4]; + int rc; + + if (thread_handle) + *thread_handle = SMP_THREAD_NULL; + + if (sizeof(smp_thread_t) != sizeof(pthread_t)) + panic("Internal error: smp_thread_t misdeclared"); + + pthread_t thrd; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); + if (thread_handle) + { + event = smp_event::create(); + event->clear(); + event_ack = smp_event::create(); + event_ack->clear(); + xargs[0] = (void*) start_routine; + xargs[1] = arg; + xargs[2] = event; + xargs[3] = event_ack; + rc = rest_pthread_create(&thrd, &attr, smp_create_thread_starter, xargs); + } + else + { + rc = rest_pthread_create(&thrd, &attr, start_routine, arg); + } + pthread_attr_destroy(&attr); + + if (rc) + { + if (dothrow) + panic("Unable to create thread"); + return FALSE; + } + + if (thread_handle) + { + *thread_handle = thrd; + event_ack->wait(); + event->set(); + } + + return TRUE; +} + +static SMP_THREAD_ROUTINE_DECL smp_create_thread_starter(void* pxargs) +{ + void** xargs = (void**) pxargs; + smp_thread_routine_t start_routine = (smp_thread_routine_t) xargs[0]; + void* arg = xargs[1]; + smp_event* event = (smp_event*) xargs[2]; + smp_event* event_ack = (smp_event*) xargs[3]; + event_ack->set(); + event->wait(); + delete event; + delete event_ack; + return (*start_routine)(arg); +} + +/********************** Linux/OSX -- wait thread **********************/ + +t_bool smp_wait_thread(smp_thread_t thread_handle, t_value* exitcode, t_bool dothrow) +{ + void* rv = NULL; + if (0 == rest_pthread_join(thread_handle, &rv)) + { + if (exitcode) + *exitcode = (t_value) rv; + return TRUE; + } + + if (dothrow) + panic("Internal error: unable to complete worker thread"); + + return FALSE; +} + +/********************** Linux/OSX -- smp_semaphore **********************/ + +smp_semaphore_impl::smp_semaphore_impl() +{ + fd[0] = fd[1] = -1; +} + +smp_semaphore_impl::~smp_semaphore_impl() +{ + DECL_RESTARTABLE(rc); + if (fd[0] != -1) DO_RESTARTABLE(rc, close(fd[0])); + if (fd[1] != -1) DO_RESTARTABLE(rc, close(fd[1])); +} + +t_bool smp_semaphore_impl::init(int initial_open_count, t_bool dothrow) +{ + DECL_RESTARTABLE(rc); + DO_RESTARTABLE(rc, pipe(fd)); + if (rc == 0) + { + smp_wmb(); + if (initial_open_count) + release(initial_open_count); + return TRUE; + } + else if (dothrow) + { + panic("Unable to initialize semaphore"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} + +smp_pollable_handle_t smp_semaphore_impl::pollable_handle() +{ + return fd[PIPE_RD]; +} + +const char* smp_semaphore_impl::pollable_handle_op() +{ + return "R"; +} + +void smp_semaphore_impl::release(int count) +{ + DECL_RESTARTABLE(rc); + while (count) + { + char buf[256]; + size_t wrsz = ((size_t) count < sizeof(buf)) ? (size_t) count : sizeof(buf); + DO_RESTARTABLE(rc, write(fd[PIPE_WR], buf, wrsz)); + if (rc == -1) + panic("Unable to release semaphore"); + count -= rc; + } +} + +void smp_semaphore_impl::clear() +{ + DECL_RESTARTABLE(rc); + int flags; + + DO_RESTARTABLE(rc, fcntl(fd[PIPE_RD], F_GETFL)); + if (rc == -1) + panic("Unable to clear semaphore"); + flags = rc; + + DO_RESTARTABLE(rc, fcntl(fd[PIPE_RD], F_SETFL, flags | O_NONBLOCK)); + if (rc == -1) + panic("Unable to clear semaphore"); + + for (;;) + { + char buf[256]; + size_t rdsz = sizeof(buf); + DO_RESTARTABLE(rc, read(fd[PIPE_RD], buf, rdsz)); + if (rc == 0) break; + if (rc == -1 && errno == EWOULDBLOCK) break; + if (rc == -1) + panic("Unable to clear semaphore"); + } + + DO_RESTARTABLE(rc, fcntl(fd[PIPE_RD], F_SETFL, flags & ~O_NONBLOCK)); + if (rc == -1) + panic("Unable to clear semaphore"); +} + +void smp_semaphore_impl::wait() +{ + DECL_RESTARTABLE(rc); + for (;;) + { + char c; + DO_RESTARTABLE(rc, read(fd[PIPE_RD], &c, 1)); + if (rc == 0) continue; /* spurious */ + if (rc == 1) break; + panic("Unable to acquire semaphore"); + } +} + +t_bool smp_semaphore_impl::trywait() +{ + panic("Unimplemented: semaphore.trywait"); + never_returns_bool_t; +} + +/****************** Linux/OSX -- smp_simple_semaphore ******************/ + +smp_simple_semaphore* smp_simple_semaphore::create(int initial_open_count, t_bool dothrow) +{ + smp_simple_semaphore_impl* sem = new smp_simple_semaphore_impl(); + if (! sem->init(initial_open_count, dothrow)) + { + delete sem; + sem = NULL; + } + return sem; +} + +smp_simple_semaphore_impl::smp_simple_semaphore_impl() +{ + inited = FALSE; + os_sem_decl_init(semaphore); +} + +smp_simple_semaphore_impl::~smp_simple_semaphore_impl() +{ + if (inited) + os_sem_destroy(os_sem_ptr(semaphore)); +} + +t_bool smp_simple_semaphore_impl::init(int initial_open_count, t_bool dothrow) +{ + if (! inited) + { + DECL_RESTARTABLE(rc); + DO_RESTARTABLE(rc, os_sem_init(os_sem_ptr(semaphore), initial_open_count)); + if (rc == -1) goto cleanup; + smp_wmb(); + inited = TRUE; + } + +cleanup: + + if (! inited && dothrow) + panic("Unable to initialize semaphore"); + + return inited; +} + +void smp_simple_semaphore_impl::clear() +{ + while (trywait()) ; +} + +void smp_simple_semaphore_impl::wait() +{ + DECL_RESTARTABLE(rc); + DO_RESTARTABLE(rc, sem_wait(semaphore)); + if (rc != 0) panic("Semaphore error"); +} + +t_bool smp_simple_semaphore_impl::trywait() +{ + DECL_RESTARTABLE(rc); + DO_RESTARTABLE(rc, sem_trywait(semaphore)); + if (rc == -1 && errno == EAGAIN) return FALSE; + if (rc != 0) panic("Semaphore error"); + return TRUE; +} + +void smp_simple_semaphore_impl::release(int count) +{ + while (count-- > 0) + { + DECL_RESTARTABLE(rc); + DO_RESTARTABLE(rc, sem_post(semaphore)); + if (rc != 0) panic("Semaphore error"); + } +} + +/********************** Linux/OSX -- smp_barrier **********************/ + +smp_barrier_impl::smp_barrier_impl() +{ + mutex_inited = FALSE; + for (int k = 0; k < SMP_BARRIER_CYCLES; k++) + cond_inited[k] = FALSE; + wait_index = 0; + barrier_count = 0; + waiting_count = 0; + wseq = 0; +} + +smp_barrier_impl::~smp_barrier_impl() +{ + dealloc(); +} + +void smp_barrier_impl::dealloc() +{ + if (mutex_inited) + { + pthread_mutex_destroy(& mutex); + mutex_inited = FALSE; + } + + for (int k = 0; k < SMP_BARRIER_CYCLES; k++) + { + if (cond_inited[k]) + pthread_cond_destroy(& cond[k]); + cond_inited[k] = FALSE; + } +} + +t_bool smp_barrier_impl::init(int initial_count, t_bool dothrow) +{ + barrier_count = initial_count; + + t_bool ok = TRUE; + + if (0 == rest_pthread_mutex_init(& mutex, NULL)) + mutex_inited = TRUE; + else + ok = FALSE; + + for (int k = 0; k < SMP_BARRIER_CYCLES; k++) + { + if (0 == rest_pthread_cond_init(& cond[k], NULL)) + cond_inited[k] = TRUE; + else + ok = FALSE; + } + + if (ok) + { + smp_wmb(); + return TRUE; + } + + dealloc(); + smp_wmb(); + + if (dothrow) + { + panic("Unable to initialize barrier"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} + +void smp_barrier_impl::wait() +{ + if (rest_pthread_mutex_lock(& mutex)) + panic("Unable to acquire barrier mutex"); + + if (++waiting_count >= barrier_count) + { + waiting_count = 0; + wseq++; + if (rest_pthread_cond_broadcast(& cond[wait_index])) + panic("Unable to wakeup thread waiting on the barrier"); + wait_index = (wait_index + 1) % SMP_BARRIER_CYCLES; + rest_pthread_mutex_unlock(& mutex); + } + else + { + /* loop for spurious wakeups */ + uint32 sv_wseq = wseq; + do + { + if (rest_pthread_cond_wait(& cond[wait_index], & mutex)) + panic("Unable to wait on the barrier"); + } + while (sv_wseq == wseq); + /* + * Is not pthreads design great? Not only it is impossible to change pthread_barrier height, + * but when pthread_cond_broadcast signals condition variable, all waiting threads are made to cram + * to reacquire the mutex, whether they need it or not, just so they could release it right away, + * and there is no way to specifiy the intent to the contrary. Two pieces of cake for the price of one package. + * Good it is not a performance-critical use in VAX MP. + */ + rest_pthread_mutex_unlock(& mutex); + } +} + +t_bool smp_barrier_impl::trywait() +{ + if (rest_pthread_mutex_lock(& mutex)) + panic("Unable to acquire barrier mutex"); + + if (waiting_count >= barrier_count - 1) + { + waiting_count = 0; + if (rest_pthread_cond_broadcast(& cond[wait_index])) + panic("Unable to wakeup thread waiting on the barrier"); + wait_index = (wait_index + 1) % SMP_BARRIER_CYCLES; + rest_pthread_mutex_unlock(& mutex); + return TRUE; + } + else + { + rest_pthread_mutex_unlock(& mutex); + return FALSE; + } +} + +t_bool smp_barrier_impl::set_count(int count, t_bool dothrow) +{ + if (rest_pthread_mutex_lock(& mutex)) + panic("Unable to acquire barrier mutex"); + + barrier_count = count; + + if (waiting_count >= barrier_count) + { + waiting_count = 0; + wseq++; + if (rest_pthread_cond_broadcast(& cond[wait_index])) + panic("Unable to wakeup thread waiting on the barrier"); + wait_index = (wait_index + 1) % SMP_BARRIER_CYCLES; + } + + rest_pthread_mutex_unlock(& mutex); + return TRUE; +} + +void smp_barrier_impl::clear() +{ + panic("Operation undefined: barrier.clear"); +} + +void smp_barrier_impl::release(int count) +{ + panic("Operation undefined: barrier.release"); +} + +/********************** Linux/OSX -- smp_mutex **********************/ + +smp_mutex_impl::smp_mutex_impl() +{ + criticality = SIM_LOCK_CRITICALITY_NONE; + inited = FALSE; +} + +smp_mutex_impl::~smp_mutex_impl() +{ + if (inited) + pthread_mutex_destroy(& mutex); +} + +t_bool smp_mutex_impl::init(t_bool dothrow) +{ + t_bool ok = TRUE; + t_bool attr_inited = FALSE; + pthread_mutexattr_t attr; + + if (ok && 0 == pthread_mutexattr_init(& attr)) + attr_inited = TRUE; + else + ok = FALSE; + + if (ok && 0 != pthread_mutexattr_settype(& attr, PTHREAD_MUTEX_RECURSIVE)) + ok = FALSE; + + if (ok && 0 != rest_pthread_mutex_init(& mutex, & attr)) + ok = FALSE; + + if (attr_inited) + pthread_mutexattr_destroy(& attr); + + if (ok) + { + inited = TRUE; + return TRUE; + } + else if (dothrow) + { + panic("Unable to create mutex"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} + +void smp_mutex_impl::set_criticality(sim_lock_criticality_t criticality) +{ + this->criticality = criticality; +} + +void smp_mutex_impl::lock() +{ + if (criticality != SIM_LOCK_CRITICALITY_NONE) + critical_lock(criticality); + + if (pthread_mutex_lock(& mutex)) + panic("Unable to acquire mutex"); +} + +void smp_mutex_impl::unlock() +{ + if (pthread_mutex_unlock(& mutex)) + panic("Unable to acquire mutex"); + + if (criticality != SIM_LOCK_CRITICALITY_NONE) + critical_unlock(criticality); +} + +t_bool smp_mutex_impl::trylock() +{ + if (criticality != SIM_LOCK_CRITICALITY_NONE) + critical_lock(criticality); + + switch (rest_pthread_mutex_trylock(& mutex)) + { + case 0: + return TRUE; + + case EBUSY: + if (criticality != SIM_LOCK_CRITICALITY_NONE) + critical_unlock(criticality); + return FALSE; + + default: + panic("Unable to acquire mutex"); + never_returns_bool_t; + } +} + +/********************** Linux/OSX -- smp_condvar **********************/ + +smp_condvar_impl::smp_condvar_impl() +{ + wseq = 0; + inited = FALSE; +} + +smp_condvar_impl::~smp_condvar_impl() +{ + if (inited) + pthread_cond_destroy(& cond); +} + +t_bool smp_condvar_impl::init(t_bool dothrow) +{ + if (0 == rest_pthread_cond_init(& cond, NULL)) + { + return TRUE; + } + else if (dothrow) + { + panic("Unable to create condition variable"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} + +void smp_condvar_impl::prepareForWait() +{ + /* nothing to do: pthreads condition variables are edge-triggered */ +} + +void smp_condvar_impl::wait(smp_mutex* mutex, t_bool reacquire_mutex) +{ + smp_mutex_impl* mi = (smp_mutex_impl*) mutex; + uint32 sv_wseq = wseq; + + /* loop for spurious wakeups */ + do + { + if (rest_pthread_cond_wait(& cond, & mi->mutex)) + panic("Unable to wait on condition variable"); + } + while (sv_wseq = wseq); + + if (! reacquire_mutex) + mutex->unlock(); +} + +void smp_condvar_impl::signal(smp_mutex* mutex) +{ + if (mutex) mutex->lock(); + + wseq++; + if (rest_pthread_cond_signal(& cond)) + { + if (mutex) mutex->unlock(); + panic("Unable to signal condition variable"); + } + + if (mutex) mutex->unlock(); +} + +/********************** Linux/OSX -- smp_event **********************/ + +smp_event_impl::smp_event_impl() +{ + inited = FALSE; + state = FALSE; + set_wseq = 0; +} + +smp_event_impl::~smp_event_impl() +{ + if (inited) + { + pthread_cond_destroy(& cond); + pthread_mutex_destroy(& mutex); + } +} + +t_bool smp_event_impl::init(t_bool dothrow) +{ + t_bool inited_mutex = FALSE; + pthread_condattr_t* p_cond_attr = NULL; + pthread_condattr_t cond_attr; + + state = FALSE; + + if (0 == rest_pthread_mutex_init(& mutex, NULL)) + inited_mutex = TRUE; + +#if defined(HAVE_POSIX_CLOCK_ID) + if (sim_posix_have_clock_id) + { + if (pthread_condattr_init(& cond_attr)) + goto cleanup; + p_cond_attr = & cond_attr; + if (pthread_condattr_setclock(p_cond_attr, sim_posix_clock_id)) + { + pthread_condattr_destroy(p_cond_attr); + p_cond_attr = NULL; + } + } +#endif + + if (0 == rest_pthread_cond_init(& cond, p_cond_attr)) + { + if (p_cond_attr) + pthread_condattr_destroy(p_cond_attr); + inited = TRUE; + return TRUE; + } + +#if defined(HAVE_POSIX_CLOCK_ID) +cleanup: +#endif + + if (p_cond_attr) + pthread_condattr_destroy(p_cond_attr); + + if (inited_mutex) + pthread_mutex_destroy(& mutex); + + if (dothrow) + { + panic("Unable to create event object"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} + +void smp_event_impl::set() +{ + if (rest_pthread_mutex_lock(& mutex)) + panic("Unable to acquire mutex"); + + state = TRUE; + set_wseq++; + + if (rest_pthread_cond_broadcast(& cond)) + { + rest_pthread_mutex_unlock(& mutex); + panic("Unable to signal condition variable"); + } + + if (rest_pthread_mutex_unlock(& mutex)) + panic("Unable to acquire mutex"); +} + +void smp_event_impl::clear() +{ + if (rest_pthread_mutex_lock(& mutex)) + panic("Unable to acquire mutex"); + + state = FALSE; + + if (rest_pthread_mutex_unlock(& mutex)) + panic("Unable to acquire mutex"); +} + +void smp_event_impl::wait() +{ + if (rest_pthread_mutex_lock(& mutex)) + panic("Unable to acquire mutex"); + + uint32 start_wseq = set_wseq; + + while (state == FALSE && set_wseq == start_wseq) + { + /* + * Wakeups can be spurious, per pthreads specification. + */ + if (rest_pthread_cond_wait(& cond, & mutex)) + panic("Unable to wait on condition variable"); + } + + if (rest_pthread_mutex_unlock(& mutex)) + panic("Unable to acquire mutex"); +} + +t_bool smp_event_impl::trywait() +{ + if (rest_pthread_mutex_lock(& mutex)) + panic("Unable to acquire mutex"); + + t_bool res = state; + + if (rest_pthread_mutex_unlock(& mutex)) + panic("Unable to acquire mutex"); + + return res; +} + +static void get_now(struct timespec* now) +{ +#if defined(HAVE_POSIX_CLOCK_ID) + if (sim_posix_have_clock_id) + { + clock_gettime(sim_posix_clock_id, now); + return; + } +#endif + + /* + * Note: On current versions of OS X gettimeofday result is computed based on combination + * of real-time clock data and RDTSC counter progress accumulated since the clock's + * recent tick and provides progress resolution down to 1 usec. + * + * For details see + * http://www.opensource.apple.com/source/Libc/Libc-763.13/x86_64/sys/i386_gettimeofday_asm.s + * http://www.opensource.apple.com/source/Libc/Libc-763.13/x86_64/sys/nanotime.s + * http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/i386/commpage/commpage.h + * http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/i386/commpage/commpage.c + * http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/i386/rtclock.c + * http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/x86_64/pal_routines_asm.s + * http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/x86_64/machine_routines_asm.s + * + * Note that TSC base data is currently kept in per-system area of commpage, rather than + * in per-processor area. As Intel processors with invariant TSC capability appear to have + * TSC reading synchrnonized across the cores (google: tsc across cores, e.g. + * http://software.intel.com/en-us/forums/showthread.php?t=77730), this should not be + * a problem with current Macs that use such processors and use only one socket. + * + * If Macs were to ever go multi-socket, they could still maintain composite RTC/TSC timer + * capability by extending comm area with per-processor pages, keeping time data in per-processor + * area and updating thread cpu migration count in thread's context every time the thread + * migrates across the processors, so read-time logics can detect migration in the middle of + * the function call and redo data fetching. + * + * For now, the best we can do anyway is a general sanity check for time jumping backwards. + * + */ + + struct timeval tv; + gettimeofday(& tv, NULL); + now->tv_sec = tv.tv_sec; + now->tv_nsec = tv.tv_usec * 1000; +} + +t_bool smp_event_impl::timed_wait(uint32 usec, uint32* p_actual_usec) +{ + struct timespec start; + struct timespec target; + t_bool res = TRUE; + + static const long million = 1000 * 1000; + static const long billion = 1000 * 1000 * 1000; + + /* get current time */ + get_now(& start); + + /* calculate wait end absolute time */ + target = start; + target.tv_sec += usec / million; + target.tv_nsec += (usec % million) * 1000; + target.tv_sec += target.tv_nsec / billion; + target.tv_nsec = target.tv_nsec % billion; + + if (rest_pthread_mutex_lock(& mutex)) + panic("Unable to acquire mutex"); + + uint32 start_wseq = set_wseq; + + while (state == FALSE && set_wseq == start_wseq) + { + /* + * Wakeups can be spurious, per pthreads specification. + */ + int tw = rest_pthread_cond_timedwait(& cond, & mutex, & target); + if (tw == ETIMEDOUT) + { + if (state == FALSE && set_wseq == start_wseq) + res = FALSE; + break; + } + else if (tw) + { + panic("Unable to wait on condition variable"); + } + } + + if (pthread_mutex_unlock(& mutex)) + panic("Unable to acquire mutex"); + + if (p_actual_usec) + { + struct timespec now; + get_now(& now); + + double delta = 0; + + if (now.tv_sec >= start.tv_sec) + { + delta = (double) (now.tv_sec - start.tv_sec) * 1000 * 1000; + delta += (double) (now.tv_nsec - start.tv_nsec) / 1000; + } + + /* time jumped backwards? */ + if (delta < 0) delta = 0; + + /* time jumped forward way too much? */ + const double delta_max = 0.9 * (double) UINT32_MAX; + if (delta > 0.9 * delta_max) + delta = delta_max; + + *p_actual_usec = (uint32) delta; + } + + return res; +} + +void smp_event_impl::wait_and_clear() +{ + if (rest_pthread_mutex_lock(& mutex)) + panic("Unable to acquire mutex"); + + uint32 start_wseq = set_wseq; + + while (state == FALSE && set_wseq == start_wseq) + { + /* + * Wakeups can be spurious, per pthreads specification. + */ + if (rest_pthread_cond_wait(& cond, & mutex)) + panic("Unable to wait on condition variable"); + } + + state = FALSE; + + if (rest_pthread_mutex_unlock(& mutex)) + panic("Unable to acquire mutex"); +} + +/********************** Linux/OSX -- run_scope_context **********************/ + +void run_scope_context::set_current() +{ + pthread_setspecific(run_scope_key, this); +} + +void run_scope_context::set_current(run_scope_context* rscx) +{ + pthread_setspecific(run_scope_key, rscx); +} + +run_scope_context* run_scope_context::get_current() +{ + return (run_scope_context*) pthread_getspecific(run_scope_key); +} + +/********************** Linux/OSX -- smp_wait_xxx **********************/ + +int smp_wait(smp_pollable_synch_object* object, int ms) +{ + return smp_wait_any(&object, 1, ms); +} + +int smp_wait_any(smp_pollable_synch_object** objects, int nobjects, int ms) +{ + if (nobjects < 0) + return -1; + + if (nobjects == 0) + return 0; + + struct pollfd* handles; + struct pollfd hh[64]; + + if (nobjects <= 64) + { + handles = hh; + } + else + { + handles = (struct pollfd*) alloca(sizeof(struct pollfd) * nobjects); + if (handles == NULL) return -1; + } + + for (int k = 0; k < nobjects; k++) + { + handles[k].fd = objects[k]->pollable_handle(); + handles[k].events = POLLIN; + } + + for (;;) + { + for (;;) + { + for (int k = 0; k < nobjects; k++) + handles[k].revents = 0; + int rc = poll(handles, nobjects, ms); + if (rc == -1) + { + if (errno == EINTR) + continue; + else + return -1; + } + else if (rc == 0) + { + return 0; + } + else + { + break; + } + } + + for (int k = 0; k < nobjects; k++) + { + if (handles[k].revents & POLLIN) + { + const char* op = objects[k]->pollable_handle_op(); + if (0 == strcmp(op, "r")) + { + return k + 1; + } + else if (0 == strcmp(op, "R")) + { + objects[k]->wait(); + return k + 1; + } + } + } + } +} +#endif + +/* ========================================== Linux all architectures ========================================== */ + +#if defined(__linux) + +/********************** Linux -- set thread priority **********************/ + +static int cpu_calibration_priority = -1; +static int cpu_kernel_critical_vm_priority = -1; +static int cpu_kernel_critical_os_hi_priority = -1; +static int cpu_kernel_critical_os_priority = -1; +static int cpu_clock_hi_priority = -1; +static int cpu_clock_lo_priority = -1; + +static pid_t gettid(void) +{ + return syscall(__NR_gettid); +} + +t_bool smp_set_thread_priority(sim_thread_priority_t prio) +{ + run_scope_context::get_current()->setting_priority(prio); + return smp_set_thread_priority(pthread_self(), prio); +} + +int smp_get_thread_os_priority(smp_thread_t thread_th) +{ + int policy = SCHED_OTHER; + struct sched_param sc_param; + if (0 != rest_pthread_getschedparam(thread_th, & policy, & sc_param)) + return -1; + if (policy == SCHED_RR || policy == SCHED_FIFO) + return sc_param.sched_priority; + else + return 0; +} + +t_bool smp_set_thread_priority(smp_thread_t thread_th, sim_thread_priority_t prio) +{ + int priority, xnice = 0; + int policy; + + switch (prio) + { + /* CPU thread -- regular instruction stream execution */ + case SIMH_THREAD_PRIORITY_CPU_RUN: + policy = SCHED_OTHER; + priority = 0; + xnice = 10; + break; + + /* console thread -- while CPUs are paused */ + case SIMH_THREAD_PRIORITY_CONSOLE_PAUSED: + policy = SCHED_OTHER; + priority = 0; + xnice = 0; + break; + + /* console thread -- while CPUs are running */ + case SIMH_THREAD_PRIORITY_CONSOLE_RUN: + // policy = SCHED_OTHER; + // priority = 0; + // xnice = -10; + /* make it the same as OS_HI, so console could preempt VCPU threads, + could even make it higher than OS_HI */ + if (cpu_kernel_critical_os_hi_priority < 0) + return FALSE; + policy = SCHED_RR; + priority = cpu_kernel_critical_os_hi_priority; + break; + + /* + * IO processing thread: make thread priority identical with CRITICAL_OS to avoid assigning criticality + * setting to locks for IOP structures, so locking can be done without the overhead of elevating/demoting thread + * priority, but at the same time also avoiding priority inversion when CPU's thread running at CRITIAL_OS + * (a priority at which OS will typically access IO device) would have to wait for the lock held by IOP thread + * executing at a lower priority and possibly preempted. Downside of this choice being that high IO rate may + * also saturate host system computationally and take precedence over host UI enviroment both in terms of + * high-priority computational load and IO requests queued at high-priority. + */ + case SIMH_THREAD_PRIORITY_IOP: + /* fall through to CRITICAL_OS */ + + /* CPU thread -- kernel is in a critical section (holding spinlocks etc.) */ + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS: + if (cpu_kernel_critical_os_priority < 0) + return FALSE; + policy = SCHED_RR; + priority = cpu_kernel_critical_os_priority; + break; + + /* CPU thread -- kernel is processing CLK or IPI interrupt, + or any of these interrupts is pending */ + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI: + if (cpu_kernel_critical_os_hi_priority < 0) + return FALSE; + policy = SCHED_RR; + priority = cpu_kernel_critical_os_hi_priority; + break; + + /* CPU thread -- VM is in a critical section (holding lock etc.) */ + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM: + if (cpu_kernel_critical_vm_priority < 0) + return FALSE; + policy = SCHED_RR; /* cannot be SCHED_OTHER, see below */ + priority = cpu_kernel_critical_vm_priority; + break; + + /* CPU thread -- processor loops calibration using SSC clock */ + case SIMH_THREAD_PRIORITY_CPU_CALIBRATION: + if (cpu_calibration_priority < 0) + return FALSE; + policy = SCHED_FIFO; + priority = cpu_calibration_priority; + break; + + /* clock strobing thread */ + case SIMH_THREAD_PRIORITY_CLOCK: + if (sim_vsmp_active) + { + if (cpu_clock_hi_priority < 0) + return FALSE; + policy = SCHED_FIFO; + priority = cpu_clock_hi_priority; + } + else + { + if (cpu_clock_lo_priority < 0) + return FALSE; + policy = SCHED_FIFO; + priority = cpu_clock_lo_priority; + } + break; + + default: + return FALSE; + } + + struct sched_param sc_param; + sc_param.sched_priority = priority; + + if (0 != rest_pthread_setschedparam(thread_th, policy, & sc_param)) + return FALSE; + + if (policy == SCHED_OTHER) + { + /* + * The only VAX MP cross-thread invocations are for CRITICAL_VM, CRITICAL_OS and CRITICAL_OS_HI, + * which are not SCHED_OTHER. All SCHED_OTHER priorities are always set for the current thread only. + * + * Note that we rely here on Linux NPTL (and LinuxThreads) feature non-compliant to + * pthreads specification: NPTL threads (contrary to pthreads specification) not sharing nice value. + * + * Unfortunately, all SCHED_OTHER processes have sched priority of 0 and the only way to + * tweak their real priority is via per-thread nice value (at least on Linux, can be different on + * other systems). Neither portable nor semi-portable access to nice value are not exposed by pthreads + * specification. + * An old good case of "Unix is snake oil". + */ + if (! pthread_equal(thread_th, pthread_self())) + return FALSE; + pid_t tid = gettid(); + if (rest_setpriority(PRIO_PROCESS, tid, xnice)) + return FALSE; + } + + return TRUE; +} + +static void smp_set_thread_priority_init() +{ + struct rlimit rlim; + int prio, pmin, pmax; + int pmin_fifo, pmax_fifo; + + // some pthreads implementatons may want to call + // pthread_setconcurrency(...) + + if (0 == getrlimit(RLIMIT_RTPRIO, & rlim)) + { + rlim.rlim_cur = rlim.rlim_max; + setrlimit(RLIMIT_RTPRIO, & rlim); + } + + if (0 == getrlimit(RLIMIT_NICE, & rlim)) + { + rlim.rlim_cur = rlim.rlim_max; + setrlimit(RLIMIT_NICE, & rlim); + } + + pmin = sched_get_priority_min(SCHED_RR); + pmax = sched_get_priority_max(SCHED_RR); + if (pmin < 0 || pmax - pmin < 31) + { + /* range is below Unix specification */ + return; + } + + pmin_fifo = sched_get_priority_min(SCHED_FIFO); + pmax_fifo = sched_get_priority_max(SCHED_FIFO); + if (pmin_fifo < 0 || pmax_fifo - pmin_fifo < 31) + { + /* range is below Unix specification */ + return; + } + + if (pmin_fifo != pmin || pmax_fifo != pmax) + { + fprintf(stderr, "\n%s INTERNAL ERROR !!! SCHED_FIFO range (%d ... %d) is distinct from SCHED_RR range (%d ... %d)\n", + sim_name, pmin_fifo, pmax_fifo, pmin, pmax); + return; + } + + /* + * calculare thread priorities starting with SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS: + * put it just above 20 (highest nice) + */ + prio = 21; + if (prio < pmin) prio = pmin; + if (prio > pmax) return; + cpu_kernel_critical_os_priority = prio; + + /* + * assign other priorities, in this increasing order: + * + * cpu_kernel_critical_os_priority + * cpu_kernel_critical_os_hi_priority + * cpu_kernel_critical_vm_priority + * cpu_clock_hi_priority = cpu_clock_lo_priority + * cpu_calibration_priority + * + */ + + if (++prio > pmax) return; + cpu_kernel_critical_os_hi_priority = prio; + + if (++prio > pmax) return; + cpu_kernel_critical_vm_priority = prio; + + if (++prio > pmax) return; + cpu_clock_hi_priority = cpu_clock_lo_priority = prio; + + if (++prio > pmax) return; + cpu_calibration_priority = prio; + + /* + * we have performed minmalist allocation + * + * now try to separate priority range from timesharing and also separate defined priorities + * from each other, if possible, to force scheduler perform cross-CPU thread migration and + * thus respect priority difference + */ + + if (prio + 4 <= pmax) + { + cpu_kernel_critical_os_priority += 4; + cpu_kernel_critical_os_hi_priority += 4; + cpu_kernel_critical_vm_priority += 4; + cpu_clock_lo_priority += 4; + cpu_clock_hi_priority = cpu_clock_lo_priority; + cpu_calibration_priority += 4; + prio += 4; + } + else + { + return; + } + + int delta = 0; + if (prio + 4 * 2 <= pmax) + delta = 2; + else if (prio + 4 * 1 <= pmax) + delta = 1; + + if (delta) + { + cpu_kernel_critical_os_priority += delta * 0; + cpu_kernel_critical_os_hi_priority += delta * 1; + cpu_kernel_critical_vm_priority += delta * 2; + cpu_clock_lo_priority += delta * 3; + cpu_clock_hi_priority = cpu_clock_lo_priority; + cpu_calibration_priority += delta * 4; + } +} + +/* print thread priority allocation etc. */ +void smp_show_thread_priority_info(SMP_FILE* st) +{ + fprintf(st, "Using thread priority range up to %d\n", cpu_calibration_priority); +} + +/* per-thread initializaion */ +t_bool smp_thread_init() +{ + t_bool res = TRUE; + struct rlimit rlim; + + /* set priotiry limits for those platforms where limits can be per-thread */ + if (0 == getrlimit(RLIMIT_RTPRIO, & rlim)) + { + rlim.rlim_cur = rlim.rlim_max; + if (rest_setrlimit(RLIMIT_RTPRIO, & rlim)) + res = FALSE; + } + + if (0 == getrlimit(RLIMIT_NICE, & rlim)) + { + rlim.rlim_cur = rlim.rlim_max; + if (rest_setrlimit(RLIMIT_NICE, & rlim)) + res = FALSE; + } + + return res; +} + +/********************** Linux -- set thread name **********************/ + +void smp_set_thread_name(const char* name) +{ + /* sanity check for 64-bit case, normally should be ok since 64-bit Linux + uses LP64 model with long defined as 64 bits */ + if (name == (const char*) (unsigned long) name) + prctl(PR_SET_NAME, (unsigned long) name, 0, 0, 0); +} + +/********************** Linux -- smp_get_cpu_count **********************/ + +static int smp_get_cpu_count() +{ + /* + * Also works for Solaris, AIX. + * For other systems (FreeBSD, MacOS X, NetBSD, OpenBSD) see + * http://stackoverflow.com/questions/150355/programmatically-find-the-number-of-cores-on-a-machine + */ + int res = (int) sysconf(_SC_NPROCESSORS_ONLN); + if (res <= 0) + panic("Unable to determine the number of CPUs in the host system"); + return res; +} + +/********************** Linux -- smp_init_info **********************/ + +static void strip_trailing_blanks(char* s) +{ + if (*s) + { + char* p = s + strlen(s) - 1; + while (p >= s && (*p == ' ' || *p == '\t')) + *p-- = '\0'; + } +} + +static int smp_ncores = 0; +static cpu_set_t smp_core_cpu_set; +static cpu_set_t smp_all_cpu_set; +static int smp_smt_per_core = -1; + + +/* + * Gather host system multiprocessor configuration. + * + * ToDo: may later want to switch to Portable Hardware Locality (hwloc) library. + * http://www.open-mpi.org/projects/hwloc + */ +static t_bool smp_init_info() +{ + /* + * /proc/cpuinfo displays information only for online CPUs, this is just what we want + */ + FILE* fd = fopen("/proc/cpuinfo", "r"); + int max_per_core = 0; + t_bool done = FALSE; + char buffer[1024]; + char* xp; + char* key; + char* value; + int hi_core = 255; + int* p_smt = (int*) malloc(sizeof(int) * (hi_core + 1)); + int cpu_id = -1; + int phys_id = -1; + cpu_set_t core_set; + + smp_ncores = 0; + CPU_ZERO(& smp_core_cpu_set); + CPU_ZERO(& smp_all_cpu_set); + CPU_ZERO(& core_set); + + CHECK(fd); + + CHECK(p_smt); + memset(p_smt, 0, sizeof(int) * (hi_core + 1)); + + while (fgets(buffer, sizeof(buffer), fd)) + { + buffer[sizeof(buffer) - 1] = '\0'; + if (xp = strchr(buffer, '\n')) *xp = '\0'; + if (xp = strchr(buffer, '\r')) *xp = '\0'; + if (buffer[0] == '\0') + { + cpu_id = -1; + phys_id = -1; + continue; + } + key = buffer; + xp = strchr(buffer, ':'); + if (! xp) + { + cpu_id = -1; + phys_id = -1; + continue; + } + *xp++ = 0; + value = xp; + while (*value == ' ' || *value == 't') value++; + strip_trailing_blanks(key); + strip_trailing_blanks(value); + + if (streqi(key, "processor")) + { + cpu_id = -1; + phys_id = -1; + CHECK(1 == sscanf(value, "%d", & cpu_id)); + CHECK(cpu_id >= 0); + CPU_SET(cpu_id, & smp_all_cpu_set); + } + else if (streqi(key, "physical id")) + { + // may not be reported when executed on hosts that do not have multicore CPUs + // or under VMWare + phys_id = -1; + CHECK(1 == sscanf(value, "%d", & phys_id)); + CHECK(phys_id >= 0); + } + else if (streqi(key, "core id")) + { + // may not be reported when executed on hosts that do not have SMT (hyperthreaded) CPUs + // or under VMWare + int core_id = -1; + CHECK(1 == sscanf(value, "%d", & core_id)); + CHECK(core_id >= 0); + if (core_id >= hi_core) + { + int new_hi_core = (hi_core * 2 + 1) - 1; + if (core_id >= new_hi_core) + new_hi_core = core_id; + int* np = (int*) realloc(p_smt, sizeof(int) * (new_hi_core + 1)); + CHECK(np); + memset(np + hi_core + 1, 0, sizeof(int) * (new_hi_core - hi_core)); + p_smt = np; + hi_core = new_hi_core; + } + p_smt[core_id]++; + + /* + * currently limited to: + * max 32 physical processors, + * max 32 cores per physical processor + */ + CHECK(cpu_id >= 0); + CHECK(phys_id >= 0 || phys_id == -1); + CHECK(core_id < 32); + int phys_core_id = (phys_id >= 0 ? phys_id * 32 : 0) + core_id; + CHECK(phys_core_id < __CPU_SETSIZE); + + if (! CPU_ISSET(phys_core_id, & core_set)) + { + CPU_SET(cpu_id, & smp_core_cpu_set); + CPU_SET(phys_core_id, & core_set); + smp_ncores++; + } + } + } + + CHECK(! ferror(fd)); + + for (int k = 0; k <= hi_core; k++) + { + if (p_smt[k] > max_per_core) + max_per_core = p_smt[k]; + } + + /* in case "core id" is not reported */ + if (max_per_core == 0) + max_per_core = 1; + + done = TRUE; + +cleanup: + + if (done) + { + smp_smt_per_core = max_per_core; + } + else + { + smp_ncores = 0; + smp_smt_per_core = -1; + } + + if (fd) + fclose(fd); + if (p_smt) + free(p_smt); + + + return done; +} + +/********************** Linux -- smp_get_smt_per_core **********************/ + +/* + * get number of SMT/HyperThreading processor units per core, + * in case of error return -1 + */ +static int smp_get_smt_per_core() +{ + return smp_smt_per_core; +} + +/********************** Linux -- smp_set_affinity **********************/ + +t_bool smp_can_alloc_per_core(int nthreads) +{ + return smp_ncores >= nthreads; +} + +void smp_set_affinity(smp_thread_t thread_th, smp_affinity_kind_t how) +{ + if (how == SMP_AFFINITY_PER_CORE) + { + pthread_setaffinity_np(thread_th, sizeof smp_core_cpu_set, & smp_core_cpu_set); + } + else + { + pthread_setaffinity_np(thread_th, sizeof smp_all_cpu_set, & smp_all_cpu_set); + } +} +#endif + +/* =========================================== OSX all architectures =========================================== */ + +#if defined(__APPLE__) + +/********************** OSX -- set thread priority **********************/ + +static int cpu_calibration_priority = -1; +static int cpu_kernel_critical_vm_priority = -1; +static int cpu_kernel_critical_os_hi_priority = -1; +static int cpu_kernel_critical_os_priority = -1; +static int cpu_clock_hi_priority = -1; +static int cpu_clock_lo_priority = -1; +static int cpu_run_priority = -1; +static int console_paused_priority = -1; +static int console_run_priority = -1; + +t_bool smp_set_thread_priority(sim_thread_priority_t prio) +{ + run_scope_context::get_current()->setting_priority(prio); + return smp_set_thread_priority(pthread_self(), prio); +} + +int smp_get_thread_os_priority(smp_thread_t thread_th) +{ + int policy = SCHED_OTHER; + struct sched_param sc_param; + if (0 != rest_pthread_getschedparam(thread_th, & policy, & sc_param)) + return -1; + return sc_param.sched_priority; +} + +t_bool smp_set_thread_priority(smp_thread_t thread_th, sim_thread_priority_t prio) +{ + int priority = -1; + int policy; + + switch (prio) + { + /* CPU thread -- regular instruction stream execution */ + case SIMH_THREAD_PRIORITY_CPU_RUN: + policy = SCHED_OTHER; + priority = cpu_run_priority; + break; + + /* console thread -- while CPUs are paused */ + case SIMH_THREAD_PRIORITY_CONSOLE_PAUSED: + policy = SCHED_OTHER; + priority = console_paused_priority; + break; + + /* console thread -- while CPUs are running */ + case SIMH_THREAD_PRIORITY_CONSOLE_RUN: + policy = SCHED_RR; + priority = console_run_priority; + break; + + /* + * IO processing thread: make thread priority identical with CRITICAL_OS to avoid assigning criticality + * setting to locks for IOP structures, so locking can be done without the overhead of elevating/demoting thread + * priority, but at the same time also avoiding priority inversion when CPU's thread running at CRITIAL_OS + * (a priority at which OS will typically access IO device) would have to wait for the lock held by IOP thread + * executing at a lower priority and possibly preempted. Downside of this choice being that high IO rate may + * also saturate host system computationally and take precedence over host UI enviroment both in terms of + * high-priority computational load and IO requests queued at high-priority. + */ + case SIMH_THREAD_PRIORITY_IOP: + /* fall through to CRITICAL_OS */ + + /* CPU thread -- kernel is in a critical section (holding spinlocks etc.) */ + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS: + policy = SCHED_RR; + priority = cpu_kernel_critical_os_priority; + break; + + /* CPU thread -- kernel is processing CLK or IPI interrupt, + or any of these interrupts is pending */ + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI: + policy = SCHED_RR; + priority = cpu_kernel_critical_os_hi_priority; + break; + + /* CPU thread -- VM is in a critical section (holding lock etc.) */ + case SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM: + policy = SCHED_FIFO; + priority = cpu_kernel_critical_vm_priority; + break; + + /* CPU thread -- processor loops calibration using SSC clock */ + case SIMH_THREAD_PRIORITY_CPU_CALIBRATION: + policy = SCHED_FIFO; + priority = cpu_calibration_priority; + break; + + /* clock strobing thread */ + case SIMH_THREAD_PRIORITY_CLOCK: + if (sim_vsmp_active) + { + policy = SCHED_FIFO; + priority = cpu_clock_hi_priority; + } + else + { + policy = SCHED_FIFO; + priority = cpu_clock_lo_priority; + } + break; + + default: + return FALSE; + } + + if (priority < 0) + return FALSE; + + struct sched_param sc_param; + sc_param.sched_priority = priority; + + if (0 != rest_pthread_setschedparam(thread_th, policy, & sc_param)) + return FALSE; + + return TRUE; +} + +static void smp_set_thread_priority_init() +{ + /* + * Assign CPU priorities in this increasing order: + * + * cpu_run_priority + * cpu_kernel_critical_os_priority + * cpu_kernel_critical_os_hi_priority + * cpu_kernel_critical_vm_priority + * cpu_clock_hi_priority = cpu_clock_lo_priority + * cpu_calibration_priority + * + * Leave console_paused_priority as default level. + * + * Set console_run_priority >= cpu_kernel_critical_vm_priority so it could preepmpt CPU threads, + * but leave cpu_calibration_priority above the console level so that normal console activity + * (other than Ctrl/E) does not disturb calibration. + * + * OS X uses thread priorities in range 0...63 has several bands of priorities: + * + * Real-time threads + * Kernel-mode threads + * System high priority range + * Normal priority range + * + * The range available to regular applications is 15...47 (higher number means higher priority). + * Default priority for console applications both in fore- and back-ground of UI is 31. + * + * See: + * + * https://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/KernelProgramming/scheduler/scheduler.html + * https://developer.apple.com/library/mac/#documentation/Darwin/Reference/Manpages/man2/setpriority.2.html + * + * http://www.opensource.apple.com/source/Libc/Libc-763.13/pthreads/pthread.c + * http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/bsd/kern/kern_resource.c + * http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/kern/mk_sp.c + * http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/kern/priority.c + */ + + int pmin, pmax; + t_bool bad = FALSE; + + pmin = sched_get_priority_min(SCHED_OTHER); + pmax = sched_get_priority_max(SCHED_OTHER); + if (pmin != 15 || pmax != 47) + { + fprintf(stderr, "Unexpected SCHED_OTHER priority band: %d/%d instead of 15/47\n", pmin, pmax); + bad = TRUE; + } + + pmin = sched_get_priority_min(SCHED_RR); + pmax = sched_get_priority_max(SCHED_RR); + if (pmin != 15 || pmax != 47) + { + fprintf(stderr, "Unexpected SCHED_RR priority band: %d/%d instead of 15/47\n", pmin, pmax); + bad = TRUE; + } + + pmin = sched_get_priority_min(SCHED_FIFO); + pmax = sched_get_priority_max(SCHED_FIFO); + if (pmin != 15 || pmax != 47) + { + fprintf(stderr, "Unexpected SCHED_FIFO priority band: %d/%d instead of 15/47\n", pmin, pmax); + bad = TRUE; + } + + if (bad) return; + + cpu_calibration_priority = 47; + cpu_clock_hi_priority = cpu_clock_lo_priority = 42; + cpu_kernel_critical_vm_priority = 42; + cpu_kernel_critical_os_hi_priority = 38; + cpu_kernel_critical_os_priority = 34; + cpu_run_priority = 25; + + console_run_priority = cpu_kernel_critical_vm_priority; + console_paused_priority = 31; +} + +/* print thread priority allocation etc. */ +void smp_show_thread_priority_info(SMP_FILE* st) +{ + fprintf(st, "Using thread priority range up to %d\n", cpu_calibration_priority); +} + +/* per-thread initializaion */ +t_bool smp_thread_init() +{ + return TRUE; +} + +/********************** OSX -- set thread name **********************/ + +void smp_set_thread_name(const char* name) +{ + pthread_setname_np(name); +} + +/********************** OSX -- smp_get_cpu_count **********************/ + +static t_bool osx_sysctl(const char* name, int* pv); + +static int smp_get_cpu_count() +{ + int ncpus = 0; + if (! osx_sysctl("hw.activecpu", & ncpus)) + panic("Unable to determine the number of CPUs in the host system"); + return ncpus; +} + +static t_bool osx_sysctl(const char* key, int* pv) +{ + int data = 0; + size_t dlen = sizeof(data); + + if (sysctlbyname(key, & data, & dlen, NULL, 0)) + { + char msg[256]; + sprintf(msg, "unable to get system information %s", key); + perror(msg); + return FALSE; + } + + if (dlen != sizeof(int)) + { + fprintf(stderr, "unable to get system information %s: buffer size mismatch", key); + return FALSE; + } + + *pv = data; + + return TRUE; +} + +/********************** OSX -- smp_init_info **********************/ + +static int smp_smt_per_core = -1; + +/* + * Gather host system multiprocessor configuration. + */ +static t_bool smp_init_info() +{ + int ncores = 0; + int nlcpus = 0; + + if (! osx_sysctl("hw.physicalcpu_max", & ncores)) + return FALSE; + + if (! osx_sysctl("hw.logicalcpu_max", & nlcpus)) + return FALSE; + + if (ncores <= 0 || nlcpus <= 0 || nlcpus < ncores || (nlcpus % ncores)) + return FALSE; + + smp_smt_per_core = nlcpus / ncores; + + return TRUE; +} + +/********************** OSX -- smp_get_smt_per_core **********************/ + +/* + * get number of SMT/HyperThreading processor units per core, + * in case of error return -1 + */ +static int smp_get_smt_per_core() +{ + return smp_smt_per_core; +} + +/********************** OSX -- smp_set_affinity **********************/ + +t_bool smp_can_alloc_per_core(int nthreads) +{ + return FALSE; +} + +void smp_set_affinity(smp_thread_t thread_th, smp_affinity_kind_t how) +{ + panic("smp_set_affinity: not implemented"); +} +#endif + +/* ===================================== Common (x86/x64) -- smp_lock_impl ===================================== */ + +#if defined(USE_SIMH_SMP_LOCK) + +#if (defined(__linux) || defined(__APPLE__)) && (defined(__x86_32__) || defined(__x86_64__)) +# define interlocked_incl(var) __sync_add_and_fetch(& (var), 1) +# define interlocked_decl(var) __sync_add_and_fetch(& (var), -1) +# define interlocked_incl_is_eq(var) (interlocked_incl(var) == 0) +# define interlocked_decl_is_geq(var) (interlocked_decl(var) >= 0) +# define interlocked_cas(var, oldvalue, newvalue) __sync_val_compare_and_swap(& (var), (oldvalue), (newvalue)) +#elif defined(_WIN32) +# define interlocked_incl(var) smp_interlocked_increment(& (var)) +# define interlocked_decl(var) smp_interlocked_decrement(& (var)) +# define interlocked_incl_is_eq(var) (interlocked_incl(var) == 0) +# define interlocked_decl_is_geq(var) (interlocked_decl(var) >= 0) +# define interlocked_cas(var, oldvalue, newvalue) smp_interlocked_cas(& (var), (oldvalue), (newvalue)) +#endif + +smp_lock_impl::smp_lock_impl() +{ + criticality = SIM_LOCK_CRITICALITY_NONE; + inited = FALSE; +#if !defined(_WIN32) + os_sem_decl_init(semaphore); +#endif + calibrating_spinloop = FALSE; +} + +t_bool smp_lock_impl::init(uint32 cycles, t_bool dothrow) +{ + if (! inited) + { + if (! check_aligned(this, SMP_MAXCACHELINESIZE, dothrow)) + goto cleanup; + if (! smp_check_aligned(& lock_count, dothrow)) + goto cleanup; + + /* gcc builtin __sync_add_and_fetch uses xaddl unavailable before i486 */ + /* __sync_val_compare_and_swap uses cmpxchgl unavailable before i486 */ + if (!have_x86_xaddl || !have_x86_cmpxchgl) + goto cleanup; + smp_var(lock_count) = -1; + recursion_count = 0; + owning_thread = 0; + spin_count = (smp_ncpus > 1) ? cycles : 0; +#if defined(_WIN32) + semaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); + if (semaphore == NULL) goto cleanup; +#else + DECL_RESTARTABLE(rc); + DO_RESTARTABLE(rc, os_sem_init(os_sem_ptr(semaphore), 0)); + if (rc == -1) goto cleanup; +#endif + perf_collect = FALSE; + perf_reset(); + inited = TRUE; + } + +cleanup: + + if (! inited && dothrow) + panic("Unable to initialize critical section"); + + return inited; +} + +t_bool smp_lock_impl::init(uint32 us, uint32 min_cycles, uint32 max_cycles, t_bool dothrow) +{ + t_bool res = init(0, dothrow); + if (res) set_spin_count(us, min_cycles, max_cycles); + return res; +} + +smp_lock_impl::~smp_lock_impl() +{ +#if defined(_WIN32) + if (inited) + CloseHandle(semaphore); +#else + if (inited) + os_sem_destroy(os_sem_ptr(semaphore)); +#endif +} + +void smp_lock_impl::set_spin_count(uint32 cycles) +{ + spin_count = (smp_ncpus > 1) ? cycles : 0; +} + +void smp_lock_impl::set_spin_count(uint32 us, uint32 min_cycles, uint32 max_cycles) +{ + uint32 cycles; + + if (smp_ncpus <= 1) + { + cycles = 0; + } + else if (spins_per_ms == 0 || us == 0) + { + cycles = min_cycles; + } + else + { + cycles = (spins_per_ms * us) / 1000 + 1; + if (cycles > max_cycles) cycles = max_cycles; + if (cycles < min_cycles) cycles = min_cycles; + } + + spin_count = cycles; +} + +void smp_lock_impl::set_perf_collect(t_bool collect) +{ + perf_collect = collect; +} + +void smp_lock_impl::perf_reset() +{ + UINT64_SET_ZERO(perf_lock_count); + UINT64_SET_ZERO(perf_nowait_count); + UINT64_SET_ZERO(perf_syswait_count); + perf_avg_spinwait = 0; +} + +void smp_lock_impl::perf_show(SMP_FILE* fp, const char* name) +{ + if (! perf_collect) + { + fprintf(fp, "Lock %s: counters disabled\n", name); + return; + } + + if (UINT64_IS_ZERO(perf_lock_count)) + { + fprintf(fp, "Lock %s: unused\n", name); + return; + } + +#if defined (USE_INT64) + fprintf(fp, "Lock %s: acquired %" PRIu64 " times\n", name, perf_lock_count); +#else + fprintf(fp, "Lock %s: acquired %.0f times\n", name, UINT64_TO_DOUBLE(perf_lock_count)); +#endif + + fprintf(fp, " immediately: %g%%\n", 100.0 * UINT64_TO_DOUBLE(perf_nowait_count) / UINT64_TO_DOUBLE(perf_lock_count)); + fprintf(fp, " blocking wait: %g%%\n", 100.0 * UINT64_TO_DOUBLE(perf_syswait_count) / UINT64_TO_DOUBLE(perf_lock_count)); + fprintf(fp, " average spinwait (loop cycles): %.1f\n", perf_avg_spinwait); +} + +/* + * There are few chief categories of locks in VAX MP. + * + * 1. Device locks are expected to have moderate contention rate. + * (Contention for the device can be high, but contenders will likely be spinning + * on OS-level interlock represented by locks category 3 below, rather than on + * the device lock.) + * + * 2. CPU database lock (cpu_database_lock) is expected to have higher + * contention rate. + * + * 3. Locks for interlocked VAX instructions can have very high contention rate. + * They may also be replaced in the future (or partially replaced anyway) + * by host-processor-specific native interlocked operations, when host + * provides such operations (such as BBSSI and BCCCI replaced by x86/x64 + * BTS and BTR instructions). + * + * Current lock implementation for the initial release of VAX MP is... well, initial. + * + * Current implementation of smp_lock_impl locking is intended for low to medium contention + * levels, as expected for VAX MP device locks, and may be adequate for cpu_database_lock + * when no synchronization window is used, but may be poor for interlocked VAX instructions + * locks in portable mode, or for cpu_database_lock when synchronization window is enabled. + * + * Once the initial release of VAX MP is stable and we get experience and peformance data + * running a realistic load (and stress tests), we can address locks implementation to + * imporve their performance. + * + * Present algorithm for the locks is as follows: + * + * If a collision is detected (critical section is locked and there is more than + * one waiting thread), second and subsequent waiters do not try to spin and perform + * OS-level blocking wait instead, thus further ensuring low spinwait contention levels + * on the lock structure in memory. + * + * Accordingly, the lock is implemented using "test-and-test-and-set" technique as deemed + * being amply sufficient for this case. + * + * In general, test-and-test-and-set approach avoids executing interlocked instructions + * in each loop cycle and thus flooding the interconnect with expensive RFO requests, and in + * addition causing cache line RFO ping-pong between multiple processors if there were + * multiple spinners (which is however not the case with the present design). Instead + * the spinning happens on the value held in local cache that is updated via cache coherency + * protocol after the lock is released by the holder. + * + * See G. Graunke, S. Thakkar, "Synchronization Algorithms for Shared-Memory + * Multiprocessors", IEEE Computer, June 1990 + * + * Even this limited optimization is amply sufficient for the present design that allows + * one holder and only one active spinner, with other contenders executing a blocking wait, + * so there is a priori no hogging of the interconnect by ping-pong and cache invalidations + * between multiple spinners. + * + * It would be great if there was a way to check if lock holder is pre-empted, and + * in this case avoid spin-waiting, however most host OS'es do not provide this functionality. + * Therefore we start by spin-waiting on the lock in optimistic assumption that the holder + * is not pre-empted and that the duration of the critical section is short. It generally + * does not make sense to spin longer than twice the cost of a context switch (combined direct + * and indirect costs, also accounting for subsequent performance hit due to possible TLB flushing) + * or much longer than the duration of the critical section itself, as the latter would indicate + * that the holder is likely to be pre-empted. Once spin cycles limit is reached, this is regarded + * as an indication that the holder is likely to be pre-empted and that the contenter is better + * to switch to OS-level locking. + * + * Should the assumptions about low contentoon level be ever revised and the lock would + * have to cope with high spinwait contention level, it would probably would have to be + * reimplemented using MCS queue locks + * + * See John Mellor-Crummey, Michael Scott, "Algorithms for Scalable Synchronization on + * Shared-Memory Multiprocessors", ACM Trans. on Computer Systems, February 1991 + * + * John Mellor-Crummey, Michael Scott, "Synchronization Without Contention", + * ASPLOS-IV, Proceedings of the fourth international conference on Architectural support for + * programming languages and operating systems; punlished as special issue of + * ACM SIGOPS Operating Systems Review, April 1991, ACM SIGARCH Computer Architecture News, + * and ACM SIGPLAN Notices. + * + * T. Anderson, "The Performance of Spin Lock Alternatives for Shared-Memory Multiprocessors", + * IEEE Transactions on Parallel and Distributed Systems, Jan 1990 + * + * John Mellor-Crummey, "Mutual Exclusion: Classical Algorithms for Locks" + * (slides for COMP 422, Lecture 17, 29 March 2011) + * http://www.clear.rice.edu/comp422/lecture-notes/comp422-2011-Lecture17-ClassicalLocks.pdf + * + * John Mellor-Crummey, "Algorithms for Scalable Lock Synchronization on Shared-memory Multiprocessors" + * (slides for COMP 422, Lecture 18 31 March 2011), + * http://www.clear.rice.edu/comp422/lecture-notes/comp422-2011-Lecture18-HWLocks.pdf + * + * James H. Anderson, Yong-jik Kim, "Shared-memory mutual exclusion: Major research trends + * since 1986", Distributed Computing, vol. 16 (issue 2-3), September 2003 + * + * Maurice Herlihy, Nir Shavit, "The Art of Multiprocessor Programming", Elsevir, 2008, ch. 7.5 + * + * or M-locks + * + * Peter Magnusson, Anders Landin, Erik Hagersten, "Queue Locks on Cache Coherent Multiprocessors", + * Proceedings of the 8th International Symposium on Parallel Processing, 1994 + * + * or composite locks + * + * Maurice Herlihy, Nir Shavit, "The Art of Multiprocessor Programming", Elsevir, 2008, ch. 7.7 + * + * ideally modified to be priority-aware and preemption-safe (i.e. not releasing the lock + * to a pre-empted contender if there are other contenders actively spinning for it). + */ + +/* + * Lock and unlock perform memory barriers as follows: + * + * lock: acquire lock + * full MB + * + * unlock: full MB + * release lock + * + * In the lock case, full MB cannot be downgraded to merely RMB, since otherwise writes + * from protected section could leak to before the lock if processor reorders instructions + * or memory references. + * + * In the unlock case, full MB cannot be downgraded to merely WMB, since otherwise reads + * from protected section can leak past the unlock. + * + * Neither read nor write references to protected data should be allowed to leak out of + * protected section bounded by atomic-lock/MB and MB/atomic-unlock. + * + */ + +void smp_lock_impl::lock() +{ +#if defined(_WIN32) + DWORD this_thread = GetCurrentThreadId(); + if (unlikely(this_thread == 0)) panic("Unexpected: ThreadId is 0"); +# define thread_eq(p1, p2) ((p1) == (p2)) +#else + pthread_t this_thread = pthread_self(); + if (unlikely(this_thread == 0)) panic("Unexpected: thread handle is 0"); +# define thread_eq(p1, p2) pthread_equal((p1), (p2)) +#endif + + /* + * We have to choose between two evils. + * + * Raising thread priority level _before_ actual lock acqusition causes lower-priority + * threads to be depressed from doing useful work and, in addition, may cause lock holder + * to be pre-empted by the contender. + * + * Whereas raising thread priority level _after_ actual lock acquision may cause lock + * holder to be pre-empted and left at low priority for extended time. The probability + * of the second unfavorable development is less, but its consequences are more drastic. + * + * On the balance, the implementation below makes choice for the former evil. + * + */ + if (criticality != SIM_LOCK_CRITICALITY_NONE) + critical_lock(criticality); + + uint32 cycles = spin_count; + + if (likely(spin_count != 0)) + { + if (thread_eq(owning_thread, this_thread)) + { + interlocked_incl(smp_var(lock_count)); + recursion_count++; + return; + } + + for (;;) + { + if (smp_var(lock_count) == -1 && interlocked_cas(smp_var(lock_count), -1, 0) == -1) + { + smp_post_interlocked_mb(); + + owning_thread = this_thread; + recursion_count = 1; + + /* record performance counters */ + if (unlikely(perf_collect)) + perf_acquired(cycles); + return; + } + + if (smp_var(lock_count) >= 1) + { + // the lock is locked, and at least one more thread is already (or also) waiting for it + // give up on spin-waiting + break; + } + + /* + * Insert PAUSE to prevent generation of multiple (and useless) simultaneous out-of-order read requests. + * Effective on Pentium 4 and higher processors, NOP on earlier Intel processors and clones. + * See Intel application note AP-949 "Using Spin-Loops on Intel Pentium 4 Processor and Intel Xeon Processor", + * P/N 248674-002 (http://software.intel.com/file/25602). + * May also help when spin-waitng on a Hyper-Threaded processor by releasing shared resources to a thread + * doing useful work, perhaps a lock holder. + */ + smp_cpu_relax(); + + if (cycles == 0 || --cycles == 0) + { + // make one last attempt at checking + if (smp_var(lock_count) != -1) + // give up on spin-waiting + break; + } + } + } + + /* comes here for OS blocking wait */ + if (unlikely(calibrating_spinloop)) + return; + + if (interlocked_incl_is_eq(smp_var(lock_count))) + { + smp_post_interlocked_mb(); + owning_thread = this_thread; + recursion_count = 1; + + /* record performance counters */ + if (unlikely(perf_collect)) + perf_acquired(cycles); + } + else if (thread_eq(owning_thread, this_thread)) + { + recursion_count++; + } + else + { +#if defined(_WIN32) + switch (WaitForSingleObject(semaphore, INFINITE)) + { + case WAIT_OBJECT_0: + break; + default: + panic("Semaphore error"); + } +#else + DECL_RESTARTABLE(rc); + DO_RESTARTABLE(rc, sem_wait(semaphore)); + if (rc != 0) panic("Semaphore error"); +#endif + + /* we do not need explicit MB here since it is expected to be performed + by OS-level synchronization primitives above */ + + owning_thread = this_thread; + recursion_count = 1; + + /* record performance counters */ + if (unlikely(perf_collect)) + { + UINT64_INC(perf_lock_count); + if (UINT64_ISMAX(perf_lock_count)) + perf_collect = FALSE; + UINT64_INC(perf_syswait_count); + } + } +} + +void smp_lock_impl::calibrate_spinloop() +{ + smp_var(lock_count) = 0; + calibrating_spinloop = TRUE; + lock(); + calibrating_spinloop = FALSE; + smp_var(lock_count) = -1; +} + +void smp_lock_impl::perf_acquired(uint32 cycles) +{ + UINT64_INC(perf_lock_count); + if (UINT64_ISMAX(perf_lock_count)) + perf_collect = FALSE; + + if (cycles == spin_count) + { + /* no-wait case */ + UINT64_INC(perf_nowait_count); + } + else + { + /* number of previous wait cases */ + double prev_spinwait_count = (UINT64_TO_DOUBLE(perf_lock_count) - 1.0) - UINT64_TO_DOUBLE(perf_nowait_count) - UINT64_TO_DOUBLE(perf_syswait_count); + perf_avg_spinwait = (prev_spinwait_count * perf_avg_spinwait + (double) (spin_count - cycles)) / (prev_spinwait_count + 1.0); + } +} + +void smp_lock_impl::unlock() +{ + if (0 != --recursion_count) + { + interlocked_decl(smp_var(lock_count)); + } + else + { + owning_thread = 0; + smp_pre_interlocked_mb(); + if (interlocked_decl_is_geq(smp_var(lock_count))) + { +#if defined(_WIN32) + if (! ReleaseSemaphore(semaphore, 1, NULL)) + panic("Semaphore error"); +#else + DECL_RESTARTABLE(rc); + DO_RESTARTABLE(rc, sem_post(semaphore)); + if (rc != 0) panic("Semaphore error"); +#endif + } + } + + if (criticality != SIM_LOCK_CRITICALITY_NONE) + critical_unlock(criticality); +} +#endif + +void smp_lock::calibrate() +{ + smp_lock_impl::calibrate(); +} + +uint32 smp_lock_impl::spins_per_ms = 0; + +static t_bool is_calibration_stable(const uint32* v, uint32 nsamples, uint32* pminv); + +#define CALIBR_MIN_SAMPLES 25 +#define CALIBR_MAX_SAMPLES 200 +#define CALIBR_TOLERANCE_RANGE 0.25 +#define CALIBR_MIN_WITHIN_TR 5 + +void smp_lock_impl::calibrate() +{ + smp_lock_impl* lck = (smp_lock_impl*) smp_lock::create(); + sim_delta_timer* tmr = sim_delta_timer::create(); + uint32 spincount; + uint32 spin_us = 0; /* initialize to suppress false GCC warning */ + uint32 valid = FALSE; + + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_CALIBRATION); + + /* increase spncount until loop exceeds 5 ms */ + for (spincount = 1000; ; spincount *= 2) + { + lck->set_spin_count(spincount); + tmr->begin(RUN_PASS_NULL); + lck->calibrate_spinloop(); + tmr->sample(RUN_PASS_NULL); + if (tmr->us_since_start(RUN_PASS_NULL) > 5000) + break; + } + + /* + * Sometimes calibraton may fail due to spurious reasons, such as external load + * or cache flush/cold/warm effects. Try thrice before giving up. + */ + for (int pass = 0; pass <= 2; pass++) + { + uint32 us[CALIBR_MAX_SAMPLES]; + int nsamples; + for (nsamples = 0; nsamples < CALIBR_MAX_SAMPLES; ) + { + tmr->begin(RUN_PASS_NULL); + lck->calibrate_spinloop(); + tmr->sample(RUN_PASS_NULL); + us[nsamples++] = tmr->us_since_start(RUN_PASS_NULL); + if (nsamples >= CALIBR_MIN_SAMPLES && is_calibration_stable(us, nsamples, & spin_us)) + { + valid = TRUE; + break; + } + } + } + + if (valid) + { + double f = 1000.0 * (double) spincount / (double) spin_us; + spins_per_ms = (uint32) floor(f); + if (f - spins_per_ms >= 0.5) spins_per_ms++; + /* sanity */ + if (spins_per_ms >= 0x10000000) spins_per_ms = 0; + } + + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CONSOLE_PAUSED); + delete lck; + delete tmr; +} + +static t_bool is_calibration_stable(const uint32* v, uint32 nsamples, uint32* pminv) +{ + uint32 minv = v[0]; + uint32 mink = 0; + uint32 in_tolerance_range = 0; + uint32 k; + + /* find minimum */ + for (k = 1; k < nsamples; k++) + { + if (v[k] < minv) + { + *pminv = minv = v[mink = k]; + } + } + + /* count samples close to minimum */ + for (k = 0; k < nsamples; k++) + { + if (k != mink && v[k] <= minv * (1.0 + CALIBR_TOLERANCE_RANGE)) + { + if (++in_tolerance_range >= CALIBR_MIN_WITHIN_TR) + return TRUE; + } + } + + return FALSE; +} + +/* ============================================ sim_DebugBreak helper ============================================ */ + +#if defined(_WIN32) && defined(__x86_32__) +// sim_DebugBreak() is defined inline +#elif defined(__GNUC__) && (defined(__x86_32__) || defined(__x86_64__)) +// sim_DebugBreak() is defined inline +#elif defined(_WIN32) && defined(__x86_64__) +void sim_DebugBreak() +{ + DebugBreak(); +} +#elif defined(__linux) || defined(__APPLE__) +void sim_DebugBreak() +{ + raise(SIGTRAP); +} +#elif defined(__GNUC__) +t_bool sim_DebugBreak_DoIt = TRUE; +void sim_DebugBreak() +{ + if (sim_DebugBreak_DoIt) + __builtin_trap(); +} +#else +int dzro_res; +int dzro_zero = 0; +void sim_DebugBreak() +{ + dzro_res = 1000 / dzro_zero; +} +#endif + +/* ============================================ Linux - specific ============================================ */ + +#if defined(__linux) +/* + * Check whether we are running 1x1 NPTL threads, rather than old user-mode LinuxThreads library. + */ +static t_bool check_nptl() +{ + size_t n = confstr(_CS_GNU_LIBPTHREAD_VERSION, NULL, 0); + if (n > 0) + { + char* buf = (char*) alloca(n + 10); + confstr(_CS_GNU_LIBPTHREAD_VERSION, buf, n); + if (strstr(buf, "NPTL")) + return TRUE; + } + return FALSE; +} +#endif + +/* ==================================== Windows x86 native interlocked ==================================== */ + +#if defined(__x86_32__) || defined(__x86_64__) +# define X86_EFLAGS_V_CF 0 +# define X86_EFLAGS_V_ZF 6 +# define X86_EFLAGS_V_SF 7 +# define X86_EFLAGS_V_OF 11 +# define CF_FROM_EFLAGS(cc, eflags) \ + cc |= ((eflags >> X86_EFLAGS_V_SF) & 1) << CC_V_N; \ + cc |= ((eflags >> X86_EFLAGS_V_ZF) & 1) << CC_V_Z; \ + cc |= ((eflags >> X86_EFLAGS_V_OF) & 1) << CC_V_V; \ + cc |= ((eflags >> X86_EFLAGS_V_CF) & 1) << CC_V_C; +#endif + +#if SMP_NATIVE_INTERLOCKED && defined(_WIN32) && defined(__x86_32__) +/* + * Adds "addendum" to "*psum" in a manner consistent with ADAWI and returns VAX CC. + * "*psum" is guaranteed to be in VAX memory space and aligned on a word boundary. + */ +int32 smp_native_adawi(volatile void* memory, int32 pa_sum, uint16 addend) +{ + uint32 eflags; + + volatile t_byte* psum = (volatile t_byte*) memory; + psum += pa_sum; + + smp_pre_interlocked_mb(); + + __asm + { + mov ax, addend + mov edx, psum + lock add word ptr [edx], ax + pushfd + pop eax + mov eflags, eax + } + + smp_post_interlocked_mb(); + + int32 cc = 0; + CF_FROM_EFLAGS(cc, eflags); + + return cc; +} + +/* + * interlocked CAS on byte argument: + * if (*p == old_value) *p = new_value + * in all cases return original value of *p + */ +static t_byte cas_byte(volatile t_byte* p, t_byte old_value, t_byte new_value) +{ + t_byte res = old_value; + + COMPILER_BARRIER; + + __asm + { + mov al, old_value + mov bl, new_value + mov edx, p + lock cmpxchg byte ptr [edx], bl + je matched + mov res, al +matched: + } + + COMPILER_BARRIER; + + return res; +} +#endif + +/* ==================================== Windows x64 native interlocked ==================================== */ + +#if SMP_NATIVE_INTERLOCKED && defined(_WIN32) && defined(__x86_64__) +extern "C" +{ +extern t_uint64 __fastcall x64_native_adawi(volatile uint16* pval, uint16 addend); +extern t_byte __fastcall x64_cas_byte(volatile t_byte* p, t_byte old_value, t_byte new_value); +} + +int32 smp_native_adawi(volatile void* memory, int32 pa_sum, uint16 addend) +{ + t_uint64 rflags; + uint32 eflags; + + volatile t_byte* psum = (volatile t_byte*) memory; + psum += pa_sum; + + smp_pre_interlocked_mb(); + rflags = x64_native_adawi((volatile uint16*) psum, addend); + smp_post_interlocked_mb(); + + eflags = (uint32) rflags; + int32 cc = 0; + CF_FROM_EFLAGS(cc, eflags); + + return cc; +} + +static t_byte cas_byte(volatile t_byte* p, t_byte old_value, t_byte new_value) +{ + COMPILER_BARRIER; + t_byte res = x64_cas_byte(p, old_value, new_value); + COMPILER_BARRIER; + return res; +} +#endif + +/* ===================================== Linux/OSX x86 native interlocked ===================================== */ + +#if SMP_NATIVE_INTERLOCKED && (defined(__linux) || defined(__APPLE__)) && defined(__x86_32__) + +int32 smp_native_adawi(volatile void* memory, int32 pa_sum, uint16 addend) +{ + uint32 eflags; + + volatile t_byte* psum = (volatile t_byte*) memory; + psum += pa_sum; + + smp_pre_interlocked_mb(); + + __asm__ __volatile__ ( + "lock; addw %1, (%2)\n\t" + "pushfl\n\t" + "popl %0\n\t" + : "=g" (eflags) + : "Q" (addend), "r" (psum) + : "memory", "cc" + ); + + smp_post_interlocked_mb(); + + int32 cc = 0; + CF_FROM_EFLAGS(cc, eflags); + + return cc; +} + +static t_byte cas_byte(volatile t_byte* p, t_byte old_value, t_byte new_value) +{ + COMPILER_BARRIER; + t_byte res = __sync_val_compare_and_swap(p, old_value, new_value); + COMPILER_BARRIER; + return res; +} + +#endif + +/* ===================================== Linux/OSX x64 native interlocked ===================================== */ + +#if SMP_NATIVE_INTERLOCKED && (defined(__linux) || defined(__APPLE__)) && defined(__x86_64__) + +int32 smp_native_adawi(volatile void* memory, int32 pa_sum, uint16 addend) +{ + t_uint64 rflags; + + volatile t_byte* psum = (volatile t_byte*) memory; + psum += pa_sum; + + smp_pre_interlocked_mb(); + + __asm__ __volatile__ ( + "lock; addw %1, (%2)\n\t" + "pushfq\n\t" + "popq %0\n\t" + : "=g" (rflags) + : "Q" (addend), "r" (psum) + : "memory", "cc" + ); + + smp_post_interlocked_mb(); + + uint32 eflags = (uint32) rflags; + int32 cc = 0; + CF_FROM_EFLAGS(cc, eflags); + + return cc; +} + +static t_byte cas_byte(volatile t_byte* p, t_byte old_value, t_byte new_value) +{ + COMPILER_BARRIER; + t_byte res = __sync_val_compare_and_swap(p, old_value, new_value); + COMPILER_BARRIER; + return res; +} + +#endif + +/* ================================= native interlocked -- all platforms ================================= */ + +#if SMP_NATIVE_INTERLOCKED && (defined(__x86_32__) || defined(__x86_64__)) +/* + * Native implementation for BBSSI and BBCCI instructions. + * + * On x86/x64 we cannot use BTS and BTR instructions since their smallest argument size is 16 bits, + * whereas VAX specification says that BBSSI and BBCCI affect only a single byte, therefore we have + * to use byte-sized CAS (CMPXCHG). + * + * "bit" is the bit number within the byte, 0...7 + * "set" is TRUE for BBSSI, FALSE for BBCCI + * + * Returns TRUE if the bit was originally set, FALSE if it was originally cleared. + * + */ +t_bool smp_native_bb(volatile void* memory, int32 pa, int32 bit, t_bool set) +{ + volatile t_byte* p = (volatile t_byte*) memory; + p += pa; + + t_byte old_value; + t_byte mask = (1u << bit); + + smp_pre_interlocked_mb(); + + old_value = *p; + + for (;;) + { + t_byte new_value = set ? (old_value | mask) : (old_value & ~mask); + + t_byte ov = cas_byte(p, old_value, new_value); + + if (ov == old_value) + { + smp_post_interlocked_mb(); + return (ov & mask) != 0; + } + else + { + old_value = ov; + } + } +} + +/* + * self-test primitives used for native-mode implementation of VAX interlocked instructions + */ +static void smp_native_self_test() +{ + static SIM_ALIGN_32 uint32 tval; + + tval = 0xFFFE; + CHECK(smp_native_adawi(& tval, 0, 1) == CC_N && tval == 0xFFFF); + CHECK(smp_native_adawi(& tval, 0, 1) == (CC_Z | CC_C) && tval == 0); + CHECK(smp_native_adawi(& tval, 0, 1) == 0 && tval == 1); + CHECK(smp_native_adawi(& tval, 0, 1) == 0 && tval == 2); + tval = 0x7000; + CHECK(smp_native_adawi(& tval, 0, 0x7000) == (CC_N | CC_V) && tval == 0xE000); + tval = 0xF000; + CHECK(smp_native_adawi(& tval, 0, 0xF000) == (CC_N | CC_C) && tval == 0xE000); + + tval = 0; + CHECK(0 == smp_native_bb(& tval, 3, 6, TRUE) && tval == 0x40000000); + CHECK(1 == smp_native_bb(& tval, 3, 6, TRUE) && tval == 0x40000000); + CHECK(1 == smp_native_bb(& tval, 3, 6, FALSE) && tval == 0); + CHECK(0 == smp_native_bb(& tval, 3, 6, FALSE) && tval == 0); + + return; + +cleanup: + panic("Self-test of native-mode interlocked primitives failed"); +} +#endif + +/* ==================================== sim_ws_setup -- all platforms ==================================== */ + +#if defined(_WIN32) +void sim_ws_setup() +{ + sim_ws_prefaulted = FALSE; + + /* default working set range from memsize + 40 MB to memsize + 100 MB */ + const uint32 mb = 1024 * 1024; + uint32 def_ws_min = (uint32) ((cpu_unit_0.capac + mb - 1) / mb) + 40; + uint32 def_ws_max = (uint32) ((cpu_unit_0.capac + mb - 1) / mb) + 100; + + if (sim_ws_lock && sim_ws_min == 0 && sim_ws_max == 0) + { + sim_ws_min = def_ws_min; + sim_ws_max = def_ws_max; + } + else if (sim_ws_min == 0 && sim_ws_max != 0) + { + sim_ws_min = imin(def_ws_min, sim_ws_max); + } + else if (sim_ws_min != 0 && sim_ws_max == 0) + { + sim_ws_max = imax(def_ws_max, sim_ws_min); + } + + if (sim_ws_min > sim_ws_max) + { + sim_ws_max = sim_ws_min; + smp_printf("Warning: WS_MAX was set below WS_MIN, increasing to %d MB\n", sim_ws_max); + if (sim_log) + fprintf(sim_log, "Warning: WS_MAX was set below WS_MIN, increasing to %d MB\n", sim_ws_max); + } + + if (sim_ws_min && sim_ws_max) + { + if (sizeof(void*) == 4 && sim_ws_max >= 3 * 1024 || + sizeof(void*) == 8 && sim_ws_max >= 300 * 1024) + { + smp_printf("Warning: requested working set size (%d MB) is too high, unable to set it\n", sim_ws_max); + if (sim_log) + fprintf(sim_log, "Warning: requested working set size (%d MB) is too high, unable to set it\n", sim_ws_max); + sim_ws_settings_changed = FALSE; + return; + } + + SIZE_T ws_min = sim_ws_min; + SIZE_T ws_max = sim_ws_max; + ws_min *= mb; + ws_max *= mb; + + if (! SetProcessWorkingSetSize(GetCurrentProcess(), ws_min, ws_max)) + { + smp_printf("Warning: Unable to set requested working set size (%d MB)\n", sim_ws_max); + if (sim_log) + fprintf(sim_log, "Warning: Unable to set requested working set size (%d MB)\n", sim_ws_max); + sim_ws_settings_changed = FALSE; + return; + } + + if (! sim_ws_prefaulted) + { + sim_prefault_memory(); + sim_ws_prefaulted = TRUE; + } + } + + sim_ws_settings_changed = FALSE; +} +#elif defined(__linux) +# include +void sim_ws_setup() +{ + sim_ws_prefaulted = FALSE; + + if (sim_ws_lock || sim_ws_min || sim_ws_max) + { + if (mlockall(MCL_CURRENT)) + { + smp_printf("Warning: Unable to lock %s in memory (not critical)\n", sim_name); + if (sim_log) + fprintf(sim_log, "Warning: Unable to lock %s in memory (not critical)\n", sim_name); + sim_ws_settings_changed = FALSE; + return; + } + } + else + { + munlockall(); + sim_ws_settings_changed = FALSE; + return; + } + + if (! sim_ws_prefaulted) + { + sim_prefault_memory(); + sim_ws_prefaulted = TRUE; + } + + sim_ws_settings_changed = FALSE; +} +#elif defined(__APPLE__) +void sim_ws_setup() +{ + if (sim_ws_min || sim_ws_max || sim_ws_lock) + { + if (! sim_ws_prefaulted) + { + sim_prefault_memory(); + sim_ws_prefaulted = TRUE; + } + } + else + { + sim_ws_prefaulted = FALSE; + } + + sim_ws_settings_changed = FALSE; +} +#else +void sim_ws_setup() +{ + sim_ws_settings_changed = FALSE; +} +#endif + +void sim_prefault_memory() +{ + cpu_prefault_memory(); +} diff --git a/src/sim_threads.h b/src/sim_threads.h new file mode 100644 index 0000000..760b626 --- /dev/null +++ b/src/sim_threads.h @@ -0,0 +1,1375 @@ +/* + * Threading support. + * + * While it would be desirable to define many functions and classes declared below inline, + * unfortunately SIMH code does not feel well about or other system-dependent headers + * included before SIMH headers, because of definition conflicts. + * + * This is the sole reason these facilities are defined not inline, but implmented in a separate source file, + * why we have to split smp_lock from smp_lock_impl, + * and why we consequentially use smp_lock::create() to create a critical section object + * instead of defining it statically, and why invocations of all functions defined in + * have to be made from a separate file and cannot be defined as a macro or inline function + * in header files. + */ + +#if !defined(__linux) && !defined(_WIN32) && !defined(__APPLE__) +# error "sim_threads: unsupported operating system" +#endif + +void init_threads_core(); +void init_threads_ext(); + +#if defined(_WIN32) && defined(_M_IX86) +# define __i386__ 1 +#endif + +#if defined(_WIN64) && defined(_M_X64) +# define __x86_64__ 1 +#endif + +#if defined(__i386__) +# define __x86_32__ 1 +#endif + +#if !defined(__x86_32__) && !defined(__x86_64__) +# error "sim_threads: unsupported target processor architecture" +#endif + +/* + * Define compiler reordering barrier + */ +#if defined(_WIN32) +# include + // void _ReadWriteBarrier(); + /* + * _ReadWriteBarrier() is a compiler-only barrier, + * it does not generate any memory barrier instructions for CPU hardware, + * it only prevents compiler from re-ordering instructions + * and makes compiler assume global variables might have been changed. + */ +# define COMPILER_BARRIER _ReadWriteBarrier() +#endif + +#if defined(__linux) || defined(__APPLE__) +# define COMPILER_BARRIER __asm__ __volatile__ ("" ::: "memory") +#endif + +/* compiler barrier */ +#define barrier() do { COMPILER_BARRIER; } while (0) +// void barrier(); + +/* + * SMP_NATIVE_INTERLOCKED defines whether native implementation for VAX interlocked + * instructions is available and should be compiled into the executable. Value is boolean (1/0). + * + * SMP_NATIVE_INTERLOCKED set to 1 does not mean that native interlock will actually + * be used, just that it is available as an option that can be toggled on at runtime. + * + * Actual use of native/portable interlock modes is controlled by flag "use_native_interlocked". + */ +#if !defined(SMP_NATIVE_INTERLOCKED) && (defined(__x86_32__) || defined(__x86_64__)) +# define SMP_NATIVE_INTERLOCKED 1 +#endif + +#if !defined(SMP_NATIVE_INTERLOCKED) +# define SMP_NATIVE_INTERLOCKED 0 +#endif + +int32 smp_native_adawi(volatile void* memory, int32 pa_sum, uint16 addend); +t_bool smp_native_bb(volatile void* memory, int32 pa, int32 bit, t_bool set); + +/* + * Defines whether employed x86/x64 compiler can emit code that utilizies SSE/3DNow + * non-temporal instructions, or whether non-temporal instructions are used by runtime libraries, + * without proper internal termination with LFENCE/SFENCE/MFENCE instructon. + * + * If non-temporal instructions are generated by compilers or used in runtime libraries + * without xFENCE termination, this termination has to be supplied by VAX MP as a part of + * memory barriers. In this case define SMP_X86_USING_NONTEMPORAL as 0. + * + * Otheriwse define SMP_X86_USING_NONTEMPORAL as 0. + * + * To the best of our knowledge: + * + * GLIBC uses non-termporal instructions but brackets then with fences. + * There was a bug in old GLIBC versions (prior to the fix in 2.5.24) when fences were not + * performed. + * + * GCC may emit non-termporal instructions but brackets then with fences. + * + * MSVC/CRTL 9.0 and Rtl functons invoked by CRTL in Windows XP do not use non-temporal + * instructions. We expect later versions either not use them or bracket then with + * fences, otherwise it would have been a gross bug. + * + */ +#if defined(__x86_32__) || defined(__x86_64__) +# define SMP_X86_USING_NONTEMPORAL 1 +#endif + +/* + * Maximum cache line size. + * + * For x86/x64 see "Intel 64 and IA-32 Architectures Software Developer’s Manual", + * Volume 3 (3A & 3B, System Programming Guide) + * Table 11-1 "Characteristics of the Caches, TLBs, Store Buffer, and + * Write Combining Buffer in Intel 64 and IA-32 Processors" + * and section 8.10.6.7 "Place Locks and Semaphores in Aligned, 128-Byte Blocks of Memory" + */ +#if defined(__x86_32__) || defined(__x86_64__) +# define SMP_MAXCACHELINESIZE 128 +#endif + +/* alignment and padding for cache-line aligned data */ +#if defined(_WIN32) +# define SIM_ALIGN_CACHELINE __declspec(align(SMP_MAXCACHELINESIZE)) +#elif defined(__GNUC__) +# define SIM_ALIGN_CACHELINE __attribute__ ((__aligned__ (SMP_MAXCACHELINESIZE))) +#else +# error Unsupported compiler +#endif + +/* memory barriers */ + +#define SMP_RMB (1 << 0) +#define SMP_WMB (1 << 1) +#define SMP_MB (SMP_RMB | SMP_WMB) + +#if defined(__x86_32__) || defined(__x86_64__) + extern void (*smp_rmb_p)(); + extern void (*smp_wmb_p)(); + extern void (*smp_mb_p)(); +# define smp_rmb() do { barrier(); (*smp_rmb_p)(); barrier(); } while (0) +# define smp_wmb() do { barrier(); (*smp_wmb_p)(); barrier(); } while (0) +# define smp_mb() do { barrier(); (*smp_mb_p)(); barrier(); } while (0) +#else + void smp_rmb(); + void smp_wmb(); + void smp_mb(); +#endif + +/* + * Read (atomic) memory location without memory barriers + * or waiting for cache coherency protocol to complete. + * + * Should be applied to atomic types only, properly aligned in memory. + * + */ +#define weak_read(m) (m) +#define weak_read_var(m) ((m).var) + +/* + * macros that define whether should execute memory barrier after host interlocked instructions, + * or these instructions do invoke the barrier by themselves + */ +#if (defined(__x86_32__) || defined(__x86_64__)) && (SMP_X86_USING_NONTEMPORAL == 0) +# define smp_pre_interlocked_wmb() barrier() +# define smp_post_interlocked_rmb() barrier() +# define smp_post_interlocked_wmb() barrier() +# define smp_pre_interlocked_mb() barrier() +# define smp_post_interlocked_mb() barrier() +#else +# define smp_pre_interlocked_wmb() smp_wmb() +# define smp_post_interlocked_rmb() smp_rmb() +# define smp_post_interlocked_wmb() smp_wmb() +# define smp_pre_interlocked_mb() smp_mb() +# define smp_post_interlocked_mb() smp_mb() +#endif + +/* thread meta-priorities */ +/* must be ordered in the order of increasing relative rank */ +typedef enum +{ + SIMH_THREAD_PRIORITY_INVALID = -1, /* priority not set or must be recalculated */ + SIMH_THREAD_PRIORITY_CPU_RUN = 0, /* normal priority for VCPU thread execution */ + SIMH_THREAD_PRIORITY_IOP = 1, /* IO processing thread (virtual disk, tape drive or network adapter) */ + SIMH_THREAD_PRIORITY_CONSOLE_PAUSED = 2, /* console thread while VCPUs are paused */ + SIMH_THREAD_PRIORITY_CONSOLE_RUN = 3, /* console thread while VCPUs are running */ + SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS = 4, /* VCPU thread inside OS critical section */ + SIMH_THREAD_PRIORITY_CPU_CRITICAL_OS_HI = 5, /* VCPU thread inside clock interrupt or IPI interrupt processing, + or any of these interrupts is pending */ + SIMH_THREAD_PRIORITY_CPU_CRITICAL_VM = 6, /* VCPU thread inside VM critical section */ + SIMH_THREAD_PRIORITY_CLOCK = 7, /* clock strobing thread */ + SIMH_THREAD_PRIORITY_CPU_CALIBRATION = 8 /* VCPU thread running calibration loop (using SSC clocks) */ +} +sim_thread_priority_t; + +/* thread types */ +typedef enum +{ + SIM_THREAD_TYPE_CONSOLE = 0, /* console thread */ + SIM_THREAD_TYPE_CPU = 1, /* VCPU thread */ + SIM_THREAD_TYPE_CLOCK = 2, /* HW clock strobing thread */ + SIM_THREAD_TYPE_IOP = 3 /* IO processor thread */ +} +sim_thread_type_t; + +typedef enum +{ + SIM_LOCK_CRITICALITY_NONE = 0, + SIM_LOCK_CRITICALITY_OS_HI = 1, + SIM_LOCK_CRITICALITY_VM = 2 +} +sim_lock_criticality_t; + +class sim_delta_timer; + +/* + * atomic access to variables: concurrent access retrives either old value or new value, + * but never a partial update (such as some bytes of previous value and other bytes of new value) + */ + +typedef SIM_ALIGN_32 volatile int32 atomic_int32; +typedef SIM_ALIGN_32 volatile uint32 atomic_uint32; +typedef SIM_ALIGN_32 volatile uint32 atomic_bool; + +/* + * Host system interlocked increment and CAS operations. + */ + +/* Basic data types for interlocked operations */ +typedef SIM_ALIGN_16 volatile uint16 smp_interlocked_uint16; +typedef SIM_ALIGN_32 volatile uint32 smp_interlocked_uint32; +typedef SIM_ALIGN_32 volatile int32 smp_interlocked_int32; +#if defined (__x86_64__) +typedef SIM_ALIGN_64 volatile t_uint64 smp_interlocked_uint64; +#endif + +/* + * Container data types for interlocked and atomic variables. + * + * Isolate each independently accessed interlocked and atomic variable in memory block aligned on cache line size + * and having a size of cache line, so when these variables are accessed by processors, they do + * not drag along data in neighbor memory locations and do not induce ping-pong cache coherency traffic + * on inter-processor interconnect (false sharing). + * + * Besides hurting performance on all processors, on some processors that implement LL/SC interlocked semantics + * interference of separate variables within cache line may cause LL/SC instructions being improperly aborted + * and having to retry them, possibly multiple times before they can succeed, making interlocked variable + * segregation even more critical. + * + * Furthermore, on Intel x86/x64 isolating interlocked variable secures it from sharing the cache line with + * a variable that may be accessed using non-temporal instructions, which would disrupt inter-processor cache + * synchronization. + */ +typedef SIM_ALIGN_CACHELINE volatile struct __tag_atomic_uint32_var +{ + atomic_uint32 var; + t_byte pad[SMP_MAXCACHELINESIZE - sizeof(atomic_uint32)]; +} +atomic_uint32_var; + +typedef SIM_ALIGN_CACHELINE volatile struct __tag_atomic_int32_var +{ + atomic_int32 var; + t_byte pad[SMP_MAXCACHELINESIZE - sizeof(atomic_int32)]; +} +atomic_int32_var; + +typedef SIM_ALIGN_CACHELINE volatile struct __tag_smp_interlocked_uint32_var +{ + smp_interlocked_uint32 var; + t_byte pad[SMP_MAXCACHELINESIZE - sizeof(smp_interlocked_uint32)]; +} +smp_interlocked_uint32_var; + +typedef SIM_ALIGN_CACHELINE volatile struct __tag_smp_interlocked_int32_var +{ + smp_interlocked_int32 var; + t_byte pad[SMP_MAXCACHELINESIZE - sizeof(smp_interlocked_int32)]; +} +smp_interlocked_int32_var; + +typedef SIM_ALIGN_CACHELINE volatile struct __tag_smp_interlocked_uint16_var +{ + smp_interlocked_uint16 var; + t_byte pad[SMP_MAXCACHELINESIZE - sizeof(smp_interlocked_uint16)]; +} +smp_interlocked_uint16_var; + +#if defined (__x86_64__) +typedef SIM_ALIGN_CACHELINE volatile struct __tag_smp_interlocked_uint64_var +{ + smp_interlocked_uint64 var; + t_byte pad[SMP_MAXCACHELINESIZE - sizeof(smp_interlocked_uint64)]; +} +smp_interlocked_uint64_var; +#endif + +#if __SIZEOF_POINTER__ == 8 +typedef smp_interlocked_uint64 smp_interlocked_addr_val; +typedef smp_interlocked_uint64_var smp_interlocked_addr_val_var; +#else +typedef smp_interlocked_uint32 smp_interlocked_addr_val; +typedef smp_interlocked_uint32_var smp_interlocked_addr_val_var; +#endif + + +#define atomic_var(x) ((x).var) +#define atomic_var_p(px) (& ((px)->var)) +#define smp_var_init(v) { (v) } + +#define smp_var(x) ((x).var) +#define smp_var_p(px) (& ((px)->var)) +#define atomic_var_init(v) { (v) } + +/*============================================= GNUC / Linux versions =============================================*/ + +#if defined(__GNUC__) + +SIM_INLINE static uint32 smp_interlocked_increment(smp_interlocked_uint32* p) +{ + COMPILER_BARRIER; + uint32 r = __sync_add_and_fetch(p, 1); + COMPILER_BARRIER; + return r; +} + +SIM_INLINE static uint32 smp_interlocked_decrement(smp_interlocked_uint32* p) +{ + COMPILER_BARRIER; + uint32 r = __sync_add_and_fetch(p, -1); + COMPILER_BARRIER; + return r; +} + +#if defined (__x86_64__) +SIM_INLINE static t_uint64 smp_interlocked_increment(smp_interlocked_uint64* p) +{ + COMPILER_BARRIER; + t_uint64 r = __sync_add_and_fetch(p, 1); + COMPILER_BARRIER; + return r; +} + +SIM_INLINE static t_uint64 smp_interlocked_decrement(smp_interlocked_uint64* p) +{ + COMPILER_BARRIER; + t_uint64 r = __sync_add_and_fetch(p, -1); + COMPILER_BARRIER; + return r; +} + +SIM_INLINE static t_uint64 smp_interlocked_cas(smp_interlocked_uint64* p, t_uint64 old_value, t_uint64 new_value) +{ + COMPILER_BARRIER; + t_uint64 res = __sync_val_compare_and_swap(p, old_value, new_value); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static t_bool smp_interlocked_cas_done(smp_interlocked_uint64* p, t_uint64 old_value, t_uint64 new_value) +{ + COMPILER_BARRIER; + t_bool res = __sync_bool_compare_and_swap(p, old_value, new_value); + COMPILER_BARRIER; + return res; +} +#endif + +SIM_INLINE static int32 smp_interlocked_cas(smp_interlocked_int32* p, int32 old_value, int32 new_value) +{ + COMPILER_BARRIER; + int32 res = __sync_val_compare_and_swap(p, old_value, new_value); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static uint32 smp_interlocked_cas(smp_interlocked_uint32* p, uint32 old_value, uint32 new_value) +{ + COMPILER_BARRIER; + uint32 res = __sync_val_compare_and_swap(p, old_value, new_value); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static uint16 smp_interlocked_cas(smp_interlocked_uint16* p, uint16 old_value, uint16 new_value) +{ + COMPILER_BARRIER; + uint16 res = __sync_val_compare_and_swap(p, old_value, new_value); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static t_byte smp_interlocked_cas(volatile t_byte* p, t_byte old_value, t_byte new_value) +{ + COMPILER_BARRIER; + t_byte res = __sync_val_compare_and_swap(p, old_value, new_value); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static t_bool smp_interlocked_cas_done(smp_interlocked_uint32* p, uint32 old_value, uint32 new_value) +{ + COMPILER_BARRIER; + t_bool res = __sync_bool_compare_and_swap(p, old_value, new_value); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static t_bool smp_interlocked_cas_done(smp_interlocked_uint16* p, uint16 old_value, uint16 new_value) +{ + COMPILER_BARRIER; + t_bool res = __sync_bool_compare_and_swap(p, old_value, new_value); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static t_bool smp_interlocked_cas_done(volatile t_byte* p, t_byte old_value, t_byte new_value) +{ + COMPILER_BARRIER; + t_bool res = __sync_bool_compare_and_swap(p, old_value, new_value); + COMPILER_BARRIER; + return res; +} + +#if defined (__x86_32__) || defined (__x86_64__) +SIM_INLINE static t_bool smp_test_set_bit(smp_interlocked_uint32* p, uint32 bit) +{ + t_byte result = FALSE; + + /* should output to "=g" (result), but GCC -O2 LTO fails to compile with error message + about unavailable register %sil/%dil */ + __asm__ __volatile__ ( + "lock; bts %1, (%2)\n\t" + "setc %0\n\t" + : "=a" (result) + : "q" (bit), "q" (p) + : "memory", "cc" + ); + + return result; +} + +SIM_INLINE static t_bool smp_test_set_bit(smp_interlocked_uint16* p, uint32 bit) +{ + t_byte result = FALSE; + + __asm__ __volatile__ ( + "lock; bts %x1, (%2)\n\t" + "setc %0\n\t" + : "=g" (result) + : "Q" (bit), "r" (p) + : "memory", "cc" + ); + + return result; +} + +SIM_INLINE static t_bool smp_test_clear_bit(smp_interlocked_uint32* p, uint32 bit) +{ + t_byte result = FALSE; + + /* should output to "=g" (result), but GCC -O2 LTO fails to compile with error message + about unavailable register %sil/%dil */ + __asm__ __volatile__ ( + "lock; btr %1, (%2)\n\t" + "setc %0\n\t" + : "=a" (result) + : "q" (bit), "q" (p) + : "memory", "cc" + ); + + return result; +} + +SIM_INLINE static t_bool smp_test_clear_bit(smp_interlocked_uint16* p, uint32 bit) +{ + t_byte result = FALSE; + + __asm__ __volatile__ ( + "lock; btr %x1, (%2)\n\t" + "setc %0\n\t" + : "=g" (result) + : "Q" (bit), "r" (p) + : "memory", "cc" + ); + + return result; +} + +/* + * smp_cpu_relax: + * + * Insert smp_cpu_relax into spin-wait loops to prevent generation of multiple (and useless) simultaneous out-of-order + * read requests. Effective on Pentium 4 and higher processors, NOP on earlier Intel processors and clones. + * See Intel application note AP-949 "Using Spin-Loops on Intel Pentium 4 Processor and Intel Xeon Processor", + * P/N 248674-002 (http://software.intel.com/file/25602). + * May also help when spin-waitng on a Hyper-Threaded processor by releasing shared resources to a thread + * doing useful work, perhaps a lock holder. + */ +#define smp_cpu_relax() do { __asm__ __volatile__ ("pause"); } while (0) +// #define smp_cpu_relax() do { __asm__ __volatile__ ("rep; nop"); } while (0) + +#endif + +/*============================================= Win32 x86 and x64 versions =============================================*/ + +#elif defined (_WIN32) + +#include +#pragma intrinsic (_InterlockedExchange, _InterlockedCompareExchange, _InterlockedCompareExchange16) +#pragma intrinsic (_InterlockedIncrement, _InterlockedDecrement) + +SIM_INLINE static int32 smp_interlocked_increment(smp_interlocked_int32* p) +{ + COMPILER_BARRIER; + int32 res = _InterlockedIncrement((long*) p); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static uint32 smp_interlocked_increment(smp_interlocked_uint32* p) +{ + COMPILER_BARRIER; + uint32 res = _InterlockedIncrement((long*) p); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static int32 smp_interlocked_decrement(smp_interlocked_int32* p) +{ + COMPILER_BARRIER; + int32 res = _InterlockedDecrement((long*) p); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static uint32 smp_interlocked_decrement(smp_interlocked_uint32* p) +{ + COMPILER_BARRIER; + uint32 res = _InterlockedDecrement((long*) p); + COMPILER_BARRIER; + return res; +} + +#if defined (__x86_64__) +#pragma intrinsic (_InterlockedIncrement64, _InterlockedDecrement64, _InterlockedCompareExchange64) +SIM_INLINE static t_uint64 smp_interlocked_increment(smp_interlocked_uint64* p) +{ + COMPILER_BARRIER; + t_uint64 res = _InterlockedIncrement64((__int64*) p); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static t_uint64 smp_interlocked_decrement(smp_interlocked_uint64* p) +{ + COMPILER_BARRIER; + t_uint64 res = _InterlockedDecrement64((__int64*) p); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static t_uint64 smp_interlocked_cas(smp_interlocked_uint64* p, t_uint64 old_value, t_uint64 new_value) +{ + COMPILER_BARRIER; + t_uint64 res = (t_uint64) _InterlockedCompareExchange64((__int64 volatile*) p, (__int64) new_value, (__int64) old_value); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static t_bool smp_interlocked_cas_done(smp_interlocked_uint64* p, t_uint64 old_value, t_uint64 new_value) +{ + return old_value == smp_interlocked_cas(p, old_value, new_value); +} +#endif + +// SIM_INLINE static uint32 smp_interlocked_xchg(smp_interlocked_uint32* p, uint32 new_value) +// { +// COMPILER_BARRIER; +// uint32 res = _InterlockedExchange((long*) p, (long) new_value); +// COMPILER_BARRIER; +// return res; +// } + +SIM_INLINE static uint32 smp_interlocked_cas(smp_interlocked_int32* p, int32 old_value, int32 new_value) +{ + COMPILER_BARRIER; + uint32 res = _InterlockedCompareExchange((long volatile*) p, (long) new_value, (long) old_value); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static uint32 smp_interlocked_cas(smp_interlocked_uint32* p, uint32 old_value, uint32 new_value) +{ + COMPILER_BARRIER; + uint32 res = _InterlockedCompareExchange((long volatile*) p, (long) new_value, (long) old_value); + COMPILER_BARRIER; + return res; +} + +SIM_INLINE static uint16 smp_interlocked_cas(smp_interlocked_uint16* p, uint16 old_value, uint16 new_value) +{ + COMPILER_BARRIER; + uint16 res = _InterlockedCompareExchange16((short volatile*) p, (short) new_value, (short) old_value); + COMPILER_BARRIER; + return res; +} + +#if defined(__x86_32__) +SIM_INLINE static t_byte smp_interlocked_cas(volatile t_byte* p, t_byte old_value, t_byte new_value) +{ + t_byte result; + + COMPILER_BARRIER; + __asm + { + mov al, old_value + mov edx, p + mov cl, new_value + lock cmpxchg byte ptr [edx], cl + mov result, al + } + COMPILER_BARRIER; + + return result; +} +#else +t_byte smp_interlocked_cas(volatile t_byte* p, t_byte old_value, t_byte new_value); +#endif + +SIM_INLINE static t_bool smp_interlocked_cas_done(smp_interlocked_uint32* p, uint32 old_value, uint32 new_value) +{ + return old_value == smp_interlocked_cas(p, old_value, new_value); +} + +SIM_INLINE static t_bool smp_interlocked_cas_done(smp_interlocked_uint16* p, uint16 old_value, uint16 new_value) +{ + return old_value == smp_interlocked_cas(p, old_value, new_value); +} + +SIM_INLINE static t_bool smp_interlocked_cas_done(volatile t_byte* p, t_byte old_value, t_byte new_value) +{ + return old_value == smp_interlocked_cas(p, old_value, new_value); +} + +#if defined(__x86_32__) +SIM_INLINE static t_bool smp_test_set_bit(smp_interlocked_uint16* p, uint32 bit) +{ + t_byte result = FALSE; + COMPILER_BARRIER; + _asm + { + mov ecx, p + mov ebx, bit + lock bts word ptr [ecx], bx + setc result + } + COMPILER_BARRIER; + return result; +} + +SIM_INLINE static t_bool smp_test_set_bit(smp_interlocked_uint32* p, uint32 bit) +{ + t_byte result = FALSE; + COMPILER_BARRIER; + _asm + { + mov ecx, p + mov ebx, bit + lock bts dword ptr [ecx], ebx + setc result + } + COMPILER_BARRIER; + return result; +} + +SIM_INLINE static t_bool smp_test_clear_bit(smp_interlocked_uint16* p, uint32 bit) +{ + t_byte result = FALSE; + COMPILER_BARRIER; + _asm + { + mov ecx, p + mov ebx, bit + lock btr word ptr [ecx], bx + setc result + } + COMPILER_BARRIER; + return result; +} + +SIM_INLINE static t_bool smp_test_clear_bit(smp_interlocked_uint32* p, uint32 bit) +{ + t_byte result = FALSE; + COMPILER_BARRIER; + _asm + { + mov ecx, p + mov ebx, bit + lock btr dword ptr [ecx], ebx + setc result + } + COMPILER_BARRIER; + return result; +} +#else +#pragma intrinsic (_interlockedbittestandset, _interlockedbittestandreset) +SIM_INLINE static t_bool smp_test_set_bit(smp_interlocked_uint32* p, uint32 bit) +{ + COMPILER_BARRIER; + t_bool r = _interlockedbittestandset((long*) p, (long) bit); + COMPILER_BARRIER; + return r; +} + +SIM_INLINE static t_bool smp_test_clear_bit(smp_interlocked_uint32* p, uint32 bit) +{ + COMPILER_BARRIER; + t_bool r = _interlockedbittestandreset((long*) p, (long) bit); + COMPILER_BARRIER; + return r; +} + +SIM_INLINE static t_bool smp_test_set_bit(smp_interlocked_uint16* p, uint32 bit); +SIM_INLINE static t_bool smp_test_clear_bit(smp_interlocked_uint16* p, uint32 bit); +#endif + +#if defined (__x86_32__) || defined (__x86_64__) +# pragma intrinsic (_mm_pause) + extern "C" void _mm_pause(void); +# define smp_cpu_relax() _mm_pause() +#endif + +#else + +/*====================================== for other platforms (not GNUC, not WIN32) ======================================*/ + +uint32 smp_interlocked_increment(smp_interlocked_uint32* p); +uint32 smp_interlocked_decrement(smp_interlocked_uint32* p); + +#if defined (__x86_64__) +t_uint64 smp_interlocked_increment(smp_interlocked_uint64* p); +t_uint64 smp_interlocked_decrement(smp_interlocked_uint64* p); +#endif + +#if !defined(smp_cpu_relax) +# define smp_cpu_relax() +#endif + +#endif + +/*=================================== end of machine-specific interlock op definitions ===================================*/ + +/* interlocked operations for variables packed in cacheline container */ +#define smp_interlocked_increment_var(p) smp_interlocked_increment(smp_var_p(p)) +#define smp_interlocked_decrement_var(p) smp_interlocked_decrement(smp_var_p(p)) +#define smp_interlocked_cas_var(p, old_value, new_value) smp_interlocked_cas(smp_var_p(p), (old_value), (new_value)) +#define smp_interlocked_cas_done_var(p, old_value, new_value) smp_interlocked_cas_done(smp_var_p(p), (old_value), (new_value)) + +/* number of available online physical processors on the host system */ +extern int smp_ncpus; + +/* number of SMT/HyperThreading units per processor core on the host system */ +extern int smp_nsmt_per_core; +extern t_bool smp_smt_factor_set; +extern double smp_smt_factor; + +/* based on expected cost of rescheduling and context switching, it is worthwhile to spin-wait on a lock + without yielding CPU for at least this number of microseconds */ +extern uint32 smp_spinwait_min_us; + +/* Helpers for locking/unlocking VM-critical objects */ +void critical_lock(sim_lock_criticality_t criticality); +void critical_unlock(sim_lock_criticality_t criticality); +#define vm_critical_lock() critical_lock(SIM_LOCK_CRITICALITY_VM) +#define vm_critical_unlock() critical_unlock(SIM_LOCK_CRITICALITY_VM) +#define os_hi_critical_lock() critical_lock(SIM_LOCK_CRITICALITY_OS_HI) +#define os_hi_critical_unlock() critical_unlock(SIM_LOCK_CRITICALITY_OS_HI) + +class smp_lock +{ +public: + virtual t_bool init(uint32 cycles = 0, t_bool dothrow = TRUE) = 0; + virtual t_bool init(uint32 us, uint32 min_cycles, uint32 max_cycles, t_bool dothrow = TRUE) = 0; + virtual void set_spin_count(uint32 cycles) = 0; + virtual void set_criticality(sim_lock_criticality_t criticality) = 0; + virtual void lock() = 0; + virtual void unlock() = 0; + virtual ~smp_lock() {}; + static smp_lock* create(uint32 cycles = 0, t_bool dothrow = TRUE); + static smp_lock* create(uint32 us, uint32 min_cycles, uint32 max_cycles, t_bool dothrow = TRUE); + + /* real-time calibration */ + static void calibrate(); + + /* performance counters */ + virtual void set_perf_collect(t_bool collect) {} + virtual void perf_reset() {} + virtual void perf_show(SMP_FILE* fp, const char* name) {} +}; + +class SIM_ALIGN_CACHELINE InterruptRegister +{ +protected: + /* dynamic part written to by other threads */ + smp_interlocked_uint32* irqs; /* irq bits set by devices and processors */ + smp_interlocked_uint32_var changed; /* marker: irqs may have changed */ + + /* dynamic part accessed locally, these variables should be updated if "changed" is set */ + uint32* local_irqs; /* local recent copy of "irqs" */ + + /* static (after init) part */ + uint32 lo_ipl; /* lowest IPL in irqs array */ + uint32 hi_ipl; /* highest IPL in irqs array */ + const uint32* devs_per_ipl; /* device count per each level */ + +public: + InterruptRegister(); + ~InterruptRegister(); + static void* operator new(size_t size) { return operator_new_aligned(size, SMP_MAXCACHELINESIZE); } + static void operator delete(void* p) { operator_delete_aligned(p); } + void init(uint32 lo_ipl, uint32 hi_ipl, const uint32* devs_per_ipl); + void reset(); + t_bool weak_changed() + { return weak_read_var(changed) != 0; } + SIM_INLINE t_bool cas_changed(t_bool old_value, t_bool new_value) + { return smp_interlocked_cas_done_var(& changed, (uint32) old_value, (uint32) new_value) ? old_value : !old_value; } + void copy_irqs_to_local(); + int32 highest_local_irql(); + t_bool is_local_int(uint32 ipl, uint32 dev); + void dismiss_int(RUN_DECL, uint32 ipl, uint32 dev); + void query_local_clk_ipi(t_bool* is_active_clk_interrupt, t_bool* is_active_ipi_interrupt); + t_bool set_int(uint32 ipl, uint32 dev, t_bool toself); + t_bool clear_int(uint32 ipl, uint32 dev, t_bool toself); + t_bool check_int_atipl_clr(RUN_DECL, uint32 ipl, uint32* int_dev); + t_bool query_syncw_sys(); + t_bool examine_int(uint32 ipl, uint32 dev); + // t_bool examine_int(uint32 ipl); + // t_bool check_int(uint32 ipl, uint32* int_ipl); + // t_bool check_int(uint32 ipl, uint32* int_ipl, uint32* int_dev); + void show_external(SMP_FILE* st, t_bool& none); + void show_local(SMP_FILE* st, t_bool& none); + +protected: + void show_aux(SMP_FILE* st, uint32* rqs, t_bool& none); + void show_aux_2(SMP_FILE* st, const char* intr, t_bool& none); +}; + +#if defined(_WIN32) +typedef uint32 /*DWORD*/ smp_tls_key; +t_bool smp_tls_alloc(smp_tls_key* key); +void tls_set_value(const smp_tls_key& key, void* value); +void* tls_get_value(const smp_tls_key& key); +#elif defined(__linux) || defined(__APPLE__) +# include +typedef pthread_key_t smp_tls_key; +SIM_INLINE static t_bool smp_tls_alloc(smp_tls_key* key) +{ + return 0 == pthread_key_create(key, NULL); +} +SIM_INLINE static void tls_set_value(smp_tls_key& key, void* value) +{ + pthread_setspecific(key, value); +} +SIM_INLINE static void* tls_get_value(smp_tls_key& key) +{ + return pthread_getspecific(key); +} +#endif + +t_bool check_aligned(void* p, uint32 alignment, t_bool dothrow = TRUE); +t_bool smp_check_aligned(const smp_interlocked_int32* p, t_bool dothrow = TRUE); +t_bool smp_check_aligned(const smp_interlocked_uint32* p, t_bool dothrow = TRUE); +t_bool smp_check_aligned(const smp_interlocked_int32_var* p, t_bool dothrow = TRUE); +t_bool smp_check_aligned(const smp_interlocked_uint32_var* p, t_bool dothrow = TRUE); +t_bool smp_check_aligned(const atomic_int32_var* p, t_bool dothrow = TRUE); +t_bool smp_check_aligned(const atomic_uint32_var* p, t_bool dothrow = TRUE); + +/* from the compiler's viewpoint, atomic_int32 is the same as smp_interlocked_int32 */ +// t_bool smp_check_aligned(const atomic_int32* p, t_bool dothrow = TRUE); + +#if defined (__x86_64__) +t_bool smp_check_aligned(const smp_interlocked_uint64* p, t_bool dothrow = TRUE); +t_bool smp_check_aligned(const smp_interlocked_uint64_var* p, t_bool dothrow = TRUE); +#endif + +#if defined(_WIN32) +typedef void* smp_pollable_handle_t; // HANDLE +#elif defined(__linux) || defined(__APPLE__) +typedef int smp_pollable_handle_t; +#endif + +#if defined(_WIN32) + typedef void* smp_thread_t; + typedef unsigned int (__stdcall *smp_thread_routine_t)(void*); +# define SMP_THREAD_NULL NULL +# define SMP_THREAD_ROUTINE_DECL unsigned int __stdcall +# define SMP_THREAD_ROUTINE_END return 0 +#elif defined(__linux) || defined(__APPLE__) + typedef pthread_t smp_thread_t; + typedef void* (*smp_thread_routine_t)(void*); +# define SMP_THREAD_NULL ((pthread_t) 0) +# define SMP_THREAD_ROUTINE_DECL void * +# define SMP_THREAD_ROUTINE_END do { pthread_exit(NULL); return NULL; } while (0) +#endif + +typedef enum +{ + SMP_AFFINITY_ALL = 0, + SMP_AFFINITY_PER_CORE = 1 +} +smp_affinity_kind_t; + +t_bool smp_thread_init(); +t_bool smp_create_thread(smp_thread_routine_t start_routine, void* arg, smp_thread_t* thread_handle, t_bool dothrow = TRUE); +t_bool smp_wait_thread(smp_thread_t thread_handle, t_value* exitcode = NULL, t_bool dothrow = TRUE); +t_bool smp_set_thread_priority(sim_thread_priority_t prio); +t_bool smp_set_thread_priority(smp_thread_t thread_th, sim_thread_priority_t prio); +void smp_set_thread_name(const char* name); +int smp_get_thread_os_priority(smp_thread_t thread_th); +t_bool smp_can_alloc_per_core(int nthreads); +void smp_set_affinity(smp_thread_t thread_th, smp_affinity_kind_t how); + +class smp_synch_object +{ +public: + smp_synch_object() {} + virtual ~smp_synch_object() {} + virtual void clear() = 0; + virtual void wait() = 0; + virtual t_bool trywait() = 0; + virtual void release(int count = 1) = 0; +}; + +class smp_pollable_synch_object : public smp_synch_object +{ +public: + smp_pollable_synch_object() {} + virtual ~smp_pollable_synch_object() {} + virtual smp_pollable_handle_t pollable_handle() = 0; + virtual const char* pollable_handle_op() = 0; +}; + +class smp_semaphore : public smp_pollable_synch_object +{ +public: + smp_semaphore() {} + virtual ~smp_semaphore() {} + static smp_semaphore* create(int initial_open_count, t_bool dothrow = TRUE); + virtual t_bool init(int initial_open_count, t_bool dothrow = TRUE) = 0; +}; + +#if defined(_WIN32) +typedef smp_semaphore smp_simple_semaphore; +#else +class smp_simple_semaphore : public smp_synch_object +{ +public: + smp_simple_semaphore() {} + virtual ~smp_simple_semaphore() {} + static smp_simple_semaphore* create(int initial_open_count, t_bool dothrow = TRUE); + virtual t_bool init(int initial_open_count, t_bool dothrow = TRUE) = 0; +}; +#endif + +class smp_barrier : public smp_synch_object +{ +public: + smp_barrier() {} + virtual ~smp_barrier() {} + static smp_barrier* create(int initial_count, t_bool dothrow = TRUE); + virtual void wait() = 0; + virtual t_bool init(int initial_count, t_bool dothrow = TRUE) = 0; + virtual t_bool set_count(int count, t_bool dothrow = TRUE) = 0; +}; + +class smp_pollable_console_keyboard : public smp_pollable_synch_object +{ +private: + static smp_pollable_console_keyboard* instance; +public: + smp_pollable_console_keyboard() {} + virtual ~smp_pollable_console_keyboard() {} + virtual smp_pollable_handle_t pollable_handle() = 0; + virtual const char* pollable_handle_op() = 0; + static smp_pollable_console_keyboard* get(); +}; + +int smp_wait_any(smp_pollable_synch_object** objects, int nobjects, int ms); +int smp_wait(smp_pollable_synch_object* object, int ms); + +#if defined(__linux) || defined(__APPLE__) +# define DECL_RESTARTABLE(rc) int rc; +# define DO_RESTARTABLE(rc, op) do {(rc) = (op);} while ((rc) == -1 && errno == EINTR) +# define DO_RVAL_RESTARTABLE(rc, op) do {(rc) = (op);} while ((rc) == EINTR) +#endif + +class smp_mutex +{ +public: + static smp_mutex* create(t_bool dothrow = TRUE); + virtual ~smp_mutex() {} + virtual void set_criticality(sim_lock_criticality_t criticality) = 0; + virtual void lock() = 0; + virtual void unlock() = 0; + virtual t_bool trylock() = 0; +}; + +class smp_condvar +{ +public: + static smp_condvar* create(t_bool dothrow = TRUE); + virtual ~smp_condvar() {} + virtual void prepareForWait() = 0; + virtual void wait(smp_mutex* mutex, t_bool reacquire_mutex) = 0; + virtual void signal(smp_mutex* mutex) = 0; +}; + +class smp_event +{ +public: + static smp_event* create(t_bool dothrow = TRUE); + virtual ~smp_event() {} + virtual void set() = 0; + virtual void clear() = 0; + virtual void wait() = 0; + virtual t_bool trywait() = 0; + virtual t_bool timed_wait(uint32 usec, uint32* p_actual_usec) = 0; + virtual void wait_and_clear() = 0; +}; + +void smp_show_thread_priority_info(SMP_FILE* st); + +/* =============================================== Internal definitions =============================================== */ + +#if defined(SIM_THREADS_H_FULL_INCLUDE) + +#if defined(_WIN32) +# define _WIN32_WINNT 0x0403 +# include +# include +# include +#elif (defined(__linux) || defined(__APPLE__)) && (defined(__x86_32__) || defined(__x86_64__)) +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +/* + * OS X does not support unnamed semaphores. Functions sem_init and sem_destroy are declared, but always + * return error code "Function not implemented". For this reason we have to use named semaphores under OS X + * and provide a wrapper that insulates between the use of unnamed semaphores under Linux and + * named semaphores under OS X. + */ +#if defined(__linux) +# define os_sem_declare(semaphore) \ + sem_t* semaphore; \ + sem_t semaphore##_holder +# define os_sem_decl_init(semaphore) \ + semaphore = NULL +# define os_sem_ptr(semaphore) \ + & (semaphore), &(semaphore##_holder) +#elif defined(__APPLE__) +# define SEM_MAX_NAME (8 + 8 + 16 + 10) +# define os_sem_declare(semaphore) \ + sem_t* semaphore; \ + char semaphore##_name[SEM_MAX_NAME] +# define os_sem_decl_init(semaphore) \ + do { semaphore = NULL; semaphore##_name[0] = '\0'; } while (0) +# define os_sem_ptr(semaphore) \ + & (semaphore), semaphore##_name +#endif + +#if (defined(__linux) || defined(__APPLE__)) && (defined(__x86_32__) || defined(__x86_64__)) +# define USE_SIMH_SMP_LOCK +#endif + +#if defined(_WIN32) +# define USE_SIMH_SMP_LOCK +#endif + +class SIM_ALIGN_CACHELINE smp_lock_impl : public smp_lock +{ +public: + smp_lock_impl(); + ~smp_lock_impl(); + t_bool init(uint32 cycles = 0, t_bool dothrow = TRUE); + t_bool init(uint32 us, uint32 min_cycles, uint32 max_cycles, t_bool dothrow = TRUE); + void set_spin_count(uint32 cycles); + void set_spin_count(uint32 us, uint32 min_cycles, uint32 max_cycles); + void set_criticality(sim_lock_criticality_t criticality) { this->criticality = criticality; } + void lock(); + void unlock(); + static void* operator new(size_t size) { return operator_new_aligned(size, SMP_MAXCACHELINESIZE); } + static void operator delete(void* p) { operator_delete_aligned(p); } + static void calibrate(); + +public: + static uint32 spins_per_ms; + +protected: + sim_lock_criticality_t criticality; + +#if defined(_WIN32) && !defined(USE_SIMH_SMP_LOCK) +protected: + SIM_ALIGN_32 CRITICAL_SECTION m_cs; + t_bool m_inited; + +#elif defined(USE_SIMH_SMP_LOCK) +public: + void set_perf_collect(t_bool collect); + void perf_reset(); + void perf_show(SMP_FILE* fp, const char* name); + +protected: + void perf_acquired(uint32 cycles); + +protected: + smp_interlocked_int32_var lock_count; + volatile int32 recursion_count; + volatile uint32 spin_count; +#if defined(_WIN32) + volatile DWORD owning_thread; + HANDLE semaphore; +#else + volatile pthread_t owning_thread; + os_sem_declare(semaphore); +#endif + t_bool inited; + +protected: + void calibrate_spinloop(); + t_bool calibrating_spinloop; + +protected: + t_bool perf_collect; + UINT64 perf_lock_count; + UINT64 perf_nowait_count; + UINT64 perf_syswait_count; + double perf_avg_spinwait; +#endif + + /* dummy padding to prevent other data falling into the same cache line as lock data */ + t_byte pad[SMP_MAXCACHELINESIZE]; +}; + +/* =============================================== Internal - Win32 =============================================== */ + +#if defined(_WIN32) +class smp_semaphore_impl : public smp_semaphore +{ +private: + volatile HANDLE hSemaphore; + +public: + smp_semaphore_impl(); + ~smp_semaphore_impl(); + t_bool init(int initial_open_count, t_bool dothrow = TRUE); + void clear(); + void wait(); + t_bool trywait(); + void release(int count = 1); + smp_pollable_handle_t pollable_handle(); + const char* pollable_handle_op(); +}; + +#define SMP_BARRIER_CYCLES 4 + +class smp_barrier_impl : public smp_barrier +{ +private: + volatile HANDLE hMutex; + volatile HANDLE hEvent; + volatile int barrier_count; + volatile int waiting_count; + volatile HANDLE hOldEvents[SMP_BARRIER_CYCLES]; + void dealloc(); + void rotate_events(); + +public: + smp_barrier_impl(); + virtual ~smp_barrier_impl(); + void wait(); + t_bool init(int initial_count, t_bool dothrow = TRUE); + t_bool set_count(int count, t_bool dothrow = TRUE); + void clear(); + t_bool trywait(); + void release(int count = 1); +}; + +class smp_mutex_impl: public smp_mutex +{ + friend class smp_condvar_impl; +private: + HANDLE hMutex; + sim_lock_criticality_t criticality; +public: + smp_mutex_impl(); + ~smp_mutex_impl(); + t_bool init(t_bool dothrow); + void set_criticality(sim_lock_criticality_t criticality); + void lock(); + void unlock(); + t_bool trylock(); +}; + +class smp_condvar_impl : public smp_condvar +{ +private: + HANDLE hEvent; +public: + smp_condvar_impl(); + ~smp_condvar_impl(); + t_bool init(t_bool dothrow); + void prepareForWait(); + void wait(smp_mutex* mutex, t_bool reacquire_mutex); + void signal(smp_mutex* mutex); +}; + +class smp_event_impl : public smp_event +{ +private: + HANDLE hEvent; + sim_delta_timer* delta_timer; +public: + smp_event_impl(); + ~smp_event_impl(); + t_bool init(t_bool dothrow); + void set(); + void clear(); + void wait(); + t_bool trywait(); + t_bool timed_wait(uint32 usec, uint32* p_actual_usec); + void wait_and_clear(); +}; + +// end of _WIN32 +#endif + +/* ============================================= Internal - Linux / OSX ============================================= */ + +#if defined(__linux) || defined(__APPLE__) + +#define PIPE_RD 0 +#define PIPE_WR 1 + +class smp_semaphore_impl : public smp_semaphore +{ +private: + int fd[2]; + +public: + smp_semaphore_impl(); + ~smp_semaphore_impl(); + t_bool init(int initial_open_count, t_bool dothrow = TRUE); + void clear(); + void wait(); + t_bool trywait(); + void release(int count = 1); + smp_pollable_handle_t pollable_handle(); + const char* pollable_handle_op(); +}; + +class smp_simple_semaphore_impl : public smp_simple_semaphore +{ +private: + t_bool inited; + os_sem_declare(semaphore); + +public: + smp_simple_semaphore_impl(); + ~smp_simple_semaphore_impl(); + t_bool init(int initial_open_count, t_bool dothrow = TRUE); + void clear(); + void wait(); + t_bool trywait(); + void release(int count = 1); +}; + +#define SMP_BARRIER_CYCLES 4 + +class smp_barrier_impl : public smp_barrier +{ +private: + pthread_mutex_t mutex; + pthread_cond_t cond[SMP_BARRIER_CYCLES]; + + t_bool mutex_inited; + t_bool cond_inited[SMP_BARRIER_CYCLES]; + + volatile int wait_index; + volatile int barrier_count; + volatile int waiting_count; + volatile uint32 wseq; + + void dealloc(); + +public: + smp_barrier_impl(); + virtual ~smp_barrier_impl(); + void wait(); + t_bool init(int initial_count, t_bool dothrow = TRUE); + t_bool set_count(int count, t_bool dothrow = TRUE); + void clear(); + t_bool trywait(); + void release(int count = 1); +}; + +class smp_mutex_impl: public smp_mutex +{ + friend class smp_condvar_impl; +private: + pthread_mutex_t mutex; + sim_lock_criticality_t criticality; + t_bool inited; +public: + smp_mutex_impl(); + ~smp_mutex_impl(); + t_bool init(t_bool dothrow); + void set_criticality(sim_lock_criticality_t criticality); + void lock(); + void unlock(); + t_bool trylock(); +}; + +class smp_condvar_impl : public smp_condvar +{ +private: + pthread_cond_t cond; + t_bool inited; + volatile uint32 wseq; +public: + smp_condvar_impl(); + ~smp_condvar_impl(); + t_bool init(t_bool dothrow); + void prepareForWait(); + void wait(smp_mutex* mutex, t_bool reacquire_mutex); + void signal(smp_mutex* mutex); +}; + +class smp_event_impl : public smp_event +{ +private: + pthread_mutex_t mutex; + pthread_cond_t cond; + t_bool inited; +#if defined(HAVE_POSIX_CLOCK_ID) + t_bool have_clock_id; + clockid_t clock_id; +#endif + volatile t_bool state; + volatile uint32 set_wseq; +public: + smp_event_impl(); + ~smp_event_impl(); + t_bool init(t_bool dothrow); + void set(); + void clear(); + void wait(); + t_bool trywait(); + t_bool timed_wait(uint32 usec, uint32* p_actual_usec); + void wait_and_clear(); +}; + +// end of __linux or __APPLE__ +#endif + +// end of SIM_THREADS_H_FULL_INCLUDE +#endif diff --git a/src/sim_threads2.h b/src/sim_threads2.h new file mode 100644 index 0000000..7d3aee1 --- /dev/null +++ b/src/sim_threads2.h @@ -0,0 +1,28 @@ +/* + * VAX interlocked instructions helper + */ +class InterlockedOpLock : public sim_try_auto_destructable +{ +public: + sim_try_volatile t_bool wmb; + + InterlockedOpLock(RUN_DECL, uint32 flags = 0); + ~InterlockedOpLock(); + void onDestroy(t_bool unregistered); + void virt_lock(int32 virt_addr, int32 acc) sim_try_volatile; + void phys_lock(int32 phys_addr) sim_try_volatile; + void prio_lock() sim_try_volatile; + void qxi_busy() sim_try_volatile; + void unlock() sim_try_volatile; + +private: + sim_try_volatile int lock_nesting_count; + sim_try_volatile int32 lock_index; + CPU_UNIT* sim_try_volatile cpu_unit; + sim_try_volatile t_bool sv_priority_stored; + sim_try_volatile sim_thread_priority_t sv_priority; + uint32 flags; + t_bool entered_temp_ilk; +}; + +#define IOP_ILK (1 << 0) \ No newline at end of file diff --git a/src/sim_timer.cpp b/src/sim_timer.cpp new file mode 100644 index 0000000..231c550 --- /dev/null +++ b/src/sim_timer.cpp @@ -0,0 +1,1961 @@ +/* sim_timer.c: simulator timer 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. + + 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 + 22-Mar-07 RMS Added sim_rtcn_init_all + 17-Oct-06 RMS Added idle support (based on work by Mark Pizzolato) + Added throttle support + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 02-Jan-04 RMS Split out from SCP + + This library includes the following routines: + + sim_timer_init - initialize timing system + sim_rtc_init - initialize calibration + sim_rtc_calb - calibrate clock + sim_timer_init - initialize timing system + sim_idle - virtual machine idle + sim_os_msec - return elapsed time in msec + sim_os_sleep - sleep specified number of seconds + sim_os_ms_sleep - sleep specified number of milliseconds + + The calibration, idle, and throttle routines are OS-independent; the _os_ + routines are not. +*/ + +#if defined (_WIN32) +# include +#endif + +#include "sim_defs.h" +#include + +#if defined(VM_VAX_MP) +# define SIM_NO_THROTTLING +#endif + +t_bool sim_idle_enab = FALSE; /* global flag */ + +#if defined(HAVE_POSIX_CLOCK_ID) +/* Linux and Unix clocks */ +t_bool sim_posix_have_clock_id = FALSE; +clockid_t sim_posix_clock_id; +#endif + +static uint32 sim_idle_rate_us = 0; +uint32 sim_idle_stable = SIM_IDLE_STDFLT; +static uint32 sim_throt_ms_start = 0; +static uint32 sim_throt_ms_stop = 0; +static uint32 sim_throt_type = SIM_THROT_NONE; +static uint32 sim_throt_val = 0; +static uint32 sim_throt_state = 0; +static int32 sim_throt_wait = 0; +extern int32 sim_switches; +extern SMP_FILE *sim_log; + +/* CPU rate (instructions/second) averaging data */ +smp_lock* cpu_cycles_per_second_lock = NULL; +atomic_uint32_var cpu_cycles_per_second = atomic_var_init(0); +static cpu_set av_valid; +static uint32 av_checkin[SIM_MAX_CPUS]; +static uint32 av_ips[SIM_MAX_CPUS]; + +t_stat sim_throt_svc (RUN_SVC_DECL, UNIT *uptr); +static int32 sim_rtcn_calb_synclk (RUN_DECL, int32 ticksper, int32 tmr, t_bool* valid, uint32* os_msec); +static int32 sim_rtcn_calb_nosynclk (RUN_DECL, int32 ticksper, int32 tmr, t_bool* valid, uint32* os_msec); + +UNIT sim_throt_unit UDATA_SINGLE (&sim_throt_svc, 0, 0); + +/* OS-dependent timer and clock routines */ + +class sim_delta_timer_impl; + +class sim_delta_timer_sample +{ + friend class sim_delta_timer_impl; + +protected: + uint32 us_since(RUN_DECL, sim_delta_timer_impl* dtimer, sim_delta_timer_sample* prev); + +protected: + t_bool valid; + uint32 cycles; + uint32 min_since; + +#if defined(_WIN32) + LARGE_INTEGER pc; + uint32 ms; +#elif defined(HAVE_POSIX_CLOCK_ID) + struct timespec tsv; +#else + struct timeval tv; +#endif +}; + +class sim_delta_timer_impl : public sim_delta_timer +{ + friend class sim_delta_timer_sample; +protected: +#if defined(_WIN32) + LARGE_INTEGER performance_counter_frequency; +#endif + sim_delta_timer_sample start; + sim_delta_timer_sample last; + sim_delta_timer_sample curr; + void sample(RUN_DECL, sim_delta_timer_sample* sample); + +public: + sim_delta_timer_impl(); + ~sim_delta_timer_impl(); + t_bool init(t_bool dothrow = TRUE); + void adjust_cycle_base(uint32 d); + void begin(RUN_DECL); + void sample(RUN_DECL); + uint32 us_since_start(RUN_DECL); + uint32 us_since_last(RUN_DECL); +}; + +sim_delta_timer* sim_delta_timer::create(t_bool dothrow) +{ + sim_delta_timer_impl* dt = new sim_delta_timer_impl(); + if (! dt->init(dothrow)) + { + delete dt; + dt = NULL; + } + return dt; +} + +/* VMS */ + +#if defined (VMS) + +#if defined (__VAX) +#define sys$gettim SYS$GETTIM +#endif + +#include +#include +#include + +const t_bool rtc_avail = TRUE; + +uint32 sim_os_msec () +{ + uint32 quo, htod, tod[2]; + int32 i; + + sys$gettim (tod); /* time 0.1usec */ + + /* To convert to msec, must divide a 64b quantity by 10000. This is actually done + by dividing the 96b quantity 0'time by 10000, producing 64b of quotient, the + high 32b of which are discarded. This can probably be done by a clever multiply... + */ + + quo = htod = 0; + for (i = 0; i < 64; i++) /* 64b quo */ + { + htod = (htod << 1) | ((tod[1] >> 31) & 1); /* shift divd */ + tod[1] = (tod[1] << 1) | ((tod[0] >> 31) & 1); + tod[0] = tod[0] << 1; + quo = quo << 1; /* shift quo */ + if (htod >= 10000) /* divd work? */ + { + htod = htod - 10000; /* subtract */ + quo = quo | 1; /* set quo bit */ + } + } + return quo; +} + +void sim_os_sleep (unsigned int sec) +{ + sleep (sec); +} + +uint32 sim_os_ms_sleep_init (void) +{ +#if defined (__VAX) + return 10; /* VAX/VMS is 10ms */ +#else + return 1; /* Alpha/VMS is 1ms */ +#endif +} + +uint32 sim_os_ms_sleep (unsigned int msec) +{ + uint32 stime = sim_os_msec (); + uint32 qtime[2]; + int32 nsfactor = -10000; + static int32 zero = 0; + + lib$emul (&msec, &nsfactor, &zero, qtime); + sys$setimr (2, qtime, 0, 0); + sys$waitfr (2); + return sim_os_msec () - stime; +} + +/* Win32 routines */ + +#elif defined (_WIN32) + +const t_bool rtc_avail = TRUE; +static void sim_os_gettime_vms_init(); + +uint32 sim_os_msec () +{ + if (sim_idle_rate_us) + return timeGetTime (); + else + return GetTickCount (); +} + +void sim_os_sleep (unsigned int sec) +{ + Sleep (sec * 1000); +} + +static void sim_timer_exit (void) +{ + timeEndPeriod (sim_idle_rate_us / 1000); +} + +uint32 sim_os_us_sleep_init (void) +{ + TIMECAPS timers; + + sim_os_gettime_vms_init(); + + if (timeGetDevCaps (&timers, sizeof (timers)) != TIMERR_NOERROR) + return 0; + if ((timers.wPeriodMin == 0) || (timers.wPeriodMin > SIM_IDLE_MAX)) + return 0; + if (timeBeginPeriod (timers.wPeriodMin) != TIMERR_NOERROR) + return 0; + atexit (sim_timer_exit); + Sleep (1); + Sleep (1); + Sleep (1); + Sleep (1); + Sleep (1); + return timers.wPeriodMin * 1000; /* sim_idle_rate_us */ +} + +uint32 sim_os_ms_sleep (unsigned int msec) +{ + uint32 stime = sim_os_msec(); + Sleep (msec); + return sim_os_msec () - stime; +} + +sim_delta_timer_impl::sim_delta_timer_impl() +{ + start.valid = FALSE; + last.valid = FALSE; + curr.valid = FALSE; +} + +sim_delta_timer_impl::~sim_delta_timer_impl() +{ +} + +t_bool sim_delta_timer_impl::init(t_bool dothrow) +{ + if (QueryPerformanceFrequency(& performance_counter_frequency)) + { + return TRUE; + } + else if (dothrow) + { + panic("Unable to create delta timer"); + never_returns_bool_t; + } + else + { + return FALSE; + } +} + +#define LARGE_INTEGER_SET_ZERO(a) do { (a).LowPart = 0; (a).HighPart = 0; } while (0) +#define LARGE_INTEGER_TO_UINT64(a) ((((t_uint64) (uint32) (a).HighPart) << 32) | (t_uint64) (uint32) (a).LowPart) + +void sim_delta_timer_impl::sample(RUN_DECL, sim_delta_timer_sample* sample) +{ + LARGE_INTEGER_SET_ZERO(sample->pc); + QueryPerformanceCounter(& sample->pc); + sample->ms = sim_os_msec(); + if (cpu_unit) + sample->cycles = CPU_CURRENT_CYCLES; + sample->valid = TRUE; + sample->min_since = 0; +} + +void sim_delta_timer_impl::begin(RUN_DECL) +{ + curr.valid = FALSE; + last.valid = FALSE; + sample(RUN_PASS, &start); +} + +void sim_delta_timer_impl::sample(RUN_DECL) +{ + if (curr.valid) + last = curr; + sample(RUN_PASS, &curr); +} + +void sim_delta_timer_impl::adjust_cycle_base(uint32 d) +{ + start.cycles += d; + last.cycles += d; + curr.cycles += d; +} + +uint32 sim_delta_timer_impl::us_since_start(RUN_DECL) +{ + return curr.us_since(RUN_PASS, this, &start); +} + +uint32 sim_delta_timer_impl::us_since_last(RUN_DECL) +{ + if (last.valid) + return curr.us_since(RUN_PASS, this, &last); + else + return curr.us_since(RUN_PASS, this, &start); +} + +uint32 sim_delta_timer_sample::us_since(RUN_DECL, sim_delta_timer_impl* dtimer, sim_delta_timer_sample* prev) +{ + uint32 res; + uint32 x_pc; + + uint32 x_ms = 1000 * (ms - prev->ms); + + t_uint64 pc1 = LARGE_INTEGER_TO_UINT64(pc); + t_uint64 pc0 = LARGE_INTEGER_TO_UINT64(prev->pc); + + if (pc1 < pc0) + x_pc = 0; + else + x_pc = (uint32) ((double) (pc1 - pc0) * 1000.0 * 1000.0 / + (double) LARGE_INTEGER_TO_UINT64(dtimer->performance_counter_frequency)); + + /* + * Data returned by QueryPerformanceCounter is more precise (has higher temporal resolution) _when_ it is valid, + * however validity is exactly the problem. QueryPerformanceCounter may utilize, under the hood, variours kinds + * of clocks, such as local APIC, HPET, CPU (RD)TSC, programmable interval timer (PIT), ACPI PM or good old RTC + * timers. Depending on the clock used and operating system version, some timers may be unreliable and can behave + * erratically, for example if QueryPerformanceCounter relies on CPU TSC timer, and the thread is migrated + * for execution to another processor, RDTSC value can jump, including jumping backwards; furthermore, + * if processor enters power-saving mode, CPU TSC clock can throttle down on earlier CPUs, but not on later models + * (that have TSC INVARIANT feature), or likewise can speed up during turbo boost on earlier models, but not + * TSC INVARIANT models, and when it does slow-down, the amount can depend on the Cx/Px mode and its duration + * (which we do not have access to in any event), and similarly for speed-ups; futhermore, some versions of Windows + * may compensate for the frequency drift, whereas others do not. + * + * Multimedia clocks utlizied by sim_os_msec (timeGetTime), by comparison, are generally more reliable, + * but have lower resolution. + * + * Hence we use QueryPerformanceCounter readings when they seem to be valid (in rough agreement with sim_os_msec), + * otherwise fallback to the value provided by sim_os_msec. + */ + + if (x_ms == 0) + { + if (x_pc <= 1500) + res = (uint32) x_pc; + else + res = 2; + } + else if (x_pc >= x_ms / 10 && x_pc < 2 * x_ms + x_ms / 2) + { + res = (uint32) x_pc; + } + else + { + res = x_ms; + } + + if (res < prev->min_since) + res = prev->min_since; + + prev->min_since = res; + + return res; +} + +#undef LARGE_INTEGER_SET_ZERO +#undef LARGE_INTEGER_TO_UINT64 + +/* + * Get host system local time in VMS format. + * + * VMS time format is the number of 100-nanosecond intervals + * since 00:00 o’clock, November 17, 1858. + */ +static t_bool sim_os_gettime_vms_inited = FALSE; +static t_int64 vms_win_delta; + +SIM_INLINE static t_uint64 FileTimeToUInt64(const FILETIME* p) +{ + t_uint64 res = p->dwHighDateTime; + res = (res << 32) | p->dwLowDateTime; + return res; +} + +static void sim_os_gettime_vms_init() +{ + if (! sim_os_gettime_vms_inited) + { + SYSTEMTIME vmsBaseTime; + FILETIME vmsBaseFile; + + /* November 17, 1858 */ + memset(& vmsBaseTime, 0, sizeof vmsBaseTime); + vmsBaseTime.wYear = 1858; + vmsBaseTime.wMonth = 11; + vmsBaseTime.wDay = 17; + vmsBaseTime.wDayOfWeek = 3; + SystemTimeToFileTime(& vmsBaseTime, & vmsBaseFile); + + vms_win_delta = (t_int64) FileTimeToUInt64(& vmsBaseFile); + + sim_os_gettime_vms_inited = TRUE; + } +} + +void sim_os_gettime_vms(uint32* p_vms_time) +{ + SYSTEMTIME localTime; + FILETIME localFile; + GetLocalTime(& localTime); + SystemTimeToFileTime(& localTime, & localFile); + t_int64 local_time = (t_int64) FileTimeToUInt64(& localFile); + local_time -= vms_win_delta; + t_uint64 vms_time = (t_uint64) local_time; + p_vms_time[0] = (uint32) (vms_time & 0xFFFFFFFF); + p_vms_time[1] = (uint32) ((vms_time >> 32) & 0xFFFFFFFF); +} + +/* OS/2 routines, from Bruce Ray */ + +#elif defined (__OS2__) + +const t_bool rtc_avail = FALSE; + +uint32 sim_os_msec () +{ + return 0; +} + +void sim_os_sleep (unsigned int sec) +{ + return; +} + +uint32 sim_os_ms_sleep_init (void) +{ + return FALSE; +} + +uint32 sim_os_ms_sleep (unsigned int msec) +{ + return 0; +} + +/* Metrowerks CodeWarrior Macintosh routines, from Ben Supnik */ + +#elif defined (__MWERKS__) && defined (macintosh) + +#include +#include +#include +#include +#include +#define NANOS_PER_MILLI 1000000 +#define MILLIS_PER_SEC 1000 + +const t_bool rtc_avail = TRUE; + +uint32 sim_os_msec (void) +{ + unsigned long long micros; + UnsignedWide macMicros; + unsigned long millis; + + Microseconds (&macMicros); + micros = *((unsigned long long *) &macMicros); + millis = micros / 1000LL; + return (uint32) millis; +} + +void sim_os_sleep (unsigned int sec) +{ + sleep (sec); +} + +uint32 sim_os_ms_sleep_init (void) +{ + return 1; +} + +uint32 sim_os_ms_sleep (unsigned int milliseconds) +{ + uint32 stime = sim_os_msec (); + struct timespec treq; + + treq.tv_sec = milliseconds / MILLIS_PER_SEC; + treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI; + (void) nanosleep (&treq, NULL); + return sim_os_msec () - stime; +} + +#else + +/* UNIX routines */ + +#include +#include +#include +#define NANOS_PER_MILLI 1000000 +#define MILLIS_PER_SEC 1000 +#define sleep1Samples 100 + +const t_bool rtc_avail = TRUE; + +uint32 sim_os_msec () +{ + struct timeval cur; + struct timezone foo; + uint32 msec; + + gettimeofday (&cur, &foo); + msec = ((uint32) cur.tv_sec) * 1000 + ((uint32) cur.tv_usec + 500) / 1000; + return msec; +} + +void sim_os_sleep (unsigned int sec) +{ + sleep (sec); +} + +#if defined(HAVE_POSIX_CLOCK_ID) +static void init_insane_resolution(struct timespec* tsv) +{ + tsv->tv_sec = tsv->tv_nsec = INT32_MAX; +} +static t_bool is_sane_resolution(struct timespec* tsv) +{ + return tsv->tv_sec == 0 && tsv->tv_nsec > 0 && tsv->tv_nsec <= (1000 * 1000 * 1000 / 50); +} +#endif + +uint32 sim_os_us_sleep_init (void) +{ +#if defined(HAVE_POSIX_CLOCK_ID) + + struct timespec tsv; + struct timespec clkres; + uint32 msec; + + clkres.tv_nsec = 0; /* initialize to suppress false GCC warning */ + +#if defined(CLOCK_MONOTONIC_RAW) + init_insane_resolution(& tsv); + if (!sim_posix_have_clock_id && 0 == clock_getres(CLOCK_MONOTONIC_RAW, & tsv) && is_sane_resolution(& tsv)) + { + clkres = tsv; + sim_posix_clock_id = CLOCK_MONOTONIC_RAW; + sim_posix_have_clock_id = TRUE; + } +#endif + +#if defined(CLOCK_MONOTONIC) + init_insane_resolution(& tsv); + if (!sim_posix_have_clock_id && 0 == clock_getres(CLOCK_MONOTONIC, & tsv) && is_sane_resolution(& tsv)) + { + clkres = tsv; + sim_posix_clock_id = CLOCK_MONOTONIC; + sim_posix_have_clock_id = TRUE; + } +#endif + +#if defined(CLOCK_REALTIME) + init_insane_resolution(& tsv); + if (!sim_posix_have_clock_id && 0 == clock_getres(CLOCK_REALTIME, & tsv) && is_sane_resolution(& tsv)) + { + clkres = tsv; + sim_posix_clock_id = CLOCK_REALTIME; + sim_posix_have_clock_id = TRUE; + } +#endif + + /* + * ToDo: Unix/Linux sleep timer resolution + * + * For Linux see + * + * http://www.advenage.com/topics/linux-timer-interrupt-frequency.php + * https://rt.wiki.kernel.org/index.php/Cyclictest + * + * Specifically, the following Linux kernel config parameters are relevant: + * + * CONFIG_HIGH_RES_TIMERS + * CONFIG_HPET_TIMER + * CONFIG_X86_PM_TIMER + * CONFIG_SCx200HR_TIMER + * CONFIG_TIMER_STATS + * CONFIG_NO_HZ + * CONFIG_HZ + * CONFIG_HZ_100 + * CONFIG_HZ_250 + * CONFIG_HZ_300 + * CONFIG_HZ_1000 + * + */ + + if (sim_posix_have_clock_id) + { + msec = (clkres.tv_nsec + (NANOS_PER_MILLI - 1)) / NANOS_PER_MILLI; + if (msec == 0) msec = 1; + } + else + { + /* + * Default (optimistically) to 1 msec. + * Benchmarking Linux 2.6.38 on x86 3.2 GHz systems shows minimum sleep delay + * ranging from 0.25 to 1.5 ms. + */ + msec = 1; + } + + if (msec > SIM_IDLE_MAX) + return 0; + + return msec * 1000; + +#elif defined(__APPLE__) + + /* + * Statistical benchmarking of OS X Lion Server 10.7.4 (using "clocks" test) shows that average minimum + * sleep request roundtrip is about 7 usec (when using 500ns/1 usec sleep time). Thus sleep delay + * below 7 usec cannot be satisfied satisfactory. Hence we could use 7 usec as a cut-off time value for + * sleep/spin decision. Actual average sleep delay when requesting sleep time of 7 usec is 12 usec. + * In more practical workloads however "7" is likely to be wasteful, so use 30 usec as more practical + * threshold. This also covers latency of RQ/TQ requests satisfied by IOP thread almost immediatelly + * (such as via host file system cache) without having to incur rescheduling delays. + */ + + return 30; + +#else /* others */ + + uint32 i, t1, t2, tot, tim; + + /* sample sleep times, elevate priority for this */ + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CPU_CALIBRATION); + for (i = 0, tot = 0; i < sleep1Samples; i++) + { + t1 = sim_os_msec (); + sim_os_ms_sleep (1); + t2 = sim_os_msec (); + tot += (t2 - t1); + } + smp_set_thread_priority(SIMH_THREAD_PRIORITY_CONSOLE_PAUSED); + + tim = (tot + (sleep1Samples - 1)) / sleep1Samples; + + if (tim == 0) + tim = 1; + else if (tim > SIM_IDLE_MAX) + tim = 0; + + return tim * 1000; + +#endif +} + +uint32 sim_os_ms_sleep (unsigned int milliseconds) +{ + uint32 stime = sim_os_msec (); + struct timespec treq; + + treq.tv_sec = milliseconds / MILLIS_PER_SEC; + treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI; + (void) nanosleep (&treq, NULL); + return sim_os_msec () - stime; +} + +sim_delta_timer_impl::sim_delta_timer_impl() +{ + start.valid = FALSE; + last.valid = FALSE; + curr.valid = FALSE; +} + +sim_delta_timer_impl::~sim_delta_timer_impl() +{ +} + +t_bool sim_delta_timer_impl::init(t_bool dothrow) +{ + return TRUE; +} + +void sim_delta_timer_impl::sample(RUN_DECL, sim_delta_timer_sample* sample) +{ +#if defined(HAVE_POSIX_CLOCK_ID) + if (sim_posix_have_clock_id) + { + clock_gettime(sim_posix_clock_id, & sample->tsv); + } + else + { + struct timeval tv; + gettimeofday(& tv, NULL); + sample->tsv.tv_sec = tv.tv_sec; + sample->tsv.tv_nsec = tv.tv_usec * 1000; + } +#else + /* + * OS X note: under OS X / Mach it may be tempting to use directly + * + * clock_get_attributes + * clock_set_attributes + * clock_get_time + * clock_map_time + * host_get_clock_service + * + * for get_now and sim_delta_time_impl::sample, however unfortunately OS X does not implement clock_map_time + * (and even if it did, there would be problems with atomicity and ordering of access), whereas for all other + * clocks exposed granularity returned by clock_get_attrbutes is 1 sec. Futhermore, composite RTC/TSC based + * timer provides better granularity. + * + * We could also use mach_absolute_time which on older i386 versions was but a wrapper around clock_get_time, + * but on newer x86 and x64 versions is RTC+RDTSC based, see + * + * http://www.opensource.apple.com/source/Libc/Libc-763.13/i386/sys/mach_absolute_time.c + * http://www.opensource.apple.com/source/Libc/Libc-763.13/i386/sys/mach_absolute_time_asm.s + * http://www.opensource.apple.com/source/Libc/Libc-763.13/x86_64/sys/nanotime.s + * + * i.e. time comes from real-time clock, and is further adjusted by RDTSC cycles accumulated from the recent + * clock tick, thus giving a highly accurate reading. This would give us nano-range precision. + * + * For our purposes however we are all well with microsecond-range precision, which is provided on OS X + * by gettimeofday (see comment in sim_threads.cpp function get_now). + */ + gettimeofday(& sample->tv, NULL); +#endif + + if (cpu_unit) + sample->cycles = CPU_CURRENT_CYCLES; + + sample->valid = TRUE; + sample->min_since = 0; +} + +void sim_delta_timer_impl::begin(RUN_DECL) +{ + curr.valid = FALSE; + last.valid = FALSE; + sample(RUN_PASS, &start); +} + +void sim_delta_timer_impl::sample(RUN_DECL) +{ + if (curr.valid) + last = curr; + sample(RUN_PASS, &curr); +} + +void sim_delta_timer_impl::adjust_cycle_base(uint32 d) +{ + start.cycles += d; + last.cycles += d; + curr.cycles += d; +} + +uint32 sim_delta_timer_impl::us_since_start(RUN_DECL) +{ + return curr.us_since(RUN_PASS, this, &start); +} + +uint32 sim_delta_timer_impl::us_since_last(RUN_DECL) +{ + if (last.valid) + return curr.us_since(RUN_PASS, this, &last); + else + return curr.us_since(RUN_PASS, this, &start); +} + +uint32 sim_delta_timer_sample::us_since(RUN_DECL, sim_delta_timer_impl* dtimer, sim_delta_timer_sample* prev) +{ + double delta = 0; + +#if defined(HAVE_POSIX_CLOCK_ID) + if (tsv.tv_sec >= prev->tsv.tv_sec) + { + delta = (double) (tsv.tv_sec - prev->tsv.tv_sec) * 1000 * 1000; + delta += (double) (tsv.tv_nsec - prev->tsv.tv_nsec) / 1000; + } +#else + if (tv.tv_sec >= prev->tv.tv_sec) + { + delta = (tv.tv_sec - prev->tv.tv_sec) * 1000 * 1000; + delta += tv.tv_usec - prev->tv.tv_usec; + } +#endif + + /* time jumped backwards? */ + if (delta < 0) delta = 0; + + /* time jumped forward way too much? */ + const double delta_max = 0.9 * (double) UINT32_MAX; + if (delta > 0.9 * delta_max) + delta = delta_max; + + uint32 res = (uint32) delta; + if (res == 0) res = 1; + + if (res < prev->min_since) + res = prev->min_since; + + prev->min_since = res; + + return res; +} + +/* + * Get host system local time in VMS format. + * + * VMS time format is the number of 100-nanosecond intervals + * since 00:00 o’clock, November 17, 1858. + */ +void sim_os_gettime_vms(uint32* p_vms_time) +{ + struct timeval tv; + struct timezone tz; + + /* Linux gettimeofday returns UTC in tv */ + gettimeofday(&tv, &tz); + + /* local time */ + tv.tv_sec -= tz.tz_minuteswest * 60; + +#if defined(__APPLE__) + /* adjust for DST */ + /* note that Linux does not use tz_dsttime */ + if (tz.tz_dsttime != DST_NONE) + tv.tv_sec += 60 * 60; +#endif + + /* ToDo: implement for hosts with no 64-bit arithmetics */ + + /* number of 100ns units between November 17, 1858 and January 1, 1970 */ + static const t_uint64 ux_vms_delta = 35067168000000000ULL; + + t_uint64 vms_time = tv.tv_sec; + vms_time *= 1000 * 1000; + vms_time += tv.tv_usec; + vms_time *= 10; /* 100 ns units since local January 1, 1970 */ + vms_time += ux_vms_delta; /* 100 ns units since November 17, 1858 */ + + p_vms_time[0] = (uint32) (vms_time & 0xFFFFFFFF); + p_vms_time[1] = (uint32) ((vms_time >> 32) & 0xFFFFFFFF); +} + +#endif + +/* OS independent clock calibration package */ + +// int32 rtc_ticks[SIM_NTIMERS] = { 0 }; /* ticks */ +// int32 rtc_hz[SIM_NTIMERS] = { 0 }; /* tick rate */ +// uint32 rtc_rtime[SIM_NTIMERS] = { 0 }; /* real time */ +// uint32 rtc_vtime[SIM_NTIMERS] = { 0 }; /* virtual time */ +// uint32 rtc_nxintv[SIM_NTIMERS] = { 0 }; /* next interval */ +// int32 rtc_based[SIM_NTIMERS] = { 0 }; /* base delay */ +// int32 rtc_currd[SIM_NTIMERS] = { 0 }; /* current delay */ +// int32 rtc_initd[SIM_NTIMERS] = { 0 }; /* initial delay */ +// uint32 rtc_elapsed[SIM_NTIMERS] = { 0 }; /* sec since init */ + +#define rtc_ticks (cpu_unit->cpu_rtc_ticks) +#define rtc_hz (cpu_unit->cpu_rtc_hz) +#define rtc_rtime (cpu_unit->cpu_rtc_rtime) +#define rtc_vtime (cpu_unit->cpu_rtc_vtime) +#define rtc_nxintv (cpu_unit->cpu_rtc_nxintv) +#define rtc_based (cpu_unit->cpu_rtc_based) +#define rtc_currd (cpu_unit->cpu_rtc_currd) +#define rtc_initd (cpu_unit->cpu_rtc_initd) +#define rtc_elapsed (cpu_unit->cpu_rtc_elapsed) + +void sim_rtcn_init_all (RUN_DECL, t_bool keep_stable) +{ + for (uint32 i = 0; i < SIM_NTIMERS; i++) + { + /* + * ToDo: Do not start calibration timer from the very start (thus resetting rtc_elapsed[]) + * if (keep_stable && rtc_elapsed[i] >= sim_idle_stable). This reset, as it happens now, + * causes simulator being unable to go to idle sleep for some time after SIMH console + * command "CONTNINUE". + */ + if (rtc_initd[i] != 0) + sim_rtcn_init (RUN_PASS, rtc_initd[i], i); + } + + /* reset rate averaging data */ + if (! keep_stable) + atomic_var(cpu_cycles_per_second) = 0; + av_valid.clear_all(); + memzero(av_checkin); +} + +/* + * Initialize timer on the primary processor. + * + * For secondaries actual initialization is done by sim_rtcn_init_secondary_cpu when it is started, + * but sim_rtcn_init still can be called merely to read the initial clock rate calibration. + */ +int32 sim_rtcn_init (RUN_DECL, int32 time, int32 tmr) +{ + if (! cpu_unit->is_primary_cpu()) + return rtc_currd[tmr]; + + if (time == 0) + time = 1; + if (tmr < 0 || tmr >= SIM_NTIMERS) + return time; + rtc_rtime[tmr] = sim_os_msec (); + rtc_vtime[tmr] = rtc_rtime[tmr]; + rtc_nxintv[tmr] = 1000; + rtc_ticks[tmr] = 0; + rtc_hz[tmr] = 0; + rtc_based[tmr] = time; + rtc_currd[tmr] = time; + rtc_initd[tmr] = time; + rtc_elapsed[tmr] = 0; + + if (tmr == TMR_CLK) + { + if (use_clock_thread) + { + cpu_unit->cpu_last_tslice_tick_cycles = CPU_CURRENT_CYCLES; + cpu_unit->cpu_last_second_tick_cycles = CPU_CURRENT_CYCLES; + cpu_unit->cpu_idle_sleep_us = 0; + cpu_unit->cpu_idle_sleep_cycles = 0; + } + + atomic_var(tmr_poll) = rtc_currd[tmr]; /* set tmr poll */ + atomic_var(tmxr_poll) = atomic_var(tmr_poll) * TMXR_MULT; /* set mux poll */ + } + + return rtc_currd[tmr]; +} + +/* + * Initialize timer on the secondary processor (cpu_unit) when starting it + * using data from originating processir (src). + */ +void sim_rtcn_init_secondary_cpu (CPU_UNIT* cpu_unit, CPU_UNIT* src, int32 tmr, int32 time, t_bool clone) +{ + if (clone) + { + rtc_rtime[tmr] = sim_os_msec(); + rtc_vtime[tmr] = rtc_rtime[tmr]; + rtc_nxintv[tmr] = 1000; + rtc_ticks[tmr] = 0; + rtc_hz[tmr] = src->cpu_rtc_hz[tmr]; + rtc_based[tmr] = src->cpu_rtc_based[tmr]; + rtc_currd[tmr] = src->cpu_rtc_currd[tmr]; + rtc_initd[tmr] = src->cpu_rtc_initd[tmr]; + rtc_elapsed[tmr] = src->cpu_rtc_elapsed[tmr]; + if (tmr == TMR_CLK && use_clock_thread) + { + cpu_unit->cpu_idle_sleep_us = src->cpu_idle_sleep_us; + cpu_unit->cpu_idle_sleep_cycles = src->cpu_idle_sleep_cycles; + } + } + else + { + if (time == 0) + time = 1; + rtc_rtime[tmr] = sim_os_msec (); + rtc_vtime[tmr] = rtc_rtime[tmr]; + rtc_nxintv[tmr] = 1000; + rtc_ticks[tmr] = 0; + rtc_hz[tmr] = 0; + rtc_based[tmr] = time; + rtc_currd[tmr] = time; + rtc_initd[tmr] = time; + rtc_elapsed[tmr] = 0; + if (tmr == TMR_CLK && use_clock_thread) + { + cpu_unit->cpu_idle_sleep_us = 0; + cpu_unit->cpu_idle_sleep_cycles = 0; + } + } + + if (tmr == TMR_CLK) + { + if (use_clock_thread) + { + cpu_unit->cpu_last_tslice_tick_cycles = CPU_CURRENT_CYCLES; + cpu_unit->cpu_last_second_tick_cycles = CPU_CURRENT_CYCLES; + } + + rtc_currd[tmr] = weak_read_var(cpu_cycles_per_second) / rtc_hz[tmr]; + + if (rtc_currd[tmr] <= 0) + rtc_currd[tmr] = 1; + } +} + +/* evaluates expected number of CPU cycles per clock tick */ +int32 sim_rtcn_calb (RUN_DECL, int32 ticksper, int32 tmr) +{ + t_bool valid = FALSE; /* initialize to suppress false GCC warning message */ + uint32 os_msec = 0; /* ... */ + int32 t; + + if (use_clock_thread) + t = sim_rtcn_calb_synclk(RUN_PASS, ticksper, tmr, & valid, & os_msec); + else + t = sim_rtcn_calb_nosynclk(RUN_PASS, ticksper, tmr, & valid, & os_msec); + + if (tmr == TMR_CLK) + { + if (rtc_ticks[tmr] == 0 && rtc_elapsed[tmr] != 0) + { + /* average with other VCPUs */ + cpu_update_cycles_per_second(RUN_PASS, (uint32) (rtc_currd[tmr] * rtc_hz[tmr]), valid, os_msec); + rtc_currd[tmr] = weak_read_var(cpu_cycles_per_second) / rtc_hz[tmr]; + + /* never negative or zero! */ + if (rtc_currd[tmr] <= 0) + rtc_currd[tmr] = 1; + + /* ToDo: rethink the interference of rtc_currd averaging with rtc_based in nosynclk case */ + + t = rtc_currd[tmr]; + + /* execute per-cpu once-a-second routine */ + cpu_once_a_second(RUN_PASS); + } + else if (rtc_elapsed[tmr] == 0 && cpu_unit->is_primary_cpu()) + { + atomic_var(tmr_poll) = rtc_currd[tmr]; /* set tmr poll */ + atomic_var(tmxr_poll) = atomic_var(tmr_poll) * TMXR_MULT; /* set mux poll */ + } + } + + return t; +} + +int32 sim_rtcn_calb_synclk (RUN_DECL, int32 ticksper, int32 tmr, t_bool* valid, uint32* os_msec) +{ + if (unlikely(tmr != TMR_CLK)) + panic("Timer calibration: not TMR_CLK"); + + if (unlikely(ticksper != CLK_TPS)) + panic("Timer calibration: unexpected clock frequency"); + + /* + * 1-second interval is sliced into "ntslices" slices, + * each composed of "sliceticks" clock ticks. + * + * At the end of each slice, rtc_currd is adjusted using simple moving average. + */ + const int ntslices = 10; + const int sliceticks = CLK_TPS / ntslices; + + if (unlikely(ntslices * sliceticks != CLK_TPS)) + panic("Timer calibration: internal error (invalid timeslice definition)"); + + rtc_hz[tmr] = ticksper; + rtc_ticks[tmr] = rtc_ticks[tmr] + 1; /* count ticks */ + + if (rtc_ticks[tmr] % sliceticks) /* intraslice? */ + { + /* do nothing */ + } + else if (rtc_ticks[tmr] < ticksper) /* 1 sec yet? */ + { + /* + * calculate intra-virtual-second rate adjustment using moving average + * (maters only during the very first second of primary VCPU execution) + */ + if (unlikely(rtc_elapsed[tmr] == 0) && cpu_unit->is_primary_cpu()) + { + /* cycles since last slice */ + uint32 dcycles = CPU_CURRENT_CYCLES - cpu_unit->cpu_last_tslice_tick_cycles; + cpu_unit->cpu_last_tslice_tick_cycles = CPU_CURRENT_CYCLES; + + /* apply the adjustment */ + rtc_currd[tmr] = ((dcycles / sliceticks) + (ntslices - 1) * rtc_currd[tmr]) / ntslices; + } + } + else /* once a virtual second */ + { + /* next virtual second */ + rtc_ticks[tmr] = 0; /* reset ticks */ + rtc_elapsed[tmr] = rtc_elapsed[tmr] + 1; /* count sec */ + + /* get elapsed real time */ + uint32 new_rtime = sim_os_msec (); + uint32 delta_rtime = new_rtime - rtc_rtime[tmr]; /* elapsed wtime */ + rtc_rtime[tmr] = new_rtime; /* adv wall time */ + + /* time goes backward or not running? */ + if (delta_rtime > 30000 || delta_rtime == 0) + { + /* do not apply adjustment, continue using old calibration */ + *valid = FALSE; + } + else + { + /* cycles since last second */ + uint32 dcycles = CPU_CURRENT_CYCLES - cpu_unit->cpu_last_second_tick_cycles; + + /* check if more than 5% of elapsed real time was spent doing VCPU cycles execution, + rather than in voluntary sleep, i.e. whether voluntary sleep made less than 95% of the + interval */ + *valid = cpu_unit->cpu_idle_sleep_us < delta_rtime * 19 * (1000 / 20); + + if (*valid) + { + dcycles -= cpu_unit->cpu_idle_sleep_cycles; + delta_rtime -= cpu_unit->cpu_idle_sleep_us / 1000; + } + + rtc_currd[tmr] = (int32) (((double) dcycles * 1000) / ((double) delta_rtime * rtc_hz[tmr])); + } + + // cpu_unit->cpu_last_tslice_tick_cycles = CPU_CURRENT_CYCLES; + cpu_unit->cpu_last_second_tick_cycles = CPU_CURRENT_CYCLES; + cpu_unit->cpu_idle_sleep_us = 0; + cpu_unit->cpu_idle_sleep_cycles = 0; + *os_msec = new_rtime; + } + + if (rtc_currd[tmr] <= 0) /* never negative or zero! */ + rtc_currd[tmr] = 1; + + return rtc_currd[tmr]; +} + +static int32 sim_rtcn_calb_nosynclk (RUN_DECL, int32 ticksper, int32 tmr, t_bool* valid, uint32* os_msec) +{ + uint32 new_rtime, delta_rtime; + int32 delta_vtime; + + if (tmr < 0 || tmr >= SIM_NTIMERS) + return 10000; + rtc_hz[tmr] = ticksper; + rtc_ticks[tmr] = rtc_ticks[tmr] + 1; /* count ticks */ + if (rtc_ticks[tmr] < ticksper) /* 1 sec yet? */ + return rtc_currd[tmr]; + rtc_ticks[tmr] = 0; /* reset ticks */ + rtc_elapsed[tmr] = rtc_elapsed[tmr] + 1; /* count sec */ + *os_msec = new_rtime = sim_os_msec (); /* wall time */ + *valid = FALSE; /* assume no valid timing data */ + if (! rtc_avail) /* no timer? */ + return rtc_currd[tmr]; + if (new_rtime < rtc_rtime[tmr]) /* time running backwards? */ + { + rtc_rtime[tmr] = new_rtime; /* reset wall time */ + return rtc_currd[tmr]; /* can't calibrate */ + } + delta_rtime = new_rtime - rtc_rtime[tmr]; /* elapsed wtime */ + rtc_rtime[tmr] = new_rtime; /* adv wall time */ + rtc_vtime[tmr] = rtc_vtime[tmr] + 1000; /* adv sim time */ + if (delta_rtime > 30000) /* gap too big? */ + return rtc_initd[tmr]; /* can't calibr */ + /* + * ToDo: integrate the use of cpu_unit->cpu_idle_sleep_cycles and cpu_unit->cpu_idle_sleep_us + * similar to sim_rtcn_calb_synclk case. + */ + *valid = TRUE; /* assume valid timing data can be produced */ + if (delta_rtime == 0) /* gap too small? */ + { + rtc_based[tmr] = rtc_based[tmr] * ticksper; /* slew wide */ + *valid = FALSE; + } + else + { + rtc_based[tmr] = (int32) (((double) rtc_based[tmr] * (double) rtc_nxintv[tmr]) / + ((double) delta_rtime)); /* new base rate */ + } + delta_vtime = rtc_vtime[tmr] - rtc_rtime[tmr]; /* gap */ + if (delta_vtime > SIM_TMAX) /* limit gap */ + delta_vtime = SIM_TMAX; + else if (delta_vtime < -SIM_TMAX) + delta_vtime = -SIM_TMAX; + rtc_nxintv[tmr] = 1000 + delta_vtime; /* next wtime */ + rtc_currd[tmr] = (int32) (((double) rtc_based[tmr] * (double) rtc_nxintv[tmr]) / + 1000.0); /* next delay */ + if (rtc_based[tmr] <= 0) /* never negative or zero! */ + { + rtc_based[tmr] = 1; + *valid = FALSE; + } + if (rtc_currd[tmr] <= 0) /* never negative or zero! */ + { + rtc_currd[tmr] = 1; + *valid = FALSE; + } + + return rtc_currd[tmr]; +} + +/* Prior interfaces - default to timer 0 */ + +int32 sim_rtc_init (RUN_DECL, int32 time) +{ + return sim_rtcn_init (RUN_PASS, time, 0); +} + +int32 sim_rtc_calb (RUN_DECL, int32 ticksper) +{ + return sim_rtcn_calb (RUN_PASS, ticksper, 0); +} + +/* + * This routine is called by each running VCPU once a virtual second to average CPU rate data + * (instructons per second) across all the VCPUs. Bases on the result of this averaging it + * calculates updated values for: + * + * cpu_cycles_per_second + * tmr_poll + * tmxr_poll + * + * valid = FALSE means that this VCPU spent virtually all time during recent virtual second + * in a voluntary idle sleep and has no meaningful rate calibration data to contribute + */ +void cpu_update_cycles_per_second(RUN_DECL, uint32 ips, t_bool valid, uint32 os_msec) +{ + uint32 cx = cpu_unit->cpu_id; + uint32 ix; + + /* sanity check */ + if (ips < CLK_TPS) + valid = FALSE; + + cpu_cycles_per_second_lock->lock(); + + /* record this VCPU's checkin time and rate */ + uint32 prev_checkin = av_checkin[cx]; + av_checkin[cx] = os_msec; + uint32 dt_checkin = os_msec - prev_checkin; + + /* record whether this VCPU has valid rate adjustment data to contribute */ + if (valid) + { + av_valid.set(cx); + av_ips[cx] = ips; + } + else + { + av_valid.clear(cx); + } + + /* mark CPUs that have not checked in for too long as contributing no valid data */ + if (prev_checkin && os_msec - prev_checkin <= 0x10000000) + { + for (ix = 0; ix < sim_ncpus; ix++) + { + if (ix != cx && av_valid.is_set(ix) && os_msec - av_checkin[ix] > dt_checkin + 1500) + av_valid.clear(ix); + } + } + + /* + * if supplied valid data, recalculate average; otherwise carry on with old average value + */ + if (valid) + { + ips = 0; + + for (ix = 0; ix < sim_ncpus; ix++) + { + if (av_valid.is_set(ix)) + ips += av_ips[ix]; + } + + ips /= av_valid.count_set(0, sim_ncpus - 1); + + /* must never be made less than CLK_TPS */ + if (ips < CLK_TPS) + ips = CLK_TPS; + + atomic_var(cpu_cycles_per_second) = ips; + + /* set tmr poll */ + atomic_var(tmr_poll) = ips / CLK_TPS; + + /* set mux poll */ + atomic_var(tmxr_poll) = atomic_var(tmr_poll) * TMXR_MULT; + } + + /* + * if simulator had UI, it could display "ips" as current CPU rate indicator + * (after the unlock below, of course) + */ + // smp_printf(" \n*** CPU%d: updated rate %d IPS\n", cx, ips); + + cpu_cycles_per_second_lock->unlock(); +} + +void cpu_stopping_ips_rate_update(RUN_DECL) +{ + cpu_cycles_per_second_lock->lock(); + av_valid.clear(cpu_unit->cpu_id); + cpu_cycles_per_second_lock->unlock(); +} + +uint32 cpu_get_cycles_per_second(RUN_DECL) +{ + uint32 cps = atomic_var(cpu_cycles_per_second); + if (cps == 0) + cps = rtc_currd[TMR_CLK] * rtc_hz[TMR_CLK]; + return cps; +} + +/* sim_timer_init - get minimum sleep time available on this host */ + +t_bool sim_timer_init (void) +{ + sim_idle_enab = FALSE; /* init idle off */ + sim_idle_rate_us = sim_os_us_sleep_init (); /* get OS timer rate */ + return sim_idle_rate_us != 0; +} + +/* + * sim_idle - idle simulator until next event or for specified interval + * + * Inputs: + * + * tmr calibrated timer to use + * maxticks max tick count to sleep (0 = till next clock tick, 1 = till tick after next, + * 2 = till second tick after next and so on) + * + * Must solve the linear equation + * + * ms_to_wait = w * ms_per_wait + * + * Or + * w = ms_to_wait / ms_per_wait + * + * May return SCPE_OK or SCPE_STOP (if stop_cpus is set). When SMP is active, the latter is ignored + * by the caller, stop_cpus will be re-checked instead during execution of next VAX instruction, + * hence under SMP control return status is ignored. + */ + +t_stat sim_idle(RUN_DECL, uint32 tmr, t_bool sin_cyc, uint32 maxticks) +{ + /* + * ToDo: Implement support for multi-tick idle sleeping. + * + * Support for multi-tick idle sleeping had been implemented at VMS VSMP layer which calculates + * and passes down the value of tick-count-to-sleep in VAXMP_API_OP_IDLE call, which is then + * propagated here as "maxticks" argument. However sim_idle currently ignores "maxticks" and + * always tries to sleep until the next tick only. + * + * Note that VSMP requests multi-tick sleep (with duration up to 50 ticks) only for secondary + * VCPUs, but primary VCPU is always requested to sleeps till next click only (at least when + * multiple CPUs are active) because the primary is responsible for providing services to other VCPUs, + * including incrementing time-keeping system variables in the kernel on each tick, so the primary + * cannot sleep multiple ticks and cannot skip wake up on clock ticks. Secondaries however can. + * + * One obvious benefit of multi-tick sleep is reduction of system overhead due to elimination + * of frequent thread scheduling. However this overhead is already low and its further reduction + * (let us say, from 1% of system computational resources to 0.3%) won't be a drastic improvement. + * + * Another benefit is that host operating system can reduce power consumption by idling cores. + * (Whereas frequent wakeups and use of cores at 100 Hz frequency can interfere with host OS + * capability to shift idling cores into lower power state.) + * + * Furthermore, with idling cores shifted into lower power state, host CPU thermal management + * may be able to boost the frequency on active cores. + * + * Nevertheless it had been deemed that these benefits are marginal enough and do not + * justify introduction of additional complexity (required to track multi-tick sleep state) + * in the initial release of VAX MP. + * + * Support for multi-tick sleeping can be added in later versions of VAX MP. + * + * When adding such support, sleep time should be constrained by the following factors: + * + * - value of "maxticks" + * + * - pending clock event queue entries for devices co-scheduled with the clock + * (min(clk_cosched) where (clk_cosched) != 0) + * + * - pending clock event queue entries for devices not co-scheduled with the clock + * (min(time) where clk_cosched == 0 and unit->device != & dev_clk) + * + * - in all cases limited to a reasonable "safety net" value, e.g. 2 seconds + * + * On wakeup sim_idle should examine duration of actual sleep and decide how much + * ticks had the VCPU actually slept (but minimize it with intended sleep time). + * + * It should then execute sequence of back-to-back CLK interrupts, as described in + * "Timer control" section of "VAX MP Technical Overview". On wakeup, sim_idle + * should advance CPU cycle counter by a value defined a minimum or actual sleep time + * and time to next tick. It should execute all pending events than fall into this + * region. If region reaches next clock tick, CLK interrupt should be raised. + * When CPU subsequently executes REI instruction, cpu_active_clk_interrupt is set + * and new IPL level is below CLK level, check for next pending sleep tick should + * be done, and if there is one, CPU cycle counter shoold be advacned, intervening clock + * queue events should be processed, and CLK interrupt raised again. SYNCLK protection + * in this case should be overriden. This sequence should be repeated until all pending + * ticks are exhausted. After the last tick is executed and corresponding REI is issued, + * CPU cycle counter should be advanced again by the amount corresponding to partial + * tick, and clock queue events falling into this range should be processed. + * + * SYNCLK during execution of the sequence should be postponed. + * + * Ability to execute back-to-back sequence relies on CLK being the highest-priority + * hardware interrupt in the system passed to VAX code. In particular, it must be + * processed before IPINTR. Thus if idling CPU is woken up by external interrupt, + * it will flush all pending CLK interrupts before processing other internal + * interrupts -- as required to mainatain logical consistent state of the system. + * + * Implementation will be slightly different for cases when use_clock_thread is set + * and not set. + * + * CPU resets (including restarts of secondaries) should be propertly handled to + * reset the sequence. + * + * It might be possible for the primary CPU to also sleep more than one tick, but + * only when no secondary CPUs are active. VSMP currently does not implement such feature + * (since, after all, VSMP is intended for multiprocessor operations), but such support + * may be added. If it is added, additional aspect should be handled at the simulator + * level: when entering ROM console mode such as via Ctrl/P, execution of REI-CLK replay + * sequence should be suspended when entering ROM console, then resumed on return from it + * via CONTINUE command. + */ + + UINT64 w_us; + uint32 cps1, cps2, w32_us; + uint32 act_cyc = 0; /* initialize to suppress false GCC warning */ + UINT64 act_cyc64; + t_bool act_cyc_isvalid = FALSE; + + /************************************************************************************* + * End SYNCLK protection period and check if CLK interrupt should be raised * + *************************************************************************************/ + + if (cpu_unit->cpu_synclk_protect_dev) + return SCPE_OK; + + cpu_unit->cpu_synclk_protect_os = 0; + cpu_unit->cpu_synclk_protect = FALSE; + + if (check_synclk_pending(RUN_PASS)) + return SCPE_OK; + + /************************************************************************************* + * Remove clock queue entries that had been rescheduled to other CPUs off the head * + * of clock queue, so they do not impact idle wait time calculation * + *************************************************************************************/ + + sim_flush_migrated_clock_queue_entries(RUN_PASS); + + /************************************************************************************* + * Check if may sleep * + *************************************************************************************/ + + if (cpu_unit->clock_queue == NULL) + { + if (use_clock_thread && cpu_unit->clk_active) + { + // clk_unit is active, may sleep + } + else + { + if (sin_cyc) + { + cpu_cycle(); + } + return SCPE_OK; + } + } + + if (cpu_unit->clock_queue && (cpu_unit->clock_queue->uptr->flags & UNIT_IDLE) == 0 || /* event not idle-able? */ + rtc_elapsed[tmr] < sim_idle_stable) /* timer not stable? */ + { + if (sin_cyc) + { + cpu_cycle(); + } + return SCPE_OK; + } + + /************************************************************************************* + * Calculate maximum microseconds to sleep * + *************************************************************************************/ + + cps1 = cpu_get_cycles_per_second(RUN_PASS); + + if (cpu_unit->clock_queue) + { + if (cpu_unit->clock_queue->clk_cosched) + { + /* will be awoken by SYNCLK -- request to sleep 1 second */ + UINT64_FROM_UINT32(w_us, 1000 * 1000); + } + else + { + UINT64_FROM_UINT32(w_us, (uint32) sim_interval); + UINT64_MUL_UINT32(w_us, 1000 * 1000); + UINT64_DIV_UINT32(w_us, cps1); + } + } + else + { + /* no clock queue entries -- sleep 1 second */ + UINT64_FROM_UINT32(w_us, 1000 * 1000); + } + + /************************************************************************************* + * Is idle sleep time below host system's sleep timer resolution? * + *************************************************************************************/ + + if (sim_idle_rate_us == 0 || UINT64_LT_UINT32(w_us, sim_idle_rate_us)) + { + /* if below, cannot wait, have to spin */ + if (sin_cyc) + { + cpu_cycle(); + } + return SCPE_OK; + } + else + { + t_bool valid = FALSE; + UINT64_TO_UINT32(w_us, w32_us, valid); + if (! valid) w32_us = 1000 * 1000 * 1000; + } + + /************************************************************************************* + * Bump priority for sleep unless SYNCLK is used * + *************************************************************************************/ + + if (! use_clock_thread) + { + /* + * Since we are in the guest OS idle loop, we should be executing at SIMH_THREAD_PRIORITY_CPU_RUN. + * Boost current VCPU thread priority so that when sleep interval expires, we get control back + * ASAP and can process HW clock ticks promptly. + * + * Increment "VM-critical" reason count, so priority does not get dropped back if cpu_reevaluate_thread_priority() + * is called either directy or indirectly between here and skip_sleep_attempt. + * + * Note that priority elevation will be suppressed if host is dedicated or if only primary VCPU is active + * (see must_control_prio()). However in these cases special efforts to secure prompt wake-up are less important; + * not to mention that all currently supported hosts systems do use clock thread (which boosts VCPU thread + * priority when sending SYNCLK interrupt to the VCPU). If these cases are deemed important later, we can + * modify thread priority change control scheme to make it accomodate idle sleep-time boosts. + */ + vm_critical_lock(); + } + + /* + * ToDo: + * + * We may want to boost VCPU thread priority even if using clock thread but sleeping for a short time, + * less then expected time to next SYNCLK interrupt. This should help VCPU thread to wake up fast and + * process pending events. Otherwise if VCPU thread is left running at normal or depressed prioriy and + * round-robin policy is in action, VCPU thread may be be placed at the end of scheduling queue. When + * thread is waked up, we may want delay downgrading (reeavluating) priority until pending clock queue + * events are processed. + * + * Thus condition for priority elevation may be + * + * (!use_clock_thread) || (use_clock_thread && next_non_clk_event_interval <= max(3 * next_clock_interval_estimate, threshold)) + * + * where "3" allows for variability in instructions per second calibration rate. If next_clock_interval_estimate + * is low, use minimum threshold instead of (3 * next_clock_interval_estimate). + * + */ + + /************************************************************************************* + * Prepare to enter idle sleep * + *************************************************************************************/ + + uint32 act_us = 0; + cpu_unit->cpu_wakeup_event->clear(); + + if (unlikely(! smp_interlocked_cas_done_var(& cpu_unit->cpu_sleeping, 0, 1))) + panic("Unexpected state of cpu_sleeping (0->1)"); + + /************************************************************************************* + * Last check if may enter sleep * + *************************************************************************************/ + + if (! cpu_may_sleep(RUN_PASS)) + { + if (unlikely(! smp_interlocked_cas_done_var(& cpu_unit->cpu_sleeping, 1, 0))) + panic("Unexpected state of cpu_sleeping (1->0 pre sleep)"); + goto skip_sleep_attempt; + } + + /************************************************************************************* + * Do sleep * + *************************************************************************************/ + + /* + * Note that due to race condition between sim_idle and wakeup_cpu, + * spurious wakeups can sometimes (infrequently) happen. + */ + cpu_unit->cpu_wakeup_event->timed_wait(w32_us, & act_us); + + /************************************************************************************* + * Leave sleep state * + *************************************************************************************/ + + if (unlikely(! smp_interlocked_cas_done_var(& cpu_unit->cpu_sleeping, 1, 0))) + panic("Unexpected state of cpu_sleeping (1->0 after sleep)"); + + /************************************************************************************* + * Advance CPU cycle counters * + *************************************************************************************/ + + if (act_us != UINT32_MAX) + { + cps2 = cpu_get_cycles_per_second(RUN_PASS); + UINT64_FROM_UINT32(act_cyc64, (cps1 + cps2) / 2); + UINT64_MUL_UINT32(act_cyc64, act_us); + UINT64_DIV_UINT32(act_cyc64, 1000 * 1000); + UINT64_TO_UINT32(act_cyc64, act_cyc, act_cyc_isvalid); + /* + * if slept for too long, e.g. as the result of host hibernation, + * ignore this sleep time altogether + */ + if (act_cyc > 0x7FFFFFFF) act_cyc_isvalid = FALSE; + } + + if (act_cyc_isvalid) + { + /* ToDo: if overslept, reduce "time" in due clock queue entries */ + if (sim_interval > (int32) act_cyc) + sim_interval -= (int32) act_cyc; + else + sim_interval = 1; + + CPU_CURRENT_CYCLES += act_cyc; + cpu_unit->cpu_idle_sleep_cycles += act_cyc; + + /* + * Register usecs spent in voluntary sleep (does not currently handle maxticks). + * + * Estimate of maxvol_us is imperfect if clock thread is used, but fairly close to the best we can do. + * We could of course try to estimate maxvol_us more precisely based on approximation of starting + * position for the sleep within a current tick derived from cycles accumulated since the last tick + * compared vs. cpu_get_cycles_per_second() / CLK_TPS, howerver current approximation seems to be + * satisfactory for the purposes it is used for. + */ + uint32 maxvol_us = imin(w32_us, (uint32) (1000 * 1000) / CLK_TPS); + cpu_unit->cpu_idle_sleep_us += imin(maxvol_us, act_us); + } + + /************************************************************************************* + * Drop priority to original (unless clock event is imminent) * + *************************************************************************************/ + +skip_sleep_attempt: + + if (! use_clock_thread) + { + RUN_SCOPE_RSCX_ONLY; + + /* Decrement "VM-critical" count to compensate for the increment above */ + rscx->vm_critical_locks--; + + /* + * If clock event is not imminent, drop priority back to what it used to be at the entrance + * to sim_idle. However if clock event is imminent, leave the thread at elevated priority. + * Priority will be dropped then either when clock interrupt is delivered (in case clock + * interrupts are enabled via CSR_IE) or in clk_svc if clock interrupts are disabled. + * + * Define threshold of "imminent" as 5% of a clock tick. + */ + int32 clk_time = sim_is_active(& clk_unit) - 1; + if (clk_time >= 0 && (uint32) clk_time <= cpu_get_cycles_per_second(RUN_PASS) / (CLK_TPS * 20) /*&& clk_time == sim_interval*/) + { + CPU_CURRENT_CYCLES += sim_interval; + cpu_unit->cpu_idle_sleep_cycles += sim_interval; + sim_interval = 0; + } + else + { + if (rscx->vm_critical_locks == 0) + cpu_reevaluate_thread_priority(RUN_PASS); + } + } + + /************************************************************************************* + * Check if CPU stop request is pending * + *************************************************************************************/ + + smp_rmb(); + if (weak_read(stop_cpus)) + return SCPE_STOP; + + return SCPE_OK; +} + +/* Set idling - implicitly disables throttling */ + +t_stat sim_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + t_stat r; + uint32 v; + + if (sim_idle_rate_us == 0) + return SCPE_NOFNC; + + if (val != 0 && sim_idle_rate_us > (uint32) val) + return SCPE_NOFNC; + + if (cptr) + { + v = (uint32) get_uint (cptr, 10, SIM_IDLE_STMAX, &r); + if (r != SCPE_OK || v < SIM_IDLE_STMIN) + return SCPE_ARG; + sim_idle_stable = v; + } + + sim_idle_enab = TRUE; + + if (sim_throt_type != SIM_THROT_NONE) + { + sim_set_throt (0, NULL); + smp_printf ("Throttling disabled\n"); + if (sim_log) + fprintf (sim_log, "Throttling disabled\n"); + } + + return SCPE_OK; +} + +/* Clear idling */ + +t_stat sim_clr_idle (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + sim_idle_enab = FALSE; + return SCPE_OK; +} + +/* Show idling */ + +t_stat sim_show_idle (SMP_FILE *st, UNIT *uptr, int32 val, void *desc) +{ + if (sim_idle_enab) + fprintf (st, "idle enabled, stability wait = %ds", sim_idle_stable); + else + fputs ("idle disabled", st); + return SCPE_OK; +} + +/* Throttling package */ + +t_stat sim_set_throt (int32 arg, char *cptr) +{ + if (arg == 0) + { + if (cptr && *cptr) + return SCPE_ARG; + sim_throt_type = SIM_THROT_NONE; + sim_throt_cancel (); + } +#if defined(SIM_NO_THROTTLING) + else + { + return SCPE_NOFNC; + } +#else + else if (sim_idle_rate_us == 0) + { + return SCPE_NOFNC; + } + else + { + char *tptr, c; + t_value val = strtotv (cptr, &tptr, 10); + if (cptr == tptr) + return SCPE_ARG; + c = toupper (*tptr++); + if (*tptr != 0) + return SCPE_ARG; + if (c == 'M') + sim_throt_type = SIM_THROT_MCYC; + else if (c == 'K') + sim_throt_type = SIM_THROT_KCYC; + else if ((c == '%') && (val > 0) && (val < 100)) + sim_throt_type = SIM_THROT_PCT; + else return SCPE_ARG; + if (sim_idle_enab) + { + smp_printf ("Idling disabled\n"); + if (sim_log) + fprintf (sim_log, "Idling disabled\n"); + sim_clr_idle (NULL, 0, NULL, NULL); + } + sim_throt_val = (uint32) val; + } +#endif + + return SCPE_OK; +} + +t_stat sim_show_throt (SMP_FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) +{ +#if defined(SIM_NO_THROTTLING) + fprintf (st, "Throttling not available\n"); +#else + if (sim_idle_rate_us == 0) + { + fprintf (st, "Throttling not available\n"); + } + else + { + switch (sim_throt_type) + { + case SIM_THROT_MCYC: + fprintf (st, "Throttle = %d megacycles\n", sim_throt_val); + break; + + case SIM_THROT_KCYC: + fprintf (st, "Throttle = %d kilocycles\n", sim_throt_val); + break; + + case SIM_THROT_PCT: + fprintf (st, "Throttle = %d%%\n", sim_throt_val); + break; + + default: + fprintf (st, "Throttling disabled\n"); + break; + } + + if (sim_switches & SWMASK ('D')) + { + fprintf (st, "Wait rate = %d us\n", sim_idle_rate_us); + if (sim_throt_type != 0) + fprintf (st, "Throttle interval = %d cycles\n", sim_throt_wait); + } + } +#endif + return SCPE_OK; +} + +void sim_throt_sched (void) +{ +#if !defined(SIM_NO_THROTTLING) + sim_throt_state = 0; + if (sim_throt_type) + sim_activate (&sim_throt_unit, SIM_THROT_WINIT); +#endif +} + +void sim_throt_cancel (void) +{ +#if !defined(SIM_NO_THROTTLING) + sim_cancel (&sim_throt_unit); +#endif +} + +/* Throttle service + + Throttle service has three distinct states + + 0 take initial measurement + 1 take final measurement, calculate wait values + 2 periodic waits to slow down the CPU +*/ + +t_stat sim_throt_svc (RUN_SVC_DECL, UNIT *uptr) +{ +#if !defined(SIM_NO_THROTTLING) + // RUN_SVC_CHECK_CANCELLED(uptr); // not required for per-CPU devices + uint32 delta_ms; + double a_cps, d_cps; + + switch (sim_throt_state) + { + case 0: /* take initial reading */ + sim_throt_ms_start = sim_os_msec (); + sim_throt_wait = SIM_THROT_WST; + sim_throt_state++; /* next state */ + break; /* reschedule */ + + case 1: /* take final reading */ + sim_throt_ms_stop = sim_os_msec (); + delta_ms = sim_throt_ms_stop - sim_throt_ms_start; + if (delta_ms < SIM_THROT_MSMIN) { /* not enough time? */ + if (sim_throt_wait >= 100000000) /* too many inst? */ + { + sim_throt_state = 0; /* fails in 32b! */ + return SCPE_OK; + } + sim_throt_wait = sim_throt_wait * SIM_THROT_WMUL; + sim_throt_ms_start = sim_throt_ms_stop; + } + else /* long enough */ + { + a_cps = ((double) sim_throt_wait) * 1000.0 / (double) delta_ms; + if (sim_throt_type == SIM_THROT_MCYC) /* calc desired cps */ + d_cps = (double) sim_throt_val * 1000000.0; + else if (sim_throt_type == SIM_THROT_KCYC) + d_cps = (double) sim_throt_val * 1000.0; + else d_cps = (a_cps * ((double) sim_throt_val)) / 100.0; + if (d_cps >= a_cps) { + sim_throt_state = 0; + return SCPE_OK; + } + sim_throt_wait = (int32) /* time between waits */ + ((a_cps * d_cps * (double) sim_idle_rate_us) / (a_cps - d_cps)); + if (sim_throt_wait < SIM_THROT_WMIN) { /* not long enough? */ + sim_throt_state = 0; + return SCPE_OK; + } + sim_throt_state++; +// fprintf (smp_stderr, "Throttle values a_cps = %f, d_cps = %f, wait = %d\n", +// a_cps, d_cps, sim_throt_wait); + } + break; + + case 2: /* throttling */ + sim_os_ms_sleep (1); + break; + } + + sim_activate (uptr, sim_throt_wait); /* reschedule */ +#endif + + return SCPE_OK; +} diff --git a/src/sim_timer.h b/src/sim_timer.h new file mode 100644 index 0000000..b428315 --- /dev/null +++ b/src/sim_timer.h @@ -0,0 +1,106 @@ +/* sim_timer.h: simulator timer library headers + + 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. + + 28-Apr-07 RMS Added sim_rtc_init_all + 17-Oct-06 RMS Added idle support + 02-Jan-04 RMS Split out from SCP +*/ + +#ifndef _SIM_TIMER_H_ +#define _SIM_TIMER_H_ 0 + +#define SIM_NTIMERS 8 /* # timers */ +#define SIM_TMAX 500 /* max timer makeup */ + +#define SIM_IDLE_CAL 10 /* ms to calibrate */ +#define SIM_IDLE_MAX 10 /* max granularity idle */ +#define SIM_IDLE_STMIN 10 /* min sec for stability */ +#define SIM_IDLE_STDFLT 20 /* dft sec for stability */ +#define SIM_IDLE_STMAX 600 /* max sec for stability */ + +#define SIM_THROT_WINIT 1000 /* cycles to skip */ +#define SIM_THROT_WST 10000 /* initial wait */ +#define SIM_THROT_WMUL 4 /* multiplier */ +#define SIM_THROT_WMIN 100 /* min wait */ +#define SIM_THROT_MSMIN 10 /* min for measurement */ +#define SIM_THROT_NONE 0 /* throttle parameters */ +#define SIM_THROT_MCYC 1 +#define SIM_THROT_KCYC 2 +#define SIM_THROT_PCT 3 + +#define TMXR_MULT 1 /* 100 Hz */ + +t_bool sim_timer_init (void); +void sim_rtcn_init_all (RUN_DECL, t_bool keep_stable); +int32 sim_rtcn_init (RUN_DECL, int32 time, int32 tmr); +int32 sim_rtcn_calb (RUN_DECL, int32 ticksper, int32 tmr); +void sim_rtcn_init_secondary_cpu (CPU_UNIT* cpu_unit, CPU_UNIT* src, int32 tmr, int32 time, t_bool clone); +int32 sim_rtc_init (RUN_DECL, int32 time); +int32 sim_rtc_calb (RUN_DECL, int32 ticksper); +t_stat sim_idle(RUN_DECL, uint32 tmr, t_bool sin_cyc, uint32 maxticks); +t_stat sim_set_throt (int32 arg, char *cptr); +t_stat sim_show_throt (SMP_FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr); +t_stat sim_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sim_clr_idle (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sim_show_idle (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +void sim_throt_sched (void); +void sim_throt_cancel (void); +uint32 sim_os_msec (void); +void sim_os_sleep (unsigned int sec); +uint32 sim_os_ms_sleep (unsigned int msec); +uint32 sim_os_us_sleep_init (void); +void sim_os_gettime_vms(uint32* vms_time); + +extern int32 clk_tps; +extern UNIT clk_unit; +extern t_bool sim_idle_enab; + +#if !defined(_WIN32) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L +# define HAVE_POSIX_CLOCK_ID +#endif + +#if defined(HAVE_POSIX_CLOCK_ID) +/* Linux and Unix clocks */ +extern t_bool sim_posix_have_clock_id; +extern clockid_t sim_posix_clock_id; +#endif + +class sim_delta_timer +{ +public: + sim_delta_timer* next; + +public: + sim_delta_timer() {} + virtual ~sim_delta_timer() {} + static sim_delta_timer* create(t_bool dothrow = TRUE); + virtual void adjust_cycle_base(uint32 d) = 0; + virtual void begin(RUN_DECL) = 0; + virtual void sample(RUN_DECL) = 0; + virtual uint32 us_since_start(RUN_DECL) = 0; + virtual uint32 us_since_last(RUN_DECL) = 0; +}; + +#endif diff --git a/src/sim_tmxr.cpp b/src/sim_tmxr.cpp new file mode 100644 index 0000000..24346a1 --- /dev/null +++ b/src/sim_tmxr.cpp @@ -0,0 +1,1252 @@ +/* sim_tmxr.c: Telnet terminal multiplexor library + + 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"), + 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. + + 02-Jun-11 MP Fixed telnet option negotiation loop with some clients + Added Option Negotiation and Debugging Support + 17-Jan-11 MP Added Buffered line capabilities + 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, + 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 */ + +/* 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 */ +#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 */ +#define TNS_DO 006 /* DO request pending rejection */ +#define TNS_DONT 007 /* DONT request pending rejection */ + +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 SMP_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, psave; + uint32 ipaddr; + char cmsg[80]; + char dmsg[80] = ""; + char lmsg[80] = ""; + char msgbuf[256]; + static char mantra[] = + { +#if defined (VM_VAX) + TN_IAC, TN_DONT, TN_LINE, + TN_IAC, TN_WILL, TN_SGA, + TN_IAC, TN_DO, TN_SGA, + TN_IAC, TN_WILL, TN_ECHO, + TN_IAC, TN_WILL, TN_BIN, + TN_IAC, TN_DO, TN_BIN +#else + 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 +#endif + }; + + 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->mp = mp; /* save mux */ + sim_write_sock (newsock, mantra, sizeof(mantra)); + tmxr_debug (TMXR_DBG_XMT, lp, "Sending", mantra, sizeof(mantra)); + sprintf (cmsg, "\n\r\nConnected to the %s simulator ", sim_name); + + if (mp->dptr) { /* device defined? */ + sprintf (dmsg, "%s device", /* report device name */ + sim_dname (mp->dptr)); + + if (mp->lines > 1) /* more than one line? */ + sprintf (lmsg, ", line %d", i); /* report the line number */ + } + + sprintf (msgbuf, "%s%s%s\r\n\n", cmsg, dmsg, lmsg); + lp->cnms = sim_os_msec (); /* time of conn */ + if (!mp->buffered) { + lp->txbpi = 0; /* init buf pointers */ + lp->txbpr = (int32)(lp->txbsz - strlen (msgbuf)); + lp->rxcnt = lp->txcnt = lp->txdrp = 0; /* init counters */ + } + else + if (lp->txcnt > lp->txbsz) + lp->txbpr = (lp->txbpi + 1) % lp->txbsz; + else + lp->txbpr = (int32)(lp->txbsz - strlen (msgbuf)); + lp->tsta = 0; /* init telnet state */ + lp->xmte = 1; /* enable transmit */ + lp->dstb = 0; /* default bin mode */ + psave = lp->txbpi; /* save insertion pointer */ + lp->txbpi = lp->txbpr; /* insert connection message */ + tmxr_linemsg (lp, msgbuf); /* beginning of buffer */ + lp->txbpi = psave; /* restore insertion pointer */ + tmxr_poll_tx (mp); /* flush output */ + lp->txcnt -= (int32)strlen (msgbuf); /* adjust statistics */ + 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; + if (!lp->txbfd) + lp->txbpr = lp->txbpi = 0; + lp->xmte = 1; + lp->dstb = 0; +} + +/* 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 */ + + tmxr_debug (TMXR_DBG_RCV, lp, "Received", &(lp->rxb[lp->rxbpi]), nbytes); + + 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) { /* IAC + IAC */ + 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; + } + switch (tmp) { + case TN_WILL: /* IAC + WILL? */ + lp->tsta = TNS_WILL; + break; + case TN_WONT: /* IAC + WONT? */ + lp->tsta = TNS_WONT; + break; + case TN_DO: /* IAC + DO? */ + lp->tsta = TNS_DO; + break; + case TN_DONT: /* IAC + DONT? */ + lp->tsta = TNS_DONT; + 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; + + 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; + } + 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: + + 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_DO: /* pending DO request */ + case TNS_DONT: /* pending DONT request */ + 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 */ + if (nbytes != (lp->rxbpi-lp->rxbpr)) + tmxr_debug (TMXR_DBG_RCV, lp, "Remaining", &(lp->rxb[lp->rxbpi]), lp->rxbpi-lp->rxbpr); + } /* 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 count of available characters for line */ + +int32 tmxr_rqln (TMLN *lp) +{ + return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? TMXR_MAXBUF: 0)); +} + +/* 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; +} + +/* 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) && (!lp->txbfd)) /* no conn & not buffered? */ + if (lp->txlog) /* if it was logged, we got it */ + return SCPE_OK; + else { + ++lp->txdrp; /* lost */ + return SCPE_LOST; + } +#define TXBUF_AVAIL(lp) (lp->txbsz - tmxr_tqln (lp)) +#define TXBUF_CHAR(lp, c) { \ + lp->txb[lp->txbpi++] = (char)(c); \ + lp->txbpi %= lp->txbsz; \ + if (lp->txbpi == lp->txbpr) \ + lp->txbpr = (1+lp->txbpr)%lp->txbsz, ++lp->txdrp; \ + } + if ((lp->txbfd) || (TXBUF_AVAIL(lp) > 1)) { /* room for char (+ IAC)? */ + if (TN_IAC == (char) chr) /* char == IAC ? */ + TXBUF_CHAR (lp, TN_IAC); /* stuff extra IAC char */ + TXBUF_CHAR (lp, chr); /* buffer char & adv pointer */ + if ((!lp->txbfd) && (TXBUF_AVAIL (lp) <= TMXR_GUARD))/* near full? */ + lp->xmte = 0; /* disable line */ + return SCPE_OK; /* char sent */ + } + ++lp->txdrp; 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; + } +} + +/* 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]), lp->txbsz - lp->txbpr); + if (sbytes != SOCKET_ERROR) { /* ok? */ + tmxr_debug (TMXR_DBG_XMT, lp, "Sent", &(lp->txb[lp->txbpr]), sbytes); + lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */ + if (lp->txbpr >= lp->txbsz) /* 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 */ + tmxr_debug (TMXR_DBG_XMT, lp, "Sent", lp->txb, sbytes); + lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */ + if (lp->txbpr >= lp->txbsz) /* 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)? lp->txbsz: 0)); +} + +/* Open master socket */ + +t_stat tmxr_open_master (TMXR *mp, char *cptr) +{ + int32 i, port; + SOCKET sock; + TMLN *lp; + t_stat r; + + if (!isdigit(*cptr)) { + char gbuf[CBUFSIZE]; + cptr = get_glyph (cptr, gbuf, '='); + if (0 == MATCH_CMD (gbuf, "LOG")) { + if ((NULL == cptr) || ('\0' == *cptr)) + return SCPE_2FARG; + strncpy(mp->logfiletmpl, cptr, sizeof(mp->logfiletmpl)-1); + for (i = 0; i < mp->lines; i++) { + lp = mp->ldsc + i; + sim_close_logfile (&lp->txlogref); + lp->txlog = NULL; + lp->txlogname = (char*) realloc(lp->txlogname, CBUFSIZE); + if (mp->lines > 1) + sprintf(lp->txlogname, "%s_%d", mp->logfiletmpl, i); + else + strcpy(lp->txlogname, mp->logfiletmpl); + r = sim_open_logfile (lp->txlogname, TRUE, &lp->txlog, &lp->txlogref); + if (r == SCPE_OK) + setvbuf(lp->txlog, NULL, _IOFBF, 65536); + else { + free (lp->txlogname); + lp->txlogname = NULL; + break; + } + } + return r; + } + if ((0 == MATCH_CMD (gbuf, "NOBUFFERED")) || + (0 == MATCH_CMD (gbuf, "UNBUFFERED"))) { + if (mp->buffered) { + mp->buffered = 0; + for (i = 0; i < mp->lines; i++) { /* default line buffers */ + lp = mp->ldsc + i; + lp->txbsz = TMXR_MAXBUF; + lp->txb = (char *)realloc(lp->txb, lp->txbsz); + lp->txbfd = lp->txbpi = lp->txbpr = 0; + } + } + return SCPE_OK; + } + if (0 == MATCH_CMD (gbuf, "BUFFERED")) { + if ((NULL == cptr) || ('\0' == *cptr)) + mp->buffered = 32768; + else { + i = (int32) get_uint (cptr, 10, 1024*1024, &r); + if ((r != SCPE_OK) || (i == 0)) + return SCPE_ARG; + mp->buffered = i; + } + for (i = 0; i < mp->lines; i++) { /* initialize line buffers */ + lp = mp->ldsc + i; + lp->txbsz = mp->buffered; + lp->txbfd = 1; + lp->txb = (char *)realloc(lp->txb, lp->txbsz); + lp->txbpi = lp->txbpr = 0; + } + return SCPE_OK; + } + if (0 == MATCH_CMD (gbuf, "NOLOG")) { + if ((NULL != cptr) && ('\0' != *cptr)) + return SCPE_2MARG; + mp->logfiletmpl[0] = '\0'; + for (i = 0; i < mp->lines; i++) { /* close line logs */ + lp = mp->ldsc + i; + free(lp->txlogname); + lp->txlogname = NULL; + if (lp->txlog) { + sim_close_logfile (&lp->txlogref); + lp->txlog = NULL; + } + } + return SCPE_OK; + } + return SCPE_ARG; + } + 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; + smp_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; + if (!mp->buffered) { + lp->txbfd = lp->txbpi = lp->txbpr = 0; + lp->txbsz = TMXR_MAXBUF; + lp->txb = (char *)realloc(lp->txb, lp->txbsz); + } + lp->rxcnt = lp->txcnt = lp->txdrp = 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; + char pmsg[20], bmsg[32] = "", lmsg[64+PATH_MAX] = ""; + + tptr = (char *) malloc (strlen (cptr) + /* get string buf */ + sizeof(pmsg) + + sizeof(bmsg) + sizeof(lmsg)); + 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; + } + sprintf (pmsg, "%d", mp->port); /* copy port */ + if (mp->buffered) + sprintf (bmsg, ", buffered=%d", mp->buffered); /* buffer info */ + if (mp->logfiletmpl[0]) + sprintf (lmsg, ", log=%s", mp->logfiletmpl); /* logfile info */ + sprintf (tptr, "%s%s%s", pmsg, bmsg, lmsg); /* assemble all */ + 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, (int32) strlen (msg)); +} + +void tmxr_linemsg (TMLN *lp, const char *msg) +{ + int32 len; + + for (len = (int32) strlen (msg); len > 0; --len) + tmxr_putc_ln (lp, *msg++); +} + +/* Print connections - used only in named SHOW command */ + +void tmxr_fconns (SMP_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); +} + +/* Print statistics - used only in named SHOW command */ + +void tmxr_fstats (SMP_FILE *st, TMLN *lp, int32 ln) +{ + static const char *enab = "on"; + static const char *dsab = "off"; + + if (ln >= 0) + fprintf (st, "line %d:\b", ln); + if (!lp->conn) + fprintf (st, "line disconnected\n"); + if (lp->rxcnt) + fprintf (st, " input (%s) queued/total = %d/%d\n", + (lp->rcve? enab: dsab), + tmxr_rqln (lp), lp->rxcnt); + if (lp->txcnt || lp->txbpi) + fprintf (st, " output (%s) queued/total = %d/%d\n", + (lp->xmte? enab: dsab), + tmxr_tqln (lp), lp->txcnt); + if (lp->txbfd) + fprintf (st, " output buffer size = %d\n", lp->txbsz); + if (lp->txcnt || lp->txbpi) + fprintf (st, " bytes in buffer = %d\n", + ((lp->txcnt > 0) && (lp->txcnt > lp->txbsz)) ? lp->txbsz : lp->txbpi); + if (lp->txdrp) + fprintf (st, " dropped = %d\n", lp->txdrp); +} + +/* 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 */ + sim_open_logfile (cptr, TRUE, &lp->txlog, &lp->txlogref);/* 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? */ + sim_close_logfile (&lp->txlogref); /* 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 (SMP_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 = sim_unit_index(uptr); /* 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 (SMP_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 (SMP_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 (SMP_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 (SMP_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; +} + + +static const struct +{ + char value; + const char *name; +} +tn_chars[] = +{ + {TN_IAC, "TN_IAC"}, /* protocol delim */ + {TN_DONT, "TN_DONT"}, /* dont */ + {TN_DO, "TN_DO"}, /* do */ + {TN_WONT, "TN_WONT"}, /* wont */ + {TN_WILL, "TN_WILL"}, /* will */ + {TN_SB, "TN_SB"}, /* sub-option negotiation */ + {TN_GA, "TN_SG"}, /* go ahead */ + {TN_EL, "TN_EL"}, /* erase line */ + {TN_EC, "TN_EC"}, /* erase character */ + {TN_AYT, "TN_AYT"}, /* are you there */ + {TN_AO, "TN_AO"}, /* abort output */ + {TN_IP, "TN_IP"}, /* interrupt process */ + {TN_BRK, "TN_BRK"}, /* break */ + {TN_DATAMK, "TN_DATAMK"}, /* data mark */ + {TN_NOP, "TN_NOP"}, /* no operation */ + {TN_SE, "TN_SE"}, /* end sub-option negot */ + /* Options */ + {TN_BIN, "TN_BIN"}, /* bin */ + {TN_ECHO, "TN_ECHO"}, /* echo */ + {TN_SGA, "TN_SGA"}, /* sga */ + {TN_LINE, "TN_LINE"}, /* line mode */ + {TN_CR, "TN_CR"}, /* carriage return */ + {TN_LF, "TN_LF"}, /* line feed */ + {0, NULL} +}; + +AUTO_INIT_DEVLOCK(tmxr_debug_lock); +static char *tmxr_debug_buf = NULL; +static size_t tmxr_debug_buf_used = 0; +static size_t tmxr_debug_buf_size = 0; + +static void tmxr_buf_debug_char (char value) +{ + if (tmxr_debug_buf_used + 2 > tmxr_debug_buf_size) + { + tmxr_debug_buf_size += 1024; + tmxr_debug_buf = (char*) realloc(tmxr_debug_buf, tmxr_debug_buf_size); + } + tmxr_debug_buf[tmxr_debug_buf_used++] = value; + tmxr_debug_buf[tmxr_debug_buf_used] = '\0'; +} + +static void tmxr_buf_debug_string (const char *string) +{ + while (*string) + tmxr_buf_debug_char (*string++); +} + +void tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize) +{ + if (lp->mp->dptr && (dbits & lp->mp->dptr->dctrl)) + { + AUTO_LOCK(tmxr_debug_lock); + int i, j; + + tmxr_debug_buf_used = 0; + if (tmxr_debug_buf) + tmxr_debug_buf[tmxr_debug_buf_used] = '\0'; + for (i = 0; i < bufsize; ++i) + { + for (j = 0; 1; ++j) + { + if (NULL == tn_chars[j].name) + { + tmxr_buf_debug_char (buf[i]); + break; + } + if (buf[i] == tn_chars[j].value) + { + tmxr_buf_debug_char ('_'); + tmxr_buf_debug_string (tn_chars[j].name); + tmxr_buf_debug_char ('_'); + break; + } + } + } + sim_debug (dbits, lp->mp->dptr, "%s %d bytes '%s'\n", msg, bufsize, tmxr_debug_buf); + } +} diff --git a/src/sim_tmxr.h b/src/sim_tmxr.h new file mode 100644 index 0000000..e6c632e --- /dev/null +++ b/src/sim_tmxr.h @@ -0,0 +1,125 @@ +/* sim_tmxr.h: terminal multiplexor definitions + + 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. + + 17-Jan-11 MP Added buffered line capabilities + 20-Nov-08 RMS Added three new standardized SHOW routines + 27-May-08 JDB Added lnorder to TMXR structure, + added tmxr_set_lnorder and tmxr_set_lnorder + 14-May-08 JDB Added dptr to TMXR structure + 04-Jan-04 RMS Changed TMXR ldsc to be pointer to linedesc array + Added tmxr_linemsg, logging (from Mark Pizzolato) + 29-Dec-03 RMS Added output stall support, increased buffer size + 22-Dec-02 RMS Added break support (from Mark Pizzolato) + 20-Aug-02 RMS Added tmxr_open_master, tmxr_close_master, tmxr.port + 30-Dec-01 RMS Renamed tmxr_fstatus, added tmxr_fstats + 20-Oct-01 RMS Removed tmxr_getchar, formalized buffer guard, + added tmxr_rqln, tmxr_tqln +*/ + +#ifndef _SIM_TMXR_H_ +#define _SIM_TMXR_H_ 0 + +#define TMXR_V_VALID 15 +#define TMXR_VALID (1 << TMXR_V_VALID) +#define TMXR_MAXBUF 256 /* buffer size */ +#define TMXR_GUARD 12 /* buffer guard */ + +#define TMXR_DBG_XMT 0x10000 /* Debug Transmit Data */ +#define TMXR_DBG_RCV 0x20000 /* Debug Received Data */ + +typedef struct tmln TMLN; +typedef struct tmxr TMXR; + +struct tmln { + SOCKET conn; /* line conn */ + uint32 ipad; /* IP address */ + uint32 cnms; /* conn time */ + int32 tsta; /* Telnet state */ + int32 rcve; /* rcv enable */ + int32 xmte; /* xmt enable */ + int32 dstb; /* disable Tlnt bin */ + int32 rxbpr; /* rcv buf remove */ + int32 rxbpi; /* rcv buf insert */ + int32 rxcnt; /* rcv count */ + int32 txbpr; /* xmt buf remove */ + int32 txbpi; /* xmt buf insert */ + int32 txcnt; /* xmt count */ + int32 txdrp; /* xmt drop count */ + int32 txbsz; /* xmt buffer size */ + int32 txbfd; /* xmt buffered flag */ + SMP_FILE *txlog; /* xmt log file */ + SMP_FILEREF *txlogref; /* xmt log file reference */ + char *txlogname; /* xmt log file name */ + char rxb[TMXR_MAXBUF]; /* rcv buffer */ + char rbr[TMXR_MAXBUF]; /* rcv break */ + char *txb; /* xmt buffer */ + TMXR *mp; /* back pointer to mux */ + }; + +struct tmxr { + int32 lines; /* # lines */ + int32 port; /* listening port */ + SOCKET master; /* master socket */ + TMLN *ldsc; /* line descriptors */ + int32 *lnorder; /* line connection order */ + DEVICE *dptr; /* multiplexer device */ + char logfiletmpl[FILENAME_MAX]; /* template logfile name */ + int32 buffered; /* Buffered Line Behavior and Buffer Size Flag */ + }; + +int32 tmxr_poll_conn (TMXR *mp); +void tmxr_reset_ln (TMLN *lp); +int32 tmxr_getc_ln (TMLN *lp); +void tmxr_poll_rx (TMXR *mp); +t_stat tmxr_putc_ln (TMLN *lp, int32 chr); +void tmxr_poll_tx (TMXR *mp); +t_stat tmxr_open_master (TMXR *mp, char *cptr); +t_stat tmxr_close_master (TMXR *mp); +t_stat tmxr_attach (TMXR *mp, UNIT *uptr, char *cptr); +t_stat tmxr_detach (TMXR *mp, UNIT *uptr); +t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +void tmxr_msg (SOCKET sock, char *msg); +void tmxr_linemsg (TMLN *lp, const char *msg); +void tmxr_fconns (SMP_FILE *st, TMLN *lp, int32 ln); +void tmxr_fstats (SMP_FILE *st, TMLN *lp, int32 ln); +t_stat tmxr_set_log (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tmxr_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tmxr_show_log (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat tmxr_dscln (UNIT *uptr, int32 val, char *cptr, void *desc); +int32 tmxr_rqln (TMLN *lp); +int32 tmxr_tqln (TMLN *lp); +t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tmxr_show_lnorder (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat tmxr_show_summ (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat tmxr_show_cstat (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat tmxr_show_lines (SMP_FILE *st, UNIT *uptr, int32 val, void *desc); +void tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize); + +#endif + diff --git a/src/sim_try.cpp b/src/sim_try.cpp new file mode 100644 index 0000000..aa16006 --- /dev/null +++ b/src/sim_try.cpp @@ -0,0 +1,93 @@ +#include "sim_defs.h" + +#if defined(USE_C_TRY_CATCH) +smp_tls_key sim_seh_frame::tls; + +void sim_seh_frame::unregister_object(sim_try_auto_destructable* obj) +{ + int k, n; + t_bool found = FALSE; + + for (n = objcnt - 1; n >= 0; n--) + { + if (objlist[n] == obj) + { + found = TRUE; + break; + } + } + + if (likely(found)) + { + objcnt--; + + for (k = n; k < objcnt; k++) + objlist[k] = objlist[k + 1]; + } +} + +void sim_seh_frame::destroy_objects() +{ + while (objcnt) + { + objcnt--; + objlist[objcnt]->onDestroy(TRUE); + } + /* + * If exception happens in a destructor, the intended behavior is: + * - sim_throw inside destructor for object inside sim_try => goto catch + * - sim_throw inside destructor for object inside sim_catch/sim_catch_all/sim_finally => unwind and handle in outer frame + */ +} + +void sim_seh_frame::do_throw(sim_exception* new_ex) +{ + sim_seh_frame* fp = get_current(); + if (unlikely(fp == NULL)) + { + fprintf(stderr, "\r\nUnhandled exception\r\n"); + abort(); + } + if (unlikely(new_ex == NULL)) + { + fprintf(stderr, "\r\nTrying to throw null exception\r\n"); + abort(); + } + fp->ex = new_ex; + fp->destroy_objects(); + if (fp->state == SIM_SEH_STATE_DO_TRY) + fp->state = SIM_SEH_STATE_DO_CATCH | SIM_SEH_STATE_DO_FINALLY | SIM_SEH_STATE_DO_ENDTHROW; + else if (fp->state & SIM_SEH_STATE_DOING_FINALLY) + fp->state = SIM_SEH_STATE_DO_ENDTHROW; + else if (fp->state & SIM_SEH_STATE_DOING_CATCH) + fp->state = SIM_SEH_STATE_DO_FINALLY | SIM_SEH_STATE_DO_ENDTHROW; + barrier(); + longjmp(fp->jmpbuf, fp->state); +} + +void sim_seh_frame::do_rethrow() +{ + sim_seh_frame* fp = get_current(); + if (unlikely(fp == NULL)) + { + fprintf(stderr, "\r\nUnhandled exception\r\n"); + abort(); + } + if (unlikely(fp->state & SIM_SEH_STATE_DOING_FINALLY)) + { + fprintf(stderr, "\r\nsim_rethrow in finally block\r\n"); + abort(); + } + if (likely(fp->state & SIM_SEH_STATE_DOING_CATCH)) + { + fp->destroy_objects(); + fp->state = SIM_SEH_STATE_DO_FINALLY | SIM_SEH_STATE_DO_ENDTHROW; + barrier(); + longjmp(fp->jmpbuf, fp->state); + } + else + { + panic("sim_rethrow not in catch block"); + } +} +#endif diff --git a/src/sim_try.h b/src/sim_try.h new file mode 100644 index 0000000..fb069ed --- /dev/null +++ b/src/sim_try.h @@ -0,0 +1,275 @@ +/* + * sim_try framework is itended to provide a limited but efficient version of C++ like structured + * exception handling on platforms where native C++ try/throw/catch sequence is costly, more costly + * than setjmp/longjmp. + * + * If symbol USE_C_TRY_CATCH is not defined, framework code compiles to natvie C++ try/catch/finally + * sequence. + * + * If symbol USE_C_TRY_CATCH is defined, framework code compiles to chained setjmp/longjmp sequence. + * + * Code sequence should be written as follows: + * + * sim_try + * { + * ... code ... + * } + * sim_catch(T1, e1) + * { + * ... code ... + * } + * sim_catch(T2, e2) + * { + * ... code ... + * } + * sim_catch_all + * { + * ... code ... + * } + * sim_finally + * { + * ... code ... + * } + * sim_end_try + * + * Elements sim_catch, sim_catch_all and sim_finally are optional, but one of them should be present, + * otherwise some C++ compilers will complain. Elements sim_try and sim_end_try are mandatory. + * + * To throw an exception use macro sim_throw(e). + * + * To rethrow an exception (from inside sim_catch or sim_catch_all block) use macro sim_rethrow. + * + * Do not intermix sim_try framework with C++ exception handling. + * Do not intermix sim_try framework with setjmp/longjmp across sim_try frames. + * + * All automatic (on-stack) variables modified between the start of sim_try block and sim_throw whose values + * will be used by the code later must be declared sim_try_volatile. + * + * All automatic (on-stack) variables modified between the start of sim_catch or sim_catch_all block and + * sim_throw/sim_rethrow whose values will be used by the code later must be declared sim_try_volatile. + * + * All C++ class objects that must be destroyed on unwinding of stack frames by properly calling their + * destructors must be derived from class sim_try_auto_destructable and have the following structure: + * + * class AAA : public sim_try_auto_destructable + * { + * publuc: + * AAA() + * { + * ... regular AAA constructor code ... + * onConstructor(); + * } + * + * ~AAA() + * { + * onDestroy(FALSE); + * } + * + * void onDestroy(t_bool unregistered) + * { + * if (onDestructor(unregistered)) + * { + * ... regular AAA destructor code ... + * } + * } + * }; + * + * If USE_C_TRY_CATCH is defined and "break", "return" or "goto" statements are executed inside try or catch/catch_all block, + * that transfer control outside of the block, the finally block will not be executed. + * + * These statements are not meant to should never be used inside the finally block. + * + */ + +#if !defined(USE_C_TRY_CATCH) +# define sim_try try +# define sim_catch(T, e) catch(T* e) +# define sim_catch_all catch (...) +# define sim_throw(e) throw e +# define sim_rethrow throw +# define sim_finally finally +# define sim_end_try + +# define sim_try_volatile +# define sim_noreturn_int32 + +class sim_try_auto_destructable +{ +public: + void onConstructor() {} + virtual void onDestroy(t_bool unregistered) = 0; + t_bool onDestructor(t_bool unregistered) { return TRUE; } +}; +#else // defined(USE_C_TRY_CATCH) +# include +# define sim_try_volatile volatile + +class sim_seh_frame; +class sim_exception; + +class sim_try_auto_destructable +{ +private: + t_bool _constructed; + sim_seh_frame* _sim_seh_fp; +public: + sim_try_auto_destructable() + { + _constructed = FALSE; + _sim_seh_fp = NULL; + } + + void onConstructor(); + virtual void onDestroy(t_bool unregistered) = 0; + t_bool onDestructor(t_bool unregistered); +}; + +#define SIM_MAX_SEH_OBJECTS 16 + +#define SIM_SEH_STATE_DO_TRY 0 +#define SIM_SEH_STATE_DO_CATCH (1 << 0) +#define SIM_SEH_STATE_DO_FINALLY (1 << 1) +#define SIM_SEH_STATE_DO_ENDTHROW (1 << 2) + +#define SIM_SEH_STATE_DOING_CATCH (1 << 3) +#define SIM_SEH_STATE_DOING_FINALLY (1 << 4) + +class sim_seh_frame +{ +public: + jmp_buf jmpbuf; + volatile int state; + volatile sim_exception* ex; +private: + volatile t_bool linked; + volatile sim_seh_frame* prev; + volatile int objcnt; + sim_try_auto_destructable* volatile objlist[SIM_MAX_SEH_OBJECTS]; + static smp_tls_key tls; + +public: + SIM_INLINE static t_bool init() + { + return smp_tls_alloc(& tls); + } + + SIM_INLINE static sim_seh_frame* get_current() + { + return (sim_seh_frame*) tls_get_value(tls); + } + + sim_seh_frame() + { + linked = FALSE; + ex = NULL; + state = SIM_SEH_STATE_DO_TRY; + objcnt = 0; + prev = get_current(); + tls_set_value(tls, this); + linked = TRUE; + } + + SIM_INLINE ~sim_seh_frame() + { + unlink(); + } + + SIM_INLINE void register_object(sim_try_auto_destructable* obj) + { + if (unlikely(objcnt == SIM_MAX_SEH_OBJECTS)) + panic("Object count exceeeds SIM_MAX_SEH_OBJECTS"); + objlist[objcnt++] = obj; + } + + void unregister_object(sim_try_auto_destructable* obj); + void destroy_objects(); + SIM_INLINE void endthrow() { unlink(); do_throw((sim_exception*) this->ex); } + static void do_throw(sim_exception* new_ex); + static void do_rethrow(); + +private: + SIM_INLINE void unlink() + { + if (likely(linked)) + { + tls_set_value(tls, (void*) prev); + linked = FALSE; + /* + * Note: We cannot perform destroy_objects here, since objects may have already + * gone off the stack (and usually will) at a point where unlink() is called. + * "objcnt" should normally be 0 when unlink is called. The only legitimately + * possible case it may be not 0 is if object destructor throws an exception + * when called from inside of destroy_objects() while unwinding. + */ + } + } +}; + +SIM_INLINE void sim_try_auto_destructable::onConstructor() +{ + sim_seh_frame* fp = sim_seh_frame::get_current(); + _sim_seh_fp = fp; + _constructed = TRUE; + if (likely(fp != NULL)) + fp->register_object(this); +} + +SIM_INLINE t_bool sim_try_auto_destructable::onDestructor(t_bool unregistered) +{ + if (likely(_constructed)) + { + if (unregistered == FALSE && _sim_seh_fp) + _sim_seh_fp->unregister_object(this); + _constructed = FALSE; + return TRUE; + } + else + { + return FALSE; + } +} + +# define sim_try \ + { \ + sim_seh_frame _sim_seh_frame; \ + barrier(); \ + int _sim_seh_state = setjmp(_sim_seh_frame.jmpbuf); \ + barrier(); \ + if (_sim_seh_state == SIM_SEH_STATE_DO_TRY) \ + { + +# define sim_catch(T, e) \ + } \ + if (unlikely((_sim_seh_state & SIM_SEH_STATE_DO_CATCH)) && ((sim_exception*) _sim_seh_frame.ex)->isType(T##_typeid)) \ + { \ + _sim_seh_state &= ~(SIM_SEH_STATE_DO_CATCH | SIM_SEH_STATE_DO_ENDTHROW); \ + _sim_seh_frame.state |= SIM_SEH_STATE_DOING_CATCH; \ + T* e = (T*) _sim_seh_frame.ex; + +# define sim_catch_all \ + } \ + if (unlikely(_sim_seh_state & SIM_SEH_STATE_DO_CATCH)) \ + { \ + _sim_seh_state &= ~(SIM_SEH_STATE_DO_CATCH | SIM_SEH_STATE_DO_ENDTHROW); \ + _sim_seh_frame.state |= SIM_SEH_STATE_DOING_CATCH; + +# define sim_finally \ + } \ + if (_sim_seh_state & SIM_SEH_STATE_DO_FINALLY) \ + { \ + _sim_seh_state &= ~SIM_SEH_STATE_DO_CATCH; \ + _sim_seh_frame.state |= SIM_SEH_STATE_DOING_FINALLY; + +# define sim_end_try \ + } \ + if (unlikely(_sim_seh_state & SIM_SEH_STATE_DO_ENDTHROW)) \ + { \ + _sim_seh_frame.endthrow(); \ + } \ + } + +# define sim_throw(e) sim_seh_frame::do_throw(e) +# define sim_rethrow sim_seh_frame::do_rethrow() + /* to suppress compiler warning: */ +# define sim_noreturn_int32 return 0 +#endif diff --git a/src/sim_util.cpp b/src/sim_util.cpp new file mode 100644 index 0000000..9119cd5 --- /dev/null +++ b/src/sim_util.cpp @@ -0,0 +1,159 @@ +/* + * Utility and data structure classes + */ + +#include "sim_defs.h" + +#define aligned_sizeof(T) ((unsigned int)(t_addr_off)(char*)(((T*) 0) + 1)) + +char* dupstr(const char* s, t_bool canFail) +{ + if (! s) return NULL; + char* p = (char*) malloc(strlen(s) + 1); + if (p) + { + strcpy(p, s); + return p; + } + else + { + if (! canFail) + ThrowOutOfMemory(); + return NULL; + } +} + +void ThrowOutOfMemory() +{ + panic("Out of memory"); +} + +/* ================================================= sim_cstream ================================================= */ + +sim_cstream::sim_cstream() +{ + const size_t inisize = 512; + m_bp = (char*) malloc(inisize); + if (! m_bp) ThrowOutOfMemory(); + *m_bp = 0; + m_allocsize = inisize; + m_w = 0; + m_r = 0; +} + +sim_cstream::~sim_cstream() +{ + if (m_bp) + free(m_bp); +} + +void sim_cstream::append(const char* s) +{ + size_t len = strlen(s); + if (m_w + len + 1 < m_allocsize) + { + size_t size = m_allocsize * 2; + char* bp = (char*) realloc(m_bp, size); + if (! bp) ThrowOutOfMemory(); + m_bp = bp; + m_allocsize = size; + } + strcpy(m_bp + m_w, s); + m_w += len; +} + +void sim_cstream::append(char c) +{ + if (m_w + 2 < m_allocsize) + { + size_t size = m_allocsize * 2; + char* bp = (char*) realloc(m_bp, size); + if (! bp) ThrowOutOfMemory(); + m_bp = bp; + m_allocsize = size; + } + m_bp[m_w++] = c; + m_bp[m_w + 1] = '\0'; +} + +char* sim_cstream::read_line(char *buf, int32 bufsize) +{ + char* cp = read_next(); + if (*cp == 0 || bufsize == 0) return NULL; + char* xp = cp; + while (*xp && *xp != '\n' && *xp != '\r') xp++; + int len = (int) (xp - cp); + if (len >= bufsize) + len = bufsize - 1; + if (len) strncpy(buf, cp, len); + buf[len] = 0; + read_setnext(*xp ? xp + 1 : xp); + return buf; +} + +void sim_cstream::rewind() +{ + m_r = 0; +} + +/* ================================================= sim_stack ================================================= */ + +template +sim_stack::sim_stack() +{ + const int inidepth = 16; + m_stack = (T*) malloc(aligned_sizeof(T) * inidepth); + if (! m_stack) ThrowOutOfMemory(); + m_depth = 0; + m_alloc = inidepth; +} + +template +sim_stack::~sim_stack() +{ + if (m_stack) + free(m_stack); +} + +template +void sim_stack::push(const T& value) +{ + if (m_depth >= m_alloc) + { + int alloc = m_alloc * 2; + T* p = (T*) realloc(m_stack, alloc * aligned_sizeof(T)); + if (! p) ThrowOutOfMemory(); + m_stack = p; + m_alloc = alloc; + } + m_stack[m_depth++] = value; +} + +template +const T& sim_stack::peek() const +{ + if (m_depth == 0) + panic("Empty stack"); + return m_stack[m_depth - 1]; +} + +template +T sim_stack::pop() +{ + if (m_depth == 0) + panic("Empty stack"); + return m_stack[--m_depth]; +} + +template +void sim_stack::reset() +{ + m_depth = 0; +} + +/* ======================================= forced template instantiations ======================================= */ + +typedef sim_cstream* sim_cstream_ptr_t; +template class sim_stack; + +char* sim_exception_SimError::nomem_msg = "Insufficient memory to record error information"; \ No newline at end of file diff --git a/src/sim_util.h b/src/sim_util.h new file mode 100644 index 0000000..2acb230 --- /dev/null +++ b/src/sim_util.h @@ -0,0 +1,92 @@ +/* + * Utility and data structure classes + */ + +void ThrowOutOfMemory(); + +char* dupstr(const char* s, t_bool canFail = TRUE); +#define dupstr_exc(s) dupstr((s), FALSE) + +class sim_cstream +{ +public: + sim_cstream(); + ~sim_cstream(); + /* writer side */ + void append(const char* s); + void append(char c); + /* reader side */ + char* read_start() { return m_bp; } + char* read_next() { return m_bp + m_r; } + void read_setnext(const char* p) { m_r = p - m_bp; } + char *read_line(char *buf, int32 bufsize); + void rewind(); + +protected: + char* m_bp; + size_t m_w; + size_t m_r; + size_t m_allocsize; +}; + +/* Note: sim_stack is currently set up to handle only simple types + with no constructors or destructors */ +template +class sim_stack +{ +public: + sim_stack(); + ~sim_stack(); + void push(const T& value); + const T& peek() const; + T pop(); + int depth() const { return m_depth; } + void reset(); +protected: + T* m_stack; + int m_depth; + int m_alloc; +}; + +template +class sim_ring_buffer +{ + T buf[rsize]; + int maxsize; + int count; + int rd; + int wr; +public: + sim_ring_buffer() + { + maxsize = rsize; + count = 0; + rd = wr = 0; + } + void clear() + { + count = rd = wr = 0; + } + t_bool is_empty() + { + return count == 0; + } + t_bool put(const T& c) + { + if (count == maxsize) + return FALSE; + buf[wr] = c; + wr = (wr + 1) % maxsize; + count++; + return TRUE; + } + t_bool get(T* c) + { + if (count == 0) + return FALSE; + *c = buf[rd]; + rd = (rd + 1) % maxsize; + count--; + return TRUE; + } +}; \ No newline at end of file diff --git a/src/x64_util/build.cmd b/src/x64_util/build.cmd new file mode 100644 index 0000000..6094dd0 --- /dev/null +++ b/src/x64_util/build.cmd @@ -0,0 +1,2 @@ +set ML_LOC=V:\WinKernel\WinDDK\7600.16385.1\bin\x86\amd64 +%ML_LOC%\ml64 /nologo /c /Cx /Zi x64_util.asm \ No newline at end of file diff --git a/src/x64_util/x64_util.asm b/src/x64_util/x64_util.asm new file mode 100644 index 0000000..81c7445 --- /dev/null +++ b/src/x64_util/x64_util.asm @@ -0,0 +1,72 @@ + TITLE X64_UTIL + + ;;.686P + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; Helper assembler routines for VAX MP x64 build, MSVC 64-bit compiler. +; +; Compiled with ML64. +; ML64 comes with Windows DDK. +; +; Perhaps it might be also compiled (but this was not tested) with YASM, NASM or JWASM. +; See http://en.wikibooks.org/wiki/X86_Assembly/x86_Assemblers for links. +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;+ +; +; uint64 __fastcall x64_native_adawi(uint16* pval, uint16 addend) +; +; Performs interlockeed addition of "addendum" to "*pval". +; Returns RFLAGS. +; +; Inputs: +; rcx = pval +; dx = addend +; +; Outputs: +; rax = result (rflags after addition) +; +;- + +PUBLIC x64_native_adawi +_TEXT SEGMENT + ALIGN 4 +x64_native_adawi PROC + lock add word ptr [rcx], dx + pushfq + pop rax + ret 0 +x64_native_adawi ENDP +_TEXT ENDS + +;+ +; +; t_byte __fastcall x64_cas_byte(t_byte* p, byte_t old_value, byte_t new_value) +; +; Performs interlockeed CAS on "*p". +; Returns old value of "*p". +; +; Inputs: +; rcx = p +; dl = old_value +; r8b = new_value +; +; Outputs: +; eax = result, old value of "*p" +; +;- + +PUBLIC x64_cas_byte +_TEXT SEGMENT + ALIGN 4 +x64_cas_byte PROC + mov al, dl + lock cmpxchg byte ptr [rcx], r8b + movzx eax, al + ret 0 +x64_cas_byte ENDP +_TEXT ENDS + +END diff --git a/src/x64_util/x64_util.obj b/src/x64_util/x64_util.obj new file mode 100644 index 0000000..1bc6b22 Binary files /dev/null and b/src/x64_util/x64_util.obj differ