diff --git a/SEL32/README.md b/SEL32/README.md new file mode 100644 index 00000000..83d9bf0b --- /dev/null +++ b/SEL32/README.md @@ -0,0 +1,112 @@ + +# SEL32 Concept/32 Simulator + +This is a working simulator for the SEL Concept/32 computer. The current +version is for the SEL 32/27, 32/67, 32/77, 32/87, 32/97, V6, and V9 +computers. All of the processors except for the 32/77 can run the Gould +diags. Operational support for the 32/77 computers may be added in the +future. + +# SEL Concept/32 + +This simulator is capable of running UTX2.1A, UTX2.1B, MPX 1.5F, MPX 3.4, +MPX 3.5, MPX 3.6, and SEL diagnostics. Actually any software using the SEL +instruction set should work. It is capable of creating a disk image for the +O/S from a UTX or MPX SDT tape. The disk image can be booted, initialized, +and can run many of the UTX and MPX utilities and programs. Ethernet is +supported on UTX and will be added to MPX in the future. Eight terminals +can be used to access MPX or UTX via Telnet port 4747. The sumulator has +support for excess 64 floating point arithmetic and passes the 32/27 and +32/67 FP diags. UTX is the SEL version of System V Unix and BSD Unix +ported to the V6 and V9 processors. UTX utilizes the basemode instruction +set and a virtual memory system supported by the V6 & V9 CPUs. The system +needs further testing to solidify the SEL32 simulator code in all of the +supported environmenets and hardware configurations. + +# SEL32 installation configuration files in the installs directory: + +The installs directory contains the simh command files to install and run +multiple UTX, MPX1X, and MPX3X systems. The install tape images are in +the tapes directory and created disks are in the dsk directory. The dsk +and tapes directories are initially missing but will be created during +the installation. The required tape(s) are read from the repo at +https://github.com/AZBevier/SEL32-installs when the installation +simh command file is run. The tapes are tar gzipped files that are +extracted during the installation. Only the tape(s) required for the +specific UTX or MPX installation are downloaded. A bootable disk is +created in the dsk directory. This disk can then be booted and the +installed operating system executed by using a second simh command file. +See the README.md file in the install directory for a description of the +available configurations. + +# SEL32 test configuration files in the tests directory: + +The tests directory contains the SEL32 machine diagnostic command file and +bootable tape. They are used by the simh makefile to test the SEL32 +simulator after it built. All of the instruction set and operating modes +are validated by the diagnostic. There is a pass/failure indication from +the diagnostic. The command file sel32_test.ini is used by the makefile. +The command file diag.ini can be run at any time by the user to rerun the +diagnostics. There is an example of the initial configuration file (ICL) +used by the diagnostic. The machine configuration is loaded by the diag +or the UTX or MPX operating system at boot time. See the MPX manuals at +bitsavers.org/pdf/sel/sel32_mpx for using MPX 1X or 3X. + +#sel32_test.ini - diag.tap; type "../sel32_test.ini" or run from makefile. +Auto run by sel32 makefile to validate build or can be run at any time +to validate system functionality. + +#diag.ini - diag.tap; type "../sel32 diag.ini" to run. +This is the SEL32 memory diagnostic that validates the SEL32 instruction +set and memory management sumulated hardware. Any errors are reported +to the terminal. Different cpu models can be configured to test various +cpu model features. + +Available Level One Diagnostic boot tape in tests directory: +diag.ini - command file to start diags. Type "../sel32 diag.ini" +diag.tap - bootable level one diagnostic tape w/auto testing. + Set cpu type to 32/27, 32/67, 32/87, 32/97, V6 or V9. All + cpu models now run all diagnostics provided on the + diagnostic tape. Running DEXP stand alone causes input + to stop after a few characters are entered. More testing + is still required. + + CV.CSL - Firmware control diag. Disabled in auto testing. + CV.CP1 - CPU diag part 1 runs OK. + CV.CP2 - CPU diag part 2 runs OK. + CV.CP3 - CPU diag part 3 runs OK. + CV.EAD - Effective address diag runs OK. + CV.BRD - Base register instruction diag runs OK + Not supported on 32/27. + CV.INT - Interrupt diag runs OK. + CV.TRP - Traps diag runs OK. + CV.CMD - Cache/Shadow diag. Disabled in auto testing. + CN.MMM - Non virtual memory diag runs OK. + VM.MMM - Virtual memory diag for V6 & V9 runs OK. + CV.IPT - IPU trap diag. Disabled in auto testing. + CV.CSD - WCS read/write trap diag. Disabled in auto testing. + CV.CON - Operators Console runs all tests for all CPUs. + CV.DXP - Diagnostic executive for level 2 diags. OK in batch. + 67.FPT - Level two floating point diag runs under DXP OK. + CV.ITD - Level two interval timer diag runs under DXP OK. + + Set GPR[0] = 0xffffffff before booting from tape to disable the + auto test and go to the Diagnostic Overlay Loader (DOL>) prompt. + Testing is extremely difficult without any source for the + diagnostics. Updates to follow as tests are corrected. + +Other MPX versions support: + I have recently received some old MPX 3.X save tapes. Using + these I have been able to hand build a MPX3.6 SDT tape that + can be used to install MPX3.6. Once installed, the system can + be used to build a new user SDT tape and install it elsewhere. + Both based and non-based O/S images can be created. More images + for installation will be made available in the future as I work + my way through the save tapes. I still do not have a master SDT + tape for any of the MPX 1.X or MPX 3.X systems. I have a + 1600/6250 BPI tape drive that can read 9 track tapes and convert + them to .tap files. If you have a master SDT, I would be very + thankfull. Please keep looking. + +James C. Bevier +02/28/2022 diff --git a/SEL32/installs/.gitignore b/SEL32/installs/.gitignore new file mode 100644 index 00000000..7ba7fdd8 --- /dev/null +++ b/SEL32/installs/.gitignore @@ -0,0 +1,6 @@ +#tape files are dynamically downloaded from github.com/AZBevier/SEL32-installs as they are needed. +/tape +#disk files are created by the simh installation command files. These are SEL32 bootable disks. +/dsk +#ignore lp output from MPX-1X install. +lprout diff --git a/SEL32/installs/README.md b/SEL32/installs/README.md new file mode 100644 index 00000000..82df5685 --- /dev/null +++ b/SEL32/installs/README.md @@ -0,0 +1,172 @@ + +# SEL32 Concept/32 Simulator + +The installs directory contains the simh command files to install and run +multiple UTX, MPX1X, and MPX3X systems. The install tape images are in +the tapes directory and created disks are in the dsk directory. The dsk +and tapes directories are initially missing but will be created during +the installation. The required tape(s) are read from the repo at +https://github.com/AZBevier/SEL32-installs when the installation +simh command file is run. The tapes are tar gzipped files that are +extracted during the installation. Only the tape(s) required for the +specific UTX or MPX installation are downloaded. A bootable disk is +created in the dsk directory. This disk can then be booted and the +installed operating system executed by using a second simh command file. + +This method of installation replaces the prebuilt disks from previous +releases. This allows more versions to be supplied in the minimum +amount of storage space. Minimal or no user input is required to +create the bootable system. The command files are in pairs, one to do +the install and one to execute the installed system. Some MPX systems +also contain some of the NBC software environment. See the NBC project +at github.com/AZBevier/nbc for all of the NBC software. See the MPX +manuals at bitsavers.org/pdf/sel/sel32_mpx for using MPX 1X or 3X. + +# SEL32 installation configuration files in the install directory: + +-------------------- + +#sel32load1x.ini - sel32sdt.tap; type "../sel32 sel32load1x.ini +This is a minimal MPX 1.5F installation to a UDP/DPII 300 MB disk. It +will initialize the disk and install an MPX bootable system. The disk +image is in the dsk directory named sel32disk. + +#sel32run1x.ini - dsk/sel32disk; type "../sel32 sel32run1x.ini" to run. +The disk is booted up to the TSM> prompt logged in as "SYSTEM". Use +@@A to log into the system console. + +-------------------- + +#loaddma1x67.ini - mpx1xsdt.tap; type "../sel32 loaddma1x67.ini +This is an MPX 1.5F installation to a UDP/DPII 300 MB disk. It will +initialize the disk and install an MPX bootable system. The disk +image is in the dsk directory named mpx1xdma0. Once the MPX software +is loaded an MPX command file is executed that runs the SYSGEN program +to create a new MPX O/S image. That system is then restarted to +install the new image to the disk as the new bootable image. The +system reboots to the new image, logs in as SYSTEM and exits TSM. +Use @@A to relogin to the console. + +#rundma1x67.ini - dsk/mpx1xdma0; type "../sel32 rundma1x67.ini" to run. +The disk is booted up to the TSM> prompt and logged in as "SYSTEM". +MPX can be accessed from a second Linux screen by using the command +"telnet localhost 4747". This will bring up the "ENTER OWNERNAME +AND KEY:". Any name is valid, but SYSTEM should be used. At the +"TSM>" prompt, type "EXIT" to exit TSM. Use ^G to get the login +prompt when the "RING IN FOR SERVICE" message is displayed. + +-------------------- + +#loaddma21b.ini - utx21b1.tap, utx21b2.tap, utx21b3.tap; +type "../sel32 loaddma21b.ini" to install UTX 21b to UDP/DPII disk. +This is an automated installation of UTX 21b to disk. Two disks, +21bdisk0.dma and 21bdisk1.dma are initialized and then the file +systems are created and loaded. Tape 1 loads "/" and tape 2 and 3 +loads "/usr.POWERNODE" filesystems. The system boots from tape and +installs the root filesystem. The system restarts and boots from +the new root filesystem where the 2nd & 3rd tapes are then loaded to +/usr.POWERNODE. A third empty file system is created and mounted +as /usr/POWERNODE/src. The second disk is one large filesystem and is +mounted under /home. Several files are modified during installation +to allow the system to be booted into multiuser mode. Only the user +"root" is created and is the only allowable user login. + +#rundma21b.ini - dsk/21bdisk0.dma & dsk/21bdisk1.dma; +type "../sel32 rundma21b.ini" to run the installed UTX system. +The disk is booted up to the "login:" prompt for the user to login +as "root" in multi-user mode. + +-------------------- + +#loadscsi21b.ini - utx21b1.tap, utx21b2.tap, utx21b3.tap; +type "../sel32 loadscsi21b.ini" to install UTX 21b to MFP SCSI disks. +This is an automated installation of UTX 21b to disk. Two disks, +scsidiska0 and scsidiska1 are initialized and then the file systems +are created and loaded. Tape 1 loads root "/" and tapes 2 and 3 +loads "/usr.POWERNODE" filesystem. The system boots from tape and +installs the root filesystem. The system restarts and boots from +the new root filesystem where the 2nd & 3rd tapes are then loaded to +/usr.POWERNODE. A third empty file system is created and mounted +as /usr/POWERNODE/src. The second disk is one large filesystem and is +mounted under /home. Several files are modified during installation +to allow the system to be booted into multiuser mode. Only the user +"root" is created and is the only allowable user login. + +#rundscsi21b.ini - dsk/scsidiska0 & dsk/scsidiska1; +type "../sel32 rundscsi21b.ini" to run the installed UTX system. +The disk is booted up to the "login:" prompt for the user to login +as "root" in multi-user mode. + +-------------------- + +#loadscsi3x.ini - mpxsdt69.tap; +type "../sel32 loadscsi3x.ini" to install MPX 3.4 to MFP SCSI disks. +This is an automated installation of MPX 3.4 to disk. Two 300MB disks, +mpx3xsba0.dsk and mpx3xsbb0.dsk are initialized and then the file +systems are created and loaded. The user sdt tape contains system +and user files that are loaded to multiple directories. The second +disk is initialized and formatted and only a system directory defined. +The install is exited and @@A is used to login into MPX. The username +SYSTEM is used to login into TSM without a password. + +#runscsi3x.ini - dsk/mpx3xsba0.dsk & dsk/mpx3csbb0.dsk; + - dsk/scsi35m1disk0 & dsk/scsi35m2disk0; +type "../sel32 rundscsi3x.ini" to run the installed MPX system. +The disk is booted up to the MPX message "Press Attention for TSM". +Use @@A to get login prompt. Login as SYSTEM. The WORK volume will +be mounted along with the SYSTEM volume and the system is ready for +use. MPX can be accessed from a second Linux screen by using the +command "telnet locallhost 4747". This will bring up the "Connected +to the SEL-32 simulator COMC device, line 0". Use ^G as the wakeup +character to get the "ENTER YOUR OWNERNAME:" login prompt. Any name +is valid, but SYSTEM should be used. At the "TSM>" prompt, type +"EXIT" to exit TSM. Use ^G to get the login prompt when the "RING +IN FOR SERVICE" message is displayed. + +-------------------- + +#user36esdtp2.ini - user36esdtp2.tap; +type "../sel32 user36esdtp2.ini" to install MPX 3.6 to HSDP disks. +This is an automated installation of MPX 3.6 to disk. A 300MB system +disk volume (user36p2udp0) and a 600MB work disk volume (user36s1udp1) +are initialized and then the file systems are created and loaded using +the volmgr. The user sdt tape contains system and user files that +are loaded to multiple directories. The second disk is initialized +and formatted and only a system directory defined. The disk is mounted +as the volume "work" as the 2nd disk drive. The installed MPX system +also has 2 scsi disks configured into the system. Two 700MB SCSI disks +are created, but they are not initialized and no directories are +created. The usage of these disks is left as an exercise for the user. +A third HSDP 600MD disk is also configured in MPX, but not used. The +user can provide other data volumes that can be mounted for use on the +system. The install is exited and @@A can be used to login into MPX. +The username SYSTEM is used to login into TSM without a password. Any +username is valid until an m.key file is created for valid user logins. + +#user36erunp2.ini - dsk/user36p2udp0 & dsk/user36s1udp1; +type "../sel32 user36erunp2.ini" to run the installed MPX 3.6 system. +The disk is booted up to the MPX message "Press Attention for TSM". +@@A is used to get the login prompt and the user is logged in as SYSTEM. +The WORK volume will be mounted along with the SYSTEM volume and the +system is ready for use at the TSM> prompt. The install tape also has +some of the NBC development system. A complete installation tape is +available at github.com/azbevier/nbc. + +-------------------- + +Other MPX versions support: + +I have recently received some old MPX 3.X save tapes. Using these +I have been able to hand build a MPX3.6 SDT tape that can be used +to install MPX3.6. Once installed, the system can be used to build +a new user SDT tape and install it elsewhere. Both based and non- +based O/S images can be created. More images for installation will +be made available in the future as I work my way through the save +tapes. I still do not have a master SDT tape for any of the MPX 1.X +or MPX 3.X systems. I have a 1600/6250 BPI tape drive that can read +9 track tapes and convert them to .tap files. If you have a master +SDT, I would be very thankfull. Please keep looking. + +James C. Bevier +02/27/2022 + diff --git a/SEL32/installs/loaddma1x67.ini b/SEL32/installs/loaddma1x67.ini new file mode 100644 index 00000000..a0d3e343 --- /dev/null +++ b/SEL32/installs/loaddma1x67.ini @@ -0,0 +1,142 @@ +;====================================================== +; SEL32 System Engineering Labs 32 bit computer +; MPX - Ver 1.5F hardware configuration +; CPU - 32/67 4M Sel32 Concept/32 +; IOP - 7e00 Model 8001 IOP Processor Controller +; DMA - 0800 2311/2314 Disk Processor II +; dma0 MH300 Model 8127 300MB MHD +; dma0 <-> dsk/mpx1xdma0.dsk +; LPR - 7ef8/7ef9 Model 924X High Speed Line Printer +; COM - 7ec0 8512 8-line async communications +; CON - 7efc/7efd Console Terminal +; RTC - 7f06 60 HZ Real-Time clock +; ITM - 7f04 38.4MS Interval timer +; MT - 1000 8051 Buffered Tape Processor +; mta0 <- tapes/mpx1xsdt.tap +; EC - 0e00 Model 8516 Ethernet (not supported) +;====================================================== +; Set hostname +set env HOST=sel32 +; Set local IP address +; set env IP=192.168.1.5 (N/U) +;====================================================== +; Set directory for disk images +mkdir dsk +mkdir tapes +; Set tape image filename +set env FILE=mpx1xsdt.tap +; Set github.com URL for downloading files +set env GITURL=https://github.com/AZBevier/SEL32-installs/raw/main/tapes/ +if exist "tapes/%FILE%" goto exists +if not exist "tapes/%FILE%" echof "file %FILE% missing, trying %FILE%.tgz" +cd tapes +if exist "%FILE%.tgz" goto nocurl +echof "fetching %FILE%.tgz file from github.com\n" +;curl -LJO https://raw.githubusercontent.com/AZBevier/sims/master/SEL32/tests/tapes/%FILE%.tgz +curl -LJO %GITURL%/%FILE%.tgz +if not exist "%FILE%.tgz" echof "FAILED - file %FILE% not available"; exit 1 +:nocurl +echof "untar %FILE%.tgz file\n" +tar -xzf %FILE%.tgz +cd .. +:exists +echof "file %FILE% present, doing install" +; +; Set debug output +; set debug -n sel.log +; +; CPU type and memory size +set CPU 32/67 4M +; +; RTC realtime clock at 7f06 +set RTC 60 +set RTC enable +; +; ITM interval timer at 7f04 +set ITM 3840 +; +; IOP at channel 7e00 +set iop enable +set iop0 dev=7e00 +; +; COM 8-Line at 7ec0 +set coml enable +set coml0 dev=7ec0 +set coml0 enable +set coml1 enable +set coml2 enable +set coml3 enable +set coml4 enable +set coml5 enable +set coml6 enable +set coml7 enable +; +; Enable telnet sessions on port 4747 +set comc enable +at comc 4747 +; +; LPR at 7ef8 +set lpr enable +; LPR output file +at lpr lprout +; +; EC ethernet at 0e00 +; Support not available yet +;set ec enable +;set ec mode=2 +;set ec0 dev=0e00 +;at ec tap:tap0 +; set mac address to cause ec to be attached +;set ec MAC=08:00:5D:01:01:20 +; +; CON Console at 7efc +; enable console +set con enable +set con0 dev=7efc +; +; MTA Buffered tape processor at 1000 +set mta enable +set mta0 dev=1000 +; Attach install sdt tape file +set mta0 locked +at mta0 tapes/mpx1xsdt.tap +; +; DMA disk processor II/UDP at 800 +set dma enable +set dma0 dev=800 +set dma0 type=MH300 +; Attach diskfile +at dma0 -i dsk/mpx1xdma0.dsk +; +; set console switches +deposit CSW 0 +; +; allow cpu idle +set cpu idle +; wait for expected output from simulator, then enter this text +expect "COLD OR WARM START (C OR W)?" send "C\r"; continue +; Set expect script for auto time entry on MPX at OPCOM prompt +expect "??" send " %DATE_MM%/%DATE_DD%/%DATE_YY%,%TIME_HH%:%TIME_MM%:%TIME_SS%\r@@A"; continue +;expect "" after=800000, delay=4000, send "@@A"; continue +expect "??" send "X\r"; continue +expect "TSM>" send "A4 SLO=UT\r"; continue +expect "TSM>" send "A3 IN=M91000,TAP\r"; continue +expect "TSM>" send "PAGE 0\r"; continue +expect "TSM>" send "FILEMGR\r"; continue +; restore all files +expect "FIL>" send "RESTORE\r"; continue +expect "FIL>" send "EXIT\r"; continue +; sysgen new mpx image +expect "TSM>" send "MAKSIMH1\r"; continue +; restart to new image +expect "TSM>" send "RESTART SEL32SY1,DEFAULT\r"; continue +expect " RESTART...DO YOU WANT TO REBOOT(Y/N)?" send "Y\r"; continue +expect "??" send " %DATE_MM%/%DATE_DD%/%DATE_YY%,%TIME_HH%:%TIME_MM%:%TIME_SS%\r@@A"; continue +expect "??" send "X\r"; continue +expect "TSM>" send "WHO\r"; continue +expect "TSM>" send "EXIT\r"; continue +expect "RING IN FOR SERVICE" send "\r^E" +; Boot from mag tape +bo mta0 +detach all +quit diff --git a/SEL32/installs/loaddma21b.ini b/SEL32/installs/loaddma21b.ini new file mode 100644 index 00000000..2decf0df --- /dev/null +++ b/SEL32/installs/loaddma21b.ini @@ -0,0 +1,239 @@ +;====================================================== +; SEL32 System Engineering Labs 32 bit computer +; UTX - Ver 2.1B hardware configuration +; CPU - V6 8M Sel32 Powernode +; MFP - 7e00 Model 8001 IOP Processor Controller +; DMA - 0800 2311/2314 Disk Processor II +; dma0 Model 8858 340MB +; dma0 <-> dsk/21bdisk0.dma +; dma1 Model 8858 340MB +; dma1 <-> dsk/21bdisk1.dma +; LPR - 7ef8/7ef9 Model 924X High Speed Line Printer +; COM - 7ee0 8512 8-line async communications +; CON - 7efc/7efd Console Terminal +; RTC - 7f06 50 HZ Real-Time clock +; ITM - 7f04 38.4MS Interval timer +; MT - 1000 8051 Buffered Tape Processor +; mta0 <- tapes/utx21b1.tap +; EC - 0e00 Model 8516 Ethernet +;====================================================== +; Set hostname +set env HOST=sel32 +; Set local IP address +set env IP=192.168.1.5 +;====================================================== +; Set directory for disk images +mkdir dsk +mkdir tapes +; Set tape image filename +set env FILE1=utx21b1.tap +; Set github.com URL for downloading files +set env GITURL=https://github.com/AZBevier/SEL32-installs/raw/main/tapes/ +if exist "tapes/%FILE1%" goto exists1 +if not exist "tapes/%FILE1%" echof "file %FILE1% missing, trying %FILE1%.tgz" +cd tapes +if exist "%FILE1%.tgz" goto nocurl1 +echof "fetching %FILE1%.tgz file from github.com\n" +curl -LJO %GITURL%/%FILE1%.tgz +if not exist "%FILE1%.tgz" echof "FAILED - file %FILE1% not available"; exit 1 +:nocurl1 +echof "untar %FILE1%.tgz file\n" +tar -xzf %FILE1%.tgz +cd .. +:exists1 +echof "file %FILE1% present, doing install" +; +; Set directory for disk images +mkdir dsk +mkdir tapes +; Set tape image filename +set env FILE2=utx21b2.tap +if exist "tapes/%FILE2%" goto exists2 +if not exist "tapes/%FILE2%" echof "file %FILE2% missing, trying %FILE2%.tgz" +cd tapes +if exist "%FILE2%.tgz" goto nocurl2 +echof "fetching %FILE2%.tgz file from github.com\n" +curl -LJO %GITURL%/%FILE2%.tgz +if not exist "%FILE2%.tgz" echof "FAILED - file %FILE2% not available"; exit 1 +:nocurl2 +echof "untar %FILE2%.tgz file\n" +tar -xzf %FILE2%.tgz +cd .. +:exists2 +echof "file %FILE2% present, doing install" +; +; Set directory for disk images +mkdir dsk +mkdir tapes +; Set tape image filename +set env FILE3=utx21b3.tap +if exist "tapes/%FILE3%" goto exists3 +if not exist "tapes/%FILE3%" echof "file %FILE% missing, trying %FILE%.tgz" +cd tapes +if exist "%FILE3%.tgz" goto nocurl3 +echof "fetching %FILE3%.tgz file from github.com\n" +curl -LJO %GITURL%/%FILE3%.tgz +if not exist "%FILE3%.tgz" echof "FAILED - file %FILE% not available"; exit 1 +:nocurl3 +echof "untar %FILE3%.tgz file\n" +tar -xzf %FILE3%.tgz +cd .. +:exists3 +echof "file %FILE3% present, doing install" +; +set RTC 50 +set CPU V6 8M +set RTC enable +set iop enable +set iop0 dev=7e00 +set dma enable +set dma0 dev=800 +set dma0 type=8858 +set dma1 type=8858 +set coml0 dev=7ee0 +set coml0 enable +set coml1 enable +set coml2 enable +set coml3 enable +set coml4 enable +set coml5 enable +set coml6 enable +set coml7 enable +set con enable +set con0 dev=7efc +set mta enable +set mta0 dev=1000 +set lpr enable +set comc enable +at comc 4747 +set ec enable +set ec mode=2 +set ec0 dev=0e00 +; set mac address to cause ec to be attached +set ec MAC=08:00:5D:01:01:20 +;at lpr lprout +;set cpu idle +; +deposit BOOTR[7] 42 +deposit BOOTR[6] 800 +set mta0 locked +at mta0 tapes/utx21b1.tap +at dma0 -i dsk/21bdisk0.dma +at dma1 -i dsk/21bdisk1.dma +at ec tap:tap0 +expect "#" continue +expect "#" send "prep /dev/rdk0a\r"; continue +expect "Ready to prep /dev/rdk0a" send "yes\r"; continue +expect "Which option ?" send "2\r"; continue +expect "Warning: some data may be lost on the pack, proceed ?" send "yes\r"; continue +expect "Rebuild UTX flaw map from diagnostic flaw map ?" send "yes\r"; continue +expect "Please select a drive type:" send "8\r"; continue +expect "#" continue +expect "#" continue +expect "How many spare tracks should be allocated " send "\r"; continue +expect "Do you really want to do this ?" send "yes\r"; continue +expect "Which option ?" send "3\r"; continue +expect "Which option ?" send "2\r"; continue +expect "(a) Starting Cylinder <0>?" send "0\r"; continue +expect "(a) Size In Blocks <25344> or Ending Cylinder ?" send "c65\r"; continue +expect "(a) Swap ?" send "\r"; continue +expect "(b) Starting Cylinder <66>?" send "66\r"; continue +expect "(b) Size In Blocks <20352> or Ending Cylinder ?" send "c118\r"; continue +expect "(b) Swap ?" send "\r"; continue +expect "(c) Starting Cylinder <0>?" send "\r"; continue +expect "(c) Size In Blocks <271488> or Ending Cylinder ?" send "\r"; continue +expect "(c) Swap ?" send "\r"; continue +expect "(d) Starting Cylinder <119>?" send "119\r"; continue +expect "(d) Size In Blocks <70272> or Ending Cylinder ?" send "c301\r"; continue +expect "(d) Swap ?" send "\r"; continue +expect "(e) Starting Cylinder <302>?" send "302\r"; continue +expect "(e) Size In Blocks <100224> or Ending Cylinder ?" send "C706\r"; continue +expect "(e) Swap ?" send "\r"; continue +expect "(f) Starting Cylinder <563>?" send "0\r"; continue +expect "(f) Size In Blocks <55296> or Ending Cylinder ?" send "0\r"; continue +expect "(f) Swap ?" send "\r"; continue +expect "(g) Starting Cylinder <0>?" send "\r"; continue +expect "(g) Size In Blocks <0> or Ending Cylinder ?" send "\r"; continue +expect "(g) Swap ?" send "\r"; continue +expect "(h) Starting Cylinder <0>?" send "\r"; continue +expect "(h) Size In Blocks <0> or Ending Cylinder ?" send "\r"; continue +expect "(h) Swap ?" send "\r"; continue +expect "Do you really want to do this ?" send "yes\r"; continue +expect "Which option ?" send "0\r"; continue +expect "Which option ?" send "0\r"; continue +expect "#" send "prep /dev/rdk1a\r"; continue +expect "Ready to prep /dev/rdk1a" send "yes\r"; continue +expect "Which option ?" send "2\r"; continue +expect "Warning: some data may be lost on the pack, proceed ?" send "yes\r"; continue +expect "Rebuild UTX flaw map from diagnostic flaw map ?" send "yes\r"; continue +expect "Please select a drive type:" send "8\r"; continue +expect "#" continue +expect "#" continue +expect "How many spare tracks should be allocated " send "\r"; continue +expect "Do you really want to do this ?" send "yes\r"; continue +expect "Which option ?" send "0\r"; continue +expect "#" send "newfs /dev/dk0a\r"; continue +expect "#" continue +expect "#" send "mount /dev/dk0a /mnt\r"; continue +expect "#" send "cd /mnt\r"; continue +expect "#" send "restore r\r"; continue +expect "#" send "cd /\r"; continue +expect "#" send "umount /dev/dk0a\r"; continue +expect "#" send "halt\r"; continue +bo mta0 +deposit BOOTR[7] 2 +at mta0 tapes/utx21b2.tap +; do not allow cpu to idle +; works around a reboot issue with UTX +; if allowed, installation will hang checking root file system +set cpu noidle +;at mta0 tapes/utx21b2-3.tap +expect "#" continue +expect "#" send "newfs /dev/dk0d\r"; continue +expect "#" continue +expect "#" send "newfs /dev/dk0e\r"; continue +expect "#" continue +expect "#" send "mount /dev/dk0d /usr.POWERNODE\r"; continue +expect "#" send "rm /restoresymtable\r"; continue +expect "#" send "cd /usr.POWERNODE\r"; continue +expect "#" send "restore rf /dev/rmt0\r"; continue +expect "then enter tape name (default: /dev/rmt0)" at mta0 tapes/utx21b3.tap; send "\r"; continue +expect "#" send "rm /usr.POWERNODE/restoresymtable\r"; continue +expect "#" send "cd /\r"; continue +expect "#" send "/ranlib.sh\r"; continue +expect "#" send 'echo "/dev/dk0e /usr.POWERNODE/src 4.3 rw,noquota 1 3" >>/etc/fstab\r'; continue +expect "echo" continue +;expect "#" send "mount /dev/dk0e /usr.POWERNODE/src\r"; at mta0 tapes/utx21b_src.tap; continue +;expect "#" send "cd /usr.POWERNODE/src\r"; continue +;expect "#" send "tar xf /dev/rmt0\r"; continue +expect "#" send "ed /etc/rc.boot\r"; continue +expect "1522" send "/preen/\r"; continue +expect "#" send "s/#/ /\r.+1\r"; continue +expect "#" continue +expect "echo" send "d\r"; 'expect "\r\n" send "/noname/\r"; continue' ; continue +expect "/bin/hostname noname" send "s/noname/%HOST%/\rw\rq\r"; continue +expect "#" send "ed /etc/hosts\r"; continue +expect "128" send "/sysname/\r"; continue +expect "sysname" continue +expect "sysname" send "d\r"; 'expect "\r\n" send "i\r%IP% %HOST%\r.\rw\rq\r"; continue'; continue +expect "#" send "ed /etc/rc.local\r" ; continue +expect "3849" send "/echo 'Setup/\r"; continue +expect "#" send "s/# //\r"; 'expect "\r\n" send "/en0 inet/\r"; continue' ; continue +expect "#" continue +expect "#" send "s/# //\r"; 'expect "\r\n" send "w\rq\r"; continue'; continue +expect "#" continue +expect "#" send "newfs /dev/dk1c\r"; continue +expect "#" continue +expect "#" send "mkdir /home\r"; continue +expect "#" send 'echo "/dev/dk1c /home 4.3 rw,noquota 2 1" >>/etc/fstab\r'; continue +expect "echo" continue +expect "#" send 'bin/echo "stty intr \003" >>/.profile\r'; continue +expect "#" send "mount /dev/dk1c /home\r"; continue +expect "#" send "df\r"; continue +;expect "#" send "reboot\r"; continue +expect "#" send "halt\r"; continue +expect "sim>" send "q\r"; continue +;Boot from disk dma0 +bo dma0 +;stop simh execution +quit diff --git a/SEL32/installs/loadscsi21b.ini b/SEL32/installs/loadscsi21b.ini new file mode 100644 index 00000000..4ea00ac4 --- /dev/null +++ b/SEL32/installs/loadscsi21b.ini @@ -0,0 +1,399 @@ +;====================================================== +; SEL32 System Engineering Labs 32 bit computer +; UTX - Ver 2.1B hardware configuration +; CPU - V6 8M Sel32 Powernode +; MFP - 7e00 Model 8002 Multi-Function Processor +; SBA - 7e00/7e08 MFP SCSI Disk controller +; sba0 Model 8821 300MB +; sba0 <-> dsk/scsidiska0 +; sba1 Model 8821 300MB +; sba1 <-> dsk/scsidiska1 +; LPR - 7ef8/7ef9 Model 924X High Speed Line Printer +; COM - 7ee0 8512 8-line async communications +; CON - 7efc/7efd Console Terminal +; RTC - 7f06 50 HZ Real-Time clock +; ITM - 7f04 38.4MS Interval timer +; MT - 1000 8051 Buffered Tape Processor +; mta0 <- tapes/utx21b1.tap +; EC - 0e00 Model 8516 Ethernet +;====================================================== +; Set hostname +set env HOST=sel32 +; Set local IP address +;set env IP=192.168.0.20 +set env IP=192.168.1.5 +;====================================================== +; Set debug output +;set debug -n sel.log +;set debug stderr +; +; Set directory for disk images +mkdir dsk +mkdir tapes +; Set tape image filename +set env FILE1=utx21b1.tap +if exist "tapes/%FILE1%" goto exists1 +; Set github.com URL for downloading files +set env GITURL=https://github.com/AZBevier/SEL32-installs/raw/main/tapes/ +if not exist "tapes/%FILE1%" echof "file %FILE1% missing, trying %FILE1%.tgz" +cd tapes +if exist "%FILE1%.tgz" goto nocurl1 +echof "fetching %FILE1%.tgz file from github.com\n" +curl -LJO %GITURL%/%FILE1%.tgz +if not exist "%FILE1%.tgz" echof "FAILED - file %FILE1% not available"; exit 1 +:nocurl1 +echof "untar %FILE1%.tgz file\n" +tar -xzf %FILE1%.tgz +cd .. +:exists1 +echof "file %FILE1% present, doing install" +; +; Set tape image filename +set env FILE2=utx21b2.tap +if exist "tapes/%FILE2%" goto exists2 +if not exist "tapes/%FILE2%" echof "file %FILE2% missing, trying %FILE2%.tgz" +cd tapes +if exist "%FILE2%.tgz" goto nocurl2 +echof "fetching %FILE2%.tgz file from github.com\n" +curl -LJO %GITURL%/%FILE2%.tgz +if not exist "%FILE2%.tgz" echof "FAILED - file %FILE2% not available"; exit 1 +:nocurl2 +echof "untar %FILE2%.tgz file\n" +tar -xzf %FILE2%.tgz +cd .. +:exists2 +echof "file %FILE2% present, doing install" +; +; Set tape image filename +set env FILE3=utx21b3.tap +if exist "tapes/%FILE3%" goto exists3 +if not exist "tapes/%FILE3%" echof "file %FILE% missing, trying %FILE%.tgz" +cd tapes +if exist "%FILE3%.tgz" goto nocurl3 +echof "fetching %FILE3%.tgz file from github.com\n" +curl -LJO %GITURL%/%FILE3%.tgz +if not exist "%FILE3%.tgz" echof "FAILED - file %FILE% not available"; exit 1 +:nocurl3 +echof "untar %FILE3%.tgz file\n" +tar -xzf %FILE3%.tgz +cd .. +:exists3 +echof "file %FILE3% present, doing install" +; +; CPU type and memory +;set CPU 32/97 4M +;set CPU V6 4M +set CPU V6 8M +;set CPU V9 4M +;set CPU V9 8M +; +; CPU debug options +;set cpu debug=cmd;exp;inst;detail;trap;xio;irq +; Set instruction trace history size +;set cpu history=10000 +; useful options +;set cpu debug=exp;irq;xio;trap;cmd +;;set cpu debug=cmd;exp;irq;trap;xio +;set cpu debug=cmd;irq;trap;exp +;;set cpu debug=irq;trap;exp;xio +;set cpu debug=irq;xio +; +; RTC realtime clock +set RTC enable +; address is 7f06 +set RTC 50 +;set RTC 60 +; RTC debug options +;set RTC debug=cmd +; +; ITM interval timer +; address is 7f04 +; set itm 3840 +;set ITM debug=cmd +; +; IOP at channel 7e00 +; useful options +;set iop debug=cmd;exp +;set iop debug=cmd +; make iop online +;set iop enable +; set iop channel address +;set iop0 dev=7e00 +; +; MFP at channel 7e00 +; useful options +;set mfp debug=cmd;exp +; make mfp online +set mfp enable +; set mfp channel address +set mfp0 dev=7e00 +;set mfp0 dev=7600 +; +; EC at channel 0e00 +; useful options +;set ec debug=cmd;exp +; set the mode +set EC mode=1 +; make ec online +set ec enable +; set ec channel address +set ec0 dev=0e00 +at ec tap:tap0 +; +; COM 8-Line +;set com debug=cmd; +;set coml0 enable +;set coml1 enable +;set coml2 enable +;set coml3 enable +;set coml4 enable +;set coml5 enable +;set coml6 enable +;set coml7 enable +; +; Enable telnet sessions on port 4747 +;set comc enable +;at comc 4747 +; +; LPR +;set lpr debug=cmd;detail +;set lpr enable +set lpr enable +; LPR output file +;at lpr lprout +at lpr lprout +; +; CON Console +; useful options +;set con debug=cmd;exp;detail +;set con debug=cmd;exp; +; enable console +set con enable +; set console address +; set con0 enable +set con0 dev=7efc +; set con1 enable +set con1 dev=7efd +; +; MTA Buffered tape processor +;set mta debug=cmd;exp;detail;data +; useful options +;set mta debug=cmd;detail;exp +; +; enable the MTA to change channel +set mta enable +; set mt channel +set mta0 dev=1000 +; attach in/out tape files +;at mta0 mpxsdt.tap +;at mta0 nbctape.tap +set mta0 locked +at mta0 tapes/utx21b1.tap +;at mta0 utx21a1.tap +;at mta1 temptape.tap +;at mta1 utx21a2.tap +;at mta2 output.tap +;at mta0 sim32sdt.tap +;at mta0 diag.tap +; +; DMA disk processor II/UDP +;set dma enable +; set disk chan to 0800 +;set dma0 dev=800 +; set disk type to MPX MH300 +;set dma0 type=MH300 +; set disk type to UTX 9346 +;set dma0 type=9346 +;set dma0 type=8155 +;set dma0 type=8887 +;set dma0 type=8148 +; Attach diskfile +;at dma0 utx0disk +;at dma0 utx1disk +;at dma0 sim32disk +;at dma0 diagdisk +; +;set dma debug=cmd;exp;detail;data +; useful options +;set dma debug=cmd;exp +;set dma debug=exp;cmd;detail +; +; SDA SCFI disk processor +;set sda debug=cmd;exp;data;detail +; Attach diskfiles +;at sda0 scsidisk0 +;at sda1 scsidisk1 +; +; SBA MFP SCSI bus 1 disk processor +set sba enable +; set disk chan to 7600 +;set sba0 dev=7600 +set sba0 dev=7e00 +;set sba1 dev=7e08 +; set disk type to MPX MH300 +set sba0 type=8821 +set sba1 type=8821 +;set sba0 type=SD300 +;set sba0 type=SD150 +;set sba0 type=8820 +;set sba debug=cmd;exp;data;detail +;set sba debug=cmd;exp;detail +; Attach diskfiles +at sba0 -i dsk/scsidiska0 +at sba1 -i dsk/scsidiska1 +; +; SBB MFP SCSI bus 2 disk processor +;;set sbb enable +; set disk chan to 7640 +;set sbb0 dev=7640 +;;set sbb0 dev=7e40 +; set disk type to MPX MH300 +;set sbb0 type=SD300 +;set sbb0 type=8821 +;;set sbb0 type=SD150 +;set sbb0 type=8820 +;set sbb debug=cmd;exp;data;detail +;;set sbb debug=cmd;exp;detail +; Attach diskfiles +;;at sbb0 scsidiskb0 +;at sbb1 scsi1disk +; +; DPA high speed disk processor +; enable the HSDP to change channel +;set dpa enable +; set channel addr +;set dpa0 dev=800 +; set disk type to UTX 8887 +;set dpa0 type=8887 +; Attach diskfiles +;at dpa0 utxdsk.dsk +;at dpa0 utx0hsdp +;at dpa1 utx1hsdp +; +;set dpa debug=cmd;detail;exp +; useful options +;set dpa debug=cmd;exp;detail +; +; set console switches +deposit CSW 0 +; +;UTX boot tape options +;set GPR 7 to 0x00 to boot in multi-user mode +;set GPR 7 to 0x01 to prompt for unix filename +;set GPR 7 to 0x02 to boot in single user mode +;set GPR 7 to 0x10 to disable swapping and paging +;set GPR 7 to 0x20 to boot from device specified in GPR6 +;set GPR 7 to 0x40 to allow progress messages on boot +;deposit BOOTR[7] 40 +;deposit BOOTR[7] 52 +deposit BOOTR[7] 42 +;deposit BOOTR[7] 2 +;deposit BOOTR[6] 800 +;deposit BOOTR[0] ffffffff +; +; Set register content at boot for SEL diagnostics +; uncomment next line to get diag loader prompt +;deposit bootr[0] ffffffff +;deposit bootr[1] 0 +;deposit bootr[2] 0 +; +; do not allow cpu to idle +; works around a reboot issue with UTX +; if allowed, installation will hang checking root file system +set cpu noidle +; +expect "#" continue +expect "#" send "prep /dev/rsd0a\r"; continue +expect "Ready to prep /dev/rsd0a" send "yes\r"; continue +expect "Which option ?" send "2\r"; continue +expect "Please select a drive type:" send "7\r"; continue +expect "#" continue +expect "#" continue +expect "#" continue +expect "Do you really want to do this ?" send "yes\r"; continue +expect "Which option ?" send "0\r"; continue +; +expect "#" continue +expect "#" continue +expect "#" send "prep /dev/rsd1a\r"; continue +expect "Which option ?" send "2\r"; continue +expect "Please select a drive type:" send "7\r"; continue +expect "#" continue +expect "#" continue +expect "#" continue +expect "Do you really want to do this ?" send "yes\r"; continue +expect "Ready to prep /dev/rsd1a" send "yes\r"; continue +expect "Which option ?" send "0\r"; continue +expect "#" continue +expect "#" continue +expect "#" send "newfs /dev/sd0a\r"; continue +expect "#" continue +expect "#" send "newfs /dev/sd0d\r"; continue +expect "#" continue +expect "#" send "newfs /dev/sd0e\r"; continue +expect "#" continue +expect "#" send "mount /dev/sd0a /mnt\r"; continue +expect "#" send "cd /mnt\r"; continue +expect "#" send "restore r\r"; continue +expect "#" send 'bin/echo "/dev/sd0a / 4.3 rw,noquota 1 1" >etc/fstab\r'; continue +expect "#" send 'bin/echo "#/dev/sd0b is the default swap partition" >>etc/fstab\r'; continue +expect "#" send 'bin/echo "/dev/sd0d /usr.POWERNODE 4.3 rw,noquota 1 2" >>etc/fstab\r'; continue +expect "#" send "cd /\r"; continue +expect "#" send "umount /dev/sd0a\r"; continue +expect "#" send "halt\r"; continue +;expect "#" send "/mnt/etc/reboot\r"; continue +; Boot from mag tape +bo mta0 +; Set expect script to load UTX 2.1B +deposit BOOTR[7] 2 +at mta0 tapes/utx21b2.tap +;set sba debug=cmd;exp;data;detail +; +expect "#" continue +expect "#" send "mount /dev/sd0d /usr.POWERNODE\r"; continue +expect "#" send "rm /restoresymtable\r"; continue +expect "#" send "cd /usr.POWERNODE\r"; continue +;expect "#" send "mkdir src\r"; continue +expect "#" send "restore rf /dev/rmt0\r"; continue +expect "then enter tape name (default: /dev/rmt0)" at mta0 tapes/utx21b3.tap; send "\r"; continue +expect "#" send "rm /usr.POWERNODE/restoresymtable\r"; continue +expect "#" send "cd /\r"; continue +expect "#" send "/ranlib.sh\r"; continue +;expect "#" send 'echo "/dev/sd0d /usr.POWERNODE 4.3 rw,noquota 1 2" >>/etc/fstab\r'; continue +expect "#" send 'echo "/dev/sd0e /usr.POWERNODE/src 4.3 rw,noquota 1 3" >>/etc/fstab\r'; continue +expect "echo" continue +;expect "#" send "mount /dev/sd0e /usr.POWERNODE/src\r"; at mta0 tapes/utx21b_src.tap; continue +;expect "#" send "cd /usr.POWERNODE/src\r"; continue +;expect "#" send "tar xf /dev/rmt0\r"; continue +expect "#" send "ed /etc/rc.boot\r"; continue +expect "1522" send "/preen/\r"; continue +expect "#" send "s/#/ /\r.+1\r"; continue +expect "#" continue +expect "echo" send "d\r"; 'expect "\r\n" send "/noname/\r"; continue' ; continue +expect "/bin/hostname noname" send "s/noname/%HOST%/\rw\rq\r"; continue +expect "#" send "ed /etc/hosts\r"; continue +expect "128" send "/sysname/\r"; continue +expect "sysname" continue +expect "sysname" send "d\r"; 'expect "\r\n" send "i\r%IP% %HOST%\r.\rw\rq\r"; continue'; continue +expect "#" send "ed /etc/rc.local\r" ; continue +expect "3849" send "/echo 'Setup/\r"; continue +expect "#" send "s/# //\r"; 'expect "\r\n" send "/en0 inet/\r"; continue' ; continue +expect "#" continue +expect "#" send "s/# //\r"; 'expect "\r\n" send "w\rq\r"; continue'; continue +expect "#" continue +expect "#" send "newfs /dev/sd1c\r"; continue +expect "#" continue +expect "#" send "mkdir /home\r"; continue +expect "#" send 'echo "/dev/sd1c /home 4.3 rw,noquota 2 1" >>/etc/fstab\r'; continue +expect "echo" continue +expect "#" send "mount /dev/sd1c /home\r"; continue +expect "#" send "df\r"; continue +expect "#" send 'bin/echo "stty intr \003" >>/.profile\r'; continue +expect "#" send 'bin/echo "Run runscsi21b.ini to run installed UTX 21b"\r'; continue +;expect "#" send "reboot\r"; continue +expect "#" send "halt\r"; continue +expect "sim>" send "q\r"; continue +;Boot from disk sba0 +bo sba0 +;stop simh execution +quit diff --git a/SEL32/installs/loadscsi3x.ini b/SEL32/installs/loadscsi3x.ini new file mode 100644 index 00000000..f6df2f55 --- /dev/null +++ b/SEL32/installs/loadscsi3x.ini @@ -0,0 +1,168 @@ +;====================================================== +; SEL32 System Engineering Labs 32 bit computer +; MPX - Ver 3.4 hardware configuration +; CPU - 32/67 8M Sel32 Concept/32 +; IOP - 7e00 Model 8001 IOP Processor Controller +; MFP - 7600 Model 8002 Multi Function Processor +; SBA - 7600 MFP SCSI Disk Controller +; sba0 SD300 Model 8828 300MB MHD +; sba0 <-> dsk/mpx3xsba0.dsk +; SBA - 7640 MFP SCSI Disk Controller +; sbb0 SD300 Model 8828 300MB MHD +; sbb0 <-> dsk/mpx3xsbb0.dsk +; LPR - 7ef8/7ef9 Model 924X High Speed Line Printer +; COM - 7ec0 8512 8-line async communications +; CON - 7efc/7efd Console Terminal +; RTC - 7f06 60 HZ Real-Time clock +; ITM - 7f04 38.4MS Interval timer +; MT - 1000 8051 Buffered Tape Processor +; mta0 <- tapes/mpxsdt69.tap +; EC - 0e00 Model 8516 Ethernet (not supported) +;====================================================== +; Set HOST simulator +set env HOST=sel32 +; Set local IP address +; set env IP=192.168.1.5 (N/U) +;====================================================== +; Set directory for disk images +mkdir dsk +mkdir tapes +; Set tape image filename +set env FILE=mpxsdt69.tap +; Set github.com URL for downloading files +set env GITURL=https://github.com/AZBevier/SEL32-installs/raw/main/tapes/ +if exist "tapes/%FILE%" goto exists +if not exist "tapes/%FILE%" echof "file %FILE% missing, trying %FILE%.tgz" +cd tapes +if exist "%FILE%.tgz" goto nocurl +echof "fetching %FILE%.tgz file from github.com\n" +curl -LJO %GITURL%/%FILE%.tgz +if not exist "%FILE%.tgz" echof "FAILED - file %FILE% not available"; exit 1 +:nocurl +echof "untar %FILE%.tgz file\n" +tar -xzf %FILE%.tgz +cd .. +:exists +echof "file %FILE% present, doing install" +; +; Set directory for disk +mkdir dsk +; Set debug output +; set debug -n sel.log +; +; CPU type and memory size +set CPU 32/67 8M +;set CPU 32/97 8M +; +; RTC realtime clock at 7f06 +set RTC 60 +set RTC enable +; +; ITM interval timer at 7f04 +set ITM 3840 +; +; IOP at channel 7e00 +set iop enable +set iop0 dev=7e00 +; +; MFP at channel 7600 +set mfp enable +set mfp0 dev=7600 +; +; COM 8-Line at 7ec0 +set coml enable +set coml0 dev=7ec0 +set coml0 enable +set coml1 enable +set coml2 enable +set coml3 enable +set coml4 enable +set coml5 enable +set coml6 enable +set coml7 enable +; +; Enable telnet sessions on port 4747 +set comc enable +at comc 4747 +; +; LPR0 at 7ef8 +set lpr enable +; LPR output file +;at lpr lprout +; +; EC ethernet at 0e00 +; Support not available yet +;set ec enable +;set ec mode=2 +;set ec0 dev=0e00 +;at ec tap:tap0 +; set mac address to cause ec to be attached +;set ec MAC=08:00:5D:01:01:20 +; +; CON Console at 7efc +set con enable +set con0 dev=7efc +; +; MTA Buffered tape processor at 1000 +set mta enable +set mta0 dev=1000 +; Attach install sdt tape file +set mta0 locked +at mta0 tapes/mpxsdt69.tap +; +; SBA MFP SCSI buss 1 disk processor at 7600 +set sba enable +; set disk chan to 7600 +set sba0 dev=7600 +set sba0 type=SD300 +; Attach diskfile +at sba0 -i dsk/mpx3xsba0.dsk +; +; SBB MFP SCSI buss 2 disk processor at 7640 +set sbb enable +set sbb0 dev=7640 +set sbb0 type=SD300 +; Attach diskfile +at sbb0 -i dsk/mpx3xsbb0.dsk +; +; set console switches +deposit CSW 0 +; +; allow cpu idle +set cpu idle +; +; wait for expected output from simulator, then enter this text +expect "ENTER" send " %DATE_MM%/%DATE_DD%/%DATE_YY%,%TIME_HH%:%TIME_MM%:%TIME_SS%\r"; continue +expect "FMT>" send "INITIALIZE DEVICE=DM7600 DEST=Y DISC=DM0300 CON=N\r"; continue +expect "FMT>" send "FORMAT DEVICE=DM7600 VOLUME=SYSTEM MAXRES=7000 CON=N\r"; continue +expect "ENTER SYSTEM VOLUME CHANNEL AND SUBADDRESS: " send "7600\r"; continue +expect "VOL>" send "CREATE D SYSTEM ENTRIES=2000\r"; continue +; restore system +expect "VOL>" send "RESTORE VOL=SYSTEM\r"; continue +; restore object, object_e, oldobj +expect "VOL>" send "RESTORE VOL=SYSTEM\r"; continue +; restore bin, doc, nblib, sample +expect "VOL>" send "RESTORE VOL=SYSTEM\r"; continue +expect "VOL>" send "REWIND\r"; continue +expect "VOL>" send "EXIT\r"; continue +; initialize 2nd disk +;expect "SYSTEM READY... PRESS ATTENTION FOR TSM..." send "@@A"; continue +expect "SYSTEM READY... PRESS ATTENTION FOR TSM..." send after=500000 delay=6000 "HIT @@A"; continue +;expect "" send after=1500000, delay=4000, " HIT @@A"; continue +expect "ENTER YOUR OWNERNAME:" send "SYSTEM\r"; continue +expect "TSM>" send "WHO\r"; continue +expect "TSM>" send "@SYSTEM(SYSTEM)J.VFMT\r"; continue +expect "FMT>" send "INITIALIZE DEV=DM7640 DES=Y DIS=DM0300 CON=N\r"; continue +expect "FMT>" send "FORMAT DEV=DM7640 VOL=WORK MAXRE=7000 CON=N\r"; continue +expect "TSM>" send "MOUNT WORK ON DM7640 OPTI=PUBLIC\r"; continue +expect "TSM>" send "VOLMGR\r"; continue +expect "VOL>" send "CREATE D @WORK^SYSTEM ENTRIES=1000\r"; continue +expect "VOL>" send "EXIT\r"; continue +expect "TSM>" send "!STAT VOL\r"; continue +;;expect "TSM>" send "WHO\r"; continue +expect "TSM>" send "EXIT\r"; continue +expect "RING IN FOR SERVICE" send "\r^E" +; Boot from mag tape +bo mta0 +det all +quit diff --git a/SEL32/installs/rundma1x67.ini b/SEL32/installs/rundma1x67.ini new file mode 100644 index 00000000..1a5cd49c --- /dev/null +++ b/SEL32/installs/rundma1x67.ini @@ -0,0 +1,215 @@ +;====================================================== +; SEL32 System Engineering Labs 32 bit computer +; MPX - Ver 1.5F hardware configuration +; CPU - 32/67 4M Sel32 Concept/32 +; IOP - 7e00 Model 8001 IOP Processor Controller +; DMA - 0800 2311/2314 Disk Processor II +; dma0 MH300 Model 8127 300MB MHD +; dma0 <-> dsk/mpx1xdma0.dsk +; LPR - 7ef8/7ef9 Model 924X High Speed Line Printer +; COM - 7ec0 8512 8-line async communications +; CON - 7efc/7efd Console Terminal +; RTC - 7f06 60 HZ Real-Time clock +; ITM - 7f04 38.4MS Interval timer +; MT - 1000 8051 Buffered Tape Processor +; mta0 <- tapes/mpx1xsdt.tap +; EC - 0e00 Model 8516 Ethernet (not supported) +;====================================================== +; Set hostname +set env HOST=sel32 +; Set local IP address +; set env IP=192.168.1.5 (N/U) +;====================================================== +; Set directory for disk images +mkdir dsk +; Set debug output +; set debug -n sel.log +; +; CPU type and memory size +set CPU 32/67 4M +; +; RTC realtime clock at 7f06 +set RTC 60 +set RTC enable +; +; ITM interval timer at 7f04 +set ITM 3840 +; +; IOP at channel 7e00 +set iop enable +set iop0 dev=7e00 +; +; COM 8-Line at 7ec0 +set coml enable +set coml0 dev=7ec0 +; Set debug output +;set debug -n sel.log +; +; CPU type and memory size +;set CPU 32/27 4M +;set CPU 32/87 4M +set CPU 32/67 4M +;set CPU 32/97 4M +; +; CPU debug options +;set cpu debug=cmd;exp;inst;detail;trap;xio;irq +; Set instruction trace history size +;set cpu history=10000 +; +; RTC realtime clock +; RTC debug options +;set RTC debug=cmd +; device address is 7f06 +set RTC enable +;set RTC 50 +set RTC 60 +; +; ITM interval timer +; ITM debug options +;set ITM debug=cmd +; device address is 7f04 +set ITM 3840 +; +; IOP at channel 7e00 +; IOP debug options +;set iop debug=cmd;exp +set iop enable +; set iop channel address +set iop0 dev=7e00 +; +; MFP at channel 7e00 +; Only supported on MPX-3X and UTX +; MFP debug options +;set mfp debug=cmd;exp +; enable MFP to change address +;set mfp enable +; set mfp channel address +;set mfp0 dev=7e00 +;set mfp0 dev=7600 +; +; COM 8-Line +; COM debug options +;set coml debug=cmd;exp;xio +; enable COML to change address +set coml enable +; MPX 3X wants 7ea0 +; set coml0 dev=7ea0 +; MPX 1X wants 7ec0 +set coml0 dev=7ec0 +set coml0 enable +set coml1 enable +set coml2 enable +set coml3 enable +set coml4 enable +set coml5 enable +set coml6 enable +set coml7 enable +; +; COMC 8-Line Mux +; COMC debug options +;set comc debug=cmd +; COMC has no address +set comc enable +; Enable telnet sessions on port 4747 +at comc 4747 +; +; LPR at 7ef8 +; LPR debug options +;set lpr debug=cmd;detail +; enable LPR to change address +set lpr enable +; attach LPR output file +at lpr lprout +; +; EC ethernet 0e00 +; Support not available yet +; EC debug options +;set ec debug=cmd;exp;xio;detail;irq +;set ec enable +;set ec0 dev=0e00 +;set ec mode=2 +;at ec tap:tap0 +; set mac address to cause ec to be attached +;set ec MAC=08:00:5D:01:01:20 +; +; CON Console +; CON debug options +;set con debug=cmd;exp;detail +; enable console +set con enable +; set console address +set con0 dev=7efc +; +; MTA Buffered tape processor +; MTA debug options +;set mta debug=cmd;exp;detail;data +; enable MTA to change channel +set mta enable +; set mta channel address +set mta0 dev=1000 +; Attach in/out tape files +;at mta0 output.tap +;set mta0 locked +; +; DMA disk processor II/UDP +; DMA debug options +;set dma debug=cmd;exp;detail;data +; enable DMA to change channel +set dma enable +; set channel address +set dma0 dev=800 +; set disk type to MPX MH300 (8887) +; must match sysgen assignment +set dma0 type=MH300 +; Attach diskfile +at dma0 dsk/mpx1xdma0.dsk +; +; SDA SCFI disk processor +; Only supported on MPX-1X +; SDA debug options +;set sda debug=cmd;exp;data;detail +; enable SCFI to change channel +;set sda enable +; set channel address +;set sda0 dev=800 +; set disk type to MPX SG120 +; must match sysgen assignment +;set sda0 type=SG120 +; Attach diskfiles +;at sda0 dsk/mpx1xsda0.dsk +; +; DPA high speed disk processor +; DPA debug options +;set dpa debug=cmd;exp;data;detail +; enable DPA to change channel +;set dpa enable +; set channel address +;set dpa0 dev=800 +; set disk type to MPX MH300 (8887) +; must match sysgen assignment +;set dpa0 type=MH300 +; Attach diskfiles +;at dpa0 dsk/mpx1xdpa0.dsk +; +; set console switches +deposit CSW 0 +; +;SEL32 boot register options +;deposit BOOTR[7] 0 +;deposit BOOTR[6] 800 +;; +; allow cpu idle +set cpu idle +; Set expect script for auto time entry on MPX at OPCOM prompt +;expect haltafter=20000 +; wait for expected output from simulator, then enter this text +expect " DO YOU WISH TO USE *SEL32SY1* DEFAULT IMAGE(Y/N)?" send "Y\r"; continue +expect "??" send " %DATE_MM%/%DATE_DD%/%DATE_YY%,%TIME_HH%:%TIME_MM%:%TIME_SS%\r@@A"; GO +;expect "" send "@@A"; continue +expect "" send after=400000, delay=6000, " @@A" +expect "??" send "X\r"; continue +expect "TSM>" send "WHO\r"; continue +;expect "TSM>" send "EXIT\r"; continue +; +; Boot from disk +bo dma0 diff --git a/SEL32/installs/rundma21b.ini b/SEL32/installs/rundma21b.ini new file mode 100644 index 00000000..2454145e --- /dev/null +++ b/SEL32/installs/rundma21b.ini @@ -0,0 +1,70 @@ +;====================================================== +; SEL32 System Engineering Labs 32 bit computer +; UTX - Ver 2.1B hardware configuration +; CPU - V6 8M Sel32 Powernode +; MFP - 7e00 Model 8001 IOP Processor Controller +; DMA - 0800 2311/2314 Disk Processor II +; dma0 Model 8858 340MB +; dma0 <-> dsk/21bdisk0.dma +; dma1 Model 8858 340MB +; dma1 <-> dsk/21bdisk1.dma +; LPR - 7ef8/7ef9 Model 924X High Speed Line Printer +; COM - 7ee0 8512 8-line async communications +; CON - 7efc/7efd Console Terminal +; RTC - 7f06 50 HZ Real-Time clock +; ITM - 7f04 38.4MS Interval timer +; MT - 1000 8051 Buffered Tape Processor +; mta0 <-> not assigned +; EC - 0e00 Model 8516 Ethernet +;====================================================== +; Set hostname +set env HOST=sel32 +; Set local IP address +set env IP=192.168.1.5 +;====================================================== +;set debug -n sel.log +mkdir dsk +set RTC 50 +set CPU V6 8M +set RTC enable +set iop enable +set iop0 dev=7e00 +set dma enable +set dma0 dev=800 +set dma0 type=8858 +set dma1 type=8858 +set coml0 dev=7ee0 +set coml0 enable +set coml1 enable +set coml2 enable +set coml3 enable +set coml4 enable +set coml5 enable +set coml6 enable +set coml7 enable +set con enable +set con0 dev=7efc +set mta enable +set mta0 dev=1000 +set lpr enable +set comc enable +at comc 4747 +set ec enable +set ec mode=1 +set ec0 dev=0e00 +; set mac address to cause ec to be attached +set ec MAC=08:00:5D:01:01:20 +;set ec debug=cmd;exp;xio;detail;irq +;at lpr lprout +set cpu idle +; +;deposit BOOTR[7] 42 +deposit BOOTR[7] 0 +deposit BOOTR[6] 800 +;at mta0 tapes/temp.tap +;at mta0 tapes/src.tar.tap +;at mta0 tapes/y2k21ab.tar.tap +at dma0 dsk/21bdisk0.dma +at dma1 dsk/21bdisk1.dma +at ec tap:tap0 +bo dma0 diff --git a/SEL32/installs/runscsi21b.ini b/SEL32/installs/runscsi21b.ini new file mode 100644 index 00000000..9cf048e9 --- /dev/null +++ b/SEL32/installs/runscsi21b.ini @@ -0,0 +1,248 @@ +;====================================================== +; SEL32 System Engineering Labs 32 bit computer +; UTX - Ver 2.1B hardware configuration +; CPU - V6 8M Sel32 Powernode +; MFP - 7e00 Model 8002 Multi-Function Processor +; SBA - 7e00/7e08 MFP SCSI Disk controller +; sba0 Model 8821 300MB +; sba0 <-> dsk/scsidiska0 +; sba1 Model 8821 300MB +; sba1 <-> dsk/scsidiska1 +; LPR - 7ef8/7ef9 Model 924X High Speed Line Printer +; COM - 7ee0 8512 8-line async communications +; CON - 7efc/7efd Console Terminal +; RTC - 7f06 50 HZ Real-Time clock +; ITM - 7f04 38.4MS Interval timer +; MT - 1000 8051 Buffered Tape Processor +; mta0 <- tapes/utx21b1.tap +; EC - 0e00 Model 8516 Ethernet +;====================================================== +; Set hostname +set env HOST=sel32 +; Set local IP address +set env IP=192.168.1.5 +;====================================================== +; Set debug output +;set debug -n sel.log +;set debug stderr +; +;mkdir dsk +; CPU type and memory +;set CPU 32/97 4M +;set CPU V6 4M +set CPU V6 8M +;set CPU V9 4M +;set CPU V9 8M +; +; CPU debug options +;set cpu debug=cmd;exp;inst;detail;trap;xio;irq +; Set instruction trace history size +;set cpu history=10000 +; useful options +;set cpu debug=exp;irq;xio;trap;cmd +;;set cpu debug=cmd;exp;irq;trap;xio +;set cpu debug=cmd;irq;trap;exp +;;set cpu debug=irq;trap;exp;xio +;;set cpu debug=irq;xio +; +; RTC realtime clock +set RTC enable +; address is 7f06 +;set RTC 50 +set RTC 60 +; RTC debug options +;set RTC debug=cmd +; +; ITM interval timer +; address is 7f04 +; set itm 3840 +;set ITM debug=cmd +; +; IOP at channel 7e00 +; useful options +;set iop debug=cmd;exp +;set iop debug=cmd +; make iop online +;set iop enable +; set iop channel address +;set iop0 dev=7e00 +; +; MFP at channel 7e00 +; useful options +;set mfp debug=cmd;exp +; make mfp online +set mfp enable +; set mfp channel address +set mfp0 dev=7e00 +;set mfp0 dev=7600 +; +; EC at channel 0e00 +; useful options +;set ec debug=cmd;exp +; set the mode +set EC mode=1 +; make ec online +set ec enable +; set ec channel address +set ec0 dev=0e00 +at ec tap:tap0 +; +; COM 8-Line +;set com debug=cmd; +;set coml0 enable +;set coml1 enable +;set coml2 enable +;set coml3 enable +;set coml4 enable +;set coml5 enable +;set coml6 enable +;set coml7 enable +; +; Enable telnet sessions on port 4747 +;set comc enable +;at comc 4747 +; +; LPR +;set lpr debug=cmd;detail +;set lpr enable +set lpr enable +; LPR output file +;at lpr lprout +;at lpr lprout +; +; CON Console +; useful options +;set con debug=cmd;exp;detail +;set con debug=cmd;exp; +; enable console +set con enable +; set console address +; set con0 enable +set con0 dev=7efc +; set con1 enable +set con1 dev=7efd +; +; MTA Buffered tape processor +;set mta debug=cmd;exp;detail;data +; useful options +;set mta debug=cmd;detail;exp +; +; enable the MTA to change channel +set mta enable +; set mt channel +set mta0 dev=1000 +; attach in/out tape files +;at mta0 mpxsdt.tap +;at mta0 nbctape.tap +;at mta0 tapes/utx21b1.tap +;at mta0 utx21a1.tap +;at mta1 temptape.tap +;at mta1 utx21a2.tap +;at mta2 output.tap +;at mta0 sim32sdt.tap +;at mta0 diag.tap +; +; DMA disk processor II/UDP +;set dma enable +; set disk chan to 0800 +;set dma0 dev=800 +; set disk type to MPX MH300 +;set dma0 type=MH300 +; set disk type to UTX 9346 +;set dma0 type=9346 +;set dma0 type=8155 +;set dma0 type=8887 +;set dma0 type=8148 +; Attach diskfile +;at dma0 utx0disk +;at dma0 utx1disk +;at dma0 sim32disk +;at dma0 diagdisk +; +;set dma debug=cmd;exp;detail;data +; useful options +;set dma debug=cmd;exp +;set dma debug=exp;cmd;detail +; +; SDA SCFI disk processor +;set sda debug=cmd;exp;data;detail +; Attach diskfiles +;at sda0 scsidisk0 +;at sda1 scsidisk1 +; +; SBA MFP SCSI bus 1 disk processor +set sba enable +; set disk chan to 7600 +;set sba0 dev=7600 +set sba0 dev=7e00 +;set sba1 dev=7e08 +; set disk type to UTX MH300 +set sba0 type=8821 +set sba1 type=8821 +;set sba0 type=8820 +;set sba debug=cmd;exp;data;detail +;set sba debug=cmd;exp;detail +; Attach diskfiles +at sba0 dsk/scsidiska0 +at sba1 dsk/scsidiska1 +; +; SBB MFP SCSI bus 2 disk processor +;;set sbb enable +; set disk chan to 7640 +;set sbb0 dev=7640 +;;set sbb0 dev=7e40 +; set disk type to MPX MH300 +;set sbb0 type=SD300 +;set sbb0 type=8821 +;;set sbb0 type=SD150 +;set sbb0 type=8820 +;set sbb debug=cmd;exp;data;detail +;;set sbb debug=cmd;exp;detail +; Attach diskfiles +;;at sbb0 scsidiskb0 +;at sbb1 scsi1disk +; +; DPA high speed disk processor +; enable the HSDP to change channel +;set dpa enable +; set channel addr +;set dpa0 dev=800 +; set disk type to UTX 8887 +;set dpa0 type=8887 +; Attach diskfiles +;at dpa0 utxdsk.dsk +;at dpa0 utx0hsdp +;at dpa1 utx1hsdp +; +;set dpa debug=cmd;detail;exp +; useful options +;set dpa debug=cmd;exp;detail +; +; set console switches +deposit CSW 0 +; +;UTX boot tape options +;set GPR 7 to 0x00 to boot in multi-user mode +;set GPR 7 to 0x01 to prompt for unix filename +;set GPR 7 to 0x02 to boot in single user mode +;set GPR 7 to 0x10 to disable swapping and paging +;set GPR 7 to 0x20 to boot from device specified in GPR6 +;set GPR 7 to 0x40 to allow progress messages on boot +;deposit BOOTR[7] 40 +;deposit BOOTR[7] 52 +deposit BOOTR[7] 40 +;deposit BOOTR[7] 2 +;deposit BOOTR[6] 800 +;deposit BOOTR[0] ffffffff +; +; Set register content at boot for SEL diagnostics +; uncomment next line to get diag loader prompt +;deposit bootr[0] ffffffff +;deposit bootr[1] 0 +;deposit bootr[2] 0 +; +; allow cpu idle +set cpu idle +; +;Boot from disk +bo sba0 diff --git a/SEL32/installs/runscsi3x.ini b/SEL32/installs/runscsi3x.ini new file mode 100644 index 00000000..466af081 --- /dev/null +++ b/SEL32/installs/runscsi3x.ini @@ -0,0 +1,219 @@ +;====================================================== +; SEL32 System Engineering Labs 32 bit computer +; MPX - Ver 3.4 hardware configuration +; CPU - 32/67 8M Sel32 Concept/32 +; IOP - 7e00 Model 8001 IOP Processor Controller +; MFP - 7600 Model 8002 Multi Function Processor +; SBA - 7600 MFP SCSI Disk Controller +; sba0 SD300 Model 8828 300MB MHD +; sba0 <-> dsk/mpx3xsba0.dsk +; SBA - 7640 MFP SCSI Disk Controller +; sbb0 SD300 Model 8828 300MB MHD +; sbb0 <-> dsk/mpx3xsbb0.dsk +; LPR - 7ef8/7ef9 Model 924X High Speed Line Printer +; COM - 7ec0 8512 8-line async communications +; CON - 7efc/7efd Console Terminal +; RTC - 7f06 60 HZ Real-Time clock +; ITM - 7f04 38.4MS Interval timer +; MT - 1000 8051 Buffered Tape Processor +; mta0 <- tapes/mpxsdt69.tap +; EC - 0e00 Model 8516 Ethernet (not supported) +;====================================================== +; Set HOST simulator +set env HOST=sel32 +; Set local IP address +; set env IP=192.168.1.5 (N/U) +;====================================================== +; Set debug output +;set debug -n sel.log +; +; CPU type and memory size +set CPU 32/67 8M +;set CPU 32/97 8M +; +; CPU debug options +;set cpu debug=cmd;exp;inst;detail;trap;xio;irq +; Set instruction trace history size +;set cpu history=10000 +; +; RTC realtime clock +; RTC debug options +;set RTC debug=cmd +; device address is 7f06 +set RTC enable +;set RTC 50 +set RTC 60 +; +; ITM interval timer +; ITM debug options +;set ITM debug=cmd +; device address is 7f04 +set ITM 3840 +; +; IOP at channel 7e00 +; IOP debug options +;set iop debug=cmd;exp +set iop enable +; set iop channel address +set iop0 dev=7e00 +; +; MFP at channel 7600 +; Only supported on MPX-3X and UTX +; MFP debug options +;set mfp debug=cmd;exp +; enable MFP to change address +set mfp enable +; set mfp channel address +;set mfp0 dev=7e00 +set mfp0 dev=7600 +; +; COM 8-Line +; COM debug options +;set coml debug=cmd;exp;xio +; enable COML to change address +set coml enable +; MPX 3X wants 7ec0 +set coml0 dev=7ec0 +set coml0 enable +set coml1 enable +set coml2 enable +set coml3 enable +set coml4 enable +set coml5 enable +set coml6 enable +set coml7 enable +; +; COMC 8-Line Mux +; COMC debug options +;set comc debug=cmd +; COMC has no address +set comc enable +; Enable telnet sessions on port 4747 +at comc 4747 +; +; LPR at 7ef8 +; LPR debug options +;set lpr debug=cmd;detail +; enable LPR to change address +set lpr enable +; attach LPR output file +at lpr lprout +; +; EC ethernet 0e00 +; Support not available yet +; EC debug options +;set ec debug=cmd;exp;xio;detail;irq +;set ec enable +;set ec0 dev=0e00 +;set ec mode=2 +;at ec tap:tap0 +; set mac address to cause ec to be attached +;set ec MAC=08:00:5D:01:01:20 +; +; CON Console +; CON debug options +;set con debug=cmd;exp;detail +; enable console +set con enable +; set console address +set con0 dev=7efc +; +; MTA Buffered tape processor +; MTA debug options +;set mta debug=cmd;exp;detail;data +; enable MTA to change channel +set mta enable +; set mta channel address +set mta0 dev=1000 +; Attach in/out tape files +;at mta0 output.tap +;set mta0 locked +; +; DMA disk processor II/UDP +; DMA debug options +;set dma debug=cmd;exp;detail;data +; enable DMA to change channel +; set dma enable +; set channel address +; set dma0 dev=800 +; set disk type to MPX MH300 (8887) +; must match sysgen assignment +; set dma0 type=MH300 +; Attach diskfile +; at dma0 dsk/mpx3xdma0.dsk +; +; SDA SCFI disk processor +; Only supported on MPX-1X +; SDA debug options +;set sda debug=cmd;exp;data;detail +; enable SCFI to change channel +;set sda enable +; set channel address +;set sda0 dev=800 +; set disk type to MPX SG120 +; must match sysgen assignment +;set sda0 type=SG120 +; Attach diskfiles +;at sda0 dsk/mpx3xsda0.dsk +; +; DPA high speed disk processor +; DPA debug options +;set dpa debug=cmd;exp;data;detail +; enable DPA to change channel +;set dpa enable +; set channel address +;set dpa0 dev=800 +; set disk type to MPX MH300 (8887) +; must match sysgen assignment +;set dpa0 type=MH300 +; Attach diskfiles +;at dpa0 dsk/mpx3xdpa0.dsk +; +; SBA MFP SCSI buss 1 disk processor +; SBA debug options +;set sba debug=cmd;exp;data;detail +; enable SBA to change channel +set sba enable +; set channel address +set sba0 dev=7600 +; set disk type to MPX SD300 (8828) +set sba0 type=SD300 +; Attach diskfiles +at sba0 dsk/mpx3xsba0.dsk +; +; SBB SCSI buss 2 disk processor +; SBB debug options +;set sbb debug=cmd;exp;data;detail +; enable SBB to change channel +set sbb enable +; set channel address +set sbb0 dev=7640 +; set disk type to MPX SD300 (8828) +set sbb0 type=SD300 +; Attach diskfiles +at sbb0 dsk/mpx3xsbb0.dsk +; +; set console switches +deposit CSW 0 +; +;SEL32 boot register options +;deposit BOOTR[7] 0 +;deposit BOOTR[6] 800 +;; +; allow cpu idle +set cpu idle +; Set expect script for auto time entry on MPX at OPCOM prompt +;expect haltafter=20000 +; wait for expected output from simulator, then enter this text +expect "??" send " %DATE_MM%/%DATE_DD%/%DATE_YY%,%TIME_HH%:%TIME_MM%:%TIME_SS%\r"; GO +;expect "??" send "X\r"; continue +;expect "SYSTEM READY... PRESS ATTENTION FOR TSM..." send "@@A"; continue +expect "SYSTEM READY... PRESS ATTENTION FOR TSM..." send after=500000 delay=4000 "@@A"; continue +expect "ENTER YOUR OWNERNAME:" send "SYSTEM\r"; continue +expect "TSM>" send "MOUNT WORK ON DM7640 OPTI=PUBLIC\r"; continue +expect "TSM>" send "!STAT VOL\r"; continue +expect "TSM>" send "WHO\r"; continue +;expect "TSM>" send "EXIT\r"; continue +; +; Boot from disk +bo sba0 diff --git a/SEL32/installs/sel32load1x.ini b/SEL32/installs/sel32load1x.ini new file mode 100644 index 00000000..7037d86b --- /dev/null +++ b/SEL32/installs/sel32load1x.ini @@ -0,0 +1,245 @@ +;====================================================== +; SEL32 System Engineering Labs 32 bit computer +; MPX - Ver 1.5F hardware configuration +; CPU - 32/27 2M Sel32 Concept/32 +; IOP - 7e00 Model 8001 IOP Processor Controller +; DMA - 0800 2311/2314 Disk Processor II +; dma0 MH300 Model 8127 300MB MHD +; dma0 <-> dsk/sel32disk +; LPR - 7ef8/7ef9 Model 924X High Speed Line Printer +; COM - 7ec0 8512 8-line async communications +; CON - 7efc/7efd Console Terminal +; RTC - 7f06 60 HZ Real-Time clock +; ITM - 7f04 38.4MS Interval timer +; MT - 1000 8051 Buffered Tape Processor +; mta0 <- tapes/mpx1xsdt.tap +; EC - 0e00 Model 8516 Ethernet (not supported) +;====================================================== +; Set hostname +set env HOST=sel32 +; Set local IP address +; set env IP=192.168.1.5 (N/U) +;====================================================== +; Set directory for disk images +mkdir dsk +mkdir tapes +; Set tape image filename +set env FILE=mpx1xsdt.tap +; Set github.com URL for downloading files +set env GITURL=https://github.com/AZBevier/SEL32-installs/raw/main/tapes/ +if exist "tapes/%FILE%" goto exists +if not exist "tapes/%FILE%" echof "file %FILE% missing, trying %FILE%.tgz" +cd tapes +if exist "%FILE%.tgz" goto nocurl +echof "fetching %FILE%.tgz file from github.com\n" +curl -LJO %GITURL%/%FILE%.tgz +if not exist "%FILE%.tgz" echof "FAILED - file %FILE% not available"; exit 1 +:nocurl +echof "untar %FILE%.tgz file\n" +tar -xzf %FILE%.tgz +cd .. +:exists +echof "file %FILE% present, doing install" +; +; Set debug output +;set debug -n sel.log +;set debug stderr +; +; CPU type and memory +; Bad on UTX +set CPU 32/27 2M +;set CPU 32/27 4M +;set CPU 32/87 4M +;set CPU 32/67 4M +;End of Bad +;set CPU 32/97 4M +;set CPU V6 4M +;set CPU V6 8M +;set CPU V9 4M +;set CPU V9 8M +; +; CPU debug options +;set cpu debug=cmd;exp;inst;detail;trap;xio;irq +; Set instruction trace history size +;set cpu history=10000 +; useful options +;set cpu debug=exp;xio;trap +;set cpu debug=cmd;exp;irq;trap;xio; +;set cpu debug=cmd;irq;trap;exp +;set cpu debug=irq;trap;exp;xio +;set cpu debug=irq;xio;exp;inst;cmd;trap +;set cpu debug=irq;xio;exp;cmd;trap +; +; RTC realtime clock +; address is 7f06 +;set RTC 50 +set RTC 60 +set RTC enable +; RTC debug options +;set RTC debug=cmd +; +; ITM interval timer +; address is 7f04 +; set ITM 3840 +;set ITM debug=cmd +; +; IOP at channel 7e00 +set iop enable +; set iop channel +set iop0 dev=7e00 +;set iop debug=cmd;exp +;set iop debug=cmd +; +; COM 8-Line +set coml enable +set coml0 dev=7ec0 +;set coml debug=cmd; +set coml0 enable +set coml1 enable +set coml2 enable +set coml3 enable +set coml4 enable +set coml5 enable +set coml6 enable +set coml7 enable +; +; Enable telnet sessions on port 4747 +set comc enable +;set comc debug=cmd; +at comc 4747 +; +; LPR +; LPR0 is at 7EF8 +; LPR1 is at 7EF9 +; useful options +;set lpr debug=cmd;detail +set lpr enable +; LPR output file +;at lpr lprout +; +;; CON Console +;set con debug=cmd;exp;detail +; useful options +; enable console +;;set con debug=cmd;exp +set con enable +; set console address +; set con0 enable +set con0 dev=7efc +; set con1 enable +set con1 dev=7efd +;set con debug=cmd;exp +; +; MTA Buffered tape processor +;set mta debug=cmd;exp;detail;data +; useful options +;;set mta debug=cmd;detail;exp +;set mta debug=cmd;exp +; +; enable MTA to change channel +set mta enable +; set mta channel +set mta0 dev=1000 +; +; Attach in/out tape files +set mta0 locked +at mta0 tapes/mpx1xsdt.tap +;at mta0 nbctape.tap +;at mta0 usertape.tap +;at mta0 mpxsdt6r.tap +;at mta0 tapes/sel32sdt.tap +;at mta0 diag.tap +;at mta1 temptape.tap +;at mta2 output.tap +; +; DMA disk processor II/UDP +; enable DMA to change channel +set dma enable +; set disk chan to 0800 +set dma0 dev=800 +; set disk type to MPX MH300 +set dma0 type=MH300 +; set disk type to MPX MH080 +;set dma0 type=MH080 +; set disk type to UTX 9346 +;set dma0 type=9346 +;set dma0 type=8155 +;set dma0 type=8887 +;set dma0 type=8148 +; +; Attach diskfile +at dma0 -i dsk/sel32disk +; +; Debug options +;set dma debug=cmd;exp;detail;data +; useful options +;set dma debug=cmd;exp +;set dma debug=exp +; +; SDA SCFI disk processor +;set sda debug=cmd;exp;data;detail +; Attach diskfiles +;at sda0 diskfile4 +;at sda1 diskfile5 +; +; DPA high speed disk processor +; enable HSDP to change channel +;set dpa enable +; set channel addr +;set dpa0 dev=800 +; set disk type to UTX 8887 +;set dpa0 type=8887 +; +; Attach diskfiles +;at dpa0 utxdsk.dsk +;at dpa0 utx0hsdp +;at dpa1 utx1hsdp +; +;set dpa debug=cmd;detail;exp +; useful options +;set dpa debug=cmd;exp +; +; set console switches +deposit CSW 0 +; +;UTX boot tape options +;set GPR 7 to 0x00 to boot in multi-user mode +;set GPR 7 to 0x01 to prompt for unix filename +;set GPR 7 to 0x02 to boot in single user mode +;set GPR 7 to 0x10 to disable swapping and paging +;set GPR 7 to 0x20 to boot from device specified in GPR6 +;set GPR 7 to 0x40 to allow progress messages on boot +;deposit BOOTR[7] 40 +;deposit BOOTR[7] 52 +;deposit BOOTR[7] 42 +;deposit BOOTR[7] 2 +;deposit BOOTR[6] 800 +;deposit BOOTR[0] ffffffff +; +; Set register content at boot for SEL diagnostics +; uncomment next line to get diag loader prompt +;deposit bootr[0] ffffffff +;deposit bootr[1] 0 +;deposit bootr[2] 0 +; +; allow cpu idle +set cpu idle +; wait for expected output from simulator, then enter this text +expect "COLD OR WARM START (C OR W)?" send "C\r"; continue +; Set expect script for auto time entry on MPX at OPCOM prompt +;expect "??" send " %DATE_MM%/%DATE_DD%/%DATE_YY%,%TIME_HH%:%TIME_MM%:%TIME_SS%\r@@A"; continue +expect "??" send " %DATE_MM%/%DATE_DD%/%DATE_YY%,%TIME_HH%:%TIME_MM%:%TIME_SS%\r@@Ax\r"; GO +expect "TSM>" send "A4 SLO=UT\r"; continue +expect "TSM>" send "A3 IN=M91000,TAP\r"; continue +expect "TSM>" send "PAGE 0\r"; continue +expect "TSM>" send "FILEMGR\r"; continue +; restore all files +expect "FIL>" send "RESTORE\r"; continue +expect "FIL>" send "EXIT\r"; continue +expect "TSM>" send "WHO\r"; continue +expect "TSM>" send "EXIT\r"; continue +expect "RING IN FOR SERVICE" send "\r^E" +; Boot from mag tape +bo mta0 +detach all +quit diff --git a/SEL32/installs/sel32run1x.ini b/SEL32/installs/sel32run1x.ini new file mode 100644 index 00000000..e5cc0632 --- /dev/null +++ b/SEL32/installs/sel32run1x.ini @@ -0,0 +1,210 @@ +;====================================================== +; SEL32 System Engineering Labs 32 bit computer +; MPX - Ver 1.5F hardware configuration +; CPU - 32/27 2M Sel32 Concept/32 +; IOP - 7e00 Model 8001 IOP Processor Controller +; DMA - 0800 2311/2314 Disk Processor II +; dma0 MH300 Model 8127 300MB MHD +; dma0 <-> dsk/sel32disk +; LPR - 7ef8/7ef9 Model 924X High Speed Line Printer +; COM - 7ec0 8512 8-line async communications +; CON - 7efc/7efd Console Terminal +; RTC - 7f06 60 HZ Real-Time clock +; ITM - 7f04 38.4MS Interval timer +; MT - 1000 8051 Buffered Tape Processor +; mta0 <-> (not assigned) +; EC - 0e00 Model 8516 Ethernet (not supported) +;====================================================== +; Set hostname +set env HOST=sel32 +; Set local IP address +; set env IP=192.168.1.5 (N/U) +;====================================================== +; Set debug output +;set debug -n sel.log +;set debug stderr +; +; CPU type and memory +; Bad on UTX +set CPU 32/27 2M +;set CPU 32/27 4M +;set CPU 32/87 4M +;set CPU 32/67 4M +;End of Bad +;set CPU 32/97 4M +;set CPU V6 4M +;set CPU V6 8M +;set CPU V9 4M +;set CPU V9 8M +; +; CPU debug options +;set cpu debug=cmd;exp;inst;detail;trap;xio;irq +; Set instruction trace history size +;set cpu history=10000 +; useful options +;set cpu debug=exp;xio;trap +;set cpu debug=cmd;exp;irq;trap;xio; +;set cpu debug=cmd;irq;trap;exp +;set cpu debug=irq;trap;exp;xio +;set cpu debug=irq;xio;exp;inst;cmd;trap +;set cpu debug=irq;xio;exp;cmd;trap +; +; RTC realtime clock +; address is 7f06 +;set RTC 50 +set RTC 60 +set RTC enable +; RTC debug options +;set RTC debug=cmd +; +; ITM interval timer +; address is 7f04 +set ITM 3840 +;set ITM debug=cmd +; +; IOP at channel 7e00 +set iop enable +; set iop channel +set iop0 dev=7e00 +;set iop debug=cmd;exp +;set iop debug=cmd +; +; COM 8-Line +set coml enable +set coml0 dev=7ec0 +;set coml debug=cmd; +set coml0 enable +set coml1 enable +set coml2 enable +set coml3 enable +set coml4 enable +set coml5 enable +set coml6 enable +set coml7 enable +; +; Enable telnet sessions on port 4747 +set comc enable +;set comc debug=cmd; +at comc 4747 +; +; LPR +;set lpr debug=cmd;detail +set lpr enable +; LPR output file +;at lpr lprout +; +;; CON Console +;set con debug=cmd;exp;detail +; useful options +; enable console +;;set con debug=cmd;exp +set con enable +; set console address +; set con0 enable +set con0 dev=7efc +;set con debug=cmd;exp +; +; MTA Buffered tape processor +;set mta debug=cmd;exp;detail;data +; useful options +;;set mta debug=cmd;detail;exp +;set mta debug=cmd;exp +; +; enable MTA to change channel +set mta enable +; set mta channel +set mta0 dev=1000 +; +; Attach in/out tape files +;at mta0 tapes/mpx1xsdt.tap +;at mta0 nbctape.tap +;set mta0 locked +;at mta0 usertape.tap +;at mta0 mpxsave6.tap +;at mta0 tapes/sel32sdt.tap +;at mta0 diag.tap +;at mta1 temptape.tap +;at mta2 output.tap +; +; DMA disk processor II/UDP +; enable DMA to change channel +set dma enable +; set disk chan to 0800 +set dma0 dev=800 +; set disk type to MPX MH300 +set dma0 type=MH300 +; set disk type to MPX MH080 +;set dma0 type=MH080 +; set disk type to UTX 9346 +;set dma0 type=9346 +;set dma0 type=8155 +;set dma0 type=8887 +;set dma0 type=8148 +; +; Attach diskfile +;at dma0 diskfile6r +at dma0 dsk/sel32disk +;at dma0 diagdisk +; +; Debug options +;set dma debug=cmd;exp;detail;data +; useful options +;set dma debug=cmd;exp +;set dma debug=exp +; +; SDA SCFI disk processor +;set sda debug=cmd;exp;data;detail +; Attach diskfiles +;at sda0 diskfile4 +;at sda1 diskfile5 +; +; DPA high speed disk processor +; enable HSDP to change channel +;set dpa enable +; set channel addr +;set dpa0 dev=800 +; set disk type to UTX 8887 +;set dpa0 type=8887 +; +; Attach diskfiles +;at dpa0 utxdsk.dsk +;at dpa0 utx0hsdp +;at dpa1 utx1hsdp +; +;set dpa debug=cmd;detail;exp +; useful options +;set dpa debug=cmd;exp +; +; set console switches +deposit CSW 0 +; +;UTX boot tape options +;set GPR 7 to 0x00 to boot in multi-user mode +;set GPR 7 to 0x01 to prompt for unix filename +;set GPR 7 to 0x02 to boot in single user mode +;set GPR 7 to 0x10 to disable swapping and paging +;set GPR 7 to 0x20 to boot from device specified in GPR6 +;set GPR 7 to 0x40 to allow progress messages on boot +;deposit BOOTR[7] 40 +;deposit BOOTR[7] 52 +;deposit BOOTR[7] 42 +;deposit BOOTR[7] 2 +;deposit BOOTR[6] 800 +;deposit BOOTR[0] ffffffff +; +; Set register content at boot for SEL diagnostics +; uncomment next line to get diag loader prompt +;deposit bootr[0] ffffffff +;deposit bootr[1] 0 +;deposit bootr[2] 0 +; +; allow cpu idle +set cpu idle +; Set expect script for auto time entry on MPX at OPCOM prompt +;expect haltafter=20000 +; wait for expected output from simulator, then enter this text +expect "??" send " %DATE_MM%/%DATE_DD%/%DATE_YY%,%TIME_HH%:%TIME_MM%:%TIME_SS%\r@@Ax\r"; GO +expect "TSM>" send "WHO\r"; continue +; Boot from disk +;bo dpa0 +bo dma0 diff --git a/SEL32/installs/user36erunp2.ini b/SEL32/installs/user36erunp2.ini new file mode 100644 index 00000000..02e3d491 --- /dev/null +++ b/SEL32/installs/user36erunp2.ini @@ -0,0 +1,327 @@ +;====================================================== +; SEL32 System Engineering Labs 32 bit computer +; MPX - Ver 3.6 extended hardware configuration +; CPU - 32/67 16M Sel32 Concept/32 +; IOP - 7e00 Model 8001 IOP Processor Controller +; MFP - 7600 Model 8002 Multi Function Processor +; SBA - 7e00/7e08 MFP SCSI Disk controller +; sba0 SD700 Model 8833 700MB +; sba0 <-> dsk/scsi35m1disk0 +; sba1 SD700 Model 8833 700MB +; sba1 <-> dsk/scsi35m2disk0 +; DMA - 0800 DMA HSDP Disk Controller +; dma0 MD300 Model 8127 300MB MHD +; dma0 <-> dsk/user36p2udp0 +; - 0802 DMA HSDP Disk Controller +; dma1 MH600 Model 8155 600MB MHD +; dma1 <-> dsk/user36s1udp1 +; - 0804 DMA HSDP Disk Controller +; dma1 MH600 Model 8155 600MB MHD +; dma1 <-> dsk/user36s1udp2 +; LPR - 7ef8/7ef9 Model 924X High Speed Line Printer +; COM - 7ec0 8512 8-line async communications +; CON - 7efc/7efd Console Terminal +; RTC - 7f06 60 HZ Real-Time clock +; ITM - 7f04 38.4MS Interval timer +; MT - 1000 8051 Buffered Tape Processor +; mta0 <- tapes/nbctape3x.tap +; EC - 0e00 Model 8516 Ethernet (not supported) +;====================================================== +; Set HOST simulator +set env HOST=sel32 +; Set local IP address +; set env IP=192.168.1.5 (N/U) +;====================================================== +; Set debug output +;set debug -n sel.log +;set debug stderr +; +; CPU type and memory +; Bad on UTX +;set CPU 32/27 2M +;set CPU 32/27 4M +;set CPU 32/87 4M +;set CPU 32/87 8M +;End of Bad +;set CPU 32/67 4M +;set CPU 32/67 8M +;set CPU 32/67 12M +set CPU 32/67 16M +;set CPU 32/97 4M +;set CPU 32/97 8M +;set CPU V6 4M +;set CPU V6 8M +;set CPU V9 4M +;set CPU V9 8M +; +; CPU debug options +;set cpu debug=cmd;exp;inst;detail;trap;xio;irq +; Set instruction trace history size +;set cpu history=10000 +; useful options +;set cpu debug=exp +;set cpu debug=cmd;exp;irq;trap;xio;inst +;;;;set cpu debug=cmd;exp;irq;trap;xio +;set cpu debug=cmd;irq;trap;exp +;set cpu debug=cmd;exp;xio +;;;set cpu debug=cmd;irq;trap;exp;xio +; +; RTC realtime clock +; address is 7f06 +;set RTC 50 +set RTC 60 +; enable RTC +set RTC enable +; RTC debug options +;set RTC debug=cmd +; +; ITM interval timer +; address is 7f04 + set ITM 3840 +;set ITM debug=cmd +; +; IOP at channel 7e00 +; useful options +;;;;set iop debug=cmd;exp +;;set iop debug=cmd +; make iop online +set iop enable +; set iop channel address +set iop0 dev=7e00 +; +; MFP at channel 7e00 +; useful options +;;;;set mfp debug=cmd;exp +; make mfp online +set mfp enable +; set mfp channel address +;set mfp0 dev=7e00 +set mfp0 dev=7600 +; +; COM 8-Line on 7ec0 +; useful options +;;set com debug=cmd; +set coml enable +set coml0 dev=7ec0 +set coml0 enable +set coml1 enable +set coml2 enable +set coml3 enable +set coml4 enable +set coml5 enable +set coml6 enable +set coml7 enable +; +; Enable telnet sessions on port 4747 +set comc enable +at comc 4747 +; +; LPR +; LPR0 is at 7EF8 +; LPR1 is at 7EF9 +; useful options +;;set lpr debug=cmd;detail +;set lpr debug=cmd +set lpr enable +; LPR output file +at lpr lprout +; +; CON Console +; useful options +;set con debug=cmd;exp;detail +; enable console +set con enable +; set console address +set con0 dev=7efc +;;;;set con debug=cmd;exp +; +; MTA Buffered tape processor +;set mta debug=cmd;exp;xio;data +; useful options +;set mta debug=cmd;exp;xio;data +;;set mta debug=cmd;exp;xio +; +; enable MTA to change channel +set mta enable +; set mt channel +set mta0 dev=1000 +; +; Attach in/out tape files +;at mta0 user36sdts2.tap +;at mta0 user36sdts1.tap +set mta0 locked +at mta0 tapes/nbctape3x.tap +;at mta0 test.tap +;at mta0 user36sdt.tap +;at mta1 output.tap +;at mta0 iscsys_dsk.tap +; +; EC at channel 0d00 +; useful options +;set ec debug=cmd;exp +; set mode +;;set mode=1 +; make ec online +;;set ec enable +; set ec channel address +;;set ec0 dev=0d00 +; set mac address to cause ec to be attached +;set ec MAC=00:00:02:00:00:00 +;;at ec tap:tap0 +; +; DMA disk processor II/UDP +; enable DMA to change channel +set dma enable +; set disk chan to 0800 +set dma0 dev=800 +; set disk type to MPX MH300 8127 +set dma0 type=MH300 +; set disk type to MPX MH600 8155 +;set dma0 type=MH600 +; set disk chan to 0802 +set dma1 dev=802 +; set disk type to MPX MH300 8127 +;set dma1 type=MH300 +; set disk type to MPX MH600 8155 +set dma1 type=MH600 +; set disk chan to 0804 +set dma2 dev=804 +; set disk type to MPX MH300 8127 +;set dma2 type=MH300 +; set disk type to MPX MH600 8155 +set dma2 type=MH600 +; +; Attach diskfile +;at dma0 -i user36s1udp0 +at dma0 dsk/user36p2udp0 +;at dma1 -i user36s1udp1 +at dma1 dsk/user36s1udp1 +;at dma2 -i user36s1udp2 +;at dma2 dsk/user36s1udp2 +;;at dma0 mpx0disk +;;at dma1 mpx1disk +; +;set dma debug=cmd;exp;detail;data +; useful options +;;set dma debug=cmd;exp +;set dma debug=exp;cmd;detail +; +; SDA SCFI disk processor +;set sda debug=cmd;exp;data;detail +;set sda debug=cmd;exp +; Attach diskfiles +;at sda0 diskfile4 +;at sda1 diskfile5 +; +; SBA MFP SCSI buss 1 disk processor +set sba enable +; set disk chan to 7600 +set sba0 dev=7600 +; set disk type to MPX MH300 +;set sba0 type=SD300 +;set sba0 type=SD150 +;;set sba0 type=SD700 +set sba0 type=SD700 +set sba1 type=SD700 +;;;;set sba debug=cmd;exp;data;detail +;;;;set sba debug=cmd;exp;xio;exp;data +; Attach diskfiles +;;at sba0 -i scsi36m1disk0 +at sba0 dsk/scsi35m1disk0 +;at sba1 scsi1disk +at sba1 dsk/scsi35m2disk0 +; +; SBB SCSI buss 2 disk processor +;set sbb enable +; set disk chan to 7640 +;set sbb0 dev=7640 +; set disk type to MPX MH300 +;;;;set sbb0 type=SD150 +;set sbb0 type=SD150 +;set sbb0 type=SD700 +;set sbb0 type=SD300 +;set sbb debug=cmd;exp;data;xio;detail +;set sbb debug=cmd;exp +; Attach diskfiles +;at sbb0 scsi35m2disk0 +;at sbb1 scsi1disk +; +; DPA high speed disk processor +; enable HSDP to change channel +;set dpa enable +; set channel addr +;set dpa0 dev=800 +; set disk type to UTX 8887 +;set dpa0 type=8887 +; +; Attach diskfiles +;at dpa0 mpx0hsdp +;at dpa1 mpx1hsdp +; +; useful options +;set dpa debug=cmd;exp +; +; set console switches +deposit CSW 0 +; +; Set register content at boot for SEL diagnostics +; uncomment next line to get diag loader prompt +;deposit bootr[0] ffffffff +;deposit bootr[1] 0 +;deposit bootr[2] 0 +; +; set Boot reg 0 1 to boot MSTRALL from master SDT +deposit bootr[0] 1 +; set Boot reg 0 2 to boot MSTREXT from master SDT +;deposit bootr[0] 2 +; +; allow cpu idle +set cpu idle +; Set expect script for auto time entry on MPX at OPCOM prompt +;expect haltafter=20000 +; +; wait for expected output from simulator, then enter this text +;expect " >>" send "BR 31D10\r"; continue +;expect " >>" send "BR 2b4q\r"; continue +;expect " >>" send "BR 430q\r"; continue +expect " >>" send "TE\r"; continue +;expect "ENTER SYSTEM DEVICE CHANNEL AND SUBADDRESS: " send "7600\r"; continue +;expect "ENTER SYSTEM DEVICE CHANNEL AND SUBADDRESS: " send "0800\r"; continue +;expect "PLEASE ENTER THE 4 CHAR. DEVICE....." send "0800\r"; continue +;expect "PLEASE ENTER THE 4 CHAR. DEVICE....." send "7600\r"; continue +;expect "ENTER DISC CONTROLLER TYPE:" send "X\r"; continue +;expect " (OR IF SYSTEM VOLUME):" send "\r"; continue +;expect "ENTER DISC CONTROLLER TYPE:" send "M\r"; continue +expect "ENTER DATE" send " %DATE_MM%/%DATE_DD%/%DATE_YY%,%TIME_HH%:%TIME_MM%:%TIME_SS%\r"; GO +expect "ENTER SWAP DEVICE CHANNEL AND SUBADDRESS" send "0800\r"; continue +expect "TASK J.INIT , 04000002 REPLY R, H, A, OR DEVICE:" send "R\r"; continue +expect "TASK J.INIT , 04000002 REPLY R, H, A, OR DEVICE:" send "R\r"; continue +expect "TASK J.INIT , 04000002 REPLY R, H, A, OR DEVICE:" send "R\r"; continue +expect "TASK J.INIT , 04000002 REPLY R, H, A, OR DEVICE:" send "R\r"; continue +;expect "MOUNT DISK VOLUME ISCSYS ON DRIVE DM0802 ---" send "R\r"; continue +;expect "MOUNT DISK VOLUME NBCSRC ON DRIVE DM0804 ---" send "R\r"; continue +;expect "MOUNT DISK VOLUME ISCSRC ON DRIVE DM7600 ---" send "R\r"; continue +;expect "MOUNT DISK VOLUME IRELAND ON DRIVE DM7608 ---" send "R\r"; continue +; +;expect "ENTER SYSTEM VOLUME CHANNEL AND SUBADDRESS: " send "7600\r"; continue +;expect "ENTER SYSTEM VOLUME CHANNEL AND SUBADDRESS: " send "0800\r"; continue +;expect "SYSTEM READY... PRESS ATTENTION FOR TSM..." +expect "SYSTEM READY... PRESS ATTENTION FOR TSM..." send "@@A"; continue +expect "ENTER YOUR OWNERNAME:" send "SYSTEM\r"; continue +expect "TSM>" send "WHO\r"; continue +expect "TSM>" send "SYSINFO\r"; continue +expect "TSM>" send "MOUNT WORK ON DM0802 OPTI=PUBLIC\r"; continue +;expect "MOUNT DISK VOLUME WORK ON DRIVE DM0802 ---" send "R\r"; continue +expect "TASK J.TSM" send "R\r"; continue +expect "TSM>" send "!STAT VOL\r"; continue +;;;expect "TSM>" send "EXIT\r"; continue +#expect "TSM>" send "J.VFMT\r"; continue +#expect "FMT>" send "INITIALIZE DEV=DM0804 DIS=MH0600 DES=Y CON=N\r"; continue +#expect "FMT>" send "FORMAT DEV=DM0804 VOL=NBCSRC MAXRE=9000 CON=N\r"; continue +#expect "TSM>" send "VOLMGR\r"; continue +#expect "VOL>" send "CREATE D @NBCSRC^SYSTEM ENTRIES=1000\r"; continue +#expect "VOL>" send "EXIT\r"; continue +; +; Boot from disk +bo dma0 +;bo sba0 diff --git a/SEL32/installs/user36esdtp2.ini b/SEL32/installs/user36esdtp2.ini new file mode 100644 index 00000000..e02125aa --- /dev/null +++ b/SEL32/installs/user36esdtp2.ini @@ -0,0 +1,364 @@ +;====================================================== +; SEL32 System Engineering Labs 32 bit computer +; MPX - Ver 3.6 extended hardware configuration +; CPU - 32/67 16M Sel32 Concept/32 +; IOP - 7e00 Model 8001 IOP Processor Controller +; MFP - 7600 Model 8002 Multi Function Processor +; SBA - 7e00/7e08 MFP SCSI Disk controller +; sba0 SD700 Model 8833 700MB +; sba0 <-> dsk/scsi35m1disk0 +; sba1 SD700 Model 8833 700MB +; sba1 <-> dsk/scsi35m2disk0 +; DMA - 0800 DMA HSDP Disk Controller +; dma0 MD300 Model 8127 300MB MHD +; dma0 <-> dsk/user36p2udp0 +; - 0802 DMA HSDP Disk Controller +; dma1 MH600 Model 8155 600MB MHD +; dma1 <-> dsk/user36s1udp1 +; - 0804 DMA HSDP Disk Controller +; dma2 MH600 Model 8155 600MB MHD (N/U) +; dma2 <-> dsk/user36s1udp2 (N/U) +; LPR - 7ef8/7ef9 Model 924X High Speed Line Printer +; COM - 7ec0 8512 8-line async communications +; CON - 7efc/7efd Console Terminal +; RTC - 7f06 60 HZ Real-Time clock +; ITM - 7f04 38.4MS Interval timer +; MT - 1000 8051 Buffered Tape Processor +; mta0 <- tapes/user36esdtp2.tap +; EC - 0e00 Model 8516 Ethernet (not supported) +;====================================================== +; Set HOST simulator +set env HOST=sel32 +; Set local IP address +; set env IP=192.168.1.5 (N/U) +;====================================================== +; Set directory for disk images +mkdir dsk +mkdir tapes +; Set tape image filename +set env FILE=user36esdtp2.tap +; Set github.com URL for downloading files +set env GITURL=https://github.com/AZBevier/SEL32-installs/raw/main/tapes/ +if exist "tapes/%FILE%" goto exists +if not exist "tapes/%FILE%" echof "file %FILE% missing, trying %FILE%.tgz" +cd tapes +if exist "%FILE%.tgz" goto nocurl +echof "fetching %FILE%.tgz file from github.com\n" +curl -LJO %GITURL%/%FILE%.tgz +if not exist "%FILE%.tgz" echof "FAILED - file %FILE% not available"; exit 1 +:nocurl +echof "untar %FILE%.tgz file\n" +tar -xzf %FILE%.tgz +cd .. +:exists +echof "file %FILE% present, doing install" +; +; Set NBC tape image filename +set env FILE2=nbctape3x.tap +if exist "tapes/%FILE2%" goto exists2 +if not exist "tapes/%FILE2%" echof "file %FILE2% missing, trying %FILE2%.tgz" +cd tapes +if exist "%FILE2%.tgz" goto nocurl2 +echof "fetching %FILE2%.tgz file from github.com\n" +curl -LJO %GITURL%/%FILE2%.tgz +if not exist "%FILE2%.tgz" echof "FAILED - file %FILE2% not available"; exit 1 +:nocurl2 +echof "untar %FILE2%.tgz file\n" +tar -xzf %FILE2%.tgz +cd .. +:exists2 +echof "file %FILE2% present, doing install" +; +; Set debug output +;set debug -n sel.log +;set debug stderr +; +; CPU type and memory +;set CPU 32/27 2M +;set CPU 32/27 4M +;set CPU 32/87 4M +;set CPU 32/87 8M +;set CPU 32/67 4M +set CPU 32/67 8M +;set CPU 32/67 16M +;set CPU 32/97 4M +;set CPU 32/97 8M +; +; CPU debug options +;set cpu debug=cmd;exp;inst;detail;trap;xio;irq +; Set instruction trace history size +;set cpu history=10000 +; useful options +;set cpu debug=exp +;set cpu debug=cmd;exp;irq;trap;xio;inst +;;;;set cpu debug=cmd;exp;irq;trap;xio +;set cpu debug=cmd;irq;trap;exp +;set cpu debug=cmd;exp;xio +;set cpu debug=irq;trap;exp;xio +; +; RTC realtime clock +; address is 7f06 +;set RTC 50 +set RTC 60 +; enable RTC +set RTC enable +; RTC debug options +;set RTC debug=cmd +; +; ITM interval timer +; address is 7f04 + set ITM 3840 +;set ITM debug=cmd +; +; IOP at channel 7e00 +; useful options +;;;;set iop debug=cmd;exp +;;set iop debug=cmd +; make iop online +set iop enable +; set iop channel address +set iop0 dev=7e00 +; +; MFP at channel 7e00 +; useful options +;;;;set mfp debug=cmd;exp +; make mfp online +set mfp enable +; set mfp channel address +;set mfp0 dev=7e00 +set mfp0 dev=7600 +; +; COM 8-Line on 7ec0 +; useful options +;;set com debug=cmd; +set coml enable +set coml0 dev=7ec0 +set coml0 enable +set coml1 enable +set coml2 enable +set coml3 enable +set coml4 enable +set coml5 enable +set coml6 enable +set coml7 enable +; +; Enable telnet sessions on port 4747 +;set comc enable +;at comc 4747 +; +; LPR +; LPR0 is at 7EF8 +; LPR1 is at 7EF9 +; useful options +;set lpr debug=cmd;detail +;;;;set lpr debug=cmd +set lpr enable +; LPR output file +;at lpr lprout +; +; CON Console +; useful options +;set con debug=cmd;exp;detail +; enable console +set con enable +; set console address +set con0 dev=7efc +;;;;set con debug=cmd;exp +; +; MTA Buffered tape processor +;set mta debug=cmd;exp;xio;data +; useful options +;set mta debug=cmd;exp;xio;data +;set mta debug=cmd;exp;xio +; +; enable MTA to change channel +set mta enable +; set mt channel +set mta0 dev=1000 +; +; Attach in/out tape files +set mta0 locked +at mta0 tapes/user36esdtp2.tap +;at mta0 user36sdts1.tap +;at mta1 temptape.tap +;at mta2 output.tap +; +; DMA disk processor II/UDP +; enable DMA to change channel +set dma enable +; set disk chan to 0800 +set dma0 dev=800 +; set disk type to MPX MH300 8127 +set dma0 type=MH300 +; set disk type to MPX MH600 8155 +;set dma0 type=MH600 +; set disk chan to 0802 +set dma1 dev=802 +; set disk type to MPX MH300 8127 +;set dma1 type=MH300 +; set disk type to MPX MH600 8155 +set dma1 type=MH600 +; +; Attach diskfile +;at dma0 udp35disk0 +at dma0 -i dsk/user36p2udp0 +;at dma1 udp35disk1 +; use s1 disk 2 +at dma1 -i dsk/user36s1udp1 +;;at dma0 mpx0disk +;;at dma1 mpx1disk +;at dma2 -i dsk/user36s1udp2 +; +;set dma debug=cmd;exp;detail;data +; useful options +;;set dma debug=cmd;exp +;set dma debug=exp;cmd;detail +; +; SDA SCFI disk processor +;set sda debug=cmd;exp;data;detail +;set sda debug=cmd;exp +; Attach diskfiles +;at sda0 diskfile4 +;at sda1 diskfile5 +; +; SBA MFP SCSI buss 1 disk processor +set sba enable +; set disk chan to 7600 +set sba0 dev=7600 +; set disk type to MPX MH300 +;set sba0 type=SD300 +;set sba0 type=SD150 +set sba0 type=SD700 +set sba1 type=SD700 +;;;;set sba debug=cmd;exp;data;detail +;;;;set sba debug=cmd;exp;xio;exp;data +; Attach diskfiles +;at sba0 scsia0disk +at sba0 -i dsk/scsi35m1disk0 +;at sba1 scsi1disk +at sba1 -i dsk/scsi35m2disk0 +; +; SBB SCSI buss 2 disk processor +;set sbb enable +; set disk chan to 7640 +;set sbb0 dev=7640 +; set disk type to MPX MH300 +;;;;set sbb0 type=SD150 +;set sbb0 type=SD150 +;set sbb0 type=SD700 +;set sbb0 type=SD300 +;set sbb debug=cmd;exp;data;xio;detail +;set sbb debug=cmd;exp +; Attach diskfiles +;at sbb0 -i scsi35m2disk0 +;at sbb1 scsi1disk +; +; DPA high speed disk processor +; enable HSDP to change channel +;set dpa enable +; set channel addr +;set dpa0 dev=800 +; set disk type to UTX 8887 +;set dpa0 type=8887 +; +; Attach diskfiles +;at dpa0 utxdsk.dsk +;at dpa0 utx0hsdp +;at dpa1 utx1hsdp +; +; useful options +;set dpa debug=cmd;exp +; +; set console switches +deposit CSW 0 +; +;UTX boot tape options +;set GPR 7 to 0x00 to boot in multi-user mode +;set GPR 7 to 0x01 to prompt for unix filename +;set GPR 7 to 0x02 to boot in single user mode +;set GPR 7 to 0x10 to disable swapping and paging +;set GPR 7 to 0x20 to boot from device specified in GPR6 +;set GPR 7 to 0x40 to allow progress messages on boot +;deposit BOOTR[7] 40 +;deposit BOOTR[7] 52 +deposit BOOTR[7] 42 +;deposit BOOTR[7] 2 +;deposit BOOTR[6] 800 +;deposit BOOTR[0] ffffffff +; +; Set register content at boot for SEL diagnostics +; uncomment next line to get diag loader prompt +;deposit bootr[0] ffffffff +;deposit bootr[1] 0 +;deposit bootr[2] 0 +; set Boot reg 0 1 to boot MSTRALL from master SDT +deposit bootr[0] 1 +; set Boot reg 0 2 to boot MSTREXT from master SDT +;deposit bootr[0] 2 +; +; allow cpu idle +set cpu idle +; Set expect script for auto time entry on MPX at OPCOM prompt +;expect haltafter=20000 +; +; wait for expected output from simulator, then enter this text +;expect " >>" send "BR 31D10\r"; continue +;expect " >>" send "BR 2b4q\r"; continue +;expect " >>" send "BR 430q\r"; continue +expect " >>" send "TE\r"; continue +;expect "ENTER SYSTEM VOLUME CHANNEL AND SUBADDRESS: " send "7600\r"; continue +expect "ENTER SYSTEM DEVICE CHANNEL AND SUBADDRESS: " send "0800\r"; continue +;;expect "BOOT FROM A SCSI TAPE? (REPLY Y OR N): " send "N\r"; continue +;;expect "PLEASE ENTER THE 4 CHAR. DEVICE....." send "0800\r"; continue +;expect "PLEASE ENTER THE 4 CHAR. DEVICE....." send "7600\r"; continue +;;expect "ENTER DISC CONTROLLER TYPE:" send "X\r"; continue +;expect "ENTER DISC CONTROLLER TYPE:" send "M\r"; continue +expect "ENTER DATE" send " %DATE_MM%/%DATE_DD%/%DATE_YY%,%TIME_HH%:%TIME_MM%:%TIME_SS%\r"; GO +; +expect "FMT>" send "INITIALIZE DEV=DM0800 DIS=MH0300 DES=Y CON=N\r"; continue +;expect "FMT>" send "INITIALIZE DEV=DM7600 DIS=SD0700 DES=Y CON=N\r"; continue +;expect "FMT>" send "INITIALIZE DEV=DM7600 DIS=SD0300 DES=Y CON=N\r"; continue +expect "FMT>" send "FORMAT DEV=DM0800 VOL=SYSTEM MAXRE=8000 DES=Y CON=N\r"; continue; +;expect "FMT>" send "FORMAT DEV=DM7600 VOL=SYSTEM MAXRE=8000 CON=N\r"; continue +;expect "FMT>" send "INI DEV=DM7600 DIS=SD0150 DES=Y CON=N\r"; continue +;expect "FMT>" send "FOR DEV=DM7600 DIS=SD0700 VOL=SYSTEM MAXRE=8000 DES=Y CON=N\r"; continue +;expect "FMT>" send "INITIALIZE DEVICE=DM7600 DEST=Y DISC=SD0700 CONFIRM=N\r"; continue +;expect "FMT>" send "INITIALIZE DISC=SD0700 DEST=Y CONFIRM=N\r"; continue +;expect "FMT>" send "INITIALIZE DEST=Y CONFIRM=N\r"; continue +;;expect "FMT>" send "EXIT\r"; continue +expect "VOL>" send "CREATE D SYSTEM ENTRIES=2000\r"; continue +; restore system +expect "VOL>" send "RESTORE VOL=SYSTEM\r"; continue +; restore object, object_e, oldobj +;;expect "VOL>" send "RESTORE VOL=SYSTEM\r"; continue +; restore bin, doc, nblib, sample +;;expect "VOL>" send "RESTORE VOL=SYSTEM\r"; continue +expect "VOL>" send "REWIND\r"; continue +expect "VOL>" send "EXIT\r"; continue +;expect "SYSTEM READY... PRESS ATTENTION FOR TSM..." +expect "SYSTEM READY... PRESS ATTENTION FOR TSM..." send "@@A"; continue +expect "ENTER YOUR OWNERNAME:" send "SYSTEM\r"; continue +expect "TSM>" send "WHO\r"; continue +expect "TSM>" send "J.VFMT\r"; continue +expect "FMT>" send "INITIALIZE DEV=DM0802 DIS=MH0600 DES=Y CON=N\r"; continue +expect "CONTINUE - Y/N" send "Y\r"; continue +expect "TASK J.VFMT" send "R\r"; continue +expect "FMT>" send "FORMAT DEV=DM0802 VOL=WORK MAXRE=10000 DES=Y CON=N\r"; continue +expect "TSM>" send "MOUNT WORK ON DM0802 OPTI=PUBLIC\r"; continue +expect "TASK J.TSM" send "R\r"; continue +expect "TSM>" send "VOLMGR\r"; continue +expect "VOL>" send "CREATE D @WORK^SYSTEM ENTRIES=2000\r"; continue +expect "VOL>" send "EXIT\r"; continue +expect "TSM>" send "!STAT VOL\r"; continue +expect "TSM>" send "SYSINFO\r"; continue +;;expect "TSM>" send "WHO\r"; continue +expect "TSM>" send "SHUTDOWN 0\r"; continue +;;;expect "TSM>" send "EXIT\r"; continue +expect "sim>" send "q\r"; continue +; +; Boot from disk +;bo dma0 +; +; Boot from mag tape +bo mta0 +quit diff --git a/SEL32/sel32_chan.c b/SEL32/sel32_chan.c new file mode 100644 index 00000000..db4d1b0d --- /dev/null +++ b/SEL32/sel32_chan.c @@ -0,0 +1,2971 @@ +/* sel32_chan.c: SEL 32 Channel functions. + + Copyright (c) 2018-2022, James C. Bevier + Portions provided by Richard Cornwell, Geert Rolf and other SIMH contributers + + 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 + JAMES C. BEVIER 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. + +*/ + +/* Handle Class E and F channel I/O operations */ +#include "sel32_defs.h" + +/* Class E I/O device instruction format */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* |00 01 02 03 04 05|06 07 08 09|10 11 12|13 14 15|16 17 18 19 20 21 22 23|24 25 26 27 28 29 30 31| */ +/* | Op Code | Channel |sub-addr| Aug | Command Code | */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* */ + +/* Bits 00-05 - Op code = 0xFC */ +/* Bits 00-09 - I/O channel Address (0-15) */ +/* Bits 10-12 - I/O sub address (0-7) */ +/* Bits 13-15 - Aug code = 6 - CD */ +/* Bits 16-31 - Command Code (Device Dependent) */ + +/* Bits 13-15 - Aug code = 5 - TD */ +/* Bits 16-18 - TD Level 2000, 4000, 8000 */ +/* 01 - TD 2000 Level Status Testing */ +/* 02 - TD 4000 Level Status Testing */ +/* 04 - TD 8000 Level Status Testing */ +/* CC1 CC2 CC3 CC4 */ +/* TD8000 Undefined I/O Activ I/O Error Dev Stat Present */ +/* TD4000 Invd Mem Acc Mem Parity Prog Viol Data Ovr/Undr */ +/* TD2000 - Status Err - Controlr Absent */ + +/* Class F I/O device instruction format */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* |00 01 02 03 04 05|06 07 08|09 10 11 12|13 14 15|16|17 18 19 20 21 22 23|24 25 26 27 28 29 30 31| */ +/* | Op Code | Reg | I/O type | Aug |0 | Channel Address | Device Sub-address | */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* */ + +/* Bits 00-06 - Op code 0xFC */ +/* Bits 09-12 - I/O type */ +/* 00 - Unassigned */ +/* 01 - Unassigned */ +/* 02 - Start I/O (SIO) */ +/* 03 - Test I/O (TIO) */ +/* 04 - Stop I/O (STPIO */ +/* 05 - Reset channel (RSCHNL) */ +/* 06 - Halt I/O (HIO) */ +/* 07 - Grab controller (GRIO) Not supported */ +/* 08 - Reset controller (RSCTL) */ +/* 09 - Enable write channel WCS (ECWCS) Not supported */ +/* 0A - Unassigned */ +/* 0B - Write channel WCS (WCWCS) Not supported */ +/* 0C - Enable channel interrupt (ECI) */ +/* 0D - Disable channel interrupt (DCI) */ +/* 0E - Activate channel interrupt (ACI) */ +/* 0F - Deactivate channel interrupt (DACI) */ +/* Bits 13-15 - Aug Code */ +/* Bit 16 - unused - must be zero */ +/* Bits 16-23 - Channel address (0-127) */ +/* Bits 24-31 - Device Sub address (0-255) */ + +uint32 channels = MAX_CHAN; /* maximum number of channels */ +int subchannels = SUB_CHANS; /* maximum number of subchannel devices */ +int irq_pend = 0; /* pending interrupt flag */ + +extern uint32 CPUSTATUS; /* CPU status word */ +extern uint32 INTS[]; /* Interrupt status flags */ +extern uint32 TPSD[]; /* Temp save of PSD from memory 0&4 */ +extern uint8 waitqcnt; /* # of instructions to xeq b4 int */ +extern uint32 inbusy; +extern uint32 outbusy; + +DIB *dib_unit[MAX_DEV]; /* Pointer to Device info block */ +DIB *dib_chan[MAX_CHAN]; /* pointer to channel mux dib */ +uint16 loading; /* set when booting */ + +/* forward definitions */ +CHANP *find_chanp_ptr(uint16 chsa); /* find chanp pointer */ +UNIT *find_unit_ptr(uint16 chsa); /* find unit pointer */ +int chan_read_byte(uint16 chsa, uint8 *data); +int chan_write_byte(uint16 chsa, uint8 *data); +void set_devattn(uint16 chsa, uint16 flags); +void set_devwake(uint16 chsa, uint16 flags); /* wakeup O/S for async line */ +void chan_end(uint16 chsa, uint16 flags); +int test_write_byte_end(uint16 chsa); +t_stat checkxio(uint16 chsa, uint32 *status); /* check XIO */ +t_stat startxio(uint16 chsa, uint32 *status); /* start XIO */ +t_stat testxio(uint16 chsa, uint32 *status); /* test XIO */ +t_stat stoptxio(uint16 chsa, uint32 *status); /* stop XIO */ +t_stat rschnlxio(uint16 chsa, uint32 *status); /* reset channel XIO */ +t_stat haltxio(uint16 chsa, uint32 *status); /* halt XIO */ +t_stat grabxio(uint16 chsa, uint32 *status); /* grab XIO n/u */ +t_stat rsctlxio(uint16 chsa, uint32 *status); /* reset controller XIO */ +uint32 find_int_icb(uint16 chsa); +uint32 find_int_lev(uint16 chsa); +uint32 scan_chan(uint32 *ilev); +uint32 cont_chan(uint16 chsa); +t_stat set_inch(UNIT *uptr, uint32 inch_addr, uint32 num_inch); /* set inch addr */ +t_stat chan_boot(uint16 chsa, DEVICE *dptr); +t_stat chan_set_devs(); +t_stat set_dev_addr(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat show_dev_addr(FILE *st, UNIT *uptr, int32 v, CONST void *desc); +DEVICE *get_dev(UNIT *uptr); +void store_csw(CHANP *chp); +void push_csw(CHANP *chp); +int16 post_csw(CHANP *chp, uint32 rstat); + +/* FIFO support */ +/* These are FIFO queues which return an error when full. + * + * FIFO is empty when in == out. + * If in != out, then + * - items are placed into in before incrementing in + * - items are removed from out before incrementing out + * FIFO is full when in == (out-1 + FIFO_SIZE) % FIFO_SIZE; + * + * The queue will hold FIFO_SIZE items before the calls + * to FIFO_Put fails. + */ + +/* initialize FIFO to empty in boot channel code */ + +/* add an entry to the start of the FIFO */ +int32 FIFO_Push(uint16 chsa, uint32 entry) +{ + int32 num; /* number of entries */ + DIB *dibp = dib_chan[get_chan(chsa)]; /* get DIB pointer for channel */ + if (dibp == NULL) { + sim_debug(DEBUG_EXP, &cpu_dev, + "FIFO_Push ERR NULL dib ptr for chsa %04x\n", chsa); + return -1; /* FIFO address error */ + } + + if (dibp->chan_fifo_in == ((dibp->chan_fifo_out-1+FIFO_SIZE) % FIFO_SIZE)) { + num = (dibp->chan_fifo_in - dibp->chan_fifo_out + FIFO_SIZE) % FIFO_SIZE; + sim_debug(DEBUG_EXP, &cpu_dev, + "FIFO_Push ERR FIFO full for chsa %04x count %02x\n", chsa, num); + return -1; /* FIFO Full */ + } + dibp->chan_fifo_out += (FIFO_SIZE - 1); /* get index to previous first entry */ + dibp->chan_fifo_out %= FIFO_SIZE; /* modulo FIFO size */ + dibp->chan_fifo[dibp->chan_fifo_out] = entry; /* add new entry to be new first */ + num = (dibp->chan_fifo_in - dibp->chan_fifo_out + FIFO_SIZE) % FIFO_SIZE; + sim_debug(DEBUG_EXP, &cpu_dev, + "FIFO_Push to FIFO for chsa %04x count %02x\n", chsa, num); + return SCPE_OK; /* all OK */ +} + +/* add an entry to the FIFO */ +int32 FIFO_Put(uint16 chsa, uint32 entry) +{ + int32 num; /* number of entries */ + DIB *dibp = dib_chan[get_chan(chsa)]; /* get DIB pointer for channel */ + if (dibp == NULL) { + sim_debug(DEBUG_EXP, &cpu_dev, + "FIFO_Put ERR NULL dib ptr for chsa %04x\n", chsa); + return -1; /* FIFO address error */ + } + + if (dibp->chan_fifo_in == ((dibp->chan_fifo_out-1+FIFO_SIZE) % FIFO_SIZE)) { + num = (dibp->chan_fifo_in - dibp->chan_fifo_out + FIFO_SIZE) % FIFO_SIZE; + sim_debug(DEBUG_EXP, &cpu_dev, + "FIFO_Put ERR FIFO full for chsa %04x count %02x\n", chsa, num); + return -1; /* FIFO Full */ + } + dibp->chan_fifo[dibp->chan_fifo_in] = entry; /* add new entry */ + dibp->chan_fifo_in += 1; /* next entry */ + dibp->chan_fifo_in %= FIFO_SIZE; /* modulo FIFO size */ + num = (dibp->chan_fifo_in - dibp->chan_fifo_out + FIFO_SIZE) % FIFO_SIZE; + return SCPE_OK; /* all OK */ +} + +/* get the next entry from the FIFO */ +int32 FIFO_Get(uint16 chsa, uint32 *old) +{ + DIB *dibp = dib_chan[get_chan(chsa)]; /* get DIB pointer for channel */ + if (dibp == NULL) { + sim_debug(DEBUG_EXP, &cpu_dev, + "FIFO_Get ERR NULL dib ptr for chsa %04x\n", chsa); + return -1; /* FIFO address error */ + } + + /* see if the FIFO is empty */ + if (dibp->chan_fifo_in == dibp->chan_fifo_out) { + return -1; /* FIFO is empty, tell caller */ + } + *old = dibp->chan_fifo[dibp->chan_fifo_out]; /* get the next entry */ + dibp->chan_fifo_out += 1; /* next entry */ + dibp->chan_fifo_out %= FIFO_SIZE; /* modulo FIFO size */ + return SCPE_OK; /* all OK */ +} + +/* get number of entries in FIFO for channel */ +int32 FIFO_Num(uint16 chsa) +{ + int32 num; /* number of entries */ + DIB *dibp = dib_chan[get_chan(chsa)]; /* get DIB pointer for channel */ + if (dibp == NULL) { + sim_debug(DEBUG_EXP, &cpu_dev, + "FIFO_Num ERR NULL dib ptr for chsa %04x\n", chsa); + return 0; /* FIFO address error */ + } + /* calc entries */ + num = (dibp->chan_fifo_in - dibp->chan_fifo_out + FIFO_SIZE) % FIFO_SIZE; + return (num>>1); /*GT*/ /* two words/entry */ +} + +/* add an entry to the IOCLQ */ +int32 IOCLQ_Put(IOCLQ *qptr, uint32 entry) +{ + int32 num; /* number of entries */ + if (qptr == NULL) { + sim_debug(DEBUG_EXP, &cpu_dev, "IOCLQ_Put ERROR NULL qptr\n"); + return -1; /* IOCLQ address error */ + } + + if (qptr->ioclq_in == ((qptr->ioclq_out-1+IOCLQ_SIZE) % IOCLQ_SIZE)) { + num = (qptr->ioclq_in - qptr->ioclq_out + IOCLQ_SIZE) % IOCLQ_SIZE; + sim_debug(DEBUG_EXP, &cpu_dev, "IOCLQ_Put ERROR IOCLQ full, entries %02x\n", num); + return -1; /* IOCLQ Full */ + } + qptr->ioclq_fifo[qptr->ioclq_in] = entry; /* add new entry */ + qptr->ioclq_in += 1; /* next entry */ + qptr->ioclq_in %= IOCLQ_SIZE; /* modulo IOCLQ size */ + num = (qptr->ioclq_in - qptr->ioclq_out + IOCLQ_SIZE) % IOCLQ_SIZE; + return SCPE_OK; /* all OK */ +} + +/* get the next entry from the IOCLQ */ +int32 IOCLQ_Get(IOCLQ *qptr, uint32 *old) +{ + if (qptr == NULL) { + sim_debug(DEBUG_EXP, &cpu_dev, "IOCLQ_Get ERROR NULL qptr\n"); + return -1; /* IOCLQ address error */ + } + + /* see if the IOCLQ is empty */ + if (qptr->ioclq_in == qptr->ioclq_out) { + return -1; /* IOCLQ is empty, tell caller */ + } + *old = qptr->ioclq_fifo[qptr->ioclq_out]; /* get the next entry */ + qptr->ioclq_out += 1; /* next entry */ + qptr->ioclq_out %= IOCLQ_SIZE; /* modulo IOCLQ size */ + return SCPE_OK; /* all OK */ +} + +/* get number of entries in IOCLQ for channel */ +int32 IOCLQ_Num(IOCLQ *qptr) +{ + int32 num = 0; /* number of entries */ + if (qptr == NULL) { + sim_debug(DEBUG_EXP, &cpu_dev, "IOCLQ_Num ERROR NULL qptr\n"); + return num; /* IOCLQ address error */ + } + /* calc entries */ + num = (qptr->ioclq_in - qptr->ioclq_out + IOCLQ_SIZE) % IOCLQ_SIZE; + return num; /* one words/entry */ +} + +/* + * Number of inch buffers defined for each channel + * IOP 128 Dbl words + * MFP 128 Dbl words + * 8-line uses IOP/MFP (128) + * BTP tape 2 DBL wds + * UDP disk 33 Dbl wds + * SCFI disk 33 Dbl wds + * HSDP disk 33 Dbl wds + * SCSI disk uses MFP (128) + * LP uses IOP/MFP (128) + * Console uses IOP/MFP (128) + * Ethernet 1 Dbl wd + * */ +/* Set INCH buffer address for channel */ +/* return SCPE_OK or SCPE_MEM if invalid address or SCPE_ARG if already defined */ +t_stat set_inch(UNIT *uptr, uint32 inch_addr, uint32 num_inch) { + uint16 chsa = GET_UADDR(uptr->u3); /* get channel & sub address */ + uint32 chan = chsa & 0x7f00; /* get just channel address */ + uint32 last = inch_addr + (num_inch-1)*8; /* last inch address to use */ + CHANP *chp; + int i, j; + DIB *dibp = dib_chan[chan>>8]; /* get channel dib ptr */ + CHANP *pchp = 0; /* for channel prog ptr */ + + /* must be valid DIB pointer */ + if (dibp == NULL) + return SCPE_MEM; /* return memory error */ + pchp = dibp->chan_prg; /* get parent channel prog ptr */ + + /* must be valid channel pointer */ + if (pchp == NULL) + return SCPE_MEM; /* return memory error */ + + /* see if start valid memory address */ + if (!MEM_ADDR_OK(inch_addr)) /* see if mem addr >= MEMSIZE */ + return SCPE_MEM; /* return memory error */ + + /* see if end valid memory address */ + if (!MEM_ADDR_OK(last)) /* see if mem addr >= MEMSIZE */ + return SCPE_MEM; /* return memory error */ + + /* set INCH address for all units on master channel */ + chp = pchp; + for (i=0; inumunits; i++) { + chp->chan_inch_addr = inch_addr; /* set the current inch addr */ + chp->base_inch_addr = inch_addr; /* set the base inch addr */ + chp->max_inch_addr = last; /* set the last inch addr */ + chp++; /* next unit channel */ + } + + sim_debug(DEBUG_XIO, &cpu_dev, + "set_inch chan %04x inch addr %06x last %06x chp %p\n", + chan, inch_addr, last, chp); + + /* now go through all the sub addresses for the channel and set inch addr */ + for (i=0; ichan_prg; /* get first unit channel prog ptr */ + /* set INCH address for all units on channel */ + for (j=0; jnumunits; j++) { + chp->chan_inch_addr = inch_addr; /* set the inch addr */ + chp->base_inch_addr = inch_addr; /* set the base inch addr */ + chp->max_inch_addr = last; /* set the last inch addr */ + chp++; /* next unit channel */ + } + } + return SCPE_OK; /* All OK */ +} + +/* Find interrupt level for the given physical device (ch/sa) */ +/* return 0 if not found, otherwise level number */ +uint32 find_int_lev(uint16 chsa) +{ + uint32 inta; + /* get the device entry for channel in SPAD */ + uint32 spadent = SPAD[get_chan(chsa)]; /* get spad device entry for logical channel */ + + if ((spadent == 0) || ((spadent&MASK24) == MASK24)) { /* see if valid entry */ + sim_debug(DEBUG_EXP, &cpu_dev, + "find_int_lev ERR chsa %04x spadent %08x\n", chsa, spadent); + return 0; /* not found */ + } + inta = ((~spadent)>>16)&0x7f; /* get interrupt level */ + return(inta); /* return the level*/ +} + +/* Find interrupt context block address for given device (ch/sa) */ +/* return 0 if not found, otherwise ICB memory address */ +uint32 find_int_icb(uint16 chsa) +{ + uint32 inta, icba; + + inta = find_int_lev(chsa); /* find the int level */ + if (inta == 0) { + sim_debug(DEBUG_EXP, &cpu_dev, + "find_int_icb ERR chsa %04x inta %02x\n", chsa, inta); + return 0; /* not found */ + } + /* add interrupt vector table base address plus int # byte address offset */ + icba = SPAD[0xf1] + (inta<<2); /* interrupt vector address in memory */ + if (!MEM_ADDR_OK(icba)) { /* needs to be valid address in memory */ + sim_debug(DEBUG_EXP, &cpu_dev, + "find_int_icb ERR chsa %04x icba %02x\n", chsa, icba); + return 0; /* not found */ + } + icba = RMW(icba); /* get address of ICB from memory */ + if (!MEM_ADDR_OK(icba)) { /* needs to be valid address in memory */ + sim_debug(DEBUG_EXP, &cpu_dev, + "find_int_icb ERR chsa %04x icba %02x\n", chsa, icba); + return 0; /* not found */ + } + return(icba); /* return the address */ +} + +/* Find unit pointer for given device (ch/sa) */ +UNIT *find_unit_ptr(uint16 chsa) +{ + struct dib *dibp; /* DIB pointer */ + UNIT *uptr; /* UNIT pointer */ + int i; + + dibp = dib_unit[chsa]; /* get DIB pointer from device address */ + if (dibp == 0) { /* if zero, not defined on system */ + sim_debug(DEBUG_EXP, &cpu_dev, + "find_unit_ptr ERR chsa %04x dibp %p\n", chsa, dibp); + return NULL; /* tell caller */ + } + + uptr = dibp->units; /* get the pointer to the units on this channel */ + if (uptr == 0) { /* if zero, no devices defined on system */ + sim_debug(DEBUG_EXP, &cpu_dev, + "find_unit_ptr ERR chsa %04x uptr %p\n", chsa, uptr); + return NULL; /* tell caller */ + } + + for (i = 0; i < dibp->numunits; i++) { /* search through units to get a match */ + if (chsa == GET_UADDR(uptr->u3)) { /* does ch/sa match? */ + return uptr; /* return the pointer */ + } + uptr++; /* next unit */ + } + return NULL; /* device not found on system */ +} + +/* Find channel program pointer for given device (ch/sa) */ +CHANP *find_chanp_ptr(uint16 chsa) +{ + struct dib *dibp; /* DIB pointer */ + UNIT *uptr; /* UNIT pointer */ + CHANP *chp; /* CHANP pointer */ + int i; + + dibp = dib_unit[chsa]; /* get DIB pointer from unit address */ + if (dibp == 0) { /* if zero, not defined on system */ + sim_debug(DEBUG_EXP, &cpu_dev, "find_chanp_ptr ERR chsa %04x dibp %p\n", chsa, dibp); + return NULL; /* tell caller */ + } + if ((chp = (CHANP *)dibp->chan_prg) == NULL) { /* must have channel information for each device */ + sim_debug(DEBUG_EXP, &cpu_dev, "find_chanp_ptr ERR chsa %04x chp %p\n", chsa, chp); + return NULL; /* tell caller */ + } + + uptr = dibp->units; /* get the pointer to the units on this channel */ + if (uptr == 0) { /* if zero, no devices defined on system */ + sim_debug(DEBUG_EXP, &cpu_dev, "find_chanp_ptr ERR chsa %04x uptr %p\n", chsa, uptr); + return NULL; /* tell caller */ + } + + for (i = 0; i < dibp->numunits; i++) { /* search through units to get a match */ + if (chsa == GET_UADDR(uptr->u3)) { /* does ch/sa match? */ + return chp; /* return the pointer */ + } + uptr++; /* next UNIT */ + chp++; /* next CHANP */ + } + sim_debug(DEBUG_EXP, &cpu_dev, "find_chanp_ptr ERR chsa %04x no match uptr %p\n", chsa, uptr); + return NULL; /* device not found on system */ +} + +/* Read a full word into memory. + * Return 1 if fail. + * Return 0 if success. + */ +int readfull(CHANP *chp, uint32 maddr, uint32 *word) +{ + maddr &= MASK24; /* mask addr to 24 bits */ + if (!MEM_ADDR_OK(maddr)) { /* see if mem addr >= MEMSIZE */ + chp->chan_status |= STATUS_PCHK; /* program check error */ + sim_debug(DEBUG_EXP, &cpu_dev, + "readfull read %08x from addr %08x ERROR\n", *word, maddr); + return 1; /* show we have error */ + } + *word = RMW(maddr); /* get 1 word */ + sim_debug(DEBUG_XIO, &cpu_dev, "READFULL chsa %04x read %08x from addr %08x\n", + chp->chan_dev, *word, maddr); + return 0; /* return OK */ +} + +/* Read a byte into the channel buffer. + * Return 1 if fail. + * Return 0 if success. + */ +int readbuff(CHANP *chp) +{ + uint32 addr = chp->ccw_addr; /* channel buffer address */ + + if (!MEM_ADDR_OK(addr & MASK24)) { /* see if memory address invalid */ + chp->chan_status |= STATUS_PCHK; /* bad, program check */ + sim_debug(DEBUG_EXP, &cpu_dev, + "readbuff PCHK addr %08x to big mem %08x status %04x\n", + addr, MEMSIZE, chp->chan_status); + chp->chan_byte = BUFF_CHNEND; /* force channel end & busy */ + return 1; /* done, with error */ + } + chp->chan_buf = RMB(addr&MASK24); /* get 1 byte */ + return 0; +} + +/* Write byte to channel buffer in memory. + * Return 1 if fail. + * Return 0 if success. + */ +int writebuff(CHANP *chp) +{ + uint32 addr = chp->ccw_addr; + + if (!MEM_ADDR_OK(addr & MASK24)) { /* make sure write to good addr */ + chp->chan_status |= STATUS_PCHK; /* non-present memory, abort */ + sim_debug(DEBUG_EXP, &cpu_dev, + "writebuff PCHK addr %08x to big mem %08x status %04x\n", + addr, MEMSIZE, chp->chan_status); + chp->chan_byte = BUFF_CHNEND; /* force channel end & busy */ + return 1; + } + addr &= MASK24; /* good address, write the byte */ + sim_debug(DEBUG_DATA, &cpu_dev, "writebuff WRITE addr %06x DATA %08x status %04x\n", + addr, chp->chan_buf, chp->chan_status); + WMB(addr, chp->chan_buf); /* write byte to memory */ + return 0; +} + +/* load in the IOCD and process the commands */ +/* return = 0 OK */ +/* return = 1 error, chan_status will have reason */ +int32 load_ccw(CHANP *chp, int32 tic_ok) +{ + uint32 word1 = 0; + uint32 word2 = 0; + int32 docmd = 0; + DIB *dibp = dib_unit[chp->chan_dev]; /* get the DIB pointer */ + UNIT *uptr = chp->unitptr; /* get the unit ptr */ + uint16 chan = get_chan(chp->chan_dev); /* our channel */ + uint16 chsa = chp->chan_dev; /* our chan/sa */ + uint16 devstat = 0; + + sim_debug(DEBUG_XIO, &cpu_dev, + "load_ccw @%06x entry chan_status[%02x]=%04x\n", + chp->chan_caw, chan, chp->chan_status); +#ifdef TEST_FOR_IOCL_CHANGE + /* see if iocla or iocd has changed since start */ + if (!loading && (chp->chan_info & INFO_SIOCD)) { /* see if 1st IOCD in channel prog */ + uint32 chan_icb; /* Interrupt level context block address */ + uint32 iocla; /* I/O channel IOCL address int ICB */ + + chan_icb = find_int_icb(chsa); /* Interrupt level context block address */ + iocla = RMW(chan_icb+16); /* iocla is in wd 4 of ICB */ + word1 = RMW(iocla & MASK24); /* get 1st IOCL word */ + word2 = RMW((iocla + 4) & MASK24); /* get 2nd IOCL word */ + if (chp->chan_caw != iocla) { + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw iocla (%06x) != chan_caw (%06x) chsa %04x\n", + iocla, chp->chan_caw, chsa); + } else + { + /* iocla has not changed, see if IOCD has */ + if (chp->new_iocla != iocla) { /* check current iocla */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw iocla (%06x) != new_iocla (%06x) chsa %04x\n", + iocla, chp->new_iocla, chsa); + } + if (word1 != chp->new_iocd1) { /* check word1 from memory */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw iocd1 (%06x) != new_iocd1 (%06x) chsa %04x\n", + word1, chp->new_iocd1, chsa); + } + if (word2 != chp->new_iocd2) { /* check word2 from memory */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw iocd2 (%06x) != new_iocd2 (%06x) chsa %04x\n", + word2, chp->new_iocd2, chsa); + } + } + } +#endif + + /* determine if channel DIB has a pre iocl processor */ + if (dibp->iocl_io != NULL) { /* NULL if no function */ + /* call the device controller to process the iocl */ + int32 tempa = dibp->iocl_io(chp, tic_ok); /* process IOCL */ + if (tempa != SCPE_OK) { /* see if OK */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw iocl_io call return ERROR chan %04x cstat %01x\n", chan, tempa); + } else { + sim_debug(DEBUG_XIO, &cpu_dev, + "load_ccw iocl_io call return OK chan %04x cstat %01x\n", chan, tempa); + } + return tempa; /* just return status */ + } + /* check for valid iocd address if 1st iocd */ + if (chp->chan_info & INFO_SIOCD) { /* see if 1st IOCD in channel prog */ + if (chp->chan_caw & 0x3) { /* must be word bounded */ + sim_debug(DEBUG_XIO, &cpu_dev, + "load_ccw iocd bad address chsa %02x caw %06x\n", + chsa, chp->chan_caw); + /* the disk returns the bad iocl in sw1 */ + chp->ccw_addr = chp->chan_caw & MASK24; /* set the bad IOCL address */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid iocd addr */ + return 1; /* error return */ + } + } + +loop: + sim_debug(DEBUG_XIO, &cpu_dev, + "load_ccw @%06x @loop chan_status[%02x]=%04x\n", + chp->chan_caw, chan, chp->chan_status); + + /* Abort if we have any errors */ + if (chp->chan_status & STATUS_ERROR) { /* check channel error status */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw ERROR1 chan_status[%02x]=%04x\n", chan, chp->chan_status); + return 1; + } + + /* Read in first CCW */ + if (readfull(chp, chp->chan_caw, &word1) != 0) { /* read word1 from memory */ + chp->chan_status |= STATUS_PCHK; /* memory read error, program check */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw ERROR2 chan_status[%02x]=%04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* Read in second CCW */ + if (readfull(chp, chp->chan_caw+4, &word2) != 0) { /* read word2 from memory */ + chp->chan_status |= STATUS_PCHK; /* memory read error, program check */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw ERROR3 chan_status[%02x]=%04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + sim_debug(DEBUG_XIO, &cpu_dev, + "load_ccw @%06x read ccw chan %02x IOCD wd 1 %08x wd 2 %08x\n", + chp->chan_caw, chan, word1, word2); + + chp->chan_caw = (chp->chan_caw & 0xfffffc) + 8; /* point to next IOCD */ + + /* Check if we had data chaining in previous iocd */ + /* if we did, use previous cmd value */ + if (((chp->chan_info & INFO_SIOCD) == 0) && /* see if 1st IOCD in channel prog */ + (chp->ccw_flags & FLAG_DC)) { /* last IOCD have DC set? */ + sim_debug(DEBUG_XIO, &cpu_dev, + "load_ccw @%06x DO DC, ccw_flags %04x cmd %02x\n", + chp->chan_caw, chp->ccw_flags, chp->ccw_cmd); + } else + chp->ccw_cmd = (word1 >> 24) & 0xff; /* set new command from IOCD wd 1 */ + + if (!MEM_ADDR_OK(word1 & MASK24)) { /* see if memory address invalid */ + chp->chan_status |= STATUS_PCHK; /* bad, program check */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw bad IOCD1 chan_status[%02x]=%04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + chp->ccw_count = word2 & 0xffff; /* get 16 bit byte count from IOCD WD 2*/ + + /* here is where we would validate the device commands */ + + if (chp->chan_info & INFO_SIOCD) { /* see if 1st IOCD in channel prog */ + /* 1st command can not be a TIC */ + if (chp->ccw_cmd == CMD_TIC) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw TIC bad cmd chan_status[%02x]=%04x\n", + chan, chp->chan_status); + return 1; /* error return */ + } + } + + /* TIC can't follow TIC or be first in command chain */ + /* diags send bad commands for testing. Use all of op */ + if (chp->ccw_cmd == CMD_TIC) { + if (tic_ok) { + if (((word1 & MASK24) == 0) || (word1 & 0x3)) { + sim_debug(DEBUG_XIO, &cpu_dev, + "load_ccw tic cmd bad address chan %02x tic caw %06x IOCD wd 1 %08x\n", + chan, chp->chan_caw, word1); + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + return 1; /* error return */ + } + tic_ok = 0; /* another tic not allowed */ + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + sim_debug(DEBUG_XIO, &cpu_dev, + "load_ccw tic cmd ccw chan %02x tic caw %06x IOCD wd 1 %08x\n", + chan, chp->chan_caw, word1); + goto loop; /* restart the IOCD processing */ + } + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw TIC ERROR chan_status[%02x]=%04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* Check if we had data chaining in previous iocd */ + if ((chp->chan_info & INFO_SIOCD) || /* see if 1st IOCD in channel prog */ + (((chp->chan_info & INFO_SIOCD) == 0) && /* see if 1st IOCD in channel prog */ + ((chp->ccw_flags & FLAG_DC) == 0))) { /* last IOCD have DC set? */ + sim_debug(DEBUG_XIO, &cpu_dev, + "load_ccw @%06x DO CMD No DC, ccw_flags %04x cmd %02x\n", + chp->chan_caw, chp->ccw_flags, chp->ccw_cmd); + docmd = 1; /* show we have a command */ + } + + /* Set up for this command */ + chp->ccw_flags = (word2 >> 16) & 0xfc00; /* get flags from bits 0-6 of WD 2 of IOCD */ + chp->chan_status = 0; /* clear status for next IOCD */ + /* make a 24 bit address */ + chp->ccw_addr = word1 & MASK24; /* set the data/seek address */ + + if (chp->ccw_flags & FLAG_PCI) { /* do we have prog controlled int? */ + chp->chan_status |= STATUS_PCI; /* set PCI flag in status */ + irq_pend = 1; /* interrupt pending */ + } + + /* validate parts of IOCD2 that is reserved, bits 5-15 */ + if (word2 & 0x07ff0000) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid iocd */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw bad IOCD2 chan_status[%02x]=%04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* DC can only be used with a read/write cmd */ + /* TODO move ccw code to LPR processing */ + /* TEMP FIX FOR LPR */ + if ((chp->ccw_flags & FLAG_DC) && (chsa != 0x7ef8)) { + if ((chp->ccw_cmd != 0x02) && (chp->ccw_cmd != 0x01)) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid DC */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw DC ERROR chan_status[%02x]=%04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + } + + chp->chan_byte = BUFF_BUSY; /* busy & no bytes transferred yet */ + + sim_debug(DEBUG_XIO, &cpu_dev, + "load_ccw @%06x read docmd %01x addr %06x count %04x chsa %04x ccw_flags %04x\n", + chp->chan_caw, docmd, chp->ccw_addr, chp->ccw_count, chsa, chp->ccw_flags); + + if (docmd) { /* see if we need to process a command */ + DIB *dibp = dib_unit[chp->chan_dev]; /* get the DIB pointer */ + + uptr = chp->unitptr; /* get the unit ptr */ + if (dibp == 0 || uptr == 0) { + chp->chan_status |= STATUS_PCHK; /* program check if it is */ + return 1; /* if none, error */ + } + + sim_debug(DEBUG_XIO, &cpu_dev, + "load_ccw @%06x before start_cmd chsa %04x status %04x count %04x SNS %08x\n", + chp->chan_caw, chsa, chp->chan_status, chp->ccw_count, uptr->u5); + + /* call the device startcmd function to process the current command */ + /* just replace device status bits */ + chp->chan_info &= ~INFO_CEND; /* show chan_end not called yet */ + devstat = dibp->start_cmd(uptr, chan, chp->ccw_cmd); + chp->chan_status = (chp->chan_status & 0xff00) | devstat; + chp->chan_info &= ~INFO_SIOCD; /* not first IOCD in channel prog */ + + sim_debug(DEBUG_XIO, &cpu_dev, + "load_ccw @%06x after start_cmd chsa %04x status %08x count %04x\n", + chp->chan_caw, chsa, chp->chan_status, chp->ccw_count); + + /* We will get a SNS_BSY status returned if device doing a command */ + /* We get STATUS_CEND & STATUS_DEND and an error */ + /* We get SCPE_OK (0) saying cmd is ready to process */ + /* see if bad status */ + if (chp->chan_status & (STATUS_ATTN|STATUS_ERROR)) { + chp->chan_status |= STATUS_CEND; /* channel end status */ + chp->ccw_flags = 0; /* no flags */ + chp->chan_byte = BUFF_NEXT; /* have main pick us up */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw bad status chsa %04x status %04x cmd %02x\n", + chsa, chp->chan_status, chp->ccw_cmd); + /* done with command */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw ERROR return chsa %04x status %08x\n", + chp->chan_dev, chp->chan_status); + return 1; /* error return */ + } + /* NOTE this code needed for MPX 1.X to run! */ + /* see if command completed */ + /* we have good status */ + /* TODO Test if chan_end called? */ + if (chp->chan_status & (STATUS_DEND|STATUS_CEND)) { + uint16 chsa = GET_UADDR(uptr->u3); /* get channel & sub address */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* show I/O complete */ + sim_debug(DEBUG_XIO, &cpu_dev, + "load_ccw @%06x FIFO #%1x cmd complete chan %04x status %04x count %04x\n", + chp->chan_caw, FIFO_Num(chsa), chan, chp->chan_status, chp->ccw_count); + } + } + /* the device processor returned OK (0), so wait for I/O to complete */ + /* nothing happening, so return */ + sim_debug(DEBUG_XIO, &cpu_dev, + "load_ccw @%06x return, chsa %04x status %04x count %04x irq_pend %1x\n", + chp->chan_caw, chsa, chp->chan_status, chp->ccw_count, irq_pend); + return 0; /* good return */ +} + +/* read byte from memory */ +/* write to device */ +int chan_read_byte(uint16 chsa, uint8 *data) +{ + CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */ + int byte; + + /* Abort if we have any errors */ + if (chp->chan_status & STATUS_ERROR) /* check channel error status */ + return 1; /* return error */ + + if (chp->chan_byte == BUFF_CHNEND) /* check for end of data */ + return 1; /* yes, return error */ + + if (chp->ccw_count == 0) { /* see if more data required */ + if ((chp->ccw_flags & FLAG_DC) == 0) { /* see if Data Chain */ + chp->chan_byte = BUFF_CHNEND; /* buffer end too */ + sim_debug(DEBUG_EXP, &cpu_dev, + "chan_read_byte no DC chan end, cnt %04x addr %06x chsa %04x\n", + chp->ccw_count, chp->ccw_addr, chsa); + return 1; /* return error */ + } else { + /* we have data chaining, process iocl */ + if (load_ccw(chp, 1)) { /* process data chaining */ + sim_debug(DEBUG_EXP, &cpu_dev, + "chan_read_byte with DC error, cnt %04x addr %06x chsa %04x\n", + chp->ccw_count, chp->ccw_addr, chsa); + return 1; /* return error */ + } + sim_debug(DEBUG_DETAIL, &cpu_dev, + "chan_read_byte with DC IOCD loaded, cnt %04x addr %06x chsa %04x\n", + chp->ccw_count, chp->ccw_addr, chsa); + } + } + /* get the next byte from memory */ + if (readbuff(chp)) /* read next char */ + return 1; /* return error */ + + /* get the byte of data */ + byte = chp->chan_buf; /* read byte from memory */ + *data = byte; /* return the data */ + sim_debug(DEBUG_DATA, &cpu_dev, "chan_read_byte transferred %02x\n", byte); + chp->ccw_addr += 1; /* next byte address */ + chp->ccw_count--; /* one char less to process */ + return 0; /* good return */ +} + +/* test end of write byte I/O (device read) */ +int test_write_byte_end(uint16 chsa) +{ + CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */ + + /* see if at end of buffer */ + if (chp->chan_byte == BUFF_CHNEND) /* check for end of data */ + return 1; /* return done */ + if (chp->ccw_count == 0) { + if ((chp->ccw_flags & FLAG_DC) == 0) { /* see if we have data chaining */ + chp->chan_byte = BUFF_CHNEND; /* thats all the data we want */ + return 1; /* return done */ + } + } + return 0; /* not done yet */ +} + +/* write byte to memory */ +/* read from device */ +int chan_write_byte(uint16 chsa, uint8 *data) +{ + int chan = get_chan(chsa); /* get the channel number */ + CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */ + + /* Abort if we have any errors */ + if (chp->chan_status & STATUS_ERROR) /* check channel error status */ + return 1; /* return error */ + + /* see if at end of buffer */ + if (chp->chan_byte == BUFF_CHNEND) { /* check for end of data */ + /* if SLI not set, we have incorrect length */ + if ((chp->ccw_flags & FLAG_SLI) == 0) { + sim_debug(DEBUG_EXP, &cpu_dev, "chan_write_byte 4 setting SLI ret\n"); + chp->chan_status |= STATUS_LENGTH; /* set SLI */ + } + return 1; /* return error */ + } + if (chp->ccw_count == 0) { + if ((chp->ccw_flags & FLAG_DC) == 0) { /* see if we have data chaining */ + sim_debug(DEBUG_EXP, &cpu_dev, + "chan_write_byte no DC ccw_flags %04x\n", chp->ccw_flags); + chp->chan_status |= STATUS_CEND; /* no, end of data */ + chp->chan_byte = BUFF_CHNEND; /* thats all the data we want */ + return 1; /* return done error */ + } else { + /* we have data chaining, process iocl */ + if (load_ccw(chp, 1)) { /* process data chaining */ + sim_debug(DEBUG_EXP, &cpu_dev, + "chan_write_byte with DC error, cnt %04x addr %06x chan %04x\n", + chp->ccw_count, chp->ccw_addr, chan); + return 1; /* return error */ + } + } + } + /* we have data byte to write to chp->ccw_addr */ + /* see if we want to skip writing data to memory */ + if (chp->ccw_flags & FLAG_SKIP) { + chp->ccw_count--; /* decrement skip count */ + chp->chan_byte = BUFF_BUSY; /* busy, but no data */ + if ((chp->ccw_cmd & 0xff) == CMD_RDBWD) + chp->ccw_addr--; /* backward */ + else + chp->ccw_addr++; /* forward */ + return 0; + } + chp->chan_buf = *data; /* get data byte */ + if (writebuff(chp)) /* write the byte */ + return 1; + + chp->ccw_count--; /* reduce count */ + chp->chan_byte = BUFF_BUSY; /* busy, but no data */ + if ((chp->ccw_cmd & 0xff) == CMD_RDBWD) /* see if reading backwards */ + chp->ccw_addr -= 1; /* no, use previous address */ + else + chp->ccw_addr += 1; /* yes, use next address */ + return 0; +} + +/* post wakeup interrupt for specified async line */ +void set_devwake(uint16 chsa, uint16 flags) +{ + uint32 stwd1, stwd2; /* words 1&2 of stored status */ + /* put sub address in byte 0 */ + stwd1 = (chsa & 0xff) << 24; /* subaddress and IOCD address to SW 1 */ + /* save 16 bit channel status and residual byte count in SW 2 */ + stwd2 = (uint32)flags << 16; + if ((FIFO_Put(chsa, stwd1) == -1) || (FIFO_Put(chsa, stwd2) == -1)) { + sim_debug(DEBUG_EXP, &cpu_dev, + "set_devwake FIFO Overflow ERROR on chsa %04x\n", chsa); + } + irq_pend = 1; /* wakeup controller */ +} + +/* post interrupt for specified channel */ +void set_devattn(uint16 chsa, uint16 flags) +{ + CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */ + + if (chp == NULL) { + /* can not do anything, so just return */ + sim_debug(DEBUG_EXP, &cpu_dev, "set_devattn chsa %04x, flags %04x\n", chsa, flags); + fprintf(stdout, "set_devattn chsa %04x invalid configured device\n", chsa); +// fflush(stdout); + return; + } + + if (chp->chan_dev == chsa && (chp->chan_status & STATUS_CEND) != 0 && (flags & SNS_DEVEND) != 0) { + chp->chan_status |= ((uint16)flags); + } + sim_debug(DEBUG_CMD, &cpu_dev, "set_devattn(%04x, %04x) %04x\n", chsa, flags, chp->chan_dev); + irq_pend = 1; +} + +/* channel operation completed */ +void chan_end(uint16 chsa, uint16 flags) { + uint16 tstat, tcnt; + CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */ + + sim_debug(DEBUG_CMD, &cpu_dev, + "chan_end entry chsa %04x flags %04x status %04x cmd %02x cpustatus %08x\n", + chsa, flags, chp->chan_status, chp->ccw_cmd, CPUSTATUS); +// fflush(sim_deb); + + /* see if already called */ + if (chp->chan_info & INFO_CEND) { + sim_debug(DEBUG_EXP, &cpu_dev, + "chan_end INFO_CEND set chsa %04x ccw_flags %04x status %04x byte %02x\n", + chsa, chp->ccw_flags, chp->chan_status, chp->chan_byte); + } + chp->chan_info |= INFO_CEND; /* we have been here */ + + chp->chan_byte = BUFF_BUSY; /* we are empty & still busy now */ + chp->chan_status |= STATUS_CEND; /* set channel end */ + chp->chan_status |= ((uint16)flags); /* add in the callers flags */ + + /* read/write must have none-zero byte count */ + /* all others can be zero, except NOP, which must be 0 */ + /* a NOP is Control command 0x03 with no modifier bits */ + /* see if this is a read/write cmd */ + if (((chp->ccw_cmd & 0x7) == 0x02) || ((chp->ccw_cmd & 0x7) == 0x01)) { + /* test for incorrect transfer length */ + if (chp->ccw_count != 0 && ((chp->ccw_flags & FLAG_SLI) == 0)) { + if ((chp->chan_status & STATUS_PCHK) == 0) /* No SLI if channel prg check */ + chp->chan_status |= STATUS_LENGTH; /* show incorrect length status */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "chan_end setting SLI chsa %04x count %04x ccw_flags %04x status %04x\n", + chsa, chp->ccw_count, chp->ccw_flags, chp->chan_status); + chp->ccw_flags = 0; /* no iocl flags */ + } + } + + /* Diags do not want SLI if we have no device end status */ + if ((chp->chan_status & STATUS_LENGTH) && ((chp->chan_status & STATUS_DEND) == 0)) + chp->chan_status &= ~STATUS_LENGTH; + + /* no flags for attention status */ + if (flags & (SNS_ATTN|SNS_UNITCHK|SNS_UNITEXP)) { + chp->ccw_flags = 0; /* no flags */ + } + + sim_debug(DEBUG_EXP, &cpu_dev, + "chan_end test end chsa %04x ccw_flags %04x status %04x byte %02x\n", + chsa, chp->ccw_flags, chp->chan_status, chp->chan_byte); + + /* test for device or controller end */ + if (chp->chan_status & (STATUS_DEND|STATUS_CEND)) { + chp->chan_byte = BUFF_BUSY; /* we are empty & still busy now */ + sim_debug(DEBUG_XIO, &cpu_dev, + "chan_end FIFO #%1x IOCL done chsa %04x ccw_flags %04x status %04x\n", + FIFO_Num(chsa), chsa, chp->ccw_flags, chp->chan_status); + + /* handle a PPCI here. DC is done and maybe have CC */ + if ((chp->chan_status & STATUS_PCI) && (chp->ccw_flags & FLAG_CC)) { + chp->chan_status &= ~STATUS_PCI; /* done with PCI */ + tstat = chp->chan_status; /* save status */ + tcnt = chp->ccw_count; /* save count */ + chp->chan_status = STATUS_PCI; /* set PCI status */ + chp->ccw_count = 0; /* zero count */ + store_csw(chp); /* store the status */ + chp->chan_status = tstat; /* restore status */ + chp->ccw_count = tcnt; /* restore count */ + sim_debug(DEBUG_EXP, &cpu_dev, + "chan_end done PCI chsa %04x ccw_flags %04x stat %04x cnt %04x\n", + chsa, chp->ccw_flags, tstat, tcnt); + } + + /* If channel end, check if we should continue */ + if (chp->ccw_flags & FLAG_CC) { /* command chain flag */ + /* we have channel end and CC flag, continue channel prog */ + sim_debug(DEBUG_CMD, &cpu_dev, + "chan_end chan end & CC chsa %04x status %04x\n", + chsa, chp->chan_status); + if (chp->chan_status & STATUS_DEND) { /* device end? */ + sim_debug(DEBUG_EXP, &cpu_dev, + "chan_end dev end & CC chsa %04x status %04x IOCLA %08x\n", + chsa, chp->chan_status, chp->chan_caw); + /* Queue us to continue from cpu level */ + chp->chan_byte = BUFF_NEXT; /* have main pick us up */ + sim_debug(DEBUG_EXP, &cpu_dev, + "chan_end set RDYQ %04x Have CC BUFF_NEXT chp %p chan_byte %04x\n", + chsa, chp, chp->chan_byte); + if (cont_chan(chsa)) { /* continue processing channel */ + sim_debug(DEBUG_EXP, &cpu_dev, "call cont_chan returns not OK\n"); + } + } + /* just return */ + goto goout; + } else { + /* we have channel end and no CC flag, continue channel prog */ + UNIT *uptr = chp->unitptr; /* get the unit ptr */ + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->u3); + int unit = (uptr-dptr->units); /* get the UNIT number */ + DIB* dibp = (DIB *)dptr->ctxt; /* get the DIB pointer */ + IOCLQ *qp = &dibp->ioclq_ptr[unit]; /* IOCLQ pointer */ + uint32 iocla; + + /* we have channel end and no CC flag, end this iocl command */ + sim_debug(DEBUG_CMD, &cpu_dev, + "chan_end chan end & no CC chsa %04x status %04x cmd %02x\n", + chsa, chp->chan_status, chp->ccw_cmd); + + /* we have completed channel program */ + /* handle case where we are loading the O/S on boot */ + /* if loading, store status to be discovered by scan_chan */ + if (!loading) { + sim_debug(DEBUG_EXP, &cpu_dev, + "chan_end call store_csw dev/chan end chsa %04x cpustat %08x iocla %08x\n", + chsa, CPUSTATUS, chp->chan_caw); + } else { + /* we are loading, so keep moving */ + sim_debug(DEBUG_EXP, &cpu_dev, + "chan_end we are loading O/S with DE & CE, keep status chsa %04x status %08x\n", + chsa, chp->chan_status); + } + /* store the status in channel FIFO to continue from cpu level */ + chp->chan_byte = BUFF_DONE; /* we are done */ + store_csw(chp); /* store the status */ + /* change chan_byte to BUFF_POST */ + chp->chan_byte = BUFF_POST; /* show done with data */ + chp->ccw_cmd = 0; /* no command anymore */ + + if (chp->chan_status & STATUS_ERROR) { /* check channel error status */ + qp = &dibp->ioclq_ptr[unit]; /* IOCLQ pointer */ + /* we have an error, so delete all other IOCLQ entries */ + while ((dibp->ioclq_ptr != NULL) && (qp != NULL) && IOCLQ_Get(qp, &iocla) == SCPE_OK) { + sim_debug(DEBUG_EXP, &cpu_dev, + "$$ CHEND removed IOCL from IOCLQ processing chsa %04x iocla %06x\n", + chsa, iocla); + } + chp->chan_status = 0; /* no channel status yet */ + } else + /* no error, see if we have a queued IOCL to start */ + /* This causes an error for hsdp where we just finished the I/O */ + /* but the status has not been posted yet nor the interrupt */ + /* starting another I/O confuses the scan_chan code and ends up */ + /* doing an extra interrupt for UTX 05/21/2021 */ + if ((dibp->ioclq_ptr != NULL) && (qp != NULL) && IOCLQ_Get(qp, &iocla) == SCPE_OK) { + /* channel not busy and ready to go, so start a new command */ + chp->chan_status = 0; /* no channel status yet */ + chp->chan_caw = iocla; /* get iocla address in memory */ + /* added 09/25/20 to fix hangs in iocl processing */ + chp->ccw_flags = 0; /* clear flags */ + + /* set status words in memory to first IOCD information */ + sim_debug(DEBUG_CMD, &cpu_dev, + "$$ CHEND start IOCL processing from IOCLQ num %02x chsa %04x iocla %06x\n", + IOCLQ_Num(qp), chsa, iocla); + + /* We are queueing the SIO */ + /* Queue us to continue IOCL from cpu level & make busy */ + chp->chan_byte = BUFF_NEXT; /* have main pick us up */ + chp->chan_info = INFO_SIOCD; /* show first IOCD in channel prog */ + sim_debug(DEBUG_CMD, &cpu_dev, + "chan_end BUFF_NEXT chsa %04x from IOCLQ cnt %02x chp %p chan_byte %04x\n", + chsa, IOCLQ_Num(qp), chp, chp->chan_byte); + // FIXME - need to call iocl processing here */ + if (cont_chan(chsa)) { /* continue processing channel */ + sim_debug(DEBUG_EXP, &cpu_dev, "call cont_chan returns not OK\n"); + } + sim_debug(DEBUG_CMD, &cpu_dev, + "CHEND SIOQ queued chsa %04x iocla %06x IOCD1 %08x IOCD2 %08x\n", + chsa, iocla, RMW(iocla), RMW(iocla+4)); + + } + } + } +goout: + sim_debug(DEBUG_CMD, &cpu_dev, + "chan_end done chsa %04x status %08x chan_byte %02x\n", + chsa, chp->chan_status, chp->chan_byte); + /* following statement required for boot to work */ + irq_pend = 1; /* flag to test for int condition */ +} + +/* post the device status from the channel FIFO into memory */ +/* the INCH command provides the status DW address in memory */ +/* rstat are the bits to remove from status */ +int16 post_csw(CHANP *chp, uint32 rstat) +{ + uint32 chsa = chp->chan_dev; /* get ch/sa information */ + uint32 incha = chp->chan_inch_addr; /* get inch status buffer address */ + uint32 sw1, sw2; /* status words */ + + irq_pend = 1; /* flag to test for int condition */ + /* check channel FIFO for status to post */ + if ((FIFO_Num(chsa)) && + ((FIFO_Get(chsa, &sw1) == 0) && (FIFO_Get(chsa, &sw2) == 0))) { + /* get chan_icb address */ + uint32 chan_icb = RMW(SPAD[0xf1] + (chp->chan_int<<2)); + + if (chan_icb == 0) { + sim_debug(DEBUG_EXP, &cpu_dev, + "post_csw %04x READ FIFO #%1x inch %06x invalid chan_icb %06x\n", + chsa, FIFO_Num(chsa), incha, chan_icb); + return 0; /* no status to post */ + } + if (chp->chan_byte != BUFF_POST) { + sim_debug(DEBUG_EXP, &cpu_dev, + "post_csw %04x CHP %p not BUFF_POST byte %04x ERROR FIFO #%1x inch %06x icb %06x\n", + chsa, chp, chp->chan_byte, FIFO_Num(chsa), incha, chan_icb); + } + /* remove user specified bits */ + sw2 &= ~rstat; /* remove bits */ + /* we have status to post, do it now */ + /* save the status double word to memory */ + /* if bit 0 of sw2 is set (STATUS_ECHO), post inch addr 0 with bit 0 set */ + if (sw2 & BIT0) { /* see if only not busy post */ + WMW(chan_icb+20, 0x80000000); /* post sw addr 0 in ICB+5w & reset CCs */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "post_csw %04x READ0 FIFO #%1x inch 0x80000000 chan_icb %06x sw1 %08x sw2 %08x\n", + chsa, FIFO_Num(chsa), chan_icb, sw1, sw2); + } else { + sim_debug(DEBUG_IRQ, &cpu_dev, + "post_csw %04x B4READ1 icb+16 %08x icb+20 %08x inch %06x chan_icb %06x\n", + chsa, RMW(chan_icb+16), RMW(chan_icb+20), incha, chan_icb); + WMW(incha, sw1); /* save sa & IOCD address in status WD 1 loc */ + WMW(incha+4, sw2); /* save status and residual cnt in status WD 2 loc */ + /* now store the status dw address into word 5 of the ICB for the channel */ + WMW(chan_icb+20, incha|BIT1); /* post sw addr in ICB+5w & set CC2 in INCH addr */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "post_csw %04x READ1 FIFO #%1x inch %06x chan_icb %06x sw1 %08x sw2 %08x\n", + chsa, FIFO_Num(chsa), incha, chan_icb, sw1, sw2); + if ((incha + 8) > chp->max_inch_addr) /* see if next inch addr OK */ + chp->chan_inch_addr = chp->base_inch_addr; /* reset to first inch addr */ + } + return 1; /* show we posted status */ + } + sim_debug(DEBUG_DETAIL, &cpu_dev, + "post_csw %04x chp %p READ FIFO #%1x inch %06x No Status chan_byte %02x\n", + chsa, chp, FIFO_Num(chsa), incha, chp->chan_byte); + return 0; /* no status to post */ +} + +/* store the device status into the status FIFO for the channel */ +void store_csw(CHANP *chp) +{ + uint32 stwd1, stwd2; /* words 1&2 of stored status */ + uint32 chsa = chp->chan_dev; /* get ch/sa information */ + + /* put sub address in byte 0 */ + stwd1 = ((chsa & 0xff) << 24) | chp->chan_caw; /* subaddress and IOCD address to SW 1 */ + + /* save 16 bit channel status and residual byte count in SW 2 */ + stwd2 = ((uint32)chp->chan_status << 16) | ((uint32)chp->ccw_count); + + if ((FIFO_Put(chsa, stwd1) == -1) || (FIFO_Put(chsa, stwd2) == -1)) { + sim_debug(DEBUG_EXP, &cpu_dev, + "store_csw FIFO Overflow ERROR on chsa %04x\n", chsa); + } + sim_debug(DEBUG_XIO, &cpu_dev, + "store_csw FIFO #%1x write chsa %04x sw1 %08x sw2 %08x incha %08x cmd %02x\n", + FIFO_Num(chsa), chsa, stwd1, stwd2, chp->chan_inch_addr, chp->ccw_cmd); + /* added 011321 */ + /* removed 112421 */ +// INTS[chp->chan_int] |= INTS_REQ; /* request an interrupt for channel */ + irq_pend = 1; /* wakeup controller */ +} + +/* store the device status into the first entry of the status FIFO for the channel */ +void push_csw(CHANP *chp) +{ + int32 stwd1, stwd2; /* words 1&2 of stored status */ + uint32 chsa = chp->chan_dev; /* get ch/sa information */ + + /* put sub address in byte 0 */ + stwd1 = ((chsa & 0xff) << 24) | chp->chan_caw; /* subaddress and IOCD address to SW 1 */ + + /* save 16 bit channel status and residual byte count in SW 2 */ + stwd2 = ((uint32)chp->chan_status << 16) | ((uint32)chp->ccw_count); + + /* Push in reverse order to allign status correctly */ + if ((FIFO_Push(chsa, stwd2) == -1) || (FIFO_Push(chsa, stwd1) == -1)) { + sim_debug(DEBUG_EXP, &cpu_dev, + "push_csw FIFO Overflow ERROR on chsa %04x\n", chsa); + } + sim_debug(DEBUG_XIO, &cpu_dev, + "push_csw FIFO #%1x write chsa %04x sw1 %08x sw2 %08x incha %08x cmd %02x\n", + FIFO_Num(chsa), chsa, stwd1, stwd2, chp->chan_inch_addr, chp->ccw_cmd); + /* added 011321 */ + /* removed 112421 */ +// INTS[chp->chan_int] |= INTS_REQ; /* request an interrupt for channel */ + irq_pend = 1; /* wakeup controller */ +} + +/* check an XIO operation */ +/* logical chan channel number 0-7f */ +/* suba unit address within channel 0-ff */ +/* Condition codes to return 0-f as specified above */ +t_stat checkxio(uint16 lchsa, uint32 *status) { + DIB *dibp; /* device information pointer */ + UNIT *uptr; /* pointer to unit in channel */ + CHANP *chp; /* channel program pointer */ + uint16 lchan = get_chan(lchsa); /* get the logical channel number */ + DEVICE *dptr; /* DEVICE pointer */ + uint32 inta; + uint32 spadent; + uint16 rchan, rchsa; /* the real channel number */ + + /* get the device entry for the logical channel in SPAD */ + spadent = SPAD[lchan]; /* get spad device entry for logical channel */ + rchan = (spadent & 0x7f00) >> 8; /* get real channel */ + rchsa = (rchan << 8) | (lchsa & 0xff); /* get the read chan & suba */ + + dibp = dib_chan[rchan]; /* get DIB pointer for channel */ + if (dibp == 0) goto nothere; + + chp = dibp->chan_prg; /* find the chanp pointer */ + if (chp == 0) goto nothere; + + uptr = dibp->units; /* find pointer to 1st unit on channel */ + if (uptr == 0) { /* if no dib or unit ptr, CC3 on return */ +nothere: + *status = CC3BIT; /* not found, so CC3 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "checkxio lchsa %04x rchan %04x is not found, CC3 return\n", lchsa, rchan); + return SCPE_OK; /* not found, CC3 */ + } + inta = ((~spadent)>>16)&0x7f; /* get channel interrupt level */ + chp->chan_int = inta; /* make sure it is set in channel */ + dptr = get_dev(uptr); /* pointer to DEVICE structure */ + + /* is device or unit marked disabled? */ + if ((dptr->flags & DEV_DIS) || ((uptr->flags & UNIT_DIS) && + ((uptr->flags & UNIT_SUBCHAN) == 0))) { + /* is device/unit disabled? */ + /* UTX wants CC1 on "mt offline" call. If not, UTX loops forever */ + if ((dptr != NULL) && + (DEV_TYPE(dptr) == DEV_TAPE)) { /* see if this is a tape */ + *status = CC1BIT; /* CCs = 1, not busy */ + sim_debug(DEBUG_EXP, &cpu_dev, + "checkxio rchsa %04x device/unit not enabled, CC1 returned\n", + rchsa); + } else { + *status = CC3BIT; /* not attached, so error CC3 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "checkxio rchsa %04x device/unit not enabled, CC3 returned\n", + rchsa); + } + return SCPE_OK; /* Not found CC3/CC1 */ + } + + /* try this as MFP says it returns 0 on OK */ + if (dptr->flags & DEV_CHAN) + *status = 0; /* CCs = 0, OK return */ + else + /* return CC1 for non iop/mfp devices */ + *status = 0; /* CCs = 0, OK return */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "checkxio lchsa %04x rchsa %04x done CC status %08x\n", + lchsa, rchsa, *status); + return SCPE_OK; /* No CC's all OK */ +} + +/* SIO CC status returned to caller */ +/* val condition */ +/* 0 command accepted, will echo status - no CC's */ +/* 1 channel busy - CC4 */ +/* 2 channel inop or undefined (operator intervention required) - CC3 */ +/* 3 sub channel busy CC3 + CC4 */ +/* 4 status stored - CC2 */ +/* 5 unsupported transaction CC2 + CC4 */ +/* 6 unassigned CC2 + CC3 */ +/* 7 unassigned CC2 + CC3 + CC4 */ +/* 8 command accepted/queued, no echo status - CC1 */ +/* 9 unassigned */ +/* a unassigned */ +/* b unassigned */ +/* c unassigned */ +/* d unassigned */ +/* e unassigned */ +/* f unassigned */ + +/* start an XIO operation */ +/* when we get here the cpu has verified that there is a valid channel address */ +/* and an interrupt entry in spad for the channel. The IOCL address in the ICB */ +/* has also been verified as present */ +/* chan channel number 0-7f */ +/* suba unit address within channel 0-ff */ +/* Condition codes to return 0-f as specified above */ +t_stat startxio(uint16 lchsa, uint32 *status) { + DIB *dibp; /* device information pointer */ + UNIT *uptr; /* pointer to unit in channel */ + uint32 chan_icb; /* Interrupt level context block address */ + uint32 iocla; /* I/O channel IOCL address int ICB */ + int32 stat; /* return status 0/1 from loadccw */ + CHANP *chp; /* channel program pointer */ + uint16 lchan = get_chan(lchsa); /* get the logical channel number */ + uint16 chsa; + uint32 tempa, inta, spadent, chan, incha; + uint32 word1, word2, cmd; + DEVICE *dptr; + + /* get the device entry for the logical channel in SPAD */ + spadent = SPAD[lchan]; /* get spad device entry for logical channel */ + inta = ((~spadent)>>16)&0x7f; /* get interrupt level */ + chan = (spadent & 0x7f00) >> 8; /* get real channel */ + chsa = (chan << 8) | (lchsa & 0xff); /* merge sa to real channel */ + sim_debug(DEBUG_XIO, &cpu_dev, + "startxio entry inta %02x lchan %04x spadent %08x rchsa %04x\n", + inta, lchan, spadent, chsa); + + dibp = dib_unit[chsa & 0x7f00]; /* get the device information pointer */ + uptr = find_unit_ptr(chsa & 0x7f00); /* get unit 0 unit pointer */ + chan_icb = find_int_icb(lchsa); /* Interrupt level context block address */ + incha = RMW(chan_icb+20); /* post inch addr in ICB+5w */ + + /* check if we have a valid unit */ + chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + if (chp == 0) goto missing; + + dibp = dib_unit[chsa]; /* get the DIB pointer */ + if (dibp == 0) goto missing; + + uptr = find_unit_ptr(chsa); /* find pointer to unit on channel */ + + if (uptr == 0) { /* if no dib or unit ptr, CC3 on return */ +missing: + *status = CC3BIT; /* not found, so CC3 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "startxio chsa %04x is not found, CC3 returned\n", chsa); + return SCPE_OK; /* not found, CC3 */ + } + + inta = ((~spadent)>>16)&0x7f; /* get channel interrupt level */ + chp->chan_int = inta; /* make sure it is set in channel */ + + sim_debug(DEBUG_EXP, &cpu_dev, + "startxio chsa %04x chp %p flags UNIT_ATTABLE %1x UNIT_ATT %1x UNIT_DIS %1x\n", + chsa, chp, (uptr->flags & UNIT_ATTABLE)?1:0, (uptr->flags & UNIT_ATT)?1:0, + (uptr->flags & UNIT_DIS)?1:0); + + /* is device or unit marked disabled? */ + dptr = get_dev(uptr); + if ((dptr->flags & DEV_DIS) || ((uptr->flags & UNIT_DIS) && + ((uptr->flags & UNIT_SUBCHAN) == 0))) { + sim_debug(DEBUG_EXP, &cpu_dev, + "startxio chsa %04x device/unit disabled, CC3 returned flags %08x\n", chsa, uptr->flags); + *status = CC3BIT; /* not attached, so error CC3 */ + return SCPE_OK; /* not found, CC3 */ + } + +#ifndef FOR_DEBUG_01172021 + if ((INTS[inta]&INTS_ACT) || (SPAD[inta+0x80]&SINT_ACT)) { /* look for level active */ + /* just output a warning */ + sim_debug(DEBUG_XIO, &cpu_dev, + "SIOT Busy INTS ACT FIFO #%1x irq %02x SPAD %08x INTS %08x chan_byte %02x\n", + FIFO_Num(SPAD[inta+0x80] & 0x7f00), inta, SPAD[inta+0x80], INTS[inta], chp->chan_byte); + } +#endif + + incha = chp->chan_inch_addr; /* get inch address */ + /* 05122021 cpu halts in diag if this code is enabled */ + /* disabling this code allows TE to be echoed at debugger prompt */ +#ifndef TEST_FOR_HSDP_PUT_BACK_05122021 + /* channel not busy and ready to go, check for any status ready */ + /* see if any status ready to post */ + if (FIFO_Num(chsa&0x7f00)) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "SIOT chsa %04x LOOK FIFO #%1x irq %02x inch %06x chp %p icba %06x chan_byte %02x\n", + chsa, FIFO_Num(chsa), inta, incha, chp, chan_icb, chp->chan_byte); + if (post_csw(chp, 0)) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "SIOT chsa %04x POST FIFO #%1x irq %02x inch %06x chan_icba+20 %08x chan_byte %02x\n", + chsa, FIFO_Num(chsa), inta, incha, RMW(chan_icb+20), chp->chan_byte); + /* change status from BUFF_POST to BUFF_DONE */ + /* if not BUFF_POST we have a PPCI or channel busy interrupt */ + /* so leave the channel status alone */ + if (chp->chan_byte == BUFF_POST) { + chp->chan_byte = BUFF_DONE; /* show done & not busy */ + } + sim_debug(DEBUG_XIO, &cpu_dev, + "SIOT END status stored incha %06x chan_icba+20 %08x chsa %04x sw1 %08x sw2 %08x\n", + incha, RMW(chan_icb+20), chsa, RMW(incha), RMW(incha+4)); + INTS[inta] &= ~INTS_REQ; /* clear level request for no status */ +// INTS[inta] |= INTS_REQ; /* set level request if no status */ + *status = CC2BIT; /* status stored from SIO, so CC2 */ + return SCPE_OK; /* No CC's all OK */ + } else { + sim_debug(DEBUG_IRQ, &cpu_dev, + "SIOT chsa %04x NOT POSTED FIFO #%1x irq %02x inch %06x chan_icba %06x chan_byte %02x\n", + chsa, FIFO_Num(chsa), inta, incha, chan_icb, chp->chan_byte); + /* now store the status dw address into word 5 of the ICB for the channel */ + WMW(chan_icb+20, 0); /* post sw addr 0 in ICB+5w & reset CCs */ + *status = 0; /* no status stored from SIO, so no CC */ + return SCPE_OK; /* No CC's all OK */ + } + } +//TRY WMW(chan_icb+20, 0); /* post sw addr 0 in ICB+5w & reset CCs */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "SIOT chsa %04x Nothing to post FIFO #%1x irq %02x inch %06x chan_icba %06x chan_byte %02x\n", + chsa, FIFO_Num(chsa), inta, incha, chan_icb, chp->chan_byte); +#endif + + /* check for a Command or data chain operation in progresss */ + if ((chp->chan_byte & BUFF_BUSY) && (chp->chan_byte != BUFF_POST)) { + uint16 tstat = chp->chan_status; /* save status */ + uint16 tcnt = chp->ccw_count; /* save count */ + DEVICE *dptr = get_dev(uptr); + + sim_debug(DEBUG_EXP, &cpu_dev, + "startxio busy return CC3&CC4 chsa %04x chp %p cmd %02x flags %04x byte %02x\n", + chsa, chp, chp->ccw_cmd, chp->ccw_flags, chp->chan_byte); + /* ethernet controller wants an interrupt for busy status */ + if ((dptr != NULL) && + (DEV_TYPE(dptr) == DEV_ETHER)) { /* see if this is ethernet */ + *status = CC1BIT; /* CCs = 1, SIO accepted & queued, no echo status */ + /* handle an Ethernet controller busy by sending interrupt/status */ + chp->chan_status = STATUS_BUSY|STATUS_CEND|STATUS_DEND; /* set busy status */ + chp->ccw_count = 0; /* zero count */ + push_csw(chp); /* store the status */ + chp->chan_status = tstat; /* restore status */ + chp->ccw_count = tcnt; /* restore count */ + sim_debug(DEBUG_XIO, &cpu_dev, + "startxio done BUSY/INT chp %p chsa %04x ccw_flags %04x stat %04x cnt %04x\n", + chp, chsa, chp->ccw_flags, tstat, tcnt); + return SCPE_OK; /* just busy CC3&CC4 */ + } + /* see if controller has a IOCLQ defined to handle multiple SIO requests */ + /* keep processing SIO and handle busy later */ + if (dibp->ioclq_ptr == NULL) { /* see if device has IOCL queue */ + /* everyone else just gets a busy return */ + *status = CC4BIT|CC3BIT; /* busy, so CC3&CC4 */ + sim_debug(DEBUG_XIO, &cpu_dev, + "startxio done2 BUSY chp %p chsa %04x ccw_flags %04x stat %04x cnt %04x\n", + chp, chsa, chp->ccw_flags, tstat, tcnt); + return SCPE_OK; /* just busy CC3&CC4 */ + } + sim_debug(DEBUG_EXP, &cpu_dev, + "startxio busy ignored for IOCLQ chsa %04x chp %p cmd %02x flags %04x byte %02x\n", + chsa, chp, chp->ccw_cmd, chp->ccw_flags, chp->chan_byte); + } + +#if 1 + sim_debug(DEBUG_XIO, &cpu_dev, + "startxio int spad %08x icb %06x inta %02x chan %04x\n", + SPAD[inta+0x80], chan_icb, inta, chan); +#endif + + /* We have to validate all the addresses and parameters for the SIO */ + /* before calling load_ccw which does it again for each IOCL step */ + iocla = RMW(chan_icb+16); /* iocla is in wd 4 of ICB */ + word1 = RMW(iocla & MASK24); /* get 1st IOCL word */ + word2 = RMW((iocla + 4) & MASK24); /* get 2nd IOCL word */ + cmd = (word1 >> 24) & 0xff; /* get channel cmd from IOCL */ + chp = find_chanp_ptr(chsa&0x7f00); /* find the parent chanp pointer */ + incha = chp->chan_inch_addr; /* get inch address */ + + sim_debug(DEBUG_XIO, &cpu_dev, + "startxio do normal chsa %04x iocla %06x incha %06x IOCD1 %08x IOCD2 %08x\n", + chsa, iocla, incha, RMW(iocla), RMW(iocla+4)); + + chp = find_chanp_ptr(chsa); /* find the chanp pointer */ +#ifdef TEST_FOR_IOCL_CHANGE + chp->new_iocla = iocla; /* save iocla */ + chp->new_iocd1 = word1; /* save iocd word 1 */ + chp->new_iocd2 = word2; /* save iocd word 2 */ +#endif + sim_debug(DEBUG_XIO, &cpu_dev, + "startxio test chsa %04x iocla %06x IOCD1 %08x IOCD2 %08x\n", + chsa, iocla, RMW(iocla), RMW(iocla+4)); + + sim_debug(DEBUG_CMD, &cpu_dev, "SIO chsa %04x cmd %02x cnt %04x ccw_flags %04x\n", + chsa, cmd, word2&MASK16, word2>>16); + + /* determine if channel DIB has a pre startio command processor */ + if (dibp->pre_io != NULL) { /* NULL if no startio function */ + DEVICE *dptr = get_dev(uptr); /* get device ptr */ + int unit = uptr-dptr->units; /* get unit number */ + + /* call the device controller to get prestart_io status */ + tempa = dibp->pre_io(uptr, chan); /* get status from device */ + /* SCPE_OK if unit not busy and IOCLQ is not full */ + /* SNS_BSY if unit IOCLQ is full */ + /* SNS_SMS if unit IOCLQ is not full, but device is busy */ + /* SNS_CTLEND if waiting for INCH command */ + if (tempa == SNS_CTLEND) { /* see if sub channel status is ready */ + /* manual says to just return OK nad do nother if inch is required */ + sim_debug(DEBUG_XIO, &cpu_dev, + "SIO pre_io call return NO INCH %04x chsa %04x cstat %02x cmd %02x cnt %02x\n", + incha, chsa, tempa, cmd, word2); + if ((cmd != 0) || ((MASK16 & word2) == 0)) { + *status = 0; /* request accepted, no status, so CC1 */ + return SCPE_OK; /* just do nothing until inch */ + } + } + if (tempa == SNS_BSY) { /* see if sub channel status is ready */ + /* The device must be busy or something, but it is not ready. Return busy */ + sim_debug(DEBUG_XIO, &cpu_dev, + "startxio pre_io call return busy1 chan %04x cstat %08x\n", chan, tempa); + *status = CC3BIT|CC4BIT; /* sub channel busy, so CC3|CC4 */ + return SCPE_OK; /* just busy or something, CC3|CC4 */ + } + if (tempa == SNS_SMS) { /* see if sub channel status is ready */ + if (dibp->ioclq_ptr == NULL) { /* see if device has IOCL queue */ + /* The device must be busy or something, but it is not ready. Return busy */ + /* This should not happen for SNS_SMS status */ + sim_debug(DEBUG_XIO, &cpu_dev, + "startxio pre_io call return busy2 chan %04x cstat %08x\n", chan, tempa); + *status = CC3BIT|CC4BIT; /* sub channel busy, so CC3|CC4 */ + return SCPE_OK; /* just busy or something, CC3|CC4 */ + } + /* device has IOCLQ, queue up the iocla */ + if (IOCLQ_Put(&dibp->ioclq_ptr[unit], iocla) == -1) { + sim_debug(DEBUG_XIO, &cpu_dev, + "startxio IOCLQ_Put error return chsa %04x unit %02x\n", chsa, unit); + *status = CC3BIT|CC4BIT; /* sub channel busy, so CC3|CC4 */ + return SCPE_OK; /* just busy or something, CC3|CC4 */ + } + sim_debug(DEBUG_XIO, &cpu_dev, + "startxio IOCLQ_Put call sucessful count %02x chan %04x unit %02x\n", + IOCLQ_Num(&dibp->ioclq_ptr[unit]), chan, unit); + *status = CC1BIT; /* CCs = 1, SIO accepted & queued, no echo status */ + return SCPE_OK; /* CC1 all OK */ + } + /* device is not busy */ + sim_debug(DEBUG_XIO, &cpu_dev, + "startxio pre_io call return not busy chan %04x cstat %08x\n", + chan, tempa); + } /* remove else 05132021 */ + + /* check for a Command or data chain operation in progresss */ + if ((chp->chan_byte & BUFF_BUSY) && (chp->chan_byte != BUFF_POST)) { + uint16 tstat = chp->chan_status; /* save status */ + uint16 tcnt = chp->ccw_count; /* save count */ + + sim_debug(DEBUG_EXP, &cpu_dev, + "startxio busy return CC3&CC4 chsa %04x chp %p cmd %02x flags %04x byte %02x\n", + chsa, chp, chp->ccw_cmd, chp->ccw_flags, chp->chan_byte); + /* everyone else just gets a busy return */ + *status = CC4BIT|CC3BIT; /* busy, so CC3&CC4 */ + sim_debug(DEBUG_XIO, &cpu_dev, + "startxio done BUSY chp %p chsa %04x ccw_flags %04x stat %04x cnt %04x\n", + chp, chsa, chp->ccw_flags, tstat, tcnt); + return SCPE_OK; /* just busy CC3&CC4 */ + } + + /* channel not busy and ready to go, so start a new command */ + chp->chan_int = inta; /* save interrupt level in channel */ + chp->chan_status = 0; /* no channel status yet */ + chp->chan_caw = iocla; /* get iocla address in memory */ + /* added 09/25/20 to fix hangs in iocl processing */ + chp->ccw_flags = 0; /* clear flags */ + + /* set status words in memory to first IOCD information */ + sim_debug(DEBUG_XIO, &cpu_dev, + "$$ SIO start IOCL processing chsa %04x iocla %08x incha %08x\n", + chsa, iocla, incha); + + /* We are queueing the SIO */ + /* Queue us to continue IOCL from cpu level & make busy */ + chp->chan_byte = BUFF_NEXT; /* have main pick us up */ + chp->chan_info |= INFO_SIOCD; /* show first IOCD in channel prog */ + chp->chan_info &= ~INFO_CEND; /* show chan_end not called yet */ + + /* start processing the IOCL */ + stat = load_ccw(chp, 0); /* start the I/O operation */ + if (stat) { + /* we have an error or user requested interrupt, return status */ + sim_debug(DEBUG_EXP, &cpu_dev, "startxio store csw CC2 chan %04x status %08x\n", + chan, chp->chan_status); +/*NOTE*//* if we have an error, we would loop forever if the CC bit was set */ + /* the only way to stop was to do a kill */ + chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* reset chaining bits */ + /* DIAG's want CC1 with memory access error */ + if (chp->chan_status & STATUS_PCHK) { + chp->chan_status &= ~STATUS_LENGTH; /* clear incorrect length */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* show I/O complete */ + sim_debug(DEBUG_EXP, &cpu_dev, + "startxio Error1 FIFO #%1x store_csw CC1 chan %04x status %08x\n", + FIFO_Num(chsa), chan, chp->chan_status); + } else { + /* other error, stop the show */ + chp->chan_status &= ~STATUS_PCI; /* remove PCI status bit */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* show I/O complete */ + sim_debug(DEBUG_EXP, &cpu_dev, + "startxio Error2 FIFO #%1x store_csw CC1 chan %04x status %08x\n", + FIFO_Num(chsa), chan, chp->chan_status); + } + /* we get here when the start cmd has been processed without error */ + /* go wait for the cmd to finish */ + sim_debug(DEBUG_XIO, &cpu_dev, + "startxio start wait chsa %04x status %08x iocla %06x byte %02x\n", + chsa, chp->chan_status, chp->chan_caw, chp->chan_byte); + } + sim_debug(DEBUG_XIO, &cpu_dev, + "SIO started chsa %04x iocla %06x IOCD1 %08x IOCD2 %08x incha %06x icb+20 %08x\n", + chsa, iocla, RMW(iocla), RMW(iocla+4), incha, RMW(chan_icb+20)); + + *status = CC1BIT; /* CCs = 1, SIO accepted & queued, no echo status */ + sim_debug(DEBUG_XIO, &cpu_dev, "SIO return chsa %04x status %08x iocla %08x CC's %08x byte %02x\n", + chsa, chp->chan_status, iocla, *status, chp->chan_byte); + return SCPE_OK; /* No CC's all OK */ +} + +/* TIO - I/O status */ +t_stat testxio(uint16 lchsa, uint32 *status) { /* test XIO */ + DIB *dibp; /* device information pointer */ + UNIT *uptr; /* pointer to unit in channel */ + uint32 chan_icb; /* Interrupt level context block address */ + CHANP *chp; /* Channel prog pointers */ + DEVICE *dptr; /* Device ptr */ + uint32 inta, incha, itva; + uint16 lchan = get_chan(lchsa); /* get the logical channel number */ + uint32 spadent; + uint16 rchan, rchsa; /* the real channel number, chsa */ + + lchsa &= 0x7f00; /* use just chan and sa of 0 */ + /* get the real channel entry for the logical channel in SPAD */ + spadent = SPAD[lchan]; /* get spad device entry for logical channel */ + rchsa = (spadent & 0x7f00); /* get real channel suba of zero */ + rchan = rchsa >> 8; /* get real channel */ + + /* get the device entry for the channel in SPAD */ + dibp = dib_chan[rchan]; /* get the DIB pointer */ + chp = find_chanp_ptr(rchan << 8); /* find the device chanp pointer */ + + if (dibp == 0 || chp == 0) { /* if no dib or channel ptr, CC3 return */ + *status = CC3BIT; /* not found, so CC3 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "TIO lchsa %04x rchsa %04x device not present, CC3 returned\n", lchsa, rchsa); + return SCPE_OK; /* Not found, CC3 */ + } + + uptr = chp->unitptr; /* get the unit 0 ptr */ + /* is device or unit marked disabled? */ + dptr = get_dev(uptr); + if ((dptr->flags & DEV_DIS) || ((uptr->flags & UNIT_DIS) && + ((uptr->flags & UNIT_SUBCHAN) == 0))) { + /* is device/unit disabled? */ + *status = CC3BIT; /* not enabled, so error CC3 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "TIO rchsa %04x device/unit not enabled, CC3 returned\n", rchsa); + return SCPE_OK; /* Not found, CC3 */ + } + + /* the XIO opcode processing software has already checked for F class */ + inta = ((~spadent)>>16)&0x7f; /* get channel interrupt level */ + chp->chan_int = inta; /* make sure it is set in channel */ + itva = SPAD[0xf1] + (inta<<2); /* int vector address */ + chan_icb = RMW(itva); /* Interrupt context block addr */ + sim_debug(DEBUG_XIO, &cpu_dev, + "TIO int spad %08x icb %06x inta %04x rchsa %04x\n", + SPAD[inta+0x80], chan_icb, inta, rchsa); + + incha = chp->chan_inch_addr; /* get inch address */ + + /* see if any status ready to post */ + if (FIFO_Num(rchsa)) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "TIO rchsa %04x LOOK FIFO #%1x irq %02x inch %06x chp %p icba %06x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, incha, chp, chan_icb, chp->chan_byte); + if (chp->chan_byte == BUFF_DONE) { + chp->chan_byte = BUFF_POST; /* if done, show post for post_csw() */ + } + if (post_csw(chp, 0)) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "TIO rchsa %04x POST FIFO #%1x irq %02x inch %06x chan_icba+20 %08x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, incha, RMW(chan_icb+20), chp->chan_byte); + /* change status from BUFF_POST to BUFF_DONE */ + /* if not BUFF_POST we have a PPCI or channel busy interrupt */ + /* so leave the channel status alone */ + if (chp->chan_byte == BUFF_POST) { + chp->chan_byte = BUFF_DONE; /* show done & not busy */ + } + sim_debug(DEBUG_XIO, &cpu_dev, + "TIO END incha %06x chan_icba+20 %08x rchsa %04x sw1 %08x sw2 %08x\n", + incha, RMW(chan_icb+20), rchsa, RMW(incha), RMW(incha+4)); + INTS[inta] &= ~INTS_REQ; /* clear any level request if no status */ + *status = CC2BIT; /* status stored from SIO, so CC2 */ + return SCPE_OK; /* No CC's all OK */ + } else { + sim_debug(DEBUG_IRQ, &cpu_dev, + "TIO rchsa %04x NOT POSTED FIFO #%1x irq %02x inch %06x chan_icba %06x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, chp->chan_inch_addr, chan_icb, chp->chan_byte); + /* now store the status dw address into word 5 of the ICB for the channel */ + WMW(chan_icb+20, 0); /* post sw addr 0 in ICB+5w & reset CCs */ + *status = 0; /* no status stored from TIO, so no CC */ + return SCPE_OK; /* No CC's all OK */ + } + } + + /* nothing going on, so say all OK */ + /* now store the status dw address into word 5 of the ICB for the channel */ +#ifdef FIXES_DMDIAG_TEST_11C_TIO_BUT_BREAKS_UTX + WMW(chan_icb+20, 0x80000000); /* post CC1 & sw addr 0 in ICB+5w & reset CCs */ + *status = CC4BIT; /* FIX FOR DIAG */ /* request accepted, not busy, so CC4 */ +#else + /* MPX 1X requires CC1 to be returned instead of CC2 or CC4 */ + /* MPX 1X will hang on boot if set to CC2 */ + WMW(chan_icb+20, 0x80000000); /* post sw addr 0 in ICB+5w & reset CCs */ + *status = CC1BIT; /* request accepted, no status, so CC1 */ +#endif +// INTS[inta] &= ~INTS_REQ; /* clear any level request if no status */ + sim_debug(DEBUG_XIO, &cpu_dev, + "TIO END rchsa %04x rchan %04x ccw_flags %04x chan_stat %04x CCs %08x\n", + rchsa, rchan, chp->ccw_flags, chp->chan_status, *status); + return SCPE_OK; /* No CC's all OK */ +} + +/* Stop XIO */ +t_stat stopxio(uint16 lchsa, uint32 *status) { /* stop XIO */ + DIB *dibp; /* device information pointer */ + UNIT *uptr; /* pointer to unit in channel */ + uint32 chan_icb; /* Interrupt level context block address */ + uint32 iocla, inta, itva; /* I/O channel IOCL address int ICB */ + CHANP *chp; /* Channel prog pointers */ + DEVICE *dptr; /* Device ptr */ + uint16 lchan = get_chan(lchsa); /* get the logical channel number */ + uint32 spadent; + uint16 rchan, rchsa; /* the real channel number, chsa */ + + /* get the device entry for the logical channel in SPAD */ + spadent = SPAD[lchan]; /* get spad device entry for logical channel */ + rchan = (spadent & 0x7f00) >> 8; /* get real channel */ + rchsa = (rchan << 8) | (lchsa & 0xff); /* get the read chan & suba */ + + /* get the device entry for the logical channel in SPAD */ + dibp = dib_unit[rchsa]; /* get the DIB pointer */ + chp = find_chanp_ptr(rchsa); /* find the device chanp pointer */ + + if (dibp == 0 || chp == 0) { /* if no dib or channel ptr, CC3 return */ + *status = CC3BIT; /* not found, so CC3 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "STPIO test 1 rchsa %04x device not present, CC3 returned\n", rchsa); + return SCPE_OK; /* not found CC3 */ + } + + uptr = chp->unitptr; /* get the unit ptr */ + /* is device or unit marked disabled? */ + dptr = get_dev(uptr); + if ((dptr->flags & DEV_DIS) || ((uptr->flags & UNIT_DIS) && + ((uptr->flags & UNIT_SUBCHAN) == 0))) { + /* is device/unit disabled? */ + *status = CC3BIT; /* not enabled, so error CC3 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "STPIO rchsa %04x device/unit not enabled, CC3 returned\n", rchsa); + return SCPE_OK; /* Not found, CC3 */ + } + + /* the XIO opcode processing software has already checked for F class */ + inta = ((~spadent)>>16)&0x7f; /* get channel interrupt level */ + chp->chan_int = inta; /* make sure it is set in channel */ + itva = SPAD[0xf1] + (inta<<2); /* int vector address */ + chan_icb = RMW(itva); /* Interrupt context block addr */ + iocla = RMW(chan_icb+16); /* iocla is in wd 4 of ICB */ + sim_debug(DEBUG_CMD, &cpu_dev, + "STPIO busy test rchsa %04x cmd %02x ccw_flags %04x IOCD1 %08x IOCD2 %08x\n", + rchsa, chp->ccw_cmd, chp->ccw_flags, M[iocla>>2], M[(iocla+4)>>2]); + /* reset the CC bit to force completion after current IOCD */ + chp->ccw_flags &= ~FLAG_CC; /* reset chaining bits */ + + /* see if we have a stopio device entry */ + if (dibp->stop_io != NULL) { /* NULL if no stop_io function */ + /* call the device controller to get stop_io status */ + int32 tempa = dibp->stop_io(uptr); /* get status from device */ + + /* test for SCPE_IOERR */ + /* CC's are returned in byte 0, status in bytes 2-3 */ + /* SCPR_OK is 0 */ + /* SCPR_IOERR is 2 */ + if ((tempa & RMASK) != SCPE_OK) { /* sub channel has status ready */ + /* The device I/O has been terminated and status stored. */ + sim_debug(DEBUG_XIO, &cpu_dev, + "STPIO stop_io call return ERROR FIFO #%1x rchan %04x retstat %08x cstat %08x\n", + FIFO_Num(rchsa), rchan, tempa, chp->chan_status); + + /* chan_end is called in stop device service routine */ + /* the device is no longer busy, post status */ + /* remove PPCI status. Unit check should not be set */ + if ((tempa & LMASK) == CC2BIT) { + chp->ccw_count = 0; /* zero the count */ + /* post status for UTX */ + if (post_csw(chp, ((STATUS_PCI) << 16))) { + INTS[inta] &= ~INTS_REQ; /* clear any level request */ + *status = CC2BIT; /* status stored */ + sim_debug(DEBUG_CMD, &cpu_dev, + "STPIO END2 rchsa %04x rchan %04x cmd %02x ccw_flags %04x status %04x\n", + rchsa, rchan, chp->ccw_cmd, chp->ccw_flags, *status); + /* change status from BUFF_POST to BUFF_DONE */ + if (chp->chan_byte == BUFF_POST) { + chp->chan_byte = BUFF_DONE; /* show done & not busy */ + } + return SCPE_OK; /* CC2 & all OK */ + } + } else { + chp->ccw_count = 0; /* zero the count */ + /* The diags want the interrupt for the disk */ + *status = CC1BIT; /* request accepted, no status, so CC1 */ + sim_debug(DEBUG_CMD, &cpu_dev, + "STPIO END2 ECHO rchsa %04x cmd %02x ccw_flags %04x status %04x\n", + rchsa, chp->ccw_cmd, chp->ccw_flags, *status); + return SCPE_OK; /* CC1 & all OK */ + } + } + /* the channel is not busy, so return OK */ + *status = CC1BIT; /* request accepted, no status, so CC1 */ + sim_debug(DEBUG_CMD, &cpu_dev, + "STPIO END3 rchsa %04x cmd %02x ccw_flags %04x status %04x\n", + rchsa, chp->ccw_cmd, chp->ccw_flags, *status); + return SCPE_OK; /* No CC's all OK */ + } + if ((chp->chan_byte & BUFF_BUSY) == 0) { + /* the channel is not busy, so return OK */ + sim_debug(DEBUG_CMD, &cpu_dev, + "STPIO not busy return rchsa %04x cmd %02x ccw_flags %04x status %04x byte %02x\n", + rchsa, chp->ccw_cmd, chp->ccw_flags, *status, chp->chan_byte); + sim_debug(DEBUG_IRQ, &cpu_dev, + "STPIO rchsa %04x NOT POSTED FIFO #%1x irq %02x inch %06x chan_icba %06x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, chp->chan_inch_addr, chan_icb, chp->chan_byte); + /* now store the status dw address into word 5 of the ICB for the channel */ + WMW(chan_icb+20, 0x80000000); /* post sw addr 0 in ICB+5w & set CC 1*/ + *status = CC1BIT; /* show not busy, post no status with CC1 */ + return SCPE_OK; /* No CC's all OK */ + } + + /* device does not have stop_io entry, so stop the I/O */ + /* check for a Command or data chain operation in progresss */ + /* set the return to CC3BIT & CC4BIT causes infinite loop in MPX1X */ + /* restore code to old CC1BIT return 12/21/2020 */ + // try using CC4 on MPX3X when still busy + if (chp->chan_byte == BUFF_POST) { + uint32 incha = chp->chan_inch_addr; /* get inch address */ + *status = CC1BIT; /* request accepted, no status, so CC1 */ + /* see if any status ready to post */ + if (FIFO_Num(rchsa)) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "STPIO chsa %04x LOOK FIFO #%1x irq %02x inch %06x chp %p icba %06x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, incha, chp, chan_icb, chp->chan_byte); + if (post_csw(chp, 0)) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "STPIO chsa %04x POST FIFO #%1x irq %02x inch %06x chan_icba+20 %08x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, incha, RMW(chan_icb+20), chp->chan_byte); + /* change status from BUFF_POST to BUFF_DONE */ + /* if not BUFF_POST we have a PPCI or channel busy interrupt */ + /* so leave the channel status alone */ + chp->chan_byte = BUFF_DONE; /* show done & not busy */ + sim_debug(DEBUG_XIO, &cpu_dev, + "STPIO END incha %06x chan_icba+20 %08x chsa %04x sw1 %08x sw2 %08x\n", + incha, RMW(chan_icb+20), rchsa, RMW(incha), RMW(incha+4)); + INTS[inta] &= ~INTS_REQ; /* clear any level request if no status */ + *status = CC2BIT; /* status stored from SIO, so CC2 */ + return SCPE_OK; /* No CC's all OK */ + } else { + sim_debug(DEBUG_IRQ, &cpu_dev, + "STPIOX chsa %04x NOT POSTED FIFO #%1x irq %02x inch %06x chan_icba %06x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, incha, chan_icb, chp->chan_byte); + /* now store the status dw address into word 5 of the ICB for the channel */ + WMW(chan_icb+20, 0x80000000); /* post CC1 & sw addr 0 in ICB+5w & reset CCs */ +// WMW(chan_icb+20, 0); /* post sw addr 0 in ICB+5w & reset CCs */ + *status = CC1BIT; /* show not busy, post no status with CC1 */ + return SCPE_OK; /* No CC's all OK */ + } + } + } else { + /* setting this to CC4 allows MPX mstrall to boot */ + /* having it set to CC1 allows diags to work, but not MPX 3X boot! */ + // This check allows DBUG2 and DIAGS to both work + if (chp->chan_byte == BUFF_NEXT) +//BAD?? *status = CC1BIT; /* request accepted, no status, so CC1 */ + *status = CC4BIT; /* BAD FOR DIAG *//* request accepted, busy, so CC4 */ + else + *status = CC4BIT; /* BAD FOR DIAG *//* request accepted, busy, so CC4 */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "STPIO2 chsa %04x NOT POSTED FIFO #%1x irq %02x inch %06x chan_icba %06x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, chp->chan_inch_addr, chan_icb, chp->chan_byte); + } + /* reset the CC bit to force completion after current IOCD */ + chp->ccw_flags &= ~FLAG_CC; /* reset chaining bits */ + sim_debug(DEBUG_CMD, &cpu_dev, + "STPIO busy return CC1/4 rchsa %04x status %08x cmd %02x flags %04x byte %02x\n", + rchsa, *status, chp->ccw_cmd, chp->ccw_flags, chp->chan_byte); + return SCPE_OK; /* go wait CC1 */ +} + +/* Reset Channel XIO */ +t_stat rschnlxio(uint16 lchsa, uint32 *status) { /* reset channel XIO */ + DIB *dibp; /* device information pointer */ + UNIT *uptr; /* pointer to unit in channel */ + CHANP *chp; /* Channel prog pointers */ + DEVICE *dptr; /* Device ptr */ + int i; + uint16 lchan = get_chan(lchsa); /* get the logical channel number */ + uint32 inta; + uint32 spadent; + uint16 rchan, rchsa; /* the real channel number */ + + /* get the device entry for the logical channel in SPAD */ + spadent = SPAD[lchan]; /* get spad device entry for logical channel */ + rchan = (spadent & 0x7f00) >> 8; /* get real channel */ + rchsa = rchan << 8; /* get the real chan & zero suba */ + + /* get the device entry for the logical channel in SPAD */ + dibp = dib_unit[rchsa]; /* get the channel device information pointer */ + chp = find_chanp_ptr(rchsa); /* find the channel chanp pointer */ + + if (dibp == 0 || chp == 0) { /* if no dib or channel ptr, CC3 return */ + *status = CC3BIT; /* not found, so CC3 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "rschnlxio test 1 dibp %p chp %p lchsa %04x rchsa %04x device not present, CC3 returned\n", + dibp, chp, lchsa, rchsa); + return SCPE_OK; /* not found CC3 */ + } + + uptr = chp->unitptr; /* get the unit ptr */ + /* is device or unit marked disabled? */ + dptr = get_dev(uptr); + if ((dptr->flags & DEV_DIS) || ((uptr->flags & UNIT_DIS) && + ((uptr->flags & UNIT_SUBCHAN) == 0))) { + /* is device/unit disabled? */ + *status = CC3BIT; /* not enabled, so error CC3 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "RSCHNL rchsa %04x device/unit not enabled, CC3 returned\n", rchsa); + return SCPE_OK; /* Not found, CC3 */ + } + + inta = ((~spadent)>>16)&0x7f; /* get channel interrupt level */ + chp->chan_int = inta; /* make sure it is set in channel */ + + /* reset this channel */ + dibp->chan_fifo_in = 0; /* reset the FIFO pointers */ + dibp->chan_fifo_out = 0; /* reset the FIFO pointers */ + chp->chan_inch_addr = 0; /* remove inch status buffer address */ + chp->base_inch_addr = 0; /* clear the base inch addr */ + chp->max_inch_addr = 0; /* clear the last inch addr */ + INTS[inta] &= ~INTS_ACT; /* clear level active */ + SPAD[inta+0x80] &= ~SINT_ACT; /* clear in spad too */ + + /* now go through all the sa for the channel and stop any IOCLs */ + for (i=0; iunitptr; /* get the unit ptr */ + + /* see if we have a rschnl device entry */ + if (dibp->rschnl_io != NULL) { /* NULL if no rschnl_io function */ + /* call the device controller to process rschnl */ + j = dibp->rschnl_io(uptr); /* get status from device */ + sim_debug(DEBUG_EXP, &cpu_dev, + "rschnl_io returned %02x chsa %04x\n", j, rchsa); + } + chp->chan_status = 0; /* clear the channel status */ + chp->chan_byte = BUFF_EMPTY; /* no data yet */ + chp->ccw_addr = 0; /* clear buffer address */ + chp->chan_caw = 0x0; /* clear IOCD address */ + chp->ccw_count = 0; /* channel byte count 0 bytes*/ + chp->ccw_flags = 0; /* clear flags */ + chp->ccw_cmd = 0; /* read command */ + chp->chan_inch_addr = 0; /* clear inch addr */ + chp->base_inch_addr = 0; /* clear the base inch addr */ + chp->max_inch_addr = 0; /* clear the last inch addr */ + } + sim_debug(DEBUG_XIO, &cpu_dev, "rschnlxio return CC1 lchan %02x lchan %02x inta %04x\n", + lchan, rchan, inta); + *status = CC1BIT; /* request accepted, no status, so CC1 TRY THIS */ + return SCPE_OK; /* All OK */ +} + +/* HIO - Halt I/O */ +t_stat haltxio(uint16 lchsa, uint32 *status) { /* halt XIO */ + DIB *dibp; + UNIT *uptr; + uint32 chan_icb; /* Interrupt level context block address */ + uint32 iocla; /* I/O channel IOCL address int ICB */ + uint32 inta, spadent, tempa, itva; + uint16 lchan = get_chan(lchsa); + uint16 rchan, rchsa; + CHANP *chp; /* Channel prog pointers */ + DEVICE *dptr; /* DEVICE pointer */ + + /* get the device entry for the logical channel in SPAD */ + spadent = SPAD[lchan]; /* get spad device entry for logical channel */ + rchan = (spadent & 0x7f00) >> 8; /* get real channel */ + rchsa = (rchan << 8) | (lchsa & 0xff); /* merge sa to real channel */ + dibp = dib_unit[rchsa]; /* get the device DIB pointer */ + chp = find_chanp_ptr(rchsa); /* find the chanp pointer */ + + if (dibp == 0 || chp == 0) { /* if no dib or chan ptr, CC3 on return */ + *status = CC3BIT; /* not found, so CC3 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "HIO lchsa %04x rchsa %04x device not present, CC3 returned\n", lchsa, rchsa); + return SCPE_OK; /* not found, CC3 */ + } + uptr = chp->unitptr; /* get the unit ptr */ + /* is device or unit marked disabled? */ + dptr = get_dev(uptr); + if ((dptr->flags & DEV_DIS) || ((uptr->flags & UNIT_DIS) && + ((uptr->flags & UNIT_SUBCHAN) == 0))) { + /* is device/unit disabled? */ + *status = CC3BIT; /* not enabled, so error CC3 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "HIO rchsa %04x device/unit not enabled, CC3 returned\n", rchsa); + return SCPE_OK; /* Not found, CC3 */ + } + + /* see if interrupt is setup in SPAD and determine IVL for channel */ + sim_debug(DEBUG_EXP, &cpu_dev, "HIO dev spad %08x lchsa %04x rchsa %04x\n", spadent, lchsa, rchsa); + + /* the haltio opcode processing software has already checked for F class */ + inta = ((~spadent)>>16)&0x7f; /* get channel interrupt level */ + chp->chan_int = inta; /* make sure it is set in channel */ + sim_debug(DEBUG_EXP, &cpu_dev, "HIO int spad %08x inta %02x rchan %02x\n", spadent, inta, rchan); + + /* get the address of the interrupt IVL in main memory */ + itva = SPAD[0xf1] + (inta<<2); /* int vector address */ + chan_icb = RMW(itva); /* Interrupt context block addr */ + iocla = RMW(chan_icb+16); /* iocla is in wd 4 of ICB */ + + sim_debug(DEBUG_EXP, &cpu_dev, + "HIO busy test byte %02x rchsa %04x cmd %02x ccw_flags %04x IOCD1 %08x IOCD2 %08x\n", + chp->chan_byte, rchsa, chp->ccw_cmd, chp->ccw_flags, RMW(iocla), RMW(iocla+4)); + + /* the channel is busy, so process */ + /* see if we have a haltio device entry */ + if (dibp->halt_io != NULL) { /* NULL if no haltio function */ + /* call the device controller to get halt_io status */ + tempa = dibp->halt_io(uptr); /* get status from device */ + + /* CC's are returned in bits 1-4. Bits 16-31 has SCPE code */ + /* SCPE_IOERR is 2 */ + /* SCPE_OK is 0 */ + if ((tempa & RMASK) != SCPE_OK) { /* sub channel has status ready */ + uint32 incha = chp->chan_inch_addr; /* get inch address */ + /* The device I/O has been terminated and status stored. */ + sim_debug(DEBUG_EXP, &cpu_dev, + "HIO halt_io call return ERROR FIFO #%1x rchsa %04x retstat %08x cstat %08x\n", + FIFO_Num(rchsa), rchsa, tempa, chp->chan_status); + + /* chan_end is called in hio device service routine */ + /* the device is no longer busy, post status */ + /* The diags want the interrupt for the disk */ + *status = CC1BIT; /* request accepted, no status, so CC1 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "HIO END2X ECHO rchsa %04x cmd %02x ccw_flags %04x status %04x\n", + rchsa, chp->ccw_cmd, chp->ccw_flags, *status); +// irq_pend = 1; /* flag to test for int condition */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "HIO rchsa %04x LOOK FIFO #%1x irq %02x inch %06x chp %p icba %06x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, incha, chp, chan_icb, chp->chan_byte); + + /* see if user wants to have status posted and setting CC2 in return value */ + if ((tempa & LMASK) == CC2BIT) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "HIO rchsa %04x LOOK FIFO #%1x irq %02x inch %06x chp %p icba %06x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, incha, chp, chan_icb, chp->chan_byte); + /* post any status */ + if (post_csw(chp, 0)) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "HIO rchsa %04x POST FIFO #%1x irq %02x inch %06x chan_icba+20 %08x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, incha, RMW(chan_icb+20), chp->chan_byte); + /* change status from BUFF_POST to BUFF_DONE */ + /* if not BUFF_POST we have a PPCI or channel busy interrupt */ + /* so leave the channel status alone */ + if (chp->chan_byte == BUFF_POST) { + chp->chan_byte = BUFF_DONE; /* show done & not busy */ + } + sim_debug(DEBUG_XIO, &cpu_dev, + "HIO END incha %06x chan_icba+20 %08x rchsa %04x sw1 %08x sw2 %08x\n", + incha, RMW(chan_icb+20), rchsa, RMW(incha), RMW(incha+4)); + /*ADDED 111921 to disable int request after data posted */ + INTS[inta] &= ~INTS_REQ; /* clear any level request */ + *status = CC2BIT; /* status stored from SIO, so CC2 */ + return SCPE_OK; /* No CC's all OK */ + } + } + /* see if user wants to have status posted by setting CC4 in return value */ + if ((tempa & LMASK) == CC4BIT) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "HIO rchsa %04x LOOK FIFO #%1x irq %02x inch %06x chp %p icba %06x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, incha, chp, chan_icb, chp->chan_byte); + } + sim_debug(DEBUG_EXP, &cpu_dev, + "HIO END2Y rchsa %04x cmd %02x ccw_flags %04x status %04x\n", + rchsa, chp->ccw_cmd, chp->ccw_flags, *status); + return SCPE_OK; /* CC1 & all OK */ + } + /* the device is not busy, so cmd is completed */ + sim_debug(DEBUG_EXP, &cpu_dev, + "HIO BUFF_DONE1 chp %p chan_byte %04x\n", chp, chp->chan_byte); + /* the channel is not busy, so return OK */ + *status = CC1BIT; /* request accepted, post good status, so CC1 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "HIO END3 rchsa %04x cmd %02x ccw_flags %04x status %04x\n", + rchsa, chp->ccw_cmd, chp->ccw_flags, *status); + +#ifndef GIVE_INT_ON_NOT_BUSY_121420_03082021 + chp->chan_byte = BUFF_DONE; /* we are done */ + sim_debug(DEBUG_EXP, &cpu_dev, + "haltxio BUFF_DONE2 chp %p chan_byte %04x\n", chp, chp->chan_byte); + if ((dptr != NULL) && + (DEV_TYPE(dptr) == DEV_ETHER)) { /* see if this is ethernet */ + /* Ethernet does not want SNS_UNITEXP */ + chp->chan_status = (STATUS_DEND|STATUS_CEND); + } else { + chp->chan_status = (STATUS_DEND|STATUS_CEND|STATUS_EXPT); + } + push_csw(chp); /* store the status 1st in FIFO */ + /* change chan_byte to BUFF_POST */ + chp->chan_byte = BUFF_POST; /* show done with data */ + chp->chan_status = 0; /* no status anymore */ + chp->ccw_cmd = 0; /* no command anymore */ + irq_pend = 1; /* flag to test for int condition */ +#endif + return SCPE_OK; /* No CC's all OK */ + } + + /* device does not have a HIO entry, so terminate the I/O */ + if ((chp->chan_byte & BUFF_BUSY) == 0) { + /* the channel is not busy, so return OK */ + *status = CC1BIT; /* request accepted, no status, so CC1 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "HIO END1 not busy return rchsa %04x cmd %02x ccw_flags %04x status %04x\n", + rchsa, chp->ccw_cmd, chp->ccw_flags, *status); + chp->chan_byte = BUFF_DONE; /* we are done */ + chp->chan_status = (STATUS_DEND|STATUS_CEND|STATUS_EXPT); + store_csw(chp); /* store the status */ + /* change chan_byte to BUFF_POST */ + chp->chan_byte = BUFF_POST; /* show done with data */ + chp->chan_status = 0; /* no status anymore */ + chp->ccw_cmd = 0; /* no command anymore */ + irq_pend = 1; /* flag to test for int condition */ + return SCPE_OK; /* CC1 & all OK */ + } + + /* device does not have a HIO entry, so terminate the I/O */ + /* a haltxio entry should be provided for a device so busy can be cleared */ + /* check for a Command or data chain operation in progresss */ + sim_debug(DEBUG_EXP, &cpu_dev, + "HIO device busy lchsa %04x rchsa %04x\n", lchsa, rchsa); + + /* reset the DC or CC bits to force completion */ + chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* reset chaining bits */ + chp->chan_byte = BUFF_BUSY; /* wait for post_csw to be done */ + sim_cancel(uptr); /* cancel timer service */ + chp->chan_status &= ~STATUS_BUSY; /* remove BUSY status bit */ + chan_end(rchsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); /* show I/O complete */ + + /* post the channel status */ + chp->ccw_count = 0; /* zero the count */ + /* remove SLI, PPCI and Unit check status bits */ + if (post_csw(chp, ((STATUS_PCI) << 16))) { + INTS[inta] &= ~INTS_REQ; /* clear any level request */ + *status = CC2BIT; /* status stored from SIO, so CC2 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "HIO END4 rchsa %04x cmd %02x ccw_flags %04x status %04x\n", + rchsa, chp->ccw_cmd, chp->ccw_flags, *status); + /* change status from BUFF_POST to BUFF_DONE */ + if (chp->chan_byte == BUFF_POST) { + chp->chan_byte = BUFF_DONE; /* show done & not busy */ + } + return SCPE_OK; /* CC2 & all OK */ + } + sim_debug(DEBUG_EXP, &cpu_dev, + "HIO END5 rchsa %04x cmd %02x ccw_flags %04x status %04x\n", + rchsa, chp->ccw_cmd, chp->ccw_flags, *status); + return SCPE_OK; /* No CC's all OK */ +} + +/* grab controller n/u */ +/* TODO return unimplemented function error, not busy */ +t_stat grabxio(uint16 lchsa, uint32 *status) { /* grab controller XIO n/u */ + DIB *dibp; /* device information pointer */ + UNIT *uptr; /* pointer to unit in channel */ + CHANP *chp; + DEVICE *dptr; /* Device ptr */ + uint16 lchan = get_chan(lchsa); /* get the logical channel number */ + uint32 spadent; + uint16 rchan, rchsa; /* the real channel number, chsa */ + + /* get the device entry for the logical channel in SPAD */ + spadent = SPAD[lchan]; /* get spad device entry for logical channel */ + rchan = (spadent & 0x7f00) >> 8; /* get real channel */ + rchsa = (rchan << 8) | (lchsa & 0xff); /* merge sa to real channel */ + + /* get the device entry for the logical channel in SPAD */ + dibp = dib_unit[rchsa]; /* get the DIB pointer */ + chp = find_chanp_ptr(rchsa); /* find the device chanp pointer */ + + if (dibp == 0 || chp == 0) { /* if no dib or channel ptr, CC3 return */ + *status = CC3BIT; /* not found, so CC3 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "GRIO test 1 rchsa %04x device not present, CC3 returned\n", rchsa); + return SCPE_OK; /* not found CC3 */ + } + + sim_debug(DEBUG_CMD, &cpu_dev, + "GRIO entry rchsa %04x status %08x cmd %02x flags %02x byte %02x\n", + rchsa, *status, chp->ccw_cmd, chp->ccw_flags, chp->chan_byte); + + uptr = chp->unitptr; /* get the unit ptr */ + /* is device or unit marked disabled? */ + dptr = get_dev(uptr); + if ((dptr->flags & DEV_DIS) || ((uptr->flags & UNIT_DIS) && + ((uptr->flags & UNIT_SUBCHAN) == 0))) { + /* is device/unit disabled? */ + *status = CC3BIT; /* not enabled, so error CC3 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "GRIO rchsa %04x device/unit not enabled, CC3 returned\n", rchsa); + return SCPE_OK; /* Not found, CC3 */ + } + + /* check for a Command or data chain operation in progresss */ + if (chp->ccw_cmd != 0 || (chp->ccw_flags & (FLAG_DC|FLAG_CC)) != 0) { + *status = CC4BIT; /* busy, so CC4 */ + sim_debug(DEBUG_CMD, &cpu_dev, + "GRIO busy return CC4 lchsa %04x rchsa %04x status %08x\n", + lchsa, rchsa, *status); + return SCPE_OK; /* CC4 all OK */ + } + +// NOW ON 05142021 */ + /* device does not have stop_io entry, so stop the I/O */ + /* check for a Command or data chain operation in progresss */ + /* set the return to CC3BIT & CC4BIT causes infinite loop in MPX1X */ + /* restore code to old CC1BIT return 12/21/2020 */ + // try using CC4 on MPX3X when still busy + if (chp->chan_byte == BUFF_POST) { + uint32 chan_icb; /* Interrupt level context block address */ + uint32 inta = ((~spadent)>>16)&0x7f; /* get channel interrupt level */ + /* get the address of the interrupt IVL in main memory */ + uint32 itva = SPAD[0xf1] + (inta<<2); /* int vector address */ + chan_icb = RMW(itva); /* Interrupt context block addr */ + *status = CC1BIT; /* request accepted, no status, so CC1 */ + /* see if any status ready to post */ + if (FIFO_Num(rchsa)) { + uint32 incha = chp->chan_inch_addr; /* get inch address */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "GRIO chsa %04x LOOK FIFO #%1x irq %02x inch %06x chp %p icba %06x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, incha, chp, chan_icb, chp->chan_byte); + if (post_csw(chp, 0)) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "GRIO chsa %04x POST FIFO #%1x irq %02x inch %06x chan_icba+20 %08x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, incha, RMW(chan_icb+20), chp->chan_byte); + /* change status from BUFF_POST to BUFF_DONE */ + /* if not BUFF_POST we have a PPCI or channel busy interrupt */ + /* so leave the channel status alone */ + chp->chan_byte = BUFF_DONE; /* show done & not busy */ + sim_debug(DEBUG_XIO, &cpu_dev, + "GRIO END incha %06x chan_icba+20 %08x chsa %04x sw1 %08x sw2 %08x\n", + incha, RMW(chan_icb+20), rchsa, RMW(incha), RMW(incha+4)); + INTS[inta] &= ~INTS_REQ; /* clear any level request if no status */ + *status = CC2BIT; /* status stored from SIO, so CC2 */ + return SCPE_OK; /* No CC's all OK */ + } else { + sim_debug(DEBUG_IRQ, &cpu_dev, + "GRIO chsa %04x NOT POSTED FIFO #%1x irq %02x inch %06x chan_icba %06x chan_byte %02x\n", + rchsa, FIFO_Num(rchsa), inta, incha, chan_icb, chp->chan_byte); + /* now store the status dw address into word 5 of the ICB for the channel */ + WMW(chan_icb+20, 0); /* post sw addr 0 in ICB+5w & reset CCs */ + *status = 0; /* no status stored from STPIO, so no CC */ + return SCPE_OK; /* No CC's all OK */ + } + } + } + + /* If this is console, debugger wants CC3 & CC4 = 0 */ + if (rchan == 0x7e) { + /* returning No CC's causes MPX1X to loop forever */ + /* so restore returning CC1 */ + *status = 0; /* return no CC's */ + } else { + /* diags want unsupported transaction for disk */ + *status = CC2BIT|CC4BIT; /* unsupported transaction */ + } + sim_debug(DEBUG_CMD, &cpu_dev, + "GRIO lchsa %04x rchsa %04x status %08x\n", lchsa, rchsa, *status); + return SCPE_OK; /* dono */ +} + +/* reset controller XIO */ +t_stat rsctlxio(uint16 lchsa, uint32 *status) { /* reset controller XIO */ + DIB *dibp; + UNIT *uptr; + uint32 spadent; + uint16 chsa; + CHANP *chp; + int lev, i; + uint32 chan = get_chan(lchsa); + DEVICE *dptr; /* get device ptr */ + + /* get the device entry for the logical channel in SPAD */ + spadent = SPAD[chan]; /* get spad device entry for logical channel */ + chan = spadent & 0x7f00; /* get real channel */ + chsa = chan; /* use just channel */ + dibp = dib_unit[chsa]; /* get the DIB pointer */ + chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + uptr = chp->unitptr; /* get the unit ptr */ + + sim_debug(DEBUG_EXP, &cpu_dev, "rsctlxio 1 chan %04x SPAD %08x\n", chsa, spadent); + if (dibp == 0 || uptr == 0) { /* if no dib or unit ptr, CC3 on return */ + *status = CC3BIT; /* not found, so CC3 */ + return SCPE_OK; /* not found, CC3 */ + } + sim_debug(DEBUG_EXP, &cpu_dev, "rsctlxio 2 chan %04x spad %08x\r\n", chsa, spadent); + /* is device or unit marked disabled? */ + dptr = get_dev(uptr); /* get device ptr */ + + /* is device/unit disabled? */ + if ((dptr->flags & DEV_DIS) || ((uptr->flags & UNIT_DIS) && + ((uptr->flags & UNIT_SUBCHAN) == 0))) { + *status = CC3BIT; /* not enabled, so error CC3 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "RSCTL rchsa %04x device/unit not enabled, CC3 returned\n", chsa); + return SCPE_OK; /* Not found, CC3 */ + } + lev = find_int_lev(chan); /* get our int level */ + INTS[lev] &= ~INTS_ACT; /* clear level active */ + SPAD[lev+0x80] &= ~SINT_ACT; /* clear spad too */ + INTS[lev] &= ~INTS_REQ; /* clear level request */ + + /* now go through all the sa for the channel and stop any IOCLs */ + for (i=0; ichan_fifo_in = 0; /* set no FIFO entries */ + dibp->chan_fifo_out = 0; /* set no FIFO entries */ + + uptr = chp->unitptr; /* get the unit ptr */ + unit = uptr - dptr->units; /* get the UNIT number */ + if (dibp->ioclq_ptr != NULL) { + qp = &dibp->ioclq_ptr[unit]; /* IOCLQ pointer */ + if (qp != NULL) { + qp->ioclq_in = 0; /* clear any entries */ + qp->ioclq_out = 0; /* clear any entries */ + } + } + + /* see if we have a rsctl device entry */ + if (dibp->rsctl_io != NULL) { /* NULL if no rsctl_io function */ + /* call the device controller to process rsctl */ + j = dibp->rsctl_io(uptr); /* get status from device */ + sim_debug(DEBUG_EXP, &cpu_dev, + "rsctl_io returned %02x chsa %04x\n", j, chsa); + } + chp->chan_status = 0; /* clear the channel status */ + chp->chan_byte = BUFF_EMPTY; /* no data yet */ + chp->ccw_addr = 0; /* clear buffer address */ + chp->chan_caw = 0x0; /* clear IOCD address */ + chp->ccw_count = 0; /* channel byte count 0 bytes*/ + chp->ccw_flags = 0; /* clear flags */ + chp->ccw_cmd = 0; /* read command */ + } + sim_debug(DEBUG_EXP, &cpu_dev, "rsctlxio return CC1 chan %04x lev %04x\n", chan, lev); + /* returning 0 for status breaks ethernet controller */ + if ((dptr != NULL) && + (DEV_TYPE(dptr) == DEV_ETHER)) { /* see if this is ethernet */ + *status = CC1BIT; /* request accepted, no status, so CC1 */ + } else + *status = 0; /* request accepted, no status, return 0 */ + return SCPE_OK; /* All OK */ +} + +/* boot from the device (ch/sa) the caller specified */ +/* on CPU reset, the cpu has set the IOCD data at location 0-4 */ +t_stat chan_boot(uint16 chsa, DEVICE *dptr) { + int chan = get_chan(chsa); + DIB *dibp = (DIB *)dptr->ctxt; /* get pointer to DIB for this device */ + UNIT *uptr = find_unit_ptr(chsa); /* find pointer to unit on channel */ + CHANP *chp = 0; + + sim_debug(DEBUG_EXP, &cpu_dev, "Channel Boot chan/device addr %04x SNS %08x\n", chsa, uptr->u5); + if (dibp == 0) /* if no channel or device, error */ + return SCPE_IOERR; /* error */ + if (dibp->chan_prg == NULL) /* must have channel information for each device */ + return SCPE_IOERR; /* error */ + chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + if (chp == 0) /* if no channel, error */ + return SCPE_IOERR; /* error */ + + /* make sure there is an IOP/MFP configured at 7e00 on system */ + if (dib_chan[0x7e] == NULL) { + sim_debug(DEBUG_CMD, dptr, + "ERROR===ERROR\nIOP/MFP device 0x7e00 not configured on system, aborting\n"); + printf("ERROR===ERROR\nIOP/MFP device 0x7e00 not configured on system, aborting\n"); + return SCPE_UNATT; /* error */ + } + + /* make sure there is an IOP/MFP console configured at 7efc/7efd on system */ + if ((dib_unit[0x7efc] == NULL) || (dib_unit[0x7efd] == NULL)) { + sim_debug(DEBUG_CMD, dptr, + "ERROR===ERROR\nCON device 0x7efc/0x7ecd not configured on system, aborting\n"); + printf("ERROR===ERROR\nCON device 0x7efc/0x7efd not configured on system, aborting\n"); + return SCPE_UNATT; /* error */ + } + + chp->chan_status = 0; /* clear the channel status */ + chp->chan_dev = chsa; /* save our address (ch/sa) */ + chp->chan_byte = BUFF_EMPTY; /* no data yet */ + chp->ccw_addr = 0; /* start loading at loc 0 */ + chp->chan_caw = 0x0; /* set IOCD address to memory location 0 */ + chp->ccw_count = 0; /* channel byte count 0 bytes*/ + chp->ccw_flags = 0; /* Command chain and supress incorrect length */ + chp->chan_info = INFO_SIOCD; /* show first IOCD in channel prog */ + chp->ccw_cmd = 0; /* read command */ + /* moved here to not destry loc 0-0x14 on reset/go cmds */ + M[0] = 0x02000000; /* 0x00 IOCD 1 read into address 0 */ + M[1] = 0x60000078; /* 0x04 IOCD 1 CMD Chain, Suppress incor length, 120 bytes */ + M[2] = 0x53000000; /* 0x08 IOCD 2 BKSR or RZR to re-read boot code */ + M[3] = 0x60000001; /* 0x0C IOCD 2 CMD chain,Supress incor length, 1 byte */ + M[4] = 0x02000000; /* 0x10 IOCD 3 Read into address 0 */ + M[5] = 0x000006EC; /* 0x14 IOCD 3 Read 0x6EC bytes */ + loading = chsa; /* show we are loading from the boot device */ + + sim_debug(DEBUG_CMD, &cpu_dev, "Channel Boot calling load_ccw chan %04x status %08x\n", + chan, chp->chan_status); + + /* start processing the boot IOCL at loc 0 */ + if (load_ccw(chp, 0)) { /* load IOCL starting from location 0 */ + sim_debug(DEBUG_EXP, &cpu_dev, "Channel Boot Error return from load_ccw chan %04x status %08x\n", + chan, chp->chan_status); + chp->ccw_flags = 0; /* clear the command flags */ + chp->chan_byte = BUFF_DONE; /* done with errors */ + loading = 0; /* show we are done loading from the boot device */ + return SCPE_IOERR; /* return error */ + } + sim_debug(DEBUG_XIO, &cpu_dev, "Channel Boot OK return from load_ccw chsa %04x status %04x\n", + chsa, chp->chan_status); + return SCPE_OK; /* all OK */ +} + +/* Continue a channel program for a device */ +uint32 cont_chan(uint16 chsa) +{ + int32 stat; /* return status 0/1 from loadccw */ + CHANP *chp = find_chanp_ptr(chsa); /* channel program */ + + sim_debug(DEBUG_XIO, &cpu_dev, + "cont_chan entry chp %p chan_byte %02x chsa %04x addr %06x\n", + chp, chp->chan_byte, chsa, chp->ccw_addr); + /* we have entries, continue channel program */ + if (chp->chan_byte != BUFF_NEXT) { + /* channel program terminated already, ignore entry */ + sim_debug(DEBUG_EXP, &cpu_dev, + "cont_chan chan_byte %02x is NOT BUFF_NEXT chsa %04x addr %06x\n", + chp->chan_byte, chsa, chp->ccw_addr); + return 1; + } + if (chp->chan_byte == BUFF_NEXT) { + uint32 chan = get_chan(chsa); + sim_debug(DEBUG_XIO, &cpu_dev, + "cont_chan resume chan prog chsa %04x iocla %06x\n", + chsa, chp->chan_caw); + + /* start a channel program */ + stat = load_ccw(chp, 1); /* resume the channel program */ + /* we get status returned if there is an error on the startio cmd call */ + if (stat) { + /* we have an error or user requested interrupt, return status */ + sim_debug(DEBUG_EXP, &cpu_dev, "cont_chan error, store csw chsa %04x status %08x\n", + chsa, chp->chan_status); +/*NOTE*/ /* if we have an error, we would loop forever if the CC bit was set */ + /* the only way to stop was to do a kill */ + chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* reset chaining bits */ + /* DIAG's want CC1 with memory access error */ + if (chp->chan_status & STATUS_PCHK) { + chp->chan_status &= ~STATUS_LENGTH; /* clear incorrect length */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* show I/O complete */ + sim_debug(DEBUG_EXP, &cpu_dev, + "cont_chan Error1 FIFO #%1x store_csw CC1 chan %04x status %08x\n", + FIFO_Num(chsa), chan, chp->chan_status); + return SCPE_OK; /* done */ + } + /* other error, stop the show */ + chp->chan_status &= ~STATUS_PCI; /* remove PCI status bit */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* show I/O complete */ + sim_debug(DEBUG_EXP, &cpu_dev, + "cont_chan Error2 FIFO #%1x store_csw CC1 chan %04x status %08x\n", + FIFO_Num(chsa), chan, chp->chan_status); + return SCPE_OK; /* done */ + } + /* we get here when the start cmd has been processed without error */ + /* go wait for the cmd to finish */ + sim_debug(DEBUG_XIO, &cpu_dev, + "cont_chan continue wait chsa %04x status %08x iocla %06x byte %02x\n", + chsa, chp->chan_status, chp->chan_caw, chp->chan_byte); + return SCPE_OK; /* done, status stored */ + } + /* must be more IOCBs, wait for them */ + sim_debug(DEBUG_XIO, &cpu_dev, + "cont_chan continue not next chsa %04x status %08x iocla %06x\n", + chsa, chp->chan_status, chp->chan_caw); + return SCPE_OK; +} + +/* Scan all channels and see if one is ready to start or has + interrupt pending. Return icb address and interrupt level +*/ +uint32 scan_chan(uint32 *ilev) { + int i; + uint32 chsa; /* No device */ + uint32 chan; /* channel num 0-7f */ + uint32 tempa; /* icb address */ + uint32 incha; /* inch address */ + uint32 chan_ivl; /* int level table address */ + uint32 chan_icba; /* Interrupt level context block address */ + CHANP *chp; /* channel prog pointer */ + DIB *dibp; /* DIB pointer */ + uint32 sw1, sw2; /* status words */ + + /* see if we are loading */ + if (loading) { + /* we are loading see if chan prog complete */ + /* get the device entry for the logical channel in SPAD */ + chan = loading & 0x7f00; /* get real channel and zero sa */ + dibp = dib_unit[chan]; /* get the IOP/MFP DIB pointer */ + if (dibp == 0) + return 0; /* skip unconfigured channel */ + /* see if status is stored in FIFO */ + /* see if the FIFO is empty */ + if ((FIFO_Num(chan)) && ((FIFO_Get(chan, &sw1) == 0) && + (FIFO_Get(chan, &sw2) == 0))) { + /* the SPAD entries are not set up, so no access to icb or ints */ + /* get the status from the FIFO and throw it away */ + /* we really should post it to the current inch address */ + /* there is really no need to post, but it might be helpfull */ + chp = find_chanp_ptr(chan); /* find the chanp pointer for channel */ + /* this address most likely will be zero here */ + tempa = chp->chan_inch_addr; /* get inch status buffer address */ + /* before overwriting memory loc 0+4, save PSD for caller in TPSD[] locations */ + TPSD[0] = M[0]; /* save PSD from loc 0&4 */ + TPSD[1] = M[1]; + /* save the status double word to memory */ + /* set BIT 1 to show status stored */ + WMW(tempa, sw1|BIT1); /* save sa & IOCD address in status WD 1 loc */ + WMW(tempa+4, sw2); /* save status and residual cnt in status WD 2 loc */ + chp->chan_byte = BUFF_DONE; /* we are done */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "LOADING %06x %04x FIFO #%1x read inch %06x sw1 %08x sw2 %08x\n", + chp->chan_caw, chan, FIFO_Num(chan), tempa, sw1|BIT1, sw2); + return loading; + } + return 0; /* not ready, return */ + } + + /* ints not blocked, so look for highest requesting interrupt */ + for (i=0; i<112; i++) { + if (SPAD[i+0x80] == 0) /* not initialize? */ + continue; /* skip this one */ + if ((SPAD[i+0x80]&MASK24) == MASK24) /* not initialize? */ + continue; /* skip this one */ + if (INTS[i] & INTS_REQ) /* if already requesting, skip */ + continue; /* skip this one */ + + /* see if there is pending status for this channel */ + /* if there is and the level is not requesting, do it */ + /* get the device entry for the logical channel in SPAD */ + chan = (SPAD[i+0x80] & 0x7f00); /* get real channel and zero sa */ + dibp = dib_chan[get_chan(chan)]; /* get the channel device information pointer */ + if (dibp == 0) /* we have a channel to check */ + continue; /* not defined, skip this one */ + + /* we have a channel to check */ + /* check for pending status */ + if (FIFO_Num(chan)) { + INTS[i] |= INTS_REQ; /* turn on channel interrupt request */ + sim_debug(DEBUG_EXP, &cpu_dev, + "scan_chan FIFO REQ FIFO #%1x irq %02x SPAD %08x INTS %08x\n", + FIFO_Num(SPAD[i+0x80] & 0x7f00), i, SPAD[i+0x80], INTS[i]); +#ifdef TRY_DEBUG_01172021 + irq_pend = 1; /* we have pending interrupt */ +#endif + continue; + } + } + + /* see if we are able to look for ints */ + if (CPUSTATUS & BIT24) /* interrupts blocked? */ + return 0; /* yes, done */ + + /* now go process the highest requesting interrupt */ + for (i=0; i<112; i++) { + if (SPAD[i+0x80] == 0) /* not initialize? */ + continue; /* skip this one */ + /* this is a bug fix for MPX 1.x restart command */ + if ((SPAD[i+0x80]&MASK24) == MASK24) /* not initialize? */ + continue; /* skip this one */ + /* stop looking if an active interrupt is found */ + if ((INTS[i]&INTS_ACT) || (SPAD[i+0x80]&SINT_ACT)) { /* look for level active */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "scan_chan INTS ACT irq %02x SPAD %08x INTS %08x\n", + i, SPAD[i+0x80], INTS[i]); + return 0; /* this level active, so stop looking */ + } + + if ((INTS[i] & INTS_ENAB) == 0) { /* ints must be enabled */ + continue; /* skip this one */ + } + + /* look for the highest requesting interrupt */ + /* that is enabled */ + if (((INTS[i] & INTS_ENAB) && (INTS[i] & INTS_REQ)) || + ((SPAD[i+0x80] & SINT_ENAB) && (INTS[i] & INTS_REQ))) { + + sim_debug(DEBUG_IRQ, &cpu_dev, + "scan_chan highest int req irq %02x SPAD %08x INTS %08x\n", + i, SPAD[i+0x80], INTS[i]); + + /* requesting, make active and turn off request flag */ + INTS[i] &= ~INTS_REQ; /* turn off request */ + INTS[i] |= INTS_ACT; /* turn on active */ + SPAD[i+0x80] |= SINT_ACT; /* show active in SPAD too */ + + /* get the address of the interrupt IVL table in main memory */ + chan_ivl = SPAD[0xf1] + (i<<2); /* contents of spad f1 points to chan ivl in mem */ + chan_icba = RMW(chan_ivl); /* get the interrupt context block addr in memory */ + + /* see if there is pending status for this channel */ + /* get the device entry for the logical channel in SPAD */ + chan = (SPAD[i+0x80] & 0x7f00); /* get real channel and zero sa */ + dibp = dib_chan[get_chan(chan)]; /* get the channel device information pointer */ + if (dibp == 0) { /* see if we have a channel to check */ + /* not a channel, must be clk or ext int */ + *ilev = i; /* return interrupt level */ + irq_pend = 0; /* not pending anymore */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "scan_chan %04x POST NON FIFO irq %02x chan_icba %06x SPAD[%02x] %08x\n", + chan, i, chan_icba, i+0x80, SPAD[i+0x80]); + return(chan_icba); /* return ICB address */ + } + /* must be a device, get status ready to post */ + if (FIFO_Num(chan)) { + /* new 051020 find actual device with the channel program */ + /* not the channel, that is not correct most of the time */ + tempa = dibp->chan_fifo[dibp->chan_fifo_out]; /* get SW1 of FIFO entry */ + chsa = chan | (tempa >> 24); /* find device address for requesting chan prog */ + chp = find_chanp_ptr(chsa); /* find the chanp pointer for channel */ + incha = chp->chan_inch_addr; /* get inch status buffer address */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "scan_chan %04x LOOK FIFO #%1x irq %02x inch %06x chp %p icba %06x chan_byte %02x\n", + chsa, FIFO_Num(chan), i, incha, chp, chan_icba, chp->chan_byte); + if (post_csw(chp, 0)) { + /* change status from BUFF_POST to BUFF_DONE */ + /* if not BUFF_POST we have a PPCI or channel busy interrupt */ + /* so leave the channel status alone */ + if (chp->chan_byte == BUFF_POST) { + chp->chan_byte = BUFF_DONE; /* show done & not busy */ + } + sim_debug(DEBUG_IRQ, &cpu_dev, + "scan_chanx %04x POST FIFO #%1x irq %02x inch %06x chan_icba+20 %08x chan_byte %02x\n", + chan, FIFO_Num(chan), i, incha, RMW(chan_icba+20), chp->chan_byte); + } else { + sim_debug(DEBUG_IRQ, &cpu_dev, + "scan_chanx %04x NOT POSTED FIFO #%1x irq %02x inch %06x chan_icba %06x chan_byte %02x\n", + chan, FIFO_Num(chan), i, incha, chan_icba, chp->chan_byte); + } + *ilev = i; /* return interrupt level */ + irq_pend = 0; /* not pending anymore */ + return(chan_icba); /* return ICB address */ + } else { + /* we had an interrupt request, but no status is available */ + /* clear the interrupt and go on */ + /* this is a fix for MPX1X restart 092220 */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "scan_chan highest int has no stat irq %02x SPAD %08x INTS %08x\n", + i, SPAD[i+0x80], INTS[i]); + + /* requesting, make active and turn off request flag */ + INTS[i] &= ~INTS_ACT; /* turn off active int */ + SPAD[i+0x80] &= ~SINT_ACT; /* clear active in SPAD too */ + } + } + } + /* if the interrupt is not zero'd here, we get SPAD error */ + irq_pend = 0; /* not pending anymore */ + return 0; /* done */ +} + +/* part of find_dev_from_unit(UNIT *uptr) in scp.c */ +/* Find_dev pointer for a unit + Input: uptr = pointer to unit + Output: dptr = pointer to device +*/ +DEVICE *get_dev(UNIT *uptr) +{ + DEVICE *dptr = NULL; + uint32 i, j; + + if (uptr == NULL) /* must be valid unit */ + return NULL; + if (uptr->dptr) /* get device pointer from unit */ + return uptr->dptr; /* return valid pointer */ + + /* the device pointer in the unit is not set up, do it now */ + /* This should never happen as the pointer is setup in first reset call */ + for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* do all devices */ + for (j = 0; j < dptr->numunits; j++) { /* do all units for device */ + if (uptr == (dptr->units + j)) { /* match found? */ + uptr->dptr = dptr; /* set the pointer in unit */ + return dptr; /* return valid pointer */ + } + } + } + return NULL; +} + +/* set up the devices configured into the simulator */ +/* only devices with a DIB will be processed */ +t_stat chan_set_devs() { + uint32 i, j; + + for (i = 0; i < MAX_DEV; i++) { + dib_unit[i] = NULL; /* clear DIB pointer array */ + } + for (i = 0; i < MAX_CHAN; i++) { + dib_chan[i] = NULL; /* clear DIB pointer array */ + } + /* Build channel & device arrays */ + for (i = 0; sim_devices[i] != NULL; i++) { + DEVICE *dptr = sim_devices[i]; /* get pointer to next configured device */ + UNIT *uptr = dptr->units; /* get pointer to units defined for this device */ + DIB *dibp = (DIB *)dptr->ctxt; /* get pointer to DIB for this device */ + CHANP *chp; /* channel program pointer */ + int chsa; /* addr of device chan & subaddress */ + + /* set the device back pointer in the unit structure */ + for (j = 0; j < dptr->numunits; j++) { /* loop through unit entries */ + uptr->dptr = dptr; /* set the device pointer in unit structure */ + uptr++; /* next UNIT pointer */ + } + uptr = dptr->units; /* get pointer to units again */ + + if (dibp == NULL) /* If no DIB, not channel device */ + continue; + if ((dptr->flags & DEV_DIS) || /* Skip disabled devices */ + ((dibp->chan_prg) == NULL)) { /* must have channel info for each device */ + chsa = GET_UADDR(uptr->u3); /* ch/sa value */ + continue; + } + + chp = (CHANP *)dibp->chan_prg; /* must have channel information for each device */ + /* Check if address is in unit or dev entry */ + for (j = 0; j < dptr->numunits; j++) { /* loop through unit entries */ + chsa = GET_UADDR(uptr->u3); /* ch/sa value */ + /* zero some channel data loc's for device */ + chp->unitptr = uptr; /* set the unit back pointer */ + chp->chan_status = 0; /* clear the channel status */ + chp->chan_dev = chsa; /* save our address (ch/sa) */ + chp->chan_byte = BUFF_EMPTY; /* no data yet */ + chp->ccw_addr = 0; /* start loading at loc 0 */ + chp->chan_caw = 0; /* set IOCD address to memory location 0 */ + chp->ccw_count = 0; /* channel byte count 0 bytes*/ + chp->ccw_flags = 0; /* Command chain and supress incorrect length */ + chp->ccw_cmd = 0; /* read command */ + chp->chan_inch_addr = 0; /* clear curr address of stat dw in memory */ + chp->base_inch_addr = 0; /* clear base address of stat dw in memory */ + chp->max_inch_addr = 0; /* clear last address of stat dw in memory */ + + /* is unit marked disabled? */ + if ((uptr->flags & UNIT_DIS) == 0 || (uptr->flags & UNIT_SUBCHAN) != 0) { + /* see if this is unit zero */ + if ((chsa & 0xff) == 0) { + /* we have channel mux or dev 0 of units */ + if (dptr->flags & DEV_CHAN) { + /* see if channel address already defined */ + if (dib_chan[get_chan(chsa)] != 0) { + return SCPE_IERR; /* no, arg error */ + } + /* channel mux, save dib for channel */ + dib_chan[get_chan(chsa)] = dibp; + if (dibp->dev_ini != NULL) /* if there is an init routine, call it now */ + dibp->dev_ini(uptr, 1); /* init the channel */ + } else { + /* we have unit 0 of non-IOP/MFP device */ + if (dib_unit[chsa] != 0) { + return SCPE_IERR; /* no, arg error */ + } else { + /* channel mux, save dib for channel */ + /* for now, save any zero dev as chan */ + if (chsa) { + dib_unit[chsa] = dibp; /* no, save the dib address */ + if (dibp->dev_ini != NULL) /* if there is an init routine, call it now */ + dibp->dev_ini(uptr, 1); /* init the channel */ + } + } + } + } else { + /* see if address already defined */ + if (dib_unit[chsa] != 0) { + return SCPE_IERR; /* no, arg error */ + } + dib_unit[chsa] = dibp; /* no, save the dib address */ + } + } + if (dibp->dev_ini != NULL) /* call channel init if defined */ + dibp->dev_ini(uptr, 1); /* init the channel */ + uptr++; /* next UNIT pointer */ + chp++; /* next CHANP pointer */ + } + } + /* now make another pass through the channels and see which integrated */ + /* channel/controllers are defined and add them to the dib_chan definitions */ + /* this will handle non-MFP/IOP channel controllers */ + for (i = 0; i < MAX_CHAN; i++) { + if (dib_chan[i] == 0) { + /* channel not defined, see if defined in dib_unit array */ + /* check device zero for suspected channel */ + if (dib_unit[i<<8]) { + /* write dibp to channel array */ + dib_chan[i] = dib_unit[i<<8]; /* save the channel dib */ + } + } else { + /* channel is defined, see if defined in dib_unit array */ + if ((dib_unit[i<<8]) == 0) { + /* write dibp to units array */ + dib_unit[i<<8] = dib_chan[i]; /* save the channel dib */ + } + } + } + return SCPE_OK; /* all is OK */ +} + +/* Validate and set the device onto a given channel */ +t_stat set_dev_addr(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { + DEVICE *dptr; /* device pointer */ + DIB *dibp; /* dib pointer */ + UNIT *tuptr; /* temp unit pointer */ + t_value chan; /* new channel addr */ + t_stat r; /* return status */ + int i; /* temp */ + int chsa, ochsa; /* dev addr */ + + if (cptr == NULL) /* is there a UNIT name specified */ + return SCPE_ARG; /* no, arg error */ + if (uptr == NULL) /* is there a UNIT pointer */ + return SCPE_IERR; /* no, arg error */ + dptr = get_dev(uptr); /* find the device from unit pointer */ + if (dptr == NULL) { /* device not found, so error */ + fprintf(stderr, "Set dev no DEVICE cptr %s uptr %p\r\n", cptr, uptr); + return SCPE_IERR; /* error */ + } + + dibp = (DIB *)dptr->ctxt; /* get dib pointer from device struct */ + if (dibp == NULL) { /* we need a DIB */ + fprintf(stderr, "Set dev no DIB ptr %s uptr %p\r\n", cptr, uptr); + return SCPE_IERR; /* no DIB, so error */ + } + + chan = get_uint(cptr, 16, 0xffff, &r); /* get new device address */ + if (r != SCPE_OK) /* need good number */ + return r; /* number error, return error */ + + dibp->chan_addr = chan; /* set new parent channel addr */ + + /* change all the unit addresses with the new channel, but keep sub address */ + /* Clear out existing entries for all units on this device */ + tuptr = dptr->units; /* get pointer to units defined for this device */ + + /* loop through all units for this device */ + for (i = 0; i < dibp->numunits; i++) { + int mask=dibp->mask; /* sa bits that are used */ + ochsa = GET_UADDR(tuptr->u3); /* get old chsa for this unit */ + dib_unit[ochsa] = NULL; /* clear sa dib pointer */ + dib_unit[ochsa&0x7f00] = NULL; /* clear the channel dib address */ + chan &= ~mask; /* remove the unit number */ + chsa = chan | (ochsa & mask); /* put in new sa */ + if (chsa != ochsa) { + fprintf(stderr, "Set unit %x new chsa %04x old chsa %04x\r\n", i, chsa, ochsa); + } + tuptr->u3 &= ~UNIT_ADDR_MASK; /* clear old chsa for this unit */ + tuptr->u3 |= UNIT_ADDR(chsa); /* set new chsa for this unit */ + dib_unit[chan&0x7f00] = dibp; /* set the channel dib address */ + dib_unit[chsa] = dibp; /* save the dib address for new chsa */ + tuptr++; /* next unit pointer */ + } + return SCPE_OK; +} + +/* display channel/sub-address for device */ +t_stat show_dev_addr(FILE *st, UNIT *uptr, int32 v, CONST void *desc) { + DEVICE *dptr; + int chsa; + + if (uptr == NULL) /* valid unit? */ + return SCPE_IERR; /* no, error return */ + dptr = get_dev(uptr); /* get the device pointer from unit */ + if (dptr == NULL) /* valid pointer? */ + return SCPE_IERR; /* return error */ + chsa = GET_UADDR(uptr->u3); /* get the unit address */ + fprintf(st, "CHAN/SA %04x", chsa); /* display channel/subaddress */ + return SCPE_OK; /* we done */ +} + diff --git a/SEL32/sel32_clk.c b/SEL32/sel32_clk.c new file mode 100644 index 00000000..5d533bf1 --- /dev/null +++ b/SEL32/sel32_clk.c @@ -0,0 +1,714 @@ +/* sel32_clk.c: SEL 32 Class F IOP processor RTOM functions. + + Copyright (c) 2018-2021, James C. Bevier + Portions provided by Richard Cornwell, Geert Rolf and other SIMH contributers + + 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 + JAMES C. BEVIER 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. + + This module support the real-time clock and the interval timer. + These are CD/TD class 3 devices. The RTC can be programmed to + 50/100 HZ or 60/120 HZ rates and creates an interrupt at the + requested rate. The interval timer is a 32 bit register that is + loaded with a value to be down counted. An interrupt is generated + when the count reaches zero, The clock continues down counting + until read/reset by the programmer. The rate can be external or + 38.4 microseconds per count. +*/ + +#include "sel32_defs.h" + +#if NUM_DEVS_RTOM > 0 + +#define UNIT_CLK UNIT_IDLE|UNIT_DISABLE + +void rtc_setup (uint32 ss, uint32 level); +t_stat rtc_srv (UNIT *uptr); +t_stat rtc_reset (DEVICE *dptr); +t_stat rtc_set_freq (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat rtc_show_freq (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat rtc_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +const char *rtc_desc(DEVICE *dptr); + +extern int irq_pend; /* go scan for pending int or I/O */ +extern uint32 INTS[]; /* interrupt control flags */ +extern uint32 SPAD[]; /* computer SPAD */ +extern uint32 M[]; /* system memory */ +extern uint32 outbusy; /* output waiting on timeout */ +extern uint32 inbusy; /* input waiting on timeout */ + +int32 rtc_pie = 0; /* rtc pulse ie */ +int32 rtc_tps = 60; /* rtc ticks/sec */ +int32 rtc_lvl = 0x18; /* rtc interrupt level */ + +/* Clock data structures + rtc_dev RTC device descriptor + rtc_unit RTC unit + rtc_reg RTC register list +*/ + +/* clock can be enabled / disabled */ +/* default to 60 HZ RTC */ +//718UNIT rtc_unit = { UDATA (&rtc_srv, UNIT_IDLE, 0), 16666, UNIT_ADDR(0x7F06)}; +UNIT rtc_unit = { UDATA (&rtc_srv, UNIT_CLK, 0), 16666, UNIT_ADDR(0x7F06)}; + +REG rtc_reg[] = { + { FLDATA (PIE, rtc_pie, 0) }, + { DRDATA (TIME, rtc_unit.wait, 32), REG_NZ + PV_LEFT }, + { DRDATA (TPS, rtc_tps, 8), PV_LEFT + REG_HRO }, + { NULL } + }; + +MTAB rtc_mod[] = { + { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ", + &rtc_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ", + &rtc_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 100, NULL, "100HZ", + &rtc_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 120, NULL, "120HZ", + &rtc_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL, + NULL, &rtc_show_freq, NULL }, + { 0 } + }; + +DEVICE rtc_dev = { + "RTC", &rtc_unit, rtc_reg, rtc_mod, + 1, 8, 8, 1, 8, 8, + NULL, NULL, &rtc_reset, /* examine, deposit, reset */ + NULL, NULL, NULL, /* boot, attach, detach */ + /* dib, dev flags, debug flags, debug */ + NULL, DEV_DEBUG|DEV_DIS|DEV_DISABLE, 0, dev_debug, + NULL, NULL, &rtc_help, /* ?, ?, help */ + NULL, NULL, &rtc_desc, /* ?, ?, description */ + }; + +/* The real time clock runs continuously; therefore, it only has + a unit service routine and a reset routine. The service routine + sets an interrupt that invokes the clock counter. +*/ + +/* service clock signal from simulator */ +t_stat rtc_srv (UNIT *uptr) +{ +#ifdef STOP_CLOCK_INTS_FOR_DEXP_TEST_DEBUGGING + /* stop clock interrupts for dexp debugging */ + rtc_pie = 0; +#endif + /* if clock disabled, do not do interrupts */ + if (((rtc_dev.flags & DEV_DIS) == 0) && rtc_pie) { + int lev = 0x13; + sim_debug(DEBUG_CMD, &rtc_dev, + "RT Clock mfp INTS[%02x] %08x SPAD[%02x] %08x\n", + lev, INTS[lev], lev+0x80, SPAD[lev+0x80]); + sim_debug(DEBUG_CMD, &rtc_dev, + "RT Clock int INTS[%02x] %08x SPAD[%02x] %08x\n", + rtc_lvl, INTS[rtc_lvl], rtc_lvl+0x80, SPAD[rtc_lvl+0x80]); + if (((INTS[rtc_lvl] & INTS_ENAB) || /* make sure enabled */ + (SPAD[rtc_lvl+0x80] & SINT_ENAB)) && /* in spad too */ + (((INTS[rtc_lvl] & INTS_ACT) == 0) || /* and not active */ + ((SPAD[rtc_lvl+0x80] & SINT_ACT) == 0))) { /* in spad too */ +#if 0 + /* HACK for console I/O stopping */ + /* This reduces the number of console I/O stopping errors */ + /* need to find real cause of I/O stopping on clock interrupt */ + if ((outbusy==0) && (inbusy==0)) /* skip interrupt if con I/O in busy wait */ + INTS[rtc_lvl] |= INTS_REQ; /* request the interrupt */ + else + sim_debug(DEBUG_CMD, &rtc_dev, + "RT Clock int console busy\n"); +#else + INTS[rtc_lvl] |= INTS_REQ; /* request the interrupt */ +#endif + irq_pend = 1; /* make sure we scan for int */ + } + sim_debug(DEBUG_CMD, &rtc_dev, + "RT Clock int INTS[%02x] %08x SPAD[%02x] %08x\n", + rtc_lvl, INTS[rtc_lvl], rtc_lvl+0x80, SPAD[rtc_lvl+0x80]); + } +// temp = sim_rtcn_calb(rtc_tps, TMR_RTC); /* timer 0 for RTC */ + sim_rtcn_calb(rtc_tps, TMR_RTC); /* timer 0 for RTC */ + sim_activate_after(uptr, 1000000/rtc_tps); /* reactivate 16666 tics / sec */ + return SCPE_OK; +} + +/* Clock interrupt start/stop */ +/* ss = 1 - starting clock */ +/* ss = 0 - stopping clock */ +/* level = interrupt level */ +void rtc_setup(uint32 ss, uint32 level) +{ + uint32 addr = SPAD[0xf1] + (level<<2); /* vector address in SPAD */ + + rtc_lvl = level; /* save the interrupt level */ + addr = M[addr>>2]; /* get the interrupt context block addr */ + if (ss == 1) { /* starting? */ + INTS[level] |= INTS_ENAB; /* make sure enabled */ + SPAD[level+0x80] |= SINT_ENAB; /* in spad too */ + sim_activate(&rtc_unit, 20); /* start us off */ + sim_debug(DEBUG_CMD, &rtc_dev, + "RT Clock setup enable int %02x rtc_pie %01x ss %01x\n", + rtc_lvl, rtc_pie, ss); + } else { + INTS[level] &= ~INTS_ENAB; /* make sure disabled */ + SPAD[level+0x80] &= ~SINT_ENAB; /* in spad too */ + INTS[level] &= ~INTS_ACT; /* make sure request not active */ + SPAD[level+0x80] &= ~SINT_ACT; /* in spad too */ + sim_debug(DEBUG_CMD, &rtc_dev, + "RT Clock setup disable int %02x rtc_pie %01x ss %01x\n", + rtc_lvl, rtc_pie, ss); + } + rtc_pie = ss; /* set new state */ +} + +/* Clock reset */ +t_stat rtc_reset(DEVICE *dptr) +{ + rtc_pie = 0; /* disable pulse */ + /* initialize clock calibration */ + sim_activate (&rtc_unit, rtc_unit.wait); /* activate unit */ + return SCPE_OK; +} + +/* Set frequency */ +t_stat rtc_set_freq(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + if (cptr) /* if chars, bad */ + return SCPE_ARG; /* ARG error */ + if ((val != 50) && (val != 60) && (val != 100) && (val != 120)) + return SCPE_IERR; /* scope error */ + rtc_tps = val; /* set the new frequency */ + return SCPE_OK; /* we done */ +} + +/* Show frequency */ +t_stat rtc_show_freq (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + /* print the current frequency setting */ + if (rtc_tps < 100) + fprintf (st, (rtc_tps == 50)? "50Hz": "60Hz"); + else + fprintf (st, (rtc_tps == 100)? "100Hz": "120Hz"); + return SCPE_OK; +} + +/* sho help rtc */ +t_stat rtc_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr) +{ + fprintf(st, "SEL 32 IOP/MFP realtime clock at 0x7F06\r\n"); + fprintf(st, "Use:\r\n"); + fprintf(st, " sim> SET RTC [50][60][100][120]\r\n"); + fprintf(st, "to set clock interrupt rate in HZ\r\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +/* device description */ +const char *rtc_desc(DEVICE *dptr) +{ + return "SEL IOP/MFP realtime clock @ address 0x7F06"; +} + +/************************************************************************/ + +/* Interval Timer support */ +int32 itm_src = 0; /* itm source freq 0=itm 1=rtc */ +int32 itm_pie = 0; /* itm pulse enable */ +int32 itm_run = 0; /* itm is running */ +int32 itm_cmd = 0; /* itm last user cmd */ +int32 itm_cnt = 0; /* itm reload pulse count */ +int32 itm_tick_size_x_100 = 3840; /* itm 26042 ticks/sec = 38.4 us per tic */ +int32 itm_lvl = 0x5f; /* itm interrupt level */ +int32 itm_strt = 0; /* clock start time in usec */ +int32 itm_load = 0; /* clock loaded */ +int32 itm_big = 26042 * 6000; /* about 100 minutes */ +t_stat itm_srv (UNIT *uptr); +t_stat itm_set_freq (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat itm_reset (DEVICE *dptr); +t_stat itm_show_freq (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat itm_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +const char *itm_desc(DEVICE *dptr); + +/* Clock data structures + itm_dev Interval Timer ITM device descriptor + itm_unit Interval Timer ITM unit + itm_reg Interval Timer ITM register list +*/ + +/* Mark suggested I remove the UNIT_IDLE flag from ITM. This causes SEL32 */ +/* to use 100% of the CPU instead of waiting and running 10% cpu usage */ +//BAD Mark UNIT itm_unit = { UDATA (&itm_srv, UNIT_IDLE, 0), 26042, UNIT_ADDR(0x7F04)}; +UNIT itm_unit = { UDATA (&itm_srv, 0, 0), 26042, UNIT_ADDR(0x7F04)}; + +REG itm_reg[] = { + { FLDATA (PIE, itm_pie, 0) }, + { FLDATA (CNT, itm_cnt, 0) }, + { FLDATA (CMD, itm_cmd, 0) }, + { DRDATA (TICK_SIZE, itm_tick_size_x_100, 32), PV_LEFT + REG_HRO }, + { NULL } + }; + +MTAB itm_mod[] = { + { MTAB_XTD|MTAB_VDV, 3840, NULL, "3840us", + &itm_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 7680, NULL, "7680us", + &itm_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "RESOLUTION", NULL, + NULL, &itm_show_freq, NULL }, + { 0 } + }; + +DEVICE itm_dev = { + "ITM", &itm_unit, itm_reg, itm_mod, + 1, 8, 8, 1, 8, 8, + NULL, NULL, &itm_reset, /* examine, deposit, reset */ + NULL, NULL, NULL, /* boot, attach, detach */ + /* dib, dev flags, debug flags, debug */ +// NULL, DEV_DEBUG|DEV_DIS|DEV_DISABLE, 0, dev_debug, + NULL, DEV_DEBUG, 0, dev_debug, /* dib, dev flags, debug flags, debug */ + NULL, NULL, &itm_help, /* ?, ?, help */ + NULL, NULL, &itm_desc, /* ?, ?, description */ + }; + +/* The interval timer downcounts the value it is loaded with and + runs continuously; therefore, it has a read/write routine, + a unit service routine and a reset routine. The service routine + sets an interrupt that invokes the clock counter. +*/ + +/* service clock expiration from simulator */ +/* cause interrupt */ +t_stat itm_srv (UNIT *uptr) +{ + if (itm_pie) { /* interrupt enabled? */ + time_t result = time(NULL); + sim_debug(DEBUG_CMD, &itm_dev, + "Intv Timer expired status %08x lev %02x cnt %x @ time %08x\n", + INTS[itm_lvl], itm_lvl, itm_cnt, (uint32)result); + if (((INTS[itm_lvl] & INTS_ENAB) || /* make sure enabled */ + (SPAD[itm_lvl+0x80] & SINT_ENAB)) && /* in spad too */ + (((INTS[itm_lvl] & INTS_ACT) == 0) || /* and not active */ + ((SPAD[itm_lvl+0x80] & SINT_ACT) == 0))) { /* in spad too */ + INTS[itm_lvl] |= INTS_REQ; /* request the interrupt */ + irq_pend = 1; /* make sure we scan for int */ + } + sim_cancel (&itm_unit); /* cancel current timer */ + itm_run = 0; /* timer is no longer running */ + /* if cmd BIT29 is set, reload & restart */ + if ((INTS[itm_lvl] & INTS_ENAB) && (itm_cmd & 0x04) && (itm_cnt != 0)) { + sim_debug(DEBUG_CMD, &itm_dev, + "Intv Timer reload on expired int %02x value %08x src %x\n", + itm_lvl, itm_cnt, itm_src); + /* restart timer with value from user */ + if (itm_src) /* use specified src freq */ + sim_activate_after_abs_d(&itm_unit, ((double)itm_cnt*350000)/rtc_tps); +//DIAG sim_activate_after_abs_d(&itm_unit, ((double)itm_cnt*400000)/rtc_tps); +//DIAG sim_activate_after_abs_d(&itm_unit, ((double)itm_cnt*1000000)/rtc_tps); + else + sim_activate_after_abs_d(&itm_unit, ((double)itm_cnt*itm_tick_size_x_100)/100.0); + itm_run = 1; /* show timer running */ + itm_load = itm_cnt; /* save loaded value */ + itm_strt = 0; /* no negative start time */ + } else { + int32 cnt = itm_big; /* 0x65ba TRY 1,000,000/38.4 10 secs */ + itm_strt = cnt; /* get negative start time */ + sim_debug(DEBUG_CMD, &itm_dev, + "Intv Timer reload for neg cnts on expired int %02x value %08x src %x\n", + itm_lvl, cnt, itm_src); + /* restart timer with large value for negative timer value simulation */ + if (itm_src) /* use specified src freq */ + sim_activate_after_abs_d(&itm_unit, ((double)cnt*1000000)/rtc_tps); + else + sim_activate_after_abs_d(&itm_unit, ((double)cnt*itm_tick_size_x_100) / 100.0); + itm_run = 1; /* show timer running */ + itm_load = cnt; /* save loaded value */ + } + } + return SCPE_OK; +} + +/* ITM read/load function called from CD command processing */ +/* cmd bit assignments */ +/* 0x40 = BIT25 = Read ITM value into R0 at anythime */ +/* 0x20 = BIT26 = Program ITM and BIT27-BIT31 are valid */ +/* 0x10 = BIT27 = =1 start timer, =0 stop timer */ +/* 0x08 = BIT28 = =1 store R0 into ITM, =0 do not alter clock value */ +/* 0x04 = BIT29 = =1 generate multiple ints on countdown to 0, reload start value */ +/* =0 generate single int on countdown to 0, continue counting negative */ +/* 0x02 = BIT30 = BIT30 = 0 BIT31 = 0 = use jumpered clock frequency */ +/* 0x01 = BIT31 = BIT30 = 0 BIT31 = 1 = use jumpered clock frequency */ +/* = BIT30 = 1 BIT31 = 0 = use RT clock frequency 50/60/100/120 HZ */ +/* = BIT30 = 1 BIT31 = 1 = use external clock frequency */ +/* level = interrupt level */ +/* cmd = 0x20 stop timer, do not transfer any value */ +/* = 0x39 load and enable interval timer, no return value */ +/* = 0x3d load and enable interval timer, countdown to zero, interrupt and reload */ +/* = 0x40 read timer value */ +/* = 0x60 read timer value and stop timer */ +/* = 0x79 read/reload and start timer */ +/* cnt = value to write to timer */ +/* ret = return value read from timer */ +int32 itm_rdwr(uint32 cmd, int32 cnt, uint32 level) +{ + uint32 temp; + + cmd &= 0x7f; /* just need the cmd */ + itm_cmd = cmd; /* save last cmd */ + switch (cmd) { + case 0x20: /* stop timer */ + /* stop the timer and save the curr value for later */ + temp = itm_load; /* use last loaded value */ + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%2x kill value %08x (%08d) itm_load %08x\n", + cmd, cnt, cnt, temp); + if (itm_run) { /* if we were running save curr cnt */ + /* read timer value */ + temp = (uint32)(100.0*sim_activate_time_usecs(&itm_unit)/itm_tick_size_x_100); + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%2x temp value %08x (%d)\n", cmd, temp, temp); + if (itm_strt) { /* see if running neg */ + /* we only get here if timer ran out and no reload value */ + /* get simulated negative start time in counts */ + temp = temp - itm_strt; /* make into a negative number */ + } + } + sim_cancel (&itm_unit); /* cancel itc */ + itm_run = 0; /* timer is not running */ + itm_cnt = 0; /* no count reset value */ + itm_load = temp; /* last loaded value */ + itm_strt = 0; /* not restarted neg */ + return 0; /* does not matter, no value returned */ + break; + + case 0x29: /* load new value and start lo rate */ + case 0x28: /* load new value and start hi rate */ + case 0x2a: /* load new value and use RTC */ + case 0x2b: /* load new value and start hi rate */ + case 0x38: /* load new value and start hi rate */ + case 0x39: /* load new value and start lo rate */ + case 0x3a: /* load new value and start hi rate */ + case 0x3b: /* load new value and start lo rate */ + if (itm_run) /* if we were running stop timer */ + sim_cancel (&itm_unit); /* cancel timer */ + itm_run = 0; /* stop timer running */ + if (cmd & 0x10) { /* clock to start? */ + /* start timer with value from user */ + /* if bits 30-31 == 20, use RTC freq */ + itm_src = (cmd>>1)&1; /* set src */ + if (itm_src) /* use specified src freq */ + /* use clock frequency */ + sim_activate_after_abs_d(&itm_unit, ((double)cnt*1000000)/rtc_tps); + else { + /* use interval timer freq */ +#ifdef MAYBE_CHANGE_FOR_MPX3X + /* tsm does not run if fake time cnt is used */ +/// if (cnt == 0) +/// cnt = 0x52f0; + /* this fixes an extra interrupt being generated on context switch */ + /* the value is load for the new task anyway */ + /* need to verify that UTX likes it too */ +/*4MPX3X*/ sim_activate_after_abs_d(&itm_unit, ((double)(cnt+1)*itm_tick_size_x_100)/100.0); +#else + sim_activate_after_abs_d(&itm_unit, ((double)cnt*itm_tick_size_x_100)/100.0); +#endif + } + itm_run = 1; /* set timer running */ + } + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%02x init value %08x (%08d)\n", cmd, cnt, cnt); + itm_cnt = 0; /* no count reset value */ + itm_load = cnt; /* now loaded */ + itm_strt = 0; /* not restarted neg */ + return 0; /* does not matter, no value returned */ + break; + + case 0x70: /* start timer with curr value*/ + case 0x71: /* start timer with curr value */ + case 0x72: /* start timer with RTC value*/ + case 0x74: /* start timer with curr value*/ + case 0x75: /* start timer with curr value */ + case 0x76: /* start timer with RTC value*/ + case 0x30: /* start timer with curr value*/ + case 0x31: /* start timer with curr value*/ + case 0x32: /* start timer with RTC value*/ + case 0x34: /* start timer with curr value*/ + case 0x35: /* start timer with curr value */ + case 0x36: /* start timer with RTC value*/ + case 0x37: /* start timer with curr value */ + temp = itm_load; /* get last loaded value */ + if (itm_run) { /* if we were running save curr cnt */ + /* read timer value */ + temp = (uint32)(100.0*sim_activate_time_usecs(&itm_unit)/itm_tick_size_x_100); + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%2x temp value %08x (%d)\n", cmd, temp, temp); + if (itm_strt) { /* see if running neg */ + /* we only get here if timer ran out and no reload value */ + /* get simulated negative start time in counts */ + temp = temp - itm_strt; /* make into a negative number */ + } + sim_cancel (&itm_unit); /* cancel timer */ + } + /* start timer with current or user value, reload on zero time */ + cnt = temp; /* use current value */ + /* if bits 30-31 == 20, use RTC freq */ + itm_src = (cmd>>1)&1; /* set src */ + if (itm_src) /* use specified src freq */ +//DIAG sim_activate_after_abs_d(&itm_unit, ((double)cnt*400000)/rtc_tps); + sim_activate_after_abs_d(&itm_unit, ((double)cnt*1000000)/rtc_tps); + else + sim_activate_after_abs_d(&itm_unit, ((double)cnt*itm_tick_size_x_100)/100.0); + itm_run = 1; /* set timer running */ + + if (cmd & 0x04) /* do we reload on zero? */ + itm_cnt = cnt; /* count reset value */ + else + itm_cnt = 0; /* no count reset value */ + itm_strt = 0; /* not restarted neg */ + itm_load = cnt; /* now loaded */ + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%02x return value %08x (%08d)\n", cmd, temp, temp); + return temp; /* return curr count */ + break; + + case 0x3c: /* load timer with new value and start */ + case 0x3d: /* load timer with new value and start */ + /* load timer with new value and start using RTC as source */ + case 0x3e: /* load timer with new value and start RTC*/ + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%2x init value %08x (%d)\n", cmd, cnt, cnt); + sim_cancel (&itm_unit); /* cancel timer */ + /* if bits 30-31 == 20, use RTC freq */ + itm_src = (cmd>>1)&1; /* set src */ + if (itm_src) /* use specified src freq */ + sim_activate_after_abs_d(&itm_unit, ((double)cnt*700000)/rtc_tps); + else + sim_activate_after_abs_d(&itm_unit, ((double)cnt*itm_tick_size_x_100)/100.0); + itm_run = 1; /* set timer running */ + + if (cmd & 0x04) /* do we reload on zero? */ + itm_cnt = cnt; /* count reset value */ + itm_strt = 0; /* not restarted neg */ + itm_load = cnt; /* now loaded */ + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%02x return value %08x (%08d)\n", cmd, cnt, cnt); + return 0; /* does not matter, no value returned */ + break; + + case 0x40: /* read the current timer value */ + /* return current count value from timer */ + temp = itm_load; /* get last loaded value */ + if (itm_run) { /* if we were running save curr cnt */ + /* read timer value */ + temp = (uint32)(100.0*sim_activate_time_usecs(&itm_unit)/itm_tick_size_x_100); + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%2x read value %08x (%d)\n", cmd, temp, temp); + if (itm_strt) { /* see if running neg */ + /* we only get here if timer ran out and no reload value */ + /* get simulated negative start time in counts */ + temp = temp - itm_strt; /* make into a negative number */ + } + } + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x40 return value %08x (%d)\n", temp, temp); + return temp; + break; + + case 0x60: /* read and stop timer */ + /* get timer value and stop timer */ + temp = itm_load; /* get last loaded value */ + if (itm_run) { /* if we were running save curr cnt */ + /* read timer value */ + temp = (uint32)(100.0*sim_activate_time_usecs(&itm_unit)/itm_tick_size_x_100); + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%2x read value %08x (%d)\n", cmd, temp, temp); + if (itm_strt) { /* see if running neg */ + /* we only get here if timer ran out and no reload value */ + /* get simulated negative start time in counts */ + temp = temp - itm_strt; /* make into a negative number */ + } + sim_cancel (&itm_unit); /* cancel timer */ + } + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%2x temp value %08x (%d)\n", cmd, temp, temp); + itm_run = 0; /* stop timer running */ + itm_cnt = 0; /* no reload count value */ + itm_load = temp; /* current loaded value */ + itm_strt = 0; /* not restarted neg */ + return temp; /* return current count value */ + break; + + case 0x6a: /* read value & load new one */ + case 0x68: /* read value & load new one */ + case 0x69: /* read value & load new one */ + /* get timer value and load new value, do not start timer */ + temp = itm_load; /* get last loaded value */ + if (itm_run) { /* if we were running save curr cnt */ + /* read timer value */ + temp = (uint32)(100.0*sim_activate_time_usecs(&itm_unit)/itm_tick_size_x_100); + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%2x read value %08x (%d)\n", cmd, temp, temp); + if (itm_strt) { /* see if running neg */ + /* we only get here if timer ran out and no reload value */ + /* get simulated negative start time in counts */ + temp = temp - itm_strt; /* make into a negative number */ + } + sim_cancel (&itm_unit); /* cancel timer */ + } + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%02x temp value %08x (%08d)\n", cmd, temp, temp); + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%02x init value %08x (%08d)\n", cmd, cnt, cnt); + itm_src = (cmd>>1)&1; /* set src */ + itm_run = 0; /* stop timer running */ + itm_cnt = 0; /* no count reset value */ + itm_strt = 0; /* not restarted neg */ + itm_load = cnt; /* now loaded */ + return temp; /* return current count value */ + break; + + case 0x7d: /* read the current timer value */ + case 0x78: /* read the current timer value */ + case 0x79: /* read the current timer value */ + case 0x7a: /* read the current timer value */ + case 0x7b: /* read the current timer value */ + case 0x7c: /* read the current timer value */ + case 0x7e: /* read the current timer value */ + case 0x7f: /* read the current timer value */ + /* get timer value, load new value and start timer */ + temp = itm_load; /* get last loaded value */ + if (itm_run) { /* if we were running save curr cnt */ + /* read timer value */ + temp = (uint32)(100.0*sim_activate_time_usecs(&itm_unit)/itm_tick_size_x_100); + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%2x read value %08x (%d)\n", cmd, temp, temp); + if (itm_strt) { /* see if running neg */ + /* we only get here if timer ran out and no reload value */ + /* get simulated negative start time in counts */ + temp = temp - itm_strt; /* make into a negative number */ + } +//extra sim_cancel (&itm_unit); /* cancel timer */ + } + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%02x temp value %08x (%08d)\n", cmd, temp, temp); + sim_debug(DEBUG_CMD, &itm_dev, + "Intv 0x%02x init value %08x (%08d)\n", cmd, cnt, cnt); + sim_cancel (&itm_unit); /* cancel timer */ + /* start timer to fire after cnt ticks */ + itm_src = (cmd>>1)&1; /* set src */ + if (itm_src) /* use specified src freq */ + sim_activate_after_abs_d(&itm_unit, ((double)cnt*1000000)/rtc_tps); + else + sim_activate_after_abs_d(&itm_unit, ((double)cnt*itm_tick_size_x_100)/100.0); + itm_cnt = 0; /* no count reset value */ + if (cmd & 0x04) /* reload on int? */ + itm_cnt = cnt; /* set reload count value */ + itm_run = 1; /* set timer running */ + itm_strt = 0; /* not restarted neg */ + itm_load = cnt; /* now loaded */ + return temp; /* return current count value */ + break; + default: + sim_debug(DEBUG_CMD, &itm_dev, + "Intv unknown cmd %02x level %02x\n", cmd, level); + break; + } + return 0; /* does not matter, no value returned */ +} + +/* Clock interrupt start/stop */ +/* ss = 1 - clock interrupt enabled */ +/* ss = 0 - clock interrupt disabled */ +/* level = interrupt level */ +void itm_setup(uint32 ss, uint32 level) +{ + itm_lvl = level; /* save the interrupt level */ + itm_load = 0; /* not loaded */ + itm_src = 0; /* use itm for freq */ + itm_strt = 0; /* not restarted neg */ + itm_run = 0; /* not running */ + itm_cnt = 0; /* no count reset value */ + sim_cancel (&itm_unit); /* not running yet */ + if (ss == 1) { /* starting? */ + INTS[level] |= INTS_ENAB; /* make sure enabled */ + SPAD[level+0x80] |= SINT_ENAB; /* in spad too */ + sim_debug(DEBUG_CMD, &itm_dev, + "Intv Timer setup enable int %02x value %08x itm_pie %01x ss %01x\n", + itm_lvl, itm_cnt, itm_pie, ss); + } else { + INTS[level] &= ~INTS_ENAB; /* make sure disabled */ + SPAD[level+0x80] &= ~SINT_ENAB; /* in spad too */ + sim_debug(DEBUG_CMD, &itm_dev, + "Intv Timer setup disable int %02x value %08x itm_pie %01x ss %01x\n", + itm_lvl, itm_cnt, itm_pie, ss); + } + itm_pie = ss; /* set new state */ +} + +/* Clock reset */ +t_stat itm_reset (DEVICE *dptr) +{ + itm_pie = 0; /* disable pulse */ + itm_run = 0; /* not running */ + itm_load = 0; /* not loaded */ + itm_src = 0; /* use itm for freq */ + itm_strt = 0; /* not restarted neg */ + itm_cnt = 0; /* no count reset value */ + sim_cancel (&itm_unit); /* not running yet */ + return SCPE_OK; +} + +/* Set frequency */ +t_stat itm_set_freq (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + if (cptr) /* if chars, bad */ + return SCPE_ARG; /* ARG error */ + if ((val != 3840) && (val != 7680)) + return SCPE_IERR; /* scope error */ + itm_tick_size_x_100 = val; /* set the new frequency */ + return SCPE_OK; /* we done */ +} + +/* Show frequency */ +t_stat itm_show_freq (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + /* print the current interval count setting */ + fprintf (st, "%0.2fus", (itm_tick_size_x_100 / 100.0)); + return SCPE_OK; +} + +/* sho help rtc */ +t_stat itm_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr) +{ + fprintf(st, "SEL 32 IOP/MFP interval timer at 0x7F04\r\n"); + fprintf(st, "Use:\r\n"); + fprintf(st, " sim> SET ITM [3840][7680]\r\n"); + fprintf(st, "to set interval timer clock rate in us x 100\r\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +/* device description */ +const char *itm_desc(DEVICE *dptr) +{ + return "SEL IOP/MFP Interval Timer @ address 0x7F04"; +} + +#endif + diff --git a/SEL32/sel32_com.c b/SEL32/sel32_com.c new file mode 100644 index 00000000..99547523 --- /dev/null +++ b/SEL32/sel32_com.c @@ -0,0 +1,1333 @@ +/* sel32_com.c: SEL 32 8-Line IOP communications controller + + Copyright (c) 2018-2021, James C. Bevier + Portions provided by Richard Cornwell and other SIMH contributers + + 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 + JAMES C. BEVIER 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. + +*/ + +#include "sel32_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include + +#if NUM_DEVS_COM > 0 + +/* Constants */ +#define COM_LINES 8 /* lines defined */ +//Change from 500 to 5000 12/02/2021 +//Change from 5000 to 4000 12/02/2021 +//#define COML_WAIT 500 +#define COML_WAIT 4000 +//Change from 1000 to 5000 12/02/2021 +//#define COM_WAIT 5000 +//#define COM_WAIT 1000 +#define COM_WAIT 5000 +#define COM_NUMLIN com_desc.lines /* curr # lines */ + +#define COMC 0 /* channel thread */ +#define COMI 1 /* input thread */ + +/* Line status */ +#define COML_XIA 0x01 /* xmt intr armed */ +#define COML_XIR 0x02 /* xmt intr req */ +#define COML_REP 0x04 /* rcv enable pend */ +#define COML_RBP 0x10 /* rcv break pend */ + +struct _com_data +{ + uint8 incnt; /* char count */ + uint8 ibuff[120]; /* Input line buffer */ +} +com_data[COM_LINES]; + +uint8 com_rbuf[COM_LINES]; /* rcv buf */ +uint8 com_xbuf[COM_LINES]; /* xmt buf */ +uint8 com_stat[COM_LINES]; /* status */ + +TMLN com_ldsc[COM_LINES] = { 0 }; /* line descrs */ +TMXR com_desc = { COM_LINES, 0, 0, com_ldsc }; /* com descr */ + +#define CMD u3 +/* Held in u3 is the device command and status */ +#define COM_INCH 0x00 /* Initialize channel command */ +#define COM_WR 0x01 /* Write terminal */ +#define COM_RD 0x02 /* Read terminal */ +#define COM_NOP 0x03 /* No op command */ +#define COM_SNS 0x04 /* Sense command */ +#define COM_WRSCM 0x05 /* Write w/Sub chan monitor */ +#define COM_RDECHO 0x06 /* Read with Echo */ +#define COM_RDFC 0x0A /* Read w/flow control */ +#define COM_DEFSC 0x0B /* Define special char */ +#define COM_WRHFC 0x0D /* Write hardware flow control */ +#define COM_RRDFLOW 0x0E /* Read w/hardware flow control only RTS */ +#define COM_RDTR 0x13 /* Reset DTR (ADVR) */ +#define COM_SDTR 0x17 /* Set DTR (ADVF) */ +#define COM_RRTS 0x1B /* Reset RTS */ +#define COM_SRTS 0x1F /* Set RTS */ +#define COM_RBRK 0x33 /* Reset BREAK */ +#define COM_SBRK 0x37 /* Set BREAK */ +#define COM_SETFLOW 0x53 /* Set transparent flow control mode */ +#define COM_RDHFC 0x8E /* Read w/hardware flow control only DTR */ +#define COM_SACE 0xFF /* Set ACE parameters */ + +#define COM_MSK 0xFF /* Command mask */ + +/* Status held in CMD (u3) */ +/* controller/unit address in upper 16 bits */ +#define COM_INPUT 0x0100 /* Input ready for unit */ +//#define COM_RDY 0x0200 /* SNS_DSRS */ /* Device is ready */ +#define COM_SCD 0x0400 /* Special char detect */ +#define COM_EKO 0x0800 /* Echo input character */ +#define COM_OUTPUT 0x1000 /* Output ready for unit */ +#define COM_READ 0x2000 /* Read mode selected */ +#define COM_ACC 0x4000 /* ASCII control char detect */ +#define COM_CONN 0x8000 /* TMXR ATT */ /* Terminal connected */ + +/* ACE data kept in u4 */ +/* ACE information in u4 */ +#define ACE u4 +#if 0 +/* ACE byte 0 Modem Control/Operation status */ +/* stored in u4 bytes 0-3 */ +#define SNS_HALFD 0x80000000 /* Half-duplix operation set */ +#define SNS_MRINGE 0x40000000 /* Modem ring enabled */ +#define SNS_ACEFP 0x20000000 /* Forced parity 0=odd, 1=even */ +#define SNS_ACEP 0x10000000 /* Parity 0=odd, 1=even */ +#define SNS_ACEPE 0x08000000 /* Parity enable 0=dis, 1=enb */ +#define SNS_ACESTOP 0x04000000 /* Stop bit 0=1, 1=1.5 or 2 */ +#define SNS_ACECLEN 0x02000000 /* Character length 00=5, 01=6, 10=7, 11=8 */ +#define SNS_ACECL2 0x01000000 /* 2nd bit for above */ + +/* ACE byte 1 Baud rate */ +#define SNS_NODCDA 0x00800000 /* Enable Delta DCD Attention Interrupt */ +#define SNS_WAITOLB 0x00400000 /* Wait on last byte enabled */ +#define SNS_RINGCR 0x00200000 /* Ring or wakeup character recognition 0=enb, 1=dis */ +#define SNS_DIAGL 0x00100000 /* Set diagnostic loopback */ +#define SNS_BAUD 0x000F0000 /* Baud rate bits 4-7 */ +#define BAUD50 0x00000000 /* 50 baud */ +#define BAUD75 0x00010000 /* 75 baud */ +#define BAUD110 0x00020000 /* 110 baud */ +#define BAUD114 0x00030000 /* 134 baud */ +#define BAUD150 0x00040000 /* 150 baud */ +#define BAUD300 0x00050000 /* 300 baud */ +#define BAUD600 0x00060000 /* 600 baud */ +#define BAUD1200 0x00070000 /* 1200 baud */ +#define BAUD1800 0x00080000 /* 1800 baud */ +#define BAUD2000 0x00090000 /* 2000 baud */ +#define BAUD2400 0x000A0000 /* 2400 baud */ +#define BAUD3600 0x000B0000 /* 3600 baud */ +#define BAUD4800 0x000C0000 /* 4800 baud */ +#define BAUD7200 0x000D0000 /* 7200 baud */ +#define BAUD9600 0x000E0000 /* 9600 baud */ +#define BAUD19200 0x000F0000 /* 19200 baud */ + +/* ACE byte 2 Wake-up character */ +#define ACE_WAKE 0x0000FF00 /* 8 bit wake-up character */ +#endif +#define ACE_WAKE 0x0000FF00 /* 8 bit wake-up character in byte 2 of ACE */ + +#define SNS u5 +/* in u5 packs sense byte 0, 1, 2 and 3 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x80000000 /* Command reject */ +#define SNS_INTVENT 0x40000000 /* Unit intervention required (N/U) */ +#define SNS_BOCHK 0x20000000 /* Bus out check (IOP parity error */ +#define SNS_EQUIPCK 0x10000000 /* Equipment check (device error) */ +#define SNS_DATACK 0x08000000 /* Data check */ +#define SNS_OVERRN 0x04000000 /* Overrun (N/U) */ +#define SNS_NUB01 0x02000000 /* Zero (N/U) */ +#define SNS_RDY SNS_NUB01 /* SNS_RDY device ready */ +#define SNS_NUB02 0x01000000 /* Zero (N/U) */ +#define SNS_CONN SNS_NUB02 /* SNS_CONN device connected */ +/* Sense byte 1 */ +#define SNS_ASCIICD 0x00800000 /* ASCII control char detected interrupt */ +#define SNS_SPCLCD 0x00400000 /* Special char detected interrupt */ +#define SNS_ETX 0x00200000 /* ETX interrupt */ +#define SNS_BREAK 0x00100000 /* BREAK interrupt */ +#define SNS_ACEFE 0x00080000 /* ACE framing error interrupt */ +#define SNS_ACEPEI 0x00040000 /* ACE parity error interrupt */ +#define SNS_ACEOVR 0x00020000 /* ACE overrun error interrupt */ +#define SNS_RING 0x00010000 /* Ring character interrupt */ +/* Sense byte 2 Modem status */ +#define SNS_RLSDS 0x00008000 /* Received line signal detect */ +#define SNS_RINGST 0x00004000 /* Ring indicator signal detect */ +#define SNS_DSRS 0x00002000 /* DSR Data set ready line status */ +#define SNS_CTSS 0x00001000 /* CTS Clear to send line status */ +#define SNS_DELTA 0x00000800 /* Delta receive line signal detect failure interrupt */ +#define SNS_MRING 0x00000400 /* RI Modem ring interrupt */ +#define SNS_DELDSR 0x00000200 /* DSR failure interrupt */ +#define SNS_DELCTS 0x00000100 /* CLS failure interrupt */ +/* Sense byte 3 Modem Control/Operation status */ +#define SNS_HALFD 0x00000080 /* Half-duplix operation set */ +#define SNS_MRINGE 0x00000040 /* Modem ring enabled (1) */ +#define SNS_ACEDEF 0x00000020 /* ACE parameters defined */ +#define SNS_DIAGM 0x00000010 /* Diagnostic mode set */ +#define SNS_AUXOL2 0x00000008 /* Auxiliary output level 2 */ +#define SNS_AUXOL1 0x00000004 /* Auxiliary output level 1 */ +#define SNS_RTS 0x00000002 /* RTS Request to send set */ +#define SNS_DTR 0x00000001 /* DTR Data terminal ready set */ +/* Sense byte 4 ACE Parameters status */ +#define SNS_ACEDLE 0x80000000 /* Divisor latch enable 0=dis, 1=enb */ +#define SNS_ACEBS 0x40000000 /* Break set 0=reset, 1=set */ +#define SNS_ACEFP 0x20000000 /* Forced parity 0=odd, 1=even */ +#define SNS_ACEP 0x10000000 /* Parity 0=odd, 1=even */ +#define SNS_ACEPE 0x08000000 /* Parity enable 0=dis, 1=enb */ +#define SNS_ACESTOP 0x04000000 /* Stop bit 0=1, 1=1.5 or 2 */ +#define SNS_ACECLEN 0x02000000 /* Character length 00=5, 01=6, 11=7, 11=8 */ +#define SNS_ACECL2 0x01000000 /* 2nd bit for above */ +/* Sense byte 5 Baud rate */ +#define SNS_NODCDA 0x00800000 /* Enable Delta DCD Attention Interrupt */ +#define SNS_WAITOLB 0x00400000 /* Wait on last byte enabled */ +#define SNS_RINGCR 0x00200000 /* Ring or wakeup character recognition 0=enb, 1=dis */ +#define SNS_DIAGL 0x00100000 /* Set diagnostic loopback */ +#define SNS_BAUD 0x000F0000 /* Baud rate bits 4-7 */ +#define BAUD50 0x00000000 /* 50 baud */ +#define BAUD75 0x00010000 /* 75 baud */ +#define BAUD110 0x00020000 /* 110 baud */ +#define BAUD114 0x00030000 /* 134 baud */ +#define BAUD150 0x00040000 /* 150 baud */ +#define BAUD300 0x00050000 /* 300 baud */ +#define BAUD600 0x00060000 /* 600 baud */ +#define BAUD1200 0x00070000 /* 1200 baud */ +#define BAUD1800 0x00080000 /* 1800 baud */ +#define BAUD2000 0x00090000 /* 2000 baud */ +#define BAUD2400 0x000A0000 /* 2400 baud */ +#define BAUD3600 0x000B0000 /* 3600 baud */ +#define BAUD4800 0x000C0000 /* 4800 baud */ +#define BAUD7200 0x000D0000 /* 7200 baud */ +#define BAUD9600 0x000E0000 /* 9600 baud */ +#define BAUD19200 0x000F0000 /* 19200 baud */ +/* Sense byte 6 Firmware ID, Revision Level */ +#define SNS_FID 0x00006200 /* ID part 1 */ +/* Sense byte 7 Firmware ID, Revision Level */ +#define SNS_REV 0x0000004f /* ID part 2 plus 4 bit rev # */ + +/* u6 */ +#define CNT u6 + +/* forward definitions */ +t_stat coml_preio(UNIT *uptr, uint16 chan); +t_stat coml_startcmd(UNIT *uptr, uint16 chan, uint8 cmd); +t_stat coml_haltio(UNIT *uptr); +void com_ini(UNIT *, t_bool); +void coml_ini(UNIT *, t_bool); +t_stat coml_rschnlio(UNIT *uptr); +t_stat com_rschnlio(UNIT *uptr); +t_stat comi_srv(UNIT *uptr); +t_stat como_srv(UNIT *uptr); +t_stat comc_srv(UNIT *uptr); +t_stat com_reset(DEVICE *dptr); +t_stat com_attach(UNIT *uptr, CONST char *cptr); +t_stat com_detach(UNIT *uptr); +void com_reset_ln(int32 ln); +const char *com_description(DEVICE *dptr); /* device description */ + +/* COM data structures + com_chp COM channel program information + com_dev COM device descriptor + com_unit COM unit descriptor + com_reg COM register list + com_mod COM modifiers list +*/ + +//#define COM_UNITS 2 +#define COM_UNITS 1 + +/* channel program information */ +CHANP com_chp[COM_UNITS] = {0}; + +/* dummy mux for 16 lines */ +MTAB com_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, &show_dev_addr, NULL}, + {MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, &com_desc}, + {UNIT_ATT, UNIT_ATT, "SUMMARY", NULL, NULL, &tmxr_show_summ, (void *) &com_desc}, + {MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat,(void *)&com_desc}, + {MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *)&com_desc}, + { 0 } +}; + +UNIT com_unit[] = { + {UDATA(&comc_srv, UNIT_ATTABLE|UNIT_IDLE, 0), COM_WAIT, UNIT_ADDR(0x0000)}, /* 0 */ +}; + +DIB com_dib = { + NULL, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + NULL, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + NULL, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + com_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + NULL, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + com_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + com_unit, /* UNIT* units */ /* Pointer to units structure */ + com_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + COM_UNITS, /* uint8 numunits */ /* number of units defined */ + 0x0f, /* uint8 mask */ /* 16 devices - device mask */ + 0x7E00, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +REG com_reg[] = { + { BRDATAD (STA, com_stat, 16, 8, COM_LINES, "status buffers, lines 0 to 7") }, + { BRDATAD (RBUF, com_rbuf, 16, 8, COM_LINES, "input buffer, lines 0 to 7") }, + { BRDATAD (XBUF, com_xbuf, 16, 8, COM_LINES, "output buffer, lines 0 to 7") }, + { NULL } + }; + +/* devices for channel 0x7ecx */ +DEVICE com_dev = { + "COMC", com_unit, com_reg, com_mod, + COM_UNITS, 8, 15, 1, 8, 8, + &tmxr_ex, &tmxr_dep, &com_reset, NULL, &com_attach, &com_detach, + /* ctxt is the DIB pointer */ + &com_dib, DEV_MUX|DEV_DISABLE|DEV_DEBUG, 0, dev_debug, + NULL, NULL, NULL, NULL, NULL, &com_description +}; + +/* COML data structures + coml_dev COM device descriptor + coml_unit COM unit descriptor + coml_reg COM register list + coml_mod COM modifiers list +*/ + +/*#define UNIT_COML UNIT_ATTABLE|UNIT_DISABLE|UNIT_IDLE */ +//#define UNIT_COML UNIT_IDLE|UNIT_DISABLE|TT_MODE_UC +//#define UNIT_COML UNIT_IDLE|UNIT_DISABLE|TT_MODE_7P +//#define UNIT_COML UNIT_IDLE|UNIT_DISABLE|TT_MODE_8B +#define UNIT_COML UNIT_IDLE|UNIT_DISABLE|TT_MODE_7B + +/* channel program information */ +CHANP coml_chp[COM_LINES*2] = {0}; + +UNIT coml_unit[] = { + /* 0-7 is input, 8-f is output */ + {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA0)}, /* 0 */ + {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA1)}, /* 1 */ + {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA2)}, /* 2 */ + {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA3)}, /* 3 */ + {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA4)}, /* 4 */ + {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA5)}, /* 5 */ + {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA6)}, /* 6 */ + {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA7)}, /* 7 */ + {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA8)}, /* 8 */ + {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA9)}, /* 9 */ + {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EAA)}, /* A */ + {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EAB)}, /* B */ + {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EAC)}, /* C */ + {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EAD)}, /* D */ + {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EAE)}, /* E */ + {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EAF)}, /* F */ +}; + +DIB coml_dib = { + coml_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + coml_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + coml_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + coml_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + NULL, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + coml_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + coml_unit, /* UNIT* units */ /* Pointer to units structure */ + coml_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + COM_LINES*2, /* uint8 numunits */ /* number of units defined */ + 0x0f, /* uint8 mask */ /* 16 devices - device mask */ + 0x7E00, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +REG coml_reg[] = { + { URDATA (TIME, coml_unit[0].wait, 10, 24, 0, COM_LINES, REG_NZ + PV_LEFT) }, + { NULL } +}; + +MTAB coml_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, &show_dev_addr, NULL}, + { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, + { 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_VUN, 0, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &com_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", + &tmxr_set_log, &tmxr_show_log, &com_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", + &tmxr_set_nolog, NULL, &com_desc }, + { 0 } + }; + +DEVICE coml_dev = { + "COML", coml_unit, coml_reg, coml_mod, + COM_LINES*2, 10, 31, 1, 8, 8, + NULL, NULL, &com_reset, + NULL, NULL, NULL, + /* ctxt is the DIB pointer */ + &coml_dib, DEV_DISABLE|DEV_DEBUG, 0, dev_debug, + NULL, NULL, NULL, NULL, NULL, &com_description +}; + +/* 8-line serial routines */ +void coml_ini(UNIT *uptr, t_bool f) +{ + /* set SNS_RLSDS SNS_DSRS SNS_CTSS SNS_RTS SNS_CTS */ + uptr->SNS = 0x0000b003; /* status is online & ready */ + uptr->CMD &= LMASK; /* leave only chsa */ + sim_cancel(uptr); /* stop any timer */ +} + +/* handle rschnlio cmds for coml */ +t_stat coml_rschnlio(UNIT *uptr) { + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & COM_MSK; + + sim_debug(DEBUG_EXP, dptr, + "coml_rschnl chsa %04x cmd = %02x\n", chsa, cmd); + coml_ini(uptr, 0); /* reset the unit */ + return SCPE_OK; +} + +/* 8-line serial routines */ +void com_ini(UNIT *uptr, t_bool f) +{ + DEVICE *dptr = get_dev(uptr); + + sim_debug(DEBUG_CMD, dptr, + "COM init device %s controller 0x7e00\n", dptr->name); + sim_cancel(uptr); /* stop input poll */ + sim_activate(uptr, 1000); /* start input poll */ +} + +/* handle rschnlio cmds for com */ +t_stat com_rschnlio(UNIT *uptr) { + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & COM_MSK; + + sim_debug(DEBUG_EXP, dptr, + "com_rschnl chsa %04x cmd = %02x\n", chsa, cmd); + com_ini(uptr, 0); /* reset the unit */ + return SCPE_OK; +} + +/* start a com operation */ +t_stat coml_preio(UNIT *uptr, uint16 chan) { + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); + uint16 chsa = GET_UADDR(uptr->CMD); /* get channel/sub-addr */ + UNIT *ruptr = &dptr->units[unit&7]; /* read uptr */ + UNIT *wuptr = &dptr->units[(unit&7)+8]; /* write uptr */ + + sim_debug(DEBUG_CMD, dptr, + "coml_preio CMD %08x unit %02x chsa %04x\n", + uptr->CMD, unit, chsa); + sim_debug(DEBUG_CMD, dptr, + "coml_preio chsa %04x ln %1x conn %x rcve %x xmte %x SNS %08x SNS %08x\n", + chsa, unit, com_ldsc[unit&7].conn, com_ldsc[unit&7].rcve, + com_ldsc[unit&7].xmte, ruptr->SNS, wuptr->SNS); + + if ((uptr->CMD & COM_MSK) != 0) { /* just return if busy */ + sim_debug(DEBUG_CMD, dptr, + "coml_preio unit %02x chsa %04x BUSY\n", unit, chsa); + return SNS_BSY; + } + + sim_debug(DEBUG_CMD, dptr, + "coml_preio unit %02x chsa %04x OK\n", unit, chsa); + return SCPE_OK; /* good to go */ +} + +/* called from sel32_chan to start an I/O operation */ +t_stat coml_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) +{ + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); +// int unit = (uptr - dptr->units) & 0x7; /* make 0-7 */ + UNIT *ruptr = &dptr->units[unit&7]; /* read uptr */ + UNIT *wuptr = &dptr->units[(unit&7)+8]; /* write uptr */ + uint16 chsa = (((uptr->CMD & LMASK) >> 16) | (chan << 8)); + uint8 ch, fcb[3]; + + if ((uptr->CMD & COM_MSK) != 0) { /* is unit busy */ + return SNS_BSY; /* yes, return busy */ + } + + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x line %1x cmd %02x conn %x rcve %x xmte %x SNS %08x SNS %08x\n", + chsa, unit, cmd, com_ldsc[unit&7].conn, com_ldsc[unit&7].rcve, + com_ldsc[unit&7].xmte, ruptr->SNS, wuptr->SNS); + + uptr->CMD &= LMASK; /* clear any flags that are set */ + /* process the commands */ + switch (cmd & 0xFF) { + case COM_INCH: /* 00 */ /* INCH command */ + sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: CMD INCH\n", chsa); + uptr->CMD &= LMASK; /* leave only chsa */ + uptr->CMD |= (0x7f & COM_MSK); /* save 0x7f as INCH cmd command */ + uptr->SNS |= SNS_RDY; /* status is online & ready */ + sim_activate(uptr, 500); /* start us up */ + break; + + /* write commands must use address 8-f */ + case COM_WR: /* 0x01 */ /* Write command */ + case COM_WRSCM: /* 0x05 */ /* Write w/ input sub channel monitor */ + case COM_WRHFC: /* 0x0D */ /* Write w/hardware flow control only */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd WRITE %02x\n", chsa, cmd); + + /* see if DSR is set, if not give unit check error */ + if (((ruptr->SNS & SNS_DSRS) == 0) || ((ruptr->SNS & SNS_CONN) == 0)) { +//YY if ((com_ldsc[unit&7].conn == 0) || + ruptr->SNS &= ~SNS_RDY; /* status is not ready */ + wuptr->SNS &= ~SNS_RDY; /* status is not ready */ + ruptr->SNS |= SNS_CMDREJ; /* command reject */ + wuptr->SNS |= SNS_CMDREJ; /* command reject */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd WRITE %02x unit check\n", chsa, cmd); + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; /* good return */ + } + uptr->CMD &= LMASK; /* leave only chsa */ + uptr->CMD |= (cmd & COM_MSK); /* save command */ + uptr->SNS |= SNS_RDY; /* status is online & ready */ + sim_activate(uptr, 250); /* TRY 08-13-18 */ + return 0; /* no status change */ + break; + + /* read commands must use address 0-7 */ + /* DSR must be set when a read command is issued, else it is unit check */ + /* bit 1-3 (ASP) of command has more definition */ + /* bit 1 A=1 ASCII control character detect (7-char mode only) */ + /* bit 2 S=1 Special character detect (7-char mode only) */ + /* bit 3 P=1 Purge input buffer */ + case COM_RD: /* 0x02 */ /* Read command */ + case 0x22: /* 0x22 */ /* Read command w/ASCII CC */ + case 0x32: /* 0x32 */ /* Read command w/ASCII CC & Purge input */ + case COM_RDECHO: /* 0x06 */ /* Read command w/ECHO */ + case 0x46: /* 0x46 */ /* Read command w/ECHO & ASCII CC*/ + case 0x56: /* 0x56 */ /* Read command w/ECHO & ASCII CC & Purge input */ + /* if bit 0 set for COM_RDFC, use DTR for flow, else use RTS for flow control */ + case COM_RDFC: /* 0x0A */ /* Read command w/flow control */ + case COM_RDHFC: /* 0x8E */ /* Read command w/hardware flow control only */ + + /* see if DSR is set, if not give unit check error */ + if (((ruptr->SNS & SNS_DSRS) == 0) || ((ruptr->SNS & SNS_CONN) == 0)) { +//XX if (com_ldsc[unit&7].conn == 0) { + ruptr->SNS &= ~SNS_RDY; /* status is not ready */ + wuptr->SNS &= ~SNS_RDY; /* status is not ready */ + ruptr->SNS |= SNS_CMDREJ; /* command reject */ + wuptr->SNS |= SNS_CMDREJ; /* command reject */ + /* SNS_DSRS will be 0 */ +/*UTX*/ ruptr->SNS |= SNS_DELDSR; /* set attention status */ +/*UTX*/ wuptr->SNS |= SNS_DELDSR; /* set attention status */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd READ %02x unit check\n", chsa, cmd); + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; /* good return */ + } + unit &= 0x7; /* make unit 0-7 */ + uptr->CMD &= ~COM_EKO; /* clear echo status */ + sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: Cmd read\n", chsa); + uptr->CMD &= LMASK; /* leave only chsa */ + uptr->CMD |= (cmd & COM_MSK); /* save command */ + if ((cmd & 0x0f) == COM_RDECHO) /* echo command? */ + uptr->CMD |= COM_EKO; /* save echo status */ + if (cmd & 0x10) { /* purge input request? */ + uptr->CNT = 0; /* no input count */ + com_data[unit].incnt = 0; /* no input data */ + com_rbuf[unit&7] = 0; /* clear read buffer */ + } + uptr->CMD |= COM_READ; /* show read mode */ + uptr->SNS |= SNS_RDY; /* status is online & ready */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: input cnt = %04x\n", + chsa, coml_chp[unit].ccw_count); + sim_activate(uptr, 250); /* TRY 08-13-18 */ + return 0; + break; + + case COM_NOP: /* 0x03 */ /* NOP has do nothing */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd %02x NOP\n", chsa, cmd); + uptr->SNS |= SNS_RDY; /* status is online & ready */ + uptr->CMD &= LMASK; /* leave only chsa */ + uptr->CMD |= (cmd & COM_MSK); /* save command */ + sim_activate(uptr, 250); /* start us up */ + break; + + case COM_SNS: /* 0x04 */ /* Sense (8 bytes) */ + unit &= 0x7; /* make unit 0-7 */ + /* status is in SNS (u5) */ + /* ACE is in ACE (u4) */ +///*MPX*/ uptr->SNS = 0x03813401; + + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd SENSE chsa %04x: unit %02x Cmd Sense SNS %08x ACE %08x\n", + chsa, unit, uptr->SNS, uptr->ACE); + + /* byte 0 device status */ + ch = (uptr->SNS >> 24) & 0xff; /* no bits in byte 0 */ + chan_write_byte(chsa, &ch); /* write status */ + + /* byte 1 line status and error conditions */ + ch = (uptr->SNS >> 16) & 0xff; /* no bits in byte 1 */ + chan_write_byte(chsa, &ch); /* write status */ + + /* byte 2 modem status */ + // SNS_DELDSR will be set if just connected, clear at end + ch = (uptr->SNS >> 8) & 0xff; /* CTS & DSR bits in byte 2 */ + chan_write_byte(chsa, &ch); /* write status */ + + /* byte 3 modem control/operation mode */ + ch = uptr->SNS & 0xff; /* maybe DTR bit in byte 3 */ + chan_write_byte(chsa, &ch); /* write status */ + + /* byte 4 ACE byte 0 parameters (parity, stop bits, char len */ + ch = (uptr->ACE >> 24) & 0xff; /* ACE byte 0 */ + chan_write_byte(chsa, &ch); /* write status */ + + /* byte 5 ACE byte 1 parameters (baud rate) */ + ch = (uptr->ACE >> 16) & 0xff; /* ACE byte 1 */ + chan_write_byte(chsa, &ch); /* write status */ + + /* byte 6 ACE parameters (Firmware ID 0x62) */ + ch = 0x62; /* ACE IOP firmware byte 0 */ +// ch = 0x19; /* ACE MFP firmware byte 0 */ + chan_write_byte(chsa, &ch); /* write status */ + + /* byte 7 ACE parameters (Revision Level 0x4?) */ +// Firmware 0x44 supports RTS flow control */ +// Firmware 0x45 supports DCD modem control */ +// ch = 0x44; /* ACE firmware byte 1 */ +// ch = 0x45; /* ACE firmware byte 1 */ + ch = 0x43; /* ACE firmware byte 1 */ +// ch = 0x40; /* ACE firmware byte 1 */ + chan_write_byte(chsa, &ch); /* write status */ + + ruptr->SNS &= ~SNS_RING; /* reset ring attention status */ + ruptr->SNS &= ~SNS_MRING; /* reset ring attention status */ + ruptr->SNS &= ~SNS_ASCIICD; /* reset ASCII attention status */ + ruptr->SNS &= ~SNS_DELDSR; /* reset attention status */ +//MPX ruptr->SNS &= ~SNS_RLSDS; /* reset rec'd line signal detect */ + ruptr->SNS &= ~SNS_CMDREJ; /* command reject */ +/*MPX*/ ruptr->SNS &= ~SNS_DELTA; /* reset attention status */ + + wuptr->SNS &= ~SNS_RING; /* reset ring attention status */ + wuptr->SNS &= ~SNS_MRING; /* reset ring attention status */ + wuptr->SNS &= ~SNS_ASCIICD; /* reset ASCII attention status */ + wuptr->SNS &= ~SNS_DELDSR; /* reset attention status */ +//1X wuptr->SNS &= ~SNS_RLSDS; /* reset rec'd line signal detect */ + wuptr->SNS &= ~SNS_CMDREJ; /* command reject */ +/*MPX*/ wuptr->SNS &= ~SNS_DELTA; /* reset attention status */ + + uptr->CMD &= LMASK; /* nothing left, command complete */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd CMD SENSE return chsa %04x: unit %02x Cmd Sense SNS %08x ACE %08x\n", + chsa, unit, uptr->SNS, uptr->ACE); + return SNS_CHNEND|SNS_DEVEND; /* good return */ + break; + + case COM_DEFSC: /* 0x0B */ /* Define special char */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd %02x DEFSC\n", chsa, cmd); + if (chan_read_byte(GET_UADDR(uptr->CMD), &ch)) { /* read char from memory */ + /* nothing to read, error */ + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; /* good return */ + } + uptr->ACE &= ~ACE_WAKE; /* clear out old wake char */ + uptr->ACE |= ((uint32)ch << 8); /* insert special char */ + ruptr->ACE = uptr->ACE; /* set special char in read unit */ + wuptr->ACE = uptr->ACE; /* set special char in write unit */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd %02x DEFSC char %02x SNS %08x ACE %08x\n", + chsa, cmd, ch, uptr->SNS, uptr->ACE); + return SNS_CHNEND|SNS_DEVEND; /* good return */ + break; + + case COM_RRTS: /* 0x1B */ /* Reset RTS */ + sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: Cmd %02x RRTS\n", chsa, cmd); + uptr->SNS &= ~SNS_RTS; /* Request to send not ready */ + return SNS_CHNEND|SNS_DEVEND; /* good return */ + break; + + case COM_SRTS: /* 0x1F */ /* Set RTS */ + sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: Cmd %02x SRTS\n", chsa, cmd); + uptr->SNS |= SNS_RTS; /* Request to send ready */ + return SNS_CHNEND|SNS_DEVEND; /* good return */ + break; + + case COM_RBRK: /* 0x33 */ /* Reset BREAK */ + sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: Cmd %02x RBRK\n", chsa, cmd); + uptr->SNS &= ~SNS_BREAK; /* Request to send not ready */ + return SNS_CHNEND|SNS_DEVEND; /* good return */ + break; + + case COM_SBRK: /* 0x37 */ /* Set BREAK */ + sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: Cmd %02x SBRK\n", chsa, cmd); + uptr->SNS |= SNS_BREAK; /* Requestd to send ready */ + return SNS_CHNEND|SNS_DEVEND; /* good return */ + break; + + case COM_RDTR: /* 0x13 */ /* Reset DTR (ADVR) */ + sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: Cmd %02x RDTR\n", chsa, cmd); + uptr->SNS &= ~SNS_DTR; /* Data terminal not ready */ + return SNS_CHNEND|SNS_DEVEND; /* good return */ + break; + + case COM_SDTR: /* 0x17 */ /* Set DTR (ADVF) */ + sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: Cmd %02x SDTR\n", chsa, cmd); + uptr->SNS |= SNS_DTR; /* Data terminal ready */ + return SNS_CHNEND|SNS_DEVEND; /* good return */ + break; + + case COM_SACE: /* 0xff */ /* Set ACE parameters (3 chars) */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd %02x SACE\n", chsa, cmd); + + if (chan_read_byte(GET_UADDR(uptr->CMD), &ch)) { /* read char 0 */ + /* nothing to read, error */ + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; /* good return */ + } + uptr->ACE = ((uint32)ch)<<24; /* byte 0 of ACE data */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd %02x ACE byte 0 %02x\n", + chsa, cmd, ch); + + if (chan_read_byte(GET_UADDR(uptr->CMD), &ch)) { /* read char 1 */ + /* nothing to read, error */ + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; /* good return */ + } + uptr->ACE |= ((uint32)ch)<<16; /* byte 1 of ACE data */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd %02x ACE byte 1 %02x\n", + chsa, cmd, ch); + + if (chan_read_byte(GET_UADDR(uptr->CMD), &ch)) { /* read char 2 */ + /* nothing to read, error */ + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; /* good return */ + } + uptr->ACE |= ((uint32)ch)<<8; /* byte 2 of ACE data */ + uptr->SNS |= SNS_ACEDEF; /* show ACE defined */ + if (uptr->SNS & SNS_CONN) { + if (!(uptr->ACE & SNS_MRINGE)) { /* see if RING enabled */ + uptr->SNS |= (SNS_DTR | SNS_RTS); /* set DTR & DSR if yes */ + } + } + ruptr->SNS |= SNS_RDY; /* status is online & ready */ + if (uptr == wuptr) { + ruptr->ACE = uptr->ACE; /* set ACE in read uptr */ + ruptr->SNS = uptr->SNS; /* set status to read uptr */ + } else { + wuptr->ACE = uptr->ACE; /* set ACE in write uptr */ + wuptr->SNS = uptr->SNS; /* set status to write uptr */ + } + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd %02x ACE byte 2 %02x\n", + chsa, cmd, ch); + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd ACE DONE chsa %04x: Cmd %02x ACE bytes %08x\n", + chsa, cmd, uptr->ACE); + + uptr->CMD &= LMASK; /* nothing left, command complete */ + return SNS_CHNEND|SNS_DEVEND; /* good return */ + break; + + /* Set transparent flow control mode */ + case COM_SETFLOW: /* 0x53 */ /* Set flow control (3 chars) */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd %02x SETFLOW\n", chsa, cmd); + + if (chan_read_byte(GET_UADDR(uptr->CMD), &ch)) { /* read char 0 */ + /* nothing to read, error */ + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; /* good return */ + } + fcb[0] = ch; /* byte 0 of Flow Cont data */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd %02x SETFLOW byte 0 %02x\n", + chsa, cmd, ch); + + if (chan_read_byte(GET_UADDR(uptr->CMD), &ch)) { /* read char 1 */ + /* nothing to read, error */ + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; /* good return */ + } + fcb[1] = ch; /* byte 1 of Flow Cont data */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd %02x SETFLOW byte 1 %02x\n", + chsa, cmd, ch); + + if (chan_read_byte(GET_UADDR(uptr->CMD), &ch)) { /* read char 2 */ + /* nothing to read, error */ + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; /* good return */ + } + fcb[2] = ch; /* byte 2 of Flow Cont data */ + ruptr->SNS |= SNS_RDY; /* status is online & ready */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd %02x SETFLOW byte 2 %02x\n", + chsa, cmd, ch); + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd SETFLOW DONE chsa %04x: Cmd %02x FCB bytes %02x%02x%02x\n", + chsa, cmd, fcb[0], fcb[1], fcb[2]); + + uptr->CMD &= LMASK; /* nothing left, command complete */ + return SNS_CHNEND|SNS_DEVEND; /* good return */ + break; + default: /* invalid command */ + uptr->SNS |= SNS_CMDREJ; /* command rejected */ + sim_debug(DEBUG_CMD, dptr, + "coml_startcmd chsa %04x: Cmd Invalid %02x status %02x\n", + chsa, cmd, uptr->u5); + return SNS_CHNEND|SNS_DEVEND|STATUS_PCHK; /* program check */ + break; + } + + if (uptr->u5 & 0xff) + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + return SNS_CHNEND|SNS_DEVEND; +} + +/* Unit service - polled input + Poll for new connections + Poll all connected lines for input +*/ +t_stat comc_srv(UNIT *uptr) +{ + uint8 ch; + DEVICE *dptr = get_dev(uptr); + int32 newln, ln, c; +// int cmd = uptr->CMD & 0xff; + uint16 chsa = GET_UADDR(coml_unit[0].CMD); /* get channel/sub-addr */ + + /* see if comc attached */ + if ((com_unit[COMC].flags & UNIT_ATT) == 0){ /* attached? */ + return SCPE_OK; + } + /* poll for any input from com lines, units 0-7 */ + newln = tmxr_poll_conn(&com_desc); /* look for connect */ + if (newln >= 0) { /* rcv enb pending? */ + uint16 chsa = GET_UADDR(coml_unit[newln].CMD); /* get read channel/sub-addr */ + uint16 wchsa = GET_UADDR(coml_unit[newln+8].CMD); /* get write channel/sub-addr */ + UNIT *nuptr = coml_unit+newln; /* get uptr for coml line */ + UNIT *wuptr = coml_unit+newln+8; /* get output uptr for coml line */ + com_ldsc[newln].rcve = 1; /* enable rcv */ + com_ldsc[newln].xmte = 1; /* enable xmt for output line */ + com_stat[newln] |= COML_RBP; /* connected */ + com_stat[newln] &= ~COML_REP; /* not pending */ + + sim_debug(DEBUG_CMD, &com_dev, + "comc_srv conn b4 wakeup on read chsa %04x line %02x SNS %08x ACE %08x\n", + chsa, newln, nuptr->SNS, nuptr->ACE); + sim_debug(DEBUG_CMD, &com_dev, + "comc_srv conn b4 wakeup on write chsa %04x line %02x SNS %08x ACE %08x\n", + wchsa, newln+8, wuptr->SNS, wuptr->ACE); + + /* send attention to OS here for this channel */ + /* set DSR, CTS and delta DSR status */ + nuptr->SNS |= SNS_CONN; /* status is now connected */ + /* UTX says this is an error if set, so do not set SNS_DELDSR */ +/*MPX*/ nuptr->SNS |= (SNS_DSRS | SNS_CTSS | SNS_RING); /* set the read bits */ + nuptr->SNS |= (SNS_RTS | SNS_DTR); /* set RTS & DTR */ +/*MPX*/ nuptr->SNS |= SNS_MRING; /* set RING interrupt */ + if (nuptr->SNS & SNS_ACEDEF) { /* ACE defined */ + /* this must be set to login for UTX after system is up */ +/*UTX*/ nuptr->SNS |= SNS_DELDSR; /* set delta dsr status */ + nuptr->SNS |= SNS_RLSDS; /* set rec'd line signal detect */ + } else { + nuptr->SNS |= SNS_DELDSR; /* set delta dsr status */ + nuptr->SNS |= SNS_RLSDS; /* set rec'd line signal detect */ + } + nuptr->SNS &= ~SNS_CMDREJ; /* no command reject */ + wuptr->SNS = nuptr->SNS; /* set write line too */ + wuptr->ACE = nuptr->ACE; /* set write line too */ + sim_debug(DEBUG_CMD, &com_dev, + "comc_srv conn wakeup on chsa %04x line %02x SNS %08x ACE %08x\n", + chsa, newln, nuptr->SNS, nuptr->ACE); + set_devwake(chsa, SNS_ATTN|SNS_DEVEND|SNS_CHNEND); /* tell user */ + } + /* poll all devices for input */ + tmxr_poll_rx(&com_desc); /* poll for input */ + for (ln = 0; ln < COM_NUMLIN; ln++) { /* loop thru lines */ + UNIT *nuptr = coml_unit+ln; /* get uptr for coml line */ + int cmd = nuptr->CMD & 0xff; /* get the active cmd */ + uint16 chsa = GET_UADDR(nuptr->CMD); /* get channel/sub-addr */ + + if (com_ldsc[ln].conn) /* connected? */ + sim_debug(DEBUG_CMD, &com_dev, + "comc_srv conn poll input chsa %04x line %02x SNS %08x ACE %08x\n", + chsa, ln, nuptr->SNS, nuptr->ACE); + + if ((com_ldsc[ln].conn) && /* connected? */ + (c = tmxr_getc_ln(&com_ldsc[ln]))) { /* get char */ + ch = c & 0x7f; + if (ch == '\n') /* convert newline */ + ch = '\r'; /* to C/R */ + sim_debug(DEBUG_CMD, &com_dev, + "comc_srv read %02x (%02x) chsa %04x line %02x SNS %08x ACE %08x CMD %08x\n", + c, ch, chsa, ln, nuptr->SNS, nuptr->ACE, nuptr->CMD); + /* tmxr says break is 0x80??, but SCPE_BREAK is 0x800000?? */ + if (c & SCPE_BREAK) { /* break? */ + nuptr->SNS |= SNS_BREAK; /* set received break bit */ + com_stat[ln] |= COML_RBP; /* set rcv brk */ + set_devwake(chsa, SNS_ATTN|SNS_DEVEND|SNS_CHNEND); + continue; + } + /* normal char */ + nuptr->SNS &= ~SNS_BREAK; /* reset received break bit */ + com_stat[ln] &= ~COML_RBP; /* clr rcv brk */ + + /* convert to user requested input */ + ch = sim_tt_inpcvt(ch, TT_GET_MODE(coml_unit[ln].flags)); + com_rbuf[ln] = ch; /* save char */ + + /* Special char detect? */ + if ((ch & 0x7f) == ((nuptr->ACE >> 8) & 0xff)) { /* is it spec char */ + nuptr->CMD |= COM_SCD; /* set special char detected */ + nuptr->SNS |= SNS_SPCLCD; /* set special char detected */ +// nuptr->SNS |= SNS_RLSDS; /* set rec'd line signal detect */ + nuptr->SNS |= SNS_RING; /* set ring attention status */ + sim_debug(DEBUG_CMD, &com_dev, + "comc_srv user ACE wakeup on chsa %04x line %02x cmd %02x SNS %08x ACE %08x\n", + chsa, ln, cmd, nuptr->SNS, nuptr->ACE); + set_devwake(chsa, SNS_ATTN|SNS_DEVEND|SNS_CHNEND); + continue; + } + + /* put char in buffer */ + com_data[ln].ibuff[com_data[ln].incnt++] = ch; + + /* see if at max, if so reset to start */ + if (com_data[ln].incnt >= sizeof(com_data[ln].ibuff)) + com_data[ln].incnt = 0; /* reset buffer cnt */ + + nuptr->CMD |= COM_INPUT; /* we have a char available */ + sim_debug(DEBUG_CMD, dptr, + "comc_srv readch ln %02x: CMD %08x read %02x CNT %02x incnt %02x c %04x\n", + ln, nuptr->CMD, ch, nuptr->CNT, com_data[ln].incnt, c); + } + else /* end if conn */ + /* if we were connected and not now, reset serial line */ + if ((nuptr->SNS & SNS_CONN) && (com_ldsc[ln].conn == 0)) { + UNIT *wuptr = coml_unit+ln+8; /* get output uptr for coml line */ + sim_debug(DEBUG_CMD, &com_dev, + "comc_srv disconnect on chsa %04x line %02x cmd %02x SNS %08x ACE %08x\n", + chsa, ln, cmd, nuptr->SNS, nuptr->ACE); + com_ldsc[ln].rcve = 0; /* disable rcv */ + com_ldsc[ln].xmte = 0; /* disable xmt for output line */ + com_stat[ln] &= ~COML_RBP; /* disconnected */ + com_stat[ln] |= COML_REP; /* set pending */ + nuptr->SNS &= ~(SNS_RTS | SNS_DTR); /* reset RTS & DTR */ + nuptr->SNS &= ~(SNS_DSRS); /* status is not connected */ + nuptr->SNS |= (SNS_DELDSR); /* status is not connected */ + nuptr->SNS |= (SNS_DELTA); /* status is not connected */ + nuptr->SNS &= ~(SNS_RDY|SNS_CONN); /* status is not connected */ + wuptr->SNS = nuptr->SNS; /* set write channel too */ + set_devwake(chsa, SNS_ATTN|SNS_DEVEND|SNS_CHNEND); + } + } /* end for */ + + sim_debug(DEBUG_DETAIL, &com_dev, + "comc_srv POLL DONE on chsa %04x\n", chsa); + /* this says to use 200, but simh really uses 50000 for cnt */ + /* changed 12/02/2021 from 200 to 5000 */ +// return sim_clock_coschedule(uptr, 200); /* continue poll */ + return sim_clock_coschedule(uptr, 5000); /* continue poll */ +// return sim_activate(uptr, 10000); /* continue poll */ +// return sim_activate(uptr, 5000); /* continue poll */ +} + +/* Unit service - input transfers */ +t_stat comi_srv(UNIT *uptr) +{ + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); /* get channel/sub-addr */ + uint32 ln = (uptr - coml_unit) & 0x7; /* use line # 0-7 for 8-15 */ + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + int cmd = uptr->CMD & 0xff; /* get active cmd */ + uint8 ch, cc; + + /* handle NOP and INCH cmds */ + sim_debug(DEBUG_CMD, dptr, + "comi_srv entry chsa %04x line %04x cmd %02x conn %x rcve %x xmte %x SNS %08x\n", + chsa, ln, cmd, com_ldsc[ln].conn, com_ldsc[ln].rcve, com_ldsc[ln].xmte, uptr->SNS); + + if (com_ldsc[ln].conn) { /* connected? */ + if ((uptr->CNT != com_data[ln].incnt) || /* input empty */ + (uptr->CMD & COM_INPUT)) { /* input waiting? */ + ch = com_data[ln].ibuff[uptr->CNT]; /* get char from read buffer */ + sim_debug(DEBUG_CMD, dptr, + "com_srvi readbuf unit %02x: CMD %08x read %02x incnt %02x CNT %02x len %02x\n", + ln, uptr->CMD, ch, com_data[ln].incnt, uptr->CNT, chp->ccw_count); + + if (uptr->CNT != com_data[ln].incnt) { /* input available */ + /* process any characters */ + /* this fixes mpx1x time entry on startup */ + if (uptr->CMD & COM_EKO) { /* ECHO requested */ + /* echo the char out */ + /* convert to user requested output */ + sim_debug(DEBUG_CMD, &com_dev, + "comi_srv echo char %02x on chsa %04x line %02x cmd %02x ACE %08x\n", + ch, chsa, ln, cmd, uptr->ACE); +// cc = sim_tt_outcvt(c, TT_GET_MODE(coml_unit[ln].flags)); + tmxr_putc_ln(&com_ldsc[ln], ch); /* output char */ + tmxr_poll_tx(&com_desc); /* poll xmt to send */ + } + if (chan_write_byte(chsa, &ch)) { /* write byte to memory */ + /* write error */ + cmd = 0; /* no cmd now */ + sim_debug(DEBUG_CMD, dptr, + "comi_srv write error ln %02x: CMD %08x read %02x CNT %02x ccw_count %02x\n", + ln, uptr->CMD, ch, uptr->CNT, chp->ccw_count); + uptr->CMD &= ~COM_MSK; /* remove old CMD */ + uptr->CMD &= ~COM_INPUT; /* input waiting? */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we done */ + return SCPE_OK; + } + /* character accepted, bump buffer pointer */ + uptr->CNT++; /* next char position */ + + sim_debug(DEBUG_CMD, dptr, + "comi_srv write to mem line %02x: CMD %08x read %02x CNT %02x incnt %02x\n", + ln, uptr->CMD, ch, uptr->CNT, com_data[ln].incnt); + + /* see if at end of buffer */ + if (uptr->CNT >= (int32)sizeof(com_data[ln].ibuff)) + uptr->CNT = 0; /* reset pointer */ + + cc = ch & 0x7f; /* clear parity bit */ + /* Special char detected? (7 bit read only) */ + if (cc == ((uptr->ACE >> 8) & 0xff)) { /* is it spec char */ +// uptr->CMD |= COM_SCD; /* set special char detected */ + uptr->SNS |= SNS_SPCLCD; /* set special char detected */ + sim_debug(DEBUG_CMD, &com_dev, + "comi_srv user ACE %02x wakeup on chsa %04x line %02x cmd %02x ACE %08x\n", + cc, chsa, ln, cmd, uptr->ACE); + uptr->CMD &= LMASK; /* nothing left, command complete */ + sim_debug(DEBUG_CMD, dptr, + "comi_srv read done chsa %04x ln %04x: chnend|devend\n", chsa, ln); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* done */ + return SCPE_OK; + } + + /* ASCII control char (7 bit read only) */ + /* see if control char detected */ + if (uptr->CMD & 0x40) { /* is ASCII ctrl char test bit set */ + if (((cc & 0x60) == 0) || (cc == 0x7f)) { + uptr->SNS |= SNS_ASCIICD; /* ASCII ctrl char detected */ + sim_debug(DEBUG_CMD, &com_dev, + "comi_srv user ASCII %02x wakeup on chsa %04x line %02x cmd %02x ACE %08x\n", + cc, chsa, ln, cmd, uptr->ACE); + uptr->CMD &= LMASK; /* nothing left, command complete */ + sim_debug(DEBUG_CMD, dptr, + "comi_srv read CC done chsa %04x ln %04x: chnend|devend\n", chsa, ln); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* done */ + return SCPE_OK; + } + } + + /* user want more data? */ + if ((test_write_byte_end(chsa)) == 0) { + sim_debug(DEBUG_CMD, dptr, + "comi_srv need more line %02x CMD %08x CNT %02x ccw_count %02x incnt %02x\n", + ln, uptr->CMD, uptr->CNT, chp->ccw_count, com_data[ln].incnt); + /* user wants more, look next time */ + if (uptr->CNT == com_data[ln].incnt) { /* input empty */ + uptr->CMD &= ~COM_INPUT; /* no input available */ + } +/* change 02DEC21*/ sim_activate(uptr, uptr->wait); /* wait */ +// change 02DEC21*/ sim_clock_coschedule(uptr, 1000); /* continue poll */ + return SCPE_OK; + } + /* command is completed */ + sim_debug(DEBUG_CMD, dptr, + "comi_srv read done line %02x CMD %08x read %02x CNT %02x ccw_count %02x incnt %02x\n", + ln, uptr->CMD, ch, uptr->CNT, chp->ccw_count, com_data[ln].incnt); + uptr->CMD &= LMASK; /* nothing left, command complete */ + if (uptr->CNT != com_data[ln].incnt) { /* input empty */ + uptr->CMD |= COM_INPUT; /* input still available */ + } + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we done */ + } + } +// change 02DEC21 sim_activate(uptr, uptr->wait); /* wait */ +/* change 02DEC21*/ sim_clock_coschedule(uptr, 1000); /* continue poll */ + return SCPE_OK; + } + /* not connected, so dump chars on ground */ + uptr->CNT = 0; /* no input count */ + com_data[ln].incnt = 0; /* no input data */ + uptr->CMD &= LMASK; /* nothing left, command complete */ + uptr->SNS |= 0x00003003; /* status is online & ready */ + uptr->SNS &= SNS_DSRS; /* reset DSR */ + uptr->SNS |= SNS_DELDSR; /* give change status */ + uptr->SNS |= SNS_MRING; /* give RING status */ + sim_debug(DEBUG_CMD, dptr, + "comi_srv read dump DONE line %04x status %04x cmd %02x SNS %08x\n", + ln, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK, cmd, uptr->SNS); + /* if line active, abort cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); /* error return */ + return SCPE_OK; +} + +/* Unit service - output transfers */ +t_stat como_srv(UNIT *uptr) +{ + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); /* get channel/sub-addr */ + uint32 ln = (uptr - coml_unit) & 0x7; /* use line # 0-7 for 8-15 */ + UNIT *ruptr = &dptr->units[ln&7]; /* read uptr */ + uint32 done; + int cmd = uptr->CMD & 0xff; /* get active cmd */ + uint8 ch; + + sim_debug(DEBUG_CMD, dptr, + "como_srv entry chsa %04x line %04x cmd %02x conn %x rcve %x xmte %x\n", + chsa, ln, cmd, com_ldsc[ln].conn, com_ldsc[ln].rcve, com_ldsc[ln].xmte); + + if (com_dev.flags & DEV_DIS) { /* disabled */ + sim_debug(DEBUG_CMD, dptr, + "como_srv chsa %04x line %02x SNS %08x DEV_DIS set\n", chsa, ln, uptr->SNS); + sim_debug(DEBUG_CMD, dptr, + "como_srv Write forced DONE %04x status %04x\n", + ln, SNS_CHNEND|SNS_DEVEND); + uptr->CMD &= LMASK; /* nothing left, command complete */ + ruptr->SNS &= SNS_DSRS; /* reset DSR */ + ruptr->SNS |= SNS_DELDSR; /* give change status */ +// chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* done */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); /* error return */ + return SCPE_OK; /* return */ + } + + /* handle NOP and INCH cmds */ + if (cmd == COM_NOP || cmd == 0x7f) { /* check for NOP or INCH */ + uptr->CMD &= LMASK; /* leave only chsa */ + sim_debug(DEBUG_CMD, &com_dev, + "como_srv NOP or INCH done chsa %04x line %04x cmd %02x\n", + chsa, ln, cmd); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* done */ + return SCPE_OK; /* return */ + } + + /* handle SACE, 3 char already read, so we are done */ + if (cmd == COM_SACE) { /* check for SACE 0xff */ + uptr->CMD &= LMASK; /* leave only chsa */ + sim_debug(DEBUG_CMD, &com_dev, + "como_srv SACE done chsa %04x line %02x cmd %02x ACE %08x\n", + chsa, ln, cmd, uptr->ACE); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* done */ + return SCPE_OK; /* return */ + } + + if (cmd == 0) + /* all done, so stop polling */ + return SCPE_OK; + + if (com_ldsc[ln].conn == 0) { /* connected? */ + /* not connected, so dump char on ground */ + sim_debug(DEBUG_CMD, dptr, + "como_srv write dump DONE line %04x status %04x cmd %02x\n", + ln, SNS_CHNEND|SNS_DEVEND, cmd); + uptr->CMD &= LMASK; /* nothing left, command complete */ + + uptr->SNS |= 0x00003003; /* status is online & ready */ + ruptr->SNS &= SNS_DSRS; /* reset DSR */ + ruptr->SNS |= SNS_DELDSR; /* give change status */ + uptr->SNS |= SNS_MRING; /* give RING status */ + /* if line not active, abort cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); /* error return */ + return SCPE_OK; + } + + sim_debug(DEBUG_CMD, dptr, + "como_srv entry 1 chsa %04x line %04x cmd %02x\n", chsa, ln, cmd); + /* get a user byte from memory */ +doagain: + done = chan_read_byte(chsa, &ch); /* get byte from memory */ + if (done) { + uptr->CMD &= LMASK; /* leave only chsa */ + sim_debug(DEBUG_CMD, dptr, + "como_srv Write DONE %01x chsa %04x line %04x status %04x\n", + done, chsa, ln, SNS_CHNEND|SNS_DEVEND); + tmxr_poll_tx(&com_desc); /* send out data */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* done */ + return SCPE_OK; /* return */ + } + + /* not done */ + sim_debug(DEBUG_DETAIL, dptr, + "como_srv poll chsa %04x line %02x SNS %08x ACE %08x\n", + chsa, ln, uptr->SNS, uptr->ACE); + + /* convert to user requested output */ + ch = sim_tt_outcvt(ch, TT_GET_MODE(coml_unit[ln].flags)); + /* send the next char out */ + tmxr_putc_ln(&com_ldsc[ln], ch); /* output char */ + sim_debug(DEBUG_CMD, dptr, + "como_srv writing char 0x%02x to ln %04x\n", ch, ln); + goto doagain; /* keep going */ +} + +/* haltxio routine */ +t_stat coml_haltio(UNIT *uptr) { + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & COM_MSK; + int unit = (uptr - coml_unit); /* unit # 0 is read, 1 is write */ + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + sim_debug(DEBUG_EXP, &com_dev, "coml_haltio enter chsa %04x cmd = %02x\n", chsa, cmd); + + /* terminate any input command */ + /* UTX wants SLI bit, but no unit exception */ + /* status must not have an error bit set */ + /* otherwise, UTX will panic with "bad status" */ + if ((uptr->CMD & COM_MSK) != 0) { /* is unit busy */ + sim_debug(DEBUG_CMD, &coml_dev, + "coml_haltio HIO chsa %04x cmd = %02x ccw_count %02x\n", chsa, cmd, chp->ccw_count); + /* stop any I/O and post status and return error status */ + chp->ccw_count = 0; /* zero the count */ + chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* reset chaining bits */ + uptr->CMD &= LMASK; /* make non-busy */ + uptr->CNT = 0; /* no I/O yet */ + com_data[unit].incnt = 0; /* no input data */ + sim_cancel(uptr); /* stop timer */ +// uptr->SNS |= (SNS_RDY|SNS_CONN); /* status is online & ready */ + sim_debug(DEBUG_CMD, &coml_dev, + "coml_haltio HIO I/O stop chsa %04x cmd = %02x\n", chsa, cmd); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* force end */ + return 1; /* tell chan code to post status */ + } + uptr->CNT = 0; /* no I/O yet */ + com_data[unit].incnt = 0; /* no input data */ + uptr->CMD &= LMASK; /* make non-busy */ +// uptr->SNS |= (SNS_RDY|SNS_CONN); /* status is online & ready */ + return SCPE_OK; /* not busy */ +} + +/* Reset routine */ +t_stat com_reset (DEVICE *dptr) +{ + int32 i; + + if (com_dev.flags & DEV_DIS) /* master disabled? */ + com_dev.flags |= DEV_DIS; /* disable lines */ + else + com_dev.flags &= ~DEV_DIS; + if (com_unit[COMC].flags & UNIT_ATT) /* master att? */ + sim_clock_coschedule(&com_unit[0], 200); /* activate */ + for (i = 0; i < COM_LINES; i++) /* reset lines */ + com_reset_ln(i); + return SCPE_OK; +} + + +/* attach master unit */ +t_stat com_attach(UNIT *uptr, CONST char *cptr) +{ + DEVICE *dptr = get_dev(uptr); + t_stat r; + + r = tmxr_attach(&com_desc, uptr, cptr); /* attach */ + if (r != SCPE_OK) /* error? */ + return r; /* return error */ + sim_debug(DEBUG_CMD, dptr, "com_srv comc is now attached\n"); + sim_activate(uptr, 100); /* start poll at once */ + return SCPE_OK; +} + +/* detach master unit */ +t_stat com_detach(UNIT *uptr) +{ + int32 i; + t_stat r; + + r = tmxr_detach(&com_desc, uptr); /* detach */ + for (i = 0; i < COM_LINES; i++) /* disable rcv */ + com_reset_ln(i); /* reset the line */ + sim_cancel(uptr); /* stop poll, cancel timer */ + return r; +} + +/* Reset an individual line */ +void com_reset_ln (int32 ln) +{ + sim_cancel(&coml_unit[ln]); + com_stat[ln] = 0; + com_stat[ln] |= COML_REP; /* set pending */ + com_rbuf[ln] = 0; /* clear read buffer */ + com_xbuf[ln] = 0; /* clear write buffer */ + com_ldsc[ln].rcve = 0; + com_ldsc[ln].xmte = 0; + coml_unit[ln].CNT = 0; /* no input count */ + com_data[ln].incnt = 0; /* no input data */ + return; +} + +t_stat com_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "SEL32 8512 8-Line Async Controller Terminal Interfaces\n\n"); +fprintf (st, "Terminals perform input and output through Telnet sessions connected to a \n"); +fprintf (st, "user-specified port.\n\n"); +fprintf (st, "The ATTACH command specifies the port to be used:\n\n"); +tmxr_attach_help (st, dptr, uptr, flag, cptr); +fprintf (st, "The additional terminals can be set to one of four modes: UC, 7P, 7B, or 8B.\n\n"); +fprintf (st, " mode input characters output characters\n\n"); +fprintf (st, " UC lower case converted lower case converted to upper case,\n"); +fprintf (st, " to upper case, high-order bit cleared,\n"); +fprintf (st, " high-order bit cleared non-printing characters suppressed\n"); +fprintf (st, " 7P high-order bit cleared high-order bit cleared,\n"); +fprintf (st, " non-printing characters suppressed\n"); +fprintf (st, " 7B high-order bit cleared high-order bit cleared\n"); +fprintf (st, " 8B no changes no changes\n\n"); +fprintf (st, "The default mode is 7P. In addition, each line can be configured to\n"); +fprintf (st, "behave as though it was attached to a dataset, or hardwired to a terminal:\n\n"); +fprintf (st, " sim> SET COMLn DATASET simulate attachment to a dataset (modem)\n"); +fprintf (st, " sim> SET COMLn NODATASET simulate direct attachment to a terminal\n\n"); +fprintf (st, "Finally, each line supports output logging. The SET COMLn LOG command enables\n"); +fprintf (st, "logging on a line:\n\n"); +fprintf (st, " sim> SET COMLn LOG=filename log output of line n to filename\n\n"); +fprintf (st, "The SET COMLn NOLOG command disables logging and closes the open log file,\n"); +fprintf (st, "if any.\n\n"); +fprintf (st, "Once DCI is attached and the simulator is running, the terminals listen for\n"); +fprintf (st, "connections on the specified port. They assume that the incoming connections\n"); +fprintf (st, "are Telnet connections. The connections remain open until disconnected either\n"); +fprintf (st, "by the Telnet client, a SET DCI DISCONNECT command, or a DETACH DCI command.\n\n"); +fprintf (st, "Other special commands:\n\n"); +fprintf (st, " sim> SHOW COMC CONNECTIONS show current connections\n"); +fprintf (st, " sim> SHOW COMC STATISTICS show statistics for active connections\n"); +fprintf (st, " sim> SET COMLn DISCONNECT disconnects the specified line.\n"); +fprintf (st, "\nThe additional terminals do not support save and restore. All open connections\n"); +fprintf (st, "are lost when the simulator shuts down or DCI is detached.\n"); + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + return SCPE_OK; +} + +/* description of controller */ +const char *com_description (DEVICE *dptr) +{ + return "SEL-32 8512 8-Line async communications controller"; +} + +#endif diff --git a/SEL32/sel32_con.c b/SEL32/sel32_con.c new file mode 100644 index 00000000..5c9b3083 --- /dev/null +++ b/SEL32/sel32_con.c @@ -0,0 +1,799 @@ +/* sel32_con.c: SEL 32 Class F IOP processor console. + + Copyright (c) 2018-2022, James C. Bevier + Portions provided by Richard Cornwell, Geert Rolf and other SIMH contributers + + 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 + JAMES C. BEVIER 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. + + This is the standard console interface. It is subchannel of the IOP 7e00. + + These units each buffer one record in local memory and signal + ready when the buffer is full or empty. The channel must be + ready to recieve/transmit data when they are activated since + they will transfer their block during chan_cmd. All data is + transmitted as ASCII characters. +*/ + +#include "sel32_defs.h" +#include "sim_tmxr.h" + +#if NUM_DEVS_CON > 0 + +#define UNIT_CON UNIT_IDLE | UNIT_DISABLE + +#define CMD u3 +/* Held in u3 is the device command and status */ +#define CON_INCH 0x00 /* Initialize channel command */ +#define CON_INCH2 0xf0 /* Initialize channel command for processing */ +#define CON_WR 0x01 /* Write console */ +#define CON_RD 0x02 /* Read console */ +#define CON_NOP 0x03 /* No op command */ +#define CON_SNS 0x04 /* Sense command */ +#define CON_ECHO 0x0a /* Read with Echo */ +#define CON_RDBWD 0x0c /* Read backward */ +#define CON_CON 0x1f /* connect line */ +#define CON_DIS 0x23 /* disconnect line */ +#define CON_RWD 0x37 /* TOF and write line */ + +#define CON_MSK 0xff /* Command mask */ + +/* Status held in u3 */ +/* controller/unit address in upper 16 bits */ +#define CON_ATAT 0x4000 /* working on @@A input */ +#define CON_READ 0x2000 /* Read mode selected */ +#define CON_OUTPUT 0x1000 /* Output ready for unit */ +#define CON_EKO 0x0800 /* Echo input character */ +#define CON_REQ 0x0400 /* Request key pressed */ +#define CON_CR 0x0200 /* Output at beginning of line */ +#define CON_INPUT 0x0100 /* Input ready for unit */ + +/* Input buffer pointer held in u4 */ + +#define SNS u5 +/* in u5 packs sense byte 0,1 and 3 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x80000000 /* Command reject */ +#define SNS_INTVENT 0x40000000 /* Unit intervention required */ +/* sense byte 3 */ +#define SNS_RDY 0x80 /* device ready */ +#define SNS_ONLN 0x40 /* device online */ +#define SNS_DSR 0x08 /* data set ready */ +#define SNS_DCD 0x04 /* data carrier detect */ + +/* std devices. data structures + con_dev Console device descriptor + con_unit Console unit descriptor + con_reg Console register list + con_mod Console modifiers list +*/ + +struct _con_data +{ + uint8 incnt; /* char count */ + uint8 ibuff[145]; /* Input line buffer */ +} +con_data[NUM_UNITS_CON]; + +uint32 atbuf=0; /* attention buffer */ +uint32 outbusy = 0; /* output waiting on timeout */ +uint32 inbusy = 0; /* input waiting on timeout */ + +/* forward definitions */ +t_stat con_preio(UNIT *uptr, uint16 chan); +t_stat con_startcmd(UNIT*, uint16, uint8); +void con_ini(UNIT*, t_bool); +t_stat con_srvi(UNIT*); +t_stat con_srvo(UNIT*); +t_stat con_haltio(UNIT *); +t_stat con_rschnlio(UNIT *uptr); /* Reset Channel */ +t_stat con_poll(UNIT *); +t_stat con_reset(DEVICE *); + +/* channel program information */ +CHANP con_chp[NUM_UNITS_CON] = {0}; + +MTAB con_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, &show_dev_addr, NULL}, + {0} +}; + +UNIT con_unit[] = { + {UDATA(&con_srvi, UNIT_CON, 0), 0, UNIT_ADDR(0x7EFC)}, /* 0 Input */ + {UDATA(&con_srvo, UNIT_CON, 0), 0, UNIT_ADDR(0x7EFD)}, /* 1 Output */ +}; + +DIB con_dib = { + con_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + con_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + con_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + con_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + NULL, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + con_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + con_unit, /* UNIT* units */ /* Pointer to units structure */ + con_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_UNITS_CON, /* uint8 numunits */ /* number of units defined */ + 0x03, /* uint8 mask */ /* 2 devices - device mask */ + 0x7e00, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +DEVICE con_dev = { + "CON", con_unit, NULL, con_mod, + NUM_UNITS_CON, 8, 15, 1, 8, 8, +// NULL, NULL, &con_reset, NULL, &con_attach, &con_detach, + NULL, NULL, &con_reset, NULL, NULL, NULL, + &con_dib, DEV_DIS|DEV_DISABLE|DEV_DEBUG, 0, dev_debug +}; + +/* + * Console print routines. + */ +/* initialize the console chan/unit */ +void con_ini(UNIT *uptr, t_bool f) { + int unit = (uptr - con_unit); /* unit 0 */ + + uptr->u4 = 0; /* no input count */ + con_data[unit].incnt = 0; /* no input data */ + uptr->CMD &= LMASK; /* leave only chsa */ + uptr->SNS = SNS_RDY|SNS_ONLN; /* status is online & ready */ + sim_cancel(uptr); /* stop input poll */ + if (unit == 0) { + sim_activate(uptr, 1000); /* start input poll */ + } +} + +/* start a console operation */ +t_stat con_preio(UNIT *uptr, uint16 chan) { + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); + + if ((uptr->CMD & CON_MSK) != 0) { /* just return if busy */ + sim_debug(DEBUG_CMD, dptr, "con_preio unit=%02x BUSY\n", unit); + return SNS_BSY; + } + + sim_debug(DEBUG_CMD, dptr, "con_preio unit=%02x OK\n", unit); + return SCPE_OK; /* good to go */ +} + +/* start an I/O operation */ +t_stat con_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) { + DEVICE *dptr = uptr->dptr; + int unit = (uptr - con_unit); /* unit 0 is read, unit 1 is write */ + + if ((uptr->CMD & CON_MSK) != 0) { /* is unit busy */ + sim_debug(DEBUG_EXP, dptr, + "con_startcmd unit %01x chan %02x cmd %02x BUSY cmd %02x uptr %p\n", + unit, chan, cmd, uptr->CMD, uptr); + return SNS_BSY; /* yes, return busy */ + } + + sim_debug(DEBUG_DETAIL, dptr, + "con_startcmd unit %01x chan %02x cmd %02x enter\n", unit, chan, cmd); + + /* substitute CON_INCH2 for CON_INCH for pprocessing */ + if (cmd == CON_INCH) + cmd = CON_INCH2; /* save INCH command as 0xf0 */ + + /* process the commands */ + switch (cmd & 0xFF) { + case CON_ECHO: /* 0x0a */ /* Read command w/ECHO */ + uptr->CMD |= CON_EKO; /* save echo status */ + case CON_RD: /* 0x02 */ /* Read command */ + atbuf = 0; /* reset attention buffer */ + uptr->CMD |= CON_READ; /* show read mode */ + /* fall through */ + case CON_INCH2: /* 0xf0 */ /* INCH command */ + case CON_RWD: /* 0x37 */ /* TOF and write line */ + case CON_WR: /* 0x01 */ /* Write command */ + case CON_NOP: /* 0x03 */ /* NOP has do nothing */ + case CON_RDBWD: /* 0x0c */ /* Read Backward */ + uptr->SNS |= (SNS_RDY|SNS_ONLN); /* status is online & ready */ + case CON_CON: /* 0x1f */ /* Connect, return Data Set ready */ + case CON_DIS: /* 0x23 */ /* Disconnect has do nothing */ + case CON_SNS: /* 0x04 */ /* Sense */ + uptr->CMD &= ~CON_MSK; /* remove old CMD */ + uptr->CMD |= (cmd & CON_MSK); /* save command */ + if (unit == 0) { + sim_cancel(uptr); /* stop input poll */ + sim_activate(uptr, 300); /* start us off */ +// sim_activate(uptr, 1000); /* start us off */ + } + else + /* using value 500 or larger causes diag to fail on 32/27 */ +// sim_activate(uptr, 500); /* start us off */ +// sim_activate(uptr, 200); /* start us off */ + sim_activate(uptr, 30); /* start us off */ + return SCPE_OK; /* no status change */ + break; + + default: /* invalid command */ + break; + } + /* invalid command */ + uptr->SNS |= SNS_CMDREJ; /* command rejected */ + sim_debug(DEBUG_EXP, dptr, + "con_startcmd %04x: Invalid command %02x Sense %02x\n", + chan, cmd, uptr->SNS); + return SNS_CHNEND|SNS_DEVEND|STATUS_PCHK; +} + +/* Handle output transfers for console */ +t_stat con_srvo(UNIT *uptr) { + DEVICE *dptr = uptr->dptr; + uint16 chsa = GET_UADDR(uptr->CMD); + int unit = (uptr - con_unit); /* unit 0 is read, unit 1 is write */ + int cmd = uptr->CMD & CON_MSK; + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + int len = chp->ccw_count; /* INCH command count */ + uint32 mema = chp->ccw_addr; /* get inch or buffer addr */ + uint32 tstart; + uint8 ch; + static uint32 dexp; + static int cnt = 0; + + sim_debug(DEBUG_CMD, dptr, + "con_srvo enter CMD %08x chsa %04x cmd %02x iocla %06x cnt %04x\n", + uptr->CMD, chsa, cmd, chp->chan_caw, chp->ccw_count); + + switch (cmd) { + + /* if input tried from output device, error */ + case CON_RD: /* 0x02 */ /* Read command */ + case CON_ECHO: /* 0x0a */ /* Read command w/ECHO */ + case CON_RDBWD: /* 0x0c */ /* Read Backward */ + /* if input requested for output device, give error */ + uptr->SNS |= SNS_CMDREJ; /* command rejected */ + uptr->CMD &= LMASK; /* nothing left, command complete */ + sim_debug(DEBUG_CMD, dptr, + "con_srvo Read to output device CMD %08x chsa %04x cmd = %02x\n", uptr->CMD, chsa, cmd); + chan_end(chsa, SNS_CHNEND|SNS_UNITCHK); /* unit check */ + break; + + case CON_CON: /* 0x1f */ /* Connect, return Data Set ready */ + uptr->SNS |= (SNS_DSR|SNS_DCD); /* Data set ready, Data Carrier detected */ + sim_debug(DEBUG_CMD, dptr, + "con_srvo CON CMD %08x chsa %04x cmd = %02x\n", uptr->CMD, chsa, cmd); + uptr->CMD &= ~CON_MSK; /* remove old CMD */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case CON_DIS: /* 0x23 */ /* Disconnect has do nothing */ + uptr->SNS &= ~(SNS_DSR|SNS_DCD); /* Data set not ready */ + sim_debug(DEBUG_CMD, dptr, + "con_srvo DIS CMD %08x chsa %04x cmd = %02x\n", uptr->CMD, chsa, cmd); + uptr->CMD &= ~CON_MSK; /* remove old CMD */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case CON_INCH2: /* 0xf0 */ /* INCH command */ + uptr->CMD &= LMASK; /* nothing left, command complete */ + sim_debug(DEBUG_CMD, dptr, + "con_srvo INCH unit %02x: CMD %08x cmd %02x incnt %02x u4 %02x\n", + unit, uptr->CMD, cmd, con_data[unit].incnt, uptr->u4); + + /* now call set_inch() function to write and test inch buffer addresses */ + /* 1-256 wd buffer is provided for 128 status dbl words */ + tstart = set_inch(uptr, mema, 128); /* new address & 128 entries */ + if ((tstart == SCPE_MEM) || (tstart == SCPE_ARG)) { /* any error */ + /* we have error, bail out */ + uptr->SNS |= SNS_CMDREJ; + sim_debug(DEBUG_CMD, dptr, + "con_srvo INCH Error unit %02x: CMD %08x cmd %02x incnt %02x u4 %02x\n", + unit, uptr->CMD, cmd, con_data[unit].incnt, uptr->u4); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + sim_debug(DEBUG_CMD, dptr, + "con_srvo INCH CMD %08x chsa %04x len %02x inch %06x\n", uptr->CMD, chsa, len, mema); + /* WARNING, if SNS_DEVEND is not set, diags fail by looping in CON diag */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case CON_NOP: /* 0x03 */ /* NOP has do nothing */ + uptr->CMD &= ~CON_MSK; /* remove old CMD */ + sim_debug(DEBUG_CMD, dptr, + "con_srvo NOP CMD %08x chsa %04x cmd = %02x\n", uptr->CMD, chsa, cmd); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case CON_SNS: /* 0x04 */ /* Sense */ + /* value 4 is Data Set Ready */ + /* value 5 is Data carrier detected n/u */ + sim_debug(DEBUG_CMD, dptr, + "con_srvo cmd %04x: Cmd Sense %02x\n", chsa, uptr->SNS); + /* value 4 is Data Set Ready */ + /* value 5 is Data carrier detected n/u */ + ch = uptr->SNS & 0xff; /* Sense byte 3 */ + if (chan_write_byte(chsa, &ch)) { /* write byte to memory */ + /* write error */ + cmd = 0; /* no cmd now */ + sim_debug(DEBUG_CMD, dptr, + "con_srvo write error unit %02x: CMD %08x read %02x u4 %02x ccw_count %02x\n", + unit, uptr->CMD, ch, uptr->u4, chp->ccw_count); + uptr->CMD &= LMASK; /* nothing left, command complete */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we done */ + break; + } + uptr->CMD &= LMASK; /* nothing left, command complete */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* good return */ + break; + + case CON_RWD: /* 0x37 */ /* TOF and write line */ + case CON_WR: /* 0x01 */ /* Write command */ +#ifdef DO_OLDWAY + /* see if write complete */ + if (uptr->CMD & CON_OUTPUT) { + /* write is complete, post status */ + sim_debug(DEBUG_CMD, &con_dev, + "con_srvo write CMD %08x chsa %04x cmd %02x complete\n", + uptr->CMD, chsa, cmd); + uptr->CMD &= ~CON_MSK; /* remove old CMD */ + uptr->CMD &= ~CON_OUTPUT; /* remove output command */ +/*RTC*/ outbusy = 0; /* output done */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* done */ + break; + } +/*RTC*/ outbusy = 1; /* tell clock output waiting */ + if (chan_read_byte(chsa, &ch) == SCPE_OK) { /* get byte from memory */ + /* Write to device */ + ch &= 0x7f; /* make 7 bit w/o parity */ + dexp = dexp<<8; /* move up last chars */ + dexp |= ch; /* insert new char */ +#ifdef DO_DYNAMIC_DEBUG +// if ((cnt == 3) && (dexp == 0x4458503e)) { /* test for "DXP>" */ + if ((cnt == 13) && (dexp == 0x4E542E48)) { /* test for "NT.H" */ + cpu_dev.dctrl |= (DEBUG_INST|DEBUG_TRAP|DEBUG_IRQ); /* start instruction trace */ + con_dev.dctrl |= DEBUG_XIO|DEBUG_CMD; +// sim_debug(DEBUG_INST, &cpu_dev, "|con_srvo DXP> received|\n"); + sim_debug(DEBUG_INST, &cpu_dev, "con_srvo CV.INT.H received start debug\n"); + } + if ((cnt == 13) && (dexp == 0x52502E48)) { /* test for "RP.H" */ + /* turn of debug trace because we are already hung */ + sim_debug(DEBUG_INST, &cpu_dev, "con_srvo got CV.TRP.H stopping debug\n"); + cpu_dev.dctrl &= ~(DEBUG_INST|DEBUG_TRAP|DEBUG_IRQ); /* start instruction trace */ + con_dev.dctrl &= ~(DEBUG_XIO|DEBUG_CMD); + } +#endif + sim_putchar(ch); /* output next char to device */ + sim_debug(DEBUG_CMD, dptr, + "con_srvo write wait %03x CMD %08x chsa %04x cmd %02x byte %d = %02x\n", + 1000, uptr->CMD, chsa, cmd, cnt, ch); + cnt++; /* count chars output */ +//01132022 sim_activate(uptr, 500); /* wait for a while before next write */ + sim_activate(uptr, 50); /* wait for a while before next write */ + break; + } + /* nothing left, finish up */ + cnt = 0; /* zero for next output */ + uptr->CMD |= CON_OUTPUT; /* output command complete */ + sim_debug(DEBUG_CMD, &con_dev, + "con_srvo write wait %03x CMD %08x chsa %04x cmd %02x to complete\n", + 1000, uptr->CMD, chsa, cmd); + sim_activate(uptr, 500); /* wait for a while */ + break; +#else + cnt = 0; /* zero count */ +/*RTC*/ outbusy = 1; /* tell clock output waiting */ + mema = chp->ccw_addr; /* get buffer addr */ + /* Write to device */ + while (chan_read_byte(chsa, &ch) == SCPE_OK) { /* get byte from memory */ + /* HACK HACK HACK */ + ch &= 0x7f; /* make 7 bit w/o parity */ + dexp = dexp<<8; /* move up last chars */ + dexp |= ch; /* insert new char */ +#ifdef DO_DYNAMIC_DEBUG +// if ((cnt == 3) && (dexp == 0x4458503e)) { /* test for "DXP>" */ + if ((cnt == 3) && (dexp == 0x44454641)) { /* test for "DEFA" */ +// cpu_dev.dctrl |= (DEBUG_INST|DEBUG_IRQ); /* start instruction trace */ + cpu_dev.dctrl |= (DEBUG_INST|DEBUG_TRAP|DEBUG_IRQ); /* start instruction trace */ +// con_dev.dctrl |= DEBUG_CMD; +// sim_debug(DEBUG_INST, &cpu_dev, "|con_srvo DXP> received|\n"); + sim_debug(DEBUG_INST, &cpu_dev, "|con_srvo DEFA received|\n"); + } +#endif + sim_putchar(ch); /* output next char to device */ + if (isprint(ch)) + sim_debug(DEBUG_CMD, dptr, + "con_srvo write addr %06x chsa %04x cmd %02x byte %d = %02x [%c]\n", + mema, chsa, cmd, cnt, ch, ch); + else + sim_debug(DEBUG_CMD, dptr, + "con_srvo write addr %06x chsa %04x cmd %02x byte %d = %02x\n", + mema, chsa, cmd, cnt, ch); + mema = chp->ccw_addr; /* get next buffer addr */ + cnt++; /* count chars output */ + } + /* write is complete, post status */ + sim_debug(DEBUG_CMD, dptr, + "con_srvo write CMD %08x chsa %04x cmd %02x complete\n", + uptr->CMD, chsa, cmd); + uptr->CMD &= LMASK; /* nothing left, command complete */ +/*RTC*/ outbusy = 0; /* output done */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* done */ + break; +#endif + } + return SCPE_OK; +} + +/* Handle input transfers for console */ +t_stat con_srvi(UNIT *uptr) { + DEVICE *dptr = uptr->dptr; + uint16 chsa = GET_UADDR(uptr->CMD); + int unit = (uptr - con_unit); /* unit 0 is read, unit 1 is write */ + int cmd = uptr->CMD & CON_MSK; + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + int len = chp->ccw_count; /* INCH command count */ + uint32 mema = chp->ccw_addr; /* get inch or buffer addr */ + uint32 tstart; + uint8 ch; + t_stat r; + int32 wait_time=10000; + + switch (cmd) { + + /* if output tried to input device, error */ + case CON_RWD: /* 0x37 */ /* TOF and write line */ + case CON_WR: /* 0x01 */ /* Write command */ + /* if input requested for output device, give error */ + uptr->SNS |= SNS_CMDREJ; /* command rejected */ + uptr->CMD &= LMASK; /* nothing left, command complete */ + sim_debug(DEBUG_CMD, dptr, + "con_srvi Write to input device CMD %08x chsa %04x cmd = %02x\n", uptr->CMD, chsa, cmd); + chan_end(chsa, SNS_CHNEND|SNS_UNITCHK); /* unit check */ + break; + + case CON_INCH2: /* 0xf0 */ /* INCH command */ + uptr->CMD &= LMASK; /* nothing left, command complete */ + sim_debug(DEBUG_CMD, dptr, + "con_srvi INCH unit %02x: CMD %08x cmd %02x incnt %02x u4 %02x inch %06x\n", + unit, uptr->CMD, cmd, con_data[unit].incnt, uptr->u4, mema); + + /* now call set_inch() function to write and test inch buffer addresses */ + tstart = set_inch(uptr, mema, 128); /* new address & 128 entries */ + if ((tstart == SCPE_MEM) || (tstart == SCPE_ARG)) { /* any error */ + /* we have error, bail out */ + uptr->SNS |= SNS_CMDREJ; + sim_debug(DEBUG_CMD, dptr, + "con_srvi INCH Error unit %02x: CMD %08x cmd %02x incnt %02x u4 %02x\n", + unit, uptr->CMD, cmd, con_data[unit].incnt, uptr->u4); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + con_data[unit].incnt = 0; /* buffer empty */ + uptr->u4 = 0; /* no I/O yet */ + sim_debug(DEBUG_CMD, dptr, + "con_srvi INCH CMD %08x chsa %04x len %02x inch %06x\n", uptr->CMD, chsa, len, mema); + /* WARNING, if SNS_DEVEND is not set, diags fail by looping in CON diag */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + /* drop through to poll input */ + break; + + case CON_NOP: /* 0x03 */ /* NOP has do nothing */ + uptr->CMD &= ~CON_MSK; /* remove old CMD */ + sim_debug(DEBUG_CMD, dptr, + "con_srvi NOP CMD %08x chsa %04x cmd = %02x\n", uptr->CMD, chsa, cmd); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + /* drop through to poll input */ + break; + + case CON_CON: /* 0x1f */ /* Connect, return Data Set ready */ + uptr->SNS |= (SNS_DSR|SNS_DCD); /* Data set ready, Data Carrier detected */ + sim_debug(DEBUG_CMD, dptr, + "con_srvi CON CMD %08x chsa %04x cmd = %02x\n", uptr->CMD, chsa, cmd); + uptr->CMD &= ~CON_MSK; /* remove old CMD */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case CON_DIS: /* 0x23 */ /* Disconnect has do nothing */ + uptr->SNS &= ~(SNS_DSR|SNS_DCD); /* Data set not ready */ + sim_debug(DEBUG_CMD, dptr, + "con_srvi DIS CMD %08x chsa %04x cmd = %02x\n", uptr->CMD, chsa, cmd); + uptr->CMD &= ~CON_MSK; /* remove old CMD */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case CON_SNS: /* 0x04 */ /* Sense */ + sim_debug(DEBUG_CMD, dptr, + "con_srvi cmd %04x: Cmd Sense %02x\n", chsa, uptr->SNS); + /* value 4 is Data Set Ready */ + /* value 5 is Data carrier detected n/u */ + ch = uptr->SNS & 0xff; /* Sense byte 3 */ + if (chan_write_byte(chsa, &ch)) { /* write byte to memory */ + /* write error */ + cmd = 0; /* no cmd now */ + sim_debug(DEBUG_CMD, dptr, + "con_srvi write error unit %02x: CMD %08x read %02x u4 %02x ccw_count %02x\n", + unit, uptr->CMD, ch, uptr->u4, chp->ccw_count); + uptr->CMD &= LMASK; /* nothing left, command complete */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we done */ + break; + } + uptr->CMD &= LMASK; /* nothing left, command complete */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we done */ + break; + + case CON_ECHO: /* 0x0a */ /* read from device w/ECHO */ + uptr->CMD |= CON_EKO; /* save echo status */ + case CON_RD: /* 0x02 */ /* read from device */ + case CON_RDBWD: /* 0x0c */ /* Read Backward */ + + if ((uptr->u4 != con_data[unit].incnt) || /* input empty */ + (uptr->CMD & CON_INPUT)) { /* input waiting? */ + ch = con_data[unit].ibuff[uptr->u4]; /* get char from read buffer */ + if (isprint(ch)) + sim_debug(DEBUG_IRQ, dptr, + "con_srvi readbuf unit %02x: CMD %08x read %02x [%c] incnt %02x u4 %02x len %02x\n", + unit, uptr->CMD, ch, ch, con_data[unit].incnt, uptr->u4, chp->ccw_count); + else + sim_debug(DEBUG_IRQ, dptr, + "con_srvi readbuf unit %02x: CMD %08x read %02x incnt %02x u4 %02x len %02x\n", + unit, uptr->CMD, ch, con_data[unit].incnt, uptr->u4, chp->ccw_count); +#ifdef DO_DYNAMIC_DEBUG + /* turn on instruction trace */ + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ +#endif + + /* process any characters */ + if (uptr->u4 != con_data[unit].incnt) { /* input available */ + ch = con_data[unit].ibuff[uptr->u4]; /* get char from read buffer */ + /* this fixes mpx1x time entry on startup */ + if (uptr->CMD & CON_EKO) /* ECHO requested */ + sim_putchar(ch); /* ECHO the char */ + if (chan_write_byte(chsa, &ch)) { /* write byte to memory */ + /* write error */ + cmd = 0; /* no cmd now */ + sim_debug(DEBUG_CMD, dptr, + "con_srvi write error unit %02x: CMD %08x read %02x u4 %02x ccw_count %02x\n", + unit, uptr->CMD, ch, uptr->u4, chp->ccw_count); + uptr->CMD &= ~CON_MSK; /* remove old CMD */ + uptr->CMD &= ~CON_INPUT; /* input waiting? */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we done */ + break; + } + /* character accepted, bump buffer pointer */ + uptr->u4++; /* next char position */ + + sim_debug(DEBUG_CMD, dptr, + "con_srvi write to mem unit %02x: CMD %08x read %02x u4 %02x incnt %02x\n", + unit, uptr->CMD, ch, uptr->u4, con_data[unit].incnt); + + /* see if at end of buffer */ + if (uptr->u4 >= (int32)sizeof(con_data[unit].ibuff)) + uptr->u4 = 0; /* reset pointer */ + + /* user want more data? */ + if ((test_write_byte_end(chsa)) == 0) { + sim_debug(DEBUG_CMD, dptr, + "con_srvi need more unit %02x CMD %08x u4 %02x ccw_count %02x incnt %02x\n", + unit, uptr->CMD, uptr->u4, chp->ccw_count, con_data[unit].incnt); + /* user wants more, look next time */ + if (uptr->u4 == con_data[unit].incnt) { /* input empty */ + uptr->CMD &= ~CON_INPUT; /* no input available */ + } +// wait_time = 200; /* process next time */ +// wait_time = 400; /* process next time */ + wait_time = 800; /* process next time */ + break; + } + /* command is completed */ + if (isprint(ch)) + sim_debug(DEBUG_CMD, dptr, + "con_srvi read done unit %02x CMD %08x read %02x [%c] u4 %02x ccw_count %02x incnt %02x\n", + unit, uptr->CMD, ch, ch, uptr->u4, chp->ccw_count, con_data[unit].incnt); + else + sim_debug(DEBUG_CMD, dptr, + "con_srvi read done unit %02x CMD %08x read %02x u4 %02x ccw_count %02x incnt %02x\n", + unit, uptr->CMD, ch, uptr->u4, chp->ccw_count, con_data[unit].incnt); +#ifdef DO_DYNAMIC_DEBUG + /* turn on instruction trace */ + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ +#endif + cmd = 0; /* no cmd now */ + uptr->CMD &= LMASK; /* nothing left, command complete */ + if (uptr->u4 != con_data[unit].incnt) { /* input empty */ + uptr->CMD |= CON_INPUT; /* input still available */ + } + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we done */ + break; + } + break; + } + default: + break; + } + + /* check for next input if reading or @@A sequence */ + r = sim_poll_kbd(); /* poll for a char */ + if (r & SCPE_KFLAG) { /* got a char */ + ch = r & 0xff; /* drop any extra bits */ + if ((uptr->CMD & CON_READ)) { /* looking for input? */ + atbuf = 0; /* reset attention buffer */ + uptr->CMD &= ~CON_ATAT; /* no @@A input */ + if (ch == '@') { /* maybe for console int */ + atbuf = (ch)<<8; /* start anew */ + uptr->CMD |= CON_ATAT; /* show getting @ */ + } +#ifndef TEST4MPX + if (ch == '\n') /* convert newline */ + ch = '\r'; /* make newline into carriage return */ +#endif + if (isprint(ch)) + sim_debug(DEBUG_CMD, dptr, + "con_srvi handle readch unit %02x: CMD %08x read %02x [%c] u4 %02x incnt %02x r %x\n", + unit, uptr->CMD, ch, ch, uptr->u4, con_data[unit].incnt, r); + else + sim_debug(DEBUG_CMD, dptr, + "con_srvi handle readch unit %02x: CMD %08x read %02x u4 %02x incnt %02x r %x\n", + unit, uptr->CMD, ch, uptr->u4, con_data[unit].incnt, r); +#ifdef DO_DYNAMIC_DEBUG + /* turn on instruction trace */ + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ +#endif + + /* put char in buffer */ + con_data[unit].ibuff[con_data[unit].incnt++] = ch; + + /* see if count at max, if so reset to start */ + if (con_data[unit].incnt >= sizeof(con_data[unit].ibuff)) + con_data[unit].incnt = 0; /* reset buffer cnt */ + + uptr->CMD |= CON_INPUT; /* we have a char available */ + if (isprint(ch)) + sim_debug(DEBUG_CMD, dptr, + "con_srvi readch unit %02x: CMD %08x read %02x [%c] u4 %02x incnt %02x\n", + unit, uptr->CMD, ch, ch, uptr->u4, con_data[unit].incnt); + else + sim_debug(DEBUG_CMD, dptr, + "con_srvi readch unit %02x: CMD %08x read %02x u4 %02x incnt %02x\n", + unit, uptr->CMD, ch, uptr->u4, con_data[unit].incnt); + sim_activate(uptr, 30); /* do this again */ +//01172021 sim_activate(uptr, 400); /* do this again */ +// sim_activate(uptr, 800); /* do this again */ + return SCPE_OK; + } + /* not looking for input, look for attn or wakeup */ + if (ch == '?') { + /* set ring bit? */ + set_devwake(chsa, SNS_ATTN|SNS_DEVEND|SNS_CHNEND); /* tell user */ + } + /* not wanting input, but we have a char, look for @@A */ + if (uptr->CMD & CON_ATAT) { /* looking for @@A */ + /* we have at least one @, look for another */ + if (ch == '@' || ch == 'A' || ch == 'a') { + uint8 cc = ch; + if (cc == 'a') + cc = 'A'; /* make uppercase */ + sim_putchar(ch); /* ECHO the char */ + atbuf = (atbuf|cc)<<8; /* merge new char */ + if (atbuf == 0x40404100) { + attention_trap = CONSOLEATN_TRAP; /* console attn (0xb4) */ + atbuf = 0; /* reset attention buffer */ + uptr->CMD &= ~CON_ATAT; /* no @@A input */ + sim_putchar('\r'); /* return char */ + sim_putchar('\n'); /* line feed char */ + sim_debug(DEBUG_CMD, dptr, + "con_srvi unit %02x: CMD %08x read @@A Console Trap\n", unit, uptr->CMD); + uptr->u4 = 0; /* no input count */ + con_data[unit].incnt = 0; /* no input data */ + } +// sim_activate(uptr, wait_time); /* do this again */ + sim_activate(uptr, 400); /* do this again */ +// sim_activate(uptr, 4000); /* do this again */ + return SCPE_OK; + } + /* char not for us, so keep looking */ + atbuf = 0; /* reset attention buffer */ + uptr->CMD &= ~CON_ATAT; /* no @@A input */ + } + /* not looking for input, look for attn or wakeup */ + if (ch == '@') { + atbuf = (atbuf|ch)<<8; /* merge in char */ + uptr->CMD |= CON_ATAT; /* show getting @ */ + sim_putchar(ch); /* ECHO the char */ + } + /* assume it is for next read request, so save it */ + /* see if count at max, if so reset to start */ + if (con_data[unit].incnt >= sizeof(con_data[unit].ibuff)) + con_data[unit].incnt = 0; /* reset buffer cnt */ + + /* put char in buffer */ + con_data[unit].ibuff[con_data[unit].incnt++] = ch; + + uptr->CMD |= CON_INPUT; /* we have a char available */ + if (isprint(ch)) + sim_debug(DEBUG_CMD, dptr, + "con_srvi readch2 unit %02x: CMD %08x read %02x [%c] u4 %02x incnt %02x r %x\n", + unit, uptr->CMD, ch, ch, uptr->u4, con_data[unit].incnt, r); + else + sim_debug(DEBUG_CMD, dptr, + "con_srvi readch2 unit %02x: CMD %08x read %02x u4 %02x incnt %02x r %x\n", + unit, uptr->CMD, ch, uptr->u4, con_data[unit].incnt, r); +#ifdef DO_DYNAMIC_DEBUG + /* turn off debug trace because we are already hung */ + sim_debug(DEBUG_INST, &cpu_dev, "con_srvi readch3 stopping debug\n"); + cpu_dev.dctrl &= ~(DEBUG_INST|DEBUG_TRAP|DEBUG_IRQ); /* start instruction trace */ + con_dev.dctrl &= ~(DEBUG_XIO|DEBUG_CMD); +#endif + } + sim_activate(uptr, wait_time); /* do this again */ + return SCPE_OK; +} + +t_stat con_reset(DEVICE *dptr) { + tmxr_set_console_units (&con_unit[0], &con_unit[1]); + return SCPE_OK; +} + +/* Handle rschnlio cmds for console */ +t_stat con_rschnlio(UNIT *uptr) { + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & CON_MSK; + con_ini(uptr, 0); /* reset the unit */ + sim_debug(DEBUG_EXP, &con_dev, "con_rschnl chsa %04x cmd = %02x\n", chsa, cmd); +// cpu_dev.dctrl |= (DEBUG_INST|DEBUG_TRAP|DEBUG_IRQ); /* start instruction trace */ + return SCPE_OK; +} + +/* Handle haltio transfers for console */ +t_stat con_haltio(UNIT *uptr) { + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & CON_MSK; + int unit = (uptr - con_unit); /* unit # 0 is read, 1 is write */ + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + sim_debug(DEBUG_EXP, &con_dev, + "con_haltio enter chsa %04x cmd = %02x\n", chsa, cmd); + + /* terminate any input command */ + /* UTX wants SLI bit, but no unit exception */ + /* status must not have an error bit set */ + /* otherwise, UTX will panic with "bad status" */ + if ((uptr->CMD & CON_MSK) != 0) { /* is unit busy */ + sim_debug(DEBUG_CMD, &con_dev, + "con_haltio HIO chsa %04x cmd = %02x ccw_count %02x\n", + chsa, cmd, chp->ccw_count); + sim_cancel(uptr); /* stop timer */ + /* stop any I/O and post status and return error status */ + chp->ccw_count = 0; /* zero the count */ + chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* reset chaining bits */ + uptr->CMD &= LMASK; /* make non-busy */ + uptr->u4 = 0; /* no I/O yet */ + con_data[unit].incnt = 0; /* no input data */ + uptr->SNS = SNS_RDY|SNS_ONLN; /* status is online & ready */ + sim_debug(DEBUG_CMD, &con_dev, + "con_haltio HIO I/O stop chsa %04x cmd = %02x\n", chsa, cmd); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* force end */ + return CC2BIT | SCPE_IOERR; /* tell chan code to post status */ + } + uptr->CMD &= LMASK; /* make non-busy */ + uptr->SNS = SNS_RDY|SNS_ONLN; /* status is online & ready */ + sim_debug(DEBUG_CMD, &con_dev, + "con_haltio HIO not busy chsa %04x cmd = %02x ccw_count %02x\n", + chsa, cmd, chp->ccw_count); + return CC1BIT | SCPE_OK; /* not busy */ +} +#endif + diff --git a/SEL32/sel32_cpu.c b/SEL32/sel32_cpu.c new file mode 100644 index 00000000..ba68f6a0 --- /dev/null +++ b/SEL32/sel32_cpu.c @@ -0,0 +1,7636 @@ +/* sel32_cpu.c: Sel 32 CPU simulator + + Copyright (c) 2018-2022, James C. Bevier + Portions provided by Richard Cornwell, Geert Rolf and other SIMH contributers + + 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 + JAMES C. BEVIER 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. + +*/ + +#include "sel32_defs.h" + +/* Concept 32 PSD Mode Trap/Interrupt Priorities */ +/* Relative|Logical |Int Vect|TCW |IOCD|Description */ +/* Priority|Priority|Location|Addr|Addr */ +/* - 080 Power Fail Safe Trap */ +/* - 084 Power On Trap */ +/* - 088 Memory Parity Trap */ +/* - 08C Nonpresent Memory Trap */ +/* - 090 Undefined Instruction Trap */ +/* - 094 Privilege Violation Trap */ +/* - 098 Supervisor Call Trap (SVC) */ +/* - 09C Machine Check Trap */ +/* - 0A0 System Check Trap */ +/* - 0A4 Map Fault Trap */ +/* - 0A8 CALM or Undefined IPU Instruction Trap */ +/* - 0AC Signal CPU or Signal IPU Trap */ +/* - 0B0 Address Specification Trap */ +/* - 0B4 Console Attention Trap */ +/* - 0B8 Privlege Mode Halt Trap */ +/* - 0BC Arithmetic Exception Trap */ +/* - 0C0 Cache Error Trap (V9 Only) */ +/* - 0C4 Demand Page Fault Trap (V6&V9 Only) */ +/* */ +/* 0 00 100 External/software Interrupt 0 */ +/* 1 01 104 External/software Interrupt 1 */ +/* 2 02 108 External/software Interrupt 2 */ +/* 3 03 10C External/software Interrupt 3 */ +/* 4 04 110 704 700 I/O Channel 0 interrupt */ +/* 5 05 114 70C 708 I/O Channel 1 interrupt */ +/* 6 06 118 714 710 I/O Channel 2 interrupt */ +/* 7 07 11C 71C 718 I/O Channel 3 interrupt */ +/* 8 08 120 724 720 I/O Channel 4 interrupt */ +/* 9 09 124 72C 728 I/O Channel 5 interrupt */ +/* A 0A 128 734 730 I/O Channel 6 interrupt */ +/* B 0B 12C 73C 738 I/O Channel 7 interrupt */ +/* C 0C 130 744 740 I/O Channel 8 interrupt */ +/* D 0D 134 74C 748 I/O Channel 9 interrupt */ +/* E 0E 138 754 750 I/O Channel A interrupt */ +/* F 0F 13C 75C 758 I/O Channel B interrupt */ +/* 10 10 140 764 760 I/O Channel C interrupt */ +/* 11 11 144 76C 768 I/O Channel D interrupt */ +/* 12 12 148 774 770 I/O Channel E interrupt */ +/* 13 13 14c 77C 778 I/O Channel F interrupt */ +/* 14 14 150 External/Software Interrupt */ +/* 15 15 154 External/Software Interrupt */ +/* 16 16 158 External/Software Interrupt */ +/* 17 17 15C External/Software Interrupt */ +/* 18 18 160 Real-Time Clock Interrupt */ +/* 19 19 164 External/Software Interrupt */ +/* 1A 1A 1A8 External/Software Interrupt */ +/* 1B 1B 1AC External/Software Interrupt */ +/* 1C 1C 1B0 External/Software Interrupt */ +/* THRU THRU THRU THRU */ +/* 6C 6C 2B0 External/Software Interrupt */ +/* 6D 6D 2B4 External/Software Interrupt */ +/* 6E 6E 2B8 External/Software Interrupt */ +/* 6F 6F 2BC Interval Timer Interrupt */ + +/* IVL ------------> ICB Trap/Interrupt Vector Location points to Interrupt Context Block */ +/* Wd 0 - Old PSD Word 1 points to return location */ +/* Wd 1 - Old PSD Word 2 */ +/* Wd 2 - New PSD Word 1 points to first instruction of service routine */ +/* Wd 3 - New PSD Word 2 */ +/* Wd 4 - CPU Status word at time of interrupt/trap */ +/* Wd 5 - N/U For Traps/Interrupts */ + +/* IVL ------------> ICB XIO Interrupt Vector Location */ +/* Wd 0 - Old PSD Word 1 points to return location */ +/* Wd 1 - Old PSD Word 2 */ +/* Wd 2 - New PSD Word 1 points to first instruction of service routine */ +/* Wd 3 - New PSD Word 2 */ +/* Wd 4 - Input/Output Command List Address (IOCL) for the Class F I/O CHannel */ +/* Wd 5 - 24 bit real address of the channel status word */ + +/* CPU registers, map cache, spad, and other variables */ +int cpu_index; /* Current CPU running */ +uint32 PSD[2]; /* the PC for the instruction */ +#define PSD1 PSD[0] /* word 1 of PSD */ +#define PSD2 PSD[1] /* word 2 of PSD */ +uint32 M[MAXMEMSIZE] = { 0 }; /* Memory */ +uint32 GPR[8]; /* General Purpose Registers */ +uint32 BR[8]; /* Base registers */ +uint32 PC; /* Program counter */ +uint32 CC; /* Condition codes, bits 1-4 of PSD1 */ +uint32 CPUSTATUS; /* cpu status word */ +uint32 TRAPSTATUS; /* trap status word */ +uint32 SPAD[256]; /* Scratch pad memory */ +uint32 INTS[112]; /* Interrupt status flags */ +uint32 pad[16]; /* In case of wrong access */ +uint32 CMCR; /* Cache Memory Control Register */ +uint32 SMCR; /* Shared Memory Control Register */ +uint32 CMSMC; /* V9 Cache/Shadow Memory Configuration */ +uint32 CSMCW; /* CPU Shadow Memory Configuration Word */ +uint32 ISMCW; /* IPU Shadow Memory Configuration Word */ +uint32 CCW; /* Computer Configuration Word */ +uint32 CSW = 0; /* Console switches going to 0x780 */ +uint32 BOOTR[8] = {0}; /* Boot registers settings */ +/* CPU mapping cache entries */ +/* 32/55 has none */ +/* 32/7x has 32 8KW maps per task */ +/* Concept 32/27 has 256 2KW maps per task */ +/* Concept 32/X7 has 2048 2KW maps per task */ +uint32 MAPC[1024]; /* maps are 16bit entries on word bountries */ +uint32 dummy=0; +uint32 pfault; /* page # of fault from read/write */ +uint32 BPIX=0; /* # pages loaded for O/S */ +uint32 CPIXPL=0; /* highest page loaded for User */ +uint32 CPIX=0; /* CPIX user MPL offset */ +uint32 HIWM=0; /* max maps loaded so far */ +uint32 MODES=0; /* Operating modes, bits 0, 5, 6, 7 of PSD1 */ +uint32 TLB[2048]; /* Translated addresses for each map entry */ +/* bits 0-4 are bits 0-4 from map entry */ +/* bit 0 valid */ +/* bit 1 p1 write access if set */ +/* bit 2 p2 write access if set */ +/* bit 3 p3 write access if set MM - memory modify */ +/* bit 4 p4 write access if set MA - memory accessed */ +/* bit 5 hit bit means entry is setup, even if not valid map */ +/* if hit bit is set and entry not valid, we will do a page fault */ +/* bit 6 dirty bit, set when written to, page update required */ +/* bits 8-18 has map reg contents for this page (Map << 13) */ +/* bit 19-31 is zero for page offset of zero */ + +uint32 dummy2=0; +uint8 wait4int = 0; /* waiting for interrupt if set */ +int32 irq_auto = 0; /* auto reset interrupt processing flag */ + +/* define traps */ +uint32 TRAPME = 0; /* trap to be executed */ +uint32 attention_trap = 0; /* set when trap is requested */ + +uint32 RDYQIN; /* fifo input index */ +uint32 RDYQOUT; /* fifo output index */ +uint32 RDYQ[128]; /* channel ready queue */ +uint8 waitqcnt = 0; /* # instructions before start */ + +struct InstHistory +{ + uint32 opsd1; /* original PSD1 */ + uint32 opsd2; /* original PSD2 */ + uint32 npsd1; /* new PSD1 after instruction */ + uint32 npsd2; /* new PSD2 after instruction */ + uint32 oir; /* the instruction itself */ + uint32 modes; /* current cpu mode bits */ + uint32 reg[16]; /* regs/bregs for operation */ +}; + +/* forward definitions */ +t_stat cpu_ex(t_value * vptr, t_addr addr, UNIT * uptr, int32 sw); +t_stat cpu_dep(t_value val, t_addr addr, UNIT * uptr, int32 sw); +t_stat cpu_reset(DEVICE * dptr); +t_stat cpu_set_size(UNIT * uptr, int32 val, CONST char *cptr, void *desc); +t_stat cpu_show_hist(FILE * st, UNIT * uptr, int32 val, CONST void *desc); +t_stat cpu_set_hist(UNIT * uptr, int32 val, CONST char *cptr, void *desc); +uint32 cpu_cmd(UNIT * uptr, uint16 cmd, uint16 dev); +t_stat cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *cpu_description (DEVICE *dptr); +t_stat RealAddr(uint32 addr, uint32 *realaddr, uint32 *prot, uint32 access); +t_stat load_maps(uint32 thepsd[2], uint32 lmap); +t_stat read_instruction(uint32 thepsd[2], uint32 *instr); +t_stat Mem_read(uint32 addr, uint32 *data); +t_stat Mem_write(uint32 addr, uint32 *data); + +/* external definitions */ +extern t_stat checkxio(uint16 addr, uint32 *status); /* XIO check in chan.c */ +extern t_stat startxio(uint16 addr, uint32 *status); /* XIO start in chan.c */ +extern t_stat testxio(uint16 addr, uint32 *status); /* XIO test in chan.c */ +extern t_stat stopxio(uint16 addr, uint32 *status); /* XIO stop in chan.c */ +extern t_stat rschnlxio(uint16 addr, uint32 *status); /* reset channel XIO */ +extern t_stat haltxio(uint16 addr, uint32 *status); /* halt XIO */ +extern t_stat grabxio(uint16 addr, uint32 *status); /* grab XIO n/u */ +extern t_stat rsctlxio(uint16 addr, uint32 *status); /* reset controller XIO */ +extern t_stat chan_set_devs(); /* set up the defined devices on the simulator */ +extern uint32 scan_chan(uint32 *ilev); /* go scan for I/O int pending */ +extern uint32 cont_chan(uint16 chsa); /* continue channel program */ +extern uint16 loading; /* set when doing IPL */ +extern int fprint_inst(FILE *of, uint32 val, int32 sw); /* instruction print function */ +extern int irq_pend; /* go scan for pending interrupt */ +extern void rtc_setup(uint32 ss, uint32 level); /* tell rtc to start/stop */ +extern void itm_setup(uint32 ss, uint32 level); /* tell itm to start/stop */ +extern int32 itm_rdwr(uint32 cmd, int32 cnt, uint32 level); /* read/write the interval timer */ +extern int16 post_csw(CHANP *chp, uint32 rstat); +extern DIB *dib_chan[MAX_CHAN]; + +/* floating point subroutines definitions */ +extern uint32 s_fixw(uint32 val, uint32 *cc); +extern uint32 s_fltw(uint32 val, uint32 *cc); +extern t_uint64 s_fixd(t_uint64 val, uint32 *cc); +extern t_uint64 s_fltd(t_uint64 val, uint32 *cc); +extern uint32 s_nor(uint32 reg, uint32 *exp); +extern t_uint64 s_nord(t_uint64 reg, uint32 *exp); +extern uint32 s_adfw(uint32 reg, uint32 mem, uint32 *cc); +extern uint32 s_sufw(uint32 reg, uint32 mem, uint32 *cc); +extern t_uint64 s_adfd(t_uint64 reg, t_uint64 mem, uint32 *cc); +extern t_uint64 s_sufd(t_uint64 reg, t_uint64 mem, uint32 *cc); +extern uint32 s_mpfw(uint32 reg, uint32 mem, uint32 *cc); +extern uint32 s_dvfw(uint32 reg, uint32 mem, uint32 *cc); +extern t_uint64 s_mpfd(t_uint64 reg, t_uint64 mem, uint32 *cc); +extern t_uint64 s_dvfd(t_uint64 reg, t_uint64 mem, uint32 *cc); +extern uint32 s_normfw(uint32 mem, uint32 *cc); +extern t_uint64 s_normfd(t_uint64 mem, uint32 *cc); + +/* History information */ +int32 hst_p = 0; /* History pointer */ +int32 hst_lnt = 0; /* History length */ +struct InstHistory *hst = NULL; /* History stack */ + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = + /* Unit data layout for CPU */ +/* { UDATA(rtc_srv, UNIT_BINK | MODEL(MODEL_27) | MEMAMOUNT(0), + * MAXMEMSIZE ), 120 }; */ + { + NULL, /* UNIT *next */ /* next active */ + NULL, /* t_stat (*action) */ /* action routine */ + NULL, /* char *filename */ /* open file name */ + NULL, /* FILE *fileref */ /* file reference */ + NULL, /* void *filebuf */ /* memory buffer */ + 0, /* uint32 hwmark */ /* high water mark */ + 0, /* int32 time */ /* time out */ + UNIT_IDLE|UNIT_FIX|UNIT_BINK|MODEL(MODEL_27)|MEMAMOUNT(4), /* flags */ + 0, /* uint32 dynflags */ /* dynamic flags */ + 0x800000, /* t_addr capac */ /* capacity */ + 0, /* t_addr pos */ /* file position */ + 0, /* void (*io_flush) */ /* io flush routine */ + 0, /* uint32 iostarttime */ /* I/O start time */ + 0, /* int32 buf */ /* buffer */ + 80, /* int32 wait */ /* wait */ +}; + +REG cpu_reg[] = { + {HRDATAD(PC, PC, 24, "Program Counter"), REG_FIT}, + {BRDATAD(PSD, PSD, 16, 32, 2, "Program Status Doubleword"), REG_FIT}, + {BRDATAD(GPR, GPR, 16, 32, 8, "Index registers"), REG_FIT}, + {BRDATAD(BR, BR, 16, 32, 8, "Base registers"), REG_FIT}, + {BRDATAD(BOOTR, BOOTR, 16, 32, 8, "Boot registers"), REG_FIT}, + {BRDATAD(SPAD, SPAD, 16, 32, 256, "CPU Scratchpad memory"), REG_FIT}, + {BRDATAD(MAPC, MAPC, 16, 32, 1024, "CPU map cache"), REG_FIT}, + {BRDATAD(TLB, TLB, 16, 32, 2048, "CPU Translation Lookaside Buffer"), REG_FIT}, + {HRDATAD(HIWM, HIWM, 32, "Max Maps Loaded"), REG_FIT}, + {HRDATAD(BPIX, BPIX, 32, "# Maps Loaded for O/S"), REG_FIT}, + {HRDATAD(CPIXPL, CPIXPL, 32, "Maximum Map # Loaded for User"), REG_FIT}, + {HRDATAD(CPIX, CPIX, 32, "Current CPIX user MPL offset"), REG_FIT}, + {HRDATAD(CPUSTATUS, CPUSTATUS, 32, "CPU Status Word"), REG_FIT}, + {HRDATAD(TRAPSTATUS, TRAPSTATUS, 32, "TRAP Status Word"), REG_FIT}, + {HRDATAD(CC, CC, 32, "Condition Codes"), REG_FIT}, + {HRDATAD(MODES, MODES, 32, "Mode bits"), REG_FIT}, + {BRDATAD(INTS, INTS, 16, 32, 112, "Interrupt Status"), REG_FIT}, + {HRDATAD(CMCR, CMCR, 32, "Cache Memory Control Register"), REG_FIT}, + {HRDATAD(SMCR, SMCR, 32, "Shared Memory Control Register"), REG_FIT}, + {HRDATAD(CMSMC, CMSMC, 32, "V9 Cache/Shadow Memory Configuration Word"), REG_FIT}, + {HRDATAD(CSMCW, CSMCW, 32, "V9 CPU Shadow Memory Configuration Word"), REG_FIT}, + {HRDATAD(ISMCW, ISMCW, 32, "V9 IPU Shadow Memory Configuration Word"), REG_FIT}, + {HRDATAD(CCW, CCW, 32, "Computer Configuration Word"), REG_FIT}, + {HRDATAD(CSW, CSW, 32, "Console Switches"), REG_FIT}, + {BRDATAD(RDYQ, RDYQ, 16, 32, 128, "Channel Program Completon Status"), REG_FIT}, + {HRDATAD(RDYQIN, RDYQIN, 32, "RDYQ input index"), REG_FIT}, + {HRDATAD(RDYQOUT, RDYQOUT, 32, "RDYQ output index"), REG_FIT}, + {NULL} +}; + +/* Modifier table layout (MTAB) - only extended entries have disp, reg, or flags */ +MTAB cpu_mod[] = { + { + /* MTAB table layout for cpu type */ + /* {UNIT_MODEL, MODEL(MODEL_55), "32/55", "32/55", NULL, NULL, NULL, "Concept 32/55"}, */ + UNIT_MODEL, /* uint32 mask */ /* mask */ + MODEL(MODEL_55), /* uint32 match */ /* match */ + "32/55", /* cchar *pstring */ /* print string */ + "32/55", /* cchar *mstring */ /* match string */ + NULL, /* t_stat (*valid) */ /* validation routine */ + NULL, /* t_stat (*disp) */ /* display routine */ + NULL, /* void *desc */ /* value desc, REG* if MTAB_VAL, int* if not */ + "Concept 32/55", /* cchar *help */ /* help string */ + }, + {UNIT_MODEL, MODEL(MODEL_75), "32/75", "32/75", NULL, NULL, NULL, "Concept 32/75"}, + {UNIT_MODEL, MODEL(MODEL_27), "32/27", "32/27", NULL, NULL, NULL, "Concept 32/27"}, + {UNIT_MODEL, MODEL(MODEL_67), "32/67", "32/67", NULL, NULL, NULL, "Concept 32/67"}, + {UNIT_MODEL, MODEL(MODEL_87), "32/87", "32/87", NULL, NULL, NULL, "Concept 32/87"}, + {UNIT_MODEL, MODEL(MODEL_97), "32/97", "32/97", NULL, NULL, NULL, "Concept 32/97"}, + {UNIT_MODEL, MODEL(MODEL_V6), "V6", "V6", NULL, NULL, NULL, "Concept V6"}, + {UNIT_MODEL, MODEL(MODEL_V9), "V9", "V9", NULL, NULL, NULL, "Concept V9"}, + { + /* MTAB table layout for cpu memory size */ + /* {UNIT_MSIZE, MEMAMOUNT(0), "128K", "128K", &cpu_set_size}, */ + UNIT_MSIZE, /* uint32 mask */ /* mask */ + MEMAMOUNT(0), /* uint32 match */ /* match */ + NULL, /* cchar *pstring */ /* print string */ + "128K", /* cchar *mstring */ /* match string */ + &cpu_set_size, /* t_stat (*valid) */ /* validation routine */ + NULL, /* t_stat (*disp) */ /* display routine */ + NULL, /* void *desc */ /* value desc, REG* if MTAB_VAL, int* if not */ + NULL, /* cchar *help */ /* help string */ + }, + {UNIT_MSIZE, MEMAMOUNT(1), NULL, "256K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(2), NULL, "512K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(3), NULL, "1M", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(4), NULL, "2M", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(5), NULL, "3M", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(6), NULL, "4M", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(7), NULL, "6M", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(8), NULL, "8M", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(9), NULL, "12M", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(10), NULL, "16M", &cpu_set_size}, + {MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle}, + {MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL}, + {MTAB_XTD | MTAB_VDV | MTAB_NMO | MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist}, + {0} +}; + +/* CPU device descriptor */ +DEVICE cpu_dev = { + /* "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 24, 1, 8, 32, + &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, + NULL, DEV_DEBUG, 0, dev_debug, + NULL, NULL, &cpu_help, NULL, NULL, &cpu_description */ + "CPU", /* cchar *name */ /* device name */ + &cpu_unit, /* UNIT *units */ /* unit array */ + cpu_reg, /* REG *registers */ /* register array */ + cpu_mod, /* MTAB *modifiers */ /* modifier array */ + 1, /* uint32 numunits */ /* number of units */ + 16, /* uint32 aradix */ /* address radix */ + 32, /* uint32 awidth */ /* address width */ + 1, /* uint32 aincr */ /* address increment */ + 16, /* uint32 dradix */ /* data radix */ + 8, /* uint32 dwidth */ /* data width */ + &cpu_ex, /* t_stat (*examine) */ /* examine routine */ + &cpu_dep, /* t_stat (*deposit) */ /* deposit routine */ + &cpu_reset, /* t_stat (*reset) */ /* reset routine */ + NULL, /* t_stat (*boot) */ /* boot routine */ + NULL, /* t_stat (*attach) */ /* attach routine */ + NULL, /* t_stat (*detach) */ /* detach routine */ + NULL, /* void *ctxt */ /* (context) device information block pointer */ + DEV_DEBUG, /* uint32 flags */ /* device flags */ + 0, /* uint32 dctrl */ /* debug control flags */ + dev_debug, /* DEBTAB *debflags */ /* debug flag name array */ + NULL, /* t_stat (*msize) */ /* memory size change routine */ + NULL, /* char *lname */ /* logical device name */ + &cpu_help, /* t_stat (*help) */ /* help function */ + NULL, /* t_stat (*attach_help) *//* attach help function */ + NULL, /* void *help_ctx */ /* Context available to help routines */ + &cpu_description, /* cchar *(*description) *//* Device description */ + NULL, /* BRKTYPTB *brk_types */ /* Breakpoint types */ +}; + +/* CPU Instruction decode flags */ +#define INV 0x0000 /* Instruction is invalid */ +#define HLF 0x0001 /* Half word instruction */ +#define ADR 0x0002 /* Normal addressing mode */ +#define IMM 0x0004 /* Immediate mode */ +#define WRD 0x0008 /* Word addressing, no index */ +#define SCC 0x0010 /* Sets CC */ +#define RR 0x0020 /* Read source register */ +#define R1 0x0040 /* Read destination register */ +#define RB 0x0080 /* Read base register into dest */ +#define SD 0x0100 /* Stores into destination register */ +#define RNX 0x0200 /* Reads memory without sign extend */ +#define RM 0x0400 /* Reads memory */ +#define SM 0x0800 /* Stores memory */ +#define DBL 0x1000 /* Double word operation */ +#define SB 0x2000 /* Store Base register */ +#define BT 0x4000 /* Branch taken, no PC incr */ +#define SF 0x8000 /* Special flag */ + +int nobase_mode[] = { + /* 00 04 08 0C */ + /* 00 ANR, ORR, EOR */ + HLF, SCC|R1|RR|SD|HLF, SCC|R1|RR|SD|HLF, SCC|R1|RR|SD|HLF, + + /* 10 14 18 1C */ + /* CAR, CMR, SBR ZBR */ + HLF, HLF, HLF, HLF, + + /* 20 24 28 2C */ + /* ABR TBR REG TRR */ + HLF, HLF, HLF, HLF, + + /* 30 34 38 3C */ + /* CALM LA ADR SUR */ + HLF, SD|ADR, HLF, HLF, + + /* 40 44 48 4C */ + /* MPR DVR */ + SCC|SD|HLF, HLF, HLF|INV, HLF|INV, + + /* 50 54 58 5C */ + /* */ + HLF|INV, HLF|INV, HLF|INV, HLF|INV, + + /* 60 64 68 6C */ + /* NOR NORD SCZ SRA */ + HLF, HLF, HLF, HLF, + + /* 70 74 78 7C */ + /* SRL SRC SRAD SRLD */ + HLF, HLF, HLF, HLF, + + /* 80 84 88 8C */ + /* LEAR ANM ORM EOM */ + SD|ADR, SD|RR|RNX|ADR, SD|RR|RNX|ADR, SD|RR|RNX|ADR, + + /* 90 94 98 9C */ + /* CAM CMM SBM ZBM */ + SCC|RR|RM|ADR, RR|RM|ADR, ADR, ADR, + + /* A0 A4 A8 AC */ + /* ABM TBM EXM L */ + ADR, ADR, ADR, SCC|SD|RM|ADR, + + /* B0 B4 B8 BC */ + /* LM LN ADM SUM */ + SCC|SD|RM|ADR, SCC|SD|RM|ADR, SD|RR|RM|ADR, SD|RR|RM|ADR, + + /* C0 C4 C8 CC */ + /* MPM DVM IMM LF */ + SCC|SD|RM|ADR, RM|ADR, IMM, ADR, + + /* D0 D4 D8 DC */ + /* LEA ST STM STF */ + SD|ADR, RR|SM|ADR, RR|SM|ADR, ADR, + + /* E0 E4 E8 EC */ + /* ADF MPF ARM BCT */ + ADR, ADR, SM|RR|RNX|ADR, ADR, + + /* F0 F4 F8 FC */ + /* BCF BI MISC IO */ + ADR, RR|SD|WRD, ADR, IMM, +}; + +int base_mode[] = { + /* 00 04 08 0C */ + /* 00 AND, OR, EOR */ + HLF, SCC|R1|RR|SD|HLF, SCC|R1|RR|SD|HLF, SCC|R1|RR|SD|HLF, + + /* 10 14 18 1C */ + /* SACZ CMR xBR SRx */ + HLF, HLF, HLF, HLF, + + /* 20 24 28 2C */ + /* SRxD SRC REG TRR */ + HLF, HLF, HLF, HLF, + + /* 30 34 38 3C */ + /* LA FLRop SUR */ + INV, INV, HLF, HLF, + + /* 40 44 48 4C */ + /* */ + INV, INV, INV, INV, + + /* 50 54 58 5C */ + /* LA BASE BASE CALLM */ + SD|ADR, SM|ADR, SB|ADR, RM|ADR, + + /* 60 64 68 6C */ + /* */ + INV, INV, INV, INV, + + /* 70 74 78 7C */ + /* */ + INV, INV, INV, INV, + + /* LEAR ANM ORM EOM */ + /* 80 84 88 8C */ + SD|ADR, SD|RR|RNX|ADR, SD|RR|RNX|ADR, SD|RR|RNX|ADR, + + /* CAM CMM SBM ZBM */ + /* 90 94 98 9C */ + SCC|RR|RM|ADR, RR|RM|ADR, ADR, ADR, + + /* A0 A4 A8 AC */ + /* ABM TBM EXM L */ + ADR, ADR, ADR, SCC|SD|RM|ADR, + + /* B0 B4 B8 BC */ + /* LM LN ADM SUM */ + SCC|SD|RM|ADR, SCC|SD|RM|ADR, SD|RR|RM|ADR, SD|RR|RM|ADR, + + /* C0 C4 C8 CC */ + /* MPM DVM IMM LF */ + SCC|SD|RM|ADR, RM|ADR, IMM, ADR, + + /* D0 D4 D8 DC */ + /* LEA ST STM STFBR */ + INV, RR|SM|ADR, RR|SM|ADR, ADR, + + /* E0 E4 E8 EC */ + /* ADF MPF ARM BCT */ + ADR, ADR, SM|RR|RNX|ADR, ADR, + + /* F0 F4 F8 FC */ + /* BCF BI MISC IO */ + ADR, RR|SD|WRD, ADR, IMM, +}; + +/* Map image descriptor 32/77 */ +/* |--------------------------------------| */ +/* |0|1|2|3 4 5 6|7 8 9 10 11 12 13 14 15| */ +/* |N|V|P| n/u | 9 bit map block entry | */ +/* |U| | | | 32kb/block | */ +/* | | 32 8kb maps per task | */ +/* | | 1 mb address space | */ +/* |--------------------------------------| */ + +/* Map image descriptor 32/27 */ +/* |--------------------------------------| */ +/* |0|1|2|3|4|5 6 7 8 9 10 11 12 13 14 15| */ +/* |V|P|P|P|P| 11 bit map block entry | */ +/* | |1|2|3|4| 8kb/block | */ +/* | | 256 8kb maps per task | */ +/* | | 2 mb address space | */ +/* |--------------------------------------| */ + +/* Map image descriptor 32/67, 32/87, 32/97 */ +/* |--------------------------------------| */ +/* |0|1|2|3|4|5 6 7 8 9 10 11 12 13 14 15| */ +/* |V|P|P|P|P| 11 bit map block entry | */ +/* | |1|2|3|4| 2kb/block | */ +/* | | 2048 8kb maps per task | */ +/* | | 16 mb address space | */ +/* |--------------------------------------| */ +/* BIT 0 = 0 Invalid map block (page) entry */ +/* = 1 Valid map block (page) entry */ +/* 1 = 0 000-7ff of 8kb page is not write protected */ +/* = 1 000-7ff of 8kb page is write protected */ +/* 2 = 0 800-fff of 8kb page is not write protected */ +/* = 1 800-fff of 8kb page is write protected */ +/* 3 = 0 1000-17ff of 8kb page is not write protected */ +/* = 1 1000-17ff of 8kb page is write protected */ +/* 4 = 0 1800-1fff of 8kb page is not write protected */ +/* = 1 1800-1fff of 8kb page is write protected */ +/* 5-15 = 11 most significant bits of the 24 bit real address for page */ + +/* Map image descriptor V6 & V9 */ +/* |--------------------------------------| */ +/* |0|1|2|3|4|5 6 7 8 9 10 11 12 13 14 15| */ +/* |V|P|P|M|M| 11 bit map block entry | */ +/* | |1|2|M|A| 2kb/map | */ +/* | | 2048 8kb maps per task | */ +/* | | 16 mb address space | */ +/* |--------------------------------------| */ +/* BIT 0 = 0 Invalid map block (page) entry */ +/* = 1 Valid map block (page) entry */ +/* */ +/* PSD 1 BIT 0 - Map Bit 1 - Map Bit 2 - Access state */ +/* Priv Bits with ECO for Access Protection change */ +/* 0 0 0 No access allowed to page */ +/* 0 0 1 No access allowed to page */ +/* 0 1 0 Read/Write/Execute access */ +/* 0 1 1 Read/Execute access only */ +/*O/S*/ +/* 1 0 0 Read/Write/Execute access */ +/* 1 0 1 Read/Execute access only */ +/* 1 1 0 Read/Write/Execute access */ +/* 1 1 1 Read/Execute access only */ +/* Priv Bits without ECO for Access Protection change */ +/* 0 0 0 No access allowed to page */ +/* 0 0 1 Read/Execute access only */ +/* 0 1 0 Read//Execute access only */ +/* 0 1 1 Read/Write/Execute access */ +/*O/S*/ +/* 1 0 0 Read/Write/Execute only */ +/* 1 0 1 Read/Execute access only */ +/* 1 1 0 Read/Write/Execute access */ +/* 1 1 1 Read/Write/Execute access */ +/* */ +/* BIT 3 = 0 (MM) A first write (modify) to the map block (page) has not occurred */ +/* = 1 (MM) A first write (modify) to the map block (page) has occurred */ +/* BIT 4 = 0 (MA) A first read or write (access) to the map block (page) has not occurred */ +/* = 1 (MA) A first read or write (access) to the map block (page) has occurred */ +/* 5-15 = 11 most significant bits of the 24 bit real address for page */ + +/* Note */ +/* If a map is valid, a MAP (page) hit occurs and logical to physical translation occures */ +/* If the map is not valid, a demand MAP (page) fault occures and the faulting page is provided */ +/* P1 and P2 are used with Bit 0 of PSD to define the access rights */ +/* A privilege violation trap occurres if access it denied */ +/* Bits 5-15 contain the 11 most-significant bits of the physical address */ +/* MSD 0 page limit is used to verify access to O/S pages */ +/* CPIXPL page limit is used to verify access to user pages and page faults */ +/* CPIX CPIX of user MPL offset */ +/* Access to pages outside the limit registers results in a map fault */ + +#define MAX32 32 /* 32/77 map limit */ +#define MAX256 256 /* 32/27 and 32/87 map limit */ +#define MAX2048 2048 /* 32/67, V6, and V9 map limit */ + +/* set up the map registers for the current task in the cpu */ +/* the PSD bpix and cpix are used to setup the maps */ +/* return non-zero if mapping error */ +/* if lmap set, always load maps on 67, 97, V6, and V7 */ +/* The RMW and WMW macros are used to read/write memory words */ +/* RMW(addr) or WMW(addr, data) where addr is a byte alligned word address */ +/* The RMR and WMR macros are used to read/write the MAPC cache registers */ +/* RMR(addr) or WMR(addr, data) where addr is a half word alligned address */ +/* We will only get here if the retain maps bit is not set in PSD word 2 */ +t_stat load_maps(uint32 thepsd[2], uint32 lmap) +{ + uint32 num, sdc, spc, onlyos=0; + uint32 mpl, cpixmsdl, bpixmsdl, msdl, midl; + uint32 cpix, bpix, i, j, map, osmsdl, osmidl; + uint32 MAXMAP = MAX2048; /* default to 2048 maps */ + + sim_debug(DEBUG_TRAP, &cpu_dev, + "Load Maps Entry PSD %08x %08x STATUS %08x lmap %1x CPU Mode %2x\n", + thepsd[0], thepsd[1], CPUSTATUS, lmap, CPU_MODEL); + + /* process 32/7X computers */ + if (CPU_MODEL < MODEL_27) { + MAXMAP = MAX32; /* 32 maps for 32/77 */ + /* 32/7x machine, 8KW maps 32 maps total */ + MODES &= ~BASEBIT; /* no basemode on 7x */ + if ((thepsd[1] & 0xc0000000) == 0) /* mapped mode? */ + return ALLOK; /* no, all OK, no mapping required */ + + /* we are mapped, so load the maps for this task into the cpu map cache */ + cpix = (thepsd[1]) & 0x3ff8; /* get cpix 12 bit offset from psd wd 2 */ + bpix = (thepsd[1] >> 16) & 0x3ff8; /* get bpix 12 bit offset from psd wd 2 */ + num = 0; /* working map number */ + + /* master process list is in 0x83 of spad for 7x */ + mpl = SPAD[0x83]; /* get mpl from spad address */ + + /* diags want the mpl entries checked to make sure valid dbl wowrd address */ + if (mpl & 0x7) { /* test for double word address */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MPL not on double word boundry %06x\n", mpl); + TRAPSTATUS |= BIT20; /* set bit 20 of trap status */ + return MAPFLT; /* not dbl bound, map fault error */ + } + + /* check if valid real address */ + if ((mpl == 0) || !MEM_ADDR_OK(mpl & MASK24)) { /* see if in memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MEM SIZE7 %06x mpl %06x invalid\n", + MEMSIZE, mpl); + TRAPSTATUS |= BIT18; /* set bit 18 of trap status */ + return MAPFLT; /* no, map fault error */ + } + + /* mpl is ok, get the msdl for given cpix */ + cpixmsdl = RMW(mpl+cpix); /* get msdl from mpl for given cpix */ + + /* if bit zero of mpl entry is set, use bpix first to load maps */ + if (cpixmsdl & BIT0) { + + /* load bpix maps first */ + bpixmsdl = RMW(mpl+bpix); /* get bpix msdl word address */ + + /* check for valid bpix msdl addr */ + if (!MEM_ADDR_OK(bpixmsdl & MASK24)) { /* see if in memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MEM SIZE8 %06x bpix msdl %08x invalid\n", + MEMSIZE, bpixmsdl); + return NPMEM; /* no, none present memory error */ + } + + sdc = (bpixmsdl >> 24) & 0x3f; /* get 6 bit segment description count */ + msdl = bpixmsdl & MASK24; /* get 24 bit real address of msdl */ + /* check for valid msdl addr */ + if (!MEM_ADDR_OK(msdl & MASK24)) { /* see if in memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MEM SIZE9 %06x msdl %08x invalid\n", MEMSIZE, msdl); + return NPMEM; /* no, none present memory error */ + } + + /* process all of the msdl's */ + for (i = 0; i < sdc; i++) { /* loop through the msd's */ + spc = (RMW(msdl+i) >> 24) & 0xff; /* get segment page count from msdl */ + midl = RMW(msdl+i) & MASK24; /* get 24 bit real word address of midl */ + + /* check for valid midl addr */ + if (!MEM_ADDR_OK(midl & MASK24)) { /* see if in memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MEM SIZEa %06x midl %08x invalid\n", MEMSIZE, midl); + return NPMEM; /* no, none present memory error */ + } + + for (j = 0; j < spc; j++, num++) { /* loop throught the midl's */ + uint32 pad = RMW(midl+(j<<1)); /* get page descriptor address */ + if (num >= MAXMAP) { + TRAPSTATUS |= BIT5; /* set bit 5 of trap status */ + return MAPFLT; /* map loading overflow, map fault error */ + } + /* load 16 bit map descriptors */ + map = RMH(pad); /* get 16 bit map entries */ + WMR((num<<1), map); /* store the map reg contents into cache */ + } + } + } + + /* now load cpix maps */ + /* check for valid cpix msdl addr */ + if (MEM_ADDR_OK(cpixmsdl & MASK24)) { /* see if in memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MEM SIZEb %06x cpix msdl %08x invalid\n", MEMSIZE, cpixmsdl); + return NPMEM; /* no, none present memory error */ + } + + sdc = (cpixmsdl >> 24) & 0x3f; /* get 6 bit segment description count */ + msdl = cpixmsdl & 0xffffff; /* get 24 bit real address of msdl */ + /* check for valid msdl addr */ + if (!MEM_ADDR_OK(msdl & MASK24)) { /* see if in memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MEM SIZEc %06x msdl %08x invalid\n", MEMSIZE, msdl); + return NPMEM; /* no, none present memory error */ + } + + /* process all of the msdl's */ + for (i = 0; i < sdc; i++) { + spc = (RMW(msdl+i) >> 24) & 0xff; /* get segment page count from msdl */ + midl = RMW(msdl+i) & MASK24; /* get 24 bit real word address of midl */ + + /* check for valid midl addr */ + if (!MEM_ADDR_OK(midl & MASK24)) { /* see if in memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MEM SIZEd %06x midl %08x invalid\n", MEMSIZE, midl); + return NPMEM; /* no, none present memory error */ + } + + for (j = 0; j < spc; j++, num++) { /* loop through the midl's */ + uint32 pad = RMW(midl+(j<<1)); /* get page descriptor address */ + if (num >= MAXMAP) { + TRAPSTATUS |= (BIT16|BIT9); /* set bit 5 of trap status */ + return MAPFLT; /* map loading overflow, map fault error */ + } + /* load 16 bit map descriptors */ + map = RMH(pad); /* get 16 bit map entries */ + WMR((num<<1), map); /* store the map reg unmodified into cache */ + } + } + /* if none loaded, map fault */ + if (num == 0) { + TRAPSTATUS |= (BIT16|BIT9); /* set bit 5 of trap status */ + return MAPFLT; /* attempt to load 0 maps, map fault error */ + } + /* clear the rest of the previously used maps */ + for (i = num; i < HIWM; i++) /* zero any remaining entries */ + WMR((i<<1), 0); /* clear the map entry to make not valid */ + HIWM = num; /* set new high water mark */ + return ALLOK; /* all cache is loaded, return OK */ + } + /****************** END-OF-32/7X-MAPPING ********************/ + + /* process a 32/27, 32/67, 32/87, 32/97, V6, or V9 here with 2KW (8kb) maps */ + /* 32/27 & 32/87 have 256 maps. Others have 2048 maps */ + /* 32/27 & 32/87 must have all maps preallocated and loaded */ + /* 32/67 & 32/97 must load O/S maps and have user preallocated maps loaded on access */ + /* V6 and V9 must load O/S maps and have user maps allocated and loaded on access */ + + /* See if any mapping to take place */ + if ((MODES & MAPMODE) == 0) /* mapped mode? */ + return ALLOK; /* no, all OK, no mapping required */ + + /* set maximum maps for 32/27 and 32/87 processors */ + if ((CPU_MODEL == MODEL_27) || (CPU_MODEL == MODEL_87)) + MAXMAP = MAX256; /* only 256 2KW (8kb) maps */ + + /* we are mapped, so load the map definitions */ + cpix = thepsd[1] & 0x3ff8; /* get cpix 11 bit offset from psd wd 2 */ + num = 0; /* no maps loaded yet */ + + /* master process list is in 0xf3 of spad for concept machines */ + mpl = SPAD[0xf3]; /* get mpl from spad address */ + + /* diags want the mpl entries checked to make sure valid dbl word address */ + if (mpl & 0x7) { /* test for double word address */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MPL not on double word boundry %06x\n", mpl); + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT6; /* set bit 6 of trap status */ + else + TRAPSTATUS |= BIT20; /* set bit 20 of trap status */ + return MAPFLT; /* no, map fault error */ + } + + /* check if valid real address */ + mpl &= MASK24; /* clean mpl address */ + if (!MEM_ADDR_OK(mpl)) { /* see if in our real memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MEM SIZE1 %06x mpl %06x invalid\n", MEMSIZE, mpl); +npmem: + BPIX = 0; /* no os maps loaded */ + CPIXPL = 0; /* no user pages */ + CPIX = cpix; /* save user CPIX */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) { + TRAPSTATUS |= BIT1; /* set bit 1 of trap status */ + } else + TRAPSTATUS |= BIT10; /* set bit 8 of trap status */ + return NPMEM; /* non present memory error */ + } + + /* output O/S and User MPL entries */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "#MEMORY %06x MPL %06x MPL[0] %08x %06x MPL[%04x] %08x %06x\n", + MEMSIZE, mpl, RMW(mpl), RMW(mpl+4), cpix, + RMW(cpix+mpl), RMW(cpix+mpl+4)); + sim_debug(DEBUG_DETAIL, &cpu_dev, + "MEMORY2 %06x BPIX %04x cpix %04x CPIX %04x CPIXPL %04x HIWM %04x\n", + MEMSIZE, BPIX, cpix, CPIX, CPIXPL, HIWM); + + /* load the users regs first or the O/S. Verify the User MPL entry too. */ + /* If bit zero of cpix mpl entry is set, use msd entry 0 first to load maps */ + /* Then load the user maps after the O/S */ + /* If the cpix is zero, then only load the O/S. */ + /* This test must be made to allow sysgen to run with a zero cpix */ + /* If bit 0 of MPL[0] is 0, load the O/S maps. */ + /* Do not load O/S if bit 0 of O/S MPL[0] is set. It is set by the */ + /* swapper on MPX startup */ + + /* mpl is valid, get msdls for O/S and User */ + osmidl = RMW(mpl); /* get O/S map count & retain flag from MPL[0] */ + osmsdl = RMW(mpl+4); /* get msdl pointer for OS from MPL[1] */ + midl = RMW(mpl+cpix); /* get midl entry for given user cpix */ + msdl = RMW(mpl+cpix+4); /* get mpl entry wd 1 for given cpix */ + spc = osmidl & MASK16; /* get 16 bit O/S segment description count */ + + /* see if we are to only load the O/S */ + if (cpix == 0) { + CPIX = cpix; /* save CPIX */ + onlyos = 1; /* flag to only load O/S, nothing else */ + if (osmidl & BIT0) { /* see if the O/S retain bit 0 is on */ + return ALLOK; /* O/S retain bit is set, no mapping required */ + } + +loados: /* merge point for loading O/S first */ + /* to be followed by user maps */ + /* the retain bit is not set so load the O/S */ + if (spc > MAXMAP) { + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps bad O/S page count %04x, map fault\n", spc); +nomaps: + /* Bad map load count specified. */ + BPIX = 0; /* no os maps loaded */ + CPIXPL = 0; /* no user pages */ + CPIX = cpix; /* save CPIX */ + HIWM = 0; /* reset high water mark */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= (BIT5|BIT9); /* set bit 5/9 of trap status */ + else + TRAPSTATUS |= BIT16; /* set bit 16 of trap status */ + return MAPFLT; /* map loading overflow, map fault error */ + } + + /* we have a valid count, load the O/S map list address */ + osmsdl &= MASK24; /* get 24 bit real address from mpl 0 wd2 */ + if (!MEM_ADDR_OK(osmsdl)) { /* see if address is within our memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MEM SIZE2 %06x os page list address %06x invalid\n", + MEMSIZE, osmsdl); + goto npmem; /* non present memory trap */ + } + + /* load the O/S maps */ + for (j = 0; j < spc; j++, num++) { /* copy maps from msdl to map cache */ + uint32 pad = osmsdl+(j<<1); /* get page descriptor address */ + + /* see if map overflow */ + if (num >= MAXMAP) { + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps O/S page count overflow %04x, map fault\n", num); + goto nomaps; /* map overflow, map fault trap */ + } + if (!MEM_ADDR_OK(pad)) { /* see if address is within our memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MEM SIZE3 %06x os page address %06x invalid\n", + MEMSIZE, pad); + goto npmem; /* non present memeory trap */ + } + /* load 16 bit map descriptors */ + map = RMH(pad); /* get page descriptor from memory */ + + /* for valid maps translate the map number to a real address */ + /* put this address in the TLB for later translation */ + /* copy the map status bits too and set hit bit in the TLB */ + if (map & 0x8000) { /* see if map is valid */ + TLB[num] = (((map & 0x7ff) << 13) | ((map << 16) & 0xf8000000)); + TLB[num] |= 0x04000000; /* set HIT bit for non lmap */ + WMR((num<<1), map); /* store the map unmodified into cache */ + } else { + TLB[num] = 0; /* clear the TLB for non valid maps */ + } + } + BPIX = num; /* save the # maps loaded in O/S */ + CPIXPL = 0; /* no user pages */ + + if (!onlyos) /* see if only O/S to be loaded */ + goto loaduser; /* no, go load the user maps */ + + for (i = BPIX; i < MAXMAP; i++) /* zero any remaining entries */ + TLB[i] = 0; /* clear look aside buffer */ + + /* Only the O/S is to be loaded, finish up & return */ + HIWM = num; /* set new high water mark */ + return ALLOK; /* all cache is loaded, return OK */ + } + + /* The csect is not zero here, so see what we have to do */ + /* See if O/S is to be loaded first because user MPL entry has BIT 0 set */ + if (midl & BIT0) { + /* the user wants the O/S to load first, if the O/S retain bit set? */ + if (osmidl & BIT0) { /* see if the O/S retain bit 0 is on */ + num = spc; /* yes, set the number of O/S maps loaded */ + BPIX = spc; /* save the # maps in O/S */ + goto loaduser; /* load user map only or after O/S */ + } + + /* no retain bit, load user maps only, or after O/S */ + /* validate O/S map count */ + if (spc > MAXMAP) { + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps bad O/S page count %04x, map fault\n", spc); + goto nomaps; /* we have error, make way out */ + } + + /* see if any O/S maps to load */ + if (spc == 0) { + BPIX = 0; /* no os maps loaded */ + /* O/S page count is zero, so just load the user */ + goto loaduser; /* load user map only or after O/S */ + } + /* user wants to have O/S loaded first */ + onlyos = 0; /* return to loaduser after loading O/S */ + goto loados; /* go load the O/S */ + } + + /* the user wants to only load the user maps, no O/S maps are to be retained */ + BPIX = 0; /* clear O/S loaded page count */ + num = 0; /* nothing loaded yet */ + /****************** END-OF-O/S-MAPPING ********************/ + +loaduser: + spc = midl & MASK16; /* get 16 bit User page count */ + + /* see if O/S has already loaded the MAXMAPS */ + /* if borrow bit is on and cpix count is zero, return ok */ + /* if borrow bit is on and cpix count not zero, map overflow error */ + if (BPIX == MAXMAP) { + HIWM = num; /* set new high water mark */ + CPIXPL = 0; /* no user pages */ + if ((midl & BIT0) && (spc == 0)) { /* see if the user had borrow bit on */ + sim_debug(DEBUG_CMD, &cpu_dev, + "load_maps @loaduser num %04x BPIX loaded %04x load done\n", num, BPIX); + return ALLOK; /* all cache is loaded, return OK */ + } + else { + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps map overflow BPIX %04x count %04x, map fault\n", BPIX, spc); + goto nomaps; /* we have error, make way out */ + } + } + + /* the O/S has been loaded if requested or retained, so now load the user */ + msdl &= MASK24; /* get 24 bit real word address of msdl */ + + /* This test fails cn.mmm diag at test 46, subtest 2 with unexpected error */ + /* Do this test if we are a LMAP instruction and not a 32/27 or 32/87 */ + if (lmap && !MEM_ADDR_OK(msdl)) { /* see if address is within our memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MEM SIZE4 %06x user page list address %06x invalid\n", + MEMSIZE, msdl); + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) { + TRAPSTATUS |= BIT1; /* set bit 1 of trap status */ + } else + TRAPSTATUS |= BIT28; /* set bit 28 of trap status */ + return NPMEM; /* non present memory error */ + } + + /* We have a valid user MPL[cpix] address, msdl */ + spc = midl & MASK16; /* get 16 bit User page count */ + + /* it is OK here to have no O/S maps loaded, num can be 0 */ + if ((spc > MAXMAP) || ((spc+BPIX) > MAXMAP)) { + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps bad User page count %04x num %04x bpix %04x, map fault\n", + spc, num, BPIX); + /* Bad map load count specified. */ + BPIX = 0; /* no os maps loaded */ + CPIXPL = 0; /* no user pages */ + CPIX = cpix; /* save CPIX */ + HIWM = 0; /* reset high water mark */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= (BIT5|BIT9); /* set bit 5/9 of trap status */ + else + TRAPSTATUS |= BIT16; /* set bit 16 of trap status */ + return MAPFLT; /* map overflow fault error */ + } + CPIX = cpix; /* save user MPL offset (cpix) */ + CPIXPL = spc; /* save user map load count */ + + /* Load maps for 32/27 aand 32/87 */ + if ((CPU_MODEL == MODEL_27) || (CPU_MODEL == MODEL_87)) { + + sim_debug(DEBUG_CMD, &cpu_dev, + "load_maps Processing 32/27 & 32/87 Model# %02x\n", CPU_MODEL); + + /* handle non virtual page loading or diag LMAP instruction */ + /* do 32/27 and 32/87 that force load all maps */ + /* now load user maps specified by the cpix value */ + for (j = 0; j < spc; j++, num++) { /* copy maps from midl to map cache */ + uint32 pad = msdl+(j<<1); /* get page descriptor address */ + + /* see if map overflow */ + if (num >= MAXMAP) { + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps User page count overflow %04x, map fault\n", num); + TRAPSTATUS |= BIT16; /* set bit 16 of trap status */ + TRAPSTATUS |= (BIT5|BIT9); /* set bit 5 of trap status */ + goto nomaps; /* map overflow, map fault trap */ + } + if (!MEM_ADDR_OK(pad)) { /* see if address is within our memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MEM SIZE5 %06x User page address %06x invalid\n", + MEMSIZE, pad); + goto npmem; /* non present memeory trap */ + } + /* load 16 bit map descriptors */ + map = RMH(pad); /* get page descriptor from memory */ + + /* for valid maps translate the map number to a real address */ + /* put this address in the TLB for later translation */ + /* copy the map status bits too and set hit bit in the TLB */ + /* leaving out diags in next statment fails test3/1 of vm.mmm */ + if ((map & 0x8000)) { /* see if map is valid */ + TLB[num] = (((map & 0x7ff) << 13) | ((map << 16) & 0xf8000000)); + TLB[num] |= 0x04000000; /* set HIT bit on */ + } else + TLB[num] = 0; /* clear the TLB for non valid maps */ + WMR((num<<1), map); /* store map unmodified into cache */ + } + if (num == 0) { /* see if any maps loaded */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps1 No maps loaded %04x, map fault\n", num); + goto nomaps; /* return map fault error */ + } + + /* All maps are now loaded, finish up & return */ + for (i = num; i < MAXMAP; i++) /* zero any remaining TLB entries */ + TLB[i] = 0; /* clear look aside buffer */ + + HIWM = num; /* set new high water mark */ + return ALLOK; /* all cache is loaded, return OK */ + } + /**************END-OF-NON-VIRTUAL-USER-MAPPING-FOR-27-87************/ + + sim_debug(DEBUG_CMD, &cpu_dev, + "load_maps Processing 32/67 & 32/97 Model# %02x\n", CPU_MODEL); + + /* handle load on memory access case for 67, 97, V6 & V9 */ + /* now clear TLB & maps specified by the cpix value */ + for (j = 0; j < spc; j++, num++) { /* clear maps in map cache */ + uint32 pad = msdl+(j<<1); /* get page descriptor address */ + + /* if this is a LPSDCM instruction, just clear the TLB entry */ + if (!lmap) { + /* load 16 bit map descriptors */ + map = RMH(pad); /* get page descriptor from memory */ + TLB[num] = 0; /* clear the TLB for non valid maps */ + if ((num < 0x20) || (num > (spc+BPIX) - 0x10)) + sim_debug(DEBUG_DETAIL, &cpu_dev, + "UserV pad %06x=%04x map #%4x, %04x, map2 %08x, TLB %08x, MAPC %08x\n", + pad, map, num, map, (((map << 16) & 0xf8000000)|(map & 0x7ff)<<13)|0x04000000, + TLB[num], MAPC[num/2]); + continue; /* just clear the TLBs */ + } + + /* only do the following tests for LMAP instruction, not LPSDCM */ + /* see if map overflow */ + if (num >= MAXMAP) { + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps User page count overflow %04x, map fault\n", num); + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= (BIT5|BIT9); /* set bit 5/9 of trap status */ + else + TRAPSTATUS |= BIT16; /* set bit 16 of trap status */ + goto nomaps; /* map overflow, map fault trap */ + } + + if (!MEM_ADDR_OK(pad)) { /* see if address is within our memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps MEM SIZE6 %06x User page address %06x non present\n", + MEMSIZE, pad); + goto npmem; /* non present memeory trap */ + } + + /* load 16 bit map descriptors */ + map = RMH(pad); /* get page descriptor from memory */ + + if (lmap) { + TLB[num] = (((map & 0x7ff) << 13) | ((map << 16) & 0xf8000000)); + TLB[num] |= 0x04000000; /* set HIT bit for lmap */ + /* removing this store of map fails test 2 on 97 */ + WMR((num<<1), map); /* store the map unmodified into cache */ + } + + if ((num < 0x20) || (num > (spc+BPIX) - 0x10)) + sim_debug(DEBUG_DETAIL, &cpu_dev, + "UserV2 pad %06x=%04x map #%4x, %04x, map2 %08x, TLB %08x, MAPC %08x\n", + pad, map, num, map, (((map << 16) & 0xf8000000)|(map & 0x7ff)<<13)|0x04000000, + TLB[num], MAPC[num/2]); + } + + if (num == 0) { /* see if any maps loaded */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "load_maps2 No maps loaded %04x, map fault\n", num); + goto nomaps; + } + + /* All maps are now loaded, finish up & return */ + /* removing this code causes diag to stop at 17/0 for 97 in cn.mmm */ + for (i = num; i < MAXMAP; i++) /* zero any remaining entries */ + TLB[i] = 0; /* clear look aside buffer */ + + HIWM = num; /* set new high water mark */ + return ALLOK; /* all cache is loaded, return OK */ + /****************** END-OF-VIRTUAL-USER-MAP-LOADING ********************/ +} + +/* + * Return the real memory address from the logical address + * Also return the protection status, 1 if write protected address. + * For 67, 97, V6, & V9 return all protection bits. + * Addr is a byte address. + */ +t_stat RealAddr(uint32 addr, uint32 *realaddr, uint32 *prot, uint32 access) +{ + uint32 word, index, map, raddr, mpl, offset; + uint32 nix, msdl, mix; + + *prot = 0; /* show unprotected memory as default */ + /* unmapped mode is unprotected */ + + /*****************START-7X-ADDRESS-PROCESSING****************/ + /* see what machine we have */ + if (CPU_MODEL < MODEL_27) { + /* 32/7x machine with 8KW maps */ + if (MODES & EXTDBIT) + word = addr & 0xfffff; /* get 20 bit logical word address */ + else + word = addr & 0x7ffff; /* get 19 bit logical word address */ + if ((MODES & MAPMODE) == 0) { + /* check if valid real address */ + if (!MEM_ADDR_OK(word)) { /* see if address is within our memory */ + return NPMEM; /* no, none present memory error */ + } + *realaddr = word; /* return the real address */ + return ALLOK; /* all OK, return instruction */ + } + + /* we are mapped, so calculate real address from map information */ + /* 32/7x machine, 8KW maps */ + index = word >> 15; /* get 4 or 5 bit value */ + map = RMR((index<<1)); /* read the map reg cache contents */ + + /* see if map is valid */ + if ((map & 0x4000) == 0) + /* map is invalid, so return map fault error */ + return MAPFLT; /* map fault error */ + + /* required map is valid, get 9 bit address and merge with 15 bit page offset */ + word = ((map & 0x1ff) << 15) | (word & 0x7fff); + /* check if valid real address */ + if (!MEM_ADDR_OK(word)) /* see if address is within our memory */ + return NPMEM; /* no, none present memory error */ + if ((MODES & PRIVBIT) == 0) { /* see if we are in unprivileged mode */ + if (map & 0x2000) /* check if protect bit is set in map entry */ + *prot = 1; /* return memory write protection status */ + } + *realaddr = word; /* return the real address */ + return ALLOK; /* all OK, return instruction */ + } + /*****************END-OF-7X-ADDRESS-PROCESSING****************/ + + /* Everyone else has 2KW maps */ + /* do common processing */ + /* diag wants the address to be 19 or 24 bit, use masked address */ + if (MODES & (BASEBIT | EXTDBIT)) + word = addr & 0xffffff; /* get 24 bit address */ + else + word = addr & 0x7ffff; /* get 19 bit address */ + + if ((MODES & MAPMODE) == 0) { + /* we are in unmapped mode, check if valid real address */ + if (!MEM_ADDR_OK(word)) { /* see if address is within our memory */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) { + if (access == MEM_RD) + TRAPSTATUS |= BIT1; /* set bit 1 of trap status */ + if (access == MEM_WR) + TRAPSTATUS |= BIT2; /* set bit 2 of trap status */ + } else { + TRAPSTATUS |= BIT10; /* set bit 10 of trap status */ + } + return NPMEM; /* no, none present memory error */ + } + *realaddr = word; /* return the real address */ + return ALLOK; /* all OK, return instruction */ + } + + mpl = SPAD[0xf3] & MASK24; /* get 24 bit dbl wd mpl from spad address */ + + /* did not get expected machine check trap for */ + /* 27, 87, 67 in test 37/0 if code removed */ + /* unexpected machine check trap for 67 in test 37/0 cn.mmm */ + /* now check the O/S midl pointer for being valid */ + /* we may want to delay checking until we actually use it */ + if (!MEM_ADDR_OK((RMW(mpl+4) & MASK24))) { /* check OS midl */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "RealAddr Non Present Memory O/S msdl MPL %06x MPL[1] %06x\n", + mpl, RMW(mpl+4)); + + if ((CPU_MODEL == MODEL_27) || (CPU_MODEL == MODEL_87)) { + // 32/27, 32/87 want MACHINECHK for test 37/1 in CN.MMM + TRAPSTATUS |= BIT10; /* set bit 10 of trap status */ + return MACHINECHK_TRAP; /* diags want machine check error */ + } else + if ((CPU_MODEL == MODEL_67) || (CPU_MODEL == MODEL_V6)) { + TRAPSTATUS |= BIT10; /* set bit 10 of trap status */ + return MAPFLT; /* map fault error */ + } + else + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) { + // V9 & 32/97 wants MACHINECHK for test 37/1 in CN.MMM & VM.MMM + TRAPSTATUS |= (BIT7|BIT9); /* set bit 7 of trap status */ + TRAPSTATUS |= BIT28; /* set bit 28 of trap status */ + return MACHINECHK_TRAP; /* diags want machine check error */ + } + } + + /* we are mapped, so calculate real address from map information */ + /* get 11 bit page number from address bits 8-18 */ + index = (word >> 13) & 0x7ff; /* get 11 bit page value */ + offset = word & 0x1fff; /* get 13 bit page offset */ + + /* make sure map index is valid */ + if (index >= (BPIX + CPIXPL)) { + sim_debug(DEBUG_TRAP, &cpu_dev, + "RealAddr %06x word %06x loadmap gets mapfault index %04x B(%x)+C(%x) %04x\n", + word, addr, index, BPIX, CPIXPL, BPIX+CPIXPL); + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= (BIT5|BIT9); /* set bit 5/9 of trap status */ + else + TRAPSTATUS |= BIT16; /* set bit 16 of trap status */ + return MAPFLT; /* map fault error */ + } + + /* continue processing non virtual machines here 32/27 & 32/87 */ + /* at this point all maps have been force loaded in load_maps */ + /* just do the conversion from logical to real address */ + if ((CPU_MODEL == MODEL_27) || (CPU_MODEL == MODEL_87)) { + map = RMR((index<<1)); /* read the map reg cache contents */ + raddr = TLB[index]; /* get the base address & bits */ + + if (!MEM_ADDR_OK(RMW(mpl+CPIX+4) & MASK24)) { /* check user midl */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "RealAddr 27 & 87 map fault index %04x B+C %04x map %04x TLB %08x\n", + index, BPIX+CPIXPL, map, TLB[index]); + // 32/27 & 32/87 want MACHINECHK for test 37/1 in CN.MMM + TRAPSTATUS |= BIT10; /* set bit 10 of trap status */ + return MACHINECHK_TRAP; /* diags want machine check error */ + } + + if (((map & 0x8000) == 0) || ((raddr & BIT0) == 0)) { /* see if valid map */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "RealAddr loadmap 0a map fault index %04x B+C %04x map %04x TLB %08x\n", + index, BPIX+CPIXPL, map, TLB[index]); + TRAPSTATUS |= BIT10; /* set bit 10 of trap status */ + return MAPFLT; /* no, map fault error */ + } + + // needed for 32/27 & 32/87 + /* check if valid real address */ + if (!MEM_ADDR_OK(raddr & MASK24)) { /* see if address is within our memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "RealAddr loadmap 0c non present memory fault addr %06x raddr %08x index %04x\n", + word, raddr, index); + TRAPSTATUS |= BIT28; /* set bit 28 of trap status */ + return NPMEM; /* no, none present memory error */ + } + word = (raddr & 0xffe000) | offset; /* combine real addr and offset */ + *realaddr = word; /* return the real address */ + if (MODES & PRIVBIT) /* all OK if privledged */ + return ALLOK; /* all OK, return instruction */ + + /* get user protection status of map */ + offset = (word >> 11) & 0x3; /* see which 1/4 page we are in */ + if ((BIT1 >> offset) & raddr) { /* is 1/4 page write protected */ + *prot = 1; /* return memory write protection status */ + } + sim_debug(DEBUG_DETAIL, &cpu_dev, + "RealAddrRa address %08x, TLB %08x MAPC[%03x] %08x wprot %02x prot %02x\n", + word, TLB[index], index/2, MAPC[index/2], (word>>11)&3, *prot); + return ALLOK; /* all OK, return instruction */ + } + /*****************END-OF-27-87-ADDRESS-PROCESSING****************/ + + /* handle 32/67, 32/97 and V6 & V9 here */ + /* Concept 32 machine, 2KW maps */ + /* the index is less than B+C, so setup to get a map */ + if (TLB[index] & 0x04000000) { /* is HIT bit on in TLB */ + /* handle HIT bit already on in TLB here */ + /* diags wants a NPMEM error if physical addr is exceeded */ + index &= 0x7ff; /* map # */ + raddr = TLB[index]; /* get the base address & bits */ + /* check if valid real address */ + if (!MEM_ADDR_OK(raddr & MASK24)) { /* see if address is within our memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "RealAddr loadmap 2a non present memory fault addr %08x raddr %08x index %04x\n", + addr, raddr, index); + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) { + if (access == MEM_RD) + TRAPSTATUS |= BIT1; /* set bit 1 of trap status */ + else + if (access == MEM_WR) + TRAPSTATUS |= BIT2; /* set bit 2 of trap status */ + } else + TRAPSTATUS |= BIT28; /* set bit 28 of trap status */ + return NPMEM; /* none present memory error */ + } + map = RMR((index<<1)); /* read the map reg contents */ + word = (raddr & 0xffe000) | offset; /* combine map and offset */ + *realaddr = word; /* return the real address */ + + /* handle 32/67 & 32/97 protection here */ + if (CPU_MODEL < MODEL_V6) { + /* process 32/67 & 32/97 load map on access */ + /* handle 32/67 & 32/97 */ + if (MODES & PRIVBIT) /* all OK if privledged */ + return ALLOK; /* all OK, return instruction */ + + /* get protection status of map */ + offset = (word >> 11) & 0x3; /* see which 1/4 page we are in */ + if ((BIT1 >> offset) & raddr) { /* is 1/4 page write protected */ + *prot = 1; /* return memory write protection status */ + } + sim_debug(DEBUG_DETAIL, &cpu_dev, + "RealAddrR address %08x, TLB %08x MAPC[%03x] %08x wprot %02x prot %02x\n", + word, TLB[index], index/2, MAPC[index/2], (word>>11)&3, *prot); + return ALLOK; /* all OK, return instruction */ + } + + /* handle valid V6 & V9 HIT bit on */ + /* get protection status of map */ + offset = ((map >> 12) & 0x6); /* get bits p1 & p2 from map into bits 13 & 14 */ + if (MODES & PRIVBIT) /* all access if privledged */ + *prot = offset | 0x8; /* set priv bit */ + else + *prot = offset; /* return memory write protection status */ + + sim_debug(DEBUG_DETAIL, &cpu_dev, + "RealAddrX address %06x, TLB %06x MAPC[%03x] %08x wprot %02x prot %02x\n", + word, TLB[index], index/2, MAPC[index/2], (word>>11)&3, *prot); + return ALLOK; /* all OK, return instruction */ + } + + /* Hit bit is off in TLB, so lets go get some maps */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "$MEMORY %06x HIT MPL %06x MPL[0] %08x %06x MPL[%04x] %08x %06x\n", + MEMSIZE, mpl, RMW(mpl), RMW(mpl+4), CPIX, RMW(CPIX+mpl), RMW(CPIX+mpl+4)); + + /* check user msdl address now that we are going to access it */ + msdl = RMW(mpl+CPIX+4); /* get msdl entry for given CPIX */ + if (!MEM_ADDR_OK(msdl & MASK24)) { /* check user midl */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "RealAddr User CPIX Non Present Memory User msdl %06x CPIX %04x\n", + msdl, CPIX); + + if (CPU_MODEL == MODEL_67) { + /* test 37/0 wants MAPFLT trap for 67 */ + TRAPSTATUS |= BIT28; /* set bit 28 of trap status */ + return MAPFLT; /* map fault error on memory access */ + } else + if (CPU_MODEL == MODEL_97) { + // 32/97 wants MAPFLT for test 37/1 in CN.MMM + TRAPSTATUS |= BIT12; /* set bit 12 of trap status */ + TRAPSTATUS |= (BIT7|BIT9); /* set bit 7/9 of trap status */ + TRAPSTATUS |= BIT10; /* set bit 10 of trap status */ + return MAPFLT; /* no, map fault error */ + } else + if (CPU_MODEL == MODEL_V6) { + // V6 wants MAPFLT for test 37/1 in CN.MMM & VM.MMM */ + TRAPSTATUS |= BIT28; /* set bit 28 of trap status */ + /* OK for V6 */ + return MAPFLT; /* map fault error */ + } + else + if (CPU_MODEL == MODEL_V9) { + /* V9 wants MAPFLT for test 37/1 in CN.MMM & VM.MMM */ + /* V9 fails test 46/subtest 2 with "did not get expected map trap */ + TRAPSTATUS |= BIT12; /* set bit 12 of trap status */ + TRAPSTATUS |= (BIT7|BIT9); /* set bit 7 of trap status */ + TRAPSTATUS |= BIT10; /* set bit 10 of trap status */ + return MAPFLT; /* map fault error */ + } + } + + /* get os or user msdl, index < BPIX; os, else user */ + if (index < BPIX) + msdl = RMW(mpl+4); /* get mpl entry wd 1 for os */ + else + /* check user msdl address now that we are going to access it */ + msdl = RMW(mpl+CPIX+4); /* get mpl entry wd 1 for given cpix */ + + /* HIT bit is off, we must load the map entries from memory */ + /* turn on the map hit flag if valid and set maps real base addr */ + nix = index & 0x7ff; /* map # or mapc index */ + word = (TLB[nix] & 0xffe000) | offset; /* combine map and offset */ + if (index < BPIX) + mix = nix; /* get map index in memory */ + else + mix = nix-BPIX; /* get map index in memory */ + map = RMH(msdl+(mix<<1)); /* map content from memory */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Addr %06x RealAddr %06x Map0[%04x] HIT %04x TLB[%3x] %08x MAPC[%03x] %08x\n", + addr, word, mix, map, nix, TLB[nix], nix/2, MAPC[nix/2]); + + /* process HIT bit off V6 & V9 here */ + if ((map & 0x8000) == 0) { + *realaddr = word; /* return the real address */ + /* for V6 & V9 handle demand paging */ + if (CPU_MODEL >= MODEL_V6) { + /* map is not valid, so we have map fault */ + sim_debug(DEBUG_EXP, &cpu_dev, + "AddrMa %06x RealAddr %06x Map0 MISS %04x, TLB[%3x] %08x MAPC[%03x] %08x\n", + addr, word, map, nix, TLB[nix], nix/2, MAPC[nix/2]); + /* do a demand page request for the required page */ + pfault = nix; /* save page number */ + sim_debug(DEBUG_EXP, &cpu_dev, + "Mem_write Daddr2 %06x page %04x demand page bits set TLB %08x map %04x\n", + addr, nix, TLB[nix], map); + return DMDPG; /* demand page request */ + } + /* handle 67 & 97 map invalid here */ + if (CPU_MODEL == MODEL_97) { + if (access == MEM_RD) + TRAPSTATUS |= BIT1; /* set bit 1 of trap status */ + else + if (access == MEM_WR) + TRAPSTATUS |= BIT2; /* set bit 2 of trap status */ + } else + TRAPSTATUS |= BIT28; /* set bit 28 of trap status */ + return MAPFLT; /* map fault error */ + } + + /* map is valid, process it */ + TLB[nix] = ((map & 0x7ff) << 13) | ((map << 16) & 0xf8000000) | 0x04000000; + word = (TLB[nix] & 0xffe000) | offset; /* combine map and offset */ + WMR((nix<<1), map); /* store the map reg contents into MAPC cache */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "RealAddrm RMH %04x mix %04x TLB[%04x] %08x B+C %04x RMR[nix] %04x\n", + map, mix, nix, TLB[nix], BPIX+CPIXPL, RMR(nix<<1)); + + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Addr1c %06x RealAddr %06x Map1[%04x] HIT %04x, TLB[%3x] %08x MAPC[%03x] %08x RMR %04x\n", + addr, word, mix, map, nix, TLB[nix], nix/2, MAPC[nix/2], RMR(nix<<1)); + + *realaddr = word; /* return the real address */ + raddr = TLB[nix]; /* get the base address & bits */ + + if ((CPU_MODEL == MODEL_67) || (CPU_MODEL == MODEL_97)) { + /* get protection status of map */ + if ((MODES & PRIVBIT) == 0) { /* OK if privledged */ + /* otherwise check protection bit */ + offset = (word >> 11) & 0x3; /* see which 1/4 page we are in */ + if ((BIT1 >> offset) & raddr) /* is 1/4 page write protected */ + *prot = 1; /* return memory write protection status */ + } + } else { + /* get protection status of map */ + offset = ((map >> 12) & 0x6); /* get bits p1 & p2 from map into bits 13 & 14 */ + if (MODES & PRIVBIT) /* all access if privledged */ + *prot = offset | 0x8; /* set priv bit */ + else + *prot = offset; /* return memory write protection status */ + } + + /* now do the other halfword of the memory map pair */ + /* calc index of previous or next halfword */ + if ((mix & 1) == 0) { + mix += 1; /* we are at lf hw, so do next hw */ + nix += 1; /* point to next map in MAPC */ + /* This is really a firmware error where the CPU loads the */ + /* right halfword map information even though it exceeds */ + /* the allowed map count. It does not hurt anything, so OK */ + /* 32/67 & V6 allow loading the extra rt hw map entry */ + if ((nix == BPIX) || (nix > (BPIX+CPIXPL))) + return ALLOK; /* no other map is valid, we are done */ + } else + if ((mix & 1) == 1) { + if (nix == BPIX) + return ALLOK; /* no other map is valid, we are done */ + mix -= 1; /* we are at rt hw, so backup hw */ + nix -= 1; /* point to last map in MAPC */ + } + + sim_debug(DEBUG_DETAIL, &cpu_dev, + "RealAddrp mix %04x nix %04x TLB[%04x] %08x B+C %04x RMR[nix] %04x\n", + mix, nix, nix, TLB[nix], BPIX+CPIXPL, RMR(nix<<1)); + + /* allow the excess map entry to be loaded, even though bad */ + if (nix <= (BPIX+CPIXPL)) { /* needs to be a mapped reg */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Addr1d BPIX %03x CPIXPL %03x RealAddr %06x TLB[%3x] %08x MAPC[%03x] %08x RMR %04x\n", + BPIX, CPIXPL, word, nix, TLB[nix], nix/2, MAPC[nix/2], RMR(nix<<1)); + + /* mix & nix has other map correct index */ + if ((TLB[nix] & 0x04000000) == 0) { /* is HIT bit already on */ + /* hit not on, so load the map */ + /* allow the excess map entry to be loaded, even though bad */ + if (nix <= (BPIX+CPIXPL)) { /* needs to be a mapped reg */ + map = RMH(msdl+(mix<<1)); /* map content from memory */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Addr2a %06x MapX[%04x] HIT %04x, TLB[%3x] %08x MAPC[%03x] %08x\n", + addr, mix, map, nix, TLB[nix], nix/2, MAPC[nix/2]); + + if (map & 0x8000) { /* must be valid to load */ + /* setting access bit fails test 15/0 in vm.mmm diag */ + TLB[nix] = ((map & 0x7ff) << 13) | ((map << 16) & 0xf8000000) | 0x04000000; + word = (TLB[nix] & 0xffe000); /* combine map and offset */ + WMR((nix<<1), map); /* store the map reg contents into MAPC cache */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Addr2b %06x RealAddr %06x Map2[%04x] HIT %04x, TLB[%3x] %08x MAPC[%03x] %08x\n", + addr, word, mix, map, nix, TLB[nix], nix/2, MAPC[nix/2]); + } + } + } + } + /*****************END-OF-V6-V9-ADDRESS-HIT-PROCESSING****************/ + /*****************END-OF-67-97-ADDRESS-HIT-PROCESSING****************/ + return ALLOK; /* all OK, return instruction */ +} + +/* fetch the current instruction from the PC address */ +t_stat read_instruction(uint32 thepsd[2], uint32 *instr) +{ + uint32 status, addr; + + if (CPU_MODEL < MODEL_27) { + /* 32/7x machine with 8KW maps */ + /* instruction must be in first 512KB of address space */ + addr = thepsd[0] & 0x7fffc; /* get 19 bit logical word address */ + } else { + /* 32/27, 32/67, 32/87, 32/97 2KW maps */ + /* Concept 32 machine, 2KW maps */ + if (thepsd[0] & BASEBIT) { /* bit 6 is base mode? */ + addr = thepsd[0] & 0xfffffc; /* get 24 bit address */ + } + else + addr = thepsd[0] & 0x7fffc; /* get 19 bit address */ + } + + /* go read the memory location */ + status = Mem_read(addr, instr); + if ((status == MAPFLT) && (TRAPSTATUS == BIT1)) { + /* if map fault on read, change code to read instruction */ + TRAPSTATUS &= ~BIT1; /* clear error on read memory */ + TRAPSTATUS |= BIT0; /* set error on instruction read */ + } else + if (status == DMDPG) + pfault |= 0x80000000; /* set instruction fetch paging error */ + sim_debug(DEBUG_DETAIL, &cpu_dev, "read_instr status %02x @ %06x\n", status, addr); + return status; /* return ALLOK or ERROR status */ +} + +/* + * Read a full word from memory + * Return error type if failure, ALLOK if + * success. Addr is logical byte address. + */ +t_stat Mem_read(uint32 addr, uint32 *data) +{ + uint32 status, realaddr, prot, page, map, mix, nix, msdl, mpl, nmap; + + status = RealAddr(addr, &realaddr, &prot, MEM_RD); /* convert address to real physical address */ + + if (status == ALLOK) { + *data = RMW(realaddr); /* valid address, get physical address contents */ + if (((CPU_MODEL >= MODEL_V6) || (CPU_MODEL == MODEL_97) || + (CPU_MODEL == MODEL_67)) && (MODES & MAPMODE)) { + + page = (addr >> 13) & 0x7ff; /* get 11 bit value */ + if (CPU_MODEL >= MODEL_V6) { + /* check for v6 & v9 if we have read access */ + switch (prot & 0x0e) { + case 0x0: case 0x2: + /* O/S or user has no read/execute access, do protection violation */ + sim_debug(DEBUG_EXP, &cpu_dev, + "Mem_readA protect error @ %06x prot %02x modes %08x page %04x\n", + addr, prot, MODES, page); + if (CPU_MODEL == MODEL_V9) + TRAPSTATUS |= BIT2; /* set bit 2 of trap status */ + else + TRAPSTATUS &= ~BIT12; /* clear bit 12 of trap status */ + return MPVIOL; /* return memory protection violation */ + case 0x4: case 0x6: case 0x8: case 0xa: case 0xc: case 0xe: + /* O/S or user has read/execute access, no protection violation */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Mem_readB protect is ok @ %06x prot %02x modes %08x page %04x\n", + addr, prot, MODES, page); + } + mpl = SPAD[0xf3]; /* get mpl from spad address */ + nix = page & 0x7ff; /* map # or mapc index */ + if (page < BPIX) { + mix = nix; /* get map index in memory */ + msdl = RMW(mpl+4); /* get mpl entry for o/s */ + } else { + mix = nix-BPIX; /* get map index in memory */ + msdl = RMW(mpl+CPIX+4); /* get mpl entry for given cpix */ + } + nmap = RMH(msdl+(mix<<1)); /* map content from memory */ + map = RMR((page<<1)); /* read the map reg contents */ + /* if I remove this test, we fail at test 14/0 */ + if (((map & 0x800) == 0)) { + map |= 0x800; /* set the accessed bit in the map cache entry */ + WMR((page<<1), map); /* store the map reg contents into cache */ + TLB[page] |= 0x0c000000; /* set the accessed bit in TLB too */ + WMH(msdl+(mix<<1), map); /* save modified map with access bit set */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Mem_read Yaddr %06x page %04x set access bit TLB %08x map %04x nmap %04x\n", + addr, page, TLB[page], map, nmap); + } + } + /* everybody else has read access */ + } + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Mem_read addr %06x realaddr %06x data %08x prot %02x\n", + addr, realaddr, *data, prot); + } else { + /* RealAddr returned an error */ + sim_debug(DEBUG_EXP, &cpu_dev, + "Mem_read error addr %06x realaddr %06x data %08x prot %02x status %04x\n", + addr, realaddr, *data, prot, status); + if (status == NPMEM) { /* operand nonpresent memory error */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) { + TRAPSTATUS |= BIT1; /* set bit 1 of trap status */ + } else + TRAPSTATUS |= BIT10; /* set bit 10 of trap status */ + } + if (status == MAPFLT) { + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= (BIT12|BIT16); /* set bit 12 of trap status */ + else + TRAPSTATUS |= BIT10; /* set bit 10 of trap status */ + } + sim_debug(DEBUG_EXP, &cpu_dev, "Mem_read MISS %02x @ %06x TRAPSTATUS %08x\n", + status, addr, TRAPSTATUS); + } + return status; /* return ALLOK or ERROR status */ +} + +/* + * Write a full word to memory, checking protection + * and alignment restrictions. Return 1 if failure, 0 if + * success. Addr is logical byte address, data is 32bit word + */ +t_stat Mem_write(uint32 addr, uint32 *data) +{ + uint32 status, realaddr=0, prot=0, raddr, page, nmap, msdl, mpl, map, nix, mix; + + status = RealAddr(addr, &realaddr, &prot, MEM_WR); /* convert address to real physical address */ + + if (prot) { + sim_debug(DEBUG_DETAIL, &cpu_dev, "Mem_write addr %.8x realaddr %.8x data %.8x prot %02x\n", + addr, realaddr, *data, prot); + } + + if (status == ALLOK) { + if (((CPU_MODEL >= MODEL_V6) || (CPU_MODEL == MODEL_97) || + (CPU_MODEL == MODEL_67)) && (MODES & MAPMODE)) { + page = (addr >> 13) & 0x7ff; /* get 11 bit value */ + if (CPU_MODEL >= MODEL_V6) { + /* check for v6 & v9 if we have write access */ + switch (prot &0x0e) { + case 0x0: case 0x2: case 0x6: case 0xa: case 0xe: + /* O/S or user has read/execute access, do protection violation */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Mem_writeA protect error @ %06x prot %02x modes %08x\n", + addr, prot, MODES); + if (CPU_MODEL == MODEL_V9) + TRAPSTATUS |= BIT1; /* set bit 1 of trap status */ + else + TRAPSTATUS |= BIT12; /* set bit 12 of trap status */ + return MPVIOL; /* return memory protection violation */ + case 0x4: case 0x8: case 0xc: + /* O/S or user has write access, no protection violation */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Mem_writeB protect is ok @ %06x prot %02x modes %08x\n", + addr, prot, MODES); + } + map = RMR((page<<1)); /* read the map reg contents */ + raddr = TLB[page]; /* get the base address & bits */ + nix = page & 0x7ff; /* map # or mapc index */ + mpl = SPAD[0xf3]; /* get mpl from spad address */ + if (page < BPIX) { + mix = nix; /* get map index in memory */ + msdl = RMW(mpl+4); /* get mpl entry for o/s */ + } else { + mix = nix-BPIX; /* get map index in memory */ + msdl = RMW(mpl+CPIX+4); /* get mpl entry for given cpix */ + } + nmap = RMH(msdl+(mix<<1)); /* map content from memory */ + if ((nmap & 0x1000) == 0) { + nmap |= 0x1800; /* set the modify/accessed bit in the map cache entry */ + WMR((page<<1), nmap); /* store the map reg contents into cache */ + TLB[page] |= 0x18000000; /* set the modify/accessed bits in TLB too */ + WMH((msdl+(mix << 1)), nmap); /* save modified map with access bit set */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Mem_write Waddr %06x page %04x set access bit TLB %08x map %04x nmap %04x raddr %08x\n", + addr, page, TLB[page], map, nmap, raddr); + } + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Mem_write Xaddr %06x page %04x MA bits set TLB %08x map %04x prot %04x modes %04x\n", + addr, page, TLB[page], map, prot, MODES); + } else { + if (prot) { /* check for write protected memory */ + sim_debug(DEBUG_EXP, &cpu_dev, + "Mem_writeB 32/67 protect error @ %06x prot %02x page %04x\n", + addr, prot, page); + if (CPU_MODEL == MODEL_97) + TRAPSTATUS |= BIT1; /* set bit 1 of trap status */ + else + TRAPSTATUS |= BIT12; /* set bit 12 of trap status */ + return MPVIOL; /* return memory protection violation */ + } + } + /* everything else has write access */ + } else { + if (prot) { /* check for write protected memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "Mem_writeC protect error @ %06x prot %02x\n", addr, prot); + TRAPSTATUS |= BIT12; /* set bit 12 of trap status */ + return MPVIOL; /* return memory protection violation */ + } + } + WMW(realaddr, *data); /* valid address, put physical address contents */ + } else { + /* RealAddr returned an error */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "Mem_write error addr %.8x realaddr %.8x data %.8x prot %02x status %04x\n", + addr, realaddr, *data, prot, status); + if (status == NPMEM) { /* operand nonpresent memory error */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) { + TRAPSTATUS |= BIT2; /* set bit 2 of trap status */ + } else + TRAPSTATUS |= BIT10; /* set bit 10 of trap status */ + } + if (status == MAPFLT) { + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= (BIT12|BIT16); /* set bit 12 of trap status */ + else + TRAPSTATUS |= BIT10; /* set bit 10 of trap status */ + } + sim_debug(DEBUG_TRAP, &cpu_dev, + "Mem_write error %02x @ %06x TRAPSTATUS %08x pfaualt %04x\n", + status, addr, TRAPSTATUS, pfault); + } + return status; /* return ALLOK or ERROR */ +} + +/* function to set the CCs in PSD1 */ +/* ovr is setting for CC1 */ +void set_CCs(uint32 value, int ovr) +{ + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + if (ovr) + CC = CC1BIT; /* CC1 value */ + else + CC = 0; /* CC1 off */ + if (value & FSIGN) + CC |= CC3BIT; /* CC3 for neg */ + else if (value == 0) + CC |= CC4BIT; /* CC4 for zero */ + else + CC |= CC2BIT; /* CC2 for greater than zero */ + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ +} + +/* retain these values across calls to sim_instr */ +uint32 skipinstr = 0; /* Skip test for interrupt on this instruction */ +uint32 drop_nop = 0; /* Set if right hw instruction is a nop */ +uint32 OIR=0; /* Original Instruction register */ +uint32 OPSD1=0; /* Original PSD1 */ +uint32 OPSD2=0; /* Original PSD2 */ +uint32 TPSD[2]; /* Temp PSD */ + +/* Opcode definitions */ +/* called from simulator */ +t_stat sim_instr(void) { + t_stat reason = 0; /* reason for stopping */ + t_uint64 dest = 0; /* Holds destination/source register */ + t_uint64 source = 0; /* Holds source or memory data */ + t_uint64 td; /* Temporary */ + t_int64 int64a; /* temp int */ + t_int64 int64b; /* temp int */ + t_int64 int64c; /* temp int */ + uint32 addr; /* Holds address of last access */ + uint32 temp; /* General holding place for stuff */ + uint32 IR; /* Instruction register */ + uint32 i_flags=0; /* Instruction description flags from table */ + uint32 t; /* Temporary */ + uint32 temp2; /* Temporary */ + uint32 bc=0; /* Temporary bit count */ + uint16 opr; /* Top half of Instruction register */ + uint16 OP; /* Six bit instruction opcode */ + uint16 chan; /* I/O channel address */ + uint16 lchan; /* Logical I/O channel address */ + uint16 suba; /* I/O subaddress */ + uint16 lchsa; /* logical I/O channel & subaddress */ + uint16 rchsa; /* real I/O channel & subaddress */ + uint8 FC; /* Current F&C bits */ + uint8 EXM_EXR=0; /* PC Increment for EXM/EXR instructions */ + uint8 BM, MM, BK; /* basemode, mapped mode, blocked mode */ + uint32 reg; /* GPR or Base register bits 6-8 */ + uint32 sreg; /* Source reg in from bits 9-11 reg-reg instructions */ + uint32 ix = 0; /* index register */ + uint32 dbl; /* Double word */ + uint32 ovr=0; /* Overflow flag */ +//FORSTEP uint32 stopnext = 0; /* Stop on next instruction */ + uint32 int_icb; /* interrupt context block address */ + uint32 rstatus; /* temp return status */ + int32 int32a; /* temp int */ + int32 int32b; /* temp int */ + int32 int32c; /* temp int */ +// uint32 uint32a; /* temp uint */ +// uint32 uint32b; /* temp uint */ +// uint32 uint32c; /* temp uint */ + +//#define MPXTEST +//#define LOOK_MAP_05272021 +#ifdef MPXTEST + int32 ii; /* temp int */ +#endif + +wait_loop: + while (reason == 0) { /* loop until halted */ + + // wait_loop: + if (sim_interval <= 0) { /* event queue? */ + reason = sim_process_event(); /* process */ + if (reason != SCPE_OK) { + if (reason == SCPE_STEP) { + sim_debug(DEBUG_EXP, &cpu_dev, + "Process Event step reason %08x interval %08x\n", + reason, sim_interval); + return reason; + break; + } + else { + sim_debug(DEBUG_EXP, &cpu_dev, + "Process Event other reason %08x interval %08x\n", + reason, sim_interval); + return reason; + break; /* process */ + } + } + } + + if (sim_brk_summ) + sim_debug(DEBUG_EXP, &cpu_dev, "Process Event sim_brk_summ = %08x\n", + sim_brk_summ); + PC = PSD1 & 0xfffffe; /* get 24 bit addr from PSD1 */ + /* stop simulator if user break requested */ + if (sim_brk_summ && sim_brk_test(PC, SWMASK('E'))) { + reason = STOP_IBKPT; +// reason = SCPE_STEP; + sim_debug(DEBUG_EXP, &cpu_dev, "Process Event test reason %08x interval %08x\n", + reason, sim_interval); + sim_interval= 0; /* count down */ + break; + } + + sim_interval--; /* count down */ + + if (drop_nop) { /* need to drop a nop? */ + drop_nop = 0; /* we dropped the nop */ + sim_debug(DEBUG_EXP, &cpu_dev, + "CPU Drop NOP PSD1 %08x\n", PSD1); + } + + if (skipinstr) { /* need to skip interrupt test? */ + skipinstr = 0; /* skip only once */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "CPU Skip instruction PSD %08x %08x irq_pend %d wait4int %d irq_auto %x\n", + PSD1, PSD2, irq_pend, wait4int, irq_auto); + goto skipi; /* skip int test */ + } + + if (waitqcnt > 0) { /* test for UTX delay */ + waitqcnt--; /* wait b4 ints */ + if (waitqcnt == 0) + irq_pend = 1; /* start scanning interrupts again */ + } + + /* we are booting the system, so see if boot channel prog is completed */ + if (loading) { + uint32 il; + uint32 chsa = scan_chan(&il); /* go scan for load complete pending */ + if (chsa != 0) { /* see if a boot channel/subaddress were returned */ + /* take interrupt, store the PSD, fetch new PSD */ + PSD1 = TPSD[0]; /* PSD1 from location 0 */ + PSD2 = TPSD[1]; /* PSD2 from location 4 */ + CC = PSD1 & 0x78000000; /* extract bits 1-4 from PSD1 */ + MODES = PSD1 & 0x87000000; /* extract bits 0, 5, 6, 7 from PSD 1 */ + CPUSTATUS &= ~0x87000000; /* reset bits in CPUSTATUS */ + CPUSTATUS |= MODES; /* now insert into CPUSTATUS */ + sim_debug(DEBUG_IRQ, &cpu_dev, "Boot Loading PSD1 %.8x PSD2 %.8x\n", PSD1, PSD2); + + /* set interrupt blocking state in CPUSTATUS */ + CPUSTATUS |= BIT24; /* set blocked state in cpu status, bit 24 too */ + MODES |= BLKMODE; /* set blocked in mode to0 */ + PSD2 &= ~RETMBIT; /* turn off retain map bit in PSD2 */ + PSD2 &= ~RETBBIT; /* turn off retain block mode bit in PSD2 */ + + SPAD[0xf5] = PSD2; /* save the current PSD2 */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ + loading = 0; /* we are done loading */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "Load Skipinstr %1x set loading PSD1 %08x PSD2 %08x CPUSTATUS %08x\n", + skipinstr, PSD1, PSD2, CPUSTATUS); + goto skipi; /* skip int test */ + } + goto wait_loop; /* continue waiting */ + } + + /* we get here when not booting */ + /* process any pending interrupts */ + if ((irq_pend || wait4int) && (irq_auto == 0)) { + /* see if ints are pending */ + uint32 ilev; + uint32 oldstatus = CPUSTATUS; /* keep for retain blocking state */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ + + int_icb = scan_chan(&ilev); /* no, go scan for I/O int pending */ + if (int_icb != 0) { /* was ICB returned for an I/O or interrupt */ + uint32 il = ilev; /* get the interrupt level */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "<|>Normal int return icb %06x level %02x irq_pend %1x wait4int %1x\n", + int_icb, il, irq_pend, wait4int); + + /* take interrupt, store the PSD, fetch new PSD */ + bc = PSD2 & 0x3ff8; /* get copy of cpix */ + M[int_icb>>2] = PSD1&0xfffffffe; /* store PSD 1 */ + M[(int_icb>>2)+1] = PSD2; /* store PSD 2 */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "<|>Normal int cpix %04x OPSD1 %08x OPSD2 %08x\n", + bc, PSD1, PSD2); +#ifdef DUMP_REGS + for (ix=0; ix<8; ix+=2) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "<|> GPR[%d] %.8x GPR[%d] %.8x\n", ix, GPR[ix], ix+1, GPR[ix+1]); + } +#endif + PSD1 = M[(int_icb>>2)+2]; /* get new PSD 1 */ + PSD2 = (M[(int_icb>>2)+3] & ~0x3fff) | bc; /* get new PSD 2 w/old cpix */ + + /* I/O status DW address will be in WD 6 */ + /* set new map mode and interrupt blocking state in CPUSTATUS */ + CC = PSD1 & 0x78000000; /* extract bits 1-4 from PSD1 */ + MODES = PSD1 & 0x87000000; /* extract bits 0, 5, 6, 7 from PSD 1 */ + CPUSTATUS &= ~0x87000080; /* reset bits in CPUSTATUS */ + CPUSTATUS |= MODES; /* now insert into CPUSTATUS */ + if (PSD2 & MAPBIT) { + CPUSTATUS |= BIT8; /* set bit 8 of cpu status to mapped */ + MODES |= MAPMODE; /* set mapped mode */ + } else { + CPUSTATUS &= ~BIT8; /* reset bit 8 of cpu status */ + MODES &= ~MAPMODE; /* reset set mapped mode */ + } + + if ((PSD2 & RETBBIT) == 0) { /* is it retain blocking state, bit 48 set */ + /* retain blocking state is off, use bit 49 to set new blocking state */ + if (PSD2 & SETBBIT) { /* no, is it set blocking state bit 49 set*/ + /* new blocking state is blocked when bits 48=0 & bit 49=1 */ + CPUSTATUS |= BIT24; /* yes, set blk state in cpu status bit 24 */ + MODES |= BLKMODE; /* set blocked mode */ + + /* This test fixed the hangs on terminal input for diags & UTX! */ + t = SPAD[il+0x80]; /* get spad entry for interrupt */ + /* Class F I/O spec says to reset active interrupt if user's */ + /* interrupt service routine runs with interrupts blocked */ + if (((t & 0x0f800000) == 0x0f000000) || /* if class F clear interrupt */ + ((t & 0x0000ffff) == 0x00007f06) || /* RT Clock */ + ((t & 0x0f00ffff) == 0x03007f04)) { /* Interval timer */ + /* if this is F class I/O interrupt, clear the active level */ + /* SPAD entries for interrupts begin at 0x80 */ + if ((irq_auto) != 0) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "<|>Auto-reset irq_auto NOT zero %x INTS[%02x] %08x SPAD[%02x] %08x\n", + irq_auto, il, INTS[il], il+0x80, SPAD[il+0x80]); + } + irq_auto = il; /* show processing in blocked mode */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "<|>Auto-reset interrupt INTS[%02x] %08x SPAD[%02x] %08x simi %02x\n", + il, INTS[il], il+0x80, SPAD[il+0x80], sim_interval); +#define LEAVE_ACTIVE +#ifndef LEAVE_ACTIVE +/*AIR*/ INTS[irq_auto] &= ~INTS_ACT; /* deactivate specified int level */ +/*AIR*/ SPAD[irq_auto+0x80] &= ~SINT_ACT; /* deactivate in SPAD too */ + irq_auto = 0; +#endif + } + } else { + sim_debug(DEBUG_IRQ, &cpu_dev, + "<|>RUN ACTIVE interrupt INTS[%02x] %08x SPAD[%02x] %08x\n", + il, INTS[il], il+0x80, SPAD[il+0x80]); + CPUSTATUS &= ~BIT24; /* no, reset blk state in cpu status bit 24 */ + MODES &= ~BLKMODE; /* reset blocked mode */ + } + } else { + /* handle retain blocking state */ + PSD2 &= ~RETMBIT; /* turn off retain bit in PSD2 */ + /* set new blocking state in PSD2 */ + PSD2 &= ~(SETBBIT|RETBBIT); /* clear bit 48 & 49 to be unblocked */ + MODES &= ~(BLKMODE|RETBLKM); /* reset blocked & retain mode bits */ + if (oldstatus & BIT24) { /* see if old mode is blocked */ + PSD2 |= SETBBIT; /* set to blocked state */ + MODES |= BLKMODE; /* set blocked mode */ + } + } + + SPAD[0xf5] = PSD2; /* save the current PSD2 */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "<|>Int %02x OPSD1 %08x OPSD2 %08x NPSD1 %08x NPSD2 %08x\n", + il, RMW(int_icb), RMW(int_icb+4), PSD1, PSD2); + bc = RMW(int_icb+20) & 0xffffff; + if (RMW(int_icb+16) == 0) + sim_debug(DEBUG_IRQ, &cpu_dev, + "<|>Int2 %02x ICBA %06x ICBA %06x IOCLA %06x\n", il, int_icb, + RMW(int_icb+16), RMW(int_icb+20)); + else + sim_debug(DEBUG_IRQ, &cpu_dev, + "<|>Int2 %02x ICBA %06x IOCLA %06x STAT %08x SW1 %08x SW2 %08x\n", + il, int_icb, RMW(int_icb+16), RMW(int_icb+20), RMW(bc), RMW(bc+4)); +#ifdef DYNAMIC_DEBUG_01172021 + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ +#endif + wait4int = 0; /* wait is over for int */ + drop_nop = 0; /* no nop skipping */ + goto skipi; /* skip int test */ + } + } + + /* see if in wait instruction */ + if (wait4int) { /* keep waiting */ + /* tell simh we will be waiting */ + sim_idle(TMR_RTC, 1); /* wait for clock tick */ + irq_pend = 1; /* start scanning interrupts again */ + goto wait_loop; /* continue waiting */ + } + + /* Check for external interrupt here */ + /* see if we have an attention request from console */ + if (!skipinstr && attention_trap) { + TRAPME = attention_trap; /* get trap number */ + attention_trap = 0; /* clear flag */ + sim_debug(DEBUG_XIO, &cpu_dev, "Attention TRAP %04x\n", TRAPME); + goto newpsd; /* got process trap */ + } + +skipi: + i_flags = 0; /* do not update pc if MF or NPM */ + TRAPSTATUS = CPUSTATUS & 0x57; /* clear all trap status except cpu type */ + + /* check for breakpoint request */ + if (sim_brk_summ && sim_brk_test(PC, SWMASK('E'))) { + reason = STOP_IBKPT; + break; + } + + /* fill IR from logical memory address */ + if ((TRAPME = read_instruction(PSD, &IR))) { + sim_debug(DEBUG_TRAP, &cpu_dev, + "read_instr TRAPME %04x PSD %08x %08x i_flags %04x drop_nop %1x\n", + TRAPME, PSD1, PSD2, i_flags, drop_nop); + if ((CPU_MODEL <= MODEL_27) || (CPU_MODEL == MODEL_67) || + (CPU_MODEL == MODEL_87) || (CPU_MODEL == MODEL_97)) { + if ((TRAPME == MAPFLT) || (TRAPME == NPMEM)) { + i_flags |= HLF; /* assume half word instr */ + PSD1 &= ~BIT31; /* force off last right */ + // fix for 32/67 test 32/3 in MMM diag + if ((CPU_MODEL == MODEL_27) || (CPU_MODEL == MODEL_67)) + i_flags |= BT; /* do not update pc if MF or NPM */ + else + i_flags &= ~BT; /* do not update pc if MF or NPM */ + } + } else { + if ((TRAPME == PRIVVIOL_TRAP) && (CPU_MODEL == MODEL_V9)) { + i_flags |= HLF; /* assume half word instr */ + drop_nop = 0; + i_flags &= ~BT; /* no branch taken */ + PSD1 &= ~BIT31; /* force off last right */ + } + } + sim_debug(DEBUG_TRAP, &cpu_dev, + "read_instr2 TRAPME %04x PSD %08x %08x i_flags %04x drop_nop %1x\n", + TRAPME, PSD1, PSD2, i_flags, drop_nop); + goto newpsd; /* got process trap */ + } + + if (PSD1 & 2) { /* see if executing right half */ + /* we have a rt hw instruction */ + IR <<= 16; /* put instruction in left hw */ + if ((CPU_MODEL <= MODEL_27) || (CPU_MODEL == MODEL_87) || + (CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) { + drop_nop = 0; /* not dropping nop for these machines */ + goto exec; /* machine does not drop nop instructions */ + } + /* We have 67 or V6 and have a rt hw instruction */ + if (IR == 0x00020000) { /* is this a NOP from rt hw? */ + PSD1 = (PSD1 + 2) | (((PSD1 & 2) >> 1) & 1); /* skip this instruction */ + if (skipinstr) + sim_debug(DEBUG_IRQ, &cpu_dev, + "2Rt HW instruction skipinstr %1x is set PSD1 %08x PSD2 %08x CPUSTATUS %08x\n", + skipinstr, PSD1, PSD2, CPUSTATUS); + goto skipi; /* go read next instruction */ + } + if (skipinstr) + sim_debug(DEBUG_IRQ, &cpu_dev, + "3Rt HW instruction skipinstr %1x is set PSD1 %08x PSD2 %08x CPUSTATUS %08x\n", + skipinstr, PSD1, PSD2, CPUSTATUS); + } else { + /* we have a left hw or fullword instruction */ + /* see if we can drop a rt hw nop instruction */ + OP = (IR >> 24) & 0xFC; /* this is a 32/67 or above, get OP */ + if ((CPU_MODEL <= MODEL_27) || (CPU_MODEL == MODEL_87) || + (CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + goto exec; /* old machines did not drop nop instructions */ + if (PSD1 & BASEBIT) + i_flags = base_mode[OP>>2]; /* set the BM instruction processing flags */ + else + i_flags = nobase_mode[OP>>2]; /* set the NBM instruction processing flags */ + if ((i_flags & 0xf) == HLF) { /* this is left HW instruction */ + if ((IR & 0xffff) == 0x0002) { /* see if rt hw is a nop */ + /* treat this as a fw instruction */ + drop_nop = 1; /* we need to skip nop next time */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "CPU setting Drop NOP PSD1 %08x IR %08x\n", PSD1, IR); + } + } + } + +exec: + /* temp saves for debugging */ + OIR = IR; /* save the instruction */ + OPSD1 = PSD1; /* save the old PSD1 */ + OPSD2 = PSD2; /* save the old PSD2 */ + TRAPSTATUS = CPUSTATUS & 0x57; /* clear all trap status except cpu type */ + + /* Split instruction into pieces */ + PC = PSD1 & 0xfffffe; /* get 24 bit addr from PSD1 */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "-----Instr @ PC %08x PSD1 %08x PSD2 %08x IR %08x drop_nop %x\n", + PC, PSD1, PSD2, IR, drop_nop); + + /* Update history for this instruction */ + if (hst_lnt) { + hst_p += 1; /* next history location */ + if (hst_p >= hst_lnt) /* check for wrap */ + hst_p = 0; /* start over at beginning */ + hst[hst_p].opsd1 = OPSD1; /* set original psd1 */ + hst[hst_p].opsd2 = OPSD2; /* set original psd2 */ + hst[hst_p].oir = OIR; /* set original instruction */ + } + + opr = (IR >> 16) & MASK16; /* use upper half of instruction */ + OP = (opr >> 8) & 0xFC; /* Get opcode (bits 0-5) left justified */ + FC = ((IR & F_BIT) ? 0x4 : 0) | (IR & 3); /* get F & C bits for addressing */ + reg = (opr >> 7) & 0x7; /* dest reg or xr on base mode */ + sreg = (opr >> 4) & 0x7; /* src reg for reg-reg instructions or BR instr */ + dbl = 0; /* no doubleword instruction */ + ovr = 0; /* no overflow or arithmetic exception either */ + dest = (t_uint64)IR; /* assume memory address specified */ + CC = PSD1 & 0x78000000; /* save CC's if any */ + MODES = PSD1 & 0x87000000; /* insert bits 0, 5, 6, 7 from PSD 1 */ + CPUSTATUS &= ~0x87000000; /* reset those bits in CPUSTATUS */ + CPUSTATUS |= MODES; /* now insert them into CPUSTATUS */ + if (PSD2 & MAPBIT) { + CPUSTATUS |= BIT8; /* set bit 8 of cpu status to mapped */ + MODES |= MAPMODE; /* set mapped mode */ + } else { + CPUSTATUS &= ~BIT8; /* reset bit 8 of cpu status */ + MODES &= ~MAPMODE; /* reset mapped mode */ + } + + if (MODES & BASEBIT) { + i_flags = base_mode[OP>>2]; /* set the instruction processing flags */ + addr = IR & RMASK; /* get address offset from instruction */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Base OP %04x i_flags %04x addr %08x\n", OP, i_flags, addr); + switch(i_flags & 0xf) { + case HLF: + source = GPR[sreg]; /* get the src reg from instruction */ + break; + case IMM: + if (PC & 02) { /* if pc is on HW boundry, bad address */ + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "ADDRSPEC1 OP %04x addr %08x\n", OP, addr); + goto newpsd; /* go execute the trap now */ + } + break; + case ADR: + ix = (IR >> 20) & 7; /* get index reg from instruction */ + if (ix != 0) + addr += (GPR[ix] & MASK24); /* if not zero, add in reg contents */ + case WRD: + if (PC & 02) { /* if pc is on HW boundry, bad address */ + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "ADDRSPEC2 OP %04x addr %08x\n", OP, addr); + goto newpsd; /* go execute the trap now */ + } + ix = (IR >> 16) & 7; /* get base reg from instruction */ + if (ix != 0) + addr += (BR[ix] & MASK24); /* if not zero, add to base reg contents */ + FC = ((IR & F_BIT) ? 4 : 0); /* get F bit from original instruction */ + FC |= addr & 3; /* set new C bits to address from orig or regs */ + addr &= MASK24; /* make pure 24 bit addr */ + break; + case INV: + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + break; + } + } else { + i_flags = nobase_mode[OP>>2]; /* set the instruction processing flags */ + addr = IR & 0x7ffff; /* get 19 bit address from instruction */ + if (PC >= 0x80000) { + TRAPME = MAPFAULT_TRAP; /* Map Fault Trap */ + // DIAG add 97 for correct PSD address CN.MMM test 32, subtest 1 fails + if ((CPU_MODEL <= MODEL_27) || (CPU_MODEL == MODEL_67) || + (CPU_MODEL == MODEL_87) || (CPU_MODEL == MODEL_97)) { + // DIAG fix for 32/87 test 33/2, clear psd bit 31 + if ((CPU_MODEL == MODEL_87)) + PSD1 &= ~BIT31; /* force off last right */ + // DIAG fix 32/27 32/67 for diag MMM test 33/2 + if ((CPU_MODEL <= MODEL_27) || (CPU_MODEL == MODEL_67)) + i_flags |= BT; /* do not update pc if MAPFAULT on 27 */ + else + i_flags &= ~BT; /* do not update pc if MF or NPM */ + i_flags |= HLF; /* assume half word instr */ + } + if ((CPU_MODEL <= MODEL_27)) { + /* 77, 27 rolls to zero, not 80000 */ + PSD1 &= 0xff07ffff; /* remove overflow bits */ + } else { + PSD1 &= 0xff0fffff; /* leave overflow bit for trap addr */ + } + sim_debug(DEBUG_TRAP, &cpu_dev, + "PC over 80000 PC %08x Base OP %02x i_flags %04x addr %06x PSD %08x %08x\n", + PC, OP, i_flags, addr, PSD1, PSD2); + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + else + TRAPSTATUS |= BIT19; /* set bit 19 of trap status */ + goto newpsd; /* handle trap */ + } + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Non Based i_flags %04x addr %08x\n", i_flags, addr); + /* non base mode instructions have bit 0 of the instruction set */ + /* for word length instructions and zero for halfword instructions */ + /* the LA (op=0x34) is the only exception. So test for PC on a halfword */ + /* address and trap if word opcode is in right hw */ + /* if pc is on HW boundry, addr trap if bit zero set */ + if (PC & 02) { + if ((OP == 0x34) || (OP & 0x80)) { + i_flags |= HLF; /* diags treats these as hw instructions */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + goto newpsd; /* go execute the trap now */ + } + } + switch(i_flags & 0xf) { + case HLF: /* halfword instruction */ + source = GPR[sreg]; /* get the src reg contents */ + break; + + case IMM: /* Immediate mode */ + if (PC & 02) { /* if pc is on HW boundry, bad address */ + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "ADDRSPEC3 OP %04x addr %08x\n", OP, addr); + goto newpsd; /* go execute the trap now */ + } + break; + + case ADR: /* Normal addressing mode */ + ix = (IR >> 21) & 3; /* get the index reg if specified */ + if (ix != 0) { + addr += GPR[ix]; /* if not zero, add in reg contents */ + FC = ((IR & F_BIT) ? 4 : 0); /* get F bit from original instruction */ + FC |= addr & 3; /* set new C bits to address from orig or regs */ + } + + /* wart alert! */ + /* the lea instruction requires special handling for indirection. */ + /* Bits 0,1 are set to 1 in result addr if indirect bit is zero in */ + /* instruction. Bits 0 & 1 are set to the last word */ + /* or instruction in the chain bits 0 & 1 if indirect bit set */ + /* if IX == 00 => dest = IR */ + /* if IX == 0x => dest = IR + reg */ + /* if IX == Ix => dest = ind + reg */ + + /* fall through */ + case WRD: /* Word addressing, no index */ + bc = 0xC0000000; /* set bits 0, 1 for instruction if not indirect */ + t = IR; /* get current IR */ + addr &= MASK24; /* make pure 24 bit addr */ + while ((t & IND) != 0) { /* process indirection */ + if ((TRAPME = Mem_read(addr, &temp))) { /* get the word from memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "case WRD Mem_read status %02x @ %08x OP %04x\n", TRAPME, addr, OP); + if (CPU_MODEL == MODEL_V9) /* V9 wants bit0 set in pfault */ + if (TRAPME == DMDPG) /* demand page request */ + pfault |= 0x80000000; /* set instruction fetch paging error */ + goto newpsd; /* memory read error or map fault */ + } + bc = temp & 0xC0000000; /* save new bits 0, 1 from indirect location */ + CC = (temp & 0x78000000); /* save CC's from the last indirect word */ + /* process new X, I, ADDR fields */ + addr = temp & MASK19; /* get just the addr */ + ix = (temp >> 21) & 3; /* get the index reg from indirect word */ + if (ix != 0) + addr += (GPR[ix] & MASK19); /* add the register to the address */ + /* if no F or C bits set, use original, else new */ + if ((temp & F_BIT) || (addr & 3)) + FC = ((temp & F_BIT) ? 0x4 : 0) | (addr & 3); + else { + addr |= (IR & F_BIT); /* copy F bit from instruction */ + addr |= (FC & 3); /* copy in last C bits */ + } + t = temp; /* go process next indirect location */ + temp &= MASK19; /* go process next indirect location */ + addr &= ~F_BIT; /* turn off F bit */ + } + dest = (t_uint64)addr; /* make into 64 bit variable */ + break; + + case INV: /* Invalid instruction */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + break; + } + } + + /* Read memory operand */ + if (i_flags & RM) { + if ((TRAPME = Mem_read(addr, &temp))) { /* get the word from memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "case RM Mem_read status %02x @ %08x\n", TRAPME, addr); + // DIAG add 97 for correct PSD address CN.MMM test 32, subtest 1 fails + if ((TRAPME == MAPFLT) || (TRAPME == NPMEM) || (TRAPME == MPVIOL)) + PSD1 &= ~BIT31; /* force off last right */ + goto newpsd; /* memory read error or map fault */ + } + source = (t_uint64)temp; /* make into 64 bit value */ + switch(FC) { + case 0: /* word address, extend sign */ + source |= (source & MSIGN) ? D32LMASK : 0; + break; + case 1: /* left hw */ + source >>= 16; /* move left hw to right hw*/ + /* Fall through */ + case 3: /* right hw or right shifted left hw */ + source &= RMASK; /* use just the right hw */ + if (source & 0x8000) { /* check sign of 16 bit value */ + /* sign extend the value to leftmost 48 bits */ + source = LMASK | (source & RMASK); /* extend low 32 bits */ + source |= (D32LMASK); /* extend hi bits */ + } + break; + case 2: /* double word address */ + if ((addr & 7) != 2) { /* must be double word adddress */ + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "ADDRSPEC4 case RM wd 1/3 Mem_read DW status %02x @ %08x src %08x\n", + TRAPME, addr, (uint32)source); + goto newpsd; /* go execute the trap now */ + } + if ((TRAPME = Mem_read(addr+4, &temp))) { /* get the 2nd word from memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "case RM wd 2 Mem_read status %02x @ %08x\n", TRAPME, addr+4); + goto newpsd; /* memory read error or map fault */ + } + source = (source << 32) | (t_uint64)temp; /* merge in the low order 32 bits */ + dbl = 1; /* double word instruction */ + break; + case 4: /* byte mode, byte 0 */ + case 5: /* byte mode, byte 1 */ + case 6: /* byte mode, byte 2 */ + case 7: /* byte mode, byte 3 */ + source = (source >> (8*(7-FC))) & 0xff; /* right justify addressed byte */ + break; + } + } + + /* Read memory operand without doing sign extend for EOMX/ANMX/ORMX/ARMX */ + if (i_flags & RNX) { + if ((TRAPME = Mem_read(addr, &temp))) { /* get the word from memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "case RNX 2 Mem_read status %02x @ %08x\n", TRAPME, addr); + goto newpsd; /* memory read error or map fault */ + } + source = (t_uint64)temp; /* make into 64 bit value */ + switch(FC) { + case 0: /* word address and no sign extend */ + source &= D32RMASK; /* just l/o 32 bits */ + break; + case 1: /* left hw */ + source >>= 16; /* move left hw to right hw*/ + /* Fall through */ + case 3: /* right hw or right shifted left hw */ + source &= RMASK; /* use just the right hw */ + break; + case 2: /* double word address */ + if ((addr & 7) != 2) { /* must be double word adddress */ + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "ADDRSPEC5 OP %04x addr %08x\n", OP, addr); + goto newpsd; /* go execute the trap now */ + } + if ((TRAPME = Mem_read(addr+4, &temp))) { /* get the 2nd word from memory */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "case RNX wd 2 Mem_read status %02x @ %08x\n", TRAPME, addr+4); + goto newpsd; /* memory read error or map fault */ + } + source = (source << 32) | (t_uint64)temp; /* merge in the low order 32 bits */ + dbl = 1; /* double word instruction */ + break; + case 4: /* byte mode, byte 0 */ + case 5: /* byte mode, byte 1 */ + case 6: /* byte mode, byte 2 */ + case 7: /* byte mode, byte 3 */ + source = (source >> (8*(7-FC))) & 0xff; /* right justify addressed byte */ + break; + } + } + + /* Read in if from register */ + if (i_flags & RR) { + if (FC == 2 && (i_flags & HLF) == 0) /* double dest? */ + dbl = 1; /* src must be dbl for dbl dest */ + dest = (t_uint64)GPR[reg]; /* get the register content */ + if (dbl) { /* is it double regs */ + if (reg & 1) { /* check for odd reg load */ + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "ADDRSPEC6 OP %04x addr %08x\n", OP, addr); + goto newpsd; /* go execute the trap now */ + } + /* merge the regs into the 64bit value */ + dest = (((t_uint64)dest) << 32) | ((t_uint64)GPR[reg+1]); + } else { + /* sign extend the data value */ + dest |= (dest & MSIGN) ? D32LMASK : 0; + } + } + + /* For Base mode */ + if (i_flags & RB) { + dest = (t_uint64)BR[reg]; /* get base reg contents */ + } + + /* For register instructions */ + if (i_flags & R1) { + source = (t_uint64)GPR[sreg]; + if (dbl) { + if (sreg & 1) { + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "ADDRSPEC7 OP %04x addr %08x\n", OP, addr); + goto newpsd; /* go execute the trap now */ + } + /* merge the regs into the 64bit value */ + source = (source << 32) | ((t_uint64)GPR[reg+1]); + } else { + /* sign extend the data value */ + source |= (source & MSIGN) ? ((t_uint64)MASK32) << 32: 0; + } + } + + /* process instruction op code */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "PSD %08x %08x SW OP %04x IR %08x addr %08x\n", + PSD1, PSD2, OP, IR, addr); + + /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + /* start processing the opcodes */ + /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + switch (OP>>2) { + /* + * For op-codes=00,04,08,0c,10,14,28,2c,38,3c,40,44,60,64,68 + */ + /* Reg - Reg instruction Format (16 bit) */ + /* |--------------------------------------| */ + /* |0 1 2 3 4 5|6 7 8 |9 10 11|12 13 14 15| */ + /* | Op Code | DReg | SReg | Aug Code | */ + /* |--------------------------------------| */ + case 0x00>>2: /* HLF - HLF */ /* CPU General operations */ + switch(opr & 0xF) { /* switch on aug code */ + case 0x0: /* HALT */ +#ifndef TEMP4DEBUG + if ((MODES & PRIVBIT) == 0) { /* must be privileged to halt */ + TRAPME = PRIVVIOL_TRAP; /* set the trap to take */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + else + TRAPSTATUS |= BIT19; /* set bit 19 of trap status */ + goto newpsd; /* Privlege violation trap */ + } + if (CPUSTATUS & BIT23) { /* Priv mode halt must be enabled */ + TRAPME = PRIVHALT_TRAP; /* set the trap to take */ + goto newpsd; /* Privlege mode halt trap */ + } +#endif + sim_debug(DEBUG_EXP, &cpu_dev, + "\n[][][][][][][][][][] HALT [][][][][][][][][][]\n"); + sim_debug(DEBUG_EXP, &cpu_dev, + "PSD1 %.8x PSD2 %.8x TRAPME %.4x CPUSTATUS %08x\n", + PSD1, PSD2, TRAPME, CPUSTATUS); + for (ix=0; ix<8; ix+=2) { + sim_debug(DEBUG_EXP, &cpu_dev, + "GPR[%d] %.8x GPR[%d] %.8x\n", ix, GPR[ix], ix+1, GPR[ix+1]); + } + sim_debug(DEBUG_EXP, &cpu_dev, + "[][][][][][][][][][] HALT [][][][][][][][][][]\n"); + + fprintf(stdout, "\r\n[][][][][][][][][][] HALT [][][][][][][][][][]\r\n"); + fprintf(stdout, "PSD1 %.8x PSD2 %.8x TRAPME %.4x CPUSTATUS %08x\r\n", + PSD1, PSD2, TRAPME, CPUSTATUS); + for (ix=0; ix<8; ix+=2) { + fprintf(stdout, "GPR[%d] %.8x GPR[%d] %.8x\r\n", + ix, GPR[ix], ix+1, GPR[ix+1]); + } + if (MODES & BASEBIT) { /* see if based */ + for (ix=0; ix<8; ix+=2) { + fprintf(stdout, "BR[%d] %.8x BR[%d] %.8x\r\n", + ix, BR[ix], ix+1, BR[ix+1]); + } + } + fprintf(stdout, "[][][][][][][][][][] HALT [][][][][][][][][][]\r\n"); +/*TEST DIAG*/reason = STOP_HALT; /* do halt for now */ + break; + + case 0x1: /* WAIT */ + if ((MODES & PRIVBIT) == 0) { /* must be privileged to wait */ + TRAPME = PRIVVIOL_TRAP; /* set the trap to take */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + else + TRAPSTATUS |= BIT19; /* set bit 19 of trap status */ + goto newpsd; /* Privlege violation trap */ + } + /* if interrupts are blocked, system check trap */ + if (CPUSTATUS & BIT24) { /* status word bit 24 says blocked */ + TRAPME = SYSTEMCHK_TRAP; /* trap condition if F class */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT12; /* set bit 0 of trap status */ + else + TRAPSTATUS |= BIT20; /* set bit 20 of trap status */ + goto newpsd; /* system check trap */ + } + if (wait4int == 0) { + time_t result = time(NULL); + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Starting WAIT mode %08x\n", (uint32)result); + } + wait4int = 1; /* show we are waiting for interrupt */ + /* tell simh we will be waiting */ + sim_idle(TMR_RTC, 0); /* wait for next pending device event */ + irq_pend = 1; /* start scanning interrupts again */ + i_flags |= BT; /* keep PC from being incremented while waiting */ + break; + case 0x2: /* NOP */ + break; + case 0x3: /* LCS */ + /* get console switches from memory loc 0x780 */ + if ((TRAPME = Mem_read(0x780, &GPR[reg]))) /* get the word from memory */ + goto newpsd; /* memory read error or map fault */ + set_CCs(GPR[reg], 0); /* set the CC's, CC1 = 0 */ + break; + case 0x4: /* ES */ + if (reg & 1) { /* see if odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + /* reg is reg to extend sign into from reg+1 */ + GPR[reg] = (GPR[reg+1] & FSIGN) ? FMASK : 0; + set_CCs(GPR[reg], 0); /* set CCs, CC2 & CC3 */ + break; + case 0x5: /* RND */ + if (reg & 1) { /* see if odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + temp = GPR[reg]; /* save the current contents of specified reg */ + t = (temp & FSIGN) != 0; /* set flag for sign bit not set in temp value */ + bc = 1; + t |= ((bc & FSIGN) != 0) ? 2 : 0; /* ditto for the bit value */ + if (GPR[reg+1] & FSIGN) { /* if sign of R+1 is set, incr R by 1 */ + temp += bc; /* add the bit value to the reg */ + /* if both signs are neg and result sign is positive, overflow */ + /* if both signs are pos and result sign is negative, overflow */ + if ((t == 3 && (temp & FSIGN) == 0) || + (t == 0 && (temp & FSIGN) != 0)) { + ovr = 1; /* we have an overflow */ + } + GPR[reg] = temp; /* update the R value */ + } else + ovr = 0; + set_CCs(temp, ovr); /* set the CC's, CC1 = ovr */ + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + goto newpsd; /* handle trap */ + } + break; + case 0x6: /* BEI */ + if ((MODES & PRIVBIT) == 0) { /* must be privileged to BEI */ + TRAPME = PRIVVIOL_TRAP; /* set the trap to take */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + else + TRAPSTATUS |= BIT19; /* set bit 19 of trap status */ + goto newpsd; /* Privlege violation trap */ + } +#ifdef DYNAMIC_DEBUG +TPSD[0] = PSD1; +TPSD[1] = PSD2; +#endif + CPUSTATUS |= BIT24; /* into status word bit 24 too */ + PSD2 &= ~(SETBBIT|RETBBIT); /* clear bit 48 & 49 */ + MODES &= ~(BLKMODE|RETBLKM); /* reset blocked & retain mode bits */ + PSD2 |= SETBBIT; /* set to blocked state */ + MODES |= BLKMODE; /* set blocked mode */ + + SPAD[0xf5] = PSD2; /* save the current PSD2 */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ +#ifdef DYNAMIC_DEBUG +sim_debug(DEBUG_IRQ, &cpu_dev, + "BEI OPSD %.8x %.8x NPSD %.8x %.8x SPDF5 %.8x CPUSTATUS %08x\n", + TPSD[0], TPSD[1], PSD1, PSD2, SPAD[0xf5], CPUSTATUS); +#endif + break; + + case 0x7: /* UEI */ + if ((MODES & PRIVBIT) == 0) { /* must be privileged to UEI */ + TRAPME = PRIVVIOL_TRAP; /* set the trap to take */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + else + TRAPSTATUS |= BIT19; /* set bit 19 of trap status */ + goto newpsd; /* Privlege violation trap */ + } +#ifdef DYNAMIC_DEBUG +TPSD[0] = PSD1; +TPSD[1] = PSD2; +#endif + if (CPUSTATUS & BIT24) { /* see if old mode is blocked */ + irq_pend = 1; /* start scanning interrupts again */ +#ifdef LEAVE_ACTIVE + if (irq_auto) { +/*AIR*/ INTS[irq_auto] &= ~INTS_ACT; /* deactivate specified int level */ +/*AIR*/ SPAD[irq_auto+0x80] &= ~SINT_ACT; /* deactivate in SPAD too */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "<|>IntX deactivate level %02x at UEI PSD1 %08x PSD2 %08x\n", + irq_auto, PSD1, PSD2); +/*AIR*/ irq_auto = 0; /* show done processing in blocked mode */ + } +#endif + } + CPUSTATUS &= ~BIT24; /* clear status word bit 24 */ + MODES &= ~(BLKMODE|RETBLKM); /* reset blocked & retain mode bits */ + PSD2 &= ~(SETBBIT|RETBBIT); /* clear bits 48 & 49 to be unblocked */ + SPAD[0xf5] = PSD2; /* save the current PSD2 */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ +#ifdef DYNAMIC_DEBUG +sim_debug(DEBUG_IRQ, &cpu_dev, + "UEI OPSD %.8x %.8x NPSD %.8x %.8x SPDF5 %.8x CPUSTATUS %08x\n", + TPSD[0], TPSD[1], PSD1, PSD2, SPAD[0xf5], CPUSTATUS); +#endif + break; + case 0x8: /* EAE */ + PSD1 |= AEXPBIT; /* set the enable AEXP flag in PSD 1 */ + MODES |= AEXPBIT; /* enable arithmetic exception in modes & PSD 1 */ + CPUSTATUS |= AEXPBIT; /* into status word too */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ + break; + case 0x9: /* RDSTS */ + GPR[reg] = CPUSTATUS; /* get CPU status word */ + break; + case 0xA: /* SIPU */ /* ignore for now */ + sim_debug(DEBUG_CMD, &cpu_dev, + "SIPU CPUSTATUS %08x SPAD[0xf9] %08x\n", CPUSTATUS, SPAD[0xf9]); + break; + case 0xB: /* RWCS */ /* RWCS ignore for now */ + /* reg = specifies reg containing the ACS/WCS address */ + /* sreg = specifies the ACS/WCS address */ + /* if the WCS option is not present, address spec error */ + /* if the mem addr is not a DW, address spec error */ + /* If 0<-Rs<=fff and Rs bit 0=0, then PROM address */ + /* If 0<-Rs<=fff and Rs bit 0=1, then ACS address */ + /* if bit 20 set, WCS enables, else addr spec error */ + if ((CPUSTATUS & 0x00000800) == 0) { + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + /* Maybe TODO copy something from WCS */ + break; + case 0xC: /* WWCS */ /* WWCS ignore for now */ + /* reg = specifies the logical address in memory that */ + /* is to receive the ACS/WCS contents */ + /* sreg = specifies the ACS/WCS address */ + /* bit 20 of cpu stat must be set=1 to to write to ACS or WCS */ + /* bit 21 of CPU stat must be 0 to write to ACS */ + /* if bit 20 set, WCS enables, else addr spec error */ + if ((CPUSTATUS & 0x00000800) == 0) { + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + /* Maybe TODO copy something to WCS */ + break; + case 0xD: /* SEA */ + if (MODES & BASEBIT) /* see if based */ + goto inv; /* invalid instruction in based mode */ + MODES |= EXTDBIT; /* set new extended flag (bit 5) in modes & PSD */ + PSD1 |= EXTDBIT; /* set the enable AEXP flag in PSD1 */ + CPUSTATUS |= EXTDBIT; /* into status word too */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ + break; + case 0xE: /* DAE */ + MODES &= ~AEXPBIT; /* disable arithmetic exception in modes & PSD */ + PSD1 &= ~AEXPBIT; /* disable AEXP flag in PSD */ + CPUSTATUS &= ~AEXPBIT; /* into status word too */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ + break; + + case 0xF: /* CEA */ + if (MODES & BASEBIT) /* see if based */ + goto inv; /* invalid instruction in based mode */ + MODES &= ~EXTDBIT; /* disable extended mode in modes and PSD */ + PSD1 &= ~EXTDBIT; /* disable extended mode (bit 5) flag in PSD */ + CPUSTATUS &= ~EXTDBIT; /* into status word too */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ + break; + } + break; + case 0x04>>2: /* 0x04 RR|R1|SD|HLF - SD|HLF */ /* ANR, SMC, CMC, RPSWT */ + i_flags &= ~SCC; /* make sure we do not set CC's for dest value */ + switch(opr & 0xF) { + case 0x0: /* ANR */ + dest &= source; /* just an and reg to reg */ + if (dest & MSIGN) + dest |= D32LMASK; /* force upper word to all ones */ + i_flags |= SCC; /* make sure we set CC's for dest value */ + break; + + case 0xA: /* CMC */ /* Cache Memory Control - Diag use only */ + if (CPU_MODEL == MODEL_87) + break; /* just ignore */ + if (CPU_MODEL < MODEL_67) { + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } + if (CPU_MODEL <= MODEL_V6) { + /* Cache memory control bit assignments for reg */ + /* 0-22 reserved, must be zero */ + /* 23 - Initialize Instruction Cache Bank 0 On = 1 Off = 0 */ + /* 24 - Initialize Instruction Cache Bank 1 On = 1 Off = 0 */ + /* 25 - Initialize Operand Cache Bank 0 On = 1 Off = 0 */ + /* 26 - Initialize Operand Cache Bank 1 On = 1 Off = 0 */ + /* 27 - Enable Instruction Cache Bank 0 On = 1 Off = 0 */ + /* 28 - Enable Instruction Cache Bank 1 On = 1 Off = 0 */ + /* 29 - Enable Operand Cache Bank 0 On = 1 Off = 0 */ + /* 30 - Enable Operand Cache Bank 1 On = 1 Off = 0 */ + /* 31 - Bypass Instruction Cache Bank 1 On = 1 Off = 0 */ + sim_debug(DEBUG_EXP, &cpu_dev, + "CMC V6/67 GPR[%02x] = %04x CMCR = %08x CPU STATUS SPAD[f9] = %08x\r\n", + reg, GPR[reg], CMCR, SPAD[0xf9]); + CMCR = GPR[reg]; /* write reg bits 23-31 to cache memory controller */ + i_flags &= ~SD; /* turn off store dest for this instruction */ + } else + if (CPU_MODEL == MODEL_V9) { + sim_debug(DEBUG_EXP, &cpu_dev, + "CMC V9 GPR[%02x] = %08x CMCR = %08x CPU STATUS SPAD[f9] = %08x\r\n", + reg, GPR[reg], CMCR, SPAD[0xf9]); + CMCR = GPR[reg]; /* write reg bits 23-31 to cache memory controller */ + i_flags &= ~SD; /* turn off store dest for this instruction */ + } + break; + + case 0x7: /* SMC */ /* Shared Memory Control - Diag use only */ + if (CPU_MODEL < MODEL_67) { + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } + /* Shared memory control bit assignments for reg */ + /* 0 - Reserved */ + /* 1 - Shared Memory Enabled (=1)/Disabled (=0) */ + /* 2-6 - Upper Bound of Shared Memory */ + /* 7 - Read & Lock Enabled (=1)/Disabled (=0) */ + /* 8-12 - Lower Bound of Shared Memory */ + /* 3-31 - Reserved and must be zero */ + sim_debug(DEBUG_CMD, &cpu_dev, + "SMC V6/67 GPR[%02x] = %08x SMCR = %08x CPU STATUS SPAD[f9] = %08x\n", + reg, GPR[reg], SMCR, SPAD[0xf9]); + SMCR = GPR[reg]; /* write reg bits 0-12 to shared memory controller */ + i_flags &= ~SD; /* turn off store dest for this instruction */ + break; + +/* 67, 97, V6 Computer Configuration Word is copied when bit zero of Rd set to one (0x80000000) */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* |00|01|02 03 04 05 06|07|08 09 10 11 12|13 14 15|16|17|18|19|20 21|22|23 24 25 26|27|28|29|30|31| */ +/* | | S| Upper Bound |RL| Lower Bound |Reserved|4k|8k|SM|P2| Res |AP| Reserved |I0|I1|D0|D1|BY| */ +/* | 0| x| x x x x x| x| x x x x x| 0 0 0| x| x| x| x| 0 0| 0| 0 0 0 0| x| x| x| x| x| */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* */ +/* Bits: 0 Reserved */ +/* 1 Shared Memory Enabled (=1)/Disabled (=0) */ +/* 2-6 Upper Bound of Shared Memory */ +/* 7 Read & Lock Enabled (=1)/Disabled (=0) */ +/* 8-12 Lower Bound of Shared Memory */ +/* 13-15 Reserved */ +/* 16 4K WCS Option Present (=1)/Not Present (=0) */ +/* 17 8K WCS Option Present (=1)/Not Present (=0) */ +/* 18 Firmware Control Store Mode ROMSIM (=1)/PROM (=0) */ +/* 19 IPU Present (=1)/Not Present (=0) */ +/* 20-21 Reserved */ +/* 22 Access Protection ECO Present (=0)/No Access Protection (=0) V6 & V9 */ +/* 23-26 Reserved */ +/* 27 Instruction Cache Bank 0 on (=1)/Off (=0) */ +/* 28 Instruction Cache Bank 1 on (=1)/Off (=0) */ +/* 29 Data Cache Bank 0 on (=1)/Off (=0) */ +/* 30 Data Cache Bank 1 on (=1)/Off (=0) */ +/* 31 Instruction Cache Enabled (=1)/Disabled (=0) */ +/* */ +/* V9 Computer Configuration Word when bit zero of Rd set to one (0x80000000) */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* |00 01 02 03|04 05 06 07|08 09 10 11|12 13 14 15|16|17|18|19|20|21|22|23|24 25 26 27|28 29 30 31| */ +/* | CPU Bank1 | CPU Bank2 | IPU Bank0 | IPU Bank1 |M1|M2|C1|C2|P2|SM|AP| | CPU FW Ver| CPU FW Rev| */ +/* | x x x x| x x x x| x x x x| x x x x| x| x| x| x| x| x| x| x| x x x x| x x x x| */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* */ +/* Bits: 0-15 Cache/Shadow Unit Present (=1)/Not Present (=0) */ +/* 16 MACC Present in CP1 (=1)/Not Present (=0) */ +/* 17 MACC Present in CP2 (=1)/Not Present (=0) */ +/* 18 CP1 Present (=1)/CP1 Not Present (=0) */ +/* 19 CP2 Present (=1)/CP2 Not Present (=0) */ +/* 20 IPU Present (=1)/Not Present (=0) */ +/* 21 Shared Memory Present (=1)/Not Present (=0) */ +/* 22 Access Protection ECO Present (=0)/No Access Protection (=0) V6 & V9 */ +/* 23 Reserved */ +/* 24-27 CPU Firmware Version */ +/* 28-31 CPU Formware Revision Level */ +/* */ +/* V9 CPU Shadow Memory Configuration Word when bit one of Rd set to one (0x40000000) */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* |00 01 02|03 04 05 06 07|08 09 10 11 12 13 14 15|16 17 18 19 20 21 22 23|24 25 26 27 28 29 30 31| */ +/* | SMU # | Not Used | CPU Unit 1 Base Addr | CPU Unit 2 Base Addr | CPU Unit 3 Base Addr | */ +/* | x x x| 0 0 0 0 0| x x x x x x x 0| x x x x x x x 0| x x x x x x x 0| */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* */ +/* Bits: 0 Shadow Memory Unit 1 Present (=1)/Not Present (=0) */ +/* 1 Shadow Memory Unit 2 Present (=1)/Not Present (=0) */ +/* 2 Shadow Memory Unit 3 Present (=1)/Not Present (=0) */ +/* 3-7 Not Used */ +/* 8-14 Shadow Memory Unit 1 Base Address (bits 08-14 of address) */ +/* 15 Always zero */ +/* 16-22 Shadow Memory Unit 2 Base Address (bits 08-14 of address) */ +/* 23 Always zero */ +/* 24-30 Shadow Memory Unit 2 Base Address (bits 08-14 of address) */ +/* 31 Always zero */ +/* */ +/* V9 IPU Shadow Memory Configuration Word when bit two of Rd set to one (0x20000000) */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* |00 01 02|03 04 05 06 07|08 09 10 11 12 13 14 15|16 17 18 19 20 21 22 23|24 25 26 27 28 29 30 31| */ +/* | SMU # | Not Used | IPU Unit 1 Base Addr | IPU Unit 2 Base Addr | IPU Unit 3 Base Addr | */ +/* | x x x| 0 0 0 0 0| x x x x x x x 0| x x x x x x x 0| x x x x x x x 0| */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* */ +/* Bits: 0 Shadow Memory Unit 1 Present (=1)/Not Present (=0) */ +/* 1 Shadow Memory Unit 2 Present (=1)/Not Present (=0) */ +/* 2 Shadow Memory Unit 3 Present (=1)/Not Present (=0) */ +/* 3-7 Not Used */ +/* 8-14 Shadow Memory Unit 1 Base Address (bits 08-14 of address) */ +/* 15 Always zero */ +/* 16-22 Shadow Memory Unit 2 Base Address (bits 08-14 of address) */ +/* 23 Always zero */ +/* 24-30 Shadow Memory Unit 2 Base Address (bits 08-14 of address) */ +/* 31 Always zero */ +/* */ +/* When bit zero of Rd is zero, PSW word 2 is copies to Rd (0x00000000) */ +/* */ + case 0xB: /* RPSWT */ /* Read Processor Status Word 2 (PSD2) */ + if ((GPR[reg] & 0x80000000) && (CPU_MODEL < MODEL_V9)) { + /* if bit 0 of reg set, return (default 0) CPU Configuration Word */ + dest = CCW; /* no cache or shared memory */ + /* make sure bit 19 is zero saying IPU not present */ + dest &= ~0x00001000; /* reset IPU bit for DIAGS */ + /* bit 22 set for access ECO present */ + dest |= 0x00000200; /* set ECO bit for DIAGS */ + /* Try setting cache on bits 27-31 */ + dest |= 0x0000001f; /* set SIM bit for DIAGS */ + } else + if ((GPR[reg] & 0x80000000) && (CPU_MODEL == MODEL_V9)) { + /* if bit 0 of reg set, return Cache/Shadow Configuration Word */ + CMSMC = 0xffff0000; /* no CPU/IPU Cache/Shadow unit present */ + CMSMC |= 0x00000000; /* CPU Cache/Shadow unit present */ + CMSMC |= 0x00000800; /* bit 20, IPU not present */ + CMSMC |= 0x00000200; /* bit 22, Access Protection ECO present */ + CMSMC |= 0x0000001f; /* CPU Firmware Version 1/Rev level 0 */ + dest = CMSMC; /* return starus */ + } else + if ((GPR[reg] & 0x40000000) && (CPU_MODEL == MODEL_V9)) { + /* if bit 1 of reg set, return CPU Shadow Memory Configuration Word */ + CSMCW = 0x00000000; /* no Shadow unit present */ + dest = CSMCW; /* return starus */ + } else + if ((GPR[reg] & 0x20000000) && (CPU_MODEL == MODEL_V9)) { + /* if bit 2 of reg set, return Cache Memory Configuration Word */ + ISMCW = 0x00000000; /* no Shadow unit present */ + dest = ISMCW; /* return starus */ + } else + if ((GPR[reg] & BIT0) == 0x00000000) { + /* if bit 0 of reg not set, return PSD2 */ + /* make sure bit 49 (block state is current state */ + dest = SPAD[0xf5]; /* get PSD2 for user from SPAD 0xf5 */ + dest &= ~(SETBBIT|RETBBIT); /* clear bit 48 & 49 to be unblocked */ + if (CPUSTATUS & BIT24) { /* see if old mode is blocked */ + dest |= SETBBIT; /* set bit 49 for blocked */ + } + } + break; + + case 0x08: /* 0x0408 INV (Diag Illegal instruction) */ + /* HACK HACK HACK for DIAGS */ + if (CPU_MODEL <= MODEL_27) { /* DIAG error for 32/27 only */ + if ((PSD1 & 2) == 0) /* if lf hw instruction */ + i_flags |= HLF; /* if nop in rt hw, bump pc a word */ + } + /* drop through */ + default: /* INV */ /* everything else is invalid instruction */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + break; + } + break; + + case 0x08>>2: /* 0x08 SCC|RR|R1|SD|HLF - */ /* ORR or ORRM */ + dest |= source; /* or the regs into dest reg */ + switch(opr & 0x0f) { + case 0x8: /* this is ORRM op */ + dest &= GPR[4]; /* mask with reg 4 contents */ + /* drop thru */ + case 0x0: /* this is ORR op */ + if (dest & MSIGN) /* see if we need to sign extend */ + dest |= D32LMASK; /* force upper word to all ones */ + break; + default: /* INV */ /* everything else is invalid instruction */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } + break; + + case 0x0C>>2: /* 0x0c SCC|RR|R1|SD|HLF - SCC|SD|HLF */ /* EOR or EORM */ + dest ^= source; /* exclusive or the regs into dest reg */ + switch(opr & 0x0f) { + case 0x8: /* this is EORM op */ + dest &= GPR[4]; /* mask with reg 4 contents */ + /* drop thru */ + case 0x0: /* this is EOR op */ + if (dest & MSIGN) /* see if we need to sign extend */ + dest |= D32LMASK; /* force upper word to all ones */ + break; + default: /* INV */ /* everything else is invalid instruction */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } + break; + + case 0x10>>2: /* 0x10 HLF - HLF */ /* CAR or (basemode SACZ ) */ + if ((opr & 0xF) == 0) { /* see if CAR instruction */ + /* handle non basemode/basemode CAR instr */ + if ((int32)GPR[reg] < (int32)GPR[sreg]) + CC = CC3BIT; /* Rd < Rs; negative */ + else + if (GPR[reg] == GPR[sreg]) + CC = CC4BIT; /* Rd == Rs; zero */ + else + CC = CC2BIT; /* Rd > Rs; positive */ + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + } else { + if ((MODES & BASEBIT) == 0) { /* if not basemode, error */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + goto newpsd; /* handle trap */ + } + /* handle basemode SACZ instruction */ +sacz: /* non basemode SCZ enters here */ + temp = GPR[reg]; /* get destination reg contents to shift */ + CC = 0; /* zero the CC's */ + t = 0; /* start with zero shift count */ + if (temp == 0) { + CC = CC4BIT; /* set CC4 showing dest is zero & cnt is zero too */ + } +#ifdef NOT_FOR_DIAG + /* The doc says the reg is not shifted if bit 0 is set on entry. */ + /* diags says it does, so that is what we will do */ + /* set count to zero, but shift reg 1 left */ + else + if (temp & BIT0) { + CC = 0; /* clear CC4 & set count to zero */ + } +#endif + else + if (temp != 0) { /* shift non zero values */ + while ((temp & FSIGN) == 0) { /* shift the reg until bit 0 is set */ + temp <<= 1; /* shift left 1 bit */ + t++; /* increment shift count */ + } + temp <<= 1; /* shift the sign bit out */ + } + GPR[reg] = temp; /* save the shifted values */ + GPR[sreg] = t; /* set the shift cnt into the src reg */ + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + } + break; + + case 0x14>>2: /* 0x14 HLF - HLF */ /* CMR compare masked with reg */ + if (opr & 0xf) { /* any subop not zero is error */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } + temp = GPR[reg] ^ GPR[sreg]; /* exclusive or src and destination values */ + temp &= GPR[4]; /* and with mask reg (GPR 4) */ + CC = 0; /* set all CCs zero */ + if (temp == 0) /* if result is zero, set CC4 */ + CC = CC4BIT; /* set CC4 to show result 0 */ + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + break; + + case 0x18>>2: /* 0x18 HLF - HLF */ /* SBR, (basemode ZBR, ABR, TBR */ + if (MODES & BASEBIT) { /* handle basemode ZBR, ABR, TBR */ + if ((opr & 0xC) == 0x0) /* SBR instruction */ + goto sbr; /* use nonbase SBR code */ + if ((opr & 0xC) == 0x4) /* ZBR instruction */ + goto zbr; /* use nonbase ZBR code */ + if ((opr & 0xC) == 0x8) /* ABR instruction */ + goto abr; /* use nonbase ABR code */ + if ((opr & 0xC) == 0xC) /* TBR instruction */ + goto tbr; /* use nonbase TBR code */ +inv: + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + + } else { /* handle non basemode SBR */ + if (opr & 0xc) { /* any subop not zero is error */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } +sbr: /* handle basemode too */ + /* move the byte field bits 14-15 to bits 27-28 */ + /* or in the bit# from dest reg field bits 6-8 into bit 29-31 */ + bc = (((opr << 3) & 0x18) | reg); /* get # bits to shift right */ + bc = BIT0 >> bc; /* make a bit mask of bit number */ + t = (PSD1 & 0x70000000) >> 1; /* get old CC bits 1-3 into CCs 2-4*/ + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + if (GPR[sreg] & bc) /* test the bit in src reg */ + t |= CC1BIT; /* set CC1 to the bit value */ + GPR[sreg] |= bc; /* set the bit in src reg */ + PSD1 |= t; /* update the CC's in the PSD */ + } + break; + + case 0x1C>>2: /* 0x1C HLF - HLF */ /* ZBR (basemode SRA, SRL, SLA, SLL) */ + if (MODES & BASEBIT) { /* handle basemode SRA, SRL, SLA, SLL */ + bc = opr & 0x1f; /* get bit shift count */ + if ((opr & 0x60) == 0x00) { /* SRA instruction */ + temp = GPR[reg]; /* get reg value to shift */ + t = temp & FSIGN; /* sign value */ + for (ix=0; ix>= 1; /* shift bit 0 right one bit */ + temp |= t; /* restore original sign bit */ + } + GPR[reg] = temp; /* save the new value */ + break; + } + if ((opr & 0x60) == 0x20) { /* SRL instruction */ + GPR[reg] >>= bc; /* value to be output */ + break; + } + if ((opr & 0x60) == 0x40) { /* SLA instruction */ + temp = GPR[reg]; /* get reg value to shift */ + t = temp & FSIGN; /* sign value */ + ovr = 0; /* set ovr off */ + for (ix=0; ix> bc; /* make a bit mask of bit number */ + t = (PSD1 & 0x70000000) >> 1; /* get old CC bits 1-3 into CCs 2-4*/ + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + if (GPR[sreg] & bc) /* test the bit in src reg */ + t |= CC1BIT; /* set CC1 to the bit value */ + GPR[sreg] &= ~bc; /* reset the bit in src reg */ + PSD1 |= t; /* update the CC's in the PSD */ + } + break; + + case 0x20>>2: /* 0x20 HLF - HLF */ /* ABR (basemode SRAD, SRLD, SLAD, SLLD) */ + if (MODES & BASEBIT) { /* handle basemode SRAD, SRLD, SLAD, SLLD */ + if (reg & 1) { /* see if odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + dest = (t_uint64)GPR[reg+1]; /* get low order reg value */ + dest |= (((t_uint64)GPR[reg]) << 32); /* insert upper reg value */ + bc = opr & 0x1f; /* get bit shift count */ + source = dest & DMSIGN; /* 64 bit sign value */ + switch (opr & 0x60) { + case 0x00: /* SRAD instruction */ + for (ix=0; ix>= 1; /* shift bit 0 right one bit */ + dest |= source; /* restore original sign bit */ + } + break; + + case 0x20: /* SRLD */ + dest >>= bc; /* shift right #bits */ + break; + + case 0x40: /* SLAD instruction */ + ovr = 0; /* set ovr off */ + for (ix=0; ix>32) & FMASK);/* save the hi order reg */ + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + if (ovr) + PSD1 |= BIT1; /* CC1 in PSD */ + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + goto newpsd; /* go execute the trap now */ + } + break; + + case 0x60: /* SLLD */ + dest <<= bc; /* shift left #bits */ + break; + } + GPR[reg+1] = (uint32)(dest & FMASK); /* save the low order reg */ + GPR[reg] = (uint32)((dest>>32) & FMASK);/* save the hi order reg */ + break; + + } else { /* handle nonbase mode ABR */ + if (opr & 0xc) { /* any subop not zero is error */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } +abr: /* basemode ABR too */ + /* move the byte field bits 14-15 to bits 27-28 */ + /* or in the bit# from dest reg field bits 6-8 into bit 29-31 */ + bc = (((opr << 3) & 0x18) | reg); /* get # bits to shift right */ + bc = BIT0 >> bc; /* make a bit mask of bit number */ + temp = GPR[sreg]; /* get reg value to add bit to */ + t = (temp & FSIGN) != 0; /* set flag for sign bit not set in temp value */ + t |= ((bc & FSIGN) != 0) ? 2 : 0; /* ditto for the bit value */ + temp += bc; /* add the bit value to the reg */ + /* if both signs are neg and result sign is positive, overflow */ + /* if both signs are pos and result sign is negative, overflow */ + if ((t == 3 && (temp & FSIGN) == 0) || + (t == 0 && (temp & FSIGN) != 0)) { + ovr = 1; /* we have an overflow */ + } + GPR[sreg] = temp; /* save the new value */ + set_CCs(temp, ovr); /* set the CC's, CC1 = ovr */ + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + goto newpsd; /* handle trap */ + } + } + break; + + case 0x24>>2: /* 0x24 HLF - HLF */ /* TBR (basemode SRC) */ + if (MODES & BASEBIT) { /* handle SRC basemode */ + bc = opr & 0x1f; /* get bit shift count */ + temp = GPR[reg]; /* get reg value to shift */ + if ((opr & 0x60) == 0x40) { /* SLCBR instruction */ + for (ix=0; ix>= 1; /* shift the bit out */ + if (t) + temp |= BIT0; /* put in new sign bit */ + } + } + GPR[reg] = temp; /* shift result */ + } else { /* handle TBR non basemode */ + if (opr & 0xc) { /* any subop not zero is error */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } +tbr: /* handle basemode TBR too */ + /* move the byte field bits 14-15 to bits 27-28 */ + /* or in the bit# from dest reg field bits 6-8 into bit 29-31 */ + bc = (((opr << 3) & 0x18) | reg); /* get # bits to shift right */ + bc = BIT0 >> bc; /* make a bit mask of bit number */ + t = (PSD1 & 0x70000000) >> 1; /* get old CC bits 1-3 into CCs 2-4*/ + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + if (GPR[sreg] & bc) /* test the bit in src reg */ + t |= CC1BIT; /* set CC1 to the bit value */ + PSD1 |= t; /* update the CC's in the PSD */ + } + break; + + case 0x28>>2: /* 0x28 HLF - HLF */ /* Misc OP REG instructions */ + switch(opr & 0xF) { + case 0x0: /* TRSW */ + if (MODES & BASEBIT) + temp = 0x78FFFFFE; /* bits 1-4 and 24 bit addr for based mode */ + else + temp = 0x7807FFFE; /* bits 1-4 and 19 bit addr for non based mode */ + addr = GPR[reg]; /* get reg value */ + /* we are returning to the addr in reg, set CC's from reg */ + /* update the PSD with new address from reg */ + PSD1 &= ~temp; /* clean the bits to be changed */ + PSD1 |= (addr & temp); /* insert the CC's and address */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "TRSW REG %01x PSD %08x %08x modes %08x temp %06x\n", + reg, PSD1, PSD2, MODES, temp); + i_flags |= BT; /* we branched, so no PC update */ + break; + + case 0x2: /* XCBR */ /* Exchange base registers */ + if ((MODES & BASEBIT) == 0) /* see if nonbased */ + goto inv; /* invalid instruction in nonbased mode */ + temp = BR[reg]; /* get dest reg value */ + BR[reg] = BR[sreg]; /* put source reg value int dest reg */ + BR[sreg] = temp; /* put dest reg value into src reg */ + break; + + case 0x4: /* TCCR */ /* Transfer condition codes to GPR bits 28-31 */ + if ((MODES & BASEBIT) == 0) /* see if nonbased */ + goto inv; /* invalid instruction in nonbased mode */ + temp = CC >> 27; /* right justify CC's in reg */ + GPR[reg] = temp; /* put dest reg value into src reg */ + break; + + case 0x5: /* TRCC */ /* Transfer GPR bits 28-31 to condition codes */ + if ((MODES & BASEBIT) == 0) /* see if nonbased */ + goto inv; /* invalid instruction in nonbased mode */ + PSD1 = ((PSD1 & 0x87fffffe)|((GPR[reg] & 0xf) << 27)); /* insert CCs from reg */ + break; + + case 0x8: /* BSUB */ /* Procedure call */ + if ((MODES & BASEBIT) == 0) /* see if nonbased */ + goto inv; /* invalid instruction in nonbased mode */ + + /* if Rd field is 0 (reg is b6-b8), this is a BSUB instruction */ + /* otherwise it is a CALL instruction (Rd != 0) */ + if (reg == 0) { + /* BSUB instruction */ + uint32 cfp = BR[2]; /* get dword bounded frame pointer from BR2 */ + if ((BR[2] & 0x7) != 0) { + /* Fault, must be dw bounded address */ + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + goto newpsd; /* go execute the trap now */ + } + cfp = BR[2] & 0x00fffff8; /* clean the cfp address to 24 bit dw */ + + M[cfp>>2] = (PSD1 + 2) & 0x01fffffe; /* save AEXP bit and PC into frame */ + M[(cfp>>2)+1] = 0x80000000; /* show frame created by BSUB instr */ + BR[1] = BR[sreg] & MASK24; /* Rs reg to BR 1 */ + PSD1 = (PSD1 & 0xff000000) | (BR[1] & MASK24); /* New PSD address */ + BR[3] = GPR[0]; /* GPR 0 to BR 3 (AP) */ + BR[0] = cfp; /* set frame pointer from BR 2 into BR 0 */ + i_flags |= BT; /* we changed the PC, so no PC update */ + } else + { + /* CALL instruction */ + /* get frame pointer from BR2-16 words & make it a dword addr */ + uint32 cfp = ((BR[2]-0x40) & 0x00fffff8); + + /* if cfp and cfp+15w are in different maps, then addr exception error */ + if ((cfp & 0xffe000) != ((cfp+0x3f) & 0xffe000)) { + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + + temp = (PSD1+2) & 0x01fffffe; /* save AEXP bit and PC from PSD1 in to frame */ + if ((TRAPME = Mem_write(cfp, &temp))) { /* Save the PSD into memory */ + goto newpsd; /* memory write error or map fault */ + } + + temp = 0x00000000; /* show frame created by CALL instr */ + if ((TRAPME = Mem_write(cfp+4, &temp))) { /* Save zero into memory */ + goto newpsd; /* memory write error or map fault */ + } + + /* Save BR 0-7 to stack */ + for (ix=0; ix<8; ix++) { + if ((TRAPME = Mem_write(cfp+(4*ix)+8, &BR[ix]))) { /* Save into memory */ + goto newpsd; /* memory write error or map fault */ + } + } + + /* save GPR 2-8 to stack */ + for (ix=2; ix<8; ix++) { + if ((TRAPME = Mem_write(cfp+(4*ix)+32, &GPR[ix]))) { /* Save into memory */ + goto newpsd; /* memory write error or map fault */ + } + } + + /* keep bits 0-7 from old PSD */ + PSD1 = (PSD1 & 0xff000000) | ((BR[sreg]) & MASK24); /* New PSD address */ + BR[1] = BR[sreg]; /* Rs reg to BR 1 */ + BR[3] = GPR[reg]; /* Rd to BR 3 (AP) */ + BR[0] = cfp; /* set current frame pointer into BR[0] */ + BR[2] = cfp; /* set current frame pointer into BR[2] */ + i_flags |= BT; /* we changed the PC, so no PC update */ + } + break; + + case 0xC: /* TPCBR */ /* Transfer program Counter to Base Register */ + if ((MODES & BASEBIT) == 0) /* see if nonbased */ + goto inv; /* invalid instruction in nonbased mode */ + BR[reg] = PSD1 & 0xfffffe; /* save PC from PSD1 into BR */ + break; + + case 0xE: /* RETURN */ /* procedure return for basemode calls */ + if ((MODES & BASEBIT) == 0) /* see if nonbased */ + goto inv; /* invalid instruction in nonbased mode */ + t = BR[0]; /* get frame pointer from BR[0] */ + if ((TRAPME = Mem_read(t+4, &temp))) /* get the word from memory */ + goto newpsd; /* memory read error or map fault */ + /* if Bit0 set, restore all saved regs, else restore only BRs */ + if ((temp & BIT0) == 0) { /* see if GPRs are to be restored */ + /* Bit 0 is not set, so restore all GPRs */ + for (ix=2; ix<8; ix++) + if ((TRAPME = Mem_read(t+ix*4+32, &GPR[ix]))) /* get the word from memory */ + goto newpsd; /* memory read error or map fault */ + } + for (ix=0; ix<8; ix++) { + if ((TRAPME = Mem_read(t+ix*4+8, &BR[ix]))) /* get the word from memory */ + goto newpsd; /* memory read error or map fault */ + } + PSD1 &= ~0x1fffffe; /* leave everything except AEXP bit and PC */ + if ((TRAPME = Mem_read(t, &temp))) /* get the word from memory */ + goto newpsd; /* memory read error or map fault */ + PSD1 |= (temp & 0x01fffffe); /* restore AEXP bit and PC from call frame */ + i_flags |= BT; /* we changed the PC, so no PC update */ + break; + + case 0x1: /* INV */ + case 0x3: /* INV */ + case 0x6: /* INV */ + case 0x7: /* INV */ + case 0x9: /* INV */ + case 0xA: /* INV */ + case 0xB: /* INV */ + case 0xD: /* INV */ + case 0xF: /* INV */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + break; + } + break; + + case 0x2C>>2: /* 0x2C HLF - HLF */ /* Reg-Reg instructions */ + temp = GPR[reg]; /* reg contents specified by Rd */ + addr = GPR[sreg]; /* reg contents specified by Rs */ + bc = 0; + + switch(opr & 0xF) { + case 0x0: /* TRR */ /* SCC|SD|R1 */ + temp = addr; /* set value to go to GPR[reg] */ + bc = 1; /* set CC's at end */ + break; + + case 0x1: /* TRBR */ /* Transfer GPR to BR */ + if ((MODES & BASEBIT) == 0) /* see if nonbased */ + goto inv; /* invalid instruction in nonbased mode */ + BR[reg] = GPR[sreg]; /* copy GPR to BR */ + break; + + case 0x2: /* TBRR */ /* transfer BR to GPR */ + if ((MODES & BASEBIT) == 0) /* see if nonbased */ + goto inv; /* invalid instruction in nonbased mode */ + temp = BR[sreg]; /* set base reg value */ + bc = 1; /* set CC's at end */ + break; + + case 0x3: /* TRC */ /* Transfer register complement */ + temp = addr ^ FMASK; /* complement Rs */ + bc = 1; /* set CC's at end */ + break; + + case 0x4: /* TRN */ /* Transfer register negative */ + temp = NEGATE32(addr); /* negate Rs value */ + if (temp == addr) /* overflow if nothing changed */ + ovr = 1; /* set overflow flag */ + /* reset ovr if val == 0, not set for DIAGS */ + if ((temp == 0) & ovr) + ovr = 0; + bc = 1; /* set the CC's */ + break; + + case 0x5: /* XCR */ /* exchange registers Rd & Rs */ + GPR[sreg] = temp; /* Rd to Rs */ + set_CCs(temp, ovr); /* set the CC's from original Rd */ + temp = addr; /* save the Rs value to Rd reg */ + break; + + case 0x6: /* INV */ + goto inv; + break; + + case 0x7: /* LMAP */ /* Load map reg - Diags only */ + if ((MODES & PRIVBIT) == 0) { /* must be privileged */ + TRAPME = PRIVVIOL_TRAP; /* set the trap to take */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + else + TRAPSTATUS |= BIT19; /* set bit 19 of trap status */ + goto newpsd; /* handle trap */ + } + /* cpu must be unmapped */ + if (MODES & MAPMODE) { /* must be unmapped cpu */ + TRAPME = MAPFAULT_TRAP; /* Map Fault Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT8; /* set bit 8 of trap status */ + else + TRAPSTATUS |= BIT19; /* set bit 19 of trap status */ + goto newpsd; /* handle trap */ + } + { /* load the cpu maps using diag psd */ + uint32 DPSD[2]; /* the PC for the instruction */ + /* get PSD pointed to by real addr in Rd (temp) */ + DPSD[0] = RMW(temp); /* get word one of psd */ + DPSD[1] = RMW(temp+4); /* get word two of psd */ + sim_debug(DEBUG_CMD, &cpu_dev, + "LMAP PSD %08x %08x DPSD %08x %08x modes %08x temp %06x\n", + PSD1, PSD2, DPSD[0], DPSD[1], MODES, temp); + if ((DPSD[1] & MAPBIT) == 0) /* if PSD2 is unmapped, treat as NOP */ + goto skipit; + if (PSD2 & RETMBIT) /* don't load maps if retain bit set */ + goto skipit; + temp2 = MODES; /* save modes bits through load_maps call */ + MODES = DPSD[0] & 0x87000000; /* extract bits 0, 5, 6, 7 from PSD 1 */ + MODES |= MAPMODE; /* set mapped mode flag for load_maps call */ + sim_debug(DEBUG_CMD, &cpu_dev, + "LMAP PSD %08x %08x DPSD %08x %08x modes %08x temp2 %08x\n", + PSD1, PSD2, DPSD[0], DPSD[1], MODES, temp2); + /* we need to load the new maps */ + TRAPME = load_maps(DPSD, 1); /* load maps for new PSD */ + sim_debug(DEBUG_CMD, &cpu_dev, + "LMAP TRAPME %08x MAPC[8-c] %08x %08x %08x %08x %08x %08x\n", + TRAPME, MAPC[7], MAPC[8], MAPC[9], MAPC[10], MAPC[11], MAPC[12]); + MODES = temp2; /* restore modes flags */ + if (TRAPME) { + /* DIAGS wants the cpix for the psd to be the requested one */ + PSD2 = (PSD2 & 0xffffc000) | (DPSD[1] & 0x3ff8); + SPAD[0xf5] = PSD2; /* save the current PSD2 */ + goto newpsd; /* handle trap */ + } + goto skipit; + break; + } + break; + + case 0x8: /* TRRM */ /* SCC|SD|R1 */ + temp = addr & GPR[4]; /* transfer reg-reg masked */ + bc = 1; /* set CC's at end */ + break; + + /* CPUSTATUS bits */ + /* Bits 0-19 reserved */ + /* Bit 20 =0 Write to writable control store is disabled */ + /* =1 Write to writable control store is enabled */ + /* Bit 21 =0 Enable PROM mode */ + /* =1 Enable Alterable Control Store Mode */ + /* Bit 22 =0 Enable High Speed Floating Point Accelerator */ + /* =1 Disable High Speed Floating Point Accelerator */ + /* Bit 23 =0 Disable privileged mode halt trap */ + /* =1 Enable privileged mode halt trap */ + /* Bit 24 is reserved */ + /* bit 25 =0 Disable software trap handling (enable automatic trap handling) */ + /* =1 Enable software trap handling */ + /* Bits 26-31 reserved */ + case 0x9: /* SETCPU */ + if ((MODES & PRIVBIT) == 0) { /* must be privileged */ + TRAPME = PRIVVIOL_TRAP; /* set the trap to take */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + else + TRAPSTATUS |= BIT19; /* set bit 19 of trap status */ + goto newpsd; /* handle trap */ + } + temp2 = CPUSTATUS; /* save original */ + /* bits 20-23 and bit 25 can change */ + CPUSTATUS &= 0xfffff0bf; /* zero bits that can change */ + CPUSTATUS |= (temp & 0x0f40); /* or in the new status bits */ + CPUSTATUS |= BIT22; /* HS Floating is set to off */ + /* make sure WCS is off and prom mode set to 0 (on) */ + CPUSTATUS &= ~(BIT20|BIT21); /* make zero */ + sim_debug(DEBUG_CMD, &cpu_dev, + "SETCPU orig %08x user bits %08x New CPUSTATUS %08x SPAD[f9] %08x\n", + temp2, temp, CPUSTATUS, SPAD[0xf9]); + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ + break; + + case 0xA: /* TMAPR */ /* Transfer map to Reg - Diags only */ + if ((MODES & PRIVBIT) == 0) { /* must be privileged */ + TRAPME = PRIVVIOL_TRAP; /* set the trap to take */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + else + TRAPSTATUS |= BIT19; /* set bit 19 of trap status */ + goto newpsd; /* handle trap */ + } + if (CPU_MODEL <= MODEL_27) { /* 7X & 27 must be unmapped */ + if (MODES & MAPMODE) { /* must be unmapped cpu */ + TRAPME = MAPFAULT_TRAP; /* Map Fault Trap */ + goto newpsd; /* handle trap */ + } + } + /* Rs has map number for even/odd pair loading */ + if (CPU_MODEL < MODEL_27) { + /* 32/77 with 32 map regs */ + addr &= 0x1e; /* make 0-15 */ + temp = MAPC[addr>>1]; /* get even/odd maps */ + } else + if ((CPU_MODEL == MODEL_27) || (CPU_MODEL == MODEL_87)) { + /* 32/27 & 32/87 have 256 maps */ + addr &= 0xfe; /* make 0-255 */ + temp = MAPC[addr>>1]; /* get even/odd maps */ + } else { + /* 32/67, 32/97, V6 & V9 have 2048 maps demand paging */ + addr &= 0x7ff; /* make 0-2047 */ + temp = MAPC[addr>>1]; /* get even/odd maps */ + if ((addr & 1) == 0) /* if even reg, use left hw */ + temp >>= 16; /* move over reg value */ + temp &= 0xffff; /* just 16 bits */ + if (TLB[addr] & 0x04000000) /* see if HIT bit set */ + temp |= 0x80000000; /* show hit BIT is set */ + temp |= ((TLB[addr] & 0xf8000000) >> 16); /* add in protect bits */ + if ((addr < 0x26) || (addr > 0x7f8)) + sim_debug(DEBUG_CMD, &cpu_dev, + "TMAPR #%4x val %08x TLB %08x RMR %04x MAPC %08x\n", + addr, temp, TLB[addr], RMR(addr<<1), MAPC[addr/2]); + } + GPR[reg] = temp; /* save the temp value to Rd reg */ + goto skipit; + break; + + case 0xB: /* TRCM */ /* Transfer register complemented masked */ + temp = (addr ^ FMASK) & GPR[4]; /* compliment & mask */ + bc = 1; /* set the CC's */ + break; + + case 0xC: /* TRNM */ /* Transfer register negative masked */ + temp = NEGATE32(addr); /* complement GPR[reg] */ + if (temp == addr) /* check for overflow */ + ovr = 1; /* overflow */ + /* reset ovr if val == 0, not set for DIAGS */ + if ((temp == 0) & ovr) + ovr = 0; + temp &= GPR[4]; /* and with negative reg */ + bc = 1; /* set the CC's */ + break; + + case 0xD: /* XCRM */ /* Exchange registers masked */ + addr &= GPR[4]; /* and Rs with mask reg */ + temp &= GPR[4]; /* and Rd with mask reg */ + GPR[sreg] = temp; /* Rs to get Rd masked value */ + set_CCs(temp, ovr); /* set the CC's from original Rd */ + temp = addr; /* save the Rs value to Rd reg */ + break; + + case 0xE: /* TRSC */ /* transfer reg to SPAD */ + if ((MODES & PRIVBIT) == 0) { /* must be privileged */ + TRAPME = PRIVVIOL_TRAP; /* set the trap to take */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + else + TRAPSTATUS |= BIT19; /* set bit 19 of trap status */ + goto newpsd; /* handle trap */ + } + t = (GPR[reg] >> 16) & 0xff; /* get SPAD address from Rd (6-8) */ + temp2 = SPAD[t]; /* get old SPAD data */ + SPAD[t] = GPR[sreg]; /* store Rs into SPAD */ + break; + + case 0xF: /* TSCR */ /* Transfer scratchpad to register */ + if ((MODES & PRIVBIT) == 0) { /* must be privileged */ + TRAPME = PRIVVIOL_TRAP; /* set the trap to take */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + else + TRAPSTATUS |= BIT19; /* set bit 19 of trap status */ + goto newpsd; /* handle trap */ + } + t = (GPR[sreg] >> 16) & 0xff; /* get SPAD address from Rs (9-11) */ + temp = SPAD[t]; /* get SPAD data into Rd (6-8) */ + break; + } + GPR[reg] = temp; /* save the temp value to Rd reg */ + if (bc) /* set cc's if bc set */ + set_CCs(temp, ovr); /* set the CC's */ + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + goto newpsd; /* handle trap */ + } +skipit: + /* for retain, leave PSD2 alone */ + break; + + case 0x30>>2: /* 0x30 */ /* CALM */ + /* Process CALM for 32/27 when in left hw, else invalid */ + if ((CPU_MODEL <= MODEL_87) && (CPU_MODEL != MODEL_67)) { + uint32 oldstatus = CPUSTATUS; /* keep for retain blocking state */ + /* DIAG error for 32/27 or 32/87 only */ + if ((PSD1 & 2) != 0) /* is it lf hw instruction */ + goto inv; /* invalid instr if in rt hw */ + addr = SPAD[0xf0]; /* get trap table memory address from SPAD (def 80) */ + if ((addr == 0) || ((addr&MASK24) == MASK24)) { /* see if secondary vector table set up */ + TRAPME = ADDRSPEC_TRAP; /* Not setup, error */ + goto newpsd; /* program error */ + } + addr = addr + (0x0A << 2); /* addr has mem addr of CALM trap vector (def A8) */ + t = M[addr >> 2]; /* get the ICB address from memory */ + if ((t == 0) || ((t&MASK24) == MASK24)) { /* see if ICB set up */ + TRAPME = ADDRSPEC_TRAP; /* Not setup, error */ + goto newpsd; /* program error */ + } + bc = PSD2 & 0x3ff8; /* get copy of cpix */ + /* this will skip over rt hw instruction if any */ + PSD1 = (PSD1 + 4) | (((PSD1 & 2) >> 1) & 1); /* bump pc by 1 wd */ + M[t>>2] = PSD1 & 0xfffffffe; /* store PSD 1 + 1HW to point to next instruction */ + M[(t>>2)+1] = PSD2; /* store PSD 2 */ + PSD1 = M[(t>>2)+2]; /* get new PSD 1 */ + PSD2 = (M[(t>>2)+3] & ~0x3fff) | bc; /* get new PSD 2 w/old cpix */ + M[(t>>2)+4] = opr & 0x03FF; /* store calm number in bits 6-15 */ + + /* set the mode bits and CCs from the new PSD */ + CC = PSD1 & 0x78000000; /* extract bits 1-4 from PSD1 */ + MODES = PSD1 & 0x87000000; /* extract bits 0, 5, 6, 7 from PSD 1 */ + CPUSTATUS &= ~0x87000000; /* reset bits in CPUSTATUS */ + CPUSTATUS |= MODES; /* now insert into CPUSTATUS */ + + /* set new map mode and interrupt blocking state in CPUSTATUS */ + if (PSD2 & MAPBIT) { + CPUSTATUS |= BIT8; /* set bit 8 of cpu status */ + MODES |= MAPMODE; /* set mapped mode */ + } else { + CPUSTATUS &= ~BIT8; /* reset bit 8 of cpu status */ +/*TRY_01072022*/ MODES &= ~MAPMODE; /* reset mapped mode */ + } + + /* set interrupt blocking state */ + if ((PSD2 & RETBBIT) == 0) { /* is it retain blocking state */ + if (PSD2 & SETBBIT) { /* no, is it set blocking state */ + CPUSTATUS |= BIT24; /* yes, set blk state in cpu status bit 24 */ + MODES |= BLKMODE; /* set blocked mode */ + } else { + CPUSTATUS &= ~BIT24; /* no, reset blk state in cpu status bit 24 */ + MODES &= ~BLKMODE; /* reset blocked mode */ + irq_pend = 1; /* start scanning interrupts again */ +#ifdef LEAVE_ACTIVE + if (irq_auto) { +/*AIR*/ INTS[irq_auto] &= ~INTS_ACT; /* deactivate specified int level */ +/*AIR*/ SPAD[irq_auto+0x80] &= ~SINT_ACT; /* deactivate in SPAD too */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "<|>IntX deactivate level %02x at CALM PSD1 %08x\n", + irq_auto, PSD1); +/*AIR*/ irq_auto = 0; /* show done processing in blocked mode */ + } +#endif + } + } else { + /* handle retain blocking state */ + PSD2 &= ~RETMBIT; /* turn off retain bit in PSD2 */ + /* set new blocking state in PSD2 */ + PSD2 &= ~(SETBBIT|RETBBIT); /* clear bit 48 & 49 to be unblocked */ + MODES &= ~(BLKMODE|RETBLKM); /* reset blocked & retain mode bits */ + if (oldstatus & BIT24) { /* see if old mode is blocked */ + PSD2 |= SETBBIT; /* set to blocked state */ + MODES |= BLKMODE; /* set blocked mode */ + } + } + + SPAD[0xf5] = PSD2; /* save the current PSD2 */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ + TRAPME = 0; /* not to be processed as trap */ + goto newpsd; /* new psd loaded */ + } else { +// fprintf(stderr, "got CALM trap\r\n"); + goto inv; /* invalid instr */ + } + break; + + case 0x34>>2: /* 0x34 SD|ADR - inv */ /* LA non-basemode */ + if (MODES & BASEBIT) /* see if based */ + goto inv; /* invalid instruction in based mode */ + if (MODES & EXTDBIT) { /* see if extended mode */ + dest = (t_uint64)(addr&MASK24); /* just pure 24 bit address */ + } else { /* use bits 13-31 */ + dest = (t_uint64)((addr&0x7ffff) | ((FC & 4) << 17)); /* F bit to bit 12 */ + } + break; + + case 0x38>>2: /* 0x38 HLF - HLF */ /* REG - REG floating point */ + switch(opr & 0xF) { + case 0x0: /* ADR */ + temp = GPR[reg]; /* reg contents specified by Rd */ + addr = GPR[sreg]; /* reg contents specified by Rs */ + t = (temp & FSIGN) != 0; /* set flag for sign bit not set in temp value */ + t |= ((addr & FSIGN) != 0) ? 2 : 0; /* ditto for the reg value */ + temp = temp + addr; /* add the values */ + /* if both signs are neg and result sign is positive, overflow */ + /* if both signs are pos and result sign is negative, overflow */ + if ((t == 3 && (temp & FSIGN) == 0) || (t == 0 && (temp & FSIGN) != 0)) { + ovr = 1; /* we have an overflow */ + } + i_flags |= SF; /* special processing */ + break; + + case 0x1: /* ADRFW */ + case 0x3: /* SURFW */ + /* TODO not on 32/27 */ + temp = GPR[reg]; /* reg contents specified by Rd */ + addr = GPR[sreg]; /* reg contents specified by Rs */ + /* temp has Rd (GPR[reg]), addr has Rs (GPR[sreg]) */ + if ((opr & 0xF) == 0x3) { + addr = NEGATE32(addr); /* subtract, so negate source */ + } + temp2 = s_adfw(temp, addr, &CC); /* do ADFW */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "%s GPR[%d] %08x addr %08x result %08x CC %08x\n", + (opr&0xf)==3 ? "SURFW":"ADRFW", + reg, GPR[reg], GPR[sreg], temp2, CC); + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + if (CC & CC1BIT) { /* check for arithmetic exception */ + ovr = 1; /* exception */ + /* leave Rd & Rs unchanged if AEXPBIT is set */ + if (MODES & AEXPBIT) { + TRAPME = AEXPCEPT_TRAP; /* trap the system now */ + goto newpsd; /* process the trap */ + } + } + /* AEXPBIT not set, so save the fixed return value */ + /* return result to destination reg */ + GPR[reg] = temp2; /* dest - reg contents specified by Rd */ + break; + + case 0x2: /* MPRBR */ + /* TODO not on 32/27 */ + if ((MODES & BASEBIT) == 0) /* see if nonbased */ + goto inv; /* invalid instruction in nonbased mode */ + if (reg & 1) { + /* Spec fault if not even reg */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + temp = GPR[reg+1]; /* get multiplicand */ + addr = GPR[sreg]; /* multiplier */ + + /* change value into a 64 bit value */ + dest = ((t_uint64)(addr & FMASK)) | ((addr & FSIGN) ? D32LMASK : 0); + source = ((t_uint64)(temp & FMASK)) | ((temp & FSIGN) ? D32LMASK : 0); + dest = dest * source; /* do the multiply */ + i_flags |= (SD|SCC); /* save dest reg and set CC's */ + dbl = 1; /* double reg save */ + break; + + case 0x4: /* DVRFW */ + /* TODO not on 32/27 */ + temp = GPR[reg]; /* reg contents specified by Rd */ + addr = GPR[sreg]; /* reg contents specified by Rs */ + /* temp has Rd (GPR[reg]), addr has Rs (GPR[sreg]) */ + temp2 = (uint32)s_dvfw(temp, addr, &CC); /* divide reg by sreg */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "DVRFW GPR[%d] %08x src %08x result %08x\n", + reg, GPR[reg], addr, temp2); + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + if (CC & CC1BIT) { /* check for arithmetic exception */ + ovr = 1; /* exception */ + /* leave Rd & Rs unchanged if AEXPBIT is set */ + if (MODES & AEXPBIT) { + TRAPME = AEXPCEPT_TRAP; /* trap the system now */ + goto newpsd; /* process the trap */ + } + } + /* AEXPBIT not set, so save the fixed return value */ + /* return result to destination reg */ + GPR[reg] = temp2; /* dest - reg contents specified by Rd */ + break; + + case 0x5: /* FIXW */ + /* TODO not on 32/27 */ + /* convert from 32 bit float to 32 bit fixed */ + addr = GPR[sreg]; /* reg contents specified by Rs */ + temp2 = s_fixw(addr, &CC); /* do conversion */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "FIXW GPR[%d] %08x result %08x\n", + sreg, GPR[sreg], temp2); + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + if (CC & CC1BIT) { /* check for arithmetic exception */ + ovr = 1; /* exception */ + /* leave Rd & Rs unchanged if AEXPBIT is set */ + if (MODES & AEXPBIT) { + TRAPME = AEXPCEPT_TRAP; /* trap the system now */ + goto newpsd; /* process the trap */ + } + } + /* AEXPBIT not set, so save the fixed return value */ + /* return result to destination reg */ + GPR[reg] = temp2; /* dest - reg contents specified by Rd */ + break; /* go set CC's */ + + case 0x6: /* MPRFW */ + /* TODO not on 32/27 */ + temp = GPR[reg]; /* reg contents specified by Rd */ + addr = GPR[sreg]; /* reg contents specified by Rs */ + /* temp has Rd (GPR[reg]), addr has Rs (GPR[sreg]) */ + temp2 = s_mpfw(temp, addr, &CC); /* mult reg by sreg */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "MPRFW GPR[%d] %08x src %08x result %08x\n", + reg, GPR[reg], addr, temp2); + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + if (CC & CC1BIT) { /* check for arithmetic exception */ + ovr = 1; /* exception */ + /* leave Rd & Rs unchanged if AEXPBIT is set */ + if (MODES & AEXPBIT) { + TRAPME = AEXPCEPT_TRAP; /* trap the system now */ + goto newpsd; /* process the trap */ + } + } + /* AEXPBIT not set, so save the fixed return value */ + /* return result to destination reg */ + GPR[reg] = temp2; /* dest - reg contents specified by Rd */ + break; + + case 0x7: /* FLTW */ + /* TODO not on 32/27 */ + /* convert from 32 bit integer to 32 bit float */ + addr = GPR[sreg]; /* reg contents specified by Rs */ + GPR[reg] = s_fltw(addr, &CC); /* do conversion & set CC's */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "FLTW GPR[%d] %08x result %08x\n", + sreg, GPR[sreg], GPR[reg]); + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + break; + + case 0x8: /* ADRM */ + temp = GPR[reg]; /* reg contents specified by Rd */ + addr = GPR[sreg]; /* reg contents specified by Rs */ + t = (temp & FSIGN) != 0; /* set flag for sign bit not set in temp value */ + t |= ((addr & FSIGN) != 0) ? 2 : 0; /* ditto for the reg value */ + temp = temp + addr; /* add the values */ + /* if both signs are neg and result sign is positive, overflow */ + /* if both signs are pos and result sign is negative, overflow */ + if ((t == 3 && (temp & FSIGN) == 0) || + (t == 0 && (temp & FSIGN) != 0)) + ovr = 1; /* we have an overflow */ + temp &= GPR[4]; /* mask the destination reg */ + i_flags |= SF; /* special processing */ + break; + + case 0x9: /* ADRFD */ + case 0xB: /* SURFD */ + /* TODO not on 32/27 */ + if ((reg & 1) || (sreg & 1)) { /* see if any odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + td = (((t_uint64)GPR[reg]) << 32); /* get upper reg value */ + td |= (t_uint64)GPR[reg+1]; /* insert low order reg value */ + source = (((t_uint64)GPR[sreg]) << 32); /* get upper reg value */ + source |= (t_uint64)GPR[sreg+1]; /* insert low order reg value */ + if ((opr & 0xF) == 0xb) { + source = NEGATE32(source); /* make negative for subtract */ + } + dest = s_adfd(td, source, &CC); /* do ADFD */ + + sim_debug(DEBUG_DETAIL, &cpu_dev, + "%s GPR[%d] %08x %08x src %016llx result %016llx\n", + (opr&0xf)==8 ? "ADRFD":"SURFD", reg, GPR[reg], GPR[reg+1], source, dest); + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + if (CC & CC1BIT) { /* check for arithmetic exception */ + ovr = 1; /* exception */ + /* leave Rd & Rs unchanged if AEXPBIT is set */ + if (MODES & AEXPBIT) { + TRAPME = AEXPCEPT_TRAP; /* trap the system now */ + goto newpsd; /* process the trap */ + } + } + /* AEXPBIT not set, so save the fixed return value */ + /* return result to destination reg */ + GPR[reg+1] = (uint32)(dest & FMASK); /* save the low order reg */ + GPR[reg] = (uint32)((dest>>32) & FMASK);/* save the hi order reg */ + break; + + case 0xA: /* DVRBR */ + /* TODO not on 32/27 */ + if ((MODES & BASEBIT) == 0) /* see if nonbased */ + goto inv; /* invalid instruction in nonbased mode */ + if (reg & 1) { + /* Spec fault if not even reg */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + /* get Rs divisor value */ + source = (t_uint64)(GPR[sreg]) | ((GPR[sreg] & FSIGN) ? D32LMASK : 0); + /* merge the dividend regs into the 64bit value */ + dest = (((t_uint64)GPR[reg]) << 32) | ((t_uint64)GPR[reg+1]); + if (source == 0) { + goto doovr4; + break; + } + td = (t_int64)dest % (t_int64)source; /* remainder */ + if (((td & DMSIGN) ^ (dest & DMSIGN)) != 0) /* Fix sign if needed */ + td = NEGATE32(td); /* dividend and remainder must be same sign */ + dest = (t_int64)dest / (t_int64)source; /* now do the divide */ + /* test for overflow */ + if ((dest & D32LMASK) != 0 && (dest & D32LMASK) != D32LMASK) { +doovr4: + ovr = 1; /* the quotient exceeds 31 bit, overflow */ + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + } + /* the original regs must be returned unchanged if aexp */ + set_CCs(temp, ovr); /* set the CC's */ + } else { + GPR[reg] = (uint32)(td & FMASK); /* reg gets remainder, reg+1 quotient */ + GPR[reg+1] = (uint32)(dest & FMASK); /* store quotient in reg+1 */ + set_CCs(GPR[reg+1], ovr); /* set the CC's, CC1 = ovr */ + } + break; + + case 0xC: /* DVRFD */ + /* TODO not on 32/27 */ + if ((reg & 1) || (sreg & 1)) { /* see if any odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + td = (((t_uint64)GPR[reg]) << 32); /* get upper reg value */ + td |= (t_uint64)GPR[reg+1]; /* insert low order reg value */ + source = (((t_uint64)GPR[sreg]) << 32); /* get upper reg value */ + source |= (t_uint64)GPR[sreg+1]; /* insert low order reg value */ + dest = s_dvfd(td, source, &CC); /* divide double values */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "DVRFD GPR[%d] %08x %08x src %016llx result %016llx\n", + reg, GPR[reg], GPR[reg+1], source, dest); + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + if (CC & CC1BIT) { /* check for arithmetic exception */ + ovr = 1; /* exception */ + /* leave Rd & Rs unchanged if AEXPBIT is set */ + if (MODES & AEXPBIT) { + TRAPME = AEXPCEPT_TRAP; /* trap the system now */ + goto newpsd; /* process the trap */ + } + } + /* AEXPBIT not set, so save the fixed return value */ + /* return result to destination reg */ + GPR[reg+1] = (uint32)(dest & FMASK); /* save the low order reg */ + GPR[reg] = (uint32)((dest>>32) & FMASK);/* save the hi order reg */ + break; + + case 0xD: /* FIXD */ + /* dest - reg contents specified by Rd & Rd+1 */ + /* source - reg contents specified by Rs & Rs+1 */ + if (sreg & 1) { + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + goto newpsd; /* go execute the trap now */ + } + /* merge the sregs into the 64bit value */ + source = (((t_uint64)GPR[sreg]) << 32) | ((t_uint64)GPR[sreg+1]); + /* convert from 64 bit double to 64 bit int */ + dest = s_fixd(source, &CC); + sim_debug(DEBUG_DETAIL, &cpu_dev, + "FIXD GPR[%d] %08x %08x result %016llx\n", + sreg, GPR[sreg], GPR[sreg+1], dest); + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + if (CC & CC1BIT) { /* check for arithmetic exception */ + ovr = 1; /* exception */ + /* leave Rd & Rs unchanged if AEXPBIT is set */ + if (MODES & AEXPBIT) { + TRAPME = AEXPCEPT_TRAP; /* trap the system now */ + goto newpsd; /* process the trap */ + } + } + /* AEXPBIT not set, so save the fixed return value */ + /* return result to destination reg */ + GPR[reg+1] = (uint32)(dest & FMASK); /* save the low order reg */ + GPR[reg] = (uint32)((dest>>32) & FMASK);/* save the hi order reg */ + break; + + case 0xE: /* MPRFD */ + /* TODO not on 32/27 */ + if ((reg & 1) || (sreg & 1)) { /* see if any odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + td = (((t_uint64)GPR[reg]) << 32); /* get upper reg value */ + td |= (t_uint64)GPR[reg+1]; /* insert low order reg value */ + source = (((t_uint64)GPR[sreg]) << 32); /* get upper reg value */ + source |= (t_uint64)GPR[sreg+1]; /* insert low order reg value */ + dest = s_mpfd(td, source, &CC); /* multiply double values */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "MPRFD GPR[%d] %08x %08x src %016llx result %016llx\n", + reg, GPR[reg], GPR[reg+1], source, dest); + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + if (CC & CC1BIT) { /* check for arithmetic exception */ + ovr = 1; /* exception */ + /* leave Rd & Rs unchanged if AEXPBIT is set */ + if (MODES & AEXPBIT) { + TRAPME = AEXPCEPT_TRAP; /* trap the system now */ + goto newpsd; /* process the trap */ + } + } + /* AEXPBIT not set, so save the fixed return value */ + /* return result to destination reg */ + GPR[reg+1] = (uint32)(dest & FMASK); /* save the low order reg */ + GPR[reg] = (uint32)((dest>>32) & FMASK);/* save the hi order reg */ + break; + + case 0xF: /* FLTD */ + /* TODO not on 32/27 */ + /* convert from 64 bit integer to 64 bit float */ + if ((reg & 1) || (sreg & 1)) { /* see if any odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + source = (((t_uint64)GPR[sreg]) << 32); /* get upper reg value */ + source |= (t_uint64)GPR[sreg+1]; /* insert low order reg value */ + dest = s_fltd(source, &CC); /* do conversion & set CC's */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "FLTD GPR[%d] %08x %08x result %016llx\n", + sreg, GPR[sreg], GPR[sreg+1], dest); + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + GPR[reg+1] = (uint32)(dest & FMASK); /* save the low order reg */ + GPR[reg] = (uint32)((dest>>32) & FMASK);/* save the hi order reg */ + break; + } + if (i_flags & SF) { /* see if special processing */ + GPR[reg] = temp; /* temp has destination reg value */ + set_CCs(temp, ovr); /* set the CC's */ + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + goto newpsd; /* go execute the trap now */ + } + } + break; + + case 0x3C>>2: /* 0x3C HLF - HLF */ /* SUR and SURM */ + temp = GPR[reg]; /* get negative value to add */ + temp2 = GPR[sreg]; /* get negative value to add */ + addr = NEGATE32(GPR[sreg]); /* reg contents specified by Rs */ + switch(opr & 0xF) { + case 0x0: /* SUR */ + t = (temp & FSIGN) != 0; /* set flag for sign bit not set in temp value */ + t |= ((addr & FSIGN) != 0) ? 2 : 0; /* ditto for the reg value */ + temp = temp + addr; /* add the values */ + /* if both signs are neg and result sign is positive, overflow */ + /* if both signs are pos and result sign is negative, overflow */ + if ((t == 3 && (temp & FSIGN) == 0) || + (t == 0 && (temp & FSIGN) != 0)) + ovr = 1; /* we have an overflow */ + if (addr == FSIGN) + ovr = 1; /* we have an overflow */ + break; + + case 0x8: /* SURM */ + t = (temp & FSIGN) != 0; /* set flag for sign bit not set in temp value */ + t |= ((addr & FSIGN) != 0) ? 2 : 0; /* ditto for the reg value */ + temp = temp + addr; /* add the values */ + /* if both signs are neg and result sign is positive, overflow */ + /* if both signs are pos and result sign is negative, overflow */ + if ((t == 3 && (temp & FSIGN) == 0) || + (t == 0 && (temp & FSIGN) != 0)) + ovr = 1; /* we have an overflow */ + temp &= GPR[4]; /* mask the destination reg */ + if (addr == FSIGN) + ovr = 1; /* we have an overflow */ + break; + default: + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + break; + } + GPR[reg] = temp; /* save the result */ + set_CCs(temp, ovr); /* set CCs for result */ + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + goto newpsd; /* go execute the trap now */ + } + break; + + case 0x40>>2: /* 0x40 SCC|SD|HLF - INV */ /* MPR */ + if (MODES & BASEBIT) + goto inv; /* invalid instruction in basemode */ + if (reg & 1) { /* odd reg specified? */ + /* Spec fault */ + /* HACK HACK HACK for DIAGS */ + if (CPU_MODEL <= MODEL_27) { /* DIAG error for 32/27 only */ + if ((PSD1 & 2) == 0) /* if lf hw instruction */ + i_flags &= ~HLF; /* if nop in rt hw, bump pc a word */ + else + PSD1 &= ~3; /* fake out 32/27 diag error */ + } + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + if (opr & 0xf) { /* any subop not zero is error */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } + temp = GPR[reg+1]; /* get multiplicand */ + addr = GPR[sreg]; /* multiplier */ + + /* change immediate value into a 64 bit value */ + dest = ((t_uint64)(addr & FMASK)) | ((addr & FSIGN) ? D32LMASK : 0); + source = ((t_uint64)(temp & FMASK)) | ((temp & FSIGN) ? D32LMASK : 0); + dest = dest * source; /* do the multiply */ + dbl = 1; /* double reg save */ + break; + + case 0x44>>2: /* 0x44 ADR - ADR */ /* DVR */ + /* sreg has Rs */ + if (reg & 1) { + /* Spec fault */ + /* HACK HACK HACK for DIAGS */ + if (CPU_MODEL <= MODEL_27) { /* DIAG error for 32/27 only */ + if ((PSD1 & 2) == 0) /* if lf hw instruction */ + i_flags &= ~HLF; /* if nop in rt hw, bump pc a word */ + else + PSD1 &= ~3; /* fake out 32/27 diag error */ + } + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + if (opr & 0xf) { /* any subop not zero is error */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + goto newpsd; /* handle trap */ + } + /* get Rs divisor value */ + source = (t_uint64)(GPR[sreg]) | ((GPR[sreg] & FSIGN) ? D32LMASK : 0); + /* merge the dividend regs into the 64bit value */ + dest = (((t_uint64)GPR[reg]) << 32) | ((t_uint64)GPR[reg+1]); + if (source == 0) + goto doovr3; + td = (t_int64)dest % (t_int64)source; /* remainder */ + if (((td & DMSIGN) ^ (dest & DMSIGN)) != 0) /* Fix sign if needed */ + td = NEGATE32(td); /* dividend and remainder must be same sign */ + dest = (t_int64)dest / (t_int64)source; /* now do the divide */ + int64a = dest; + if (int64a < 0) + int64a = -int64a; + if (int64a > 0x7fffffff) /* if more than 31 bits, we have an error */ + goto doovr3; + if (((dest & D32LMASK) != 0 && (dest & D32LMASK) != D32LMASK) || + (((dest & D32LMASK) == D32LMASK) && ((dest & D32RMASK) == 0))) { /* test for overflow */ +doovr3: + dest = (((t_uint64)GPR[reg]) << 32);/* insert upper reg value */ + dest |= (t_uint64)GPR[reg+1]; /* get low order reg value */ + ovr = 1; /* the quotient exceeds 31 bit, overflow */ + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + } + /* the original regs must be returned unchanged if aexp */ + CC = CC1BIT; /* set ovr CC bit */ + if (dest == 0) + CC |= CC4BIT; /* dw is zero, so CC4 */ + else + if (dest & DMSIGN) + CC |= CC3BIT; /* it is neg dw, so CC3 */ + else + CC |= CC2BIT; /* then dest > 0, so CC2 */ + PSD1 &= 0x87FFFFFE; /* clear the old CC's from PSD1 */ + PSD1 |= CC; /* update the CC's in the PSD */ + } else { + GPR[reg] = (uint32)(td & FMASK); /* reg gets remainder, reg+1 quotient */ + GPR[reg+1] = (uint32)(dest & FMASK); /* store quotient in reg+1 */ + set_CCs(GPR[reg+1], ovr); /* set the CC's, CC1 = ovr */ + } + break; + + case 0x48>>2: /* 0x48 INV - INV */ /* unused opcodes */ + case 0x4C>>2: /* 0x4C INV - INV */ /* unused opcodes */ + default: + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + break; + + case 0x50>>2: /* 0x50 INV - SD|ADR */ /* LA basemode LABRM */ + if ((MODES & BASEBIT) == 0) /* see if nonbased */ + goto inv; /* invalid instruction in nonbased mode */ + dest = (t_uint64)(addr&MASK24); /* just pure 24 bit address */ + break; + + case 0x54>>2: /* 0x54 SM|ADR - INV */ /* (basemode STWBR) */ + if ((MODES & BASEBIT) == 0) /* see if nonbased */ + goto inv; /* invalid instruction in nonbased mode */ + if (FC != 0) { /* word address only */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "ADDRSPEC8 OP %04x addr %08x\n", OP, addr); + goto newpsd; /* go execute the trap now */ + } + dest = BR[reg]; /* save the BR to memory */ + break; + + case 0x58>>2: /* 0x58 SB|ADR - INV */ /* (basemode SUABR and LABR) */ + if ((MODES & BASEBIT) == 0) /* see if nonbased */ + goto inv; /* invalid instruction in nonbased mode */ + if ((FC & 4) == 0) { /* see if SUABR F=0 0x5800 */ + dest = BR[reg] - addr; /* subtract addr from the BR and store back to BR */ + } else { /* LABR if F=1 0x5808 */ + dest = addr; /* addr goes to specified BR */ + } + break; + + case 0x5C>>2: /* 0x5C RM|ADR - INV */ /* (basemode LWBR and BSUBM) */ + if ((MODES & BASEBIT) == 0) /* see if nonbased */ + goto inv; /* invalid instruction in nonbased mode */ + if ((FC & 3) != 0) { /* word address only */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "ADDRSPEC9 OP %04x addr %08x\n", OP, addr); + goto newpsd; /* go execute the trap now */ + } + if ((FC & 0x4) == 0) { /* this is a LWBR 0x5C00 instruction */ + BR[reg] = (uint32)source; /* load memory location into BR */ + } else + { /* this is a CALLM/BSUBM instruction */ + /* if Rd field is 0 (reg is b6-8), this is a BSUBM instruction */ + /* otherwise it is a CALLM instruction (Rd != 0) */ + if (reg == 0) { + /* BSUBM instruction */ + uint32 cfp = BR[2]; /* get dword bounded frame pointer from BR2 */ + + if ((BR[2] & 0x7) != 0) { + /* Fault, must be dw bounded address */ + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + goto newpsd; /* go execute the trap now */ + } + + temp = (PSD1+4) & 0x01fffffe; /* save AEXP bit and PC from PSD1 into frame */ + if ((TRAPME = Mem_write(cfp, &temp))) { /* Save the PSD into memory */ + goto newpsd; /* memory write error or map fault */ + } + + temp = 0x80000000; /* show frame created by BSUBM instr */ + if ((TRAPME = Mem_write(cfp+4, &temp))) { /* Save zero into memory */ + goto newpsd; /* memory write error or map fault */ + } + + temp = addr & 0xfffffe; /* CALL memory address */ + if ((temp & 0x3) != 0) { /* check for word aligned */ + /* Fault, must be word bounded address */ + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + goto newpsd; /* go execute the trap now */ + } + + if ((TRAPME = Mem_read(temp, &addr))) /* get the word from memory */ + goto newpsd; /* memory read error or map fault */ + + BR[1] = addr; /* effective address contents to BR 1 */ + /* keep bits 0-7 from old PSD */ + PSD1 = ((PSD1 & 0xff000000) | (BR[1] & 0x01fffffe)); /* New PSD address */ + BR[3] = GPR[0]; /* GPR[0] to BR[3] (AP) */ + BR[0] = cfp; /* set current frame pointer into BR[0] */ + i_flags |= BT; /* we changed the PC, so no PC update */ + } else { + /* CALLM instruction */ + + /* get frame pointer from BR2 - 16 words & make it a dword addr */ + uint32 cfp = ((BR[2]-0x40) & 0x00fffff8); + + /* if cfp and cfp+15w are in different maps, then addr exception error */ + if ((cfp & 0xffe000) != ((cfp+0x3f) & 0xffe000)) { + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + + temp = (PSD1+4) & 0x01fffffe; /* save AEXP bit and PC from PSD1 in to frame */ + if ((TRAPME = Mem_write(cfp, &temp))) { /* Save the PSD into memory */ + goto newpsd; /* memory write error or map fault */ + } + + temp = 0x00000000; /* show frame created by CALL instr */ + if ((TRAPME = Mem_write(cfp+4, &temp))) { /* Save zero into memory */ + goto newpsd; /* memory write error or map fault */ + } + + /* save the BRs 0-7 on stack */ + for (ix=0; ix<8; ix++) { + if ((TRAPME = Mem_write(cfp+(4*ix)+8, &BR[ix]))) { /* Save into memory */ + goto newpsd; /* memory write error or map fault */ + } + } + + /* save GPRs 2-7 on stack */ + for (ix=2; ix<8; ix++) { + if ((TRAPME = Mem_write(cfp+(4*ix)+32, &GPR[ix]))) { /* Save into memory */ + goto newpsd; /* memory write error or map fault */ + } + } + + temp = addr & 0xfffffe; /* CALL memory address */ + if ((temp & 0x3) != 0) { /* check for word aligned */ + /* Fault, must be word bounded address */ + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + goto newpsd; /* go execute the trap now */ + } + + if ((TRAPME = Mem_read(temp, &addr))) /* get the word from memory */ + goto newpsd; /* memory read error or map fault */ + + BR[1] = addr; /* effective address contents to BR 1 */ + /* keep bits 0-6 from old PSD */ + PSD1 = (PSD1 & 0xff000000) | ((BR[1]) & 0x01fffffe); /* New PSD address */ + BR[3] = GPR[reg]; /* Rd to BR 3 (AP) */ + BR[0] = cfp; /* set current frame pointer into BR[0] */ + BR[2] = cfp; /* set current frame pointer into BR[2] */ + i_flags |= BT; /* we changed the PC, so no PC update */ + } + } + break; + + case 0x60>>2: /* 0x60 HLF - INV */ /* NOR Rd,Rs */ + if ((MODES & BASEBIT)) { /* only for nonbased mode */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } + if (opr & 0xf) { /* any subop not zero is error */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } + /* exponent must not be zero or all 1's */ + /* normalize the value Rd in GPR[reg] and put exponent into Rs GPR[sreg] */ + temp = s_nor(GPR[reg], &GPR[sreg]); + sim_debug(DEBUG_DETAIL, &cpu_dev, + "NOR GPR[%d] %08x result %08x exp %02x\n", + reg, GPR[reg], temp, GPR[sreg]); + GPR[reg] = temp; + break; + + case 0x64>>2: /* 0x64 SD|HLF - INV */ /* NORD */ + if ((MODES & BASEBIT)) { /* only for nonbased mode */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } + if (reg & 1) { /* see if odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + if (opr & 0xf) { /* any subop not zero is error */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } + /* shift until upper 5 bits are neither 0 or all 1's */ + /* merge the GPR[reg] & GPR[reg+1] into a 64bit value */ + td = (((t_uint64)GPR[reg]) << 32) | ((t_uint64)GPR[reg+1]); + /* normalize the value Rd in GPR[reg] and put exponent into Rs GPR[sreg] */ + dest = s_nord(td, &GPR[sreg]); + sim_debug(DEBUG_DETAIL, &cpu_dev, + "NORD GPR[%d] %08x %08x result %016llx exp %02x\n", + reg, GPR[reg], GPR[reg+1], dest, GPR[sreg]); + GPR[reg+1] = (uint32)(dest & FMASK); /* save the low order reg */ + GPR[reg] = (uint32)((dest>>32) & FMASK);/* save the hi order reg */ + break; + + case 0x68>>2: /* 0x68 HLF - INV */ /* non basemode SCZ */ + if (MODES & BASEBIT) + goto inv; /* invalid instruction */ + if (opr & 0xf) { /* any subop not zero is error */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } + goto sacz; /* use basemode sacz instruction */ + + case 0x6C>>2: /* 0x6C HLF - INV */ /* non basemode SRA & SLA */ + if (MODES & BASEBIT) + goto inv; /* invalid instruction */ + bc = opr & 0x1f; /* get bit shift count */ + temp = GPR[reg]; /* get reg value to shift */ + t = temp & FSIGN; /* sign value */ + if (opr & 0x0040) { /* is this SLA */ + ovr = 0; /* set ovr off */ + for (ix=0; ix>= 1; /* shift bit 0 right one bit */ + temp |= t; /* restore original sign bit */ + } + GPR[reg] = temp; /* save the new value */ + } + break; + + case 0x70>>2: /* 0x70 SD|HLF - INV */ /* non-basemode SRL & SLL */ + if (MODES & BASEBIT) + goto inv; /* invalid instruction in basemode */ + bc = opr & 0x1f; /* get bit shift count */ + if (opr & 0x0040) /* is this SLL, bit 9 set */ + GPR[reg] <<= bc; /* shift left #bits */ + else + GPR[reg] >>= bc; /* shift right #bits */ + break; + + case 0x74>>2: /* 0x74 SD|HLF - INV */ /* non-basemode SRC & SLC */ + if (MODES & BASEBIT) + goto inv; /* invalid instruction in basemode */ + bc = opr & 0x1f; /* get bit shift count */ + temp = GPR[reg]; /* get reg value to shift */ + if (opr & 0x0040) { /* is this SLC, bit 9 set */ + for (ix=0; ix>= 1; /* shift the bit out */ + if (t) + temp |= BIT0; /* put in new sign bit */ + } + } + GPR[reg] = temp; /* shift result */ + break; + + case 0x78>>2: /* 0x78 HLF - INV */ /* non-basemode SRAD & SLAD */ + if (MODES & BASEBIT) /* Base mode? */ + goto inv; /* invalid instruction in basemode */ + if (reg & 1) { /* see if odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + bc = opr & 0x1f; /* get bit shift count */ + dest = (t_uint64)GPR[reg+1]; /* get low order reg value */ + dest |= (((t_uint64)GPR[reg]) << 32); /* insert upper reg value */ + source = dest & DMSIGN; /* 64 bit sign value */ + if (opr & 0x0040) { /* is this SLAD */ + ovr = 0; /* set ovr off */ + for (ix=0; ix>32) & FMASK);/* save the hi order reg */ + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + if (ovr) + PSD1 |= BIT1; /* CC1 in PSD */ + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + goto newpsd; /* go execute the trap now */ + } + } else { /* this is a SRAD */ + for (ix=0; ix>= 1; /* shift bit 0 right one bit */ + dest |= source; /* restore original sign bit */ + } + GPR[reg+1] = (uint32)(dest & FMASK); /* save the low order reg */ + GPR[reg] = (uint32)((dest>>32) & FMASK);/* save the hi order reg */ + } + break; + + case 0x7C>>2: /* 0x7C HLF - INV */ /* non-basemode SRLD & SLLD */ + if (MODES & BASEBIT) + goto inv; /* invalid instruction in basemode */ + if (reg & 1) { /* see if odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + dest = (t_uint64)GPR[reg+1]; /* get low order reg value */ + dest |= (((t_uint64)GPR[reg]) << 32); /* insert upper reg value */ + bc = opr & 0x1f; /* get bit shift count */ + if (opr & 0x0040) /* is this SLL, bit 9 set */ + dest <<= bc; /* shift left #bits */ + else + dest >>= bc; /* shift right #bits */ + GPR[reg+1] = (uint32)(dest & FMASK); /* save the low order reg */ + GPR[reg] = (uint32)((dest>>32) & FMASK);/* save the hi order reg */ + break; + + case 0x80>>2: /* 0x80 SD|ADR - SD|ADR */ /* LEAR */ + /* convert address to real physical address */ + TRAPME = RealAddr(addr, &temp, &t, MEM_RD); + // diag allows any addr if mapped + if (TRAPME != ALLOK) { + sim_debug(DEBUG_TRAP, &cpu_dev, + "At LEAR with TRAPME %04x addr %08x\n", TRAPME, addr); + goto newpsd; /* memory read error or map fault */ + } + /* set access bit for mapped addresses */ + if ((CPU_MODEL >= MODEL_V6) && (MODES & MAPMODE)) { + uint32 map, mix, nix, msdl, mpl, mmap; + + nix = (addr >> 13) & 0x7ff; /* get 11 bit map value */ + /* check our access to the memory */ + switch (t & 0x0e) { + case 0x0: case 0x2: + /* O/S or user has no read/execute access, do protection violation */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "LEAR readI protect error @ %06x prot %02x modes %08x page %04x\n", + addr, t, MODES, nix); + if (CPU_MODEL == MODEL_V9) + TRAPSTATUS |= BIT1; /* set bit 1 of trap status */ + else + TRAPSTATUS |= BIT12; /* set bit 12 of trap status */ + return MPVIOL; /* return memory protection violation */ + case 0x4: case 0x6: case 0x8: case 0xc: case 0xa: case 0xe: + /* O/S or user has read/execute access, no protection violation */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "LEAR readJ protect is ok @ %06x prot %02x modes %08x page %04x\n", + addr, t, MODES, nix); + } + /* we have read access, so go set the access bit in the map entry */ + mpl = SPAD[0xf3]; /* get mpl from spad address */ + if (nix < BPIX) { + mix = nix; /* get map index in memory */ + msdl = RMW(mpl+4); /* get mpl entry for O/S */ + } else { + mix = nix-BPIX; /* get map index in memory */ + msdl = RMW(mpl+CPIX+4); /* get mpl entry for given CPIX */ + } + mmap = RMH(msdl+(mix<<1)); /* map content from memory */ + map = RMR((nix<<1)); /* read the map cache contents */ + if (((map & 0x800) == 0)) { /* see if access bit is already on */ + mmap |= 0x800; /* set the accessed bit in the map cache entry */ + map |= 0x800; /* set the accessed bit in the memory map entry */ + WMR((nix<<1), map); /* store the map reg contents into cache */ + TLB[nix] |= 0x0c000000; /* set the accessed & hit bits in TLB too */ + WMH(msdl+(mix<<1), mmap); /* save modified memory map with access bit set */ + sim_debug(DEBUG_EXP, &cpu_dev, + "LEAR Laddr %06x page %04x set access bit TLB %08x map %04x nmap %04x\n", + addr, nix, TLB[nix], map, mmap); + } + } + + /* OS code says F bit is not transferred, so just ignore it */ + /* DIAGS needs it, so put it back */ + if (FC & 4) /* see if F bit was set */ + temp |= 0x01000000; /* set bit 7 of address */ + dest = temp; /* put in dest to go out */ + break; + + case 0x84>>2: /* 0x84 SD|RR|RNX|ADR - SD|RNX|ADR */ /* ANMx */ + td = dest & source; /* DO ANMX */ + CC = 0; + switch(FC) { /* adjust for hw or bytes */ + case 4: case 5: case 6: case 7: /* byte address */ + /* ANMB */ + td &= 0xff; /* mask out right most byte */ + dest &= 0xffffff00; /* make place for byte */ + if (td == 0) + CC |= CC4BIT; /* byte is zero, so CC4 */ + else + CC |= CC2BIT; /* then td > 0, so CC2 */ + break; + case 1: /* left halfword addr */ + case 3: /* right halfword addr */ + /* ANMH */ + td &= RMASK; /* mask out right most 16 bits */ + dest &= LMASK; /* make place for halfword */ + if (td == 0) + CC |= CC4BIT; /* hw is zero, so CC4 */ + else + CC |= CC2BIT; /* then td > 0, so CC2 */ + break; + case 0: /* 32 bit word */ + /* ANMW */ + td &= D32RMASK; /* mask out right most 32 bits */ + dest = 0; /* make place for 64 bits */ + if (td == 0) + CC |= CC4BIT; /* word is zero, so CC4 */ + else + if (td & 0x80000000) + CC |= CC3BIT; /* it is neg wd, so CC3 */ + else + CC |= CC2BIT; /* then td > 0, so CC2 */ + break; + case 2: /* 64 bit double */ + /* ANMD */ + dest = 0; /* make place for 64 bits */ + if (td == 0) + CC |= CC4BIT; /* dw is zero, so CC4 */ + else + if (td & DMSIGN) + CC |= CC3BIT; /* it is neg dw, so CC3 */ + else + CC |= CC2BIT; /* then td > 0, so CC2 */ + break; + } + dest |= td; /* insert result into dest */ + if (FC != 2) { /* do not sign extend DW */ + if (dest & 0x80000000) /* see if we need to sign extend */ + dest |= D32LMASK; /* force upper word to all ones */ + } + PSD1 &= 0x87FFFFFE; /* clear the old CC's from PSD1 */ + PSD1 |= CC; /* update the CC's in the PSD */ + break; + + case 0x88>>2: /* 0x88 SD|RR|RNX|ADR - SD|RNX|ADR */ /* ORMx */ + td = dest | source; /* DO ORMX */ +meoa: /* merge point for eor, and, or */ + CC = 0; + switch(FC) { /* adjust for hw or bytes */ + case 4: case 5: case 6: case 7: /* byte address */ + /* ORMB */ + td &= 0xff; /* mask out right most byte */ + dest &= 0xffffff00; /* make place for byte */ + dest |= td; /* insert result into dest */ + if (dest == 0) + CC |= CC4BIT; /* byte is zero, so CC4 */ + else + if (dest & MSIGN) { + CC |= CC3BIT; /* assume negative */ + dest |= D32LMASK; /* force upper word to all ones */ + } + else + CC |= CC2BIT; /* then td > 0, so CC2 */ + break; + case 1: /* left halfword addr */ + case 3: /* right halfword addr */ + /* ORMH */ + td &= RMASK; /* mask out right most 16 bits */ + dest &= LMASK; /* make place for halfword */ + dest |= td; /* insert result into dest */ + if (dest == 0) + CC |= CC4BIT; /* byte is zero, so CC4 */ + else + if (dest & MSIGN) { + CC |= CC3BIT; /* assume negative */ + dest |= D32LMASK; /* force upper word to all ones */ + } + else + CC |= CC2BIT; /* then td > 0, so CC2 */ + break; + case 0: /* 32 bit word */ + /* ORMW */ + td &= D32RMASK; /* mask out right most 32 bits */ + dest = 0; /* make place for 64 bits */ + dest |= td; /* insert result into dest */ + if (dest == 0) + CC |= CC4BIT; /* byte is zero, so CC4 */ + else + if (dest & MSIGN) { + CC |= CC3BIT; /* assume negative */ + dest |= D32LMASK; /* force upper word to all ones */ + } + else + CC |= CC2BIT; /* then td > 0, so CC2 */ + break; + case 2: /* 64 bit double */ + /* ORMD */ + dest = 0; /* make place for 64 bits */ + dest |= td; /* insert result into dest */ + if (dest == 0) + CC |= CC4BIT; /* byte is zero, so CC4 */ + else + if (dest & DMSIGN) + CC |= CC3BIT; /* assume negative */ + else + CC |= CC2BIT; /* then td > 0, so CC2 */ + break; + } + PSD1 &= 0x87FFFFFE; /* clear the old CC's from PSD1 */ + PSD1 |= CC; /* update the CC's in the PSD */ + break; + + case 0x8C>>2: /* 0x8C SD|RR|RNX|ADR - SD|RNX|ADR */ /* EOMx */ + /* must special handle because we are getting bit difference */ + /* for word, halfword, & byte zero the upper 32 bits of dest */ + /* Diags require CC's to be set on result value of byte, hw, wd, or dw */ + td = dest ^ source; /* DO EOMX */ + goto meoa; + break; + + case 0x90>>2: /* 0x90 SCC|RR|RM|ADR - RM|ADR */ /* CAMx */ + if (dbl == 0) { + int32a = dest & D32RMASK; /* mask out right most 32 bits */ + int32b = source & D32RMASK; /* mask out right most 32 bits */ + int32c = int32a - int32b; /* signed diff */ + td = int32c; + if (int32a > int32b) dest = 1; + else + if (int32a == int32b) dest = 0; + else dest = -1; + } else { + int64a = dest; /* mask out right most 32 bits */ + int64b = source; /* mask out right most 32 bits */ + int64c = int64a - int64b; /* signed diff */ + td = int64c; + if (int64a > int64b) dest = 1; + else + if (int64a == int64b) dest = 0; + else dest = -1; + } + break; + + case 0x94>>2: /* 0x94 RR|RM|ADR - RM|ADR */ /* CMMx */ + /* CMMD needs both regs to be masked with R4 */ + if (dbl) { + /* we need to and both regs with R4 */ + t_uint64 nm = (((t_uint64)GPR[4]) << 32) | (((t_uint64)GPR[4]) & D32RMASK); + td = dest; /* save dest */ + dest ^= source; + dest &= nm; /* mask both regs with reg 4 contents */ + } else { + td = dest; /* save dest */ + dest ^= source; /* <= 32 bits, so just do lower 32 bits */ + dest &= (((t_uint64)GPR[4]) & D32RMASK); /* mask with reg 4 contents */ + } + CC = 0; + if (dest == 0ll) + CC |= CC4BIT; + PSD1 &= 0x87FFFFFE; /* clear the old CC's from PSD1 */ + PSD1 |= CC; /* update the CC's in the PSD */ + break; + + case 0x98>>2: /* 0x98 ADR - ADR */ /* SBM */ + if ((FC & 04) == 0) { + /* Fault, f-bit must be set for SBM instruction */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + if ((TRAPME = Mem_read(addr, &temp))) /* get the word from memory */ + goto newpsd; /* memory read error or map fault */ + + t = (PSD1 & 0x70000000) >> 1; /* get old CC bits 1-3 into CCs 2-4*/ + /* use C bits and bits 6-8 (reg) to generate shift bit count */ + bc = ((FC & 3) << 3) | reg; /* get # bits to shift right */ + bc = BIT0 >> bc; /* make a bit mask of bit number */ + PSD1 &= 0x87FFFFFE; /* clear the old CC's from PSD1 */ + if (temp & bc) /* test the bit in memory */ + t |= CC1BIT; /* set CC1 to the bit value */ + PSD1 |= t; /* update the CC's in the PSD */ + temp |= bc; /* set the bit in temp */ + if ((TRAPME = Mem_write(addr, &temp))) { /* put word back into memory */ + goto newpsd; /* memory write error or map fault */ + } + break; + + case 0x9C>>2: /* 0x9C ADR - ADR */ /* ZBM */ + if ((FC & 04) == 0) { + /* Fault, byte address not allowed */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + if ((TRAPME = Mem_read(addr, &temp))) /* get the word from memory */ + goto newpsd; /* memory read error or map fault */ + + t = (PSD1 & 0x70000000) >> 1; /* get old CC bits 1-3 into CCs 2-4*/ + /* use C bits and bits 6-8 (reg) to generate shift bit count */ + bc = ((FC & 3) << 3) | reg; /* get # bits to shift right */ + bc = BIT0 >> bc; /* make a bit mask of bit number */ + PSD1 &= 0x87FFFFFE; /* clear the old CC's from PSD1 */ + if (temp & bc) /* test the bit in memory */ + t |= CC1BIT; /* set CC1 to the bit value */ + PSD1 |= t; /* update the CC's in the PSD */ + temp &= ~bc; /* reset the bit in temp */ + if ((TRAPME = Mem_write(addr, &temp))) { /* put word into memory */ + goto newpsd; /* memory write error or map fault */ + } + break; + + case 0xA0>>2: /* 0xA0 ADR - ADR */ /* ABM */ + if ((FC & 04) == 0) { + /* Fault, byte address not allowed */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + if ((TRAPME = Mem_read(addr, &temp))) /* get the word from memory */ + goto newpsd; /* memory read error or map fault */ + + /* use C bits and bits 6-8 (reg) to generate shift bit count */ + bc = ((FC & 3) << 3) | reg; /* get # bits to shift right */ + bc = BIT0 >> bc; /* make a bit mask of bit number */ + t = (temp & FSIGN) != 0; /* set flag for sign bit not set in temp value */ + t |= ((bc & FSIGN) != 0) ? 2 : 0; /* ditto for the bit value */ + temp += bc; /* add the bit value to the reg */ + /* if both signs are neg and result sign is positive, overflow */ + /* if both signs are pos and result sign is negative, overflow */ + if ((t == 3 && (temp & FSIGN) == 0) || + (t == 0 && (temp & FSIGN) != 0)) { + ovr = 1; /* we have an overflow */ + } + set_CCs(temp, ovr); /* set the CC's, CC1 = ovr */ + if ((TRAPME = Mem_write(addr, &temp))) { /* put word into memory */ + goto newpsd; /* memory write error or map fault */ + } + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + goto newpsd; /* handle trap */ + } + break; + + case 0xA4>>2: /* 0xA4 ADR - ADR */ /* TBM */ + if ((FC & 04) == 0) { + /* Fault, byte address not allowed */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + if ((TRAPME = Mem_read(addr, &temp))) /* get the word from memory */ + goto newpsd; /* memory read error or map fault */ + + t = (PSD1 & 0x70000000) >> 1; /* get old CC bits 1-3 into CCs 2-4*/ + /* use C bits and bits 6-8 (reg) to generate shift bit count */ + bc = ((FC & 3) << 3) | reg; /* get # bits to shift right */ + bc = BIT0 >> bc; /* make a bit mask of bit number */ + PSD1 &= 0x87FFFFFE; /* clear the old CC's from PSD1 */ + if (temp & bc) /* test the bit in memory */ + t |= CC1BIT; /* set CC1 to the bit value */ + PSD1 |= t; /* update the CC's in the PSD */ + break; + + case 0xA8>>2: /* 0xA8 RM|ADR - RM|ADR */ /* EXM */ + if ((FC & 04) != 0 || FC == 2) { /* can not be byte or doubleword */ + /* Fault */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + if ((TRAPME = Mem_read(addr, &temp))) { /* get the word from memory */ + if (CPU_MODEL == MODEL_V9) /* V9 wants bit0 set in pfault */ + if (TRAPME == DMDPG) /* demand page request */ + pfault |= 0x80000000; /* set instruction fetch paging error */ + goto newpsd; /* memory read error or map fault */ + } + + IR = temp; /* get instruction from memory */ + if (FC == 3) /* see if right halfword specified */ + IR <<= 16; /* move over the HW instruction */ +#ifdef DIAG_SAYS_OK_TO_EXECUTE_ANOTHER_EXECUTE + if ((IR & 0xFC7F0000) == 0xC8070000 || /* No EXR target */ + (IR & 0xFF800000) == 0xA8000000 || /* No EXM target */ + (IR & 0xFC000000) == 0x80000000) { +#else + /* 32/67 diag says execute of execute is OK */ + if ((IR & 0xFC000000) == 0x80000000) { +#endif + /* Fault, attempt to execute another EXR, EXRR, EXM, or LEAR */ + goto inv; /* invalid instruction */ + } + EXM_EXR = 4; /* set PC increment for EXM */ + + OPSD1 &= 0x87FFFFFE; /* clear the old PSD CC's */ + OPSD1 |= PSD1 & 0x78000000; /* update the CC's in the old PSD */ + /* TODO Update other history information for this instruction */ + if (hst_lnt) { + hst[hst_p].opsd1 = OPSD1; /* update the CC in opsd1 */ + hst[hst_p].npsd1 = PSD1; /* save new psd1 */ + hst[hst_p].npsd2 = PSD2; /* save new psd2 */ + hst[hst_p].modes = MODES; /* save current mode bits */ + hst[hst_p].modes |= (CPUSTATUS & BIT24); /* save blocking mode bit */ + for (ix=0; ix<8; ix++) { + hst[hst_p].reg[ix] = GPR[ix]; /* save reg */ + hst[hst_p].reg[ix+8] = BR[ix]; /* save breg */ + } + } + + /* DEBUG_INST support code */ + OPSD1 &= 0x87FFFFFE; /* clear the old CC's */ + OPSD1 |= PSD1 & 0x78000000; /* update the CC's in the PSD */ + /* output mapped/unmapped */ + if (MODES & BASEBIT) + BM = 'B'; + else + BM = 'N'; + if (MODES & MAPMODE) + MM = 'M'; + else + MM = 'U'; + if (CPUSTATUS & BIT24) + BK = 'B'; + else + BK = 'U'; + sim_debug(DEBUG_INST, &cpu_dev, "%c%c%c %.8x %.8x %.8x ", + BM, MM, BK, OPSD1, PSD2, OIR); + if (cpu_dev.dctrl & DEBUG_INST) + fprint_inst(sim_deb, OIR, 0); /* display instruction */ + sim_debug(DEBUG_INST, &cpu_dev, + "\n\tR0=%.8x R1=%.8x R2=%.8x R3=%.8x", GPR[0], GPR[1], GPR[2], GPR[3]); + sim_debug(DEBUG_INST, &cpu_dev, + " R4=%.8x R5=%.8x R6=%.8x R7=%.8x\n", GPR[4], GPR[5], GPR[6], GPR[7]); + goto exec; /* go execute the instruction */ + break; + + case 0xAC>>2: /* 0xAC SCC|SD|RM|ADR - SCC|SD|RM|ADR */ /* Lx */ + dest = source; /* set value to load into reg */ + break; + + case 0xB0>>2: /* 0xB0 SCC|SD|RM|ADR - SCC|SD|RM|ADR */ /* LMx */ + /* LMD needs both regs to be masked with R4 */ + if (dbl) { + /* we need to and both regs with R4 */ + t_uint64 nm = (((t_uint64)GPR[4]) << 32) | (((t_uint64)GPR[4]) & D32RMASK); + dest = source & nm; /* mask both regs with reg 4 contents */ + } else { + dest = source; /* <= 32 bits, so just do lower 32 bits */ + dest &= (((t_uint64)GPR[4]) & D32RMASK); /* mask with reg 4 contents */ + if (dest & 0x80000000) /* see if we need to sign extend */ + dest |= D32LMASK; /* force upper word to all ones */ + } + break; + + case 0xB4>>2: /* 0xB4 SCC|SD|RM|ADR - SCC|SD|RM|ADR */ /* LNx */ + dest = NEGATE32(source); /* set the value to load into reg */ + td = dest; + if (dest != 0 && (dest == source || dest == 0x80000000)) + ovr = 1; /* set arithmetic exception status */ + if (FC != 2) { /* do not sign extend DW */ + if (dest & 0x80000000) /* see if we need to sign extend */ + dest |= D32LMASK; /* force upper word to all ones */ + } + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (dest != 0 && ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + } + break; + + case 0xBC>>2: /* 0xBC SD|RR|RM|ADR - SD|RR|RM|ADR */ /* SUMx */ + source = NEGATE32(source); + /* Fall through */ + + case 0xB8>>2: /* 0xB8 SD|RR|RM|ADR - SD|RR|RM|ADR */ /* ADMx */ + ovr = 0; + CC = 0; + /* DIAG fixs */ + if (dbl == 0) { + source &= D32RMASK; /* just 32 bits */ + dest &= D32RMASK; /* just 32 bits */ + t = (source & MSIGN) != 0; + t |= ((dest & MSIGN) != 0) ? 2 : 0; + td = dest + source; /* DO ADMx*/ + td &= D32RMASK; /* mask out right most 32 bits */ + dest = 0; /* make place for 64 bits */ + dest |= td; /* insert 32 bit result into dest */ + /* if both signs are neg and result sign is positive, overflow */ + /* if both signs are pos and result sign is negative, overflow */ + if (((t == 3) && ((dest & MSIGN) == 0)) || + ((t == 0) && ((dest & MSIGN) != 0))) + ovr = 1; + if ((td == 0) && ((source & MSIGN) == MSIGN) && ovr) + ovr = 0; /* Diags want 0 and no ovr on MSIGN - MSIGN */ + if (dest & MSIGN) + dest = (D32LMASK | dest); /* sign extend */ + else + dest = (D32RMASK & dest); /* zero fill */ + if (td == 0) + CC |= CC4BIT; /* word is zero, so CC4 */ + else + if (td & 0x80000000) + CC |= CC3BIT; /* it is neg wd, so CC3 */ + else + CC |= CC2BIT; /* then td > 0, so CC2 */ + } else { + /* ADMD */ + t = (source & DMSIGN) != 0; + t |= ((dest & DMSIGN) != 0) ? 2 : 0; + td = dest + source; /* get sum */ + dest = td; /* insert 64 bit result into dest */ + /* if both signs are neg and result sign is positive, overflow */ + /* if both signs are pos and result sign is negative, overflow */ + if (((t == 3) && ((dest & DMSIGN) == 0)) || + ((t == 0) && ((dest & DMSIGN) != 0))) + ovr = 1; + if (td == 0) + CC |= CC4BIT; /* word is zero, so CC4 */ + else + if (td & DMSIGN) + CC |= CC3BIT; /* it is neg wd, so CC3 */ + else + CC |= CC2BIT; /* then td > 0, so CC2 */ + } + if (ovr) + CC |= CC1BIT; /* set overflow CC */ + PSD1 &= 0x87FFFFFE; /* clear the old CC's from PSD1 */ + PSD1 |= CC; /* update the CC's in the PSD */ + + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + } + break; + + case 0xC0>>2: /* 0xC0 SCC|SD|RM|ADR - SCC|SD|RM|ADR */ /* MPMx */ + if (reg & 1) { /* see if odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + if (FC == 2) { /* must not be double word adddress */ + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + goto newpsd; /* go execute the trap now */ + } + td = dest; + dest = GPR[reg+1]; /* get low order reg value */ + if (dest & MSIGN) + dest = (D32LMASK | dest); /* sign extend */ + dest = (t_uint64)((t_int64)dest * (t_int64)source); + dbl = 1; + break; + + case 0xC4>>2: /* 0xC4 RM|ADR - RM|ADR */ /* DVMx */ + if (reg & 1) { /* see if odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + if (FC == 2) { /* must not be double word adddress */ + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + goto newpsd; /* go execute the trap now */ + } + if (source == 0) + goto doovr; /* we have div by zero */ + dest = (((t_uint64)GPR[reg]) << 32); /* insert upper reg value */ + dest |= (t_uint64)GPR[reg+1]; /* get low order reg value */ + td = ((t_int64)dest % (t_int64)source); /* remainder */ + if (((td & DMSIGN) ^ (dest & DMSIGN)) != 0) /* Fix sign if needed */ + td = NEGATE32(td); /* dividend and remainder must be same sign */ + dest = (t_int64)dest / (t_int64)source; /* now do the divide */ + int64a = dest; + if (int64a < 0) + int64a = -int64a; + if (int64a > 0x7fffffff) /* if more than 31 bits, we have an error */ + goto doovr; + if (((dest & D32LMASK) != 0 && (dest & D32LMASK) != D32LMASK) || + (((dest & D32LMASK) == D32LMASK) && ((dest & D32RMASK) == 0))) { /* test for overflow */ +doovr: + dest = (((t_uint64)GPR[reg]) << 32);/* insert upper reg value */ + dest |= (t_uint64)GPR[reg+1]; /* get low order reg value */ + ovr = 1; /* the quotient exceeds 31 bit, overflow */ + /* the original regs must be returned unchanged if aexp */ + CC = CC1BIT; /* set ovr CC bit */ + if (dest == 0) + CC |= CC4BIT; /* dw is zero, so CC4 */ + else + if (dest & DMSIGN) + CC |= CC3BIT; /* it is neg dw, so CC3 */ + else + CC |= CC2BIT; /* then dest > 0, so CC2 */ + PSD1 &= 0x87FFFFFE; /* clear the old CC's from PSD1 */ + PSD1 |= CC; /* update the CC's in the PSD */ + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (MODES & AEXPBIT) + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + } else { + GPR[reg] = (uint32)(td & FMASK); /* reg gets remainder, reg+1 quotient */ + GPR[reg+1] = (uint32)(dest & FMASK); /* store quotient in reg+1 */ + set_CCs(GPR[reg+1], ovr); /* set the CC's, CC1 = ovr */ + } + break; + + case 0xC8>>2: /* 0xC8 IMM - IMM */ /* Immedate */ + temp = GPR[reg]; /* get reg contents */ + addr = IR & RMASK; /* sign extend 16 bit imm value from IR */ + if (addr & 0x8000) /* negative */ + addr |= LMASK; /* extend sign */ + + switch(opr & 0xF) { /* switch on aug code */ + case 0x0: /* LI */ /* SCC | SD */ + GPR[reg] = addr; /* put immediate value into reg */ + set_CCs(addr, ovr); /* set the CC's, CC1 = ovr */ + break; + + case 0x2: /* SUI */ + addr = NEGATE32(addr); /* just make value a negative add */ + /* drop through */ + case 0x1: /* ADI */ + t = (temp & FSIGN) != 0; /* set flag for sign bit not set in reg value */ + t |= ((addr & FSIGN) != 0) ? 2 : 0; /* ditto for the extended immediate value */ + temp = temp + addr; /* now add the numbers */ + /* if both signs are neg and result sign is positive, overflow */ + /* if both signs are pos and result sign is negative, overflow */ + if ((t == 3 && (temp & FSIGN) == 0) || + (t == 0 && (temp & FSIGN) != 0)) + ovr = 1; /* we have an overflow */ + GPR[reg] = temp; /* save the result */ + set_CCs(temp, ovr); /* set the CC's, CC1 = ovr */ + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + goto newpsd; /* go execute the trap now */ + } + break; + + case 0x3: /* MPI */ + if (reg & 1) { /* see if odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + /* change immediate value into a 64 bit value */ + source = ((t_uint64)(addr & FMASK)) | ((addr & FSIGN) ? D32LMASK : 0); + temp = GPR[reg+1]; /* get reg multiplier */ + dest = ((t_uint64)(temp & FMASK)) | ((temp & FSIGN) ? D32LMASK : 0); + dest = dest * source; /* do the multiply */ + i_flags |= (SD|SCC); /* save regs and set CC's */ + dbl = 1; /* double reg save */ + break; + + case 0x4: /* DVI */ + if (reg & 1) { /* see if odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + /* change immediate value into a 64 bit value */ + source = ((t_uint64)(addr & FMASK)) | ((addr & FSIGN) ? D32LMASK : 0); + if (source == 0) { + goto doovr2; + } + dest = (((t_uint64)GPR[reg]) << 32); /* get upper reg value */ + dest |= (t_uint64)GPR[reg+1]; /* insert low order reg value */ + td = ((t_int64)dest % (t_int64)source); /* remainder */ + /* fix double reg if neg remainder */ + if (((td & DMSIGN) ^ (dest & DMSIGN)) != 0) /* Fix sign if needed */ + td = NEGATE32(td); /* dividend and remainder must be same sign */ + dest = (t_int64)dest / (t_int64)source; /* now do the divide */ + int64a = dest; + if (int64a < 0) + int64a = -int64a; + if (int64a > 0x7fffffff) /* if more than 31 bits, we have an error */ + goto doovr2; + if ((dest & D32LMASK) != 0 && (dest & D32LMASK) != D32LMASK) { /* test for overflow */ +doovr2: + dest = (((t_uint64)GPR[reg]) << 32); /* get upper reg value */ + dest |= (t_uint64)GPR[reg+1]; /* insert low order reg value */ + ovr = 1; /* the quotient exceeds 31 bit, overflow */ + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (MODES & AEXPBIT) + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + /* the original regs must be returned unchanged if aexp */ + /* put reg values back in dest for CC test */ + CC = CC1BIT; /* set ovr CC bit */ + if (dest == 0) + CC |= CC4BIT; /* dw is zero, so CC4 */ + else + if (dest & DMSIGN) + CC |= CC3BIT; /* it is neg dw, so CC3 */ + else + CC |= CC2BIT; /* then dest > 0, so CC2 */ + PSD1 &= 0x87FFFFFE; /* clear the old CC's from PSD1 */ + PSD1 |= CC; /* update the CC's in the PSD */ + } else { + GPR[reg] = (uint32)(td & FMASK); /* reg gets remainder, reg+1 quotient */ + GPR[reg+1] = (uint32)(dest & FMASK); /* store quotient in reg+1 */ + set_CCs(GPR[reg+1], ovr); /* set the CC's, CC1 = ovr */ + } + break; + + case 0x5: /* CI */ /* SCC */ + temp = ((int)temp - (int)addr); /* subtract imm value from reg value */ + set_CCs(temp, ovr); /* set the CC's, CC1 = ovr */ + break; + +/* SVC instruction format C806 */ +/* |-------+-------+-------+-------+-------+-------+-------+-------| */ +/* |0 0 0 0 0 0|0 0 0|0 1 1|1 1 1 1|1 1 1 1|2 2 2 2 2 2 2 2 2 2 3 3| */ +/* |0 1 2 3 4 5|6 7 8|8 0 1|2 3 4 5|6 7 8 9|0 1 2 3 4 5 6 7 8 9 0 1| */ +/* | Op Code | N/U | N/U | Aug |SVC num| SVC Call Number | */ +/* |1 1 0 0 1 0|0 0 0|0 0 0|0 1 1 0|x x x x|x x x x x x x x x x x x| */ +/* |-------+-------+-------+-------+-------+-------+-------+-------| */ +/* */ + case 0x6: /* SVC none - none */ /* Supervisor Call Trap */ + { +#ifdef MPXTEST /* set to 1 for traceme in MPX to work */ + /* get current MPX task name */ + int j; + char n[9]; + uint32 sq59 = M[0x930>>2]; /* get C.SQ59 headcell */ + uint32 dqe = M[0x8e8>>2]; /* get DQE of current task */ + + sim_debug(DEBUG_IRQ, &cpu_dev, + "SVC start sq59 %04x dqe %04x\n",sq59, dqe); + if (sq59 != 0x930) + goto skipdqe2; /* not running on mpx, skip */ + for (j=0; j<8; j++) { /* get the task name */ + n[j] = (M[((dqe+0x18)>>2)+(j/4)] >> ((3-(j&7))*8)) & 0xff; + if (n[j] == 0) + n[j] = 0x20; + } + n[8] = 0; +skipdqe2: +#endif + int32c = CPUSTATUS; /* keep for retain blocking state */ + addr = SPAD[0xf0]; /* get trap table memory address from SPAD (def 80) */ + int32a = addr; + if (addr == 0 || ((addr&MASK24) == MASK24)) { /* see if secondary vector table set up */ + TRAPME = ADDRSPEC_TRAP; /* Not setup, error */ + goto newpsd; /* program error */ + } + addr = addr + (0x06 << 2); /* addr has mem addr of SVC trap vector (def 98) */ + temp = M[addr >> 2]; /* get the secondary trap table address from memory */ + if (temp == 0 || ((temp&MASK24) == MASK24)) { /* see if ICB set up */ + TRAPME = ADDRSPEC_TRAP; /* Not setup, error */ + goto newpsd; /* program error */ + } + temp2 = ((IR>>12) & 0x0f) << 2; /* get SVC index from IR */ + t = M[(temp+temp2)>>2]; /* get secondary trap vector address ICB address */ + if (temp == 0 || ((temp&MASK24) == MASK24)) { /* see if ICB set up */ + TRAPME = ADDRSPEC_TRAP; /* Not setup, error */ + goto newpsd; /* program error */ + } + bc = PSD2 & 0x3ff8; /* get copy of cpix */ + M[t>>2] = (PSD1+4) & 0xfffffffe; /* store PSD 1 + 1W to point to next instruction */ + M[(t>>2)+1] = PSD2; /* store PSD 2 */ + PSD1 = M[(t>>2)+2]; /* get new PSD 1 */ + PSD2 = (M[(t>>2)+3] & ~0x3ff8) | bc; /* get new PSD 2 w/old cpix */ + M[(t>>2)+4] = IR&0xFFF; /* store call number */ +#ifdef MPXTEST /* set to 1 for traceme to work */ + if (sq59 == 0x930) { /* running on MPX? */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "SVC %x,%x @ %.8x PSD %.8x %.8x SPAD PSD2 %x C.CURR %x LMN %8s\n", + temp2>>2, IR&0xFFF, OPSD1, PSD1, PSD2, SPAD[0xf5], dqe, n); + sim_debug(DEBUG_IRQ, &cpu_dev, + " R0=%.8x R1=%.8x R2=%.8x R3=%.8x\n", GPR[0], GPR[1], GPR[2], GPR[3]); + sim_debug(DEBUG_IRQ, &cpu_dev, + " R4=%.8x R5=%.8x R6=%.8x R7=%.8x\n", GPR[4], GPR[5], GPR[6], GPR[7]); + } +#if DYNAMIC_DEBUG + if (((temp2>>2) == 1) && ((IR&0xFFF) == 0x03f)) { /* SVC 1,3f */ + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ + } +#endif +#else + sim_debug(DEBUG_IRQ, &cpu_dev, + "SVC %x,%x @ %.8x PSD %.8x %.8x SPADF5 PSD2 %x CPUSTATUS %08x\n", + temp2>>2, IR&0xFFF, OPSD1, PSD1, PSD2, SPAD[0xf5], CPUSTATUS); + sim_debug(DEBUG_IRQ, &cpu_dev, + " R0=%.8x R1=%.8x R2=%.8x R3=%.8x\n", GPR[0], GPR[1], GPR[2], GPR[3]); + sim_debug(DEBUG_IRQ, &cpu_dev, + " R4=%.8x R5=%.8x R6=%.8x R7=%.8x\n", GPR[4], GPR[5], GPR[6], GPR[7]); +#endif +#if DYNAMIC_DEBUG + if (((temp2>>2) == 0) && ((IR&0xFFF) == 0xb01)) { /* SVC 0,VOMM,1 */ + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ + } +#endif +#ifdef DO_DYNAMIC_DEBUG + if (((temp2>>2) == 0) && ((IR&0xFFF) == 0x303)) { /* SVC 0,TAMM,1 */ + if (GPR[3] == 0x3a000) + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ + } +#endif +#ifdef DO_DYNAMIC_DEBUG + if (((temp2>>2) == 2) && ((IR&0xFFF) == 0x028)) { /* SVC 2,28 H.VOMM,9 */ + if (cpu_dev.dctrl & DEBUG_INST) + cpu_dev.dctrl &= ~DEBUG_INST; /* stop instruction trace */ + else + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ + } +#endif +#ifdef DO_DYNAMIC_DEBUG + if (((temp2>>2) == 0) && ((IR&0xFFF) == 0xa11)) { /* SVC 0,REMM,17 */ + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ + } +#endif +#ifdef DO_DYNAMIC_DEBUG + if (((temp2>>2) == 0) && ((IR&0xFFF) == 0x910)) { /* SVC 0,REXS,16 */ + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ + } + if (((temp2>>2) == 0) && ((IR&0xFFF) == 0x925)) { /* SVC 0,REXS,40 */ + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ + } +#endif + /* set the mode bits and CCs from the new PSD */ + CC = PSD1 & 0x78000000; /* extract bits 1-4 from PSD1 */ + MODES = PSD1 & 0x87000000; /* extract bits 0, 5, 6, 7 from PSD 1 */ + CPUSTATUS &= ~0x87000000; /* reset bits in CPUSTATUS */ + CPUSTATUS |= MODES; /* now insert into CPUSTATUS */ + + /* set new map mode and interrupt blocking state in CPUSTATUS */ + if (PSD2 & MAPBIT) { + CPUSTATUS |= BIT8; /* set bit 8 of cpu status */ + MODES |= MAPMODE; /* set mapped mode */ + } else { + CPUSTATUS &= ~BIT8; /* reset bit 8 of cpu status */ +/*TRY_01072022*/ MODES &= ~MAPMODE; /* reset mapped mode */ + } + + /* set interrupt blocking state */ + if ((PSD2 & RETBBIT) == 0) { /* is it retain blocking state */ + if (PSD2 & SETBBIT) { /* no, is it set blocking state */ + CPUSTATUS |= BIT24; /* yes, set blk state in cpu status bit 24 */ + MODES |= BLKMODE; /* set blocked mode */ + } else { + CPUSTATUS &= ~BIT24; /* no, reset blk state in cpu status bit 24 */ + MODES &= ~BLKMODE; /* reset blocked mode */ + irq_pend = 1; /* start scanning interrupts again */ +#ifdef LEAVE_ACTIVE + if (irq_auto) { +/*AIR*/ INTS[irq_auto] &= ~INTS_ACT; /* deactivate specified int level */ +/*AIR*/ SPAD[irq_auto+0x80] &= ~SINT_ACT; /* deactivate in SPAD too */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "<|>IntX deactivate level %02x at SVC #%2x PSD1 %08x\n", + irq_auto, temp2, PSD1); +/*AIR*/ irq_auto = 0; /* show done processing in blocked mode */ + } +#endif + } + } else { + /* handle retain blocking state */ + PSD2 &= ~RETMBIT; /* turn off retain bit in PSD2 */ + /* set new blocking state in PSD2 */ + PSD2 &= ~(SETBBIT|RETBBIT); /* clear bit 48 & 49 to be unblocked */ + MODES &= ~(BLKMODE|RETBLKM); /* reset blocked & retain mode bits */ + if (int32c & BIT24) { /* see if old mode is blocked */ + PSD2 |= SETBBIT; /* set to blocked state */ + MODES |= BLKMODE; /* set blocked mode */ + } + } + + SPAD[0xf5] = PSD2; /* save the current PSD2 */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ + TRAPME = 0; /* not to be processed as trap */ + goto newpsd; /* new psd loaded */ + } + break; + + case 0x7: /* EXR */ + IR = temp; /* get instruction to execute */ + /* if bit 30 set, instruction is in right hw, do EXRR */ + if (addr & 2) + IR <<= 16; /* move instruction to left HW */ +#ifdef DIAG_SAYS_OK_TO_EXECUTE_ANOTHER_EXECUTE + /* 32/67 diag says execute of execute is OK */ + if ((IR & 0xFC7F0000) == 0xC8070000 || + (IR & 0xFF800000) == 0xA8000000) { + /* Fault, attempt to execute another EXR, EXRR, or EXM */ + goto inv; /* invalid instruction */ + } +#endif + EXM_EXR = 4; /* set PC increment for EXR */ + OPSD1 &= 0x87FFFFFE; /* clear the old CC's */ + OPSD1 |= PSD1 & 0x78000000; /* update the CC's in the PSD */ + /* TODO Update other history information for this instruction */ + if (hst_lnt) { + hst[hst_p].opsd1 = OPSD1; /* update the CC in opsd1 */ + hst[hst_p].npsd1 = PSD1; /* save new psd1 */ + hst[hst_p].npsd2 = PSD2; /* save new psd2 */ + hst[hst_p].modes = MODES; /* save current mode bits */ + hst[hst_p].modes |= (CPUSTATUS & BIT24); /* save blocking mode bit */ + for (ix=0; ix<8; ix++) { + hst[hst_p].reg[ix] = GPR[ix]; /* save reg */ + hst[hst_p].reg[ix+8] = BR[ix]; /* save breg */ + } + } + /* DEBUG_INST support code */ + OPSD1 &= 0x87FFFFFE; /* clear the old CC's */ + OPSD1 |= PSD1 & 0x78000000; /* update the CC's in the PSD */ + /* output mapped/unmapped */ + if (MODES & BASEBIT) + BM = 'B'; + else + BM = 'N'; + if (MODES & MAPMODE) + MM = 'M'; + else + MM = 'U'; + if (CPUSTATUS & BIT24) + BK = 'B'; + else + BK = 'U'; + sim_debug(DEBUG_INST, &cpu_dev, "%c%c%c %.8x %.8x %.8x ", + BM, MM, BK, OPSD1, PSD2, OIR); + if (cpu_dev.dctrl & DEBUG_INST) { + fprint_inst(sim_deb, OIR, 0); /* display instruction */ + sim_debug(DEBUG_INST, &cpu_dev, + "\n\tR0=%.8x R1=%.8x R2=%.8x R3=%.8x", GPR[0], GPR[1], GPR[2], GPR[3]); + sim_debug(DEBUG_INST, &cpu_dev, + " R4=%.8x R5=%.8x R6=%.8x R7=%.8x\n", GPR[4], GPR[5], GPR[6], GPR[7]); + if (MODES & BASEBIT) { + sim_debug(DEBUG_INST, &cpu_dev, + "\tB0=%.8x B1=%.8x B2=%.8x B3=%.8x", BR[0], BR[1], BR[2], BR[3]); + sim_debug(DEBUG_INST, &cpu_dev, + " B4=%.8x B5=%.8x B6=%.8x B7=%.8x\n", BR[4], BR[5], BR[6], BR[7]); + } + } + goto exec; /* go execute the instruction */ + break; + + /* these instruction were never used by MPX, only diags */ + /* diags treat them as invalid halfword instructions */ + /* so set the HLF flag to get proper PC increment */ + case 0x8: /* SEM */ + case 0x9: /* LEM */ + case 0xA: /* CEMA */ + case 0xB: /* INV */ + case 0xC: /* INV */ + case 0xD: /* INV */ + case 0xE: /* INV */ + case 0xF: /* INV */ + default: + goto inv; /* invalid instruction */ + break; + } + break; + + case 0xCC>>2: /* 0xCC ADR - ADR */ /* LF */ + /* For machines with Base mode 0xCC08 stores base registers */ + if ((FC & 3) != 0) { /* must be word address */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + temp = addr & 0xffe000; /* get 11 bit map # */ + bc = addr & 0x20; /* bit 26 initial value */ + while (reg < 8) { + if (bc != (addr & 0x20)) { /* test for crossing file boundry */ + if (CPU_MODEL < MODEL_27) { + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + } + if (temp != (addr & 0xffe000)) { /* test for crossing map boundry */ + if (CPU_MODEL >= MODEL_V6) { + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + } + if (FC & 0x4) /* LFBR? 0xCC08 */ + TRAPME = Mem_read(addr, &BR[reg]); /* read the base reg */ + else /* LF? 0xCC00 */ + TRAPME = Mem_read(addr, &GPR[reg]); /* read the GPR reg */ + if (TRAPME) /* TRAPME has error */ + goto newpsd; /* go execute the trap now */ + reg++; /* next reg to write */ + addr += 4; /* next addr */ + } + break; + + case 0xD0>>2: /* 0xD0 SD|ADR - INV */ /* LEA none basemode only */ + if (MODES & BASEBIT) + goto inv; /* invalid instruction in basemode */ + /* bc has last bits 0,1 for indirect addr of both 1 for no indirection */ + addr &= 0x3fffffff; /* clear bits 0-1 */ + addr |= bc; /* insert bits 0,1 values into address */ + if (FC & 0x4) + addr |= F_BIT; /* copy F bit from instruction */ + dest = (t_uint64)(addr); + break; + + case 0xD4>>2: /* 0xD4 RR|SM|ADR - RR|SM|ADR */ /* STx */ + break; + + case 0xD8>>2: /* 0xD8 RR|SM|ADR - RR|SM|ADR */ /* STMx */ + /* STMD needs both regs to be masked with R4 */ + if (dbl) { + /* we need to and both regs */ + t_uint64 nm = (((t_uint64)GPR[4]) << 32) | (((t_uint64)GPR[4]) & D32RMASK); + dest &= nm; /* mask both regs with reg 4 contents */ + } else { + dest &= (((t_uint64)GPR[4]) & D32RMASK); /* mask with reg 4 contents */ + } + break; + + case 0xDC>>2: /* 0xDC INV - ADR */ /* INV nonbasemode (STFx basemode) */ + /* DC00 STF */ /* DC08 STFBR */ + if ((FC & 0x4) && (CPU_MODEL <= MODEL_27)) { + /* basemode undefined for 32/7x & 32/27 */ + TRAPME = UNDEFINSTR_TRAP; /* Undefined Instruction Trap */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* handle trap */ + } + /* For machines with Base mode 0xDC08 stores base registers */ + if ((FC & 3) != 0) { /* must be word address */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + bc = addr & 0x20; /* bit 26 initial value */ + temp = addr & 0xffe000; /* get 11 bit map # */ + while (reg < 8) { + if (bc != (addr & 0x20)) { /* test for crossing file boundry */ + if (CPU_MODEL < MODEL_27) { + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + } + if (temp != (addr & 0xffe000)) { /* test for crossing map boundry */ + if (CPU_MODEL >= MODEL_V6) { + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + } + if (FC & 0x4) /* STFBR? */ + TRAPME = Mem_write(addr, &BR[reg]); /* store the base reg */ + else /* STF */ + TRAPME = Mem_write(addr, &GPR[reg]); /* store the GPR reg */ + if (TRAPME) /* TRAPME has error */ + goto newpsd; /* go execute the trap now */ + reg++; /* next reg to write */ + addr += 4; /* next addr */ + } + break; + + case 0xE0>>2: /* 0xE0 ADR - ADR */ /* ADFx, SUFx */ + if ((TRAPME = Mem_read(addr, &temp))) { /* get the word from memory */ + goto newpsd; /* memory read error or map fault */ + } + source = (t_uint64)temp; /* make into 64 bit value */ + if (FC & 2) { /* see if double word addr */ + if ((TRAPME = Mem_read(addr+4, &temp))) { /* get the 2nd word from memory */ + goto newpsd; /* memory read error or map fault */ + } + source = (source << 32) | (t_uint64)temp; /* merge in the low order 32 bits */ + dbl = 1; /* double word instruction */ + } else { + source |= (source & MSIGN) ? D32LMASK : 0; + dbl = 0; /* not double wd */ + } + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + CC = 0; /* clear the CC'ss */ + /* handle float or double add/sub instructions */ + if (dbl == 0) { + /* do ADFW or SUFW instructions */ + temp2 = GPR[reg]; /* dest - reg contents specified by Rd */ + addr = (uint32)(source & D32RMASK); /* get 32 bits from source memory */ + if ((opr & 8) == 0) { /* Was it SUFW? */ + addr = NEGATE32(addr); /* take negative for add */ + } + temp = s_adfw(temp2, addr, &CC); /* do ADFW */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "%s GPR[%d] %08x addr %08x result %08x CC %08x\n", + (opr&8) ? "ADFW":"SUFW", reg, GPR[reg], addr, temp, CC); + ovr = 0; + if (CC & CC1BIT) + ovr = 1; + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + /* check if we had an arithmetic exception on the last instruction*/ + if (ovr && (MODES & AEXPBIT)) { + /* leave regs unchanged */ + TRAPME = AEXPCEPT_TRAP; /* trap the system now */ + goto newpsd; /* process the trap */ + } + /* AEXP not enabled, so apply fix here */ + /* return temp to destination reg */ + GPR[reg] = temp; /* dest - reg contents specified by Rd */ + } else { + /* handle ADFD or SUFD */ + if (reg & 1) { /* see if odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + /* do ADFD or SUFD instructions */ + td = (((t_uint64)GPR[reg]) << 32); /* get upper reg value */ + td |= (t_uint64)GPR[reg+1]; /* insert low order reg value */ + /* source has 64 bit memory data */ + if ((opr & 8) == 0) { /* Was it SUFD? */ + source = NEGATE32(source); /* make negative for subtract */ + } + dest = s_adfd(td, source, &CC); /* do ADFD */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "%s GPR[%d] %08x %08x src %016llx result %016llx CC %08x\n", + (opr&8) ? "ADFD":"SUFD", reg, GPR[reg], GPR[reg+1], source, dest, CC); + ovr = 0; + if (CC & CC1BIT) /* test for overflow detection */ + ovr = 1; + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + /* check if we had an arithmetic exception on the last instruction */ + if (ovr && (MODES & AEXPBIT)) { + /* leave regs unchanged */ + TRAPME = AEXPCEPT_TRAP; /* trap the system now */ + goto newpsd; /* process the trap */ + } + /* dest will be returned to destination regs */ + /* if AEXP not enabled, apply fix here */ + /* return dest to destination reg */ + GPR[reg] = (uint32)((dest & D32LMASK) >> 32); /* get upper reg value */ + GPR[reg+1] = (uint32)(dest & D32RMASK); /* get lower reg value */ + } + break; + + case 0xE4>>2: /* 0xE4 ADR - ADR */ /* MPFx, DVFx */ + if ((TRAPME = Mem_read(addr, &temp))) { /* get the word from memory */ + goto newpsd; /* memory read error or map fault */ + } + source = (t_uint64)temp; /* make into 64 bit value */ + if (FC & 2) { /* see if double word addr */ + if ((TRAPME = Mem_read(addr+4, &temp))) { /* get the 2nd word from memory */ + goto newpsd; /* memory read error or map fault */ + } + source = (source << 32) | (t_uint64)temp; /* merge in the low order 32 bits */ + dbl = 1; /* double word instruction */ + } else { + source |= (source & MSIGN) ? D32LMASK : 0; + dbl = 0; /* not double wd */ + } + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + CC = 0; /* clear the CC'ss */ + /* handle float or double mul/div instructions */ + if (dbl == 0) { + /* do MPFW or DVFW instructions */ + temp2 = GPR[reg]; /* dest - reg contents specified by Rd */ + addr = (uint32)(source & D32RMASK); /* get 32 bits from source memory */ + if (opr & 8) { /* Was it MPFW? */ + temp = s_mpfw(temp2, addr, &CC); /* do MPFW */ + } else { + temp = (uint32)s_dvfw(temp2, addr, &CC); /* do DVFW */ + } + sim_debug(DEBUG_DETAIL, &cpu_dev, + "%s GPR[%d] %08x addr %08x result %08x\n", + (opr&8) ? "MPFW":"DVFW", reg, GPR[reg], addr, temp); + if (CC & CC1BIT) + ovr = 1; + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + /* check if we had an arithmetic exception on the last instruction*/ + if (ovr && (MODES & AEXPBIT)) { + /* leave regs unchanged */ + TRAPME = AEXPCEPT_TRAP; /* trap the system now */ + goto newpsd; /* process the trap */ + } + /* if AEXP not enabled, apply fix here */ + /* return temp to destination reg */ + GPR[reg] = temp; /* dest - reg contents specified by Rd */ + } else { + /* handle MPFD or DVFD */ + if (reg & 1) { /* see if odd reg specified */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + goto newpsd; /* go execute the trap now */ + } + /* do MPFD or DVFD instructions */ + td = (((t_uint64)GPR[reg]) << 32); /* get upper reg value */ + td |= (t_uint64)GPR[reg+1]; /* insert low order reg value */ + /* source has 64 bit memory data */ + if (opr & 8) { /* Was it MPFD? */ + dest = s_mpfd(td, source, &CC); /* do MPFD */ + } else { + dest = s_dvfd(td, source, &CC); /* do DVFD */ + } + sim_debug(DEBUG_DETAIL, &cpu_dev, + "%s GPR[%d] %08x %08x src %016llx result %016llx\n", + (opr&8) ? "MPFD":"DVFD", reg, GPR[reg], GPR[reg+1], source, dest); + if (CC & CC1BIT) /* test for overflow detection */ + ovr = 1; + PSD1 |= (CC & 0x78000000); /* update the CC's in the PSD */ + /* check if we had an arithmetic exception on the last instruction*/ + if (ovr && (MODES & AEXPBIT)) { + /* leave regs unchanged */ + TRAPME = AEXPCEPT_TRAP; /* trap the system now */ + goto newpsd; /* process the trap */ + } + /* dest will be returned to destination regs */ + /* if AEXP not enabled, apply fix here */ + /* return dest to destination reg */ + GPR[reg] = (uint32)((dest & D32LMASK) >> 32); /* get upper reg value */ + GPR[reg+1] = (uint32)(dest & D32RMASK); /* get lower reg value */ + } + break; + + case 0xE8>>2: /* 0xE8 SM|RR|RNX|ADR - SM|RM|ADR */ /* ARMx */ + ovr = 0; + CC = 0; + switch(FC) { /* adjust for hw or bytes */ + case 4: case 5: case 6: case 7: /* byte address */ + /* ARMB */ + td = dest + source; /* DO ARMB */ + td &= 0xff; /* mask out right most byte */ + dest &= 0xffffff00; /* make place for byte */ + dest |= td; /* insert result into dest */ + if (td == 0) + CC |= CC4BIT; /* byte is zero, so CC4 */ + break; + case 1: /* left halfword addr */ + case 3: /* right halfword addr */ + /* ARMH */ + td = dest + source; /* DO ARMH */ + td &= RMASK; /* mask out right most 16 bits */ + dest &= LMASK; /* make place for halfword */ + dest |= td; /* insert result into dest */ + if (td == 0) + CC |= CC4BIT; /* hw is zero, so CC4 */ + break; + case 0: /* 32 bit word */ + /* ARMW */ + /* dest and source are really 32 bit values */ + t = (source & MSIGN) != 0; + t |= ((dest & MSIGN) != 0) ? 2 : 0; + td = dest + source; /* DO ARMW */ + td &= D32RMASK; /* mask out right most 32 bits */ + dest = 0; /* make place for 64 bits */ + dest |= td; /* insert result into dest */ + /* if both signs are neg and result sign is positive, overflow */ + /* if both signs are pos and result sign is negative, overflow */ + if (((t == 3) && ((dest & MSIGN) == 0)) || + ((t == 0) && ((dest & MSIGN) != 0))) + ovr = 1; + if (dest & MSIGN) + dest = (D32LMASK | dest); /* sign extend */ + else + dest = (D32RMASK & dest); /* zero fill */ + if (td == 0) + CC |= CC4BIT; /* word is zero, so CC4 */ + else { + if (td & 0x80000000) + CC |= CC3BIT; /* it is neg wd, so CC3 */ + else + CC |= CC2BIT; /* then td > 0, so CC2 */ + } + break; + case 2: /* 64 bit double */ + /* ARMD */ + t = (source & DMSIGN) != 0; + t |= ((dest & DMSIGN) != 0) ? 2 : 0; + td = dest + source; /* DO ARMD */ + dest = td; /* insert result into dest */ + /* if both signs are neg and result sign is positive, overflow */ + /* if both signs are pos and result sign is negative, overflow */ + if (((t == 3) && ((dest & DMSIGN) == 0)) || + ((t == 0) && ((dest & DMSIGN) != 0))) + ovr = 1; + if (td == 0) + CC |= CC4BIT; /* dw is zero, so CC4 */ + else { + if (td & DMSIGN) + CC |= CC3BIT; /* it is neg dw, so CC3 */ + else + CC |= CC2BIT; /* then td > 0, so CC2 */ + } + break; + } + if (ovr) + CC |= CC1BIT; /* set overflow CC */ + PSD1 &= 0x87FFFFFE; /* clear the old CC's from PSD1 */ + PSD1 |= CC; /* update the CC's in the PSD */ + /* the arithmetic exception will be handled */ + /* after instruction is completed */ + /* check for arithmetic exception trap enabled */ + if (ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* set the trap type */ + } + break; + + case 0xEC>>2: /* 0xEC ADR - ADR */ /* Branch unconditional or Branch True */ + /* GOOF alert, the assembler sets bit 31 to 1 so this test will fail*/ + /* so just test for F bit and go on */ + /* if ((FC & 5) != 0) { */ + if ((FC & 4) != 0) { + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "ADDRSPEC10 OP %04x addr %08x\n", OP, addr); + goto newpsd; /* go execute the trap now */ + } + temp2 = CC; /* save the old CC's */ + CC = PSD1 & 0x78000000; /* get CC's if any */ + switch(reg) { + case 0: t = 1; break; + case 1: t = (CC & CC1BIT) != 0; break; + case 2: t = (CC & CC2BIT) != 0; break; + case 3: t = (CC & CC3BIT) != 0; break; + case 4: t = (CC & CC4BIT) != 0; break; + case 5: t = (CC & (CC2BIT|CC4BIT)) != 0; break; + case 6: t = (CC & (CC3BIT|CC4BIT)) != 0; break; + case 7: t = (CC & (CC1BIT|CC2BIT|CC3BIT|CC4BIT)) != 0; break; + } + if (t) { /* see if we are going to branch */ + /* we are taking the branch, set CC's if indirect, else leave'm */ + /* update the PSD with new address */ + PSD1 = (PSD1 & 0xff000000) | (addr & 0xfffffe); /* set new PC */ + i_flags |= BT; /* we branched, so no PC update */ + if (((MODES & BASEBIT) == 0) && (IR & IND)) /* see if CCs from last indirect wanted */ + PSD1 = (PSD1 & 0x87fffffe) | temp2; /* insert last indirect CCs */ +/*FIX F77*/ if ((MODES & (BASEBIT|EXTDBIT)) == 0) /* see if basemode */ +/*FIX F77*/ PSD1 &= 0xff07ffff; /* only 19 bit address allowed */ + } + /* branch not taken, go do next instruction */ + break; + + case 0xF0>>2: /* 0xF0 ADR - ADR */ /* Branch False or Branch Function True BFT */ + /* GOOF alert, the assembler sets bit 31 to 1 so this test will fail*/ + /* so just test for F bit and go on */ + /* if ((FC & 5) != 0) { */ + if ((FC & 4) != 0) { + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "ADDRSPEC11 OP %04x addr %08x\n", OP, addr); + goto newpsd; /* go execute the trap now */ + } + temp2 = CC; /* save the old CC's */ + CC = PSD1 & 0x78000000; /* get CC's if any */ + switch(reg) { + case 0: t = (GPR[4] & (0x8000 >> ((CC >> 27) & 0xf))) != 0; break; + case 1: t = (CC & CC1BIT) == 0; break; + case 2: t = (CC & CC2BIT) == 0; break; + case 3: t = (CC & CC3BIT) == 0; break; + case 4: t = (CC & CC4BIT) == 0; break; + case 5: t = (CC & (CC2BIT|CC4BIT)) == 0; break; + case 6: t = (CC & (CC3BIT|CC4BIT)) == 0; break; + case 7: t = (CC & (CC1BIT|CC2BIT|CC3BIT|CC4BIT)) == 0; break; + } + if (t) { /* see if we are going to branch */ + /* we are taking the branch, set CC's if indirect, else leave'm */ + /* update the PSD with new address */ + PSD1 = (PSD1 & 0xff000000) | (addr & 0xfffffe); /* set new PC */ + i_flags |= BT; /* we branched, so no PC update */ + if (((MODES & BASEBIT) == 0) && (IR & IND)) /* see if CCs from last indirect wanted */ + PSD1 = (PSD1 & 0x87fffffe) | temp2; /* insert last indirect CCs */ +/*FIX F77*/ if ((MODES & (BASEBIT|EXTDBIT)) == 0) /* see if basemode */ +/*FIX F77*/ PSD1 &= 0xff07ffff; /* only 19 bit address allowed */ + } + break; + + case 0xF4>>2: /* 0xF4 RR|SD|ADR - RR|SB|WRD */ /* Branch increment */ + dest += ((t_uint64)1) << ((IR >> 21) & 3); /* use bits 9 & 10 to incr reg */ + if (dest != 0) { /* if reg is not 0, take the branch */ + /* we are taking the branch, set CC's if indirect, else leave'm */ + /* update the PSD with new address */ + +#if 0 /* set #if to 1 to stop branch to self while tracing, for now */ + if (PC == (addr & 0xFFFFFC)) { /* BIB to current PC, bump branch addr */ + addr += 4; +// fprintf(stderr, "BI? stopping BIB $ addr %x PC %x\r\n", addr, PC); + dest = 0; /* force reg to zero */ + } +#endif + PSD1 = (PSD1 & 0xff000000) | (addr & 0xfffffe); /* set new PC */ + if (((MODES & BASEBIT) == 0) && (IR & IND)) /* see if CCs from last indirect wanted */ + PSD1 = (PSD1 & 0x87fffffe) | CC; /* insert last CCs */ + i_flags |= BT; /* we branched, so no PC update */ +/*FIX F77*/ if ((MODES & (BASEBIT|EXTDBIT)) == 0) /* see if basemode */ +/*FIX F77*/ PSD1 &= 0xff07ffff; /* only 19 bit address allowed */ + } + break; + + case 0xF8>>2: /* 0xF8 SM|ADR - SM|ADR */ /* ZMx, BL, BRI, LPSD, LPSDCM, TPR, TRP */ + switch((opr >> 7) & 0x7) { /* use bits 6-8 to determine instruction */ + case 0x0: /* ZMx F80x */ /* SM */ + dest = 0; /* destination value is zero */ + i_flags |= SM; /* SM not set so set it to store value */ + break; + case 0x1: /* BL F880 */ + /* copy CC's from instruction and PC incremented by 4 */ + GPR[0] = ((PSD1 & 0xff000000) | ((PSD1 + 4) & 0xfffffe)); + if (((MODES & BASEBIT) == 0) && (IR & IND)) /* see if CCs from last indirect wanted */ + PSD1 = (PSD1 & 0x87fffffe) | CC; /* insert last CCs */ + /* update the PSD with new address */ + if (MODES & BASEBIT) + PSD1 = (PSD1 & 0xff000000) | (addr & 0xfffffe); /* bit 8-30 */ + else + PSD1 = (PSD1 & 0xff000000) | (addr & 0x07fffe); /* bit 13-30 */ + i_flags |= BT; /* we branched, so no PC update */ +/*FIX F77*/ if ((MODES & (BASEBIT|EXTDBIT)) == 0) /* see if basemode */ +/*FIX F77*/ PSD1 &= 0xff07ffff; /* only 19 bit address allowed */ + break; + + case 0x3: /* LPSD F980 */ + /* fall through */; + case 0x5: /* LPSDCM FA80 */ + irq_pend = 1; /* start scanning interrupts again */ + if ((MODES & PRIVBIT) == 0) { /* must be privileged */ + TRAPME = PRIVVIOL_TRAP; /* set the trap to take */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + else + TRAPSTATUS |= BIT19; /* set bit 19 of trap status */ + goto newpsd; /* Privlege violation trap */ + } + CPUSTATUS |= BIT25; /* enable software traps */ + /* this will allow attn and */ + /* power fail traps */ + if ((FC & 04) != 0 || FC == 2) { /* can not be byte or doubleword */ + /* Fault */ + TRAPME = ADDRSPEC_TRAP; /* bad reg address, error */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "ADDRSPEC12 OP %04x addr %08x\n", OP, addr); + goto newpsd; /* go execute the trap now */ + } + if ((TRAPME = Mem_read(addr, &temp))) { /* get PSD1 from memory */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) { + TRAPSTATUS |= BIT10; /* set bit 10 of trap status */ + TRAPSTATUS |= BIT7; /* set bit 7 of trap status */ + } else + TRAPSTATUS |= BIT18; /* set bit 18 of trap status */ + goto newpsd; /* memory read error or map fault */ + } + bc = CPUSTATUS; /* save the CPU STATUS */ + TPSD[0] = PSD1; /* save the PSD for the instruction */ + TPSD[1] = PSD2; + t = MODES; /* save modes too */ + ix = SPAD[0xf5]; /* save the current PSD2 */ + reg = irq_pend; /* save intr status */ + + if ((TRAPME = Mem_read(addr+4, &temp2))) { /* get PSD2 from memory */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) { + TRAPSTATUS |= BIT10; /* set bit 10 of trap status */ + TRAPSTATUS |= BIT7; /* set bit 7 of trap status */ + } else + TRAPSTATUS |= BIT18; /* set bit 18 of trap status */ + goto newpsd; /* memory read error or map fault */ + } + if (opr & 0x0200) { /* Was it LPSDCM? */ + /* LPSDCM */ + PSD2 = temp2 & 0xfffffff8; /* PSD2 access good, clean & save it */ + } else { + /* LPSD */ + /* lpsd can not change cpix, so keep it */ + PSD2 = ((PSD2 & 0x3ff8) | (temp2 & 0xffffc000)); /* use current cpix */ + } + PSD1 = temp; /* PSD1 good, so set it */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "LPSD(CM) load [%06x] New PSD1 %08x %08x OPSD2 %08x SPAD %08x CPUSTATUS %08x\n", + addr, PSD1, PSD2, TPSD[1], ix, CPUSTATUS); +#ifdef MPXTEST + for (ii=0; ii<8; ii+=4) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "LPSD(CM) GPR[%d] %.8x GPR[%d] %.8x GPR[%d] %.8x GPR[%d] %.8x\n", + ii, GPR[ii], ii+1, GPR[ii+1], ii+2, GPR[ii+2], ii+3, GPR[ii+3]); + } + /* DYNAMIC 05282021 */ +#endif + /* set the mode bits and CCs from the new PSD */ + CC = PSD1 & 0x78000000; /* extract bits 1-4 from PSD1 */ + MODES = PSD1 & 0x87000000; /* extract bits 0, 5, 6, 7 from PSD 1 */ + CPUSTATUS &= ~0x87000000; /* reset bits in CPUSTATUS */ + CPUSTATUS |= MODES; /* now insert into CPUSTATUS */ + + /* set new map mode and interrupt blocking state in CPUSTATUS */ + if (PSD2 & MAPBIT) { + CPUSTATUS |= BIT8; /* set bit 8 of cpu status */ + MODES |= MAPMODE; /* set mapped mode */ + } else { + CPUSTATUS &= ~BIT8; /* reset bit 8 of cpu status */ +/*TRY_01072022*/ MODES &= ~MAPMODE; /* reset mapped mode */ + } + + /* set interrupt blocking state */ + if ((PSD2 & RETBBIT) == 0) { /* is it retain blocking state */ + if (PSD2 & SETBBIT) { /* no, is it set blocking state */ + CPUSTATUS |= BIT24; /* yes, set blk state in cpu status bit 24 */ + MODES |= BLKMODE; /* set blocked mode */ + } else { + CPUSTATUS &= ~BIT24; /* no, reset blk state in cpu status bit 24 */ + MODES &= ~BLKMODE; /* reset blocked mode */ + irq_pend = 1; /* start scanning interrupts again */ +#ifdef LEAVE_ACTIVE + if (irq_auto) { +/*AIR*/ INTS[irq_auto] &= ~INTS_ACT; /* deactivate specified int level */ +/*AIR*/ SPAD[irq_auto+0x80] &= ~SINT_ACT; /* deactivate in SPAD too */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "<|>IntX deactivate level %02x at LPSD(CM) %08x %08x R[3] %08x\n", + irq_auto, PSD1, PSD2, GPR[3]); +/*AIR*/ irq_auto = 0; /* show done processing in blocked mode */ +#ifdef DYNAMIC_DEBUG_01172021 + cpu_dev.dctrl &= ~DEBUG_INST; /* end instruction trace */ +#endif + } +#endif + } + } else { + /* set new blocking state in PSD2 */ + PSD2 &= ~(SETBBIT|RETBBIT); /* clear bit 48 & 49 to be unblocked */ + MODES &= ~(BLKMODE|RETBLKM); /* reset blocked & retain mode bits */ + if (bc & BIT24) { /* see if old mode is blocked */ + PSD2 |= SETBBIT; /* set to blocked state */ + MODES |= BLKMODE; /* set blocked mode */ + } + } + +#ifdef MPXTEST /* set to 1 for traceme to work */ + /* get current MPX task name */ + { + int j; + char n[9]; + uint32 sq59 = M[0x930>>2]; /* get C.SQ59 headcell */ + uint32 dqe = M[0x8e8>>2]; /* get DQE of current task */ + if (sq59 != 0x930) + goto skipdqe; /* not running on mpx, skip */ + for (j=0; j<8; j++) { /* get the task name */ + n[j] = (M[((dqe+0x18)>>2)+(j/4)] >> ((3-(j&7))*8)) & 0xff; + if (n[j] == 0) + n[j] = 0x20; + } + n[8] = 0; +#if 0 +#if DYNAMIC_DEBUG + if (dqe == 0x56e0) { + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ + } +#endif +#endif + if (opr & 0x0200) { /* Was it LPSDCM? */ +sim_debug(DEBUG_IRQ, &cpu_dev, + "LPSDCM OPSD %.8x %.8x NPSD %.8x %.8x SPDF5 %.8x DQE %x LMN %8s\n", + TPSD[0], TPSD[1], PSD1, PSD2, SPAD[0xf5], dqe, n); +sim_debug(DEBUG_IRQ, &cpu_dev, + " R0=%.8x R1=%.8x R2=%.8x R3=%.8x\n", GPR[0], GPR[1], GPR[2], GPR[3]); +sim_debug(DEBUG_IRQ, &cpu_dev, + " R4=%.8x R5=%.8x R6=%.8x R7=%.8x\n", GPR[4], GPR[5], GPR[6], GPR[7]); + } else { +sim_debug(DEBUG_IRQ, &cpu_dev, + "LPSD OPSD %.8x %.8x NPSD %.8x %.8x SPDF5 %.8x DQE %x LMN %8s\n", + TPSD[0], TPSD[1], PSD1, PSD2, SPAD[0xf5], dqe, n); +sim_debug(DEBUG_IRQ, &cpu_dev, + " R0=%.8x R1=%.8x R2=%.8x R3=%.8x\n", GPR[0], GPR[1], GPR[2], GPR[3]); +sim_debug(DEBUG_IRQ, &cpu_dev, + " R4=%.8x R5=%.8x R6=%.8x R7=%.8x\n", GPR[4], GPR[5], GPR[6], GPR[7]); + } +// cpu_dev.dctrl |= DEBUG_DETAIL; /* start instruction trace */ + } +skipdqe: +#else + if (opr & 0x0200) { /* Was it LPSDCM? */ +sim_debug(DEBUG_IRQ, &cpu_dev, + "LPSDCM OPSD %.8x %.8x NPSD %.8x %.8x SPDF5 %.8x CPUSTATUS %08x\n", + TPSD[0], TPSD[1], PSD1, PSD2, SPAD[0xf5], CPUSTATUS); +sim_debug(DEBUG_IRQ, &cpu_dev, + " R0=%.8x R1=%.8x R2=%.8x R3=%.8x\n", GPR[0], GPR[1], GPR[2], GPR[3]); +sim_debug(DEBUG_IRQ, &cpu_dev, + " R4=%.8x R5=%.8x R6=%.8x R7=%.8x\n", GPR[4], GPR[5], GPR[6], GPR[7]); + } else { +sim_debug(DEBUG_IRQ, &cpu_dev, + "LPSD OPSD %.8x %.8x NPSD %.8x %.8x SPDF5 %.8x CPUSTATUS %08x\n", + TPSD[0], TPSD[1], PSD1, PSD2, SPAD[0xf5], CPUSTATUS); +sim_debug(DEBUG_IRQ, &cpu_dev, + " R0=%.8x R1=%.8x R2=%.8x R3=%.8x\n", GPR[0], GPR[1], GPR[2], GPR[3]); +sim_debug(DEBUG_IRQ, &cpu_dev, + " R4=%.8x R5=%.8x R6=%.8x R7=%.8x\n", GPR[4], GPR[5], GPR[6], GPR[7]); + } +#endif + if (opr & 0x0200) { /* Was it LPSDCM? */ + /* map bit must be on to load maps */ + if (PSD2 & MAPBIT) { + /* set mapped mode in cpu status */ + CPUSTATUS |= BIT8; /* set bit 8 of cpu status */ +#ifdef LOOK_MAP_05272021 + sim_debug(DEBUG_IRQ, &cpu_dev, + "B4 LPSDCM temp %06x TPSD %08x %08x PSD %08x %08x\n", + temp, TPSD[0], TPSD[1], PSD1, PSD2); + sim_debug(DEBUG_IRQ, &cpu_dev, + "B4 LPSDCM BPIX %04x CPIX %04x CPIXPL %04x\n", + BPIX, CPIX, CPIXPL); + sim_debug(DEBUG_IRQ, &cpu_dev, + "B4 LPSDCM OS MAPC[0-7] %08x %08x %08x %08x %08x %08x %08x %08x\n", + MAPC[0], MAPC[1], MAPC[2], MAPC[3], MAPC[4], MAPC[5], MAPC[6], MAPC[7]); + sim_debug(DEBUG_IRQ, &cpu_dev, + "B4 LPSDCM US MAPC[%x-%x] %08x %08x %08x %08x %08x %08x %08x %08x\n", + BPIX, BPIX+5, MAPC[BPIX], MAPC[BPIX+1], MAPC[BPIX+2], + MAPC[BPIX+3], MAPC[BPIX+4], MAPC[BPIX+5], + MAPC[BPIX+6], MAPC[BPIX+7]); +#endif + /* this mod fixes MPX 1.X 1st swapr load */ + /* any O/S or user maps yet? */ + if (((CPIX != 0) && (CPIXPL == 0)) && (PSD2 & RETMBIT)) { + PSD2 &= ~RETMBIT; /* no, turn off retain bit in PSD2 */ + sim_debug(DEBUG_EXP, &cpu_dev, "Turn off retain bit\n"); + } + + /* test if user count is equal to CPIXPL, if not load maps */ + /* this fixes software error in MPX3X where count is changed */ + /* but the retain bit was left set, so new maps were not loaded */ + /* until the next context switch and causes loading error */ + /* CHANGED 041420 maybe not right */ + if ((PSD2 & RETMBIT)) { /* don't load maps if retain bit set */ + uint32 mpl = SPAD[0xf3]; /* get mpl from spad address */ + uint32 cpix = PSD2 & 0x3ff8; /* get cpix 11 bit offset from psd wd 2 */ + uint32 midl = RMW(mpl+cpix); /* get midl entry for given user cpix */ + uint32 spc = midl & MASK16; /* get 16 bit user segment description count */ +#ifdef TRY_TEST_05182021 + /* output O/S and User MPL entries */ + sim_debug(DEBUG_EXP, &cpu_dev, + "#LPSDCM MEM %06x MPL %06x MPL[0] %08x %06x MPL[%04x] %08x %06x\n", + MEMSIZE, mpl, RMW(mpl), RMW(mpl+4), cpix, + RMW(cpix+mpl), RMW(cpix+mpl+4)); + sim_debug(DEBUG_EXP, &cpu_dev, + "#LPSDCM2 MEM %06x BPIX %04x cpix %04x CPIX %04x CPIXPL %04x HIWM %04x\n", + MEMSIZE, BPIX, cpix, CPIX, CPIXPL, HIWM); +#endif + /* if this code is not present, MPX3X will not boot correctly */ + if (spc != CPIXPL) { + PSD2 &= ~RETMBIT; /* no, turn off retain bit in PSD2 */ + } + /* if this code is not present MPX3X will abort */ + /* when trying to mount a secondary disk */ + else + { + if ((CPU_MODEL == MODEL_67) || (CPU_MODEL == MODEL_97) || + (CPU_MODEL == MODEL_V6) || (CPU_MODEL == MODEL_V9)) { + PSD2 &= ~RETMBIT; /* no, turn off retain bit in PSD2 */ + } + } + sim_debug(DEBUG_IRQ, &cpu_dev, + "LPSDCM FIX MAP TRAPME %02x PSD1 %08x PSD2 %08x spc %02x BPIX %02x CPIXPL %02x retain %01x\n", + TRAPME, PSD1, PSD2, spc, BPIX, CPIXPL, PSD2&RETMBIT?1:0); + } + + if ((PSD2 & RETMBIT) == 0) { /* don't load maps if retain bit set */ + /* we need to load the new maps */ + TRAPME = load_maps(PSD, 0); /* load maps for new PSD */ +#ifdef LOOK_MAP_05272021 + sim_debug(DEBUG_IRQ, &cpu_dev, + "AF LPSDCM TPSD %08x %08x PSD %08x %08x TRAPME %02x\n", + TPSD[0], TPSD[1], PSD1, PSD2, TRAPME); + sim_debug(DEBUG_IRQ, &cpu_dev, + "AF LPSDCM BPIX %04x CPIX %04x CPIXPL %04x\n", + BPIX, CPIX, CPIXPL); + sim_debug(DEBUG_IRQ, &cpu_dev, + "AF LPSDCM OS MAPC[0-7] %08x %08x %08x %08x %08x %08x %08x %08x\n", + MAPC[0], MAPC[1], MAPC[2], MAPC[3], MAPC[4], MAPC[5], MAPC[6], MAPC[7]); + sim_debug(DEBUG_IRQ, &cpu_dev, + "AF LPSDCM US MAPC[%x-%x] %08x %08x %08x %08x %08x %08x %08x %08x\n", + BPIX, BPIX+5, MAPC[BPIX], MAPC[BPIX+1], MAPC[BPIX+2], + MAPC[BPIX+3], MAPC[BPIX+4], MAPC[BPIX+5], + MAPC[BPIX+6], MAPC[BPIX+7]); +#endif + } + PSD2 &= ~RETMBIT; /* turn off retain bit in PSD2 */ + SPAD[0xf5] = PSD2; /* save the current PSD2 */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ + sim_debug(DEBUG_IRQ, &cpu_dev, + "LPSDCM MAPS LOADED TRAPME %02x PSD1 %08x PSD2 %08x BPIX %02x CPIXPL %02x retain %01x\n", + TRAPME, PSD1, PSD2, BPIX, CPIXPL, PSD2&RETMBIT?1:0); + } + PSD2 &= ~RETMBIT; /* turn off retain bit in PSD2 */ + } else { + /* LPSD */ + /* if cpix is zero, copy cpix from PSD2 in SPAD[0xf5] */ + if ((PSD2 & 0x3ff8) == 0) { + PSD2 |= (SPAD[0xf5] & 0x3ff8); /* use new cpix */ + } + } + /* TRAPME can be error from LPSDCM or OK here */ + if (TRAPME) { /* if we have an error, restore old PSD */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "LPSDCM MAPS2 LOADED TRAPME = %02x PSD1 %08x PSD2 %08x CPUSTAT %08x SPAD[f9] %08x\n", + TRAPME, PSD1, PSD2, CPUSTATUS, SPAD[0xf9]); + PSD1 = TPSD[0]; /* restore PSD1 */ + /* HACK HACK HACK */ + /* Diags wants the new PSD2, not the original??? */ + /* if old one was used, we fail test 21/0 in cn.mmm for 32/67 */ + CPUSTATUS = bc; /* restore the CPU STATUS */ + MODES = t; /* restore modes too */ + SPAD[0xf5] = ix; /* restore the current PSD2 to SPAD */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ + irq_pend = reg; /* restore intr status */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) { + TRAPSTATUS |= BIT10; /* set bit 10 of trap status */ + TRAPSTATUS |= BIT7; /* set bit 7 of trap status */ + } else + TRAPSTATUS |= BIT18; /* set bit 18 of trap status */ + goto newpsd; /* go process error */ + } + SPAD[0xf5] = PSD2; /* save the current PSD2 */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ + drop_nop = 0; /* nothing to drop */ + goto newpsd; /* load the new psd, or process error */ + break; + + case 0x4: /* JWCS */ /* not used in simulator */ + sim_debug(DEBUG_EXP, &cpu_dev, "Got JWCS\n"); + break; + case 0x2: /* BRI */ /* TODO - only for 32/55 or 32/7X in PSW mode */ + case 0x6: /* TRP */ + case 0x7: /* TPR */ + TRAPME = UNDEFINSTR_TRAP; /* trap condition */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* undefined instruction trap */ + break; + } + break; + +/* F Class I/O device instruction format */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* |00 01 02 03 04 05|06 07 08|09 10 11 12|13 14 15|16|17 18 19 20 21 22 23|24 25 26 27 28 29 30 31| */ +/* | Op Code | Reg | I/O type | Aug |0 | Channel Address | Device Sub-address | */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* */ +/* E Class I/O device instruction format */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* |00 01 02 03 04 05|06 07 08 09 10 11 12|13 14 15|16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31| */ +/* | Op Code | Device Number | Aug | Command Code | */ +/* |-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------| */ +/* */ + case 0xFC>>2: /* 0xFC IMM - IMM */ /* XIO, CD, TD, Interrupt Control */ +#ifdef MAYBE_NO + irq_pend = 1; /* start scanning interrupts again */ +#endif + if ((MODES & PRIVBIT) == 0) { /* must be privileged to do I/O */ + TRAPME = PRIVVIOL_TRAP; /* set the trap to take */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + else + TRAPSTATUS |= BIT19; /* set bit 19 of trap status */ + goto newpsd; /* Privlege violation trap */ + } + if ((opr & 0x7) != 0x07) { /* aug is 111 for XIO instruction */ + /* Process Non-XIO instructions */ + uint32 status = 0; /* status returned from device */ + uint32 device = (opr >> 3) & 0x7f; /* get device code */ + uint32 prior = device; /* interrupt priority */ + uint32 maxlev = 0x5f; /* max lev for all but 32/27 in diags */ +//MAYBEBAD uint32 maxlev = 0x6f; /* max lev for all but 32/27 in diags */ + + t = SPAD[prior+0x80]; /* get spad entry for interrupt */ + addr = SPAD[0xf1] + (prior<<2); /* vector address in SPAD */ + addr = M[addr>>2]; /* get the interrupt context block addr */ + prior = (opr >> 3) & 0x7f; /* get priority level */ + if (CPU_MODEL <= MODEL_27) { + maxlev = 0x6f; /* 27 uses 112 */ + } + + switch(opr & 0x7) { /* use bits 13-15 to determine instruction */ + case 0x0: /* EI FC00 Enable Interrupt */ + if (prior > maxlev) /* ignore for invalid levels */ + break; /* ignore */ + /* SPAD entries for interrupts begin at 0x80 */ + t = SPAD[prior+0x80]; /* get spad entry for interrupt */ + if ((t == 0) || ((t&MASK24) == MASK24)) /* if unused, ignore instruction */ + break; /* ignore */ + + if ((t & 0x0f800000) == 0x0f000000) /* if class F ignore instruction */ + break; /* ignore for F class */ + + /* does not effect REQ status */ + INTS[prior] |= INTS_ENAB; /* enable specified int level */ + SPAD[prior+0x80] |= SINT_ENAB; /* enable in SPAD too */ + irq_pend = 1; /* start scanning interrupts again */ + +#ifdef TRY_ME_01072022 + sim_debug(DEBUG_IRQ, &cpu_dev, + "EI skipinstr %d PSD1 %08x irq_pend %d wait4int %d irq_auto %x\n", + skipinstr, PSD1, irq_pend, wait4int, irq_auto); + sim_debug(DEBUG_IRQ, &cpu_dev, + "EI INTS[%d] = %08x SPAD[%d] %08x CPUSTATUS %08x\n", + prior, INTS[prior], 0x80+prior, SPAD[prior+0x80], CPUSTATUS); +#endif + /* test for clock at address 0x7f06 and interrupt level 0x18 */ + /* the diags want the type to be 0 */ + /* UTX wants the type to be 3?? */ + /* UTX would be 0x03807f06 Diags would be 0x00807f06 */ + if ((SPAD[prior+0x80] & 0x0000ffff) == 0x00007f06) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "Clock EI %02x SPAD %08x Turn on\n", prior, t); + rtc_setup(1, prior); /* tell clock to start */ + } + /* the diags want the type to be 3 */ + if ((SPAD[prior+0x80] & 0x0f00ffff) == 0x03007f04) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "Intv Timer EI %02x SPAD %08x Turn on\n", prior, t); + itm_setup(1, prior); /* tell timer to start */ +#ifdef DO_DYNAMIC_INSTR + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ +#endif + } + break; + + case 0x1: /* DI FC01 */ + if (prior > maxlev) /* ignore for invalid levels */ + break; /* ignore */ + /* SPAD entries for interrupts begin at 0x80 */ + t = SPAD[prior+0x80]; /* get spad entry for interrupt */ + + if ((t == 0) || ((t&MASK24) == MASK24)) /* if unused, ignore instruction */ + break; /* ignore */ + + if ((t & 0x0f800000) == 0x0f000000) /* if class F ignore instruction */ + break; /* ignore for F class */ + + /* active state is left alone */ + INTS[prior] &= ~INTS_ENAB; /* disable specified int level */ + SPAD[prior+0x80] &= ~SINT_ENAB; /* disable in SPAD too */ + INTS[prior] &= ~INTS_REQ; /* clears any requests also */ + irq_pend = 1; /* start scanning interrupts again */ + + /* test for clock at address 0x7f06 and interrupt level 0x18 */ + /* the diags want the type to be 0 */ + /* UTX wants the type to be 3?? */ + /* UTX would be 0x03807f06 Diags would be 0x00807f06 */ + if ((SPAD[prior+0x80] & 0x0000ffff) == 0x00007f06) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "Clock DI %02x SPAD %08x Turn off\n", prior, t); + rtc_setup(0, prior); /* tell clock to stop */ + } + /* the diags want the type to be 3 */ + if ((SPAD[prior+0x80] & 0x0f00ffff) == 0x03007f04) { + sim_debug(DEBUG_IRQ, &cpu_dev, + "Intv Timer DI %02x SPAD %08x Turn off\n", prior, t); +#ifdef DO_DYNAMIC_INSTR + cpu_dev.dctrl &= ~DEBUG_INST; /* stop instruction trace */ +#endif + itm_setup(0, prior); /* tell timer to stop */ + } + break; + + case 0x2: /* RI FC02 */ + if (prior > maxlev) /* ignore for invalid levels */ + break; /* ignore */ + /* SPAD entries for interrupts begin at 0x80 */ + t = SPAD[prior+0x80]; /* get spad entry for interrupt */ + if ((t == 0) || ((t&MASK24) == MASK24)) /* if unused, ignore instruction */ + break; /* ignore */ + + if ((t & 0x0f800000) == 0x0f000000) /* if class F ignore instruction */ + break; /* ignore for F class */ + + INTS[prior] |= INTS_REQ; /* set the request flag for this level */ + irq_pend = 1; /* start scanning interrupts again */ + break; + + case 0x3: /* AI FC03 */ + if (prior > maxlev) /* ignore for invalid levels */ + break; /* ignore */ + /* SPAD entries for interrupts begin at 0x80 */ + t = SPAD[prior+0x80]; /* get spad entry for interrupt */ + if ((t == 0) || ((t&MASK24) == MASK24)) /* if unused, ignore instruction */ + break; /* ignore */ + + if ((t & 0x0f800000) == 0x0f000000) /* if class F ignore instruction */ + break; /* ignore for F class */ + + INTS[prior] |= INTS_ACT; /* activate specified int level */ + SPAD[prior+0x80] |= SINT_ACT; /* activate in SPAD too */ + irq_pend = 1; /* start scanning interrupts again */ + break; + + case 0x4: /* DAI FC04 */ + if (prior > maxlev) /* ignore for invalid levels */ + break; /* ignore */ + /* SPAD entries for interrupts begin at 0x80 */ + t = SPAD[prior+0x80]; /* get spad entry for interrupt */ + if ((t == 0) || ((t&MASK24) == MASK24)) /* if unused, ignore instruction */ + break; /* ignore */ + + if ((t & 0x0f800000) == 0x0f000000) /* if class F ignore instruction */ + break; /* ignore for F class */ + + sim_debug(DEBUG_IRQ, &cpu_dev, + "DAI spad %08x INTS[%02x] %08x\n", t, prior, INTS[prior]); + INTS[prior] &= ~INTS_ACT; /* deactivate specified int level */ + SPAD[prior+0x80] &= ~SINT_ACT; /* deactivate in SPAD too */ + irq_pend = 1; /* start scanning interrupts again */ + /* instruction following a DAI can not be interrupted */ + /* skip tests for interrupts if this is the case */ + skipinstr = 1; /* skip interrupt test */ +#ifdef DO_DYNAMIC_DEBUG + /* test for DAI 0x03 */ + if ((prior == 3) && (GPR[5] == 0xfc1c0000) && (GPR[6] == 0x44a4)) { + cpu_dev.dctrl |= (DEBUG_INST|DEBUG_TRAP|DEBUG_IRQ); /* start instruction trace */ + con_dev.dctrl |= DEBUG_XIO|DEBUG_CMD; + sim_debug(DEBUG_IRQ, &cpu_dev, "DAI 0x03 received start debug\n"); + } + if ((prior == 0x0a) && (GPR[5] == 0xfc540000)) { /* test for DAI 0x0A */ + /* turn off debug trace because we are already hung */ + sim_debug(DEBUG_IRQ, &cpu_dev, "DAI 0x0A received stopping debug\n"); + cpu_dev.dctrl &= ~(DEBUG_INST|DEBUG_TRAP|DEBUG_IRQ); /* start instruction trace */ + con_dev.dctrl &= ~(DEBUG_XIO|DEBUG_CMD); + } +#endif + break; + + case 0x5: /* TD FC05 */ /* bits 13-15 is test code type */ + case 0x6: /* CD FC06 */ + /* If CD or TD, make sure device is not F class device */ + /* the channel must be defined as a non class F I/O channel in SPAD */ + /* if class F, the system will generate a system check trap */ + t = SPAD[device]; /* get spad entry for channel */ + if ((t & 0x0f000000) == 0x0f000000) { /* class in bits 4-7 */ + TRAPME = SYSTEMCHK_TRAP; /* trap condition if F class */ + TRAPSTATUS &= ~BIT0; /* class E error bit */ + TRAPSTATUS &= ~BIT1; /* I/O processing error */ + goto newpsd; /* machine check trap */ + } + /* t has spad entry for device */ + /* get the 1's comp of interrupt address from bits 9-15 SPAD entry */ + ix = ((~t)>>16)&0x7f; /* get positive number for interrupt */ + if (opr & 0x1) { /* see if CD or TD */ + /* TODO process a TD */ + if (device == 0x7f) { + /* if this is for the interval timer check cmd type */ + /* if TD 8000 or 4000, set all cc's zero */ + temp = (IR & 0xf000); /* get cmd from instruction */ + if ((temp == 0x4000) || (temp == 0x8000)) + status = 0; /* no CC's */ + else + /* if TD 2000 set CC2 for caller */ + if (temp == 0x2000) + status = CC2BIT; /* set CC2 */ + /* return status has new CC's in bits 1-4 of status word */ + /* insert status CCs */ + PSD1 = ((PSD1 & 0x87fffffe) | (status & 0x78000000)); + } else { +#if 0 + /* may want to handle class E someday */ + if ((TRAPME = testEIO(device, testcode, &status))) + goto newpsd; /* error returned, trap cpu */ + /* return status has new CC's in bits 1-4 of status word */ + /* insert status CCs */ + PSD1 = ((PSD1 & 0x87fffffe) | (status & 0x78000000)); +#endif + goto inv; /* invalid instruction until I fix it */ + } + } else { + /* TODO process a CD */ +#if 0 + if ((TRAPME = startEIO(device, &status))) + goto newpsd; /* error returned, trap cpu */ +#endif + if (device == 0x7f) { + temp = (IR & 0x7f); /* get cmd from instruction */ + status = itm_rdwr(temp, GPR[0], ix); /* read/write the interval timer */ + /* see if the cmd does not return value */ + /* if bit 25 set, read reg val into R0 */ + if (temp & 0x40) + GPR[0] = status; /* return count in reg 0 */ + /* No CC's going out */ + } else { + goto inv; /* invalid instruction until I fix it */ + } + } + break; + case 0x7: /* XIO FC07*/ /* should never get here */ + break; + } + break; /* skip over XIO code */ + } + + /* Process XIO instructions */ + /* see if valid instruction */ + /* DIAGS wants this tested first */ + switch((opr >> 3) & 0xf) { /* use bits 9-12 to determine I/O instruction */ + case 0x00: /* Unassigned */ + case 0x01: /* Unassigned */ + case 0x0A: /* Unassigned */ + TRAPME = UNDEFINSTR_TRAP; /* trap condition */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* undefined instruction trap */ + } + + /* if reg is non-zero, add reg to 15 bits from instruction */ + if (reg) + temp2 = (IR & 0x7fff) + (GPR[reg] & 0x7fff); /* set new chan/suba into IR */ + else + temp2 = (IR & 0x7fff); /* set new chan/suba into IR */ + lchan = (temp2 & 0x7F00) >> 8; /* get 7 bit logical channel address */ + suba = temp2 & 0xFF; /* get 8 bit subaddress */ + lchsa = (lchan << 8) | suba; /* logical address */ + /* the channel must be defined as a class F I/O channel in SPAD */ + /* if not class F, the system will generate a system check trap */ + t = SPAD[lchan]; /* get spad entry for channel */ + if ((t == 0) || ((t&MASK24) == MASK24) || /* if not set up, system check */ + ((t & 0x0f800000) != 0x0f000000)) { /* class in bits 4-7 */ + TRAPME = SYSTEMCHK_TRAP; /* trap condition if F class */ + TRAPSTATUS |= BIT0; /* class F error bit */ + TRAPSTATUS &= ~BIT1; /* I/O processing error */ + goto newpsd; /* undefined instruction trap */ + } + /* get real channel from spad device entry */ + chan = (t & 0x7f00) >> 8; /* real channel */ + rchsa = (chan << 8) | suba; /* real ch & sa */ + /* get the 1's comp of interrupt address from bits 9-15 SPAD entry */ + ix = ((~t)>>16)&0x7f; /* get positive number for interrupt */ + bc = SPAD[ix+0x80]; /* get interrupt spad entry for channel */ + /* SPAD address F1 has interrupt table address */ + temp = SPAD[0xf1] + (ix<<2); /* vector address in SPAD */ + sim_debug(DEBUG_XIO, &cpu_dev, + "$$ XIO chsa %04x spad %08x BLK %1x INTS[%02x] %08x\n", + rchsa, t, CPUSTATUS&0x80?1:0, ix, INTS[ix]); + sim_debug(DEBUG_XIO, &cpu_dev, + "$$ XIO chsa %04x PSD1 %08x PSD2 %08x IR %08x ICBA %06x\n", + rchsa, PSD1, PSD2, IR, temp); + if ((TRAPME = Mem_read(temp, &addr))) { /* get interrupt context block addr */ +mcheck: + /* machine check if not there */ + TRAPME = MACHINECHK_TRAP; /* trap condition */ + TRAPSTATUS |= BIT0; /* class F error bit */ + TRAPSTATUS &= ~BIT1; /* I/O processing error */ + goto newpsd; /* machine check trap */ + } + /* the context block contains the old PSD, */ + /* new PSD, IOCL address, and I/O status address */ + if ((addr == 0) || (addr == 0xffffffff)) { /* must be initialized address */ + goto mcheck; /* bad int icb address */ + } + if ((TRAPME = Mem_read(addr+16, &temp))) { /* get iocl address from icb wd 4 */ + goto mcheck; /* machine check if not there */ + } + /* iocla must be valid addr if it is a SIO instruction */ + if (((temp & MASK24) == 0) && (((opr >> 2) & 0xf) == 2)) { + goto mcheck; /* bad iocl address */ + } + + sim_debug(DEBUG_XIO, &cpu_dev, + "XIO rdy PSD1 %08x chan %02x irq %02x icb %06x iocla %06x iocd %08x %08x\n", + PSD1, chan, ix, addr, addr+16, RMW(temp), RMW(temp+4)); + /* at this point, the channel has a valid SPAD channel entry */ + /* t is SPAD entry contents for chan device */ + /* temp2 has logical channel address */ + /* lchan - logical channel address */ + /* lchsa - logical channel & subaddress */ + /* chan - channel address */ + /* suba - channel device subaddress */ + /* rchsa - real chan & sub address from spad for logical channel */ + /* ix - positive interrupt level */ + /* addr - ICBA for specified interrupt level, points to 6 wd block */ + /* temp - First IOCD address */ + sim_debug(DEBUG_XIO, &cpu_dev, + "XIO switch %02x lchan %02x irq %02x rchsa %04x IOCDa %08x CPUSTATUS %08x BLK %1x\n", + ((opr>>3)&0x0f), lchan, ix, rchsa, temp, CPUSTATUS, CPUSTATUS&0x80?1:0); + + switch((opr >> 3) & 0xf) { /* use bits 9-12 to determine I/O instruction */ + case 0x00: /* Unassigned */ + case 0x01: /* Unassigned */ + case 0x0A: /* Unassigned */ + TRAPME = UNDEFINSTR_TRAP; /* trap condition */ + if ((CPU_MODEL == MODEL_97) || (CPU_MODEL == MODEL_V9)) + TRAPSTATUS |= BIT0; /* set bit 0 of trap status */ + goto newpsd; /* undefined instruction trap */ + break; + + case 0x09: /* Enable write channel ECWCS */ + case 0x0B: /* Write channel WCS WCWCS */ + /* TODO, provide support code */ + /* for now or maybe forever, return unsupported transaction */ + PSD1 = ((PSD1 & 0x87fffffe) | (CC2BIT|CC4BIT)); /* insert status 5 */ + sim_debug(DEBUG_XIO, &cpu_dev, + "XIO unsupported WCS chan %04x chsa %04x status %08x\n", + chan, rchsa, rstatus); + /* just give unsupported transaction */ +#ifdef JUST_RETURN_STATUS + TRAPME = SYSTEMCHK_TRAP; /* trap condition if F class */ + TRAPSTATUS |= BIT0; /* class F error bit */ + TRAPSTATUS &= ~BIT1; /* I/O processing error */ + goto newpsd; /* undefined instruction trap */ +#endif + break; + + case 0x02: /* Start I/O SIO */ + sim_debug(DEBUG_XIO, &cpu_dev, + "SIO b4 call PSD1 %08x rchsa %04x lchsa %04x BLK %1x\n", + PSD1, rchsa, lchsa, CPUSTATUS&0x80?1:0); + if ((TRAPME = startxio(lchsa, &rstatus))) + goto newpsd; /* error returned, trap cpu */ + PSD1 = ((PSD1 & 0x87fffffe) | (rstatus & 0x78000000)); /* insert status */ + sim_debug(DEBUG_XIO, &cpu_dev, + "SIO ret PSD1 %08x chsa %04x status %08x BLK %1x\n", + PSD1, lchsa, rstatus, CPUSTATUS&0x80?1:0); + break; + + case 0x03: /* Test I/O TIO */ + if ((TRAPME = testxio(lchsa, &rstatus))) { + sim_debug(DEBUG_TRAP, &cpu_dev, + "TIO ret PSD1 %x rchsa %x lchsa %x status %x BLK %1x\n", + PSD1, rchsa, lchsa, rstatus, CPUSTATUS&0x80?1:0); + goto newpsd; /* error returned, trap cpu */ + } + PSD1 = ((PSD1 & 0x87fffffe) | (rstatus & 0x78000000)); /* insert status */ + sim_debug(DEBUG_XIO, &cpu_dev, + "TIO ret PSD1 %08x lchsa %04x stat %08x spad %08x INTS[%02x] %08x BLK %1x\n", + PSD1, lchsa, rstatus, t, ix, INTS[ix], CPUSTATUS&0x80?1:0); + break; + + case 0x04: /* Stop I/O STPIO */ + if ((TRAPME = stopxio(lchsa, &rstatus))) + goto newpsd; /* error returned, trap cpu */ + PSD1 = ((PSD1 & 0x87fffffe) | (rstatus & 0x78000000)); /* insert status */ + sim_debug(DEBUG_XIO, &cpu_dev, "STPIO ret rchsa %04x lchsa %04x status %08x\n", + rchsa, lchsa, rstatus); + break; + + case 0x05: /* Reset channel RSCHNL */ + if ((TRAPME = rschnlxio(lchsa, &rstatus))) + goto newpsd; /* error returned, trap cpu */ + /* SPAD entries for interrupts begin at 0x80 */ + INTS[ix] &= ~INTS_REQ; /* clears any requests */ + INTS[ix] &= ~INTS_ACT; /* deactivate specified int level */ + SPAD[ix+0x80] &= ~SINT_ACT; /* deactivate in SPAD too */ + PSD1 = ((PSD1 & 0x87fffffe) | (rstatus & 0x78000000)); /* insert status */ + sim_debug(DEBUG_XIO, &cpu_dev, "RSCHNL rschnlxio ret rchsa %04x lchsa %04x status %08x\n", + rchsa, lchsa, rstatus); + break; + + case 0x06: /* Halt I/O HIO */ + if ((TRAPME = haltxio(lchsa, &rstatus))) + goto newpsd; /* error returned, trap cpu */ + PSD1 = ((PSD1 & 0x87fffffe) | (rstatus & 0x78000000)); /* insert status */ + sim_debug(DEBUG_XIO, &cpu_dev, + "HIO haltxio ret rchsa %04x lchsa %04x status %08x\n", + rchsa, lchsa, rstatus); + break; + + case 0x07: /* Grab controller GRIO n/u */ + if ((TRAPME = grabxio(lchsa, &rstatus))) + goto newpsd; /* error returned, trap cpu */ + PSD1 = ((PSD1 & 0x87fffffe) | (rstatus & 0x78000000)); /* insert status */ + sim_debug(DEBUG_XIO, &cpu_dev, "GRIO ret rchsa %04x lchsa %04x status %08x\n", + rchsa, lchsa, rstatus); + break; + + case 0x08: /* Reset controller RSCTL */ + if ((TRAPME = rsctlxio(lchsa, &rstatus))) + goto newpsd; /* error returned, trap cpu */ + PSD1 = ((PSD1 & 0x87fffffe) | (rstatus & 0x78000000)); /* insert status */ + sim_debug(DEBUG_XIO, &cpu_dev, "RSCTL ret rchsa %04x lchsa %04x status %08x\n", + rchsa, lchsa, rstatus); + break; + + case 0x0C: /* Enable channel interrupt ECI */ + /* disable int only */ + sim_debug(DEBUG_XIO, &cpu_dev, + "ECI chsa %04x lchsa %04x spad %08x INTS[%02x] %08x\n", + rchsa, lchsa, t, ix, INTS[ix]); + if ((TRAPME = checkxio(lchsa, &rstatus))) + goto newpsd; /* error returned, trap cpu */ + sim_debug(DEBUG_XIO, &cpu_dev, + "ECI after checkxio rchsa %04x suba %04x status %08x\n", + rchsa, suba, rstatus); + + if ((INTS[ix] & INTS_ACT) == 0) + sim_debug(DEBUG_XIO, &cpu_dev, + "ECI INT %02x is NOT set rchsa %04x lchsa %04x status %08x\n", + ix, rchsa, lchsa, rstatus); + /* SPAD entries for interrupts begin at 0x80 */ + INTS[ix] |= INTS_ENAB; /* enable specified int level */ + SPAD[ix+0x80] |= SINT_ENAB; /* enable in SPAD too */ + irq_pend = 1; /* start scanning interrupts again */ + /* return status of zero for present and functioning */ + PSD1 = ((PSD1 & 0x87fffffe) | (rstatus & 0x78000000)); /* insert status */ + break; + + case 0x0D: /* Disable channel interrupt DCI */ + /* disable int, leave req */ + sim_debug(DEBUG_XIO, &cpu_dev, + "DCI rchsa %04x lchsa %04x spad %08x INTS[%02x] %08x\n", + rchsa, lchsa, t, ix, INTS[ix]); + + if ((TRAPME = checkxio(lchsa, &rstatus))) + goto newpsd; /* error returned, trap cpu */ + sim_debug(DEBUG_XIO, &cpu_dev, + "DCI After checkxio call rstatus %08x\n", rstatus); + /* doc says we need to drop 1 queued status entry too */ + if ((INTS[ix] & INTS_ACT) == 0) + sim_debug(DEBUG_XIO, &cpu_dev, + "DCI INT %02x is NOT set rchsa %04x lchsa %04x status %08x\n", + ix, rchsa, lchsa, rstatus); + /* SPAD entries for interrupts begin at 0x80 */ + INTS[ix] &= ~INTS_ENAB; /* disable specified int level */ + SPAD[ix+0x80] &= ~SINT_ENAB; /* disable in SPAD too */ + PSD1 = ((PSD1 & 0x87fffffe) | (rstatus & 0x78000000)); /* insert status */ + break; + + case 0x0E: /* Activate channel interrupt ACI */ + /* Set int active, clear request */ + sim_debug(DEBUG_XIO, &cpu_dev, + "ACI rchsa %04x lchsa %04x spad %08x INTS[%02x] %08x\n", + rchsa, lchsa, t, ix, INTS[ix]); + + if ((TRAPME = checkxio(lchsa, &rstatus))) + goto newpsd; /* error returned, trap cpu */ + if ((INTS[ix] & INTS_ACT) == 0) + sim_debug(DEBUG_XIO, &cpu_dev, + "ACI INT %02x is NOT set rchsa %04x lchsa %04x status %08x\n", + ix, rchsa, lchsa, rstatus); + /* SPAD entries for interrupts begin at 0x80 */ + INTS[ix] |= INTS_ACT; /* activate specified int level */ + SPAD[ix+0x80] |= SINT_ACT; /* enable in SPAD too */ + PSD1 = ((PSD1 & 0x87fffffe) | (rstatus & 0x78000000)); /* insert status */ + break; + + case 0x0F: /* Deactivate channel interrupt DACI */ + /* Clear active and leave any request */ + /* Note, instruction following DACI is not interruptable */ + sim_debug(DEBUG_XIO, &cpu_dev, + "DACI rchsa %04x lchsa %04x spad %08x INTS[%02x] %08x\n", + rchsa, lchsa, t, ix, INTS[ix]); + + if ((TRAPME = checkxio(rchsa, &rstatus))) + goto newpsd; /* error returned, trap cpu */ + if ((INTS[ix] & INTS_ACT) == 0) + sim_debug(DEBUG_XIO, &cpu_dev, + "DACI INT %02x is NOT set chan %04x suba %04x status %08x\n", + ix, chan, suba, rstatus); + /* SPAD entries for interrupts begin at 0x80 */ + INTS[ix] &= ~INTS_ACT; /* deactivate specified int level */ + SPAD[ix+0x80] &= ~SINT_ACT; /* deactivate in SPAD too */ + irq_pend = 1; /* start scanning interrupts again */ + skipinstr = 1; /* skip interrupt test */ + PSD1 = ((PSD1 & 0x87fffffe) | (rstatus & 0x78000000)); /* insert status */ + sim_debug(DEBUG_XIO, &cpu_dev, + "DACI ret lchsa %04x status %08x spad %08x INTS[%02x] %08x BLK %1x\n", + lchsa, rstatus, t, ix, INTS[ix], CPUSTATUS&0x80?1:0); + break; + } /* end of XIO switch */ + break; + } /* End of Instruction Switch */ + + /* [*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*] */ + + /* any instruction with an arithmetic exception will still end up here */ + /* after the instruction is done and before incrementing the PC, */ + /* we will trap the cpu if ovl is set nonzero by an instruction */ + + /* Store result to register */ + if (i_flags & SD) { + if (dbl) { /* if double reg, store 2nd reg */ + if (reg & 1) { /* is it double regs into odd reg */ + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "ADDRSPEC13 OP %04x addr %08x\n", OP, addr); + goto newpsd; /* go execute the trap now */ + } + GPR[reg+1] = (uint32)(dest & FMASK); /* save the low order reg */ + GPR[reg] = (uint32)((dest>>32) & FMASK);/* save the hi order reg */ + } else { + GPR[reg] = (uint32)(dest & FMASK); /* save the reg */ + } + } + + /* Store result to base register */ + if (i_flags & SB) { + if (dbl) { /* no dbl wd store to base regs */ + TRAPME = ADDRSPEC_TRAP; /* bad address, error */ + goto newpsd; /* go execute the trap now */ + } + BR[reg] = (uint32)(dest & FMASK); /* save the base reg */ + } + + /* Store result to memory */ + if (i_flags & SM) { + /* Check if byte of half word */ + if (((FC & 04) || (FC & 5) == 1)) { /* hw or byte requires read first */ + if ((TRAPME = Mem_read(addr, &temp))) { /* get the word from memory */ + goto newpsd; /* memory read error or map fault */ + } + } + switch(FC) { + case 2: /* double word store */ + if ((addr & 7) != 2) { + TRAPME = ADDRSPEC_TRAP; /* address not on dbl wd boundry, error */ + goto newpsd; /* go execute the trap now */ + } + temp = (uint32)(dest & MASK32);/* get lo 32 bit */ + if ((TRAPME = Mem_write(addr + 4, &temp))) + goto newpsd; /* memory write error or map fault */ + temp = (uint32)(dest >> 32); /* move upper 32 bits to lo 32 bits */ + break; + + case 0: /* word store */ + temp = (uint32)(dest & FMASK); /* mask 32 bit of reg */ + if ((addr & 3) != 0) { + /* Address fault */ + TRAPME = ADDRSPEC_TRAP; /* address not on wd boundry, error */ + goto newpsd; /* go execute the trap now */ + } + break; + + case 1: /* left halfword write */ + temp &= RMASK; /* mask out 16 left most bits */ + temp |= (uint32)(dest & RMASK) << 16; /* put into left most 16 bits */ + if ((addr & 1) != 1) { + /* Address fault */ + TRAPME = ADDRSPEC_TRAP; /* address not on hw boundry, error */ + goto newpsd; /* go execute the trap now */ + } + break; + + case 3: /* right halfword write */ + temp &= LMASK; /* mask out 16 right most bits */ + temp |= (uint32)(dest & RMASK); /* put into right most 16 bits */ + if ((addr & 3) != 3) { + TRAPME = ADDRSPEC_TRAP; /* address not on hw boundry, error */ + goto newpsd; /* go execute the trap now */ + } + break; + + case 4: + case 5: + case 6: + case 7: /* byte store operation */ + temp &= ~(0xFF << (8 * (7 - FC))); /* clear the byte to store */ + temp |= (uint32)(dest & 0xFF) << (8 * (7 - FC)); /* insert new byte */ + break; + } + /* store back the modified memory location */ + if ((TRAPME = Mem_write(addr, &temp))) /* store back to memory */ + goto newpsd; /* memory write error or map fault */ + } + + /* Update condition code registers */ + if (i_flags & SCC) { + PSD1 &= 0x87FFFFFE; /* clear the old CC's */ + if (ovr) /* if overflow, set CC1 */ + CC = CC1BIT; /* show we had AEXP */ + else + CC = 0; /* no CC's yet */ + if (dest & DMSIGN) /* if neg, set CC3 */ + CC |= CC3BIT; /* if neg, set CC3 */ + else if (dest == 0) + CC |= CC4BIT; /* if zero, set CC4 */ + else + CC |= CC2BIT; /* if gtr than zero, set CC2 */ + PSD1 |= CC & 0x78000000; /* update the CC's in the PSD */ + } + + /* check if we had an arithmetic exception on the last instruction*/ + if (ovr && (MODES & AEXPBIT)) { + TRAPME = AEXPCEPT_TRAP; /* trap the system now */ + goto newpsd; /* process the trap */ + } + + /* Update instruction pointer to next instruction */ + if ((i_flags & BT) == 0) { /* see if PSD was replaced on a branch instruction */ + /* branch not taken, so update the PC */ + if (EXM_EXR != 0) { /* special handling for EXM, EXR, EXRR */ + PSD1 = (PSD1 + 4) | (((PSD1 & 2) >> 1) & 1); + EXM_EXR = 0; /* reset PC increment for EXR */ + } else + if (i_flags & HLF) { /* if nop in rt hw, bump pc a word */ + if ((drop_nop) && ((CPU_MODEL == MODEL_67) || (CPU_MODEL == MODEL_V6))) + { + PSD1 = (PSD1 + 4) | (((PSD1 & 2) >> 1) & 1); + } else { + PSD1 = (PSD1 + 2) | (((PSD1 & 2) >> 1) & 1); + } + } else { + PSD1 = (PSD1 + 4) | (((PSD1 & 2) >> 1) & 1); + } + drop_nop = 0; /* no NOP to drop */ + } else { + EXM_EXR = 0; /* reset PC increment for EXR */ + drop_nop = 0; /* no NOP to drop */ + } + + OPSD1 &= 0x87FFFFFE; /* clear the old CC's */ + OPSD1 |= PSD1 & 0x78000000; /* update the CC's in the PSD */ + /* TODO Update other history information for this instruction */ + if (hst_lnt) { + hst[hst_p].opsd1 = OPSD1; /* update the CC in opsd1 */ + hst[hst_p].npsd1 = PSD1; /* save new psd1 */ + hst[hst_p].npsd2 = PSD2; /* save new psd2 */ + hst[hst_p].modes = MODES; /* save current mode bits */ + hst[hst_p].modes |= (CPUSTATUS & BIT24); /* save blocking mode bit */ + for (ix=0; ix<8; ix++) { + hst[hst_p].reg[ix] = GPR[ix]; /* save reg */ + hst[hst_p].reg[ix+8] = BR[ix]; /* save breg */ + } + } + + /* DEBUG_INST support code */ + /* output mapped/unmapped */ + if (MODES & BASEBIT) + BM = 'B'; + else + BM = 'N'; + if (MODES & MAPMODE) + MM = 'M'; + else + MM = 'U'; + if (CPUSTATUS & BIT24) + BK = 'B'; + else + BK = 'U'; + sim_debug(DEBUG_INST, &cpu_dev, "%c%c%c %.8x %.8x %.8x ", + BM, MM, BK, OPSD1, PSD2, OIR); + if (cpu_dev.dctrl & DEBUG_INST) { + fprint_inst(sim_deb, OIR, 0); /* display instruction */ + sim_debug(DEBUG_INST, &cpu_dev, + "\n\tR0=%.8x R1=%.8x R2=%.8x R3=%.8x", GPR[0], GPR[1], GPR[2], GPR[3]); + sim_debug(DEBUG_INST, &cpu_dev, + " R4=%.8x R5=%.8x R6=%.8x R7=%.8x\n", GPR[4], GPR[5], GPR[6], GPR[7]); + if (MODES & BASEBIT) { + sim_debug(DEBUG_INST, &cpu_dev, + "\tB0=%.8x B1=%.8x B2=%.8x B3=%.8x", BR[0], BR[1], BR[2], BR[3]); + sim_debug(DEBUG_INST, &cpu_dev, + " B4=%.8x B5=%.8x B6=%.8x B7=%.8x\n", BR[4], BR[5], BR[6], BR[7]); + } + } +#ifdef BAD_02102022 + SPAD[0xf5] = PSD2; /* save the current PSD2 */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ +#endif + continue; /* keep running */ + +newpsd: +#ifndef BAD_02102022 + SPAD[0xf5] = PSD2; /* save the current PSD2 */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ +#endif + /* Trap Context Block - 6 words */ + /* WD1 Old PSD Wd 1 */ + /* WD2 Old PSD Wd 2 */ + /* WD3 New PSD WD 1 */ + /* WD4 New PSD Wd 2 */ + /* WD5 Multi Use */ /* N/U for Interrupts */ + /* WD6 Multi Use */ /* N/U for Interrupts */ + + /* WD5 Multi Use */ /* IOCL address for I/O */ + /* WD6 Multi Use */ /* Status address for I/O */ + + /* WD5 Multi Use */ /* Secondary vector table for SVC */ + /* WD6 Multi Use */ /* N/U for SVC */ + + /* WD5 Multi Use */ /* Trap status word for traps */ + /* WD6 Multi Use */ /* N/U for traps */ + + /* WD5 Multi Use */ /* Trap status word for page faults */ + /* WD6 Multi Use */ /* Page fault status word */ + /* Bit 0 = 0 The map fault was caused by an instruction fetch */ + /* = 1 The mp fault was caused by an operand access */ + /* Bits 1-20 Always zero */ + /* Map register number (logical map block number) */ + + /* we get here from a LPSD, LPSDCM, INTR, or TRAP */ + if (TRAPME) { + /* SPAD location 0xf0 has trap vector base address */ + uint32 tta = SPAD[0xf0]; /* get trap table address in memory */ + uint32 tvl; /* trap vector location */ + if ((tta == 0) || ((tta&MASK24) == MASK24)) + tta = 0x80; /* if not set, assume 0x80 FIXME */ + /* Trap Table Address in memory is pointed to by SPAD 0xF0 */ + /* TODO update cpu status and trap status words with reason too */ + switch(TRAPME) { + case POWERFAIL_TRAP: /* 0x80 PL00/PL01 power fail trap */ + case POWERON_TRAP: /* 0x84 PL00/PL01 Power-On trap */ + case MEMPARITY_TRAP: /* 0x88 PL02 Memory Parity Error trap */ + case NONPRESMEM_TRAP: /* 0x8C PL03 Non Present Memory trap */ + case UNDEFINSTR_TRAP: /* 0x90 PL04 Undefined Instruction Trap */ + case PRIVVIOL_TRAP: /* 0x94 PL05 Privlege Violation Trap */ +//MOVED case SVCCALL_TRAP: /* 0x98 PL06 Supervisor Call Trap */ + case MACHINECHK_TRAP: /* 0x9C PL07 Machine Check Trap */ + case SYSTEMCHK_TRAP: /* 0xA0 PL08 System Check Trap */ + case MAPFAULT_TRAP: /* 0xA4 PL09 Map Fault Trap */ + case IPUUNDEFI_TRAP: /* 0xA8 PL0A IPU Undefined Instruction Trap */ +//MOVED case CALM_TRAP: /* 0xA8 PL0A Call Monitor Instruction Trap */ + case SIGNALIPU_TRAP: /* 0xAC PL0B Signal IPU/CPU Trap */ + case ADDRSPEC_TRAP: /* 0xB0 PL0C Address Specification Trap */ +//BAD HERE case CONSOLEATN_TRAP: /* 0xB4 PL0D Console Attention Trap */ + case PRIVHALT_TRAP: /* 0xB8 PL0E Privlege Mode Halt Trap */ + case AEXPCEPT_TRAP: /* 0xBC PL0F Arithmetic Exception Trap */ + case CACHEERR_TRAP: /* 0xC0 PL10 Cache Error Trap (V9 Only) */ + /* drop through */ + default: + sim_debug(DEBUG_TRAP, &cpu_dev, + "##TRAPME %02x LOAD MAPS PSD1 %08x PSD2 %08x CPUSTATUS %08x drop_nop %1x i_flags %04x\n", + TRAPME, PSD1, PSD2, CPUSTATUS, drop_nop, i_flags); + /* adjust PSD1 to next instruction */ + /* Update instruction pointer to next instruction */ + if ((i_flags & BT) == 0) { /* see if PSD was replaced on a branch instruction */ + /* branch not taken, so update the PC */ + if (EXM_EXR != 0) { /* special handling for EXM, EXR, EXRR */ + PSD1 = (PSD1 + 4) | (((PSD1 & 2) >> 1) & 1); + EXM_EXR = 0; /* reset PC increment for EXR */ + } else + if (i_flags & HLF) { /* if nop in rt hw, bump pc a word */ + if ((drop_nop) && ((CPU_MODEL == MODEL_67) || (CPU_MODEL == MODEL_V6))) { + PSD1 = (PSD1 + 4) | (((PSD1 & 2) >> 1) & 1); + } else { + PSD1 = (PSD1 + 2) | (((PSD1 & 2) >> 1) & 1); + } + drop_nop = 0; + } else { + PSD1 = (PSD1 + 4) | (((PSD1 & 2) >> 1) & 1); + //DIAG fix for test 34/10 in MMM diag, reset bit 31 + if ((CPU_MODEL == MODEL_87) || (CPU_MODEL == MODEL_97) || + (CPU_MODEL == MODEL_V9)) + PSD1 &= ~BIT31; /* force off last right */ + } + } else { + EXM_EXR = 0; /* reset PC increment for EXR */ + if ((drop_nop) && ((CPU_MODEL == MODEL_67) || (CPU_MODEL >= MODEL_V6))) + PSD1 &= ~BIT31; /* force off last right */ + drop_nop = 0; + sim_debug(DEBUG_TRAP, &cpu_dev, + "##GOT BT TRAPME %04x LOAD MAPS PSD1 %08x PSD2 %08x\n", + TRAPME, PSD1, PSD2); + } + /* fall through */ + /* do not update pc for page fault */ + case DEMANDPG_TRAP: /* 0xC4 Demand Page Fault Trap (V6&V9 Only) */ + if (TRAPME == DEMANDPG_TRAP) { /* 0xC4 Demand Page Fault Trap (V6&V9 Only) */ + /* Set map number */ + if (CPU_MODEL >= MODEL_V9) + PSD1 &= ~BIT31; /* force off last right */ + /* pfault will have 11 bit page number and bit 0 set if op fetch */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "##PAGEFAULT TRAPS %02x page# %04x LOAD MAPS PSD1 %08x PSD2 %08x CPUSTATUS %08x\n", + TRAPME, pfault, PSD1, PSD2, CPUSTATUS); + } + /* Moved here 05/28/2021 so PC gets incremented incorrectly */ + /* This caused the 2nd instruction of an int service routine to be skipped */ + /* The attn trap had to be on 2nd instruction */ + case CONSOLEATN_TRAP: /* 0xB4 PL0D Console Attention Trap */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "At TRAPME %02x PSD1 %08x PSD2 %08x CPUSTATUS %08x drop_nop %02x\n", + TRAPME, PSD1, PSD2, CPUSTATUS, drop_nop); + sim_debug(DEBUG_TRAP, &cpu_dev, + "At TRAP %02x IR %08x PSD1 %08x PSD2 %08x CPUSTATUS %08x ovr %01x drop_nop %01x\n", + TRAPME, IR, PSD1, PSD2, CPUSTATUS, ovr, drop_nop); + sim_debug(DEBUG_TRAP, &cpu_dev, + "R0=%.8x R1=%.8x R2=%.8x R3=%.8x\n", GPR[0], GPR[1], GPR[2], GPR[3]); + sim_debug(DEBUG_TRAP, &cpu_dev, + "R4=%.8x R5=%.8x R6=%.8x R7=%.8x\n", GPR[4], GPR[5], GPR[6], GPR[7]); + + tta = tta + (TRAPME - 0x80); /* tta has mem addr of trap vector */ + if (MODES & (BASEBIT | EXTDBIT)) + tvl = M[tta>>2] & 0xFFFFFC; /* get 24 bit trap address from trap vector loc */ + else + tvl = M[tta>>2] & 0x7FFFC; /* get 19 bit trap address from trap vector loc */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "tvl %08x, tta %08x status %08x page# %04x\n", tvl, tta, CPUSTATUS, pfault); +#ifndef TEMP_CHANGE_FOR_MPX3X_DEBUG + if (tvl == 0 || (CPUSTATUS & 0x40) == 0) { +#else + /* next line changed to force halt on halt trap */ + /* TRIED 041320 for MPX3.X install and testing */ + if (((tvl == 0) || (CPUSTATUS & 0x40) == 0) || + (TRAPME == PRIVHALT_TRAP)) { /* 0xB8 PL0E Privlege Mode Halt Trap */ +#endif + /* vector is zero or software has not enabled traps yet */ + /* execute a trap halt */ + /* set the PSD to trap vector location */ + fprintf(stderr, "[][][][][][][][][][] HALT TRAP [][][][][][][][][][]\r\n"); + fprintf(stderr, "PSD1 %08x PSD2 %08x TRAPME %04x\r\n", PSD1, PSD2, TRAPME); + PSD1 = 0x80000000 + TRAPME; /* just priv and PC to trap vector */ + PSD2 = 0x00004000; /* unmapped, blocked interrupts mode */ + M[0x680>>2] = PSD1; /* store PSD 1 */ + M[0x684>>2] = PSD2; /* store PSD 2 */ + M[0x688>>2] = TRAPSTATUS; /* store trap status */ + M[0x68C>>2] = 0; /* This will be device table entry later TODO */ + for (ix=0; ix<8; ix+=2) { + fprintf(stderr, "GPR[%d] %08x GPR[%d] %08x\r\n", ix, GPR[ix], ix+1, GPR[ix+1]); + } + if (MODES & BASEBIT) { + for (ix=0; ix<8; ix+=2) { + fprintf(stderr, "BR[%d] %08x BR[%d] %08x\r\n", ix, BR[ix], ix+1, BR[ix+1]); + } + } + fprintf(stderr, "[][][][][][][][][][] HALT TRAP [][][][][][][][][][]\r\n"); + return STOP_HALT; /* exit to simh for halt */ + } else { + uint32 oldstatus = CPUSTATUS; /* keep for retain blocking state */ + /* valid vector, so store the PSD, fetch new PSD */ + bc = PSD2 & 0x3ff8; /* get copy of cpix */ + if ((TRAPME) && ((CPU_MODEL <= MODEL_27))) { + /* Traps on 27 have bit 31 reset */ + M[tvl>>2] = PSD1 & 0xfffffffe; /* store PSD 1 */ + } else + M[tvl>>2] = PSD1 & 0xffffffff; /* store PSD 1 */ + M[(tvl>>2)+1] = PSD2; /* store PSD 2 */ + PSD1 = M[(tvl>>2)+2]; /* get new PSD 1 */ + PSD2 = (M[(tvl>>2)+3] & ~0x3ff8) | bc; /* get new PSD 2 w/old cpix */ + M[(tvl>>2)+4] = TRAPSTATUS; /* store trap status */ + if (TRAPME == DEMANDPG_TRAP) { /* 0xC4 Demand Page Fault Trap (V6&V9 Only) */ + M[(tvl>>2)+5] = pfault; /* store page fault number */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "DPAGE tvl %06x PSD1 %08x PSD2 %08x TRAPME %04x TRAPSTATUS %08x\n", + tvl, PSD1, PSD2, TRAPME, pfault); + } + + /* set the mode bits and CCs from the new PSD */ + CC = PSD1 & 0x78000000; /* extract bits 1-4 from PSD1 */ + MODES = PSD1 & 0x87000000; /* extract bits 0, 5, 6, 7 from PSD 1 */ + CPUSTATUS &= ~0x87000000; /* reset bits in CPUSTATUS */ + CPUSTATUS |= MODES; /* now insert into CPUSTATUS */ + + /* set new map mode and interrupt blocking state in CPUSTATUS */ + if (PSD2 & MAPBIT) { + CPUSTATUS |= BIT8; /* set bit 8 of cpu status */ + MODES |= MAPMODE; /* set mapped mode */ + } else { + CPUSTATUS &= ~BIT8; /* reset bit 8 of cpu status */ +/*TRY_01072022*/ MODES &= ~MAPMODE; /* reset mapped mode */ + } + + /* set interrupt blocking state */ + if ((PSD2 & RETBBIT) == 0) { /* is it retain blocking state */ + if (PSD2 & SETBBIT) { /* no, is it set blocking state */ + CPUSTATUS |= BIT24; /* yes, set blk state in cpu status bit 24 */ + MODES |= BLKMODE; /* set blocked mode */ + } else { + CPUSTATUS &= ~BIT24; /* no, reset blk state in cpu status bit 24 */ + MODES &= ~BLKMODE; /* reset blocked mode */ + } + } else { + /* handle retain blocking state */ + PSD2 &= ~RETMBIT; /* turn off retain bit in PSD2 */ + /* set new blocking state in PSD2 */ + PSD2 &= ~(SETBBIT|RETBBIT); /* clear bit 48 & 49 to be unblocked */ + MODES &= ~(BLKMODE|RETBLKM);/* reset blocked & retain mode bits */ + if (oldstatus & BIT24) { /* see if old mode is blocked */ + PSD2 |= SETBBIT; /* set to blocked state */ + MODES |= BLKMODE; /* set blocked mode */ + } + } + + SPAD[0xf5] = PSD2; /* save the current PSD2 */ + SPAD[0xf9] = CPUSTATUS; /* save the cpu status in SPAD */ + + sim_debug(DEBUG_TRAP, &cpu_dev, + "Process TRAPME %04x PSD1 %08x PSD2 %08x CPUSTATUS %08x\n", + TRAPME, PSD1, PSD2, CPUSTATUS); + /* TODO provide page fault data to word 6 */ + if (TRAPME == DEMANDPG_TRAP) { /* 0xC4 Demand Page Fault Trap (V6&V9 Only) */ + /* Set map number */ + /* pfault will have 11 bit page number and bit 0 set if op fetch */ + sim_debug(DEBUG_TRAP, &cpu_dev, + "PAGE TRAP %04x TSTAT %08x LOAD MAPS PSD1 %08x PSD2 %08x CPUSTAT %08x pfault %08x\n", + TRAPME, TRAPSTATUS, PSD1, PSD2, CPUSTATUS, pfault); + } + TRAPSTATUS = CPUSTATUS & 0x57; /* clear all trap status except cpu type */ + break; /* Go execute the trap */ + } + break; + } + } + /* we have a new PSD loaded via a LPSD or LPSDCM */ + /* finish instruction history, then continue */ + /* update cpu status word too */ + OPSD1 &= 0x87FFFFFF; /* clear the old CC's */ + OPSD1 |= PSD1 & 0x78000000; /* update the CC's in the PSD */ + /* Update other history information for this instruction */ + if (hst_lnt) { + hst[hst_p].opsd1 = OPSD1; /* update the CC in opsd1 */ + hst[hst_p].npsd1 = PSD1; /* save new psd1 */ + hst[hst_p].npsd2 = PSD2; /* save new psd2 */ + hst[hst_p].modes = MODES; /* save current mode bits */ + hst[hst_p].modes |= (CPUSTATUS & BIT24); /* save blocking mode bit */ + for (ix=0; ix<8; ix++) { + hst[hst_p].reg[ix] = GPR[ix]; /* save reg */ + hst[hst_p].reg[ix+8] = BR[ix]; /* save breg */ + } + } + + /* DEBUG_INST support code */ + /* output mapped/unmapped */ + if (MODES & BASEBIT) + BM = 'B'; + else + BM = 'N'; + if (MODES & MAPMODE) + MM = 'M'; + else + MM = 'U'; + if (CPUSTATUS & BIT24) + BK = 'B'; + else + BK = 'U'; + sim_debug(DEBUG_INST, &cpu_dev, "%c%c%c %.8x %.8x %.8x ", + BM, MM, BK, OPSD1, PSD2, OIR); + if (cpu_dev.dctrl & DEBUG_INST) { + fprint_inst(sim_deb, OIR, 0); /* display instruction */ + sim_debug(DEBUG_INST, &cpu_dev, + "\n\tR0=%.8x R1=%.8x R2=%.8x R3=%.8x", GPR[0], GPR[1], GPR[2], GPR[3]); + sim_debug(DEBUG_INST, &cpu_dev, + " R4=%.8x R5=%.8x R6=%.8x R7=%.8x\n", GPR[4], GPR[5], GPR[6], GPR[7]); + if (MODES & BASEBIT) { + sim_debug(DEBUG_INST, &cpu_dev, + "\tB0=%.8x B1=%.8x B2=%.8x B3=%.8x", BR[0], BR[1], BR[2], BR[3]); + sim_debug(DEBUG_INST, &cpu_dev, + " B4=%.8x B5=%.8x B6=%.8x B7=%.8x\n", BR[4], BR[5], BR[6], BR[7]); + } + } + continue; /* single step cpu just for now */ + } /* end while */ + + /* Simulation halted */ + return reason; +} + +/* these are the default ipl devices defined by the CPU jumpers */ +/* they can be overridden by specifying IPL device at ipl time */ +uint32 def_disk = 0x0800; /* disk channel 8, device 0 */ +uint32 def_tape = 0x1000; /* tape device 10, device 0 */ +uint32 def_floppy = 0x7ef0; /* IOP floppy disk channel 7e, device f0 */ + +/* Reset routine */ +/* do any one time initialization here for cpu */ +t_stat cpu_reset(DEVICE *dptr) +{ + int i; + t_stat devs = SCPE_OK; + + /* leave regs alone so values can be passed to boot code */ + PSD1 = 0x80000000; /* privileged, non mapped, non extended, address 0 */ + PSD2 = 0x00004000; /* blocked interrupts mode */ + MODES = (PRIVBIT | BLKMODE); /* set modes to privileged and blocked interrupts */ + CC = 0; /* no CCs too */ + CPUSTATUS = CPU_MODEL; /* clear all cpu status except cpu type */ + CPUSTATUS |= PRIVBIT; /* set privleged state bit 0 */ + CPUSTATUS |= BIT24; /* set blocked mode state bit 24 */ + CPUSTATUS |= BIT22; /* set HS floating point unit not present bit 22 */ + TRAPSTATUS = CPU_MODEL; /* clear all trap status except cpu type */ + CMCR = 0; /* No Cache Enabled */ + SMCR = 0; /* No Shared Memory Enabled */ + CMSMC = 0x00ff0a10; /* No V9 Cache/Shadow Memory Configuration */ + CSMCW = 0; /* No V9 CPU Shadow Memory Configuration */ + ISMCW = 0; /* No V9 IPU Shadow Memory Configuration */ + RDYQIN = RDYQOUT = 0; /* initialize channel ready queue */ + + devs = chan_set_devs(); /* set up the defined devices on the simulator */ + + /* set default breaks to execution tracing */ + sim_brk_types = sim_brk_dflt = SWMASK('E'); + /* zero regs */ + for (i = 0; i < 8; i++) { + GPR[i] = BOOTR[i]; /* set boot register values */ + BR[i] = 0; /* clear the registers */ + } + + /* set console switch settings */ + M[0x780>>2] = CSW; /* set console switch settings */ + + /* zero interrupt status words */ + for (i = 0; i < 112; i++) + INTS[i] = 0; /* clear interrupt status flags */ + + /* add code here to initialize the SEL32 cpu scratchpad on initial start */ + /* see if spad setup by software, if yes, leave spad alone */ + /* otherwise set the default values into the spad */ + /* CPU key is 0xECDAB897, IPU key is 0x13254768 */ + /* Keys are loaded by the O/S software during the boot loading sequence */ + if (SPAD[0xf7] != 0xecdab897) + { + int ival = 0; /* init value for concept 32 */ + + if (CPU_MODEL < MODEL_27) + ival = 0xfffffff; /* init value for 32/7x int and dev entries */ + for (i = 0; i < 1024; i++) + MAPC[i] = 0; /* clear 2048 halfword map cache */ + for (i = 0; i < 224; i++) + SPAD[i] = ival; /* init 128 devices and 96 ints in the spad */ + for (i = 224; i < 256; i++) /* clear the last 32 extries */ + SPAD[i] = 0; /* clear the spad */ + SPAD[0xf0] = 0x80; /* default Trap Table Address (TTA) */ + SPAD[0xf1] = 0x100; /* Interrupt Table Address (ITA) */ + SPAD[0xf2] = 0x700; /* IOCD Base Address */ + SPAD[0xf3] = 0x788; /* Master Process List (MPL) table address */ + SPAD[0xf4] = def_tape; /* Default IPL address from console IPL command or jumper */ + SPAD[0xf5] = PSD2; /* current PSD2 defaults to blocked */ + SPAD[0xf6] = 0; /* reserved (PSD1 ??) */ + SPAD[0xf7] = 0xecdab897; /* load the CPU key */ + SPAD[0xf8] = 0x0000f000; /* set DRT to class f (anything else is E) */ + SPAD[0xf9] = CPUSTATUS; /* set default cpu type in cpu status word */ + SPAD[0xff] = 0x00ffffff; /* interrupt level 7f 1's complament */ + } +#if 0 + /* set low memory bootstrap code */ + /* moved to boot code in sel32_chan.c so we can reset system and not destroy memory */ + M[0] = 0x02000000; /* 0x00 IOCD 1 read into address 0 */ + M[1] = 0x60000078; /* 0x04 IOCD 1 CMD Chain, Suppress incor len, 120 bytes */ + M[2] = 0x53000000; /* 0x08 IOCD 2 BKSR or RZR to re-read boot code */ + M[3] = 0x60000001; /* 0x0C IOCD 2 CMD chain,Supress incor length, 1 byte */ + M[4] = 0x02000000; /* 0x10 IOCD 3 Read into address 0 */ + M[5] = 0x000006EC; /* 0x14 IOCD 3 Read 0x6EC bytes */ +#endif + loading = 0; /* not loading yet */ + /* we are good to go or error from device setup */ + if (devs != SCPE_OK) + return devs; + return SCPE_OK; +} + +/* Memory examine */ +/* examine a 32bit memory location and return a byte */ +t_stat cpu_ex(t_value *vptr, t_addr baddr, UNIT *uptr, int32 sw) +{ + uint32 status, realaddr, prot; + uint32 addr = (baddr & 0xfffffc) >> 2; /* make 24 bit byte address into word address */ + + if (sw & SWMASK('V')) { + /* convert address to real physical address */ + status = RealAddr(addr, &realaddr, &prot, MEM_RD); + sim_debug(DEBUG_CMD, &cpu_dev, "cpu_ex Mem_read status = %02x\n", status); + if (status == ALLOK) { + *vptr = (M[realaddr] >> (8 * (3 - (baddr & 0x3)))); /* return memory contents */ + return SCPE_OK; /* we are all ok */ + } + return SCPE_NXM; /* no, none existant memory error */ + } + /* MSIZE is in 32 bit words */ + if (!MEM_ADDR_OK(addr)) /* see if address is within our memory */ + return SCPE_NXM; /* no, none existant memory error */ + if (vptr == NULL) /* any address specified by user */ + return SCPE_OK; /* no, just ignore the request */ + *vptr = (M[addr] >> (8 * (3 - (baddr & 0x3)))); /* return memory contents */ + return SCPE_OK; /* we are all ok */ +} + +/* Memory deposit */ +/* modify a byte specified by a 32bit memory location */ +/* address is byte address with bits 30,31 = 0 */ +t_stat cpu_dep(t_value val, t_addr baddr, UNIT *uptr, int32 sw) +{ + uint32 addr = (baddr & 0xfffffc) >> 2; /* make 24 bit byte address into word address */ + static const uint32 bmasks[4] = {0x00FFFFFF, 0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00}; + + /* MSIZE is in 32 bit words */ + if (!MEM_ADDR_OK(addr)) /* see if address is within our memory */ + return SCPE_NXM; /* no, none existant memory error */ + val = (M[addr] & bmasks[baddr & 0x3]) | (val << (8 * (3 - (baddr & 0x3)))); + M[addr] = val; /* set new value */ + return SCPE_OK; /* all OK */ +} + +/* set the CPU memory size */ +/* table values are in words, not bytes */ +uint32 memwds [] = { + 0x008000, /* size index 0 - 128KB = 32KW */ + 0x010000, /* 1 - 256KB = 64KW */ + 0x020000, /* 2 - 512KB = 128KW */ + 0x040000, /* 3 - 1MB = 256KW */ + 0x080000, /* 4 - 2MB = 512KW */ + 0x0c0000, /* 5 - 3MB = 768KW */ + 0x100000, /* 6 - 4MB = 1MW */ + 0x180000, /* 7 - 6MB = 1.5MW */ + 0x200000, /* 8 - 8MB = 2MW */ + 0x300000, /* 9 - 12MB = 3MW */ + 0x400000, /* 10 - 16MB = 4MW */ +}; + +t_stat cpu_set_size(UNIT *uptr, int32 sval, CONST char *cptr, void *desc) +{ + uint32 i; + uint32 sz; + int32 val = (int32)sval; + t_addr msize; + + val >>= UNIT_V_MSIZE; /* shift index right 19 bits */ + if (val >= (int32)(sizeof(memwds)/sizeof(uint32))) /* is size valid */ + return SCPE_ARG; /* nope, argument error */ + sz = memwds[val]; /* (128KB/4) << index == memory size in KW */ + if ((sz <= 0) || (sz > MAXMEMSIZE)) /* is size valid */ + return SCPE_ARG; /* nope, argument error */ + msize = sz << 2; /* Convert to words */ + if (msize < MEMSIZE) { /* is size smaller */ + uint32 mc = 0; /* yes, see if larger memory was used */ + for (i = sz-1; i < (MEMSIZE>>2); i++) + mc = mc | M[i]; /* or in any bits we might find */ + if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; /* forget update */ + } + for (i = (MEMSIZE>>2) - 1; i < sz; i++) + M[i] = 0; /* zero all of the new memory */ + cpu_unit.flags &= ~UNIT_MSIZE; /* clear old size value 0-31 */ + cpu_unit.flags |= val << UNIT_V_MSIZE; /* set new memory size index value (0-31) */ + cpu_unit.capac = (t_addr)msize; /* set new size */ + return SCPE_OK; /* we done */ +} + +/* Handle execute history */ + +/* Set history */ +t_stat +cpu_set_hist(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int32 i, lnt; + t_stat r; + + if (cptr == NULL) { /* check for any user options */ + for (i = 0; i < hst_lnt; i++) /* none, so just zero the history */ + hst[i].opsd1 = 0; /* just psd1 for now */ + hst_p = 0; /* start at the beginning */ + return SCPE_OK; /* all OK */ + } + /* the user has specified options, process them */ + lnt = (int32)get_uint(cptr, 10, HIST_MAX, &r); + if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) + return SCPE_ARG; /* arg error for bad input or too small a value */ + hst_p = 0; /* start at beginning */ + if (hst_lnt) { /* if a new length was input, resize history buffer */ + free(hst); /* out with the old */ + hst_lnt = 0; /* no length anymore */ + hst = NULL; /* and no pointer either */ + } + if (lnt) { /* see if new size specified, if so get new resized bfer */ + hst = (struct InstHistory *)calloc(sizeof(struct InstHistory), lnt); + if (hst == NULL) + return SCPE_MEM; /* allocation error, so tell user */ + hst_lnt = lnt; /* set new length */ + } + return SCPE_OK; /* we are good to go */ +} + +/* Show history */ +t_stat cpu_show_hist(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + int32 k, di, lnt; + char *cptr = (char *) desc; + t_stat r; + uint8 BM, MM, BK; /* basemode, mapped mode, blocked mode */ + struct InstHistory *h; + + if (hst_lnt == 0) /* see if show history is enabled */ + return SCPE_NOFNC; /* no, so we are out of here */ + if (cptr) { /* see if user provided a display count */ + lnt = (int32)get_uint(cptr, 10, hst_lnt, &r); /* get the count */ + if ((r != SCPE_OK) || (lnt == 0)) /* if error or 0 count */ + return SCPE_ARG; /* report argument error */ + } else + lnt = hst_lnt; /* dump all the entries */ + di = hst_p - lnt; /* work forward */ + if (di < 0) + di = di + hst_lnt; /* wrap */ + for (k = 0; k < lnt; k++) { /* print specified entries */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + /* display the instruction and results */ + if (MODES & BASEBIT) + BM = 'B'; + else + BM = 'N'; + if (MODES & MAPMODE) + MM = 'M'; + else + MM = 'U'; + if (MODES & 0x80) /* get blocked bit */ + BK = 'B'; + else + BK = 'U'; + fprintf(st, "%c%c%c %.8x %.8x %.8x ", BM, MM, BK, h->opsd1, h->npsd2, h->oir); + if (h->modes & BASEBIT) + fprint_inst(st, h->oir, SWMASK('M')); /* display basemode instruction */ + else + fprint_inst(st, h->oir, SWMASK('N')); /* display non basemode instruction */ + fprintf(st, "\n"); + fprintf(st, "\tR0=%.8x R1=%.8x R2=%.8x R3=%.8x", h->reg[0], h->reg[1], h->reg[2], h->reg[3]); + fprintf(st, " R4=%.8x R5=%.8x R6=%.8x R7=%.8x", h->reg[4], h->reg[5], h->reg[6], h->reg[7]); + if (h->modes & BASEBIT) { + fprintf(st, "\n"); + fprintf(st, "\tB0=%.8x B1=%.8x B2=%.8x B3=%.8x", h->reg[8], h->reg[9], h->reg[10], h->reg[11]); + fprintf(st, " B4=%.8x B5=%.8x B6=%.8x B7=%.8x", h->reg[12], h->reg[13], h->reg[14], h->reg[15]); + } + fprintf(st, "\n"); + } /* end for */ + return SCPE_OK; /* all is good */ +} + +/* return description for the specified device */ +const char *cpu_description (DEVICE *dptr) +{ + return "SEL 32 CPU"; /* return description */ +} + +t_stat cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf(st, "The CPU can maintain a history of the most recently executed instructions.\n"); + fprintf(st, "This is controlled by the SET CPU HISTORY and SHOW CPU HISTORY commands:\n\n"); + fprintf(st, " sim> SET CPU HISTORY clear history buffer\n"); + fprintf(st, " sim> SET CPU HISTORY=0 disable history\n"); + fprintf(st, " sim> SET CPU HISTORY=n{:file} enable history, length = n\n"); + fprintf(st, " sim> SHOW CPU HISTORY print CPU history\n"); + return SCPE_OK; +} diff --git a/SEL32/sel32_defs.h b/SEL32/sel32_defs.h new file mode 100644 index 00000000..ce5574ab --- /dev/null +++ b/SEL32/sel32_defs.h @@ -0,0 +1,535 @@ +/* sel32_defs.h: SEL-32 Concept/32 simulator definitions + + Copyright (c) 2018-2022, James C. Bevier + Portions provided by Richard Cornwell, Geert Rolf and other SIMH contributers + + 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 + JAMES C. BEVIER 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. + +*/ + +#include "sim_defs.h" /* simh simulator defns */ + +/* Simulator stop codes */ +#define STOP_IONRDY 1 /* I/O dev not ready */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_UUO 4 /* invalid opcode */ +#define STOP_INVINS 5 /* invalid instr */ +#define STOP_INVIOP 6 /* invalid I/O op */ +#define STOP_INDLIM 7 /* indirect limit */ +#define STOP_XECLIM 8 /* XEC limit */ +#define STOP_IOCHECK 9 /* IOCHECK */ +#define STOP_MMTRP 10 /* mm in trap */ +#define STOP_TRPINS 11 /* trap inst not BRM */ +#define STOP_RTCINS 12 /* rtc inst not MIN/SKR */ +#define STOP_ILLVEC 13 /* zero vector */ +#define STOP_CCT 14 /* runaway CCT */ + +/* I/O equates */ +/* Channel sense bytes set by device */ +#define SNS_BSY 0x80 /* Unit Busy */ +#define SNS_SMS 0x40 /* Status modified */ +#define SNS_CTLEND 0x20 /* Control unit end */ +#define SNS_ATTN 0x10 /* Unit attention */ +#define SNS_CHNEND 0x08 /* Channel end */ +#define SNS_DEVEND 0x04 /* Device end */ +#define SNS_UNITCHK 0x02 /* Unit check */ +#define SNS_UNITEXP 0x01 /* Unit exception */ + +/* Command masks */ +#define CCMDMSK 0xff000000 /* Mask for command */ +#define CMD_CHAN 0x00 /* Channel control */ +#define CMD_SENSE 0x04 /* Sense channel command */ +#define CMD_TIC 0x08 /* Transfer in channel */ +#define CMD_RDBWD 0x0c /* Read backward */ +/* operation types */ +#define CMD_TYPE 0x03 /* Type mask */ +#define CMD_WRITE 0x01 /* Write command */ +#define CMD_READ 0x02 /* Read command */ +#define CMD_CTL 0x03 /* Control command */ + +/* IOCD word 2 status bits */ +#define STATUS_ECHO 0x8000 /* Halt I/O and Stop I/O function */ +#define STATUS_PCI 0x4000 /* Program controlled interrupt */ +#define STATUS_LENGTH 0x2000 /* Incorrect length */ +#define STATUS_PCHK 0x1000 /* Channel program check */ +#define STATUS_CDATA 0x0800 /* Channel data check */ +#define STATUS_CCNTL 0x0400 /* Channel control check */ +#define STATUS_INTER 0x0200 /* Channel interface check */ +#define STATUS_CHAIN 0x0100 /* Channel chain check */ +#define STATUS_BUSY 0x0080 /* Device busy */ +#define STATUS_MOD 0x0040 /* Status modified */ +#define STATUS_CTLEND 0x0020 /* Controller end */ +#define STATUS_ATTN 0x0010 /* Device raised attention */ +#define STATUS_CEND 0x0008 /* Channel end */ +#define STATUS_DEND 0x0004 /* Device end */ +#define STATUS_CHECK 0x0002 /* Unit check */ +#define STATUS_EXPT 0x0001 /* Unit exception */ +#define STATUS_ERROR 0x3f03 /* bad errors */ +//#define STATUS_ERROR (STATUS_LENGTH|STATUS_PCHK|STATUS_CDATA|STATUS_CCNTL| +// STATUS_INTER|STATUS_CHAIN|STATUS_CHECK|STATUS_EXPT) + +/* Class F channel bits */ +/* bit 32 - 37 of IOCD word 2 (0-5) */ +/* ccw_flags bit assignment */ +#define FLAG_DC 0x8000 /* Data chain */ +#define FLAG_CC 0x4000 /* Chain command */ +#define FLAG_SLI 0x2000 /* Suppress length indicator */ +#define FLAG_SKIP 0x1000 /* Suppress memory write */ +#define FLAG_PCI 0x0800 /* Program controlled interrupt */ +#define FLAG_RTO 0x0400 /* Real-Time Option */ + +/* chan_byte bit assignments */ +#define BUFF_EMPTY 0x00 /* Buffer is empty */ +#define BUFF_BUSY 0x04 /* Channel program busy & empty */ +#define BUFF_NEXT 0x0C /* 0x08|0x04 Continue Channel with next IOCB */ +#define BUFF_CHNEND 0x14 /* 0x10|0x04 Channel end */ +#define BUFF_DONE 0x20 /* 0x20 Channel ready for new command */ +#define BUFF_POST 0x24 /* 0x20|0x04 Waiting for status to be posted */ + +/* chan_info bit flags */ +#define INFO_SIOCD 0x01 /* Initial IOCD from SIO if set */ +#define INFO_CEND 0x02 /* Channel End (chan_end) called if set */ +/* bits 0-5 unused */ + +#define MAX_CHAN 128 /* max channels that can be defined */ +#define SUB_CHANS 256 /* max sub channels that can be defined */ +#define MAX_DEV (MAX_CHAN * SUB_CHANS) /* max possible */ + +/* simulator devices configuration */ +#define NUM_DEVS_IOP 1 /* 1 device IOP channel controller */ +#define NUM_UNITS_IOP 1 /* 1 master IOP channel device */ +#define NUM_DEVS_MFP 1 /* 1 device MFP channel controller */ +#define NUM_UNITS_MFP 1 /* 1 master MFP channel device */ +#define NUM_DEVS_COM 2 /* 8-Line async controller */ +#define NUM_UNITS_COM 16 /* 8-Line async units */ +#define NUM_DEVS_CON 1 /* 1 I/O console controller */ +#define NUM_UNITS_CON 2 /* 2 console input & output */ +#define NUM_DEVS_MT 1 /* 1 mag tape controllers */ +#define NUM_UNITS_MT 4 /* 4 of 8 devices */ +#define NUM_DEVS_HSDP 1 /* 1 hspd disk drive controller */ +//#define NUM_UNITS_HSDP 2 /* 2 disk drive devices */ +#define NUM_UNITS_HSDP 4 /* 4 disk drive devices */ +#define NUM_DEVS_DISK 1 /* 1 dp02 disk drive controller */ +//#define NUM_UNITS_DISK 2 /* 2 disk drive devices */ +#define NUM_UNITS_DISK 4 /* 4 disk drive devices */ +#define NUM_DEVS_SCFI 1 /* 1 scfi (SCSI) disk drive units */ +//#define NUM_UNITS_SCFI 2 /* 1 of 4 disk drive devices */ +#define NUM_UNITS_SCFI 4 /* 1 of 4 disk drive devices */ +#define NUM_DEVS_SCSI 2 /* 2 scsi (MFP SCSI) scsi buss units */ +#define NUM_UNITS_SCSI 2 /* 2 scsi disk drive devices */ +#define NUM_DEVS_RTOM 1 /* 1 IOP RTOM channel */ +#define NUM_UNITS_RTOM 1 /* 1 IOP RTOM device (clock & interval timer) */ +#define NUM_DEVS_LPR 1 /* 1 IOP Line printer */ +#define NUM_UNITS_LPR 1 /* 1 IOP Line printer device */ +#define NUM_DEVS_ETHER 1 /* 1 Ethernet controller */ +#define NUM_UNITS_ETHER 16 /* 16 Ethernet devices */ + +extern DEVICE cpu_dev; /* cpu device */ +extern UNIT cpu_unit; /* the cpu unit */ +#ifdef NUM_DEVS_IOP +extern DEVICE iop_dev; /* IOP channel controller */ +#endif +#ifdef NUM_DEVS_MFP +extern DEVICE mfp_dev; /* MFP channel controller */ +#endif +#ifdef NUM_DEVS_RTOM +extern DEVICE rtc_dev; /* RTOM rtc */ +extern DEVICE itm_dev; /* RTOM itm */ +#endif +#ifdef NUM_DEVS_CON +extern DEVICE con_dev; +#endif +#ifdef NUM_DEVS_MT +extern DEVICE mta_dev; +#endif +#if NUM_DEVS_MT > 1 +extern DEVICE mtb_dev; +#endif +#ifdef NUM_DEVS_DISK +extern DEVICE dda_dev; +#endif +#if NUM_DEVS_DISK > 1 +extern DEVICE ddb_dev; +#endif +#ifdef NUM_DEVS_HSDP +extern DEVICE dpa_dev; +#endif +#if NUM_DEVS_HSDP > 1 +extern DEVICE dpb_dev; +#endif +#ifdef NUM_DEVS_SCFI +extern DEVICE sda_dev; +#endif +#if NUM_DEVS_SCFI > 1 +extern DEVICE sdb_dev; +#endif +#ifdef NUM_DEVS_SCSI +extern DEVICE sba_dev; +#endif +#if NUM_DEVS_SCSI > 1 +extern DEVICE sbb_dev; +#endif +#ifdef NUM_DEVS_COM +extern DEVICE coml_dev; +extern DEVICE com_dev; +#endif +#ifdef NUM_DEVS_LPR +extern DEVICE lpr_dev; +#endif +#ifdef NUM_DEVS_ETHER +extern DEVICE ec_dev; +#endif + +/* Memory */ + +#define MAXMEMSIZE ((16*1024*1024)/4) /* max memory size in 32bit words */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define MEM_ADDR_OK(x) (((x)) < MEMSIZE) + +/* channel program data for a chan/sub-address */ +typedef struct chp { + /* channel program values */ + UNIT *unitptr; /* Back pointer to units structure */ + uint32 chan_inch_addr; /* Current channel status dw addr in memory */ + uint32 base_inch_addr; /* Original channel status dw addr in memory */ + uint16 max_inch_addr; /* maximum inch buffer pointer */ + uint32 chan_caw; /* Channel command address word */ + uint32 ccw_addr; /* Channel address */ +#ifdef TEST_FOR_IOCL_CHANGE + uint32 new_iocla; /* start iocl address */ + uint32 new_iocd1; /* start word 1 of iocd */ + uint32 new_iocd2; /* start word 2 of iocd */ +#endif + uint32 chan_buf; /* Channel data buffer */ + uint16 ccw_count; /* Channel count */ + uint16 ccw_flags; /* Channel flags */ + uint16 chan_status; /* Channel status */ + uint16 chan_dev; /* Device on channel */ + uint8 ccw_cmd; /* Channel command and flags */ + uint8 chan_byte; /* Current byte, empty/full */ + uint8 chan_int; /* channel interrupt level */ + uint8 chan_info; /* misc flags for channel */ +} CHANP; + +/* Device information block */ +#define FIFO_SIZE 256 /* fifo to hold 128 double words of status */ +extern int32 FIFO_Put(uint16 chsa, uint32 entry); +extern int32 FIFO_Get(uint16 chsa, uint32 *old); +extern int32 FIFO_Num(uint16 chsa); + +#define IOCLQ_SIZE 32 /* fifo to hold 32 iocl cmds */ + +typedef struct ioclq { + uint32 ioclq_fifo[IOCLQ_SIZE]; + int16 ioclq_in; + int16 ioclq_out; +} IOCLQ; + +extern int32 IOCLQ_Put(IOCLQ *qptr, uint32 entry); +extern int32 IOCLQ_Get(IOCLQ *qptr, uint32 *old); +extern int32 IOCLQ_Num(IOCLQ *qptr); + +typedef struct dib { + /* Pre start I/O operation */ + t_stat (*pre_io)(UNIT *uptr, uint16 chan); + /* Start a channel command SIO */ + t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd); + /* Halt I/O HIO */ + t_stat (*halt_io)(UNIT *uptr); /* Halt I/O */ + /* Test I/O STOPIO */ + t_stat (*stop_io)(UNIT *uptr); /* Stop I/O */ + /* Test I/O TESTIO */ + t_stat (*test_io)(UNIT *uptr); /* Test I/O */ + /* Reset Controller RSCTL */ + t_stat (*rsctl_io)(UNIT *uptr); /* Reset Controller */ + /* Reset Controller RSCHNL */ + t_stat (*rschnl_io)(UNIT *uptr); /* Reset Channel */ + /* Post I/O processing */ + t_stat (*iocl_io)(CHANP *chp, int32 tic_ok); /* IOCL processing */ + /* Controller init */ + void (*dev_ini)(UNIT *, t_bool); /* init function */ + UNIT *units; /* Pointer to units structure */ + CHANP *chan_prg; /* Pointer to channel program */ + IOCLQ *ioclq_ptr; /* pointer to array of IOCLQ entries */ + uint8 numunits; /* number of units */ + uint8 mask; /* device mask */ + uint16 chan_addr; /* parent channel address */ + uint32 chan_fifo_in; /* fifo input index */ + uint32 chan_fifo_out; /* fifo output index */ + uint32 chan_fifo[FIFO_SIZE]; /* interrupt status fifo for each channel */ +} DIB; + +extern DIB *dib_unit[MAX_DEV]; /* Pointer to Device info block */ +extern DIB *dib_chan[MAX_CHAN]; /* Pointer to channel mux dib */ + +/* defined in upper 16 bits of dptr->flags */ +#define DEV_CHAN (1 << DEV_V_UF) /* Device is channel mux if set */ +#define DEV_V_UF2 (DEV_V_UF+1) /* current usage */ +#define DEV_BUF_NUM(x) (((x) & 07) << DEV_V_UF2) +#define GET_DEV_BUF(x) (((x) >> DEV_V_UF2) & 07) + +#ifdef NOT_USED_NOW +//#define DEV_V_ADDR DEV_V_UF /* Pointer to device address (16) */ +//#define DEV_V_DADDR (DEV_V_UF + 8) /* Device address */ +//#define DEV_ADDR_MASK (0x7f << DEV_V_DADDR) /* 24 bits shift */ +//#define DEV_V_UADDR (DEV_V_UF) /* Device address in Unit */ +//#define DEV_UADDR (1 << DEV_V_UADDR) +//#define GET_DADDR(x) (0x7f & ((x) >> DEV_V_ADDR)) +//#define DEV_ADDR(x) ((x) << DEV_V_ADDR) +//#define PROTECT_V UNIT_V_UF+15 +//#define PROTECT (1 << PROTECT_V) +#endif + +/* defined in rightmost 8 bits of upper 16 bits of uptr->flags */ +/* allow 255 type disks */ +#define UNIT_SUBCHAN (1 << (UNIT_V_UF_31)) +#define UNIT_V_TYPE (UNIT_V_UF + 0) +#define UNIT_TYPE (0xff << UNIT_V_TYPE) +/* get & set disk types */ +#define GET_TYPE(x) ((UNIT_TYPE & (x)) >> UNIT_V_TYPE) +#define SET_TYPE(x) (UNIT_TYPE & ((x) << UNIT_V_TYPE)) + +/* defined in uptr->u3 upper 16 bits */ +/* DEV 0x7F000000 UNIT 0x00ff0000 */ +#define UNIT_V_ADDR 16 +#define UNIT_ADDR_MASK (0x7fff << UNIT_V_ADDR) +#define GET_UADDR(x) ((UNIT_ADDR_MASK & x) >> UNIT_V_ADDR) +#define UNIT_ADDR(x) ((x) << UNIT_V_ADDR) + +/* Debugging controls */ +#define DEBUG_CMD 0x0000001 /* Show device commands */ +#define DEBUG_DATA 0x0000002 /* Show data transfers */ +#define DEBUG_DETAIL 0x0000004 /* Show details */ +#define DEBUG_INFO 0x0000004 /* Show details */ +#define DEBUG_EXP 0x0000008 /* Show error conditions */ +#define DEBUG_INST 0x0000010 /* Show instructions */ +#define DEBUG_XIO 0x0000020 /* Show XIO I/O instructions */ +#define DEBUG_IRQ 0x0000040 /* Show IRQ requests */ +#define DEBUG_TRAP 0x0000080 /* Show TRAP requests */ + +extern DEBTAB dev_debug[]; + +/* defines for all programs */ +#define RMASK 0x0000FFFF /* right hw 16 bit mask */ +#define LMASK 0xFFFF0000 /* left hw 16 bit mask */ +#define FMASK 0xFFFFFFFF /* 32 bit mask */ +#define DMASK 0xFFFFFFFFFFFFFFFFLL /* 64 bit all bits mask */ +#define D48LMASK 0xFFFFFFFFFFFF0000LL /* 64 bit left 48 bits mask */ +#define D32LMASK 0xFFFFFFFF00000000LL /* 64 bit left 32 bits mask */ +#define D32RMASK 0x00000000FFFFFFFFLL /* 64 bit right 32 bits mask */ +#define MSIGN 0x80000000 /* 32 bit minus sign */ +#define DMSIGN 0x8000000000000000LL /* 64 bit minus sign */ +#define FSIGN 0x80000000 /* 32 bit minus sign */ +/* sign extend 16 bit value to uint32 */ +#define SEXT16(x) (x&0x8000?(uint32)(((uint32)x&RMASK)|LMASK):(uint32)x) +/* sign extend 16 bit value to uint64 */ +#define DSEXT16(x) (x&0x8000?(l_uint64)(((l_uint64)x&RMASK)|D48LMASK):(t_uint64)x) +/* sign extend 32 bit value to uint64 */ +#define DSEXT32(x) (x&0x8000?(l_uint64)(((l_uint64)x&D32RMASK)|D32LMASK):(t_uint64)x) +#define NEGATE32(val) ((~val) + 1) /* negate a value 16/32/64 bits */ + +/* defined in rightmost 8 bits of upper 16 bits of uptr->flags */ +#define UNIT_V_MODEL (UNIT_V_UF + 0) +#define UNIT_MODEL (7 << UNIT_V_MODEL) +#define MODEL(x) (x << UNIT_V_MODEL) +#define UNIT_V_MSIZE (UNIT_V_MODEL + 3) +#define UNIT_MSIZE (0x1F << UNIT_V_MSIZE) +#define MEMAMOUNT(x) (x << UNIT_V_MSIZE) +#define CPU_MODEL ((cpu_unit.flags >> UNIT_V_MODEL) & 0x7) /* cpu model 0-7 */ + +#define MODEL_55 0 /* 512K Mode Only */ +#define MODEL_75 1 /* Extended */ +#define MODEL_27 2 /* */ +#define MODEL_67 3 /* */ +#define MODEL_87 4 /* */ +#define MODEL_97 5 /* */ +#define MODEL_V6 6 /* V6 CPU */ +#define MODEL_V9 7 /* V9 CPU */ + +#define TMR_RTC 1 /* RTC will not work if set to 0!! */ +//#define TMR_RTC 0 + +#define HIST_MIN 64 +#define HIST_MAX 10000 +#define HIST_PC 0x80000000 + +/* CC defs Held in CC */ +#define CC1BIT 0x40000000 /* CC1 in PSD1 */ +#define CC2BIT 0x20000000 /* CC2 in PSD1 */ +#define CC3BIT 0x10000000 /* CC3 in PSD1 */ +#define CC4BIT 0x08000000 /* CC4 in PSD1 */ + +#define MAPMODE 0x40 /* Map mode, PSD 2 bit 0 */ +#define RETMODE 0x20 /* Retain current maps, PSD 2 bit 15 */ +#define RETBLKM 0x10 /* Set retain blocked mode, PSD 2 bit 16 */ +#define BLKMODE 0x08 /* Set blocked mode, PSD 2 bit 17 */ + +/* PSD mode bits in PSD words 1&2 variable */ +#define PRIVBIT 0x80000000 /* Privileged mode PSD 1 bit 0 */ +#define EXTDBIT 0x04000000 /* Extended Addressing PSD 1 bit 5 */ +#define BASEBIT 0x02000000 /* Base Mode PSD 1 bit 6 */ +#define AEXPBIT 0x01000000 /* Arithmetic exception PSD 1 bit 7 */ + +#define MAPBIT 0x80000000 /* Map mode, PSD 2 bit 0 */ +#define RETMBIT 0x00010000 /* Retain current maps, PSD 2 bit 15 */ +#define RETBBIT 0x00008000 /* Retain current blocking state, PSD 2 bit 16 */ +#define SETBBIT 0x00004000 /* Set blocked mode, PSD 2 bit 17 */ + +/* Trap Table Address in memory is pointed to by SPAD 0xF0 */ +#define POWERFAIL_TRAP 0x80 /* Power fail trap */ +#define POWERON_TRAP 0x84 /* Power-On trap */ +#define MEMPARITY_TRAP 0x88 /* Memory Parity Error trap */ +#define NONPRESMEM_TRAP 0x8C /* Non Present Memory trap */ +#define UNDEFINSTR_TRAP 0x90 /* Undefined Instruction Trap */ +#define PRIVVIOL_TRAP 0x94 /* Privlege Violation Trap */ +#define SVCCALL_TRAP 0x98 /* Supervisor Call Trap */ +#define MACHINECHK_TRAP 0x9C /* Machine Check Trap */ +#define SYSTEMCHK_TRAP 0xA0 /* System Check Trap */ +#define MAPFAULT_TRAP 0xA4 /* Map Fault Trap */ +#define IPUUNDEFI_TRAP 0xA8 /* IPU Undefined Instruction Trap */ +#define SIGNALIPU_TRAP 0xAC /* Signal IPU/CPU Trap */ +#define ADDRSPEC_TRAP 0xB0 /* Address Specification Trap */ +#define CONSOLEATN_TRAP 0xB4 /* Console Attention Trap */ +#define PRIVHALT_TRAP 0xB8 /* Privlege Mode Halt Trap */ +#define AEXPCEPT_TRAP 0xBC /* Arithmetic Exception Trap */ +#define CACHEERR_TRAP 0xC0 /* Cache Error Trap (V9 Only) */ +#define DEMANDPG_TRAP 0xC4 /* Demand Page Fault Trap (V6&V9 Only) */ + +/* Errors returned from various functions */ +#define ALLOK 0x0000 /* no error, all is OK */ +#define MAPFLT MAPFAULT_TRAP /* map fault error */ +#define NPMEM NONPRESMEM_TRAP /* non present memory */ +#define MPVIOL PRIVVIOL_TRAP /* memory protection violation */ +#define DMDPG DEMANDPG_TRAP /* Demand Page Fault Trap (V6&V9 Only) */ + +/* general instruction decode equates */ +#define IND 0x00100000 /* indirect bit in instruction, bit 11 */ +#define F_BIT 0x00080000 /* byte flag addressing bit 11 in instruction */ +#define C_BITS 0x00000003 /* byte number or hw, dw, dw flags bits 30 & 31 */ +#define BIT0 0x80000000 /* general use for bit 0 testing */ +#define BIT1 0x40000000 /* general use for bit 1 testing */ +#define BIT2 0x20000000 /* general use for bit 2 testing */ +#define BIT3 0x10000000 /* general use for bit 3 testing */ +#define BIT4 0x08000000 /* general use for bit 4 testing */ +#define BIT5 0x04000000 /* general use for bit 5 testing */ +#define BIT6 0x02000000 /* general use for bit 6 testing */ +#define BIT7 0x01000000 /* general use for bit 7 testing */ +#define BIT8 0x00800000 /* general use for bit 8 testing */ +#define BIT9 0x00400000 /* general use for bit 9 testing */ +#define BIT10 0x00200000 /* general use for bit 10 testing */ +#define BIT11 0x00100000 /* general use for bit 11 testing */ +#define BIT12 0x00080000 /* general use for bit 12 testing */ +#define BIT13 0x00040000 /* general use for bit 13 testing */ +#define BIT14 0x00020000 /* general use for bit 14 testing */ +#define BIT15 0x00010000 /* general use for bit 15 testing */ +#define BIT16 0x00008000 /* general use for bit 16 testing */ +#define BIT17 0x00004000 /* general use for bit 17 testing */ +#define BIT18 0x00002000 /* general use for bit 18 testing */ +#define BIT19 0x00001000 /* general use for bit 19 testing */ +#define BIT20 0x00000800 /* general use for bit 20 testing */ +#define BIT21 0x00000400 /* general use for bit 21 testing */ +#define BIT22 0x00000200 /* general use for bit 22 testing */ +#define BIT23 0x00000100 /* general use for bit 23 testing */ +#define BIT24 0x00000080 /* general use for bit 24 testing */ +#define BIT25 0x00000040 /* general use for bit 25 testing */ +#define BIT26 0x00000020 /* general use for bit 26 testing */ +#define BIT27 0x00000010 /* general use for bit 27 testing */ +#define BIT28 0x00000008 /* general use for bit 28 testing */ +#define BIT29 0x00000004 /* general use for bit 29 testing */ +#define BIT30 0x00000002 /* general use for bit 30 testing */ +#define BIT31 0x00000001 /* general use for bit 31 testing */ +#define MASK16 0x0000FFFF /* 16 bit address mask */ +#define MASK19 0x0007FFFF /* 19 bit address mask */ +#define MASK20 0x000FFFFF /* 20 bit address mask */ +#define MASK24 0x00FFFFFF /* 24 bit address mask */ +#define MASK32 0xFFFFFFFF /* 32 bit address mask */ + +/* SPAD int entry equates, entries accessed by interrupt level number */ +#define SINT_RAML 0x80000000 /* ram loaded (n/u) */ +#define SINT_EWCS 0x40000000 /* Enabled channel WCS executed (XIO) */ +#define SINT_ACT 0x20000000 /* Interrupt active when set (copy is in INTS */ +#define SINT_ENAB 0x10000000 /* Interrupt enabled when set (copy is in INTS */ +#define SINT_EXTL 0x00800000 /* IOP/RTOM ext interrupt if set, I/O if not set (copy in INTS) */ + +/* INTS int entry equates, entries accessed by interrupt level number */ +#define INTS_NU1 0x80000000 /* Not used */ +#define INTS_REQ 0x40000000 /* Interrupt is requesting (use bit 1) */ +#define INTS_ACT 0x20000000 /* Interrupt active when set (copy is of SPAD */ +#define INTS_ENAB 0x10000000 /* Interrupt enabled when set (copy is of SPAD */ +#define INTS_EXTL 0x00800000 /* IOP/RTOM ext interrupt if set, I/O if not set (copy of SPAD) */ + +/* ReadAddr memory access requested */ +#define MEM_RD 0x0 /* read memory */ +#define MEM_WR 0x1 /* write memory */ +#define MEM_EX 0x2 /* execute memory */ + +/* Rename of global PC variable to avoid namespace conflicts on some platforms */ +#define PC PC_Global + +/* memory access macros */ +/* The RMW and WMW macros are used to read/write memory words */ +/* RMW(addr) or WMW(addr, data) where addr is a byte alligned word address */ + +#define RMB(a) ((M[(a)>>2]>>(8*(3-(a&3))))&0xff) /* read memory addressed byte */ +#define RMH(a) ((a)&2?(M[(a)>>2]&RMASK):(M[(a)>>2]>>16)&RMASK) /* read memory addressed halfword */ +#define RMW(a) (M[((a)&MASK24)>>2]) /* read memory addressed word */ +#define WMW(a,d) (M[((a)&MASK24)>>2]=d) /* write memory addressed word */ +/* write halfword to memory address */ +#define WMH(a,d) ((a)&2?(M[(a)>>2]=(M[(a)>>2]&LMASK)|((d)&RMASK)):(M[(a)>>2]=(M[(a)>>2]&RMASK)|((d)<<16))) +/* write byte to memory */ +#define WMB(a,d) (M[(a)>>2]=(((M[(a)>>2])&(~(0xff<<(8*(3-(a&3))))))|((d&0xff)<<(8*(3-(a&3)))))) + +/* map register access macros */ +/* The RMR and WMR macros are used to read/write the MAPC cache registers */ +/* RMR(addr) or WMR(addr, data) where addr is a half word alligned address */ +/* read map register halfword from cache address */ +#define RMR(a) ((a)&2?(MAPC[(a)>>2]&RMASK):(MAPC[(a)>>2]>>16)&RMASK) +/* write halfword map register to MAP cache address */ +#define WMR(a,d) ((a)&2?(MAPC[(a)>>2]=(MAPC[(a)>>2]&LMASK)|((d)&RMASK)):(MAPC[(a)>>2]=(MAPC[(a)>>2]&RMASK)|((d)<<16))) + +/* Definitions for commonly used functions */ +extern t_stat set_dev_addr(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +extern t_stat show_dev_addr(FILE * st, UNIT *uptr, int32 v, CONST void *desc); +extern void chan_end(uint16 chan, uint16 flags); +extern int chan_read_byte(uint16 chsa, uint8 *data); +extern int chan_write_byte(uint16 chsa, uint8 *data); +extern void set_devattn(uint16 addr, uint16 flags); +extern void set_devwake(uint16 chsa, uint16 flags); +extern t_stat chan_boot(uint16 addr, DEVICE *dptr); +extern int test_write_byte_end(uint16 chsa); +extern DEVICE *get_dev(UNIT *uptr); +extern t_stat set_inch(UNIT *uptr, uint32 inch_addr, uint32 num_inch); /* set inch addr */ +extern CHANP *find_chanp_ptr(uint16 chsa); /* find chanp pointer */ + +extern uint32 M[]; /* our memory */ +extern uint32 SPAD[]; /* cpu SPAD memory */ +extern uint32 attention_trap; +extern uint32 RDYQ[]; /* ready queue */ +extern uint32 RDYQIN; /* input index */ +extern uint32 RDYQOUT; /* output index */ +#define RDYQ_SIZE 128 +extern int32 RDYQ_Put(uint32 entry); +extern int32 RDYQ_Get(uint32 *old); +extern int32 RDYQ_Num(void); + +extern char *dump_mem(uint32 mp, int cnt); +extern char *dump_buf(uint8 *mp, int32 off, int cnt); + +#define get_chan(chsa) ((chsa>>8)&0x7f) /* get channel number from ch/sa */ + diff --git a/SEL32/sel32_disk.c b/SEL32/sel32_disk.c new file mode 100644 index 00000000..ca66aad9 --- /dev/null +++ b/SEL32/sel32_disk.c @@ -0,0 +1,3482 @@ +/* sel32_disk.c: SEL-32 2311/2314 Disk Processor II + + Copyright (c) 2018-2022, James C. Bevier + Portions provided by Richard Cornwell and other SIMH contributers + + 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 + JAMES C. BEVIER 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. +*/ + +#include "sel32_defs.h" + +/* uncomment to use fast sim_activate times when running UTX */ +/* UTX gets an ioi error for dm0801 if slow times are used */ +/* dm0801 is not even a valid unit number for UDP controller */ +#define FAST_FOR_UTX + +#if NUM_DEVS_DISK > 0 + +#define UNIT_DISK UNIT_ATTABLE | UNIT_IDLE | UNIT_DISABLE + +/* useful conversions */ +/* Fill STAR value from cyl, trk, sec data */ +#define CHS2STAR(c,h,s) (((c<<16) & LMASK)|((h<<8) & 0xff00)|(s & 0xff)) +/* convert STAR value to number of sectors */ +#define STAR2SEC(star,spt,spc) ((star&0xff)+(((star>>8)&0xff)*spt)+(((star>>16)&0xffff)*spc)) +/* convert STAR value to number of heads or tracks */ +#define STAR2TRK(star,tpc) (((star>>16)&0xffff)*tpc+((star>>8)&0x0ff)) +/* convert STAR value to number of cylinders */ +#define STAR2CYL(star) ((star>>16)&RMASK) +/* convert byte value to number of sectors mod sector size */ +#define BYTES2SEC(bytes,ssize) (((bytes) + (ssize-1)) >> 10) +/* get sectors per track for specified type */ +#define SPT(type) (disk_type[type].spt) +/* get sectors per cylinder for specified type */ +#define SPC(type) (disk_type[type].spt*disk_type[type].nhds) +/* get number of tracks for specified type */ +#define TRK(type) (disk_type[type].cyl*disk_type[type].nhds) +/* get number of cylinders for specified type */ +#define CYL(type) (disk_type[type].cyl) +/* get number of heads for specified type */ +#define HDS(type) (disk_type[type].nhds) +/* get disk capacity in sectors for specified type */ +#define CAP(type) (CYL(type)*HDS(type)*SPT(type)) +/* get number of bytes per sector for specified type */ +#define SSB(type) (disk_type[type].ssiz*4) +/* get disk capacity in bytes for specified type */ +#define CAPB(type) (CAP(type)*SSB(type)) +/* get disk geometry as STAR value for specified type */ +#define GEOM(type) (CHS2STAR(CYL(type),HDS(type),SPT(type))) + +/* INCH command information */ +/* +WD 0 - Data address +WD 1 - Flags - 0 -36 byte count + +Data - 224 word INCH buffer address (SST) +WD 1 Drive 0 Attribute register +WD 2 Drive 1 Attribute register +WD 3 Drive 2 Attribute register +WD 4 Drive 3 Attribute register +WD 5 Drive 4 Attribute register +WD 6 Drive 5 Attribute register +WD 7 Drive 6 Attribute register +WD 8 Drive 7 Attribute register + +Memory attribute register layout +bits 0-7 - Flags + bits 0&1 - 00=Reserved, 01=MHD, 10=FHD, 11=MHD with FHD option + bit 2 - 1=Cartridge module drive + bit 3 - 0=Reserved + bit 4 - 1=Drive not present + bit 5 - 1=Dual Port + bit 6&7 - 0=Blk size 00=768 byte blk + 01=1024 byte blk + 10=2048 byte blk + 11=Unassigned +bits 8-15 - sector count (sectors per track)(F16=16, F20=20) +bits 16-23 - MHD Head count (number of heads on MHD) +bits 24-31 - FHD head count (number of heads on FHD or number head on FHD option of + mini-module) +*/ + +/* 224 word INCH Buffer layout */ +/* 128 word subchannel status storage (SST) */ +/* 66 words of program status queue (PSQ) */ +/* 26 words of scratchpad */ +/* 4 words of label buffer registers */ + +/************************************/ +/* track label definations 34 bytes */ + /* for track 0, write max cyl/head/sec values in 0-3 */ + /* otherwise write current values */ +/* +0 short lcyl; cylinder +2 char ltkn; head or track number +3 char lid; track label id (0xff means last track) +4 char lflg1; track status flags + bit 0 good trk + 1 alternate trk + 2 spare trk + 3 reserved trk + 4 defective trk + 5 last track + 6-7 n/u = 0 +5 char lflg2; + bit 0 write lock + 1 write protected + 2-7 n/u = 0 +6 short lspar1; n/u = 0 +8 short lspar2; n/u = 0 +10 short ldef1; defect #1 sec and byte position + * for track 0 write DMAP + * write sector number of cyl-4, hds-2, sec 0 value in 12-15 + * otherwise write current values +12 short ldef2; defect #2 sec and byte position +14 short ldef3; defect #3 sec and byte position + * for track 0 write UMAP which is DMAP - 2 * SPT + * write sector number of cyl-4, hds-3, sec 0 value in 16-19 + * otherwise write current values +16 short ladef1; defect #1 abs position +18 short ladef2; defect #2 abs position +20 short ladef3; defect #3 abs position +22 short laltcyl; alternate cylinder number or return cyl num +24 char lalttk; alrernate track number or return track num +25 char ldscnt; data sector count 16/20 +26 char ldatrflg; device attributes + bit 0 n/u + 1 disk is mhd + 2 n/u + 3 n/u + 4 n/u + 5 dual ported + 6/7 00 768 bytes/blk + 01 1024 bytes/blk + 10 2048 bytes/blk +27 char ldatrscnt; sectors per track (again) +28 char ldatrmhdc; MHD head count +29 char ldatrfhdc; FHD head count +30 uint32 lcrc; Label CRC-32 value + */ + +/*************************************/ +/* sector label definations 34 bytes */ +/* +0 short lcyl; cylinder number +2 char lhd; head number +3 char lsec; sec # 0-15 or 0-19 for 16/20 format +4 char lflg1; track/sector status flags + bit 0 good sec + 1 alternate sec + 2 spare sec + 3 reserved sec + 4 defective sec + 5 last sec + 6-7 n/u = 0 +5 char lflg2; + bit 0 write lock + 1 write protected + 2-7 n/u = 0 +6 short lspar1; n/u = 0 +8 short lspar2; n/u = 0 +10 short ldef1; defect #1 sec and byte position +12 short ldef2; defect #2 sec and byte position +14 short ldef3; defect #3 sec and byte position + * for sec 1 UTX prep will write UMAP, which is DMAP - 1 * SPT + * write sector number of cyl-4, hds-3, sec 0 value in 16-19 + * otherwise write zeros +16 short lspar3; n/u = 0 +18 short lspar4; n/u = 0 +20 short lspar5; n/u = 0 +22 short laltcyl; alternate cylinder number or return cyl num +24 char lalttk; alrernate track number or return track num +25 char ldscnt; data sector count 16/20 +26 char ldatrflg; device attributes + bit 0 n/u + 1 disk is mhd + 2 n/u + 3 n/u + 4 n/u + 5 dual ported + 6/7 00 768 bytes/blk + 01 1024 bytes/blk + 10 2048 bytes/blk +27 char ldatrscnt; sectors per track (again) +28 char ldatrmhdc; MHD head count +29 char ldatrfhdc; FHD head count +30 uint32 lcrc; Label CRC-32 value + */ + +#define CMD u3 +/* u3 */ +/* in u3 is device command code and status */ +#define DSK_CMDMSK 0x00ff /* Command being run */ +#define DSK_STAR 0x0100 /* STAR value in u4 */ +#define DSK_NU2 0x0200 /* */ +#define DSK_READDONE 0x0400 /* Read finished, end channel */ +#define DSK_ENDDSK 0x0800 /* Sensed end of disk */ +#define DSK_SEEKING 0x1000 /* Disk is currently seeking */ +#define DSK_READING 0x2000 /* Disk is reading data */ +#define DSK_WRITING 0x4000 /* Disk is writing data */ +#define DSK_BUSY 0x8000 /* Disk is busy */ +/* commands */ +#define DSK_INCH 0x00 /* Initialize channel */ +#define DSK_INCH2 0xF0 /* Initialize channel for processing */ +#define DSK_WD 0x01 /* Write data */ +#define DSK_RD 0x02 /* Read data */ +#define DSK_NOP 0x03 /* No operation */ +#define DSK_SNS 0x04 /* Sense */ +#define DSK_SCK 0x07 /* Seek cylinder, track, sector */ +#define DSK_TIC 0x08 /* Transfer in channel */ +#define DSK_FNSK 0x0B /* Format for no skip */ +#define DSK_LPL 0x13 /* Lock protected label */ +#define DSK_LMR 0x1F /* Load mode register */ +#define DSK_RES 0x23 /* Reserve */ +#define DSK_WSL 0x31 /* Write sector label */ +#define DSK_RSL 0x32 /* Read sector label */ +#define DSK_REL 0x33 /* Release */ +#define DSK_XEZ 0x37 /* Rezero */ +#define DSK_POR 0x43 /* Priority Override */ +#define DSK_IHA 0x47 /* Increment head address */ +#define DSK_SRM 0x4F /* Set reserve track mode */ +#define DSK_WTL 0x51 /* Write track label */ +#define DSK_RTL 0x52 /* Read track label */ +#define DSK_XRM 0x5F /* Reset reserve track mode */ +#define DSK_RAP 0xA2 /* Read angular positions */ +#define DSK_TESS 0xAB /* Test STAR (subchannel target address register) */ +#define DSK_REC 0xB2 /* Read ECC correction mask */ +#define DSK_ICH 0xFF /* Initialize controller */ + +#define STAR u4 +/* u4 - sector target address register (STAR) */ +/* Holds the current cylinder, head(track), sector */ +#define DISK_CYL 0xFFFF0000 /* cylinder mask */ +#define DISK_TRACK 0x0000FF00 /* track mask */ +#define DISK_SECTOR 0x000000ff /* sector mask */ + +#define SNS u5 +/* u5 */ +/* Sense byte 0 - mode register */ +#define SNS_DROFF 0x80000000 /* Drive Carriage will be offset */ +#define SNS_TRKOFF 0x40000000 /* Track offset: 0=positive, 1=negative */ +#define SNS_RDTMOFF 0x20000000 /* Read timing offset = 1 */ +#define SNS_RDSTRBT 0x10000000 /* Read strobe timing: 1=positive, 0=negative */ +#define SNS_DIAGMOD 0x08000000 /* Diagnostic Mode ECC Code generation and checking */ +#define SNS_RSVTRK 0x04000000 /* Reserve Track mode: 1=OK to write, 0=read only */ +#define SNS_FHDOPT 0x02000000 /* FHD or FHD option = 1 */ +#define SNS_RESERV 0x01000000 /* Reserved */ + +/* Sense byte 1 */ +#define SNS_CMDREJ 0x800000 /* Command reject */ +#define SNS_INTVENT 0x400000 /* Unit intervention required */ +#define SNS_SPARE1 0x200000 /* Spare */ +#define SNS_EQUCHK 0x100000 /* Equipment check */ +#define SNS_DATCHK 0x080000 /* Data Check */ +#define SNS_OVRRUN 0x040000 /* Data overrun/underrun */ +#define SNS_DSKFERR 0x020000 /* Disk format error */ +#define SNS_DEFTRK 0x010000 /* Defective track encountered */ + +/* Sense byte 2 */ +#define SNS_LAST 0x8000 /* Last track flag encountered */ +#define SNS_AATT 0x4000 /* At Alternate track */ +#define SNS_WPER 0x2000 /* Write protection error */ +#define SNS_WRL 0x1000 /* Write lock error */ +#define SNS_MOCK 0x0800 /* Mode check */ +#define SNS_INAD 0x0400 /* Invalid memory address */ +#define SNS_RELF 0x0200 /* Release fault */ +#define SNS_CHER 0x0100 /* Chaining error */ + +/* Sense byte 3 */ +#define SNS_REVL 0x80 /* Revolution lost */ +#define SNS_DADE 0x40 /* Disc addressing or seek error */ +#define SNS_BUCK 0x20 /* Buffer check */ +#define SNS_ECCS 0x10 /* ECC error in sector label */ +#define SNS_ECCD 0x08 /* ECC error in data */ +#define SNS_ECCT 0x04 /* ECC error in track label */ +#define SNS_RTAE 0x02 /* Reserve track access error */ +#define SNS_UESS 0x01 /* Uncorrectable ECC error */ + +#define SNS2 us9 +/* us9 */ +/* us9 holds bytes 4 & 5 of the status for the drive */ + +#define LASTCNT us10 +/* us10 */ +/* us10 holds original read/write byte count from iocd */ + +/* Sense byte 4 */ +#define SNS_SEND 0x8000 /* Seek End */ +#define SNS_USEL 0x4000 /* Unit Selected */ +#define SNS_SPC0 0x2000 /* Sector Pulse Count B0 */ +#define SNS_SPC1 0x1000 /* Sector Pulse Count B1 */ +#define SNS_SPC2 0x0800 /* Sector Pulse Count B2 */ +#define SNS_SPC3 0x0400 /* Sector Pulse Count B3 */ +#define SNS_SPC4 0x0200 /* Sector Pulse Count B4 */ +#define SNS_SPC5 0x0100 /* Sector Pulse Count B5 */ + +/* Sense byte 5 */ +#define SNS_FLT 0x80 /* Disk Drive fault */ +#define SNS_SKER 0x40 /* Seek error */ +#define SNS_ONC 0x20 /* On Cylinder */ +#define SNS_UNR 0x10 /* Unit Ready */ +#define SNS_WRP 0x08 /* Write Protected */ +#define SNS_BUSY 0x04 /* Drive is busy */ +#define SNS_NU1 0x02 /* Spare 1 */ +#define SNS_NU2 0x01 /* Spare 2 */ + +#define CHS u6 +/* u6 */ +/* u6 holds the current cyl, hd, sec for the drive */ + +/* this attribute information is provided by the INCH command */ +/* for each device and is not used. It is reconstructed from */ +/* the disk_t structure data for the assigned disk */ +/* +bits 0-7 - Flags + bits 0&1 - 00=Reserved, 01=MHD, 10=FHD, 11=MHD with FHD option + bit 2 - 1=Cartridge module drive + bit 3 - 0=Reserved + bit 4 - 1=Drive not present + bit 5 - 1=Dual Port + bit 6 - 0=Reserved 00 768 byte sec + bit 7 - 0=Reserved 01 1024 byte sec +bits 8-15 - sector count (sectors per track)(F16=16, F20=20) +bits 16-23 - MHD Head count (number of heads on MHD) +bits 24-31 - FHD head count (number of heads on FHD or number head on FHD option of + mini-module) +*/ + +/* Not Used up7 */ + +static uint8 obuf[1024], bbuf[1024]; +static uint32 decc[512] = {0}; + +/* disk definition structure */ +struct disk_t +{ + const char *name; /* Device ID Name */ + uint16 nhds; /* Number of heads */ + uint16 ssiz; /* sector size in words */ + uint16 spt; /* # sectors per track(head) */ + uint16 ucyl; /* Number of cylinders used */ + uint16 cyl; /* Number of cylinders on disk */ + uint8 type; /* Device type code */ + /* bit 1 mhd */ + /* bits 6/7 = 0 768 byte blk */ /* not used on UDP/DPII */ + /* = 1 1024 byte blk */ /* not used on UDP/DPII */ +} + +disk_type[] = +{ + /* Class F Disc Devices */ + /* For MPX */ +#ifndef NOTFORMPX1X + {"MH040", 5, 192, 20, 407, 411, 0x40}, /* 0 411 40M XXXX */ + {"MH080", 5, 192, 20, 819, 823, 0x40}, /* 1 823 80M 8138 */ + {"MH160", 10, 192, 20, 819, 823, 0x40}, /* 2 823 160M 8148 */ + {"MH300", 19, 192, 20, 819, 823, 0x40}, /* 3 823 300M 8127 */ + {"MH600", 40, 192, 20, 839, 843, 0x40}, /* 4 843 600M 8155 */ +#else + {"MH040", 5, 192, 20, 400, 411, 0x40}, /* 0 411 40M XXXX */ + {"MH080", 5, 192, 20, 800, 823, 0x40}, /* 1 823 80M 8138 */ + {"MH160", 10, 192, 20, 800, 823, 0x40}, /* 2 823 160M 8148 */ + {"MH300", 19, 192, 20, 800, 823, 0x40}, /* 3 823 300M 8127 */ + {"MH600", 40, 192, 20, 800, 843, 0x40}, /* 4 843 600M 8155 */ +#endif + /* For UTX */ + {"9342", 5, 256, 16, 819, 823, 0x41}, /* 5 823 80M XXXX */ + {"8148", 10, 256, 16, 819, 823, 0x41}, /* 6 823 160M 8148 */ + {"9346", 19, 256, 16, 819, 823, 0x41}, /* 7 823 300M */ + {"8858", 24, 256, 16, 707, 711, 0x41}, /* 8 711 340M */ + {"8887", 10, 256, 35, 819, 823, 0x41}, /* 9 823 340M */ + {"8155", 40, 256, 16, 839, 843, 0x41}, /* 10 843 675M */ + {"8888", 16, 256, 43, 861, 865, 0x41}, /* 11 823 674M 8888 DP689 */ + {NULL, 0} +}; + +t_stat disk_preio(UNIT *uptr, uint16 chan) ; +t_stat disk_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) ; +t_stat disk_haltio(UNIT *uptr); +t_stat disk_iocl(CHANP *chp, int32 tic_ok); +t_stat disk_srv(UNIT *uptr); +t_stat disk_boot(int32 unitnum, DEVICE *dptr); +void disk_ini(UNIT *, t_bool); +t_stat disk_rschnlio(UNIT *uptr); +t_stat disk_reset(DEVICE *); +t_stat disk_attach(UNIT *, CONST char *); +t_stat disk_detach(UNIT *); +t_stat disk_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat disk_get_type(FILE *st, UNIT *uptr, int32 v, CONST void *desc); +t_stat disk_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *disk_description (DEVICE *dptr); +extern uint32 inbusy; +extern uint32 outbusy; +extern uint32 readfull(CHANP *chp, uint32 maddr, uint32 *word); +extern int irq_pend; /* go scan for pending int or I/O */ +extern UNIT itm_unit; +extern uint32 PSD[]; /* PSD */ +extern uint32 cont_chan(uint16 chsa); + +/* channel program information */ +CHANP dda_chp[NUM_UNITS_DISK] = {0}; + +#define TRK_CACHE 10 +/* track label queue */ +struct _trk_data +{ + int32 age; + uint32 track; + uint8 label[30]; +}; + +struct _trk_label +{ + struct _trk_data tkl[TRK_CACHE]; +}; + +static struct _trk_label tkl_label[NUM_UNITS_DISK] = {0}; + +MTAB disk_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "TYPE", "TYPE", + &disk_set_type, &disk_get_type, NULL, "Type of disk"}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, + &show_dev_addr, NULL, "Device channel address"}, + {0}, +}; + +UNIT dda_unit[] = { +/* SET_TYPE(3) DM300 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0x800)}, /* 0 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0x802)}, /* 1 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0x804)}, /* 2 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0x806)}, /* 3 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0x808)}, /* 4 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0x80A)}, /* 5 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0x80C)}, /* 6 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0x80E)}, /* 7 */ +}; + +DIB dda_dib = { + disk_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + disk_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + disk_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + disk_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + disk_iocl, /* t_stat (*iocl_io)(CHANP *chp, int32 tik_ok)) */ /* Process IOCL */ + disk_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + dda_unit, /* UNIT* units */ /* Pointer to units structure */ + dda_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_UNITS_DISK, /* uint8 numunits */ /* number of units defined */ + 0x0F, /* uint8 mask */ /* 8 devices - device mask */ + 0x0800, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +DEVICE dda_dev = { + "DMA", dda_unit, NULL/*dda_reg*/, disk_mod, + NUM_UNITS_DISK, 16, 24, 4, 16, 32, + NULL, NULL, &disk_reset, &disk_boot, &disk_attach, &disk_detach, + /* ctxt is the DIB pointer */ + &dda_dib, DEV_DISABLE|DEV_DEBUG|DEV_DIS, 0, dev_debug, + NULL, NULL, &disk_help, NULL, NULL, &disk_description +}; + +#if NUM_DEVS_DISK > 1 +/* channel program information */ +CHANP ddb_chp[NUM_UNITS_DISK] = {0}; + +UNIT ddb_unit[] = { +/* SET_TYPE(3) DM300 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC00)}, /* 0 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC02)}, /* 1 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC04)}, /* 2 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC06)}, /* 3 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC08)}, /* 4 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC0A)}, /* 5 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC0C)}, /* 6 */ + {UDATA(&disk_srv, UNIT_DISK|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC0E)}, /* 7 */ +}; + +DIB ddb_dib = { + disk_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + disk_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + disk_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + disk_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + disk_iocl, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + disk_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + ddb_unit, /* UNIT* units */ /* Pointer to units structure */ + ddb_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_UNITS_DISK, /* uint8 numunits */ /* number of units defined */ + 0x0F, /* uint8 mask */ /* 8 devices - device mask */ + 0x0C00, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +DEVICE ddb_dev = { + "DMB", ddb_unit, NULL, /*ddb_reg*/, disk_mod, + NUM_UNITS_DISK, 16, 24, 4, 16, 32, + NULL, NULL, &disk_reset, &disk_boot, &disk_attach, &disk_detach, + /* ctxt is the DIB pointer */ + &ddb_dib, DEV_DISABLE|DEV_DEBUG|DEV_DIS, 0, dev_debug, + NULL, NULL, &disk_help, NULL, NULL, &disk_description +}; +#endif + +uint32 dmle_ecc32(uint8 *str, int32 len) +{ + int i, j; + uint32 ch, ecc = 0; + uint32 pmask = 0x7e11f439; /* SEL LE poly mask */ + + ecc = (~ecc & MASK32); /* initialize ecc to all bits (~0) */ + for (j=0; j>= 1; /* just shift out the bit */ + ecc ^= pmask; /* eor with poly mask */ + } else + ecc >>= 1; /* just shift out the bit */ + ch >>= 1; /* next bit */ + } + } + return (~ecc & MASK32); /* return ecc value */ +} + +uint32 dmbe_ecc32(uint8 *str, int32 len) +{ + int i, j; + uint32 ch, ecc = 0; + uint32 pmask = 0x9C2F887E; /* SEL BE poly mask */ + + ecc = (~ecc & MASK32); /* initialize ecc to all bits (~0) */ + for (j=0; jflags); + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); /* get the UNIT number */ + int len, i, cn, found = -1; + + int ds = ((CYL(type) - 3) * HDS(type)) * SPT(type); /* diag start */ + /* get file offset in sectors */ + tstart = STAR2SEC(star, SPT(type), SPC(type)); + /* convert sector number back to chs value to sync disk for diags */ + nstar = disksec2star(tstart, type); + if (ds >= (int)tstart) { + /* zero the Track Label flags */ + buf[4] = 0; + return nstar; /* not in diag track, return */ + } + + cyl = (nstar >> 16) & 0xffff; /* get the cylinder */ + trk = (nstar >> 8) & 0xff; /* get the track */ + sec = nstar & 0xff; /* save sec if any */ + + /* get track number */ + tstart = (cyl * HDS(type)) + trk; + sim_debug(DEBUG_DETAIL, dptr, + "get_dmatrk RTL star %08x nstar %08x cyl %4x(%d) trk %x sec# %06x\n", + star, nstar, cyl, cyl, trk, tstart); + + /* calc offset in file to track label */ + offset = CAPB(type) + (tstart * 30); + + /* zero the Track Label Buffer */ + for (i = 0; i < 30; i++) + buf[i] = 0; + + /* see if track label is in cache */ + for (cn=0; cnfileref, offset, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, + "get_dpatrk RTL, Error on seek to %04x\n", offset); + return 0; + } + + /* read in a track label from disk */ + if ((len=sim_fread(buf, 1, 30, uptr->fileref)) != 30) { + sim_debug(DEBUG_EXP, dptr, + "get_dpatrk Error %08x on read %04x of diskfile cyl %04x hds %02x sec 00\n", + len, 30, cyl, trk); + return 0; + } + } + + /* now write track label data to log */ + sim_debug(DEBUG_DETAIL, dptr, "Track %08x label", nstar); + for (i = 0; i < 30; i++) { + if (i == 16) + sim_debug(DEBUG_DETAIL, dptr, "\nTrack %08x label", nstar); + sim_debug(DEBUG_DETAIL, dptr, " %02x", buf[i]); + } + sim_debug(DEBUG_DETAIL, dptr, "\n"); + + if (buf[4] == 0x08) { /* see if defective track */ + uptr->SNS |= SNS_DEFTRK; /* flag as defective */ + tstart = nstar; /* save orginal track */ + /* get the alternate track address */ + cyl = (buf[22] << 8) | buf[23]; /* get the cylinder */ + trk = buf[24]; /* get the track */ + nstar = CHS2STAR(cyl, trk, sec); + sim_debug(DEBUG_DETAIL, dptr, + "Track %08x is defective, new track %08x\n", tstart, nstar); + } + /* see if we had it in our cache */ + if (found == -1) { + /* not in our cache, save the new track label */ + int32 na = 0; + for (cn=0; cn na) + continue; /* older */ + /* this is less used, so replace it */ + na = cn; + } + /* use na entry */ + for (i=0; i<30; i++) + tkl_label[unit].tkl[na].label[i] = buf[i]; + tkl_label[unit].tkl[na].age = 1; + tkl_label[unit].tkl[cn].track = offset; + } + return nstar; /* return track address */ +} + +/* start a disk operation */ +t_stat disk_preio(UNIT *uptr, uint16 chan) +{ + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + int unit = (uptr - dptr->units); + + sim_debug(DEBUG_DETAIL, dptr, "disk_preio CMD %08x unit %02x\n", uptr->CMD, unit); + if ((uptr->CMD & 0xff00) != 0) { /* just return if busy */ + return SNS_BSY; + } + + sim_debug(DEBUG_DETAIL, dptr, "disk_preio unit %02x chsa %04x OK\n", unit, chsa); + return SCPE_OK; /* good to go */ +} + +/* load in the IOCD and process the commands */ +/* return = 0 OK */ +/* return = 1 error, chan_status will have reason */ +t_stat disk_iocl(CHANP *chp, int32 tic_ok) +{ + uint32 word1 = 0; + uint32 word2 = 0; + int32 docmd = 0; + UNIT *uptr = chp->unitptr; /* get the unit ptr */ + uint16 chan = get_chan(chp->chan_dev); /* our channel */ + uint16 chsa = chp->chan_dev; /* our chan/sa */ + uint16 devstat = 0; + DEVICE *dptr = get_dev(uptr); + + /* check for valid iocd address if 1st iocd */ + if (chp->chan_info & INFO_SIOCD) { /* see if 1st IOCD in channel prog */ + if (chp->chan_caw & 0x3) { /* must be word bounded */ + sim_debug(DEBUG_EXP, dptr, + "disk_iocl iocd bad address chsa %02x caw %06x\n", + chsa, chp->chan_caw); + chp->ccw_addr = chp->chan_caw; /* set the bad iocl address */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid iocd addr */ + uptr->SNS |= SNS_INAD; /* invalid address status */ + return 1; /* error return */ + } + } +loop: + sim_debug(DEBUG_EXP, dptr, + "disk_iocl @%06x entry PSD %08x chan_status[%04x] %04x\n", + chp->chan_caw, PSD[0], chan, chp->chan_status); + + /* Abort if we have any errors */ + if (chp->chan_status & STATUS_ERROR) { /* check channel error status */ + sim_debug(DEBUG_EXP, dptr, + "disk_iocl ERROR1 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* return error */ + } + + /* Read in first CCW */ + if (readfull(chp, chp->chan_caw, &word1) != 0) { /* read word1 from memory */ + chp->chan_status |= STATUS_PCHK; /* memory read error, program check */ + sim_debug(DEBUG_EXP, dptr, + "disk_iocl ERROR2 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* Read in second CCW */ + if (readfull(chp, chp->chan_caw+4, &word2) != 0) { /* read word2 from memory */ + chp->chan_status |= STATUS_PCHK; /* memory read error, program check */ + sim_debug(DEBUG_EXP, dptr, + "disk_iocl ERROR3 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + sim_debug(DEBUG_CMD, dptr, + "disk_iocl @%06x read ccw chan %02x IOCD wd 1 %08x wd 2 %08x\n", + chp->chan_caw, chan, word1, word2); + + chp->chan_caw = (chp->chan_caw & 0xfffffc) + 8; /* point to next IOCD */ + + /* Check if we had data chaining in previous iocd */ + /* if we did, use previous cmd value */ + if (((chp->chan_info & INFO_SIOCD) == 0) && /* see if 1st IOCD in channel prog */ + (chp->ccw_flags & FLAG_DC)) { /* last IOCD have DC set? */ + sim_debug(DEBUG_CMD, dptr, + "disk_iocl @%06x DO DC, ccw_flags %04x cmd %02x\n", + chp->chan_caw, chp->ccw_flags, chp->ccw_cmd); + } else + chp->ccw_cmd = (word1 >> 24) & 0xff; /* set new command from IOCD wd 1 */ + + if (!MEM_ADDR_OK(word1 & MASK24)) { /* see if memory address invalid */ + chp->chan_status |= STATUS_PCHK; /* bad, program check */ + uptr->SNS |= SNS_INAD; /* invalid address status */ + sim_debug(DEBUG_EXP, dptr, + "disk_iocl bad IOCD1 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + chp->ccw_count = word2 & 0xffff; /* get 16 bit byte count from IOCD WD 2 */ + + /* validate the commands for the disk */ + switch (chp->ccw_cmd) { + case DSK_WD: case DSK_RD: case DSK_INCH: case DSK_NOP: + case DSK_SCK: case DSK_XEZ: case DSK_LMR: case DSK_WSL: case DSK_RSL: + case DSK_IHA: case DSK_WTL: case DSK_RTL: case DSK_RAP: case DSK_TESS: + case DSK_FNSK: case DSK_REL: case DSK_RES: case DSK_POR: case DSK_TIC: + case DSK_REC: + case DSK_SNS: + break; + case DSK_ICH: + if (chp->ccw_count == 896) /* count must be 896 to be valid */ + break; + /* drop through */ + default: + chp->chan_status |= STATUS_PCHK; /* program check for invalid cmd */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected */ + sim_debug(DEBUG_EXP, dptr, + "disk_iocl bad cmd chan_status[%04x] %04x cmd %02x\n", + chan, chp->chan_status, chp->ccw_cmd); + return 1; /* error return */ + } + + if (chp->chan_info & INFO_SIOCD) { /* see if 1st IOCD in channel prog */ + /* 1st command can not be a TIC or NOP */ + if ((chp->ccw_cmd == DSK_NOP) || (chp->ccw_cmd == CMD_TIC)) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected status */ + sim_debug(DEBUG_EXP, dptr, + "disk_iocl TIC/NOP bad cmd chan_status[%04x] %04x cmd %02x\n", + chan, chp->chan_status, chp->ccw_cmd); + return 1; /* error return */ + } + } + + /* TIC can't follow TIC or be first in command chain */ + /* diags send bad commands for testing. Use all of op */ + if (chp->ccw_cmd == CMD_TIC) { + if (tic_ok) { + if (((word1 & MASK24) == 0) || (word1 & 0x3)) { + sim_debug(DEBUG_EXP, dptr, + "disk_iocl tic cmd bad address chan %02x tic caw %06x IOCD wd 1 %08x\n", + chan, chp->chan_caw, word1); + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected status */ + uptr->SNS |= SNS_INAD; /* invalid address status */ + return 1; /* error return */ + } + tic_ok = 0; /* another tic not allowed */ + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + sim_debug(DEBUG_CMD, dptr, + "disk_iocl tic cmd ccw chan %02x tic caw %06x IOCD wd 1 %08x\n", + chan, chp->chan_caw, word1); + goto loop; /* restart the IOCD processing */ + } + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected status */ + if (((word1 & MASK24) == 0) || (word1 & 0x3)) + uptr->SNS |= SNS_INAD; /* invalid address status */ + sim_debug(DEBUG_EXP, dptr, + "disk_iocl TIC ERROR chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* Check if we had data chaining in previous iocd */ + if ((chp->chan_info & INFO_SIOCD) || /* see if 1st IOCD in channel prog */ + (((chp->chan_info & INFO_SIOCD) == 0) && /* see if 1st IOCD in channel prog */ + ((chp->ccw_flags & FLAG_DC) == 0))) { /* last IOCD have DC set? */ + sim_debug(DEBUG_CMD, dptr, + "disk_iocl @%06x DO CMD No DC, ccw_flags %04x cmd %02x\n", + chp->chan_caw, chp->ccw_flags, chp->ccw_cmd); + docmd = 1; /* show we have a command */ + } + + /* Set up for this command */ + chp->ccw_flags = (word2 >> 16) & 0xf000; /* get flags from bits 0-4 of WD 2 of IOCD */ + chp->chan_status = 0; /* clear status for next IOCD */ + /* make a 24 bit address */ + chp->ccw_addr = word1 & MASK24; /* set the data/seek address */ + + /* validate parts of IOCD2 that are reserved */ + if (word2 & 0x0fff0000) { /* bits 5-15 must be zero */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid iocd */ + sim_debug(DEBUG_EXP, dptr, + "disk_iocl IOCD2 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* DC can only be used with a read/write cmd */ + if (chp->ccw_flags & FLAG_DC) { + if ((chp->ccw_cmd != DSK_RD) && (chp->ccw_cmd != DSK_WD)) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid DC */ + uptr->SNS |= SNS_CHER; /* chaining error */ + sim_debug(DEBUG_EXP, dptr, + "disk_iocl DC ERROR chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + } + + chp->chan_byte = BUFF_BUSY; /* busy & no bytes transferred yet */ + + sim_debug(DEBUG_XIO, dptr, + "disk_iocl @%06x read docmd %01x addr %06x count %04x chan %04x ccw_flags %04x\n", + chp->chan_caw, docmd, chp->ccw_addr, chp->ccw_count, chan, chp->ccw_flags); + + if (docmd) { /* see if we need to process a command */ + DIB *dibp = dib_unit[chp->chan_dev]; /* get the DIB pointer */ + + uptr = chp->unitptr; /* get the unit ptr */ + if (dibp == 0 || uptr == 0) { + chp->chan_status |= STATUS_PCHK; /* program check if it is */ + return 1; /* if none, error */ + } + + sim_debug(DEBUG_DETAIL, dptr, + "disk_iocl @%06x before start_cmd chan %04x status %04x count %04x SNS %08x\n", + chp->chan_caw, chan, chp->chan_status, chp->ccw_count, uptr->u5); + + /* call the device startcmd function to process the current command */ + /* just replace device status bits */ + chp->chan_info &= ~INFO_CEND; /* show chan_end not called yet */ + devstat = dibp->start_cmd(uptr, chan, chp->ccw_cmd); + chp->chan_status = (chp->chan_status & 0xff00) | devstat; + chp->chan_info &= ~INFO_SIOCD; /* show not first IOCD in channel prog */ + + sim_debug(DEBUG_DETAIL, dptr, + "disk_iocl @%06x after start_cmd chan %04x status %08x count %04x byte %02x\n", + chp->chan_caw, chan, chp->chan_status, chp->ccw_count, chp->chan_byte); + + /* see if bad status */ + if (chp->chan_status & (STATUS_ATTN|STATUS_ERROR)) { + chp->chan_status |= STATUS_CEND; /* channel end status */ + chp->ccw_flags = 0; /* no flags */ + chp->chan_byte = BUFF_NEXT; /* have main pick us up */ + sim_debug(DEBUG_EXP, dptr, + "disk_iocl bad status chan %04x status %04x cmd %02x\n", + chan, chp->chan_status, chp->ccw_cmd); + /* done with command */ + sim_debug(DEBUG_EXP, &cpu_dev, + "load_ccw ERROR return chsa %04x status %08x\n", + chp->chan_dev, chp->chan_status); + return 1; /* error return */ + } + /* NOTE this code needed for MPX 1.X to run! */ + /* see if command completed */ + /* we have good status */ + if (chp->chan_status & (STATUS_DEND|STATUS_CEND)) { + uint16 chsa = GET_UADDR(uptr->u3); /* get channel & sub address */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* show I/O complete */ + sim_debug(DEBUG_DETAIL, dptr, + "disk_iocl @%06x FIFO #%1x cmd complete chan %04x status %04x count %04x\n", + chp->chan_caw, FIFO_Num(chsa), chan, chp->chan_status, chp->ccw_count); + } + } + /* the device processor returned OK (0), so wait for I/O to complete */ + /* nothing happening, so return */ + sim_debug(DEBUG_DETAIL, dptr, + "disk_iocl @%06x return, chan %04x status %04x count %04x irq_pend %1x\n", + chp->chan_caw, chan, chp->chan_status, chp->ccw_count, irq_pend); + return 0; /* good return */ +} + +t_stat disk_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) +{ + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + int32 unit = (uptr - dptr->units); + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + sim_debug(DEBUG_DETAIL, dptr, + "disk_startcmd chsa %04x unit %02x cmd %02x CMD %08x\n", + chsa, unit, cmd, uptr->CMD); + if ((uptr->flags & UNIT_ATT) == 0) { /* unit attached status */ + sim_debug(DEBUG_EXP, dptr, "disk_startcmd unit %02x not attached\n", unit); + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + if (cmd != DSK_SNS) /* we are completed with unit check status */ + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + } + + if ((uptr->CMD & DSK_CMDMSK) != 0) { + sim_debug(DEBUG_EXP, dptr, "disk_startcmd unit %02x busy\n", unit); + uptr->CMD |= DSK_BUSY; /* Flag we are busy */ + return SNS_BSY; + } + uptr->SNS2 |= SNS_USEL; /* unit selected */ + sim_debug(DEBUG_DETAIL, dptr, + "disk_startcmd CMD continue unit=%02x cmd %02x iocla %06x cnt %04x\n", + unit, cmd, chp->chan_caw, chp->ccw_count); + + /* Unit is online, so process a command */ + switch (cmd) { + + case DSK_INCH: /* INCH cmd 0x0 */ + sim_debug(DEBUG_CMD, dptr, + "disk_startcmd starting INCH %06x cmd, chsa %04x MemBuf %06x cnt %04x\n", + chp->chan_inch_addr, chsa, chp->ccw_addr, chp->ccw_count); + + uptr->SNS &= ~SNS_CMDREJ; /* not rejected yet */ + uptr->CMD |= DSK_INCH2; /* use 0xF0 for inch, just need int */ +#ifdef FAST_FOR_UTX + sim_activate(uptr, 30); /* start things off */ +#else + sim_activate(uptr, 250); /* start things off */ +#endif + return SCPE_OK; /* good to go */ + break; + + case DSK_NOP: /* NOP 0x03 */ + if ((cmd == DSK_NOP) && + (chp->chan_info & INFO_SIOCD)) { /* is NOP 1st IOCD? */ + chp->chan_caw -= 8; /* backup iocd address for diags */ + break; /* yes, can't be 1st */ + } + case DSK_ICH: /* 0xFF Initialize controller */ + if ((cmd == DSK_ICH) && + (chp->ccw_count != 896)) { /* count must be 896 to be valid */ + break; + } + case DSK_SCK: /* Seek command 0x07 */ + case DSK_XEZ: /* Rezero & Read IPL record 0x37 */ + case DSK_WD: /* Write command 0x01 */ + case DSK_RD: /* Read command 0x02 */ + case DSK_LMR: /* read mode register 0x1F */ + case DSK_WSL: /* WSL 0x31 */ + case DSK_RSL: /* RSL 0x32 */ + case DSK_IHA: /* 0x47 Increment head address */ + case DSK_WTL: /* WTL 0x51 */ + case DSK_RTL: /* RTL 0x52 */ + case DSK_RAP: /* 0xA2 Read angular positions */ + case DSK_TESS: /* TESS 0xAB Test STAR */ + case DSK_FNSK: /* 0x0B Format for no skip */ + case DSK_REC: /* 0xB2 Read ECC correction mask */ + case DSK_RES: /* 0x23 Reserve */ + case DSK_REL: /* 0x33 Release */ + uptr->SNS &= ~MASK24; /* clear data & leave mode */ + uptr->SNS2 = (SNS_UNR|SNS_ONC|SNS_USEL);/* reset status to on cyl & ready */ + case DSK_SNS: /* Sense 0x04 */ + uptr->CMD |= cmd; /* save cmd */ + uptr->LASTCNT = chp->ccw_count; /* save cmd count for diags */ + sim_debug(DEBUG_CMD, dptr, + "disk_startcmd starting disk cmd %02x chsa %04x cnt %04x \n", + cmd, chsa, chp->ccw_count); +#ifdef FAST_FOR_UTX + /* when value was 50, UTX would get a spontainous interrupt */ + /* when value was 30, UTX would get a spontainous interrupt */ + /* changed to 25 from 30 121420 */ +//utx21a sim_activate(uptr, 20); /* start things off */ + /* changed to 15 from 20 12/17/2021 to fix utx21a getting */ + /* "panic: ioi: tis_busy - bad cc" during root fsck on boot */ + /* changed back to 20 from 15 12/18/2021 to refix utx21a getting */ + /* "panic: ioi: tis_busy - bad cc" during root fsck on boot */ + sim_activate(uptr, 20); /* start things off */ + /* when using 500, UTX gets "ioi: sio at 801 failed, cc3, retry=0" */ +#else + sim_activate(uptr, 500); /* start things off */ +#endif + return SCPE_OK; /* good to go */ + break; + } + + sim_debug(DEBUG_EXP, dptr, + "disk_startcmd done with bad disk cmd %02x chsa %04x SNS %08x\n", + cmd, chsa, uptr->SNS); + uptr->SNS |= SNS_CMDREJ; /* cmd rejected */ + return SNS_CHNEND|SNS_DEVEND|STATUS_PCHK; /* return error */ +} + +/* Handle haltio transfers for disk */ +t_stat disk_haltio(UNIT *uptr) { + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + int cmd = uptr->CMD & DSK_CMDMSK; + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + sim_debug(DEBUG_DETAIL, dptr, + "disk_haltio enter chsa %04x cmd = %02x\n", chsa, cmd); + + /* terminate any input command */ + /* UTX wants SLI bit, but no unit exception */ + /* status must not have an error bit set */ + /* otherwise, UTX will panic with "bad status" */ + /* stop any I/O and post status and return error status */ + sim_debug(DEBUG_EXP, dptr, + "disk_haltio HIO I/O stop chsa %04x cmd = %02x CHS %08x STAR %08x\n", + chsa, cmd, uptr->CHS, uptr->STAR); + if ((uptr->CMD & DSK_CMDMSK) != 0) { /* is unit busy */ + sim_debug(DEBUG_EXP, dptr, + "disk_haltio HIO chsa %04x cmd = %02x ccw_count %02x\n", + chsa, cmd, chp->ccw_count); + sim_cancel(uptr); /* clear the input timer */ + chp->ccw_count = 0; /* zero the count */ + chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* stop any chaining */ + uptr->CMD &= LMASK; /* make non-busy */ + uptr->SNS2 |= (SNS_ONC|SNS_UNR); /* on cylinder & ready */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); /* force end */ + return CC1BIT | SCPE_IOERR; /* DIAGS want just an interrupt */ + } + sim_debug(DEBUG_DETAIL, dptr, + "disk_haltio HIO I/O not busy chsa %04x cmd = %02x\n", chsa, cmd); + uptr->CMD &= LMASK; /* make non-busy */ + uptr->SNS2 |= (SNS_ONC|SNS_UNR); /* on cylinder & ready */ + return CC1BIT | SCPE_OK; /* not busy return */ +} + +/* Handle processing of disk requests. */ +t_stat disk_srv(UNIT *uptr) +{ + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */ + int cmd = uptr->CMD & DSK_CMDMSK; + int type = GET_TYPE(uptr->flags); + uint32 tcyl=0, trk=0, cyl=0, sec=0, tempt=0; + int unit = (uptr - dptr->units); + int len = chp->ccw_count; + int i,j,k; + uint32 mema, ecc, cecc; /* memory address / ecc */ + uint8 ch; + uint16 ssize = disk_type[type].ssiz * 4; /* disk sector size in bytes */ + uint32 tstart; + char *bufp; + uint8 lbuf[32]; + uint8 buf[1024]; + uint8 buf2[1024]; + + sim_debug(DEBUG_CMD, dptr, + "disk_srv entry unit %02x CMD %08x chsa %04x count %04x %x/%x/%x \n", + unit, uptr->CMD, chsa, chp->ccw_count, + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + + if ((uptr->flags & UNIT_ATT) == 0) { /* unit attached status */ + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + if (cmd != DSK_SNS) { /* we are completed with unit check status */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + } + + sim_debug(DEBUG_DETAIL, dptr, + "disk_srv cmd=%02x chsa %04x count %04x\n", cmd, chsa, chp->ccw_count); + + switch (cmd) { + case 0: /* No command, stop disk */ + break; + + case DSK_ICH: /* 0xFF Initialize controller */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + len = chp->ccw_count; /* INCH command count */ + mema = chp->ccw_addr; /* get inch or buffer addr */ + sim_debug(DEBUG_CMD, dptr, + "disk_srv cmd CONT INCH %06x chsa %04x addr %06x count %04x completed\n", + chp->chan_inch_addr, chsa, mema, chp->ccw_count); + /* to use this inch method, byte count must be 896 */ + if (len != 896) { + /* we have invalid count, error, bail out */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + /* now call set_inch() function to write and test inch buffer addresses */ + /* 1-224 wd buffer is provided, status is 128 words offset from start */ + mema += (128*4); /* offset to inch buffers */ + tstart = set_inch(uptr, mema, 33); /* new address of 33 entries */ + if ((tstart == SCPE_MEM) || (tstart == SCPE_ARG)) { /* any error */ + /* we have error, bail out */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_INCH2: /* use 0xF0 for inch, just need int */ + len = chp->ccw_count; /* INCH command count */ + mema = chp->ccw_addr; /* get inch or buffer addr */ + sim_debug(DEBUG_CMD, dptr, + "disk_srv starting INCH %06x cmd, chsa %04x MemBuf %06x cnt %04x\n", + chp->chan_inch_addr, chsa, chp->ccw_addr, chp->ccw_count); + + /* mema has IOCD word 1 contents. For the disk processor it contains */ + /* a pointer to the INCH buffer followed by 8 drive attribute words that */ + /* contains the flags, sector count, MHD head count, and FHD count */ + /* len has the byte count from IOCD wd2 and should be 0x24 (36) */ + /* the INCH buffer address must be set for the parent channel as well */ + /* as all other devices on the channel. Call set_inch() to do this for us */ + /* just return OK and channel software will use u4 as status buffer addr */ + + if (len != 36) { + /* we have invalid count, error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* read all 36 bytes, stopping every 4 bytes to make words */ + /* the first word has the inch buffer address */ + /* the next 8 words have drive data for each unit */ + /* WARNING 8 drives must be defined for this controller */ + /* so we will not have a map fault */ + for (i=0; i < 36; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (((i+1)%4) == 0) { /* see if we have a word yet */ + if (i == 3) + /* inch buffer address */ + mema = (buf[0]<<24) | (buf[1]<<16) | + (buf[2]<<8) | (buf[3]); + else + /* drive attribute registers */ + /* may want to use this later */ + /* clear warning errors */ + tstart = (buf[i-3]<<24) | (buf[i-2]<<16) + | (buf[i-1]<<8) | (buf[i]); + } + } + /* now call set_inch() function to write and test inch buffer addresses */ + /* 1-224 wd buffer is provided, status is 128 words offset from start */ + mema += (128*4); /* offset to inch buffers */ + i = set_inch(uptr, mema, 33); /* new address of 33 entries */ + if ((i == SCPE_MEM) || (i == SCPE_ARG)) { /* any error */ + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "disk_srv cmd INCH %06x chsa %04x addr %06x count %04x completed\n", + chp->chan_inch_addr, chsa, mema, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_RES: /* 0x23 Reserve */ + case DSK_REL: /* 0x33 Release */ + case DSK_NOP: /* NOP 0x03 */ + /* diags want chan prog check and cmd reject if 1st cmd of IOCL */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "disk_srv cmd NOP chsa %04x count %04x completed\n", + chsa, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_RAP: /* 0xA2 Read angular positions */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + /* get STAR (target sector) data in STAR */ + cyl = STAR2CYL(uptr->CHS); /* get current cyl */ + trk = (uptr->CHS >> 8) & 0xff; /* get trk/head */ + sec = uptr->CHS & 0xff; /* set sec */ + + ch = ((2*SPT(type))-1) & 0x3f; /* get index cnt */ + uptr->SNS2 = (uptr->SNS2 & 0xc0ff) | ((((uint32)ch) & 0x3f) << 8); + sim_debug(DEBUG_CMD, dptr, + "disk_srv RAP %02x cyl %04x trk %02x sec %02x\n", + ch, cyl&0xffff, trk, sec); + + if (chan_write_byte(chsa, &ch)) { /* put a byte to memory */ + sim_debug(DEBUG_CMD, dptr, + "DISK RAP %02x for addr /%04x/%02x/%02x\n", + ch, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + if (chp->chan_status & STATUS_PCHK) { /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + } else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + } + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_IHA: /* 0x47 Increment head address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + /* get STAR (target sector) data in STAR */ + cyl = STAR2CYL(uptr->CHS); /* get current cyl */ + trk = (uptr->CHS >> 8) & 0xff; /* get trk/head */ + sec = 0; /* set sec to zero for this head */ + + sim_debug(DEBUG_CMD, dptr, + "disk_srv IHA cyl %04x trk %02x sec %02x unit=%02x\n", + cyl&0xffff, trk, sec, unit); + + /* Check if head increment valid */ + trk += 1; /* increment the head # */ + if (trk >= disk_type[type].nhds) { /* see if too big */ + trk = 0; /* back to trk 0 */ + cyl += 1; /* next cylinder */ + if (cyl >= disk_type[type].cyl) { /* see if too big */ + /* set new STAR value using new values */ + uptr->CHS = CHS2STAR(cyl, trk, sec); + sim_debug(DEBUG_EXP, dptr, + "disk_srv IHA ERROR cyl %04x trk %02x sec %02x unit=%02x\n", + cyl, trk, sec, unit); + uptr->SNS |= SNS_DADE; /* set error status */ + uptr->SNS2 |= (SNS_SKER|SNS_SEND); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); /* error */ + break; + } + } + + /* set new STAR value using new values */ + uptr->CHS = CHS2STAR(cyl, trk, sec); + /* get alternate track if this one is defective */ + tempt = get_dmatrk(uptr, uptr->CHS, lbuf); + /* file offset in bytes to std or alt track */ + tstart = STAR2SEC(tempt, SPT(type), SPC(type)) * SSB(type); + + if ((tempt == 0) && (uptr->CHS != 0)) { + /* we have error */ + sim_debug(DEBUG_EXP, dptr, + "disk_srv IHA get_dmatrk return error tempt %06x tstart %06x CHS %08x\n", + tempt, tstart, uptr->CHS); + goto iha_error; + } + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ +iha_error: + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_DADE; /* set error status */ + uptr->SNS2 |= (SNS_SKER|SNS_SEND); + sim_debug(DEBUG_EXP, dptr, "disk_srv IHA error on seek to %04x\n", tstart); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_REC: /* 0xB2 */ /* Read ECC correction code */ + sim_debug(DEBUG_CMD, dptr, "disk_srv CMD REC Read ECC\n"); + /* count must be 4, if not prog check */ + if (len != 4) { + sim_debug(DEBUG_CMD, dptr, + "disk_srv REC bad count unit=%02x count%04x CHS %08x\n", + unit, len, uptr->CHS); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK|STATUS_LENGTH); + break; + } + /* create offset and mask */ + ecc = dmle_ecc32(obuf, ssize); /* calc ecc for original sector */ + sim_debug(DEBUG_DETAIL, dptr, + "disk_srv DEC old obuf data %02x%02x%02x%02x %02x%02x%02x%02x\n", + obuf[1016], obuf[1017], obuf[1018], obuf[1019], + obuf[1020], obuf[1021], obuf[1022], obuf[1023]); + cecc = dmle_ecc32(bbuf, ssize); /* calc ecc for bad sector */ + sim_debug(DEBUG_DETAIL, dptr, + "disk_srv DEC bad bbuf data %02x%02x%02x%02x %02x%02x%02x%02x\n", + bbuf[1016], bbuf[1017], bbuf[1018], bbuf[1019], + bbuf[1020], bbuf[1021], bbuf[1022], bbuf[1023]); + mema = 0; + for (i=0, j=0; i>= 1; /* move mask right */ + } + tcyl = (k * 8) + sec; /* starting bit# */ + sim_debug(DEBUG_CMD, dptr, + "disk_srv REC sb# %04x byte# %04x mask %06x start %08x\n", + sec, k, mema, tcyl); + /* 16 bit sector offset and 9 of 16 bit mask */ + /* tcyl - fake 14 bit offset */ + /* mema - fake 9 bit mask */ + buf[0] = (tcyl & 0x3f00) >> 8; /* upper 6 bits */ + buf[1] = tcyl & 0xff; /* lower 8 bits */ + buf[2] = (mema & 0x100) >> 8; /* upper 1 bits */ + buf[3] = mema & 0xff; /* lower 8 bits */ + /* write the offset and mask data */ + for (i=0; i<4; i++) { + ch = buf[i]; /* get a char from buffer */ + if (chan_write_byte(chsa, &ch)) { /* put a byte to memory */ + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + sim_debug(DEBUG_CMD, dptr, + "disk_srv DEC read %04x bytes of %04x\n", + i, chp->ccw_count); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } + } + sim_debug(DEBUG_CMD, dptr, + "disk_srv wrote DEC offset %04x mask %04x CHS %08x\n", + tcyl & 0x3fff, mema & 0x1ff, uptr->CHS); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case DSK_SNS: /* 0x04 */ /* Sense */ + sim_debug(DEBUG_CMD, dptr, "disk_srv CMD sense\n"); + + /* count must be 12 or 14, if not prog check */ + if (len != 12 && len != 14) { + sim_debug(DEBUG_EXP, dptr, + "disk_srv Sense bad count unit=%02x count%04x\n", unit, len); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK|STATUS_LENGTH); + break; + } + /* bytes 0,1 - Cyl entry from CHS reg */ + ch = (uptr->CHS >> 24) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "disk_srv sense CHS b0 unit=%02x 1 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = (uptr->CHS >> 16) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "disk_srv sense CHS b1 unit=%02x 2 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + /* byte 2 - Track entry from CHS reg */ + ch = (uptr->CHS >> 8) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "disk_srv sense CHS b2 unit=%02x 3 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + /* byte 3 - Sector entry from CHS reg */ + ch = (uptr->CHS) & 0xff; + sec = ch; + sim_debug(DEBUG_DETAIL, dptr, "disk_srv sense CHS b3 unit=%02x 4 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + + /* bytes 4 - mode reg, byte 0 of SNS */ + ch = (uptr->SNS >> 24) & 0xff; /* return the sense data */ + sim_debug(DEBUG_DETAIL, dptr, "disk_srv sense unit=%02x 1 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + /* bytes 5-7 - status bytes, bytes 1-3 of SNS */ + ch = (uptr->SNS >> 16) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "disk_srv sense unit=%02x 2 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = (uptr->SNS >> 8) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "disk_srv sense unit=%02x 3 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = (uptr->SNS) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "disk_srv sense unit=%02x 4 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + + /* bytes 8-11 - drive mode register entries from assigned disk */ + ch = disk_type[type].type & 0x40; /* zero bits 0, 2-7 in type byte */ + sim_debug(DEBUG_DETAIL, dptr, "disk_srv datr unit=%02x 1 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = disk_type[type].spt & 0xff; /* get sectors per track */ + sim_debug(DEBUG_DETAIL, dptr, "disk_srv datr unit=%02x 2 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = disk_type[type].nhds & 0xff; /* get # MHD heads */ + sim_debug(DEBUG_DETAIL, dptr, "disk_srv datr unit=%02x 3 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = 0; /* no FHD heads */ + sim_debug(DEBUG_DETAIL, dptr, "disk_srv datr unit=%02x 4 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + + /* bytes 12 & 13 are optional, so check if read done */ + /* TODO add drive status bits here */ + if ((test_write_byte_end(chsa)) == 0) { + /* bytes 12 & 13 contain drive related status */ + uptr->SNS2 |= (SNS_SEND|SNS_USEL); /* selected & seek end */ + /* bits 2-7 have sector pulse count */ + ch = ((sec * 2) % SPT(type)) & 0x3f;/* get index cnt */ + uptr->SNS2 = (uptr->SNS2 & 0xc0ff) | ((((uint32)ch) & 0x3f) << 8); + ch = (uptr->SNS2 >> 8) & 0xff; /* seek end and unit selected for now */ + sim_debug(DEBUG_DETAIL, dptr, "disk_srv dsr unit=%02x 1 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + + uptr->SNS2 |= (SNS_ONC|SNS_UNR); /* on cylinder & ready */ + ch = uptr->SNS2 & 0xff; /* drive on cylinder and ready for now */ + sim_debug(DEBUG_DETAIL, dptr, "disk_srv dsr unit=%02x 2 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + } + uptr->SNS &= 0xff000000; /* reset status */ + uptr->SNS2 = 0; /* reset status */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case DSK_SCK: /* Seek cylinder, track, sector 0x07 */ + /* If we are waiting on seek to finish, check if there yet. */ + if (uptr->CMD & DSK_SEEKING) { + /* see if on cylinder yet */ + if (STAR2CYL(uptr->STAR) == STAR2CYL(uptr->CHS)) { + /* we are on cylinder, seek is done */ + sim_debug(DEBUG_CMD, dptr, + "disk_srv seek on cylinder unit %02x new %04x old %04x\n", + unit, uptr->STAR>>16, uptr->CHS>>16); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS2 |= (SNS_SEND|SNS_ONC); /* On cylinder & seek done */ + /* we have already seeked to the required sector */ + /* we do not need to seek again, so move on */ + chan_end(chsa, SNS_DEVEND|SNS_CHNEND); + break; + } else { + /* we have wasted enough time, we are there */ + /* we are on cylinder, seek is done */ + sim_debug(DEBUG_CMD, dptr, "disk_srv seek over on cylinder unit=%02x %04x %04x\n", + unit, uptr->STAR >> 16, uptr->CHS >> 16); + uptr->CHS = uptr->STAR; /* we are there */ +#ifdef FAST_FOR_UTX + sim_activate(uptr, 15); /* start things off */ +#else + sim_activate(uptr, 150); /* start things off */ +#endif + break; + } + } + + /* not seeking, so start a new seek */ + /* set buf data to current STAR values */ + tcyl = cyl = STAR2CYL(uptr->CHS); /* get current cyl */ + buf[0] = (cyl >> 8) & 0xff; /* split cylinder */ + buf[1] = cyl & 0xff; + buf[2] = (uptr->CHS >> 8) & 0xff; /* get trk/head */ + buf[3] = uptr->CHS & 0xff; /* get sec */ + + sim_debug(DEBUG_CMD, dptr, + "disk_srv current STAR unit=%02x star %02x %02x %02x %02x\n", + unit, buf[0], buf[1], buf[2], buf[3]); + + if (len > 4) { + sim_debug(DEBUG_EXP, dptr, + "disk_srv SEEK bad count unit %02x count %04x\n", unit, len); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK|STATUS_LENGTH); + break; + } + + /* Read in 1-4 character seek code */ + for (i = 0; i < 4; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + if (i == 0) { + sim_debug(DEBUG_EXP, dptr, + "disk_srv seek error unit=%02x star %02x %02x %02x %02x\n", + unit, buf[0], buf[1], buf[2], buf[3]); + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_DADE; /* Disc addressing or seek error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + chp->ccw_count = len; /* restore count, huh? */ + return SCPE_OK; + break; + } + /* done reading, see how many we read */ + if (i == 1) { + /* UTX wants to set seek STAR to zero */ + buf[0] = buf[1] = buf[2] = buf[3] = 0; + break; + } + /* just read the next byte */ + } + } + chp->ccw_count = len; /* restore count for diag, huh? */ + /* else the cyl, trk, and sec are ready to update */ + sim_debug(DEBUG_CMD, dptr, + "disk_srv STAR unit=%02x star %02x %02x %02x %02x\n", + unit, buf[0], buf[1], buf[2], buf[3]); + + /* save STAR (target sector) data in STAR */ + uptr->STAR = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]); + cyl = STAR2CYL(uptr->STAR); /* get the cylinder */ + trk = buf[2]; /* get the track */ + sec = buf[3]; /* get sec */ + + sim_debug(DEBUG_DETAIL, dptr, + "disk_srv NEW SEEK cyl %04x trk %02x sec %02x unit=%02x\n", + cyl&0xffff, trk, buf[3], unit); + + /* Check if seek valid */ + if (cyl >= disk_type[type].cyl || + trk >= disk_type[type].nhds || + buf[3] >= disk_type[type].spt) { + + sim_debug(DEBUG_EXP, dptr, + "disk_srv seek ERROR cyl %04x trk %02x sec %02x unit=%02x\n", + cyl, trk, buf[3], unit); + + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_DADE; /* set error status */ + uptr->SNS2 |= (SNS_SKER|SNS_SEND); + + /* set new STAR value, even if invalid */ + uptr->CHS = CHS2STAR(cyl, trk, buf[3]); + + /* we have an error, tell user */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); /* end command */ + break; + } + + /* set new STAR value using new values */ + tempt = CHS2STAR(cyl, trk, sec); + /* get alternate track if this one is defective */ + tempt = get_dmatrk(uptr, tempt, lbuf); + /* file offset in bytes to std or alt track */ + tstart = STAR2SEC(tempt, SPT(type), SPC(type)) * SSB(type); + + if ((tempt == 0) && (uptr->STAR != 0)) { + /* we have error */ + sim_debug(DEBUG_EXP, dptr, + "disk_srv SEEK get_dmatrk return error tempt %06x tstart %06x, STAR %08x\n", + tempt, tstart, uptr->STAR); + } + + /* calc the new sector address of data */ + /* calculate file position in bytes of requested sector */ + /* set new STAR value using new values */ + uptr->STAR = CHS2STAR(cyl, trk, sec); + /* file offset in bytes to std or alt track */ + tstart = STAR2SEC(uptr->STAR, SPT(type), SPC(type)) * SSB(type); + + sim_debug(DEBUG_DETAIL, dptr, + "disk_srv seek start %04x cyl %04x trk %02x sec %02x CHS %08x\n", + tstart, cyl, trk, buf[3], uptr->CHS); + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "disk_srv Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* Check if already on correct cylinder */ + /* if not, do a delay to slow things down */ + if (STAR2CYL(uptr->STAR) != STAR2CYL(uptr->CHS)) { + int diff = ((int)tcyl - (int)cyl); + if (diff < 0) + diff = -diff; + /* Do a fake seek to kill time */ + uptr->CMD |= DSK_SEEKING; /* show we are seeking */ + sim_debug(DEBUG_EXP, dptr, + "disk_srv seeking unit=%02x to %04x/%02x/%02x from cyl %04x (%04x)\n", + unit, cyl, trk, buf[3], tcyl, diff); +#ifdef FAST_FOR_UTX + sim_activate(uptr, 15); /* start us off */ +#else + sim_activate(uptr, 400+diff); /* start us off */ +#endif + } else { + /* we are on cylinder/track/sector, so go on */ + sim_debug(DEBUG_DETAIL, dptr, + "disk_srv done seeking to %04x cyl %04x trk %02x sec %02x\n", + tstart, cyl, trk, buf[3]); + /* set new STAR value */ + uptr->CHS = CHS2STAR(cyl, trk, buf[3]); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_DEVEND|SNS_CHNEND); + } + break; + + case DSK_XEZ: /* 0x37 */ /* Rezero & Read IPL record */ + sim_debug(DEBUG_CMD, dptr, "XEZ REZERO IPL unit=%02x seek 0\n", unit); + /* Do a seek to 0 */ + uptr->STAR = 0; /* set STAR to 0, 0, 0 */ + uptr->CHS = 0; /* set current CHS to 0, 0, 0 */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->CMD |= DSK_SCK; /* show as seek command */ + tstart = 0; /* byte offset is 0 */ + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "disk_srv Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + /* we are on cylinder/track/sector zero, so go on */ + sim_debug(DEBUG_DETAIL, dptr, "disk_srv done seek trk 0\n"); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_DEVEND|SNS_CHNEND); + break; + + case DSK_LMR: /* 0x1F */ + sim_debug(DEBUG_CMD, dptr, "Load Mode Reg unit=%02x\n", unit); + /* Read in 1 character of mode data */ + if (chan_read_byte(chsa, &buf[0])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + } + sim_debug(DEBUG_CMD, dptr, "Load Mode Reg unit=%02x old %x new %x\n", + unit, (uptr->SNS)&0xff, buf[0]); + uptr->CMD &= LMASK; /* remove old cmd */ + uptr->SNS &= MASK24; /* clear old mode data */ + uptr->SNS |= (buf[0] << 24); /* save mode value */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case DSK_TESS: /* 0xAB */ /* Test STAR (subchannel target address register) */ + len = chp->ccw_count; + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + + /* set position data for current STAR values */ + cyl = STAR2CYL(uptr->CHS); /* get current cyl */ + trk = (uptr->CHS >> 8) & 0xff; /* get trk/head */ + sec = uptr->CHS & 0xff; /* get sec */ + buf[0] = (cyl >> 8) & 0xff; /* split cylinder */ + buf[1] = cyl & 0xff; + buf[2] = trk; /* get trk/head */ + buf[3] = sec; /* get sec */ + + sim_debug(DEBUG_CMD, dptr, + "disk_srv TESS STAR unit=%02x star %04x %02x %02x\n", + unit, cyl, trk, sec); + + /* a count of 0,1 is prog check */ + if (len <= 1) { + uptr->SNS |= SNS_CMDREJ; /* cmd rejected */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + /* Read in 2-4 character tess code */ + for (i = 0; i < 4; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + if (i <= 1) { + sim_debug(DEBUG_DETAIL, dptr, + "disk_srv TESS error unit=%02x star %04x %02x %02x\n", + unit, cyl, trk, sec); + /* we have error, bail out */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected */ + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + break; + } + /* just read the next byte */ + } + } + tstart = SNS_CHNEND|SNS_DEVEND; /* set default status */ + /* if len = 2, set SNS_SM if tcyl > cyl */ + if (len == 2) { + tcyl = (buf[0] << 8) | buf[1]; /* test cyl */ + if (tcyl > cyl) + tstart |= SNS_SMS; /* set status modifier bit */ + } else + /* if len = 3, set SNS_SM if tcyl > cyl or tcyl == cyl & buf[2] >= trk */ + if (len == 3) { + tcyl = (buf[0] << 8) | buf[1]; /* test cyl */ + if ((tcyl > cyl) || ((tcyl == cyl) && (buf[2] >= trk))) + tstart |= SNS_SMS; /* set status modifier bit */ + } else + /* if len = 4, set SNS_SM if tcyl > cyl or */ + /* if (tcyl == cyl and buf[2] >= trk) */ + /* or if (tcyl == cyl and buf[2] == trk and buf[3] >= sec) */ + if (len >= 4) { + tcyl = (buf[0] << 8) | buf[1]; /* test cyl */ + if ((tcyl > cyl) || ((tcyl == cyl) && (buf[2] >= trk)) || + ((tcyl == cyl) && (buf[2] == trk) && (buf[3] >= sec))) + tstart |= SNS_SMS; /* set status modifier bit */ + } + /* else the cyl, trk, and sect are ready to update */ + sim_debug(DEBUG_CMD, dptr, + "disk_srv tess STAR unit=%02x star %02x %02x %02x %02x\n", + unit, buf[0], buf[1], buf[2], buf[3]); + + chan_end(chsa, tstart); + break; + + case DSK_FNSK: /* 0x0B Format for no skip */ + /* buffer must be on halfword boundry if not STATUS_PCHK and SNS_CMDREJ status */ + /* byte count can not exceed 20160 for the track */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "DISK Format starting CMD %08x chsa %04x buffer %06x count %04x\n", + uptr->CMD, chsa, chp->ccw_addr, chp->ccw_count); + sim_debug(DEBUG_DETAIL, dptr, "Format %x label", uptr->CHS); + /* now read sector label data */ + len = chp->ccw_count; + for (i = 0; i < len; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have write error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + if ((i%16) == 0) + sim_debug(DEBUG_DETAIL, dptr, "\nFormat %x label", uptr->CHS); + sim_debug(DEBUG_DETAIL, dptr, " %02x", buf[i]); + } + sim_debug(DEBUG_DETAIL, dptr, "\n"); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case DSK_RD: /* Read Data command 0x02 */ + if ((uptr->CMD & DSK_READING) == 0) { /* see if we are reading data */ + uptr->CMD |= DSK_READING; /* read from disk starting */ + sim_debug(DEBUG_CMD, dptr, + "DISK READ starting CMD %08x chsa %04x buffer %06x count %04x\n", + uptr->CMD, chsa, chp->ccw_addr, chp->ccw_count); + } + + if (uptr->CMD & DSK_READING) { /* see if we are reading data */ + /* get file offset in sectors */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)); + /* convert sector number back to chs value to sync disk for diags */ + uptr->CHS = disksec2star(tstart, type); + + sim_debug(DEBUG_CMD, dptr, + "DISK B4READ reading CMD %08x chsa %04x tstart %04x buffer %06x count %04x\n", + uptr->CMD, chsa, tstart, chp->ccw_addr, chp->ccw_count); + + /* get alternate track if this one is defective */ + tempt = get_dmatrk(uptr, uptr->CHS, lbuf); + /* file offset in bytes to std or alt track */ + tstart = STAR2SEC(tempt, SPT(type), SPC(type)) * SSB(type); + + sim_debug(DEBUG_CMD, dptr, + "DISK FTRREAD reading CMD %08x chsa %04x tstart %04x buffer %06x count %04x\n", + uptr->CMD, chsa, tstart, chp->ccw_addr, chp->ccw_count); + + if ((tempt == 0) && (uptr->STAR != 0)) { + /* we have error */ + sim_debug(DEBUG_EXP, dptr, + "disk_srv READ1 get_dmatrk return error tempt %06x tstart %06x\n", tempt, tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_DADE; /* set error status */ + uptr->SNS2 |= (SNS_SKER|SNS_SEND); + sim_debug(DEBUG_EXP, dptr, "disk_srv READ error on seek to %04x\n", tstart); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + uptr->SNS &= ~SNS_DEFTRK; /* remove defective flag */ + /* see if spare track */ + if (lbuf[4] & 0x20) { /* see if spare track */ + uptr->SNS |= SNS_DADE; /* disk addr error */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_EXP, dptr, + "disk_srv READ2 get_dmatrk return spare tempt %06x tstart %06x LASTCNT %04x\n", + tempt, tstart, uptr->LASTCNT); + /* restore original transfer count */ + chp->ccw_count = uptr->LASTCNT; /* restore original transfer count */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + + /* see if reserved track */ + if (lbuf[4] & 0x10) { /* see if reserved track */ + uptr->SNS |= SNS_MOCK; /* mode check error */ + uptr->SNS |= SNS_RTAE; /* reserved track access error */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_EXP, dptr, + "disk_srv READ3 get_dmatrk return spare tempt %06x tstart %06x LASTCNT %04x\n", + tempt, tstart, uptr->LASTCNT); + /* restore original transfer count */ + chp->ccw_count = uptr->LASTCNT; /* restore original transfer count */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "disk_srv READ, Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + sim_debug(DEBUG_CMD, dptr, + "DISK READ reading CMD %08x chsa %04x tstart %04x buffer %06x count %04x\n", + uptr->CMD, chsa, tstart, chp->ccw_addr, chp->ccw_count); + + /* read in a sector of data from disk */ + if ((len=sim_fread(buf, 1, ssize, uptr->fileref)) != ssize) { + sim_debug(DEBUG_EXP, dptr, + "Error %08x on read %04x of diskfile cyl %04x hds %02x sec %02x\n", + len, ssize, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + sim_debug(DEBUG_CMD, dptr, + "disk_srv after READ chsa %04x buffer %06x count %04x\n", + chsa, chp->ccw_addr, chp->ccw_count); + bufp = dump_buf(buf, 0, 16); + sim_debug(DEBUG_CMD, dptr, "disk_srv READ buf %s\n", bufp); + bufp = dump_buf(buf, 16, 16); + sim_debug(DEBUG_CMD, dptr, "disk_srv READ buf %s\n", bufp); + bufp = dump_buf(buf, 32, 16); + sim_debug(DEBUG_CMD, dptr, "disk_srv READ buf %s\n", bufp); +#ifdef EXTRA_WORDS + bufp = dump_buf(buf, 48, 16); + sim_debug(DEBUG_CMD, dptr, "disk_srv READ buf %s\n", bufp); +if ((chp->ccw_addr == 0x3cde0) && (buf[0] == 0x4a)) { + bufp = dump_buf(buf, 64, 16); + sim_debug(DEBUG_CMD, dptr, "disk_srv READ buf %s\n", bufp); + bufp = dump_buf(buf, 80, 16); + sim_debug(DEBUG_CMD, dptr, "disk_srv READ buf %s\n", bufp); + bufp = dump_buf(buf, 96, 16); + sim_debug(DEBUG_CMD, dptr, "disk_srv READ buf %s\n", bufp); + bufp = dump_buf(buf, 112, 16); + sim_debug(DEBUG_CMD, dptr, "disk_srv READ buf %s\n", bufp); + bufp = dump_buf(buf, 128, 16); + sim_debug(DEBUG_CMD, dptr, "disk_srv READ buf %s\n", bufp); + bufp = dump_buf(buf, 144, 16); + sim_debug(DEBUG_CMD, dptr, "disk_srv READ buf %s\n", bufp); + bufp = dump_buf(buf, 160, 16); + sim_debug(DEBUG_CMD, dptr, "disk_srv READ buf %s\n", bufp); + bufp = dump_buf(buf, 176, 16); + sim_debug(DEBUG_CMD, dptr, "disk_srv READ buf %s\n", bufp); +} +#endif + uptr->CHS++; /* next sector number */ + /* process the next sector of data */ + for (i=0; ichan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + sim_debug(DEBUG_EXP, dptr, + "DISK READ4 %04x bytes leaving %04x from diskfile %04x/%02x/%02x\n", + i, chp->ccw_count, ((uptr->CHS)>>16)&0xffff, + ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } + } + + /* get current sector offset */ + j = STAR2SEC(tempt, SPT(type), SPC(type)); /* current sector */ + i = ((CYL(type) - 3) * HDS(type)) * SPT(type); /* diag start */ + sim_debug(DEBUG_DETAIL, dptr, + "disk_srv after READ j %04x i %04x j-i %04x CAP %06x DIAG %06x\n", + j, i, j-i, CAP(type), (((CYL(type) - 3) * HDS(type)) * SPT(type))); /* diag start */ + if (j >= i) { /* only do diag sectors */ + cecc = dmle_ecc32(buf, ssize); /* calc ecc for sector */ + sim_debug(DEBUG_DETAIL, dptr, + "ECC j %02x i %02x data calc Old %08x Cur %08x cyl %04x hds %02x sec %02x\n", + j, i, decc[j-i], cecc, STAR2CYL(tempt), ((tempt) >> 8)&0xff, (tempt&0xff)); + if ((decc[j-i] != 0) && (cecc != decc[j-i])) { /* test against old */ + /* checksum error */ + sim_debug(DEBUG_EXP, dptr, + "ECC j %02x i %02x data error Old %08x New %08x cyl %04x hds %02x sec %02x\n", + j, i, decc[j-i], cecc, STAR2CYL(tempt), ((tempt) >> 8)&0xff, (tempt&0xff)); + uptr->SNS |= SNS_ECCD; /* data ECC error */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_CHECK|STATUS_EXPT); + return SCPE_OK; + } + } + + /* see if this is a read ECC from diag */ + /* mode byte will be 0x08 and remaining count will be 4 */ + if ((uptr->SNS & SNS_DIAGMOD) && (chp->ccw_count == 4)) { + for (i=0; iCHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + /* set ECC value here */ + for (i=0; i<4; i++) { + ch = (ecc >> ((3-i)*8)) & 0xff; /* get a char from buffer */ + if (chan_write_byte(chsa, &ch)) { /* put a byte to memory */ + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + return SCPE_OK; + } + } + sim_debug(DEBUG_CMD, dptr, + "Read ECC %04x for diags 4 bytes to ECC REG cyl %04x hds %02x sec %02x\n", + ecc, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + } + + sim_debug(DEBUG_CMD, dptr, + "DISK READ %04x bytes leaving %4x to be read to %06x from diskfile %04x/%02x/%02x\n", + ssize, chp->ccw_count, chp->ccw_addr, + ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + + /* get sector offset */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)); + + /* see if over end of disk */ + if (tstart >= (uint32)CAP(type)) { + /* EOM reached, abort */ + sim_debug(DEBUG_EXP, dptr, + "DISK Read reached EOM for read from disk @ /%04x/%02x/%02x\n", + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->CHS = 0; /* reset cylinder position */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* see if we are done reading data */ + if (test_write_byte_end(chsa)) { + sim_debug(DEBUG_CMD, dptr, + "DISK Read complete for read from diskfile %04x/%02x/%02x\n", + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + } + + sim_debug(DEBUG_CMD, dptr, + "DISK sector read complete, %x bytes to go from diskfile %04x/%02x/%02x\n", + chp->ccw_count, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); +#ifdef FAST_FOR_UTX + sim_activate(uptr, 10); /* wait to read next sector */ +#else + sim_activate(uptr, 300); /* wait to read next sector */ +#endif + break; + } + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + break; + + case DSK_WD: /* Write Data command 0x01 */ + if ((uptr->CMD & DSK_WRITING) == 0) { /* see if we are writing data */ + sim_debug(DEBUG_CMD, dptr, + "DISK WRITE starting unit=%02x CMD %08x write %04x from %06x to %03x/%02x/%02x\n", + unit, uptr->CMD, chp->ccw_count, chp->ccw_addr, + ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + + if (uptr->SNS & 0xf0000000) { /* see if any mode bit 0-3 is set */ + sim_debug(DEBUG_CMD, dptr, + "DISK WRITE2 starting CMD %08x chsa %04x buffer %06x count %04x\n", + uptr->CMD, chsa, chp->ccw_addr, chp->ccw_count); + uptr->SNS |= SNS_MOCK; /* mode check error */ + chp->chan_status |= STATUS_PCHK; /* channel prog check */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + uptr->CMD |= DSK_WRITING; /* write to disk starting */ + } + if (uptr->CMD & DSK_WRITING) { /* see if we are writing data */ + /* get file offset in sectors */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)); + /* file offset in bytes */ + tstart = tstart * SSB(type); + + /* get alternate track if this one is defective */ + tempt = get_dmatrk(uptr, uptr->CHS, lbuf); + /* file offset in bytes to std or alt track */ + tstart = STAR2SEC(tempt, SPT(type), SPC(type)) * SSB(type); + + if ((tempt == 0) && (uptr->STAR != 0)) { + /* we have error */ + sim_debug(DEBUG_EXP, dptr, + "disk_srv WRITE get_dmatrk return error tempt %06x tstart %06x\n", + tempt, tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_DADE; /* set error status */ + uptr->SNS2 |= (SNS_SKER|SNS_SEND); + sim_debug(DEBUG_EXP, dptr, "disk_srv WRITE error on seek to %04x\n", tstart); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + uptr->SNS &= ~SNS_DEFTRK; /* remove defective flag */ + /* see if spare track */ + if (lbuf[4] & 0x20) { /* see if spare track */ + uptr->SNS |= SNS_DADE; /* disk addr error */ + chp->chan_status |= STATUS_PCHK; /* channel prog check */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + /* see if reserved track */ + if (lbuf[4] & 0x10) { /* see if reserved track */ + uptr->SNS |= SNS_MOCK; /* mode check error */ + uptr->SNS |= SNS_RTAE; /* reserved track access error */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "disk_srv WRITE, Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + + /* process the next sector of data */ + tcyl = 0; /* used here as a flag for short read */ + for (i=0; ichan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* if error on reading 1st byte, we are done writing */ + if ((i == 0) || (chp->chan_status & STATUS_PCHK)) { + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_EXP, dptr, + "DISK Wrote %04x bytes to diskfile cyl %04x hds %02x sec %02x\n", + ssize, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } + ch = 0; /* finish out the sector with zero */ + tcyl++; /* show we have no more data to write */ + } + buf2[i] = ch; /* save the char */ + } + + /* get file offset in sectors */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)); + /* convert sector number back to chs value to sync disk for diags */ + uptr->CHS = disksec2star(tstart, type); + + /* write the sector to disk */ + if ((i=sim_fwrite(buf2, 1, ssize, uptr->fileref)) != ssize) { + sim_debug(DEBUG_EXP, dptr, + "Error %08x on write %04x bytes to diskfile cyl %04x hds %02x sec %02x\n", + i, ssize, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + sim_debug(DEBUG_CMD, dptr, + "disk_srv after WRITE buffer %06x count %04x\n", + chp->ccw_addr, chp->ccw_count); + sim_debug(DEBUG_DETAIL, dptr, + "disk_srv WRITE data %02x%02x%02x%02x %02x%02x%02x%02x " + "%02x%02x%02x%02x %02x%02x%02x%02x\n", + buf2[0], buf2[1], buf2[2], buf2[3], buf2[4], buf2[5], buf2[6], buf2[7], + buf2[8], buf2[9], buf2[10], buf2[11], buf2[12], buf2[13], buf2[14], buf2[15]); + sim_debug(DEBUG_DETAIL, dptr, + "disk_srv after WRITE CAP %06x DIAG %06x\n", + CAP(type), (((CYL(type) - 3) * HDS(type)) * SPT(type))); /* diag start */ + + /* get current sector offset */ + j = STAR2SEC(tempt, SPT(type), SPC(type)); /* current sector */ + i = ((CYL(type) - 3) * HDS(type)) * SPT(type); /* diag start */ + sim_debug(DEBUG_DETAIL, dptr, + "disk_srv after WRITE j %04x i %04x j-i %04x CAP %06x DIAG %06x\n", + j, i, j-i, CAP(type), (((CYL(type) - 3) * HDS(type)) * SPT(type))); /* diag start */ + if (j >= i) { /* only do diag sectors */ + cecc = dmle_ecc32(buf2, ssize); /* calc ecc for sector */ + sim_debug(DEBUG_DETAIL, dptr, + "ECC j %02x i %02x data write Old %08x Cur %08x cyl %04x hds %02x sec %02x\n", + j, i, decc[j-i], cecc, STAR2CYL(tempt), ((tempt) >> 8)&0xff, (tempt&0xff)); + decc[j-i] = cecc; /* set new ecc */ + } + j = j-i; /* save index */ + + /* see if this is a write ECC from diag */ + /* mode byte will be 0x08 and remaining count will be 4 */ + if ((uptr->SNS & SNS_DIAGMOD) && (chp->ccw_count == 4)) { + for (i=0; iCHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + /* set ECC value here */ + for (i=0; i<4; i++) { + if (chan_read_byte(chsa, &ch)) {/* get a byte from memory */ + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + return SCPE_OK; + } + /* get an ECC byte */ + buf[i] = ch; /* put a char to buffer */ + ecc |= ((ch & 0xff) << ((3-i)*8)); + } + tcyl++; /* show we have no more data to write */ + sim_debug(DEBUG_DETAIL, dptr, + "Write decc[%04x] ECC=%08x from diags, calc ECC=%08x cyl %04x hds %02x sec %02x\n", + j, ecc, cecc, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + decc[j] = ecc; /* set new ecc from diag */ + } + + sim_debug(DEBUG_CMD, dptr, + "DISK WR to sec end %04x bytes end %04x to diskfile cyl %04x hds %02x sec %02x\n", + len, ssize, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + + uptr->CHS++; /* next sector number */ + if (tcyl != 0) { /* see if done with write command */ + sim_debug(DEBUG_CMD, dptr, + "DISK WroteB %04x bytes to diskfile cyl %04x hds %02x sec %02x\n", + ssize, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we done */ + break; + } + /* get sector offset */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)); + + /* see if over end of disk */ + if (tstart >= (uint32)CAP(type)) { + /* EOM reached, abort */ + sim_debug(DEBUG_EXP, dptr, + "DISK Write reached EOM for write to disk @ %04x/%02x/%02x\n", + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->CHS = 0; /* reset cylinder position */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* see if we are done reading data */ + if (test_write_byte_end(chsa)) { + sim_debug(DEBUG_CMD, dptr, + "DISK Write complete for read from diskfile %04x/%02x/%02x\n", + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + } + +#ifdef FAST_FOR_UTX + sim_activate(uptr, 15); /* wait to read next sector */ +#else + sim_activate(uptr, 300); /* wait to read next sector */ +#endif + break; + } + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + break; + + case DSK_RSL: /* RSL 0x32 */ + /* Read sector label zero to get disk geometry */ + /* write 30 bytes, b0-b1=cyl, b1=trk, b2=sec */ + /* zero the Track Label Buffer */ + for (i = 0; i < 30; i++) + buf[i] = 0; + + len = chp->ccw_count; /* get number bytes to read */ + mema = uptr->CHS+(len/30); /* save address */ + + sim_debug(DEBUG_DETAIL, dptr, + "before RSL Sector %x len %x\n", uptr->CHS, len); + + /* read a 30 byte track label for each sector on track */ + /* for 16 sectors per track, that is 480 bytes */ + /* for 20 sectors per track, that is 600 bytes */ + for (j=0; jCHS, SPT(type), SPC(type)); + /* convert sector number back to chs value to sync disk for diags */ + uptr->CHS = disksec2star(tstart, type); + + cyl = (uptr->CHS >> 16) & 0xffff; /* get the cylinder */ + trk = (uptr->CHS >> 8) & 0xff; /* get the track */ + sec = uptr->CHS & 0xff; /* get sec */ + seeksec = tstart; /* save sector number */ + + sim_debug(DEBUG_EXP, dptr, + "disk_srv RSL cyl %04x trk %02x sec %02x sector# %06x\n", + cyl, trk, sec, seeksec); + + /* seek sector label area after end of track label area */ + tstart = CAPB(type) + (CYL(type)*HDS(type)*30) + (tstart*30); + + /* file offset in bytes to sector label */ + sim_debug(DEBUG_EXP, dptr, + "disk_srv RSL SEEK on seek to %08x\n", tstart); + + /* seek to the location where we will read sector label */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { + sim_debug(DEBUG_EXP, dptr, + "Error seeking sector label area at sect %06x offset %08x\n", + seeksec, tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + + /* read in a sector label from disk */ + if (sim_fread(buf, 1, 30, uptr->fileref) != 30) { + sim_debug(DEBUG_EXP, dptr, + "Error %08x on read %04x of diskfile cyl %04x hds %02x sec %02x\n", + len, 30, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + + sim_debug(DEBUG_DETAIL, dptr, "Sector %x label", uptr->CHS); + /* now write sector label data */ + for (i = 0; i < 30; i++) { + if (chan_write_byte(chsa, &buf[i])) { + /* we have write error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_DETAIL, dptr, "\n"); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + if (i == 16) + sim_debug(DEBUG_DETAIL, dptr, "\nSector %x label", uptr->CHS); + sim_debug(DEBUG_DETAIL, dptr, " %02x", buf[i]); + } + sim_debug(DEBUG_DETAIL, dptr, "\n"); + + /* leave STAR "unnormalized" for diags */ + uptr->CHS++; /* bump to next sector */ + if ((uptr->CHS & 0xff) == SPC(type)) + break; /* stop at last sector */ + len -= 30; /* count 1 sector label size */ + if (len > 0) + continue; + break; /* done */ + } + + uptr->CHS = mema; /* restore address */ + + sim_debug(DEBUG_DETAIL, dptr, "after RSL Sector %x len %x\n", uptr->CHS, chp->ccw_count); + + /* command done */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, "disk_srv cmd RSL done chsa %04x count %04x completed\n", + chsa, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_WSL: /* WSL 0x31 write sector labels */ + /* Write sector label to disk */ + /* write 30 bytes, b0-b1=cyl, b1=trk, b2=sec */ + len = chp->ccw_count; /* get number bytes to read */ + mema = uptr->CHS; /* save address */ + + sim_debug(DEBUG_DETAIL, dptr, "before WSL Sector %x len %x\n", uptr->CHS, len); + + /* read a 30 byte sector label for each sector on track */ + /* for 16 sectors per track, that is 480 bytes */ + /* for 20 sectors per track, that is 600 bytes */ + for (j=0; jCHS); + /* now read sector label data */ + for (i = 0; i < 30; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have read error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + if ((i%16) == 0) + sim_debug(DEBUG_DETAIL, dptr, "\nSector %x label", uptr->CHS); + sim_debug(DEBUG_DETAIL, dptr, " %02x", buf[i]); + } + sim_debug(DEBUG_DETAIL, dptr, "\n"); + + /* see if user trying to set invalid bit pattern */ + if ((buf[4] & 0x48) == 0x48) { /* see if setting defective alternate trk */ + uptr->SNS |= SNS_DSKFERR; /* disk formating error */ + uptr->CHS = mema; /* restore address */ + chp->ccw_count = len; /* restore number bytes to read */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + return SCPE_OK; + break; + } + + /* get file offset in sectors */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)); + + /* convert sector number back to chs value to sync disk for diags */ + uptr->CHS = disksec2star(tstart, type); + + cyl = (uptr->CHS >> 16) & 0xffff; /* get the cylinder */ + trk = (uptr->CHS >> 8) & 0xff; /* get the track */ + sec = uptr->CHS & 0xff; /* get sec */ + seeksec = tstart; /* save sector number */ + + sim_debug(DEBUG_CMD, dptr, "disk_srv WSL cyl %04x trk %02x sec %02x sector# %06x\n", + cyl, trk, sec, seeksec); + + /* seek sector label area after end of track label area */ + tstart = CAPB(type) + (CYL(type)*HDS(type)*30) + (tstart*30); + + /* file offset in bytes to sector label */ + sim_debug(DEBUG_CMD, dptr, "disk_srv WSL SEEK on seek to %08x\n", tstart); + + /* seek to the location where we will write sector label */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { + sim_debug(DEBUG_EXP, dptr, + "Error seeking sector label area at sect %06x offset %08x\n", + seeksec, tstart); + uptr->CHS = mema; /* restore address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + + /* write sector label to disk */ + if (sim_fwrite(buf, 1, 30, uptr->fileref) != 30) { + sim_debug(DEBUG_EXP, dptr, + "Error %08x on write %04x of diskfile cyl %04x hds %02x sec %02x\n", + len, 30, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CHS = mema; /* restore address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + + /* leave STAR "unnormalized" for diags */ + uptr->CHS++; /* bump to next sector */ + if ((uptr->CHS & 0xff) == SPC(type)) + break; /* stop at last sector */ + len -= 30; /* count 1 sector label size */ + if (len > 0) + continue; + break; /* done */ + } + + uptr->CHS = mema; /* restore address */ + + sim_debug(DEBUG_DETAIL, dptr, "after WSL Sector %x len %x\n", uptr->CHS, chp->ccw_count); + + /* command done */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, "disk_srv cmd WSL done chsa %04x count %04x completed\n", + chsa, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_RTL: /* RTL 0x52 */ + /* Read track zero to get disk geometry */ + /* read 30 bytes, b0-b1=cyl, b1=trk, b2=sec */ + + /* zero the Track Label Buffer */ + for (i = 0; i < 30; i++) + buf[i] = 0; + uptr->CHS &= 0xffffff00; /* zero sector for trk read */ + mema = uptr->CHS; + + /* get file offset in sectors */ + tstart = STAR2SEC(mema, SPT(type), SPC(type)); + + /* convert sector number back to chs value to sync disk for diags */ + mema = disksec2star(tstart, type); + cyl = (mema >> 16) & 0xffff; /* get the cylinder */ + trk = (mema >> 8) & 0xff; /* get the track */ + + /* get track number */ + tstart = (cyl * HDS(type)) + trk; + sim_debug(DEBUG_CMD, dptr, "disk_srv RTL cyl %4x(%d) trk %x sec# %06x\n", + cyl, cyl, trk, tstart); + + /* calc offset in file to track label */ + tstart = CAPB(type) + (tstart * 30); + + /* file offset in bytes */ + sim_debug(DEBUG_CMD, dptr, "disk_srv RTL SEEK on seek to %06x\n", tstart); + + /* seek to the location where we will r/w track label */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "disk_srv RTL, Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + + /* read in a track label from disk */ + if ((len=sim_fread(buf, 1, 30, uptr->fileref)) != 30) { + sim_debug(DEBUG_EXP, dptr, + "Error %08x on read %04x of diskfile cyl %04x hds %02x sec %02x\n", + len, 30, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + if (buf[4] == 0x08) { /* see if defective track */ + uptr->SNS |= SNS_DEFTRK; /* flag as defective */ + sim_debug(DEBUG_DETAIL, dptr, "Track %08x is defective\n", uptr->CHS); + } + + if (buf[4] == 0x40) { /* see if alternate track */ + uptr->SNS |= SNS_AATT; /* flag as alternate */ + sim_debug(DEBUG_DETAIL, dptr, "Track %08x is alternate\n", uptr->CHS); + } + + /* now write track label data to memory */ + sim_debug(DEBUG_DETAIL, dptr, "Track %08x label", uptr->CHS); + for (i = 0; i < 30; i++) { + if (chan_write_byte(chsa, &buf[i])) { + /* we have write error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (i == 16) + sim_debug(DEBUG_DETAIL, dptr, "\nTrack %08x label", uptr->CHS); + sim_debug(DEBUG_DETAIL, dptr, " %02x", buf[i]); + } + sim_debug(DEBUG_DETAIL, dptr, "\n"); + + /* command done */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, "disk_srv cmd RTL done chsa %04x count %04x completed\n", + chsa, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_WTL: /* WTL 0x51 */ + /* Write track zero to set disk geometry */ + /* write 30 bytes, b0-b1=cyl, b1=trk, b2=sec */ + + sim_debug(DEBUG_DETAIL, dptr, "disk_srv WTL start cnt %04x CHS %08x\n", + chp->ccw_count, uptr->CHS); + + /* get file offset in sectors */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)); + /* convert sector number back to chs value to sync disk for diags */ + uptr->CHS = disksec2star(tstart, type); + uptr->CHS &= 0xffffff00; /* zero sector for trk read */ + mema = uptr->CHS; + + cyl = (uptr->CHS >> 16) & 0xffff; /* get the cylinder */ + trk = (uptr->CHS >> 8) & 0xff; /* get the track */ + + /* get track number */ + tstart = (cyl * HDS(type)) + trk; + sim_debug(DEBUG_CMD, dptr, "disk_srv WTL cyl %4x trk %x track# %06x CHS %08x\n", + cyl, trk, tstart, uptr->CHS); + + /* calc offset in file to track label */ + tstart = CAPB(type) + (tstart * 30); + + /* file offset in bytes */ + sim_debug(DEBUG_CMD, dptr, "disk_srv WTL SEEK on seek to %06x\n", tstart); + + /* seek to the location where we will write track label */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "disk_srv WTL, Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + + sim_debug(DEBUG_DETAIL, dptr, "Track %08x label", uptr->CHS); + /* now read track label data from memory */ + for (i = 0; i < 30; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have read error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + if (i == 16) + sim_debug(DEBUG_DETAIL, dptr, "\nTrack %08x label", uptr->CHS); + sim_debug(DEBUG_DETAIL, dptr, " %02x", buf[i]); + } + sim_debug(DEBUG_DETAIL, dptr, "\n"); + + /* see if user trying to set invalid bit pattern */ + if ((buf[4] & 0x48) == 0x48) { /* see if setting defective alternate trk */ + uptr->SNS |= SNS_DSKFERR; /* disk formating error */ + uptr->CHS = mema; /* restore address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + + /* write out a track label to disk */ + if ((len=sim_fwrite(buf, 1, 30, uptr->fileref)) != 30) { + sim_debug(DEBUG_EXP, dptr, + "Error %08x on write %04x of diskfile cyl %04x hds %02x sec %02x\n", + len, 30, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CHS = mema; /* restore address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* clear cache entry for this track */ + /* see if track label is in cache */ + for (i=0; iCHS = mema; /* restore address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "disk_srv cmd WTL chsa %04x count %04x completed CHS %08x\n", + chsa, chp->ccw_count, uptr->CHS); + + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + default: + sim_debug(DEBUG_EXP, dptr, "invalid command %02x unit %02x\n", cmd, unit); + uptr->SNS |= SNS_CMDREJ; + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|STATUS_PCHK); /* return Prog Check */ + break; + } + sim_debug(DEBUG_DETAIL, dptr, + "disk_srv done cmd %02x chsa %04x chs %04x/%02x/%02x\n", + cmd, chsa, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + return SCPE_OK; +} + +/* handle rschnlio cmds for disk */ +t_stat disk_rschnlio(UNIT *uptr) { + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & DSK_CMDMSK; + + sim_debug(DEBUG_EXP, dptr, + "disk_rschnl chsa %04x cmd = %02x\n", chsa, cmd); + disk_ini(uptr, 0); /* reset the unit */ + return SCPE_OK; +} + +/* initialize the disk */ +void disk_ini(UNIT *uptr, t_bool f) +{ + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); /* get the UNIT number */ + int i = GET_TYPE(uptr->flags); + int cn; + + /* start out at sector 0 */ + uptr->CHS = 0; /* set CHS to cyl/hd/sec = 0 */ + uptr->STAR = 0; /* set STAR to cyl/hd/sec = 0 */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + /* total sectors on disk */ + uptr->capac = CAP(i); /* size in sectors */ + sim_cancel(uptr); /* stop any timers */ + /* reset track cache */ + for (cn=0; cnname, GET_UADDR(uptr->CMD), uptr->capac, uptr->capac); +} + +t_stat disk_reset(DEVICE *dptr) +{ + int cn, unit; + + for(unit=0; unit < NUM_UNITS_DISK; unit++) { + for (cn=0; cnflags); + DEVICE *dptr = get_dev(uptr); + uint32 trk, cyl, sec; + uint32 ssize = SSB(type); /* disk sector size in bytes */ + uint32 tsize = SPT(type); /* get track size in sectors */ + uint32 tot_tracks = TRK(type); /* total tracks on disk */ + uint32 tot_sectors = CAP(type); /* total number of sectors on disk */ + uint32 cap = CAP(type); /* disk capacity in sectors */ + uint32 CHS; /* cyl, hds, sec format */ + uint8 label[34]; /* track/sector label */ + int32 i, j; + /* get sector address of vendor defect table VDT */ + /* put data = 0xf0000000 0xf4000000 */ + int32 vaddr = (CYL(type)-4) * SPC(type) + (HDS(type)-1) * SPT(type); + /* get sector address of utx diag map (DMAP) track 0 pointer */ + /* put data = 0xf0000000 + (cyl-1), 0x8a000000 + daddr, */ + /* 0x9a000000 + (cyl-1), 0xf4000000 */ + int32 daddr = (CYL(type)-4) * SPC(type) + (HDS(type)-2) * SPT(type); + /* get sector address of utx flaw map sec 1 pointer */ + /* use this address for sec 1 label pointer */ + int32 uaddr = (CYL(type)-4) * SPC(type) + (HDS(type)-4) * SPT(type); + + /* write 30 byte track labels for all tracks on disk */ + /* tot_tracks entries will be created starting at end of disk */ + /* seek first sector after end of disk data */ + if ((sim_fseek(uptr->fileref, CAPB(type), SEEK_SET)) != 0) { + sim_debug(DEBUG_EXP, dptr, + "Error seeking track label area at sect %06x offset %06x\n", + CAP(type), CAPB(type)); + return 1; + } + /* write track labels */ + for (i=0; i<(int)tot_tracks; i++) { + + /* zero the Track Label Buffer */ + for (j = 0; j < 30; j++) + label[j] = 0; + + sec = i * SPT(type); /* get track address in sectors */ + /* convert sector number to CHS value for label */ + CHS = disksec2star(sec, type); /* get current CHS value */ + + /* set buf data to current CHS values */ + if (CHS == 0) { /* write last address on trk 0 */ + cyl = CYL(type)-1; /* lcyl cyl upper 8 bits */ + trk = HDS(type)-1; /* ltkn trk */ + sec = SPT(type)-1; /* lid sector ID */ + } else { + /* write current address on other tracks */ + cyl = (CHS >> 16) & 0xffff; /* get the cylinder */ + trk = (CHS >> 8) & 0xff; /* get the track */ + sec = (CHS) & 0xff; /* get the sector */ + } + + sim_debug(DEBUG_CMD, dptr, "disk_format WTL STAR %08x disk geom %08x\n", + CHS, GEOM(type)); + + /* set buf data to current STAR values */ + label[0] = (cyl >> 8) & 0xff; /* lcyl cyl upper 8 bits */ + label[1] = cyl & 0xff; /* lcyl cyl lower 8 bits */ + label[2] = trk & 0xff; /* ltkn trk */ + label[3] = sec & 0xff; /* lid sector ID */ + label[4] = 0x80; /* show good sector */ + if (i == (tot_tracks-1)) { /* last track? */ + label[3] = 0xff; /* lid show as last track label */ + label[4] |= 0x04; /* set last track flag */ + } + + sim_debug(DEBUG_CMD, dptr, + "disk_format WTL star %02x %02x %02x %02x\n", + label[0], label[1], label[2], label[3]); + + /* daddr has dmap value for track zero label */ + if (CHS == 0) { /* only write dmap address in trk 0 */ + /* output diag defect map address of disk */ + label[12] = (daddr >> 24) & 0xff; /* ldeallp DMAP pointer */ + label[13] = (daddr >> 16) & 0xff; + label[14] = (daddr >> 8) & 0xff; + label[15] = (daddr) & 0xff; + printf("disk_label WTL daddr@daddr %08x -> %08x\r\n", daddr, 0); + sim_debug(DEBUG_CMD, dptr, + "disk_label WTL daddr@daddr %08x -> %08x\n", vaddr, 0); + } + + /* write vaddr to track label for dmap */ + if ((i*SPT(type)) == daddr) { /* get track address in sectors */ + /* output vendor defect map address of disk */ + label[12] = (vaddr >> 24) & 0xff; /* Vaddr pointer */ + label[13] = (vaddr >> 16) & 0xff; + label[14] = (vaddr >> 8) & 0xff; + label[15] = (vaddr) & 0xff; + printf("disk_format WTL vaddr@daddr %08x -> %08x\r\n", vaddr, daddr); + sim_debug(DEBUG_CMD, dptr, + "disk_format WTL vaddr@daddr %08x -> %08x\n", vaddr, daddr); + } + /* if this is removed, utx is unable to create newfs */ + /* get preposterous size 0 error message */ + /* maybe not needed, but left anyway */ + /* uaddr has umap value for track zero label */ + if (CHS == 0) { /* only write dmap address in trk 0 */ + /* output umap address */ + label[16] = (uaddr >> 24) & 0xff; /* lumapp DMAP pointer */ + label[17] = (uaddr >> 16) & 0xff; + label[18] = (uaddr >> 8) & 0xff; + label[19] = (uaddr) & 0xff; + } + + /* the tech doc shows the cyl/trk/sec data is in the first 4 bytes */ + /* of the track label, BUT it is really in the configuration data */ + /* area too. Byte 27 is sectors/track and byte 28 is number of heads. */ + /* Byte 26 is mode. Byte 25 is copy of byte 27. */ + label[25] = SPT(type) & 0xff; + label[26] = disk_type[type].type & 0xfc; /* zero bits 6 & 7 in type byte */ + label[27] = SPT(type) & 0xff; + label[28] = HDS(type) & 0xff; + + if ((sim_fwrite((char *)&label, sizeof(uint8), 30, uptr->fileref)) != 30) { + sim_debug(DEBUG_EXP, dptr, + "Error writing track label to sect %06x offset %06x\n", + cap+(i*tsize), cap*ssize+(i*tsize*ssize)); + return 1; + } + } + + /* write 30 byte sector labels for all sectors on disk */ + /* tot_sector entries will be created starting at end of disk */ + /* plus the track label area size. Seek first sector after end */ + /* of disk track label area */ + if ((sim_fseek(uptr->fileref, CAPB(type)+TRK(type)*30, SEEK_SET)) != 0) { + sim_debug(DEBUG_EXP, dptr, + "Error seeking sector label area at sect %06x offset %06x\n", + CAP(type)+TRK(type), CAPB(type)+TRK(type)*30); + return 1; + } + + /* zero the Sector Label Buffer */ + for (j = 0; j < 30; j++) + label[j] = 0; + + /* convert sector number to CHS value for label */ + /* write sector labels */ + for (i=0; i<(int)tot_sectors; i++) { + + CHS = disksec2star(i, type); /* get current CHS value */ + + /* set buf data to current CHS values */ + /* write current address on other tracks */ + cyl = (CHS >> 16) & 0xffff; /* get the cylinder */ + trk = (CHS >> 8) & 0xff; /* get the track */ + sec = (CHS) & 0xff; /* get the sector */ + + sim_debug(DEBUG_CMD, dptr, "disk_format WSL STAR %08x disk geom %08x\n", + CHS, GEOM(type)); + + /* set buf data to current STAR values */ + label[0] = (cyl >> 8) & 0xff; /* lcyl cyl upper 8 bits */ + label[1] = cyl & 0xff; /* lcyl cyl lower 8 bits */ + label[2] = trk & 0xff; /* ltkn trk */ + label[3] = sec & 0xff; /* lid sector ID */ + label[4] = 0x80; /* show good sector */ + + sim_debug(DEBUG_CMD, dptr, + "disk_format WSL star %02x %02x %02x %02x\n", + label[0], label[1], label[2], label[3]); + + label[12] = 0; + label[13] = 0; + label[14] = 0; + label[15] = 0; + + /* the tech doc shows the cyl/trk/sec data is in the first 4 bytes */ + /* of the track label, BUT it is really in the configuration data */ + /* area too. Byte 27 is sectors/track and byte 28 is number of heads. */ + /* Byte 26 is mode. Byte 25 is copy of byte 27. */ + label[25] = disk_type[type].spt & 0xff; + /* The UDP/DPII controllers do not use these bits, so UTX keys */ + /* on these bits to determine type of controller. Bit 31 is set */ + /* for a HSDP and not set for the UDP/DPII */ + label[26] = disk_type[type].type & 0xfc; /* zero bits 6 & 7 in type byte */ + label[27] = disk_type[type].spt & 0xff; + label[28] = disk_type[type].nhds & 0xff; + + if ((sim_fwrite((char *)&label, sizeof(uint8), 30, uptr->fileref)) != 30) { + sim_debug(DEBUG_CMD, dptr, + "Error writing sector label to sect %06x offset %06x\n", + i, CAPB(type)+TRK(type)*30+i*ssize); + return 1; + } + } + + /* seek home again */ + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + fprintf (stderr, "Error on seek to 0\r\n"); + return 1; + } + return SCPE_OK; /* good to go */ +} + +/* create the disk file for the specified device */ +int disk_format(UNIT *uptr) { + int type = GET_TYPE(uptr->flags); + DEVICE *dptr = get_dev(uptr); + uint32 ssize = SSB(type); /* disk sector size in bytes */ + uint32 tsize = SPT(type); /* get track size in sectors */ + uint32 csize = SPC(type); /* get cylinder size in sectors */ + uint32 cyl = CYL(type); /* get # cylinders */ + uint32 cap = CAP(type); /* disk capacity in sectors */ + uint32 cylv = cyl; /* number of cylinders */ + uint8 *buff; + int32 i; + t_stat oldsw = sim_switches; /* save switches */ + + /* last sector address of disk (cyl * hds * spt) - 1 */ + uint32 laddr = CAP(type) - 1; /* last sector of disk */ + + /* last track address of disk (cyl * hds * spt) - spt */ + uint32 ltaddr = CAP(type)-SPT(type); /* last track of disk */ + + /* get sector address of vendor defect table VDT */ + /* put data = 0xf0000000 0xf4000000 */ + int32 vaddr = (CYL(type)-4) * SPC(type) + (HDS(type)-1) * SPT(type); + + /* get sector address of utx diag map (DMAP) track 0 pointer */ + /* put data = 0xf0000000 + (cyl-1), 0x8a000000 + daddr, */ + /* 0x9a000000 + (cyl-1), 0xf4000000 */ + int32 daddr = (CYL(type)-4) * SPC(type) + (HDS(type)-2) * SPT(type); + + /* get sector address of utx flaw data (1 track long) */ + /* set trace data to zero */ + int32 faddr = (CYL(type)-4) * SPC(type) + (HDS(type)-3) * SPT(type); + + /* get sector address of utx flaw map sec 1 pointer */ + /* use this address for sec 1 label pointer */ + int32 uaddr = (CYL(type)-4) * SPC(type) + (HDS(type)-4) * SPT(type); + + /* vendor flaw map in vaddr */ + uint32 vmap[2] = {0xf0000004, 0xf4000000}; + + /* defect map */ + uint32 dmap[4] = {0xf0000000 | (cap-1), 0x8a000000 | daddr, + 0x9a000000 | (cap-1), 0xf4000000}; + + /* utx flaw map */ + uint32 fmap[4] = {0xf0000000 | (cap-1), 0x8a000000 | daddr, + 0x9a000000 | ltaddr, 0xf4000000}; + + /* see if -i or -n specified on attach command */ + if (!(sim_switches & SWMASK('N')) && !(sim_switches & SWMASK('I'))) { + sim_switches = 0; /* simh tests 'N' & 'Y' switches */ + /* see if user wants to initialize the disk */ + if (!get_yn("Initialize disk? [Y] ", TRUE)) { + sim_switches = oldsw; + return 1; + } + sim_switches = oldsw; /* restore switches */ + } + + /* VDT 249264 (819/18/0) 0x3cdb0 for 9346 - 823/19/16 vaddr */ + /* MDT 249248 (819/17/0) 0x3cda0 for 9346 - 823/19/16 daddr */ + /* UMAP 249216 (819/15/0) 0x3cd80 for 9346 - 823/19/16 uaddr */ + + /* seek to sector 0 */ + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + fprintf (stderr, "Error on seek to 0\r\n"); + return 1; + } + + /* get buffer for track data in bytes */ + if ((buff = (uint8 *)calloc(csize*ssize, sizeof(uint8))) == 0) { + detach_unit(uptr); + return SCPE_ARG; + } + sim_debug(DEBUG_CMD, dptr, + "Creating disk file of trk size %04x bytes, capacity %d\n", + tsize*ssize, cap*ssize); + + /* write zeros to each track of the disk */ + for (cyl = 0; cyl < cylv; cyl++) { + if ((sim_fwrite(buff, 1, csize*ssize, uptr->fileref)) != csize*ssize) { + sim_debug(DEBUG_EXP, dptr, + "Error on write to diskfile cyl %04x\n", cyl); + free(buff); /* free cylinder buffer */ + buff = 0; + return 1; + } + if ((cyl % 100) == 0) + fputc('.', stderr); + } + fputc('\r', stderr); + fputc('\n', stderr); + free(buff); /* free cylinder buffer */ + buff = 0; + + /* byte swap the buffers for dmap and umap */ + for (i=0; i<2; i++) { + vmap[i] = (((vmap[i] & 0xff) << 24) | ((vmap[i] & 0xff00) << 8) | + ((vmap[i] & 0xff0000) >> 8) | ((vmap[i] >> 24) & 0xff)); + } + for (i=0; i<4; i++) { + dmap[i] = (((dmap[i] & 0xff) << 24) | ((dmap[i] & 0xff00) << 8) | + ((dmap[i] & 0xff0000) >> 8) | ((dmap[i] >> 24) & 0xff)); + } + + for (i=0; i<4; i++) { + fmap[i] = (((fmap[i] & 0xff) << 24) | ((fmap[i] & 0xff00) << 8) | + ((fmap[i] & 0xff0000) >> 8) | ((fmap[i] >> 24) & 0xff)); + } + + /* now seek to end of disk and write the dmap data */ + /* setup dmap pointed to by track label 0 wd[3] = (cyl-4) * spt + (spt - 1) */ + + /* write dmap data to last sector on disk */ + if ((sim_fseek(uptr->fileref, laddr*ssize, SEEK_SET)) != 0) { /* seek last sector */ + sim_debug(DEBUG_EXP, dptr, + "Error on last sector seek to sect %06x offset %06x\n", + cap-1, (cap-1)*ssize); + return 1; + } + if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) { + sim_debug(DEBUG_EXP, dptr, + "Error writing DMAP to sect %06x offset %06x\n", + cap-1, (cap-1)*ssize); + return 1; + } + + /* seek to vendor label area VMAP */ + if ((sim_fseek(uptr->fileref, vaddr*ssize, SEEK_SET)) != 0) { /* seek VMAP */ + sim_debug(DEBUG_EXP, dptr, + "Error on vendor map seek to sect %06x offset %06x\n", + vaddr, vaddr*ssize); + return 1; + } + if ((sim_fwrite((char *)&vmap, sizeof(uint32), 2, uptr->fileref)) != 2) { + sim_debug(DEBUG_CMD, dptr, + "Error writing VMAP to sect %06x offset %06x\n", + vaddr, vaddr*ssize); + return 1; + } + + /* write DMAP to daddr that is the address in trk 0 label */ + if ((sim_fseek(uptr->fileref, daddr*ssize, SEEK_SET)) != 0) { /* seek DMAP */ + sim_debug(DEBUG_CMD, dptr, + "Error on diag map seek to sect %06x offset %06x\n", + daddr, daddr*ssize); + return 1; + } + if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) { + sim_debug(DEBUG_CMD, dptr, + "Error writing DMAP to sect %06x offset %06x\n", + daddr, daddr*ssize); + return 1; + } + + /* write dummy UTX DMAP to faddr */ + if ((sim_fseek(uptr->fileref, faddr*ssize, SEEK_SET)) != 0) { /* seek DMAP */ + sim_debug(DEBUG_CMD, dptr, + "Error on media flaw map seek to sect %06x offset %06x\n", + faddr, faddr*ssize); + return 1; + } + if ((sim_fwrite((char *)&fmap, sizeof(uint32), 4, uptr->fileref)) != 4) { + sim_debug(DEBUG_CMD, dptr, + "Error writing UTX flaw map to sect %06x offset %06x\n", + faddr, faddr*ssize); + return 1; + } + + printf("Disk %s has %x (%d) cyl, %x (%d) hds, %x (%d) sec\r\n", + disk_type[type].name, CYL(type), CYL(type), HDS(type), HDS(type), + SPT(type), SPT(type)); + printf("writing to vmap sec %x (%d) bytes %x (%d)\r\n", + vaddr, vaddr, (vaddr)*ssize, (vaddr)*ssize); + printf("writing to dmap sec %x (%d) %x (%d) dmap to %x (%d) %x (%d)\r\n", + cap-1, cap-1, (cap-1)*ssize, (cap-1)*ssize, + daddr, daddr, daddr*ssize, daddr*ssize); + printf("writing to fmap sec %x (%d) bytes %x (%d)\r\n", + faddr, faddr, (faddr)*ssize, (faddr)*ssize); + printf("writing to umap sec %x (%d) bytes %x (%d)\r\n", + uaddr, uaddr, (uaddr)*ssize, (uaddr)*ssize); + + /* create labels for disk */ + i = disk_label(uptr); /* label disk */ + + /* seek home again */ + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + fprintf (stderr, "Error on seek to 0\r\n"); + return 1; + } + return i; /* good or error */ +} + +/* attach the selected file to the disk */ +t_stat disk_attach(UNIT *uptr, CONST char *file) +{ + uint16 chsa = GET_UADDR(uptr->CMD); + CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */ + int type = GET_TYPE(uptr->flags); + DEVICE *dptr = get_dev(uptr); + DIB *dibp = 0; + t_stat r,s; + uint32 ssize; /* sector size in bytes */ + uint32 info, good; + uint8 buff[1024]; + int i, j; + + /* last sector address of disk (cyl * hds * spt) - 1 */ + uint32 laddr = CAP(type) - 1; /* last sector of disk */ + /* get sector address of utx diag map (DMAP) track 0 pointer */ + /* put data = 0xf0000000 + (cyl-1), 0x8a000000 + daddr, */ + /* 0x9a000000 + (cyl-1), 0xf4000000 */ + int32 daddr = (CYL(type)-4) * SPC(type) + (HDS(type)-2) * SPT(type); + /* defect map */ + uint32 dmap[4] = {0xf0000000 | (CAP(type)-1), 0x8a000000 | daddr, + 0x9a000000 | (CAP(type)-1), 0xf4000000}; + + for (i=0; i<4; i++) { /* byte swap data for last sector */ + dmap[i] = (((dmap[i] & 0xff) << 24) | ((dmap[i] & 0xff00) << 8) | + ((dmap[i] & 0xff0000) >> 8) | ((dmap[i] >> 24) & 0xff)); + } + + /* see if valid disk entry */ + if (disk_type[type].name == 0) { /* does the assigned disk have a name */ + detach_unit(uptr); /* no, reject */ + return SCPE_FMT; /* error */ + } + + if (dptr->flags & DEV_DIS) { + fprintf(sim_deb, + "ERROR===ERROR\nDisk device %s disabled on system, aborting\r\n", + dptr->name); + printf("ERROR===ERROR\nDisk device %s disabled on system, aborting\r\n", + dptr->name); + return SCPE_UDIS; /* device disabled */ + } + + /* have simulator attach the file to the unit */ + if ((r = attach_unit(uptr, file)) != SCPE_OK) + return r; + + uptr->capac = CAP(type); /* disk capacity in sectors */ + ssize = SSB(type); /* get sector size in bytes */ + for (i=0; i<(int)ssize; i++) + buff[i] = 0; /* zero the buffer */ + + sim_debug(DEBUG_CMD, dptr, + "Disk %s cyl %d hds %d sec %d ssiz %d capacity %d\n", + disk_type[type].name, disk_type[type].cyl, disk_type[type].nhds, + disk_type[type].spt, ssize, uptr->capac); /* disk capacity */ + printf("Disk %s cyl %d hds %d sec %d ssiz %d capacity %d\r\n", + disk_type[type].name, disk_type[type].cyl, disk_type[type].nhds, + disk_type[type].spt, ssize, uptr->capac); /* disk capacity */ + + /* see if -i or -n specified on attach command */ + if ((sim_switches & SWMASK('N')) || (sim_switches & SWMASK('I'))) { + goto fmt; /* user wants new disk */ + } + + /* seek to end of disk */ + if ((sim_fseek(uptr->fileref, 0, SEEK_END)) != 0) { + sim_debug(DEBUG_CMD, dptr, "UDP Disk attach SEEK end failed\n"); + printf("Disk attach SEEK end failed\r\n"); + goto fmt; /* not setup, go format */ + } + + s = ftell(uptr->fileref); /* get current file position */ + if (s == 0) { + sim_debug(DEBUG_CMD, dptr, "UDP Disk attach ftell failed s=%06d\n", s); + printf("Disk attach ftell failed s=%06d\r\n", s); + goto fmt; /* not setup, go format */ + } + sim_debug(DEBUG_CMD, dptr, "UDP Disk attach ftell value s=%06d b=%06d CAP %06d\n", s/ssize, s, CAP(type)); + printf("Disk attach ftell value s=%06d b=%06d CAP %06d\r\n", s/ssize, s, CAP(type)); + + if (((int)s/(int)ssize) < ((int)CAP(type))) { /* full sized disk? */ + j = (CAP(type) - (s/ssize)); /* get # sectors to write */ + sim_debug(DEBUG_CMD, dptr, + "Disk attach for MPX 1.X needs %04d more sectors added to disk\n", j); + printf("Disk attach for MPX 1.X needs %04d more sectors added to disk\r\n", j); + /* must be MPX 1.X disk, extend to MPX 3.X size */ + /* write sectors of zero to end of disk to fill it out */ + for (i=0; ifileref) != ssize)) { + sim_debug(DEBUG_CMD, dptr, "Disk attach fread ret = %04d\n", r); + printf("Disk attach fread ret = %04d\r\n", r); + goto fmt; /* not setup, go format */ + } + } + s = ftell(uptr->fileref); /* get current file position */ + sim_debug(DEBUG_CMD, dptr, + "Disk attach MPX 1.X file extended & sized secs %06d bytes %06d\n", s/ssize, s); + printf("Disk attach MPX 1.X file extended & sized secs %06d bytes %06d\r\n", s/ssize, s); + } + + /* seek last sector of disk */ + if ((sim_fseek(uptr->fileref, (CAP(type)-1)*ssize, SEEK_SET)) != 0) { + sim_debug(DEBUG_CMD, dptr, "UDP Disk attach SEEK last sector failed\n"); + printf("UDP Disk attach SEEK last sector failed\r\n"); + goto fmt; + } + + /* see if there is disk size-1 in last sector of disk, if not add it */ + if ((r = sim_fread(buff, sizeof(uint8), ssize, uptr->fileref) != ssize)) { + sim_debug(DEBUG_CMD, dptr, "UDP Disk format fread error = %04d\n", r); + printf("UDP Disk format fread error = %04d\r\n", r); +add_size: + /* write dmap data to last sector on disk for mpx 1.x */ + if ((sim_fseek(uptr->fileref, laddr*ssize, SEEK_SET)) != 0) { /* seek last sector */ + sim_debug(DEBUG_CMD, dptr, + "Disk Error on last sector seek to sect %06d offset %06d bytes\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + printf("Disk Error on last sector seek to sect %06d offset %06d bytes\r\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + goto fmt; + } + if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) { + sim_debug(DEBUG_CMD, dptr, + "Disk Error writing DMAP to sect %06x offset %06d bytes\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + printf("Disk Error writing DMAP to sect %06x offset %06d bytes\r\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + goto fmt; + } + + /* seek last sector of disk */ + if ((sim_fseek(uptr->fileref, (CAP(type))*ssize, SEEK_SET)) != 0) { + sim_debug(DEBUG_CMD, dptr, "Disk attach SEEK last sector failed\n"); + printf("Disk attach SEEK last sector failed\r\n"); + goto fmt; + } + s = ftell(uptr->fileref); /* get current file position */ + sim_debug(DEBUG_CMD, dptr, + "UDP Disk attach MPX file extended & sized secs %06d bytes %06d\n", s/ssize, s); + printf("UDP Disk attach MPX file extended & sized secs %06d bytes %06d\r\n", s/ssize, s); + goto ldone; + } else { + /* if not disk size, go add it in for MPX, error if UTX */ + if ((buff[0] | buff[1] | buff[2] | buff[3]) == 0) { + sim_debug(DEBUG_CMD, dptr, + "UDP Disk format0 buf0 %02x buf1 %02x buf2 %02x buf3 %02x\n", + buff[0], buff[1], buff[2], buff[3]); + goto add_size; + } + } + + info = (buff[0]<<24) | (buff[1]<<16) | (buff[2]<<8) | buff[3]; + good = 0xf0000000 | (CAP(type)-1); + /* check for 0xf0ssssss where ssssss is disk size-1 in sectors */ + if (info != good) { + sim_debug(DEBUG_EXP, dptr, + "Disk format error buf0 %02x buf1 %02x buf2 %02x buf3 %02x\n", + buff[0], buff[1], buff[2], buff[3]); + printf("Disk format error buf0 %02x buf1 %02x buf2 %02x buf3 %02x\r\n", + buff[0], buff[1], buff[2], buff[3]); +fmt: + /* format the drive */ + if (disk_format(uptr)) { + detach_unit(uptr); /* if no space, error */ + return SCPE_FMT; /* error */ + } + } + +ldone: + /* see if disk has labels already, seek to sector past end of disk */ + if ((sim_fseek(uptr->fileref, CAP(type)*ssize, SEEK_SET)) != 0) { /* seek end */ + sim_debug(DEBUG_CMD, dptr, "UDP Disk attach SEEK last sector @ldone failed\n"); + printf("UDP Disk attach SEEK last sector @ldone failed\r\n"); + detach_unit(uptr); /* detach if error */ + return SCPE_FMT; /* error */ + } + + i = SCPE_OK; + /* see if disk has labels already, seek to sector past end of disk */ + if ((r = sim_fread(buff, sizeof(uint8), 30, uptr->fileref) != 30)) { + /* the disk does not have labels, add them on */ + /* create labels for disk */ + sim_debug(DEBUG_CMD, dptr, + "File %s attached to %s creating labels\n", + file, disk_type[type].name); + printf("File %s attached to %s creating labels\r\n", + file, disk_type[type].name); + i = disk_label(uptr); /* label disk */ + if (i != 0) { + detach_unit(uptr); /* detach if error */ + return SCPE_FMT; /* error */ + } + } else { + int32 uaddr = (CYL(type)-4) * SPC(type) + (HDS(type)-4) * SPT(type); + /* uaddr has umap value for track zero label */ + /* output umap address */ + buff[16] = (uaddr >> 24) & 0xff; /* lumapp DMAP pointer */ + buff[17] = (uaddr >> 16) & 0xff; + buff[18] = (uaddr >> 8) & 0xff; + buff[19] = (uaddr) & 0xff; + if ((sim_fseek(uptr->fileref, CAP(type)*ssize, SEEK_SET)) != 0) { /* seek end */ + detach_unit(uptr); /* detach if error */ + return SCPE_FMT; /* error */ + } + /* output updated umap address to track 0 for UTX21a */ + if ((sim_fwrite(buff, sizeof(uint8), 30, uptr->fileref)) != 30) { + sim_debug(DEBUG_EXP, dptr, + "Error writing back track 0 label to sect %06x offset %06x\n", + CAP(type), CAP(type)*ssize); + return SCPE_FMT; /* error */ + } + } + + /* UTX map (NUMP) does not insert an F4 after the replacement tracks */ + /* so do it after the tracks are defined to stop halt on bootup */ + /* utxmap + 32 + 88 + (3*spare) + 1 */ + /* spare count is at utxmap + 8w (32) */ + + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + detach_unit(uptr); /* detach if error */ + return SCPE_FMT; /* error */ + } + + /* start out at sector 0 */ + uptr->CHS = 0; /* set CHS to cyl/hd/sec = 0 */ + + sim_debug(DEBUG_CMD, dptr, + "UDP %s cyl %d hds %d spt %d spc %d cap sec %d cap bytes %d\n", + disk_type[type].name, CYL(type), HDS(type), SPT(type), SPC(type), + CAP(type), CAPB(type)); + printf("UDP Attach %s cyl %d hds %d spt %d spc %d cap sec %d cap bytes %d\r\n", + disk_type[type].name, CYL(type), HDS(type), SPT(type), SPC(type), + CAP(type), CAPB(type)); + + sim_debug(DEBUG_CMD, dptr, + "UDP File %s attached to %s with labels\n", + file, disk_type[type].name); + printf("UDP File %s attached to %s with labels\r\n", + file, disk_type[type].name); + + /* check for valid configured disk */ + /* must have valid DIB and Channel Program pointer */ + dibp = (DIB *)dptr->ctxt; /* get the DIB pointer */ + if ((dib_unit[chsa] == NULL) || (dibp == NULL) || (chp == NULL)) { + sim_debug(DEBUG_EXP, dptr, + "ERROR===ERROR\nUDP device %s not configured on system, aborting\n", + dptr->name); + printf("ERROR===ERROR\nUDP device %s not configured on system, aborting\r\n", + dptr->name); + detach_unit(uptr); /* detach if error */ + return SCPE_UNATT; /* error */ + } + set_devattn(chsa, SNS_DEVEND); + return SCPE_OK; +} + +/* detach a disk device */ +t_stat disk_detach(UNIT *uptr) { + uptr->SNS = 0; /* clear sense data */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + return detach_unit(uptr); /* tell simh we are done with disk */ +} + +/* boot from the specified disk unit */ +t_stat disk_boot(int32 unit_num, DEVICE *dptr) { + UNIT *uptr = &dptr->units[unit_num]; /* find disk unit number */ + + sim_debug(DEBUG_CMD, dptr, + "Disk Boot dev/unit %x\n", GET_UADDR(uptr->CMD)); + + /* see if device disabled */ + if (dptr->flags & DEV_DIS) { + printf("ERROR===ERROR\r\nDisk device %s disabled on system, aborting\r\n", + dptr->name); + return SCPE_UDIS; /* device disabled */ + } + + if ((uptr->flags & UNIT_ATT) == 0) { + sim_debug(DEBUG_EXP, dptr, + "Disk Boot attach error dev/unit %04x\n", + GET_UADDR(uptr->CMD)); + return SCPE_UNATT; /* attached? */ + } + + SPAD[0xf4] = GET_UADDR(uptr->CMD); /* put boot device chan/sa into spad */ + SPAD[0xf8] = 0xF000; /* show as F class device */ + + /* now boot the disk */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + return chan_boot(GET_UADDR(uptr->CMD), dptr); /* boot the ch/sa */ +} + +/* Disk option setting commands */ +/* set the disk type attached to unit */ +t_stat disk_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int i; + + if (cptr == NULL) /* any disk name input? */ + return SCPE_ARG; /* arg error */ + if (uptr == NULL) /* valid unit? */ + return SCPE_IERR; /* no, error */ + if (uptr->flags & UNIT_ATT) /* is unit attached? */ + return SCPE_ALATT; /* no, error */ + + /* now loop through the units and find named disk */ + for (i = 0; disk_type[i].name != 0; i++) { + if (strcmp(disk_type[i].name, cptr) == 0) { + uptr->flags &= ~UNIT_TYPE; /* clear the old UNIT type */ + uptr->flags |= SET_TYPE(i); /* set the new type */ + /* set capacity of disk in sectors */ + uptr->capac = CAP(i); + return SCPE_OK; + } + } + return SCPE_ARG; +} + +t_stat disk_get_type(FILE *st, UNIT *uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fputs("TYPE=", st); + fputs(disk_type[GET_TYPE(uptr->flags)].name, st); + return SCPE_OK; +} + +/* help information for disk */ +t_stat disk_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + int i; + fprintf (st, "SEL 2314 Disk Processor II\r\n"); + fprintf (st, "Use:\r\n"); + fprintf (st, " sim> SET %sn TYPE=type\r\n", dptr->name); + fprintf (st, "Type can be: "); + for (i = 0; disk_type[i].name != 0; i++) { + fprintf(st, "%s", disk_type[i].name); + if (disk_type[i+1].name != 0) + fprintf(st, ", "); + } + fprintf (st, ".\nEach drive has the following storage capacity:\r\n"); + for (i = 0; disk_type[i].name != 0; i++) { + int32 size = CAPB(i); /* disk capacity in bytes */ + size /= 1024; /* make KB */ + size = (10 * size) / 1024; /* size in MB * 10 */ + fprintf(st, " %-8s %4d.%1d MB cyl %3d hds %3d sec %3d blk %3d\r\n", + disk_type[i].name, size/10, size%10, CYL(i), HDS(i), SPT(i), SSB(i)); + } + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char *disk_description (DEVICE *dptr) +{ + return "SEL 2314 Disk Processor II"; +} + +#endif diff --git a/SEL32/sel32_ec.c b/SEL32/sel32_ec.c new file mode 100644 index 00000000..5c49ba7d --- /dev/null +++ b/SEL32/sel32_ec.c @@ -0,0 +1,1909 @@ +/* sel32_ec.c: SEL-32 8516 Ethernet controller. + + Copyright (c) 2020-2022, Richard Cornwell + Portions provided by James C. Bevier and other SIMH contributers + + 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 + RICHARD CORNWELL 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. +*/ + +#include "sel32_defs.h" + +#if NUM_DEVS_ETHER > 0 +#include "sim_ether.h" + +/* allow 3 modes */ +#define UNIT_V_MODE (UNIT_V_UF + 1) +#define UNIT_MODE (0x3 << UNIT_V_MODE) +/* get & set disk types */ +#define GET_MODE(x) ((UNIT_MODE & (x)) >> UNIT_V_MODE) +#define SET_MODE(x) (UNIT_MODE & ((x) << UNIT_V_MODE)) + + +#define CMD u3 +/* u3 */ +/* in u3 is device command code and status */ +#define EC_CMDMSK 0x0ff /* Command being run */ +/* commands */ +#define EC_INCH 0x00 /* Initialize channel */ +#define EC_INCH2 0xF0 /* Initialize channel command for processing */ +#define EC_WRITE 0x01 /* Write frame */ +#define EC_READ 0x02 /* Read frame*/ +#define EC_NOP 0x03 /* No operation */ +#define EC_SNS 0x04 /* Sense */ +#define EC_LIA 0x07 /* Load individual address */ +#define EC_TIC 0x08 /* Transfer in channel */ +#define EC_CGA 0x0B /* Disable multicast address */ +#define EC_LGA 0x0F /* Load Multicast address */ +#define EC_LCC 0x10 /* Configure LCC */ +#define EC_STATS 0x14 /* Read Statistics */ +#define EC_CSTATS 0x15 /* Clear software counters */ +#define EC_BUSY 0x100 /* Mark Device as Busy */ + +#define SNS u5 +/* u5 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x80000000 /* Command reject */ +#define SNS_SPARE0 0x40000000 /* Spare */ +#define SNS_SPARE1 0x20000000 /* Spare */ +#define SNS_EQUCHK 0x10000000 /* Equipment check */ +#define SNS_SPARE2 0x08000000 /* Spare */ +#define SNS_SPARE3 0x04000000 /* Spare */ +#define SNS_MODE_M 0x03000000 /* Mode Mask */ + +/* Sense byte 1 */ +#define SNS_RCV_RDY 0x00800000 /* Receive unit ready */ +#define SNS_TMT_DEF 0x00400000 /* Transmission deferred */ +#define SNS_COL_RTY 0x00300000 /* Collision retry */ +#define SNS_HRT_TST 0x00080000 /* Heartbeat test failure */ +#define SNS_DMA_UND 0x00040000 /* DMA under run */ +#define SNS_LST_CTS 0x00020000 /* Lost Clear to send */ +#define SNS_NO_CAR 0x00010000 /* No carrier. */ + +/* Sense byte 2 & 3 */ +#define SNS_XFR_MASK 0x0000FFFF /* Previous frame count */ + +typedef uint32 in_addr_T; + +#define ETHTYPE_ARP 0x0806 +#define ETHTYPE_IP 0x0800 + +#define STAT_FR_ALIGN 0 /* Frame alignment errors */ +#define STAT_FR_CRC 1 /* Frame CRC errors */ +#define STAT_LCL_AVAIL 2 /* Local bus available errors */ +#define STAT_LCL_OVER 3 /* Local bus overflow */ +#define STAT_TX_COLL 4 /* Transmission collisions */ +#define STAT_RX_LEN 5 /* Receive length errors */ +#define STAT_TX_SUCC 6 /* Transmitt success after 2-15 collisions */ +#define STAT_TX_DEF 7 /* Transmitt deferred */ +#define STAT_TX_UNSUCC 8 /* Transmitt unsuccessful */ +#define STAT_TX_SUCC1 9 /* Transmitt success after 1 collision */ +#define STAT_LEN 10 /* Number of half word stats */ + +PACKED_BEGIN +struct ec_eth_hdr { + ETH_MAC dest; + ETH_MAC src; + uint16 type; +} PACKED_END; + +/* + * Structure of an internet header, naked of options. + */ +PACKED_BEGIN +struct ip { + uint8 ip_v_hl; /* version,header length */ + uint8 ip_tos; /* type of service */ + uint16 ip_len; /* total length */ + uint16 ip_id; /* identification */ + uint16 ip_off; /* fragment offset field */ +#define IP_DF 0x4000 /* don't fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + uint8 ip_ttl; /* time to live */ + uint8 ip_p; /* protocol */ + uint16 ip_sum; /* checksum */ + in_addr_T ip_src; + in_addr_T ip_dst; /* source and dest address */ +} PACKED_END; + +#define TCP_PROTO 6 +PACKED_BEGIN +struct tcp { + uint16 tcp_sport; /* Source port */ + uint16 tcp_dport; /* Destination port */ + uint32 seq; /* Sequence number */ + uint32 ack; /* Ack number */ + uint16 flags; /* Flags */ +#define TCP_FL_FIN 0x01 +#define TCP_FL_SYN 0x02 +#define TCP_FL_RST 0x04 +#define TCP_FL_PSH 0x08 +#define TCP_FL_ACK 0x10 +#define TCP_FL_URG 0x20 + uint16 window; /* Window size */ + uint16 chksum; /* packet checksum */ + uint16 urgent; /* Urgent pointer */ +} PACKED_END; + +#define UDP_PROTO 17 +PACKED_BEGIN +struct udp { + uint16 udp_sport; /* Source port */ + uint16 udp_dport; /* Destination port */ + uint16 len; /* Length */ + uint16 chksum; /* packet checksum */ +} PACKED_END; + +PACKED_BEGIN +struct udp_hdr { + in_addr_T ip_src; + in_addr_T ip_dst; /* source and dest address */ + uint8 zero; + uint8 proto; /* Protocol */ + uint16 hlen; /* Length of header and data */ +} PACKED_END; + +#define ICMP_PROTO 1 +PACKED_BEGIN +struct icmp { + uint8 type; /* Type of packet */ + uint8 code; /* Code */ + uint16 chksum; /* packet checksum */ +} PACKED_END; + +PACKED_BEGIN +struct ip_hdr { + struct ec_eth_hdr ethhdr; + struct ip iphdr; +} PACKED_END; + +#define ARP_REQUEST 1 +#define ARP_REPLY 2 +#define ARP_HWTYPE_ETH 1 + +PACKED_BEGIN +struct arp_hdr { + struct ec_eth_hdr ethhdr; + uint16 hwtype; + int16 protocol; + uint8 hwlen; + uint8 protolen; + uint16 opcode; + ETH_MAC shwaddr; + in_addr_T sipaddr; + ETH_MAC dhwaddr; + in_addr_T dipaddr; + uint8 padding[18]; +} PACKED_END; + +struct ec_device { + ETH_PCALLBACK rcallback; /* read callback routine */ + ETH_PCALLBACK wcallback; /* write callback routine */ + ETH_MAC mac; /* Hardware MAC addresses */ + ETH_DEV etherface; + ETH_QUE ReadQ; + ETH_PACK rec_buff[1024]; /* Buffer for received packet */ + ETH_PACK snd_buff; /* Buffer for sending packet */ + int macs_n; /* Number of multi-cast addresses */ + ETH_MAC macs[67]; /* Watched Multi-cast addresses */ + int amc; /* Recieve all multicast packets */ + uint32 rx_count; /* Packets received */ + uint32 tx_count; /* Packets sent */ + t_stat drop_cnt; /* Packets dropped */ + int r_pkt; /* Packet pending */ + int poll; /* Need to poll receiver */ + int lp_rdy; /* Loop back packet ready */ + int rec_ptr; /* Receive pointer */ + int xtr_ptr; /* Extract pointer */ + uint8 conf[12]; /* user specified configuration */ +} ec_data; + +#define LOOP_MSK 0x3ff + +extern int32 tmxr_poll; +extern uint32 readfull(CHANP *chp, uint32 maddr, uint32 *word); +extern uint32 cont_chan(uint16 chsa); + +static CONST ETH_MAC broadcast_ethaddr = {0xff,0xff,0xff,0xff,0xff,0xff}; + +/* channel program information */ +CHANP ec_chp[NUM_UNITS_ETHER] = {0}; + +/* forward definitions */ +t_stat ec_preio(UNIT *uptr, uint16 chan); +t_stat ec_startcmd(UNIT *uptr, uint16 chan, uint8 cmd); +t_stat ec_rec_srv(UNIT *uptr); +t_stat ec_srv(UNIT *uptr); +t_stat ec_haltio(UNIT *uptr); +t_stat ec_iocl(CHANP *chp, int32 tic_ok); +void ec_packet_debug(struct ec_device *ec, const char *action, ETH_PACK *packet); +t_stat ec_reset (DEVICE *dptr); +void ec_ini(UNIT *, t_bool); +t_stat ec_rsctrl(UNIT *uptr); +t_stat ec_rschnlio(UNIT *uptr); +t_stat ec_show_mac (FILE* st, UNIT* uptr, int32 val, CONST void* desc); +t_stat ec_set_mac (UNIT* uptr, int32 val, CONST char* cptr, void* desc); +t_stat ec_show_mode (FILE* st, UNIT* uptr, int32 val, CONST void* desc); +t_stat ec_set_mode (UNIT* uptr, int32 val, CONST char* cptr, void* desc); +t_stat ec_attach (UNIT * uptr, CONST char * cptr); +t_stat ec_detach (UNIT * uptr); +t_stat ec_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +const char *ec_description (DEVICE *dptr); + +#define ec_master_uptr (&ec_unit[0]) /* Unit doing receive digestion */ + +UNIT ec_unit[] = { + {UDATA(ec_rec_srv, UNIT_IDLE|UNIT_ATTABLE, 0), 0, UNIT_ADDR(0xE00)}, /* 0 */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE01)}, /* 1 */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE02)}, /* 2 */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE03)}, /* 3 */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE04)}, /* 4 */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE05)}, /* 5 */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE06)}, /* 6 */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE07)}, /* 7 */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE08)}, /* 8 */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE09)}, /* 9 */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE0A)}, /* A */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE0B)}, /* B */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE0C)}, /* C */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE0D)}, /* D */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE0E)}, /* E */ + {UDATA(ec_srv, UNIT_IDLE|UNIT_DIS|UNIT_SUBCHAN, 0), 0, UNIT_ADDR(0xE0F)}, /* F */ +}; + +DIB ec_dib = { + ec_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + ec_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + ec_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + ec_rsctrl, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + ec_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + ec_iocl, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + ec_ini, /* void (*dev_ini)(UNIT *uptr) */ /* init function */ + ec_unit, /* UNIT *units */ /* Pointer to units structure */ + ec_chp, /* CHANP *chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_UNITS_ETHER, /* number of units defined */ + 0x0F, /* device mask */ + 0x0E00, /* parent channel address */ + 0, /* fifo input index */ + 0, /* fifo output index */ + {0}, /* interrupt status fifo for channel */ +}; + +MTAB ec_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NC, 0, "MODE", "MODE=#", + &ec_set_mode, &ec_show_mode, NULL, "Ethernet mode" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NC, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx", + &ec_set_mac, &ec_show_mac, NULL, "MAC address" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "ETH", NULL, NULL, + ð_show, NULL, "Display attachedable devices" }, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", + &set_dev_addr, &show_dev_addr, NULL, "Device channel address"}, + { 0 } + }; + +/* Simulator debug controls */ +DEBTAB ec_debug[] = { + {"CMD", DEBUG_CMD, "Show command execution to devices"}, + {"DATA", DEBUG_DATA, "Show data transfers"}, + {"DETAIL", DEBUG_DETAIL, "Show details about device"}, + {"EXP", DEBUG_EXP, "Show exception information"}, + {"IRQ", DEBUG_IRQ, "Show IRQ requests"}, + {"XIO", DEBUG_XIO, "Show XIO I/O instructions"}, +#define DEBUG_ARP (DEBUG_IRQ<<1) + {"ARP", DEBUG_ARP, "Show ARP activities"}, +#define DEBUG_TCP (DEBUG_ARP<<1) + {"TCP", DEBUG_TCP, "Show TCP packet activities"}, +#define DEBUG_UDP (DEBUG_TCP<<1) + {"UDP", DEBUG_UDP, "Show UDP packet activities"}, +#define DEBUG_ICMP (DEBUG_UDP<<1) + {"ICMP", DEBUG_ICMP, "Show ICMP packet activities"}, +#define DEBUG_ETHER (DEBUG_ICMP<<1) + {"ETHER", DEBUG_ETHER, "Show ETHER activities"}, + {0, 0} +}; + +DEVICE ec_dev = { + "EC", ec_unit, NULL, ec_mod, + NUM_UNITS_ETHER, 16, 24, 4, 16, 32, + NULL, NULL, &ec_reset, NULL, &ec_attach, &ec_detach, + &ec_dib, DEV_DISABLE | DEV_DEBUG | DEV_ETHER, 0, ec_debug, + NULL, NULL, &ec_help, NULL, NULL, &ec_description +}; + +/* load in the IOCD and process the commands */ +/* return = 0 OK */ +/* return = 1 error, chan_status will have reason */ +t_stat ec_iocl(CHANP *chp, int32 tic_ok) +{ + uint32 word1 = 0; + uint32 word2 = 0; + int32 docmd = 0; + UNIT *uptr = chp->unitptr; /* get the unit ptr */ + uint16 chan = get_chan(chp->chan_dev); /* our channel */ + uint16 chsa = chp->chan_dev; + uint16 devstat = 0; + DEVICE *dptr = get_dev(uptr); + + /* check for valid iocd address if 1st iocd */ + if (chp->chan_info & INFO_SIOCD) { /* see if 1st IOCD in channel prog */ + if (chp->chan_caw & 0x3) { /* must be word bounded */ + sim_debug(DEBUG_EXP, dptr, + "ec_iocl iocd bad address chsa %02x caw %06x\n", + chsa, chp->chan_caw); + chp->ccw_addr = chp->chan_caw; /* set the bad iocl address */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid iocd addr */ + return 1; /* error return */ + } + } +loop: + sim_debug(DEBUG_EXP, dptr, + "ec_iocl @%06x @loop chan_status[%04x] %04x SNS %08x\n", + chp->chan_caw, chan, chp->chan_status, uptr->SNS); + + /* Abort if we have any errors */ + if (chp->chan_status & STATUS_ERROR) { /* check channel error status */ + sim_debug(DEBUG_EXP, dptr, + "ec_iocl ERROR1 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* return error */ + } + + /* Read in first CCW */ + if (readfull(chp, chp->chan_caw, &word1) != 0) { /* read word1 from memory */ + chp->chan_status |= STATUS_PCHK; /* memory read error, program check */ + sim_debug(DEBUG_EXP, dptr, + "ec_iocl ERROR2 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* Read in second CCW */ + if (readfull(chp, chp->chan_caw+4, &word2) != 0) { /* read word2 from memory */ + chp->chan_status |= STATUS_PCHK; /* memory read error, program check */ + sim_debug(DEBUG_EXP, dptr, + "ec_iocl ERROR3 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + sim_debug(DEBUG_CMD, dptr, + "ec_iocl @%06x read ccw chsa %04x IOCD wd 1 %08x wd 2 %08x SNS %08x\n", + chp->chan_caw, chp->chan_dev, word1, word2, uptr->SNS); +//#define DYNAMIC_DEBUG +#ifdef DYNAMIC_DEBUG + if ((word1 == 0x0202f000) && (word2 == 0x0000003C) && (uptr->SNS == 0x0080003e)) { + cpu_dev.dctrl |= (DEBUG_INST|DEBUG_XIO); /* start instruction trace */ + } else + if ((word1 == 0x0202f000) && (word2 == 0x00000040) && (uptr->SNS == 0x0080003e)) { + cpu_dev.dctrl &= ~(DEBUG_INST|DEBUG_XIO); /* stop instruction trace */ + } +#endif + + chp->chan_caw = (chp->chan_caw & 0xfffffc) + 8; /* point to next IOCD */ + + /* Check if we had data chaining in previous iocd */ + /* if we did, use previous cmd value */ + if (((chp->chan_info & INFO_SIOCD) == 0) && /* see if 1st IOCD in channel prog */ + (chp->ccw_flags & FLAG_DC)) { /* last IOCD have DC set? */ + sim_debug(DEBUG_CMD, dptr, + "ec_iocl @%06x DO DC, ccw_flags %04x cmd %02x\n", + chp->chan_caw, chp->ccw_flags, chp->ccw_cmd); + } else + chp->ccw_cmd = (word1 >> 24) & 0xff; /* set new command from IOCD wd 1 */ + + chp->ccw_count = 0; + + if (!MEM_ADDR_OK(word1 & MASK24)) { /* see if memory address invalid */ + chp->chan_status |= STATUS_PCHK; /* bad, program check */ + sim_debug(DEBUG_EXP, dptr, + "ec_iocl mem error PCHK chan_status[%04x] %04x addr %08x\n", + chan, chp->chan_status, word1 & MASK24); + return 1; /* error return */ + } + + /* this switch is here to satisify the SEL diag who wants a program */ + /* check error instead of a unit check error for these cmd values??? */ + /* validate the commands for the ethernet */ + switch (chp->ccw_cmd) { + case 0x18: case 0x20: case 0x28: case 0x30: case 0x38: case 0x40: case 0x48: + case 0x50: case 0x58: case 0x60: case 0x68: case 0x70: case 0x78: case 0x80: + case 0x88: case 0x90: case 0x98: case 0xa0: case 0xa8: case 0xb0: case 0xb8: + case 0xc0: case 0xc8: case 0xd0: case 0xd8: case 0xe0: case 0xe8: case 0xf0: + case 0xf8: + uptr->SNS &= ~SNS_CMDREJ; /* remove CMD reject status */ + sim_debug(DEBUG_CMD, dptr, + "ec_iocl illegal at ec_startcmd %02x SNS %08x\n", + chp->ccw_cmd, uptr->SNS); + chp->ccw_count = 0; /* diags want zero count */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid cmd */ + return 1; /* error return */ + + case EC_READ: + /* read count must be multiple of 4 */ + if ((word2 & 0xffff) & 3) { + sim_debug(DEBUG_DETAIL, &ec_dev, + "ec_iocl READ cnt not multiple of 4 %d\n", word2 & 0xffff); + /* diags wants prog check instead of unit check */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid cmd */ + return 1; /* error return */ + } + /* see if too few bytes */ + if (((chp->chan_info & INFO_SIOCD) == 1) && /* see if 1st IOCD in channel prog */ + ((word2 & 0xffff) < 20) && /* and not at least 20 bytes */ + ((word2 & BIT0) == 0)) { /* and not data chained */ + sim_debug(DEBUG_DETAIL, &ec_dev, + "ec_iocl READ error small packet 0x%04x\n", word2 & 0xffff); + /* diags wants incorrect length instead of program check */ + chp->chan_status |= STATUS_LENGTH; /* incorrect length error */ + return 1; /* error return */ + } + /* see if too many bytes */ + if ((word2 & 0xffff) > (ETH_MAX_PACKET+2)) { + sim_debug(DEBUG_DETAIL, &ec_dev, + "ec_iocl READ error large packet 0x%04x\n", word2 & 0xffff); + /* diags wants prog check instead of length check for test 4E */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid cmd */ + return 1; /* error return */ + } + uptr->SNS = 0; + break; + case EC_WRITE: + /* see if too few bytes */ + if (((chp->chan_info & INFO_SIOCD) == 1) && /* see if 1st IOCD in channel prog */ + ((word2 & 0xffff) < 8) && /* and not at least 8 bytes */ + ((word2 & BIT0) == 0)) { /* and not data chained */ + sim_debug(DEBUG_DETAIL, &ec_dev, + "ec_iocl WRITE error small packet 0x%04x\n", word2 & 0xffff); + /* diags wants prog check instead of unit check */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid cmd */ + return 1; /* error return */ + } + /* see if too many bytes */ + if ((word2 & 0xffff) > ETH_MAX_PACKET) { + sim_debug(DEBUG_DETAIL, &ec_dev, + "ec_iocl WRITE error large packet 0x%04x\n", word2 & 0xffff); + /* diags wants prog check instead of length check for test 4E */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid cmd */ + return 1; /* error return */ + } + uptr->SNS = 0; + break; + case EC_INCH: case EC_LIA: + case EC_TIC: case EC_CGA: case EC_LGA: case EC_LCC: + uptr->SNS = 0; + break; + case EC_STATS: case EC_CSTATS: + case EC_SNS: + break; + case EC_NOP: + uptr->SNS = 0; + /* nop must have non zero count */ + if ((word2 & 0xffff) == 0) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid cmd */ + return 1; /* error return */ + } + break; + default: + uptr->SNS |= SNS_CMDREJ; + chp->chan_status |= STATUS_CHECK; /* diags want unit check */ + sim_debug(DEBUG_CMD, dptr, + "ec_startcmd illegal2 cmd %02x SNS %08x\n", + chp->ccw_cmd, uptr->SNS); + return 1; /* error return */ + break; + } + + chp->ccw_count = word2 & 0xffff; /* get 16 bit byte count from IOCD WD 2 */ + + if (chp->chan_info & INFO_SIOCD) { /* see if 1st IOCD in channel prog */ + /* 1st command can not be a TIC */ + if (chp->ccw_cmd == CMD_TIC) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected status */ + sim_debug(DEBUG_EXP, dptr, + "ec_iocl TIC bad cmd chan_status[%04x] %04x\n", + chan, chp->chan_status); + return 1; /* error return */ + } + } + + /* TIC can't follow TIC or be first in command chain */ + /* diags send bad commands for testing. Use all of op */ + if (chp->ccw_cmd == CMD_TIC) { + if (tic_ok) { + if (((word1 & MASK24) == 0) || (word1 & 0x3)) { + sim_debug(DEBUG_EXP, dptr, + "ec_iocl tic cmd bad address chan %02x tic caw %06x IOCD wd 1 %08x\n", + chan, chp->chan_caw, word1); + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected status */ + return 1; /* error return */ + } + tic_ok = 0; /* another tic not allowed */ + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + sim_debug(DEBUG_CMD, dptr, + "ec_iocl tic cmd ccw chan %02x tic caw %06x IOCD wd 1 %08x\n", + chan, chp->chan_caw, word1); + goto loop; /* restart the IOCD processing */ + } + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected status */ + sim_debug(DEBUG_EXP, dptr, + "ec_iocl TIC ERROR chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* Check if we had data chaining in previous iocd */ + if ((chp->chan_info & INFO_SIOCD) || /* see if 1st IOCD in channel prog */ + (((chp->chan_info & INFO_SIOCD) == 0) && /* see if 1st IOCD in channel prog */ + ((chp->ccw_flags & FLAG_DC) == 0))) { /* last IOCD have DC set? */ + sim_debug(DEBUG_CMD, dptr, + "ec_iocl @%06x DO CMD No DC, ccw_flags %04x cmd %02x\n", + chp->chan_caw, chp->ccw_flags, chp->ccw_cmd); + docmd = 1; /* show we have a command */ + } + + /* Set up for this command */ + chp->ccw_flags = (word2 >> 16) & 0xf800; /* get flags from bits 0-4 of WD 2 of IOCD */ + chp->chan_status = 0; /* clear status for next IOCD */ + /* make a 24 bit address */ + chp->ccw_addr = word1 & MASK24; /* set the data/seek address */ + + /* validate parts of IOCD2 that are reserved */ + if (word2 & 0x07ff0000) { /* bits 5-15 must be zero */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid iocd */ + sim_debug(DEBUG_EXP, dptr, + "ec_iocl IOCD2 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* DC can only be used with a read/write cmd */ + if (chp->ccw_flags & FLAG_DC) { + if ((chp->ccw_cmd == EC_INCH) || (chp->ccw_cmd == EC_NOP) || + (chp->ccw_cmd == EC_CGA) || (chp->ccw_cmd == EC_CSTATS)) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid DC */ + sim_debug(DEBUG_EXP, dptr, + "ec_iocl DC ERROR chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + } + + chp->chan_byte = BUFF_BUSY; /* busy & no bytes transferred yet */ + + sim_debug(DEBUG_XIO, dptr, + "ec_iocl @%06x read docmd %01x addr %06x count %04x chan %04x ccw_flags %04x\n", + chp->chan_caw, docmd, chp->ccw_addr, chp->ccw_count, chan, chp->ccw_flags); + + if (docmd) { /* see if we need to process a command */ + DIB *dibp = dib_unit[chp->chan_dev]; /* get the DIB pointer */ + + uptr = chp->unitptr; /* get the unit ptr */ + if (dibp == 0 || uptr == 0) { + chp->chan_status |= STATUS_PCHK; /* program check if it is */ + sim_debug(DEBUG_EXP, dptr, + "ec_iocl bad dibp or uptr chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* if none, error */ + } + + sim_debug(DEBUG_XIO, dptr, + "ec_iocl @%06x before start_cmd chan %04x status %04x count %04x SNS %08x\n", + chp->chan_caw, chan, chp->chan_status, chp->ccw_count, uptr->SNS); + + /* call the device startcmd function to process the current command */ + /* just replace device status bits */ + chp->chan_info &= ~INFO_CEND; /* show chan_end not called yet */ + devstat = dibp->start_cmd(uptr, chan, chp->ccw_cmd); + chp->chan_status = (chp->chan_status & 0xff00) | devstat; + chp->chan_info &= ~INFO_SIOCD; /* show not first IOCD in channel prog */ + + sim_debug(DEBUG_XIO, dptr, + "ec_iocl @%06x after start_cmd chsa %04x status %08x count %04x SNS %08x\n", + chp->chan_caw, chsa, chp->chan_status, chp->ccw_count, uptr->SNS); + + /* see if bad status */ + if (chp->chan_status & (STATUS_ATTN|STATUS_ERROR)) { + chp->chan_status |= STATUS_CEND; /* channel end status */ + chp->ccw_flags = 0; /* no flags */ + chp->chan_byte = BUFF_NEXT; /* have main pick us up */ + sim_debug(DEBUG_EXP, dptr, + "ec_iocl bad status chsa %04x status %04x cmd %02x\n", + chsa, chp->chan_status, chp->ccw_cmd); + /* done with command */ + sim_debug(DEBUG_EXP, &cpu_dev, + "ec_iocl ERROR return chsa %04x status %08x\n", + chp->chan_dev, chp->chan_status); + return 1; /* error return */ + } + /* NOTE this code needed for MPX 1.X to run! */ + /* see if command completed */ + /* we have good status */ + if (chp->chan_status & (STATUS_DEND|STATUS_CEND)) { + uint16 chsa = GET_UADDR(uptr->u3); /* get channel & sub address */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* show I/O complete */ + sim_debug(DEBUG_XIO, dptr, + "ec_iocl @%06x FIFO #%1x cmd complete chan %04x status %04x count %04x\n", + chp->chan_caw, FIFO_Num(chsa), chan, chp->chan_status, chp->ccw_count); + } + } + /* the device processor returned OK (0), so wait for I/O to complete */ + /* nothing happening, so return */ + sim_debug(DEBUG_XIO, dptr, + "ec_iocl @%06x return, chsa %04x status %04x count %04x\n", + chp->chan_caw, chsa, chp->chan_status, chp->ccw_count); + return 0; /* good return */ +} + +/* start an ethernet operation */ +t_stat ec_preio(UNIT *uptr, uint16 chan) { + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); + uint16 chsa = GET_UADDR(uptr->CMD); + + sim_debug(DEBUG_CMD, dptr, "ec_preio CMD %08x unit %02x chsa %04x\n", + uptr->CMD, unit, chsa); + if ((uptr->CMD & EC_CMDMSK) != 0) { /* just return if busy */ + sim_debug(DEBUG_CMD, dptr, + "ec_preio unit %02x chsa %04x BUSY\n", unit, chsa); + return SNS_BSY; + } + + sim_debug(DEBUG_CMD, dptr, "ec_preio CMD %08x unit %02x chsa %04x OK\n", + uptr->CMD, unit, chsa); + return SCPE_OK; /* good to go */ +} + +/* Start ethernet command */ +t_stat ec_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) +{ + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + sim_debug(DEBUG_CMD, dptr, + "ec_startcmd chsa %04x unit %d cmd %02x CMD %08x\n", + chsa, (int)(uptr - ec_unit), cmd, uptr->CMD); + if ((uptr->CMD & 0xff) != 0) { /* if any status info, we are busy */ + sim_debug(DEBUG_CMD, dptr, "ec_startcmd busy\n"); + return SNS_BSY; + } + + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + /* Unit is online, so process a command */ + switch (cmd) { + case EC_WRITE: /* Write command 0x01 */ + uptr->CMD |= (cmd|EC_BUSY); /* save cmd */ + // This works most of the time & stops at test 30 with no len errors +//Was sim_activate(uptr, 5000); /* start things off */ + // This works +/*jb*/ sim_activate(uptr, 7500); /* start things off */ + return 0; + case EC_INCH: /* INCH cmd 0x0 */ + cmd = EC_INCH2; /* set dummy INCH cmd 0xf0 */ + case EC_READ: /* Read command 0x02 */ + case EC_TIC: /* Transfer in channel */ + case EC_CGA: /* Disable multicast address */ + case EC_LCC: /* Configure LCC 0x10 */ + case EC_STATS: /* Read Statistics */ + case EC_CSTATS: /* Clear software counters */ + case EC_NOP: /* NOP 0x03 */ + case EC_LIA: /* Load individual address */ + case EC_LGA: /* Load Multicast address */ + /* Fall through */ + case EC_SNS: /* Sense 0x04 */ + uptr->CMD |= cmd|EC_BUSY; /* save cmd */ +//Was M sim_activate(uptr, 100); /* start things off */ + sim_activate(uptr, 150); /* start things off */ + return 0; + } + + uptr->SNS |= SNS_CMDREJ; + sim_debug(DEBUG_CMD, dptr, "ec_startcmd illegal3 cmd %02x SNS %08x\n", + cmd, uptr->SNS); + chp->ccw_count = 0; /* diags want zero count */ + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; /* diags want unit check */ +} + +/* Handle processing of ethernet requests. */ +t_stat ec_rec_srv(UNIT *uptr) +{ + DEVICE *dptr = get_dev(uptr); + int cmd = uptr->CMD & EC_CMDMSK; + + /* If not in loopback try and receive a packet */ + if ((ec_data.conf[0] & 0x40) == 0) { + int q = (((ec_data.rec_ptr + 1) & LOOP_MSK) + LOOP_MSK + 1) - ec_data.xtr_ptr; + if (q > LOOP_MSK) + q -= (LOOP_MSK + 1); + if (eth_read(&ec_data.etherface, &ec_data.rec_buff[ec_data.rec_ptr], NULL) > 0) { +//jb if (((ec_data.rec_ptr + 1) & LOOP_MSK) == ec_data.xtr_ptr) { +//jb if (q > 16) { + if (q > 716) { + ec_data.drop_cnt++; + sim_debug(DEBUG_DETAIL, dptr, + "ec_rec_srv received packet %08x dropped %08x\n", + ec_data.rx_count, ec_data.drop_cnt); + } else { + ec_data.rec_ptr = (ec_data.rec_ptr + 1) & LOOP_MSK; + ec_data.rx_count++; + sim_debug(DEBUG_DETAIL, dptr, + "ec_rec_srv received packet %08x\n", ec_data.rx_count); + } + } + } + + /* If there is a command on this subchannel, do it */ + if (cmd != 0) + return ec_srv(uptr); + + return SCPE_OK; +} + +/* Handle processing of ethernet requests. */ +t_stat ec_srv(UNIT *uptr) +{ + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */ + int cmd = uptr->CMD & EC_CMDMSK; + uint32 mema; + int i; + int n, len; + int pirq, cnt, dcnt; + uint8 ch; + uint8 buf[1520]; + uint8 *pck; + struct ec_eth_hdr *hdr; + + sim_debug(DEBUG_CMD, dptr, + "ec_srv chp %p cmd=%02x chsa %04x count %04x SNS %08x\n", + chp, cmd, chsa, chp->ccw_count, uptr->SNS); + + switch (cmd) { +// case EC_INCH: /* 0x00 INCH cmd */ + case EC_INCH2: /* 0xF0 INCH cmd */ + len = chp->ccw_count; /* INCH command count */ + mema = chp->ccw_addr; /* get inch or buffer addr */ + sim_debug(DEBUG_CMD, dptr, + "ec_srv starting INCH %06x cmd, chsa %04x addr %06x cnt %04x\n", + chp->chan_inch_addr, chsa, chp->ccw_addr, chp->ccw_count); + /* now call set_inch() function to write and test inch buffer addresses */ + /* Ethernet uses 1 dbl wd */ + i = set_inch(uptr, mema, 1); /* new address */ + ec_ini(uptr, 0); + if ((i == SCPE_MEM) || (i == SCPE_ARG)) { /* any error */ + /* we have error, bail out */ + uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + for (i=0; i < len; i++) { + if (chan_read_byte(chsa, &buf[i])) { + /* we have error, bail out */ + uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + /* just dump data */ + } + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case EC_LIA: /* 0x07 Load individual address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + for(i = 0; i < sizeof (ETH_MAC); i++) { + if (chan_read_byte(chsa, &buf[i])) { + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + + } + } + memcpy(&ec_data.mac, &buf[0], sizeof (ETH_MAC)); + eth_mac_fmt(&ec_data.mac, (char *)&buf[0]); + sim_debug(DEBUG_CMD, dptr, "ec_srv setting mac %s\n", buf); + n = ec_data.macs_n + 2; + memcpy(&ec_data.macs[0], &ec_data.mac, sizeof (ETH_MAC)); + memcpy(&ec_data.macs[1], &broadcast_ethaddr, sizeof (ETH_MAC)); + if (ec_master_uptr->flags & UNIT_ATT) + /* set promiscuous if bit 7 of byte zero of mac address is set */ + eth_filter (&ec_data.etherface, n, ec_data.macs, ec_data.amc, + ec_data.macs[0][0] & 1); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case EC_CGA: /* 0x0B Disable multicast address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + ec_data.macs_n = 0; + ec_data.amc = 0; + if (ec_master_uptr->flags & UNIT_ATT) + eth_filter (&ec_data.etherface, 2, ec_data.macs, ec_data.amc, + ec_data.macs[0][0] & 1); + if (chan_read_byte(chsa, &ch)) + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case EC_LGA: /* 0x0F Load Multicast address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + ec_data.macs_n = 0; + len = 2; + for(n = 2; n < (int)(sizeof(ec_data.macs) / sizeof (ETH_MAC)); n++) { + for(i = 0; i < sizeof (ETH_MAC); i++) { + if (chan_read_byte(chsa, &buf[i])) { + break; + } + } + if (i != sizeof (ETH_MAC)) + break; + memcpy(&ec_data.macs[len++], &buf[0], sizeof (ETH_MAC)); + } + ec_data.macs_n = len - 2; + ec_data.amc = 1; + + for (i = 0; i< len; i++) { + eth_mac_fmt(&ec_data.macs[i], (char *)&buf[0]); + sim_debug(DEBUG_DETAIL, &ec_dev, "ec_srv load mcast%d: %s\n",i,buf); + } + + if (ec_master_uptr->flags & UNIT_ATT) + /* multicast on means promiscous is too */ + eth_filter (&ec_data.etherface, n, ec_data.macs, ec_data.amc, + ec_data.macs[0][0] & 1); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case EC_WRITE: /* 0x01 Write command */ + /* get queue length */ + n = (((ec_data.rec_ptr) & LOOP_MSK) + LOOP_MSK + 1) - ec_data.xtr_ptr; + if (n >LOOP_MSK) + n -= (LOOP_MSK + 1); + len = sizeof(struct ec_eth_hdr); /* std header size /dest/src/len/ (14) */ + pirq = 0; + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + i = GET_MODE(ec_master_uptr->flags); /* get the mode setting */ + sim_debug(DEBUG_DETAIL, dptr, "ec_srv START %04x mode %d write %d %d conf=%d cnt 0x%x q %d\n", + chsa, i, ec_data.xtr_ptr, ec_data.rec_ptr, ec_data.conf[9], chp->ccw_count, n); + hdr = (struct ec_eth_hdr *)(&ec_data.snd_buff.msg[0]); + pck = (uint8 *)(&ec_data.snd_buff.msg[0]); + uptr->SNS &= LMASK; /* remove old count */ + + /* create packet: destination(6)/source(6)/type(2) or len(2)/ data 46-1500 */ + switch (GET_MODE(ec_master_uptr->flags)) { + case 0: + /* user buffer has /dest(6)/src(6)/type(2) or len(2)/ data/ */ + /* create packet: destination(6)/source(6)/type(2) or len(2)/ data 46-1500 */ + + /* copy users header unchanged */ + for(i = 0; i < sizeof(struct ec_eth_hdr); i++) { + if (chan_read_byte(chsa, &pck[i])) { + pirq = 1; + n = i; + sim_debug(DEBUG_DETAIL, dptr, "rw_end case 0 error 0\n"); + goto wr_end; + } + } + /* set transfer count of standard header supplied */ + uptr->SNS |= (sizeof(struct ec_eth_hdr) & 0xffff); /* set transfer count (14) */ + + /* copy in user supplied packet data */ + /* make min cnt 60 and max 1514 */ + i = sizeof(struct ec_eth_hdr); /* dest/src/len 14 bytes */ + while (chan_read_byte(chsa, &ch) == 0) { + if (i < ETH_MAX_PACKET) { + if (i>6 && i<28) + sim_debug(DEBUG_DATA, dptr, "ec_srv data[%3x]: %06x %02x\n", + i, chp->ccw_addr, ch); + pck[i] = ch; + } + i++; + uptr->SNS++; /* set count */ + } + sim_debug(DEBUG_DETAIL, dptr, + "ec_srv case 0 transmit bytes %d (0x%x) SNS %08x\n", + len, len, uptr->SNS); + break; + case 1: + case 2: + /* user buffer has /dest(6)/type(2)/data(46-1500)/ */ + /* create packet: destination(6)/source(6)/type(2) or len(2)/ data 46-1500 */ + /* copy in user dest/type/data */ + + /* get 6 byte destination from user */ + for(i = 0; i < sizeof(ETH_MAC); i++) { + if (chan_read_byte(chsa, &pck[i])) { + pirq = 1; + n = i; + sim_debug(DEBUG_DETAIL, dptr, "rw_end case 1&2 error 0\n"); + goto wr_end; + } + } + /* insert 6 byte source from configuration */ + memcpy(&hdr->src, ec_data.mac, sizeof(ETH_MAC)); + + /* copy two byte type/len from user buffer */ + for(i = sizeof(ETH_MAC) * 2; i < sizeof(struct ec_eth_hdr); i++) { + if (chan_read_byte(chsa, &pck[i])) { + pirq = 1; + n = i; + sim_debug(DEBUG_DETAIL, dptr, "rw_end case 1&2 error 2\n"); + goto wr_end; + } + } + /* set transfer count of user bytes supplied dest(6)/type(2) */ + uptr->SNS |= ((sizeof(struct ec_eth_hdr) - sizeof(ETH_MAC)) & 0xffff); + + /* copy in user supplied packet data */ + /* make min cnt 60 and max 1514 */ + i = sizeof(struct ec_eth_hdr); /* dest/src/len 14 bytes */ + while (chan_read_byte(chsa, &ch) == 0) { + if (i < ETH_MAX_PACKET) { + if (i>6 && i<28) + sim_debug(DEBUG_DATA, dptr, "ec_srv data[%3x]: %06x %02x\n", + i, chp->ccw_addr, ch); + pck[i] = ch; + } + i++; + uptr->SNS++; /* set count */ + } + sim_debug(DEBUG_DETAIL, dptr, + "ec_srv case 1&2 transmit bytes %d (0x%x) SNS %08x i 0x%x\n", + len-6, len-6, uptr->SNS, i); + + /* This code is to simulate word transfers into memory */ + /* from the users buffer. 1-3 extra bytes are placed */ + /* into the buffer. Diags in test 20 checks for this data */ + /* being present. These were set to 0 by the old code */ + /* and diags would complain 11/11/2021 */ + /* save data count */ + dcnt = i - sizeof(struct ec_eth_hdr); /* dest/src/len 14 bytes */ + n = 0; + while (dcnt++ % 4) { + pck[i+n] = RMB(((chp->ccw_addr+n))); + sim_debug(DEBUG_DATA, dptr, "ec_srx i %x data[%3x]: %06x %02x\n", + i, i+n, chp->ccw_addr+n, pck[i+n]); + n++; + } + n = n + i; /* last written char in buffer */ + break; + case 3: + /* user buffer has /dest(6)/data(46-1500)/ */ + /* create packet: destination(6)/source(6)/type(2) or len(2)/ data 46-1500 */ + + /* copy destination(6) from user buffer */ + for(i = 0; i < sizeof(ETH_MAC); i++) { + if (chan_read_byte(chsa, &pck[i])) { + pirq = 1; + n = i; + sim_debug(DEBUG_DETAIL, dptr, "rw_end case 3 error 0\n"); + goto wr_end; + } + } + /* insert source(6) */ + memcpy(&hdr->src, ec_data.mac, sizeof(ETH_MAC)); + +//#define USE_DATA_CNT +#ifdef USE_DATA_CNT + /* insert type(2) */ + hdr->type = htons(ETHTYPE_IP); +#endif + + /* set transfer count of user bytes supplied */ + uptr->SNS |= ((sizeof(struct ec_eth_hdr) - + sizeof(ETH_MAC) - sizeof(int16)) & 0xffff); + + /* copy in user supplied packet data */ + /* make min cnt 60 and max 1514 */ + i = sizeof(struct ec_eth_hdr); /* dest/src/len 14 bytes */ + cnt = 0; + while (chan_read_byte(chsa, &ch) == 0) { + if (i < ETH_MAX_PACKET) { + if (i>6 && i<28) + sim_debug(DEBUG_DATA, dptr, "ec_srv data[%3x]: %06x %02x\n", + i, chp->ccw_addr, ch); + pck[i] = ch; + } + i++; + uptr->SNS++; /* set count */ +#ifndef USE_DATA_CNT + cnt++; /* user data count */ +#endif + } + +#ifndef USE_DATA_CNT + /* insert type(2) */ +// hdr->type = htons(ETHTYPE_IP); + hdr->type = htons(cnt); /* set cnt into type/count field */ +#endif + + sim_debug(DEBUG_DETAIL, dptr, + "ec_srv case 3 transmit bytes %d (0x%x) SNS %08x i 0x%x cnt %x\n", + len-8, len-8, uptr->SNS, i, cnt); + + /* This code is to simulate word transfers into memory */ + /* from the users buffer. 1-3 extra bytes are placed */ + /* into the buffer. Diags in test 20 check for this data */ + /* being present. These were set to 0 by the old code */ + /* and diags would complain 11/11/2021 */ + /* save data count */ + dcnt = i - sizeof(struct ec_eth_hdr); /* dest/src/len 14 bytes */ + n = 0; + while (dcnt++ % 4) { + pck[i+n] = RMB(((chp->ccw_addr+n))); + sim_debug(DEBUG_DATA, dptr, "ec_srx i %x data[%3x]: %06x %02x\n", + i, i+n, chp->ccw_addr+n, pck[i+n]); + n++; + } + n = n + i; /* last written char in buffer */ + break; + } +wr_end: + ec_data.snd_buff.len = i; /* set actual count */ + ec_packet_debug(&ec_data, "send", &ec_data.snd_buff); + sim_debug(DEBUG_DETAIL, dptr, + "ec_srv @wr_end count 0x%x i 0x%04x SNS 0x%04x\n", + chp->ccw_count, i, uptr->SNS); + + /* make sure packet is minimum size for mode 1,2 & 3 */ + /* when handling non-loopback packets */ + if ((ec_data.snd_buff.len < ec_data.conf[9]) && + GET_MODE(ec_master_uptr->flags)) { + /* If not in loopback, pad packet */ + if (((ec_data.conf[0] & 0x40) == 0) || + /* this fixes test 20 for mode 3 */ + (GET_MODE(ec_master_uptr->flags) != 3)) { + /* Pad the packet */ + while(i < ETH_MIN_PACKET) { + ec_data.snd_buff.len++; /* increment actual count */ + pck[n++] = 0; + i++; + } + } + sim_debug(DEBUG_DETAIL, dptr, + "ec_srv @wr_end2 count 0x%x i 0x%04x n 0x%04x SNS 0x%04x\n", + chp->ccw_count, i, n, uptr->SNS); + if (i <= ETH_MIN_PACKET) { + ec_packet_debug(&ec_data, "send", &ec_data.snd_buff); + } + } + /* see if too many bytes, did not get channel end before packet filled */ + if (ec_data.snd_buff.len > ETH_MAX_PACKET) { + sim_debug(DEBUG_DETAIL, dptr, + "ec_srv WRITE error user 2manybytes %0x\n", chp->ccw_count); + /* diags wants prog check instead of length check test 4E */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + + ec_data.tx_count++; + if (ec_data.conf[0] & 0x40) { /* see if internal loopback */ + /* yes loopback, buffer the packet */ + int q = (((ec_data.rec_ptr + 1) & LOOP_MSK) + LOOP_MSK + 1) - ec_data.xtr_ptr; + if (q >LOOP_MSK) + q -= (LOOP_MSK + 1); +//jb if (((ec_data.rec_ptr + 1) & LOOP_MSK) == ec_data.xtr_ptr) { +//jb if (q > 16) { + if (q > 716) { + ec_data.drop_cnt++; + sim_debug(DEBUG_DETAIL, dptr, "ec_srv write packet dropped %d q %d\n", + ec_data.drop_cnt, q); + } else { + memcpy(&ec_data.rec_buff[ec_data.rec_ptr], + &ec_data.snd_buff, sizeof(ETH_PACK)); + ec_data.rec_ptr = (ec_data.rec_ptr + 1) & LOOP_MSK; + sim_debug(DEBUG_DETAIL, dptr, "ec_srv WRITE rec queued %d xtr %d queue %04x\n", + ec_data.rec_ptr, ec_data.xtr_ptr, q); + } + } + + /* check for internal loopback */ + if ((ec_data.conf[0] & 0x40) == 0) { +#ifndef NEW_02052021 + /* not internal loopback, user wants to write to network */ + /* check if attached, if not give no carrier and unit exception */ + if ((ec_master_uptr->flags & UNIT_ATT) == 0) { + sim_debug(DEBUG_EXP, dptr, + "EC write device %s not attached on unit EC%04X\n", + dptr->name, GET_UADDR(uptr->CMD)); + uptr->SNS |= SNS_NO_CAR; /* no carrier error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_EXPT); + break; + } +#endif + /* no loopback, write out the packet */ + if (eth_write(&ec_data.etherface, &ec_data.snd_buff, NULL) != SCPE_OK) { + sim_debug(DEBUG_DETAIL, dptr, "ec_srv short packet %d\n", i); + /* diags wants prog check instead of unit check */ + pirq = 1; + } + } + sim_debug(DEBUG_DETAIL, dptr, + "ec_srv sent packet pirq %d 0x%x bytes tx_count=%08x SNS %08x\n", + pirq, ec_data.snd_buff.len, ec_data.tx_count, uptr->SNS); + + if (pirq) + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + else { + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + } + break; + + case EC_READ: /* 0x02 Read command */ + /* If no data to receive wait for some more */ + if (ec_data.xtr_ptr == ec_data.rec_ptr) { +// sim_debug(DEBUG_DETAIL, &ec_dev, "ec_srv WAIT %04x read %d %d size=%d cnt %d\n", +// chsa, ec_data.xtr_ptr, ec_data.rec_ptr, ec_data.conf[9], chp->ccw_count); +//XX sim_clock_coschedule(uptr, 1500); /* continue poll */ +//HH sim_activate(uptr, 1511); /* continue poll */ + /* this is OK for mode 0, 1, 2, 3 */ +//12dec21 sim_activate(uptr, 2511); /* continue poll */ + /* this is really a 50000 cnt poll by simh */ + sim_clock_coschedule(uptr, 1000); /* continue poll */ + return SCPE_OK; + } + /* get queue length */ + n = (((ec_data.rec_ptr) & LOOP_MSK) + LOOP_MSK + 1) - ec_data.xtr_ptr; + if (n > LOOP_MSK) + n -= (LOOP_MSK + 1); + pirq = 0; + i = GET_MODE(ec_master_uptr->flags); /* get the mode setting */ + sim_debug(DEBUG_DETAIL, &ec_dev, "ec_srv START %04x mode %d read %d %d conf=%d cnt %d q %d\n", + chsa, i, ec_data.xtr_ptr, ec_data.rec_ptr, ec_data.conf[9], chp->ccw_count, n); + uptr->CMD &= LMASK; /* remove old status bits & cnt */ + + /* Read must be word bounded */ + if (chp->ccw_addr & 0x3) { + sim_debug(DEBUG_EXP, dptr, + "ec_srv iocd bad address caw %06x ccw %06x\n", + chp->chan_caw, chp->ccw_addr); + ec_data.xtr_ptr = (ec_data.xtr_ptr + 1) & LOOP_MSK; + chp->ccw_count = 0; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_LENGTH|STATUS_PCHK); + break; + } + + uptr->SNS &= LMASK; /* remove old count */ + ec_master_uptr->SNS |= SNS_RCV_RDY; + ec_packet_debug(&ec_data, "recv", &ec_data.rec_buff[ec_data.xtr_ptr]); + pck = (uint8 *)(&ec_data.rec_buff[ec_data.xtr_ptr].msg[0]); + len = (int)(ec_data.rec_buff[ec_data.xtr_ptr].len); + n = sizeof(struct ec_eth_hdr); + cnt = len - n; /* number of data bytes */ + sim_debug(DEBUG_DETAIL, &ec_dev, "ec_srv READ addr %06x pktlen 0x%x rdcnt 0x%x conf 0x%x\n", + chp->ccw_addr, len, chp->ccw_count, ec_data.conf[9]); + + switch (GET_MODE(ec_master_uptr->flags)) { + case 0: + /* create output: destination(6)/source(6)/type(2) or len(2)/ data 46-1500 */ + /* user buffer: destination(6)/source(6)/type(2) or len)2) */ + for(i = 0; i < sizeof(struct ec_eth_hdr); i++) { + if (chan_write_byte(chsa, &pck[i])) { + pirq = 1; + break; + } + } + uptr->SNS |= (len & 0xffff); /* set real transfer count */ + sim_debug(DEBUG_DETAIL, &ec_dev, + "ec_srv case 0 received bytes %d (0x%x) SNS %08x i 0x%x cnt 0x%x\n", + len, len, uptr->SNS, i, cnt); + break; + case 1: + case 2: + /* create output: destination(6)/len(2)/source(6)/type(2) or len(2)/ data 46-1500 */ + /* destination / len / source / type or len */ + /* copy 6 byte destination */ + for(i = 0; i < sizeof(ETH_MAC); i++) { + if (chan_write_byte(chsa, &pck[i])) { + pirq = 1; + break; + } + } + /* insert length byte 1 */ + ch = (len >> 8) & 0xff; + if (chan_write_byte(chsa, &ch)) { + pirq = 1; + break; + } + /* insert length byte 2 */ + ch = len & 0xff; + if (chan_write_byte(chsa, &ch)) { + pirq = 1; + break; + } + /* copy in source(6)/type(2) 6 + 2 = 8 = 14 - 6 */ + for(; i < sizeof(struct ec_eth_hdr); i++) { + if (chan_write_byte(chsa, &pck[i])) { + pirq = 1; + break; + } + if (i>5) + sim_debug(DEBUG_DATA, &ec_dev, "ec_srr pck[%3x]: %02x\n", i, pck[i]); + } + uptr->SNS |= ((len+2) & 0xffff); /* set real transfer count */ + sim_debug(DEBUG_DETAIL, &ec_dev, + "ec_srv case 1&2 received bytes %d (0x%x) SNS %08x i 0x%x cnt 0x%x\n", + len, len, uptr->SNS, i, cnt); + break; + case 3: + /* create output: destination(6)/len(2)/source(6)/len(2)/ data 46-1500 */ + /* copy 6 byte destination */ + for(i = 0; i < sizeof(ETH_MAC); i++) { + if (chan_write_byte(chsa, &pck[i])) { + pirq = 1; + break; + } + } + /* insert length byte 1 */ + ch = (len >> 8) & 0xff; + if (chan_write_byte(chsa, &ch)) { + pirq = 1; + break; + } + /* insert length byte 2 */ + ch = len & 0xff; + if (chan_write_byte(chsa, &ch)) { + pirq = 1; + break; + } + /* copy in 6 byte source */ + for (; i < sizeof(ETH_MAC) * 2; i++) { + if (chan_write_byte(chsa, &pck[i])) { + pirq = 1; + break; + } + if (i>5) + sim_debug(DEBUG_DATA, &ec_dev, "ec_srr pck[%3x]: %02x\n", i, pck[i]); + } + /* insert length byte 1 */ + ch = (len >> 8) & 0xff; + if (chan_write_byte(chsa, &ch)) { + pirq = 1; + break; + } + /* insert length byte 2 */ + ch = len & 0xff; + if (chan_write_byte(chsa, &ch)) { + pirq = 1; + break; + } + uptr->SNS |= ((len + 2) & 0xffff); /* set real transfer count */ + sim_debug(DEBUG_DETAIL, &ec_dev, + "ec_srv case 3 received bytes %d (0x%x) SNS %08x i 0x%x cnt 0x%x\n", + len, len, uptr->SNS, i, cnt); + break; + } + + /* see if user did not request enough data */ + i = sizeof(struct ec_eth_hdr); + if (pirq || (i >= len)) { + ec_data.xtr_ptr = (ec_data.xtr_ptr + 1) & LOOP_MSK; + ec_data.rx_count++; + sim_debug(DEBUG_DETAIL, &ec_dev, + "ec_srv0 pirq %d cnt %x received bytes %d of %d rx_count=%08x conf %x\n", + pirq, cnt, i, len, ec_data.rx_count, ec_data.conf[9]); + /* diag wants incorrect length error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_LENGTH); + break; + } + + /* now copy in the user data */ + for (i = sizeof(struct ec_eth_hdr); i < len; i++) { + if (i > (len - 8)) + sim_debug(DEBUG_DETAIL, &ec_dev, "ec_sww pck[%3x]: %02x %02x\n", i, pck[i], chp->ccw_count); + if (chan_write_byte(chsa, &pck[i])) { + /* we read less than or exact bytes, good to go */ + ec_data.xtr_ptr = (ec_data.xtr_ptr + 1) & LOOP_MSK; + ec_data.rx_count++; + sim_debug(DEBUG_DETAIL, &ec_dev, + "ec_srv1 left 0x%x rec'd 0x%x bytes rx_count %08x conf %02x SNS %08x\n", + chp->ccw_count, len, ec_data.rx_count, ec_data.conf[9], uptr->SNS); + /* diag wants incorrect length error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_LENGTH); + sim_debug(DEBUG_DETAIL, dptr, + "ec_srv done cmd=%02x chsa %04x addr %06x count %04x SNS 0x%08x stat %04x\n", + cmd, chsa, chp->ccw_addr, chp->ccw_count, uptr->SNS, chp->chan_status); + return SCPE_OK; + } + } + /* we have some bytes left, set count mismatch */ + chp->ccw_flags |= FLAG_SLI; + ec_data.xtr_ptr = (ec_data.xtr_ptr + 1) & LOOP_MSK; + ec_data.rx_count++; + sim_debug(DEBUG_DETAIL, &ec_dev, + "ec_srv2 left 0x%x rec'd 0x%x bytes rx_count %08x conf %02x SNS %08x\n", + chp->ccw_count, len, ec_data.rx_count, ec_data.conf[9], uptr->SNS); + /* diag does not want incorrect length error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case EC_LCC: /* 0x10 Configure LCC */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + /* Read up to 12 bytes of configuration data */ + for (i = 0; i < 12; i++) { + if (chan_read_byte(chsa, &ec_data.conf[i])) { + break; + } + } + sim_debug(DEBUG_CMD, &ec_dev, + "ec_srv LCC CONF: %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n", + ec_data.conf[0], ec_data.conf[1], ec_data.conf[2], ec_data.conf[3], + ec_data.conf[4], ec_data.conf[5], ec_data.conf[6], ec_data.conf[7], + ec_data.conf[8], ec_data.conf[9], ec_data.conf[10], ec_data.conf[11]); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case EC_STATS: /* 0x14 Read Statistics */ + ch = 0; + /* First 5 words are always zero since these errors are not supported */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_DETAIL, dptr, "ec_srv stats drop_count %08x\n", ec_data.drop_cnt); + for (i = 0; i < STAT_LEN * 2; i++) { + if (i == 6) + ch = (ec_data.drop_cnt >> 8) & 0xff; + if (i == 7) + ch = ec_data.drop_cnt & 0xff; + if (i == 8) + ch = 0; + if (chan_write_byte(chsa, &ch)) { + break; + } + } + sim_debug(DEBUG_DETAIL, dptr, "ec_srv stats rx_count %08x\n", ec_data.rx_count); + ch = (ec_data.rx_count >> 24) & 0xff; + chan_write_byte(chsa, &ch); + ch = (ec_data.rx_count >> 16) & 0xff; + chan_write_byte(chsa, &ch); + ch = (ec_data.rx_count >> 8) & 0xff; + chan_write_byte(chsa, &ch); + ch = ec_data.rx_count & 0xff; + chan_write_byte(chsa, &ch); + sim_debug(DEBUG_DETAIL, dptr, "ec_srv stats tx_count %08x\n", ec_data.tx_count); + ch = (ec_data.tx_count >> 24) & 0xff; + chan_write_byte(chsa, &ch); + ch = (ec_data.tx_count >> 16) & 0xff; + chan_write_byte(chsa, &ch); + ch = (ec_data.tx_count >> 8) & 0xff; + chan_write_byte(chsa, &ch); + ch = ec_data.tx_count & 0xff; + chan_write_byte(chsa, &ch); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case EC_CSTATS: /* 0x15 Clear software counters */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + ec_data.rx_count = ec_data.tx_count = 0; + (void)chan_read_byte(chsa, &ch); + sim_debug(DEBUG_CMD, dptr, + "ec_srv cmd clear counters chsa %04x count %04x completed\n", + chsa, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case EC_NOP: /* 0x03 NOP */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "ec_srv cmd NOP chsa %04x count %04x completed\n", + chsa, chp->ccw_count); + /* diags want the count to be returned zero */ + chp->ccw_count = 0; /* NOP command count */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case EC_SNS: /* 0x04 Sense */ + sim_debug(DEBUG_CMD, dptr, + "ec_startcmd CMD sense cnt %02x\n", chp->ccw_count); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + + /* diags want incorrect length or prog check */ + if (chp->ccw_count < 0x04) { + chp->ccw_count = 0; /* zero command count */ + if ((chp->ccw_flags & FLAG_SLI) == 0) { + /* diag wants incorrect length */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_LENGTH); + break; + } + } + + len = uptr->SNS & 0xffff; + sim_debug(DEBUG_DETAIL, &ec_dev, "ec_srv SNS len %d xt %d rd %d\n", + len, ec_data.xtr_ptr, ec_data.rec_ptr); + ch = (uptr->SNS >> 24) & 0xfc; + ch |= GET_MODE(ec_master_uptr->flags); + sim_debug(DEBUG_DETAIL, dptr, "ec_srv sense b0 1 %02x\n", ch); + chan_write_byte(chsa, &ch); + ch = (ec_master_uptr->SNS >> 16) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "ec_srv sense b1 2 %02x\n", ch); + chan_write_byte(chsa, &ch); + ch = (len >> 8) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "ec_srv sense b2 3 %02x\n", ch); + chan_write_byte(chsa, &ch); + ch = len & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "ec_srv sense b3 4 %02x\n", ch); + chan_write_byte(chsa, &ch); + + if (chp->ccw_count > 0) { + if (chp->ccw_flags & FLAG_SLI) + /* diags want prog check */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + else + /* diag wants incorrect length */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_LENGTH); + sim_debug(DEBUG_CMD, dptr, + "ec_startcmd CMD sense excess cnt %02x\n", chp->ccw_count); + break; + } +//020522uptr->SNS &= ~(SNS_CMDREJ|SNS_EQUCHK); /* clear old status */ + uptr->SNS = 0; /* clear old status */ + uptr->SNS &= LMASK; /* remove old count */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* done */ + break; + + default: + sim_debug(DEBUG_CMD, dptr, "invalid command %02x\n", cmd); + uptr->SNS |= SNS_CMDREJ; + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + sim_debug(DEBUG_DETAIL, dptr, + "ec_srv done cmd=%02x chsa %04x count %04x addr %06x flags %04x stat %x SNS 0x%x\n", + cmd, chsa, chp->ccw_count, chp->ccw_addr, chp->ccw_flags, chp->chan_status, uptr->SNS); + return SCPE_OK; +} + +/* Handle haltio transfers for ethernet */ +t_stat ec_haltio(UNIT *uptr) { + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + int cmd = uptr->CMD & EC_CMDMSK; + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + sim_debug(DEBUG_EXP, dptr, "ec_haltio enter chsa %04x chp %p cmd %02x\n", chsa, chp, cmd); + + /* terminate any input command */ + /* UTX wants SLI bit, but no unit exception */ + /* status must not have an error bit set */ + /* otherwise, UTX will panic with "bad status" */ + if ((uptr->CMD & EC_CMDMSK) != 0) { /* is unit busy */ + sim_debug(DEBUG_CMD, dptr, + "ec_haltio HIO chsa %04x cmd = %02x ccw_count %02x\n", chsa, cmd, chp->ccw_count); + // stop any I/O and post status and return error status */ + if (chsa & 0x0f) /* no cancel for 0 */ + sim_cancel(uptr); /* clear the output timer */ + chp->ccw_count = 0; /* zero the count */ + chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* stop any chaining */ + uptr->CMD &= LMASK; /* make non-busy */ + uptr->SNS = SNS_RCV_RDY; /* status is online & ready */ + sim_debug(DEBUG_CMD, dptr, + "ec_haltio HIO I/O stop chsa %04x cmd = %02x\n", chsa, cmd); + /* No unit exception status for ethernet */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* force end */ + return CC1BIT | SCPE_IOERR; + } + sim_debug(DEBUG_CMD, dptr, + "ec_haltio HIO I/O not busy chsa %04x cmd = %02x\n", chsa, cmd); + uptr->CMD &= LMASK; /* make non-busy */ + uptr->SNS = SNS_RCV_RDY; /* status is online & ready */ + return CC1BIT | SCPE_OK; /* not busy */ +} + +/* initialize the ethernet */ +void ec_ini(UNIT *uptr, t_bool f) +{ + DEVICE *dptr = get_dev(uptr); + + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS = 0; /* save mode value */ + memset(&ec_data.conf[0], 0, sizeof(ec_data.conf)); + ec_data.macs_n = 0; + ec_data.tx_count = 0; + ec_data.rx_count = 0; + ec_data.rec_ptr = 0; + ec_data.xtr_ptr = 0; + ec_data.drop_cnt = 0; + ec_data.amc = 0; + if (ec_master_uptr->flags & UNIT_ATT) { + /* multicast on means promiscous is too */ + eth_filter (&ec_data.etherface, ec_data.macs_n + 2, ec_data.macs, + ec_data.amc, ec_data.macs[0][0] & 1); + sim_debug(DEBUG_EXP, dptr, + "EC init device %s is attached on unit EC%04X\n", + dptr->name, GET_UADDR(uptr->CMD)); + } else { + sim_debug(DEBUG_EXP, dptr, + "EC init device %s not attached on unit EC%04X\n", + dptr->name, GET_UADDR(uptr->CMD)); +//OLD uptr->SNS |= SNS_NO_CAR; /* no carrier error */ + } +} + +/* handle reset controller cmds for Ethernet */ +t_stat ec_rsctrl(UNIT *uptr) { + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & EC_CMDMSK; + + sim_debug(DEBUG_EXP, dptr, + "ec_rsctlr chsa %04x cmd = %02x\n", chsa, cmd); +// memset(&ec_data.conf[0], 0, sizeof(ec_data.conf)); + ec_data.tx_count = 0; + ec_data.rx_count = 0; + ec_data.drop_cnt = 0; + ec_data.rec_ptr = 0; /* clear queue */ + ec_data.xtr_ptr = 0; /* clear queue */ + return SCPE_OK; +} + +/* handle reset channel cmds for Ethernet */ +t_stat ec_rschnlio(UNIT *uptr) { + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & EC_CMDMSK; + + sim_debug(DEBUG_EXP, dptr, + "ec_rschnl chsa %04x cmd = %02x\n", chsa, cmd); + ec_ini(uptr, 0); /* reset the unit */ + /* the interrupt level will be reset in sel32_chan.c code */ + return SCPE_OK; +} + +static char * +ipv4_inet_ntoa(struct in_addr ip) +{ + static char str[20]; + + if (sim_end) + sprintf (str, "%d.%d.%d.%d", ip.s_addr & 0xFF, + (ip.s_addr >> 8) & 0xFF, + (ip.s_addr >> 16) & 0xFF, + (ip.s_addr >> 24) & 0xFF); + else + sprintf (str, "%d.%d.%d.%d", (ip.s_addr >> 24) & 0xFF, + (ip.s_addr >> 16) & 0xFF, + (ip.s_addr >> 8) & 0xFF, + ip.s_addr & 0xFF); + return str; +} + +/* + * Pretty print a packet for debugging. + */ +void ec_packet_debug(struct ec_device *ec, const char *action, + ETH_PACK *packet) { + struct ec_eth_hdr *eth = (struct ec_eth_hdr *)&packet->msg[0]; + struct arp_hdr *arp = (struct arp_hdr *)eth; + struct ip *ip = (struct ip *)&packet->msg[sizeof(struct ec_eth_hdr)]; + struct udp *udp; + struct tcp *tcp; + struct icmp *icmp; + uint8 *payload; + struct in_addr ipaddr; + size_t len; + int flag; + char src_ip[20]; + char dst_ip[20]; + char src_port[8]; + char dst_port[8]; + char flags[64]; + static struct tcp_flag_bits { + const char *name; + uint16 bitmask; + } bits[] = { + {"FIN", TCP_FL_FIN}, + {"SYN", TCP_FL_SYN}, + {"RST", TCP_FL_RST}, + {"PSH", TCP_FL_PSH}, + {"ACK", TCP_FL_ACK}, + {"URG", TCP_FL_URG}, + {NULL, 0} + }; + static const char *icmp_types[] = { + "Echo Reply", // Type 0 + "Type 1 - Unassigned", + "Type 2 - Unassigned", + "Destination Unreachable", // Type 3 + "Source Quench (Deprecated)", // Type 4 + "Redirect", // Type 5 + "Type 6 - Alternate Host Address (Deprecated)", + "Type 7 - Unassigned", + "Echo Request", // Type 8 + "Router Advertisement", // Type 9 + "Router Selection", // Type 10 + "Time Exceeded", // Type 11 + "Type 12 - Parameter Problem", + "Type 13 - Timestamp", + "Type 14 - Timestamp Reply", + "Type 15 - Information Request (Deprecated)", + "Type 16 - Information Reply (Deprecated)", + "Type 17 - Address Mask Request (Deprecated)", + "Type 18 - Address Mask Reply (Deprecated)", + "Type 19 - Reserved (for Security)", + "Type 20 - Reserved (for Robustness Experiment)", + "Type 21 - Reserved (for Robustness Experiment)", + "Type 22 - Reserved (for Robustness Experiment)", + "Type 23 - Reserved (for Robustness Experiment)", + "Type 24 - Reserved (for Robustness Experiment)", + "Type 25 - Reserved (for Robustness Experiment)", + "Type 26 - Reserved (for Robustness Experiment)", + "Type 27 - Reserved (for Robustness Experiment)", + "Type 28 - Reserved (for Robustness Experiment)", + "Type 29 - Reserved (for Robustness Experiment)", + "Type 30 - Traceroute (Deprecated)", + "Type 31 - Datagram Conversion Error (Deprecated)", + "Type 32 - Mobile Host Redirect (Deprecated)", + "Type 33 - IPv6 Where-Are-You (Deprecated)", + "Type 34 - IPv6 I-Am-Here (Deprecated)", + "Type 35 - Mobile Registration Request (Deprecated)", + "Type 36 - Mobile Registration Reply (Deprecated)", + "Type 37 - Domain Name Request (Deprecated)", + "Type 38 - Domain Name Reply (Deprecated)", + "Type 39 - SKIP (Deprecated)", + "Type 40 - Photuris", + "Type 41 - ICMP messages utilized by experimental mobility protocols such as Seamoby", + "Type 42 - Extended Echo Request", + "Type 43 - Extended Echo Reply" + }; + + if (ntohs(eth->type) == ETHTYPE_ARP) { + struct in_addr in_addr; + const char *arp_op = (ARP_REQUEST == ntohs(arp->opcode)) ? "REQUEST" : + ((ARP_REPLY == ntohs(arp->opcode)) ? "REPLY" : "Unknown"); + char eth_src[20], eth_dst[20]; + char arp_shwaddr[20], arp_dhwaddr[20]; + char arp_sipaddr[20], arp_dipaddr[20]; + + if (!(ec_dev.dctrl & DEBUG_ARP)) + return; + eth_mac_fmt(&arp->ethhdr.src, eth_src); + eth_mac_fmt(&arp->ethhdr.dest, eth_dst); + eth_mac_fmt(&arp->shwaddr, arp_shwaddr); + memcpy(&in_addr, &arp->sipaddr, sizeof(in_addr)); + strlcpy(arp_sipaddr, ipv4_inet_ntoa(in_addr), sizeof(arp_sipaddr)); + eth_mac_fmt(&arp->dhwaddr, arp_dhwaddr); + memcpy(&in_addr, &arp->dipaddr, sizeof(in_addr)); + strlcpy(arp_dipaddr, ipv4_inet_ntoa(in_addr), sizeof(arp_dipaddr)); + sim_debug(DEBUG_ARP, &ec_dev, + "%s %s EthDst=%s EthSrc=%s shwaddr=%s sipaddr=%s dhwaddr=%s dipaddr=%s\n", + action, arp_op, eth_dst, eth_src, arp_shwaddr, arp_sipaddr, arp_dhwaddr, arp_dipaddr); + return; + } +#ifdef OLDWAY + if (ntohs(eth->type) != ETHTYPE_IP) { + payload = (uint8 *)&packet->msg[0]; + len = packet->len; + sim_data_trace(&ec_dev, ec_unit, payload, "", len, "", DEBUG_DATA); + return; + } +#else + /* always dump packet */ + payload = (uint8 *)&packet->msg[0]; + len = packet->len; + sim_data_trace(&ec_dev, ec_unit, payload, "", len, "", DEBUG_DATA); + if (ntohs(eth->type) != ETHTYPE_IP) { + return; + } +#endif + if (!(ec_dev.dctrl & (DEBUG_TCP|DEBUG_UDP|DEBUG_ICMP))) + return; + memcpy(&ipaddr, &ip->ip_src, sizeof(ipaddr)); + strlcpy(src_ip, ipv4_inet_ntoa(ipaddr), sizeof(src_ip)); + memcpy(&ipaddr, &ip->ip_dst, sizeof(ipaddr)); + strlcpy(dst_ip, ipv4_inet_ntoa(ipaddr), sizeof(dst_ip)); + payload = (uint8 *)&packet->msg[sizeof(struct ec_eth_hdr) + (ip->ip_v_hl & 0xf) * 4]; + switch (ip->ip_p) { + case UDP_PROTO: + udp = (struct udp *)payload; + snprintf(src_port, sizeof(src_port), "%d", ntohs(udp->udp_sport)); + snprintf(dst_port, sizeof(dst_port), "%d", ntohs(udp->udp_dport)); + sim_debug(DEBUG_UDP, &ec_dev, "%s %d byte packet from %s:%s to %s:%s\n", action, + ntohs(udp->len), src_ip, src_port, dst_ip, dst_port); + if (udp->len && (ec_dev.dctrl & DEBUG_UDP)) + sim_data_trace(&ec_dev, ec_unit, payload + sizeof(struct udp), "", + ntohs(udp->len), "", DEBUG_DATA); + break; + case TCP_PROTO: + tcp = (struct tcp *)payload; + snprintf(src_port, sizeof(src_port), "%d", ntohs(tcp->tcp_sport)); + snprintf(dst_port, sizeof(dst_port), "%d", ntohs(tcp->tcp_dport)); + strlcpy(flags, "", sizeof(flags)); + for (flag=0; bits[flag].name; flag++) { + if (ntohs(tcp->flags) & bits[flag].bitmask) { + if (*flags) + strlcat(flags, ",", sizeof(flags)); + strlcat(flags, bits[flag].name, sizeof(flags)); + } + + } + len = ntohs(ip->ip_len) - ((ip->ip_v_hl & 0xf) * 4 + (ntohs(tcp->flags) >> 12) * 4); + sim_debug(DEBUG_TCP, &ec_dev, "%s %s%s %d byte packet from %s:%s to %s:%s\n", action, + flags, *flags ? ":" : "", (int)len, src_ip, src_port, dst_ip, dst_port); + if (len && (ec_dev.dctrl & DEBUG_TCP)) + sim_data_trace(&ec_dev, ec_unit, payload + + 4 * (ntohs(tcp->flags) >> 12), "", len, "", DEBUG_DATA); + break; + case ICMP_PROTO: + icmp = (struct icmp *)payload; + len = ntohs(ip->ip_len) - (ip->ip_v_hl & 0xf) * 4; + sim_debug(DEBUG_ICMP, &ec_dev, "%s %s %d byte packet from %s to %s\n", action, + (icmp->type < sizeof(icmp_types)/sizeof(icmp_types[0])) ? + icmp_types[icmp->type] : "", (int)len, src_ip, dst_ip); + if (len && (ec_dev.dctrl & DEBUG_ICMP)) + sim_data_trace(&ec_dev, ec_unit, payload + sizeof(struct icmp), "", len, "", DEBUG_DATA); + break; + } +} + +t_stat ec_show_mode (FILE* st, UNIT* uptr, int32 val, CONST void* desc) +{ + fprintf(st, "MODE=%d", GET_MODE(uptr->flags)); + return SCPE_OK; +} + +t_stat ec_set_mode (UNIT* uptr, int32 val, CONST char* cptr, void* desc) +{ + t_stat r; + int newmode; + + if (!cptr) return SCPE_IERR; + + newmode = get_uint(cptr, 10, 4, &r); + + if (r != SCPE_OK) + return r; + + if (newmode > 3) + return SCPE_ARG; + + uptr->flags &= ~UNIT_MODE; + uptr->flags |= SET_MODE(newmode); + return SCPE_OK; +} + + +t_stat ec_show_mac (FILE* st, UNIT* uptr, int32 val, CONST void* desc) +{ + char buffer[20]; + eth_mac_fmt(&ec_data.mac, buffer); + fprintf(st, "MAC=%s", buffer); + return SCPE_OK; +} + +t_stat ec_set_mac (UNIT* uptr, int32 val, CONST char* cptr, void* desc) +{ + t_stat status; + + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + + status = eth_mac_scan_ex(&ec_data.mac, cptr, uptr); + if (status != SCPE_OK) + return status; + + return SCPE_OK; +} + +t_stat ec_reset (DEVICE *dptr) +{ + int i; + + for (i = 0; i < sizeof(ETH_MAC); i++) { + if (ec_data.mac[i] != 0) + break; + } + if (i == 6) { /* First call to reset? */ + /* Set a default MAC address in a BBN assigned OID range no longer in use */ + ec_set_mac (dptr->units, 0, "00:00:02:00:00:00/24", NULL); + } + memset(&ec_data.conf[0], 0, sizeof(ec_data.conf)); + ec_data.macs_n = 0; + ec_data.tx_count = 0; + ec_data.rx_count = 0; + ec_data.rec_ptr = 0; + ec_data.xtr_ptr = 0; + ec_data.drop_cnt = 0; + ec_data.amc = 0; + if (ec_master_uptr->flags & UNIT_ATT) + /* multicast on means promiscous is too */ + eth_filter (&ec_data.etherface, ec_data.macs_n + 2, ec_data.macs, + ec_data.amc, ec_data.macs[0][0] & 1); + sim_debug(DEBUG_EXP, dptr, + "EC reset device %s on unit EC%04X\n", dptr->name, + GET_UADDR(dptr->units->CMD)); + return SCPE_OK; +} + +/* attach device: */ +t_stat ec_attach(UNIT* uptr, CONST char* cptr) +{ + t_stat status; + char* tptr; + char buf[32]; + + tptr = (char *) malloc(strlen(cptr) + 1); + if (tptr == NULL) return SCPE_MEM; + strcpy(tptr, cptr); + + memcpy(&ec_data.macs[0], &ec_data.mac, sizeof (ETH_MAC)); + memcpy(&ec_data.macs[1], &broadcast_ethaddr, sizeof (ETH_MAC)); + status = eth_open(&ec_data.etherface, cptr, &ec_dev, DEBUG_ETHER); + if (status != SCPE_OK) { + free(tptr); + return status; + } + eth_mac_fmt(&ec_data.mac, buf); /* format ethernet mac address */ + if (SCPE_OK != eth_check_address_conflict (&ec_data.etherface, + &ec_data.mac)) { + eth_close(&ec_data.etherface); + free(tptr); + return sim_messagef (SCPE_NOATT, + "%s: MAC Address Conflict on LAN for address %s\n", + ec_dev.name, buf); + } + if (SCPE_OK != eth_filter(&ec_data.etherface, 2, ec_data.macs, 0, 0)) { + eth_close(&ec_data.etherface); + free(tptr); + return sim_messagef (SCPE_NOATT, + "%s: Can't set packet filter for MAC Address %s\n", + ec_dev.name, buf); + } + + uptr->filename = tptr; + uptr->flags |= UNIT_ATT; + eth_setcrc(&ec_data.etherface, 0); /* Enable CRC */ + + /* init read queue (first time only) */ + status = ethq_init(&ec_data.ReadQ, 8); /* 8 per device */ + if (status != SCPE_OK) { + eth_close(&ec_data.etherface); + uptr->filename = NULL; + free(tptr); + return sim_messagef (status, "%s: Can't initialize receive queue\n", + ec_dev.name); + } + + eth_set_async (&ec_data.etherface, 0); + return SCPE_OK; +} + +/* detach device: */ +t_stat ec_detach(UNIT* uptr) +{ + if ((uptr->flags & UNIT_ATT) && (uptr->flags & UNIT_DIS) == 0) { + eth_close (&ec_data.etherface); + free(uptr->filename); + uptr->filename = NULL; + uptr->flags &= ~UNIT_ATT; + } + return SCPE_OK; +} + +t_stat ec_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf(st, "Ethernet interface\n\n"); + fprintf(st, "The ethernet interfaces to the network. Setting MAC defines default MAC address\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + eth_attach_help(st, dptr, uptr, flag, cptr); + return SCPE_OK; +} + +const char *ec_description (DEVICE *dptr) +{ + return "SEL32 8516 Ethernet interface"; +} +#endif diff --git a/SEL32/sel32_fltpt.c b/SEL32/sel32_fltpt.c new file mode 100644 index 00000000..c9f09b9a --- /dev/null +++ b/SEL32/sel32_fltpt.c @@ -0,0 +1,1637 @@ +/* sel32_fltpt.c: SEL 32 floating point instructions processing. + + Copyright (c) 2018-2021, James C. Bevier + Portions provided by Richard Cornwell, Geert Rolf and other SIMH contributers + + 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 + JAMES C. BEVIER 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. + + This set of subroutines simulate the excess 64 floating point instructions. + ADFW - add memory float to register + ADFD - add memory double to register pair + SUFW - subtract memory float from register + SUFD - subtract memory double from register pair + MPFW - multiply register by memory float + MPFD - multiply register pair by memory double + DVFW - divide register by memory float + DVFD - divide register pair by memory double + FIXW - convert float to integer (32 bit) + FIXD - convert double to long long (64 bit) + FLTW - convert integer (32 bit) to float + FLTD - convert long long (64 bit) to double + ADRFW - add regist float to register + SURFW - subtract register float from register + DVRFW - divide register float by register float + MPRFW - multiply register float by register float + ADRFD - add register pair double to register pair double + SURFD - subtract register pair double from register pair double + DVRFD - divide register pair double by register pair double + MPRFD - multiply register pair double by register pair double + + Floating Point Formats + float + S - 1 sign bit + X - 7 bit exponent + M - 24 bit mantissa + S XXXXXXX MMMMMMMM MMMMMMMM MMMMMMMM + double + S - 1 sign bit + X - 7 bit exponent + M - 56 bit mantissa + S XXXXXXX MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM + +*/ + +#include "sel32_defs.h" +#include + +uint32 s_fixw(uint32 val, uint32 *cc); +uint32 s_fltw(uint32 val, uint32 *cc); + +t_uint64 s_fixd(t_uint64 val, uint32 *cc); +t_uint64 s_fltd(t_uint64 val, uint32 *cc); + +uint32 s_nor(uint32 reg, uint32 *exp); +t_uint64 s_nord(t_uint64 reg, uint32 *exp); + +uint32 s_mpfw(uint32 reg, uint32 mem, uint32 *cc); +uint32 s_dvfw(uint32 reg, uint32 mem, uint32 *cc); + +uint32 s_adfw(uint32 reg, uint32 mem, uint32 *cc); +uint32 s_sufw(uint32 reg, uint32 mem, uint32 *cc); + +t_uint64 s_adfd(t_uint64 reg, t_uint64 mem, uint32 *cc); +t_uint64 s_sufd(t_uint64 reg, t_uint64 mem, uint32 *cc); + +t_uint64 s_mpfd(t_uint64 reg, t_uint64 mem, uint32 *cc); +t_uint64 s_dvfd(t_uint64 reg, t_uint64 mem, uint32 *cc); + +uint32 s_normfw(uint32 num, uint32 *cc); +t_uint64 s_normfd(t_uint64 num, uint32 *cc); + +#define NORMASK 0xf8000000 /* normalize 5 bit mask */ +#define DNORMASK 0xf800000000000000ll /* double normalize 5 bit mask */ +#define EXMASK 0x7f000000 /* exponent mask */ +#define FRMASK 0x80ffffff /* fraction mask */ +#define DEXMASK 0x7f00000000000000ll /* exponent mask */ +#define DFSVAL 0xff00000000000000ll /* minus full scale value */ +#define DFRMASK 0x80ffffffffffffffll /* fraction mask */ +#define NEGATE32(val) ((~val) + 1) /* negate a value 16/32/64 bits */ + +/************************************************************** +* Common routine for finishing the various F.P. instruction * +* * +* Floating point operations not terminating with an arith- * +* metic exception produce the following condition codes: * +* * +* CC1 CC2 CC3 CC4 Definition * +* ------------------------------------------------------- * +* 0 1 0 0 no exception, fraction positive * +* 0 0 1 0 no exception, fraction negative * +* 0 0 0 1 no exception, fraction = zero * +* * +* * +* an arithmetic exception produces the follwing condition * +* code settings: * +* * +* CC1 CC2 CC3 CC4 Definition * +* -------------------------------------------------------- * +* 1 0 1 0 exp underflow, fraction negative * +* 1 0 1 1 exp overflow, fraction negative * +* 1 1 0 0 exp underflow, fraction positive * +* 1 1 0 1 exp overflow, fraction positive * +* * +**************************************************************/ + +/* normalize floating point fraction */ +uint32 s_nor(uint32 reg, uint32 *exp) { + uint32 texp = 0; /* no exponent yet */ + + if (reg != 0) { /* do nothing if reg is already zero */ + uint32 mv = reg & NORMASK; /* mask off bits 0-4 */ + while ((mv == 0) || (mv == NORMASK)) { + /* not normalized yet, so shift 4 bits left */ + reg <<= 4; /* move over 4 bits */ + texp++; /* bump shift count */ + mv = reg & NORMASK; /* just look at bits 0-4 */ + } + /* bits 0-4 of reg is neither 0 nor all ones */ + /* show that reg is normalized */ + texp = (uint32)(0x40-(int32)texp); /* subtract shift count from 0x40 */ + } + *exp = texp; /* return exponent */ + return (reg); /* return normalized register */ +} + +/* normalize double floating point number */ +t_uint64 s_nord(t_uint64 reg, uint32 *exp) { + uint32 texp = 0; /* no exponent yet */ + + if (reg != 0) { /* do nothing if reg is already zero */ + t_uint64 mv = reg & DNORMASK; /* mask off bits 0-4 */ + while ((mv == 0) || (mv == DNORMASK)) { + /* not normalized yet, so shift 4 bits left */ + reg <<= 4; /* move over 4 bits */ + texp++; /* bump shift count */ + mv = reg & DNORMASK; /* just look at bits 0-4 */ + } + /* bits 0-4 of reg is neither 0 nor all ones */ + /* show that reg is normalized */ + texp = (uint32)(0x40-(int32)texp); /* subtract shift count from 0x40 */ + } + *exp = texp; /* return exponent */ + return (reg); /* return normalized double register */ +} + +/* normalize the memory value when adding number to zero */ +uint32 s_normfw(uint32 num, uint32 *cc) { + uint32 ret; + int32 val; /* temp word */ + int32 exp; /* exponent */ + int32 CCs; /* condition codes */ + uint8 sign; /* original sign */ + + if (num == 0) { /* make sure we have a number */ + *cc = CC4BIT; /* set the cc's */ + return 0; /* return zero */ + } + sign = 0; + + /* special case 0x80000000 (-0) to set CCs to 1011 * value to 0x80000001 */ + if (num == 0x80000000) { + CCs = CC1BIT|CC3BIT|CC4BIT; /* we have AE, exp overflow, neg frac */ + ret = 0x80000001; /* return max neg value */ + + /* return normalized number */ + *cc = CCs; /* set the cc's */ + return ret; /* return result */ + } + + /* special case pos exponent & zero mantissa to be 0 */ + if (((num & 0x80000000) == 0) && ((num & 0xff000000) > 0) && ((num & 0x00ffffff) == 0)) { + ret = 0; /* 0 to any power is still 0 */ + CCs = CC4BIT; /* set zero CC */ + + /* return normalized number */ + *cc = CCs; /* set the cc's */ + return ret; /* return result */ + } + + /* if we have 1xxx xxxx 0000 0000 0000 0000 0000 0000 */ + /* we need to convert to 1yyy yyyy 1111 0000 0000 0000 0000 0000 */ + /* where y = x - 1 */ + if ((num & 0x80ffffff) == 0x80000000) { + int nexp = (0x7f000000 & num) - 0x01000000; + num = 0x80000000 | (nexp & 0x7f000000) | 0x00f00000; + } + + exp = (num & 0x7f000000) >> 24; /* get exponent */ + if (num & 0x80000000) { /* test for neg */ + sign = 1; /* we are neq */ + num = NEGATE32(num); /* two's complement */ + exp ^= 0x7f; /* complement exponent */ + } + val = num & 0x00ffffff; /* get mantissa */ + + /* now make sure number is normalized */ + while ((val != 0) && ((val & 0x00f00000) == 0)) { + val <<= 4; /* move up a nibble */ + exp--; /* and decrease exponent */ + } + + if (exp < 0) { /* check for underflow */ + CCs = CC1BIT; /* we have underflow */ + if (sign & 1) /* we are neq */ + CCs |= CC3BIT; /* set neg CC */ + else + CCs |= CC2BIT; /* set pos CC */ + ret = 0; /* number too small, make 0 */ + exp = 0; /* exponent too */ + + /* return normalized number */ + *cc = CCs; /* set the cc's */ + return ret; /* return result */ + } + + /* rebuild normalized number */ + val = ((val & 0x00ffffff) | ((exp & 0x7f) << 24)); + if (sign & 1) /* we are neq */ + val = NEGATE32(val); /* two's complement */ + if (val == 0) + CCs = CC4BIT; /* show zero */ + else if (val & 0x80000000) /* neqative? */ + CCs = CC3BIT; /* show negative */ + else + CCs = CC2BIT; /* show positive */ + ret = val; /* return normalized number */ + + /* return normalized number */ + *cc = CCs; /* set the cc's */ + return ret; /* return result */ +} + +#ifdef FOR_DEBUG +/* sfpval - determine floating point data value */ +float sfpval(uint32 val) +{ + uint32 wd32; + float num; + int32 exp; + + exp = ((val >> 24) & 0x7f) - 0x40; /* get exponent and remove excess 0x40 */ + wd32 = val & 0x00ffffff; /* get mantissa */ + num = (float)wd32; /* make it a float */ + num *= exp2f((4*exp) - 24); /* raise to power of exponent */ + if (val & 0x80000000) + num *= -1.0; /* if negative, return negative num */ + return num; /* return value */ +} + +/* dfpval - determine double floating point data value */ +double dfpval(t_uint64 wd64) +{ + double dbl; + int32 exp; + t_uint64 sav = wd64; + + if (wd64 & 0x8000000000000000ll) + wd64 = NEGATE32(wd64); + exp = ((wd64 >> 56) & 0x7f) - 0x40; /* get exponent and remove excess 0x40 */ + wd64 &= 0x00ffffffffffffffll; /* get 56 bit mantissa */ + dbl = (double)wd64; /* make it a double float */ + dbl *= exp2((4*exp) - 56); /* raise to power of exponent */ + if (sav & 0x8000000000000000ll) + dbl *= -1.0; /* if negative, return negative num */ + return dbl; /* return value */ +} +#endif /* FOR_DEBUG */ + +/* normalize the memory value when adding number to zero */ +t_uint64 s_normfd(t_uint64 num, uint32 *cc) { + t_uint64 ret; + t_uint64 val; /* temp word */ + int32 exp; /* exponent */ + int32 CCs; /* condition codes */ + uint8 sign; /* original sign */ + + if (num == 0) { /* make sure we have a number */ + *cc = CC4BIT; /* set the cc's */ + return 0; /* return zero */ + } + sign = 0; + + /* special case 0x8000000000000000 (-0) to set CCs to 1011 */ + /* and value to 0x8000000000000001 */ + if (num == 0x8000000000000000LL) { + CCs = CC1BIT|CC3BIT|CC4BIT; /* we have AE, exp overflow, neg frac */ + ret = 0x8000000000000001LL; /* return max neg value */ + /* return normalized number */ + *cc = CCs; /* set the cc's */ + return ret; /* return normalized result */ + } + + /* special case pos exponent & zero mantissa to be 0 */ + if (((num & 0x8000000000000000LL) == 0) && ((num & 0xff00000000000000LL) > 0) && + (num & 0x00ffffffffffffffLL) == 0) { + ret = 0; /* 0 to any power is still 0 */ + CCs = CC4BIT; /* set zero CC */ + /* return normalized number */ + *cc = CCs; /* set the cc's */ + return ret; /* return normalized result */ + } + + /* if we have 1xxx xxxx 0000 0000 0000 0000 0000 0000 */ + /* we need to convert to 1yyy yyyy 1111 0000 0000 0000 0000 0000 */ + /* where y = x - 1 */ + if ((num & 0x80ffffffffffffffLL) == 0x8000000000000000LL) { + t_uint64 nexp = (0x7f00000000000000LL & num) - 0x0100000000000000LL; + num = 0x8000000000000000LL | (nexp & 0x7f00000000000000LL) | 0x00f0000000000000LL; + } + + exp = (num & 0x7f00000000000000LL) >> 56; /* get exponent */ + if (num & 0x8000000000000000LL) { /* test for neg */ + sign = 1; /* we are neq */ + num = NEGATE32(num); /* two's complement */ + exp ^= 0x7f; /* complement exponent */ + } + val = num & 0x00ffffffffffffffLL; /* get mantissa */ + + /* now make sure number is normalized */ + while ((val != 0) && ((val & 0x00f0000000000000LL) == 0)) { + val <<= 4; /* move up a nibble */ + exp--; /* and decrease exponent */ + } + + if (exp < 0) { + CCs = CC1BIT; /* we have underflow */ + if (sign & 1) /* we are neg */ + CCs |= CC3BIT; /* set neg CC */ + else + CCs |= CC2BIT; /* set pos CC */ + ret = 0; /* number too small, make 0 */ + /* return normalized number */ + *cc = CCs; /* set the cc's */ + return ret; /* return normalized result */ + } + + /* rebuild normalized number */ + ret = ((val & 0x00ffffffffffffffll) | (((t_uint64)exp & 0x7f) << 56)); + if (sign & 1) /* we were neg */ + ret = NEGATE32(ret); /* two's complement */ + if (ret == 0) + CCs = CC4BIT; /* show zero */ + else if (ret & 0x8000000000000000LL) /* neqative? */ + CCs = CC3BIT; /* show negative */ + else + CCs = CC2BIT; /* show positive */ + + /* return normalized number */ + *cc = CCs; /* set the cc's */ + return ret; /* return normalized result */ +} + +/* convert from 32 bit float to 32 bit integer */ +/* set CC1 if overflow/underflow exception */ +uint32 s_fixw(uint32 fltv, uint32 *cc) { + uint32 CC = 0, temp, temp2, sc; + uint32 neg = 0; /* clear neg flag */ + + if (fltv & MSIGN) { /* check for negative */ + fltv = NEGATE32(fltv); /* make src positive */ + neg = 1; /* set neg val flag */ + } else { + if (fltv == 0) { /* value zero? */ + temp = 0; /* return zero */ + goto setcc; /* go set CC's */ + } + /* gt 0, fall through */ + } + temp2 = (uint32)(fltv >> 24); /* get exponent */ + fltv <<= 8; /* move src to upper 3 bytes */ + temp2 -= 64; /* take off excess notation */ + temp = temp2; /* save val */ + if ((int32)temp2 == 0) /* exp of zero means zero */ + goto setcc; /* go set CC's */ + if (temp2 & MSIGN) { + /* set CC1 for underflow */ + if (neg) { + temp = 0x7fffffff; /* too big, set to max value */ + goto OVFLO; /* go set CC's */ + } else { + temp = 0; /* assume zero for small values */ + goto UNFLO; /* go set CC's */ + } + } + + temp2 -= 8; /* see if in range */ + temp = fltv; /* save val */ + if ((temp2 == 0) && (fltv == 0x80000000) && (neg == 1)) + goto setcc; /* go set CC's */ + if ((int32)temp2 > 0) { + /* set CC1 for overflow */ + temp = 0; /* assume zero for small values */ + goto OVFLO; /* go set CC's */ + } + sc = (NEGATE32(temp2) * 4); /* pos shift cnt * 4 */ + fltv >>= sc; /* do 4 bit shifts */ + + /* see if overflow to neg */ + /* set CC1 for overflow */ + if (fltv & MSIGN) { + /* set CC1 for overflow */ + temp = 0; /* assume zero for small values */ + goto OVFLO; /* go set CC's */ + } + + /* see if original value was negative */ + if (neg) + fltv = NEGATE32(fltv); /* put back to negative */ + temp = fltv; /* return integer value */ + /* come here to set cc's and return */ + /* temp has return value */ +setcc: + if (temp & MSIGN) + CC |= CC3BIT; /* CC3 for neg */ + else if (temp == 0) + CC |= CC4BIT; /* CC4 for zero */ + else + CC |= CC2BIT; /* CC2 for greater than zero */ + /* return temp for destination reg */ + *cc = CC; /* return CC's */ + return temp; /* return result */ + + /* handle underflow/overflow */ +OVFLO: + CC |= CC4BIT; /* set CC4 for exponent overflow */ +UNFLO: + CC |= CC1BIT; /* set CC1 for arithmetic exception */ + if (neg) /* test for negative */ + CC |= CC3BIT; /* set neg fraction bit CC3 */ + else + CC |= CC2BIT; /* set pos fraction bit CC2 */ + *cc = CC; /* return CC's */ + return temp; /* return result */ +} + +/* convert from 32 bit integer to 32 bit float */ +/* No overflow (CC1) can be generated */ +uint32 s_fltw(uint32 intv, uint32 *cc) { + uint32 CC = 0; + uint32 ret; + uint32 neg = 0; /* zero sign flag */ + uint32 exp = 0; /* exponent */ + uint32 val = (int32)intv; /* integer value */ + + if (intv & 0x80000000) { + val = NEGATE32(intv); + neg = 1; + } + while ((val != 0) && ((val & 0xf0000000) == 0)) { + val <<= 4; /* no round up */ + exp--; + } + if (val != 0) + exp += 0x48; /* set default exponent */ + /* shift value rt 8 bits and round */ + if (val & 0x80) { + if (neg) { + if (val & 0x7f) + val = (val >> 8) + 1; /* round up */ + else + val = (val >> 8); /* no round up */ + } else { + val = (val >> 8) + 1; /* round up */ + } + } else + val = (val >> 8); /* no round up */ + if (val & 0x01000000) { + val = (val >> 4); /* move 1 nibble */ + exp++; + } + ret = (exp << 24) | (val & 0x00ffffff); /* merge value */ + if (neg) + ret = NEGATE32(ret); + if (ret & MSIGN) + CC |= CC3BIT; /* CC3 for neg */ + else if (ret == 0) + CC |= CC4BIT; /* CC4 for zero */ + else + CC |= CC2BIT; /* CC2 for greater than zero */ + /* return temp for destination reg */ + *cc = CC; /* save CC's */ + return ret; /* return results */ +} + +/* convert from 64 bit double to 64 bit integer */ +/* set CC1 if overflow/underflow exception */ +t_uint64 s_fixd(t_uint64 dblv, uint32 *cc) { + uint32 temp2, CC = 0, neg = 0, sc = 0; + t_uint64 dest; + + /* neg and CC flags already set to zero */ + if (dblv & DMSIGN) { + dblv = NEGATE32(dblv); /* make src positive */ + neg = 1; /* set neg val flag */ + } else { + if (dblv == 0) { + dest = 0; /* return zero */ + goto dodblcc; /* go set CC's */ + } + /* gt 0, fall through */ + } + + temp2 = (uint32)(dblv >> 56); /* get exponent */ + dblv <<= 8; /* move fraction to upper 7 bytes */ + temp2 -= 64; /* take off excess notation */ + dest = temp2; /* save val */ + if ((int32)temp2 == 0) /* zero exp means zero */ + goto dodblcc; /* go set CC's */ + if (temp2 & MSIGN) { + /* set CC1 for underflow */ + if (neg) { + dest = 0x7fffffffffffffff; /* too big, set to max value */ + goto DOVFLO; /* go set CC's */ + } else { + dest = 0; /* assume zero for small values */ + goto DUNFLO; /* go set CC's */ + } + } + + temp2 -= 16; /* see if in range */ + dest = dblv; /* save val */ + if ((temp2 == 0) && (dblv == DMSIGN) && (neg == 1)) + goto dodblcc; /* go set CC's */ + if ((int32)temp2 > 0) { + /* set CC1 for overflow */ + dest = 0; /* assume zero for small values */ + goto DOVFLO; /* go set CC's */ + } + sc = (NEGATE32(temp2) * 4); /* pos shift cnt * 4 */ + dblv >>= sc; /* do 4 bit shifts */ + + /* see if overflow to neg */ + if (dblv & DMSIGN) { + /* set CC1 for overflow */ + dest = 0; /* assume zero for small values */ + goto DOVFLO; /* go set CC's */ + } + /* see if original values was negative */ + if (neg) + dblv = NEGATE32(dblv); /* put back to negative */ + dest = dblv; /* return integer value */ + +dodblcc: + /* dest has return value */ + if (dest & DMSIGN) + CC |= CC3BIT; /* CC3 for neg */ + else if (dest == 0) + CC |= CC4BIT; /* CC4 for zero */ + else + CC |= CC2BIT; /* CC2 for greater than zero */ + *cc = CC; /* return CC's */ + return dest; /* return result */ + + /* handle underflow/overflow */ +DOVFLO: + CC |= CC4BIT; /* set CC4 for exponent overflow */ +DUNFLO: + CC |= CC1BIT; /* set CC1 for arithmetic exception */ + if (neg) /* test for negative */ + CC |= CC3BIT; /* set neg fraction bit CC3 */ + else + CC |= CC2BIT; /* set pos fraction bit CC2 */ + *cc = CC; /* return CC's */ + return dest; /* return result */ +} + +/* convert from 64 bit integer to 64 bit double */ +/* No overflow (CC1) can be generated */ +t_uint64 s_fltd(t_uint64 intv, uint32 *cc) { + t_uint64 ret = 0; /* zero return val */ + uint32 neg = 0; /* zero sign flag */ + uint32 CC = 0; /* n0 CC's yet */ + uint32 exp = 0; /* exponent */ + t_uint64 val = intv; /* integer value */ + + if (intv & DMSIGN) { + val = NEGATE32(intv); /* make src positive */ + neg = 1; /* set neg flag */ + } else { + if (intv == 0) { /* see if zero */ + ret = 0; /* return zero */ + CC = CC4BIT; /* CC4 for zero */ + /* return 0 for destination regs */ + *cc = CC; /* return CC's */ + return ret; /* return result */ + } + /* gt 0, fall through */ + } + + /* see if normalized */ + while ((val) && ((val & 0xf000000000000000ll) == 0)) { + val <<= 4; /* zero, shift in next nibble */ + exp--; /* decr exp value */ + } + if (val != 0) + exp += 0x50; /* default exponent */ + + /* shift value rt 8 bits and round */ + if (val & 0x91ll) { + if (neg) { + if (val & 0x7fl) + val = (val >> 8) + 1; /* round up */ + else + val = (val >> 8); /* no round up */ + } else { + if (val & 0x7fl) + val = (val >> 8); /* no round up */ + else + val = (val >> 8) + 1; /* round up */ + } + } else + val = (val >> 8); /* no round up */ + + if (val & 0x0100000000000000ll) { + val = (val >> 4); /* no round up */ + exp++; + } + ret = (((t_uint64)exp) << 56) | (val & 0x00ffffffffffffffll); /* merge value */ + + if (neg) + ret = NEGATE32(ret); + if (ret & DMSIGN) + CC |= CC3BIT; /* CC3 for neg */ + else if (ret == 0) + CC |= CC4BIT; /* CC4 for zero */ + else + CC |= CC2BIT; /* CC2 for greater than zero */ + + /* return temp for destination regs */ + *cc = CC; /* return CC's */ + return ret; /* return result */ +} + +#define CMASK 0x10000000 /* carry mask */ +#define EMASK 0x7f000000 /* single exponent mask */ +#define UMASK 0x0ffffff0 /* single fp mask */ +#define XMASK 0x0fffffff /* single fp mask */ +#define MMASK 0x00ffffff /* single mantissa mask */ +#define NMASK 0x0f000000 /* single nibble mask */ +#define ZMASK 0x00f00000 /* single nibble mask */ + +/* this new version is perfect against the diags, so good */ +/* do new SEL floating add derived from IBM370 code */ +/* Add/Sub single floating point */ +uint32 s_adfw(uint32 reg, uint32 mem, uint32 *cc) +{ + uint32 res, ret; + char sign = 0; + int er, em, temp; + uint32 CC; + + /* first we want to make sure the numbers are normalized */ + ret = s_normfw(reg, &CC); /* get the reg value */ + if (CC & CC1BIT) { /* see if we have AE */ + *cc = CC; /* save CC's */ + return ret; /* return results */ + } + reg = ret; /* use normalized value */ + if (mem == 0) { /* test for add of zero */ + *cc = CC; /* save CC's */ + return ret; /* return normalized results */ + } + + ret = s_normfw(mem, &CC); /* get the reg value */ + if (CC & CC1BIT) { /* see if we have AE */ + *cc = CC; /* save CC's */ + return ret; /* return results */ + } + mem = ret; /* use normalized value */ + if (reg == 0) { /* test for add to zero */ + *cc = CC; /* save CC's */ + return ret; /* return results results */ + } + + /* extract reg exponent and mantissa */ + if (reg & MSIGN) { /* reg negative */ + sign |= 2; /* set neg flag */ + reg = NEGATE32(reg); /* make negative positive */ + } + er = (reg & EMASK) >> 24; /* extract reg exponent */ + reg &= MMASK; /* extract reg mantissa */ + + /* extract mem exponent and mantissa */ + if (mem & MSIGN) { /* mem negative */ + sign |= 1; /* set neg flag */ + mem = NEGATE32(mem); /* make negative positive */ + } + em = (mem & EMASK) >> 24; /* extract mem exponent */ + mem &= MMASK; /* extract mem mantissa */ + + temp = er - em; /* get signed exp difference */ + mem = mem << 4; /* align mem for guard digit */ + reg = reg << 4; /* align reg for guard digit */ + + if (temp > 0) { /* reg exp > mem exp */ + if (temp > 8) { + mem = 0; /* if too much difference, make zero */ + } else { + /* Shift mem right if reg has larger exponent */ + while (temp-- != 0) { + mem >>= 4; /* adjust for exponent difference */ + em++; /* bump exponent */ + } + } + } else + if (temp < 0) { /* reg < mem exp */ + if (temp < -8) { + reg = 0; /* if too much difference, make zero */ + er = em; /* make exponents the same using mem exp */ + } else { + /* Shift reg right if mem has larger exponent */ + while (temp++ != 0) { + reg >>= 4; /* adjust for exponent difference */ + er++; /* bump exponent */ + } + } + } + + /* exponents should be equal now */ + /* add results */ + if (sign == 2 || sign == 1) { + /* different signs so do subtract */ + mem ^= XMASK; /* complement the value */ + mem++; /* increment the value */ + res = reg + mem; /* add the values */ + if (res & CMASK) { /* see if carry */ + res &= XMASK; /* clear off the carry bit */ + } else { + sign ^= 2; /* flip the sign */ + res ^= XMASK; /* and negate the value by comp */ + res++; /* incr */ + } + } else { + res = reg + mem; /* same sign, just add */ + if (sign == 3) + res += 7; /* round number */ + } + + /* If overflow, shift right 4 bits */ + if (res & CMASK) { /* see if overflow carry */ + res >>= 4; /* move mantissa down 4 bits */ + er++; /* and adjust exponent */ + if (er >= 128) { /* if exponent too large, overflow */ + CC = CC1BIT|CC4BIT; /* set arithmetic overflow */ + /* OVERFLOW */ + /* set CC2 & CC3 on exit */ + CC |= (sign & 2)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */ + if (CC & CC3BIT) /* NEG overflow? */ + res = 0x80000001; /* yes */ + else + res = 0x7FFFFFFF; /* no, pos */ + /* Store result */ + *cc = CC; /* save CC's */ + return res; + } + } + + CC = 0; /* no CC's yet */ + /* Set condition codes */ + if (res != 0) /* see if non zero */ + CC |= (sign & 2) ? 1 : 2; + else { + er = sign = 0; /* we have zero CC4 */ + } + + /* normalize the fraction */ + if (CC != 0) { /* check for zero value */ + while ((res != 0) && ((res & NMASK) == 0)) { + res <<= 4; /* adjust mantisa by a nibble */ + er--; /* and adjust exponent smaller by 1 */ + } + /* Check if underflow */ + if (er < 0) { + /* UNDERFLOW */ + CC |= CC1BIT; /* set arithmetic exception */ + CC |= (sign & 2)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */ + res = 0; /* make all zero */ + /* Store result */ + *cc = CC; /* save CC's */ + return res; /* return value */ + } + } else { + /* result is zero */ + sign = er = 0; /* make abs zero */ + } + + res >>= 4; /* remove the guard nibble */ + + /* create result */ + res |= (er << 24) & EXMASK; /* combine exponent & mantissa */ + + /* set the CC's */ + if (CC == 0) { + CC = CC4BIT; /* zero value */ + } else { + if (sign & 2) + res = NEGATE32(res); /* make negative */ + CC = (CC & 3) << 28; /* neg is CC3, pos is CC2 */ + } + + *cc = CC; /* save CC's */ + return res; /* return result */ +} + +/* subtract memory floating point number from register floating point number */ +uint32 s_sufw(uint32 reg, uint32 mem, uint32 *cc) { + return s_adfw(reg, NEGATE32(mem), cc); +} + +/* multiply register floating point number by memory floating point number */ +/* set CC1 if overflow/underflow */ +/* use revised normalization code */ +uint32 s_mpfw(uint32 reg, uint32 mem, uint32 *cc) { + uint32 res, ret; + int sign = 0; + int lsb = 0; + int er, em, temp; + uint32 CC; + + /* first we want to make sure the numbers are normalized */ + ret = s_normfw(reg, &CC); /* get the reg value */ + if (CC & CC1BIT) { /* see if we have AE */ + *cc = CC; /* save CC's */ + return ret; /* return results */ + } + reg = ret; /* use normalized value */ + + ret = s_normfw(mem, &CC); /* get the reg value */ + if (CC & CC1BIT) { /* see if we have AE */ + *cc = CC; /* save CC's */ + return ret; /* return results */ + } + mem = ret; /* use normalized value */ + + /* see if multiply by zero */ + if ((reg == 0) || (mem == 0)) { /* test for mult by zero */ + *cc = CC4BIT; /* set CC 4 for 0 */ + return 0; /* return results */ + } + + /* extract reg exponent and mantissa */ + if (reg & MSIGN) { /* reg negative */ + sign ^= 1; /* set neg flag */ + reg = NEGATE32(reg); /* make negative positive */ + } + if (reg & 0x1) /* test lsb */ + lsb = 1; /* reg is odd */ + er = (reg & EXMASK) >> 24; /* extract reg exponent */ + reg &= MMASK; /* extract reg mantissa */ + + /* extract mem exponent and mantissa */ + if (mem & MSIGN) { /* mem negative */ + sign ^= 1; /* set neg flag */ + mem = NEGATE32(mem); /* make negative positive */ + } + if (mem & 0x1) /* test lsb */ + lsb = 1; /* reg is odd */ + em = (mem & EXMASK) >> 24; /* extract mem exponent */ + mem &= MMASK; /* extract mem mantissa */ + + er = er + em - 0x40; /* get the exp value */ + reg = reg << 4; /* create guard digit */ + mem = mem << 4; /* create guard digit */ + + res = 0; /* zero result for multiply */ + /* Do multiply with guard bit */ + for (temp = 0; temp < 28; temp++) { + /* Add if we need too */ + if (reg & 1) + res += mem; + /* Shift right by one */ + reg >>= 1; + res >>= 1; + } + + /* fix up some boundry rounding */ + if ((res >= 0x01000000) && (sign == 0)) { + res += 0x8; + } + if ((res == 0x00FFFFFF) && (sign == 1) && (er != 1)) { + if (lsb == 1) { + if ((er != 0x41) && (er != 0x81)) { + res += 0x1; + } + } + } + + /* If overflow, shift right 4 bits */ + if (res & 0x70000000) { /* see if overflow carry */ + res >>= 4; /* move mantissa down 4 bits */ + er++; /* and adjust exponent */ + if (er >= 128) { /* if exponent is too large, overflow */ + /* OVERFLOW */ + CC = CC1BIT; /* set arithmetic exception */ + CC |= (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */ + if (CC & CC3BIT) /* NEG overflow? */ + res = 0x80000001; /* double yes */ + else + res = 0x7FFFFFFF; /* no, pos */ + /* store results */ + *cc = CC; /* save CC's */ + return res; /* return results */ + } + } + + /* Align the results & normalize */ + if (res != 0) { + while ((res != 0) && (res & NMASK) == 0) { + res <<= 4; + er--; + } + /* Check if overflow */ + if (er >= 128) { /* if exponent is too large, overflow */ + /* OVERFLOW */ + CC = CC1BIT|CC4BIT; /* set arithmetic exception */ + if (sign & 1) { + CC |= CC3BIT; + res = 0x80000001; /* neg overflow 1011 */ + } else { + CC |= CC2BIT; + res = 0x7FFFFFFF; /* pos overflow 1101 */ + } + /* store results */ + *cc = CC; /* save CC's */ + return res; /* return results */ + } + /* Check if underflow */ + if (er < 0) { + /* UNDERFLOW */ + res = 0; /* make return value zero */ + CC = (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */ + CC |= CC1BIT; /* set arithmetic exception */ + *cc = CC; /* save CC's */ + return res; /* return results */ + } + res >>= 4; /* remove guard nibble */ + } else + er = sign = 0; + + res &= MMASK; /* clear exponent */ + + res |= ((((uint32)er) << 24) & EXMASK); /* merge exp and mantissa */ + + if (sign == 1) /* is result to be negative */ + res = NEGATE32(res); /* make value negative */ + + CC = 0; + if (res != 0) /* see if non zero */ + CC = (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */ + else + CC = CC4BIT; /* set zero cc */ + + /* return results */ + *cc = CC; /* save CC's */ + return res; /* return results */ +} + +/* divide register float by memory float */ +uint32 s_dvfw(uint32 reg, uint32 mem, uint32 *cc) { + uint32 CC = 0, temp, temp2, sign; + uint32 expm, expr; + t_uint64 dtemp; + + /* process operator */ + sign = mem & MSIGN; /* save original value for sign */ + if (mem == 0) /* check for divide by zero */ + goto DOVFLO; /* go process divide overflow */ + + if (mem & MSIGN) /* check for negative */ + mem = NEGATE32(mem); /* make mem positive */ + + expm = (mem >> 24); /* get operand exponent */ + mem <<= 8; /* move fraction to upper 3 bytes */ + mem >>= 1; /* adjust fraction for divide */ + + /* process operand */ + if (reg == 0) { + temp = 0; /* return zero */ + goto setcc; /* go set CC's */ + } + if (reg & MSIGN) { /* check for negative */ + reg = NEGATE32(reg); /* make reg positive */ + sign ^= MSIGN; /* complement sign */ + } + expr = (reg >> 24); /* get operator exponent */ + reg <<= 8; /* move fraction to upper 3 bytes */ + reg >>= 6; /* adjust fraction for divide */ + + temp = expr - expm; /* subtract exponents */ + dtemp = ((t_uint64)reg) << 32; /* put reg fraction in upper 32 bits */ + temp2 = (uint32)(dtemp / mem); /* divide reg fraction by mem fraction */ + temp2 >>= 3; /* shift out excess bits */ + temp2 <<= 3; /* replace with zero bits */ + + if (sign & MSIGN) + temp2 = NEGATE32(temp2); /* if negative, negate fraction */ + /* normalize the result in temp and put exponent into expr */ + temp2 = s_nor(temp2, &expr); /* normalize fraction */ + temp += 1; /* adjust exponent */ + +//RROUND: + if (temp2 == MSIGN) { /* check for minus zero */ + temp2 = 0xF8000000; /* yes, fixup value */ + expr++; /* bump exponent */ + } + + if ((int32)temp2 >= 0x7fffffc0) /* check for special rounding */ + goto RRND2; /* no special handling */ + + if (expr != 0x40) { /* result normalized? */ + goto RRND2; /* if not, don't round */ + } + /* result normalized */ + if ((sign & MSIGN) == 0) + goto RRND1; /* if sign set, don't round yet */ + expr += temp; /* add exponent */ + + if (expr & MSIGN) /* test for underflow */ + goto DUNFLO; /* go process underflow */ + + if ((int32)expr > 0x7f) /* test for overflow */ + goto DOVFLO; /* go process overflow */ + + expr ^= FMASK; /* complement exponent */ + temp2 += 0x40; /* round at bit 25 */ + goto RRND3; /* go merge code */ + +RRND1: + temp2 += 0x40; /* round at bit 25 */ +RRND2: + expr += temp; /* add exponent */ + + if (expr & MSIGN) /* test for underflow */ + goto DUNFLO; /* go process underflow */ + + if ((int32)expr > 0x7f) /* test for overflow */ + goto DOVFLO; /* go process overflow */ + + if (sign & MSIGN) /* test for negative */ + expr ^= FMASK; /* yes, complement exponent */ +RRND3: + temp2 <<= 1; /* adjust fraction */ + temp = (expr << 24) | (temp2 >> 8); /* merge exp & fraction */ + goto setcc; /* go set CC's */ + +DOVFLO: + CC |= CC4BIT; /* set CC4 for exponent overflow */ +DUNFLO: + CC |= CC1BIT; /* set CC1 for arithmetic exception */ + if (sign & MSIGN) /* test for negative */ + CC |= CC3BIT; /* set neg fraction bit CC3 */ + else + CC |= CC2BIT; /* set pos fraction bit CC2 */ + *cc = CC; /* return CC's */ + /* return value is not valid, but return fixup value anyway */ + switch ((CC >> 27) & 3) { /* rt justify CC3 & CC4 */ + case 0: + return 0; /* pos underflow */ + break; + case 1: + return 0x7fffffff; /* positive overflow */ + break; + case 2: + return 0; /* neg underflow */ + break; + case 3: + return 0x80000001; /* negative overflow */ + break; + } +setcc: + /* come here to set cc's and return */ + /* temp has return value */ + if (temp & MSIGN) + CC |= CC3BIT; /* CC3 for neg */ + else + if (temp == 0) + CC |= CC4BIT; /* CC4 for zero */ + else + CC |= CC2BIT; /* CC2 for greater than zero */ + /* return temp to destination reg */ + *cc = CC; /* return CC's */ + return temp; /* return result */ +} + +#define DMMASK 0x00ffffffffffffffLL /* double mantissa mask */ +#define DCMASK 0x1000000000000000LL /* double carry mask */ +#define DIBMASK 0x0fffffffffffffffLL /* double fp nibble mask */ +#define DUMASK 0x0ffffffffffffff0LL /* double fp mask */ +#define DNMASK 0x0f00000000000000LL /* double nibble mask */ +#define DZMASK 0x00f0000000000000LL /* shifted nibble mask */ + +/* add memory floating point number to register floating point number */ +/* this code creates an extra guard digit, so it is more accurate than SEL */ +/* The code was modified to have the same results as SEL, so we will use this one */ +/* set CC1 if overflow/underflow */ +t_uint64 s_adfd(t_uint64 reg, t_uint64 mem, uint32 *cc) +{ + t_uint64 res, ret; + uint8 sign = 0; + int er, em, temp; + uint32 CC; + + /* first we want to make sure the numbers are normalized */ + ret = s_normfd(reg, &CC); /* get the reg value */ + if (CC & CC1BIT) { /* see if we have AE */ + *cc = CC; /* save CC's */ + return ret; /* return results */ + } + reg = ret; /* use normalized value */ + if (mem == 0) { /* test for add of zero */ + *cc = CC; /* save CC's */ + return ret; /* return normalized results */ + } + + ret = s_normfd(mem, &CC); /* get the reg value */ + if (CC & CC1BIT) { /* see if we have AE */ + *cc = CC; /* save CC's */ + return ret; /* return results */ + } + mem = ret; /* use normalized value */ + if (reg == 0) { /* test for add to zero */ + *cc = CC; /* save CC's */ + return ret; /* return results results */ + } + + /* process the memory operand value */ + /* extract exponent and mantissa */ + if (reg & DMSIGN) { /* reg negative */ + sign |= 2; /* set neg flag */ + reg = NEGATE32(reg); /* make negative positive */ + } + er = (reg & DEXMASK) >> 56; /* extract reg exponent */ + reg &= DMMASK; /* extract reg mantissa */ + + /* extract mem exponent and mantissa */ + if (mem & DMSIGN) { /* mem negative */ + sign |= 1; /* set neg flag */ + mem = NEGATE32(mem); /* make negative positive */ + } + em = (mem & DEXMASK) >> 56; /* extract mem exponent */ + mem &= DMMASK; /* extract mem mantissa */ + + mem = mem << 4; /* align mem for normalization */ + reg = reg << 4; /* align reg for normalization */ + temp = er - em; /* get signed exp difference */ + + if (temp > 0) { /* reg exp > mem exp */ + if (temp > 15) { + mem = 0; /* if too much difference, make zero */ + } else { + /* Shift mem right if reg has larger exponent */ + mem >>= (4 * temp); /* adjust for exponent difference */ + } + } else + if (temp < 0) { /* reg < mem exp */ + if (temp < -15) { + reg = 0; /* if too much difference, make zero */ + } else + /* Shift reg right if mem larger */ + reg >>= (4 * (-temp)); /* adjust for exponent difference */ + er = em; /* make exponents the same */ + } + + /* er now has equal exponent for both values */ + /* add results */ + if (sign == 2 || sign == 1) { + /* different signs so do subtract */ + mem ^= DIBMASK; /* complement the value and inc */ + mem++; /* negate all but upper nibble */ + res = reg + mem; /* add the values */ + if (res & DCMASK) { /* see if carry */ + res &= DIBMASK; /* clear off the carry bit */ + } else { + sign ^= 2; /* flip the sign */ + res ^= DIBMASK; /* and negate the value */ + res++; /* negate all but the upper nibble */ + } + } else { + res = reg + mem; /* same sign, just add */ + if ((res & 0x0fffffffffffff80ll) != 0x0fffffffffffff80ll) { + if (sign == 3) + res += 7; /* round number */ + if ((sign == 3) && (er == 0x7e)) + res |= 0x0f00ll; /* round number more */ + if (sign == 0) + res += 0xf; /* round number */ + } + } + /* following statement effectively removes guard nibble to be like SEL */ + res &= 0xfffffffffffffff0ll; /* remove extra bits */ + + /* If overflow, shift right 4 bits */ + if (res & DCMASK) { /* see if overflow carry */ + res >>= 4; /* move mantissa down 4 bits */ + er++; /* and adjust exponent */ + if (er >= 128) { /* if exponent is too large, overflow */ + /* OVERFLOW */ + CC = CC1BIT|CC4BIT; /* set arithmetic overflow */ + /* set CC2 & CC3 on exit */ + CC |= (sign & 2)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */ + if (CC & CC3BIT) /* NEG overflow? */ + res = 0x8000000000000001; /* double yes */ + else + res = 0x7FFFFFFFFFFFFFFF; /* no, pos */ + /* store results */ + *cc = CC; /* save CC's */ + return res; /* return results */ + } + } + + CC = 0; + /* Set condition codes */ + if (res != 0) /* see if non zero */ + CC |= (sign & 2) ? 1 : 2; + else { + er = sign = 0; /* we have zero CC4 */ + } + + /* normalize the fraction */ + if (res != 0) { /* see if non zero */ + while ((res != 0) && (res & DNMASK) == 0) { + res <<= 4; /* adjust mantisa by a nibble */ + er--; /* and adjust exponent smaller by 1 */ + } + /* Check if exponent underflow */ + if (er < 0) { + /* UNDERFLOW */ + CC |= CC1BIT; /* set arithmetic exception */ + CC |= (sign & 2)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */ + res = 0; /* make all zero */ + /* store results */ + *cc = CC; /* save CC's */ + return res; /* return results */ + } + } else { + /* result is zero */ + sign = er = 0; /* make abs zero */ + } + + res >>= 4; /* remove the carryout nibble */ + res &= DMMASK; /* clear exponent */ + + res |= ((((t_uint64)er) << 56) & DEXMASK); /* merge exp and mantissa */ + + /* Set condition codes */ + if (CC == 0) { + CC = CC4BIT; /* set zero cc */ + } else { + if (sign & 2) /* see if negative */ + res = NEGATE32(res); /* make negative */ + CC = (CC & 3) << 28; /* neg is CC3, pos is CC2 */ + } + /* store results */ + *cc = CC; /* save CC's */ + return res; /* return results */ +} + +/* subtract memory floating point number from register floating point number */ +t_uint64 s_sufd(t_uint64 reg, t_uint64 mem, uint32 *cc) { + return s_adfd(reg, NEGATE32(mem), cc); +} + +/* multiply register floating point number by memory floating point number */ +/* set CC1 if overflow/underflow */ +/* use revised normalization code */ +t_uint64 s_mpfd(t_uint64 reg, t_uint64 mem, uint32 *cc) { + t_uint64 res, ret; + int sign = 0; + int lsb = 0; + int er, em, temp; + uint32 CC; + + /* first we want to make sure the numbers are normalized */ + ret = s_normfd(reg, &CC); /* get the reg value */ + if (CC & CC1BIT) { /* see if we have AE */ + *cc = CC; /* save CC's */ + return ret; /* return results */ + } + reg = ret; /* use normalized value */ + + ret = s_normfd(mem, &CC); /* get the reg value */ + if (CC & CC1BIT) { /* see if we have AE */ + *cc = CC; /* save CC's */ + return ret; /* return results */ + } + mem = ret; /* use normalized value */ + + /* see if multiply by zero */ + if ((reg == 0ll) || (mem == 0ll)) { /* test for mult by zero */ + *cc = CC4BIT; /* set CC 4 for 0 */ + return 0ll; /* return results results */ + } + + /* extract reg exponent and mantissa */ + if (reg & DMSIGN) { /* reg negative */ + sign ^= 1; /* set neg flag */ + reg = NEGATE32(reg); /* make negative positive */ + } + if (reg & 0x1ll) /* test lsb */ + lsb = 1; /* reg is odd */ + er = (reg & DEXMASK) >> 56; /* extract reg exponent */ + reg &= DMMASK; /* extract reg mantissa */ + + /* extract mem exponent and mantissa */ + if (mem & DMSIGN) { /* mem negative */ + sign ^= 1; /* set neg flag */ + mem = NEGATE32(mem); /* make negative positive */ + } + if (mem & 0x1ll) /* test lsb */ + lsb = 1; /* reg is odd */ + em = (mem & DEXMASK) >> 56; /* extract mem exponent */ + mem &= DMMASK; /* extract mem mantissa */ + + er = er + em - 0x40; /* get the exp value */ + + res = 0; /* zero result for multiply */ + /* multiply by doing shifts and adds */ + for (temp = 0; temp < 56; temp++) { + /* Add if we need too */ + if (reg & 1) + res += mem; + /* Shift right by one */ + reg >>= 1; + res >>= 1; + } + er++; /* adjust exp for extra nible shift */ + + /* fix up some boundry conditions */ + if ((res >= 0x0010000000000000ll) && (sign == 1)) { + res += 0x1; + } + else + if ((res == 0x000FFFFFFFFFFFFFll) && (sign == 1) && (er != 1)) { + if (lsb == 0) { + if ((er == 0x41) || (er == 0x81)) { + er++; + } + } else { + res += 0x1ll; + } + } + + /* If overflow, shift right 4 bits */ + if (res & DEXMASK) { /* see if overflow carry */ + res >>= 4; /* move mantissa down 4 bits */ + er++; /* and adjust exponent */ + if (er >= 0x80) { /* if exponent is too large, overflow */ + /* OVERFLOW */ + CC = CC1BIT; /* set arithmetic exception */ + CC |= (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */ + if (CC & CC3BIT) /* NEG overflow? */ + res = 0x8000000000000001ll; /* double yes */ + else + res = 0x7FFFFFFFFFFFFFFFll; /* no, pos */ + /* store results */ + *cc = CC; /* save CC's */ + return res; /* return results */ + } + } + + /* Align the results */ + if (res != 0) { + while ((res != 0) && (res & DNMASK) == 0) { + res <<= 4; /* move over mantessa */ + er--; /* reduce exponent cocunt by 1 */ + if ((res == 0x00FFFFFFFFFFFFF0ll) && (sign == 1)) { + if (lsb == 0) { + er--; + } + else { + res += 0x10ll; + } + } + } + /* Check if overflow */ + if (er >= 128) { /* if exponent is too large, overflow */ + /* OVERFLOW */ + CC = CC1BIT|CC4BIT; /* set arithmetic exception */ + if (sign & 1) { + CC |= CC3BIT; /* neg CC */ + res = 0x8000000000000001ll; /* neg overflow 1011 */ + } else { + CC |= CC2BIT; /* pos CC */ + res = 0x7FFFFFFFFFFFFFFFll; /* pos overflow 1101 */ + } + /* store results */ + *cc = CC; /* save CC's */ + return res; /* return results */ + } + /* Check if underflow */ + if (er < 0) { + /* UNDERFLOW */ + res = 0; /* make return value zero */ + CC = (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */ + CC |= CC1BIT; /* set arithmetic exception */ + *cc = CC; /* save CC's */ + return res; /* return results */ + } + res >>= 4; /* remove guard nibble */ + } else { + er = sign = 0; /* have real zero */ + } + + res &= DMMASK; /* clear exponent */ + + res |= ((((t_uint64)er) << 56) & DEXMASK); /* merge exp and mantissa */ + if (sign == 1) /* is result to be negative */ + res = NEGATE32(res); /* make value negative */ + + /* determine CC's for result */ + CC = 0; + if (res != 0) /* see if non zero */ + CC = (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */ + else + CC = CC4BIT; /* set zero cc */ + + /* return results */ + *cc = CC; /* save CC's */ + return res; /* return results */ +} + +/* divide register floating point number by memory floating point number */ +/* set CC1 if overflow/underflow */ +/* use revised normalization code */ +t_uint64 s_dvfd(t_uint64 reg, t_uint64 mem, uint32 *cc) { + t_uint64 res, ret; + int sign = 0; + int sign2 = 0; + int lsb = 0; + int er, em, temp; + uint32 CC; + + /* first we want to make sure the numbers are normalized */ + ret = s_normfd(reg, &CC); /* get the reg value */ + if (CC & CC1BIT) { /* see if we have AE */ + *cc = CC; /* save CC's */ + return ret; /* return results */ + } + reg = ret; /* use normalized value */ + + ret = s_normfd(mem, &CC); /* get the reg value */ + if (CC & CC1BIT) { /* see if we have AE */ + *cc = CC; /* save CC's */ + return ret; /* return results */ + } + mem = ret; /* use normalized value */ + + /* see if divide by or into zero */ + if ((reg == 0ll) || (mem == 0ll)) { /* test for divide by zero */ + *cc = CC4BIT; /* set CC 4 for 0 */ + return 0ll; /* return results */ + } + + /* extract reg exponent and mantissa */ + if (reg & DMSIGN) { /* reg negative */ + sign ^= 1; /* set neg flag */ + reg = NEGATE32(reg); /* make negative positive */ + sign2 |= 2; /* set neg flag */ + } + if (reg & 0x1ll) /* test lsb */ + lsb = 1; /* reg is odd */ + er = (reg & DEXMASK) >> 56; /* extract reg exponent */ + reg &= DMMASK; /* extract reg mantissa */ + + /* extract mem exponent and mantissa */ + if (mem & DMSIGN) { /* mem negative */ + sign ^= 1; /* set neg flag */ + mem = NEGATE32(mem); /* make negative positive */ + sign2 |= 1; /* set neg flag */ + } + if (mem & 0x1ll) /* test lsb */ + lsb = 1; /* reg is odd */ + em = (mem & DEXMASK) >> 56; /* extract mem exponent */ + mem &= DMMASK; /* extract mem mantissa */ + + er = er - em + 0x40; /* get the exp value */ + + /* move left 1 nibble for divide */ + /* Shift numbers up 4 bits so as not to lose precision below */ + reg <<= 4; + mem <<= 4; + + /* see if we need to adjust divisor if larger that dividend */ + if (reg > mem) { + reg >>= 4; + er++; + } + + mem ^= DIBMASK; /* change sign of mem val to do add */ + mem++; /* comp & incr */ + + res = 0; /* zero result for multiply */ + /* do divide by using shift & add (subt) */ + for (temp = 56; temp > 0; temp--) { + t_uint64 tmp; + + /* Add if we need too */ + /* Shift left by one */ + reg <<= 1; + /* Subtract remainder to dividend */ + tmp = reg + mem; + + /* Shift quotent left one bit */ + res <<= 1; + + /* If remainder larger then divisor replace */ + if ((tmp & DCMASK) != 0) { + reg = tmp; + res |= 1; + } + } + + /* Compute one final set to see if rounding needed */ + /* Shift left by one */ + reg <<= 1; + /* Subtract remainder to dividend */ + reg += mem; + + /* If .5 off, round, but do not cause carry overflow */ + if (((reg & DMSIGN) != 0) && (res != 0x00FFFFFFFFFFFFFFll)){ + res++; + } + + /* fix up some boundry condtions to make diags happy */ + if (res == 0x00FFFFFFFFFFFFF1ll) { + res += 0x0fll; /* round up by nibble */ + } + else + if (res == 0x00FFFFFFFFFFFFF8ll) { + res &= 0x0FFFFFFFFFFFFFC0ll; /* remove some extra bits */ + } + else + if (res == 0x00FFFFFFFFFFFFFFll) { + if (lsb == 0) { + res += 0x1ll; /* round up by bit to force carry */ + } else { + if (sign) { + if (sign2 == 1) { + res &= 0x00FFFFFFFFFFFFF0ll; /* clear last nibble */ + } else { + res += 0x1ll; /* round up by bit to for carry */ + } + } else { + if (sign2 == 3) { + res += 0x1ll; /* round up by bit to for carry */ + } else { + res &= 0x00FFFFFFFFFFFFF0ll; /* clear last nibble */ + } + } + } + } + /* diags should be happy now */ + + /* If overflow, shift right 4 bits */ + if (res & DEXMASK) { /* see if overflow carry */ + res >>= 4; /* move mantissa down 4 bits */ + er++; /* and adjust exponent */ + if (er >= 128) { /* if exponent is too large, overflow */ + /* OVERFLOW */ + CC = CC1BIT|CC4BIT; /* set arithmetic exception */ + CC |= (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */ + if (CC & CC3BIT) /* NEG overflow? */ + res = 0x8000000000000001ll; /* double yes */ + else + res = 0x7FFFFFFFFFFFFFFFll; /* no, pos */ + /* store results */ + *cc = CC; /* save CC's */ + return res; /* return results */ + } + } + + /* Align the results */ + if ((res) != 0) { + while ((res != 0) && (res & DZMASK) == 0) { + res <<= 4; + er--; + } + /* Check if overflow */ + if (er >= 128) { /* if exponent is too large, overflow */ + /* OVERFLOW */ + CC = CC1BIT|CC4BIT; /* set arithmetic exception */ + if (sign & 1) { + CC |= CC3BIT; + res = 0x8000000000000001ll; /* neg overflow 1011 */ + } else { + CC |= CC2BIT; + res = 0x7FFFFFFFFFFFFFFFll; /* pos overflow 1101 */ + } + /* store results */ + *cc = CC; /* save CC's */ + return res; /* return results */ + } + /* Check if underflow */ + if (er < 0) { + /* UNDERFLOW */ + res = 0; /* make return value zero */ + CC = (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */ + CC |= CC1BIT; /* set arithmetic exception */ + *cc = CC; /* save CC's */ + return res; /* return results */ + } + } else { + er = sign = 0; + } + + res &= DMMASK; /* clear exponent space */ + + res |= ((((t_uint64)er) << 56) & DEXMASK); /* merge exp and mantissa */ + if (sign & 1) /* is result to be negative */ + res = NEGATE32(res); /* make value negative */ + + /* determine CC's for result */ + CC = 0; + if (res != 0) /* see if non zero */ + CC = (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */ + else + CC = CC4BIT; /* set zero cc */ + + /* return results */ + *cc = CC; /* save CC's */ + return res; /* return results */ +} + diff --git a/SEL32/sel32_hsdp.c b/SEL32/sel32_hsdp.c new file mode 100644 index 00000000..be0f19c7 --- /dev/null +++ b/SEL32/sel32_hsdp.c @@ -0,0 +1,3790 @@ +/* sel32_hsdp.c: SEL-32 8064 High Speed Disk Processor + + Copyright (c) 2018-2022, James C. Bevier + Portions provided by Richard Cornwell and other SIMH contributers + + 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 + JAMES C. BEVIER 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. +*/ + +#include "sel32_defs.h" + +/* uncomment to use fast sim_activate times when running UTX */ +/* UTX gets an ioi error for dm0801 if slow times are used */ +/* dm0801 is not even a valid unit number for UDP controller */ +#define FAST_FOR_UTX + +#if NUM_DEVS_HSDP > 0 + +#define UNIT_HSDP UNIT_ATTABLE | UNIT_IDLE | UNIT_DISABLE + +/* useful conversions */ +/* Fill STAR value from cyl, trk, sec data */ +#define CHS2STAR(c,h,s) (((c<<16) & LMASK)|((h<<8) & 0xff00)|(s & 0xff)) +/* convert STAR value to number of sectors */ +#define STAR2SEC(star,spt,spc) ((star&0xff)+(((star>>8)&0xff)*spt)+(((star>>16)&0xffff)*spc)) +/* convert STAR value to number of heads or tracks */ +#define STAR2TRK(star,tpc) (((star>>16)&0xffff)*tpc+((star>>8)&0x0ff)) +/* convert STAR value to number of cylinders */ +#define STAR2CYL(star) ((star>>16)&RMASK) +/* convert byte value to number of sectors mod sector size */ +#define BYTES2SEC(bytes,ssize) (((bytes) + (ssize-1)) >> 10) +/* get sectors per track for specified type */ +#define SPT(type) (hsdp_type[type].spt) +/* get sectors per cylinder for specified type */ +#define SPC(type) (hsdp_type[type].spt*hsdp_type[type].nhds) +/* get number of tracks for specified type */ +#define TRK(type) (hsdp_type[type].cyl*hsdp_type[type].nhds) +/* get number of cylinders for specified type */ +#define CYL(type) (hsdp_type[type].cyl) +/* get number of heads for specified type */ +#define HDS(type) (hsdp_type[type].nhds) +/* get disk capacity in sectors for specified type */ +#define CAP(type) (CYL(type)*HDS(type)*SPT(type)) +/* get number of bytes per sector for specified type */ +#define SSB(type) (hsdp_type[type].ssiz*4) +/* get disk capacity in bytes for specified type */ +#define CAPB(type) (CAP(type)*SSB(type)) +/* get disk geometry as STAR value for specified type */ +#define GEOM(type) (CHS2STAR(CYL(type),HDS(type),SPT(type))) + +/* INCH command information */ +/* +WD 0 - Data address +WD 1 - Flags - 0 -36 byte count + +Data - 224 word INCH buffer address (SST) +WD 1 Drive 0 Attribute register +WD 2 Drive 1 Attribute register +WD 3 Drive 2 Attribute register +WD 4 Drive 3 Attribute register +WD 5 Drive 4 Attribute register +WD 6 Drive 5 Attribute register +WD 7 Drive 6 Attribute register +WD 8 Drive 7 Attribute register + +Drive attribute register bit assignments (DATR) +Byte 0 bits 0-7 - Flags + Drive type + bits 0&1 - 00=Undefined + - 01=MHD + - 10=Undefined + - 11=Undefined + Optimized seeks + bit 2&3 - 00=Optimize seeks and post IOCL status out of order + - 01=Optimize seeks and post IOCL status in order + - 10=Do not optimize seeks + - 11=Do not optimize seeks + bit 4 - 0=Drive is present + - 1=Drive not present + bit 5 - 0=Not Dual Port + - 1=Dual Port + Sector Size + bit 6&7 - 00=768 bytes + 01=1024 bytes + 10=2048 bytes + 11=Unassigned +Byte 1 bits 8-15 - Sectors per track +Byte 2 bits 16-23 - Number of head +Byte 3 bits 24-31 - Reserved (zero) +*/ + +/* +Drive status bit assignments (DSR) +Byte 0 bits 0-7 + bit 00 - Seek End + 01 - Unit selected + 02 - Sector pulse counter bit 0 + 03 - Sector pulse counter bit 1 + 04 - Sector pulse counter bit 2 + 05 - Sector pulse counter bit 3 + 06 - Sector pulse counter bit 4 + 07 - Sector pulse counter bit 5 +Byte 1 bits 7-15 + bit 08 - Disc drive fault + 09 - Seek error + 10 - On cylinder + 11 - Unit Ready + 12 - Write protected + 13 - Drive busy + 14 - Reserved (zero) + 15 - Reserved (zero) +*/ + +/* Subchannel Target Register (STAR) */ +/* byte 0 - Cylinder MS byte */ +/* byte 1 - Cylinder LS byte */ +/* byte 2 - Track count */ +/* byte 3 - Sector count */ + +/* Mode Register (MODE) */ +/* Bits 0-7 - bit assignments */ + +/* Bits 0-3 are for data recovery operations which can be */ +/* tried by the software */ +/* 0 - Servo offset 0/1=disable/enable */ +/* 1 - Servo offset polarity 0/1=positive/negative */ +/* 2 - Data strobe offset 0/1=disable/enable */ +/* 3 - Data strobe offset polarity 0/1=positive/negative */ +/* Bit 4 enables sector ECC data to be read or written for */ +/* diagnostic commands */ +/* 4 - Read/write ECC data 0/1=disable/enable */ +/* Bit 5 controls the transfer of an ID during express bus */ +/* read commands */ +/* 5 - Express bus ID 0/1=enable/disable */ +/* Bit 6 enables auto-retry in accordance with the firmware */ +/* auto-retry algorithms */ +/* 6 - Auto retry 0/1=enable/disable */ +/* Bit 7 disables the subchannel from interacting with the */ +/* disc drive and is for diagnostic testing only */ +/* 7 - Diagnostic mode 0/1=disable/enable */ + +/* Sense Buffer Register (SBR) */ +/* The SBR contains subchannel error status information */ +/* Byte 0 + * bit 00 Command rejected (CR) + * 01 Intervention requested (IR) + * 02 Unit select error (USEL) + * 03 Equipment check (EQCK) + * 04 Reserved (zero) + * 05 Reserved (zero) + * 06 Disc format error (DFER) + * 07 Defective track encountered (DETR) + * Byte 1 + * bit 08 Reserved (zero) + * 09 At alternate track (AATT) + * 10 Write protect error (WPER) + * 11 Write lock error (WRL) + * 12 Mode check (MOCK) + * 13 Invalid address (INAD) + * 14 Release fault (RELF) + * 15 Chaining error (CHER) + * Byte 2 + * bit 16 Revolution lost (REVL) + * 17 Disc addressing or seek error + * 18 Reserved (zero) + * 19 Reserved (zero) + * 20 ECC error in data (ECCD) + * 21 Reserved (zero) + * 22 Reserved (zero) + * 23 Uncorrectable ECC error (UECC) + * Byte 3 + * Not used + * */ + +/* 224 word Subchannel Storage Buffer (SST) */ +/* 128 words reserved */ +/* 66 words (33 DW) of program status queue (PSQ) */ +/* 8 words of retry counters (1/channel) */ +/* 22 words reserved */ + +/************************************/ +/* track label definations 34 bytes */ + /* for track 0, write max cyl/head/sec values in 0-3 */ + /* otherwise write current values */ +/* +0 short lcyl; cylinder +2 char ltkn; head or track number +3 char lid; track label id (0xff means last track) +4 char lflg1; track status flags + bit 0 good trk + 1 alternate trk + 2 spare trk + 3 reserved trk + 4 defective trk + 5 last track + 6-7 n/u = 0 +5 char lflg2; + bit 0 write lock + 1 write protected + 2-7 n/u = 0 +6 short lspar1; n/u = 0 +8 short lspar2; n/u = 0 +10 short ldef1; defect #1 sec and byte position + * for track 0 write DMAP + * write sector number of cyl-4, hds-2, sec 0 value in 12-15 + * otherwise write current values +12 short ldef2; defect #2 sec and byte position +14 short ldef3; defect #3 sec and byte position + * for track 0 write UMAP which is DMAP - 2 * SPT + * write sector number of cyl-4, hds-4, sec 0 value in 16-19 + * otherwise write current values +16 short ladef1; defect #1 abs position +18 short ladef2; defect #2 abs position +20 short ladef3; defect #3 abs position +22 short laltcyl; alternate cylinder number or return cyl num +24 char lalttk; alrernate track number or return track num +25 char ldscnt; data sector count 16/20 +26 char ldatrflg; device attributes + bit 0 n/u + 1 disk is mhd + 2 n/u + 3 n/u + 4 n/u + 5 dual ported + 6/7 00 768 bytes/blk + 01 1024 bytes/blk + 10 2048 bytes/blk +27 char ldatrscnt; sectors per track (again) +28 char ldatrmhdc; MHD head count +29 char ldatrfhdc; FHD head count +30 uint32 lcrc; Label CRC-32 value + */ + +/*************************************/ +/* sector label definations 34 bytes */ +/* +0 short lcyl; cylinder number +2 char lhd; head number +3 char lsec; sec # 0-15 or 0-19 for 16/20 format +4 char lflg1; track/sector status flags + bit 0 good sec + 1 alternate sec + 2 spare sec + 3 reserved sec + 4 defective sec + 5 last sec + 6-7 n/u = 0 +5 char lflg2; + bit 0 write lock + 1 write protected + 2-7 n/u = 0 +6 short lspar1; n/u = 0 +8 short lspar2; n/u = 0 +10 short ldef1; defect #1 sec and byte position +12 short ldef2; defect #2 sec and byte position +14 short ldef3; defect #3 sec and byte position + * for track 0 write UMAP which is DMAP - 2 * SPT + * write sector number of cyl-4, hds-4, sec 0 value in 16-19 + * otherwise write zeros +16 short lspar3; n/u = 0 +18 short lspar4; n/u = 0 +20 short lspar5; n/u = 0 +22 short laltcyl; alternate cylinder number or return cyl num +24 char lalttk; alrernate track number or return track num +25 char ldscnt; data sector count 16/20 +26 char ldatrflg; device attributes + bit 0 n/u + 1 disk is mhd + 2 n/u + 3 n/u + 4 n/u + 5 dual ported + 6/7 00 768 bytes/blk + 01 1024 bytes/blk + 10 2048 bytes/blk +27 char ldatrscnt; sectors per track (again) +28 char ldatrmhdc; MHD head count +29 char ldatrfhdc; FHD head count +30 uint32 lcrc; Label CRC-32 value + */ + +/* track label / sector label definations */ +/* + 0 short lcyl; cylinder + 2 char ltkn; track + 3 char lid; sector id + 4 char lflg1; track/sector status flags + bit 0 good + 1 alternate + 2 spare + 3 reserved + 4 flaw + 5 last track + 6 start of alternate + 5 char lflg2; + 6 short lspar1; + 8 short lspar2; +10 short ldef1; +12 int ldeallp; DMAP block number trk0 +16 int lumapp; UMAP block number sec1 +20 short ladef3; +22 short laltcyl; +24 char lalttk; sectors per track +25 char ldscnt; number of heads +26 char ldatrflg; device attributes + bit 0 n/u + 1 disk is mhd + 2 n/u + 3 n/u + 4 n/u + 5 dual ported + 6/7 00 768 bytes/blk + 01 1024 bytes/blk + 10 2048 bytes/blk +27 char ldatrscnt; sectors per track (again) +28 char ldatrmhdc; MHD head count +29 char ldatrfhdc; FHD head count + */ + +#define CMD u3 +/* u3 */ +/* in u3 is device command code and status */ +#define DSK_CMDMSK 0x00ff /* Command being run */ +#define DSK_STAR 0x0100 /* STAR value in u4 */ +#define DSK_WAITING 0x0200 /* Doing NOP wait */ +#define DSK_READDONE 0x0400 /* Read finished, end channel */ +#define DSK_ENDDSK 0x0800 /* Sensed end of disk */ +#define DSK_SEEKING 0x1000 /* Disk is currently seeking */ +#define DSK_READING 0x2000 /* Disk is reading data */ +#define DSK_WRITING 0x4000 /* Disk is writing data */ +#define DSK_BUSY 0x8000 /* Disk is busy */ +/* commands */ +#define DSK_INCH 0x00 /* Initialize channel */ +#define DSK_INCH2 0xF0 /* Fake while in srv Initialize channel */ +#define DSK_WD 0x01 /* Write data */ +#define DSK_RD 0x02 /* Read data */ +#define DSK_NOP 0x03 /* No operation */ +#define DSK_SNS 0x04 /* Sense */ +#define DSK_SKC 0x07 /* Seek cylinder, track, sector */ +#define DSK_TIC 0x08 /* Transfer in channel */ +#define DSK_FMT 0x0B /* Format track */ +#define DSK_RE 0x12 /* Read express bus with ECC */ +//#define DSK_LPL 0x13 /* Lock protected label */ +#define DSK_LMR 0x1F /* Load mode register */ +#define DSK_RENO 0x22 /* Read express bus with no ECC */ +#define DSK_RES 0x23 /* Reserve */ +#define DSK_WSL 0x31 /* Write sector label */ +#define DSK_RSL 0x32 /* Read sector label */ +#define DSK_REL 0x33 /* Release */ +#define DSK_XEZ 0x37 /* Rezero */ +#define DSK_WTF 0x41 /* Write track format */ +#define DSK_RVL 0x42 /* Read vendor label */ +#define DSK_POR 0x43 /* Priority Override */ +#define DSK_IHA 0x47 /* Increment head address */ +//#define DSK_SRM 0x4F /* Set reserve track mode */ +#define DSK_WTL 0x51 /* Write track label */ +#define DSK_RTL 0x52 /* Read track label */ +//#define DSK_XRM 0x5F /* Reset reserve track mode */ +#define DSK_RAP 0xA2 /* Read angular position */ +//#define DSK_TESS 0xAB /* Test STAR (subchannel target address register) */ +#define DSK_REC 0xB2 /* Read ECC correction mask */ +#define DSK_INC 0xFF /* Initialize Controller */ + +#define DAI u4 +/* u4 holds the current disk attribute value from the INCH command */ +/* for the current drive. */ +/* Holds the current cylinder, head(track), sector */ + +/* this attribute information is provided by the INCH command */ +/* for each device and is saved. It is reconstructed from */ +/* the hsdp_t structure data for the assigned disk */ +/* +bits 0-7 - Flags + bits 0&1 - 00=Reserved, 01=MHD, 10=FHD, 11=MHD with FHD option + bit 2 - 1=Cartridge module drive + bit 3 - 0=Reserved + bit 4 - 1=Drive not present + bit 5 - 1=Dual Port + bit 6 - 0=Reserved 00 768 byte sec + bit 7 - 0=Reserved 01 1024 byte sec +bits 8-15 - sector count (sectors per track)(F16=16, F20=20) +bits 16-23 - MHD Head count (number of heads on MHD) +bits 24-31 - FHD head count (number of heads on FHD or + number head on FHD option of mini-module) +*/ + +#define SNS u5 +/* u5 */ +/* Sense byte 0 - mode register */ +#define SNS_DROFF 0x80000000 /* Drive Carriage will be offset */ +#define SNS_TRKOFF 0x40000000 /* Track offset: 0=positive, 1=negative */ +#define SNS_RDTMOFF 0x20000000 /* Read timing offset = 1 */ +#define SNS_RDSTRBT 0x10000000 /* Read strobe timing: 1=positive, 0=negative */ +#define SNS_DIAGMOD 0x08000000 /* Diagnostic Mode ECC read/write 0/1=disab;e/enable */ +#define SNS_XPBUS 0x04000000 /* Express Bus ID 0/1=enable/disable */ +#define SNS_AUTORT 0x02000000 /* Auto retry 0/1=disable/enable */ +#define SNS_DIAG 0x01000000 /* Diagnostic mode 0/1=disable/enable */ + +/* Sense byte 1 */ +#define SNS_CMDREJ 0x800000 /* Command reject */ +#define SNS_INTVENT 0x400000 /* Unit intervention required */ +#define SNS_USELE 0x200000 /* Unit Select Error */ +#define SNS_EQUCHK 0x100000 /* Equipment check */ +#define SNS_RES2 0x080000 /* Reserved */ +#define SNS_RES3 0x040000 /* Reserved */ +#define SNS_DSKFERR 0x020000 /* Disk format error */ +#define SNS_DEFTRK 0x010000 /* Defective track encountered */ + +/* Sense byte 2 */ +#define SNS_RES4 0x8000 /* Reserved */ +#define SNS_AATT 0x4000 /* At Alternate track */ +#define SNS_WPER 0x2000 /* Write protection error */ +#define SNS_WRL 0x1000 /* Write lock error */ +#define SNS_MOCK 0x0800 /* Mode check */ +#define SNS_INAD 0x0400 /* Invalid memory address */ +#define SNS_RELF 0x0200 /* Release fault */ +#define SNS_CHER 0x0100 /* Chaining error */ + +/* Sense byte 3 */ +#define SNS_REVL 0x80 /* Revolution lost */ +#define SNS_DADE 0x40 /* Disc addressing or seek error */ +#define SNS_RES5 0x20 /* Reserved */ +#define SNS_RES6 0x10 /* Reserved */ +#define SNS_ECCD 0x08 /* ECC error in data */ +#define SNS_RES7 0x04 /* Reserved */ +#define SNS_RES8 0x02 /* Reserved */ +#define SNS_UESS 0x01 /* Uncorrectable ECC error */ + +#define SNS2 us9 +/* us9 */ +/* us9 holds bytes 4 & 5 of the status for the drive */ + +/* Sense byte 4 */ +#define SNS_SEND 0x8000 /* Seek End */ +#define SNS_USEL 0x4000 /* Unit Selected */ +#define SNS_SPC0 0x2000 /* Sector Pulse Count B0 */ +#define SNS_SPC1 0x1000 /* Sector Pulse Count B1 */ +#define SNS_SPC2 0x0800 /* Sector Pulse Count B2 */ +#define SNS_SPC3 0x0400 /* Sector Pulse Count B3 */ +#define SNS_SPC4 0x0200 /* Sector Pulse Count B4 */ +#define SNS_SPC5 0x0100 /* Sector Pulse Count B5 */ + +/* Sense byte 5 */ +#define SNS_FLT 0x80 /* Disk Drive fault */ +#define SNS_SKER 0x40 /* Seek error */ +#define SNS_ONC 0x20 /* On Cylinder */ +#define SNS_UNR 0x10 /* Unit Ready */ +#define SNS_WRP 0x08 /* Write Protected */ +#define SNS_BUSY 0x04 /* Drive is busy */ +#define SNS_NU1 0x02 /* Spare 1 */ +#define SNS_NU2 0x01 /* Spare 2 */ + +#define CHS u6 +/* u6 - sector target address register (STAR) */ +/* Holds the current cylinder, head(track), sector */ +#define DISK_CYL 0xFFFF0000 /* cylinder mask */ +#define DISK_TRACK 0x0000FF00 /* track mask */ +#define DISK_SECTOR 0x000000FF /* sector mask */ + +/* Not Used up7 */ + +#define LSC us10 +/* us10 */ +/* us10 byte 0 unused */ +/* us10 byte 1 holds logical sector count from track 0 byte 25 */ + +static uint8 obuf[1024], bbuf[1024]; +static uint32 decc[512] = {0}; + +/* disk definition structure */ +struct hsdp_t +{ + const char *name; /* Device ID Name */ + uint16 nhds; /* Number of heads */ + uint16 ssiz; /* sector size in words */ + uint16 spt; /* # sectors per track(head) */ + uint16 ucyl; /* Number of cylinders used */ + uint16 cyl; /* Number of cylinders on disk */ + uint8 type; /* Device type code */ + /* bit 1 mhd */ + /* bits 6/7 = 0 768 byte blk */ /* not used on UDP/DPII */ + /* = 1 1024 byte blk */ /* not used on UDP/DPII */ +} + +hsdp_type[] = +{ + /* Class F Disc Devices */ + /* For MPX */ + {"MH040", 5, 192, 20, 407, 411, 0x40}, /* 0 411 40M XXXX */ + {"MH080", 5, 192, 20, 819, 823, 0x40}, /* 1 823 80M 8138 */ + {"MH160", 10, 192, 20, 819, 823, 0x40}, /* 2 823 160M 8148 */ + {"MH300", 19, 192, 20, 819, 823, 0x40}, /* 3 823 300M 9346 */ + {"MH337", 10, 192, 45, 819, 823, 0x40}, /* 4 823 337M 8887 DP337 */ + {"MH600", 40, 192, 20, 839, 843, 0x40}, /* 5 843 600M 8155 */ + {"MH689", 16, 192, 54, 861, 865, 0x40}, /* 6 823 674M 8888 DP689 */ + /* For UTX */ + {"9342", 5, 256, 16, 819, 823, 0x41}, /* 7 823 80M 9342 MH080 */ + {"8148", 10, 256, 16, 819, 823, 0x41}, /* 8 823 160M 8146 MH160 */ + {"9346", 19, 256, 16, 819, 823, 0x41}, /* 9 823 300M 9344 MH300 */ + {"8858", 24, 256, 16, 707, 711, 0x41}, /* 10 711 340M 8858 DC340 */ + {"8887", 10, 256, 35, 819, 823, 0x41}, /* 11 823 337M 8887 DP337 */ + {"8155", 40, 256, 16, 839, 843, 0x41}, /* 12 843 600M 8155 MH600 */ + {"8888", 16, 256, 43, 861, 865, 0x41}, /* 13 823 674M 8888 DP689 */ + {NULL} +}; + +t_stat hsdp_preio(UNIT *uptr, uint16 chan) ; +t_stat hsdp_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) ; +t_stat hsdp_haltio(UNIT *uptr); +t_stat hsdp_rsctl(UNIT *uptr); +t_stat hsdp_iocl(CHANP *chp, int32 tic_ok); +t_stat hsdp_srv(UNIT *); +t_stat hsdp_boot(int32 unitnum, DEVICE *); +void hsdp_ini(UNIT *, t_bool); +t_stat hsdp_reset(DEVICE *); +t_stat hsdp_attach(UNIT *, CONST char *); +t_stat hsdp_detach(UNIT *); +t_stat hsdp_set_type(UNIT * uptr, int32 val, CONST char *cptr, void *desc); +t_stat hsdp_get_type(FILE * st, UNIT * uptr, int32 v, CONST void *desc); +t_stat hsdp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *hsdp_description (DEVICE *dptr); +extern uint32 inbusy; +extern uint32 outbusy; +extern uint32 readfull(CHANP *chp, uint32 maddr, uint32 *word); +extern int irq_pend; /* go scan for pending int or I/O */ +extern UNIT itm_unit; +extern uint32 PSD[]; /* PSD */ +extern uint32 cont_chan(uint16 chsa); + +/* channel program information */ +CHANP dpa_chp[NUM_UNITS_HSDP] = {0}; +/* IOCL queue */ +IOCLQ dpa_ioclq[NUM_UNITS_HSDP] = {0}; + +#define TRK_CACHE 10 +/* track label queue */ +struct _trk_data +{ + int32 age; + uint32 track; + uint8 label[30]; +}; + +struct _trk_label +{ + struct _trk_data tkl[TRK_CACHE]; +}; + +static struct _trk_label tkl_label[NUM_UNITS_HSDP] = {0}; + +MTAB hsdp_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "TYPE", "TYPE", + &hsdp_set_type, &hsdp_get_type, NULL, "Type of disk"}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, + &show_dev_addr, NULL, "Device channel address"}, + {0}, +}; + +UNIT dpa_unit[] = { +/* SET_TYPE(10) 8887 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(10), 0), 0, UNIT_ADDR(0x800)}, /* 0 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(10), 0), 0, UNIT_ADDR(0x802)}, /* 1 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(10), 0), 0, UNIT_ADDR(0x804)}, /* 2 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(10), 0), 0, UNIT_ADDR(0x806)}, /* 3 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(10), 0), 0, UNIT_ADDR(0x808)}, /* 4 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(10), 0), 0, UNIT_ADDR(0x80A)}, /* 5 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(10), 0), 0, UNIT_ADDR(0x80C)}, /* 6 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(10), 0), 0, UNIT_ADDR(0x80E)}, /* 7 */ +}; + +DIB dpa_dib = { + hsdp_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + hsdp_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + hsdp_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + hsdp_haltio, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + hsdp_rsctl, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + NULL, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + hsdp_iocl, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + hsdp_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + dpa_unit, /* UNIT* units */ /* Pointer to units structure */ + dpa_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + dpa_ioclq, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_UNITS_HSDP, /* uint8 numunits */ /* number of units defined */ + 0x0F, /* uint8 mask */ /* 8 devices - device mask */ + 0x0800, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +DEVICE dpa_dev = { + "DPA", dpa_unit, NULL, hsdp_mod, + NUM_UNITS_HSDP, 16, 24, 4, 16, 32, + NULL, NULL, &hsdp_reset, &hsdp_boot, &hsdp_attach, &hsdp_detach, + /* ctxt is the DIB pointer */ + &dpa_dib, DEV_DISABLE|DEV_DEBUG|DEV_DIS, 0, dev_debug, + NULL, NULL, &hsdp_help, NULL, NULL, &hsdp_description +}; + +#if NUM_DEVS_HSDP > 1 +/* channel program information */ +CHANP dpb_chp[NUM_UNITS_HSDP] = {0}; +/* IOCL queue */ +IOCPQ dpb_ioclq[NUM_UNITS_HSDP] = {0}; + +UNIT dpb_unit[] = { +/* SET_TYPE(3) DM300 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC00)}, /* 0 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC02)}, /* 1 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC04)}, /* 2 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC06)}, /* 3 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC08)}, /* 4 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC0A)}, /* 5 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC0C)}, /* 6 */ + {UDATA(&hsdp_srv, UNIT_HSDP|SET_TYPE(3), 0), 0, UNIT_ADDR(0xC0E)}, /* 7 */ +}; + + +DIB dpb_dib = { + hsdp_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + hsdp_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + hsdp_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + hsdp_haltio, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + hsdp_rsctl, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + NULL, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + hsdp_iocl, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + hsdp_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + dpb_unit, /* UNIT* units */ /* Pointer to units structure */ + dpb_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + dpb_ioclq, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_UNITS_HSDP, /* uint8 numunits */ /* number of units defined */ + 0x0F, /* uint8 mask */ /* 8 devices - device mask */ + 0x0C00, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +DEVICE dpb_dev = { + "DPB", dpb_unit, NULL, hsdp_mod, + NUM_UNITS_HSDP, 16, 24, 4, 16, 32, + NULL, NULL, &hsdp_reset, &hsdp_boot, &hsdp_attach, &hsdp_detach, + /* ctxt is the DIB pointer */ + &dpb_dib, DEV_DISABLE|DEV_DEBUG|DEV_DIS, 0, dev_debug, + NULL, NULL, &hsdp_help, NULL, NULL, &hsdp_description +}; +#endif + +uint32 dple_ecc32(uint8 *str, int32 len) +{ + int i, j; + uint32 ch, ecc = 0; + uint32 pmask = 0x7e11f439; /* SEL LE poly mask */ + + ecc = (~ecc & MASK32); /* initialize ecc to all bits (~0) */ + for (j=0; j>= 1; /* just shift out the bit */ + ecc ^= pmask; /* eor with poly mask */ + } else + ecc >>= 1; /* just shift out the bit */ + ch >>= 1; /* next bit */ + } + } + return (~ecc & MASK32); /* return ecc value */ +} + +uint32 dpbe_ecc32(uint8 *str, int32 len) +{ + int i, j; + uint32 ch, ecc = 0; + uint32 pmask = 0x9C2F887E; /* SEL BE poly mask */ + + ecc = (~ecc & MASK32); /* initialize ecc to all bits (~0) */ + for (j=0; jflags); + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); /* get the UNIT number */ + int len, i, cn, found = -1; + + /* zero the Track Label Buffer */ + for (i = 0; i < 30; i++) + buf[i] = 0; + + /* get file offset in sectors */ + tstart = STAR2SEC(star, SPT(type), SPC(type)); + /* convert sector number back to chs value to sync disk for diags */ + nstar = hsdpsec2star(tstart, type); + + cyl = (nstar >> 16) & 0xffff; /* get the cylinder */ + trk = (nstar >> 8) & 0xff; /* get the track */ + sec = nstar & 0xff; /* save sec if any */ + + /* get track number */ + tstart = (cyl * HDS(type)) + trk; + sim_debug(DEBUG_EXP, dptr, "get_dpatrk RTL cyl %4x(%d) trk %x ec# %06x\n", + cyl, cyl, trk, tstart); + + /* calc offset in file to track label */ + offset = CAPB(type) + (tstart * 30); + + /* see if track label is in cache */ + for (cn=0; cnfileref, offset, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "get_dpatrk RTL, Error on seek to %04x\n", offset); + return 0; + } + + /* read in a track label from disk */ + if ((len=sim_fread(buf, 1, 30, uptr->fileref)) != 30) { + sim_debug(DEBUG_CMD, dptr, + "get_dpatrk Error %08x on read %04x of diskfile cyl %04x hds %02x sec 00\n", + len, 30, cyl, trk); + return 0; + } + } + + /* now write track label data to log */ + sim_debug(DEBUG_CMD, dptr, "Dpatrk %08x label", nstar); + for (i = 0; i < 30; i++) { + if (i == 16) + sim_debug(DEBUG_CMD, dptr, "\nDpatrl %08x label", nstar); + sim_debug(DEBUG_CMD, dptr, " %02x", buf[i]); + } + sim_debug(DEBUG_CMD, dptr, "\n"); + + if (buf[4] == 0x08) { /* see if defective track */ + uptr->SNS |= SNS_DEFTRK; /* flag as defective */ + tstart = nstar; /* save orginal track */ + /* get the alternate track address */ + cyl = (buf[22] << 8) | buf[23]; /* get the cylinder */ + trk = buf[24]; /* get the track */ + nstar = CHS2STAR(cyl, trk, sec); + sim_debug(DEBUG_CMD, dptr, + "Track %08x is defective, new track %08x\n", tstart, nstar); + } + /* see if we had it in our cache */ + if (found == -1) { + /* not in our cache, save the new track label */ + int32 na = 0; + for (cn=0; cn na) + continue; /* older */ + /* this is less used, so replace it */ + na = cn; + } + /* use na entry */ + for (i=0; i<30; i++) + tkl_label[unit].tkl[na].label[i] = buf[i]; + tkl_label[unit].tkl[na].age = 1; + tkl_label[unit].tkl[cn].track = offset; + } + return nstar; /* return track address */ +} + +/* start a disk operation */ +t_stat hsdp_preio(UNIT *uptr, uint16 chan) +{ + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + int unit = (uptr - dptr->units); /* get the UNIT number */ + DIB* dibp = (DIB *)dptr->ctxt; /* get the DIB pointer */ + int32 cnt; + + sim_debug(DEBUG_CMD, dptr, "hsdp_preio CMD %08x unit %02x\n", uptr->CMD, unit); + if ((cnt = IOCLQ_Num(&dibp->ioclq_ptr[unit])) >= (IOCLQ_SIZE)) { + sim_debug(DEBUG_CMD, dptr, "hsdp_preio CMD %08x unit %02x IOCLQ cnt %02x Full\n", + uptr->CMD, unit, cnt); + return SNS_BSY; /* IOCLQ is full, return busy */ + } + if ((uptr->CMD & 0xff) != 0) { /* just return if busy */ + sim_debug(DEBUG_CMD, dptr, "hsdp_preio CMD %08x unit %02x IOCLQ cnt %02x Busy\n", + uptr->CMD, unit, cnt); + return SNS_SMS; /* busy, but IOCLQ is not full */ + } + + sim_debug(DEBUG_CMD, dptr, "hsdp_preio unit %02x chsa %04x OK not busy\n", unit, chsa); + return SCPE_OK; /* not busy and IOCLQ not full */ +} + +/* load in the IOCD and process the commands */ +/* return = 0 OK */ +/* return = 1 error, chan_status will have reason */ +t_stat hsdp_iocl(CHANP *chp, int32 tic_ok) +{ + uint32 word1 = 0; + uint32 word2 = 0; + int32 docmd = 0; + UNIT *uptr = chp->unitptr; /* get the unit ptr */ + uint16 chan = get_chan(chp->chan_dev); /* our channel */ + uint16 chsa = chp->chan_dev; /* our chan/sa */ + uint16 devstat = 0; + DEVICE *dptr = get_dev(uptr); + + /* check for valid iocd address if 1st iocd */ + if (chp->chan_info & INFO_SIOCD) { /* see if 1st IOCD in channel prog */ + if (chp->chan_caw & 0x3) { /* must be word bounded */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_iocl iocd bad address chsa %02x caw %06x\n", + chsa, chp->chan_caw); + chp->ccw_addr = chp->chan_caw; /* set the bad iocl address */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid iocd addr */ + uptr->SNS |= SNS_INAD; /* invalid address status */ + return 1; /* error return */ + } + } +loop: + sim_debug(DEBUG_EXP, dptr, + "hsdp_iocl @%06x entry chan_status[%04x] %04x SNS %08x\n", + chp->chan_caw, chan, chp->chan_status, uptr->SNS); + + /* Abort if we have any errors */ + if (chp->chan_status & STATUS_ERROR) { /* check channel error status */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_iocl ERROR1 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* return error */ + } + + /* Read in first CCW */ + if (readfull(chp, chp->chan_caw, &word1) != 0) { /* read word1 from memory */ + chp->chan_status |= STATUS_PCHK; /* memory read error, program check */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_iocl ERROR2 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* Read in second CCW */ + if (readfull(chp, chp->chan_caw+4, &word2) != 0) { /* read word2 from memory */ + chp->chan_status |= STATUS_PCHK; /* memory read error, program check */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_iocl ERROR3 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + sim_debug(DEBUG_CMD, dptr, + "hsdp_iocl @%06x read ccw chan %02x IOCD wd 1 %08x wd 2 %08x SNS %08x\n", + chp->chan_caw, chan, word1, word2, uptr->SNS); + + chp->chan_caw = (chp->chan_caw & 0xfffffc) + 8; /* point to next IOCD */ + + /* Check if we had data chaining in previous iocd */ + /* if we did, use previous cmd value */ + if (((chp->chan_info & INFO_SIOCD) == 0) && /* see if 1st IOCD in channel prog */ + (chp->ccw_flags & FLAG_DC)) { /* last IOCD have DC set? */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_iocl @%06x DO DC, ccw_flags %04x cmd %02x\n", + chp->chan_caw, chp->ccw_flags, chp->ccw_cmd); + } else + chp->ccw_cmd = (word1 >> 24) & 0xff; /* set new command from IOCD wd 1 */ + + if (!MEM_ADDR_OK(word1 & MASK24)) { /* see if memory address invalid */ + chp->chan_status |= STATUS_PCHK; /* bad, program check */ + uptr->SNS |= SNS_INAD; /* invalid address status */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_iocl bad IOCD1 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + chp->ccw_count = word2 & 0xffff; /* get 16 bit byte count from IOCD WD 2 */ + + /* validate the commands for the disk */ + switch (chp->ccw_cmd) { + case DSK_WD: case DSK_RD: case DSK_INCH: case DSK_NOP: case DSK_INC: + case DSK_SKC: case DSK_XEZ: case DSK_LMR: case DSK_WSL: case DSK_RSL: + case DSK_IHA: case DSK_WTL: case DSK_RTL: case DSK_RAP: case DSK_WTF: + case DSK_FMT: case DSK_RE: case DSK_RENO: case DSK_REL: case DSK_RES: + case DSK_RVL: case DSK_POR: case DSK_REC: case DSK_TIC: + case DSK_SNS: + break; + default: + chp->chan_status |= STATUS_PCHK; /* program check for invalid cmd */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_iocl bad cmd %02x chan_status[%04x] %04x SNS %08x\n", + chp->ccw_cmd, chan, chp->chan_status, uptr->SNS); + return 1; /* error return */ + } + + if (chp->chan_info & INFO_SIOCD) { /* see if 1st IOCD in channel prog */ + /* 1st command can not be a TIC */ + if (chp->ccw_cmd == CMD_TIC) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected status */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_iocl TIC bad cmd chan_status[%04x] %04x\n", + chan, chp->chan_status); + return 1; /* error return */ + } + } + + /* TIC can't follow TIC or be first in command chain */ + /* diags send bad commands for testing. Use all of op */ + if (chp->ccw_cmd == CMD_TIC) { + if (tic_ok) { + if (((word1 & MASK24) == 0) || (word1 & 0x3)) { + sim_debug(DEBUG_EXP, dptr, + "hsdp_iocl tic cmd bad address chan %02x tic caw %06x IOCD wd 1 %08x\n", + chan, chp->chan_caw, word1); + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected status */ + uptr->SNS |= SNS_INAD; /* invalid address status */ + return 1; /* error return */ + } + tic_ok = 0; /* another tic not allowed */ + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_iocl tic cmd ccw chan %02x tic caw %06x IOCD wd 1 %08x\n", + chan, chp->chan_caw, word1); + goto loop; /* restart the IOCD processing */ + } + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected status */ + if (((word1 & MASK24) == 0) || (word1 & 0x3)) + uptr->SNS |= SNS_INAD; /* invalid address status */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_iocl TIC ERROR chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* Check if we had data chaining in previous iocd */ + if ((chp->chan_info & INFO_SIOCD) || /* see if 1st IOCD in channel prog */ + (((chp->chan_info & INFO_SIOCD) == 0) && /* see if 1st IOCD in channel prog */ + ((chp->ccw_flags & FLAG_DC) == 0))) { /* last IOCD have DC set? */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_iocl @%06x DO CMD No DC, ccw_flags %04x cmd %02x\n", + chp->chan_caw, chp->ccw_flags, chp->ccw_cmd); + docmd = 1; /* show we have a command */ + } + + /* Set up for this command */ + chp->ccw_flags = (word2 >> 16) & 0xf000; /* get flags from bits 0-3 of WD 2 of IOCD */ + chp->chan_status = 0; /* clear status for next IOCD */ + /* make a 24 bit address */ + chp->ccw_addr = word1 & MASK24; /* set the data/seek address */ + + /* validate parts of IOCD2 that are reserved */ + if (word2 & 0x0fff0000) { /* bits 4-15 must be zero */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid iocd */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_iocl IOCD2 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* DC can only be used with a read/write cmd */ + if (chp->ccw_flags & FLAG_DC) { + if ((chp->ccw_cmd != DSK_RD) && (chp->ccw_cmd != DSK_WD)) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid DC */ + uptr->SNS |= SNS_CHER; /* chaining error */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_iocl DC ERROR chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + } + + chp->chan_byte = BUFF_BUSY; /* busy & no bytes transferred yet */ + + sim_debug(DEBUG_XIO, dptr, + "hsdp_iocl @%06x read docmd %01x addr %06x count %04x chan %04x ccw_flags %04x\n", + chp->chan_caw, docmd, chp->ccw_addr, chp->ccw_count, chan, chp->ccw_flags); + + if (docmd) { /* see if we need to process a command */ + DIB *dibp = dib_unit[chp->chan_dev]; /* get the DIB pointer */ + + uptr = chp->unitptr; /* get the unit ptr */ + if (dibp == 0 || uptr == 0) { + chp->chan_status |= STATUS_PCHK; /* program check if it is */ + return 1; /* if none, error */ + } + + sim_debug(DEBUG_XIO, dptr, + "hsdp_iocl @%06x before start_cmd chan %04x status %04x count %04x SNS %08x\n", + chp->chan_caw, chan, chp->chan_status, chp->ccw_count, uptr->u5); + + /* call the device startcmd function to process the current command */ + /* just replace device status bits */ + chp->chan_info &= ~INFO_CEND; /* show chan_end not called yet */ + devstat = dibp->start_cmd(uptr, chan, chp->ccw_cmd); + chp->chan_status = (chp->chan_status & 0xff00) | devstat; + chp->chan_info &= ~INFO_SIOCD; /* show not first IOCD in channel prog */ + + sim_debug(DEBUG_XIO, dptr, + "hsdp_iocl @%06x after start_cmd chan %04x status %08x count %04x\n", + chp->chan_caw, chan, chp->chan_status, chp->ccw_count); + + /* see if bad status */ + if (chp->chan_status & (STATUS_ATTN|STATUS_ERROR)) { + chp->chan_status |= STATUS_CEND; /* channel end status */ + chp->ccw_flags = 0; /* no flags */ + chp->chan_byte = BUFF_NEXT; /* have main pick us up */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_iocl bad status chsa %04x status %04x cmd %02x\n", + chsa, chp->chan_status, chp->ccw_cmd); + /* done with command */ + sim_debug(DEBUG_EXP, &cpu_dev, + "hsdp_iocl ERROR return chsa %04x status %08x\n", + chp->chan_dev, chp->chan_status); + return 1; /* error return */ + } else + + /* NOTE this code needed for MPX 1.X to run! */ + /* see if command completed */ + /* we have good status */ + if (chp->chan_status & (STATUS_DEND|STATUS_CEND)) { + uint16 chsa = GET_UADDR(uptr->u3); /* get channel & sub address */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* show I/O complete */ + sim_debug(DEBUG_XIO, dptr, + "hsdp_iocl @%06x FIFO #%1x cmd complete chan %04x status %04x count %04x\n", + chp->chan_caw, FIFO_Num(chsa), chan, chp->chan_status, chp->ccw_count); + } + } + /* the device processor returned OK (0), so wait for I/O to complete */ + /* nothing happening, so return */ + sim_debug(DEBUG_XIO, dptr, + "hsdp_iocl @%06x return, chan %04x status %04x count %04x irq_pend %1x\n", + chp->chan_caw, chan, chp->chan_status, chp->ccw_count, irq_pend); + return 0; /* good return */ +} + +t_stat hsdp_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) +{ + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + int32 unit = (uptr - dptr->units); + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + sim_debug(DEBUG_CMD, dptr, + "hsdp_startcmd chsa %04x unit %02x cmd %02x CMD %08x\n", + chsa, unit, cmd, uptr->CMD); + if ((uptr->flags & UNIT_ATT) == 0) { /* unit attached status */ + sim_debug(DEBUG_CMD, dptr, "hsdp_startcmd unit %02x not attached\n", unit); + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + if (cmd != DSK_SNS) /* we are completed with unit check status */ + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + } + + if ((uptr->CMD & DSK_CMDMSK) != 0) { + sim_debug(DEBUG_CMD, dptr, "hsdp_startcmd unit %02x busy\n", unit); + uptr->CMD |= DSK_BUSY; /* Flag we are busy */ + return SNS_BSY; + } + uptr->SNS2 |= SNS_USEL; /* unit selected */ + sim_debug(DEBUG_CMD, dptr, "hsdp_startcmd CMD continue unit=%02x cmd %02x\n", unit, cmd); + + /* Unit is online, so process a command */ + switch (cmd) { + + case DSK_INCH: /* INCH 0x0 */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_startcmd starting INCH %06x cmd, chsa %04x MemBuf %08x cnt %04x\n", + uptr->u4, chsa, chp->ccw_addr, chp->ccw_count); + + uptr->SNS &= ~SNS_CMDREJ; /* not rejected yet */ + uptr->CMD |= DSK_INCH2; /* use 0xF0 for inch, just need int */ +#ifdef FAST_FOR_UTX +// sim_activate(uptr, 20); /* start things off */ + sim_activate(uptr, 30); /* start things off */ +#else + sim_activate(uptr, 250); /* start things off */ +// sim_activate(uptr, 500); /* start things off */ +#endif + return SCPE_OK; /* good to go */ + break; + + case DSK_INC: /* 0xFF Initialize controller */ + if ((cmd == DSK_INC) && + (chp->ccw_count != 0x20)) /* count must be 32 to be valid */ + break; + case DSK_NOP: /* NOP 0x03 */ + case DSK_SKC: /* Seek command 0x07 */ + case DSK_XEZ: /* Rezero & Read IPL record 0x1f */ + case DSK_WD: /* Write command 0x01 */ + case DSK_RD: /* Read command 0x02 */ + case DSK_LMR: /* read mode register */ + case DSK_WSL: /* WSL 0x31 */ + case DSK_RSL: /* RSL 0x32 */ + case DSK_IHA: /* 0x47 Increment head address */ + case DSK_WTL: /* WTL 0x51 */ + case DSK_RTL: /* RTL 0x52 */ + case DSK_RVL: /* 0x42 Read vendor label */ + case DSK_WTF: /* 0x41 Write track format */ + case DSK_RAP: /* 0xA2 Read angular positions */ + case DSK_FMT: /* 0x0B Format for no skip */ + case DSK_RES: /* 0x23 Reserve */ + case DSK_REL: /* 0x33 Release */ + uptr->SNS &= ~MASK24; /* clear data & leave mode */ + uptr->SNS2 = (SNS_UNR|SNS_ONC|SNS_USEL);/* reset status to on cyl & ready */ + case DSK_SNS: /* Sense 0x04 */ + uptr->CMD |= cmd; /* save cmd */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_startcmd starting disk cmd %02x chsa %04x\n", + cmd, chsa); +#ifdef FAST_FOR_UTX +// sim_activate(uptr, 20); /* start things off */ +// sim_activate(uptr, 30); /* start things off */ + sim_activate(uptr, 25); /* start things off */ +#else + sim_activate(uptr, 250); /* start things off */ +// sim_activate(uptr, 500); /* start things off */ +#endif + return SCPE_OK; /* good to go */ + break; + + } + + sim_debug(DEBUG_CMD, dptr, + "hsdp_startcmd done with hsdp_startcmd %02x chsa %04x SNS %08x\n", + cmd, chsa, uptr->SNS); + /* diags want the chan addr to point at bad command?? */ + chp->chan_caw -= 8; /* backup iocd address for diags */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected */ + return SNS_CHNEND|SNS_DEVEND|STATUS_PCHK; /* return error */ +} + +/* Handle haltio transfers for disk */ +t_stat hsdp_haltio(UNIT *uptr) { + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + int cmd = uptr->CMD & DSK_CMDMSK; + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + sim_debug(DEBUG_EXP, dptr, "hsdp_haltio enter chsa %04x cmd = %02x\n", chsa, cmd); + + /* terminate any input command */ + /* UTX wants SLI bit, but no unit exception */ + /* status must not have an error bit set */ + /* otherwise, UTX will panic with "bad status" */ + /* stop any I/O and post status and return error status */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_haltio HIO I/O stop chsa %04x cmd = %02x\n", chsa, cmd); + if ((uptr->CMD & DSK_CMDMSK) != 0) { /* is unit busy */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_haltio HIO chsa %04x cmd = %02x ccw_count %02x\n", chsa, cmd, chp->ccw_count); + sim_cancel(uptr); /* clear the input timer */ + chp->ccw_count = 0; /* zero the count */ + chp->chan_caw = 0; /* zero iocd address for diags */ + chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* stop any chaining */ + uptr->CMD &= LMASK; /* make non-busy */ + uptr->SNS2 |= (SNS_ONC|SNS_UNR); /* on cylinder & ready */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* force end */ + return CC2BIT | SCPE_IOERR; /* busy return */ + } else { + sim_debug(DEBUG_CMD, dptr, + "hsdp_haltio HIO I/O not busy chsa %04x cmd = %02x\n", chsa, cmd); + uptr->CMD &= LMASK; /* make non-busy */ + uptr->SNS2 |= (SNS_ONC|SNS_UNR); /* on cylinder & ready */ + return CC1BIT | SCPE_OK; /* not busy */ + } +} + +/* Handle rsctl command for disk */ +t_stat hsdp_rsctl(UNIT *uptr) { + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & DSK_CMDMSK; + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + if ((uptr->CMD & DSK_CMDMSK) != 0) { /* is unit busy */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_rsctl RSCTL chsa %04x cmd %02x ccw_count %02x\n", chsa, cmd, chp->ccw_count); + sim_cancel(uptr); /* clear the input timer */ + chp->ccw_count = 0; /* zero the count */ + chp->chan_caw = 0; /* zero iocd address for diags */ + chp->ccw_flags &= ~(FLAG_DC|FLAG_CC);/* stop any chaining */ + } + uptr->CMD &= LMASK; /* make non-busy */ + uptr->SNS2 |= (SNS_ONC|SNS_UNR); /* on cylinder & ready */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_rsctl RSCTL I/O not busy chsa %04x cmd %02x\n", chsa, cmd); + return SCPE_OK; /* not busy */ +} + +/* Handle processing of hsdp requests. */ +t_stat hsdp_srv(UNIT *uptr) +{ + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */ + int cmd = uptr->CMD & DSK_CMDMSK; + int type = GET_TYPE(uptr->flags); + uint32 tcyl=0, trk=0, cyl=0, sec=0, tempt=0; + int unit = (uptr - dptr->units); + int len = chp->ccw_count; + int i,j,k; + uint32 mema, ecc, cecc, tstar; /* memory address */ + uint8 ch; + uint16 ssize = hsdp_type[type].ssiz * 4; /* disk sector size in bytes */ + uint32 tstart; + uint8 lbuf[32]; + uint8 buf2[1024]; + uint8 buf[1024]; + + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv entry unit %02x CMD %08x chsa %04x count %04x %x/%x/%x \n", + unit, uptr->CMD, chsa, chp->ccw_count, + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + + if ((uptr->flags & UNIT_ATT) == 0) { /* unit attached status */ + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + if (cmd != DSK_SNS) { /* we are completed with unit check status */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + } + + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv cmd=%02x chsa %04x count %04x\n", cmd, chsa, chp->ccw_count); + +#ifdef FOR_TESTING_DIAGS + /* see if mode reg is 1 (diag mode), if so we are done */ + if ((uptr->SNS >> 24) & 1) { /* get mode value */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chp->ccw_count = 0; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } +#endif + + switch (cmd) { + case 0: /* No command, stop disk */ + break; + + case DSK_INC: /* 0xFF Initialize controller */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + len = chp->ccw_count; /* INCH command count */ + mema = chp->ccw_addr; /* get inch or buffer addr */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv cmd CONT INC %06x chsa %04x addr %06x count %04x completed\n", + chp->chan_inch_addr, chsa, mema, chp->ccw_count); + /* to use this inch method, byte count must be 0x20 */ + if (len != 0x20) { + /* we have invalid count, error, bail out */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + /* read all 32 bytes, stopping every 4 bytes to make words */ + /* the 8 words have drive data for each unit */ + /* WARNING 8 drives must be defined for this controller */ + /* so we will not have a map fault */ + sim_debug(DEBUG_CMD, dptr, "hsdp_srv CONT INC data:"); + for (i=0; i < 32; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (i == 16) + sim_debug(DEBUG_CMD, dptr, "\nhsdp_srv CONT INC data:"); + sim_debug(DEBUG_CMD, dptr, " %02x", buf[i]); + if (((i+1)%4) == 0) { /* see if we have a word yet */ +#ifndef FOR_TESTING + int dn = i/4; /* get drive number */ +// UNIT *up = uptr0[dn]; /* get our unit pointer */ + UNIT *uptr0 = dptr->units; /* get unit 0 pointer */ +#endif + /* drive attribute registers */ + /* may want to use this later */ + /* clear warning errors */ + tstart = (buf[i-3]<<24) | (buf[i-2]<<16) + | (buf[i-1]<<8) | (buf[i]); +#ifndef FOR_TESTING + uptr0[dn].DAI = tstart; /* save drive attribute register */ + /* set mode data from last byte */ + uptr0[dn].SNS &= MASK24; /* clear old mode data */ + uptr0[dn].SNS |= (buf[i] << 24); /* save mode value */ +#endif + } + } + sim_debug(DEBUG_CMD, dptr, "\n"); + + uptr->CMD &= LMASK; /* remove old cmd */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv cmd INC chsa %04x chsa %06x count %04x mode %08x completed\n", + chsa, mema, chp->ccw_count, uptr->DAI); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_INCH2: /* use 0xF0 for inch, just need int */ + len = chp->ccw_count; /* INCH command count */ + mema = chp->ccw_addr; /* get inch or buffer addr */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv starting INCH %06x cmd, chsa %04x MemBuf %06x cnt %04x\n", + chp->chan_inch_addr, chsa, chp->ccw_addr, chp->ccw_count); + + /* mema has IOCD word 1 contents. For the disk processor it contains */ + /* a pointer to the INCH buffer followed by 8 drive attribute words that */ + /* contains the flags, sector count, MHD head count, and FHD count */ + /* len has the byte count from IOCD wd2 and should be 0x24 (36) */ + /* the INCH buffer address must be set for the parent channel as well */ + /* as all other devices on the channel. Call set_inch() to do this for us */ + /* just return OK and channel software will use u4 as status buffer addr */ + + if (len != 36) { + /* we have invalid count, error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* read all 36 bytes, stopping every 4 bytes to make words */ + /* the first word has the inch buffer address */ + /* the next 8 words have drive data for each unit */ + /* WARNING 8 drives must be defined for this controller */ + /* so we will not have a map fault */ + for (i=0; i < 36; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (((i+1)%4) == 0) { /* see if we have a word yet */ + if (i == 3) { + /* inch buffer address */ + mema = (buf[0]<<24) | (buf[1]<<16) | + (buf[2]<<8) | (buf[3]); + sim_debug(DEBUG_CMD, dptr, "Inch buffer %08x", mema); + } else { +#ifndef FOR_TESTING + int dn = (i-4)/4; /* get drive number */ + UNIT *uptr0 = dptr->units; /* get unit 0 pointer */ +#endif + /* drive attribute registers */ + /* may want to use this later */ + /* clear warning errors */ + tstart = (buf[i-3]<<24) | (buf[i-2]<<16) + | (buf[i-1]<<8) | (buf[i]); + if (i == 23) + sim_debug(DEBUG_CMD, dptr, "\nInch buffer %08x", mema); +#ifndef FOR_TESTING + uptr0[dn].DAI = tstart; /* save drive attribute register */ + /* set mode data from last byte */ + uptr0[dn].SNS &= MASK24; /* clear old mode data */ + uptr0[dn].SNS |= (buf[i] << 24); /* save mode value */ +#endif + sim_debug(DEBUG_CMD, dptr, " %08x", tstart); + } + } + } + sim_debug(DEBUG_CMD, dptr, "\n"); + + /* now call set_inch() function to write and test inch buffer addresses */ + /* 1-224 wd buffer is provided, status is 128 words offset from start */ + mema += (128*4); /* offset to inch buffers */ + i = set_inch(uptr, mema, 33); /* new address of 33 entries */ + if ((i == SCPE_MEM) || (i == SCPE_ARG)) { /* any error */ + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + uptr->CMD &= LMASK; /* remove old cmd */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv cmd INCH %06x chsa %04x addr %06x count %04x mode %08x completed\n", + chp->chan_inch_addr, chsa, mema, chp->ccw_count, tcyl); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_NOP: /* NOP 0x03 */ + if ((uptr->CMD & DSK_WAITING) == 0) { + /* Do a fake wait to kill some time */ + uptr->CMD |= DSK_WAITING; /* show waiting for NOP */ + sim_debug(DEBUG_CMD, dptr, "hsdp_srv cmd NOP stalling for 50 cnts\n"); +// sim_activate(uptr, 250); /* start waiting */ +// sim_activate(uptr, 50); /* start waiting */ + sim_activate(uptr, 350); /* start waiting */ + break; + } + /* NOP drop through after wait */ + case DSK_RES: /* 0x23 Reserve */ + case DSK_REL: /* 0x33 Release */ + uptr->CMD &= LMASK; /* remove old cmd */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv cmd NOP chsa %04x count %04x completed\n", + chsa, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_RAP: /* 0xA2 Read angular positions */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + /* get STAR (target sector) data in STAR */ + cyl = STAR2CYL(uptr->CHS); /* get current cyl */ + trk = (uptr->CHS >> 8) & 0xff; /* get trk/head */ + sec = uptr->CHS & 0xff; /* set sec */ + +// ch = ((sec * 2) % SPT(type)) & 0x3f; /* get index cnt */ + ch = ((2*SPT(type))-1) & 0x3f; /* get index cnt */ + uptr->SNS2 = (uptr->SNS2 & 0xc0ff) | ((((uint32)ch) & 0x3f) << 8); + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv RAP %02x cyl %04x trk %02x sec %02x\n", + ch, cyl&0xffff, trk, sec); + + if (chan_write_byte(chsa, &ch)) { /* put a byte to memory */ + sim_debug(DEBUG_CMD, dptr, + "HSDP RAP %02x for addr /%04x/%02x/%02x\n", + ch, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + if (chp->chan_status & STATUS_PCHK) { /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + } else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + } + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_IHA: /* 0x47 Increment head address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + /* get STAR (target sector) data in STAR */ + cyl = STAR2CYL(uptr->CHS); /* get current cyl */ + trk = (uptr->CHS >> 8) & 0xff; /* get trk/head */ + sec = 0; /* set sec to zero for this head */ + + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv IHA cyl %04x trk %02x sec %02x unit=%02x\n", + cyl&0xffff, trk, sec, unit); + + /* Check if head increment valid */ + trk += 1; /* increment the head # */ + if (trk >= hsdp_type[type].nhds) { /* see if too big */ + trk = 0; /* back to trk 0 */ + cyl += 1; /* next cylinder */ + if (cyl >= hsdp_type[type].cyl) { /* see if too big */ + /* set new STAR value using new values */ + uptr->CHS = CHS2STAR(cyl, trk, sec); + sim_debug(DEBUG_EXP, dptr, + "hsdp_srv IHA ERROR cyl %04x trk %02x sec %02x unit=%02x\n", + cyl, trk, sec, unit); + uptr->SNS |= SNS_DADE; /* set error status */ + uptr->SNS2 |= (SNS_SKER|SNS_SEND); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); /* error */ + break; + } + } + + /* set new STAR value using new values */ + uptr->CHS = CHS2STAR(cyl, trk, sec); + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv IHA unit=%02x STAR %08x %04x/%02x/%02x\n", + unit, uptr->CHS, cyl, trk, sec); + /* get alternate track if this one is defective */ +//sim_debug(DEBUG_CMD, dptr, "Dpatrk1 %08x label\n", uptr->CHS); + tempt = get_dpatrk(uptr, uptr->CHS, lbuf); + /* file offset in bytes to std or alt track */ + tstart = STAR2SEC(tempt, SPT(type), SPC(type)) * SSB(type); + + if ((tempt == 0) && (uptr->CHS != 0)) { + /* we have error */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_srv IHA get_dpatrk return error tempt %06x tstart %06x CHS %08x\n", + tempt, tstart, uptr->CHS); + goto iha_error; + } + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ +iha_error: + uptr->SNS |= SNS_DADE; /* set error status */ + uptr->SNS2 |= (SNS_SKER|SNS_SEND); + sim_debug(DEBUG_EXP, dptr, "hsdp_srv IHA error on seek to %04x\n", tstart); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_REC: /* 0xB2 */ /* Read ECC correction code */ + sim_debug(DEBUG_CMD, dptr, "hsdp_startcmd CMD REC Read ECC\n"); + /* count must be 4, if not prog check */ + if (len != 4) { + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv REC bad count unit=%02x count%04x CHS %08x\n", + unit, len, uptr->CHS); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK|STATUS_LENGTH); + break; + } + /* create offset and mask */ + ecc = dple_ecc32(obuf, ssize); /* calc ecc for original sector */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv DEC old obuf data %02x%02x%02x%02x %02x%02x%02x%02x\n", + obuf[1016], obuf[1017], obuf[1018], obuf[1019], + obuf[1020], obuf[1021], obuf[1022], obuf[1023]); + cecc = dple_ecc32(bbuf, ssize); /* calc ecc for bad sector */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv DEC bad bbuf data %02x%02x%02x%02x %02x%02x%02x%02x\n", + bbuf[1016], bbuf[1017], bbuf[1018], bbuf[1019], + bbuf[1020], bbuf[1021], bbuf[1022], bbuf[1023]); + mema = 0; + for (i=0, j=0; i>= 1; /* move mask right */ + } + tcyl = (k * 8) + sec; /* starting bit# */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv REC sb# %04x byte# %04x mask %06x start %08x\n", + sec, k, mema, tcyl); + /* 16 bit sector offset and 9 of 16 bit mask */ + /* tcyl - fake 14 bit offset */ + /* mema - fake 9 bit mask */ + buf[0] = (tcyl & 0x3f00) >> 8; /* upper 6 bits */ + buf[1] = tcyl & 0xff; /* lower 8 bits */ + buf[2] = (mema & 0x100) >> 8; /* upper 1 bits */ + buf[3] = mema & 0xff; /* lower 8 bits */ + /* write the offset and mask data */ + for (i=0; i<4; i++) { + ch = buf[i]; /* get a char from buffer */ + if (chan_write_byte(chsa, &ch)) { /* put a byte to memory */ + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv DEC read %04x bytes of %04x\n", + i, chp->ccw_count); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } + } + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv wrote DEC offset %04x mask %04x CHS %08x\n", + tcyl & 0x3fff, mema & 0x1ff, uptr->CHS); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case DSK_SNS: /* 0x04 */ + sim_debug(DEBUG_CMD, dptr, "hsdp_startcmd CMD sense\n"); + + /* count must be 12 or 14, if not prog check */ + if (len != 12 && len != 14) { + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv Sense bad count unit=%02x count%04x\n", unit, len); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK|STATUS_LENGTH); + break; + } + /* bytes 0,1 - Cyl entry from CHS reg */ + ch = (uptr->CHS >> 24) & 0xff; + sim_debug(DEBUG_CMD, dptr, "hsdp_srv sense CHS b0 unit=%02x 1 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = (uptr->CHS >> 16) & 0xff; + sim_debug(DEBUG_CMD, dptr, "hsdp_srv sense CHS b1 unit=%02x 2 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + /* byte 2 - Track entry from CHS reg */ + ch = (uptr->CHS >> 8) & 0xff; + sim_debug(DEBUG_CMD, dptr, "hsdp_srv sense CHS b2 unit=%02x 3 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + /* byte 3 - Sector entry from CHS reg */ + ch = (uptr->CHS) & 0xff; + sec = ch; /* save sec num for later */ +#ifndef FOR_TESTING + /* get STAR (target sector) data in STAR */ + cyl = STAR2CYL(uptr->CHS); /* get current cyl */ + trk = (uptr->CHS >> 8) & 0xff; /* get trk/head */ + if ((trk == (hsdp_type[type].nhds-1)) && /* see if last trk */ + ((cyl == hsdp_type[type].cyl-1)) && /* see if last cyl */ + (sec == 0)) { /* sec is zero */ + ch = 0; /* show last track */ + } +#endif + sim_debug(DEBUG_CMD, dptr, "hsdp_srv sense CHS b3 unit=%02x 4 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + + /* bytes 4 - mode reg, byte 0 of SNS */ + ch = (uptr->SNS >> 24) & 0xff; /* return the sense data */ + sim_debug(DEBUG_CMD, dptr, "hsdp_srv sense unit=%02x 1 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + /* bytes 5-7 - status bytes, bytes 1-3 of SNS */ + ch = (uptr->SNS >> 16) & 0xff; + sim_debug(DEBUG_CMD, dptr, "hsdp_srv sense unit=%02x 2 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = (uptr->SNS >> 8) & 0xff; + sim_debug(DEBUG_CMD, dptr, "hsdp_srv sense unit=%02x 3 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = (uptr->SNS) & 0xff; + sim_debug(DEBUG_CMD, dptr, "hsdp_srv sense unit=%02x 4 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + + /* bytes 8-11 - drive mode register entries from assigned hsdp */ + ch = hsdp_type[type].type & 0xff; /* type byte */ + sim_debug(DEBUG_CMD, dptr, "hsdp_srv datr unit=%02x 1 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = hsdp_type[type].spt & 0xff; /* get sectors per track */ + sim_debug(DEBUG_CMD, dptr, "hsdp_srv datr unit=%02x 2 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = hsdp_type[type].nhds & 0xff; /* get # MHD heads */ + sim_debug(DEBUG_CMD, dptr, "hsdp_srv datr unit=%02x 3 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); +#ifdef FOR_TESTING + ch = (uptr->SNS >> 24) & 0xff; /* get mode data */ +#else + ch = 0; /* no FHD heads */ +#endif + sim_debug(DEBUG_CMD, dptr, "hsdp_srv datr unit=%02x 4 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + + /* bytes 12 & 13 are optional, so check if read done */ + /* TODO add drive status bits here */ + if ((test_write_byte_end(chsa)) == 0) { + /* bytes 12 & 13 contain drive related status */ + uptr->SNS2 |= (SNS_SEND|SNS_USEL); /* selected & seek end */ + /* bits 2-7 have sector pulse count */ + ch = ((sec * 2) % SPT(type)) & 0x3f;/* get index cnt */ + uptr->SNS2 = (uptr->SNS2 & 0xc0ff) | ((((uint32)ch) & 0x3f) << 8); + ch = (uptr->SNS2 >> 8) & 0xff; /* seek end and unit selected for now */ + sim_debug(DEBUG_CMD, dptr, "hsdp_srv dsr unit=%02x 1 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + + ch = 0x30; /* drive on cylinder and ready for now */ + uptr->SNS2 |= (SNS_ONC|SNS_UNR); /* on cylinder & ready */ + ch = uptr->SNS2 & 0xff; /* drive on cylinder and ready for now */ + sim_debug(DEBUG_CMD, dptr, "hsdp_srv dsr unit=%02x 2 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + } + uptr->SNS &= 0xff000000; /* reset status */ + uptr->SNS2 = 0; /* reset status */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case DSK_SKC: /* Seek cylinder, track, sector 0x07 */ + /* If we are waiting on seek to finish, check if there yet. */ + if (uptr->CMD & DSK_SEEKING) { + /* file offset in bytes to std or alt track */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)) * SSB(type); + /* we are on cylinder, seek is done */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv seek on cylinder to %04x/%02x/%02x bytes %06x\n", + (uptr->CHS >> 16) & 0xffff, (uptr->CHS >> 8) & 0xff, + uptr->CHS & 0xff, tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS2 |= (SNS_SEND|SNS_ONC); /* On cylinder & seek done */ + /* we have already seeked to the required sector */ + /* we do not need to seek again, so move on */ + chan_end(chsa, SNS_DEVEND|SNS_CHNEND); + break; + } + + /* not seeking, so start a new seek */ + /* set buf data to current STAR values */ + tcyl = cyl = STAR2CYL(uptr->CHS); /* get current cyl */ + buf[0] = (cyl >> 8) & 0xff; /* split cylinder */ + buf[1] = cyl & 0xff; + buf[2] = (uptr->CHS >> 8) & 0xff; /* get trk/head */ + buf[3] = uptr->CHS & 0xff; /* get sec */ + + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv current STAR unit=%02x star %02x %02x %02x %02x\n", + unit, buf[0], buf[1], buf[2], buf[3]); + + if (len > 4) { + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv SEEK bad count unit=%02x count%04x\n", unit, len); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK|STATUS_LENGTH); + break; + } + + /* Read in 1-4 character seek code */ + for (i = 0; i < 4; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + if (i == 0) { + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv seek error unit=%02x star %02x %02x %02x %02x\n", + unit, buf[0], buf[1], buf[2], buf[3]); + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_DADE; /* Disc addressing or seek error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + chp->ccw_count = len; /* restore count, huh? */ + return SCPE_OK; + break; + } + /* just read the next byte */ + /* done reading, see how many we read */ + if (i == 1) { + /* UTX wants to set seek STAR to zero */ + buf[0] = buf[1] = buf[2] = buf[3] = 0; + break; + } + } + } + /* else the cyl, trk, and sect are ready to update */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv STAR unit=%02x star %02x %02x %02x %02x\n", + unit, buf[0], buf[1], buf[2], buf[3]); + + /* save STAR (target sector) data in STAR */ + tstar = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]); + cyl = STAR2CYL(tstar); /* get the cylinder */ + trk = buf[2]; /* get the track */ + sec = buf[3]; /* get sec */ + + /* see if we need to incr to next track for alt sec support */ + if (uptr->LSC != SPT(type)) { + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv LSC0 %02x B4 test/incr cyl %04x trk %02x sec %02x\n", + uptr->LSC, (tstar>>16)&0xffff, (tstar>>8)&0xff, tstar&0xff); + if ((int)(tstar&0xff) >= (int)(SPT(type)-1)) { + tstar &= 0xffffff00; /* clear sector number */ + tstar += 0x00000100; /* bump head # */ + if (((tstar>>8)&0xff) >= (HDS(type))) { + tstar &= 0xffff00ff; /* clear head number */ + tstar += 0x00010000; /* bump cyl # */ + } + } + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv LSC0 %02x AF test/incr cyl %04x trk %02x sec %02x\n", + uptr->LSC, (tstar>>16)&0xffff, (tstar>>8)&0xff, tstar&0xff); +//#define DO_DYNAMIC_DEBUG +#ifdef DO_DYNAMIC_DEBUG +// cpu_dev.dctrl |= DEBUG_INST|DEBUG_TRAP|DEBUG_CMD|DEBUG_DETAIL; /* start instruction trace */ +#endif + } + + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv NEW SEEK cyl %04x trk %02x sec %02x unit=%02x\n", + cyl&0xffff, trk, buf[3], unit); + + /* Check if seek valid */ + if (cyl >= hsdp_type[type].cyl || + trk >= hsdp_type[type].nhds || + buf[3] >= uptr->LSC) { + + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv seek ERROR LSC %02x cyl %04x trk %02x sec %02x unit=%02x\n", + uptr->LSC, cyl, trk, buf[3], unit); + + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_DADE; /* set error status */ + uptr->SNS2 |= (SNS_SKER|SNS_SEND); + + /* set new STAR value */ + uptr->CHS = CHS2STAR(cyl, trk, buf[3]); + + /* we have an error, tell user */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); /* end command */ + break; + } + + /* get alternate track if this one is defective */ + tempt = get_dpatrk(uptr, tstar, lbuf); + + if ((tempt == 0) && (tstar != 0)) { + /* we have error */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_srv SEEK get_dpatrk return error tempt %06x STAR %08x\n", + tempt, tstar); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* file offset in bytes to std or alt track */ + tstart = STAR2SEC(tempt, SPT(type), SPC(type)) * SSB(type); + /* set new STAR value using new values */ + uptr->CHS = tstar; + + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv seek start %04x cyl %04x trk %02x sec %02x CHS %08x\n", + tstart, cyl, trk, buf[3], uptr->CHS); + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* seek home */ + sim_debug(DEBUG_CMD, dptr, "hsdp_srv Error on seek to %08x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* do a delay to slow things down */ + if (STAR2CYL(uptr->CHS) != tcyl) { + k = ((int)tcyl - (int)cyl); + if (k < 0) + k = -k; + } else { + k = 20; + } + /* Do a fake seek to kill time */ + uptr->CMD |= DSK_SEEKING; /* show we are seeking */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv seeking unit=%02x to %04x/%02x/%02x from cyl %04x (%04x)\n", + unit, cyl, trk, buf[3], tcyl, k); +#ifdef FAST_FOR_UTX + sim_activate(uptr, 15); +// sim_activate(uptr, 20); /* start things off */ +// sim_activate(uptr, 20+k); /* start us off */ +#else +// sim_activate(uptr, 150); /* start things off */ + sim_activate(uptr, 200+k); /* start us off */ +// sim_activate(uptr, 400+k); /* start us off */ +#endif + break; + + case DSK_XEZ: /* 0x37 */ /* Rezero & Read IPL record */ + + sim_debug(DEBUG_CMD, dptr, "RD REZERO IPL unit=%02x seek 0\n", unit); + /* Do a seek to 0 */ + uptr->CHS = 0; /* set current CHS to 0, 0, 0 */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->CMD |= DSK_SKC; /* show as seek command */ + tstart = 0; /* byte offset is 0 */ + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "hsdp_srv Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + /* we are on cylinder/track/sector zero, so go on */ + sim_debug(DEBUG_CMD, dptr, "hsdp_srv done seek trk 0\n"); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_DEVEND|SNS_CHNEND); + break; + + case DSK_LMR: /* 0x1F */ + sim_debug(DEBUG_CMD, dptr, "Load Mode Reg unit=%02x\n", unit); + /* Read in 1 character of mode data */ + if (chan_read_byte(chsa, &buf[0])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + } + sim_debug(DEBUG_CMD, dptr, "Load Mode Reg unit=%02x old %02x new %02x\n", + unit, (uptr->SNS>>24)&0xff, buf[0]); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS &= MASK24; /* clear old mode data */ + uptr->SNS |= (buf[0] << 24); /* save mode value */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case DSK_FMT: /* 0x0B Format for no skip */ + /* buffer must be on halfword boundry if not STATUS_PCHK and SNS_CMDREJ status */ +// chp->chan_status |= STATUS_PCHK; /* program check for invalid cmd */ +// uptr->SNS |= SNS_CMDREJ; /* cmd rejected */ + /* byte count can not exceed 20160 for the track */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "HSDP Format starting CMD %08x chsa %04x buffer %06x count %04x\n", + uptr->CMD, chsa, chp->ccw_addr, chp->ccw_count); + sim_debug(DEBUG_CMD, dptr, "Format %x label", uptr->CHS); + /* now read sector label data */ + len = chp->ccw_count; + for (i = 0; i < len; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have write error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + if ((i%16) == 0) + sim_debug(DEBUG_CMD, dptr, "\nFormat %x label", uptr->CHS); + sim_debug(DEBUG_CMD, dptr, " %02x", buf[i]); + } + sim_debug(DEBUG_CMD, dptr, "\n"); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case DSK_RD: /* Read Data */ + if ((uptr->CMD & DSK_READING) == 0) { /* see if we are reading data */ + uptr->CMD |= DSK_READING; /* read from disk starting */ + sim_debug(DEBUG_CMD, dptr, + "HSDP READ starting CMD %08x chsa %04x buffer %06x count %04x\n", + uptr->CMD, chsa, chp->ccw_addr, chp->ccw_count); + } + + if (uptr->CMD & DSK_READING) { /* see if we are reading data */ + /* get sector offset */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)); + /* convert sector number back to chs value to sync disk for diags */ + uptr->CHS = hsdpsec2star(tstart, type); + + /* get alternate track if this one is defective */ + tempt = get_dpatrk(uptr, uptr->CHS, lbuf); + /* file offset in bytes to std or alt track */ + tstart = STAR2SEC(tempt, SPT(type), SPC(type)) * SSB(type); + + if ((tempt == 0) && (uptr->CHS != 0)) { + /* we have error */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_srv READ get_dpatrk return error tempt %06x tstart %06x\n", tempt, tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_DADE; /* set error status */ + uptr->SNS2 |= (SNS_SKER|SNS_SEND); + sim_debug(DEBUG_EXP, dptr, "hsdp_srv READ error on seek to %04x\n", tstart); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + uptr->SNS &= ~SNS_DEFTRK; /* remove defective flag */ + /* see if spare track */ + if (lbuf[4] & 0x20) { /* see if spare track */ + uptr->SNS |= SNS_DADE; /* disk addr error */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_srv READ get_dpatrk return spare tempt %06x tstart %06x\n", tempt, tstart); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + + /* see if reserved track */ + if (lbuf[4] & 0x10) { /* see if reserved track */ + uptr->SNS |= SNS_MOCK; /* mode check error */ + uptr->SNS |= SNS_RES8; /* reserved track access error */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "hsdp_srv READ, Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + sim_debug(DEBUG_CMD, dptr, + "HSDP READ reading CMD %08x chsa %04x tstart %04x buffer %06x count %04x\n", + uptr->CMD, chsa, tstart, chp->ccw_addr, chp->ccw_count); + + /* read in a sector of data from disk */ + if ((len=sim_fread(buf, 1, ssize, uptr->fileref)) != ssize) { + sim_debug(DEBUG_CMD, dptr, + "Error %08x on read %04x of diskfile cyl %04x hds %02x sec %02x\n", + len, ssize, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv after READ chsa %04x buffer %06x count %04x\n", + chsa, chp->ccw_addr, chp->ccw_count); + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv READ data %02x%02x%02x%02x %02x%02x%02x%02x " + "%02x%02x%02x%02x %02x%02x%02x%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); + + uptr->CHS++; /* next sector number */ + /* process the next sector of data */ + for (i=0; ichan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + sim_debug(DEBUG_CMD, dptr, + "HSDP Read %04x bytes leaving %04x from diskfile /%04x/%02x/%02x\n", + i, chp->ccw_count, ((uptr->CHS)>>16)&0xffff, + ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } + } + + /* get current sector offset */ + j = STAR2SEC(tempt, SPT(type), SPC(type)); /* current sector */ + i = ((CYL(type) - 3) * HDS(type)) * SPT(type); /* diag start */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv after READ j %04x i %04x j-i %04x CAP %06x DIAG %06x\n", + j, i, j-i, CAP(type), (((CYL(type) - 3) * HDS(type)) * SPT(type))); /* diag start */ + if (j >= i) { /* only do diag sectors */ + cecc = dple_ecc32(buf, ssize); /* calc ecc for sector */ + sim_debug(DEBUG_CMD, dptr, + "ECC j %02x i %02x data calc Old %08x Cur %08x cyl %04x hds %02x sec %02x\n", + j, i, decc[j-i], cecc, STAR2CYL(tempt), ((tempt) >> 8)&0xff, (tempt&0xff)); + if ((decc[j-i] != 0) && (cecc != decc[j-i])) { /* test against old */ + /* checksum error */ + sim_debug(DEBUG_CMD, dptr, + "ECC j %02x i %02x data error Old %08x New %08x cyl %04x hds %02x sec %02x\n", + j, i, decc[j-i], cecc, STAR2CYL(tempt), ((tempt) >> 8)&0xff, (tempt&0xff)); + uptr->SNS |= SNS_ECCD; /* data ECC error */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_CHECK|STATUS_EXPT); + return SCPE_OK; + } + } + + /* see if this is a read ECC from diag */ + /* mode byte will be 0x08 and remaining count will be 4 */ + if ((uptr->SNS & SNS_DIAGMOD) && (chp->ccw_count == 4)) { + for (i=0; iCHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + /* set ECC value here */ + for (i=0; i<4; i++) { + ch = (ecc >> ((3-i)*8)) & 0xff; /* get a char from buffer */ + if (chan_write_byte(chsa, &ch)) { /* put a byte to memory */ + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + return SCPE_OK; + } + } + sim_debug(DEBUG_CMD, dptr, + "Read ECC %04x for diags 4 bytes to ECC REG cyl %04x hds %02x sec %02x\n", + ecc, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + } + + sim_debug(DEBUG_CMD, dptr, + "HSDP READ %04x bytes leaving %4x to be read to %06x from diskfile %04x/%02x/%02x\n", + ssize, chp->ccw_count, chp->ccw_addr, + ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + + /* see if we need to incr to next track for alt sec support */ + if (uptr->LSC != SPT(type)) { + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv LSC %02x B4 test/incr cyl %04x trk %02x sec %02x\n", + uptr->LSC, (uptr->CHS>>16)&0xffff, (uptr->CHS>>8)&0xff, uptr->CHS&0xff); + if ((uptr->CHS&0xff) >= (SPT(type)-1)) { + uptr->CHS &= 0xffffff00; /* clear sector number */ + uptr->CHS += 0x00000100; /* bump head # */ + if (((uptr->CHS>>8)&0xff) >= (HDS(type))) { + uptr->CHS &= 0xffff00ff; /* clear head number */ + uptr->CHS += 0x00010000; /* bump cyl # */ + } + } + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv LSC %02x AF test/incr cyl %04x trk %02x sec %02x\n", + uptr->LSC, (uptr->CHS>>16)&0xffff, (uptr->CHS>>8)&0xff, uptr->CHS&0xff); + } + + /* get sector offset */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)); + + /* see if over end of disk */ + if (tstart >= (uint32)CAP(type)) { + /* EOM reached, abort */ + sim_debug(DEBUG_CMD, dptr, + "HSDP Read reached EOM for read from disk @ %04x/%02x/%02x\n", + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->CHS = 0; /* reset cylinder position */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* see if we are done reading data */ + if (test_write_byte_end(chsa)) { + /* EOM reached, abort */ + sim_debug(DEBUG_CMD, dptr, + "HSDP Read complete for read from disk @ %04x/%02x/%02x\n", + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + } + + sim_debug(DEBUG_CMD, dptr, + "HSDP sector read complete, %x bytes to go from diskfile /%04x/%02x/%02x\n", + chp->ccw_count, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); +#ifdef FAST_FOR_UTX +// sim_activate(uptr, 10); /* wait to read next sector */ + sim_activate(uptr, 15); /* wait to read next sector */ +// sim_activate(uptr, 20); /* wait to read next sector */ +#else + sim_activate(uptr, 150); /* wait to read next sector */ +// sim_activate(uptr, 300); /* wait to read next sector */ +#endif + break; + } + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + break; + + case DSK_WD: /* Write Data */ + if ((uptr->CMD & DSK_WRITING) == 0) { /* see if we are writing data */ + sim_debug(DEBUG_CMD, dptr, + "HSDP WRITE starting unit=%02x CMD %08x write %04x from %06x to %03x/%02x/%02x\n", + unit, uptr->CMD, chp->ccw_count, chp->ccw_addr, + ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + + if (uptr->SNS & 0xf0000000) { /* see if any mode bit 0-3 is set */ + uptr->SNS |= SNS_MOCK; /* mode check error */ + chp->chan_status |= STATUS_PCHK; /* channel prog check */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + uptr->CMD |= DSK_WRITING; /* write to disk starting */ + } + if (uptr->CMD & DSK_WRITING) { /* see if we are writing data */ + /* get file offset in sectors */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)); + /* file offset in bytes */ + tstart = tstart * SSB(type); + + /* get alternate track if this one is defective */ + tempt = get_dpatrk(uptr, uptr->CHS, lbuf); + /* file offset in bytes to std or alt track */ + tstart = STAR2SEC(tempt, SPT(type), SPC(type)) * SSB(type); + + if ((tempt == 0) && (uptr->CHS != 0)) { + /* we have error */ + sim_debug(DEBUG_EXP, dptr, + "hsdp_srv WRITE get_dpatrk return error tempt %06x tstart %06x\n", tempt, tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_DADE; /* set error status */ + uptr->SNS2 |= (SNS_SKER|SNS_SEND); + sim_debug(DEBUG_EXP, dptr, "hsdp_srv WRITE error on seek to %04x\n", tstart); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + uptr->SNS &= ~SNS_DEFTRK; /* remove defective flag */ + /* see if spare track */ + if (lbuf[4] & 0x20) { /* see if spare track */ + uptr->SNS |= SNS_DADE; /* disk addr error */ + chp->chan_status |= STATUS_PCHK; /* channel prog check */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + /* see if reserved track */ + if (lbuf[4] & 0x10) { /* see if reserved track */ + uptr->SNS |= SNS_MOCK; /* mode check error */ + uptr->SNS |= SNS_RES8; /* reserved track access error */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "hsdp_srv WRITE, Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + + /* process the next sector of data */ + tcyl = 0; /* used here as a flag for short read */ + for (i=0; ichan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* if error on reading 1st byte, we are done writing */ + if ((i == 0) || (chp->chan_status & STATUS_PCHK)) { + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "HSDP Wrote %04x bytes to diskfile cyl %04x hds %02x sec %02x\n", + ssize, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } + ch = 0; /* finish out the sector with zero */ + tcyl++; /* show we have no more data to write */ + } + buf2[i] = ch; /* save the char */ + } + + /* get file offset in sectors */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)); + /* convert sector number back to chs value to sync disk for diags */ + uptr->CHS = hsdpsec2star(tstart, type); + + /* write the sector to disk */ + if ((i=sim_fwrite(buf2, 1, ssize, uptr->fileref)) != ssize) { + sim_debug(DEBUG_CMD, dptr, + "Error %08x on write %04x bytes to diskfile cyl %04x hds %02x sec %02x\n", + i, ssize, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv after WRITE buffer %06x count %04x\n", + chp->ccw_addr, chp->ccw_count); + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv WRITE data %02x%02x%02x%02x %02x%02x%02x%02x " + "%02x%02x%02x%02x %02x%02x%02x%02x\n", + buf2[0], buf2[1], buf2[2], buf2[3], buf2[4], buf2[5], buf2[6], buf2[7], + buf2[8], buf2[9], buf2[10], buf2[11], buf2[12], buf2[13], buf2[14], buf2[15]); + sim_debug(DEBUG_DATA, dptr, + "hsdp_srv after WRITE CAP %06x DIAG %06x\n", + CAP(type), (((CYL(type) - 3) * HDS(type)) * SPT(type))); /* diag start */ + + /* get current sector offset */ + j = STAR2SEC(tempt, SPT(type), SPC(type)); /* current sector */ + i = ((CYL(type) - 3) * HDS(type)) * SPT(type); /* diag start */ + sim_debug(DEBUG_DATA, dptr, + "hsdp_srv after WRITE j %04x i %04x j-i %04x CAP %06x DIAG %06x\n", + j, i, j-i, CAP(type), (((CYL(type) - 3) * HDS(type)) * SPT(type))); /* diag start */ + if (j >= i) { /* only do diag sectors */ + cecc = dple_ecc32(buf2, ssize); /* calc ecc for sector */ + sim_debug(DEBUG_DATA, dptr, + "ECC j %02x i %02x data write Old %08x Cur %08x cyl %04x hds %02x sec %02x\n", + j, i, decc[j-i], cecc, STAR2CYL(tempt), ((tempt) >> 8)&0xff, (tempt&0xff)); + decc[j-i] = cecc; /* set new ecc */ + } + j = j-i; /* save index */ + + /* see if this is a write ECC from diag */ + /* mode byte will be 0x08 and remaining count will be 4 */ + if ((uptr->SNS & SNS_DIAGMOD) && (chp->ccw_count == 4)) { + for (i=0; iCHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + /* set ECC value here */ + for (i=0; i<4; i++) { + if (chan_read_byte(chsa, &ch)) {/* get a byte from memory */ + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + return SCPE_OK; + } + /* get an ECC byte */ + buf[i] = ch; /* put a char to buffer */ + ecc |= ((ch & 0xff) << ((3-i)*8)); + } + tcyl++; /* show we have no more data to write */ + sim_debug(DEBUG_CMD, dptr, + "Write decc[%04x] ECC=%08x from diags, calc ECC=%08x cyl %04x hds %02x sec %02x\n", + j, ecc, cecc, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + decc[j] = ecc; /* set new ecc from diag */ + } + + sim_debug(DEBUG_CMD, dptr, + "DISK WR to sec end %04x bytes end %04x to diskfile cyl %04x hds %02x sec %02x\n", + len, ssize, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + + uptr->CHS++; /* next sector number */ + if (tcyl != 0) { /* see if done with write command */ + sim_debug(DEBUG_CMD, dptr, + "HSDP WroteB %04x bytes to diskfile cyl %04x hds %02x sec %02x\n", + ssize, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we done */ + break; + } + + /* see if we need to incr to next track for alt sec support */ + if (uptr->LSC != SPT(type)) { + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv LSC2 %02x B4 test/incr cyl %04x trk %02x sec %02x\n", + uptr->LSC, (uptr->CHS>>16)&0xffff, (uptr->CHS>>8)&0xff, uptr->CHS&0xff); + if ((uptr->CHS&0xff) >= (SPT(type)-1)) { + uptr->CHS &= 0xffffff00; /* clear sector number */ + uptr->CHS += 0x00000100; /* bump track # */ + if (((uptr->CHS>>8)&0xff) >= (HDS(type))) { + uptr->CHS &= 0xffff00ff; /* clear head number */ + uptr->CHS += 0x00010000; /* bump cyl # */ + } + } + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv LSC2 %02x AF test/incr cyl %04x trk %02x sec %02x\n", + uptr->LSC, (uptr->CHS>>16)&0xffff, (uptr->CHS>>8)&0xff, uptr->CHS&0xff); + } + + /* get sector offset */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)); + + /* see if over end of disk */ + if (tstart >= (uint32)CAP(type)) { + /* EOM reached, abort */ + sim_debug(DEBUG_CMD, dptr, + "HSDP Write reached EOM for write to disk @ /%04x/%02x/%02x\n", + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->CHS = 0; /* reset cylinder position */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* see if we are done reading data */ + if (test_write_byte_end(chsa)) { + sim_debug(DEBUG_CMD, dptr, + "DISK Write complete for read from diskfile %04x/%02x/%02x\n", + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + } + +#ifdef FAST_FOR_UTX +// sim_activate(uptr, 10); /* keep writing */ + sim_activate(uptr, 15); /* keep writing */ +#else + sim_activate(uptr, 150); /* wait to read next sector */ +// sim_activate(uptr, 300); /* wait to read next sector */ +#endif + break; + } + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + break; + + case DSK_RSL: /* RSL 0x32 */ + /* Read sector label zero to get disk geometry */ + /* write 30 bytes, b0-b1=cyl, b1=trk, b2=sec */ + /* zero the Sector Label Buffer */ + for (i = 0; i < 30; i++) + buf[i] = 0; + + len = chp->ccw_count; /* get number of sectors per track */ + mema = uptr->CHS+(len/30); /* save address */ + + sim_debug(DEBUG_CMD, dptr, "before RSL Sector %x len %x\n", uptr->CHS, len); + + /* read a 30 byte sector label for each sector on track */ + /* for 16 sectors per track, that is 480 bytes */ + /* for 20 sectors per track, that is 600 bytes */ + for (j=0; jCHS, SPT(type), SPC(type)); + /* convert sector number back to chs value to sync disk for diags */ + uptr->CHS = hsdpsec2star(tstart, type); + + cyl = (uptr->CHS >> 16) & 0xffff; /* get the cylinder */ + trk = (uptr->CHS >> 8) & 0xff; /* get the track */ + sec = uptr->CHS & 0xff; /* get sec */ + seeksec = tstart; /* save sector number */ + + sim_debug(DEBUG_EXP, dptr, "hsdp_srv RSL cyl %04x trk %02x sec %02x sector# %06x\n", + cyl, trk, sec, seeksec); + + /* seek sector label area after end of track label area */ + tstart = CAPB(type) + (CYL(type)*HDS(type)*30) + (tstart*30); + + /* file offset in bytes to sector label */ + sim_debug(DEBUG_EXP, dptr, "hsdp_srv RSL SEEK on seek to %08x\n", tstart); + + /* seek to the location where we will read sector label */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { + sim_debug(DEBUG_CMD, dptr, + "Error seeking sector label area at sect %06x offset %08x\n", + seeksec, tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + + /* read in a sector label from disk */ + if (sim_fread(buf, 1, 30, uptr->fileref) != 30) { + sim_debug(DEBUG_CMD, dptr, + "Error %08x on read %04x of diskfile cyl %04x hds %02x sec %02x\n", + len, 30, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + + sim_debug(DEBUG_CMD, dptr, "Sector %x label", uptr->CHS); + /* now write sector label data */ + for (i = 0; i < 30; i++) { + if (chan_write_byte(chsa, &buf[i])) { + /* we have write error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, "\n"); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + if (i == 16) + sim_debug(DEBUG_CMD, dptr, "\nSector %x label", uptr->CHS); + sim_debug(DEBUG_CMD, dptr, " %02x", buf[i]); + } + sim_debug(DEBUG_CMD, dptr, "\n"); + + /* leave STAR "unnormalized" for diags */ + uptr->CHS++; /* bump to next track */ + if ((uptr->CHS & 0xff) == SPC(type)) + break; /* stop at last sector */ + len -= 30; /* count 1 sector label size */ + if (len > 0) + continue; + break; /* done */ + } + + uptr->CHS = mema; /* restore address */ + + sim_debug(DEBUG_CMD, dptr, "after RSL Sector %x len %x\n", uptr->CHS, chp->ccw_count); + + /* command done */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, "hsdp_srv cmd RSL done chsa %04x count %04x completed\n", + chsa, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_WTF: /* 0x41 Write track format */ + case DSK_WSL: /* WSL 0x31 */ + len = chp->ccw_count; /* get number bytes to read */ + mema = uptr->CHS; /* save address */ + + sim_debug(DEBUG_CMD, dptr, "before WSL/WTF Sector %x len %x\n", uptr->CHS, len); + + /* read a 30 byte sector label for each sector on track */ + /* for 16 sectors per track, that is 480 bytes */ + /* for 20 sectors per track, that is 600 bytes */ + for (j=0; jCHS); + /* now read sector label data */ + for (i = 0; i < 30; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have read error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + if ((i%16) == 0) + sim_debug(DEBUG_CMD, dptr, "\nSector %x label", uptr->CHS); + sim_debug(DEBUG_CMD, dptr, " %02x", buf[i]); + } + sim_debug(DEBUG_CMD, dptr, "\n"); + + /* see if user trying to set invalid bit pattern */ + if ((buf[4] & 0x48) == 0x48) { /* see if setting defective alternate trk */ + uptr->SNS |= SNS_DSKFERR; /* disk formating error */ + uptr->CHS = mema; /* restore address */ + chp->ccw_count = len; /* restore number bytes to read */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + return SCPE_OK; + break; + } + + /* get file offset in sectors */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)); + + /* convert sector number back to chs value to sync disk for diags */ + uptr->CHS = hsdpsec2star(tstart, type); + + cyl = (uptr->CHS >> 16) & 0xffff; /* get the cylinder */ + trk = (uptr->CHS >> 8) & 0xff; /* get the track */ + sec = uptr->CHS & 0xff; /* get sec */ + seeksec = tstart; /* save sector number */ + + sim_debug(DEBUG_EXP, dptr, "hsdp_srv WSL/WTF cyl %04x trk %02x sec %02x sector# %06x\n", + cyl, trk, sec, seeksec); + + /* seek sector label area after end of track label area */ + tstart = CAPB(type) + (CYL(type)*HDS(type)*30) + (tstart*30); + + /* file offset in bytes to sector label */ + sim_debug(DEBUG_EXP, dptr, "hsdp_srv WSL/WTF SEEK on seek to %08x\n", tstart); + + /* seek to the location where we will write sector label */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { + sim_debug(DEBUG_CMD, dptr, + "Error seeking sector label area at sect %06x offset %08x\n", + seeksec, tstart); + uptr->CHS = mema; /* restore address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + + /* write sector label to disk */ + if (sim_fwrite(buf, 1, 30, uptr->fileref) != 30) { + sim_debug(DEBUG_CMD, dptr, + "Error %08x on write %04x of diskfile cyl %04x hds %02x sec %02x\n", + len, 30, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CHS = mema; /* restore address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + + /* leave STAR "unnormalized" for diags */ + uptr->CHS++; /* bump to next sector */ + if ((uptr->CHS & 0xff) == SPC(type)) + break; /* stop at last sector */ + len -= 30; /* count 1 sector label size */ + if (len > 0) + continue; + break; /* done */ + } + + uptr->CHS = mema; /* restore address */ + + sim_debug(DEBUG_CMD, dptr, "after WSL/WTF Sector %x len %x\n", uptr->CHS, chp->ccw_count); + + /* command done */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, "hsdp_srv cmd WSL/WTF done chsa %04x count %04x completed\n", + chsa, chp->ccw_count); + + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_RVL: /* 0x42 Read vendor label */ + /* Read track label to get defect information */ + /* write 30 bytes, b0-b1=cyl, b1=trk, b2=sec */ + + mema = uptr->CHS & 0xffffff00; /* zero sector for trk read */ + + /* get file offset in sectors */ + tstart = STAR2SEC(mema, SPT(type), SPC(type)); + + /* convert sector number back to chs value to sync disk for diags */ + mema = hsdpsec2star(tstart, type); + cyl = (mema >> 16) & 0xffff; /* get the cylinder */ + trk = (mema >> 8) & 0xff; /* get the track */ + + /* get track number */ + tstart = (cyl * HDS(type)) + trk; + sim_debug(DEBUG_EXP, dptr, "hsdp_srv RVL cyl %4x(%d) trk %x sec# %06x\n", + cyl, cyl, trk, tstart); + + /* calc offset in file to track label */ + tstart = CAPB(type) + (tstart * 30); + + /* file offset in bytes */ + sim_debug(DEBUG_EXP, dptr, "hsdp_srv RVL SEEK on seek to %06x\n", tstart); + + /* seek to the location where we will r/w track label */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "hsdp_srv RVL, Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + + /* read in a track label from disk */ + if ((len=sim_fread(buf, 1, 30, uptr->fileref)) != 30) { + sim_debug(DEBUG_CMD, dptr, + "Error %08x on read %04x of diskfile cyl %04x hds %02x sec %02x\n", + len, 30, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* clear out area for bad sector errors to show no errors on track */ + /* error info in bytes 10-24 */ + for (i=10; i<25; i++) + buf[i] = 0; + + if (buf[4] == 0x08) { /* see if defective track */ + uptr->SNS |= SNS_DEFTRK; /* flag as defective */ + sim_debug(DEBUG_CMD, dptr, "Track %08x is defective\n", uptr->CHS); + } + + if (buf[4] == 0x40) { /* see if alternate track */ + uptr->SNS |= SNS_AATT; /* flag as alternate */ + sim_debug(DEBUG_CMD, dptr, "Track %08x is alternate\n", uptr->CHS); + } + + /* now write track label data to memory */ + sim_debug(DEBUG_CMD, dptr, "Track %08x label", uptr->CHS); + for (i = 0; i < 30; i++) { + if (chan_write_byte(chsa, &buf[i])) { + /* we have write error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (i == 16) + sim_debug(DEBUG_CMD, dptr, "\nTrack %08x label", uptr->CHS); + sim_debug(DEBUG_CMD, dptr, " %02x", buf[i]); + } + sim_debug(DEBUG_CMD, dptr, "\n"); + + /* command done */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, "hsdp_srv cmd RVL done chsa %04x count %04x completed\n", + chsa, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + + case DSK_RTL: /* RTL 0x52 */ + /* Read track zero to get disk geometry */ + /* write 30 bytes, b0-b1=cyl, b1=trk, b2=sec */ + + /* zero the Track Label Buffer */ + for (i = 0; i < 30; i++) + buf[i] = 0; + + uptr->CHS &= 0xffffff00; /* zero sector for trk read */ + mema = uptr->CHS; + + /* get file offset in sectors */ + tstart = STAR2SEC(mema, SPT(type), SPC(type)); + + /* convert sector number back to chs value to sync disk for diags */ + mema = hsdpsec2star(tstart, type); + cyl = (mema >> 16) & 0xffff; /* get the cylinder */ + trk = (mema >> 8) & 0xff; /* get the track */ + + /* get track number */ + tstart = (cyl * HDS(type)) + trk; + sim_debug(DEBUG_EXP, dptr, "hsdp_srv RTL cyl %4x(%d) trk %x sec# %06x\n", + cyl, cyl, trk, tstart); + + /* calc offset in file to track label */ + tstart = CAPB(type) + (tstart * 30); + + /* file offset in bytes */ + sim_debug(DEBUG_EXP, dptr, "hsdp_srv RTL SEEK on seek to %06x\n", tstart); + + /* seek to the location where we will r/w track label */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "hsdp_srv RTL, Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + + /* read in a track label from disk */ + if ((len=sim_fread(buf, 1, 30, uptr->fileref)) != 30) { + sim_debug(DEBUG_CMD, dptr, + "Error %08x on read %04x of diskfile cyl %04x hds %02x sec %02x\n", + len, 30, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + if (buf[4] == 0x08) { /* see if defective track */ + uptr->SNS |= SNS_DEFTRK; /* flag as defective */ + sim_debug(DEBUG_CMD, dptr, "Track %08x is defective\n", uptr->CHS); + } + + if (buf[4] == 0x40) { /* see if alternate track */ + uptr->SNS |= SNS_AATT; /* flag as alternate */ + sim_debug(DEBUG_CMD, dptr, "Track %08x is alternate\n", uptr->CHS); + } + + /* now write track label data to memory */ + sim_debug(DEBUG_CMD, dptr, "Track %08x label", uptr->CHS); + for (i = 0; i < 30; i++) { + if (chan_write_byte(chsa, &buf[i])) { + /* we have write error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (i == 16) + sim_debug(DEBUG_CMD, dptr, "\nTrack %08x label", uptr->CHS); + sim_debug(DEBUG_CMD, dptr, " %02x", buf[i]); + } + sim_debug(DEBUG_CMD, dptr, "\n"); + + /* see if we are operating in sector replacement mode */ + /* if specified sector count will be 1 less than physical sector count */ + if (uptr->CHS == 0) /* see if trk 0 label read */ + uptr->LSC = buf[25]; /* save logical sector count from label */ + /* command done */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, "hsdp_srv cmd RTL done LSC %02x chsa %04x count %04x completed\n", + uptr->LSC, chsa, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_WTL: /* WTL 0x51 */ + /* Write track zero to set disk geometry */ + /* write 30 bytes, b0-b1=cyl, b1=trk, b2=sec */ + + sim_debug(DEBUG_EXP, dptr, "hsdp_srv WTL start cnt %04x CHS %08x\n", + chp->ccw_count, uptr->CHS); + + /* get file offset in sectors */ + tstart = STAR2SEC(uptr->CHS, SPT(type), SPC(type)); + /* convert sector number back to chs value to sync disk for diags */ + uptr->CHS = hsdpsec2star(tstart, type); + uptr->CHS &= 0xffffff00; /* zero sector for trk read */ + mema = uptr->CHS; + + cyl = (uptr->CHS >> 16) & 0xffff; /* get the cylinder */ + trk = (uptr->CHS >> 8) & 0xff; /* get the track */ + + /* get track number */ + tstart = (cyl * HDS(type)) + trk; + sim_debug(DEBUG_EXP, dptr, "hsdp_srv WTL cyl %4x trk %x track# %06x CHS %08x\n", + cyl, trk, tstart, uptr->CHS); + + /* calc offset in file to track label */ + tstart = CAPB(type) + (tstart * 30); + + /* file offset in bytes */ + sim_debug(DEBUG_EXP, dptr, "hsdp_srv WTL SEEK on seek to %06x\n", tstart); + + /* seek to the location where we will write track label */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "hsdp_srv WTL, Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + + sim_debug(DEBUG_EXP, dptr, "Track %08x label", uptr->CHS); + /* now read track label data from memory */ + for (i = 0; i < 30; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have read error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + if (i == 16) + sim_debug(DEBUG_EXP, dptr, "\nTrack %08x label", uptr->CHS); + sim_debug(DEBUG_EXP, dptr, " %02x", buf[i]); + } + sim_debug(DEBUG_EXP, dptr, "\n"); + + /* see if user trying to set invalid bit pattern */ + if ((buf[4] & 0x48) == 0x48) { /* see if setting defective alternate trk */ + uptr->SNS |= SNS_DSKFERR; /* disk formating error */ + uptr->CHS = mema; /* restore address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + + /* write out a track label to disk */ + if ((len=sim_fwrite(buf, 1, 30, uptr->fileref)) != 30) { + sim_debug(DEBUG_CMD, dptr, + "Error %08x on write %04x of diskfile cyl %04x hds %02x sec %02x\n", + len, 30, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CHS = mema; /* restore address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* clear cache entry for this track */ + /* see if track label is in cache */ + for (i=0; iCHS = mema; /* restore address */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv cmd WTL chsa %04x count %04x completed CHS %08x\n", + chsa, chp->ccw_count, uptr->CHS); + + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + default: + sim_debug(DEBUG_CMD, dptr, "invalid command %02x unit %02x\n", cmd, unit); + uptr->SNS |= SNS_CMDREJ; + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|STATUS_PCHK); /* return prog check */ + break; + } + sim_debug(DEBUG_CMD, dptr, + "hsdp_srv done cmd %02x chsa %04x chs %04x/%02x/%02x\n", + cmd, chsa, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + return SCPE_OK; +} + +/* initialize the disk */ +void hsdp_ini(UNIT *uptr, t_bool f) +{ + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); /* get the UNIT number */ + int i = GET_TYPE(uptr->flags); + int cn; + + /* start out at sector 0 */ + uptr->CHS = 0; /* set CHS to cyl/hd/sec = 0 */ + uptr->CMD &= LMASK; /* clear out the flags but leave ch/sa */ + /* total sectors on disk */ + uptr->capac = CAP(i); /* size in sectors */ + sim_cancel(uptr); /* stop any timer */ + /* reset track label cache */ + for (cn=0; cnname, GET_UADDR(uptr->CMD), uptr->capac, uptr->capac); +} + +/* handle rschnlio cmds for hsdp */ +t_stat hsdp_rschnlio(UNIT *uptr) { + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & DSK_CMDMSK; + + sim_debug(DEBUG_EXP, dptr, "hsdp_rschnl chsa %04x cmd = %02x\n", chsa, cmd); + hsdp_ini(uptr, 0); /* reset the unit */ + return SCPE_OK; +} + +t_stat hsdp_reset(DEVICE *dptr) +{ + int cn, unit; + + for(unit=0; unit < NUM_UNITS_HSDP; unit++) { + for (cn=0; cnflags); + DEVICE *dptr = get_dev(uptr); + uint32 trk, cyl, sec; + uint32 ssize = SSB(type); /* disk sector size in bytes */ + uint32 tsize = SPT(type); /* get track size in sectors */ + uint32 tot_tracks = TRK(type); /* total tracks on disk */ + uint32 tot_sectors = CAP(type); /* total number of sectors on disk */ + uint32 cap = CAP(type); /* disk capacity in sectors */ + uint32 CHS; /* cyl, hds, sec format */ + uint8 label[34]; /* track/sector label */ + int32 i, j; + /* get sector address of vendor defect table VDT */ + /* put data = 0xf0000004 0xf4000000 */ + int32 vaddr = (CYL(type)-4) * SPC(type) + (HDS(type)-1) * SPT(type); + /* get sector address of utx diag map (DMAP) track 0 pointer */ + /* put data = 0xf0000000 + (cyl-1), 0x8a000000 + daddr, */ + /* 0x9a000000 + (cyl-1), 0xf4000000 */ + int32 daddr = (CYL(type)-4) * SPC(type) + (HDS(type)-2) * SPT(type); + /* make logical */ + int32 logda = daddr*(SPT(type)-1)/(SPT(type)); + + /* get sector address of utx flaw map sec 1 pointer */ + /* use this address for sec 1 label pointer */ + int32 uaddr = (CYL(type)-4) * SPC(type) + (HDS(type)-3) * SPT(type); + /* make logical */ + int32 logua = uaddr*(SPT(type)-1)/(SPT(type)); + + /* write 30 byte track labels for all tracks on disk */ + /* tot_tracks entries will be created starting at end of disk */ + /* seek first sector after end of disk data */ + if ((sim_fseek(uptr->fileref, CAPB(type), SEEK_SET)) != 0) { + sim_debug(DEBUG_CMD, dptr, + "Error seeking track label area at sect %06x offset %06x\n", + CAP(type), CAPB(type)); + return 1; + } + /* write track labels */ + for (i=0; i<(int)tot_tracks; i++) { + + /* zero the Track Label Buffer */ + for (j = 0; j < 30; j++) + label[j] = 0; + + sec = i * SPT(type); /* get track address in sectors */ + /* convert sector number to CHS value for label */ + CHS = hsdpsec2star(sec, type); /* get current CHS value */ + + /* set buf data to current CHS values */ + if (CHS == 0) { /* write last address on trk 0 */ + cyl = CYL(type)-1; /* lcyl cyl upper 8 bits */ + trk = HDS(type)-1; /* ltkn trk */ + sec = SPT(type)-1; /* lid sector ID */ + } else { + /* write current address on other tracks */ + cyl = (CHS >> 16) & 0xffff; /* get the cylinder */ + trk = (CHS >> 8) & 0xff; /* get the track */ + sec = (CHS) & 0xff; /* get the sector */ + } + + sim_debug(DEBUG_CMD, dptr, "hsdp_label WTL STAR %08x disk geom %08x\n", + CHS, GEOM(type)); + + /* set buf data to current STAR values */ + label[0] = (cyl >> 8) & 0xff; /* lcyl cyl upper 8 bits */ + label[1] = cyl & 0xff; /* lcyl cyl lower 8 bits */ + label[2] = trk & 0xff; /* ltkn trk */ + label[3] = sec & 0xff; /* lid sector ID */ + label[4] = 0x80; /* show good sector */ + if (i == (tot_tracks-1)) { /* last track? */ + label[3] = 0xff; /* lid show as last track label */ + label[4] |= 0x04; /* set last track flag */ + } + + sim_debug(DEBUG_CMD, dptr, + "hsdp_label WTL star %02x %02x %02x %02x\n", + label[0], label[1], label[2], label[3]); + + /* prepare track 0 label on disk */ + if (CHS == 0) { /* only write dmap address in trk 0 */ + /* daddr has dmap value for track zero label */ + /* this logical or physical address must be there, */ + /* otherwise prep crashes with bad diag flaw map pointer */ + if (use_strep) { + /* output logical diag defect map address of disk */ + label[12] = (logda >> 24) & 0xff; /* ldeallp DMAP pointer */ + label[13] = (logda >> 16) & 0xff; + label[14] = (logda >> 8) & 0xff; + label[15] = (logda) & 0xff; + printf("hsdp_label WTL logda@daddr %08x -> %08x\r\n", logda, 0); + sim_debug(DEBUG_CMD, dptr, + "hsdp_label WTL logda@daddr %08x -> %08x\n", logda, 0); + } else { + /* output physical diag defect map address of disk */ + label[12] = (daddr >> 24) & 0xff; /* ldeallp DMAP pointer */ + label[13] = (daddr >> 16) & 0xff; + label[14] = (daddr >> 8) & 0xff; + label[15] = (daddr) & 0xff; + printf("hsdp_label WTL daddr@daddr %08x -> %08x\r\n", daddr, 0); + sim_debug(DEBUG_CMD, dptr, + "hsdp_label WTL daddr@daddr %08x -> %08x\n", vaddr, 0); + } + /* if this is removed, UTX is unable to create newfs */ + /* get preposterous size 0 error message */ + /* address must be logical/physical */ + /* uaddr has umap value for track zero label */ + if (use_strep) { + /* output logical umap address */ + label[16] = (logua >> 24) & 0xff; /* lumapp UMAP lofical pointer */ + label[17] = (logua >> 16) & 0xff; + label[18] = (logua >> 8) & 0xff; + label[19] = (logua) & 0xff; + } else { + /* output physical umap address */ + label[16] = (uaddr >> 24) & 0xff; /* lumapp UMAP physical pointer */ + label[17] = (uaddr >> 16) & 0xff; + label[18] = (uaddr >> 8) & 0xff; + label[19] = (uaddr) & 0xff; + } + } + + /* write vaddr to track label for dmap */ + if ((i*SPT(type)) == daddr) { /* get track address in sectors */ + /* output physical vendor defect map address of disk */ + label[12] = (vaddr >> 24) & 0xff; /* Vaddr pointer vdt */ + label[13] = (vaddr >> 16) & 0xff; + label[14] = (vaddr >> 8) & 0xff; + label[15] = (vaddr) & 0xff; + printf("hsdp_label WTL vaddr@vaddr %08x -> %08x\r\n", vaddr, vaddr); + sim_debug(DEBUG_CMD, dptr, + "hsdp_label WTL vaddr@vaddr %08x -> %08x\n", vaddr, vaddr); + } + + /* the tech doc shows the cyl/trk/sec data is in the first 4 bytes */ + /* of the track label, BUT it is really in the configuration data */ + /* area too. Byte 27 is sectors/track and byte 28 is number of heads. */ + /* Byte 26 is mode. Byte 25 is copy of byte 27. */ + label[25] = SPT(type) & 0xff; /* save sectors per track */ + if ((use_strep) && (i == 0)) { + label[25] = (SPT(type)-1) & 0xff; + } + uptr->LSC = label[25]; /* save logical/physical sector count from label */ + label[26] = hsdp_type[type].type & 0xfd; /* zero bits 6 in type byte */ + label[27] = label[25]; /* same as label[25] */ + label[28] = HDS(type) & 0xff; + + if ((sim_fwrite((char *)&label, sizeof(uint8), 30, uptr->fileref)) != 30) { + sim_debug(DEBUG_CMD, dptr, + "Error writing track label to sect %06x offset %06x\n", + cap+(i*tsize), cap*ssize+(i*tsize*ssize)); + return 1; + } + } + + /* write 30 byte sector labels for all sectors on disk */ + /* tot_sector entries will be created starting at end of disk */ + /* plus the track label area size. seek first sector after end */ + /* of disk track label area */ + if ((sim_fseek(uptr->fileref, CAPB(type)+TRK(type)*30, SEEK_SET)) != 0) { + sim_debug(DEBUG_CMD, dptr, + "Error seeking sector label area at sect %06x offset %06x\n", + CAP(type)+TRK(type), CAPB(type)+TRK(type)*30); + return 1; + } + + /* zero the Sector Label Buffer */ + for (j = 0; j < 30; j++) + label[j] = 0; + + /* convert sector number to CHS value for label */ + /* write sector labels */ + for (i=0; i<(int)tot_sectors; i++) { + + CHS = hsdpsec2star(i, type); /* get current CHS value */ + + /* set buf data to current CHS values */ + /* write current address on other tracks */ + cyl = (CHS >> 16) & 0xffff; /* get the cylinder */ + trk = (CHS >> 8) & 0xff; /* get the track */ + sec = (CHS) & 0xff; /* get the sector */ + + sim_debug(DEBUG_CMD, dptr, "hsdp_label WSL STAR %08x disk geom %08x\n", + CHS, GEOM(type)); + + /* set buf data to current STAR values */ + label[0] = (cyl >> 8) & 0xff; /* lcyl cyl upper 8 bits */ + label[1] = cyl & 0xff; /* lcyl cyl lower 8 bits */ + label[2] = trk & 0xff; /* ltkn trk */ + label[3] = sec & 0xff; /* lid sector ID */ + label[4] = 0x80; /* show good sector */ + + sim_debug(DEBUG_CMD, dptr, + "hsdp_label WSL star %02x %02x %02x %02x\n", + label[0], label[1], label[2], label[3]); + + label[12] = 0; + label[13] = 0; + label[14] = 0; + label[15] = 0; + label[16] = 0; + label[17] = 0; + label[18] = 0; + label[19] = 0; + + /* if this is written, UTX will not be able to do a newfs */ + /* gets preposterous size 0 error */ + /* the tech doc shows the cyl/trk/sec data is in the first 4 bytes */ + /* of the track label, BUT it is really in the configuration data */ + /* area too. Byte 27 is sectors/track and byte 28 is number of heads. */ + /* Byte 26 is mode. Byte 25 is copy of byte 27. */ + label[25] = hsdp_type[type].spt & 0xff; + /* The UDP/DPII controllers do not use these bits, so UTX keys */ + /* on these bits to determine type of controller. Bit 31 is set */ + /* for a HSDP and not set for the UDP/DPII */ + label[26] = hsdp_type[type].type & 0xfd; /* zero bits 6 & leave 7 in type byte */ + label[27] = hsdp_type[type].spt & 0xff; + label[28] = hsdp_type[type].nhds & 0xff; + + if ((sim_fwrite((char *)&label, sizeof(uint8), 30, uptr->fileref)) != 30) { + sim_debug(DEBUG_CMD, dptr, + "Error writing sector label to sect %06x offset %06x\n", + i, CAPB(type)+TRK(type)*30+i*ssize); + return 1; + } + } + + /* seek home again */ + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + fprintf (stderr, "Error on seek to 0\r\n"); + return 1; + } + return SCPE_OK; /* good to go */ +} + +/* create the disk file for the specified device */ +int hsdp_format(UNIT *uptr) { + int type = GET_TYPE(uptr->flags); + DEVICE *dptr = get_dev(uptr); + uint32 ssize = SSB(type); /* disk sector size in bytes */ + uint32 tsize = SPT(type); /* get track size in sectors */ + uint32 csize = SPC(type); /* get cylinder size in sectors */ + uint32 cyl = CYL(type); /* get # cylinders */ + uint32 cap = CAP(type); /* disk capacity in sectors */ + uint32 cylv = cyl; /* number of cylinders */ + uint8 *buff; + int i; + int use_st_format = 1; /* assume we use s/t replacement */ + t_stat oldsw = sim_switches; /* save switches */ + + /* last sector address of disk (cyl * hds * spt) - 1 */ + uint32 laddr = CAP(type) - 1; /* last sector of disk */ + + /* get sector address of vendor defect table VDT */ + /* put data = 0xf0000004 0xf4000000 */ + int32 vaddr = (CYL(type)-4) * SPC(type) + (HDS(type)-1) * SPT(type); + /* get sector address of utx diag map (DMAP) track 0 pointer */ + /* put data = 0xf0000000 + (cyl-1), 0x8a000000 + daddr, */ + /* 0x9a000000 + (cyl-1), 0xf4000000 */ + int32 daddr = (CYL(type)-4) * SPC(type) + (HDS(type)-2) * SPT(type); + /* make logical */ + int32 logda = daddr*(SPT(type)-1)/(SPT(type)); + + int32 uaddr = (CYL(type)-4) * SPC(type) + (HDS(type)-3) * SPT(type); + + /* NULL vendor flaw map */ + uint32 vmap[2] = {0xf0000004, 0xf4000000}; + + /* NULL diag flaw map */ + uint32 pdmap[4] = {0xf0000000 | (cap-1), 0x8a000000 | daddr, + 0x9a000000 | (cap-1), 0xf4000000}; + + uint32 dmap[4] = {0xf0000000 | (((cap-1)*(SPT(type)-1))/(SPT(type))), + 0x8a000000 | logda, + 0x9a000000 | (((cap-1)*(SPT(type)-1))/(SPT(type))), 0xf4000000}; + + /* see if -i or -n specified on attach command */ + if (!(sim_switches & SWMASK('N')) && !(sim_switches & SWMASK('I'))) { + sim_switches = 0; /* simh tests 'N' & 'Y' switches */ + /* see if user wants to initialize the disk */ + if (!get_yn("Initialize disk? [Y] ", TRUE)) { + sim_switches = oldsw; + return 1; + } + } + if (!get_yn("Use Sector/Track replacement format? [N] ", FALSE)) { + use_st_format = 0; /* do not use s/t replacement */ + } + sim_switches = oldsw; /* restore switches */ + + /* get physical sector address of media defect table */ + /* VDT 286965 (819/9/0) 0x460f5 for 8887 - 823/10/35 */ + /* MDT 286930 (819/8/0) 0x460d2 for 8887 - 823/10/35 Trk 0 ptr */ + /* FMAP 286895 (819/7/0) 0x460af for 8887 - 823/10/35 */ + /* UMAP 286860 (819/6/0) 0x4608c for 8887 - 823/10/35 */ + + /* get logical sector address of media defect table */ + /* VDT 278766 (819/9/0) 0x440ee for 8887 - 823/10/34 */ + /* MDT 278732 (819/8/0) 0x440cc for 8887 - 823/10/34 */ + /* FMAP 278698 (819/7/0) 0x440aa for 8887 - 823/10/34 Sec 0 ptr */ + /* UMAP 278664 (819/6/0) 0x44088 for 8887 - 823/10/34 Sec 0 ptr */ + + /* seek to sector 0 */ + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + fprintf (stderr, "Error on seek to 0\r\n"); + return 1; + } + + /* get buffer for track data in bytes */ + if ((buff = (uint8 *)calloc(csize*ssize, sizeof(uint8))) == 0) { + detach_unit(uptr); + return SCPE_ARG; + } + sim_debug(DEBUG_CMD, dptr, + "Creating disk file of trk size %04x bytes, capacity %d\n", + tsize*ssize, cap*ssize); + + /* write zeros to each track of the disk */ + for (cyl = 0; cyl < cylv; cyl++) { + if ((sim_fwrite(buff, 1, csize*ssize, uptr->fileref)) != csize*ssize) { + sim_debug(DEBUG_CMD, dptr, + "Error on write to diskfile cyl %04x\n", cyl); + free(buff); /* free cylinder buffer */ + buff = NULL; + return 1; + } + if ((cyl % 100) == 0) + fputc('.', stderr); + } + fputc('\r', stderr); + fputc('\n', stderr); + free(buff); /* free cylinder buffer */ + buff = NULL; + + /* byte swap the buffers for dmap and umap */ + for (i=0; i<2; i++) { + vmap[i] = (((vmap[i] & 0xff) << 24) | ((vmap[i] & 0xff00) << 8) | + ((vmap[i] & 0xff0000) >> 8) | ((vmap[i] >> 24) & 0xff)); + } + for (i=0; i<4; i++) { + dmap[i] = (((dmap[i] & 0xff) << 24) | ((dmap[i] & 0xff00) << 8) | + ((dmap[i] & 0xff0000) >> 8) | ((dmap[i] >> 24) & 0xff)); + } + for (i=0; i<4; i++) { + pdmap[i] = (((pdmap[i] & 0xff) << 24) | ((pdmap[i] & 0xff00) << 8) | + ((pdmap[i] & 0xff0000) >> 8) | ((pdmap[i] >> 24) & 0xff)); + } + + /* now seek to end of disk and write the dmap data */ + /* setup dmap pointed to by track label 0 wd[3] = (cyl-4) * spt + (spt - 1) */ + + /* write dmap data to last sector on disk */ + if ((sim_fseek(uptr->fileref, laddr*ssize, SEEK_SET)) != 0) { /* seek last sector */ + sim_debug(DEBUG_CMD, dptr, + "Error on last sector seek to sect %06x offset %06x\n", + cap-1, (cap-1)*ssize); + return 1; + } + if ((sim_fwrite((char *)&pdmap, sizeof(uint32), 4, uptr->fileref)) != 4) { + sim_debug(DEBUG_CMD, dptr, + "Error writing DMAP to sect %06x offset %06x\n", + cap-1, (cap-1)*ssize); + return 1; + } + + /* seek to vendor label area VMAP */ + if ((sim_fseek(uptr->fileref, vaddr*ssize, SEEK_SET)) != 0) { /* seek VMAP */ + sim_debug(DEBUG_CMD, dptr, + "Error on vendor map seek to sect %06x offset %06x\n", + vaddr, vaddr*ssize); + return 1; + } + if ((sim_fwrite((char *)&vmap, sizeof(uint32), 2, uptr->fileref)) != 2) { + sim_debug(DEBUG_CMD, dptr, + "Error writing VMAP to sect %06x offset %06x\n", + vaddr, vaddr*ssize); + return 1; + } + + /* write DMAP to daddr that is the address in trk 0 label */ + if ((sim_fseek(uptr->fileref, daddr*ssize, SEEK_SET)) != 0) { /* seek DMAP */ + sim_debug(DEBUG_CMD, dptr, + "Error on diag map seek to sect %06x offset %06x\n", + daddr, daddr*ssize); + return 1; + } + if (use_st_format) { + if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) { + sim_debug(DEBUG_CMD, dptr, + "Error writing LDMAP to sect %06x offset %06x\n", + daddr, daddr*ssize); + return 1; + } + } else + { + if ((sim_fwrite((char *)&pdmap, sizeof(uint32), 4, uptr->fileref)) != 4) { + sim_debug(DEBUG_CMD, dptr, + "Error writing DMAP to sect %06x offset %06x\n", + daddr, daddr*ssize); + return 1; + } + } + printf("Disk %s has %x (%d) cyl, %x (%d) hds, %x (%d) sec\r\n", + hsdp_type[type].name, CYL(type), CYL(type), HDS(type), HDS(type), + SPT(type), SPT(type)); + printf("writing to vmap sec %x (%d) bytes %x (%d)\r\n", + vaddr, vaddr, (vaddr)*ssize, (vaddr)*ssize); + printf("writing to dmap %x (%d) %x (%d) dmap to %x (%d) %x (%d)\r\n", + cap-1, cap-1, (cap-1)*ssize, (cap-1)*ssize, + daddr, daddr, daddr*ssize, daddr*ssize); + printf("writing to umap sec %x (%d) bytes %x (%d)\r\n", + uaddr, uaddr, (uaddr)*ssize, (uaddr)*ssize); + + /* create labels for disk */ + i = hsdp_label(uptr, use_st_format); /* label disk */ + + /* seek home again */ + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + fprintf (stderr, "Error on seek to 0\r\n"); + return 1; + } + return SCPE_OK; +} + +/* attach the selected file to the disk */ +t_stat hsdp_attach(UNIT *uptr, CONST char *file) +{ + uint16 chsa = GET_UADDR(uptr->CMD); + CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */ + int type = GET_TYPE(uptr->flags); + DEVICE *dptr = get_dev(uptr); + DIB *dibp = 0; + t_stat r, s; + uint32 ssize; /* sector size in bytes */ + uint32 info, good; + uint8 buff[1024]; + int i, j; + int use_st_format = 0; + + /* last sector address of disk (cyl * hds * spt) - 1 */ + int32 laddr = CAP(type) - 1; /* last sector of disk */ + /* get sector address of utx diag map (DMAP) track 0 pointer */ + /* put data = 0xf0000000 + (cyl-1), 0x8a000000 + daddr, */ + /* 0x9a000000 + (cyl-1), 0xf4000000 */ + int32 daddr = (CYL(type)-4) * SPC(type) + (HDS(type)-2) * SPT(type); + int32 umapaddr = (CYL(type)-4) * SPC(type) + (HDS(type)-3) * SPT(type); + /* defect map */ + uint32 dmap[4] = {0xf0000000 | (CAP(type)-1), 0x8a000000 | daddr, + 0x9a000000 | (CAP(type)-1), 0xf4000000}; + + for (i=0; i<4; i++) { /* byte swap data for last sector */ + dmap[i] = (((dmap[i] & 0xff) << 24) | ((dmap[i] & 0xff00) << 8) | + ((dmap[i] & 0xff0000) >> 8) | ((dmap[i] >> 24) & 0xff)); + } + + /* see if valid disk entry */ + if (hsdp_type[type].name == 0) { /* does the assigned disk have a name */ + detach_unit(uptr); /* no, reject */ + return SCPE_FMT; /* error */ + } + + if (dptr->flags & DEV_DIS) { + fprintf(sim_deb, + "ERROR===ERROR\nHSDP device %s disabled on system, aborting\r\n", + dptr->name); + printf("ERROR===ERROR\nHSDP device %s disabled on system, aborting\r\n", + dptr->name); + return SCPE_UDIS; /* device disabled */ + } + + /* have simulator attach the file to the unit */ + if ((r = attach_unit(uptr, file)) != SCPE_OK) + return r; + + uptr->capac = CAP(type); /* size in sectors */ + ssize = SSB(type); /* disk sector size in bytes */ + for (i=0; i<(int)ssize; i++) + buff[i] = 0; /* zero the buffer */ + + sim_debug(DEBUG_CMD, dptr, + "Disk %s cyl %d hds %d sec %d ssiz %d capacity %d\n", + hsdp_type[type].name, hsdp_type[type].cyl, hsdp_type[type].nhds, + hsdp_type[type].spt, ssize, uptr->capac); /* hsdp capacity */ + + printf("Disk %s cyl %d hds %d sec %d ssiz %d capacity %d\r\n", + hsdp_type[type].name, hsdp_type[type].cyl, hsdp_type[type].nhds, + hsdp_type[type].spt, ssize, uptr->capac); /* hsdp capacity */ + + /* see if -i or -n specified on attach command */ + if ((sim_switches & SWMASK('N')) || (sim_switches & SWMASK('I'))) { + goto fmt; /* user wants new disk */ + } + + /* seek to end of disk */ + if ((sim_fseek(uptr->fileref, 0, SEEK_END)) != 0) { + sim_debug(DEBUG_CMD, dptr, "HSDP Disk attach SEEK end failed\n"); + printf("Disk attach SEEK end failed\r\n"); + goto fmt; /* not setup, go format */ + } + + s = ftell(uptr->fileref); /* get current file position */ + if (s == 0) { + sim_debug(DEBUG_CMD, dptr, "HSDP Disk attach ftell failed s=%06d\n", s); + printf("HSDP Disk attach ftell failed s=%06d\r\n", s); + goto fmt; /* not setup, go format */ + } + + if (((int)s/(int)ssize) < ((int)CAP(type))) { /* full sized disk? */ + j = (CAP(type) - (s/ssize)); /* get # sectors to write */ + sim_debug(DEBUG_CMD, dptr, + "Disk attach for MPX 1.X needs %04d more sectors added to disk\n", j); + printf("Disk attach for MPX 1.X needs %04d more sectors added to disk\r\n", j); + /* must be MPX 1.X disk, extend to MPX 3.X size */ + /* write sectors of zero to end of disk to fill it out */ + for (i=0; ifileref) != ssize)) { + sim_debug(DEBUG_CMD, dptr, "Disk attach fread ret = %04d\n", r); + printf("Disk attach fread ret = %04d\r\n", r); + goto fmt; /* not setup, go format */ + } + } + s = ftell(uptr->fileref); /* get current file position */ + sim_debug(DEBUG_CMD, dptr, + "Disk attach MPX 1.X file extended & sized secs %06d bytes %06d\n", s/ssize, s); + printf("Disk attach MPX 1.X file extended & sized secs %06d bytes %06d\r\n", s/ssize, s); + } + + /* seek last sector of disk */ + if ((sim_fseek(uptr->fileref, (CAP(type)-1)*ssize, SEEK_SET)) != 0) { + sim_debug(DEBUG_CMD, dptr, "HSDP Disk attach SEEK last sector failed\n"); + printf("HSDP Disk attach SEEK last sector failed\r\n"); + goto fmt; + } + + /* see if there is disk size-1 in last sector of disk, if not add it */ + if ((r = sim_fread(buff, sizeof(uint8), ssize, uptr->fileref) != ssize)) { + sim_debug(DEBUG_CMD, dptr, "HSDP Disk format fread error = %04d\n", r); + printf("HSDP Disk format fread error = %04d\r\n", r); +add_size: + /* write dmap data to last sector on disk for mpx 1.x */ + if ((sim_fseek(uptr->fileref, laddr*ssize, SEEK_SET)) != 0) { /* seek last sector */ + sim_debug(DEBUG_CMD, dptr, + "Disk Error on last sector seek to sect %06d offset %06d bytes\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + printf("Disk Error on last sector seek to sect %06d offset %06d bytes\r\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + goto fmt; + } + if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) { + sim_debug(DEBUG_CMD, dptr, + "Disk Error writing DMAP to sect %06x offset %06d bytes\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + printf("Disk Error writing DMAP to sect %06x offset %06d bytes\r\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + goto fmt; + } + + /* seek last sector of disk */ + if ((sim_fseek(uptr->fileref, (CAP(type))*ssize, SEEK_SET)) != 0) { + sim_debug(DEBUG_CMD, dptr, "Disk attach SEEK last sector failed\n"); + printf( "Disk attach SEEK last sector failed\r\n"); + goto fmt; + } + s = ftell(uptr->fileref); /* get current file position */ + sim_debug(DEBUG_CMD, dptr, + "HSDP Disk attach MPX file extended & sized secs %06d bytes %06d\n", s/ssize, s); + printf("HSDP Disk attach MPX file extended & sized secs %06d bytes %06d\r\n", s/ssize, s); + goto ldone; + } else { + /* if not disk size, go add it in for MPX, error if UTX */ + if ((buff[0] | buff[1] | buff[2] | buff[3]) == 0) { + sim_debug(DEBUG_CMD, dptr, + "HSDP Disk format0 buf0 %02x buf1 %02x buf2 %02x buf3 %02x\n", + buff[0], buff[1], buff[2], buff[3]); + goto add_size; + } + } + + info = (buff[0]<<24) | (buff[1]<<16) | (buff[2]<<8) | buff[3]; + good = 0xf0000000 | (CAP(type)-1); + /* check for 0xf0ssssss where ssssss is disk size-1 in sectors */ + if (info != good) { + sim_debug(DEBUG_CMD, dptr, + "Disk format error buf0 %02x buf1 %02x buf2 %02x buf3 %02x\n", + buff[0], buff[1], buff[2], buff[3]); + printf("Disk format error buf0 %02x buf1 %02x buf2 %02x buf3 %02x\r\n", + buff[0], buff[1], buff[2], buff[3]); +fmt: + /* format the drive */ + if (hsdp_format(uptr)) { + detach_unit(uptr); /* if no space, error */ + return SCPE_FMT; /* error */ + } + } + +ldone: + /* see if disk has labels already, seek to sector past end of disk */ + if ((sim_fseek(uptr->fileref, CAP(type)*ssize, SEEK_SET)) != 0) { /* seek end */ + detach_unit(uptr); /* detach if error */ + return SCPE_FMT; /* error */ + } + i = SCPE_OK; + /* see if disk has labels already, seek to sector past end of disk */ + if ((r = sim_fread(buff, sizeof(uint8), 30, uptr->fileref) != 30)) { + use_st_format = 1; + /* the disk does not have labels, add them on */ + /* create labels for disk */ + sim_debug(DEBUG_CMD, dptr, + "File %s attached to %s creating labels\n", + file, hsdp_type[type].name); + printf("File %s attached to %s creating labels\r\n", + file, hsdp_type[type].name); + if (!get_yn("Use Sector/Track replacement format for labels? [Y] ", TRUE)) { + use_st_format = 0; + } + /* create labels for disk */ + i = hsdp_label(uptr, use_st_format); /* label disk */ + if (i != 0) { + detach_unit(uptr); /* detach if error */ + return SCPE_FMT; /* error */ + } + } + + /* see if disk has labels already, seek to sector past end of disk */ + if ((sim_fseek(uptr->fileref, CAP(type)*ssize, SEEK_SET)) != 0) { /* seek end */ + detach_unit(uptr); /* detach if error */ + return SCPE_FMT; /* error */ + } + /* see if disk has labels already, read label for track 0 */ + if ((r = sim_fread(buff, sizeof(uint8), 30, uptr->fileref) != 30)) { + detach_unit(uptr); /* detach if error */ + return SCPE_FMT; /* error */ + } + uptr->LSC = buff[25]; /* save logical sector count from label */ + + /* UTX map (NUMP) does not insert an F4 after the replacement tracks */ + /* so do it after the tracks are defined to stop halt on bootup */ + /* utxmap + 32 + 88 + (3*spare) + 1 */ + /* spare count is at utxmap + 8w (32) */ + /* get umap sector address from umapaddr */ + info = (buff[16]<<24) | (buff[17]<<16) | (buff[18]<<8) | buff[19]; + daddr = umapaddr * ssize; /* byte offset in file */ + if ((sim_fseek(uptr->fileref, umapaddr*ssize, SEEK_SET)) != 0) { /* seek umap */ + detach_unit(uptr); /* detach if error */ + return SCPE_FMT; /* error */ + } + /* read umap into buff */ + if ((r = sim_fread(buff, sizeof(uint8), ssize, uptr->fileref) != ssize)) { + detach_unit(uptr); /* detach if error */ + return SCPE_FMT; /* error */ + } + info = (buff[0]<<24) | (buff[1]<<16) | (buff[2]<<8) | buff[3]; + good = 0x4e554d50; /* NUMP */ + if (info == good) { + /* we have a UTX umap, so fixup the data */ + if (buff[35] <= SPT(type)) + i = (127) + (buff[35]*12); /* byte offset to where to put 0xf4 */ + else + i = 127; /* only 1 track of replacement */ + buff[i] = 0xf4; /* set stop for UTX search */ + if ((sim_fseek(uptr->fileref, umapaddr*ssize, SEEK_SET)) != 0) { /* seek umap */ + detach_unit(uptr); /* detach if error */ + return SCPE_FMT; /* error */ + } + if ((r = sim_fwrite(buff, sizeof(uint8), ssize, uptr->fileref) != ssize)) { + detach_unit(uptr); /* detach if error */ + return SCPE_FMT; /* error */ + } + } + + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + detach_unit(uptr); /* detach if error */ + return SCPE_FMT; /* error */ + } + + /* start out at sector 0 */ + uptr->CHS = 0; /* set CHS to cyl/hd/sec = 0 */ + + if (uptr->LSC == SPT(type)) { + /* physical sectoring */ + sim_debug(DEBUG_CMD, dptr, + "HSDP PHY %02x Attach %s cyl %d hds %d pspt %d pspc %d cap sec %d cap bytes %d\n", + uptr->LSC, hsdp_type[type].name, CYL(type), HDS(type), SPT(type), SPC(type), + CAP(type), CAPB(type)); + printf("HSDP PHY %02x Attach %s cyl %d hds %d pspt %d pspc %d cap sec %d cap bytes %d\r\n", + uptr->LSC, hsdp_type[type].name, CYL(type), HDS(type), SPT(type), SPC(type), + CAP(type), CAPB(type)); + } else { + /* logical sectoring */ + sim_debug(DEBUG_CMD, dptr, + "HSDP LSF %02x Attach %s cyl %d hds %d lspt %d lspc %d cap sec %d cap bytes %d\n", + uptr->LSC, hsdp_type[type].name, CYL(type), HDS(type), SPT(type)-1, (SPT(type)-1)*HDS(type), + (CYL(type)*HDS(type)*(SPT(type)-1)), (CYL(type)*HDS(type)*(SPT(type)-1))*ssize); + printf("HSDP LSF %02x Attach %s cyl %d hds %d lspt %d lspc %d cap sec %d cap bytes %d\r\n", + uptr->LSC, hsdp_type[type].name, CYL(type), HDS(type), SPT(type)-1, (SPT(type)-1)*HDS(type), + (CYL(type)*HDS(type)*(SPT(type)-1)), (CYL(type)*HDS(type)*(SPT(type)-1))*ssize); + } + + sim_debug(DEBUG_CMD, dptr, + "HSDP File %s attached to %s\n", + file, hsdp_type[type].name); + printf("HSDP File %s attached to %s\r\n", + file, hsdp_type[type].name); + + /* check for valid configured disk */ + /* must have valid DIB and Channel Program pointer */ + dibp = (DIB *)dptr->ctxt; /* get the DIB pointer */ + if ((dib_unit[chsa] == NULL) || (dibp == NULL) || (chp == NULL)) { + sim_debug(DEBUG_CMD, dptr, + "ERROR===ERROR\nHSDP device %s not configured on system, aborting\n", + dptr->name); + printf("ERROR===ERROR\nHSDP device %s not configured on system, aborting\r\n", + dptr->name); + detach_unit(uptr); /* detach if error */ + return SCPE_UNATT; /* error */ + } + set_devattn(chsa, SNS_DEVEND); + return SCPE_OK; +} + +/* detach a disk device */ +t_stat hsdp_detach(UNIT *uptr) { + uptr->SNS = 0; /* clear sense data */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + return detach_unit(uptr); /* tell simh we are done with disk */ +} + +/* boot from the specified disk unit */ +t_stat hsdp_boot(int32 unit_num, DEVICE * dptr) { + UNIT *uptr = &dptr->units[unit_num]; /* find disk unit number */ + + sim_debug(DEBUG_CMD, dptr, "HSDP Boot dev/unit %x\n", GET_UADDR(uptr->CMD)); + + /* see if device disabled */ + if (dptr->flags & DEV_DIS) { + printf("ERROR===ERROR\r\nHSDP device %s disabled on system, aborting\r\n", + dptr->name); + return SCPE_UDIS; /* device disabled */ + } + + if ((uptr->flags & UNIT_ATT) == 0) { + sim_debug(DEBUG_EXP, dptr, "HSDP Boot attach error dev/unit %04x\n", + GET_UADDR(uptr->CMD)); + printf("HSDP Boot attach error dev/unit %04x\n", GET_UADDR(uptr->CMD)); + return SCPE_UNATT; /* attached? */ + } + SPAD[0xf4] = GET_UADDR(uptr->CMD); /* put boot device chan/sa into spad */ + SPAD[0xf8] = 0xF000; /* show as F class device */ + + /* now boot the disk */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + return chan_boot(GET_UADDR(uptr->CMD), dptr); /* boot the ch/sa */ +} + +/* Disk option setting commands */ +/* set the disk type attached to unit */ +t_stat hsdp_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int i; + + if (cptr == NULL) /* any disk name input? */ + return SCPE_ARG; /* arg error */ + if (uptr == NULL) /* valid unit? */ + return SCPE_IERR; /* no, error */ + if (uptr->flags & UNIT_ATT) /* is unit attached? */ + return SCPE_ALATT; /* no, error */ + + /* now loop through the units and find named disk */ + for (i = 0; hsdp_type[i].name != 0; i++) { + if (strcmp(hsdp_type[i].name, cptr) == 0) { + uptr->flags &= ~UNIT_TYPE; /* clear old type */ + uptr->flags |= SET_TYPE(i); /* set new type */ + /* set capacity of disk in sectors */ + uptr->capac = CAP(i); + return SCPE_OK; + } + } + return SCPE_ARG; +} + +t_stat hsdp_get_type(FILE *st, UNIT * uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fputs("TYPE=", st); + fputs(hsdp_type[GET_TYPE(uptr->flags)].name, st); + return SCPE_OK; +} + +/* help information for disk */ +t_stat hsdp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + int i; + fprintf (st, "SEL 8064 High Speed Disk Processor\r\n"); + fprintf (st, "Use:\r\n"); + fprintf (st, " sim> SET %sn TYPE=type\r\n", dptr->name); + fprintf (st, "Type can be: "); + for (i = 0; hsdp_type[i].name != 0; i++) { + fprintf(st, "%s", hsdp_type[i].name); + if (hsdp_type[i+1].name != 0) + fprintf(st, ", "); + } + fprintf (st, ".\nEach drive has the following storage capacity:\r\n"); + for (i = 0; hsdp_type[i].name != 0; i++) { + int32 size = CAPB(i); /* disk capacity in bytes */ + size /= 1024; /* make KB */ + size = (10 * size) / 1024; /* size in MB * 10 */ + fprintf(st, " %-8s %4d.%1d MB cyl %3d hds %3d sec %3d blk %3d\r\n", + hsdp_type[i].name, size/10, size%10, CYL(i), HDS(i), SPT(i), SSB(i)); + } + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char *hsdp_description (DEVICE *dptr) +{ + return "SEL 8064 High Speed Disk Processor"; +} + +#endif diff --git a/SEL32/sel32_iop.c b/SEL32/sel32_iop.c new file mode 100644 index 00000000..707de588 --- /dev/null +++ b/SEL32/sel32_iop.c @@ -0,0 +1,301 @@ +/* sel32_iop.c: SEL-32 Model 8000/8001/8002 IOP processor controller + + Copyright (c) 2018-2022, James C. Bevier + + 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 + JAMES C. BEVIER 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. + + This channel is the interrupt fielder for all of the IOP sub channels. It's + channel address is 7E00. This code handles the INCH command for the IOP + devices and controls the status FIFO for the iop devices on interrupts and + TIO instructions.. + + Possible devices: + The f8iop communication controller (TY7EA0), (TY7EB0), (TY7EC0) + The ctiop console communications controller (CT7EFC & CT7EFD) + The lpiop line printer controller (LP7EF8), (LP7EF9) + +*/ + +#include "sel32_defs.h" + +#if NUM_DEVS_IOP > 0 + +#define UNIT_IOP UNIT_IDLE | UNIT_DISABLE + +/* forward definitions */ +t_stat iop_preio(UNIT *uptr, uint16 chan); +t_stat iop_startcmd(UNIT *uptr, uint16 chan, uint8 cmd); +void iop_ini(UNIT *uptr, t_bool f); +t_stat iop_rschnlio(UNIT *uptr); +t_stat iop_srv(UNIT *uptr); +t_stat iop_reset(DEVICE *dptr); +t_stat iop_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *iop_desc(DEVICE *dptr); + +/* Held in u3 is the device command and status */ +#define IOP_INCH 0x00 /* Initialize channel command */ +#define IOP_INCH2 0xf0 /* Initialize channel command after start */ +#define IOP_NOP 0x03 /* NOP command */ +#define IOP_MSK 0xff /* Command mask */ + +/* Status held in u3 */ +/* controller/unit address in upper 16 bits */ +#define CON_INPUT 0x100 /* Input ready for unit */ +#define CON_CR 0x200 /* Output at beginning of line */ +#define CON_REQ 0x400 /* Request key pressed */ +#define CON_EKO 0x800 /* Echo input character */ +#define CON_OUTPUT 0x1000 /* Output ready for unit */ +#define CON_READ 0x2000 /* Read mode selected */ + +/* not used u4 */ + +/* in u5 packs sense byte 0,1 and 3 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x80000000 /* Command reject */ +#define SNS_INTVENT 0x40000000 /* Unit intervention required */ +/* sense byte 3 */ +#define SNS_RDY 0x80 /* device ready */ +#define SNS_ONLN 0x40 /* device online */ + +/* std devices. data structures + iop_dev Console device descriptor + iop_unit Console unit descriptor + iop_reg Console register list + iop_mod Console modifiers list +*/ + +struct _iop_data +{ + uint8 ibuff[145]; /* Input line buffer */ + uint8 incnt; /* char count */ +} +iop_data[NUM_UNITS_IOP]; + +/* channel program information */ +CHANP iop_chp[NUM_UNITS_IOP] = {0}; + +MTAB iop_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", + &set_dev_addr, &show_dev_addr, NULL, "Controller Channel address"}, + {0} +}; + +UNIT iop_unit[] = { + {UDATA(&iop_srv, UNIT_IOP, 0), 0, UNIT_ADDR(0x7E00)}, /* Channel controller */ +}; + +DIB iop_dib = { + iop_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + iop_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command SIO */ + NULL, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O HIO */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O HIO */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O TIO */ + NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + iop_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + NULL, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + iop_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + iop_unit, /* UNIT* units */ /* Pointer to units structure */ + iop_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_UNITS_IOP, /* uint8 numunits */ /* number of units defined */ + 0xff, /* uint8 mask */ /* 16 devices - device mask */ + 0x7e00, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +DEVICE iop_dev = { + "IOP", iop_unit, NULL, iop_mod, + NUM_UNITS_IOP, 8, 15, 1, 8, 8, + NULL, NULL, &iop_reset, /* examine, deposit, reset */ + NULL, NULL, NULL, /* boot, attach, detach */ + /* dib ptr, dev flags, debug flags, debug */ + &iop_dib, DEV_CHAN|DEV_DIS|DEV_DISABLE|DEV_DEBUG, 0, dev_debug, +}; + +/* IOP controller routines */ +/* initialize the console chan/unit */ +void iop_ini(UNIT *uptr, t_bool f) +{ + int unit = (uptr - iop_unit); /* unit 0 */ + DEVICE *dptr = &iop_dev; /* one and only dummy device */ + + iop_data[unit].incnt = 0; /* no input data */ + uptr->u5 = SNS_RDY|SNS_ONLN; /* status is online & ready */ + sim_cancel(uptr); /* stop any timers */ + sim_debug(DEBUG_CMD, &iop_dev, + "IOP init device %s controller/device %04x\n", + dptr->name, GET_UADDR(uptr->u3)); +} + +/* handle rschnlio cmds for iop */ +t_stat iop_rschnlio(UNIT *uptr) { + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->u3); + int cmd = uptr->u3 & IOP_MSK; + + sim_debug(DEBUG_EXP, dptr, + "iop_rschnl chsa %04x cmd = %02x\n", chsa, cmd); + iop_ini(uptr, 0); /* reset the unit */ + return SCPE_OK; +} + +/* start an iop operation */ +t_stat iop_preio(UNIT *uptr, uint16 chan) { + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); + uint16 chsa = GET_UADDR(uptr->u3); + + sim_debug(DEBUG_CMD, dptr, "iop_preio CMD %08x unit %02x chsa %04x\n", + uptr->u3, unit, chsa); + + if ((uptr->u3 & IOP_MSK) != 0) { /* is unit busy */ + sim_debug(DEBUG_CMD, dptr, + "iop_preio unit %02x chsa %04x BUSY\n", unit, chsa); + return SNS_BSY; /* yes, return busy */ + } + + sim_debug(DEBUG_CMD, dptr, "iop_preio unit %02x chsa %04x OK\n", unit, chsa); + return SCPE_OK; /* good to go */ +} + +/* start an I/O operation */ +t_stat iop_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) +{ + sim_debug(DEBUG_CMD, &iop_dev, + "IOP startcmd %02x controller/device %04x\n", + cmd, GET_UADDR(uptr->u3)); + if ((uptr->u3 & IOP_MSK) != 0) /* is unit busy */ + return SNS_BSY; /* yes, return busy */ + + /* process the commands */ + switch (cmd & 0xFF) { + /* UTX uses the INCH cmd to detect the IOP or MFP */ + /* IOP has INCH cmd of 0, while MFP uses 0x80 */ + case IOP_INCH: /* INCH command */ + uptr->u5 = SNS_RDY|SNS_ONLN; /* status is online & ready */ + uptr->u3 &= LMASK; /* leave only chsa */ + sim_debug(DEBUG_CMD, &iop_dev, + "iop_startcmd %04x: Cmd INCH iptr %06x INCHa %06x\n", + chan, iop_chp[0].ccw_addr, /* set inch buffer addr */ + iop_chp[0].chan_inch_addr); /* set inch buffer addr */ + + iop_chp[0].chan_inch_addr = iop_chp[0].ccw_addr; /* set inch buffer addr */ + iop_chp[0].base_inch_addr = iop_chp[0].ccw_addr; /* set inch buffer addr */ + iop_chp[0].max_inch_addr = iop_chp[0].ccw_addr + (128 * 8); /* set last inch buffer addr */ + + uptr->u3 |= IOP_INCH2; /* save INCH command as 0xf0 */ + sim_activate(uptr, 40); /* go on */ + return 0; /* no status change */ + break; + + case IOP_NOP: /* NOP command */ + sim_debug(DEBUG_CMD, &iop_dev, "iop_startcmd %04x: Cmd NOP\n", chan); + uptr->u5 = SNS_RDY|SNS_ONLN; /* status is online & ready */ + uptr->u3 &= LMASK; /* leave only chsa */ + uptr->u3 |= (cmd & IOP_MSK); /* save NOP command */ + sim_activate(uptr, 40); /* TRY 07-13-19 */ + return 0; /* no status change */ + break; + + default: /* invalid command */ + uptr->u5 |= SNS_CMDREJ; /* command rejected */ + sim_debug(DEBUG_CMD, &iop_dev, "iop_startcmd %04x: Cmd Invalid %02x status %02x\n", + chan, cmd, uptr->u5); + uptr->u3 &= LMASK; /* leave only chsa */ + uptr->u3 |= (cmd & IOP_MSK); /* save command */ + sim_activate(uptr, 40); /* force interrupt */ + return 0; /* no status change */ + break; + } + + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; /* not reachable for now */ +} + +/* Handle transfers for other sub-channels on IOP */ +t_stat iop_srv(UNIT *uptr) +{ + uint16 chsa = GET_UADDR(uptr->u3); + int cmd = uptr->u3 & IOP_MSK; + CHANP *chp = &iop_chp[0]; /* find the chanp pointer */ + uint32 mema = chp->ccw_addr; /* get inch or buffer addr */ + uint32 tstart; + + /* test for NOP or INCH cmds */ + if ((cmd != IOP_NOP) && (cmd != IOP_INCH2)) { /* NOP or INCH */ + uptr->u3 &= LMASK; /* nothing left, command complete */ + sim_debug(DEBUG_CMD, &iop_dev, + "iop_srv Unknown cmd %02x chan %02x: chnend|devend|unitexp\n", cmd, chsa); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); /* done */ + return SCPE_OK; + } else + + if (cmd == IOP_NOP) { /* NOP do nothing */ + uptr->u3 &= LMASK; /* nothing left, command complete */ + sim_debug(DEBUG_CMD, &iop_dev, "iop_srv INCH/NOP chan %02x: chnend|devend\n", chsa); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* done */ + return SCPE_OK; + } else + + /* test for INCH cmd */ + if (cmd == IOP_INCH2) { /* INCH */ + sim_debug(DEBUG_CMD, &iop_dev, + "iop_srv starting INCH %06x cmd, chsa %04x MemBuf %06x cnt %04x\n", + mema, chsa, chp->ccw_addr, chp->ccw_count); + + /* now call set_inch() function to write and test inch buffer addresses */ + /* the chp->ccw_addr location contains the inch address */ + /* 1-256 wd buffer is provided for 128 status dbl words */ + tstart = set_inch(uptr, mema, 128); /* new address of 128 entries */ + if ((tstart == SCPE_MEM) || (tstart == SCPE_ARG)) { /* any error */ + /* we have error, bail out */ + uptr->u5 |= SNS_CMDREJ; + uptr->u3 &= LMASK; /* nothing left, command complete */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + uptr->u3 &= LMASK; /* clear the cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */ + } + return SCPE_OK; +} + +t_stat iop_reset(DEVICE *dptr) +{ + /* add reset code here */ + return SCPE_OK; +} + +/* sho help iop */ +t_stat iop_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr) +{ + fprintf(st, "SEL-32 IOP Model 8000 Channel Controller at 0x7E00\r\n"); + fprintf(st, "The IOP fields all interrupts and status posting\r\n"); + fprintf(st, "for each of the controllers on the system.\r\n"); + fprintf(st, "Nothing can be configured for this Channel.\r\n"); + return SCPE_OK; +} + +const char *iop_desc(DEVICE *dptr) +{ + return("SEL-32 IOP Model 8000 Channel Controller @ 0x7E00"); +} + +#endif + diff --git a/SEL32/sel32_lpr.c b/SEL32/sel32_lpr.c new file mode 100644 index 00000000..91ea9345 --- /dev/null +++ b/SEL32/sel32_lpr.c @@ -0,0 +1,677 @@ +/* sel32_lpr.c: SEL32 922x & 924x High Speed Line Printer + + Copyright (c) 2018-2022, James C. Bevier + Portions provided by Richard Cornwell and other SIMH contributers + + 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 + JAMES C. BEVIER 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. + + This is the standard line printer. + + These units each buffer one record in local memory and signal + ready when the buffer is full or empty. The channel must be + ready to recieve/transmit data when they are activated since + they will transfer their block during chan_cmd. All data is + transmitted as BCD characters. +*/ + +#include "sel32_defs.h" +#include + +/**** COMMANDS TO PRINT BUFFER THEN DO FORMS CONTROL */ +/* +LP.CMD1 DATAW X'01000000' PRINT ONLY - NO FORMS CONTROL +LP.CMD2 DATAW X'05000000' PRINT BUFFER, +LP.CMD3 DATAW X'15000000' PRINT BUFFER, +LP.CMD4 DATAW X'25000000' PRINT BUFFER, +LP.CMD5 DATAW X'35000000' PRINT BUFFER, +LP.CMD6 DATAW X'45000000' PRINT BUFFER, +LP.CMD7 DATAW X'85000000' PRINT BUFFER, , THEN CLEAR BUFFER +* +**** COMMANDS TO DO FORMS CONTROL AND THEN PRINT BUFFER. +**** NOTE: THESE COMMANDS ARE ARRANGED SO THAT BY USING THE INDEX +**** OF THE FORMS CONTROL TABLE AND A OFFSET INTO THIS TABLE +**** YOU CAN GET THE APPROPRIATE COMMAND FOR THE FC CHAR. +* +LP.CMD8 DATAW X'0D000000' , PRINT BUFFER, +LP.CMD9 DATAW X'4D000000' , PRINT BUFFER, + DATAW X'4D000000' , PRINT BUFFER, +LP.CMD10 DATAW X'2D000000' , PRINT BUFFER +LP.CMD11 DATAW X'1D000000' , PRINT BUFFER, +LP.CMD12 DATAW X'3D000000' , PRINT, (SPARE) +* +**** COMMANDS THAT DO ONLY FORMS CONTROL (NO PRINTING) +* +LP.CMD13 DATAW X'03000000' +LP.CMD14 DATAW X'47000000' + DATAW X'47000000' +LP.CMD15 DATAW X'27000000' +LP.CMD16 DATAW X'17000000' +LP.CMD17 DATAW X'37000000' (SPARE) +* +** LINE PRINTER FORMS CONTROL TABLE +* +LPFCTBL EQU $ + 2B DATAB C'+' 0x2b FORMS CONTROL FOR CR THEN PRINT + 31 DATAB C'1' 0x31 FORMS CONTROL FOR FF THEN PRINT + 2D DATAB C'-' 0x2d FORMS CONTROL FOR FF THEN PRINT + 30 DATAB C'0' 0x30 FORMS CONTROL FOR 2 LF'S THEN PRINT + 20 DATAB C' ' 0x20 FORMS CONTROL FOR LF THEN PRINT +*/ + +#if NUM_DEVS_LPR > 0 + +#define UNIT_LPR UNIT_ATTABLE | UNIT_IDLE | UNIT_DISABLE | UNIT_SEQ + +#define CMD u3 +/* u3 holds command and status information */ + +#define LPR_INCH 0x00 /* INCH command */ +/* print buffer then CC commands */ +#define LPR_PBNCC 0x01 /* print only, no forms control */ +#define LPR_PBC 0x05 /* print buffer, then */ +#define LPR_PBL 0x15 /* print buffer, then */ +#define LPR_PBLL 0x25 /* print buffer, then */ +#define LPR_PBLLL 0x35 /* print buffer, then */ +#define LPR_PBF 0x45 /* print buffer, then */ +#define LPR_PBCCB 0x85 /* print buffer, then */ + /* Do CC then print commands then CC */ +#define LPR_CPBC 0x0d /* print buffer */ +#define LPR_LPBC 0x1d /* print buffer */ +#define LPR_LLPBC 0x2d /* print buffer */ +#define LPR_LLLPBC 0x3d /* print buffer */ +#define LPR_FPBC 0x4d /* print buffer */ + /* Do CC only, no print */ +#define LPR_NPC 0x03 /* */ +#define LPR_NPL 0x17 /* */ +#define LPR_NPLL 0x27 /* */ +#define LPR_NPLLL 0x37 /* */ +#define LPR_NPF 0x47 /* */ + +#define LPR_SNS 0x04 /* Sense command */ +#define LPR_CMDMSK 0xff /* Mask command part. */ +#define LPR_FULL 0x100 /* Buffer full (BOF) */ +#define LPR_PRE 0x200 /* Apply pre CC */ +#define LPR_POST 0x400 /* Apply post CC */ + +#define CNT u4 +/* u4 holds current line count */ + +/* channel status bits 13-15 */ +/* 0x0c - normal completion - OK & carriage is not at bottom of form */ +/* 0x0e - Unit check - Sense error present with SNS_PRINTF status */ +/* 0x0d - Unit exception - OK & carriage is at bottom of form */ + +#define SNS u5 +/* in u5 packs sense byte 0,1 and 3 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x80000000 /* Command reject + Unit check */ +#define SNS_OPRINTR 0x40000000 /* Operator intervention required */ + /* reason code is in status byte 1 */ +#define SNS_BUSCHK 0x20000000 /* Parity error on bus */ +/* bits 3-7 are unused */ +#define SNS_NU3 0x10000000 /* Not used */ +#define SNS_NU4 0x08000000 /* Not used */ +#define SNS_NU5 0x04000000 /* Not used */ +#define SNS_NU6 0x02000000 /* Not used */ +#define SNS_NU7 0x01000000 /* Not used */ +#define SNS_BOF 0x01000000 /* Not used, temp setting for paper at BOT */ +/* Sense byte 1 */ +#define SNS_DEVVFY 0x00800000 /* Device Verify Interface Cable Disconnected */ + /* plus SNS_OPRINTR */ +#define SNS_DEVPWR 0x00400000 /* Device Powered Off + SNS_OPRINTR */ +#define SNS_DEVCHK 0x00200000 /* Device Check - Not Ready + SNS_OPRINTR */ +#define SNS_OFFLINE 0x00100000 /* Off Line + SNS_OPRINTR */ +#define SNS_NU2 0x00080000 /* Not used */ +#define SNS_NU1 0x00040000 /* Not used */ +#define SNS_BEGOF 0x00020000 /* Beginning of form */ +#define SNS_TOF 0x00010000 /* Top of form on printer */ +/* Sense byte 2-3 have remaining channel cnt of zero */ + +#define CBP u6 +/* u6 hold buffer position */ + +/* std devices. data structures + lpr_dev Line Printer device descriptor + lpr_unit Line Printer unit descriptor + lpr_reg Line Printer register list + lpr_mod Line Printer modifiers list +*/ + +struct _lpr_data +{ + uint8 lbuff[160]; /* Output line buffer */ +}; + +struct _lpr_data lpr_data[NUM_DEVS_LPR]; + +/* forward definitions */ +t_stat lpr_preio(UNIT *uptr, uint16 chan); +t_stat lpr_startcmd(UNIT *, uint16, uint8); +void lpr_ini(UNIT *, t_bool); +t_stat lpr_haltio(UNIT *uptr); +t_stat lpr_rschnlio(UNIT *uptr); +t_stat lpr_srv(UNIT *); +t_stat lpr_reset(DEVICE *); +t_stat lpr_attach(UNIT *, CONST char *); +t_stat lpr_detach(UNIT *); +t_stat lpr_setlpp(UNIT *, int32, CONST char *, void *); +t_stat lpr_getlpp(FILE *, UNIT *, int32, CONST void *); +t_stat lpr_help(FILE *, DEVICE *, UNIT *, int32, const char *); +const char *lpr_description (DEVICE *dptr); + +/* channel program information */ +CHANP lpr_chp[NUM_DEVS_LPR] = {0}; + +MTAB lpr_mod[] = { + {MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LINESPERPAGE", "LINESPERPAGE", + &lpr_setlpp, &lpr_getlpp, NULL, "Number of lines per page"}, + {MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, + &show_dev_addr, NULL}, + {0} +}; + +UNIT lpr_unit[] = { + {UDATA(&lpr_srv, UNIT_LPR, 66), 300, UNIT_ADDR(0x7EF8)}, /* A */ +#if NUM_DEVS_LPR > 1 + {UDATA(&lpr_srv, UNIT_LPR, 66), 300, UNIT_ADDR(0x7EF9)}, /* B */ +#endif +}; + +/* Device Information Block */ +DIB lpr_dib = { + lpr_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + lpr_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + lpr_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + lpr_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + NULL, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + lpr_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + lpr_unit, /* UNIT* units */ /* Pointer to units structure */ + lpr_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_DEVS_LPR, /* uint8 numunits */ /* number of units defined */ + 0x01, /* uint8 mask */ /* 2 devices - device mask */ + 0x7e00, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +DEVICE lpr_dev = { + "LPR", lpr_unit, NULL, lpr_mod, + NUM_DEVS_LPR, 8, 15, 1, 8, 8, + NULL, NULL, NULL, NULL, &lpr_attach, &lpr_detach, + /* ctxt is the DIB pointer */ + &lpr_dib, DEV_DISABLE|DEV_DEBUG, 0, dev_debug, +// &lpr_dib, DEV_DISABLE|DEV_DEBUG|DEV_DIS, 0, dev_debug, + NULL, NULL, &lpr_help, NULL, NULL, &lpr_description, +}; + +/* initialize the line printer */ +void lpr_ini(UNIT *uptr, t_bool f) { + uptr->CMD &= ~(LPR_CMDMSK); /* zero cmd */ + sim_cancel(uptr); /* stop any timers */ + uptr->SNS = 0; /* no status */ + uptr->CBP = 0; /* start of buffer */ + uptr->CNT = 0; /* restart line count */ +} + +/* handle rschnlio cmds for lpr */ +t_stat lpr_rschnlio(UNIT *uptr) { + DEVICE *dptr = get_dev(uptr); /* get device pointer */ + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & LPR_CMDMSK; + + sim_debug(DEBUG_EXP, dptr, + "lpr_rschnl chsa %04x cmd = %02x\n", chsa, cmd); + lpr_ini(uptr, 0); /* reset the unit */ + return SCPE_OK; +} + +/* start a line printer operation */ +t_stat lpr_preio(UNIT *uptr, uint16 chan) { + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); + uint16 chsa = GET_UADDR(uptr->CMD); + + sim_debug(DEBUG_CMD, dptr, "lpr_preio CMD %08x unit %02x chsa %04x\n", + uptr->CMD, unit, chsa); + if ((uptr->CMD & LPR_CMDMSK) != 0) { /* just return if busy */ + sim_debug(DEBUG_CMD, dptr, + "lpr_preio unit %02x chsa %04x BUSY\n", unit, chsa); + return SNS_BSY; + } + + sim_debug(DEBUG_CMD, dptr, + "lpr_preio unit %02x chsa %04x OK\n", unit, chsa); + return SCPE_OK; /* good to go */ +} + +/* start an I/O operation */ +t_stat lpr_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) +{ + DEVICE *dptr = get_dev(uptr); /* get device pointer */ + + if ((uptr->CMD & LPR_CMDMSK) != 0) { /* unit busy */ + return SNS_BSY; /* yes, busy (already tested) */ + } + + uptr->CMD &= ~(LPR_POST|LPR_PRE); /* set no CC */ + if (((cmd & 0x03) == 0x03) || (cmd & 0x0f) == 0x0d) { + uptr->CMD |= LPR_PRE; /* apply pre CC */ + } + if (((cmd & 0x0f) == 0x05) || (cmd & 0x0f) == 0x0d) { + uptr->CMD |= LPR_POST; /* apply post CC */ + } + sim_debug(DEBUG_CMD, dptr, "lpr_startcmd Cmd %02x\n", cmd); + + /* process the command */ + switch (cmd & LPR_CMDMSK) { + case 0x00: /* INCH command */ + /* the IOP should already have the inch buffer set, so ignore */ + sim_debug(DEBUG_CMD, dptr, "lpr_startcmd %04x: Cmd INCH\n", chan); + return SNS_CHNEND|SNS_DEVEND; /* all is well */ + break; + + /* No CC */ + case 0x01: /* print only, no forms control */ + /* print buffer then CC commands */ + case 0x05: /* print buffer, then */ + case 0x15: /* print buffer, then */ + case 0x25: /* print buffer, then */ + case 0x35: /* print buffer, then */ + case 0x45: /* print buffer, then */ + case 0x85: /* print buffer, then */ + /* Do CC then print commands then CC */ + case 0x0d: /* print buffer */ + case 0x1d: /* print buffer */ + case 0x2d: /* print buffer */ + case 0x3d: /* print buffer */ + case 0x4d: /* print buffer */ + /* Do CC only, no print */ + case 0x03: /* */ + case 0x17: /* */ + case 0x27: /* */ + case 0x37: /* */ + case 0x47: /* */ + + /* process the command */ + sim_debug(DEBUG_CMD, dptr, + "lpr_startcmd %04x: Cmd %02x print\n", chan, cmd&LPR_CMDMSK); + uptr->CMD &= ~(LPR_CMDMSK); /* zero cmd */ + uptr->CMD |= (cmd & LPR_CMDMSK); /* save new command in CMD */ + sim_activate(uptr, 100); /* Start unit off */ + return 0; /* we are good to go */ + + case 0x4: /* Sense Status */ + sim_debug(DEBUG_CMD, dptr, + "lpr_startcmd %04x: Cmd %02x sense\n", chan, cmd&LPR_CMDMSK); + uptr->CMD &= ~(LPR_CMDMSK); /* zero cmd */ + uptr->CMD |= (cmd & LPR_CMDMSK); /* save new command in CMD */ + sim_activate(uptr, 100); /* Start unit off */ + return 0; /* we are good to go */ + + default: /* invalid command */ + sim_debug(DEBUG_EXP, dptr, + "lpr_startcmd %04x: Cmd %02x INVALID\n", chan, cmd&LPR_CMDMSK); + uptr->SNS |= SNS_CMDREJ; + break; + } + if (uptr->SNS & 0xff) + return SNS_CHNEND|SNS_DEVEND|STATUS_PCHK; + return SNS_CHNEND|SNS_DEVEND; +} + +/* Handle transfer of data for printer */ +t_stat lpr_srv(UNIT *uptr) { + int chsa = GET_UADDR(uptr->CMD); + int u = (uptr - lpr_unit); + int cmd = (uptr->CMD & 0xff); + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + DEVICE *dptr = get_dev(uptr); /* get device pointer */ + + sim_debug(DEBUG_CMD, dptr, + "lpr_srv called chsa %04x cmd %02x CMD %08x addr %06x cnt %04x\n", + chsa, cmd, uptr->CMD, chp->ccw_addr, chp->ccw_count); + + /* using IOP lp status bit assignments */ + if (cmd == 0x04) { /* sense? */ + uint8 ch; /* get current status */ + ch = (uptr->SNS >> 24) & 0xff; /* Get status */ + ch &= ~SNS_BOF; /* remove BOF flag */ + if (chan_write_byte(chsa, &ch)) { /* write byte 0 status to memory */ + sim_debug(DEBUG_CMD, dptr, + "lpr_srv write1 error CMD %08x read %02x SNS %02x ccw_count %02x\n", + uptr->CMD, ch, uptr->SNS, chp->ccw_count); + uptr->CMD &= ~(LPR_CMDMSK); /* clear command */ + uptr->SNS = 0; /* no status */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); /* 4 byte req'd */ + return SCPE_OK; + } + ch = (uptr->SNS >> 16) & 0xff; /* Get status */ + if (chan_write_byte(chsa, &ch)) { /* write the status to memory */ + sim_debug(DEBUG_CMD, dptr, + "lpr_srv write2 error CMD %08x read %02x SNS %02x ccw_count %02x\n", + uptr->CMD, ch, uptr->SNS, chp->ccw_count); + uptr->CMD &= ~(LPR_CMDMSK); /* clear command */ + uptr->SNS = 0; /* no status */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); /* 4 byte req'd */ + return SCPE_OK; + } +#ifdef MPX_WANTS_ONLY_2_BYTES + ch = 0; /* byte 2 is always zero */ + if (chan_write_byte(chsa, &ch)) { /* write the status to memory */ + sim_debug(DEBUG_CMD, dptr, + "lpr_srv write3 error CMD %08x read %02x SNS %02x ccw_count %02x\n", + uptr->CMD, ch, uptr->SNS, chp->ccw_count); + uptr->CMD &= ~(LPR_CMDMSK); /* clear command */ + uptr->SNS = 0; /* no status */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); /* 4 byte req'd */ + return SCPE_OK; + } + ch = 0; /* byte 3 is always zero */ + if (chan_write_byte(chsa, &ch)) { /* write the status to memory */ + sim_debug(DEBUG_CMD, dptr, + "lpr_srv write4 error CMD %08x read %02x SNS %02x ccw_count %02x\n", + uptr->CMD, ch, uptr->SNS, chp->ccw_count); + uptr->CMD &= ~(LPR_CMDMSK); /* clear command */ + uptr->SNS = 0; /* no status */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); /* 4 byte req'd */ + return SCPE_OK; + } +#endif + sim_debug(DEBUG_CMD, dptr, + "lpr_srv sense write CMD %08x read %02x SNS %02x ccw_count %02x\n", + uptr->CMD, ch, uptr->SNS, chp->ccw_count); + uptr->CMD &= LMASK; /* make non-busy */ + uptr->SNS = 0; /* no status */ + chan_end(chsa, SNS_DEVEND|SNS_CHNEND); /* we are done */ + return SCPE_OK; + } + + /* NEW_02092022 */ + /* make sure we have a file attached, else give error */ + if ((uptr->flags & UNIT_ATT) == 0) { + uptr->CMD &= LMASK; /* make non-busy */ +// uptr->SNS |= SNS_DEVPWR; /* show powered off */ + uptr->SNS |= SNS_DEVCHK; /* show device check */ +// uptr->SNS |= SNS_OFFLINE; /* show printer offline */ + uptr->SNS |= SNS_OPRINTR; /* operator intervention required */ + sim_debug(DEBUG_CMD, dptr, + "lpr_startcmd Cmd %02x LPR not attached SNS %08x\n", cmd, uptr->SNS); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_CHECK); + return SCPE_OK; + } + + /* process any CC before printing buffer */ + if ((uptr->CMD & LPR_PRE) && (((cmd & 0x03) == 0x03) || + (cmd & 0x0f) == 0x0d)) { + uptr->CMD &= ~LPR_PRE; /* remove pre flag */ + /* we have CC to do */ + switch ((cmd & 0xf0) >> 4) { + case 0: /* (0x0d) */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0d; + break; + case 3: /* */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0a; + uptr->CNT++; /* increment the line count */ + /* drop thru */ + case 2: /* */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0a; + uptr->CNT++; /* increment the line count */ + /* drop thru */ + case 1: /* (0x0a) */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0a; + uptr->CNT++; /* increment the line count */ + break; + case 4: /* (0x0c) */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0d; /* add C/R */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0a; /* add L/F */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0c; /* add FF */ + uptr->CNT = 0; /* restart line count */ + /* set beginning of form and top of form */ + uptr->SNS |= (SNS_TOF|SNS_BEGOF); + break; + } + } + + /* Copy next byte from users buffer */ + while ((uptr->CMD & LPR_FULL) == 0) { /* copy in a char if not full */ + if(chan_read_byte(chsa, &lpr_data[u].lbuff[uptr->CBP])) { + uptr->CMD |= LPR_FULL; /* end of buffer or error */ + break; /* done reading */ + } else { + /* remove nulls */ + if (lpr_data[u].lbuff[uptr->CBP] == '\0') { + lpr_data[u].lbuff[uptr->CBP] = ' '; + } + /* remove backspace */ + if (lpr_data[u].lbuff[uptr->CBP] == 0x8) { + lpr_data[u].lbuff[uptr->CBP] = ' '; + } + uptr->CBP++; /* next buffer loc */ + } + } + + /* remove trailing blanks before we apply trailing carriage control */ + while (uptr->CBP > 0) { + if ((lpr_data[u].lbuff[uptr->CBP-1] == ' ') || + (lpr_data[u].lbuff[uptr->CBP-1] == '\0')) { + uptr->CBP--; + continue; + } + break; + } + + /* process any CC after printing buffer */ + if ((uptr->CMD & LPR_FULL) && (uptr->CMD & LPR_POST) && + ((cmd & 0x0f) == 0x0d)) { + /* we have CC to do */ + uptr->CMD &= ~LPR_POST; /* remove post flag */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0d; /* just a */ + } + + /* process any CC after printing buffer */ + if ((uptr->CMD & LPR_FULL) && (uptr->CMD & LPR_POST) && + ((cmd & 0x0f) == 0x05)) { + /* we have CC to do */ + uptr->CMD &= ~LPR_POST; /* remove post flag */ + switch ((cmd & 0xf0) >> 4) { + case 0: /* (0x0d) */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0d; + break; + case 3: /* */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0a; + uptr->CNT++; /* increment the line count */ + /* drop thru */ + case 2: /* */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0a; + uptr->CNT++; /* increment the line count */ + /* drop thru */ + case 1: /* (0x0a) */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0a; + uptr->CNT++; /* increment the line count */ + break; + case 4: /* (0x0c) */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0d; /* add C/R */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0a; /* add L/F */ + lpr_data[u].lbuff[uptr->CBP++] = 0x0c; /* add FF */ + uptr->CNT = 0; /* restart line count */ + /* set beginning of form and top of form */ + uptr->SNS |= (SNS_TOF|SNS_BEGOF); + break; + } + } + + /* print the line if buffer is full */ + if (uptr->CMD & LPR_FULL || uptr->CBP >= 156) { + lpr_data[u].lbuff[uptr->CBP] = 0x00; /* NULL terminate */ + sim_fwrite(&lpr_data[u].lbuff, 1, uptr->CBP, uptr->fileref); /* Print our buffer */ + sim_debug(DEBUG_DETAIL, dptr, "LPR %d %s\n", uptr->CNT, (char*)&lpr_data[u].lbuff); + uptr->CMD &= ~(LPR_FULL|LPR_CMDMSK); /* clear old status */ + uptr->CBP = 0; /* start at beginning of buffer */ + if ((uint32)uptr->CNT > uptr->capac) { /* see if at max lines/page */ + uptr->CNT = 0; /* yes, restart count */ + uptr->SNS |= SNS_BOF; /* set BOF for SENSE */ + sim_debug(DEBUG_CMD, dptr, "lpr_srv Got BOF\n"); + /* IOP spec says to give unit exception if at BOF */ + chan_end(chsa, SNS_DEVEND|SNS_CHNEND|SNS_UNITEXP); /* we are done */ + } else { + uptr->SNS &= ~SNS_BOF; /* reset BOF for SENSE */ + if (uptr->CNT == 0) { + /* set beginning of form and top of form */ + uptr->SNS |= (SNS_TOF|SNS_BEGOF); + } + chan_end(chsa, SNS_DEVEND|SNS_CHNEND); /* we are done */ + } + /* done, so no time out */ + return SCPE_OK; + } + + /* should not get here */ + return SCPE_OK; +} + +/* Handle haltio transfers for printer */ +t_stat lpr_haltio(UNIT *uptr) { + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & LPR_CMDMSK; + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + sim_debug(DEBUG_EXP, &lpr_dev, + "lpr_haltio enter chsa %04x cmd = %02x\n", chsa, cmd); + + /* terminate any input command */ + /* UTX wants SLI bit, but no unit exception */ + /* status must not have an error bit set */ + /* otherwise, UTX will panic with "bad status" */ + if ((uptr->CMD & LPR_CMDMSK) != 0) { /* is unit busy */ + sim_debug(DEBUG_CMD, &con_dev, + "lpr_haltio HIO chsa %04x cmd = %02x ccw_count %02x\n", + chsa, cmd, chp->ccw_count); + sim_cancel(uptr); /* stop timer */ + } else { + sim_debug(DEBUG_CMD, &con_dev, + "lpr_haltio HIO not busy chsa %04x cmd = %02x ccw_count %02x\n", + chsa, cmd, chp->ccw_count); + } + /* stop any I/O and post status and return error status */ + chp->ccw_count = 0; /* zero the count */ + chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* reset chaining bits */ + uptr->CMD &= LMASK; /* make non-busy */ + uptr->SNS = 0; /* no status */ + uptr->CBP = 0; /* start of buffer */ + sim_debug(DEBUG_CMD, &con_dev, + "lpr_haltio HIO I/O stop chsa %04x cmd = %02x\n", chsa, cmd); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* force end */ + return SCPE_IOERR; /* tell chan code to post status */ +} + +/* Set the number of lines per page on printer */ +t_stat lpr_setlpp(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int i; + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + i = 0; + while(*cptr != '\0') { + if (*cptr < '0' || *cptr > '9') + return SCPE_ARG; + i = (i * 10) + (*cptr++) - '0'; + } + if (i < 20 || i > 100) + return SCPE_ARG; + uptr->capac = i; + uptr->CNT = 0; + /* set beginning of form and top of form */ + uptr->SNS |= (SNS_TOF|SNS_BEGOF); + return SCPE_OK; +} + +/* display the number of lines per page */ +t_stat lpr_getlpp(FILE *st, UNIT *uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fprintf(st, "linesperpage=%02d", uptr->capac); + return SCPE_OK; +} + +/* attach a file to the line printer device */ +t_stat lpr_attach(UNIT *uptr, CONST char *file) +{ + t_stat r; + uint16 chsa = GET_UADDR(uptr->CMD); /* get address of lpr device */ + CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */ + DEVICE *dptr = get_dev(uptr); /* get device pointer */ + DIB *dibp = 0; + + if ((r = attach_unit(uptr, file)) != SCPE_OK) + return r; + uptr->CMD &= ~(LPR_FULL|LPR_CMDMSK); + uptr->CNT = 0; + uptr->SNS = 0; + /* set beginning of form and top of form */ + uptr->SNS |= (SNS_TOF|SNS_BEGOF); + uptr->capac = 66; + + /* check for valid configured lpr */ + /* must have valid DIB and Channel Program pointer */ + dibp = (DIB *)dptr->ctxt; /* get the DIB pointer */ + if ((dib_unit[chsa] == NULL) || (dibp == NULL) || (chp == NULL)) { + sim_debug(DEBUG_CMD, dptr, + "ERROR===ERROR\nLPR device %s not configured on system, aborting\n", + dptr->name); + printf("ERROR===ERROR\nLPR device %s not configured on system, aborting\r\n", + dptr->name); + detach_unit(uptr); /* detach if error */ + return SCPE_UNATT; /* error */ + } + set_devattn(chsa, SNS_DEVEND); /* ready int???? */ + return SCPE_OK; +} + +/* help information for lpr */ +t_stat lpr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "SEL32 924x High Speed Line Printer\n"); + fprintf (st, "The Line printer can be configured to any number of\n"); + fprintf (st, "lines per page with the:\n"); + fprintf (st, "sim> SET LPRn LINESPERPAGE=n\n\n"); + fprintf (st, "The default is 66 lines per page.\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +/* detach a file from the line printer */ +t_stat lpr_detach(UNIT * uptr) +{ + return detach_unit(uptr); +} + +const char *lpr_description (DEVICE *dptr) +{ + return "SEL32 924x High Speed Line Printer"; +} + +#endif diff --git a/SEL32/sel32_mfp.c b/SEL32/sel32_mfp.c new file mode 100644 index 00000000..c46a893b --- /dev/null +++ b/SEL32/sel32_mfp.c @@ -0,0 +1,357 @@ +/* sel32_mfp.c: SEL-32 Model 8002 MFP processor controller + + Copyright (c) 2018-2022, James C. Bevier + + 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 + JAMES C. BEVIER 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. + + This channel is the interrupt fielder for all of the MFP sub channels. It's + channel address is 7600. This code handles the INCH command for the MFP + devices and controls the status FIFO for the mfp devices on interrupts and + TIO instructions.. + + Possible devices: + The f8iop communication controller (TY76A0), (TY76B0), (TY76C0) + The ctiop console communications controller (CT76FC & CT76FD) + The lpiop line printer controller (LP76F8), (LP76F9) + The scsi SCSI disk controller (DM7600), (DM7640) + +*/ + +#include "sel32_defs.h" + +#if NUM_DEVS_MFP > 0 + +#define UNIT_MFP UNIT_IDLE | UNIT_DISABLE + +/* forward definitions */ +t_stat mfp_preio(UNIT *uptr, uint16 chan); +t_stat mfp_startcmd(UNIT *uptr, uint16 chan, uint8 cmd); +void mfp_ini(UNIT *uptr, t_bool f); +t_stat mfp_rschnlio(UNIT *uptr); +t_stat mfp_srv(UNIT *uptr); +t_stat mfp_reset(DEVICE *dptr); +t_stat mfp_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *mfp_desc(DEVICE *dptr); + +/* Held in u3 is the device command and status */ +#define MFP_INCH 0x00 /* Initialize channel command */ +#define MFP_INCH2 0xf0 /* Initialize channel command after start */ +#define MFP_NOP 0x03 /* NOP command */ +#define MFP_SID 0x80 /* MFP status command */ +#define MFP_MSK 0xff /* Command mask */ + +/* Status held in u3 */ +/* controller/unit address in upper 16 bits */ +#define CON_INPUT 0x100 /* Input ready for unit */ +#define CON_CR 0x200 /* Output at beginning of line */ +#define CON_REQ 0x400 /* Request key pressed */ +#define CON_EKO 0x800 /* Echo input character */ +#define CON_OUTPUT 0x1000 /* Output ready for unit */ +#define CON_READ 0x2000 /* Read mode selected */ + +/* not used u4 */ + +/* in u5 packs sense byte 0,1 and 3 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x80000000 /* Command reject */ +#define SNS_INTVENT 0x40000000 /* Unit intervention required */ +/* sense byte 3 */ +#define SNS_RDY 0x80 /* device ready */ +#define SNS_ONLN 0x40 /* device online */ + +/* std devices. data structures + + mfp_dev Console device descriptor + mfp_unit Console unit descriptor + mfp_reg Console register list + mfp_mod Console modifiers list +*/ + +struct _mfp_data +{ + uint8 ibuff[145]; /* Input line buffer */ + uint8 incnt; /* char count */ +} +mfp_data[NUM_UNITS_MFP]; + +/* channel program information */ +CHANP mfp_chp[NUM_UNITS_MFP] = {0}; + +MTAB mfp_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", + &set_dev_addr, &show_dev_addr, NULL, "Controller Channel address"}, + {0} +}; + +UNIT mfp_unit[] = { + {UDATA(&mfp_srv, UNIT_MFP, 0), 0, UNIT_ADDR(0x7600)}, /* Channel controller */ +}; + +//DIB mfp_dib = {NULL, mfp_startcmd, NULL, NULL, NULL, mfp_ini, mfp_unit, mfp_chp, NUM_UNITS_MFP, 0xff, 0x7600,0,0,0}; +DIB mfp_dib = { + mfp_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + mfp_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + NULL, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O HIO */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O HIO */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O TIO */ + NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + mfp_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + NULL, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + mfp_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + mfp_unit, /* UNIT* units */ /* Pointer to units structure */ + mfp_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_UNITS_MFP, /* uint8 numunits */ /* number of units defined */ + 0xff, /* uint8 mask */ /* 16 devices - device mask */ + 0x7600, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +DEVICE mfp_dev = { + "MFP", mfp_unit, NULL, mfp_mod, + NUM_UNITS_MFP, 8, 15, 1, 8, 8, + NULL, NULL, &mfp_reset, /* examine, deposit, reset */ + NULL, NULL, NULL, /* boot, attach, detach */ + /* dib ptr, dev flags, debug flags, debug */ + &mfp_dib, DEV_CHAN|DEV_DIS|DEV_DISABLE|DEV_DEBUG, 0, dev_debug, +}; + +/* MFP controller routines */ +/* initialize the console chan/unit */ +void mfp_ini(UNIT *uptr, t_bool f) +{ + int unit = (uptr - mfp_unit); /* unit 0 */ + DEVICE *dptr = &mfp_dev; /* one and only dummy device */ + + mfp_data[unit].incnt = 0; /* no input data */ + uptr->u5 = SNS_RDY|SNS_ONLN; /* status is online & ready */ + sim_cancel(uptr); /* stop any timers */ + sim_debug(DEBUG_CMD, &mfp_dev, + "MFP init device %s controller/device %04x SNS %08x\n", + dptr->name, GET_UADDR(uptr->u3), uptr->u5); +} + +/* handle rschnlio cmds for disk */ +t_stat mfp_rschnlio(UNIT *uptr) { + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->u3); + int cmd = uptr->u3 & MFP_MSK; + + sim_debug(DEBUG_EXP, dptr, + "mfp_rschnl chsa %04x cmd = %02x\n", chsa, cmd); + mfp_ini(uptr, 0); /* reset the unit */ + return SCPE_OK; +} + +/* start an mfp operation */ +t_stat mfp_preio(UNIT *uptr, uint16 chan) { + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); + uint16 chsa = GET_UADDR(uptr->u3); + + sim_debug(DEBUG_CMD, dptr, "mfp_preio CMD %08x unit %02x chsa %04x\n", + uptr->u3, unit, chsa); + + if ((uptr->u3 & MFP_MSK) != 0) { /* is unit busy */ + sim_debug(DEBUG_CMD, dptr, + "mfp_preio unit %02x chsa %04x BUSY\n", unit, chsa); + return SNS_BSY; /* yes, return busy */ + } + + sim_debug(DEBUG_CMD, dptr, "mfp_preio unit %02x chsa %04x OK\n", unit, chsa); + return SCPE_OK; /* good to go */ +} + +/* start an I/O operation */ +t_stat mfp_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) +{ + sim_debug(DEBUG_CMD, &mfp_dev, + "MFP startcmd %02x controller/device %04x\n", + cmd, GET_UADDR(uptr->u3)); + if ((uptr->u3 & MFP_MSK) != 0) /* is unit busy */ + return SNS_BSY; /* yes, return busy */ + + /* process the commands */ + switch (cmd & 0xFF) { + /* UTX uses the INCH cmd to detect the MFP or MFP */ + /* MFP has INCH cmd of 0, while MFP uses 0x80 */ + case MFP_INCH: /* INCH command */ + uptr->u5 = SNS_RDY|SNS_ONLN; /* status is online & ready */ + uptr->u3 &= LMASK; /* leave only chsa */ + sim_debug(DEBUG_CMD, &mfp_dev, + "mfp_startcmd %04x: Cmd INCH iptr %06x INCHa %06x\n", + chan, mfp_chp[0].ccw_addr, /* set inch buffer addr */ + mfp_chp[0].chan_inch_addr); /* set inch buffer addr */ + + mfp_chp[0].chan_inch_addr = mfp_chp[0].ccw_addr; /* set inch buffer addr */ + mfp_chp[0].base_inch_addr = mfp_chp[0].ccw_addr; /* set inch buffer addr */ + mfp_chp[0].max_inch_addr = mfp_chp[0].ccw_addr + (128 * 8); /* set last inch buffer addr */ + + uptr->u3 |= MFP_INCH2; /* save INCH command as 0xf0 */ + sim_activate(uptr, 40); /* go on */ + return 0; /* no status change */ + break; + + case MFP_NOP: /* NOP command */ + sim_debug(DEBUG_CMD, &mfp_dev, "mfp_startcmd %04x: Cmd NOP\n", chan); + uptr->u5 = SNS_RDY|SNS_ONLN; /* status is online & ready */ + uptr->u3 &= LMASK; /* leave only chsa */ + uptr->u3 |= (cmd & MFP_MSK); /* save NOP command */ + sim_activate(uptr, 40); /* TRY 07-13-19 */ + return 0; /* no status change */ + break; + + case MFP_SID: /* status ID command */ + sim_debug(DEBUG_CMD, &mfp_dev, "mfp_startcmd %04x: Cmd SID\n", chan); + uptr->u5 = SNS_RDY|SNS_ONLN; /* status is online & ready */ + uptr->u3 &= LMASK; /* leave only chsa */ + uptr->u3 |= (cmd & MFP_MSK); /* save SID command */ + sim_activate(uptr, 40); /* TRY 07-13-19 */ + return 0; /* no status change */ + break; + + default: /* invalid command */ + uptr->u5 |= SNS_CMDREJ; /* command rejected */ + sim_debug(DEBUG_CMD, &mfp_dev, "mfp_startcmd %04x: Cmd Invalid %02x status %02x\n", + chan, cmd, uptr->u5); + uptr->u3 &= LMASK; /* leave only chsa */ + uptr->u3 |= (cmd & MFP_MSK); /* save command */ + sim_activate(uptr, 40); /* force interrupt */ + return 0; /* no status change */ + break; + } + + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; /* not reachable for now */ +} + +/* Handle transfers for other sub-channels on MFP */ +t_stat mfp_srv(UNIT *uptr) +{ + uint16 chsa = GET_UADDR(uptr->u3); + int cmd = uptr->u3 & MFP_MSK; + CHANP *chp = &mfp_chp[0]; /* find the chanp pointer */ + uint32 mema = chp->ccw_addr; /* get inch or buffer addr */ + uint32 tstart; + + /* test for NOP or INCH cmds */ + if ((cmd != MFP_NOP) && (cmd != MFP_INCH2) && (cmd != MFP_SID)) { /* NOP, SID or INCH */ + uptr->u3 &= LMASK; /* nothing left, command complete */ + sim_debug(DEBUG_CMD, &mfp_dev, + "mfp_srv Unknown cmd %02x chan %02x: chnend|devend|unitexp\n", cmd, chsa); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); /* done */ + return SCPE_OK; + } else + + if (cmd == MFP_NOP) { /* NOP do nothing */ + uptr->u3 &= LMASK; /* nothing left, command complete */ + sim_debug(DEBUG_CMD, &mfp_dev, "mfp_srv NOP chan %02x: chnend|devend\n", chsa); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* done */ + return SCPE_OK; + } else + + /* 3 status wds are to be returned */ + /* Wd 1 MMXXXXXX board model # assume 00 00 08 02*/ + /* Wd 2 MMXXXXXX board firmware model # assume 00 00 08 02*/ + /* Wd 3 MMXXXXXX board firmware revision # assume 00 00 00 14*/ + if (cmd == MFP_SID) { /* send 12 byte Status ID data */ + uint8 ch; + + /* Word 0 */ /* board mod 4324724 = 0x0041fd74 */ + ch = 0x00; + chan_write_byte(chsa, &ch); /* write byte 0 */ + ch = 0x00; + chan_write_byte(chsa, &ch); /* write byte 1 */ + ch = 0x81; + chan_write_byte(chsa, &ch); /* write byte 2 */ + ch = 0x02; + chan_write_byte(chsa, &ch); /* write byte 3 */ + + /* Word 1 */ /* firmware 4407519 = 0x004340df */ + ch = 0x00; + chan_write_byte(chsa, &ch); /* write byte 4 */ + ch = 0x00; + chan_write_byte(chsa, &ch); /* write byte 5 */ + ch = 0x80; + chan_write_byte(chsa, &ch); /* write byte 6 */ + ch = 0x02; + chan_write_byte(chsa, &ch); /* write byte 7 */ + + /* Word 2 */ /* firmware rev 4259588 = 0x0040ff04 */ + ch = 0x00; + chan_write_byte(chsa, &ch); /* write byte 8 */ + ch = 0x00; + chan_write_byte(chsa, &ch); /* write byte 9 */ + ch = 0x00; + chan_write_byte(chsa, &ch); /* write byte 10 */ + ch = 0x14; + chan_write_byte(chsa, &ch); /* write byte 11 */ + + uptr->u3 &= LMASK; /* nothing left, command complete */ + sim_debug(DEBUG_CMD, &mfp_dev, "mfp_srv SID chan %02x: chnend|devend\n", chsa); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* done */ + return SCPE_OK; + } else + + /* test for INCH cmd */ + if (cmd == MFP_INCH2) { /* INCH */ + sim_debug(DEBUG_CMD, &mfp_dev, + "mfp_srv starting INCH %06x cmd, chsa %04x MemBuf %06x cnt %04x\n", + mema, chsa, chp->ccw_addr, chp->ccw_count); + + /* now call set_inch() function to write and test inch buffer addresses */ + /* the chp->ccw_addr location contains the inch address */ + /* 1-256 wd buffer is provided for 128 status dbl words */ + tstart = set_inch(uptr, mema, 128); /* new address of 128 entries */ + if ((tstart == SCPE_MEM) || (tstart == SCPE_ARG)) { /* any error */ + /* we have error, bail out */ + uptr->u5 |= SNS_CMDREJ; + uptr->u3 &= LMASK; /* nothing left, command complete */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + uptr->u3 &= LMASK; /* clear the cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */ + } + return SCPE_OK; +} + +t_stat mfp_reset(DEVICE *dptr) +{ + /* add reset code here */ + return SCPE_OK; +} + +/* sho help mfp */ +t_stat mfp_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr) +{ + fprintf(st, "SEL-32 MFP Model 8002 Channel Controller at 0x7600\r\n"); + fprintf(st, "The MFP fields all interrupts and status posting\r\n"); + fprintf(st, "for each of the controllers on the system.\r\n"); + fprintf(st, "Nothing can be configured for this Channel.\r\n"); + return SCPE_OK; +} + +const char *mfp_desc(DEVICE *dptr) +{ + return("SEL-32 MFP Model 8002 Channel Controller @ 0x7600"); +} + +#endif + diff --git a/SEL32/sel32_mt.c b/SEL32/sel32_mt.c new file mode 100644 index 00000000..33f7be68 --- /dev/null +++ b/SEL32/sel32_mt.c @@ -0,0 +1,1645 @@ +/* sel32_mt.c: SEL-32 8051 Buffered Tape Processor + + Copyright (c) 2018-2022, James C. Bevier + Portions provided by Richard Cornwell and other SIMH contributers + + 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 + JAMES C. BEVIER 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. + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. EOT is + represented as 0xffffffff (-1) byte count. +*/ + +#include "sel32_defs.h" +#include "sim_tape.h" + +#if NUM_DEVS_MT > 0 + +#define BUFFSIZE (64 * 1024) +#define UNIT_MT UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE + +#define CMD u3 +/* BTP tape commands */ +#define MT_INCH 0x00 /* Initialize channel command */ +#define MT_WRITE 0x01 /* Write command */ +#define MT_READ 0x02 /* Read command */ +#define MT_NOP 0x03 /* Control command */ +#define MT_SENSE 0x04 /* Sense command */ +#define MT_RDBK 0x0c /* Read Backward */ +#define MT_RDCMP 0x13 /* Read and compare command */ +#define MT_REW 0x23 /* Rewind command */ +#define MT_RUN 0x33 /* Rewind and unload */ +#define MT_FSR 0x43 /* Advance record */ +#define MT_BSR 0x53 /* Backspace record */ +#define MT_FSF 0x63 /* Advance to filemark */ +#define MT_BSF 0x73 /* Backspace to filemark */ +#define MT_SETM 0x83 /* Set Mode command */ +#define MT_WTM 0x93 /* Write Tape filemark */ +#define MT_ERG 0xA3 /* Erase 3.5 of tape */ +#define MT_MODEMSK 0xFF /* Mode Mask */ + +/* set mode bits for BTP (MT_SETM) */ +#define MT_MODE_AUTO 0x80 /* =0 Perform auto error recovery on read */ +#define MT_MODE_FORCE 0x80 /* =1 Read regardless if error recovery fails */ +#define MT_MDEN_800 0x40 /* =0 select 800 BPI NRZI mode 9 track only */ +#define MT_MDEN_1600 0x40 /* =1 select 1600 BPI PE mode 9 track only */ +#define MT_MDEN_6250 0x02 /* =0 Use mode from bit one for NRZI/PE */ +#define MT_MDEN_6250 0x02 /* =1 6250 BPI GCR mode 9 track only */ +#define MT_MDEN_SCATGR 0x01 /* =1 HSTP scatter/gather mode */ +#define MT_MDEN_MSK 0x42 /* Density mask */ + +#define MT_CTL_MSK 0x38 /* Mask for control flags */ +#define MT_CTL_NOP 0x00 /* Nop control mode */ +#define MT_CTL_NRZI 0x08 /* 9 track 800 bpi mode */ +#define MT_CTL_RST 0x10 /* Set density, odd, convert on, trans off */ +#define MT_CTL_NOP2 0x18 /* 9 track 1600 NRZI mode */ + +/* in u3 is device command code and status */ +#define MT_CMDMSK 0x00ff /* Command being run */ +#define MT_READDONE 0x0400 /* Read finished, end channel */ +#define MT_MARK 0x0800 /* Sensed tape mark in move command */ +#define MT_ODD 0x1000 /* Odd parity */ +#define MT_TRANS 0x2000 /* Translation turned on ignored 9 track */ +#define MT_CONV 0x4000 /* Data converter on ignored 9 track */ +#define MT_BUSY 0x8000 /* Flag to send a CUE */ + +#define POS u4 +/* in u4 is current buffer position */ + +#define SNS u5 +/* in u5 packs sense byte 0, 1, 2 and 3 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x80000000 /* Command reject */ +#define SNS_INTVENT 0x40000000 /* Unit intervention required */ +#define SNS_SPARE1 0x20000000 /* Spare */ +#define SNS_EQUCHK 0x10000000 /* Equipment check */ +#define SNS_DATCHK 0x08000000 /* Data Check */ +#define SNS_OVRRUN 0x04000000 /* Data overrun */ +#define SNS_SPARE2 0x02000000 /* Spare */ +#define SNS_LOOKER 0x01000000 /* lookahead error */ + +/* Sense byte 1 */ +#define SNS_PEMODER 0x800000 /* PE tape mode error */ +#define SNS_TPECHK 0x400000 /* Tape PE mode check */ +#define SNS_FMRKDT 0x200000 /* File mark detected EOF */ +#define SNS_CORERR 0x100000 /* Corrected Error */ +#define SNS_HARDER 0x080000 /* Hard Error */ +#define SNS_MRLDER 0x040000 /* Mode register load error */ +#define SNS_DATAWR 0x020000 /* Data written */ +#define SNS_SPARE3 0x010000 /* Spare */ + +/* Sense byte 2 mode register bits */ +#define SNS_MREG0 0x8000 /* 0 - Auto retry on read error */ + /* 1 - Ignore read errors */ +#define SNS_MREG1 0x4000 /* 0 - NRZI */ + /* 1 - PE */ +#define SNS_MREG2 0x2000 /* Mode register bit 2 N/U */ +#define SNS_MREG3 0x1000 /* Mode register bit 3 N/U */ +#define SNS_MREG4 0x0800 /* Mode register bit 4 N/U */ +#define SNS_MREG5 0x0400 /* Mode register bit 5 N/U */ +#define SNS_MREG6 0x0200 /* Mode register bit 6 N/U */ +#define SNS_MREG7 0x0100 /* 1 - HSDP scatter/gather mode */ + +/* Sense byte 3 */ +/* data returned for SENSE cmd (0x04) */ +#define SNS_RDY 0x80 /* Drive Ready */ +#define SNS_ONLN 0x40 /* Drive Online */ +#define SNS_WRP 0x20 /* Drive is file protected (write ring missing) */ +#define SNS_NRZI 0x10 /* Drive is NRZI */ +#define SNS_SPARE4 0x08 /* Spare */ +#define SNS_LOAD 0x04 /* Drive is at load point */ +#define SNS_EOT 0x02 /* Drive is at EOT */ +#define SNS_SPARE5 0x01 /* Spare */ + +#define SNS_BYTE4 0x00 /* Hardware errors not supported */ +#define SNS_BYTE5 0x00 /* Hardware errors not supported */ + +#define MT_CONV1 0x40 +#define MT_CONV2 0x80 +#define MT_CONV3 0xc0 + +/* u6 holds the packed characters and unpack counter */ +#define BUF_EMPTY(u) (u->hwmark == 0xFFFFFFFF) +#define CLR_BUF(u) u->hwmark = 0xFFFFFFFF + +/* forward definitions */ +t_stat mt_preio(UNIT *uptr, uint16 chan); +t_stat mt_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) ; +t_stat mt_iocl(CHANP *chp, int32 tic_ok); +t_stat mt_srv(UNIT *uptr); +t_stat mt_boot(int32 unitnum, DEVICE *dptr); +void mt_ini(UNIT *uptr, t_bool); +t_stat mt_rschnlio(UNIT *uptr); +t_stat mt_haltio(UNIT *uptr); +t_stat mt_reset(DEVICE *dptr); +t_stat mt_attach(UNIT *uptr, CONST char *); +t_stat mt_detach(UNIT *uptr); +t_stat mt_help(FILE *, DEVICE *dptr, UNIT *uptr, int32, const char *); +const char *mt_description(DEVICE *); +extern uint32 readfull(CHANP *chp, uint32 maddr, uint32 *word); +extern uint16 loading; /* set when doing IPL */ +extern int irq_pend; /* pending interrupt flag */ +extern uint32 cont_chan(uint16 chsa); + +/* One buffer per channel */ +uint8 mt_buffer[NUM_DEVS_MT][BUFFSIZE]; +uint8 mt_busy[NUM_DEVS_MT]; + +/* Gould Buffered Tape Processor (BTP) - Model 8051 */ +/* Integrated channel controller */ + +/* Class F MT BTP I/O device status response in IOCD address pointer location */ +/* |-------+-------+-------+-------+-------+-------+-------+-------| */ +/* |0 0 0 0|0 0 0 0|0 0 1 1|1 1 1 1|1 1 1 1|2 2 2 2|2 2 2 2|2 2 3 3| */ +/* |0 1 2 3|4 5 6 7|8 9 0 1|2 3 4 5|6 7 8 9|0 1 2 3|4 5 6 7|8 9 3 1| */ +/* | Cond |0 0 0 0| Address of status doubleword or zero | */ +/* | Code | */ +/* |-------+-------+-------+-------+-------+-------+-------+-------| */ +/* */ +/* Bits 0-3 - Condition codes */ +/* 0000 - operation accepted will echo status not sent by the channel */ +/* 0001 - channel busy */ +/* 0010 - channel inop or undefined */ +/* 0011 - subchannel busy */ +/* 0100 - status stored */ +/* 0101 - unsupported transaction */ +/* 1000 - Operation accepted/queued, no echo status */ + +/* Status Doubleword */ +/* Word 1 */ +/* |-------+-------+-------+-------+-------+-------+-------+-------| */ +/* |0 0 0 0|0 0 0 0|0 0 1 1|1 1 1 1|1 1 1 1|2 2 2 2|2 2 2 2|2 2 3 3| */ +/* |0 1 2 3|4 5 6 7|8 9 0 1|2 3 4 5|6 7 8 9|0 1 2 3|4 5 6 7|8 9 3 1| */ +/* |Sub Address | 24 bit IOCD address | */ +/* |-------+-------+-------+-------+-------+-------+-------+-------| */ +/* Word 2 */ +/* |-------+-------+-------+-------+-------+-------+-------+-------| */ +/* |0 0 0 0|0 0 0 0|0 0 1 1|1 1 1 1|1 1 1 1|2 2 2 2|2 2 2 2|2 2 3 3| */ +/* |0 1 2 3|4 5 6 7|8 9 0 1|2 3 4 5|6 7 8 9|0 1 2 3|4 5 6 7|8 9 3 1| */ +/* | 16 bit of status | Residual Byte Count | */ +/* |-------+-------+-------+-------+-------+-------+-------+-------| */ + +/* Status Bits */ +/* Bit 00 - ECHO Halt I/O and Stop I/O function */ +/* Bit 01 - PCI Program Controlled Interrupt */ +/* Bit 02 - IL Incorrect Length */ +/* Bit 03 - CPC Channel Program Check */ +/* Bit 04 - CDC Channel Data Check */ +/* Bit 05 - CCC Channel Control Check */ +/* Bit 06 - IC Interface Check */ +/* Bit 07 - CHC Chaining Check */ +/* Bit 08 - DB Device Busy */ +/* Bit 09 - SM Status Modifier */ +/* Bit 10 - CNTE Controller End */ +/* Bit 11 - ATTN Attention */ +/* Bit 12 - CE Channel End */ +/* Bit 13 - DE Device End */ +/* Bit 14 - UC Unit Check */ +/* Bit 15 - UE Unit Exception */ + +/* 41 Word Main memory channel buffer provided by INCH command */ +/* when software is initializing the channel */ +/* Word 01 - Status Doubleword 1 - Word 1 */ +/* Word 02 - Status Doubleword 1 - Word 2 */ +/* Word 03 - Status Doubleword 2 - Word 1 */ +/* Word 04 - Status Doubleword 2 - Word 2 */ +/* Word 05 - BTP Error Recovery IOCD Address */ +/* Word 06 - Queue Command List Doubleword - Word 1 */ +/* Word 07 - Queue Command List Doubleword - Word 2 */ +/* Word 08 - 16 bit Logical Q-pointer | 16 bit Physical Q-pointer */ +/* Word 09 - 16 bit Active Retry Count | 16 bit Constant Retry Count */ +/* Word 10 - Accumulated Write Count - Drive 0 */ +/* Word 11 - Accumulated Read Count - Drive 0 */ +/* Word 12 - Write Error Count - Drive 0 */ +/* Word 13 - Read Error Count - Drive 0 */ +/* Word 14 - Accumulated Write Count - Drive 1 */ +/* Word 15 - Accumulated Read Count - Drive 1 */ +/* Word 16 - Write Error Count - Drive 1 */ +/* Word 17 - Read Error Count - Drive 1 */ +/* Word 18 - Accumulated Write Count - Drive 2 */ +/* Word 19 - Accumulated Read Count - Drive 2 */ +/* Word 20 - Write Error Count - Drive 2 */ +/* Word 21 - Read Error Count - Drive 2 */ +/* Word 22 - Accumulated Write Count - Drive 3 */ +/* Word 23 - Accumulated Read Count - Drive 3 */ +/* Word 24 - Write Error Count - Drive 3 */ +/* Word 25 - Read Error Count - Drive 3 */ +/* Word 26 - Accumulated Write Count - Drive 4 */ +/* Word 27 - Accumulated Read Count - Drive 4 */ +/* Word 28 - Write Error Count - Drive 4 */ +/* Word 29 - Read Error Count - Drive 4 */ +/* Word 30 - Accumulated Write Count - Drive 5 */ +/* Word 31 - Accumulated Read Count - Drive 5 */ +/* Word 32 - Write Error Count - Drive 5 */ +/* Word 33 - Read Error Count - Drive 5 */ +/* Word 34 - Accumulated Write Count - Drive 6 */ +/* Word 35 - Accumulated Read Count - Drive 6 */ +/* Word 36 - Write Error Count - Drive 6 */ +/* Word 37 - Read Error Count - Drive 6 */ +/* Word 38 - Accumulated Write Count - Drive 7 */ +/* Word 39 - Accumulated Read Count - Drive 7 */ +/* Word 40 - Write Error Count - Drive 7 */ +/* Word 41 - Read Error Count - Drive 7 */ + +int32 valid_dens = MT_800_VALID|MT_1600_VALID|MT_6250_VALID; +MTAB mt_mod[] = { + {MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL, NULL, NULL, + "Write ring in place"}, + {MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL, NULL, NULL, + "No write ring in place"}, + {MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "DENSITY", "DENSITY", + &sim_tape_set_dens, &sim_tape_show_dens, &valid_dens, + "Set tape density"}, + {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL, + "Set/Display tape format (SIMH, E11, TPC, P7B)"}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, + &show_dev_addr, NULL, "Device address"}, + {0} +}; + +UNIT mta_unit[] = { + /* Unit data layout for MT devices */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1000)}, /* 0 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1001)}, /* 1 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1002)}, /* 2 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1003)}, /* 3 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1004)}, /* 4 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1005)}, /* 5 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1006)}, /* 6 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1007)}, /* 7 */ +}; + +/* channel program information */ +CHANP mta_chp[NUM_UNITS_MT] = {0}; + +DIB mta_dib = { + mt_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + mt_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + mt_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + mt_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + NULL, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + mt_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + mta_unit, /* UNIT* units */ /* Pointer to units structure */ + mta_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_UNITS_MT, /* uint8 numunits */ /* number of units defined */ + 0x07, /* uint8 mask */ /* 8 devices - device mask */ + 0x1000, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +DEVICE mta_dev = { + "MTA", mta_unit, NULL, mt_mod, + NUM_UNITS_MT, 16, 24, 4, 16, 32, + NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, + /* ctxt is the DIB pointer */ + &mta_dib, DEV_BUF_NUM(0)|DEV_DIS|DEV_DISABLE|DEV_DEBUG|DEV_TAPE, 0, dev_debug, + NULL, NULL, &mt_help, NULL, NULL, &mt_description + +}; + +#if NUM_DEVS_MT > 1 +/* channel program information */ +CHANP mtb_chp[NUM_UNITS_MT] = {0}; + +UNIT mtb_unit[] = { + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1800)}, /* 0 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1801)}, /* 1 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1802)}, /* 2 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1803)}, /* 3 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1804)}, /* 4 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1805)}, /* 5 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1806)}, /* 6 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1807)}, /* 7 */ +}; + +/* device information block */ +DIB mtb_dib = { + mt_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + mt_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + mt_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + mt_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + NULL, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + mt_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + mtb_unit, /* UNIT* units */ /* Pointer to units structure */ + mtb_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_UNITS_MT, /* uint8 numunits */ /* number of units defined */ + 0x07, /* uint8 mask */ /* 8 devices - device mask */ + 0x1800, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +DEVICE mtb_dev = { + "MTB", mtb_unit, NULL, mt_mod, + NUM_UNITS_MT, 8, 15, 1, 8, 8, + NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, + &mtb_dib, DEV_BUF_NUM(0)|DEV_DIS|DEV_DISABLE|DEV_DEBUG|DEV_TAPE, 0, dev_debug, + NULL, NULL, &mt_help, NULL, NULL, &mt_description +}; +#endif + +/* load in the IOCD and process the commands */ +/* return = 0 OK */ +/* return = 1 error, chan_status will have reason */ +t_stat mt_iocl(CHANP *chp, int32 tic_ok) +{ + uint32 word1 = 0; + uint32 word2 = 0; + int32 docmd = 0; + UNIT *uptr = chp->unitptr; /* get the unit ptr */ + uint16 chan = get_chan(chp->chan_dev); /* our channel */ + uint16 chsa = chp->chan_dev; + uint16 devstat = 0; + DEVICE *dptr = get_dev(uptr); + + /* check for valid iocd address if 1st iocd */ + if (chp->chan_info & INFO_SIOCD) { /* see if 1st IOCD in channel prog */ + if (chp->chan_caw & 0x3) { /* must be word bounded */ + sim_debug(DEBUG_EXP, dptr, + "mt_iocl iocd bad address chsa %02x caw %06x\n", + chsa, chp->chan_caw); + chp->ccw_addr = chp->chan_caw; /* set the bad iocl address */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid iocd addr */ + return 1; /* error return */ + } + } +loop: + sim_debug(DEBUG_EXP, dptr, + "mt_iocl @%06x @loop chan_status[%04x] %04x SNS %08x\n", + chp->chan_caw, chan, chp->chan_status, uptr->SNS); + + /* Abort if we have any errors */ + if (chp->chan_status & STATUS_ERROR) { /* check channel error status */ + sim_debug(DEBUG_EXP, dptr, + "mt_iocl ERROR1 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* return error */ + } + + /* Read in first CCW */ + if (readfull(chp, chp->chan_caw, &word1) != 0) { /* read word1 from memory */ + chp->chan_status |= STATUS_PCHK; /* memory read error, program check */ + sim_debug(DEBUG_EXP, dptr, + "mt_iocl ERROR2 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* Read in second CCW */ + if (readfull(chp, chp->chan_caw+4, &word2) != 0) { /* read word2 from memory */ + chp->chan_status |= STATUS_PCHK; /* memory read error, program check */ + sim_debug(DEBUG_EXP, dptr, + "mt_iocl ERROR3 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + sim_debug(DEBUG_CMD, dptr, + "mt_iocl @%06x read ccw chsa %04x IOCD wd 1 %08x wd 2 %08x SNS %08x\n", + chp->chan_caw, chp->chan_dev, word1, word2, uptr->SNS); + + chp->chan_caw = (chp->chan_caw & 0xfffffc) + 8; /* point to next IOCD */ + + /* Check if we had data chaining in previous iocd */ + /* if we did, use previous cmd value */ + if (((chp->chan_info & INFO_SIOCD) == 0) && /* see if 1st IOCD in channel prog */ + (chp->ccw_flags & FLAG_DC)) { /* last IOCD have DC set? */ + sim_debug(DEBUG_CMD, dptr, + "mt_iocl @%06x DO DC, ccw_flags %04x cmd %02x\n", + chp->chan_caw, chp->ccw_flags, chp->ccw_cmd); + } else + chp->ccw_cmd = (word1 >> 24) & 0xff; /* set new command from IOCD wd 1 */ + + if (!MEM_ADDR_OK(word1 & MASK24)) { /* see if memory address invalid */ + chp->chan_status |= STATUS_PCHK; /* bad, program check */ + sim_debug(DEBUG_EXP, dptr, + "mt_iocl mem error PCHK chan_status[%04x] %04x addr %08x\n", + chan, chp->chan_status, word1 & MASK24); + return 1; /* error return */ + } + + chp->ccw_count = word2 & 0xffff; /* get 16 bit byte count from IOCD WD 2*/ + + /* validate the commands for the mt */ + switch (chp->ccw_cmd) { + case MT_WRITE: case MT_READ: case MT_NOP: case MT_SENSE: + case MT_RDBK: case MT_RDCMP: case MT_REW: case MT_RUN: case MT_FSR: + case MT_BSR: case MT_FSF: case MT_BSF: case MT_SETM: case MT_WTM: case MT_ERG: + /* the inch command must be first command issued */ + if ((!loading) && (chp->chan_inch_addr == 0)) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid cmd */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected */ + sim_debug(DEBUG_EXP, dptr, + "mt_iocl bad cmd %02x chan_status[%04x] %04x\n", + chp->ccw_cmd, chan, chp->chan_status); + return 1; /* error return */ + } + case MT_INCH: + break; + default: + chp->chan_status |= STATUS_PCHK; /* program check for invalid cmd */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected */ + sim_debug(DEBUG_EXP, dptr, + "mt_iocl bad cmd %02x chan_status[%04x] %04x\n", + chp->ccw_cmd, chan, chp->chan_status); + return 1; /* error return */ + } + + if (chp->chan_info & INFO_SIOCD) { /* see if 1st IOCD in channel prog */ + /* 1st command can not be a TIC */ + if (chp->ccw_cmd == CMD_TIC) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + sim_debug(DEBUG_EXP, dptr, + "mt_iocl TIC bad cmd chan_status[%04x] %04x\n", + chan, chp->chan_status); + return 1; /* error return */ + } + } + + /* TIC can't follow TIC or be first in command chain */ + /* diags send bad commands for testing. Use all of op */ + if (chp->ccw_cmd == CMD_TIC) { + if (tic_ok) { + if (((word1 & MASK24) == 0) || (word1 & 0x3)) { + sim_debug(DEBUG_EXP, dptr, + "mt_iocl tic cmd bad address chan %02x tic caw %06x IOCD wd 1 %08x\n", + chan, chp->chan_caw, word1); + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected status */ + return 1; /* error return */ + } + tic_ok = 0; /* another tic not allowed */ + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + sim_debug(DEBUG_CMD, dptr, + "mt_iocl tic cmd ccw chan %02x tic caw %06x IOCD wd 1 %08x\n", + chan, chp->chan_caw, word1); + goto loop; /* restart the IOCD processing */ + } + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + sim_debug(DEBUG_EXP, dptr, + "mt_iocl TIC ERROR chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* Check if we had data chaining in previous iocd */ + if ((chp->chan_info & INFO_SIOCD) || /* see if 1st IOCD in channel prog */ + (((chp->chan_info & INFO_SIOCD) == 0) && /* see if 1st IOCD in channel prog */ + ((chp->ccw_flags & FLAG_DC) == 0))) { /* last IOCD have DC set? */ + sim_debug(DEBUG_CMD, dptr, + "mt_iocl @%06x DO CMD No DC, ccw_flags %04x cmd %02x\n", + chp->chan_caw, chp->ccw_flags, chp->ccw_cmd); + docmd = 1; /* show we have a command */ + } + + /* Set up for this command */ + chp->ccw_flags = (word2 >> 16) & 0xfc00; /* get flags from bits 0-4 of WD 2 of IOCD */ + chp->chan_status = 0; /* clear status for next IOCD */ + /* make a 24 bit address */ + chp->ccw_addr = word1 & MASK24; /* set the data/seek address */ + + if (chp->ccw_flags & FLAG_PCI) { /* do we have prog controlled int? */ + chp->chan_status |= STATUS_PCI; /* set PCI flag in status */ + irq_pend = 1; /* interrupt pending */ + } + + /* validate parts of IOCD2 that are reserved */ + if (word2 & 0x07ff0000) { /* bits 5-15 must be zero */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid iocd */ + sim_debug(DEBUG_EXP, dptr, + "mt_iocl IOCD2 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* DC can only be used with a read/write cmd */ + if (chp->ccw_flags & FLAG_DC) { + if ((chp->ccw_cmd != MT_READ) && (chp->ccw_cmd != MT_WRITE) && + (chp->ccw_cmd != MT_RDBK)) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid DC */ + sim_debug(DEBUG_EXP, dptr, + "mt_iocl DC ERROR chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + } + + chp->chan_byte = BUFF_BUSY; /* busy & no bytes transferred yet */ + + sim_debug(DEBUG_XIO, dptr, + "mt_iocl @%06x read docmd %01x addr %06x count %04x chan %04x ccw_flags %04x\n", + chp->chan_caw, docmd, chp->ccw_addr, chp->ccw_count, chan, chp->ccw_flags); + + if (docmd) { /* see if we need to process a command */ + DIB *dibp = dib_unit[chp->chan_dev]; /* get the DIB pointer */ + + uptr = chp->unitptr; /* get the unit ptr */ + if (dibp == 0 || uptr == 0) { + chp->chan_status |= STATUS_PCHK; /* program check if it is */ + sim_debug(DEBUG_EXP, dptr, + "mt_iocl bad dibp or uptr chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* if none, error */ + } + + sim_debug(DEBUG_XIO, dptr, + "mt_iocl @%06x before start_cmd chan %04x status %04x count %04x SNS %08x\n", + chp->chan_caw, chan, chp->chan_status, chp->ccw_count, uptr->SNS); + + /* call the device startcmd function to process the current command */ + /* just replace device status bits */ + chp->chan_info &= ~INFO_CEND; /* show chan_end not called yet */ + devstat = dibp->start_cmd(uptr, chan, chp->ccw_cmd); + chp->chan_status = (chp->chan_status & 0xff00) | devstat; + chp->chan_info &= ~INFO_SIOCD; /* show not first IOCD in channel prog */ + + sim_debug(DEBUG_XIO, dptr, + "mt_iocl @%06x after start_cmd chsa %04x status %08x count %04x SNS %08x\n", + chp->chan_caw, chsa, chp->chan_status, chp->ccw_count, uptr->SNS); + + /* see if bad status */ + if (chp->chan_status & (STATUS_ATTN|STATUS_ERROR)) { + chp->chan_status |= STATUS_CEND; /* channel end status */ + chp->ccw_flags = 0; /* no flags */ + chp->chan_byte = BUFF_NEXT; /* have main pick us up */ + sim_debug(DEBUG_EXP, dptr, + "mt_iocl bad status chsa %04x status %04x cmd %02x\n", + chsa, chp->chan_status, chp->ccw_cmd); + /* done with command */ + sim_debug(DEBUG_EXP, &cpu_dev, + "mt_iocl ERROR return chsa %04x status %08x\n", + chp->chan_dev, chp->chan_status); + return 1; /* error return */ + } + /* NOTE this code needed for MPX 1.X to run! */ + /* see if command completed */ + /* we have good status */ + if (chp->chan_status & (STATUS_DEND|STATUS_CEND)) { + uint16 chsa = GET_UADDR(uptr->u3); /* get channel & sub address */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* show I/O complete */ + sim_debug(DEBUG_XIO, dptr, + "mt_iocl @%06x FIFO #%1x cmd complete chan %04x status %04x count %04x\n", + chp->chan_caw, FIFO_Num(chsa), chan, chp->chan_status, chp->ccw_count); + } + } + /* the device processor returned OK (0), so wait for I/O to complete */ + /* nothing happening, so return */ + sim_debug(DEBUG_XIO, dptr, + "mt_iocl @%06x return, chsa %04x status %04x count %04x\n", + chp->chan_caw, chsa, chp->chan_status, chp->ccw_count); + return 0; /* good return */ +} + +/* start a tape operation */ +t_stat mt_preio(UNIT *uptr, uint16 chan) { + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); + uint16 chsa = GET_UADDR(uptr->CMD); + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + sim_debug(DEBUG_CMD, dptr, "mt_preio CMD %08x unit %02x chsa %04x incha %08x\n", + uptr->CMD, unit, chsa, chp->chan_inch_addr); + if ((!loading) && (chp->chan_inch_addr == 0)) { + sim_debug(DEBUG_CMD, dptr, + "mt_preio unit %02x chsa %04x NO INCH\n", unit, chsa); + /* no INCH yet, so do nothing */ + return SNS_CTLEND; + } + if ((uptr->CMD & MT_CMDMSK) != 0) { /* just return if busy */ + sim_debug(DEBUG_CMD, dptr, + "mt_preio unit %02x chsa %04x BUSY\n", unit, chsa); + return SNS_BSY; + } + if ((uptr->flags & UNIT_ATT) == 0) { /* unit attached status */ + /* set status */ + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + uptr->SNS &= ~(SNS_RDY|SNS_ONLN); /* unit not online or rdy */ + uptr->SNS &= ~SNS_LOAD; /* reset BOT detected */ + return SCPE_OK; /* good to go */ + } + + sim_debug(DEBUG_CMD, dptr, "mt_preio unit %02x chsa %04x OK\n", unit, chsa); + return SCPE_OK; /* good to go */ +} + +/* start an I/O operation */ +t_stat mt_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) +{ + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + sim_debug(DEBUG_EXP, dptr, "mt_startcmd entry chan %04x cmd %02x\n", chan, cmd); + if (mt_busy[GET_DEV_BUF(dptr->flags)] != 0 || (uptr->CMD & MT_CMDMSK) != 0) { + sim_debug(DEBUG_EXP, dptr, "mt_startcmd busy %02x chan %04x flags %08x CMD %02x\n", + mt_busy[GET_DEV_BUF(dptr->flags)], chan, dptr->flags, uptr->CMD); + uptr->flags |= MT_BUSY; /* Flag we need to send CUE */ + return SNS_BSY; + } + + sim_debug(DEBUG_EXP, dptr, "mt_startcmd processing unit %01x cmd %02x\n", unit, cmd); + + switch (cmd & 0xFF) { + case 0x00: /* INCH command */ + sim_debug(DEBUG_CMD, dptr, "start INCH command\n"); + + sim_debug(DEBUG_CMD, dptr, + "mt_startcmd starting INCH cmd, chsa %04x MemBuf %08x cnt %04x\n", + chsa, chp->ccw_addr, chp->ccw_count); + + /* UTX_needs_interrupt */ + cmd = MT_CMDMSK; /* insert INCH cmd as 0xff */ + /* fall through */ + case 0x03: /* Tape motion commands or NOP */ + case 0x13: /* Read and compare command */ + case 0x23: /* Rewind command */ + case 0x33: /* Rewind and unload */ + case 0x43: /* Advance record */ + case 0x53: /* Backspace record */ + case 0x63: /* Advance filemark */ + case 0x73: /* Backspace filemark */ + case 0x83: /* Set Mode command */ + case 0x93: /* Write Tape filemark */ + case 0xA3: /* Erase 3.5 of tape */ + /* UTX_needs_interrupt on NOP or INCH */ + /* fall through */ + case 0x01: /* Write command */ + case 0x02: /* Read command */ + case 0x0C: /* Read backward */ + if (cmd == 0x01) + sim_debug(DEBUG_EXP, dptr, + "mt_startcmd WRITE chan %04x addr %06x cnt %04x\n", + chan, chp->ccw_addr, chp->ccw_count); + if (cmd == 0x02) + sim_debug(DEBUG_EXP, dptr, + "mt_startcmd READ chan %04x addr %06x cnt %04x\n", + chan, chp->ccw_addr, chp->ccw_count); + if (cmd != 0x03) { /* if this is a nop do not zero status */ + uptr->SNS = (uptr->SNS & 0x0000ff00); /* clear all but byte 2 */ + } + if ((uptr->flags & UNIT_ATT) == 0) { /* unit attached status */ + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + uptr->SNS &= ~(SNS_RDY|SNS_ONLN); /* unit not online or rdy */ + uptr->SNS &= ~SNS_LOAD; /* reset BOT detected */ + sim_debug(DEBUG_CMD, dptr, "mt_startcmd detached sense %08x chan %04x cmd %02x\n", + uptr->SNS, chan, cmd); + } else { + uptr->SNS |= (SNS_RDY|SNS_ONLN); /* set ready status */ + if (sim_tape_wrp(uptr)) + uptr->SNS |= (SNS_WRP); /* write protected */ + if (sim_tape_bot(uptr)) + uptr->SNS |= (SNS_LOAD); /* tape at load point */ + if (sim_tape_eot(uptr)) + uptr->SNS |= (SNS_EOT); /* tape at EOM */ + sim_debug(DEBUG_CMD, dptr, "mt_startcmd attached sense %08x chan %04x cmd %02x\n", + uptr->SNS, chan, cmd); + } + /* Fall through */ + + case 0x04: /* Sense */ + uptr->CMD &= ~(MT_CMDMSK); /* clear out last cmd */ + uptr->CMD |= cmd & MT_CMDMSK; /* insert new cmd */ + CLR_BUF(uptr); /* buffer is empty */ + uptr->POS = 0; /* reset buffer position pointer */ + mt_busy[GET_DEV_BUF(dptr->flags)] = 1; /* show we are busy */ + sim_debug(DEBUG_EXP, dptr, "mt_startcmd sense %08x return OK chan %04x cmd %02x\n", + uptr->SNS, chan, cmd); + sim_activate(uptr, 20); /* Start unit off */ + return SCPE_OK; /* good to go */ + + default: /* invalid command */ + sim_debug(DEBUG_EXP, dptr, "mt_startcmd CMDREJ return chan %04x cmd %02x\n", + chan, cmd); + uptr->SNS |= SNS_CMDREJ; + /* send program check */ + return SNS_CHNEND|SNS_DEVEND|STATUS_PCHK; /* add DEVEND 08/16/20 */ + break; + } +} + +/* Map simH errors into machine errors */ +t_stat mt_error(UNIT *uptr, uint16 chsa, t_stat r, DEVICE *dptr) +{ + sim_debug(DEBUG_CMD, dptr, "mt_error status %08x\n", r); + mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1; /* not busy anymore */ + + switch (r) { /* switch on return value */ + case MTSE_OK: /* no error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we are done with command */ + break; + + case MTSE_TMK: /* tape mark */ + sim_debug(DEBUG_CMD, dptr, "FILE MARK\n"); + uptr->SNS |= SNS_FMRKDT; /* file mark detected */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + + case MTSE_WRP: /* write protected */ + uptr->SNS |= SNS_WRP; /* write protected */ + sim_debug(DEBUG_CMD, dptr, "WRITE PROTECT %08x\n", r); /* operator intervention */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we are done with command */ + break; + + case MTSE_UNATT: /* unattached */ + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + uptr->SNS &= ~(SNS_RDY|SNS_ONLN); /* unit not online or rdy */ + uptr->SNS &= ~SNS_LOAD; /* reset BOT detected */ + sim_debug(DEBUG_CMD, dptr, "ATTENTION %08x\n", r); /* operator intervention */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); + break; + + case MTSE_IOERR: /* IO error */ + case MTSE_FMT: /* invalid format */ + case MTSE_RECE: /* error in record */ + sim_debug(DEBUG_CMD, dptr, "ERROR %08x\n", r); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we are done with command */ + break; + + case MTSE_BOT: /* beginning of tape */ + uptr->SNS |= SNS_LOAD; /* tape at BOT */ + sim_debug(DEBUG_CMD, dptr, "BOT\n"); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); + break; + + case MTSE_INVRL: /* invalid rec lnt */ + case MTSE_EOM: /* end of medium */ + uptr->SNS |= SNS_EOT; /* tape at EOT */ + sim_debug(DEBUG_CMD, dptr, "EOT\n"); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); + break; + } + return SCPE_OK; +} + +/* Handle processing of tape requests. */ +t_stat mt_srv(UNIT *uptr) +{ + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); + int cmd = uptr->CMD & MT_CMDMSK; + int bufnum = GET_DEV_BUF(dptr->flags); + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + t_mtrlnt reclen; + t_stat r = SCPE_ARG; /* Force error if not set */ + int i; + char *bufp; + uint32 mema, m, skip; + uint16 len; + uint8 ch; + + sim_debug(DEBUG_CMD, dptr, + "mt_srv unit %02x cmd %02x POS %x hwmark %03x\n", + unit, cmd, uptr->POS, uptr->hwmark); + + switch (cmd) { + case MT_CMDMSK: /* 0x0ff for inch 0x00 */ /* INCH is for channel, nothing for us */ + len = chp->ccw_count; /* INCH command count */ + mema = chp->ccw_addr; /* get inch or buffer addr */ + sim_debug(DEBUG_CMD, dptr, + "mt_srv starting INCH %06x cmd, chsa %04x MemBuf %06x cnt %04x\n", + mema, chsa, chp->ccw_addr, chp->ccw_count); + + if (len == 0) { + /* we have invalid count, error, bail out */ + uptr->CMD &= ~(0xffff); /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + + /* the chp->ccw_addr location contains the inch address */ + /* call set_inch() to setup inch buffer */ + /* 4 wd buffer is provided for 2 status dbl words */ + i = set_inch(uptr, mema, 2); /* new address of 33 entries */ + + if ((i == SCPE_MEM) || (i == SCPE_ARG)) { /* any error */ + /* we have error, bail out */ + uptr->CMD &= ~(0xffff); /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + /* set halfwords 16 & 17 to 5 as default retry count in inch data */ + /* UTX uses this value to see if the device is a buffered tape processor */ + /* they must be non-zero and equal to be BTP */ + WMH(mema+(16<<1),5); /* write left HW with count */ + WMH(mema+(17<<1),5); /* write right HW with count */ + sim_debug(DEBUG_CMD, dptr, + "mt_srv cmd INCH chsa %04x chsa %06x count %04x completed word 16 %08x\n", + chsa, mema, chp->ccw_count, RMW(mema+(8<<2))); + uptr->CMD &= ~MT_CMDMSK; /* clear the cmd */ + mt_busy[bufnum] &= ~1; /* make our buffer not busy */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */ + return SCPE_OK; + + case 0x80: /* other? */ /* default to NOP */ + sim_debug(DEBUG_CMD, dptr, "mt_srv cmd 80 DIAG unit=%04x SNS %08x\n", unit, uptr->SNS); + ch = (uptr->SNS >> 24) & 0xff; /* get sense byte 0 status */ + sim_debug(DEBUG_CMD, dptr, "sense unit %02x byte 0 %02x\n", unit, ch); + chan_write_byte(chsa, &ch); /* write byte 0 */ + ch = (uptr->SNS >> 16) & 0xff; /* get sense byte 1 status */ + sim_debug(DEBUG_CMD, dptr, "sense unit %02x byte 1 %02x\n", unit, ch); + chan_write_byte(chsa, &ch); /* write byte 1 */ + ch = (uptr->SNS >> 8) & 0xff; /* get sense byte 2 status */ + sim_debug(DEBUG_CMD, dptr, "sense unit %02x byte 2 %02x\n", unit, ch); + chan_write_byte(chsa, &ch); /* write byte 2 */ + ch = (uptr->SNS >> 0) & 0xff; /* get sense byte 3 status */ + sim_debug(DEBUG_CMD, dptr, "sense unit %02x byte 3 %02x\n", unit, ch); + chan_write_byte(chsa, &ch); /* write byte 3 */ + /* write zero extra status */ + for (ch=4; ch < 0xc; ch++) { + uint8 zc = 0; + chan_write_byte(chsa, &zc); /* write zero byte */ + sim_debug(DEBUG_CMD, dptr, + "sense unit %02x byte %1x %02x\n", unit, ch, zc); + } + uptr->CMD &= ~MT_CMDMSK; /* clear the cmd */ + mt_busy[bufnum] &= ~1; /* make our buffer not busy */ + uptr->SNS = (uptr->SNS & 0x0000ff00); /* clear all but byte 2 */ + if ((uptr->flags & UNIT_ATT) == 0) /* unit attached status */ + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + else + uptr->SNS &= ~(SNS_RDY|SNS_ONLN); /* unit not online or rdy */ + sim_debug(DEBUG_CMD, dptr, "mt_srv DIAG SNS %08x char complete unit=%02x\n", + uptr->SNS, unit); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */ + return SCPE_OK; + + case MT_NOP: /* 0x03 */ /* NOP motion command */ + uptr->CMD &= ~MT_CMDMSK; /* clear the cmd */ + mt_busy[bufnum] &= ~1; /* make our buffer not busy */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */ + return SCPE_OK; + + case MT_SENSE: /* 0x04 */ /* get sense data */ + /* write requested status */ + len = chp->ccw_count; /* command count */ + for (i=0; i<4; i++) { + ch = 0; + if (i<4) + ch = (uptr->SNS >> (24-(i*8))) & 0xff; /* get 8 bits of status */ + chan_write_byte(chsa, &ch); /* write zero byte */ + sim_debug(DEBUG_CMD, dptr, + "sense unit %02x byte %1x %02x\n", unit, i, ch); + } + uptr->CMD &= ~MT_CMDMSK; /* clear the cmd */ + mt_busy[bufnum] &= ~1; /* make our buffer not busy */ + uptr->SNS = (uptr->SNS & 0x0000ff00); /* clear all but byte 2 */ + if (!(uptr->flags & UNIT_ATT)) /* unit attached status */ + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + else + uptr->SNS |= (SNS_RDY|SNS_ONLN); /* unit not online or rdy */ + sim_debug(DEBUG_CMD, dptr, "mt_srv SENSE %08x char complete unit=%02x\n", + uptr->SNS, unit); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */ + return SCPE_OK; + + case MT_SETM: /* 0x83 */ /* set mode byte */ + sim_debug(DEBUG_CMD, dptr, "mt_srv cmd 0x83 SETM unit=%02x\n", unit); + /* Grab data until channel has no more */ + if (chan_read_byte(chsa, &ch)) { + if (uptr->POS > 0) { /* Only if data in record */ + reclen = uptr->hwmark; /* set record length */ + ch = mt_buffer[bufnum][0]; /* get the first byte read */ + sim_debug(DEBUG_CMD, dptr, + "Write mode data done unit %02x chars %02x mode %02x\n", unit, reclen, ch); + /* put mode bits into byte 2 of SNS */ + uptr->SNS = (uptr->SNS & 0xffff00ff) | (ch << 8); + uptr->POS = 0; /* no bytes anymore */ + uptr->CMD &= ~MT_CMDMSK; /* no cmd to do */ + mt_busy[bufnum] &= ~1; /* set not busy */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return end status */ + } + } else { + mt_buffer[bufnum][uptr->POS++] = ch; /* save the character read in */ + sim_debug(DEBUG_CMD, dptr, "Write mode data in unit %02x POS %04x mode %02x\n", + unit, uptr->POS, ch); + uptr->hwmark = uptr->POS; /* set high water mark */ + sim_activate(uptr, 30); /* wait time */ + } + return SCPE_OK; + default: + break; + } + + /* only run these commands if we have a tape attached */ + if ((uptr->flags & UNIT_ATT) == 0) { /* unit attached status */ + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + uptr->SNS &= ~(SNS_RDY|SNS_ONLN); /* unit not online or rdy */ + uptr->SNS &= ~SNS_LOAD; /* reset BOT detected */ + mt_busy[bufnum] &= ~1; /* make our buffer not busy */ + /* we are completed with unit check status */ + uptr->CMD &= ~MT_CMDMSK; /* clear the cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + + switch (cmd) { + case MT_READ: /* 0x02 */ /* read a record from the device */ +reread: + if (uptr->CMD & MT_READDONE) { /* is the read complete */ + uptr->SNS &= ~(SNS_LOAD|SNS_EOT); /* reset BOT & EOT */ + if (sim_tape_eot(uptr)) { /* see if at EOM */ + uptr->SNS |= SNS_EOT; /* set EOT status */ + } + + uptr->CMD &= ~(MT_CMDMSK|MT_READDONE); /* clear all but readdone & cmd */ + uptr->CMD &= ~MT_CMDMSK; /* clear the cmd */ + mt_busy[bufnum] &= ~1; /* not busy anymore */ + sim_debug(DEBUG_CMD, dptr, + "mt_srv READ %04x char complete unit=%02x sense %08x\n", + uptr->POS, unit, uptr->SNS); + if (uptr->SNS & SNS_EOT) + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); /* set CE, DE, UE */ + else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* set CE, DE */ + break; + } + /* read is not completed, get an input char */ + /* If empty buffer, fill */ + if (BUF_EMPTY(uptr)) { + m = chp->ccw_addr & MASK24; /* memory buffer address */ + /* buffer is empty, so fill it with next record data */ + if ((r = sim_tape_rdrecf(uptr, &mt_buffer[bufnum][0], &reclen, BUFFSIZE)) != MTSE_OK) { + sim_debug(DEBUG_CMD, dptr, "mt_srv READ fill buffer unit=%02x\n", unit); + uptr->CMD &= ~(MT_CMDMSK|MT_READDONE); /* clear all but readdone & cmd */ + return mt_error(uptr, chsa, r, dptr); /* process any error & return status */ + } + uptr->SNS &= ~(SNS_LOAD|SNS_EOT); /* reset BOT & EOT */ + uptr->POS = 0; /* reset buffer position */ + uptr->hwmark = reclen; /* set buffer chars read in */ + sim_debug(DEBUG_CMD, dptr, "mt_srv READ fill buffer %06x complete count %04x\n", m, reclen); + + bufp = dump_buf(&mt_buffer[bufnum][0], 0, 16); + sim_debug(DEBUG_CMD, dptr, "mt_srv READ buf %s\n", bufp); + bufp = dump_buf(&mt_buffer[bufnum][0], 16, 16); + sim_debug(DEBUG_CMD, dptr, "mt_srv READ buf %s\n", bufp); + bufp = dump_buf(&mt_buffer[bufnum][0], 32, 16); + sim_debug(DEBUG_CMD, dptr, "mt_srv READ buf %s\n", bufp); + + m = chp->ccw_addr & MASK24; /* memory buffer address */ + bufp = dump_mem(m, 16); + sim_debug(DEBUG_CMD, dptr, "mt_srv READ mem %s\n", bufp); + bufp = dump_mem(m+16, 16); + sim_debug(DEBUG_CMD, dptr, "mt_srv READ mem %s\n", bufp); + bufp = dump_mem(m+32, 16); + sim_debug(DEBUG_CMD, dptr, "mt_srv READ mem %s\n", bufp); + } + /* get a char from the buffer */ + ch = mt_buffer[bufnum][uptr->POS++]; + + /* Send character over to channel */ + if (chan_write_byte(chsa, &ch)) { + sim_debug(DEBUG_CMD, dptr, + "Read unit %02x EOR cnt %04x hwm %04x\n", unit, uptr->POS-1, uptr->hwmark); + /* If not read whole record, skip till end */ + if ((uint32)uptr->POS < uptr->hwmark) { + /* Send dummy character to force SLI */ + chan_write_byte(chsa, &ch); /* write the byte */ + sim_debug(DEBUG_CMD, dptr, "Read unit %02x send dump SLI\n", unit); + sim_activate(uptr, (uptr->hwmark-uptr->POS) * 4); /* wait again */ + uptr->CMD |= MT_READDONE; /* read is done */ + break; + } + sim_debug(DEBUG_CMD, dptr, + "Read data @1 unit %02x cnt %04x ch %02x hwm %04x\n", + unit, uptr->POS, ch, uptr->hwmark); +#ifndef OLDWAY + uptr->CMD &= ~MT_CMDMSK; /* clear the cmd */ + mt_busy[bufnum] &= ~1; /* set not busy */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return end status */ +#else + sim_activate(uptr, 50); + uptr->CMD |= MT_READDONE; /* read is done */ + break; +#endif + } else { + sim_debug(DEBUG_DATA, dptr, + "Read data @2 unit %02x cnt %04x ch %02x hwm %04x\n", + unit, uptr->POS, ch, uptr->hwmark); + if ((uint32)uptr->POS >= uptr->hwmark) { /* In IRG */ + /* Handle end of data record */ + sim_debug(DEBUG_CMD, dptr, + "Read end of data unit %02x cnt %04x ch %02x hwm %04x\n", + unit, uptr->POS, ch, uptr->hwmark); + uptr->CMD |= MT_READDONE; /* read is done */ + goto reread; + } else + goto reread; + } + break; + + case MT_WRITE: /* 0x01 */ /* write record */ + /* Check if write protected */ + if (sim_tape_wrp(uptr)) { + uptr->SNS |= SNS_CMDREJ; + uptr->CMD &= ~MT_CMDMSK; + mt_busy[bufnum] &= ~1; + sim_debug(DEBUG_CMD, dptr, "Write write protected unit=%02x\n", unit); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + +rewrite: + /* Grab data until channel has no more */ + if (chan_read_byte(chsa, &ch)) { + if (uptr->POS > 0) { /* Only if data in record */ + reclen = uptr->hwmark; + sim_debug(DEBUG_CMD, dptr, "Write unit=%02x Block %04x chars\n", + unit, reclen); + r = sim_tape_wrrecf(uptr, &mt_buffer[bufnum][0], reclen); + uptr->POS = 0; + uptr->CMD &= ~MT_CMDMSK; + mt_error(uptr, chsa, r, dptr); /* Record errors */ + } + } else { + mt_buffer[bufnum][uptr->POS++] = ch; + sim_debug(DEBUG_DATA, dptr, "Write data unit=%02x %04x %02x\n", + unit, uptr->POS, ch); + uptr->hwmark = uptr->POS; + goto rewrite; + } + break; + + case MT_RDBK: /* 0x0C */ /* Read Backwards */ + if (uptr->CMD & MT_READDONE) { + uptr->CMD &= ~(MT_CMDMSK|MT_READDONE); + mt_busy[bufnum] &= ~1; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } + + /* If at end of record, fill buffer */ + if (BUF_EMPTY(uptr)) { + if (sim_tape_bot(uptr)) { + uptr->CMD &= ~MT_CMDMSK; + mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + sim_debug(DEBUG_CMD, dptr, "Read backward unit=%02x\n", unit); + if ((r = sim_tape_rdrecr(uptr, &mt_buffer[bufnum][0], &reclen, BUFFSIZE)) != MTSE_OK) { + uptr->CMD &= ~(MT_CMDMSK|MT_READDONE); + return mt_error(uptr, chsa, r, dptr); + } + uptr->POS = reclen; + uptr->hwmark = reclen; + sim_debug(DEBUG_CMD, dptr, "Binary Block %04x chars\n", reclen); + } + + ch = mt_buffer[bufnum][--uptr->POS]; + + if (chan_write_byte(chsa, &ch)) { + sim_debug(DEBUG_CMD, dptr, "Read unit=%02x EOR cnt %04x\n", + unit, uptr->POS); + /* If not read whole record, skip till end */ + if (uptr->POS >= 0) { + sim_activate(uptr, (uptr->POS) * 10); + uptr->CMD |= MT_READDONE; + return SCPE_OK; + } + uptr->CMD &= ~MT_CMDMSK; + mt_busy[bufnum] &= ~1; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + } else { + sim_debug(DEBUG_CMD, dptr, "Read data unit=%02x %04x %02x\n", + unit, uptr->POS, ch); + if (uptr->POS == 0) { /* In IRG */ + uptr->CMD &= ~MT_CMDMSK; + mt_busy[bufnum] &= ~1; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + } else + sim_activate(uptr, 30); + } + break; + + case MT_WTM: /* 0x93 */ /* Write tape filemark */ + if (uptr->POS == 0) { + if (sim_tape_wrp(uptr)) { + uptr->SNS |= SNS_CMDREJ; + uptr->CMD &= ~MT_CMDMSK; + mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + uptr->POS ++; + sim_activate(uptr, 100); + } else { + sim_debug(DEBUG_CMD, dptr, "Write Mark unit=%02x\n", unit); + uptr->CMD &= ~(MT_CMDMSK); + r = sim_tape_wrtmk(uptr); + chan_end(chsa, SNS_DEVEND); + mt_busy[bufnum] &= ~1; + } + break; + + case MT_BSR: /* 0x53 */ /* Backspace record */ + sim_debug(DEBUG_CMD, dptr, "mt_srv cmd 0x53 BSR unit %02x POS %x SNS %08x\n", + unit, uptr->POS, uptr->SNS); + switch (uptr->POS ) { + case 0: + if (sim_tape_bot(uptr)) { + uptr->CMD &= ~MT_CMDMSK; + mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + uptr->POS++; + sim_activate(uptr, 30); + break; + case 1: + uptr->POS++; + r = sim_tape_sprecr(uptr, &reclen); + sim_debug(DEBUG_CMD, dptr, "Backspace rec unit %02x POS %x r %x\n", + unit, uptr->POS, r); + /* SEL requires Unit Except & EOF on EOF */ + if (r == MTSE_TMK) { /* test for EOF */ + uptr->POS++; + sim_debug(DEBUG_CMD, dptr, "BSR got EOF MARK\n"); + sim_activate(uptr, 30); + /* SEL requires Unit Except & BOT on BOT */ + } else if (r == MTSE_BOT) { + uptr->POS+= 2; + sim_debug(DEBUG_CMD, dptr, "BSR got BOT\n"); + sim_activate(uptr, 30); + } else { + sim_debug(DEBUG_CMD, dptr, "Backspace reclen %04x SNS %08x\n", reclen, uptr->SNS); + sim_activate(uptr, 30); + } + break; + case 2: + uptr->CMD &= ~(MT_CMDMSK); + mt_busy[bufnum] &= ~1; + sim_debug(DEBUG_CMD, dptr, "Backspace record completed with NO status\n"); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + case 3: /* EOF */ + uptr->CMD &= ~(MT_CMDMSK); + mt_busy[bufnum] &= ~1; + uptr->SNS |= SNS_FMRKDT; /* file mark detected */ + sim_debug(DEBUG_CMD, dptr, "Backspace record completed with EOF status\n"); + chan_end(chsa, SNS_DEVEND|SNS_UNITEXP); + break; + case 4: /* BOT */ + uptr->CMD &= ~(MT_CMDMSK); + mt_busy[bufnum] &= ~1; + uptr->SNS |= SNS_LOAD; /* set BOT detected */ + sim_debug(DEBUG_CMD, dptr, "Backspace record completed with BOT status\n"); + chan_end(chsa, SNS_DEVEND|SNS_UNITEXP); + break; + } + break; + + case MT_BSF: /* 0x73 */ /* Backspace file */ + sim_debug(DEBUG_CMD, dptr, "mt_srv cmd 0x73 BSF unit %02x POS %04x\n", + unit, uptr->POS); + switch(uptr->POS) { + case 0: + if (sim_tape_bot(uptr)) { + uptr->CMD &= ~MT_CMDMSK; + mt_busy[bufnum] &= ~1; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + uptr->POS++; + sim_activate(uptr, 100); + break; + case 1: +//#define NBSF +#ifdef NBSF + skip = 1; /* skip 1 file */ +#endif + uptr->SNS &= ~(SNS_LOAD|SNS_EOT|SNS_FMRKDT); /* reset BOT, EOT, EOF */ +#ifdef NBSF + /* using the backspace file call does not work with MPX */ + r = sim_tape_spfiler(uptr, skip, &reclen); + uptr->POS++; +#else + r = sim_tape_sprecr(uptr, &reclen); +#endif + sim_debug(DEBUG_CMD, dptr, "Backspace file unit=%02x r %x\n", unit, r); + if (r == MTSE_TMK) { + uptr->POS++; + sim_debug(DEBUG_CMD, dptr, "BSF got EOF MARK\n"); + sim_activate(uptr, 30); + } else if (r == MTSE_BOT) { + uptr->POS+= 2; + sim_debug(DEBUG_CMD, dptr, "BSF got BOT\n"); + sim_activate(uptr, 30); + } else { + /* already there */ + sim_debug(DEBUG_CMD, dptr, "Backspace file reclen %04x\n", reclen); + sim_activate(uptr, 20); + } + break; +#ifdef NBSF + case 2: + uptr->CMD &= ~(MT_CMDMSK); + /* no EOF detected, but we did go back 1 record */ + uptr->SNS |= SNS_FMRKDT; /* file mark detected */ + mt_busy[bufnum] &= ~1; + sim_debug(DEBUG_CMD, dptr, "Backspace file Completed with NO EOF status\n"); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); + break; +#endif +#ifdef NBSF + case 3: /* File Mark */ +#else + case 2: /* File Mark */ +#endif + uptr->CMD &= ~(MT_CMDMSK); + uptr->SNS |= SNS_FMRKDT; /* file mark detected */ + mt_busy[bufnum] &= ~1; + sim_debug(DEBUG_CMD, dptr, "Backspace file Completed with EOF status\n"); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); + break; +#ifdef NBSF + case 4: /* BOT */ +#else + case 3: /* BOT */ +#endif + uptr->CMD &= ~(MT_CMDMSK); + mt_busy[bufnum] &= ~1; + uptr->SNS |= SNS_LOAD; /* set BOT detected */ + sim_debug(DEBUG_CMD, dptr, "Backspace file Completed with BOT status\n"); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); + break; + } + break; + + case MT_FSR: /* 0x43 */ /* Advance record */ + switch(uptr->POS) { + case 0: + sim_debug(DEBUG_CMD, dptr, "Skip rec entry unit=%02x POS %x\n", unit, uptr->POS); + uptr->POS++; + sim_activate(uptr, 30); + break; + case 1: + uptr->POS++; + uptr->SNS &= ~(SNS_LOAD|SNS_EOT|SNS_FMRKDT); /* reset BOT, EOT, EOF */ + r = sim_tape_sprecf(uptr, &reclen); + sim_debug(DEBUG_CMD, dptr, "Skip rec unit=%02x r %x\n", unit, r); + if (r == MTSE_TMK) { + uptr->POS = 3; + uptr->SNS |= SNS_FMRKDT; /* file mark detected */ + sim_debug(DEBUG_CMD, dptr, "FSR got EOF MARK\n"); + sim_activate(uptr, 30); + } else if (r == MTSE_EOM) { + uptr->POS = 4; + uptr->SNS |= SNS_EOT; /* set EOT status */ + sim_debug(DEBUG_CMD, dptr, "FSR got EOT\n"); + sim_activate(uptr, 30); + } else { + sim_debug(DEBUG_CMD, dptr, "FSR skipped %04x byte record\n", + reclen); + sim_activate(uptr, 30); + } + break; + case 2: + uptr->CMD &= ~(MT_CMDMSK); + mt_busy[bufnum] &= ~1; + sim_debug(DEBUG_CMD, dptr, "Skip record Completed\n"); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */ + break; + case 3: + uptr->CMD &= ~(MT_CMDMSK); + mt_busy[bufnum] &= ~1; + sim_debug(DEBUG_CMD, dptr, "Skip record now at EOF\n"); + chan_end(chsa, SNS_DEVEND|SNS_UNITEXP); + break; + case 4: + uptr->CMD &= ~(MT_CMDMSK); + mt_busy[bufnum] &= ~1; + sim_debug(DEBUG_CMD, dptr, "Skip record now at EOT\n"); + chan_end(chsa, SNS_DEVEND|SNS_UNITEXP); + break; + } + break; + + case MT_FSF: /* 0x63 */ /* advance filemark */ + switch(uptr->POS) { + case 0: + sim_debug(DEBUG_CMD, dptr, + "Skip file entry sense %08x unit %02x\n", uptr->SNS, unit); + uptr->POS++; + sim_activate(uptr, 30); + break; + case 1: + skip = 1; /* skip forward 1 file */ + uptr->POS++; + uptr->SNS &= ~(SNS_LOAD|SNS_EOT|SNS_FMRKDT); /* reset BOT, EOT, EOF */ + r = sim_tape_spfilef(uptr, skip, &reclen); + sim_debug(DEBUG_CMD, dptr, "Skip file unit=%02x r %x\n", unit, r); + if (r == MTSE_TMK) { + uptr->POS++; + uptr->SNS |= SNS_FMRKDT; /* file mark detected */ + sim_debug(DEBUG_CMD, dptr, "FSF EOF MARK sense %08x\n", uptr->SNS); + sim_activate(uptr, 30); + } else if (r == MTSE_EOM) { + uptr->SNS |= SNS_EOT; /* set EOT status */ + sim_debug(DEBUG_CMD, dptr, "FSF EOT sense %08x\n", uptr->SNS); + uptr->POS+= 2; + sim_activate(uptr, 30); + } else { + sim_debug(DEBUG_CMD, dptr, "FSF skipped %04x file\n", reclen); + sim_activate(uptr, 30); + } + break; + case 2: + uptr->CMD &= ~(MT_CMDMSK); + mt_busy[bufnum] &= ~1; + sim_debug(DEBUG_CMD, dptr, + "Skip file done sense %08x unit %02x\n", uptr->SNS, unit); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */ + break; + case 3: + uptr->CMD &= ~(MT_CMDMSK); + mt_busy[bufnum] &= ~1; + sim_debug(DEBUG_CMD, dptr, + "Skip file got EOF sense %08x unit %02x\n", uptr->SNS, unit); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); + break; + case 4: + uptr->CMD &= ~(MT_CMDMSK); + uptr->SNS |= SNS_EOT; /* set EOT status */ + mt_busy[bufnum] &= ~1; + sim_debug(DEBUG_CMD, dptr, + "Skip file got EOT sense %08x unit %02x\n", uptr->SNS, unit); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); + break; + } + break; + + case MT_ERG: /* 0xA3 */ /* Erace 3.5 in tape */ + switch (uptr->POS) { + case 0: + if (sim_tape_wrp(uptr)) { + uptr->SNS |= SNS_CMDREJ; + uptr->CMD &= ~MT_CMDMSK; + mt_busy[bufnum] &= ~1; + chan_end(chsa, SNS_DEVEND|SNS_UNITEXP); + } else { + uptr->POS ++; + sim_activate(uptr, 50); + } + break; + case 1: + sim_debug(DEBUG_CMD, dptr, "Erase unit=%02x\n", unit); + r = sim_tape_wrgap(uptr, 35); + sim_activate(uptr, 100); + uptr->POS++; + break; + case 2: + uptr->CMD &= ~(MT_CMDMSK); + mt_busy[bufnum] &= ~1; + /* we are done dev|chan end */ + chan_end(chsa, SNS_DEVEND); + break; + } + break; + + case MT_REW: /* 0x23 */ /* rewind tape */ + if (uptr->POS == 0) { + uptr->POS++; + sim_debug(DEBUG_CMD, dptr, "Start rewind unit %02x\n", unit); + sim_activate(uptr, 2500); + } else { + sim_debug(DEBUG_CMD, dptr, "Rewind complete unit %02x\n", unit); + uptr->CMD &= ~(MT_CMDMSK); + r = sim_tape_rewind(uptr); + uptr->SNS |= SNS_LOAD; /* set BOT */ + mt_busy[bufnum] &= ~1; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */ + } + break; + + case MT_RUN: /* 0x33 */ /* Rewind and unload tape */ + if (uptr->POS == 0) { + uptr->POS++; + mt_busy[bufnum] &= ~1; + sim_debug(DEBUG_CMD, dptr, "Start rewind/unload unit %02x\n", unit); + sim_activate(uptr, 300); + } else { + sim_debug(DEBUG_CMD, dptr, "Unload unit=%02x\n", unit); + uptr->CMD &= ~(MT_CMDMSK); + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + uptr->SNS &= ~(SNS_RDY|SNS_ONLN); /* unit not online or rdy */ + uptr->SNS &= ~SNS_LOAD; /* reset BOT detected */ + r = sim_tape_detach(uptr); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */ + } + break; + } + return SCPE_OK; +} + +/* initialize the tape chan/unit */ +void mt_ini(UNIT *uptr, t_bool f) +{ + DEVICE *dptr = get_dev(uptr); + if (MT_DENS(uptr->dynflags) == 0) + uptr->dynflags |= MT_DENS_6250 << UNIT_S_DF_TAPE; + + uptr->CMD &= ~0xffff; /* clear out the flags but leave ch/sa */ + uptr->SNS = 0; /* clear sense data */ + uptr->SNS |= (SNS_RDY|SNS_ONLN); /* set initial status */ + mt_busy[GET_DEV_BUF(dptr->flags)] = 0; /* set not busy */ + sim_cancel(uptr); /* cancel any timers */ + sim_debug(DEBUG_EXP, dptr, "MT init device %s unit %02x\n", + dptr->name, GET_UADDR(uptr->CMD)); +} + +/* handle rschnlio cmds for tape */ +t_stat mt_rschnlio(UNIT *uptr) { + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & MT_CMDMSK; + + sim_debug(DEBUG_EXP, dptr, "mt_rschnl chsa %04x cmd = %02x\n", chsa, cmd); + mt_ini(uptr, 0); /* reset the unit */ + return SCPE_OK; +} + +/* Handle haltio transfers for mag tape */ +t_stat mt_haltio(UNIT *uptr) { + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & MT_CMDMSK; + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + DEVICE *dptr = get_dev(uptr); + + sim_debug(DEBUG_EXP, dptr, + "mt_haltio enter chsa %04x cmd = %02x\n", chsa, cmd); + + /* terminate any input command */ + /* UTX wants SLI bit, but no unit exception */ + /* status must not have an error bit set */ + /* otherwise, UTX will panic with "bad status" */ + if (cmd != 0) { /* is unit busy */ + sim_debug(DEBUG_CMD, dptr, + "mt_haltio HIO chsa %04x cmd = %02x ccw_count %02x\n", + chsa, cmd, chp->ccw_count); + sim_cancel(uptr); /* stop timer */ + } else { + sim_debug(DEBUG_CMD, dptr, + "mt_haltio HIO not busy chsa %04x cmd = %02x ccw_count %02x\n", + chsa, cmd, chp->ccw_count); + } + /* stop any I/O and post status and return error status */ + uptr->CMD &= LMASK; /* make non-busy */ + uptr->POS = 0; /* clear position data */ + uptr->SNS = SNS_RDY|SNS_ONLN; /* status is online & ready */ + chp->ccw_count = 0; /* zero the count */ + chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* reset chaining bits */ + sim_debug(DEBUG_CMD, dptr, + "mt_haltio HIO I/O stop chsa %04x cmd = %02x\n", chsa, cmd); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* force end */ + return SCPE_IOERR; /* tell chan code to post status */ +} +/* reset the mag tape */ +t_stat mt_reset(DEVICE *dptr) +{ + /* nothing to do?? */ + sim_debug(DEBUG_EXP, dptr, "MT reset name %s\n", dptr->name); + return SCPE_OK; +} + +/* attach the specified file to the tape device */ +t_stat mt_attach(UNIT *uptr, CONST char *file) +{ + uint16 chsa = GET_UADDR(uptr->CMD); /* get address of mt device */ + CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */ + DEVICE *dptr = get_dev(uptr); /* get device pointer */ + t_stat r; + DIB *dibp = 0; + + if (dptr->flags & DEV_DIS) { + fprintf(sim_deb, "ERROR===ERROR\nMT device %s disabled on system, aborting\r\n", + dptr->name); + printf("ERROR===ERROR\nMT device %s disabled on system, aborting\r\n", + dptr->name); + return SCPE_UDIS; /* device disabled */ + } + + /* mount the specified file to the MT */ + if ((r = sim_tape_attach(uptr, file)) != SCPE_OK) { + fprintf(sim_deb, "mt_attach ERROR filename %s status %08x\r\n", file, r); + return r; /* report any error */ + } + sim_debug(DEBUG_EXP, dptr, "mt_attach complete filename %s\n", file); + uptr->CMD &= ~0xffff; /* clear out the flags but leave ch/sa */ + uptr->POS = 0; /* clear position data */ + uptr->SNS = 0; /* clear sense data */ + uptr->SNS |= SNS_ONLN; /* 0x40 Drive Online */ + + /* check for valid configured tape */ + /* must have valid DIB and Channel Program pointer */ + dibp = (DIB *)dptr->ctxt; /* get the DIB pointer */ + if ((dib_unit[chsa] == NULL) || (dibp == NULL) || (chp == NULL)) { + sim_debug(DEBUG_CMD, dptr, + "ERROR===ERROR\nMT device %s not configured on system, aborting\n", + dptr->name); + printf("ERROR===ERROR\nMT device %s not configured on system, aborting\r\n", + dptr->name); + fprintf(sim_deb, "ERROR===ERROR\nMT device %s not configured on system, aborting\r\n", + dptr->name); + detach_unit(uptr); /* detach if error */ + return SCPE_UNATT; /* error */ + } + set_devattn(chsa, SNS_DEVEND); /* ready int???? */ + return SCPE_OK; /* return good status */ +} + +/* detach the MT device and unload any tape */ +t_stat mt_detach(UNIT *uptr) +{ + DEVICE *dptr = get_dev(uptr); /* get device pointer */ + sim_debug(DEBUG_EXP, dptr, "mt_detach\n"); + uptr->CMD &= ~0xffff; /* clear out the flags but leave ch/sa */ + uptr->POS = 0; /* clear position data */ + uptr->SNS = 0; /* clear sense data */ + uptr->flags &= ~MTUF_WRP; /* clear write protect */ + uptr->flags &= ~UNIT_RO; /* clear read only */ + return sim_tape_detach(uptr); +} + +/* boot from the specified tape unit */ +t_stat mt_boot(int32 unit_num, DEVICE *dptr) +{ + UNIT *uptr = &dptr->units[unit_num]; /* find tape unit pointer */ + + /* see if device disabled */ + if (dptr->flags & DEV_DIS) { + printf("ERROR===ERROR\r\nMT device %s disabled on system, aborting\r\n", + dptr->name); + return SCPE_UDIS; /* device disabled */ + } + sim_debug(DEBUG_EXP, dptr, "MT Boot dev/unit %04x\n", GET_UADDR(uptr->CMD)); + printf("MT Boot dev/unit %04x\r\n", GET_UADDR(uptr->CMD)); + if ((uptr->flags & UNIT_ATT) == 0) { /* Is MT device already attached? */ + sim_debug(DEBUG_EXP, dptr, + "MT Boot attach error dev/unit %04x\n", GET_UADDR(uptr->CMD)); + printf("MT Boot attach error dev/unit %04x\r\n", GET_UADDR(uptr->CMD)); + return SCPE_UNATT; /* not attached, return error */ + } + SPAD[0xf4] = GET_UADDR(uptr->CMD); /* put boot device chan/sa into spad */ + SPAD[0xf8] = 0xF000; /* show as F class device */ + + uptr->CMD &= ~0xffff; /* clear out old status */ + return chan_boot(GET_UADDR(uptr->CMD), dptr); /* boot the ch/sa */ +} + +t_stat mt_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + char buffer[256]; + fprintf (st, "%s\n\n", mt_description(dptr)); + fprintf (st, "The mag tape drives support the BOOT command\n\n"); + (void)sim_tape_density_supported (buffer, sizeof(buffer), valid_dens); + fprintf (st, " The density of the mag tape drive can be set with\n"); + fprintf (st, " SET %s DENSITY=%s\n\n", dptr->name, buffer); + sim_tape_attach_help (st, dptr, uptr, flag, cptr); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char *mt_description(DEVICE *dptr) +{ + return "8051 Buffered Tape Processor"; +} + +#endif /* NUM_DEVS_MT */ diff --git a/SEL32/sel32_scfi.c b/SEL32/sel32_scfi.c new file mode 100644 index 00000000..8785a199 --- /dev/null +++ b/SEL32/sel32_scfi.c @@ -0,0 +1,1997 @@ +/* sel32_scfi.c: SEL-32 SCFI SCSI Disk Controller + + Copyright (c) 2018-2022, James C. Bevier + Portions provided by Richard Cornwell and other SIMH contributers + + 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 + JAMES C. BEVIER 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. +*/ + +#include "sel32_defs.h" + +/* uncomment to use fast sim_activate times when running UTX */ +/* UTX gets an ioi error for dm0801 if slow times are used */ +/* dm0801 is not even a valid unit number for UDP controller */ +#define FAST_FOR_UTX + +#if NUM_DEVS_SCFI > 0 + +#define UNIT_SCFI UNIT_ATTABLE | UNIT_IDLE | UNIT_DISABLE + +/* useful conversions */ +/* Fill STAR value from cyl, trk, sec data */ +#define CHS2STAR(c,h,s) (((c<<16) & LMASK)|((h<<8) & 0xff00)|(s & 0xff)) +/* convert STAR value to number of sectors */ +#define STAR2SEC(star,spt,spc) ((star&0xff)+(((star>>8)&0xff)*spt)+(((star>>16)&0xffff)*spc)) +/* convert STAR value to number of heads or tracks */ +#define STAR2TRK(star,tpc) (((star>>16)&0xffff)*tpc+((star>>8)&0x0ff)) +/* convert STAR value to number of cylinders */ +#define STAR2CYL(star) ((star>>16)&RMASK) +/* convert byte value to number of sectors mod sector size */ +#define BYTES2SEC(bytes,ssize) (((bytes) + (ssize-1)) >> 10) +/* get sectors per track for specified type */ +#define SPT(type) (scfi_type[type].spt) +/* get sectors per cylinder for specified type */ +#define SPC(type) (scfi_type[type].spt*scfi_type[type].nhds) +/* get number of tracks for specified type */ +#define TRK(type) (scfi_type[type].cyl*scfi_type[type].nhds) +/* get number of cylinders for specified type */ +#define CYL(type) (scfi_type[type].cyl) +/* get number of heads for specified type */ +#define HDS(type) (scfi_type[type].nhds) +/* get disk capacity in sectors for specified type */ +#define CAP(type) (CYL(type)*HDS(type)*SPT(type)) +/* get number of bytes per sector for specified type */ +#define SSB(type) (scfi_type[type].ssiz*4) +/* get disk capacity in bytes for specified type */ +#define CAPB(type) (CAP(type)*SSB(type)) +/* get disk geometry as STAR value for specified type */ +#define GEOM(type) (CHS2STAR(CYL(type),HDS(type),SPT(type))) + +/* INCH command information */ +/* +WD 0 - Data address +WD 1 - Flags - 0 -36 byte count + +Data - 224 word INCH buffer address (SST) +WD 1 Drive 0 Attribute register +WD 2 Drive 1 Attribute register +WD 3 Drive 2 Attribute register +WD 4 Drive 3 Attribute register +WD 5 Drive 4 Attribute register +WD 6 Drive 5 Attribute register +WD 7 Drive 6 Attribute register +WD 8 Drive 7 Attribute register + +Memory attribute register layout +bits 0-7 - Flags + bits 0&1 - 00=Reserved, 01=MHD, 10=FHD, 11=MHD with FHD option + bit 2 - 1=Cartridge module drive + bit 3 - 0=Reserved + bit 4 - 1=Drive not present + bit 5 - 1=Dual Port + bit 6&7 - 0=Reserved +bits 8-15 - sector count (sectors per track)(F16=16, F20=20) +bits 16-23 - MHD Head count (number of heads on MHD) +bits 24-31 - FHD head count (number of heads on FHD or number head on FHD option of + mini-module) +*/ + +/* 224 word INCH Buffer layout */ +/* 128 word subchannel status storage (SST) */ +/* 66 words of program status queue (PSQ) */ +/* 26 words of scratchpad */ +/* 4 words of label buffer registers */ + +#define CMD u3 +/* u3 */ +/* in u3 is device command code and status */ +#define DSK_CMDMSK 0x00ff /* Command being run */ +#define DSK_STAR 0x0100 /* STAR value in u4 */ +#define DSK_NU2 0x0200 /* */ +#define DSK_READDONE 0x0400 /* Read finished, end channel */ +#define DSK_ENDDSK 0x0800 /* Sensed end of disk */ +#define DSK_SEEKING 0x1000 /* Disk is currently seeking */ +#define DSK_READING 0x2000 /* Disk is reading data */ +#define DSK_WRITING 0x4000 /* Disk is writing data */ +#define DSK_BUSY 0x8000 /* Disk is busy */ +/* commands */ +#define DSK_INCH 0x00 /* Initialize channel */ +#define DSK_ICH 0xFF /* Initialize controller */ +#define DSK_INCH2 0xF0 /* Initialize channel for processing */ +#define DSK_WD 0x01 /* Write data */ +#define DSK_RD 0x02 /* Read data */ +#define DSK_NOP 0x03 /* No operation */ +#define DSK_SNS 0x04 /* Sense */ +#define DSK_SCK 0x07 /* Seek cylinder, track, sector */ +#define DSK_TIC 0x08 /* Transfer in channel */ +#define DSK_FNSK 0x0B /* Format for no skip */ +#define DSK_LPL 0x13 /* Lock protected label */ +#define DSK_LMR 0x1F /* Load mode register */ +#define DSK_RES 0x23 /* Reserve */ +#define DSK_WSL 0x31 /* Write sector label */ +#define DSK_RSL 0x32 /* Read sector label */ +#define DSK_REL 0x33 /* Release */ +#define DSK_XEZ 0x37 /* Rezero */ +#define DSK_POR 0x43 /* Priority Override */ +#define DSK_IHA 0x47 /* Increment head address */ +#define DSK_SRM 0x4F /* Set reserve track mode */ +#define DSK_WTL 0x51 /* Write track label */ +#define DSK_RTL 0x52 /* Read track label */ +#define DSK_XRM 0x5F /* Reset reserve track mode */ +#define DSK_RAP 0xA2 /* Read angular positions */ +#define DSK_TESS 0xAB /* Test STAR (subchannel target address register) */ +#define DSK_REC 0xB2 /* Read ECC correction mask */ +#define DSK_ICH 0xFF /* Initialize Controller */ + +#define STAR u4 +/* u4 - sector target address register (STAR) */ +/* Holds the current cylinder, head(track), sector */ +#define DISK_CYL 0xFFFF0000 /* cylinder mask */ +#define DISK_TRACK 0x0000FF00 /* track mask */ +#define DISK_SECTOR 0x000000ff /* sector mask */ + +#define SNS u5 +/* u5 */ +/* Sense byte 0 - mode register */ +#define SNS_DROFF 0x80000000 /* Drive Carriage will be offset */ +#define SNS_TRKOFF 0x40000000 /* Track offset: 0=positive, 1=negative */ +#define SNS_RDTMOFF 0x20000000 /* Read timing offset = 1 */ +#define SNS_RDSTRBT 0x10000000 /* Read strobe timing: 1=positive, 0=negative */ +#define SNS_DIAGMOD 0x08000000 /* Diagnostic Mode ECC Code generation and checking */ +#define SNS_RSVTRK 0x04000000 /* Reserve Track mode: 1=OK to write, 0=read only */ +#define SNS_FHDOPT 0x02000000 /* FHD or FHD option = 1 */ +#define SNS_RESERV 0x01000000 /* Reserved */ + +/* Sense byte 1 */ +#define SNS_CMDREJ 0x800000 /* Command reject */ +#define SNS_INTVENT 0x400000 /* Unit intervention required */ +#define SNS_SPARE1 0x200000 /* Spare */ +#define SNS_EQUCHK 0x100000 /* Equipment check */ +#define SNS_DATCHK 0x080000 /* Data Check */ +#define SNS_OVRRUN 0x040000 /* Data overrun/underrun */ +#define SNS_DSKFERR 0x020000 /* Disk format error */ +#define SNS_DEFTRK 0x010000 /* Defective track encountered */ + +/* Sense byte 2 */ +#define SNS_LAST 0x8000 /* Last track flag encountered */ +#define SNS_AATT 0x4000 /* At Alternate track */ +#define SNS_WPER 0x2000 /* Write protection error */ +#define SNS_WRL 0x1000 /* Write lock error */ +#define SNS_MOCK 0x0800 /* Mode check */ +#define SNS_INAD 0x0400 /* Invalid memory address */ +#define SNS_RELF 0x0200 /* Release fault */ +#define SNS_CHER 0x0100 /* Chaining error */ + +/* Sense byte 3 */ +#define SNS_REVL 0x80 /* Revolution lost */ +#define SNS_DADE 0x40 /* Disc addressing or seek error */ +#define SNS_BUCK 0x20 /* Buffer check */ +#define SNS_ECCS 0x10 /* ECC error in sector label */ +#define SNS_ECCD 0x08 /* ECC error in data */ +#define SNS_ECCT 0x04 /* ECC error in track label */ +#define SNS_RTAE 0x02 /* Reserve track access error */ +#define SNS_UESS 0x01 /* Uncorrectable ECC error */ + +#define SNS2 us9 +/* us9 */ +/* us9 holds bytes 4 & 5 of the status for the drive */ + +/* Sense byte 4 */ +#define SNS_SEND 0x8000 /* Seek End */ +#define SNS_USEL 0x4000 /* Unit Selected */ +#define SNS_SPC0 0x2000 /* Sector Pulse Count B0 */ +#define SNS_SPC1 0x1000 /* Sector Pulse Count B1 */ +#define SNS_SPC2 0x0800 /* Sector Pulse Count B2 */ +#define SNS_SPC3 0x0400 /* Sector Pulse Count B3 */ +#define SNS_SPC4 0x0200 /* Sector Pulse Count B4 */ +#define SNS_SPC5 0x0100 /* Sector Pulse Count B5 */ + +/* Sense byte 5 */ +#define SNS_FLT 0x80 /* Disk Drive fault */ +#define SNS_SKER 0x40 /* Seek error */ +#define SNS_ONC 0x20 /* On Cylinder */ +#define SNS_UNR 0x10 /* Unit Ready */ +#define SNS_WRP 0x08 /* Write Protected */ +#define SNS_BUSY 0x04 /* Drive is busy */ +#define SNS_NU1 0x02 /* Spare 1 */ +#define SNS_NU2 0x01 /* Spare 2 */ + +#define CHS u6 +/* u6 holds the current cyl, hd, sec for the drive */ + +/* this attribute information is provided by the INCH command */ +/* for each device and is not used. It is reconstructed from */ +/* the scfi_t structure data for the assigned disk */ +/* +bits 0-7 - Flags + bits 0&1 - 00=Reserved, 01=MHD, 10=FHD, 11=MHD with FHD option + bit 2 - 1=Cartridge module drive + bit 3 - 0=Reserved + bit 4 - 1=Drive not present + bit 5 - 1=Dual Port + bit 6 - 0=Reserved 00 768 byte sec + bit 7 - 0=Reserved 01 1024 byte sec +bits 8-15 - sector count (sectors per track)(F16=16, F20=20) +bits 16-23 - MHD Head count (number of heads on MHD) +bits 24-31 - FHD head count (number of heads on FHD or number head on FHD option of + mini-module) +*/ + +/* Not Used up7 */ + +/* disk definition structure */ +struct scfi_t +{ + const char *name; /* Device ID Name */ + uint16 nhds; /* Number of heads */ + uint16 ssiz; /* sector size in words */ + uint16 spt; /* # sectors per track(head) */ + uint16 ucyl; /* Number of cylinders used */ + uint16 cyl; /* Number of cylinders on disk */ + uint8 type; /* Device type code */ + /* bit 1 mhd */ + /* bits 6/7 = 0 768 byte blk */ /* not used on UDP/DPII */ + /* = 1 1024 byte blk */ /* not used on UDP/DPII */ +} + +/* BM SIZ TOT AL U */ +/* DF0B, 1, 8, 20, 192, 1, 1712, 54760, SF336 */ +/* DF0C, 1, 8, 20, 192, 1, 4082, 130612, SG102 */ +/* DF0D, 1, 8, 20, 192, 1, 3491, 111705, SG654 */ +/* */ +/* DF0B, 1, 8, 20, 192, 1, 1711, 54752, SG038 */ +/* DF0C, 1, 16, 20, 192, 1, 2732, 87424, SG120 */ +/* DF0D, 1, 8, 20, 192, 1, 3491, 111680, SG076 */ +/* DF0E, 1, 16, 20, 192, 1, 2732, 87424, SG121 */ +scfi_type[] = +{ + /* Class F Disc Devices */ + /* MPX SCSI disks for SCFI controller */ + {"MH1GB", 1, 192, 40, 34960, 34960, 0x40}, /*0 69920 1000M */ + {"SG038", 1, 192, 20, 21900, 21900, 0x40}, /*1 21900 38M */ + {"SG120", 1, 192, 40, 34970, 34970, 0x40}, /*2 69940 1200M */ + {"SG076", 1, 192, 20, 46725, 46725, 0x40}, /*3 46725 760M */ + {"SG121", 1, 192, 20, 34970, 34970, 0x40}, /*4 69940 1210M */ + {"SD150", 9, 192, 24, 963, 967, 0x40}, /*5 8820 150M 208872 sec */ + {"SD300", 9, 192, 32, 1405, 1409, 0x40}, /*6 8828 300M 396674 sec */ + {"SD700", 15, 192, 35, 1542, 1546, 0x40}, /*7 8833 700M 797129 sec */ + {"SD1200",15, 192, 49, 1927, 1931, 0x40}, /*8 8835 1200M 1389584 sec */ + {NULL, 0} +}; + +t_stat scfi_preio(UNIT *uptr, uint16 chan); +t_stat scfi_startcmd(UNIT *uptr, uint16 chan, uint8 cmd); +t_stat scfi_haltio(UNIT *uptr); +t_stat scfi_iocl(CHANP *chp, int32 tic_ok); +t_stat scfi_srv(UNIT *uptr); +t_stat scfi_boot(int32 unitnum, DEVICE *dptr); +void scfi_ini(UNIT *, t_bool); +t_stat scfi_rschnlio(UNIT *uptr); +t_stat scfi_reset(DEVICE *); +t_stat scfi_attach(UNIT *, CONST char *); +t_stat scfi_detach(UNIT *); +t_stat scfi_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat scfi_get_type(FILE *st, UNIT *uptr, int32 v, CONST void *desc); +t_stat scfi_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *scfi_description (DEVICE *dptr); +extern uint32 inbusy; +extern uint32 outbusy; +extern uint32 readfull(CHANP *chp, uint32 maddr, uint32 *word); +extern int irq_pend; /* go scan for pending int or I/O */ +extern UNIT itm_unit; +extern uint32 PSD[]; /* PSD */ +extern uint32 cont_chan(uint16 chsa); + +/* channel program information */ +CHANP sda_chp[NUM_UNITS_SCFI] = {0}; + +MTAB scfi_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "TYPE", "TYPE", + &scfi_set_type, &scfi_get_type, NULL, "Type of disk"}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, + &show_dev_addr, NULL, "Device channel address"}, + {0}, +}; + +UNIT sda_unit[] = { +/* SET_TYPE(2) SG120 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(2), 0), 0, UNIT_ADDR(0x400)}, /* 0 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(2), 0), 0, UNIT_ADDR(0x410)}, /* 1 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(2), 0), 0, UNIT_ADDR(0x420)}, /* 2 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(2), 0), 0, UNIT_ADDR(0x430)}, /* 3 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(2), 0), 0, UNIT_ADDR(0x440)}, /* 4 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(2), 0), 0, UNIT_ADDR(0x450)}, /* 5 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(2), 0), 0, UNIT_ADDR(0x460)}, /* 6 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(2), 0), 0, UNIT_ADDR(0x470)}, /* 7 */ +}; + +DIB sda_dib = { + scfi_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + scfi_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + scfi_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + scfi_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + scfi_iocl, /* t_stat (*iocl_io)(CHANP *chp, int32 tik_ok)) */ /* Process IOCL */ + scfi_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + sda_unit, /* UNIT* units */ /* Pointer to units structure */ + sda_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_UNITS_SCFI, /* uint8 numunits */ /* number of units defined */ + 0x70, /* uint8 mask */ /* 8 devices - device mask */ + 0x0400, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0}, /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +DEVICE sda_dev = { + "SDA", sda_unit, NULL/*sda_reg*/, scfi_mod, + NUM_UNITS_SCFI, 16, 24, 4, 16, 32, + NULL, NULL, &scfi_reset, &scfi_boot, &scfi_attach, &scfi_detach, + /* ctxt is the DIB pointer */ + &sda_dib, DEV_DISABLE|DEV_DEBUG|DEV_DIS, 0, dev_debug, + NULL, NULL, &scfi_help, NULL, NULL, &scfi_description +}; + +#if NUM_DEVS_SCFI > 1 +/* channel program information */ +CHANP sdb_chp[NUM_UNITS_SCFI] = {0}; + +UNIT sdb_unit[] = { +/* SET_TYPE(0) DM1GB */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(0), 0), 0, UNIT_ADDR(0xC00)}, /* 0 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(0), 0), 0, UNIT_ADDR(0xC10)}, /* 1 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(0), 0), 0, UNIT_ADDR(0xC20)}, /* 2 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(0), 0), 0, UNIT_ADDR(0xC30)}, /* 3 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(0), 0), 0, UNIT_ADDR(0xC40)}, /* 4 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(0), 0), 0, UNIT_ADDR(0xC50)}, /* 5 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(0), 0), 0, UNIT_ADDR(0xC60)}, /* 6 */ + {UDATA(&scfi_srv, UNIT_SCFI|SET_TYPE(0), 0), 0, UNIT_ADDR(0xC70)}, /* 7 */ +}; + +DIB sdb_dib = { + scfi_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + scfi_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + scfi_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + scfi_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + scfi_iocl, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + scfi_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + sdb_unit, /* UNIT* units */ /* Pointer to units structure */ + sdb_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_UNITS_SCFI, /* uint8 numunits */ /* number of units defined */ + 0x70, /* uint8 mask */ /* 16 devices - device mask */ + 0x0C00, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0}, /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +DEVICE sdb_dev = { + "SDB", sdb_unit, NULL, /*sdb_reg*/, scfi_mod, + NUM_UNITS_SCFI, 16, 24, 4, 16, 32, + NULL, NULL, &scfi_reset, &scfi_boot, &scfi_attach, &scfi_detach, + /* ctxt is the DIB pointer */ + &sdb_dib, DEV_DISABLE|DEV_DEBUG|DEV_DIS, 0, dev_debug, + NULL, NULL, &scfi_help, NULL, NULL, &scfi_description +}; +#endif + +/* convert sector disk address to star values (c,h,s) */ +uint32 scfisec2star(uint32 daddr, int type) +{ + uint32 sec = daddr % scfi_type[type].spt; /* get sector value */ + uint32 spc = scfi_type[type].nhds * scfi_type[type].spt; /* sec per cyl */ + uint32 cyl = daddr / spc; /* cylinders */ + uint32 hds = (daddr % spc) / scfi_type[type].spt; /* heads */ + + /* now return the star value */ + return (CHS2STAR(cyl,hds,sec)); /* return STAR */ +} + +/* start a disk operation */ +t_stat scfi_preio(UNIT *uptr, uint16 chan) +{ + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + int unit = (uptr - dptr->units); + + sim_debug(DEBUG_DETAIL, dptr, + "scfi_preio CMD %08x unit %02x\n", uptr->CMD, unit); + if ((uptr->CMD & 0xff00) != 0) { /* just return if busy */ + return SNS_BSY; + } + + sim_debug(DEBUG_DETAIL, dptr, + "scfi_preio unit %02x chsa %04x OK\n", unit, chsa); + return SCPE_OK; /* good to go */ +} + +/* load in the IOCD and process the commands */ +/* return = 0 OK */ +/* return = 1 error, chan_status will have reason */ +t_stat scfi_iocl(CHANP *chp, int32 tic_ok) +{ + uint32 word1 = 0; + uint32 word2 = 0; + int32 docmd = 0; + UNIT *uptr = chp->unitptr; /* get the unit ptr */ + uint16 chan = get_chan(chp->chan_dev); /* our channel */ + uint16 chsa = chp->chan_dev; /* our chan/sa */ + uint16 devstat = 0; + DEVICE *dptr = get_dev(uptr); + + /* check for valid iocd address if 1st iocd */ + if (chp->chan_info & INFO_SIOCD) { /* see if 1st IOCD in channel prog */ + if (chp->chan_caw & 0x3) { /* must be word bounded */ + sim_debug(DEBUG_EXP, dptr, + "scfi_iocl iocd bad address chsa %02x caw %06x\n", + chsa, chp->chan_caw); + chp->ccw_addr = chp->chan_caw; /* set the bad iocl address */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid iocd addr */ + uptr->SNS |= SNS_INAD; /* invalid address status */ + return 1; /* error return */ + } + } +loop: + sim_debug(DEBUG_EXP, dptr, + "scfi_iocl @%06x entry PSD %08x chan_status[%04x] %04x\n", + chp->chan_caw, PSD[0], chan, chp->chan_status); + + /* Abort if we have any errors */ + if (chp->chan_status & STATUS_ERROR) { /* check channel error status */ + sim_debug(DEBUG_EXP, dptr, + "scfi_iocl ERROR1 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* return error */ + } + + /* Read in first CCW */ + if (readfull(chp, chp->chan_caw, &word1) != 0) { /* read word1 from memory */ + chp->chan_status |= STATUS_PCHK; /* memory read error, program check */ + sim_debug(DEBUG_EXP, dptr, + "scfi_iocl ERROR2 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* Read in second CCW */ + if (readfull(chp, chp->chan_caw+4, &word2) != 0) { /* read word2 from memory */ + chp->chan_status |= STATUS_PCHK; /* memory read error, program check */ + sim_debug(DEBUG_EXP, dptr, + "scfi_iocl ERROR3 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + sim_debug(DEBUG_CMD, dptr, + "scfi_iocl @%06x read ccw chan %02x IOCD wd 1 %08x wd 2 %08x\n", + chp->chan_caw, chan, word1, word2); + + chp->chan_caw = (chp->chan_caw & 0xfffffc) + 8; /* point to next IOCD */ + + /* Check if we had data chaining in previous iocd */ + /* if we did, use previous cmd value */ + if (((chp->chan_info & INFO_SIOCD) == 0) && /* see if 1st IOCD in channel prog */ + (chp->ccw_flags & FLAG_DC)) { /* last IOCD have DC set? */ + sim_debug(DEBUG_CMD, dptr, + "scfi_iocl @%06x DO DC, ccw_flags %04x cmd %02x\n", + chp->chan_caw, chp->ccw_flags, chp->ccw_cmd); + } else + chp->ccw_cmd = (word1 >> 24) & 0xff; /* set new command from IOCD wd 1 */ + + if (!MEM_ADDR_OK(word1 & MASK24)) { /* see if memory address invalid */ + chp->chan_status |= STATUS_PCHK; /* bad, program check */ + uptr->SNS |= SNS_INAD; /* invalid address status */ + sim_debug(DEBUG_EXP, dptr, + "scfi_iocl bad IOCD1 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + chp->ccw_count = word2 & 0xffff; /* get 16 bit byte count from IOCD WD 2 */ + + /* validate the commands for the disk */ + switch (chp->ccw_cmd) { + case DSK_WD: case DSK_RD: case DSK_INCH: case DSK_NOP: case DSK_ICH: + case DSK_SCK: case DSK_XEZ: case DSK_LMR: case DSK_WSL: case DSK_RSL: + case DSK_IHA: case DSK_WTL: case DSK_RTL: case DSK_RAP: case DSK_TESS: + case DSK_FNSK: case DSK_REL: case DSK_RES: case DSK_POR: case DSK_TIC: + case DSK_REC: + case DSK_SNS: + break; + default: + chp->chan_status |= STATUS_PCHK; /* program check for invalid cmd */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected */ + sim_debug(DEBUG_EXP, dptr, + "scfi_iocl bad cmd chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + if (chp->chan_info & INFO_SIOCD) { /* see if 1st IOCD in channel prog */ + /* 1st command can not be a TIC or NOP */ + if ((chp->ccw_cmd == DSK_NOP) || (chp->ccw_cmd == CMD_TIC)) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected status */ + sim_debug(DEBUG_EXP, dptr, + "scfi_iocl TIC/NOP bad cmd chan_status[%04x] %04x\n", + chan, chp->chan_status); + return 1; /* error return */ + } + } + + /* TIC can't follow TIC or be first in command chain */ + /* diags send bad commands for testing. Use all of op */ + if (chp->ccw_cmd == CMD_TIC) { + if (tic_ok) { + if (((word1 & MASK24) == 0) || (word1 & 0x3)) { + sim_debug(DEBUG_EXP, dptr, + "scfi_iocl tic cmd bad address chan %02x tic caw %06x IOCD wd 1 %08x\n", + chan, chp->chan_caw, word1); + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected status */ + uptr->SNS |= SNS_INAD; /* invalid address status */ + return 1; /* error return */ + } + tic_ok = 0; /* another tic not allowed */ + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + sim_debug(DEBUG_CMD, dptr, + "scfi_iocl tic cmd ccw chan %02x tic caw %06x IOCD wd 1 %08x\n", + chan, chp->chan_caw, word1); + goto loop; /* restart the IOCD processing */ + } + chp->chan_caw = word1 & MASK24; /* get new IOCD address */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid tic */ + uptr->SNS |= SNS_CMDREJ; /* cmd rejected status */ + if (((word1 & MASK24) == 0) || (word1 & 0x3)) + uptr->SNS |= SNS_INAD; /* invalid address status */ + sim_debug(DEBUG_EXP, dptr, + "scfi_iocl TIC ERROR chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* Check if we had data chaining in previous iocd */ + if ((chp->chan_info & INFO_SIOCD) || /* see if 1st IOCD in channel prog */ + (((chp->chan_info & INFO_SIOCD) == 0) && /* see if 1st IOCD in channel prog */ + ((chp->ccw_flags & FLAG_DC) == 0))) { /* last IOCD have DC set? */ + sim_debug(DEBUG_CMD, dptr, + "scfi_iocl @%06x DO CMD No DC, ccw_flags %04x cmd %02x\n", + chp->chan_caw, chp->ccw_flags, chp->ccw_cmd); + docmd = 1; /* show we have a command */ + } + + /* Set up for this command */ + chp->ccw_flags = (word2 >> 16) & 0xf000; /* get flags from bits 0-4 of WD 2 of IOCD */ + chp->chan_status = 0; /* clear status for next IOCD */ + /* make a 24 bit address */ + chp->ccw_addr = word1 & MASK24; /* set the data/seek address */ + + /* validate parts of IOCD2 that are reserved */ + if (word2 & 0x0fff0000) { /* bits 5-15 must be zero */ + chp->chan_status |= STATUS_PCHK; /* program check for invalid iocd */ + sim_debug(DEBUG_EXP, dptr, + "scfi_iocl IOCD2 chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + + /* DC can only be used with a read/write cmd */ + if (chp->ccw_flags & FLAG_DC) { + if ((chp->ccw_cmd != DSK_RD) && (chp->ccw_cmd != DSK_WD)) { + chp->chan_status |= STATUS_PCHK; /* program check for invalid DC */ + uptr->SNS |= SNS_CHER; /* chaining error */ + sim_debug(DEBUG_EXP, dptr, + "scfi_iocl DC ERROR chan_status[%04x] %04x\n", chan, chp->chan_status); + return 1; /* error return */ + } + } + + chp->chan_byte = BUFF_BUSY; /* busy & no bytes transferred yet */ + + sim_debug(DEBUG_XIO, dptr, + "scfi_iocl @%06x read docmd %01x addr %06x count %04x chan %04x ccw_flags %04x\n", + chp->chan_caw, docmd, chp->ccw_addr, chp->ccw_count, chan, chp->ccw_flags); + + if (docmd) { /* see if we need to process a command */ + DIB *dibp = dib_unit[chp->chan_dev]; /* get the DIB pointer */ + + uptr = chp->unitptr; /* get the unit ptr */ + if (dibp == 0 || uptr == 0) { + chp->chan_status |= STATUS_PCHK; /* program check if it is */ + return 1; /* if none, error */ + } + + sim_debug(DEBUG_XIO, dptr, + "scfi_iocl @%06x before start_cmd chan %04x status %04x count %04x SNS %08x\n", + chp->chan_caw, chan, chp->chan_status, chp->ccw_count, uptr->u5); + + /* call the device startcmd function to process the current command */ + /* just replace device status bits */ + chp->chan_info &= ~INFO_CEND; /* show chan_end not called yet */ + devstat = dibp->start_cmd(uptr, chan, chp->ccw_cmd); + chp->chan_status = (chp->chan_status & 0xff00) | devstat; + chp->chan_info &= ~INFO_SIOCD; /* show not first IOCD in channel prog */ + + sim_debug(DEBUG_XIO, dptr, + "scfi_iocl @%06x after start_cmd chan %04x status %08x count %04x\n", + chp->chan_caw, chan, chp->chan_status, chp->ccw_count); + + /* see if bad status */ + if (chp->chan_status & (STATUS_ATTN|STATUS_ERROR)) { + chp->chan_status |= STATUS_CEND; /* channel end status */ + chp->ccw_flags = 0; /* no flags */ + chp->chan_byte = BUFF_NEXT; /* have main pick us up */ + sim_debug(DEBUG_EXP, dptr, + "scfi_iocl bad status chsa %04x status %04x cmd %02x\n", + chsa, chp->chan_status, chp->ccw_cmd); + /* done with command */ + sim_debug(DEBUG_EXP, &cpu_dev, + "scfi_iocl ERROR return chsa %04x status %08x\n", + chp->chan_dev, chp->chan_status); + return 1; /* error return */ + } + /* NOTE this code needed for MPX 1.X to run! */ + /* see if command completed */ + /* we have good status */ + if (chp->chan_status & (STATUS_DEND|STATUS_CEND)) { + uint16 chsa = GET_UADDR(uptr->u3); /* get channel & sub address */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* show I/O complete */ + sim_debug(DEBUG_XIO, dptr, + "scfi_iocl @%06x FIFO #%1x cmd complete chan %04x status %04x count %04x\n", + chp->chan_caw, FIFO_Num(chsa), chan, chp->chan_status, chp->ccw_count); + } + } + /* the device processor returned OK (0), so wait for I/O to complete */ + /* nothing happening, so return */ + sim_debug(DEBUG_XIO, dptr, + "scfi_iocl @%06x return, chan %04x status %04x count %04x irq_pend %1x\n", + chp->chan_caw, chan, chp->chan_status, chp->ccw_count, irq_pend); + return 0; /* good return */ +} + +t_stat scfi_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) +{ + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + int32 unit = (uptr - dptr->units); + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + sim_debug(DEBUG_CMD, dptr, + "scfi_startcmd chsa %04x unit %02x cmd %02x CMD %08x\n", + chsa, unit, cmd, uptr->CMD); + if ((uptr->flags & UNIT_ATT) == 0) { /* unit attached status */ + sim_debug(DEBUG_EXP, dptr, "scfi_startcmd unit %02x not attached\n", unit); + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + if (cmd != DSK_SNS) /* we are completed with unit check status */ + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + } + + if ((uptr->CMD & DSK_CMDMSK) != 0) { + sim_debug(DEBUG_EXP, dptr, "scfi_startcmd unit %02x busy\n", unit); + uptr->CMD |= DSK_BUSY; /* Flag we are busy */ + return SNS_BSY; + } + uptr->SNS2 |= SNS_USEL; /* unit selected */ + sim_debug(DEBUG_CMD, dptr, + "scfi_startcmd CMD continue unit=%02x cmd %02x iocla %06x cnt %04x\n", + unit, cmd, chp->chan_caw, chp->ccw_count); + + /* Unit is online, so process a command */ + switch (cmd) { + + case DSK_INCH: /* INCH cmd 0x0 */ + sim_debug(DEBUG_CMD, dptr, + "scfi_startcmd starting INCH %06x cmd, chsa %04x MemBuf %06x cnt %04x\n", + chp->chan_inch_addr, chsa, chp->ccw_addr, chp->ccw_count); + + uptr->SNS &= ~SNS_CMDREJ; /* not rejected yet */ + uptr->CMD |= DSK_INCH2; /* use 0xF0 for inch, just need int */ +#ifdef FAST_FOR_UTX + sim_activate(uptr, 20); /* start things off */ +#else + sim_activate(uptr, 250); /* start things off */ +#endif + return SCPE_OK; /* good to go */ + break; + + case DSK_NOP: /* NOP 0x03 */ + if ((cmd == DSK_NOP) && + (chp->chan_info & INFO_SIOCD)) { /* is NOP 1st IOCD? */ + chp->chan_caw -= 8; /* backup iocd address for diags */ + break; /* yes, can't be 1st */ + } + case DSK_ICH: /* 0xFF Initialize controller */ + case DSK_SCK: /* Seek command 0x07 */ + case DSK_XEZ: /* Rezero & Read IPL record 0x37 */ + case DSK_WD: /* Write command 0x01 */ + case DSK_RD: /* Read command 0x02 */ + case DSK_LMR: /* read mode register 0x1F */ + case DSK_WSL: /* WSL 0x31 */ + case DSK_RSL: /* RSL 0x32 */ + case DSK_IHA: /* 0x47 Increment head address */ + case DSK_WTL: /* WTL 0x51 */ + case DSK_RTL: /* RTL 0x52 */ + case DSK_RAP: /* 0xA2 Read angular positions */ + case DSK_TESS: /* TESS 0xAB Test STAR */ + case DSK_FNSK: /* 0x0B Format for no skip */ + case DSK_REC: /* 0xB2 Read ECC correction mask */ + case DSK_RES: /* 0x23 Reserve */ + case DSK_REL: /* 0x33 Release */ + uptr->SNS &= ~MASK24; /* clear data & leave mode */ + uptr->SNS2 = (SNS_UNR|SNS_ONC|SNS_USEL);/* reset status to on cyl & ready */ + case DSK_SNS: /* Sense 0x04 */ + uptr->CMD |= cmd; /* save cmd */ + sim_debug(DEBUG_CMD, dptr, + "scfi_startcmd starting disk cmd %02x chsa %04x\n", + cmd, chsa); +#ifdef FAST_FOR_UTX + sim_activate(uptr, 20); /* start things off */ +#else + sim_activate(uptr, 250); /* start things off */ +#endif + return SCPE_OK; /* good to go */ + break; + } + + sim_debug(DEBUG_EXP, dptr, + "scfi_startcmd done with bad disk cmd %02x chsa %04x SNS %08x\n", + cmd, chsa, uptr->SNS); + uptr->SNS |= SNS_CMDREJ; /* cmd rejected */ + return SNS_CHNEND|SNS_DEVEND|STATUS_PCHK; /* return error */ +} + +/* Handle haltio transfers for disk */ +t_stat scfi_haltio(UNIT *uptr) { + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + int cmd = uptr->CMD & DSK_CMDMSK; + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + sim_debug(DEBUG_EXP, dptr, + "scfi_haltio enter chsa %04x cmd = %02x\n", chsa, cmd); + + /* terminate any input command */ + /* UTX wants SLI bit, but no unit exception */ + /* status must not have an error bit set */ + /* otherwise, UTX will panic with "bad status" */ + if ((uptr->CMD & DSK_CMDMSK) != 0) { /* is unit busy */ + sim_debug(DEBUG_CMD, dptr, + "scfi_haltio HIO chsa %04x cmd = %02x ccw_count %02x\n", + chsa, cmd, chp->ccw_count); + /* stop any I/O and post status and return error status */ + sim_cancel(uptr); /* clear the input timer */ + chp->ccw_count = 0; /* zero the count */ + chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* stop any chaining */ + uptr->CMD &= LMASK; /* make non-busy */ + uptr->SNS2 |= (SNS_ONC|SNS_UNR); /* on cylinder & ready */ + sim_debug(DEBUG_CMD, dptr, + "scfi_haltio HIO I/O stop chsa %04x cmd = %02x\n", chsa, cmd); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); /* force end */ + return CC1BIT | SCPE_IOERR; /* DIAGS want just an interrupt */ + } + uptr->CMD &= LMASK; /* make non-busy */ + uptr->SNS2 |= (SNS_ONC|SNS_UNR); /* on cylinder & ready */ + sim_debug(DEBUG_CMD, dptr, + "scfi_haltio HIO I/O not busy chsa %04x cmd = %02x\n", chsa, cmd); + return CC1BIT | SCPE_OK; /* not busy return */ +} + +/* Handle processing of disk requests. */ +t_stat scfi_srv(UNIT *uptr) +{ + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + CHANP *chp = find_chanp_ptr(chsa);/* get channel prog pointer */ + int cmd = uptr->CMD & DSK_CMDMSK; + int type = GET_TYPE(uptr->flags); + uint32 tcyl=0, trk=0, cyl=0, sec=0; + int unit = (uptr - dptr->units); + int len = chp->ccw_count; + int i; + uint32 mema; /* memory address */ + uint8 ch; + uint16 ssize = scfi_type[type].ssiz * 4; /* disk sector size in bytes */ + uint32 tstart; + uint8 buf[1024]; + uint8 buf2[1024]; + + sim_debug(DEBUG_CMD, dptr, + "scfi_srv entry unit %02x CMD %08x chsa %04x count %04x %x/%x/%x \n", + unit, uptr->CMD, chsa, chp->ccw_count, + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + + if ((uptr->flags & UNIT_ATT) == 0) { /* unit attached status */ + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + if (cmd != DSK_SNS) { /* we are completed with unit check status */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + } + + sim_debug(DEBUG_CMD, dptr, + "scfi_srv cmd=%02x chsa %04x count %04x\n", cmd, chsa, chp->ccw_count); + + switch (cmd) { + case 0: /* No command, stop disk */ + break; + + case DSK_ICH: /* 0xFF Initialize controller */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + len = chp->ccw_count; /* INCH command count */ + mema = chp->ccw_addr; /* get inch or buffer addr */ + sim_debug(DEBUG_CMD, dptr, + "scfi_srv cmd CONT ICH %06x chsa %04x addr %06x count %04x completed\n", + chp->chan_inch_addr, chsa, mema, chp->ccw_count); + if (len == 0x14) { + /* read all 20 bytes, stopping every 4 bytes to make words */ + /* the first word has the inch buffer address */ + /* the next 4 words have drive data for each unit */ + /* WARNING 4 drives must be defined for this controller */ + /* so we will not have a map fault */ + for (i=0; i < 20; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (((i+1)%4) == 0) { /* see if we have a word yet */ + if (i == 3) { + /* inch buffer address */ + mema = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | (buf[3]); + sim_debug(DEBUG_CMD, dptr, + "scfi_srv cmd CONT ICH %06x chsa %04x mema %06x completed\n", + chp->chan_inch_addr, chsa, mema); + } else { + /* drive attribute registers */ + /* may want to use this later */ + /* clear warning errors */ + tstart = (buf[i-3]<<24) | (buf[i-2]<<16) | (buf[i-1]<<8) | (buf[i]); + sim_debug(DEBUG_CMD, dptr, + "scfi_srv cmd CONT ICH %06x chsa %04x data %06x completed\n", + chp->chan_inch_addr, chsa, tstart); + } + } + } + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + } + + /* to use this inch method, byte count must be 896 */ + if (len != 896) { + /* we have invalid count, error, bail out */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + /* now call set_inch() function to write and test inch buffer addresses */ + /* 1-224 wd buffer is provided, status is 128 words offset from start */ + mema += (128*4); /* offset to inch buffers */ + tstart = set_inch(uptr, mema, 33); /* new address of 33 entries */ + if ((tstart == SCPE_MEM) || (tstart == SCPE_ARG)) { /* any error */ + /* we have error, bail out */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + case DSK_INCH2: /* use 0xF0 for inch, just need int */ + len = chp->ccw_count; /* INCH command count */ + mema = chp->ccw_addr; /* get inch or buffer addr */ + sim_debug(DEBUG_CMD, dptr, + "scfi_srv starting INCH %06x cmd, chsa %04x MemBuf %06x cnt %04x\n", + chp->chan_inch_addr, chsa, chp->ccw_addr, chp->ccw_count); + + /* mema has IOCD word 1 contents. For the disk processor it contains */ + /* a pointer to the INCH buffer followed by 8 drive attribute words that */ + /* contains the flags, sector count, MHD head count, and FHD count */ + /* len has the byte count from IOCD wd2 and should be 0x24 (36) */ + /* the INCH buffer address must be set for the parent channel as well */ + /* as all other devices on the channel. Call set_inch() to do this for us */ + /* just return OK and channel software will use u4 as status buffer addr */ + + /* see if New SCFI controller */ + if (len == 4) { + /* get just the INCH addr */ + for (i=0; i < 4; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + } + /* inch buffer address */ + mema = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | (buf[3]); + goto gohere; + } + + if (len != 36) { + /* we have invalid count, error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* read all 36 bytes, stopping every 4 bytes to make words */ + /* the first word has the inch buffer address */ + /* the next 8 words have drive data for each unit */ + /* WARNING 8 drives must be defined for this controller */ + /* so we will not have a map fault */ + for (i=0; i < 36; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (((i+1)%4) == 0) { /* see if we have a word yet */ + if (i == 3) + /* inch buffer address */ + mema = (buf[0]<<24) | (buf[1]<<16) | + (buf[2]<<8) | (buf[3]); + else + /* drive attribute registers */ + /* may want to use this later */ + /* clear warning errors */ + tstart = (buf[i-3]<<24) | (buf[i-2]<<16) + | (buf[i-1]<<8) | (buf[i]); + } + } +gohere: + /* now call set_inch() function to write and test inch buffer addresses */ + /* 1-224 wd buffer is provided, status is 128 words offset from start */ + mema += (128*4); /* offset to inch buffers */ + i = set_inch(uptr, mema, 33); /* new address of 33 entries */ + if ((i == SCPE_MEM) || (i == SCPE_ARG)) { /* any error */ + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "scfi_srv cmd INCH %06x chsa %04x addr %06x count %04x completed\n", + chp->chan_inch_addr, chsa, mema, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_RES: /* 0x23 Reserve */ + case DSK_REL: /* 0x33 Release */ + case DSK_POR: /* 0x43 Priority Override */ + case DSK_REC: /* 0xB2 Read ECC correction mask */ + case DSK_TESS: /* 0xAB Test STAR (subchannel target address register) */ + case DSK_FNSK: /* 0x0B Format for no skip */ + case DSK_RAP: /* 0xA2 Read angular positions */ + case DSK_IHA: /* 0x47 Increment head address */ + case DSK_RSL: /* RSL 0x32 */ + case DSK_WSL: /* WSL 0x31 write sector labels */ + case DSK_RTL: /* RTL 0x52 */ + case DSK_WTL: /* WTL 0x51 */ + case DSK_NOP: /* NOP 0x03 */ + /* diags want chan prog check and cmd reject if 1st cmd of IOCL */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "scfi_srv cmd NOP chsa %04x count %04x completed\n", + chsa, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_SNS: /* 0x04 */ /* Sense */ + sim_debug(DEBUG_CMD, dptr, "scfi_startcmd CMD sense\n"); + + /* count must be 12 or 14, if not prog check */ + if (len != 12 && len != 14) { + sim_debug(DEBUG_CMD, dptr, + "scfi_srv Sense bad count unit=%02x count%04x\n", unit, len); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK|STATUS_LENGTH); + break; + } + /* bytes 0,1 - Cyl entry from CHS reg */ + ch = (uptr->CHS >> 24) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv sense CHS b0 unit=%02x 1 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = (uptr->CHS >> 16) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv sense CHS b1 unit=%02x 2 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + /* byte 2 - Track entry from CHS reg */ + ch = (uptr->CHS >> 8) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv sense CHS b2 unit=%02x 3 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + /* byte 3 - Sector entry from CHS reg */ + ch = (uptr->CHS) & 0xff; + sec = ch; + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv sense CHS b3 unit=%02x 4 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + + /* bytes 4 - mode reg, byte 0 of SNS */ + ch = (uptr->SNS >> 24) & 0xff; /* return the sense data */ + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv sense unit=%02x 1 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + /* bytes 5-7 - status bytes, bytes 1-3 of SNS */ + ch = (uptr->SNS >> 16) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv sense unit=%02x 2 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = (uptr->SNS >> 8) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv sense unit=%02x 3 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = (uptr->SNS) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv sense unit=%02x 4 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + + /* bytes 8-11 - drive mode register entries from assigned disk */ + ch = scfi_type[type].type & 0x40; /* zero bits 0, 2-7 in type byte */ + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv datr unit=%02x 1 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = scfi_type[type].spt & 0xff; /* get sectors per track */ + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv datr unit=%02x 2 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = scfi_type[type].nhds & 0xff; /* get # MHD heads */ + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv datr unit=%02x 3 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = 0; /* no FHD heads */ + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv datr unit=%02x 4 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + + /* bytes 12 & 13 are optional, so check if read done */ + /* TODO add drive status bits here */ + if ((test_write_byte_end(chsa)) == 0) { + /* bytes 12 & 13 contain drive related status */ + uptr->SNS2 |= (SNS_SEND|SNS_USEL); /* selected & seek end */ + /* bits 2-7 have sector pulse count */ + ch = ((sec * 2) % SPT(type)) & 0x3f;/* get index cnt */ + uptr->SNS2 = (uptr->SNS2 & 0xc0ff) | ((((uint32)ch) & 0x3f) << 8); + ch = (uptr->SNS2 >> 8) & 0xff; /* seek end and unit selected for now */ + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv dsr unit=%02x 1 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + + uptr->SNS2 |= (SNS_ONC|SNS_UNR); /* on cylinder & ready */ + ch = uptr->SNS2 & 0xff; /* drive on cylinder and ready for now */ + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv dsr unit=%02x 2 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + } + uptr->SNS &= 0xff000000; /* reset status */ + uptr->SNS2 = 0; /* reset status */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case DSK_SCK: /* Seek cylinder, track, sector 0x07 */ + /* If we are waiting on seek to finish, check if there yet. */ + if (uptr->CMD & DSK_SEEKING) { + /* see if on cylinder yet */ + if (STAR2CYL(uptr->STAR) == STAR2CYL(uptr->CHS)) { + /* we are on cylinder, seek is done */ + sim_debug(DEBUG_CMD, dptr, + "scfi_srv seek on cylinder unit %02x new %04x old %04x\n", + unit, uptr->STAR>>16, uptr->CHS>>16); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS2 |= (SNS_SEND|SNS_ONC); /* On cylinder & seek done */ + /* we have already seeked to the required sector */ + /* we do not need to seek again, so move on */ + chan_end(chsa, SNS_DEVEND|SNS_CHNEND); + break; + } else { + /* we have wasted enough time, we are there */ + /* we are on cylinder, seek is done */ + sim_debug(DEBUG_CMD, dptr, "scfi_srv seek over on cylinder unit=%02x %04x %04x\n", + unit, uptr->STAR >> 16, uptr->CHS >> 16); + uptr->CHS = uptr->STAR; /* we are there */ +#ifdef FAST_FOR_UTX + sim_activate(uptr, 15); /* start things off */ +#else + sim_activate(uptr, 150); /* start things off */ +#endif + break; + } + } + + /* not seeking, so start a new seek */ + /* set buf data to current STAR values */ + tcyl = STAR2CYL(uptr->CHS); /* get current cyl */ + + /* the value is really a sector offset for the disk */ + /* but will treat as c/h/s for processing */ + /* the cyl, trk, and sect are ready to update */ + sim_debug(DEBUG_CMD, dptr, + "scfi_srv current STAR unit=%02x star %02x %02x %02x %02x\n", + unit, (uptr->CHS >> 24) & 0xff, (uptr->CHS >> 16) & 0xff, + (uptr->CHS >> 8) & 0xff, (uptr->CHS) & 0xff); + + if (len != 4) { + sim_debug(DEBUG_EXP, dptr, + "scfi_srv SEEK bad count unit %02x count %04x\n", unit, len); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK|STATUS_LENGTH); + break; + } + + /* Read in 4 character required seek code */ + for (i = 0; i < 4; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + if (i == 0) { + sim_debug(DEBUG_DETAIL, dptr, + "scfi_srv seek error unit=%02x\n", unit); + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_DADE; /* Disc addressing or seek error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + chp->ccw_count = len; /* restore count, huh? */ + return SCPE_OK; + break; + } + /* just read the next byte */ + } + } + chp->ccw_count = len; /* restore count for diag, huh? */ + /* the value is really a sector offset for the disk */ + /* but will treat as c/h/s for processing */ + /* the cyl, trk, and sect are ready to update */ + sim_debug(DEBUG_CMD, dptr, + "scfi_srv STAR unit=%02x star %02x %02x %02x %02x\n", + unit, buf[0], buf[1], buf[2], buf[3]); + + /* save STAR (target sector) data in STAR */ + uptr->STAR = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]); + cyl = STAR2CYL(uptr->STAR); /* get the cylinder */ + trk = buf[2]; /* get the track */ + sec = buf[3]; /* get sec */ + + sim_debug(DEBUG_DETAIL, dptr, + "scfi_srv NEW SEEK cyl %04x trk %02x sec %02x unit=%02x\n", + cyl&0xffff, trk, buf[3], unit); + + /* Check if seek valid */ + if (uptr->STAR >= CAP(type)) { + + sim_debug(DEBUG_EXP, dptr, + "scfi_srv seek ERROR cyl %04x trk %02x sec %02x unit=%02x\n", + cyl, trk, buf[3], unit); + + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_DADE; /* set error status */ + uptr->SNS2 |= (SNS_SKER|SNS_SEND); + + /* set new STAR value, even if invalid */ + uptr->CHS = uptr->STAR; + + /* we have an error, tell user */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); /* end command */ + break; + } + + /* calc the new sector address of data */ + /* calculate file position in bytes of requested sector */ + /* file offset in bytes */ + tstart = uptr->STAR * SSB(type); + uptr->CHS = uptr->STAR; + + sim_debug(DEBUG_DETAIL, dptr, + "scfi_srv seek start %06x trk %04x sec %02x\n", + tstart, trk, buf[3]); + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "scfi_srv Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* Check if already on correct cylinder */ + /* if not, do a delay to slow things down */ + if (tcyl != cyl) { + int diff = ((int)tcyl - (int)cyl); + if (diff < 0) + diff = -diff; + /* Do a fake seek to kill time */ + uptr->CMD |= DSK_SEEKING; /* show we are seeking */ + sim_debug(DEBUG_EXP, dptr, + "scfi_srv seeking unit=%02x to %04x/%02x/%02x from cyl %04x (%04x)\n", + unit, cyl, trk, buf[3], tcyl, cyl); +#ifdef FAST_FOR_UTX + sim_activate(uptr, 15); /* start things off */ +#else + sim_activate(uptr, 200+diff); /* start us off */ +#endif + } else { + /* we are on cylinder/track/sector, so go on */ + sim_debug(DEBUG_DETAIL, dptr, + "scfi_srv done seeking to %04x cyl %04x trk %02x sec %02x\n", + tstart, cyl, trk, buf[3]); + /* set new STAR value */ + uptr->CHS = uptr->STAR; + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_DEVEND|SNS_CHNEND); + } + break; + + case DSK_XEZ: /* 0x37 */ /* Rezero & Read IPL record */ + + sim_debug(DEBUG_CMD, dptr, "XEZ REZERO IPL unit=%02x seek 0\n", unit); + /* Do a seek to 0 */ + tcyl = STAR2CYL(uptr->CHS); /* get current cyl */ + uptr->STAR = 0; /* set STAR to 0, 0, 0 */ + uptr->CHS = 0; /* set current CHS to 0, 0, 0 */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->CMD |= DSK_SCK; /* show as seek command */ + tstart = 0; /* byte offset is 0 */ + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "scfi_srv Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + /* we are on cylinder/track/sector zero, so go on */ + sim_debug(DEBUG_DETAIL, dptr, "scfi_srv done seek trk 0\n"); + uptr->CMD |= DSK_SEEKING; /* show we are seeking */ + sim_debug(DEBUG_EXP, dptr, + "scfi_srv XEZ seeking unit=%02x to cyl 0000 from cyl %04x\n", + unit, tcyl); + sim_activate(uptr, 15); /* start things off */ + break; + + case DSK_LMR: /* 0x1F */ + sim_debug(DEBUG_CMD, dptr, "Load Mode Reg unit=%02x\n", unit); + /* Read in 1 character of mode data */ + if (chan_read_byte(chsa, &buf[0])) { + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ; + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + } + sim_debug(DEBUG_CMD, dptr, "Load Mode Reg unit=%02x old %x new %x\n", + unit, (uptr->SNS)&0xff, buf[0]); + uptr->CMD &= LMASK; /* remove old cmd */ + uptr->SNS &= MASK24; /* clear old mode data */ + uptr->SNS |= (buf[0] << 24); /* save mode value */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case DSK_RD: /* Read Data command 0x02 */ + if ((uptr->CMD & DSK_READING) == 0) { /* see if we are reading data */ + uptr->CMD |= DSK_READING; /* read from disk starting */ + sim_debug(DEBUG_CMD, dptr, + "DISK READ starting CMD %08x chsa %04x buffer %06x count %04x\n", + uptr->CMD, chsa, chp->ccw_addr, chp->ccw_count); + } + + if (uptr->CMD & DSK_READING) { /* see if we are reading data */ + /* get file offset in sectors */ + tstart = uptr->CHS; + /* file offset in bytes */ + tstart = tstart * SSB(type); + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "scfi_srv READ, Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + sim_debug(DEBUG_CMD, dptr, + "DISK READ reading CMD %08x chsa %04x tstart %04x buffer %06x count %04x\n", + uptr->CMD, chsa, tstart, chp->ccw_addr, chp->ccw_count); + + /* read in a sector of data from disk */ + if ((len=sim_fread(buf, 1, ssize, uptr->fileref)) != ssize) { + sim_debug(DEBUG_EXP, dptr, + "Error %08x on read %04x of diskfile cyl %04x hds %02x sec %02x\n", + len, ssize, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + sim_debug(DEBUG_DETAIL, dptr, + "scfi_srv after READ chsa %04x buffer %06x count %04x\n", + chsa, chp->ccw_addr, chp->ccw_count); + sim_debug(DEBUG_DETAIL, dptr, + "scfi_srv after READ buffer %06x count %04x data %02x%02x%02x%02x %02x%02x%02x%02x\n", +// chp->ccw_addr, chp->ccw_count, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + chp->ccw_addr, chp->ccw_count, buf[1016], buf[1017], buf[1018], buf[1019], + buf[1020], buf[1021], buf[1022], buf[1023]); + + uptr->CHS++; /* next sector number */ + /* process the next sector of data */ + for (i=0; ichan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + sim_debug(DEBUG_CMD, dptr, + "SCFI Read %04x bytes leaving %04x from diskfile %04x/%02x/%02x\n", + i, chp->ccw_count, ((uptr->CHS)>>16)&0xffff, + ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } + } + + sim_debug(DEBUG_CMD, dptr, + "SCFI READ %04x bytes leaving %4x to be read to %06x from diskfile %04x/%02x/%02x\n", + ssize, chp->ccw_count, chp->ccw_addr, + ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + + /* get sector offset */ + tstart = uptr->CHS; + + /* see if over end of disk */ + if (tstart >= (uint32)CAP(type)) { + /* EOM reached, abort */ + sim_debug(DEBUG_EXP, dptr, + "DISK Read reached EOM for read from disk @ /%04x/%02x/%02x\n", + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->CHS = 0; /* reset cylinder position */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* see if we are done reading data */ + if (test_write_byte_end(chsa)) { + sim_debug(DEBUG_CMD, dptr, + "DISK Read complete for read from diskfile %04x/%02x/%02x\n", + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + } + + sim_debug(DEBUG_CMD, dptr, + "DISK sector read complete, %x bytes to go from diskfile %04x/%02x/%02x\n", + chp->ccw_count, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); +#ifdef FAST_FOR_UTX + sim_activate(uptr, 15); /* start things off */ +#else + sim_activate(uptr, 150); /* wait to read next sector */ +#endif + break; + } + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + break; + + case DSK_WD: /* Write Data command 0x01 */ + if ((uptr->CMD & DSK_WRITING) == 0) { /* see if we are writing data */ + sim_debug(DEBUG_CMD, dptr, + "DISK WRITE starting unit=%02x CMD %08x write %04x from %06x to %03x/%02x/%02x\n", + unit, uptr->CMD, chp->ccw_count, chp->ccw_addr, + ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + + if (uptr->SNS & 0xf0000000) { /* see if any mode bit 0-3 is set */ + uptr->SNS |= SNS_MOCK; /* mode check error */ + chp->chan_status |= STATUS_PCHK; /* channel prog check */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + break; + } + uptr->CMD |= DSK_WRITING; /* write to disk starting */ + } + if (uptr->CMD & DSK_WRITING) { /* see if we are writing data */ + /* get file offset in sectors */ + tstart = uptr->CHS; + /* file offset in bytes */ + tstart = tstart * SSB(type); + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "scfi_srv WRITE, Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + + /* process the next sector of data */ + tcyl = 0; /* used here as a flag for short read */ + for (i=0; ichan_status & STATUS_PCHK) /* test for memory error */ + uptr->SNS |= SNS_INAD; /* invalid address */ + /* if error on reading 1st byte, we are done writing */ + if ((i == 0) || (chp->chan_status & STATUS_PCHK)) { + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "DISK Wrote %04x bytes to diskfile cyl %04x hds %02x sec %02x\n", + ssize, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + if (chp->chan_status & STATUS_PCHK) /* test for memory error */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); + else + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } + ch = 0; /* finish out the sector with zero */ + tcyl++; /* show we have no more data to write */ + } + buf2[i] = ch; /* save the char */ + } + + /* get file offset in sectors */ + tstart = uptr->CHS; + + /* write the sector to disk */ + if ((i=sim_fwrite(buf2, 1, ssize, uptr->fileref)) != ssize) { + sim_debug(DEBUG_EXP, dptr, + "Error %08x on write %04x bytes to diskfile cyl %04x hds %02x sec %02x\n", + i, ssize, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + sim_debug(DEBUG_DETAIL, dptr, + "scfi_srv after WRITE buffer %06x count %04x data %02x%02x%02x%02x %02x%02x%02x%02x\n", + chp->ccw_addr, chp->ccw_count, buf2[1016], buf2[1017], buf2[1018], buf2[1019], + buf2[1020], buf2[1021], buf2[1022], buf2[1023]); + + sim_debug(DEBUG_CMD, dptr, + "DISK WR to sec end %04x bytes end %04x to diskfile cyl %04x hds %02x sec %02x\n", + len, ssize, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + + uptr->CHS++; /* next sector number */ + if (tcyl != 0) { /* see if done with write command */ + sim_debug(DEBUG_CMD, dptr, + "DISK WroteB %04x bytes to diskfile cyl %04x hds %02x sec %02x\n", + ssize, STAR2CYL(uptr->CHS), ((uptr->CHS) >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we done */ + break; + } + /* get sector offset */ + tstart = uptr->CHS; + + /* see if over end of disk */ + if (tstart >= (uint32)CAP(type)) { + /* EOM reached, abort */ + sim_debug(DEBUG_EXP, dptr, + "DISK Write reached EOM for write to disk @ %04x/%02x/%02x\n", + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->CHS = 0; /* reset cylinder position */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* see if we are done reading data */ + if (test_write_byte_end(chsa)) { + sim_debug(DEBUG_CMD, dptr, + "DISK Write complete for read from diskfile %04x/%02x/%02x\n", + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + } + +#ifdef FAST_FOR_UTX + sim_activate(uptr, 15); /* start things off */ +#else + sim_activate(uptr, 150); /* wait to read next sector */ +#endif + break; + } + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + break; + + default: + sim_debug(DEBUG_EXP, dptr, "invalid command %02x unit %02x\n", cmd, unit); + uptr->SNS |= SNS_CMDREJ; + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|STATUS_PCHK); /* return Prog Check */ + break; + } + sim_debug(DEBUG_DETAIL, dptr, + "scfi_srv done cmd %02x chsa %04x chs %04x/%02x/%02x\n", + cmd, chsa, ((uptr->CHS)>>16)&0xffff, ((uptr->CHS)>>8)&0xff, (uptr->CHS)&0xff); + return SCPE_OK; +} + +/* handle rschnlio cmds for disk */ +t_stat scfi_rschnlio(UNIT *uptr) { + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & DSK_CMDMSK; + + sim_debug(DEBUG_EXP, dptr, + "scfi_rschnl chsa %04x cmd = %02x\n", chsa, cmd); + scfi_ini(uptr, 0); /* reset the unit */ + return SCPE_OK; +} + +/* initialize the disk */ +void scfi_ini(UNIT *uptr, t_bool f) +{ + DEVICE *dptr = get_dev(uptr); + int i = GET_TYPE(uptr->flags); + + /* start out at sector 0 */ + uptr->CHS = 0; /* set CHS to cyl/hd/sec = 0 */ + uptr->STAR = 0; /* set STAR to cyl/hd/sec = 0 */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + /* total sectors on disk */ + uptr->capac = CAP(i); /* size in sectors */ + sim_cancel(uptr); /* stop any timers */ + + sim_debug(DEBUG_EXP, &sda_dev, "SDA init device %s on unit SDA%04x cap %x %d\n", + dptr->name, GET_UADDR(uptr->CMD), uptr->capac, uptr->capac); +} + +t_stat scfi_reset(DEVICE *dptr) +{ + /* add reset code here */ + return SCPE_OK; +} + +/* create the disk file for the specified device */ +int scfi_format(UNIT *uptr) { + int type = GET_TYPE(uptr->flags); + DEVICE *dptr = get_dev(uptr); + uint32 ssize = SSB(type); /* disk sector size in bytes */ + uint32 tsize = SPT(type); /* get track size in sectors */ + uint32 csize = SPC(type); /* get cylinder size in sectors */ + uint32 cyl = CYL(type); /* get # cylinders */ + uint32 cap = CAP(type); /* disk capacity in sectors */ + uint32 cylv = cyl; /* number of cylinders */ + uint8 *buff; + int32 i; + t_stat oldsw = sim_switches; /* save switches */ + + /* last sector address of disk (cyl * hds * spt) - 1 */ + uint32 laddr = CAP(type) - 1; /* last sector of disk */ + + /* make up dummy defect map */ + uint32 dmap[4] = {0xf0000000 | (cap-1), 0x8a000000, + 0x9a000000 | (cap-1), 0xf4000000}; + + + /* see if -i or -n specified on attach command */ + if (!(sim_switches & SWMASK('N')) && !(sim_switches & SWMASK('I'))) { + sim_switches = 0; /* simh tests 'N' & 'Y' switches */ + /* see if user wants to initialize the disk */ + if (!get_yn("Initialize disk? [Y] ", TRUE)) { + sim_switches = oldsw; + return 1; + } + sim_switches = oldsw; /* restore switches */ + } + + /* seek to sector 0 */ + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + fprintf (stderr, "Error on seek to 0\r\n"); + return 1; + } + + /* get buffer for track data in bytes */ + if ((buff = (uint8 *)calloc(csize*ssize, sizeof(uint8))) == 0) { + detach_unit(uptr); + return SCPE_ARG; + } + sim_debug(DEBUG_CMD, dptr, + "Creating disk file of trk size %04x bytes, capacity %d\n", + tsize*ssize, cap*ssize); + + /* write zeros to each track of the disk */ + for (cyl = 0; cyl < cylv; cyl++) { + if ((sim_fwrite(buff, 1, csize*ssize, uptr->fileref)) != csize*ssize) { + sim_debug(DEBUG_EXP, dptr, + "Error on write to diskfile cyl %04x\n", cyl); + free(buff); /* free cylinder buffer */ + buff = 0; + return 1; + } + if ((cyl % 100) == 0) + fputc('.', stderr); + } + fputc('\r', stderr); + fputc('\n', stderr); + free(buff); /* free cylinder buffer */ + buff = 0; + + /* byte swap the buffer for dmap */ + for (i=0; i<4; i++) { + dmap[i] = (((dmap[i] & 0xff) << 24) | ((dmap[i] & 0xff00) << 8) | + ((dmap[i] & 0xff0000) >> 8) | ((dmap[i] >> 24) & 0xff)); + } + + /* now seek to end of disk and write the dmap data to last sector */ + /* write dmap data to last sector on disk */ + if ((sim_fseek(uptr->fileref, laddr*ssize, SEEK_SET)) != 0) { /* seek last sector */ + sim_debug(DEBUG_EXP, dptr, + "Error on last sector seek to sect %06x offset %06x\n", + cap-1, (cap-1)*ssize); + return 1; + } + if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) { + sim_debug(DEBUG_EXP, dptr, + "Error writing DMAP to sect %06x offset %06x\n", + cap-1, (cap-1)*ssize); + return 1; + } + + printf("Disk %s has %x (%d) cyl, %x (%d) hds, %x (%d) sec\r\n", + scfi_type[type].name, CYL(type), CYL(type), HDS(type), HDS(type), + SPT(type), SPT(type)); + + /* seek home again */ + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + fprintf (stderr, "Error on seek to 0\r\n"); + return 1; + } + return 0; /* good or error */ +} + +/* attach the selected file to the disk */ +t_stat scfi_attach(UNIT *uptr, CONST char *file) +{ + uint16 chsa = GET_UADDR(uptr->CMD); + CHANP *chp = find_chanp_ptr(chsa);/* get channel prog pointer */ + int type = GET_TYPE(uptr->flags); + DEVICE *dptr = get_dev(uptr); + DIB *dibp = 0; + t_stat r, s; + uint32 ssize; /* sector size in bytes */ + uint32 info, good; + uint8 buff[1024]; + int i, j; + + /* last sector address of disk (cyl * hds * spt) - 1 */ + uint32 laddr = CAP(type) - 1; /* last sector of disk */ + /* get sector address of utx diag map (DMAP) track 0 pointer */ + /* put data = 0xf0000000 + (cyl-1), 0x8a000000 + daddr, */ + /* 0x9a000000 + (cyl-1), 0xf4000000 */ + /* defect map */ + uint32 dmap[4] = {0xf0000000 | (CAP(type)-1), 0x8a000000, + 0x9a000000 | (CAP(type)-1), 0xf4000000}; + + for (i=0; i<4; i++) { /* byte swap data for last sector */ + dmap[i] = (((dmap[i] & 0xff) << 24) | ((dmap[i] & 0xff00) << 8) | + ((dmap[i] & 0xff0000) >> 8) | ((dmap[i] >> 24) & 0xff)); + } + + /* see if valid disk entry */ + if (scfi_type[type].name == 0) { /* does the assigned disk have a name */ + detach_unit(uptr); /* no, reject */ + return SCPE_FMT; /* error */ + } + + if (dptr->flags & DEV_DIS) { + fprintf(sim_deb, + "ERROR===ERROR\nSCFI Disk device %s disabled on system, aborting\r\n", + dptr->name); + printf("ERROR===ERROR\nSCFI Disk device %s disabled on system, aborting\r\n", + dptr->name); + return SCPE_UDIS; /* device disabled */ + } + + /* have simulator attach the file to the unit */ + if ((r = attach_unit(uptr, file)) != SCPE_OK) + return r; + + uptr->capac = CAP(type); /* disk capacity in sectors */ + ssize = SSB(type); /* get sector size in bytes */ + for (i=0; i<(int)ssize; i++) + buff[i] = 0; /* zero the buffer */ + + sim_debug(DEBUG_CMD, dptr, + "SCFI Disk %s cyl %d hds %d sec %d ssiz %d capacity %d\n", + scfi_type[type].name, scfi_type[type].cyl, scfi_type[type].nhds, + scfi_type[type].spt, ssize, uptr->capac); /* disk capacity */ + printf("SCFI Disk %s cyl %d hds %d sec %d ssiz %d capacity %d\r\n", + scfi_type[type].name, scfi_type[type].cyl, scfi_type[type].nhds, + scfi_type[type].spt, ssize, uptr->capac); /* disk capacity */ + + /* see if -i or -n specified on attach command */ + if ((sim_switches & SWMASK('N')) || (sim_switches & SWMASK('I'))) { + goto fmt; /* user wants new disk */ + } + + /* seek to end of disk */ + if ((sim_fseek(uptr->fileref, 0, SEEK_END)) != 0) { + sim_debug(DEBUG_CMD, dptr, "SCFI Disk attach SEEK end failed\n"); + printf( "SCFI Disk attach SEEK end failed\r\n"); + goto fmt; /* not setup, go format */ + } + + s = ftell(uptr->fileref); /* get current file position */ + if (s == 0) { + sim_debug(DEBUG_CMD, dptr, "SCFI Disk attach ftell failed s=%06d\n", s); + printf("SCFI Disk attach ftell failed s=%06d\r\n", s); + goto fmt; /* not setup, go format */ + } + sim_debug(DEBUG_CMD, dptr, + "SCFI Disk attach ftell value s=%06d b=%06d CAP %06d\n", s/ssize, s, CAP(type)); + printf("SCFI Disk attach ftell value s=%06d b=%06d CAP %06d\r\n", s/ssize, s, CAP(type)); + + if (((int)s/(int)ssize) < ((int)CAP(type))) { /* full sized disk? */ + j = (CAP(type) - (s/ssize)); /* get # sectors to write */ + sim_debug(DEBUG_CMD, dptr, + "SCFI Disk attach for MPX 1.X needs %04d more sectors added to disk\n", j); + printf("SCFI Disk attach for MPX 1.X needs %04d more sectors added to disk\r\n", j); + /* must be MPX 1.X disk, extend to MPX 3.X size */ + /* write sectors of zero to end of disk to fill it out */ + for (i=0; ifileref) != ssize)) { + sim_debug(DEBUG_CMD, dptr, "SCFI Disk attach fread ret = %04d\n", r); + printf("SCFI Disk attach fread ret = %04d\r\n", r); + goto fmt; /* not setup, go format */ + } + } + s = ftell(uptr->fileref); /* get current file position */ + sim_debug(DEBUG_CMD, dptr, + "SCFI Disk attach MPX 1.X file extended & sized secs %06d bytes %06d\n", + s/ssize, s); + printf("SCFI Disk attach MPX 1.X file extended & sized secs %06d bytes %06d\r\n", + s/ssize, s); + } + + /* seek last sector of disk */ + if ((sim_fseek(uptr->fileref, (CAP(type)-1)*ssize, SEEK_SET)) != 0) { + sim_debug(DEBUG_CMD, dptr, "SCFI Disk attach SEEK last sector failed\n"); + printf("SCFI Disk attach SEEK last sector failed\r\n"); + goto fmt; + } + + /* see if there is disk size-1 in last sector of disk, if not add it */ + if ((r = sim_fread(buff, sizeof(uint8), ssize, uptr->fileref) != ssize)) { + sim_debug(DEBUG_CMD, dptr, "SCFI Disk format fread error = %04d\n", r); + printf("SCFI Disk format fread error = %04d\r\n", r); +add_size: + if (ssize == 768) { + /* assume we have MPX 1x, and go on */ + /* write dmap data to last sector on disk for mpx 1.x */ + if ((sim_fseek(uptr->fileref, laddr*ssize, SEEK_SET)) != 0) { /* seek last sector */ + sim_debug(DEBUG_CMD, dptr, + "SCFI Error on last sector seek to sect %06d offset %06d bytes\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + printf("SCFI Error on last sector seek to sect %06d offset %06d bytes\r\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + goto fmt; + } + if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) { + sim_debug(DEBUG_CMD, dptr, + "SCFI Error writing DMAP to sect %06x offset %06d bytes\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + printf("SCFI Error writing DMAP to sect %06x offset %06d bytes\r\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + goto fmt; + } + + /* seek last sector of disk */ + if ((sim_fseek(uptr->fileref, (CAP(type))*ssize, SEEK_SET)) != 0) { + sim_debug(DEBUG_CMD, dptr, "SCFI Disk attach SEEK last sector failed\n"); + printf( "SCFI Disk attach SEEK last sector failed\r\n"); + goto fmt; + } + s = ftell(uptr->fileref); /* get current file position */ + sim_debug(DEBUG_CMD, dptr, + "SCFI Disk attach MPX file extended & sized secs %06d bytes %06d\n", s/ssize, s); + printf("SCFI Disk attach MPX file extended & sized secs %06d bytes %06d\r\n", s/ssize, s); + goto ldone; + } else { + /* error if UTX */ + detach_unit(uptr); /* if error, abort */ + return SCPE_FMT; /* error */ + } + } else { + /* if not disk size, go add it in for MPX, error if UTX */ + if ((buff[0] | buff[1] | buff[2] | buff[3]) == 0) { + sim_debug(DEBUG_CMD, dptr, + "SCFI Disk format0 buf0 %02x buf1 %02x buf2 %02x buf3 %02x\n", + buff[0], buff[1], buff[2], buff[3]); + goto add_size; + } + } + + info = (buff[0]<<24) | (buff[1]<<16) | (buff[2]<<8) | buff[3]; + good = 0xf0000000 | (CAP(type)-1); + /* check for 0xf0ssssss where ssssss is disk size-1 in sectors */ + if (info != good) { + sim_debug(DEBUG_CMD, dptr, + "SCFI Disk format error buf0 %02x buf1 %02x buf2 %02x buf3 %02x\n", + buff[0], buff[1], buff[2], buff[3]); + printf( + "SCFI Disk format error buf0 %02x buf1 %02x buf2 %02x buf3 %02x\r\n", + buff[0], buff[1], buff[2], buff[3]); +fmt: + /* format the drive */ + if (scfi_format(uptr)) { + detach_unit(uptr); /* if no space, error */ + return SCPE_FMT; /* error */ + } + } +ldone: + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + detach_unit(uptr); /* detach if error */ + return SCPE_FMT; /* error */ + } + + /* start out at sector 0 */ + uptr->CHS = 0; /* set CHS to cyl/hd/sec = 0 */ + + sim_debug(DEBUG_CMD, dptr, + "SCFI Attach %s %04x cyl %d hds %d spt %d spc %d cap sec %d cap bytes %d\n", + scfi_type[type].name, chsa, CYL(type), HDS(type), SPT(type), SPC(type), + CAP(type), CAPB(type)); + printf("SCFI Attach %s %04x cyl %d hds %d spt %d spc %d cap sec %d cap bytes %d\r\n", + scfi_type[type].name, chsa, CYL(type), HDS(type), SPT(type), SPC(type), + CAP(type), CAPB(type)); + + sim_debug(DEBUG_CMD, dptr, + "SCFI File %s at chsa %04x attached to %s is ready\n", + file, chsa, scfi_type[type].name); + printf("SCFI File %s at chsa %04x attached to %s is ready\r\n", + file, chsa, scfi_type[type].name); + + /* check for valid configured disk */ + /* must have valid DIB and Channel Program pointer */ + dibp = (DIB *)dptr->ctxt; /* get the DIB pointer */ + if ((dib_unit[chsa] == NULL) || (dibp == NULL) || (chp == NULL)) { + sim_debug(DEBUG_EXP, dptr, + "ERROR===ERROR\nSCFI device %s not configured on system, aborting\n", + dptr->name); + printf("ERROR===ERROR\nSCFI device %s not configured on system, aborting\r\n", + dptr->name); + detach_unit(uptr); /* detach if error */ + return SCPE_UNATT; /* error */ + } + set_devattn(chsa, SNS_DEVEND); + return SCPE_OK; +} + +/* detach a disk device */ +t_stat scfi_detach(UNIT *uptr) { + uptr->SNS = 0; /* clear sense data */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + return detach_unit(uptr); /* tell simh we are done with disk */ +} + +/* boot from the specified disk unit */ +t_stat scfi_boot(int32 unit_num, DEVICE *dptr) { + UNIT *uptr = &dptr->units[unit_num]; /* find disk unit number */ + + sim_debug(DEBUG_CMD, dptr, + "SCFI Disk Boot dev/unit %x\n", GET_UADDR(uptr->CMD)); + + /* see if device disabled */ + if (dptr->flags & DEV_DIS) { + printf("ERROR===ERROR\r\nSCFI Disk device %s disabled on system, aborting\r\n", + dptr->name); + return SCPE_UDIS; /* device disabled */ + } + + if ((uptr->flags & UNIT_ATT) == 0) { + sim_debug(DEBUG_EXP, dptr, + "SCFI Disk Boot attach error dev/unit %04x\n", GET_UADDR(uptr->CMD)); + printf("SCFI Disk Boot attach error dev/unit %04x\n", GET_UADDR(uptr->CMD)); + return SCPE_UNATT; /* attached? */ + } + SPAD[0xf4] = GET_UADDR(uptr->CMD); /* put boot device chan/sa into spad */ + SPAD[0xf8] = 0xF000; /* show as F class device */ + + /* now boot the disk */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + return chan_boot(GET_UADDR(uptr->CMD), dptr); /* boot the ch/sa */ +} + +/* Disk option setting commands */ +/* set the disk type attached to unit */ +t_stat scfi_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int i; + + if (cptr == NULL) /* any disk name input? */ + return SCPE_ARG; /* arg error */ + if (uptr == NULL) /* valid unit? */ + return SCPE_IERR; /* no, error */ + if (uptr->flags & UNIT_ATT) /* is unit attached? */ + return SCPE_ALATT; /* no, error */ + + /* now loop through the units and find named disk */ + for (i = 0; scfi_type[i].name != 0; i++) { + if (strcmp(scfi_type[i].name, cptr) == 0) { + uptr->flags &= ~UNIT_TYPE; /* clear the old UNIT type */ + uptr->flags |= SET_TYPE(i); /* set the new type */ + /* set capacity of disk in sectors */ + uptr->capac = CAP(i); + return SCPE_OK; + } + } + return SCPE_ARG; +} + +t_stat scfi_get_type(FILE *st, UNIT *uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fputs("TYPE=", st); + fputs(scfi_type[GET_TYPE(uptr->flags)].name, st); + return SCPE_OK; +} + +/* help information for disk */ +t_stat scfi_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + int i; + fprintf (st, "SEL-32 SCFI Disk Processor\r\n"); + fprintf (st, "Use:\r\n"); + fprintf (st, " sim> SET %sn TYPE=type\r\n", dptr->name); + fprintf (st, "Type can be: "); + for (i = 0; scfi_type[i].name != 0; i++) { + fprintf(st, "%s", scfi_type[i].name); + if (scfi_type[i+1].name != 0) + fprintf(st, ", "); + } + fprintf (st, ".\nEach drive has the following storage capacity:\r\n"); + for (i = 0; scfi_type[i].name != 0; i++) { + int32 size = CAPB(i); /* disk capacity in bytes */ + size /= 1024; /* make KB */ + size = (10 * size) / 1024; /* size in MB * 10 */ + fprintf(st, " %-8s %4d.%1d MB cyl %3d hds %3d sec %3d blk %3d\r\n", + scfi_type[i].name, size/10, size%10, CYL(i), HDS(i), SPT(i), SSB(i)); + } + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char *scfi_description (DEVICE *dptr) +{ + return "SEL-32 SCFI Disk Processor"; +} + +#endif diff --git a/SEL32/sel32_scsi.c b/SEL32/sel32_scsi.c new file mode 100644 index 00000000..8540f9a5 --- /dev/null +++ b/SEL32/sel32_scsi.c @@ -0,0 +1,2068 @@ +/* sel32_scsi.c: SEL-32 MFP SCSI Disk controller + + Copyright (c) 2018-2022, James C. Bevier + Portions provided by Richard Cornwell and other SIMH contributers + + 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 + JAMES C. BEVIER 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. +*/ + +#include "sel32_defs.h" + +/* uncomment to use fast sim_activate times when running UTX */ +/* UTX gets an ioi error for dm0801 if slow times are used */ +/* dm0801 is not even a valid unit number for UDP controller */ +#define FAST_FOR_UTX + +#if NUM_DEVS_SCSI > 0 + +#define UNIT_SCSI UNIT_ATTABLE | UNIT_IDLE | UNIT_DISABLE + +/* useful conversions */ +/* Fill STAR value from cyl, trk, sec data */ +#define CHS2STAR(c,h,s) (((c<<16) & LMASK)|((h<<8) & 0xff00)|(s & 0xff)) +/* convert STAR value to number of sectors */ +#define STAR2SEC(star,spt,spc) ((star&0xff)+(((star>>8)&0xff)*spt)+((star>>16)*spc)) +/* convert STAR value to number of heads or tracks */ +#define STAR2TRK(star,tpc) ((star >> 16) * tpc + ((star >> 8) & 0x0ff)) +/* convert STAR value to number of cylinders */ +#define STAR2CYL(star) ((star >> 16) & RMASK) +/* convert byte value to number of sectors mod sector size */ +#define BYTES2SEC(bytes,ssize) (((bytes) + (ssize-1)) >> 10) +/* get sectors per track for specified type */ +#define SPT(type) (scsi_type[type].spt) +/* get sectors per cylinderfor specified type */ +#define SPC(type) (scsi_type[type].spt*scsi_type[type].nhds) +/* get number of cylinders for specified type */ +#define CYL(type) (scsi_type[type].cyl) +/* get number of heads for specified type */ +#define HDS(type) (scsi_type[type].nhds) +/* get disk capacity in sectors for specified type */ +#define CAP(type) (CYL(type)*HDS(type)*SPT(type)) +/* get number of bytes per sector for specified type */ +#define SSB(type) (scsi_type[type].ssiz*4) +/* get disk capacity in bytes for specified type */ +#define CAPB(type) (CAP(type)*SSB(type)) +/* get disk geometry as STAR value for specified type */ +#define GEOM(type) (CHS2STAR(CYL(type),HDS(type),SPT(type))) + +/* INCH command information */ +/* +WD 0 - Data address +WD 1 - Flags - 0 -36 byte count + +Data - 224 word INCH buffer address (SST) +WD 1 Drive 0 Attribute register +WD 2 Drive 1 Attribute register +WD 3 Drive 2 Attribute register +WD 4 Drive 3 Attribute register +WD 5 Drive 4 Attribute register +WD 6 Drive 5 Attribute register +WD 7 Drive 6 Attribute register +WD 8 Drive 7 Attribute register + +Memory attribute register layout +bits 0-7 - Flags + bits 0&1 - 00=Reserved, 01=MHD, 10=FHD, 11=MHD with FHD option + bit 2 - 1=Cartridge module drive + bit 3 - 0=Reserved + bit 4 - 1=Drive not present + bit 5 - 1=Dual Port + bit 6 - 0=Blk size 00=768 byte blk + bit 7 - 0=Blk size 01=1024 byte blk +bits 8-15 - sector count (sectors per track)(F16=16, F20=20) +bits 16-23 - MHD Head count (number of heads on MHD) +bits 24-31 - FHD head count (number of heads on FHD or number head on FHD option of + mini-module) +*/ + + +/* 224 word INCH Buffer layout */ +/* 128 word subchannel status storage (SST) */ +/* 66 words of program status queue (PSQ) */ +/* 26 words of scratchpad */ +/* 4 words of label buffer registers */ + +/* track label / sector label definations */ +/* + short lcyl; cylinder + char ltkn; track + char lid; sector id + char lflg1; track/sector status flags + bit 0 good + 1 alternate + 2 spare + 3 reserved + 4 flaw + 5 last track + 6 start of alternate + char lflg2; + short lspar1; + short lspar2; + short ldef1; + int ldeallp; DMAP block number trk0 + int lumapp; UMAP block number sec1 + short ladef3; + short laltcyl; + char lalttk; sectors per track + char ldscnt; number of heads + char ldatrflg; device attributes + bit 0 n/u + 1 disk is mhd + 2 n/u + 3 n/u + 4 n/u + 5 dual ported + 6/7 00 768 bytes/blk + 01 1024 bytes/blk + 10 2048 bytes/blk + char ldatrscnt; sectors per track (again) + char ldatrmhdc; MHD head count + char ldatrfhdc; FHD head count + */ + +#define CMD u3 +/* u3 */ +/* in u3 is device command code and status */ +#define DSK_CMDMSK 0x00ff /* Command being run */ +#define DSK_STAR 0x0100 /* STAR value in u4 */ +#define DSK_NU 0x0200 /* Not used */ +#define DSK_READDONE 0x0400 /* Read finished, end channel */ +#define DSK_ENDDSK 0x0800 /* Sensed end of disk */ +#define DSK_SEEKING 0x1000 /* Disk is currently seeking */ +#define DSK_READING 0x2000 /* Disk is reading data */ +#define DSK_WRITING 0x4000 /* Disk is writing data */ +#define DSK_BUSY 0x8000 /* Disk is busy */ +/* commands */ +#define DSK_INCH 0x00 /* Initialize channel */ +#define DSK_INCH2 0xF0 /* Initialize channel for processing */ +#define DSK_WD 0x01 /* Write data */ +#define DSK_RD 0x02 /* Read data */ +#define DSK_NOP 0x03 /* No operation */ +#define DSK_SNS 0x04 /* Sense */ +#define DSK_SCK 0x07 /* Seek cylinder, track, sector */ +#define DSK_TIC 0x08 /* Transfer in channel */ +//#define DSK_FNSK 0x0B /* Format for no skip */ +#define DSK_RBLK 0x13 /* Reassign Block */ +#define DSK_LMR 0x1F /* Load mode register */ +#define DSK_RWD 0x23 /* Rewind */ +//#define DSK_WSL 0x31 /* Write sector label */ +//#define DSK_RSL 0x32 /* Read sector label */ +//#define DSK_REL 0x33 /* Release */ +#define DSK_XEZ 0x37 /* Rezero */ +//#define DSK_ADV 0x43 /* Advance Record */ +//#define DSK_IHA 0x47 /* Increment head address */ +//#define DSK_SRM 0x4F /* Set reserve track mode */ +//#define DSK_WTL 0x51 /* Write track label */ +//#define DSK_RTL 0x52 /* Read track label */ +#define DSK_RCAP 0x53 /* Read Capacity */ +//#define DSK_XRM 0x5F /* Reset reserve track mode */ +//#define DSK_RAP 0xA2 /* Read angular positions */ +#define DSK_RES 0xA3 /* Reserve Unit */ +//#define DSK_TESS 0xAB /* Test STAR (subchannel target address register) */ +#define DSK_INQ 0xB3 /* Inquiry */ +#define DSK_REL 0xC3 /* Release Unit */ +#define DSK_TCMD 0xD3 /* Transfer Command Packet (specifies CDB to send) */ +//#define DSK_ICH 0xFF /* Initialize Controller */ +#define DSK_FRE 0xF3 /* Reserved */ +#define DSK_SID 0x80 /* MFP status command */ + +#define STAR u4 +/* u4 - sector target address register (STAR) */ +/* Holds the current cylinder, head(track), sector */ +#define DISK_CYL 0xFFFF0000 /* cylinder mask */ +#define DISK_TRACK 0x0000FF00 /* track mask */ +#define DISK_SECTOR 0x000000ff /* sector mask */ + +#define SNS u5 +/* u5 */ +/* Sense byte 0 - mode register */ +#define SNS_DROFF 0x80000000 /* Drive Carriage will be offset */ +#define SNS_TRKOFF 0x40000000 /* Track offset: 0=positive, 1=negative */ +#define SNS_RDTMOFF 0x20000000 /* Read timing offset = 1 */ +#define SNS_RDSTRBT 0x10000000 /* Read strobe timing: 1=positive, 0=negative */ +#define SNS_DIAGMOD 0x08000000 /* Diagnostic Mode ECC Code generation and checking */ +#define SNS_RSVTRK 0x04000000 /* Reserve Track mode: 1=OK to write, 0=read only */ +#define SNS_FHDOPT 0x02000000 /* FHD or FHD option = 1 */ +//#define SNS_RESERV 0x01000000 /* Reserved */ +#define SNS_TCMD 0x01000000 /* Presessing CMD cmd chain */ + +/* Sense byte 1 */ +#define SNS_CMDREJ 0x800000 /* Command reject */ +#define SNS_INTVENT 0x400000 /* Unit intervention required */ +#define SNS_SPARE1 0x200000 /* Spare */ +#define SNS_EQUCHK 0x100000 /* Equipment check */ +#define SNS_DATCHK 0x080000 /* Data Check */ +#define SNS_OVRRUN 0x040000 /* Data overrun/underrun */ +#define SNS_DSKFERR 0x020000 /* Disk format error */ +#define SNS_DEFTRK 0x010000 /* Defective track encountered */ + +/* Sense byte 2 */ +#define SNS_LAST 0x8000 /* Last track flag encountered */ +#define SNS_AATT 0x4000 /* At Alternate track */ +#define SNS_WPER 0x2000 /* Write protection error */ +#define SNS_WRL 0x1000 /* Write lock error */ +#define SNS_MOCK 0x0800 /* Mode check */ +#define SNS_INAD 0x0400 /* Invalid memory address */ +#define SNS_RELF 0x0200 /* Release fault */ +#define SNS_CHER 0x0100 /* Chaining error */ + +/* Sense byte 3 */ +#define SNS_REVL 0x80 /* Revolution lost */ +#define SNS_DADE 0x40 /* Disc addressing or seek error */ +#define SNS_BUCK 0x20 /* Buffer check */ +#define SNS_ECCS 0x10 /* ECC error in sector label */ +#define SNS_ECCD 0x08 /* ECC error iin data */ +#define SNS_ECCT 0x04 /* ECC error in track label */ +#define SNS_RTAE 0x02 /* Reserve track access error */ +#define SNS_UESS 0x01 /* Uncorrectable ECC error */ + +#define CHS u6 +/* u6 holds the current cyl, hd, sec for the drive */ + +/* this attribute information is provided by the INCH command */ +/* for each device and is not used. It is reconstructed from */ +/* the disk_t structure data for the assigned disk */ +/* +bits 0-7 - Flags + bits 0&1 - 00=Reserved, 01=MHD, 10=FHD, 11=MHD with FHD option + bit 2 - 1=Cartridge module drive + bit 3 - 0=Reserved + bit 4 - 1=Drive not present + bit 5 - 1=Dual Port + bit 6 - 0=Reserved 00 768 byte sec + bit 7 - 0=Reserved 01 1024 byte sec +bits 8-15 - sector count (sectors per track)(F16=16, F20=20) +bits 16-23 - MHD Head count (number of heads on MHD) +bits 24-31 - FHD head count (number of heads on FHD or number head on FHD option of + mini-module) +*/ + +/* INCH addr up7 */ + +/* disk definition structure */ +struct scsi_t +{ + const char *name; /* Device ID Name */ + uint16 nhds; /* Number of heads */ + uint16 ssiz; /* sector size in words */ + uint16 spt; /* # sectors per track(head) */ + uint16 ucyl; /* Number of cylinders used */ + uint16 cyl; /* Number of cylinders on disk */ + uint8 type; /* Device type code */ + /* bit 1 mhd */ + /* bits 6/7 = 0 768 byte blk */ /* not used on UDP/DPII */ + /* = 1 1024 byte blk */ /* not used on UDP/DPII */ +} + +scsi_type[] = +{ + /* Class F Disc Devices */ + /* MPX SCSI disks for SCSI controller */ + {"SD150", 9, 192, 24, 963, 967, 0x40}, /*0 8820 150M 208872 sec */ + {"SD300", 9, 192, 32, 1405, 1409, 0x40}, /*1 8828 300M 396674 sec */ + {"SD700", 15, 192, 35, 1542, 1546, 0x40}, /*2 8833 700M 797129 sec */ + {"SD1200", 15, 192, 49, 1927, 1931, 0x40}, /*3 8835 1200M 1389584 sec */ + {"SD2400", 19, 192, 59, 2703, 2707, 0x40}, /*4 8842 2400M 2909128 sec */ + {"SH1200", 15, 192, 50, 1868, 1872, 0x40}, /*5 8832 1200M 1395014 sec */ + {"SH2550", 19, 192, 55, 2703, 2707, 0x40}, /*6 8834 2550M 2909128 sec */ + {"SH5150", 21, 192, 83, 3707, 3711, 0x40}, /*7 0000 5150M 5581145 sec */ + {"8820", 9, 256, 18, 963, 967, 0x41}, /*8 8820 150M 156654 sec */ + {"8821", 9, 256, 36, 963, 967, 0x41}, /*9 8828 300M 313308 sec */ + {"8833", 18, 256, 20, 1542, 1546, 0x41}, /*10 8833 700M 556560 sec */ + {"8835", 18, 256, 20, 1927, 1931, 0x41}, /*11 8835 1200M 695160 sec */ + /* For UTX */ + {NULL, 0} +}; + +t_stat scsi_preio(UNIT *uptr, uint16 chan); +t_stat scsi_startcmd(UNIT *uptr, uint16 chan, uint8 cmd); +t_stat scsi_haltio(UNIT *uptr); +t_stat scsi_srv(UNIT *); +t_stat scsi_boot(int32 unitnum, DEVICE *); +void scsi_ini(UNIT *, t_bool); +t_stat scsi_rschnlio(UNIT *uptr); +t_stat scsi_reset(DEVICE *); +t_stat scsi_attach(UNIT *, CONST char *); +t_stat scsi_detach(UNIT *); +t_stat scsi_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat scsi_get_type(FILE * st, UNIT *uptr, int32 v, CONST void *desc); +t_stat scsi_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *scsi_description (DEVICE *dptr); + +/* One buffer per unit */ +//#define BUFFSIZE (256) +#define BUFFSIZE (512) +uint8 scsi_buf[NUM_DEVS_SCSI][NUM_UNITS_SCSI][BUFFSIZE]; +uint8 scsi_pcmd[NUM_DEVS_SCSI][NUM_UNITS_SCSI]; + +/* channel program information */ +CHANP sba_chp[NUM_UNITS_SCSI] = {0}; + +MTAB scsi_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "TYPE", "TYPE", + &scsi_set_type, &scsi_get_type, NULL, "Type of disk"}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, + &show_dev_addr, NULL, "Device channel address"}, + {0} +}; + +UNIT sba_unit[] = { +/* SET_TYPE(0) SD150 */ + {UDATA(&scsi_srv, UNIT_SCSI|SET_TYPE(0), 0), 0, UNIT_ADDR(0x7600)}, /* 0 */ + {UDATA(&scsi_srv, UNIT_SCSI|SET_TYPE(0), 0), 0, UNIT_ADDR(0x7608)}, /* 1 */ +}; + +DIB sba_dib = { + scsi_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + scsi_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + scsi_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + scsi_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + NULL, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + scsi_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + sba_unit, /* UNIT* units */ /* Pointer to units structure */ + sba_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_UNITS_SCSI, /* uint8 numunits */ /* number of units defined */ + 0x38, /* uint8 mask */ /* 8 devices - device mask */ + 0x7600, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +DEVICE sba_dev = { + "SBA", sba_unit, NULL, scsi_mod, + NUM_UNITS_SCSI, 16, 24, 4, 16, 32, + NULL, NULL, &scsi_reset, &scsi_boot, &scsi_attach, &scsi_detach, + /* ctxt is the DIB pointer */ + &sba_dib, DEV_BUF_NUM(0)|DEV_DISABLE|DEV_DEBUG|DEV_DIS, 0, dev_debug, + NULL, NULL, &scsi_help, NULL, NULL, &scsi_description +}; + +#if NUM_DEVS_SCSI > 1 + +/* channel program information */ +CHANP sbb_chp[NUM_UNITS_SCSI] = {0}; + +UNIT sbb_unit[] = { +/* SET_TYPE(0) DM150 */ + {UDATA(&scsi_srv, UNIT_SCSI|SET_TYPE(0), 0), 0, UNIT_ADDR(0x7640)}, /* 0 */ + {UDATA(&scsi_srv, UNIT_SCSI|SET_TYPE(0), 0), 0, UNIT_ADDR(0x7648)}, /* 1 */ +}; + +//DIB sdb_dib = {scsi_preio, scsi_startcmd, NULL, NULL, NULL, +//scsi_ini, sdb_unit, sdb_chp, NUM_UNITS_SCSI, 0x0f, 0x0c00, 0, 0, 0}; + +DIB sbb_dib = { + scsi_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */ + scsi_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */ + scsi_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */ + NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */ + NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */ + NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */ + scsi_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */ + NULL, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */ + scsi_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */ + sbb_unit, /* UNIT* units */ /* Pointer to units structure */ + sbb_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */ + NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */ + NUM_UNITS_SCSI, /* uint8 numunits */ /* number of units defined */ + 0x38, /* uint8 mask */ /* 2 devices - device mask */ + 0x7600, /* uint16 chan_addr */ /* parent channel address */ + 0, /* uint32 chan_fifo_in */ /* fifo input index */ + 0, /* uint32 chan_fifo_out */ /* fifo output index */ + {0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */ +}; + +DEVICE sbb_dev = { + "SBB", sbb_unit, NULL, scsi_mod, + NUM_UNITS_SCSI, 16, 24, 4, 16, 32, + NULL, NULL, &scsi_reset, &scsi_boot, &scsi_attach, &scsi_detach, + /* ctxt is the DIB pointer */ + &sbb_dib, DEV_BUF_NUM(1)|DEV_DISABLE|DEV_DEBUG|DEV_DIS, 0, dev_debug, + NULL, NULL, &scsi_help, NULL, NULL, &scsi_description +}; +#endif + +/* convert sector disk address to star values (c,h,s) */ +uint32 scsisec2star(uint32 daddr, int type) +{ + int32 sec = daddr % scsi_type[type].spt; /* get sector value */ + int32 spc = scsi_type[type].nhds * scsi_type[type].spt; /* sec per cyl */ + int32 cyl = daddr / spc; /* cylinders */ + int32 hds = (daddr % spc) / scsi_type[type].spt; /* heads */ + + /* now return the star value */ + return (CHS2STAR(cyl,hds,sec)); /* return STAR */ +} + +/* start a disk operation */ +t_stat scsi_preio(UNIT *uptr, uint16 chan) +{ + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + int unit = (uptr - dptr->units); + + sim_debug(DEBUG_CMD, dptr, "scsi_preio CMD %08x unit=%02x\n", uptr->CMD, unit); + if ((uptr->CMD & 0xff00) != 0) { /* just return if busy */ + return SNS_BSY; + } + sim_debug(DEBUG_CMD, dptr, "scsi_preio unit %02x chsa %04x OK\n", unit, chsa); + return 0; /* good to go */ +} + +t_stat scsi_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) +{ + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + int unit = (uptr - dptr->units); + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + + sim_debug(DEBUG_CMD, dptr, + "scsi_startcmd unit %02x cmd %02x CMD %08x SNS %08x\n", + unit, cmd, uptr->CMD, uptr->SNS); + if ((uptr->flags & UNIT_ATT) == 0) { /* unit attached status */ + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + if (cmd != DSK_SNS) /* we are completed with unit check status */ + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + } + + if ((uptr->CMD & DSK_CMDMSK) != 0) { + uptr->CMD |= DSK_BUSY; /* Flag we we are busy */ + return SNS_BSY; + } + if ((uptr->CMD & 0xff00) != 0) { /* if any status info, we are busy */ + return SNS_BSY; + } + sim_debug(DEBUG_CMD, dptr, "scsi_startcmd enter unit=%02x cmd %02x\n", unit, cmd); + + /* Unit is online, so process a command */ + switch (cmd) { + + case DSK_INCH: /* INCH 0x00 */ + sim_debug(DEBUG_CMD, dptr, + "scsi_startcmd starting INCH %06x cmd, chsa %04x MemBuf %08x cnt %04x\n", + uptr->u4, chsa, chp->ccw_addr, chp->ccw_count); + + uptr->CMD |= DSK_INCH2; /* use 0xF0 for inch, just need int */ + /* leave the TCMD bit */ + uptr->SNS &= ~MASK24; /* clear all but old mode data */ +#ifdef FAST_FOR_UTX +//zz sim_activate(uptr, 100); /* start things off */ + sim_activate(uptr, 30); /* start things off */ +#else + sim_activate(uptr, 100); /* start things off */ +#endif + return 0; + break; + + case DSK_SCK: /* Seek command 0x07 */ + case DSK_XEZ: /* Rezero & Read IPL record 0x1f */ + case DSK_WD: /* Write command 0x01 */ + case DSK_RD: /* Read command 0x02 */ + case DSK_LMR: /* read mode register */ + case DSK_NOP: /* NOP 0x03 */ + case DSK_RCAP: /* Read Capacity 0x53 */ + /* Transfer Command Packet (specifies CDB to send) */ + case DSK_TCMD: /* Transfer command packet 0xD3 */ + case DSK_SID: /* channel Sense 0x80 */ + /* leave the TCMD bit */ + uptr->SNS &= ~MASK24; /* clear all but old mode data */ + case DSK_SNS: /* Sense 0x04 */ + uptr->CMD |= cmd; /* save cmd */ + sim_debug(DEBUG_CMD, dptr, + "scsi_startcmd starting disk cmd %02x chsa %04x\n", cmd, chsa); + sim_activate(uptr, 100); /* start things off */ + return 0; + break; + } + sim_debug(DEBUG_CMD, dptr, + "scsi_startcmd done with scsi_startcmd %02x chsa %04x SNS %08x\n", + cmd, chsa, uptr->SNS); + if (uptr->SNS & 0xff) /* any other cmd is error */ + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; +#ifdef FAST_FOR_UTX +//zz sim_activate(uptr, 100); /* start things off */ + sim_activate(uptr, 20); /* start things off */ +#else + sim_activate(uptr, 100); /* start things off */ +#endif + return SNS_CHNEND|SNS_DEVEND; +} + +/* Handle processing of disk requests. */ +t_stat scsi_srv(UNIT *uptr) +{ + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + /* get pointer to Dev Info Blk for this device */ + CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */ + int cmd = uptr->CMD & DSK_CMDMSK; + int type = GET_TYPE(uptr->flags); + int unit = (uptr - dptr->units); + int bufnum = GET_DEV_BUF(dptr->flags); + int len=0; + int i; + uint32 cap = CAP(type); + uint32 mema; /* memory address */ + uint8 ch; + int32 ssize = scsi_type[type].ssiz*4; /* Size of one sector in bytes */ + uint32 tstart = 0; /* Location of start of cyl/track/sect in data */ + uint8 buf2[1024]; + uint8 buf[1024]; + + sim_debug(DEBUG_DETAIL, &sba_dev, + "scsi_srv entry unit %02x CMD %08x chsa %04x count %04x %x/%x/%x \n", + unit, uptr->CMD, chsa, chp->ccw_count, + STAR2CYL(uptr->CHS), (uptr->CHS >> 8)&0xff, (uptr->CHS&0xff)); + + if ((uptr->flags & UNIT_ATT) == 0) { /* unit attached status */ + uptr->SNS |= SNS_INTVENT; /* unit intervention required */ + if (cmd != DSK_SNS) /* we are completed with unit check status */ + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + } + + sim_debug(DEBUG_CMD, dptr, + "scsi_srv cmd=%02x chsa %04x count %04x SNS %02x\n", + cmd, chsa, chp->ccw_count, uptr->SNS); + switch (cmd) { + case 0: /* No command, stop disk */ + break; + + case DSK_INCH2: /* use 0xF0 for inch, just need int */ + len = chp->ccw_count; /* INCH command count */ + mema = chp->ccw_addr; /* get inch or buffer addr */ + sim_debug(DEBUG_CMD, dptr, + "scsi_srv starting INCH cmd, chsa %04x MemBuf %06x cnt %04x\n", + chsa, chp->ccw_addr, chp->ccw_count); + +#if 0 + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ +#endif + /* mema has IOCD word 1 contents. For the MFP (scsi processor) */ + /* a pointer to the INCH buffer. The INCH buffer address must be */ + /* set for the parent channel as well as all other devices on the */ + /* channel. Call set_inch() to do this for us. Just return OK and */ + /* channel software will use the status buffer addr */ + + /* now call set_inch() function to write and test inch buffer addresses */ + /* 1-256 wd buffer is provided for 128 status dbl words */ + i = set_inch(uptr, mema, 128); /* new address of 33 entries */ + if ((i == SCPE_MEM) || (i == SCPE_ARG)) { /* any error */ + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + uptr->CMD &= LMASK; /* remove old cmd */ + sim_debug(DEBUG_CMD, dptr, + "scsi_srv cmd INCH chsa %04x chsa %06x count %04x completed\n", + chsa, mema, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_NOP: /* NOP 0x03 */ + uptr->CMD &= LMASK; /* remove old cmd */ + sim_debug(DEBUG_CMD, dptr, + "scsi_srv cmd NOP chsa %04x count %04x completed\n", + chsa, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + /* 3 status wds are to be returned */ + /* Wd 1 MMXXXXXX board model # assume 00 00 08 02*/ + /* Wd 2 MMXXXXXX board firmware model # assume 00 00 08 02*/ + /* Wd 3 MMXXXXXX board firmware revision # assume 00 00 00 14*/ + case DSK_SID: /* 0x80 */ /* this is really for the MFP controller */ + /* send 12 byte Status ID data */ + /* Word 0 */ /* board mod 4324724 = 0x0041fd74 */ + ch = 0x00; + chan_write_byte(chsa, &ch); /* write byte 0 */ + ch = 0x41; + chan_write_byte(chsa, &ch); /* write byte 1 */ + ch = 0xfd; + chan_write_byte(chsa, &ch); /* write byte 2 */ + ch = 0x74; + chan_write_byte(chsa, &ch); /* write byte 3 */ + + /* Word 1 */ /* firmware 4407519 = 0x004340df */ + ch = 0x00; + chan_write_byte(chsa, &ch); /* write byte 4 */ + ch = 0x43; + chan_write_byte(chsa, &ch); /* write byte 5 */ + ch = 0x40; + chan_write_byte(chsa, &ch); /* write byte 6 */ + ch = 0xdf; + chan_write_byte(chsa, &ch); /* write byte 7 */ + + /* Word 2 */ /* firmware rev 4259588 = 0x0040ff04 */ + ch = 0x00; + chan_write_byte(chsa, &ch); /* write byte 8 */ + ch = 0x40; + chan_write_byte(chsa, &ch); /* write byte 9 */ + ch = 0xff; + chan_write_byte(chsa, &ch); /* write byte 10 */ + ch = 0x04; + chan_write_byte(chsa, &ch); /* write byte 11 */ + + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, "scsi_srv SID chan %02x: chnend|devend\n", chsa); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + case DSK_SNS: /* 0x4 */ + sim_debug(DEBUG_CMD, dptr, "scsi_startcmd CMD sense\n"); + + /* bytes 0,1 - Cyl entry from CHS reg */ + ch = (uptr->CHS >> 24) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense CHS b0 unit=%02x 1 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = (uptr->CHS >> 16) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense CHS b1 unit=%02x 2 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + /* byte 2 - Track entry from CHS reg */ + ch = (uptr->CHS >> 8) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense CHS b2 unit=%02x 3 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + /* byte 3 - Sector entry from CHS reg */ + ch = (uptr->CHS) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense CHS b3 unit=%02x 4 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + + /* bytes 4 - mode reg, byte 0 of SNS */ + /* skip the TCMD bit */ + ch = (uptr->SNS >> 24) & 0xfe; /* return the sense data */ + sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense unit=%02x 1 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + /* bytes 5-7 - status bytes, bytes 1-3 of SNS */ + ch = (uptr->SNS >> 16) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense unit=%02x 2 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = (uptr->SNS >> 8) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense unit=%02x 3 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + ch = (uptr->SNS) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "scsi_srv sense unit=%02x 4 %02x\n", + unit, ch); + chan_write_byte(chsa, &ch); + + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case DSK_SCK: /* Seek cylinder, track, sector 0x07 */ + /* If we are waiting on seek to finish, check if there yet. */ + if (uptr->CMD & DSK_SEEKING) { + /* see if on cylinder yet */ + if (uptr->STAR == uptr->CHS) { + /* we are on cylinder, seek is done */ + sim_debug(DEBUG_CMD, dptr, "scsi_srv seek on sector unit=%02x %06x %06x\n", + unit, uptr->STAR, uptr->CHS); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + /* we have already seeked to the required sector */ + /* we do not need to seek again, so move on */ + chan_end(chsa, SNS_DEVEND|SNS_CHNEND); + return SCPE_OK; + break; + } else { + /* we have wasted enough time, we there */ + /* we are on cylinder, seek is done */ + sim_debug(DEBUG_CMD, dptr, "scsi_srv seek over on cylinder unit=%02x %04x %04x\n", + unit, uptr->STAR, uptr->CHS); + uptr->CHS = uptr->STAR; /* we are there */ +#ifdef FAST_FOR_UTX + sim_activate(uptr, 20); /* start things off */ +#else + sim_activate(uptr, 40); +#endif + break; + } + } + + /* not seeking, so start a new seek */ + /* Read in 1-4 character seek code */ + for (i = 0; i < 4; i++) { + if (chan_read_byte(chsa, &buf[i])) { + if (i == 0) { + sim_debug(DEBUG_DETAIL, dptr, + "scsi_srv seek error unit=%02x star %02x %02x %02x %02x\n", + unit, buf[0], buf[1], buf[2], buf[3]); + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + /* done reading, see how many we read */ + if (i == 1) { + /* UTX wants to set seek STAR to zero */ + buf[0] = buf[1] = buf[2] = buf[3] = 0; + break; + } + /* just read the next byte */ + } + } + /* else the cyl, trk, and sect are ready to update */ + sim_debug(DEBUG_CMD, dptr, + "scsi_srv STAR unit=%02x star %02x %02x %02x %02x\n", + unit, buf[0], buf[1], buf[2], buf[3]); + + sim_debug(DEBUG_DETAIL, dptr, + "scsi_srv seek unit=%02x star %02x%02x%02x%02x\n", + unit, buf[0], buf[1], buf[2], buf[3]); + + /* save STAR (target sector) data in STAR */ + uptr->STAR = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]); + + sim_debug(DEBUG_DETAIL, dptr, + "scsi_srv SEEK %08x sector %06x (%d) unit=%02x\n", + uptr->CMD, uptr->STAR, uptr->STAR, unit); + + /* Check if seek valid */ + if (uptr->STAR >= CAP(type)) { + sim_debug(DEBUG_CMD, dptr, + "scsi_srv seek ERROR sector %06x unit=%02x\n", + uptr->STAR, unit); + + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK; /* set error status */ + + /* we have an error, tell user */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); /* end command */ + break; + } + + /* calc the new sector address of data */ + /* calculate file position in bytes of requested sector */ + /* file offset in bytes */ + tstart = uptr->STAR * SSB(type); + + sim_debug(DEBUG_DETAIL, dptr, + "scsi_srv seek start %04x sector %06x\n", + tstart, uptr->STAR); + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* seek r/w sec */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_DETAIL, dptr, "scsi_srv Error on seek to %08x\n", tstart); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + + /* Check if already on correct cylinder */ + /* if not, do a delay to slow things down */ + if (uptr->STAR != uptr->CHS) { + /* Do a fake seek to kill time */ + uptr->CMD |= DSK_SEEKING; /* show we are seeking */ + sim_debug(DEBUG_DETAIL, dptr, + "scsi_srv seeking unit=%02x to sector %06x\n", + unit, uptr->STAR); +#ifdef FAST_FOR_UTX + /* making this value 40 or so create volume mount error on boot */ + sim_activate(uptr, 20); /* start things off */ +#else + sim_activate(uptr, 40); +#endif + } else { + /* we are on cylinder/track/sector, so go on */ + sim_debug(DEBUG_DETAIL, dptr, + "scsi_srv calc sect addr seek start %08x sector %06x\n", + tstart, uptr->STAR); + uptr->CHS = uptr->STAR; /* set new sector position */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_DEVEND|SNS_CHNEND); + } + return SCPE_OK; + + case DSK_XEZ: /* Rezero & Read IPL record */ + + sim_debug(DEBUG_CMD, dptr, "RD REZERO IPL unit=%02x seek 0\n", unit); + /* Do a seek to 0 */ + uptr->STAR = 0; /* set STAR to 0, 0, 0 */ + uptr->CHS = 0; /* set current CHS to 0, 0, 0 */ + uptr->CMD &= LMASK; /* remove old cmd */ + uptr->CMD |= DSK_SCK; /* show as seek command */ + tstart = 0; /* byte offset is 0 */ + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "scsi_srv Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + /* we are on cylinder/track/sector zero, so go on */ + sim_debug(DEBUG_DETAIL, dptr, "scsi_srv done seek trk 0\n"); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_DEVEND|SNS_CHNEND); + return SCPE_OK; + break; + + case DSK_LMR: + sim_debug(DEBUG_CMD, dptr, "Load Mode Reg unit=%02x\n", unit); + /* Read in 1 character of mode data */ + if (chan_read_byte(chsa, &buf[0])) { + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + sim_debug(DEBUG_CMD, dptr, "Load Mode Reg unit=%02x old %x new %x\n", + unit, (uptr->SNS)&0xff, buf[0]); + uptr->CMD &= LMASK; /* remove old cmd */ + /* leave the TCMD bit */ + uptr->SNS &= (MASK24 | SNS_TCMD); /* clear old mode data */ + /* do not change TCMD bit */ + uptr->SNS |= ((buf[0]&0xfe) << 24); /* save mode value */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + + case DSK_RD: /* 0x02 Read Data */ + if (uptr->SNS & SNS_TCMD) { + /* we need to process a read TCMD data */ + int cnt = scsi_buf[bufnum][unit][4]; /* byte count of status to send */ + uint32 cyl = CYL(type); /* number of cylinders */ + uint32 spt = SPT(type); /* sectors per track */ + uint32 ssb = SSB(type); /* sector size in bytes */ + int bcnt; + + /* cnt has # bytes to return (0xf0) */ + uint8 pagecode = scsi_buf[bufnum][unit][2] & 0x3f; /* get page code */ + uint8 pagecont = (scsi_buf[bufnum][unit][2] & 0xc0) >> 6; /* get page control */ + + ch = scsi_buf[bufnum][unit][0]; /* return TCMD cmd */ + uptr->SNS &= ~SNS_TCMD; /* show not presessing TCMD cmd chain */ + sim_debug(DEBUG_CMD, dptr, + "scsi_srv processing TCMD read cmd %02x, chsa %04x tcma %06x cnt %04x\n", + ch, chsa, chp->ccw_addr, chp->ccw_count); + + switch (ch) { + case 0x25: /* read capacity */ + sim_debug(DEBUG_CMD, dptr, + "scsi_srv TCMD read call DSK_RCAP cmd %02x, chsa %04x tcma %06x cnt %04x\n", + ch, chsa, chp->ccw_addr, chp->ccw_count); + goto read_cap; /* use IOCL cmd processing */ + break; + + case 0x28: /* read 10 byte cmd */ + /* blk is in bytes 2-5, sects is in 7-8 */ + sim_debug(DEBUG_CMD, dptr, + "scsi_srv TCMD call read cmd %02x, chsa %04x tcma %06x cnt %04x\n", + ch, chsa, chp->ccw_addr, chp->ccw_count); + tstart = (scsi_buf[bufnum][unit][2] << 24) | /* get sector address */ + (scsi_buf[bufnum][unit][3] << 16) | + (scsi_buf[bufnum][unit][4] << 8) | + (scsi_buf[bufnum][unit][5]); + bcnt = (scsi_buf[bufnum][unit][8] << 8) | /* get transfer block count */ + (scsi_buf[bufnum][unit][9]); + sim_debug(DEBUG_CMD, dptr, + "scsi_srv TCMD call read DATA cmd %02x, chsa %04x buf addr %08x SA %08x cnt %02x\n", + ch, chsa, chp->ccw_addr, tstart, bcnt); + + /* convert sect address to chs value */ + uptr->CHS = tstart; + /* get byte address for seek */ + tstart = tstart * SSB(type); + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "scsi_srv read TCMD Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + /* we are on cylinder/track/sector, get data to write */ + sim_debug(DEBUG_DETAIL, dptr, "scsi_srv TCMD done seek\n"); + tstart = uptr->CHS; /* get sector offset back */ + goto doread; /* use IOCL cmd processing */ + break; + + case 0x1a: /* mode sense */ + for (i=0; i> 6; /* get page control */ + /* pagecont = 0 return current values */ + /* pagecont = 1 return changable values */ + /* pagecont = 2 return default values */ + /* pagecont = 3 return saved values */ + sim_debug(DEBUG_CMD, dptr, + "scsi_srv TCMD read call MOD SEN cmd %02x pgcd %02x pgco %1x chsa %04x tcma %06x cnt %04x\n", + ch, pagecode, pagecont, chsa, chp->ccw_addr, chp->ccw_count); + buf[0] = 0xf0; /* page length */ + if (pagecode == 3) { + buf[2] = 0; /* 0x80 if write protected */ + buf[3] = 0; /* block descriptor length */ +// buf[4] = 0x83; /* savable and page type 3 */ + buf[4] = 0x03; /* not savable and page type 3 */ + buf[5] = 22; /* 22 data bytes follow */ + buf[6] = 0; /* tracks per zone ub */ + buf[7] = 1; /* tracks per zone lb */ + buf[8] = 0; /* alt sec per zone ub */ + buf[9] = 1; /* alt sec per zone lb */ + buf[10] = 0; /* alt trks per zone ub */ + buf[11] = 0; /* alt trks per zone lb */ + buf[12] = 0; /* alt trks per unit ub */ + buf[13] = 0; /* alt trks per unit lb */ + buf[14] = (uint8)((spt & 0xff00) >> 8); /* Sect/track */ + buf[15] = (uint8)((spt & 0x00ff)); /* Sect/track */ + buf[16] = (uint8)((ssb & 0xff00) >> 8); /* Sect size */ + buf[17] = (uint8)((ssb & 0x00ff)); /* Sect size */ + buf[18] = 0; /* interleave ub */ + buf[19] = 0; /* interleave lb */ + buf[20] = 0; /* track skew factor ub */ + buf[21] = 0; /* track skew factor lb */ + buf[22] = 0; /* cyl skew factor ub */ + buf[23] = 0; /* cyl skew factor lb */ +// buf[24] |= 0x80; /* soft sectoring */ + buf[24] |= 0x40; /* hard sectoring */ +// buf[24] |= 0x20; /* drive removable */ +// buf[24] |= 0x08; /* inhibit save */ + goto merge; /* go output data and return */ + } + if (pagecode == 4) { + /* num cyl */ + buf[2] = 0; /* 0x80 if write protected */ + buf[3] = 0; /* block descriptor length */ +// buf[4] = 0x84; /* savable and page type 4 */ + buf[4] = 0x04; /* not savable and page type 4 */ + buf[5] = 18; /* 18 data bytes follow */ + buf[6] = (uint8)((cyl & 0xff0000) >> 16); + buf[7] = (uint8)((cyl & 0x00ff00) >> 8); + buf[8] = (uint8)((cyl & 0x0000ff)); + buf[9] = (uint8)HDS(type); /* # of heads */ + goto merge; /* go output data and return */ + } + + case 0x12: /* inquiry */ + /* size is 0x24 = 36 bytes */ + /* ssize has sector size in bytes */ + for (i=0; iCMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + } + sim_debug(DEBUG_DETAIL, dptr, + "scsi_srv TCMD inq read data chsa=%02x data %02x%02x%02x%02x %02x%02x%02x%02x\n", + chsa, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + sim_debug(DEBUG_DETAIL, dptr, + "scsi_srv TCMD inq read data chsa=%02x data %02x%02x%02x%02x %02x%02x%02x%02x\n", + chsa, buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); + sim_debug(DEBUG_DETAIL, dptr, + "scsi_srv TCMD inq read data chsa=%02x data %02x%02x%02x%02x %02x%02x%02x%02x\n", + chsa, buf[16], buf[17], buf[18], buf[19], buf[20], buf[21], buf[22], buf[23]); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + break; + + case 0x00: /* test unit ready */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, "scsi_srv test unit ready cmd %02x unit %02x\n", ch, unit); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + break; + + default: /* bad or unsupported scsi command */ + sim_debug(DEBUG_CMD, dptr, "invalid scsi read command %02x unit %02x\n", ch, unit); + uptr->SNS |= SNS_CMDREJ; + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + } + +doread: + /* normal disk read starts here */ + /* tstart has start of sector address in bytes */ + if ((uptr->CMD & DSK_READING) == 0) { /* see if we are reading data */ + uptr->CMD |= DSK_READING; /* read from disk starting */ + sim_debug(DEBUG_CMD, dptr, + "SCSI READ starting unit=%02x CMD %08x count %04x\n", + unit, uptr->CMD, chp->ccw_count); + } + + if (uptr->CMD & DSK_READING) { /* see if we are reading data */ + tstart = uptr->CHS; /* get sector offset */ + + sim_debug(DEBUG_CMD, dptr, + "SCSI READ reading CMD %08x chsa %04x tstart %04x buffer %06x count %04x\n", + uptr->CMD, chsa, tstart, chp->ccw_addr, chp->ccw_count); + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart*SSB(type), SEEK_SET)) != 0) { /* seek r/w sec */ + sim_debug(DEBUG_DETAIL, dptr, + "scsi_srv READ, Error on seek to %08x\n", tstart*SSB(type)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + + /* read in a sector of data from disk */ + if ((len=sim_fread(buf, 1, ssize, uptr->fileref)) != ssize) { + sim_debug(DEBUG_CMD, dptr, + "Error %08x on read %04x of diskfile sector %06x\n", + len, ssize, tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + sim_debug(DEBUG_CMD, dptr, "scsi_srv after READ chsa %04x count %04x\n", + chsa, chp->ccw_count); + + /* process the sector of data */ + for (i=0; iccw_count, tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } + } + + sim_debug(DEBUG_CMD, dptr, + "SCSI READ %04x bytes leaving %4x to be read to %06x from diskfile sector %06x\n", + ssize, chp->ccw_count, chp->ccw_addr+4, tstart); + + /* see if we are done reading data */ + if (test_write_byte_end(chsa)) { + sim_debug(DEBUG_DATA, dptr, + "SCSI Read complete for read from diskfile sector %06x\n", + uptr->CHS); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + break; + } + + /* tstart has file offset in sectors */ + tstart++; /* bump to next sector */ + uptr->CHS = tstart; /* new position */ + /* see if over end of disk */ + if (tstart >= (uint32)CAP(type)) { + /* EOM reached, abort */ + sim_debug(DEBUG_CMD, dptr, + "SCSI Read reached EOM for read from disk @ sector %06x\n", + tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->CHS = 0; /* reset cylinder position */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + sim_debug(DEBUG_DATA, dptr, + "SCSI sector read complete, %x bytes to go from diskfile sector %06x\n", + chp->ccw_count, uptr->CHS); +#ifdef FAST_FOR_UTX +//zz sim_activate(uptr, 10); /* start things off */ + sim_activate(uptr, 15); /* start things off */ +#else + sim_activate(uptr, 10); /* wait to read next sector */ +//BAD4MPX sim_activate(uptr, 40); /* wait to read next sector */ +#endif + break; + } + break; + + case DSK_WD: /* Write Data */ + if (uptr->SNS & SNS_TCMD) { + /* we need to process a write TCMD data */ +//? int cnt = scsi_buf[bufnum][unit][4]; /* byte count of status to read */ + /* cnt has # bytes to read */ + int cnt = chp->ccw_count; /* byte count of status to read */ + + ch = scsi_buf[bufnum][unit][0]; /* return TCMD cmd */ + uptr->SNS &= ~SNS_TCMD; /* show not presessing TCMD cmd chain */ + sim_debug(DEBUG_CMD, dptr, + "scsi_srv processing TCMD write cmd %02x, chsa %04x tcma %06x cnt %04x\n", + ch, chsa, chp->ccw_addr, chp->ccw_count); + + switch (ch) { + + case 0x4: /* write 6 byte cmd, format disk */ + if (scsi_buf[bufnum][unit][2] == 10) { + if ((--scsi_buf[bufnum][unit][3]) > 0) { + uptr->SNS |= SNS_TCMD; /* show still presessing TCMD cmd chain */ + sim_activate(uptr, 200000); /* wait a while */ + return SCPE_OK; + break; + } + scsi_buf[bufnum][unit][2] = 0; /* show done */ + } + /* MPX cmd data 04 18 00 00 00 00 */ + /* Format unit */ + len = 0; + for (i=0; iCMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "SCSI Read %04x bytes from MPX buffer\n", len); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } + ch = 0; /* finish out the sector with zero */ + len++; /* show we have no more data to write */ + } + buf2[i] = ch; /* save the char */ + } + sim_debug(DEBUG_CMD, dptr, "SCSI CMD 4 %04x bytes read from MPX buffer\n", cnt); + sim_debug(DEBUG_CMD, dptr, + "SCSI Disk format buf2 %02x%02x%02x%02x\n", buf2[0], buf2[1], buf2[2], buf2[3]); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + break; + + case 0x15: /* write 6 byte cmd, format disk */ + /* MODE select */ + len = 0; + for (i=0; iCMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "SCSI Read %04x bytes from MPX buffer\n", len); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } + ch = 0; /* finish out the sector with zero */ + len++; /* show we have no more data to write */ + } + buf2[i] = ch; /* save the char */ + } + sim_debug(DEBUG_CMD, dptr, "SCSI Format %04x bytes to status buffer\n", cnt); + sim_debug(DEBUG_CMD, dptr, + "SCSI Disk format buf2 %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n", + buf2[0], buf2[1], buf2[2], buf2[3], buf2[4], buf2[5], buf2[6], buf2[7], + buf2[8], buf2[9], buf2[10], buf2[11], buf2[12], buf2[13], buf2[14], buf2[15]); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + break; + + case 0x2a: /* write 10 byte cmd */ + tstart = (scsi_buf[bufnum][unit][2] << 24) | /* get sector address */ + (scsi_buf[bufnum][unit][3] << 16) | + (scsi_buf[bufnum][unit][4] << 8) | + (scsi_buf[bufnum][unit][5]); + sim_debug(DEBUG_CMD, dptr, + "scsi_srv TCMD call write DATA cmd %02x, chsa %04x addr %08x data %08x %08x\n", + ch, chsa, chp->ccw_addr, RMW(chp->ccw_addr), RMW(chp->ccw_addr+4)); + + /* convert sect address to chs value */ + uptr->CHS = tstart; /* set seek sector address */ + /* get byte address for seek */ + tstart = tstart * SSB(type); + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart, SEEK_SET)) != 0) { /* do seek */ + sim_debug(DEBUG_EXP, dptr, "scsi_srv TCMD Error on seek to %04x\n", tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + /* we are on cylinder/track/sector, get data to write */ + sim_debug(DEBUG_DETAIL, dptr, "scsi_srv TCMD done seek\n"); + tstart = uptr->CHS; /* get sector offset */ + goto dowrite; + break; + + default: /* bad or unsupported scsi command */ + sim_debug(DEBUG_CMD, dptr, "invalid scsi write command %02x unit %02x\n", ch, unit); + uptr->SNS |= SNS_CMDREJ; + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + break; + } +dowrite: + /* tstart has file offset in sectors */ + if ((uptr->CMD & DSK_WRITING) == 0) { /* see if we are writing data */ + uptr->CMD |= DSK_WRITING; /* write to disk starting */ + sim_debug(DEBUG_CMD, dptr, + "SCSI WRITE starting unit=%02x CMD %02x write %4x from %06x to sector %06x\n", + unit, uptr->CMD, chp->ccw_count, chp->ccw_addr, uptr->CHS); + } + if (uptr->CMD & DSK_WRITING) { /* see if we are writing data */ + tstart = uptr->CHS; /* get sector offset */ + + /* just seek to the location where we will r/w data */ + if ((sim_fseek(uptr->fileref, tstart*SSB(type), SEEK_SET)) != 0) { /* seek r/w sec */ + sim_debug(DEBUG_EXP, dptr, + "scsi_srv WRITE, Error on seek to %08x\n", tstart*SSB(type)); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + + /* process the next sector of data */ + len = 0; /* used here as a flag for short read */ + for (i=0; iCMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "SCSI Wrote %04x bytes to diskfile sector %06x\n", + ssize, tstart); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } + ch = 0; /* finish out the sector with zero */ + len++; /* show we have no more data to write */ + } + buf2[i] = ch; /* save the char */ + } + + /* write the sector to disk */ + if ((i=sim_fwrite(buf2, 1, ssize, uptr->fileref)) != ssize) { + sim_debug(DEBUG_CMD, dptr, + "Error %08x on write %04x bytes to diskfile sector %06x\n", + i, ssize, tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (len != 0) { /* see if done with write command */ + sim_debug(DEBUG_DATA, dptr, + "SCSI WroteB %04x bytes to diskfile sector %06x\n", + ssize, tstart); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we done */ + break; + } + sim_debug(DEBUG_CMD, dptr, + "SCSI WR to sec end %04x bytes end %04x to diskfile sector %06x\n", + len, ssize, tstart); + + /* tstart has file offset in sectors */ + tstart++; /* bump to next sector */ + uptr->CHS = tstart; /* save new sector */ + /* see if over end of disk */ + if (tstart >= (uint32)CAP(type)) { + /* EOM reached, abort */ + sim_debug(DEBUG_CMD, dptr, + "SCSI Write reached EOM for write to disk @ sector %06x\n", + uptr->CHS); + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->CHS = 0; /* reset cylinder position */ + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } +#ifdef FAST_FOR_UTX +//zz sim_activate(uptr, 10); /* start things off */ + sim_activate(uptr, 20); /* start things off */ +#else + sim_activate(uptr, 10); /* keep writing */ +//BAD4MPX sim_activate(uptr, 40); /* keep writing */ +#endif + break; + } + break; + + case DSK_RCAP: /* Read Capacity 0x53 */ + /* return 8 bytes */ + /* wd 1 disk size in sectors */ + /* wd 2 is sector size in bytes */ + /* cap has disk capacity */ +read_cap: /* merge point from TCMD processing */ +#if 0 + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ +#endif + for (i=0; i<4; i++) { + /* I think they want cap-1, not cap?????????? */ + /* verified that MPX wants cap-1, else J.VFMT aborts */ + ch = ((cap-1) >> ((3-i)*8)) & 0xff; /* use cap-1 */ + if (chan_write_byte(chsa, &ch)) { /* write byte to memory */ + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + } + /* ssize has sector size in bytes */ + for (i=0; i<4; i++) { + ch = (((uint32)ssize) >> ((3-i)*8)) & 0xff; + if (chan_write_byte(chsa, &ch)) { + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + } + + /* command is completed */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "scsi_srv cmd RCAP chsa %04x capacity %06x secsize %03x completed\n", + chsa, cap, ssize); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + break; + + /* Transfer Command Packet (specifies CDB to send) */ + /* address points to CDB */ + case DSK_TCMD: /* Transfer command packet 0xD3 */ + { + uint32 mema; /* memory address */ + int32 i; + + uptr->SNS &= ~SNS_TCMD; /* show not presessing TCMD cmd chain */ + len = chp->ccw_count; /* INCH command count */ + mema = chp->ccw_addr; /* get inch or buffer addr */ + sim_debug(DEBUG_CMD, dptr, + "scsi_srv starting TCMD cmd, chsa %04x tcma %06x cnt %04x\n", + chsa, chp->ccw_addr, chp->ccw_count); + +#if 0 + cpu_dev.dctrl |= DEBUG_INST; /* start instruction trace */ +#endif + /* mema has IOCD word 1 contents. */ + /* len has the byte count from IOCD wd2 */ + len = chp->ccw_count; /* TCMD command count */ + + for (i=0; i < len; i++) { + if (chan_read_byte(chsa, &buf[i])) { + /* we have error, bail out */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS |= SNS_CMDREJ|SNS_EQUCHK; + chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + break; + } + } + sim_debug(DEBUG_DETAIL, dptr, + "scsi_srv TCMD data chsa=%02x data %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + chsa, buf[0], buf[1], buf[2], buf[3], buf[4], + buf[5], buf[6], buf[7], buf[8], buf[9]); + + /* save the CMD packet */ + for (i=0; i < len; i++) { + scsi_buf[bufnum][unit][i] = buf[i]; /* save the cmd */ + } + scsi_pcmd[bufnum][unit] = buf[0]; /* save the cmd */ + + /* if this a disk format, do a big wait */ + if ((buf[0] == 4) && (buf[1] == 0x18)) { + scsi_buf[bufnum][unit][2] = 10; /* show Processing Format cmd */ + scsi_buf[bufnum][unit][3] = 10; /* show Processing cmd */ + } + + /* see if just test unit ready */ + if ((buf[0] == 0) && (len == 6)) + uptr->SNS &= ~SNS_TCMD; /* clear TCMD flag */ + else + uptr->SNS |= SNS_TCMD; /* show Processing CMD cmd chain */ + + /* command is completed */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + sim_debug(DEBUG_CMD, dptr, + "scsi_srv cmd TCMD chsa %04x addr %06x count %04x completed\n", + chsa, mema, chp->ccw_count); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */ + } + return SCPE_OK; + break; + + default: + sim_debug(DEBUG_EXP, dptr, "invalid command %02x unit %02x\n", cmd, unit); + uptr->SNS |= SNS_CMDREJ; + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + return SNS_CHNEND|STATUS_PCHK; + break; + } + sim_debug(DEBUG_CMD, dptr, + "scsi_srv done cmd %02x chsa %04x count %04x\n", cmd, chsa, chp->ccw_count); + return SCPE_OK; +} + +/* initialize the disk */ +void scsi_ini(UNIT *uptr, t_bool f) +{ + DEVICE *dptr = get_dev(uptr); + int i = GET_TYPE(uptr->flags); + + /* start out at sector 0 */ + uptr->CHS = 0; /* set CHS to cyl/hd/sec = 0 */ + uptr->STAR = 0; /* set STAR to cyl/hd/sec = 0 */ + uptr->CMD &= LMASK; /* remove old status bits & cmd */ + uptr->SNS = 0; /* clear any status */ + /* total sectors on disk */ + uptr->capac = CAP(i); /* disk size in sectors */ + sim_cancel(uptr); /* stop any timers */ + + sim_debug(DEBUG_EXP, &sba_dev, "SCSI init device %s on unit SBA%04x cap %x %d\n", + dptr->name, GET_UADDR(uptr->CMD), uptr->capac, uptr->capac); +} + +/* handle rschnlio cmds for scsi */ +t_stat scsi_rschnlio(UNIT *uptr) { + DEVICE *dptr = get_dev(uptr); + uint16 chsa = GET_UADDR(uptr->CMD); + int cmd = uptr->CMD & DSK_CMDMSK; + + sim_debug(DEBUG_EXP, dptr, + "scsi_rschnl chsa %04x cmd = %02x\n", chsa, cmd); + scsi_ini(uptr, 0); /* reset the unit */ + return SCPE_OK; +} + +t_stat scsi_reset(DEVICE *dptr) +{ + UNIT *uptr = dptr->units; + uint16 chsa = GET_UADDR(uptr->CMD); + + /* add reset code here */ + sim_debug(DEBUG_EXP, dptr, + "scsi_reset chsa %04x\n", chsa); + return SCPE_OK; +} + +/* create the disk file for the specified device */ +int scsi_format(UNIT *uptr) { + int type = GET_TYPE(uptr->flags); + DEVICE *dptr = get_dev(uptr); + int32 ssize = scsi_type[type].ssiz * 4; /* disk sector size in bytes */ + uint32 tsize = scsi_type[type].spt; /* get track size in sectors */ + uint32 csize = scsi_type[type].nhds * tsize; /* get cylinder size in sectors */ + uint32 cyl = scsi_type[type].cyl; /* get # cyl */ + uint32 cap = scsi_type[type].cyl * csize; /* disk capacity in sectors */ + uint32 cylv = cyl; /* number of cylinders */ + uint8 *buff; + int i; + t_stat oldsw = sim_switches; /* save switches */ + + /* last sector address of disk (cyl * hds * spt) - 1 */ + uint32 laddr = CAP(type) - 1; /* last sector of disk */ + + /* last track address of disk (cyl * hds * spt) - spt */ + uint32 ltaddr = CAP(type)-SPT(type); /* last sector of disk */ + + /* get sector address of vendor defect table VDT */ + /* put data = 0xf0000000 0xf4000000 */ + int32 vaddr = (CYL(type)-4) * SPC(type) + (HDS(type)-1) * SPT(type); + + /* get sector address of utx diag map (DMAP) track 0 pointer */ + /* put data = 0xf0000000 + (cyl-1), 0x8a000000 + daddr, */ + /* 0x9a000000 + (cyl-1), 0xf4000008 */ + int32 daddr = (CYL(type)-4) * SPC(type) + (HDS(type)-2) * SPT(type); + + /* get sector address of utx flaw data (1 track long) */ + /* set trace data to zero */ + int32 faddr = (CYL(type)-4) * SPC(type) + (HDS(type)-3) * SPT(type); + + /* get sector address of utx flaw map sec 1 pointer */ + /* use this address for sec 1 label pointer */ + int32 uaddr = (CYL(type)-4) * SPC(type) + (HDS(type)-4) * SPT(type); + + /* last user block available */ + int32 luaddr = (CYL(type)-4) * SPC(type); + + /* make up a UMAP with the partiton data for 9346 disk */ + uint32 umap[256] = + { + /* try to makeup a utx dmap */ + 0x4e554d50,(cap-1),(uint32)(luaddr-1),0,0,0,0,0xe10, + 0,0x5320,0,0x4e60,0x46,(uint32)luaddr,0,0xd360, + 0x88,0x186b0,0x13a,0xd100,0x283,0,0,0, + 0,0x22c2813e,0,0x06020000,0xf4,0,0x431b1c,0, + }; + + /* vendor flaw map in vaddr */ + uint32 vmap[2] = {0xf0000004, 0xf4000000}; + + /* defect map */ + uint32 dmap[4] = {0xf0000000 | (cap-1), 0x8a000000 | daddr, + 0x9a000000 | (cap-1), 0xf4000000}; + + /* utx flaw map */ + uint32 fmap[4] = {0xf0000000 | (cap-1), 0x8a000000 | daddr, + 0x9a000000 | ltaddr, 0xf4000000}; + + /* see if -i or -n specified on attach command */ + if (!(sim_switches & SWMASK('N')) && !(sim_switches & SWMASK('I'))) { + sim_switches = 0; /* simh tests 'N' & 'Y' switches */ + /* see if user wants to initialize the disk */ + if (!get_yn("Initialize disk? [Y] ", TRUE)) { + sim_switches = oldsw; + return 1; + } + sim_switches = oldsw; /* restore switches */ + } + +#if 0 + /* see if user wants to initialize the disk */ + if (!get_yn("Initialize disk? [Y] ", TRUE)) { + return 1; + } +#endif + + /* VDT 249264 (819/18/0) 0x3cdb0 for 9346 - 823/19/16 vaddr */ + /* MDT 249248 (819/17/0) 0x3cda0 for 9346 - 823/19/16 daddr */ + /* DMAP 249232 (819/16/0) 0x3cd90 for 9346 - 823/19/16 faddr */ + /* UMAP 249216 (819/15/0) 0x3cd80 for 9346 - 823/19/16 uaddr */ + + /* seek to sector 0 */ + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + fprintf (stderr, "Error on seek to 0\r\n"); + } + + /* get buffer for track data */ + if ((buff = (uint8 *)calloc(csize*ssize, sizeof(uint8))) == 0) { + detach_unit(uptr); + return SCPE_ARG; + } + sim_debug(DEBUG_CMD, dptr, + "Creating disk file of trk size %04x bytes, capacity %d\n", + tsize*ssize, cap*ssize); + + /* write zeros to each track of the disk */ + for (cyl = 0; cyl < cylv; cyl++) { + if ((sim_fwrite(buff, 1, csize*ssize, uptr->fileref)) != csize*ssize) { + sim_debug(DEBUG_CMD, dptr, + "Error on write to diskfile cyl %04x\n", cyl); + free(buff); /* free cylinder buffer */ + buff = 0; + return 1; + } + if ((cyl % 100) == 0) + fputc('.', stderr); + } + fputc('\r', stderr); + fputc('\n', stderr); + free(buff); /* free cylinder buffer */ + buff = 0; + + /* byte swap the buffers for dmap and umap */ + for (i=0; i<2; i++) { + vmap[i] = (((vmap[i] & 0xff) << 24) | ((vmap[i] & 0xff00) << 8) | + ((vmap[i] & 0xff0000) >> 8) | ((vmap[i] >> 24) & 0xff)); + } + for (i=0; i<4; i++) { + dmap[i] = (((dmap[i] & 0xff) << 24) | ((dmap[i] & 0xff00) << 8) | + ((dmap[i] & 0xff0000) >> 8) | ((dmap[i] >> 24) & 0xff)); + } + for (i=0; i<4; i++) { + fmap[i] = (((fmap[i] & 0xff) << 24) | ((fmap[i] & 0xff00) << 8) | + ((fmap[i] & 0xff0000) >> 8) | ((fmap[i] >> 24) & 0xff)); + } + for (i=0; i<256; i++) { + umap[i] = (((umap[i] & 0xff) << 24) | ((umap[i] & 0xff00) << 8) | + ((umap[i] & 0xff0000) >> 8) | ((umap[i] >> 24) & 0xff)); + } + + /* now seek to end of disk and write the dmap data */ + /* setup dmap pointed to by track label 0 wd[3] = (cyl-4) * spt + (spt - 1) */ + + /* write dmap data to last sector on disk */ + if ((sim_fseek(uptr->fileref, laddr*ssize, SEEK_SET)) != 0) { /* seek last sector */ + sim_debug(DEBUG_CMD, dptr, + "Error on last sector seek to sect %06x offset %06x\n", + cap-1, (cap-1)*ssize); + return 1; + } + if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) { + sim_debug(DEBUG_CMD, dptr, + "Error writing DMAP to sect %06x offset %06x\n", + cap-1, (cap-1)*ssize); + return 1; + } + + /* seek to vendor label area VMAP */ + if ((sim_fseek(uptr->fileref, vaddr*ssize, SEEK_SET)) != 0) { /* seek VMAP */ + sim_debug(DEBUG_CMD, dptr, + "Error on vendor map seek to sect %06x offset %06x\n", + vaddr, vaddr*ssize); + return 1; + } + if ((sim_fwrite((char *)&vmap, sizeof(uint32), 2, uptr->fileref)) != 2) { + sim_debug(DEBUG_CMD, dptr, + "Error writing VMAP to sect %06x offset %06x\n", + vaddr, vaddr*ssize); + return 1; + } + + /* write DMAP to daddr that is the address in trk 0 label */ + if ((sim_fseek(uptr->fileref, daddr*ssize, SEEK_SET)) != 0) { /* seek DMAP */ + sim_debug(DEBUG_CMD, dptr, + "Error on diag map seek to sect %06x offset %06x\n", + daddr, daddr*ssize); + return 1; + } + if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) { + sim_debug(DEBUG_CMD, dptr, + "Error writing DMAP to sect %06x offset %06x\n", + daddr, daddr*ssize); + return 1; + } + + /* write dummy DMAP to faddr */ + if ((sim_fseek(uptr->fileref, faddr*ssize, SEEK_SET)) != 0) { /* seek DMAP */ + sim_debug(DEBUG_CMD, dptr, + "Error on media flaw map seek to sect %06x offset %06x\n", + faddr, faddr*ssize); + return 1; + } + if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) { + sim_debug(DEBUG_CMD, dptr, + "Error writing flaw map to sect %06x offset %06x\n", + faddr, faddr*ssize); + return 1; + } + + /* write UTX umap to uaddr */ + if ((sim_fseek(uptr->fileref, uaddr*ssize, SEEK_SET)) != 0) { /* seek UMAP */ + sim_debug(DEBUG_CMD, dptr, + "Error on umap seek to sect %06x offset %06x\n", + uaddr, uaddr*ssize); + return 1; + } + if ((sim_fwrite((char *)&umap, sizeof(uint32), 256, uptr->fileref)) != 256) { + sim_debug(DEBUG_CMD, dptr, + "Error writing UMAP to sect %06x offsewt %06x\n", + uaddr, uaddr*ssize); + return 1; + } + + printf("SCSI Disk %s has %x (%d) cyl, %x (%d) hds, %x (%d) sec\r\n", + scsi_type[type].name, CYL(type), CYL(type), HDS(type), HDS(type), + SPT(type), SPT(type)); + printf("writing to vmap sec %x (%d) bytes %x (%d)\n", + vaddr, vaddr, (vaddr)*ssize, (vaddr)*ssize); + printf("writing to flaw map sec %x (%d) bytes %x (%d)\n", + faddr, faddr, (faddr)*ssize, (faddr)*ssize); + printf("writing dmap to %x %d %x %d dmap to %x %d %x %d\n", + cap-1, cap-1, (cap-1)*ssize, (cap-1)*ssize, + daddr, daddr, daddr*ssize, daddr*ssize); + printf("writing to umap sec %x (%d) bytes %x (%d)\n", + uaddr, uaddr, (uaddr)*ssize, (uaddr)*ssize); + + /* seek home again */ + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + fprintf (stderr, "Error on seek to 0\r\n"); + return 1; + } + return 0; +} + +/* attach the selected file to the disk */ +t_stat scsi_attach(UNIT *uptr, CONST char *file) { + uint16 chsa = GET_UADDR(uptr->CMD); + int type = GET_TYPE(uptr->flags); + DEVICE *dptr = get_dev(uptr); + DIB *dibp = 0; + t_stat r, s; + uint32 ssize; /* sector size in bytes */ + uint32 info, good,zmap = 0x5a4d4150; /* ZMAP */ + uint8 buff[1024]; + int i, j; + + /* last sector address of disk (cyl * hds * spt) - 1 */ + uint32 laddr = CAP(type) - 1; /* last sector of disk */ + /* defect map */ + uint32 dmap[4] = {0xf0000000 | (CAP(type)-1), 0x8a000000, + 0x9a000000 | (CAP(type)-1), 0xf4000000}; + + for (i=0; i<4; i++) { /* byte swap data for last sector */ + dmap[i] = (((dmap[i] & 0xff) << 24) | ((dmap[i] & 0xff00) << 8) | + ((dmap[i] & 0xff0000) >> 8) | ((dmap[i] >> 24) & 0xff)); + } + + uptr->SNS = 0; /* clear any status */ + + /* see if valid disk entry */ + if (scsi_type[type].name == 0) { /* does the assigned disk have a name */ + detach_unit(uptr); /* no, reject */ + return SCPE_FMT; /* error */ + } + + if (dptr->flags & DEV_DIS) { + fprintf(sim_deb, + "ERROR===ERROR\nSCSI Disk device %s disabled on system, aborting\r\n", + dptr->name); + printf("ERROR===ERROR\nSCSI Disk device %s disabled on system, aborting\r\n", + dptr->name); + return SCPE_UDIS; /* device disabled */ + } + + /* have simulator attach the file to the unit */ + if ((r = attach_unit(uptr, file)) != SCPE_OK) + return r; + + uptr->capac = CAP(type); /* disk capacity in sectors */ + ssize = SSB(type); /* get sector size in bytes */ + for (i=0; i<(int)ssize; i++) + buff[i] = 0; /* zero the buffer */ + + sim_debug(DEBUG_CMD, dptr, "SCSI Disk %s %04x cyl %d hds %d sec %d ssiz %d capacity %d\n", + scsi_type[type].name, chsa, scsi_type[type].cyl, scsi_type[type].nhds, + scsi_type[type].spt, ssize, uptr->capac); /* disk capacity */ + printf("SCSI Disk %s %04x cyl %d hds %d sec %d ssiz %d capacity %d\r\n", + scsi_type[type].name, chsa, scsi_type[type].cyl, scsi_type[type].nhds, + scsi_type[type].spt, ssize, uptr->capac); /* disk capacity */ + + /* see if -i or -n specified on attach command */ + if ((sim_switches & SWMASK('N')) || (sim_switches & SWMASK('I'))) { + goto fmt; /* user wants new disk */ + } + + /* seek to end of disk */ + if ((sim_fseek(uptr->fileref, 0, SEEK_END)) != 0) { + sim_debug(DEBUG_CMD, dptr, "SCSI Disk attach SEEK end failed\n"); + printf( "SCSI Disk attach SEEK end failed\r\n"); + goto fmt; /* not setup, go format */ + } + + s = ftell(uptr->fileref); /* get current file position */ + if (s == 0) { + sim_debug(DEBUG_CMD, dptr, "SCSI Disk attach ftell failed s=%06d\n", s); + printf("SCSI Disk attach ftell failed s=%06d\r\n", s); + goto fmt; /* not setup, go format */ + } + sim_debug(DEBUG_CMD, dptr, "SCSI Disk attach ftell value s=%06d b=%06d CAP %06d\n", s/ssize, s, CAP(type)); + printf("SCSI Disk attach ftell value s=%06d b=%06d CAP %06d\r\n", s/ssize, s, CAP(type)); + + if (((int)s/(int)ssize) < ((int)CAP(type))) { /* full sized disk? */ + j = (CAP(type) - (s/ssize)); /* get # sectors to write */ + sim_debug(DEBUG_CMD, dptr, + "SCSI Disk attach for MPX 1.X needs %04d more sectors added to disk\n", j); + printf("SSFI Disk attach for MPX 1.X needs %04d more sectors added to disk\r\n", j); + /* must be MPX 1.X disk, extend to MPX 3.X size */ + /* write sectors of zero to end of disk to fill it out */ + for (i=0; ifileref) != ssize)) { + sim_debug(DEBUG_CMD, dptr, "SCSI Disk attach fread ret = %04d\n", r); + printf("SCSI Disk attach fread ret = %04d\r\n", r); + goto fmt; /* not setup, go format */ + } + } + s = ftell(uptr->fileref); /* get current file position */ + sim_debug(DEBUG_CMD, dptr, + "SCSI Disk attach MPX 1.X file extended & sized secs %06d bytes %06d\n", s/ssize, s); + printf("SCSI Disk attach MPX 1.X file extended & sized secs %06d bytes %06d\r\n", s/ssize, s); + } + + /* seek last sector of disk */ + if ((sim_fseek(uptr->fileref, (CAP(type)-1)*ssize, SEEK_SET)) != 0) { + sim_debug(DEBUG_CMD, dptr, "SCSI Disk attach SEEK last sector failed\n"); + printf("SCSI Disk attach SEEK last sector failed\r\n"); + goto fmt; + } + + /* see if there is disk size-1 in last sector of disk, if not add it */ + if ((r = sim_fread(buff, sizeof(uint8), ssize, uptr->fileref) != ssize)) { + sim_debug(DEBUG_CMD, dptr, "SCSI Disk format fread error = %04d\n", r); + printf("SCSI Disk format fread error = %04d\r\n", r); +add_size: + if (ssize == 768) { + /* assume we have MPX 1x, and go on */ + /* write dmap data to last sector on disk for mpx 1.x */ + if ((sim_fseek(uptr->fileref, laddr*ssize, SEEK_SET)) != 0) { /* seek last sector */ + sim_debug(DEBUG_CMD, dptr, + "SCSI Error on last sector seek to sect %06d offset %06d bytes\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + printf("SCSI Error on last sector seek to sect %06d offset %06d bytes\r\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + goto fmt; + } + if ((sim_fwrite((char *)&dmap, sizeof(uint32), 4, uptr->fileref)) != 4) { + sim_debug(DEBUG_CMD, dptr, + "SCSI Error writing DMAP to sect %06x offset %06d bytes\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + printf("SCSI Error writing DMAP to sect %06x offset %06d bytes\r\n", + (CAP(type)-1), (CAP(type)-1)*ssize); + goto fmt; + } + + /* seek last sector of disk */ + if ((sim_fseek(uptr->fileref, (CAP(type))*ssize, SEEK_SET)) != 0) { + sim_debug(DEBUG_CMD, dptr, "SCSI Disk attach SEEK last sector failed\n"); + printf( "SCSI Disk attach SEEK last sector failed\r\n"); + goto fmt; + } + s = ftell(uptr->fileref); /* get current file position */ + sim_debug(DEBUG_CMD, dptr, + "SCSI Disk attach MPX file extended & sized secs %06d bytes %06d\n", s/ssize, s); + printf("SCSI Disk attach MPX file extended & sized secs %06d bytes %06d\r\n", s/ssize, s); + goto ldone; + } else { + /* error if UTX */ + detach_unit(uptr); /* if error, abort */ + return SCPE_FMT; /* error */ + } + } else { + /* if not disk size, go add it in for MPX, error if UTX */ + if ((buff[0] | buff[1] | buff[2] | buff[3]) == 0) { + sim_debug(DEBUG_CMD, dptr, + "SCSI Disk format0 buf0 %02x buf1 %02x buf2 %02x buf3 %02x\n", + buff[0], buff[1], buff[2], buff[3]); + goto add_size; + } + } + + /* the last sector is used by UTX for a ZMAP, so if there we are good to go */ + info = (buff[0]<<24) | (buff[1]<<16) | (buff[2]<<8) | buff[3]; + good = 0xf0000000 | (CAP(type)-1); + /* check for 0xf0ssssss where ssssss is disk size-1 in sectors */ + if ((info != good) && (info != zmap)) { + sim_debug(DEBUG_CMD, dptr, + "SCSI Disk format error buf0 %02x buf1 %02x buf2 %02x buf3 %02x\n", + buff[0], buff[1], buff[2], buff[3]); + printf("SCSI Disk format error buf0 %02x buf1 %02x buf2 %02x buf3 %02x\r\n", + buff[0], buff[1], buff[2], buff[3]); +fmt: + /* format the drive */ + if (scsi_format(uptr)) { + detach_unit(uptr); /* if no space, error */ + return SCPE_FMT; /* error */ + } + } +ldone: + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + detach_unit(uptr); /* if no space, error */ + return SCPE_FMT; /* error */ + } + + /* start out at sector 0 */ + uptr->CHS = 0; /* set CHS to cyl/hd/sec = 0 */ + + sim_debug(DEBUG_CMD, dptr, + "SCSI Attach %s %04x cyl %d hds %d spt %d spc %d cap sec %d cap bytes %d\n", + scsi_type[type].name, chsa, CYL(type), HDS(type), SPT(type), SPC(type), + CAP(type), CAPB(type)); + + printf("SCSI Attach %s %04x cyl %d hds %d spt %d spc %d cap sec %d cap bytes %d\r\n", + scsi_type[type].name, chsa, CYL(type), HDS(type), SPT(type), SPC(type), + CAP(type), CAPB(type)); + + sim_debug(DEBUG_CMD, dptr, "SCSI File %s at chsa %04x attached to %s is ready\n", + file, chsa, scsi_type[type].name); + printf("SCSI File %s at chsa %04x attached to %s is ready\r\n", + file, chsa, scsi_type[type].name); + + /* check for valid configured disk */ + /* must have valid DIB and Channel Program pointer */ + dibp = (DIB *)dptr->ctxt; /* get the DIB pointer */ + if ((dib_unit[chsa] == NULL) || (dibp == NULL) || (dibp->chan_prg == NULL)) { + sim_debug(DEBUG_CMD, dptr, + "ERROR===ERROR\nSCSI device %s not configured on system, aborting\n", + dptr->name); + printf("ERROR===ERROR\nSCSI device %s not configured on system, aborting\n", + dptr->name); + detach_unit(uptr); /* detach if error */ + return SCPE_UNATT; /* error */ + } + set_devattn(chsa, SNS_DEVEND); + return SCPE_OK; +} + +/* detach a disk device */ +t_stat scsi_detach(UNIT *uptr) { + uptr->SNS = 0; /* clear sense data */ + uptr->CMD &= LMASK; /* no cmd and flags */ + return detach_unit(uptr); /* tell simh we are done with disk */ +} + +/* Handle haltio transfers for disk */ +t_stat scsi_haltio(UNIT *uptr) { + uint16 chsa = GET_UADDR(uptr->CMD); + DEVICE *dptr = get_dev(uptr); + int cmd = uptr->CMD & DSK_CMDMSK; + CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */ + +// sim_debug(DEBUG_EXP, dptr, + sim_debug(DEBUG_DETAIL, dptr, + "scsi_haltio enter chsa %04x cmd = %02x\n", chsa, cmd); + + /* terminate any input command */ + /* UTX wants SLI bit, but no unit exception */ + /* status must not have an error bit set */ + /* otherwise, UTX will panic with "bad status" */ + if ((uptr->CMD & DSK_CMDMSK) != 0) { /* is unit busy */ + sim_debug(DEBUG_EXP, dptr, + "scsi_haltio HIO chsa %04x cmd = %02x ccw_count %02x\n", + chsa, cmd, chp->ccw_count); + sim_cancel(uptr); /* clear the input timer */ + } else { + sim_debug(DEBUG_DETAIL, dptr, + "scsi_haltio HIO I/O not busy chsa %04x cmd = %02x\n", chsa, cmd); + } + /* stop any I/O and post status and return error status */ + chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* stop any chaining */ + uptr->CMD &= LMASK; /* no cmd and flags */ + uptr->SNS &= ~MASK24; /* clear all but old mode data */ + sim_debug(DEBUG_EXP, dptr, + "scsi_haltio HIO I/O stop chsa %04x cmd = %02x CHS %08x STAR %08x\n", + chsa, cmd, uptr->CHS, uptr->STAR); + chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* force end */ +// chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); /* force end */ + return SCPE_IOERR; +} + +/* boot from the specified disk unit */ +t_stat scsi_boot(int32 unit_num, DEVICE *dptr) { + UNIT *uptr = &dptr->units[unit_num]; /* find disk unit number */ + + sim_debug(DEBUG_CMD, dptr, "SCSI Disk Boot dev/unit %04x\n", GET_UADDR(uptr->CMD)); + + /* see if device disabled */ + if (dptr->flags & DEV_DIS) { + printf("ERROR===ERROR\r\nSCSI Disk device %s disabled on system, aborting\r\n", + dptr->name); + return SCPE_UDIS; /* device disabled */ + } + + if ((uptr->flags & UNIT_ATT) == 0) { + sim_debug(DEBUG_EXP, dptr, "SCSI Disk Boot attach error dev/unit %04x\n", + GET_UADDR(uptr->CMD)); + printf("SCSI Disk Boot attach error dev/unit %04x\n", GET_UADDR(uptr->CMD)); + return SCPE_UNATT; /* attached? */ + } + + /* seek to sector 0 */ + if ((sim_fseek(uptr->fileref, 0, SEEK_SET)) != 0) { /* seek home */ + printf("SCSI Disk Boot Error on seek to 0\n"); + } + + SPAD[0xf4] = GET_UADDR(uptr->CMD); /* put boot device chan/sa into spad */ + SPAD[0xf8] = 0xF000; /* show as F class device */ + return chan_boot(GET_UADDR(uptr->CMD), dptr); /* boot the ch/sa */ +} + +/* Disk option setting commands */ +t_stat scsi_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int i; + + if (cptr == NULL) /* any disk name input? */ + return SCPE_ARG; /* arg error */ + if (uptr == NULL) /* valid unit? */ + return SCPE_IERR; /* no, error */ + if (uptr->flags & UNIT_ATT) /* is unit attached? */ + return SCPE_ALATT; /* no, error */ + + /* now loop through the units and find named disk */ + for (i = 0; scsi_type[i].name != 0; i++) { + if (strcmp(scsi_type[i].name, cptr) == 0) { + uptr->flags &= ~UNIT_TYPE; /* clear the old UNIT type */ + uptr->flags |= SET_TYPE(i); /* set the new type */ + /* set capacity of disk in sectors */ + uptr->capac = CAP(i); + return SCPE_OK; + } + } + return SCPE_ARG; +} + +t_stat scsi_get_type(FILE * st, UNIT *uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fputs("TYPE=", st); + fputs(scsi_type[GET_TYPE(uptr->flags)].name, st); + return SCPE_OK; +} + +/* help information for disk */ +t_stat scsi_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr) +{ + int i; + fprintf (st, "SEL-32 MFP SCSI Bus Disk Controller\r\n"); + fprintf (st, "Use:\r\n"); + fprintf (st, " sim> SET %sn TYPE=type\r\n", dptr->name); + fprintf (st, "Type can be: "); + for (i = 0; scsi_type[i].name != 0; i++) { + fprintf(st, "%s", scsi_type[i].name); + if (scsi_type[i+1].name != 0) + fprintf(st, ", "); + } + fprintf (st, ".\nEach drive has the following storage capacity:\r\n"); + for (i = 0; scsi_type[i].name != 0; i++) { + int32 size = CAPB(i); /* disk capacity in bytes */ + size /= 1024; /* make KB */ + size = (10 * size) / 1024; /* size in MB * 10 */ + fprintf(st, " %-8s %4d.%1d MB cyl %3d hds %3d sec %3d blk %3d\r\n", + scsi_type[i].name, size/10, size%10, CYL(i), HDS(i), SPT(i), SSB(i)); + } + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char *scsi_description (DEVICE *dptr) +{ + return "SEL-32 MFP SCSI Disk Controller"; +} + +#endif diff --git a/SEL32/sel32_sys.c b/SEL32/sel32_sys.c new file mode 100644 index 00000000..66d9c801 --- /dev/null +++ b/SEL32/sel32_sys.c @@ -0,0 +1,1722 @@ +/* sel32_sys.c: SEL-32 Gould Concept/32 (orignal SEL-32) Simulator system interface. + + Copyright (c) 2018-2022, James C. Bevier + Portions provided by Richard Cornwell, Geert Rolf and other SIMH contributers + + 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 + JAMES C. BEVIER 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. +*/ + +#include "sel32_defs.h" +#include + +extern REG cpu_reg[]; +extern uint32 M[MAXMEMSIZE]; +extern uint32 SPAD[]; +extern uint32 PSD[]; +char *dump_mem(uint32 mp, int cnt); +char *dump_buf(uint8 *mp, int32 off, int cnt); + +/* SCP data structures and interface routines + +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 (in sel32_cpu.c) +sim_stop_messages[] array of pointers to stop messages +sim_instr() instruction execution routine (in sel32_cpu.c) +sim_load() binary loader routine +sim_emax maximum number of words for examine + +In addition, the simulator must supply routines to print and parse +architecture specific formats + +fprint_sym print symbolic output +fparse_sym parse symbolic input +*/ + +char sim_name[] = "SEL-32"; /* our simulator name */ +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 4; /* maximum number of instructions/words to examine */ + +DEVICE *sim_devices[] = { + &cpu_dev, +#ifdef NUM_DEVS_IOP + &iop_dev, /* IOP channel controller */ +#endif +#ifdef NUM_DEVS_MFP + &mfp_dev, /* MFP channel controller */ +#endif +#ifdef NUM_DEVS_RTOM + &rtc_dev, + &itm_dev, +#endif +#ifdef NUM_DEVS_CON + &con_dev, +#endif +#ifdef NUM_DEVS_CDR + &cdr_dev, +#endif +#ifdef NUM_DEVS_CDP + &cdp_dev, +#endif +#ifdef NUM_DEVS_LPR + &lpr_dev, +#endif +#ifdef NUM_DEVS_MT + &mta_dev, +#if NUM_DEVS_MT > 1 + &mtb_dev, +#endif +#endif +#ifdef NUM_DEVS_DISK + &dda_dev, +#if NUM_DEVS_DISK > 1 + &ddb_dev, +#endif +#endif +#ifdef NUM_DEVS_SCFI + &sda_dev, +#if NUM_DEVS_SCFI > 1 + &sdb_dev, +#endif +#endif +#ifdef NUM_DEVS_HSDP + &dpa_dev, +#if NUM_DEVS_HSDP > 1 + &dpb_dev, +#endif +#endif +#ifdef NUM_DEVS_SCSI + &sba_dev, +#if NUM_DEVS_SCSI > 1 + &sbb_dev, +#endif +#endif +#ifdef NUM_DEVS_ETHER + &ec_dev, +#endif +#ifdef NUM_DEVS_COM + &coml_dev, + &com_dev, +#endif + NULL }; + +/* Simulator debug controls */ +DEBTAB dev_debug[] = { + {"CMD", DEBUG_CMD, "Show command execution to devices"}, + {"DATA", DEBUG_DATA, "Show data transfers"}, + {"DETAIL", DEBUG_DETAIL, "Show details about device"}, + {"EXP", DEBUG_EXP, "Show exception information"}, + {"INST", DEBUG_INST, "Show instruction execution"}, + {"XIO", DEBUG_XIO, "Show XIO I/O instructions"}, + {"IRQ", DEBUG_IRQ, "Show interrupt requests"}, + {"TRAP", DEBUG_TRAP, "Show trap requests"}, + {0, 0} +}; + +const char *sim_stop_messages[SCPE_BASE] = { + "Unknown error", + "IO device not ready", + "HALT instruction", + "Breakpoint", + "Unknown Opcode", + "Invalid instruction", + "Invalid I/O operation", + "Nested indirects exceed limit", + "I/O Check opcode", + "Memory management trap during trap", +}; + +#define PRINTABLE(x) ((x < 32) || (x > 126)) ? '.' : x + +static char line[257]; +/* function to dump SEL32 memory up to 16 bytes with side by side ascii values */ +char *dump_mem(uint32 mp, int cnt) +{ + char buff[257]; + uint32 ma = mp; /* save memory address */ + char *cp = &line[0]; /* output buffer */ + int cc=0, ch, bp=0, bl=cnt; + + if (cnt > 16) + bl = 16; /* stop at 16 chars */ + + while (bp < bl) { + if (!bp) { + cc = sprintf(cp, " %06x : ", ma); /* output location address */ + cp += cc; /* next print location */ + } + ch = RMB(ma) & 0xff; /* get a char from memory */ + ma++; /* next loc */ + cc += sprintf(cp, "%02x", ch); /* print out current char */ + cp += 2; /* next print location */ + buff[bp++] = PRINTABLE(ch); /* get printable version of char */ + if (!(bp % 4)) { /* word boundry yet? */ + cc += sprintf(cp, " "); /* space between words */ + cp += 1; /* next print location */ + } + } + + while (bp < 16) { + cc += sprintf(cp, " "); /* print out one space */ + cp += 1; /* next print location */ + buff[bp++] = 0x20; /* blank char buffer */ + if (!(bp % 4)) { + cc += sprintf(cp, " "); /* space between words */ + cp += 1; /* next print location */ + } + } + buff[bp] = 0; /* terminate line */ + cc += sprintf(cp, "|%s|\n", buff); /* print out ascii text */ + return (line); /* return pointer to caller */ +} + +/* function to dump caller buffer upto 16 bytes with side by side ascii values */ +/* off is offset in buffer to start */ +char *dump_buf(uint8 *mp, int32 off, int cnt) +{ + char buff[257]; + uint32 ma = off; /* save memory address */ + char *cp = &line[0]; /* output buffer */ + int cc=0, ch, bp=0, bl=cnt; + + if (cnt > 16) + bl = 16; /* stop at 16 chars */ + + while (bp < bl) { + if (!bp) { + cc = sprintf(cp, " %06x : ", ma); /* output location offset */ + cp += cc; /* next print location */ + } + ch = mp[ma++] & 0xff; /* get a char from memory */ + cc += sprintf(cp, "%02x", ch); /* print out current char */ + cp += 2; /* next print location */ + buff[bp++] = PRINTABLE(ch); /* get printable version of char */ + if (!(bp % 4)) { /* word boundry yet? */ + cc += sprintf(cp, " "); /* space between words */ + cp += 1; /* next print location */ + } + } + + while (bp < 16) { + cc += sprintf(cp, " "); /* print out one space */ + cp += 1; /* next print location */ + buff[bp++] = 0x20; /* blank char buffer */ + if (!(bp % 4)) { + cc += sprintf(cp, " "); /* space between words */ + cp += 1; /* next print location */ + } + } + buff[bp] = 0; /* terminate line */ + cc += sprintf(cp, "|%s|\n", buff); /* print out ascii text */ + return (line); /* return pointer to caller */ +} + + + +/* + * get_word - function to load a 32 bit word from the input file + * return 1 - OK + * return 0 - error or eof + */ +int get_word(FILE *fileref, uint32 *word) +{ + unsigned char cbuf[4]; + + /* read in the 4 chars */ + if (sim_fread(cbuf, 1, 4, fileref) != 4) + return 1; /* read error or eof */ + /* byte swap while reading data */ + *word = ((cbuf[0]) << 24) | ((cbuf[1]) << 16) | + ((cbuf[2]) << 8) | ((cbuf[3])); + return 0; /* all OK */ +} + +#ifdef NO_TAP_FOR_NOW +/* + * get_halfword - function to load a 16 bit halfword from the input file + * return 1 - OK + * return 0 - error or eof + */ +int get_halfword(FILE *fileref, uint16 *word) +{ + unsigned char cbuf[2]; + + /* read in the 2 chars */ + if (sim_fread(cbuf, 1, 2, fileref) != 2) + return 1; /* read error or eof */ + /* byte swap while reading data */ + *word = ((uint16)(cbuf[0]) << 8) | ((uint16)(cbuf[1])); + return 0; /* all OK */ +} +#endif + +/* load a binary file into memory starting at loc 0 */ +/* return SCPE_OK on load complete */ +t_stat load_mem (FILE *fileref) +{ + uint32 data; + uint32 ma = 0; /* start at mem add 0 */ + + /* read the file until the end */ + for ( ;; ) { + if (get_word(fileref, &data)) /* get 32 bits of data */ + return SCPE_OK; /* load is complete, return */ + M[ma++] = data; /* put data in memory */ + } + return SCPE_OK; /* never here */ +} + +#ifdef NO_TAP_FOR_NOW +/* load tap formated tape into memory */ +/* return SCPE_OK on load complete */ +t_stat load_tap (FILE *fileref) +{ + uint32 bdata, edata; + uint16 hdata; + uint32 ma = 0; /* start loading at loc 0 */ + int32 wc; + + for ( ;; ) { /* loop until EOF read */ + /* look for record byte count or zero for EOF */ + if (get_word(fileref, &bdata)) /* read 4 bytes of data */ + return SCPE_FMT; /* must be error, exit */ + wc = (int32)(bdata); /* byte count in tape record */ + wc = (wc + 1)/2; /* change byte count into hw count */ + if (wc == 0) + return SCPE_OK; /* eof found, return */ + /* copy data to memory in 16 bit halfwords */ + while (wc-- != 0) { + if (get_halfword(fileref, &hdata)) /* get 16 bits of data */ + return SCPE_FMT; /* must be error, exit */ + ((uint16*)M)[ma++] = hdata; /* put the hw into memory */ + } + /* look only for record byte count */ + if (get_word(fileref, &edata)) /* read 4 bytes of data */ + return SCPE_FMT; /* must be error, exit */ + /* the before and after byte count must be equal */ + if (bdata != edata) + return SCPE_FMT; /* must be error, exit */ + } + return SCPE_OK; /* never here */ +} +#endif + +/* ICL formats */ +/*********************************************** + * + * *DEVXX=FCILCASA (,N) + * + * *DEV defines a controller definition entry + * XX hex address that will be used by I/O instructions to address controller + * = required delimiter for following 8 hex characters + * F flag used for I/O emulation by CPU, not used but must be zero. + * C defines the class of controller: + * 0 = Line Printer + * 1 = Card Reader + * 2 = Teletype + * 3 = Interval Timer + * 4 = Panel + * 5-D Unassigned + * E = All Others + * F = Extended I/O + * IL Controller interrupt priority level of Service Interrupt (0x14 - 0x23) + * CA Controller address defined by hardware switches on controller + * SA Lowest controller device subaddress. Usually zero when only 1 device configured + * The subaddress field (SA) must reflect the following for TLC controller: + * 00 = Card Reader + * 01 = Teletype + * 02 = Line Printer + * () denotes optional parameter + * ,NN 2 digit hexx number of devices configured on the controller` + * + *********************************************** + * + * *INTXX=RS + * *INT defines interrupt definition entry + * XX Hex interrupt priority level to be defined + * = required delimiter for following 2 hex characters + * R Hex RTOM board number to which the interrupt XX is assigned + * S 1's complement of the hex subaddress of the RTOM board + * assigned to the interrupt XX + * + * RTOM physical controller address 0x79 is RTOM board number 1, RTOM + * address 0x7A is board number 2, etc. + * Real-Time Clock is connected tp subaddress 6 on RTOM board + * Interval Timer is connected to subaddress 4 on RTOM board + * RTOM physical address must be 0x79 or above to be able to + * support up to seven RTOM boards for maximum configuration + * of 112 interrupt levels (7 x 16). + * + *********************************************** + * + * *END + * *END Defines the last record of the Initial Configuration Load file. + * + *********************************************** +*/ + +/* Example device entry + * *DEV04=0E140100,04 + * The controller is "E" class + * CPU command device address will be 0x04 + * The priority of the Service Interrupt is 0x14 + * The first device has suaddress of 00 and there are 4 devices defined + * There will be four devices defined in SPAD. The I/O commands (CD and TD) + * will address the devices as 0x04, 0x05, 0x06, and 0x07. + * The physical address of the controller is 0x10. + * Assigning SI address of 0x14 means: + * The transfer Interrupt location for priority 0x14 is 0x100. + * The Service Interrupt vector location for priority 0x14 is 0x140. + * The emulation IOCD will be stored at loation 0x700. + * The interrupt control instructions (DI, DI, RI, AI, DAI) will control + * the interrupt of the controller by addressing priority 0x14. + * + * Example interrupt entry (RTOM) + * *INT28=16 + * The interrupt control instructions (DI, EI, RI, AI, DAI) will control + * the interrupt on the RTOM by addressing priority 0x28. + * The RTOM board is 1 + * The subaddress on the board is 0x06 (jumpered locic subaddress is 9) + */ + +/* + * Example ICL file + * *DEV04=0E150400,02 Cartridge disc with two platters + * *DEV08=0E160800,04 Moving head disc + * *DEV10=0E181000,04 9-Track magnetic tape + * *DEV20=0E1A2000,10 GPMC with 16 terminals + * *DEV60=0E1E6000,08 ADS + * *DEV78=01207800 Primary card reader + * *DEV7A=00217802 Primary line printer + * *DEV7E=02237801 Primary Teletype + * *INT00=1F Power fail/Auto restart + * *INT01=1E System Overide + * *INT12=1D Memory parity + * *INT13=1C Console Interrupt + * *INT24=1B Nonpresent memory + * *INT25=1A Undefined instruction trap + * *INT26=19 Privlege violation + * *INT27=18 Call Monitor + * *INT28=16 Real-time clock + * *INT29=17 Arithmetic exception + * *INT2A=15 External interrupt + * *INT2B=14 External interrupt + * *INT2C=13 External interrupt + * *INT2D=12 External interrupt + * *END + */ + +/* process two hex input characters into a number + * pt - input char pointer + * val - word oiunter when number will be saved + * return SCPE_OK for OK + * or SCPE_ARG for arg error (bad number) + */ +t_value get_2hex(char *pt, uint32 *val) +{ + int32 hexval; + uint32 c1 = sim_toupper((uint32)pt[0]); /* first hex char */ + uint32 c2 = sim_toupper((uint32)pt[1]); /* next hex char */ + + if (isdigit(c1)) /* digit */ + hexval = c1 - (uint32)'0'; /* get value */ + else + if (isxdigit(c1)) /* hex digit */ + hexval = c1 - (uint32)'A' + 10; /* get hex value */ + else + return SCPE_ARG; /* oops, error */ + hexval <<= 4; /* move to upper nibble */ + if (isdigit(c2)) /* digit */ + hexval += c2 - (uint32)'0'; /* get value */ + else + if (isxdigit(c2)) /* hex digit */ + hexval += c2 - (uint32)'A' + 10; /* get hex value */ + else + return SCPE_ARG; /* oops, error */ + *val = hexval; /* return value to caller */ + return SCPE_OK; /* all OK */ +} + +/* load an ICL file and configure SPAD interupt and device entries */ +/* SPAD keyword will not be set and will be set when MPX or UTX is loaded */ +/* return SCPE_OK on load complete */ +t_stat load_icl(FILE *fileref) +{ + char *cp; /* work pointer in buf[] */ + uint32 sa; /* spad address */ + uint32 dev; /* device entry */ + uint32 intr; /* interrupt entry */ + uint32 data; /* entry data */ + uint32 cls; /* device class */ + uint32 ivl; /* Interrupt Vector Location */ + uint32 i; /* just a tmp */ + char buf[120]; /* input buffer */ + + /* read file input records until the end */ + while (fgets(&buf[0], 120, fileref) != 0) { + /* skip any white spaces */ + for(cp = &buf[0]; *cp == ' ' || *cp == '\t'; cp++); + if (*cp++ != '*') + continue; /* if line does not start with *, ignore */ + if(sim_strncasecmp(cp, "END", 3) == 0) { + return SCPE_OK; /* we are done */ + } + else + if(sim_strncasecmp(cp, "DEV", 3) == 0) { + /* process device entry */ + /* + |----+----+----+----+----+----+----+----| + |Flgs|CLS |0|Int Lev|0|Phy Adr|Sub Addr | + |----+----+----+----+----+----+----+----| + */ + for(cp += 3; *cp == ' ' || *cp == '\t'; cp++); /* skip white spaces */ + if (get_2hex(cp, &dev) != SCPE_OK) /* get the device address */ + return SCPE_ARG; /* unknown input, argument error */ + if (dev > 0x7f) /* devices are 0-7f (0-127) */ + return SCPE_ARG; /* argument error */ + sa = dev + 0x00; /* device entry spad address is dev# + 0x00 */ + cp += 2; /* skip the 2 processed chars */ + if (*cp++ != '=') /* must have = sign */ + return SCPE_ARG; /* unknown input, argument error */ + if (get_2hex(cp, &cls) != SCPE_OK) /* get unused '0" and class */ + return SCPE_ARG; /* unknown input, argument error */ + cp += 2; /* skip the 2 processed chars */ + if (get_2hex(cp, &intr) != SCPE_OK) /* get the interrupt level value */ + return SCPE_ARG; /* unknown input, argument error */ + if (intr > 0x6f) /* ints are 0-6f (0-111) */ + return SCPE_ARG; /* argument error */ + /* put class and 1's intr in place */ + dev = ((~intr & 0x7f) << 16) | ((cls & 0x0f) << 24); + cp += 2; /* skip the 2 processed chars */ + if (get_2hex(cp, &data) != SCPE_OK) /* get the selbus physical address */ + return SCPE_ARG; /* unknown input, argument error */ + if (data > 0x7f) /* address is 0-7f (0-127) */ + return SCPE_ARG; /* argument error */ + dev |= (data & 0x7f) << 8; /* insert the physical address */ + cp += 2; /* skip the 2 processed chars */ + if (get_2hex(cp, &data) != SCPE_OK) /* get the starting sub address 0-ff (255) */ + return SCPE_ARG; /* unknown input, argument error */ + if (data > 0x7f) /* sub address is 0-ff (0-256) */ + return SCPE_ARG; /* argument error */ + if ((cls & 0xf) != 0xf) /* sub addr must be zero for class F */ + dev |= (data & 0xff); /* insert the starting sub address for non f class */ + SPAD[sa] = dev; /* put the first device entry into the spad */ + /* see if there is an optional device count for class 'E' I/O */ + if ((cls & 0xf) == 0xe) { + cp += 2; /* skip the 2 processed chars */ + if (*cp++ == ',') { /* must have comma if optional parameters */ + /* check for optional sub addr cnt */ + if (get_2hex(cp, &data) != SCPE_OK) /* get the count */ + return SCPE_ARG; /* unknown input, argument error */ + if (data > 0x10) /* sub address is max of 16 */ + return SCPE_ARG; /* argument error */ + for (i=0; i 0x6f) /* ints are 0-6f (0-111) */ + return SCPE_ARG; /* argument error */ + sa = intr + 0x80; /* interrupt entry spad address is int# + 0x80 */ +/* TODO call function here to create 32/7x IVL location for interrupt */ +/* if (CPU_MODEL < MODEL_27) get_IVL(intr, &ivl); */ + ivl = (intr << 2) + 0x100; /* default IVL base is 0x100 for Concept machines */ + cp += 2; /* skip the 2 processed chars */ + if (*cp++ != '=') /* must have = sign */ + return SCPE_ARG; /* unknown input, argument error */ + if (get_2hex(cp, &data) != SCPE_OK) + return SCPE_ARG; /* unknown input, argument error */ + /* first digit is 3 ls bits of RTOM addr 0x79 is 001 */ + intr = 0x00800000 | ((data & 0x70) << 16); /* put the RTOM 3 LSBs into entry */ + /* second digit is subaddress on RTOM board for interrupt connection, ~6 = 9 */ + intr |= (data & 0xf) << 16; /* put in 1's comp of RTOM subaddress */ + /* add in the correct IVL for 32/7x or concelt machines */ + intr |= ivl; /* set the IVL location */ + SPAD[sa] = intr; /* put the interrupt entry into the spad */ + } + else + return SCPE_ARG; /* unknown input, argument error */ + } + return SCPE_OK; /* file done */ +} + + +/* Load a file image into memory. */ +/* file.mem files are binary files created with the makecode utility */ +#ifdef NO_TAP_FOR_NOW +/* file.tap files are TAP formatted binary files created from tape images */ +#endif +/* data is raw binary memory data and is loaded starting at loc 0 */ + +#define FMT_NONE 0 +#define FMT_MEM 1 +#ifdef NO_TAP_FOR_NOW +#define FMT_TAP 2 +#endif +#define FMT_ICL 3 +t_stat sim_load (FILE *fileref, CONST char *cptr, CONST char *fnam, int flag) +{ + int32 fmt; + + fmt = FMT_NONE; /* no format */ + /* match the extension to .mem for this file */ + if (match_ext(fnam, "MEM")) + fmt = FMT_MEM; /* we have binary format */ + else +#ifdef NO_TAP_FOR_NOW + if (match_ext(fnam, "TAP")) + fmt = FMT_TAP; /* we have tap tape format */ + else +#endif + /* match the extension to .icl for this file */ + if (match_ext(fnam, "ICL")) + fmt = FMT_ICL; /* we have initial configuration load (ICL) format */ + else + return SCPE_FMT; /* format error */ + + switch (fmt) { + + case FMT_MEM: /* binary memory image */ + return load_mem(fileref); + +#ifdef NO_TAP_FOR_NOW + case FMT_TAP: /* tape file image */ + return load_tap(fileref); +#endif + + case FMT_ICL: /* icl file image */ + return load_icl(fileref); + +#ifdef NO_TAP_FOR_NOW + case FMT_NONE: /* nothing */ +#endif + default: + break; + } + return SCPE_FMT; /* format error */ +} + +/* Symbol tables */ + +/* + * The SEL 32 supports the following instruction formats. + * + * TYPE Format Normal Base Mode + * A ADR d,[*]o,x d,o[(b)],x FC = extra + * B BRA [*]o,x o[(b)],x + * C IMM d,o d,o + * D BIT d,[*]o d,o[(b)] + * E ADR [*]o,x o[(b)],x FC = extra + * HALFWORD + * F REG s,d s,d Half Word + * G RG1 s s + * H HLF + * I SHF d,v d,v + * K RBT d,b d,b + * L EXR s s + * M IOP n,b n,b + * N SVC n,b n,b + */ + +#define TYPE_A 0 +#define TYPE_B 1 +#define TYPE_C 2 +#define TYPE_D 3 +#define TYPE_E 4 +#define TYPE_F 5 +#define TYPE_G 6 +#define TYPE_H 7 +#define TYPE_I 8 +#define TYPE_K 9 +#define TYPE_L 10 +#define TYPE_M 11 +#define TYPE_N 12 +#define H 0x10 /* halfword instruction */ +/* all instruction unless specified as base/nobase only will be either */ +#define B 0x20 /* base register mode only */ +#define N 0x40 /* non base register mode only */ +#define X 0x80 /* 32/55 or 32/75 only */ + +typedef struct _opcode { + uint16 opbase; + uint16 mask; + uint8 type; + const char *name; +} t_opcode; + +t_opcode optab[] = { + { 0x0000, 0xFFFF, H|TYPE_H, "HALT", }, /* Halt # * */ + { 0x0001, 0xFFFF, H|TYPE_H, "WAIT", }, /* Wait # * */ + { 0x0002, 0xFFFF, H|TYPE_H, "NOP", }, /* Nop # */ + { 0x0003, 0xFC0F, H|TYPE_G, "LCS", }, /* Load Control Switches */ + { 0x0004, 0xFC0F, H|TYPE_G, "ES", }, /* Extend Sign # */ + { 0x0005, 0xFC0F, H|TYPE_G, "RND", }, /* Round Register # */ + { 0x0006, 0xFFFF, H|TYPE_H, "BEI", }, /* Block External Interrupts # */ + { 0x0007, 0xFFFF, H|TYPE_H, "UEI", }, /* Unblock External Interrupts # */ + { 0x0008, 0xFFFF, H|TYPE_H, "EAE", }, /* Enable Arithmetic Exception Trap # */ + { 0x0009, 0xFC0F, H|TYPE_G, "RDSTS", }, /* Read CPU Status Word * */ + { 0x000A, 0xFFFF, H|TYPE_H, "SIPU", }, /* Signal IPU # */ + { 0x000B, 0xFC0F, H|TYPE_F, "RWCS", }, /* Read Writable Control Store # */ + { 0x000C, 0xFC0F, H|TYPE_F, "WWCS", }, /* Write Writable Control Store # */ + { 0x000D, 0xFFFF, N|H|TYPE_H, "SEA", }, /* Set Extended Addressing # NBR Only */ + { 0x000E, 0xFFFF, H|TYPE_H, "DAE", }, /* Disable Arithmetic Exception Trap # */ + { 0x000F, 0xFFFF, N|H|TYPE_H, "CEA", }, /* Clear Extended Addressing # NBR Only */ + { 0x0400, 0xFC0F, H|TYPE_F, "ANR", }, /* And Register # */ + { 0x0407, 0xFC0F, H|TYPE_G, "SMC", }, /* Shared Memory Control # */ + { 0x040A, 0xFC0F, H|TYPE_G, "CMC", }, /* Cache Memory Control # */ + { 0x040B, 0xFC0F, H|TYPE_G, "RPSWT", }, /* Read Processor Status Word Two # */ + { 0x0800, 0xFC0F, H|TYPE_F, "ORR", }, /* Or Register # */ + { 0x0808, 0xFC0F, H|TYPE_F, "ORRM", }, /* Or Register Masked # */ + { 0x0C00, 0xFC0F, H|TYPE_F, "EOR", }, /* Exclusive Or Register # */ + { 0x0C08, 0xFC0F, H|TYPE_F, "EORM", }, /* Exclusive Or Register Masked # */ + { 0x1000, 0xFC0F, H|TYPE_F, "CAR", }, /* Compare Arithmetic Register # */ + { 0x1008, 0xFC0F, B|H|TYPE_F, "SACZ", }, /* Shift and Count Zeros # BR */ + { 0x1400, 0xFC0F, H|TYPE_F, "CMR", }, /* Compare masked with register */ + { 0x1800, 0xFC0C, H|TYPE_K, "SBR", }, /* Set Bit in Register # */ + { 0x1804, 0xFC0C, B|H|TYPE_K, "ZBR", }, /* Zero Bit In register # BR */ + { 0x1808, 0xFC0C, B|H|TYPE_K, "ABR", }, /* Add Bit In Register # BR */ + { 0x180C, 0xFC0C, B|H|TYPE_K, "TBR", }, /* Test Bit in Register # BR */ + { 0x1C00, 0xFC0C, N|H|TYPE_K, "ZBR", }, /* Zero Bit in Register # NBR */ /* CON SRABR */ + { 0x1C00, 0xFC60, B|H|TYPE_I, "SRABR", }, /* Shift Right Arithmetic # BR */ /* CON ZBM */ + { 0x1C20, 0xFC60, B|H|TYPE_I, "SRLBR", }, /* Shift Right Logical # BR */ + { 0x1C40, 0xFC60, B|H|TYPE_I, "SLABR", }, /* Shift Left Arithmetic # BR */ + { 0x1C60, 0xFC60, B|H|TYPE_I, "SLLBR", }, /* Shift Left Logical # BR */ + { 0x2000, 0xFC0C, N|H|TYPE_K, "ABR", }, /* Add Bit in Register # NBR */ /* CON SRADBR */ + { 0x2000, 0xFC60, B|H|TYPE_I, "SRADBR", }, /* Shift Right Arithmetic Double # BR */ /* CON ABR */ + { 0x2020, 0xFC60, B|H|TYPE_I, "SRLDBR", }, /* Shift Left Logical Double # BR */ + { 0x2040, 0xFC60, B|H|TYPE_I, "SLADBR", }, /* Shift Right Arithmetic Double # BR */ + { 0x2060, 0xFC60, B|H|TYPE_I, "SLLDBR", }, /* Shift Left Logical Double # BR */ + { 0x2400, 0xFC0C, N|H|TYPE_K, "TBR", }, /* Test Bit in Register # NBR */ /* CON SRCBR */ + { 0x2400, 0xFC60, B|H|TYPE_I, "SRCBR", }, /* Shift Right Circular # BR */ /* CON TBR */ + { 0x2440, 0xFC60, B|H|TYPE_F, "SLCBR", }, /* Shift Left Circular # BR */ + { 0x2800, 0xFC0F, H|TYPE_G, "TRSW", }, /* Transfer GPR to PSD */ + { 0x2802, 0xFC0F, B|H|TYPE_F, "XCBR", }, /* Exchange Base Registers # BR Only */ + { 0x2804, 0xFC0F, B|H|TYPE_G, "TCCR", }, /* Transfer CC to GPR # BR Only */ + { 0x2805, 0xFC0F, B|H|TYPE_G, "TRCC", }, /* Transfer GPR to CC # BR */ + { 0x2808, 0xFF8F, B|H|TYPE_F, "BSUB", }, /* Branch Subroutine # BR Only */ + { 0x2808, 0xFC0F, B|H|TYPE_F, "CALL", }, /* Procedure Call # BR Only */ + { 0x280C, 0xFC0F, B|H|TYPE_G, "TPCBR", }, /* Transfer Program Counter to Base # BR Only */ + { 0x280E, 0xFC7F, B|H|TYPE_G, "RETURN", }, /* Procedure Return # BR Only */ + { 0x2C00, 0xFC0F, H|TYPE_F, "TRR", }, /* Transfer Register to Register # */ + { 0x2C01, 0xFC0F, B|H|TYPE_F, "TRBR", }, /* Transfer GPR to BR # */ + { 0x2C02, 0xFC0F, B|H|TYPE_F, "TBRR", }, /* Transfer BR to GPR # BR Only */ + { 0x2C03, 0xFC0F, H|TYPE_F, "TRC", }, /* Transfer Register Complement # */ + { 0x2C04, 0xFC0F, H|TYPE_F, "TRN", }, /* Transfer Register Negative # */ + { 0x2C05, 0xFC0F, H|TYPE_F, "XCR", }, /* Exchange Registers # */ + { 0x2C07, 0xFC0F, H|TYPE_G, "LMAP", }, /* Load MAP * */ + { 0x2C08, 0xFC0F, H|TYPE_F, "TRRM", }, /* Transfer Register to Register Masked # */ + { 0x2C09, 0xFC0F, H|TYPE_G, "SETCPU", }, /* Set CPU Mode # * */ + { 0x2C0A, 0xFC0F, H|TYPE_F, "TMAPR", }, /* Transfer MAP to Register # * */ + { 0x2C0B, 0xFC0F, H|TYPE_F, "TRCM", }, /* Transfer Register Complement Masked # */ + { 0x2C0C, 0xFC0F, H|TYPE_F, "TRNM", }, /* Transfer Register Negative Masked # */ + { 0x2C0D, 0xFC0F, H|TYPE_F, "XCRM", }, /* Exchange Registers Masked # */ + { 0x2C0E, 0xFC0F, H|TYPE_F, "TRSC", }, /* Transfer Register to Scratchpad # * */ + { 0x2C0F, 0xFC0F, H|TYPE_F, "TSCR", }, /* Transfer Scratchpad to Register # * */ + { 0x3000, 0xFC0F, X|H|TYPE_F, "CALM", }, /* Call Monitor 32/55 # */ + { 0x3400, 0xFC00, N|TYPE_D, "LA", }, /* Load Address NBR Note! FW instruction */ + { 0x3800, 0xFC0F, H|TYPE_F, "ADR", }, /* Add Register to Register # */ + { 0x3801, 0xFC0F, H|TYPE_F, "ADRFW", }, /* Add Floating Point to Register # */ + { 0x3802, 0xFC0F, B|H|TYPE_F, "MPR", }, /* Multiply Register BR # */ + { 0x3803, 0xFC0F, H|TYPE_F, "SURFW", }, /* Subtract Floating Point Register # */ + { 0x3804, 0xFC0F, H|TYPE_F, "DVRFW", }, /* Divide Floating Point Register # */ + { 0x3805, 0xFC0F, H|TYPE_F, "FIXW", }, /* Fix Floating Point Register # */ + { 0x3806, 0xFC0F, H|TYPE_F, "MPRFW", }, /* Multiply Floating Point Register # */ + { 0x3807, 0xFC0F, H|TYPE_F, "FLTW", }, /* Float Floating Point Register # */ + { 0x3808, 0xFC0F, H|TYPE_F, "ADRM", }, /* Add Register to Register Masked # */ + { 0x3809, 0xFC0F, H|TYPE_F, "ADRFD", }, /* Add Floating Point Register to Register # */ + { 0x380A, 0xFC0F, B|H|TYPE_F, "DVR", }, /* Divide Register by Registier BR # */ + { 0x380B, 0xFC0F, H|TYPE_F, "SURFD", }, /* Subtract Floating Point Double # */ + { 0x380C, 0xFC0F, H|TYPE_F, "DVRFD", }, /* Divide Floating Point Double # */ + { 0x380D, 0xFC0F, H|TYPE_F, "FIXD", }, /* Fix Double Register # */ + { 0x380E, 0xFC0F, H|TYPE_F, "MPRFD", }, /* Multiply Double Register # */ + { 0x380F, 0xFC0F, H|TYPE_F, "FLTD", }, /* Float Double # */ + { 0x3C00, 0xFC0F, H|TYPE_F, "SUR", }, /* Subtract Register to Register # */ + { 0x3C08, 0xFC0F, H|TYPE_F, "SURM", }, /* Subtract Register to Register Masked # */ + { 0x4000, 0xFC0F, N|H|TYPE_F, "MPR", }, /* Multiply Register to Register # NBR */ + { 0x4400, 0xFC0F, N|H|TYPE_F, "DVR", }, /* Divide Register to Register # NBR */ + { 0x5000, 0xFC08, B|TYPE_D, "LABRM", }, /* Load Address BR Mode */ + { 0x5400, 0xFC08, B|TYPE_A, "STWBR", }, /* Store Base Register BR Only */ + { 0x5800, 0xFC08, B|TYPE_A, "SUABR", }, /* Subtract Base Register BR Only */ + { 0x5808, 0xFC08, B|TYPE_D, "LABR", }, /* Load Address Base Register BR Only */ + { 0x5C00, 0xFC08, B|TYPE_A, "LWBR", }, /* Load Base Register BR Only */ + { 0x5C08, 0xFF88, B|TYPE_B, "BSUBM", }, /* Branch Subroutine Memory BR Only */ + { 0x5C08, 0xFC08, B|TYPE_B, "CALLM", }, /* Call Memory BR Only */ + { 0x6000, 0xFC0F, N|H|TYPE_F, "NOR", }, /* Normalize # NBR Only */ + { 0x6400, 0xFC0F, N|H|TYPE_F, "NORD", }, /* Normalize Double # NBR Only */ + { 0x6800, 0xFC0F, N|H|TYPE_F, "SCZ", }, /* Shift and Count Zeros # */ + { 0x6C00, 0xFC40, N|H|TYPE_I, "SRA", }, /* Shift Right Arithmetic # NBR */ + { 0x6C40, 0xFC40, N|H|TYPE_I, "SLA", }, /* Shift Left Arithmetic # NBR */ + { 0x7000, 0xFC40, N|H|TYPE_I, "SRL", }, /* Shift Right Logical # NBR */ + { 0x7040, 0xFC40, N|H|TYPE_I, "SLL", }, /* Shift Left Logical # NBR */ + { 0x7400, 0xFC40, N|H|TYPE_I, "SRC", }, /* Shift Right Circular # NBR */ + { 0x7440, 0xFC40, N|H|TYPE_I, "SLC", }, /* Shift Left Circular # NBR */ + { 0x7800, 0xFC40, N|H|TYPE_I, "SRAD", }, /* Shift Right Arithmetic Double # NBR */ + { 0x7840, 0xFC40, N|H|TYPE_I, "SLAD", }, /* Shift Left Arithmetic Double # NBR */ + { 0x7C00, 0xFC40, N|H|TYPE_I, "SRLD", }, /* Shift Right Logical Double # NBR */ + { 0x7C40, 0xFC40, N|H|TYPE_I, "SLLD", }, /* Shift Left Logical Double # NBR */ + { 0x8000, 0xFC08, TYPE_A, "LEAR", }, /* Load Effective Address Real * */ + { 0x8400, 0xFC00, TYPE_A, "ANM", }, /* And Memory B,H,W,D */ + { 0x8800, 0xFC00, TYPE_A, "ORM", }, /* Or Memory B,H,W,D */ + { 0x8C00, 0xFC00, TYPE_A, "EOM", }, /* Exclusive Or Memory */ + { 0x9000, 0xFC00, TYPE_A, "CAM", }, /* Compare Arithmetic with Memory */ + { 0x9400, 0xFC00, TYPE_A, "CMM", }, /* Compare Masked with Memory */ + { 0x9800, 0xFC00, TYPE_D, "SBM", }, /* Set Bit in Memory */ + { 0x9C00, 0xFC00, TYPE_D, "ZBM", }, /* Zero Bit in Memory */ + { 0xA000, 0xFC00, TYPE_D, "ABM", }, /* Add Bit in Memory */ + { 0xA400, 0xFC00, TYPE_D, "TBM", }, /* Test Bit in Memory */ + { 0xA800, 0xFC00, TYPE_B, "EXM", }, /* Execute Memory */ + { 0xAC00, 0xFC00, TYPE_A, "L", }, /* Load B,H,W,D */ + { 0xB000, 0xFC00, TYPE_A, "LM", }, /* Load Masked B,H,W,D */ + { 0xB400, 0xFC00, TYPE_A, "LN", }, /* Load Negative B,H,W,D */ + { 0xB800, 0xFC00, TYPE_A, "ADM", }, /* Add Memory B,H,W,D */ + { 0xBC00, 0xFC00, TYPE_A, "SUM", }, /* Subtract Memory B,H,W,D */ + { 0xC000, 0xFC00, TYPE_A, "MPM", }, /* Multiply Memory B,H,W,D */ + { 0xC400, 0xFC00, TYPE_A, "DVM", }, /* Divide Memory B,H,W,D */ + { 0xC800, 0xFC0F, TYPE_C, "LI", }, /* Load Immediate */ + { 0xC801, 0xFC0F, TYPE_C, "ADI", }, /* Add Immediate */ + { 0xC802, 0xFC0F, TYPE_C, "SUI", }, /* Subtract Immediate */ + { 0xC803, 0xFC0F, TYPE_C, "MPI", }, /* Multiply Immediate */ + { 0xC804, 0xFC0F, TYPE_C, "DVI", }, /* Divide Immediate */ + { 0xC805, 0xFC0F, TYPE_C, "CI", }, /* Compare Immediate */ + { 0xC806, 0xFC0F, TYPE_N, "SVC", }, /* Supervisor Call */ + { 0xC807, 0xFC0F, TYPE_G, "EXR", }, /* Execute Register/ Right */ + { 0xC808, 0xFC0F, X|TYPE_A, "SEM", }, /* Store External Map 32/7X * */ + { 0xC809, 0xFC0F, X|TYPE_A, "LEM", }, /* Load External Map 32/7X * */ + { 0xC80A, 0xFC0F, X|TYPE_A, "CEMA", }, /* Convert External Map 32/7X * */ + { 0xCC00, 0xFC08, TYPE_A, "LF", }, /* Load File */ + { 0xCC08, 0xFC08, TYPE_A, "LFBR", }, /* Load Base File */ + { 0xD000, 0xFC00, N|TYPE_A, "LEA", }, /* Load Effective Address # NBR */ + { 0xD400, 0xFC00, TYPE_A, "ST", }, /* Store B,H,W,D */ + { 0xD800, 0xFC00, TYPE_A, "STM", }, /* Store Masked B,H,W,D */ + { 0xDC00, 0xFC08, TYPE_A, "STF", }, /* Store File */ + { 0xDC08, 0xFC08, TYPE_A, "STFBR", }, /* Store Base File */ + { 0xE000, 0xFC08, TYPE_A, "SUF", }, /* Subtract Floating Memory D,W */ + { 0xE008, 0xFC08, TYPE_A, "ADF", }, /* Add Floating Memory D,W */ + { 0xE400, 0xFC08, TYPE_A, "DVF", }, /* Divide Floating Memory D,W */ + { 0xE408, 0xFC08, TYPE_A, "MPF", }, /* Multiply Floating Memory D,W */ + { 0xE800, 0xFC00, TYPE_A, "ARM", }, /* Add Register to Memory B,H,W,D */ + { 0xEC00, 0xFF80, TYPE_B, "BU", }, /* Branch Unconditional */ + { 0xEC00, 0xFF80, TYPE_A, "BCT", }, /* Branch Condition True */ + { 0xEC80, 0xFF80, TYPE_B, "BS", }, /* Branch Condition True CC1 = 1 */ + { 0xED00, 0xFF80, TYPE_B, "BGT", }, /* Branch Condition True CC2 = 1 */ + { 0xED80, 0xFF80, TYPE_B, "BLT", }, /* Branch Condition True CC3 = 1 */ + { 0xEE00, 0xFF80, TYPE_B, "BEQ", }, /* Branch Condition True CC4 = 1 */ + { 0xEE80, 0xFF80, TYPE_B, "BGE", }, /* Branch Condition True CC2|CC4 = 1 */ + { 0xEF00, 0xFF80, TYPE_B, "BLE", }, /* Branch Condition True CC3|CC4 = 1 */ + { 0xEF80, 0xFF80, TYPE_B, "BANY", }, /* Branch Condition True CC1|CC2|CC3|CC4 */ + { 0xF000, 0XFF80, TYPE_B, "BFT", }, /* Branch Function True */ + { 0xF000, 0xFF80, TYPE_A, "BCF", }, /* Branch Condition False */ + { 0xF080, 0xFF80, TYPE_B, "BNS", }, /* Branch Condition False CC1 = 0 */ + { 0xF100, 0xFF80, TYPE_B, "BNP", }, /* Branch Condition False CC2 = 0 */ + { 0xF180, 0xFF80, TYPE_B, "BNN", }, /* Branch Condition False CC3 = 0 */ + { 0xF200, 0xFF80, TYPE_B, "BNE", }, /* Branch Condition False CC4 = 0 */ + { 0xF280, 0xFF80, TYPE_B, "BCF 5,", }, /* Branch Condition False CC2|CC4 = 0 */ + { 0xF300, 0xFF80, TYPE_B, "BCF 6,", }, /* Branch Condition False CC3|CC4 = 0 */ + { 0xF380, 0xFF80, TYPE_B, "BAZ", }, /* Branch Condition False CC1|CC2|CC3|CC4=0*/ + { 0xF400, 0xFC70, TYPE_D, "BIB", }, /* Branch after Incrementing Byte */ + { 0xF420, 0xFC70, TYPE_D, "BIH", }, /* Branch after Incrementing Half */ + { 0xF440, 0xFC70, TYPE_D, "BIW", }, /* Branch after Incrementing Word */ + { 0xF460, 0xFC70, TYPE_D, "BID", }, /* Branch after Incrementing Double */ + { 0xF800, 0xFF80, TYPE_E, "ZM", }, /* Zero Memory B,H,W,D */ + { 0xF880, 0xFF80, TYPE_B, "BL", }, /* Branch and Link */ + { 0xF900, 0xFCC0, X|TYPE_B, "BRI", }, /* Branch and Reset Interrupt 32/55 * */ + { 0xF980, 0xFF80, TYPE_B, "LPSD", }, /* Load Program Status Double * */ + { 0xFA08, 0xFC00, TYPE_B, "JWCS", }, /* Jump to Writable Control Store * */ + { 0xFA80, 0xFF80, TYPE_B, "LPSDCM", }, /* LPSD and Change Map * */ + { 0xFB00, 0xFCC0, X|TYPE_A, "TRP", }, /* Transfer Register to Protect Register 32/7X */ + { 0xFB80, 0xFCC0, X|TYPE_A, "TPR", }, /* Transfer Protect Register to Register 32/7X */ + { 0xFC00, 0xFC07, TYPE_L, "EI", }, /* Enable Interrupt */ + { 0xFC01, 0xFC07, TYPE_L, "DI", }, /* Disable Interrupt */ + { 0xFC02, 0xFC07, TYPE_L, "RI", }, /* Request Interrupt */ + { 0xFC03, 0xFC07, TYPE_L, "AI", }, /* Activate Interrupt */ + { 0xFC04, 0xFC07, TYPE_L, "DAI", }, /* Deactivate Interrupt */ + { 0xFC05, 0xFC07, TYPE_M, "TD", }, /* Test Device */ + { 0xFC06, 0xFC07, TYPE_M, "CD", }, /* Command Device */ + { 0xFC17, 0xFC7F, TYPE_C, "SIO", }, /* Start I/O */ + { 0xFC1F, 0xFC7F, TYPE_C, "TIO", }, /* Test I/O */ + { 0xFC27, 0xFC7F, TYPE_C, "STPIO", }, /* Stop I/O */ + { 0xFC2F, 0xFC7F, TYPE_C, "RSCHNL", }, /* Reset Channel */ + { 0xFC37, 0xFC7F, TYPE_C, "HIO", }, /* Halt I/O */ + { 0xFC3F, 0xFC7F, TYPE_C, "GRIO", }, /* Grab Controller */ + { 0xFC47, 0xFC7F, TYPE_C, "RSCTL", }, /* Reset Controller */ + { 0xFC4F, 0xFC7F, TYPE_C, "ECWCS", }, /* Enable Channel WCS Load */ + { 0xFC5F, 0xFC7F, TYPE_C, "WCWCS", }, /* Write Channel WCS */ + { 0xFC67, 0xFC7F, TYPE_C, "ECI", }, /* Enable Channel Interrupt */ + { 0xFC6F, 0xFC7F, TYPE_C, "DCI", }, /* Disable Channel Interrupt */ + { 0xFC77, 0xFC7F, TYPE_C, "ACI", }, /* Activate Channel Interrupt */ + { 0xFC7F, 0xFC7F, TYPE_C, "DACI", }, /* Deactivate Channel Interrupt */ +}; + +/* Instruction decode printing routine + Inputs: + *of = output stream + val = 16/32 bit instruction to print left justified + sw = mode switches, 'M'=base mode, 'N'=nonbase mode +*/ +const char *fc_type = "WHDHBBBB"; /* F & C bit values */ + +int fprint_inst(FILE *of, uint32 val, int32 sw) +{ + uint16 inst = (val >> 16) & 0xFFFF; + int i; + int mode = 0; /* assume non base mode instructions */ + t_opcode *tab; + + if ((PSD[0] & 0x02000000) || (sw & SWMASK('M'))) /* bit 6 is base mode */ + mode = 1; + /* loop through the instruction table for an opcode match and get the type */ + for (tab = optab; tab->name != NULL; tab++) { + if (tab->opbase == (inst & tab->mask)) { + if (mode && (tab->type & (X | N))) + continue; /* non basemode instruction in base mode, skip */ + if (!mode && (tab->type & B)) + continue; /* basemode instruction in nonbase mode, skip */ + + /* TODO? Maybe want to make sure MODEL is 32/7X for X type instructions */ + + /* match found */ + fputs(tab->name, of); /* output the base opcode */ + + /* process the other fields of the instruction */ + switch(tab->type & 0xF) { + /* memory reference instruction */ + case TYPE_A: /* r,[*]o[,x] or r,o[(b)][,x] */ + /* zero memory instruction */ + case TYPE_E: /* [*]o[,x] or o[(b)][,x] */ + /* append B, H, W, D to base instruction using F & C bits */ + i = (val & 3) | ((inst >> 1) & 04); + if (((inst&0xfc00) == 0xe000) || + ((inst&0xfc00) == 0xe400)) + i &= ~4; /* remove f bit from fpt instr */ + if (((inst&0xfc00) != 0xdc00) && + ((inst&0xfc00) != 0xd000) && + ((inst&0xfc00) != 0x5400) && + ((inst&0xfc00) != 0x5800) && + ((inst&0xfc00) != 0x5c00) && + ((inst&0xfc00) != 0xcc00) && + ((inst&0xfc00) != 0x8000)) + fputc(fc_type[i], of); + /* Fall through */ + + /* BIx instructions or bit in memory reference instructions */ + case TYPE_D: /* r,[*]o[,x] or r,o[(b)],[,x] */ + if ((tab->type & 0xF) != TYPE_E) { + fputc(' ', of); +// fputc('R', of); + /* output the reg or bit number */ + fputc('0'+((inst>>7) & 07), of); + fputc(',', of); + } + /* Fall through */ + + /* branch instruction */ + case TYPE_B: /* [*]o[,x] or o[(b)],[,x] */ + if (((tab->type & 0xf) != TYPE_A) && ((tab->type & 0xf) != TYPE_D)) + fputc(' ', of); + if (mode) { + /* base reg mode */ + fprint_val(of, val&0xffff, 16, 16, PV_LEFT); /* output 16 bit offset */ + if (inst & 07) { + fputc('(', of); +// fputc('B', of); + fputc(('0'+(inst & 07)), of); /* output the base reg number */ + fputc(')', of); + } + if (inst & 0x70) { + fputc(',', of); +// fputc('R', of); + fputc(('0'+((inst >> 4) & 07)), of); /* output the index reg number */ + } + } else { + /* nonbase reg mode */ + if (inst & 0x10) + fputc('*', of); /* show indirection */ + fprint_val(of, val&0x7ffff, 16, 19, PV_LEFT); /* 19 bit offset */ + if (inst & 0x60) { + fputc(',', of); /* register coming */ +// fputc('R', of); + if (tab->type != TYPE_D) + fputc('0'+((inst & 0x60) >> 5), of); /* output the index reg number */ + else { + if ((inst & 0xfc00) != 0xf400) + fputc('0'+((inst & 0x60) >> 5), of); /* output the index reg number */ + } + } + } + break; + + /* immediate or XIO instructions */ + case TYPE_C: /* r,v */ + fputc(' ', of); +// fputc('R', of); + fputc('0'+((inst>>7) & 07), of); /* index reg number */ + fputc(',', of); + fprint_val(of, val&0xffff, 16, 16, PV_LEFT); /* 16 bit imm val or chan/suba */ + break; + + /* reg - reg instructions */ + case TYPE_F: /* rs,rd */ + fputc(' ', of); +// fputc('R', of); + fputc('0'+((inst>>4) & 07), of); /* src reg */ + fputc(',', of); +// fputc('R', of); + fputc('0'+((inst>>7) & 07), of); /* dest reg */ + break; + + /* single reg instructions */ + case TYPE_G: /* op r */ + fputc(' ', of); +// fputc('R', of); + fputc('0'+((inst>>7) & 07), of); /* output src/dest reg num */ + break; + + /* just output the instruction */ + case TYPE_H: /* empty */ + break; + + /* reg and bit shift cnt */ + case TYPE_I: /* r,b */ + fputc(' ', of); +// fputc('R', of); + fputc('0'+((inst>>7) & 07), of); /* reg number */ + fputc(',', of); + fprint_val(of, inst&0x1f, 10, 5, PV_LEFT); /* 5 bit shift count */ + break; + + /* register bit operations */ + case TYPE_K: /* r,rb */ + fputc(' ', of); +// fputc('R', of); + fputc('0'+((inst>>4) & 07), of); /* register number */ + fputc(',', of); + i = ((inst & 3) << 3) | ((inst >> 7) & 07); + fprint_val(of, i, 10, 5, PV_LEFT); /* reg bit number to operate on */ + break; + + /* interrupt control instructions */ + case TYPE_L: /* i */ + fputc(' ', of); + fprint_val(of, (inst>>3)&0x7f, 16, 7, PV_RZRO); /* output 7 bit priority level value */ + break; + + /* CD/TD Class E I/O instructions */ + case TYPE_M: /* i,v */ + fputc(' ', of); + fprint_val(of, (inst>>3)&0x7f, 16, 7, PV_RZRO); /* output 7 bit device address */ + fputc(',', of); + fprint_val(of, (val&0xffff), 16, 16, PV_RZRO); /* output 16 bit command code */ + break; + + /* SVC instructions */ + case TYPE_N: /* i,v */ + fputc(' ', of); + fprint_val(of, (val>>12)&0xf, 16, 4, PV_RZRO); /* output 4 bit svc number */ + fputc(',', of); + fprint_val(of, (val & 0xFFF), 16, 12, PV_LEFT); /* output 12 bit command code */ + break; + + default: + /* FIXME - return error code here? */ +// /* fputs(" unknown type", of); /* output error message */ +// return SCPE_ARG; /* unknown type */ + break; + } + /* return the size of the instruction */ + return (tab->type & H) ? 2 : 4; + } + } + /* FIXME - should we just return error here? or dump as hex data? */ + /* we get here if opcode not found, print data value */ + if (mode) + fputs(" Binvld ", of); /* output basemode error message */ + else + fputs(" Ninvld ", of); /* output non-basmode error message */ + fprint_val(of, val, 16, 32, PV_RZRO); /* output unknown 32 bit instruction code */ + return 4; /* show as full word size */ +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) +{ + int i; + int l = 4; /* default to full words */ + int rdx = 16; /* default radex is hex */ + uint32 num; +// uint32 tmp=*val; /* for debug */ + + if (sw & SIM_SW_STOP) { /* special processing for step */ + if (PSD[0] & 0x02000000) { /* bit 6 is base mode */ + sw |= SWMASK('M'); /* display basemode */ + sw &= ~SWMASK('N'); /* no non-based display */ + } else { + sw |= SWMASK('N'); /* display non-basemode */ + sw &= ~SWMASK('M'); /* no basemode display */ + } + } + if (addr & 0x02) + l = 2; + /* determine base for number output */ + if (sw & SWMASK ('D')) + rdx = 10; /* decimal */ + else + if (sw & SWMASK ('O')) + rdx = 8; /* octal */ + else + if (sw & SWMASK ('H')) + rdx = 16; /* hex */ + + if (sw & SWMASK ('M')) { /* machine base mode? */ + sw &= ~ SWMASK('B'); /* Can't do B and M at same time */ + sw &= ~ SWMASK('C'); /* Can't do C and M at same time */ + if (addr & 0x02) + l = 2; + else + l = 4; + } else + if (sw & SWMASK('F')) { + l = 4; /* words are 4 bytes */ + } else + if (sw & SWMASK('W')) { + l = 2; /* halfwords are 2 bytes */ + } else + if (sw & SWMASK('B')) { + l = 1; /* bytes */ + } + + if (sw & SWMASK ('C')) { + fputc('\'', of); /* opening apostorphe */ + for(i = 0; i < l; i++) { + int ch = val[i] & 0xff; /* get the char */ + if (ch >= 0x20 && ch <= 0x7f) /* see if printable */ + fprintf(of, "%c", ch); /* output the ascii char */ + else + fputc('_', of); /* use underscore for unprintable char */ + } + fputc('\'', of); /* closing apostorphe */ + } else + /* go print the symbolic instruction for base or nonbase mode */ + if (sw & (SWMASK('M') | SWMASK('N'))) { + num = 0; + for (i = 0; i < l && i < 4; i++) { + num |= (uint32)val[i] << ((l-i-1) * 8); /* collect 8-32 bit data value to print */ + } + if (addr & 0x02) + num <<= 16; /* use rt hw */ + l = fprint_inst(of, num, sw); /* go print the instruction */ + if (((addr & 2) == 0) && (l == 2)) { /* did we execute a left halfword instruction */ + fprintf(of, "; "); + l = fprint_inst(of, num<<16, sw); /* go print right halfword instruction */ + l = 4; /* next word address */ + } + } else { + /* print the numeric value of the memory data */ + num = 0; + for (i = 0; i < l && i < 4; i++) + num |= (uint32)val[i] << ((l-i-1) * 8); /* collect 8-32 bit data value to print */ + fprint_val(of, num, rdx, l*8, PV_RZRO); /* print it in requested radix */ + } + return -(l-1); /* will be negative if we did anything */ +} + +/* + * Collect offset in radix. + */ +t_stat get_off (CONST char *cptr, CONST char **tptr, uint32 radix, t_value *val, char *m) +{ + t_stat r = SCPE_OK; /* assume OK return */ + + *m = 0; /* left parend found flag if set */ + *val = (uint32)strtotv(cptr, tptr, radix); /* convert to value */ + if (cptr == *tptr) + r = SCPE_ARG; /* no argument found error */ + else { + cptr = *tptr; /* where to start looking */ + while (sim_isspace(*cptr)) + cptr++; /* skip any spaces */ + if (*cptr++ == '(') { + *m = 1; /* show we found a left parend */ + while (sim_isspace(*cptr)) + cptr++; /* skip any spaces */ + } + *tptr = cptr; /* return next char pointer */ + } + return r; /* return status */ +} + +/* + * Collect immediate in radix. + */ +t_stat get_imm (CONST char *cptr, CONST char **tptr, uint32 radix, t_value *val) +{ + t_stat r; + + r = SCPE_OK; + *val = (uint32)strtotv (cptr, tptr, radix); + if ((cptr == *tptr) || (*val > 0xffff)) + r = SCPE_ARG; + else { + cptr = *tptr; + while (sim_isspace (*cptr)) cptr++; + *tptr = cptr; + } + return r; +} + +/* Symbolic input + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ + int i; + int x; + int l = 4; /* default to full words */ + int rdx = 16; /* default radex is hex */ + char mod = 0; + t_opcode *tab; + t_stat r; + uint32 num; + uint32 max[5] = {0, 0xff, 0xffff, 0, 0xffffffff}; + CONST char *tptr; + char gbuf[CBUFSIZE]; + + /* determine base for numbers */ + if (sw & SWMASK ('D')) + rdx = 10; /* decimal */ + else + if (sw & SWMASK ('O')) + rdx = 8; /* octal */ + else + if (sw & SWMASK ('H')) + rdx = 16; /* hex */ + + /* set instruction size */ + if (sw & SWMASK('F')) { + l = 4; + } else + if (sw & SWMASK('W')) { + l = 2; + } + + /* process a character string */ + if (sw & SWMASK ('C')) { + cptr = get_glyph_quoted(cptr, gbuf, 0); /* Get string */ + for(i = 0; gbuf[i] != 0; i++) { + val[i] = gbuf[i]; /* copy in the string */ + } + return -(i - 1); + } + + /* see if we are processing a nonbase instruction */ + if (sw & SWMASK ('N')) { + /* process nonbased instruction */ + cptr = get_glyph(cptr, gbuf, 0); /* Get uppercase opcode */ + l = strlen(gbuf); /* opcode length */ + /* try to find the opcode in the table */ + for (tab = optab; tab->name != NULL; tab++) { + i = tab->type & 0xf; /* get the instruction type */ + /* check for memory reference instruction */ + if (i == TYPE_A || i == TYPE_E) { + /* test for base opcode name without B, H, W, D applied */ + if (sim_strncasecmp(tab->name, gbuf, l - 1) == 0) + break; /* found */ + } else + /* test the full opcode name */ + if (sim_strcasecmp(tab->name, gbuf) == 0) + break; /* found */ + } + if (tab->name == NULL) /* see if anything found */ + return SCPE_ARG; /* no, return invalid argument error */ + num = tab->opbase<<16; /* get the base opcode value */ + + /* process each instruction type */ + switch(i) { + /* mem ref instruction */ + case TYPE_A: /* c r,[*]o[,x] */ + /* zero memory instruction */ + case TYPE_E: /* c [*]o[,x] */ + switch(gbuf[l]) { + case 'B': num |= 0x80000; break; /* byte, set F bit */ + case 'H': num |= 0x00001; break; /* halfword */ + case 'W': num |= 0x00000; break; /* word */ + case 'D': num |= 0x00002; break; /* doubleword */ + default: + return SCPE_ARG; /* base op suffix error */ + } + /* Fall through */ + + /* BIx instructions or memory reference */ + case TYPE_D: /* r,[*]o[,x] */ + while (sim_isspace(*cptr)) + cptr++; /* skip leading blanks */ + if (i != TYPE_E) { + /* get reg number except for zero memory instruction */ + if (*cptr >= '0' || *cptr <= '7') { /* reg# is 0-7 */ + x = *cptr++ - '0'; /* get the reg# */ + while (sim_isspace(*cptr)) + cptr++; /* skip any blanks */ + if (*cptr++ != ',') /* check for required comma */ + return SCPE_ARG; /* anything else is an argument error */ + num |= x << 23; /* position reg number in instruction */ + } else + return SCPE_ARG; /* invalid reg number is an argument error */ + } + /* Fall through */ + + /* branch instruction */ + case TYPE_B: /* [*]o[,x] */ + if (*cptr == '*') { /* test for indirection */ + num |= 0x100000; /* set indirect flag */ + cptr++; /* skip past the '*' */ + while (sim_isspace(*cptr)) + cptr++; /* skip blanks */ + } + if ((r = get_off(cptr, &tptr, 16, val, &mod))) /* get operand address */ + return r; /* argument error if a problem */ + cptr = tptr; /* set pointer to returned next char pointer */ + if (*val > 0x7FFFF) /* 19 bit address max */ + return SCPE_ARG; /* argument error */ + num |= *val; /* or our address into instruction */ + if (mod) { + return SCPE_ARG; /* if a '(' found, that is an arg error */ + } + if (*cptr++ == ',') { /* test for optional index reg number */ + if (*cptr >= '0' || *cptr <= '7') { /* reg# is 0-7 */ + x = *cptr++ - '0'; /* get reg number */ + num |= x << 20; /* position and put into instruction */ + } else + return SCPE_ARG; /* reg# not 0-7, so arg error */ + } + break; + + /* immediate or XIO instruction */ + case TYPE_C: /* r,v */ + while (sim_isspace(*cptr)) + cptr++; /* skip spaces */ + if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */ + x = *cptr++ - '0'; /* get reg# */ + while (sim_isspace(*cptr)) + cptr++; /* skip spaces */ + if (*cptr++ != ',') /* next char need to be a comma */ + return SCPE_ARG; /* it's not, so arg error */ + num |= x << 23; /* position and put into instruction */ + } else + return SCPE_ARG; /* invalid reg#, so arg error */ + while (sim_isspace(*cptr)) + cptr++; /* skip any blanks */ + if ((r = get_imm(cptr, &tptr, rdx, val))) /* get 16 bit immediate value */ + return r; /* return error from conversion */ + num |= *val; /* or in the 16 bit value */ + break; + + /* reg-reg instructions */ + case TYPE_F: /* r,r */ + while (sim_isspace(*cptr)) + cptr++; /* skip blanks */ + if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */ + x = *cptr++ - '0'; /* calc reg# */ + while (sim_isspace(*cptr)) + cptr++; /* skip spaces */ + if (*cptr++ != ',') /* test for required ',' */ + return SCPE_ARG; /* it's not there, so error */ + num |= x << 23; /* insert first reg# into instruction */ + } else + return SCPE_ARG; /* reg# invalid, so arg error */ + while (sim_isspace(*cptr)) + cptr++; /* skip more spaces */ + if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */ + x = *cptr++ - '0'; /* calc reg# */ + while (sim_isspace(*cptr)) + cptr++; /* skip any spaces */ + num |= x << 20; /* insert 2nd reg# into instruction */ + } else + return SCPE_ARG; /* reg# invalid, so arg error */ + break; + + /* single reg instructions */ + case TYPE_G: /* r */ + while (sim_isspace(*cptr)) + cptr++; /* skip blanks */ + if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */ + x = *cptr++ - '0'; /* calc reg# */ + while (sim_isspace(*cptr)) + cptr++; /* skip spaces */ + num |= x << 23; /* insert first reg# into instruction */ + } else + return SCPE_ARG; /* reg# invalid, so arg error */ + break; + + /* opcode only instructions */ + case TYPE_H: /* empty */ + break; + + /* reg and bit shift instructions */ + case TYPE_I: /* r,b */ + while (sim_isspace (*cptr)) + cptr++; /* skip blanks */ + if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */ + x = *cptr++ - '0'; /* calc reg# */ + while (sim_isspace(*cptr)) + cptr++; /* skip spaces */ + if (*cptr++ != ',') /* test for required ',' */ + return SCPE_ARG; /* it's not there, so error */ + num |= (x << 23); /* insert first reg# into instruction */ + } else + return SCPE_ARG; /* reg# invalid, so arg error */ + while (sim_isspace(*cptr)) + cptr++; /* skip any blanks */ + if ((r = get_imm(cptr, &tptr, 10, val))) /* get 5 bit shift value */ + return r; /* return error from conversion */ + if (*val > 0x1f) /* 5 bit max count */ + return SCPE_ARG; /* invalid shift count */ + num |= (*val << 16); /* or in the 5 bit value */ + break; + + /* register bit operations */ + case TYPE_K: /* r,rb */ + while (sim_isspace(*cptr)) + cptr++; /* skip blanks */ + if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */ + x = *cptr++ - '0'; /* calc reg# */ + while (sim_isspace(*cptr)) + cptr++; /* skip spaces */ + if (*cptr++ != ',') /* test for required ',' */ + return SCPE_ARG; /* it's not there, so error */ + num |= (x << 20); /* insert reg# into instruction */ + } else + return SCPE_ARG; /* reg# invalid, so arg error */ + while (sim_isspace(*cptr)) + cptr++; /* skip any blanks */ + if ((r = get_imm(cptr, &tptr, 10, val))) /* get 5 bit bit number */ + return r; /* return error from conversion */ + if (*val > 0x1f) /* 5 bit max count */ + return SCPE_ARG; /* invalid bit count */ + x = *val / 8; /* get 2 bit byte number */ + num |= (x & 3) << 16; /* insert 2 bit byte code into instruction */ + x = *val % 8; /* get bit in byte value */ + num |= (x & 7) << 23; /* or in the bit value */ + break; + + /* interrupt control instructions */ + case TYPE_L: /* i */ + while (sim_isspace(*cptr)) + cptr++; /* skip any blanks */ + if ((r = get_imm(cptr, &tptr, rdx, val))) /* get 7 bit bit number */ + return r; /* return error from conversion */ + if (*val > 0x7f) /* 7 bit max count */ + return SCPE_ARG; /* invalid value */ + num |= (*val & 0x7f) << 19; /* or in the interrupt level */ + break; + + /* CD/TD Class E I/O instructions */ + case TYPE_M: /* d,v */ + while (sim_isspace(*cptr)) + cptr++; /* skip any blanks */ + if ((r = get_imm(cptr, &tptr, rdx, val))) /* get 7 bit bit number */ + return r; /* return error from conversion */ + if (*val > 0x7f) /* 7 bit max count */ + return SCPE_ARG; /* invalid value */ + num |= (*val & 0x7f) << 19; /* or in the device address */ + while (sim_isspace(*cptr)) + cptr++; /* skip any blanks */ + if ((r = get_imm(cptr, &tptr, rdx, val))) /* get 16 bit command code */ + return r; /* return error from conversion */ + num |= *val; /* or in the 16 bit value */ + break; + } + return (tab->type & H) ? 2 : 4; /* done with nonbased instructions */ + } + + /* see if we are processing a base mode instruction */ + if (sw & SWMASK ('M')) { /* base mode? */ + /* process base mode instruction */ + cptr = get_glyph(cptr, gbuf, 0); /* Get uppercase opcode */ + l = strlen(gbuf); /* save the num of char in opcode */ + /* loop through the instruction table for an opcode match and get the type */ + for (tab = optab; tab->name != NULL; tab++) { + i = tab->type & 0xf; /* get the type */ + /* check for memory reference instruction */ + if (i == TYPE_A || i == TYPE_E) { + /* test for base opcode name without B, H, W, D applied */ + if (sim_strncasecmp(tab->name, gbuf, l - 1) == 0) + break; /* found */ + } else + /* test the full opcode name */ + if (sim_strcasecmp(tab->name, gbuf) == 0) + break; /* found */ + } + if (tab->name == NULL) /* see if anything found */ + return SCPE_ARG; /* no, return invalid argument error */ + num = tab->opbase<<16; /* get the base opcode value */ + + /* process each instruction type */ + switch(i) { + /* mem ref instruction */ + case TYPE_A: /* c r,o[(b)][,x] */ + /* zero memory instruction */ + case TYPE_E: /* c o[(b)][,x] */ + switch(gbuf[l]) { + case 'B': num |= 0x80000; break; /* byte, set F bit */ + case 'H': num |= 0x00001; break; /* halfword */ + case 'W': num |= 0x00000; break; /* word */ + case 'D': num |= 0x00002; break; /* doubleword */ + default: + return SCPE_ARG; /* base op suffix error */ + } + /* Fall through */ + + /* BIx instructions or memory reference */ + case TYPE_D: /* r,o[(b)],[,x] */ + while (sim_isspace(*cptr)) + cptr++; /* skip leading blanks */ + if (i != TYPE_E) { + /* get reg number except for zero memory instruction */ + if (*cptr >= '0' || *cptr <= '7') { /* reg# is 0-7 */ + x = *cptr++ - '0'; /* get the reg# */ + while (sim_isspace(*cptr)) + cptr++; /* skip any blanks */ + if (*cptr++ != ',') /* check for required comma */ + return SCPE_ARG; /* anything else is an argument error */ + num |= x << 23; /* position reg number in instruction */ + } else + return SCPE_ARG; /* invalid reg number is an argument error */ + } + /* Fall through */ + + /* branch instruction */ + case TYPE_B: /* o[(b)],[,x] */ + if ((r = get_off(cptr, &tptr, 16, val, &mod))) /* get offset */ + return r; /* argument error if a problem */ + cptr = tptr; /* set pointer to returned next char pointer */ + if (*val > 0xFFFF) /* 16 bit offset max */ + return SCPE_ARG; /* argument error */ + num |= *val; /* or offset into instruction */ + if (mod) { /* see if '(' found in input */ + if (*cptr >= '0' || *cptr <= '7') { /* base reg# 0-7 */ + x = *cptr++ - '0'; /* get reg number */ + while (sim_isspace(*cptr)) + cptr++; /* skip any spaces */ + if (*cptr++ != ')') /* test for closing right parend */ + return SCPE_ARG; /* arg error if not found */ + num |= x << 16; /* put base reg number into instruction */ + } else + return SCPE_ARG; /* no '(' found, so arg error */ + } + if (*cptr++ == ',') { /* test for optional index reg number */ + if (*cptr >= '0' || *cptr <= '7') { /* reg# is 0-7 */ + x = *cptr++ - '0'; /* get reg number */ + num |= x << 20; /* position and put into instruction */ + } else + return SCPE_ARG; /* reg# not 0-7, so arg error */ + } + break; + + /* immediate or XIO instruction */ + case TYPE_C: /* r,v */ + while (sim_isspace(*cptr)) + cptr++; /* skip spaces */ + if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */ + x = *cptr++ - '0'; /* get reg# */ + while (sim_isspace(*cptr)) + cptr++; /* skip spaces */ + if (*cptr++ != ',') /* next char need to be a comma */ + return SCPE_ARG; /* it's not, so arg error */ + num |= x << 23; /* position and put into instruction */ + } else + return SCPE_ARG; /* invalid reg#, so arg error */ + while (sim_isspace(*cptr)) + cptr++; /* skip any blanks */ + if ((r = get_imm(cptr, &tptr, rdx, val))) /* get 16 bit immediate value */ + return r; /* return error from conversion */ + num |= *val; /* or in the 16 bit value */ + break; + + /* reg-reg instructions */ + case TYPE_F: /* r,r */ + while (sim_isspace(*cptr)) + cptr++; /* skip blanks */ + if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */ + x = *cptr++ - '0'; /* calc reg# */ + while (sim_isspace(*cptr)) + cptr++; /* skip spaces */ + if (*cptr++ != ',') /* test for required ',' */ + return SCPE_ARG; /* it's not there, so error */ + num |= x << 23; /* insert first reg# into instruction */ + } else + return SCPE_ARG; /* reg# invalid, so arg error */ + while (sim_isspace(*cptr)) + cptr++; /* skip more spaces */ + if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */ + x = *cptr++ - '0'; /* calc reg# */ + while (sim_isspace(*cptr)) + cptr++; /* skip any spaces */ + num |= x << 20; /* insert 2nd reg# into instruction */ + } else + return SCPE_ARG; /* reg# invalid, so arg error */ + break; + + /* single reg instructions */ + case TYPE_G: /* r */ + while (sim_isspace(*cptr)) + cptr++; /* skip blanks */ + if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */ + x = *cptr++ - '0'; /* calc reg# */ + while (sim_isspace(*cptr)) + cptr++; /* skip spaces */ + num |= x << 23; /* insert first reg# into instruction */ + } else + return SCPE_ARG; /* reg# invalid, so arg error */ + break; + + /* opcode only instructions */ + case TYPE_H: /* empty */ + break; + + /* reg and bit shift instructions */ + case TYPE_I: /* r,b */ + while (sim_isspace(*cptr)) + cptr++; /* skip blanks */ + if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */ + x = *cptr++ - '0'; /* calc reg# */ + while (sim_isspace(*cptr)) + cptr++; /* skip spaces */ + if (*cptr++ != ',') /* test for required ',' */ + return SCPE_ARG; /* it's not there, so error */ + num |= (x << 23); /* insert first reg# into instruction */ + } else + return SCPE_ARG; /* reg# invalid, so arg error */ + while (sim_isspace(*cptr)) + cptr++; /* skip any blanks */ + if ((r = get_imm(cptr, &tptr, 10, val))) /* get 5 bit shift value */ + return r; /* return error from conversion */ + if (*val > 0x1f) /* 5 bit max count */ + return SCPE_ARG; /* invalid shift count */ + num |= (*val << 16); /* or in the 5 bit value */ + break; + + /* register bit operations */ + case TYPE_K: /* r,rb */ + while (sim_isspace(*cptr)) + cptr++; /* skip blanks */ + if (*cptr >= '0' || *cptr <= '7') { /* test for valid reg# */ + x = *cptr++ - '0'; /* calc reg# */ + while (sim_isspace(*cptr)) + cptr++; /* skip spaces */ + if (*cptr++ != ',') /* test for required ',' */ + return SCPE_ARG; /* it's not there, so error */ + num |= (x << 20); /* insert reg# into instruction */ + } else + return SCPE_ARG; /* reg# invalid, so arg error */ + while (sim_isspace(*cptr)) + cptr++; /* skip any blanks */ + if ((r = get_imm(cptr, &tptr, 10, val))) /* get 5 bit bit number */ + return r; /* return error from conversion */ + if (*val > 0x1f) /* 5 bit max count */ + return SCPE_ARG; /* invalid bit count */ + x = *val / 8; /* get 2 bit byte number */ + num |= (x & 3) << 16; /* insert 2 bit byte code into instruction */ + x = *val % 8; /* get bit in byte value */ + num |= (x & 7) << 23; /* or in the bit value */ + break; + + /* interrupt control instructions */ + case TYPE_L: /* i */ + while (sim_isspace(*cptr)) + cptr++; /* skip any blanks */ + if ((r = get_imm(cptr, &tptr, rdx, val))) /* get 7 bit bit number */ + return r; /* return error from conversion */ + if (*val > 0x7f) /* 7 bit max count */ + return SCPE_ARG; /* invalid value */ + num |= (*val & 0x7f) << 19; /* or in the interrupt level */ + break; + + /* CD/TD Class E I/O instructions */ + case TYPE_M: /* d,v */ + while (sim_isspace(*cptr)) + cptr++; /* skip any blanks */ + if ((r = get_imm(cptr, &tptr, rdx, val))) /* get 7 bit bit number */ + return r; /* return error from conversion */ + if (*val > 0x7f) /* 7 bit max count */ + return SCPE_ARG; /* invalid value */ + num |= (*val & 0x7f) << 19; /* or in the device address */ + while (sim_isspace(*cptr)) + cptr++; /* skip any blanks */ + if ((r = get_imm(cptr, &tptr, rdx, val))) /* get 16 bit command code */ + return r; /* return error from conversion */ + num |= *val; /* or in the 16 bit value */ + break; + } + return (tab->type & H) ? 2 : 4; /* done with base mode insrructions */ + } + + /* get here for any other switch value */ + /* this code will get a value based on length specified in switches */ + num = get_uint(cptr, rdx, max[l], &r); /* get the unsigned value */ + for (i = 0; i < l && i < 4; i++) + val[i] = (num >> ((l - (1 + i)) * 8)) & 0xff; /* get 1-4 bytes of data */ + return -(l-1); +} + diff --git a/SEL32/tests/SetupNet b/SEL32/tests/SetupNet new file mode 100755 index 00000000..ef4c3084 --- /dev/null +++ b/SEL32/tests/SetupNet @@ -0,0 +1,635 @@ +#!/bin/sh + +# Copyright (c) 2020, Geert Rolf +# +# 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 +# GEERT ROLF 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. +# + +# Test platforms: +# +# Debian 10 (Buster) +# Raspbian Buster +# FreeBSD 12.1 +# OpenBSD 6.7 +# NetBSD 9.1 + +# CHANGE HISTORY +# 2021/4/8 avoid setting PATH in scripts for root +# 2021/5/31 whereis used without -b in OpenBSD and NetBSD +# Corrected: bridge create and fwdelay done twice for NetBSD +# FreeBSD: tapX opened by userland program not by ifconfig +# 2021/11/17 check for existance of br0 done too lousy. Done better. +# + +# uncomment next line if you need more than one tap = run multiple SIMHs... +#OPSMODE="expert" +# ... by editting the saved params file followed by rerun of this script + +OS=`uname -s` +case $OS in + "FreeBSD" | "OpenBSD" | "NetBSD") + # List of Utils + LOU="ifconfig brconfig sysctl chown netstat" + + # set $util to path of util for all in $LOU + for U in $LOU + do + # whereis -b in only FreeBSD as in Linux + if test $OS = "FreeBSD" + then + eval ${U}="`whereis -b $U | + awk '{ if($2 != "") + print $2; + else + print "void"; + }'`" + else + eval ${U}="`whereis $U | + awk '{ if($1 != "") + print $1; + else + print "void"; + }'`" + fi + done + + # make list of all interfaces ignoring lo + AVL_IF=`$ifconfig -a | + grep -v '^[ ]' | + awk -F: '{ print $1 }' | + grep -v 'lo' | + sed '1,$s/^ //'` + + ## echo $AVL_IF + + # found bridge0 in active interfaces? + L=`echo $AVL_IF | grep bridge0 | wc -l` + if test $L -ne 0 + then + echo "$0: bridge0 already configured." + echo "-- if you configured bridge0 statically don't use this script" + echo "-- otherwise if you do not want permanent settings" + echo "-- and want bridge0 configured differently reboot first" + exit 1 + fi + + ;; + "Linux") + # List of Utils + LOU="ip brctl tunctl netstat" + + # set $util to path of util for all in $LOU + for U in $LOU + do + eval ${U}="`whereis -b $U | + awk '{ if($2 != "") + print $2; + else + print "void"; + }'`" + done + + ERR=0 + # check brctl to exist + if test $brctl = "void" + then + echo 'No brctl program -- please install package bridge-utils' + ERR=1 + fi + + # check tunctl to exist + if test $tunctl = "void" + then + echo 'No tunctl program -- please install package uml-utilities' + ERR=1 + fi + + # quit when either not installed + if test $ERR -eq 1 + then + exit 1 + fi + + # make list of all interfaces ignoring lo + AVL_IF=`$ip link show | + grep '^[0-9]' | + awk -F: '{ print $2 }' | + grep -v 'lo' | + sed '1,$s/^ //'` + + # found br0 in active interfaces? + L=`echo $AVL_IF | tr ' ' '\012' | grep '^br0$' | wc -l` + if test $L -ne 0 + then + echo "$0: br0 already configured." + echo "-- if you configured br0 statically don't use this script" + echo "-- otherwise if you do not want permanent settings" + echo "-- and want br0 configured differently reboot first" + exit 1 + fi + + ;; +esac + +# let user chose the interface he/she wants to use +echo " Available networkinterfaces" +N=0 +for I in $AVL_IF +do + N=`expr $N + 1` + case $OS in + "FreeBSD" | "OpenBSD" | "NetBSD") + IPCMD="$ifconfig $I " + ;; + "Linux") + IPCMD="$ip addr show $I " + ;; + esac + + L=`$IPCMD | + grep 'inet' | + grep -v 'inet6' | + wc -l` + if test $L -eq 0 + then + M="not configured" + else + M=`$IPCMD | + grep 'inet' | + grep -v 'inet6' | + awk '{ print $2 }' ` + fi + + # did we see wlan? alas... + L=`echo $I | grep wlan | wc -l` + if test $L -gt 0 + then + M="$M (wlan not supported)" + echo "-: $I $M" + N=`expr $N - 1` + else + if test -r SnetSaved.$I + then + echo "$N: $I $M (saved params available)" + else + echo "$N: $I $M" + fi + fi +done +# take wlan out +AVL_IF=`echo $AVL_IF | sed 's/wlan.//'` + +if test $N -gt 1 +then + ANSWER=0 + while test $ANSWER -lt 1 -o $ANSWER -gt $N + do + echo -n "Which interface do you want to use? " + read ANSWER + done +else + ANSWER=1 +fi + +N=0 +for I in $AVL_IF +do + N=`expr $N + 1` + if test $N -eq $ANSWER + then + ACT_IF=$I + fi +done + +# see if there are saved params for this interface +if test -r SnetSaved.$ACT_IF +then + . ./SnetSaved.$ACT_IF + echo "Saved params loaded from SnetSaved.$ACT_IF" +fi + +echo "" +echo "Interface to use for SIMH......... " $ACT_IF + +if test "x$IPNR" = "x" +then + case $OS in + "FreeBSD" | "OpenBSD" | "NetBSD") + IPCMD="$ifconfig $ACT_IF " + ;; + "Linux") + IPCMD="$ip addr show dev $ACT_IF " + ;; + esac + + L=`$IPCMD | + grep inet | + grep -v inet6 | + wc -l` + if test $L -eq 0 + then + IPNR="not configured" + else + IPNR=`$IPCMD | + grep inet | + grep -v inet6 | + awk '{ print $2 }' ` + fi +fi +echo "IPnumber on this interface........ " $IPNR + +if test "x$IPBRO" = "x" +then + case $OS in + "FreeBSD" | "OpenBSD" | "NetBSD") + IPCMD="$ifconfig $ACT_IF " + SEDBRO='1s/^.*broadcast //' + ;; + "Linux") + IPCMD="$ip addr show dev $ACT_IF " + SEDBRO='1s/^.*brd //' + ;; + esac + + IPBRO=`$IPCMD | + grep inet | + grep -v inet6 | + sed "$SEDBRO" | + sed '1s/[ ].*$//'` +fi +echo "IP Broadcast on this interface.... " $IPBRO + +if test "x$DEFRT" = "x" +then + case $OS in + "FreeBSD" | "OpenBSD" | "NetBSD") + DEFRT=`$netstat -rn | + grep $ACT_IF | + grep 'default' | + awk '{ print $2 }'` + ;; + "Linux") + DEFRT=`$netstat -rn | + grep $ACT_IF | + grep '^0\.' | + uniq | + awk '{ print $2 }'` + ;; + esac +fi +if test "x$DEFRT" = "x" +then + echo "Default Route set to Gateway...... " none +else + echo "Default Route set to Gateway...... " $DEFRT +fi + +if test "x$OS" = "xLinux" +then + if test "x$IPFWD" = "x" + then + IPFWD=`cat /proc/sys/net/ipv4/ip_forward` + fi + if test $IPFWD -eq 1 + then + echo "IP forwarding is active........... " YES + else + echo "IP forwarding is active........... " NO + fi +fi + +# nr of taps the user wants or 1 for default +if test "x${NrTaps}" = "x" +then + NrTaps=1 +fi +echo "Number of taps to create.......... " $NrTaps + + +# the user who needs access to the tap +if test "x$SimhUser" = "x" +then + case $OS in + "FreeBSD" | "OpenBSD" | "NetBSD") + SimhUser="root" + ;; + "Linux") + if test "x${SUDO_USER}" != "x" + then + SimhUser=${SUDO_USER} + else + SimhUser=${USER} + fi + ;; + esac +fi +echo "User who runs SIMH on the taps.... " $SimhUser + +# only save params when in expert OPSMODE +if test "X$OPSMODE" = "Xexpert" +then + if test ! -r SnetSaved.$ACT_IF + then + cat - > SnetSaved.$ACT_IF < $JOBFILE <<\EOF +#!/bin/sh + +# This script should be run under root permission + +EOF + +cat - > $TMPFILE <<\EOF +echo ${CMD} +${CMD} +if test $? -ne 0 +then + echo '*** FAIL:' ${CMD} + exit 1 +fi + +EOF + +case $OS in + "Linux") + echo "CMD=\"$brctl addbr br0\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + N=0 + while test $N -lt $NrTaps + do + echo "CMD=\"$tunctl -t tap${N} -u ${SimhUser}\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + N=`expr $N + 1` + done + + echo "CMD=\"$brctl addif br0 ${ACT_IF}\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + echo "CMD=\"$brctl setfd br0 0\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + echo "CMD=\"$ip link set ${ACT_IF} down\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + if ! test "$IPNR" = "not configured" + then + echo "CMD=\"$ip addr add ${IPNR} dev br0\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + fi + + if ! test "$IPNR" = "not configured" + then + ## if test ! "x$DEFRT" = "x" + ## then + ## echo "CMD=\"$ip route del default via ${DEFRT} dev ${ACT_IF}\"" >> $JOBFILE + ## cat $TMPFILE >> $JOBFILE + ## fi + echo "CMD=\"$ip addr del ${IPNR} dev ${ACT_IF}\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + fi + + echo "CMD=\"$ip link set ${ACT_IF} up\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + echo "CMD=\"$ip link set br0 up\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + + if test ! "x$DEFRT" = "x" + then + echo "CMD=\"$ip route add default via ${DEFRT} dev br0\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + fi + + ipfwd=`cat /proc/sys/net/ipv4/ip_forward` + if test $IPFWD -ne $ipfwd + then + echo "CMD=\"echo ${IPFWD} > /proc/sys/net/ipv4/ip_forward\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + fi + + N=0 + while test $N -lt $NrTaps + do + echo "CMD=\"$brctl addif br0 tap${N}\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + echo "CMD=\"$ip link set tap${N} up\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + N=`expr $N + 1` + done + BRIDGE="br0" + EXAMPLE="tap:tap0" + + ;; + "FreeBSD") + N=0 + while test $N -lt $NrTaps + do + echo "CMD=\"$ifconfig tap${N} create\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + N=`expr $N + 1` + done + + echo "CMD=\"$sysctl net.link.tap.up_on_open=1\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + echo "CMD=\"$ifconfig bridge0 create\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + case $NrTaps in + 1 ) + echo "CMD=\"$ifconfig bridge0 addm ${ACT_IF} addm tap0\"" >> $JOBFILE + ;; + 2 ) + echo "CMD=\"$ifconfig bridge0 addm ${ACT_IF} addm tap0 addm tap1\"" >> $JOBFILE + ;; + 3 ) + echo "CMD=\"$ifconfig bridge0 addm ${ACT_IF} addm tap0 addm tap1 addm tap2\"" >> $JOBFILE + ;; + 4 ) + echo "CMD=\"$ifconfig bridge0 addm ${ACT_IF} addm tap0 addm tap1 addm tap2 addm tap3\"" >> $JOBFILE + ;; + * ) + echo "Sorry too many taps..." + exit + esac + cat $TMPFILE >> $JOBFILE + + echo "CMD=\"$ifconfig bridge0 up\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + BRIDGE="bridge0" + EXAMPLE="tap:tap0" + ;; + "OpenBSD") + N=0 + while test $N -lt $NrTaps + do + echo "CMD=\"$ifconfig tap${N} create\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + echo "CMD=\"$ifconfig tap${N} up\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + N=`expr $N + 1` + done + + echo "CMD=\"$ifconfig bridge0 create\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + echo "CMD=\"$ifconfig bridge0 fwddelay 4\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + case $NrTaps in + 1 ) + echo "CMD=\"$ifconfig bridge0 add ${ACT_IF} add tap0\"" >> $JOBFILE + ;; + 2 ) + echo "CMD=\"$ifconfig bridge0 add ${ACT_IF} add tap0 add tap1\"" >> $JOBFILE + ;; + 3 ) + echo "CMD=\"$ifconfig bridge0 add ${ACT_IF} add tap0 add tap1 add tap2\"" >> $JOBFILE + ;; + 4 ) + echo "CMD=\"$ifconfig bridge0 add ${ACT_IF} add tap0 add tap1 add tap2 add tap3\"" >> $JOBFILE + ;; + * ) + echo "Sorry too many taps..." + exit + esac + cat $TMPFILE >> $JOBFILE + + echo "CMD=\"$ifconfig bridge0 up\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + BRIDGE="bridge0" + EXAMPLE="tap:tap0" + ;; + "NetBSD") + N=0 + while test $N -lt $NrTaps + do + echo "CMD=\"$ifconfig tap${N} create\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + echo "CMD=\"$ifconfig tap${N} up\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + N=`expr $N + 1` + done + + echo "CMD=\"$ifconfig bridge0 create\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + echo "CMD=\"$brconfig bridge0 fwddelay 1\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + case $NrTaps in + 1 ) + echo "CMD=\"$brconfig bridge0 add ${ACT_IF} add tap0\"" >> $JOBFILE + ;; + 2 ) + echo "CMD=\"$brconfig bridge0 add ${ACT_IF} add tap0 add tap1\"" >> $JOBFILE + ;; + 3 ) + echo "CMD=\"$brconfig bridge0 add ${ACT_IF} add tap0 add tap1 add tap2\"" >> $JOBFILE + ;; + 4 ) + echo "CMD=\"$brconfig bridge0 add ${ACT_IF} add tap0 add tap1 add tap2 add tap3\"" >> $JOBFILE + ;; + * ) + echo "Sorry too many taps..." + exit + esac + cat $TMPFILE >> $JOBFILE + + echo "CMD=\"$brconfig bridge0 up\"" >> $JOBFILE + cat $TMPFILE >> $JOBFILE + + BRIDGE="bridge0" + EXAMPLE="tap:tap0" + ;; +esac + + cat - >> $JOBFILE < detached +; LPR - 7ef8/7ef9 Model 924X High Speed Line Printer +; COM - 7ec0 8512 8-line async communications (N/U) +; CON - 7efc/7efd Console Terminal +; RTC - 7f06 60 HZ Real-Time clock +; ITM - 7f04 38.4MS Interval timer +; MT - 1000 8051 Buffered Tape Processor +; mta0 <- diag.tap +; EC - 0e00 Model 8516 Ethernet (not supported) +;====================================================== +; Set hostname +set env HOST=sel32 +; Set local IP address +; set env IP=192.168.1.5 (N/U) +;====================================================== +; +; Set run limit of 2 minutes +set runlimit 2 minutes +set on +on error ignore +on runtime echof "\r\n*** FAILED - SEL32 Test Runtime Limit %SIM_RUNLIMIT% %SIM_RUNLIMIT_UNITS% Exceeded ***\n"; exit 1 +; +if not exist "diag.tap" echo "\n*** FAILURE diag.tap file missing ***\n"; exit 1 +; +; Set debug output +;set debug -n sel.log +;set debug stderr +; +; CPU type and memory +;set CPU 32/27 2M +;set CPU 32/27 4M +;set CPU 32/87 4M +set CPU 32/67 4M +;set CPU 32/97 4M +;set CPU V6 4M +;set CPU V6 8M +;set CPU V9 4M +;set CPU V9 8M +; +; CPU debug options +;set cpu debug=cmd;exp;inst;detail;trap;xio;irq +; Set instruction trace history size +;;set cpu history=10000 +; useful options +;set cpu debug=exp +;set cpu debug=cmd;exp;irq;trap;xio +;set cpu debug=cmd;irq;trap;exp +;set cpu debug=irq;trap;exp;xio +;set cpu debug=irq;xio +;set cpu debug=irq;exp;trap +; +; RTC realtime clock +set RTC 50 +;set RTC 60 +set RTC enable +; RTC debug options +;set RTC debug=cmd +; +; ITM interval timer +;set ITM debug=cmd +; +; IOP at channel 7e00 +; useful options +;set iop debug=cmd;exp +;set iop debug=cmd +; make iop online +set iop enable +; set iop channel address +set iop0 dev=7e00 +; +; MFP at channel 7e00 +; useful options +;set mfp debug=cmd;exp +; make mfp online +;set mfp enable +; set mfp channel address +;set mfp0 dev=7e00 +;set mfp0 dev=7600 +; +; COM 8-Line +;set com debug=cmd; +;set coml0 enable +;set coml1 enable +;set coml2 enable +;set coml3 enable +;set coml4 enable +;set coml5 enable +;set coml6 enable +;set coml7 enable +; +; Enable telnet sessions on port 4747 +;set comc enable +;at comc 4747 +; +; LPR +;set lpr debug=cmd;detail +;set lpr enable +; LPR output file +;at lpr lprout +; +; CON Console +;set con debug=cmd;exp;detail +; useful options +; enable console +set con enable +; set console address +; set con0 enable +set con0 dev=7efc +; set con1 enable +set con1 dev=7efd +;set con debug=cmd;exp +; +; MTA Buffered tape processor +;set mta debug=cmd;exp;detail;data +; useful options +; +; enable MTA to change channel +set mta enable +; set mta channel +set mta0 dev=1000 +; +; Attach in/out tape files +set mta0 locked +at mta0 diag.tap +;at mta1 temptape.tap +;at mta2 output.tap +; +; DMA disk processor II/UDP +; enable DMA to change channel +;set dma enable +; set disk chan to 0800 +;set dma0 dev=800 +; set disk type to MPX MH300 +;set dma0 type=MH300 +; set disk type to UTX 9346 +;set dma0 type=9346 +;set dma0 type=8155 +;set dma0 type=8887 +;set dma0 type=8148 +; +; Attach diskfile +;at dma0 utx0disk +;at dma0 utx1disk +;at dma0 sim32disk +;at dma debug=cmd;exp;detail;data +;at dma0 diagdisk +; useful options +;set dma debug=cmd;exp +;set dma debug=exp;cmd;detail +; +; SDA SCFI disk processor +;set sda debug=cmd;exp;data;detail +; Attach diskfiles +;at sda0 diskfile4 +;at sda1 diskfile5 +; +; DPA high speed disk processor +; enable the HSDP to change channel +;set dpa enable +; set channel addr +;set dpa dev=800 +; set disk type to UTX 8887 +;set dpa0 type=8887 +; +; Attach diskfiles +;at utxdsk.dsk +;at dpa0 utx0hsdp +;at dpa1 utx1hsdp +; +;set dpa debug=cmd;detail;exp +; useful options +;set dpa debug=cmd;exp +; +; set console switches +deposit CSW 0 +; +;UTX boot tape options +;set GPR 7 to 0x00 to boot in multi-user mode +;set GPR 7 to 0x01 to prompt for unix filename +;set GPR 7 to 0x02 to boot in single user mode +;set GPR 7 to 0x10 to disable swapping and paging +;set GPR 7 to 0x20 to boot from device specified in GPR6 +;set GPR 7 to 0x40 to allow progress messages on boot +;deposit BOOTR[7] 40 +;deposit BOOTR[7] 52 +;deposit BOOTR[7] 42 +;deposit BOOTR[7] 2 +;deposit BOOTR[6] 800 +;deposit BOOTR[0] ffffffff +; +; Set register content at boot for SEL diagnostics +; uncomment next line to get diag loader prompt +;deposit bootr[0] ffffffff +deposit bootr[1] 0 +deposit bootr[2] 0 +; +; allow cpu idle +set cpu idle +; Set expect script for auto time entry on MPX at OPCOM prompt +;expect haltafter=20000 +; wait for expected output from simulator, then enter this text +;expect "??" send " %DATE_MM%/%DATE_DD%/%DATE_YY%,%TIME_HH%:%TIME_MM%:%TIME_SS%\r"; GO +; +; Boot from disk +;bo dpa0 +;bo dma0 +; +; Go to simh on completion of script +expect "DOL>" echof "\r\n*** PASSED - SEL32 Autobatch Diagnostic Successfully Completed\n"; exit 0 +; Boot from mag tape +bo mta0 +;det all +;rm temptape.tap +;rm output.tap +expect "[][]" echof "\r\n*** FAILED - SEL32 Autobatch Diagnostic Failed to Complete\n"; exit 1 +echof "\r\n*** FAILED - SEL32 Autobatch Diagnostic Failed to Complete\n" +exit 1 +;quit diff --git a/SEL32/tests/diag.tap b/SEL32/tests/diag.tap new file mode 100644 index 00000000..1986f76c Binary files /dev/null and b/SEL32/tests/diag.tap differ diff --git a/SEL32/tests/sel32_test.ini b/SEL32/tests/sel32_test.ini new file mode 100644 index 00000000..7e2976e7 --- /dev/null +++ b/SEL32/tests/sel32_test.ini @@ -0,0 +1,226 @@ +cd %~p0 +;====================================================== +; SEL32 System Engineering Labs 32 bit computer +; MPX/UTX CPU diagnostic hardware configuration +; CPU - 32/67 4M Sel32 Concept/32 +; IOP - 7e00 Model 8001 IOP Processor Controller +; DMA - 0800 2311/2314 Disk Processor II (N/U) +; dma0 - N/U +; dma0 <-> detached +; LPR - 7ef8/7ef9 Model 924X High Speed Line Printer +; COM - 7ec0 8512 8-line async communications (N/U) +; CON - 7efc/7efd Console Terminal +; RTC - 7f06 60 HZ Real-Time clock +; ITM - 7f04 38.4MS Interval timer +; MT - 1000 8051 Buffered Tape Processor +; mta0 <- diag.tap +; EC - 0e00 Model 8516 Ethernet (not supported) +;====================================================== +; Set hostname +set env HOST=sel32 +; Set local IP address +; set env IP=192.168.1.5 (N/U) +;====================================================== +; +; Set run limit of 2 minutes +set runlimit 2 minutes +set on +on error ignore +on runtime echof "\r\n*** FAILED - SEL32 Test Runtime Limit %SIM_RUNLIMIT% %SIM_RUNLIMIT_UNITS% Exceeded ***\n"; exit 1 +; +if not exist "diag.tap" echo "\n*** FAILURE diag.tap file missing ***\n"; exit 1 +; +; Set debug output +;set debug -n sel.log +;set debug stderr +; +; CPU type and memory +;set CPU 32/27 2M +;set CPU 32/27 4M +;set CPU 32/87 4M +set CPU 32/67 4M +;set CPU 32/97 4M +;set CPU V6 4M +;set CPU V6 8M +;set CPU V9 4M +;set CPU V9 8M +; +; CPU debug options +;set cpu debug=cmd;exp;inst;detail;trap;xio;irq +; Set instruction trace history size +;;set cpu history=10000 +; useful options +;set cpu debug=exp +;set cpu debug=cmd;exp;irq;trap;xio +;set cpu debug=cmd;irq;trap;exp +;set cpu debug=irq;trap;exp;xio +;set cpu debug=irq;xio +;set cpu debug=irq;exp;trap +; +; RTC realtime clock +set RTC 50 +;set RTC 60 +set RTC enable +; RTC debug options +;set RTC debug=cmd +; +; ITM interval timer +;set ITM debug=cmd +; +; IOP at channel 7e00 +; useful options +;set iop debug=cmd;exp +;set iop debug=cmd +; make iop online +set iop enable +; set iop channel address +set iop0 dev=7e00 +; +; MFP at channel 7e00 +; useful options +;set mfp debug=cmd;exp +; make mfp online +;set mfp enable +; set mfp channel address +;set mfp0 dev=7e00 +;set mfp0 dev=7600 +; +; COM 8-Line +;set com debug=cmd; +;set coml0 enable +;set coml1 enable +;set coml2 enable +;set coml3 enable +;set coml4 enable +;set coml5 enable +;set coml6 enable +;set coml7 enable +; +; Enable telnet sessions on port 4747 +;set comc enable +;at comc 4747 +; +; LPR +;set lpr debug=cmd;detail +;set lpr enable +; LPR output file +;at lpr lprout +; +; CON Console +;set con debug=cmd;exp;detail +; useful options +; enable console +set con enable +; set console address +; set con0 enable +set con0 dev=7efc +; set con1 enable +set con1 dev=7efd +;set con debug=cmd;exp +; +; MTA Buffered tape processor +;set mta debug=cmd;exp;detail;data +; useful options +; +; enable MTA to change channel +set mta enable +; set mta channel +set mta0 dev=1000 +; +; Attach in/out tape files +set mta0 locked +at mta0 diag.tap +;at mta1 temptape.tap +;at mta2 output.tap +; +; DMA disk processor II/UDP +; enable DMA to change channel +;set dma enable +; set disk chan to 0800 +;set dma0 dev=800 +; set disk type to MPX MH300 +;set dma0 type=MH300 +; set disk type to UTX 9346 +;set dma0 type=9346 +;set dma0 type=8155 +;set dma0 type=8887 +;set dma0 type=8148 +; +; Attach diskfile +;at dma0 utx0disk +;at dma0 utx1disk +;at dma0 sim32disk +;at dma debug=cmd;exp;detail;data +;at dma0 diagdisk +; useful options +;set dma debug=cmd;exp +;set dma debug=exp;cmd;detail +; +; SDA SCFI disk processor +;set sda debug=cmd;exp;data;detail +; Attach diskfiles +;at sda0 diskfile4 +;at sda1 diskfile5 +; +; DPA high speed disk processor +; enable the HSDP to change channel +;set dpa enable +; set channel addr +;set dpa dev=800 +; set disk type to UTX 8887 +;set dpa0 type=8887 +; +; Attach diskfiles +;at utxdsk.dsk +;at dpa0 utx0hsdp +;at dpa1 utx1hsdp +; +;set dpa debug=cmd;detail;exp +; useful options +;set dpa debug=cmd;exp +; +; set console switches +deposit CSW 0 +; +;UTX boot tape options +;set GPR 7 to 0x00 to boot in multi-user mode +;set GPR 7 to 0x01 to prompt for unix filename +;set GPR 7 to 0x02 to boot in single user mode +;set GPR 7 to 0x10 to disable swapping and paging +;set GPR 7 to 0x20 to boot from device specified in GPR6 +;set GPR 7 to 0x40 to allow progress messages on boot +;deposit BOOTR[7] 40 +;deposit BOOTR[7] 52 +;deposit BOOTR[7] 42 +;deposit BOOTR[7] 2 +;deposit BOOTR[6] 800 +;deposit BOOTR[0] ffffffff +; +; Set register content at boot for SEL diagnostics +; uncomment next line to get diag loader prompt +;deposit bootr[0] ffffffff +deposit bootr[1] 0 +deposit bootr[2] 0 +; +; allow cpu idle +set cpu idle +; Set expect script for auto time entry on MPX at OPCOM prompt +;expect haltafter=20000 +; wait for expected output from simulator, then enter this text +;expect "??" send " %DATE_MM%/%DATE_DD%/%DATE_YY%,%TIME_HH%:%TIME_MM%:%TIME_SS%\r"; GO +; +; Boot from disk +;bo dpa0 +;bo dma0 +; +; Go to simh on completion of script +expect "DOL>" echof "\r\n*** PASSED - SEL32 Autobatch Diagnostic Successfully Completed\n"; exit 0 +; Boot from mag tape +bo mta0 +;det all +;rm temptape.tap +;rm output.tap +expect "[][]" echof "\r\n*** FAILED - SEL32 Autobatch Diagnostic Failed to Complete\n"; exit 1 +echof "\r\n*** FAILED - SEL32 Autobatch Diagnostic Failed to Complete\n" +exit 1 +;quit diff --git a/Visual Studio Projects/SEL32.vcproj b/Visual Studio Projects/SEL32.vcproj new file mode 100755 index 00000000..077355f1 --- /dev/null +++ b/Visual Studio Projects/SEL32.vcproj @@ -0,0 +1,565 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/Simh.sln b/Visual Studio Projects/Simh.sln old mode 100644 new mode 100755 index dc0e7674..3a213c5a --- a/Visual Studio Projects/Simh.sln +++ b/Visual Studio Projects/Simh.sln @@ -383,6 +383,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PDP10-KS", "PDP10-KS.vcproj {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SEL32", "SEL32.vcproj", "{9B214A06-3727-44D4-99B7-2C3E44B86B32}" + ProjectSection(ProjectDependencies) = postProject + {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -697,6 +702,10 @@ Global {7F4EB115-2027-4582-A6EB-6A7DD9BA6F11}.Debug|Win32.Build.0 = Debug|Win32 {7F4EB115-2027-4582-A6EB-6A7DD9BA6F11}.Release|Win32.ActiveCfg = Release|Win32 {7F4EB115-2027-4582-A6EB-6A7DD9BA6F11}.Release|Win32.Build.0 = Release|Win32 + {9B214A06-3727-44D4-99B7-2C3E44B86B32}.Debug|Win32.ActiveCfg = Debug|Win32 + {9B214A06-3727-44D4-99B7-2C3E44B86B32}.Debug|Win32.Build.0 = Debug|Win32 + {9B214A06-3727-44D4-99B7-2C3E44B86B32}.Release|Win32.ActiveCfg = Release|Win32 + {9B214A06-3727-44D4-99B7-2C3E44B86B32}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/doc/sel32_doc.doc b/doc/sel32_doc.doc new file mode 100644 index 00000000..b3d4c440 Binary files /dev/null and b/doc/sel32_doc.doc differ diff --git a/makefile b/makefile index fda3c6f1..2baa5453 100644 --- a/makefile +++ b/makefile @@ -126,6 +126,10 @@ ifneq (3,${SIM_MAJOR}) VIDEO_USEFUL = true endif endif +# building the SEL32 networking can be used +ifneq (,$(findstring sel32,${MAKECMDGOALS})) + NETWORK_USEFUL = true +endif # building the PDP-7 needs video support ifneq (,$(findstring pdp7,${MAKECMDGOALS})) VIDEO_USEFUL = true @@ -2075,9 +2079,18 @@ SIGMA = ${SIGMAD}/sigma_cpu.c ${SIGMAD}/sigma_sys.c ${SIGMAD}/sigma_cis.c \ ${SIGMAD}/sigma_coc.c ${SIGMAD}/sigma_dk.c ${SIGMAD}/sigma_dp.c \ ${SIGMAD}/sigma_fp.c ${SIGMAD}/sigma_io.c ${SIGMAD}/sigma_lp.c \ ${SIGMAD}/sigma_map.c ${SIGMAD}/sigma_mt.c ${SIGMAD}/sigma_pt.c \ - ${SIGMAD}/sigma_rad.c ${SIGMAD}/sigma_rtc.c ${SIGMAD}/sigma_tt.c + ${SIGMAD}/sigma_rad.c ${SIGMAD}/sigma_rtc.c ${SIGMAD}/sigma_tt.c SIGMA_OPT = -I ${SIGMAD} +SEL32D = ${SIMHD}/SEL32 +SEL32 = ${SEL32D}/sel32_cpu.c ${SEL32D}/sel32_sys.c ${SEL32D}/sel32_chan.c \ + ${SEL32D}/sel32_iop.c ${SEL32D}/sel32_com.c ${SEL32D}/sel32_con.c \ + ${SEL32D}/sel32_clk.c ${SEL32D}/sel32_mt.c ${SEL32D}/sel32_lpr.c \ + ${SEL32D}/sel32_scfi.c ${SEL32D}/sel32_fltpt.c ${SEL32D}/sel32_disk.c \ + ${SEL32D}/sel32_hsdp.c ${SEL32D}/sel32_mfp.c ${SEL32D}/sel32_scsi.c \ + ${SEL32D}/sel32_ec.c ${SEL32D}/sel32_defs.h +SEL32_OPT = -I $(SEL32D) -DUSE_INT32 -DSEL32 ${NETWORK_OPT} + ### ### Experimental simulators ### @@ -2131,7 +2144,7 @@ ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 \ i7094 ibm1130 id16 id32 sds lgp h316 cdc1700 \ swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 intel-mds \ scelbi 3b2 i701 i704 i7010 i7070 i7080 i7090 \ - sigma uc15 pdp10-ka pdp10-ki pdp10-kl pdp10-ks pdp6 i650 + sigma uc15 pdp10-ka pdp10-ki pdp10-kl pdp10-ks pdp6 i650 sel32 all : ${ALL} @@ -2561,6 +2574,15 @@ ifneq (,$(call find_test,${S3D},s3)) $@ $(call find_test,${S3D},s3) ${TEST_ARG} endif +sel32: $(BIN)sel32$(EXE) + +${BIN}sel32${EXE}: ${SEL32} ${SIM} + ${MKDIRBIN} + ${CC} ${SEL32} ${SIM} ${SEL32_OPT} $(CC_OUTSPEC) ${LDFLAGS} +ifneq (,$(call find_test,${SEL32D},sel32)) + $@ $(call find_test,${SEL32D},sel32) $(TEST_ARG) +endif + altair : ${BIN}altair${EXE} ${BIN}altair${EXE} : ${ALTAIR} ${SIM}