/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *   Mupen64plus - memory.c                                                *
 *   Mupen64Plus homepage: http://code.google.com/p/mupen64plus/           *
 *   Copyright (C) 2002 Hacktarux                                          *
 *                                                                         *
 *   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.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "memory.h"

#include "../api/m64p_types.h"
#include "../api/callbacks.h"

#include "../main/device.h"
#include "../main/main.h"

#include "../r4300/new_dynarec/new_dynarec.h"
#include "../r4300/r4300_core.h"

#include "../rdp/rdp_core.h"
#include "../rsp/rsp_core.h"

#include "../ai/ai_controller.h"
#include "../pi/pi_controller.h"
#include "../ri/ri_controller.h"
#include "../si/si_controller.h"
#include "../vi/vi_controller.h"
#include "../dd/dd_controller.h"

#ifdef DBG
#include "../debugger/dbg_types.h"
#include "../debugger/dbg_memory.h"
#include "../debugger/dbg_breakpoints.h"

#include <string.h>
#endif

#include <stddef.h>
#include <stdint.h>

#if NEW_DYNAREC != NEW_DYNAREC_ARM
// address : address of the read/write operation being done
uint32_t address = 0;
#endif

// values that are being written are stored in these variables
#if NEW_DYNAREC != NEW_DYNAREC_ARM
uint32_t cpu_word;
uint8_t cpu_byte;
uint16_t cpu_hword;
uint64_t cpu_dword;
#endif

// address where the read value will be stored
uint64_t* rdword;

// hash tables of read functions
void (*readmem[0x10000])(void);
void (*readmemb[0x10000])(void);
void (*readmemh[0x10000])(void);
void (*readmemd[0x10000])(void);

// hash tables of write functions
void (*writemem[0x10000])(void);
void (*writememb[0x10000])(void);
void (*writememd[0x10000])(void);
void (*writememh[0x10000])(void);

typedef int (*readfn)(void*,uint32_t,uint32_t*);
typedef int (*writefn)(void*,uint32_t,uint32_t,uint32_t);

#ifndef BYTE4_XOR_BE
#ifdef MSB_FIRST
#define BYTE4_XOR_BE(a) (a)
#else
#define BYTE4_XOR_BE(a) ((a) ^ 3)
#endif
#endif

#ifndef BSHIFT
#define BSHIFT(a) (BYTE4_XOR_BE((a & 3)) << 3)
#endif

#ifndef HSHIFT
#define HSHIFT(a) (((a & 2) ^ 2) << 3)
#endif

static int readb(readfn read_word, void* opaque, uint32_t address, uint64_t* value)
{
   uint32_t w;
   unsigned shift = BSHIFT(address);
   int     result = read_word(opaque, address, &w);
   *value = (w >> shift) & 0xff;
   return result;
}

static int readh(readfn read_word, void* opaque, uint32_t address, uint64_t* value)
{
   uint32_t w;
   unsigned shift = HSHIFT(address);
   int result     = read_word(opaque, address, &w);
   *value = (w >> shift) & 0xffff;
   return result;
}

static int readw(readfn read_word, void* opaque, uint32_t address, uint64_t* value)
{
   uint32_t w;
   int result = read_word(opaque, address, &w);
   *value = w;
   return result;
}

static int readd(readfn read_word, void* opaque, uint32_t address, uint64_t* value)
{
   uint32_t w[2];
   int result =
      read_word(opaque, address , &w[0]);
   read_word(opaque, address + 4, &w[1]);
   *value = ((uint64_t)w[0] << 32) | w[1];
   return result;
}

static int writeb(writefn write_word, void *opaque, uint32_t address, uint8_t value)
{
   unsigned int shift = BSHIFT(address);
   uint32_t w         = (uint32_t)value << shift;
   uint32_t mask      = (uint32_t)0xff << shift;
   return write_word(opaque, address, w, mask);
}

static int writeh(writefn write_word, void *opaque, uint32_t address, uint16_t value)
{
   unsigned int shift = HSHIFT(address);
   uint32_t w         = (uint32_t)value << shift;
   uint32_t mask      = (uint32_t)0xffff << shift;
   return write_word(opaque, address, w, mask);
}

static int writew(writefn write_word, void *opaque, uint32_t address, uint32_t value)
{
   return write_word(opaque, address, value, ~0U);
}

static int writed(writefn write_word, void *opaque, uint32_t address, uint64_t value)
{
   int result;
   const uint64_t doubleword = (uint64_t)value;
   const uint32_t word_hi    = (uint32_t)(doubleword >> 32);
   const uint32_t word_lo    = (uint32_t)(doubleword >>  0);

   result =
      write_word(opaque, address + 0, word_hi, ~0U);
   write_word   (opaque, address + 4, word_lo, ~0U);
   return result;
}

