/* hp3000_cpu_cis.c: HP 32234A COBOL II Instruction Set simulator

   Copyright (c) 2016-2020, 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-Oct-20    JDB     Improved the comments describing the instructions
   20-Oct-20    JDB     CVND now queues down until 3 TOS registers are left
   09-Oct-20    JDB     Renamed trap_Word_Count_Overflow to trap_Invalid_Decimal_Length
   26-Aug-20    JDB     Corrected line ends for trace to stdout
   09-Dec-19    JDB     Replaced debugging macros with tracing macros
   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:

     Name  Description
     ----  ---------------------------
     ABSD  Absolute decimal
     ABSN  Absolute numeric
     ALGN  Align numeric
     CMPS  Compare strings
     CMPT  Compare translated strings
     CVND  Convert numeric display
     EDIT  Edited move
     ENDP  End of paragraph
     LDDW  Load double-word
     LDW   Load word
     NEGD  Negate decimal
     PARC  Paragraph procedure call
     TCCS  Test condition code and set
     TR    Translate
     XBR   External branch

   The instructions occupy the the firmware extension range 020460-020477 and
   are encoded as follows:

       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
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       0 = delete 3 words
       1 = delete 4 words

   Transfer the external decimal number designated by RA (digit and fraction
   count) and RB (address) to an external decimal number designated by RC
   (digit and fraction count) and RD (address) while aligning the source number
   with the target decimal point.


     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 0   0   1   1 | 0   0   1 | S |  ABSN
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       0 = delete 1 word
       1 = delete 2 words

   Convert in place the external decimal number RA (count) and RB (address) to
   an unsigned value.


     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 0   0   1   1 | 1   0   0 | B |  EDIT
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     Buffer Location:

       0 = edit program address is relative to PB
       1 = edit program address is relative to DB

   Move a string of characters from the source buffer address contained in RB to
   the target buffer address contained in RC under the control of the edit
   program addressed by RD.  RA contains a zero word used to resume after an
   interrupt.


     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 0   0   1   1 | 1   0   1 | B |  CMPS
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     Buffer Location:

       0 = second string address is relative to PB
       1 = second string address is relative to DB

   Compare the string of characters designated by RA (count) and RB (address) to
   the string of characters designated by RC (count) and RD (address) and set
   the condition code appropriately.


     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 0   0   1   1 | 1   1   0   0 |  XBR
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Jump to the location indicated by the segment number contained in RA and the
   PB-relative address contained in RB.


     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 0   0   1   1 | 1   1   0   1 |  PARC
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Call the location indicated by the paragraph number contained in RA, the
   segment number contained in RB, and the PB-relative address contained in RC.


     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 0   0   1   1 | 1   1   1   0 |  ENDP
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Return to the location indicated by the segment number contained in RC and
   the PB-relative address contained in RD if the paragraph numbers contained in
   RA and RB are identical.


     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 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 |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     Buffer Location:

       0 = second string address is relative to PB
       1 = second string address is relative to DB

   Compare the string of characters designated by RA (count) and RB (address) to
   the string of characters designated by RC (count) and RD (address) using the
   translation table addressed by SM and set the condition code appropriately.


     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 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 | > | = | < |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Test the condition code in the status register against the set of conditions
   specified in bits 13-15 and load -1 to the TOS if the condition matches and 0
   if it does not.


     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 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 |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     Sign Conversion:

       000 = source sign is leading separate
       001 = source sign is trailing separate
       010 = source sign is leading overpunch
       011 = source sign is trailing overpunch
       1xx = source is unsigned

     S-Decrement:

       0 = delete 2 words
       1 = delete 3 words

   Convert the display decimal number designated by RA (count) and RB (address)
   to the external decimal number designated by RC (address).


     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 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 |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       0 = delete no words
       1 = delete 1 word

   Load two bytes from the byte address contained in RA to the TOS.


     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 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 |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       0 = delete delete no words
       1 = delete 1 word

   Load four bytes from the byte address contained in RA to the TOS.


     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 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 |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     Buffer Location:

       0 = translation table address is relative to PB
       1 = translation table address is relative to DB

   Convert the string of characters designated by RA (count) and RC (address) to
   the target string designated by RB (address) using the translation table
   addressed by RD.


     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 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 |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       0 = delete 1 word
       1 = delete 2 words

   Convert in place the packed decimal number designated by RA (count) and RB
   (address) to an unsigned value.


     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 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 |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       0 = delete 1 word
       1 = delete 2 words

   Negate in place the packed decimal number designated by RA (count) and RB
   (address) to an unsigned value.



   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
   the starting digits into 16-bit words:

       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15    addr/size
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |    unused     |     digit     |      ...      |      ...      |  even/even
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |     digit     |     digit     |      ...      |      ...      |  even/odd
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |      ...      |      ...      |    unused     |     digit     |  odd/even
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |              ...              |     digit     |     digit     |  odd/odd
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Numbers always end with the sign in the lower half of the byte, so there are
   two possible cases of packing the ending digits into 16-bit words, depending
   on the total number of digits:

       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |     digit     |     sign      |              ...              |
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     |      ...      |      ...      |     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:

     Parameter  Description
     ---------  ------------------------------------------------
      000014    Invalid ASCII Digit
      000017    Invalid Decimal Length

   The Invalid Decimal Length 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
       describe 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 Invalid Decimal Length 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_Invalid_Decimal_Length;         /*         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 (TRACING (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:
        while (SR > 2)                                  /* if more than two TOS registers are valid */
            cpu_queue_down ();                          /*   then queue them down until exactly two are left */

        if (RA > MAX_DIGITS)                            /* if the digit count is too large */
            trap = trap_Invalid_Decimal_Length;         /*   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 (TRACING (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 (TRACING (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 (TRACING (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 (TRACING (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:
                while (SR > 3)                          /* if more than three TOS registers are valid */
                    cpu_queue_down ();                  /*   then queue them down until exactly three are left */

                if (RA > MAX_DIGITS)                    /* if the digit count is too large */
                    trap = trap_Invalid_Decimal_Length; /*   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:
                while (SR > 1)                          /* if more than one TOS register is valid */
                    cpu_queue_down ();                  /*   then queue them down until exactly one is left */

                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 (TRACING (cpu_dev, DEB_MOPND))
                    fprint_operand (&source, "source", &fmt_byte_operand);
                break;


            case 042:                                   /* LDDW (none; STOV, STUN, BNDV) */
            case 043:
                while (SR > 1)                          /* if more than one TOS register is valid */
                    cpu_queue_down ();                  /*   then queue them down until exactly one is left */

                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 (TRACING (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 (TRACING (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:
                while (SR > 2)                          /* if more than two TOS registers are valid */
                    cpu_queue_down ();                  /*   then queue them down until exactly two are left */

                if (RA > MAX_DIGITS)                    /* if the digit count is too large */
                    trap = trap_Invalid_Decimal_Length; /*   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 (TRACING (cpu_dev, DEB_MOPND))
                        fprint_operand (&source, "source", &fmt_bcd_operand);

                    byte = mem_read_byte (&target);     /* get the sign byte and check the bounds */

                    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 and check the bounds */
                    mem_post_byte (&target);            /*   and post it */

                    if (TRACING (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 (TRACING (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 (TRACING (cpu_dev, DEB_MOPND)) {                 /* if operand tracing is enabled */
        hp_trace (&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);

        if (sim_deb == stdout)                          /* if debug output is to the (raw) console */
            fputc ('\r', sim_deb);                      /*   then insert a carriage return */

        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 (TRACING (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_trace (&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_trace (&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_trace (&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;
}
