text: /* IBM_PROLOG_BEGIN_TAG                                                   */
  /* This is an automatically generated prolog.                             */
  /*                                                                        */
  /* $Source: src/usr/hwpf/hwp/build_winkle_images/p8_slw_build/pore_inline_assembler.c $ */
  /*                                                                        */
  /* OpenPOWER HostBoot Project                                             */
  /*                                                                        */
  /* COPYRIGHT International Business Machines Corp. 2012,2014              */
  /*                                                                        */
  /* Licensed under the Apache License, Version 2.0 (the "License");        */
  /* you may not use this file except in compliance with the License.       */
  /* You may obtain a copy of the License at                                */
  /*                                                                        */
  /*     http://www.apache.org/licenses/LICENSE-2.0                         */
  /*                                                                        */
  /* Unless required by applicable law or agreed to in writing, software    */
  /* distributed under the License is distributed on an "AS IS" BASIS,      */
  /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
  /* implied. See the License for the specific language governing           */
  /* permissions and limitations under the License.                         */
  /*                                                                        */
  /* IBM_PROLOG_END_TAG                                                     */
  // $Id: pore_inline_assembler.c,v 1.22 2013/12/11 00:11:14 bcbrock Exp $
  // $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/pore_inline_assembler.c,v $
  //-----------------------------------------------------------------------------
  // *! (C) Copyright International Business Machines Corp. 2013
  // *! All Rights Reserved -- Property of IBM
  // *! *** IBM Confidential ***
  //-----------------------------------------------------------------------------
  
  // ** WARNING : This file is maintained as part of the OCC firmware.  Do **
  // ** not edit this file in the PMX area or the hardware procedure area  **
  // ** as any changes will be lost.                                       **
  
  /// \file pore_inline_assembler.c
  /// \brief Inline PGAS assembler for PgP/Stage1 PORE
  ///
  /// \page pore_inline_assembler PORE Inline Assembler and Disassembler
  ///
  /// Several procedures targeting the PORE engine require inline assembly and
  /// disassembly of PORE code, that is, they require that PORE instructions be
  /// assembled/disassembled directly into/from a host memory buffer. This page
  /// describes these facilities.  The APIs described here are implemented in
  /// the files pore_inline.h, pore_inline_assembler.c and
  /// pore_inline_disassembler.c.  Both the inline assembelr and disassembler
  /// conform to the PGAS assembly format for PORE.
  ///
  /// Both inline assembly and disassembly make use of a PoreInlineContext
  /// structure. This structure represents the state of a memory area being
  /// targeted for inline assembly and disassembly.  The context is initialized
  /// with the pore_inline_context_create() API, and a pointer to an instance of
  /// this structure appears as the first argument of all assembler/disassembler
  /// APIs. As assembly/disassembly progresses the PoreInlineContext keeps
  /// track of how much host memory area has been filled by assembled code or
  /// scanned by the disassebler.
  ///
  /// Assembler/disassembler APIs are predicates that return 0 for success and a
  /// non-zero error code for failure.  In the event of failure, the error code
  /// (a small integer) is also stored in the \a error field of the context
  /// structure.  String forms of the error codes are also available in the
  /// global array pore_inline_error_strings[].
  ///
  /// The assembler always produces PORE code in the PORE-native big-endian
  /// format.  Likewise, the diassembler assumes the host memory to be
  /// disassembled contains PORE code in big-endian format.
  ///
  /// \section Initialization
  ///
  /// Before invoking inline assembly/disassembly APIs, an instance of a
  /// PoreInlineContext structure must be initialized using the
  /// pore_inline_context_create() API.  For assembly, the context describes the
  /// host memory buffer that will contain the assembled code.  For disassembly,
  /// the context describes the host memory area that contains the code to be
  /// disassembled. Full documentation is available for
  /// pore_inline_context_create(), including documentation for options that
  /// control assembly and disassembly.  The implementation also provides a
  /// 'copy operator' for the context, pore_inline_context_copy().
  ///
  /// An example of initializing a context for inline assembly with parity
  /// checking appears below.
  ///
  /// \code 
  ///
  /// PoreInlineContext ctx;
  /// uint32_t buf[BUFSIZE];
  ///
  /// rc = pore_inline_context_create(&ctx, buf, BUFSIZE * 4, 0,
  ///                                 PORE_INLINE_CHECK_PARITY);
  /// if (rc) . . . Handle Error
  ///
  /// \endcode
  ///
  /// Applications that reuse the same memory buffer for assembling and
  /// processing multiple PORE programs can 'reset' the context between uses by
  /// using the pore_inline_context_reset() API.  pore_inline_context_reset()
  /// resets the location counter and memory extent to their initial (creation)
  /// values, and the context error code is cleared.  Any options specified at
  /// creation remain as they were.
  ///
  /// \section Assembler
  ///
  /// The inline assembler implements each PORE/PGAS instruction as individual
  /// function calls.  The APIs are consistently named \c pore_\<OPCODE\>, where
  /// \c \<OPCODE\> is a PGAS mnemonic in upper case.  The arguments to each
  /// opcode appear in the same order that they appear in the source-level
  /// assembler, with appropriate C-language types. The supported opcode APIs
  /// are defined in pore_inline.h
  /// 
  /// Since the PORE instruction APIs are effectivly predicates, linear code
  /// sequences are easily assembled using the C-language logical OR construct.
  /// Any non-0 return code will immediatly break the sequence and set the
  /// expression value to 1.  The failure code can then be recovered from the \a
  /// error field of the context.  This coding technique is illustrated in the
  /// following example of assembling a memory-memory copy sequence.
  ///
  /// \code 
  ///
  /// PoreInlineContext ctx;
  /// int error;
  ///
  /// . . . // Initialize context
  ///
  /// error =
  ///     pore_LD(&ctx, D0, 0, A0) ||
  ///     pore_STD(&ctx, D0, 0, A1);
  ///
  /// if (error) <. . . Handle error based on ctx.error>
  ///
  /// \endcode
  ///
  /// The above example generates code equivalent to
  ///
  /// \code
  ///
  ///         ld      D0, 0, A0
  ///         std     D0, 0, A1
  ///
  /// \endcode
  ///
  /// Again, if an error were to occur during assembly, inline assembly would
  /// stop (and the logical OR would terminate) at the point of failure. In
  /// particular, the inline assembler will never allow assembled code to exceed
  /// the bounds of the memory area defined by the initial call of
  /// pore_inline_context_create() that defines the assembler memory space.
  ///
  ///
  /// \subsection Register Names and Other Mnemonics
  ///
  /// The header file pore_inline.h defines macros for the register mnemonics.
  ///
  /// - D0, D1 : 64-bit data registers
  /// - A0, A1 : 32-bit address registers
  /// - P0, P1 : 7-bit Pervasive chiplet id registers
  /// - CTR : 24-bit ounter register
  /// - PC : 48-bit Program Counter
  /// - ETR : 64-bit EXE-Trigger Register (Low-order 32 bits are writable)
  /// - EMR : The Error Mask Register
  /// - IFR : ID/Flags Register
  /// - SPRG0 : 32-bit Special-Purpose General Register 0
  ///
  /// Mnemonics for the condition code bits are also defined by pore_inline.h
  /// using the PGAS mnemonics.
  ///
  ///
  /// \subsection Assembling Branches
  ///
  /// Opcodes that implement relative branches require that the branch target be
  /// specified as a <em> location counter </em>. Once initialized, the current
  /// location counter is available as the \a lc field of the PoreInlineContext
  /// object controlling the assembly.  The \a lc field is the only field
  /// (besides the error code held in the \a error field) that application code
  /// should ever reference. The inline assembler also provides a typedef
  /// PoreInlineLocation to use for location counters, as well as the macro
  /// PORE_LOCATION() to define a location variable inline with the code flow.
  /// 
  /// \subsubsection Backward Branches
  ///
  /// Backward branches are straightforward.  For example, the memory-memory
  /// copy example from earlier can be converted into a loop as shown below.  The
  /// \a loop_target variable is initialized with the location counter of the
  /// first instruction of the loop.  The final instruction of the loop then
  /// branches back to the \a loop_target.
  ///
  /// \code
  ///
  /// PoreInlineContext ctx;
  /// PoreInlineLocation loop_target = 0; // See ** below the example
  /// int error;
  ///
  /// . . . // Initialize context
  ///
  /// error =
  ///     PORE_LOCATION(&ctx, loop_target) ||
  ///     pore_LD(&ctx, D0, 0, A0)         ||
  ///     pore_STD(&ctx, D0, 0, A1)      	 ||
  ///     pore_ADDS(&ctx, A0, A0, 8)     	 ||
  ///     pore_ADDS(&ctx, A1, A1, 8)     	 ||
  ///     pore_LOOP(&ctx, loop_target);
  ///
  /// if (error) <. . . Handle error based on ctx.error>
  ///
  /// \endcode
  ///
  /// The above inline assembler sequence is equivalent to the PGAS code
  /// sequence:
  ///
  /// \code
  ///
  /// loop_target:
  ///        ld      D0, 0, A0
  ///        std     D0, 0, A1
  ///        adds    A0, A0, 8
  ///        adds    A1, A1, 8
  ///        loop    loop_target
  ///
  /// \endcode
  ///
  /// ** Location counters used as loop targets may need to be initialized,
  /// otherwise the compiler may issue a warning that the variable "may be used
  /// uninitialized", although in well-written code this would never happen.
  ///
  ///
  /// \subsubsection Forward Branches
  ///
  /// Forward branches are more complex.  Since the target location counter is
  /// not known until the target has been assembled, the inline assembler
  /// provides the API pore_inline_branch_fixup() to fix up forward branches
  /// once the actual target is known.  This is illustrated in the simple code
  /// sequence below, where an instruction is conditionally skipped.
  ///
  /// \code
  ///
  /// PoreInlineContext ctx;
  /// PoreInlineLocation source = 0, target = 0;
  /// int error, rc;
  ///
  /// . . . // Initialize context
  ///
  /// error = 
  ///     PORE_LOCATION(&ctx, source)  ||
  ///     pore_BRANZ(&ctx, D0, source) ||
  ///     pore_ADDS(&ctx, D1, D1, 1)   ||
  ///     PORE_LOCATION(&ctx, target)  ||
  ///     pore_LD(&ctx, D0, 0, A0);
  ///
  /// if (error) <. . . Handle assembly error based on ctx->error>
  /// rc = pore_inline_branch_fixup(&ctx, source, target);
  /// if (rc) <. . . Handle branch fixup error>
  ///
  /// \endcode
  ///
  /// In the above code, the branch instruction is initially assembled as a
  /// branch-to-self - the recommended idiom for forward branch source
  /// instructions.  Once the entire sequence has been assembled,
  /// pore_inline_branch_fixup() reassembles the \c source instruction as a
  /// branch to the \c target instruction. The above instruction sequence is
  /// equivalent to the PGAS code below:
  ///
  /// \code
  ///
  /// source:
  ///         branz   D0, target
  ///         adds    D1, D1, 1
  /// target:
  ///         ld      D0, 0, A0
  ///
  /// \endcode
  ///
  ///
  /// \subsubsection Absolute Branches
  ///
  /// It is unlikely that a typical application of the PORE inline assembler
  /// would ever need to include an absolute branch, since the branch target in
  /// this case is a fixed absolute address that must be known at assembly
  /// time. However the inline assembler does provide the pore_BRAIA() API for
  /// this purpose.  This opcode requires a 16-bit address space constant and a
  /// 32-bit absoulte address (offset) within the memory space to specify the
  /// branch. 
  ///
  ///
  /// \section Disassembly
  ///
  /// Inline disassembly is implemented by a single API,
  /// pore_inline_disassemble(). The idea is similar to assembly: A host memory
  /// context containing PORE code (or data) is described by a PoreInlineContext
  /// structure.  Each call of pore_inline_disassemble() disassembles the next
  /// instruction (or datum) in the context into a PoreInlineDisassembly
  /// structure provided by the caller.  The disassembly object contains both
  /// binary and string forms of the disassembled instruction (or data). The
  /// next call of pore_inline_disassemble() proceses the next instruction (or
  /// datum) and so on.
  ///
  /// \subsection Text (Code) Disassembly
  ///
  /// In the example below the inline disassembler is used to completely
  /// disassemble a memory area containing text (code) to \a stdout until an
  /// error occurs, assumed to be either due to disassembling the entire memory
  /// area or finding an illegal instruction.
  ///
  /// \code
  ///
  /// PoreInlineContext ctx;
  /// PoreInlineDisassembly dis;
  ///
  /// . . . // Initialize context
  ///
  /// while (pore_inline_disassemble(&ctx, &dis) == 0) {
  ///     printf("%s\n", dis.s);
  /// }
  ///
  /// \endcode
  ///
  /// To illustrate binary disassembly, the following example uses the
  /// disassembler to search for a RET statement in a block of PORE code, in
  /// order to extend an inline subroutine with more code.  Note that the field
  /// \a dis->ctx contains the context that existed at the time the instruction
  /// was assembled.  By copying this context back into the global context,
  /// inline assembly will continue by overwriting the RET with new
  /// instructions. If the copy had \e not been done, then newly assembled code
  /// would have \e followed the RET.
  ///
  /// \code
  ///
  /// PoreInlineContext ctx;
  /// PoreInlineDisassembly dis;
  ///
  /// . . . // Initialize context
  ///
  /// while ((pore_inline_disassemble(&ctx, &dis) == 0) &&
  ///        (dis.opcode != PORE_OPCODE_RET));
  /// if (ctx.error != 0) {
  ///     . . . // Handle error
  /// } else {
  ///     pore_inline_context_copy(&ctx, &dis.ctx);
  ///     . . . // Continue assembly by overwriting the RET
  /// }
  ///
  /// \endcode
  ///
  /// A special type of context reset is available to simplify applications that
  /// need to disassemble a just-assembled code sequence, e.g. for debugging.
  /// pore_inline_context_reset_excursion() resets the context such that the
  /// effective size of the context only covers the just-assembled code,
  /// allowing a dissassembly loop to cleanly stop once all code has been
  /// disassembled. The use is illustrated below - note that the disassembly
  /// stops on the expected error code PORE_INLINE_NO_MEMORY once the
  /// (effective) end of the buffer is reached.
  ///
  /// \code
  ///
  /// PoreInlineContext ctx;
  /// PoreInlineDisassembly dis;
  ///
  /// . . . // Initialize context
  /// . . . // Assemble code into context
  ///
  /// pore_inline_context_reset_excursion(&ctx);
  ///
  /// while (pore_inline_disassemble(&ctx, &dis) == 0) {
  ///     printf("%s\n", dis.s);
  /// }
  /// if (ctx.error != PORE_INLINE_NO_MEMORY) {
  ///     . . . // Handle error
  /// }
  ///
  /// \endcode
  ///
  /// \subsection Data Disassembly
  ///
  /// If the PoreInlineContext is created with the flag
  /// PORE_INLINE_DISASSEMBLE_DATA, then the context is disassembled as data. If
  /// the PoreInlineContext is created with the flag
  /// PORE_INLINE_DISASSEMBLE_UNKNOWN then putative data embedded in a text
  /// section will be disassembled as data.  For complete information see the
  /// documentation for pore_inline_disassemble().
  
  
  #define __PORE_INLINE_ASSEMBLER_C__
  #include "pore_inline.h"
  #undef __PORE_INLINE_ASSEMBLER_C__
  
  // Definitions of PORE register classes.  These are predicates that return
  // 1 if the register is a member of the class, else 0.
  
  PORE_STATIC int
  pore_data(int reg)
  {
      return 
  	(reg == D0) ||
  	(reg == D1);
  }
  
  
  PORE_STATIC int
  pore_address(int reg)
  {
      return
  	(reg == A0) ||
  	(reg == A1);
  }
  
  
  PORE_STATIC int
  pore_pervasive_chiplet_id(int reg)
  {
      return
  	(reg == P0) ||
  	(reg == P1);
  }
  
  
  PORE_STATIC int
  pore_branch_compare_data(int reg)
  {
      return 
  	(reg == D0) ||
  	(reg == D1) ||
  	(reg == CTR);
  }
  
  
  PORE_STATIC int
  pore_ls_destination(int reg)
  {
      return
          (reg == D0) ||
          (reg == D1) ||
          (reg == A0) ||
          (reg == A1) ||
          (reg == P0) ||
          (reg == P1) ||
          (reg == CTR);
  }
  
  
  PORE_STATIC int
  pore_li_destination(int reg)
  {
      return 
          (reg == D0)   ||
          (reg == D1)   ||
          (reg == A0)   ||
          (reg == A1)   ||
          (reg == P0)   ||
          (reg == P1)   ||
          (reg == CTR);
  }
  
  
  PORE_STATIC int
  pore_mr_source(int reg)
  {
      return
          (reg == D0)    ||
          (reg == D1)    ||
          (reg == A0)    ||
          (reg == A1)    ||
          (reg == P0)    ||
          (reg == P1)    ||
          (reg == CTR)   ||
          (reg == PC)    ||
          (reg == ETR)   ||
          (reg == SPRG0) ||
          (reg == IFR)   ||
          (reg == EMR);
  }
  
  PORE_STATIC int
  pore_mr_destination(int reg)
  {
      return
          (reg == D0)   ||
          (reg == D1)   ||
          (reg == A0)   ||
          (reg == A1)   ||
          (reg == P0)   ||
          (reg == P1)   ||
          (reg == CTR)  ||
          (reg == PC)   ||
          (reg == SPRG0)||
          (reg == EMR);
  }                    
  
  
  /// Portable store of a 32-bit integer in big-endian format
  ///
  /// The address \a p to receive the data is in the form of an unsigned long.
  
  void
  pore_inline_be32(unsigned long p, uint32_t x)
  {
      uint8_t *p8 = (uint8_t *)p;
      uint8_t *px = (uint8_t *)(&x);
      int i, j;
  
      if (!PORE_BIG_ENDIAN) {
  	for (i = 0, j = 3; i < 4; i++, j--) {
  	    p8[i] = px[j];
  	}
      } else {
  	*((uint32_t *)p) = x;
      }
  }
  	
  	
  /// Portable store of a 64-bit integer in big-endian format
  ///
  /// The address \a p to receive the data is in the form of an unsigned long.
  
  void
  pore_inline_be64(unsigned long p, uint64_t x)
  {
      uint8_t *p8 = (uint8_t *)p;
      uint8_t *px = (uint8_t *)(&x);
      int i, j;
  
      if (!PORE_BIG_ENDIAN) {
  	for (i = 0, j = 7; i < 8; i++, j--) {
  	    p8[i] = px[j];
  	}
      } else {
  	*((uint64_t *)p) = x;
      }
  }
  
  
  // Portable load of a 32-bit integer in big-endian format
  
  uint32_t
  pore_inline_host32(unsigned long p)
  {
      uint32_t x;
      uint8_t *p8 = (uint8_t *)p;
      uint8_t *px = (uint8_t *)(&x);
      int i, j;
  
      if (!PORE_BIG_ENDIAN) {
  	for (i = 0, j = 3; i < 4; i++, j--) {
  	    px[j] = p8[i];
  	}
      } else {
  	x = *((uint32_t *)p);
      }
  
      return x;
  }
  	
  	
  // Portable load of a 64-bit integer in big-endian format
  
  uint64_t
  pore_inline_host64(unsigned long p)
  {
      uint64_t x;
      uint8_t *p8 = (uint8_t *)p;
      uint8_t *px = (uint8_t *)(&x);
      int i, j;
  
      if (!PORE_BIG_ENDIAN) {
  	for (i = 0, j = 7; i < 8; i++, j--) {
  	    px[j] = p8[i];
  	}
      } else {
  	x = *((uint64_t *)p);
      }
  
      return x;
  }
  
  
  // 32-bit population count
  //
  // This is a well-known divide-and-conquer algorithm.  The idea is to compute
  // sums of adjacent bit segments in parallel, in place.
  
  PORE_STATIC int
  pore_popcount32(uint32_t x)
  {
      uint32_t m1 = 0x55555555;
      uint32_t m2 = 0x33333333;
      uint32_t m4 = 0x0f0f0f0f;
      x -= (x >> 1) & m1;		   /* Sum pairs of bits */
      x = (x & m2) + ((x >> 2) & m2);/* Sum 4-bit segments */
      x = (x + (x >> 4)) & m4;	   /* Sum 8-bit segments */
      x += x >>  8;		   /* Sum 16-bit segments */
      return (x + (x >> 16)) & 0x3f; /* Final sum */
  }
  
  
  // 64-bit population count
  
  PORE_STATIC int
  pore_popcount64(uint64_t x)
  {
      return pore_popcount32(x & 0xffffffff) + pore_popcount32(x >> 32);
  }
  
  
  // Compute the parity of a PORE instruction as 0 or 1
  
  int
  pore_inline_parity(uint32_t instruction, uint64_t imd64)
  {
      return (pore_popcount32(instruction) + pore_popcount64(imd64)) % 2;
  }
  	
  	
  /// Reset a PORE inline assembler context to its creation state
  ///
  /// \param ctx A pointer to an initialized (and likely 'used')
  /// PoreInlineContext object.
  ///
  /// This API resets a PoreInlineContext object to it's \e creation state, that
  /// is, the state it was in after the call of pore_inline_context_create().
  /// This API is designed for applications that reuse a memory buffer to
  /// assemble multiple PORE code sequences.  After each sequence has been fully
  /// assembled and processed, calling pore_inline_context_reset() sets the
  /// context back as it was when the context was initially created so that the
  /// memory area can be reused.  In particular, this API resets the location
  /// counter and memory extent to their initial values, and the error code is
  /// cleared.  Any options specified at creation remain as they were.
  ///
  /// For a slightly different type of reset, see
  /// pore_inline_context_reset_excursion().  
  
  void
  pore_inline_context_reset(PoreInlineContext *ctx)
  {
      ctx->lc_address = ctx->memory;
      ctx->remaining = ctx->size;
      ctx->lc = ctx->original_lc;
      ctx->error = 0;
  }
  
  
  
  /// Reset a PORE inline assembler context to a special state for disassembly
  ///
  /// \param ctx A pointer to an initialized (and almost certainly 'used')
  /// PoreInlineContext object.
  ///
  /// This API resets a PoreInlineContext object to it's \e creation state, that
  /// is, the state it was in after the call of pore_inline_context_create(), \e
  /// except that the effective size of the memory area has been reduced to the
  /// size that was actually used during assembly.  This API is designed for
  /// applications that assemble into a memory buffer and then want to easily
  /// disassemble the code (e.g., for debugging).  After a code sequence has
  /// been assembled, calling pore_inline_context_reset_excursion() sets the
  /// context back as it was when the context was initially created, but with a
  /// (typically) shorter effective length, so that the disassembly will cleanly
  /// stop once the entire sequence has been disassembled. Once disassembled,
  /// the buffer can be fully resued after a subsequent call of
  /// pore_inline_context_reset().  In particular, this API resets the location
  /// counter to its initial value, clears the error code, and sets the
  /// effective size of the context to the amount of memory currently used.  Any
  /// options specified at creation remain as they were.
  ///
  /// For a full context reset see pore_inline_context_reset(). For an example
  /// see the \b Disassembly section of \ref pore_inline_assembler.
  
  void
  pore_inline_context_reset_excursion(PoreInlineContext *ctx)
  {
      ctx->lc_address = ctx->memory;
      ctx->remaining = ctx->size - ctx->remaining;
      ctx->lc = ctx->original_lc;
      ctx->error = 0;
  }
  
  
  /// Create a PORE inline assembler context
  ///
  /// \param ctx A pointer to a PoreInlineContext object to be initialized
  /// and used for inline assembly. or disassembly.
  ///
  /// \param memory A pointer to the host memory area to receive the assembled
  /// code, or contain the code to disassemble. In general the inline assembler
  /// will expect this memory area to be 4-byte aligned. This pointer may be
  /// NULL (0) only if the associated \a size is also 0.
  ///
  /// \param size The size (in bytes) of the host memory area. The inline
  /// assembler will generate the PORE_INLINE_NO_MEMORY error if an attempt is
  /// made to assemble an instruction that would overflow the buffer, or
  /// disassemble past the end of the buffer. A 0 size is valid.
  ///
  /// \param lc The initial, bytewise, target location counter for the assembled
  /// or disassembled code. This paramater will normally be initialized to 0 for
  /// assembling relocatable programs. The parameter would only need to be
  /// specified as non-0 for special cases, such as creating a context for
  /// disassembly.
  ///
  /// \param options Option flags.  Option flags are OR-ed together to create
  /// the final set of options. Valid options are
  ///
  /// - PORE_INLINE_GENERATE_PARITY : Generate the proper parity bit for each
  /// instruction during assembly.
  ///
  /// - PORE_INLINE_CHECK_PARITY : Check for correct instruction parity during
  /// disassembly.
  ///
  /// - PORE_INLINE_LISTING_MODE : Generate disassembly strings in the form of a
  /// listing that contains location counters and encoded instructions as well
  /// as their diassembly.  By default the disassembly strings do not contain
  /// this information and can be fed back in as source code to a PORE
  /// assembler.
  ///
  /// - PORE_INLINE_DISASSEMBLE_DATA : generate disassembly assuming that the
  /// context contains data rather than text. Normally data is disassembled as
  /// .long directives, however if the context is unaligned or of an odd length
  /// then .byte directives may be used as well.  This option can be used in
  /// conjunction with PORE_INLINE_LISTING_MODE.
  ///
  /// - PORE_INLINE_8_BYTE_DATA : generate data disassembly using 8-byte values
  /// rather than the default 4-byte values.  Normally data is disassembled as
  /// .quad directives under this option, however if the context is unaligned or
  /// of an odd length then .long and .byte directives may be used as well.
  /// This option can be used in conjunction with PORE_INLINE_LISTING_MODE.
  ///
  /// A PoreInlineContext describes a memory area and assembler context for
  /// inline assembly and disassembly.  Assembly/disassembly begins at the host
  /// memory location and virtual location counter described in the parameters.
  /// As instructions are assembled/disassembled the PoreInlineContext keeps
  /// track of where in the host memory and virtual PORE memory areas to place
  /// new instructions during assembly, or from where to fetch the next
  /// instruction to disassemble.
  ///
  /// \retval 0 Success
  ///
  /// \retval PORE_INLINE_INVALID_PARAMETER Either the \a context pointer is
  /// NULL (0), the \a memory pointer is NULL (0) with a non-0 size, or the \a
  /// options include invalid options.  The error code is also stored as the
  /// value of ctx->error, and in the event of an error the ctx->size field is
  /// set to 0, effectively preventing the context from being used.
  
  int
  pore_inline_context_create(PoreInlineContext *ctx,
  			   void *memory, size_t size, 
  			   PoreInlineLocation lc, int options)
  {
      int rc;
  
      int valid_options = 
  	PORE_INLINE_GENERATE_PARITY  |
  	PORE_INLINE_CHECK_PARITY     |
  	PORE_INLINE_LISTING_MODE     |
          PORE_INLINE_DISASSEMBLE_DATA |
          PORE_INLINE_8_BYTE_DATA      |
          PORE_INLINE_DISASSEMBLE_UNKNOWN;
  
      if ((ctx == NULL) || ((memory == NULL) && (size != 0)) ||
  	((options & ~valid_options) != 0)) {
  	rc = PORE_INLINE_INVALID_PARAMETER;
      } else {
  	rc = 0;
  	ctx->memory = (unsigned long)memory;
  	ctx->size = size;
  	ctx->original_lc = lc;
  	ctx->options = options;
  	pore_inline_context_reset(ctx);
      }
  
      if (ctx != NULL) {
          ctx->error = rc;
          if (rc) {
              ctx->size = 0;      /* Effectively prevents using the ctx */
          }
      }
  
      return rc;
  }
  	
  	
  /// Copy a PORE inline assembler context
  ///
  /// \param dest A pointer to a PoreInlineContext object to be initialized
  /// as a copy of the \a src context.
  ///
  /// \param src A pointer to a PoreInlineContext object to be used as the
  /// source of the copy.
  ///
  /// This API copies one PoreInlineContext structure to another.  An example
  /// use appears in \ref pore_inline_assembler in the section discussing
  /// disassembly.
  
  void
  pore_inline_context_copy(PoreInlineContext *dest, PoreInlineContext *src)
  {
      *dest = *src;
  }
  
  
  // 'Bump' a context forward by a given number of bytes.  This an internal API
  // and the bump is always known to be legal.
  
  void
  pore_inline_context_bump(PoreInlineContext *ctx, size_t bytes)
  {
      ctx->remaining -= bytes;
      ctx->lc += bytes;
      ctx->lc_address += bytes;
  }    
  
  
  // Allocate space in the inline assembler context
  //
  // Allocation is specified and implemented in bytes.  Both the physical
  // memory and the virtual LC are required to be 4-byte aligned. The allocator
  // returns a pointer to the memory area, or 0 if allocation fails.
  // Allocation failure sets the context error code to either
  // PORE_INLINE_NO_MEMORY or PORE_INLINE_ALIGNMENT_ERROR.
  
  PORE_STATIC unsigned long
  pore_inline_allocate(PoreInlineContext *ctx, size_t bytes)
  {
      unsigned long p = 0;
  
      if (((ctx->lc % 4) != 0) || 
  	((ctx->lc_address % 4) != 0)) {
  	ctx->error = PORE_INLINE_ALIGNMENT_ERROR;
  
      } else if (bytes > ctx->remaining) {
  	ctx->error = PORE_INLINE_NO_MEMORY;
  
      } else {
  	p = ctx->lc_address;
  	pore_inline_context_bump(ctx, bytes);
      }
      return p;
  }
  
  
  // Assemble a 1-word instruction
  //
  // The opcode and operand are assumed to be legal, having come from
  // abstractions that check their arguments.  This call may fail with
  // PORE_INLINE_NO_MEMORY if there is no more room in the memory buffer. A
  // non-zero return indicates failure.
  
  int
  pore_inline_instruction1(PoreInlineContext *ctx, int opcode, uint32_t operand)
  {
      uint32_t instruction;
      unsigned long p;
  
      p = pore_inline_allocate(ctx, 4);
      if (p != 0) {
  
  	instruction = (opcode << 25) | operand;
  	if (ctx->options & PORE_INLINE_GENERATE_PARITY) {
  	    instruction |= (1 - pore_inline_parity(instruction, 0)) << 24;
  	}
  
  	pore_inline_be32(p, instruction);
  	ctx->error = 0;
      }
      return p == 0;
  }
  	    
  
  // Assemble a 3-word instruction
  //
  // The opcode and operand are assumed to be legal, having come from
  // abstractions that check their arguments.  This call may fail with
  // PORE_INLINE_NO_MEMORY if there is no more room in the memory buffer.  A
  // non-zero return indicates failure.
  
  int
  pore_inline_instruction3(PoreInlineContext *ctx, int opcode, uint32_t operand,
  			 uint64_t immediate)
  {
      uint32_t instruction;
      unsigned long p;
  
      p = pore_inline_allocate(ctx, 12);
      if (p != 0) {
  
  	instruction = (opcode << 25) | operand;
  	if (ctx->options & PORE_INLINE_GENERATE_PARITY) {
  	    instruction |= (1 - pore_inline_parity(instruction, immediate)) << 24;
  	}
  
  	pore_inline_be32(p, instruction);
  	pore_inline_be64(p + 4, immediate);
  	ctx->error = 0;
      }
      return p == 0;
  }
  
  
  // Assemble WAIT
  //
  // The cycle count must be an unsigned 24-bit immediate otherwise the error
  // PORE_INLINE_UINT24_REQUIRED is signalled.  PGAS requires that HALT be used
  // if the intention is to halt 
  
  int
  pore_WAITS(PoreInlineContext *ctx, uint32_t cycles)
  {
      uint32_t operand;
      int opcode = PGAS_OPCODE_WAITS;
  
      if (cycles == 0) {
          ctx->error = PORE_INLINE_USE_HALT;
      } else if ((cycles & 0xffffff) != cycles) {
  	ctx->error = PORE_INLINE_UINT24_REQUIRED;
      } else {
  	operand = cycles;
  	pore_inline_instruction1(ctx, opcode, operand);
      }
      return ctx->error;
  }
  
  
  // Assemble HOOKI
  //
  // The hook index must be an unsigned 24-bit immediate otherwise the error
  // PORE_INLINE_UINT24_REQUIRED is signalled.
  
  int
  pore_HOOKI(PoreInlineContext *ctx, uint32_t index, uint64_t imm)
  {
      uint32_t operand;
      int opcode = PGAS_OPCODE_HOOKI;
  
      if ((index & 0xffffff) != index) {
  	ctx->error = PORE_INLINE_UINT24_REQUIRED;
      } else {
  	operand = index;
  	pore_inline_instruction3(ctx, opcode, operand, imm);
      }
      return ctx->error;
  }
  
  
  // Assemble BRA, BSR and LOOP
  //
  // The branch target here is a bytewise location counter.  The target must be
  // 4-byte aligned and must be within the legal signed 24-bit word offset of
  // the current LC. Unaligned targets cause PORE_INLINE_ALIGNMENT_ERROR.
  // Unreachable targets cause PORE_INLINE_UNREACHABLE_TARGET.
  
  int
  pore_inline_bra(PoreInlineContext *ctx, int opcode, PoreInlineLocation target)
  {
      int32_t offset;
      uint32_t operand;
  
      if (target % 4) {
  	ctx->error = PORE_INLINE_ALIGNMENT_ERROR;
      } else {
  	offset = (int32_t)(target - ctx->lc) / 4;
  	if ((offset >= (1 << 23)) ||
  	    (offset < -(1 << 23))) {
  	    ctx->error = PORE_INLINE_UNREACHABLE_TARGET;
  	} else {
  	    operand = offset & 0xffffff;
  	    pore_inline_instruction1(ctx, opcode, operand);
  	}
      }
      return ctx->error;
  }
  	    
  
  // Assemble BRAZ and BRANZ
  //
  // The branch target here is a bytewise location counter.  The target must be
  // 4-byte aligned and must be within the legal signed 20-bit word offset of
  // the current LC. Unaligned targets cause PORE_INLINE_ALIGNMENT_ERROR.
  // Unreachable targets cause PORE_INLINE_UNREACHABLE_TARGET.  Illegal
  // operands cause PORE_INLINE_ILLEGAL_REGISTER.
  
  int
  pore_inline_brac(PoreInlineContext *ctx, int opcode, int reg, 
  		 PoreInlineLocation target)
  {
      int32_t offset;
      uint32_t operand;
  
      if (target % 4) {
  	ctx->error = PORE_INLINE_ALIGNMENT_ERROR;
      } else if (!pore_branch_compare_data(reg)) {
  	ctx->error = PORE_INLINE_ILLEGAL_REGISTER;
      } else {
  	offset = (int32_t)(target - ctx->lc) / 4;
  	if ((offset >= (1 << 20)) ||
  	    (offset < -(1 << 20))) {
  	    ctx->error = PORE_INLINE_UNREACHABLE_TARGET;
  	} else {
  	    operand = (offset & 0xfffff) | (reg << 20);
  	    pore_inline_instruction1(ctx, opcode, operand);
  	}
      }
      return ctx->error;
  }
  
  
  // Assemble CMPIBRAEQ, CMPIBRANE, CMPIBSREQ
  //
  // The branch target here is a bytewise location counter.  The target must be
  // 4-byte aligned and must be within the legal signed 24-bit word offset of
  // the current LC. Unaligned targets cause PORE_INLINE_ALIGNMENT_ERROR.
  // Unreachable targets cause PORE_INLINE_UNREACHABLE_TARGET. Illegal
  // operands cause PORE_INLINE_ILLEGAL_REGISTER.
  
  int
  pore_inline_cmpibra(PoreInlineContext *ctx, int opcode, int reg,
                      PoreInlineLocation target, uint64_t imm)
  {
      int32_t offset;
      uint32_t operand;
  
      if (target % 4) {
  	ctx->error = PORE_INLINE_ALIGNMENT_ERROR;
      } else if (reg != D0) {
          ctx->error = PORE_INLINE_ILLEGAL_REGISTER;
      } else {
  	offset = (int32_t)(target - ctx->lc) / 4;
  	if ((offset >= (1 << 23)) ||
  	    (offset < -(1 << 23))) {
  	    ctx->error = PORE_INLINE_UNREACHABLE_TARGET;
  	} else {
  	    operand = offset & 0xffffff;
  	    pore_inline_instruction3(ctx, opcode, operand, imm);
  	}
      }
      return ctx->error;
  }
  
  
  // Assemble BRAD and BSRD
  //
  // Illegal operands cause PORE_INLINE_ILLEGAL_REGISTER.
  
  int
  pore_inline_brad(PoreInlineContext *ctx, int opcode, int reg)
  {
      uint32_t operand;
  
      if (!pore_data(reg)) {
  	ctx->error = PORE_INLINE_ILLEGAL_REGISTER;
      } else {
  	operand = reg << 20;
  	pore_inline_instruction1(ctx, opcode, operand);
      }
      return ctx->error;
  }
  
  
  // Assemble ANDI, ORI, XORI
  //
  // Source and destination must be of class 'data' otherwise the
  // PORE_INLINE_ILLEGAL_REGISTER error is generated.
  
  int
  pore_inline_ilogic(PoreInlineContext *ctx, int opcode, 
  		   int dest, int src, uint64_t imm)
  {
      uint32_t operand;
  
      if (!pore_data(dest) || !pore_data(src)) {
  	ctx->error = PORE_INLINE_ILLEGAL_REGISTER;
      } else {
  	operand = (dest << 20) | (src << 16);
  	pore_inline_instruction3(ctx, opcode, operand, imm);
      }
      return ctx->error;
  }
  
  
  // Assemble AND, OR, XOR, ADD, SUB
  //
  // Destination must be of class 'data' otherwise the
  // PORE_INLINE_ILLEGAL_REGISTER error is generated.  src1 and src2 must be D0,
  // D1 respectively otherwise the PORE_INLINE_ILLEGAL_REGISTER error is
  // generated.
  
  int
  pore_inline_alurr(PoreInlineContext *ctx, 
                    int opcode, int dest, int src1, int src2)
  {
      uint32_t operand;
  
      if (!pore_data(dest) || (src1 != D0) || (src2 != D1)) {
  	ctx->error = PORE_INLINE_ILLEGAL_REGISTER;
      } else {
  	operand = (dest << 20);
  	pore_inline_instruction1(ctx, opcode, operand);
      }
      return ctx->error;
  }
  
  
  // Assemble ADDS and SUBS
  //
  // Destination must be of class 'ls_destination' and must be equal to source,
  // otherwise the PORE_INLINE_ILLEGAL_REGISTER error is generated.  If the
  // immediate is not a signed 16-bit immediate then the
  // PORE_INLINE_INT16_REQUIRED error is generated.
  
  int
  pore_inline_adds(PoreInlineContext *ctx, 
                   int opcode, int dest, int src, int imm)
  {
      uint32_t operand;
  
      if (!pore_ls_destination(dest) || (dest != src)) {
  	ctx->error = PORE_INLINE_ILLEGAL_REGISTER;
      } else {
  	if ((imm >= (1 << 15)) ||
  	    (imm < -(1 << 15))) {
  	    ctx->error = PORE_INLINE_INT16_REQUIRED;
  	} else {
  	    operand = (dest << 20) | (imm & 0xffff);
  	    pore_inline_instruction1(ctx, opcode, operand);
  	}
      }
      return ctx->error;
  }
  
  
  // Assemble NEG
  //
  // Source and destination must be of class 'data' otherwise the
  // PORE_INLINE_ILLEGAL_REGISTER error is generated.
  
  int
  pore_NEG(PoreInlineContext *ctx, int dest, int src)
  {
      uint32_t operand;
      int opcode = PGAS_OPCODE_NEG;
  
      if (!pore_data(dest) || !pore_data(src)) {
  	ctx->error = PORE_INLINE_ILLEGAL_REGISTER;
      } else {
  	operand = (dest << 20) | (src << 16);
  	pore_inline_instruction1(ctx, opcode, operand);
      }
      return ctx->error;
  }
  
  
  // Assemble MR
  //
  // The source must be an 'mr_source' and the destination must be an
  // 'mr_destination' otherwise the PORE_INLINE_ILLEGAL_REGISTER error is
  // generated.
  
  int
  pore_MR(PoreInlineContext *ctx, int dest, int src)
  {
      uint32_t operand;
      int opcode = PGAS_OPCODE_MR;
  
      if (!pore_mr_destination(dest) || !pore_mr_source(src)) {
  	ctx->error = PORE_INLINE_ILLEGAL_REGISTER;
      } else {
  	operand = (dest << 20) | (src << 16);
  	pore_inline_instruction1(ctx, opcode, operand);
      }
      return ctx->error;
  }
  
  	    
  
  // Assemble ROLS
  //
  // Source and destination must be of class 'data' otherwise the
  // PORE_INLINE_ILLEGAL_REGISTER error is generated.  Illegal shifts yield the
  // PORE_INLINE_ILLEGAL_ROTATE error.
  
  int
  pore_ROLS(PoreInlineContext *ctx, int dest, int src, int imm)
  {
      uint32_t operand;
      int opcode = PGAS_OPCODE_ROLS;
      
      if (!pore_data(dest) || !pore_data(src)) {
  	ctx->error = PORE_INLINE_ILLEGAL_REGISTER;
      } else if ((imm != 1) &&
  	       (imm != 4) &&
  	       (imm != 8) &&
  	       (imm != 16) &&
  	       (imm != 32)) {
  	ctx->error = PORE_INLINE_ILLEGAL_ROTATE;
      } else {
  	operand = (dest << 20) | (src << 16) | imm;
  	pore_inline_instruction1(ctx, opcode, operand);
      }
      return ctx->error;
  }
  
  
  // Assemble LS
  //
  // The destination must be an 'ls_destination' otherwise the
  // PORE_INLINE_ILLEGAL_REGISTER error is generated.  If the immediate is not
  // a signed 20-bit immediate then the PORE_INLINE_INT20_REQUIRED error is
  // generated. 
  
  int
  pore_LS(PoreInlineContext *ctx, int dest, int imm)
  {
      uint32_t operand;
      int opcode = PGAS_OPCODE_LS;
      
      if (!pore_ls_destination(dest)) {
  	ctx->error = PORE_INLINE_ILLEGAL_REGISTER;
      } else if ((imm >= (1 << 19)) ||
                 (imm < -(1 << 19))) {
          ctx->error = PORE_INLINE_INT20_REQUIRED;
      } else {
  	operand = (dest << 20) | (imm & 0xfffff);
  	pore_inline_instruction1(ctx, opcode, operand);
      }
      return ctx->error;
  }
  
  
  // Assemble LI
  //
  // The destination must be an 'li destination' otherwise the
  // PORE_INLINE_ILLEGAL_REGISTER error is generated.
  
  int
  pore_LI(PoreInlineContext *ctx, int dest, uint64_t imm)
  {
      uint32_t operand;
      int opcode = PGAS_OPCODE_LI;
      
      if (!pore_li_destination(dest)) {
  	ctx->error = PORE_INLINE_ILLEGAL_REGISTER;
      } else {
  	operand = dest << 20;
  	pore_inline_instruction3(ctx, opcode, operand, imm);
      }
      return ctx->error;
  }
  
  
  // BSI and BCI are normally redacted as instructions due to HW274735
  
  // LD, LDANDI, STD, STI, BSI, BCI
  
  PORE_STATIC void
  pervasive_ima24(PoreInlineContext *ctx, 
                  int opcode, uint32_t offset, int base, uint64_t imm)
  {
      uint32_t operand;
  
      if ((offset & 0x80f00000) != 0) {
          ctx->error = PORE_INLINE_ILLEGAL_SCOM_ADDRESS;
      } else {
          operand = ((base % 2) << 22) | (offset & 0xfffff);
          switch (opcode) {
          case PGAS_OPCODE_LD0:
          case PGAS_OPCODE_LD1:
          case PGAS_OPCODE_STD0:
          case PGAS_OPCODE_STD1:
              pore_inline_instruction1(ctx, opcode, operand);
              break;
          default:
              pore_inline_instruction3(ctx, opcode, operand, imm);
              break;
          }
      }
  }            
          
  
  PORE_STATIC void
  memory_ima24(PoreInlineContext *ctx, 
               int opcode, uint32_t offset, int base, uint64_t imm)
  {
      uint32_t operand;
  
      if ((offset & 0x3fffff) != offset) {
          ctx->error = PORE_INLINE_UINT22_REQUIRED;
      } else if ((offset % 8) != 0) {
          ctx->error = PORE_INLINE_ALIGNMENT_ERROR;
      } else {
          operand = 0x800000 | ((base % 2) << 22) | (offset & 0x3fffff);
          switch (opcode) {
          case PGAS_OPCODE_LD0:
          case PGAS_OPCODE_LD1:
          case PGAS_OPCODE_STD0:
          case PGAS_OPCODE_STD1:
              pore_inline_instruction1(ctx, opcode, operand);
              break;
          default:
              pore_inline_instruction3(ctx, opcode, operand, imm);
              break;
          }
      }
  }
  
  
  PORE_STATIC void
  ima24(PoreInlineContext *ctx, 
        int opcode, uint32_t offset, int base, uint64_t imm)
  {
      if (pore_pervasive_chiplet_id(base)) {
          pervasive_ima24(ctx, opcode, offset, base, imm);
      } else if (pore_address(base)) {
          memory_ima24(ctx, opcode, offset, base, imm);
      } else {
          ctx->error = PORE_INLINE_ILLEGAL_REGISTER;
      }
  }
  
  
  int
  pore_inline_load_store(PoreInlineContext *ctx, 
  		       int opcode, int src_dest, int32_t offset, int base,
  		       uint64_t imm)
  {
      switch (opcode) {
  
      case PORE_INLINE_PSEUDO_LD:
      case PORE_INLINE_PSEUDO_LDANDI:
      case PORE_INLINE_PSEUDO_STD:
  
          // These three pick the real opcode based on the dest. register
  
          if (!pore_data(src_dest)) {
              ctx->error = PORE_INLINE_ILLEGAL_REGISTER;
          } else {
              switch (opcode) {
              case PORE_INLINE_PSEUDO_LD:
                  opcode = (src_dest == D0) ? 
                      PGAS_OPCODE_LD0 : PGAS_OPCODE_LD1;
                  break;
              case PORE_INLINE_PSEUDO_LDANDI:
                  opcode = (src_dest == D0) ? 
                      PGAS_OPCODE_LD0ANDI : PGAS_OPCODE_LD1ANDI;
                  break;
              case PORE_INLINE_PSEUDO_STD:
                  opcode = (src_dest == D0) ? 
                      PGAS_OPCODE_STD0 : PGAS_OPCODE_STD1;
                  break;
              }
          }
          break;
  
  #ifdef IGNORE_HW274735
  
          // BSI and BCI are normally redacted as instructions due to HW274735
          
          case PGAS_OPCODE_BSI:
          case PGAS_OPCODE_BCI:
          
              if (src_dest != D0) {
                  ctx->error = PORE_INLINE_ILLEGAL_REGISTER;
              }
              break;
  
  #endif // IGNORE_HW274735
  
      case PGAS_OPCODE_STI:
          break;
  
      default:
          ctx->error = PORE_INLINE_BUG;
      }
  
      if (ctx->error == 0) {
          ima24(ctx, opcode, offset, base, imm);
      }
  
      return ctx->error;
  }
  
  
  // Assemble BRAIA
  
  int
  pore_BRAIA(PoreInlineContext *ctx,
             uint16_t address_space, uint32_t offset)
  {
      int opcode = PGAS_OPCODE_BRAI;
      uint32_t operand = 0;
      uint64_t imm = ((uint64_t)address_space << 32) | offset;
  
      pore_inline_instruction3(ctx, opcode, operand, imm);
  
      return ctx->error;
  }
  
  
  // Assemble SCAND
  
  int
  pore_SCAND(PoreInlineContext *ctx,
             int update, int capture, uint16_t length, 
             uint32_t select, uint32_t offset)
  {
      int opcode = PGAS_OPCODE_SCAND;
      uint32_t operand;
      uint64_t imm = ((uint64_t)select << 32) | offset;
  
      if ((update < 0) ||
          (update > 1) ||
          (capture < 0) ||
          (capture > 1)) {
          ctx->error = PORE_INLINE_INVALID_PARAMETER;
      } else {
          opcode = PGAS_OPCODE_SCAND;
          operand = (update << 23) | (capture << 22) | length;
          pore_inline_instruction3(ctx, opcode, operand, imm);
      }
      return ctx->error;
  }
          
  
  /// Fix up a PORE inline assembler forward branch instruction
  ///
  /// \param ctx A pointer to the initialized PoreInlineContext object
  /// controlling inline assembly.
  ///
  /// \param source The PORE inline location counter associated with the source
  /// instruction of the forward branch.
  ///
  /// \param target The PORE inline location counter associated with the target
  /// instruction of the forward branch.
  ///
  /// For usage examples, see the documentation \ref pore_inline_assembler.
  /// Although intended for forward branches, this API could be used to create
  /// backward branches as well.  Note however the limitation that the \a source
  /// must be in the current context, since the source instruction needs to be
  /// reassembled with the branch target. In theory the \a target could be
  /// anywhere, as long as the location counter of the target is known.
  ///
  /// \retval 0 Success
  ///
  /// \retval code Failure.  Any non-zero return is the PORE inline assmebler
  /// error code. The failure code is also stored in the PoreInlineContext
  /// object \a error field.  The most likely causes of failure include a source
  /// location that is not in the current context or not associated with a
  /// branch instruction.
  
  int
  pore_inline_branch_fixup(PoreInlineContext *ctx, 
  			 PoreInlineLocation source,
  			 PoreInlineLocation target)
  {
      uint32_t instruction;
      int32_t distance;
      uint64_t imm;
      int opcode, reg;
      PoreInlineContext source_ctx;
  
      if ((source < ctx->original_lc) ||
  	(source > ctx->lc)) {
  	ctx->error = PORE_INLINE_ILLEGAL_SOURCE_LC;
      } else {
  
  	// Create a context as it existed when the source instruction was
  	// initially assembled, and then reassemble the instruction in that
  	// context with the actual target.
  
  	distance = ctx->lc - source;
  
  	source_ctx = *ctx;
  	source_ctx.lc = source;
  	source_ctx.remaining += distance;
  	source_ctx.lc_address -= distance;
  	source_ctx.error = 0;
  	
  	instruction = pore_inline_host32(source_ctx.lc_address);
  	opcode = (instruction >> 25);
  	reg = (instruction >> 20) & 0xf;
  	
  	switch (opcode) {
  	case PGAS_OPCODE_BRA:
  	    pore_BRA(&source_ctx, target);
  	    break;
  	case PGAS_OPCODE_BSR:
  	    pore_BSR(&source_ctx, target);
  	    break;
  	case PGAS_OPCODE_LOOP:
  	    pore_LOOP(&source_ctx, target);
  	    break;
  	case PGAS_OPCODE_BRAZ:
  	    pore_BRAZ(&source_ctx, reg, target);
  	    break;
  	case PGAS_OPCODE_BRANZ:
  	    pore_BRANZ(&source_ctx, reg, target);
  	    break;
  	case PGAS_OPCODE_CMPIBRAEQ:
  	    imm = pore_inline_host64(source_ctx.lc_address + 4);
  	    pore_CMPIBRAEQ(&source_ctx, D0, target, imm);
  	    break;
  	case PGAS_OPCODE_CMPIBRANE:
  	    imm = pore_inline_host64(source_ctx.lc_address + 4);
  	    pore_CMPIBRANE(&source_ctx, D0, target, imm);
  	    break;
  	case PGAS_OPCODE_CMPIBSREQ:
  	    imm = pore_inline_host64(source_ctx.lc_address + 4);
  	    pore_CMPIBSREQ(&source_ctx, D0, target, imm);
  	    break;
  	default:
  	    source_ctx.error = PORE_INLINE_NOT_A_BRANCH;
  	    break;
  	}
  
  	ctx->error = source_ctx.error;
      }
      return ctx->error;
  }
  