/* hp3000_defs.h: HP 3000 simulator general declarations

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

   08-Apr-21    JDB     Expanded bank register widths from 4 to 6 bits
   19-Mar-21    JDB     Added "iso646.h" inclusion to use "not" for "!"
   07-Feb-21    JDB     Changed register macros to use new generic XRDATA macro
   30-Sep-20    JDB     Added HIGH_UPPER_WORD, LOW_UPPER_WORD for EIS
   23-Sep-20    JDB     Added HP_BYTE type for EIS
   17-Aug-20    JDB     Corrected "cputc" definition
   09-Dec-19    JDB     Replaced debugging macros with tracing macros
   28-Mar-19    JDB     Added extensions
   16-Oct-17    JDB     Suppressed logical-not-parentheses warning on clang
   29-Dec-16    JDB     Changed the status mnemonic flag from REG_S to REG_T
   20-Nov-16    JDB     Added mapped memory access classes
   24-Oct-16    JDB     Added half-byte definitions for CIS decoding
   10-Oct-16    JDB     Moved ACCESS_CLASS definition here from hp3000_cpu.h
   03-Sep-16    JDB     Added the STOP_POWER and STOP_ARSINH codes
   13-May-16    JDB     Modified for revised SCP API function parameter types
   21-Mar-16    JDB     Changed uint16 types to HP_WORD
   19-Mar-16    JDB     Added UNDEFs for the additional register macros
   04-Feb-16    JDB     First release version
   11-Dec-12    JDB     Created


   This file provides the general declarations used throughout the HP 3000
   simulator.  It is required by all modules.

   The author gratefully acknowledges the help of Frank McConnell in answering
   questions about the HP 3000.


   -----------------------------------------------------
   Implementation Note -- Compiling the Simulator as C++
   -----------------------------------------------------

   This simulator is written in ISO-standard C, specifically to the ISO/IEC
   9899:1999 standard.  While it might be desirable to compile the simulator
   with a C++ compiler to obtain more careful type checking, the simulator would
   have to be be written in the subset of C that is also valid C++.  Using valid
   C features beyond that subset, as the HP 3000 simulator does, will produce
   C++ compiler errors.

   The standard C features used by the simulator that prevent error-free C++
   compilation are:

    1. Incomplete types.

       In C, mutually recursive type definitions are allowed by the use of
       incomplete type declarations, such as "DEVICE ms_dev;" followed later by
       "DEVICE ms_dev {...};".  Some HP device simulators use this feature to
       place a pointer to the device structure in the "desc" field of an MTAB
       array element, typically when the associated validation or display
       routine handles multiple devices.  As the DEVICE contains a pointer to
       the MTAB array, and an MTAB array element contains a pointer to the
       DEVICE, the definitions are mutually recursive, and incomplete types are
       employed.  C++ does not permit incomplete types.

    2. Implicit conversion of ints to enums.

       In C, enumeration types are compatible with integer types, and its
       members are constants having type "int".  As such, they are semantically
       equivalent to and may be used interchangeably with integers.  For the
       developer, though, C enumerations have some advantages.  In particular,
       the compiler may check a "switch" statement to ensure that all of the
       enumeration cases are covered.  Also, a mathematical set may be modeled
       by an enumeration type with disjoint enumerator values, with the bitwise
       integer OR and AND operators modeling the set union and intersection
       operations.  The latter has direct support in the "gdb" debugger, which
       will display an enumerated type value as a union of the various
       enumerators.  The HP simulator makes extensive use of both features to
       model hardware signal buses (e.g., INBOUND_SET, OUTBOUND_SET) and so
       performs bitwise integer operations on the enumerations to model signal
       assertion and denial.  In C++, implicit conversion from enumerations to
       integers is allowed, but conversion from integers to enumerations is
       illegal without explicit casts.  Therefore, the idiom employed by the
       simulator to assert a signal (e.g., "outbound_signals |= INTREQ") is
       rejected by the C++ compiler.

    3. Implicit increment operations on enums.

       Because enums are compatible with integers in C, no special enumerator
       increment operator is provided.  To cycle through the range of an
       enumeration type, e.g. in a "for" statement, the standard integer
       increment operator, "++", is used.  In C++, the "++" operator must be
       overloaded with a version specific to the enumeration type; applying the
       integer "++" to an enumeration is illegal.

    4. Use of C++ keywords as variable names.

       C++ reserves a number of additional keywords beyond those reserved by C.
       Use of any of these keywords as a variable or type name is legal C but
       illegal C++.  The HP simulator uses variables named "class" and
       "operator", which are keywords in C++.

   The HP simulator will compile cleanly with a compiler implementing the 1999 C
   standard.  Compilation as C++ is not a goal of the simulator and cannot work,
   given the incompatibilities listed above.
*/



