/* hp3000_cpu_eis.c: HP 30012A Extended Instruction Set simulator

   Copyright (c) 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.

   29-Sep-20    JDB     Passes the EIS decimal arithmetic firmware diagnostic (D431)
   14-Sep-20    JDB     Passes the EIS floating point firmware diagnostic (D431)
   05-Sep-20    JDB     Created

   References:
     - HP 3000 Series II/III System Reference Manual
         (30000-90020, July 1978)
     - Machine Instruction Set Reference Manual
         (30000-90022, June 1984)
     - HP 3000 Series II System Microprogram Listing
         (30000-90023, August 1976)


   This module implements the HP 30012A Extended Instruction Set firmware
   consisting of extended floating point and decimal arithmetic instructions.
   The set contains these instructions:

     Name  Description
     ----  ------------------------------
     EADD  Extended precision add
     ESUB  Extended precision subtract
     EMPY  Extended precision multiply
     EDIV  Extended precision divide
     ENEG  Extended precision negate
     ECMP  Extended precision compare

     ADDD  Add decimal
     CMPD  Compare decimal
     CVAD  Convert ASCII to decimal
     CVBD  Convert binary to decimal
     CVDA  Convert decimal to ASCII
     CVDB  Convert decimal to binary
     DMPY  Double logical multiply
     MPYD  Multiply decimal
     NSLD  Normalizing shift left decimal
     SLD   Shift left decimal
     SRD   Shift right decimal
     SUBD  Subtract decimal

   The floating-point instructions occupy the the firmware extension range
   020400-020417.  For each instruction, addresses of the operand(s) and result
   as DB+ relative word offsets reside on the stack.  They 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   0   0 | 1   0   0   0 |  EADD
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Add the four-word floating-point number addressed by RA to the four-word
   floating-point number addressed by RB and store the result in the four-word
   target area addressed by RC.


       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   0   0 | 1   0   0   1 |  ESUB
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Subtract the four-word floating-point number addressed by RA from the
   four-word floating-point number addressed by RB and store the result in the
   four-word target area addressed by RC.


       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   0   0 | 1   0   1   0 |  EMPY
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Multiply the four-word floating-point number addressed by RA to the four-word
   floating-point number addressed by RB and store the result in the four-word
   target area addressed by RC.


       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   0   0 | 1   0   1   1 |  EDIV
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Divide the four-word floating-point number addressed by RA into the four-word
   floating-point number addressed by RB and store the result in the four-word
   target area addressed by RC.


       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   0   0 | 1   1   0   0 |  ENEG
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Negate in place the four-word floating-point number addressed by RA.


       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   0   0 | 1   1   0   1 |  ECMP
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Compare the four-word floating-point number addressed by RB to the four-word
   floating-point number addressed by RA and set the condition code
   appropriately.



   The decimal arithmetic instructions occupy the the firmware extension range
   020600-020777.  For most instructions, addresses of the source and target
   operands as DB+ relative byte (for packed decimal) or word (for binary)
   offsets reside on the stack.  They 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 | 1   0   0   0 | 0   0   0   1 |  DMPY
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Multiply the double-word unsigned integer contained in RB and RA to the
   double-word unsigned integer contained in RD and RC and leaves the four-word
   unsigned integer product on the stack.


       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 1   0   0 | S | 0   0   1   0 |  CVAD
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       0 = delete 2 words
       1 = delete 4 words

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


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

     S-Decrement:

       0 = delete 1 word
       1 = delete 3 words

     Sign Control:

       00 = target sign is source sign
       01 = target sign is negative if source negative else unsigned
       10 = target sign is unsigned
       11 = target sign is unsigned

   Convert the packed decimal number designated by RA (address) to an external
   decimal number designated by RB (count) and RC (address).  The number of
   digits converted is also designated by RB.


       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 1   0   0 | S | 0   1   0   0 |  CVBD
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       0 = delete 2 words
       1 = delete 4 words

   Convert the binary number designated by RA (count) and RB (address) to a
   packed decimal number designated by RC (count) and RD (address).


       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 1   0   0 | S | 0   1   0   1 |  CVDB
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       0 = delete 2 words
       1 = delete 3 words

   Convert the packed decimal number designated by RA (count) and RB (address)
   to a binary number designated by RC (address).  The number of target words is
   determined by the source digit count.


       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 1   0 | sdec  | 0   1   1   0 |  SLD
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       00 = delete no words
       01 = delete 2 words
       10 = delete 4 words

   Shift the packed decimal number designated by RA (count) and RB (address)
   left by the number of digits specified by the X register and store the result
   in a packed decimal number designated by RC (count) and RD (address).  Digits
   shifted off the end of the number are lost.


       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 1   0 | sdec  | 0   1   1   1 |  NSLD
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       00 = delete no words
       01 = delete 2 words
       10 = delete 4 words

   Shift the packed decimal number designated by RA (count) and RB (address)
   left by the number of digits specified by the X register and store the result
   in a packed decimal number designated by RC (count) and RD (address).  If
   shifting would lose significant digits off the end of the number, the shift
   count is reduced to leave the most-significant digit at the start of the
   packed number.


       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 1   0 | sdec  | 1   0   0   0 |  SRD
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       00 = delete no words
       01 = delete 2 words
       10 = delete 4 words

   Shift the packed decimal number designated by RA (count) and RB (address)
   right by the number of digits specified by the X register and store the
   result in a packed decimal number designated by RC (count) and RD (address).
   Digits shifted off the end of the number are lost.


       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 1   0 | sdec  | 1   0   0   1 |  ADDD
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       00 = delete no words
       01 = delete 2 words
       10 = delete 4 words

   Add the packed decimal number designated by RA (count) and RB (address) to
   the packed decimal number designated by RC (count) and RD (address) and store
   the result in the target area addressed by RD.


       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 1   0 | sdec  | 1   0   1   0 |  CMPD
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       00 = delete no words
       01 = delete 2 words
       10 = delete 4 words

   Compare the packed decimal number designated by RA (count) and RB (address)
   to the packed decimal number designated by RC (count) and RD (address) and
   set the condition code appropriately.


       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 1   0 | sdec  | 1   0   1   1 |  SUBD
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       00 = delete no words
       01 = delete 2 words
       10 = delete 4 words

   Subtract the packed decimal number designated by RA (count) and RB (address)
   from the packed decimal number designated by RC (count) and RD (address) and
   store the result in the target area addressed by RD.


       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 1   0 | sdec  | 1   1   0   0 |  MPYD
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     S-Decrement:

       00 = delete no words
       01 = delete 2 words
       10 = delete 4 words

   Multiply the packed decimal number designated by RA (count) and RB (address)
   by the packed decimal number designated by RC (count) and RD (address) and
   store the result in the target area addressed by RD.


   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.


   Eight user traps may be taken by these instructions if the T bit is on in the
   status register:

     Parameter  Description
     ---------  ------------------------------------------------
      000010    Extended Precision Floating Point Overflow
      000011    Extended Precision Floating Point Underflow
      000012    Extended Precision Floating Point Divide by Zero
      000013    Decimal Overflow
      000014    Invalid ASCII Digit
      000015    Invalid Decimal Digit
      000016    Invalid Source Word Count
      000017    Invalid Decimal Length


   Implementation notes:

    1. Each instruction executor begins with a comment listing the instruction
       mnemonic and, following in parentheses, the condition code setting, or
       "none" if the condition code is not altered, and a list of any traps that
       might be generated.  The condition code and trap mnemonics are those used
       in the Machine Instruction Set manual.
*/



#include "hp3000_defs.h"
#include "hp3000_cpu.h"
#include "hp3000_cpu_fp.h"
#include "hp3000_mem.h"



/* Program constants */

#define MAX_DIGITS          28                  /* maximum number of decimal digits accepted */
#define MAX_WORDS           6                   /* maximum number of words needed for conversion */

#define MAX_COUNT_MASK      000037u             /* maximum shift count mask */

#define NOT_SET             MAX_DIGITS          /* indicator that an index is not set */


/* Packed decimal constants */

#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 */


/* External decimal constants */

typedef enum {                                  /* shift mode, corresponds to EIS subopcode */
    Left        = 006,                          /*   SLD  (020606) */
    Normalizing = 007,                          /*   NSLD (020607) */
    Right       = 010                           /*   SRD  (020610) */
    } SHIFT_MODE;

typedef enum {                                  /* numeric sign values */
    Negative,
    Unsigned,
    Positive
    } NUMERIC_SIGN;

static HP_BYTE sign_digit [3] =                 /* sign digit, indexed by NUMERIC_SIGN */
    { SIGN_MINUS,                               /*   Negative */
      SIGN_UNSIGNED,                            /*   Unsigned */
      SIGN_PLUS };                              /*   Positive */

