/* hp3000_cpu_cis.c: HP 32234A COBOL II Instruction Set simulator | |
Copyright (c) 2016, J. David Bryan | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
Except as contained in this notice, the name of the author shall not be used | |
in advertising or otherwise to promote the sale, use or other dealings in | |
this Software without prior written authorization from the author. | |
29-Dec-16 JDB Disabled interrupt checks pending test generation | |
19-Oct-16 JDB Passes the COBOL-II firmware "A" diagnostic (D441A) | |
06-Oct-16 JDB Passes the COBOL-II firmware "B" diagnostic (D442A) | |
21-Sep-16 JDB Created | |
References: | |
- Machine Instruction Set Reference Manual | |
(30000-90022, June 1984) | |
- HP 3000 Series 64/68/70 Computer Systems Microcode Manual | |
(30140-90045, October 1986) | |
This module implements the HP 32234A COBOL II Extended Instruction | |
Set firmware, also known as the Language Extension Instructions. The set | |
contains these instructions in the firmware extension range 020460-020477: | |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 0 0 0 | S | ALGN | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 0 0 1 | S | ABSN | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 0 0 | B | EDIT | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 0 1 | B | CMPS | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 0 0 | XBR | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 0 1 | PARC | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 0 | ENDP | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | CMPT | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 0 0 0 0 0 0 0 0 | 0 0 0 1 1 | B | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | TCCS | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 0 0 0 0 0 0 0 0 | 0 0 1 | > | = | < | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | CVND | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 0 0 0 0 0 0 0 0 | 0 1 | sign op | S | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | LDW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 0 0 0 0 0 0 0 0 | 1 0 0 0 0 | S | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | LDDW | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 0 0 0 0 0 0 0 0 | 1 0 0 0 1 | S | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | TR | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 0 0 0 0 0 0 0 0 | 1 0 0 1 0 | B | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | ABSD | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 0 0 0 0 0 0 0 0 | 1 0 0 1 1 | S | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | NEGD | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 0 0 0 0 0 0 0 0 | 1 0 1 0 0 | S | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
Where: | |
S = S-decrement | |
B = Bank select (0/1 = PB-relative/DB-relative) | |
The PARC, ENDP, and XBR instructions implement the COBOL "PERFORM" statement. | |
ABSD and NEGD manipulate packed decimal numbers. ALGN, ABSN, EDIT, and CVND | |
manipulate external decimal numbers. The LDW and LDDW instructions load | |
single and double-words, respectively, from byte-aligned addresses. TCCS | |
tests the status register for a specific condition code and loads the logical | |
result; operation is similar to the Bcc instruction, except that the result | |
is explicit rather than implied by the branch. TR translates a string using | |
a mapping table. CMPS and CMPT compare two strings (with translation for | |
CMPT) and set the condition code accordingly. CMPS is similar to CMPB, | |
except that a shorter string is blank-padded for comparison. | |
Packed decimal (also known as COMPUTATIONAL-3, BCD, and binary-coded decimal) | |
numbers contain from 1 to 28 digits that are stored in pairs in successive | |
memory bytes in this format: | |
0 1 2 3 4 5 6 7 | |
+---+---+---+---+---+---+---+---+ | |
| unused/digit | digit | | |
+---+---+---+---+---+---+---+---+ | |
| digit | digit | | |
+---+---+---+---+---+---+---+---+ | |
[...] | |
+---+---+---+---+---+---+---+---+ | |
| digit | digit | | |
+---+---+---+---+---+---+---+---+ | |
| digit | sign | | |
+---+---+---+---+---+---+---+---+ | |
The sign is always located in the lower four bits of the final byte, so | |
numbers with an even number of digits will not use the upper four bits of the | |
first byte. Digits are represented by four-bit values from 0-9 (i.e., in | |
Binary-Coded Decimal or BCD), with the most-significant digit first and the | |
least-significant digit last. The sign is given by one of these encodings: | |
1100 - the number is positive | |
1101 - the number is negative | |
1111 - the number is unsigned | |
All other values are interpreted as meaning the number is positive; however, | |
only one of the three values above is generated. | |
Numbers may begin at an even or odd byte address, and the size of the number | |
(in digits) may be even or odd, so there are four possible cases of packing | |
into 16-bit words: | |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| unused/digit | digit | digit | digit | addr even | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| digit | digit | digit | sign | size even | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| unused/digit | digit | digit | digit | addr even | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| digit | sign | ... | size odd | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| ... | unused/digit | digit | addr odd | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| digit | digit | digit | sign | size ?... | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| ... | unused/digit | digit | addr odd | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| digit | sign | ... | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
External decimal (also known as DISPLAY, numeric display, and ASCII) values | |
contain contain from 1 to 28 digits that are stored as ASCII characters in | |
successive memory bytes in this format: | |
0 1 2 3 4 5 6 7 | |
+---+---+---+---+---+---+---+---+ | |
| digit | | |
+---+---+---+---+---+---+---+---+ | |
| digit | | |
+---+---+---+---+---+---+---+---+ | |
[...] | |
+---+---+---+---+---+---+---+---+ | |
| digit | | |
+---+---+---+---+---+---+---+---+ | |
| digit and sign | | |
+---+---+---+---+---+---+---+---+ | |
The number begins with the most-significant digit. The sign is combined with | |
the least-significant digit in the final byte. Each digit except the LSD | |
must be in the ASCII range "0" through "9". Leading blanks are allowed, and | |
the entire number may be blank, but blanks within a number are not. The | |
least-signifiant digit and sign are represented by either: | |
"0" and "1" through "9" for an unsigned number | |
"{" and "A" through "I" for a positive number | |
"}" and "J" through "R" for a negative number | |
Numbers may begin at an even or odd byte address, and the size of the number | |
(in digits) may be even or odd, so there are four possible cases of packing | |
into 16-bit words: | |
- the number completely fills the words | |
- the number has an unused leading byte in the first word | |
- the number has an unused trailing byte in the last word | |
- the number has an unused byte at each end | |
Any unused bytes are not part of the number and are not disturbed. | |
The EDIT instruction moves bytes from a source string to a target string | |
under the control of a subprogram indicated by a PB- or DB-relative address. | |
The subprogram consists of 8-bit instructions, each followed by zero or more | |
8-bit operands. The subprogram ends with a TE (Terminate Edit) instruction. | |
The supported instructions are: | |
0 1 2 3 4 5 6 7 | |
+---+---+---+---+---+---+---+---+ | |
| 0 0 0 0 | imm. operand | MC - move characters | |
+---+---+---+---+---+---+---+---+ | |
{ | extended operand | } (present if imm. operand = 0) | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 0 0 0 1 | imm. operand | MA - move alphabetics | |
+---+---+---+---+---+---+---+---+ | |
{ | extended operand | } (present if imm. operand = 0) | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | imm. operand | MN - move numerics | |
+---+---+---+---+---+---+---+---+ | |
{ | extended operand | } (present if imm. operand = 0) | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 0 0 1 1 | imm. operand | MNS - move numerics suppressed | |
+---+---+---+---+---+---+---+---+ | |
{ | extended operand | } (present if imm. operand = 0) | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 0 1 0 0 | imm. operand | MFL - move numerics with floating insertion | |
+---+---+---+---+---+---+---+---+ | |
{ | extended operand | } (present if imm. operand = 0) | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 0 1 0 1 | imm. operand | IC - insert character | |
+---+---+---+---+---+---+---+---+ | |
{ | extended operand | } (present if imm. operand = 0) | |
+---+---+---+---+---+---+---+---+ | |
| character to insert | | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 0 1 1 0 | imm. operand | ICS - insert character suppressed | |
+---+---+---+---+---+---+---+---+ | |
{ | extended operand | } (present if imm. operand = 0) | |
+---+---+---+---+---+---+---+---+ | |
| character to insert | | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 0 1 1 1 | imm. operand | ICI - insert characters immediate | |
+---+---+---+---+---+---+---+---+ | |
{ | extended operand | } (present if imm. operand = 0) | |
+---+---+---+---+---+---+---+---+ | |
| character 1 to insert | | |
+---+---+---+---+---+---+---+---+ | |
... | |
+---+---+---+---+---+---+---+---+ | |
{ | character n to insert | } (present if operand > 1) | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 0 0 0 | imm. operand | ICSI - insert characters suppressed immediate | |
+---+---+---+---+---+---+---+---+ | |
{ | extended operand | } (present if imm. operand = 0) | |
+---+---+---+---+---+---+---+---+ | |
| character 1 to insert | | |
+---+---+---+---+---+---+---+---+ | |
... | |
+---+---+---+---+---+---+---+---+ | |
{ | character n to insert | } (present if operand > 1) | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 0 0 1 | imm. operand | BRIS - branch if significance | |
+---+---+---+---+---+---+---+---+ | |
{ | extended operand | } (present if imm. operand = 0) | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 0 1 0 | imm. operand | SUFT - subtract from target | |
+---+---+---+---+---+---+---+---+ | |
{ | extended operand | } (present if imm. operand = 0) | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 0 1 1 | imm. operand | SUFS - subtract from source | |
+---+---+---+---+---+---+---+---+ | |
{ | extended operand | } (present if imm. operand = 0) | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 0 1 1 | imm. operand | ICP - insert character punctuation | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 1 0 0 | imm. operand | ICPS - insert character punctuation suppressed | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 1 1 0 | imm. operand | IS - insert character on sign | |
+---+---+---+---+---+---+---+---+ | |
| positive sign character 1 | | |
+---+---+---+---+---+---+---+---+ | |
... | |
+---+---+---+---+---+---+---+---+ | |
{ | character n to insert | } (present if operand > 1) | |
+---+---+---+---+---+---+---+---+ | |
| negative sign character 1 | | |
+---+---+---+---+---+---+---+---+ | |
... | |
+---+---+---+---+---+---+---+---+ | |
{ | character n to insert | } (present if operand > 1) | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 1 1 1 | 0 0 0 0 | TE - terminate edit | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 1 1 1 | 0 0 0 1 | ENDF - end floating point insertion | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 1 1 1 | 0 0 1 0 | SST1 - set significance to 1 | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 1 1 1 | 0 0 1 1 | SST0 - set significance to 0 | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 1 1 1 | 0 1 0 0 | MDWO - move digit with overpunch | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 1 1 1 | 0 1 0 1 | SFC - set fill character | |
+---+---+---+---+---+---+---+---+ | |
| fill character | | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 1 1 1 | 0 1 1 0 | SFLC - set float character | |
+---+---+---+---+---+---+---+---+ | |
| + float char | - float char | | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 1 1 1 | 0 1 1 1 | DFLC - define float character | |
+---+---+---+---+---+---+---+---+ | |
| positive float character | | |
+---+---+---+---+---+---+---+---+ | |
| negative float character | | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 1 1 1 | 1 0 0 0 | SETC - set loop count | |
+---+---+---+---+---+---+---+---+ | |
| loop count | | |
+---+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+---+ | |
| 1 1 1 1 | 1 0 0 1 | DBNZ - decrement loop count and branch | |
+---+---+---+---+---+---+---+---+ | |
| branch displacement | | |
+---+---+---+---+---+---+---+---+ | |
The EDIT instruction is interruptible after each subprogram command. The TR, | |
CMPS, and CMPT instructions are interruptible after each byte processed. The | |
remaining instructions execute to completion. | |
Two user traps may be taken by these instructions if the T bit is on in the | |
status register: | |
- Word Count Overflow (parameter 17) | |
- Invalid ASCII Digit (parameter 14) | |
The Word Count Overflow trap is taken when an instruction is given an | |
external decimal number with more than 28 digits. The Invalid ASCII Digit | |
trap occurs when an external decimal number contains characters other than | |
"0"-"9", a "+" or "-" sign in any position other than the first or last | |
position, blanks other than leading blanks, an invalid overpunch character, | |
or an overpunch character in any position other than the last digit. | |
Enabling the OPND debug flag traces the instruction operands, including the | |
subprogram operations of the EDIT instruction. | |
Implementation notes: | |
1. In several cases noted below, the hardware microcode implementations | |
differ from the descriptions in the Machine Instruction Set manual. | |
Also, the comments in the microcode source sometimes do not correctly | |
described the microcode actions. In all cases of conflict, the simulator | |
follows the microcode implementation. | |
2. The Machine Instruction Set manual references trap conditions (Invalid | |
Alphabetic Character, Invalid Operand Length, Invalid Source Character | |
Count, and Invalid Digit Count) that are not defined in the Series II/III | |
System Reference Manual. Examination of the microcode indicates that | |
only the Invalid ASCII Digit and Word Count Overflow traps are taken. | |
3. Target operand tracing is not done if a trap occurred, as the result will | |
be invalid. | |
4. The calls to "cpu_interrupt_pending" are currently stubbed out pending | |
testing of interrupted instruction exits and reentries. The COBOL II | |
firmware diagnostics do not test interruptibility, so tests have to be | |
written before correct action is assured. This will be addressed in a | |
future release. | |
*/ | |
#include "hp3000_defs.h" | |
#include "hp3000_cpu.h" | |
#include "hp3000_mem.h" | |
/* Disable intra-instruction interrupt checking pending testing */ | |
#define cpu_interrupt_pending(x) FALSE /* TEMPORARY ONLY */ | |
/* Program constants */ | |
#define MAX_DIGITS 28 /* maximum number of decimal digits accepted */ | |
/* Packed decimal constants */ | |
#define SIGN_MASK 0360u /* 8-bit numeric sign mask */ | |
#define SIGN_TEST_MASK 0017u /* 8-bit numeric sign test mask */ | |
#define SIGN_PLUS 0014u /* 1100 -> the number is positive */ | |
#define SIGN_MINUS 0015u /* 1101 -> the number is negative */ | |
#define SIGN_UNSIGNED 0017u /* 1111 -> the number is unsigned */ | |
#define IS_NEG(v) (((v) & SIGN_TEST_MASK) == SIGN_MINUS) | |
/* External decimal constants */ | |
typedef enum { /* location and type of the numeric sign character */ | |
Leading_Separate = 0, /* plus or minus prefix */ | |
Trailing_Separate = 1, /* plus or minus suffix */ | |
Leading_Overpunch = 2, /* overpunched first character */ | |
Trailing_Overpunch = 3, /* overpunched last character */ | |
Absolute = 4 /* no sign character */ | |
} DISPLAY_MODE; | |
typedef enum { /* numeric sign values */ | |
Negative, | |
Unsigned, | |
Positive | |
} NUMERIC_SIGN; | |
static uint8 overpunch [3] [10] = { /* sign overpunches, indexed by NUMERIC_SIGN */ | |
{ '}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R' }, /* Negative */ | |
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }, /* Unsigned */ | |
{ '{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I' } /* Positive */ | |
}; | |
/* Operand printer function */ | |
typedef char * (*OP_PRINT) (uint32 byte_address, uint32 byte_length); | |
/* CIS local utility routine declarations */ | |
static void branch_external (HP_WORD segment, HP_WORD offset); | |
static uint32 strip_overpunch (uint8 *byte, NUMERIC_SIGN *sign); | |
static uint32 convert (HP_WORD sba, HP_WORD tba, HP_WORD count, DISPLAY_MODE mode); | |
static t_bool edit (t_stat *status, uint32 *trap); | |
static t_bool compare (BYTE_ACCESS *source, BYTE_ACCESS *target, BYTE_ACCESS *table, t_stat *status); | |
static void fprint_operands (BYTE_ACCESS *source, BYTE_ACCESS *target, uint32 trap); | |
static void fprint_translated_operands (BYTE_ACCESS *source, BYTE_ACCESS *target, BYTE_ACCESS *table); | |
static void fprint_operand (BYTE_ACCESS *op, char *label, OP_PRINT operand_printer); | |
/* CIS global routines */ | |
/* Execute a CIS operation. | |
This routine is called to execute the COBOL II instruction currently in the | |
CIR. The instruction format is: | |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | CIS opcode | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
Instructions 020460 - 020476 are decoded directly. Code 020477 introduces a | |
set of two-word instructions; the second word decodes the operation. Codes | |
020464 through 020467 and code 020477 with the second word less than 000006 | |
or greater than 000051 are unimplemented. | |
Entry is with four TOS registers preloaded (this is done for all firmware | |
extension instructions). Therefore, no SR preload is needed here. | |
Instructions that provide option bits to leave addresses on the stack do not | |
modify those addresses during instruction execution. | |
If an invalid digit or digit count is detected, a microcode abort occurs. If | |
the T (trap) bit is not set in the status register, the O (overflow) bit is | |
set and execution continues. If the T bit is set, the O bit is not set and | |
the trap is taken. In either case, the stack is popped according to the | |
instruction. | |
Implementation notes: | |
1. If a two-word instruction has a valid first word (020477) but an invalid | |
second word, the CIR must retain the first-word value and P must be | |
incremented over the second word when the Unimplemented Instruction trap | |
is taken. | |
2. The MICRO_ABORT macro does a longjmp to the microcode abort handler in | |
the sim_instr routine. If the user trap bit (T-bit) in the status word | |
is set, the routine clears the overflow bit (O-bit) and invokes the trap | |
handler. If the T-bit is clear, the routine sets the O-bit and continues | |
with the next instruction. Therefore, we do not need to check the T-bit | |
here and can simply do an unconditional MICRO_ABORT if a trap is | |
indicated. | |
3. Regarding error traps, the "Instruction Commentary" for the Language | |
Extension Instructions section of the Machine Instruction Set manual | |
says, in part: | |
If the User Traps bit (STA.2) is not set, the Overflow bit (STA.4) is | |
set, the stack is popped in accordance with the instruction, and | |
execution continues with the following instruction. If the Traps bit | |
is set, the Overflow bit is not set, the stack is not popped, and a | |
trap to the Traps segment, segment 1, is taken. | |
However, the Series 6x microcode for, e.g., the ALGN instruction does a | |
"JSZ TRPR" after unconditionally popping the stack (page 334). TRPR | |
checks STA.2 and either starts trap processing if it is set or sets | |
Overflow and proceeds to the next instruction if it is clear. So the | |
stack is popped in either case. This implementation follows this | |
behavior. | |
4. In the comments for the ABSD/NEGD instructions, the Series 6x microcode | |
manual says, "Extract the last two bits of the sign..." and then "If the | |
sign bits = 01 (the sign was negative => D), then...", implying that only | |
the lower two bits are tested. The microcode itself, however, tests all | |
four bits and assumes a negative value only if they equal 1101. This | |
behavior agrees with the EIS commentary in the Machine Instruction Set | |
manual, which says that all sign values other than those defined as plus, | |
minus, and unsigned, are treated as plus. This implementation follows | |
this behavior. | |
*/ | |
t_stat cpu_cis_op (void) | |
{ | |
BYTE_ACCESS source, target, table; | |
NUMERIC_SIGN sign; | |
HP_WORD segment, offset, sign_cntl; | |
HP_WORD source_rba, table_rba; | |
char label [64]; | |
uint8 byte; | |
uint8 source_length, source_lead, source_fraction; | |
uint8 target_length, target_lead, target_fraction; | |
uint32 opcode; | |
t_bool store, zero_fill; | |
uint32 trap = trap_None; | |
t_stat status = SCPE_OK; | |
opcode = FMEXSUBOP (CIR); /* get the opcode from the instruction */ | |
switch (opcode) { /* dispatch the opcode */ | |
case 000: /* ALGN (O; INV DIG, WC OVF, STOV, STUN, BNDV) */ | |
case 001: | |
source_length = LOWER_BYTE (RA); /* get the source digit count */ | |
source_fraction = UPPER_BYTE (RA); /* and the source fraction count */ | |
target_length = LOWER_BYTE (RC); /* get the target digit count */ | |
target_fraction = UPPER_BYTE (RC); /* and the target fraction count */ | |
if (source_fraction > source_length /* if the source fraction count is invalid */ | |
|| source_length > MAX_DIGITS /* or the source digit count is too large */ | |
|| target_fraction > target_length /* or the target fraction count is invalid */ | |
|| target_length > MAX_DIGITS) /* or the target digit count is too large */ | |
trap = trap_Word_Count_Overflow; /* then trap for a count overflow */ | |
else if (source_length > 0 && target_length > 0) { /* otherwise if there is an alignment to do */ | |
sign = Unsigned; /* then assume the source is unsigned */ | |
store = TRUE; /* enable storing */ | |
zero_fill = TRUE; /* and zero fill of the target */ | |
source_lead = source_length - source_fraction; /* get the counts of the leading digits */ | |
target_lead = target_length - target_fraction; /* of the source and target numbers */ | |
mem_init_byte (&source, data_checked, &RB, source_length); /* set up byte accessors */ | |
mem_init_byte (&target, data_checked, &RD, target_length); /* for the source and target strings */ | |
while (source_length > 0 || target_length > 0) { /* while digits remain to be aligned */ | |
if (source_lead < target_lead /* if the target has more leading digits */ | |
|| source_length == 0) /* or there are no more source digits */ | |
byte = '0'; /* then transfer a zero */ | |
else { /* otherwise */ | |
store = (source_lead == target_lead); /* don't store if still more leading source digits */ | |
byte = mem_read_byte (&source); /* get a source digit */ | |
source_length = source_length - 1; /* and count it */ | |
if (source_length == 0) { /* if this is the last source digit */ | |
trap = strip_overpunch (&byte, &sign); /* then strip the sign from the digit */ | |
if (trap != trap_None) /* if the overpunch was invalid */ | |
break; /* then abandon the transfer */ | |
} | |
if (source_lead > 0) /* if this is a leading digit */ | |
source_lead = source_lead - 1; /* then count it */ | |
if (byte >= '0' && byte <= '9') /* if it is numeric */ | |
zero_fill = FALSE; /* then turn zero-filling off */ | |
else if (byte == ' ' && zero_fill) /* otherwise if it's a blank and zero-filling is on */ | |
byte = '0'; /* then fill */ | |
else { /* otherwise */ | |
trap = trap_Invalid_ASCII_Digit; /* the digit is invalid */ | |
break; /* so abandon the transfer */ | |
} | |
} | |
if (store && target_length > 0) { /* if storing and target space is available */ | |
if (target_length == 1) /* then if this is the last byte to store */ | |
byte = overpunch [sign] [byte - '0']; /* then overpunch with the source sign */ | |
mem_write_byte (&target, byte); /* store the target digit */ | |
target_length = target_length - 1; /* and count it */ | |
if (target_lead > 0) /* if this is a leading digit */ | |
target_lead = target_lead - 1; /* then count it */ | |
} | |
} /* continue the alignment loop */ | |
mem_update_byte (&target); /* update the final target byte */ | |
if (DPRINTING (cpu_dev, DEB_MOPND)) { | |
sprintf (label, "source fraction %d length", source_fraction); | |
fprint_operand (&source, label, &fmt_byte_operand); | |
if (trap == trap_None) { | |
sprintf (label, "target fraction %d length", target_fraction); | |
fprint_operand (&target, label, &fmt_byte_operand); | |
} | |
} | |
} | |
if (CIR & CIS_SDEC_MASK) /* if the S-decrement bit is set in the instruction */ | |
SR = 0; /* then pop all four operands */ | |
else { /* otherwise */ | |
cpu_pop (); /* pop all */ | |
cpu_pop (); /* of the operands */ | |
cpu_pop (); /* except the target address */ | |
} | |
if (trap == trap_None) /* if the instruction succeeded */ | |
STA &= ~STATUS_O; /* then clear overflow status */ | |
else /* otherwise */ | |
MICRO_ABORT (trap); /* abort with the indicated trap */ | |
break; | |
case 002: /* ABSN (CCA, O; INV DIG, WC OVF, STOV, STUN, BNDV) */ | |
case 003: | |
if (RA > MAX_DIGITS) /* if the digit count is too large */ | |
trap = trap_Word_Count_Overflow; /* then trap for a count overflow */ | |
else if (RA > 0) { /* otherwise if there are digits to process */ | |
source_rba = RB; /* then use a working source byte address pointer */ | |
mem_init_byte (&source, data_checked, /* and set up a byte accessor for the string */ | |
&source_rba, RA); | |
if (DPRINTING (cpu_dev, DEB_MOPND)) | |
fprint_operand (&source, "source", &fmt_byte_operand); | |
byte = mem_read_byte (&source); /* get the first source digit */ | |
while (byte == ' ' && RA > 0) { /* while there are leading blanks */ | |
mem_modify_byte (&source, '0'); /* replace each blank with a zero digit */ | |
RA = RA - 1; /* and count it */ | |
if (RA > 0) /* if there are more digits */ | |
byte = mem_read_byte (&source); /* then get the next one */ | |
} | |
while (RA > 1 && byte >= '0' && byte <= '9') { /* validate the digits */ | |
byte = mem_read_byte (&source); /* by getting and checking */ | |
RA = RA - 1; /* each digit until the last */ | |
} | |
if (RA == 1) { /* if this is the last digit */ | |
trap = strip_overpunch (&byte, &sign); /* then strip the sign from the digit */ | |
if (trap == trap_None) { /* if the overpunch was valid */ | |
if (sign == Negative) /* then if the number was negative */ | |
SET_CCL; /* then set the less-than condition code */ | |
else /* otherwise a positive or zero number */ | |
SET_CCG; /* sets the greater-than condition code */ | |
mem_modify_byte (&source, byte); /* rewrite the digit without the sign overpunch */ | |
} | |
} | |
else if (RA > 0) /* otherwise if we've abandoned the validation */ | |
trap = trap_Invalid_ASCII_Digit; /* then trap for an invalid digit */ | |
mem_post_byte (&source); /* post the last byte written */ | |
if (DPRINTING (cpu_dev, DEB_MOPND) && trap == trap_None) | |
fprint_operand (&source, "target", &fmt_byte_operand); | |
} | |
cpu_pop (); /* pop the digit count */ | |
if (CIR & CIS_SDEC_MASK) /* if the S-decrement bit is set in the instruction */ | |
cpu_pop (); /* then pop the address as well */ | |
if (trap == trap_None) /* if the instruction succeeded */ | |
STA &= ~STATUS_O; /* then clear overflow status */ | |
else /* otherwise */ | |
MICRO_ABORT (trap); /* abort with the indicated trap */ | |
break; | |
case 010: /* EDIT (O; INV DIG, WC OVF, STUN, BNDV) */ | |
case 011: | |
if (edit (&status, &trap)) { /* process the edit subprogram; if no interrupt intervened */ | |
SR = 0; /* then pop all four values from the stack */ | |
if (trap != trap_None) /* if a trap occurred */ | |
MICRO_ABORT (trap); /* then take it now */ | |
} | |
break; | |
case 012: /* CMPS (CCx; STOV, STUN, BNDV) */ | |
case 013: | |
mem_init_byte (&source, data_checked, &RB, RA); /* set up a byte accessor for the first operand */ | |
if (CIR & CIS_DB_FLAG) /* if the second operand is in the data segment */ | |
mem_init_byte (&target, data_checked, &RD, RC); /* then set up a DB byte accessor */ | |
else /* otherwise */ | |
mem_init_byte (&target, program_checked, &RD, RC); /* set up a PB byte accessor */ | |
if (compare (&source, &target, NULL, &status)) { /* compare the strings; if no interrupt intervened */ | |
SR = 0; /* then pop all four values from the stack */ | |
if (DPRINTING (cpu_dev, DEB_MOPND)) | |
fprint_operands (&source, &target, trap); | |
} | |
break; | |
case 014: /* XBR (none; STUN, BNDV, CSTV, MODE, ABS CST, TRACE) */ | |
segment = RA; /* get the segment number */ | |
offset = RB; /* and PB-relative offset of the target */ | |
cpu_pop (); /* pop the operands */ | |
cpu_pop (); | |
branch_external (segment, offset); /* branch to the target location */ | |
break; | |
case 015: /* PARC (none; STOV, STUN, BNDV, CSTV, MODE, ABS CST, TRACE) */ | |
segment = RB; /* get the segment number */ | |
offset = RC; /* and the PB-relative offset of the target */ | |
RB = STA; /* replace the segment number with the current status */ | |
RC = P - 1 - PB & R_MASK; /* and the target with the PB-relative return address */ | |
branch_external (segment, offset); /* branch to the target location */ | |
break; | |
case 016: /* ENDP (none; STUN, BNDV, CSTV, MODE, ABS CST, TRACE) */ | |
if (RA == RB) { /* if the paragraph numbers are equal */ | |
SR = 0; /* then pop all of the parameters */ | |
branch_external (RC, RD); /* and return to the caller */ | |
} | |
else /* otherwise the paragraph numbers are unequal */ | |
cpu_pop (); /* so pop the current paragraph number and continue */ | |
break; | |
case 017: /* double-word instructions */ | |
opcode = NIR; /* get the operation code from the second word */ | |
cpu_read_memory (fetch, P, &NIR); /* load the next instruction */ | |
P = P + 1 & R_MASK; /* and point to the following instruction */ | |
switch (opcode) { /* dispatch the second instruction word */ | |
case 006: /* CMPT (CCx; STOV, STUN, BNDV) */ | |
case 007: | |
cpu_read_memory (stack, SM, &table_rba); /* get the byte address of the translation table */ | |
mem_init_byte (&source, data_checked, &RB, RA); /* set up a byte accessor for the first operand */ | |
if (opcode & CIS_DB_FLAG) /* if the second operand is in the data segment */ | |
mem_init_byte (&target, data_checked, &RD, RC); /* then set up a DB byte accessor */ | |
else /* otherwise */ | |
mem_init_byte (&target, program_checked, &RD, RC); /* set up a PB byte accessor */ | |
mem_init_byte (&table, data, &table_rba, 0); /* set up a byte accessor for the translation table */ | |
if (compare (&source, &target, &table, &status)) { /* compare the strings; if no interrupt intervened */ | |
SR = 0; /* then pop the first four parameters */ | |
cpu_pop (); /* and then the fifth parameter */ | |
if (DPRINTING (cpu_dev, DEB_MOPND)) | |
fprint_translated_operands (&source, &target, &table); | |
} | |
break; | |
case 010: /* TCCS (Test condition code and set, bits 13-15 options) */ | |
case 011: | |
case 012: | |
case 013: | |
case 014: | |
case 015: | |
case 016: | |
case 017: | |
cpu_push (); /* push the stack down */ | |
if ((STA & STATUS_CC_MASK) != STATUS_CCI /* if the condition code is not invalid */ | |
&& TO_CCF (STA) & opcode << TCCS_CCF_SHIFT) /* and the test succeeds */ | |
RA = D16_UMAX; /* then set the TOS to TRUE */ | |
else /* otherwise */ | |
RA = 0; /* set the TOS to FALSE */ | |
break; | |
case 020: /* CVND (O; INV DIG, WC OVF, STOV, STUN, BNDV) */ | |
case 021: | |
case 022: | |
case 023: | |
case 024: | |
case 025: | |
case 026: | |
case 027: | |
case 030: | |
case 031: | |
case 032: | |
case 033: | |
case 034: | |
case 035: | |
case 036: | |
case 037: | |
if (RA > MAX_DIGITS) /* if the digit count is too large */ | |
trap = trap_Word_Count_Overflow; /* then trap for a count overflow */ | |
else if (RA > 0) { /* otherwise if there are digits to convert */ | |
sign_cntl = (opcode & CVND_SC_MASK) /* then get the sign control code */ | |
>> CVND_SC_SHIFT; | |
if (sign_cntl > Absolute) /* if the code is one of the unnormalized values */ | |
sign_cntl = Absolute; /* then normalize it */ | |
trap = convert (RB, RC, RA, sign_cntl); /* convert the number as directed */ | |
} | |
cpu_pop (); /* pop the source character count */ | |
cpu_pop (); /* and source byte address */ | |
if (opcode & CIS_SDEC_MASK) /* if the S-decrement bit is set in the instruction */ | |
cpu_pop (); /* then pop the target byte address also */ | |
if (trap == trap_None) /* if the instruction succeeded */ | |
STA &= ~STATUS_O; /* then clear overflow status */ | |
else /* otherwise */ | |
MICRO_ABORT (trap); /* abort with the indicated trap */ | |
break; | |
case 040: /* LDW (none; STOV, STUN, BNDV) */ | |
case 041: | |
source_rba = RA; /* use a working source byte address pointer */ | |
if ((opcode & CIS_SDEC_MASK) == 0) /* if the S-decrement bit is clear */ | |
cpu_push (); /* then push the stack down for the return value */ | |
mem_init_byte (&source, data_checked, &source_rba, 2); /* set up a byte accessor for the characters */ | |
byte = mem_read_byte (&source); /* get the first byte */ | |
RA = TO_WORD (byte, mem_read_byte (&source)); /* and the second byte and merge them */ | |
if (DPRINTING (cpu_dev, DEB_MOPND)) | |
fprint_operand (&source, "source", &fmt_byte_operand); | |
break; | |
case 042: /* LDDW (none; STOV, STUN, BNDV) */ | |
case 043: | |
source_rba = RA; /* use a working source byte address pointer */ | |
if ((opcode & CIS_SDEC_MASK) == 0) /* if the S-decrement bit is clear */ | |
cpu_push (); /* then push the stack down for the return value */ | |
cpu_push (); /* push again for the two-word return value */ | |
mem_init_byte (&source, data_checked, &source_rba, 4); /* set up a byte accessor for the characters */ | |
byte = mem_read_byte (&source); /* get the first byte */ | |
RA = TO_WORD (byte, mem_read_byte (&source)); /* and the second byte and merge them */ | |
byte = mem_read_byte (&source); /* get the third byte */ | |
RB = TO_WORD (byte, mem_read_byte (&source)); /* and the fourth byte and merge them */ | |
if (DPRINTING (cpu_dev, DEB_MOPND)) | |
fprint_operand (&source, "source", &fmt_byte_operand); | |
break; | |
case 044: /* TR (none; STUN, STOV, BNDV) */ | |
case 045: | |
if (RA > 0) { /* if there are bytes to translate */ | |
mem_init_byte (&source, data_checked, &RC, RA); /* then set up byte accessors */ | |
mem_init_byte (&target, data_checked, &RB, RA); /* for the source and target strings */ | |
if (opcode & CIS_DB_FLAG) /* if the table is in the data segment */ | |
mem_init_byte (&table, data, &RD, 0); /* then set up a DB byte accessor */ | |
else /* otherwise */ | |
mem_init_byte (&table, program, &RD, 0); /* set up a PB byte accessor */ | |
while (RA > 0) { /* while there are bytes to translate */ | |
byte = mem_read_byte (&source); /* get the next byte */ | |
byte = mem_lookup_byte (&table, byte); /* look up the translated value */ | |
mem_write_byte (&target, byte); /* and write it to the target */ | |
RA = RA - 1; /* update the byte count (cannot underflow) */ | |
if (cpu_interrupt_pending (&status)) { /* if an interrupt is pending */ | |
mem_update_byte (&target); /* then update the last word written */ | |
return status; /* and return with an interrupt set up or an error */ | |
} | |
} | |
mem_update_byte (&target); /* update the final target byte */ | |
if (DPRINTING (cpu_dev, DEB_MOPND)) | |
fprint_operands (&source, &target, trap); | |
} | |
SR = 0; /* pop all four values from the stack */ | |
break; | |
case 046: /* ABSD (CCA, O; WC OVF, STOV, STUN, BNDV) */ | |
case 047: | |
case 050: /* NEGD (CCA, O; WC OVF, STOV, STUN, BNDV) */ | |
case 051: | |
if (RA > MAX_DIGITS) /* if the digit count is too large */ | |
trap = trap_Word_Count_Overflow; /* then trap for a count overflow */ | |
else { /* otherwise */ | |
mem_init_byte (&source, data, &RB, RA); /* set up a byte accessor for the operand */ | |
source_rba = RA / 2 + RB; /* index to the trailing sign digit */ | |
mem_init_byte (&target, data, &source_rba, 0); /* and set up a byte accessor for the sign */ | |
if (DPRINTING (cpu_dev, DEB_MOPND)) | |
fprint_operand (&source, "source", &fmt_bcd_operand); | |
byte = mem_read_byte (&target); /* get the sign byte */ | |
if (opcode < 050) { /* if this is an ABSD instruction */ | |
if (IS_NEG (byte)) /* then if the number is negative */ | |
SET_CCL; /* then set the less-than condition code */ | |
else /* otherwise a positive or zero number */ | |
SET_CCG; /* sets the greater-than condition code */ | |
byte |= SIGN_UNSIGNED; /* change the number to unsigned */ | |
} | |
else /* otherwise this is a NEGD instruction */ | |
if (IS_NEG (byte)) { /* so if the number is negative */ | |
byte = byte & SIGN_MASK | SIGN_PLUS; /* then make the number positive */ | |
SET_CCG; /* and set the greater-than condition code */ | |
} | |
else { /* otherwise the number is positive */ | |
byte = byte & SIGN_MASK | SIGN_MINUS; /* so make it negative */ | |
SET_CCL; /* and set the less-than condition code */ | |
} | |
mem_modify_byte (&target, byte); /* rewrite the digit */ | |
mem_post_byte (&target); /* and post it */ | |
if (DPRINTING (cpu_dev, DEB_MOPND)) | |
fprint_operand (&source, "target", &fmt_bcd_operand); | |
} | |
cpu_pop (); /* pop the digit count */ | |
if (opcode & CIS_SDEC_MASK) /* if the S-decrement bit is set in the instruction */ | |
cpu_pop (); /* then pop the address as well */ | |
if (trap == trap_None) /* if the instruction succeeded */ | |
STA &= ~STATUS_O; /* then clear overflow status */ | |
else /* otherwise */ | |
MICRO_ABORT (trap); /* abort with the indicated trap */ | |
break; | |
default: | |
status = STOP_UNIMPL; /* the second instruction word is unimplemented */ | |
} | |
break; /* end of the double-word instructions */ | |
default: | |
status = STOP_UNIMPL; /* the firmware extension instruction is unimplemented */ | |
} | |
return status; /* return the execution status */ | |
} | |
/* CIS local utility routine declarations */ | |
/* Execute a branch to a location in a specified segment. | |
If the target segment is the same as the current segment, as indicated in the | |
status register, then a local label is used. Otherwise, an external label is | |
used that specifies the target segment entry 0 of the STT, which specifies | |
the start of the segment. The target is then set up in the same manner as a | |
procedure call, with the program counter adjusted by the target offset. | |
The procedure setup may abort, rather than returning, if a trap prevents the | |
setup from succeeding. | |
*/ | |
static void branch_external (HP_WORD segment, HP_WORD offset) | |
{ | |
HP_WORD label; | |
if (STATUS_CS (segment) == STATUS_CS (STA)) /* if the target segment is current */ | |
label = LABEL_LOCAL; /* then use a local label */ | |
else /* otherwise */ | |
label = LABEL_EXTERNAL /* use an external label */ | |
| TO_LABEL (STATUS_CS (segment), 0); /* that specifies the target segment and STT 0 */ | |
cpu_call_procedure (label, offset); /* set up the segment as for a procedure call */ | |
return; /* return with the branch completed */ | |
} | |
/* Strip the sign from an overpunched digit. | |
If the supplied digit includes an overpunched sign, it is stripped and | |
returned separately, along with the stripped digit; trap_None is returned to | |
indicate success. If the digit is not a valid overpunch character, then | |
trap_Invalid_ASCII_Digit is returned to indicate failure. | |
Implementation notes: | |
1. A set of direct tests is faster than looping through the overpunch table | |
to search for the matching digit. Faster still would be a direct 256-way | |
reverse overpunch lookup, but the gain is not significant. | |
*/ | |
static uint32 strip_overpunch (uint8 *byte, NUMERIC_SIGN *sign) | |
{ | |
if (*byte == '{') { /* if the digit is a zero with positive overpunch */ | |
*byte = '0'; /* then strip the overpunch */ | |
*sign = Positive; /* and set the returned sign positive */ | |
} | |
else if (*byte >= 'A' && *byte <= 'I') { /* otherwise if the digit is a positive overpunch */ | |
*byte = *byte - 'A' + '1'; /* then strip the overpunch to yield a 1-9 value */ | |
*sign = Positive; /* and set the returned sign positive */ | |
} | |
else if (*byte == '}') { /* otherwise if the digit is a zero with negative overpunch */ | |
*byte = '0'; /* then strip the overpunch */ | |
*sign = Negative; /* and set the returned sign negative */ | |
} | |
else if (*byte >= 'J' && *byte <= 'R') { /* otherwise if the digit is a negative overpunch */ | |
*byte = *byte - 'J' + '1'; /* then strip the overpunch to yield a 1-9 value */ | |
*sign = Negative; /* and set the returned sign negative */ | |
} | |
else if (*byte >= '0' && *byte <= '9') /* otherwise if the digit is not overpunched */ | |
*sign = Unsigned; /* then simply set the return as unsigned */ | |
else /* otherwise the digit is not a valid overpunch */ | |
return trap_Invalid_ASCII_Digit; /* so return an Invalid Digit trap as failure */ | |
return trap_None; /* return no trap for successful stripping */ | |
} | |
/* Convert a numeric display string. | |
This routine converts a numeric display string to an external decimal number. | |
A display string consists of the ASCII characters "0" to "9" and optional | |
leading spaces. The sign may be omitted (unsigned), a separate leading or | |
trailing "+" or "-" sign, or an integral leading or trailing overpunch. The | |
result is an external decimal number, consisting of "0" to "9" digits with a | |
trailing sign overpunch. The routine implements the CVND instruction. | |
The "sba" parameter is the DB-relative byte address of the source string, | |
"tba" is the DB-relative byte address of the target string, "count" is the | |
number of source characters (including the sign character, if separate), and | |
"mode" is the sign display mode of the source (leading separate, trailing | |
separate, leading overpunch, trailing overpunch, or unsigned). The count is | |
always non-zero on entry. | |
The routine validates all of the source characters, even if no conversion is | |
needed, and returns trap_Invalid_ASCII_Digit if validation fails. trap_None | |
is returned if the conversion succeeds. | |
The implementation starts by setting indices for the location of the sign. | |
Separate indices for separate and overpunched signs are kept, with the unused | |
index set to a value beyond the string so that it never matches. If the | |
input has separate sign mode and only one character, then that character is | |
the sign, and a zero value is implied. Leading blanks are zero-filled until | |
the first numeric digit (if the mode is leading overpunch, then the first | |
digit is numeric, so leading blanks are not permitted). | |
Implementation notes: | |
1. The "last_digit" variable is only used in Trailing_Separate mode when | |
there is at least one character other than the sign. Consequently, it | |
will never be used before it is set. However, the compiler cannot detect | |
this and will warn erroneously that it could be used uninitialized. | |
Therefore, it is set (redundantly) before the conversion loop entry. | |
*/ | |
static uint32 convert (HP_WORD sba, HP_WORD tba, HP_WORD count, DISPLAY_MODE mode) | |
{ | |
BYTE_ACCESS source, target; | |
NUMERIC_SIGN sign; | |
HP_WORD separate_index, overpunch_index; | |
uint8 byte, last_digit; | |
uint32 trap = trap_None; | |
t_bool zero_fill = TRUE; | |
t_bool bare_sign = FALSE; | |
switch (mode) { /* set up the sign flag and indices */ | |
case Leading_Separate: | |
separate_index = count; /* the first character is the separate sign */ | |
overpunch_index = MAX_DIGITS + 1; /* and no character is the overpunched sign */ | |
bare_sign = (count == 1); /* only one character implies a bare sign */ | |
break; | |
case Trailing_Separate: | |
separate_index = 1; /* the last character is the separate sign */ | |
overpunch_index = MAX_DIGITS + 1; /* and no character is the overpunched sign */ | |
bare_sign = (count == 1); /* only one character implies a bare sign */ | |
break; | |
case Leading_Overpunch: | |
overpunch_index = count; /* the first character is the overpunched sign */ | |
separate_index = MAX_DIGITS + 1; /* and no character is the separate sign */ | |
break; | |
case Trailing_Overpunch: | |
overpunch_index = 1; /* the last character is the overpunched sign */ | |
separate_index = MAX_DIGITS + 1; /* and no character is the separate sign */ | |
break; | |
case Absolute: | |
default: /* quiet "potentially uninitialized local variable" warnings */ | |
separate_index = MAX_DIGITS + 1; /* no character is the overpunched sign */ | |
overpunch_index = MAX_DIGITS + 1; /* and no character is the separate sign */ | |
break; | |
} | |
mem_init_byte (&source, data_checked, &sba, count); /* set up byte accessors */ | |
mem_init_byte (&target, data_checked, &tba, count); /* for the source and target strings */ | |
sign = Unsigned; /* assume that the source is unsigned */ | |
byte = 0; /* shut up incorrect "may be used uninitialized" warning */ | |
while (count > 0) { /* while there are characters to convert */ | |
last_digit = byte; /* then save any previous character */ | |
byte = mem_read_byte (&source); /* and get the next source character */ | |
if (count == separate_index) { /* If this is the separate sign character */ | |
if (byte == '+' || byte == ' ') /* then a plus or blank */ | |
sign = Positive; /* indicates a positive number */ | |
else if (byte == '-') /* otherwise a minus */ | |
sign = Negative; /* indicates a negative number */ | |
else { /* otherwise */ | |
trap = trap_Invalid_ASCII_Digit; /* the character is not a valid sign */ | |
break; /* so abandon the conversion */ | |
} | |
if (bare_sign) /* if this is the only character */ | |
byte = '0'; /* then supply a zero for overpunching */ | |
else { /* otherwise */ | |
if (mode == Trailing_Separate) { /* if this is the trailing sign */ | |
byte = overpunch [sign] [last_digit - '0']; /* then overpunch the last numeric digit with the sign */ | |
mem_modify_byte (&target, byte); /* and update it */ | |
} | |
count = count - 1; /* count the separate sign character */ | |
continue; /* and continue with the next character */ | |
} | |
} | |
else if (count == overpunch_index && byte != ' ') { /* otherwise if this is the non-blank overpunched sign */ | |
trap = strip_overpunch (&byte, &sign); /* then strip the overpunch and set the sign */ | |
if (trap == trap_None) /* if the overpunch was valid */ | |
zero_fill = FALSE; /* then turn zero-filling off */ | |
else /* otherwise the overpunch was not valid */ | |
break; /* so abandon the conversion */ | |
} | |
if (byte >= '0' && byte <= '9') /* if the character is numeric */ | |
zero_fill = FALSE; /* then turn zero-filling off */ | |
else if (byte == ' ' && zero_fill) /* otherwise if it's a blank and zero-filling is on */ | |
byte = '0'; /* then fill */ | |
else { /* otherwise */ | |
trap = trap_Invalid_ASCII_Digit; /* the digit is invalid */ | |
break; /* so abandon the conversion */ | |
} | |
if (count == 1 && mode != Absolute) /* if this is the last character and the value is signed */ | |
byte = overpunch [sign] [byte - '0']; /* then overpunch with the sign */ | |
mem_write_byte (&target, byte); /* store it in the target string */ | |
count = count - 1; /* count the character */ | |
} /* and continue */ | |
mem_update_byte (&target); /* update the final target byte */ | |
if (DPRINTING (cpu_dev, DEB_MOPND)) | |
fprint_operands (&source, &target, trap); | |
return trap; /* return the trap condition */ | |
} | |
/* Edit a number into a formatted picture. | |
This routine moves an external decimal number to a target buffer under the | |
control of an editing subprogram. The subprogram consists of one or more | |
editing operations. The routine is interruptible between operations. | |
On entry, the TOS registers and the condition code in the STA register are | |
set as follows: | |
RA = 0 on initial entry, or 177777 on reentry after an interrupt | |
RB = the source byte address (DB-relative) | |
RC = the target byte address (DB-relative) | |
RD = the subprogram byte address (PB or DB-relative) | |
CC = the sign of the source number | |
On return, the "status" parameter is set to the SCP status returned by the | |
interrupt test, and "trap" is set to trap_Invalid_ASCII_Digit if an operation | |
encountered an invalid digit, or trap_None if the edit succeeded. The | |
routine returns TRUE if the operation ran to completion, or FALSE if the | |
operation was interrupted and should be resumed. | |
If an interrupt is detected between operations, two words are pushed onto the | |
stack before the interrupt handler is called. These words hold the current | |
significance trigger (a flag indicating whether a significant digit has been | |
seen or zero filling should occur), loop count, float character, and fill | |
character, in this format: | |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | TOS | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| S | 0 0 0 0 0 0 0 | loop count | TOS - 1 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| fill character | float character | TOS - 2 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
Where: | |
S = significance trigger (0/1) | |
The loop count/significance trigger word is written over the zero that | |
resides at the TOS on instruction entry, and then the fill/float character | |
word and a word of all ones are pushed. The source, target, and subprogram | |
byte addresses are updated during execution, so they retain their modified | |
values on reentry. When reentry is detected, the fill, float, and count | |
values are reestablished, and two words are popped off the stack to restore | |
the initial entry conditions. | |
If operand tracing is enabled, each subprogram operation is printed in this | |
format: | |
>>CPU opnd: 00.136024 000000 IS 6,"AAAAAA","BBBBBB" | |
~~ ~~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ | |
| | | | | |
| | | +-- subprogram operation and operands (if any) | |
| | +------------ octal loop counter value | |
| +-------------------- octal subprogram address (effective address) | |
+------------------------- octal bank (PBANK, DBANK, or SBANK) | |
...and source and target operands are printed in this format: | |
>>CPU opnd: 00.045177 000467 source 3,"ABC" | |
~~ ~~~~~~ ~~~~~~ ~~~~~~~~~~~~~~ | |
| | | | | |
| | | +-- byte operand label, length, and value | |
| | +------------ octal relative byte offset from base register | |
| +-------------------- octal address (effective address) | |
+------------------------- octal bank (PBANK, DBANK, or SBANK) | |
Implementation notes: | |
1. The Machine Instruction Set manual says that the DBNZ ("decrement loop | |
count and branch") operation adds the displacement operand. However, | |
the microcode manual shows that the displacement is actually subtracted. | |
The simulator follows the microcode implementation. | |
2. The BRIS ("branch if significance") operation adds the displacement to | |
the address of the displacement byte. After reading the displacement | |
byte, the address is incremented to the next location, so the operation | |
subtracts 1 before adding the displacement value. | |
*/ | |
static t_bool edit (t_stat *status, uint32 *trap) | |
{ | |
BYTE_ACCESS source, target, prog; | |
ACCESS_CLASS class; | |
HP_WORD bank; | |
char fill_char, float_char; | |
uint8 byte, opcode, operand, count; | |
uint32 loop_count; | |
t_bool filling = TRUE; /* TRUE if zero-filling is enabled */ | |
t_bool terminate = FALSE; /* TRUE if the operation loop is ending */ | |
*status = SCPE_OK; /* initialize the return status */ | |
*trap = trap_None; /* and trap condition */ | |
if (RA != 0) { /* if this is a reentry after an interrupt */ | |
filling = ((RB & D16_SIGN) != 0); /* then reset the zero-filling flag */ | |
loop_count = LOWER_BYTE (RB); /* and the loop counter */ | |
fill_char = UPPER_BYTE (RC); /* reset the fill */ | |
float_char = LOWER_BYTE (RC); /* and float characters */ | |
cpu_pop (); /* pop the extra words */ | |
cpu_pop (); /* added to save the context */ | |
} | |
else { /* otherwise this is an initial entry */ | |
filling = TRUE; /* so set the zero-filling flag */ | |
loop_count = 0; /* and clear the loop counter */ | |
fill_char = ' '; /* set the fill */ | |
float_char = '$'; /* and float character defaults */ | |
} | |
RA = D16_UMAX; /* set the in-process flag in case of microcode abort */ | |
STA &= ~STATUS_O; /* clear overflow status */ | |
if (CIR & CIS_DB_FLAG) { /* if the subprogram is in the data segment */ | |
class = data; /* then set up for data reads */ | |
bank = DBANK; /* and use DBANK for traces */ | |
} | |
else { /* otherwise it's in the program segment */ | |
class = program; /* so set up for program reads */ | |
bank = PBANK; /* and use PBANK for traces */ | |
} | |
mem_init_byte (&source, data, &RB, 0); /* set up byte accessors */ | |
mem_init_byte (&target, data, &RC, 0); /* for the source and target strings */ | |
mem_init_byte (&prog, class, &RD, 0); /* and the subprogram */ | |
do { /* process operations while "terminate" is FALSE */ | |
operand = mem_read_byte (&prog); /* get the next operation */ | |
if (DPRINTING (cpu_dev, DEB_MOPND)) { /* if operand tracing is enabled */ | |
hp_debug (&cpu_dev, DEB_MOPND, BOV_FORMAT " ", /* then print the current subprogram address */ | |
bank, prog.word_address, loop_count); /* and loop count as octal values */ | |
fprint_edit (sim_deb, NULL, 0, /* print the operation mnemonic */ | |
prog.initial_byte_address /* at the current physical byte address */ | |
+ prog.count - 1); | |
fputc ('\n', sim_deb); /* end the trace with a newline */ | |
} | |
opcode = UPPER_HALF (operand); /* split the opcode */ | |
operand = LOWER_HALF (operand); /* from the immediate operand */ | |
if (operand == 0 && opcode < 014) /* if this is an extended operand */ | |
operand = mem_read_byte (&prog); /* then read the full value from the next byte */ | |
switch (opcode) { /* dispatch on the opcode */ | |
case 000: /* MC - move characters */ | |
while (operand > 0) { /* while there are characters to move */ | |
byte = mem_read_byte (&source); /* get the next byte */ | |
mem_write_byte (&target, byte); /* move it to the target */ | |
operand = operand - 1; /* and count it */ | |
} | |
break; | |
case 001: /* MA - move alphabetics */ | |
while (operand > 0) { /* while there are characters to move */ | |
byte = mem_read_byte (&source); /* get the next byte */ | |
if (byte >= 'A' && byte <= 'Z' /* if the character is an uppercase letter */ | |
|| byte >= 'a' && byte <= 'z' /* or a lowercase letter */ | |
|| byte == ' ') { /* or a space */ | |
mem_write_byte (&target, byte); /* then move it to the target */ | |
operand = operand - 1; /* and count it */ | |
} | |
else { /* otherwise */ | |
*trap = trap_Invalid_ASCII_Digit; /* the character is not a valid alphabetic */ | |
terminate = TRUE; /* so abandon the subprogram */ | |
break; /* and the move */ | |
} | |
} | |
break; | |
case 002: /* MN - move numerics */ | |
while (operand > 0) { /* while there are characters to move */ | |
byte = mem_read_byte (&source); /* get the next byte */ | |
if (byte == ' ' && filling) /* if it's a blank and zero-filling is on */ | |
byte = '0'; /* then fill */ | |
else if (byte >= '0' && byte <= '9') /* otherwise if the character is a digit */ | |
filling = FALSE; /* then turn zero-filling off */ | |
else { /* otherwise */ | |
*trap = trap_Invalid_ASCII_Digit; /* the character is not a valid numeric */ | |
terminate = TRUE; /* so abandon the subprogram */ | |
break; /* and the move */ | |
} | |
mem_write_byte (&target, byte); /* move the character to the target */ | |
operand = operand - 1; /* and count it */ | |
} | |
break; | |
case 003: /* MNS - move numerics suppressed */ | |
while (operand > 0) { /* while there are characters to move */ | |
byte = mem_read_byte (&source); /* get the next byte */ | |
if (filling /* if zero-filling is on */ | |
&& (byte == ' ' || byte == '0')) /* and it's a blank or zero */ | |
byte = fill_char; /* then substitute the fill character */ | |
else if (byte >= '0' && byte <= '9') /* otherwise if the character is a digit */ | |
filling = FALSE; /* then turn zero-filling off */ | |
else { /* otherwise */ | |
*trap = trap_Invalid_ASCII_Digit; /* the character is not a valid numeric */ | |
terminate = TRUE; /* so abandon the subprogram */ | |
break; /* and the move */ | |
} | |
mem_write_byte (&target, byte); /* move the character to the target */ | |
operand = operand - 1; /* and count it */ | |
} | |
break; | |
case 004: /* MFL - move numerics with floating insertion */ | |
while (operand > 0) { /* while there are characters to move */ | |
byte = mem_read_byte (&source); /* get the next byte */ | |
if (filling /* if zero-filling is on */ | |
&& (byte == ' ' || byte == '0')) /* and it's a blank or zero */ | |
byte = fill_char; /* then substitute the fill character */ | |
else if (byte >= '0' && byte <= '9') { /* otherwise if the character is a digit */ | |
if (filling) { /* then if zero-filling is still on */ | |
filling = FALSE; /* then turn it off */ | |
mem_write_byte (&target, float_char); /* insert the float character before the digit */ | |
} | |
} | |
else { /* otherwise */ | |
*trap = trap_Invalid_ASCII_Digit; /* the character is not a valid numeric */ | |
terminate = TRUE; /* so abandon the subprogram */ | |
break; /* and the move */ | |
} | |
mem_write_byte (&target, byte); /* move the character to the target */ | |
operand = operand - 1; /* and count it */ | |
} | |
break; | |
case 005: /* IC - insert character */ | |
byte = mem_read_byte (&prog); /* get the insertion character */ | |
while (operand > 0) { /* while there are characters to insert */ | |
mem_write_byte (&target, byte); /* copy the character to the target */ | |
operand = operand - 1; /* and count it */ | |
} | |
break; | |
case 006: /* ICS - insert character suppressed */ | |
byte = mem_read_byte (&prog); /* get the insertion character */ | |
if (filling) /* if zero-filling is on */ | |
byte = fill_char; /* then substitute the fill character */ | |
while (operand > 0) { /* while there are characters to insert */ | |
mem_write_byte (&target, byte); /* copy the character to the target */ | |
operand = operand - 1; /* and count it */ | |
} | |
break; | |
case 007: /* ICI - insert characters immediate */ | |
while (operand > 0) { /* while there are characters to insert */ | |
byte = mem_read_byte (&prog); /* get the next byte */ | |
mem_write_byte (&target, byte); /* move it to the target */ | |
operand = operand - 1; /* and count it */ | |
} | |
break; | |
case 010: /* ICSI - insert characters suppressed immediate */ | |
while (operand > 0) { /* while there are characters to insert */ | |
byte = mem_read_byte (&prog); /* get the next byte */ | |
if (filling) /* if zero-filling is on */ | |
byte = fill_char; /* then substitute the fill character */ | |
mem_write_byte (&target, byte); /* copy the character to the target */ | |
operand = operand - 1; /* and count it */ | |
} | |
break; | |
case 011: /* BRIS - branch if significance */ | |
if (filling == FALSE) { /* if zero-filling is off */ | |
RD = RD - 1 + SEXT8 (operand) & R_MASK; /* then add the signed displacement to the offset */ | |
mem_set_byte (&prog); /* and reset the subprogram accessor */ | |
} | |
break; | |
case 012: /* SUFT - subtract from target */ | |
mem_update_byte (&target); /* update the final target byte if needed */ | |
RC = RC - SEXT8 (operand) & R_MASK; /* subtract the signed displacement from the offset */ | |
mem_set_byte (&target); /* and reset the target accessor */ | |
break; | |
case 013: /* SUFS - subtract from source */ | |
RB = RB - SEXT8 (operand) & R_MASK; /* subtract the signed displacement from the offset */ | |
mem_set_byte (&source); /* and reset the source accessor */ | |
break; | |
case 014: /* ICP - insert character punctuation */ | |
mem_write_byte (&target, operand + ' '); /* write the punctuation character to the target */ | |
break; | |
case 015: /* ICPS - insert character punctuation suppressed */ | |
if (filling) /* if zero-filling is on */ | |
byte = fill_char; /* then substitute the fill character */ | |
else /* otherwise */ | |
byte = operand + ' '; /* use the supplied punctuation character */ | |
mem_write_byte (&target, byte); /* write the character to the target */ | |
break; | |
case 016: /* IS - insert characters on sign */ | |
if (operand > 0) { /* if the character strings are present */ | |
count = operand; /* then get the character count */ | |
if ((STA & STATUS_CC_MASK) == STATUS_CCL) { /* if the sign is negative */ | |
RD = RD + count & R_MASK; /* then index to the negative character string */ | |
mem_set_byte (&prog); /* and reset the subprogram accessor */ | |
} | |
while (count > 0) { /* while there are characters to copy */ | |
byte = mem_read_byte (&prog); /* get the next byte */ | |
mem_write_byte (&target, byte); /* copy the character to the target */ | |
count = count - 1; /* and count it */ | |
} | |
if ((STA & STATUS_CC_MASK) != STATUS_CCL) { /* if the sign is positive */ | |
RD = RD + operand & R_MASK; /* then skip over the negative character string */ | |
mem_set_byte (&prog); /* and reset the subprogram accessor */ | |
} | |
} | |
break; | |
case 017: /* two-byte operations */ | |
switch (operand) { /* dispatch on the second operation byte */ | |
case 000: /* TE - terminate edit */ | |
terminate = TRUE; /* terminate the subprogram */ | |
break; | |
case 001: /* ENDF - end floating point insertion */ | |
if (filling) /* if zero-filling is on */ | |
mem_write_byte (&target, float_char); /* then insert the float character */ | |
break; | |
case 002: /* SST1 - set significance to 1 */ | |
filling = FALSE; /* set zero-filling off */ | |
break; | |
case 003: /* SST0 - set significance to 0 */ | |
filling = TRUE; /* set zero-filling on */ | |
break; | |
case 004: /* MDWO - move digit with overpunch */ | |
byte = mem_read_byte (&source); /* get the digit */ | |
if (byte == ' ' && filling) /* if it's a blank and zero-filling is on */ | |
byte = '0'; /* then fill */ | |
else if (byte < '0' || byte > '9') { /* otherwise if the character is not a digit */ | |
*trap = trap_Invalid_ASCII_Digit; /* then the it is not a valid number */ | |
terminate = TRUE; /* so abandon the subprogram */ | |
break; | |
} | |
if ((STA & STATUS_CC_MASK) == STATUS_CCL) /* if the number is negative */ | |
byte = overpunch [Negative] [byte - '0']; /* then overpunch a minus sign */ | |
else /* otherwise */ | |
byte = overpunch [Positive] [byte - '0']; /* overpunch a plus sign */ | |
mem_write_byte (&target, byte); /* write the overpunched character to the target */ | |
break; | |
case 005: /* SFC - set fill character */ | |
fill_char = mem_read_byte (&prog); /* set the fill character from the next byte */ | |
break; | |
case 006: /* SFLC - set float character on sign */ | |
byte = mem_read_byte (&prog); /* get the float characters */ | |
if ((STA & STATUS_CC_MASK) == STATUS_CCL) /* if the number is negative */ | |
float_char = LOWER_HALF (byte) + ' '; /* then use the negative float character */ | |
else /* otherwise */ | |
float_char = UPPER_HALF (byte) + ' '; /* use the positive float character */ | |
break; | |
case 007: /* DFLC - define float character on sign */ | |
float_char = mem_read_byte (&prog); /* set the positive float character */ | |
if ((STA & STATUS_CC_MASK) == STATUS_CCL) /* if the number is negative */ | |
float_char = mem_read_byte (&prog); /* then set the negative float character */ | |
else /* otherwise */ | |
mem_read_byte (&prog); /* skip over it */ | |
break; | |
case 010: /* SETC - set loop count */ | |
loop_count = mem_read_byte (&prog); /* get the new loop count */ | |
break; | |
case 011: /* DBNZ - decrement loop count and branch */ | |
byte = mem_read_byte (&prog); /* get the displacement */ | |
loop_count = loop_count - 1 & D8_MASK; /* decrement the loop count modulo 256 */ | |
if (loop_count > 0) { /* if the count is not zero */ | |
RD = RD - 1 - SEXT8 (byte) & R_MASK; /* then subtract the signed displacement from the offset */ | |
mem_set_byte (&prog); /* and reset the subprogram accessor */ | |
} | |
break; | |
default: /* invalid two-word opcodes */ | |
break; /* are ignored */ | |
} /* end of two-word dispatcher */ | |
break; | |
} /* all cases are handled */ | |
if (terminate == FALSE /* if the subprogram is continuing */ | |
&& cpu_interrupt_pending (status)) { /* and an interrupt is pending */ | |
cpu_push (); /* then push the stack down twice */ | |
cpu_push (); /* to save the subprogram execution state */ | |
RA = D16_UMAX; /* set the resumption flag */ | |
RB = TO_WORD ((filling ? D16_SIGN : 0), loop_count); /* save the significance trigger and loop count */ | |
RC = TO_WORD (fill_char, float_char); /* save the fill and float characters */ | |
mem_update_byte (&target); /* update the last word written */ | |
return FALSE; /* and return with an interrupt set up or a status error */ | |
} | |
} | |
while (terminate == FALSE); /* continue subprogram execution until terminated */ | |
mem_update_byte (&target); /* update the final target byte */ | |
if (DPRINTING (cpu_dev, DEB_MOPND)) { /* if operand tracing is enabled */ | |
mem_set_byte (&source); /* then reset the source and target accessors */ | |
mem_set_byte (&target); /* to finalize the operand extents */ | |
if (source.length > 0) /* if the source operand was used */ | |
fprint_operands (&source, &target, *trap); /* then print both source and target operands */ | |
else /* otherwise */ | |
fprint_operand (&target, "target", &fmt_byte_operand); /* print just the target operand */ | |
} | |
RA = 0; /* clear the resumption flag */ | |
return TRUE; /* return with completion status */ | |
} | |
/* Compare two padded byte strings. | |
This routine compares two byte strings with optional translation and sets the | |
condition code in the status register to indicate the result. Starting with | |
the first, successive pairs of bytes are compared; if the string lengths are | |
unequal, the shorter string is padded with blanks. The comparison stops when | |
the bytes are unequal or the end of the strings is reached. The condition | |
code is CCG if the source byte is greater than the target byte, CCL if the | |
source byte is less than the target byte, or CCE if the strings are equal or | |
both strings are of zero-length. | |
On entry, the RA and RC TOS registers contain the lengths of the source and | |
target strings, and the "source" and "target" parameters point at the byte | |
accessors for the source and target strings, respectively. The "table" | |
parameter either points at a 256-byte translation table or is NULL if no | |
translation is desired. If supplied, the table is used to translate the | |
source bytes and the target bytes if the target string is DB-relative. If | |
the target is PB-relative, no target translation is performed. | |
The routine is interruptible between bytes. If an interrupt is pending, the | |
routine will return FALSE with the TOS registers updated to reflect the | |
partial comparison; reentering the routine will complete the operation. If | |
the comparison runs to completion, the condition code will be set, and the | |
routine will return TRUE. In either case, the "status" parameter is set to | |
the SCP status returned by the interrupt test. | |
This routine implements the CMPS and CMPT instructions. | |
*/ | |
static t_bool compare (BYTE_ACCESS *source, BYTE_ACCESS *target, BYTE_ACCESS *table, t_stat *status) | |
{ | |
uint8 source_byte, target_byte; | |
*status = SCPE_OK; /* initialize the return status */ | |
while (RA > 0 || RC > 0) { /* while there are bytes to compare */ | |
if (RA == 0) /* if the source string is exhausted */ | |
source_byte = ' '; /* then use a blank */ | |
else /* otherwise */ | |
source_byte = mem_read_byte (source); /* get the next source byte */ | |
if (RC == 0) /* if the target string is exhausted */ | |
target_byte = ' '; /* then use a blank */ | |
else /* otherwise */ | |
target_byte = mem_read_byte (target); /* get the next target byte */ | |
if (table != NULL) { /* if the translation table was supplied */ | |
source_byte = mem_lookup_byte (table, source_byte); /* then translate the source byte */ | |
if (target->class == data || RC == 0) /* if the target is in the data segment or is exhausted */ | |
target_byte = mem_lookup_byte (table, target_byte); /* then translate the target byte */ | |
} | |
if (source_byte != target_byte) /* if the bytes do not compare */ | |
break; /* then terminate the loop */ | |
if (RA > 0) /* if source bytes remain */ | |
RA = RA - 1; /* then count the byte (cannot underflow) */ | |
if (RC > 0) /* if target bytes remain */ | |
RC = RC - 1; /* then count the byte (cannot underflow) */ | |
if (cpu_interrupt_pending (status)) /* if an interrupt is pending */ | |
return FALSE; /* then return with an interrupt set up or a status error */ | |
} | |
if (RA == 0 && RC == 0) /* if the counts expired together */ | |
SET_CCE; /* then the strings are equal */ | |
else if (source_byte > target_byte) /* otherwise if the source byte > the target byte */ | |
SET_CCG; /* set the source string is greater */ | |
else /* otherwise the source byte < the target byte */ | |
SET_CCL; /* so the source string is less */ | |
return TRUE; /* return comparison completion status */ | |
} | |
/* Format and print byte string operands. | |
This routine formats and prints source and target byte string operands. The | |
source operand is always printed. The target operand is printed only if the | |
supplied trap condition is "trap_None"; otherwise, it is omitted. Tracing | |
must be enabled when the routine is called. | |
*/ | |
static void fprint_operands (BYTE_ACCESS *source, BYTE_ACCESS *target, uint32 trap) | |
{ | |
fprint_operand (source, "source", &fmt_byte_operand); | |
if (trap == trap_None) | |
fprint_operand (target, "target", &fmt_byte_operand); | |
return; | |
} | |
/* Format, translate, and print byte string operands. | |
This routine formats, optionally translates, and prints source and target | |
byte string operands. The source operand is always translated. The target | |
operand is translated only if it resides in the data segment. Tracing must | |
be enabled when the routine is called; the trace format is identical to that | |
produced by the fprint_operand routine. | |
*/ | |
static void fprint_translated_operands (BYTE_ACCESS *source, BYTE_ACCESS *target, BYTE_ACCESS *table) | |
{ | |
hp_debug (&cpu_dev, DEB_MOPND, BOV_FORMAT " source %d,\"%s\"\n", | |
TO_BANK (source->first_byte_address / 2), | |
TO_OFFSET (source->first_byte_address / 2), | |
source->first_byte_offset, source->length, | |
fmt_translated_byte_operand (source->first_byte_address, | |
source->length, | |
table->first_byte_address)); | |
if (target->class == program) | |
fprint_operand (target, "target", &fmt_byte_operand); | |
else | |
hp_debug (&cpu_dev, DEB_MOPND, BOV_FORMAT " target %d,\"%s\"\n", | |
TO_BANK (target->first_byte_address / 2), | |
TO_OFFSET (target->first_byte_address / 2), | |
target->first_byte_offset, target->length, | |
fmt_translated_byte_operand (target->first_byte_address, | |
target->length, | |
table->first_byte_address)); | |
return; | |
} | |
/* Format and print a memory operand. | |
The byte operand described by the byte accessor is sent to the debug trace | |
log file. Tracing must be enabled when the routine is called. | |
On entry, "op" points at the byte accessor describing the operand, "label" | |
points to text used to label the operand, and "operand_printer" points to the | |
routine used to print the operand. The latter may be "fprint_byte_operand" | |
to print operands consisting of 8-bit characters, or "fprint_bcd_operand" to | |
print extended-decimal (BCD) operands as character strings; the operand | |
length field of the trace line will indicate the number of characters or BCD | |
digits, respectively. | |
The operand is printed in this format: | |
>>CPU opnd: 00.045177 000467 source 15,"NOW IS THE TIME" | |
~~ ~~~~~~ ~~~~~~ ~~~~~~ ~~ ~~~~~~~~~~~~~~~~~ | |
| | | | | | | |
| | | | | +-- operand value | |
| | | | +------------- operand length | |
| | | +------------------ operand label | |
| | +---------------------------- octal relative byte offset from base register | |
| +------------------------------------ octal operand address (effective address) | |
+----------------------------------------- octal operand bank (PBANK, DBANK, or SBANK) | |
*/ | |
static void fprint_operand (BYTE_ACCESS *op, char *label, OP_PRINT operand_printer) | |
{ | |
hp_debug (&cpu_dev, DEB_MOPND, BOV_FORMAT " %s %d,\"%s\"\n", | |
TO_BANK (op->first_byte_address / 2), | |
TO_OFFSET (op->first_byte_address / 2), | |
op->first_byte_offset, label, op->length, | |
operand_printer (op->first_byte_address, op->length)); | |
return; | |
} |