#include <iso646.h>

#include "sim_rev.h"
#include "sim_defs.h"
#include "sim_extension.h"



/* The following pragmas quell clang and Microsoft Visual C++ warnings that are
   on by default but should not be, in my opinion.  They warn about the use of
   perfectly valid code and require the addition of redundant parentheses and
   braces to silence them.  Rather than clutter up the code with scores of extra
   symbols that, in my view, make the code harder to read and maintain, I elect
   to suppress these warnings.

   VC++ 2008 warning descriptions:

    - 4114: "same type qualifier used more than once" [legal per C99]

    - 4554: "check operator precedence for possible error; use parentheses to
            clarify precedence"

    - 4996: "function was declared deprecated"
*/

#if defined (__clang__)

  #pragma clang diagnostic ignored "-Wlogical-not-parentheses"
  #pragma clang diagnostic ignored "-Wlogical-op-parentheses"
  #pragma clang diagnostic ignored "-Wbitwise-op-parentheses"
  #pragma clang diagnostic ignored "-Wshift-op-parentheses"
  #pragma clang diagnostic ignored "-Wdangling-else"

#elif defined (_MSC_VER)

  #pragma warning (disable: 4114 4554 4996)

#endif


/* Device register display mode flags */

#define REG_X               REG_VMIO                    /* permit symbolic display overrides */

#define REG_A               (1u << REG_V_UF + 0)        /* default format is -A (one ASCII character) */
#define REG_C               (1u << REG_V_UF + 1)        /* default format is -C (two ASCII characters) */
#define REG_M               (1u << REG_V_UF + 2)        /* default format is -M (mnemonic) */
#define REG_T               (1u << REG_V_UF + 3)        /* default format is -T (status mnemonic) */


/* Register macros.

   These additional register definition macros are used to define:

     FBDATA -- a one-bit flag in an arrayed register
     YRDATA -- a binary register
     SVDATA -- a register only used for SAVE/RESTORE

   The FBDATA macro defines a flag that is replicated in the same bit position
   in each element of an array.  The YRDATA macro extends the functionality of
   the ORDATA, DRDATA, and HRDATA macros to registers with binary (base 2)
   representation.  The SVDATA macro is used solely to SAVE data so that it may
   be RESTOREd later.
*/

/*              Macro                    name   loc    radix  width  offset  depth       size          stride     */
/*      ----------------------           ----  ------  -----  -----  ------  -----  --------------  ------------- */
#define FBDATA(nm,loc,ofs,dep)  XRDATA   (nm,  (loc),    2,     1,   (ofs),  (dep), sizeof  *(loc), sizeof  *(loc))
#define YRDATA(nm,loc,wid)      GRDATA   (nm,  (loc),    2,   (wid),   0)
#define SVDATA(nm,loc)          SAVEDATA (nm,  (loc))


/* Tracing and console output.

   "tprintf" is used to write tracing messages.  It does an "fprintf" to the
   debug output stream if the stream is open and the trace "flag" is currently
   enabled in device "dev".  Otherwise, it's a NOP.  "..." is the format string
   and associated values.

   "tpprintf" is identical to "tprintf", except that a device pointer is passed
   instead of a device structure.

   "TRACING" and "TRACINGP" implement the test conditions for device and device
   pointer tracing, respectively.  They are used explicitly only when several
   trace statements employing the same flag are required, and it is desirable to
   avoid repeating the stream and flag test for each one.

   "cprintf", "cputs", and "cputc" are used to write messages to the console
   and, if console logging is enabled, to the log output stream.  They do
   "(f)printf", "fputs", or "(f)putc", respectively.  "..." is the format string
   and associated values, "str" is the string to write, and "ch" is the
   character to write.


   Implementation notes:

    1. The "cputs" macro uses "fputs" for both console and log file output
       because "puts" appends a newline, whereas "fputs" does not.
*/