static void read_nothing(void)
{
    *rdword = 0;
}

static void read_nothingb(void)
{
    *rdword = 0;
}

static void read_nothingh(void)
{
    *rdword = 0;
}

static void read_nothingd(void)
{
    *rdword = 0;
}

static void write_nothing(void)
{
}

static void write_nothingb(void)
{
}

static void write_nothingh(void)
{
}

static void write_nothingd(void)
{
}

static void read_nomem(void)
{
    address = virtual_to_physical_address(&g_dev.r4300, address,0);
    if (address == 0x00000000) return;
    read_word_in_memory();
}

static void read_nomemb(void)
{
    address = virtual_to_physical_address(&g_dev.r4300, address,0);
    if (address == 0x00000000) return;
    read_byte_in_memory();
}

static void read_nomemh(void)
{
    address = virtual_to_physical_address(&g_dev.r4300, address,0);
    if (address == 0x00000000) return;
    read_hword_in_memory();
}

static void read_nomemd(void)
{
    address = virtual_to_physical_address(&g_dev.r4300, address,0);
    if (address == 0x00000000) return;
    read_dword_in_memory();
}

static void write_nomem(void)
{
    invalidate_r4300_cached_code(address, 4);
    address = virtual_to_physical_address(&g_dev.r4300, address,1);
    if (address == 0x00000000) return;
    write_word_in_memory();
}

static void write_nomemb(void)
{
    invalidate_r4300_cached_code(address, 1);
    address = virtual_to_physical_address(&g_dev.r4300, address,1);
    if (address == 0x00000000) return;
    write_byte_in_memory();
}

static void write_nomemh(void)
{
    invalidate_r4300_cached_code(address, 2);
    address = virtual_to_physical_address(&g_dev.r4300, address,1);
    if (address == 0x00000000) return;
    write_hword_in_memory();
}

static void write_nomemd(void)
{
    invalidate_r4300_cached_code(address, 8);
    address = virtual_to_physical_address(&g_dev.r4300, address,1);
    if (address == 0x00000000) return;
    write_dword_in_memory();
}


void read_rdram(void)
{
    readw(read_rdram_dram, &g_dev.ri, address, rdword);
}

void read_rdramb(void)
{
    readb(read_rdram_dram, &g_dev.ri, address, rdword);
}

void read_rdramh(void)
{
    readh(read_rdram_dram, &g_dev.ri, address, rdword);
}

void read_rdramd(void)
{
    readd(read_rdram_dram, &g_dev.ri, address, rdword);
}

void write_rdram(void)
{
    writew(write_rdram_dram, &g_dev.ri, address, cpu_word);
}

void write_rdramb(void)
{
    writeb(write_rdram_dram, &g_dev.ri, address, cpu_byte);
}

void write_rdramh(void)
{
    writeh(write_rdram_dram, &g_dev.ri, address, cpu_hword);
}

void write_rdramd(void)
{
    writed(write_rdram_dram, &g_dev.ri, address, cpu_dword);
}


void read_rdramFB(void)
{
    readw(read_rdram_fb, &g_dev.dp, address, rdword);
}

void read_rdramFBb(void)
{
    readb(read_rdram_fb, &g_dev.dp, address, rdword);
}

void read_rdramFBh(void)
{
    readh(read_rdram_fb, &g_dev.dp, address, rdword);
}

void read_rdramFBd(void)
{
    readd(read_rdram_fb, &g_dev.dp, address, rdword);
}

void write_rdramFB(void)
{
    writew(write_rdram_fb, &g_dev.dp, address, cpu_word);
}

void write_rdramFBb(void)
{
    writeb(write_rdram_fb, &g_dev.dp, address, cpu_byte);
}

void write_rdramFBh(void)
{
    writeh(write_rdram_fb, &g_dev.dp, address, cpu_hword);
}

void write_rdramFBd(void)
{
    writed(write_rdram_fb, &g_dev.dp, address, cpu_dword);
}


static void read_rdramreg(void)
{
    readw(read_rdram_regs, &g_dev.ri, address, rdword);
}

static void read_rdramregb(void)
{
    readb(read_rdram_regs, &g_dev.ri, address, rdword);
}

static void read_rdramregh(void)
{
    readh(read_rdram_regs, &g_dev.ri, address, rdword);
}

static void read_rdramregd(void)
{
    readd(read_rdram_regs, &g_dev.ri, address, rdword);
}

static void write_rdramreg(void)
{
    writew(write_rdram_regs, &g_dev.ri, address, cpu_word);
}

static void write_rdramregb(void)
{
    writeb(write_rdram_regs, &g_dev.ri, address, cpu_byte);
}

static void write_rdramregh(void)
{
    writeh(write_rdram_regs, &g_dev.ri, address, cpu_hword);
}