static HP_BYTE overpunch [3] [10] = {                       /* sign overpunches, indexed by NUMERIC_SIGN and value */
    { '}', '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 */
    };


/* Digit accessors.

   Decimal numbers are stored in memory as byte-addressable arrays.  Two number
   formats are supported.  Packed decimal numbers contain binary-coded-decimal
   (BCD) digits stored two per byte.  External decimal numbers contain ASCII
   digits with an optional overpunched sign in the last digit position; they are
   stored one per byte.

   Digit accessors extend the byte accessor structure to contain additional
   information useful in manipulating decimal numbers.  A digit accessor is
   initialized in the same manner as a byte accessor, with an additional
   parameter to specify the desired numeric format.  Routines are provided to
   read and write decimal numbers via accessors.  Unlike byte accessors, digit
   accessors contain a buffer large enough to hold the maximum number of digits
   allowed in a decimal number.  Every decimal number is right-justified in the
   buffer with leading zeros as necessary.  The accessor maintains a count of
   the actual number of digits specified, so that reading and writing of shorter
   numbers is handled transparently.
*/

typedef enum {                                  /* decimal number format */
    Packed,                                     /*   packed decimal */
    External                                    /*   external decimal */
    } DECIMAL_FORMAT;

typedef struct {                                /* decimal number accessor */
    BYTE_ACCESS     bac;                        /*   the underlying byte accessor */
    DECIMAL_FORMAT  format;                     /*   the format of the decimal number */
    HP_WORD         byte_offset;                /*   the byte offset for the byte accessor routines */
    uint32          starting_index;             /*   the index of the first digit in the number */
    uint32          significant_index;          /*   the index of the first significant digit in the number */
    uint32          digit_count;                /*   the count of digits in the number */
    NUMERIC_SIGN    sign;                       /*   the sign of the number */
    HP_BYTE         digits [MAX_DIGITS];        /*   the digits of the number */
    } DIGIT_ACCESS;


/* EIS local utility routine declarations */

static void   init_decimal  (DIGIT_ACCESS *dap, DECIMAL_FORMAT format, ACCESS_CLASS class,
                               HP_WORD byte_offset, HP_WORD digit_count);
static uint32 read_decimal  (DIGIT_ACCESS *dap);
static void   write_decimal (DIGIT_ACCESS *dap, t_bool merge_digits);

static HP_WORD compare_decimal  (DIGIT_ACCESS *first,        DIGIT_ACCESS *second);
static uint32  add_decimal      (DIGIT_ACCESS *augend,       DIGIT_ACCESS *addend);
static uint32  subtract_decimal (DIGIT_ACCESS *minuend,      DIGIT_ACCESS *subtrahend);
static uint32  multiply_decimal (DIGIT_ACCESS *multiplicand, DIGIT_ACCESS *multiplier);
static uint32  shift_decimal    (DIGIT_ACCESS *target,       DIGIT_ACCESS *source, SHIFT_MODE shift);
static uint32  convert_decimal  (DIGIT_ACCESS *target,       DIGIT_ACCESS *source);

static uint32  convert_binary   (DIGIT_ACCESS *decimal, HP_WORD address, HP_WORD count);

static t_bool read_operands   (DIGIT_ACCESS   *first, DIGIT_ACCESS *second, uint32 *trap);
static void   write_operand   (DIGIT_ACCESS   *operand);
static void   set_cca_decimal (DIGIT_ACCESS   *dap);
static void   decrement_stack (uint32 trap,   uint32 count_0, uint32 count_1, uint32 count_2);
static uint32 strip_overpunch (HP_BYTE *byte, NUMERIC_SIGN *sign);

static void fprint_decimal_operand (DIGIT_ACCESS *op, char *label);



/* EIS global routines */


/* Execute an EIS floating point operation.

   This routine is called to execute the floating point 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   0   0 | 1 | EIS FP op |  EIS FP
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     EIS FP Opcode:

       0 = EADD (Extended precision add)
       1 = ESUB (Extended precision subtract)
       2 = EMPY (Extended precision multiply)
       3 = EDIV (Extended precision divide)
       4 = ENEG (Extended precision negate)
       5 = ECMP (Extended precision compare)
       6 = (undefined)
       7 = (undefined)

   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.


   Implementation notes:

    1. Each instruction lists a potential stack underflow trap.  The underflow
       is actually detected in the firmware dispatcher, which has a stack
       preadjust of 4, before this routine is called.  The "cpu_pop" calls
       below always succeed, as the stack registers are fully populated on
       entry.

    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. The instruction executors follow the microcode in the placement of bounds
       checks.

    4. The ECMP instruction checks operand addresses against SM rather than SM +
       SR.  Because SR = 4 on entry, this effectively checks the entire
       four-word operand (if the first word is below SM, then the fourth word is
       below RB) before retrieving the individual operand words as needed for
       the comparison.  Therefore, no queue-downs are needed.  Note that a
       bounds violation can occur even if the first words differ and no
       additional words are read.  The diagnostic tests for this.
*/

t_stat cpu_eis_fp_op (void)
{
t_bool  negative;
HP_WORD operand_x, operand_y, address_x, address_y;
FP_OPND operand_u, operand_v, operand_w;
uint32  opcode, index;
t_stat  status = SCPE_OK;

opcode = FMEXSUBOP (CIR);                               /* get the opcode from the instruction */

switch (opcode) {                                       /* dispatch the opcode */

    case 010:                                           /* EADD (CCA, O; STUN, STOV, ARITH) */
    case 011:                                           /* ESUB (CCA, O; STUN, STOV, ARITH) */
    case 012:                                           /* EMPY (CCA, O; STUN, STOV, ARITH) */
    case 013:                                           /* EDIV (CCA, O; STUN, STOV, ARITH) */
        while (SR > 3)                                  /* if more than three TOS register are valid */
            cpu_queue_down ();                          /*   then queue them down until exactly three are left */

        operand_u.precision = fp_e;                     /* set the operand precision to double float */
        operand_v.precision = fp_e;                     /*   and the result precision to double float */

        for (index = 0; index < 4; index++) {           /* read both operands */
            cpu_read_memory (data_checked, DB + RB + index & LA_MASK, &operand_u.words [index]);
            cpu_read_memory (data_checked, DB + RA + index & LA_MASK, &operand_v.words [index]);
            }

        STA &= ~STATUS_O;                               /* clear the overflow flag */

        operand_w =                                     /* call the floating-point executor */
           fp_exec ((FP_OPR) (opcode - 010 + fp_add),   /*   and convert the opcode */
                    operand_u, operand_v);              /*     to an arithmetic operation */

        for (index = 0; index < 4; index++)             /* write the result */
            cpu_write_memory (data_checked, DB + RC + index & LA_MASK, operand_w.words [index]);

        cpu_pop ();                                     /* delete two words */
        cpu_pop ();                                     /*   from the stack */

        SET_CCA (operand_w.words [0],                   /* set the condition code */
                 operand_w.words [1] | operand_w.words [2] | operand_w.words [3]);

        if (operand_w.trap != trap_None) {                  /* if an error occurred */
            if (operand_w.trap == trap_Ext_Float_Overflow   /*   then if the result overflowed */
              && (STA & STATUS_CC_MASK) == STATUS_CCE)      /*     to a zero value */
                SET_CCG;                                    /*       then set CCG */

            if ((STA & STATUS_T) == 0)                  /* if user traps are disabled */
                cpu_pop ();                             /*   then delete the result address */

            MICRO_ABORT (operand_w.trap);               /* trap or set overflow */
            }

        else                                            /* otherwise the operation completed normally */
            cpu_pop ();                                 /*   so delete the result address */
        break;


    case 014:                                               /* ENEG (CCA; STUN) */
        cpu_read_memory (data_checked, DB + RA & LA_MASK,   /* read the first word */
                         &operand_x);                       /*   of the operand */

        if (operand_x == 0) {                               /* if the first word is zero */
            for (index = 1; index < 4; index++) {           /*   then check the other words */
                cpu_read_memory (data_checked,              /*     to see if they are */
                                 DB + RA + index & LA_MASK, /*       all zero as well */
                                 &operand_y);

                if (operand_y != 0)                         /* if a non-zero word is seen */
                    break;                                  /*   then quit the check */
                }

            if (index == 4) {                           /* if the operand value is zero */
                SET_CCE;                                /*   then set CCE */
                cpu_pop ();                             /*     and delete the operand address */
                break;                                  /*       and return without rewriting the value */
                }
            }

        operand_x = operand_x ^ D16_SIGN;               /* complement the sign bit of the non-zero operand */

        cpu_write_memory (data_checked, DB + RA & LA_MASK,  /* write the updated value back */
                          operand_x);

        SET_CCA (operand_x, 1);                         /* set CCL or CCG from the sign bit */
        cpu_pop ();                                     /*   and delete the operand address from the stack */
        break;


    case 015:                                           /* ECMP (CCC; STUN) */
        address_x = DB + RB & LA_MASK;                  /* form the data offset */
        address_y = DB + RA & LA_MASK;                  /*   for the two operands */

        if (NPRV && (address_y < DL || address_y > SM)) /* if non-privileged and the operand is out of bounds */
            MICRO_ABORT (trap_Bounds_Violation);        /*   then trap for a bounds violation */

        cpu_pop ();                                     /* delete two words */
        cpu_pop ();                                     /*   from the stack */

        if (NPRV && (address_x < DL || address_x > SM)) /* if non-privileged and the operand is out of bounds */
            MICRO_ABORT (trap_Bounds_Violation);        /*   then trap for a bounds violation */

        cpu_read_memory (data, address_x, &operand_x);  /* read the first word */
        cpu_read_memory (data, address_y, &operand_y);  /*   of each of the two operands */

        negative = operand_x & D16_SIGN;                /* TRUE if first operand is negative */

        if ((operand_x ^ operand_y) & D16_SIGN)         /* if the operand signs differ */
            SET_CCA (operand_x, 1);                     /*   then set the condition on the first words excluding CCE */

        else if (operand_x != operand_y)                /* otherwise if the first operand words differ */
            if (negative)                               /*   then if they're both negative */
                SET_CCC (operand_y, 0, operand_x, 0);   /*     then reverse the comparison */
            else                                        /*   otherwise */
                SET_CCC (operand_x, 0, operand_y, 0);   /*     compare the integer operands */

        else {
            for (index = 1; index < 4; index++) {       /* otherwise compare the remaining words */
                cpu_read_memory (data, address_x + index & LA_MASK, &operand_x);
                cpu_read_memory (data, address_y + index & LA_MASK, &operand_y);

                if (operand_x != operand_y)             /* once the words differ */
                    break;                              /*   then the comparison is finished */
                }

            if (negative)                               /* if the operands are negative */
                SET_CCC (0, operand_y, 0, operand_x);   /*   then reverse the logical comparison */
            else                                        /* otherwise */
                SET_CCC (0, operand_x, 0, operand_y);   /*   compare the operand words logically */
            }
        break;


    default:
        status = STOP_UNIMPL;                           /* the firmware extension instruction is unimplemented */
    }

return status;                                          /* return the execution status */
}


/* Execute an EIS decimal arithmetic operation.

   This routine is called to execute the decimal arithmetic 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 | 1 |  options  |  decimal op   |  EIS Decimal
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     Decimal Opcode:

       00 - (undefined)
       01 - DMPY (Double logical multiply)
       02 - CVAD (Convert ASCII to decimal)
       03 - CVDA (Convert decimal to ASCII)
       04 - CVBD (Convert binary to decimal)
       05 - CVDB (Convert decimal to binary)
       06 - SLD  (Shift left decimal)
       07 - NSLD (Normalizing shift left decimal)
       10 - SRD  (Shift right decimal)
       11 - ADDD (Add decimal)
       12 - CMPD (Compare decimal)
       13 - SUBD (Subtract decimal)
       14 - MPYD (Multiply decimal)
       15 - (undefined)
       16 - (undefined)
       17 - (undefined)

   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.


   Implementation notes:

    1. Each instruction lists a potential stack underflow trap.  The underflow
       is actually detected in the firmware dispatcher, which has a stack
       preadjust of 4, before this routine is called.  The "cpu_pop" calls
       below always succeed, as the stack registers are fully populated on
       entry.

    2. All of the decimal instruction except DMPY, SLD, NSLD, and SRD, test for
       seven words of available stack space on entry (including the four words
       already present in the TOS registers) and trap for a stack overflow if
       they are not present.  In microode, the test passes if SM + 7 <= Z.  In
       simulation, the test compares SM plus SR plus a per-opcode addition to Z,
       which is equivalent.  The test is skipped for the four instructions
       above, although for SLD, NSLD, and SRD, the test is merely postponed
       until after the X register is masked to the lower five bits in the opcode
       handlers.

    3. The CVDA, SLD, NSLD, and SRD instructions test for trap conditions before
       setting the condition code for the operand.  As "read_decimal" sets the
       condition code, these instructions must save the status register on entry
       and restore if a trap is taken.

    4. If a bad decimal digit is present, the CVDA microcode converts and writes
       ASCII characters until the digit is encountered, resulting in a partial
       conversion before the trap is taken.  The diagnostic tests for this.

    5. For the CMPD instruction with both operands negative, XORing the
       condition code with STATUS_CCL flips the result of the magnitude
       comparison, i.e., CCL becomes CCG, and vice versa.
*/

t_stat cpu_eis_dec_op (void)
{
static const HP_WORD stack_check [] = {         /* extra stack words needed, indexed by opcode */
    0, 0, 3, 3, 3, 3, 0, 0,
    0, 3, 3, 3, 3, 0, 0, 0
    };

DIGIT_ACCESS source, target, left, right;
HP_WORD      entry_status, comparison;
uint32       opcode;
t_uint64     product;
uint32       trap   = trap_None;
t_stat       status = SCPE_OK;

opcode = FMEXSUBOP (CIR);                               /* get the opcode from the instruction */

if (stack_check [opcode] > 0                            /* if extra words on the stack are needed */
 && SM + SR + stack_check [opcode] > Z)                 /*   and they aren't available */
    MICRO_ABORT (trap_Stack_Overflow);                  /*     then trap for a stack overflow */

else switch (opcode) {                                  /* otherwise dispatch the opcode */

    case 001:                                           /* DMPY (CCA, C; STUN) */
        product = (t_uint64) TO_DWORD (RB, RA)          /* multiply the TOS double word */
                * (t_uint64) TO_DWORD (RD, RC);         /*   by the NOS double word */

        RD = HIGH_UPPER_WORD (product);                 /* separate */
        RC = LOW_UPPER_WORD (product);                  /*   then resulting */
        RB = UPPER_WORD (product);                      /*     quad word product */
        RA = LOWER_WORD (product);                      /*       and return in the TOS registers */

        SET_CARRY (RD | RC);                            /* set carry if the upper double-word is significant */
        SET_CCA (RD, RC | RB | RA);                     /*   and set the condition code for the product */
        break;


    case 002:                                           /* CVAD (CCA, O; STUN, STOV, ARITH) */
        if (RA > MAX_DIGITS || RC > MAX_DIGITS)         /* if the source or target digit counts are too large */
            trap = trap_Invalid_Decimal_Length;         /*   then trap for a count overflow */

        else if (RA > 0 && RC > 0) {                                /* otherwise if there are digits to process */
            init_decimal (&source, External, data_checked, RB, RA); /*   so set up digit accessors */
            init_decimal (&target, Packed, data_checked, RD, RC);   /*     for the source and target decimals */

            read_decimal (&source);                     /* read the source ASCII number, ignoring errors */

            if (TRACING (cpu_dev, DEB_MOPND))
                fprint_decimal_operand (&source, "source");

            trap = convert_decimal (&target, &source);  /* convert ASCII to packed decimal and check for errors */

            write_decimal (&target, FALSE);             /* write the decimal with a leading zero if required */

            if (TRACING (cpu_dev, DEB_MOPND))
                fprint_decimal_operand (&target, "result");

            set_cca_decimal (&target);                  /* set CCA on the decimal result */
            }

        decrement_stack (trap, 2, 4, 0);                /* decrement the stack and trap if indicated */
        break;


    case 003:                                           /* CVDA (CCA, O; STUN, STOV, ARITH) */
        while (SR > 3)                                  /* if more than three TOS register are valid */
            cpu_queue_down ();                          /*   then queue them down until exactly three are left */

        if (RB > MAX_DIGITS)                            /* if the target digit count is too large */
            trap = trap_Invalid_Decimal_Length;         /*   then trap for a count overflow */

        else if (RB > 0) {                              /* otherwise if there are digits to process */
            entry_status = STA;                         /*   then save the entry status for potential rollback */

            init_decimal (&source, Packed, data_checked, RA, RB);   /* set up digit accessors */
            init_decimal (&target, External, data_checked, RC, RB); /*   for the source and target decimals */

            read_decimal (&source);                     /* read the source decimal number, ignoring errors */

            if (TRACING (cpu_dev, DEB_MOPND))
                fprint_decimal_operand (&source, "source");

            trap = convert_decimal (&target, &source);  /* convert packed decimal to ASCII and check for errors */

            write_decimal (&target, TRUE);              /* write the decimal number to memory */

            if (TRACING (cpu_dev, DEB_MOPND))
                fprint_decimal_operand (&target, "result");

            if (trap == trap_None)                      /* if the conversion succeeded */
                set_cca_decimal (&target);              /*   then set CCA on the decimal result */
            else                                        /* otherwise */
                STA = entry_status;                     /*   restore the original entry status */
            }

        decrement_stack (trap, 1, 3, 0);                /* decrement the stack and trap if indicated */
        break;


    case 004:                                           /* CVBD (CCA, O; STUN, STOV, ARITH) */
        if (RA > MAX_WORDS)                             /* if the source word count is too large */
            trap = trap_Invalid_Word_Count;             /*   then trap for a word count overflow */

        else if (RC > MAX_DIGITS)                       /* otherwise if the target digit count is too large */
            trap = trap_Invalid_Decimal_Length;         /*   then trap for a count overflow */

        else if (RA > 0 && RC > 0) {                                /* otherwise if there are words to process */
            init_decimal (&target, Packed, data_checked, RD, RC);   /*   then set up the target digit accessor */

            trap = convert_binary (&target, RB, RA);    /* convert the binary number (RB,RA) to decimal */

            write_decimal (&target, TRUE);              /* write the (possibly truncated) decimal number */

            if (TRACING (cpu_dev, DEB_MOPND))
                fprint_decimal_operand (&target, "result");

            set_cca_decimal (&target);                  /* set CCA on the decimal result */
            }

        decrement_stack (trap, 2, 4, 0);                /* decrement the stack and trap if indicated */
        break;


    case 005:                                           /* CVDB (CCA, O; STUN, STOV, ARITH) */
        while (SR > 3)                                  /* if more than three TOS register are valid */
            cpu_queue_down ();                          /*   then queue them down until exactly three are left */

        if (RA > MAX_DIGITS)                            /* if the source 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 */
            init_decimal (&source, Packed, data_checked, RB, RA);   /*   then set up the source digit accessor */

            trap = convert_binary (&source, RC, RA);    /* convert the decimal number to binary (RC,RA) */

            if (trap == trap_None)                      /* if the source decimal was valid */
                set_cca_decimal (&source);              /*   then set CCA on the decimal result */
            }

        decrement_stack (trap, 2, 3, 0);                /* decrement the stack and trap if indicated */
        break;


    case 006:                                           /* SLD  (CCA, C, O; STUN, STOV, ARITH) */
    case 007:                                           /* NSLD (CCA, C, O; STUN, STOV, ARITH) */
        SET_CARRY (FALSE);                              /* clear carry in anticipation of a good result */

        /* fall through into SRD case */

    case 010:                                           /* SRD  (CCA, O; STUN, STOV, ARITH) */
        entry_status = STA;                             /* save the entry status for potential rollback */

        X = X & MAX_COUNT_MASK;                         /* mask the shift count to the lower five bits */

        if (SM + SR + 3 > Z)                            /* if there aren't three free words on the stack */
            MICRO_ABORT (trap_Stack_Overflow);          /*   then trap for a stack overflow */

        else if (RA > MAX_DIGITS || RC > MAX_DIGITS)    /* otherwise if the source or target counts are too large */
            trap = trap_Invalid_Decimal_Length;         /*   then trap for a count overflow */

        else if (RA > 0 && RC > 0) {                        /* otherwise if there are digits to process */
            init_decimal (&source, Packed, data_checked, RB, RA);   /*   so set up digit accessors */
            init_decimal (&target, Packed, data_checked, RD, RC);   /*     for the source and target decimals */

            trap = read_decimal (&source);              /* read the source decimal number */

            if (TRACING (cpu_dev, DEB_MOPND))
                fprint_decimal_operand (&source, "source");

            if (trap == trap_None) {                        /* if the source number is valid */
                trap = shift_decimal (&target, &source,     /*   then shift the number as indicated */
                                      (SHIFT_MODE) opcode); /*     by the instruction opcode */

                if (trap == trap_None) {                /* if the shift succeeded */
                    write_decimal (&target, TRUE);      /*   then write the result to memory */

                    if (TRACING (cpu_dev, DEB_MOPND))
                        fprint_decimal_operand (&target, "target");

                    set_cca_decimal (&target);          /* set CCA on the decimal result */
                    }

                else                                        /* otherwise the shift failed */
                    STA = STA & STATUS_C | entry_status;    /*   so restore the status but keep the carry bit */
                }
            }

        decrement_stack (trap, 0, 2, 4);                /* decrement the stack and trap if indicated */
        break;


    case 011:                                           /* ADDD (CCA, O; STUN, STOV, ARITH) */
        if (read_operands (&source, &target, &trap)) {  /* read the decimal operands; if they are valid */
            trap = add_decimal (&target, &source);      /*   then add them */
            write_operand (&target);                    /*     and write the result back */
            }

        decrement_stack (trap, 0, 2, 4);                /* decrement the stack and trap if indicated */
        break;


    case 012:                                               /* CMPD (CCC, O; STUN, STOV, ARITH) */
        if (read_operands (&right, &left, &trap)) {         /* read the decimal operands; if they are valid */
            comparison = compare_decimal (&left, &right);   /*   then compare the operand magnitudes */

            if (left.sign == Negative                   /* if the operand signs are the same */
              && right.sign == Negative                 /*   and negative */
              && comparison != STATUS_CCE)              /*     and the values aren't equal */
                comparison = comparison ^ STATUS_CCL;   /*       then flip the magnitude comparison */

            else if (right.sign != left.sign            /* otherwise if the signs are different */
              && (right.significant_index != NOT_SET    /*   and the comparison */
              || left.significant_index != NOT_SET))    /*     is not +0 = -0 */
                if (right.sign == Negative)             /*       then if the right operand is negative */
                    comparison = STATUS_CCG;            /*         then the left is greater (positive) */
                else                                    /*       otherwise the right is positive */
                    comparison = STATUS_CCL;            /*         so the left is smaller (negative) */

            STA = STA & ~STATUS_CC_MASK | comparison;   /* set the condition code */
            }

        decrement_stack (trap, 0, 2, 4);                /* decrement the stack and trap if indicated */
        break;


    case 013:                                           /* SUBD (CCA, O; STUN, STOV, ARITH) */
        if (read_operands (&source, &target, &trap)) {  /* read the decimal operands; if they are valid */
            trap = subtract_decimal (&target, &source); /*   then subtract them */
            write_operand (&target);                    /*     and write the result back */
            }

        decrement_stack (trap, 0, 2, 4);                /* decrement the stack and trap if indicated */
        break;


    case 014:                                           /* MPYD (CCA, O; STUN, STOV, ARITH) */
        if (read_operands (&source, &target, &trap)) {  /* read the decimal operands; if they are valid */
            trap = multiply_decimal (&target, &source); /*   then multiply them */
            write_operand (&target);                    /*     and write the result back */
            }

        decrement_stack (trap, 0, 2, 4);                /* decrement the stack and trap if indicated */
        break;


    default:
        status = STOP_UNIMPL;                           /* the firmware extension instruction is unimplemented */
    }

return status;                                          /* return the execution status */
}