#define TRACING(d,f)        (sim_deb != NULL && ((d).dctrl & (f)))

#define TRACINGP(d,f)       (sim_deb != NULL && ((d)->dctrl & (f)))

#define tprintf(dev, flag, ...) \
          if (TRACING (dev, flag)) \
              hp_trace (&(dev), (flag), __VA_ARGS__); \
          else \
              (void) 0

#define tpprintf(dptr, flag, ...) \
          if (TRACINGP (dptr, flag)) \
              hp_trace ((dptr), (flag), __VA_ARGS__); \
          else \
              (void) 0

#define cprintf(...) \
          do { \
              printf (__VA_ARGS__); \
              if (sim_log) \
                  fprintf (sim_log, __VA_ARGS__); \
              } \
          while (0)

#define cputs(str) \
          do { \
              fputs (str, stdout); \
              if (sim_log) \
                  fputs (str, sim_log); \
              } \
          while (0)

#define cputc(ch) \
          do { \
              putchar (ch); \
              if (sim_log) \
                  fputc (ch, sim_log); \
              } \
          while (0)


/* Simulation stop codes.

   These VM-specific status codes stop the simulator.  The "sim_stop_messages"
   array in "hp3000_sys.c" contains the message strings that correspond
   one-for-one with the stop codes.


   Implementation notes:

    1. Codes before STOP_RERUN cause the instruction to be rerun, so P is backed
       up twice.  For codes after, P points to the next instruction to be
       executed (which is the current instruction for an infinite loop stop).
*/

#define STOP_SYSHALT        1                   /* system halt */
#define STOP_UNIMPL         2                   /* unimplemented instruction stop */
#define STOP_UNDEF          3                   /* undefined instruction stop */
#define STOP_PAUS           4                   /* PAUS instruction stop */

#define STOP_RERUN          4                   /* stops above here cause the instruction to be re-run */

#define STOP_HALT           5                   /* programmed halt */
#define STOP_BRKPNT         6                   /* breakpoint */
#define STOP_INFLOOP        7                   /* infinite loop stop */
#define STOP_CLOAD          8                   /* cold load complete */
#define STOP_CDUMP          9                   /* cold dump complete */
#define STOP_ARSINH         10                  /* auto-restart inhibited */
#define STOP_POWER          11                  /* power is off */


/* Modifier validation identifiers */

#define MTAB_XDV            (MTAB_XTD | MTAB_VDV)
#define MTAB_XUN            (MTAB_XTD | MTAB_VUN)

#define VAL_DEVNO           0                   /* validate DEVNO=0-127      */
#define VAL_INTMASK         1                   /* validate INTMASK=0-15/E/D */
#define VAL_INTPRI          2                   /* validate INTPRI=0-31      */
#define VAL_SRNO            3                   /* validate SRNO=0-15        */


/* I/O event timing.

   I/O events are scheduled for future service by specifying the desired delay
   in units of event ticks.  Typically, one event tick represents the execution
   of one CPU instruction, and this is the way event ticks are defined in the
   current simulator implementation.  However, while the average execution time
   of a typical instruction mix on a Series II is given as 2.57 microseconds,
   actual instruction times vary greatly, due to the presence of block move and
   loop instructions.  Variations of an order of magnitude are common, and two
   orders or more are possible for longer blocks.

   To accommodate possible future variable instruction timing, I/O service
   activation times must not assume a constant 2.57 microseconds per event tick.
   Delays should be defined in terms of the "uS" (microseconds), "mS"
   (milliseconds), and "S" (seconds) macros below.
*/

#define USEC_PER_EVENT      2.57                /* average CPU instruction time in microseconds */

#define uS(t)               (uint32) ((t) > USEC_PER_EVENT ? (t) / USEC_PER_EVENT + 0.5 : 1)
#define mS(t)               (uint32) (((t) * 1000.0)    / USEC_PER_EVENT + 0.5)
#define S(t)                (uint32) (((t) * 1000000.0) / USEC_PER_EVENT + 0.5)


