428 lines
13 KiB
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;
|
|
}
|
|
|
|
}
|
|
|
|
}
|