/* EIS local utility routine declarations */


/* Initialize a decimal accessor.

   The supplied decimal accessor structure is initialized for the numeric
   format, starting relative byte offset pointer, digit count, and type of
   memory access desired.  If checked accesses are requested, then the starting
   and ending word addresses will be bounds-checked, and a Bounds Violation will
   occur if the address range exceeds that permitted by the access.

   On return, the decimal accessor is ready for use with the other decimal
   access routines.

   Decimal accessors may be used to sequentially read or write packed or
   external decimal numbers from or to memory.  Packed numbers store two BCD
   digits per byte, except for the last byte, which contains the LSD and the
   sign, and the first byte, which contains a single digit if the count of
   digits is even.  External numbers store one ASCII digit per byte.  The read
   and write routines handle the digit packing and unpacking automatically.


   Implementation notes:

    1. The underlying byte access routines assume that if the initial range or
       starting address is checked, succeeding accesses need not be checked, and
       vice versa.  The implication is that if the access class passed to this
       routine is checked, the routine might abort with a Bounds Violation, but
       succeeding read or write accesses will not, and if the class is
       unchecked, this routine will not abort but a succeeding access might.
*/

static void init_decimal (DIGIT_ACCESS *dap, DECIMAL_FORMAT format, ACCESS_CLASS class,
                          HP_WORD byte_offset, HP_WORD digit_count)
{
int    zero;
uint32 byte_count;

dap->format = format;                                   /* set the decimal number format */

if (format == Packed) {                                 /* if a packed number is designated */
    byte_count = digit_count / 2 + 1;                   /*   then convert the digit count to a byte count */
    dap->sign = Positive;                               /* set the sign of the zero value */
    zero = 0;                                           /*   and initialize with a numeric zero */
    }

else {                                                  /* otherwise an external number is designated */
    byte_count = digit_count;                           /*   so the byte count is the digit count */
    dap->sign = Unsigned;                               /* set the sign of the zero value */
    zero = (int) '0';                                   /*   and initialize with a character zero */
    }

dap->byte_offset = byte_offset;                         /* save the offset to the first byte to access */

mem_init_byte (&dap->bac, class, &dap->byte_offset, byte_count);    /* set up a byte accessor for the digits */

dap->significant_index = NOT_SET;                       /* initialize the significant digit index */
dap->starting_index    = MAX_DIGITS - digit_count;      /*   and save the index of the first valid digit */
dap->digit_count       = digit_count;                   /*     and the number of valid digits */

memset (dap->digits, zero, sizeof dap->digits);         /* store zeros in the full digit array */

return;
}


