Search
lxdream.org :: lxdream/src/sh4/sh4mem.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/sh4mem.c
changeset 562:e598411b640b
prev561:533f6b478071
next569:a1c49e1e8776
author nkeynes
date Tue Jan 01 05:48:06 2008 +0000 (16 years ago)
branchlxdream-mmu
permissions -rw-r--r--
last change Add configure and command-line parameters to perform IO tracing
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[];
    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 	switch( addr & 0x1F000000 ) {
    77 	case 0x00000000: case 0x01000000: case 0x02000000: case 0x03000000: 
    78 	    /* Store queue - readable? */
    79 	    return 0;
    80 	    break;
    81 	case 0x10000000: return mmu_icache_addr_read( addr );
    82 	case 0x11000000: return mmu_icache_data_read( addr );
    83 	case 0x12000000: return mmu_itlb_addr_read( addr );
    84 	case 0x13000000: return mmu_itlb_data_read( addr );
    85 	case 0x14000000: return mmu_ocache_addr_read( addr );
    86 	case 0x15000000: return mmu_ocache_data_read( addr );
    87 	case 0x16000000: return mmu_utlb_addr_read( addr );
    88 	case 0x17000000: return mmu_utlb_data_read( addr );
    89 	default:
    90             WARN( "Attempted read from unknown or invalid P4 region: %08X", addr );
    91 	    return 0;
    92         }
    93     } else {
    94 	int32_t val = io->io_read( addr&0xFFF );
    95 	TRACE_P4IO( "Long read %08X <= %08X", io, (addr&0xFFF), val, addr );
    96         return val;
    97     }    
    98 }
   100 void sh4_write_p4( sh4addr_t addr, int32_t val )
   101 {
   102     struct mmio_region *io = P4_io[(addr&0x1FFFFFFF)>>19];
   103     if( !io ) {
   104 	switch( addr & 0x1F000000 ) {
   105 	case 0x00000000: case 0x01000000: case 0x02000000: case 0x03000000: 
   106             /* Store queue */
   107             SH4_WRITE_STORE_QUEUE( addr, val );
   108 	    break;
   109 	case 0x10000000: mmu_icache_addr_write( addr, val ); break;
   110 	case 0x11000000: mmu_icache_data_write( addr, val ); break;
   111 	case 0x12000000: mmu_itlb_addr_write( addr, val ); break;
   112 	case 0x13000000: mmu_itlb_data_write( addr, val ); break;
   113 	case 0x14000000: mmu_ocache_addr_write( addr, val ); break;
   114 	case 0x15000000: mmu_ocache_data_write( addr, val ); break;
   115 	case 0x16000000: mmu_utlb_addr_write( addr, val ); break;
   116 	case 0x17000000: mmu_utlb_data_write( addr, val ); break;
   117 	default:
   118             WARN( "Attempted write to unknown P4 region: %08X", addr );
   119         }
   120     } else {
   121 	TRACE_P4IO( "Long write %08X => %08X", io, (addr&0xFFF), val, addr );
   122         io->io_write( addr&0xFFF, val );
   123     }
   124 }
   126 int32_t sh4_read_phys_word( sh4addr_t addr )
   127 {
   128     sh4ptr_t page;
   129     if( addr >= 0xE0000000 ) /* P4 Area, handled specially */
   130         return SIGNEXT16(sh4_read_p4( addr ));
   132     if( (addr&0x1F800000) == 0x04000000 ) {
   133         addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
   134     }
   136     page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
   137     if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
   138         if( page == NULL ) {
   139             WARN( "Attempted word read to missing page: %08X",
   140                    addr );
   141             return 0;
   142         }
   143         return SIGNEXT16(io_rgn[(uintptr_t)page]->io_read(addr&0xFFF));
   144     } else {
   145         return SIGNEXT16(*(int16_t *)(page+(addr&0xFFF)));
   146     }
   147 }
   149 /**
   150  * Convenience function to read a quad-word (implemented as two long reads).
   151  */
   152 int64_t sh4_read_quad( sh4addr_t addr )
   153 {
   154     return ((int64_t)((uint32_t)sh4_read_long(addr))) |
   155 	(((int64_t)((uint32_t)sh4_read_long(addr+4))) << 32);
   156 }
   158 int64_t sh4_read_long( sh4addr_t vma )
   159 {
   160     sh4ptr_t page;
   162     CHECK_READ_WATCH(addr,4);
   164     uint64_t ppa = mmu_vma_to_phys_read(vma);
   165     if( ppa>>32 ) {
   166 	return ppa;
   167     }
   168     sh4addr_t addr = (sh4addr_t)ppa;
   170     if( addr >= 0xE0000000 ) { /* P4 Area, handled specially */
   171         return ZEROEXT32(sh4_read_p4( addr ));
   172     } else if( (addr&0x1C000000) == 0x0C000000 ) {
   173 	return ZEROEXT32(*(int32_t *)(sh4_main_ram + (addr&0x00FFFFFF)));
   174     } else if( (addr&0x1F800000) == 0x04000000 ) {
   175         addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
   176 	pvr2_render_buffer_invalidate(addr, FALSE);
   177     } else if( (addr&0x1F800000) == 0x05000000 ) {
   178 	pvr2_render_buffer_invalidate(addr, FALSE);
   179     }
   181     page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
   182     if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
   183         int32_t val;
   184         if( page == NULL ) {
   185             WARN( "Attempted long read to missing page: %08X", addr );
   186             return 0;
   187         }
   188         val = io_rgn[(uintptr_t)page]->io_read(addr&0xFFF);
   189         TRACE_IO( "Long read %08X <= %08X", page, (addr&0xFFF), val, addr );
   190         return ZEROEXT32(val);
   191     } else {
   192         return ZEROEXT32(*(int32_t *)(page+(addr&0xFFF)));
   193     }
   194 }
   196 int64_t sh4_read_word( sh4addr_t vma )
   197 {
   198     sh4ptr_t page;
   200     CHECK_READ_WATCH(addr,2);
   202     uint64_t ppa = mmu_vma_to_phys_read(vma);
   203     if( ppa>>32 ) {
   204 	return ppa;
   205     }
   206     sh4addr_t addr = (sh4addr_t)ppa;
   208     if( addr >= 0xE0000000 ) { /* P4 Area, handled specially */
   209         return ZEROEXT32(SIGNEXT16(sh4_read_p4( addr )));
   210     } else if( (addr&0x1C000000) == 0x0C000000 ) {
   211 	return ZEROEXT32(SIGNEXT16(*(int16_t *)(sh4_main_ram + (addr&0x00FFFFFF))));
   212     } else if( (addr&0x1F800000) == 0x04000000 ) {
   213         addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
   214 	pvr2_render_buffer_invalidate(addr, FALSE);
   215     } else if( (addr&0x1F800000) == 0x05000000 ) {
   216 	pvr2_render_buffer_invalidate(addr, FALSE);
   217     }
   219     page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
   220     if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
   221         int32_t val;
   222         if( page == NULL ) {
   223 	    WARN( "Attempted word read to missing page: %08X", addr );
   224             return 0;
   225         }
   226         val = SIGNEXT16(io_rgn[(uintptr_t)page]->io_read(addr&0xFFF));
   227         TRACE_IO( "Word read %04X <= %08X", page, (addr&0xFFF), val&0xFFFF, addr );
   228         return ZEROEXT32(val);
   229     } else {
   230         return ZEROEXT32(SIGNEXT16(*(int16_t *)(page+(addr&0xFFF))));
   231     }
   232 }
   234 int64_t sh4_read_byte( sh4addr_t vma )
   235 {
   236     sh4ptr_t page;
   238     CHECK_READ_WATCH(addr,1);
   240     uint64_t ppa = mmu_vma_to_phys_read(vma);
   241     if( ppa>>32 ) {
   242 	return ppa;
   243     }
   244     sh4addr_t addr = (sh4addr_t)ppa;
   246     if( addr >= 0xE0000000 ) { /* P4 Area, handled specially */
   247         return ZEROEXT32(SIGNEXT8(sh4_read_p4( addr )));
   248     } else if( (addr&0x1C000000) == 0x0C000000 ) {
   249 	return ZEROEXT32(SIGNEXT8(*(int8_t *)(sh4_main_ram + (addr&0x00FFFFFF))));
   250     } else if( (addr&0x1F800000) == 0x04000000 ) {
   251         addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
   252     	pvr2_render_buffer_invalidate(addr, FALSE);
   253     } else if( (addr&0x1F800000) == 0x05000000 ) {
   254 	pvr2_render_buffer_invalidate(addr, FALSE);
   255     }
   258     page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
   259     if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
   260         int32_t val;
   261         if( page == NULL ) {
   262             WARN( "Attempted byte read to missing page: %08X", addr );
   263             return 0;
   264         }
   265         val = SIGNEXT8(io_rgn[(uintptr_t)page]->io_read(addr&0xFFF));
   266         TRACE_IO( "Byte read %02X <= %08X", page, (addr&0xFFF), val&0xFF, addr );
   267         return ZEROEXT32(val);
   268     } else {
   269         return ZEROEXT32(SIGNEXT8(*(int8_t *)(page+(addr&0xFFF))));
   270     }
   271 }
   273 /**
   274  * Convenience function to write a quad-word (implemented as two long writes).
   275  */
   276 void sh4_write_quad( sh4addr_t addr, uint64_t val )
   277 {
   278     sh4_write_long( addr, (uint32_t)val );
   279     sh4_write_long( addr+4, (uint32_t)(val>>32) );
   280 }
   282 int32_t sh4_write_long( sh4addr_t vma, uint32_t val )
   283 {
   284     sh4ptr_t page;
   286     uint64_t ppa = mmu_vma_to_phys_write(vma);
   287     if( ppa>>32 ) {
   288 	return ppa>>32;
   289     }
   290     sh4addr_t addr = (sh4addr_t)ppa;
   292     CHECK_WRITE_WATCH(addr,4,val);
   294     if( addr >= 0xE0000000 ) {
   295         sh4_write_p4( addr, val );
   296         return 0;
   297     } else if( (addr&0x1C000000) == 0x0C000000 ) {
   298 	*(uint32_t *)(sh4_main_ram + (addr&0x00FFFFFF)) = val;
   299 	xlat_invalidate_long(addr);
   300 	return 0;
   301     } else if( (addr&0x1F800000) == 0x04000000 || 
   302 	       (addr&0x1F800000) == 0x11000000 ) {
   303 	texcache_invalidate_page(addr& 0x7FFFFF);
   304         addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
   305 	pvr2_render_buffer_invalidate(addr, TRUE);
   306     } else if( (addr&0x1F800000) == 0x05000000 ) {
   307 	pvr2_render_buffer_invalidate(addr, TRUE);
   308     }
   310     if( (addr&0x1FFFFFFF) < 0x200000 ) {
   311         WARN( "Attempted write to read-only memory: %08X => %08X", val, addr);
   312         sh4_stop();
   313         return 0;
   314     }
   315     if( (addr&0x1F800000) == 0x00800000 )
   316 	asic_g2_write_word();
   318     page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
   319     if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
   320         if( page == NULL ) {
   321 	    if( (addr & 0x1F000000) >= 0x04000000 &&
   322 		(addr & 0x1F000000) < 0x07000000 )
   323 		return 0;
   324             WARN( "Long write to missing page: %08X => %08X", val, addr );
   325             return 0;
   326         }
   327         TRACE_IO( "Long write %08X => %08X", page, (addr&0xFFF), val, addr );
   328         io_rgn[(uintptr_t)page]->io_write(addr&0xFFF, val);
   329     } else {
   330         *(uint32_t *)(page+(addr&0xFFF)) = val;
   331     }
   332     return 0;
   333 }
   335 int32_t sh4_write_word( sh4addr_t vma, uint32_t val )
   336 {
   337     sh4ptr_t page;
   339     uint64_t ppa = mmu_vma_to_phys_write(vma);
   340     if( ppa>>32 ) {
   341 	return ppa>>32;
   342     }
   343     sh4addr_t addr = (sh4addr_t)ppa;
   345     CHECK_WRITE_WATCH(addr,2,val);
   347     if( addr >= 0xE0000000 ) {
   348         sh4_write_p4( addr, (int16_t)val );
   349         return 0;
   350     } else if( (addr&0x1C000000) == 0x0C000000 ) {
   351 	*(uint16_t *)(sh4_main_ram + (addr&0x00FFFFFF)) = val;
   352 	xlat_invalidate_word(addr);
   353 	return 0;
   354     } else if( (addr&0x1F800000) == 0x04000000 ||
   355 	(addr&0x1F800000) == 0x11000000 ) {
   356 	texcache_invalidate_page(addr& 0x7FFFFF);
   357         addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
   358 	pvr2_render_buffer_invalidate(addr, TRUE);
   359     } else if( (addr&0x1F800000) == 0x05000000 ) {
   360 	pvr2_render_buffer_invalidate(addr, TRUE);
   361     }
   363     if( (addr&0x1FFFFFFF) < 0x200000 ) {
   364         WARN( "Attempted write to read-only memory: %08X => %08X", val, addr);
   365         sh4_stop();
   366         return 0;
   367     }
   368     page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
   369     if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
   370         if( page == NULL ) {
   371             WARN( "Attempted word write to missing page: %08X", addr );
   372             return 0;
   373         }
   374         TRACE_IO( "Word write %04X => %08X", page, (addr&0xFFF), val&0xFFFF, addr );
   375         io_rgn[(uintptr_t)page]->io_write(addr&0xFFF, val);
   376     } else {
   377         *(uint16_t *)(page+(addr&0xFFF)) = val;
   378     }
   379     return 0;
   380 }
   382 int32_t sh4_write_byte( sh4addr_t vma, uint32_t val )
   383 {
   384     sh4ptr_t page;
   386     uint64_t ppa = mmu_vma_to_phys_write(vma);
   387     if( ppa>>32 ) {
   388 	return ppa>>32;
   389     }
   390     sh4addr_t addr = (sh4addr_t)ppa;
   392     CHECK_WRITE_WATCH(addr,1,val);
   394     if( addr >= 0xE0000000 ) {
   395         sh4_write_p4( addr, (int8_t)val );
   396         return 0;
   397     } else if( (addr&0x1C000000) == 0x0C000000 ) {
   398 	*(uint8_t *)(sh4_main_ram + (addr&0x00FFFFFF)) = val;
   399 	xlat_invalidate_word(addr);
   400 	return 0;
   401     } else if( (addr&0x1F800000) == 0x04000000 ||
   402 	       (addr&0x1F800000) == 0x11000000 ) {
   403 	texcache_invalidate_page(addr& 0x7FFFFF);
   404         addr = TRANSLATE_VIDEO_64BIT_ADDRESS(addr);
   405 	pvr2_render_buffer_invalidate(addr, TRUE);
   406     } else if( (addr&0x1F800000) == 0x05000000 ) {
   407 	pvr2_render_buffer_invalidate(addr, TRUE);
   408     }
   410     if( (addr&0x1FFFFFFF) < 0x200000 ) {
   411         WARN( "Attempted write to read-only memory: %08X => %08X", val, addr);
   412         sh4_stop();
   413         return 0;
   414     }
   415     page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
   416     if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
   417         if( page == NULL ) {
   418             WARN( "Attempted byte write to missing page: %08X", addr );
   419             return 0;
   420         }
   421         TRACE_IO( "Byte write %02X => %08X", page, (addr&0xFFF), val&0xFF, addr );
   422         io_rgn[(uintptr_t)page]->io_write( (addr&0xFFF), val);
   423     } else {
   424         *(uint8_t *)(page+(addr&0xFFF)) = val;
   425     }
   426     return 0;
   427 }
   431 /* FIXME: Handle all the many special cases when the range doesn't fall cleanly
   432  * into the same memory block
   433  */
   434 void mem_copy_from_sh4( sh4ptr_t dest, sh4addr_t srcaddr, size_t count ) {
   435     if( srcaddr >= 0x04000000 && srcaddr < 0x05000000 ) {
   436 	pvr2_vram64_read( dest, srcaddr, count );
   437     } else {
   438 	sh4ptr_t src = mem_get_region(srcaddr);
   439 	if( src == NULL ) {
   440 	    WARN( "Attempted block read from unknown address %08X", srcaddr );
   441 	} else {
   442 	    memcpy( dest, src, count );
   443 	}
   444     }
   445 }
   447 void mem_copy_to_sh4( sh4addr_t destaddr, sh4ptr_t src, size_t count ) {
   448     if( destaddr >= 0x10000000 && destaddr < 0x14000000 ) {
   449 	pvr2_dma_write( destaddr, src, count );
   450 	return;
   451     } else if( (destaddr & 0x1F800000) == 0x05000000 ) {
   452 	pvr2_render_buffer_invalidate( destaddr, TRUE );
   453     } else if( (destaddr & 0x1F800000) == 0x04000000 ) {
   454 	pvr2_vram64_write( destaddr, src, count );
   455 	return;
   456     }
   457     sh4ptr_t dest = mem_get_region(destaddr);
   458     if( dest == NULL )
   459 	WARN( "Attempted block write to unknown address %08X", destaddr );
   460     else {
   461 	xlat_invalidate_block( destaddr, count );
   462 	memcpy( dest, src, count );
   463     }
   464 }
   466 void sh4_flush_store_queue( sh4addr_t addr )
   467 {
   468     /* Store queue operation */
   469     int queue = (addr&0x20)>>2;
   470     sh4ptr_t src = (sh4ptr_t)&sh4r.store_queue[queue];
   471     uint32_t hi = (MMIO_READ( MMU, (queue == 0 ? QACR0 : QACR1) ) & 0x1C) << 24;
   472     uint32_t target = (addr&0x03FFFFE0) | hi;
   473     mem_copy_to_sh4( target, src, 32 );
   474 }
   476 sh4ptr_t sh4_get_region_by_vma( sh4addr_t vma )
   477 {
   478     uint64_t ppa = mmu_vma_to_phys_read(vma);
   479     if( ppa>>32 ) {
   480 	return 0;
   481     }
   483     sh4addr_t addr = (sh4addr_t)ppa;
   484     sh4ptr_t page = page_map[ (addr & 0x1FFFFFFF) >> 12 ];
   485     if( ((uintptr_t)page) < MAX_IO_REGIONS ) { /* IO Region */
   486         return NULL;
   487     } else {
   488         return page+(addr&0xFFF);
   489     }
   490 }
.