filename | src/sh4/sh4mem.c |
changeset | 569:a1c49e1e8776 |
prev | 562:e598411b640b |
next | 570:d2893980fbf5 |
author | nkeynes |
date | Fri Jan 04 11:54:17 2008 +0000 (15 years ago) |
branch | lxdream-mmu |
permissions | -rw-r--r-- |
last change | Bring icache partially into line with the mmu, a little less slow with AT off now. |
view | annotate | diff | log | raw |
1 /**
2 * $Id$
3 * sh4mem.c is responsible for the SH4's access to memory (including memory
4 * mapped I/O), using the page maps created in mem.c
5 *
6 * Copyright (c) 2005 Nathan Keynes.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
19 #define MODULE sh4_module
21 #include <string.h>
22 #include <zlib.h>
23 #include "dream.h"
24 #include "mem.h"
25 #include "mmio.h"
26 #include "dreamcast.h"
27 #include "sh4/sh4core.h"
28 #include "sh4/sh4mmio.h"
29 #include "sh4/xltcache.h"
30 #include "pvr2/pvr2.h"
31 #include "asic.h"
33 #define OC_BASE 0x1C000000
34 #define OC_TOP 0x20000000
36 #define TRANSLATE_VIDEO_64BIT_ADDRESS(a) ( (((a)&0x00FFFFF8)>>1)|(((a)&0x00000004)<<20)|((a)&0x03)|0x05000000 )
38 #ifdef ENABLE_WATCH
39 #define CHECK_READ_WATCH( addr, size ) \
40 if( mem_is_watched(addr,size,WATCH_READ) != NULL ) { \
41 WARN( "Watch triggered at %08X by %d byte read", addr, size ); \
42 dreamcast_stop(); \
43 }
44 #define CHECK_WRITE_WATCH( addr, size, val ) \
45 if( mem_is_watched(addr,size,WATCH_WRITE) != NULL ) { \
46 WARN( "Watch triggered at %08X by %d byte write <= %0*X", addr, size, size*2, val ); \
47 dreamcast_stop(); \
48 }
49 #else
50 #define CHECK_READ_WATCH( addr, size )
51 #define CHECK_WRITE_WATCH( addr, size, val )
52 #endif
54 #ifdef ENABLE_TRACE_IO
55 #define TRACE_IO( str, p, r, ... ) if(io_rgn[(uint32_t)p]->trace_flag && !MMIO_NOTRACE_BYNUM((uint32_t)p,r)) \
56 TRACE( str " [%s.%s: %s]", __VA_ARGS__, \
57 MMIO_NAME_BYNUM((uint32_t)p), MMIO_REGID_BYNUM((uint32_t)p, r), \
58 MMIO_REGDESC_BYNUM((uint32_t)p, r) )
59 #define TRACE_P4IO( str, io, r, ... ) if(io->trace_flag && !MMIO_NOTRACE_IOBYNUM(io,r)) \
60 TRACE( str " [%s.%s: %s]", __VA_ARGS__, \
61 io->id, MMIO_REGID_IOBYNUM(io, r), \
62 MMIO_REGDESC_IOBYNUM(io, r) )
63 #else
64 #define TRACE_IO( str, p, r, ... )
65 #define TRACE_P4IO( str, io, r, ... )
66 #endif
68 extern struct mem_region mem_rgn[];
69 extern struct mmio_region *P4_io[];
71 int32_t sh4_read_p4( sh4addr_t addr )
72 {
73 struct mmio_region *io = P4_io[(addr&0x1FFFFFFF)>>19];
74 if( !io ) {
75 switch( addr & 0x1F000000 ) {
76 case 0x00000000: case 0x01000000: case 0x02000000: case 0x03000000:
77 /* Store queue - readable? */
78 return 0;
79 break;
80 case 0x10000000: return mmu_icache_addr_read( addr );
81 case 0x11000000: return mmu_icache_data_read( addr );
82 case 0x12000000: return mmu_itlb_addr_read( addr );
83 case 0x13000000: return mmu_itlb_data_read( addr );
84 case 0x14000000: return mmu_ocache_addr_read( addr );
85 case 0x15000000: return mmu_ocache_data_read( addr );
86 case 0x16000000: return mmu_utlb_addr_read( addr );
87 case 0x17000000: return mmu_utlb_data_read( addr );
88 default:
89 WARN( "Attempted read from unknown or invalid P4 region: %08X", addr );
90 return 0;
91 }
92 } else {
93 int32_t val = io->io_read( addr&0xFFF );
94 TRACE_P4IO( "Long read %08X <= %08X", io, (addr&0xFFF), val, addr );
95 return val;
96 }
97 }
99 void sh4_write_p4( sh4addr_t addr, int32_t val )
100 {
101 struct mmio_region *io = P4_io[(addr&0x1FFFFFFF)>>19];
102 if( !io ) {
103 switch( addr & 0x1F000000 ) {
104 case 0x00000000: case 0x01000000: case 0x02000000: case 0x03000000:
105 /* Store queue */
106 SH4_WRITE_STORE_QUEUE( addr, val );
107 break;
108 case 0x10000000: mmu_icache_addr_write( addr, val ); break;
109 case 0x11000000: mmu_icache_data_write( addr, val ); break;
110 case 0x12000000: mmu_itlb_addr_write( addr, val ); break;
111 case 0x13000000: mmu_itlb_data_write( addr, val ); break;
112 case 0x14000000: mmu_ocache_addr_write( addr, val ); break;
113 case 0x15000000: mmu_ocache_data_write( addr, val ); break;
114 case 0x16000000: mmu_utlb_addr_write( addr, val ); break;
115 case 0x17000000: mmu_utlb_data_write( addr, val ); break;
116 default:
117 WARN( "Attempted write to unknown P4 region: %08X", addr );
118 }
119 } else {
120 TRACE_P4IO( "Long write %08X => %08X", io, (addr&0xFFF), val, addr );
121 io->io_write( addr&0xFFF, val );
122 }
123 }
125 int32_t sh4_read_phys_word( sh4addr_t addr )
126 {
127 sh4ptr_t page;
128 if( addr >= 0xE0000000 ) /* P4 Area, handled specially */
129 return SIGNEXT16(sh4_read_p4( addr ));
131 if( (addr&0x1F800000) == 0x04000000 ) {
132 addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
133 }
135 page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
136 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
137 if( page == NULL ) {
138 WARN( "Attempted word read to missing page: %08X",
139 addr );
140 return 0;
141 }
142 return SIGNEXT16(io_rgn[(uintptr_t)page]->io_read(addr&0xFFF));
143 } else {
144 return SIGNEXT16(*(int16_t *)(page+(addr&0xFFF)));
145 }
146 }
148 /**
149 * Convenience function to read a quad-word (implemented as two long reads).
150 */
151 int64_t sh4_read_quad( sh4addr_t addr )
152 {
153 return ((int64_t)((uint32_t)sh4_read_long(addr))) |
154 (((int64_t)((uint32_t)sh4_read_long(addr+4))) << 32);
155 }
157 int64_t sh4_read_long( sh4addr_t vma )
158 {
159 sh4ptr_t page;
161 CHECK_READ_WATCH(addr,4);
163 uint64_t ppa = mmu_vma_to_phys_read(vma);
164 if( ppa>>32 ) {
165 return ppa;
166 }
167 sh4addr_t addr = (sh4addr_t)ppa;
169 if( addr >= 0xE0000000 ) { /* P4 Area, handled specially */
170 return ZEROEXT32(sh4_read_p4( addr ));
171 } else if( (addr&0x1C000000) == 0x0C000000 ) {
172 return ZEROEXT32(*(int32_t *)(sh4_main_ram + (addr&0x00FFFFFF)));
173 } else if( (addr&0x1F800000) == 0x04000000 ) {
174 addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
175 pvr2_render_buffer_invalidate(addr, FALSE);
176 } else if( (addr&0x1F800000) == 0x05000000 ) {
177 pvr2_render_buffer_invalidate(addr, FALSE);
178 }
180 page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
181 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
182 int32_t val;
183 if( page == NULL ) {
184 WARN( "Attempted long read to missing page: %08X", addr );
185 return 0;
186 }
187 val = io_rgn[(uintptr_t)page]->io_read(addr&0xFFF);
188 TRACE_IO( "Long read %08X <= %08X", page, (addr&0xFFF), val, addr );
189 return ZEROEXT32(val);
190 } else {
191 return ZEROEXT32(*(int32_t *)(page+(addr&0xFFF)));
192 }
193 }
195 int64_t sh4_read_word( sh4addr_t vma )
196 {
197 sh4ptr_t page;
199 CHECK_READ_WATCH(addr,2);
201 uint64_t ppa = mmu_vma_to_phys_read(vma);
202 if( ppa>>32 ) {
203 return ppa;
204 }
205 sh4addr_t addr = (sh4addr_t)ppa;
207 if( addr >= 0xE0000000 ) { /* P4 Area, handled specially */
208 return ZEROEXT32(SIGNEXT16(sh4_read_p4( addr )));
209 } else if( (addr&0x1C000000) == 0x0C000000 ) {
210 return ZEROEXT32(SIGNEXT16(*(int16_t *)(sh4_main_ram + (addr&0x00FFFFFF))));
211 } else if( (addr&0x1F800000) == 0x04000000 ) {
212 addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
213 pvr2_render_buffer_invalidate(addr, FALSE);
214 } else if( (addr&0x1F800000) == 0x05000000 ) {
215 pvr2_render_buffer_invalidate(addr, FALSE);
216 }
218 page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
219 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
220 int32_t val;
221 if( page == NULL ) {
222 WARN( "Attempted word read to missing page: %08X", addr );
223 return 0;
224 }
225 val = SIGNEXT16(io_rgn[(uintptr_t)page]->io_read(addr&0xFFF));
226 TRACE_IO( "Word read %04X <= %08X", page, (addr&0xFFF), val&0xFFFF, addr );
227 return ZEROEXT32(val);
228 } else {
229 return ZEROEXT32(SIGNEXT16(*(int16_t *)(page+(addr&0xFFF))));
230 }
231 }
233 int64_t sh4_read_byte( sh4addr_t vma )
234 {
235 sh4ptr_t page;
237 CHECK_READ_WATCH(addr,1);
239 uint64_t ppa = mmu_vma_to_phys_read(vma);
240 if( ppa>>32 ) {
241 return ppa;
242 }
243 sh4addr_t addr = (sh4addr_t)ppa;
245 if( addr >= 0xE0000000 ) { /* P4 Area, handled specially */
246 return ZEROEXT32(SIGNEXT8(sh4_read_p4( addr )));
247 } else if( (addr&0x1C000000) == 0x0C000000 ) {
248 return ZEROEXT32(SIGNEXT8(*(int8_t *)(sh4_main_ram + (addr&0x00FFFFFF))));
249 } else if( (addr&0x1F800000) == 0x04000000 ) {
250 addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
251 pvr2_render_buffer_invalidate(addr, FALSE);
252 } else if( (addr&0x1F800000) == 0x05000000 ) {
253 pvr2_render_buffer_invalidate(addr, FALSE);
254 }
257 page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
258 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
259 int32_t val;
260 if( page == NULL ) {
261 WARN( "Attempted byte read to missing page: %08X", addr );
262 return 0;
263 }
264 val = SIGNEXT8(io_rgn[(uintptr_t)page]->io_read(addr&0xFFF));
265 TRACE_IO( "Byte read %02X <= %08X", page, (addr&0xFFF), val&0xFF, addr );
266 return ZEROEXT32(val);
267 } else {
268 return ZEROEXT32(SIGNEXT8(*(int8_t *)(page+(addr&0xFFF))));
269 }
270 }
272 /**
273 * Convenience function to write a quad-word (implemented as two long writes).
274 */
275 void sh4_write_quad( sh4addr_t addr, uint64_t val )
276 {
277 sh4_write_long( addr, (uint32_t)val );
278 sh4_write_long( addr+4, (uint32_t)(val>>32) );
279 }
281 int32_t sh4_write_long( sh4addr_t vma, uint32_t val )
282 {
283 sh4ptr_t page;
285 uint64_t ppa = mmu_vma_to_phys_write(vma);
286 if( ppa>>32 ) {
287 return ppa>>32;
288 }
289 sh4addr_t addr = (sh4addr_t)ppa;
291 CHECK_WRITE_WATCH(addr,4,val);
293 if( addr >= 0xE0000000 ) {
294 sh4_write_p4( addr, val );
295 return 0;
296 } else if( (addr&0x1C000000) == 0x0C000000 ) {
297 *(uint32_t *)(sh4_main_ram + (addr&0x00FFFFFF)) = val;
298 xlat_invalidate_long(addr);
299 return 0;
300 } else if( (addr&0x1F800000) == 0x04000000 ||
301 (addr&0x1F800000) == 0x11000000 ) {
302 texcache_invalidate_page(addr& 0x7FFFFF);
303 addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
304 pvr2_render_buffer_invalidate(addr, TRUE);
305 } else if( (addr&0x1F800000) == 0x05000000 ) {
306 pvr2_render_buffer_invalidate(addr, TRUE);
307 }
309 if( (addr&0x1FFFFFFF) < 0x200000 ) {
310 WARN( "Attempted write to read-only memory: %08X => %08X", val, addr);
311 sh4_stop();
312 return 0;
313 }
314 if( (addr&0x1F800000) == 0x00800000 )
315 asic_g2_write_word();
317 page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
318 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
319 if( page == NULL ) {
320 if( (addr & 0x1F000000) >= 0x04000000 &&
321 (addr & 0x1F000000) < 0x07000000 )
322 return 0;
323 WARN( "Long write to missing page: %08X => %08X", val, addr );
324 return 0;
325 }
326 TRACE_IO( "Long write %08X => %08X", page, (addr&0xFFF), val, addr );
327 io_rgn[(uintptr_t)page]->io_write(addr&0xFFF, val);
328 } else {
329 *(uint32_t *)(page+(addr&0xFFF)) = val;
330 }
331 return 0;
332 }
334 int32_t sh4_write_word( sh4addr_t vma, uint32_t val )
335 {
336 sh4ptr_t page;
338 uint64_t ppa = mmu_vma_to_phys_write(vma);
339 if( ppa>>32 ) {
340 return ppa>>32;
341 }
342 sh4addr_t addr = (sh4addr_t)ppa;
344 CHECK_WRITE_WATCH(addr,2,val);
346 if( addr >= 0xE0000000 ) {
347 sh4_write_p4( addr, (int16_t)val );
348 return 0;
349 } else if( (addr&0x1C000000) == 0x0C000000 ) {
350 *(uint16_t *)(sh4_main_ram + (addr&0x00FFFFFF)) = val;
351 xlat_invalidate_word(addr);
352 return 0;
353 } else if( (addr&0x1F800000) == 0x04000000 ||
354 (addr&0x1F800000) == 0x11000000 ) {
355 texcache_invalidate_page(addr& 0x7FFFFF);
356 addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
357 pvr2_render_buffer_invalidate(addr, TRUE);
358 } else if( (addr&0x1F800000) == 0x05000000 ) {
359 pvr2_render_buffer_invalidate(addr, TRUE);
360 }
362 if( (addr&0x1FFFFFFF) < 0x200000 ) {
363 WARN( "Attempted write to read-only memory: %08X => %08X", val, addr);
364 sh4_stop();
365 return 0;
366 }
367 page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
368 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
369 if( page == NULL ) {
370 WARN( "Attempted word write to missing page: %08X", addr );
371 return 0;
372 }
373 TRACE_IO( "Word write %04X => %08X", page, (addr&0xFFF), val&0xFFFF, addr );
374 io_rgn[(uintptr_t)page]->io_write(addr&0xFFF, val);
375 } else {
376 *(uint16_t *)(page+(addr&0xFFF)) = val;
377 }
378 return 0;
379 }
381 int32_t sh4_write_byte( sh4addr_t vma, uint32_t val )
382 {
383 sh4ptr_t page;
385 uint64_t ppa = mmu_vma_to_phys_write(vma);
386 if( ppa>>32 ) {
387 return ppa>>32;
388 }
389 sh4addr_t addr = (sh4addr_t)ppa;
391 CHECK_WRITE_WATCH(addr,1,val);
393 if( addr >= 0xE0000000 ) {
394 sh4_write_p4( addr, (int8_t)val );
395 return 0;
396 } else if( (addr&0x1C000000) == 0x0C000000 ) {
397 *(uint8_t *)(sh4_main_ram + (addr&0x00FFFFFF)) = val;
398 xlat_invalidate_word(addr);
399 return 0;
400 } else if( (addr&0x1F800000) == 0x04000000 ||
401 (addr&0x1F800000) == 0x11000000 ) {
402 texcache_invalidate_page(addr& 0x7FFFFF);
403 addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
404 pvr2_render_buffer_invalidate(addr, TRUE);
405 } else if( (addr&0x1F800000) == 0x05000000 ) {
406 pvr2_render_buffer_invalidate(addr, TRUE);
407 }
409 if( (addr&0x1FFFFFFF) < 0x200000 ) {
410 WARN( "Attempted write to read-only memory: %08X => %08X", val, addr);
411 sh4_stop();
412 return 0;
413 }
414 page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
415 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
416 if( page == NULL ) {
417 WARN( "Attempted byte write to missing page: %08X", addr );
418 return 0;
419 }
420 TRACE_IO( "Byte write %02X => %08X", page, (addr&0xFFF), val&0xFF, addr );
421 io_rgn[(uintptr_t)page]->io_write( (addr&0xFFF), val);
422 } else {
423 *(uint8_t *)(page+(addr&0xFFF)) = val;
424 }
425 return 0;
426 }
430 /* FIXME: Handle all the many special cases when the range doesn't fall cleanly
431 * into the same memory block
432 */
433 void mem_copy_from_sh4( sh4ptr_t dest, sh4addr_t srcaddr, size_t count ) {
434 if( srcaddr >= 0x04000000 && srcaddr < 0x05000000 ) {
435 pvr2_vram64_read( dest, srcaddr, count );
436 } else {
437 sh4ptr_t src = mem_get_region(srcaddr);
438 if( src == NULL ) {
439 WARN( "Attempted block read from unknown address %08X", srcaddr );
440 } else {
441 memcpy( dest, src, count );
442 }
443 }
444 }
446 void mem_copy_to_sh4( sh4addr_t destaddr, sh4ptr_t src, size_t count ) {
447 if( destaddr >= 0x10000000 && destaddr < 0x14000000 ) {
448 pvr2_dma_write( destaddr, src, count );
449 return;
450 } else if( (destaddr & 0x1F800000) == 0x05000000 ) {
451 pvr2_render_buffer_invalidate( destaddr, TRUE );
452 } else if( (destaddr & 0x1F800000) == 0x04000000 ) {
453 pvr2_vram64_write( destaddr, src, count );
454 return;
455 }
456 sh4ptr_t dest = mem_get_region(destaddr);
457 if( dest == NULL )
458 WARN( "Attempted block write to unknown address %08X", destaddr );
459 else {
460 xlat_invalidate_block( destaddr, count );
461 memcpy( dest, src, count );
462 }
463 }
465 void sh4_flush_store_queue( sh4addr_t addr )
466 {
467 /* Store queue operation */
468 int queue = (addr&0x20)>>2;
469 sh4ptr_t src = (sh4ptr_t)&sh4r.store_queue[queue];
470 uint32_t hi = (MMIO_READ( MMU, (queue == 0 ? QACR0 : QACR1) ) & 0x1C) << 24;
471 uint32_t target = (addr&0x03FFFFE0) | hi;
472 mem_copy_to_sh4( target, src, 32 );
473 }
475 sh4ptr_t sh4_get_region_by_vma( sh4addr_t vma )
476 {
477 uint64_t ppa = mmu_vma_to_phys_read(vma);
478 if( ppa>>32 ) {
479 return 0;
480 }
482 sh4addr_t addr = (sh4addr_t)ppa;
483 sh4ptr_t page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
484 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
485 return NULL;
486 } else {
487 return page+(addr&0xFFF);
488 }
489 }
.