/* Read a decimal number from memory.

   The decimal number indicated by the supplied decimal accessor is read from
   memory into the accessor's digit array and checked for correctness.  The
   routine returns trap_Invalid_Decimal_Digit or trap_Invalid_ASCII_Digit if
   invalid digit is encountered, depending on the accessor format.  If all of
   the digits are legal representations, trap_None is returned after the index
   of the first significant digit in the number is determined.  The index is
   that of the first non-zero digit after the leading zeros.  If the decimal
   number is zero, the significant digit index will equal the maximum digit
   count, i.e., will point just beyond the last digit.

   Special handling is needed for the first byte of a packed decimal number  if
   the digit count is even.  In this case, only the right-hand digit within the
   byte is part of the number.  Otherwise, all bytes contain two digits except
   for the last, which contains a digit and the sign.


   Implementation notes:

    1. For packed decimal numbers only, the microcode sets the condition code to
       CCL or CCG, depending on the sign of the number, before checking digits
       for validity.  Consequently, if an invalid digit trap is taken, the
       condition code has already been set.  We follow that behavior here.
*/

static uint32 read_decimal (DIGIT_ACCESS *dap)
{
HP_BYTE byte, upper_digit, lower_digit;
uint32  byte_count, index;
uint32  digit_trap = trap_None, sign_trap = trap_None;

index = dap->starting_index;                            /* get the index of the first digit to store */
byte_count = dap->bac.length;                           /*   and the number of bytes to read */

dap->significant_index = NOT_SET;                       /* initialize the index of the first significant digit */

if (dap->format == Packed) {                            /* if this is a packed decimal value */
    if ((dap->digit_count & 1) == 0) {                  /*   then if the digit count is even */
        byte = mem_read_byte (&dap->bac);               /*     then read the byte containing the single MSD */
        byte_count--;                                   /*       and drop the remaining count */

        lower_digit = LOWER_HALF (byte);                /* get the right digit from the byte */

        if (lower_digit > 9)                            /* if the digit is invalid */
            digit_trap = trap_Invalid_Decimal_Digit;    /*   then set up the trap */
        else if (lower_digit > 0)                       /* otherwise if the digit is non-zero */
            dap->significant_index = index;             /*   then it is the first significant digit */

        dap->digits [index++] = lower_digit;            /* save it as the first digit */
        }

    while (byte_count > 0) {                            /* for the remaining bytes */
        byte = mem_read_byte (&dap->bac);               /*   read the next byte from memory */
        byte_count--;                                   /*     and drop the remaining count */

        upper_digit = UPPER_HALF (byte);                /* split the byte */
        lower_digit = LOWER_HALF (byte);                /*   into left and right digits */

        if (upper_digit > 9)                                        /* if the digit is invalid */
            digit_trap = trap_Invalid_Decimal_Digit;                /*   then set up the trap */
        if (upper_digit > 0 && dap->significant_index == NOT_SET)   /* otherwise if it's non-zero and not yet indexed */
            dap->significant_index = index;                         /*   then save the first significant digit index */

        dap->digits [index++] = upper_digit;            /* save the left-hand digit */

        if (byte_count == 0)                            /* if this is the last byte */
            if (lower_digit == SIGN_MINUS) {            /*   then if a minus sign is present */
                SET_CCL;                                /*     then preset the condition code to "less than" */
                dap->sign = Negative;                   /*       and set the decimal sign negative */
                }

            else {                                      /* otherwise */
                SET_CCG;                                /*   preset the condition code to "greater than" */

                if (lower_digit == SIGN_UNSIGNED)       /* if an unsigned indicator is present */
                    dap->sign = Unsigned;               /*   then the decimal is unsigned */
                else                                    /* otherwise a plus sign is assumed */
                    dap->sign = Positive;               /*   and the decimal is positive */
                }

        else {                                              /* otherwise this is an intermediate byte */
            if (lower_digit > 9)                            /*   so if the digit is invalid */
                digit_trap = trap_Invalid_Decimal_Digit;    /*     then set up the trap */

            else if (lower_digit > 0                    /* otherwise if it's non-zero */
              && dap->significant_index == NOT_SET)     /*   and not yet indexed */
                dap->significant_index = index;         /*     then save the first significant digit index */

            dap->digits [index++] = lower_digit;        /* save the right-hand byte */
            }
        }
    }

else if (dap->format == External)                       /* otherwise it's an external decimal */
    while (byte_count > 0) {                            /* for the remaining bytes */
        byte = mem_read_byte (&dap->bac);               /*   read the byte from memory */
        byte_count--;                                   /*     and drop the remaining count */

        if (byte_count == 0 && byte != ' ')                     /* if this is a non-blank overpunched sign */
            sign_trap = strip_overpunch (&byte, &dap->sign);    /*   then strip the overpunch and set the sign */

        else if (byte < '0' && byte != ' ' || byte > '9')   /* otherwise if the digit is not valid */
            digit_trap = trap_Invalid_ASCII_Digit;          /*   then trap for the error */

        if (byte > '0' && dap->significant_index == NOT_SET)    /* if it's non-zero and not yet indexed */
            dap->significant_index = index;                     /*   the save the first significant digit index */

        dap->digits [index++] = byte;                   /* save the byte */
        }

if (digit_trap != trap_None)                            /* if a bad digit was seen */
    return digit_trap;                                  /*   then return the trap code */
else                                                    /* otherwise */
    return sign_trap;                                   /*   return success or a bad sign trap code */
}


/* Write a decimal number to memory.

   The decimal number indicated by the supplied decimal accessor is written to
   memory.  Special handling is needed for the first byte of a packed decimal
   number if the digit count is even.  In this case, only the right-hand digit
   within the byte is part of the number.  Whether the most-significant digit is
   merged with the left-hand four bits of the byte or whether those bits are
   zeroed is determined by the supplied "merge_digits" parameter.  The parameter
   is ignored when writing numbers in external decimal format.


   Implementation notes:

    1. The CVAD instruction is the only one that does NOT merge the MSD into the
       leading byte.  It is also the only instruction that can write a negative
       zero number.

    2. Error aborts (e.g., from the CVDA instruction) may request writing fewer
       digits than are indicated by the starting index by reducing the byte
       accessor length.  Therefore, we use the latter instead of the former to
       determine the number of bytes to write.
*/

static void write_decimal (DIGIT_ACCESS *dap, t_bool merge_digits)
{
HP_BYTE byte, upper_digit, lower_digit;
uint32  byte_count, index;

index = dap->starting_index;                            /* get the index of the first digit to store */
byte_count = dap->bac.length;                           /*   and the number of bytes to read */

if (byte_count == 0)                                    /* if there are no bytes to write */
    return;                                             /*   then quit now */

if (dap->format == Packed) {                                /* if this is a packed decimal value */
    if (dap->significant_index == NOT_SET && merge_digits)  /*   then if the value is zero and merged */
        dap->sign = Positive;                               /*     then ensure that we write a positive zero */

    if ((dap->digit_count & 1) == 0) {                  /* if the digit count is even */
        if (merge_digits) {                             /*   then if the MSD must be merged */
            byte = mem_read_byte (&dap->bac);           /*     then get the current byte value */

            mem_reset_byte (&dap->bac);                 /* reset the byte accessor back to its original location */

            byte = TO_BYTE (UPPER_HALF (byte),          /* merge the MSD with the existing value */
                            dap->digits [index++]);     /*   in the byte */
            }

        else                                            /* otherwise merging is not required */
            byte = dap->digits [index++];               /*   so set the upper half of the byte to zero */

        mem_write_byte (&dap->bac, byte);               /* write the initial byte to memory */
        byte_count--;                                   /*   and drop the remaining count */
        }

    while (byte_count > 0) {                            /* for the remaining bytes */
        upper_digit = dap->digits [index++];            /*   get the left-hand digit */

        if (byte_count > 1)                             /* if this is an intermediate byte */
            lower_digit = dap->digits [index++];        /*   then get the right-hand digit */
        else                                            /* otherwise it's the last byte */
            lower_digit = sign_digit [dap->sign];       /*   so get the sign instead */

        byte = TO_BYTE (upper_digit, lower_digit);      /* merge the digits in the byte */

        mem_write_byte (&dap->bac, byte);               /* write the byte to memory */
        byte_count--;                                   /*   and drop the remaining count */
        }
    }

else if (dap->format == External)                       /* otherwise it's an external decimal */
    do {                                                /*   so for each digit */
        byte = dap->digits [index++];                   /*     get the next digit */

        if (byte_count == 1                             /* if this is the last byte */
          && byte >= '0' && byte <= '9')                /*   and the digit is valid */
            byte = overpunch [dap->sign] [byte - '0'];  /*     then get the overpunched sign */

        mem_write_byte (&dap->bac, byte);               /* write the byte to memory */
        }
    while (--byte_count > 0);                           /* continue until all bytes are written */

mem_update_byte (&dap->bac);                            /* write any partial final word if present */

return;
}


