OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [tags/] [nog_patch_70/] [or1ksim/] [cpu/] [or32/] [dyn_rec.c] - Rev 1765

Compare with Previous | Blame | View Log

/* dyn_rec.c -- Dynamic recompiler implementation for or32
   Copyright (C) 2005 György `nog' Jeney, nog@sdf.lonestar.org
 
This file is part of OpenRISC 1000 Architectural Simulator.
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <signal.h>
#include <errno.h>
#include <execinfo.h>
 
#include "config.h"
 
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
 
#include "port.h"
#include "arch.h"
#include "immu.h"
#include "abstract.h"
#include "opcode/or32.h"
#include "spr_defs.h"
#include "execute.h"
#include "except.h"
#include "spr_defs.h"
#include "sim-config.h"
#include "sched.h"
 
#include "rec_i386.h"
#include "i386_regs.h"
 
#include "dyn_rec.h"
#include "gen_ops.h"
 
#include "op_support.h"
 
/* NOTE: All openrisc (or) addresses in this file are *PHYSICAL* addresses */
 
/* FIXME: Optimise sorted list adding */
 
/* FIXME: remove this and use config.immu.pagesize */
#define PAGE_LEN 8192
 
typedef void (*generic_gen_op)(struct op_queue *opq, int end);
typedef void (*imm_gen_op)(struct op_queue *opq, int end, uorreg_t imm);
 
void gen_l_invalid(struct op_queue *opq, int param_t[3], orreg_t param[3],
                   int delay_slot);
 
static const generic_gen_op gen_op_move_gpr_t[NUM_T_REGS][32] = {
 { NULL,
   gen_op_move_gpr1_t0,
   gen_op_move_gpr2_t0,
   gen_op_move_gpr3_t0,
   gen_op_move_gpr4_t0,
   gen_op_move_gpr5_t0,
   gen_op_move_gpr6_t0,
   gen_op_move_gpr7_t0,
   gen_op_move_gpr8_t0,
   gen_op_move_gpr9_t0,
   gen_op_move_gpr10_t0,
   gen_op_move_gpr11_t0,
   gen_op_move_gpr12_t0,
   gen_op_move_gpr13_t0,
   gen_op_move_gpr14_t0,
   gen_op_move_gpr15_t0,
   gen_op_move_gpr16_t0,
   gen_op_move_gpr17_t0,
   gen_op_move_gpr18_t0,
   gen_op_move_gpr19_t0,
   gen_op_move_gpr20_t0,
   gen_op_move_gpr21_t0,
   gen_op_move_gpr22_t0,
   gen_op_move_gpr23_t0,
   gen_op_move_gpr24_t0,
   gen_op_move_gpr25_t0,
   gen_op_move_gpr26_t0,
   gen_op_move_gpr27_t0,
   gen_op_move_gpr28_t0,
   gen_op_move_gpr29_t0,
   gen_op_move_gpr30_t0,
   gen_op_move_gpr31_t0 },
 { NULL,
   gen_op_move_gpr1_t1,
   gen_op_move_gpr2_t1,
   gen_op_move_gpr3_t1,
   gen_op_move_gpr4_t1,
   gen_op_move_gpr5_t1,
   gen_op_move_gpr6_t1,
   gen_op_move_gpr7_t1,
   gen_op_move_gpr8_t1,
   gen_op_move_gpr9_t1,
   gen_op_move_gpr10_t1,
   gen_op_move_gpr11_t1,
   gen_op_move_gpr12_t1,
   gen_op_move_gpr13_t1,
   gen_op_move_gpr14_t1,
   gen_op_move_gpr15_t1,
   gen_op_move_gpr16_t1,
   gen_op_move_gpr17_t1,
   gen_op_move_gpr18_t1,
   gen_op_move_gpr19_t1,
   gen_op_move_gpr20_t1,
   gen_op_move_gpr21_t1,
   gen_op_move_gpr22_t1,
   gen_op_move_gpr23_t1,
   gen_op_move_gpr24_t1,
   gen_op_move_gpr25_t1,
   gen_op_move_gpr26_t1,
   gen_op_move_gpr27_t1,
   gen_op_move_gpr28_t1,
   gen_op_move_gpr29_t1,
   gen_op_move_gpr30_t1,
   gen_op_move_gpr31_t1 },
 { NULL,
   gen_op_move_gpr1_t2,
   gen_op_move_gpr2_t2,
   gen_op_move_gpr3_t2,
   gen_op_move_gpr4_t2,
   gen_op_move_gpr5_t2,
   gen_op_move_gpr6_t2,
   gen_op_move_gpr7_t2,
   gen_op_move_gpr8_t2,
   gen_op_move_gpr9_t2,
   gen_op_move_gpr10_t2,
   gen_op_move_gpr11_t2,
   gen_op_move_gpr12_t2,
   gen_op_move_gpr13_t2,
   gen_op_move_gpr14_t2,
   gen_op_move_gpr15_t2,
   gen_op_move_gpr16_t2,
   gen_op_move_gpr17_t2,
   gen_op_move_gpr18_t2,
   gen_op_move_gpr19_t2,
   gen_op_move_gpr20_t2,
   gen_op_move_gpr21_t2,
   gen_op_move_gpr22_t2,
   gen_op_move_gpr23_t2,
   gen_op_move_gpr24_t2,
   gen_op_move_gpr25_t2,
   gen_op_move_gpr26_t2,
   gen_op_move_gpr27_t2,
   gen_op_move_gpr28_t2,
   gen_op_move_gpr29_t2,
   gen_op_move_gpr30_t2,
   gen_op_move_gpr31_t2 } };
 
static const generic_gen_op gen_op_move_t_gpr[NUM_T_REGS][32] = {
 { NULL,
   gen_op_move_t0_gpr1,
   gen_op_move_t0_gpr2,
   gen_op_move_t0_gpr3,
   gen_op_move_t0_gpr4,
   gen_op_move_t0_gpr5,
   gen_op_move_t0_gpr6,
   gen_op_move_t0_gpr7,
   gen_op_move_t0_gpr8,
   gen_op_move_t0_gpr9,
   gen_op_move_t0_gpr10,
   gen_op_move_t0_gpr11,
   gen_op_move_t0_gpr12,
   gen_op_move_t0_gpr13,
   gen_op_move_t0_gpr14,
   gen_op_move_t0_gpr15,
   gen_op_move_t0_gpr16,
   gen_op_move_t0_gpr17,
   gen_op_move_t0_gpr18,
   gen_op_move_t0_gpr19,
   gen_op_move_t0_gpr20,
   gen_op_move_t0_gpr21,
   gen_op_move_t0_gpr22,
   gen_op_move_t0_gpr23,
   gen_op_move_t0_gpr24,
   gen_op_move_t0_gpr25,
   gen_op_move_t0_gpr26,
   gen_op_move_t0_gpr27,
   gen_op_move_t0_gpr28,
   gen_op_move_t0_gpr29,
   gen_op_move_t0_gpr30,
   gen_op_move_t0_gpr31 },
 { NULL,
   gen_op_move_t1_gpr1,
   gen_op_move_t1_gpr2,
   gen_op_move_t1_gpr3,
   gen_op_move_t1_gpr4,
   gen_op_move_t1_gpr5,
   gen_op_move_t1_gpr6,
   gen_op_move_t1_gpr7,
   gen_op_move_t1_gpr8,
   gen_op_move_t1_gpr9,
   gen_op_move_t1_gpr10,
   gen_op_move_t1_gpr11,
   gen_op_move_t1_gpr12,
   gen_op_move_t1_gpr13,
   gen_op_move_t1_gpr14,
   gen_op_move_t1_gpr15,
   gen_op_move_t1_gpr16,
   gen_op_move_t1_gpr17,
   gen_op_move_t1_gpr18,
   gen_op_move_t1_gpr19,
   gen_op_move_t1_gpr20,
   gen_op_move_t1_gpr21,
   gen_op_move_t1_gpr22,
   gen_op_move_t1_gpr23,
   gen_op_move_t1_gpr24,
   gen_op_move_t1_gpr25,
   gen_op_move_t1_gpr26,
   gen_op_move_t1_gpr27,
   gen_op_move_t1_gpr28,
   gen_op_move_t1_gpr29,
   gen_op_move_t1_gpr30,
   gen_op_move_t1_gpr31 },
 { NULL,
   gen_op_move_t2_gpr1,
   gen_op_move_t2_gpr2,
   gen_op_move_t2_gpr3,
   gen_op_move_t2_gpr4,
   gen_op_move_t2_gpr5,
   gen_op_move_t2_gpr6,
   gen_op_move_t2_gpr7,
   gen_op_move_t2_gpr8,
   gen_op_move_t2_gpr9,
   gen_op_move_t2_gpr10,
   gen_op_move_t2_gpr11,
   gen_op_move_t2_gpr12,
   gen_op_move_t2_gpr13,
   gen_op_move_t2_gpr14,
   gen_op_move_t2_gpr15,
   gen_op_move_t2_gpr16,
   gen_op_move_t2_gpr17,
   gen_op_move_t2_gpr18,
   gen_op_move_t2_gpr19,
   gen_op_move_t2_gpr20,
   gen_op_move_t2_gpr21,
   gen_op_move_t2_gpr22,
   gen_op_move_t2_gpr23,
   gen_op_move_t2_gpr24,
   gen_op_move_t2_gpr25,
   gen_op_move_t2_gpr26,
   gen_op_move_t2_gpr27,
   gen_op_move_t2_gpr28,
   gen_op_move_t2_gpr29,
   gen_op_move_t2_gpr30,
   gen_op_move_t2_gpr31 } };
 
static const imm_gen_op calc_insn_ea_table[NUM_T_REGS] =
 { gen_op_calc_insn_ea_t0, gen_op_calc_insn_ea_t1, gen_op_calc_insn_ea_t2 };
 
/* Linker stubs.  This will allow the linker to link in op.o.  The relocations
 * that the linker does for these will be irrelevent anyway, since we patch the
 * relocations during recompilation. */
uorreg_t __op_param1;
uorreg_t __op_param2;
uorreg_t __op_param3;
 
/* The number of bytes that a dynamicly recompiled page should be enlarged by */
#define RECED_PAGE_ENLARGE_BY 51200
 
/* The number of entries that the micro operations array in op_queue should be
 * enlarged by */
#define OPS_ENLARGE_BY 5
 
#define T_NONE (-1)
 
void *rec_stack_base;
 
/* FIXME: Put this into some header */
extern int do_stats;
 
static int sigsegv_state = 0;
static void *sigsegv_addr = NULL;
 
void dyn_ret_stack_prot(void);
void dump_held_xrefs(struct dyn_page *dp, FILE *f);
 
void dyn_sigsegv_debug(int u, siginfo_t *siginf, void *dat)
{
  struct dyn_page *dp;
  FILE *f;
  char filen[18]; /* 18 == strlen("or_page.%08x") + 1 */
  void *stack;
  int i, j;
  void *trace[10];
  int num_trace;
  char **trace_names;
 
  if(!sigsegv_state) {
    sigsegv_addr = siginf->si_addr;
  } else {
    printf("Nested SIGSEGV occured, dumping next chuck of info\n");
    sigsegv_state++;
  }
 
  /* First dump all the data that does not need dereferenceing to get */
  switch(sigsegv_state) {
  case 0:
    fflush(stdout);
    printf("Segmentation fault at %p (or address: 0x%"PRIxADDR")\n\n",
           sigsegv_addr, get_pc());
    sigsegv_state++;
  case 1:
    /* Run through the recompiled pages, dumping them to disk as we go */
    for(dp = cpu_state.dyn_pages; dp; dp = dp->next) {
      printf("Dumping%s page 0x%"PRIxADDR" recompiled to %p (len: %u) to disk\n",
             dp->dirty ? " dirty" : "", dp->or_page, dp->host_page,
             dp->host_len);
      fflush(stdout);
 
      sprintf(filen, "or_page.%"PRIxADDR, dp->or_page);
      if(!(f = fopen(filen, "w"))) {
        fprintf(stderr, "Unable to open %s to dump the recompiled page to: %s\n",
                filen, strerror(errno));
        continue;
      }
      if(fwrite(dp->host_page, dp->host_len, 1, f) < 1)
        fprintf(stderr, "Unable to write recompiled data to file: %s\n",
                strerror(errno));
 
      fclose(f);
    }
    sigsegv_state++;
  case 2:
    /* Dump the x-refs to disk */
    for(dp = cpu_state.dyn_pages; dp; dp = dp->next) {
      printf("Dumping cross references of 0x%"PRIxADDR" to disk\n", dp->or_page);
 
      sprintf(filen, "or_xref.%"PRIxADDR, dp->or_page);
      if(!(f = fopen(filen, "w"))) {
        fprintf(stderr, "Unable to open %s to dump cross references to: %s\n",
                filen, strerror(errno));
        continue;
      }
 
      fprintf(f, "Cross references in the page:\n");
      dump_xrefs(dp, f);
 
      fprintf(f, "\nCross references held by this page:\n");
      dump_held_xrefs(dp, f);
 
      fclose(f);
    }
    sigsegv_state++;
  case 3:
    /* Dump the contents of the stack */
    printf("Stack dump: ");
    fflush(stdout);
 
    num_trace = backtrace(trace, 10);
 
    trace_names = backtrace_symbols(trace, num_trace);
 
    stack = get_sp();
    printf("(of stack at %p, base: %p)\n", stack, rec_stack_base);
    fflush(stdout);
    for(i = 0; stack < rec_stack_base; i++, stack += 4) {
      printf(" <%i> 0x%08x", i, *(uint32_t *)stack);
      /* Try to find a symbolic name with this entry */
      for(j = 0; j < num_trace; j++) {
        if(trace[j] == *(void **)stack)
          printf(" <%s>", trace_names[j]);
      }
      printf("\n");
      fflush(stdout);
    }
    sigsegv_state++;
  case 4:
    sim_done();
  }
}
 
void dump_xrefs(struct dyn_page *dp, FILE *f)
{
  struct x_ref *xref;
 
  fprintf(f, "--- Cross reference dump for %"PRIxADDR" at %p ---\n",
          dp->or_page, dp->host_page);
  for(xref = dp->xrefs; xref; xref = xref->next) {
    fprintf(f, "x-refed or location: 0x%"PRIxADDR", host-location: %p, ref: %i\n",
            xref->or_addr, xref->dyn_addr, xref->ref);
  }
  fprintf(f, "--- Cross reference dump end ---\n");
}
 
void dump_held_xrefs(struct dyn_page *dp, FILE *f)
{
  struct x_ref **xrefs;
 
  fprintf(f, "--- Held cross reference dump for %"PRIxADDR" at %p ---\n",
          dp->or_page, dp->host_page);
  for(xrefs = dp->held_xrefs; *xrefs; xrefs++)
    fprintf(f, "Holds an x-ref to 0x%"PRIxADDR", host-location: %p, ref: %i\n",
            (*xrefs)->or_addr, (*xrefs)->dyn_addr, (*xrefs)->ref);
  fprintf(f, "--- Held cross reference dump end ---\n");
}
 
static void add_to_dp(struct dyn_page *new)
{
  struct dyn_page *cur;
  struct dyn_page *prev;
 
  /* Find the location to insert the address */
  for(cur = cpu_state.dyn_pages, prev = NULL; cur; prev = cur, cur = cur->next) {
    if(cur->or_page > new->or_page)
      break;
  }
 
  if(prev)
    prev->next = new;
  else
    cpu_state.dyn_pages = new;
  new->next = cur;
}
 
struct dyn_page *new_dp(oraddr_t page)
{
  struct dyn_page *dp = malloc(sizeof(struct dyn_page));
  dp->or_page = ADDR_PAGE(page);
 
  /* Allocate xref terminator */
  dp->xrefs = NULL;
 
  dp->held_xrefs = malloc(sizeof(struct x_ref *));
  dp->held_xrefs[0] = NULL;
 
  dp->host_len = 0;
  dp->host_page = NULL;
  dp->dirty = 1;
 
  add_to_dp(dp);
  return dp;
}
 
