//
// MIPS32.C
//
// Target CPU specific interface code for MIPS32 core
//
// Copyright (c) 2002, Jason Riffel - TotalEmbedded LLC.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions 
// are met:
//
// Redistributions of source code must retain the above copyright 
// notice, this list of conditions and the following disclaimer. 
//
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in 
// the documentation and/or other materials provided with the
// distribution. 
//
// Neither the name of TotalEmbedded nor the names of its 
// contributors may be used to endorse or promote products derived 
// from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
//

//
// INCLUDES
//
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>
#include "mips32.h"
#include "jtag.h"

//
// PROTOTYPES
//
static unsigned int fMIPS32_Endian       (unsigned int ui_data);
static unsigned int fMIPS32_ExitDebugMode(void);
static void         fMIPS32_HandleWrite  (unsigned int ui_address, unsigned int ui_data);
static unsigned int fMIPS32_HandleRead   (unsigned int ui_address);

//
// GLOBALS
//
unsigned int aui_registers[dMIPS32_NUM_DEBUG_REGISTERS];
unsigned int  ui_data_register;
unsigned int  ui_address_register;
unsigned int  ui_cp0datawatchlo_register;
unsigned int  ui_cp0datawatchhi_register;
unsigned int  ui_cp0instwatchlo_register;
unsigned int  ui_cp0instwatchhi_register;
unsigned int  ui_ejtag_state;
unsigned int  ui_last_signal;

//
// MIPS32_Init
//
// Performs intialization specific to the target CPU MIPS32
//
void fMIPS32_Init(void)
{
  unsigned int ui_result;

  // Call the JTAG low level init function
  fJTAG_Init();

  // Initial state for debug exception catcher - we always halt
  // on attach.
  ui_ejtag_state = dMIPS32_EJTAG_WAIT_FOR_CONTINUE;
  
  // We need to capture the processor.  If it is already in debug mode we need
  // to let it out and then bring it back because it is in some unknown state.
  ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_ACCESS_PENDING |
              dMIPS32_ECR_PROBE_ENABLE | dMIPS32_ECR_PROBE_VECTOR;

  fJTAG_Instruction(dJTAG_REGISTER_CONTROL);
  ui_result = fJTAG_Data(ui_result);

  // Check for reset
  if (ui_result & dMIPS32_ECR_CPU_RESET_OCCURED)
  {
    printf("JTAGEMUL: Processor was reset.\n");
    
    // Clear the reset and set our bits
    ui_result = dMIPS32_ECR_ACCESS_PENDING | dMIPS32_ECR_PROBE_ENABLE | 
                dMIPS32_ECR_PROBE_VECTOR;

    ui_result = fJTAG_Data(ui_result);
    
    // Now re-read our results without the reset clear command
    ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_ACCESS_PENDING |
                dMIPS32_ECR_PROBE_ENABLE | dMIPS32_ECR_PROBE_VECTOR;

    ui_result = fJTAG_Data(ui_result);
  }

  // Check for debug mode
  if (ui_result & dMIPS32_ECR_IN_DEBUG_MODE)
  {
    // We are in debug mode - Must exit
    printf("JTAGEMUL: Processor already in debug mode - Forcing exit.\n");
    fMIPS32_ExitDebugMode();
  }

  printf("JTAGEMUL: Issuing debug break.\n");

  // Force debug break  
  ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_ACCESS_PENDING |
              dMIPS32_ECR_PROBE_ENABLE | dMIPS32_ECR_PROBE_VECTOR |
              dMIPS32_ECR_EJTAG_BREAK;
  
  fJTAG_Instruction(dJTAG_REGISTER_CONTROL);
  ui_result = fJTAG_Data(ui_result);

  // Check to see if we have entered debug mode
  ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_ACCESS_PENDING |
              dMIPS32_ECR_PROBE_ENABLE | dMIPS32_ECR_PROBE_VECTOR;

  fJTAG_Instruction(dJTAG_REGISTER_CONTROL);
  ui_result = fJTAG_Data(ui_result);
      
  // Check for debug mode
  if (ui_result & dMIPS32_ECR_IN_DEBUG_MODE)
  {
    // We have entered debug mode.
    ui_ejtag_state = dMIPS32_EJTAG_WAIT_FOR_CONTINUE;
      	
    printf("JTAGEMUL: Debug exception!\n");
  }
  else
  {
    // Processor did NOT enter debug mode!
    printf("JTAGEMUL ERROR: Processor did not debug break!\n");
  }
  
  // Set last signal to a breakpoint/trap
  ui_last_signal=SIGTRAP;

  // Clear all hardware instruction/data breakpoints
  ui_cp0datawatchlo_register=0x00;
  ui_cp0datawatchhi_register=0x00;
  ui_cp0instwatchlo_register=0x00;
  ui_cp0instwatchhi_register=0x00;
  fMIPS32_ExecuteDebugModule(aui_hwbkptwrite_code);

  // Make hardware watchpoints generate debug exception using the
  // EJWATCH register bit 0.  EJWATCH is only an 8 bit register -
  // fJTAG_Instruction sends 32 bits, however - all JTAG interfaces
  // are just shift registers until the mode bit chages states.  So
  // if we just make the value mirror the requested 8 bit setting
  // the last byte sent will set the register correctly.
  ui_result = 0x01010101;
  fJTAG_Instruction(dJTAG_REGISTER_EJWATCH);
  ui_result = fJTAG_Data(ui_result);

  return;
}

