/* This file is part of jpcsp. Jpcsp is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Jpcsp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Jpcsp. If not, see <http://www.gnu.org/licenses/>. */ package jpcsp.memory; import static jpcsp.MemoryMap.SIZE_RAM; import static jpcsp.MemoryMap.SIZE_SCRATCHPAD; import static jpcsp.MemoryMap.SIZE_VRAM; import static jpcsp.MemoryMap.START_RAM; import static jpcsp.MemoryMap.START_SCRATCHPAD; import static jpcsp.MemoryMap.START_VRAM; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import jpcsp.Emulator; import jpcsp.Memory; import jpcsp.MemoryMap; import jpcsp.HLE.Modules; public class StandardMemory extends Memory { private static final int PAGE_COUNT = 0x00100000; private static final int PAGE_MASK = 0x00000FFF; private static final int PAGE_SHIFT = 12; private static final int INDEX_SCRATCHPAD = 0; private static final int INDEX_VRAM = SIZE_SCRATCHPAD >>> PAGE_SHIFT; private static final int INDEX_RAM = INDEX_VRAM + (SIZE_VRAM >>> PAGE_SHIFT); private static int SIZE_ALLMEM; private byte[] all; // all psp memory is held in here private static int[] map; // hold map of memory private ByteBuffer buf; // for easier memory reads/writes private ByteBuffer scratchpad; private ByteBuffer videoram; private ByteBuffer mainmemory; @Override public boolean allocate() { // This value can change depending on the PSP memory configuration (32MB/64MB) SIZE_ALLMEM = SIZE_SCRATCHPAD + SIZE_VRAM + SIZE_RAM; // Free previously allocated memory all = null; map = null; try { all = new byte[SIZE_ALLMEM]; map = new int[PAGE_COUNT]; buf = ByteBuffer.wrap(all); buf.order(ByteOrder.LITTLE_ENDIAN); scratchpad = ByteBuffer.wrap( all, 0, SIZE_SCRATCHPAD).slice(); scratchpad.order(ByteOrder.LITTLE_ENDIAN); videoram = ByteBuffer.wrap( all, SIZE_SCRATCHPAD, SIZE_VRAM).slice(); videoram.order(ByteOrder.LITTLE_ENDIAN); mainmemory = ByteBuffer.wrap( all, SIZE_SCRATCHPAD + SIZE_VRAM, SIZE_RAM).slice(); mainmemory.order(ByteOrder.LITTLE_ENDIAN); buildMap(); } catch (OutOfMemoryError e) { // Not enough memory provided for this VM, cannot use StandardMemory model Memory.log.error("Cannot allocate StandardMemory: add the option '-Xmx64m' to the Java Virtual Machine startup command to improve Performance"); return false; } return super.allocate(); } @Override public void Initialise() { Arrays.fill(all, (byte)0); } public StandardMemory() { } private void buildMap() { int i; int page; Arrays.fill(map, -1); page = START_SCRATCHPAD >>> PAGE_SHIFT; for (i = 0; i < (SIZE_SCRATCHPAD >>> PAGE_SHIFT); ++i) { map[0x00000 + page + i] = (INDEX_SCRATCHPAD + i) << PAGE_SHIFT; map[0x40000 + page + i] = (INDEX_SCRATCHPAD + i) << PAGE_SHIFT; map[0x80000 + page + i] = (INDEX_SCRATCHPAD + i) << PAGE_SHIFT; } page = START_VRAM >>> PAGE_SHIFT; for (i = 0; i < (SIZE_VRAM >>> PAGE_SHIFT); ++i) { map[0x00000 + page + i] = (INDEX_VRAM + i) << PAGE_SHIFT; map[0x40000 + page + i] = (INDEX_VRAM + i) << PAGE_SHIFT; map[0x80000 + page + i] = (INDEX_VRAM + i) << PAGE_SHIFT; // Test on a PSP: 0x4200000 is equivalent to 0x4000000 map[0x00000 + 0x200 + page + i] = (INDEX_VRAM + i) << PAGE_SHIFT; map[0x40000 + 0x200 + page + i] = (INDEX_VRAM + i) << PAGE_SHIFT; map[0x80000 + 0x200 + page + i] = (INDEX_VRAM + i) << PAGE_SHIFT; } page = START_RAM >>> PAGE_SHIFT; for (i = 0; i < (SIZE_RAM >>> PAGE_SHIFT); ++i) { map[0x00000 + page + i] = (INDEX_RAM + i) << PAGE_SHIFT; map[0x40000 + page + i] = (INDEX_RAM + i) << PAGE_SHIFT; map[0x80000 + page + i] = (INDEX_RAM + i) << PAGE_SHIFT; } } private static int indexFromAddr(int address) throws Exception { int index = map[address >>> PAGE_SHIFT]; if (index == -1) { throw new Exception( "Invalid memory address : " + Integer.toHexString(address) + " PC=" + Integer.toHexString(Emulator.getProcessor().cpu.pc)); } return index; } @Override public int read8(int address) { try { int page = indexFromAddr(address); return buf.get(page + (address & PAGE_MASK)) & 0xFF; } catch (Exception e) { Memory.log.error("read8 - " + e.getMessage()); Emulator.PauseEmuWithStatus(Emulator.EMU_STATUS_MEM_READ); return 0; } } @Override public int read16(int address) { try { int page = indexFromAddr(address); return buf.getShort(page + (address & PAGE_MASK)) & 0xFFFF; } catch (Exception e) { Memory.log.error("read16 - " + e.getMessage()); Emulator.PauseEmuWithStatus(Emulator.EMU_STATUS_MEM_READ); return 0; } } @Override public int read32(int address) { try { int page = indexFromAddr(address); return buf.getInt(page + (address & PAGE_MASK)); } catch (Exception e) { if (read32AllowedInvalidAddress(address)) { return 0; } Memory.log.error("read32 - " + e.getMessage()); Emulator.PauseEmuWithStatus(Emulator.EMU_STATUS_MEM_READ); return 0; } } @Override public long read64(int address) { try { int page = indexFromAddr(address); return buf.getLong(page + (address & PAGE_MASK)); } catch (Exception e) { Memory.log.error("read64 - " + e.getMessage()); Emulator.PauseEmuWithStatus(Emulator.EMU_STATUS_MEM_READ); return 0; } } @Override public void write8(int address, byte data) { try { int page = indexFromAddr(address); buf.put(page + (address & PAGE_MASK), data); Modules.sceDisplayModule.write8(address & addressMask); } catch (Exception e) { Memory.log.error("write8 - " + e.getMessage()); Emulator.PauseEmuWithStatus(Emulator.EMU_STATUS_MEM_WRITE); } } @Override public void write16(int address, short data) { try { int page = indexFromAddr(address); buf.putShort(page + (address & PAGE_MASK), data); Modules.sceDisplayModule.write16(address & addressMask); } catch (Exception e) { Memory.log.error("write16 - " + e.getMessage()); Emulator.PauseEmuWithStatus(Emulator.EMU_STATUS_MEM_WRITE); } } @Override public void write32(int address, int data) { try { int page = indexFromAddr(address); buf.putInt(page + (address & PAGE_MASK), data); Modules.sceDisplayModule.write32(address & addressMask); } catch (Exception e) { Memory.log.error("write32 - " + e.getMessage()); Emulator.PauseEmuWithStatus(Emulator.EMU_STATUS_MEM_WRITE); } } @Override public void write64(int address, long data) { try { int page = indexFromAddr(address); buf.putLong(page + (address & PAGE_MASK), data); //Modules.sceDisplayModule.write64(address, data); } catch (Exception e) { Memory.log.error("write64 - " + e.getMessage()); Emulator.PauseEmuWithStatus(Emulator.EMU_STATUS_MEM_WRITE); } } @Override public ByteBuffer getMainMemoryByteBuffer() { return mainmemory; } @Override public ByteBuffer getBuffer(int address, int length) { address = normalizeAddress(address); int endAddress = address + length - 1; if (address >= MemoryMap.START_RAM && endAddress <= MemoryMap.END_RAM) { return ByteBuffer.wrap(mainmemory.array(), mainmemory.arrayOffset() + address - MemoryMap.START_RAM, length).slice().order(ByteOrder.LITTLE_ENDIAN); } else if (address >= MemoryMap.START_VRAM && endAddress <= MemoryMap.END_VRAM) { return ByteBuffer.wrap(videoram.array(), videoram.arrayOffset() + address - MemoryMap.START_VRAM, length).slice().order(ByteOrder.LITTLE_ENDIAN); } else if (address >= MemoryMap.START_SCRATCHPAD && endAddress <= MemoryMap.END_SCRATCHPAD) { return ByteBuffer.wrap(scratchpad.array(), scratchpad.arrayOffset() + address - MemoryMap.START_SCRATCHPAD, length).slice().order(ByteOrder.LITTLE_ENDIAN); } return null; } @Override public void memset(int address, byte data, int length) { ByteBuffer buffer = getBuffer(address, length); Arrays.fill(buffer.array(), buffer.arrayOffset(), buffer.arrayOffset() + length, data); } @Override public void copyToMemory(int address, ByteBuffer source, int length) { byte[] data = new byte[length]; source.get(data); ByteBuffer destination = getBuffer(address, length); destination.put(data); } @Override protected void memcpy(int destination, int source, int length, boolean checkOverlap) { destination = normalizeAddress(destination); source = normalizeAddress(source); if (checkOverlap || !areOverlapping(destination, source, length)) { // Direct copy if buffers do not overlap. // ByteBuffer operations are handling correctly overlapping buffers. ByteBuffer destinationBuffer = getBuffer(destination, length); ByteBuffer sourceBuffer = getBuffer(source, length); destinationBuffer.put(sourceBuffer); } else { // Buffers are overlapping and we have to copy them as they would not overlap. IMemoryReader sourceReader = MemoryReader.getMemoryReader(source, length, 1); for (int i = 0; i < length; i++) { write8(destination + i, (byte) sourceReader.readNext()); } } } }