/* 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 java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import jpcsp.Memory; import jpcsp.MemoryMap; import jpcsp.HLE.Modules; /** * A Memory implementation using multiple memory areas allocated natively. * Using NativeMemoryUtils to perform the native operations. * * @author gid15 * */ public class SparseNativeMemory extends Memory { private long[] memory; private int memorySize; // We need to have the PSP RAM residing into one single page (for getBuffer). private int pageShift = 26; private int pageSize = 1 << pageShift; private int pageMask = pageSize - 1; @Override public boolean allocate() { NativeMemoryUtils.init(); memorySize = MemoryMap.END_RAM + 1; memory = new long[Math.max((memorySize + pageSize - 1) >> pageShift, 1)]; for (int i = 0; i < memory.length; i++) { memory[i] = NativeMemoryUtils.alloc(pageSize); if (memory[i] == 0) { // Not enough native memory available for (int j = 0; j < i; j++) { NativeMemoryUtils.free(memory[j]); } return false; } } log.info(String.format("Using SparseNativeMemory(littleEndian=%b)", NativeMemoryUtils.isLittleEndian())); return super.allocate(); } @Override public void Initialise() { for (int i = 0; i < memory.length; i++) { NativeMemoryUtils.memset(memory[i], 0, 0, pageSize); } } @Override public int read8(int address) { address &= addressMask; return NativeMemoryUtils.read8(memory[address >> pageShift], address & pageMask); } @Override public int read16(int address) { address &= addressMask; return NativeMemoryUtils.read16(memory[address >> pageShift], address & pageMask); } @Override public int read32(int address) { address &= addressMask; return NativeMemoryUtils.read32(memory[address >> pageShift], address & pageMask); } @Override public void write8(int address, byte data) { address &= addressMask; NativeMemoryUtils.write8(memory[address >> pageShift], address & pageMask, data); Modules.sceDisplayModule.write8(address); } @Override public void write16(int address, short data) { address &= addressMask; NativeMemoryUtils.write16(memory[address >> pageShift], address & pageMask, data); Modules.sceDisplayModule.write16(address); } @Override public void write32(int address, int data) { address &= addressMask; NativeMemoryUtils.write32(memory[address >> pageShift], address & pageMask, data); Modules.sceDisplayModule.write32(address); } @Override public void memset(int address, byte data, int length) { address &= addressMask; while (length > 0) { int pageLength = Math.min(pageSize - (address & pageMask), length); NativeMemoryUtils.memset(memory[address >> pageShift], address & pageMask, data, pageLength); length -= pageLength; address += pageLength; } } @Override public Buffer getMainMemoryByteBuffer() { return null; } @Override public Buffer getBuffer(int address, int length) { address &= addressMask; ByteBuffer buffer = NativeMemoryUtils.getBuffer(memory[address >> pageShift], address & pageMask, length); // Set the correct byte order if (NativeMemoryUtils.isLittleEndian()) { buffer.order(ByteOrder.LITTLE_ENDIAN); } else { buffer.order(ByteOrder.BIG_ENDIAN); } return buffer; } @Override public void copyToMemory(int address, ByteBuffer source, int length) { address &= addressMask; length = Math.min(length, source.capacity()); if (source.isDirect()) { NativeMemoryUtils.copyBufferToMemory(memory[address >> pageShift], address & pageMask, source, source.position(), length); } else { for (; length > 0; address++, length--) { NativeMemoryUtils.write8(memory[address >> pageShift], address & pageMask, source.get()); } } } @Override protected void memcpy(int destination, int source, int length, boolean checkOverlap) { if (length <= 0) { return; } destination &= addressMask; source &= addressMask; Modules.sceDisplayModule.write(destination); if (!checkOverlap || source >= destination || !areOverlapping(destination, source, length)) { while (length > 0) { int pageLengthDestination = Math.min(pageSize - (destination & pageMask), length); int pageLengthSource = Math.min(pageSize - (source & pageMask), length); int pageLength = Math.min(pageLengthDestination, pageLengthSource); NativeMemoryUtils.memcpy(memory[destination >> pageShift], destination & pageMask, memory[source >> pageShift], source & pageMask, pageLength); length -= pageLength; destination += pageLength; source += pageLength; } } else { // Source and destination are overlapping and source < destination, // copy from the tail. for (int i = length - 1; i >= 0; i--) { int b = read8(source + i); write8(destination + i, (byte) b); } } } }