static void write_rdramregd(void)
{
    writed(write_rdram_regs, &g_dev.ri, address, cpu_dword);
}


static void read_rspmem(void)
{
    readw(read_rsp_mem, &g_dev.sp, address, rdword);
}

static void read_rspmemb(void)
{
    readb(read_rsp_mem, &g_dev.sp, address, rdword);
}

static void read_rspmemh(void)
{
    readh(read_rsp_mem, &g_dev.sp, address, rdword);
}

static void read_rspmemd(void)
{
    readd(read_rsp_mem, &g_dev.sp, address, rdword);
}

static void write_rspmem(void)
{
    writew(write_rsp_mem, &g_dev.sp, address, cpu_word);
}

static void write_rspmemb(void)
{
    writeb(write_rsp_mem, &g_dev.sp, address, cpu_byte);
}

static void write_rspmemh(void)
{
    writeh(write_rsp_mem, &g_dev.sp, address, cpu_hword);
}

static void write_rspmemd(void)
{
    writed(write_rsp_mem, &g_dev.sp, address, cpu_dword);
}


static void read_rspreg(void)
{
    readw(read_rsp_regs, &g_dev.sp, address, rdword);
}

static void read_rspregb(void)
{
    readb(read_rsp_regs, &g_dev.sp, address, rdword);
}

static void read_rspregh(void)
{
    readh(read_rsp_regs, &g_dev.sp, address, rdword);
}

static void read_rspregd(void)
{
    readd(read_rsp_regs, &g_dev.sp, address, rdword);
}

static void write_rspreg(void)
{
    writew(write_rsp_regs, &g_dev.sp, address, cpu_word);
}

static void write_rspregb(void)
{
    writeb(write_rsp_regs, &g_dev.sp, address, cpu_byte);
}

static void write_rspregh(void)
{
    writeh(write_rsp_regs, &g_dev.sp, address, cpu_hword);
}

static void write_rspregd(void)
{
    writed(write_rsp_regs, &g_dev.sp, address, cpu_dword);
}


static void read_rspreg2(void)
{
    readw(read_rsp_regs2, &g_dev.sp, address, rdword);
}

static void read_rspreg2b(void)
{
    readb(read_rsp_regs2, &g_dev.sp, address, rdword);
}

static void read_rspreg2h(void)
{
    readh(read_rsp_regs2, &g_dev.sp, address, rdword);
}

static void read_rspreg2d(void)
{
    readd(read_rsp_regs2, &g_dev.sp, address, rdword);
}

static void write_rspreg2(void)
{
    writew(write_rsp_regs2, &g_dev.sp, address, cpu_word);
}

static void write_rspreg2b(void)
{
    writeb(write_rsp_regs2, &g_dev.sp, address, cpu_byte);
}

static void write_rspreg2h(void)
{
    writeh(write_rsp_regs2, &g_dev.sp, address, cpu_hword);
}

static void write_rspreg2d(void)
{
    writed(write_rsp_regs2, &g_dev.sp, address, cpu_dword);
}


static void read_dp(void)
{
    readw(read_dpc_regs, &g_dev.dp, address, rdword);
}

static void read_dpb(void)
{
    readb(read_dpc_regs, &g_dev.dp, address, rdword);
}

static void read_dph(void)
{
    readh(read_dpc_regs, &g_dev.dp, address, rdword);
}

static void read_dpd(void)
{
    readd(read_dpc_regs, &g_dev.dp, address, rdword);
}

static void write_dp(void)
{
    writew(write_dpc_regs, &g_dev.dp, address, cpu_word);
}

static void write_dpb(void)
{
    writeb(write_dpc_regs, &g_dev.dp, address, cpu_byte);
}

static void write_dph(void)
{
    writeh(write_dpc_regs, &g_dev.dp, address, cpu_hword);
}

static void write_dpd(void)
{
    writed(write_dpc_regs, &g_dev.dp, address, cpu_dword);
}


static void read_dps(void)
{
    readw(read_dps_regs, &g_dev.dp, address, rdword);
}

static void read_dpsb(void)
{
    readb(read_dps_regs, &g_dev.dp, address, rdword);
}

static void read_dpsh(void)
{
    readh(read_dps_regs, &g_dev.dp, address, rdword);
}

static void read_dpsd(void)
{
    readd(read_dps_regs, &g_dev.dp, address, rdword);
}

static void write_dps(void)
{
    writew(write_dps_regs, &g_dev.dp, address, cpu_word);
}

static void write_dpsb(void)
{
    writeb(write_dps_regs, &g_dev.dp, address, cpu_byte);
}

static void write_dpsh(void)
{
    writeh(write_dps_regs, &g_dev.dp, address, cpu_hword);
}

static void write_dpsd(void)
{
    writed(write_dps_regs, &g_dev.dp, address, cpu_dword);
}