//
// fMIPS32_EJTAG_Machine
//
// State machine that manages the the EJTAG processor module.
// This state machine hands instructions and memory accesses
// over the EJTAG port.
//
int fMIPS32_EJTAG_Machine(void)
{
  unsigned int ui_result;

  // Need to handle resets here on each execution.
  ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_ACCESS_PENDING |
              dMIPS32_ECR_PROBE_ENABLE | dMIPS32_ECR_PROBE_VECTOR;

  fJTAG_Instruction(dJTAG_REGISTER_CONTROL);
  ui_result = fJTAG_Data(ui_result);

  // Check for reset
  if (ui_result & dMIPS32_ECR_CPU_RESET_OCCURED)
  {
    // Reset occured - Must acknowlege this before EJTAG will work
    printf("JTAGEMUL: Processor was reset.\n");
    
    // Clear the reset and set our bits
    ui_result = dMIPS32_ECR_ACCESS_PENDING | dMIPS32_ECR_PROBE_ENABLE | 
                dMIPS32_ECR_PROBE_VECTOR;

    ui_result = fJTAG_Data(ui_result);
  }
  
  switch(ui_ejtag_state)
  {
    case dMIPS32_EJTAG_WAIT_FOR_DEBUG_MODE:
      // Check to see if we have entered debug mode

      ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_ACCESS_PENDING |
                  dMIPS32_ECR_PROBE_ENABLE | dMIPS32_ECR_PROBE_VECTOR;

      fJTAG_Instruction(dJTAG_REGISTER_CONTROL);
      ui_result = fJTAG_Data(ui_result);
      
      // Check for debug mode
      if (ui_result & dMIPS32_ECR_IN_DEBUG_MODE)
      {
      	// We have entered debug mode.
      	ui_ejtag_state = dMIPS32_EJTAG_WAIT_FOR_CONTINUE;
      	
//        printf("JTAGEMUL: Debug exception!\n");

        // Turn on the debug mode LED
        fMIPS32_ExecuteDebugModule(aui_led_code);

        // Read out the debug register to determine the cause
        fMIPS32_ExecuteDebugModule(aui_readdebug_code);

        // We're done with ui_result at this point - Use it as a count
        // of how many exceptions matched.  We only ever want to match
        // one exception at any given time.
        ui_result = 0x00;

        if (ui_data_register & dMIPS32_DEBUG_SW_BKPT)
        {
          ui_result++;
          ui_last_signal=SIGTRAP; // Breakpoint
        }
        if (ui_data_register & dMIPS32_DEBUG_HW_INST_BKPT)
        {
          ui_result++;
          ui_last_signal=SIGTRAP; // Breakpoint
        }
        if (ui_data_register & dMIPS32_DEBUG_HW_DATA_LD_BKPT)
        {
          ui_result++;
          ui_last_signal=SIGTRAP; // Breakpoint
        }
        if (ui_data_register & dMIPS32_DEBUG_HW_DATA_ST_BKPT)
        {
          ui_result++;
          ui_last_signal=SIGTRAP; // Breakpoint
        }
        if (ui_data_register & dMIPS32_DEBUG_HW_DATA_LD_IMP_BKPT)
        {
          ui_result++;
          ui_last_signal=SIGTRAP; // Breakpoint
        }
        if (ui_data_register & dMIPS32_DEBUG_HW_DATA_ST_IMP_BKPT)
        {
          ui_result++;
          ui_last_signal=SIGTRAP; // Breakpoint
        }
        if (ui_data_register & dMIPS32_DEBUG_SINGLE_STEP_BKPT)
        {
          ui_result++;
          ui_last_signal=SIGTRAP; // Breakpoint
          
          // We need to clear the single step bit
          fMIPS32_ExecuteDebugModule(aui_singlestepclear_code);
        }
        if (ui_data_register & dMIPS32_DEBUG_JTAG_BKPT)
        {
          ui_result++;
          ui_last_signal=SIGTSTP; // User stop
        }

        // Check for AU1500 specific hardware breakpoint
        ui_data_register = ui_data_register >> 10;
        ui_data_register &= 0x1F;
        if (dMIPS32_AU1500_HW_BKPT == ui_data_register)
        {
          ui_result++;
          ui_last_signal=SIGTRAP; // Breakpoint
        }

        if (ui_result == 0x00)
        {
          printf("JTAGEMUL ERROR: Unknown debug exception = 0x%08X\n", ui_data_register);
        }
        if (ui_result > 0x01)
        {
          printf("JTAGEMUL ERROR: Multiple debug exceptions = 0x%08X\n", ui_data_register);
        }

        return(-1); // Tell GDBRDP we entered debug mode
      }
      
      break;

    case dMIPS32_EJTAG_WAIT_FOR_CONTINUE:
      // Check to see if we have stayed in debug mode

      ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_ACCESS_PENDING |
                  dMIPS32_ECR_PROBE_ENABLE | dMIPS32_ECR_PROBE_VECTOR;

      fJTAG_Instruction(dJTAG_REGISTER_CONTROL);
      ui_result = fJTAG_Data(ui_result);
      
      // Check for debug mode
      if (!(ui_result & dMIPS32_ECR_IN_DEBUG_MODE))
      {
        // Program got away from us and is RUNNING!?!?!?! ARGH!
        printf("JTAGEMUL: Program got away, currently running!\n");

        ui_ejtag_state = dMIPS32_EJTAG_WAIT_FOR_DEBUG_MODE;
      }
      
      break;
      
    case dMIPS32_EJTAG_EXIT_DEBUG_MODE:
      if (!fMIPS32_ExitDebugMode()) 
      {
        printf("JTAGEMUL ERROR: Could not resume!\n");
      }

      ui_ejtag_state = dMIPS32_EJTAG_WAIT_FOR_DEBUG_MODE;
      break;

    case dMIPS32_EJTAG_FORCE_DEBUG_MODE:
      // Force debug break  
      ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_ACCESS_PENDING |
                  dMIPS32_ECR_PROBE_ENABLE | dMIPS32_ECR_PROBE_VECTOR |
                  dMIPS32_ECR_EJTAG_BREAK;
  
      fJTAG_Instruction(dJTAG_REGISTER_CONTROL);
      ui_result = fJTAG_Data(ui_result);

      ui_ejtag_state = dMIPS32_EJTAG_WAIT_FOR_DEBUG_MODE;
      break;
    
    default:
      // Should never get here
      ui_ejtag_state = dMIPS32_EJTAG_WAIT_FOR_DEBUG_MODE;
      printf("JTAGEMUL ERROR: Corrupted debug mode catcher state!\n");
      break;  
  }
  
  return(1);
}

