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; } } }