static void read_mi(void)
{
    readw(read_mi_regs, &g_dev.r4300, address, rdword);
}

static void read_mib(void)
{
    readb(read_mi_regs, &g_dev.r4300, address, rdword);
}

static void read_mih(void)
{
    readh(read_mi_regs, &g_dev.r4300, address, rdword);
}

static void read_mid(void)
{
    readd(read_mi_regs, &g_dev.r4300, address, rdword);
}

void write_mi(void)
{
    writew(write_mi_regs, &g_dev.r4300, address, cpu_word);
}

void write_mib(void)
{
    writeb(write_mi_regs, &g_dev.r4300, address, cpu_byte);
}

void write_mih(void)
{
    writeh(write_mi_regs, &g_dev.r4300, address, cpu_hword);
}

void write_mid(void)
{
    writed(write_mi_regs, &g_dev.r4300, address, cpu_dword);
}


static void read_vi(void)
{
    readw(read_vi_regs, &g_dev.vi, address, rdword);
}

static void read_vib(void)
{
    readb(read_vi_regs, &g_dev.vi, address, rdword);
}

static void read_vih(void)
{
    readh(read_vi_regs, &g_dev.vi, address, rdword);
}

static void read_vid(void)
{
    readd(read_vi_regs, &g_dev.vi, address, rdword);
}

static void write_vi(void)
{
    writew(write_vi_regs, &g_dev.vi, address, cpu_word);
}

static void write_vib(void)
{
    writeb(write_vi_regs, &g_dev.vi, address, cpu_byte);
}

static void write_vih(void)
{
    writeh(write_vi_regs, &g_dev.vi, address, cpu_hword);
}

static void write_vid(void)
{
    writed(write_vi_regs, &g_dev.vi, address, cpu_dword);
}


static void read_ai(void)
{
    readw(read_ai_regs, &g_dev.ai, address, rdword);
}

static void read_aib(void)
{
    readb(read_ai_regs, &g_dev.ai, address, rdword);
}

static void read_aih(void)
{
    readh(read_ai_regs, &g_dev.ai, address, rdword);
}

static void read_aid(void)
{
    readd(read_ai_regs, &g_dev.ai, address, rdword);
}

static void write_ai(void)
{
    writew(write_ai_regs, &g_dev.ai, address, cpu_word);
}

static void write_aib(void)
{
    writeb(write_ai_regs, &g_dev.ai, address, cpu_byte);
}

static void write_aih(void)
{
    writeh(write_ai_regs, &g_dev.ai, address, cpu_hword);
}

static void write_aid(void)
{
    writed(write_ai_regs, &g_dev.ai, address, cpu_dword);
}


static void read_pi(void)
{
    readw(read_pi_regs, &g_dev.pi, address, rdword);
}

static void read_pib(void)
{
    readb(read_pi_regs, &g_dev.pi, address, rdword);
}

static void read_pih(void)
{
    readh(read_pi_regs, &g_dev.pi, address, rdword);
}

static void read_pid(void)
{
    readd(read_pi_regs, &g_dev.pi, address, rdword);
}

static void write_pi(void)
{
    writew(write_pi_regs, &g_dev.pi, address, cpu_word);
}

static void write_pib(void)
{
    writeb(write_pi_regs, &g_dev.pi, address, cpu_byte);
}

static void write_pih(void)
{
    writeh(write_pi_regs, &g_dev.pi, address, cpu_hword);
}

static void write_pid(void)
{
    writed(write_pi_regs, &g_dev.pi, address, cpu_dword);
}


static void read_ri(void)
{
    readw(read_ri_regs, &g_dev.ri, address, rdword);
}

static void read_rib(void)
{
    readb(read_ri_regs, &g_dev.ri, address, rdword);
}

static void read_rih(void)
{
    readh(read_ri_regs, &g_dev.ri, address, rdword);
}

static void read_rid(void)
{
    readd(read_ri_regs, &g_dev.ri, address, rdword);
}

static void write_ri(void)
{
    writew(write_ri_regs, &g_dev.ri, address, cpu_word);
}

static void write_rib(void)
{
    writeb(write_ri_regs, &g_dev.ri, address, cpu_byte);
}

static void write_rih(void)
{
    writeh(write_ri_regs, &g_dev.ri, address, cpu_hword);
}

static void write_rid(void)
{
    writed(write_ri_regs, &g_dev.ri, address, cpu_dword);
}


static void read_si(void)
{
    readw(read_si_regs, &g_dev.si, address, rdword);
}

static void read_sib(void)
{
    readb(read_si_regs, &g_dev.si, address, rdword);
}

static void read_sih(void)
{
    readh(read_si_regs, &g_dev.si, address, rdword);
}

static void read_sid(void)
{
    readd(read_si_regs, &g_dev.si, address, rdword);
}

