pvbemu/src/desktop/vue/JavaVue.java

428 lines
13 KiB
Java

package vue;
// Java imports
import java.util.*;
// Java emulation core implementation
class JavaVue extends Vue {
// Package fields
int breakCode; // Application breakpoint code
int breakType; // Most recent breakpoint scenario
int[] stack; // Breakpoint condition evaluation stack
byte[] wram; // System WRAM
// System components
CPU cpu; // Processor
GamePak pak; // Game pak
VIP vip; // Video unit
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Memory access type sizes
static final int[] TYPE_SIZES = { 1, 1, 2, 2, 4 };
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
JavaVue() {
// Configure instance fields
stack = new int[0];
wram = new byte[0x10000];
// Configure system components
cpu = new CPU (this);
pak = new GamePak(this);
vip = new VIP (this);
// Initialize state
reset();
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Release any used resources
public void dispose() { }; // No action needed
// Process the simulation
public int emulate(int maxCycles) {
// Process up to the given number of cycles
do {
// Determine the number of cycles where no breakpoint will occur
int cycles = maxCycles; // min(maxCycles, nextFrameCycles)
cycles = cpu .until(cycles);
//cycles = pad .until(cycles);
//cycles = link .until(cycles);
//cycles = timer.until(cycles);
cycles = vip .until(cycles);
// Process all system components
breakCode = 0;
cpu .emulate(cycles);
//pad .emulate(cycles);
//link .emulate(cycles);
//timer.emulate(cycles);
vip .emulate(cycles);
//vsu .emulate(cycles);
maxCycles -= cycles;
} while (breakCode == 0 && maxCycles != 0);
// A break condition has occurred
return maxCycles;
}
// Retrieve the most recent applicaiton break code
public int getBreakCode() {
return breakCode;
}
// Retrieve the most recent exception code
public int getExceptionCode() {
return cpu.exception;
}
// Retrieve a register value
public int getRegister(int index, boolean system) {
// Non-indexed registers
if (system) switch (index) {
case Vue.JUMP_FROM: return
cpu.jumpFrom[cpu.psw_np != 0 ? 2 : cpu.psw_ep];
case Vue.JUMP_TO : return
cpu.jumpTo [cpu.psw_np != 0 ? 2 : cpu.psw_ep];
case Vue.PC : return cpu.pc;
}
// Indexed registers
return
index < 0 || index > 31 ? 0 :
system ? cpu.getSystemRegister(index) :
cpu.program[index]
;
}
// Retrieve a copy of the ROM data
public byte[] getROM() {
// No ROM data
if (pak.rom == null)
return null;
// Copy the ROM data
var ret = new byte[pak.rom.length];
System.arraycopy(pak.rom, 0, ret, 0, ret.length);
return ret;
}
// Determine whether the context is native-backed
public boolean isNative() {
return false;
}
// Read a value from the CPU bus
public int read(int address, int type) {
// Error checking
if (type < 0 || type > 4)
return 0;
// Perform the operation
switch (address >> 24 & 7) {
case 0: return vip.read ( address, type);
case 5: return readBuffer(wram , address, type);
case 6: return readBuffer(pak.ram, address, type);
case 7: return readBuffer(pak.rom, address, type);
}
return 0;
}
// Read bytes from the CPU bus
public boolean readBytes(int address, byte[] dest, int offset, int length){
// Error checking
if (
dest == null ||
offset < 0 ||
length < 0 ||
offset + length > dest.length
) return false;
// Perform the operation
while (length > 0) {
int count = Math.min(length, 0x01000000 - (address & 0x00FFFFFF));
switch (address >> 24 & 7) {
case 0: vip.readBytes( address,dest,offset,count); break;
case 5: readBytes(wram ,address,dest,offset,count); break;
case 6: readBytes(pak.ram,address,dest,offset,count); break;
case 7: readBytes(pak.rom,address,dest,offset,count); break;
default: Arrays.fill(dest, offset, offset + count, (byte) 0);
}
address += count;
length -= count;
offset += count;
}
return true;
}
// Initialize all system components
public void reset() {
cpu.reset();
pak.reset();
Arrays.fill(wram, 0, 0x10000, (byte) 0);
}
// Specify a register value
public int setRegister(int index, boolean system, int value) {
// PC
if (index == Vue.PC && system) {
if (cpu.stage == CPU.EXECUTE || cpu.stage == CPU.FETCH) {
cpu.fetch = -1;
cpu.stage = CPU.FETCH;
}
return cpu.pc = value & 0xFFFFFFFE;
}
// Other
return
index < 0 || index > 31 ? 0 :
system ? cpu.setSystemRegister(index, value, true) :
index == 0 ? 0 : (cpu.program[index] = value)
;
}
// Provide new ROM data
public boolean setROM(byte[] data, int offset, int length) {
// Error checking
if (
data == null ||
offset < 0 ||
length < 1024 || length > 0x01000000 ||
(length & length - 1) != 0 ||
offset + length > data.length
) return false;
// Accept the new ROM data
pak.rom = new byte[length];
System.arraycopy(data, offset, pak.rom, 0, length);
return true;
}
// Write a value to the CPU bus
public void write(int address, int type, int value) {
switch (address >> 24 & 7) {
case 0: vip.write ( address, type, value); break;
case 5: writeBuffer(wram , address, type, value); break;
case 6: writeBuffer(pak.ram, address, type, value); break;
case 7: writeBuffer(pak.rom, address, type, value); break;
}
}
// Write bytes to the CPU bus
public boolean writeBytes(int address, byte[] src, int offset, int length){
// Error checking
if (
src == null ||
offset < 0 ||
length < 0 ||
offset + length > src.length
) return false;
// Perform the operation
while (length > 0) {
int count = Math.min(length, 0x01000000 - (address & 0x00FFFFFF));
switch (address >> 24 & 7) {
case 0: vip.writeBytes( address,src,offset,count); break;
case 5: writeBytes(wram ,address,src,offset,count); break;
case 6: writeBytes(pak.ram,address,src,offset,count); break;
case 7: writeBytes(pak.rom,address,src,offset,count); break;
}
address += count;
length -= count;
offset += count;
}
return true;
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Retrieve the current state's breakpoint scenario
int getBreakType() {
return breakType;
}
// Retrieve the current instruction fetch index
int getFetch() {
return cpu.fetch;
}
// Check for breakpoints
boolean onBreakpoint(int breakType) {
int end = 0;
boolean ranged = false;
int start = 0;
this.breakType = breakType;
// Processing for Execute
if (breakType == Breakpoint.EXECUTE) {
ranged = true;
start = cpu.pc;
end = start + cpu.inst.size - 1;
}
// Processing for Read and Write
else if (breakType==Breakpoint.READ || breakType==Breakpoint.WRITE) {
ranged = true;
start = cpu.access.address;
end = start + TYPE_SIZES[cpu.access.type] - 1;
}
// Check all breakpoints
int count = breakpoints.size();
boolean fetch = breakType == Breakpoint.READ && cpu.fetch != -1;
for (int x = 0; breakCode == 0 && x < count; x++) {
var brk = breakpoints.get(x);
if (
brk.appliesTo(breakType, fetch) &&
(!ranged || brk.inRange(start, end)) &&
brk.isTrue(stack, breakType, cpu.inst, cpu.access)
) breakCode = x + 1;
}
return breakCode != 0;
}
// Read a value from a byte buffer
static int readBuffer(byte[] data, int address, int type) {
// Error checking
if (data == null)
return 0;
// Processing by data type
int size = TYPE_SIZES[type];
int value = 0;
address &= ~size + 1 & data.length - 1;
switch (type) {
case Vue.S32: value =
data[address + 3] << 24 |
(data[address + 2] & 0xFF) << 16;
// Fallthrough
case Vue.S16: // Fallthrough
case Vue.U16: value |=
(data[address + 1] & 0xFF) << 8;
// Fallthrough
case Vue.S8 : // Fallthrough
case Vue.U8 : value |=
data[address ] & 0xFF;
}
// Sign-extend the value if appropriate
if ((type & 1) == 0) {
size = 32 - (size << 3);
value = value << size >> size;
}
return value;
}
// Read bytes from a byte buffer
static void readBytes(byte[] data, int address, byte[] dest, int offset,
int length) {
// The source does not exist
if (data == null) {
Arrays.fill(dest, offset, offset + length, (byte) 0);
return;
}
// Transfer bytes from the source as a circular buffer
while (length > 0) {
address &= data.length - 1;
int count = Math.min(length, data.length - address);
System.arraycopy(data, address, dest, offset, count);
address += count;
length -= count;
offset += count;
}
}
// A breakpoint's condition tokens have changed
void updateTokens(Breakpoint brk) {
super.updateTokens(brk);
int depth = 0;
for (var bre : breakpoints)
depth = Math.max(depth, bre.getDepth());
if (depth != stack.length)
stack = new int[depth];
}
// Write a value to a byte buffer
static void writeBuffer(byte[] data, int address, int type, int value) {
// The destination does not exist
if (data == null)
return;
// Processing by data type
int size = TYPE_SIZES[type];
address &= ~size + 1 & data.length - 1;
switch (type) {
case Vue.S32:
data[address + 3] = (byte) (value >> 24);
data[address + 2] = (byte) (value >> 16);
// Fallthrough
case Vue.S16: // Fallthrough
case Vue.U16:
data[address + 1] = (byte) (value >> 8);
// Fallthrough
case Vue.S8 : // Fallthrough
case Vue.U8 : value |=
data[address ] = (byte) value;
}
}
// Write bytes to a byte buffer
static void writeBytes(byte[] data, int address, byte[] src, int offset,
int length) {
// The destination does not exist
if (data == null)
return;
// Transfer bytes to the destination as a circular buffer
while (length > 0) {
address &= data.length - 1;
int count = Math.min(length, data.length - address);
System.arraycopy(src, offset, data, address, count);
address += count;
length -= count;
offset += count;
}
}
}