struct dyn_page *find_dynd_page(oraddr_t addr)
{
  struct dyn_page *cur = cpu_state.dyn_pages;
 
  addr &= ~(ADDR_C(PAGE_LEN) - 1);
  while(cur) {
    if(cur->or_page == addr)
      return cur;
    if(cur->or_page > addr)
      return NULL; /* The dyn_page linked list is ordered */
    cur = cur->next;
  }
  return NULL;
}
 
/* Finds the dynamicly recompiled location of the given or address */
struct x_ref *find_host_x_ref(struct x_ref *x_refs, oraddr_t addr)
{
  /* FIXME: Optimise this by knowing that the x_refs array is orderd */
  while(x_refs && (x_refs->or_addr != addr)) x_refs = x_refs->next;
 
  return x_refs;
}
 
static void remove_xref(struct dyn_page *dp, struct x_ref *xref)
{
  struct x_ref *prev_xref;
 
  if(dp->xrefs == xref) {
    dp->xrefs = xref->next;
    free(xref);
    return;
  }
 
  prev_xref = dp->xrefs;
  while(prev_xref->next != xref)
    prev_xref = prev_xref->next;
 
  prev_xref->next = xref->next;
  free(xref);
}
 
struct x_ref *find_held_x_ref(struct x_ref **held_xrefs, oraddr_t or_addr)
{
  /* FIXME: Order this list in add_to_held_xrefs below and optimise this */
  while(*held_xrefs && ((*held_xrefs)->or_addr != or_addr)) held_xrefs++;
  return *held_xrefs;
}
 
void add_to_held_xrefs(struct dyn_page *dp, struct x_ref *xref)
{
  unsigned int i;
 
  for(i = 0; dp->held_xrefs[i]; i++);
 
  dp->held_xrefs = realloc(dp->held_xrefs, sizeof(struct x_ref *) * (i + 2));
  dp->held_xrefs[i] = xref;
  dp->held_xrefs[++i] = NULL;
}
 
/* This is called whenever the immu is either enabled/disabled or reconfigured
 * while enabled.  This checks if an itlb miss would occour and updates the immu
 * hit delay counter */
void recheck_immu(int got_en_dis)
{
  oraddr_t addr = get_pc();
  extern int immu_ex_from_insn;
 
  if(cpu_state.delay_insn) {
    /* If an instruction pagefault or ITLB would occur, it must appear to have
     * come from the jumped-to address */
    if(ADDR_PAGE(addr) == ADDR_PAGE(cpu_state.pc_delay)) {
      immu_ex_from_insn = 1;
      immu_translate(addr + 4);
      immu_ex_from_insn = 0;
      runtime.sim.mem_cycles = 0;
    }
    return;
  }
 
  if(ADDR_PAGE(addr) == ADDR_PAGE(addr + 4)) {
    /* If the next instruction is on another page then the immu will be checked
     * when the jump to the next page happens */
    immu_ex_from_insn = 1;
    immu_translate(addr + 4);
    immu_ex_from_insn = 0;
    /* If we had am immu hit then runtime.sim.mem_cycles will hold the value
     * config.immu.hitdelay, but this value is added to the cycle when the next
     * instruction is run */
    runtime.sim.mem_cycles = 0;
  }
  /* Only update the cycle decrementer if the mmu got enabled or disabled */
  if(got_en_dis == IMMU_GOT_ENABLED)
    /* Add the mmu hit delay to the cycle counter */
    upd_cycles_dec(cpu_state.curr_page->delayr - config.immu.hitdelay);
  else if(got_en_dis == IMMU_GOT_DISABLED) {
    upd_cycles_dec(cpu_state.curr_page->delayr);
    /* Since we updated the cycle decrementer above the immu hit delay will not
     * be added to the cycle counter for this instruction.  Compensate for this
     * by adding it now */
    /* FIXME: This is not correct here.  In the complex execution model the hit
     * delay is added to runtime.sim.mem_cycles which is only joined with the
     * cycle counter after analysis() and before the scheduler would run.
     * Therefore the scheduler will still be correct but analysis() will produce
     * wrong results just for this one instruction. */
    add_to_cycles(config.immu.hitdelay);
  }
}
 
/* Runs the scheduler.  Called from except_handler (and dirtyfy_page below) */
void run_sched_out_of_line(int add_normal)
{
  int brk;
  oraddr_t pc = get_pc();
 
  if(!cpu_state.ts_current)
    upd_reg_from_t(pc);
 
  if(add_normal && do_stats) {
    cpu_state.iqueue.insn_addr = pc;
    cpu_state.iqueue.insn = eval_insn_direct(pc, &brk, 1);
    cpu_state.iqueue.insn_index = insn_decode(cpu_state.iqueue.insn);
    analysis(&cpu_state.iqueue);
  }
 
  /* Run the scheduler */
  if(add_normal)
    sched_add_cycles();
 
  op_join_mem_cycles();
  upd_sim_cycles();
  if(scheduler.job_queue->time <= 0)
    do_scheduler();
}
 
/* Signals a page as dirty */
void dirtyfy_page(struct dyn_page *dp)
{
  struct x_ref **held_xrefs;
  struct x_ref *xref;
  oraddr_t check;
 
  printf("Dirtyfying page 0x%"PRIxADDR"\n", dp->or_page);
 
  /* decrease the reference counts of the xrefs that we hold */
  for(held_xrefs = dp->held_xrefs; *held_xrefs; held_xrefs++)
    (*held_xrefs)->ref--;
  dp->held_xrefs = realloc(dp->held_xrefs, sizeof(struct x_ref *));
  dp->held_xrefs[0] = NULL;
 
  dp->dirty = 1;
 
  /* If the execution is currently in the page that was touched then recompile
   * it now and jump back to the point of execution */
  check = cpu_state.delay_insn ? cpu_state.pc_delay : get_pc() + 4;
  if(ADDR_PAGE(check) == dp->or_page) {
    run_sched_out_of_line(1);
    if(!(xref = find_host_x_ref(dp->xrefs, check))) {
      xref = add_to_xrefs(dp, check);
      add_to_held_xrefs(dp, xref);
    } else {
      if(!find_held_x_ref(dp->held_xrefs, check)) {
        add_to_held_xrefs(dp, xref);
        xref->ref++;
      }
    }
    recompile_page(dp);
 
    cpu_state.delay_insn = 0;
 
    /* Jump out to the next instruction */
    or_longjmp(xref->dyn_addr);
  }
}
 
static void ship_gprs_out_t(struct op_queue *opq, int end, unsigned int *reg_t)
{
  int i;
 
  for(i = 0; i < NUM_T_REGS; i++) {
    if(reg_t[i] < 32)
      gen_op_move_gpr_t[i][reg_t[i]](opq, end);
  }
}
 
static int find_unused_t(unsigned int *pres_t, unsigned int *reg_t)
{
  int empty = -1; /* Invalid */
  int i;
 
  /* Try to find a temporary that does not contain a register and is not
   * needed to be preserved */
  for(i = 0; i < NUM_T_REGS; i++) {
    if(!pres_t[i]) {
      empty = i;
      if(reg_t[i] > 31)
        return i;
    }
  }
  return empty;
}
 
/* Checks if there is enough space in dp->host_page, if not grow it */
void *enough_host_page(struct dyn_page *dp, void *cur, unsigned int *len,
                       unsigned int amount)
{
  unsigned int used = cur - dp->host_page;
 
  /* The array is long enough */
  if((used + amount) <= *len)
    return cur;
 
  /* Reallocate */
  *len += RECED_PAGE_ENLARGE_BY;
 
  if(!(dp->host_page = realloc(dp->host_page, *len))) {
    fprintf(stderr, "OOM\n");
    exit(1);
  }
 
  return dp->host_page + used;
}
 
/* Adds an operation to the opq */
void add_to_opq(struct op_queue *opq, int end, int op)
{
  if(opq->num_ops == opq->ops_len) {
    opq->ops_len += OPS_ENLARGE_BY;
    if(!(opq->ops = realloc(opq->ops, opq->ops_len * sizeof(int)))) {
      fprintf(stderr, "OOM\n");
      exit(1);
    }
  }
 
  if(end)
    opq->ops[opq->num_ops] = op;
  else {
    /* Shift everything over by one */
    memmove(opq->ops + 1, opq->ops, opq->num_ops* sizeof(int));
    opq->ops[0] = op;
  }
 
  opq->num_ops++;
}
 
/* Adds a parameter to the opq */
void add_to_op_params(struct op_queue *opq, int end, unsigned long param)
{
  if(opq->num_ops_param == opq->ops_param_len) {
    opq->ops_param_len += OPS_ENLARGE_BY * sizeof(int);
    if(!(opq->ops_param = realloc(opq->ops_param, opq->ops_param_len))) {
      fprintf(stderr, "OOM\n");
      exit(1);
    }
  }
 
  if(end)
    opq->ops_param[opq->num_ops_param] = param;
  else {
    /* Shift everything over by one */
    memmove(opq->ops_param + 1, opq->ops_param, opq->num_ops_param);
    opq->ops_param[0] = param;
  }
 
  opq->num_ops_param++;
}
 
/* Function to guard against rogue ret instructions in the operations */
void dyn_ret_stack_prot(void)
{
  fprintf(stderr, "An operation (I have no clue which) has a ret statement in it\n");
  fprintf(stderr, "Good luck debugging it!\n");
 
  exit(1);
}
 
/* Jumps out to some Openrisc address */
void jump_dyn_code(oraddr_t addr)
{
  set_pc(addr);
  do_jump(addr);
}
 
/* Initialises the recompiler */
void init_dyn_recomp(void)
{
  struct sigaction sigact;
  struct op_queue *opq;
  struct x_ref *xref;
  unsigned int i;
 
  cpu_state.opqs = NULL;
 
  /* Allocate the operation queue list (+1 for the page chaining) */
  for(i = 0; i < (PAGE_LEN / 4) + 1; i++) {
    if(!(opq = malloc(sizeof(struct op_queue)))) {
      fprintf(stderr, "OOM\n");
      exit(1);
    }
 
    /* initialise some fields */
    opq->ops_len = 0;
    opq->ops = NULL;
    opq->ops_param_len = 0;
    opq->ops_param = NULL;
 
    if(cpu_state.opqs)
      cpu_state.opqs->prev = opq;
 
    opq->next = cpu_state.opqs;
    cpu_state.opqs = opq;
  }
 
  opq->prev = NULL;
 
  /* Allocate the x-ref structures that will be used for the infinite loop
   * instruction (l.j 0).  Allocate a whole page's worth just to make sure that
   * we will have enough */
  for(i = 0; i < (PAGE_LEN / 4); i++) {
    if(!(xref = malloc(sizeof(struct x_ref)))) {
      fprintf(stderr, "Out-of-memory while allocateing x-ref structures\n");
      exit(1);
    }
    xref->next = cpu_state.inf_xrefs;
    cpu_state.inf_xrefs = xref;
  }
 
  /* Just some value that we'll use as the base for our stack */
  rec_stack_base = get_sp();
 
  cpu_state.curr_page = NULL;
  cpu_state.dyn_pages = NULL;
 
  /* Register our segmentation fault handler */
  sigact.sa_sigaction = dyn_sigsegv_debug;
  memset(&sigact.sa_mask, 0, sizeof(sigact.sa_mask));
  sigact.sa_flags = SA_SIGINFO | SA_NOMASK;
  if(sigaction(SIGSEGV, &sigact, NULL))
    printf("WARN: Unable to install SIGSEGV handler! Don't expect to be able to debug the recompiler.\n");
 
  /* Allocate memory for the rfe corss reference cache */
  if(!(cpu_state.rfe_held_xrefs = malloc(sizeof(struct xref *) * NUM_RFE_HELD))) {
    printf("OOM\n");
    exit(1);
  }
  cpu_state.rfe_held_xref_pos = 0;
  memset(cpu_state.rfe_held_xrefs, 0, sizeof(struct xref *) * NUM_RFE_HELD);
 
  init_dyn_rec();
 
  /* FIXME: Find a better place for this */
    { /* Needed by execution */
      extern int do_stats;
      do_stats = config.cpu.dependstats || config.cpu.superscalar || config.cpu.dependstats
              || config.sim.history || config.sim.exe_log;
    }
 
  printf("Recompile engine up and running\n");
}
 