//
// fMIPS32_Endian
//
// Handles conversion of data to proper 'endian'ness and return
// the result.
//
static unsigned int fMIPS32_Endian(unsigned int ui_data)
{
  return(ui_data);
}

//
// fMIPS32_ExitDebugMode
//
// Exits debug mode using the DERET instruction
static unsigned int fMIPS32_ExitDebugMode (void)
{
  unsigned int ui_result;

  // Feed the processor a DERET instruction.

  // Read the control register and make sure processor is attempting to 
  // read from dmseg.
  ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_ACCESS_PENDING |
              dMIPS32_ECR_PROBE_ENABLE | dMIPS32_ECR_PROBE_VECTOR;

  fJTAG_Instruction(dJTAG_REGISTER_CONTROL);
  ui_result = fJTAG_Data(ui_result);

  if (ui_result & dMIPS32_ECR_ACCESS_PENDING)
  {
    // Make sure processor is not writing
    if (ui_result & dMIPS32_ECR_ACCESS_RW) // Bit set for a WRITE
    {
      // Chip is writing, need to clear the write
      printf("EJTAGEMUL ERROR: Processor writing on debug exit.\n");  
      return(0);
    }

    // OK - Processor is reading, make sure its in the instruction space
    fJTAG_Instruction(dJTAG_REGISTER_ADDRESS);
    ui_result = fJTAG_Data(0x00); // Address is 0-write only
    if (ui_result > dMIPS32_DMSEG_RAM_BEGIN)
    {
      // Chip is not reading from the instruction area!
      printf("JTAGEMUL ERROR: Processor not reading instruction for DERET!\n");
      return(0);
    }

    // Chip is reading from instruction space - feed it a DERET
    // Write the DERET instruction into the data register            
    fJTAG_Instruction(dJTAG_REGISTER_DATA);
    fJTAG_Data(fMIPS32_Endian(dMIPS32_DERET_INSTRUCTION));

    // Now finish the processor access by clearing the access pending bit
    fJTAG_Instruction(dJTAG_REGISTER_CONTROL);

    ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_PROBE_ENABLE | 
                dMIPS32_ECR_PROBE_VECTOR;

    ui_result = fJTAG_Data(ui_result);

    // At this point we are done.  Even if the chip didn't exit debug mode
    // the debug mode catcher will grab it and report the reason to GDB.
//    printf("JTAGEMUL: Processor exited debug mode.\n");
    return(1);
  }
  else
  {
    // ERROR!  What to do if the processor is not accessing?
    printf("EJTAGEMUL ERROR: Processor not accessing for debug exit.\n");
    return(0);
  }

  // Should never get here!  
  return(0);
}

