nkeynes@10 | 1 | /**
|
nkeynes@586 | 2 | * $Id$
|
nkeynes@929 | 3 | * sh4mem.c is responsible for interfacing between the SH4's internal memory
|
nkeynes@10 | 4 | *
|
nkeynes@10 | 5 | * Copyright (c) 2005 Nathan Keynes.
|
nkeynes@10 | 6 | *
|
nkeynes@10 | 7 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@10 | 8 | * it under the terms of the GNU General Public License as published by
|
nkeynes@10 | 9 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@10 | 10 | * (at your option) any later version.
|
nkeynes@10 | 11 | *
|
nkeynes@10 | 12 | * This program is distributed in the hope that it will be useful,
|
nkeynes@10 | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@10 | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@10 | 15 | * GNU General Public License for more details.
|
nkeynes@10 | 16 | */
|
nkeynes@10 | 17 |
|
nkeynes@35 | 18 | #define MODULE sh4_module
|
nkeynes@35 | 19 |
|
nkeynes@10 | 20 | #include <string.h>
|
nkeynes@10 | 21 | #include <zlib.h>
|
nkeynes@10 | 22 | #include "dream.h"
|
nkeynes@10 | 23 | #include "mem.h"
|
nkeynes@10 | 24 | #include "mmio.h"
|
nkeynes@10 | 25 | #include "dreamcast.h"
|
nkeynes@422 | 26 | #include "sh4/sh4core.h"
|
nkeynes@422 | 27 | #include "sh4/sh4mmio.h"
|
nkeynes@422 | 28 | #include "sh4/xltcache.h"
|
nkeynes@100 | 29 | #include "pvr2/pvr2.h"
|
nkeynes@10 | 30 |
|
nkeynes@929 | 31 | /* System regions (probably should be defined elsewhere) */
|
nkeynes@929 | 32 | extern struct mem_region_fn mem_region_unmapped;
|
nkeynes@929 | 33 | extern struct mem_region_fn mem_region_sdram;
|
nkeynes@929 | 34 | extern struct mem_region_fn mem_region_vram32;
|
nkeynes@929 | 35 | extern struct mem_region_fn mem_region_vram64;
|
nkeynes@929 | 36 | extern struct mem_region_fn mem_region_audioram;
|
nkeynes@929 | 37 | extern struct mem_region_fn mem_region_flashram;
|
nkeynes@929 | 38 | extern struct mem_region_fn mem_region_bootrom;
|
nkeynes@10 | 39 |
|
nkeynes@929 | 40 | /* On-chip regions other than defined MMIO regions */
|
nkeynes@933 | 41 | extern struct mem_region_fn p4_region_storequeue;
|
nkeynes@933 | 42 | extern struct mem_region_fn p4_region_icache_addr;
|
nkeynes@933 | 43 | extern struct mem_region_fn p4_region_icache_data;
|
nkeynes@933 | 44 | extern struct mem_region_fn p4_region_ocache_addr;
|
nkeynes@933 | 45 | extern struct mem_region_fn p4_region_ocache_data;
|
nkeynes@933 | 46 | extern struct mem_region_fn p4_region_itlb_addr;
|
nkeynes@933 | 47 | extern struct mem_region_fn p4_region_itlb_data;
|
nkeynes@933 | 48 | extern struct mem_region_fn p4_region_utlb_addr;
|
nkeynes@933 | 49 | extern struct mem_region_fn p4_region_utlb_data;
|
nkeynes@10 | 50 |
|
nkeynes@929 | 51 | /********************* The main ram address space **********************/
|
nkeynes@929 | 52 | static int32_t FASTCALL ext_sdram_read_long( sh4addr_t addr )
|
nkeynes@929 | 53 | {
|
nkeynes@934 | 54 | return *((int32_t *)(dc_main_ram + (addr&0x00FFFFFF)));
|
nkeynes@929 | 55 | }
|
nkeynes@929 | 56 | static int32_t FASTCALL ext_sdram_read_word( sh4addr_t addr )
|
nkeynes@929 | 57 | {
|
nkeynes@934 | 58 | return SIGNEXT16(*((int16_t *)(dc_main_ram + (addr&0x00FFFFFF))));
|
nkeynes@929 | 59 | }
|
nkeynes@929 | 60 | static int32_t FASTCALL ext_sdram_read_byte( sh4addr_t addr )
|
nkeynes@929 | 61 | {
|
nkeynes@934 | 62 | return SIGNEXT8(*((int16_t *)(dc_main_ram + (addr&0x00FFFFFF))));
|
nkeynes@929 | 63 | }
|
nkeynes@929 | 64 | static void FASTCALL ext_sdram_write_long( sh4addr_t addr, uint32_t val )
|
nkeynes@929 | 65 | {
|
nkeynes@934 | 66 | *(uint32_t *)(dc_main_ram + (addr&0x00FFFFFF)) = val;
|
nkeynes@929 | 67 | xlat_invalidate_long(addr);
|
nkeynes@929 | 68 | }
|
nkeynes@929 | 69 | static void FASTCALL ext_sdram_write_word( sh4addr_t addr, uint32_t val )
|
nkeynes@929 | 70 | {
|
nkeynes@934 | 71 | *(uint16_t *)(dc_main_ram + (addr&0x00FFFFFF)) = (uint16_t)val;
|
nkeynes@929 | 72 | xlat_invalidate_word(addr);
|
nkeynes@929 | 73 | }
|
nkeynes@929 | 74 | static void FASTCALL ext_sdram_write_byte( sh4addr_t addr, uint32_t val )
|
nkeynes@929 | 75 | {
|
nkeynes@934 | 76 | *(uint8_t *)(dc_main_ram + (addr&0x00FFFFFF)) = (uint8_t)val;
|
nkeynes@929 | 77 | xlat_invalidate_word(addr);
|
nkeynes@929 | 78 | }
|
nkeynes@929 | 79 | static void FASTCALL ext_sdram_read_burst( unsigned char *dest, sh4addr_t addr )
|
nkeynes@929 | 80 | {
|
nkeynes@934 | 81 | memcpy( dest, dc_main_ram+(addr&0x00FFFFFF), 32 );
|
nkeynes@929 | 82 | }
|
nkeynes@929 | 83 | static void FASTCALL ext_sdram_write_burst( sh4addr_t addr, unsigned char *src )
|
nkeynes@929 | 84 | {
|
nkeynes@934 | 85 | memcpy( dc_main_ram+(addr&0x00FFFFFF), src, 32 );
|
nkeynes@929 | 86 | }
|
nkeynes@929 | 87 |
|
nkeynes@929 | 88 | struct mem_region_fn mem_region_sdram = { ext_sdram_read_long, ext_sdram_write_long,
|
nkeynes@929 | 89 | ext_sdram_read_word, ext_sdram_write_word,
|
nkeynes@929 | 90 | ext_sdram_read_byte, ext_sdram_write_byte,
|
nkeynes@929 | 91 | ext_sdram_read_burst, ext_sdram_write_burst };
|
nkeynes@929 | 92 |
|
nkeynes@929 | 93 |
|
nkeynes@931 | 94 | /***************************** P4 Regions ************************************/
|
nkeynes@929 | 95 |
|
nkeynes@931 | 96 | /* Store-queue (long-write only?) */
|
nkeynes@931 | 97 | static void FASTCALL p4_storequeue_write_long( sh4addr_t addr, uint32_t val )
|
nkeynes@931 | 98 | {
|
nkeynes@931 | 99 | sh4r.store_queue[(addr>>2)&0xF] = val;
|
nkeynes@931 | 100 | }
|
nkeynes@931 | 101 | static int32_t FASTCALL p4_storequeue_read_long( sh4addr_t addr )
|
nkeynes@931 | 102 | {
|
nkeynes@931 | 103 | return sh4r.store_queue[(addr>>2)&0xF];
|
nkeynes@931 | 104 | }
|
nkeynes@931 | 105 |
|
nkeynes@931 | 106 | struct mem_region_fn p4_region_storequeue = {
|
nkeynes@931 | 107 | p4_storequeue_read_long, p4_storequeue_write_long,
|
nkeynes@931 | 108 | p4_storequeue_read_long, p4_storequeue_write_long,
|
nkeynes@931 | 109 | p4_storequeue_read_long, p4_storequeue_write_long,
|
nkeynes@931 | 110 | unmapped_read_burst, unmapped_write_burst }; // No burst access.
|
nkeynes@931 | 111 |
|
nkeynes@931 | 112 | /* TLB access */
|
nkeynes@929 | 113 | struct mem_region_fn p4_region_itlb_addr = {
|
nkeynes@929 | 114 | mmu_itlb_addr_read, mmu_itlb_addr_write,
|
nkeynes@929 | 115 | mmu_itlb_addr_read, mmu_itlb_addr_write,
|
nkeynes@929 | 116 | mmu_itlb_addr_read, mmu_itlb_addr_write,
|
nkeynes@929 | 117 | unmapped_read_burst, unmapped_write_burst };
|
nkeynes@929 | 118 | struct mem_region_fn p4_region_itlb_data = {
|
nkeynes@929 | 119 | mmu_itlb_data_read, mmu_itlb_data_write,
|
nkeynes@929 | 120 | mmu_itlb_data_read, mmu_itlb_data_write,
|
nkeynes@929 | 121 | mmu_itlb_data_read, mmu_itlb_data_write,
|
nkeynes@929 | 122 | unmapped_read_burst, unmapped_write_burst };
|
nkeynes@929 | 123 | struct mem_region_fn p4_region_utlb_addr = {
|
nkeynes@929 | 124 | mmu_utlb_addr_read, mmu_utlb_addr_write,
|
nkeynes@929 | 125 | mmu_utlb_addr_read, mmu_utlb_addr_write,
|
nkeynes@929 | 126 | mmu_utlb_addr_read, mmu_utlb_addr_write,
|
nkeynes@929 | 127 | unmapped_read_burst, unmapped_write_burst };
|
nkeynes@929 | 128 | struct mem_region_fn p4_region_utlb_data = {
|
nkeynes@929 | 129 | mmu_utlb_data_read, mmu_utlb_data_write,
|
nkeynes@929 | 130 | mmu_utlb_data_read, mmu_utlb_data_write,
|
nkeynes@929 | 131 | mmu_utlb_data_read, mmu_utlb_data_write,
|
nkeynes@929 | 132 | unmapped_read_burst, unmapped_write_burst };
|
nkeynes@929 | 133 |
|
nkeynes@931 | 134 | /********************** Initialization *************************/
|
nkeynes@931 | 135 |
|
nkeynes@931 | 136 | mem_region_fn_t *sh4_address_space;
|
nkeynes@931 | 137 |
|
nkeynes@931 | 138 | static void sh4_register_mem_region( uint32_t start, uint32_t end, mem_region_fn_t fn )
|
nkeynes@931 | 139 | {
|
nkeynes@931 | 140 | int count = (end - start) >> 12;
|
nkeynes@931 | 141 | mem_region_fn_t *ptr = &sh4_address_space[start>>12];
|
nkeynes@931 | 142 | while( count-- > 0 ) {
|
nkeynes@931 | 143 | *ptr++ = fn;
|
nkeynes@931 | 144 | }
|
nkeynes@931 | 145 | }
|
nkeynes@931 | 146 |
|
nkeynes@931 | 147 | static gboolean sh4_ext_page_remapped( sh4addr_t page, mem_region_fn_t fn, void *user_data )
|
nkeynes@931 | 148 | {
|
nkeynes@931 | 149 | int i;
|
nkeynes@931 | 150 | for( i=0; i<= 0xC0000000; i+= 0x20000000 ) {
|
nkeynes@931 | 151 | sh4_address_space[(page|i)>>12] = fn;
|
nkeynes@931 | 152 | }
|
nkeynes@931 | 153 | }
|
nkeynes@931 | 154 |
|
nkeynes@931 | 155 |
|
nkeynes@931 | 156 | void sh4_mem_init()
|
nkeynes@931 | 157 | {
|
nkeynes@931 | 158 | int i;
|
nkeynes@931 | 159 | mem_region_fn_t *ptr;
|
nkeynes@931 | 160 | sh4_address_space = mem_alloc_pages( sizeof(mem_region_fn_t) * 256 );
|
nkeynes@931 | 161 | for( i=0, ptr = sh4_address_space; i<7; i++, ptr += LXDREAM_PAGE_TABLE_ENTRIES ) {
|
nkeynes@931 | 162 | memcpy( ptr, ext_address_space, sizeof(mem_region_fn_t) * LXDREAM_PAGE_TABLE_ENTRIES );
|
nkeynes@931 | 163 | }
|
nkeynes@931 | 164 |
|
nkeynes@931 | 165 | /* Setup main P4 regions */
|
nkeynes@931 | 166 | sh4_register_mem_region( 0xE0000000, 0xE4000000, &p4_region_storequeue );
|
nkeynes@931 | 167 | sh4_register_mem_region( 0xE4000000, 0xF0000000, &mem_region_unmapped );
|
nkeynes@931 | 168 | sh4_register_mem_region( 0xF0000000, 0xF1000000, &p4_region_icache_addr );
|
nkeynes@931 | 169 | sh4_register_mem_region( 0xF1000000, 0xF2000000, &p4_region_icache_data );
|
nkeynes@931 | 170 | sh4_register_mem_region( 0xF2000000, 0xF3000000, &p4_region_itlb_addr );
|
nkeynes@931 | 171 | sh4_register_mem_region( 0xF3000000, 0xF4000000, &p4_region_itlb_data );
|
nkeynes@931 | 172 | sh4_register_mem_region( 0xF4000000, 0xF5000000, &p4_region_ocache_addr );
|
nkeynes@931 | 173 | sh4_register_mem_region( 0xF5000000, 0xF6000000, &p4_region_ocache_data );
|
nkeynes@931 | 174 | sh4_register_mem_region( 0xF6000000, 0xF7000000, &p4_region_utlb_addr );
|
nkeynes@931 | 175 | sh4_register_mem_region( 0xF7000000, 0xF8000000, &p4_region_utlb_data );
|
nkeynes@931 | 176 | sh4_register_mem_region( 0xF8000000, 0x00000000, &mem_region_unmapped );
|
nkeynes@931 | 177 |
|
nkeynes@931 | 178 | /* Setup P4 control region */
|
nkeynes@931 | 179 | sh4_register_mem_region( 0xFF000000, 0xFF001000, &mmio_region_MMU.fn );
|
nkeynes@931 | 180 | sh4_register_mem_region( 0xFF100000, 0xFF101000, &mmio_region_PMM.fn );
|
nkeynes@931 | 181 | sh4_register_mem_region( 0xFF200000, 0xFF201000, &mmio_region_UBC.fn );
|
nkeynes@931 | 182 | sh4_register_mem_region( 0xFF800000, 0xFF801000, &mmio_region_BSC.fn );
|
nkeynes@931 | 183 | sh4_register_mem_region( 0xFF900000, 0xFFA00000, &mem_region_unmapped ); // SDMR2 + SDMR3
|
nkeynes@931 | 184 | sh4_register_mem_region( 0xFFA00000, 0xFFA01000, &mmio_region_DMAC.fn );
|
nkeynes@931 | 185 | sh4_register_mem_region( 0xFFC00000, 0xFFC01000, &mmio_region_CPG.fn );
|
nkeynes@931 | 186 | sh4_register_mem_region( 0xFFC80000, 0xFFC81000, &mmio_region_RTC.fn );
|
nkeynes@931 | 187 | sh4_register_mem_region( 0xFFD00000, 0xFFD01000, &mmio_region_INTC.fn );
|
nkeynes@931 | 188 | sh4_register_mem_region( 0xFFD80000, 0xFFD81000, &mmio_region_TMU.fn );
|
nkeynes@931 | 189 | sh4_register_mem_region( 0xFFE00000, 0xFFE01000, &mmio_region_SCI.fn );
|
nkeynes@931 | 190 | sh4_register_mem_region( 0xFFE80000, 0xFFE81000, &mmio_region_SCIF.fn );
|
nkeynes@931 | 191 | sh4_register_mem_region( 0xFFF00000, 0xFFF01000, &mem_region_unmapped ); // H-UDI
|
nkeynes@931 | 192 |
|
nkeynes@931 | 193 | register_mem_page_remapped_hook( sh4_ext_page_remapped, NULL );
|
nkeynes@931 | 194 | }
|
nkeynes@931 | 195 |
|
nkeynes@931 | 196 | /************** Access methods ***************/
|
nkeynes@929 | 197 | #ifdef HAVE_FRAME_ADDRESS
|
nkeynes@929 | 198 | #define RETURN_VIA(exc) do{ *(((void **)__builtin_frame_address(0))+1) = exc; return; } while(0)
|
nkeynes@10 | 199 | #else
|
nkeynes@929 | 200 | #define RETURN_VIA(exc) return NULL
|
nkeynes@10 | 201 | #endif
|
nkeynes@10 | 202 |
|
nkeynes@931 | 203 |
|
nkeynes@931 | 204 | int32_t FASTCALL sh4_read_long( sh4addr_t addr )
|
nkeynes@10 | 205 | {
|
nkeynes@931 | 206 | return sh4_address_space[addr>>12]->read_long(addr);
|
nkeynes@10 | 207 | }
|
nkeynes@10 | 208 |
|
nkeynes@931 | 209 | int32_t FASTCALL sh4_read_word( sh4addr_t addr )
|
nkeynes@10 | 210 | {
|
nkeynes@931 | 211 | return sh4_address_space[addr>>12]->read_word(addr);
|
nkeynes@10 | 212 | }
|
nkeynes@10 | 213 |
|
nkeynes@931 | 214 | int32_t FASTCALL sh4_read_byte( sh4addr_t addr )
|
nkeynes@10 | 215 | {
|
nkeynes@931 | 216 | return sh4_address_space[addr>>12]->read_byte(addr);
|
nkeynes@931 | 217 | }
|
nkeynes@931 | 218 |
|
nkeynes@931 | 219 | void FASTCALL sh4_write_long( sh4addr_t addr, uint32_t val )
|
nkeynes@931 | 220 | {
|
nkeynes@931 | 221 | sh4_address_space[addr>>12]->write_long(addr, val);
|
nkeynes@931 | 222 | }
|
nkeynes@931 | 223 |
|
nkeynes@931 | 224 | void FASTCALL sh4_write_word( sh4addr_t addr, uint32_t val )
|
nkeynes@931 | 225 | {
|
nkeynes@931 | 226 | sh4_address_space[addr>>12]->write_word(addr,val);
|
nkeynes@931 | 227 | }
|
nkeynes@931 | 228 |
|
nkeynes@931 | 229 | void FASTCALL sh4_write_byte( sh4addr_t addr, uint32_t val )
|
nkeynes@931 | 230 | {
|
nkeynes@931 | 231 | sh4_address_space[addr>>12]->write_byte(addr, val);
|
nkeynes@10 | 232 | }
|
nkeynes@10 | 233 |
|
nkeynes@10 | 234 | /* FIXME: Handle all the many special cases when the range doesn't fall cleanly
|
nkeynes@400 | 235 | * into the same memory block
|
nkeynes@10 | 236 | */
|
nkeynes@912 | 237 | void mem_copy_from_sh4( sh4ptr_t dest, sh4addr_t srcaddr, size_t count ) {
|
nkeynes@103 | 238 | if( srcaddr >= 0x04000000 && srcaddr < 0x05000000 ) {
|
nkeynes@736 | 239 | pvr2_vram64_read( dest, srcaddr, count );
|
nkeynes@103 | 240 | } else {
|
nkeynes@736 | 241 | sh4ptr_t src = mem_get_region(srcaddr);
|
nkeynes@736 | 242 | if( src == NULL ) {
|
nkeynes@736 | 243 | WARN( "Attempted block read from unknown address %08X", srcaddr );
|
nkeynes@736 | 244 | } else {
|
nkeynes@736 | 245 | memcpy( dest, src, count );
|
nkeynes@736 | 246 | }
|
nkeynes@103 | 247 | }
|
nkeynes@10 | 248 | }
|
nkeynes@10 | 249 |
|
nkeynes@912 | 250 | void mem_copy_to_sh4( sh4addr_t destaddr, sh4ptr_t src, size_t count ) {
|
nkeynes@325 | 251 | if( destaddr >= 0x10000000 && destaddr < 0x14000000 ) {
|
nkeynes@736 | 252 | pvr2_dma_write( destaddr, src, count );
|
nkeynes@736 | 253 | return;
|
nkeynes@325 | 254 | } else if( (destaddr & 0x1F800000) == 0x05000000 ) {
|
nkeynes@736 | 255 | pvr2_render_buffer_invalidate( destaddr, TRUE );
|
nkeynes@325 | 256 | } else if( (destaddr & 0x1F800000) == 0x04000000 ) {
|
nkeynes@736 | 257 | pvr2_vram64_write( destaddr, src, count );
|
nkeynes@736 | 258 | return;
|
nkeynes@325 | 259 | }
|
nkeynes@502 | 260 | sh4ptr_t dest = mem_get_region(destaddr);
|
nkeynes@325 | 261 | if( dest == NULL )
|
nkeynes@736 | 262 | WARN( "Attempted block write to unknown address %08X", destaddr );
|
nkeynes@325 | 263 | else {
|
nkeynes@736 | 264 | xlat_invalidate_block( destaddr, count );
|
nkeynes@736 | 265 | memcpy( dest, src, count );
|
nkeynes@90 | 266 | }
|
nkeynes@10 | 267 | }
|