/* rec_page is a physical address */
void recompile_page(struct dyn_page *dyn)
{
  unsigned int j, k;
  unsigned int reg_t[NUM_T_REGS];
  unsigned int pres_t[NUM_T_REGS]; /* Which temporary to preserve */
  unsigned int insn_index;
  int delay_insn = 0; /* Is the next instruction to be decoded in a delay slot*/
  enum insn_type delay_insn_type = 0;
  uint32_t insn;
  int param_t[3]; /* Which temporary the parameters reside in */
  int param_r[3]; /* is parameter a register */
  orreg_t param[3];
  int param_num;
  struct op_queue *opq = NULL;
  oraddr_t rec_addr = dyn->or_page;
  oraddr_t rec_page = dyn->or_page;
  struct x_ref *xref;
  int breakp;
  struct dyn_page *prev_dp;
 
  struct insn_op_struct *opd;
 
  /* The start of the next page */
  rec_page += PAGE_LEN;
 
  printf("Recompileing page %"PRIxADDR"\n", rec_addr);
  fflush(stdout);
 
  /* Mark all temporaries as not containing a register */
  for(j = 0; j < NUM_T_REGS; j++)
    reg_t[j] = 32; /* Out-of-range registers */
 
  dyn->delayr = -verify_memoryarea(rec_addr)->delayr;
 
  dyn->carrys_delay_slot = 0;
 
  /* Check if the previous page carries a delay slot over to this page */
  if((prev_dp = find_dynd_page(rec_addr - PAGE_LEN)))
    delay_insn = prev_dp->carrys_delay_slot;
 
  for(opq = cpu_state.opqs; rec_addr < rec_page; rec_addr += 4, opq = opq->next) {
    opq->num_ops = 0;
    opq->num_ops_param = 0;
    opq->jump_local = 0;
 
    opq->insn_addr = rec_addr;
 
    breakp = 0;
    insn = eval_insn(rec_addr, &breakp);
 
    /* FIXME: If a breakpoint is set at this location, insert exception code */
    if(breakp) {
      fprintf(stderr, "FIXME: Insert breakpoint code\n");
    }
 
    insn_index = insn_decode(insn);
 
    /* FIXME: Optimise this by knowing that dyn->x_refs is ordered (ie. Don't
     *        call find_host_x_ref) */
    /* Check if this location is cross referenced */
    if((xref = find_host_x_ref(dyn->xrefs, rec_addr))) {
      /* If the x-refs reference count reached zero remove it */
      if(xref->ref) {
        /* If the current address is cross-referenced, the temporaries shall be
         * in an undefined state, so we must assume that no registers reside in
         * them */
        /* Ship out the current set of registers from the temporaries */
        if(opq->prev) {
          ship_gprs_out_t(opq->prev, 1, reg_t);
        }
        for(j = 0; j < NUM_T_REGS; j++)
          reg_t[j] = 32;
      } else {
        /* Remove x-ref */
        remove_xref(dyn, xref);
      }
    }
 
    memcpy(opq->reg_t, reg_t, sizeof(reg_t));
 
    /* Check if we have an illegal instruction */
    if(insn_index == -1) {
      gen_l_invalid(opq, param_t, param, delay_insn);
      if(delay_insn) {
        /* There is no need to do any jump handleing stuff as the instruction
         * will generate an exception */
        if(opq->prev->jump_local == 2) {
          opq->prev->xref->next = cpu_state.inf_xrefs;
          cpu_state.inf_xrefs = opq->prev->xref;
        }
        opq->prev->jump_local = 0;
        delay_insn = 0;
      }
      continue;
    }
 
    /* figure out instruction operands */
    for(j = 0; j < NUM_T_REGS; j++)
      pres_t[j] = 0;
 
    param_t[0] = T_NONE;
    param_t[1] = T_NONE;
    param_t[2] = T_NONE;
    param_r[0] = 0;
    param_r[1] = 0;
    param_r[2] = 0;
    param_num = 0;
 
    opd = op_start[insn_index];
    while(1) {
      param[param_num] = eval_operand_val(insn, opd);
 
      if(opd->type & OPTYPE_REG) {
        /* check which temporary the register is in, if any */
        for(j = 0; j < NUM_T_REGS; j++) {
          if(reg_t[j] == param[param_num]) {
            param_t[param_num] = j;
            pres_t[j] = 1;
          }
        }
      }
 
      param_num++;
      while(!(opd->type & OPTYPE_OP)) opd++;
      if(opd->type & OPTYPE_LAST)
        break;
      opd++;
    }
 
    opd = op_start[insn_index];
 
    /* Before an exception takes place, all registers must be stored. */
    if((or32_opcodes[insn_index].func_unit == it_exception)) {
      if(opq->prev) {
        ship_gprs_out_t(opq->prev, 1, reg_t);
        for(j = 0; j < NUM_T_REGS; j++) {
          opq->prev->reg_t[j] = 32;
          reg_t[j] = 32;
        }
      }
    }
 
    for(j = 0; j < param_num; j++, opd++) {
      while(!(opd->type & OPTYPE_OP)) opd++;
      if(!(opd->type & OPTYPE_REG))
        continue;
 
      /* Never, ever, move r0 into a temporary */
      if(!param[j])
        continue;
 
      /* Check if this register has been moved into a temporary in a previous
       * operand */
      for(k = 0; k < NUM_T_REGS; k++) {
        if(reg_t[k] == param[j]) {
          /* Yes, this register is already in a temporary */
          pres_t[k] = 1;
          reg_t[k] = param[j];
          param_t[j] = k;
          break;
        }
      }
      if(k != NUM_T_REGS)
        continue;
 
      if((param_t[j] != T_NONE))
        continue;
 
      /* Search for an unused temporary */
      k = find_unused_t(pres_t, reg_t);
      if(reg_t[k] < 32) {
        gen_op_move_gpr_t[k][reg_t[k]](opq->prev, 1);
        opq->reg_t[k] = 32;
      }
      pres_t[k] = 1;
      reg_t[k] = param[j];
      param_t[j] = k;
      /* FIXME: Only generate code to move the register into a temporary if it
       *        is used as a source operand */
      gen_op_move_t_gpr[k][reg_t[k]](opq, 1);
    }
 
    /* FIXME: Do this in a more elegent way */
    if(!strncmp(or32_opcodes[insn_index].name, "l.jal", 5)) {
      /* In the case of a l.jal instruction, make sure that LINK_REGNO is not in
       * a temporary.  The problem is that the l.jal(r) instruction stores the
       * `return address' in LINK_REGNO.  The temporaries are shiped out only
       * after the delay slot instruction has executed and so it overwrittes the
       * `return address'. */
      for(k = 0; k < NUM_T_REGS; k++) {
        if(reg_t[k] == LINK_REGNO) {
          gen_op_move_gpr_t[k][LINK_REGNO](opq, 1);
          reg_t[k] = 32;
          opq->reg_t[k] = 32;
          break;
        }
      }
    }
 
    /* Store the state of the temporaries into dyn->ts */
    dyn->ts[(rec_addr & (PAGE_LEN - 1)) / 2] = 0;
    if(reg_t[0] < 32)
      dyn->ts[(rec_addr & (PAGE_LEN - 1)) / 2] = reg_t[0];
    if(reg_t[1] < 32)
      dyn->ts[(rec_addr & (PAGE_LEN - 1)) / 2] |= reg_t[1] << 5;
    if(reg_t[2] < 32)
      dyn->ts[(rec_addr & (PAGE_LEN - 1)) / 2] |= reg_t[2] << 10;
 
    /* To get the execution log correct for instructions like l.lwz r4,0(r4) the
     * effective address needs to be calculated before the instruction is
     * simulated */
    if(do_stats) {
      /* Find any disposition in the instruction */
      opd = op_start[insn_index];
      for(j = 0; j < param_num; j++, opd++) {
        while(!(opd->type & OPTYPE_OP)) opd++;
        if(!(opd->type & OPTYPE_DIS))
          continue;
 
        if(!param[j + 1])
          gen_op_store_insn_ea(opq, 1, param[j]);
        else
          calc_insn_ea_table[param_t[j + 1]](opq, 1, param[j]);
      }
    }
 
    or32_opcodes[insn_index].exec(opq, param_t, param, delay_insn);
 
    /* If any sort of analysis is done, store all temporaries and run
     * analysis() */
    if(do_stats) {
      ship_gprs_out_t(opq, 1, reg_t);
      for(j = 0; j < NUM_T_REGS; j++)
        reg_t[j] = 32;
 
      gen_op_analysis(opq, 1, insn_index, insn);
    }
 
    /* The call to join_mem_cycles() could be put into the individual operations
     * that emulate the load/store instructions, but then it would be added to
     * the cycle counter before analysis() is called, which not how the complex
     * execution modell does it. */
    if((or32_opcodes[insn_index].func_unit == it_load) ||
       (or32_opcodes[insn_index].func_unit == it_store))
      gen_op_join_mem_cycles(opq, 1);
 
    /* If a delay sloted instruction is in the delay slot, avoid doing a jump on
     * the first delay sloted instruction.  The problem with not doing the above
     * is that the 0x00000000 instruction is a jump instruction, which is used
     * for padding, and if there ends up being a jump instruction directly after
     * some padding and the code jumps to this location (as with the mmu test)
     * the jump instruction will set cpu_state.pc_delay but code will get
     * generated after the jump instruction and before the delay slot
     * instruciton to check cpu_state.pc_delay and jump out if it is set and so
     * we end up jumping out to the padding instruction.  With some thought, the
     * 0x00000000 opcode could really have been encoded to some arithmetic
     * operation that would end up nop-ing (or better yet, to the l.nop 0
     * instruction itself) */
    /* If we came up to a page local jump and because it is the delay slot of
     * another delay sloted instruction the case below is skipped and
     * opq->prev->jump_local will remain set to 1, fix this by reseting it now*/
    if(delay_insn && ((or32_opcodes[insn_index].func_unit == it_jump) ||
                      (or32_opcodes[insn_index].func_unit == it_branch))) {
      /* We don't generate code to do the relocation so there will be none.
       * Avoid haveing a reference to it */
      /* Also remove the cross reference to it */
      if(opq->prev) {
        if(opq->prev->jump_local == 2) {
          opq->prev->xref->next = cpu_state.inf_xrefs;
          cpu_state.inf_xrefs = opq->prev->xref;
        }
        opq->prev->jump_local = 0;
      }
      delay_insn = 0;
    }
 
    /* In the case of an instruction in the delay slot the pc must be updated
     * before op_do_sched runs because if it so happens to generate an exception
     * it will think that we are still executeing the delay slot instruction
     * which infact we have just executed and then SPR_EPCR_BASE will end up
     * pointing to the delay slot instruction, which is wrong.  If the delay
     * slot instruction is an exception instruction (l.trap/l.sys) the exception
     * must appear to have been generated in the delay slot */
    if(delay_insn && (or32_opcodes[insn_index].func_unit != it_exception)) {
      if(xref || (delay_insn_type == it_branch))
        gen_op_set_pc_preemt_check(opq, 1);
      else /* delay_insn_tyte == it_jump */
        gen_op_set_pc_preemt(opq, 1);
      /* Move temporaries to their permanent storage */
      ship_gprs_out_t(opq, 1, reg_t);
    }
 
    /* Same reason as for the above case */
    if(or32_opcodes[insn_index].func_unit == it_exception) {
      /* FIXME: Do the instruction switch below in a more elegent way */
      if(!strcmp(or32_opcodes[insn_index].name, "l.rfe")) {
        gen_op_set_rfe_pc(opq, 1);
      } else if(!strcmp(or32_opcodes[insn_index].name, "l.sys")) {
        gen_op_set_except_pc(opq, 1, EXCEPT_SYSCALL - 4);
      } else { /* or32_opcodes[insn_index].name == "l.trap" */
        gen_op_set_except_pc(opq, 1, EXCEPT_TRAP - 4);
      }
      gen_op_set_ts_current(opq, 1);
    }
 
    gen_op_do_sched(opq, 1);
 
    /* If this is an exception instruction then we still need to perform the
     * exception */
    if(or32_opcodes[insn_index].func_unit == it_exception) {
      /* FIXME: Do the instruction switch below in a more elegent way */
      if(!strcmp(or32_opcodes[insn_index].name, "l.rfe")) {
        gen_op_rfe(opq, 1);
      } else if(!strcmp(or32_opcodes[insn_index].name, "l.sys")) {
        gen_op_do_except(opq, 1, EXCEPT_SYSCALL);
      } else { /* or32_opcodes[insn_index].name == "l.trap" */
        gen_op_do_except(opq, 1, EXCEPT_TRAP);
      }
    }
 
    /* FIXME: If the delay slot is cross referenced after we have stuck the jump
     * instruction in the operations queue we will genererate temporary->
     * register code after the jump, which will be unreachable.  This is no
     * problem as all temporaries are stored in anticipation for a jump. */
    /* FIXME: If the delay slot is cross referenced we should generate the
     * conditional jump code as we do below.  This will not happen if the delay
     * slot is cross referenced after we generate the operations for the jump */
    /* FIXME: If the instruction in the delay slot is an exception instruction
     * the code that we generate below will be unreachable since the exception
     * instruction jumps to the exection vector */
    /* Generate code to jump out to the proper location */
    if(delay_insn) {
      for(j = 0; j < NUM_T_REGS; j++)
        reg_t[j] = 32;
 
      if(xref || (delay_insn_type == it_branch)) {
        /* If the delay-slot instruction is cross referenced, then we have to
         * check env->delay_insn */
        if(opq->prev && opq->prev->jump_local) {
          gen_op_jmp_imm_check(opq, 1, 0);
          opq->prev->jump_local_loc = &opq->ops_param[opq->num_ops_param - 1];
        } else {
          gen_op_do_jump_check(opq, 1);
        }
      } else if(delay_insn_type == it_jump) {
        gen_op_clear_delay_insn(opq, 1);
        if(opq->prev && opq->prev->jump_local) {
          /* The 0 will get patched when the page-local jumps get patched */
          gen_op_jmp_imm(opq, 1, 0);
          /* FIXME: opq->ops_param is realloced with realloc and so we risk a
           * reallocation in which the location ends up moveing in memory */
          opq->prev->jump_local_loc = &opq->ops_param[opq->num_ops_param - 1];
        } else {
          gen_op_do_jump(opq, 1);
        }
      }
      delay_insn = 0;
    }
 
    /* Set flag for next instruction to be in a delay slot */
    if((or32_opcodes[insn_index].func_unit == it_jump) ||
       (or32_opcodes[insn_index].func_unit == it_branch)) {
      delay_insn = 1;
      delay_insn_type = or32_opcodes[insn_index].func_unit;
    }
  }
 
  if(delay_insn) {
    dyn->carrys_delay_slot = 1;
    /* Quick hack to avoid dereferencing an uninitialised pointer below with
     * *opq->jump_local_loc */
    if(opq->prev->jump_local == 2) {
      /* FIXME: In this case the delay slot instruction won't get executed */
      opq->prev->xref->next = cpu_state.inf_xrefs;
      cpu_state.inf_xrefs = opq->prev->xref;
    }
    opq->prev->jump_local = 0;
  }
 
  dyn->dirty = 0;
 
  /* Ship temporaries out to the corrisponding registers */
  ship_gprs_out_t(opq->prev, 1, reg_t);
 
  opq->num_ops = 0;
  opq->num_ops_param = 0;
  opq->jump_local = 0;
 
  /* Insert code to jump to the next page */
  gen_op_set_ts_current(opq, 1);
  gen_op_do_jump_pc(opq, 1);
 
  /* Generate the code */
  gen_code(cpu_state.opqs, dyn);
 
  /* Patch the x-ref table */
  for(xref = dyn->xrefs; xref; xref = xref->next)
    xref->dyn_addr = dyn->host_page + (unsigned int)xref->dyn_addr;
 
  /* Search for page-local jumps */
  for(opq = cpu_state.opqs; opq; opq = opq->next) {
    if(opq->jump_local) {
      if(opq->jump_local == 2)
        /* This cross reference was not patched above so patch it now */
        opq->xref->dyn_addr = dyn->host_page + (unsigned int)opq->xref->dyn_addr;
 
      *opq->jump_local_loc = (unsigned int)opq->xref->dyn_addr;
      if(opq->jump_local == 2) {
        /* Return the xref to the pool of infinite loop cross references */
        opq->xref->next = cpu_state.inf_xrefs;
        cpu_state.inf_xrefs = opq->xref;
      }
    }
  }
 
  /* Patch the relocations */
  patch_relocs(cpu_state.opqs, dyn->host_page);
 
  /* FIXME: Fix the issue below in a more elegent way */
  /* Since eval_insn is called to get the instruction, runtime.sim.mem_cycles is
   * updated but the recompiler expectes it to start a 0, so reset it */
  runtime.sim.mem_cycles = 0;
 
#if 0
  This is very usefull during debuging
  /* Count the number of infinite loop cross references (to make sure that we
   * returned them all) */
  for(j = 0, xref = cpu_state.inf_xrefs; xref; xref = xref->next) {
    printf("Cross reference to %"PRIxADDR" is here\n", xref->or_addr);
    j++;
  }
 
  if(j != (PAGE_LEN / 4)) {
    fprintf(stderr, "Infinite loop cross references are leaked!\n");
    fprintf(stderr, "Number in free list now: %i, meant to be: %i\n", j, PAGE_LEN / 4);
    exit(1);
  }
#endif
 
}
 
struct x_ref *add_to_xrefs(struct dyn_page *dp, oraddr_t addr)
{
  struct x_ref *new;
  struct x_ref *cur;
  struct x_ref *prev;
 
  new = malloc(sizeof(struct x_ref));
 
  new->ref = 1;
  new->or_addr = addr;
 
  /* Find the location to insert the address */
  for(cur = dp->xrefs, prev = NULL; cur; prev = cur, cur = cur->next) {
    if(cur->or_addr > addr)
      break;
  }
 
  if(prev)
    prev->next = new;
  else
    dp->xrefs = new;
  new->next = cur;
 
  return new;
}
 
/* Returns non-zero if the jump is into this page, 0 otherwise */
static int find_jump_loc(oraddr_t j_ea, struct op_queue *opq)
{
  struct dyn_page *dp;
  int i;
  struct x_ref *xref = NULL;
  int *ops;
 
  /* Mark the jump as non page local if the delay slot instruction is on the
   * next page to the jump instruction.  This should not be needed */
  if((ADDR_PAGE(j_ea) != ADDR_PAGE(opq->insn_addr)) ||
     (ADDR_PAGE(opq->insn_addr) != ADDR_PAGE(opq->insn_addr + 4)))
    /* We can't do anything as the j_ea (as passed to find_jump_loc) is a
     * VIRTUAL offset and the next physical page may not be the next VIRTUAL
     * page */
    return 0;
 
  /* The jump is into the page currently undergoing dynamic recompilation */
 
  /* FIXME: It would be great if we didn't have to do this (find_dynd...) (it is
   *        already passed to recompile_page) */
  dp = find_dynd_page(j_ea);
 
  /* Check if we have already x-refed this location */
  if((xref = find_host_x_ref(dp->xrefs, j_ea))) {
    /* If we have already x-refed this location, don't x-ref it again */
    if(!find_held_x_ref(dp->held_xrefs, j_ea)) {
      xref->ref++;
      add_to_held_xrefs(dp, xref);
    }
  } else {
    /* Stick this address into the page's x-ref table */
    xref = add_to_xrefs(dp, j_ea);
    add_to_held_xrefs(dp, xref);
  }
 
  opq->xref = xref;
 
  /* If we haven't got to the location of the jump, everything is ok */
  if(j_ea > opq->insn_addr)
    return 1;
 
  /* Insert temporary -> register code before the jump ea and register ->
   * temporary at the x-ref address */
  while(opq->insn_addr > j_ea) opq = opq->prev;
  if(!opq->prev)
    /* We're at the begining of a page, no need to do anything */
    return 1;
 
  /* Found location, insert code */
  ship_gprs_out_t(opq->prev, 1, opq->reg_t);
 
  for(i = 0; i < NUM_T_REGS; i++) {
    if(opq->reg_t[i] < 32) {
      gen_op_move_t_gpr[i][opq->reg_t[i]](opq, 0);
      opq->reg_t[i] = 32;
    }
  }
 
  /* In the event of a page local jump that jumps backwards (l.j -4) the cross
   * reference to the target may not have existed when the jump-ed to adress was
   * recompiled and if the jump-ed to address is in the delay slot of another
   * jump instruction an op_jmp_imm_check operation must be generated and not an
   * op_jmp_imm operation */
  for(ops = opq->ops, i = 0; i < opq->num_ops; i++, ops++) {
    if(*ops == op_jmp_imm_indx)
      *ops = op_jmp_imm_check_indx;
    else if(*ops == op_set_pc_preemt_indx)
      *ops = op_set_pc_preemt_check_indx;
  }
 
  return 1;
}
 