/* Compare two decimal numbers.

   This routine compares the magnitudes of two decimal numbers and returns a
   condition code to indicate the result (cc = first < | = | > second).  The
   signs of the numbers are not considered.

   If one number has more significant digits than the other, then it is larger
   by definition, and vice versa.  If both numbers have the same number of
   significant digits, then the corresponding digits of each number are compared
   until they differ or until all digits have been examined.  If both numbers
   have no significant digits, then both are zero values and so are equal.

   The returned status code represents the comparison of the first operand to
   the second operand.
*/

static HP_WORD compare_decimal (DIGIT_ACCESS *first, DIGIT_ACCESS *second)
{
uint32 index;

if (first->significant_index < second->significant_index)       /* if the first has more significant digits */
    return STATUS_CCG;                                          /*   than the second, then it is greater in value */

else if (first->significant_index > second->significant_index)  /* otherwise if the first has fewer significant */
    return STATUS_CCL;                                          /*   digits, then it is smaller in value */

else {                                                  /* otherwise they have the same significance */
    index = first->significant_index;                   /*   so they must be examined digit by digit */

    while (index < MAX_DIGITS) {                                    /* while digits remain */
        if (first->digits [index] > second->digits [index])         /*   if the first digit is greater */
            return STATUS_CCG;                                      /*     then the first operand is greater */

        else if (first->digits [index] < second->digits [index])    /* otherwise if the digit is smaller */
            return STATUS_CCL;                                      /*   then the first operand is smaller */

        index++;                                        /* otherwise they are equal, so try the next pair */
        }

    return STATUS_CCE;                                  /* all digits are equal, so the operands are equal */
    }
}


/* Add two decimal numbers.

   The sum of the two decimal operands is returned in the accessor of the first
   operand (augend = augend + addend).  If one operand is zero and the other is
   not, then the non-zero operand is returned as the sum.  Otherwise, the
   operands are added digit-by-digit.

   To ensure that the sum does not underflow (i.e., that the magnitude of the
   sum is always greater than zero), the operands are compared.  If the operand
   signs are the then same, the result is the sum of the magnitudes.  If the
   signs are different, then the sum is the smaller value subtracted from the
   larger value, and the result adopts the sign of the larger value.  If the
   magnitudes are equal and the signs are opposite, then the result is zero.

   Addition or subtraction is performed digit-by-digit, with carries between
   digits, until the all of the digits of the larger operand have been
   processed.

   A Decimal Overflow trap is returned if the result does not fit in the augend
   operand.


   Implementation notes:

    1. If the augend is zero, the addend digits must be copied to the augend
       digit array.  However, the ADDD and SUBD instructions allow the two
       operands to overlap.  Therefore, the "memmove" function must be used to
       copy the digits instead of the "memcpy" function.

    2. A borrow out of the last digit cannot occur because the operands are
       reordered if necessary to ensure that the difference is always positive.
*/

static uint32 add_decimal (DIGIT_ACCESS *augend, DIGIT_ACCESS *addend)
{
DIGIT_ACCESS *op1, *op2;
NUMERIC_SIGN operator;
HP_WORD      comparison;
HP_BYTE      carry;
int32        result;
uint32       index, last;

if (addend->significant_index == NOT_SET)               /* if the addend is zero */
    return trap_None;                                   /*   then the augend value is the sum */

else if (augend->significant_index == NOT_SET) {        /* otherwise if the augend is zero */
    memmove (augend->digits, addend->digits,            /*   then copy the addend value */
             sizeof augend->digits);                    /*     into the augend digit array */

    augend->sign = addend->sign;                            /* copy the addend sign */
    augend->significant_index = addend->significant_index;  /*   and index of significant digits */

    if (addend->significant_index < augend->starting_index) /* if the augend does not have enough room */
        return trap_Decimal_Overflow;                       /*   then an overflow occurs */
    else                                                    /* otherwise */
        return trap_None;                                   /*   the addend value is the sum */
    }

else {                                                  /* otherwise neither value is zero */
    comparison = compare_decimal (augend, addend);      /*   so compare the operand magnitudes */

    if (comparison == STATUS_CCL) {                     /* if the augend is smaller than the addend */
        op1 = addend;                                   /*   then swap the order */
        op2 = augend;                                   /*     for the sum or difference */
        }

    else {                                              /* otherwise the augend is larger than the addend */
        op1 = augend;                                   /*   so keep the supplied order */
        op2 = addend;                                   /*     for the sum or difference */
        }

    if (augend->sign == addend->sign)                   /* if the operand signs are the same */
        operator = Positive;                            /*   then sum the magnitudes */

    else if (comparison == STATUS_CCE) {                /* otherwise if the values are equal with different signs */
        memset (augend->digits, 0,                      /*   then the sum is zero */
                sizeof augend->digits);

        augend->sign = Positive;                        /* the result is positive */
        augend->significant_index = NOT_SET;            /*   with no significant digits */
        return trap_None;                               /*     and no error */
        }

    else {                                              /* otherwise the sum is determined */
        operator = Negative;                            /*   by subtracting the magnitudes */
        augend->sign = op1->sign;                       /*     and assuming the sign of the larger operand */
        }
    }

last = op1->significant_index;                          /* stop after processing the MSD of the larger value */

augend->significant_index = NOT_SET;                    /* reset the result significant digit index */

carry = 0;                                              /* start with no carry */
index = MAX_DIGITS;                                     /*   and with the LSD and work forward */

do {                                                    /* sum the digits in sequence */
    index--;                                            /* move the index to the next digit */

    if (operator == Positive)                                       /* if we're summing */
        result = op1->digits [index] + op2->digits [index] + carry; /*   then add the digits and carry */
    else                                                            /* otherwise */
        result = op1->digits [index] - op2->digits [index] - carry; /*   subtract the digits and borrow */

    if (result > 9) {                                   /* if a carry occurred */
        result = LOWER_HALF (result + 6);               /*   then correct the digit */
        carry = 1;                                      /*     and set the carry */
        }

    else if (result < 0) {                              /* otherwise if a borrow occurred */
        result = result + 10;                           /*   then correct the digit */
        carry = 1;                                      /*     and set the borrow */
        }

    else                                                /* otherwise */
        carry = 0;                                      /*   neither carry nor borrow was generated */

    if (result > 0 && index >= augend->starting_index)  /* if a significant digit that will fit in the result */
        augend->significant_index = index;              /*   then count it */

    augend->digits [index] = (HP_BYTE) result;          /* save the digit */
    }
while (index > last);                                   /*   and continue until all significant digits processed */

if (carry > 0 && index > 0) {                           /* if a carry out of the last significant digit occurs */
    augend->digits [--index] = carry;                   /*   then store it in the next MSD */
    carry = 0;                                          /*     and indicate that space was available for it */

    if (index >= augend->starting_index)                /* if the carry did not overflow the available space */
        augend->significant_index = index;              /*   then it becomes the most significant digit */
    }

if (carry > 0 || augend->starting_index > index)        /* if there is insufficient room to contain the result */
    return trap_Decimal_Overflow;                       /*   then indicate an overflow */
else                                                    /* otherwise */
    return trap_None;                                   /*   the addition succeeded */
}


/* Subtract two decimal numbers.

   The difference of the two decimal operands is returned in the accessor of the
   first operand (minuend = minuend - subtrahend).  Subtraction is implemented
   by negating the subtrahend and then adding the minuend.


   Implementation notes:

    1. If the subtrahend is zero, negating produces a "negative zero."  However,
       the "add" routine handles this as positive zero, so we do not need to
       worry about this condition.
*/

static uint32 subtract_decimal (DIGIT_ACCESS *minuend, DIGIT_ACCESS *subtrahend)
{
if (subtrahend->sign == Negative)                       /* invert the sign */
    subtrahend->sign = Positive;                        /*   of the subtrahend */
else
    subtrahend->sign = Negative;

return add_decimal (minuend, subtrahend);               /* add to obtain the difference */
}


/* Multiply two decimal numbers.

   The product of the two decimal operands is returned in the accessor of the
   first operand (multiplicand = multiplicand * multiplier).  Conceptually, the
   implementation is a 28 x 28 = 56-digit multiply with the lower 28 digits
   retained.  If either operand is zero, zero is returned as the product.
   Otherwise, the product is obtained by long multiplication (digit-by-digit
   multiplication and summation of the partial products) with the shorter of the
   two operands selected as the multiplier to improve efficiency.

   If the result would overflow 28 digits (the maximum allowed), the
   multiplication is not attempted, and no result is returned.  If the result
   fits in 28 digits but not in the space available for the result, the
   truncated result is returned.  In both cases, a Decimal Overflow trap is
   returned.  Otherwise, the product is returned in the multiplicand accessor,
   and no trap occurs.

   The product is obtained by multiplying the significant digits of the
   multiplicand by each significant digit of the multiplier and summing the
   partial products.  The sign of the product is positive if the operand signs
   are the same and negative if they differ.


   Implementation notes:

    1. An initial check is made to see if the product would exceed the maximum
       number of significant digits permitted (28).  If the sum of the numbers
       of significant digits exceeds 29, the product will not fit.  If the sum
       is 28 or less, the product will fit.  If the sum is 29, the product may
       fit, depending on the values of the operands.  In the last case, a carry
       out of the 28th digit indicates that the product won't fit.
*/