//
// fMIPS32_ExecuteDebugModule
//
// This will take an array of 32 bit values and feed them into the 
// processor via the EJTAG port as instructions.
//
void fMIPS32_ExecuteDebugModule(unsigned int *pui_module)
{
  unsigned int ui_stack[32];
  unsigned int ui_stack_index;
  unsigned int ui_result;
  unsigned int ui_address;
  unsigned int ui_data;
  unsigned int ui_finished;

  ui_stack_index = 0x00;
  ui_finished    = 0x00;

//  printf("JTAGEMUL: Executing a module.\n");

  // Feed that chip
  while (1)
  {
    // Read the control register.  Make sure an access is requested, then 
    // do it.
    ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_ACCESS_PENDING |
                dMIPS32_ECR_PROBE_ENABLE | dMIPS32_ECR_PROBE_VECTOR;
  
    fJTAG_Instruction(dJTAG_REGISTER_CONTROL);
    ui_result = fJTAG_Data(ui_result);
    
    if (!(ui_result & dMIPS32_ECR_ACCESS_PENDING))
    {
      printf("JTAGEMUL ERROR: No memory access in progress!\n");
      return;
    }

    fJTAG_Instruction(dJTAG_REGISTER_ADDRESS);
    ui_address = fJTAG_Data(0x00);
    
    // Check for read or write
    if (ui_result & dMIPS32_ECR_ACCESS_RW) // Bit set for a WRITE
    {
      // Read the data out
      fJTAG_Instruction(dJTAG_REGISTER_DATA);
      ui_data = fJTAG_Data(0x00);

      // Clear the access pending bit (let the processor eat!)
      ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_PROBE_ENABLE | 
                  dMIPS32_ECR_PROBE_VECTOR;
  
      fJTAG_Instruction(dJTAG_REGISTER_CONTROL);
      ui_result = fJTAG_Data(ui_result);

      // Processor is writing to us
      if (dMIPS32_PSEUDO_STACK_ADDR == ui_address)
      {
        // Stack write
//        printf("JTAGEMUL: Stack write of 0x%08X\n", ui_data);
        ui_stack[ui_stack_index++] = ui_data;
      }
      else
      {
        // Data out to the real world
//        printf("JTAGEMUL: Write 0x%08X to address 0x%08X.\n", ui_data, ui_address);
        fMIPS32_HandleWrite(ui_address, ui_data);
      }
    }
    else
    {
      // Check to see if its reading at the debug vector.  The first pass through
      // the module is always read at the vector, so the first one we allow.  When
      // the second read from the vector occurs we are done and just exit.
      if (dMIPS32_DMSEG_BASEADDRESS == ui_address)
      {
        if (ui_finished++) // Allows ONE pass
        {
//          printf("JTAGEMUL: Finished module.\n");
          return;
        }
      }

      // Processor is reading from us
      if (dMIPS32_PSEUDO_STACK_ADDR == ui_address)
      {
        // Stack read
        ui_data = ui_stack[--ui_stack_index];

//        printf("JTAGEMUL: Stack read of 0x%08X\n", ui_data);

        // Send the data out
        fJTAG_Instruction(dJTAG_REGISTER_DATA);
        ui_data = fJTAG_Data(ui_data);
        
        // Clear the access pending bit (let the processor eat!)
        ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_PROBE_ENABLE | 
                    dMIPS32_ECR_PROBE_VECTOR;
  
        fJTAG_Instruction(dJTAG_REGISTER_CONTROL);
        ui_result = fJTAG_Data(ui_result);
      }
      else if (ui_address < 0xFF2F0000)
      {
        // Reading an instruction from our module

        // Fetch the instruction from the module
//        printf("JTAGEMUL: Instruction read at 0x%08X ", ui_address); fflush(stdout);
        ui_data = (ui_address - dMIPS32_DMSEG_BASEADDRESS) / 4;
//        printf("offset -> %04d ", ui_data); fflush(stdout);
        ui_data = *(unsigned int *)(pui_module + ui_data);
//        printf("data -> 0x%08X\n", ui_data); fflush(stdout);

        // Send the data out
        fJTAG_Instruction(dJTAG_REGISTER_DATA);
        ui_data = fJTAG_Data(ui_data);

        // Clear the access pending bit (let the processor eat!)
        ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_PROBE_ENABLE | 
                    dMIPS32_ECR_PROBE_VECTOR;
  
        fJTAG_Instruction(dJTAG_REGISTER_CONTROL);
        ui_result = fJTAG_Data(ui_result);
      }
      else
      {
      	// A read from dmseg virtual registers
      	ui_data = fMIPS32_HandleRead(ui_address);

//        printf("JTAGEMUL: Read address 0x%08X result = 0x%08X.\n", ui_address, ui_data);

        // Send the data out
        fJTAG_Instruction(dJTAG_REGISTER_DATA);
        ui_data = fJTAG_Data(ui_data);

        // Clear the access pending bit (let the processor eat!)
        ui_result = dMIPS32_ECR_CPU_RESET_OCCURED | dMIPS32_ECR_PROBE_ENABLE | 
                    dMIPS32_ECR_PROBE_VECTOR;
  
        fJTAG_Instruction(dJTAG_REGISTER_CONTROL);
        ui_result = fJTAG_Data(ui_result);
      }
    }    
  }  
}