static void write_si(void)
{
    writew(write_si_regs, &g_dev.si, address, cpu_word);
}

static void write_sib(void)
{
    writeb(write_si_regs, &g_dev.si, address, cpu_byte);
}

static void write_sih(void)
{
    writeh(write_si_regs, &g_dev.si, address, cpu_hword);
}

static void write_sid(void)
{
    writed(write_si_regs, &g_dev.si, address, cpu_dword);
}

static void read_pi_flashram_status(void)
{
    readw(read_flashram_status, &g_dev.pi, address, rdword);
}

static void read_pi_flashram_statusb(void)
{
    readb(read_flashram_status, &g_dev.pi, address, rdword);
}

static void read_pi_flashram_statush(void)
{
    readh(read_flashram_status, &g_dev.pi, address, rdword);
}

static void read_pi_flashram_statusd(void)
{
    readd(read_flashram_status, &g_dev.pi, address, rdword);
}

static void write_pi_flashram_command(void)
{
    writew(write_flashram_command, &g_dev.pi, address, cpu_word);
}

static void write_pi_flashram_commandb(void)
{
    writeb(write_flashram_command, &g_dev.pi, address, cpu_byte);
}

static void write_pi_flashram_commandh(void)
{
    writeh(write_flashram_command, &g_dev.pi, address, cpu_hword);
}

static void write_pi_flashram_commandd(void)
{
    writed(write_flashram_command, &g_dev.pi, address, cpu_dword);
}


static void read_rom(void)
{
    readw(read_cart_rom, &g_dev.pi, address, rdword);
}

static void read_romb(void)
{
    readb(read_cart_rom, &g_dev.pi, address, rdword);
}

static void read_romh(void)
{
    readh(read_cart_rom, &g_dev.pi, address, rdword);
}

static void read_romd(void)
{
    readd(read_cart_rom, &g_dev.pi, address, rdword);
}

static void write_rom(void)
{
    writew(write_cart_rom, &g_dev.pi, address, cpu_word);
}


static void read_pif(void)
{
    readw(read_pif_ram, &g_dev.si, address, rdword);
}

static void read_pifb(void)
{
    readb(read_pif_ram, &g_dev.si, address, rdword);
}

static void read_pifh(void)
{
    readh(read_pif_ram, &g_dev.si, address, rdword);
}

static void read_pifd(void)
{
    readd(read_pif_ram, &g_dev.si, address, rdword);
}

static void write_pif(void)
{
    writew(write_pif_ram, &g_dev.si, address, cpu_word);
}

static void write_pifb(void)
{
    writeb(write_pif_ram, &g_dev.si, address, cpu_byte);
}

static void write_pifh(void)
{
    writeh(write_pif_ram, &g_dev.si, address, cpu_hword);
}

static void write_pifd(void)
{
    writed(write_pif_ram, &g_dev.si, address, cpu_dword);
}

static void read_dd(void)
{
    readw(read_dd_regs, &g_dev.dd, address, rdword);
}

static void read_ddb(void)
{
    readb(read_dd_regs, &g_dev.dd, address, rdword);
}

static void read_ddh(void)
{
    readh(read_dd_regs, &g_dev.dd, address, rdword);
}

static void read_ddd(void)
{
    readd(read_dd_regs, &g_dev.dd, address, rdword);
}

static void write_dd(void)
{
    writew(write_dd_regs, &g_dev.dd, address, cpu_word);
}

static void write_ddb(void)
{
    writeb(write_dd_regs, &g_dev.dd, address, cpu_byte);
}

static void write_ddh(void)
{
    writeh(write_dd_regs, &g_dev.dd, address, cpu_hword);
}

static void write_ddd(void)
{
    writed(write_dd_regs, &g_dev.dd, address, cpu_dword);
}

static void read_ddipl(void)
{
   readw(read_dd_ipl, &g_dev.pi, address, rdword);
}

static void read_ddiplb(void)
{
   readb(read_dd_ipl, &g_dev.pi, address, rdword);
}

static void read_ddiplh(void)
{
   readh(read_dd_ipl, &g_dev.pi, address, rdword);
}

static void read_ddipld(void)
{
   readd(read_dd_ipl, &g_dev.pi, address, rdword);
}

static void write_ddipl(void)
{
   writew(write_dd_ipl, &g_dev.pi, address, cpu_word);
}

#ifdef DBG
static int memtype[0x10000];
static void (*saved_readmemb[0x10000])(void);
static void (*saved_readmemh[0x10000])(void);
static void (*saved_readmem [0x10000])(void);
static void (*saved_readmemd[0x10000])(void);
static void (*saved_writememb[0x10000])(void);
static void (*saved_writememh[0x10000])(void);
static void (*saved_writemem [0x10000])(void);
static void (*saved_writememd[0x10000])(void);