/*------------------------------[ Operation generation for an instruction ]---*/
/* FIXME: Flag setting is not done in any instruction */
/* FIXME: Since r0 is not moved into a temporary, check all arguments below! */
 
static const generic_gen_op clear_t[NUM_T_REGS] =
 { gen_op_clear_t0, gen_op_clear_t1, gen_op_clear_t2 };
 
static const generic_gen_op move_t_t[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { NULL, gen_op_move_t0_t1, gen_op_move_t0_t2 },
/* param0 -> t1 */ { gen_op_move_t1_t0, NULL, gen_op_move_t1_t2 },
/* param0 -> t2 */ { gen_op_move_t2_t0, gen_op_move_t2_t1, NULL } };
 
static const imm_gen_op mov_t_imm[NUM_T_REGS] =
 { gen_op_t0_imm, gen_op_t1_imm, gen_op_t2_imm };
 
static const imm_gen_op l_add_imm_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_add_imm_t0_t0, gen_op_add_imm_t0_t1, gen_op_add_imm_t0_t2 },
/* param0 -> t1 */ { gen_op_add_imm_t1_t0, gen_op_add_imm_t1_t1, gen_op_add_imm_t1_t2 },
/* param0 -> t2 */ { gen_op_add_imm_t2_t0, gen_op_add_imm_t2_t1, gen_op_add_imm_t2_t2 } };
 
static const generic_gen_op l_add_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */              {
/* param0 -> t0, param1 -> t0 */ { gen_op_add_t0_t0_t0, gen_op_add_t0_t0_t1, gen_op_add_t0_t0_t2 },
/* param0 -> t0, param1 -> t1 */ { gen_op_add_t0_t1_t0, gen_op_add_t0_t1_t1, gen_op_add_t0_t1_t2 },
/* param0 -> t0, param1 -> t2 */ { gen_op_add_t0_t2_t0, gen_op_add_t0_t2_t1, gen_op_add_t0_t2_t2 } },
/* param0 -> t1 */              {
/* param0 -> t1, param1 -> t0 */ { gen_op_add_t1_t0_t0, gen_op_add_t1_t0_t1, gen_op_add_t1_t0_t2 },
/* param0 -> t1, param1 -> t1 */ { gen_op_add_t1_t1_t0, gen_op_add_t1_t1_t1, gen_op_add_t1_t1_t2 },
/* param0 -> t1, param1 -> t2 */ { gen_op_add_t1_t2_t0, gen_op_add_t1_t2_t1, gen_op_add_t1_t2_t2 } },
/* param0 -> t2 */              {
/* param0 -> t2, param1 -> t0 */ { gen_op_add_t2_t0_t0, gen_op_add_t2_t0_t1, gen_op_add_t2_t0_t2 },
/* param0 -> t2, param1 -> t1 */ { gen_op_add_t2_t1_t0, gen_op_add_t2_t1_t1, gen_op_add_t2_t1_t2 },
/* param0 -> t2, param1 -> t2 */ { gen_op_add_t2_t2_t0, gen_op_add_t2_t2_t1, gen_op_add_t2_t2_t2 } } };
 
void gen_l_add(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0])
    /* Screw this, the operation shall do nothing */
    return;
 
  if(!param[1] && !param[2]) {
    /* Just clear param_t[0] */
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  if(!param[2]) {
    if(param[0] != param[1])
      /* This just moves a register */
      move_t_t[param_t[0]][param_t[1]](opq, 1);
    return;
  }
 
  if(!param[1]) {
    /* Check if we are moveing an immediate */
    if(param_t[2] == T_NONE) {
      /* Yep, an immediate */
      mov_t_imm[param_t[0]](opq, 1, param[2]);
      return;
    }
    /* Just another move */
    if(param[0] != param[2])
      move_t_t[param_t[0]][param_t[2]](opq, 1);
    return;
  }
 
  /* Ok, This _IS_ an add... */
  if(param_t[2] == T_NONE)
    /* immediate */
    l_add_imm_t_table[param_t[0]][param_t[1]](opq, 1, param[2]);
  else
    l_add_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1);
}
 
static const generic_gen_op l_addc_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */              {
/* param0 -> t0, param1 -> t0 */ { gen_op_addc_t0_t0_t0, gen_op_addc_t0_t0_t1, gen_op_addc_t0_t0_t2 },
/* param0 -> t0, param1 -> t1 */ { gen_op_addc_t0_t1_t0, gen_op_addc_t0_t1_t1, gen_op_addc_t0_t1_t2 },
/* param0 -> t0, param1 -> t2 */ { gen_op_addc_t0_t2_t0, gen_op_addc_t0_t2_t1, gen_op_addc_t0_t2_t2 } },
/* param0 -> t1 */              {
/* param0 -> t1, param1 -> t0 */ { gen_op_addc_t1_t0_t0, gen_op_addc_t1_t0_t1, gen_op_addc_t1_t0_t2 },
/* param0 -> t1, param1 -> t1 */ { gen_op_addc_t1_t1_t0, gen_op_addc_t1_t1_t1, gen_op_addc_t1_t1_t2 },
/* param0 -> t1, param1 -> t2 */ { gen_op_addc_t1_t2_t0, gen_op_addc_t1_t2_t1, gen_op_addc_t1_t2_t2 } },
/* param0 -> t2 */              {
/* param0 -> t2, param1 -> t0 */ { gen_op_addc_t2_t0_t0, gen_op_addc_t2_t0_t1, gen_op_addc_t2_t0_t2 },
/* param0 -> t2, param1 -> t1 */ { gen_op_addc_t2_t1_t0, gen_op_addc_t2_t1_t1, gen_op_addc_t2_t1_t2 },
/* param0 -> t2, param1 -> t2 */ { gen_op_addc_t2_t2_t0, gen_op_addc_t2_t2_t1, gen_op_addc_t2_t2_t2 } } };
 
void gen_l_addc(struct op_queue *opq, int param_t[3], orreg_t param[3],
                int delay_slot)
{
  if(!param[0])
    /* Screw this, the operation shall do nothing */
    return;
 
  /* FIXME: More optimisations !! (...and immediate...) */
  l_addc_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1);
}
 
static const imm_gen_op l_and_imm_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_and_imm_t0_t0, gen_op_and_imm_t0_t1, gen_op_and_imm_t0_t2 },
/* param0 -> t1 */ { gen_op_and_imm_t1_t0, gen_op_and_imm_t1_t1, gen_op_and_imm_t1_t2 },
/* param0 -> t2 */ { gen_op_and_imm_t2_t0, gen_op_and_imm_t2_t1, gen_op_and_imm_t2_t2 } };
 
static const generic_gen_op l_and_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */              {
/* param0 -> t0, param1 -> t0 */ { NULL, gen_op_and_t0_t0_t1, gen_op_and_t0_t0_t2 },
/* param0 -> t0, param1 -> t1 */ { gen_op_and_t0_t1_t0, gen_op_and_t0_t1_t1, gen_op_and_t0_t1_t2 },
/* param0 -> t0, param1 -> t2 */ { gen_op_and_t0_t2_t0, gen_op_and_t0_t2_t1, gen_op_and_t0_t2_t2 } },
/* param0 -> t1 */              {
/* param0 -> t1, param1 -> t0 */ { gen_op_and_t1_t0_t0, gen_op_and_t1_t0_t1, gen_op_and_t1_t0_t2 },
/* param0 -> t1, param1 -> t1 */ { gen_op_and_t1_t1_t0, NULL, gen_op_and_t1_t1_t2 },
/* param0 -> t1, param1 -> t2 */ { gen_op_and_t1_t2_t0, gen_op_and_t1_t2_t1, gen_op_and_t1_t2_t2 } },
/* param0 -> t2 */              {
/* param0 -> t2, param1 -> t0 */ { gen_op_and_t2_t0_t0, gen_op_and_t2_t0_t1, gen_op_and_t2_t0_t2 },
/* param0 -> t2, param1 -> t1 */ { gen_op_and_t2_t1_t0, gen_op_and_t2_t1_t1, gen_op_and_t2_t1_t2 },
/* param0 -> t2, param1 -> t2 */ { gen_op_and_t2_t2_t0, gen_op_and_t2_t2_t1, NULL } } };
 
void gen_l_and(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0])
    /* Screw this, the operation shall do nothing */
    return;
 
  if(!param[1] || !param[2]) {
    /* Just clear param_t[0] */
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  if((param[0] == param[1] == param[2]) && (param_t[2] != T_NONE))
    return;
 
  if(param_t[2] == T_NONE)
    l_and_imm_t_table[param_t[0]][param_t[1]](opq, 1, param[2]);
  else
    l_and_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1);
}
 
void gen_l_bf(struct op_queue *opq, int param_t[3], orreg_t param[3],
              int delay_slot)
{
  opq->jump_local = find_jump_loc(opq->insn_addr + (orreg_t)(param[0] << 2), opq);
  gen_op_check_flag(opq, 1, param[0] << 2);
}
 
void gen_l_bnf(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  opq->jump_local = find_jump_loc(opq->insn_addr + (orreg_t)(param[0] << 2), opq);
  gen_op_check_not_flag(opq, 1, param[0] << 2);
}
 
static const generic_gen_op l_cmov_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */              {
/* param0 -> t0, param1 -> t0 */ { NULL, gen_op_cmov_t0_t0_t1, gen_op_cmov_t0_t0_t2 },
/* param0 -> t0, param1 -> t1 */ { gen_op_cmov_t0_t1_t0, NULL, gen_op_cmov_t0_t1_t2 },
/* param0 -> t0, param1 -> t2 */ { gen_op_cmov_t0_t2_t0, gen_op_cmov_t0_t2_t1, NULL } },
/* param0 -> t1 */              {
/* param0 -> t1, param1 -> t0 */ { NULL, gen_op_cmov_t1_t0_t1, gen_op_cmov_t1_t0_t2 },
/* param0 -> t1, param1 -> t1 */ { gen_op_cmov_t1_t1_t0, NULL, gen_op_cmov_t1_t1_t2 },
/* param0 -> t1, param1 -> t2 */ { gen_op_cmov_t1_t2_t0, gen_op_cmov_t1_t2_t1, NULL } },
/* param0 -> t2 */              {
/* param0 -> t2, param1 -> t0 */ { NULL, gen_op_cmov_t2_t0_t1, gen_op_cmov_t2_t0_t2 },
/* param0 -> t2, param1 -> t1 */ { gen_op_cmov_t2_t1_t0, NULL, gen_op_cmov_t2_t1_t2 },
/* param0 -> t2, param1 -> t2 */ { gen_op_cmov_t2_t2_t0, gen_op_cmov_t2_t2_t1, NULL } } };
 
/* FIXME: Check if either opperand 1 or 2 is r0 */
void gen_l_cmov(struct op_queue *opq, int param_t[3], orreg_t param[3],
                int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1] && !param[2]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  if(param[1] == param[2]) {
    move_t_t[param_t[0]][param_t[1]](opq, 1);
    return;
  }
 
  if((param[1] == param[2]) && (param[0] == param[1]))
    return;
 
  l_cmov_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1);
}
 
void gen_l_cust1(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
}
 
void gen_l_cust2(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
}
 
void gen_l_cust3(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
}
 
void gen_l_cust4(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
}
 
void gen_l_cust5(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
}
 
void gen_l_cust6(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
}
 
void gen_l_cust7(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
}
 
void gen_l_cust8(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
}
 
/* FIXME: All registers need to be stored before the div instructions as they
 * have the potenticial to cause an exception */
 
static const generic_gen_op check_null_excpt[NUM_T_REGS] =
 { gen_op_check_null_except_t0, gen_op_check_null_except_t1, gen_op_check_null_except_t2 };
 
static const generic_gen_op check_null_excpt_delay[NUM_T_REGS] = {
 gen_op_check_null_except_t0_delay,
 gen_op_check_null_except_t1_delay,
 gen_op_check_null_except_t2_delay };
 
static const generic_gen_op l_div_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */              {
/* param0 -> t0, param1 -> t0 */ { gen_op_div_t0_t0_t0, gen_op_div_t0_t0_t1, gen_op_div_t0_t0_t2 },
/* param0 -> t0, param1 -> t1 */ { gen_op_div_t0_t1_t0, gen_op_div_t0_t1_t1, gen_op_div_t0_t1_t2 },
/* param0 -> t0, param1 -> t2 */ { gen_op_div_t0_t2_t0, gen_op_div_t0_t2_t1, gen_op_div_t0_t2_t2 } },
/* param0 -> t1 */              {
/* param0 -> t1, param1 -> t0 */ { gen_op_div_t1_t0_t0, gen_op_div_t1_t0_t1, gen_op_div_t1_t0_t2 },
/* param0 -> t1, param1 -> t1 */ { gen_op_div_t1_t1_t0, gen_op_div_t1_t1_t1, gen_op_div_t1_t1_t2 },
/* param0 -> t1, param1 -> t2 */ { gen_op_div_t1_t2_t0, gen_op_div_t1_t2_t1, gen_op_div_t1_t2_t2 } },
/* param0 -> t2 */              {
/* param0 -> t2, param1 -> t0 */ { gen_op_div_t2_t0_t0, gen_op_div_t2_t0_t1, gen_op_div_t2_t0_t2 },
/* param0 -> t2, param1 -> t1 */ { gen_op_div_t2_t1_t0, gen_op_div_t2_t1_t1, gen_op_div_t2_t1_t2 },
/* param0 -> t2, param1 -> t2 */ { gen_op_div_t2_t2_t0, gen_op_div_t2_t2_t1, gen_op_div_t2_t2_t2 } } };
 
void gen_l_div(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  /* Cross reference this location, since an ILLEGAL exception may happen */
  find_jump_loc(opq->insn_addr, opq);
  if(!param[2]) {
    /* There is no option.  This _will_ cause an illeagal exception */
    if(!delay_slot)
      gen_op_illegal(opq, 1);
    else
      gen_op_illegal(opq, 1);
    return;
  }
 
  if(!delay_slot)
    check_null_excpt[param_t[2]](opq, 1);
  else
    check_null_excpt_delay[param_t[2]](opq, 1);
 
  if(!param[0])
    return;
 
  if(!param[1]) {
    /* Clear param_t[0] */
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  l_div_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1);
}
 
