2020-08-03 02:21:59 +00:00
|
|
|
package vue;
|
|
|
|
|
2020-08-09 23:35:57 +00:00
|
|
|
// Java imports
|
|
|
|
import java.util.*;
|
|
|
|
|
2020-08-03 02:21:59 +00:00
|
|
|
// Java emulation core implementation
|
2020-10-03 19:20:56 +00:00
|
|
|
class JavaVue extends Vue {
|
2020-08-03 02:21:59 +00:00
|
|
|
|
2020-08-09 23:35:57 +00:00
|
|
|
// Package fields
|
2020-10-05 13:10:14 +00:00
|
|
|
int breakCode; // Application break code
|
|
|
|
CPU cpu; // Processor
|
2020-12-17 21:40:37 +00:00
|
|
|
int hook; // Most recent breakpoint hook
|
2020-10-05 13:10:14 +00:00
|
|
|
GamePak pak; // Game pak
|
2020-12-17 21:40:37 +00:00
|
|
|
int[] stack; // Breakpoint evaluation stack
|
2020-10-05 13:10:14 +00:00
|
|
|
byte[] wram; // System WRAM
|
2020-08-04 01:34:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Constants //
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Memory access type sizes
|
2020-08-05 02:17:56 +00:00
|
|
|
static final int[] TYPE_SIZES = { 1, 1, 2, 2, 4 };
|
2020-08-03 02:21:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Constructors //
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Default constructor
|
2020-10-03 19:20:56 +00:00
|
|
|
JavaVue() {
|
2020-12-17 21:40:37 +00:00
|
|
|
cpu = new CPU (this);
|
|
|
|
pak = new GamePak(this);
|
|
|
|
stack = new int[0];
|
|
|
|
wram = new byte[0x10000];
|
2020-08-06 21:37:05 +00:00
|
|
|
reset();
|
2020-08-03 02:21:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Public Methods //
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2020-08-03 18:51:46 +00:00
|
|
|
// Release any used resources
|
|
|
|
public void dispose() { }; // No action needed
|
|
|
|
|
2020-08-09 23:35:57 +00:00
|
|
|
// Process the simulation
|
|
|
|
public int emulate(int maxCycles) {
|
|
|
|
|
|
|
|
// Process up to the given number of cycles
|
|
|
|
do {
|
|
|
|
|
|
|
|
// Determine the number of cycles during which nothing will happen
|
2020-08-11 01:24:00 +00:00
|
|
|
int cycles = -1;
|
2020-12-17 21:40:37 +00:00
|
|
|
cycles = cpu .until(cycles);
|
2020-08-09 23:35:57 +00:00
|
|
|
//cycles = pad .until(cycles);
|
|
|
|
//cycles = link .until(cycles);
|
|
|
|
//cycles = timer.until(cycles);
|
|
|
|
//cycles = vip .until(cycles);
|
|
|
|
//cycles = vsu .until(cycles);
|
2020-08-11 01:24:00 +00:00
|
|
|
|
|
|
|
// Range checking
|
|
|
|
if (cycles == -1) // No activity on any component
|
|
|
|
break;
|
|
|
|
if (maxCycles >= 0) // Restrict to given number of cycles
|
|
|
|
cycles = Math.min(cycles, maxCycles);
|
2020-08-09 23:35:57 +00:00
|
|
|
|
|
|
|
// Process all system components
|
2020-12-17 21:40:37 +00:00
|
|
|
cpu .emulate(cycles);
|
2020-08-09 23:35:57 +00:00
|
|
|
//pad .emulate(cycles);
|
|
|
|
//link .emulate(cycles);
|
|
|
|
//timer.emulate(cycles);
|
|
|
|
//vip .emulate(cycles);
|
|
|
|
//vsu .emulate(cycles);
|
|
|
|
|
|
|
|
// An application break was requested
|
2020-12-17 21:40:37 +00:00
|
|
|
//if (...)
|
|
|
|
// break;
|
2020-08-09 23:35:57 +00:00
|
|
|
|
|
|
|
// Update the number of cycles remaining
|
|
|
|
if (maxCycles >= 0)
|
|
|
|
maxCycles -= cycles;
|
|
|
|
} while (maxCycles != 0);
|
|
|
|
|
|
|
|
// A break condition has occurred
|
2020-08-11 01:24:00 +00:00
|
|
|
return Math.max(0, maxCycles);
|
|
|
|
}
|
|
|
|
|
2020-12-17 21:40:37 +00:00
|
|
|
// Evaluate an expression
|
|
|
|
public Object evaluate(String expression) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retrieve the most recent exception code
|
|
|
|
public int getException() {
|
|
|
|
return cpu.exception.code;
|
2020-08-09 23:35:57 +00:00
|
|
|
}
|
|
|
|
|
2020-08-06 01:40:23 +00:00
|
|
|
// Retrieve a register value
|
|
|
|
public int getRegister(int index, boolean system) {
|
2020-08-07 19:21:03 +00:00
|
|
|
|
|
|
|
// Non-indexed registers
|
|
|
|
if (system) switch (index) {
|
2020-10-03 19:20:56 +00:00
|
|
|
case Vue.JUMP_FROM: return
|
2020-08-08 02:24:09 +00:00
|
|
|
cpu.jumpFrom[cpu.psw_np != 0 ? 2 : cpu.psw_ep];
|
2020-10-03 19:20:56 +00:00
|
|
|
case Vue.JUMP_TO : return
|
2020-08-08 02:24:09 +00:00
|
|
|
cpu.jumpTo [cpu.psw_np != 0 ? 2 : cpu.psw_ep];
|
2020-10-03 19:20:56 +00:00
|
|
|
case Vue.PC : return cpu.pc;
|
2020-08-07 19:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Indexed registers
|
2020-08-06 01:40:23 +00:00
|
|
|
return
|
|
|
|
index < 0 || index > 31 ? 0 :
|
|
|
|
system ? cpu.getSystemRegister(index) :
|
|
|
|
cpu.program[index]
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2020-08-03 02:21:59 +00:00
|
|
|
// Retrieve a copy of the ROM data
|
|
|
|
public byte[] getROM() {
|
2020-08-06 01:40:23 +00:00
|
|
|
|
|
|
|
// No ROM data
|
2020-08-09 23:35:57 +00:00
|
|
|
if (pak.rom == null)
|
2020-08-06 01:40:23 +00:00
|
|
|
return null;
|
|
|
|
|
|
|
|
// Copy the ROM data
|
2020-08-09 23:35:57 +00:00
|
|
|
var ret = new byte[pak.rom.length];
|
|
|
|
System.arraycopy(pak.rom, 0, ret, 0, ret.length);
|
2020-08-03 02:21:59 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determine whether the context is native-backed
|
|
|
|
public boolean isNative() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-08-09 23:35:57 +00:00
|
|
|
// Read a value from the CPU bus
|
|
|
|
public int read(int address, int type) {
|
|
|
|
switch (address >> 24 & 7) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-08-05 02:17:56 +00:00
|
|
|
// Read bytes from the CPU bus
|
2020-08-09 23:35:57 +00:00
|
|
|
public boolean readBytes(int address, byte[] dest, int offset, int length){
|
2020-08-04 01:34:02 +00:00
|
|
|
|
|
|
|
// Error checking
|
2020-08-05 02:17:56 +00:00
|
|
|
if (
|
|
|
|
dest == null ||
|
2020-08-04 01:34:02 +00:00
|
|
|
offset < 0 ||
|
|
|
|
length < 0 ||
|
2020-08-05 02:17:56 +00:00
|
|
|
offset + length > dest.length
|
2020-08-04 01:34:02 +00:00
|
|
|
) return false;
|
|
|
|
|
|
|
|
// Perform the operation
|
2020-08-09 23:35:57 +00:00
|
|
|
while (length > 0) {
|
|
|
|
int count = Math.min(length, 0x01000000 - (address & 0x00FFFFFF));
|
2020-08-05 02:17:56 +00:00
|
|
|
switch (address >> 24 & 7) {
|
2020-08-09 23:35:57 +00:00
|
|
|
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);
|
2020-08-05 02:17:56 +00:00
|
|
|
}
|
2020-08-09 23:35:57 +00:00
|
|
|
address += count;
|
|
|
|
length -= count;
|
|
|
|
offset += count;
|
|
|
|
}
|
2020-08-04 01:34:02 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-08-06 21:37:05 +00:00
|
|
|
// Initialize all system components
|
|
|
|
public void reset() {
|
|
|
|
cpu.reset();
|
2020-08-09 23:35:57 +00:00
|
|
|
pak.reset();
|
|
|
|
Arrays.fill(wram, 0, 0x10000, (byte) 0);
|
2020-08-06 21:37:05 +00:00
|
|
|
}
|
|
|
|
|
2020-08-06 01:40:23 +00:00
|
|
|
// Specify a register value
|
|
|
|
public int setRegister(int index, boolean system, int value) {
|
|
|
|
return
|
2020-10-03 19:20:56 +00:00
|
|
|
index == Vue.PC && system ? cpu.pc = value & 0xFFFFFFFE :
|
2020-08-06 01:40:23 +00:00
|
|
|
index < 0 || index > 31 ? 0 :
|
|
|
|
system ? cpu.setSystemRegister(index, value, true) :
|
2020-08-06 21:37:05 +00:00
|
|
|
index == 0 ? 0 : (cpu.program[index] = value)
|
2020-08-06 01:40:23 +00:00
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2020-08-03 02:21:59 +00:00
|
|
|
// Provide new ROM data
|
|
|
|
public boolean setROM(byte[] data, int offset, int length) {
|
|
|
|
|
|
|
|
// Error checking
|
2020-08-04 01:34:02 +00:00
|
|
|
if (
|
|
|
|
data == null ||
|
|
|
|
offset < 0 ||
|
|
|
|
length < 1024 || length > 0x01000000 ||
|
|
|
|
(length & length - 1) != 0 ||
|
|
|
|
offset + length > data.length
|
|
|
|
) return false;
|
2020-08-03 02:21:59 +00:00
|
|
|
|
|
|
|
// Accept the new ROM data
|
2020-08-09 23:35:57 +00:00
|
|
|
pak.rom = new byte[length];
|
|
|
|
System.arraycopy(data, offset, pak.rom, 0, length);
|
2020-08-03 02:21:59 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-08-09 23:35:57 +00:00
|
|
|
// Write a value to the CPU bus
|
|
|
|
public void write(int address, int type, int value) {
|
|
|
|
switch (address >> 24 & 7) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-05 02:17:56 +00:00
|
|
|
// Write bytes to the CPU bus
|
2020-08-09 23:35:57 +00:00
|
|
|
public boolean writeBytes(int address, byte[] src, int offset, int length){
|
2020-08-05 02:17:56 +00:00
|
|
|
|
|
|
|
// Error checking
|
|
|
|
if (
|
|
|
|
src == null ||
|
|
|
|
offset < 0 ||
|
|
|
|
length < 0 ||
|
|
|
|
offset + length > src.length
|
|
|
|
) return false;
|
|
|
|
|
|
|
|
// Perform the operation
|
2020-08-09 23:35:57 +00:00
|
|
|
while (length > 0) {
|
|
|
|
int count = Math.min(length, 0x01000000 - (address & 0x00FFFFFF));
|
2020-08-05 02:17:56 +00:00
|
|
|
switch (address >> 24 & 7) {
|
2020-08-09 23:35:57 +00:00
|
|
|
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;
|
2020-08-05 02:17:56 +00:00
|
|
|
}
|
2020-08-09 23:35:57 +00:00
|
|
|
address += count;
|
|
|
|
length -= count;
|
|
|
|
offset += count;
|
|
|
|
}
|
2020-08-05 02:17:56 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-08-09 23:35:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Package Methods //
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2020-10-14 01:15:27 +00:00
|
|
|
// Evaluate the condition in a breakpoint
|
|
|
|
boolean evaluate(Breakpoint brk) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-10-05 13:10:14 +00:00
|
|
|
// Exception break handler
|
2020-12-17 21:40:37 +00:00
|
|
|
boolean onException() {
|
|
|
|
return onHook(Breakpoint.EXCEPTION);
|
2020-10-05 13:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Execute break handler
|
2020-12-17 21:40:37 +00:00
|
|
|
boolean onExecute() {
|
|
|
|
return onHook(Breakpoint.EXECUTE);
|
2020-10-05 13:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Read break handler
|
2020-12-17 21:40:37 +00:00
|
|
|
boolean onRead() {
|
|
|
|
return onHook(Breakpoint.READ);
|
2020-10-05 13:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Write break handler
|
2020-12-17 21:40:37 +00:00
|
|
|
boolean onWrite() {
|
|
|
|
return onHook(Breakpoint.WRITE);
|
2020-10-05 13:10:14 +00:00
|
|
|
}
|
|
|
|
|
2020-08-09 23:35:57 +00:00
|
|
|
// 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) {
|
2020-10-03 19:20:56 +00:00
|
|
|
case Vue.S32: value =
|
2020-08-09 23:35:57 +00:00
|
|
|
data[address + 3] << 24 |
|
|
|
|
(data[address + 2] & 0xFF) << 16;
|
|
|
|
// Fallthrough
|
2020-10-03 19:20:56 +00:00
|
|
|
case Vue.S16: // Fallthrough
|
|
|
|
case Vue.U16: value |=
|
2020-08-09 23:35:57 +00:00
|
|
|
(data[address + 1] & 0xFF) << 8;
|
|
|
|
// Fallthrough
|
2020-10-03 19:20:56 +00:00
|
|
|
case Vue.S8 : // Fallthrough
|
|
|
|
case Vue.U8 : value |=
|
2020-08-09 23:35:57 +00:00
|
|
|
data[address ] & 0xFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sign-extend the value if appropriate
|
2020-08-11 18:38:31 +00:00
|
|
|
if ((type & 1) == 0) {
|
2020-08-09 23:35:57 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write a value to a byte buffer
|
|
|
|
static void writeBuffer(byte[] data, int address, int type, int value) {
|
|
|
|
|
2020-08-11 01:24:00 +00:00
|
|
|
// The destination does not exist
|
2020-08-09 23:35:57 +00:00
|
|
|
if (data == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Processing by data type
|
|
|
|
int size = TYPE_SIZES[type];
|
|
|
|
address &= ~size + 1 & data.length - 1;
|
|
|
|
switch (type) {
|
2020-10-03 19:20:56 +00:00
|
|
|
case Vue.S32:
|
2020-08-09 23:35:57 +00:00
|
|
|
data[address + 3] = (byte) (value >> 24);
|
|
|
|
data[address + 2] = (byte) (value >> 16);
|
|
|
|
// Fallthrough
|
2020-10-03 19:20:56 +00:00
|
|
|
case Vue.S16: // Fallthrough
|
|
|
|
case Vue.U16:
|
2020-08-09 23:35:57 +00:00
|
|
|
data[address + 1] = (byte) (value >> 8);
|
|
|
|
// Fallthrough
|
2020-10-03 19:20:56 +00:00
|
|
|
case Vue.S8 : // Fallthrough
|
|
|
|
case Vue.U8 : value |=
|
2020-08-09 23:35:57 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-12-17 21:40:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Private Methods //
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Check breakpoints during a hooked event
|
|
|
|
private boolean onHook(int hook) {
|
|
|
|
this.hook = hook;
|
|
|
|
for (var brk : breakpoints)
|
|
|
|
if (evaluate(brk))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-08-03 02:21:59 +00:00
|
|
|
}
|