/*
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.IntBuffer;
import jpcsp.Memory;
import jpcsp.MemoryMap;
import jpcsp.Allegrex.compiler.RuntimeContext;
/**
* @author gid15
*
*/
public class MemoryWriter {
private static int getMaxLength(int address) {
int length;
if (address >= MemoryMap.START_RAM && address <= MemoryMap.END_RAM) {
length = MemoryMap.END_RAM - address + 1;
} else if (address >= MemoryMap.START_VRAM && address <= MemoryMap.END_VRAM) {
length = MemoryMap.END_VRAM - address + 1;
} else if (address >= MemoryMap.START_SCRATCHPAD && address <= MemoryMap.END_SCRATCHPAD) {
length = MemoryMap.END_SCRATCHPAD - address + 1;
} else {
length = 0;
}
return length;
}
private static IMemoryWriter getFastMemoryWriter(int address, int step) {
int[] memoryInt = RuntimeContext.getMemoryInt();
switch (step) {
case 1: return new MemoryWriterIntArray8(memoryInt, address);
case 2: return new MemoryWriterIntArray16(memoryInt, address);
case 4: return new MemoryWriterIntArray32(memoryInt, address);
}
// Default (generic) MemoryWriter
return new MemoryWriterGeneric(address, getMaxLength(address), step);
}
/**
* Creates a MemoryWriter to write values from memory.
*
* @param address the address where to start writing.
* When step == 2, the address has to be 16-bit aligned ((address & 1) == 0).
* When step == 4, the address has to be 32-bit aligned ((address & 3) == 0).
* @param length the maximum number of bytes that can be written.
* @param step when step == 1, write 8-bit values
* when step == 2, write 16-bit values
* when step == 4, write 32-bit values
* other value for step are not allowed.
* @return the MemoryWriter
*/
public static IMemoryWriter getMemoryWriter(int address, int length, int step) {
address &= Memory.addressMask;
if (RuntimeContext.hasMemoryInt()) {
return getFastMemoryWriter(address, step);
}
if (!DebuggerMemory.isInstalled()) {
Buffer buffer = Memory.getInstance().getBuffer(address, length);
if (buffer instanceof IntBuffer) {
IntBuffer intBuffer = (IntBuffer) buffer;
switch (step) {
case 1: return new MemoryWriterInt8(intBuffer, address);
case 2: return new MemoryWriterInt16(intBuffer, address);
case 4: return new MemoryWriterInt32(intBuffer, address);
}
} else if (buffer instanceof ByteBuffer) {
ByteBuffer byteBuffer = (ByteBuffer) buffer;
switch (step) {
case 1: return new MemoryWriterByte8(byteBuffer, address);
case 2: return new MemoryWriterByte16(byteBuffer, address);
case 4: return new MemoryWriterByte32(byteBuffer, address);
}
}
}
// Default (generic) MemoryWriter
return new MemoryWriterGeneric(address, length, step);
}
/**
* Creates a MemoryWriter to write values from memory.
*
* @param address the address where to start writing.
* When step == 2, the address has to be 16-bit aligned ((address & 1) == 0).
* When step == 4, the address has to be 32-bit aligned ((address & 3) == 0).
* @param step when step == 1, write 8-bit values
* when step == 2, write 16-bit values
* when step == 4, write 32-bit values
* other value for step are not allowed.
* @return the MemoryWriter
*/
public static IMemoryWriter getMemoryWriter(int address, int step) {
address &= Memory.addressMask;
if (RuntimeContext.hasMemoryInt()) {
return getFastMemoryWriter(address, step);
}
return getMemoryWriter(address, getMaxLength(address), step);
}
private static class MemoryWriterGeneric implements IMemoryWriter {
private Memory mem;
private int address;
private int length;
private int step;
public MemoryWriterGeneric(int address, int length, int step) {
this.address = address;
this.length = length;
this.step = step;
mem = Memory.getInstance();
}
@Override
public void writeNext(int value) {
if (length <= 0) {
return;
}
switch (step) {
case 1: mem.write8(address, (byte) value); break;
case 2: mem.write16(address, (short) value); break;
case 4: mem.write32(address, value); break;
}
address += step;
length -= step;
}
@Override
public void flush() {
}
@Override
public void skip(int n) {
address += n * step;
length -= n * step;
}
@Override
public int getCurrentAddress() {
return address;
}
}
private static class MemoryWriterIntArray8 implements IMemoryWriter {
private int index;
private int offset;
private int value;
private int[] buffer;
private static final int mask[] = { 0, 0x000000FF, 0x0000FFFF, 0x00FFFFFF, 0xFFFFFFFF };
public MemoryWriterIntArray8(int[] buffer, int addr) {
this.buffer = buffer;
offset = addr >> 2;
index = addr & 3;
value = buffer[offset] & mask[index];
}
@Override
public void writeNext(int n) {
n &= 0xFF;
if (index == 4) {
buffer[offset++] = value;
value = n;
index = 1;
} else {
value |= (n << (index << 3));
index++;
}
}
@Override
public void flush() {
if (index > 0) {
buffer[offset] = (buffer[offset] & ~mask[index]) | value;
}
}
@Override
public final void skip(int n) {
if (n > 0) {
flush();
index += n;
offset += index >> 2;
index &= 3;
value = buffer[offset] & mask[index];
}
}
@Override
public int getCurrentAddress() {
return (offset << 2) + index;
}
}
private static class MemoryWriterIntArray16 implements IMemoryWriter {
private int index;
private int offset;
private int value;
private int[] buffer;
public MemoryWriterIntArray16(int[] buffer, int addr) {
this.buffer = buffer;
offset = addr >> 2;
index = (addr >> 1) & 1;
if (index != 0) {
value = buffer[offset] & 0x0000FFFF;
}
}
@Override
public void writeNext(int n) {
if (index == 0) {
value = n & 0xFFFF;
index = 1;
} else {
buffer[offset++] = (n << 16) | value;
index = 0;
}
}
@Override
public void flush() {
if (index != 0) {
buffer[offset] = (buffer[offset] & 0xFFFF0000) | value;
}
}
@Override
public void skip(int n) {
if (n > 0) {
flush();
index += n;
offset += index >> 1;
index &= 1;
if (index != 0) {
value = buffer[offset] & 0x0000FFFF;
}
}
}
@Override
public int getCurrentAddress() {
return (offset << 2) + (index << 1);
}
}
private static class MemoryWriterIntArray32 implements IMemoryWriter {
private int offset;
private int[] buffer;
public MemoryWriterIntArray32(int[] buffer, int addr) {
offset = addr >> 2;
this.buffer = buffer;
}
@Override
public void writeNext(int value) {
buffer[offset++] = value;
}
@Override
public void flush() {
}
@Override
public void skip(int n) {
offset += n;
}
@Override
public int getCurrentAddress() {
return offset << 2;
}
}
private static class MemoryWriterInt8 implements IMemoryWriter {
private int index;
private int value;
private IntBuffer buffer;
private int address;
private static final int mask[] = { 0, 0x000000FF, 0x0000FFFF, 0x00FFFFFF, 0xFFFFFFFF };
public MemoryWriterInt8(IntBuffer buffer, int address) {
this.buffer = buffer;
this.address = address & ~3;
index = address & 3;
if (index > 0 && buffer.capacity() > 0) {
value = buffer.get(buffer.position()) & mask[index];
}
}
@Override
public void writeNext(int n) {
n &= 0xFF;
if (index == 4) {
buffer.put(value);
value = n;
index = 1;
} else {
value |= (n << (index << 3));
index++;
}
}
@Override
public void flush() {
if (index > 0) {
buffer.put((buffer.get(buffer.position()) & ~mask[index]) | value);
}
}
@Override
public void skip(int n) {
throw new UnsupportedOperationException();
}
@Override
public int getCurrentAddress() {
return address + (buffer.position() << 2) + index;
}
}
private static class MemoryWriterInt16 implements IMemoryWriter {
private int index;
private int value;
private IntBuffer buffer;
private int address;
public MemoryWriterInt16(IntBuffer buffer, int address) {
this.buffer = buffer;
this.address = address & ~3;
this.index = (address & 0x02) >> 1;
if (index != 0 && buffer.capacity() > 0) {
value = buffer.get(buffer.position()) & 0x0000FFFF;
}
}
@Override
public void writeNext(int n) {
if (index == 0) {
value = n & 0xFFFF;
index = 1;
} else {
buffer.put((n << 16) | value);
index = 0;
}
}
@Override
public void flush() {
if (index != 0) {
buffer.put((buffer.get(buffer.position()) & 0xFFFF0000) | value);
}
}
@Override
public void skip(int n) {
if (n > 0) {
int bufferSkip = 0;
if (index != 0) {
flush();
bufferSkip++;
n--;
}
bufferSkip += n / 2;
buffer.position(buffer.position() + bufferSkip);
index = n & 1;
if (index != 0) {
value = buffer.get(buffer.position()) & 0x0000FFFF;
}
}
}
@Override
public int getCurrentAddress() {
return address + (buffer.position() << 2) + index;
}
}
private static class MemoryWriterInt32 implements IMemoryWriter {
private IntBuffer buffer;
private int address;
public MemoryWriterInt32(IntBuffer buffer, int address) {
this.buffer = buffer;
this.address = address;
}
@Override
public void writeNext(int value) {
buffer.put(value);
}
@Override
public void flush() {
}
@Override
public void skip(int n) {
if (n > 0) {
buffer.position(buffer.position() + n);
}
}
@Override
public int getCurrentAddress() {
return address + (buffer.position() << 2);
}
}
private static class MemoryWriterByte8 implements IMemoryWriter {
private ByteBuffer buffer;
private int address;
public MemoryWriterByte8(ByteBuffer buffer, int address) {
this.buffer = buffer;
this.address = address;
}
@Override
public void writeNext(int value) {
buffer.put((byte) value);
}
@Override
public void flush() {
}
@Override
public final void skip(int n) {
if (n > 0) {
buffer.position(buffer.position() + n);
}
}
@Override
public int getCurrentAddress() {
return address + buffer.position();
}
}
private static class MemoryWriterByte16 implements IMemoryWriter {
private ByteBuffer buffer;
private int address;
public MemoryWriterByte16(ByteBuffer buffer, int address) {
this.buffer = buffer;
this.address = address;
}
@Override
public void writeNext(int value) {
buffer.putShort((short) value);
}
@Override
public void flush() {
}
@Override
public final void skip(int n) {
if (n > 0) {
buffer.position(buffer.position() + (n << 1));
}
}
@Override
public int getCurrentAddress() {
return address + buffer.position();
}
}
private static class MemoryWriterByte32 implements IMemoryWriter {
private ByteBuffer buffer;
private int address;
public MemoryWriterByte32(ByteBuffer buffer, int address) {
this.buffer = buffer;
this.address = address;
}
@Override
public void writeNext(int value) {
buffer.putInt(value);
}
@Override
public void flush() {
}
@Override
public final void skip(int n) {
if (n > 0) {
buffer.position(buffer.position() + (n << 2));
}
}
@Override
public int getCurrentAddress() {
return address + buffer.position();
}
}
}