static const generic_gen_op l_divu_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */              {
/* param0 -> t0, param1 -> t0 */ { gen_op_divu_t0_t0_t0, gen_op_divu_t0_t0_t1, gen_op_divu_t0_t0_t2 },
/* param0 -> t0, param1 -> t1 */ { gen_op_divu_t0_t1_t0, gen_op_divu_t0_t1_t1, gen_op_divu_t0_t1_t2 },
/* param0 -> t0, param1 -> t2 */ { gen_op_divu_t0_t2_t0, gen_op_divu_t0_t2_t1, gen_op_divu_t0_t2_t2 } },
/* param0 -> t1 */              {
/* param0 -> t1, param1 -> t0 */ { gen_op_divu_t1_t0_t0, gen_op_divu_t1_t0_t1, gen_op_divu_t1_t0_t2 },
/* param0 -> t1, param1 -> t1 */ { gen_op_divu_t1_t1_t0, gen_op_divu_t1_t1_t1, gen_op_divu_t1_t1_t2 },
/* param0 -> t1, param1 -> t2 */ { gen_op_divu_t1_t2_t0, gen_op_divu_t1_t2_t1, gen_op_divu_t1_t2_t2 } },
/* param0 -> t2 */              {
/* param0 -> t2, param1 -> t0 */ { gen_op_divu_t2_t0_t0, gen_op_divu_t2_t0_t1, gen_op_divu_t2_t0_t2 },
/* param0 -> t2, param1 -> t1 */ { gen_op_divu_t2_t1_t0, gen_op_divu_t2_t1_t1, gen_op_divu_t2_t1_t2 },
/* param0 -> t2, param1 -> t2 */ { gen_op_divu_t2_t2_t0, gen_op_divu_t2_t2_t1, gen_op_divu_t2_t2_t2 } } };
 
void gen_l_divu(struct op_queue *opq, int param_t[3], orreg_t param[3],
                int delay_slot)
{
  /* Cross reference this location, since an ILLEGAL exception may happen */
  find_jump_loc(opq->insn_addr, opq);
  if(!param[2]) {
    /* There is no option.  This _will_ cause an illeagal exception */
    if(!delay_slot)
      gen_op_illegal(opq, 1);
    else
      gen_op_illegal(opq, 1);
    return;
  }
 
  if(!delay_slot)
    check_null_excpt[param_t[2]](opq, 1);
  else
    check_null_excpt_delay[param_t[2]](opq, 1);
 
  if(!param[0])
    return;
 
  if(!param[1]) {
    /* Clear param_t[0] */
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  l_divu_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1);
}
 
static const generic_gen_op l_extbs_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_extbs_t0_t0, gen_op_extbs_t0_t1, gen_op_extbs_t0_t2 },
/* param0 -> t1 */ { gen_op_extbs_t1_t0, gen_op_extbs_t1_t1, gen_op_extbs_t1_t2 },
/* param0 -> t2 */ { gen_op_extbs_t2_t0, gen_op_extbs_t2_t1, gen_op_extbs_t2_t2 } };
 
void gen_l_extbs(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  l_extbs_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const generic_gen_op l_extbz_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_extbz_t0_t0, gen_op_extbz_t0_t1, gen_op_extbz_t0_t2 },
/* param0 -> t1 */ { gen_op_extbz_t1_t0, gen_op_extbz_t1_t1, gen_op_extbz_t1_t2 },
/* param0 -> t2 */ { gen_op_extbz_t2_t0, gen_op_extbz_t2_t1, gen_op_extbz_t2_t2 } };
 
void gen_l_extbz(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  l_extbz_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const generic_gen_op l_exths_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_exths_t0_t0, gen_op_exths_t0_t1, gen_op_exths_t0_t2 },
/* param0 -> t1 */ { gen_op_exths_t1_t0, gen_op_exths_t1_t1, gen_op_exths_t1_t2 },
/* param0 -> t2 */ { gen_op_exths_t2_t0, gen_op_exths_t2_t1, gen_op_exths_t2_t2 } };
 
void gen_l_exths(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  l_exths_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const generic_gen_op l_exthz_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_exthz_t0_t0, gen_op_exthz_t0_t1, gen_op_exthz_t0_t2 },
/* param0 -> t1 */ { gen_op_exthz_t1_t0, gen_op_exthz_t1_t1, gen_op_exthz_t1_t2 },
/* param0 -> t2 */ { gen_op_exthz_t2_t0, gen_op_exthz_t2_t1, gen_op_exthz_t2_t2 } };
 
void gen_l_exthz(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  l_exthz_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
void gen_l_extws(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  if(param[0] == param[1])
    return;
 
  /* In the 32-bit architechture this instruction reduces to a move */
  move_t_t[param_t[0]][param_t[1]](opq, 1);
}
 
void gen_l_extwz(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  if(param[0] == param[1])
    return;
 
  /* In the 32-bit architechture this instruction reduces to a move */
  move_t_t[param_t[0]][param_t[1]](opq, 1);
}
 
static const generic_gen_op l_ff1_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_ff1_t0_t0, gen_op_ff1_t0_t1, gen_op_ff1_t0_t2 },
/* param0 -> t1 */ { gen_op_ff1_t1_t0, gen_op_ff1_t1_t1, gen_op_ff1_t1_t2 },
/* param0 -> t2 */ { gen_op_ff1_t2_t0, gen_op_ff1_t2_t1, gen_op_ff1_t2_t2 } };
 
void gen_l_ff1(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  l_ff1_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
void gen_l_j(struct op_queue *opq, int param_t[3], orreg_t param[3],
             int delay_slot)
{
  gen_op_set_pc_delay_imm(opq, 1, param[0] << 2);
 
  /* Don't allocate a seporate x-ref structure for the infinite loop instruction
   * (l.j 0) */
  if(!param[0]) {
    opq->jump_local = 2;
    opq->xref = cpu_state.inf_xrefs;
    opq->xref->or_addr = opq->insn_addr;
    cpu_state.inf_xrefs = opq->xref->next;
    return;
  }
 
  opq->jump_local = find_jump_loc(opq->insn_addr + (orreg_t)(param[0] << 2), opq);
}
 
void gen_l_jal(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  /* It is highly likely that the location that was jumped to will `return'.
   * Therefore, insert a cross reference at that address */
  find_jump_loc(opq->insn_addr + 8, opq);
 
  gen_l_j(opq, param_t, param, delay_slot);
 
  /* Store the return address */
  gen_op_store_link_addr_gpr(opq, 1);
}
 
static const generic_gen_op set_pc_delay_t[NUM_T_REGS] =
 { gen_op_set_pc_delay_t0, gen_op_set_pc_delay_t1, gen_op_set_pc_delay_t2 };
 
void gen_l_jr(struct op_queue *opq, int param_t[3], orreg_t param[3],
              int delay_slot)
{
  /* Treat all jumps as non page-local */
  opq->jump_local = 0;
 
  if(!param[0]) {
    gen_op_clear_pc_delay(opq, 1);
    return;
  }
 
  set_pc_delay_t[param_t[0]](opq, 1);
}
 
void gen_l_jalr(struct op_queue *opq, int param_t[3], orreg_t param[3],
                int delay_slot)
{
  /* It is highly likely that the location that was jumped to will `return'.
   * Therefore, insert a cross reference at that address */
  find_jump_loc(opq->insn_addr + 8, opq);
 
  gen_l_jr(opq, param_t, param, delay_slot);
 
  /* Store the return address */
  gen_op_store_link_addr_gpr(opq, 1);
}
 
/* FIXME: Optimise all load instruction when the disposition == 0 */
 
static const imm_gen_op l_lbs_imm_t_table[NUM_T_REGS] =
 { gen_op_lbs_imm_t0, gen_op_lbs_imm_t1, gen_op_lbs_imm_t2 };
 
static const imm_gen_op l_lbs_t_table[3][3] = {
/* param0 -> t0 */ { gen_op_lbs_t0_t0, gen_op_lbs_t0_t1, gen_op_lbs_t0_t2 },
/* param0 -> t1 */ { gen_op_lbs_t1_t0, gen_op_lbs_t1_t1, gen_op_lbs_t1_t2 },
/* param0 -> t2 */ { gen_op_lbs_t2_t0, gen_op_lbs_t2_t1, gen_op_lbs_t2_t2 } };
 
void gen_l_lbs(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0]) {
    /* FIXME: This will work, but the statistics need to be updated... */
    return;
  }
 
  if(!param[2]) {
    /* Load the data from the immediate */
    l_lbs_imm_t_table[param_t[0]](opq, 1, param[1]);
    return;
  }
 
  l_lbs_t_table[param_t[0]][param_t[2]](opq, 1, param[1]);
}
 
static const imm_gen_op l_lbz_imm_t_table[NUM_T_REGS] =
 { gen_op_lbz_imm_t0, gen_op_lbz_imm_t1, gen_op_lbz_imm_t2 };
 
static const imm_gen_op l_lbz_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_lbz_t0_t0, gen_op_lbz_t0_t1, gen_op_lbz_t0_t2 },
/* param0 -> t1 */ { gen_op_lbz_t1_t0, gen_op_lbz_t1_t1, gen_op_lbz_t1_t2 },
/* param0 -> t2 */ { gen_op_lbz_t2_t0, gen_op_lbz_t2_t1, gen_op_lbz_t2_t2 } };
 
void gen_l_lbz(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0]) {
    /* FIXME: This will work, but the statistics need to be updated... */
    return;
  }
 
  if(!param[2]) {
    /* Load the data from the immediate */
    l_lbz_imm_t_table[param_t[0]](opq, 1, param[1]);
    return;
  }
 
  l_lbz_t_table[param_t[0]][param_t[2]](opq, 1, param[1]);
}
 
static const imm_gen_op l_lhs_imm_t_table[NUM_T_REGS] =
 { gen_op_lhs_imm_t0, gen_op_lhs_imm_t1, gen_op_lhs_imm_t2 };
 
static const imm_gen_op l_lhs_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_lhs_t0_t0, gen_op_lhs_t0_t1, gen_op_lhs_t0_t2 },
/* param0 -> t1 */ { gen_op_lhs_t1_t0, gen_op_lhs_t1_t1, gen_op_lhs_t1_t2 },
/* param0 -> t2 */ { gen_op_lhs_t2_t0, gen_op_lhs_t2_t1, gen_op_lhs_t2_t2 } };
 
void gen_l_lhs(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0]) {
    /* FIXME: This will work, but the statistics need to be updated... */
    return;
  }
 
  if(!param[2]) {
    /* Load the data from the immediate */
    l_lhs_imm_t_table[param_t[0]](opq, 1, param[1]);
    return;
  }
 
  l_lhs_t_table[param_t[0]][param_t[2]](opq, 1, param[1]);
}
 
static const imm_gen_op l_lhz_imm_t_table[NUM_T_REGS] =
 { gen_op_lhz_imm_t0, gen_op_lhz_imm_t1, gen_op_lhz_imm_t2 };
 
static const imm_gen_op l_lhz_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_lhz_t0_t0, gen_op_lhz_t0_t1, gen_op_lhz_t0_t2 },
/* param0 -> t1 */ { gen_op_lhz_t1_t0, gen_op_lhz_t1_t1, gen_op_lhz_t1_t2 },
/* param0 -> t2 */ { gen_op_lhz_t2_t0, gen_op_lhz_t2_t1, gen_op_lhz_t2_t2 } };
 
void gen_l_lhz(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0]) {
    /* FIXME: This will work, but the statistics need to be updated... */
    return;
  }
 
  if(!param[2]) {
    /* Load the data from the immediate */
    l_lhz_imm_t_table[param_t[0]](opq, 1, param[1]);
    return;
  }
 
  l_lhz_t_table[param_t[0]][param_t[2]](opq, 1, param[1]);
}
 
static const imm_gen_op l_lws_imm_t_table[NUM_T_REGS] =
 { gen_op_lws_imm_t0, gen_op_lws_imm_t1, gen_op_lws_imm_t2 };
 
static const imm_gen_op l_lws_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_lws_t0_t0, gen_op_lws_t0_t1, gen_op_lws_t0_t2 },
/* param0 -> t1 */ { gen_op_lws_t1_t0, gen_op_lws_t1_t1, gen_op_lws_t1_t2 },
/* param0 -> t2 */ { gen_op_lws_t2_t0, gen_op_lws_t2_t1, gen_op_lws_t2_t2 } };
 
void gen_l_lws(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0]) {
    /* FIXME: This will work, but the statistics need to be updated... */
    return;
  }
 
  if(!param[2]) {
    /* Load the data from the immediate */
    l_lws_imm_t_table[param_t[0]](opq, 1, param[1]);
    return;
  }
 
  l_lws_t_table[param_t[0]][param_t[2]](opq, 1, param[1]);
}
 
static const imm_gen_op l_lwz_imm_t_table[NUM_T_REGS] =
 { gen_op_lwz_imm_t0, gen_op_lwz_imm_t1, gen_op_lwz_imm_t2 };
 
static const imm_gen_op l_lwz_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_lwz_t0_t0, gen_op_lwz_t0_t1, gen_op_lwz_t0_t2 },
/* param0 -> t1 */ { gen_op_lwz_t1_t0, gen_op_lwz_t1_t1, gen_op_lwz_t1_t2 },
/* param0 -> t2 */ { gen_op_lwz_t2_t0, gen_op_lwz_t2_t1, gen_op_lwz_t2_t2 } };
 
void gen_l_lwz(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0]) {
    /* FIXME: This will work, but the statistics need to be updated... */
    return;
  }
 
  if(!param[2]) {
    /* Load the data from the immediate */
    l_lwz_imm_t_table[param_t[0]](opq, 1, param[1]);
    return;
  }
 
  l_lwz_t_table[param_t[0]][param_t[2]](opq, 1, param[1]);
}
 
static const imm_gen_op l_mac_imm_t_table[NUM_T_REGS] =
 { gen_op_mac_imm_t0, gen_op_mac_imm_t1, gen_op_mac_imm_t2 };
 
static const generic_gen_op l_mac_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_mac_t0_t0, gen_op_mac_t0_t1, gen_op_mac_t0_t2 },
/* param0 -> t1 */ { gen_op_mac_t0_t1, gen_op_mac_t1_t1, gen_op_mac_t1_t2 },
/* param0 -> t2 */ { gen_op_mac_t0_t2, gen_op_mac_t1_t2, gen_op_mac_t2_t2 } };
 
void gen_l_mac(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0] || !param[1])
    return;
 
  if(param_t[1] == T_NONE)
    l_mac_imm_t_table[param_t[0]](opq, 1, param[1]);
  else
    l_mac_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const generic_gen_op l_macrc_t_table[NUM_T_REGS] =
 { gen_op_macrc_t0, gen_op_macrc_t1, gen_op_macrc_t2 };
 
void gen_l_macrc(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0]) {
    gen_op_macc(opq, 1);
    return;
  }
 
  l_macrc_t_table[param_t[0]](opq, 1);
}
 
static const imm_gen_op l_mfspr_imm_t_table[NUM_T_REGS] =
 { gen_op_mfspr_t0_imm, gen_op_mfspr_t1_imm, gen_op_mfspr_t2_imm };
 
static const imm_gen_op l_mfspr_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_mfspr_t0_t0, gen_op_mfspr_t0_t1, gen_op_mfspr_t0_t2 },
/* param0 -> t1 */ { gen_op_mfspr_t1_t0, gen_op_mfspr_t1_t1, gen_op_mfspr_t1_t2 },
/* param0 -> t2 */ { gen_op_mfspr_t2_t0, gen_op_mfspr_t2_t1, gen_op_mfspr_t2_t2 } };
 
void gen_l_mfspr(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1]) {
    l_mfspr_imm_t_table[param_t[0]](opq, 1, param[2]);
    return;
  }
 
  l_mfspr_t_table[param_t[0]][param_t[1]](opq, 1, param[2]);
}
 
void gen_l_movhi(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  mov_t_imm[param_t[0]](opq, 1, param[1] << 16);
}
 
static const generic_gen_op l_msb_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_msb_t0_t0, gen_op_msb_t0_t1, gen_op_msb_t0_t2 },
/* param0 -> t1 */ { gen_op_msb_t0_t1, gen_op_msb_t1_t1, gen_op_msb_t1_t2 },
/* param0 -> t2 */ { gen_op_msb_t0_t2, gen_op_msb_t1_t2, gen_op_msb_t2_t2 } };
 
void gen_l_msb(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0] || !param[1])
    return;
 
  l_msb_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const imm_gen_op l_mtspr_clear_t_table[NUM_T_REGS] =
 { gen_op_mtspr_t0_clear, gen_op_mtspr_t1_clear, gen_op_mtspr_t2_clear };
 
static const imm_gen_op l_mtspr_imm_t_table[NUM_T_REGS] =
 { gen_op_mtspr_imm_t0, gen_op_mtspr_imm_t1, gen_op_mtspr_imm_t2 };
 