//
// fMIPS32_HandleWrite
//
// This routine takes the data and address from a write and puts it in the 
// correct location.
//
static void fMIPS32_HandleWrite(unsigned int ui_address, unsigned int ui_data)
{
  switch(ui_address)
  {
    case (dMIPS32_REGISTER_LOCATION + ( 0 * 4)): aui_registers[ 0] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + ( 1 * 4)): aui_registers[ 1] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + ( 2 * 4)): aui_registers[ 2] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + ( 3 * 4)): aui_registers[ 3] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + ( 4 * 4)): aui_registers[ 4] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + ( 5 * 4)): aui_registers[ 5] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + ( 6 * 4)): aui_registers[ 6] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + ( 7 * 4)): aui_registers[ 7] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + ( 8 * 4)): aui_registers[ 8] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + ( 9 * 4)): aui_registers[ 9] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (10 * 4)): aui_registers[10] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (11 * 4)): aui_registers[11] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (12 * 4)): aui_registers[12] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (13 * 4)): aui_registers[13] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (14 * 4)): aui_registers[14] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (15 * 4)): aui_registers[15] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (16 * 4)): aui_registers[16] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (17 * 4)): aui_registers[17] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (18 * 4)): aui_registers[18] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (19 * 4)): aui_registers[19] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (20 * 4)): aui_registers[20] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (21 * 4)): aui_registers[21] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (22 * 4)): aui_registers[22] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (23 * 4)): aui_registers[23] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (24 * 4)): aui_registers[24] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (25 * 4)): aui_registers[25] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (26 * 4)): aui_registers[26] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (27 * 4)): aui_registers[27] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (28 * 4)): aui_registers[28] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (29 * 4)): aui_registers[29] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (30 * 4)): aui_registers[30] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (31 * 4)): aui_registers[31] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (32 * 4)): aui_registers[32] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (33 * 4)): aui_registers[33] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (34 * 4)): aui_registers[34] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (35 * 4)): aui_registers[35] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (36 * 4)): aui_registers[36] = ui_data; break;
    case (dMIPS32_REGISTER_LOCATION + (37 * 4)): aui_registers[37] = ui_data; break;

    case dMIPS32_ADDRESS_ACCESS: ui_address_register        = ui_data; break;
    case dMIPS32_DATA_ACCESS:    ui_data_register           = ui_data; break;
    case dMIPS32_CP0DATAWATCHLO: ui_cp0datawatchlo_register = ui_data; break;
    case dMIPS32_CP0DATAWATCHHI: ui_cp0datawatchhi_register = ui_data; break;
    case dMIPS32_CP0INSTWATCHLO: ui_cp0instwatchlo_register = ui_data; break;
    case dMIPS32_CP0INSTWATCHHI: ui_cp0instwatchhi_register = ui_data; break;

    default:
      printf("JTAGEMUL: Write to unknown location 0x%08X with 0x%08X data.\n", ui_address, ui_data);
      break;
  }
}

