Search
lxdream.org :: lxdream/src/sh4/sh4mem.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/sh4mem.c
changeset 527:14c9489f647e
prev504:61afb3921c4a
next550:a27e31340147
author nkeynes
date Sun Nov 18 11:12:44 2007 +0000 (12 years ago)
permissions -rw-r--r--
last change x86-64 translator work-in-progress
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 }
.