static void readmemb_with_bp_checks(void)
{
   check_breakpoints_on_mem_access((*r4300_pc())-0x4, address, 1,
         M64P_BKP_FLAG_ENABLED | M64P_BKP_FLAG_READ);

   saved_readmemb[address>>16]();
}

static void readmemh_with_bp_checks(void)
{
   check_breakpoints_on_mem_access((*r4300_pc())-0x4, address, 2,
         M64P_BKP_FLAG_ENABLED | M64P_BKP_FLAG_READ);

   saved_readmemh[address>>16]();
}

static void readmem_with_bp_checks(void)
{
   check_breakpoints_on_mem_access((*r4300_pc())-0x4, address, 4,
         M64P_BKP_FLAG_ENABLED | M64P_BKP_FLAG_READ);

   saved_readmem[address>>16]();
}

static void readmemd_with_bp_checks(void)
{
   check_breakpoints_on_mem_access((*r4300_pc())-0x4, address, 8,
         M64P_BKP_FLAG_ENABLED | M64P_BKP_FLAG_READ);

   saved_readmemd[address>>16]();
}

static void writememb_with_bp_checks(void)
{
   check_breakpoints_on_mem_access((*r4300_pc())-0x4, address, 1,
         M64P_BKP_FLAG_ENABLED | M64P_BKP_FLAG_WRITE);

   return saved_writememb[address>>16]();
}

static void writememh_with_bp_checks(void)
{
   check_breakpoints_on_mem_access((*r4300_pc())-0x4, address, 2,
         M64P_BKP_FLAG_ENABLED | M64P_BKP_FLAG_WRITE);

   return saved_writememh[address>>16]();
}

static void writemem_with_bp_checks(void)
{
   check_breakpoints_on_mem_access((*r4300_pc())-0x4, address, 4,
         M64P_BKP_FLAG_ENABLED | M64P_BKP_FLAG_WRITE);

   return saved_writemem[address>>16]();
}

static void writememd_with_bp_checks(void)
{
   check_breakpoints_on_mem_access((*r4300_pc())-0x4, address, 8,
         M64P_BKP_FLAG_ENABLED | M64P_BKP_FLAG_WRITE);

   return saved_writememd[address>>16]();
}

void activate_memory_break_read(uint32_t address)
{
   uint16_t region = address >> 16;

   if (saved_readmem[region] != NULL)
      return;

   saved_readmemb[region] = readmemb[region];
   saved_readmemh[region] = readmemh[region];
   saved_readmem [region] = readmem [region];
   saved_readmemd[region] = readmemd[region];
   readmemb[region] = readmemb_with_bp_checks;
   readmemh[region] = readmemh_with_bp_checks;
   readmem [region] = readmem_with_bp_checks;
   readmemd[region] = readmemd_with_bp_checks;
}

void deactivate_memory_break_read(uint32_t address)
{
   uint16_t region = address >> 16;

   if (saved_readmem[region] == NULL)
      return;

   readmemb[region] = saved_readmemb[region];
   readmemh[region] = saved_readmemh[region];
   readmem [region] = saved_readmem [region];
   readmemd[region] = saved_readmemd[region];
   saved_readmemb[region] = NULL;
   saved_readmemh[region] = NULL;
   saved_readmem [region] = NULL;
   saved_readmemd[region] = NULL;
}

void activate_memory_break_write(uint32_t address)
{
   uint16_t region = address >> 16;

   if (saved_writemem[region] != NULL)
      return;

   saved_writememb[region] = writememb[region];
   saved_writememh[region] = writememh[region];
   saved_writemem [region] = writemem [region];
   saved_writememd[region] = writememd[region];
   writememb[region] = writememb_with_bp_checks;
   writememh[region] = writememh_with_bp_checks;
   writemem [region] = writemem_with_bp_checks;
   writememd[region] = writememd_with_bp_checks;
}

void deactivate_memory_break_write(uint32_t address)
{
   uint16_t region = address >> 16;

   if (saved_writemem[region] == NULL)
      return;

   writememb[region] = saved_writememb[region];
   writememh[region] = saved_writememh[region];
   writemem [region] = saved_writemem [region];
   writememd[region] = saved_writememd[region];
   saved_writememb[region] = NULL;
   saved_writememh[region] = NULL;
   saved_writemem [region] = NULL;
   saved_writememd[region] = NULL;
}

int get_memory_type(uint32_t address)
{
   return memtype[address >> 16];
}
#endif

#define R(x) read_ ## x ## b, read_ ## x ## h, read_ ## x, read_ ## x ## d
#define W(x) write_ ## x ## b, write_ ## x ## h, write_ ## x, write_ ## x ## d
#define RW(x) R(x), W(x)