static uint32 multiply_decimal (DIGIT_ACCESS *multiplicand, DIGIT_ACCESS *multiplier)
{
DIGIT_ACCESS *op1, *op2;
HP_WORD      comparison;
HP_BYTE      product [MAX_DIGITS];
uint32       index_1, index_2, index_p, start_1, start_2, digit, partial, carry;

if (multiplicand->significant_index == NOT_SET)         /* if the multiplicand is zero */
    return trap_None;                                   /*   then it already hold the product */

else if (multiplier->significant_index == NOT_SET) {    /* otherwise if the multiplier is zero */
    memset (multiplicand->digits, 0,                    /*   then set the multiplicand value to zero */
            sizeof multiplicand->digits);               /*     by clearing the digit array */

    multiplicand->sign = Positive;                      /* the result is positive */
    multiplicand->significant_index = NOT_SET;          /*   with no significant digits */
    return trap_None;                                   /*     and no error */
    }

else if (multiplicand->significant_index                /* otherwise if the product would overflow */
  + multiplier->significant_index < MAX_DIGITS - 1)     /*   the maximum number of digits allowed */
    return trap_Decimal_Overflow;                       /*     then report it without trying */

else {                                                          /* otherwise neither value is zero */
    comparison = compare_decimal (multiplicand, multiplier);    /*   so compare the operand magnitudes */

    if (comparison == STATUS_CCL) {                     /* if the multiplicand is smaller than the multiplier */
        op1 = multiplier;                               /*   then swap the order */
        op2 = multiplicand;                             /*     to reduce the number of operations */
        }

    else {                                              /* otherwise the multiplicand is larger than the multiplier */
        op1 = multiplicand;                             /*   so keep the supplied order */
        op2 = multiplier;                               /*     which is already optimal */
        }
    }

if (multiplicand->sign == multiplier->sign)             /* if the operand signs are the same */
    multiplicand->sign = Positive;                      /*   then the result will be positive */
else                                                    /* otherwise */
    multiplicand->sign = Negative;                      /*   a negative value will result */

memset (product, 0, sizeof product);                    /* clear the product */

start_1 = op1->significant_index;                       /* get the indices of */
start_2 = op2->significant_index;                       /*   the first non-zero digits in each operand */

index_2 = MAX_DIGITS;                                   /* begin with the multiplier LSD and work toward the MSD */

do {                                                    /* form the partial products in sequence */
    index_p = index_2;                                  /* align the product sum with the multiplier digit */
    carry = 0;                                          /*   and start with no initial carry */

    digit = op2->digits [--index_2];                    /* get the next multiplier digit */

    if (digit > 0) {                                    /* if the partial product will contribute to the sum */
        index_1 = MAX_DIGITS;                           /*   then start at the multiplicand LSD and work forward */

        do {                                                /* form the next partial product */
            partial = product [--index_p] + carry           /*   from the sum of the current product and carry */
                        + op1->digits [--index_1] * digit;  /*     and the product of the next two operand digits */

            product [index_p] = partial % 10;           /* save the new current product digit */
            carry             = partial / 10;           /*   and carry any overflow to the next digit */
            }
        while ((index_1 > start_1 || carry > 0)         /* continue until the multiplicand is exhausted */
          && index_p > 0);                              /*   or the product has no more room */
        }
    }
while (index_2 > start_2);                              /* continue until the multiplier is exhausted */

if (carry > 0) {                                        /* if a carry out of the last digit occurred */
    multiplicand->bac.length = 0;                       /*   then skip writing back the result */
    return trap_Decimal_Overflow;                       /*     because it is larger than the maximum allowed */
    }

else {                                                  /* otherwise */
    multiplicand->significant_index = index_p;          /*   update the count of significant product digits */

    memcpy (multiplicand->digits, product,              /* copy the product digits */
            sizeof multiplicand->digits);               /*   back into the result accessor */

    if (multiplicand->significant_index                 /* if some significant digits will be lost */
      < multiplicand->starting_index)                   /*   because the result isn't large enough */
        return trap_Decimal_Overflow;                   /*     then signal an overflow */
    else                                                /* otherwise */
        return trap_None;                               /*   then correct product is returned */
    }
}


/* Shift a decimal number.

   The decimal number specified by the "source" accessor is shifted by the number
   of digits specified by the value in the X register in the direction and mode
   specified by the "shift" parameter and is returned in the "target" accessor.
   Three shift modes are supported:

     Shift Mode   Action
     -----------  ---------------------------------------------------------
     Right        Shift digits to the right, zero fill on the left
     Left         Shift digits to the left, zero fill on the right
     Normalizing  Same as Left, except stop shifting if a significant digit
                  would be lost

   For the Right shift mode, digits shifted off of the right end are lost.  For
   the Left shift mode, digits shifted off the left end are lost; if a lost
   digit is significant (i.e., non-zero), the Carry flag is set in the Status
   register.  The Normalizing shift mode is identical to the Left mode, except
   that shifting is stopped if a significant digit would be lost; the remaining
   shift count is present in the X register on return.  If no shifting can be
   done without losing a significant digit (i.e., the number of significant
   source digits is greater than the number of available target digits), Carry
   is set, and a Decimal Overflow trap is returned.  If the shift succeeds, no
   trap is returned.

   Entry is with the target value initialized to zero.  If the source value is
   zero, then shifting will not change the result, so the routine returns
   immediately.  Otherwise, shifting is performed by copying a selected set of
   digits from the source to the target.  This is accomplished by determining
   the starting and ending indices in the source digit array and the starting
   index in the target array.

   First, the proposed shift is examined to determine if significant digits will
   be lost due to an insufficiently large target.  This would occur if a left
   shift moved the most-significant digit outside of the available target space
   or a right shift failed to move the most-significant digit into the target
   space.  In the case of a normal left or right shift, the source index is
   advanced to the first digit to be transferred, and the target index is set to
   the first target digit.  For a normalizing left shift, the shift count is
   reduced to ensure that target will start with the most-significant source
   digit.  The left shifts also set Carry status; the right shift simply
   truncates the result.

   If no significant digits would be lost, the source and target indices are set
   as directed by the shift count and direction, and the index where the copy
   should end is calculated.  If the shift count is large enough to shift all
   source digits off either end of the target, no digits are copied, and the
   zero result is returned.

   The routine converts an unsigned source into a positive target but otherwise
   sets the target sign to that of the source.  As the requisite digits are
   copied, the significance of the target is computed, as losing significant
   digits from the source will render a smaller significance in the target.
*/

static uint32 shift_decimal (DIGIT_ACCESS *target, DIGIT_ACCESS *source, SHIFT_MODE shift)
{
uint32 source_index, target_index, end_index;

if (source->significant_index == NOT_SET)               /* if the source value is zero */
    return trap_None;                                   /*   then the target value is also zero */

else if (shift == Right) {
    if (source->significant_index + X < target->starting_index) {   /* if significant digits will be lost */
        source_index = target->starting_index - X;                  /*    then start at the first non-truncated digit */
        target_index = target->starting_index;                      /*      and copy to the first target digit */
        }

    else {                                              /* otherwise the leading digits will fit */
        source_index = source->significant_index;       /*   so start at the first significant digit */
        target_index = source_index + X;                /*     and target the desired shift location */
        }

    if (target_index < MAX_DIGITS)                              /* if there are target digits to move */
        end_index = source_index + MAX_DIGITS - target_index;   /*   then set up the ending source index */
    else                                                        /* otherwise the shift loses all digits */
        source_index = MAX_DIGITS;                              /*   so point beyond the source array */
    }

else if (shift == Left) {
    if (source->significant_index < target->starting_index + X) {   /* if significant digits will be lost */
        source_index = target->starting_index + X;                  /*    then start at the first non-truncated digit */
        target_index = target->starting_index;                      /*      and copy to the first target digit */

        SET_CARRY (TRUE);                                           /* set Carry to indicate a significance loss */
        }

    else {                                              /* otherwise all digits will fit */
        source_index = source->significant_index;       /*   so start at the first significant digit */
        target_index = source_index - X;                /*     and target the desired shift location */
        }

    end_index = MAX_DIGITS;                             /* set up the ending source index */
    }

else {
    if (source->significant_index < target->starting_index) {   /* if shift cannot be done without losing significance */
        SET_CARRY (TRUE);                                       /*   then set Carry status */
        return trap_Decimal_Overflow;                           /*     and trap for an overflow */
        }

    else                                                /* otherwise the leading digit will fit */
        source_index = source->significant_index;       /*   so start with the first significant digit */

    if (X > source_index - target->starting_index) {    /* if significant digits will be lost */
        target_index = target->starting_index;          /*   then start the copy at the first target digit */
        X = X - (source_index - target_index);          /*     and drop the shift count by the amount shifted */

        SET_CARRY (TRUE);                               /* set Carry to indicate a significance loss */
        }

    else                                                /* otherwise all source digits will fit */
        target_index = source_index - X;                /*   so target the desired shift location */

    end_index = MAX_DIGITS;                             /* set up the ending source index */
    }

if (source_index >= MAX_DIGITS)                         /* if all digits will be shifted out of the target */
    return trap_None;                                   /*   then the result is zero */
else if (source->sign == Unsigned)                      /* otherwise if the source is unsigned */
    target->sign = Positive;                            /*   then set the result positive */
else                                                    /* otherwise */
    target->sign = source->sign;                        /*   the result is the same sign as the source */

while (source_index < end_index) {                      /* while there are digits to move */
    if (target->significant_index == NOT_SET            /* if the significant digit count has not been set */
      && source->digits [source_index] > 0)             /*   and the next source digit is non-zero */
        target->significant_index = target_index;       /*     then mark it as significant */

    target->digits [target_index++] = source->digits [source_index++];  /* copy the digit to the result */
    }

return trap_None;                                       /* return success */
}


/* Convert between packed and external decimal.

   The supplied source operand is converted to the format of the target operand.
   If the target is in external decimal format, each packed decimal digit in the
   source is converted to ASCII and stored in the corresponding location in the
   target digit array.  Bits 9 and 10 of the instruction indicate how the sign
   is to be handled.  If bit 9 (NABS_FLAG) is set, the target is unsigned.  If
   bit 10 (ABS_FLAG) is set, the target is negative if the source is negative,
   otherwise it is unsigned.  If neither bit is set, the target adopts the
   source sign.

   If the target is packed, external decimal source digits are converted to BCD
   and stored in the target digit array.  Leading source blanks are allowed and
   are converted to zeros, but embedded blanks will cause a trap.  The source
   sign is decoded from the potentially overpunched LSD and set as the target
   sign.  Conversion stops if the target is filled; remaining source digits are
   not checked for validity.

   Invalid source digits will cause an Invalid Decimal Digit or Invalid ASCII
   Digit trap, depending on the format.  In addition, the partially converted
   value is present in the target to the same extent as in the microcode.  A
   decimal to ASCII conversion proceeds from left to right and stops at the
   point an invalid digit is encountered.  Any good digits converted to that
   point will be written to memory.

   For an ASCII to decimal conversion, the check is executed from right to left,
   and an Invalid ASCII Digit trap occurs if the check fails.  If a trap occurs,
   a partial decimal conversion may be done.  The rules are a bit arcane, and
   the diagnostic tests for expected results for bad source strings.

   The sign is checked first, so if it is bad, nothing is written.  Thereafter,
   words are written as they are converted until an invalid character is seen,
   whereupon an immediate trap is taken, and the word in which the bad character
   would appear is not written.  Because the decimal target may end in either
   the upper or lower byte of the last word, an illegal character as the
   next-to-last digit may or may not write a target word.  If the target ends in
   the upper byte, that word is written; if it ends in the lower byte, that word
   is not written.

   The situation is complicated by embedded blanks.  Because processing occurs
   from right to left, encountering one or more contiguous blanks may or may not
   be illegal, depending on whether any additional digits appear to the left of
   the blank(s).  Working backward, the first space encountered is replaced by a
   zero, and a flag is set to require all additional characters to be spaces
   (i.e., it assumes that it is processing leading spaces).  If a non-space
   character is subsequently encountered, the trap is taken at that point, and
   any prior blanks encountered are stored as valid zeros.

   This means, for example, that an embedded space that would go in the MSD of a
   word will write that word with a zero in the MSD and then trap on the NEXT
   valid digit.  So processing "1234 67" will write an "067F" word before
   trapping, but processing "12345 7" or "1234-67"  will write nothing.

   As another example, if the ASCII string consists of a "1" followed by eight
   blanks followed by the sign, and the target ends in the right-hand byte, a
   word of four zeros and a word of three zeros plus a sign would be written
   before the trap is taken.  However, if the string contained a "1" followed by
   two blanks followed by the sign, nothing would be written.

   The diagnostic checks these three cases:

     Step 463, source 7,"1234 67", result 3,"067F"
       stored as: 067F

     Step 464, source 9,"9=7654321", result 5,"54321F"
       stored as: 5432 1FFF

     Step 465, source 10,"0+2345678R", result 7,"3456789D"
       stored as: 3456 789D

   If all digits are valid, the routine returns no trap, and the converted value
   is present in the target accessor.


   Implementation notes:

    1. Error handling for either conversion requires that the target decimal be
       shortened, so that the resulting memory write matches that produced by
       the microcode.  Shortening a number involves resetting the byte accessor
       length, the starting digit index, and the digit count.  For a left-to-
       right conversion, the leading digits are written first, so setting these
       three values suffices.  For a right-to-left conversion, in addition to
       setting the previous three values, the first byte address, first byte
       offset, and current byte offset must be advanced to the start of the
       first valid digit, and the byte accessor must be set to pick up the new
       values.

    2. Because of the need to return partial results, entry may be made with
       illegal digits present in the source accessor, so they must be detected
       here.  An invalid ASCII sign (overpunch) will have something other than
       an ASCII digit in the last position, so no special handling is needed to
       detect this.

    3. On entry, at least one digit is to be converted, so special testing for a
       zero count is not required.
*/

