From d29c6f43e376a36e40b25495ad84ec2232c9cb6d Mon Sep 17 00:00:00 2001 From: Bob Supnik Date: Mon, 16 May 2022 03:01:45 -0700 Subject: [PATCH] S3: Restore original S3 code to branch --- S3/haltguide.txt | 950 ++++++++++++++++++++++++ S3/readme_s3.txt | 78 ++ S3/s3_cd.c | 448 ++++++++++++ S3/s3_cpu.c | 1830 ++++++++++++++++++++++++++++++++++++++++++++++ S3/s3_defs.h | 94 +++ S3/s3_disk.c | 792 ++++++++++++++++++++ S3/s3_lp.c | 364 +++++++++ S3/s3_pkb.c | 313 ++++++++ S3/s3_sys.c | 950 ++++++++++++++++++++++++ S3/system3.txt | 472 ++++++++++++ 10 files changed, 6291 insertions(+) create mode 100644 S3/haltguide.txt create mode 100644 S3/readme_s3.txt create mode 100644 S3/s3_cd.c create mode 100644 S3/s3_cpu.c create mode 100644 S3/s3_defs.h create mode 100644 S3/s3_disk.c create mode 100644 S3/s3_lp.c create mode 100644 S3/s3_pkb.c create mode 100644 S3/s3_sys.c create mode 100644 S3/system3.txt diff --git a/S3/haltguide.txt b/S3/haltguide.txt new file mode 100644 index 00000000..24e90f86 --- /dev/null +++ b/S3/haltguide.txt @@ -0,0 +1,950 @@ + IBM System/3 Model 8/10 SCP + + ********** + Halt Guide + ********** + +This following list is my own reformatting and rewording of the +official IBM Halt Guide for the Model 8/10 SCP. + +The halts are those displayed on the message display unit. The +list is in alphabetical order for easy reference. + +When the system halts, the two 7-segment displays will display the +halt as listed here, and the system console (or printer if the log +device is the printer) will print the "SCP Message" below. To +respond to the halt, deposit one of the valid response numbers +(0 thru 3) into the SR, and then use the C command to continue. + +Unless otherwise stated, a response of 0 means to continue and accept +the error, 1 means to retry the operation or re-read the statement in +error, 2 means to cancel the job and retain added records (if any) and +3 means to end the job and discard any added records in files. + +This is a listing of those halts likely to be encountered using SCP on +the simuator, it is not the complete list of all possible halts. + +Halt SCP Message Description +---- ----------- ----------- + +00 Invalid response to another halt. Deposit a valid + value (0 thru 3) in SR. + +0A A 5448 Disk Unit not ready. + +0C 5448 Disk Equipment Check + +0E Permanent disk error during logging. + +0F ID0FXX 23 Invalid cylinder number on disk operation. + XX = Disk Drive. + +0H ID0HXX 23 Invalid sector number on disk operation + XX = Disk Drive. + +0Y IK0Y0X 123 3741 Error. X: 1=not ready 2=wrong mode + 3=parity error 5=record length error + +0 ID0 XX 23 Disk Data Check + XX = Disk Drive. + +0- ID0-XX Invalid disk operation code: Start cancels job. + XX = Disk Drive. + +10 3 No input file allocate, user error. + +11 0 23 Square root of a negative field + +12 0 23 Divide Overflow + +13 0 23 Divide by zero + +14 0 23 Varible index zero of out of range + +15 0 23 Sequenced table is out of sequence + +16 0 23 (RPG) OBject tables expected. /* Read. + +17 0 23 (RPG) Object table exceeds specified length + +18 0 23 (RPG) Terminal errors in source program. + +19 0 3 (RPG) Warning errors in source program. 0=continue. + +1A 3 (RPG) Out of core memory + +1C 23 Unidentified halt has been issued. Probable system + error. + +1E 0 3 (RPG) Demand file at end of file. + +1F 23 (RPG) End of file or end of extent. If during RPG + compilation, expand $SOURCE or $WORK. + +1H 0 23 Duplicate keys found during build of indexed file. + 0=skip this record and continue. + +1J 0 23 Out of sequence keys during build of indexed file. + 0=skip this record and continue. + +1L 0 23 Key changed during record update. User error. + 0=continue, do not update record. + +1P 01 Forms in printer need positioning. + +1U 123 No record found on direct or indexed file. + +1Y 0 23 (RPG) Invalid numeric data to DSPLY statement. + +1 0 3 Object program ready to punch tables. + +20 1 3 Disk Sort: Invalid header and no // SOURCE + +21 01 3 Disk Sort: Name on // SOURCE not found + +22 0 2 Disk Sort: Warning errors found. + +23 3 Disk Sort: Unrecoverable error. + +25 3 Disk Sort: Terminal errors in sort statements. + +27 0 Disk Sort: In debug mode, finished pass. + +2C 0 3 Disk Sort: No Input Records selected. + +2E 3 Disk Sort: Workfile too small. + +2F 23 Disk Sort: Output file too small. + +2L DT2LY2 3 Tape Record too large to process. + DT2LY7 3 No FILE statement for tape file open. + DT2LY9 3 No enough storage for tape operation + DT2LTC 3 Invalid tape header length + DT2LYF 123 Incorrect block length read from tape + +2P Permanent tape error. + +2U 12 Tape unit is write protected. + +2Y 3 Invalid device specification in object. + +2- 0 3 First statement was not a Tape Sort header. + +30 EG30 3 Space not available on R1 or F1. + UB30A1 0 3 Active files exist on output disk + UB30AF 0 3 Active files exist on 5448 disk + UB30H1 0 3 Wrong capacity or uninitialized pack + UB30NS 3 No 5448 disk for $PCOPY + UB30TP 0 3 Pack change required. + UC30AF 3 Active or system files on target + UC30BD 3 Volume label cannot be read + UC30SP 3 Not enough space for work file + UP30AF 3 Active or system files on target + +31 UI31AF 0 3 Active or system files on target + 0=proceed to next unit to initialize + UI30WP 01 3 Wrong pack, name does not match. + +32 UB32Bx 01 3 5444 pack is not a $PCOPY backup pack. + UB32NP 01 3 Unit specified is not a $PCOPY pack. + UC32BD 3 FROM pack is a TO pack from an interrupted + COPYPACK run. + UC32BP 3 Output pack is a $PCOPY output pack. + Must be initialized or reset by a RESET + statement to be used. + UC32DS 3 Packs FROM and TO are different sizes. + +33 UI33PU 0 3 Pack defective, cannot be initialized. + +34 Ux34 1 3 Keyword in a utility control statement is invalid. + +35 UC35xx 1 3 Error in $COPY or $KCOPY control statement. + +36 UI36CE 0 3 CE track defective on unit F1. + +37 UC37xx 0 3 Pack change required. xx: FP=mount on R1, + IP=pack on COPYIN, OP=pack on COPYO. + +38 UA38XX 0 3 Wrong pack mounted. + UB38DA 01 3 Dates do not match. + UB38DM 01 3 2nd 5444 pack not from same backup set as 1st. + UB38IP 01 3 PACKIN keyword pack not same as pack mounted. + UB38OP 01 3 PACKO keyword not same as pack mounted. + +3A UC3Axx 3 Key out of sequence (DP), invalid high key (HK), + out of space to copy file (XE), or disk I/O error. + +3C UC3CCS 3 COPYFILE out of core. + UC3CNF 3 Module not found, name is logged as R XXXXXX. + +3E UC3EOX 0 3 COPYFILE output not as big as size of input. + +3F UC3Fxx 1 3 Error in COPYFILE statement. + +3J UC3Fxx 3 Invalid specification for Copy/Dump. + +3P UC3Pxx 1 3 Error in COPYPACK, RESET, or LABELS statement. + +3Y UI3YIS 0 3 Requested secondary init when primary required. + +3 UI3 xx 1 3 Error in VOL statement. + +40 DD40 3 File has been referenced as an output or add file + and the file is already allocated. + +4A DD4A 3 File had already been opened and is re-opened. + +4C DD4C 3 Multivolumne file spec error. + +4E DD4E 3 FILE indicates a multivolule file being built, + but program compiled for single volume. + +4F DD4F 3 Print buffers not aligned in program. + +4H DD4H 0 3 Unordered load specified for ISAM. Ordered load + must be specified on RPG file specs. + +4J DD4J 3 All file specs have been checked and there were + errors. + +4L DD4L 3 Referenced file already allocated. + +4P DD4P 3 Prgram/FILE statement mismatch. + +4U DD4U 3 File referenced as update, already allocated. + +4Y DD4Y 3 File has an incorrect device specification. + +4 DD4 3 No FILE specification for referenced file. + +4' DD4' 3 Attempting reference to a file in 2 levels, one or + both using RETAIN-S. + +50 UA50ID 2 Bad track which can't be reassigned. + +51 UR51 12 Can't use Alternate Track program in procedure. + +52 ML52 12 EOJ for Card List program. + +53 IU53 1 3 Number of VOL statements does not agree with number + of units on UIN statement. + +54 EO54 3 End-of-file. + +55 UF55xx 1 3 Error in SCRATCH or REMOVE statement. + +56 UA56TS 0 3 ASSIGN track is over disk capacity. + UA56XX 0 3 Unit specified is uninitialized. + +57 UF57WP 01 3 File delete program. Wrong pack is mounted. + 0: Mount correct pack and continue. 1: + correct statement and retry. + +5A UA5Axx 012 Alternate track assignment error. + +5C MR5Cxx 1 3 Invalid reformat specs. + +5F UF5Fxx 1 3 Error in DISPLAY statement. + +5H UA5HEU 0123 Primary track is still defective. + +5L UF5LAF 0 3 PRogram try to delete files that are being used by + another program. + UF5LNF 0 3 File not found. + UF5LTM 23 Too many files specified. Max is 40. + +5U UI5Uxx 1 3 Error in UIN statement. + +5Y UR5Yxx 1 3 Error in REBUILD statment. + +5 UA5 xx 1 3 Error in ALT statement. + +5- 3 Tape Sort error occurred. + +5' UF5'N1 0 3 Pack cannot be used. Not inited. + UF5'NU 0 3 Pack was used as TO pack on a COPYPACK job + that required early termination. Can only + be used for another COPYPACK job. + +60 LM60SY 0 3 Cannot remove or change library size on pack + from which $MAINT was loaded. + +61 LM61EP 0 3 Trying to copy a system to a library that is + 1) not empty, 2) not allocated with large enough + SWA, or 3) not allocated with enough space. + LM61NS 0 3 System does not exist on FROM pack. + +62 LM62CS 01 3 Check Sum error. + LM62DR Can't determine if REMOVE is data or control. + LM62EF FROM, TO, or AFTER statement does not exist or + is out of sequence. + LM62ND NO data records following INSERT or REPLACE. + LM62SQ Records are out of sequence. + LM62TP Incorrect type record. + +63 LM63DE 0 3 Directory entry error. Name can't be found or + attributes don't match, or attempt to remove + dir entry with MODIFY. + +64 LM64DS 0 3 Syntax error in ALLOCATE. + +65 LM65UN 0 3 Pack not properly initialized. + +66 LA66xx 3 Error with LOAD * function. + +67 EL67NL 0 3 Library does not exist. + +68 EL68DF 0 3 No room in library or directory. + +69 XX69HE 3 Disk I/O Error while using library. + +6A LM6Axx 1 3 $MAINT has detected a syntax error on a control + statement. xx gives a hint of what might be wrong. + AL: SOURCE or OBJECT missing or invalid + AZ: SYSTEM missing or invalid + D2: FROM, TO, or WORK is R2 or F2, not available + DK: Duplicate keyword + DS: Invalid DIRSIZE + FL: Invalid or missing FILE keyword + FM: Invalid or missing FROM keyword + IK: Invalid keyword + IN: Invalid INCR keyword + IS: first 3 columns must be // blank + IV: Invalid statement identifier + LB: Invalid LIBRARY keyword + LS: Invalid LIST keyword + NK: No keywords + NM: Invalid NAME keyword + NU: Invalid NEWNAME keyword + OM: Invalid OMIT keyword + RL: Invalid RECL keyword + RS: Invalid RESER keyword + RT: Invalid RETAIN keyword + SF: INvalid SEQFLD keyword + SQ: Invalid FROM, TO, or AFTER in MODIFY mode + XC: Invalid record. + XD: Duplicate keyword + XF: $$SYFG could not be found. + XL: LIBRARY keyword missing + XM: NAME keyword missing + XN: NAME parameter is invalid + XP: Library does not exist on this pack + XS: Syntax error + XT: Invalid library type + XV: INvalid operation + +6C LM6CSP 0 3 Not enough space on pack. + LM6CSW 0 3 Space not available for work file. + +6E LM6EOF 0 3 Overflow in seq field during RESER. + LM6EDP 0 3 Entry with same name already exists in library. + +6H EL6HDT 0 3 Trying to replace perm with temp entry. + LM6HDP 0 3 NEWNAME is already in library. + +6J LM6JCC 0 3 Control statements are missing. + +6L UA6L 3 Log device is required for this program. + +6Y LM6YNN 1 3 No NEWNAME when copying to same library. + +6 LM6 BC 3 Invalid character in source record. + LM6 CM 0 3 Invalid object deck. + LM6 ND 0 3 No data between COPY and CEND. + +6- LM6-BC 01 Entry containing a blank card being placed in + library. 0: accept, 1: skip and read next card. + +6' LM6'CE 1 3 // CEND expected but not found. 1: Retry, + provide CEND. NOTE: For option 3, if a module + was being replaced, it may have been deleted but + new module not loaded. + +70 CR70 3 Too many overrides for procedure. Max is 25. + +71 CR71 0 3 OCL Syntax Error. + +73 CR73 0 // PARTITION given in invalid location. + +74 CR74 3 /& between LOAD and RUN or CALL and RUN. + +75 CR75 23 Extraneous statement. + +76 CR76 0 3 // Missing from OCL statement. + +77 CR77 23 Invalid OCL statement identifier. + +78 CR78 0 3 Unknown OCL keyword. + +79 CR79 23 Continuation expected but not received. + +7A CR7A 3 A second LOAD or CALL found before run, or a + CALL in procedure overrides. + +7C CR7C 0 3 // COMPILE found between jobs. + +7E CR7E 0 3 // DATE found between jobs. 0: Ignore and continue. + +7F CR7F 0 3 // FILE found between jobs. Must go between + // LOAD or // CALL and // RUN statements. + +7H CR7H 0 3 // SWITCH found between jobs. + +7J CR7J 23 // READER found between LOAD or CALL and RUN. + +7L CI7Lxx 23 Error when reading a tape file. + +7P New print chain expected. Load it and press START. + +7U CR7U 3 RUN statement not preceeded by LOAD or CALL. + +7Y CI7Yxx 23 Error outputing a tape file. + +7 CR7 3 Too many utility control statements, max is 25. + +7- CR7- 0 // PARTITION was read but system does not support + Dual Programming. + +7' Error during tape processing. + +80 CR80 0 // DATE card has not been entered. + +81 CR81 23 Error in LOAD statement. + +83 CR83 23 Error in LOAD * statement. + +84 CR84 23 Error in CALL statement + +85 CR85 23 Second SWITCH statement found. + +86 CR86 23 Invalid paramter in switch statement. + +88 CR88 1 3 Procedure not found. + +89 CR89 01 // DATE has already been given. + 0 - accept the new date as the date. + 1 - leave the old date as the current date. + +8A CR8A01 0 Invalid date specified. + CR8A02 0 DATE parameter missing. + +8C CR8C 23 Second DATE found. + +8E CR8E01 23 Date specified incorrectly. + +8F CR8Fxx 23 Invalid BSCA statement. + +8H CR8H 3 More than 9 levels of procedures have been called. + +8J CR8J 0 Invalid // READER parameter. + +8L CR8L 0 Desired system input device being used by other + program. + +8P CR8P 0 Output device not defined. + +8U CU8UIP 23 Invalid HIKEY in FILE statement: non-numeric. + CR8UKL Parameter length mismatch. + CR8ULO Key greater than 29. + CR8UPL HIKEY-P greater than 15. + CR8USQ HIKEY parameters not in sequence. + +8Y CR8Y 0 Not logging can be done. Log turned off by + other program level. + +8- CR8- 0 3 Logging requested but cannot be done. + +90 CR90 0 // PAUSE statement read. Check printer or console + for instructions and continue. PAUSE was outside + LOAD and RUN. + +91 CR90 0 // PAUSE statement read. Check printer or console + for instructions and continue. PAUSE was inside + LOAD and RUN. + +92 CR92 23 COMPILE already recieved for this job. + +93 CR93 23 Error in COMPILE statement. + +94 CR94 23 Error in COMPILE statement. + +95 CR95 23 Error in COMPILE statement. + +96 CR96 0 23 System error. An OCL error was found, but the system + cannot resolve the error. + +97 CR97 0 Error in LOG statement. + +98 CR98 23 Error in LOG statement. + +99 CR99 0 23 Error in LOG statement. + +9A CR9A 23 Indicated action on last OCL statement read will + be ignored due to previous errors detected. + +9C CR9Cxx 123 Incorrect tape volume online. + +9E CR9E 0 Logging device being used by other program level. + +9F CR9F 0 23 Logging device in use by other program. + +9H CR9H 23 Log device in use. + +9J CR9J 0 Error in FORMS statement. + +9L CR9L 0 23 Error in FORMS statement. + +9P CR9P 23 Error in FORMS statement. + +9U CR9U 0 3 Other program has gotten a // IMAGE or other + program level is using the printer. + +9Y CR9Y 0 23 Logging device not sysgenned or CCP has it. + +9 CR9 0 23 Same as 9Y. + +9- CR9- 0 3 Other program level received a // FORMS or + other level using the printer. + +9' CR9' 0 Same as 9Y. + +A0 CRa0xx 23 Syntax error in FILE statement. + +A1 CRA1xx 23 Keyword error in FILE statement. + +A2 CRA2xx 23 Parameter error on FILE statement. + xx gives parameter: + 01 NAME, 02 UNIT, 03 PACK, 04 LABEL, + 05 RETAIN, 06 DATE, 07 RECORDS, 08 TRACKS, + 09 LOCATION, AS ASCII, BL BLKL, CV CONVERT, + DF DEFER, DN DENSITY, EN END, PT PARITY, + RC RECL, RF RECFM, RL REEL, SP SPLIT, + TN TRANSLATE. + +A3 CRA3xx 23 Missing Parameter on FILE statement, xx = + NN: NAME, NP: PACK, NU: UNIT, OP: no + parameters. + +A4 CRA4xx 23 Invalid parameter combination in FILE statement: + AS: ASCII-YES and RECFM-D/DB on 7-track tape + AV: ASCII-YES and RECFB-V/VB + AY: RECFM-D/DB without ASCII-YES + CT: CONVERT-ON and TRANSLATE + DI: UNIT says tape but disk parameters given + DN: DENSITY-800 not supported. + FS: RECFM is fexed and block or rec len less than 18 + IL: Incorrected RECL or BLKL for RECFM + IP: SPLIT or LOCATION used with RECORDS / TRACKS. + IR: LABEL, DATE or RETAIN wirh REEL-NL or REEL-NS + NS: Not all units are 7-track + PC: CONVERT-ON and PARITY-EVEN + RC: CONVERT-ON not given with RECFM-V/VB for 7-track + SD: DENSITY-1600 invalid for 7-track + SL: LOCATION missing or invalid for SPLIT. + SM: SPLIT invalid for multivolume files. + ST: 7-track paras with 9-track unit + SU: SPlit can't be used with 5444 + TL: TRACKS/LOCATION invalid with unit + TP: UNIT is disk but tape paras given + TR: TRACKS and RECORDS both given + +A6 CRA6xx 23 Error in FILE statement for multivolumne files. + +A7 CRA7xx 23 Error in IMAGE statement. + +A8 CRA8xx 0 Error in IMAGE statements o disk. + +A9 CRA9xx 0 23 Same as A8. + +AA CRAAxx 23 Same as A8. + +AC CRAC 0 Invalid hex character in chain image. + +AE CRAE 0 23 Same as AC. + +AF CRAF 23 Same as AC. + +AH CRAH 0 Error in IMAGE statement. + +AJ CRAJ 0 23 Same as AH + +AL CRALxx 0 Error in PARTITION statement. + +AP CRMN 0 3 Either reocvery option has has been selected during + a job, or OCL errors have occurred for this job. + 0: Continue iwth next job, or no data cards in + reader for this job, otherwise, 3 to cancel. + +AU CRAUxx 23 Error in PARTITION statement. + +A CRA 23 Total number of volumes for a FILE statement + exceeds 40 (!). + +A- CRA-xx 0 23 Error in PARTITION statement. + +A' CRA' 3 No space remaining is System work area. Too many + FILE statements are in this job. + +C1-C9 IFC1 123 1442 Check, various causes. + +CL UDCLxx 1 3 5445 Data Interchange Utility error + +E7 DKE7 0 3 Incorrect record length for attached 3741 + +E8 UTE8xx 1 3 Error in Tape Init VOL statement. + +E9 UTE9xy 0 3 Error during Tape Init Processing. + +F8 DDF8 3 RPG--LIne counter specs omitted and skip past + page size for printer. + +F9 CIF9xy 23 Tape drive not available, x = drive #. + +FA CIFA 3 Program requesting Data Recorder, unsupported. + +FC CIFC 3 Program requesting CRT, unsupported. + +FE DDFE 0 3 Program requesting line line on printer that + exceeds sysgen value. + +FF RPQ routine error. Press start to continue. + +FH CIFH 123 BSCA line not supported. + +FJ CIFJ01 123 1442 not supported but requested + CIFJ02 123 3741 not supported but requested + +FL CIFL 123 Printer/keyboard not supported or unavailable. + +FP CIFP 123 Printer not supported or allocated to other level + +FU CIFU 123 MFCU not supported or allocated to other level + +FY CIFY 23 Device is not supported or in use. + +F CIF 23 Conflict with a resource being used by other level. + +H0-H9 0 23 RPG Programmed halt indicator is on. + +HA CIHA 3 Out of space on $SOURCE during compile. + +HC CIHC 3 Program given on LOAD statement not found. + +HE Hardware error. Simulator has messed up. + +HF CIHF 0 3 // COMPILE read but not required. + +HJ CIHJ01 1 3 Program not found on removable unit, 1: + mount new unit and retry. + CIHJ02 3 Program not found, but removable unit in use. + +HL CIHL 3 Inquiry request made but program is wrong type. + +HP CIHP 3 Insufficient main storage for program. + LMHP 3 $MAINT function out of storage. NOTE: After the + cancel, IPL from the system pack or the pack will + be unusable. + +HU CIHUxx 3 Source program not found on disk. IF a 1 option + is present, you can mount a new removeable pack. + +HY CCHYNN 0 A checkpoint is received and accepted. + +H CCH NN 0 23 A restart has been requested. + +H' CIH' 3 An uninitialized pack has been referenced. + +J0-J9 123 Record with specified match field out of sequence. + This is an RPG error, the 2nd digit indicates which + RPG file statement the error applies to in the + source program. 0=greater than statement 9, + otherwise indicates the file statement number. + +JA CIJA 3 Trying to laod a program that requires or allows + inquiry while another inquiry program is running + in the other level. + +JC CIJCxx 3 Program cannot be run for this reason (xx): + 01: Must be dedicated and other level active + 02: Program in other level must be dedicated + 03: $$RSTR cannot run in level 2 + 04: CHeckpointed program not allowed in level 2 + 05: Program can't run while checkpoint active + +JE CIJE 0 3 Level 1 partition too small. + +JF CIJF 3 Attempt to start inquiry program but keyboard + in use. + +JH CIJF 3 Attempt to start program which allows interrupts in + level 2. + +JJ CIJJ 3 No object library on pack requested for load. + +JL CIJL 3 Not enough storage for program. DPF only. + +JP System input device in use by other level. + +JU 0123 Cancel request made from interrupt key. 0: ignore + 1: continue, request ignored + +JY CIJYRD 0 2 Inquiry request made and accepted. + +J- 3 Attempt to run a CCP program, but CCP not running. + +J' 01 3 Inquiry request is completed, interrupted program + can now resume. + +L0-L9 123 RPG. Unidentified record, 2nd digit gives file + statement number in source program 1-9, 0 means + greater than 9. Can also occur if record is out + of sequence. + +LA CILA 23 Too little storage for number of files in program. + +LC CILC 23 Too little storage for requested allocation. + +LE CILE 23 No FILE or an incorrect FILE for a file requested + by current program. + +LH CILH 23 No space given for an output file on FILE statement. + +LJ CILJ 23 Attempt to output to existing permanent file. + +LL CILL 0 23 Attempt to output over an existing temporary file. + +LP CILP 23 File already exists. + +LU CILU 123 Pack name requested but wrong pack mounted. 1: + retry after mounting correct pack. + +LY CILYxx 23 Attempt to allocate space that isn't available. + xx=02 means space not available in split cylinder + area. + +L LML CP 01 3 $MAINT detected attempt to modify a program on + a pack with an active checkpoint. + +L- CIL- 3 Attempt to add a split cylinder to a split cyl + file while other level is fiddling with a split + cylinder file. + +L' CIl' 23 Trying to allocate a split cylinder file before + allocating the first split cylinder file in a group. + +P1-P8 Printer hardware errors, should not occur in sim. + +PC IPPC 0 23 Unprintable character. + +PH CIPH 23 LOCATION plus TRACKS goes past end of pack. + +PJ CIPJxx 1 A Pack is to be remounted, pack name printed before + half code, xx= unit. + +PU CIPU 3 Duplicate file names in the FILE statements. + +PY CIPY01 3 ISAM file requires at least 2 tracks. + CIPY02 3 ISAM file can't be split cylinder. + +P' CIP'xx 23 Too many scratch or work files. + +U0-U9 0123 RPG. Unidentified record in file, 2nd digit of + halt is file statement in RPG source, 0= greater + than 9. + +UA CIUA 3 Attempt to create a multivolume file in + invalid. + +UC CIUC 3 The printed actived file cannot be found in the + list of scratch files. + +UE CIUExx 1 3 PACK parameter does not match pack name on unit. + xx = Unit referenced. + 1 = Mount another pack and continue. + +UF CIUF 3 Disk file referenced by name and date not found. + +UH CIUH 3 Attempt to create multivolume file failed, + because name alreayd exists. + +UJ CIUJ 3 A LOCATION was specified for an existing disk file + and the file exists but not at that location. + +UL CIUL 3 File on // FILE statement not found, and no size + in TRACKS or RECORDS was given. + +UP CIUP 3 Permanent file referenced with RETAIN-S + +UU CIUU 3 Disk Pack not available. + +UY CIUY 3 File is a System/3 BASIC file which must be unique. + +U CIU 3 Existing file: TRACKS/RECORDS or LOCATION mismatch. + +U- General CCP halt. Press start to see subhalt. + Refer to CCP manual for more info. + +U' CIU' 23 VTOC is full, or more than 2 multivolume files per + pack, or more than 2 ISAM files using HIKEY + parameter. + +YH CRYH 0 3 Cards are being punched, but card read from + reader was not blank. This means you are trying to + punch with a file attached to the CDR device. + Unattach the file and take the zero option. + + 0 (blank 0) FILE WRITE switch in off position. + + 1 (blank 1) Permanent DIsk I/O Error + + 2 RC 211 3 COBOL. Out of room on $WORK. + RC 212 3 Out of room on $SOURCE. + RC 213 3 Out of room on $WORKX. + RC 214 3 Subprogram name table greater than 20. + RC 219 0 3 C or E level diagnostics during compile. + RC 2A1 23 Subscript invalid + RC 2A2 23 Negative exponent or 0 degrees in program + RC 2F1 23 MFCU File not open or opened improperly + RC 2F2 23 1442 File not open or opened improperly + RC 2F3 23 1403/5203 File not open or opened improperly + RC 2F4 23 5444 Disk File not open or opened improperly + RC 2F5 23 5444 File not open or opened improperly + RC 2F7 23 5444 File not open or opened improperly + RC 2F8 23 Tape File not open or opened improperly + RC 2H1 23 OPEN attempted after CLOSE WITH LOCK + RC 2H2 23 Error during ACCEPT + RC 2H3 23 $$STOP not found + RC 2H4 23 CHeckpoint could not be taken. + RC 2H5 23 $$STIC not found for ACCEPT + RC 2H6 23 Parameter mismatch CALL and USING + RC 2H7 23 ACCEPT after /& read + RC 2H8 23 OPEN for a file already OPEN + RC 2 0 3 Too little core for compile + RC 2 1 3 PROCEDURE or DATA division not found. + RC 2 3 3 Program has more than 65535 statements (!) + RC 2 4 3 Source name on COMPILE statement not found + + 3 R 3XX 0 3 COBOL Stop literal. XX is user-specified. + 0 continues program 3 cancels. + + 4 VF 4NF 3 Program not found. Program library and + not printed before halt message. + + 6 RF 6XX 0 23 FORTRAN stop statement. + + 7 RF 701 23 Source member on COMPILE not found + RF 702 23 Object program too large for core. + + 8 CS 8 1 3 System input device allocated to other level. + + 9 CS 9 1 3 System input device has an error. This usually + means the card hopper is empty (i.e. EOF on the + file attached to the reader but SCP wants more + input). + + A DC A 123 Number of characters entered from keyboard + incorrect. + + C DD C 0 23 Unprintable character for printer/keyboard. + + E DC E 123 Hardware error, PKB + + F DC F 0 23 End of forms, PKB + + L DD L 0 3 Records with duplicate keys have been loaded + into ISAM file. Each dup key is logged followed + by blank P halt. 0: continue. Index will + contain duplicate keys. 3: cancel, file is + not usable, reload it. + + P DD P 0 3 Duplicate key encountered. The key is printed + on the log. 0: continue, halt will recur for + any other duplicates, then blank L appears. + + U DD U 3 Disk I/O error while sorting ISAM index. + + Y DD Y 3 System error during file termination. + +-0 DD-0XX 3 ISAM multivolume file being used and high key + not found for current columme, or does not agree + with HIKEY spec. XX=unit number. + +-1 DD-1XX 123 Halt -P occurred and option 0 taken. But, the + pack mounted is not a part of volume set. + +-2 DD-2XX 123 Multivolume load sequence error. + +-3 DD-3XX 123 Multivolume load sequence error. + +-4 DD-4XX 0123 Warning that one or more volumes are about to be + bypassed. + +-5 DD-5XX 123 Multivolume file not found. 1: mount correct pack. + +-6 DD-6XX 0 23 Warning. ENd of volume and HIKEY not found. + +-7 DD-7XX 1 3 -A halt and option 1 taken. But the pack referenced + does not match pack name. + +-8 DD-8XX 3 Multivolume file referenced but file isn't + multivolume. + +-9 DD-9XX 3 Add to a multivolumen file, but last pack not + mounted. + +-A DD-AXX 1 3 Add to existing multivolume filebut no room. + +-C DD-CXX 3 Multivolume file error. Probably out of sequence + volume mounts. + +-E DD-EXX 123 Next volume cannot be processed, because the + location is not available or space is not available + or there are scratch files on the pack. + +-F DD-Fxx 123 Finished a volume, next cannot be processed, mount + the correct pack or cancel. + +-H DD-Hxx 3 HIKEY length does not match file. + +-J DD-Jxx 01 3 First volume referenced is not volumme 1. 0: + continue with this volume, 1: mount another pack. + +-L DD-Lxx 3 Output to multivolume, but file isn't multivolume + or referenced volume isn't first one of set. + +-P DD-PXX 0123 Mount next volume. XX=unit number. 0: continue + bypassing volumes, 1: mount next volume. + +-U DD-UXX 1 3 Halt -J just occurred and 0 or 1 taken. But the + pack name is incorrect or the file isn't found. + +- DD- 123 Multivolume key error. Key too low or high for + volume. + +-' DD-' 123 Sequential add to multivolume file, but HIKEY + record missing on previous volume. + +'0 GM'0DE 3 SYSGEN. I/O Error on reader. + GM'0EX 3 SYSGEN. End of extent on MACOUT or $SOURCE. + GM'0IC 1 3 SYSGEN. Option dependent on a preceding option, the + preceding one was omitted or invalid. + GM'0ID 1 3 SYSGEN invalid delimiter. + GM'0IK 1 3 SYSGEN invalid keyword. + GM'0IR 1 3 SYSGEN invalid option. + GM'0IS 1 3 SYSGEN sequence error. + GM'0NF 1 3 SYSGEN entry in cols 8-12 not found. + GM'0NS 3 SYSGEN Requested source program not found. + GM'0EM 1 3 SYSGEN. END statement not found. + GM'0NP 3 SYSGEN. Module $SGXP2, $SGXP3, $SGXP4, $SGXP5, or + $SGXP6 missing for sysgen, or $MPXP2, $MPXP3 or + $MPXP4 missing for macro processor. + +'1 GG'1 3 System Generation Errors. + +'2 0 3 Error during macro processor run. + +'3 3 Invalid 5445 disk label record. + +'4 GG'4EX 3 Out of room on Sysgen, or disk error. + +----------------------- End of haltguide.txt --------------------------- diff --git a/S3/readme_s3.txt b/S3/readme_s3.txt new file mode 100644 index 00000000..37afdd9c --- /dev/null +++ b/S3/readme_s3.txt @@ -0,0 +1,78 @@ + Welcome to the IBM System/3 Model 10 SIMH simulator. + --------------------------------------------------- + + To compile under linux: + + cc s3*.c scp*.c sim_rev.c -o s3 + + This code can be compiled and run as a console application using + Microsoft Visual C++. + + + + To IPL the provided SCP distribution disk: + + ./s3 + sim> at r1 m10scp.dsk + sim> at f1 f1f1f1.dsk + sim> at lpt print.txt + sim> d sr 5471 + sim> boot r1 + + + // DATE 06/14/01 + // NOHALT + // LOAD $MAINT,R1 + // RUN + // COPY FROM-R1,LIBRARY-ALL,NAME-DIR,TO-PRINT + // END + + + (A printout of the libraries and directories on the SCP DTR + disk will be in the file print.txt) + + + The text file "system3.txt" gives details on the simulators + implementation of System/3 hardware. + + A write up on the use of the SCP and the OCL job control language is + in the text file "userguide.txt". This includes examples of using the + utility programs, and a tutorial guiding you thru a sysgen. + + A nearly complete listing of all possible SCP halts is in the + document "haltguide.txt". + + IMPORTANT NOTES: + + 1) How to correct typing errors when using the System/3 console: + If you make an error, press ESC, which will cancel the current + line being typed and print a quote in position 1. Then you + can use CTRL/R to retype characters up until the error, then + type correctly. Or simply retype the line. BACKSPACE DOES NOT + WORK with the SCP. + + 2) While the simulator allows disk images to be independently + attached to any disk unit, on the real hardware R1 and F1 were on + a single spindle, and R2 and F2 likewise. It is not possible using + SCP to attach R1 without attaching a disk image to F1 also, because + SCP will always look at F1 even when IPLed off R1. + + The OS distributed with the simulator is version 16 of the Model + 10 SCP. This is sysgenned with support only for R1 and F1. If you + do a sysgen to support R2 amd F2 also, you must have images attached + to all 4 disks when you IPL, because SCP looks at all drives when + it starts up, and you will get an "Unattached Unit" error if you + fail to have one attached. + + 3) The 1442 card reader had in reality one card input hopper + and two stackers. This means the same path is used for reading and + punching cards. When punching cards, SCP does a read operation + and inspects the card read for blanks, and if it is not blank, + issues a YH halt. SCP will not punch data onto non-blank cards. + This feature causes problems in the simulator, and as a result + if you punch cards from SCP, YOU MUST not have any file attached + to the CDR device. Leaving this device unattached presents an + infinite supply of blank cards to SCP for punching. + + + -- End of README_S3.txt -- diff --git a/S3/s3_cd.c b/S3/s3_cd.c new file mode 100644 index 00000000..9dd3a93e --- /dev/null +++ b/S3/s3_cd.c @@ -0,0 +1,448 @@ +/* s3_cd.c: IBM 1442 card reader/punch + + Copyright (c) 2001-2012, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + cdr card reader + cdp card punch + cdp2 card punch stacker 2 + + 19-Mar-12 RMS Fixed declaration of conversion tables (Mark Pizzolato) + 25-Apr-03 RMS Revised for extended file support + 08-Oct-02 RMS Added impossible function catcher + + Normally, cards are represented as ASCII text streams terminated by newlines. + This allows cards to be created and edited as normal files. Set the EBCDIC + flag on the card unit allows cards to be read or punched in EBCDIC format, + suitable for binary data. +*/ + +#include "s3_defs.h" +#include + +extern uint8 M[]; +extern unsigned char ebcdic_to_ascii[]; +extern unsigned char ascii_to_ebcdic[]; +int32 s1sel, s2sel; +char rbuf[CBUFSIZE]; /* > CDR_WIDTH */ +t_stat cdr_svc (UNIT *uptr); +t_stat cdr_boot (int32 unitno, DEVICE *dptr); +t_stat cdr_attach (UNIT *uptr, char *cptr); +t_stat cd_reset (DEVICE *dptr); +t_stat read_card (int32 ilnt, int32 mod); +t_stat punch_card (int32 ilnt, int32 mod); + +int32 DAR; /* Data address register */ +int32 LCR; /* Length Count Register */ +int32 lastcard = 0; /* Last card switch */ +int32 carderr = 0; /* Error switch */ +int32 pcherror = 0; /* Punch error */ +int32 notready = 0; /* Not ready error */ +int32 cdr_ebcdic = 0; /* EBCDIC mode on reader */ +int32 cdp_ebcdic = 0; /* EBCDIC mode on punch */ + +extern int32 GetMem(int32 addr); +extern int32 PutMem(int32 addr, int32 data); + +/* Card reader data structures + + cdr_dev CDR descriptor + cdr_unit CDR unit descriptor + cdr_reg CDR register list +*/ + +UNIT cdr_unit = { UDATA (&cdr_svc, UNIT_SEQ+UNIT_ATTABLE, 0), 100 }; + +REG cdr_reg[] = { + { FLDATA (LAST, lastcard, 0) }, + { FLDATA (ERR, carderr, 0) }, + { FLDATA (NOTRDY, notready, 0) }, + { HRDATA (DAR, DAR, 16) }, + { HRDATA (LCR, LCR, 16) }, + { FLDATA (EBCDIC, cdr_ebcdic, 0) }, + { FLDATA (S2, s2sel, 0) }, + { DRDATA (POS, cdr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, cdr_unit.wait, 24), PV_LEFT }, + { BRDATA (BUF, rbuf, 8, 8, CDR_WIDTH) }, + { NULL } +}; + +DEVICE cdr_dev = { + "CDR", &cdr_unit, cdr_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &cd_reset, + &cdr_boot, &cdr_attach, NULL +}; + +/* CDP data structures + + cdp_dev CDP device descriptor + cdp_unit CDP unit descriptor + cdp_reg CDP register list +*/ + +UNIT cdp_unit = { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }; + +REG cdp_reg[] = { + { FLDATA (ERR, pcherror, 0) }, + { FLDATA (EBCDIC, cdp_ebcdic, 0) }, + { FLDATA (S2, s2sel, 0) }, + { FLDATA (NOTRDY, notready, 0) }, + { HRDATA (DAR, DAR, 16) }, + { HRDATA (LCR, LCR, 16) }, + { DRDATA (POS, cdp_unit.pos, T_ADDR_W), PV_LEFT }, + { NULL } +}; + +DEVICE cdp_dev = { + "CDP", &cdp_unit, cdp_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &cd_reset, + NULL, NULL, NULL +}; + +/* Stacker data structures + + stack_dev STACK device descriptor + stack_unit STACK unit descriptors + stack_reg STACK register list +*/ + +UNIT stack_unit[] = { + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) } +}; + +REG stack_reg[] = { + { DRDATA (POS0, stack_unit[0].pos, 32), PV_LEFT }, + { NULL } +}; + +DEVICE stack_dev = { + "CDP2", stack_unit, stack_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &cd_reset, + NULL, NULL, NULL +}; + + +/* -------------------------------------------------------------------- */ + +/* 1442: master routine */ + +int32 crd (int32 op, int32 m, int32 n, int32 data) +{ + int32 iodata; + switch (op) { + case 0: /* SIO 1442 */ + /* if (n == 1) + return STOP_IBKPT; */ + switch (data) { /* Select stacker */ + case 0x00: + break; + case 0x01: + s2sel = 1; + break; + default: + break; + } + switch (n) { + case 0x00: /* Feed */ + iodata = SCPE_OK; + break; + case 0x01: /* Read only */ + if (cdr_ebcdic) + iodata = read_card(0, 1); + else + iodata = read_card(0, 0); + break; + case 0x02: /* Punch and feed */ + iodata = punch_card(0, 0); + break; + case 0x03: /* Read Col Binary */ + iodata = read_card(0, 1); + break; + case 0x04: /* Punch no feed */ + iodata = punch_card(0, 1); + break; + default: + return STOP_INVDEV; + } + return iodata; + case 1: /* LIO 1442 */ + switch (n) { + case 0x00: /* Load LCR */ + LCR = data & 0xffff; + break; + case 0x04: + DAR = data & 0xffff; + break; + default: + return STOP_INVDEV; + } + return SCPE_OK; + case 2: /* TIO 1442 */ + iodata = 0; + switch (n) { + case 0x00: /* Error */ + if (carderr || pcherror || notready) + iodata = 1; + if ((cdr_unit.flags & UNIT_ATT) == 0) + iodata = 1; /* attached? */ + break; + case 0x02: /* Busy */ + if (sim_is_active (&cdr_unit)) + iodata = 1; + break; + default: + return (STOP_INVDEV << 16); + } + return ((SCPE_OK << 16) | iodata); + case 3: /* SNS 1442 */ + iodata = 0; + switch (n) { + case 0x01: + break; + case 0x02: + break; + case 0x03: + if (carderr) + iodata |= 0x80; + if (lastcard) + iodata |= 0x40; + if (pcherror) + iodata |= 0x20; + if ((cdr_unit.flags & UNIT_ATT) == 0) + iodata |= 0x08; + if (notready) + iodata |= 0x08; + break; + case 0x04: + iodata = DAR; + break; + default: + return (STOP_INVDEV << 16); + } + iodata |= ((SCPE_OK << 16) & 0xffff0000); + return (iodata); + case 4: /* APL 1442 */ + iodata = 0; + switch (n) { + case 0x00: /* Error */ + if (carderr || pcherror || notready) + iodata = 1; + if ((cdr_unit.flags & UNIT_ATT) == 0) + iodata = 1; /* attached? */ + break; + case 0x02: /* Busy */ + if (sim_is_active (&cdr_unit)) + iodata = 1; + break; + default: + return (STOP_INVDEV << 16); + } + return ((SCPE_OK << 16) | iodata); + default: + break; + } + sim_printf (">>CRD non-existent function %d\n", op); + return SCPE_OK; +} + +/* Card read routine + mod 0 = ASCII read + mod 1 = EBCDIC read +*/ + +t_stat read_card (int32 ilnt, int32 mod) +{ +int32 i; +t_stat r; + +if (sim_is_active (&cdr_unit)) { /* busy? */ + sim_cancel (&cdr_unit); /* cancel */ + if (r = cdr_svc (&cdr_unit)) return r; /* process */ +} + +if (((cdp_unit.flags & UNIT_ATT) != 0 || + (stack_unit[0].flags & UNIT_ATT) != 0) && /* Punch is attached and */ + (cdr_unit.flags & UNIT_ATT) == 0) { /* reader is not --- */ + for (i = 0; i < 80; i++) { /* Assume blank cards in hopper */ + PutMem(DAR, 0x40); + DAR++; + } + sim_activate (&cdr_unit, cdr_unit.wait); /* activate */ + return SCPE_OK; +} + +if ((cdr_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */ + +lastcard = carderr = notready = s1sel = s2sel = 0; /* default stacker */ + +for (i = 0; i < CBUFSIZE; i++) rbuf[i] = 0x20; /* clear buffer */ +if (mod) { + for (i = 0; i < 80; i++) { + rbuf[i] = fgetc(cdr_unit.fileref); /* Read EBCDIC */ + } +} else { + fgets (rbuf, CBUFSIZE, cdr_unit.fileref); /* read Ascii */ +} +if (feof (cdr_unit.fileref)) { /* eof? */ + notready = 1; + return STOP_NOCD; +} +if (ferror (cdr_unit.fileref)) { /* error? */ + perror ("Card reader I/O error"); + clearerr (cdr_unit.fileref); + carderr = 1; + return SCPE_OK; } +cdr_unit.pos = ftell (cdr_unit.fileref); /* update position */ +i = getc (cdr_unit.fileref); /* see if more */ +if (feof (cdr_unit.fileref)) lastcard = 1; /* eof? set flag */ +fseek (cdr_unit.fileref, cdr_unit.pos, SEEK_SET); +for (i = 0; i < 80; i++) { + if (mod == 0) { /* If ASCII mode... */ + if (rbuf[i] == '\n' || /* remove ASCII CR/LF */ + rbuf[i] == '\r' || + rbuf[i] == 0x00) + rbuf[i] = ' '; + rbuf[i] = ascii_to_ebcdic[rbuf[i]]; /* convert to EBCDIC */ + } + PutMem(DAR, rbuf[i]); /* Copy to main memory */ + DAR++; +} +sim_activate (&cdr_unit, cdr_unit.wait); /* activate */ +return SCPE_OK; +} + +/* Card reader service. If a stacker select is active, copy to the + selected stacker. Otherwise, copy to the normal stacker. If the + unit is unattached, simply exit. +*/ + +t_stat cdr_svc (UNIT *uptr) +{ +int32 i; + +if (s2sel) uptr = &stack_unit[0]; /* stacker 1? */ +else uptr = &stack_unit[0]; /* then default */ +if ((uptr -> flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ +for (i = 0; (size_t)i < CDR_WIDTH; i++) rbuf[i] = ebcdic_to_ascii[rbuf[i]]; +for (i = CDR_WIDTH - 1; (i >= 0) && (rbuf[i] == ' '); i--) rbuf[i] = 0; +rbuf[CDR_WIDTH] = 0; /* null at end */ +fputs (rbuf, uptr -> fileref); /* write card */ +fputc ('\n', uptr -> fileref); /* plus new line */ +if (ferror (uptr -> fileref)) { /* error? */ + perror ("Card stacker I/O error"); + clearerr (uptr -> fileref); +} +uptr -> pos = ftell (uptr -> fileref); /* update position */ +return SCPE_OK; +} + +/* Card punch routine + + mod: not used +*/ + +t_stat punch_card (int32 ilnt, int32 mod) +{ +int32 i, colcount; +static char pbuf[CDP_WIDTH + 1]; /* + null */ +UNIT *uptr; + +if (s2sel) uptr = &stack_unit[0]; /* stack 2? */ +else uptr = &cdp_unit; /* normal output */ +if ((uptr -> flags & UNIT_ATT) == 0) { /* Attached? */ + notready = 1; + return SCPE_OK; +} +pcherror = s1sel = notready = 0; /* clear flags */ + +colcount = 128 - LCR; +for (i = 0; i < colcount; i++) { /* Fetch data */ + if (cdp_ebcdic) + pbuf[i] = GetMem(DAR) & 0xff; + else + pbuf[i] = ebcdic_to_ascii[GetMem(DAR)]; + DAR++; +} +for (i = CDP_WIDTH - 1; (i >= 0) && (pbuf[i] == ' '); i--) pbuf[i] = 0; +pbuf[CDP_WIDTH] = 0; /* trailing null */ +if (!cdp_ebcdic) { + fputs (pbuf, uptr -> fileref); /* output card */ + fputc ('\n', uptr -> fileref); /* plus new line */ +} else { + for (i = 0; i < 80; i++) { + fputc(pbuf[i], uptr -> fileref); + } +} +if (ferror (uptr -> fileref)) { /* error? */ + perror ("Card punch I/O error"); + clearerr (uptr -> fileref); + pcherror = 1; +} +uptr -> pos = ftell (uptr -> fileref); /* update position */ +return SCPE_OK; +} + +/* Select stack routine + + Modifiers have been checked by the caller + Modifiers are 1, 2, for the respective stack +*/ + +t_stat select_stack (int32 ilnt, int32 mod) +{ +if (mod == 1) s1sel = 1; +else if (mod == 2) s2sel = 1; +return SCPE_OK; +} + +/* Card reader/punch reset */ + +t_stat cd_reset (DEVICE *dptr) +{ +lastcard = carderr = notready = pcherror = 0; /* clear indicators */ +s1sel = s2sel = 0; /* clear stacker sel */ +sim_cancel (&cdr_unit); /* clear reader event */ +return SCPE_OK; +} + +/* Card reader attach */ + +t_stat cdr_attach (UNIT *uptr, char *cptr) +{ +carderr = lastcard = notready = 0; /* clear last card */ +return attach_unit (uptr, cptr); +} + +/* Bootstrap routine */ + +t_stat cdr_boot (int32 unitno, DEVICE *dptr) +{ +cdr_ebcdic = 1; +DAR = 0; +LCR = 80; +read_card(0, 1); +return SCPE_OK; +} diff --git a/S3/s3_cpu.c b/S3/s3_cpu.c new file mode 100644 index 00000000..84de92f2 --- /dev/null +++ b/S3/s3_cpu.c @@ -0,0 +1,1830 @@ +/* s3_cpu.c: IBM System/3 CPU simulator + + Copyright (c) 2001-2012, Charles E. Owen + HPL & SLC instruction code Copyright (c) 2001 by Henk Stegeman + Decimal Arithmetic Copyright (c) 2000 by Roger Bowler + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + ------------------------------------------------------------------------------ + + cpu System/3 (models 10 and 15) central processor + + 19-Mar-12 RMS Changed int to int32 in declarations (Mark Pizzolato) + + The IBM System/3 was a popular small-business computing system introduced + in 1969 as an entry-level system for businesses that could not afford + the lowest rungs of the System/360. Its architecture is inspired by and + in some ways similar to the 360, but to save cost the instruction set is + much smaller and the I/O channel system greatly simplified. There is no + compatibilty between the two systems. + + The original System/3 had two models, 6 and 10, and these came in two + configurations: card system and disk system. The unique feature of + the /3 was the use of 96-column cards, although traditional 80-column + cards were supprted also via attachment of a 1442 reader/punch. + System/3 is a batch-oriented system, controlled by an operating + system known as SCP (System Control Program), with it's own job control + language known as OCL (simpler and more logical than the JCL used on + the mainframes). Original models did not support multiprogramming + or any form of interactivity. (There was a hardware dual-program + facility available on the model 10 at the high end). + + The line grew throughout the 1970s, overlapping the low end of the 360 + line with the introduction of the model 15. The 15 (and later larger + variations of the model 12) broke the 64K limit designed in the original + models by adding a simple address translation unit to support up to 512K + bytes. The model 15 added a system of storage protection and allowed + multiprogramming in up to 3 partitions. Communications were added to + allow support of multiple 3270 terminals and the models 12 and 15 broke + the batch orientation and facilitated interactive use via the CCP + (communications control program). The System/3 was effectively replaced + by the much easier to manage and use System/34 and System/36 at the + low and middle of the range, and by System/370 or System/38 at the + high end. + + This simulator implements the model 10 and model 15. Models 4, 6, + 8, and 12 are not supported (these were technical variations on the + design which offered no functionality not present on either 10 or 15). + + The System/3 is a byte-oriented machine with a data path of 8 bits + in all models, and an address width of 16 bits. + + The register state for the System/3 CPU is: + + BAR <0:15> Operand 1 address register + AAR <0:15> Operand 2 address register + XR1 <0:15> Index Register 1 + XR2 <0:15> Index Register 2 + PSR <0:15> Condition Register + IAR [0:9]<0:15> Instruction Address Register (p1, p2, plus 1 for each interrupt) + ARR [0:9]<0:15> Address Recall Register (p1, p2, plus 1 for each interrupt) + (The P2 IAR & ARR are used for the Dual Program feature) + + Instruction formats follow the same basic pattern: a 1-byte opcode, a + 1-byte "Q byte", and one or two addresses following in a format defined + by the first 4 bits of the opcode: + + Op Code Q Byte Address(es) + + 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 + +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--... + | A 1 | A 2 | operation | | (defined by operation)| | Format based on A1, A2 + +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--... + + { --- } <---------------- Bits 00 = Operand 2 specified by 2-byte direct addr + Bits 01 = Operand 2 is 1-byte displacement + XR1 + Bits 10 = Operand 2 is 1-byte displacement + XR2 + Bits 11 = Operand 2 is not used + + { --- } <---------------------- Bits 00 = Operand 1 specified by 2-byte direct addr + Bits 01 = Operand 1 is 1-byte displacement + XR1 + Bits 10 = Operand 1 is 1-byte displacement + XR2 + Bits 11 = Operand 1 is not used + + Instructions come in 3 basic formats, of varying lengths which are determined + by the top 4 bits of opcode defined above. Minimum instruction length is 3 bytes, + maximum is 6. + + 1) Command Format (Bits 0-3 are 1111): + + +------------+ +------------+ +------------+ + | Opcode | | Q-byte | | R-byte + + +------------+ +------------+ +------------+ + + (The meaning of Q-byte and R-byte defined by the operation) + + + 2) One Address Instructions (either bits 0-1 or bits 2-3 are 01): + + + Direct Addressing Format: + + +------------+ +------------+ +-----------+----------+ + | Opcode | | Q-byte | | MSB + LSB + + +------------+ +------------+ +-----------+----------+ + + Base-Displacement Format: + + +------------+ +------------+ +------------+ + | Opcode | | Q-byte | |displacement+ + +------------+ +------------+ +------------+ + + Opcodes are 0011xxxx or 1100xxxx. + + Q-byte can be: 1) An immediate operand + 2) A mask + 3) A branch condition + 4) A data selection + + 2) Two Address Instructions (neither bits 0-1 nor bits 2-3 are both 11): + + Operand 1 Address Direct (opcodes 0001 or 0010): + + +------------+ +------------+ +----------+----------+ +------------+ + | Opcode | | Q-byte | | MSB + LSB + |displacement| + +------------+ +------------+ +----------+----------+ +------------+ + + Operand 2 Address Direct (opcodes 0100 or 1000): + + +------------+ +------------+ +------------+ +----------+----------+ + | Opcode | | Q-byte | |displacement| | MSB + LSB + + +------------+ +------------+ +------------+ +----------+----------+ + + Both Addresses Direct (opcode 0000): + + +------------+ +------------+ +----------+----------+ +-----------+----------+ + | Opcode | | Q-byte | | MSB + LSB + + MSB + LSB + + +------------+ +------------+ +----------+----------+ +-----------+----------+ + + Both Addresses Displacement (opcodes 0101, 0110, 1001, or 1010): + + +------------+ +------------+ +------------+ +------------+ + | Opcode | | Q-byte | |displacement| |displacement| + +------------+ +------------+ +------------+ +------------+ + + +Assembler Mnemonic Format +------------------------- + + The assembler format contains the same elements as the machine language operation, +but not always in the same format. The operation code frequently specifies both +the opcode and the Q byte, and the top nybble of the opcode is determined by +the format of the addresses. + + Addresses take two forms: the direct address in hex, or a relative address +specified thusly: (byte,XRx) where 'byte' is a 1-byte offset, and XRx is +either XR1 or XR2 for the two index registers. Use these formats when +'address' is indicated below: + + When 'reg' is mentioned, a mnemonic may be used for the register, thusly: + IAR Instruction Address Register for the current program level + ARR Address Recall Register for the current program level + P1IAR IAR for Program Level 1 + P2IAR IAR for Program Level 2 + PSR Program Status Register + 0x01 - Equal + 0x02 - Low + 0x04 - High + 0x08 - Decimal overflow + 0x10 - Test false + 0x20 - Binary overflow + 0x40 - Not used + 0x80 - Not used + XR1 Index Register 1 + XR2 Index Register 2 + IARx IAR for the interrupt level x (x = 0 thru 7) + + All other operands mentioned below are single-byte hex, except for the +length (len) operand of the two-address instructions, which is a decimal length +in the range 1-256. + + No-address formats: + ------------------ + + HPL hex,hex Halt Program Level, the operands are the Q and R bytes + + + One-address formats: + ------------------- + + A reg,address Add to register + CLI address,byte Compare Logical Immediate + MVI address,byte Move Immediate + TBF address,mask Test Bits Off + TBN address,mask Test Bits On + SBF address,mask Set Bits Off + SBN address,mask Set Bits On + ST reg,address Store Register + L reg,address Load Register + LA reg,address Load Address + JC address,cond Jump on Condition + BC address,cond Branch on Condition + + These operations do not specify a qbyte, it is implicit in the opcode: + + B address Unconditional branch to address + BE address Branch Equal + BNE address Branch Not Equal + BH address Branch High + BNH address Branch Not High + BL address Branch Low + BNL address Branch Not Low + BT address Branch True + BF address Branch False + BP address Branch Plus + BM address Branch Minus + BNP address Branch Not Plus + BNM address Branch Not Minus + BZ address Branch Zero + BNZ address Branch Not Zero + BOZ address Branch Overflow Zoned + BOL address Branch Overflow Logical + BNOZ address Branch No Overflow Zoned + BNOL address Branch No Overflow Logical + NOPB address No - never jump + + (substitute J for B above for a set of Jumps -- 1-byte operand (not 2), + always jumps forward up to 255 bytes. In this case, 'address' cannot be + less than the current address, nor greater than the current address + 255) + + Two-address formats (first address is destination, len is decimal 1-256): + ------------------- + + MVC address,address,len Move Characters + CLC address,address,len Compare Logical Characters + ALC address,address,len Add Logical Characters + SLC address,address,len Subtract Logical Characters + ED address,address,len Edit + ITC address,address,len Insert and Test Characters + AZ address,address,len Add Zoned Decimal + SZ address,address,len Subtract Zoned Decimal + + MNN address,address Move Numeric to Numeric + MNZ address,address Move Numeric to Zone + MZZ address,address Move Zone to Zone + MZN address,address Move Zone to Numeric + + I/O Format + ---------- + + In the I/O format, there are always 3 fields: + + da - Device Address 0-15 (decimal) + m - Modifier 0-1 + n - Function 0-7 + + The meaning of these is entirely defined by the device addressed. + + There may be an optional control byte, or an optional address (based on +the type of instruction). + + SNS da,m,n,address Sense I/O + LIO da,m,n,address Load I/O + TIO da,m,n,address Test I/O + + SIO da,m,n,cc Start I/O -- cc is a control byte + + APL da,m,n Advance Program Level + + + + --------------------------------------------- + Here is a handy opcode cross-reference table: + --------------------------------------------- + + | x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF +---+------------------------------------------------------------------ +0x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +1x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +2x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +3x | SNS LIO - - ST L A - TBN TBF SBN SBF MVI CLI - - + | +4x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +5x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +6x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +7x | SNS LIO - - ST L A - TBN TBF SBN SBF MVI CLI - - + | +8x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +9x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +Ax | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +Bx | SNS LIO - - ST L A - TBN TBF SBN SBF MVI CLI - - + | +Cx | BC TIO LA - - - - - - - - - - - - - +Dx | BC TIO LA - - - - - - - - - - - - - +Ex | BC TIO LA - - - - - - - - - - - - - +Fx | HPL APL JC SIO - - - - - - - - - - - - + + + This routine is the instruction decode routine for System/3. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + program check caused by invalid opcode or qbyte or address or I/O spec + unknown I/O device and STOP_DEV flag set + I/O error in I/O simulator + + 2. Interrupts. + + There are 8 levels of interrupt, each with it's own IAR (program + counter). When an interrupt occurs, execution begins at the + location in the IAR for that level interrupt. The program + must save and restore state. Each device is assigned both a + level and a priority in hardware. Interrupts are reset via + an SIO instruction, when this happens, the program level + IAR resumes control. + + Interrupts are maintained in the global variable int_req, + which is zero if no interrupts are pending, otherwise, the + lower 16 bits represent devices, rightmost bit being device + 0. Each device requesting an interrupt sets its bit on. + + + 3. Non-existent memory. On the System/3, any reference to non-existent + memory (read or write) causes a program check and machine stop. + + 4. Adding I/O devices. These modules must be modified: + + ibms3_defs.h add interrupt request definition + ibms3_cpu.c add IOT mask, PI mask, and routine to dev_table + ibms3_sys.c add pointer to data structures to sim_devices +*/ + +#include "s3_defs.h" + +#define UNIT_V_M15 (UNIT_V_UF) /* Model 15 extensions */ +#define UNIT_M15 (1 << UNIT_V_M15) +#define UNIT_V_DPF (UNIT_V_UF+1) /* Dual Programming */ +#define UNIT_DPF (1 << UNIT_V_DPF) +#define UNIT_V_MSIZE (UNIT_V_UF+3) /* dummy mask */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) + +uint8 M[MAXMEMSIZE] = { 0 }; /* memory */ +int32 AAR = 0; /* Operand 1 addr reg */ +int32 BAR = 0; /* Operand 2 addr reg */ +int32 XR1 = 0; /* Index register 1 */ +int32 XR2 = 0; /* Index register 2 */ +int32 PSR = 0; /* Condition Register */ +int32 IAR[10] = { 0 }; /* IAR 0-7 = int level 8=P1 9=P2 */ +int32 ARR[10] = { 0 }; /* ARR 0-7 = int level 8=P1 9=P2 */ +int32 dev_disable = 0; /* interrupt disable mask */ +int32 int_req = 0; /* Interrupt request device bitmap */ +int32 level = 8; /* Current Execution Level*/ +int32 stop_dev = 0; /* stop on ill dev */ +int32 SR = 0; /* Switch Register */ +int32 saved_PC; /* Saved (old) PC) */ +int32 debug_reg = 0; /* set for debug/trace */ +int32 debug_flag = 0; /* 1 when trace.log open */ +FILE *trace; + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_boot (int32 unitno, DEVICE *dptr1); +extern int32 pkb (int32 op, int32 m, int32 n, int32 data); +extern int32 crd (int32 op, int32 m, int32 n, int32 data); +extern int32 lpt (int32 op, int32 m, int32 n, int32 data); +extern int32 dsk1 (int32 op, int32 m, int32 n, int32 data); +extern int32 dsk2 (int32 op, int32 m, int32 n, int32 data); +extern int32 cpu (int32 op, int32 m, int32 n, int32 data); +int32 nulldev (int32 opcode, int32 m, int32 n, int32 data); +int32 add_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2); +int32 subtract_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2); +static int32 compare(int32 byte1, int32 byte2, int32 cond); +static int32 condition(int32 qbyte); +static void store_decimal (int32 addr, int32 len, uint8 *dec, int sign); +static void load_decimal (int32 addr, int32 len, uint8 *result, int *count, int *sign); +static void add_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int *count); +static void subtract_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int *count, int *sign); +int32 GetMem(int32 addr); +int32 PutMem(int32 addr, int32 data); + +/* IOT dispatch table */ + +/* System/3 supports only 16 unique device addresses! */ + +struct ndev dev_table[16] = { + { 0, 0, &cpu }, /* Device 0: CPU control */ + { 1, 0, &pkb }, /* Device 1: 5471 console printer/keyboard */ + { 0, 0, &nulldev }, + { 0, 0, &nulldev }, + { 0, 0, &nulldev }, + { 0, 0, &crd }, /* Device 5: 1442 card reader/punch */ + { 0, 0, &nulldev }, /* Device 6: 3410 Tape drives 1 & 2 */ + { 0, 0, &nulldev }, /* Device 7: 3410 Tape drives 3 & 4 */ + { 0, 0, &nulldev }, + { 0, 0, &nulldev }, + { 0, 0, &dsk1 }, /* Device 10: 5444 Disk Drive 1 */ + { 0, 0, &dsk2 }, /* Device 11: 5444 Disk Drive 2 */ + { 0, 0, &nulldev }, /* Device 12: 5448 Disk Drive 1 */ + { 0, 0, &nulldev }, /* DEvice 13: 5448 Disk Drive 2 */ + { 0, 0, &lpt }, /* Device 14: 1403/5203 Printer */ + { 0, 0, &nulldev } /* Device 15: 5424 MFCU */ +}; + +/* Priority assigned to interrupt levels */ + +int32 priority[8] = {8, 7, 5, 4, 3, 6, 2, 1}; + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { HRDATA (IAR, saved_PC, 16), REG_RO }, + { HRDATA (IAR-P1, IAR[8], 16) }, + { HRDATA (IAR-P2, IAR[9], 16) }, + { HRDATA (ARR-P1, ARR[8], 16) }, + { HRDATA (ARR-P2, ARR[9], 16) }, + { HRDATA (AAR, AAR, 16) }, + { HRDATA (BAR, BAR, 16) }, + { HRDATA (XR1, XR1, 16) }, + { HRDATA (XR2, XR2, 16) }, + { HRDATA (PSR, PSR, 16) }, + { HRDATA (SR, SR, 16) }, + { HRDATA (INT, int_req, 16), REG_RO }, + { HRDATA (LEVEL, level, 16) }, + { HRDATA (IAR0, IAR[0], 16) }, + { HRDATA (IAR1, IAR[1], 16) }, + { HRDATA (IAR2, IAR[2], 16) }, + { HRDATA (IAR3, IAR[3], 16) }, + { HRDATA (IAR4, IAR[4], 16) }, + { HRDATA (IAR5, IAR[5], 16) }, + { HRDATA (IAR6, IAR[6], 16) }, + { HRDATA (IAR7, IAR[7], 16) }, + { HRDATA (ARR0, ARR[0], 16) }, + { HRDATA (ARR1, ARR[1], 16) }, + { HRDATA (ARR2, ARR[2], 16) }, + { HRDATA (ARR3, ARR[3], 16) }, + { HRDATA (ARR4, ARR[4], 16) }, + { HRDATA (ARR5, ARR[5], 16) }, + { HRDATA (ARR6, ARR[6], 16) }, + { HRDATA (ARR7, ARR[7], 16) }, + { HRDATA (DISABLE, dev_disable, 16), REG_RO }, + { FLDATA (STOP_DEV, stop_dev, 0) }, + { HRDATA (WRU, sim_int_char, 8) }, + { HRDATA (DEBUG, debug_reg, 16) }, + { NULL } +}; + +MTAB cpu_mod[] = { + { UNIT_M15, UNIT_M15, "M15", "M15", NULL }, + { UNIT_M15, 0, "M10", "M10", NULL }, + { UNIT_DPF, UNIT_DPF, "DPF", "DPF", NULL }, + { UNIT_DPF, 0, "NODPF", "NODPF", NULL }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, + { UNIT_MSIZE, 65535, NULL, "64K", &cpu_set_size }, + { 0 } +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 16, 16, 1, 16, 8, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL +}; + +t_stat sim_instr (void) +{ +register int32 PC, IR; +int32 i, j, carry, zero, op1, op2; +int32 opcode = 0, qbyte = 0, rbyte = 0; +int32 opaddr, addr1, addr2, dlen1, dlen2, r; +int32 int_savelevel = 8, intpri, intlev, intdev, intmask; +int32 devno, devm, devn; +char display[3][9]; +int32 val [32]; +register t_stat reason; + +/* Restore register state */ + +PC = IAR[level]; /* load local PC */ +reason = 0; + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ +if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; +} + +if (int_req) { /* interrupt? */ + intpri = 16; + for (i = 0; i < 16; i++) { /* Get highest priority device */ + if ((int_req >> i) & 0x01) { + intlev = dev_table[i].level; + if (priority[intlev] < intpri) { + intdev = i; + intpri = priority[intlev]; + } + } + } + intmask = 1 << intdev; /* mask is interrupting dev bit */ + int_req = ~int_req & intmask; /* Turn off int_req for device */ + int_savelevel = level; /* save current level for reset */ + level = dev_table[intdev].level; /* get int level from device */ + PC = IAR[level]; /* Use int level IAR for new PC */ +} /* end interrupt */ + +if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; +} + +/* Machine Instruction Execution Here */ + +if ((debug_reg == 0) && debug_flag == 1) { + fclose(trace); + debug_flag = 0; +} +if (debug_reg) { + if (!debug_flag) { + trace = fopen("trace.log", "w"); + debug_flag = 1; + } +} + +if (debug_reg & 0x01) { + fprintf(trace, "ARR=%04X XR1=%04X XR2=%04X IAR=%04X ", ARR[level], XR1, XR2, PC); + val[0] = GetMem(PC); + val[1] = GetMem(PC+1); + val[2] = GetMem(PC+2); + val[3] = GetMem(PC+3); + val[4] = GetMem(PC+4); + val[5] = GetMem(PC+5); + fprint_sym(trace, PC, (uint32 *) val, &cpu_unit, SWMASK('M')); + fprintf(trace, "\n"); +} + +saved_PC = PC; +opaddr = GetMem(PC) & 0xf0; /* fetch addressing mode */ +opcode = GetMem(PC) & 0x0f; /* fetch opcode */ +PC = (PC + 1) & AMASK; +sim_interval = sim_interval - 1; + +qbyte = GetMem(PC) & 0xff; /* fetch qbyte */ +PC = (PC + 1) & AMASK; + +if (opaddr == 0xf0) { /* Is it command format? */ + rbyte = GetMem(PC) & 0xff; + PC = (PC + 1) & AMASK; + switch (opcode) { + case 0x00: /* HPL: Halt Program Level */ + for (i = 0; i < 3; i++) { + for (j = 0; j < 9; j++) { + display[i][j] = ' '; + } + } + /* First line */ + if (qbyte & 0x04) display[0][2] = '_' ; + if (rbyte & 0x04) display[0][6] = '_' ; + /* Second line */ + if (qbyte & 0x08) display[1][1] = '|' ; + if (rbyte & 0x08) display[1][5] = '|' ; + if (qbyte & 0x10) display[1][2] = '_' ; + if (rbyte & 0x10) display[1][6] = '_' ; + if (qbyte & 0x02) display[1][3] = '|' ; + if (rbyte & 0x02) display[1][7] = '|' ; + /* Third line */ + if (qbyte & 0x20) display[2][1] = '|' ; + if (rbyte & 0x20) display[2][5] = '|' ; + if (qbyte & 0x40) display[2][2] = '_' ; + if (rbyte & 0x40) display[2][6] = '_' ; + if (qbyte & 0x01) display[2][3] = '|' ; + if (rbyte & 0x01) display[2][7] = '|' ; + /* Print display segment array */ + sim_printf("\n"); + for (i = 0; i < 3; i++) { + for (j = 0; j < 9; j++) { + sim_printf ("%c", display[i][j]); + } + sim_printf ("\n"); + } + reason = STOP_HALT; + break; + case 0x01: /* APL: Advance Program Level */ + devno = (qbyte >> 4) & 0x0f; + devm = (qbyte >> 3) & 0x01; + devn = qbyte & 0x07; + op1 = dev_table[devno].routine(4, devm, devn, rbyte); + if (op1 & 0x01) { + if (cpu_unit.flags & UNIT_DPF) { /* Dual Programming? */ + if (level == 8) /* Yes: switch program levels */ + level = 9; + else + level = 8; + PC = IAR[level]; + } else { /* No: Loop on this inst */ + PC = PC - 3; + } + } + reason = (op1 >> 16) & 0xffff; + break; + case 0x02: /* JC: Jump on Condition */ + if (condition(qbyte) == 1) { + PC = (PC + rbyte) & AMASK; + } + break; + case 0x03: /* SIO: Start I/O */ + devno = (qbyte >> 4) & 0x0f; + devm = (qbyte >> 3) & 0x01; + devn = qbyte & 0x07; + reason = dev_table[devno].routine(0, devm, devn, rbyte); + if (reason == RESET_INTERRUPT) { + reason = SCPE_OK; + IAR[level] = PC; + level = int_savelevel; + PC = IAR[level]; + } + break; + default: + reason = STOP_INVOP; + break; + } /* switch (opcode) */ + IAR[level] = PC; + continue; +} + +/* Not command format: fetch the addresses */ + +addr1 = (opaddr >> 6) & 3; +addr2 = (opaddr >> 4) & 3; + +switch (addr1) { + case 0: + BAR = GetMem(PC) << 8; + PC = (PC + 1) & AMASK; + BAR |=GetMem(PC); + PC = (PC + 1) & AMASK; + break; + case 1: + BAR = GetMem(PC); + BAR = (BAR + XR1) & AMASK; + PC = (PC + 1) & AMASK; + break; + case 2: + BAR = GetMem(PC); + BAR = (BAR + XR2) & AMASK; + PC = (PC + 1) & AMASK; + break; + case 3: + break; + default: + break; +} /* switch (addr1) */ + +switch (addr2) { + case 0: + AAR = GetMem(PC) << 8; + PC = (PC + 1) & AMASK; + AAR |= GetMem(PC); + PC = (PC + 1) & AMASK; + break; + case 1: + AAR = GetMem(PC); + AAR = (AAR + XR1) & AMASK; + PC = (PC + 1) & AMASK; + break; + case 2: + AAR = GetMem(PC); + AAR = (AAR + XR2) & AMASK; + PC = (PC + 1) & AMASK; + break; + case 3: + break; + default: + break; +} /* switch (addr1) */ + +switch (opaddr) { + case 0x00: + case 0x10: + case 0x20: + case 0x40: + case 0x50: + case 0x60: + case 0x80: + case 0x90: + case 0xa0: + switch (opcode) { + case 4: /* ZAZ: Zero and Add Zoned */ + dlen2 = qbyte & 0x0f; + dlen1 = (qbyte >> 4) & 0xf; + dlen1 += dlen2; + op1 = BAR; + for (i = 0; i < (dlen1+1); i++) { + PutMem(op1, 0xf0); + op1--; + } + r = add_zoned(BAR, dlen1+1, AAR, dlen2+1); + PSR &= 0xF8; /* HJS mod */ + switch (r) { + case 0: + PSR |= 0x01; + break; + case 1: + PSR |= 0x02; + break; + case 2: + PSR |= 0x04; + break; + default: + break; + } + break; + case 6: /* AZ: Add Zoned */ + dlen2 = qbyte & 0x0f; + dlen1 = (qbyte >> 4) & 0xf; + dlen1 += dlen2; + r = add_zoned(BAR, dlen1+1, AAR, dlen2+1); + PSR &= 0xF0; + switch (r) { + case 0: + PSR |= 0x01; + break; + case 1: + PSR |= 0x02; + break; + case 2: + PSR |= 0x04; + break; + case 3: + PSR |= 0x08; + break; + default: + break; + } + break; + case 7: /* SZ: Subtract Zoned */ + dlen2 = qbyte & 0x0f; + dlen1 = (qbyte >> 4) & 0xf; + dlen1 += dlen2; + r = subtract_zoned(BAR, dlen1+1, AAR, dlen2+1); + PSR &= 0xF0; + switch (r) { + case 0: + PSR |= 0x01; + break; + case 1: + PSR |= 0x02; + break; + case 2: + PSR |= 0x04; + break; + case 3: + PSR |= 0x08; + break; + default: + break; + } + break; + case 8: /* MVX: Move Hex */ + op1 = GetMem(BAR); + op2 = GetMem(AAR); + switch (qbyte) { + case 0: /* Zone to zone */ + op1 = (op1 & 0x0F) | (op2 & 0xF0); + break; + case 1: /* Numeric to zone */ + op1 = (op1 & 0x0F) | (op2 << 4); + break; + case 2: /* Zone to numeric */ + op1 = (op1 & 0xF0) | (op2 >> 4); + break; + case 3: /* Numeric to numeric */ + op1 = (op1 & 0xF0) | (op2 & 0x0F); + break; + default: + reason = STOP_INVQ; + break; + } + PutMem(BAR, op1); + break; + case 0xa: /* ED: Edit */ + zero = 1; + PSR &= 0xF8; + IR = GetMem(AAR); + if ((IR & 0xf0) != 0xF0) + PSR |= 0x02; + else + PSR |= 0x04; + while (qbyte > -1) { + op2 = GetMem(AAR); + op1 = GetMem(BAR); + if (op1 == 0x20) { + op2 |= 0xf0; + PutMem(BAR, op2); + AAR--; + if (op2 != 0xF0) zero = 0; + } + BAR--; + qbyte--; + } + if (zero) + PSR |= 0x01; + break; + case 0xb: /* ITC: Insert and Test Chars */ + op2 = GetMem(AAR); + while (qbyte > -1) { + op1 = GetMem(BAR); + if (op1 >= 0xF1 && op1 <= 0xF9) + break; + PutMem(BAR, op2); + BAR++; + qbyte--; + } + ARR[level] = BAR; + break; + case 0xc: /* MVC: Move Characters */ + while (qbyte > -1) { + PutMem(BAR, GetMem(AAR)); + BAR--; + AAR--; + qbyte--; + } + break; + case 0xd: /* CLC: Compare Characters */ + PSR &= 0xF8; + i = BAR = BAR - qbyte; + j = AAR = AAR - qbyte; + while (qbyte > -1) { + if (GetMem(i) > GetMem(j)) { + PSR |= 0x04; + break; + } + if (GetMem(i) < GetMem(j)) { + PSR |= 0x02; + break; + } + i++; + j++; + qbyte--; + } + if (qbyte == -1) + PSR |= 0x01; + break; + case 0xe: /* ALC: Add Logical Characters */ + carry = 0; + zero = 1; + while (qbyte > -1) { + IR = GetMem(BAR) + GetMem(AAR) + carry; + if (IR & 0x100) + carry = 1; + else + carry = 0; + if ((IR & 0xFF) != 0) zero = 0; /* HJS mod */ + PutMem(BAR,(IR & 0xFF)); + BAR--; + AAR--; + qbyte--; + } + PSR &= 0xD8; + if (zero) + PSR |= 0x01; /* Equal */ + if (!zero && !carry) + PSR |= 0x02; /* Low */ + if (!zero && carry) + PSR |= 0x04; /* High */ + if (carry) + PSR |= 0x20; /* Overflow */ + break; + case 0xf: /* SLC: Subtract Logical Characters */ + carry = 1; + zero = 1; + while (qbyte > -1) { + IR = GetMem(BAR) + (0xFF - GetMem(AAR)) + carry; + if (IR & 0x100) + carry = 1; + else + carry = 0; + if ((IR & 0xFF) != 0) zero = 0; /* HJS mod */ + PutMem(BAR,(IR & 0xFF)); + BAR--; + AAR--; + qbyte--; + } + PSR &= 0xF8; + if (zero) + PSR |= 0x01; /* Equal */ + if (!zero && !carry) + PSR |= 0x02; /* Low */ + if (!zero && carry) + PSR |= 0x04; /* High */ + break; + default: + reason = STOP_INVOP; + break; + } + IAR[level] = PC; + continue; + break; + case 0x30: + case 0x70: + case 0xb0: + switch (opcode) { + case 0: /* SNS: Sense I/O */ + devno = (qbyte >> 4) & 0x0f; + devm = (qbyte >> 3) & 0x01; + devn = qbyte & 0x07; + i = dev_table[devno].routine(3, devm, devn, rbyte); + PutMem(BAR, i & 0xff); + BAR--; + PutMem(BAR, (i >> 8) & 0xff); + reason = (i >> 16) & 0xffff; + break; + case 1: /* LIO: Load I/O */ + devno = (qbyte >> 4) & 0x0f; + devm = (qbyte >> 3) & 0x01; + devn = qbyte & 0x07; + op1 = GetMem(BAR); + BAR--; + op1 |= (GetMem(BAR) << 8) & 0xff00; + reason = dev_table[devno].routine(1, devm, devn, op1); + break; + case 4: /* ST: Store Register */ + switch (qbyte) { + case 0x01: + PutMem(BAR, XR1 & 0xff); + BAR--; + PutMem(BAR, (XR1 >> 8) & 0xff); + break; + case 0x02: + PutMem(BAR, XR2 & 0xff); + BAR--; + PutMem(BAR, (XR2 >> 8) & 0xff); + break; + case 0x04: + PutMem(BAR, PSR & 0xFF); + BAR--; + PutMem(BAR, 0); /* LCRR, not imp. */ + break; + case 0x08: + PutMem(BAR, ARR[level] & 0xff); + BAR--; + PutMem(BAR, (ARR[level] >> 8) & 0xff); + break; + case 0x10: + PutMem(BAR, IAR[level] & 0xff); + BAR--; + PutMem(BAR, (IAR[level] >> 8) & 0xff); + break; + case 0x20: + PutMem(BAR, IAR[8] & 0xff); + BAR--; + PutMem(BAR, (IAR[8] >> 8) & 0xff); + break; + case 0x40: + PutMem(BAR, IAR[9] & 0xff); + BAR--; + PutMem(BAR, (IAR[9] >> 8) & 0xff); + break; + case 0x80: + PutMem(BAR, IAR[0] & 0xff); + BAR--; + PutMem(BAR, (IAR[0] >> 8) & 0xff); + break; + case 0x81: + PutMem(BAR, IAR[7] & 0xff); + BAR--; + PutMem(BAR, (IAR[7] >> 8) & 0xff); + break; + case 0x82: + PutMem(BAR, IAR[6] & 0xff); + BAR--; + PutMem(BAR, (IAR[6] >> 8) & 0xff); + break; + case 0x84: + PutMem(BAR, IAR[5] & 0xff); + BAR--; + PutMem(BAR, (IAR[5] >> 8) & 0xff); + break; + case 0x88: + PutMem(BAR, IAR[4] & 0xff); + BAR--; + PutMem(BAR, (IAR[4] >> 8) & 0xff); + break; + case 0x90: + PutMem(BAR, IAR[3] & 0xff); + BAR--; + PutMem(BAR, (IAR[3] >> 8) & 0xff); + break; + case 0xA0: + PutMem(BAR, IAR[2] & 0xff); + BAR--; + PutMem(BAR, (IAR[2] >> 8) & 0xff); + break; + case 0xC0: + PutMem(BAR, IAR[1] & 0xff); + BAR--; + PutMem(BAR, (IAR[1] >> 8) & 0xff); + break; + default: + reason = STOP_INVQ; + break; + } + break; + case 5: /* L: Load Register */ + switch (qbyte) { + case 0x01: + XR1 = GetMem(BAR) & 0xff; + BAR--; + XR1 |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x02: + XR2 = GetMem(BAR) & 0xff; + BAR--; + XR2 |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x04: + PSR = GetMem(BAR) & 0xff; + BAR--; + break; + case 0x08: + ARR[level] = GetMem(BAR) & 0xff; + BAR--; + ARR[level] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x10: + IAR[level] = GetMem(BAR) & 0xff; + BAR--; + IAR[level] |= (GetMem(BAR) << 8) & 0xff00; + PC = IAR[level]; + break; + case 0x20: + IAR[8] = GetMem(BAR) & 0xff; + BAR--; + IAR[8] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x40: + IAR[9] = GetMem(BAR) & 0xff; + BAR--; + IAR[9] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x80: + IAR[0] = GetMem(BAR) & 0xff; + BAR--; + IAR[0] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x81: + IAR[7] = GetMem(BAR) & 0xff; + BAR--; + IAR[7] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x82: + IAR[6] = GetMem(BAR) & 0xff; + BAR--; + IAR[6] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x84: + IAR[5] = GetMem(BAR) & 0xff; + BAR--; + IAR[5] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x88: + IAR[4] = GetMem(BAR) & 0xff; + BAR--; + IAR[4] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x90: + IAR[3] = GetMem(BAR) & 0xff; + BAR--; + IAR[3] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0xA0: + IAR[2] = GetMem(BAR) & 0xff; + BAR--; + IAR[2] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0xC0: + IAR[1] = GetMem(BAR) & 0xff; + BAR--; + IAR[1] |= (GetMem(BAR) << 8) & 0xff00; + break; + default: + reason = STOP_INVQ; + break; + } + break; + case 6: /* A: Add to Register */ + IR = GetMem(BAR) & 0x00ff; + BAR--; + IR |= (GetMem(BAR) << 8) & 0xff00; + switch (qbyte) { + case 0x01: + IR += XR1; + XR1 = IR & AMASK; + break; + case 0x02: + IR += XR2; + XR2 = IR & AMASK; + break; + case 0x04: + IR += PSR; + PSR = IR & AMASK; + break; + case 0x08: + IR += ARR[level]; + ARR[level] = IR & AMASK; + break; + case 0x10: + IR += IAR[level]; + IAR[level] = IR & AMASK; + break; + case 0x20: + IR += IAR[8]; + IAR[8] = IR & AMASK; + break; + case 0x40: + IR += IAR[9]; + IAR[9] = IR & AMASK; + break; + case 0x80: + IR += IAR[0]; + IAR[0] = IR & AMASK; + break; + case 0x81: + IR += IAR[7]; + IAR[7] = IR & AMASK; + break; + case 0x82: + IR += IAR[6]; + IAR[6] = IR & AMASK; + break; + case 0x84: + IR += IAR[5]; + IAR[5] = IR & AMASK; + break; + case 0x88: + IR += IAR[4]; + IAR[4] = IR & AMASK; + break; + case 0x90: + IR += IAR[3]; + IAR[3] = IR & AMASK; + break; + case 0xA0: + IR += IAR[2]; + IAR[2] = IR & AMASK; + break; + case 0xC0: + IR += IAR[1]; + IAR[1] = IR & AMASK; + break; + default: + reason = STOP_INVQ; + break; + } + PSR &= 0xD8; + if ((IR & 0xffff) == 0) + PSR |= 0x01; /* Zero */ + if ((IR & 0xffff) != 0 && !(IR & 0x10000)) + PSR |= 0x02; /* Low */ + if ((IR & 0xffff) != 0 && (IR & 0x10000)) + PSR |= 0x04; /* High */ + if ((IR & 0x10000)) + PSR |= 0x20; /* Bin overflow */ + break; + case 8: /* TBN: Test Bits On */ + IR = GetMem(BAR); + PSR &= 0xFF; + if ((IR & qbyte) != qbyte) + PSR |= 0x10; + break; + case 9: /* TBF: Test Bits Off */ + IR = GetMem(BAR); + PSR &= 0xFF; + if ((IR & qbyte)) + PSR |= 0x10; + break; + case 0xa: /* SBN: Set Bits On */ + IR = GetMem(BAR); + IR |= qbyte; + PutMem(BAR, IR); + break; + case 0xb: /* SBF: Set Bits Off */ + IR = GetMem(BAR); + IR &= ~qbyte; + PutMem(BAR, IR); + break; + case 0xc: /* MVI: Move Immediate */ + PutMem(BAR, qbyte); + break; + case 0xd: /* CLI: Compare Immediate */ + PSR = compare(GetMem(BAR), qbyte, PSR); + break; + default: + reason = STOP_INVOP; + break; + } + IAR[level] = PC; + continue; + break; + case 0xc0: + case 0xd0: + case 0xe0: + switch (opcode) { + case 0: /* BC: Branch on Condition */ + ARR[level] = AAR & AMASK; + if (condition(qbyte) == 1) { + IR = ARR[level]; + ARR[level] = PC & AMASK; + PC = IR; + } + break; + case 1: /* TIO: Test I/O */ + devno = (qbyte >> 4) & 0x0f; + devm = (qbyte >> 3) & 0x01; + devn = qbyte & 0x07; + op1 = dev_table[devno].routine(2, devm, devn, rbyte); + if (op1 & 0x01) { + ARR[level] = AAR & AMASK; + IR = ARR[level]; + ARR[level] = PC & AMASK; + PC = IR; + } + reason = (op1 >> 16) & 0xffff; + break; + case 2: /* LA: Load Address */ + switch (qbyte) { + case 1: + XR1 = AAR; + break; + case 2: + XR2 = AAR; + break; + default: + reason = STOP_INVQ; + break; + } + break; + default: + reason = STOP_INVOP; + break; + } /* switch (opcode) */ + IAR[level] = PC; + continue; + + default: + reason = STOP_INVOP; + break; +} /* switch (opaddr) */ + +} /* end while (reason == 0) */ + +/* Simulation halted */ + +saved_PC = PC; +return reason; +} + +/* On models 4-12, these memory functions could be inline, but + on a model 15 with ATU address mapping must be performed so + they are in functions here for future expansion. +*/ + +/* Fetch a byte from memory */ + +int32 GetMem(int32 addr) +{ + return M[addr] & 0xff; +} + +/* Place a byte in memory */ + +int32 PutMem(int32 addr, int32 data) +{ + M[addr] = data & 0xff; + return 0; +} + +/* Check the condition register against the qbyte and return 1 if true */ + +static int32 condition(int32 qbyte) +{ + int32 r = 0, t, q; + t = (qbyte & 0xf0) >> 4; + q = qbyte & 0x0f; + if (qbyte & 0x80) { /* True if any condition tested = 1*/ + if (((qbyte & 0x3f) & PSR) != 0) r = 1; + } else { /* True if all conditions tested = 0 */ + if (((qbyte & 0x3f) & PSR) == 0) r = 1; + } + /* these bits reset by a test */ + if (qbyte & 0x10) + PSR &= 0xEF; /* Reset test false if used */ + if (qbyte & 0x08) + PSR &= 0xF7; /* Reset decimal overflow if tested */ + if (qbyte == 0x00) + r = 1; /* unconditional branch */ + if (qbyte == 0x80) + r = 0; /* force no branch */ + if (t >=0 && t < 8 && (q == 7 || q == 0xf)) + r = 0; /* no-op */ + if (t > 7 && t < 0x10 && (q == 7 || q == 0xf)) + r = 1; /* Force branch */ +return (r); +} +/* Given operand 1 and operand 2, compares the two and returns + the System/3 condition register bits appropriately, given the + condition register initial state in parameter 3 +*/ + +static int32 compare(int32 byte1, int32 byte2, int32 cond) +{ + int32 r; + + r = cond & 0xF8; /* mask off all but unaffected bits 2,3,4 */ + if (byte1 == byte2) + r |= 0x01; /* set equal bit */ + if (byte1 < byte2) + r |= 0x02; /* set less-than bit */ + if (byte1 > byte2) + r |= 0x04; /* set greater than bit */ + return r; +} + +/*-------------------------------------------------------------------*/ +/* Add two zoned decimal operands */ +/* */ +/* Input: */ +/* addr1 Logical address of packed decimal storage operand 1 */ +/* len1 Length minus one of storage operand 1 (range 0-15) */ +/* addr2 Logical address of packed decimal storage operand 2 */ +/* len2 Length minus one of storage operand 2 (range 0-15) */ +/* Output: */ +/* The return value is the condition code: */ +/* 0=result zero, 1=result -ve, 2=result +ve, 3=overflow */ +/* */ +/* A program check may be generated if either logical address */ +/* causes an addressing, translation, or fetch protection */ +/* exception, or if either operand causes a data exception */ +/* because of invalid decimal digits or sign, or if the */ +/* first operand is store protected. Depending on the PSW */ +/* program mask, decimal overflow may cause a program check. */ +/*-------------------------------------------------------------------*/ +int32 add_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2) +{ +int cc; /* Condition code */ +uint8 dec1[MAX_DECIMAL_DIGITS]; /* Work area for operand 1 */ +uint8 dec2[MAX_DECIMAL_DIGITS]; /* Work area for operand 2 */ +uint8 dec3[MAX_DECIMAL_DIGITS]; /* Work area for result */ +int count1, count2, count3; /* Significant digit counters*/ +int sign1, sign2, sign3; /* Sign of operands & result */ + + /* Load operands into work areas */ + load_decimal (addr1, len1, dec1, &count1, &sign1); + load_decimal (addr2, len2, dec2, &count2, &sign2); + + /* Add or subtract operand values */ + if (count2 == 0) + { + /* If second operand is zero then result is first operand */ + memcpy (dec3, dec1, MAX_DECIMAL_DIGITS); + count3 = count1; + sign3 = sign1; + } + else if (count1 == 0) + { + /* If first operand is zero then result is second operand */ + memcpy (dec3, dec2, MAX_DECIMAL_DIGITS); + count3 = count2; + sign3 = sign2; + } + else if (sign1 == sign2) + { + /* If signs are equal then add operands */ + add_decimal (dec1, dec2, dec3, &count3); + sign3 = sign1; + } + else + { + /* If signs are opposite then subtract operands */ + subtract_decimal (dec1, dec2, dec3, &count3, &sign3); + if (sign1 < 0) sign3 = -sign3; + } + + /* Set condition code */ + cc = (count3 == 0) ? 0 : (sign3 < 1) ? 1 : 2; + + /* Overflow if result exceeds first operand length */ + if (count3 > len1) + cc = 3; + + /* Set positive sign if result is zero */ + if (count3 == 0) + sign3 = 1; + + /* Store result into first operand location */ + store_decimal (addr1, len1, dec3, sign3); + + /* Return condition code */ + return cc; + +} /* end function add_packed */ + +/*-------------------------------------------------------------------*/ +/* Subtract two zoned decimal operands */ +/* */ +/* Input: */ +/* addr1 Logical address of packed decimal storage operand 1 */ +/* len1 Length minus one of storage operand 1 (range 0-15) */ +/* addr2 Logical address of packed decimal storage operand 2 */ +/* len2 Length minus one of storage operand 2 (range 0-15) */ +/* Output: */ +/* The return value is the condition code: */ +/* 0=result zero, 1=result -ve, 2=result +ve, 3=overflow */ +/* */ +/* A program check may be generated if either logical address */ +/* causes an addressing, translation, or fetch protection */ +/* exception, or if either operand causes a data exception */ +/* because of invalid decimal digits or sign, or if the */ +/* first operand is store protected. Depending on the PSW */ +/* program mask, decimal overflow may cause a program check. */ +/*-------------------------------------------------------------------*/ +int32 subtract_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2) +{ +int cc; /* Condition code */ +uint8 dec1[MAX_DECIMAL_DIGITS]; /* Work area for operand 1 */ +uint8 dec2[MAX_DECIMAL_DIGITS]; /* Work area for operand 2 */ +uint8 dec3[MAX_DECIMAL_DIGITS]; /* Work area for result */ +int count1, count2, count3; /* Significant digit counters*/ +int sign1, sign2, sign3; /* Sign of operands & result */ + + /* Load operands into work areas */ + load_decimal (addr1, len1, dec1, &count1, &sign1); + load_decimal (addr2, len2, dec2, &count2, &sign2); + + /* Add or subtract operand values */ + if (count2 == 0) + { + /* If second operand is zero then result is first operand */ + memcpy (dec3, dec1, MAX_DECIMAL_DIGITS); + count3 = count1; + sign3 = sign1; + } + else if (count1 == 0) + { + /* If first operand is zero then result is -second operand */ + memcpy (dec3, dec2, MAX_DECIMAL_DIGITS); + count3 = count2; + sign3 = -sign2; + } + else if (sign1 != sign2) + { + /* If signs are opposite then add operands */ + add_decimal (dec1, dec2, dec3, &count3); + sign3 = sign1; + } + else + { + /* If signs are equal then subtract operands */ + subtract_decimal (dec1, dec2, dec3, &count3, &sign3); + if (sign1 < 0) sign3 = -sign3; + } + + /* Set condition code */ + cc = (count3 == 0) ? 0 : (sign3 < 1) ? 1 : 2; + + /* Overflow if result exceeds first operand length */ + if (count3 > len1) + cc = 3; + + /* Set positive sign if result is zero */ + if (count3 == 0) + sign3 = 1; + + /* Store result into first operand location */ + store_decimal (addr1, len1, dec3, sign3); + + /* Return condition code */ + return cc; + +} /* end function subtract_packed */ + + +/*-------------------------------------------------------------------*/ +/* Add two decimal byte strings as unsigned decimal numbers */ +/* */ +/* Input: */ +/* dec1 A 31-byte area containing the decimal digits of */ +/* the first operand. Each byte contains one decimal */ +/* digit in the low-order 4 bits of the byte. */ +/* dec2 A 31-byte area containing the decimal digits of */ +/* the second operand. Each byte contains one decimal */ +/* digit in the low-order 4 bits of the byte. */ +/* Output: */ +/* result Points to a 31-byte area to contain the result */ +/* digits. One decimal digit is placed in the low-order */ +/* 4 bits of each byte. */ +/* count Points to an integer to receive the number of */ +/* digits in the result excluding leading zeroes. */ +/* This field is set to zero if the result is all zero, */ +/* or to MAX_DECIMAL_DIGITS+1 if overflow occurred. */ +/*-------------------------------------------------------------------*/ +static void add_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int *count) +{ +int d; /* Decimal digit */ +int i; /* Array subscript */ +int n = 0; /* Significant digit counter */ +int carry = 0; /* Carry indicator */ + + /* Add digits from right to left */ + for (i = MAX_DECIMAL_DIGITS - 1; i >= 0; i--) + { + /* Add digits from first and second operands */ + d = dec1[i] + dec2[i] + carry; + + /* Check for carry into next digit */ + if (d > 9) { + d -= 10; + carry = 1; + } else { + carry = 0; + } + + /* Check for significant digit */ + if (d != 0) + n = MAX_DECIMAL_DIGITS - i; + + /* Store digit in result */ + result[i] = d; + + } /* end for */ + + /* Check for carry out of leftmost digit */ + if (carry) + n = MAX_DECIMAL_DIGITS + 1; + + /* Return significant digit counter */ + *count = n; + +} /* end function add_decimal */ + +/*-------------------------------------------------------------------*/ +/* Subtract two decimal byte strings as unsigned decimal numbers */ +/* */ +/* Input: */ +/* dec1 A 31-byte area containing the decimal digits of */ +/* the first operand. Each byte contains one decimal */ +/* digit in the low-order 4 bits of the byte. */ +/* dec2 A 31-byte area containing the decimal digits of */ +/* the second operand. Each byte contains one decimal */ +/* digit in the low-order 4 bits of the byte. */ +/* Output: */ +/* result Points to a 31-byte area to contain the result */ +/* digits. One decimal digit is placed in the low-order */ +/* 4 bits of each byte. */ +/* count Points to an integer to receive the number of */ +/* digits in the result excluding leading zeroes. */ +/* This field is set to zero if the result is all zero. */ +/* sign -1 if the result is negative (operand2 > operand1) */ +/* +1 if the result is positive (operand2 <= operand1) */ +/*-------------------------------------------------------------------*/ +static void subtract_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int *count, int *sign) +{ +int d; /* Decimal digit */ +int i; /* Array subscript */ +int n = 0; /* Significant digit counter */ +int borrow = 0; /* Borrow indicator */ +int rc; /* Return code */ +uint8 *higher; /* -> Higher value operand */ +uint8 *lower; /* -> Lower value operand */ + + /* Compare digits to find which operand has higher numeric value */ + rc = memcmp (dec1, dec2, MAX_DECIMAL_DIGITS); + + /* Return positive zero result if both operands are equal */ + if (rc == 0) { + memset (result, 0, MAX_DECIMAL_DIGITS); + *count = 0; + *sign = +1; + return; + } + + /* Point to higher and lower value operands and set sign */ + if (rc > 0) { + higher = dec1; + lower = dec2; + *sign = +1; + } else { + lower = dec1; + higher = dec2; + *sign = -1; + } + + /* Subtract digits from right to left */ + for (i = MAX_DECIMAL_DIGITS - 1; i >= 0; i--) + { + /* Subtract lower operand digit from higher operand digit */ + d = higher[i] - lower[i] - borrow; + + /* Check for borrow from next digit */ + if (d < 0) { + d += 10; + borrow = 1; + } else { + borrow = 0; + } + + /* Check for significant digit */ + if (d != 0) + n = MAX_DECIMAL_DIGITS - i; + + /* Store digit in result */ + result[i] = d; + + } /* end for */ + + /* Return significant digit counter */ + *count = n; + +} /* end function subtract_decimal */ + +/*-------------------------------------------------------------------*/ +/* Load a zoned decimal storage operand into a decimal byte string */ +/* */ +/* Input: */ +/* addr Logical address of zoned decimal storage operand */ +/* len Length minus one of storage operand (range 0-15) */ +/* Output: */ +/* result Points to a 31-byte area into which the decimal */ +/* digits are loaded. One decimal digit is loaded */ +/* into the low-order 4 bits of each byte, and the */ +/* result is padded to the left with high-order zeroes */ +/* if the storage operand contains less than 31 digits. */ +/* count Points to an integer to receive the number of */ +/* digits in the result excluding leading zeroes. */ +/* This field is set to zero if the result is all zero. */ +/* sign Points to an integer which will be set to -1 if a */ +/* negative sign was loaded from the operand, or +1 if */ +/* a positive sign was loaded from the operand. */ +/* */ +/* A program check may be generated if the logical address */ +/* causes an addressing, translation, or fetch protection */ +/* exception, or if the operand causes a data exception */ +/* because of invalid decimal digits or sign. */ +/*-------------------------------------------------------------------*/ +static void load_decimal (int32 addr, int32 len, uint8 *result, int *count, int *sign) +{ +int h; /* Hexadecimal digit */ +int i, j; /* Array subscripts */ +int n; /* Significant digit counter */ + + if ((GetMem(addr) & 0xf0) == 0xD0) + *sign = -1; + else + *sign = 1; + j = len; + for (i = MAX_DECIMAL_DIGITS; i > -1; i--) { + if (j) { + h = GetMem(addr) & 0x0f; + addr--; + j--; + } else { + h = 0; + } + result [i-1] = h; + if (h > 0) n = i; + } + *count = 32 - n; + +} /* end function load_decimal */ + +/*-------------------------------------------------------------------*/ +/* Store decimal byte string into packed decimal storage operand */ +/* */ +/* Input: */ +/* addr Logical address of packed decimal storage operand */ +/* len Length minus one of storage operand (range 0-15) */ +/* dec A 31-byte area containing the decimal digits to be */ +/* stored. Each byte contains one decimal digit in */ +/* the low-order 4 bits of the byte. */ +/* sign -1 if a negative sign is to be stored, or +1 if a */ +/* positive sign is to be stored. */ +/* */ +/* A program check may be generated if the logical address */ +/* causes an addressing, translation, or protection exception. */ +/*-------------------------------------------------------------------*/ +static void store_decimal (int32 addr, int32 len, uint8 *dec, int sign) +{ +int i, j, a; /* Array subscripts */ + + j = len; + a = addr; + for (i = MAX_DECIMAL_DIGITS; i > -1; i--) { + if (j) { + PutMem(a, (dec[i-1] | 0xf0)); + a--; + j--; + } else { + break; + } + } + if (sign == -1) { + PutMem(addr, (GetMem(addr) & 0x0f)); + PutMem(addr, (GetMem(addr) | 0xf0)); + } + +} /* end function store_decimal */ + +/* CPU Device Control */ + +int32 cpu (int32 op, int32 m, int32 n, int32 data) +{ + int32 iodata = 0; + + switch (op) { + case 0x00: /* Start IO */ + return SCPE_OK; + case 0x01: /* LIO */ + return SCPE_OK; + case 0x02: /* TIO */ + break; + case 0x03: /* SNS */ + /* SNS CPU gets the data switches */ + iodata = SR; + break; + case 0x04: /* APL */ + break; + default: + break; + } + return ((SCPE_OK << 16) | iodata); +} + + + +/* Null device */ + +int32 nulldev (int32 opcode, int32 m, int32 n, int32 data) +{ +if (opcode == 1) + return SCPE_OK; /* Ok to LIO unconfigured devices? */ +return STOP_INVDEV; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +int_req = 0; +level = 8; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & 0xff; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +M[addr] = val & 0xff; +return SCPE_OK; +} + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i; + +if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} + +t_stat cpu_boot (int32 unitno, DEVICE *dptr) +{ +level = 8; +IAR[8] = 0; +return SCPE_OK; +} diff --git a/S3/s3_defs.h b/S3/s3_defs.h new file mode 100644 index 00000000..3bb0a93d --- /dev/null +++ b/S3/s3_defs.h @@ -0,0 +1,94 @@ +/* s3_defs.h: IBM System/3 simulator definitions + + Copyright (c) 2001-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. +*/ + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_INVOP 4 /* program check - invalid op */ +#define STOP_INVQ 5 /* Prog check - invalid Q */ +#define STOP_INVADDR 6 /* Prog check - invalid addr */ +#define STOP_INVDEV 7 /* Prog check - invalid dev cmd */ +#define STOP_NOCD 8 /* ATTN card reader */ +#define RESET_INTERRUPT 77 /* special return from SIO */ + +/* Memory */ + +#define MAXMEMSIZE 65536 /* max memory size */ +#define AMASK (MAXMEMSIZE - 1) /* logical addr mask */ +#define PAMASK (MAXMEMSIZE - 1) /* physical addr mask */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ + +#define MAX_DECIMAL_DIGITS 31 /* max size of a decimal number */ +#define CDR_WIDTH 80 /* Max card size */ +#define CDP_WIDTH 80 /* Punch width */ +#define LPT_WIDTH 132 +#define CCT_LNT 132 + +#define DSK_SECTSIZE 256 /* Sector length */ +#define DSK_CYLSIZE 256*48 /* Cylinder length */ + +/* I/O structure + + The I/O structure is tied together by dev_table, indexed by + the device number. Each entry in dev_table consists of + + level Interrupt level for device (0-7) + priority Priority for device (1-8) + routine IOT action routine +*/ + +struct ndev { + int32 level; /* interrupt level */ + int32 pri; /* Device priority */ + int32 (*routine)(int32, int32, int32, int32); /* dispatch routine */ +}; + +/* Structure to define operation codes */ + +struct opdef { + char op[6]; /* Mnemonic for op */ + int32 opmask; /* Bits set on in opcode */ + int32 q; /* Qbyte */ + int32 form; /* Forms are: + 0 - 1-byte hex operand + 1 - 1-byte register addr, A-Addr + 2 - A-addr,B-addr,Qbyte + 3 - A-addr,Qbyte + 4 - da,m,n + 5 - da,m,n,cc + 6 - da,m,n,A-addr + 7 - 1-address implict Q + 8 - 2-address implict Q */ + int32 group; /* Group Code: + 0 - Command Format (0xFx) + 1 - 1-address A (0xx) + 2 - 2-address (0x<0,1,2,4,5,6,8,9,A>x) + 3 - 1-address B (0x<3,7,B>x) */ +}; diff --git a/S3/s3_disk.c b/S3/s3_disk.c new file mode 100644 index 00000000..1faea2e5 --- /dev/null +++ b/S3/s3_disk.c @@ -0,0 +1,792 @@ +/* s3_disk.c: IBM 5444 Disk Drives + + Copyright (c) 2001-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + r1 Removeable disk 1 + f1 Fixed disk 1 + r2 Removeable disk 2 + f2 Fixed disk 2 + + 25-Apr-03 RMS Revised for extended file support + 08-Oct-02 RMS Added impossible function catcher +*/ + +#include "s3_defs.h" +#include + +extern uint8 M[]; +extern int32 IAR[], level; +extern FILE *trace; +extern int32 debug_reg; +char dbuf[DSK_SECTSIZE]; /* Disk buffer */ +int32 dsk (int32 disk, int32 op, int32 m, int32 n, int32 data); +int32 read_sector(UNIT *uptr, char *dbuf, int32 sect); +int32 write_sector(UNIT *uptr, char *dbuf, int32 sect); +t_stat r1_svc (UNIT *uptr); +t_stat r1_boot (int32 unitno, DEVICE *dptr); +t_stat r1_attach (UNIT *uptr, char *cptr); +t_stat r1_reset (DEVICE *dptr); +t_stat f1_svc (UNIT *uptr); +t_stat f1_boot (int32 unitno, DEVICE *dptr); +t_stat f1_attach (UNIT *uptr, char *cptr); +t_stat f1_reset (DEVICE *dptr); +t_stat r2_svc (UNIT *uptr); +t_stat r2_boot (int32 unitno, DEVICE *dptr); +t_stat r2_attach (UNIT *uptr, char *cptr); +t_stat r2_reset (DEVICE *dptr); +t_stat f2_svc (UNIT *uptr); +t_stat f2_boot (int32 unitno, DEVICE *dptr); +t_stat f2_attach (UNIT *uptr, char *cptr); +t_stat f2_reset (DEVICE *dptr); +extern int32 GetMem(int32 addr); +extern int32 PutMem(int32 addr, int32 data); + +char opstr[5][5] = { "SIO", "LIO", "TIO", "SNS", "APL" }; + +int32 DDAR[2]; /* Data address register */ +int32 DCAR[2]; /* Disk Control Address Register */ +int32 diskerr[2] = { 0, 0 }; /* Error status */ +int32 notrdy[2] = { 0, 0 }; /* Not ready error */ +int32 seekbusy[2] = { 0, 0 }; /* Drive busy flags */ +int32 seekhead[2] = { 0, 0 }; /* Disk head 0,1 */ +int32 found[2] = { 0, 0 }; /* Scan found bit */ +int32 RIDsect[2] = { 0, 0 }; /* for Read ID */ + +/* Disk data structures + + xy_dev CDR descriptor + xy_unit CDR unit descriptor + xy_reg CDR register list + + x = F or R + y = 1 or 2 +*/ + +UNIT r1_unit = { UDATA (&r1_svc, UNIT_FIX+UNIT_ATTABLE, 0), 100 }; + +REG r1_reg[] = { + { FLDATA (NOTRDY, notrdy[0], 0) }, + { FLDATA (SEEK, seekbusy[0], 0) }, + { HRDATA (DAR, DDAR[0], 16) }, + { HRDATA (CAR, DCAR[0], 16) }, + { HRDATA (ERR, diskerr[0], 16) }, + { DRDATA (CYL, r1_unit.u3, 8) }, + { DRDATA (HEAD, seekhead[0], 8) }, + { DRDATA (POS, r1_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, r1_unit.wait, 24), PV_LEFT }, + { BRDATA (BUF, dbuf, 8, 8, 256) }, + { NULL } +}; + +DEVICE r1_dev = { + "R1", &r1_unit, r1_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &r1_reset, + &r1_boot, &r1_attach, NULL +}; + +UNIT f1_unit = { UDATA (&f1_svc, UNIT_FIX+UNIT_ATTABLE, 0), 100 }; + +REG f1_reg[] = { + { FLDATA (NOTRDY, notrdy[0], 0) }, + { FLDATA (SEEK, seekbusy[0], 0) }, + { HRDATA (DAR, DDAR[0], 16) }, + { HRDATA (CAR, DCAR[0], 16) }, + { HRDATA (ERR, diskerr[0], 16) }, + { DRDATA (CYL, f1_unit.u3, 8) }, + { DRDATA (HEAD, seekhead[0], 8) }, + { DRDATA (POS, f1_unit.pos, 32), PV_LEFT }, + { DRDATA (TIME, f1_unit.wait, 24), PV_LEFT }, + { BRDATA (BUF, dbuf, 8, 8, 256) }, + { NULL } +}; + +DEVICE f1_dev = { + "F1", &f1_unit, f1_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &f1_reset, + &f1_boot, &f1_attach, NULL +}; + +UNIT r2_unit = { UDATA (&r2_svc, UNIT_FIX+UNIT_ATTABLE, 0), 100 }; + +REG r2_reg[] = { + { FLDATA (NOTRDY, notrdy[1], 0) }, + { FLDATA (SEEK, seekbusy[1], 0) }, + { HRDATA (DAR, DDAR[1], 16) }, + { HRDATA (CAR, DCAR[1], 16) }, + { HRDATA (ERR, diskerr[1], 16) }, + { DRDATA (CYL, r2_unit.u3, 8) }, + { DRDATA (HEAD, seekhead[1], 8) }, + { DRDATA (POS, r2_unit.pos, 32), PV_LEFT }, + { DRDATA (TIME, r2_unit.wait, 24), PV_LEFT }, + { BRDATA (BUF, dbuf, 8, 8, 256) }, + { NULL } +}; + +DEVICE r2_dev = { + "R2", &r2_unit, r2_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &r2_reset, + &r2_boot, &r2_attach, NULL +}; + +UNIT f2_unit = { UDATA (&f2_svc, UNIT_FIX+UNIT_ATTABLE, 0), 100 }; + +REG f2_reg[] = { + { FLDATA (NOTRDY, notrdy[1], 0) }, + { FLDATA (SEEK, seekbusy[1], 0) }, + { HRDATA (DAR, DDAR[1], 16) }, + { HRDATA (CAR, DCAR[1], 16) }, + { HRDATA (ERR, diskerr[1], 16) }, + { DRDATA (CYL, f2_unit.u3, 8) }, + { DRDATA (HEAD, seekhead[1], 8) }, + { DRDATA (POS, f2_unit.pos, 32), PV_LEFT }, + { DRDATA (TIME, f2_unit.wait, 24), PV_LEFT }, + { BRDATA (BUF, dbuf, 8, 8, 256) }, + { NULL } +}; + +DEVICE f2_dev = { + "F2", &f2_unit, f2_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &f2_reset, + &f2_boot, &f2_attach, NULL +}; + +/* -------------------------------------------------------------------- */ + +/* 5444: master routines */ + +int32 dsk1 (int32 op, int32 m, int32 n, int32 data) +{ + int32 r; + + r = dsk(0, op, m, n, data); + return (r); +} + +int32 dsk2 (int32 op, int32 m, int32 n, int32 data) +{ + int32 r; + + r = dsk(1, op, m, n, data); + return (r); +} + +/* 5444: operational routine */ + +int32 dsk (int32 disk, int32 op, int32 m, int32 n, int32 data) +{ + int32 iodata, i, j, u, sect, nsects, addr, r, c, res; + int32 F, C, S, N, usave; + UNIT *uptr; + + u = m; + if (disk == 1) u += 2; + F = GetMem(DCAR[disk]+0); /* Flag bits */ + C = GetMem(DCAR[disk]+1); /* Cylinder */ + S = GetMem(DCAR[disk]+2); /* Sector */ + N = GetMem(DCAR[disk]+3); /* Number of sectors */ + switch (u) { + case 0: + uptr = r1_dev.units; + break; + case 1: + uptr = f1_dev.units; + break; + case 2: + uptr = r2_dev.units; + break; + case 3: + uptr = f2_dev.units; + break; + default: + break; + } + if (debug_reg & 0x02) + fprintf(trace, "==> %04X %s %01X,%d,%04X DAR=%04X CAR=%04X C=%02X, S=%02X, N=%02X\n", + IAR[level], + opstr[op], + m, n, data, + DDAR[disk], + DCAR[disk], + C, S, N); + + switch (op) { + + /* SIO 5444 */ + case 0: + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; + diskerr[disk] = 0; /* SIO resets errors */ + found[disk] = 0; /* ... and found bit */ + iodata = 0; + switch (n) { + case 0x00: /* Seek */ + if (S & 0x80) + seekhead[disk] = 1; + else + seekhead[disk] = 0; + if (S & 1) { + uptr -> u3 += N; + } else { + uptr -> u3 -= N; + } + if (uptr -> u3 < 0) + uptr -> u3 = 0; + if (uptr -> u3 > 203) { + uptr -> u3 = 0; + diskerr[disk] |= 0x0100; + if (debug_reg & 0x02) + fprintf(trace, "==> Seek Past End of Disk\n"); + } + + /*sim_activate(uptr, uptr -> wait);*/ + sim_activate(uptr, 1); + + /* Seek arms are the same for both disks on a drive: + update the other arm */ + + usave = uptr -> u3; + if (u == 0) uptr = f1_dev.units; + if (u == 1) uptr = r1_dev.units; + if (u == 2) uptr = f2_dev.units; + if (u == 3) uptr = r2_dev.units; + uptr -> u3 = usave; + + seekbusy[disk] = 1; + iodata = SCPE_OK; + break; + + case 0x01: /* Read */ + switch (data) { + case 0: /* Read data */ + sect = (S >> 2) & 0x3F; + nsects = N + 1; + addr = DDAR[disk]; + + for (i = 0; i < nsects; i++) { + r = read_sector(uptr, dbuf, sect); + if (r != 1 || uptr->u3 != C) { + diskerr[disk] |= 0x0800; + break; + } + for (j = 0; j < DSK_SECTSIZE; j++) { + PutMem(addr, dbuf[j]); + addr++; + } + + if (sect == 55) { /* HJS MODS */ + S = sect; + N = nsects - i - 2; + if (N > -1) diskerr[disk] |= 0x0020; /* end of cyl. */ + DDAR[disk] = addr & 0xFFFF; /* HJS mod */ + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + sim_activate(uptr, 1); + iodata = SCPE_OK; + break; + } + + sect++; + S = sect - 1; + N = nsects - i - 2; + if (sect == 24) + sect = 32; + } + DDAR[disk] = addr & 0xFFFF; /* HJS mod */ + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + /*sim_activate(uptr, uptr -> wait);*/ + sim_activate(uptr, 1); + iodata = SCPE_OK; + break; + case 1: /* Read ID */ + if (uptr -> u3 > 0 && uptr -> u3 < 4) + PutMem(DCAR[disk], 1); + else + PutMem(DCAR[disk], 0); + PutMem(DCAR[disk]+1, uptr -> u3); + PutMem(DCAR[disk]+2, RIDsect[disk]); + RIDsect[disk]++; + if (RIDsect[disk] > 23) + RIDsect[disk] = 32; + if (RIDsect[disk] > 55) + RIDsect[disk] = 0; + break; + case 2: /* Read Diagnostic */ + iodata = STOP_INVDEV; + break; + case 3: /* Verify */ + sect = (S >> 2) & 0x3F; + nsects = N + 1; + addr = DDAR[disk]; + for (i = 0; i < nsects; i++) { + r = read_sector(uptr, dbuf, sect); + if (r != 1 || uptr->u3 != C) { + diskerr[disk] |= 0x0800; + break; + } + if (sect == 55) { /* HJS MODS */ + S = sect; + N = nsects - i - 2; + if (N > -1) diskerr[disk] |= 0x0020; /* end of cyl. */ + DDAR[disk] = addr & 0xFFFF; + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + sim_activate(uptr, 1); + iodata = SCPE_OK; + break; + } + sect++; + S = sect - 1; + N = nsects - i - 2; + if (sect == 24) + sect = 32; + } + DDAR[disk] = addr & 0xFFFF; + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + /*sim_activate(uptr, uptr -> wait);*/ + sim_activate(uptr, 1); + break; + default: + return STOP_INVDEV; + } + break; + case 0x02: /* Write */ + switch (data) { + case 0: /* Write Data */ + sect = (S >> 2) & 0x3F; + nsects = N + 1; + addr = DDAR[disk]; + for (i = 0; i < nsects; i++) { + for (j = 0; j < DSK_SECTSIZE; j++) { + dbuf[j] = GetMem(addr); + addr++; + } + r = write_sector(uptr, dbuf, sect); + if (r != 1 || uptr->u3 != C) { + diskerr[disk] |= 0x0400; + break; + } + if (sect == 55) { /* HJS MODS */ + S = sect; + N = nsects - i - 2; + if (N > -1) diskerr[disk] |= 0x0020; /* end of cyl. */ + DDAR[disk] = addr & 0xFFFF; + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + sim_activate(uptr, 1); + iodata = SCPE_OK; + break; + } + sect++; + S = sect - 1; + N = nsects - i - 2; + if (sect == 24) + sect = 32; + } + DDAR[disk] = addr & 0xFFFF; /* HJS mod */ + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + /*sim_activate(uptr, uptr -> wait);*/ + sim_activate(uptr, 1); + break; + case 1: /* Write identifier */ + if (seekhead[disk] == 0) + S = 0; + else + S = 0x80; + N = 23; + + sect = (S >> 2) & 0x3F; + nsects = N + 1; + addr = DDAR[disk]; + for (i = 0; i < nsects; i++) { + for (j = 0; j < DSK_SECTSIZE; j++) { + dbuf[j] = GetMem(addr); + } + r = write_sector(uptr, dbuf, sect); + if (r != 1) { + diskerr[disk] |= 0x0400; + break; + } + if (sect == 55) { + S = sect; + N = nsects - i - 2; + if (N > 0) diskerr[disk] |= 0x0020; + DDAR[disk] = addr & 0xFFFF; + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + sim_activate(uptr, 1); + iodata = SCPE_OK; + break; + } + sect++; + S = sect - 1; + N = nsects - i - 2; + if (sect == 24) + sect = 32; + } + DDAR[disk] = addr & 0xFFFF; + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + /*sim_activate(uptr, uptr -> wait);*/ + sim_activate(uptr, 1); + break; + default: + return STOP_INVDEV; + } + break; + case 0x03: /* Scan */ + sect = (S >> 2) & 0x3F; + nsects = N + 1; + addr = DDAR[disk]; + for (i = 0; i < nsects; i++) { + r = read_sector(uptr, dbuf, sect); + if (r != 1 || uptr->u3 != C) { + diskerr[disk] |= 0x0800; + break; + } + res = 0; + for (j = 0; j < DSK_SECTSIZE; j++) { + c = GetMem(addr); + if (j != 0xff) { + if (dbuf[i] < c) + res = 1; + if (dbuf[i] > c) + res = 3; + } + addr++; + } + if (res == 0) + found[disk] = 1; + if (res == data) + break; + if (sect == 55) { /* HJS MODS */ + S = sect; + N = nsects - i - 2; + if (N > -1) diskerr[disk] |= 0x0020; /* end of cyl. */ + DDAR[disk] = addr & 0xFFFF; + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + sim_activate(uptr, 1); + iodata = SCPE_OK; + break; + } + sect++; + S = sect - 1; + N = nsects - i - 2; + if (sect == 24) + sect = 32; + } + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + /*sim_activate(uptr, uptr -> wait);*/ + sim_activate(uptr, 1); + break; + default: + return STOP_INVDEV; + } + return iodata; + + /* LIO 5444 */ + case 1: + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; + switch (n) { + case 0x04: /* Data Addr */ + DDAR[disk] = data; + break; + case 0x06: /* Control Addr */ + DCAR[disk] = data; + break; + default: + return STOP_INVDEV; + } + return SCPE_OK; + /* TIO 5444 */ + case 2: + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT << 16; + iodata = 0; + switch (n) { + case 0x00: /* Error */ + if (diskerr[disk] || notrdy[disk]) + iodata = 1; + if ((uptr -> flags & UNIT_ATT) == 0) + iodata = 1; + break; + case 0x02: /* Busy */ + if (sim_is_active (uptr)) + iodata = 1; + break; + case 0x04: + if (found[disk]) + iodata = 1; + break; + default: + return (STOP_INVDEV << 16); + } + return ((SCPE_OK << 16) | iodata); + + /* SNS 5444 */ + case 3: + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT << 16; + iodata = 0; + switch (n) { + case 0x01: + break; + case 0x02: + iodata = diskerr[disk]; + if (notrdy[disk]) + iodata |= 0x4000; + if ((uptr -> flags & UNIT_ATT) == 0) + iodata |= 0x4000; + if (seekbusy[disk]) + iodata |= 0x0010; + if (uptr -> u3 == 0) + iodata |= 0x0040; + break; + case 0x03: + iodata = 0; + break; + case 0x04: + iodata = DDAR[disk]; + break; + case 0x06: + iodata = DCAR[disk]; + break; + default: + return (STOP_INVDEV << 16); + } + iodata |= ((SCPE_OK << 16) & 0xffff0000); + return (iodata); + + /* APL 5444 */ + case 4: + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT << 16; + iodata = 0; + switch (n) { + case 0x00: /* Error */ + if (diskerr[disk] || notrdy[disk]) + iodata = 1; + if ((uptr -> flags & UNIT_ATT) == 0) + iodata = 1; + break; + case 0x02: /* Busy */ + if (sim_is_active (uptr)) + iodata = 1; + break; + default: + return (STOP_INVDEV << 16); + } + return ((SCPE_OK << 16) | iodata); + default: + break; + } + sim_printf (">>DSK%d non-existent function %d\n", disk, op); + return SCPE_OK; +} + +/* Disk unit service. If a stacker select is active, copy to the + selected stacker. Otherwise, copy to the normal stacker. If the + unit is unattached, simply exit. +*/ + +t_stat r1_svc (UNIT *uptr) +{ +seekbusy[0] = 0; +return SCPE_OK; +} +t_stat f1_svc (UNIT *uptr) +{ +seekbusy[0] = 0; +return SCPE_OK; +} +t_stat r2_svc (UNIT *uptr) +{ +seekbusy[1] = 0; +return SCPE_OK; +} +t_stat f2_svc (UNIT *uptr) +{ +seekbusy[1] = 0; +return SCPE_OK; +} + + +/* Disk reset */ + +t_stat r1_reset (DEVICE *dptr) +{ +diskerr[0] = notrdy[0] = seekbusy[0] = 0; /* clear indicators */ +found[0] = 0; +sim_cancel (&r1_unit); /* clear event */ +r1_unit.u3 = 0; /* cylinder 0 */ +return SCPE_OK; +} +t_stat f1_reset (DEVICE *dptr) +{ +diskerr[0] = notrdy[0] = seekbusy[0] = 0; /* clear indicators */ +found[0] = 0; +sim_cancel (&f1_unit); /* clear event */ +f1_unit.u3 = 0; /* cylinder 0 */ +return SCPE_OK; +} +t_stat r2_reset (DEVICE *dptr) +{ +diskerr[1] = notrdy[1] = seekbusy[1] = 0; /* clear indicators */ +found[1] = 0; +sim_cancel (&r2_unit); /* clear event */ +r2_unit.u3 = 0; /* cylinder 0 */ +return SCPE_OK; +} +t_stat f2_reset (DEVICE *dptr) +{ +diskerr[1] = notrdy[1] = seekbusy[1] = 0; /* clear indicators */ +found[1] = 0; +sim_cancel (&f2_unit); /* clear event */ +f2_unit.u3 = 0; /* cylinder 0 */ +return SCPE_OK; +} + +/* Disk unit attach */ + +t_stat r1_attach (UNIT *uptr, char *cptr) +{ +diskerr[0] = notrdy[0] = seekbusy[0] = 0; /* clear status */ +found[0] = 0; +uptr -> u3 = 0; /* cylinder 0 */ +return attach_unit (uptr, cptr); +} +t_stat f1_attach (UNIT *uptr, char *cptr) +{ +diskerr[0] = notrdy[0] = seekbusy[0] = 0; /* clear status */ +found[0] = 0; +uptr -> u3 = 0; /* cylinder 0 */ +return attach_unit (uptr, cptr); +} +t_stat r2_attach (UNIT *uptr, char *cptr) +{ +diskerr[1] = notrdy[1] = seekbusy[1] = 0; /* clear status */ +found[1] = 0; +uptr -> u3 = 0; /* cylinder 0 */ +return attach_unit (uptr, cptr); +} +t_stat f2_attach (UNIT *uptr, char *cptr) +{ +diskerr[1] = notrdy[1] = seekbusy[1] = 0; /* clear status */ +found[1] = 0; +uptr -> u3 = 0; /* cylinder 0 */ +return attach_unit (uptr, cptr); +} + +/* Bootstrap routine */ + +t_stat r1_boot (int32 unitno, DEVICE *dptr) +{ +int i; +r1_unit.u3 = 0; +read_sector(r1_dev.units, dbuf, 0); +for (i = 0; i < 256; i++) { + M[i] = dbuf[i]; +} +return SCPE_OK; +} +t_stat f1_boot (int32 unitno, DEVICE *dptr) +{ +int i; +f1_unit.u3 = 0; +read_sector(f1_dev.units, dbuf, 0); +for (i = 0; i < 256; i++) { + M[i] = dbuf[i]; +} +return SCPE_OK; +} +t_stat r2_boot (int32 unitno, DEVICE *dptr) +{ +int i; +r2_unit.u3 = 0; +read_sector(r2_dev.units, dbuf, 0); +for (i = 0; i < 256; i++) { + M[i] = dbuf[i]; +} +return SCPE_OK; +} +t_stat f2_boot (int32 unitno, DEVICE *dptr) +{ +int i; +f2_unit.u3 = 0; +read_sector(f2_dev.units, dbuf, 0); +for (i = 0; i < 256; i++) { + M[i] = dbuf[i]; +} +return SCPE_OK; +} + + +/* Raw Disk Data In/Out */ + +int32 read_sector(UNIT *uptr, char *dbuf, int32 sect) +{ + static int32 rtn, realsect; + static long pos; + + /* calculate real sector no */ + if (sect > 23) + realsect = sect - 8; + else + realsect = sect; + /* physically read the sector */ + pos = DSK_CYLSIZE * uptr -> u3; + pos += DSK_SECTSIZE * realsect; + rtn = fseek(uptr -> fileref, pos, 0); + rtn = fread(dbuf, DSK_SECTSIZE, 1, uptr -> fileref); + return (rtn); +} + +int32 write_sector(UNIT *uptr, char *dbuf, int32 sect) +{ + static int32 rtn, realsect; + static long pos; + + /* calculate real sector no */ + if (sect > 23) + realsect = sect - 8; + else + realsect = sect; + if (uptr -> u3 == 0 && realsect == 32) + rtn = 0; + /* physically write the sector */ + pos = DSK_CYLSIZE * uptr -> u3; + pos += DSK_SECTSIZE * realsect; + rtn = fseek(uptr -> fileref, pos, 0); + rtn = fwrite(dbuf, DSK_SECTSIZE, 1, uptr -> fileref); + return (rtn); +} diff --git a/S3/s3_lp.c b/S3/s3_lp.c new file mode 100644 index 00000000..a0507f50 --- /dev/null +++ b/S3/s3_lp.c @@ -0,0 +1,364 @@ +/* s3_lp.c: IBM 1403 line printer simulator + + Copyright (c) 2001-2012, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + lpt 1403 line printer + + 19-Mar-12 RMS Fixed declaration of conversion tables (Mark Pizzolato) + 25-Apr-03 RMS Revised for extended file support + 08-Oct-02 RMS Added impossible function catcher +*/ + +#include "s3_defs.h" + +extern uint8 M[]; +extern char bcd_to_ascii[64]; +extern int32 iochk, ind[64]; +int32 cct[CCT_LNT] = { 03 }; +int32 cctlnt = 66, cctptr = 0, lines = 0, lflag = 0; +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, char *cptr); +t_stat write_line (int32 ilnt, int32 mod); +t_stat space (int32 lines, int32 lflag); +t_stat carriage_control (int32 action, int32 mod); +extern unsigned char ebcdic_to_ascii[]; + +#define UNIT_V_PCHAIN (UNIT_V_UF + 0) +#define UNIT_M_PCHAIN 03 +#define M_UCS 00 /* Universal */ +#define M_PCF 00 /* full */ +#define M_PCA 01 /* business */ +#define M_PCH 02 /* Fortran */ +#define UNIT_PCHAIN (UNIT_M_PCHAIN << UNIT_V_PCHAIN) +#define UCS (M_UCS << UNIT_V_PCHAIN) +#define PCF (M_PCF << UNIT_V_PCHAIN) +#define PCA (M_PCA << UNIT_V_PCHAIN) +#define PCH (M_PCH << UNIT_V_PCHAIN) +#define GET_PCHAIN(x) (((x) >> UNIT_V_PCHAIN) & UNIT_M_PCHAIN) +#define CHP(ch,val) ((val) & (1 << (ch))) + +int32 LPDAR; /* Data Address */ +int32 LPFLR; /* Forms Length */ +int32 LPIAR; /* Image address */ +int32 linectr; /* current line # */ +int32 lpterror = 0; +int32 CC9 = 0; +int32 CC12 = 0; + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +UNIT lpt_unit = { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }; + +REG lpt_reg[] = { + { FLDATA (ERR, lpterror, 0) }, + { HRDATA (LPDAR, LPDAR, 16) }, + { HRDATA (LPFLR, LPFLR, 8) }, + { HRDATA (LPIAR, LPIAR, 16) }, + { DRDATA (LINECT, linectr, 8) }, + { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, + { BRDATA (CCT, cct, 8, 32, CCT_LNT) }, + { DRDATA (LINES, lines, 8), PV_LEFT }, + { DRDATA (CCTP, cctptr, 8), PV_LEFT }, + { DRDATA (CCTL, cctlnt, 8), REG_RO + PV_LEFT }, + { GRDATA (CHAIN, lpt_unit.flags, 10, 2, UNIT_V_PCHAIN), REG_HRO }, + { NULL } +}; + +MTAB lpt_mod[] = { + { UNIT_PCHAIN, UCS, "UCS", "UCS", NULL }, + { UNIT_PCHAIN, PCA, "A chain", "PCA", NULL }, + { UNIT_PCHAIN, PCH, "H chain", "PCH", NULL }, + { 0 } +}; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, lpt_mod, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &lpt_reset, + NULL, NULL, NULL +}; + + +/* -------------------------------------------------------------------- */ + +/* Printer: master routine */ + +int32 lpt (int32 op, int32 m, int32 n, int32 data) +{ + int32 iodata; + switch (op) { + case 0: /* SIO 1403 */ + iodata = 0; +// printf("\0"); + switch (n) { + case 0x00: /* Spacing only */ + if (data > 0 && data < 4) + iodata = carriage_control(2, data); + break; + case 0x02: /* Print & space */ + iodata = write_line(0, 0); + if (data > 3) data = 0; + if (iodata == SCPE_OK) + iodata = carriage_control(2, data); + break; + case 0x04: /* Skip only */ + iodata = carriage_control(4, data); + break; + case 0x06: /* Print and skip */ + iodata = write_line(0, 0); + if (iodata == SCPE_OK) + iodata = carriage_control(4, data); + break; + default: + return STOP_INVDEV; + } + return iodata; + case 1: /* LIO 1403 */ + switch (n) { + case 0x00: /* LPFLR */ + LPFLR = (data >> 8) & 0xff; + break; + case 0x04: + LPIAR = data & 0xffff; + break; + case 0x06: + LPDAR = data & 0xffff; + break; + default: + return STOP_INVDEV; + } + return SCPE_OK; + case 2: /* TIO 1403 */ + iodata = 0; + switch (n) { + case 0x00: /* Not ready/check */ + if (lpterror) + iodata = 1; + if ((lpt_unit.flags & UNIT_ATT) == 0) + iodata = 1; + break; + case 0x02: /* Buffer Busy */ + iodata = 0; + break; + case 0x04: /* Carriage Busy */ + iodata = 0; + break; + case 0x06: /* Printer busy */ + iodata = 0; + break; + default: + return (STOP_INVDEV << 16); + } + return ((SCPE_OK << 16) | iodata); + case 3: /* SNS 1403 */ + switch (n) { + case 0x00: /* Line count */ + iodata = (linectr << 8); + break; + case 0x02: /* Timing data */ + iodata = 0; + break; + case 0x03: /* Check data */ + iodata = 0; + break; + case 0x04: /* LPIAR */ + iodata = LPIAR; + break; + case 0x06: /* LPDAR */ + iodata = LPDAR; + break; + default: + return (STOP_INVDEV << 16); + } + return ((SCPE_OK << 16) | iodata); + case 4: /* APL 1403 */ + iodata = 0; + return ((SCPE_OK << 16) | iodata); + default: + break; + } + sim_printf (">>LPT non-existent function %d\n", op); + return SCPE_OK; +} + + +/* Print routine + + Modifiers have been checked by the caller + S = suppress automatic newline +*/ + +t_stat write_line (int32 ilnt, int32 mod) +{ +int32 i, t, lc; +static char lbuf[LPT_WIDTH + 1]; /* + null */ + +if ((lpt_unit.flags & UNIT_ATT) == 0) + return SCPE_UNATT; + +lpterror = 0; +lc = LPDAR; /* clear error */ +for (i = 0; i < LPT_WIDTH; i++) { /* convert print buf */ + t = M[lc]; + lbuf[i] = ebcdic_to_ascii[t & 0xff]; + M[lc] = 0x40; /* HJS MOD */ + lc++; +} +for (i = LPT_WIDTH - 1; (i >= 0) && (lbuf[i] == ' '); i--) lbuf[i] = 0; +fputs (lbuf, lpt_unit.fileref); /* write line */ +if (lines) space (lines, lflag); /* cc action? do it */ +else if (mod == 0) space (1, FALSE); /* default? 1 line */ +else { + fputc ('\r', lpt_unit.fileref); /* sup -> overprint */ + lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ +} +lines = lflag = 0; /* clear cc action */ +if (ferror (lpt_unit.fileref)) { /* error? */ + perror ("Line printer I/O error"); + clearerr (lpt_unit.fileref); + lpterror = 1; +} +return SCPE_OK; +} + +/* Carriage control routine + + Parameters: + action = 00, skip to channel now + = 01, space lines after + = 02, space lines now + = 03, skip to channel after + = 04, skip to line number + mod = number of lines or channel number or line number +*/ + +t_stat carriage_control (int32 action, int32 mod) +{ +int32 i; + +if ((lpt_unit.flags & UNIT_ATT) == 0) + return SCPE_UNATT; + +switch (action) { +case 0: /* to channel now */ + if ((mod == 0) || (mod > 12) || CHP (mod, cct[cctptr])) return SCPE_OK; + for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */ + if (CHP (mod, cct[(cctptr + i) % cctlnt])) + return space (i, TRUE); + } + return STOP_INVDEV; /* runaway channel */ +case 1: /* space after */ + if (mod <= 3) { + lines = mod; /* save # lines */ + lflag = FALSE; /* flag spacing */ + CC9 = CC12 = 0; + } + return SCPE_OK; +case 2: /* space now */ + if (mod <= 3) return space (mod, FALSE); + return SCPE_OK; +case 3: /* to channel after */ + if ((mod == 0) || (mod > 12)) return SCPE_OK; /* check channel */ + CC9 = CC12 = 0; + for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */ + if (CHP (mod, cct[(cctptr + i) % cctlnt])) { + lines = i; /* save # lines */ + lflag = TRUE; /* flag skipping */ + return SCPE_OK; + } + } + return STOP_INVDEV; +case 4: /* To line # */ + if (mod < 2) { + fputs ("\n\f", lpt_unit.fileref); /* nl, ff */ + linectr = 1; + } else { + if (mod <= linectr) { + fputs ("\n\f", lpt_unit.fileref); + linectr = 1; + } + while (1) { + if (linectr == mod) + break; + space(1, 0); + } + } + return SCPE_OK; +} +return SCPE_OK; +} + +/* Space routine - space or skip n lines + + Inputs: + count = number of lines to space or skip + sflag = skip (TRUE) or space (FALSE) +*/ + +t_stat space (int32 count, int32 sflag) +{ +int32 i; + +if ((lpt_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; +cctptr = (cctptr + count) % cctlnt; /* adv cct, mod lnt */ +if (sflag && CHP (0, cct[cctptr])) { /* skip, top of form? */ + fputs ("\n\f", lpt_unit.fileref); /* nl, ff */ + linectr = 1; +} else { + for (i = 0; i < count; i++) fputc ('\n', lpt_unit.fileref); +} +lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ +CC9 = CHP (9, cct[cctptr]) != 0; /* set indicators */ +CC12 = CHP (12, cct[cctptr]) != 0; +linectr += count; +if (linectr > LPFLR) + linectr -= LPFLR; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +cctptr = 0; /* clear cct ptr */ +lines = linectr = lflag = 0; /* no cc action */ +lpterror = 0; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ +cctptr = 0; /* clear cct ptr */ +lines = 0; /* no cc action */ +lpterror = 0; +linectr = 0; +return attach_unit (uptr, cptr); +} diff --git a/S3/s3_pkb.c b/S3/s3_pkb.c new file mode 100644 index 00000000..7dcdd4d4 --- /dev/null +++ b/S3/s3_pkb.c @@ -0,0 +1,313 @@ +/* s3_pkb.c: System/3 5471 console terminal simulator + + Copyright (c) 2001-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + pkb 5471 printer/keyboard + + 25-Apr-03 RMS Revised for extended file support + 08-Oct-02 RMS Added impossible function catcher +*/ + +#include "s3_defs.h" +#include + +extern int32 int_req, dev_busy, dev_done, dev_disable; +t_stat pkb_svc (UNIT *uptr); +t_stat pkb_reset (DEVICE *dptr); +extern int32 IAR[], level; +extern int32 debug_reg; + +/* 5471 data structures + + pkb_dev TTI device descriptor + pkb_unit TTI unit descriptor + pkb_reg TTI register list + pkb_mod TTI/TTO modifiers list +*/ + +/* Flag bits : (kept in pkb_unit.u3) */ + +#define PRT_INTREQ 0x800 /* Printer interrupt pending */ +#define KBD_INTREQ 0x400 /* Request key interrupt pending */ +#define KBD_INTEND 0x200 /* End or cancel key interrupt pending */ +#define KBD_INTKEY 0x100 /* Return or other key interrupt pending */ +#define KBD_REQLIGHT 0x20 /* Request Pending Indicator (light on/off) */ +#define KBD_PROLIGHT 0x10 /* Proceed indicator (light on/off) */ +#define KBD_REQINT 0x04 /* Req key interrupts enabled */ +#define KBD_KEYINT 0x02 /* Other key interrupts enabled */ +#define PRT_PRTINT 0x01 /* Printer interrupts enabled */ + +/* Keys mapped to 5471 functions */ + +int32 key_req = 0x01; /* Request key: ^A */ +int32 key_rtn = 0x12; /* Return key: ^R */ +int32 key_can = 0x1B; /* Cancel key: ESC */ +int32 key_end = 0x0d; /* End key - CR */ + +UNIT pkb_unit = { UDATA (&pkb_svc, 0, 0), KBD_POLL_WAIT }; + +REG pkb_reg[] = { + { HRDATA (FLAG, pkb_unit.u3, 16) }, + { HRDATA (IBUF, pkb_unit.buf, 8) }, + { HRDATA (OBUF, pkb_unit.u4, 8) }, + { HRDATA (REQKEY, key_req, 8) }, + { HRDATA (RTNKEY, key_rtn, 8) }, + { HRDATA (CANKEY, key_can, 8) }, + { HRDATA (ENDKEY, key_end, 8) }, + { DRDATA (POS, pkb_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, pkb_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } +}; + +MTAB pkb_mod[] = { + { 0 } +}; + +DEVICE pkb_dev = { + "PKB", &pkb_unit, pkb_reg, pkb_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &pkb_reset, + NULL, NULL, NULL +}; + + +/*-------------------------------------------------------------------*/ +/* EBCDIC to ASCII translate table */ +/*-------------------------------------------------------------------*/ +unsigned char ebcdic_to_ascii[] = { +"\x00\x01\x02\x03\xA6\x09\xA7\x7F\xA9\xB0\xB1\x0B\x0C\x0D\x0E\x0F" +"\x10\x11\x12\x13\xB2\xB4\x08\xB7\x18\x19\x1A\xB8\xBA\x1D\xBB\x1F" +"\xBD\xC0\x1C\xC1\xC2\x0A\x17\x1B\xC3\xC4\xC5\xC6\xC7\x05\x06\x07" +"\xC8\xC9\x16\xCB\xCC\x1E\xCD\x04\xCE\xD0\xD1\xD2\x14\x15\xD3\xFC" +"\x20\xD4\x83\x84\x85\xA0\xD5\x86\x87\xA4\xD6\x2E\x3C\x28\x2B\xD7" +"\x26\x82\x88\x89\x8A\xA1\x8C\x8B\x8D\xD8\x21\x24\x2A\x29\x3B\x5E" +"\x2D\x2F\xD9\x8E\xDB\xDC\xDD\x8F\x80\xA5\x7C\x2C\x25\x5F\x3E\x3F" +"\xDE\x90\xDF\xE0\xE2\xE3\xE4\xE5\xE6\x60\x3A\x23\x40\x27\x3D\x22" +"\xE7\x61\x62\x63\x64\x65\x66\x67\x68\x69\xAE\xAF\xE8\xE9\xEA\xEC" +"\xF0\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\xF1\xF2\x91\xF3\x92\xF4" +"\xF5\x7E\x73\x74\x75\x76\x77\x78\x79\x7A\xAD\xA8\xF6\x5B\xF7\xF8" +"\x9B\x9C\x9D\x9E\x9F\xB5\xB6\xAC\xAB\xB9\xAA\xB3\xBC\x5D\xBE\xBF" +"\x7B\x41\x42\x43\x44\x45\x46\x47\x48\x49\xCA\x93\x94\x95\xA2\xCF" +"\x7D\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\xDA\x96\x81\x97\xA3\x98" +"\x5C\xE1\x53\x54\x55\x56\x57\x58\x59\x5A\xFD\xEB\x99\xED\xEE\xEF" +"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\xFE\xFB\x9A\xF9\xFA\xFF" +}; + +/*-------------------------------------------------------------------*/ +/* ASCII to EBCDIC translate table */ +/*-------------------------------------------------------------------*/ +unsigned char ascii_to_ebcdic[] = { +"\x00\x01\x02\x03\x37\x2D\x2E\x2F\x16\x05\x25\x0B\x0C\x0D\x0E\x0F" +"\x10\x11\x12\x13\x3C\x3D\x32\x26\x18\x19\x1A\x27\x22\x1D\x35\x1F" +"\x40\x5A\x7F\x7B\x5B\x6C\x50\x7D\x4D\x5D\x5C\x4E\x6B\x60\x4B\x61" +"\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\x7A\x5E\x4C\x7E\x6E\x6F" +"\x7C\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xD1\xD2\xD3\xD4\xD5\xD6" +"\xD7\xD8\xD9\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xAD\xE0\xBD\x5F\x6D" +"\x79\x81\x82\x83\x84\x85\x86\x87\x88\x89\x91\x92\x93\x94\x95\x96" +"\x97\x98\x99\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xC0\x6A\xD0\xA1\x07" +"\x68\xDC\x51\x42\x43\x44\x47\x48\x52\x53\x54\x57\x56\x58\x63\x67" +"\x71\x9C\x9E\xCB\xCC\xCD\xDB\xDD\xDF\xEC\xFC\xB0\xB1\xB2\xB3\xB4" +"\x45\x55\xCE\xDE\x49\x69\x04\x06\xAB\x08\xBA\xB8\xB7\xAA\x8A\x8B" +"\x09\x0A\x14\xBB\x15\xB5\xB6\x17\x1B\xB9\x1C\x1E\xBC\x20\xBE\xBF" +"\x21\x23\x24\x28\x29\x2A\x2B\x2C\x30\x31\xCA\x33\x34\x36\x38\xCF" +"\x39\x3A\x3B\x3E\x41\x46\x4A\x4F\x59\x62\xDA\x64\x65\x66\x70\x72" +"\x73\xE1\x74\x75\x76\x77\x78\x80\x8C\x8D\x8E\xEB\x8F\xED\xEE\xEF" +"\x90\x9A\x9B\x9D\x9F\xA0\xAC\xAE\xAF\xFD\xFE\xFB\x3F\xEA\xFA\xFF" +}; + +/* -------------------------------------------------------------------- */ + +/* Console Input: master routine */ + +int32 pkb (int32 op, int32 m, int32 n, int32 data) +{ + int32 iodata= 0, ec, ac; + switch (op) { + case 0: /* SIO 5471 */ + if (n != 0) + return STOP_INVDEV; + /*printf("%04X SIO %d,%d,%02X\n\r", IAR[level]-4, m, n, data);*/ + if (m == 0) { /* Keyboard */ + pkb_unit.u3 &= 0xFC1; + pkb_unit.u3 |= data; + if (data & 0x01) { + pkb_unit.u3 &= ~KBD_INTREQ; + pkb_unit.u3 &= ~KBD_INTKEY; + pkb_unit.u3 &= ~KBD_INTEND; + return RESET_INTERRUPT; + } + } else { /* Printer */ + if (data & 0x80) { /* start print bit */ + if (debug_reg & 0x80) + return STOP_IBKPT; + ec = pkb_unit.u4 & 0xff; + ac = ebcdic_to_ascii[ec]; + sim_putchar(ac); + pkb_unit.u3 |= PRT_INTREQ; + } + if (data & 0x40) { /* Carr. Return */ + sim_putchar('\n'); + sim_putchar('\r'); + pkb_unit.u3 |= PRT_INTREQ; + } + pkb_unit.u3 &= 0xFFe; + if (data & 0x04) /* Print interrupt flag */ + pkb_unit.u3 |= PRT_PRTINT; + if (data & 0x01) { /* Reset Interrupt */ + if (level < 8) { + if (!(data & 0x80)) + pkb_unit.u3 &= ~PRT_INTREQ; + return RESET_INTERRUPT; + } + } + } + return SCPE_OK; + case 1: /* LIO 5471 */ + if (n != 0) + return STOP_INVDEV; + if (m != 1) + return STOP_INVDEV; + pkb_unit.u4 = (data >> 8) & 0xff; + return SCPE_OK; + break; + case 2: /* TIO 5471 */ + return STOP_INVDEV; + case 3: /* SNS 5471 */ + if (n != 1 && n != 3) + return (STOP_INVDEV << 16); + if (m == 0) { /* Keyboard data */ + if (n == 1) { /* Sense bytes 0 & 1 */ + iodata = (pkb_unit.buf << 8) & 0xff00; + if (pkb_unit.u3 & KBD_INTREQ) + iodata |= 0x80; + if (pkb_unit.u3 & KBD_INTEND) + iodata |= 0x40; + if (pkb_unit.u3 & KBD_INTKEY) + iodata |= 0x08; + if (pkb_unit.buf == 0x12) /* Return key */ + iodata |= 0x04; + if (pkb_unit.buf == 0x03) /* Cancel key */ + iodata |= 0x20; + if (pkb_unit.buf == 0x0d) /* End key */ + iodata |= 0x10; + iodata |= ((SCPE_OK << 16) & 0xffff0000); + } else { /* Sense bytes 2 & 3 */ + iodata = 0; /* Manual says CE use only */ + } + } else { /* Printer Data */ + if (n == 1) { /* Sense bytes 0 & 1 */ + iodata = 0; + if (pkb_unit.u3 & PRT_INTREQ) + iodata |= 0x80; + } else { + iodata = 0; /* CE use only */ + } + } + iodata |= ((SCPE_OK << 16) & 0xffff0000); + return (iodata); + case 4: /* APL 5471 */ + return STOP_INVDEV; + default: + break; + } + sim_printf (">>PKB non-existent function %d\n", op); + return SCPE_OK; +} + +/* Unit service */ + +t_stat pkb_svc (UNIT *uptr) +{ +int32 temp, ac, ec; + +sim_activate (&pkb_unit, pkb_unit.wait); /* continue poll */ + +if (pkb_unit.u3 & PRT_INTREQ) { /* Printer Interrupt */ + int_req |= 2; + return SCPE_OK; +} + +/* Keyboard : handle input */ + +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ + +ac = temp & 0x7f; /* placed type ASCII char in ac */ +if (pkb_unit.u3 & KBD_REQINT) { + if (ac == key_req) { /* Request Key */ + pkb_unit.u3 |= KBD_INTREQ; + int_req |= 2; + return SCPE_OK; + } +} +if (islower(ac)) + ac = toupper(ac); +ec = ascii_to_ebcdic[ac]; /* Translate */ +pkb_unit.buf = ec; /* put in buf */ +pkb_unit.pos = pkb_unit.pos + 1; +if (ac == key_end) { /* End key */ + if (pkb_unit.u3 & KBD_KEYINT) { + pkb_unit.u3 |= KBD_INTEND; + pkb_unit.buf = 0x0d; + int_req |= 2; + } + return SCPE_OK; +} +if (ac == key_can) { /* Cancel key */ + if (pkb_unit.u3 & KBD_KEYINT) { + pkb_unit.u3 |= KBD_INTEND; + pkb_unit.buf = 0x03; + int_req |= 2; + } + return SCPE_OK; +} +if (ac == key_rtn) { /* Return key */ + if (pkb_unit.u3 & KBD_KEYINT) { + pkb_unit.u3 |= KBD_INTKEY; + pkb_unit.buf = 0x12; + int_req |= 2; + } + return SCPE_OK; +} +if (pkb_unit.u3 & KBD_KEYINT) { /* Key interupts enabled ? */ + int_req |= 2; /* Device 1 Interrupt! */ + pkb_unit.u3 |= KBD_INTKEY; /* Set pending flag */ +} +return SCPE_OK; +} + +/* Reset routine */ + +t_stat pkb_reset (DEVICE *dptr) +{ +pkb_unit.buf = 0; +int_req = int_req & ~0x02; /* reset interrupt */ +sim_activate (&pkb_unit, pkb_unit.wait); /* activate unit */ +return SCPE_OK; +} + +t_stat pkb_setmod (UNIT *uptr, int32 value) +{ +return SCPE_OK; +} + diff --git a/S3/s3_sys.c b/S3/s3_sys.c new file mode 100644 index 00000000..85ee11a9 --- /dev/null +++ b/S3/s3_sys.c @@ -0,0 +1,950 @@ +/* s3_sys.c: IBM System/3 system interface + + Copyright (c) 2001-2020, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + 06-Jun-20 RMS Fixed declaration of bldaddr (Mark Pizzolato) + 19-Mar-12 RMS Fixed declaration of conversion tables (Mark Pizzolato) +*/ + +#include +#include "s3_defs.h" + +extern DEVICE cpu_dev; +extern DEVICE pkb_dev; +extern DEVICE cdr_dev; +extern DEVICE cdp_dev; +extern DEVICE stack_dev; +extern DEVICE lpt_dev; +extern DEVICE r1_dev; +extern DEVICE f1_dev; +extern DEVICE r2_dev; +extern DEVICE f2_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern unsigned char M[]; +extern int32 saved_PC, IAR[]; +extern unsigned char ebcdic_to_ascii[]; +char *parse_addr(char *cptr, char *gbuf, t_addr *addr, int32 *addrtype); + +int32 printf_sym (FILE *of, char *strg, t_addr addr, uint32 *val, + UNIT *uptr, int32 sw); + +/* SCP data structures + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words needed for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "System/3"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 6; + +DEVICE *sim_devices[] = { + &cpu_dev, + &pkb_dev, + &cdr_dev, + &cdp_dev, + &stack_dev, + &lpt_dev, + &r1_dev, + &f1_dev, + &r2_dev, + &f2_dev, + NULL +}; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unknown I/O Instruction", + "HALT instruction", + "Breakpoint", + "Invalid Opcode", + "Invalid Qbyte", + "Invalid Address", + "Invalid Device Command", + "ATTN Card Reader" +}; + +/* This is the opcode master defintion table. Each possible opcode mnemonic + is defined here, with enough information to translate to and from + symbolic to binary machine code. + First field is the opcode's mnemonic + Second field is the hex of the right nybble of the binary opcode + Third field is the Q code for those with implicit Q codes + Fourth field is the symbolic format of the operands: + 0 - (Q-byte),(R-byte) + 1 - (Q-byte),(Address) + 2 - (Address),(Address),(Qbyte) + 3 - (Address),(Qbyte) + 4 - (device),(modifier),(function) -- these 3 make up qbyte + 5 - (device),(modifier),(function),(control) + 6 - (device),(modifier),(function),(Address) + 7 - (displacement) -- Q byte is implicit in opcode + 8 - (address) -- Qbyte is implicit in opcode + 9 - (Address),(Address) -- Qbyte is implicit in opcode + Fifth Field is the group number: + 0 - Command Group (left op nybble is F) + 1 - One Address Operations A (Left Nybble C, D, or E) + 2 - Two Address Operations (Left Nybble 0,1,2,4,5,6,8,9, or A) + 3 - One Address Operations B (left Nybble 3, 7, or B) + + There is duplication in this table -- IBM defines different opcodes + that resolve to the same binary machine instruction -- e.g. JE and + JZ. On input this is no problem, on output, define the one you + want to appear first, the second will never appear on output. +*/ + +int32 nopcode = 75; +struct opdef opcode[75] = { + {"HPL", 0x00,0,0,0}, /** Halt Program Level */ + {"A", 0x06,0,1,3}, /** Add to Register: A R,AADD */ + {"ST", 0x04,0,1,3}, /** Store Register */ + {"L", 0x05,0,1,3}, /** Load Register */ + {"LA", 0x02,0,1,1}, /** Load Address */ + {"ZAZ", 0x04,0,2,2}, /** Zero and Add Zoned */ + {"AZ", 0x06,0,2,2}, /** Add Zoned Decimal */ + {"SZ", 0x07,0,2,2}, /** Subtract Zoned Decimal */ + {"ALC", 0x0E,0,2,2}, /** Add Logical: ALC BADD,AADD,LEN */ + {"SLC", 0x0F,0,2,2}, /** Sub Logical: SLC BADD,AADD,LEN */ + {"MVC", 0x0C,0,2,2}, /** Move Chars MVX BADD,AADD,LEN */ + {"ED", 0x0A,0,2,2}, /** Edit: ED BADD,AADD,LEN */ + {"ITC", 0x0B,0,2,2}, /** Insert Chars: ITC BADD,AADD,LEN */ + {"CLC", 0x0D,0,2,2}, /** Compare Logical: CLC BADD,AADD,LEN */ + {"MVI", 0x0C,0,3,3}, /** Move Immediate */ + {"SBN", 0x0A,0,3,3}, /** Set Bits On */ + {"SBF", 0x0B,0,3,3}, /** Set Bits Off */ + {"CLI", 0x0D,0,3,3}, /** Compare Immediate */ + {"TBN", 0x08,0,3,3}, /** Test Bits On */ + {"TBF", 0x09,0,3,3}, /** Test Bits Off */ + {"APL", 0x01,0,4,0}, /** Advance Program Level */ + {"SIO", 0x03,0,5,0}, /** Start I/O */ + {"SNS", 0x00,0,6,3}, /** Sense I/O */ + {"LIO", 0x01,0,6,3}, /** Load I/O */ + {"TIO", 0x01,0,6,1}, /** Test I/O */ + {"J", 0x02,0,7,0}, /** Jump Unconditional */ + {"J", 0x02,0x87,7,0}, /* Alternate J */ + {"JH", 0x02,132,7,0}, /* Jump if High */ + {"JL", 0x02,130,7,0}, /* Jump if Low */ + {"JE", 0x02,129,7,0}, /* Jump if Equal */ + {"JNH", 0x02,4,7,0}, /** Jump if Not High */ + {"JNL", 0x02,2,7,0}, /** Jump if Not Low */ + {"JNE", 0x02,1,7,0}, /** Jump if Not Equal */ + {"JOZ", 0x02,136,7,0}, /* Jump if Overflow Zoned */ + {"JOL", 0x02,160,7,0}, /* Jump if Overflow Logical */ + {"JNOZ", 0x02,8,7,0}, /** Jump if No Overflow Zoned */ + {"JNOL", 0x02,32,7,0}, /* Jump if No Overflow Logical */ + {"JT", 0x02,16,7,0}, /* Jump if True */ + {"JF", 0x02,144,7,0}, /* Jump if False */ + {"JP", 0x02,132,7,0}, /* Jump if Plus */ + {"JM", 0x02,130,7,0}, /* Jump if Minus */ + {"JZ", 0x02,129,7,0}, /* Jump if Zero */ + {"JNP", 0x02,4,7,0}, /** Jump if Not Plus */ + {"JNM", 0x02,2,7,0}, /** Jump if Not Minus */ + {"JNZ", 0x02,1,7,0}, /** Jump if Not Zero */ + {"NOPJ", 0x02,0x80,7,0}, /* Never Jump - NOP */ + {"B", 0x00,0x00,8,1}, /* Branch Unconditional */ + {"B", 0x00,0x87,8,1}, /* Alternate B */ + {"BH", 0x00,0x84,8,1}, /* Branch if High */ + {"BL", 0x00,0x82,8,1}, /* Branch if Low */ + {"BE", 0x00,0x81,8,1}, /* Branch if Equal */ + {"BNH", 0x00,0x04,8,1}, /* Branch if Not High */ + {"BNL", 0x00,0x02,8,1}, /* Branch if Not Low */ + {"BNE", 0x00,0x01,8,1}, /* Branch if Not Equal */ + {"BOZ", 0x00,0x88,8,1}, /* Branch if Overflow Zoned */ + {"BOL", 0x00,0xA0,8,1}, /* Branch if Overflow Logical */ + {"BNOZ", 0x00,0x08,8,1}, /* Branch if No Overflow Zoned */ + {"BNOL", 0x00,0x20,8,1}, /* Branch if No Overflow Logical */ + {"BT", 0x00,0x10,8,1}, /* Branch if True */ + {"BF", 0x00,0x90,8,1}, /* Branch if False */ + {"BP", 0x00,0x84,8,1}, /* Branch if Plus */ + {"BM", 0x00,0x82,8,1}, /* Branch if Minus */ + {"BZ", 0x00,0x81,8,1}, /* Branch if Zero */ + {"BNP", 0x00,0x04,8,1}, /* Branch if Not Plus */ + {"BNM", 0x00,0x02,8,1}, /* Branch if Not Minus */ + {"BNZ", 0x00,0x01,8,1}, /* Branch if Not Zero */ + {"NOPB", 0x00,0x80,8,1}, /* Never Branch - NOP */ + {"MZZ", 0x08,0,9,2}, /** Move Zone to Zone */ + {"MNZ", 0x08,1,9,2}, /** Move Numeric to Zone */ + {"MZN", 0x08,2,9,2}, /** Move Zone to Numeric */ + {"MNN", 0x08,3,9,2}, /** Move Numeric to Numeric */ + {"MVX", 0x08,0,2,2}, /** Move Hex: MVX BADD,AADD,CODE */ + {"JC", 0x02,0,3,0}, /** Jump on Specified Condition bits */ + {"BC", 0x00,0,3,1}, /** Branch on Specified Condition */ + {"***", 0x00,0,0,0} +}; + +int32 regcode[15] = { 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, + 0x80, 0xC0, 0xA0, 0x90, 0x88, 0x84, 0x82, 0x81 +}; + +char regname[15][8] = { "(P2IAR)", + "(P1IAR)", + "(IAR)", + "(ARR)", + "(PSR)", + "(XR2)", + "(XR1)", + "(IAR0)", + "(IAR1)", + "(IAR2)", + "(IAR3)", + "(IAR4)", + "(IAR5)", + "(IAR6)", + "(IAR7)" +}; + +/* This is the binary loader. The input file is considered to be + a string of literal bytes with no special format. The + load starts at the current value of the P1IAR. +*/ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +int32 i, addr = 0, cnt = 0; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +addr = IAR[8]; +while ((i = getc (fileref)) != EOF) { + M[addr] = i & 0xff; + addr++; + cnt++; +} /* end while */ +printf ("%d Bytes loaded.\n", cnt); +return (SCPE_OK); +} + +/* Symbolic output + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + status = error code +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, uint32 *val, + UNIT *uptr, int32 sw) +{ + int32 r; + char strg[256]; + + strcpy(strg, ""); + r = printf_sym(of, strg, addr, val, uptr, sw); + if (sw & SWMASK ('A')) + strcpy(strg, ""); + else + fprintf(of, "%s", strg); + return (r); +} + +t_stat printf_sym (FILE *of, char *strg, t_addr addr, uint32 *val, + UNIT *uptr, int32 sw) +{ +int32 c1, c2, group, len1, len2, inst, aaddr, baddr; +int32 oplen, groupno, i, j, vpos, qbyte, da, m, n; +char bld[128], bldaddr[160], boperand[32], aoperand[32]; +int32 blk[16], blt[16]; +int32 blkadd; + +c1 = val[0] & 0xff; +if (sw & SWMASK ('A')) { + for (i = 0; i < 16; i++) { + blkadd = addr + (i*16); + for (j = 0; j < 16; j++) { + blk[j] = M[blkadd+j] & 0xff; + c2 = ebcdic_to_ascii[blk[j]]; + if (c2 < 040 || c2 > 0177 || blk[j] == 07) { + blt[j] = '.'; + } else { + blt[j] = c2; + } + } + if (i == 0) { + fprintf(of, "%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X [%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c]\n ", + blk[0], blk[1], blk[2], blk[3], blk[4], blk[5], blk[6], blk[7], + blk[8], blk[9], blk[10], blk[11], blk[12], blk[13], blk[14], blk[15], + blt[0], blt[1], blt[2], blt[3], blt[4], blt[5], blt[6], blt[7], + blt[8], blt[9], blt[10], blt[11], blt[12], blt[13], blt[14], blt[15]); + } else { + fprintf(of, "%X\t%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X [%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c]\n ", + blkadd, blk[0], blk[1], blk[2], blk[3], blk[4], blk[5], blk[6], blk[7], + blk[8], blk[9], blk[10], blk[11], blk[12], blk[13], blk[14], blk[15], + blt[0], blt[1], blt[2], blt[3], blt[4], blt[5], blt[6], blt[7], + blt[8], blt[9], blt[10], blt[11], blt[12], blt[13], blt[14], blt[15]); + } + } + return SCPE_OK; } +if (sw & SWMASK ('C')) { + c2 = ebcdic_to_ascii[c1]; + if (c2 < 040 || c2 > 0177) { + sprintf(strg, "<%02X>", c1 & 0xff); + } else { + sprintf (strg, "%c", c2 & 0xff); + } + return SCPE_OK; } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +inst = val[0] & 0x0f; +len1 = (val[0] >> 6) & 3; +len2 = (val[0] >> 4) & 3; +group = (val[0] >> 4) & 0x0f; +qbyte = val[1]; + +/* Get total length of instruction */ + +if (group == 0x0f) { + oplen = 3; +} else { + oplen = 2; + if (len1 == 0) oplen += 2; + if (len1 == 1 || len1 == 2) oplen++; + if (len2 == 0) oplen += 2; + if (len2 == 1 || len2 == 2) oplen++; +} + +/* Find which group it belongs to */ + +switch (group) { + case 0x0f: + groupno = 0; + break; + case 0x0c: + case 0x0d: + case 0x0e: + groupno = 1; + break; + case 0x03: + case 0x07: + case 0x0b: + groupno = 3; + break; + default: + groupno = 2; + break; +} + +/* find the table entry */ + +for (i = 0; i < nopcode; i++) { + if (opcode[i].form < 7) { /* Explicit Q */ + if (opcode[i].group == groupno && + opcode[i].opmask == inst) break; + } else { /* Implicit Q */ + if (opcode[i].group == groupno && + opcode[i].opmask == inst && + opcode[i].q == qbyte) break; + } +} + +/* print the opcode */ + +if (i >= nopcode) { + sprintf(strg, "%02X", val[0]); + oplen = 1; +} else { + sprintf(bld, "%s ", opcode[i].op); + + /* Extract the addresses into aaddr and baddr */ + + strcpy(aoperand, "ERROR"); + strcpy(boperand, "ERROR"); + vpos = 2; + aaddr = baddr = 0; + switch (len1) { + case 0: + baddr = ((val[vpos] << 8) & 0xff00) | (val[vpos + 1] & 0x00ff); + sprintf(boperand, "%04X", baddr); + vpos = 4; + break; + case 1: + baddr = val[vpos] & 255; + sprintf(boperand, "(%02X,XR1)", baddr); + vpos = 3; + break; + case 2: + baddr = val[vpos] & 255; + sprintf(boperand, "(%02X,XR2)", baddr); + vpos = 3; + break; + default: + baddr = 0; + break; + } + switch (len2) { + case 0: + aaddr = ((val[vpos] << 8) & 0xff00) | (val[vpos + 1] & 0x00ff); + if (group == 0x0C || group == 0x0D || group == 0x0E) + sprintf(boperand, "%04X", aaddr); + else + sprintf(aoperand, "%04X", aaddr); + break; + case 1: + aaddr = val[vpos] & 255; + if (group == 0x0C || group == 0x0D || group == 0x0E) + sprintf(boperand, "(%02X,XR1)", aaddr); + else + sprintf(aoperand, "(%02X,XR1)", aaddr); + break; + case 2: + aaddr = val[vpos] & 255; + if (group == 0x0C || group == 0x0D || group == 0x0E) + sprintf(boperand, "(%02X,XR2)", aaddr); + else + sprintf(aoperand, "(%02X,XR2)", aaddr); + break; + default: + aaddr = 0; + break; + } + + /* Display the operands in the correct format */ + + da = (qbyte >> 4) & 0x0f; + m = (qbyte >> 3) & 0x01; + n = (qbyte) & 0x07; + + switch (opcode[i].form) { + case 0: + sprintf(bldaddr, "%02X,%02X", qbyte, val[2]); + break; + case 1: + if (inst == 2 || inst == 4 || inst == 5 || inst == 6) { + for (i = 0; i < 16; i++) { + if (regcode[i] == qbyte) + break; + } + if (i < 16) { + sprintf(bldaddr, "%s,%s", regname[i], boperand); + } else { + sprintf(bldaddr, "%02X,%s", qbyte, boperand); + } + } else { + sprintf(bldaddr, "%02X,%s", qbyte, boperand); + } + break; + case 2: + if (inst > 9 || inst == 4 || inst == 6 || inst == 7) + qbyte++; /* special +1 for length display */ + sprintf(bldaddr, "%s,%s,%d", boperand, aoperand, qbyte); + break; + case 3: + if (strcmp(opcode[i].op, "JC") == 0) { + sprintf(bldaddr, "%04X,%02X", addr+oplen+val[2], qbyte); + } else { + sprintf(bldaddr, "%s,%02X", boperand, qbyte); + } + break; + case 4: + sprintf(bldaddr, "%d,%d,%d", da, m, n); + break; + case 5: + sprintf(bldaddr, "%d,%d,%d,%02X", da, m, n, val[2]); + break; + case 6: + sprintf(bldaddr, "%d,%d,%d,%s", da, m, n, boperand); + break; + case 7: + sprintf(bldaddr, "%04X", addr+oplen+val[2]); + break; + case 8: + sprintf(bldaddr, "%s", boperand); + break; + default: + sprintf(bldaddr, "%s,%s", boperand, aoperand); + break; + } + sprintf(strg, "%s%s", bld, bldaddr); +} + +return -(oplen - 1); +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 cflag, i = 0, j, r, oplen, addtyp, saveaddr, vptr; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (unsigned int) cptr[0]; + return SCPE_OK; +} +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = ((unsigned int) cptr[0] << 8) + (unsigned int) cptr[1]; + return SCPE_OK; +} + +/* An instruction: get opcode (all characters until null, comma, left paren, + or numeric (including spaces). +*/ + +while (1) { + if (*cptr == ',' || *cptr == '\0' || *cptr == '(' || + isdigit(*cptr)) + break; + gbuf[i] = toupper(*cptr); + cptr++; + i++; +} + +/* kill trailing spaces if any */ +gbuf[i] = '\0'; +for (j = i - 1; gbuf[j] == ' '; j--) { + gbuf[j] = '\0'; +} + +/* find opcode in table */ +for (j = 0; j < nopcode; j++) { + if (strcmp(gbuf, opcode[j].op) == 0) + break; +} +if (j >= nopcode) /* not found */ + return SCPE_ARG; + +oplen = 2; /* start with op & q */ + +val[0] = opcode[j].opmask; /* store opcode right nybble */ + +switch (opcode[j].form) { /* Get operands based on operand format */ + case 0: /* Single Byte Operand */ + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); /* Get Q Byte */ + sscanf(gbuf, "%x", &r); + val[1] = r; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); /* Get R Byte */ + sscanf(gbuf, "%x", &r); + val[2] = r; + oplen = 3; + val[0] = 0xf0 | opcode[j].opmask; + break; + case 1: + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + if (opcode[j].opmask == 2 || + opcode[j].opmask == 4 || + opcode[j].opmask == 5 || + opcode[j].opmask == 6) { + if (isdigit(gbuf[0])) { + sscanf(gbuf, "%x", &r); + } else { + for (i = 0; i < 16; i++) { + if (strcmp(gbuf, regname[i]) == 0) + break; + } + if (i < 16) { + r = regcode[i]; + } else { + return SCPE_ARG; + } + } + } else { + sscanf(gbuf, "%x", &r); + } + if (r > 255) return SCPE_ARG; + val[1] = r; + if (*cptr == ',') cptr++; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + val[2] = (addr >> 8) & 0x00ff; + val[3] = addr & 0xff; + oplen = 4; + if (opcode[j].group == 1) + val[0] = 0xC0 | opcode[j].opmask; + else + val[0] = 0x30 | opcode[j].opmask; + break; + case 1: + val[2] = addr & 0xff; + oplen = 3; + if (opcode[j].group == 1) + val[0] = 0xD0 | opcode[j].opmask; + else + val[0] = 0x70 | opcode[j].opmask; + break; + case 2: + val[2] = addr & 0xff; + oplen = 3; + if (opcode[j].group == 1) + val[0] = 0xE0 | opcode[j].opmask; + else + val[0] = 0xB0 | opcode[j].opmask; + break; + default: + return SCPE_ARG; + break; + } + break; + case 2: + oplen = 2; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + val[2] = (addr >> 8) & 0xff; + val[3] = addr & 0xff; + oplen += 2; + vptr = 4; + val[0] = 0x00 | opcode[j].opmask; + break; + case 1: + val[2] = addr & 0xff; + oplen += 1; + vptr = 3; + val[0] = 0x40 | opcode[j].opmask; + break; + case 2: + val[2] = addr & 0xff; + oplen += 1; + vptr = 3; + val[0] = 0x80 | opcode[j].opmask; + break; + default: + return SCPE_ARG; + break; + } + if (*cptr == ',') cptr++; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + val[vptr] = (addr >> 8) & 0xff; + val[vptr+1] = addr & 0xff; + oplen += 2; + break; + case 1: + val[vptr] = addr & 0xff; + oplen += 1; + val[0] = 0x10 | val[0]; + break; + case 2: + val[vptr] = addr & 0xff; + oplen += 1; + val[0] = 0x20 | val[0]; + break; + default: + return SCPE_ARG; + break; + } + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); + sscanf(gbuf, "%d", &r); + if (opcode[j].opmask > 9 || + opcode[j].opmask == 4 || + opcode[j].opmask == 6 || + opcode[j].opmask == 7) r--; /* special: length -1 */ + val[1] = r; + if (*cptr == ',') cptr++; + break; + case 3: + saveaddr = addr; + if (*cptr == ',') cptr++; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + if (opcode[j].group == 0) { /* Group 0 form 3 is JC with explicit Q */ + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); + sscanf(gbuf, "%x", &r); + if ((addr - (saveaddr+3)) > 255 || (addr - (saveaddr+3)) < 1) + return SCPE_ARG; + val[2] = addr - (saveaddr+3); + val[1] = r; + val[0] = 0xf0 | opcode[j].opmask; + oplen = 3; + + } else { + val[2] = (addr >> 8) & 0x00ff; + val[3] = addr & 0xff; + oplen = 4; + if (opcode[j].group == 1) + val[0] = 0xC0 | opcode[j].opmask; + else + val[0] = 0x30 | opcode[j].opmask; + } + break; + case 1: + val[2] = addr & 0xff; + oplen = 3; + if (opcode[j].group == 1) + val[0] = 0xD0 | opcode[j].opmask; + else + val[0] = 0x70 | opcode[j].opmask; + break; + case 2: + val[2] = addr & 0xff; + oplen = 3; + if (opcode[j].group == 1) + val[0] = 0xE0 | opcode[j].opmask; + else + val[0] = 0xB0 | opcode[j].opmask; + break; + default: + return SCPE_ARG; + break; + } + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); + sscanf(gbuf, "%x", &r); + if (r > 255) return SCPE_ARG; + val[1] = r; + break; + case 4: + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 15) return SCPE_ARG; + val[1] = (r << 4) & 0xf0; + val[0] = 0xf0 | opcode[j].opmask; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 1) return SCPE_ARG; + val[1] |= (r << 3) & 0x08; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); + sscanf(gbuf, "%d", &r); + if (r > 7) return SCPE_ARG; + val[1] |= r & 0x07; + val[2] = 0; + oplen = 3; + break; + case 5: + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 15) return SCPE_ARG; + val[1] = (r << 4) & 0xf0; + val[0] = 0xf0 | opcode[j].opmask; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 1) return SCPE_ARG; + val[1] |= (r << 3) & 0x08; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 7) return SCPE_ARG; + val[1] |= r & 0x07; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); + sscanf(gbuf, "%x", &r); + if (r > 255) return SCPE_ARG; + val[2] = r; + oplen = 3; + break; + case 6: + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 15) return SCPE_ARG; + val[1] = (r << 4) & 0xf0; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 1) return SCPE_ARG; + val[1] |= (r << 3) & 0x08; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 7) return SCPE_ARG; + val[1] |= r & 0x07; + if (*cptr == ',') cptr++; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + val[2] = (addr >> 8) & 0x00ff; + val[3] = addr & 0xff; + oplen = 4; + if (opcode[j].group == 1) + val[0] = 0xC0 | opcode[j].opmask; + else + val[0] = 0x30 | opcode[j].opmask; + break; + case 1: + val[2] = addr & 0xff; + oplen = 3; + if (opcode[j].group == 1) + val[0] = 0xD0 | opcode[j].opmask; + else + val[0] = 0x70 | opcode[j].opmask; + break; + case 2: + val[2] = addr & 0xff; + oplen = 3; + if (opcode[j].group == 1) + val[0] = 0xE0 | opcode[j].opmask; + else + val[0] = 0xB0 | opcode[j].opmask; + break; + default: + return SCPE_ARG; + break; + } + break; + case 7: + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); + sscanf(gbuf, "%x", &r); + if ((r - (addr+3)) > 255 || (r - (addr+3)) < 1) return SCPE_ARG; + val[2] = r - (addr+3); + val[1] = opcode[j].q; + val[0] = 0xf0 | opcode[j].opmask; + oplen = 3; + break; + + case 8: + if (*cptr == ',') cptr++; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + val[2] = (addr >> 8) & 0x00ff; + val[3] = addr & 0xff; + oplen = 4; + val[0] = 0xC0 | opcode[j].opmask; + break; + case 1: + val[2] = addr & 0xff; + oplen = 3; + val[0] = 0xD0 | opcode[j].opmask; + break; + case 2: + val[2] = addr & 0xff; + oplen = 3; + val[0] = 0xE0 | opcode[j].opmask; + break; + default: + return SCPE_ARG; + break; + } + val[1] = opcode[j].q; + break; + case 9: + oplen = 2; + val[0] = 0; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + val[2] = (addr >> 8) & 0xff; + val[3] = addr & 0xff; + oplen += 2; + vptr = 4; + val[0] = 0x00 | opcode[j].opmask; + break; + case 1: + val[2] = addr & 0xff; + oplen += 1; + vptr = 3; + val[0] = 0x40 | opcode[j].opmask; + break; + case 2: + val[2] = addr & 0xff; + oplen += 1; + vptr = 3; + val[0] = 0x80 | opcode[j].opmask; + break; + default: + return SCPE_ARG; + break; + } + if (*cptr == ',') cptr++; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + val[vptr] = (addr >> 8) & 0xff; + val[vptr+1] = addr & 0xff; + oplen += 2; + break; + case 1: + val[vptr] = addr & 0xff; + oplen += 1; + val[0] = 0x10 | val[0]; + break; + case 2: + val[vptr] = addr & 0xff; + oplen += 1; + val[0] = 0x20 | val[0]; + break; + default: + return SCPE_ARG; + break; + } + val[1] = opcode[j].q; + break; + default: + break; +} + + +return (-(oplen-1)); +} + +char *parse_addr(char *cptr, char *gbuf, t_addr *addr, int32 *addrtype) +{ +int32 nybble = 0; +char temp[32]; + +cptr = get_glyph(cptr, gbuf, ','); +if (gbuf[0] == '(') { /* XR relative */ + strcpy(temp, gbuf+1); + sscanf(temp, "%x", addr); + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + nybble = -1; + if (strcmp(gbuf, "XR1)") == 0) + nybble = 1; + if (strcmp(gbuf, "XR2)") == 0) + nybble = 2; +} else { /* Direct */ + sscanf(gbuf, "%x", addr); + nybble = 0; +} +*addrtype = nybble; +return cptr; +} + diff --git a/S3/system3.txt b/S3/system3.txt new file mode 100644 index 00000000..e01a97df --- /dev/null +++ b/S3/system3.txt @@ -0,0 +1,472 @@ + The IBM System/3 simulator is configured as follows: + + CPU 5410 (Model 10) CPU with 64KB of memory. + PKB 5471 Printer/Keyboard console. + CDR 1442 Card Reader + CDP 1442 Card Punch + CDP2 1442 2nd stacker + LPT 1403 Line Printer + R1 5444 Top Drive (removeable) + F1 5444 Top Drive (fixed) + R2 5444 Bottom Drive (removeable) + F2 5444 Bottom Drive (fixed + + The only CPU options are to set Model 15 mode (not implemented), DPF +(Dual Programming Facility, not implemented), and the memory size 8K, 16K, +32K, 48K, or 64K. + + CPU registers are the standard System/3 set: + + name size Description + + IAR-P1 16 Instruction Address Register for Program Level 1 + ARR-P2 16 Address Recall Register for Program Level 1 + IAR-P2 16 IAR for Program Level 2 (not implemented) + ARR-P2 16 ARR for Program Level 2 (not implemented) + AAR 16 A-Address Register + BAR 16 B-Address Register + PSR 16 Program Status Register + XR1 16 Index Register 1 + XR2 16 Index Register 2 + IAR<0:7> 16 IAR for interrupt level 0 thru 7 + ARR<0:7> 16 ARR for interrupt level 0 thru 7 + + Plus these simulator registers: + + IAR 16 Value of last IAR used. + LEVEL 8 Current operating level (8=P1, 9=P2, + 0 thru 7 = Interrupt level) + SR 16 Front Panel switches + INT 16 Interrupt Request Flags + WRU 8 Simulator Interrupt Character + BREAK 17 Breakpoint Address + DEBUG 16 Debugging bits: + 0x01: Write all instructions executed to + file trace.log. + 0x02: Write details of all Disk I/O + requests to trace.log. + 0x80: Breakpoint on first character + written to 5471 printer. + +1 5471 Printer/Keyboard + + This is the operator console. It has the following registers: + + FLAG 5471 Flag Bytes + IBUF: Input character from keyboard + OBUF: Output character to printer + POS: Number of characters printed + TIME: Delay for device operation + REQKEY: ASCII value of key mapped to 5471 REQUEST key + RTNKEY: ASCII value of key mapped to 5471 RETURN key + ENDKEY: ASCII value of key mapped to 5471 END key + CANKEY: ASCII value of key mapped to 5471 CANCEL key + + +2 1442 Card Reader. This reader reads 80-column cards; the input + is usually an ASCII file which is translated to EBCDIC when read, + but optionally can be a binary file in EBCDIC format (such as an + object program). + + LAST Last card switch + ERR Card Reader Error + NOTRDY 1442 reader not ready (not attached or past EOF) + DAR Data Address Register (shared with punch) + LCR Length Count Register (shared with punch) + EBCDIC EBCDIC mode flag: if 1, input is 80-col EBCDIC binary. + (IPL from 1442 automatically sets this to 1). + S2 Stacker 2 is selected when this is 1 + POS Number of cards read + TIME Device Delay + + The real hardware 1442 had only 1 hopper for cards, whether + these were used for blank cards for punching, or cards to be + read. Cards could be read without a feed cycle, then + punched. When punching cards, the SCP does a read of a card, + makes sure it is blank, and then punches it. To simulate + this without requiring that a stack of blank lines be attached + to the card reader device, a special feature of the simulator + is this: if no file is attached to the cdr device, but a file + is attached to the cdp or the cdp2 devices, any read from the + reader will return a blank card -- i.e. when punching, an + unattached cdr device is assumed to be loaded with an unlimited + supply of blank cards. + + +3 1442 Card Punch. Normally cards are written to the attached + disk file as ASCII with newline/cr delimiters. But an optional + flag allows writing as 80-column binary EBCDIC. + + ERR Card Punch Error + EBCDIC When this is 1, output will be 80-col EBCDIC. + S2 When this is 1, output is placed in stacker 2 + NOTRDY 1442 punch not ready (not attached) + DAR Data Address Register (shared with reader) + LCR Length Count Register (shared with reader) + POS Number of cards punched + TIME Device Delay + +4 1442 Stacker 2. When cards are to be punched in stacker 2, + attach a disk file to this device (cdp2) to hold that output. + Note: When SCP punches cards, the default is to punch in + stacker 2. + + POS0 Number of cards punched. + +5 1403 Printer. This is a 132-column output device, emulating + the famous IBM 1403, models 2, 6, and N1. Output is always + translated to ASCII with newline/CR delimiters. Page advance + is output as a form feed. + + ERR 1403 error flags + LPDAR Data Address Register + LPFLR Forms Length Register + LPIAR Image Address Register + LINECT Current Line on form + POS Number of lines printed + +6 5444 Disk Drives (R1, R2, F1, F2) + + The 5444 came as a set of two drives, each with two disks. The + top disk in a drive was removable, the bottom fixed. The first + drive consists of disks R1 and F1, the second drive R2 and F2. + Each disk holds 2,467,600 bytes of user data, plus 3 alternate + tracks and a CE track. Flagging of alternate tracks is not + supported in this version of the simulator. + + NOTRDY Drive not ready (not attached) + SEEK Drive is busy with a seek operation + DAR Data Address Register + CAR Control Address Register + ERR Error Flags (16 bits) + CYL Current Cylinder (0 thru 203) + HEAD Current head (0 or 1) + POS Current position in attached disk file + TIME Device Delay + +7 Symbolic Display and Input + + The System/3 Simulator supports symbolic display and input. + Display is controlled by command line switches: + + (none) display as hex EBCDIC + -c display bytes as characters + -m display instruction mnemonics. + -a display a 256-byte block of memory in both hex and ASCII. + + The symbolic format contains the same elements as the machine + language operation, but not always in the same order. The + operation code frequently specifies both the opcode and the Q byte, + and the top nybble of the opcode is determined by the format of the + addresses. + + Addresses take two forms: the direct address in hex, or a relative + address specified thusly: (byte,XRx) where 'byte' is a 1-byte + offset, and XRx is either XR1 or XR2 for the two index registers. + Use these formats when 'address' is indicated below: + + When 'reg' is mentioned, a mnemonic may be used for the register, + thusly: + + IAR Instruction Address Register for the current program level + ARR Address Recall Register for the current program level + P1IAR IAR for Program Level 1 + P2IAR IAR for Program Level 2 + PSR Program Status Register + XR1 Index Register 1 + XR2 Index Register 2 + IARx IAR for the interrupt level x (x = 0 thru 7) + + All other operands mentioned below are single-byte hex, except for + the length (len) operand of the two-address instructions, which is a + decimal length in the range 1-256. + + In operations where there is a source and a destination, the + destination is always operand 1, the source is operand 2. + + No-address formats: + ------------------ + + HPL hex,hex Halt Program Level, the operands are the + Q and R bytes. + + + One-address formats: + ------------------- + + A reg,address Add to register + CLI address,byte Compare Logical Immediate + MVI address,byte Move Immediate + TBF address,mask Test Bits Off + TBN address,mask Test Bits On + SBF address,mask Set Bits Off + SBN address,mask Set Bits On + ST reg,address Store Register + L reg,address Load Register + LA reg,address Load Address (reg can only be XR1 or XR2) + JC address,cond Jump on Condition + BC address,cond Branch on Condition + + These operations do not specify a qbyte, it is implicit in the + opcode: + + B address Unconditional branch to address + BE address Branch Equal + BNE address Branch Not Equal + BH address Branch High + BNH address Branch Not High + BL address Branch Low + BNL address Branch Not Low + BT address Branch True + BF address Branch False + BP address Branch Plus + BM address Branch Minus + BNP address Branch Not Plus + BNM address Branch Not Minus + BZ address Branch Zero + BNZ address Branch Not Zero + BOZ address Branch Overflow Zoned + BOL address Branch Overflow Logical + BNOZ address Branch No Overflow Zoned + BNOL address Branch No Overflow Logical + NOPB address No - never branch + + (substitute J for B above for a set of Jumps -- 1-byte operand (not + 2), always jumps forward up to 255 bytes from the address following + the Jump instruction. In this case, 'address' cannot be less than + the current address, nor greater than the current address + 255) + + Two-address formats (first address is destination, len is decimal 1-256): + ------------------- + + MVC address,address,len Move Characters + CLC address,address,len Compare Logical Characters + ALC address,address,len Add Logical Characters + SLC address,address,len Subtract Logical Characters + ED address,address,len Edit + ITC address,address,len Insert and Test Characters + AZ address,address,len Add Zoned Decimal + SZ address,address,len Subtract Zoned Decimal + + MNN address,address Move Numeric to Numeric + MNZ address,address Move Numeric to Zone + MZZ address,address Move Zone to Zone + MZN address,address Move Zone to Numeric + + I/O Format + ---------- + + In the I/O format, there are always 3 fields: + + da - Device Address 0-15 (decimal) + m - Modifier 0-1 + n - Function 0-7 + + The meaning of these is entirely defined by the device addressed. + + There may be an optional control byte, or an optional address (based + on the type of instruction). + + SNS da,m,n,address Sense I/O + LIO da,m,n,address Load I/O + TIO da,m,n,address Test I/O + + SIO da,m,n,cc Start I/O -- cc is a control byte + + APL da,m,n Advance Program Level + + +8 Device Programming. + + Note: On a model 15, interrupts are used for all devices. On + other models, interrupts are only used for the printer/keyboard. + + This is a summary of the DA, M, N, and CC codes for all supported + devices: + + 5471 Printer Keyboard + --------------------- + + The PKB has 2 visible indicators: Proceed and Request + Pending. It has a normal keyboard and 4 special keys: + Request, Return, End, and Cancel. + + SIO 1,0,0,XX Start Keyboard Operation, bit masks for XX are: + X'20': Request Pending Indicator On + X'10': Proceed Indicator On + X'04': Request Key Interrupts Enable (1) Disable (0) + X'02': Other Key Interrupts Enable (1) Disable (0) + X'01': Reset request key and other key interrupts + + SIO 1,1,0,XX Start Printer Operation, bit masks for XX are: + X'80': Start Printing + X'40': Start Carrier Return + X'04': Printer Interrupt Enable(1) or Disable (0) + X'01': Reset Printer Interrupt + + LIO 1,1,0,addr Load Printer Output Character + addr is address of low-order (highest numbered) + byte of two-byte field, and high-order byte + (that is, addr - 1) is loaded into output + register to print. Printing is done one character + at a time. + + SNS 1,0,1,addr Sense Status Bytes 0 and 1: + Byte 0 (leftmost) is the character typed in + in EBCDIC. + Byte 1 is status: + X'80': Request key interrupt pending + X'40': End or Cancel key interrupt pending + X'20': Cancel key pressed + X'10': End Key Pressed + X'08': Return or data key interrupt pending + X'04': Return key pressed + + SNS 1,0,3,addr Sense Status Bytes 2 and 3: returns 0000 in + this sim. + + 1442 Reader/Punch + ----------------- + + SIO 5,0,0,XX Feed Card without reading/punching + XX is stacker select for all functions: 0 = stacker + 1 (normal), 1 = stacker 2. + + SIO 5,0,1,XX Read Card + SIO 5,0,2,XX Punch and Feed + SIO 5,0,3,XX Read Column Binary + SIO 5,0,4,XX Punch with no feed + + TIO 5,0,0,addr Branch to addr if not ready or busy + TIO 5,0,2,addr Branch to addr if busy + TIO 5,0,5,addr (mod 15 only) branch if interrupt pending + + APL 5,0,0 Loop (or switch prog levels) if not ready/busy + APL 5,0,2 Loop (or switch) if busy + APL 5,0,5 Loop (or switch) if interrupt pending (mod 15 only) + + LIO 5,0,0,addr Load 2-byte field to Length Count Register + LIO 5,0,4,addr Load 2-byte field to Data Address Register + (DAR is incremented by a read/punch operation and must + be reset every card) + + SNS 5,0,1,addr Sense CE indicators (0000 returned in this sim) + SNS 5,0,2,addr Sense CE indicators (0000 returned in this sim) + SNS 5,0,3,addr Sense Status Indicators: (only simulated bits shown) + X'8000': Read Check + X'4000': Last Card + X'2000': Punch Check + X'1000': Data Overrun + X'0800': Not Ready + + + 1403 Printer + ------------ + + SIO 14,0,0,XX Line space XX lines (0-3 valid in XX) + SIO 14,0,2,XX Print a line space (0-3 valid in XX) + SIO 14,0,4,XX Skip Only (line number 1-112 in XX) + + SIO 14,0,6,XX Print and Skip (line number 0-112 in XX) + + TIO 14,0,0,addr Branch to addr if not ready + TIO 14,0,2,addr Branch to addr if buffer busy + TIO 14,0,3,addr Branch to addr if interrupt pending (mod 15 only) + TIO 14,0,4,addr Branch if carriage busy + TIO 14,0,6,addr Branch if printer busy + + APL 14,0,0 Loop (or switch prog levels) if not ready/check + APL 14,0,2 Loop (or switch) if buffer busy + APL 14,0,3 Loop (or switch) if interrupt pending (mod 15 only) + APL 14,0,4 Loop (or switch) if carriage busy + APL 14,0,6 Loop (or switch) if printer busy + + LIO 14,0,0,addr Load 1 byte to Forms Length Reg at addr-1 + LIO 14,0,4,addr Load 2 bytes to Chain Image Address Register + LIO 14,0,6,addr Load 2 bytes to Data Address Register + + SNS 14,0,0,addr Sense Character Count + SNS 14,0,4,addr Sense LPIAR (Image Address Register) + SNS 14,0,6,addr Sense LPDAR (data addres register) + + + 5444 Disk Drives + ---------------- + + Each drive has two disks (upper and lower), each disk + has two surfaces (upper and lower), each surface has + 24 256-byte sectors, sectors are number 0 thru 23 on + upper surface, 32 thru 55 on lower. + + d = drive, 0 is R1/F1, 1 is R2/F2 + s = surface, 0 = upper (removable), 1 = lower (fixed) + + The Control register points to the leftmost byte of a 4-byte + control field in memory with these bytes: + + F - Flag byte (not supported in this sim) + C - Cylinder Address (0-203) + S - Sector Number (0-23, or 32-55) in top 6 bits + N - Number of sectors minus 1 + + These have meaning for all operations except seek, seek uses + the fields differently. + + SIO 1d,s,0,XX Seek, XX not used, control field is used: + + F - not used + C - not used + S - high bit is head to be used 0-upper 1-lower + low bit is direction to move 0-back 1-forward + N - number of cylinders to move + + SIO 1d,s,1,XX Read, values of XX are as follows: + X'00': Read Data + X'01': Read Identifier (updates control field, no + data is read) + X'02': Read Data Diagnostic + X'03': Verify (does not read, but checks) + + SIO 1d,s,2,XX Write, values of XX are as follows: + X'00': Write Data + X'01': Write Identifier (24 sectors with byte at + data address register) + + SIO 1d,s,3,XX Scan. All 256 bytes in memory at data + address register are compared to disk + sectors on current track, except those + bytes of X'FF' are not compared. Values of + XX are: + + X'00': Scan Equal + X'01': Scan Low or Equal + X'02': Scan High or Equal + + LIO 1d,0,4,addr Load Data Address Register + LIO 1d,0,6,addr Load Disk Control Address Register + + TIO 1d,0,0,addr Branch if not ready/unit check + TIO 1d,0,2,addr Branch if busy + TIO 1d,0,4,addr Branch if Scan Found + + APL 1d,0,0 Loop if not ready/unit check + APL 1d,0,2 Loop if busy + APL 1d,0,4 Loop if scan found + + SNS 1d,0,2,addr Sense Status Bytes 0 and 1: (simulated + bits only are shown, otehrs are 0): + X'1000': equipment check + X'0800': data check + X'0400': No record found + X'0100': Seek Check (past cyl 203) + X'0080': Scan equal Hit + X'0040': Cylinder 0 + X'0020': End of Cylinder + X'0010': Seek Busy + SNS 1d,0,3,addr Sense bytes 2 and 3 (0000 in this sim) + SNS 1d,0,4,addr Sense Data Address Register + SNS 1d,0,6,addr Sense Control Address Register + + + +