filename | src/sh4/sh4mem.c |
changeset | 527:14c9489f647e |
prev | 504:61afb3921c4a |
next | 550:a27e31340147 |
author | nkeynes |
date | Thu Nov 22 11:10:15 2007 +0000 (16 years ago) |
permissions | -rw-r--r-- |
last change | Re-add "Load Binary" menu item (misplaced in GUI rewrite) Prevent running with no code loaded |
view | annotate | diff | log | raw |
1 /**
2 * $Id: sh4mem.c,v 1.31 2007-11-08 12:01:57 nkeynes Exp $
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[];
70 sh4ptr_t sh4_main_ram;
72 int32_t sh4_read_p4( sh4addr_t addr )
73 {
74 struct mmio_region *io = P4_io[(addr&0x1FFFFFFF)>>19];
75 if( !io ) {
76 if( (addr & 0xFF000000) != 0xF4000000 ) {
77 /* OC address cache isn't implemented, but don't complain about it.
78 * Complain about anything else though */
79 WARN( "Attempted read from unknown P4 region: %08X", addr );
80 }
81 return 0;
82 } else {
83 int32_t val = io->io_read( addr&0xFFF );
84 TRACE_P4IO( "Long read %08X <= %08X", io, (addr&0xFFF), val, addr );
85 return val;
86 }
87 }
89 void sh4_write_p4( sh4addr_t addr, int32_t val )
90 {
91 struct mmio_region *io = P4_io[(addr&0x1FFFFFFF)>>19];
92 if( !io ) {
93 if( (addr & 0xFC000000) == 0xE0000000 ) {
94 /* Store queue */
95 SH4_WRITE_STORE_QUEUE( addr, val );
96 } else if( (addr & 0xFF000000) != 0xF4000000 ) {
97 /* OC address cache isn't implemented, but don't complain about it.
98 * Complain about anything else though */
99 WARN( "Attempted write to unknown P4 region: %08X", addr );
100 }
101 } else {
102 TRACE_P4IO( "Long write %08X => %08X", io, (addr&0xFFF), val, addr );
103 io->io_write( addr&0xFFF, val );
104 }
105 }
107 int32_t sh4_read_phys_word( sh4addr_t addr )
108 {
109 sh4ptr_t page;
110 if( addr >= 0xE0000000 ) /* P4 Area, handled specially */
111 return SIGNEXT16(sh4_read_p4( addr ));
113 if( (addr&0x1F800000) == 0x04000000 ) {
114 addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
115 }
117 page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
118 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
119 if( page == NULL ) {
120 WARN( "Attempted word read to missing page: %08X",
121 addr );
122 return 0;
123 }
124 return SIGNEXT16(io_rgn[(uintptr_t)page]->io_read(addr&0xFFF));
125 } else {
126 return SIGNEXT16(*(int16_t *)(page+(addr&0xFFF)));
127 }
128 }
130 /**
131 * Convenience function to read a quad-word (implemented as two long reads).
132 */
133 int64_t sh4_read_quad( sh4addr_t addr )
134 {
135 return ((int64_t)((uint32_t)sh4_read_long(addr))) |
136 (((int64_t)((uint32_t)sh4_read_long(addr+4))) << 32);
137 }
139 int32_t sh4_read_long( sh4addr_t addr )
140 {
141 sh4ptr_t page;
143 CHECK_READ_WATCH(addr,4);
145 if( addr >= 0xE0000000 ) { /* P4 Area, handled specially */
146 return sh4_read_p4( addr );
147 } else if( (addr&0x1C000000) == 0x0C000000 ) {
148 return *(int32_t *)(sh4_main_ram + (addr&0x00FFFFFF));
149 } else if( (addr&0x1F800000) == 0x04000000 ) {
150 addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
151 pvr2_render_buffer_invalidate(addr, FALSE);
152 } else if( (addr&0x1F800000) == 0x05000000 ) {
153 pvr2_render_buffer_invalidate(addr, FALSE);
154 }
156 page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
157 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
158 int32_t val;
159 if( page == NULL ) {
160 WARN( "Attempted long read to missing page: %08X", addr );
161 return 0;
162 }
163 val = io_rgn[(uintptr_t)page]->io_read(addr&0xFFF);
164 TRACE_IO( "Long read %08X <= %08X", page, (addr&0xFFF), val, addr );
165 return val;
166 } else {
167 return *(int32_t *)(page+(addr&0xFFF));
168 }
169 }
171 int32_t sh4_read_word( sh4addr_t addr )
172 {
173 sh4ptr_t page;
175 CHECK_READ_WATCH(addr,2);
177 if( addr >= 0xE0000000 ) { /* P4 Area, handled specially */
178 return SIGNEXT16(sh4_read_p4( addr ));
179 } else if( (addr&0x1C000000) == 0x0C000000 ) {
180 return SIGNEXT16(*(int16_t *)(sh4_main_ram + (addr&0x00FFFFFF)));
181 } else if( (addr&0x1F800000) == 0x04000000 ) {
182 addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
183 pvr2_render_buffer_invalidate(addr, FALSE);
184 } else if( (addr&0x1F800000) == 0x05000000 ) {
185 pvr2_render_buffer_invalidate(addr, FALSE);
186 }
188 page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
189 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
190 int32_t val;
191 if( page == NULL ) {
192 WARN( "Attempted word read to missing page: %08X", addr );
193 return 0;
194 }
195 val = SIGNEXT16(io_rgn[(uintptr_t)page]->io_read(addr&0xFFF));
196 TRACE_IO( "Word read %04X <= %08X", page, (addr&0xFFF), val&0xFFFF, addr );
197 return val;
198 } else {
199 return SIGNEXT16(*(int16_t *)(page+(addr&0xFFF)));
200 }
201 }
203 int32_t sh4_read_byte( sh4addr_t addr )
204 {
205 sh4ptr_t page;
207 CHECK_READ_WATCH(addr,1);
209 if( addr >= 0xE0000000 ) { /* P4 Area, handled specially */
210 return SIGNEXT8(sh4_read_p4( addr ));
211 } else if( (addr&0x1C000000) == 0x0C000000 ) {
212 return SIGNEXT8(*(int8_t *)(sh4_main_ram + (addr&0x00FFFFFF)));
213 } else if( (addr&0x1F800000) == 0x04000000 ) {
214 addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
215 pvr2_render_buffer_invalidate(addr, FALSE);
216 } else if( (addr&0x1F800000) == 0x05000000 ) {
217 pvr2_render_buffer_invalidate(addr, FALSE);
218 }
221 page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
222 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
223 int32_t val;
224 if( page == NULL ) {
225 WARN( "Attempted byte read to missing page: %08X", addr );
226 return 0;
227 }
228 val = SIGNEXT8(io_rgn[(uintptr_t)page]->io_read(addr&0xFFF));
229 TRACE_IO( "Byte read %02X <= %08X", page, (addr&0xFFF), val&0xFF, addr );
230 return val;
231 } else {
232 return SIGNEXT8(*(int8_t *)(page+(addr&0xFFF)));
233 }
234 }
236 /**
237 * Convenience function to write a quad-word (implemented as two long writes).
238 */
239 void sh4_write_quad( sh4addr_t addr, uint64_t val )
240 {
241 sh4_write_long( addr, (uint32_t)val );
242 sh4_write_long( addr+4, (uint32_t)(val>>32) );
243 }
245 void sh4_write_long( sh4addr_t addr, uint32_t val )
246 {
247 sh4ptr_t page;
249 CHECK_WRITE_WATCH(addr,4,val);
251 if( addr >= 0xE0000000 ) {
252 sh4_write_p4( addr, val );
253 return;
254 } else if( (addr&0x1C000000) == 0x0C000000 ) {
255 *(uint32_t *)(sh4_main_ram + (addr&0x00FFFFFF)) = val;
256 xlat_invalidate_long(addr);
257 return;
258 } else if( (addr&0x1F800000) == 0x04000000 ||
259 (addr&0x1F800000) == 0x11000000 ) {
260 texcache_invalidate_page(addr& 0x7FFFFF);
261 addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
262 pvr2_render_buffer_invalidate(addr, TRUE);
263 } else if( (addr&0x1F800000) == 0x05000000 ) {
264 pvr2_render_buffer_invalidate(addr, TRUE);
265 }
267 if( (addr&0x1FFFFFFF) < 0x200000 ) {
268 WARN( "Attempted write to read-only memory: %08X => %08X", val, addr);
269 sh4_stop();
270 return;
271 }
272 if( (addr&0x1F800000) == 0x00800000 )
273 asic_g2_write_word();
275 page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
276 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
277 if( page == NULL ) {
278 if( (addr & 0x1F000000) >= 0x04000000 &&
279 (addr & 0x1F000000) < 0x07000000 )
280 return;
281 WARN( "Long write to missing page: %08X => %08X", val, addr );
282 return;
283 }
284 TRACE_IO( "Long write %08X => %08X", page, (addr&0xFFF), val, addr );
285 io_rgn[(uintptr_t)page]->io_write(addr&0xFFF, val);
286 } else {
287 *(uint32_t *)(page+(addr&0xFFF)) = val;
288 }
289 }
291 void sh4_write_word( sh4addr_t addr, uint32_t val )
292 {
293 sh4ptr_t page;
295 CHECK_WRITE_WATCH(addr,2,val);
297 if( addr >= 0xE0000000 ) {
298 sh4_write_p4( addr, (int16_t)val );
299 return;
300 } else if( (addr&0x1C000000) == 0x0C000000 ) {
301 *(uint16_t *)(sh4_main_ram + (addr&0x00FFFFFF)) = val;
302 xlat_invalidate_word(addr);
303 return;
304 } else if( (addr&0x1F800000) == 0x04000000 ||
305 (addr&0x1F800000) == 0x11000000 ) {
306 texcache_invalidate_page(addr& 0x7FFFFF);
307 addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
308 pvr2_render_buffer_invalidate(addr, TRUE);
309 } else if( (addr&0x1F800000) == 0x05000000 ) {
310 pvr2_render_buffer_invalidate(addr, TRUE);
311 }
313 if( (addr&0x1FFFFFFF) < 0x200000 ) {
314 WARN( "Attempted write to read-only memory: %08X => %08X", val, addr);
315 sh4_stop();
316 return;
317 }
318 page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
319 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
320 if( page == NULL ) {
321 WARN( "Attempted word write to missing page: %08X", addr );
322 return;
323 }
324 TRACE_IO( "Word write %04X => %08X", page, (addr&0xFFF), val&0xFFFF, addr );
325 io_rgn[(uintptr_t)page]->io_write(addr&0xFFF, val);
326 } else {
327 *(uint16_t *)(page+(addr&0xFFF)) = val;
328 }
329 }
331 void sh4_write_byte( sh4addr_t addr, uint32_t val )
332 {
333 sh4ptr_t page;
335 CHECK_WRITE_WATCH(addr,1,val);
337 if( addr >= 0xE0000000 ) {
338 sh4_write_p4( addr, (int8_t)val );
339 return;
340 } else if( (addr&0x1C000000) == 0x0C000000 ) {
341 *(uint8_t *)(sh4_main_ram + (addr&0x00FFFFFF)) = val;
342 xlat_invalidate_word(addr);
343 return;
344 } else if( (addr&0x1F800000) == 0x04000000 ||
345 (addr&0x1F800000) == 0x11000000 ) {
346 texcache_invalidate_page(addr& 0x7FFFFF);
347 addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
348 pvr2_render_buffer_invalidate(addr, TRUE);
349 } else if( (addr&0x1F800000) == 0x05000000 ) {
350 pvr2_render_buffer_invalidate(addr, TRUE);
351 }
353 if( (addr&0x1FFFFFFF) < 0x200000 ) {
354 WARN( "Attempted write to read-only memory: %08X => %08X", val, addr);
355 sh4_stop();
356 return;
357 }
358 page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
359 if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
360 if( page == NULL ) {
361 WARN( "Attempted byte write to missing page: %08X", addr );
362 return;
363 }
364 TRACE_IO( "Byte write %02X => %08X", page, (addr&0xFFF), val&0xFF, addr );
365 io_rgn[(uintptr_t)page]->io_write( (addr&0xFFF), val);
366 } else {
367 *(uint8_t *)(page+(addr&0xFFF)) = val;
368 }
369 }
373 /* FIXME: Handle all the many special cases when the range doesn't fall cleanly
374 * into the same memory block
375 */
376 void mem_copy_from_sh4( sh4ptr_t dest, sh4addr_t srcaddr, size_t count ) {
377 if( srcaddr >= 0x04000000 && srcaddr < 0x05000000 ) {
378 pvr2_vram64_read( dest, srcaddr, count );
379 } else {
380 sh4ptr_t src = mem_get_region(srcaddr);
381 if( src == NULL ) {
382 WARN( "Attempted block read from unknown address %08X", srcaddr );
383 } else {
384 memcpy( dest, src, count );
385 }
386 }
387 }
389 void mem_copy_to_sh4( sh4addr_t destaddr, sh4ptr_t src, size_t count ) {
390 if( destaddr >= 0x10000000 && destaddr < 0x14000000 ) {
391 pvr2_dma_write( destaddr, src, count );
392 return;
393 } else if( (destaddr & 0x1F800000) == 0x05000000 ) {
394 pvr2_render_buffer_invalidate( destaddr, TRUE );
395 } else if( (destaddr & 0x1F800000) == 0x04000000 ) {
396 pvr2_vram64_write( destaddr, src, count );
397 return;
398 }
399 sh4ptr_t dest = mem_get_region(destaddr);
400 if( dest == NULL )
401 WARN( "Attempted block write to unknown address %08X", destaddr );
402 else {
403 xlat_invalidate_block( destaddr, count );
404 memcpy( dest, src, count );
405 }
406 }
408 void sh4_flush_store_queue( sh4addr_t addr )
409 {
410 /* Store queue operation */
411 int queue = (addr&0x20)>>2;
412 sh4ptr_t src = (sh4ptr_t)&sh4r.store_queue[queue];
413 uint32_t hi = (MMIO_READ( MMU, (queue == 0 ? QACR0 : QACR1) ) & 0x1C) << 24;
414 uint32_t target = (addr&0x03FFFFE0) | hi;
415 mem_copy_to_sh4( target, src, 32 );
416 }
.