nkeynes@931 | 1 | /**
|
nkeynes@931 | 2 | * $Id$
|
nkeynes@939 | 3 | * Implements the on-chip operand cache, instruction cache, and store queue.
|
nkeynes@931 | 4 | *
|
nkeynes@931 | 5 | * Copyright (c) 2008 Nathan Keynes.
|
nkeynes@931 | 6 | *
|
nkeynes@931 | 7 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@931 | 8 | * it under the terms of the GNU General Public License as published by
|
nkeynes@931 | 9 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@931 | 10 | * (at your option) any later version.
|
nkeynes@931 | 11 | *
|
nkeynes@931 | 12 | * This program is distributed in the hope that it will be useful,
|
nkeynes@931 | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@931 | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@931 | 15 | * GNU General Public License for more details.
|
nkeynes@931 | 16 | */
|
nkeynes@931 | 17 |
|
nkeynes@931 | 18 | #define MODULE sh4_module
|
nkeynes@931 | 19 |
|
nkeynes@931 | 20 | #include <string.h>
|
nkeynes@931 | 21 | #include "dream.h"
|
nkeynes@931 | 22 | #include "mem.h"
|
nkeynes@931 | 23 | #include "mmio.h"
|
nkeynes@971 | 24 | #include "clock.h"
|
nkeynes@931 | 25 | #include "sh4/sh4core.h"
|
nkeynes@931 | 26 | #include "sh4/sh4mmio.h"
|
nkeynes@946 | 27 | #include "sh4/mmu.h"
|
nkeynes@931 | 28 |
|
nkeynes@931 | 29 | #define OCRAM_START (0x7C000000>>LXDREAM_PAGE_BITS)
|
nkeynes@931 | 30 | #define OCRAM_MID (0x7E000000>>LXDREAM_PAGE_BITS)
|
nkeynes@931 | 31 | #define OCRAM_END (0x80000000>>LXDREAM_PAGE_BITS)
|
nkeynes@931 | 32 |
|
nkeynes@931 | 33 | #define CACHE_VALID 1
|
nkeynes@931 | 34 | #define CACHE_DIRTY 2
|
nkeynes@931 | 35 |
|
nkeynes@931 | 36 | #define ICACHE_ENTRY_COUNT 256
|
nkeynes@931 | 37 | #define OCACHE_ENTRY_COUNT 512
|
nkeynes@931 | 38 |
|
nkeynes@931 | 39 | struct cache_line {
|
nkeynes@931 | 40 | uint32_t key; // Fast address match - bits 5..28 for valid entry, -1 for invalid entry
|
nkeynes@931 | 41 | uint32_t tag; // tag + flags value from the address field
|
nkeynes@931 | 42 | };
|
nkeynes@931 | 43 |
|
nkeynes@931 | 44 |
|
nkeynes@931 | 45 | static struct cache_line ccn_icache[ICACHE_ENTRY_COUNT];
|
nkeynes@971 | 46 | struct cache_line ccn_ocache[OCACHE_ENTRY_COUNT];
|
nkeynes@931 | 47 | static unsigned char ccn_icache_data[ICACHE_ENTRY_COUNT*32];
|
nkeynes@971 | 48 | unsigned char ccn_ocache_data[OCACHE_ENTRY_COUNT*32];
|
nkeynes@931 | 49 |
|
nkeynes@931 | 50 |
|
nkeynes@931 | 51 | /*********************** General module requirements ********************/
|
nkeynes@931 | 52 |
|
nkeynes@971 | 53 | void CCN_reset()
|
nkeynes@971 | 54 | {
|
nkeynes@971 | 55 | /* Clear everything for consistency */
|
nkeynes@971 | 56 | memset( ccn_icache, 0, sizeof(ccn_icache) );
|
nkeynes@971 | 57 | memset( ccn_ocache, 0, sizeof(ccn_icache) );
|
nkeynes@971 | 58 | memset( ccn_icache_data, 0, sizeof(ccn_icache) );
|
nkeynes@971 | 59 | memset( ccn_ocache_data, 0, sizeof(ccn_icache) );
|
nkeynes@971 | 60 | }
|
nkeynes@971 | 61 |
|
nkeynes@931 | 62 | void CCN_save_state( FILE *f )
|
nkeynes@931 | 63 | {
|
nkeynes@931 | 64 | fwrite( &ccn_icache, sizeof(ccn_icache), 1, f );
|
nkeynes@931 | 65 | fwrite( &ccn_icache_data, sizeof(ccn_icache_data), 1, f );
|
nkeynes@931 | 66 | fwrite( &ccn_ocache, sizeof(ccn_ocache), 1, f);
|
nkeynes@931 | 67 | fwrite( &ccn_ocache_data, sizeof(ccn_ocache_data), 1, f);
|
nkeynes@931 | 68 | }
|
nkeynes@931 | 69 |
|
nkeynes@931 | 70 | int CCN_load_state( FILE *f )
|
nkeynes@931 | 71 | {
|
nkeynes@931 | 72 | /* Setup the cache mode according to the saved register value
|
nkeynes@931 | 73 | * (mem_load runs before this point to load all MMIO data)
|
nkeynes@931 | 74 | */
|
nkeynes@931 | 75 | mmio_region_MMU_write( CCR, MMIO_READ(MMU, CCR) );
|
nkeynes@931 | 76 |
|
nkeynes@931 | 77 | if( fread( &ccn_icache, sizeof(ccn_icache), 1, f ) != 1 ) {
|
nkeynes@931 | 78 | return 1;
|
nkeynes@931 | 79 | }
|
nkeynes@931 | 80 | if( fread( &ccn_icache_data, sizeof(ccn_icache_data), 1, f ) != 1 ) {
|
nkeynes@931 | 81 | return 1;
|
nkeynes@931 | 82 | }
|
nkeynes@931 | 83 | if( fread( &ccn_ocache, sizeof(ccn_ocache), 1, f ) != 1 ) {
|
nkeynes@931 | 84 | return 1;
|
nkeynes@931 | 85 | }
|
nkeynes@931 | 86 | if( fread( &ccn_ocache_data, sizeof(ccn_ocache_data), 1, f ) != 1 ) {
|
nkeynes@931 | 87 | return 1;
|
nkeynes@931 | 88 | }
|
nkeynes@931 | 89 | return 0;
|
nkeynes@931 | 90 | }
|
nkeynes@931 | 91 |
|
nkeynes@931 | 92 | /************************* OCRAM memory address space ************************/
|
nkeynes@931 | 93 |
|
nkeynes@931 | 94 | #define OCRAMPAGE0 (&ccn_ocache_data[4096]) /* Lines 128-255 */
|
nkeynes@931 | 95 | #define OCRAMPAGE1 (&ccn_ocache_data[12288]) /* Lines 384-511 */
|
nkeynes@931 | 96 |
|
nkeynes@931 | 97 | static int32_t FASTCALL ocram_page0_read_long( sh4addr_t addr )
|
nkeynes@931 | 98 | {
|
nkeynes@931 | 99 | return *((int32_t *)(OCRAMPAGE0 + (addr&0x00000FFF)));
|
nkeynes@931 | 100 | }
|
nkeynes@931 | 101 | static int32_t FASTCALL ocram_page0_read_word( sh4addr_t addr )
|
nkeynes@931 | 102 | {
|
nkeynes@931 | 103 | return SIGNEXT16(*((int16_t *)(OCRAMPAGE0 + (addr&0x00000FFF))));
|
nkeynes@931 | 104 | }
|
nkeynes@931 | 105 | static int32_t FASTCALL ocram_page0_read_byte( sh4addr_t addr )
|
nkeynes@931 | 106 | {
|
nkeynes@931 | 107 | return SIGNEXT8(*((int16_t *)(OCRAMPAGE0 + (addr&0x00000FFF))));
|
nkeynes@931 | 108 | }
|
nkeynes@931 | 109 | static void FASTCALL ocram_page0_write_long( sh4addr_t addr, uint32_t val )
|
nkeynes@931 | 110 | {
|
nkeynes@931 | 111 | *(uint32_t *)(OCRAMPAGE0 + (addr&0x00000FFF)) = val;
|
nkeynes@931 | 112 | }
|
nkeynes@931 | 113 | static void FASTCALL ocram_page0_write_word( sh4addr_t addr, uint32_t val )
|
nkeynes@931 | 114 | {
|
nkeynes@931 | 115 | *(uint16_t *)(OCRAMPAGE0 + (addr&0x00000FFF)) = (uint16_t)val;
|
nkeynes@931 | 116 | }
|
nkeynes@931 | 117 | static void FASTCALL ocram_page0_write_byte( sh4addr_t addr, uint32_t val )
|
nkeynes@931 | 118 | {
|
nkeynes@931 | 119 | *(uint8_t *)(OCRAMPAGE0 + (addr&0x00000FFF)) = (uint8_t)val;
|
nkeynes@931 | 120 | }
|
nkeynes@931 | 121 | static void FASTCALL ocram_page0_read_burst( unsigned char *dest, sh4addr_t addr )
|
nkeynes@931 | 122 | {
|
nkeynes@931 | 123 | memcpy( dest, OCRAMPAGE0+(addr&0x00000FFF), 32 );
|
nkeynes@931 | 124 | }
|
nkeynes@931 | 125 | static void FASTCALL ocram_page0_write_burst( sh4addr_t addr, unsigned char *src )
|
nkeynes@931 | 126 | {
|
nkeynes@931 | 127 | memcpy( OCRAMPAGE0+(addr&0x00000FFF), src, 32 );
|
nkeynes@931 | 128 | }
|
nkeynes@931 | 129 |
|
nkeynes@931 | 130 | struct mem_region_fn mem_region_ocram_page0 = {
|
nkeynes@931 | 131 | ocram_page0_read_long, ocram_page0_write_long,
|
nkeynes@931 | 132 | ocram_page0_read_word, ocram_page0_write_word,
|
nkeynes@931 | 133 | ocram_page0_read_byte, ocram_page0_write_byte,
|
nkeynes@946 | 134 | ocram_page0_read_burst, ocram_page0_write_burst,
|
nkeynes@975 | 135 | unmapped_prefetch, ocram_page0_read_byte };
|
nkeynes@931 | 136 |
|
nkeynes@931 | 137 | static int32_t FASTCALL ocram_page1_read_long( sh4addr_t addr )
|
nkeynes@931 | 138 | {
|
nkeynes@931 | 139 | return *((int32_t *)(OCRAMPAGE1 + (addr&0x00000FFF)));
|
nkeynes@931 | 140 | }
|
nkeynes@931 | 141 | static int32_t FASTCALL ocram_page1_read_word( sh4addr_t addr )
|
nkeynes@931 | 142 | {
|
nkeynes@931 | 143 | return SIGNEXT16(*((int16_t *)(OCRAMPAGE1 + (addr&0x00000FFF))));
|
nkeynes@931 | 144 | }
|
nkeynes@931 | 145 | static int32_t FASTCALL ocram_page1_read_byte( sh4addr_t addr )
|
nkeynes@931 | 146 | {
|
nkeynes@931 | 147 | return SIGNEXT8(*((int16_t *)(OCRAMPAGE1 + (addr&0x00000FFF))));
|
nkeynes@931 | 148 | }
|
nkeynes@931 | 149 | static void FASTCALL ocram_page1_write_long( sh4addr_t addr, uint32_t val )
|
nkeynes@931 | 150 | {
|
nkeynes@931 | 151 | *(uint32_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = val;
|
nkeynes@931 | 152 | }
|
nkeynes@931 | 153 | static void FASTCALL ocram_page1_write_word( sh4addr_t addr, uint32_t val )
|
nkeynes@931 | 154 | {
|
nkeynes@931 | 155 | *(uint16_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = (uint16_t)val;
|
nkeynes@931 | 156 | }
|
nkeynes@931 | 157 | static void FASTCALL ocram_page1_write_byte( sh4addr_t addr, uint32_t val )
|
nkeynes@931 | 158 | {
|
nkeynes@931 | 159 | *(uint8_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = (uint8_t)val;
|
nkeynes@931 | 160 | }
|
nkeynes@931 | 161 | static void FASTCALL ocram_page1_read_burst( unsigned char *dest, sh4addr_t addr )
|
nkeynes@931 | 162 | {
|
nkeynes@931 | 163 | memcpy( dest, OCRAMPAGE1+(addr&0x00000FFF), 32 );
|
nkeynes@931 | 164 | }
|
nkeynes@931 | 165 | static void FASTCALL ocram_page1_write_burst( sh4addr_t addr, unsigned char *src )
|
nkeynes@931 | 166 | {
|
nkeynes@931 | 167 | memcpy( OCRAMPAGE1+(addr&0x00000FFF), src, 32 );
|
nkeynes@931 | 168 | }
|
nkeynes@931 | 169 |
|
nkeynes@931 | 170 | struct mem_region_fn mem_region_ocram_page1 = {
|
nkeynes@931 | 171 | ocram_page1_read_long, ocram_page1_write_long,
|
nkeynes@931 | 172 | ocram_page1_read_word, ocram_page1_write_word,
|
nkeynes@931 | 173 | ocram_page1_read_byte, ocram_page1_write_byte,
|
nkeynes@946 | 174 | ocram_page1_read_burst, ocram_page1_write_burst,
|
nkeynes@975 | 175 | unmapped_prefetch, ocram_page1_read_byte };
|
nkeynes@953 | 176 |
|
nkeynes@971 | 177 | /**************************** Cache functions ********************************/
|
nkeynes@971 | 178 | char ccn_cache_map[16 MB]; // 24 bits of address space
|
nkeynes@971 | 179 |
|
nkeynes@971 | 180 | /**
|
nkeynes@971 | 181 | * Load a 32-byte cache line from external memory at the given ext address.
|
nkeynes@971 | 182 | * @param addr external address pre-masked to 1FFFFFFE0
|
nkeynes@971 | 183 | */
|
nkeynes@971 | 184 | sh4addr_t FASTCALL ccn_ocache_load_line( sh4addr_t addr )
|
nkeynes@971 | 185 | {
|
nkeynes@971 | 186 | int entry = addr & 0x00003FE0;
|
nkeynes@971 | 187 | struct cache_line *line = &ccn_ocache[entry>>5];
|
nkeynes@980 | 188 | unsigned char *cache_data = &ccn_ocache_data[entry];
|
nkeynes@971 | 189 | sh4addr_t old_addr = line->tag;
|
nkeynes@971 | 190 | line->tag = addr & 0x1FFFFFE0;
|
nkeynes@971 | 191 | char oldstate = ccn_cache_map[old_addr>>5];
|
nkeynes@971 | 192 | ccn_cache_map[old_addr>>5] = 0;
|
nkeynes@971 | 193 | ccn_cache_map[addr>>5] = CACHE_VALID;
|
nkeynes@971 | 194 | if( oldstate == (CACHE_VALID|CACHE_DIRTY) ) {
|
nkeynes@971 | 195 | // Cache line is dirty - writeback.
|
nkeynes@971 | 196 | ext_address_space[old_addr>>12]->write_burst(old_addr, cache_data);
|
nkeynes@971 | 197 | }
|
nkeynes@971 | 198 | ext_address_space[addr>>12]->read_burst(cache_data, addr & 0x1FFFFFE0);
|
nkeynes@971 | 199 | return addr;
|
nkeynes@971 | 200 | }
|
nkeynes@971 | 201 |
|
nkeynes@971 | 202 | /* Long read through the operand cache */
|
nkeynes@971 | 203 | /*
|
nkeynes@971 | 204 | int32_t FASTCALL ccn_ocache_read_long( sh4addr_t addr );
|
nkeynes@971 | 205 | int32_t FASTCALL ccn_ocache_read_word( sh4addr_t addr );
|
nkeynes@971 | 206 | int32_t FASTCALL ccn_ocache_read_byte( sh4addr_t addr );
|
nkeynes@971 | 207 | void FASTCALL ccn_ocache_write_long_copyback( sh4addr_t addr, uint32_t val );
|
nkeynes@971 | 208 | void FASTCALL ccn_ocache_write_word_copyback( sh4addr_t addr, uint32_t val );
|
nkeynes@971 | 209 | void FASTCALL ccn_ocache_write_byte_copyback( sh4addr_t addr, uint32_t val );
|
nkeynes@971 | 210 |
|
nkeynes@971 | 211 | */
|
nkeynes@971 | 212 | static int32_t FASTCALL ccn_ocache_read_long( sh4addr_t addr )
|
nkeynes@971 | 213 | {
|
nkeynes@971 | 214 | addr &= 0x1FFFFFFF;
|
nkeynes@971 | 215 | if( (ccn_cache_map[addr>>5] & CACHE_VALID) == 0 ) {
|
nkeynes@971 | 216 | ccn_ocache_load_line(addr);
|
nkeynes@971 | 217 | }
|
nkeynes@971 | 218 | return *(int32_t *)&ccn_ocache_data[addr & 0x3FFF];
|
nkeynes@971 | 219 | }
|
nkeynes@971 | 220 |
|
nkeynes@971 | 221 | static int32_t FASTCALL ccn_ocache_read_word( sh4addr_t addr )
|
nkeynes@971 | 222 | {
|
nkeynes@971 | 223 | addr &= 0x1FFFFFFF;
|
nkeynes@971 | 224 | if( (ccn_cache_map[addr>>5] & CACHE_VALID) == 0 ) {
|
nkeynes@971 | 225 | ccn_ocache_load_line(addr);
|
nkeynes@971 | 226 | }
|
nkeynes@971 | 227 | return SIGNEXT16(*(int16_t *)&ccn_ocache_data[addr&0x3FFF]);
|
nkeynes@971 | 228 | }
|
nkeynes@971 | 229 |
|
nkeynes@971 | 230 | static int32_t FASTCALL ccn_ocache_read_byte( sh4addr_t addr )
|
nkeynes@971 | 231 | {
|
nkeynes@971 | 232 | addr &= 0x1FFFFFFF;
|
nkeynes@971 | 233 | if( (ccn_cache_map[addr>>5] & CACHE_VALID) == 0 ) {
|
nkeynes@971 | 234 | ccn_ocache_load_line(addr);
|
nkeynes@971 | 235 | }
|
nkeynes@971 | 236 | return SIGNEXT8(ccn_ocache_data[addr&0x3FFF]);
|
nkeynes@971 | 237 | }
|
nkeynes@971 | 238 |
|
nkeynes@971 | 239 | static void FASTCALL ccn_ocache_write_long_copyback( sh4addr_t addr, uint32_t value )
|
nkeynes@971 | 240 | {
|
nkeynes@971 | 241 | addr &= 0x1FFFFFFF;
|
nkeynes@971 | 242 | if( (ccn_cache_map[addr>>5] & CACHE_VALID) == 0 ) {
|
nkeynes@971 | 243 | ccn_ocache_load_line(addr);
|
nkeynes@971 | 244 | }
|
nkeynes@971 | 245 | ccn_cache_map[addr>>5] |= CACHE_DIRTY;
|
nkeynes@971 | 246 | *(uint32_t *)&ccn_ocache_data[addr&0x3FFF] = value;
|
nkeynes@971 | 247 | }
|
nkeynes@971 | 248 |
|
nkeynes@971 | 249 | static void FASTCALL ccn_ocache_write_word_copyback( sh4addr_t addr, uint32_t value )
|
nkeynes@971 | 250 | {
|
nkeynes@971 | 251 | addr &= 0x1FFFFFFF;
|
nkeynes@971 | 252 | if( (ccn_cache_map[addr>>5] & CACHE_VALID) == 0 ) {
|
nkeynes@971 | 253 | ccn_ocache_load_line(addr);
|
nkeynes@971 | 254 | }
|
nkeynes@971 | 255 | ccn_cache_map[addr>>5] |= CACHE_DIRTY;
|
nkeynes@971 | 256 | *(uint16_t *)&ccn_ocache_data[addr&0x3FFF] = (uint16_t)value;
|
nkeynes@971 | 257 | }
|
nkeynes@971 | 258 |
|
nkeynes@971 | 259 | static void FASTCALL ccn_ocache_write_byte_copyback( sh4addr_t addr, uint32_t value )
|
nkeynes@971 | 260 | {
|
nkeynes@971 | 261 | addr &= 0x1FFFFFFF;
|
nkeynes@971 | 262 | if( (ccn_cache_map[addr>>5] & CACHE_VALID) == 0 ) {
|
nkeynes@971 | 263 | ccn_ocache_load_line(addr);
|
nkeynes@971 | 264 | }
|
nkeynes@971 | 265 | ccn_cache_map[addr>>5] |= CACHE_DIRTY;
|
nkeynes@971 | 266 | ccn_ocache_data[addr&0x3FFF] = (uint8_t)value;
|
nkeynes@971 | 267 | }
|
nkeynes@971 | 268 |
|
nkeynes@971 | 269 | static void FASTCALL ccn_ocache_prefetch( sh4addr_t addr )
|
nkeynes@971 | 270 | {
|
nkeynes@971 | 271 | addr &= 0x1FFFFFFF;
|
nkeynes@971 | 272 | if( (ccn_cache_map[addr>>5] & CACHE_VALID) == 0 ) {
|
nkeynes@971 | 273 | ccn_ocache_load_line(addr);
|
nkeynes@971 | 274 | }
|
nkeynes@971 | 275 | }
|
nkeynes@971 | 276 |
|
nkeynes@971 | 277 | void FASTCALL ccn_ocache_invalidate( sh4addr_t addr )
|
nkeynes@971 | 278 | {
|
nkeynes@971 | 279 | addr &= 0x1FFFFFFF;
|
nkeynes@971 | 280 | ccn_cache_map[addr>>5] &= ~CACHE_VALID;
|
nkeynes@971 | 281 | }
|
nkeynes@971 | 282 |
|
nkeynes@971 | 283 | void FASTCALL ccn_ocache_purge( sh4addr_t addr )
|
nkeynes@971 | 284 | {
|
nkeynes@971 | 285 | addr &= 0x1FFFFFE0;
|
nkeynes@971 | 286 | int oldflags = ccn_cache_map[addr>>5];
|
nkeynes@971 | 287 | ccn_cache_map[addr>>5] &= ~CACHE_VALID;
|
nkeynes@971 | 288 | if( oldflags == (CACHE_VALID|CACHE_DIRTY) ) {
|
nkeynes@980 | 289 | unsigned char *cache_data = &ccn_ocache_data[addr & 0x3FE0];
|
nkeynes@971 | 290 | ext_address_space[addr>>12]->write_burst(addr, cache_data);
|
nkeynes@971 | 291 | }
|
nkeynes@971 | 292 | }
|
nkeynes@971 | 293 |
|
nkeynes@971 | 294 | void FASTCALL ccn_ocache_writeback( sh4addr_t addr )
|
nkeynes@971 | 295 | {
|
nkeynes@971 | 296 | addr &= 0x1FFFFFE0;
|
nkeynes@971 | 297 | if( ccn_cache_map[addr>>5] == (CACHE_VALID|CACHE_DIRTY) ) {
|
nkeynes@971 | 298 | ccn_cache_map[addr>>5] &= ~CACHE_DIRTY;
|
nkeynes@980 | 299 | unsigned char *cache_data = &ccn_ocache_data[addr & 0x3FE0];
|
nkeynes@971 | 300 | ext_address_space[addr>>12]->write_burst(addr, cache_data);
|
nkeynes@971 | 301 | }
|
nkeynes@971 | 302 | }
|
nkeynes@971 | 303 |
|
nkeynes@971 | 304 | struct mem_region_fn ccn_ocache_cb_region = {
|
nkeynes@971 | 305 | ccn_ocache_read_long, ccn_ocache_write_long_copyback,
|
nkeynes@971 | 306 | ccn_ocache_read_word, ccn_ocache_write_word_copyback,
|
nkeynes@971 | 307 | ccn_ocache_read_byte, ccn_ocache_write_byte_copyback,
|
nkeynes@971 | 308 | unmapped_read_burst, unmapped_write_burst,
|
nkeynes@975 | 309 | ccn_ocache_prefetch, ccn_ocache_read_byte };
|
nkeynes@971 | 310 |
|
nkeynes@931 | 311 |
|
nkeynes@933 | 312 | /************************** Cache direct access ******************************/
|
nkeynes@933 | 313 |
|
nkeynes@968 | 314 | static int32_t FASTCALL ccn_icache_addr_read( sh4addr_t addr )
|
nkeynes@933 | 315 | {
|
nkeynes@933 | 316 | int entry = (addr & 0x00001FE0);
|
nkeynes@933 | 317 | return ccn_icache[entry>>5].tag;
|
nkeynes@933 | 318 | }
|
nkeynes@933 | 319 |
|
nkeynes@968 | 320 | static void FASTCALL ccn_icache_addr_write( sh4addr_t addr, uint32_t val )
|
nkeynes@933 | 321 | {
|
nkeynes@933 | 322 | int entry = (addr & 0x00003FE0);
|
nkeynes@933 | 323 | struct cache_line *line = &ccn_ocache[entry>>5];
|
nkeynes@933 | 324 | if( addr & 0x08 ) { // Associative
|
nkeynes@933 | 325 | /* FIXME: implement this - requires ITLB lookups, with exception in case of multi-hit */
|
nkeynes@933 | 326 | } else {
|
nkeynes@933 | 327 | line->tag = val & 0x1FFFFC01;
|
nkeynes@933 | 328 | line->key = (val & 0x1FFFFC00)|(entry & 0x000003E0);
|
nkeynes@933 | 329 | }
|
nkeynes@933 | 330 | }
|
nkeynes@933 | 331 |
|
nkeynes@933 | 332 | struct mem_region_fn p4_region_icache_addr = {
|
nkeynes@933 | 333 | ccn_icache_addr_read, ccn_icache_addr_write,
|
nkeynes@933 | 334 | unmapped_read_long, unmapped_write_long,
|
nkeynes@933 | 335 | unmapped_read_long, unmapped_write_long,
|
nkeynes@946 | 336 | unmapped_read_burst, unmapped_write_burst,
|
nkeynes@975 | 337 | unmapped_prefetch, unmapped_read_long };
|
nkeynes@933 | 338 |
|
nkeynes@933 | 339 |
|
nkeynes@968 | 340 | static int32_t FASTCALL ccn_icache_data_read( sh4addr_t addr )
|
nkeynes@933 | 341 | {
|
nkeynes@933 | 342 | int entry = (addr & 0x00001FFC);
|
nkeynes@933 | 343 | return *(uint32_t *)&ccn_icache_data[entry];
|
nkeynes@933 | 344 | }
|
nkeynes@933 | 345 |
|
nkeynes@968 | 346 | static void FASTCALL ccn_icache_data_write( sh4addr_t addr, uint32_t val )
|
nkeynes@933 | 347 | {
|
nkeynes@933 | 348 | int entry = (addr & 0x00001FFC);
|
nkeynes@933 | 349 | *(uint32_t *)&ccn_icache_data[entry] = val;
|
nkeynes@933 | 350 | }
|
nkeynes@933 | 351 |
|
nkeynes@933 | 352 | struct mem_region_fn p4_region_icache_data = {
|
nkeynes@933 | 353 | ccn_icache_data_read, ccn_icache_data_write,
|
nkeynes@933 | 354 | unmapped_read_long, unmapped_write_long,
|
nkeynes@933 | 355 | unmapped_read_long, unmapped_write_long,
|
nkeynes@946 | 356 | unmapped_read_burst, unmapped_write_burst,
|
nkeynes@975 | 357 | unmapped_prefetch, unmapped_read_long };
|
nkeynes@933 | 358 |
|
nkeynes@968 | 359 | static int32_t FASTCALL ccn_ocache_addr_read( sh4addr_t addr )
|
nkeynes@933 | 360 | {
|
nkeynes@933 | 361 | int entry = (addr & 0x00003FE0);
|
nkeynes@971 | 362 | sh4addr_t tag = ccn_ocache[entry>>5].tag;
|
nkeynes@971 | 363 | return (tag&0x1FFFFC00) | ccn_cache_map[tag>>5];
|
nkeynes@933 | 364 | }
|
nkeynes@933 | 365 |
|
nkeynes@968 | 366 | static void FASTCALL ccn_ocache_addr_write( sh4addr_t addr, uint32_t val )
|
nkeynes@933 | 367 | {
|
nkeynes@933 | 368 | int entry = (addr & 0x00003FE0);
|
nkeynes@933 | 369 | struct cache_line *line = &ccn_ocache[entry>>5];
|
nkeynes@933 | 370 | if( addr & 0x08 ) { // Associative
|
nkeynes@933 | 371 | } else {
|
nkeynes@971 | 372 | sh4addr_t tag = line->tag;
|
nkeynes@971 | 373 | if( ccn_cache_map[tag>>5] == (CACHE_VALID|CACHE_DIRTY) ) {
|
nkeynes@933 | 374 | // Cache line is dirty - writeback.
|
nkeynes@971 | 375 | unsigned char *cache_data = &ccn_ocache_data[entry];
|
nkeynes@971 | 376 | ext_address_space[tag>>12]->write_burst(tag, cache_data);
|
nkeynes@933 | 377 | }
|
nkeynes@971 | 378 | line->tag = tag = (val & 0x1FFFFC00) | (addr & 0x3E0);
|
nkeynes@971 | 379 | ccn_cache_map[tag>>5] = val & 0x03;
|
nkeynes@933 | 380 | }
|
nkeynes@933 | 381 | }
|
nkeynes@933 | 382 |
|
nkeynes@933 | 383 | struct mem_region_fn p4_region_ocache_addr = {
|
nkeynes@933 | 384 | ccn_ocache_addr_read, ccn_ocache_addr_write,
|
nkeynes@933 | 385 | unmapped_read_long, unmapped_write_long,
|
nkeynes@933 | 386 | unmapped_read_long, unmapped_write_long,
|
nkeynes@946 | 387 | unmapped_read_burst, unmapped_write_burst,
|
nkeynes@975 | 388 | unmapped_prefetch, unmapped_read_long };
|
nkeynes@933 | 389 |
|
nkeynes@933 | 390 |
|
nkeynes@968 | 391 | static int32_t FASTCALL ccn_ocache_data_read( sh4addr_t addr )
|
nkeynes@933 | 392 | {
|
nkeynes@933 | 393 | int entry = (addr & 0x00003FFC);
|
nkeynes@933 | 394 | return *(uint32_t *)&ccn_ocache_data[entry];
|
nkeynes@933 | 395 | }
|
nkeynes@933 | 396 |
|
nkeynes@968 | 397 | static void FASTCALL ccn_ocache_data_write( sh4addr_t addr, uint32_t val )
|
nkeynes@933 | 398 | {
|
nkeynes@933 | 399 | int entry = (addr & 0x00003FFC);
|
nkeynes@933 | 400 | *(uint32_t *)&ccn_ocache_data[entry] = val;
|
nkeynes@933 | 401 | }
|
nkeynes@933 | 402 |
|
nkeynes@933 | 403 | struct mem_region_fn p4_region_ocache_data = {
|
nkeynes@933 | 404 | ccn_ocache_data_read, ccn_ocache_data_write,
|
nkeynes@933 | 405 | unmapped_read_long, unmapped_write_long,
|
nkeynes@933 | 406 | unmapped_read_long, unmapped_write_long,
|
nkeynes@946 | 407 | unmapped_read_burst, unmapped_write_burst,
|
nkeynes@975 | 408 | unmapped_prefetch, unmapped_read_long };
|
nkeynes@933 | 409 |
|
nkeynes@933 | 410 |
|
nkeynes@931 | 411 | /****************** Cache control *********************/
|
nkeynes@931 | 412 |
|
nkeynes@931 | 413 | void CCN_set_cache_control( int reg )
|
nkeynes@931 | 414 | {
|
nkeynes@931 | 415 | uint32_t i;
|
nkeynes@933 | 416 |
|
nkeynes@933 | 417 | if( reg & CCR_ICI ) { /* icache invalidate */
|
nkeynes@933 | 418 | for( i=0; i<ICACHE_ENTRY_COUNT; i++ ) {
|
nkeynes@971 | 419 | ccn_icache[i].key = -1;
|
nkeynes@933 | 420 | ccn_icache[i].tag &= ~CACHE_VALID;
|
nkeynes@933 | 421 | }
|
nkeynes@933 | 422 | }
|
nkeynes@933 | 423 |
|
nkeynes@933 | 424 | if( reg & CCR_OCI ) { /* ocache invalidate */
|
nkeynes@933 | 425 | for( i=0; i<OCACHE_ENTRY_COUNT; i++ ) {
|
nkeynes@983 | 426 | ccn_ocache[i].key = -1;
|
nkeynes@933 | 427 | ccn_ocache[i].tag &= ~(CACHE_VALID|CACHE_DIRTY);
|
nkeynes@933 | 428 | }
|
nkeynes@933 | 429 | }
|
nkeynes@933 | 430 |
|
nkeynes@931 | 431 | switch( reg & (CCR_OIX|CCR_ORA|CCR_OCE) ) {
|
nkeynes@931 | 432 | case MEM_OC_INDEX0: /* OIX=0 */
|
nkeynes@931 | 433 | for( i=OCRAM_START; i<OCRAM_END; i+=4 ) {
|
nkeynes@931 | 434 | sh4_address_space[i] = &mem_region_ocram_page0;
|
nkeynes@931 | 435 | sh4_address_space[i+1] = &mem_region_ocram_page0;
|
nkeynes@931 | 436 | sh4_address_space[i+2] = &mem_region_ocram_page1;
|
nkeynes@931 | 437 | sh4_address_space[i+3] = &mem_region_ocram_page1;
|
nkeynes@931 | 438 | }
|
nkeynes@931 | 439 | break;
|
nkeynes@931 | 440 | case MEM_OC_INDEX1: /* OIX=1 */
|
nkeynes@931 | 441 | for( i=OCRAM_START; i<OCRAM_MID; i++ )
|
nkeynes@931 | 442 | sh4_address_space[i] = &mem_region_ocram_page0;
|
nkeynes@931 | 443 | for( i=OCRAM_MID; i<OCRAM_END; i++ )
|
nkeynes@931 | 444 | sh4_address_space[i] = &mem_region_ocram_page1;
|
nkeynes@931 | 445 | break;
|
nkeynes@931 | 446 | default: /* disabled */
|
nkeynes@931 | 447 | for( i=OCRAM_START; i<OCRAM_END; i++ )
|
nkeynes@931 | 448 | sh4_address_space[i] = &mem_region_unmapped;
|
nkeynes@931 | 449 | break;
|
nkeynes@931 | 450 | }
|
nkeynes@939 | 451 | }
|
nkeynes@939 | 452 |
|
nkeynes@946 | 453 | /**
|
nkeynes@946 | 454 | * Prefetch for non-storequeue regions
|
nkeynes@946 | 455 | */
|
nkeynes@946 | 456 | void FASTCALL ccn_prefetch( sh4addr_t addr )
|
nkeynes@946 | 457 | {
|
nkeynes@946 | 458 |
|
nkeynes@946 | 459 | }
|
nkeynes@939 | 460 |
|
nkeynes@971 | 461 | /************************** Uncached memory access ***************************/
|
nkeynes@971 | 462 | int32_t FASTCALL ccn_uncached_read_long( sh4addr_t addr )
|
nkeynes@971 | 463 | {
|
nkeynes@971 | 464 | sh4r.slice_cycle += (4*sh4_bus_period);
|
nkeynes@971 | 465 | addr &= 0x1FFFFFFF;
|
nkeynes@971 | 466 | return ext_address_space[addr>>12]->read_long(addr);
|
nkeynes@971 | 467 | }
|
nkeynes@971 | 468 | int32_t FASTCALL ccn_uncached_read_word( sh4addr_t addr )
|
nkeynes@971 | 469 | {
|
nkeynes@971 | 470 | sh4r.slice_cycle += (4*sh4_bus_period);
|
nkeynes@971 | 471 | addr &= 0x1FFFFFFF;
|
nkeynes@971 | 472 | return ext_address_space[addr>>12]->read_word(addr);
|
nkeynes@971 | 473 | }
|
nkeynes@971 | 474 | int32_t FASTCALL ccn_uncached_read_byte( sh4addr_t addr )
|
nkeynes@971 | 475 | {
|
nkeynes@971 | 476 | sh4r.slice_cycle += (4*sh4_bus_period);
|
nkeynes@971 | 477 | addr &= 0x1FFFFFFF;
|
nkeynes@971 | 478 | return ext_address_space[addr>>12]->read_byte(addr);
|
nkeynes@971 | 479 | }
|
nkeynes@971 | 480 | void FASTCALL ccn_uncached_write_long( sh4addr_t addr, uint32_t val )
|
nkeynes@971 | 481 | {
|
nkeynes@971 | 482 | sh4r.slice_cycle += (4*sh4_bus_period);
|
nkeynes@971 | 483 | addr &= 0x1FFFFFFF;
|
nkeynes@971 | 484 | return ext_address_space[addr>>12]->write_long(addr, val);
|
nkeynes@971 | 485 | }
|
nkeynes@971 | 486 | void FASTCALL ccn_uncached_write_word( sh4addr_t addr, uint32_t val )
|
nkeynes@971 | 487 | {
|
nkeynes@971 | 488 | sh4r.slice_cycle += (4*sh4_bus_period);
|
nkeynes@971 | 489 | addr &= 0x1FFFFFFF;
|
nkeynes@971 | 490 | return ext_address_space[addr>>12]->write_word(addr, val);
|
nkeynes@971 | 491 | }
|
nkeynes@971 | 492 | void FASTCALL ccn_uncached_write_byte( sh4addr_t addr, uint32_t val )
|
nkeynes@971 | 493 | {
|
nkeynes@971 | 494 | sh4r.slice_cycle += (4*sh4_bus_period);
|
nkeynes@971 | 495 | addr &= 0x1FFFFFFF;
|
nkeynes@971 | 496 | return ext_address_space[addr>>12]->write_byte(addr, val);
|
nkeynes@971 | 497 | }
|
nkeynes@946 | 498 | void FASTCALL ccn_uncached_prefetch( sh4addr_t addr )
|
nkeynes@946 | 499 | {
|
nkeynes@946 | 500 | }
|
nkeynes@971 | 501 |
|
nkeynes@971 | 502 | struct mem_region_fn ccn_uncached_region = {
|
nkeynes@971 | 503 | ccn_uncached_read_long, ccn_uncached_write_long,
|
nkeynes@971 | 504 | ccn_uncached_read_word, ccn_uncached_write_word,
|
nkeynes@971 | 505 | ccn_uncached_read_byte, ccn_uncached_write_byte,
|
nkeynes@971 | 506 | unmapped_read_burst, unmapped_write_burst,
|
nkeynes@975 | 507 | ccn_uncached_prefetch, ccn_uncached_read_byte };
|
nkeynes@971 | 508 |
|
nkeynes@971 | 509 |
|
nkeynes@946 | 510 | /********************************* Store-queue *******************************/
|
nkeynes@946 | 511 | /*
|
nkeynes@946 | 512 | * The storequeue is strictly speaking part of the cache, but most of
|
nkeynes@946 | 513 | * the complexity is actually around its addressing (ie in the MMU). The
|
nkeynes@946 | 514 | * methods here can assume we've already passed SQMD protection and the TLB
|
nkeynes@946 | 515 | * lookups (where appropriate).
|
nkeynes@946 | 516 | */
|
nkeynes@946 | 517 | void FASTCALL ccn_storequeue_write_long( sh4addr_t addr, uint32_t val )
|
nkeynes@939 | 518 | {
|
nkeynes@939 | 519 | sh4r.store_queue[(addr>>2)&0xF] = val;
|
nkeynes@939 | 520 | }
|
nkeynes@946 | 521 | int32_t FASTCALL ccn_storequeue_read_long( sh4addr_t addr )
|
nkeynes@939 | 522 | {
|
nkeynes@939 | 523 | return sh4r.store_queue[(addr>>2)&0xF];
|
nkeynes@939 | 524 | }
|
nkeynes@939 | 525 |
|
nkeynes@946 | 526 | /**
|
nkeynes@946 | 527 | * Variant used when tlb is disabled - address will be the original prefetch
|
nkeynes@946 | 528 | * address (ie 0xE0001234). Due to the way the SQ addressing is done, it can't
|
nkeynes@946 | 529 | * be hardcoded on 4K page boundaries, so we manually decode it here.
|
nkeynes@946 | 530 | */
|
nkeynes@946 | 531 | void FASTCALL ccn_storequeue_prefetch( sh4addr_t addr )
|
nkeynes@946 | 532 | {
|
nkeynes@946 | 533 | int queue = (addr&0x20)>>2;
|
nkeynes@946 | 534 | sh4ptr_t src = (sh4ptr_t)&sh4r.store_queue[queue];
|
nkeynes@946 | 535 | uint32_t hi = MMIO_READ( MMU, QACR0 + (queue>>1)) << 24;
|
nkeynes@946 | 536 | sh4addr_t target = (addr&0x03FFFFE0) | hi;
|
nkeynes@946 | 537 | ext_address_space[target>>12]->write_burst( target, src );
|
nkeynes@946 | 538 | }
|
nkeynes@946 | 539 |
|
nkeynes@946 | 540 | /**
|
nkeynes@946 | 541 | * Variant used when tlb is enabled - address in this case is already
|
nkeynes@946 | 542 | * mapped to the external target address.
|
nkeynes@946 | 543 | */
|
nkeynes@946 | 544 | void FASTCALL ccn_storequeue_prefetch_tlb( sh4addr_t addr )
|
nkeynes@946 | 545 | {
|
nkeynes@946 | 546 | int queue = (addr&0x20)>>2;
|
nkeynes@946 | 547 | sh4ptr_t src = (sh4ptr_t)&sh4r.store_queue[queue];
|
nkeynes@946 | 548 | ext_address_space[addr>>12]->write_burst( (addr & 0x1FFFFFE0), src );
|
nkeynes@946 | 549 | }
|
nkeynes@971 | 550 |
|