aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/src/mem_map_funcs.cpp
blob: ee2f792780df57d6df5c68dc0b68478e73ca7bed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/**
 * Copyright (C) 2014 Citra Emulator
 *
 * @file    mem_map_funcs.cpp
 * @author  ShizZy <shizzy247@gmail.com>
 * @date    2013-09-18
 * @brief   Memory map R/W functions
 *
 * @section LICENSE
 * 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 at
 * http://www.gnu.org/copyleft/gpl.html
 *
 * Official project repository can be found at:
 * http://code.google.com/p/gekko-gc-emu/
 */

#include "common.h"

#include "mem_map.h"
#include "hw/hw.h"

namespace Memory {

template <typename T>
inline void _Read(T &var, const u32 addr) {
    // TODO: Figure out the fastest order of tests for both read and write (they are probably different).
    // TODO: Make sure this represents the mirrors in a correct way.
    // Could just do a base-relative read, too.... TODO

    // Hardware I/O register reads
    // 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space
    if ((addr & 0xFF000000) == 0x10000000 || (addr & 0xFF000000) == 0x1E000000) {
        HW::Read<T>(var, addr);

    // FCRAM virtual address reads
    } else if ((addr & 0x3E000000) == 0x08000000) {
        var = *((const T*)&g_fcram[addr & MEM_FCRAM_MASK]);

    // Scratchpad memory
    } else if (addr > MEM_SCRATCHPAD_VADDR && addr <= (MEM_SCRATCHPAD_VADDR + MEM_SCRATCHPAD_SIZE)) {
        var = *((const T*)&g_scratchpad[addr & MEM_SCRATCHPAD_MASK]);
 
    /*else if ((addr & 0x3F800000) == 0x04000000) {
        var = *((const T*)&m_pVRAM[addr & VRAM_MASK]);
    }*/

    // HACK(bunnei): There is no layer yet to translate virtual addresses to physical addresses. 
    // Until we progress far enough along, we'll accept all physical address reads here. I think 
    // that this is typically a corner-case from usermode software unless they are trying to do 
    // bare-metal things (e.g. early 3DS homebrew writes directly to the FB @ 0x20184E60, etc.
    } else if (((addr & 0xF0000000) == MEM_FCRAM_PADDR) && (addr < (MEM_FCRAM_PADDR_END))) {
        var = *((const T*)&g_fcram[addr & MEM_FCRAM_MASK]);

    } else {
        _assert_msg_(MEMMAP, false, "unknown memory read");
    }
}

template <typename T>
inline void _Write(u32 addr, const T data) {
    
    // Hardware I/O register writes
    // 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space
    if ((addr & 0xFF000000) == 0x10000000 || (addr & 0xFF000000) == 0x1E000000) {
        HW::Write<const T>(addr, data);
    
    // ExeFS:/.code is loaded here:
    } else if ((addr & 0xFFF00000) == 0x00100000) {
        // TODO(ShizZy): This is dumb... handle correctly. From 3DBrew:
        // http://3dbrew.org/wiki/Memory_layout#ARM11_User-land_memory_regions
        // The ExeFS:/.code is loaded here, executables must be loaded to the 0x00100000 region when
        // the exheader "special memory" flag is clear. The 0x03F00000-byte size restriction only 
        // applies when this flag is clear. Executables are usually loaded to 0x14000000 when the 
        // exheader "special memory" flag is set, however this address can be arbitrary.
        *(T*)&g_fcram[addr & MEM_FCRAM_MASK] = data;

    // Scratchpad memory
    } else if (addr > MEM_SCRATCHPAD_VADDR && addr <= (MEM_SCRATCHPAD_VADDR + MEM_SCRATCHPAD_SIZE)) {
        *(T*)&g_scratchpad[addr & MEM_SCRATCHPAD_MASK] = data;

    // Heap mapped by ControlMemory:
    } else if ((addr & 0x3E000000) == 0x08000000) {
        // TODO(ShizZy): Writes to this virtual address should be put in physical memory at FCRAM + GSP
        // heap size... the following is writing to FCRAM + 0, which is actually supposed to be the 
        // application's GSP heap
        *(T*)&g_fcram[addr & MEM_FCRAM_MASK] = data;

    } else if ((addr & 0xFF000000) == 0x14000000) {
        _assert_msg_(MEMMAP, false, "umimplemented write to GSP heap");
    } else if ((addr & 0xFFF00000) == 0x1EC00000) {
        _assert_msg_(MEMMAP, false, "umimplemented write to IO registers");
    } else if ((addr & 0xFF000000) == 0x1F000000) {
        _assert_msg_(MEMMAP, false, "umimplemented write to VRAM");
    } else if ((addr & 0xFFF00000) == 0x1FF00000) {
        _assert_msg_(MEMMAP, false, "umimplemented write to DSP memory");
    } else if ((addr & 0xFFFF0000) == 0x1FF80000) {
        _assert_msg_(MEMMAP, false, "umimplemented write to Configuration Memory");
    } else if ((addr & 0xFFFFF000) == 0x1FF81000) {
        _assert_msg_(MEMMAP, false, "umimplemented write to shared page");
    
    // HACK(bunnei): There is no layer yet to translate virtual addresses to physical addresses. 
    // Until we progress far enough along, we'll accept all physical address writes here. I think 
    // that this is typically a corner-case from usermode software unless they are trying to do 
    // bare-metal things (e.g. early 3DS homebrew writes directly to the FB @ 0x20184E60, etc.
    } else if (((addr & 0xF0000000) == MEM_FCRAM_PADDR) && (addr < (MEM_FCRAM_PADDR_END))) {
        *(T*)&g_fcram[addr & MEM_FCRAM_MASK] = data;

    // Error out...
    } else {
        _assert_msg_(MEMMAP, false, "unknown memory write");
    }
}

bool IsValidAddress(const u32 addr) {
    if ((addr & 0x3E000000) == 0x08000000) {
        return true;
    } else if ((addr & 0x3F800000) == 0x04000000) {
        return true;
    } else if ((addr & 0xBFFF0000) == 0x00010000) {
        return true;
    } else if ((addr & 0x3F000000) >= 0x08000000 && (addr & 0x3F000000) < 0x08000000 + MEM_FCRAM_MASK) {
        return true;
    } else {
        return false;
    }
}

u8 *GetPointer(const u32 addr) {
    // TODO(bunnei): Just a stub for now... ImplementMe!
    if ((addr & 0x3E000000) == 0x08000000) {
        return g_fcram + (addr & MEM_FCRAM_MASK);

    // HACK(bunnei): There is no layer yet to translate virtual addresses to physical addresses. 
    // Until we progress far enough along, we'll accept all physical address reads here. I think 
    // that this is typically a corner-case from usermode software unless they are trying to do 
    // bare-metal things (e.g. early 3DS homebrew writes directly to the FB @ 0x20184E60, etc.
    } else if (((addr & 0xF0000000) == MEM_FCRAM_PADDR) && (addr < (MEM_FCRAM_PADDR_END))) {
        return g_fcram + (addr & MEM_FCRAM_MASK);

    //else if ((addr & 0x3F800000) == 0x04000000) {
    //    return g_vram + (addr & MEM_VRAM_MASK);
    //}
    //else if ((addr & 0x3F000000) >= 0x08000000 && (addr & 0x3F000000) < 0x08000000 + g_MemorySize) {
    //    return m_pRAM + (addr & g_MemoryMask);
    //}
    } else {
        //ERROR_LOG(MEMMAP, "Unknown GetPointer %08x PC %08x LR %08x", addr, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
        ERROR_LOG(MEMMAP, "Unknown GetPointer %08x", addr);
        static bool reported = false;
        //if (!reported) {
        //    Reporting::ReportMessage("Unknown GetPointer %08x PC %08x LR %08x", addr, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
        //    reported = true;
        //}
        //if (!g_Config.bIgnoreBadMemAccess) {
        //    Core_EnableStepping(true);
        //    host->SetDebugMode(true);
        //}
        return 0;
    }
}

u8 Read8(const u32 addr) {
    u8 _var = 0;
    _Read<u8>(_var, addr);
    return (u8)_var;
}

u16 Read16(const u32 addr) {
    u16_le _var = 0;
    _Read<u16_le>(_var, addr);
    return (u16)_var;
}

u32 Read32(const u32 addr) {
    u32_le _var = 0;
    _Read<u32_le>(_var, addr);
    return _var;
}

u64 Read64(const u32 addr) {
    u64_le _var = 0;
    _Read<u64_le>(_var, addr);
    return _var;
}

u32 Read8_ZX(const u32 addr) {
    return (u32)Read8(addr);
}

u32 Read16_ZX(const u32 addr) {
    return (u32)Read16(addr);
}

void Write8(const u32 addr, const u8 data) {
    _Write<u8>(addr, data);
}

void Write16(const u32 addr, const u16 data) {
    _Write<u16_le>(addr, data);
}

void Write32(const u32 addr, const u32 data) {
    _Write<u32_le>(addr, data);
}

void Write64(const u32 addr, const u64 data) {
    _Write<u64_le>(addr, data);
}

} // namespace