static const imm_gen_op l_mtspr_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_mtspr_t0_t0, gen_op_mtspr_t0_t1, gen_op_mtspr_t0_t2 },
/* param0 -> t1 */ { gen_op_mtspr_t1_t0, gen_op_mtspr_t1_t1, gen_op_mtspr_t1_t2 },
/* param0 -> t2 */ { gen_op_mtspr_t2_t0, gen_op_mtspr_t2_t1, gen_op_mtspr_t2_t2 } };
 
void gen_l_mtspr(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0]) {
    if(!param[1]) {
      /* Clear the immediate SPR */
      gen_op_mtspr_imm_clear(opq, 1, param[2]);
      return;
    }
    l_mtspr_imm_t_table[param_t[1]](opq, 1, param[2]);
    return;
  }
 
  if(!param[1]) {
    l_mtspr_clear_t_table[param_t[0]](opq, 1, param[2]);
    return;
  }
 
  l_mtspr_t_table[param_t[0]][param_t[1]](opq, 1, param[2]);
}
 
static const imm_gen_op l_mul_imm_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_mul_imm_t0_t0, gen_op_mul_imm_t0_t1, gen_op_mul_imm_t0_t2 },
/* param0 -> t1 */ { gen_op_mul_imm_t1_t0, gen_op_mul_imm_t1_t1, gen_op_mul_imm_t1_t2 },
/* param0 -> t2 */ { gen_op_mul_imm_t2_t0, gen_op_mul_imm_t2_t1, gen_op_mul_imm_t2_t2 } };
 
static const generic_gen_op l_mul_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */              {
/* param0 -> t0, param1 -> t0 */ { gen_op_mul_t0_t0_t0, gen_op_mul_t0_t0_t1, gen_op_mul_t0_t0_t2 },
/* param0 -> t0, param1 -> t1 */ { gen_op_mul_t0_t1_t0, gen_op_mul_t0_t1_t1, gen_op_mul_t0_t1_t2 },
/* param0 -> t0, param1 -> t2 */ { gen_op_mul_t0_t2_t0, gen_op_mul_t0_t2_t1, gen_op_mul_t0_t2_t2 } },
/* param0 -> t1 */              {
/* param0 -> t1, param1 -> t0 */ { gen_op_mul_t1_t0_t0, gen_op_mul_t1_t0_t1, gen_op_mul_t1_t0_t2 },
/* param0 -> t1, param1 -> t1 */ { gen_op_mul_t1_t1_t0, gen_op_mul_t1_t1_t1, gen_op_mul_t1_t1_t2 },
/* param0 -> t1, param1 -> t2 */ { gen_op_mul_t1_t2_t0, gen_op_mul_t1_t2_t1, gen_op_mul_t1_t2_t2 } },
/* param0 -> t2 */              {
/* param0 -> t2, param1 -> t0 */ { gen_op_mul_t2_t0_t0, gen_op_mul_t2_t0_t1, gen_op_mul_t2_t0_t2 },
/* param0 -> t2, param1 -> t1 */ { gen_op_mul_t2_t1_t0, gen_op_mul_t2_t1_t1, gen_op_mul_t2_t1_t2 },
/* param0 -> t2, param1 -> t2 */ { gen_op_mul_t2_t2_t0, gen_op_mul_t2_t2_t1, gen_op_mul_t2_t2_t2 } } };
 
void gen_l_mul(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1] || !param[2]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  if(param_t[2] == T_NONE)
    l_mul_imm_t_table[param_t[0]][param_t[1]](opq, 1, param[2]);
  else
    l_mul_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1);
}
 
static const generic_gen_op l_mulu_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */              {
/* param0 -> t0, param1 -> t0 */ { gen_op_mulu_t0_t0_t0, gen_op_mulu_t0_t0_t1, gen_op_mulu_t0_t0_t2 },
/* param0 -> t0, param1 -> t1 */ { gen_op_mulu_t0_t1_t0, gen_op_mulu_t0_t1_t1, gen_op_mulu_t0_t1_t2 },
/* param0 -> t0, param1 -> t2 */ { gen_op_mulu_t0_t2_t0, gen_op_mulu_t0_t2_t1, gen_op_mulu_t0_t2_t2 } },
/* param0 -> t1 */              {
/* param0 -> t1, param1 -> t0 */ { gen_op_mulu_t1_t0_t0, gen_op_mulu_t1_t0_t1, gen_op_mulu_t1_t0_t2 },
/* param0 -> t1, param1 -> t1 */ { gen_op_mulu_t1_t1_t0, gen_op_mulu_t1_t1_t1, gen_op_mulu_t1_t1_t2 },
/* param0 -> t1, param1 -> t2 */ { gen_op_mulu_t1_t2_t0, gen_op_mulu_t1_t2_t1, gen_op_mulu_t1_t2_t2 } },
/* param0 -> t2 */              {
/* param0 -> t2, param1 -> t0 */ { gen_op_mulu_t2_t0_t0, gen_op_mulu_t2_t0_t1, gen_op_mulu_t2_t0_t2 },
/* param0 -> t2, param1 -> t1 */ { gen_op_mulu_t2_t1_t0, gen_op_mulu_t2_t1_t1, gen_op_mulu_t2_t1_t2 },
/* param0 -> t2, param1 -> t2 */ { gen_op_mulu_t2_t2_t0, gen_op_mulu_t2_t2_t1, gen_op_mulu_t2_t2_t2 } } };
 
void gen_l_mulu(struct op_queue *opq, int param_t[3], orreg_t param[3],
                int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1] || !param[2]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  l_mulu_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1);
}
 
void gen_l_nop(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  /* Do parameter switch now */
  switch (param[0]) {
  case NOP_NOP:
    break;
  case NOP_EXIT:
    gen_op_nop_exit(opq, 1);
    break;
  case NOP_CNT_RESET:
    /* FIXME: Since op_nop_reset calls handle_except, this instruction wont show
     * up in the execution log, nor will the scheduler run */
    gen_op_nop_reset(opq, 1);
    break;    
  case NOP_PRINTF:
    gen_op_nop_printf(opq, 1);
    break;
  case NOP_REPORT:
    gen_op_nop_report(opq, 1);
    break;
  default:
    if((param[0] >= NOP_REPORT_FIRST) && (param[0] <= NOP_REPORT_LAST))
      gen_op_nop_report_imm(opq, 1, param[0] - NOP_REPORT_FIRST);
    break;
  }
}
 
static const imm_gen_op l_or_imm_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_or_imm_t0_t0, gen_op_or_imm_t0_t1, gen_op_or_imm_t0_t2 },
/* param0 -> t1 */ { gen_op_or_imm_t1_t0, gen_op_or_imm_t1_t1, gen_op_or_imm_t1_t2 },
/* param0 -> t2 */ { gen_op_or_imm_t2_t0, gen_op_or_imm_t2_t1, gen_op_or_imm_t2_t2 } };
 
static const generic_gen_op l_or_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */              {
/* param0 -> t0, param1 -> t0 */ { NULL, gen_op_or_t0_t0_t1, gen_op_or_t0_t0_t2 },
/* param0 -> t0, param1 -> t1 */ { gen_op_or_t0_t1_t0, gen_op_or_t0_t1_t1, gen_op_or_t0_t1_t2 },
/* param0 -> t0, param1 -> t2 */ { gen_op_or_t0_t2_t0, gen_op_or_t0_t2_t1, gen_op_or_t0_t2_t2 } },
/* param0 -> t1 */              {
/* param0 -> t1, param1 -> t0 */ { gen_op_or_t1_t0_t0, gen_op_or_t1_t0_t1, gen_op_or_t1_t0_t2 },
/* param0 -> t1, param1 -> t1 */ { gen_op_or_t1_t1_t0, NULL, gen_op_or_t1_t1_t2 },
/* param0 -> t1, param1 -> t2 */ { gen_op_or_t1_t2_t0, gen_op_or_t1_t2_t1, gen_op_or_t1_t2_t2 } },
/* param0 -> t2 */              {
/* param0 -> t2, param1 -> t0 */ { gen_op_or_t2_t0_t0, gen_op_or_t2_t0_t1, gen_op_or_t2_t0_t2 },
/* param0 -> t2, param1 -> t1 */ { gen_op_or_t2_t1_t0, gen_op_or_t2_t1_t1, gen_op_or_t2_t1_t2 },
/* param0 -> t2, param1 -> t2 */ { gen_op_or_t2_t2_t0, gen_op_or_t2_t2_t1, NULL } } };
 
void gen_l_or(struct op_queue *opq, int param_t[3], orreg_t param[3],
              int delay_slot)
{
  if(!param[0])
    return;
 
  if((param[0] == param[1] == param[2]) && (param_t[2] != T_NONE))
    return;
 
  if(!param[1] && !param[2]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  if(!param[2]) {
    if((param_t[2] == T_NONE) && (param[0] == param[1]))
      return;
    move_t_t[param_t[0]][param_t[1]](opq, 1);
    return;
  }
 
  if(!param[1]) {
    /* Check if we are moveing an immediate */
    if(param_t[2] == T_NONE) {
      /* Yep, an immediate */
      mov_t_imm[param_t[0]](opq, 1, param[2]);
      return;
    }
    /* Just another move */
    move_t_t[param_t[0]][param_t[2]](opq, 1);
    return;
  }
 
  if(param_t[2] == T_NONE)
    l_or_imm_t_table[param_t[0]][param_t[1]](opq, 1, param[2]);
  else
    l_or_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1);
}
 
void gen_l_rfe(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  gen_op_prep_rfe(opq, 1);
}
 
/* FIXME: All store instructions should be optimised when the disposition = 0 */
 
static const imm_gen_op l_sb_clear_table[NUM_T_REGS] =
 { gen_op_sb_clear_t0, gen_op_sb_clear_t1, gen_op_sb_clear_t2 };
 
static const imm_gen_op l_sb_imm_t_table[NUM_T_REGS] =
 { gen_op_sb_imm_t0, gen_op_sb_imm_t1, gen_op_sb_imm_t2 };
 
static const imm_gen_op l_sb_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sb_t0_t0, gen_op_sb_t0_t1, gen_op_sb_t0_t2 },
/* param0 -> t1 */ { gen_op_sb_t1_t0, gen_op_sb_t1_t1, gen_op_sb_t1_t2 },
/* param0 -> t2 */ { gen_op_sb_t2_t0, gen_op_sb_t2_t1, gen_op_sb_t2_t2 } };
 
void gen_l_sb(struct op_queue *opq, int param_t[3], orreg_t param[3],
              int delay_slot)
{
  if(!param[2]) {
    if(!param[1]) {
      gen_op_sb_clear_imm(opq, 1, param[0]);
      return;
    }
    l_sb_clear_table[param_t[1]](opq, 1, param[0]);
    return;
  }
 
  if(!param[1]) {
    /* Store the data to the immediate */
    l_sb_imm_t_table[param_t[2]](opq, 1, param[0]);
    return;
  }
 
  l_sb_t_table[param_t[1]][param_t[2]](opq, 1, param[0]);
}
 
static const imm_gen_op l_sh_clear_table[NUM_T_REGS] =
 { gen_op_sh_clear_t0, gen_op_sh_clear_t1, gen_op_sh_clear_t2 };
 
static const imm_gen_op l_sh_imm_t_table[NUM_T_REGS] =
 { gen_op_sh_imm_t0, gen_op_sh_imm_t1, gen_op_sh_imm_t2 };
 
static const imm_gen_op l_sh_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sh_t0_t0, gen_op_sh_t0_t1, gen_op_sh_t0_t2 },
/* param0 -> t1 */ { gen_op_sh_t1_t0, gen_op_sh_t1_t1, gen_op_sh_t1_t2 },
/* param0 -> t2 */ { gen_op_sh_t2_t0, gen_op_sh_t2_t1, gen_op_sh_t2_t2 } };
 
void gen_l_sh(struct op_queue *opq, int param_t[3], orreg_t param[3],
              int delay_slot)
{
  if(!param[2]) {
    if(!param[1]) {
      gen_op_sh_clear_imm(opq, 1, param[0]);
      return;
    }
    l_sh_clear_table[param_t[1]](opq, 1, param[0]);
    return;
  }
 
  if(!param[1]) {
    /* Store the data to the immediate */
    l_sh_imm_t_table[param_t[2]](opq, 1, param[0]);
    return;
  }
 
  l_sh_t_table[param_t[1]][param_t[2]](opq, 1, param[0]);
}
 
static const imm_gen_op l_sw_clear_table[NUM_T_REGS] =
 { gen_op_sw_clear_t0, gen_op_sw_clear_t1, gen_op_sw_clear_t2 };
 
static const imm_gen_op l_sw_imm_t_table[NUM_T_REGS] =
 { gen_op_sw_imm_t0, gen_op_sw_imm_t1, gen_op_sw_imm_t2 };
 
static const imm_gen_op l_sw_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sw_t0_t0, gen_op_sw_t0_t1, gen_op_sw_t0_t2 },
/* param0 -> t1 */ { gen_op_sw_t1_t0, gen_op_sw_t1_t1, gen_op_sw_t1_t2 },
/* param0 -> t2 */ { gen_op_sw_t2_t0, gen_op_sw_t2_t1, gen_op_sw_t2_t2 } };
 
void gen_l_sw(struct op_queue *opq, int param_t[3], orreg_t param[3],
              int delay_slot)
{
  if(!param[2]) {
    if(!param[1]) {
      gen_op_sw_clear_imm(opq, 1, param[0]);
      return;
    }
    l_sw_clear_table[param_t[1]](opq, 1, param[0]);
    return;
  }
 
  if(!param[1]) {
    /* Store the data to the immediate */
    l_sw_imm_t_table[param_t[2]](opq, 1, param[0]);
    return;
  }
 
  l_sw_t_table[param_t[1]][param_t[2]](opq, 1, param[0]);
}
 
static const generic_gen_op l_sfeq_null_t_table[NUM_T_REGS] =
 { gen_op_sfeq_null_t0, gen_op_sfeq_null_t1, gen_op_sfeq_null_t2 };
 
static const imm_gen_op l_sfeq_imm_t_table[NUM_T_REGS] =
 { gen_op_sfeq_imm_t0, gen_op_sfeq_imm_t1, gen_op_sfeq_imm_t2 };
 
static const generic_gen_op l_sfeq_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sfeq_t0_t0, gen_op_sfeq_t0_t1, gen_op_sfeq_t0_t2 },
/* param0 -> t1 */ { gen_op_sfeq_t1_t0, gen_op_sfeq_t1_t1, gen_op_sfeq_t1_t2 },
/* param0 -> t2 */ { gen_op_sfeq_t2_t0, gen_op_sfeq_t2_t1, gen_op_sfeq_t2_t2 } };
 
void gen_l_sfeq(struct op_queue *opq, int param_t[3], orreg_t param[3],
                int delay_slot)
{
  if(!param[0] && !param[1]) {
    gen_op_set_flag(opq, 1);
    return;
  }
 
  if(!param[0]) {
    if(param_t[1] == T_NONE) {
      if(!param[1])
        gen_op_set_flag(opq, 1);
      else
        gen_op_clear_flag(opq, 1);
    } else
      l_sfeq_null_t_table[param_t[1]](opq, 1);
    return;
  }
 
  if(!param[1]) {
    l_sfeq_null_t_table[param_t[0]](opq, 1);
    return;
  }
 
  if(param_t[1] == T_NONE)
    l_sfeq_imm_t_table[param_t[0]](opq, 1, param[1]);
  else
    l_sfeq_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const generic_gen_op l_sfges_null_t_table[NUM_T_REGS] =
 { gen_op_sfges_null_t0, gen_op_sfges_null_t1, gen_op_sfges_null_t2 };
 
static const generic_gen_op l_sfles_null_t_table[NUM_T_REGS] =
 { gen_op_sfles_null_t0, gen_op_sfles_null_t1, gen_op_sfles_null_t2 };
 
static const imm_gen_op l_sfges_imm_t_table[NUM_T_REGS] =
 { gen_op_sfges_imm_t0, gen_op_sfges_imm_t1, gen_op_sfges_imm_t2 };
 
static const generic_gen_op l_sfges_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sfges_t0_t0, gen_op_sfges_t0_t1, gen_op_sfges_t0_t2 },
/* param0 -> t1 */ { gen_op_sfges_t1_t0, gen_op_sfges_t1_t1, gen_op_sfges_t1_t2 },
/* param0 -> t2 */ { gen_op_sfges_t2_t0, gen_op_sfges_t2_t1, gen_op_sfges_t2_t2 } };
 