/* Architectural constants.

   These macros specify the width, sign location, value mask, and minimum and
   maximum signed and unsigned values for the data sizes supported by the
   simulator.  In addition, masks for 16-bit and 32-bit overflow are defined (an
   overflow is indicated if the masked bits are not all ones or all zeros).

   The HP_WORD type is used to declare variables that represent 16-bit registers
   or buses in hardware.


   Implementation notes:

    1. The HP_WORD type is a 32-bit unsigned type, instead of the more logical
       16-bit unsigned type.  There are two reasons for this.  First, SCP
       requires that scalars referenced by REG (register) entries be 32 bits in
       size.  Second, IA-32 processors execute instructions with 32-bit operands
       much faster than those with 16-bit operands.

       Using 16-bit operands omits the masking required for 32-bit values.  For
       example, the code generated for the following operations is as follows:

         uint16 a, b, c;
         a = b + c & 0xFFFF;

            movzwl  _b, %eax
            addw    _c, %ax
            movw    %ax, _a

         uint32 x, y, z;
         x = y + z & 0xFFFF;

            movl    _z, %eax
            addl    _y, %eax
            andl    $65535, %eax
            movl    %eax, _x

       However, the first case uses operand override prefixes, which require
       substantially more time to decode (6 clock cycles vs. 1 clock cycle).
       This time outweighs the additional 32-bit AND instruction, which executes
       in 1 clock cycle.

       On an Intel Core 2 Duo processor, defining HP_WORD as uint16 causes the
       HP 3000 memory diagnostic to run about 10% slower.
*/

typedef uint32              HP_WORD;                    /* HP 16-bit data word representation */
typedef uint8               HP_BYTE;                    /* HP 8-bit data word representation */

#define R_MASK              0177777u                    /* 16-bit register mask */

#define D4_WIDTH            4                           /* 4-bit data bit width */
#define D4_MASK             0017u                       /* 4-bit data mask */

#define D8_WIDTH            8                           /* 8-bit data bit width */
#define D8_MASK             0377u                       /* 8-bit data mask */
#define D8_UMAX             0377u                       /* 8-bit unsigned maximum value */
#define D8_SMAX             0177u                       /* 8-bit signed maximum value */
#define D8_SMIN             0200u                       /* 8-bit signed minimum value */
#define D8_SIGN             0200u                       /* 8-bit sign */

#define D16_WIDTH           16                          /* 16-bit data bit width */
#define D16_MASK            0177777u                    /* 16-bit data mask */
#define D16_UMAX            0177777u                    /* 16-bit unsigned maximum value */
#define D16_SMAX            0077777u                    /* 16-bit signed maximum value */
#define D16_SMIN            0100000u                    /* 16-bit signed minimum value */
#define D16_SIGN            0100000u                    /* 16-bit sign */

#define D32_WIDTH           32                          /* 32-bit data bit width */
#define D32_MASK            037777777777u               /* 32-bit data mask */
#define D32_UMAX            037777777777u               /* 32-bit unsigned maximum value */
#define D32_SMAX            017777777777u               /* 32-bit signed maximum value */
#define D32_SMIN            020000000000u               /* 32-bit signed minimum value */
#define D32_SIGN            020000000000u               /* 32-bit sign */

#define D48_WIDTH           48                          /* 48-bit data bit width */
#define D48_MASK            07777777777777777uL         /* 48-bit data mask */
#define D48_UMAX            07777777777777777uL         /* 48-bit unsigned maximum value */
#define D48_SMAX            03777777777777777uL         /* 48-bit signed maximum value */
#define D48_SMIN            04000000000000000uL         /* 48-bit signed minimum value */
#define D48_SIGN            04000000000000000uL         /* 48-bit sign */

#define D64_WIDTH           64                          /* 64-bit data bit width */
#define D64_MASK            01777777777777777777777uL   /* 64-bit data mask */
#define D64_UMAX            01777777777777777777777uL   /* 64-bit unsigned maximum value */
#define D64_SMAX            00777777777777777777777uL   /* 64-bit signed maximum value */
#define D64_SMIN            01000000000000000000000uL   /* 64-bit signed minimum value */
#define D64_SIGN            01000000000000000000000uL   /* 64-bit sign */

#define S16_OVFL_MASK       ((uint32) D16_UMAX << D16_WIDTH | \
                              D16_SIGN)                 /* 16-bit signed overflow mask */

