From 45246a33395f197a87339998f01bbad8df2448ec Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Wed, 21 Mar 2012 06:14:01 -0700 Subject: [PATCH] Updated HP2100 from Dave Bryan --- HP2100/hp2100_baci.c | 58 +- HP2100/hp2100_bugfixes.txt | 389 +++- HP2100/hp2100_cpu.c | 38 +- HP2100/hp2100_cpu0.c | 1 + HP2100/hp2100_cpu4.c | 7 +- HP2100/hp2100_cpu5.c | 6 +- HP2100/hp2100_cpu7.c | 5 +- HP2100/hp2100_defs.h | 32 +- HP2100/hp2100_di.c | 1927 ++++++++++++++++++ HP2100/hp2100_di.h | 296 +++ HP2100/hp2100_di_da.c | 2127 ++++++++++++++++++++ HP2100/hp2100_diag.txt | 621 +++++- HP2100/hp2100_dp.c | 47 +- HP2100/hp2100_dq.c | 13 +- HP2100/hp2100_dr.c | 13 +- HP2100/hp2100_ds.c | 2686 ++++++++++++-------------- HP2100/hp2100_fp1.c | 5 +- HP2100/hp2100_ipl.c | 16 +- HP2100/hp2100_lps.c | 8 +- HP2100/hp2100_lpt.c | 8 +- HP2100/hp2100_mpx.c | 60 +- HP2100/hp2100_ms.c | 25 +- HP2100/hp2100_mt.c | 15 +- HP2100/hp2100_mux.c | 44 +- HP2100/hp2100_pif.c | 19 +- HP2100/hp2100_stddev.c | 27 +- HP2100/hp2100_sys.c | 86 +- HP2100/hp_disclib.c | 2407 +++++++++++++++++++++++ HP2100/hp_disclib.h | 380 ++++ Visual Studio Projects/HP2100.vcproj | 20 + descrip.mms | 4 +- makefile | 3 +- 32 files changed, 9699 insertions(+), 1694 deletions(-) create mode 100644 HP2100/hp2100_di.c create mode 100644 HP2100/hp2100_di.h create mode 100644 HP2100/hp2100_di_da.c create mode 100644 HP2100/hp_disclib.c create mode 100644 HP2100/hp_disclib.h diff --git a/HP2100/hp2100_baci.c b/HP2100/hp2100_baci.c index ac695f6a..063705a6 100644 --- a/HP2100/hp2100_baci.c +++ b/HP2100/hp2100_baci.c @@ -1,6 +1,6 @@ /* hp2100_baci.c: HP 12966A buffered asynchronous communications interface simulator - Copyright (c) 2007-2011, J. David Bryan + Copyright (c) 2007-2012, J. David Bryan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,7 @@ BACI 12966A BACI card + 10-Feb-12 JDB Deprecated DEVNO in favor of SC 28-Mar-11 JDB Tidied up signal handling 26-Oct-10 JDB Changed I/O signal handler for revised signal model 25-Nov-08 JDB Revised for new multiplexer library SHOW routines @@ -358,12 +359,6 @@ t_bool baci_enq_seen = FALSE; /* ENQ seen flag */ uint32 baci_enq_cntr = 0; /* ENQ seen counter */ -/* Terminal multiplexer library interface */ - -TMLN baci_ldsc = { 0 }; /* line descriptor */ -TMXR baci_desc = { 1, 0, 0, &baci_ldsc }; /* device descriptor */ - - /* BACI local routines */ static int32 service_time (uint32 control_word); @@ -387,12 +382,14 @@ t_stat baci_detach (UNIT *uptr); /* BACI data structures + baci_ldsc BACI terminal multiplexer line descriptor + baci_desc BACI terminal multiplexer device descriptor baci_dib BACI device information block - baci_dev BACI device descriptor baci_unit BACI unit list baci_reg BACI register list baci_mod BACI modifier list baci_deb BACI debug list + baci_dev BACI device descriptor Two units are used: one to handle character I/O via the Telnet library, and another to poll for connections and input. The character I/O service routine @@ -405,10 +402,13 @@ t_stat baci_detach (UNIT *uptr); ten millisecond period. */ -DIB baci_dib = { &baci_io, BACI, 0 }; - DEVICE baci_dev; +TMLN baci_ldsc = { 0 }; /* line descriptor */ +TMXR baci_desc = { 1, 0, 0, &baci_ldsc }; /* device descriptor */ + +DIB baci_dib = { &baci_io, BACI, 0 }; + UNIT baci_unit[] = { { UDATA (&baci_term_svc, UNIT_ATTABLE | UNIT_FASTTIME, 0) }, /* terminal I/O unit */ { UDATA (&baci_poll_svc, UNIT_DIS, POLL_FIRST) } /* Telnet poll unit */ @@ -444,11 +444,12 @@ REG baci_reg[] = { { FLDATA (ENQFLAG, baci_enq_seen, 0), REG_HRO }, { DRDATA (ENQCNTR, baci_enq_cntr, 16), REG_HRO }, - { FLDATA (LKO, baci.lockout, 0) }, - { FLDATA (CTL, baci.control, 0) }, - { FLDATA (FLG, baci.flag, 0) }, - { FLDATA (FBF, baci.flagbuf, 0) }, - { FLDATA (SRQ, baci.srq, 0) }, + { FLDATA (LKO, baci.lockout, 0) }, + { FLDATA (CTL, baci.control, 0) }, + { FLDATA (FLG, baci.flag, 0) }, + { FLDATA (FBF, baci.flagbuf, 0) }, + { FLDATA (SRQ, baci.srq, 0) }, + { ORDATA (SC, baci_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, baci_dib.select_code, 6), REG_HRO }, { NULL } }; @@ -470,7 +471,8 @@ MTAB baci_mod[] = { { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, &baci_desc }, { MTAB_XTD | MTAB_VDV, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &baci_desc }, - { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &baci_dev }, + { MTAB_XTD | MTAB_VDV, 0, "SC", "SC", &hp_setsc, &hp_showsc, &baci_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &baci_dev }, { 0 } }; @@ -500,7 +502,7 @@ DEVICE baci_dev = { &baci_attach, /* attach routine */ &baci_detach, /* detach routine */ &baci_dib, /* device information block */ - DEV_NET | DEV_DEBUG | DEV_DISABLE, /* device flags */ + DEV_DEBUG | DEV_DISABLE, /* device flags */ 0, /* debug control flags */ baci_deb, /* debug flag name table */ NULL, /* memory size change routine */ @@ -786,8 +788,8 @@ return stat_data; transmit mode enables the output of the FIFO to be unloaded into the transmitter holding register (THR). Characters received or transmitted pass through the receiver register (RR) or transmitter register (TR), - respectively. They are not strictly necessary in terminal (Telnet) - transactions but are critical to diagnostic operations. + respectively. They are not strictly necessary in terminal transactions but + are critical to diagnostic operations. The UART signals an overrun if a complete character is received while the RHR still contains the previous character. The BACI does not use this signal, @@ -854,7 +856,7 @@ while (xmit_loop && (baci_uart_thr & IN_VALID)) { /* valid character in UA "ENQ count = %d\n", baci_enq_cntr); } - else { /* character is not an ENQ */ + else { /* character is not ENQ or not fast timing */ baci_enq_cntr = 0; /* reset ENQ counter */ if (is_attached) { /* attached to network? */ @@ -980,12 +982,16 @@ return status; characters. If characters are available, the terminal I/O service routine is scheduled. It starts when the socket is attached and stops when the socket is detached. + + As there is only one line, we only poll for a new connection when the line is + disconnected. */ t_stat baci_poll_svc (UNIT *uptr) { -if (baci_term.flags & UNIT_ATT) { /* attached to network? */ - if (tmxr_poll_conn (&baci_desc) >= 0) /* new connection established? */ +if (baci_term.flags & UNIT_ATT) { /* attached to line? */ + if ((baci_ldsc.conn == 0) && /* line not connected? */ + (tmxr_poll_conn (&baci_desc) >= 0)) /* and new connection established? */ baci_ldsc.rcve = 1; /* enable line to receive */ tmxr_poll_rx (&baci_desc); /* poll for input */ @@ -1205,9 +1211,9 @@ return; /* Calculate service time from baud rate. - Service times are based on 1580 instructions per second, which is the 1000 - E-Series execution speed. The "external clock" rate uses the 9600 baud rate, - as most real terminals were set to their maximum rate. + Service times are based on 1580 instructions per millisecond, which is the + 1000 E-Series execution speed. The "external clock" rate uses the 9600 baud + rate, as most real terminals were set to their maximum rate. Note that the RTE driver has a race condition that will trip if the service time is less than 1500 instructions. Therefore, these times cannot be @@ -1216,6 +1222,8 @@ return; static int32 service_time (uint32 control_word) { +/* Baud Rates 0- 7 : ext., 50, 75, 110, 134.5, 150, 300, 600, */ +/* Baud Rates 8-15 : 900, 1200, 1800, 2400, 3600, 4800, 7200, 9600 */ static const int32 ticks [] = { 1646, 316000, 210667, 143636, 117472, 105333, 52667, 26333, 17556, 13667, 8778, 6583, 4389, 3292, 2194, 1646 }; diff --git a/HP2100/hp2100_bugfixes.txt b/HP2100/hp2100_bugfixes.txt index 68ef6e55..da1e22a1 100644 --- a/HP2100/hp2100_bugfixes.txt +++ b/HP2100/hp2100_bugfixes.txt @@ -1,6 +1,6 @@ HP 2100 SIMULATOR BUG FIX WRITEUPS ================================== - Last update: 2011-06-21 + Last update: 2012-03-17 1. PROBLEM: Booting from magnetic tape reports "HALT instruction, P: 77756 @@ -4433,7 +4433,7 @@ (except for binary mode). A correct negotiation mechanism must be implemented to handle the variety of Telnet clients properly. - WORKAROUND: Modify the TNS_SKIP case in "tmxr_poll_rx" (sim_txmxr.c) to + RESOLUTION: Modify the TNS_SKIP case in "tmxr_poll_rx" (sim_txmxr.c) to skip only LF or NUL following CR. Any other character is processed as is. STATUS: Fixed in version 3.8-0. @@ -5379,7 +5379,7 @@ RESOLUTION: Modify "do_arg" (scp.c) to test "do_arg[0]" for NULL and to return SCPE_2FARG if so. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5412,7 +5412,7 @@ pending, skip instruction execution. This allows consecutive DMA cycles without intervening instruction executions if SRQ is asserted continuously. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5438,7 +5438,7 @@ inhibit DMA channel 2 if a channel 1 request is still pending after a channel 1 cycle. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5454,7 +5454,7 @@ RESOLUTION: Modify the device names (hp2100_cpu.c, hp2100_defs.h, and hp2100_sys.c) from 0 and 1 to 1 and 2 to align with HP usage. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5470,7 +5470,7 @@ RESOLUTION: Removed obsolete comments. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5487,7 +5487,7 @@ RESOLUTION: Modify hp2100_cpu.c to remove the latency counter. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5522,7 +5522,7 @@ modify all I/O device handlers to allow processing of multiple concurrent signals. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5542,7 +5542,7 @@ and modify all signal handlers accordingly. Modify all device DIBs to add card numbers to allow for multiple-device handlers. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5577,7 +5577,7 @@ DIAG mode in three instructions if the CPU is a 2114, 2115, or 2116 and in two instructions otherwise. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5608,7 +5608,7 @@ RESOLUTION: Modify "mtcio" (hp2100_mt.c) to use the count of command table entries as the loop count. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5625,7 +5625,7 @@ RESOLUTION: Modify "iplio" (hp2100_ipl.c) to add a CRS invocation counter and to report a single debug line for consecutive CRS calls. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5647,7 +5647,7 @@ the device signal handler, and modify "sim_instr" to stop instruction execution if the returned status is not SCPE_OK. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5678,7 +5678,7 @@ if the I/O cycle completes successfully, and modify "iplio" (hp2100_ipl.c) to allow for restarting of a failed I/O cycle. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5697,7 +5697,7 @@ RESOLUTION: Expanded and completed the comments in the "divide" function (hp2100_fp1.c). - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5719,7 +5719,7 @@ unloaded" condition as Not Ready and Busy, and a "disabled" condition as Not Ready only. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5767,7 +5767,7 @@ Head-Sector Compare Error (Head-Sector and Cylinder Compare Errors can only occur during sparing operations which are not supported in simulation). - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5828,7 +5828,7 @@ RESOLUTION: Modify the Read Without Verify command handler in "ds_svc_u" (hp2100_ds.c) to begin verifying if a track boundary is crossed. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5884,7 +5884,7 @@ Unavailable if so. Modify "ds_svc_u" to check that the heads are loaded on the target unit and report Status-2 if not. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. @@ -5902,4 +5902,351 @@ the status-1 field and to check for invalid units and report Unit Unavailable if so. - STATUS: Fixed in version 3.8-2. + STATUS: Fixed in version 3.9-0. + + + +232. PROBLEM: SHOW doesn't show the unit number when all but one unit are + disabled. + + VERSION: 3.8-1 + + OBSERVATION: For multi-unit devices, the SHOW command prints device + information on the first line and then prints each unit's information on + succeeding lines. For single-unit devices, the device and unit information + are combined on one line, as the device name is allowed as a synonym for + unit 0. However, if a multi-unit device has all but one unit disabled, the + SHOW command reports the remaining unit as though the device had only + one unit, implying that the enabled unit is unit 0. + + For example, HP device DQC has two units. Attaching a file to unit 1 and + disabling unit 0 produces this output for the SHOW DQC command: + + DQC, devno=24/25, 11MW, attached to file.tmp, heads loaded, write enabled + + There is no indication that the file is attached to unit 1, and indeed if + the attachment and disabled units are reversed, the command output is the + same as above. + + CAUSE: Routine "show_device" (scp.c) combines the device and unit display + when a device has only one enabled unit. This is intended to hide the + implementation detail of single-unit devices, e.g., paper tape readers, + while allowing additional permanently-disabled units to be used by the + device for scheduling. However, it should not combine the device and units + when user-disabled units exist. + + RESOLUTION: Modify "show_device" (scp.c) to count units that have been + disabled by the user instead of units that may be disabled by the user, and + to report the unit number if user-disabled units are present. Also change + the count of reported units from the number of enabled units to the sum of + the enabled and user-disabled units. + + STATUS: Fixed in version 3.9-0. + + + +233. ENHANCEMENT: Add a simulation of the ICD series of disc drives and the + 12821A Disc Interface. + + VERSION: 3.8-1 + + OBSERVATION: The ICD drives were lower-cost versions of the earlier MAC + drives that incorporated single-drive controllers in the drive chassis. + This obviated the requirement for the separate 13037 disc controller. They + were interfaced via the 12821A HP-IB Disc Interface card; this card was + also used to interface CS/80 disc and Amigo tape drives, such as the 7912 + and 7974A. + + The addition of an ICD simulation allows preparation and direct exchange of + image files with the "HPDrive" disc emulator to enable hardware CPUs to run + with emulated drives. + + RESOLUTION: Add a simulation of the 12821A Disc Interface (hp2100_di.c + and hp2100_di.h). Add the DA device to simulate the ICD disc drives and + the ICD disc loader ROM (hp2100_di_da.c). Generalize the controller code + in the DS simulation into a common disc simulation library (hp_disclib.c + and hp_disclib.h). Alter "hp2100_sys.c" and "hp2100_defs.h" to add the + device structure and default select code assignment. + + STATUS: Fixed in version 3.9-0. + + + +234. ENHANCEMENT: Revise the simulator and documentation to use the term + "select code" instead of "device number." + + VERSION: 3.8-1 + + OBSERVATION: The HP2100 simulator and documentation use the terms "device + number," "device address," and "DEVNO" to refer to the I/O addresses of the + CPU interface cards. These terms are alien to HP users; all of the CPU and + interface documentation, from the 2116 through the 1000, use the term + "select code" for this property. + + With the addition of the 12821A disc interface and associated HP-IB drives, + the terms in use are now confusing as well. The switches on the drives + that set the bus addresses are labelled, "HP-IB DEVICE ADDRESS." Other HP + disc and tape drive manuals refer to "unit addresses" or "unit numbers" to + indicate the addresses that differentiate multiple drives on a given + interface. + + It would be clearer, especially to new users, if the existing terms were + deprecated in preference to "select code" in the simulator and + documentation. + + RESOLUTION: Modify all I/O device simulators to add "SC" as an alias for + "DEVNO" in the register and modifier tables, retaining "DEVNO" to preserve + backward compatibility with existing procedures and command files. Add + MTAB_NMO to the DEVNO modifier entry so that it does not appear in EXAMINE + or SHOW lists but can still be displayed or modified if requested + explicitly. Add hp_setsc and hp_showsc functions (hp2100_sys.c) to set and + show the select code, and modify hp_setdev and hp_showdev to call hp_setsc + and hp_showsc, respectively, and to work aroung newline suppression for + MTAB_NMO entries. Modify the documentation to change device number + references to select code references. + + STATUS: Fixed in version 3.9-0. + + + +235. ENHANCEMENT: Deprecate the device name CLK in favor of TBG. + + VERSION: 3.8-1 + + OBSERVATION: The CLK device provides a simulation of the 12539C Time Base + Generator interface. This interface is universally known to HP users as + the TBG. It would be clearer for these users if the device were named TBG. + + RESOLUTION: Modify clk_dev (hp_stddev.c) to add "TBG" as the logical name. + This still allows use of the CLK name for existing command files. + + STATUS: Fixed in version 3.9-0. + + + +236. PROBLEM: The 13037 disc controller indicates a data under/overrun + incorrectly. + + VERSION: 3.8-1 + + OBSERVATION: The 13037 disc simulator monitors the data transfer during a + read or write operation. A data overrun is indicated if SRQ is still set + when a data transfer is ready, indicating that the previous transfer has + not been handled yet by DCPC. However, the interface contains a 16-word + FIFO, so an overrun should be indicated only if the FIFO is full when a + read transfer is ready or empty when a write transfer is ready. + + A read transfer writes a word to the FIFO and sets SRQ. Currently, DCPC + must remove the word and clear SRQ before the next read transfer occurs, + even though 15 empty slots still remain in the FIFO. Similarly, a write + transfer reads a word from the FIFO and sets SRQ. DCPC must supply the + next word and clear SRQ before the next write transfer occurs, even though + available words may remain in the FIFO. Effectively, the FIFO doesn't + exist, as the simulator treats it as a single-word register. + + Moreover, the SRQ generation logic does not attempt to keep the FIFO full + for a write or empty for a read. If DCPC activity on the other channel + delays the DS channel, even by one word, an overrun is indicated, even + though available space remains in the FIFO. + + CAUSE: Incomplete FIFO implementation. + + RESOLUTION: Modify the read and write data transfer logic (hp2100_ds.c) to + indicate a data overrun when the FIFO is full or empty, respectively, and + extend the SRQ logic to continue requesting DCPC transfers until the FIFO + is empty (read) or has five words present (write), as in the hardware. + + STATUS: Fixed in version 3.9-0. + + + +237. PROBLEM: The 13037 disc controller Clear command clears too much. + + VERSION: 3.8-1 + + OBSERVATION: In hardware, the Clear command issues a Controller Preset + (CPS) tag to all connected disc drives. The description of CPS says that + it clears drive faults, the drive head and sector registers, the drive + illegal head and sector address flip-flops, and the seek check, first + status, drive fault, and attention status bits. In simulation, the + "ds_clear" routine clears the drive current cylinder register and all + status bits. + + CAUSE: Incorrect implementation. + + RESOLUTION: Modify "ds_clear" (hp_disclib.c) to clear just the indicted + drive status. + + STATUS: Fixed in version 3.9-0. + + + +238. PROBLEM: The 13037 Recalibrate command clears the End-of-Cylinder flag. + + VERSION: 3.8-1 + + OBSERVATION: The 13037 disc controller provides the Recalibrate command to + recover from Cylinder Compare errors. Recalibrate does not alter the + cylinder, head, or sector address in the controller. This allows a Read to + follow the Recalibrate directly without requiring an intervening Seek. + + However, the DS simulator clears the EOC flag. This flag indicates that + the controller cylinder address must be incremented before it is used by + the read or write routines. Therefore, a Read following a Recalibrate will + begin at the wrong address if the last successful read ended after the + last sector on a track. + + CAUSE: Oversight. + + RESOLUTION: Modify the "ds_opflags" table (hp2100_ds.c) to remove the + CMF_CLREC flag from the Recalibrate entry. + + STATUS: Fixed in version 3.9-0. + + + +239. PROBLEM: The 13037 Request Status command reports Normal Completion for an + invalid unit. + + VERSION: 3.8-1 + + OBSERVATION: The Request Status command includes a unit number field to + specify the disc drive whose status is returned in the second word. The + unit number is checked for validity, and a "unit not present" status is + returned if the number is invalid. However, if the unit number is illegal + (i.e., > 10), the command sets Normal Completion status instead of Unit + Unavailable status. + + CAUSE: The status is set without checking for unit number legality. + + RESOLUTION: Modify "ds_svc_c" (hp2100_ds.c) to set Unit Unavailable status + if the supplied unit number is greater than 10. + + STATUS: Fixed in version 3.9-0. + + + +240. PROBLEM: The 13037 Cold Load Read and Seek commands do not set Seek Check + if issued while a seek is in progress. + + VERSION: 3.8-1 + + OBSERVATION: In hardware, the read, write, and recalibrate commands wait + for seek completion if they are issued while the heads are positioning. + The Cold Load Read and Seek commands do not; they issue a seek to the drive + without checking. The drive rejects a seek while the heads are in motion + and sets Seek Check status. In simulation, however, the Cold Load Read and + Seek commands wait for seek completion before seeking. + + CAUSE: Oversight. + + RESOLUTION: Modify the "ds_opflags" table (hp2100_ds.c) to remove the + CMF_UIDLE flag from the Cold Load Read and Seek entries, and modify + "ds_docmd" to check for a seek in progress when processing the Cold Load + Read and Seek commands. + + STATUS: Fixed in version 3.9-0. + + + +241. PROBLEM: A 13037 Seek command followed by Read does not set the busy flag. + + VERSION: 3.8-1 + + OBSERVATION: In hardware, a Read command (e.g.) may be issued while a seek + is in progress. The controller firmware sets the busy flag to indicate + that the command was accepted. In simulation, however, the Read command is + not started until the seek is complete, so the busy flag is clear. A + program checking the busy flag will conclude that the Read was rejected. + + CAUSE: The busy flag is set after the check for a seek in progress, and + the firmware wait is modeled by leaving the command pending on the + interface until the seek completes. + + RESOLUTION: Modify "ds_docmd" (hp2100_ds.c) to eliminate the command + holdoff and instead model the wait for seek completion by changing the unit + function from "seek completion" to "read initiation" (e.g.). + + STATUS: Fixed in version 3.9-0. + + + +242. PROBLEM: The DO -E command continues to execute commands after a VM error. + + VERSION: 3.8-1 + + OBSERVATION: According to the manual, when invoking a DO command file and + specifying the -E switch, "command processing (including nested command + invocations) will be aborted if any error is encountered." Errors that + occur as a result of commands, e.g., an invalid unit name supplied to the + ATTACH command, correctly terminate the DO command. However, errors that + occur when running the simulated CPU (e.g., a host I/O error) do not; the + error is printed on the console, but the command file continues to execute. + + CAUSE: When the -E switch is specified, the DO command processor checks + the return status from each command invoked and aborts execution of the + command file if the return is not SCPE_OK. When a RUN, GO, STEP, CONT, or + BOOT command is invoked, the run command processor calls the VM-provided + instruction execution function to start the simulation. + + When simulation is stopped, the instruction executor returns a status code + to indicate why execution has stopped. The reason might be a VM-defined + condition (e.g., execution of a "halt" instruction) or a system condition + (e.g., a host file system I/O error). The run command processor uses this + code to print an error message, but then returns SCPE_OK to the caller. As + the DO command processor never sees the error message, it continues to + execute commands. + + RESOLUTION: Modify "run_cmd" (scp.c) to return the correct status instead + of SCPE_OK. Consolidate the error printing code from "run_cmd", "main", + and "do_cmd" into a new "fprint_error" function that combines both VM stop + and command error messages. Modify the error checkers in "main" and + "do_cmd" to call "fprint_error" to avoid printing messages from "run_cmd" + twice. + + STATUS: Fixed in version 3.9-0. + + + +243. ENHANCEMENT: Modify the 13037 disc simulator to use the common disc + controller library. + + VERSION: 3.8-1 + + OBSERVATION: The 13037 (MAC) and 13365 (ICD) disc controllers are almost + identical. Altering the DS simulator to use the controller library + introduced with the DA simulator would reduce code size, ease maintenance, + and ensure that controller bug fixes propagate to both simulators. + + RESOLUTION: Modify the 13037 simulator (hp2100_ds.c) to call routines in + the common disc controller library (hp_disclib.c). + + STATUS: Fixed in version 3.9-0. + + + +244. ENHANCEMENT: Add debug printout support to the 13037 disc simulator. + + VERSION: 3.8-1 + + OBSERVATION: Debugging the disc controller behavior would be easier if the + internal state of the simulator was observable and recordable. + + RESOLUTION: Modify "hp2100_ds.c" to add debug-mode printouts. + + STATUS: Fixed in version 3.9-0. + + + +245. ENHANCEMENT: Eliminate the poll for parameters to 13037 disc commands. + + VERSION: 3.8-1 + + OBSERVATION: The DS simulator repeatedly polls the CPU interface for the + parameters to certain disc commands, such as Seek. It would be more + efficient to wait for a parameter to be output by the CPU. + + RESOLUTION: Modify "ds_io" (hp2100_ds.c) to activate the controller + service only when a parameter word has been received with an ioIOO signal. + + STATUS: Fixed in version 3.9-0. diff --git a/HP2100/hp2100_cpu.c b/HP2100/hp2100_cpu.c index 0f7331b5..7ccb3c4e 100644 --- a/HP2100/hp2100_cpu.c +++ b/HP2100/hp2100_cpu.c @@ -1,6 +1,6 @@ /* hp2100_cpu.c: HP 21xx/1000 CPU simulator - Copyright (c) 1993-2011, Robert M. Supnik + Copyright (c) 1993-2012, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -29,6 +29,8 @@ DMA1,DMA2 12607B/12578A/12895A direct memory access controller DCPC1,DCPC2 12897B dual channel port controller + 13-Jan-12 JDB Minor speedup in "is_mapped" + Added casts to cpu_mod, dmasio, dmapio, cpu_reset, dma_reset 07-Apr-11 JDB Fixed I/O return status bug for DMA cycles Failed I/O cycles now stop on failing instruction 28-Mar-11 JDB Tidied up signal handling @@ -729,17 +731,17 @@ REG cpu_reg[] = { Reference Handbook. */ MTAB cpu_mod[] = { - { UNIT_MODEL_MASK, UNIT_2116, "", "2116", &cpu_set_model, &cpu_show_model, "2116" }, - { UNIT_MODEL_MASK, UNIT_2115, "", "2115", &cpu_set_model, &cpu_show_model, "2115" }, - { UNIT_MODEL_MASK, UNIT_2114, "", "2114", &cpu_set_model, &cpu_show_model, "2114" }, - { UNIT_MODEL_MASK, UNIT_2100, "", "2100", &cpu_set_model, &cpu_show_model, "2100" }, - { UNIT_MODEL_MASK, UNIT_1000_E, "", "1000-E", &cpu_set_model, &cpu_show_model, "1000-E" }, - { UNIT_MODEL_MASK, UNIT_1000_E, NULL, "21MX-E", &cpu_set_model, &cpu_show_model, "1000-E" }, - { UNIT_MODEL_MASK, UNIT_1000_M, "", "1000-M", &cpu_set_model, &cpu_show_model, "1000-M" }, - { UNIT_MODEL_MASK, UNIT_1000_M, NULL, "21MX-M", &cpu_set_model, &cpu_show_model, "1000-M" }, + { UNIT_MODEL_MASK, UNIT_2116, "", "2116", &cpu_set_model, &cpu_show_model, (void *) "2116" }, + { UNIT_MODEL_MASK, UNIT_2115, "", "2115", &cpu_set_model, &cpu_show_model, (void *) "2115" }, + { UNIT_MODEL_MASK, UNIT_2114, "", "2114", &cpu_set_model, &cpu_show_model, (void *) "2114" }, + { UNIT_MODEL_MASK, UNIT_2100, "", "2100", &cpu_set_model, &cpu_show_model, (void *) "2100" }, + { UNIT_MODEL_MASK, UNIT_1000_E, "", "1000-E", &cpu_set_model, &cpu_show_model, (void *) "1000-E" }, + { UNIT_MODEL_MASK, UNIT_1000_E, NULL, "21MX-E", &cpu_set_model, &cpu_show_model, (void *) "1000-E" }, + { UNIT_MODEL_MASK, UNIT_1000_M, "", "1000-M", &cpu_set_model, &cpu_show_model, (void *) "1000-M" }, + { UNIT_MODEL_MASK, UNIT_1000_M, NULL, "21MX-M", &cpu_set_model, &cpu_show_model, (void *) "1000-M" }, #if defined (HAVE_INT64) - { UNIT_MODEL_MASK, UNIT_1000_F, "", "1000-F", &cpu_set_model, &cpu_show_model, "1000-F" }, + { UNIT_MODEL_MASK, UNIT_1000_F, "", "1000-F", &cpu_set_model, &cpu_show_model, (void *) "1000-F" }, #endif { MTAB_XTD | MTAB_VDV, 1, "IDLE", "IDLE", &cpu_set_idle, &cpu_show_idle, NULL }, @@ -2442,13 +2444,15 @@ return; static t_bool is_mapped (uint32 va) { -uint32 dms_fence = dms_sr & MST_FENCE; /* get BP fence value */ +uint32 dms_fence; if (va >= 02000) /* above the base bage? */ return TRUE; /* always mapped */ -else +else { + dms_fence = dms_sr & MST_FENCE; /* get BP fence value */ return (dms_sr & MST_FLT) ? (va < dms_fence) : /* below BP fence and lower portion mapped? */ (va >= dms_fence); /* or above BP fence and upper portion mapped? */ + } } @@ -3073,7 +3077,7 @@ return stat_data; uint32 dmasio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data) { -const CHANNEL ch = dibptr->card_index; /* DMA channel number */ +const CHANNEL ch = (CHANNEL) dibptr->card_index; /* DMA channel number */ uint16 data; IOSIGNAL signal; IOCYCLE working_set = signal_set; /* no SIR handler needed */ @@ -3155,7 +3159,7 @@ return stat_data; uint32 dmapio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data) { -const CHANNEL ch = dibptr->card_index; /* DMA channel number */ +const CHANNEL ch = (CHANNEL) dibptr->card_index; /* DMA channel number */ uint16 data; IOSIGNAL signal; IOCYCLE working_set = IOADDSIR (signal_set); /* add ioSIR if needed */ @@ -3459,7 +3463,7 @@ if (M == NULL) { /* initial call after st else /* not defined */ return SCPE_IERR; /* internal error */ - M = calloc (PASIZE, sizeof (uint16)); /* alloc mem */ + M = (uint16 *) calloc (PASIZE, sizeof (uint16)); /* alloc mem */ if (M == NULL) /* alloc fail? */ return SCPE_MEM; @@ -3504,7 +3508,7 @@ return SCPE_OK; t_stat dma_reset (DEVICE *dptr) { DIB *dibptr = (DIB *) dptr->ctxt; /* DIB pointer */ -const CHANNEL ch = dibptr->card_index; /* DMA channel number */ +const CHANNEL ch = (CHANNEL) dibptr->card_index; /* DMA channel number */ if (UNIT_CPU_MODEL != UNIT_2114) /* 2114 has only one channel */ hp_enbdis_pair (dma_dptrs [ch], /* make specified channel */ @@ -3980,7 +3984,7 @@ switch (sel) { break; case 3: /* DS boot */ - ibl_copy (ds_rom,dev); + ibl_copy (ds_rom, dev); break; } diff --git a/HP2100/hp2100_cpu0.c b/HP2100/hp2100_cpu0.c index 2e7e6eb2..838d2883 100644 --- a/HP2100/hp2100_cpu0.c +++ b/HP2100/hp2100_cpu0.c @@ -98,6 +98,7 @@ 105521 105301 "Closed loop" (trap cell handler) 105522 105302 [unknown] [test] 105524 105304 [self test] + -- 105310 7974 boot loader ROM extension Notes: diff --git a/HP2100/hp2100_cpu4.c b/HP2100/hp2100_cpu4.c index acc39862..3da7a9d3 100644 --- a/HP2100/hp2100_cpu4.c +++ b/HP2100/hp2100_cpu4.c @@ -1,6 +1,6 @@ /* hp2100_cpu4.c: HP 1000 FPP/SIS - Copyright (c) 2006-2008, J. David Bryan + Copyright (c) 2006-2012, J. David Bryan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,7 @@ CPU4 Floating Point Processor and Scientific Instruction Set + 06-Feb-12 JDB Added OPSIZE casts to fp_accum calls in .FPWR/.TPWR 11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h 05-Sep-08 JDB Removed option-present tests (now in UIG dispatchers) 18-Mar-08 JDB Fixed B register return bug in /CMRT @@ -1074,7 +1075,7 @@ switch (entry) { /* decode IR<3:0> */ exponent = exponent - 1; O = 0; /* clear overflow */ - fp_accum (&op[2], (fp_f + p)); /* acc = arg */ + fp_accum (&op[2], (OPSIZE) (fp_f + p)); /* acc = arg */ while (exponent-- > 0) { O = O | fp_exec ((uint16) (0054 | p), /* square acc */ @@ -1086,7 +1087,7 @@ switch (entry) { /* decode IR<3:0> */ i = i << 1; } - op[2] = fp_accum (NULL, (fp_f + p)); /* get accum */ + op[2] = fp_accum (NULL, (OPSIZE) (fp_f + p)); /* get accum */ if (op[2].fpk[0] == 0) /* result zero? */ O = 1; /* underflow */ diff --git a/HP2100/hp2100_cpu5.c b/HP2100/hp2100_cpu5.c index 00dec44e..6285ef10 100644 --- a/HP2100/hp2100_cpu5.c +++ b/HP2100/hp2100_cpu5.c @@ -1,7 +1,7 @@ /* hp2100_cpu5.c: HP 1000 RTE-6/VM VMA and RTE-IV EMA instructions Copyright (c) 2007-2008, Holger Veit - Copyright (c) 2006-2008, J. David Bryan + Copyright (c) 2006-2011, J. David Bryan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -26,6 +26,7 @@ CPU5 RTE-6/VM and RTE-IV firmware option instructions + 28-Dec-11 JDB Eliminated unused variable in "cpu_ema_vset" 11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h 05-Sep-08 JDB Removed option-present tests (now in UIG dispatchers) 30-Jul-08 JDB Redefined ABORT to pass address, moved def to hp2100_cpu.h @@ -970,7 +971,7 @@ uint32 scalars = op[3].word; /* S4 */ uint32 vectors = op[4].word; /* S5 */ uint32 k = op[5].word; /* S6 */ uint32 imax = 0; /* imax S11*/ -uint32 xidex,idext1,mseg,phys, addr, i, MA; +uint32 xidex, idext1, mseg, addr, i, MA; t_bool negflag = FALSE; for (i=0; i> 1) & MSEGMASK; /* S9 get logical start MSEG */ -phys = idext1 & 01777; /* phys start of EMA */ for (i=0; ifpk[0] & 0100000) -static void vis_abs(OP* in, uint32 opsize) +static void vis_abs(OP* in, OPSIZE opsize) { uint32 sign = GET_MSIGN(in); /* get sign */ if (sign) (void)fp_pcom(in, opsize); /* if negative, make positive */ diff --git a/HP2100/hp2100_defs.h b/HP2100/hp2100_defs.h index f999867b..3118e2d1 100644 --- a/HP2100/hp2100_defs.h +++ b/HP2100/hp2100_defs.h @@ -1,6 +1,6 @@ /* hp2100_defs.h: HP 2100 simulator definitions - Copyright (c) 1993-2011, Robert M. Supnik + Copyright (c) 1993-2012, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,10 +23,12 @@ be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 10-Feb-12 JDB Added hp_setsc, hp_showsc functions to support SC modifier 28-Mar-11 JDB Tidied up signal handling 29-Oct-10 JDB DMA channels renamed from 0,1 to 1,2 to match documentation 27-Oct-10 JDB Revised I/O signal enum values for concurrent signals Revised I/O macros for new signal handling + 09-Oct-10 JDB Added DA and DC device select code assignments 07-Sep-08 JDB Added POLL_FIRST to indicate immediate connection attempt 15-Jul-08 JDB Rearranged declarations with hp2100_cpu.h 26-Jun-08 JDB Rewrote device I/O to model backplane signals @@ -170,6 +172,8 @@ typedef enum { INITIAL, SERVICE } POLLMODE; /* poll synchronization #define MUXL 040 /* 12920A lower data */ #define MUXU 041 /* 12920A upper data */ #define MUXC 042 /* 12920A control */ +#define DI_DA 043 /* 12821A Disc Interface with Amigo disc devices */ +#define DI_DC 044 /* 12821A Disc Interface with CS/80 disc and tape devices */ #define OPTDEV 002 /* start of optional devices */ #define CRSDEV 006 /* start of devices that receive CRS */ @@ -178,19 +182,19 @@ typedef enum { INITIAL, SERVICE } POLLMODE; /* poll synchronization /* IBL assignments */ -#define IBL_V_SEL 14 /* ROM select */ +#define IBL_V_SEL 14 /* ROM select <15:14> */ #define IBL_M_SEL 03 -#define IBL_PTR 0000000 /* PTR */ -#define IBL_DP 0040000 /* disk: DP */ -#define IBL_DQ 0060000 /* disk: DQ */ -#define IBL_MS 0100000 /* option 0: MS */ -#define IBL_DS 0140000 /* option 1: DS */ -#define IBL_MAN 0010000 /* RPL/man boot */ -#define IBL_V_DEV 6 /* dev in <11:6> */ +#define IBL_PTR 0000000 /* ROM 0: 12992K paper tape reader (PTR) */ +#define IBL_DP 0040000 /* ROM 1: 12992A 7900 disc (DP) */ +#define IBL_DQ 0060000 /* ROM 1: 12992A 2883 disc (DQ) */ +#define IBL_MS 0100000 /* ROM 2: 12992D 7970 tape (MS) */ +#define IBL_DS 0140000 /* ROM 3: 12992B 7905/06/20/25 disc (DS) */ +#define IBL_MAN 0010000 /* RPL/manual boot <13:12> */ +#define IBL_V_DEV 6 /* select code <11:6> */ #define IBL_OPT 0000070 /* options in <5:3> */ -#define IBL_DP_REM 0000001 /* DP removable */ -#define IBL_DS_HEAD 0000003 /* DS head number */ -#define IBL_LNT 64 /* boot ROM length */ +#define IBL_DP_REM 0000001 /* DP removable <0:0> */ +#define IBL_DS_HEAD 0000003 /* DS head number <1:0> */ +#define IBL_LNT 64 /* boot ROM length in words */ #define IBL_MASK (IBL_LNT - 1) /* boot length mask */ #define IBL_DPC (IBL_LNT - 2) /* DMA ctrl word */ #define IBL_END (IBL_LNT - 1) /* last location */ @@ -453,10 +457,12 @@ extern void hp_enbdis_pair (DEVICE *ccp, DEVICE *dcp); extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); extern const char *fmt_char (uint8 ch); +extern t_stat hp_setsc (UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat hp_showsc (FILE *st, UNIT *uptr, int32 val, void *desc); extern t_stat hp_setdev (UNIT *uptr, int32 val, char *cptr, void *desc); extern t_stat hp_showdev (FILE *st, UNIT *uptr, int32 val, void *desc); -/* Standard device functions */ +/* Device-specific functions */ extern int32 sync_poll (POLLMODE poll_mode); diff --git a/HP2100/hp2100_di.c b/HP2100/hp2100_di.c new file mode 100644 index 00000000..4b790ff1 --- /dev/null +++ b/HP2100/hp2100_di.c @@ -0,0 +1,1927 @@ +/* hp2100_di.c: HP 12821A HP-IB Disc Interface simulator + + Copyright (c) 2010-2012, J. David Bryan + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + DI 12821A Disc Interface + + 13-Feb-12 JDB First release + 15-Dec-11 JDB Added dummy DC device for diagnostics + 09-Oct-10 JDB Created DI simulation + + References: + - HP 12821A Disc Interface Installation and Service Manual (12821-90006, + Feb-1985) + - IEEE Standard Digital Interface for Programmable Instrumentation + (IEEE-488A-1980, Sep-1979) + + + The 12821A was a high-speed implementation of the Hewlett-Packard Interface + Bus (HP-IB, formalized as IEEE Std. 488-1978). It was used to interface + HP-IB disc and tape devices, such as the HP 7906H, 7908A, and 7974A, to the + HP 1000 running RTE-IVB and RTE-6/VM. Three device command protocols were + supported by the I/O drivers: Amigo discs by driver DVA32, CS/80 discs by + DVM33, and Amigo tapes by DVS23. + + In an RTE environment, the 12821A was the system controller. While + electrically compatible with the HP-IB specification and capable of receiving + addressing commands from the bus, the 12821A did not use the full IEEE-488 + protocol. Card talker and listener states were set by bits in a control + register, rather than by receiving talk and listen commands over the bus. + The bus address of the card could be set via DIP switches, but this feature + was only used by the diagnostic. + + The card supported packed and unpacked transfers across the bus. Up to four + devices could be connected to each card; the limit was imposed by the maximum + electrical loading on the bus compatible with the high data rate. + + The 12821A had a 16-word FIFO buffer and could sustain DCPC transfers of one + megabyte per second. Burst transfers by the CPU to fill or empty the FIFO + could run at the full bandwidth of the I/O backplane. This could hold off + lower-priority devices for 10-15 microseconds until the card slowed down to + the rate of the disc or tape. + + Card assembly 12821-60003 was revised to add a DCPC pacing option. Placing + jumper W1 in position A inhibited SRQ for one I/O cycle in six to allow a + lower-priority interface card to transfer one word. Position B allowed SRQ + to assert continuously as it did on the earlier card assembly 12821-60001. + + The simulator is logically partitioned into three sets of functions: the + interface card simulation, the HP-IB bus simulation, and the device + simulation. This is the card simulation and the card portion of the HP-IB + simulation. Separate files for the tape and disc devices contain the device + simulations and the device portions of the HP-IB simulations. + + This simulation is written to allow the definition of multiple DI cards in a + system. The RTE operating system provided separate I/O drivers for the Amigo + disc, Amigo tape, and CS/80 disc devices. As only one I/O driver could + control a given interface, separate interfaces were required if more than one + device class was installed. For example, it was not possible to install an + Amigo disc and an Amigo tape on the same interface card. + + + Implementation notes: + + 1. The simulator behaves as though card switches S1-S7 are initially closed, + providing a card bus address of 0. The address may be changed with the + SET ADDRESS=n command. Only addresses 0-7 are supported, and the + address may duplicate a device bus address without conflict, as the + address is only used during the diagnostic when devices are disconnected. + + 2. The simulator behaves as though card switch S8 is open, enabling the card + to be the system controller. This cannot be changed by the user. + + 3. The simulator behaves as though card jumper W1 (DCPC pacing) is in + position B. This cannot be changed by the user. +*/ + + + +#include "hp2100_defs.h" +#include "hp2100_di.h" + + + +/* Program constants */ + +#define SW8_SYSCTL 1 /* card is always the system controller (switch 8) */ + +#define IFC_TIMEOUT 157 /* 157 instructions = ~ 100 microseconds */ + +#define CONTROLLER 31 /* dummy unit number for DI */ + + +/* Character constants */ + +#define LF '\012' + + +/* Control Word Register */ + +#define CNTL_SRQ 0100000 /* enable service request interrupt */ +#define CNTL_IFC 0040000 /* assert IFC, enable IFC interrupt */ +#define CNTL_REN 0020000 /* assert remote enable */ +#define CNTL_IRL 0010000 /* enable input-register-loaded interrupt */ +#define CNTL_LBO 0004000 /* enable last-byte-out interrupt */ +#define CNTL_LF 0002000 /* enable line feed terminator */ +#define CNTL_EOI 0001000 /* assert end or identify */ +#define CNTL_ATN 0000400 /* assert attention */ +#define CNTL_DIAG 0000200 /* diagnostic loopback */ +#define CNTL_NRFD 0000100 /* assert not ready for data */ +#define CNTL_PPE 0000040 /* parallel poll enable */ +#define CNTL_ODD 0000020 /* odd number of bytes */ +#define CNTL_PACK 0000010 /* packed data transfer */ +#define CNTL_LSTN 0000004 /* listen */ +#define CNTL_TALK 0000002 /* talk */ +#define CNTL_CIC 0000001 /* controller in charge */ + + +/* Status Word Register */ + +#define STAT_SRQBUS 0100000 /* service request bus state */ +#define STAT_IFCBUS 0040000 /* interface clear bus state */ +#define STAT_RENBUS 0020000 /* remote enable bus state */ +#define STAT_IRL 0010000 /* input register loaded */ +#define STAT_LBO 0004000 /* last byte out */ +#define STAT_LBI 0002000 /* last byte in */ +#define STAT_EOIBUS 0001000 /* end or identify */ +#define STAT_ATNBUS 0000400 /* attention */ +#define STAT_IFC 0000200 /* interface clear seen */ +#define STAT_ODD 0000020 /* odd number of bytes */ +#define STAT_SYSCTL 0000010 /* system controller */ +#define STAT_LSTN 0000004 /* listener */ +#define STAT_TALK 0000002 /* talker */ +#define STAT_CIC 0000001 /* controller in charge */ + + +/* Data word */ + +#define DATA_LBO 0100000 /* last byte out */ +#define DATA_EOI 0001000 /* end or identify */ +#define DATA_ATN 0000400 /* attention */ + + +/* Tag word */ + +#define BUS_SHIFT 16 /* left shift count to align BUS_ATN, EOI with tag */ +#define DATA_SHIFT 8 /* left shift count to align DATA_ATN, EOI with tag */ + +#define TAG_ATN 0000200000 /* bit 16: attention */ +#define TAG_EOI 0000400000 /* bit 17: end-or-identify */ +#define TAG_EDT 0001000000 /* bit 18: end of data transfer */ +#define TAG_LBR 0002000000 /* bit 19: last byte received */ + +#define TAG_MASK (TAG_ATN | TAG_EOI | TAG_EDT | TAG_LBR) + + +/* FIFO access modes */ + +#define FIFO_EMPTY (di_card->fifo_count == 0) /* FIFO empty test */ +#define FIFO_FULL (di_card->fifo_count == FIFO_SIZE) /* FIFO full test */ + +typedef enum { + bus_access, /* per-byte access */ + cpu_access, /* per-word access */ + diag_access /* mixed access */ + } FIFO_ACCESS; + + +/* Disc interface state variables */ + +DI_STATE di [card_count]; /* per-card state */ + + +/* Disc interface global VM routines */ + +IOHANDLER di_io; +t_stat di_reset (DEVICE *dptr); + +/* Disc interface global SCP routines */ + +t_stat di_set_address (UNIT *uptr, int32 value, char *cptr, void *desc); +t_stat di_show_address (FILE *st, UNIT *uptr, int32 value, void *desc); +t_stat di_set_cable (UNIT *uptr, int32 value, char *cptr, void *desc); +t_stat di_show_cable (FILE *st, UNIT *uptr, int32 value, void *desc); + +/* Disc interface global bus routines */ + +t_bool di_bus_source (CARD_ID card, uint8 data); +void di_bus_control (CARD_ID card, uint32 unit, uint8 assert, uint8 deny); +void di_poll_response (CARD_ID card, uint32 unit, FLIP_FLOP response); + +/* Disc interface local bus routines */ + +static t_bool di_bus_accept (CARD_ID card, uint8 data); +static void di_bus_respond (CARD_ID card, uint8 cntl); +static void di_bus_poll (CARD_ID card); + +/* Disc interface local utility routines */ + +static void master_reset (CARD_ID card); +static void update_state (CARD_ID card); +static void fifo_load (CARD_ID card, uint16 data, FIFO_ACCESS access); +static uint16 fifo_unload (CARD_ID card, FIFO_ACCESS access); +static void fprint_bus (FILE *file, char *format, uint8 cntl); + + + +/* Dummy DC device. + + This dummy device allows the DI diagnostic to test inter-card signals. Test + 15 can only be performed if there are two DIs available. + + This device provides a second "bare" card. Normally, it is disabled and + cannot be enabled by the user. Enabling or disabling DIAG mode on the DA + device automatically enables or disables the DC device. The select code of + the DC device is fixed at 45B and cannot be changed. +*/ + +DIB dc_dib = { &di_io, DI_DC, dc }; + +REG dc_reg [] = { + { BRDATA (FIFO, di [dc].fifo, 8, 20, FIFO_SIZE), REG_CIRC }, /* needed for "qptr" */ + { NULL } + }; + +DEVICE dc_dev = { + "DC", /* device name */ + NULL, /* unit array */ + dc_reg, /* register array */ + NULL, /* modifier array */ + 0, /* number of units */ + 10, /* address radix */ + 31, /* address width */ + 1, /* address increment */ + 8, /* data radix */ + 8, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &di_reset, /* reset routine */ + NULL, /* boot routine */ + NULL, /* attach routine */ + NULL, /* detach routine */ + &dc_dib, /* device information block */ + DEV_DEBUG | DEV_DIS, /* device flags */ + 0, /* debug control flags */ + di_deb, /* debug flag name table */ + NULL, /* memory size change routine */ + NULL }; /* logical device name */ + + + +/* DI data structures. + + *dptrs device pointers + *bus_accept device acceptor function pointers + *bus_respond device responder function pointers + + di_deb DI debug table + + The first three pointer arrays have elements that correspond one-for-one with + the supported devices. These allow the DI simulator to work with multiple + cards. The actual devices are defined in the individual device simulators. + + Note that the DC and MA devices are reserved for future use. Until one or + the other is fully implemented, a dummy DC device is provided above for use + by the diagnostic only. +*/ + +extern DEVICE da_dev; + +static DEVICE *dptrs [card_count] = { &da_dev, &dc_dev, NULL }; +static ACCEPTOR *bus_accept [card_count] = { &da_bus_accept, NULL, NULL }; +static RESPONDER *bus_respond [card_count] = { &da_bus_respond, NULL, NULL }; + + +DEBTAB di_deb [] = { + { "CPU", DEB_CPU }, + { "CMDS", DEB_CMDS }, + { "BUF", DEB_BUF }, + { "XFER", DEB_XFER }, + { "RWSC", DEB_RWSC }, + { "SERV", DEB_SERV }, + { NULL, 0 } + }; + + + +/* Disc interface global VM routines */ + + +/* I/O signal handler. + + The card has two input and two output registers. The Input Data Register and + Output Data Register are addressed when the control flip-flop is set. The + Status Word and the Control Word Register are addressed when the control + flip-flop is clear. The card has the usual control, flag buffer, flag, and + SRQ flip-flops, though flag and SRQ are decoupled to allow the full DCPC + transfer rate. + + In hardware, the presence of the card FIFO, which is necessary to obtain full + DCPC bandwidth, implies a delay between CPU actions, such as outputting the + last word in a data transfer, and device actions, such as accepting the last + word of a disc write. Four flip-flops are used to monitor FIFO status: + + - EDT (End of Data Transfer) + - LBO (Last Byte Out) + - LBI (Last Byte In) + - EOR (End of Record) + + The EDT signal indicates that the final data word of a transfer is being + written to the FIFO. The flip-flop is set by the EDT backplane signal when + the last cycle of a DCPC transfer is executing, or during programmed output + transfers when CLF does not accompany IOO in packed mode, or when bit 15 of + the data word is set in unpacked mode. It remains set until cleared by a + master reset. The output of the EDT flip-flop drives the EDT tag input of + the FIFO. + + The LBO signal indicates that the final data byte of a transfer has been + sourced to the bus. The flip-flop is set when the last byte of the entry + tagged with EDT has been unloaded from the FIFO. It is cleared by a master + reset, or when an entry not tagged with EDT is unloaded. The output of the + LBO flip-flop drives the LBO bit in the Status Word. + + The LBI signal indicates that the final byte of an input transfer has been + accepted from the bus. The flip-flop is set when a byte tagged with EOI is + received and the EOI bit in the control register is set, or a line-feed byte + is received and the LF bit in the control register is set. It is cleared by + a master reset, or when neither of these conditions is true. The input of + the LBI flip-flop also drives the LBR (last byte received) tag input of the + FIFO, and the output of the flip-flop drives the LBI bit in the Status Word. + + The EOR signal indicates that the final data word of a transfer is available + in the Input Data Register. The flip-flop is set when the last byte of the + entry tagged with LBR has been unloaded from the FIFO and written to the IDR. + It is cleared by a master reset, or when an entry not tagged with LBR is + unloaded and written to the IDR. The output of the EOR flip-flop sets the + flag flip-flop when the IDR is unloaded. + + + Implementation notes: + + 1. In hardware, the Status Word consists of individual flip-flops and status + signals that are enabled onto the I/O backplane. In simulation, the + individual status values are collected into a Status Word Register, and + the Output Data Register does not exist (output data is written directly + to the FIFO buffer). + + 2. The DIAG, T, and L control bits enable a data loopback path on the card. + An IOO issued to the card unloads a word from the FIFO and then loads the + lower byte into both bytes of the FIFO. The data word output with the + IOO instruction is not used. + + In hardware, IOO triggers the FIFO unload and reload; T and L are + required only for the loopback path. If L is not asserted, the FIFO is + loaded with 177777 due to the floating bus. If L is asserted and T is + not, the FIFO is loaded with 000000 due to pullups on the DIO lines. + In simulation, we look only for DIAG and assume that T/L are set + properly, i.e., unloaded data is reloaded. + + 3. In hardware, the SRQ and NRFD lines are open-collector and may be driven + simultaneously from several bus devices. Simulating this fully would + require keeping the state of the lines for each device, and deriving the + common bus signals from the logical AND/OR of the state values. + Fortunately, some simplifications are possible. + + The DI asserts SRQ only if control word bit 15 is 1 and bit 0 is 0. + Other bit combinations deny SRQ; as neither the Amigo nor CS/80 protocols + use SRQ and serial polls, there will be no other driver. + + In hardware, every listener drives NRFD, but in practice there is only + one listener at a time. When the card is the listener, it asserts NRFD + if the FIFO becomes full. In simulation, we assert NRFD on the bus if + NRFD is set in the control register, or we are listening and the FIFO is + full. We deny NRFD if NRFD had been set in the control register but is + no longer, or if we had been a listener but are no longer. That is, we + assume that if we have forced NRFD or set it as a listener, then no one + else will be asserting NRFD, so it's safe for us to deny NRFD when the + override is removed or we are no longer a listener. + + We also deny NRFD when a CRS is issued if NRFD had been explicitly + requested or the card had been listening. The rationale is the same: + only a listener can assert NRFD, so if we were listening, it's safe to + deny it, because only we could have set it. + + 4. In hardware, the IRL, LBO, LBI, and IFC status bits are driven by + corresponding flip-flops. In simulation, the status bits themselves hold + the equivalent states and are set and cleared as indicated. + + 5. The card state must be updated during status read (IOI) processing + because the 7974 boot ROM watches the IFC line to determine when IFC + assertion ends. + + 6. DCPC performance is optimized by recognizing that the normal cases (an + input that empties the FIFO or an output that fills the FIFO) do not + alter the card state, and so the usual update_state call may be omitted. + + 7. The gcc compiler (at least as of version 4.6.2) does not optimize + repeated use of array-of-structures accesses. Instead, it recalculates + the index each time, even though the index is a constant within the + function. To avoid this performance penalty, we use a pointer to the + selected DI_STATE structure. Note that VC++ 2008 does perform this + optimization. +*/ + + +uint32 di_io (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data) +{ +static const char * const output_state [] = { "Control", "Data" }; +static const char * const input_state [] = { "Status", "Data" }; + +const char * const hold_or_clear = (signal_set & ioCLF ? ",C" : ""); +const CARD_ID card = (CARD_ID) (dibptr->card_index); +DI_STATE * const di_card = &di [card]; + +uint8 assert, deny; /* new bus control states */ +uint16 data; +t_bool update_required = TRUE; /* TRUE if CLF must update card state */ + +IOSIGNAL signal; +IOCYCLE working_set = IOADDSIR (signal_set); /* add ioSIR if needed */ + +while (working_set) { + signal = IONEXT (working_set); /* isolate next signal */ + + switch (signal) { /* dispatch I/O signal */ + + case ioCLF: /* clear flag flip-flop */ + di_card->flag = CLEAR; /* clear flag */ + di_card->flagbuf = CLEAR; /* and flag buffer */ + + if (DEBUG_PRJ (dptrs [card], DEB_CMDS)) + fprintf (sim_deb, ">>%s cmds: [CLF] Flag cleared\n", + dptrs [card]->name); + + if (update_required) /* if card state has changed */ + update_state (card); /* then update the state */ + break; + + + case ioSTF: /* set flag flip-flop */ + if (DEBUG_PRJ (dptrs [card], DEB_CMDS)) + fprintf (sim_deb, ">>%s cmds: [STF] Flag set\n", + dptrs [card]->name); + + /* fall into ENF handler */ + + case ioENF: /* enable flag */ + di_card->flag = SET; /* set flag */ + di_card->flagbuf = SET; /* and flag buffer */ + break; + + + case ioSFC: /* skip if flag is clear */ + setstdSKF (di [card]); + break; + + + case ioSFS: /* skip if flag is set */ + setstdSKF (di [card]); + break; + + + case ioIOI: /* I/O data input */ + if (di_card->control == SET) { /* control set = data mode? */ + data = di_card->input_data_register; /* read input data register */ + di_card->status_register &= ~STAT_IRL; /* clear input register loaded status */ + + if (FIFO_EMPTY && di_card->eor == CLEAR) { /* FIFO empty and end of record not seen? */ + if (di_card->srq == SET && DEBUG_PRJ (dptrs [card], DEB_CMDS)) + fprintf (sim_deb, ">>%s cmds: SRQ cleared\n", + dptrs [card]->name); + + di_card->srq = CLEAR; /* clear SRQ */ + update_required = FALSE; /* card state does not change */ + } + } + + else { /* control clear = status mode */ + di_card->status_register &= /* clear the values to be computed, */ + STAT_IRL | STAT_LBO /* preserving those set elsewhere */ + | STAT_LBI | STAT_IFC; + + di_card->status_register |= /* set T/L/C status from control register */ + di_card->cntl_register /* (T/L are ORed, as MTA or MLA can also set) */ + & (CNTL_CIC | CNTL_TALK | CNTL_LSTN); + + + if (SW8_SYSCTL) /* if SW8 set, card is system controller */ + di_card->status_register |= STAT_SYSCTL; + + if (di_card->ibp == lower) /* if lower byte input is next */ + di_card->status_register |= STAT_ODD; /* then last transfer was odd */ + + di_card->status_register |= /* set bus status bits */ + (di_card->bus_cntl /* from corresponding bus control lines */ + & (BUS_SRQ | BUS_IFC | BUS_REN + | BUS_EOI | BUS_ATN)) << DATA_SHIFT; + + data = di_card->status_register; /* return the status word */ + } + + if (DEBUG_PRJ (dptrs [card], DEB_CPU)) + fprintf (sim_deb, ">>%s cpu: [LIx%s] %s = %06o\n", + dptrs [card]->name, hold_or_clear, + input_state [di_card->control], data); + + if (update_required && !(signal_set & ioCLF)) /* if update and CLF not present, */ + update_state (card); /* update state, else ioCLF will update */ + + stat_data = IORETURN (SCPE_OK, data); /* merge in return status */ + break; + + + case ioIOO: /* I/O data output */ + data = IODATA (stat_data); /* get the data value */ + + if (DEBUG_PRJ (dptrs [card], DEB_CPU)) + fprintf (sim_deb, ">>%s cpu: [OTx%s] %s = %06o\n", + dptrs [card]->name, hold_or_clear, + output_state [di_card->control], data); + + if (di_card->control == SET) { /* control set = data mode */ + if (signal_set & ioEDT) /* if end of DCPC transfer */ + di_card->edt = SET; /* set EDT flip-flop */ + + else if (di_card->cntl_register & CNTL_PACK) { /* packed transfer? */ + if (!(signal_set & ioCLF)) /* and CLF not given? */ + di_card->edt = SET; /* set EDT flip-flop */ + } + + else /* unpacked transfer */ + if (data & DATA_LBO) /* and last byte out? */ + di_card->edt = SET; /* set EDT flip-flop */ + + if (di_card->cntl_register & CNTL_DIAG) { /* DIAG loopback? */ + data = fifo_unload (card, diag_access); /* unload data from FIFO */ + fifo_load (card, data, diag_access); /* and load back in */ + } + + else { /* normal mode */ + fifo_load (card, data, cpu_access); /* load data word into FIFO */ + + if (FIFO_FULL && (di_card->bus_cntl & BUS_NRFD)) { /* FIFO full and listener not ready? */ + if (di_card->srq == SET && DEBUG_PRJ (dptrs [card], DEB_CMDS)) + fprintf (sim_deb, ">>%s cmds: SRQ cleared\n", + dptrs [card]->name); + + di_card->srq = CLEAR; /* clear SRQ */ + update_required = FALSE; /* card state does not change */ + } + } + } + + else { /* control clear = write control word */ + assert = 0; /* initialize bus control assertions */ + deny = 0; /* and denials */ + + if (!(data & CNTL_PACK)) /* unpacked mode always sets */ + di_card->ibp = di_card->obp = lower; /* byte selectors to the lower byte */ + + if (data & CNTL_TALK) { /* talking enables ATN and EOI outputs */ + if ((data & (CNTL_PPE | CNTL_CIC)) /* if parallel poll is enabled */ + == (CNTL_PPE | CNTL_CIC)) /* and card is CIC */ + assert = BUS_PPOLL; /* then conduct parallel poll */ + + else if ((di_card->cntl_register /* if PP was enabled */ + & (CNTL_PPE | CNTL_CIC)) /* but is not now */ + == (CNTL_PPE | CNTL_CIC)) + deny = BUS_PPOLL; /* then end the parallel poll */ + + else if ((data /* if packed mode */ + & (CNTL_PACK | CNTL_CIC | CNTL_ATN)) /* and card is CIC */ + == (CNTL_PACK | CNTL_CIC | CNTL_ATN)) /* then ATN control output */ + assert = BUS_ATN; /* is coupled to the bus */ + + else /* if none of the above */ + deny = BUS_ATN; /* ATN is not driven */ + } + + else /* not talking */ + deny = BUS_ATN | BUS_EOI; /* so ATN and EOI are disabled */ + + + if (data & CNTL_NRFD) /* card not ready set explicitly? */ + assert |= BUS_NRFD; /* assert NRFD on bus */ + + else if (di_card->cntl_register & CNTL_NRFD) /* NRFD was set but is not now? */ + deny |= BUS_NRFD; /* deny NRFD on bus */ + + if (FIFO_FULL) /* is FIFO full? */ + if (data & CNTL_LSTN) /* is card now listening? */ + assert |= BUS_NRFD; /* listener and full FIFO asserts NRFD */ + + else if (di_card->cntl_register & CNTL_LSTN) /* was listener but is not now? */ + deny |= BUS_NRFD; /* deny NRFD on bus */ + + + if (SW8_SYSCTL) { /* system controller drives REN and IFC */ + if (data & CNTL_REN) /* REN control */ + assert |= BUS_REN; /* output is */ + else /* coupled to */ + deny |= BUS_REN; /* the bus */ + + if (data & CNTL_IFC) { /* IFC set? */ + assert |= BUS_IFC; /* assert IFC on bus */ + + di_card->status_register = + di_card->status_register + & ~(STAT_LSTN | STAT_TALK) /* clear listen and talk status */ + | STAT_IFC; /* and set IFC status */ + + di_card->ifc_timer = /* start IFC timer by calculating */ + sim_gtime () + IFC_TIMEOUT; /* IFC stop time (100 microseconds) */ + } + } + + if ((data & (CNTL_SRQ | CNTL_CIC)) == CNTL_SRQ) /* service request and not controller? */ + assert |= BUS_SRQ; /* assert SRQ on bus */ + else /* else */ + deny |= BUS_SRQ; /* deny SRQ on bus */ + + di_card->cntl_register = data; /* save control word */ + di_bus_control (card, CONTROLLER, assert, deny); /* update bus control state */ + } + + if (update_required && !(signal_set & ioCLF)) /* if update and CLF not present, */ + update_state (card); /* update state, else ioCLF will update */ + break; + + + case ioPOPIO: /* power-on preset to I/O */ + di_card->flag = SET; /* set flag */ + di_card->flagbuf = SET; /* and flag buffer */ + + if (DEBUG_PRJ (dptrs [card], DEB_CMDS)) + fprintf (sim_deb, ">>%s cmds: [POPIO] Flag set\n", + dptrs [card]->name); + break; + + + case ioCRS: /* control reset */ + if (DEBUG_PRJ (dptrs [card], DEB_CMDS)) + fprintf (sim_deb, ">>%s cmds: [CRS] Master reset\n", + dptrs [card]->name); + + di_card->status_register &= /* clear listen and talk status */ + ~(STAT_LSTN | STAT_TALK); + + deny = BUS_SRQ | BUS_REN | BUS_ATN | BUS_EOI; /* clear lines driven by control register */ + + if (di_card->cntl_register & (CNTL_NRFD | CNTL_LSTN)) /* if asserting NRFD or listening */ + deny |= BUS_NRFD; /* deny because we're clearing */ + + di_card->cntl_register = 0; /* clear control word register */ + di_card->control = CLEAR; /* clear control */ + di_card->srq = CLEAR; /* clear srq */ + + master_reset (card); /* master reset */ + + di_bus_control (card, CONTROLLER, 0, deny); /* update bus control state */ + update_state (card); /* update card state */ + break; + + + case ioCLC: /* clear control flip-flop */ + di_card->control = CLEAR; /* clear control */ + + if (DEBUG_PRJ (dptrs [card], DEB_CMDS)) { + fprintf (sim_deb, ">>%s cmds: [CLC%s] Control cleared (configure mode)", + dptrs [card]->name, hold_or_clear); + + if (signal_set & ioCLF) /* if ioCLF given, */ + fputs (", master reset\n", sim_deb); /* then report master reset */ + else + fputc ('\n', sim_deb); + } + + if (signal_set & ioCLF) /* if ioCLF given, */ + master_reset (card); /* then do master reset */ + break; /* (ioCLF will call update_state for us) */ + + + case ioSTC: /* set control flip-flop */ + di_card->control = SET; /* set control */ + + if (DEBUG_PRJ (dptrs [card], DEB_CMDS)) + fprintf (sim_deb, ">>%s cmds: [STC%s] Control set (data mode)\n", + dptrs [card]->name, hold_or_clear); + break; + + + case ioEDT: /* end data transfer */ + if (DEBUG_PRJ (dptrs [card], DEB_CPU)) + fprintf (sim_deb, ">>%s cpu: [EDT] DCPC transfer ended\n", + dptrs [card]->name); + break; + + + case ioSIR: /* set interrupt request */ + setstdPRL (di [card]); /* set standard PRL signal */ + setstdIRQ (di [card]); /* set standard IRQ signal */ + + setSRQ (dibptr->select_code, /* set SRQ signal from control and SRQ */ + di_card->srq == SET && di_card->control == SET); + break; + + + case ioIAK: /* interrupt acknowledge */ + di_card->flagbuf = CLEAR; /* clear flag buffer */ + break; + + + default: /* all other signals */ + break; /* are ignored */ + } + + working_set = working_set & ~signal; /* remove current signal from set */ + } + +return stat_data; +} + + +/* Reset the simulator. + + During a hardware PRESET, POPIO sets the flag buffer and flag flip-flops, and + CRS clears the control flip-flop and Control Word Register. In addition, CRS + asserts a master reset on the card. + + PON is not used by the card. + + + Implementation notes: + + 1. During a power-on reset, a pointer to the FIFO simulation register is + saved to allow access to the "qptr" field during FIFO loading and + unloading. This enables the SCP to view the FIFO as a circular queue, so + that the bottom word of the FIFO is always displayed as FIFO[0], + regardless of where it is in the actual FIFO array. +*/ + +t_stat di_reset (DEVICE *dptr) +{ +DIB *dibptr = (DIB *) dptr->ctxt; /* DIB pointer */ +const CARD_ID card = (CARD_ID) (dibptr->card_index); /* card number */ + +if (sim_switches & SWMASK ('P')) { /* power-on reset? */ + di [card].fifo_reg = find_reg ("FIFO", NULL, dptr); /* find the FIFO register entry */ + + if (di [card].fifo_reg == NULL) /* not there? */ + return SCPE_IERR; /* is a programming error! */ + else /* found it */ + di [card].fifo_reg->qptr = 0; /* reset the FIFO bottom index */ + + di [card].status_register = 0; /* clear the status word */ + + di [card].bus_cntl = 0; /* deny the HP-IB control lines */ + + di [card].listeners = 0; /* clear map of listeners */ + di [card].talker = 0; /* clear map of talker */ + di [card].poll_response = 0; /* clear map of parallel poll responses */ + + di [card].ifc_timer = 0.0; /* clear IFC timer */ + } + +IOPRESET (dibptr); /* PRESET the device */ + +return SCPE_OK; +} + + + +/* Disc interface global SCP routines */ + + +/* Set a unit's bus address. + + Bus addresses range from 0-7 and are initialized to the unit number. All + units of a device must have unique bus addresses. In addition, the card also + has a bus address, although this is only used for the diagnostic. The card + address may be the same as a unit address, as all units are disconnected + during a diagnostic run. + + The "value" parameter indicates whether the routine is setting a unit's bus + address (0) or a card's bus address (1). + + + Implementation notes: + + 1. To ensure that each address is unique, a check is made of the other units + for conflicting addresses. An "invalid argument" error is returned if + the desired address duplicates another. This means that addresses cannot + be exchanged without first assigning one of them to an unused address. + Also, an address cannot be set that duplicates the address of a disabled + unit (which cannot be displayed without enabling it). + + An alternate method would be to set the new assignments into a "shadow + array" that is set into the unit flags (and checked for validity) only + when a power-on reset is done. This does follow the disc and tape + controller hardware, which reads the HP-IB address switch settings only + at power-up. +*/ + +t_stat di_set_address (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +t_stat status; +uint32 index, new_address; +uint32 old_address = GET_BUSADR (uptr->flags); +DEVICE *dptr = (DEVICE *) desc; + +if (cptr == NULL) /* if address not given */ + return SCPE_ARG; /* report a missing argument */ + +new_address = get_uint (cptr, 10, 7, &status); /* parse the address value */ + +if (status == SCPE_OK) { /* parse OK? */ + if (value) /* setting the card address? */ + dptr->flags = dptr->flags & ~DEV_BUSADR /* store new address in the device flags */ + | SET_DIADR (new_address); + + else { /* setting a unit address */ + for (index = 0; index < dptr->numunits; index++) /* look through units */ + if (new_address != old_address /* to ensure address is unique */ + && new_address == GET_BUSADR (dptr->units [index].flags)) { + printf ("Bus address conflict: DA%d\n", index); + + if (sim_log) + fprintf (sim_log, "Bus address conflict: DA%d\n", index); + + return SCPE_NOFNC; /* a duplicate address gives an error */ + } + + uptr->flags = uptr->flags & ~UNIT_BUSADR /* address is valid; change */ + | SET_BUSADR (new_address); /* the address in the unit flags */ + } + } + +return status; /* return the result of the parse */ +} + + +/* Show a unit's bus address. + + The "value" parameter indicates whether the routine is showing a unit's bus + address (0) or a card's bus address (1). +*/ + +t_stat di_show_address (FILE *st, UNIT *uptr, int32 value, void *desc) +{ +DEVICE *dptr = (DEVICE *) desc; + +if (value) /* card address? */ + fprintf (st, "address=%d", GET_DIADR (dptr->flags)); /* get from device flags */ +else /* unit address */ + fprintf (st, "bus=%d", GET_BUSADR (uptr->flags)); /* get from unit flags */ + +return SCPE_OK; +} + + +/* Set the bus cable connection. + + In normal use, the various tape and disc devices are connected together and + to the disc interface card by HP-IB cables. For the diagnostic, two disc + interface cards are connected by a single cable. + + The "value" parameter indicates whether the routine is connecting the + cable to devices for normal use (0) or to another card for diagnostics (1). + + + Implementation notes: + + 1. Initially, only one card and peripheral set is simulated: the ICD disc + family (DA device). For diagnostic use, a second, dummy card is enabled + (DC device). Once a second card simulation is implemented, this code + will no longer be necessary. +*/ + +t_stat di_set_cable (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +if (value) { /* diagnostic cable selected? */ + ((DEVICE *) desc)->flags |= DEV_DIAG; /* set diagnostic flag */ + dc_dev.flags &= ~DEV_DIS; /* enable dummy device */ + dc_dev.flags |= DEV_DIAG; /* and set its flag as well */ + } +else { /* peripheral cable selected */ + ((DEVICE *) desc)->flags &= ~DEV_DIAG; /* clear diagnostic flag */ + dc_dev.flags |= DEV_DIS; /* disable dummy device */ + dc_dev.flags &= ~DEV_DIAG; /* and clear its flag */ + } + +return SCPE_OK; +} + + +/* Show the bus cable connection. + + The "value" parameter indicates whether the cable is connected to devices for + normal use (0) or to another card for diagnostics (1). +*/ + +t_stat di_show_cable (FILE *st, UNIT *uptr, int32 value, void *desc) +{ +if (((DEVICE *) desc)->flags & DEV_DIAG) /* is cable connected for diagnostics? */ + fputs ("diagnostic cable", st); /* report it */ +else /* cable is connected for normal use */ + fputs ("HP-IB cable", st); /* report the condition */ + +return SCPE_OK; +} + + + +/* Disc interface global bus routines. + + In hardware, the HP-IB bus consists of eight control lines and eight data + lines. Signals are asserted on the control lines to establish communication + between a source and one or more acceptors. For commands, the source is + always the controller (the 12821A card), and the acceptors are all connected + devices. For data, the source is the current talker, and the acceptors are + one or more current listeners. A three-wire interlocking handshake enables + communication at the rate of the slowest of the multiple acceptors. The + controller conducts a parallel poll by asserting ATN and EOI together. + Devices whose parallel poll responses are enabled each assert one of the data + lines to indicate that service is required. + + In simulation, a disabled or detached unit logically is not connected to the + bus. The card maintains a bitmap of acceptors (all devices currently + attached), listeners (all devices currently addressed to listen), the talker + (the device currently addressed to talk), and the enabled parallel poll + responses. Changes in control line state are communicated to all acceptors + via control/respond function calls, and data is exchanged between talker and + listeners via source/acceptor function calls. Data bytes are sent to all + current listeners in bus-address order. The card conducts a parallel poll by + checking the response bitmap; devices must set and clear their poll responses + appropriately in advance of the poll. + + Not all of the HP-IB control lines are simulated. The DAV and NDAC handshake + lines are never asserted; instead, they are simulated by the bus source + function calling one or more bus acceptor functions. SRQ and REN are + asserted as directed by the system controller but are not otherwise used (no + HP disc or tape devices assert SRQ or respond to REN). IFC, ATN, EOI, and + NRFD are asserted and tested by the controller and devices. In particular, + asserting NRFD will hold off a pending data transmission until it is denied. + + The functions that simulate the HP-IB (where "*" is "di", "da", etc.) are: + + di_bus_source -- Source a data byte to the bus. Returns TRUE if the + byte was accepted (i.e., there were one or more + listeners) and FALSE if it was not. Called by the + controller to send commands to devices, and called by + the current talker to send data to the listener(s). ATN + and EOI should be set as required in the control word + before calling. + + *_bus_accept -- Accept a data byte from the bus. Returns TRUE if the + byte was accepted and FALSE if it was not. Called by + di_bus_source to handshake between source and acceptor. + If ATN is set in the control word, the byte is a + command; otherwise, it is data. If EOI is set for a + data byte, it is the last byte of a transmission. + + di_bus_control -- Set the control lines on the bus. Called by the system + controller to assert or deny REN or IFC, by the + controller to assert or deny SRQ, NRFD, or ATN and EOI + (to conduct or conclude a parallel poll), and by the + current listener to assert or deny NRFD. All connected + devices on the bus are notified of the changes. It is + not necessary to call di_bus_control for changes to ATN + and EOI that accompany a command or data byte. + + *_bus_respond -- Respond to changes in the control lines on the bus. + Called by di_bus_control to inform each connected device + of a change in control state. + + di_poll_response -- Set a device's poll response. Called by a device to + enable or disable its response to a future parallel + poll. +*/ + + +/* Source a byte to the bus. + + This routine is called to send bytes to devices on the bus connected to the + specified card. If the card is in diagnostic mode, which simulate two cards + connected by an HP-IB cable, then the byte is sent to another card in the + card cage that is also in diagnostic mode and enabled to receive. If the + card is not in diagnostic mode, then the byte is sent to all acceptors (if a + command) or to all listeners (if data) on the bus. + + An indication is returned showing whether or not there were any acceptors on + the bus. + + + Implementation notes: + + 1. If the responses from a previously conducted parallel poll are not + cleared from the FIFO before enabling the card to transmit, the card will + appear to conduct a new parallel poll because the FIFO tags cause ATN and + EOI to be asserted. This "fake" parallel poll is ignored (a real + parallel poll does not source data onto the bus). +*/ + +t_bool di_bus_source (CARD_ID card, uint8 data) +{ +CARD_ID other; +uint32 acceptors, unit; +t_bool accepted = FALSE; + +if (DEBUG_PRJ (dptrs [card], DEB_XFER)) { + fprintf (sim_deb, ">>%s xfer: HP-IB DIO %03o available ", dptrs [card]->name, data); + fprint_bus (sim_deb, "[%s]\n", di [card].bus_cntl); + } + +if (dptrs [card]->flags & DEV_DIAG) /* diagnostic run? */ + for (other = first_card; other <= last_card; other++) { /* look through list of cards */ + if (other != card && dptrs [other] /* for the other card */ + && (dptrs [other]->flags & DEV_DIAG) /* that is configured for diagnostic */ + && (di [other].cntl_register & CNTL_LSTN)) /* and is listening */ + accepted = di_bus_accept (other, data); /* call interface acceptor for other card */ + } + +else if ((di [card].bus_cntl & BUS_PPOLL) != BUS_PPOLL) { /* normal run; not a fake poll? */ + if (di [card].cntl_register & CNTL_LSTN) /* is card a listener? */ + accepted = di_bus_accept (card, data); /* call interface acceptor for this card */ + + acceptors = di [card].acceptors; /* get map of acceptors */ + + if (!(di [card].bus_cntl & BUS_ATN) /* if a data transfer, */ + || (data & BUS_COMMAND) == BUS_ACG) /* or an addressed command, e.g., SDC */ + acceptors = di [card].listeners; /* then limit just to listeners */ + + for (unit = 0; acceptors; unit++) { /* loop through units */ + if (acceptors & 1) /* is current unit accepting? */ + accepted |= (*bus_accept [card]) (unit, data); /* call acceptor for this card */ + + acceptors = acceptors >> 1; /* move to next acceptor */ + } + } + +if (DEBUG_PRJ (dptrs [card], DEB_XFER) && !accepted) + fprintf (sim_deb, ">>%s xfer: HP-IB no acceptors\n", + dptrs [card]->name); + +return accepted; +} + + +/* Assert or deny control on the bus. + + This routine is called by the indicated unit to assert or deny the HP-IB + control lines on the bus connected to the specified card. Separate sets of + signals to assert and deny are provided. + + If the bus state after modification did not change, the routine returns with + no further action. Otherwise, if the card is in diagnostic mode, then + notification of the bus change is sent to another card in the card cage that + is also in diagnostic mode. + + If the card is not in diagnostic mode, then the set of control lines that + are changing is checked to determine whether notification is necessary. If + not, then the change is not broadcast to improve performance. However, if + notification is required, then all acceptors on the bus are informed of the + change. + + + Implementation notes: + + 1. If a signal is asserted and denied in the same call, the assertion takes + precedence. + + 2. Of the sixteen potential control line state changes, only IFC assertion + and ATN and NRFD denial must be broadcast. Asserting IFC unaddresses all + devices, and denying ATN or NRFD allows a waiting talker to source a data + byte to the bus. Devices do not act upon the remaining thirteen state + changes, and a considerable performance improvement is obtained by + omitting the notification calls. + + 3. All control line state notifications are sent in diagnostic mode, as the + responses of the other card are specifically tested by the diagnostic. + + 4. Asserting ATN and EOI will conduct a parallel poll. Devices are not + notified of the poll. Instead, the previously stored parallel poll + responses will be used. +*/ + +#define ASSERT_SET (BUS_IFC) +#define DENY_SET (BUS_ATN | BUS_NRFD) + +void di_bus_control (CARD_ID card, uint32 unit, uint8 assert, uint8 deny) +{ +CARD_ID other; +uint32 acceptors, responder; +t_bool responded; +uint8 new_state, new_assertions, new_denials; + +new_state = di [card].bus_cntl & ~deny | assert; /* set up new control state */ + +if (new_state == di [card].bus_cntl) /* if control state did not change */ + return; /* return now */ + +new_assertions = ~di [card].bus_cntl & assert; /* get changing assertions */ +new_denials = di [card].bus_cntl & deny; /* get changing denials */ + +di [card].bus_cntl = new_state; /* establish the new control state */ + +if (DEBUG_PRJ (dptrs [card], DEB_XFER)) { + if (unit == CONTROLLER) + fprintf (sim_deb, ">>%s xfer: HP-IB card %d", dptrs [card]->name, card); + else + fprintf (sim_deb, ">>%s xfer: HP-IB address %d", + dptrs [card]->name, GET_BUSADR (dptrs [card]->units [unit].flags)); + + if (new_assertions) + fprint_bus (sim_deb, " asserted [%s]", new_assertions); + + if (new_denials) + fprint_bus (sim_deb, " denied [%s]", new_denials); + + fprint_bus (sim_deb, ", bus is [%s]\n", new_state); + } + +if ((dptrs [card]->flags & DEV_DIAG) /* diagnostic mode? */ + || (new_assertions & ASSERT_SET) /* or changed signals in the */ + || (new_denials & DENY_SET)) { /* set that must be broadcast? */ + responded = FALSE; /* assume no response */ + + if (dptrs [card]->flags & DEV_DIAG) { /* diagnostic run? */ + for (other = first_card; other <= last_card; other++) /* look through list of cards */ + if (other != card && dptrs [other] /* for the other card */ + && (dptrs [other]->flags & DEV_DIAG)) { /* that is configured for diagnostic */ + di_bus_respond (other, new_state); /* notify other card of new control state */ + responded = TRUE; /* and note that there was a responder */ + } + } + + else { /* normal run */ + update_state (card); /* update card for new control state */ + + acceptors = di [card].acceptors; /* get map of acceptors */ + responded = (acceptors != 0); /* set if there are any acceptors */ + + for (responder = 0; acceptors; responder++) { /* loop through units */ + if ((acceptors & 1) && responder != unit) /* is current unit accepting? */ + (*bus_respond [card]) (card, responder, new_state); /* call responder for this card */ + + acceptors = acceptors >> 1; /* move to next acceptor */ + } + } + + if (DEBUG_PRJ (dptrs [card], DEB_XFER) & !responded) + fprintf (sim_deb, ">>%s xfer: HP-IB no responders\n", + dptrs [card]->name); +} + +if ((new_state & BUS_PPOLL) == BUS_PPOLL) /* parallel poll requested? */ + di_bus_poll (card); /* conduct the poll */ + +return; +} + + +/* Enable or disable a unit's parallel poll response. + + The poll response for a unit connected to a specified card is set or cleared + as indicated. If a parallel poll is in progress when a poll response is set, + the poll is conducted again to reflect the new response. +*/ + +void di_poll_response (CARD_ID card, uint32 unit, FLIP_FLOP response) +{ +const uint32 address = GET_BUSADR (dptrs [card]->units [unit].flags); +uint32 previous_response = di [card].poll_response; + +if (response == SET) { /* enable PPR? */ + di [card].poll_response |= PPR (address); /* set response bit */ + + if ((di [card].bus_cntl & BUS_PPOLL) == BUS_PPOLL) /* is parallel poll in progress? */ + di_bus_poll (card); /* conduct with new response */ + } +else /* disable PPR */ + di [card].poll_response &= ~PPR (address); /* clear response bit */ + +if (DEBUG_PRJ (dptrs [card], DEB_XFER) + && previous_response != di [card].poll_response) + fprintf (sim_deb, ">>%s xfer: HP-IB address %d parallel poll response %s\n", + dptrs [card]->name, address, (response == SET ? "enabled" : "disabled")); + +return; +} + + + +/* Disc interface local bus routines */ + + +/* Conduct a parallel poll on the bus. + + A controller asserting ATN and EOI simultaneously on the bus is conducting a + parallel poll. In hardware, each device whose poll response is enabled + asserts one of the data lines corresponding to its bus address. The + controller terminates the poll by denying ATN and EOI. + + Setting the CIC (controller in charge) and PPE (parallel poll enable) bits in + the Control Word Register directs the disc interface to conduct a poll. + Setting PPE without CIC enables the poll response for the interface. + + In the diagnostic mode, one card is set to conduct the poll, and the other is + set to respond to it. In the normal mode, connected devices have set or + cleared their respective poll responses before this routine is called. + + + Implementation notes: + + 1. The card hardware fills the upper and lower bytes of the FIFO with the + response byte. In simulation, we use the diag_access mode to do the same + thing (diagnostic loopback also fills both bytes with the lower byte). +*/ + +static void di_bus_poll (CARD_ID card) +{ +CARD_ID other; +uint8 response; + +if ((di [card].cntl_register + & (CNTL_PPE | CNTL_CIC)) == CNTL_PPE) /* card poll response enabled? */ + response = di [card].poll_response /* add card's response */ + | PPR (GET_DIADR (dptrs [card]->flags)); /* to the devices' responses */ +else + response = di [card].poll_response; /* card disabled, so just use devices */ + +if (dptrs [card]->flags & DEV_DIAG) /* diagnostic run? */ + for (other = first_card; other <= last_card; other++) /* look through list of cards */ + if (other != card && dptrs [other] /* for another card */ + && (dptrs [other]->flags & DEV_DIAG) /* that is configured for diagnostic */ + && (di [other].cntl_register /* and has PPE asserted */ + & (CNTL_PPE | CNTL_CIC)) == CNTL_PPE) + response |= /* merge its poll response */ + PPR (GET_DIADR (dptrs [other]->flags)); + +if (response) { /* poll response indicated? */ + if (DEBUG_PRJ (dptrs [card], DEB_XFER)) + fprintf (sim_deb, ">>%s xfer: HP-IB parallel poll DIO %03o\n", + dptrs [card]->name, response); + + while (di [card].fifo_count != FIFO_SIZE) /* fill card FIFO with responses */ + fifo_load (card, (uint16) response, diag_access); /* (hardware feature) */ + + update_state (card); /* update card state */ + } + +return; +} + + +/* Accept a data byte from the bus. + + The indicated card accepts a byte that has been sourced to the bus. The byte + is loaded into the FIFO, and the card state is updated to reflect the load. + + Bus acceptors return TRUE to indicate if the byte was accepted. A card + always accepts a byte, so the routine always returns TRUE. +*/ + +static t_bool di_bus_accept (CARD_ID card, uint8 data) +{ +if (DEBUG_PRJ (dptrs [card], DEB_XFER)) + fprintf (sim_deb, ">>%s xfer: HP-IB card %d accepted data %03o \n", + dptrs [card]->name, card, data); + +fifo_load (card, data, bus_access); /* load data byte into the FIFO */ +update_state (card); /* and update the card state */ +return TRUE; /* indicate the byte was accepted */ +} + + +/* Respond to the bus control lines. + + The indicated card is notified of the new control state on the bus. The + routine establishes the new bus state and updates the card state to reflect + the change. +*/ + +static void di_bus_respond (CARD_ID card, uint8 new_cntl) +{ +di [card].bus_cntl = new_cntl; /* update bus control */ +update_state (card); /* update card state */ +return; +} + + + +/* Disc interface local utility routines */ + + +/* Master reset the interface. + + This is the programmed card master reset, not the simulator reset routine. + Master reset initializes a number of flip-flops and data paths on the card. + The primary use, other than during a PRESET, is to clear the FIFO in + preparation to changing the card from a listener to a talker or vice versa. + This ensures that unneeded FIFO data is not transmitted inadvertently to the + bus or the CPU. It is also used when changing the data mode from unpacked to + packed to release the byte pointer flip-flops, which are held in the "lower + byte" position during unpacked transfers. + + In hardware, master reset: + - clears the EDT, EOR, IRL, LBO, LBI, and IFC flip-flops + - clears the Input Data Register + - clears the FIFO + - sets or clears the odd/even input and output byte pointer flip-flops, + depending on whether the P (packed transfer) bit is set in the Control + Word Register +*/ + +static void master_reset (CARD_ID card) +{ +di [card].edt = CLEAR; /* clear the EDT flip-flop */ +di [card].eor = CLEAR; /* clear the EOR flip-flop */ + +if (di [card].cntl_register & CNTL_PACK) /* if packed mode is set, */ + di [card].ibp = di [card].obp = upper; /* MR sets selectors to the upper byte */ +else /* otherwise, unpacked mode overrides */ + di [card].ibp = di [card].obp = lower; /* and sets the selectors to the lower byte */ + +di [card].status_register &= /* clear status flip-flops */ + ~(STAT_IRL | STAT_LBO | STAT_LBI | STAT_IFC); + +di [card].input_data_register = 0; /* clear the input data register */ +di [card].fifo_count = 0; /* clear the FIFO */ + +if (DEBUG_PRJ (dptrs [card], DEB_BUF)) + fprintf (sim_deb, ">>%s buf: FIFO cleared\n", + dptrs [card]->name); + +return; +} + + +/* Update the interface state. + + In hardware, certain external operations cause automatic responses by the + disc interface card. For example, when the Input Data Register is unloaded + by an LIx instruction, it is automatically reloaded with the next word from + the FIFO. Also, the card may be set to interrupt in response to the + assertion of certain bus control lines. + + In simulation, this routine must be called whenever the FIFO, card control, + or bus control state changes. It determines whether: + + 1. the next word from the FIFO should be unloaded into the IDR. If the card + is listening, and the IDR is empty, and the FIFO contains data, then a + word is unloaded and stored in the IDR, and the Input Register Loaded + status bit is set. + + 2. the next word from the FIFO should be unloaded and sourced to the bus. + If the card is talking (but not polling), and the listener is ready to + accept data, and the last byte has not been sent, and the FIFO contains + data, then a word is unloaded and sourced to the bus. This occurs + regardless of whether or not there are any listeners. + + 3. an interface clear operation has completed. If IFC is asserted, and the + current simulation time is later than the IFC expiration time, then IFC + is denied, and the timer is reset. + + 4. the card should assert NRFD to prevent FIFO overflow. If the card is + listening, and the FIFO is full, or the last byte has been received, or a + pause has been explicitly requested, then NRFD is asserted. + + 5. the SRQ flip-flop should be set or cleared. If the card is listening and + the Input Data Register has been loaded, or the card is talking and the + FIFO is not full, then SRQ is asserted to request a DCPC transfer. + + 6. the flag flip-flop should be set or cleared. If the Input Data Register + has been loaded or the Last Byte Out flip-flop is set and the + corresponding Control Word Register IRL or LBO bits are set, or the End + of Record flip-flop is set and the Input Data Register has been unloaded, + or SRQ is asserted on the bus and the corresponding Control Word Register + bit is set when the card is not the controller-in-charge, or REN or IFC + is asserted on the bus and the corresponding Control Word Register bits + are set when the card is not the system controller, then the flag is set + to request an interrupt. + + + Implementation notes: + + 1. The fifo_unload routine may set STAT_LBO, so the flag test must be done + after unloading. + + 2. The gcc compiler (at least as of version 4.6.2) does not optimize + repeated use of array-of-structures accesses. Instead, it recalculates + the index each time, even though the index is a constant within the + function. To avoid this performance penalty, we use a pointer to the + selected DI_STATE structure. Note that VC++ 2008 does perform this + optimization. + */ + +static void update_state (CARD_ID card) +{ +DIB * const dibptr = (DIB *) dptrs [card]->ctxt; +DI_STATE * const di_card = &di [card]; +uint8 assert = 0; +uint8 deny = 0; +uint16 data; +FLIP_FLOP previous_state; + +if (di_card->cntl_register & CNTL_LSTN) { /* is card a listener? */ + if (!(di_card->status_register & STAT_IRL) /* is IDR empty? */ + && ! FIFO_EMPTY) { /* and more in FIFO? */ + data = fifo_unload (card, cpu_access); /* unload FIFO */ + di_card->input_data_register = data; /* into IDR */ + di_card->status_register |= STAT_IRL; /* set input register loaded status */ + } + } + +else if ((di_card->cntl_register /* is card a talker? */ + & (CNTL_TALK | CNTL_PPE)) == CNTL_TALK) /* and not polling? */ + while (! FIFO_EMPTY /* is data remaining in FIFO? */ + && !(di_card->bus_cntl & BUS_NRFD) /* and NRFD denied? */ + && !(di_card->status_register & STAT_LBO)) { /* and last byte not sent? */ + data = fifo_unload (card, bus_access); /* unload FIFO byte */ + di_bus_source (card, (uint8) data); /* source it to bus */ + } + + +if (di_card->bus_cntl & BUS_IFC /* IFC in progress? */ + && di_card->ifc_timer != 0.0 /* and I am timing? */ + && sim_gtime () > di_card->ifc_timer) { /* and timeout has elapsed? */ + deny = BUS_IFC; /* deny IFC on bus */ + di_card->ifc_timer = 0.0; /* clear IFC timer */ + di_card->status_register &= ~STAT_IFC; /* clear IFC status */ + } + + +if (di_card->cntl_register & CNTL_LSTN) /* is card a listener? */ + if (di_card->cntl_register & CNTL_NRFD /* if explicitly requested */ + || di_card->status_register & STAT_LBI /* or last byte is in */ + || FIFO_FULL) /* or FIFO is full */ + assert = BUS_NRFD; /* then assert NRFD */ + else /* otherwise card is ready for data */ + deny |= BUS_NRFD; /* so deny NRFD */ + +if (assert != deny) /* any change in bus state? */ + di_bus_control (card, CONTROLLER, assert, deny); /* update bus control */ + + +previous_state = di_card->srq; /* save current SRQ state */ + +if (di_card->cntl_register & CNTL_LSTN /* if card is a listener */ + && di_card->status_register & STAT_IRL /* and input register is loaded, */ + || di_card->cntl_register & CNTL_TALK /* or card is a talker */ + && ! FIFO_FULL) /* and FIFO is not full */ + di_card->srq = SET; /* then request a DCPC cycle */ +else + di_card->srq = CLEAR; /* else DCPC service is not needed */ + + +if (DEBUG_PRJ (dptrs [card], DEB_CMDS) + && di_card->srq != previous_state) + fprintf (sim_deb, ">>%s cmds: SRQ %s\n", + dptrs [card]->name, di_card->srq == SET ? "set" : "cleared"); + + +if (di_card->status_register & STAT_IRL /* input register loaded */ + && di_card->cntl_register & CNTL_IRL /* and notification wanted? */ + || di_card->status_register & STAT_LBO /* or last byte out */ + && di_card->cntl_register & CNTL_LBO /* and notification wanted? */ + || di_card->eor == SET /* or end of record seen */ + && !(di_card->status_register & STAT_IRL) /* and input register unloaded? */ + || di_card->bus_cntl & BUS_SRQ /* or SRQ is asserted on the bus */ + && di_card->cntl_register & CNTL_SRQ /* and notification wanted */ + && di_card->cntl_register & CNTL_CIC /* and card is not controller? */ + || !SW8_SYSCTL /* or card is not system controller */ + && di_card->bus_cntl & BUS_REN /* and REN is asserted on the bus */ + && di_card->cntl_register & CNTL_REN /* and notification wanted? */ + || !SW8_SYSCTL /* or card is not system controller */ + && di_card->status_register & STAT_IFC /* and IFC is asserted on the bus */ + && di_card->cntl_register & CNTL_IFC) { /* and notification wanted? */ + + if (DEBUG_PRJ (dptrs [card], DEB_CMDS)) + fprintf (sim_deb, ">>%s cmds: Flag set\n", + dptrs [card]->name); + + di_io (dibptr, ioENF, 0); /* set flag and recalculate interrupts */ + } + +else if (di_card->srq != previous_state) /* if SRQ changed state, */ + di_io (dibptr, ioSIR, 0); /* then recalculate interrupts */ + +return; +} + + +/* Load a word or byte into the FIFO. + + A word or byte is loaded into the next available location in the FIFO. The + significance of the data parameter is indicated by the access mode as + follows: + + - For CPU access, the parameter is a 16-bit value. + + - For bus access, the parameter is an 8-bit value in the lower byte and a + zero in the upper byte. + + - For diagnostic access, the parameter is an 8-bit value in the lower byte + that will be duplicated in the upper byte. + + For bus access, byte loading into the FIFO is controlled by the value of the + Input Buffer Pointer (IBP) selector. + + In addition to data words, the FIFO holds tags that mark the last byte + received or to be transmitted and that indicate the state of the ATN and EOI + bus lines (if listening) or the states to assert (if talking). The tag is + assembled into the upper word, the data is assembled into the lower word, and + then the 32-bit value is stored in the next available FIFO location. + + If data is coming from the CPU, the 16-bit value is loaded into the next FIFO + location, and the occupancy count is incremented. + + If the data is coming from the bus, and the input mode is unpacked, the 8-bit + value is loaded into the lower byte of the next FIFO location, and the + occupancy count is incremented. In hardware, the upper FIFO is not clocked; + in simulation, the upper byte is set to zero. The IBP always points at the + lower byte in unpacked mode. + + If the data is coming from the bus, and the input mode is packed, the 8-bit + value is loaded into either the upper or lower byte of the next FIFO + location, depending on the value of the IBP, and the IBP is toggled. If the + value was stored in the lower byte, the occupancy count is incremented. + + A special case occurs when the value is to be stored in the upper byte, and + the LBR tag is set to indicate that this is the last byte to be received. In + this case, the value is stored in both bytes of the next FIFO location, and + the occupancy counter is incremented. + + If data is coming from the diagnostic FIFO loopback, the 8-bit value in the + lower byte is copied to the upper byte, the resulting 16-bit value is loaded + into the next FIFO location, and the occupancy count is incremented. + + + Implementation notes: + + 1. Four tag bits are loaded into the upper word of each FIFO entry: + + - Last Byte Received (while receiving, a line feed is received and the + LF bit is set in the Control Word Register, or a byte with EOI + asserted is received and the EOI bit is set). + + - End of Data Transfer (while transmitting, DCPC asserts the EDT + backplane signal, or an unpacked-mode data word has the LBO bit set, + or a packed-mode OTx is issued without an accompanying CLF). + + - ATN (the state of ATN on the bus if receiving, or the ATN bit in the + unpacked data word if transmitting). + + - EOI (the state of EOI on the bus if receiving, or the EOI bit in the + unpacked data word if transmitting). + + 2. The FIFO is implemented as circular queue to take advantage of REG_CIRC + EXAMINE semantics. REG->qptr is the index of the first word currently in + the FIFO. By specifying REG_CIRC, examining FIFO[0-n] will always + display the words in load order, regardless of the actual array index of + the start of the list. The number of words currently present in the FIFO + is kept in fifo_count (0 = empty, 1-16 = number of words available). + + If fifo_count < FIFO_SIZE, (REG->qptr + fifo_count) mod FIFO_SIZE is the + index of the new word location. Loading stores the word there and then + increments fifo_count. + + 3. Because the load and unload routines need access to qptr in the REG + structure for the FIFO array, pointers to the REG for each card are + stored in the fifo_reg array during device reset. + + 4. The gcc compiler (at least as of version 4.6.2) does not optimize + repeated use of array-of-structures accesses. Instead, it recalculates + the index each time, even though the index is a constant within the + function. To avoid this performance penalty, we use a pointer to the + selected DI_STATE structure. Note that VC++ 2008 does perform this + optimization. +*/ + +static void fifo_load (CARD_ID card, uint16 data, FIFO_ACCESS access) +{ +uint32 tag, index; +t_bool add_word = TRUE; +DI_STATE * const di_card = &di [card]; + +if (FIFO_FULL) { /* is the FIFO already full? */ + if (DEBUG_PRJ (dptrs [card], DEB_BUF)) + fprintf (sim_deb, ">>%s buf: Attempted load to full FIFO, data %0*o\n", + dptrs [card]->name, (access == bus_access ? 3 : 6), data); + + return; /* return with load ignored */ + } + +if (di_card->cntl_register & CNTL_LSTN) { /* is card receiving? */ + tag = (di_card->bus_cntl /* set tag from bus signals */ + & (BUS_ATN | BUS_EOI)) << BUS_SHIFT; /* shifted to tag locations */ + + if ((di_card->cntl_register & CNTL_EOI /* EOI detection enabled, */ + && di_card->bus_cntl & BUS_EOI) /* and data was tagged with EOI? */ + || (di_card->cntl_register & CNTL_LF /* or LF detection enabled, */ + && GET_LOWER (data) == LF)) { /* and byte is a line feed? */ + tag = tag | TAG_LBR; /* tag as last byte received */ + di_card->status_register |= STAT_LBI; /* set last byte in status */ + } + else /* neither termination condition seen */ + di_card->status_register &= ~STAT_LBI; /* so clear last byte in status */ + } + +else /* card is transmitting */ + tag = (data & (DATA_ATN | DATA_EOI)) << DATA_SHIFT; /* set tag from data shifted to tag location */ + +if (di_card->edt == SET) /* end of data transfer? */ + tag = tag | TAG_EDT; /* set EDT tag */ + + +index = (di_card->fifo_reg->qptr /* calculate index */ + + di_card->fifo_count) % FIFO_SIZE; /* of next available location */ + +if (access == bus_access) { /* bus access? */ + if (di_card->ibp == upper) { /* packed and this is the upper byte? */ + di_card->ibp = lower; /* set lower byte as next */ + + if (tag & TAG_LBR) /* is this the last byte? */ + di_card->fifo [index] = /* copy to both bytes of FIFO */ + tag | SET_BOTH (data); /* and store with tag */ + else { /* more bytes expected */ + di_card->fifo [index] = /* so position this byte */ + tag | SET_UPPER (data); /* and store with tag */ + add_word = FALSE; /* wait for second byte before adding */ + } + } + + else /* this is the lower byte */ + if (di_card->cntl_register & CNTL_PACK) { /* packed mode? */ + di_card->ibp = upper; /* set upper byte as next */ + + di_card->fifo [index] = /* merge in data and tag */ + tag | di_card->fifo [index] | SET_LOWER (data); + } + else /* unpacked mode */ + di_card->fifo [index] = /* position this byte */ + tag | SET_LOWER (data); /* and store with tag */ + } + +else if (access == cpu_access) /* cpu access? */ + di_card->fifo [index] = tag | data; /* store tag and full word in FIFO */ + +else { /* diag access */ + data = SET_BOTH (GET_LOWER (data)); /* copy lower byte to upper byte */ + di_card->fifo [index] = tag | data; /* and store tag and full word in FIFO */ + } + +if (add_word) /* did we add a word to the FIFO? */ + di_card->fifo_count = di_card->fifo_count + 1; /* increment count of words stored */ + +if (DEBUG_PRJ (dptrs [card], DEB_BUF)) { + fprintf (sim_deb, ">>%s buf: Data %0*o tag ", + dptrs [card]->name, (access == bus_access ? 3 : 6), data); + fprint_val (sim_deb, tag >> BUS_SHIFT, 2, 4, PV_RZRO); + fprintf (sim_deb, " loaded into FIFO (%d)\n", di_card->fifo_count); + } + +return; +} + + +/* Unload a word or byte from the FIFO. + + A word or byte is unloaded from the first location in the FIFO. The + significance of the returned value is indicated by the access mode as + follows: + + - For CPU access, a 16-bit value is unloaded and returned. + + - For bus access, an 8-bit value is unloaded and returned. + + - For diagnostic access, an 16-bit value is unloaded, and the lower byte + is returned. + + For bus access, byte unloading from the FIFO is controlled by the value of + the Output Buffer Pointer (OBP) selector. + + If the FIFO is not empty, the first entry is obtained and split into tag and + data words. The LBR tag value is loaded into the EOR flip-flop if the CPU is + accessing. The EDT tag sets Last Byte Out status if the last byte is being + unloaded. + + If the data is going to the CPU, the 16-bit packed data value is returned as + is, or the lower byte of the unpacked value is merged with the tags for ATN + and EOI and returned. The occupancy count is decremented to unload the FIFO + entry. + + If the data is going to the bus, and the input mode is unpacked, the 8-bit + value is returned in the lower byte, and the occupancy count is decremented. + In hardware, the upper FIFO is not clocked; in simulation, the upper byte is + ignored. The OBP always points at the lower byte in unpacked mode. + + If the data is going to the bus, and the input mode is packed, the 8-bit + value is unloaded from either the upper or lower byte of the data word, + depending on the value of the OBP, and returned in the lower byte. The OBP + value is toggled. If the value was obtained from the lower byte, the + occupancy count is decremented to unload the FIFO. Otherwise, the count is + not altered, so that the lower-byte access will be from the same FIFO entry. + + If data is going to the diagnostic FIFO loopback, the lower byte of the + 16-bit value is returned; the upper byte of the returned value is zero. + + + Implementation notes: + + 1. Four tag bits are unloaded from the upper word of each FIFO entry: + + - Last Byte Received (sets the End of Record flip-flop when the last + byte received is loaded into the Input Data Register). + + - End of Data Transfer (sets the LBO bit in the Status Word Register + when the last byte is unloaded from the FIFO). + + - ATN (in unpacked mode, sets the ATN bit in the returned data word + if listening, or controls the bus ATN line if talking; in packed mode, + the tag is ignored). + + - EOI (in unpacked mode, sets the EOI bit in the returned data word if + listening, or asserts the bus EOI line if talking; in packed mode, the + tag is ignored). + + ATN and EOI tag handling is a bit complex. If the card is listening in + the unpacked mode, the ATN tag substitutes for bit 8 of the data word, + and the EOI tag substitutes for bit 9. In the packed mode, bits 8 and 9 + are as stored in the FIFO (they are upper-byte data bits). + + If the card is talking in the unpacked mode, the ATN tag asserts or + denies ATN on the bus if the card is the CIC, and the EOI tag asserts or + denies EOI on the bus. In the packed mode, the ATN bit in the Control + Word Register asserts or denies ATN on the bus if the card is the CIC, + and the EOI bit asserts EOI on the bus if the last byte of the entry + tagged with EDT has been unloaded from the FIFO (which sets LBO status) + or denies EOI otherwise. + + 2. In hardware, the EOR flip-flop is clocked with the Input Data Register. + Therefore, when the card is listening, EOR is set not when the last byte + is unloaded from the FIFO, but rather when that byte is loaded into the + IDR. These two actions occur together when the IDR is empty. + + However, during diagnostic access, data unloaded from the FIFO is + reloaded, and the IDR is never clocked. As the T and L bits must be set + with DIAG in the Control Word Register to enable the loopback path, the + LBR tag will be entered into the FIFO if EOI or LF detection is enabled, + but the EOR flip-flop will not be set when that word falls through to be + unloaded. + + In simulation, EOR is set whenever the LBR tag is unloaded from the FIFO + during CPU access, as a CPU unload is always followed by an IDR store. + + 3. If fifo_count > 0, REG->qptr is the index of the word to remove. Removal + gets the word and then increments qptr (mod FIFO_SIZE) and decrements + fifo_count. + + 4. The gcc compiler (at least as of version 4.6.2) does not optimize + repeated use of array-of-structures accesses. Instead, it recalculates + the index each time, even though the index is a constant within the + function. To avoid this performance penalty, we use a pointer to the + selected DI_STATE structure. Note that VC++ 2008 does perform this + optimization. +*/ + +static uint16 fifo_unload (CARD_ID card, FIFO_ACCESS access) +{ +uint32 data, tag; +t_bool remove_word = TRUE; +DI_STATE * const di_card = &di [card]; + +if (FIFO_EMPTY) { /* is the FIFO already empty? */ + if (DEBUG_PRJ (dptrs [card], DEB_BUF)) + fprintf (sim_deb, ">>%s buf: Attempted unload from empty FIFO\n", + dptrs [card]->name); + + return 0; /* return with no data */ + } + +data = di_card->fifo [di_card->fifo_reg->qptr]; /* get tag and data from the FIFO */ + +tag = data & TAG_MASK; /* mask the tag to just the tag bits */ +data = data & DMASK; /* and the data to just the data bits */ + +if (tag & TAG_EDT /* is this the end of a data transfer */ + && (di_card->obp == lower /* and the lower byte is next */ + || di_card->cntl_register & CNTL_ODD)) /* or we are sending an odd number of bytes? */ + di_card->status_register |= STAT_LBO; /* set the last byte out status */ + + +if (access == cpu_access) { /* cpu access? */ + if (!(di_card->cntl_register & CNTL_PACK)) /* unpacked data format? */ + data = data & ~(DATA_ATN | DATA_EOI) /* substitute ATN/EOI tag values */ + | (tag & (TAG_ATN | TAG_EOI)) >> DATA_SHIFT; /* into the data word */ + + if (tag & TAG_LBR) /* is this the last byte? */ + di_card->eor = SET; /* set */ + else /* or clear */ + di_card->eor = CLEAR; /* the end-of-record flip-flop */ + } + +else if (access == bus_access) /* bus access? */ + if (di_card->obp == upper) { /* is this the upper byte? */ + di_card->obp = lower; /* set the lower byte as next */ + data = GET_UPPER (data); /* mask and position the upper byte in the data word */ + remove_word = FALSE; /* do not unload the FIFO until the next byte */ + } + + else { /* this is the lower byte */ + data = GET_LOWER (data); /* mask and position it in the data word */ + + if (di_card->cntl_register & CNTL_PACK) /* in packed mode? */ + di_card->obp = upper; /* set the upper byte as next */ + } + +else /* diagnostic access */ + data = GET_LOWER (data); /* access is to the lower byte only */ + + +if (remove_word) { /* remove the word from the FIFO? */ + di_card->fifo_reg->qptr = /* update the FIFO queue pointer */ + (di_card->fifo_reg->qptr + 1) % FIFO_SIZE; /* and wrap around as needed */ + + di_card->fifo_count = di_card->fifo_count - 1; /* decrement the FIFO count */ + } + + +if (DEBUG_PRJ (dptrs [card], DEB_BUF)) { + fprintf (sim_deb, ">>%s buf: Data %0*o tag ", + dptrs [card]->name, (access == cpu_access ? 6 : 3), data); + fprint_val (sim_deb, tag >> BUS_SHIFT, 2, 4, PV_RZRO); + fprintf (sim_deb, " unloaded from FIFO (%d)\n", di_card->fifo_count); + } + + +if (di_card->cntl_register & CNTL_TALK) /* is the card talking? */ + if (di_card->cntl_register & CNTL_PACK) /* in packed mode? */ + if (di_card->status_register & STAT_LBO /* yes, is the last byte out? */ + && di_card->cntl_register & CNTL_EOI) /* and EOI control enabled? */ + di_card->bus_cntl |= BUS_EOI; /* assert EOI */ + else + di_card->bus_cntl &= ~BUS_EOI; /* deny EOI */ + + else { /* in unpacked mode */ + if (di_card->cntl_register & CNTL_CIC) /* is card the controller in charge? */ + di_card->bus_cntl = /* assert or deny the ATN bus line */ + di_card->bus_cntl & ~BUS_ATN /* from the ATN tag value */ + | (tag & TAG_ATN) >> BUS_SHIFT; + + di_card->bus_cntl = /* assert or deny the EOI bus line */ + di_card->bus_cntl & ~BUS_EOI /* from the EOI tag value */ + | (tag & TAG_EOI) >> BUS_SHIFT; + } + +return (uint16) data; /* return the data value */ +} + + +/* Print the bus state for debugging. + + The states of the supplied bus control lines are decoded and printed in + mnemonic form to the specified file using the indicated format string. An + asserted bus signal is indicated by its name; a denied signal is omitted. + + + Implementation notes: + + 1. The strings in the cntl_names array must appear in BUS_xxx order. The + first element corresponds to bus bit 0, etc. +*/ + +static void fprint_bus (FILE *file, char *format, uint8 cntl) +{ +static const char *cntl_names [] = { + "ATN", /* bit 0: attention */ + "EOI", /* bit 1: end or identify */ + "DAV", /* bit 2: data available */ + "NRFD", /* bit 3: not ready for data */ + "NDAC", /* bit 4: not data accepted */ + "REN", /* bit 5: remote enable */ + "IFC", /* bit 6: interface clear */ + "SRQ" /* bit 7: service request */ + }; + +uint32 signal; +char mnemonics [40]; + +if (cntl == 0) /* any control signal asserted? */ + strcpy (mnemonics, "---"); /* no; use dashes in lieu of an empty string */ + +else { /* one or more signals are asserted */ + mnemonics [0] = '\0'; + + for (signal = 0; signal <= 7; signal++) /* loop though the set of signals */ + if (cntl & (1 << signal)) { /* is this signal asserted? */ + if (strlen (mnemonics) > 0) /* yes; is it the first one asserted? */ + strcat (mnemonics, " "); /* no, so append a space to separate */ + strcat (mnemonics, cntl_names [signal]); /* append the name of the asserted signal */ + } + } + +fprintf (file, format, mnemonics); /* print the bus state */ +return; +} diff --git a/HP2100/hp2100_di.h b/HP2100/hp2100_di.h new file mode 100644 index 00000000..79d742ab --- /dev/null +++ b/HP2100/hp2100_di.h @@ -0,0 +1,296 @@ +/* hp2100_di.h: HP 12821A HP-IB Disc Interface simulator common definitions + + Copyright (c) 2010-2012, J. David Bryan + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + DI 12821A Disc Interface + + 14-Feb-12 JDB First release + 16-Nov-10 JDB Created DI common definitions file + + + This file defines the interface between HP-IB device simulators and the + 12821A Disc Interface simulator. It must be included by the device-specific + modules (hp2100_di_da.c, etc.). + + + Implementation notes: + + 1. Three CARD_ID values are defined, corresponding to the Amigo disc (DA), + CS/80 disc (DC), and Amigo mag tape (MA) simulators. At first release, + only the DA device is implemented. However, as the 12821A diagnostic + requires two cards to test I/O fully, a dummy DC device is provided by + the DA simulator. It is enabled only when the DA card is configured for + diagnostic mode. This dummy device may be removed when either the DC or + MA device is implemented. +*/ + + + +/* Program constants */ + +#define FIFO_SIZE 16 /* FIFO depth */ + +typedef enum { da, dc, ma, /* card IDs */ + first_card = da, /* first card ID */ + last_card = ma, /* last card ID */ + card_count } CARD_ID; /* count of card IDs */ + + +/* Device flags and accessors (leaves space for disc/tape flags) */ + +#define DEV_V_BUSADR (DEV_V_UF + 8) /* bits 10-8: HP-IB address */ +#define DEV_V_DIAG (DEV_V_UF + 11) /* bit 11: diagnostic mode */ +#define DEV_V_W1 (DEV_V_UF + 12) /* bit 12: DCPC pacing jumper */ + +#define DEV_M_BUSADR 7 /* bus address mask */ + +#define DEV_BUSADR (DEV_M_BUSADR << DEV_V_BUSADR) +#define DEV_DIAG (1 << DEV_V_DIAG) +#define DEV_W1 (1 << DEV_V_W1) + +#define GET_DIADR(f) (((f) >> DEV_V_BUSADR) & DEV_M_BUSADR) +#define SET_DIADR(f) (((f) & DEV_M_BUSADR) << DEV_V_BUSADR) + + +/* Unit flags and accessors (leaves space for disc/tape flags) */ + +#define UNIT_V_BUSADR (UNIT_V_UF + 8) /* bits 10-8: HP-IB address */ + +#define UNIT_M_BUSADR 7 /* bus address mask */ + +#define UNIT_BUSADR (UNIT_M_BUSADR << UNIT_V_BUSADR) + +#define GET_BUSADR(f) (((f) >> UNIT_V_BUSADR) & UNIT_M_BUSADR) +#define SET_BUSADR(f) (((f) & UNIT_M_BUSADR) << UNIT_V_BUSADR) + + +/* Debug flags */ + +#define DEB_CPU (1 << 0) /* words received from and sent to the CPU */ +#define DEB_CMDS (1 << 1) /* interface commands received from the CPU */ +#define DEB_BUF (1 << 2) /* data read from and written to the card FIFO */ +#define DEB_XFER (1 << 3) /* data received and transmitted via HP-IB */ +#define DEB_RWSC (1 << 4) /* device read/write/status/control commands */ +#define DEB_SERV (1 << 5) /* unit service scheduling calls */ + + +/* HP-IB control state bit flags. + + NOTE that these flags align with the corresponding flags in the DI status + register, so don't change the order! +*/ + +#define BUS_ATN 0001 /* attention */ +#define BUS_EOI 0002 /* end or identify */ +#define BUS_DAV 0004 /* data available */ +#define BUS_NRFD 0010 /* not ready for data */ +#define BUS_NDAC 0020 /* not data accepted */ +#define BUS_REN 0040 /* remote enable */ +#define BUS_IFC 0100 /* interface clear */ +#define BUS_SRQ 0200 /* service request */ + +#define BUS_PPOLL (BUS_ATN | BUS_EOI) /* parallel poll */ + +/* HP-IB data */ + +#define BUS_ADDRESS 0037 /* bus address mask */ +#define BUS_GROUP 0140 /* bus group mask */ +#define BUS_COMMAND 0160 /* bus command type mask */ +#define BUS_DATA 0177 /* bus data mask */ +#define BUS_PARITY 0200 /* bus parity mask */ + +#define BUS_PCG 0000 /* primary command group */ +#define BUS_LAG 0040 /* listen address group */ +#define BUS_TAG 0100 /* talk address group */ +#define BUS_SCG 0140 /* secondary command group */ + +#define BUS_UCG 0020 /* universal command group */ +#define BUS_ACG 0000 /* addressed command group */ + +#define BUS_UNADDRESS 0037 /* unlisten and untalk */ + +#define PPR(a) (uint8) (1 << (7 - (a))) /* parallel poll response */ + + +/* Byte accessors */ + +#define BYTE_SHIFT 8 /* byte shift count */ +#define UPPER_BYTE 0177400 /* high-order byte mask */ +#define LOWER_BYTE 0000377 /* low-order byte mask */ + +#define GET_UPPER(w) (uint8) (((w) & UPPER_BYTE) >> BYTE_SHIFT) +#define GET_LOWER(w) (uint8) ((w) & LOWER_BYTE) + +#define SET_UPPER(b) ((b) << BYTE_SHIFT) +#define SET_LOWER(b) (b) +#define SET_BOTH(b) (SET_UPPER (b) | SET_LOWER (b)) + +typedef enum { upper, /* byte selector */ + lower } SELECTOR; + + +/* Per-card state variables */ + +typedef struct { + FLIP_FLOP control; /* control flip-flop */ + FLIP_FLOP flag; /* flag flip-flop */ + FLIP_FLOP flagbuf; /* flag buffer flip-flop */ + FLIP_FLOP srq; /* SRQ flip-flop */ + FLIP_FLOP edt; /* EDT flip-flop */ + FLIP_FLOP eor; /* EOR flip-flop */ + SELECTOR ibp; /* input byte pointer selector */ + SELECTOR obp; /* output byte pointer selector */ + + uint16 cntl_register; /* control word register */ + uint16 status_register; /* status word register */ + uint16 input_data_register; /* input data register */ + + uint32 fifo [FIFO_SIZE]; /* FIFO buffer */ + uint32 fifo_count; /* FIFO occupancy counter */ + REG *fifo_reg; /* FIFO register pointer */ + + uint32 acceptors; /* unit bitmap of bus acceptors */ + uint32 listeners; /* unit bitmap of bus listeners */ + uint32 talker; /* unit bitmap of bus talker */ + + uint8 bus_cntl; /* HP-IB bus control state (ATN, EOI, etc.) */ + uint8 poll_response; /* address bitmap of parallel poll responses */ + + double ifc_timer; /* 100 microsecond IFC timer */ + } DI_STATE; + + +/* Disc interface VM global register definitions. + + Include these definitions before any device-specific registers. + + + Implementation notes: + + 1. The TMR register is included to ensure that the IFC timer is saved by a + SAVE command. It is declared as a hidden, read-only byte array of a size + compatible with a double-precision floating-point value, as there is no + appropriate macro for the double type. +*/ + +#define DI_REGS(dev) \ + { ORDATA (CWR, di [dev].cntl_register, 16), REG_FIT }, \ + { ORDATA (SWR, di [dev].status_register, 16), REG_FIT }, \ + { ORDATA (IDR, di [dev].input_data_register, 16), REG_FIT }, \ + \ + { DRDATA (FCNT, di [dev].fifo_count, 5) }, \ + { BRDATA (FIFO, di [dev].fifo, 8, 20, FIFO_SIZE), REG_CIRC }, \ + \ + { GRDATA (ACPT, di [dev].acceptors, 2, 4, 0) }, \ + { GRDATA (LSTN, di [dev].listeners, 2, 4, 0) }, \ + { GRDATA (TALK, di [dev].talker, 2, 4, 0) }, \ + { GRDATA (PPR, di [dev].poll_response, 2, 8, 0), REG_FIT }, \ + { GRDATA (BUSCTL, di [dev].bus_cntl, 2, 8, 0), REG_FIT }, \ + \ + { FLDATA (CTL, di [dev].control, 0) }, \ + { FLDATA (FLG, di [dev].flag, 0) }, \ + { FLDATA (FBF, di [dev].flagbuf, 0) }, \ + { FLDATA (SRQ, di [dev].srq, 0) }, \ + { FLDATA (EDT, di [dev].edt, 0) }, \ + { FLDATA (EOR, di [dev].eor, 0) }, \ + \ + { BRDATA (TMR, &di [dev].ifc_timer, 10, CHAR_BIT, sizeof (double)), REG_HRO }, \ + \ + { ORDATA (SC, dev##_dib.select_code, 6), REG_HRO } + + +/* Disc interface VM global modifier definitions. + + Include these definitions before any device-specific modifiers. +*/ + +#define DI_MODS(dev) \ + { MTAB_XTD | MTAB_VDV, 1, "ADDRESS", "ADDRESS", &di_set_address, &di_show_address, &dev }, \ + \ + { MTAB_XTD | MTAB_VDV, 1, NULL, "DIAG", &di_set_cable, NULL, &dev }, \ + { MTAB_XTD | MTAB_VDV, 0, NULL, "HPIB", &di_set_cable, NULL, &dev }, \ + { MTAB_XTD | MTAB_VDV, 0, "CABLE", NULL, NULL, &di_show_cable, &dev }, \ + \ + { MTAB_XTD | MTAB_VDV, 0, "SC", "SC", &hp_setsc, &hp_showsc, &dev }, \ + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &dev }, \ + \ + { MTAB_XTD | MTAB_VUN, 0, "BUS", "BUS", &di_set_address, &di_show_address, &dev } + + +/* Disc interface global bus routine definitions */ + +typedef t_bool ACCEPTOR (uint32 unit, uint8 data); +typedef void RESPONDER (CARD_ID card, uint32 unit, uint8 new_cntl); + + +/* Disc interface global variables */ + +extern DI_STATE di []; +extern DEBTAB di_deb []; + + +/* Disc interface global VM routines */ + +extern IOHANDLER di_io; +extern t_stat di_reset (DEVICE *dptr); + +/* Disc interface global SCP routines */ + +extern t_stat di_set_address (UNIT *uptr, int32 value, char *cptr, void *desc); +extern t_stat di_show_address (FILE *st, UNIT *uptr, int32 value, void *desc); +extern t_stat di_set_cable (UNIT *uptr, int32 value, char *cptr, void *desc); +extern t_stat di_show_cable (FILE *st, UNIT *uptr, int32 value, void *desc); + +/* Disc interface global bus routines */ + +extern t_bool di_bus_source (CARD_ID card, uint8 data); +extern void di_bus_control (CARD_ID card, uint32 unit, uint8 assert, uint8 deny); +extern void di_poll_response (CARD_ID card, uint32 unit, FLIP_FLOP response); + + +/* Amigo disc global VM routines */ + +extern t_stat da_service (UNIT *uptr); +extern t_stat da_boot (int32 unitno, DEVICE *dptr); + +/* Amigo disc global bus routines */ + +extern ACCEPTOR da_bus_accept; +extern RESPONDER da_bus_respond; + + +/* Amigo mag tape global VM routines */ + +extern t_stat ma_service (UNIT *uptr); +extern t_stat ma_boot (int32 unitno, DEVICE *dptr); + +/* Amigo mag tape global SCP routines */ + +extern t_stat ma_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat ma_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc); + +/* Amigo mag tape global bus routines */ + +extern ACCEPTOR ma_bus_accept; +extern RESPONDER ma_bus_respond; diff --git a/HP2100/hp2100_di_da.c b/HP2100/hp2100_di_da.c new file mode 100644 index 00000000..24b1766d --- /dev/null +++ b/HP2100/hp2100_di_da.c @@ -0,0 +1,2127 @@ +/* hp2100_di_da.c: HP 12821A HP-IB Disc Interface simulator for Amigo disc drives + + Copyright (c) 2011-2012, J. David Bryan + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + DA 12821A Disc Interface with Amigo disc drives + + 21-Feb-12 JDB First release + 04-Nov-11 JDB Created DA device + + References: + - HP 13365 Integrated Controller Programming Guide (13365-90901, Feb-1980) + - HP 7910 Disc Drive Service Manual (07910-90903, Apr-1981) + - 12745D Disc Controller (13037) to HP-IB Adapter Kit Installation and + Service Manual (12745-90911, Sep-1983) + - HP's 5 1/4-Inch Winchester Disc Drive Service Documentation + (09134-90032, Aug-1983) + - HP 12992 Loader ROMs Installation Manual (12992-90001, Apr-1986) + - RTE Driver DVA32 Source (92084-18708, revision 2540) + - IEEE Standard Digital Interface for Programmable Instrumentation + (IEEE-488A-1980, Sep-1979) + + + The HP 7906H, 7920H, and 7925H Integrated Controller Disc (ICD) drives were + connected via an 12821A disc interface and provided 20MB, 50MB, and 120MB + capacities. The drives were identical to the 7906M, 7920M, and 7925M + Multi-Access Controller (MAC) units but incorporated internal two-card + controllers in each drive and connected to the CPU interface via HP-IB. Each + controller was dedicated to a single drive and operated similarly to the + 12745 Disc Controller to HP-IB Adapter option for the 13037 Disc Controller + box. The 7906H was introduced in 1980 (there was no 7905H version, as the + 7905 was obsolete by that time). Up to four ICD drives could be connected to + a single 12821A card. The limitation was imposed by the bus loading and the + data transfer rate. + + The ICD command set essentially was the MAC command set modified for + single-unit operation. The unit number and CPU hold bit fields in the opcode + words were unused in the ICD implementation. The Load TIO Register, Wakeup, + and Request Syndrome commands were removed, as Load TIO was used with the HP + 3000, Wakeup was used in a multi-CPU environment, and the simpler ICD + controller did not support ECC. Controller status values 02B (Unit + Available) and 27B (Unit Unavailable) were dropped as the controller + supported only single units, 12B (I/O Program Error) was reused to indicate + HP-IB sequence errors, 13B (Sync Not Received) was added, and 17B (Possibly + Correctable Data Error) was removed as error correction was not supported. + + Some minor redefinitions also occurred. For example, status 14B (End of + Cylinder) was expanded to include an auto-seek beyond the drive limits, and + 37B (Drive Attention) was restricted from head loads and unloads to just head + unloads. + + The command set was expanded to include several commands related to HP-IB + operation. These were, in large part, adapted from the Amigo disc command + protocol outlined in the service manual for the HP 9133/34/35 series of + 5-1/4" Winchester drives. They include the Amigo Identify and Amigo Clear + sequences, Read and Write Loopback channel tests, and controller Self Test + commands. + + Unfortunately, the primary reference for the ICD controller (the HP 13365 + Integrated Controller Programming Guide) does not indicate parallel poll + responses for these HP-IB commands. Therefore, the responses have been + derived from the sequences in the 7910 and 12745 manuals, although they + sometimes conflict. + + This simulator implements the Amigo disc protocol. It calls the 12821A Disc + Interface (DI) simulator to send and receive bytes across the HP-IB to and + from the CPU, and it calls the HP Disc Library to implement the controller + functions related to disc unit operation (e.g., seek, read, write, etc.). + Four units are provided, and any combination of 7906H/20H/25H drives may be + defined. + + The drives respond to the following commands; numeric values are in hex, and + bus addressing is indicated by U [untalk], L [listen], and T [talk]: + + Bus Sec Op Operation + --- --- -- -------------------------------- + U MSA -- Amigo Identify + + L 00 -- Write Data + L 08 00 Cold Load Read + L 08 01 Recalibrate + L 08 02 Seek + L 08 03 Request Status + L 08 04 Request Sector Address + L 08 05 Read + L 08 06 Read Full Sector + L 08 07 Verify + L 08 08 Write + L 08 09 Write Full Sector + L 08 0A Clear + L 08 0B Initialize + L 08 0C Address Record + L 08 0E Read with Offset + L 08 0F Set File Mask + L 08 12 Read without Verify + L 08 14 Request Logical Disc Address + L 08 15 End + L 09 -- Cyclic Redundancy Check + L 10 -- Amigo Clear + L 1E -- Write Loopback + L 1F ss Initiate Self-Test + + T 00 -- Read Data + T 08 -- Read Status + T 09 -- Cyclic Redundancy Check + T 10 -- Device Specified Jump + T 1E -- Read Loopback + T 1F -- Return Self-Test Result + + In addition, the controller responds to the Selected Device Clear primary + (04). + + + HP-IB Transaction Sequences + =========================== + + Amigo Identify + + ATN UNT Untalk + ATN MSA My secondary address + DAB ID data byte #1 = 00H + EOI DAB ID data byte #2 = 03H + ATN OTA Talk 30 + + + Amigo Clear + + ATN MLA My listen address + ATN SCG Secondary command 10H + ppd Parallel poll disabled + EOI DAB Unused data byte + ATN SDC Selected device clear + ATN UNL Unlisten + ... + ppe Parallel poll enabled when clear completes + + + CRC + + ATN MTA My talk address + ATN SCG Secondary command 09H + ppd Parallel poll disabled + DAB Data byte #1 + ... + EOI DAB Data byte #n + ppe Parallel poll enabled + ATN UNT Untalk + + or + + ATN MLA My listen address + ATN SCG Secondary command 09H + ppd Parallel poll disabled + DAB Data byte #1 + ... + EOI DAB Data byte #n + ppe Parallel poll enabled + ATN UNL Unlisten + + + Device Specified Jump + + ATN MTA My talk address + ATN SCG Secondary command 10H + ppd Parallel poll disabled + EOI DAB DSJ data byte + ATN UNT Untalk + + + Initiate Self-Test and Return Self-Test Result + + ATN MLA My listen address + ATN SCG Secondary command 1FH + ppd Parallel poll disabled + EOI DAB Self-test number + ppe Parallel poll enabled + ATN UNL Unlisten + + ATN MTA My talk address + ATN SCG Secondary command 1FH + ppd Parallel poll disabled + EOI DAB Result data byte + ppe Parallel poll enabled + ATN UNT Untalk + + + Write Loopback and Read Loopback + + ATN MLA My listen address + ATN SCG Secondary command 1EH + ppd Parallel poll disabled + DAB Loopback data byte #1 + ... + EOI DAB Loopback data byte #256 + ppe Parallel poll enabled + ATN UNL Unlisten + + ATN MTA My talk address + ATN SCG Secondary command 1EH + ppd Parallel poll disabled + DAB Loopback data byte #1 + ... + EOI DAB Loopback data byte #16 + ppe Parallel poll enabled + ATN UNT Untalk + + + Recalibrate and Seek + + ATN MLA My listen address + ATN SCG Secondary command 08H + ppd Parallel poll disabled + DAB Opcode 01H, 02H + ... (one to five + EOI DAB parameter bytes) + ATN UNL Unlisten + ... + ppe Parallel poll enabled when seek completes + + + Clear, Address Record, and Set File Mask + + ATN MLA My listen address + ATN SCG Secondary command 08H + ppd Parallel poll disabled + DAB Opcode 0AH, 0CH, 0FH + ... (one to five + EOI DAB parameter bytes) + ppe Parallel poll enabled + ATN UNL Unlisten + + + End + + ATN MLA My listen address + ATN SCG Secondary command 08H + ppd Parallel poll disabled + DAB Opcode 15H + EOI DAB Unused data byte + ATN UNL Unlisten + + + Request Status, Request Sector Address, and Request Logical Disc Address + + ATN MLA My listen address + ATN SCG Secondary command 08H + ppd Parallel poll disabled + DAB Opcode 03H, 04H, 14H + EOI DAB Unused data byte + ATN UNL Unlisten + + ATN MTA My talk address + ATN SCG Secondary command 08H + DAB Status byte #1 + ... (two to four + EOI DAB status bytes) + ppe Parallel poll enabled + ATN UNT Untalk + + + Cold Load Read, Read, Read Full Sector, Verify, Read with Offset, and Read + without Verify + + ATN MLA My listen address + ATN SCG Secondary command 08H + ppd Parallel poll disabled + DAB Opcode 00H, 05H, 06H, 07H, 0EH, 12H + EOI DAB Unused data byte + ATN UNL Unlisten + + ATN MTA My talk address + ATN SCG Secondary command 00H + DAB Read data byte #1 + ... + DAB Read data byte #n + ATN UNT Untalk + ... + ppe Parallel poll enabled when sector ends + + + Write, Write Full Sector, and Initialize + + ATN MLA My listen address + ATN SCG Secondary command 08H + ppd Parallel poll disabled + DAB Opcode 08H, 09H, 0BH + EOI DAB Unused data byte + ATN UNL Unlisten + + ATN MLA My listen address + ATN SCG Secondary command 00H + DAB Write data byte #1 + ... + EOI DAB Write data byte #n + ppe Parallel poll enabled + ATN UNL Unlisten + + + Implementation notes: + + 1. The 12745 does not alter the parallel poll response for the + Device-Specified Jump command. + + 2. The 7910 does not perform a parallel poll response enable and disable + between the Initiate Self-Test and Return Self-Test Result commands. + + 3. The 12745 does not disable the parallel poll response for the Read + Loopback command. +*/ + + + +#include "hp2100_defs.h" +#include "hp2100_di.h" +#include "hp_disclib.h" + + + +/* Program constants */ + +#define DA_UNITS 4 /* number of addressable disc units */ + + +/* Interface states */ + +typedef enum { + idle = 0, /* idle = default for reset */ + opcode_wait, /* waiting for opcode reception */ + parameter_wait, /* waiting for parameter reception */ + read_wait, /* waiting for send read data secondary */ + write_wait, /* waiting for receive write data secondary */ + status_wait, /* waiting for send status secondary */ + command_exec, /* executing an interface command */ + command_wait, /* waiting for command completion */ + read_xfer, /* sending read data or status */ + write_xfer, /* receiving write data */ + error_source, /* sending bytes for error recovery */ + error_sink /* receiving bytes for error recovery */ + } IF_STATE; + + +/* Interface state names */ + +static const char *if_state_name [] = { + "idle", + "opcode wait", + "parameter wait", + "read wait", + "write wait", + "status wait", + "command execution", + "command wait", + "read transfer", + "write transfer", + "error source", + "error sink" + }; + + +/* Next interface state after command recognition */ + +static const IF_STATE next_state [] = { + read_wait, /* cold load read */ + command_exec, /* recalibrate */ + command_exec, /* seek */ + status_wait, /* request status */ + status_wait, /* request sector address */ + read_wait, /* read */ + read_wait, /* read full sector */ + command_exec, /* verify */ + write_wait, /* write */ + write_wait, /* write full sector */ + command_exec, /* clear */ + write_wait, /* initialize */ + command_exec, /* address record */ + idle, /* request syndrome */ + read_wait, /* read with offset */ + command_exec, /* set file mask */ + idle, /* invalid */ + idle, /* invalid */ + read_wait, /* read without verify */ + idle, /* load TIO register */ + status_wait, /* request disc address */ + command_exec, /* end */ + idle /* wakeup */ + }; + + +/* Interface commands */ + +typedef enum { + invalid = 0, /* invalid = default for reset */ + + disc_command, /* MLA 08 */ + crc_listen, /* MLA 09 */ + amigo_clear, /* MLA 10 */ + write_loopback, /* MLA 1E */ + initiate_self_test, /* MLA 1F */ + + crc_talk, /* MTA 09 */ + device_specified_jump, /* MTA 10 */ + read_loopback, /* MTA 1E */ + return_self_test_result, /* MTA 1F */ + + amigo_identify /* UNT MSA */ + } IF_COMMAND; + +/* Interface command names */ + +static const char *if_command_name [] = { + "invalid", + + "disc command", + "CRC listen", + "Amigo clear", + "write loopback", + "initiate self-test", + + "CRC talk", + "device specified jump", + "read loopback", + "return self-test result", + + "Amigo identify" + }; + + + +/* Amigo disc state variables */ + +static uint16 buffer [DL_BUFSIZE]; /* command/status/sector buffer */ + +static uint8 if_dsj [DA_UNITS]; /* ICD controller DSJ values */ +static IF_STATE if_state [DA_UNITS]; /* ICD controller state */ +static IF_COMMAND if_command [DA_UNITS]; /* ICD controller command */ + +static CNTLR_VARS icd_cntlr [DA_UNITS] = /* ICD controllers: */ + { { CNTLR_INIT (ICD, buffer, NULL) }, /* unit 0 controller */ + { CNTLR_INIT (ICD, buffer, NULL) }, /* unit 1 controller */ + { CNTLR_INIT (ICD, buffer, NULL) }, /* unit 2 controller */ + { CNTLR_INIT (ICD, buffer, NULL) } }; /* unit 3 controller */ + + + +/* Amigo disc global VM routines */ + +t_stat da_service (UNIT *uptr); +t_stat da_reset (DEVICE *dptr); +t_stat da_attach (UNIT *uptr, char *cptr); +t_stat da_detach (UNIT *uptr); +t_stat da_boot (int32 unitno, DEVICE *dptr); + +/* Amigo disc global SCP routines */ + +t_stat da_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc); + +/* Amigo disc global bus routines */ + +t_bool da_bus_accept (uint32 unit, uint8 data); +void da_bus_respond (CARD_ID card, uint32 unit, uint8 new_cntl); + +/* Amigo disc local utility routines */ + +static t_bool start_command (uint32 unit); +static void abort_command (uint32 unit, CNTLR_STATUS status, IF_STATE state); +static void complete_read (uint32 unit); +static void complete_write (uint32 unit); +static void complete_abort (uint32 unit); +static uint8 get_buffer_byte (CVPTR cvptr); +static void put_buffer_byte (CVPTR cvptr, uint8 data); +static t_stat activate_unit (UNIT *uptr); + + + +/* Amigo disc VM global data structures. + + da_dib DA device information block + da_unit DA unit list + da_reg DA register list + da_mod DA modifier list + da_dev DA device descriptor + + + Implementation notes: + + 1. The IFSTAT and IFCMD registers are declared to accommodate the + corresponding arrays of enums. Arrayed registers assume that elements + are allocated space only to the integral number of bytes implied by the + "width" field. The storage size of an enum is implementation-defined, so + we must determine the number of bits for "width" at compile time. + PV_LEFT is used to avoid the large number of leading zeros that would be + displayed if an implementation stored enums in full words. + + 2. The CNVARS register is included to ensure that the controller state + variables array is saved by a SAVE command. It is declared as a hidden, + read-only byte array of a depth compatible with the size of the array. +*/ + +DEVICE da_dev; + +DIB da_dib = { &di_io, DI_DA, da }; + +#define UNIT_FLAGS (UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD) + +UNIT da_unit [] = { + { UDATA (&da_service, UNIT_FLAGS | MODEL_7906 | SET_BUSADR (0), D7906_WORDS) }, /* bus address 0 */ + { UDATA (&da_service, UNIT_FLAGS | MODEL_7906 | SET_BUSADR (1), D7906_WORDS) }, /* bus address 1 */ + { UDATA (&da_service, UNIT_FLAGS | MODEL_7906 | SET_BUSADR (2), D7906_WORDS) }, /* bus address 2 */ + { UDATA (&da_service, UNIT_FLAGS | MODEL_7906 | SET_BUSADR (3), D7906_WORDS) } /* bus address 3 */ + }; + +REG da_reg [] = { + DI_REGS (da), + + { BRDATA (BUFFER, buffer, 8, 16, DL_BUFSIZE) }, + + { BRDATA (DSJ, if_dsj, 10, 2, DA_UNITS) }, + { BRDATA (IFSTAT, if_state, 10, sizeof (IF_STATE) * CHAR_BIT, DA_UNITS), PV_LEFT }, + { BRDATA (IFCMD, if_command, 10, sizeof (IF_COMMAND) * CHAR_BIT, DA_UNITS), PV_LEFT }, + + { BRDATA (CNVARS, icd_cntlr, 10, CHAR_BIT, sizeof (CNTLR_VARS) * DA_UNITS), REG_HRO }, + + { NULL } + }; + +MTAB da_mod [] = { + DI_MODS (da_dev), + + { UNIT_UNLOAD, UNIT_UNLOAD, "heads unloaded", "UNLOADED", &da_load_unload, NULL, NULL }, + { UNIT_UNLOAD, 0, "heads loaded", "LOADED", &da_load_unload, NULL, NULL }, + + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL, NULL, NULL }, + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL, NULL, NULL }, + + { UNIT_FMT, UNIT_FMT, "format enabled", "FORMAT", NULL, NULL, NULL }, + { UNIT_FMT, 0, "format disabled", "NOFORMAT", NULL, NULL, NULL }, + + { UNIT_MODEL, MODEL_7906, "7906H", "7906H", &dl_set_model, NULL, NULL }, + { UNIT_MODEL, MODEL_7920, "7920H", "7920H", &dl_set_model, NULL, NULL }, + { UNIT_MODEL, MODEL_7925, "7925H", "7925H", &dl_set_model, NULL, NULL }, + + { 0 } + }; + +DEVICE da_dev = { + "DA", /* device name */ + da_unit, /* unit array */ + da_reg, /* register array */ + da_mod, /* modifier array */ + DA_UNITS, /* number of units */ + 10, /* address radix */ + 26, /* address width */ + 1, /* address increment */ + 8, /* data radix */ + 16, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &da_reset, /* reset routine */ + &da_boot, /* boot routine */ + &da_attach, /* attach routine */ + &da_detach, /* detach routine */ + &da_dib, /* device information block */ + DEV_DEBUG | DEV_DISABLE, /* device flags */ + 0, /* debug control flags */ + di_deb, /* debug flag name table */ + NULL, /* memory size change routine */ + NULL /* logical device name */ + }; + + + +/* Amigo disc global VM routines */ + + +/* Service an Amigo disc drive I/O event. + + The service routine is called to start commands and control the transfer of + data to and from the HP-IB card. The actions to be taken depend on the + current state of the ICD interface. The possibilities are: + + 1. A command is pending on the interface. This occurs only when a command + is received while a Seek or Recalibrate command is in progress. + + 2. A command is ready for execution. + + 3. Data is being sent or received over the HP-IB during command execution. + + 4. Dummy bytes are being sent or received over the HP-IB due to a command + error. + + Entry to the the service routine in any other interface state or to process a + command not allowed in a valid state will return an Internal Error to cause a + simulator stop. Exit from the routine will be either in one of the above + states, or in the idle state if the operation is complete. + + The specific actions taken for the various interface states are as follows: + + command_wait + ============ + + We are entered in this state only if a unit that was busy (still seeking) + was addressed to listen or talk. The card has been held off by asserting + NRFD after receiving MLA or MTA. Upon entry, we complete the seek and then + release the interface by denying NRFD to allow the remainder of the command + sequence to be received from the card. + + command_exec + ============ + + We are entered in this state to initiate, continue, or complete a command. + The command may be a disc command, such as Seek or Read, or an interface + command, such as Amigo Identify or Device-Specified Jump. + + Disc commands call the disc library service routine to perform all of the + common controller actions. Any ICD-specific actions needed, such as + setting the DSJ value, are performed after the call. + + Certain disc commands require multiple execution phases. For example, the + Read command has a start phase that reads data from the disc image file + into the sector buffer, a data phase that transfers bytes from the buffer + to the card, and an end phase that schedules the intersector gap time and + resets to the start phase. Data phase transfers are performed in the + read_exec or write_exec interface states. + + The results of the disc library service are inferred by the controller + state. If the controller is busy, then the command continues in a new + phase. Otherwise, the command either has completed normally or has + terminated with an error. If an error has occurred during a disc command + that transfers data, DSJ is set to 1, and the interface state is changed to + source or sink dummy bytes to complete the command sequence. + + Interface commands may either complete immediately (e.g., Amigo Clear) or + transfer data (e.g., DSJ). + + read_xfer + ========= + + Commands that send data to the CPU enter the service routine to source a + byte to the bus. Bytes are transferred only when ATN and NRFD are denied; + if they are not, we simply exit, as we will be rescheduled when the lines + are dropped. Otherwise, we get a byte from the sector buffer and send it + to the card. If the card has stopped listening, or the buffer is now + empty, then we terminate the transfer and move to the end phase of the + command. Otherwise, we reschedule the next data phase byte transfer. + + Disc and interface commands are handled separately, as EOI is always + asserted on the last byte of an interface command transfer and never on a + (good) disc command transfer. + + write_xfer + ========== + + Commands that receive data from the CPU enter the service routine to + determine whether or not to continue the transfer. Our bus accept routine + has already stored the received byte in the sector buffer and has asserted + NRFD to hold off the card. If the buffer is now full, or the byte was + tagged with EOI, then we terminate the transfer and move to the end phase + of the command. Otherwise, we deny NRFD and exit, as we will be + rescheduled when the next byte arrives. + + error_source + ============ + + If an error occurred during the data transfer phase of a read or status + command, a dummy byte tagged with EOI is sourced to the bus. This allows + the OS driver for the card to terminate the command and request the + controller's status. + + error_sink + ========== + + If an error occurred during the data transfer phase of a write command, + dummy bytes are sunk from the bus until EOI is seen or the card is + unaddressed. This allows the OS driver to complete the command as expected + and to determine the failure by requesting the controller's status. + + + Implementation notes: + + 1. The disc library sets the controller state to idle for a normal End, + Seek, or Recalibrate command and to Wait for all other commands that end + normally. So we determine command completion by checking if the + controller is not busy, rather than checking if the controller is idle. + + Drive Attention status is the normal result of the completion of a Seek + or Recalibrate command. Normal Completion status is the normal result of + all other commands. + + 2. The disc library returns the buffer length in words. We double the + return value to count bytes. + + 3. Some commands, such as DSJ, could be completed in the bus accept routine. + They are serviced here instead to avoid presenting a zero execution time + to the CPU. + + 4. The Amigo command set does not provide the disc with the number of bytes + that will be read, and the unit expects to be untalked when the read is + to terminate. The RTE ICD bootstrap extension does not do this. + Instead, it resets the card via CLC 0,C to terminate the Cold Load Read + that was started by the ICD boot loader ROM. + + In hardware, if the LSTN control bit is cleared, e.g., by CRS, + transmission stops because the card denies NDAC and NRFD (the GPIB + handshake requires NDAC and NRFD to be asserted to start the handshake + sequence; TACS * SDYS * ~NDAC * ~NRFD is an error condition). In + simulation, we handle this by terminating a read transfer if the card + stops accepting. If we did not, then the disc would continue to source + bytes to the bus, overflowing the card FIFO (a FIFO full condition cannot + assert NRFD if the LSTN control bit is clear). +*/ + +t_stat da_service (UNIT *uptr) +{ +uint8 data; +CNTLR_CLASS command_class; +const uint32 unit = uptr - da_dev.units; /* disc unit number */ +const CVPTR cvptr = &icd_cntlr [unit]; /* pointer to controller */ +t_stat result = SCPE_OK; +t_bool release_interface = FALSE; + +switch (if_state [unit]) { /* dispatch interface state */ + + case command_wait: /* command is waiting */ + release_interface = TRUE; /* release interface at end if idle */ + + /* fall into the command_exec handler to process the current command */ + + case command_exec: /* command is executing */ + switch (if_command [unit]) { /* dispatch the interface command */ + + case disc_command: /* execute a disc command */ + result = dl_service_drive (cvptr, uptr); /* service the disc unit */ + + if (cvptr->opcode == clear) /* Clear command? */ + if_dsj [unit] = 2; /* indicate self test complete */ + + if (cvptr->state != cntlr_busy) { /* has the controller stopped? */ + if_state [unit] = idle; /* idle the interface */ + + if (cvptr->status == normal_completion || /* normal completion? */ + cvptr->status == drive_attention) /* or drive attention? */ + break; /* we're done */ + + else { /* if status is abnormal */ + if_dsj [unit] = 1; /* an error has occurred */ + + command_class = dl_classify (*cvptr); /* classify command */ + + if (command_class == class_write) { /* write command failed? */ + if_state [unit] = error_sink; /* sink remaining bytes */ + uptr->wait = cvptr->cmd_time; /* activate to complete processing */ + } + + else if (command_class != class_control) { /* read or status command failed? */ + if_state [unit] = error_source; /* source an error byte */ + uptr->wait = cvptr->cmd_time; /* activate to complete processing */ + } + } + } + + else if (uptr->PHASE == data_phase) { /* starting the data phase? */ + cvptr->length = cvptr->length * 2; /* convert buffer length to bytes */ + + if (dl_classify (*cvptr) == class_write) /* write command? */ + if_state [unit] = write_xfer; /* set for write data transfer */ + else /* read or status command */ + if_state [unit] = read_xfer; /* set for read data transfer */ + } + + break; + + + case amigo_identify: /* Amigo Identify */ + buffer [0] = 0x0003; /* store response in buffer */ + cvptr->length = 2; /* return two bytes */ + + if_state [unit] = read_xfer; /* ready to transfer data */ + uptr->wait = cvptr->cmd_time; /* schedule the transfer */ + + if (DEBUG_PRI (da_dev, DEB_RWSC)) + fprintf (sim_deb, ">>DA rwsc: Unit %d Amigo identify response %04XH\n", + unit, buffer [0]); + break; + + + case initiate_self_test: /* Initiate a self test */ + sim_cancel (&da_unit [unit]); /* cancel any operation in progress */ + dl_clear_controller (cvptr, /* hard clear the controller */ + &da_unit [unit], + hard_clear); + if_dsj [unit] = 2; /* set DSJ for self test */ + if_state [unit] = idle; /* command is complete */ + di_poll_response (da, unit, SET); /* with PPR enabled */ + break; + + + case amigo_clear: /* Amigo clear */ + dl_idle_controller (cvptr); /* idle the controller */ + if_dsj [unit] = 0; /* clear the DSJ value */ + if_state [unit] = idle; /* command is complete */ + di_poll_response (da, unit, SET); /* with PPR enabled */ + break; + + + default: /* no other commands are executed */ + result = SCPE_IERR; /* signal an internal error */ + break; + } /* end of command dispatch */ + break; + + + case error_source: /* send data after an error */ + if (! (di [da].bus_cntl & (BUS_ATN | BUS_NRFD))) { /* is card ready for data? */ + di [da].bus_cntl |= BUS_EOI; /* set EOI */ + di_bus_source (da, 0); /* send a dummy byte to the card */ + if_state [unit] = idle; /* command is complete */ + } + break; + + + case read_xfer: /* send read data */ + if (! (di [da].bus_cntl & (BUS_ATN | BUS_NRFD))) /* is card ready for data? */ + switch (if_command [unit]) { /* dispatch the interface command */ + + case disc_command: /* disc read or status commands */ + data = get_buffer_byte (cvptr); /* get the next byte from the buffer */ + + if (di_bus_source (da, data) == FALSE) /* send data to card; is it listening? */ + cvptr->eod = SET; /* no, so terminate read */ + + if (cvptr->length == 0 || cvptr->eod == SET) { /* is the data phase complete? */ + uptr->PHASE = end_phase; /* set the end phase */ + + if (cvptr->opcode == request_status) /* is it a Request Status command? */ + if_dsj [unit] = 0; /* clear the DSJ value */ + + if_state [unit] = command_exec; /* set to execute the command */ + uptr->wait = cvptr->cmd_time; /* and reschedule the service */ + } + + else /* data phase continues */ + uptr->wait = cvptr->data_time; /* reschedule the next transfer */ + + break; + + + case amigo_identify: + case read_loopback: + case return_self_test_result: + data = get_buffer_byte (cvptr); /* get the next byte from the buffer */ + + if (cvptr->length == 0) /* is the transfer complete? */ + di [da].bus_cntl |= BUS_EOI; /* set EOI */ + + if (di_bus_source (da, data) /* send data to card; is it listening? */ + && cvptr->length > 0) /* and more to transfer? */ + uptr->wait = cvptr->data_time; /* reschedule the next transfer */ + + else { /* transfer is complete */ + if_state [unit] = idle; /* command is complete */ + di_poll_response (da, unit, SET); /* enable PPR */ + } + break; + + + case device_specified_jump: + di [da].bus_cntl |= BUS_EOI; /* set EOI */ + di_bus_source (da, if_dsj [unit]); /* send the DSJ value to the card */ + if_state [unit] = idle; /* command is complete */ + break; + + + case crc_talk: + di [da].bus_cntl |= BUS_EOI; /* set EOI */ + di_bus_source (da, 0); /* send dummy bytes */ + break; /* until the card untalks */ + + + default: /* no other commands send data */ + result = SCPE_IERR; /* signal an internal error */ + break; + } /* end of read data transfer dispatch */ + break; + + + case error_sink: /* absorb data after an error */ + cvptr->index = 0; /* absorb data until EOI */ + + if (cvptr->eod == SET) /* is the transfer complete? */ + if_state [unit] = idle; /* command is complete */ + + di_bus_control (da, unit, 0, BUS_NRFD); /* deny NRFD to allow the card to resume */ + break; + + + case write_xfer: /* receive write data */ + switch (if_command [unit]) { /* dispatch the interface command */ + + case disc_command: /* disc write commands */ + if (cvptr->length == 0 || cvptr->eod == SET) { /* is the data phase complete? */ + uptr->PHASE = end_phase; /* set the end phase */ + + if_state [unit] = command_exec; /* set to execute the command */ + uptr->wait = cvptr->cmd_time; /* and schedule the service */ + + if (cvptr->eod == CLEAR) /* is the transfer continuing? */ + break; /* do not deny NRFD until next service! */ + } + + di_bus_control (da, unit, 0, BUS_NRFD); /* deny NRFD to allow the card to resume */ + break; + + + case write_loopback: + if (cvptr->eod == SET) { /* is the transfer complete? */ + cvptr->length = 16 - cvptr->length; /* set the count of bytes transferred */ + if_state [unit] = idle; /* command is complete */ + } + + di_bus_control (da, unit, 0, BUS_NRFD); /* deny NRFD to allow the card to resume */ + break; + + + default: /* no other commands receive data */ + result = SCPE_IERR; /* signal an internal error */ + break; + } /* end of write data transfer dispatch */ + break; + + + default: /* no other states schedule service */ + result = SCPE_IERR; /* signal an internal error */ + break; + } /* end of interface state dispatch */ + + +if (uptr->wait) /* is service requested? */ + activate_unit (uptr); /* schedule the next event */ + +if (result == SCPE_IERR && DEBUG_PRI (da_dev, DEB_RWSC)) { /* internal error? */ + fprintf (sim_deb, ">>DA rwsc: Unit %d ", unit); + + if (if_state [unit] == command_exec + && if_command [unit] == disc_command) + fprintf (sim_deb, "%s command %s phase ", + dl_opcode_name (ICD, (CNTLR_OPCODE) uptr->OP), + dl_phase_name ((CNTLR_PHASE) uptr->PHASE)); + else + fprintf (sim_deb, "%s state %s ", + if_command_name [if_command [unit]], + if_state_name [if_state [unit]]); + + fputs ("service not handled\n", sim_deb); + } + +if (if_state [unit] == idle) { /* is the command now complete? */ + if (if_command [unit] == disc_command) { /* did a disc command complete? */ + if (cvptr->opcode != end) /* yes; if the command was not End, */ + di_poll_response (da, unit, SET); /* then enable PPR */ + + if (DEBUG_PRI (da_dev, DEB_RWSC)) + fprintf (sim_deb, ">>DA rwsc: Unit %d %s disc command completed\n", + unit, dl_opcode_name (ICD, cvptr->opcode)); + } + + else /* an interface command completed */ + if (DEBUG_PRI (da_dev, DEB_RWSC)) + fprintf (sim_deb, ">>DA rwsc: Unit %d %s command completed\n", + unit, if_command_name [if_command [unit]]); + + if (release_interface) /* if next command is already pending */ + di_bus_control (da, unit, 0, BUS_NRFD); /* deny NRFD to allow the card to resume */ + } + +return result; /* return the result of the service */ +} + + +/* Reset or preset the simulator. + + In hardware, a self-test is performed by the controller at power-on. When + the self-test completes, the controller sets DSJ = 2 and enables the parallel + poll response. + + A front panel PRESET or programmed CRS has no direct effect on the controller + or drive. However, the card reacts to CRS by clearing its talker and + listener states, so an in-progress read or status command will abort when the + next byte sourced to the bus finds no acceptors. +*/ + +t_stat da_reset (DEVICE *dptr) +{ +uint32 unit; +t_stat status; + +status = di_reset (dptr); /* reset the card */ + +if (status == SCPE_OK && (sim_switches & SWMASK ('P'))) /* card OK and power-on reset? */ + for (unit = 0; unit < dptr->numunits; unit++) { /* loop through units */ + sim_cancel (dptr->units + unit); /* cancel activation */ + dptr->units [unit].CYL = 0; /* reset the head position */ + dptr->units [unit].pos = 0; /* to cylinder 0 */ + + dl_clear_controller (&icd_cntlr [unit], /* hard clear the controller */ + dptr->units + unit, + hard_clear); + + if_state [unit] = idle; /* reset the interface state */ + if_command [unit] = invalid; /* reset the interface command */ + + if_dsj [unit] = 2; /* set DSJ for power up */ + } + +return status; +} + + +/* Attach a unit to a disc image file. + + The simulator considers an attached unit to be connected to the bus, and an + unattached unit to be disconnected, so we set the card's acceptor bit for the + selected unit if the attach is successful. An attached unit is ready if the + heads are loaded and not ready of not. + + This model is slightly different than the MAC (DS) simulation, where an + unattached unit is considered "connected but not ready" -- the same indication + returned by an attached unit whose heads are unloaded. Therefore, the + situation when the simulator is started is that all DS units are "connected to + the controller but not ready," whereas all DA units are "not connected to the + controller." This eliminates the overhead of sending HP-IB messages to unused + units. + + In tabular form, the simulator responses are: + + Enabled Loaded Attached DS (MAC) DA (ICD) + ------- ------ -------- ------------ ------------ + N N N disconnected disconnected + N N Y -- -- + N Y N -- -- + N Y Y -- -- + Y N N unloaded disconnected + Y N Y unloaded unloaded + Y Y N -- -- + Y Y Y ready ready + + The unspecified responses are illegal conditions; for example, the simulator + does not allow an attached unit to be disabled. + + + Implementation notes: + + 1. To conform exactly to the MAC responses would have required intercepting + the SET DISABLED/ENABLED commands in order to clear or set the unit + accepting bits. However, short of intercepting the all SET commands with + a custom command table, there is no way to ensure that unit enables are + observed. Adding ENABLED and DISABLED to the modifiers table and + specifying a validation routine works for the DISABLED case but not the + ENABLED case -- set_unit_enbdis returns SCPE_UDIS before calling the + validation routine. +*/ + +t_stat da_attach (UNIT *uptr, char *cptr) +{ +t_stat result; +const uint32 unit = uptr - da_unit; /* calculate the unit number */ + +result = dl_attach (&icd_cntlr [unit], uptr, cptr); /* attach the drive */ + +if (result == SCPE_OK) /* attach successful? */ + di [da].acceptors |= (1 << unit); /* set the unit's accepting bit */ + +return result; +} + + +/* Detach a disc image file from a unit. + + As explained above, detaching a unit is the hardware equivalent of + disconnecting the drive from the bus, so we clear the unit's acceptor bit is + the detach is successful. +*/ + +t_stat da_detach (UNIT *uptr) +{ +t_stat result; +const uint32 unit = uptr - da_unit; /* calculate the unit number */ + +result = dl_detach (&icd_cntlr [unit], uptr); /* detach the drive */ + +if (result == SCPE_OK) { /* detach successful? */ + di [da].acceptors &= ~(1 << unit); /* clear the unit's accepting bit */ + di_poll_response (da, unit, CLEAR); /* and its PPR, as it's no longer present */ + } + +return result; +} + + +/* Boot an Amigo disc drive. + + The ICD disc bootstrap prorgam is loaded from the HP 12992H Boot Loader ROM + into memory, the I/O instructions are configured from the interface card + select code, and the program is run to boot from the specified unit. The + loader supports booting the disc at bus address 0 only. Before execution, + the S register is automatically set as follows: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + ------ ------ ---------------------- ------------- ----- + ROM # 0 1 select code reserved head + + The boot routine sets bits 15-6 of the S register to appropriate values. + Bits 5-3 and 1-0 retain their original values, so S should be set before + booting. These bits are typically set to 0, although bit 5 is set for an RTE + reconfiguration boot, and bits 1-0 may be set if booting from a head other + than 0 is desired. +*/ + +static const BOOT_ROM da_rom = { + 0102501, /* START LIA 1 GET SWITCH REGISTER SETTING */ + 0100044, /* LSL 4 SHIFT A LEFT 4 */ + 0006111, /* CLE,SLB,RSS SR BIT 12 SET FOR MANUAL BOOT? */ + 0100041, /* LSL 1 NO. SHIFT HEAD # FOR RPL BOOT */ + 0001424, /* ALR,ALR SHIFT HEAD 2, CLEAR SIGN */ + 0033744, /* IOR HDSEC SET EOI BIT */ + 0073744, /* STA HDSEC PLACE IN COMMAND BUFFER */ + 0017756, /* JSB BTCTL SEND DUMMY,U-CLR,PP */ + 0102510, /* LIA IBI READ INPUT REGISTER */ + 0101027, /* ASR 7 SHIFT DRIVE 0 RESPONSE TO LSB */ + 0002011, /* SLA,RSS DID DRIVE 0 RESPOND? */ + 0027710, /* JMP *-3 NO, GO LOOK AGAIN */ + 0107700, /* CLC 0,C */ + 0017756, /* JSB BTCTL SEND TALK, CL-RD,BUS HOLDER */ + 0002300, /* CCE */ + 0017756, /* JSB BTCTL TELL CARD TO LISTEN */ + 0063776, /* LDA DMACW LOAD DMA CONTROL WORD */ + 0102606, /* OTA 6 OUTPUT TO DCPC */ + 0106702, /* CLC 2 READY DCPC */ + 0063735, /* LDA ADDR1 LOAD DMA BUFFER ADDRESS */ + 0102602, /* OTA 2 OUTPUT TO DCPC */ + 0063740, /* LDA DMAWC LOAD DMA WORD COUNT */ + 0102702, /* STC 2 READY DCPC */ + 0102602, /* OTA 2 OUTPUT TO DCPC */ + 0103706, /* STC 6,C START DCPC */ + 0102206, /* TEST SFC 6 SKIP IF DMA NOT DONE */ + 0117750, /* JSB ADDR2,I SUCCESSFUL END OF TRANSFER */ + 0102310, /* SFS IBI SKIP IF DISC ABORTED TRANSFER */ + 0027731, /* JMP TEST RECHECK FOR TRANSFER END */ + 0102011, /* ADDR1 HLT 11B ERROR HALT */ + 0000677, /* UNCLR OCT 677 UNLISTEN */ + 0000737, /* OCT 737 UNTALK */ + 0176624, /* DMAWC OCT 176624 UNIVERSAL CLEAR,LBO */ + 0000440, /* LIST OCT 440 LISTEN BUS ADDRESS 0 */ + 0000550, /* CMSEC OCT 550 SECONDARY GET COMMAND */ + 0000000, /* BOOT OCT 0 COLD LOAD READ COMMAND */ + 0001000, /* HDSEC OCT 1000 HEAD,SECTOR PLUS EOI */ + 0000677, /* UNLST OCT 677 ATN,PRIMARY UNLISTEN,PARITY */ + 0000500, /* TALK OCT 500 SEND READ DATA */ + 0100740, /* RDSEC OCT 100740 SECONDARY READ DATA */ + 0102055, /* ADDR2 OCT 102055 BOOT EXTENSION STARTING ADDRESS */ + 0004003, /* CTLP OCT 4003 INT=LBO,T,CIC */ + 0000047, /* OCT 47 PPE,L,T,CIC */ + 0004003, /* OCT 4003 INT=LBO,T,CIC */ + 0000413, /* OCT 413 ATN,P,L,CIC */ + 0001015, /* OCT 1015 INT=EOI,P,L,CIC */ + 0000000, /* BTCTL NOP */ + 0107710, /* CLC IBI,C RESET IBI */ + 0063751, /* BM LDA CTLP LOAD CONTROL WORD */ + 0102610, /* OTA IBI OUTPUT TO CONTROL REGISTER */ + 0102710, /* STC IBI RETURN IBI TO DATA MODE */ + 0037760, /* ISZ BM INCREMENT CONTROL WORD POINTER */ + 0002240, /* SEZ,CME */ + 0127756, /* JMP BTCTL,I RETURN */ + 0063736, /* LABL LDA UNCLR LOAD DATA WORD */ + 0037766, /* ISZ LABL INCREMENT WORD POINTER */ + 0102610, /* OTA IBI OUTPUT TO HPIB */ + 0002021, /* SSA,RSS SKIP IF LAST WORD */ + 0027766, /* JMP LABL GO BACK FOR NEXT WORD */ + 0102310, /* SFS IBI SKIP IF LAST WORD SENT TO BUS */ + 0027773, /* JMP *-1 RECHECK ACCEPTANCE */ + 0027757, /* JMP BTCTL+1 */ + 0000010, /* DMACW ABS IBI */ + 0170100 /* ABS -START */ + }; + +t_stat da_boot (int32 unitno, DEVICE *dptr) +{ +if (GET_BUSADR (da_unit [unitno].flags) != 0) /* boot supported on bus address 0 only */ + return SCPE_NOFNC; /* report "Command not allowed" if attempted */ + +if (ibl_copy (da_rom, da_dib.select_code)) /* copy the boot ROM to memory and configure */ + return SCPE_IERR; /* return an internal error if failed */ + +SR = SR & (IBL_OPT | IBL_DS_HEAD) /* set S to a reasonable value */ + | IBL_DS | IBL_MAN | (da_dib.select_code << IBL_V_DEV); /* before boot execution */ + +return SCPE_OK; +} + + + +/* Amigo disc global SCP routines */ + + +/* Load or unload a unit's heads. + + The heads are automatically loaded when a unit is attached and unloaded when + a unit is detached. While a unit is attached, the heads may be manually + unloaded; this yields a "not ready" status if the unit is accessed. An + unloaded drive may be manually loaded, returning the unit to "ready" status. + + The ICD controller sets Drive Attention status when the heads unload and also + asserts a parallel poll response if the heads unload while in idle state 2 + (i.e., after an End command). + + + Implementation notes: + + 1. The 13365 manual says on page 28 that Drive Attention status is + "Generated whenever...the drive unloads and the controller is in Idle + State 2 or 3." However, the ICD diagnostic tests for Drive Attention + status on head unload immediately after the Request Status command that + completes the previous step, which leaves the controller in idle state 1. + + Moreover, the diagnostic does NOT check for Drive Attention status if the + Amigo ID is 2 (MAC controller). But the 12745 manual says on page 3-7 + that the status is returned if "...Drive becomes not ready (heads + unload)" with no mention of controller state. + + It appears as though the diagnostic test is exactly backward. However, + we match the diagnostic expectation below. +*/ + +t_stat da_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +const uint32 unit = uptr - da_unit; /* calculate the unit number */ +const t_bool load = (value != UNIT_UNLOAD); /* true if heads are loading */ +t_stat result; + +result = dl_load_unload (&icd_cntlr [unit], uptr, load); /* load or unload the heads */ + +if (result == SCPE_OK && ! load) { /* unload successful? */ + icd_cntlr [unit].status = drive_attention; /* set Drive Attention status */ + + if (uptr->OP == end) /* is the controller in idle state 2? */ + di_poll_response (da, unit, SET); /* enable PPR */ + } + +return result; +} + + + +/* Amigo disc global bus routines */ + + +/* Accept a data byte from the bus. + + The indicated unit is offered a byte that has been sourced to the bus. The + routine returns TRUE or FALSE to indicate whether or not it accepted the + byte. + + Commands from the bus may be universal (applying to all acceptors) or + addressed (applying only to those acceptors that have been addressed to + listen). Data bytes are accepted only if the unit has been addressed to + listen. As we are called for a data transfer or an addressed command only if + we are currently listening, the only bytes that we do not accept are talk or + listen commands directed to another address, or secondary commands when we + are not addressed to listen. + + This routine handles the HP-IB protocol. The type of byte passed is + determined by the state of the ATN signal and, if asserted, by the high-order + bits of the value. Most of the work involves decoding secondary commands and + their associated data parameters. The interface state is changed as needed + to track the command protocol. The states processed in this routine are: + + opcode_wait + =========== + + A receive disc command secondary has been received, and the interface is + waiting for the opcode that should follow. + + parameter_wait + ============== + + A disc opcode or interface command has been received, and the interface is + waiting for a parameter byte that should follow. + + write_wait + ========== + + A disc write command has been received, and the interface is waiting for + the receive write data secondary that should follow. + + read_wait + ========= + + A disc read command has been received, and the interface is waiting for the + send read data secondary that should follow. + + status_wait + =========== + + A disc status command has been received, and the interface is waiting for + the send disc status secondary that should follow. + + write_xfer + ========== + + A disc write is in progress, and the interface is waiting for a data byte + that should follow. + + error_sink + ========== + + A disc write has terminated with an error, and the interface is waiting to + absorb all of the remaining data bytes of the transfer. + + + Disc commands and parameters are assembled in the sector buffer before being + passed to the disc library to start the command. Once the command is + started, then the interface state is set either to execute the command or to + wait for the receipt of a data transfer secondary before executing, depending + on the command. + + Two disc command protocol errors are detected. First, an Illegal Opcode is + identified during the check for the expected number of disc command opcode + parameters. This allows us to sink an arbitrary number of parameter bytes. + Second, an I/O Program Error occurs if an unsupported secondary is received, + or the HP-IB sequence is incorrect. The latter occurs if a command has the + wrong number of parameters, or a secondary data transfer sequence is invalid. + + Disc commands that require data transfers (e.g., read, write, request status) + involve a pair of secondaries. The first transmits the command, and the + second transmits or receives the data. If one occurs without the other, an + I/O Program Error occurs. + + A secondary or command that generates an I/O Program Error is always ignored. + Error recovery is as follows: + + - An unsupported talk secondary sends a single data byte tagged with EOI. + + - An unsupported listen secondary accepts and discards any accompanying data + bytes until EOI or unlisten. + + - A supported command with too few parameter bytes or for which the last + parameter byte is not tagged with EOI (before unlisten) does nothing. + + - A supported command with too many parameter bytes accepts and discards + excess parameter bytes until EOI or unlisten. + + - A read or status command that is not followed by a send data or send + status secondary does nothing. The unexpected secondary is executed + normally. + + - A write command that is not followed by a receive data secondary does + nothing. The unexpected secondary is executed normally. + + - A send data or send status secondary that is not preceded by a read or + status command sends a single data byte tagged with EOI. + + - A receive data secondary that is not preceded by a write command accepts + and discards data bytes until EOI or unlisten. + + The Amigo command sequence does not provide a byte count for disc read and + write commands, so the controller continues to source or accept data bytes + until the device is unaddressed. Normally, this is done by an Unlisten or + Untalk. However, per IEEE-488, a listening device may be unaddressed by IFC, + by an Unlisten, or by addressing the device to talk, and a talking device may + be unaddressed by IFC, by addressing another device to talk (or no device via + Untalk), or by addressing the device to listen. Therefore, we keep track of + whether the unit stopped talking or listening, and if it has, we check for + command termination. + + If the controller is unaddressed in the middle of a sector transfer, the read + or write must be terminated cleanly to ensure that the disc image is + coherent. It is also permissable to untalk the controller before all of the + requested status bytes are returned. + + In addition, the controller has no way to inform the host that an error has + occurred that prevents the command from continuing. For example, a data + error encountered while reading, or a protected track encountered while + writing must still source or sink data bytes until the command is terminated + by the host. The controller handles read errors by sourcing a single data + byte tagged with EOI, and write errors by sinking data bytes until EOI is + seen or the unit is unaddressed. + + Therefore, if the unit is unaddressed while a read, write, or status command + is transferring data, the unit service must be scheduled to end the current + command. Unaddressing while an error condition is present merely terminates + the source or sink operation. + + + Implementation notes: + + 1. The 13365 manual does not indicate that the controller responds to + Universal Clear, but the 12992H loader ROM issues this primary and + expects the controller to initialize. + + 2. It is not necessary to check for listening when processing addressed + commands, as only listeners are called by the bus source. + + 3. The dl_prepare_command routine returns FALSE if the command accesses a + busy unit. However, we check for a busy unit during addressing and hold + off the CPU if it is. Therefore, the unit will never be busy when the + routine is called, so a FALSE return will indicate a rejected command. +*/ + +t_bool da_bus_accept (uint32 unit, uint8 data) +{ +const uint8 message_address = data & BUS_ADDRESS; +t_bool accepted = TRUE; +t_bool initiated = FALSE; +t_bool addressed = FALSE; +t_bool stopped_listening = FALSE; +t_bool stopped_talking = FALSE; +char action [40] = ""; +uint32 my_address; + +if (di [da].bus_cntl & BUS_ATN) { /* is it a bus command (ATN asserted)? */ + switch (data & BUS_GROUP) { /* dispatch the bus group */ + + case BUS_PCG: /* primary command group */ + switch (message_address) { + + case 0x04: /* selected device clear */ + case 0x05: /* SDC with parity freeze */ + case 0x14: /* universal clear */ + if (DEBUG_PRI (da_dev, DEB_RWSC)) + fprintf (sim_deb, ">>DA rwsc: Unit %d device cleared\n", unit); + + sim_cancel (&da_unit [unit]); /* cancel any in-progress command */ + dl_idle_controller (&icd_cntlr [unit]); /* idle the controller */ + if_dsj [unit] = 0; /* clear DSJ */ + if_state [unit] = idle; /* idle the interface */ + di_poll_response (da, unit, SET); /* enable PPR */ + + if (DEBUG_PRI (da_dev, DEB_XFER)) + strcpy (action, "device clear"); + break; + + + default: /* unsupported universal command */ + break; /* universals are always accepted */ + } + + break; + + + case BUS_LAG: /* listen address group */ + my_address = GET_BUSADR (da_unit [unit].flags); /* get my bus address */ + + if (message_address == my_address) { /* my listen address? */ + di [da].listeners |= (1 << unit); /* set my listener bit */ + di [da].talker &= ~(1 << unit); /* clear my talker bit */ + + addressed = TRUE; /* unit is now addressed */ + stopped_talking = TRUE; /* MLA stops unit from talking */ + + if (DEBUG_PRI (da_dev, DEB_XFER)) + sprintf (action, "listen %d", message_address); + } + + else if (message_address == BUS_UNADDRESS) { /* unlisten? */ + di [da].listeners = 0; /* clear all listeners */ + + stopped_listening = TRUE; /* UNL stops unit from listening */ + + if (DEBUG_PRI (da_dev, DEB_XFER)) + strcpy (action, "unlisten"); + } + + else /* other listen address */ + accepted = FALSE; /* is not accepted */ + + break; + + + case BUS_TAG: /* talk address group */ + my_address = GET_BUSADR (da_unit [unit].flags); /* get my bus address */ + + if (message_address == my_address) { /* my talk address? */ + di [da].talker = (1 << unit); /* set my talker bit and clear others */ + di [da].listeners &= ~(1 << unit); /* clear my listener bit */ + + addressed = TRUE; /* unit is now addressed */ + stopped_listening = TRUE; /* MTA stops unit from listening */ + + if (DEBUG_PRI (da_dev, DEB_XFER)) + sprintf (action, "talk %d", message_address); + } + + else { /* some other talker (or untalk) */ + di [da].talker &= ~(1 << unit); /* clear talker */ + + stopped_talking = TRUE; /* UNT or OTA stops unit from talking */ + + if (message_address != BUS_UNADDRESS) /* other talk address? */ + accepted = FALSE; /* is not accepted */ + + else /* untalk */ + if (DEBUG_PRI (da_dev, DEB_XFER)) + strcpy (action, "untalk"); + } + + break; + + + case BUS_SCG: /* secondary command group */ + icd_cntlr [unit].index = 0; /* reset the buffer index */ + + if (di [da].listeners & (1 << unit)) { /* is it a listen secondary? */ + if (if_state [unit] == write_wait /* if waiting for a write data secondary */ + && message_address != 0x00) /* but it's not there, */ + abort_command (unit, io_program_error, /* then abort the pending command */ + idle); /* and process the new command */ + + switch (message_address) { /* dispatch the listen secondary */ + + case 0x00: /* Receive Write Data */ + if (if_state [unit] != write_wait) /* if we're not expecting it */ + abort_command (unit, io_program_error, /* abort and sink any data */ + error_sink); + else { /* sequence is correct */ + if_state [unit] = command_exec; /* now ready to execute */ + da_unit [unit].wait = icd_cntlr [unit].cmd_time; /* schedule the unit */ + di_bus_control (da, unit, BUS_NRFD, 0); /* assert NRFD to hold off the card */ + } + + initiated = TRUE; /* log the command or abort initiation */ + break; + + case 0x08: /* disc commands */ + if_command [unit] = disc_command; /* set the command */ + if_state [unit] = opcode_wait; /* opcode must follow */ + break; + + case 0x09: /* CRC (Listen) */ + if_command [unit] = crc_listen; /* set the command */ + if_state [unit] = error_sink; /* sink any data that will be coming */ + initiated = TRUE; /* log the command initiation */ + break; + + case 0x10: /* Amigo Clear */ + if_command [unit] = amigo_clear; /* set the command */ + if_state [unit] = parameter_wait; /* a parameter must follow */ + icd_cntlr [unit].length = 1; /* expect one (unused) byte */ + break; + + case 0x1E: /* Write Loopback */ + if_command [unit] = write_loopback; /* set the command */ + if_state [unit] = write_xfer; /* data will be coming */ + icd_cntlr [unit].length = 16; /* accept only the first 16 bytes */ + initiated = TRUE; /* log the command initiation */ + break; + + case 0x1F: /* Initiate Self-Test */ + if_command [unit] = initiate_self_test; /* set the command */ + if_state [unit] = parameter_wait; /* a parameter must follow */ + icd_cntlr [unit].length = 1; /* expect the test ID byte */ + break; + + default: /* an unsupported listen secondary */ + abort_command (unit, io_program_error, /* abort and sink any data */ + error_sink); /* that might accompany the command */ + initiated = TRUE; /* log the abort initiation */ + break; + } + } + + + else if (di [da].talker & (1 << unit)) { /* is it a talk secondary? */ + da_unit [unit].wait = icd_cntlr [unit].cmd_time; /* these are always scheduled and */ + initiated = TRUE; /* logged as initiated */ + + if (if_state [unit] == read_wait /* if waiting for a send data secondary */ + && message_address != 0x00 /* but it's not there */ + || if_state [unit] == status_wait /* or a send status secondary, */ + && message_address != 0x08) /* but it's not there */ + abort_command (unit, io_program_error, /* then abort the pending command */ + idle); /* and process the new command */ + + switch (message_address) { /* dispatch the talk secondary */ + + case 0x00: /* Send Read Data */ + if (if_state [unit] != read_wait) /* if we're not expecting it */ + abort_command (unit, io_program_error, /* abort and source a data byte */ + error_source); /* tagged with EOI */ + else + if_state [unit] = command_exec; /* now ready to execute */ + break; + + case 0x08: /* Read Status */ + if (if_state [unit] != status_wait) /* if we're not expecting it */ + abort_command (unit, io_program_error, /* abort and source a data byte */ + error_source); /* tagged with EOI */ + else /* all status commands */ + if_state [unit] = read_xfer; /* are ready to transfer data */ + break; + + case 0x09: /* CRC (Talk) */ + if_command [unit] = crc_talk; /* set the command */ + if_state [unit] = read_xfer; /* data will be going */ + break; + + case 0x10: /* Device-Specified Jump */ + if_command [unit] = device_specified_jump; /* set the command */ + if_state [unit] = read_xfer; /* data will be going */ + break; + + case 0x1E: /* Read Loopback */ + if_command [unit] = read_loopback; /* set the command */ + if_state [unit] = read_xfer; /* data will be going */ + break; + + case 0x1F: /* Return Self-Test Result */ + if_command [unit] = return_self_test_result; /* set the command */ + if_state [unit] = read_xfer; /* data will be going */ + icd_cntlr [unit].length = 1; /* return one byte that indicates */ + buffer [0] = 0; /* that the self-test passed */ + break; + + default: /* an unsupported talk secondary */ + abort_command (unit, io_program_error, /* abort and source a data byte */ + error_source); /* tagged with EOI */ + break; + } + } + + + else { /* the unit is not addressed */ + my_address = GET_BUSADR (da_unit [unit].flags); /* get my bus address */ + + if (di [da].talker == 0 && di [da].listeners == 0 /* if there are no talkers or listeners */ + && message_address == my_address) { /* and this is my secondary address, */ + if_command [unit] = amigo_identify; /* then this is an Amigo ID sequence */ + if_state [unit] = command_exec; /* set up execution */ + da_unit [unit].wait = icd_cntlr [unit].cmd_time; /* schedule the unit */ + initiated = TRUE; /* log the command initiation */ + } + + else /* an unaddressed secondary */ + accepted = FALSE; /* is not accepted */ + } + + + if (accepted) { /* was the command accepted? */ + if (DEBUG_PRI (da_dev, DEB_XFER)) + sprintf (action, "secondary %02XH", message_address); + + if (if_command [unit] != amigo_identify) /* disable PPR for all commands */ + di_poll_response (da, unit, CLEAR); /* except Amigo ID */ + } + + break; /* end of secondary processing */ + } + + + if (addressed && sim_is_active (&da_unit [unit])) { /* is the unit being addressed while busy? */ + if_state [unit] = command_wait; /* change the interface state to wait */ + di_bus_control (da, unit, BUS_NRFD, 0); /* and assert NRFD to hold off the card */ + + if (DEBUG_PRI (da_dev, DEB_RWSC)) + fprintf (sim_deb, ">>DA rwsc: Unit %d addressed while controller is busy\n", unit); + } + + if (stopped_listening) { /* was the unit unlistened? */ + if (icd_cntlr [unit].state == cntlr_busy) /* if the controller is busy, */ + complete_write (unit); /* then check for write completion */ + + else if (if_command [unit] == invalid) /* if a command was aborting, */ + complete_abort (unit); /* then complete it */ + + else if (if_state [unit] == opcode_wait /* if waiting for an opcode */ + || if_state [unit] == parameter_wait) /* or a parameter, */ + abort_command (unit, io_program_error, idle); /* then abort the pending command */ + } + + else if (stopped_talking) { /* was the unit untalked? */ + if (icd_cntlr [unit].state == cntlr_busy) /* if the controller is busy */ + complete_read (unit); /* then check for read completion */ + + else if (if_command [unit] == invalid) /* if a command was aborting, */ + complete_abort (unit); /* then complete it */ + } + } /* end of bus command processing */ + + +else { /* it is bus data (ATN denied) */ + switch (if_state [unit]) { /* dispatch the interface state */ + + case opcode_wait: /* wait for an opcode */ + if (DEBUG_PRI (da_dev, DEB_XFER)) + sprintf (action, "opcode %02XH", data & DL_OPCODE_MASK); + + buffer [0] = SET_UPPER (data); /* set the opcode into the buffer */ + + if (dl_prepare_command (&icd_cntlr [unit], /* is the command valid? */ + da_unit, unit)) { + if_state [unit] = parameter_wait; /* set up to get pad byte */ + icd_cntlr [unit].index = 0; /* reset the word index for byte 1 */ + icd_cntlr [unit].length = /* convert the parameter count to bytes */ + icd_cntlr [unit].length * 2 + 1; /* and include the pad byte */ + } + + else { /* the disc command is invalid */ + abort_command (unit, illegal_opcode, /* abort the command */ + error_sink); /* and sink any parameter bytes */ + initiated = TRUE; /* log the abort initiation */ + } /* (the unit cannot be busy) */ + break; + + + case parameter_wait: /* wait for a parameter */ + if (DEBUG_PRI (da_dev, DEB_XFER)) + sprintf (action, "parameter %02XH", data); + + put_buffer_byte (&icd_cntlr [unit], data); /* add the byte to the buffer */ + + if (icd_cntlr [unit].length == 0) /* is this the last parameter? */ + if (di [da].bus_cntl & BUS_EOI) /* does the host agree? */ + initiated = start_command (unit); /* start the command and log the initiation */ + + else { /* the parameter count is wrong */ + abort_command (unit, io_program_error, /* abort the command and sink */ + error_sink); /* any additional parameter bytes */ + initiated = TRUE; /* log the abort initiation */ + } + break; + + + case write_xfer: /* transfer write data */ + if (icd_cntlr [unit].length > 0) /* if there is more to transfer */ + put_buffer_byte (&icd_cntlr [unit], data); /* then add the byte to the buffer */ + + /* fall into error_sink handler */ + + case error_sink: /* sink data after an error */ + if (DEBUG_PRI (da_dev, DEB_XFER)) + sprintf (action, "data %03o", data); + + if (di [da].bus_cntl & BUS_EOI) /* is this the last byte from the bus? */ + icd_cntlr [unit].eod = SET; /* indicate EOD to the controller */ + + di_bus_control (da, unit, BUS_NRFD, 0); /* assert NRFD to hold off the card */ + + da_unit [unit].wait = icd_cntlr [unit].data_time; /* schedule the unit */ + break; + + + default: /* data was received in wrong state */ + abort_command (unit, io_program_error, /* report the error */ + error_sink); /* and sink any data that follows */ + + if (DEBUG_PRI (da_dev, DEB_XFER)) + sprintf (action, "unhandled data %03o", data); + break; + } + } + + +if (accepted && DEBUG_PRI (da_dev, DEB_XFER)) + fprintf (sim_deb, ">>DA xfer: HP-IB address %d accepted %s\n", + GET_BUSADR (da_unit [unit].flags), action); + +if (da_unit [unit].wait > 0) /* is service requested? */ + activate_unit (&da_unit [unit]); /* schedule the unit */ + +if (initiated && DEBUG_PRI (da_dev, DEB_RWSC)) + if (if_command [unit] == disc_command) + fprintf (sim_deb, ">>DA rwsc: Unit %d position %d %s disc command initiated\n", + unit, da_unit [unit].pos, dl_opcode_name (ICD, icd_cntlr [unit].opcode)); + else + fprintf (sim_deb, ">>DA rwsc: Unit %d %s command initiated\n", + unit, if_command_name [if_command [unit]]); + +return accepted; /* indicate the acceptance condition */ +} + + +/* Respond to the bus control lines. + + The indicated unit is notified of the new control state on the bus. There + are two conditions to which we must respond: + + 1. An Interface Clear is initiated. IFC unaddresses all units, so any + in-progress disc command must be terminated as if an Untalk and Unlisten + were accepted from the data bus. + + 2. Attention and Not Ready for Data are denied. A device addressed to talk + must wait for ATN to deny before data may be sent. Also, a listener that + has asserted NRFD must deny it before a talker may send data. If the + interface is sending data and both ATN and NRFD are denied, then we + reschedule the service routine to send the next byte. + +*/ + +void da_bus_respond (CARD_ID card, uint32 unit, uint8 new_cntl) +{ +if (new_cntl & BUS_IFC) { /* is interface clear asserted? */ + di [da].listeners = 0; /* unlisten */ + di [da].talker = 0; /* untalk */ + + if (icd_cntlr [unit].state == cntlr_busy) { /* is the controller busy? */ + complete_write (unit); /* check for write completion */ + complete_read (unit); /* or read completion */ + + if (da_unit [unit].wait > 0) /* is service needed? */ + activate_unit (&da_unit [unit]); /* activate the unit */ + } + + else if (if_command [unit] == invalid) /* if a command was aborting, */ + complete_abort (unit); /* then complete it */ + + else if (if_state [unit] == opcode_wait /* if waiting for an opcode */ + || if_state [unit] == parameter_wait) /* or a parameter, */ + abort_command (unit, io_program_error, idle); /* then abort the pending command */ + } + +if (! (new_cntl & (BUS_ATN | BUS_NRFD)) /* in data mode and card is ready for data? */ + && (if_state [unit] == read_xfer /* is interface waiting to send data */ + || if_state [unit] == error_source)) /* or source error bytes? */ + da_service (&da_unit [unit]); /* start or resume the transfer */ +} + + + +/* Amigo disc local utility routines */ + + +/* Start a command with parameters. + + A command that has been pending until all of its parameters were received is + now ready to start. If this is a disc command, call the disc library to + validate the parameters and, if they are OK, to start the command. Status + commands return the status values in the sector buffer and the number of + words that were returned in the buffer length, which we convert to a byte + count. + + If the disc command was accepted, the controller will be busy. In this case, + set the next interface state as determined by the command that is executing. + For example, a Read command sets the interface to read_wait status in order + to wait until the accompanying Send Read Data secondary is received. If the + controller is not busy, then the command was rejected, so we set DSJ = 1 and + leave the interface state in parameter_wait; the controller status will have + been set to the reason for rejection. + + If the interface state is command_exec, then the disc command is ready for + execution, and we return TRUE to schedule the unit service. Otherwise, we + return FALSE, and the appropriate action will be taken by the caller. + + For all other commands, execution begins as soon as the correct parameters + are received, so we set command_exec state and return TRUE. (Only Amigo + Clear and Inititate Self Test require parameters, so they will be the only + other commands that must be started here.) + +set wait = 0 if not executing (e.g., for read wait) + + Implementation notes: + + 1. The ICD implemenation does not need to differentiate between unit and + controller commands, the return value from the dl_start_command routine + is not used. +*/ + +static t_bool start_command (uint32 unit) +{ +if (if_command [unit] == disc_command) { /* starting a disc command? */ + dl_start_command (&icd_cntlr [unit], da_unit, unit); /* ask the controller to start the command */ + + icd_cntlr [unit].length = icd_cntlr [unit].length * 2; /* convert return length from words to bytes */ + + if (icd_cntlr [unit].state == cntlr_busy) /* was the command accepted? */ + if_state [unit] = next_state [icd_cntlr [unit].opcode]; /* set the next interface state */ + else /* the command was rejected */ + if_dsj [unit] = 1; /* so indicate an error */ + + if (if_state [unit] == command_exec) /* if command is executing */ + return TRUE; /* activate the unit */ + + else { /* if we must wait */ + da_unit [unit].wait = 0; /* for another secondary */ + return FALSE; /* skip the activation */ + } + } + +else { /* all other commands */ + if_state [unit] = command_exec; /* execute as soon */ + da_unit [unit].wait = icd_cntlr [unit].cmd_time; /* as they */ + return TRUE; /* are received */ + } +} + + +/* Abort an in-process command. + + A command sequence partially received via the bus must be aborted. The cause + might be an unknown secondary, an illegal disc command opcode, an improper + secondary sequence (e.g., a Read not followed by Send Read Data), an + incorrect number of parameters, or unaddressing before the sequence was + complete. In any event, the controller and interface are set to an abort + state, and the DSJ value is set to 1 to indicate an error. +*/ + +static void abort_command (uint32 unit, CNTLR_STATUS status, IF_STATE state) +{ +if_command [unit] = invalid; /* indicate an invalid command */ +if_state [unit] = state; /* set the interface state as directed */ +if_dsj [unit] = 1; /* set DSJ to indicate an error condition */ +dl_end_command (&icd_cntlr [unit], status); /* place the disc controller into a wait state */ +return; +} + + +/* Complete an in-process read command. + + An Untalk terminates a Read, Read Full Sector, Read Without Verify, Read With + Offset, or Cold Load Read command, which must be tied off cleanly by setting + the end-of-data condition and calling the service routine. This is required + only if the read has not already aborted (e.g., for an auto-seek error). + + If a read is in progress, the controller will be busy, and the interface + state will be either command_exec (if between sectors) or read_xfer (if + within a sector). We set up the end phase for the command and schedule the + disc service to tidy up. + + If a read has aborted, the controller will be waiting, and the interface + state will be error_source. In this latter case, we no nothing, as the + controller has already set the required error status. + + We must be careful NOT to trigger on an Untalk that may follow the opcode and + precede the Send Read Data sequence. In this case, the controller will be + busy, but the interface state will be either read_wait or status_wait. + + + Implementation notes: + + 1. The test for controller busy is made before calling this routine. This + saves the call overhead for the most common case, which is the card is + being unaddressed after command completion. + + 2. There is no need to test if we are processing a disc command, as the + controller would not be busy otherwise. +*/ + +static void complete_read (uint32 unit) +{ +if ((if_state [unit] == command_exec /* is a command executing */ + || if_state [unit] == read_xfer) /* or is data transferring */ + && (dl_classify (icd_cntlr [unit]) == class_read /* and the controller is executing */ + || dl_classify (icd_cntlr [unit]) == class_status)) { /* a read or status? */ + icd_cntlr [unit].eod = SET; /* set the end of data */ + + if_state [unit] = command_exec; /* set to execute */ + da_unit [unit].PHASE = end_phase; /* the completion phase */ + da_unit [unit].wait = icd_cntlr [unit].data_time; /* ensure that the controller will finish */ + } + +return; +} + + +/* Complete an in-process write command. + + Normally, the host sends a byte tagged with EOI to end a Write, Write Full + Sector, or Initialize command. However, an Unlisten may terminate a write, + which must be tied off cleanly by setting the end-of-data condition and + calling the service routine. This is required only if the write has not + already aborted (e.g., for a write-protected disc). + + If a write is in progress, the controller will be busy, and the interface + state will be either command_exec (if between sectors) or write_xfer (if + within a sector). We set up the end phase for the command and schedule the + disc service to tidy up. + + If a write has aborted, the controller will be waiting, and the interface + state will be error_sink. In this latter case, we do nothing, as the + controller has already set the required error status. + + We must be careful NOT to trigger on the Unlisten that may follow the opcode + and precede the Receive Write Data sequence. In this case, the controller + will be busy, but the interface state will be write_wait. + + + Implementation notes: + + 1. The test for controller busy is made before calling this routine. This + saves the call overhead for the most common case, which is the card is + being unaddressed after command completion. + + 2. There is no need to test if we are processing a disc command, as the + controller would not be busy otherwise. +*/ + +static void complete_write (uint32 unit) +{ +if ((if_state [unit] == command_exec /* is a command executing */ + || if_state [unit] == write_xfer) /* or is data transferring */ + && dl_classify (icd_cntlr [unit]) == class_write) { /* and the controller is executing a write? */ + icd_cntlr [unit].eod = SET; /* set the end of data */ + + if_state [unit] = command_exec; /* set to execute */ + da_unit [unit].PHASE = end_phase; /* the completion phase */ + da_unit [unit].wait = icd_cntlr [unit].data_time; /* ensure that the controller will finish */ + } + +return; +} + + +/* Complete an in-process command abort. + + Errors in the command protocol begin an abort sequence that may involve + sourcing or sinking bytes to allow the sequence to complete as expected by + the CPU. Unaddressing the unit terminates the aborted command. + + If an abort is in progress, and the interface is not idle, the end-of-data + indication is set, and the disc service routine is called directly to process + the completion of the abort. The service routine will terminate the + error_source or error_sink state cleanly and then idle the interface. + + + Implementation notes: + + 1. The test for an abort-in-progress is made before calling this routine. + This saves the call overhead for the most common case, which is the card + is being unaddressed after normal command completion. +*/ + +static void complete_abort (uint32 unit) +{ +if (if_state [unit] != idle) { /* is the interface busy? */ + icd_cntlr [unit].eod = SET; /* set the end of data */ + da_service (&da_unit [unit]); /* and process the abort completion */ + } + +return; +} + + +/* Get a byte from the sector buffer. + + The next available byte in the sector buffer is returned to the caller. The + determination of which byte of the 16-bit buffer word to return is made by + the polarity of the buffer byte count. The count always begins with an even + number, as it is set by doubling the word count returned from the disc + library. Therefore, because we decrement the count first, the upper byte is + indicated by an odd count, and the lower byte is indicated by an even count. + The buffer index is incremented only after the lower byte is returned. +*/ + +static uint8 get_buffer_byte (CVPTR cvptr) +{ +cvptr->length = cvptr->length - 1; /* count the byte */ + +if (cvptr->length & 1) /* is the upper byte next? */ + return GET_UPPER (buffer [cvptr->index]); /* return the byte */ +else /* the lower byte is next */ + return GET_LOWER (buffer [cvptr->index++]); /* return the byte and bump the word index */ +} + + +/* Put a byte into the sector buffer. + + The supplied byte is stored in the sector buffer. The determination of which + byte of the 16-bit buffer word to store is made by the polarity of the buffer + byte count. The count always begins with an even number, as it is set by + doubling the word count returned from the disc library. Therefore, because + we decrement the count first, the upper byte is indicated by an odd count, + and the lower byte is indicated by an even count. The buffer index is + incremented only after the lower byte is stored. +*/ + +static void put_buffer_byte (CVPTR cvptr, uint8 data) +{ +cvptr->length = cvptr->length - 1; /* count the byte */ + +if (cvptr->length & 1) /* is the upper byte next? */ + buffer [cvptr->index] = SET_UPPER (data); /* save the byte */ +else /* the lower byte is next */ + buffer [cvptr->index++] |= SET_LOWER (data); /* merge the byte and bump the word index */ +return; +} + + +/* Activate the unit. + + The specified unit is activated using the unit's "wait" time. If debugging + is enabled, the activation is logged to the debug file. +*/ + +static t_stat activate_unit (UNIT *uptr) +{ +uint32 unit; +t_stat result; + +if (DEBUG_PRI (da_dev, DEB_SERV)) { + unit = uptr - da_unit; + + fprintf (sim_deb, ">>DA serv: Unit %d state %s delay %d service scheduled\n", + unit, if_state_name [if_state [unit]], uptr->wait); + } + +result = sim_activate (uptr, uptr->wait); /* activate the unit */ +uptr->wait = 0; /* reset the activation time */ + +return result; /* return the activation status */ +} diff --git a/HP2100/hp2100_diag.txt b/HP2100/hp2100_diag.txt index c605c425..67cc0fc3 100644 --- a/HP2100/hp2100_diag.txt +++ b/HP2100/hp2100_diag.txt @@ -1,6 +1,6 @@ SIMH/HP 21XX DIAGNOSTICS PERFORMANCE ==================================== - Last update: 2008-08-07 + Last update: 2012-02-13 The HP 24396 diagnostic suite has been run against the SIMH HP 21xx simulation. @@ -70,7 +70,7 @@ The results of the diagnostic runs are summarized below: 103116 12967 Synchronous Interface 1438 - No simulation 103017 12966 Asynchronous Data Set 1519 3.8-0 Passed 103121 12968 Asynchronous Comm. Interface 1602 - No simulation -103024 12821 ICD Disc Interface 1928 - No simulation +103024 12821 ICD Disc Interface 1928 3.8-2 Passed 104000 2600 Keyboard Display Terminal 1615 - No simulation 104003 Teleprinter 1509 3.2-3 Partial @@ -132,6 +132,7 @@ Part Number Diagnostic Name Code Op. Sys. Code Vers. Resul 12824-16002 Vector Instruction Set Firmware 2026 RTE-IVB 5010 3.8-0 Passed 12829-16006 Vector Instruction Set Firmware 2226 RTE-6/VM 6200 3.8-0 Passed 92835-16006 SIGNAL/1000 Firmware Diagnostic 2040 RTE-6/VM 6200 3.8-0 Passed +91711-12032 ICD/MAC Disc Diagnostic 2201 RTE-IVB 5010 3.8-2 Partial The "SIMH Version" is the version number of the earliest SIMH system that was @@ -1237,6 +1238,105 @@ TEST RESULT: Passed. +------------------------------------- +DSN 103024 - 12821 ICD Disc Interface +------------------------------------- + +TESTED DEVICE: DA, DC (hp2100_di.c, hp2100_di_da.c) + +CONFIGURATION: sim> set DA DIAG + sim> set DA ADDRESS=5 + sim> deposit S 000000 + sim> reset + sim> go 100 + +TEST REPORT: *** HPIB 12821A DIAGNOSTIC *** + + INSTRUCTIONS: + + 1. ENTER ? WHEN YOU NEED MORE INFORMATION. + + 2. INDICATE OCTAL VALUES BY A FINAL B (E.G. 13B). + + 3. SET SR BIT 15 ON TO LOOP ON DIAGNOSTIC, + TOGGLE ON,OFF TO BREAK OUT OF A SINGLE TEST + LOOP (CONVERSATIONAL-MODE). + + 4. SET SR BIT 14 TO SUPPRESS PRESET TEST + + 5. SET SR BIT 13 T0 PRINT ONLY ERROR MESSAGES + (EXCEPT CONFIGURATION MESSAGES). + + 6. SET SR BIT 12 TO DELAY 1 SECOND BETWEEN TESTS + + IS THE FIRST CARD IN SELECT CODE 43B? + IF YES, INPUT CARRIAGE RETURN + OTHERWISE, INPUT THE CORRECT SELECT CODE.. [CR entered] + + INPUT THE LAST OCTAL DIGIT OF THE BUS + ADDRESS (0-7 DERIVED FROM S1-S3) ... 5 + + IS THE SECOND CARD IN SELECT CODE 44B? + IF YES, INPUT CARRIAGE RETURN + IF NO SECOND CARD, INPUT 77B + IF DIAGNOSTIC GUESSED INCORRECTLY AND THERE IS + A SECOND CARD, ENTER CORRECT SELECT CODE... [CR entered] + + INPUT THE LAST OCTAL DIGIT OF THE BUS + ADDRESS (0-7 DERIVED FROM S1-S3) ... 0 + + + PROGRAM OPTIONS: + + CR - CARRIAGE RETURN- (DEFAULT) EXECUTE DIAG. + FROM TEST 0. + + N - BEGIN EXECUTION FROM TEST N + + LN - LOOP ON TEST N + (TOGGLE SW BIT 15 TO REGAIN CONTROL) + + E - EXIT PROGRAM WITH HLT77 + (PRESS RUN TO RE-ENTER PROGRAM) + + F - FORCE TESTING TO SECOND BOARD + + G - GO BACK AND RETEST BOARD ONE + + R - GO BACK TO RECONFIGURATION SECTION + + ? [CR entered] + + PRESS HALT,PRESET,RUN WITHIN 10 SECONDS ! + + [CTRL+E] + Simulation stopped + + sim> reset + sim> go + + TESTING COMPLETED ON SELECT CODE 43B + + PRESS HALT,PRESET,RUN WITHIN 10 SECONDS ! + + [CTRL+E] + Simulation stopped + + sim> reset + sim> go + + TESTING COMPLETED ON SELECT CODE 44B + + (T15)..DI TO HP-IB TO DI TEST PASSES ! + + ? E + + HALT instruction 102077 + +TEST RESULT: Passed. + + + ------------------------ DSN 104003 - Teleprinter ------------------------ @@ -3505,3 +3605,520 @@ TEST REPORT: SIGNAL/1000 FIRMWARE DIAGNOSTIC SIGNAL/1000 FIRMWARE DIAGNOSTIC SUCCESSFUL COMPLETION TEST RESULT: Passed. + + + +------------------------------ +DIAG - ICD/MAC Disc Diagnostic +------------------------------ + +TESTED DEVICE: DA (hp2100_di.c, hp2100_di_da.c) + +BINARY FILE: 91711-12032 Rev. 2201 + +HOST SYSTEM: RTE-IVB Rev. 5010 + +CONFIGURATION: sim> go + +TEST REPORT: DIAG : HP-IB DISC DIAGNOSTIC + + DIAG : CAUTION: DATA COULD BE DESTROYED ON THE DISC TESTED. + DIAG : REMOVABLE MEDIA SHOULD BE REPLACED. + + DIAG : List LU (0 for none) ? 0 + DIAG : Do you want to trace disc operations ? YES + DIAG : Start trace at what step ? 0 + DIAG : Trace operations which are not part of the test steps ? NO + DIAG : Stop after first failure ? NO + DIAG : Disc LU ? 41 + DIAG : Disc address ? 0 + DIAG : Drive model number ? 06 + DIAG : Do you want to run the interactive part of the test ? YES + + DIAG : LU 41 address 0 select code 25 7906 drive + + DIAG : CHECK THAT ALL SWITCHES ARE SET CORRECTLY. + DIAG : THE RUN/STOP SWITCH SHOULD BE IN THE RUN POSITION. + DIAG : THE FORMAT SWITCH SHOULD BE ON. + DIAG : THE PROTECT/READ ONLY SWITCH SHOULD BE OFF. + DIAG : Type , + + DIAG : Beginning part 1 of diagnostic. + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : WRITE LOOPBACK REC length 10 + DIAG : READ LOOPBACK REC length 8 + DIAG : test data read test passed + DIAG : STEP 0 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : INITIATE SELF TEST + DIAG : RETURN DSJ 2 + DIAG : RTN SELF-TEST RES result 0 + DIAG : RETURN DSJ 2 + DIAG : STEP 1 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : REQUEST STATUS drive type 0 0 0 50 0 + DIAG : IDENTIFY result 3 0 0 40 0 + DIAG : STEP 3 PASSED + + DIAG : Part 1 of diagnostic completed. + + DIAG : Information for test track selection. + DIAG : Drive address limits: + DIAG : cylinders: 0 - 410 heads: 0 - 3 sectors: 0 - 47 + DIAG : First and last tracks on LU: + DIAG : cylinder 0 head 0 (track 0) + DIAG : cylinder 199 head 1 (track 399) + DIAG : First and last spares on LU: + DIAG : cylinder 200 head 0 (track 400) + DIAG : cylinder 202 head 1 (track 405) + DIAG : Heads on LU (first - last): 0 - 1 + DIAG : Searching entire LU for file directory: + DIAG : NO DIRECTORY OR UNABLE TO READ DIRECTORY ON TEST LU + DIAG : First and last tracks available for testing: + DIAG : cylinder 0 head 0 (track 0) + DIAG : cylinder 199 head 1 (track 399) + DIAG : Default test tracks: + DIAG : cylinder 199 head 0 (track 398) + DIAG : cylinder 199 head 1 (track 399) + DIAG : Use default test tracks ? YES + + DIAG : Checking test track preambles. + DIAG : Test track preambles are OK. + + DIAG : Beginning part 2 of diagnostic. + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : STEP 4 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : REQUEST DISC ADDR cy 199 hd 0 sec 0 0 0 40 0 + DIAG : STEP 5 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : RECALIBRATE 0 + DIAG : REQUEST STATUS drive type 0 0 37 40 0 + DIAG : STEP 6 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : REQUEST SECTOR ADDR sec 2 0 0 40 0 + DIAG : STEP 7 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : CLEAR (OPCODE) + DIAG : RETURN DSJ 2 + DIAG : RETURN DSJ 2 + DIAG : REQUEST STATUS drive type 0 0 0 40 0 + DIAG : STEP 8 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : READ FULL SECTOR length 138 0 0 40 0 + DIAG : decode preamble cy 199 hd 0 sec 0 spd 0 + DIAG : STEP 9 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : WRITE FULL SECTOR length 138 0 0 40 0 + DIAG : STEP 10 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : VERIFY sector count 1 0 0 40 0 + DIAG : STEP 11 FAILED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : READ length 128 0 0 40 0 + DIAG : STEP 12 FAILED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : READ WITH OFFSET length 128 offset 55 0 0 40 0 + DIAG : STEP 13 FAILED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : READ WITHOUT VERIFY length 128 0 0 40 0 + DIAG : STEP 14 FAILED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SET FILE MASK mask 0 0 0 40 0 + DIAG : SEEK cy 199 hd 0 sec 47 0 37 40 0 + DIAG : READ FULL SECTOR length 140 0 14 40 1 + DIAG : STEP 16 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SET FILE MASK mask 2 0 0 40 0 + DIAG : SEEK cy 199 hd 0 sec 47 0 37 40 0 + DIAG : READ FULL SECTOR length 140 0 0 40 0 + DIAG : STEP 17 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SET FILE MASK mask 2 0 0 40 0 + DIAG : SEEK cy 199 hd 3 sec 47 0 37 40 0 + DIAG : READ FULL SECTOR length 140 0 14 40 1 + DIAG : STEP 18 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SET FILE MASK mask 3 0 0 40 0 + DIAG : SEEK cy 199 hd 3 sec 47 0 37 40 0 + DIAG : READ FULL SECTOR length 140 0 0 40 0 + DIAG : REQUEST DISC ADDR cy 200 hd 0 sec 1 0 0 40 0 + DIAG : STEP 19 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SET FILE MASK mask 11 0 0 40 0 + DIAG : SEEK cy 199 hd 0 sec 47 0 37 40 0 + DIAG : READ FULL SECTOR length 140 0 0 40 0 + DIAG : REQUEST DISC ADDR cy 198 hd 0 sec 1 0 0 40 0 + DIAG : STEP 20 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : ADDRESS RECORD cy 199 hd 1 sec 0 0 0 40 0 + DIAG : INITIALIZE length 6144 spd 1 1 0 40 0 + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : READ FULL SECTOR length 3 0 0 40 0 + DIAG : decode preamble cy 199 hd 0 sec 0 spd 0 + DIAG : STEP 21 FAILED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : ADDRESS RECORD cy 199 hd 0 sec 0 0 0 40 0 + DIAG : INITIALIZE length 6144 spd 4 4 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : READ FULL SECTOR length 3 0 0 40 0 + DIAG : decode preamble cy 199 hd 1 sec 0 spd 0 + DIAG : STEP 22 FAILED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : ADDRESS RECORD cy 199 hd 1 sec 0 0 0 40 0 + DIAG : INITIALIZE length 6144 spd 2 2 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : READ FULL SECTOR length 3 0 0 40 0 + DIAG : decode preamble cy 199 hd 1 sec 0 spd 0 + DIAG : STEP 25 FAILED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : ADDRESS RECORD cy 199 hd 0 sec 0 0 0 40 0 + DIAG : INITIALIZE length 6144 spd 6 6 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : READ FULL SECTOR length 3 0 0 40 0 + DIAG : decode preamble cy 199 hd 1 sec 0 spd 0 + DIAG : STEP 26 FAILED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : ADDRESS RECORD cy 199 hd 1 sec 0 0 0 40 0 + DIAG : INITIALIZE length 6144 spd 3 3 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : READ FULL SECTOR length 3 0 0 40 0 + DIAG : decode preamble cy 199 hd 1 sec 0 spd 0 + DIAG : STEP 28 FAILED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : ADDRESS RECORD cy 199 hd 0 sec 0 0 0 40 0 + DIAG : INITIALIZE length 6144 spd 4 4 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : READ FULL SECTOR length 3 0 0 40 0 + DIAG : decode preamble cy 199 hd 1 sec 0 spd 0 + DIAG : STEP 30 FAILED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : ADDRESS RECORD cy 199 hd 0 sec 0 0 0 40 0 + DIAG : INITIALIZE length 6144 spd 0 0 0 40 0 + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : READ FULL SECTOR length 3 0 0 40 0 + DIAG : decode preamble cy 199 hd 0 sec 0 spd 0 + DIAG : SET FILE MASK mask 0 0 0 40 0 + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : READ length 128 0 0 40 0 + DIAG : STEP 31 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : ADDRESS RECORD cy 199 hd 1 sec 0 0 0 40 0 + DIAG : INITIALIZE length 6144 spd 0 0 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : READ FULL SECTOR length 3 0 0 40 0 + DIAG : decode preamble cy 199 hd 1 sec 0 spd 0 + DIAG : SET FILE MASK mask 0 0 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : READ length 128 0 0 40 0 + DIAG : STEP 32 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : WRITE length 128 0 0 40 0 + DIAG : STEP 35 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : READ length 128 0 0 40 0 + DIAG : test data read test passed + DIAG : STEP 36 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 1 hd 0 sec 0 0 37 40 0 + DIAG : REQUEST DISC ADDR cy 1 hd 0 sec 0 0 0 40 0 + DIAG : SEEK cy 2 hd 0 sec 0 0 37 40 0 + DIAG : REQUEST DISC ADDR cy 2 hd 0 sec 0 0 0 40 0 + DIAG : SEEK cy 4 hd 0 sec 0 0 37 40 0 + DIAG : REQUEST DISC ADDR cy 4 hd 0 sec 0 0 0 40 0 + DIAG : SEEK cy 8 hd 0 sec 0 0 37 40 0 + DIAG : REQUEST DISC ADDR cy 8 hd 0 sec 0 0 0 40 0 + DIAG : SEEK cy 16 hd 0 sec 0 0 37 40 0 + DIAG : REQUEST DISC ADDR cy 16 hd 0 sec 0 0 0 40 0 + DIAG : SEEK cy 32 hd 0 sec 0 0 37 40 0 + DIAG : REQUEST DISC ADDR cy 32 hd 0 sec 0 0 0 40 0 + DIAG : SEEK cy 64 hd 0 sec 0 0 37 40 0 + DIAG : REQUEST DISC ADDR cy 64 hd 0 sec 0 0 0 40 0 + DIAG : SEEK cy 128 hd 0 sec 0 0 37 40 0 + DIAG : REQUEST DISC ADDR cy 128 hd 0 sec 0 0 0 40 0 + DIAG : SEEK cy 256 hd 0 sec 0 0 37 40 0 + DIAG : REQUEST DISC ADDR cy 256 hd 0 sec 0 0 0 40 0 + DIAG : SEEK cy 410 hd 0 sec 0 0 37 40 0 + DIAG : REQUEST DISC ADDR cy 410 hd 0 sec 0 0 0 40 0 + DIAG : STEP 40 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : VERIFY sector count 1 0 0 40 0 + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : VERIFY sector count 2 0 0 40 0 + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : VERIFY sector count 4 0 0 40 0 + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : VERIFY sector count 8 0 0 40 0 + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : VERIFY sector count 16 0 0 40 0 + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : VERIFY sector count 32 0 0 40 0 + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : VERIFY sector count 48 0 0 40 0 + DIAG : STEP 45 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : secondary HP-IB value 162 + DIAG : REQUEST STATUS drive type 0 0 12 40 0 + DIAG : secondary HP-IB value 163 + DIAG : REQUEST STATUS drive type 0 0 12 40 0 + DIAG : secondary HP-IB value 164 + DIAG : REQUEST STATUS drive type 0 0 12 40 0 + DIAG : secondary HP-IB value 165 + DIAG : REQUEST STATUS drive type 0 0 12 40 0 + DIAG : secondary HP-IB value 166 + DIAG : REQUEST STATUS drive type 0 0 12 40 0 + DIAG : secondary HP-IB value 167 + DIAG : REQUEST STATUS drive type 0 0 12 40 0 + DIAG : secondary HP-IB value 170 + DIAG : REQUEST STATUS drive type 0 0 12 40 0 + DIAG : secondary HP-IB value 171 + DIAG : REQUEST STATUS drive type 0 0 12 40 0 + DIAG : secondary HP-IB value 172 + DIAG : REQUEST STATUS drive type 0 0 12 40 0 + DIAG : secondary HP-IB value 173 + DIAG : REQUEST STATUS drive type 0 0 12 40 0 + DIAG : secondary HP-IB value 174 + DIAG : REQUEST STATUS drive type 0 0 12 40 0 + DIAG : secondary HP-IB value 175 + DIAG : REQUEST STATUS drive type 0 0 12 40 0 + DIAG : STEP 49 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : opcode HP-IB value 1 + DIAG : REQUEST STATUS drive type 0 0 37 40 0 + DIAG : opcode HP-IB value 15 + DIAG : REQUEST STATUS drive type 0 0 1 40 0 + DIAG : opcode HP-IB value 26 + DIAG : REQUEST STATUS drive type 0 0 1 40 0 + DIAG : STEP 50 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : ADDRESS RECORD cy 198 hd 1 sec 0 0 0 40 0 + DIAG : INITIALIZE length 6144 spd 0 0 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : READ FULL SECTOR length 3 0 0 40 0 + DIAG : decode preamble cy 199 hd 1 sec 0 spd 0 + DIAG : STEP 52 FAILED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : ADDRESS RECORD cy 199 hd 0 sec 0 0 0 40 0 + DIAG : INITIALIZE length 6144 spd 0 0 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : READ FULL SECTOR length 3 0 0 40 0 + DIAG : decode preamble cy 199 hd 1 sec 0 spd 0 + DIAG : STEP 54 FAILED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : ADDRESS RECORD cy 199 hd 1 sec 0 0 0 40 0 + DIAG : INITIALIZE length 6144 spd 0 0 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : READ FULL SECTOR length 3 0 0 40 0 + DIAG : decode preamble cy 199 hd 1 sec 0 spd 0 + DIAG : SET FILE MASK mask 0 0 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : READ length 128 0 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 5 0 37 40 0 + DIAG : READ FULL SECTOR length 138 0 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : WRITE FULL SECTOR length 138 0 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 1 0 37 40 0 + DIAG : READ length 128 0 0 40 0 + DIAG : STEP 55 FAILED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : read without SRD + DIAG : REQUEST STATUS drive type 0 0 12 40 0 + DIAG : STEP 56 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SWD without write + DIAG : REQUEST STATUS drive type 0 0 12 40 0 + DIAG : STEP 57 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SET FILE MASK mask 3 0 0 40 0 + DIAG : SEEK cy 410 hd 3 sec 47 0 37 40 0 + DIAG : READ FULL SECTOR length 140 0 14 44 1 + DIAG : STEP 59 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SET FILE MASK mask 11 0 0 44 0 + DIAG : SEEK cy 0 hd 3 sec 47 0 37 40 0 + DIAG : READ FULL SECTOR length 140 0 14 44 1 + DIAG : STEP 60 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 4 sec 0 0 23 44 1 + DIAG : SEEK cy 0 hd 0 sec 0 0 37 40 0 + DIAG : STEP 72 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 411 hd 1 sec 0 0 23 44 1 + DIAG : SEEK cy 0 hd 0 sec 0 0 37 40 0 + DIAG : STEP 73 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 49 0 23 44 1 + DIAG : SEEK cy 0 hd 0 sec 0 0 37 40 0 + DIAG : STEP 74 PASSED + + DIAG : Part 2 of diagnostic completed. + + DIAG : Beginning part 3 of diagnostic (interactive). + + DIAG : PUT RUN/STOP SWITCH IN STOP POSITION + DIAG : Type , + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : REQUEST STATUS drive type 0 0 37 243 0 + DIAG : STEP 80 PASSED + + + DIAG : PUT RUN/STOP SWITCH IN RUN POSITION + DIAG : Type , + DIAG : WAITING FOR THE DRIVE TO BE READY + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : RETURN DSJ 0 + DIAG : REQUEST STATUS drive type 0 0 0 50 0 + DIAG : STEP 81 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : ADDRESS RECORD cy 199 hd 1 sec 0 0 0 40 0 + DIAG : INITIALIZE length 6144 spd 2 2 0 40 0 + DIAG : SEEK cy 199 hd 1 sec 0 0 37 40 0 + DIAG : READ FULL SECTOR length 3 0 0 40 0 + DIAG : decode preamble cy 199 hd 1 sec 0 spd 0 + DIAG : STEP 82 FAILED + + + DIAG : TURN OFF FORMAT SWITCH + DIAG : Type , + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : REQUEST STATUS drive type 0 0 0 0 0 + DIAG : STEP 83 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 0 0 37 0 0 + DIAG : WRITE length 1 0 0 0 0 + DIAG : STEP 84 FAILED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 0 0 37 0 0 + DIAG : WRITE FULL SECTOR length 1 0 23 0 1 + DIAG : STEP 85 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 1 sec 0 0 37 0 0 + DIAG : INITIALIZE length 1 spd 0 0 23 0 1 + DIAG : STEP 86 PASSED + + + DIAG : TURN ON FORMAT SWITCH + DIAG : Type , + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : REQUEST STATUS drive type 0 0 0 40 0 + DIAG : STEP 87 PASSED + + + DIAG : TURN ON UPPER PLATTER PROTECT SWITCH + DIAG : Type , + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 140 0 + DIAG : STEP 89 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 140 0 + DIAG : WRITE length 1 0 23 140 1 + DIAG : STEP 90 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 140 0 + DIAG : WRITE FULL SECTOR length 1 0 23 140 1 + DIAG : STEP 91 PASSED + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 140 0 + DIAG : INITIALIZE length 1 spd 0 0 23 140 1 + DIAG : STEP 92 PASSED + + + DIAG : TURN OFF PROTECT READ ONLY SWITCH + DIAG : Type , + + DIAG : operation parameters/results spd cs ds DSJ + DIAG : SEEK cy 199 hd 0 sec 0 0 37 40 0 + DIAG : STEP 97 PASSED + + DIAG : Part 3 of diagnostic completed. + + DIAG : Cleaning up. + + DIAG : DIAGNOSTIC TERMINATED. 15 FAILURES DETECTED. + +TEST RESULT: Partially passed. + +TEST NOTES: Steps 11-14 test CRC generation and checking. Steps 21 and 28 + test the defective cylinder bit. Steps 22 and 26 test the spare + cylinder bit. Steps 25, 82, and 84 test the protected cylinder + bit. Step 30 tests track sparing. Steps 52, 54, and 55 test + cylinder, head, and sector miscompares by writing incorrect + preambles. These features are not simulated. diff --git a/HP2100/hp2100_dp.c b/HP2100/hp2100_dp.c index f2eddd5c..f244d356 100644 --- a/HP2100/hp2100_dp.c +++ b/HP2100/hp2100_dp.c @@ -1,6 +1,6 @@ /* hp2100_dp.c: HP 2100 12557A/13210A disk simulator - Copyright (c) 1993-2011, Robert M. Supnik + Copyright (c) 1993-2012, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -26,6 +26,8 @@ DP 12557A 2871 disk subsystem 13210A 7900 disk subsystem + 10-Feb-12 JDB Deprecated DEVNO in favor of SC + Added CNTLR_TYPE cast to dp_settype 28-Mar-11 JDB Tidied up signal handling 26-Oct-10 JDB Changed I/O signal handler for revised signal model 10-Aug-08 JDB Added REG_FIT to register variables < 32-bit size @@ -131,7 +133,7 @@ #define DP_NUMWD (1 << DP_N_NUMWD) /* words/sector */ #define DP_NUMSC2 12 /* sectors/srf 12557 */ #define DP_NUMSC3 24 /* sectors/srf 13210 */ -#define DP_NUMSC (dp_ctype? DP_NUMSC3: DP_NUMSC2) +#define DP_NUMSC (dp_ctype ? DP_NUMSC3 : DP_NUMSC2) #define DP_NUMSF 4 /* surfaces/cylinder */ #define DP_NUMCY 203 /* cylinders/disk */ #define DP_SIZE2 (DP_NUMSF * DP_NUMCY * DP_NUMSC2 * DP_NUMWD) @@ -171,11 +173,11 @@ #define DA_V_SC 0 /* sector */ #define DA_M_SC2 017 #define DA_M_SC3 037 -#define DA_M_SC (dp_ctype? DA_M_SC3: DA_M_SC2) +#define DA_M_SC (dp_ctype ? DA_M_SC3 : DA_M_SC2) #define DA_GETSC(x) (((x) >> DA_V_SC) & DA_M_SC) #define DA_CKMASK2 037 /* check mask */ #define DA_CKMASK3 077 -#define DA_CKMASK (dp_ctype? DA_CKMASK3: DA_CKMASK2) +#define DA_CKMASK (dp_ctype ? DA_CKMASK3 : DA_CKMASK2) /* Status in dpc_sta[drv], (u) = unused in 13210, (d) = dynamic */ @@ -215,7 +217,14 @@ struct { FLIP_FLOP flagbuf; /* cch flag buffer flip-flop */ } dpc = { CLEAR, CLEAR, CLEAR, CLEAR }; -enum { A12557, A13210 } dp_ctype = A13210; /* ctrl type */ +/* Controller types */ + +typedef enum { + A12557, + A13210 + } CNTLR_TYPE; + +CNTLR_TYPE dp_ctype = A13210; /* ctrl type */ int32 dpc_busy = 0; /* cch unit */ int32 dpc_poll = 0; /* cch poll enable */ int32 dpc_cnt = 0; /* check count */ @@ -289,13 +298,14 @@ REG dpd_reg[] = { { FLDATA (FBF, dpd.flagbuf, 0) }, { FLDATA (XFER, dpd_xfer, 0) }, { FLDATA (WVAL, dpd_wval, 0) }, + { ORDATA (SC, dpd_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, dpd_dib.select_code, 6), REG_HRO }, { NULL } }; MTAB dpd_mod[] = { - { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &dpd_dev }, + { MTAB_XTD | MTAB_VDV, 1, "SC", "SC", &hp_setsc, &hp_showsc, &dpd_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &dpd_dev }, { 0 } }; @@ -350,6 +360,7 @@ REG dpc_reg[] = { DP_NUMDRV, REG_HRO) }, { URDATA (CAPAC, dpc_unit[0].capac, 10, T_ADDR_W, 0, DP_NUMDRV, PV_LEFT | REG_HRO) }, + { ORDATA (SC, dpc_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, dpc_dib.select_code, 6), REG_HRO }, { NULL } }; @@ -365,8 +376,8 @@ MTAB dpc_mod[] = { &dp_settype, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, NULL, &dp_showtype, NULL }, - { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &dpd_dev }, + { MTAB_XTD | MTAB_VDV, 1, "SC", "SC", &hp_setsc, &hp_showsc, &dpd_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &dpd_dev }, { 0 } }; @@ -1064,13 +1075,18 @@ t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc) { int32 i; -if ((val < 0) || (val > 1) || (cptr != NULL)) return SCPE_ARG; +if ((val < 0) || (val > 1) || (cptr != NULL)) + return SCPE_ARG; + for (i = 0; i < DP_NUMDRV; i++) { - if (dpc_unit[i].flags & UNIT_ATT) return SCPE_ALATT; + if (dpc_unit[i].flags & UNIT_ATT) + return SCPE_ALATT; } + for (i = 0; i < DP_NUMDRV; i++) dpc_unit[i].capac = (val? DP_SIZE3: DP_SIZE2); -dp_ctype = val; + +dp_ctype = (CNTLR_TYPE) val; return SCPE_OK; } @@ -1079,8 +1095,11 @@ return SCPE_OK; t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc) { -if (dp_ctype == A13210) fprintf (st, "13210A"); -else fprintf (st, "12557A"); +if (dp_ctype == A13210) + fprintf (st, "13210A"); +else + fprintf (st, "12557A"); + return SCPE_OK; } diff --git a/HP2100/hp2100_dq.c b/HP2100/hp2100_dq.c index 726aa4ba..3f4062f8 100644 --- a/HP2100/hp2100_dq.c +++ b/HP2100/hp2100_dq.c @@ -1,7 +1,7 @@ /* hp2100_dq.c: HP 2100 12565A disk simulator Copyright (c) 1993-2006, Bill McDermith - Copyright (c) 2004-2011 J. David Bryan + Copyright (c) 2004-2012 J. David Bryan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -26,6 +26,7 @@ DQ 12565A 2883 disk system + 10-Feb-12 JDB Deprecated DEVNO in favor of SC 28-Mar-11 JDB Tidied up signal handling 26-Oct-10 JDB Changed I/O signal handler for revised signal model 10-Aug-08 JDB Added REG_FIT to register variables < 32-bit size @@ -223,13 +224,14 @@ REG dqd_reg[] = { { FLDATA (FBF, dqd.flagbuf, 0) }, { FLDATA (XFER, dqd_xfer, 0) }, { FLDATA (WVAL, dqd_wval, 0) }, + { ORDATA (SC, dqd_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, dqd_dib.select_code, 6), REG_HRO }, { NULL } }; MTAB dqd_mod[] = { - { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &dqd_dev }, + { MTAB_XTD | MTAB_VDV, 1, "SC", "SC", &hp_setsc, &hp_showsc, &dqd_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &dqd_dev }, { 0 } }; @@ -276,6 +278,7 @@ REG dqc_reg[] = { { DRDATA (XTIME, dqc_xtime, 24), REG_NZ + PV_LEFT }, { URDATA (UFNC, dqc_unit[0].FNC, 8, 8, 0, DQ_NUMDRV, REG_HRO) }, + { ORDATA (SC, dqc_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, dqc_dib.select_code, 6), REG_HRO }, { NULL } }; @@ -285,8 +288,8 @@ MTAB dqc_mod[] = { { UNIT_UNLOAD, 0, "heads loaded", "LOADED", dqc_load_unload }, { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, - { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &dqd_dev }, + { MTAB_XTD | MTAB_VDV, 1, "SC", "SC", &hp_setsc, &hp_showsc, &dqd_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &dqd_dev }, { 0 } }; diff --git a/HP2100/hp2100_dr.c b/HP2100/hp2100_dr.c index 1ae61f6a..69c8b7f2 100644 --- a/HP2100/hp2100_dr.c +++ b/HP2100/hp2100_dr.c @@ -1,6 +1,6 @@ /* hp2100_dr.c: HP 2100 12606B/12610B fixed head disk/drum simulator - Copyright (c) 1993-2011, Robert M. Supnik + Copyright (c) 1993-2012, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -26,6 +26,7 @@ DR 12606B 2770/2771 fixed head disk 12610B 2773/2774/2775 drum + 10-Feb-12 JDB Deprecated DEVNO in favor of SC 28-Mar-11 JDB Tidied up signal handling 26-Oct-10 JDB Changed I/O signal handler for revised signal model 09-Jul-08 JDB Revised drc_boot to use ibl_copy @@ -240,13 +241,14 @@ REG drd_reg[] = { { FLDATA (CTL, drd.control, 0) }, { FLDATA (FLG, drd.flag, 0) }, { ORDATA (BPTR, drd_ptr, 6) }, + { ORDATA (SC, drd_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, drd_dib.select_code, 6), REG_HRO }, { NULL } }; MTAB drd_mod[] = { - { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &drd_dev }, + { MTAB_XTD | MTAB_VDV, 1, "SC", "SC", &hp_setsc, &hp_showsc, &drd_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &drd_dev }, { 0 } }; @@ -278,6 +280,7 @@ REG drc_reg[] = { { FLDATA (RUN, drc_run, 0) }, { DRDATA (TIME, dr_time, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, dr_stopioe, 0) }, + { ORDATA (SC, drc_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, drc_dib.select_code, 6), REG_HRO }, { DRDATA (CAPAC, drc_unit.capac, 24), REG_HRO }, { NULL } @@ -300,8 +303,8 @@ MTAB drc_mod[] = { { UNIT_PROT, 0, "unprotected", "UNPROTECTED", NULL }, { MTAB_XTD | MTAB_VDV, 0, "TRACKPROT", "TRACKPROT", &dr_set_prot, &dr_show_prot, NULL }, - { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &drd_dev }, + { MTAB_XTD | MTAB_VDV, 1, "SC", "SC", &hp_setsc, &hp_showsc, &drd_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &drd_dev }, { 0 } }; diff --git a/HP2100/hp2100_ds.c b/HP2100/hp2100_ds.c index ee22bead..79552417 100644 --- a/HP2100/hp2100_ds.c +++ b/HP2100/hp2100_ds.c @@ -1,6 +1,7 @@ -/* hp2100_ds.c: HP 2100 13037 disk controller simulator +/* hp2100_ds.c: HP 13037D/13175D disc controller/interface simulator - Copyright (c) 2004-2011, Robert M. Supnik + Copyright (c) 2004-2012, Robert M. Supnik + Copyright (c) 2012 J. David Bryan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -15,16 +16,24 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - Except as contained in this notice, the name of Robert M Supnik shall not be + Except as contained in this notice, the names of the authors shall not be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. + in this Software without prior written authorization from the authors. - DS 13037 disk controller + DS 13037D/13175D disc controller/interface + 02-Mar-12 JDB Rewritten to use the MAC/ICD disc controller library + ioIOO now notifies controller service of parameter output + 14-Feb-12 JDB Corrected SRQ generation and FIFO under/overrun detection + Corrected Clear command to conform to the hardware + Fixed Request Status to return Unit Unavailable if illegal + Seek and Cold Load Read now Seek Check if seek in progress + Remodeled command wait for seek completion + 10-Feb-12 JDB Deprecated DEVNO in favor of SC 21-Jun-11 JDB Corrected status returns for disabled drive, auto-seek beyond drive limits, Request Sector Address and Wakeup with invalid or offline unit @@ -41,652 +50,542 @@ 18-Mar-05 RMS Added attached test to detach routine 01-Mar-05 JDB Added SET UNLOAD/LOAD - Reference: + References: - 13037 Disc Controller Technical Information Package (13037-90902, Aug-1980) + - 7925D Disc Drive Service Manual (07925-90913, Apr-1984) + - HP 12992 Loader ROMs Installation Manual (12992-90001, Apr-1986) + - DVR32 RTE Moving Head Driver source (92084-18711, Revision 5000) - States of the controller: the controller uP runs all the time, but most of - the time it is waiting for an event. The simulator only 'runs' the controller - when there's an event to process: change in CPU interface state, change in - disk state, or timeout. The controller has three states: + The 13037D multiple-access (MAC) disc controller supports from one to eight + HP 7905 (15 MB), 7906 (20MB), 7920 (50 MB), and 7925 (120 MB) disc drives + accessed by one to eight CPUs. The controller hardware consists of a 16-bit + microprogrammed processor constructed from 74S181 bit slices and operating at + 5 MHz, a device controller providing the interconnections to the drives and + CPU interfaces, and error correction circuitry that enables the controller to + correct up to a 32-bit error burst. 1024 words of 24-bit firmware are stored + in ROM. - - Idle. No operations other than seek or recalibrate are in progress, and - the CPU interface is disconnected. The controller responds both to new - commands and to drive attention interrupts. - - Wait. No operations other than seek or recalibrate are in progress, but - the CPU interface is connected. The controller responds to new commands - but not to drive attention interrupts. - - Busy. The controller is processing a command. The controller does not - respond to new commands or to drive attention interrupts. + The 13175D disc interface is used to connect the CPU to the 13037 device + controller. In a multiple-CPU system, one interface is strapped to reset the + controller when the CPU's front panel PRESET button is pressed. - The controller busy state is loosely related to the testable (visible) busy - flop. If the visible busy flop is set, the controller is in the busy state; - but the controller can also be busy (processing an invalid opcode or invalid - unit) while visible busy is clear. + This module simulates a 13037D connected to a single 13175D interface. From + one to eight drives may be connected, and drive types may be freely + intermixed. A unit that is enabled but not attached appears to be a + connected drive that does not have a disc pack in place. A unit that is + disabled appears to be disconnected. - Omissions: the following features are not implemented: + This simulator is an adaptation of the code originally written by Bob Supnik. + The functions of the controller have been separated from the functions of the + interface, with the former placed into a separate disc controller library. + This allows the library to support other CPU interfaces, such as the 12821A + HP-IB disc interface, that use substantially different communication + protocols. The library functions implement the controller command set for + the drive units. The interface functions handle the transfer of commands and + data to and from the CPU. - - Drive hold. Since this is a single CPU implementation, the drives are - always available to the CPU. - - Spare, defective, protected. The disk files carry only data. - - Formatting. The disk files carry only data. - - ECC. Data errors are always uncorrectable. + In hardware, the controller runs continuously in one of three states: in the + Poll Loop (idle state), in the Command Wait Loop (wait state), or in command + execution (busy state). In simulation, the controller is run only when a + command is executing or when a transition into or out of the two loops might + occur. Internally, the controller handles these transitions: + + - when a command other than End terminates (busy => wait) + - when the End command terminates (busy => idle) + - when a command timeout occurs (wait => idle) + - when a parameter timeout occurs (busy => idle) + - when a seek completes (if idle and interrupts are enabled, idle => wait) + + The interface must call the controller library to handle these transitions: + + - when a command is received from the CPU (idle or wait => busy) + - when interrupts are enabled (if idle and drive Attention, idle => wait) + + In addition, each transition to the wait state must check for a pending + command, and each transition to the idle state must check for both a pending + command and a drive with Attention status asserted. + + + Implementation notes: + + 1. Although the 13175D has a 16-word FIFO, the "full" level is set at 5 + entries in hardware to avoid a long DCPC preemption time at the start of + a disc write as the FIFO fills. */ -#include + + #include "hp2100_defs.h" +#include "hp_disclib.h" -#define DS_NUMDR 8 /* max drives */ -#define DS_DRMASK (DS_NUMDR - 1) -#define DS_NUMWD 128 /* data words/sec */ -#define DS_NUMWDF 138 /* total words/sec */ -#define DS_FSYNC 0 /* sector offsets */ -#define DS_FCYL 1 -#define DS_FHS 2 -#define DS_FDATA 3 -#define DS_FIFO_SIZE 16 /* fifo size */ -#define DS_FIFO_EMPTY (ds_fifo_cnt == 0) -#define ds_ctrl ds_unit[DS_NUMDR] /* ctrl thread */ -#define ds_timer ds_unit[DS_NUMDR + 1] /* timeout thread */ -#define GET_CURSEC(x,d) ((int32) fmod (sim_gtime() / ((double) (x)), \ - ((double) (drv_tab[d].sc)))) -/* Flags in the unit flags word */ -#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ -#define UNIT_V_UNLOAD (UNIT_V_UF + 1) /* heads unloaded */ -#define UNIT_V_DTYPE (UNIT_V_UF + 2) /* disk type */ -#define UNIT_M_DTYPE 3 -#define UNIT_V_AUTO (UNIT_V_UF + 4) /* autosize */ -#define UNIT_V_FMT (UNIT_V_UF + 5) /* format enabled */ -#define UNIT_WLK (1 << UNIT_V_WLK) -#define UNIT_FMT (1 << UNIT_V_FMT) -#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) -#define UNIT_AUTO (1 << UNIT_V_AUTO) -#define UNIT_UNLOAD (1 << UNIT_V_UNLOAD) -#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) -#define UNIT_WPR (UNIT_WLK | UNIT_RO) /* write prot */ +/* Program constants */ -/* Parameters in the unit descriptor */ +#define DS_DRIVES (DL_MAXDRIVE + 1) /* number of disc drive units */ +#define DS_UNITS (DS_DRIVES + DL_AUXUNITS) /* total number of units */ -#define FNC u3 /* function */ -#define CYL u4 /* current cylinder */ -#define STA u5 /* status */ +#define ds_cntlr ds_unit [DL_MAXDRIVE + 1] /* controller unit alias */ -/* Arguments to subroutines */ +#define FIFO_SIZE 16 /* FIFO depth */ -#define CLR_BUSY 0 /* clear visible busy */ -#define SET_BUSY 1 /* set visible busy */ +#define FIFO_EMPTY (ds.fifo_count == 0) /* FIFO empty test */ +#define FIFO_STOP (ds.fifo_count >= 5) /* FIFO stop filling test */ +#define FIFO_FULL (ds.fifo_count == FIFO_SIZE) /* FIFO full test */ -/* Command word - <12:8> are opcode, <7:0> are opcode dependent +#define PRESET_ENABLE TRUE /* Preset Jumper (W4) enabled */ - cold load read <7:6> = head - <5:0> = sector - set file mask <7:4> = retry count - <3:0> = file mask (auto-seek options) - commands with units <7> = hold flag - <4:0> = unit number */ -#define DSC_V_OP 8 /* opcode */ -#define DSC_M_OP 037 -#define DSC_COLD 000 /* cold load read */ -#define DSC_RECAL 001 /* recalibrate */ -#define DSC_SEEK 002 /* seek */ -#define DSC_RSTA 003 /* request status */ -#define DSC_RSA 004 /* request sector addr */ -#define DSC_READ 005 /* read */ -#define DSC_RFULL 006 /* read full */ -#define DSC_VFY 007 /* verify */ -#define DSC_WRITE 010 /* write */ -#define DSC_WFULL 011 /* write full */ -#define DSC_CLEAR 012 /* clear */ -#define DSC_INIT 013 /* initialize */ -#define DSC_AREC 014 /* address record */ -#define DSC_RSYN 015 /* request syndrome */ -#define DSC_ROFF 016 /* read with offset */ -#define DSC_SFM 017 /* set file mask */ -#define DSC_RNOVFY 022 /* read no verify */ -#define DSC_WTIO 023 /* write TIO */ -#define DSC_RDA 024 /* request disk addr */ -#define DSC_END 025 /* end */ -#define DSC_WAKE 026 /* wakeup */ -#define DSC_ATN 035 /* pseudo: ATN */ -#define DSC_BADU 036 /* pseudo: bad unit */ -#define DSC_BADF 037 /* pseudo: bad opcode */ -#define DSC_NEXT 0040 /* state increment */ -#define DSC_2ND 0040 /* subcommand states */ -#define DSC_3RD 0100 -#define DSC_4TH 0140 -#define DSC_V_CHD 6 /* cold load head */ -#define DSC_M_CHD 03 -#define DSC_V_CSC 0 /* cold load sector */ -#define DSC_M_CSC 077 -#define DSC_V_RTY 4 /* retry count */ -#define DSC_M_RTY 017 -#define DSC_V_DECR 3 /* seek decrement */ -#define DSC_V_SPEN 2 /* enable sparing */ -#define DSC_V_CYLM 1 /* cylinder mode */ -#define DSC_V_AUTO 0 /* auto seek */ -#define DSC_V_HOLD 7 /* hold flag */ -#define DSC_V_UNIT 0 /* unit */ -#define DSC_M_UNIT 017 -#define DSC_V_SPAR 15 /* INIT spare */ -#define DSC_V_PROT 14 /* INIT protected */ -#define DSC_V_DFCT 13 /* INIT defective */ +/* Debug flags */ -#define DSC_HOLD (1u << DSC_V_HOLD) -#define DSC_DECR (1u << DSC_V_DECR) -#define DSC_SPEN (1u << DSC_V_SPEN) -#define DSC_CYLM (1u << DSC_V_CYLM) -#define DSC_AUTO (1u << DSC_V_AUTO) -#define DSC_FMASK ((DSC_M_RTY << DSC_V_RTY)|DSC_DECR|\ - DSC_SPEN|DSC_CYLM|DSC_AUTO) -#define DSC_GETOP(x) (((x) >> DSC_V_OP) & DSC_M_OP) -#define DSC_GETUNIT(x) (((x) >> DSC_V_UNIT) & DSC_M_UNIT) -#define DSC_GETCHD(x) (((x) >> DSC_V_CHD) & DSC_M_CHD) -#define DSC_GETCSC(x) (((x) >> DSC_V_CSC) & DSC_M_CSC) -#define DSC_SPAR (1u << DSC_V_SPAR) -#define DSC_PROT (1u << DSC_V_PROT) -#define DSC_DFCT (1u << DSC_V_DFCT) +#define DEB_CPU (1 << 0) /* words received from and sent to the CPU */ +#define DEB_CMDS (1 << 1) /* interface commands received from the CPU */ +#define DEB_BUF (1 << 2) /* data read from and written to the card FIFO */ +#define DEB_RWSC (1 << 3) /* device read/write/status/control commands */ +#define DEB_SERV (1 << 4) /* unit service scheduling calls */ -/* Command flags */ -#define CMF_UNDF 001 /* undefined */ -#define CMF_CLREC 002 /* clear eoc flag */ -#define CMF_CLRS 004 /* clear status */ -#define CMF_UIDLE 010 /* requires unit no */ -/* Cylinder words - 16b */ +/* Per-card state variables */ -/* Head/sector word */ - -#define DSHS_V_HD 8 /* head */ -#define DSHS_M_HD 037 -#define DSHS_V_SC 0 /* sector */ -#define DSHS_M_SC 0377 -#define DSHS_HD (DSHS_M_HD << DSHS_V_HD) -#define DSHS_SC (DSHS_M_SC << DSHS_V_SC) -#define DSHS_GETHD(x) (((x) >> DSHS_V_HD) & DSHS_M_HD) -#define DSHS_GETSC(x) (((x) >> DSHS_V_SC) & DSHS_M_SC) - -/* Status 1 */ - -#define DS1_V_SPAR 15 /* spare - na */ -#define DS1_V_PROT 14 /* protected - na */ -#define DS1_V_DFCT 13 /* defective - na */ -#define DS1_V_STAT 8 /* status */ -#define DS1_OK (000 << DS1_V_STAT) /* normal */ -#define DS1_ILLOP (001 << DS1_V_STAT) /* illegal opcode */ -#define DS1_AVAIL (002 << DS1_V_STAT) /* available */ -#define DS1_CYLCE (007 << DS1_V_STAT) /* cyl compare err */ -#define DS1_UNCOR (010 << DS1_V_STAT) /* uncor data err */ -#define DS1_HSCE (011 << DS1_V_STAT) /* h/s compare err */ -#define DS1_IOPE (012 << DS1_V_STAT) /* IO oper err - na */ -#define DS1_EOCYL (014 << DS1_V_STAT) /* end cylinder */ -#define DS1_OVRUN (016 << DS1_V_STAT) /* overrun */ -#define DS1_CORDE (017 << DS1_V_STAT) /* correctible - na */ -#define DS1_ILLST (020 << DS1_V_STAT) /* illegal spare - na */ -#define DS1_DEFTK (021 << DS1_V_STAT) /* defective trk - na */ -#define DS1_ACCER (022 << DS1_V_STAT) /* access not rdy - na */ -#define DS1_S2ERR (023 << DS1_V_STAT) /* status 2 error */ -#define DS1_TKPER (026 << DS1_V_STAT) /* protected trk - na */ -#define DS1_UNAVL (027 << DS1_V_STAT) /* illegal unit */ -#define DS1_ATN (037 << DS1_V_STAT) /* attention */ -#define DS1_V_UNIT 0 -#define DS1_SPAR (1u << DS1_V_SPAR) -#define DS1_PROT (1u << DS1_V_PROT) -#define DS1_DFCT (1u << DS1_V_DFCT) - -/* Status 2, ^ = kept in unit status, * = dynamic */ - -#define DS2_ERR 0100000 /* *error */ -#define DS2_V_ID 9 /* drive type */ -#define DS2_ATN 0000200 /* ^attention */ -#define DS2_RO 0000100 /* *read only */ -#define DS2_FRM 0000040 /* *format */ -#define DS2_FLT 0000020 /* fault - na */ -#define DS2_FS 0000010 /* ^first status */ -#define DS2_SC 0000004 /* ^seek error */ -#define DS2_NR 0000002 /* *not ready */ -#define DS2_BS 0000001 /* *busy */ -#define DS2_ALLERR (DS2_FLT|DS2_SC|DS2_NR|DS2_BS) - -/* Controller state */ - -#define DS_IDLE 0 /* idle */ -#define DS_WAIT 1 /* command wait */ -#define DS_BUSY 2 /* busy */ - -/* This controller supports four different disk drive types: - - type #sectors/ #surfaces/ #cylinders/ - surface cylinder drive - - 7905 48 3 411 =15MB - 7906 48 4 411 =20MB - 7920 48 5 823 =50MB - 7925 64 9 823 =120MB - - In theory, each drive can be a different type. The size field in - each unit selects the drive capacity for each drive and thus the - drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE. - - The 7905 and 7906 have fixed and removable platters. Consequently, - they are almost always accessed with cylinders limited to each - platter. The 7920 and 7925 have multiple-platter packs, and so are - almost always accessed with cylinders that span all surfaces. - - Disk image files are arranged as a linear set of tracks. To improve - locality, tracks on the 7905 and 7906 images are grouped per-platter, - i.e., all tracks on heads 0 and 1, followed by all tracks on head 2 - (and, for the 7906, head 3), whereas tracks on the 7920 and 7925 are - sequential by cylinder and head number. - - This variable-access geometry is accomplished by defining a "heads - per cylinder" value for the fixed and removable sections of each - drive that indicates the number of heads that should be grouped for - locality. The removable values are set to 2 on the 7905 and 7906, - indicating that those drives typically use cylinders of two surfaces. - They are set to the number of surfaces per drive for the 7920 and - 7925, as those typically use cylinders encompassing the entire - spindle. -*/ - -#define GET_DA(x,y,z,t) \ - (((((y) < drv_tab[t].rh)? \ - (x) * drv_tab[t].rh + (y): \ - drv_tab[t].cyl * drv_tab[t].rh + \ - ((x) * drv_tab[t].fh + (y) - drv_tab[t].rh)) * \ - drv_tab[t].sc + (z)) * DS_NUMWD) - -#define D7905_DTYPE 0 -#define D7905_SECT 48 -#define D7905_SURF 3 -#define D7905_RH 2 -#define D7905_FH (D7905_SURF - D7905_RH) -#define D7905_CYL 411 -#define D7905_ID (2 << DS2_V_ID) -#define D7905_SIZE (D7905_SECT * D7905_SURF * D7905_CYL * DS_NUMWD) - -#define D7906_DTYPE 1 -#define D7906_SECT 48 -#define D7906_SURF 4 -#define D7906_RH 2 -#define D7906_FH (D7906_SURF - D7906_RH) -#define D7906_CYL 411 -#define D7906_ID (0 << DS2_V_ID) -#define D7906_SIZE (D7906_SECT * D7906_SURF * D7906_CYL * DS_NUMWD) - -#define D7920_DTYPE 2 -#define D7920_SECT 48 -#define D7920_SURF 5 -#define D7920_RH D7920_SURF -#define D7920_FH (D7920_SURF - D7920_RH) -#define D7920_CYL 823 -#define D7920_ID (1 << DS2_V_ID) -#define D7920_SIZE (D7920_SECT * D7920_SURF * D7920_CYL * DS_NUMWD) - -#define D7925_DTYPE 3 -#define D7925_SECT 64 -#define D7925_SURF 9 -#define D7925_RH D7925_SURF -#define D7925_FH (D7925_SURF - D7925_RH) -#define D7925_CYL 823 -#define D7925_ID (3 << DS2_V_ID) -#define D7925_SIZE (D7925_SECT * D7925_SURF * D7925_CYL * DS_NUMWD) - -struct drvtyp { - uint32 sc; /* sectors */ - uint32 hd; /* surfaces */ - uint32 cyl; /* cylinders */ - uint32 size; /* #blocks */ - uint32 id; /* device type */ - uint32 rh; /* removable surfaces */ - uint32 fh; /* fixed surfaces */ - }; - -static struct drvtyp drv_tab[] = { - { D7905_SECT, D7905_SURF, D7905_CYL, D7905_SIZE, D7905_ID, D7905_RH, D7905_FH }, - { D7906_SECT, D7906_SURF, D7906_CYL, D7906_SIZE, D7906_ID, D7906_RH, D7906_FH }, - { D7920_SECT, D7920_SURF, D7920_CYL, D7920_SIZE, D7920_ID, D7920_RH, D7920_FH }, - { D7925_SECT, D7925_SURF, D7925_CYL, D7925_SIZE, D7925_ID, D7925_RH, D7925_FH }, - { 0 } - }; - -struct { +typedef struct { FLIP_FLOP control; /* control flip-flop */ FLIP_FLOP flag; /* flag flip-flop */ FLIP_FLOP flagbuf; /* flag buffer flip-flop */ - FLIP_FLOP srq; /* service request flip-flop */ - } ds = { CLEAR, CLEAR, CLEAR, CLEAR }; + FLIP_FLOP srq; /* SRQ flip-flop */ + FLIP_FLOP edt; /* EDT flip-flop */ + FLIP_FLOP cmfol; /* command follows flip-flop */ + FLIP_FLOP cmrdy; /* command ready flip-flop */ + uint16 fifo [FIFO_SIZE]; /* FIFO buffer */ + uint32 fifo_count; /* FIFO occupancy counter */ + REG *fifo_reg; /* FIFO register pointer */ + } CARD_STATE; -uint32 ds_fifo[DS_FIFO_SIZE] = { 0 }; /* fifo */ -uint32 ds_fifo_ip = 0; /* insertion ptr */ -uint32 ds_fifo_rp = 0; /* removal ptr */ -uint32 ds_fifo_cnt = 0; /* count */ -uint32 ds_cmd = 0; /* command word */ -uint32 ds_sr1 = 0; /* status word 1 */ -uint32 ds_busy = 0; /* busy flag */ -uint32 ds_eoc = 0; /* end of cylinder */ -uint32 ds_eod = 0; /* end of data */ -uint32 ds_fmask = 0; /* file mask */ -uint32 ds_cmdf = 0; /* command follows */ -uint32 ds_cmdp = 0; /* command present */ -uint32 ds_cyl = 0; /* disk address: cyl */ -uint32 ds_hs = 0; /* disk address: hs */ -uint32 ds_vctr = 0; /* verify counter */ -uint32 ds_state = 0; /* controller state */ -uint32 ds_lastatn = 0; /* last atn intr */ -int32 ds_stime = 100; /* seek time */ -int32 ds_rtime = 100; /* inter-sector time */ -int32 ds_ctime = 3; /* command time */ -int32 ds_dtime = 1; /* dch time */ -int32 ds_tmo = 2749200; /* timeout = 1.74 sec */ -uint32 ds_ptr = 0; /* buffer ptr */ -uint16 dsxb[DS_NUMWDF]; /* sector buffer */ -static const uint32 ds_opflags[32] = { /* flags for ops */ - CMF_CLREC|CMF_CLRS|CMF_UIDLE, /* cold read */ - CMF_CLREC|CMF_CLRS|CMF_UIDLE, /* recalibrate */ - CMF_CLREC|CMF_CLRS|CMF_UIDLE, /* seek */ - 0, /* read status */ - CMF_CLRS, /* read sector */ - CMF_CLRS|CMF_UIDLE, /* read */ - CMF_CLRS|CMF_UIDLE, /* read full */ - CMF_CLRS|CMF_UIDLE, /* verify */ - CMF_CLRS|CMF_UIDLE, /* write */ - CMF_CLRS|CMF_UIDLE, /* write full */ - CMF_CLRS, /* clear */ - CMF_CLRS|CMF_UIDLE, /* init */ - CMF_CLREC|CMF_CLRS, /* addr record */ - 0, /* read syndrome */ - CMF_CLRS|CMF_UIDLE, /* read offset */ - CMF_CLRS, /* set file mask */ - CMF_UNDF|CMF_CLRS, /* undefined */ - CMF_UNDF|CMF_CLRS, /* undefined */ - CMF_CLRS|CMF_UIDLE, /* read no verify */ - CMF_CLRS, /* write TIO */ - CMF_CLRS, /* read disk addr */ - CMF_CLRS, /* end */ - CMF_CLRS, /* wake */ - CMF_UNDF|CMF_CLRS, /* undefined */ - CMF_UNDF|CMF_CLRS, /* undefined */ - CMF_UNDF|CMF_CLRS, /* undefined */ - CMF_UNDF|CMF_CLRS, /* undefined */ - CMF_UNDF|CMF_CLRS, /* undefined */ - CMF_UNDF|CMF_CLRS, /* undefined */ - CMF_UNDF|CMF_CLRS, /* undefined */ - CMF_UNDF|CMF_CLRS, /* undefined */ - CMF_UNDF|CMF_CLRS /* undefined */ - }; +/* MAC disc state variables */ -DEVICE ds_dev; +static UNIT ds_unit [DS_UNITS]; /* unit array */ -IOHANDLER dsio; +static CARD_STATE ds; /* card state */ + +static uint16 buffer [DL_BUFSIZE]; /* command/status/sector buffer */ + +static CNTLR_VARS mac_cntlr = /* MAC controller */ + { CNTLR_INIT (MAC, buffer, &ds_cntlr) }; + + + +/* MAC disc global VM routines */ + +IOHANDLER ds_io; +t_stat ds_service_drive (UNIT *uptr); +t_stat ds_service_controller (UNIT *uptr); +t_stat ds_service_timer (UNIT *uptr); +t_stat ds_reset (DEVICE *dptr); +t_stat ds_attach (UNIT *uptr, char *cptr); +t_stat ds_detach (UNIT *uptr); +t_stat ds_boot (int32 unitno, DEVICE *dptr); + +/* MAC disc global SCP routines */ -t_stat ds_svc_c (UNIT *uptr); -t_stat ds_svc_u (UNIT *uptr); -t_stat ds_svc_t (UNIT *uptr); -t_stat ds_reset (DEVICE *dptr); -t_stat ds_attach (UNIT *uptr, char *cptr); -t_stat ds_detach (UNIT *uptr); -t_stat ds_boot (int32 unitno, DEVICE *dptr); t_stat ds_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc); -t_stat ds_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); -void ds_poll (void); -void ds_docmd (uint32 cmd); -void ds_doatn (void); -uint32 ds_updds2 (UNIT *uptr); -void ds_cmd_done (t_bool sf, uint32 sr1); -void ds_wait_for_cpu (UNIT *uptr, uint32 newst); -void ds_set_idle (void); -void ds_sched_ctrl_op (uint32 op, uint32 arg, uint32 busy); -void ds_reqad (uint16 *cyl, uint16 *hs); -void ds_start_seek (UNIT *uptr, uint32 cyl, uint32 newst); -t_bool ds_start_rw (UNIT *uptr, int32 tm, t_bool vfy); -void ds_next_sec (UNIT *uptr); -void ds_next_cyl (UNIT *uptr); -t_stat ds_start_rd (UNIT *uptr, uint32 off, t_bool vfy); -void ds_start_wr (UNIT *uptr, t_bool vfy); -void ds_cont_rd (UNIT *uptr, uint32 bsize); -t_stat ds_cont_wr (UNIT *uptr, uint32 off, uint32 bsize); -void ds_end_rw (UNIT *uptr, uint32 newst); -t_stat ds_set_uncorr (UNIT *uptr); -t_stat ds_clear (void); -void ds_sched_atn (UNIT *uptr); -uint32 ds_fifo_read (void); -void ds_fifo_write (uint32 dat); -void ds_fifo_reset (void); -/* DS data structures +/* MAC disc local utility routines */ - ds_dev DS device descriptor +static void start_command (void); +static void poll_interface (void); +static void poll_drives (void); +static void fifo_load (uint16 data); +static uint16 fifo_unload (void); +static void fifo_clear (void); +static t_stat activate_unit (UNIT *uptr); + + + +/* MAC disc VM data structures. + + ds_dib DS device information block ds_unit DS unit list ds_reg DS register list ds_mod DS modifier list + ds_deb DS debug table + ds_dev DS device descriptor + + For the drive models, the modifiers provide this SHOW behavior: + + - when detached and autosized, prints "autosize" + - when detached and not autosized, prints the model number + - when attached, prints the model number (regardless of autosizing) + + + Implementation notes: + + 1. The validation routine does not allow the model number or autosizing + option to be changed when the unit is attached. Therefore, specifying + UNIT_ATT in the mask field has no deleterious effect. + + 2. The modifier DEVNO is deprecated in favor of SC but is retained for + compatibility. */ -DIB ds_dib = { &dsio, DS }; -UNIT ds_unit[] = { - { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | - UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, - { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | - UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, - { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | - UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, - { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | - UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, - { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | - UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, - { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | - UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, - { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | - UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, - { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | - UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, - { UDATA (&ds_svc_c, UNIT_DIS, 0) }, - { UDATA (&ds_svc_t, UNIT_DIS, 0) } +DEVICE ds_dev; + +static DIB ds_dib = { &ds_io, DS }; + +#define UNIT_FLAGS (UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE | UNIT_UNLOAD) + +static UNIT ds_unit [] = { + { UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* unit 0 */ + { UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* unit 1 */ + { UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* unit 2 */ + { UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* unit 3 */ + { UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* unit 4 */ + { UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* unit 5 */ + { UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* unit 6 */ + { UDATA (&ds_service_drive, UNIT_FLAGS | MODEL_7905, D7905_WORDS) }, /* unit 7 */ + { UDATA (&ds_service_controller, UNIT_DIS, 0) }, /* controller unit */ + { UDATA (&ds_service_timer, UNIT_DIS, 0) } /* timer unit */ }; -REG ds_reg[] = { - { ORDATA (CMD, ds_cmd, 16) }, - { BRDATA (FIFO, ds_fifo, 8, 16, DS_FIFO_SIZE) }, - { ORDATA (SR1, ds_sr1, 16) }, - { ORDATA (VCTR, ds_vctr, 16) }, - { ORDATA (FMASK, ds_fmask, 8) }, - { ORDATA (CYL, ds_cyl, 16) }, - { ORDATA (HS, ds_hs, 16) }, - { ORDATA (STATE, ds_state, 2), REG_RO }, - { ORDATA (LASTA, ds_lastatn, 3) }, - { DRDATA (FIP, ds_fifo_ip, 4) }, - { DRDATA (FRP, ds_fifo_rp, 4) }, - { DRDATA (FCNT, ds_fifo_cnt, 5) }, - { FLDATA (CTL, ds.control, 0) }, - { FLDATA (FLG, ds.flag, 0) }, - { FLDATA (FBF, ds.flagbuf, 0) }, - { FLDATA (SRQ, ds.srq, 0) }, - { FLDATA (BUSY, ds_busy, 0) }, - { FLDATA (CMDF, ds_cmdf, 0) }, - { FLDATA (CMDP, ds_cmdp, 0) }, - { FLDATA (EOC, ds_eoc, 0) }, - { FLDATA (EOD, ds_eod, 0) }, - { BRDATA (DBUF, dsxb, 8, 16, DS_NUMWDF) }, - { DRDATA (DPTR, ds_ptr, 8) }, - { DRDATA (CTIME, ds_ctime, 24), PV_LEFT + REG_NZ }, - { DRDATA (DTIME, ds_dtime, 24), PV_LEFT + REG_NZ }, - { DRDATA (STIME, ds_stime, 24), PV_LEFT + REG_NZ }, - { DRDATA (RTIME, ds_rtime, 24), PV_LEFT + REG_NZ }, - { DRDATA (TIMEOUT, ds_tmo, 31), PV_LEFT + REG_NZ }, - { URDATA (UCYL, ds_unit[0].CYL, 10, 10, 0, - DS_NUMDR + 1, PV_LEFT | REG_HRO) }, - { URDATA (UFNC, ds_unit[0].FNC, 8, 8, 0, - DS_NUMDR + 1, REG_HRO) }, - { URDATA (USTA, ds_unit[0].STA, 8, 16, 0, - DS_NUMDR + 1, REG_HRO) }, - { URDATA (CAPAC, ds_unit[0].capac, 10, T_ADDR_W, 0, - DS_NUMDR, PV_LEFT | REG_HRO) }, - { ORDATA (DEVNO, ds_dib.select_code, 6), REG_HRO }, +static REG ds_reg [] = { + { FLDATA (CMFOL, ds.cmfol, 0) }, + { FLDATA (CMRDY, ds.cmrdy, 0) }, + { BRDATA (FIFO, ds.fifo, 8, 16, FIFO_SIZE), REG_CIRC }, + { DRDATA (FCNT, ds.fifo_count, 5) }, + { ORDATA (FREG, ds.fifo_reg, 32), REG_HRO }, + + { ORDATA (CNTYPE, mac_cntlr.type, 2), REG_HRO }, + { ORDATA (STATE, mac_cntlr.state, 2) }, + { ORDATA (OPCODE, mac_cntlr.opcode, 6) }, + { ORDATA (STATUS, mac_cntlr.status, 6) }, + { FLDATA (EOC, mac_cntlr.eoc, 0) }, + { FLDATA (EOD, mac_cntlr.eod, 0) }, + { ORDATA (SPDU, mac_cntlr.spd_unit, 16) }, + { ORDATA (FLMASK, mac_cntlr.file_mask, 4) }, + { ORDATA (RETRY, mac_cntlr.retry, 4), REG_HRO }, + { ORDATA (CYL, mac_cntlr.cylinder, 16) }, + { ORDATA (HEAD, mac_cntlr.head, 6) }, + { ORDATA (SECTOR, mac_cntlr.sector, 8) }, + { ORDATA (VFYCNT, mac_cntlr.verify_count, 16) }, + { ORDATA (LASPOL, mac_cntlr.poll_unit, 3) }, + { HRDATA (BUFPTR, mac_cntlr.buffer, 32), REG_HRO }, + { BRDATA (BUFFER, buffer, 8, 16, DL_BUFSIZE) }, + { DRDATA (INDEX, mac_cntlr.index, 8) }, + { DRDATA (LENGTH, mac_cntlr.length, 8) }, + { HRDATA (AUXPTR, mac_cntlr.aux, 32), REG_HRO }, + { DRDATA (STIME, mac_cntlr.seek_time, 24), PV_LEFT | REG_NZ }, + { DRDATA (ITIME, mac_cntlr.sector_time, 24), PV_LEFT | REG_NZ }, + { DRDATA (CTIME, mac_cntlr.cmd_time, 24), PV_LEFT | REG_NZ }, + { DRDATA (DTIME, mac_cntlr.data_time, 24), PV_LEFT | REG_NZ }, + { DRDATA (WTIME, mac_cntlr.wait_time, 31), PV_LEFT | REG_NZ }, + + { FLDATA (CTL, ds.control, 0) }, + { FLDATA (FLG, ds.flag, 0) }, + { FLDATA (FBF, ds.flagbuf, 0) }, + { FLDATA (SRQ, ds.srq, 0) }, + { FLDATA (EDT, ds.edt, 0) }, + + { URDATA (UCYL, ds_unit[0].CYL, 10, 10, 0, DS_UNITS, PV_LEFT) }, + { URDATA (UOP, ds_unit[0].OP, 8, 6, 0, DS_UNITS, PV_RZRO) }, + { URDATA (USTAT, ds_unit[0].STAT, 2, 8, 0, DS_UNITS, PV_RZRO) }, + { URDATA (UPHASE, ds_unit[0].PHASE, 8, 3, 0, DS_UNITS, PV_RZRO) }, + { URDATA (UPOS, ds_unit[0].pos, 8, T_ADDR_W, 0, DS_UNITS, PV_LEFT) }, + { URDATA (UWAIT, ds_unit[0].wait, 8, 32, 0, DS_UNITS, PV_LEFT) }, + + { ORDATA (SC, ds_dib.select_code, 6), REG_HRO }, + { ORDATA (DEVNO, ds_dib.select_code, 6), REG_HRO }, { NULL } }; -MTAB ds_mod[] = { - { UNIT_UNLOAD, UNIT_UNLOAD, "heads unloaded", "UNLOADED", ds_load_unload }, - { UNIT_UNLOAD, 0, "heads loaded", "LOADED", ds_load_unload }, - { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, - { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, - { UNIT_FMT, 0, "format disabled", "NOFORMAT", NULL }, - { UNIT_FMT, UNIT_FMT, "format enabled", "FORMAT", NULL }, - { (UNIT_DTYPE+UNIT_ATT), (D7905_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, - "7905", NULL, NULL }, - { (UNIT_DTYPE+UNIT_ATT), (D7906_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, - "7906", NULL, NULL }, - { (UNIT_DTYPE+UNIT_ATT), (D7920_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, - "7920", NULL, NULL }, - { (UNIT_DTYPE+UNIT_ATT), (D7925_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, - "7925", NULL, NULL }, - { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7905_DTYPE << UNIT_V_DTYPE), - "7905", NULL, NULL }, - { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7906_DTYPE << UNIT_V_DTYPE), - "7906", NULL, NULL }, - { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7920_DTYPE << UNIT_V_DTYPE), - "7920", NULL, NULL }, - { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7925_DTYPE << UNIT_V_DTYPE), - "7925", NULL, NULL }, - { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, - { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, - { (UNIT_AUTO+UNIT_DTYPE), (D7905_DTYPE << UNIT_V_DTYPE), - NULL, "7905", &ds_set_size }, - { (UNIT_AUTO+UNIT_DTYPE), (D7906_DTYPE << UNIT_V_DTYPE), - NULL, "7906", &ds_set_size }, - { (UNIT_AUTO+UNIT_DTYPE), (D7920_DTYPE << UNIT_V_DTYPE), - NULL, "7920", &ds_set_size }, - { (UNIT_AUTO+UNIT_DTYPE), (D7925_DTYPE << UNIT_V_DTYPE), - NULL, "7925", &ds_set_size }, - { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &ds_dev }, +static MTAB ds_mod [] = { +/* mask match pstring mstring valid disp desc */ + { UNIT_UNLOAD, UNIT_UNLOAD, "heads unloaded", "UNLOADED", &ds_load_unload, NULL, NULL }, + { UNIT_UNLOAD, 0, "heads loaded", "LOADED", &ds_load_unload, NULL, NULL }, + + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL, NULL, NULL }, + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL, NULL, NULL }, + + { UNIT_FMT, UNIT_FMT, "format enabled", "FORMAT", NULL, NULL, NULL }, + { UNIT_FMT, 0, "format disabled", "NOFORMAT", NULL, NULL, NULL }, + +/* mask match pstring mstring valid disp desc */ + { UNIT_AUTO | UNIT_ATT, UNIT_AUTO, "autosize", "AUTOSIZE", &dl_set_model, NULL, NULL }, + { UNIT_AUTO | UNIT_ATT | UNIT_MODEL, MODEL_7905, "7905", "7905", &dl_set_model, NULL, NULL }, + { UNIT_AUTO | UNIT_ATT | UNIT_MODEL, MODEL_7906, "7906", "7906", &dl_set_model, NULL, NULL }, + { UNIT_AUTO | UNIT_ATT | UNIT_MODEL, MODEL_7920, "7920", "7920", &dl_set_model, NULL, NULL }, + { UNIT_AUTO | UNIT_ATT | UNIT_MODEL, MODEL_7925, "7925", "7925", &dl_set_model, NULL, NULL }, + { UNIT_ATT | UNIT_MODEL, UNIT_ATT | MODEL_7905, "7905", NULL, NULL, NULL, NULL }, + { UNIT_ATT | UNIT_MODEL, UNIT_ATT | MODEL_7906, "7906", NULL, NULL, NULL, NULL }, + { UNIT_ATT | UNIT_MODEL, UNIT_ATT | MODEL_7920, "7920", NULL, NULL, NULL, NULL }, + { UNIT_ATT | UNIT_MODEL, UNIT_ATT | MODEL_7925, "7925", NULL, NULL, NULL, NULL }, + + { MTAB_XTD | MTAB_VDV, 0, "SC", "SC", &hp_setsc, &hp_showsc, &ds_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &ds_dev }, { 0 } }; -DEVICE ds_dev = { - "DS", ds_unit, ds_reg, ds_mod, - DS_NUMDR + 2, 8, 27, 1, 8, 16, - NULL, NULL, &ds_reset, - &ds_boot, &ds_attach, &ds_detach, - &ds_dib, DEV_DISABLE +static DEBTAB ds_deb [] = { + { "CPU", DEB_CPU }, + { "CMDS", DEB_CMDS }, + { "BUF", DEB_BUF }, + { "RWSC", DEB_RWSC }, + { "SERV", DEB_SERV }, + { NULL, 0 } }; +DEVICE ds_dev = { + "DS", /* device name */ + ds_unit, /* unit array */ + ds_reg, /* register array */ + ds_mod, /* modifier array */ + DS_UNITS, /* number of units */ + 8, /* address radix */ + 27, /* address width = 128 MB */ + 1, /* address increment */ + 8, /* data radix */ + 16, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &ds_reset, /* reset routine */ + &ds_boot, /* boot routine */ + &ds_attach, /* attach routine */ + &ds_detach, /* detach routine */ + &ds_dib, /* device information block */ + DEV_DEBUG | DEV_DISABLE, /* device flags */ + 0, /* debug control flags */ + ds_deb, /* debug flag name table */ + NULL, /* memory size change routine */ + NULL /* logical device name */ + }; + + + +/* MAC disc global VM routines */ + /* I/O signal handler. - The 13175A disc interface is unusual in that the flag and SRQ signals are - decoupled. This is done to allow DMA transfers at the maximum possible speed - (driving SRQ from the flag limits transfers to only every other cycle). SRQ - is based on the card's FIFO; if data or room in the FIFO is available, SRQ is - set to transfer it. The flag is only used to signal an interrupt at the end - of a command. + The 13175D disc interface data path consists of an input multiplexer/latch + and a 16-word FIFO buffer. The FIFO source may be either the CPU's I/O + input bus or the controller's interface data bus. The output of the FIFO may + be enabled either to the CPU's I/O output bus or the interface data bus. - Also unusual is that SFC and SFS test different things, rather than + The control path consists of the usual control, flag buffer, flag, and SRQ + flip-flops, although flag and SRQ are decoupled to allow the full DCPC + transfer rate through the FIFO (driving SRQ from the flag limits transfers to + only every other cycle). SRQ is based on the FIFO level: if data or room in + the FIFO is available, SRQ is set to transfer it. The flag is only used to + signal an interrupt at the end of a command. + + One unusual aspect is that SFC and SFS test different things, rather than complementary states of the same thing. SFC tests the busy flip-flop, and SFS tests the flag flip-flop. + In addition, the card contains end-of-data-transfer, command-follows, and + command-ready flip-flops. EDT is set when the DCPC EDT signal is asserted + and is used in conjunction with the FIFO level to assert the end-of-data + signal to the controller. The command-follows flip-flop is set by a CLC to + indicate that the next data word output from the CPU is a disc command. The + command-ready flip-flop is set when a command is received to schedule an + interface poll. + + Implementation notes: - 1. The dispatcher runs the command poll after each I/O signal, except for - SIR and ENF. Running the poll for these two will cause multi-drive - access to fail. + 1. In hardware, SRQ is enabled only when the controller is reading or + writing the disc (IFIN or IFOUT functions are asserted) and set when the + FIFO is not empty (read) or not full (write). In simulation, SRQ is set + by the unit service read/write data phase transfers and cleared below + when the FIFO is empty (read) or full (write). + + 2. The DCPC EDT signal cannot set the controller's end-of-data flag directly + because a write EOD must occur only after the FIFO has been drained. + + 3. Polling the interface or drives must be deferred to the end of the I/O + signal service. If they are performed in the IOO/STC handlers + themselves, an associated CLF might clear the flag that was set by the + poll. + + 4. Executing a CLC sets the controller's end-of-data flag, which will abort + a read or write data transfer in progress. Parameter transfers are not + affected. If a command is received when a parameter is expected, the + word is interpreted as data, even though the command-ready flip-flop is + set. The controller firmware only checks DTRDY for a parameter transfer, + and DTRDY is asserted whenever the FIFO is not empty. + + 5. The hardware Interface Function and Flag Buses are not implemented + explicitly. Instead, interface functions and signals are inferred by the + interface by the current command operation and phase. */ -uint32 dsio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data) +uint32 ds_io (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data) { +static const char * const output_state [] = { "Data", "Command" }; +const char * const hold_or_clear = (signal_set & ioCLF ? ",C" : ""); + +uint16 data; +t_stat status; IOSIGNAL signal; IOCYCLE working_set = IOADDSIR (signal_set); /* add ioSIR if needed */ +t_bool command_issued = FALSE; +t_bool interrupt_enabled = FALSE; while (working_set) { - signal = IONEXT (working_set); /* isolate next signal */ + signal = IONEXT (working_set); /* isolate the next signal */ - switch (signal) { /* dispatch I/O signal */ + switch (signal) { /* dispatch the I/O signal */ case ioCLF: /* clear flag flip-flop */ - ds.flag = ds.flagbuf = CLEAR; /* clear flag */ - ds.srq = CLEAR; /* CLF clears SRQ */ + ds.flag = CLEAR; /* clear the flag */ + ds.flagbuf = CLEAR; /* and flag buffer */ + + if (DEBUG_PRI (ds_dev, DEB_CMDS)) + fputs (">>DS cmds: [CLF] Flag cleared\n", sim_deb); break; case ioSTF: /* set flag flip-flop */ case ioENF: /* enable flag */ - ds.flag = ds.flagbuf = SET; /* set flag and flag buffer */ + ds.flag = SET; /* set the flag */ + ds.flagbuf = SET; /* and flag buffer */ + + if (DEBUG_PRI (ds_dev, DEB_CMDS)) + fputs (">>DS cmds: [STF] Flag set\n", sim_deb); break; case ioSFC: /* skip if flag is clear */ - setSKF (ds_busy == 0); /* skip if not busy */ + setSKF (mac_cntlr.state != cntlr_busy); /* skip if the controller is not busy */ break; case ioSFS: /* skip if flag is set */ - setstdSKF (ds); + setstdSKF (ds); /* assert SKF if the flag is set */ break; - case ioIOI: /* I/O data input */ - stat_data = IORETURN (SCPE_OK, ds_fifo_read ()); /* merge in return status */ + case ioIOI: /* I/O data input */ + data = fifo_unload (); /* unload the next word from the FIFO */ + stat_data = IORETURN (SCPE_OK, data); /* merge in the return status */ + + if (DEBUG_PRI (ds_dev, DEB_CPU)) + fprintf (sim_deb, ">>DS cpu: [LIx%s] Data = %06o\n", hold_or_clear, data); + + if (FIFO_EMPTY) { /* is the FIFO now empty? */ + if (ds.srq == SET && DEBUG_PRI (ds_dev, DEB_CMDS)) + fprintf (sim_deb, ">>DS cmds: [LIx%s] SRQ cleared\n", hold_or_clear); + + ds.srq = CLEAR; /* clear SRQ */ + + if (ds_cntlr.PHASE == data_phase) { /* is this an outbound parameter? */ + ds_cntlr.wait = mac_cntlr.data_time; /* activate the controller */ + activate_unit (&ds_cntlr); /* to acknowledge the data */ + } + } break; - case ioIOO: /* I/O data output */ - if (ds_cmdf) { /* expecting command? */ - ds_cmd = IODATA (stat_data); /* save command */ - ds_cmdf = 0; - ds_cmdp = 1; /* command present */ + case ioIOO: /* I/O data output */ + data = IODATA (stat_data); /* mask to just the data word */ + + if (DEBUG_PRI (ds_dev, DEB_CPU)) + fprintf (sim_deb, ">>DS cpu: [OTx%s] %s = %06o\n", + hold_or_clear, output_state [ds.cmfol], data); + + fifo_load (data); /* load the word into the FIFO */ + + if (ds.cmfol == SET) { /* are we expecting a command? */ + ds.cmfol = CLEAR; /* clear the command follows flip-flop */ + ds.cmrdy = SET; /* set the command ready flip-flop */ + command_issued = TRUE; /* and request an interface poll */ } - else - ds_fifo_write (IODATA (stat_data)); /* put in fifo */ + else { /* not a command */ + if (ds_cntlr.PHASE == data_phase) { /* is this an inbound parameter? */ + ds_cntlr.wait = mac_cntlr.data_time; /* activate the controller */ + activate_unit (&ds_cntlr); /* to receive the data */ + } + + if (FIFO_STOP) { /* is the FIFO now full enough? */ + if (ds.srq == SET && DEBUG_PRI (ds_dev, DEB_CMDS)) + fprintf (sim_deb, ">>DS cmds: [OTx%s] SRQ cleared\n", hold_or_clear); + + ds.srq = CLEAR; /* clear SRQ */ + } + } break; case ioPOPIO: /* power-on preset to I/O */ - ds.flag = ds.flagbuf = SET; /* set flag and flag buffer */ - ds_cmdp = 0; /* clear command ready */ + ds.flag = SET; /* set the flag */ + ds.flagbuf = SET; /* and flag buffer */ + ds.cmrdy = CLEAR; /* clear the command ready flip-flop */ + + if (DEBUG_PRI (ds_dev, DEB_CMDS)) + fputs (">>DS cmds: [POPIO] Flag set\n", sim_deb); break; - case ioCRS: /* control reset */ - ds.control = CLEAR; /* clear control */ - ds_cmdf = 0; /* not expecting command */ - ds_clear (); /* do controller CLEAR */ + case ioCRS: /* control reset */ + if (DEBUG_PRI (ds_dev, DEB_CMDS)) + fputs (">>DS cmds: [CRS] Master reset\n", sim_deb); + + ds.control = CLEAR; /* clear the control */ + ds.cmfol = CLEAR; /* and command follows flip-flops */ + + if (PRESET_ENABLE) { /* is preset enabled for this interface? */ + fifo_clear (); /* clear the FIFO */ + + status = dl_clear_controller (&mac_cntlr, /* do a hard clear of the controller */ + ds_unit, hard_clear); + + stat_data = IORETURN (status, 0); /* return the status from the controller */ + } break; case ioCLC: /* clear control flip-flop */ - ds.control = CLEAR; /* clear control */ - ds_cmdf = 1; /* expecting command */ - ds_cmdp = 0; /* none pending */ - ds_eod = 1; /* set EOD flag */ - ds_fifo_reset (); /* clear fifo */ + if (DEBUG_PRI (ds_dev, DEB_CMDS)) + fprintf (sim_deb, ">>DS cmds: [CLC%s] Control cleared\n", hold_or_clear); + + ds.control = CLEAR; /* clear the control */ + ds.edt = CLEAR; /* and EDT flip-flops */ + ds.cmfol = SET; /* set the command follows flip-flop */ + mac_cntlr.eod = SET; /* set the controller's EOD flag */ + + fifo_clear (); /* clear the FIFO */ break; case ioSTC: /* set control flip-flop */ - ds.control = SET; /* set control */ + ds.control = SET; /* set the control flip-flop */ + + interrupt_enabled = TRUE; /* check for drive attention */ + + if (DEBUG_PRI (ds_dev, DEB_CMDS)) + fprintf (sim_deb, ">>DS cmds: [STC%s] Control set\n", hold_or_clear); break; case ioEDT: /* end data transfer */ - ds_eod = 1; /* flag end transfer */ + ds.edt = SET; /* set the EDT flip-flop */ + + if (DEBUG_PRI (ds_dev, DEB_CPU)) + fputs (">>DS cpu: [EDT] DCPC transfer ended\n", sim_deb); break; case ioSIR: /* set interrupt request */ - setstdPRL (ds); /* set standard PRL signal */ - setstdIRQ (ds); /* set standard IRQ signal */ - setSRQ (dibptr->select_code, ds.srq); /* set SRQ signal */ + setstdPRL (ds); /* set the standard PRL signal */ + setstdIRQ (ds); /* set the standard IRQ signal */ + setSRQ (dibptr->select_code, ds.srq); /* set the SRQ signal */ break; case ioIAK: /* interrupt acknowledge */ - ds.flagbuf = CLEAR; + ds.flagbuf = CLEAR; /* clear the flag */ break; @@ -694,1041 +593,884 @@ while (working_set) { break; /* are ignored */ } - working_set = working_set & ~signal; /* remove current signal from set */ + working_set = working_set & ~signal; /* remove the current signal from the set */ } -if (!(signal_set & ioSIR) && !(signal_set & ioENF)) /* if not IRQ update */ - ds_poll (); /* run the controller */ + +if (command_issued) /* was a command received? */ + poll_interface (); /* poll the interface for the next command */ +else if (interrupt_enabled) /* were interrupts enabled? */ + poll_drives (); /* poll the drives for Attention */ return stat_data; } -/* Run the controller polling loop, based on ds_state: +/* Service the disc drive unit. - IDLE commands and ATN interrupts - WAIT commands only - BUSY nothing + The unit service routine is called to execute scheduled controller commands + for the specified unit. The actions to be taken depend on the current state + of the controller and the unit. + + Generally, the controller library service routine handles all of the disc + operations except data transfer to and from the interface. Read transfers + are responsible for loading words from the sector buffer into the FIFO and + enabling SRQ. If the current sector transfer is complete, either due to EDT + assertion or buffer exhaustion, the controller is moved to the end phase to + complete or continue the read with the next sector. In either case, the unit + is rescheduled. If the FIFO overflows, the read terminates with a data + overrun error. + + Write transfers set the initial SRQ to request words from the CPU. As each + arrives, it is unloaded from the FIFO into the sector buffer, and SRQ is + enabled. If the current sector transfer is complete, the controller is moved + to the end phase. If the FIFO underflows, the write terminates with a data + overrun error. + + The synchronous nature of the disc drive requires that data be supplied or + accepted continuously by the CPU. DCPC generally assures that this occurs, + and the FIFO allows for some latency before an overrun or underrun occurs. + + The other operation the interface must handle is seek completion. The + controller handles seek completion by setting Attention status in the drive's + status word. The interface is responsible for polling the drives if the + controller is idle and interrupts are enabled. + + + Implementation notes: + + 1. Every command except Seek, Recalibrate, and End set the flag when the + command completes. A command completes when the controller is no longer + busy (it becomes idle for Seek, Recalibrate, and End, and becomes waiting + for all others). Seek and Recalibrate may generate errors (e.g., heads + unloaded), in which case the flag must be set. But in these cases, the + controller state is waiting, not idle. + + However, it is insufficient simply to check that the controller has moved + to the wait state, because a seek may complete while the controller is + waiting for the next command. For example, a Seek is started on unit 0, + and the controller moves to the idle state. But before the seek + completes, another command is issued that attempts to access unit 1, + which is not ready. The command fails with a Status-2 error, and the + controller moves to the wait state. When the seek completes, the + controller is waiting with error status. We must determine if the seek + completed successfully or not, as we must interrupt in the latter case. + + Therefore, we determine seek completion by checking if the Attention + status was set. Attention sets only if the seek completes successfully. + + (Actually, Attention sets if a seek check occurs, but in that case, the + command terminated before the seek ever started. Also, a seek may + complete while the controller is busy, waiting, or idle.) + + 2. For debug printouts, we want to print the name of the command that has + completed when the controller returns to the idle or wait state. + Normally, we would use the controller's "opcode" field to identify the + command that completed. However, while waiting for Seek or Recalibrate + completion, "opcode" may be set to another command if that command does + not access this drive. For example, it might be set to a Read of + another unit, or a Request Status for this unit. So we can't rely on + "opcode" to report the correct positioning command completion. + + However, we cannot rely on "uptr->OP" either, as it can be changed + during the course of a command. For example, Read Without Verify is + changed to Read after a track crossing. + + Instead, we have to determine whether a seek is completing. If it is, + then we report "uptr->OP"; otherwise, we report "opcode". + + 3. The initial write SRQ must set only at the transition from the start + phase to the data phase. If a write command begins with an auto-seek, + the drive service will be entered twice in the start phase (the first + entry performs the seek, and the second begins the write). In hardware, + SRQ does not assert until the write begins. + + 4. The DCPC EDT signal cannot set the controller's end-of-data flag + directly because a write EOD must only occur after the FIFO has been + drained. */ -void ds_poll (void) +t_stat ds_service_drive (UNIT *uptr) { -if ((ds_state != DS_BUSY) && ds_cmdp) /* cmd pending? */ - ds_docmd (ds_cmd); /* do it */ -if ((ds_state == DS_IDLE) && ds.control) /* idle? */ - ds_doatn (); /* check ATN */ -return; -} +static const char completion_message [] = ">>DS rwsc: Unit %d %s command completed\n"; +t_stat result; +t_bool seek_completion; +FLIP_FLOP entry_srq = ds.srq; /* SRQ state on entry */ +CNTLR_PHASE entry_phase = (CNTLR_PHASE) uptr->PHASE; /* operation phase on entry */ +uint32 entry_status = uptr->STAT; /* drive status on entry */ +result = dl_service_drive (&mac_cntlr, uptr); /* service the drive */ -/* Process a command - ctrl state is either IDLE or WAIT. +if ((CNTLR_PHASE) uptr->PHASE == data_phase) /* is the drive in the data phase? */ + switch ((CNTLR_OPCODE) uptr->OP) { /* dispatch the current operation */ - - A drive may be processing a seek or recalibrate - - The controller unit is idle - - If the command can be processed, ds_state is set to BUSY, and - the interface command buffer is cleared - - If the command cannot be processed, ds_state is set to WAIT, - and the command is retained in the interface command buffer */ - -void ds_docmd (uint32 cmd) -{ -uint32 op, f, unum; - -op = DSC_GETOP (cmd); /* operation */ -f = ds_opflags[op]; /* flags */ -if (op == DSC_COLD) unum = 0; /* boot force unit 0 */ -else unum = DSC_GETUNIT (cmd); /* get unit */ -if ((f & CMF_UIDLE) && (unum < DS_NUMDR) && /* idle required */ - sim_is_active (&ds_unit[unum])) { /* but unit busy? */ - ds_state = DS_WAIT; /* wait */ - return; - } -ds_cmdp = 0; /* flush command */ -ds_state = DS_BUSY; /* ctrl is busy */ -if (f & CMF_CLRS) ds_sr1 = 0; /* clear status */ -if (f & CMF_CLREC) ds_eoc = 0; /* clear end cyl */ -if (f & CMF_UNDF) { /* illegal op? */ - ds_sched_ctrl_op (DSC_BADF, 0, CLR_BUSY); /* sched, clr busy */ - return; - } -switch (op) { - -/* Drive commands */ - - case DSC_COLD: /* cold load read */ - ds_fmask = DSC_SPEN; /* sparing enabled */ - ds_cyl = 0; /* cylinder 0 */ - ds_hs = (DSC_GETCHD (ds_cmd) << DSHS_V_HD) | /* reformat hd/sec */ - (DSC_GETCSC (ds_cmd) << DSHS_V_SC); - case DSC_RECAL: /* recalibrate */ - case DSC_SEEK: /* seek */ - case DSC_RSA: /* read sector address */ - case DSC_READ: /* read */ - case DSC_RFULL: /* read full */ - case DSC_ROFF: /* read offset */ - case DSC_RNOVFY: /* read no verify */ - case DSC_VFY: /* verify */ - case DSC_WRITE: /* write */ - case DSC_WFULL: /* write full */ - case DSC_INIT: /* init */ - ds_sr1 = unum; /* init status */ - if (unum >= DS_NUMDR) { /* invalid unit? */ - ds_sched_ctrl_op (DSC_BADU, unum, CLR_BUSY);/* sched, not busy */ - return; - } - if (op == DSC_INIT) ds_sr1 |= /* init? */ - ((cmd & DSC_SPAR)? DS1_SPAR: 0) | /* copy SPD to stat1 */ - ((cmd & DSC_PROT)? DS1_PROT: 0) | - ((cmd & DSC_DFCT)? DS1_DFCT: 0); - ds_unit[unum].FNC = op; /* save op */ - ds_unit[unum].STA &= ~DS2_ATN; /* clear ATN */ - sim_cancel (&ds_unit[unum]); /* cancel current */ - sim_activate (&ds_unit[unum], ds_ctime); /* schedule unit */ - ds_busy = 1; /* set visible busy */ - break; - -/* Read status commands */ - - case DSC_RSTA: /* read status */ - dsxb[1] = ds_sr1; /* return SR1 */ - ds_sr1 = 0; /* clear SR1 */ - if (unum < DS_NUMDR) { /* return SR2 */ - dsxb[0] = ds_updds2 (&ds_unit[unum]); - ds_unit[unum].STA &= ~DS2_FS; /* clear 1st */ - } - else dsxb[0] = DS2_ERR|DS2_NR; - ds_sched_ctrl_op (DSC_RSTA, 2, SET_BUSY); /* sched 2 wds, busy */ - break; - - case DSC_RDA: /* read disk address */ - ds_reqad (&dsxb[1], &dsxb[0]); /* return disk address */ - ds_sched_ctrl_op (DSC_RSTA, 2, SET_BUSY); /* sched 2 wds, busy */ - break; - - case DSC_RSYN: /* read syndrome */ - dsxb[6] = ds_sr1; /* return SR1 */ - ds_reqad (&dsxb[5], &dsxb[4]); /* return disk address */ - dsxb[3] = dsxb[2] = dsxb[1] = dsxb[0] = 0; /* syndrome is 0 */ - ds_sched_ctrl_op (DSC_RSTA, 7, SET_BUSY); /* sched 7 wds, busy */ - break; - -/* Other controller commands */ - - case DSC_WAKE: /* wakeup */ - ds_sr1 = unum; /* init status */ - if (unum >= DS_NUMDR) { /* invalid unit? */ - ds_sched_ctrl_op (DSC_BADU, unum, CLR_BUSY);/* sched, not busy */ - return; - } - case DSC_SFM: /* set file mask */ - case DSC_CLEAR: /* clear */ - case DSC_AREC: /* address record */ - case DSC_WTIO: /* write TIO */ - ds_sched_ctrl_op (op, 0, SET_BUSY); /* schedule, busy */ - break; - - case DSC_END: /* end */ - ds_set_idle (); /* idle ctrl */ - break; - } - -return; -} - - -/* Check for attention */ - -void ds_doatn (void) -{ -uint32 i; - -for (i = 0; i < DS_NUMDR; i++) { /* intr disabled? */ - ds_lastatn = (ds_lastatn + 1) & DS_DRMASK; /* loop through units */ - if (ds_unit[ds_lastatn].STA & DS2_ATN) { /* ATN set? */ - ds_unit[ds_lastatn].STA &= ~DS2_ATN; /* clear ATN */ - dsio (&ds_dib, ioENF, 0); /* request interrupt */ - ds_sr1 = DS1_ATN | ds_lastatn; /* set up status 1 */ - ds_state = DS_WAIT; /* block atn intrs */ - return; - } - } -return; -} - - -/* Controller service - - The argument for the function, if any, is stored in uptr->CYL */ - -t_stat ds_svc_c (UNIT *uptr) -{ -uint32 op; - -op = uptr->FNC; -switch (op) { - - case DSC_AREC: /* address record */ - ds_wait_for_cpu (uptr, DSC_AREC|DSC_2ND); /* set flag, new state */ - break; - case DSC_AREC | DSC_2ND: /* poll done */ - if (!DS_FIFO_EMPTY) { /* OTA ds? */ - ds_cyl = ds_fifo_read (); /* save cylinder */ - ds_wait_for_cpu (uptr, DSC_AREC|DSC_3RD); /* set flag, new state */ - } - else sim_activate (uptr, ds_ctime); /* no, continue poll */ - break; - case DSC_AREC | DSC_3RD: /* poll done */ - if (!DS_FIFO_EMPTY) { /* OTA ds? */ - ds_hs = ds_fifo_read (); /* save head/sector */ - ds_cmd_done (0, DS1_OK); /* op done, no flag */ - } - else sim_activate (uptr, ds_ctime); /* no, continue poll */ - break; - - case DSC_RSTA: /* rd stat (all forms) */ - if (DS_FIFO_EMPTY) { /* fifo empty? */ - uptr->CYL--; - ds_fifo_write (dsxb[uptr->CYL]); /* store next status */ - ds_wait_for_cpu (uptr, DSC_RSTA | - (uptr->CYL? 0: DSC_2ND)); /* set flag, new state */ - } - else sim_activate (uptr, ds_ctime); /* no, continue poll */ - break; - case DSC_RSTA | DSC_2ND: /* poll done */ - if (DS_FIFO_EMPTY) ds_cmd_done (0, DS1_OK); /* op done? no flag */ - else sim_activate (uptr, ds_ctime); /* no, continue poll */ - break; - - case DSC_CLEAR: /* clear */ - ds_clear (); /* reset ctrl */ - - ds.control = CLEAR; /* clear CTL, SRQ */ - ds.srq = CLEAR; - dsio (&ds_dib, ioSIR, 0); /* set interrupt request */ - - ds_cmd_done (1, DS1_OK); /* op done, set flag */ - break; - - case DSC_SFM: /* set file mask */ - ds_fmask = ds_cmd & DSC_FMASK; - ds_cmd_done (1, DS1_OK); /* op done, set flag */ - break; - - case DSC_WTIO: /* write I/O */ - ds_cmd_done (0, DS1_OK); /* op done, no flag */ - break; - - case DSC_WAKE: /* wakeup */ - ds_cmd_done (1, DS1_AVAIL); /* op done, set flag */ - break; - - case DSC_BADU: /* invalid unit */ - if (uptr->CYL > 10) ds_cmd_done (1, DS1_UNAVL); /* [11,16]? bad unit */ - else ds_cmd_done (1, DS1_S2ERR); /* else unit not ready */ - break; - - case DSC_BADF: /* invalid operation */ - ds_cmd_done (1, DS1_ILLOP); /* op done, set flag */ - break; - - default: - return SCPE_IERR; - } - -ds_poll (); /* run the controller */ -return SCPE_OK; -} - - -/* Timeout service */ - -t_stat ds_svc_t (UNIT *uptr) -{ -int32 i; - -for (i = 0; i < (DS_NUMDR + 1); i++) /* cancel all ops */ - sim_cancel (&ds_unit[i]); -ds_set_idle (); /* idle the controller */ -ds_fmask = 0; /* clear file mask */ -ds_poll (); /* run the controller */ -return SCPE_OK; -} - - -/* Unit service */ - -t_stat ds_svc_u (UNIT *uptr) -{ -uint32 op, dtyp; -t_stat r; - -op = uptr->FNC; -dtyp = GET_DTYPE (uptr->flags); - -switch (op) { /* case on function */ - -/* Seek and recalibrate */ - - case DSC_RECAL: /* recalibrate */ - if ((uptr->flags & UNIT_UNLOAD) == 0) { /* drive up? */ - ds_start_seek (uptr, 0, DSC_RECAL|DSC_2ND); /* set up seek */ - ds_set_idle (); /* ctrl is idle */ - } - else ds_cmd_done (1, DS1_S2ERR); /* not ready error */ - break; - case DSC_RECAL | DSC_2ND: /* recal complete */ - uptr->STA = uptr->STA | DS2_ATN; /* set attention */ - break; - - case DSC_SEEK: /* seek */ - ds_wait_for_cpu (uptr, DSC_SEEK|DSC_2ND); /* set flag, new state */ - break; - case DSC_SEEK | DSC_2ND: /* waiting for word 1 */ - if (!DS_FIFO_EMPTY) { /* OTA ds? */ - ds_cyl = ds_fifo_read (); /* save cylinder */ - ds_wait_for_cpu (uptr, DSC_SEEK|DSC_3RD); /* set flag, new state */ - } - else sim_activate (uptr, ds_ctime); /* no, continue poll */ - break; - case DSC_SEEK | DSC_3RD: /* waiting for word 2 */ - if (!DS_FIFO_EMPTY) { /* OTA ds? */ - ds_hs = ds_fifo_read (); /* save head/sector */ - if ((uptr->flags & UNIT_UNLOAD) == 0) { /* drive up? */ - ds_start_seek (uptr, ds_cyl, DSC_SEEK|DSC_4TH); /* set up seek */ - ds_set_idle (); /* ctrl is idle */ + case read: /* read operations */ + case read_full_sector: + case read_with_offset: + case read_without_verify: + if (mac_cntlr.length == 0 || ds.edt == SET) { /* is the data phase complete? */ + mac_cntlr.eod = ds.edt; /* set EOD if DCPC is done */ + uptr->PHASE = end_phase; /* set the end phase */ + uptr->wait = mac_cntlr.cmd_time; /* and schedule the controller */ } - else ds_cmd_done (1, DS1_S2ERR); /* else not ready error */ - } - else sim_activate (uptr, ds_ctime); /* continue poll */ - break; - case DSC_SEEK | DSC_4TH: /* seek complete */ - uptr->STA = uptr->STA | DS2_ATN; /* set attention */ - break; -/* Read variants */ + else if (FIFO_FULL) /* is the FIFO already full? */ + dl_end_command (&mac_cntlr, data_overrun); /* terminate the command with an overrun */ - case DSC_RSA: /* read sector address */ - if ((uptr->flags & UNIT_UNLOAD) == 0) { /* drive up? */ - dsxb[0] = GET_CURSEC (ds_dtime * DS_NUMWD, dtyp); /* rot position */ - ds_sched_ctrl_op (DSC_RSTA, 1, SET_BUSY); /* sched 1 wd, busy */ - } - else /* no drive or heads unloaded */ - ds_cmd_done (1, DS1_S2ERR); /* not ready error */ - break; + else { + fifo_load (buffer [mac_cntlr.index++]); /* load the next word into the FIFO */ + mac_cntlr.length--; /* count it */ + ds.srq = SET; /* ask DCPC to pick it up */ + ds_io (&ds_dib, ioSIR, 0); /* and recalculate the interrupts */ + uptr->wait = mac_cntlr.data_time; /* schedule the next data transfer */ + } - case DSC_ROFF: /* read with offset */ - ds_wait_for_cpu (uptr, DSC_ROFF|DSC_2ND); /* set flag, new state */ - break; - case DSC_ROFF | DSC_2ND: /* poll done */ - if (!DS_FIFO_EMPTY) { /* OTA ds? new state */ - ds_fifo_read (); /* drain fifo */ - uptr->FNC = DSC_READ; - dsio (&ds_dib, ioENF, 0); /* handshake */ - } - sim_activate (uptr, ds_ctime); /* schedule unit */ - break; - - case DSC_COLD: /* cold load read */ - if ((uptr->flags & UNIT_UNLOAD) == 0) /* drive up? */ - ds_start_seek (uptr, 0, DSC_READ); /* set up seek */ - else ds_cmd_done (1, DS1_S2ERR); /* no, not ready error */ - break; - - case DSC_READ: /* read */ - if (r = ds_start_rd (uptr, 0, 1)) return r; /* new sector; error? */ - break; - case DSC_READ | DSC_2ND: /* word transfer */ - ds_cont_rd (uptr, DS_NUMWD); /* xfr wd, check end */ - break; - case DSC_READ | DSC_3RD: /* end of sector */ - ds_end_rw (uptr, DSC_READ); /* see if more to do */ - break; - - case DSC_RNOVFY: /* read, no verify before xfer */ - if (r = ds_start_rd (uptr, 0, 0)) return r; /* new sector; error? */ - break; - case DSC_RNOVFY | DSC_2ND: /* word transfer */ - ds_cont_rd (uptr, DS_NUMWD); /* xfr wd, check end */ - break; - case DSC_RNOVFY | DSC_3RD: /* end of sector */ - ds_end_rw (uptr, /* see if more to do */ - DSHS_GETSC (ds_hs) ? DSC_RNOVFY : DSC_READ); /* start verifying if end of track */ - break; - - case DSC_RFULL: /* read full */ - dsxb[DS_FSYNC] = 0100376; /* fill in header */ - dsxb[DS_FCYL] = uptr->CYL; - dsxb[DS_FHS] = ds_hs; /* before h/s update */ - if (r = ds_start_rd (uptr, DS_FDATA, 0)) /* new sector; error? */ - return r; - break; - case DSC_RFULL | DSC_2ND: /* word transfer */ - ds_cont_rd (uptr, DS_NUMWDF); /* xfr wd, check end */ - break; - case DSC_RFULL | DSC_3RD: /* end of sector */ - ds_end_rw (uptr, DSC_RFULL); /* see if more to do */ - break; - - case DSC_VFY: /* verify */ - ds_wait_for_cpu (uptr, DSC_VFY|DSC_2ND); /* set flag, new state */ - break; - case DSC_VFY | DSC_2ND: /* poll done */ - if (!DS_FIFO_EMPTY) { /* OTA ds? */ - ds_vctr = ds_fifo_read (); /* save count */ - uptr->FNC = DSC_VFY | DSC_3RD; /* next state */ - sim_activate (uptr, ds_rtime); /* delay for transfer */ - } - else sim_activate (uptr, ds_ctime); /* no, continue poll */ - break; - case DSC_VFY | DSC_3RD: /* start sector */ - if (ds_start_rw (uptr, ds_dtime * DS_NUMWD, 1)) /* new sector; error? */ break; - ds_next_sec (uptr); /* increment hd, sc */ - break; - case DSC_VFY | DSC_4TH: /* end sector */ - ds_vctr = (ds_vctr - 1) & DMASK; /* decrement count */ - if (ds_vctr) ds_end_rw (uptr, DSC_VFY|DSC_3RD); /* more to do? */ - else ds_cmd_done (1, DS1_OK); /* no, set done */ - break; -/* Write variants */ - case DSC_WRITE: /* write */ - ds_start_wr (uptr, 1); /* new sector */ - break; - case DSC_WRITE | DSC_2ND: - if (r = ds_cont_wr (uptr, 0, DS_NUMWD)) /* write word */ - return r; /* error? */ - break; - case DSC_WRITE | DSC_3RD: /* end sector */ - ds_end_rw (uptr, DSC_WRITE); /* see if more to do */ - break; + case write: /* write operations */ + case write_full_sector: + case initialize: + if (entry_phase == start_phase) { /* is this the phase transition? */ + ds.srq = SET; /* start the DCPC transfer */ + ds_io (&ds_dib, ioSIR, 0); /* and recalculate the interrupts */ + } - case DSC_INIT: /* init */ - ds_start_wr (uptr, 0); /* new sector */ - break; - case DSC_INIT | DSC_2ND: - if (r = ds_cont_wr (uptr, 0, DS_NUMWD)) /* write word */ - return r; /* error? */ - break; - case DSC_INIT | DSC_3RD: /* end sector */ - ds_end_rw (uptr, DSC_INIT); /* see if more to do */ - break; + else if (FIFO_EMPTY) /* is the FIFO empty? */ + dl_end_command (&mac_cntlr, data_overrun); /* terminate the command with an underrun */ - case DSC_WFULL: /* write full */ - ds_start_wr (uptr, 0); /* new sector */ - break; - case DSC_WFULL | DSC_2ND: - if (r = ds_cont_wr (uptr, DS_FDATA, DS_NUMWDF)) /* write word */ - return r; /* error */ - break; - case DSC_WFULL | DSC_3RD: - ds_end_rw (uptr, DSC_WFULL); /* see if more to do */ - break; + else { + buffer [mac_cntlr.index++] = fifo_unload (); /* unload the next word from the FIFO */ + mac_cntlr.length--; /* count it */ - default: - break; - } + if (ds.edt == SET && FIFO_EMPTY) /* if DCPC is complete and the FIFO is empty */ + mac_cntlr.eod = SET; /* then set the end-of-data flag */ -ds_poll (); -return SCPE_OK; + if (mac_cntlr.length == 0 || mac_cntlr.eod == SET) { /* is the data phase complete? */ + uptr->PHASE = end_phase; /* set the end phase */ + uptr->wait = mac_cntlr.cmd_time; /* and schedule the controller */ + } + + else { + if (ds.edt == CLEAR) { /* if DCPC is still transferring */ + ds.srq = SET; /* then request the next word */ + ds_io (&ds_dib, ioSIR, 0); /* and recalculate the interrupts */ + } + + uptr->wait = mac_cntlr.data_time; /* schedule the next data transfer */ + } + } + + break; + + + default: /* entered with an invalid state */ + result = SCPE_IERR; /* return an internal (programming) error */ + break; + } /* end of data phase operation dispatch */ + + +if (DEBUG_PRI (ds_dev, DEB_CMDS) && entry_srq != ds.srq) + fprintf (sim_deb, ">>DS cmds: SRQ %s\n", ds.srq == SET ? "set" : "cleared"); + + +if (uptr->wait) /* is service requested? */ + activate_unit (uptr); /* schedule the next event */ + +seek_completion = ~entry_status & uptr->STAT & DL_S2ATN; /* seek is complete when Attention sets */ + +if (mac_cntlr.state != cntlr_busy) { /* is the command complete? */ + if (mac_cntlr.state == cntlr_wait && !seek_completion) /* is it command and not seek completion? */ + ds_io (&ds_dib, ioENF, 0); /* set the data flag to interrupt the CPU */ + + poll_interface (); /* poll the interface for the next command */ + poll_drives (); /* poll the drives for Attention */ + } + + +if (DEBUG_PRI (ds_dev, DEB_RWSC)) + if (result == SCPE_IERR) /* did an internal error occur? */ + fprintf (sim_deb, ">>DS rwsc: Unit %d %s command %s phase service not handled\n", + uptr - ds_dev.units, + dl_opcode_name (MAC, (CNTLR_OPCODE) uptr->OP), + dl_phase_name ((CNTLR_PHASE) uptr->PHASE)); + + else if (seek_completion) /* if a seek has completed */ + fprintf (sim_deb, completion_message, /* report the unit command */ + uptr - ds_dev.units, + dl_opcode_name (MAC, (CNTLR_OPCODE) uptr->OP)); + + else if (mac_cntlr.state == cntlr_wait) /* if the controller has stopped */ + fprintf (sim_deb, completion_message, /* report the controller command */ + uptr - ds_dev.units, + dl_opcode_name (MAC, mac_cntlr.opcode)); + +return result; /* return the result of the service */ } -/* Schedule timed wait for CPU response +/* Service the controller unit. - - Set flag to get CPU attention - - Set specified unit to 'newstate' and schedule - - Schedule timeout */ + The controller service routine is called to execute scheduled controller + commands that do not access drive units. It is also called to obtain command + parameters from the interface and to return command result values to the + interface. -void ds_wait_for_cpu (UNIT *uptr, uint32 newst) -{ -dsio (&ds_dib, ioENF, 0); /* set flag */ -uptr->FNC = newst; /* new state */ -sim_activate (uptr, ds_ctime); /* activate unit */ -sim_cancel (&ds_timer); /* activate timeout */ -sim_activate (&ds_timer, ds_tmo); -return; -} + Most controller commands are handled completely in the library's service + routine, so we call that first. Commands that neither accept nor supply + parameters are complete when the library routine returns, so all we have to + do is set the interface flag if required. + + For parameter transfers in the data phase, the interface is responsible for + moving words between the sector buffer and the FIFO and setting the flag to + notify the CPU. -/* Set idle state + Implementation notes: - - Controller is set to idle state - - Visible busy is cleared - - Timeout is cancelled */ + 1. In hardware, the Read With Offset command sets the data flag after the + offset parameter has been read and the head positioner has been moved by + the indicated amount. The intent is to delay the DCPC start until the + drive is ready to supply data from the disc. -void ds_set_idle (void) -{ -ds_busy = 0; /* busy clear */ -ds_state = DS_IDLE; /* ctrl idle */ -sim_cancel (&ds_timer); /* no timeout */ -return; -} - - -/* Set wait state - - - Set flag if required - - Set controller to wait state - - Clear visible busy - - Schedule timeout */ - -void ds_cmd_done (t_bool sf, uint32 sr1) -{ -if (sf) /* set host flag? */ - dsio (&ds_dib, ioENF, 0); /* set flag */ - -ds_busy = 0; /* clear visible busy */ -ds_sr1 = ds_sr1 | sr1; /* final status */ -ds_state = DS_WAIT; /* ctrl waiting */ -sim_cancel (&ds_timer); /* activate timeout */ -sim_activate (&ds_timer, ds_tmo); -return; -} - - -/* Return drive status (status word 2). - - In hardware, the controller outputs the Address Unit command on the drive tag - bus and the unit number on the drive control bus. The addressed drive - responds by setting its internal "selected" flag. The controller then - outputs Request Status on the tag bug. If a drive is selected but the heads - are unloaded, the drive returns Not Ready and Busy status on the control bus. - If no drive is selected, the control bus floats inactive. This is - interpreted by the controller as Not Ready status (because the drive returns - inactive Ready status). - - Under simulation, an enabled but detached unit corresponds to "selected but - heads unloaded," and a disabled unit corresponds to a non-existent unit. + In simulation, the flag is set as soon as the parameter is received. */ -uint32 ds_updds2 (UNIT *uptr) +t_stat ds_service_controller (UNIT *uptr) { -uint32 sta; -uint32 dtyp = GET_DTYPE (uptr->flags); +t_stat result; +const CNTLR_OPCODE opcode = (CNTLR_OPCODE) uptr->OP; -sta = drv_tab[dtyp].id | /* form status */ - uptr->STA | /* static bits */ - ((uptr->flags & UNIT_WPR) ? DS2_RO : 0) | /* dynamic bits */ - ((uptr->flags & UNIT_FMT) ? DS2_FRM : 0) | - ((uptr->flags & UNIT_DIS) ? DS2_NR : - (uptr->flags & UNIT_UNLOAD) ? DS2_NR | DS2_BS : 0) | - (sim_is_active (uptr) ? DS2_BS : 0); -if (sta & DS2_ALLERR) sta = sta | DS2_ERR; /* set error */ -return sta; -} +result = dl_service_controller (&mac_cntlr, uptr); /* service the controller */ + +switch ((CNTLR_PHASE) uptr->PHASE) { /* dispatch the current phase */ + + case start_phase: /* most controller operations */ + case end_phase: /* start and end on the same phase */ + switch (opcode) { /* dispatch the current operation */ + + case request_status: + case request_sector_address: + case address_record: + case request_syndrome: + case load_tio_register: + case request_disc_address: + case end: + break; /* complete the operation without setting the flag */ -/* Schedule controller operation */ - -void ds_sched_ctrl_op (uint32 op, uint32 arg, uint32 busy) -{ -ds_ctrl.FNC = op; /* save op */ -ds_ctrl.CYL = arg; /* save argument */ -ds_busy = busy; /* set visible busy */ -sim_activate (&ds_ctrl, ds_ctime); /* schedule */ -sim_cancel (&ds_timer); /* activate timeout */ -sim_activate (&ds_timer, ds_tmo); -return; -} + case clear: + case set_file_mask: + case wakeup: + ds_io (&ds_dib, ioENF, 0); /* complete the operation with the flag set */ + break; -/* Request address - if pending eoc, report cylinder + 1 */ - -void ds_reqad (uint16 *cyl, uint16 *hs) -{ -*cyl = ds_cyl + (ds_eoc? 1: 0); -*hs = ds_hs; -return; -} + default: /* entered with an invalid state */ + result = SCPE_IERR; /* return an internal (programming) error */ + break; + } /* end of operation dispatch */ + break; /* end of start and end phase handlers */ -/* Start seek - schedule whether in bounds or out of bounds */ + case data_phase: + switch (opcode) { /* dispatch the current operation */ -void ds_start_seek (UNIT *uptr, uint32 cyl, uint32 newst) -{ -int32 t; -uint32 hd, sc; -uint32 dtyp = GET_DTYPE (uptr->flags); + case seek: /* operations that accept parameters */ + case verify: + case address_record: + case read_with_offset: + case load_tio_register: + buffer [mac_cntlr.index++] = fifo_unload (); /* unload the next word from the FIFO */ + mac_cntlr.length--; /* count it */ -uptr->FNC = newst; /* set new state */ -if (cyl >= drv_tab[dtyp].cyl) { /* out of bounds? */ - t = 0; /* don't change cyl */ - uptr->STA = uptr->STA | DS2_SC; /* set seek check */ + if (mac_cntlr.length) /* are there more words to transfer? */ + ds_io (&ds_dib, ioENF, 0); /* set the flag to request the next one */ + + else { /* all parameters have been received */ + uptr->PHASE = end_phase; /* set the end phase */ + + if (opcode == read_with_offset) /* a Read With Offset command sets the flag */ + ds_io (&ds_dib, ioENF, 0); /* to indicate that offsetting is complete */ + + start_command (); /* the command is now ready to execute */ + } + break; + + + case request_status: /* operations that supply parameters */ + case request_sector_address: + case request_syndrome: + case request_disc_address: + if (mac_cntlr.length) { /* are there more words to return? */ + fifo_load (buffer [mac_cntlr.index++]); /* load the next word into the FIFO */ + mac_cntlr.length--; /* count it */ + + ds_io (&ds_dib, ioENF, 0); /* set the flag to request pickup by the CPU */ + } + + else { /* all parameters have been sent */ + uptr->PHASE = end_phase; /* set the end phase */ + uptr->wait = mac_cntlr.cmd_time; /* schedule the controller */ + activate_unit (uptr); /* to complete the command */ + } + break; + + + default: /* entered with an invalid state */ + result = SCPE_IERR; /* return an internal (programming) error */ + break; + } /* end of operation dispatch */ + break; /* end of data phase handlers */ + } /* end of phase dispatch */ + + +if (result == SCPE_IERR && DEBUG_PRI (ds_dev, DEB_RWSC)) /* did an internal error occur? */ + fprintf (sim_deb, ">>DS rwsc: Controller %s command %s phase service not handled\n", + dl_opcode_name (MAC, opcode), dl_phase_name ((CNTLR_PHASE) uptr->PHASE)); + + +if (mac_cntlr.state != cntlr_busy) { /* has the controller stopped? */ + poll_interface (); /* poll the interface for the next command */ + poll_drives (); /* poll the drives for Attention status */ + + if (DEBUG_PRI (ds_dev, DEB_RWSC)) + fprintf (sim_deb, ">>DS rwsc: Controller %s command completed\n", + dl_opcode_name (MAC, opcode)); } -else { - t = abs (uptr->CYL - cyl); /* delta cylinders */ - uptr->CYL = cyl; /* put on cylinder */ - hd = DSHS_GETHD (ds_hs); /* invalid head or sec? */ - sc = DSHS_GETSC (ds_hs); - if ((hd >= drv_tab[dtyp].hd) || - (sc >= drv_tab[dtyp].sc)) - uptr->STA = uptr->STA | DS2_SC; /* set seek check */ - else uptr->STA = uptr->STA & ~DS2_SC; /* clear seek check */ - } -sim_activate (uptr, ds_stime * (t + 1)); /* schedule */ -return; + +return result; /* return the result of the service */ } -/* Start next sector for read or write +/* Service the command wait timer unit. - - If error, set command done, return TRUE, nothing is scheduled - - If implicit seek, return TRUE, implicit seek is scheduled, but - state is not changed - we will return here when seek is done - - Otherwise, advance state, set position in file, schedule next state - - If a an auto-seek was done, it may have incremented or decremented beyond the - cylinder bounds. If so, then Seek Check status will have been set. If we - see that, terminate the current command with a Status-2 error. + The command wait timer service routine is called if the command wait timer + expires. The library is called to reset the file mask and idle the + controller. Then the interface is polled for a command and the drives are + polled for Attention status. */ -t_bool ds_start_rw (UNIT *uptr, int32 tm, t_bool vfy) +t_stat ds_service_timer (UNIT *uptr) { -uint32 da, hd, sc; -uint32 dtyp = GET_DTYPE (uptr->flags); +t_stat result; -ds_eod = 0; /* init eod */ -ds_ptr = 0; /* init buffer ptr */ -if (uptr->flags & UNIT_UNLOAD | uptr->STA & DS2_SC) { /* drive down or seek check? */ - ds_cmd_done (1, DS1_S2ERR); /* report Status-2 error */ - return TRUE; - } -if (ds_eoc) { /* at end of cylinder? */ - ds_next_cyl (uptr); /* auto seek to next */ - return TRUE; /* or error */ - } -if (vfy && ((uint32) uptr->CYL != ds_cyl)) { /* on wrong cylinder? */ - ds_start_seek (uptr, ds_cyl, uptr->FNC); /* seek right cyl */ - return TRUE; - } -hd = DSHS_GETHD (ds_hs); -sc = DSHS_GETSC (ds_hs); -if (((uint32) uptr->CYL >= drv_tab[dtyp].cyl) || /* valid cylinder? (sanity check) */ - (hd >= drv_tab[dtyp].hd) || /* valid head, sector? */ - (sc >= drv_tab[dtyp].sc)) { - uptr->STA = uptr->STA | DS2_SC; /* set seek check */ - ds_cmd_done (1, DS1_S2ERR); /* report Status-2 error */ - return TRUE; - } -da = GET_DA (uptr->CYL, hd, sc, dtyp); /* position in file */ -sim_fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET); /* set file pos */ -uptr->FNC += DSC_NEXT; /* next state */ -sim_activate (uptr, tm); /* activate unit */ -return FALSE; +result = dl_service_timer (&mac_cntlr, uptr); /* service the timer */ + +poll_interface (); /* poll the interface for the next command */ +poll_drives (); /* poll the drives for Attention status */ + +return result; /* return the result of the service */ } -/* Start next sector for read +/* Reset the simulator. - - Do common start for read and write - - If error, return, command has been terminated, nothing scheduled - - If implicit seek, return, seek scheduled - - If no error or seek, state has been advanced and unit scheduled - - Read sector - - If read error, terminate command and return, nothing scheduled - - If no error, advance head/sector, next state scheduled */ - -t_stat ds_start_rd (UNIT *uptr, uint32 off, t_bool vfy) -{ -uint32 t; - -if (ds_start_rw (uptr, ds_rtime, vfy)) return SCPE_OK; /* new sec; err or seek? */ -t = sim_fread (dsxb + off, sizeof (uint16), DS_NUMWD, uptr->fileref); -for (t = t + off ; t < DS_NUMWDF; t++) dsxb[t] = 0; /* fill sector */ -if (ferror (uptr->fileref)) /* error? */ - return ds_set_uncorr (uptr); /* say uncorrectable */ -ds_next_sec (uptr); /* increment hd, sc */ -return SCPE_OK; -} + In hardware, the PON signal clears the Interface Selected flip-flop, + disconnecting the interface from the disc controller. In simulation, the + interface always remains connected to the controller, so no special action is + needed. -/* Start next sector for write + Implementation notes: - - Do common start for read and write - - If error, return, command has been terminated, nothing scheduled - - If implicit seek, return, seek scheduled - - If no error or seek, state has been advanced and unit scheduled - - Clear buffer - - Set service request */ + 1. During a power-on reset, a pointer to the FIFO simulation register is + saved to allow access to the "qptr" field during FIFO loading and + unloading. This enables the SCP to view the FIFO as a circular queue, so + that the bottom word of the FIFO is always displayed as FIFO[0], + regardless of where it is in the actual FIFO array. -void ds_start_wr (UNIT *uptr, t_bool vfy) -{ -uint32 i; - -if ((uptr->flags & UNIT_WPR) || /* write protected? */ - (!vfy && ((uptr->flags & UNIT_FMT) == 0))) { /* format, not enbl? */ - ds_cmd_done (1, DS1_S2ERR); /* error */ - return; - } -if (ds_start_rw (uptr, ds_rtime, vfy)) return; /* new sec; err or seek? */ -for (i = 0; i < DS_NUMWDF; i++) dsxb[i] = 0; /* clear buffer */ -ds.srq = SET; /* request word */ -dsio (&ds_dib, ioSIR, 0); /* set interrupt request */ -return; -} - - -/* Advance to next sector (but not next cylinder) */ - -void ds_next_sec (UNIT *uptr) -{ -uint32 dtyp = GET_DTYPE (uptr->flags); - -ds_hs = ds_hs + 1; /* increment sector */ -if (DSHS_GETSC (ds_hs) < drv_tab[dtyp].sc) return; /* end of track? */ -ds_hs = ds_hs & ~DSHS_SC; /* yes, wrap sector */ -if (ds_fmask & DSC_CYLM) { /* cylinder mode? */ - ds_hs = ds_hs + (1 << DSHS_V_HD); /* increment head */ - if (DSHS_GETHD (ds_hs) < drv_tab[dtyp].hd) return; /* end of cyl? */ - ds_hs = ds_hs & ~DSHS_HD; /* 0 head */ - } -ds_eoc = 1; /* flag end cylinder */ -return; -} - - -/* Advance to next cylinder - - - If autoseek enabled, seek to cylinder +/- 1 - - Otherwise, done with end of cylinder error. - - If we exceed the cylinder range, the seek will set Seek Check status. -*/ - -void ds_next_cyl (UNIT *uptr) -{ -if (ds_fmask & DSC_AUTO) { /* auto seek allowed? */ - if (ds_fmask & DSC_DECR) ds_cyl = (ds_cyl - 1) & DMASK; - else ds_cyl = (ds_cyl + 1) & DMASK; - ds_eoc = 0; /* clear end cylinder */ - ds_start_seek (uptr, ds_cyl, uptr->FNC); /* seek, same state */ - } -else ds_cmd_done (1, DS1_EOCYL); /* no, end of cyl err */ -return; -} - - -/* Transfer word for read - - - If end of data, terminate command, nothing scheduled - - Otherwise, transfer word, advance state if last word, schedule */ - -void ds_cont_rd (UNIT *uptr, uint32 bsize) -{ -if (ds_eod) ds_cmd_done (1, DS1_OK); /* DMA end? done */ -else if (ds.srq) { /* overrun? */ - ds_cmd_done (1, DS1_OVRUN); /* set done */ - return; - } -else { - ds_fifo_write (dsxb[ds_ptr++]); /* next word */ - ds.srq = SET; /* request service */ - dsio (&ds_dib, ioSIR, 0); /* set interrupt request */ - if (ds_ptr >= bsize) uptr->FNC += DSC_NEXT; /* sec done? next state */ - sim_activate (uptr, ds_dtime); /* schedule */ - } -return; -} - - -/* Transfer word for write - - - Copy word from fifo to buffer - - If end of data, write buffer, terminate command, nothing scheduled - - If end of sector, write buffer, next state, schedule - - Otherwise, set service request, schedule */ - -t_stat ds_cont_wr (UNIT *uptr, uint32 off, uint32 bsize) -{ -uint32 i, dat; - -if (ds.srq) { /* overrun? */ - ds_cmd_done (1, DS1_OVRUN); /* set done */ - return SCPE_OK; - } -dsxb[ds_ptr++] = dat = ds_fifo_read (); /* next word */ -if (ds_eod || (ds_ptr >= bsize)) { /* xfr or sector done? */ - for (i = ds_ptr; i < bsize; i++) dsxb[i] = dat; /* fill sector */ - sim_fwrite (dsxb + off, sizeof (uint16), DS_NUMWD, uptr->fileref); - if (ferror (uptr->fileref)) /* error on write? */ - return ds_set_uncorr (uptr); /* uncorrectable */ - ds_next_sec (uptr); /* increment hd, sc */ - if (ds_eod) { /* end data? */ - ds_cmd_done (1, DS1_OK); /* set done */ - return SCPE_OK; - } - else uptr->FNC += DSC_NEXT; /* no, next state */ - } -else { - ds.srq = SET; /* request next word */ - dsio (&ds_dib, ioSIR, 0); /* set interrupt request */ - } -sim_activate (uptr, ds_dtime); /* schedule */ -return SCPE_OK; -} - - -/* End sector for read or write - - - If end of data, terminate command, nothing scheduled - - If end of cylinder, schedule next cylinder - - Else schedule start of next sector */ - -void ds_end_rw (UNIT *uptr, uint32 newst) -{ -uptr->FNC = newst; /* new state */ -if (ds_eod) ds_cmd_done (1, DS1_OK); /* done? */ -else if (ds_eoc) ds_next_cyl (uptr); /* end cyl? seek */ -else sim_activate (uptr, ds_rtime); /* normal transfer */ -return; -} - - -/* Report uncorrectable data error */ - -t_stat ds_set_uncorr (UNIT *uptr) -{ -sim_cancel (uptr); /* cancel any operation */ -ds_cmd_done (1, DS1_UNCOR); /* done with error */ -perror ("DS I/O error"); /* visible error */ -clearerr (uptr->fileref); -ds_poll (); /* force poll */ -return SCPE_IOERR; -} - - -/* Fifo read */ - -uint32 ds_fifo_read (void) -{ -uint32 dat; - -if (ds_fifo_cnt == 0) return ds_fifo[ds_fifo_rp]; -dat = ds_fifo[ds_fifo_rp++]; -if (ds_fifo_rp >= DS_FIFO_SIZE) ds_fifo_rp = 0; -ds_fifo_cnt--; -return dat; -} - -void ds_fifo_write (uint32 dat) -{ -ds_fifo[ds_fifo_ip++] = dat; -if (ds_fifo_ip >= DS_FIFO_SIZE) ds_fifo_ip = 0; -if (ds_fifo_cnt < DS_FIFO_SIZE) ds_fifo_cnt++; -return; -} - -void ds_fifo_reset (void) -{ -uint32 i; - -ds_fifo_ip = ds_fifo_rp = ds_fifo_cnt = 0; -for (i = 0; i < DS_FIFO_SIZE; i++) ds_fifo[i] = 0; -return; -} - - -/* Controller clear */ - -t_stat ds_clear (void) -{ -int32 i; - -ds_cmd = 0; /* clear command */ -ds_cmdf = ds_cmdp = 0; /* clear commands flops */ -ds_fifo_reset (); /* clear fifo */ -ds_eoc = ds_eod = 0; -ds_busy = 0; -ds_state = DS_IDLE; /* ctrl idle */ -ds_lastatn = 0; -ds_fmask = 0; -ds_ptr = 0; -ds_cyl = ds_hs = 0; -ds_vctr = 0; -for (i = 0; i < DS_NUMDR; i++) { /* loop thru drives */ - sim_cancel (&ds_unit[i]); /* cancel activity */ - ds_unit[i].FNC = 0; /* clear function */ - ds_unit[i].CYL = 0; - ds_unit[i].STA = 0; - } -sim_cancel (&ds_ctrl); -sim_cancel (&ds_timer); -return SCPE_OK; -} - - -/* Reset routine. - - The PON signal clears the Interface Selected flip-flop, disconnecting the - interface from the disc controller. Under simulation, the interface always - remains connected to the controller, so we take no special action on - power-up. + 2. SRQ is denied because neither IFIN nor IFOUT is asserted when the + interface is not selected. */ t_stat ds_reset (DEVICE *dptr) { -IOPRESET (&ds_dib); /* PRESET device */ +uint32 unit; + +if (sim_switches & SWMASK ('P')) { /* is this a power-on reset? */ + ds.fifo_reg = find_reg ("FIFO", NULL, dptr); /* find the FIFO register entry */ + + if (ds.fifo_reg == NULL) /* if it cannot be found, */ + return SCPE_IERR; /* report a programming error! */ + + else { /* found it */ + ds.fifo_reg->qptr = 0; /* so reset the FIFO bottom index */ + ds.fifo_count = 0; /* and clear the FIFO */ + } + + for (unit = 0; unit < dptr->numunits; unit++) { /* loop through all of the units */ + sim_cancel (dptr->units + unit); /* cancel activation */ + dptr->units [unit].CYL = 0; /* reset the head position to cylinder 0 */ + dptr->units [unit].pos = 0; /* (irrelevant for the controller and timer) */ + } + } + +IOPRESET (&ds_dib); /* PRESET the device */ ds.srq = CLEAR; /* clear SRQ */ + return SCPE_OK; } -/* Device attach */ +/* Attach a drive unit. + + The specified file is attached to the indicated drive unit. The library + attach routine will load the heads. This will set the First Status and + Attention bits in the drive status, so we poll the drives to ensure that the + CPU is notified that the drive is now online. + + + Implementation notes: + + 1. If we are called during a RESTORE command, the drive status will not be + changed, so polling the drives will have no effect. +*/ t_stat ds_attach (UNIT *uptr, char *cptr) { -uint32 i, p; -t_stat r; +t_stat result; -uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size; -r = attach_unit (uptr, cptr); /* attach unit */ -if (r != SCPE_OK) return r; /* error? */ -ds_load_unload (uptr, 0, NULL, NULL); /* if OK, load heads */ -ds_sched_atn (uptr); /* schedule attention */ -if (((uptr->flags & UNIT_AUTO) == 0) || /* static size? */ - ((p = sim_fsize (uptr->fileref)) == 0)) return SCPE_OK; /* new file? */ -for (i = 0; drv_tab[i].sc != 0; i++) { /* find best fit */ - if (p <= (drv_tab[i].size * sizeof (uint16))) { - uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); - uptr->capac = drv_tab[i].size; - return SCPE_OK; - } - } -return SCPE_OK; +result = dl_attach (&mac_cntlr, uptr, cptr); /* attach the drive */ + +if (result == SCPE_OK) /* was the attach successful? */ + poll_drives (); /* poll the drives to notify the CPU */ + +return result; } -/* Device detach */ +/* Detach a drive unit. + + The specified file is detached from the indicated drive unit. The library + detach routine will unload the heads. This will set the Attention bit in the + drive status, so we poll the drives to ensure that the CPU is notified that + the drive is now offline. +*/ t_stat ds_detach (UNIT *uptr) { -ds_load_unload (uptr, UNIT_UNLOAD, NULL, NULL); /* unload heads if attached */ -return detach_unit (uptr); +t_stat result; + +result = dl_detach (&mac_cntlr, uptr); /* detach the drive */ + +if (result == SCPE_OK) /* was the detach successful? */ + poll_drives (); /* poll the drives to notify the CPU */ + +return result; } -/* Load and unload heads */ +/* Boot a MAC disc drive. -t_stat ds_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc) -{ -if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* must be attached to [un]load */ -if (value == UNIT_UNLOAD) { /* unload heads? */ - uptr->flags = uptr->flags | UNIT_UNLOAD; /* indicate unload */ - uptr->STA = DS2_ATN; /* update drive status */ - ds_sched_atn (uptr); /* schedule attention */ - } -else { /* load heads */ - uptr->flags = uptr->flags & ~UNIT_UNLOAD; /* indicate load */ - uptr->STA = DS2_ATN | DS2_FS; /* update drive status */ - } -return SCPE_OK; -} + The MAC disc bootstrap program is loaded from the HP 12992B Boot Loader ROM + into memory, the I/O instructions are configured from the interface card + select code, and the program is run to boot from the specified unit. The + loader supports booting from cylinder 0 of drive unit 0 only. Before + execution, the S register is automatically set as follows: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + ------ ------ ---------------------- --------- --------- + ROM # 0 1 select code reserved head + + The boot routine sets bits 15-6 of the S register to appropriate values. + Bits 5-3 and 1-0 retain their original values, so S should be set before + booting. These bits are typically set to 0, although bit 5 is set for an RTE + reconfiguration boot, and bits 1-0 may be set if booting from a head other + than 0 is desired. -/* Schedule attention interrupt if CTL set, not restore, and controller idle */ + Implementation notes: -void ds_sched_atn (UNIT *uptr) -{ -int32 i; + 1. The Loader ROMs manual indicates that bits 2-0 select the head to use, + implying that heads 0-7 are valid. However, Table 5 has entries only for + heads 0-3, and the boot loader code will malfunction if heads 4-7 are + specified. The code masks the head number to three bits but forms the + Cold Load Read command by shifting the head number six bits to the left. + As the head field in the command is only two bits wide, specifying heads + 4-7 will result in bit 2 being shifted into the opcode field, resulting + in a Recalibrate command. +*/ -if (!ds.control || (sim_switches & SIM_SW_REST)) return; -for (i = 0; i < (DS_NUMDR + 1); i++) { /* check units, ctrl */ - if (sim_is_active (ds_dev.units + i)) return; - } -uptr->FNC = DSC_ATN; /* pseudo operation */ -sim_activate (uptr, 1); /* do immediately */ -return; -} - - -/* Set size command validation routine */ - -t_stat ds_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) -{ -if (uptr->flags & UNIT_ATT) return SCPE_ALATT; -uptr->capac = drv_tab[GET_DTYPE (val)].size; -return SCPE_OK; -} - - -/* 13037 bootstrap routine (HP 12992B ROM) */ const BOOT_ROM ds_rom = { - 0017727, /* STRT JSB STAT ; get status */ - 0002021, /* SSA,RSS ; is drive ready? */ - 0027742, /* JMP DMA ; yes, set up DMA */ - 0013714, /* AND B20 ; no, check status bits */ - 0002002, /* SZA ; faulty or hard down? */ - 0102030, /* HLT 30B ; HALT 30B */ - 0027700, /* JMP STRT ; try again */ - 0102011, /* ADR1 OCT 102011 */ - 0102055, /* ADR2 OCT 102055 */ - 0164000, /* CNT DEC -6144 */ - 0000007, /* D7 OCT 7 */ - 0001400, /* STCM OCT 1400 */ - 0000020, /* B20 OCT 20 */ - 0017400, /* STMS OCT 17400 */ - 0000000, /* 9 NOP's */ - 0000000, - 0000000, - 0000000, - 0000000, - 0000000, - 0000000, - 0000000, - 0000000, - 0000000, /* STAT NOP ; status check routine */ - 0107710, /* CLC DC,C ; set command mode */ - 0063713, /* LDA STCM ; get status command */ - 0102610, /* OTA DC ; output status command */ - 0102310, /* SFS DC ; wait for stat#1 word */ - 0027733, /* JMP *-1 */ - 0107510, /* LIB DC,C ; B-reg - status#1 word */ - 0102310, /* SFS DC ; wait for stat#2 word */ - 0027736, /* JMP *-1 */ - 0103510, /* LIA DC,C ; A-reg - status#2 word */ - 0127727, /* JMP STAT,I ; return */ - 0067776, /* DMA LDB DMAC ; get DMA control word */ - 0106606, /* OTB 6 ; output DMA ctrl word */ - 0067707, /* LDB ADR1 ; get memory address */ - 0106702, /* CLC 2 ; set memory addr mode */ - 0106602, /* OTB 2 ; output mem addr to DMA */ - 0102702, /* STC 2 ; set word count mode */ - 0067711, /* LDB CNT ; get word count */ - 0106602, /* OTB 2 ; output word cnt to DMA */ - 0106710, /* CLC CLC DC ; set command follows */ - 0102501, /* LIA 1 ; load switches */ - 0106501, /* LIB 1 ; register settings */ - 0013712, /* AND D7 ; isolate head number */ - 0005750, /* BLF,CLE,SLB ; bit 12 = 0? */ - 0027762, /* JMP *+3 ; no, manual boot */ - 0002002, /* SZA ; yes, RPL, head# = 0? */ - 0001000, /* ALS ; no, head# = 1 --> 2 */ - 0001720, /* ALF,ALS ; form cold load */ - 0001000, /* ALS ; command word */ - 0103706, /* STC 6,C ; activate DMA */ - 0103610, /* OTA DC,C ; output cold load cmd */ - 0102310, /* SFS DC ; is cold load done? */ - 0027766, /* JMP *-1 ; no, wait */ - 0017727, /* JSB STAT ; yes, get status */ - 0060001, /* LDA 1 ; get status word #1 */ - 0013715, /* AND STMS ; isolate status bits */ - 0002002, /* SZA ; is transfer ok? */ - 0027700, /* JMP STRT ; no, try again */ - 0117710, /* JSB ADR2,I ; yes, start program */ - 0000010, /* DMAC ABS DC ; DMA command word */ - 0170100, /* ABS -STRT */ + 0017727, /* START JSB STAT GET STATUS */ + 0002021, /* SSA,RSS IS DRIVE READY ? */ + 0027742, /* JMP DMA YES, SET UP DMA */ + 0013714, /* AND B20 NO, CHECK STATUS BITS */ + 0002002, /* SZA IS DRIVE FAULTY OR HARD DOWN ? */ + 0102030, /* HLT 30B YES, HALT 30B, "RUN" TO TRY AGAIN */ + 0027700, /* JMP START NO, TRY AGAIN FOR DISC READY */ + 0102011, /* ADDR1 OCT 102011 */ + 0102055, /* ADDR2 OCT 102055 */ + 0164000, /* CNT DEC -6144 */ + 0000007, /* D7 OCT 7 */ + 0001400, /* STCMD OCT 1400 */ + 0000020, /* B20 OCT 20 */ + 0017400, /* STMSK OCT 17400 */ + 0000000, /* NOP */ + 0000000, /* NOP */ + 0000000, /* NOP */ + 0000000, /* NOP */ + 0000000, /* NOP */ + 0000000, /* NOP */ + 0000000, /* NOP */ + 0000000, /* NOP */ + 0000000, /* NOP */ + 0000000, /* STAT NOP STATUS CHECK SUBROUTINE */ + 0107710, /* CLC DC,C SET STATUS COMMAND MODE */ + 0063713, /* LDA STCMD GET STATUS COMMAND */ + 0102610, /* OTA DC OUTPUT STATUS COMMAND */ + 0102310, /* SFS DC WAIT FOR STATUS#1 WORD */ + 0027733, /* JMP *-1 */ + 0107510, /* LIB DC,C B-REG = STATUS#1 WORD */ + 0102310, /* SFS DC WAIT FOR STATUS#2 WORD */ + 0027736, /* JMP *-1 */ + 0103510, /* LIA DC,C A-REG = STATUS#2 WORD */ + 0127727, /* JMP STAT,I RETURN */ + 0067776, /* DMA LDB DMACW GET DMA CONTROL WORD */ + 0106606, /* OTB 6 OUTPUT DMA CONTROL WORD */ + 0067707, /* LDB ADDR1 GET MEMORY ADDRESS */ + 0106702, /* CLC 2 SET MEMORY ADDRESS INPUT MODE */ + 0106602, /* OTB 2 OUTPUT MEMORY ADDRESS TO DMA */ + 0102702, /* STC 2 SET WORD COUNT INPUT MODE */ + 0067711, /* LDB CNT GET WORD COUNT */ + 0106602, /* OTB 2 OUTPUT WORD COUNT TO DMA */ + 0106710, /* CLDLD CLC DC SET COMMAND INPUT MODE */ + 0102501, /* LIA 1 LOAD SWITCH */ + 0106501, /* LIB 1 REGISTER SETTINGS */ + 0013712, /* AND D7 ISOLATE HEAD NUMBER */ + 0005750, /* BLF,CLE,SLB BIT 12=0? */ + 0027762, /* JMP *+3 NO,MANUAL BOOT */ + 0002002, /* SZA YES,RPL BOOT. HEAD#=0? */ + 0001000, /* ALS NO,HEAD#1, MAKE HEAD#=2 */ + 0001720, /* ALF,ALS FORM COLD LOAD */ + 0001000, /* ALS COMMAND WORD */ + 0103706, /* STC 6,C ACTIVATE DMA */ + 0103610, /* OTA DC,C OUTPUT COLD LOAD COMMAND */ + 0102310, /* SFS DC IS COLD LOAD COMPLETED ? */ + 0027766, /* JMP *-1 NO, WAIT */ + 0017727, /* JSB STAT YES, GET STATUS */ + 0060001, /* LDA 1 */ + 0013715, /* AND STMSK A-REG = STATUS BITS OF STATUS#1 WD */ + 0002002, /* SZA IS TRANSFER OK ? */ + 0027700, /* JMP START NO,TRY AGAIN */ + 0117710, /* EXIT JSB ADDR2,I YES, EXEC LOADED PROGRAM _@ 2055B */ + 0000010, /* DMACW ABS DC */ + 0170100, /* ABS -START */ }; t_stat ds_boot (int32 unitno, DEVICE *dptr) { -int32 dev; +if (unitno != 0) /* boot supported on drive unit 0 only */ + return SCPE_NOFNC; /* report "Command not allowed" if attempted */ + +if (ibl_copy (ds_rom, ds_dib.select_code)) /* copy the boot ROM to memory and configure */ + return SCPE_IERR; /* return an internal error if failed */ + +SR = SR & (IBL_OPT | IBL_DS_HEAD) /* set S to a reasonable value */ + | IBL_DS | IBL_MAN | (ds_dib.select_code << IBL_V_DEV); /* before boot execution */ -if (unitno != 0) return SCPE_NOFNC; /* only unit 0 */ -dev = ds_dib.select_code; /* get data chan dev */ -if (ibl_copy (ds_rom, dev)) return SCPE_IERR; /* copy boot to memory */ -SR = (SR & (IBL_OPT | IBL_DS_HEAD)) | IBL_DS | IBL_MAN | (dev << IBL_V_DEV); return SCPE_OK; } + + + +/* MAC disc global SCP routines */ + + +/* Load or unload the drive heads. + + The SCP command SET DSn UNLOADED simulates setting the hardware RUN/STOP + switch to STOP. The heads are unloaded, and the drive is spun down. + + The SET DSn LOADED command simulates setting the switch to RUN. The drive is + spun up, and the heads are loaded. + + The library handles command validation and setting the appropriate drive unit + status. +*/ + +t_stat ds_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +const t_bool load = (value != UNIT_UNLOAD); /* true if heads are loading */ + +return dl_load_unload (&mac_cntlr, uptr, load); /* load or unload the heads */ +} + + + +/* MAC disc local utility routines */ + + +/* Start a command. + + The previously prepared command is executed by calling the corresponding + library routine. On entry, the controller's opcode field contains the + command to start, and the buffer contains the command word in element 0 and + the parameters required by the command, if any, beginning in element 1. + + If the command started, the returned pointer will point at the unit to + activate (if that unit's "wait" field is non-zero). If the returned pointer + is NULL, the command failed to start, and the controller status has been set + to indicate the reason. The interface flag is set to notify the CPU of the + failure. + + + Implementation notes: + + 1. If a command that accesses the drive is attempted on a drive currently + seeking, the returned pointer will be valid, but the unit's "wait" time + will be zero. The unit must not be activated (as it already is active). + When the seek completes, the command will be executed automatically. + + If a Seek or Cold Load Read command is attempted on a drive currently + seeking, seek completion will occur normally, but Seek Check status will + be set. + + 2. For debug printouts, we want to print the name of the command (Seek or + Recalibrate) in progress when a new command is started. However, when + the library routine returns, the unit operation and controller opcode + have been changed to reflect the new command. Therefore, we must record + the operation in progress before calling the library. + + The problem is in determining which unit's operation code to record. We + cannot blindly use the unit field from the new command, as recorded in + the controller, as preparation has ensured only that the target unit + number is legal but not necessarily valid. Therefore, we must validate + the unit number before accessing the unit's operation code. + + If the unit number is invalid, the command will not start, but the + compiler does not know this. Therefore, we must ensure that the saved + operation code is initialized, or a "variable used uninitialized" warning + will occur. +*/ + +static void start_command (void) +{ +int32 unit, time; +UNIT *uptr; +CNTLR_OPCODE drive_command; + +unit = GET_S1UNIT (mac_cntlr.spd_unit); /* get the (prepared) unit from the command */ + +if (unit <= DL_MAXDRIVE) /* is the unit number valid? */ + drive_command = (CNTLR_OPCODE) ds_unit [unit].OP; /* get the opcode from the unit that will be used */ +else /* the unit is invalid, so the command will not start */ + drive_command = end; /* but the compiler doesn't know this! */ + +uptr = dl_start_command (&mac_cntlr, ds_unit, DL_MAXDRIVE); /* ask the controller to start the command */ + +if (uptr) { /* did the command start? */ + time = uptr->wait; /* save the activation time */ + + if (time) /* was the unit scheduled? */ + activate_unit (uptr); /* activate it (clears "wait") */ + + if (DEBUG_PRI (ds_dev, DEB_RWSC)) { + unit = uptr - ds_unit; /* get the unit number */ + + if (time == 0) /* was the unit busy? */ + fprintf (sim_deb, ">>DS rwsc: Unit %d %s in progress\n", + unit, dl_opcode_name (MAC, drive_command)); + + fputs (">>DS rwsc: ", sim_deb); + + if (unit > DL_MAXDRIVE) + fputs ("Controller ", sim_deb); + else + fprintf (sim_deb, "Unit %d position %d ", unit, uptr->pos); + + fprintf (sim_deb, "%s command initiated\n", + dl_opcode_name (MAC, mac_cntlr.opcode)); + } + } + +else /* the command failed to start */ + ds_io (&ds_dib, ioENF, 0); /* so set the flag to notify the CPU */ + +return; +} + + +/* Poll the interface for a new command. + + If a new command is available, and the controller is not busy, prepare the + command for execution. If preparation succeeded, and the command needs + parameters before executing, set the flag to request the first one from the + CPU. If no parameters are needed, the command is ready to execute. + + If preparation failed, set the flag to notify the CPU. The controller + status contains the reason for the failure. +*/ + +static void poll_interface (void) +{ +if (ds.cmrdy == SET && mac_cntlr.state != cntlr_busy) { /* are the interface and controller ready? */ + buffer [0] = fifo_unload (); /* unload the command into the buffer */ + + if (dl_prepare_command (&mac_cntlr, ds_unit, DL_MAXDRIVE)) { /* prepare the command; did it succeed? */ + if (mac_cntlr.length) /* does the command require parameters? */ + ds_io (&ds_dib, ioENF, 0); /* set the flag to request the first one */ + else /* if not waiting for parameters */ + start_command (); /* start the command */ + } + + else /* preparation failed */ + ds_io (&ds_dib, ioENF, 0); /* so set the flag to notify the CPU */ + + ds.cmrdy = CLEAR; /* flush the command from the interface */ + } + +return; +} + + +/* Poll the drives for attention requests. + + If the controller is idle and interrupts are allowed, the drives are polled + to see if any drive is requesting attention. If one is found, the controller + resets that drive's Attention status, saves the drive's unit number, sets + Drive Attention status, and waits for a command from the CPU. The interface + sets the flag to notify the CPU. +*/ + +void poll_drives (void) +{ +if (mac_cntlr.state == cntlr_idle && ds.control == SET) /* controller is idle and OK to interrupt? */ + if (dl_poll_drives (&mac_cntlr, ds_unit, DL_MAXDRIVE)) /* poll drives; was Attention seen? */ + ds_io (&ds_dib, ioENF, 0); /* request an interrupt */ +return; +} + + +/* Load a word into the FIFO. + + A word is loaded into the next available location in the FIFO, and the FIFO + occupancy count is incremented. If the FIFO is full on entry, the load is + ignored. + + + Implementation notes: + + 1. The FIFO is implemented as circular queue to take advantage of REG_CIRC + EXAMINE semantics. REG->qptr is the index of the first word currently in + the FIFO. By specifying REG_CIRC, examining FIFO[0-n] will always + display the words in load order, regardless of the actual array index of + the start of the list. The number of words currently present in the FIFO + is kept in fifo_count (0 = empty, 1-16 = number of words available). + + If fifo_count < FIFO_SIZE, (REG->qptr + fifo_count) mod FIFO_SIZE is the + index of the new word location. Loading stores the word there and then + increments fifo_count. + + 2. Because the load and unload routines need access to qptr in the REG + structure for the FIFO array, a pointer to the REG is stored in the + fifo_reg variable during device reset. +*/ + +static void fifo_load (uint16 data) +{ +uint32 index; + +if (FIFO_FULL) { /* is the FIFO already full? */ + if (DEBUG_PRI (ds_dev, DEB_BUF)) + fprintf (sim_deb, ">>DS buf: Attempted load to full FIFO, data %06o\n", data); + + return; /* return with the load ignored */ + } + +index = (ds.fifo_reg->qptr + ds.fifo_count) % FIFO_SIZE; /* calculate the index of the next available location */ + +ds.fifo [index] = data; /* store the word in the FIFO */ +ds.fifo_count = ds.fifo_count + 1; /* increment the count of words stored */ + +if (DEBUG_PRI (ds_dev, DEB_BUF)) + fprintf (sim_deb, ">>DS buf: Data %06o loaded into FIFO (%d)\n", + data, ds.fifo_count); + +return; +} + + +/* Unload a word from the FIFO. + + A word is unloaded from the first location in the FIFO, and the FIFO + occupancy count is decremented. If the FIFO is empty on entry, the unload + returns dummy data. + + + Implementation notes: + + 1. If fifo_count > 0, REG->qptr is the index of the word to remove. Removal + gets the word and then increments qptr (mod FIFO_SIZE) and decrements + fifo_count. +*/ + +static uint16 fifo_unload (void) +{ +uint16 data; + +if (FIFO_EMPTY) { /* is the FIFO already empty? */ + if (DEBUG_PRI (ds_dev, DEB_BUF)) + fputs (">>DS buf: Attempted unload from empty FIFO\n", sim_deb); + + return 0; /* return with no data */ + } + +data = ds.fifo [ds.fifo_reg->qptr]; /* get the word from the FIFO */ + +ds.fifo_reg->qptr = (ds.fifo_reg->qptr + 1) % FIFO_SIZE; /* update the FIFO queue pointer */ +ds.fifo_count = ds.fifo_count - 1; /* decrement the count of words stored */ + +if (DEBUG_PRI (ds_dev, DEB_BUF)) + fprintf (sim_deb, ">>DS buf: Data %06o unloaded from FIFO (%d)\n", + data, ds.fifo_count); + +return data; +} + + +/* Clear the FIFO. + + The FIFO is cleared by setting the occupancy counter to zero. +*/ + +static void fifo_clear (void) +{ +ds.fifo_count = 0; /* clear the FIFO */ + +if (DEBUG_PRI (ds_dev, DEB_BUF)) + fputs (">>DS buf: FIFO cleared\n", sim_deb); + +return; +} + + +/* Activate the unit. + + The specified unit is activated using the unit's "wait" time. If debugging + is enabled, the activation is logged to the debug file. +*/ + +static t_stat activate_unit (UNIT *uptr) +{ +uint32 unit; +t_stat result; + +if (DEBUG_PRI (ds_dev, DEB_SERV)) { + unit = uptr - ds_unit; /* calculate the unit number */ + + if (uptr == &ds_cntlr) + fprintf (sim_deb, ">>DS serv: Controller delay %d service scheduled\n", + uptr->wait); + else + fprintf (sim_deb, ">>DS serv: Unit %d delay %d service scheduled\n", + unit, uptr->wait); + } + +result = sim_activate (uptr, uptr->wait); /* activate the unit */ +uptr->wait = 0; /* reset the activation time */ + +return result; /* return the activation status */ +} diff --git a/HP2100/hp2100_fp1.c b/HP2100/hp2100_fp1.c index cf078435..94ce633a 100644 --- a/HP2100/hp2100_fp1.c +++ b/HP2100/hp2100_fp1.c @@ -1,6 +1,6 @@ /* hp2100_fp1.c: HP 1000 multiple-precision floating point routines - Copyright (c) 2005-2011, J. David Bryan + Copyright (c) 2005-2012, J. David Bryan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. + 06-Feb-12 JDB Added missing precision on constant "one" in fp_trun 21-Jun-11 JDB Completed the comments for divide; no code changes 08-Jun-08 JDB Quieted bogus gcc warning in fp_exec 10-May-08 JDB Fixed uninitialized return in fp_accum when setting @@ -1421,7 +1422,7 @@ uint32 fp_trun (OP *result, OP source, OPSIZE precision) { t_bool bits_lost; FPU unpacked; -FPU one = { FP_ONEHALF, 1, 0 }; /* 0.5 * 2 ** 1 = 1.0 */ +FPU one = { FP_ONEHALF, 1, fp_t }; /* 0.5 * 2 ** 1 = 1.0 */ OP zero = { { 0, 0, 0, 0, 0 } }; /* 0.0 */ t_uint64 mask = mant_mask[precision] & ~FP_MSIGN; diff --git a/HP2100/hp2100_ipl.c b/HP2100/hp2100_ipl.c index 14811d01..595d47e9 100644 --- a/HP2100/hp2100_ipl.c +++ b/HP2100/hp2100_ipl.c @@ -1,6 +1,6 @@ /* hp2100_ipl.c: HP 2000 interprocessor link simulator - Copyright (c) 2002-2011, Robert M Supnik + Copyright (c) 2002-2012, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,8 @@ IPLI, IPLO 12875A interprocessor link + 10-Feb-12 JDB Deprecated DEVNO in favor of SC + Added CARD_INDEX casts to dib.card_index 07-Apr-11 JDB A failed STC may now be retried 28-Mar-11 JDB Tidied up signal handling 27-Mar-11 JDB Consolidated reporting of consecutive CRS signals @@ -165,6 +167,7 @@ REG ipli_reg [] = { { ORDATA (HOLD, ipl [ipli].hold, 8) }, { DRDATA (TIME, ipl_ptime, 24), PV_LEFT }, { FLDATA (STOP_IOE, ipl_stopioe, 0) }, + { ORDATA (SC, ipli_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, ipli_dib.select_code, 6), REG_HRO }, { NULL } }; @@ -174,8 +177,8 @@ MTAB ipl_mod [] = { { UNIT_DIAG, 0, "link mode", "LINK", &ipl_setdiag }, { MTAB_XTD | MTAB_VDV, 0, NULL, "DISCONNECT", &ipl_dscln, NULL, NULL }, - { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &ipli_dev }, + { MTAB_XTD | MTAB_VDV, 1, "SC", "SC", &hp_setsc, &hp_showsc, &ipli_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &ipli_dev }, { 0 } }; @@ -203,6 +206,7 @@ REG iplo_reg [] = { { FLDATA (FBF, ipl [iplo].flagbuf, 0) }, { ORDATA (HOLD, ipl [iplo].hold, 8) }, { DRDATA (TIME, ipl_ptime, 24), PV_LEFT }, + { ORDATA (SC, iplo_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, iplo_dib.select_code, 6), REG_HRO }, { NULL } }; @@ -291,7 +295,7 @@ DEVICE iplo_dev = { uint32 iplio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data) { -const CARD_INDEX card = dibptr->card_index; /* set card selector */ +CARD_INDEX card = (CARD_INDEX) dibptr->card_index; /* set card selector */ UNIT *const uptr = &(ipl_unit [card]); /* associated unit pointer */ const char *iotype [] = { "Status", "Command" }; int32 sta; @@ -474,7 +478,7 @@ if (nb < 0) { /* connection closed? */ else if (nb == 0) /* no data? */ return SCPE_OK; -card = (uptr == &iplo_unit); /* set card selector */ +card = (CARD_INDEX) (uptr == &iplo_unit); /* set card selector */ if (uptr->flags & UNIT_HOLD) { /* holdover byte? */ uptr->IBUF = (ipl [card].hold << 8) | (((int32) msg [0]) & 0377); @@ -537,7 +541,7 @@ t_stat ipl_reset (DEVICE *dptr) { UNIT *uptr = dptr->units; DIB *dibptr = (DIB *) dptr->ctxt; /* DIB pointer */ -CARD_INDEX card = dibptr->card_index; /* card number */ +CARD_INDEX card = (CARD_INDEX) dibptr->card_index; /* card number */ hp_enbdis_pair (dptr, dptrs [card ^ 1]); /* make pair cons */ diff --git a/HP2100/hp2100_lps.c b/HP2100/hp2100_lps.c index 165c2190..ea51c6fb 100644 --- a/HP2100/hp2100_lps.c +++ b/HP2100/hp2100_lps.c @@ -1,6 +1,6 @@ /* hp2100_lps.c: HP 2100 12653A/2767 line printer simulator - Copyright (c) 1993-2011, Robert M. Supnik + Copyright (c) 1993-2012, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -26,6 +26,7 @@ LPS 12653A 2767 line printer 12566B microcircuit interface with loopback diagnostic connector + 10-Feb-12 JDB Deprecated DEVNO in favor of SC 28-Mar-11 JDB Tidied up signal handling 26-Oct-10 JDB Changed I/O signal handler for revised signal model Revised detection of CLC at last DMA cycle @@ -245,6 +246,7 @@ REG lps_reg[] = { { DRDATA (RTIME, lps_rtime, 24), PV_LEFT }, { FLDATA (TIMING, lps_timing, 0), REG_HRO }, { FLDATA (STOP_IOE, lps_stopioe, 0) }, + { ORDATA (SC, lps_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, lps_dib.select_code, 6), REG_HRO }, { NULL } }; @@ -262,8 +264,8 @@ MTAB lps_mod[] = { &lps_set_timing, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TIMING", NULL, NULL, &lps_show_timing, NULL }, - { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &lps_dev }, + { MTAB_XTD | MTAB_VDV, 0, "SC", "SC", &hp_setsc, &hp_showsc, &lps_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &lps_dev }, { 0 } }; diff --git a/HP2100/hp2100_lpt.c b/HP2100/hp2100_lpt.c index 6122841d..84795443 100644 --- a/HP2100/hp2100_lpt.c +++ b/HP2100/hp2100_lpt.c @@ -1,6 +1,6 @@ /* hp2100_lpt.c: HP 2100 12845B line printer simulator - Copyright (c) 1993-2011, Robert M. Supnik + Copyright (c) 1993-2012, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,7 @@ LPT 12845B 2607 line printer + 10-Feb-12 JDB Deprecated DEVNO in favor of SC 28-Mar-11 JDB Tidied up signal handling 26-Oct-10 JDB Changed I/O signal handler for revised signal model 26-Jun-08 JDB Rewrote device I/O to model backplane signals @@ -137,6 +138,7 @@ REG lpt_reg[] = { { DRDATA (CTIME, lpt_ctime, 24), PV_LEFT }, { DRDATA (PTIME, lpt_ptime, 24), PV_LEFT }, { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { ORDATA (SC, lpt_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, lpt_dib.select_code, 6), REG_HRO }, { NULL } }; @@ -146,8 +148,8 @@ MTAB lpt_mod[] = { { UNIT_POWEROFF, 0, "power on", "POWERON", lpt_restart }, { UNIT_OFFLINE, UNIT_OFFLINE, "offline", "OFFLINE", NULL }, { UNIT_OFFLINE, 0, "online", "ONLINE", lpt_restart }, - { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &lpt_dev }, + { MTAB_XTD | MTAB_VDV, 0, "SC", "SC", &hp_setsc, &hp_showsc, &lpt_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &lpt_dev }, { 0 } }; diff --git a/HP2100/hp2100_mpx.c b/HP2100/hp2100_mpx.c index ab51adf0..65aa65c2 100644 --- a/HP2100/hp2100_mpx.c +++ b/HP2100/hp2100_mpx.c @@ -1,6 +1,6 @@ /* hp2100_mpx.c: HP 12792C eight-channel asynchronous multiplexer simulator - Copyright (c) 2008-2011, J. David Bryan + Copyright (c) 2008-2012, J. David Bryan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,7 @@ MPX 12792C 8-channel multiplexer card + 10-Feb-12 JDB Deprecated DEVNO in favor of SC 28-Mar-11 JDB Tidied up signal handling 26-Oct-10 JDB Changed I/O signal handler for revised signal model 25-Nov-08 JDB Revised for new multiplexer library SHOW routines @@ -105,9 +106,9 @@ The simulation provides both the "realistic timing" described above, as well as an optimized "fast timing" option. Optimization makes three improvements: - 1. Buffered characters are transferred via Telnet in blocks. + 1. Buffered characters are transferred in blocks. - 2. ENQ/ACK handshaking is done locally without involving the Telnet client. + 2. ENQ/ACK handshaking is done locally without involving the client. 3. BS and DEL respond visually more like prior RTE terminal drivers. @@ -599,15 +600,15 @@ t_stat mpx_show_frev (FILE *st, UNIT *uptr, int32 val, void *desc); /* MPX data structures. - mpx_order MPX line connection order table - mpx_ldsc MPX line descriptors - mpx_desc MPX multiplexer descriptor - mpx_dib MPX device information block - mpx_unit MPX unit list - mpx_reg MPX register list - mpx_mod MPX modifier list - mpx_deb MPX debug list - mpx_dev MPX device descriptor + mpx_order MPX line connection order table + mpx_ldsc MPX terminal multiplexer line descriptors + mpx_desc MPX terminal multiplexer device descriptor + mpx_dib MPX device information block + mpx_unit MPX unit list + mpx_reg MPX register list + mpx_mod MPX modifier list + mpx_deb MPX debug list + mpx_dev MPX device descriptor The first eight units correspond to the eight multiplexer line ports. These handle character I/O via the Telnet library. A ninth unit acts as the card @@ -626,19 +627,17 @@ t_stat mpx_show_frev (FILE *st, UNIT *uptr, int32 val, void *desc); ten millisecond period. The controller and poll units are hidden by disabling them, so as to present - a logical picture of the multiplexer to the user. However, we cannot attach - to a disabled unit, so the poll unit is enabled prior to attaching and - disabled thereafter. + a logical picture of the multiplexer to the user. */ +DEVICE mpx_dev; + int32 mpx_order [MPX_PORTS] = { -1 }; /* connection order */ TMLN mpx_ldsc [MPX_PORTS] = { { 0 } }; /* line descriptors */ TMXR mpx_desc = { MPX_PORTS, 0, 0, mpx_ldsc, mpx_order }; /* device descriptor */ DIB mpx_dib = { &mpx_io, MPX }; -DEVICE mpx_dev; - UNIT mpx_unit [] = { { UDATA (&mpx_line_svc, UNIT_FASTTIME, 0) }, /* terminal I/O line 0 */ { UDATA (&mpx_line_svc, UNIT_FASTTIME, 0) }, /* terminal I/O line 1 */ @@ -685,9 +684,10 @@ REG mpx_reg [] = { { BRDATA (SEP, mpx_sep, 10, 10, MPX_PORTS * 2) }, { BRDATA (PUT, mpx_put, 10, 10, MPX_PORTS * 2) }, - { FLDATA (CTL, mpx.control, 0) }, - { FLDATA (FLG, mpx.flag, 0) }, - { FLDATA (FBF, mpx.flagbuf, 0) }, + { FLDATA (CTL, mpx.control, 0) }, + { FLDATA (FLG, mpx.flag, 0) }, + { FLDATA (FBF, mpx.flagbuf, 0) }, + { ORDATA (SC, mpx_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, mpx_dib.select_code, 6), REG_HRO }, { BRDATA (CONNORD, mpx_order, 10, 32, MPX_PORTS), REG_HRO }, @@ -711,7 +711,8 @@ MTAB mpx_mod [] = { { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, &mpx_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, &mpx_desc }, { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, &mpx_desc }, - { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &mpx_dev }, + { MTAB_XTD | MTAB_VDV, 0, "SC", "SC", &hp_setsc, &hp_showsc, &mpx_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &mpx_dev }, { 0 } }; @@ -742,7 +743,7 @@ DEVICE mpx_dev = { &mpx_attach, /* attach routine */ &mpx_detach, /* detach routine */ &mpx_dib, /* device information block */ - DEV_NET | DEV_DEBUG | DEV_DISABLE, /* device flags */ + DEV_DEBUG | DEV_DISABLE, /* device flags */ 0, /* debug control flags */ mpx_deb, /* debug flag name table */ NULL, /* memory size change routine */ @@ -2052,7 +2053,7 @@ return SCPE_OK; the first serial line and be reported there in a SHOW MPX command. To preserve the logical picture, we attach the port to the Telnet poll unit, - which is normally disabled, inhibiting its display. Attaching to a disabled + which is normally disabled to inhibit its display. Attaching to a disabled unit is not allowed, so we first enable the unit, then attach it, then disable it again. Attachment is reported by the "mpx_status" routine below. @@ -2073,7 +2074,7 @@ mpx_poll.flags = mpx_poll.flags | UNIT_DIS; /* disable unit */ if (status == SCPE_OK) { mpx_poll.wait = POLL_FIRST; /* set up poll */ - sim_activate (&mpx_poll, mpx_poll.wait); /* start Telnet poll immediately */ + sim_activate (&mpx_poll, mpx_poll.wait); /* start poll immediately */ } return status; } @@ -2229,9 +2230,9 @@ return; /* Calculate service time from baud rate. - Service times are based on 1580 instructions per second, which is the 1000 - E-Series execution speed. Baud rate 0 means "don't change" and is handled by - the "Set port key" command executor. + Service times are based on 1580 instructions per millisecond, which is the + 1000 E-Series execution speed. Baud rate 0 means "don't change" and is + handled by the "Set port key" command executor. Baud rate settings of 13-15 are marked as "reserved" in the user manual, but the firmware defines these as 38400, 9600, and 9600 baud, respectively. @@ -2239,9 +2240,8 @@ return; static uint32 service_time (uint16 control_word) { -/* Baud Rates 0- 7 : --, 50, 75, 110, 134.5, 150, 300, 1200, - Baud Rates 8-15 : 1800, 2400, 4800, 9600, 19200, 38400, 9600, 9600 -*/ +/* Baud Rates 0- 7 : --, 50, 75, 110, 134.5, 150, 300, 1200, */ +/* Baud Rates 8-15 : 1800, 2400, 4800, 9600, 19200, 38400, 9600, 9600 */ static const int32 ticks [] = { 0, 316000, 210667, 143636, 117472, 105333, 52667, 13167, 8778, 6583, 3292, 1646, 823, 411, 1646, 1646 }; diff --git a/HP2100/hp2100_ms.c b/HP2100/hp2100_ms.c index d9f44416..bc3acca6 100644 --- a/HP2100/hp2100_ms.c +++ b/HP2100/hp2100_ms.c @@ -1,6 +1,6 @@ /* hp2100_ms.c: HP 2100 13181A/13183A magnetic tape simulator - Copyright (c) 1993-2011, Robert M. Supnik + Copyright (c) 1993-2012, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -26,6 +26,8 @@ MS 13181A 7970B 800bpi nine track magnetic tape 13183A 7970E 1600bpi nine track magnetic tape + 10-Feb-12 JDB Deprecated DEVNO in favor of SC + Added CNTLR_TYPE cast to ms_settype 28-Mar-11 JDB Tidied up signal handling 26-Oct-10 JDB Changed I/O signal handler for revised signal model 11-Aug-08 JDB Revised to use AR instead of saved_AR in boot @@ -151,7 +153,14 @@ #define STA_DYN (STA_PE | STA_SEL | STA_TBSY | STA_BOT | \ STA_EOT | STA_WLK | STA_LOCAL) -enum { A13181, A13183 } ms_ctype = A13181; /* ctrl type */ +/* Controller types */ + +typedef enum { + A13181, + A13183 + } CNTLR_TYPE; + +CNTLR_TYPE ms_ctype = A13181; /* ctrl type */ int32 ms_timing = 1; /* timing type */ struct { @@ -260,13 +269,14 @@ REG msd_reg[] = { { BRDATA (DBUF, msxb, 8, 8, DBSIZE) }, { DRDATA (BPTR, ms_ptr, DB_N_SIZE + 1) }, { DRDATA (BMAX, ms_max, DB_N_SIZE + 1) }, + { ORDATA (SC, msd_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, msd_dib.select_code, 6), REG_HRO }, { NULL } }; MTAB msd_mod[] = { - { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &msd_dev }, + { MTAB_XTD | MTAB_VDV, 1, "SC", "SC", &hp_setsc, &hp_showsc, &msd_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &msd_dev }, { 0 } }; @@ -319,6 +329,7 @@ REG msc_reg[] = { { FLDATA (TIMING, ms_timing, 0), REG_HRO }, { FLDATA (STOP_IOE, msc_stopioe, 0) }, { FLDATA (CTYPE, ms_ctype, 0), REG_HRO }, + { ORDATA (SC, msc_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, msc_dib.select_code, 6), REG_HRO }, { NULL } }; @@ -346,8 +357,8 @@ MTAB msc_mod[] = { &ms_set_timing, NULL, NULL }, { MTAB_XTD | MTAB_VDV, 0, "TIMING", NULL, NULL, &ms_show_timing, NULL }, - { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &msd_dev }, + { MTAB_XTD | MTAB_VDV, 1, "SC", "SC", &hp_setsc, &hp_showsc, &msd_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &msd_dev }, { 0 } }; @@ -1079,7 +1090,7 @@ if ((val < 0) || (val > 1) || (cptr != NULL)) return SCPE_ARG; for (i = 0; i < MS_NUMDR; i++) { if (msc_unit[i].flags & UNIT_ATT) return SCPE_ALATT; } -ms_ctype = val; +ms_ctype = (CNTLR_TYPE) val; ms_config_timing (); /* update for new type */ return SCPE_OK; } diff --git a/HP2100/hp2100_mt.c b/HP2100/hp2100_mt.c index 32c0f072..144db5de 100644 --- a/HP2100/hp2100_mt.c +++ b/HP2100/hp2100_mt.c @@ -1,6 +1,6 @@ /* hp2100_mt.c: HP 2100 12559A magnetic tape simulator - Copyright (c) 1993-2011, Robert M. Supnik + Copyright (c) 1993-2012, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,8 +25,9 @@ MT 12559A 3030 nine track magnetic tape + 10-Feb-12 JDB Deprecated DEVNO in favor of SC 28-Mar-11 JDB Tidied up signal handling - 29-Oct-10 JDB Fixed error in command scan in mtcio ioIOO handler + 29-Oct-10 JDB Fixed command scanning error in mtcio ioIOO handler 26-Oct-10 JDB Changed I/O signal handler for revised signal model 04-Sep-08 JDB Fixed missing flag after CLR command 02-Sep-08 JDB Moved write enable and format commands from MTD to MTC @@ -168,13 +169,14 @@ REG mtd_reg[] = { { BRDATA (DBUF, mtxb, 8, 8, DBSIZE) }, { DRDATA (BPTR, mt_ptr, DB_V_SIZE + 1) }, { DRDATA (BMAX, mt_max, DB_V_SIZE + 1) }, + { ORDATA (SC, mtd_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, mtd_dib.select_code, 6), REG_HRO }, { NULL } }; MTAB mtd_mod[] = { - { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &mtd_dev }, + { MTAB_XTD | MTAB_VDV, 1, "SC", "SC", &hp_setsc, &hp_showsc, &mtd_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &mtd_dev }, { 0 } }; @@ -210,6 +212,7 @@ REG mtc_reg[] = { { DRDATA (GTIME, mtc_gtime, 24), REG_NZ + PV_LEFT }, { DRDATA (XTIME, mtc_xtime, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, mtc_stopioe, 0) }, + { ORDATA (SC, mtc_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, mtc_dib.select_code, 6), REG_HRO }, { NULL } }; @@ -219,8 +222,8 @@ MTAB mtc_mod[] = { { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, { MTAB_XTD | MTAB_VDV | MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, - { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &mtd_dev }, + { MTAB_XTD | MTAB_VDV, 1, "SC", "SC", &hp_setsc, &hp_showsc, &mtd_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &mtd_dev }, { 0 } }; diff --git a/HP2100/hp2100_mux.c b/HP2100/hp2100_mux.c index 2712f1c4..f341a51d 100644 --- a/HP2100/hp2100_mux.c +++ b/HP2100/hp2100_mux.c @@ -1,6 +1,6 @@ /* hp2100_mux.c: HP 2100 12920A terminal multiplexor simulator - Copyright (c) 2002-2011, Robert M Supnik + Copyright (c) 2002-2012, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,7 @@ MUX,MUXL,MUXM 12920A terminal multiplexor + 10-Feb-12 JDB Deprecated DEVNO in favor of SC 28-Mar-11 JDB Tidied up signal handling 26-Oct-10 JDB Changed I/O signal handler for revised signal model 25-Nov-08 JDB Revised for new multiplexer library SHOW routines @@ -329,17 +330,12 @@ t_stat mux_detach (UNIT *uptr); t_stat mux_setdiag (UNIT *uptr, int32 val, char *cptr, void *desc); -/* MUX data structures. +/* MUXL/MUXU device information block. - mux_order MUX line connection order table - mux_ldsc MUX line descriptors - mux_desc MUX multiplexer descriptor + The DIBs of adjacent cards must be contained in an array, so they are defined + here and referenced in the lower and upper card device structures. */ -int32 mux_order [MUX_LINES] = { -1 }; /* connection order */ -TMLN mux_ldsc [MUX_LINES] = { { 0 } }; /* line descriptors */ -TMXR mux_desc = { MUX_LINES, 0, 0, mux_ldsc, mux_order }; /* device descriptor */ - DIB mux_dib[] = { { &muxlio, MUXL }, { &muxuio, MUXU } @@ -358,6 +354,8 @@ DIB mux_dib[] = { muxl_dev MUXL device descriptor */ +TMXR mux_desc; + DEVICE muxl_dev; UNIT muxl_unit[] = { @@ -393,6 +391,7 @@ REG muxl_reg[] = { { BRDATA (BDFR, mux_defer, 8, 1, MUX_LINES) }, { URDATA (TIME, muxl_unit[0].wait, 10, 24, 0, MUX_LINES, REG_NZ + PV_LEFT) }, + { ORDATA (SC, muxl_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, muxl_dib.select_code, 6), REG_HRO }, { NULL } }; @@ -409,8 +408,9 @@ MTAB muxl_mod[] = { { MTAB_XTD | MTAB_VUN | MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &mux_desc }, { MTAB_XTD | MTAB_VUN | MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &mux_desc }, - { MTAB_XTD | MTAB_VUN, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &mux_desc }, - { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &muxl_dev }, + { MTAB_XTD | MTAB_VUN, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &mux_desc }, + { MTAB_XTD | MTAB_VDV, 1, "SC", "SC", &hp_setsc, &hp_showsc, &muxl_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &muxl_dev }, { 0 } }; @@ -442,6 +442,10 @@ DEVICE muxl_dev = { /* MUXU data structures + mux_order MUX line connection order table + mux_ldsc MUX terminal multiplexer line descriptors + mux_desc MUX terminal multiplexer device descriptor + muxu_dib MUXU device information block muxu_unit MUXU unit list muxu_reg MUXU register list @@ -452,11 +456,16 @@ DEVICE muxl_dev = { DEVICE muxu_dev; +int32 mux_order [MUX_LINES] = { -1 }; /* connection order */ +TMLN mux_ldsc [MUX_LINES] = { { 0 } }; /* line descriptors */ +TMXR mux_desc = { MUX_LINES, 0, 0, mux_ldsc, mux_order }; /* device descriptor */ + UNIT muxu_unit = { UDATA (&muxi_svc, UNIT_ATTABLE, 0), POLL_FIRST }; REG muxu_reg[] = { { ORDATA (IBUF, muxu_ibuf, 16) }, { ORDATA (OBUF, muxu_obuf, 16) }, + { ORDATA (SC, muxu_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, muxu_dib.select_code, 6), REG_HRO }, { NULL } }; @@ -471,7 +480,8 @@ MTAB muxu_mod[] = { { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, &mux_desc }, { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, &mux_desc }, { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, &mux_desc }, - { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &muxl_dev }, + { MTAB_XTD | MTAB_VDV, 1, "SC", "SC", &hp_setsc, &hp_showsc, &muxl_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &muxl_dev }, { 0 } }; @@ -501,7 +511,7 @@ DEVICE muxu_dev = { &mux_attach, /* attach routine */ &mux_detach, /* detach routine */ &muxu_dib, /* device information block */ - DEV_NET | DEV_DISABLE | DEV_DEBUG, /* device flags */ + DEV_DISABLE | DEV_DEBUG, /* device flags */ 0, /* debug control flags */ muxu_deb, /* debug flag name table */ NULL, /* memory size change routine */ @@ -531,12 +541,14 @@ REG muxc_reg[] = { { ORDATA (CHAN, muxc_chan, 4) }, { BRDATA (DSO, muxc_ota, 8, 6, MUX_LINES) }, { BRDATA (DSI, muxc_lia, 8, 2, MUX_LINES) }, + { ORDATA (SC, muxc_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, muxc_dib.select_code, 6), REG_HRO }, { NULL } }; MTAB muxc_mod[] = { - { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &muxc_dev }, + { MTAB_XTD | MTAB_VDV, 0, "SC", "SC", &hp_setsc, &hp_showsc, &muxc_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &muxc_dev }, { 0 } }; @@ -975,8 +987,6 @@ t_bool loopback; loopback = ((muxu_unit.flags & UNIT_DIAG) != 0); /* diagnostic mode? */ if (!loopback) { /* terminal mode? */ - if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ - if (uptr->wait == POLL_FIRST) /* first poll? */ uptr->wait = sync_poll (INITIAL); /* initial synchronization */ else /* not first */ @@ -1328,7 +1338,7 @@ if (status == SCPE_OK) { sim_activate (&muxu_unit, muxu_unit.wait); /* start Telnet poll immediately */ } -return SCPE_OK; +return status; } diff --git a/HP2100/hp2100_pif.c b/HP2100/hp2100_pif.c index 494f5860..ceaaeb8c 100644 --- a/HP2100/hp2100_pif.c +++ b/HP2100/hp2100_pif.c @@ -1,6 +1,6 @@ /* hp2100_pif.c: HP 12620A/12936A privileged interrupt fence simulator - Copyright (c) 2008-2011, J. David Bryan + Copyright (c) 2008-2012, J. David Bryan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,7 @@ PIF 12620A/12936A privileged interrupt fence + 10-Feb-12 JDB Deprecated DEVNO in favor of SC 28-Mar-11 JDB Tidied up signal handling 26-Oct-10 JDB Changed I/O signal handler for revised signal model 26-Jun-08 JDB Rewrote device I/O to model backplane signals @@ -147,18 +148,20 @@ UNIT pif_unit = { }; REG pif_reg [] = { - { FLDATA (CTL, pif.control, 0) }, - { FLDATA (FLG, pif.flag, 0) }, - { FLDATA (FBF, pif.flagbuf, 0) }, + { FLDATA (CTL, pif.control, 0) }, + { FLDATA (FLG, pif.flag, 0) }, + { FLDATA (FBF, pif.flagbuf, 0) }, + { ORDATA (SC, pif_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, pif_dib.select_code, 6), REG_HRO }, { NULL } }; MTAB pif_mod [] = { - { MTAB_XTD | MTAB_VDV, 0, NULL, "12620A", &pif_set_card, NULL, NULL }, - { MTAB_XTD | MTAB_VDV, 1, NULL, "12936A", &pif_set_card, NULL, NULL }, - { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, NULL, &pif_show_card, NULL }, - { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &pif_dev }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "12620A", &pif_set_card, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "12936A", &pif_set_card, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, NULL, &pif_show_card, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "SC", "SC", &hp_setsc, &hp_showsc, &pif_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &pif_dev }, { 0 } }; diff --git a/HP2100/hp2100_stddev.c b/HP2100/hp2100_stddev.c index ebc8c04f..82740889 100644 --- a/HP2100/hp2100_stddev.c +++ b/HP2100/hp2100_stddev.c @@ -1,6 +1,6 @@ /* hp2100_stddev.c: HP2100 standard devices simulator - Copyright (c) 1993-2011, Robert M. Supnik + Copyright (c) 1993-2012, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -28,6 +28,8 @@ TTY 12531C buffered teleprinter interface CLK 12539C time base generator + 12-Feb-12 JDB Add TBG as a logical name for the CLK device + 10-Feb-12 JDB Deprecated DEVNO in favor of SC 28-Mar-11 JDB Tidied up signal handling 26-Oct-10 JDB Changed I/O signal handler for revised signal model 26-Jun-08 JDB Rewrote device I/O to model backplane signals @@ -228,6 +230,7 @@ REG ptr_reg[] = { { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { ORDATA (SC, ptr_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, ptr_dib.select_code, 6), REG_HRO }, { NULL } }; @@ -235,8 +238,8 @@ REG ptr_reg[] = { MTAB ptr_mod[] = { { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL }, { UNIT_DIAG, 0, "reader mode", "READER", NULL }, - { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &ptr_dev }, + { MTAB_XTD | MTAB_VDV, 0, "SC", "SC", &hp_setsc, &hp_showsc, &ptr_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &ptr_dev }, { 0 } }; @@ -270,13 +273,14 @@ REG ptp_reg[] = { { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { ORDATA (SC, ptp_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, ptp_dib.select_code, 6), REG_HRO }, { NULL } }; MTAB ptp_mod[] = { - { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &ptp_dev }, + { MTAB_XTD | MTAB_VDV, 0, "SC", "SC", &hp_setsc, &hp_showsc, &ptp_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &ptp_dev }, { 0 } }; @@ -322,6 +326,7 @@ REG tty_reg[] = { { DRDATA (TTIME, tty_unit[TTO].wait, 24), REG_NZ + PV_LEFT }, { DRDATA (PPOS, tty_unit[TTP].pos, T_ADDR_W), PV_LEFT }, { FLDATA (STOP_IOE, ttp_stopioe, 0) }, + { ORDATA (SC, tty_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, tty_dib.select_code, 6), REG_HRO }, { NULL } }; @@ -333,8 +338,8 @@ MTAB tty_mod[] = { { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_opt }, { UNIT_AUTOLF, UNIT_AUTOLF, "autolf", "AUTOLF", &tty_set_alf }, { UNIT_AUTOLF, 0 , NULL, "NOAUTOLF", &tty_set_alf }, - { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &tty_dev }, + { MTAB_XTD | MTAB_VDV, 0, "SC", "SC", &hp_setsc, &hp_showsc, &tty_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &tty_dev }, { 0 } }; @@ -367,6 +372,7 @@ REG clk_reg[] = { { FLDATA (ERR, clk_error, CLK_V_ERROR) }, { BRDATA (TIME, clk_time, 10, 24, 8) }, { DRDATA (IPTICK, clk_tick, 24), PV_RSPC | REG_RO }, + { ORDATA (SC, clk_dib.select_code, 6), REG_HRO }, { ORDATA (DEVNO, clk_dib.select_code, 6), REG_HRO }, { NULL } }; @@ -374,8 +380,8 @@ REG clk_reg[] = { MTAB clk_mod[] = { { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL }, { UNIT_DIAG, 0, "calibrated", "CALIBRATED", NULL }, - { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", - &hp_setdev, &hp_showdev, &clk_dev }, + { MTAB_XTD | MTAB_VDV, 0, "SC", "SC", &hp_setsc, &hp_showsc, &clk_dev }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &clk_dev }, { 0 } }; @@ -384,7 +390,8 @@ DEVICE clk_dev = { 1, 0, 0, 0, 0, 0, NULL, NULL, &clk_reset, NULL, NULL, NULL, - &clk_dib, DEV_DISABLE + &clk_dib, DEV_DISABLE, + 0, NULL, NULL, "TBG" }; diff --git a/HP2100/hp2100_sys.c b/HP2100/hp2100_sys.c index 044c2825..c6cd201c 100644 --- a/HP2100/hp2100_sys.c +++ b/HP2100/hp2100_sys.c @@ -1,6 +1,6 @@ /* hp2100_sys.c: HP 2100 simulator interface - Copyright (c) 1993-2010, Robert M. Supnik + Copyright (c) 1993-2012, Robert M. Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,9 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 10-Feb-12 JDB Deprecated DEVNO in favor of SC + Added hp_setsc, hp_showsc functions to support SC modifier + 15-Dec-11 JDB Added DA and dummy DC devices 29-Oct-10 JDB DMA channels renamed from 0,1 to 1,2 to match documentation 26-Oct-10 JDB Changed DIB access for revised signal model 03-Sep-08 JDB Fixed IAK instruction dual-use mnemonic display @@ -78,6 +81,7 @@ extern DEVICE ds_dev; extern DEVICE muxl_dev, muxu_dev, muxc_dev; extern DEVICE ipli_dev, iplo_dev; extern DEVICE pif_dev; +extern DEVICE da_dev, dc_dev; /* SCP data structures and interface routines @@ -118,6 +122,7 @@ DEVICE *sim_devices[] = { &muxl_dev, &muxu_dev, &muxc_dev, &ipli_dev, &iplo_dev, &pif_dev, + &da_dev, &dc_dev, NULL }; @@ -768,39 +773,84 @@ else { /* printable character * } -/* Set device number */ +/* Set select code */ -t_stat hp_setdev (UNIT *uptr, int32 num, char *cptr, void *desc) +t_stat hp_setsc (UNIT *uptr, int32 num, char *cptr, void *desc) { DEVICE *dptr = (DEVICE *) desc; DIB *dibptr; int32 i, newdev; t_stat r; -if (cptr == NULL) return SCPE_ARG; -if ((desc == NULL) || (num > 1)) return SCPE_IERR; +if (cptr == NULL) + return SCPE_ARG; + +if ((desc == NULL) || (num > 1)) + return SCPE_IERR; + dibptr = (DIB *) dptr->ctxt; -if (dibptr == NULL) return SCPE_IERR; + +if (dibptr == NULL) + return SCPE_IERR; + newdev = get_uint (cptr, 8, I_DEVMASK - num, &r); -if (r != SCPE_OK) return r; -if (newdev < VARDEV) return SCPE_ARG; -for (i = 0; i <= num; i++, dibptr++) dibptr->select_code = newdev + i; + +if (r != SCPE_OK) + return r; + +if (newdev < VARDEV) + return SCPE_ARG; + +for (i = 0; i <= num; i++, dibptr++) + dibptr->select_code = newdev + i; + return SCPE_OK; } +/* Show select code */ + +t_stat hp_showsc (FILE *st, UNIT *uptr, int32 num, void *desc) +{ +DEVICE *dptr = (DEVICE *) desc; +DIB *dibptr; +int32 i; + +if ((desc == NULL) || (num > 1)) + return SCPE_IERR; + +dibptr = (DIB *) dptr->ctxt; + +if (dibptr == NULL) + return SCPE_IERR; + +fprintf (st, "select code=%o", dibptr->select_code); + +for (i = 1; i <= num; i++) + fprintf (st, "/%o", dibptr->select_code + i); + +return SCPE_OK; +} + + +/* Set device number */ + +t_stat hp_setdev (UNIT *uptr, int32 num, char *cptr, void *desc) +{ +return hp_setsc (uptr, num, cptr, desc); +} + + /* Show device number */ t_stat hp_showdev (FILE *st, UNIT *uptr, int32 num, void *desc) { -DEVICE *dptr = (DEVICE *) desc; -DIB *dibptr; -int32 i; +t_stat result; -if ((desc == NULL) || (num > 1)) return SCPE_IERR; -dibptr = (DIB *) dptr->ctxt; -if (dibptr == NULL) return SCPE_IERR; -fprintf (st, "devno=%o", dibptr->select_code); -for (i = 1; i <= num; i++) fprintf (st, "/%o", dibptr->select_code + i); -return SCPE_OK; +result = hp_showsc (st, uptr, num, desc); + +if (result == SCPE_OK) + fputc ('\n', st); + +return result; } diff --git a/HP2100/hp_disclib.c b/HP2100/hp_disclib.c new file mode 100644 index 00000000..2424c05c --- /dev/null +++ b/HP2100/hp_disclib.c @@ -0,0 +1,2407 @@ +/* hp_disclib.c: HP MAC/ICD disc controller simulator library + + Copyright (c) 2011-2012, J. David Bryan + Copyright (c) 2004-2011, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the authors shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the authors. + + 15-Mar-12 JDB First release + 09-Nov-11 JDB Created disc controller common library from DS simulator + + References: + - 13037 Disc Controller Technical Information Package (13037-90902, Aug-1980) + - HP 13365 Integrated Controller Programming Guide (13365-90901, Feb-1980) + - HP 1000 ICD/MAC Disc Diagnostic Reference Manual (5955-4355, Jun-1984) + - RTE-IVB System Manager's Manual (92068-90006, Jan-1983) + - DVR32 RTE Moving Head Driver source (92084-18711, Revision 5000) + + + This library provides common functions required by HP disc controllers. It + implements the 13037 MAC and 13365 ICD controller command sets used with the + 7905/06/20/25 and 7906H/20H/25H disc drives. + + The library is an adaptation of the code originally written by Bob Supnik + for the DS simulator. DS simulates a 13037 controller connected via a 13175 + disc interface to an HP 1000 computer. To create the library, the functions + of the controller were separated from the functions of the interface. This + allows the library to work with other CPU interfaces, such as the 12821A + HP-IB disc interface, that use substantially different communication + protocols. The library functions implement the controller command set for + the drive units. The interface functions handle the transfer of commands and + data to and from the CPU. + + As a result of this separation, the library does not handle the data transfer + between the controller and the interface directly. Instead, data is moved + between the interface and a sector buffer by the interface simulator, and + then the buffer is passed to the disc library for reading or writing. This + buffer is also used to pass disc commands and parameters to the controller, + and to receive status information from the controller. Only one buffer is + needed per interface, regardless of the number of controllers or units + handled, as a single interface cannot perform data transfers concurrently + with controller commands. + + The library provides routines to prepare, start, and end commands, service + units, and poll drives for Attention status. In addition, routines are + provided to attach and detach disc images from drive units, load and unload + disc heads, classify commands, and provide opcode and phase name strings for + debugging. + + The interface simulator declares a structure that contains the state + variables for a controller. A MAC controller may handle multiple disc units. + An ICD controller handles only a single disc unit, but multiple controllers + may be employed to support several drives on a given interface. The type of + the controller (MAC or ICD) is contained in the structure, which is passed to + the disc library routines. The minor differences in controller action + between the two are handled internally. A macro (CNTLR_INIT) is provided to + initialize the structure. + + The interface simulator also declares the sector buffer. The buffer is an + array containing DL_BUFSIZE 16-bit elements. The address of the buffer is + stored in the controller state structure. The controller maintains the + current index into the buffer, as well as the length of valid data stored + there. Other than setting the length when the controller places data into + the buffer, and resetting the index at the start of a sector read or write, + the interface simulator is free to manipulate these values as desired. + + In general, a user of the library is free to read any of the controller state + variable structure fields. Writing to the fields generally will interfere + with controller operations, with these exceptions: + + Field Name Description + =========== ======================== + status controller status + eod end of data flag + index data buffer index + length data buffer length + seek_time seek delay time + sector_time intersector delay time + cmd_time command start delay time + data_time data transfer delay time + wait_time command wait timeout + + In hardware, the controller executes in three basic states: + + 1. In the Poll Loop, which looks for commands and drive attention requests. + + In each pass of the loop, the next CPU interface in turn is checked for a + command; if present, it is executed. If none are pending, all drives are + checked in turn until one is found with Attention status; if none are + found, the loop continues. If a drive is requesting attention, the + associated CPU interface is connected to check for a command; if present, + it is executed. If not, and the interface allows interrupts, an + interrupt request is made and the Command Wait Loop is entered. If + interrupts are not allowed, the Poll Loop continues. + + 2. In the Command Wait Loop, which looks for commands. + + In each pass of the loop, the current CPU interface is checked for a + command; if present, it is executed. If not, the Command Wait Loop + continues. While in the loop, a 1.8 second timer is running. If it + expires before a command is received, the file mask is reset, and the + Poll Loop is entered. + + 3. In command execution, which processes the current command. + + During command execution, waits for input parameters, seek completion, + data transfers, and output status words are handled internally. Each + wait is governed by the 1.8 second timer; if it expires, the command is + aborted. + + In simulation, these states are represented by the values cntlr_idle, + cntlr_wait, and cntlr_busy, respectively. + + A MAC controller operates from one to eight drives, represented by an array + of one to eight units. When operating multiple units, a pointer to the first + unit of a contiguous array is passed, and the unit number present in the + command is used to index to the target unit. + + A MAC controller emulation requires an array of two contiguous auxiliary + units containing a controller unit and a command wait timeout unit. Commands + that do not access the drive, such as Address Record, are scheduled on the + controller unit to allow controller commands to execute while drive units are + seeking. The command wait timer limits the amount of time the controller will + wait for the interface to supply a command or parameter. A pointer to the + auxiliary unit array is set up during controller state variable + initialization. + + An ICD controller manages a single unit corresponding to the drive in which + the controller is integrated. An interface declares a unit array + corresponding to the number of drives supported and passes the unit number to + use to the command preparation and start routines. Auxiliary units are not + used, and all commands are scheduled on the drive unit associated with a + given controller. + + The library provides a unit service routine to handle all of the disc + commands. The routine is called from the interface service routine to handle + the common disc actions, while the interface routine handles actions specific + to the operation of the interface (such as data transfer). + + The service routine schedules the unit to continue command execution under + these conditions: + + 1. A Seek or Recalibrate command is waiting for the seek completion. + + 2. A read or write command is waiting for the first data transfer of a + sector to start. + + 3. A read or write command is waiting for the next sector to start after + the final data transfer of the preceding sector. + + 4. A Verify command is waiting for the end of the current sector. + + The library also provides controller and timer service routines for MAC + emulations. All three (unit, controller, and timer) must be called from + their respective interface service routines before any interface-specific + actions, if any, are taken. + + On return from the library unit or controller service routines, the "wait" + field of the UNIT structure will be set to the activation time if the unit + is to be scheduled. The caller is responsible for activating the unit. If + the caller uses this feature, the field should be reset to zero before the + next service call. + + The MAC timer unit is activated by the library, and the "wait" field is not + used. The timer starts when a command other than End, Seek, or Recalibrate + completes, and when the controller is waiting for the interface to supply or + accept a parameter during command execution. It stops when an End, Seek, or + Recalibrate command completes, a command is prepared for execution, or the + final parameter has been supplied or accepted by the interface during command + execution. + + The controller maintains six variables in each drive's unit structure: + + wait -- the current service activation time + pos -- the current file offset + u3 (CYL) -- the current drive cylinder + u4 (STAT) -- the drive status (Status-2) + u5 (OP) -- the drive operation in process + u6 (PHASE) -- the current operation phase + + These and other definitions are in the file hp_disclib.h, which must be + included in the interface simulator. + + The controller library supports up to eight drives per MAC controller and one + drive per ICD controller. Unit numbers 0-7 represent valid drive addresses + for a MAC controller. The unit number field is ignored for an ICD + controller, and unit 0 is always implied. In simulation, MAC unit numbers + correspond one-for-one with device units, whereas one ICD controller is + associated with each of the several device units that are independently + addressed as unit 0. + + The MAC controller firmware allows access to unit numbers 8-10 without + causing a Unit Unavailable error. Instead, the controller reports these + legal-but-invalid units as permanently offline. + + + Implementation notes: + + 1. The library does not simulate sector headers and trailers. Initialize + and Write Full Sector commands ignore the SPD bits and the supplied + header and trailer words. Read Full Sector fills in the header with the + current CHS address and sets the SPD bits to zero. The CRC and ECC words + in the trailer are returned as zeros. Programs that depend on drives + retaining the set values will fail. + + 2. The library does not simulate drive hold bits or support multiple CPU + interfaces connected to the same controller. CPU access to a valid drive + always succeeds. + + 3. The library does not simulate interface signals or function bus orders, + except for EOD (End of Data) and BUSY. The interface simulators must + decide for themselves what actions to take (e.g., interrupting the CPU) + on the basis of the controller state. + + 4. The command/sector buffer is an array of 16-bit elements. Byte-oriented + interface simulators, such as the 12821A HP-IB Disc Interface, must do + their own byte packing and unpacking. + + 5. The SAVE command does not save the "wait" and "pos" fields of the UNIT + structure automatically. To ensure that they are saved, they are + referenced by hidden, read-only registers. +*/ + + + +#include + +#include "hp_disclib.h" + + + +/* Command accessors (16-bit word) */ + +#define DL_V_OPCODE 8 /* bits 12- 8: general opcode */ +#define DL_V_HOLD 7 /* bits 7- 7: general hold flag */ +#define DL_V_UNIT 0 /* bits 3- 0: general unit number */ + +#define DL_V_SPD 13 /* bits 15-13: initialize S/P/D flags */ +#define DL_V_CHEAD 6 /* bits 7- 6: cold load head */ +#define DL_V_CSECT 0 /* bits 5- 0: cold load sector */ +#define DL_V_FRETRY 4 /* bits 7- 4: file mask retry count */ +#define DL_V_FDECR 3 /* bits 3- 3: file mask seek decrement */ +#define DL_V_FSPEN 2 /* bits 2- 2: file mask sparing enable */ +#define DL_V_FCYLM 1 /* bits 1- 1: file mask cylinder mode */ +#define DL_V_FAUTSK 0 /* bits 0- 0: file mask auto seek */ + +#define DL_V_FMASK 0 /* bits 3- 0: file mask (combined) */ + + +#define DL_M_OPCODE 037 +#define DL_M_UNIT 017 + +#define DL_M_SPD 007 +#define DL_M_CHEAD 003 +#define DL_M_CSECT 077 +#define DL_M_FRETRY 017 +#define DL_M_FMASK 017 + + +#define GET_OPCODE(c) (CNTLR_OPCODE) (((c) >> DL_V_OPCODE) & DL_M_OPCODE) +#define GET_UNIT(c) (((c) >> DL_V_UNIT) & DL_M_UNIT) + +#define GET_SPD(c) (((c) >> DL_V_SPD) & DL_M_SPD) +#define GET_CHEAD(c) (((c) >> DL_V_CHEAD) & DL_M_CHEAD) +#define GET_CSECT(c) (((c) >> DL_V_CSECT) & DL_M_CSECT) +#define GET_FRETRY(c) (((c) >> DL_V_FRETRY) & DL_M_FRETRY) +#define GET_FMASK(c) (((c) >> DL_V_FMASK) & DL_M_FMASK) + +#define DL_FDECR (1 << DL_V_FDECR) +#define DL_FSPEN (1 << DL_V_FSPEN) +#define DL_FCYLM (1 << DL_V_FCYLM) +#define DL_FAUTSK (1 << DL_V_FAUTSK) + + +/* Parameter accessors (16-bit word) */ + +#define DL_V_HEAD 8 /* bits 12- 8: head number */ +#define DL_V_SECTOR 0 /* bits 7- 0: sector number */ + +#define DL_M_HEAD 0017 +#define DL_M_SECTOR 0377 + +#define GET_HEAD(p) (((p) >> DL_V_HEAD) & DL_M_HEAD) +#define GET_SECTOR(p) (((p) >> DL_V_SECTOR) & DL_M_SECTOR) + +#define SET_HEAD(c) (((c)->head & DL_M_HEAD) << DL_V_HEAD) +#define SET_SECTOR(c) (((c)->sector & DL_M_SECTOR) << DL_V_SECTOR) + + +/* Drive properties table. + + In hardware, drives report their Drive Type numbers to the controller upon + receipt of a Request Status tag bus command. The drive type is used to + determine the legal range of head and sector addresses (the drive itself will + validate the cylinder during seeks). + + In simulation, we set up a table of drive properties and use the model ID as + an index into the table. The table is used to validate seek parameters and + to provide the mapping between CHS addresses and the linear byte addresses + required by the host file access routines. + + The 7905/06/06H drives consist of removable and fixed platters, whereas the + 7920/20H/25/25H drives have only removable multi-platter packs. As a result, + 7905/06 drives are almost always accessed in platter mode, i.e., a given + logical disc area is fully contained on either the removable or fixed + platter, whereas the 7920/25 drives are almost always accessed in cylinder + mode with logical disc areas spanning some or all of the platters. + + Disc image files are arranged as a linear set of tracks. To improve + locality of access, tracks in the 7905/06 images are grouped per-platter, + whereas tracks on the 7920 and 7925 are sequential by cylinder and head + number. + + The simulator maps the tracks on the 7905/06 removable platter (heads 0 and + 1) to the first half of the disc image, and the tracks on the fixed platter + (heads 2 and, for the 7906 only, 3) to the second half of the image. For the + 7906/06H, the cylinder-head order of the tracks is 0-0, 0-1, 1-0, 1-1, ..., + 410-0, 410-1, 0-2, 0-3, 1-2, 1-3, ..., 410-2, 410-3. The 7905 order is the + same, except that head 3 tracks are omitted. + + For the 7920/20H/25/25H, all tracks appear in cylinder-head order, e.g., 0-0, + 0-1, 0-2, 0-3, 0-4, 1-0, 1-1, ..., 822-2, 822-3, 822-4 for the 7920/20H. + + This variable-access geometry is accomplished by defining additional "heads + per cylinder" values for the fixed and removable sections of each drive that + indicates the number of heads that should be grouped for locality. The + removable values are set to 2 on the 7905 and 7906, indicating that those + drives typically use cylinders consisting of two heads. They are set to the + number of heads per drive for the 7920 and 7925, as those typically use + cylinders encompassing the entire pack. +*/ + +#define D7905_RH 2 +#define D7905_FH (D7905_HEADS - D7905_RH) + +#define D7906_RH 2 +#define D7906_FH (D7906_HEADS - D7906_RH) + +#define D7920_RH D7920_HEADS +#define D7920_FH (D7920_HEADS - D7920_RH) + +#define D7925_RH D7925_HEADS +#define D7925_FH (D7925_HEADS - D7925_RH) + +typedef struct { + uint32 sectors; /* sectors per head */ + uint32 heads; /* heads per cylinder*/ + uint32 cylinders; /* cylinders per drive */ + uint32 words; /* words per drive */ + uint32 type; /* drive type */ + uint32 remov_heads; /* number of removable-platter heads */ + uint32 fixed_heads; /* number of fixed-platter heads */ + } DRIVE_PROPERTIES; + + +static const DRIVE_PROPERTIES drive_props [] = { + { D7905_SECTS, D7905_HEADS, D7905_CYLS, D7905_WORDS, D7905_TYPE, D7905_RH, D7905_FH }, + { D7906_SECTS, D7906_HEADS, D7906_CYLS, D7906_WORDS, D7906_TYPE, D7906_RH, D7906_FH }, + { D7920_SECTS, D7920_HEADS, D7920_CYLS, D7920_WORDS, D7920_TYPE, D7920_RH, D7920_FH }, + { D7925_SECTS, D7925_HEADS, D7925_CYLS, D7925_WORDS, D7925_TYPE, D7925_RH, D7925_FH } + }; + +#define PROPS_COUNT (sizeof (drive_props) / sizeof (drive_props [0])) + + +/* Convert a CHS address to a block offset. + + A cylinder/head/sector address is converted into a linear block address that + may be used to calculate a byte offset to pass to the file access routines. + The conversion logic is: + + if Head < removable_heads_per_cylinder then + tracks := Cylinder * removable_heads_per_cylinder + Head; + else + tracks := cylinders_per_drive * removable_heads_per_cylinder + + Cylinder * fixed_heads_per_cylinder + (Head - removable_heads_per_cylinder); + + block := tracks * sectors_per_track + Sector; + + byte_offset := block * words_per_sector * bytes_per_word; + + The byte offset is calculated in two steps to allow for future controller + enhancements to support the CS/80 command set and its associated linear block + addressing mode. +*/ + +#define TO_BLOCK(cylinder,head,sector,model) \ + (((head) < drive_props [model].remov_heads \ + ? (cylinder) * drive_props [model].remov_heads + (head) \ + : drive_props [model].cylinders * drive_props [model].remov_heads \ + + ((cylinder) * drive_props [model].fixed_heads + (head) - drive_props [model].remov_heads)) \ + * drive_props [model].sectors + (sector)) + +#define TO_OFFSET(block) ((block) * DL_WPSEC * sizeof (uint16)) + + +/* Estimate the current sector. + + The sector currently passing under the disc heads is estimated from the + current simulator time (i.e., the count of instructions since startup) and + the simulated disc rotation time. The computation logic is: + + per_sector_time := word_transfer_time * words_per_sector + intersector_time; + + current_sector := (current_time / per_sector_time) MOD sectors_per_track; +*/ + +#define GET_CURSEC(cvptr,uptr) \ + ((uint16) fmod (sim_gtime() / (double) ((cvptr->data_time * DL_WPSEC + cvptr->sector_time)), \ + (double) drive_props [GET_MODEL (uptr->flags)].sectors)) + + +/* Command properties table. + + The validity of each command for a given type of controller is checked + against the command properties table when it is prepared. The table also + includes the count of inbound and outbound properties, the class of the + command, and flags to indicate certain common actions that should be taken. +*/ + +#define CP_V_CLRS 15 /* bits 15-15: command clears the controller status */ +#define CP_V_WAIT 14 /* bits 14-15: command waits for seek completion */ +#define CP_V_UACC 13 /* bits 13-13: command accesses the unit */ +#define CP_V_UCHK 12 /* bits 12-12: command checks for unit legality */ +#define CP_V_UNIT 11 /* bits 11-11: command has a unit field */ +#define CP_V_CTYPE 9 /* bits 10- 9: valid controller types */ +#define CP_V_CLASS 6 /* bits 8- 6: command classification code */ +#define CP_V_INCNT 3 /* bits 5- 3: inbound parameter word count */ +#define CP_V_OUTCNT 0 /* bits 2- 0: outbound parameter word count */ + +#define CP_M_CTYPE 3 /* 2-bit valid controller types mask */ +#define CP_M_CLASS 7 /* 3-bit classification code mask */ +#define CP_M_INCNT 7 /* 3-bit inbound parameter word count mask */ +#define CP_M_OUTCNT 7 /* 3-bit outbound parameter word count mask */ + +#define CP_CLRS (1 << CP_V_CLRS) +#define CP_WAIT (1 << CP_V_WAIT) +#define CP_UACC (1 << CP_V_UACC) +#define CP_UCHK (1 << CP_V_UCHK) +#define CP_UNIT (1 << CP_V_UNIT) + +#define CP_MAC (MAC << CP_V_CTYPE) /* command is valid for MAC controllers */ +#define CP_ICD (ICD << CP_V_CTYPE) /* command is valid for ICD controllers */ + +#define CP_INVAL (class_invalid << CP_V_CLASS) /* invalid classification */ +#define CP_READ (class_read << CP_V_CLASS) /* read classification */ +#define CP_WRITE (class_write << CP_V_CLASS) /* write classification */ +#define CP_CNTL (class_control << CP_V_CLASS) /* control classification */ +#define CP_STAT (class_status << CP_V_CLASS) /* status classification */ + +#define TO_CTYPE(t) (uint32) ((t) << CP_V_CTYPE) + +#define GET_CLASS(c) (CNTLR_CLASS) (((c) >> CP_V_CLASS) & CP_M_CLASS) +#define GET_INCNT(c) (((c) >> CP_V_INCNT) & CP_M_INCNT) +#define GET_OUTCNT(c) (((c) >> CP_V_OUTCNT) & CP_M_OUTCNT) + +// >>> better? +#if 0 +typedef struct { + CNTLR_OPCODE code; + uint32 inbound; + uint32 outbound; + CNTLR_CLASS class; + t_bool mac; + t_bool icd; + t_bool clear_status; + t_bool unit_field; + t_bool unit_check; + t_bool unit_access; + t_bool seek_wait; + } DS_PROPS; + +static const DS_PROPS cmd_props [] = { +/* in out class mac icd clst unit uchk uacc wait */ + { 0, 0, class_read, 1, 1, 1, 0, 1, 1, 0 }, /* cold load read */ + { 0, 0, class_control, 1, 1, 1, 1, 1, 1, 1 }, /* recalibrate */ + { 2, 0, class_control, 1, 1, 1, 1, 1, 1, 0 }, /* seek */ + { 0, 2, class_status, 1, 1, 0, 1, 0, 0, 0 }, /* request status */ + { 0, 1, class_status, 1, 1, 1, 1, 1, 0, 0 }, /* request sector address */ + { 0, 0, class_read, 1, 1, 1, 1, 1, 1, 1 }, /* read */ + { 0, 0, class_read, 1, 1, 1, 1, 1, 1, 1 }, /* read full sector */ + { 1, 0, class_read, 1, 1, 1, 1, 1, 1, 1 }, /* verify */ + { 0, 0, class_write, 1, 1, 1, 1, 1, 1, 1 }, /* write */ + { 0, 0, class_write, 1, 1, 1, 1, 1, 1, 1 }, /* write full sector */ + { 0, 0, class_control, 1, 1, 1, 0, 0, 0, 0 }, /* clear */ + { 0, 0, class_write, 1, 1, 1, 1, 1, 1, 1 }, /* initialize */ + { 2, 0, class_control, 1, 1, 1, 0, 0, 0, 0 }, /* address record */ + { 0, 7, class_status, 1, 0, 0, 0, 0, 0, 0 }, /* request syndrome */ + { 1, 0, class_read, 1, 1, 1, 1, 1, 1, 1 }, /* read with offset */ + { 0, 0, class_control, 1, 1, 1, 0, 0, 0, 0 }, /* set file mask */ + { 0, 0, class_invalid, 0, 0, 1, 0, 0, 0, 0 }, /* invalid */ + { 0, 0, class_invalid, 0, 0, 1, 0, 0, 0, 0 }, /* invalid */ + { 0, 0, class_read, 1, 1, 1, 1, 1, 1, 1 }, /* read without verify */ + { 1, 0, class_status, 1, 0, 1, 0, 0, 0, 0 }, /* load TIO register */ + { 0, 2, class_status, 1, 1, 0, 0, 0, 0, 0 }, /* request disc address */ + { 0, 0, class_control, 1, 1, 1, 0, 0, 0, 0 }, /* end */ + { 0, 0, class_control, 1, 0, 1, 1, 1, 0, 0 } /* wakeup */ + }; +#endif + +static const uint32 cmd_props [] = { + 000 | CP_READ | CP_MAC | CP_ICD | CP_CLRS | CP_UCHK | CP_UACC, /* cold load read */ + 000 | CP_CNTL | CP_MAC | CP_ICD | CP_CLRS | CP_UNIT | CP_UCHK | CP_UACC | CP_WAIT, /* recalibrate */ + 020 | CP_CNTL | CP_MAC | CP_ICD | CP_CLRS | CP_UNIT | CP_UCHK | CP_UACC, /* seek */ + 002 | CP_STAT | CP_MAC | CP_ICD | CP_UNIT, /* request status */ + 001 | CP_STAT | CP_MAC | CP_ICD | CP_CLRS | CP_UNIT | CP_UCHK, /* request sector address */ + 000 | CP_READ | CP_MAC | CP_ICD | CP_CLRS | CP_UNIT | CP_UCHK | CP_UACC | CP_WAIT, /* read */ + 000 | CP_READ | CP_MAC | CP_ICD | CP_CLRS | CP_UNIT | CP_UCHK | CP_UACC | CP_WAIT, /* read full sector */ + 010 | CP_READ | CP_MAC | CP_ICD | CP_CLRS | CP_UNIT | CP_UCHK | CP_UACC | CP_WAIT, /* verify */ + 000 | CP_WRITE | CP_MAC | CP_ICD | CP_CLRS | CP_UNIT | CP_UCHK | CP_UACC | CP_WAIT, /* write */ + 000 | CP_WRITE | CP_MAC | CP_ICD | CP_CLRS | CP_UNIT | CP_UCHK | CP_UACC | CP_WAIT, /* write full sector */ + 000 | CP_CNTL | CP_MAC | CP_ICD | CP_CLRS, /* clear */ + 000 | CP_WRITE | CP_MAC | CP_ICD | CP_CLRS | CP_UNIT | CP_UCHK | CP_UACC | CP_WAIT, /* initialize */ + 020 | CP_CNTL | CP_MAC | CP_ICD | CP_CLRS, /* address record */ + 007 | CP_STAT | CP_MAC, /* request syndrome */ + 010 | CP_READ | CP_MAC | CP_ICD | CP_CLRS | CP_UNIT | CP_UCHK | CP_UACC | CP_WAIT, /* read with offset */ + 000 | CP_CNTL | CP_MAC | CP_ICD | CP_CLRS, /* set file mask */ + 000 | CP_INVAL | CP_CLRS, /* invalid */ + 000 | CP_INVAL | CP_CLRS, /* invalid */ + 000 | CP_READ | CP_MAC | CP_ICD | CP_CLRS | CP_UNIT | CP_UCHK | CP_UACC | CP_WAIT, /* read without verify */ + 010 | CP_STAT | CP_MAC | CP_CLRS, /* load TIO register */ + 002 | CP_STAT | CP_MAC | CP_ICD, /* request disc address */ + 000 | CP_CNTL | CP_MAC | CP_ICD | CP_CLRS, /* end */ + 000 | CP_CNTL | CP_MAC | CP_CLRS | CP_UNIT | CP_UCHK /* wakeup */ + }; + + +/* Auxiliary unit indices */ + +typedef enum { + controller = 0, /* controller unit index */ + timer = 1 /* command wait timer index */ + } AUX_INDEX; + + +/* Controller opcode names */ + +static const char invalid_name [] = "invalid"; + +static const char *opcode_name [] = { + "cold load read", /* 00 */ + "recalibrate", /* 01 */ + "seek", /* 02 */ + "request status", /* 03 */ + "request sector address", /* 04 */ + "read", /* 05 */ + "read full sector", /* 06 */ + "verify", /* 07 */ + "write", /* 10 */ + "write full sector", /* 11 */ + "clear", /* 12 */ + "initialize", /* 13 */ + "address record", /* 14 */ + "request syndrome", /* 15 */ + "read with offset", /* 16 */ + "set file mask", /* 17 */ + invalid_name, /* 20 = invalid */ + invalid_name, /* 21 = invalid */ + "read without verify", /* 22 */ + "load TIO register", /* 23 */ + "request disc address", /* 24 */ + "end", /* 25 */ + "wakeup" /* 26 */ + }; + +/* Controller phase names */ + +static const char *phase_name [] = { + "start", + "data", + "end" + }; + + + +/* Disc library local controller routines */ + +static t_bool start_seek (CVPTR cvptr, UNIT *uptr, CNTLR_OPCODE next_opcode, CNTLR_PHASE next_phase); +static t_stat start_read (CVPTR cvptr, UNIT *uptr); +static void end_read (CVPTR cvptr, UNIT *uptr); +static void start_write (CVPTR cvptr, UNIT *uptr); +static t_stat end_write (CVPTR cvptr, UNIT *uptr); +static t_bool position_sector (CVPTR cvptr, UNIT *uptr, t_bool verify); +static void next_sector (CVPTR cvptr, UNIT *uptr); +static t_stat io_error (CVPTR cvptr, UNIT *uptr); + +/* Disc library local utility routines */ + +static void set_address (CVPTR cvptr, uint32 index); +static void set_timer (CVPTR cvptr, FLIP_FLOP action); +static uint16 drive_status (UNIT *uptr); + + + +/* Disc library global controller routines */ + + +/* Prepare a command for execution. + + On entry, the first word of the controller buffer contains the command to + prepare, the "cvptr" parameter points at the controller state variable + structure, and the "units" parameter points at the first unit of the unit + array. For a MAC controller, the "unit limit" parameter indicates the last + valid unit number, and the unit to use is taken from the unit field of the + command word. For an ICD controller, the parameter indicates the number + of the unit to use directly. + + If a valid command was prepared for execution, the routine returns TRUE and + sets the controller state to "busy." If the command is illegal, the routine + returns FALSE and sets the controller state to "waiting." In the latter + case, the controller status will indicate the reason for the rejection. + + The opcode and unit number (for MAC controllers) are obtained from the buffer + and checked for legality. If either is illegal, the controller status is set + appropriately, and the routine returns FALSE. + + For a valid command and an available unit, the controller's opcode field is + set from the buffer, the length field is set to the number of inbound + parameter words expected, and the index field is set to 1 to point at the + first parameter entry in the buffer. +*/ + +t_bool dl_prepare_command (CVPTR cvptr, UNIT *units, uint32 unit_limit) +{ +uint32 props, unit; +CNTLR_OPCODE opcode; + +set_timer (cvptr, CLEAR); /* stop the command wait timer */ + +opcode = GET_OPCODE (cvptr->buffer [0]); /* get the opcode from the command */ + +if (opcode > last_opcode) /* is the opcode invalid? */ + props = CP_CLRS; /* undefined commands clear prior status */ +else /* the opcode is potentially valid */ + props = cmd_props [opcode]; /* get the command properties */ + +if (cvptr->type == MAC) /* is this a MAC controller? */ + if (props & CP_UNIT) /* is the unit field defined for this command? */ + unit = GET_UNIT (cvptr->buffer [0]); /* get the unit from the command */ + else /* no unit specified in the command */ + unit = 0; /* so the unit is always unit 0 */ + +else /* an ICD controller */ + unit = unit_limit; /* uses the supplied unit number */ + +if (props & CP_CLRS) { /* clear the prior controller status */ + cvptr->status = normal_completion; /* if indicated for this command */ + cvptr->spd_unit = SET_S1UNIT (unit); /* save the unit number for status requests */ + } + +if (props & TO_CTYPE (cvptr->type)) /* is the opcode defined for this controller? */ + if ((props & CP_UCHK) && unit > DL_MAXUNIT) /* if the unit number is checked and is illegal, */ + dl_end_command (cvptr, unit_unavailable); /* end with a unit unavailable error */ + + else { + cvptr->state = cntlr_busy; /* legal unit, so controller is now busy */ + cvptr->opcode = opcode; /* save the controller opcode */ + cvptr->length = GET_INCNT (props); /* set the inbound parameter count */ + cvptr->index = 1; /* point at the first parameter element (if any) */ + + if (cvptr->type == MAC && cvptr->length) { /* MAC controller and inbound parameters? */ + cvptr->aux [controller].OP = opcode; /* save the opcode */ + cvptr->aux [controller].PHASE = data_phase; /* and set the phase for parameter pickup */ + set_timer (cvptr, SET); /* start the timer to wait for the first parameter */ + } + + return TRUE; /* command is now prepared for execution */ + } + +else /* the opcode is undefined */ + dl_end_command (cvptr, illegal_opcode); /* so set bad opcode status */ + +return FALSE; /* the preparation has failed */ +} + + +/* Start a command. + + On entry, the controller's opcode field contains the command to start, and + the buffer contains the command word in element 0 and the parameters required + by the command, if any, beginning in element 1. The call parameters are the + same as those supplied to the "prepare command" routine. + + If the command was started successfully, the routine returns a pointer to the + unit to be activated and sets that unit's "wait" field to the activation + time. The caller should activate the unit upon return to complete or + continue command processing. If the command did not start, the routine + returns NULL. + + If a seek is in progress on a drive when a command accessing that drive is + started, the unit pointer is returned but the unit's "wait" field is set to + zero. In this case, the unit must not be activated (as it already is). + Instead, the unit's opcode and phase fields are set to start the command + automatically when the seek completes. + + For commands that return status from the controller, the buffer will contain + the returned value(s), the buffer index will be zero, and the buffer length + will be set to the number of words returned in the buffer. These words must + be returned to the CPU via the interface. + + + Implementation notes: + + 1. A command must have been prepared by calling dl_prepare_command first. + After preparation, the controller's opcode will be valid, and the unit + number field will be legal (but not necessarily valid) for those commands + that check the unit. + + Unit numbers 0-7 represent valid drive addresses. However, the MAC + controller firmware allows access to unit numbers 8-10 without causing a + Unit Unavailable error. Instead, the controller reports these units as + permanently offline. + + 2. Commands that check for a valid unit do some processing before failing + with a Status-2 (not ready) error. For example, the Seek command accepts + its parameters from the CPU and sets the CHS values into the controller + before failing. + + 3. In hardware, read, write, and recalibrate commands wait in an internal + loop for a pending seek completion and clear the resulting Attention + status before executing. In simulation, we change a seeking drive unit's + opcode and phase fields from seek completion to the start of the next + command. This eliminates the setting of the Attention status and begins + command execution automatically when the seek completes. + + If the seek completed between the command preparation and start, + Attention will have been set. If the unit is idle on entry, we clear the + Attention status unilaterally (it doesn't matter whether or not it was + set; Attention always is clear when commands start). + + 4. The Seek and Cold Load Read commands do not check for a seek or + recalibrate in progress. If the heads are moving, the drive will reject + a seek command with a Seek Check error. The firmware does not test + explicitly for Access Not Ready before executing the command, so the + parameters (e.g., controller CHS addresses) are still set as though the + command succeeded. + + A Seek command will return to the Poll Loop with Seek Check status set. + When the seek in progress completes, the controller will interrupt with + Drive Attention status. The controller address will differ from the + drive address, so it's incumbent upon the caller to issue a Request + Status command after the seek, which will return Status-2 Error status. + + A Cold Load Read command issues a seek to cylinder 0 and then begins a + read, which first waits for seek completion. The Seek Check error will + abort the command at this point with Status-2 Error status. + + In simulation, a Seek command allows the seek in progress to complete + normally, whereas a Cold Load Read command modifies the unit command + and phase from the end phase of Seek or Recalibrate to the start + phase of Read, which will catch the Seek Check error as in hardware. + + 5. The Cold Load Read command checks if the drive is ready before setting + the file mask. Therefore, we normally defer setting the file mask until + the unit service is called. However, if a seek is in progress, then the + drive must be ready, so we set the file mask here. + + 6. ECC is not simulated, so the Request Syndrome command always returns zero + values for the displacement and patterns. + + 7. The Request Status, Request Sector Address, and Wakeup commands reference + drive units but are scheduled on the controller unit because they may be + issued while a drive is processing a seek. + + 8. The activation time is set to one-half of the intersector time (latency) + for read and write commands, and to the controller processing time for + all others. +*/ + +UNIT *dl_start_command (CVPTR cvptr, UNIT *units, uint32 unit_limit) +{ +UNIT *uptr, *rptr; +uint32 props, unit; +t_bool is_seeking = FALSE; + +props = cmd_props [cvptr->opcode]; /* get the command properties */ + +if (cvptr->type == MAC) { /* is this a MAC controller? */ + if (props & CP_UNIT) /* is the unit field defined for this command? */ + unit = GET_UNIT (cvptr->buffer [0]); /* get the unit number from the command */ + else /* no unit is specified in the command */ + unit = 0; /* so the unit number defaults to 0 */ + + if (unit > unit_limit) /* if the unit number is invalid, */ + uptr = NULL; /* it does not correspond to a unit */ + else if (props & CP_UACC) /* if the command accesses a drive, */ + uptr = units + unit; /* get the address of the unit */ + else /* the command accesses the controller only */ + uptr = cvptr->aux + controller; /* so use the controller unit */ + } + +else { /* for an ICD controller, */ + unit = 0; /* the unit value is ignored */ + uptr = units + unit_limit; /* and we use the indicated unit */ + } + +if ((props & CP_UCHK) && !uptr /* if the unit number is checked and is invalid */ + || (props & CP_WAIT) /* or the command waits for the drive */ + && (drive_status (uptr) & DL_S2STOPS)) { /* and the drive is offline or faulted */ + dl_end_command (cvptr, status_2_error); /* then the command ends with a Status-2 error */ + uptr = NULL; /* prevent command from starting */ + } + +else if (uptr) { /* otherwise, we have a valid unit */ + uptr->wait = cvptr->cmd_time; /* most commands use the command delay */ + + if (props & CP_UACC) { /* does the command access the unit? */ + is_seeking = sim_is_active (uptr) != 0; /* see if the unit is busy */ + + if (is_seeking) /* if a seek is in progress, */ + uptr->wait = 0; /* set for no unit activation */ + + else { /* otherwise, the unit is idle */ + uptr->STAT &= ~DL_S2ATN; /* clear the drive Attention status */ + + if (GET_CLASS (props) == class_read /* if a read command */ + || GET_CLASS (props) == class_write) /* or a write command */ + uptr->wait = cvptr->sector_time / 2; /* schedule the sector start latency */ + } + } + } + +cvptr->index = 0; /* reset the buffer index */ +cvptr->length = GET_OUTCNT (props); /* set the count of outbound parameters */ +cvptr->eod = CLEAR; /* clear the end of data flag */ + + +switch (cvptr->opcode) { /* dispatch the command */ + + case cold_load_read: + cvptr->cylinder = 0; /* set cylinder to 0 */ + cvptr->head = GET_CHEAD (cvptr->buffer [0]); /* get the head */ + cvptr->sector = GET_CSECT (cvptr->buffer [0]); /* and sector from the command */ + + if (is_seeking) { /* if a seek is in progress, */ + uptr->STAT |= DL_S2SC; /* a Seek Check occurs */ + cvptr->file_mask = DL_FSPEN; /* set sparing enabled */ + uptr->OP = read; /* start the read on seek completion */ + uptr->PHASE = start_phase; /* and reset the command phase */ + return uptr; /* to allow the seek to complete normally */ + } + + else /* the drive is not seeking */ + uptr->wait = cvptr->cmd_time; /* the command starts with a seek, not a read */ + + break; + + + case seek: + cvptr->cylinder = cvptr->buffer [1]; /* get the supplied cylinder */ + cvptr->head = GET_HEAD (cvptr->buffer [2]); /* and head */ + cvptr->sector = GET_SECTOR (cvptr->buffer [2]); /* and sector */ + + if (is_seeking) { /* if a seek is in progress, */ + uptr->STAT |= DL_S2SC; /* a Seek Check occurs */ + dl_idle_controller (cvptr); /* return the controller to the idle condition */ + return uptr; /* to allow the seek to complete normally */ + } + + break; + + + case request_status: + cvptr->buffer [0] = /* set the Status-1 value */ + cvptr->spd_unit | SET_S1STAT (cvptr->status); /* into the buffer */ + + if (unit > unit_limit) /* if the unit number is invalid */ + rptr = NULL; /* it does not correspond to a unit */ + else /* otherwise, the unit is valid */ + rptr = &units [unit]; /* so get the address of the referenced unit */ + + cvptr->buffer [1] = drive_status (rptr); /* set the Status-2 value */ + + if (rptr) /* if the unit is valid */ + rptr->STAT &= ~DL_S2FS; /* clear the First Status bit */ + + cvptr->spd_unit = SET_S1UNIT (unit); /* save the unit number */ + + if (unit > DL_MAXUNIT) /* if the unit number is illegal, */ + cvptr->status = unit_unavailable; /* the next status will be Unit Unavailable */ + else /* a legal unit */ + cvptr->status = normal_completion; /* clears the controller status */ + + break; + + + case request_disc_address: + set_address (cvptr, 0); /* return the CHS values in buffer 0-1 */ + break; + + + case request_sector_address: + if (unit > unit_limit) /* if the unit number is invalid */ + rptr = NULL; /* it does not correspond to a unit */ + else /* otherwise, the unit is valid */ + rptr = &units [unit]; /* so get the address of the referenced unit */ + + if (drive_status (rptr) & DL_S2NR) /* if the drive is not ready, */ + dl_end_command (cvptr, status_2_error); /* terminate with not ready status */ + else /* otherwise, the drive is ready */ + cvptr->buffer [0] = GET_CURSEC (cvptr, rptr); /* so calculate the current sector address */ + break; + + + case request_syndrome: + cvptr->buffer [0] = /* return the Status-1 value in buffer 0 */ + cvptr->spd_unit | SET_S1STAT (cvptr->status); + + set_address (cvptr, 1); /* return the CHS values in buffer 1-2 */ + + cvptr->buffer [3] = 0; /* the displacement is always zero */ + cvptr->buffer [4] = 0; /* the syndrome is always zero */ + cvptr->buffer [5] = 0; + cvptr->buffer [6] = 0; + break; + + + case address_record: + cvptr->cylinder = cvptr->buffer [1]; /* get the supplied cylinder */ + cvptr->head = GET_HEAD (cvptr->buffer [2]); /* and head */ + cvptr->sector = GET_SECTOR (cvptr->buffer [2]); /* and sector */ + cvptr->eoc = CLEAR; /* clear the end-of-cylinder flag */ + break; + + + case set_file_mask: + cvptr->file_mask = GET_FMASK (cvptr->buffer [0]); /* get the supplied file mask */ + + if (cvptr->type == MAC) /* if a MAC controller */ + cvptr->retry = GET_FRETRY (cvptr->buffer [0]); /* the retry counter is supplied too */ + break; + + + case initialize: + if (uptr) /* if the unit is valid, */ + cvptr->spd_unit |= /* merge the SPD flags */ + SET_S1SPD (GET_SPD (cvptr->buffer [0])); /* from the command word */ + break; + + + case verify: + cvptr->verify_count = cvptr->buffer [1]; /* get the supplied sector count */ + break; + + + default: /* the remaining commands */ + break; /* are handled in the service routines */ + } + + +if (uptr) { /* if the command accesses a valid unit */ + uptr->OP = cvptr->opcode; /* save the opcode in the unit */ + + if (cvptr->length) /* if the command has outbound parameters, */ + uptr->PHASE = data_phase; /* set up the data phase for the transfer */ + else /* if there are no parameters, */ + uptr->PHASE = start_phase; /* set up the command phase for execution */ + + return uptr; /* return a pointer to the scheduled unit */ + } + +else + return NULL; /* the command did not start */ +} + + +/* Complete a command. + + The current command is terminated with the indicated status. The command + result status is set, the controller enters the command wait state, and the + CPU timer is restarted. +*/ + +void dl_end_command (CVPTR cvptr, CNTLR_STATUS status) +{ +cvptr->status = status; /* set the command result status */ +cvptr->state = cntlr_wait; /* set the controller state to waiting */ +set_timer (cvptr, SET); /* start the command wait timer */ +return; +} + + +/* Poll the drives for attention status. + + If interrupts are enabled on the interface, this routine is called to check + if any drive is requesting attention. The routine returns TRUE if a drive is + requestion attention and FALSE if not. + + Starting with the last unit requesting attention, each drive is checked in + sequence. If a drive has its Attention status set, the controller sets its + unit number, sets the result status to Drive Attention, and enters the + command wait state. The routine returns TRUE to indicate that an interrupt + should be generated. The next time the routine is called, the poll begins + with the last unit that requested attention, so that each unit is given an + equal chance to respond. + + If no unit is requesting attention, the routine returns FALSE to indicate + that no interrupt should be generated. +*/ + +t_bool dl_poll_drives (CVPTR cvptr, UNIT *units, uint32 unit_limit) +{ +uint32 unit; + +for (unit = 0; unit <= unit_limit; unit++) { /* check each unit in turn */ + cvptr->poll_unit = /* start with the last unit checked */ + (cvptr->poll_unit + 1) % (unit_limit + 1); /* and cycle back to unit 0 */ + + if (units [cvptr->poll_unit].STAT & DL_S2ATN) { /* if the unit is requesting attention, */ + units [cvptr->poll_unit].STAT &= ~DL_S2ATN; /* clear the Attention status */ + cvptr->spd_unit = SET_S1UNIT (cvptr->poll_unit); /* set the controller's unit number */ + cvptr->status = drive_attention; /* and status */ + cvptr->state = cntlr_wait; /* and wait for a command */ + return TRUE; /* tell the caller to interrupt */ + } + } + +return FALSE; /* no requests, so no interrupt */ +} + + +/* Service the disc drive unit. + + The unit service routine is called to execute scheduled controller commands + for the specified unit. The actions to be taken depend on the current state + of the controller and the unit. + + In addition to the controller state variables supplied in the call, the + service routine accesses these six variables in the UNIT structure: + + wait -- the current service activation time + pos -- the current file offset + u3 (CYL) -- the current drive cylinder + u4 (STAT) -- the drive status (Status-2) + u5 (OP) -- the drive operation in process + u6 (PHASE) -- the current operation phase + + The activation time is set non-zero if the service should be rescheduled. + The caller is responsible upon return for activating the unit. The file + offset indicates the byte position in the disc image file for the next read + or write operation. + + The drive cylinder gives the current location of the head positioner. This + may differ from the cylinder value in the controller if the Address Record + command has been used. The drive status maintains various per-drive + conditions (e.g., the state of the read-only and format switches, drive + ready, first status). The operation in process and operation phase define + the action to be taken by this service routine. + + Initially, the operation in process is set to the opcode field of the command + when it is started. However, the operation in process may change during + execution (the controller opcode never does). This is to aid code reuse in + the service routine. For example, a Cold Load Read command is changed to a + Read command once the seek portion is complete, and a Read Without Verify + command is changed to a normal Read command after a track boundary is + crossed. + + The operation phase provides different substates for those commands that + transfer data or that have different starting and ending actions. Three + phases are defined: start, data, and end. Commands that do not transfer data + to or from the CPU interface do not have data phases, and commands that + complete upon first service do not have end phases. The service routine + validates phase assignments and returns SCPE_IERR (Internal Error) if entry + is made with an illegal operation phase or a phase that is not valid for a + given operation. + + An operation in the data phase is in the process of transferring data between + the CPU and sector buffer. Because this process is interface-specific, the + service routine does nothing (other than validate) with this phase. It is up + to the caller to transition from the data phase to the end phase when the + transfer is complete. + + If an operation is completed, or an error has occurred, the controller state + on return will be either idle or waiting, instead of busy. The caller should + check the controller state to determine if normal completion or error + recovery is appropriate. + + If the command is continuing, the service activation time will be set + appropriately. The caller should then call sim_activate to schedule the next + service and clear the "wait" field in preparation for the next service call. + + + Implementation notes: + + 1. The Cold Load Read and Seek commands check only the drive's Not Ready + status because seeking clears a Seek Check. The other commands that + access the unit (e.g., Read and Write) have already checked in the + command start routine for Not Ready, Seek Check, or Fault status and + terminated with a Status-2 error. + + 2. Several commands (e.g., Set File Mask, Address Record) are executed + completely within the dl_start_command routine, so all we do here is + finish the command with the expected status. The service routine is + called only to provide the proper command execution delay. + + 3. If a host file system error occurs, the service routine returns SCPE_IERR + to stop simulation. If simulation is resumed, the controller will behave + as though an uncorrectable data error had occurred. +*/ + +t_stat dl_service_drive (CVPTR cvptr, UNIT *uptr) +{ +t_stat result = SCPE_OK; +const CNTLR_OPCODE opcode = (CNTLR_OPCODE) uptr->OP; + +switch ((CNTLR_PHASE) uptr->PHASE) { /* dispatch the phase */ + + case start_phase: + switch (opcode) { /* dispatch the current operation */ + + case recalibrate: + case seek: + if (start_seek (cvptr, uptr, opcode, end_phase) /* start the seek; if it succeeded, */ + && (cvptr->type == MAC)) /* and this a MAC controller, */ + dl_idle_controller (cvptr); /* idle until the seek completes */ + break; + + + case cold_load_read: + if (start_seek (cvptr, uptr, read, start_phase)) /* start the seek; did it succeed? */ + cvptr->file_mask = DL_FSPEN; /* set sparing enabled now */ + break; + + + case read: + case read_with_offset: + case read_without_verify: + cvptr->length = DL_WPSEC; /* transfer just the data */ + result = start_read (cvptr, uptr); /* start the sector read */ + break; + + + case read_full_sector: + cvptr->length = DL_WPFSEC; /* transfer the header/data/trailer */ + result = start_read (cvptr, uptr); /* start the sector read */ + break; + + + case verify: + cvptr->length = 0; /* no data transfer needed */ + result = start_read (cvptr, uptr); /* start the sector read */ + + if (uptr->PHASE == data_phase) { /* did the read start successfully? */ + uptr->PHASE = end_phase; /* skip the data phase for Verify */ + uptr->wait = cvptr->sector_time /* reschedule for the intersector time */ + + cvptr->data_time * DL_WPSEC; /* plus the data read time */ + } + break; + + + case write: + case initialize: + cvptr->length = DL_WPSEC; /* transfer just the data */ + start_write (cvptr, uptr); /* start the sector write */ + break; + + + case write_full_sector: + cvptr->length = DL_WPFSEC; /* transfer the header/data/trailer */ + start_write (cvptr, uptr); /* start the sector write */ + break; + + + case request_status: + case request_sector_address: + case clear: + case address_record: + case request_syndrome: + case set_file_mask: + case load_tio_register: + case request_disc_address: + case end: + case wakeup: + dl_service_controller (cvptr, uptr); /* call the controller for service */ + break; + + + default: /* entered with an invalid state */ + result = SCPE_IERR; /* return an internal (programming) error */ + break; + } /* end of operation dispatch */ + break; /* end of start phase handlers */ + + + case data_phase: + switch (opcode) { /* dispatch the current operation */ + case read: + case read_full_sector: + case read_with_offset: + case read_without_verify: + case write: + case write_full_sector: + case initialize: + break; /* data transfers are handled by the caller */ + + + default: /* entered with an invalid state */ + result = SCPE_IERR; /* return an internal (programming) error */ + break; + } /* end of operation dispatch */ + break; /* end of data phase handlers */ + + + case end_phase: + switch (opcode) { /* dispatch the operation command */ + + case recalibrate: + case seek: + if (cvptr->type == ICD) /* is this an ICD controller? */ + dl_end_command (cvptr, drive_attention); /* seek ends with drive attention status */ + else /* if not an ICD controller, */ + uptr->STAT |= DL_S2ATN; /* set Attention in the unit status */ + break; + + + case read: + case read_full_sector: + case read_with_offset: + end_read (cvptr, uptr); /* end the sector read */ + break; + + + case read_without_verify: + if (cvptr->sector == 0) /* have we reached the end of the track? */ + uptr->OP = read; /* begin verifying next time */ + + end_read (cvptr, uptr); /* end the sector read */ + break; + + + case verify: + cvptr->verify_count = /* decrement the count */ + (cvptr->verify_count - 1) & DMASK; /* modulo 65536 */ + + if (cvptr->verify_count == 0) /* more sectors to verify? */ + cvptr->eod = SET; /* no, so terminate the command cleanly */ + + end_read (cvptr, uptr); /* end the sector read */ + break; + + + case write: + case write_full_sector: + case initialize: + result = end_write (cvptr, uptr); /* end the sector write */ + break; + + + case request_status: + case request_sector_address: + case request_disc_address: + dl_service_controller (cvptr, uptr); /* call the controller for service */ + break; + + + default: /* entered with an invalid state */ + result = SCPE_IERR; /* return an internal (programming) error */ + break; + } /* end of operation dispatch */ + break; /* end of end phase handlers */ + } /* end of phase dispatch */ + +return result; /* return the result of the service */ +} + + +/* Service the controller unit. + + The controller service routine is called to execute scheduled controller + commands that do not access drive units. It is also called to obtain command + parameters from the interface and to return command result values to the + interface. The actions to be taken depend on the current state of the + controller. + + Controller commands are scheduled on a separate unit to allow concurrent + processing while seeks are in progress. For example, a seek may be started + on unit 0. While the seek is in progress, the CPU may request status from + the controller. In between returning the first and second status words to + the CPU, the seek may complete. Separating the controller unit allows seek + completion to be handled while the controller is "busy" waiting for the CPU + to indicate that it is ready for the second word. + + For ICD controllers, the controller unit is not used, and all commands are + scheduled on the drive unit. To reduce code duplication, however, the drive + unit service calls the controller service directly to handle controller + commands. + + The service routine validates phase assignments and returns SCPE_IERR + (Internal Error) if entry is made with an illegal operation phase or a phase + that is not valid for a given operation. + + Implementation notes: + + 1. While the interface simulator is responsible for data phase transfers, + the controller service routine is responsible for (re)starting and + stopping the command wait timer for each parameter sent to and received + from the interface. +*/ + +t_stat dl_service_controller (CVPTR cvptr, UNIT *uptr) +{ +t_stat result = SCPE_OK; +const CNTLR_OPCODE opcode = (CNTLR_OPCODE) uptr->OP; + +switch ((CNTLR_PHASE) uptr->PHASE) { /* dispatch the phase */ + + case start_phase: + case end_phase: + switch (opcode) { /* dispatch the current operation */ + case request_status: + dl_end_command (cvptr, cvptr->status); /* command is complete with no status change */ + break; + + + case clear: + dl_clear_controller (cvptr, uptr, soft_clear); /* clear the controller */ + dl_end_command (cvptr, normal_completion); /* command is complete */ + break; + + + case request_sector_address: + case address_record: + case request_syndrome: + case set_file_mask: + case load_tio_register: + case request_disc_address: + dl_end_command (cvptr, normal_completion); /* command is complete */ + break; + + + case end: + dl_idle_controller (cvptr); /* command is complete with controller idle */ + break; + + + case wakeup: + dl_end_command (cvptr, unit_available); /* command is complete with unit available */ + break; + + + default: /* entered with an invalid state */ + result = SCPE_IERR; /* return an internal (programming) error */ + break; + } /* end of operation dispatch */ + break; /* end of start and end phase handlers */ + + + case data_phase: + switch (opcode) { /* dispatch the current operation */ + + case seek: + case verify: + case address_record: + case read_with_offset: + case load_tio_register: + if (cvptr->length > 1) /* at least one more parameter to input? */ + set_timer (cvptr, SET); /* restart the timer for the next parameter */ + else /* this is the last one */ + set_timer (cvptr, CLEAR); /* so stop the command wait timer */ + break; + + + case request_status: + case request_sector_address: + case request_syndrome: + case request_disc_address: + if (cvptr->length > 0) /* at least one more to parameter output? */ + set_timer (cvptr, SET); /* restart the timer for the next parameter */ + else /* this is the last one */ + set_timer (cvptr, CLEAR); /* so stop the command wait timer */ + break; + + + default: /* entered with an invalid state */ + result = SCPE_IERR; /* return an internal (programming) error */ + break; + } /* end of operation dispatch */ + break; /* end of data phase handlers */ + } /* end of phase dispatch */ + +return result; /* return the result of the service */ +} + + +/* Service the command wait timer unit. + + The command wait timer service routine is called if the command wait timer + expires. This indicates that the CPU did not respond to a parameter transfer + or did not issue a new command within the 1.8 second timeout period. The + timer is used with the MAC controller to ensure that a hung CPU does not tie + up the controller, preventing it from servicing other CPUs or drives. ICD + controllers do not use the command wait timer; they will wait forever, as + each controller is dedicated to a single interface. + + When a timeout occurs, the controller unit is cancelled in case the cause was + a parameter timeout. Then the file mask is reset, and the controller is + idled. + + The interface is responsible for polling for a new command and for drive + attention when a timeout occurs. + + Implementation notes: + + 1. Only the controller unit may be active when the command wait timer + expires. A unit is never active because the timer is cancelled when + commands are executing and is restarted after the command completes. +*/ + +t_stat dl_service_timer (CVPTR cvptr, UNIT *uptr) +{ +sim_cancel (cvptr->aux); /* cancel the controller */ + +dl_idle_controller (cvptr); /* idle the controller */ +cvptr->file_mask = 0; /* clear the file mask */ + +return SCPE_OK; +} + + +/* Clear the controller. + + The controller connected to the specified unit is cleared as directed. A MAC + controller is connected to several units, so the unit is used to find the + associated device and thereby the unit array. An ICD controller is connected + only to the specified unit. + + In hardware, four conditions clear the 13037 controller: + + - an initial application of power + - an assertion of the CLEAR signal by the CPU interface + - a timeout of the command wait timer + - a programmed Clear command + + The first two conditions, called "hard clears," are equivalent and cause a + firmware restart with the PWRON flag set. The 13175 interface for the HP + 1000 asserts CLEAR in response to the backplane CRS signal if the PRESET + ENABLE jumper is not installed (which is the usual case). The third + condition also causes a firmware restart but with the PWRON flag clear. The + last condition is executed in the command handler and therefore returns to + the Command Wait Loop instead of the Poll Loop. + + For a hard clear, the 13037 controller will: + + - disconnect the CPU interface + - zero the controller RAM (no drives held, last polled unit number reset) + - issue a Controller Preset to clear all connected drives + - clear the clock offset + - clear the file mask + - enter the Poll Loop (clears the controller status) + + For a timeout clear, the 13037 controller will: + + - disconnect the CPU interface + - clear the hold bits of any drives held by the interface that timed out + - clear the clock offset + - clear the file mask + - enter the Poll Loop (clears the controller status) + + For a programmed "soft" clear, the 13037 controller will: + + - clear the controller status + - issue a Controller Preset to clear all connected drives + - enter the Command Wait Loop + + Controller Preset is a tag bus command that is sent to all drives connected + to the controller. Each drive will: + + - disconnect from the controller + - clear its internal drive faults + - clear its head and sector registers + - clear its illegal head and sector flip-flops + - reset its seek check, first status, drive fault, and attention status + + In simulation, a hard clear occurs when a RESET -P or RESET command is + issued, or a programmed CLC 0 instruction is executed. A soft clear occurs + when a programmed Clear command is started. A timeout clear occurs when the + command wait timer unit is serviced, but this action is handled in the timer + unit service. + + Because the controller execution state is implemented by scheduling command + phases for the target or controller unit, a simulated firmware restart must + abort any in-process activation. However, a firmware restart does not affect + seeks in progress, so these must be allowed to continue to completion so that + their Attention requests will be honored. + + + Implementation notes: + + 1. The specific 13365 controller actions on hard or soft clears are not + documented. Therefore, an ICD controller clear is handled as a MAC + controller clear, except that only the current drive is preset (as an ICD + controller manages only a single drive). + + 2. Neither hard nor soft clears affect the controller flags (e.g., EOC) or + registers (e.g., cylinder address). + + 3. In simulation, an internal seek, such as an auto-seek during a Read + command or the initial seek during a Cold Load Read command, will be + aborted for a hard or timeout clear, whereas in hardware it would + complete normally. This is OK, however, because an internal seek always + clears the drive's Attention status on completion, so aborting the + simulated seek is equivalent to seek completion. + + 4. In simulation, a Controller Preset only resets the specified status bits, + as the remainder of the hardware actions are not implemented. +*/ + +t_stat dl_clear_controller (CVPTR cvptr, UNIT *uptr, CNTLR_CLEAR clear_type) +{ +uint32 unit, unit_count; +DEVICE *dptr = NULL; + +if (clear_type == hard_clear) { /* is this a hard clear? */ + dl_idle_controller (cvptr); /* idle the controller */ + cvptr->file_mask = 0; /* clear the file mask */ + cvptr->poll_unit = 0; /* clear the last unit polled */ + } + +if (cvptr->type == ICD) /* is this an ICD controller? */ + unit_count = 1; /* there is only one unit per controller */ + +else { /* a MAC controller clears all units */ + dptr = find_dev_from_unit (uptr); /* find the associated device */ + + if (dptr == NULL) /* device doesn't exist?!? */ + return SCPE_IERR; /* this is an impossible condition! */ + else /* the device was found */ + unit_count = dptr->numunits; /* so get the number of units */ + } + +for (unit = 0; unit < unit_count; unit++) { /* loop through the unit(s) */ + if (dptr) /* pick up the unit from the device? */ + uptr = dptr->units + unit; /* yes, so get the next unit */ + + if (!(uptr->flags & UNIT_DIS)) { /* is the unit enabled? */ + if (clear_type == hard_clear /* a hard clear cancels */ + && uptr->OP != seek /* only if not seeking */ + && uptr->OP != recalibrate) /* or recalibrating */ + sim_cancel (uptr); /* cancel the service */ + + uptr->STAT &= ~DL_S2CPS; /* do "Controller Preset" for the unit */ + } + } + +return SCPE_OK; +} + + +/* Idle the controller. + + The command wait timer is turned off, the status is reset, and the controller + is returned to the idle state (Poll Loop). +*/ + +void dl_idle_controller (CVPTR cvptr) +{ +cvptr->state = cntlr_idle; /* idle the controller */ +cvptr->status = normal_completion; /* the Poll Loop clears the status */ + +set_timer (cvptr, CLEAR); /* stop the command wait timer */ +return; +} + + + +/* Load or unload the drive heads. + + In hardware, a drive's heads are loaded when a disc pack is installed and the + RUN/STOP switch is set to RUN. The drive reports First Status when the heads + load to indicate that the pack has potentially changed. Setting the switch + to STOP unloads the heads. When the heads are unloaded, the drive reports + Not Ready and Drive Busy status. + + In simulation, the unit must be attached before the heads may be unloaded or + loaded. As the heads should be automatically loaded when a unit is attached + and unloaded when a unit is detached, this routine must be called after + attaching or before detaching. + + + Implementation notes: + + 1. The drive sets its Attention status bit when the heads load or unload. + However, the ICD controller reports Attention only for head unloading. + + 2. Loading or unloading the heads clears Fault and Seek Check status. + + 3. If we are called during a RESTORE command, the unit's flags are not + changed to avoid upsetting the state that was SAVEd. +*/ + +t_stat dl_load_unload (CVPTR cvptr, UNIT *uptr, t_bool load) +{ +if ((uptr->flags & UNIT_ATT) == 0) /* must be attached to [un]load */ + return SCPE_UNATT; /* return "Unit not attached" if not */ + +else if (!(sim_switches & SIM_SW_REST)) /* modify the flags only if not restoring */ + if (load) { /* are we loading the heads? */ + uptr->flags = uptr->flags & ~UNIT_UNLOAD; /* clear the unload flag */ + uptr->STAT = DL_S2FS; /* and set First Status */ + + if (cvptr->type != ICD) /* if this is not an ICD controller */ + uptr->STAT |= DL_S2ATN; /* set Attention status also */ + } + + else { /* we are unloading the heads */ + uptr->flags = uptr->flags | UNIT_UNLOAD; /* set the unload flag */ + uptr->STAT = DL_S2ATN; /* and Attention status */ + } + +return SCPE_OK; +} + + + +/* Disc library global utility routines */ + + +/* Classify the current controller opcode. + + The controller opcode is classified as a read, write, control, or status + command, and the classification is returned to the caller. If the opcode is + illegal or undefined for the indicated controller, the classification is + marked as invalid. +*/ + +CNTLR_CLASS dl_classify (CNTLR_VARS cntlr) +{ +uint32 flags; + +if (cntlr.opcode > last_opcode) /* is the opcode illegal? */ + return class_invalid; /* return an invalid classification */ + +flags = cmd_props [cntlr.opcode]; /* get the opcode properties */ + +if (cntlr.type == MAC && (flags & CP_MAC) /* is the opcode */ + || cntlr.type == ICD && (flags & CP_ICD)) /* defined for this controller? */ + return GET_CLASS (cmd_props [cntlr.opcode]); /* yes, so return the command classification */ +else /* opcode is undefined */ + return class_invalid; /* so return an invalid classification */ +} + + +/* Return the name of an opcode. + + A string representing the supplied controller opcode is returned to the + caller. If the opcode is illegal or undefined for the indicated controller, + the string "invalid" is returned. +*/ + +const char *dl_opcode_name (CNTLR_TYPE controller, CNTLR_OPCODE opcode) +{ +uint32 flags; + +if (opcode > last_opcode) /* is the opcode illegal? */ + return invalid_name; /* return an error indication */ + +flags = cmd_props [opcode]; /* get the opcode properties */ + +if (controller == MAC && (flags & CP_MAC) || /* is the opcode */ + controller == ICD && (flags & CP_ICD)) /* defined for this controller? */ + return opcode_name [opcode]; /* yes, so return the opcode name */ +else /* opcode is undefined, */ + return invalid_name; /* so return an error indication */ +} + + +/* Return the name of a command phase. + + A string representing the supplied phase is returned to the caller. If the + phase is illegal, the string "invalid" is returned. +*/ + +const char *dl_phase_name (CNTLR_PHASE phase) +{ +if (phase > last_phase) /* is the phase illegal? */ + return invalid_name; /* return an error indication */ +else /* phase is defined, */ + return phase_name [phase]; /* so return the phase name */ +} + + + +/* Disc library global VM routines */ + + +/* Attach a disc image file to a unit. + + The file specified by the supplied filename is attached to the indicated + unit. If the attach was successful, the heads are loaded on the drive. + + If the drive is set to auto-type, the size of the image file is compared to + the table of drive capacities to determine which type of drive was used to + create it. If the image file is new, then the previous drive type is + retained. +*/ + +t_stat dl_attach (CVPTR cvptr, UNIT *uptr, char *cptr) +{ +uint32 id, size; +t_stat result; + +result = attach_unit (uptr, cptr); /* attach the unit */ + +if (result != SCPE_OK) /* did the attach fail? */ + return result; /* yes, so return the error status */ + +dl_load_unload (cvptr, uptr, TRUE); /* if the attach succeeded, load the heads */ + +if (uptr->flags & UNIT_AUTO) { /* is auto-typing desired? */ + size = sim_fsize (uptr->fileref) / sizeof (uint16); /* get the file size in words */ + + if (size > 0) /* a new file retains the current drive type */ + for (id = 0; id < PROPS_COUNT; id++) /* find the best fit to the drive types */ + if (size <= drive_props [id].words /* if the file size fits the drive capacity */ + || id == PROPS_COUNT - 1) { /* or this is the largest drive */ + uptr->capac = drive_props [id].words; /* then set the capacity */ + uptr->flags = (uptr->flags & ~UNIT_MODEL) /* and the model */ + | SET_MODEL (id); + break; + } + } + +return SCPE_OK; /* unit was successfully attached */ +} + + +/* Detach a disc image file from a unit. + + The heads are unloaded on the drive, and the attached file, if any, is + detached. +*/ + +t_stat dl_detach (CVPTR cvptr, UNIT *uptr) +{ +dl_load_unload (cvptr, uptr, FALSE); /* unload the heads if attached */ +return detach_unit (uptr); /* and detach the unit */ +} + + +/* Set the drive model. + + This validation routine is called to set the model of disc drive associated + with the specified unit. The "value" parameter indicates the model ID, and + the unit capacity is set to the size indicated. +*/ + +t_stat dl_set_model (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) /* cannot alter the disc model */ + return SCPE_ALATT; /* if the unit is attached */ + +if (value != UNIT_AUTO) /* if we are not autosizing */ + uptr->capac = drive_props [GET_MODEL (value)].words; /* set the capacity to the new value */ + +return SCPE_OK; +} + + + +/* Disc library local controller routines */ + + +/* Start a read operation on the current sector. + + The current sector indicated by the controller address is read from the disc + image file into the sector buffer in preparation for data transfer to the + CPU. If the end of the track had been reached, and the file mask permits, + an auto-seek is scheduled instead to allow the read to continue. + + On entry, the end-of-data flag is checked. If it is set, the current read is + completed. Otherwise, the buffer data offset and verify options are set up. + For a Read Full Sector, the sync word is set from the controller type, and + dummy cylinder and head-sector words are generated from the current location + (as would be the case in the absence of track sparing). + + The image file is positioned to the correct sector in preparation for + reading. If the positioning requires a permitted seek, it is scheduled, and + the routine returns with the operation phase unchanged to wait for seek + completion before resuming the read (when the seek completes, the service + routine will be entered, and we will be called again; this time, the + end-of-cylinder flag will be clear and positioning will succeed). If + positioning resulted in an error, the current read is terminated with the + error status set. + + If positioning succeeded within the same cylinder, the sector image is read + into the buffer at an offset determined by the operation (Read Full Sector + leaves room at the start of the buffer for the sector header). If the image + file read did not return a full sector, the remainder of the buffer is padded + with zeros. If the image read failed with a file system error, SCPE_IOERR is + returned from the service routine to cause a simulation stop; resumption is + handled as an Uncorrectable Data Error. + + If the image was read correctly, the next sector address is updated, the + operation phase is set for the data transfer, and the index of the first word + to transfer is set. + + + Implementation notes: + + 1. The length of the transfer required (cvptr->length) must be set before + entry. + + 2. Entry while executing a Read Without Verify or Read Full Sector command + inhibits address verification. The unit opcode is tested instead of the + controller opcode because a Read Without Verify is changed to a Read to + begin verifying after a track switch occurs. +*/ + +static t_stat start_read (CVPTR cvptr, UNIT *uptr) +{ +uint32 count, offset; +t_bool verify; +const CNTLR_OPCODE opcode = (CNTLR_OPCODE) uptr->OP; + +if (cvptr->eod == SET) { /* end of data indicated? */ + dl_end_command (cvptr, normal_completion); /* complete the command */ + return SCPE_OK; + } + +if (opcode == read_full_sector) { /* starting a Read Full Sector command? */ + if (cvptr->type == ICD) /* is this an ICD controller? */ + cvptr->buffer [0] = 0100377; /* ICD does not support ECC */ + else + cvptr->buffer [0] = 0100376; /* MAC does support ECC */ + + set_address (cvptr, 1); /* set the current address into buffer 1-2 */ + offset = 3; /* start the data after the header */ + verify = FALSE; /* no address verification */ + } + +else { /* it's another read command */ + offset = 0; /* data starts at the beginning */ + verify = (opcode != read_without_verify); /* verify unless RWV */ + } + +if (! position_sector (cvptr, uptr, verify)) /* position the sector */ + return SCPE_OK; /* seek in progress or an error occurred */ + +count = sim_fread (cvptr->buffer + offset, /* read the sector from the image */ + sizeof (uint16), DL_WPSEC, /* into the sector buffer */ + uptr->fileref); + +for (count = count + offset; count < cvptr->length; count++) /* pad sector as needed */ + cvptr->buffer [count] = 0; /* e.g., if reading from a new file */ + +if (ferror (uptr->fileref)) /* did a host file system error occur? */ + return io_error (cvptr, uptr); /* set up data error status and stop the simulation */ + +next_sector (cvptr, uptr); /* address the next sector */ + +uptr->PHASE = data_phase; /* set up data transfer phase */ +cvptr->index = 0; /* reset the data index */ + +return SCPE_OK; /* the read was successfully started */ +} + + +/* Finish a read operation on the current sector. + + On entry, the end-of-data flag is checked. If it is set, the current read is + completed. Otherwise, the command phase is reset to start the next sector, + and the disc service is scheduled to allow for the intersector delay. + + + Implementation notes: + + 1. The intersector time is required to allow the ICD interface to set the + end-of-data flag before the next sector begins. The CPU must have enough + time to receive the last byte of the current sector and then unaddress + the disc controller before the first byte of the next sector is sent. If + the time is not long enough, the sector address will be incremented twice + (e.g., a 128-word read of sector 0 will terminate with sector 2 as the + next sector instead of sector 1). +*/ + +static void end_read (CVPTR cvptr, UNIT *uptr) +{ +if (cvptr->eod == SET) /* end of data indicated? */ + dl_end_command (cvptr, normal_completion); /* complete the command */ + +else { /* reading continues */ + uptr->PHASE = start_phase; /* reset to the start phase */ + uptr->wait = cvptr->sector_time; /* delay for the intersector time */ + } + +return; +} + + +/* Start a write operation on the current sector. + + The current sector indicated by the controller address is positioned for + writing from the sector buffer to the disc image file after data transfer + from the CPU. + + On entry, if writing is not permitted, or formatting is required but not + enabled, the command is terminated with an error. Otherwise, the disc image + file is positioned to the correct sector in preparation for writing. + + If the positioning requires a permitted seek, it is scheduled, and the + routine returns with the operation phase unchanged to wait for seek + completion before resuming the write (when the seek completes, the service + routine will be entered, and we will be called again; this time, the + end-of-cylinder flag will be clear and positioning will succeed). If + positioning resulted in an error, the current write is terminated with the + error status set. + + If positioning succeeded within the same cylinder, the operation phase is set + for the data transfer, and the index of the first word to transfer is set. + + + Implementation notes: + + 1. Entry while executing a Write Full Sector or Initialize command inhibits + address verification. In addition, the drive's FORMAT switch must be set + to the enabled position for these commands to succeed. +*/ + +static void start_write (CVPTR cvptr, UNIT *uptr) +{ +const t_bool verify = (CNTLR_OPCODE) uptr->OP == write; /* only Write verifies the sector */ + +if ((uptr->flags & UNIT_WPROT) /* is the unit write protected? */ + || !verify && !(uptr->flags & UNIT_FMT)) /* or is formatting required but not enabled? */ + dl_end_command (cvptr, status_2_error); /* terminate the write with an error */ + +else if (position_sector (cvptr, uptr, verify)) { /* write OK; position the sector */ + uptr->PHASE = data_phase; /* position OK; set up data transfer phase */ + cvptr->index = 0; /* reset the data index */ + } + +return; +} + + +/* Finish a write operation on the current sector. + + The current sector is written from the sector buffer to the disc image file + at the current file position. If the end of the track had been reached, and + the file mask permits, an auto-seek is scheduled instead to allow the write + to continue. + + On entry, the drive is checked to ensure that it is ready for the write. + Then the sector buffer is padded appropriately if a full sector of data was + not transferred. The buffer is written to the disc image file at the + position corresponding to the controller address as set when the sector was + started. The write begins at a buffer offset determined by the command (a + Write Full Sector leaves room at the start of the buffer for the sector + header). + + If the image write failed with a file system error, SCPE_IOERR is returned + from the service routine to cause a simulation stop; resumption is handled as + an Uncorrectable Data Error. If the image was written correctly, the next + sector address is updated. If the end-of-data flag is set, the current write + is completed. Otherwise, the command phase is reset to start the next + sector, and the disc service is scheduled to allow for the intersector delay. + + + Implementation notes: + + 1. A partial sector is filled with 177777B words (ICD) or copies of the last + word (MAC) per page 7-10 of the ICD/MAC Disc Diagnostic manual. +*/ + +static t_stat end_write (CVPTR cvptr, UNIT *uptr) +{ +uint32 count; +uint16 pad; +const CNTLR_OPCODE opcode = (CNTLR_OPCODE) uptr->OP; +const uint32 offset = (opcode == write_full_sector ? 3 : 0); + +if (uptr->flags & UNIT_UNLOAD) { /* if the drive is not ready, */ + dl_end_command (cvptr, access_not_ready); /* terminate the command with an error */ + return SCPE_OK; + } + +if (cvptr->index < DL_WPSEC + offset) { /* was a partial sector transferred? */ + if (cvptr->type == ICD) /* an ICD controller */ + pad = DMASK; /* pads the sector with -1 */ + else /* a MAC controller */ + pad = cvptr->buffer [cvptr->index - 1]; /* pads with the last word written */ + + for (count = cvptr->index; count < DL_WPSEC + offset; count++) + cvptr->buffer [count] = pad; /* pad the sector buffer */ + } + +sim_fwrite (cvptr->buffer + offset, sizeof (uint16), /* write the sector to the file */ + DL_WPSEC, uptr->fileref); + +if (ferror (uptr->fileref)) /* did a host file system error occur? */ + return io_error (cvptr, uptr); /* set up data error status and stop the simulation */ + +next_sector (cvptr, uptr); /* address the next sector */ + +if (cvptr->eod == SET) /* is the end of data indicated? */ + dl_end_command (cvptr, normal_completion); /* complete the command */ + +else { /* writing continues */ + uptr->PHASE = start_phase; /* reset to the start phase */ + uptr->wait = cvptr->sector_time; /* delay for the intersector time */ + } + +return SCPE_OK; +} + + +/* Position the disc image file to the current sector. + + The image file is positioned to the byte address corresponding to the + controller's current cylinder, head, and sector address. Positioning may + involve an auto-seek if the prior read or write addressed the final sector in + a cylinder. If a seek is initiated or an error is detected, the routine + returns FALSE to indicate that the positioning was not performed. If the + file was positioned, the routine returns TRUE. + + On entry, if the controller's end-of-cylinder flag is set, the prior read or + write addressed the final sector in the current cylinder. If the file mask + does not permit auto-seeking, the current command is terminated with an End + of Cylinder error. Otherwise, the cylinder is incremented or decremented as + directed by the file mask, and a seek to the new cylinder is started. + + If the increment or decrement resulted in an out-of-bounds value, the seek + will return Seek Check status, and the command is terminated with an error. + If the seek is legal, the routine returns with the disc service scheduled for + seek completion and the command state unchanged. When the service is called, + the read or write will continue on the new cylinder. + + If the EOC flag was not set, the drive position is checked against the + controller position. If they are different (as may occur with an Address + Record command that specified a different location than the last Seek + command), a seek is started to the correct cylinder, and the routine returns + with the disc service scheduled for seek completion as above. + + If the drive and controller positions agree, the controller CHS address is + validated against the drive limits. If they are invalid, Seek Check status + is set, and the command is terminated with an error. + + If the address is valid, the drive is checked to ensure that it is ready for + positioning. If it is, the the byte offset in the image file is calculated + from the CHS address, and the file is positioned. The disc service is + scheduled to begin the data transfer, and the routine returns TRUE to + indicate that the file position was set. + + + Implementation notes: + + 1. The ICD controller returns an End of Cylinder error if an auto-seek + results in a position beyond the drive limits. The MAC controller + returns a Status-2 error. Both controllers set the Seek Check bit in the + drive status word. +*/ + +static t_bool position_sector (CVPTR cvptr, UNIT *uptr, t_bool verify) +{ +uint32 block; +uint32 model = GET_MODEL (uptr->flags); + +if (cvptr->eoc == SET) /* positioned at the end of a cylinder? */ + if (cvptr->file_mask & DL_FAUTSK) { /* is an auto-seek allowed? */ + if (cvptr->file_mask & DL_FDECR) /* decremental seek? */ + cvptr->cylinder = (cvptr->cylinder - 1) & DMASK; /* decrease cylinder with wraparound */ + else /* incremental seek */ + cvptr->cylinder = (cvptr->cylinder + 1) & DMASK; /* increase cylinder with wraparound */ + + start_seek (cvptr, uptr, /* start the auto-seek */ + (CNTLR_OPCODE) uptr->OP, /* with the current operation */ + (CNTLR_PHASE) uptr->PHASE); /* and phase unchanged */ + + if (uptr->STAT & DL_S2SC) /* did a seek check occur? */ + if (cvptr->type == ICD) /* ICD controller? */ + dl_end_command (cvptr, end_of_cylinder); /* report as end of cylinder error */ + else /* MAC controller? */ + dl_end_command (cvptr, status_2_error); /* report as Status-2 error */ + } + + else /* file mask does not permit auto-seek */ + dl_end_command (cvptr, end_of_cylinder); /* so terminate with an EOC error */ + +else if (verify && (uint32) uptr->CYL != cvptr->cylinder) { /* is the positioner on the wrong cylinder? */ + start_seek (cvptr, uptr, /* start a seek to the correct cylinder */ + (CNTLR_OPCODE) uptr->OP, /* with the current operation */ + (CNTLR_PHASE) uptr->PHASE); /* and phase unchanged */ + + if (uptr->STAT & DL_S2SC) /* did a seek check occur? */ + dl_end_command (cvptr, status_2_error); /* report as Status-2 error */ + } + +else if (((uint32) uptr->CYL >= drive_props [model].cylinders) /* is the cylinder out of bounds? */ + || (cvptr->head >= drive_props [model].heads) /* or the head? */ + || (cvptr->sector >= drive_props [model].sectors)) { /* or the sector? */ + uptr->STAT = uptr->STAT | DL_S2SC; /* set Seek Check status */ + dl_end_command (cvptr, status_2_error); /* and terminate with an error */ + } + +else if (uptr->flags & UNIT_UNLOAD) /* is the drive ready for positioning? */ + dl_end_command (cvptr, access_not_ready); /* terminate the command with an access error */ + +else { /* ready to position the image file */ + block = TO_BLOCK (uptr->CYL, cvptr->head, /* calculate the new block position */ + cvptr->sector, model); /* (for inspection only) */ + uptr->pos = TO_OFFSET (block); /* and then convert to a byte offset */ + + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set the image file position */ + + uptr->wait = cvptr->data_time; /* delay for the data access time */ + return TRUE; /* and report that positioning was accomplished */ + } + +return FALSE; /* report that positioning failed or was deferred */ +} + + +/* Address the next sector. + + The controller's CHS address is incremented to point at the next sector. If + the next sector number is valid, the routine returns. Otherwise, the sector + number is reset to sector 0. If the file mask is set for cylinder mode, the + head is incremented, and if the new head number is valid, the routine + returns. If the head number is invalid, it is reset to head 0, and the + end-of-cylinder flag is set. The EOC flag is also set if the file mask is + set for surface mode. + + The new cylinder address is not set here, because cylinder validation must + only occur when the next sector is actually accessed. Otherwise, reading or + writing the last sector on a track or cylinder with auto-seek disabled would + cause an End of Cylinder error even if the transfer ended with that sector. + Instead, we set the EOC flag to indicate that a cylinder update is pending. + + As a result of this deferred update method, the state of the EOC flag must be + considered when returning the disc address to the CPU. +*/ + +static void next_sector (CVPTR cvptr, UNIT *uptr) +{ +const uint32 model = GET_MODEL (uptr->flags); /* get the disc model */ + +cvptr->sector = cvptr->sector + 1; /* increment the sector number */ + +if (cvptr->sector < drive_props [model].sectors) /* at the end of the track? */ + return; /* no, so the next sector value is OK */ + +cvptr->sector = 0; /* wrap the sector number */ + +if (cvptr->file_mask & DL_FCYLM) { /* are we in cylinder mode? */ + cvptr->head = cvptr->head + 1; /* yes, so increment the head */ + + if (cvptr->head < drive_props [model].heads) /* at the end of the cylinder? */ + return; /* no, so the next head value is OK */ + + cvptr->head = 0; /* wrap the head number */ + } + +cvptr->eoc = SET; /* set the end-of-cylinder flag to */ +return; /* indicate that an update is required */ +} + + +/* Start a seek. + + A seek is initiated on the indicated unit if the drive is ready and the + cylinder, head, and sector values in the controller are valid for the current + drive model. If the current operation is a recalibrate, a seek is initiated + to cylinder 0 instead of the cylinder value stored in the controller. The + routine returns TRUE if the drive was ready for the seek and FALSE if it was + not. + + If the controller cylinder is beyond the drive's limit, Seek Check status is + set in the unit, and the heads are not moved. Otherwise, the relative + cylinder position change is calculated, and the heads are moved to the new + position. + + If the controller head or sector is beyond the drive's limit, Seek Check + status is set in the unit. Otherwise, Seek Check status is cleared, and the + new file offset is calculated. + + A seek check terminates the current command for an ICD controller. For a MAC + controller, the seek check is noted in the drive status, but processing will + continue until the drive sets Attention status. + + Finally, the drive operation and phase are set to the supplied values before + returning. + + + Implementation notes: + + 1. EOC is not reset for recalibrate so that a reseek will return to the same + location as was pending when the recalibrate was done. + + 2. Calculation of the file offset is performed here simply to keep the unit + position register available for inspection. The actual file positioning + is done in position_sector. + + 3. In hardware, a seek to the current location will set Drive Busy status + for 1.3 milliseconds (the head settling time). In simulation, disc + service is scheduled as though a one-cylinder seek was requested. +*/ + +static t_bool start_seek (CVPTR cvptr, UNIT *uptr, CNTLR_OPCODE next_opcode, CNTLR_PHASE next_phase) +{ +int32 delta; +uint32 block, target_cylinder; +const uint32 model = GET_MODEL (uptr->flags); /* get the drive model */ + +if (uptr->flags & UNIT_UNLOAD) { /* are the heads unloaded? */ + dl_end_command (cvptr, status_2_error); /* seek ends with Status-2 error */ + return FALSE; /* drive was not ready */ + } + +if ((CNTLR_OPCODE) uptr->OP == recalibrate) /* is the unit recalibrating? */ + target_cylinder = 0; /* seek to cylinder 0 and don't reset EOC */ + +else { /* Seek command or auto-seek request */ + target_cylinder = cvptr->cylinder; /* seek to the controller cylinder */ + cvptr->eoc = CLEAR; /* clear the end-of-cylinder flag */ + } + +if (target_cylinder >= drive_props [model].cylinders) { /* is the cylinder out of bounds? */ + delta = 0; /* don't change the positioner */ + uptr->STAT = uptr->STAT | DL_S2SC; /* and set Seek Check status */ + } + +else { /* cylinder value is OK */ + delta = abs (uptr->CYL - target_cylinder); /* calculate the relative movement */ + uptr->CYL = target_cylinder; /* and move the positioner */ + + if ((cvptr->head >= drive_props [model].heads) /* if the head */ + || (cvptr->sector >= drive_props [model].sectors)) /* or sector is out of bounds, */ + uptr->STAT = uptr->STAT | DL_S2SC; /* set Seek Check status */ + + else { /* the head and sector are OK */ + uptr->STAT = uptr->STAT & ~DL_S2SC; /* clear Seek Check status */ + + block = TO_BLOCK (uptr->CYL, cvptr->head, /* set up the new block position */ + cvptr->sector, model); /* (for inspection only) */ + uptr->pos = TO_OFFSET (block); /* and then convert to a byte offset */ + } + } + +if ((uptr->STAT & DL_S2SC) && cvptr->type == ICD) /* Seek Check and ICD controller? */ + dl_end_command (cvptr, status_2_error); /* command ends with Status-2 error */ + +else { /* seek OK or this is a MAC controller */ + if (delta == 0) /* if seek is to the same cylinder */ + delta = 1; /* schedule as a one-cylinder seek */ + + uptr->wait = cvptr->seek_time * delta; /* seek delay is based on the relative movement */ + } + +uptr->OP = next_opcode; /* set the next operation */ +uptr->PHASE = next_phase; /* and command phase */ +return TRUE; /* and report that the drive was ready */ +} + + +/* Report an I/O error. + + Errors indicated by the host file system are reported to the console and + simulation is stopped with an "I/O error" message. If the simulation is + continued, the CPU will receive an Uncorrectable Data Error indication from + the controller. +*/ + +static t_stat io_error (CVPTR cvptr, UNIT *uptr) +{ +dl_end_command (cvptr, uncorrectable_data_error); /* terminate the command with an error */ + +perror ("DiscLib I/O error"); /* report the error to the console */ +clearerr (uptr->fileref); /* and clear the error in case we resume */ + +return SCPE_IOERR; /* return an I/O error to stop the simulator */ +} + + + +/* Disc library local utility routines */ + + +/* Set the current controller address into the buffer. + + The controller's current cylinder, head, and sector are packed into two words + and stored in the sector buffer, starting at the index specified. If the + end-of-cylinder flag is set, the cylinder is incremented to reflect the + auto-seek that will be attempted when the next disc access is made. + + + Implementation notes: + + 1. The 13037 firmware always increments the cylinder number if the EOC flag + is set, rather than checking cylinder increment/decrement bit in the file + mask. +*/ + +static void set_address (CVPTR cvptr, uint32 index) +{ +cvptr->buffer [index] = cvptr->cylinder + (cvptr->eoc == SET ? 1 : 0); /* update the cylinder if EOC */ +cvptr->buffer [index + 1] = SET_HEAD (cvptr) | SET_SECTOR (cvptr); /* merge the head and sector */ +return; +} + + +/* Start or stop the command wait timer. + + A MAC controller uses a 1.8 second timer to ensure that it does not wait + forever for a non-responding disc drive or CPU interface. In simulation, MAC + interfaces supply an auxiliary timer unit that is activated when the command + wait timer is started and cancelled when the timer is stopped. + + ICD interfaces do not use the command wait timer. + + + Implementation notes: + + 1. Absolute activation is used because the timer is restarted between + parameter word transfers. +*/ + +static void set_timer (CVPTR cvptr, FLIP_FLOP action) +{ +if (cvptr->type == MAC) /* is this a MAC controller? */ + if (action == SET) /* start the timer? */ + sim_activate_abs (cvptr->aux + timer, /* activate the auxiliary unit */ + cvptr->wait_time); + else /* stop the timer */ + sim_cancel (cvptr->aux + timer); /* by canceling the unit */ +return; +} + + +/* Return the drive status (status word 2). + + In hardware, the controller outputs the Address Unit command on the drive tag + bus and the unit number on the drive control bus. The addressed drive then + responds by setting its internal "selected" flag. The controller then + outputs the Request Status command on the tag bug, and the selected drive + returns its status on the control bus. If a drive is selected but the heads + are unloaded, the drive returns Not Ready and Busy status. If no drive is + selected, the control bus floats inactive. This is interpreted by the + controller as Not Ready status (because the drive returns an inactive Ready + status). + + In simulation, an enabled but detached unit corresponds to "selected but + heads unloaded," and a disabled unit corresponds to a non-existent unit. + + + Implementation notes: + + 1. The Attention, Drive Fault, First Status, and Seek Check flags are stored + in the unit status word. The other status flags are determined + dynamically. + + 2. The Drive Busy bit is set if the unit service is scheduled. In hardware, + this bit indicates that the heads are not positioned over a track, i.e., + that a seek is in progress. In simulation, the only time a Request + Status command is allowed is either when the controller is waiting for + seek completion or for another command. In the latter case, unit service + will not be scheduled, so activation can only be for seek completion. +*/ + +static uint16 drive_status (UNIT *uptr) +{ +uint16 status; +uint32 model; + +if (uptr == NULL) /* if the unit is invalid */ + return DL_S2ERR | DL_S2NR; /* then it does not respond */ + +model = GET_MODEL (uptr->flags); /* get the drive type */ +status = drive_props [model].type | uptr->STAT; /* start with the drive type and unit status */ + +if (uptr->flags & UNIT_WPROT) /* is the write protect switch set? */ + status |= DL_S2RO; /* set the Protected status bit */ + +if (uptr->flags & UNIT_FMT) /* is the format switch enabled? */ + status |= DL_S2FMT; /* set the Format status bit */ + +if (uptr->flags & UNIT_DIS) /* is the unit non-existent? */ + status |= DL_S2NR; /* set the Not Ready bit */ + +else if (uptr->flags & UNIT_UNLOAD) /* are the heads unloaded? */ + status |= DL_S2NR | DL_S2BUSY; /* set the Not Ready and Drive Busy bits */ + +if (sim_is_active (uptr)) /* is the drive positioner moving? */ + status |= DL_S2BUSY; /* set the Drive Busy bit */ + +if (status & DL_S2ERRORS) /* are there any Status-2 errors? */ + status |= DL_S2ERR; /* set the Error bit */ + +return status; /* return the unit status */ +} diff --git a/HP2100/hp_disclib.h b/HP2100/hp_disclib.h new file mode 100644 index 00000000..90056b27 --- /dev/null +++ b/HP2100/hp_disclib.h @@ -0,0 +1,380 @@ +/* hp_disclib.h: HP MAC/ICD disc controller simulator library definitions + + Copyright (c) 2011-2012, J. David Bryan + Copyright (c) 2004-2011, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the authors shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the authors. + + 15-Mar-12 JDB First release + 09-Nov-11 JDB Created disc controller common library from DS simulator + + + This file defines the interface between interface simulators and the + simulation library for the HP 13037 and 13365 disc controllers. It must be + included by the interface-specific modules (DA, DS, etc.). +*/ + + + +#include "hp2100_defs.h" + + + +/* Program limits */ + +#define DL_MAXDRIVE 7 /* last valid drive number */ +#define DL_MAXUNIT 10 /* last legal unit number */ + +#define DL_AUXUNITS 2 /* number of MAC auxiliary units required */ + +#define DL_WPSEC 128 /* words per normal sector */ +#define DL_WPFSEC 138 /* words per full sector */ +#define DL_BUFSIZE DL_WPFSEC /* required buffer size in words */ + + +/* Default controller times */ + +#define DL_SEEK_TIME 100 /* seek delay time (per cylinder) */ +#define DL_SECTOR_TIME 27 /* intersector delay time */ +#define DL_CMD_TIME 3 /* command start delay time */ +#define DL_DATA_TIME 1 /* data transfer delay time */ + +#define DL_WAIT_TIME 2749200 /* command wait timeout (1.74 seconds) */ + + +/* Common per-unit disc drive state variables */ + +#define CYL u3 /* current drive cylinder */ +#define STAT u4 /* current drive status (Status 2) */ +#define OP u5 /* current drive operation in process */ +#define PHASE u6 /* current command phase */ + + +/* Unit flags and accessors */ + +#define UNIT_V_MODEL (UNIT_V_UF + 0) /* bits 1-0: model ID */ +#define UNIT_V_WLK (UNIT_V_UF + 2) /* bits 2-2: write locked (protect switch) */ +#define UNIT_V_UNLOAD (UNIT_V_UF + 3) /* bits 3-3: heads unloaded */ +#define UNIT_V_FMT (UNIT_V_UF + 4) /* bits 4-4: format enabled */ +#define UNIT_V_AUTO (UNIT_V_UF + 5) /* bits 5-5: autosize */ +#define DL_V_UF (UNIT_V_UF + 6) /* first free unit flag bit */ + +#define UNIT_M_MODEL 3 /* model ID mask */ + +#define UNIT_MODEL (UNIT_M_MODEL << UNIT_V_MODEL) +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_UNLOAD (1 << UNIT_V_UNLOAD) +#define UNIT_FMT (1 << UNIT_V_FMT) +#define UNIT_AUTO (1 << UNIT_V_AUTO) + +#define UNIT_WPROT (UNIT_WLK | UNIT_RO) /* write protected if locked or read-only */ + +#define GET_MODEL(t) (((t) >> UNIT_V_MODEL) & UNIT_M_MODEL) +#define SET_MODEL(t) (((t) & UNIT_M_MODEL) << UNIT_V_MODEL) + + +/* Status-1 accessors */ + +#define DL_V_S1SPD 13 /* bits 15-13: S/P/D flags */ +#define DL_V_S1STAT 8 /* bits 12- 8: controller status */ +#define DL_V_S1UNIT 0 /* bits 7- 0: last unit number */ + +#define DL_M_S1UNIT 15 /* unit mask */ + +#define GET_S1UNIT(v) (((v) >> DL_V_S1UNIT) & DL_M_S1UNIT) + +#define SET_S1SPD(v) ((v) << DL_V_S1SPD) +#define SET_S1STAT(v) ((v) << DL_V_S1STAT) +#define SET_S1UNIT(v) ((v) << DL_V_S1UNIT) + + +/* Status-2 accessors (+ = kept in unit status, - = determined dynamically */ + +#define DL_V_S2ERR 15 /* bits 15-15: (-) any error flag */ +#define DL_V_S2DTYP 9 /* bits 12- 9: (-) drive type */ +#define DL_V_S2ATN 7 /* bits 7- 7: (+) attention flag */ +#define DL_V_S2RO 6 /* bits 6- 6: (-) read only flag */ +#define DL_V_S2FMT 5 /* bits 5- 5: (-) format enabled flag */ +#define DL_V_S2FAULT 4 /* bits 4- 4: (+) drive fault flag */ +#define DL_V_S2FS 3 /* bits 3- 3: (+) first status flag */ +#define DL_V_S2SC 2 /* bits 2- 2: (+) seek error flag */ +#define DL_V_S2NR 1 /* bits 1- 1: (-) not ready flag */ +#define DL_V_S2BUSY 0 /* bits 0- 1: (-) drive busy flag */ + +#define DL_S2ERR (1 << DL_V_S2ERR) +#define DL_S2DTYP (1 << DL_V_S2DTYP) +#define DL_S2ATN (1 << DL_V_S2ATN) +#define DL_S2RO (1 << DL_V_S2RO) +#define DL_S2FMT (1 << DL_V_S2FMT) +#define DL_S2FAULT (1 << DL_V_S2FAULT) +#define DL_S2FS (1 << DL_V_S2FS) +#define DL_S2SC (1 << DL_V_S2SC) +#define DL_S2NR (1 << DL_V_S2NR) +#define DL_S2BUSY (1 << DL_V_S2BUSY) + +#define DL_S2STOPS (DL_S2FAULT | DL_S2SC | DL_S2NR) /* bits that stop drive access */ +#define DL_S2ERRORS (DL_S2FAULT | DL_S2SC | DL_S2NR | DL_S2BUSY) /* bits that set S2ERR */ +#define DL_S2CPS (DL_S2ATN | DL_S2FAULT | DL_S2FS | DL_S2SC) /* bits cleared by Controller Preset */ + + +/* Drive properties. + + The controller library supports four different disc drive models with these + properties: + + Drive Model Drive Sectors Heads per Cylinders Megabytes + Model ID Type per Head Cylinder per Drive per Drive + ----- ----- ----- -------- --------- --------- --------- + 7905 0 2 48 3 411 15 + 7906 1 0 48 4 411 20 + 7920 2 1 48 5 823 50 + 7925 3 3 64 9 823 120 + + The Drive Type is reported by the controller in the second status word + (Status-2) returned by the Request Status command. + + Model IDs are used in the unit flags to identify the unit's model. For the + autosizing feature to work, models must be assigned ascending IDs in order of + ascending drive sizes. +*/ + +#define D7905_MODEL 0 +#define D7905_SECTS 48 +#define D7905_HEADS 3 +#define D7905_CYLS 411 +#define D7905_TYPE (2 << DL_V_S2DTYP) +#define D7905_WORDS (D7905_SECTS * D7905_HEADS * D7905_CYLS * DL_WPSEC) + +#define D7906_MODEL 1 +#define D7906_SECTS 48 +#define D7906_HEADS 4 +#define D7906_CYLS 411 +#define D7906_TYPE (0 << DL_V_S2DTYP) +#define D7906_WORDS (D7906_SECTS * D7906_HEADS * D7906_CYLS * DL_WPSEC) + +#define D7920_MODEL 2 +#define D7920_SECTS 48 +#define D7920_HEADS 5 +#define D7920_CYLS 823 +#define D7920_TYPE (1 << DL_V_S2DTYP) +#define D7920_WORDS (D7920_SECTS * D7920_HEADS * D7920_CYLS * DL_WPSEC) + +#define D7925_MODEL 3 +#define D7925_SECTS 64 +#define D7925_HEADS 9 +#define D7925_CYLS 823 +#define D7925_TYPE (3 << DL_V_S2DTYP) +#define D7925_WORDS (D7925_SECTS * D7925_HEADS * D7925_CYLS * DL_WPSEC) + +#define MODEL_7905 SET_MODEL (D7905_MODEL) +#define MODEL_7906 SET_MODEL (D7906_MODEL) +#define MODEL_7920 SET_MODEL (D7920_MODEL) +#define MODEL_7925 SET_MODEL (D7925_MODEL) + + +/* Controller types (values must be powers of 2) */ + +typedef enum { + MAC = 1, + ICD = 2 + } CNTLR_TYPE; + + +/* Controller opcodes */ + +typedef enum { + cold_load_read = 000, + recalibrate = 001, + seek = 002, + request_status = 003, + request_sector_address = 004, + read = 005, + read_full_sector = 006, + verify = 007, + write = 010, + write_full_sector = 011, + clear = 012, + initialize = 013, + address_record = 014, + request_syndrome = 015, + read_with_offset = 016, + set_file_mask = 017, + read_without_verify = 022, + load_tio_register = 023, + request_disc_address = 024, + end = 025, + wakeup = 026, last_opcode = wakeup /* last opcode */ + } CNTLR_OPCODE; + +#define DL_OPCODE_MASK 037 + + +/* Controller command phases */ + +typedef enum { + start_phase, + data_phase, + end_phase, last_phase = end_phase /* last phase */ + } CNTLR_PHASE; + + +/* Controller status. + + Not all status values are returned by the library. The values not currently + returned are: + + - illegal_drive_type + - cylinder_miscompare + - head_sector_miscompare + - io_program_error + - sync_timeout + - correctable_data_error + - illegal_spare_access + - defective_track + - protected_track +*/ + +typedef enum { + normal_completion = 000, + illegal_opcode = 001, + unit_available = 002, + illegal_drive_type = 003, + cylinder_miscompare = 007, + uncorrectable_data_error = 010, + head_sector_miscompare = 011, + io_program_error = 012, + sync_timeout = 013, + end_of_cylinder = 014, + data_overrun = 016, + correctable_data_error = 017, + illegal_spare_access = 020, + defective_track = 021, + access_not_ready = 022, + status_2_error = 023, + protected_track = 026, + unit_unavailable = 027, + drive_attention = 037 + } CNTLR_STATUS; + + +/* Controller execution states */ + +typedef enum { + cntlr_idle, /* idle */ + cntlr_wait, /* command wait */ + cntlr_busy /* busy */ + } CNTLR_STATE; + + +/* Controller command classifications */ + +typedef enum { + class_invalid, /* invalid classification */ + class_read, /* read classification */ + class_write, /* write classification */ + class_control, /* control classification */ + class_status /* status classification */ + } CNTLR_CLASS; + + +/* Controller clear types */ + +typedef enum { + hard_clear, /* power-on/preset hard clear */ + soft_clear /* programmed soft clear */ + } CNTLR_CLEAR; + + +/* Controller state variables */ + +typedef struct { + CNTLR_TYPE type; /* controller type */ + CNTLR_STATE state; /* controller state */ + CNTLR_OPCODE opcode; /* controller opcode */ + CNTLR_STATUS status; /* controller status */ + FLIP_FLOP eoc; /* end-of-cylinder flag */ + FLIP_FLOP eod; /* end-of-data flag */ + uint32 spd_unit; /* S/P/D flags and unit number */ + uint32 file_mask; /* file mask */ + uint32 retry; /* retry counter */ + uint32 cylinder; /* cylinder address */ + uint32 head; /* head address */ + uint32 sector; /* sector address */ + uint32 verify_count; /* count of sectors to verify */ + uint32 poll_unit; /* last unit polled for attention */ + uint16 *buffer; /* data buffer pointer */ + uint32 index; /* data buffer index */ + uint32 length; /* data buffer length */ + UNIT *aux; /* auxiliary units (controller and timer) */ + int32 seek_time; /* seek delay time (per cylinder) */ + int32 sector_time; /* intersector delay time */ + int32 cmd_time; /* command start delay time */ + int32 data_time; /* data transfer delay time */ + int32 wait_time; /* command wait timeout */ + } CNTLR_VARS; + + +typedef CNTLR_VARS *CVPTR; /* pointer to controller state variables */ + +/* Controller state variables initialiation. + + Parameters are: + + ctype - type of the controller (CNTLR_TYPE) + bufptr - pointer to the data buffer + auxptr - pointer to the auxiliary units (MAC only; NULL for ICD) +*/ + +#define CNTLR_INIT(ctype,bufptr,auxptr) \ + (ctype), cntlr_idle, end, normal_completion, \ + CLEAR, CLEAR, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + (bufptr), 0, 0, (auxptr), \ + DL_SEEK_TIME, DL_SECTOR_TIME, \ + DL_CMD_TIME, DL_DATA_TIME, DL_WAIT_TIME + + + +/* Disc library global controller routines */ + +extern t_bool dl_prepare_command (CVPTR cvptr, UNIT *units, uint32 unit_limit); +extern UNIT *dl_start_command (CVPTR cvptr, UNIT *units, uint32 unit_limit); +extern void dl_end_command (CVPTR cvptr, CNTLR_STATUS status); +extern t_bool dl_poll_drives (CVPTR cvptr, UNIT *units, uint32 unit_limit); +extern t_stat dl_service_drive (CVPTR cvptr, UNIT *uptr); +extern t_stat dl_service_controller (CVPTR cvptr, UNIT *uptr); +extern t_stat dl_service_timer (CVPTR cvptr, UNIT *uptr); +extern void dl_idle_controller (CVPTR cvptr); +extern t_stat dl_clear_controller (CVPTR cvptr, UNIT *uptr, CNTLR_CLEAR clear_type); +extern t_stat dl_load_unload (CVPTR cvptr, UNIT *uptr, t_bool load); + +/* Disc library global utility routines */ + +extern CNTLR_CLASS dl_classify (CNTLR_VARS cntlr); +extern const char *dl_opcode_name (CNTLR_TYPE controller, CNTLR_OPCODE opcode); +extern const char *dl_phase_name (CNTLR_PHASE phase); + +/* Disc library global VM routines */ + +extern t_stat dl_attach (CVPTR cvptr, UNIT *uptr, char *cptr); +extern t_stat dl_detach (CVPTR cvptr, UNIT *uptr); +extern t_stat dl_set_model (UNIT *uptr, int32 value, char *cptr, void *desc); diff --git a/Visual Studio Projects/HP2100.vcproj b/Visual Studio Projects/HP2100.vcproj index d1f1068d..2978e132 100644 --- a/Visual Studio Projects/HP2100.vcproj +++ b/Visual Studio Projects/HP2100.vcproj @@ -225,6 +225,14 @@ RelativePath="..\HP2100\hp2100_cpu7.c" > + + + + @@ -289,6 +297,10 @@ RelativePath="..\HP2100\hp2100_sys.c" > + + @@ -338,6 +350,10 @@ RelativePath="hp2100_defs.h" > + + @@ -346,6 +362,10 @@ RelativePath="..\HP2100\hp2100_fp1.h" > + + diff --git a/descrip.mms b/descrip.mms index 7fd0c78a..f6d73e9c 100644 --- a/descrip.mms +++ b/descrip.mms @@ -334,7 +334,9 @@ HP2100_SOURCE1 = $(HP2100_DIR)HP2100_STDDEV.C,$(HP2100_DIR)HP2100_DP.C,\ $(HP2100_DIR)HP2100_CPU6.C,$(HP2100_DIR)HP2100_CPU7.C HP2100_LIB2 = $(LIB_DIR)HP2100L2-$(ARCH).OLB HP2100_SOURCE2 = $(HP2100_DIR)HP2100_FP1.C,$(HP2100_DIR)HP2100_BACI.C,\ - $(HP2100_DIR)HP2100_MPX.C,$(HP2100_DIR)HP2100_PIF.C + $(HP2100_DIR)HP2100_MPX.C,$(HP2100_DIR)HP2100_PIF.C \ + $(HP2100_DIR)HP2100_DI.C,$(HP2100_DIR)HP2100_DI_DA.C,\ + $(HP2100_DIR)HP_DISCLIB.C .IFDEF ALPHA_OR_IA64 HP2100_OPTIONS = /INCL=($(SIMH_DIR),$(HP2100_DIR))\ /DEF=($(CC_DEFS),"HAVE_INT64=1") diff --git a/makefile b/makefile index aaea003f..17ab28ee 100644 --- a/makefile +++ b/makefile @@ -470,7 +470,8 @@ HP2100 = ${HP2100D}/hp2100_stddev.c ${HP2100D}/hp2100_dp.c ${HP2100D}/hp2100_dq. ${HP2100D}/hp2100_cpu1.c ${HP2100D}/hp2100_cpu2.c ${HP2100D}/hp2100_cpu3.c \ ${HP2100D}/hp2100_cpu4.c ${HP2100D}/hp2100_cpu5.c ${HP2100D}/hp2100_cpu6.c \ ${HP2100D}/hp2100_cpu7.c ${HP2100D}/hp2100_fp1.c ${HP2100D}/hp2100_baci.c \ - ${HP2100D}/hp2100_mpx.c ${HP2100D}/hp2100_pif.c + ${HP2100D}/hp2100_mpx.c ${HP2100D}/hp2100_pif.c ${HP2100D}/hp2100_di.c \ + ${HP2100D}/hp2100_di_da.c ${HP2100D}/hp_disclib.c HP2100_OPT = -DHAVE_INT64 -I ${HP2100D}