static uint32 convert_decimal (DIGIT_ACCESS *target, DIGIT_ACCESS *source)
{
HP_BYTE byte;
uint32  index, blank_index, bytes_skipped, byte_count, digit_count;
uint32  trap = trap_None;

if (target->format == Packed) {
    index = MAX_DIGITS;                                 /* work right-to-left */
    blank_index = NOT_SET;                              /* initialize the blank index */

    target->sign = source->sign;                        /* the value adopts the source sign */
    target->significant_index = NOT_SET;                /* recalculate the significant digit index */

    while (index > source->starting_index               /* while there are ASCII digits to convert */
      && index > target->starting_index) {              /*   and packed digits to fill */
        byte = source->digits [--index];                /*     get the next source character */

        if (byte >= '0' && byte <= '9'                  /* if the character is numeric */
          && blank_index == NOT_SET)                    /*   and blanks are not being skipped */
            byte = byte - '0';                          /*     then convert to BCD */

        else if (byte == ' ') {                         /* otherwise if it's a blank */
            byte = 0;                                   /*   then fill with a zero */

            if (blank_index == NOT_SET)                 /* if the blank index has not been set */
                blank_index = index;                    /*   then set it now */
            }

        else {                                          /* otherwise */
            trap = trap_Invalid_ASCII_Digit;            /*   the digit is invalid */
            break;                                      /*     so quit at this point */
            }

        if (byte > 0)                                   /* if the digit is significant */
            target->significant_index = index;          /*   then set or reset the index */

        target->digits [index] = byte;                  /* add the digit to the target */
        }

    if (trap != trap_None) {                            /* if a bad digit is present */
        byte_count = (MAX_DIGITS - index) / 2;          /*   then get the count of good bytes including the sign */

        if (byte_count == 0) {                          /* if all bytes are bad, i.e., a bad sign */
            target->bac.length = 0;                     /*   then skip the write */
            target->significant_index = 0;              /*     but force CCG to match the microcode */
            }

        else {                                                              /* otherwise adjust for full word writes */
            if (target->bac.initial_byte_offset + target->bac.length & 1)   /* if the target ends on an even byte */
                byte_count = byte_count - 1 | 1;                            /*   then adjust the byte count */
            else                                                            /* otherwise it ends on an odd byte */
                byte_count = byte_count & ~1;                               /*   so adjust accordingly */

            bytes_skipped = target->bac.length - byte_count;    /* get the number of bytes that will be skipped */

            target->bac.length = byte_count;                    /* reset the number of bytes to write */
            target->bac.first_byte_address += bytes_skipped;    /*   and move the byte address and offset */
            target->bac.first_byte_offset += bytes_skipped;     /*     forward to the new starting byte */

            digit_count = byte_count * 2 - 1;           /* get the number of digits to write, excluding the sign */

            target->byte_offset += bytes_skipped;               /* move the working offset forward */
            target->starting_index = MAX_DIGITS - digit_count;  /* reset the starting index */
            target->digit_count = digit_count;                  /*   and the count of digits to write */

            mem_set_byte (&target->bac);                /* set the new write location in the target accessor */
            }
        }
    }

else if (target->format == External) {
    if (CIR & NABS_FLAG                                 /* if the request is for an unsigned result */
      || CIR & ABS_FLAG && source->sign != Negative)    /*   or unsigned unless negative */
        target->sign = Unsigned;                        /*     then reset the result sign */
    else                                                /* otherwise */
        target->sign = source->sign;                    /*   the target adopts the source sign */

    index = source->starting_index;                         /* start with the first digit */
    target->significant_index = source->significant_index;  /*   and set the significance index */

    do                                                  /* convert packed decimal to external decimal */
        if (source->digits [index] <= 9)                            /* if the source digit is valid */
            target->digits [index] = source->digits [index] + '0';  /*   then convert it to a character */

        else {                                                      /* otherwise */
            target->bac.length = index - source->starting_index;    /*   reset the operand length */
            target->digit_count = target->bac.length;               /*     to the count of good digits */
            target->sign = Unsigned;                                /*       and omit the sign overpunch */

            trap = trap_Invalid_Decimal_Digit;          /* trap for the error */
            break;                                      /*   and stop the conversion */
            }

    while (++index < MAX_DIGITS);                       /* loop until all digits are converted */
    }

return trap;                                            /* return the trap status */
}


/* Convert between binary and decimal number formats.

   This routines converts a packed decimal number into its multi-word twos
   complement binary equivalent or converts a binary number into its packed
   decimal equivalent.  The direction of the conversion is specified by the LSB
   of the machine instruction in the CIR: 0 for binary-to-decimal and 1 for
   decimal-to-binary.

   On entry, the "decimal" parameter contains an initialized digit accessor that
   is set up to provide (decimal-to-binary) or hold (binary-to-decimal) the
   packed decimal number.  The "address" and "count" parameters specify the
   memory address of the multi-word array used as a corresponding target or
   source for the binary number.  The size of the array depends on the number of
   digits in the packed decimal number, as follows:

     Digits  Words
     ------  -----
      1-4      1
      5-9      2
     10-18     4
     19-28     6

   The binary number is a twos complement value with the most-significant word
   first in the array.

   On return, the converted binary number has been written to memory
   (decimal-to-binary) or the converted decimal number is present in the digit
   accessor.  The function returns a trap value if an error occurred.

   Conversion from decimal to binary proceeds from the LSD to the MSD of the
   decimal value, forming a binary word from each group of four decimal digits
   (resulting in a value from 0-9999), multiplying the multi-word binary value
   by 10,000, and adding in the latest conversion.  This process is repeated
   until all of the significant digits in the source decimal are exhausted.  The
   multi-word multiplication is performed by doing 16 x 16 = 32 bit multiplies,
   saving the lower 16 bits as the current product word, and using the upper 16
   bits as a carry to the next product word.

   After the value conversion is complete, the binary result is negated if the
   source decimal sign is negative; this is done by a complement and increment
   across the array words.

   Conversion from binary to decimal begins by negating the binary number if it
   is negative to obtain the absolute value.  The number is then divided by
   10,000, with the remainder forming the least significant four digits of the
   decimal number.  The division is repeated, obtaining each set of four digits
   in turn, until the dividend is zero or the maximum number of decimal digits
   is reached.  If the limit is reached and the dividend is not yet zero, then a
   decimal overflow trap is indicated.


   Implementation notes:

    1. The CVDB microcode checks for a target bounds violation before reading
       the decimal number or writing any of the target words.  The diagnostic
       checks for this, so we must call "read_decimal" here instead of reading
       the number before calling this routine.  The diagnostic does not test for
       negative numbers, overflows, or bad digits, so these were tested
       independently.

    2. The CVBD microcode checks the upper bound of the binary array against SM
       and not SM + SR as is usually done; see page 172 of the microcode manual.
       The simulation follows the microcode.

    3. When converting from decimal to binary, the starting index into the digit
       array must be adjusted to start on a four-digit boundary, so that each
       group represents a division by 10,000.
*/