#define S32_OVFL_MASK       ((t_uint64) D32_UMAX << D32_WIDTH | \
                              D32_SIGN)                 /* 32-bit signed overflow mask */


/* Memory constants */

#define LA_WIDTH            16                              /* logical address bit width */
#define LA_MASK             ((1u << LA_WIDTH) - 1)          /* logical address mask (2 ** 16 - 1) */
#define LA_MAX              ((1u << LA_WIDTH) - 1)          /* logical address maximum (2 ** 16 - 1) */

#define BA_WIDTH            6                               /* 6-bit bank address bit width */
#define BA_MASK             mem_bank_mask                   /* bank address mask (varies with CPU model) */
#define BA_MAX              mem_bank_mask                   /* bank address maximum (varies with CPU model) */

#define PA_WIDTH            (LA_WIDTH + BA_WIDTH)           /* physical address bit width */
#define PA_MASK             ((1u << PA_WIDTH) - 1)          /* physical address mask (2 ** 22 - 1) */
#define PA_MAX              ((1u << PA_WIDTH) - 1)          /* physical address maximum (2 ** 22 - 1) */

#define DV_WIDTH            16                              /* data value bit width */
#define DV_MASK             ((1u << DV_WIDTH) - 1)          /* data value mask (2 ** 16 - 1) */
#define DV_SIGN             ( 1u << (DV_WIDTH - 1))         /* data value sign (2 ** 15) */
#define DV_UMAX             ((1u << DV_WIDTH) - 1)          /* data value unsigned maximum (2 ** 16 - 1) */
#define DV_SMAX             ((1u << (DV_WIDTH - 1)) - 1)    /* data value signed maximum  (2 ** 15 - 1) */


/* Memory address macros.

   These macros convert between logical and physical addresses.  The functions
   provided are:

     - TO_PA     -- merge a bank number and offset into a physical address
     - TO_BANK   -- extract the bank number part of a physical address
     - TO_OFFSET -- extract the offset part of a physical address


   Implementation notes:

    1. The TO_PA offset parameter is not masked to 16 bits, as this value is
       almost always derived from a value that is inherently 16 bits in size.
       In the few cases where it is not, explicit masking is required.

    2. The size of the bank number field is dependent on the CPU model selected.
       Bank registers are two bits wide for the Series II and four bits wide for
       the Series III, although MPE-V/R supported six-bit bank register widths.
*/

#define TO_PA(b,o)          (((uint32) (b) & mem_bank_mask) << LA_WIDTH | (uint32) (o))
#define TO_BANK(p)          ((p) >> LA_WIDTH & mem_bank_mask)
#define TO_OFFSET(p)        ((p) & LA_MASK)


/* Memory access classifications.

   The access classification determines which bank register is used with the
   supplied offset to access memory, whether or not the access is bounds
   checked, and whether or not the access is mapped to the TOS registers if the
   address is between SM and SM + SR.

   Bounds checking is optionally performed on program (including instruction
   fetch), data, and stack accesses when not in privileged mode.  Absolute
   addresses are always accessed in privileged mode, so bounds checking is never
   performed.

   If the memory address lies between SM and SM + SR within the stack bank, then
   access to the TOS registers may be substituted for access to memory.
   Register mapping is always performed on stack accesses, optionally performed
   on absolute and data accesses, and is never performed on program (and fetch)
   accesses.

   To summarize bounds checking and TOS register mapping:

               Bounds    TOS
     Access    Check   Mapping
     --------  ------  -------
     absolute    N        O
     fetch       O        N
     program     O        N
     data        O        O
     stack       O        Y
     dma         N        N


   Implementation notes:

    1. The enumeration values must be ordered such that the "checked" classes
       are odd and differ from their corresponding "unchecked" classes only in
       the LSBs.

    2. There is no hardware DMA bank register.  The "dma" class exists only to
       differentiate DMA memory accesses from CPU memory accesses when tracing.
*/