void gen_l_sfges(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0] && !param[1]) {
    gen_op_set_flag(opq, 1);
    return;
  }
 
  if(!param[0]) {
    /* sfles IS correct */
    if(param_t[1] == T_NONE) {
      if(0 >= (orreg_t)param[1])
        gen_op_set_flag(opq, 1);
      else
        gen_op_clear_flag(opq, 1);
    } else
      l_sfles_null_t_table[param_t[1]](opq, 1);
    return;
  }
 
  if(!param[1]) {
    l_sfges_null_t_table[param_t[0]](opq, 1);
    return;
  }
 
  if(param_t[1] == T_NONE)
    l_sfges_imm_t_table[param_t[0]](opq, 1, param[1]);
  else
    l_sfges_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const generic_gen_op l_sfgeu_null_t_table[NUM_T_REGS] =
 { gen_op_sfgeu_null_t0, gen_op_sfgeu_null_t1, gen_op_sfgeu_null_t2 };
 
static const generic_gen_op l_sfleu_null_t_table[NUM_T_REGS] =
 { gen_op_sfleu_null_t0, gen_op_sfleu_null_t1, gen_op_sfleu_null_t2 };
 
static const imm_gen_op l_sfgeu_imm_t_table[NUM_T_REGS] =
 { gen_op_sfgeu_imm_t0, gen_op_sfgeu_imm_t1, gen_op_sfgeu_imm_t2 };
 
static const generic_gen_op l_sfgeu_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sfgeu_t0_t0, gen_op_sfgeu_t0_t1, gen_op_sfgeu_t0_t2 },
/* param0 -> t1 */ { gen_op_sfgeu_t1_t0, gen_op_sfgeu_t1_t1, gen_op_sfgeu_t1_t2 },
/* param0 -> t2 */ { gen_op_sfgeu_t2_t0, gen_op_sfgeu_t2_t1, gen_op_sfgeu_t2_t2 } };
 
void gen_l_sfgeu(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0] && !param[1]) {
    gen_op_set_flag(opq, 1);
    return;
  }
 
  if(!param[0]) {
    /* sfleu IS correct */
    if(param_t[1] == T_NONE) {
      if(0 >= param[1])
        gen_op_set_flag(opq, 1);
      else
        gen_op_clear_flag(opq, 1);
    } else
      l_sfleu_null_t_table[param_t[1]](opq, 1);
    return;
  }
 
  if(!param[1]) {
    l_sfgeu_null_t_table[param_t[0]](opq, 1);
    return;
  }
  if(param_t[1] == T_NONE)
    l_sfgeu_imm_t_table[param_t[0]](opq, 1, param[1]);
  else
    l_sfgeu_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const generic_gen_op l_sfgts_null_t_table[NUM_T_REGS] =
 { gen_op_sfgts_null_t0, gen_op_sfgts_null_t1, gen_op_sfgts_null_t2 };
 
static const generic_gen_op l_sflts_null_t_table[NUM_T_REGS] =
 { gen_op_sflts_null_t0, gen_op_sflts_null_t1, gen_op_sflts_null_t2 };
 
static const imm_gen_op l_sfgts_imm_t_table[NUM_T_REGS] =
 { gen_op_sfgts_imm_t0, gen_op_sfgts_imm_t1, gen_op_sfgts_imm_t2 };
 
static const generic_gen_op l_sfgts_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sfgts_t0_t0, gen_op_sfgts_t0_t1, gen_op_sfgts_t0_t2 },
/* param0 -> t1 */ { gen_op_sfgts_t1_t0, gen_op_sfgts_t1_t1, gen_op_sfgts_t1_t2 },
/* param0 -> t2 */ { gen_op_sfgts_t2_t0, gen_op_sfgts_t2_t1, gen_op_sfgts_t2_t2 } };
 
void gen_l_sfgts(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0] && !param[1]) {
    gen_op_clear_flag(opq, 1);
    return;
  }
 
  if(!param[0]) {
    /* sflts IS correct */
    if(param_t[1] == T_NONE) {
      if(0 > (orreg_t)param[1])
        gen_op_set_flag(opq, 1);
      else
        gen_op_clear_flag(opq, 1);
    } else
      l_sflts_null_t_table[param_t[1]](opq, 1);
    return;
  }
 
  if(!param[1]) {
    l_sfgts_null_t_table[param_t[0]](opq, 1);
    return;
  }
 
  if(param_t[1] == T_NONE)
    l_sfgts_imm_t_table[param_t[0]](opq, 1, param[1]);
  else
    l_sfgts_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const generic_gen_op l_sfgtu_null_t_table[NUM_T_REGS] =
 { gen_op_sfgtu_null_t0, gen_op_sfgtu_null_t1, gen_op_sfgtu_null_t2 };
 
static const generic_gen_op l_sfltu_null_t_table[NUM_T_REGS] =
 { gen_op_sfltu_null_t0, gen_op_sfltu_null_t1, gen_op_sfltu_null_t2 };
 
static const imm_gen_op l_sfgtu_imm_t_table[NUM_T_REGS] =
 { gen_op_sfgtu_imm_t0, gen_op_sfgtu_imm_t1, gen_op_sfgtu_imm_t2 };
 
static const generic_gen_op l_sfgtu_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sfgtu_t0_t0, gen_op_sfgtu_t0_t1, gen_op_sfgtu_t0_t2 },
/* param0 -> t1 */ { gen_op_sfgtu_t1_t0, gen_op_sfgtu_t1_t1, gen_op_sfgtu_t1_t2 },
/* param0 -> t2 */ { gen_op_sfgtu_t2_t0, gen_op_sfgtu_t2_t1, gen_op_sfgtu_t2_t2 } };
 
void gen_l_sfgtu(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0] && !param[1]) {
    gen_op_clear_flag(opq, 1);
    return;
  }
 
  if(!param[0]) {
    /* sfltu IS correct */
    if(param_t[1] == T_NONE) {
      if(0 > param[1])
        gen_op_set_flag(opq, 1);
      else
        gen_op_clear_flag(opq, 1);
    } else
      l_sfltu_null_t_table[param_t[1]](opq, 1);
    return;
  }
 
  if(!param[1]) {
    l_sfgtu_null_t_table[param_t[0]](opq, 1);
    return;
  }
 
  if(param_t[1] == T_NONE)
    l_sfgtu_imm_t_table[param_t[0]](opq, 1, param[1]);
  else
    l_sfgtu_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const imm_gen_op l_sfles_imm_t_table[NUM_T_REGS] =
 { gen_op_sfles_imm_t0, gen_op_sfles_imm_t1, gen_op_sfles_imm_t2 };
 
static const generic_gen_op l_sfles_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sfles_t0_t0, gen_op_sfles_t0_t1, gen_op_sfles_t0_t2 },
/* param0 -> t1 */ { gen_op_sfles_t1_t0, gen_op_sfles_t1_t1, gen_op_sfles_t1_t2 },
/* param0 -> t2 */ { gen_op_sfles_t2_t0, gen_op_sfles_t2_t1, gen_op_sfles_t2_t2 } };
 
void gen_l_sfles(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0] && !param[1]) {
    gen_op_set_flag(opq, 1);
    return;
  }
 
  if(!param[0]) {
    /* sfges IS correct */
    if(param_t[1] == T_NONE) {
      if(0 <= (orreg_t)param[1])
        gen_op_set_flag(opq, 1);
      else
        gen_op_clear_flag(opq, 1);
    } else
      l_sfges_null_t_table[param_t[1]](opq, 1);
    return;
  }
 
  if(!param[1]) {
    l_sfles_null_t_table[param_t[0]](opq, 1);
    return;
  }
 
  if(param_t[1] == T_NONE)
    l_sfles_imm_t_table[param_t[0]](opq, 1, param[1]);
  else
    l_sfles_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const imm_gen_op l_sfleu_imm_t_table[NUM_T_REGS] =
 { gen_op_sfleu_imm_t0, gen_op_sfleu_imm_t1, gen_op_sfleu_imm_t2 };
 
static const generic_gen_op l_sfleu_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sfleu_t0_t0, gen_op_sfleu_t0_t1, gen_op_sfleu_t0_t2 },
/* param0 -> t1 */ { gen_op_sfleu_t1_t0, gen_op_sfleu_t1_t1, gen_op_sfleu_t1_t2 },
/* param0 -> t2 */ { gen_op_sfleu_t2_t0, gen_op_sfleu_t2_t1, gen_op_sfleu_t2_t2 } };
 
void gen_l_sfleu(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0] && !param[1]) {
    gen_op_set_flag(opq, 1);
    return;
  }
 
  if(!param[0]) {
    /* sfleu IS correct */
    if(param_t[1] == T_NONE) {
      if(0 <= param[1])
        gen_op_set_flag(opq, 1);
      else
        gen_op_clear_flag(opq, 1);
    } else
      l_sfgeu_null_t_table[param_t[1]](opq, 1);
    return;
  }
 
  if(!param[1]) {
    l_sfleu_null_t_table[param_t[0]](opq, 1);
    return;
  }
 
  if(param_t[1] == T_NONE)
    l_sfleu_imm_t_table[param_t[0]](opq, 1, param[1]);
  else
    l_sfleu_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const imm_gen_op l_sflts_imm_t_table[NUM_T_REGS] =
 { gen_op_sflts_imm_t0, gen_op_sflts_imm_t1, gen_op_sflts_imm_t2 };
 
static const generic_gen_op l_sflts_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sflts_t0_t0, gen_op_sflts_t0_t1, gen_op_sflts_t0_t2 },
/* param0 -> t1 */ { gen_op_sflts_t1_t0, gen_op_sflts_t1_t1, gen_op_sflts_t1_t2 },
/* param0 -> t2 */ { gen_op_sflts_t2_t0, gen_op_sflts_t2_t1, gen_op_sflts_t2_t2 } };
 
void gen_l_sflts(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0] && !param[1]) {
    gen_op_clear_flag(opq, 1);
    return;
  }
 
  if(!param[0]) {
    /* sfgts IS correct */
    if(param_t[1] == T_NONE) {
      if(0 < (orreg_t)param[1])
        gen_op_set_flag(opq, 1);
      else
        gen_op_clear_flag(opq, 1);
    } else
      l_sfgts_null_t_table[param_t[1]](opq, 1);
    return;
  }
 
  if(!param[1]) {
    l_sflts_null_t_table[param_t[0]](opq, 1);
    return;
  }
 
  if(param_t[1] == T_NONE)
    l_sflts_imm_t_table[param_t[0]](opq, 1, param[1]);
  else
    l_sflts_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const imm_gen_op l_sfltu_imm_t_table[NUM_T_REGS] =
 { gen_op_sfltu_imm_t0, gen_op_sfltu_imm_t1, gen_op_sfltu_imm_t2 };
 
static const generic_gen_op l_sfltu_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sfltu_t0_t0, gen_op_sfltu_t0_t1, gen_op_sfltu_t0_t2 },
/* param0 -> t1 */ { gen_op_sfltu_t1_t0, gen_op_sfltu_t1_t1, gen_op_sfltu_t1_t2 },
/* param0 -> t2 */ { gen_op_sfltu_t2_t0, gen_op_sfltu_t2_t1, gen_op_sfltu_t2_t2 } };
 
void gen_l_sfltu(struct op_queue *opq, int param_t[3], orreg_t param[3],
                 int delay_slot)
{
  if(!param[0] && !param[1]) {
    gen_op_clear_flag(opq, 1);
    return;
  }
 
  if(!param[0]) {
    /* sfgtu IS correct */
    if(param_t[1] == T_NONE) {
      if(0 < param[1])
        gen_op_set_flag(opq, 1);
      else
        gen_op_clear_flag(opq, 1);
    } else
      l_sfgtu_null_t_table[param_t[1]](opq, 1);
    return;
  }
 
  if(!param[1]) {
    l_sfltu_null_t_table[param_t[0]](opq, 1);
    return;
  }
 
  if(param_t[1] == T_NONE)
    l_sfltu_imm_t_table[param_t[0]](opq, 1, param[1]);
  else
    l_sfltu_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const generic_gen_op l_sfne_null_t_table[NUM_T_REGS] =
 { gen_op_sfne_null_t0, gen_op_sfne_null_t1, gen_op_sfne_null_t2 };
 
static const imm_gen_op l_sfne_imm_t_table[NUM_T_REGS] =
 { gen_op_sfne_imm_t0, gen_op_sfne_imm_t1, gen_op_sfne_imm_t2 };
 
static const generic_gen_op l_sfne_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sfne_t0_t0, gen_op_sfne_t0_t1, gen_op_sfne_t0_t2 },
/* param0 -> t1 */ { gen_op_sfne_t1_t0, gen_op_sfne_t1_t1, gen_op_sfne_t1_t2 },
/* param0 -> t2 */ { gen_op_sfne_t2_t0, gen_op_sfne_t2_t1, gen_op_sfne_t2_t2 } };
 
void gen_l_sfne(struct op_queue *opq, int param_t[3], orreg_t param[3],
                int delay_slot)
{
  if(!param[0] && !param[1]) {
    gen_op_set_flag(opq, 1);
    return;
  }
 
  if(!param[0]) {
    if(param_t[1] == T_NONE)
      if(param[1])
        gen_op_set_flag(opq, 1);
      else
        gen_op_clear_flag(opq, 1);
    else
      l_sfne_null_t_table[param_t[1]](opq, 1);
    return;
  }
 
  if(!param[1]) {
    l_sfne_null_t_table[param_t[0]](opq, 1);
    return;
  }
 
  if(param_t[1] == T_NONE)
    l_sfne_imm_t_table[param_t[0]](opq, 1, param[1]);
  else
    l_sfne_t_table[param_t[0]][param_t[1]](opq, 1);
}
 
static const imm_gen_op l_sll_imm_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sll_imm_t0_t0, gen_op_sll_imm_t0_t1, gen_op_sll_imm_t0_t2 },
/* param0 -> t1 */ { gen_op_sll_imm_t1_t0, gen_op_sll_imm_t1_t1, gen_op_sll_imm_t1_t2 },
/* param0 -> t2 */ { gen_op_sll_imm_t2_t0, gen_op_sll_imm_t2_t1, gen_op_sll_imm_t2_t2 } };
 
static const generic_gen_op l_sll_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */              {
/* param0 -> t0, param1 -> t0 */ { gen_op_sll_t0_t0_t0, gen_op_sll_t0_t0_t1, gen_op_sll_t0_t0_t2 },
/* param0 -> t0, param1 -> t1 */ { gen_op_sll_t0_t1_t0, gen_op_sll_t0_t1_t1, gen_op_sll_t0_t1_t2 },
/* param0 -> t0, param1 -> t2 */ { gen_op_sll_t0_t2_t0, gen_op_sll_t0_t2_t1, gen_op_sll_t0_t2_t2 } },
/* param0 -> t1 */              {
/* param0 -> t1, param1 -> t0 */ { gen_op_sll_t1_t0_t0, gen_op_sll_t1_t0_t1, gen_op_sll_t1_t0_t2 },
/* param0 -> t1, param1 -> t1 */ { gen_op_sll_t1_t1_t0, gen_op_sll_t1_t1_t1, gen_op_sll_t1_t1_t2 },
/* param0 -> t1, param1 -> t2 */ { gen_op_sll_t1_t2_t0, gen_op_sll_t1_t2_t1, gen_op_sll_t1_t2_t2 } },
/* param0 -> t2 */              {
/* param0 -> t2, param1 -> t0 */ { gen_op_sll_t2_t0_t0, gen_op_sll_t2_t0_t1, gen_op_sll_t2_t0_t2 },
/* param0 -> t2, param1 -> t1 */ { gen_op_sll_t2_t1_t0, gen_op_sll_t2_t1_t1, gen_op_sll_t2_t1_t2 },
/* param0 -> t2, param1 -> t2 */ { gen_op_sll_t2_t2_t0, gen_op_sll_t2_t2_t1, gen_op_sll_t2_t2_t2 } } };
 