static uint32 convert_binary (DIGIT_ACCESS *decimal, HP_WORD address, HP_WORD count)
{
static const char *const binary_formats [] = {          /* binary display formats, indexed by word count */
    BOV_FORMAT "%s %d, %06o\n",                         /*   1 word format */
    BOV_FORMAT "%s %d, %06o %06o\n",                    /*   2 word format */
    BOV_FORMAT "%s %d, %06o %06o %06o\n",               /*   3 word format */
    BOV_FORMAT "%s %d, %06o %06o %06o %06o\n",          /*   4 word format */
    BOV_FORMAT "%s %d, %06o %06o %06o %06o %06o\n",     /*   5 word format */
    BOV_FORMAT "%s %d, %06o %06o %06o %06o %06o %06o\n" /*   6 word format */
    };

HP_WORD binary [6], carry;
uint32  index, counter, digit, offset, end, word_count;
uint32  complement, remainder, accumulator, dividend, sum, partial;
uint32  trap = trap_None;

if (CIR & 1) {                                          /* if this is a decimal-to-binary conversion */
    if (count < 5)                                      /*   then determine */
        word_count = 1;                                 /*     the number of */
    else if (count < 10)                                /*       binary words */
        word_count = 2;                                 /*         needed to hold */
    else if (count < 19)                                /*           the decimal number */
        word_count = 4;                                 /*             that is to be */
    else                                                /*               converted */
        word_count = 6;

    offset = DB + address & LA_MASK;                    /* get the starting and ending */
    end = offset + word_count - 1 & LA_MASK;            /*   memory offsets of the binary array */

    if (NPRV && (offset < DL || end > SM))              /* if non-privileged and out of range */
        MICRO_ABORT (trap_Bounds_Violation);            /*   then trap for a bounds violation */
    else                                                /* otherwise */
        trap = read_decimal (decimal);                  /*   read the source decimal number */

    if (TRACING (cpu_dev, DEB_MOPND))
        fprint_decimal_operand (decimal, "source");

    if (trap == trap_None) {                            /* if the source decimal is valid */
        memset (binary, 0, sizeof binary);              /*   then zero the target array */

        if (decimal->significant_index != NOT_SET) {        /* if the source decimal is not zero */
            index = (decimal->significant_index / 4) * 4;   /*   then point at the first group of four digits */

            do {                                        /* convert groups of four digits to binary */
                sum = 0;                                /* clear the group sum */

                for (counter = 0; counter < 4; counter++)       /* sum the next four */
                    sum = sum * 10 + decimal->digits [index++]; /*   decimal digits */

                carry = sum;                            /* set up the carry into the LSW */
                counter = word_count;                   /*   and start at the end of the array */

                do {                                                /* multiply the binary number by 10,000 */
                    partial = binary [--counter] * 10000 + carry;   /*   and add the sum */

                    binary [counter] = LOWER_WORD (partial);
                    carry            = UPPER_WORD (partial);
                    }
                while (counter > 0);
                }
            while (index < MAX_DIGITS);                 /* loop until all digits are converted */

            if (decimal->sign == Negative) {            /* if the decimal number is negative */
                carry = 1;                              /*   then negate the words */
                counter = word_count;                   /*     in the binary array */

                do {                                    /* perform a twos complement of the binary number */
                    complement       = LOWER_WORD (~binary [--counter]) + carry;
                    binary [counter] = LOWER_WORD (complement);
                    carry            = UPPER_WORD (complement);
                    }
                while (counter > 0);
                }
            }

        tprintf (cpu_dev, DEB_MOPND, binary_formats [word_count - 1],
                 DBANK, offset, address, "  target", word_count,
                 binary [0], binary [1], binary [2],
                 binary [3], binary [4], binary [5]);

        for (index = 0; index < word_count; index++)            /* write the binary number to memory */
            cpu_write_memory (data, offset++, binary [index]);  /*   with checking already done above */
        }
    }

else {                                                  /* otherwise this is a binary-to-decimal conversion */
    offset = DB + address & LA_MASK;                    /* get the starting and ending */
    end = offset + count - 1 & LA_MASK;                 /*   memory offsets of the binary array */

    if (NPRV && (offset < DL || end > SM))              /* if non-privileged and out of range */
        MICRO_ABORT (trap_Bounds_Violation);            /*   then trap for a bounds violation */

    for (index = 0; index < count; index++)                         /* load the binary array */
        cpu_read_memory (data, offset + index, &binary [index]);    /*   with checking already done above */

    tprintf (cpu_dev, DEB_MOPND, binary_formats [count - 1],
             DBANK, offset, address, "  source", count,
             binary [0], binary [1], binary [2],
             binary [3], binary [4], binary [5]);

    if (binary [0] & D16_SIGN) {                        /* if the source binary number is negative */
        decimal->sign = Negative;                       /*   then set the target decimal sign */
        carry = 1;                                      /*     and negate the words */
        counter = count;                                /*       in the array */

        do {                                            /* perform a twos complement of the binary number */
            complement       = LOWER_WORD (~binary [--counter]) + carry;
            binary [counter] = LOWER_WORD (complement);
            carry            = UPPER_WORD (complement);
            }
        while (counter > 0);
        }

    else                                                /* otherwise the binary number is positive */
        decimal->sign = Positive;                       /*   so set the decimal sign */

    decimal->significant_index = NOT_SET;               /* clear the significance counter for CCE detection */
    index = MAX_DIGITS;                                 /*   and start the conversion from the right end */

    do {                                                /* convert the binary array to decimal by decades */
        remainder = 0;                                  /* clear the initial remainder */
        accumulator = 0;                                /*   and the zero accumulator */

        for (counter = 0; counter < count; counter++) { /* divide the binary number by 10,000 */
            dividend = TO_DWORD (remainder, binary [counter]);

            remainder        = dividend % 10000;        /* divide the number by 10,000 */
            binary [counter] = dividend / 10000;        /*   to isolate groups of four digits */

            accumulator |= binary [counter];            /* accumulate to detect when the dividend is zero */
            }

        for (digit = 0; digit < 4; digit++) {           /* split the remainder */
            decimal->digits [--index] = remainder % 10; /*   into four separate digits */
            remainder                 = remainder / 10; /*     and store in the decimal number */

            if (decimal->digits [index] > 0)            /* if the digit is non-zero */
                decimal->significant_index = index;     /*   then (re)set the significance index */
            }
        }
    while (index > 0 && accumulator > 0);               /* loop until out of (significant) digits */

    if (accumulator > 0)                                /* if more digits are present than will fit */
        trap = trap_Decimal_Overflow;                   /*   then set up for an overflow trap */
    }

return trap;
}


/* Read a pair of decimal operands from memory.

   The two decimal operands specified by the four top-of-stack values are read
   into the supplied digit accessors.  The first accessor is read from the
   packed decimal number designated by RA (count) and RB (address) and the
   second from the number designated by RC (count) and RD (address).  If either
   digit count is too large, the routine returns a Word Count Overflow trap.  If
   either number contains an invalid digit, the routine returns an Invalid
   Decimal Digit trap.  The function returns TRUE if the accessors were
   populated with valid numbers and FALSE otherwise.

   If operand tracing is enabled, the two operand values are printed on the
   trace log before returning.
*/

static t_bool read_operands (DIGIT_ACCESS *first, DIGIT_ACCESS *second, uint32 *trap)
{
if (RA > MAX_DIGITS || RC > MAX_DIGITS) {               /* if the operand digit counts are too large */
    *trap = trap_Invalid_Decimal_Length;                /*   then trap for a count overflow */
    return FALSE;                                       /*     and indicate read failure */
    }

else if (RA == 0 || RC == 0) {                          /* otherwise if there are digits to process */
    *trap = trap_None;                                  /*   then indicate read failure */
    return FALSE;                                       /*     without a trap */
    }

else {                                                      /* otherwise there are digits to process */
    init_decimal (first, Packed, data_checked, RB, RA);     /*   so set up the digit accessors */
    init_decimal (second, Packed, data_checked, RD, RC);    /*     for the decimal operands */

    *trap = read_decimal (first);                       /* read the first decimal operand */

    if (TRACING (cpu_dev, DEB_MOPND))
        fprint_decimal_operand (first, "operand-1");

    if (*trap == trap_None) {                           /* if the first decimal is valid */
        *trap = read_decimal (second);                  /*   then read the second decimal operand */

        if (TRACING (cpu_dev, DEB_MOPND))
            fprint_decimal_operand (second, "operand-2");
        }

    return (*trap == trap_None);                        /* return TRUE if both reads were good */
    }
}


/* Write a decimal operand to memory.

   The packed decimal number specified by the supplied digit accessor is written
   to memory, and the condition code is set according to the value.  The
   accessor is reset before writing, in case it was used to read a decimal value
   earlier (e.g., in-place update is being done).  If operand tracing is
   enabled, the operand value is printed on the trace log before returning.
*/

static void write_operand (DIGIT_ACCESS *operand)
{
mem_reset_byte (&operand->bac);                         /* reset the accessor in case it has been used */
write_decimal (operand, TRUE);                          /*   and write the operand to memory */

if (TRACING (cpu_dev, DEB_MOPND))
    fprint_decimal_operand (operand, "result");

set_cca_decimal (operand);                              /* set CCA on the decimal result */

return;
}


/* Set Condition Code A for a decimal number.

   The condition code in the status register is set to reflect the value passed
   in the supplied digit accessor.
*/

static void set_cca_decimal (DIGIT_ACCESS *dap)
{
if (dap->significant_index == NOT_SET)                  /* if the number has no significant digits */
    SET_CCE;                                            /*   then the value is zero */
else if (dap->sign == Negative)                         /* otherwise if the sign is negative */
    SET_CCL;                                            /*   then the value is less than zero */
else                                                    /* otherwise */
    SET_CCG;                                            /*   the value is greater than zero */

return;
}


/* Decrement the stack and trap if enabled.

   This routine is called with a trap value and either two or three stack
   decrement values.  If no trap is indicated or traps are disabled, the stack
   is decremented as specified.  If two values are specified, indicated by the
   third decrement value being zero, the stack decrement bit is examined to
   select either the first (0) or second (1) decrement value.  If three values
   are specified, then the stack decrement field (two bits) is examined, and the
   field value determines the stack decrement value to use.  For example:

     Parameters   CIR Test   Decrements
     ----------  ----------  ----------
      1, 3, 0      bit 11      -1, -3
      0, 2, 0      bit 11       0, -2
      0, 2, 4    bits 10-11   0, -2, -4

   After the decrement is performed, if a trap is indicated, a microcode abort
   is done.  Otherwise, the overflow register is cleared, and the routine
   returns.
*/

static void decrement_stack (uint32 trap, uint32 count_0, uint32 count_1, uint32 count_2)
{
uint32 count, decrement;

if (trap == trap_None || (STA & STATUS_T) == 0) {       /* if the instruction succeeded or user traps are disabled */
    if (count_2 == 0)                                   /*   then if only two choices are present */
        if (CIR & EIS_SDEC_FLAG)                        /*     then if the S-decrement flag is set */
            decrement = count_1;                        /*       then decrement by the second choice */
        else                                            /*     otherwise */
            decrement = count_0;                        /*       then decrement by the first choice */

    else switch (EIS_SDEC (CIR)) {                      /* otherwise select among the three choices */

        case 0:                                         /* if the S-decrement field is 00 */
            decrement = count_0;                        /*   then select the first choice */
            break;

        case 1:                                         /* if the S-decrement field is 01 */
            decrement = count_1;                        /*   then select the second choice */
            break;

        case 2:                                         /* if the S-decrement field is 10 */
        default:                                        /*   or 11 (invalid) */
            decrement = count_2;                        /*   then select the third choice */
            break;
        }

    if (decrement == 4)                                 /* if four parameters are to be deleted */
        SR = 0;                                         /*   then simply clear the stack counter */

    else for (count = 0; count < decrement; count++)    /* otherwise delete the number */
        cpu_pop ();                                     /*   of items requested */
    }

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 */

return;
}


/* Strip the sign from an overpunched digit.

   If the supplied character includes a valid overpunched sign, it is stripped
   and returned separately, the character is set to the stripped digit, and
   trap_None is returned to indicate success.  If the character 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 (HP_BYTE *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 */
}


/* Format and print a decimal memory operand.

   The decimal operand described by the decimal accessor is sent to the debug
   trace log file.  Operand tracing must be enabled when the routine is called.

   On entry, "op" points at the decimal accessor describing the operand, and
   "label" points to text used to label the operand.

   The operand is printed in this format:

     >>CPU  opnd: 00.045177  000467    source 15,"314159265358979"
                  ~~ ~~~~~~  ~~~~~~    ~~~~~~ ~~ ~~~~~~~~~~~~~~~~~
                  |    |       |         |    |          |
                  |    |       |         |    |          +-- 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_decimal_operand (DIGIT_ACCESS *op, char *label)
{
typedef char * (*OP_PRINT) (uint32 address, uint32 length);

OP_PRINT operand_printer;

if (op->format == Packed)                               /* if this is a packed decimal number */
    operand_printer = fmt_bcd_operand;                  /*   then use the BCD operand printer */
else                                                    /* otherwise */
    operand_printer = fmt_byte_operand;                 /*   use the character operand printer */

hp_trace (&cpu_dev, DEB_MOPND, BOV_FORMAT "  %s %d,\"%s\"\n",
          TO_BANK (op->bac.first_byte_address / 2),
          TO_OFFSET (op->bac.first_byte_address / 2),
          op->bac.first_byte_offset, label, op->digit_count,
          operand_printer (op->bac.first_byte_address, op->digit_count));

return;
}