typedef enum {
    absolute,                                   /* absolute bank */
    absolute_mapped,                            /* absolute bank, TOS registers mapped */
    fetch,                                      /* program bank, instruction fetch */
    fetch_checked,                              /* program bank, instruction fetch, bounds checked */
    program,                                    /* program bank, data access */
    program_checked,                            /* program bank, data access, bounds checked */
    data,                                       /* data bank, data access */
    data_checked,                               /* data bank, data access, bounds checked */
    data_mapped,                                /* data bank, data or TOS register access */
    data_mapped_checked,                        /* data bank, data or TOS register access, bounds checked */
    stack,                                      /* stack bank, data or TOS register access */
    stack_checked,                              /* stack bank, data or TOS register access, bounds checked */
    dma                                         /* DMA bank */
    } ACCESS_CLASS;

#define UNCHECKED(c)        ((c) & ~1u)         /* reclassify a request as unchecked */
#define INVERT_CHECK(c)     ((c) ^ 1u)          /* convert checked to unchecked and vice versa */


/* Portable conversions.

   SIMH is written with the assumption that the defined-size types (e.g.,
   uint16) are at least the required number of bits but may be larger.
   Conversions that otherwise would make inherent size assumptions must instead
   be coded explicitly.  For example, doing:

     negative_value_32 = (int32) negative_value_16;

   ...will not guarantee that bits 0-15 of "negative_value_32" are ones, whereas
   the supplied sign-extension macro will.

   The conversions available are:

     - SEXT8  -- int8 sign-extended to int32
     - SEXT16 -- int16 sign-extended to int32
     - NEG16  -- int8 negated
     - NEG16  -- int16 negated
     - NEG32  -- int32 negated
     - INT16  -- uint16 to int16
     - INT32  -- uint32 to int32


   Implementation notes:

    1. The routines assume that 16-bit values are masked to exactly 16 bits
       before invoking.
*/

#define SEXT8(x)        (int32) ((x) & D8_SIGN  ? (x) | ~D8_MASK  : (x))
#define SEXT16(x)       (int32) ((x) & D16_SIGN ? (x) | ~D16_MASK : (x))

#define NEG8(x)         ((~(x) + 1) & D8_MASK)
#define NEG16(x)        ((~(x) + 1) & D16_MASK)
#define NEG32(x)        ((~(x) + 1) & D32_MASK)

#define INT16(u)        ((u) > D16_SMAX ? (-(int16) (D16_UMAX - (u)) - 1) : (int16) (u))
#define INT32(u)        ((u) > D32_SMAX ? (-(int32) (D32_UMAX - (u)) - 1) : (int32) (u))


/* Half-byte accessors */

#define UPPER_HALF(b)       ((b) >> D4_WIDTH & D4_MASK)
#define LOWER_HALF(b)       ((b) &  D4_MASK)
#define TO_BYTE(u,l)        (HP_BYTE) (((u) & D4_MASK) << D4_WIDTH | (l) & D4_MASK)


/* Byte accessors.

   These macros extract the upper and lower bytes from a word and form a word
   from upper and lower bytes.  Replacement of a byte within a word is also
   provided, as is an enumeration type that defines byte selection.

   The accessors are:

     - UPPER_BYTE     -- return the byte from the upper position of a word value
     - LOWER_BYTE     -- return the byte from the lower position of a word value
     - TO_WORD        -- return a word with the specified upper and lower bytes

     - REPLACE_UPPER  -- replace the upper byte of the word value
     - REPLACE_LOWER  -- replace the lower byte of the word value

*/

typedef enum {
    upper,                                      /* upper byte selected */
    lower                                       /* lower byte selected */
    } BYTE_SELECTOR;

#define UPPER_BYTE(w)       (HP_BYTE) ((w) >> D8_WIDTH & D8_MASK)
#define LOWER_BYTE(w)       (HP_BYTE) ((w) &  D8_MASK)
#define TO_WORD(u,l)        (HP_WORD) (((u) & D8_MASK) << D8_WIDTH | (l) & D8_MASK)

#define REPLACE_UPPER(w,b)  ((w) & D8_MASK | ((b) & D8_MASK) << D8_WIDTH)
#define REPLACE_LOWER(w,b)  ((w) & D8_MASK << D8_WIDTH | (b) & D8_MASK)


/* Double-word accessors */

#define UPPER_WORD(d)       (HP_WORD) ((d) >> D16_WIDTH & D16_MASK)
#define LOWER_WORD(d)       (HP_WORD) ((d) &  D16_MASK)
#define TO_DWORD(u,l)       ((uint32) (u) << D16_WIDTH | (l))