void gen_l_sll(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  if(!param[2]) {
    move_t_t[param_t[0]][param_t[1]](opq, 1);
    return;
  }
 
  if(param_t[2] == T_NONE)
    l_sll_imm_t_table[param_t[0]][param_t[1]](opq, 1, param[2]);
  else
    l_sll_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1);
}
 
static const imm_gen_op l_sra_imm_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_sra_imm_t0_t0, gen_op_sra_imm_t0_t1, gen_op_sra_imm_t0_t2 },
/* param0 -> t1 */ { gen_op_sra_imm_t1_t0, gen_op_sra_imm_t1_t1, gen_op_sra_imm_t1_t2 },
/* param0 -> t2 */ { gen_op_sra_imm_t2_t0, gen_op_sra_imm_t2_t1, gen_op_sra_imm_t2_t2 } };
 
static const generic_gen_op l_sra_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */              {
/* param0 -> t0, param1 -> t0 */ { gen_op_sra_t0_t0_t0, gen_op_sra_t0_t0_t1, gen_op_sra_t0_t0_t2 },
/* param0 -> t0, param1 -> t1 */ { gen_op_sra_t0_t1_t0, gen_op_sra_t0_t1_t1, gen_op_sra_t0_t1_t2 },
/* param0 -> t0, param1 -> t2 */ { gen_op_sra_t0_t2_t0, gen_op_sra_t0_t2_t1, gen_op_sra_t0_t2_t2 } },
/* param0 -> t1 */              {
/* param0 -> t1, param1 -> t0 */ { gen_op_sra_t1_t0_t0, gen_op_sra_t1_t0_t1, gen_op_sra_t1_t0_t2 },
/* param0 -> t1, param1 -> t1 */ { gen_op_sra_t1_t1_t0, gen_op_sra_t1_t1_t1, gen_op_sra_t1_t1_t2 },
/* param0 -> t1, param1 -> t2 */ { gen_op_sra_t1_t2_t0, gen_op_sra_t1_t2_t1, gen_op_sra_t1_t2_t2 } },
/* param0 -> t2 */              {
/* param0 -> t2, param1 -> t0 */ { gen_op_sra_t2_t0_t0, gen_op_sra_t2_t0_t1, gen_op_sra_t2_t0_t2 },
/* param0 -> t2, param1 -> t1 */ { gen_op_sra_t2_t1_t0, gen_op_sra_t2_t1_t1, gen_op_sra_t2_t1_t2 },
/* param0 -> t2, param1 -> t2 */ { gen_op_sra_t2_t2_t0, gen_op_sra_t2_t2_t1, gen_op_sra_t2_t2_t2 } } };
 
void gen_l_sra(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  if(!param[2]) {
    move_t_t[param_t[0]][param_t[1]](opq, 1);
    return;
  }
 
  if(param_t[2] == T_NONE)
    l_sra_imm_t_table[param_t[0]][param_t[1]](opq, 1, param[2]);
  else
    l_sra_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1);
}
 
static const imm_gen_op l_srl_imm_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_srl_imm_t0_t0, gen_op_srl_imm_t0_t1, gen_op_srl_imm_t0_t2 },
/* param0 -> t1 */ { gen_op_srl_imm_t1_t0, gen_op_srl_imm_t1_t1, gen_op_srl_imm_t1_t2 },
/* param0 -> t2 */ { gen_op_srl_imm_t2_t0, gen_op_srl_imm_t2_t1, gen_op_srl_imm_t2_t2 } };
 
static const generic_gen_op l_srl_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */              {
/* param0 -> t0, param1 -> t0 */ { gen_op_srl_t0_t0_t0, gen_op_srl_t0_t0_t1, gen_op_srl_t0_t0_t2 },
/* param0 -> t0, param1 -> t1 */ { gen_op_srl_t0_t1_t0, gen_op_srl_t0_t1_t1, gen_op_srl_t0_t1_t2 },
/* param0 -> t0, param1 -> t2 */ { gen_op_srl_t0_t2_t0, gen_op_srl_t0_t2_t1, gen_op_srl_t0_t2_t2 } },
/* param0 -> t1 */              {
/* param0 -> t1, param1 -> t0 */ { gen_op_srl_t1_t0_t0, gen_op_srl_t1_t0_t1, gen_op_srl_t1_t0_t2 },
/* param0 -> t1, param1 -> t1 */ { gen_op_srl_t1_t1_t0, gen_op_srl_t1_t1_t1, gen_op_srl_t1_t1_t2 },
/* param0 -> t1, param1 -> t2 */ { gen_op_srl_t1_t2_t0, gen_op_srl_t1_t2_t1, gen_op_srl_t1_t2_t2 } },
/* param0 -> t2 */              {
/* param0 -> t2, param1 -> t0 */ { gen_op_srl_t2_t0_t0, gen_op_srl_t2_t0_t1, gen_op_srl_t2_t0_t2 },
/* param0 -> t2, param1 -> t1 */ { gen_op_srl_t2_t1_t0, gen_op_srl_t2_t1_t1, gen_op_srl_t2_t1_t2 },
/* param0 -> t2, param1 -> t2 */ { gen_op_srl_t2_t2_t0, gen_op_srl_t2_t2_t1, gen_op_srl_t2_t2_t2 } } };
 
void gen_l_srl(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0])
    return;
 
  if(!param[1]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  if(!param[2]) {
    move_t_t[param_t[0]][param_t[1]](opq, 1);
    return;
  }
 
  if(param_t[2] == T_NONE)
    l_srl_imm_t_table[param_t[0]][param_t[1]](opq, 1, param[2]);
  else
    l_srl_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1);
}
 
static const generic_gen_op l_neg_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_neg_t0_t0, gen_op_neg_t0_t1, gen_op_neg_t0_t2 },
/* param0 -> t1 */ { gen_op_neg_t1_t0, gen_op_neg_t1_t1, gen_op_neg_t1_t2 },
/* param0 -> t2 */ { gen_op_neg_t2_t0, gen_op_neg_t2_t1, gen_op_neg_t2_t2 } };
 
static const generic_gen_op l_sub_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */              {
/* param0 -> t0, param1 -> t0 */ { gen_op_sub_t0_t0_t0, gen_op_sub_t0_t0_t1, gen_op_sub_t0_t0_t2 },
/* param0 -> t0, param1 -> t1 */ { gen_op_sub_t0_t1_t0, gen_op_sub_t0_t1_t1, gen_op_sub_t0_t1_t2 },
/* param0 -> t0, param1 -> t2 */ { gen_op_sub_t0_t2_t0, gen_op_sub_t0_t2_t1, gen_op_sub_t0_t2_t2 } },
/* param0 -> t1 */              {
/* param0 -> t1, param1 -> t0 */ { gen_op_sub_t1_t0_t0, gen_op_sub_t1_t0_t1, gen_op_sub_t1_t0_t2 },
/* param0 -> t1, param1 -> t1 */ { gen_op_sub_t1_t1_t0, gen_op_sub_t1_t1_t1, gen_op_sub_t1_t1_t2 },
/* param0 -> t1, param1 -> t2 */ { gen_op_sub_t1_t2_t0, gen_op_sub_t1_t2_t1, gen_op_sub_t1_t2_t2 } },
/* param0 -> t2 */              {
/* param0 -> t2, param1 -> t0 */ { gen_op_sub_t2_t0_t0, gen_op_sub_t2_t0_t1, gen_op_sub_t2_t0_t2 },
/* param0 -> t2, param1 -> t1 */ { gen_op_sub_t2_t1_t0, gen_op_sub_t2_t1_t1, gen_op_sub_t2_t1_t2 },
/* param0 -> t2, param1 -> t2 */ { gen_op_sub_t2_t2_t0, gen_op_sub_t2_t2_t1, gen_op_sub_t2_t2_t2 } } };
 
void gen_l_sub(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0])
    return;
 
  if((param_t[2] != T_NONE) && (param[1] == param[2])) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  if(!param[1] && !param[2]) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  if(!param[1]) {
    if(param_t[2] == T_NONE)
      mov_t_imm[param_t[0]](opq, 1, -param[2]);
    else
      l_neg_t_table[param_t[0]][param_t[2]](opq, 1);
    return;
  }
 
  if(!param[2]) {
    move_t_t[param_t[0]][param_t[1]](opq, 1);
    return;
  }
 
  l_sub_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1);
}
 
/* FIXME: This will not work if the l.sys is in a delay slot */
void gen_l_sys(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  /* Since we *know* that we *will* jump to the next instruction, insert an xref
   * there */
  find_jump_loc(opq->insn_addr + 4, opq);
 
  if(!delay_slot)
    gen_op_prep_sys(opq, 1);
  else
    gen_op_prep_sys_delay(opq, 1);
}
 
/* FIXME: This will not work if the l.trap is in a delay slot */
void gen_l_trap(struct op_queue *opq, int param_t[3], orreg_t param[3],
                int delay_slot)
{
  /* Since we *know* that we *will* jump to the next instruction, insert an xref
   * there */
  find_jump_loc(opq->insn_addr + 4, opq);
 
  if(!delay_slot)
    gen_op_prep_trap(opq, 1);
  else
    gen_op_prep_trap_delay(opq, 1);
}
 
static const imm_gen_op l_xor_imm_t_table[NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */ { gen_op_xor_imm_t0_t0, gen_op_xor_imm_t0_t1, gen_op_xor_imm_t0_t2 },
/* param0 -> t1 */ { gen_op_xor_imm_t1_t0, gen_op_xor_imm_t1_t1, gen_op_xor_imm_t1_t2 },
/* param0 -> t2 */ { gen_op_xor_imm_t2_t0, gen_op_xor_imm_t2_t1, gen_op_xor_imm_t2_t2 } };
 
static const generic_gen_op l_xor_t_table[NUM_T_REGS][NUM_T_REGS][NUM_T_REGS] = {
/* param0 -> t0 */              {
/* param0 -> t0, param1 -> t0 */ { gen_op_xor_t0_t0_t0, gen_op_xor_t0_t0_t1, gen_op_xor_t0_t0_t2 },
/* param0 -> t0, param1 -> t1 */ { gen_op_xor_t0_t1_t0, gen_op_xor_t0_t1_t1, gen_op_xor_t0_t1_t2 },
/* param0 -> t0, param1 -> t2 */ { gen_op_xor_t0_t2_t0, gen_op_xor_t0_t2_t1, gen_op_xor_t0_t2_t2 } },
/* param0 -> t1 */              {
/* param0 -> t1, param1 -> t0 */ { gen_op_xor_t1_t0_t0, gen_op_xor_t1_t0_t1, gen_op_xor_t1_t0_t2 },
/* param0 -> t1, param1 -> t1 */ { gen_op_xor_t1_t1_t0, gen_op_xor_t1_t1_t1, gen_op_xor_t1_t1_t2 },
/* param0 -> t1, param1 -> t2 */ { gen_op_xor_t1_t2_t0, gen_op_xor_t1_t2_t1, gen_op_xor_t1_t2_t2 } },
/* param0 -> t2 */              {
/* param0 -> t2, param1 -> t0 */ { gen_op_xor_t2_t0_t0, gen_op_xor_t2_t0_t1, gen_op_xor_t2_t0_t2 },
/* param0 -> t2, param1 -> t1 */ { gen_op_xor_t2_t1_t0, gen_op_xor_t2_t1_t1, gen_op_xor_t2_t1_t2 },
/* param0 -> t2, param1 -> t2 */ { gen_op_xor_t2_t2_t0, gen_op_xor_t2_t2_t1, gen_op_xor_t2_t2_t2 } } };
 
void gen_l_xor(struct op_queue *opq, int param_t[3], orreg_t param[3],
               int delay_slot)
{
  if(!param[0])
    return;
 
  if((param_t[2] != T_NONE) && (param[1] == param[2])) {
    clear_t[param_t[0]](opq, 1);
    return;
  }
 
  if(!param[2]) {
    if((param_t[2] == T_NONE) && (param[0] == param[1]))
      return;
    move_t_t[param_t[0]][param_t[1]](opq, 1);
    return;
  }
 
  if(!param[1]) {
    if(param_t[2] == T_NONE) {
      mov_t_imm[param_t[0]](opq, 1, param[2]);
      return;
    }
    move_t_t[param_t[0]][param_t[2]](opq, 1);
    return;
  }
 
  if(param_t[2] == T_NONE)
    l_xor_imm_t_table[param_t[0]][param_t[1]](opq, 1, param[2]);
  else
    l_xor_t_table[param_t[0]][param_t[1]][param_t[2]](opq, 1);
}
 
void gen_l_invalid(struct op_queue *opq, int param_t[3], orreg_t param[3],
                   int delay_slot)
{
  /* The program running on openrisc may decide to patch this location, so
   * just cross reference this location just-in-case */
  find_jump_loc(opq->insn_addr, opq);
  if(!delay_slot)
    gen_op_illegal(opq, 1);
  else
    gen_op_illegal_delay(opq, 1);
}
 
/*----------------------------------[ Floating point instructions (stubs) ]---*/
void gen_lf_add_s(struct op_queue *opq, int param_t[3], orreg_t param[3],
                  int delay_slot)
{
  gen_l_invalid(opq, param_t, param, delay_slot);
}
 
void gen_lf_div_s(struct op_queue *opq, int param_t[3], orreg_t param[3],
                  int delay_slot)
{
  gen_l_invalid(opq, param_t, param, delay_slot);
}
 
void gen_lf_ftoi_s(struct op_queue *opq, int param_t[3], orreg_t param[3],
                   int delay_slot)
{
  gen_l_invalid(opq, param_t, param, delay_slot);
}
 
void gen_lf_itof_s(struct op_queue *opq, int param_t[3], orreg_t param[3],
                   int delay_slot)
{
  gen_l_invalid(opq, param_t, param, delay_slot);
}
 
void gen_lf_madd_s(struct op_queue *opq, int param_t[3], orreg_t param[3],
                   int delay_slot)
{
  gen_l_invalid(opq, param_t, param, delay_slot);
}
 
void gen_lf_mul_s(struct op_queue *opq, int param_t[3], orreg_t param[3],
                  int delay_slot)
{
  gen_l_invalid(opq, param_t, param, delay_slot);
}
 
void gen_lf_rem_s(struct op_queue *opq, int param_t[3], orreg_t param[3],
                  int delay_slot)
{
  gen_l_invalid(opq, param_t, param, delay_slot);
}
 
void gen_lf_sfeq_s(struct op_queue *opq, int param_t[3], orreg_t param[3],
                   int delay_slot)
{
  gen_l_invalid(opq, param_t, param, delay_slot);
}
 
void gen_lf_sfge_s(struct op_queue *opq, int param_t[3], orreg_t param[3],
                   int delay_slot)
{
  gen_l_invalid(opq, param_t, param, delay_slot);
}
 
void gen_lf_sfgt_s(struct op_queue *opq, int param_t[3], orreg_t param[3],
                   int delay_slot)
{
  gen_l_invalid(opq, param_t, param, delay_slot);
}
 
void gen_lf_sfle_s(struct op_queue *opq, int param_t[3], orreg_t param[3],
                   int delay_slot)
{
  gen_l_invalid(opq, param_t, param, delay_slot);
}
 
void gen_lf_sflt_s(struct op_queue *opq, int param_t[3], orreg_t param[3],
                   int delay_slot)
{
  gen_l_invalid(opq, param_t, param, delay_slot);
}
 
void gen_lf_sfne_s(struct op_queue *opq, int param_t[3], orreg_t param[3],
                   int delay_slot)
{
  gen_l_invalid(opq, param_t, param, delay_slot);
}
 
void gen_lf_sub_s(struct op_queue *opq, int param_t[3], orreg_t param[3],
                  int delay_slot)
{
  gen_l_invalid(opq, param_t, param, delay_slot);
}
 
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.