void poweron_memory(void)
{
   int i;

#ifdef DBG
   memset(saved_readmem, 0, 0x10000*sizeof(saved_readmem[0]));
   memset(saved_writemem, 0, 0x10000*sizeof(saved_writemem[0]));
#endif

   /* clear mappings */
   for (i = 0; i < 0x10000; ++i)
   {
      map_region(i, M64P_MEM_NOMEM, RW(nomem));
   }

   /* map RDRAM */
   for (i = 0; i< 0x80; ++i)
   {
      map_region(0x8000+i, M64P_MEM_RDRAM, RW(rdram));
      map_region(0xa000+i, M64P_MEM_RDRAM, RW(rdram));
   }

   for(i = 0x80; i < 0x3f0; ++i)
   {
      map_region(0x8000+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa000+i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map RDRAM registers */
   map_region(0x83f0, M64P_MEM_RDRAMREG, RW(rdramreg));
   map_region(0xa3f0, M64P_MEM_RDRAMREG, RW(rdramreg));
   for(i = 1; i < 0x10; ++i)
   {
      map_region(0x83f0+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa3f0+i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map RSP memory */
   map_region(0x8400, M64P_MEM_RSPMEM, RW(rspmem));
   map_region(0xa400, M64P_MEM_RSPMEM, RW(rspmem));
   for (i=0x1; i<0x4; i++)
   {
      map_region(0x8400+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa400+i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map RSP registers (1) */
   map_region(0x8404, M64P_MEM_RSPREG, RW(rspreg));
   map_region(0xa404, M64P_MEM_RSPREG, RW(rspreg));
   for (i=0x5; i<0x8; i++)
   {
      map_region(0x8400+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa400+i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map RSP registers (2) */
   map_region(0x8408, M64P_MEM_RSP, RW(rspreg2));
   map_region(0xa408, M64P_MEM_RSP, RW(rspreg2));

   for(i = 0x9; i < 0x10; ++i)
   {
      map_region(0x8400+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa400+i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map DPC registers */
   map_region(0x8410, M64P_MEM_DP, RW(dp));
   map_region(0xa410, M64P_MEM_DP, RW(dp));
   for(i = 1; i < 0x10; ++i)
   {
      map_region(0x8410+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa410+i, M64P_MEM_NOTHING, RW(nothing));
   }

   map_region(0x8420, M64P_MEM_DPS, RW(dps));
   map_region(0xa420, M64P_MEM_DPS, RW(dps));
   for(i = 1; i < 0x10; ++i)
   {
      map_region(0x8420+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa420+i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map MI registers */
   map_region(0x8430, M64P_MEM_MI, RW(mi));
   map_region(0xa430, M64P_MEM_MI, RW(mi));

   for(i = 1; i < 0x10; ++i)
   {
      map_region(0x8430+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa430+i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map VI registers */
   map_region(0x8440, M64P_MEM_VI, RW(vi));
   map_region(0xa440, M64P_MEM_VI, RW(vi));
   for(i = 1; i < 0x10; ++i)
   {
      map_region(0x8440+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa440+i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map AI registers */
   map_region(0x8450, M64P_MEM_AI, RW(ai));
   map_region(0xa450, M64P_MEM_AI, RW(ai));

   for(i = 1; i < 0x10; ++i)
   {
      map_region(0x8450+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa450+i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map PI registers */

   map_region(0x8460, M64P_MEM_PI, RW(pi));
   map_region(0xa460, M64P_MEM_PI, RW(pi));
   for(i = 1; i < 0x10; ++i)
   {
      map_region(0x8460+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa460+i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map RI registers */
   map_region(0x8470, M64P_MEM_RI, RW(ri));
   map_region(0xa470, M64P_MEM_RI, RW(ri));
   for(i = 1; i < 0x10; ++i)
   {
      map_region(0x8470+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa470+i, M64P_MEM_NOTHING, RW(nothing));
   }

    /* map SI registers */
   map_region(0x8480, M64P_MEM_SI, RW(si));
   map_region(0xa480, M64P_MEM_SI, RW(si));
   for(i = 0x481; i < 0x500; ++i)
   {
      map_region(0x8000+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa000+i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map DD registers */
   map_region(0x8500, M64P_MEM_DD, RW(dd));
   map_region(0xa500, M64P_MEM_DD, RW(dd));
   for(i = 0x501; i < 0x600; ++i)
   {
      map_region(0x8000+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa000+i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map DD IPL ROM */
   for (i = 0x600; i < 0x640; ++i)
   {
      map_region(0x8000 + i, M64P_MEM_DD, R(ddipl), W(nothing));
      map_region(0xa000 + i, M64P_MEM_DD, R(ddipl), W(nothing));
   }
   for (i = 0x640; i < 0x800; ++i)
   {
      map_region(0x8000 + i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa000 + i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map flashram/sram */
   map_region(0x8800, M64P_MEM_FLASHRAMSTAT, R(pi_flashram_status), W(nothing));
   map_region(0xa800, M64P_MEM_FLASHRAMSTAT, R(pi_flashram_status), W(nothing));
   map_region(0x8801, M64P_MEM_NOTHING, R(nothing), W(pi_flashram_command));
   map_region(0xa801, M64P_MEM_NOTHING, R(nothing), W(pi_flashram_command));
   for(i = 0x802; i < 0x1000; ++i)
   {
      map_region(0x8000+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xa000+i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map cart ROM */

   for(i = 0; i < (g_dev.pi.cart_rom.rom_size >> 16); ++i)
   {
      map_region(0x9000+i, M64P_MEM_ROM, R(rom), W(nothing));
      map_region(0xb000+i, M64P_MEM_ROM, R(rom),
            write_nothingb, write_nothingh, write_rom, write_nothingd);
   }
   for(i = (g_dev.pi.cart_rom.rom_size >> 16); i < 0xfc0; ++i)
   {
      map_region(0x9000+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xb000+i, M64P_MEM_NOTHING, RW(nothing));
   }

   /* map PIF RAM */
   map_region(0x9fc0, M64P_MEM_PIF, RW(pif));
   map_region(0xbfc0, M64P_MEM_PIF, RW(pif));
   for(i = 0xfc1; i < 0x1000; ++i)
   {
      map_region(0x9000+i, M64P_MEM_NOTHING, RW(nothing));
      map_region(0xb000+i, M64P_MEM_NOTHING, RW(nothing));
   }
}

static void map_region_t(uint16_t region, int type)
{
#ifdef DBG
   memtype[region] = type;
#else
   (void)region;
   (void)type;
#endif
}

static void map_region_r(uint16_t region,
 void (*read8)(void),
 void (*read16)(void),
 void (*read32)(void),
 void (*read64)(void))
{
#ifdef DBG
   if (lookup_breakpoint(((uint32_t)region << 16), 0x10000,
            M64P_BKP_FLAG_ENABLED | M64P_BKP_FLAG_READ) != -1)
   {
      saved_readmemb[region] = read8;
      saved_readmemh[region] = read16;
      saved_readmem [region] = read32;
      saved_readmemd[region] = read64;
      readmemb[region] = readmemb_with_bp_checks;
      readmemh[region] = readmemh_with_bp_checks;
      readmem [region] = readmem_with_bp_checks;
      readmemd[region] = readmemd_with_bp_checks;
   }
   else
#endif
   {
      readmemb[region] = read8;
      readmemh[region] = read16;
      readmem [region] = read32;
      readmemd[region] = read64;
   }
}

void map_region_w(uint16_t region,
 void (*write8)(void),
 void (*write16)(void),
 void (*write32)(void),
 void (*write64)(void))
{
#ifdef DBG
   if (lookup_breakpoint(((uint32_t)region << 16), 0x10000,
            M64P_BKP_FLAG_ENABLED | M64P_BKP_FLAG_WRITE) != -1)
   {
      saved_writememb[region] = write8;
      saved_writememh[region] = write16;
      saved_writemem [region] = write32;
      saved_writememd[region] = write64;
      writememb[region] = writememb_with_bp_checks;
      writememh[region] = writememh_with_bp_checks;
      writemem [region] = writemem_with_bp_checks;
      writememd[region] = writememd_with_bp_checks;
   }
   else
#endif
   {
      writememb[region] = write8;
      writememh[region] = write16;
      writemem [region] = write32;
      writememd[region] = write64;
   }
}

void map_region(uint16_t region,
      int type,
 void (*read8)(void),
 void (*read16)(void),
 void (*read32)(void),
 void (*read64)(void),
 void (*write8)(void),
 void (*write16)(void),
 void (*write32)(void),
 void (*write64)(void))
{
   map_region_t(region, type);
   map_region_r(region, read8, read16, read32, read64);
   map_region_w(region, write8, write16, write32, write64);
}

uint32_t *fast_mem_access(uint32_t address)
{
   /* This code is performance critical, specially on pure interpreter mode.
    * Removing error checking saves some time, but the emulator may crash. */
   if ((address & 0xc0000000) != 0x80000000)
      address = virtual_to_physical_address(&g_dev.r4300, address, 2);

   address &= UINT32_C(0x1ffffffc);

   if (address < RDRAM_MAX_SIZE)
      return (uint32_t*)((uint8_t*)g_dev.ri.rdram.dram + address);
   else if (address >= UINT32_C(0x10000000))
      return (uint32_t*)((uint8_t*)g_dev.pi.cart_rom.rom + address - UINT32_C(0x10000000));
   else if ((address & UINT32_C(0xffffe000)) == UINT32_C(0x04000000))
      return (uint32_t*)((uint8_t*)g_dev.sp.mem + (address & UINT32_C(0x1ffc)));
   return NULL;
}