/* Quad-word accessors */

#define UPPER_DWORD(q)      (uint32) ((q) >> D32_WIDTH & D32_MASK)
#define LOWER_DWORD(q)      (uint32) ((q) &  D32_MASK)

#define HIGH_UPPER_WORD(q)  (HP_WORD) ((q) >> D48_WIDTH & D16_MASK)
#define LOW_UPPER_WORD(q)   (HP_WORD) ((q) >> D32_WIDTH & D16_MASK)
#define TO_QWORD(h,a,b,l)   ((t_uint64) (h) << D48_WIDTH | (t_uint64) (a) << D32_WIDTH \
                              | (uint32) (b) << D16_WIDTH | (l))


/* Flip-flops */

typedef enum {
    CLEAR = 0,                                  /* the flip-flop is clear */
    SET   = 1                                   /* the flip-flop is set */
    } FLIP_FLOP;

#define TOGGLE(ff)          ff = (FLIP_FLOP) (ff ^ 1)   /* toggle a flip-flop variable */

#define D_FF(b)             (FLIP_FLOP) ((b) != 0)      /* use a Boolean expression for a D flip-flop */


/* Bitset formatting.

   See the comments at the "fmt_bitset" function (hp3000_sys.c) for details of
   the specification of bitset names and format structures.
*/

typedef enum {                                  /* direction of interpretation */
    msb_first,                                  /*   left-to-right */
    lsb_first                                   /*   right-to-left */
    } BITSET_DIRECTION;

typedef enum {                                  /* alternate names */
    no_alt,                                     /*   no alternates are present in the name array */
    has_alt                                     /*   the name array contains alternates */
    } BITSET_ALTERNATE;

typedef enum {                                  /* trailing separator */
    no_bar,                                     /*   omit a trailing separator */
    append_bar                                  /*   append a trailing separator */
    } BITSET_BAR;

typedef const char *const BITSET_NAME;          /* a bit name string pointer */

typedef struct {                                /* bit set format descriptor */
    uint32            name_count;               /*   count of bit names */
    BITSET_NAME       *names;                   /*   pointer to an array of bit names */
    uint32            offset;                   /*   offset from LSB to first bit */
    BITSET_DIRECTION  direction;                /*   direction of interpretation */
    BITSET_ALTERNATE  alternate;                /*   alternate interpretations presence */
    BITSET_BAR        bar;                      /*   trailing separator choice */
    } BITSET_FORMAT;

/* Bitset format specifier initialization */

#define FMT_INIT(names,offset,dir,alt,bar) \
          sizeof (names) / sizeof (names) [0], \
          (names), (offset), (dir), (alt), (bar)


/* System interface global data structures */

extern const HP_WORD odd_parity [256];          /* a table of parity bits for odd parity */

extern const BITSET_FORMAT inbound_format;      /* the inbound signal format structure */
extern const BITSET_FORMAT outbound_format;     /* the outbound signal format structure */


/* System interface global SCP support routines declared in scp.h

extern t_stat sim_load   (FILE *fptr,  char   *cptr, char    *fnam, int     flag);
extern t_stat fprint_sym (FILE *ofile, t_addr addr,  t_value *val,  UNIT    *uptr, int32 sw);
extern t_stat parse_sym  (char *cptr,  t_addr addr,  UNIT    *uptr, t_value *val,  int32 sw);
*/

/* System interface global SCP support routines */

extern t_stat hp_set_dib  (UNIT *uptr, int32 code,  char  *cptr, void *desc);
extern t_stat hp_show_dib (FILE *st,   UNIT  *uptr, int32 code,  void *desc);


/* System interface global utility routines */

extern t_stat fprint_cpu  (FILE *ofile, t_value *val, uint32 radix, int32  switches);
extern uint32 fprint_edit (FILE *ofile, t_value *val, uint32 radix, uint32 byte_address);

extern const char *fmt_status (uint32 status);
extern const char *fmt_char   (uint32 charval);
extern const char *fmt_bitset (uint32 bitset, const BITSET_FORMAT bitfmt);

extern void   hp_trace           (DEVICE *dptr, uint32 flag, ...);
extern t_bool hp_device_conflict (void);
