nkeynes@10: /** nkeynes@586: * $Id$ nkeynes@929: * sh4mem.c is responsible for interfacing between the SH4's internal memory nkeynes@10: * nkeynes@10: * Copyright (c) 2005 Nathan Keynes. nkeynes@10: * nkeynes@10: * This program is free software; you can redistribute it and/or modify nkeynes@10: * it under the terms of the GNU General Public License as published by nkeynes@10: * the Free Software Foundation; either version 2 of the License, or nkeynes@10: * (at your option) any later version. nkeynes@10: * nkeynes@10: * This program is distributed in the hope that it will be useful, nkeynes@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@10: * GNU General Public License for more details. nkeynes@10: */ nkeynes@10: nkeynes@35: #define MODULE sh4_module nkeynes@35: nkeynes@10: #include nkeynes@10: #include nkeynes@10: #include "dream.h" nkeynes@10: #include "mem.h" nkeynes@10: #include "mmio.h" nkeynes@10: #include "dreamcast.h" nkeynes@422: #include "sh4/sh4core.h" nkeynes@422: #include "sh4/sh4mmio.h" nkeynes@422: #include "sh4/xltcache.h" nkeynes@100: #include "pvr2/pvr2.h" nkeynes@10: nkeynes@929: /* System regions (probably should be defined elsewhere) */ nkeynes@929: extern struct mem_region_fn mem_region_unmapped; nkeynes@929: extern struct mem_region_fn mem_region_sdram; nkeynes@929: extern struct mem_region_fn mem_region_vram32; nkeynes@929: extern struct mem_region_fn mem_region_vram64; nkeynes@929: extern struct mem_region_fn mem_region_audioram; nkeynes@929: extern struct mem_region_fn mem_region_flashram; nkeynes@929: extern struct mem_region_fn mem_region_bootrom; nkeynes@10: nkeynes@929: /* On-chip regions other than defined MMIO regions */ nkeynes@933: extern struct mem_region_fn p4_region_storequeue; nkeynes@933: extern struct mem_region_fn p4_region_icache_addr; nkeynes@933: extern struct mem_region_fn p4_region_icache_data; nkeynes@933: extern struct mem_region_fn p4_region_ocache_addr; nkeynes@933: extern struct mem_region_fn p4_region_ocache_data; nkeynes@933: extern struct mem_region_fn p4_region_itlb_addr; nkeynes@933: extern struct mem_region_fn p4_region_itlb_data; nkeynes@933: extern struct mem_region_fn p4_region_utlb_addr; nkeynes@933: extern struct mem_region_fn p4_region_utlb_data; nkeynes@10: nkeynes@929: /********************* The main ram address space **********************/ nkeynes@929: static int32_t FASTCALL ext_sdram_read_long( sh4addr_t addr ) nkeynes@929: { nkeynes@934: return *((int32_t *)(dc_main_ram + (addr&0x00FFFFFF))); nkeynes@929: } nkeynes@929: static int32_t FASTCALL ext_sdram_read_word( sh4addr_t addr ) nkeynes@929: { nkeynes@934: return SIGNEXT16(*((int16_t *)(dc_main_ram + (addr&0x00FFFFFF)))); nkeynes@929: } nkeynes@929: static int32_t FASTCALL ext_sdram_read_byte( sh4addr_t addr ) nkeynes@929: { nkeynes@934: return SIGNEXT8(*((int16_t *)(dc_main_ram + (addr&0x00FFFFFF)))); nkeynes@929: } nkeynes@929: static void FASTCALL ext_sdram_write_long( sh4addr_t addr, uint32_t val ) nkeynes@929: { nkeynes@934: *(uint32_t *)(dc_main_ram + (addr&0x00FFFFFF)) = val; nkeynes@929: xlat_invalidate_long(addr); nkeynes@929: } nkeynes@929: static void FASTCALL ext_sdram_write_word( sh4addr_t addr, uint32_t val ) nkeynes@929: { nkeynes@934: *(uint16_t *)(dc_main_ram + (addr&0x00FFFFFF)) = (uint16_t)val; nkeynes@929: xlat_invalidate_word(addr); nkeynes@929: } nkeynes@929: static void FASTCALL ext_sdram_write_byte( sh4addr_t addr, uint32_t val ) nkeynes@929: { nkeynes@934: *(uint8_t *)(dc_main_ram + (addr&0x00FFFFFF)) = (uint8_t)val; nkeynes@929: xlat_invalidate_word(addr); nkeynes@929: } nkeynes@929: static void FASTCALL ext_sdram_read_burst( unsigned char *dest, sh4addr_t addr ) nkeynes@929: { nkeynes@934: memcpy( dest, dc_main_ram+(addr&0x00FFFFFF), 32 ); nkeynes@929: } nkeynes@929: static void FASTCALL ext_sdram_write_burst( sh4addr_t addr, unsigned char *src ) nkeynes@929: { nkeynes@934: memcpy( dc_main_ram+(addr&0x00FFFFFF), src, 32 ); nkeynes@929: } nkeynes@929: nkeynes@929: struct mem_region_fn mem_region_sdram = { ext_sdram_read_long, ext_sdram_write_long, nkeynes@929: ext_sdram_read_word, ext_sdram_write_word, nkeynes@929: ext_sdram_read_byte, ext_sdram_write_byte, nkeynes@929: ext_sdram_read_burst, ext_sdram_write_burst }; nkeynes@929: nkeynes@929: nkeynes@931: /***************************** P4 Regions ************************************/ nkeynes@929: nkeynes@931: /* Store-queue (long-write only?) */ nkeynes@931: static void FASTCALL p4_storequeue_write_long( sh4addr_t addr, uint32_t val ) nkeynes@931: { nkeynes@931: sh4r.store_queue[(addr>>2)&0xF] = val; nkeynes@931: } nkeynes@931: static int32_t FASTCALL p4_storequeue_read_long( sh4addr_t addr ) nkeynes@931: { nkeynes@931: return sh4r.store_queue[(addr>>2)&0xF]; nkeynes@931: } nkeynes@931: nkeynes@931: struct mem_region_fn p4_region_storequeue = { nkeynes@931: p4_storequeue_read_long, p4_storequeue_write_long, nkeynes@931: p4_storequeue_read_long, p4_storequeue_write_long, nkeynes@931: p4_storequeue_read_long, p4_storequeue_write_long, nkeynes@931: unmapped_read_burst, unmapped_write_burst }; // No burst access. nkeynes@931: nkeynes@931: /* TLB access */ nkeynes@929: struct mem_region_fn p4_region_itlb_addr = { nkeynes@929: mmu_itlb_addr_read, mmu_itlb_addr_write, nkeynes@929: mmu_itlb_addr_read, mmu_itlb_addr_write, nkeynes@929: mmu_itlb_addr_read, mmu_itlb_addr_write, nkeynes@929: unmapped_read_burst, unmapped_write_burst }; nkeynes@929: struct mem_region_fn p4_region_itlb_data = { nkeynes@929: mmu_itlb_data_read, mmu_itlb_data_write, nkeynes@929: mmu_itlb_data_read, mmu_itlb_data_write, nkeynes@929: mmu_itlb_data_read, mmu_itlb_data_write, nkeynes@929: unmapped_read_burst, unmapped_write_burst }; nkeynes@929: struct mem_region_fn p4_region_utlb_addr = { nkeynes@929: mmu_utlb_addr_read, mmu_utlb_addr_write, nkeynes@929: mmu_utlb_addr_read, mmu_utlb_addr_write, nkeynes@929: mmu_utlb_addr_read, mmu_utlb_addr_write, nkeynes@929: unmapped_read_burst, unmapped_write_burst }; nkeynes@929: struct mem_region_fn p4_region_utlb_data = { nkeynes@929: mmu_utlb_data_read, mmu_utlb_data_write, nkeynes@929: mmu_utlb_data_read, mmu_utlb_data_write, nkeynes@929: mmu_utlb_data_read, mmu_utlb_data_write, nkeynes@929: unmapped_read_burst, unmapped_write_burst }; nkeynes@929: nkeynes@931: /********************** Initialization *************************/ nkeynes@931: nkeynes@931: mem_region_fn_t *sh4_address_space; nkeynes@931: nkeynes@931: static void sh4_register_mem_region( uint32_t start, uint32_t end, mem_region_fn_t fn ) nkeynes@931: { nkeynes@931: int count = (end - start) >> 12; nkeynes@931: mem_region_fn_t *ptr = &sh4_address_space[start>>12]; nkeynes@931: while( count-- > 0 ) { nkeynes@931: *ptr++ = fn; nkeynes@931: } nkeynes@931: } nkeynes@931: nkeynes@931: static gboolean sh4_ext_page_remapped( sh4addr_t page, mem_region_fn_t fn, void *user_data ) nkeynes@931: { nkeynes@931: int i; nkeynes@931: for( i=0; i<= 0xC0000000; i+= 0x20000000 ) { nkeynes@931: sh4_address_space[(page|i)>>12] = fn; nkeynes@931: } nkeynes@931: } nkeynes@931: nkeynes@931: nkeynes@931: void sh4_mem_init() nkeynes@931: { nkeynes@931: int i; nkeynes@931: mem_region_fn_t *ptr; nkeynes@931: sh4_address_space = mem_alloc_pages( sizeof(mem_region_fn_t) * 256 ); nkeynes@931: for( i=0, ptr = sh4_address_space; i<7; i++, ptr += LXDREAM_PAGE_TABLE_ENTRIES ) { nkeynes@931: memcpy( ptr, ext_address_space, sizeof(mem_region_fn_t) * LXDREAM_PAGE_TABLE_ENTRIES ); nkeynes@931: } nkeynes@931: nkeynes@931: /* Setup main P4 regions */ nkeynes@931: sh4_register_mem_region( 0xE0000000, 0xE4000000, &p4_region_storequeue ); nkeynes@931: sh4_register_mem_region( 0xE4000000, 0xF0000000, &mem_region_unmapped ); nkeynes@931: sh4_register_mem_region( 0xF0000000, 0xF1000000, &p4_region_icache_addr ); nkeynes@931: sh4_register_mem_region( 0xF1000000, 0xF2000000, &p4_region_icache_data ); nkeynes@931: sh4_register_mem_region( 0xF2000000, 0xF3000000, &p4_region_itlb_addr ); nkeynes@931: sh4_register_mem_region( 0xF3000000, 0xF4000000, &p4_region_itlb_data ); nkeynes@931: sh4_register_mem_region( 0xF4000000, 0xF5000000, &p4_region_ocache_addr ); nkeynes@931: sh4_register_mem_region( 0xF5000000, 0xF6000000, &p4_region_ocache_data ); nkeynes@931: sh4_register_mem_region( 0xF6000000, 0xF7000000, &p4_region_utlb_addr ); nkeynes@931: sh4_register_mem_region( 0xF7000000, 0xF8000000, &p4_region_utlb_data ); nkeynes@931: sh4_register_mem_region( 0xF8000000, 0x00000000, &mem_region_unmapped ); nkeynes@931: nkeynes@931: /* Setup P4 control region */ nkeynes@931: sh4_register_mem_region( 0xFF000000, 0xFF001000, &mmio_region_MMU.fn ); nkeynes@931: sh4_register_mem_region( 0xFF100000, 0xFF101000, &mmio_region_PMM.fn ); nkeynes@931: sh4_register_mem_region( 0xFF200000, 0xFF201000, &mmio_region_UBC.fn ); nkeynes@931: sh4_register_mem_region( 0xFF800000, 0xFF801000, &mmio_region_BSC.fn ); nkeynes@931: sh4_register_mem_region( 0xFF900000, 0xFFA00000, &mem_region_unmapped ); // SDMR2 + SDMR3 nkeynes@931: sh4_register_mem_region( 0xFFA00000, 0xFFA01000, &mmio_region_DMAC.fn ); nkeynes@931: sh4_register_mem_region( 0xFFC00000, 0xFFC01000, &mmio_region_CPG.fn ); nkeynes@931: sh4_register_mem_region( 0xFFC80000, 0xFFC81000, &mmio_region_RTC.fn ); nkeynes@931: sh4_register_mem_region( 0xFFD00000, 0xFFD01000, &mmio_region_INTC.fn ); nkeynes@931: sh4_register_mem_region( 0xFFD80000, 0xFFD81000, &mmio_region_TMU.fn ); nkeynes@931: sh4_register_mem_region( 0xFFE00000, 0xFFE01000, &mmio_region_SCI.fn ); nkeynes@931: sh4_register_mem_region( 0xFFE80000, 0xFFE81000, &mmio_region_SCIF.fn ); nkeynes@931: sh4_register_mem_region( 0xFFF00000, 0xFFF01000, &mem_region_unmapped ); // H-UDI nkeynes@931: nkeynes@931: register_mem_page_remapped_hook( sh4_ext_page_remapped, NULL ); nkeynes@931: } nkeynes@931: nkeynes@931: /************** Access methods ***************/ nkeynes@929: #ifdef HAVE_FRAME_ADDRESS nkeynes@929: #define RETURN_VIA(exc) do{ *(((void **)__builtin_frame_address(0))+1) = exc; return; } while(0) nkeynes@10: #else nkeynes@929: #define RETURN_VIA(exc) return NULL nkeynes@10: #endif nkeynes@10: nkeynes@931: nkeynes@931: int32_t FASTCALL sh4_read_long( sh4addr_t addr ) nkeynes@10: { nkeynes@931: return sh4_address_space[addr>>12]->read_long(addr); nkeynes@10: } nkeynes@10: nkeynes@931: int32_t FASTCALL sh4_read_word( sh4addr_t addr ) nkeynes@10: { nkeynes@931: return sh4_address_space[addr>>12]->read_word(addr); nkeynes@10: } nkeynes@10: nkeynes@931: int32_t FASTCALL sh4_read_byte( sh4addr_t addr ) nkeynes@10: { nkeynes@931: return sh4_address_space[addr>>12]->read_byte(addr); nkeynes@931: } nkeynes@931: nkeynes@931: void FASTCALL sh4_write_long( sh4addr_t addr, uint32_t val ) nkeynes@931: { nkeynes@931: sh4_address_space[addr>>12]->write_long(addr, val); nkeynes@931: } nkeynes@931: nkeynes@931: void FASTCALL sh4_write_word( sh4addr_t addr, uint32_t val ) nkeynes@931: { nkeynes@931: sh4_address_space[addr>>12]->write_word(addr,val); nkeynes@931: } nkeynes@931: nkeynes@931: void FASTCALL sh4_write_byte( sh4addr_t addr, uint32_t val ) nkeynes@931: { nkeynes@931: sh4_address_space[addr>>12]->write_byte(addr, val); nkeynes@10: } nkeynes@10: nkeynes@10: /* FIXME: Handle all the many special cases when the range doesn't fall cleanly nkeynes@400: * into the same memory block nkeynes@10: */ nkeynes@912: void mem_copy_from_sh4( sh4ptr_t dest, sh4addr_t srcaddr, size_t count ) { nkeynes@103: if( srcaddr >= 0x04000000 && srcaddr < 0x05000000 ) { nkeynes@736: pvr2_vram64_read( dest, srcaddr, count ); nkeynes@103: } else { nkeynes@736: sh4ptr_t src = mem_get_region(srcaddr); nkeynes@736: if( src == NULL ) { nkeynes@736: WARN( "Attempted block read from unknown address %08X", srcaddr ); nkeynes@736: } else { nkeynes@736: memcpy( dest, src, count ); nkeynes@736: } nkeynes@103: } nkeynes@10: } nkeynes@10: nkeynes@912: void mem_copy_to_sh4( sh4addr_t destaddr, sh4ptr_t src, size_t count ) { nkeynes@325: if( destaddr >= 0x10000000 && destaddr < 0x14000000 ) { nkeynes@736: pvr2_dma_write( destaddr, src, count ); nkeynes@736: return; nkeynes@325: } else if( (destaddr & 0x1F800000) == 0x05000000 ) { nkeynes@736: pvr2_render_buffer_invalidate( destaddr, TRUE ); nkeynes@325: } else if( (destaddr & 0x1F800000) == 0x04000000 ) { nkeynes@736: pvr2_vram64_write( destaddr, src, count ); nkeynes@736: return; nkeynes@325: } nkeynes@502: sh4ptr_t dest = mem_get_region(destaddr); nkeynes@325: if( dest == NULL ) nkeynes@736: WARN( "Attempted block write to unknown address %08X", destaddr ); nkeynes@325: else { nkeynes@736: xlat_invalidate_block( destaddr, count ); nkeynes@736: memcpy( dest, src, count ); nkeynes@90: } nkeynes@10: }