1977 lines
102 KiB
C
1977 lines
102 KiB
C
/* hp3000_cpu_cis.c: HP 32234A COBOL II Instruction Set simulator
|
|
|
|
Copyright (c) 2016-2017, 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.
|
|
|
|
22-Apr-17 JDB Corrected the significance flag setting in "edit"
|
|
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 that a significant digit has been
|
|
seen or that 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.
|
|
|
|
3. The significance trigger is represented by the "filling" flag; its value
|
|
is the opposite of the trigger, i.e., FALSE if a significant digit has
|
|
been seen, TRUE if all leading digits have been zeros, because that more
|
|
clearly indicates that it controls leading zero suppression and
|
|
replacement.
|
|
*/
|
|
|
|
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 = (filling ? 0 : D16_SIGN) | 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;
|
|
}
|