//
// fMIPS32_HandleRead
//
// This routine takes the address from a read and returns the correct data
//
static unsigned int fMIPS32_HandleRead(unsigned int ui_address)
{
  switch(ui_address)
  {
    case (dMIPS32_REGISTER_LOCATION + ( 0 * 4)): return(aui_registers[ 0]);
    case (dMIPS32_REGISTER_LOCATION + ( 1 * 4)): return(aui_registers[ 1]);
    case (dMIPS32_REGISTER_LOCATION + ( 2 * 4)): return(aui_registers[ 2]);
    case (dMIPS32_REGISTER_LOCATION + ( 3 * 4)): return(aui_registers[ 3]);
    case (dMIPS32_REGISTER_LOCATION + ( 4 * 4)): return(aui_registers[ 4]);
    case (dMIPS32_REGISTER_LOCATION + ( 5 * 4)): return(aui_registers[ 5]);
    case (dMIPS32_REGISTER_LOCATION + ( 6 * 4)): return(aui_registers[ 6]);
    case (dMIPS32_REGISTER_LOCATION + ( 7 * 4)): return(aui_registers[ 7]);
    case (dMIPS32_REGISTER_LOCATION + ( 8 * 4)): return(aui_registers[ 8]);
    case (dMIPS32_REGISTER_LOCATION + ( 9 * 4)): return(aui_registers[ 9]);
    case (dMIPS32_REGISTER_LOCATION + (10 * 4)): return(aui_registers[10]);
    case (dMIPS32_REGISTER_LOCATION + (11 * 4)): return(aui_registers[11]);
    case (dMIPS32_REGISTER_LOCATION + (12 * 4)): return(aui_registers[12]);
    case (dMIPS32_REGISTER_LOCATION + (13 * 4)): return(aui_registers[13]);
    case (dMIPS32_REGISTER_LOCATION + (14 * 4)): return(aui_registers[14]);
    case (dMIPS32_REGISTER_LOCATION + (15 * 4)): return(aui_registers[15]);
    case (dMIPS32_REGISTER_LOCATION + (16 * 4)): return(aui_registers[16]);
    case (dMIPS32_REGISTER_LOCATION + (17 * 4)): return(aui_registers[17]);
    case (dMIPS32_REGISTER_LOCATION + (18 * 4)): return(aui_registers[18]);
    case (dMIPS32_REGISTER_LOCATION + (19 * 4)): return(aui_registers[19]);
    case (dMIPS32_REGISTER_LOCATION + (20 * 4)): return(aui_registers[20]);
    case (dMIPS32_REGISTER_LOCATION + (21 * 4)): return(aui_registers[21]);
    case (dMIPS32_REGISTER_LOCATION + (22 * 4)): return(aui_registers[22]);
    case (dMIPS32_REGISTER_LOCATION + (23 * 4)): return(aui_registers[23]);
    case (dMIPS32_REGISTER_LOCATION + (24 * 4)): return(aui_registers[24]);
    case (dMIPS32_REGISTER_LOCATION + (25 * 4)): return(aui_registers[25]);
    case (dMIPS32_REGISTER_LOCATION + (26 * 4)): return(aui_registers[26]);
    case (dMIPS32_REGISTER_LOCATION + (27 * 4)): return(aui_registers[27]);
    case (dMIPS32_REGISTER_LOCATION + (28 * 4)): return(aui_registers[28]);
    case (dMIPS32_REGISTER_LOCATION + (29 * 4)): return(aui_registers[29]);
    case (dMIPS32_REGISTER_LOCATION + (30 * 4)): return(aui_registers[30]);
    case (dMIPS32_REGISTER_LOCATION + (31 * 4)): return(aui_registers[31]);
    case (dMIPS32_REGISTER_LOCATION + (32 * 4)): return(aui_registers[32]);
    case (dMIPS32_REGISTER_LOCATION + (33 * 4)): return(aui_registers[33]);
    case (dMIPS32_REGISTER_LOCATION + (34 * 4)): return(aui_registers[34]);
    case (dMIPS32_REGISTER_LOCATION + (35 * 4)): return(aui_registers[35]);
    case (dMIPS32_REGISTER_LOCATION + (36 * 4)): return(aui_registers[36]);
    case (dMIPS32_REGISTER_LOCATION + (37 * 4)): return(aui_registers[37]);

    case dMIPS32_ADDRESS_ACCESS: return(ui_address_register);        break;
    case dMIPS32_DATA_ACCESS:    return(ui_data_register);           break;
    case dMIPS32_CP0DATAWATCHLO: return(ui_cp0datawatchlo_register); break;
    case dMIPS32_CP0DATAWATCHHI: return(ui_cp0datawatchhi_register); break;
    case dMIPS32_CP0INSTWATCHLO: return(ui_cp0instwatchlo_register); break;
    case dMIPS32_CP0INSTWATCHHI: return(ui_cp0instwatchhi_register); break;
      
    default:
      printf("JTAGEMUL: Read to unknown location 0x%08X.\n", ui_address);
      break;
  }
  
  return(0);
}
