diff --git a/public/a.html b/public/a.html index 8ed0649c..47dcd981 100644 --- a/public/a.html +++ b/public/a.html @@ -49,49 +49,26 @@
-
+

Alloy Proxy

A fast, lightweight proxy developed by Titanium Network.

-
-
-
- -
+
+
+
diff --git a/public/assets/js/magic.js b/public/assets/js/magic.js new file mode 100644 index 00000000..bd9f9e63 --- /dev/null +++ b/public/assets/js/magic.js @@ -0,0 +1 @@ +//window.history.replaceState('', '', '/'); \ No newline at end of file diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 00000000..9db7a791 --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,82 @@ +.gembed { + width: 100%; + height: 100%; + top: 0; + bottom: 0; + left: 0; + right: 0; + position: fixed; +} + +.fbody { + background-color: black; + color: white; + font-family: sans-serif; +} + +.glink { + display: block; + text-decoration: none; + color: black; + font: 18px; + border: 1px solid #ddd; + margin-top: -1px; + background-color: #f6f6f6; + padding: 12px; + transition: 0.2s ease; +} + +.glink:hover { + background-color: #eee; + text-shadow: 0 0 5px rgba(0,0,0,0.4); +} + +#gsearchbar { + outline: none; + width: 374px; + top: 12px; + font-size: 16px; + border: 1px solid rgba(0,0,0,0.3); + padding: 12px; + margin-bottom: 12px; + transition: 0.2s ease; +} + +#gsearchbar:focus { + background-color: #eee; + box-shadow: inset 0 0 5px 1px rgba(0,0,0,0.4); +} + +.container { + padding: 20px; +} + +.box { + margin: auto; + background-color: white; + padding: 10px 10px 15px 10px; + width: 400px; + height: 570px; +} + +#glist { + overflow-x: hidden; + overflow-y: scroll; + width: 400px; + height: 520px; + padding: 0; + text-align: center; +} + +#glist h2 { + display: block; + text-decoration: none; + color: white; + font: 18px; + border: 1px solid #060606; + margin: -1px 0 0 0; + background-color: #060606; + padding: 12px; + font-weight: bold; + user-select: none; +} \ No newline at end of file diff --git a/public/gfiles/f.html b/public/gfiles/f.html new file mode 100644 index 00000000..cff060c7 --- /dev/null +++ b/public/gfiles/f.html @@ -0,0 +1 @@ +Flash Game \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/CPU.js b/public/gfiles/gba/IodineGBA/core/CPU.js new file mode 100644 index 00000000..c893b945 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/CPU.js @@ -0,0 +1,653 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceCPU(IOCore) { + this.IOCore = IOCore; +} +GameBoyAdvanceCPU.prototype.initialize = function () { + this.memory = this.IOCore.memory; + this.wait = this.IOCore.wait; + this.mul64ResultHigh = 0; //Scratch MUL64. + this.mul64ResultLow = 0; //Scratch MUL64. + this.initializeRegisters(); + this.ARM = new ARMInstructionSet(this); + this.THUMB = new THUMBInstructionSet(this); + //this.swi = new GameBoyAdvanceSWI(this); + this.IOCore.assignInstructionCoreReferences(this.ARM, this.THUMB); +} +GameBoyAdvanceCPU.prototype.initializeRegisters = function () { + /* + R0-R7 Are known as the low registers. + R8-R12 Are the high registers. + R13 is the stack pointer. + R14 is the link register. + R15 is the program counter. + CPSR is the program status register. + SPSR is the saved program status register. + */ + //Normal R0-R15 Registers: + this.registers = getInt32Array(16); + //Used to copy back the R8-R14 state for normal operations: + this.registersUSR = getInt32Array(7); + //Fast IRQ mode registers (R8-R14): + this.registersFIQ = getInt32Array(7); + //Supervisor mode registers (R13-R14): + this.registersSVC = getInt32Array(2); + //Abort mode registers (R13-R14): + this.registersABT = getInt32Array(2); + //IRQ mode registers (R13-R14): + this.registersIRQ = getInt32Array(2); + //Undefined mode registers (R13-R14): + this.registersUND = getInt32Array(2); + //CPSR Register: + this.branchFlags = ARMCPSRAttributeTable(); + this.modeFlags = 0xD3; + //Banked SPSR Registers: + this.SPSR = getUint16Array(5); + this.SPSR[0] = 0xD3; //FIQ + this.SPSR[1] = 0xD3; //IRQ + this.SPSR[2] = 0xD3; //Supervisor + this.SPSR[3] = 0xD3; //Abort + this.SPSR[4] = 0xD3; //Undefined + this.triggeredIRQ = 0; //Pending IRQ found. + //Pre-initialize stack pointers if no BIOS loaded: + if (this.IOCore.SKIPBoot) { + this.HLEReset(); + } + //Start in fully bubbled pipeline mode: + this.IOCore.flagBubble(); +} +GameBoyAdvanceCPU.prototype.HLEReset = function () { + this.registersSVC[0] = 0x3007FE0; + this.registersIRQ[0] = 0x3007FA0; + this.registers[13] = 0x3007F00; + this.registers[15] = 0x8000000; + this.modeFlags = this.modeFlags | 0x1f; +} +GameBoyAdvanceCPU.prototype.branch = function (branchTo) { + branchTo = branchTo | 0; + //if ((branchTo | 0) > 0x3FFF || this.IOCore.BIOSFound) { + //Branch to new address: + this.registers[15] = branchTo | 0; + //Mark pipeline as invalid: + this.IOCore.flagBubble(); + //Next PC fetch has to update the address bus: + this.wait.NonSequentialBroadcastClear(); + /*} + else { + //We're branching into BIOS, handle specially: + if ((branchTo | 0) == 0x130) { + //IRQ mode exit handling: + //ROM IRQ handling returns back from its own subroutine back to BIOS at this address. + this.HLEIRQExit(); + } + else { + //Reset to start of ROM if no BIOS ROM found: + this.HLEReset(); + } + }*/ +} +GameBoyAdvanceCPU.prototype.triggerIRQ = function (didFire) { + this.triggeredIRQ = didFire | 0; + this.assertIRQ(); +} +GameBoyAdvanceCPU.prototype.assertIRQ = function () { + if ((this.triggeredIRQ | 0) != 0 && (this.modeFlags & 0x80) == 0) { + this.IOCore.flagIRQ(); + } +} +GameBoyAdvanceCPU.prototype.getCurrentFetchValue = function () { + if ((this.modeFlags & 0x20) != 0) { + return this.THUMB.getCurrentFetchValue() | 0; + } + else { + return this.ARM.getCurrentFetchValue() | 0; + } +} +GameBoyAdvanceCPU.prototype.enterARM = function () { + this.modeFlags = this.modeFlags & 0xdf; + this.THUMBBitModify(false); +} +GameBoyAdvanceCPU.prototype.enterTHUMB = function () { + this.modeFlags = this.modeFlags | 0x20; + this.THUMBBitModify(true); +} +GameBoyAdvanceCPU.prototype.getLR = function () { + //Get the previous instruction address: + if ((this.modeFlags & 0x20) != 0) { + return this.THUMB.getLR() | 0; + } + else { + return this.ARM.getLR() | 0; + } +} +GameBoyAdvanceCPU.prototype.THUMBBitModify = function (isThumb) { + if (isThumb) { + this.IOCore.flagTHUMB(); + } + else { + this.IOCore.deflagTHUMB(); + } +} +GameBoyAdvanceCPU.prototype.IRQinARM = function () { + //Mode bits are set to IRQ: + this.switchMode(0x12); + //Save link register: + this.registers[14] = this.ARM.getIRQLR() | 0; + //Disable IRQ: + this.modeFlags = this.modeFlags | 0x80; + //if (this.IOCore.BIOSFound) { + //IRQ exception vector: + this.branch(0x18); + /*} + else { + //HLE the IRQ entrance: + this.HLEIRQEnter(); + }*/ + //Deflag IRQ from state: + this.IOCore.deflagIRQ(); +} +GameBoyAdvanceCPU.prototype.IRQinTHUMB = function () { + //Mode bits are set to IRQ: + this.switchMode(0x12); + //Save link register: + this.registers[14] = this.THUMB.getIRQLR() | 0; + //Disable IRQ: + this.modeFlags = this.modeFlags | 0x80; + //Exception always enter ARM mode: + this.enterARM(); + //if (this.IOCore.BIOSFound) { + //IRQ exception vector: + this.branch(0x18); + /*} + else { + //HLE the IRQ entrance: + this.HLEIRQEnter(); + }*/ + //Deflag IRQ from state: + this.IOCore.deflagIRQ(); +} +GameBoyAdvanceCPU.prototype.HLEIRQEnter = function () { + //Get the base address: + var currentAddress = this.registers[0xD] | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + for (var rListPosition = 0xF; (rListPosition | 0) > -1; rListPosition = ((rListPosition | 0) - 1) | 0) { + if ((0x500F & (1 << (rListPosition | 0))) != 0) { + //Push a register into memory: + currentAddress = ((currentAddress | 0) - 4) | 0; + this.memory.memoryWrite32(currentAddress | 0, this.registers[rListPosition | 0] | 0); + } + } + //Store the updated base address back into register: + this.registers[0xD] = currentAddress | 0; + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + this.registers[0] = 0x4000000; + //Save link register: + this.registers[14] = 0x130; + //Skip BIOS ROM processing: + this.branch(this.read32(0x3FFFFFC) & -0x4); +} +GameBoyAdvanceCPU.prototype.HLEIRQExit = function () { + //Get the base address: + var currentAddress = this.registers[0xD] | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Load register(s) from memory: + for (var rListPosition = 0; (rListPosition | 0) < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((0x500F & (1 << (rListPosition | 0))) != 0) { + //Load a register from memory: + this.registers[rListPosition & 0xF] = this.memory.memoryRead32(currentAddress | 0) | 0; + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + //Store the updated base address back into register: + this.registers[0xD] = currentAddress | 0; + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Return from an exception mode: + var data = this.branchFlags.setSUBFlags(this.registers[0xE] | 0, 4) | 0; + //Restore SPSR to CPSR: + data = data & (-4 >> (this.SPSRtoCPSR() >> 5)); + //We performed a branch: + this.branch(data | 0); +} +GameBoyAdvanceCPU.prototype.SWI = function () { + //if (this.IOCore.BIOSFound) { + //Mode bits are set to SWI: + this.switchMode(0x13); + //Save link register: + this.registers[14] = this.getLR() | 0; + //Disable IRQ: + this.modeFlags = this.modeFlags | 0x80; + //Exception always enter ARM mode: + this.enterARM(); + //SWI exception vector: + this.branch(0x8); + /*} + else { + if ((this.modeFlags & 0x20) != 0) { + this.THUMB.incrementProgramCounter(); + //HLE the SWI command: + this.swi.execute(this.THUMB.getSWICode() | 0); + } + else { + this.ARM.incrementProgramCounter(); + //HLE the SWI command: + this.swi.execute(this.ARM.getSWICode() | 0); + } + }*/ +} +GameBoyAdvanceCPU.prototype.UNDEFINED = function () { + //Only process undefined instruction if BIOS loaded: + //if (this.IOCore.BIOSFound) { + //Mode bits are set to SWI: + this.switchMode(0x1B); + //Save link register: + this.registers[14] = this.getLR() | 0; + //Disable IRQ: + this.modeFlags = this.modeFlags | 0x80; + //Exception always enter ARM mode: + this.enterARM(); + //Undefined exception vector: + this.branch(0x4); + /*} + else { + //Pretend we didn't execute the bad instruction then: + if ((this.modeFlags & 0x20) != 0) { + this.THUMB.incrementProgramCounter(); + } + else { + this.ARM.incrementProgramCounter(); + } + }*/ +} +GameBoyAdvanceCPU.prototype.SPSRtoCPSR = function () { + //Used for leaving an exception and returning to the previous state: + var bank = 1; + switch (this.modeFlags & 0x1f) { + case 0x12: //IRQ + break; + case 0x13: //Supervisor + bank = 2; + break; + case 0x11: //FIQ + bank = 0; + break; + case 0x17: //Abort + bank = 3; + break; + case 0x1B: //Undefined + bank = 4; + break; + default: //User & system lacks SPSR + return this.modeFlags & 0x20; + } + var spsr = this.SPSR[bank | 0] | 0; + this.branchFlags.setNZCV(spsr << 20); + this.switchRegisterBank(spsr & 0x1F); + this.modeFlags = spsr & 0xFF; + this.assertIRQ(); + this.THUMBBitModify((spsr & 0x20) != 0); + return spsr & 0x20; +} +GameBoyAdvanceCPU.prototype.switchMode = function (newMode) { + newMode = newMode | 0; + this.CPSRtoSPSR(newMode | 0); + this.switchRegisterBank(newMode | 0); + this.modeFlags = (this.modeFlags & 0xe0) | (newMode | 0); +} +GameBoyAdvanceCPU.prototype.CPSRtoSPSR = function (newMode) { + //Used for entering an exception and saving the previous state: + var spsr = this.modeFlags & 0xFF; + spsr = spsr | (this.branchFlags.getNZCV() >> 20); + switch (newMode | 0) { + case 0x12: //IRQ + this.SPSR[1] = spsr | 0; + break; + case 0x13: //Supervisor + this.SPSR[2] = spsr | 0; + break; + case 0x11: //FIQ + this.SPSR[0] = spsr | 0; + break; + case 0x17: //Abort + this.SPSR[3] = spsr | 0; + break; + case 0x1B: //Undefined + this.SPSR[4] = spsr | 0; + } +} +GameBoyAdvanceCPU.prototype.switchRegisterBank = function (newMode) { + newMode = newMode | 0; + switch (this.modeFlags & 0x1F) { + case 0x10: + case 0x1F: + this.registersUSR[0] = this.registers[8] | 0; + this.registersUSR[1] = this.registers[9] | 0; + this.registersUSR[2] = this.registers[10] | 0; + this.registersUSR[3] = this.registers[11] | 0; + this.registersUSR[4] = this.registers[12] | 0; + this.registersUSR[5] = this.registers[13] | 0; + this.registersUSR[6] = this.registers[14] | 0; + break; + case 0x11: + this.registersFIQ[0] = this.registers[8] | 0; + this.registersFIQ[1] = this.registers[9] | 0; + this.registersFIQ[2] = this.registers[10] | 0; + this.registersFIQ[3] = this.registers[11] | 0; + this.registersFIQ[4] = this.registers[12] | 0; + this.registersFIQ[5] = this.registers[13] | 0; + this.registersFIQ[6] = this.registers[14] | 0; + break; + case 0x12: + this.registersUSR[0] = this.registers[8] | 0; + this.registersUSR[1] = this.registers[9] | 0; + this.registersUSR[2] = this.registers[10] | 0; + this.registersUSR[3] = this.registers[11] | 0; + this.registersUSR[4] = this.registers[12] | 0; + this.registersIRQ[0] = this.registers[13] | 0; + this.registersIRQ[1] = this.registers[14] | 0; + break; + case 0x13: + this.registersUSR[0] = this.registers[8] | 0; + this.registersUSR[1] = this.registers[9] | 0; + this.registersUSR[2] = this.registers[10] | 0; + this.registersUSR[3] = this.registers[11] | 0; + this.registersUSR[4] = this.registers[12] | 0; + this.registersSVC[0] = this.registers[13] | 0; + this.registersSVC[1] = this.registers[14] | 0; + break; + case 0x17: + this.registersUSR[0] = this.registers[8] | 0; + this.registersUSR[1] = this.registers[9] | 0; + this.registersUSR[2] = this.registers[10] | 0; + this.registersUSR[3] = this.registers[11] | 0; + this.registersUSR[4] = this.registers[12] | 0; + this.registersABT[0] = this.registers[13] | 0; + this.registersABT[1] = this.registers[14] | 0; + break; + case 0x1B: + this.registersUSR[0] = this.registers[8] | 0; + this.registersUSR[1] = this.registers[9] | 0; + this.registersUSR[2] = this.registers[10] | 0; + this.registersUSR[3] = this.registers[11] | 0; + this.registersUSR[4] = this.registers[12] | 0; + this.registersUND[0] = this.registers[13] | 0; + this.registersUND[1] = this.registers[14] | 0; + } + switch (newMode | 0) { + case 0x10: + case 0x1F: + this.registers[8] = this.registersUSR[0] | 0; + this.registers[9] = this.registersUSR[1] | 0; + this.registers[10] = this.registersUSR[2] | 0; + this.registers[11] = this.registersUSR[3] | 0; + this.registers[12] = this.registersUSR[4] | 0; + this.registers[13] = this.registersUSR[5] | 0; + this.registers[14] = this.registersUSR[6] | 0; + break; + case 0x11: + this.registers[8] = this.registersFIQ[0] | 0; + this.registers[9] = this.registersFIQ[1] | 0; + this.registers[10] = this.registersFIQ[2] | 0; + this.registers[11] = this.registersFIQ[3] | 0; + this.registers[12] = this.registersFIQ[4] | 0; + this.registers[13] = this.registersFIQ[5] | 0; + this.registers[14] = this.registersFIQ[6] | 0; + break; + case 0x12: + this.registers[8] = this.registersUSR[0] | 0; + this.registers[9] = this.registersUSR[1] | 0; + this.registers[10] = this.registersUSR[2] | 0; + this.registers[11] = this.registersUSR[3] | 0; + this.registers[12] = this.registersUSR[4] | 0; + this.registers[13] = this.registersIRQ[0] | 0; + this.registers[14] = this.registersIRQ[1] | 0; + break; + case 0x13: + this.registers[8] = this.registersUSR[0] | 0; + this.registers[9] = this.registersUSR[1] | 0; + this.registers[10] = this.registersUSR[2] | 0; + this.registers[11] = this.registersUSR[3] | 0; + this.registers[12] = this.registersUSR[4] | 0; + this.registers[13] = this.registersSVC[0] | 0; + this.registers[14] = this.registersSVC[1] | 0; + break; + case 0x17: + this.registers[8] = this.registersUSR[0] | 0; + this.registers[9] = this.registersUSR[1] | 0; + this.registers[10] = this.registersUSR[2] | 0; + this.registers[11] = this.registersUSR[3] | 0; + this.registers[12] = this.registersUSR[4] | 0; + this.registers[13] = this.registersABT[0] | 0; + this.registers[14] = this.registersABT[1] | 0; + break; + case 0x1B: + this.registers[8] = this.registersUSR[0] | 0; + this.registers[9] = this.registersUSR[1] | 0; + this.registers[10] = this.registersUSR[2] | 0; + this.registers[11] = this.registersUSR[3] | 0; + this.registers[12] = this.registersUSR[4] | 0; + this.registers[13] = this.registersUND[0] | 0; + this.registers[14] = this.registersUND[1] | 0; + } +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceCPU.prototype.calculateMUL32 = Math.imul; +} +else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceCPU.prototype.calculateMUL32 = function (rs, rd) { + rs = rs | 0; + rd = rd | 0; + /* + We have to split up the 32 bit multiplication, + as JavaScript does multiplication on the FPU + as double floats, which drops the low bits + rather than the high bits. + */ + var lowMul = (rs & 0xFFFF) * rd; + var highMul = (rs >> 16) * rd; + //Cut off bits above bit 31 and return with proper sign: + return ((highMul << 16) + lowMul) | 0; + } +} +GameBoyAdvanceCPU.prototype.performMUL32 = function (rs, rd) { + rs = rs | 0; + rd = rd | 0; + //Predict the internal cycle time: + if ((rd >>> 8) == 0 || (rd >>> 8) == 0xFFFFFF) { + this.IOCore.wait.CPUInternalSingleCyclePrefetch(); + } + else if ((rd >>> 16) == 0 || (rd >>> 16) == 0xFFFF) { + this.IOCore.wait.CPUInternalCyclePrefetch(2); + } + else if ((rd >>> 24) == 0 || (rd >>> 24) == 0xFF) { + this.IOCore.wait.CPUInternalCyclePrefetch(3); + } + else { + this.IOCore.wait.CPUInternalCyclePrefetch(4); + } + return this.calculateMUL32(rs | 0, rd | 0) | 0; +} +GameBoyAdvanceCPU.prototype.performMUL32MLA = function (rs, rd) { + rs = rs | 0; + rd = rd | 0; + //Predict the internal cycle time: + if ((rd >>> 8) == 0 || (rd >>> 8) == 0xFFFFFF) { + this.IOCore.wait.CPUInternalCyclePrefetch(2); + } + else if ((rd >>> 16) == 0 || (rd >>> 16) == 0xFFFF) { + this.IOCore.wait.CPUInternalCyclePrefetch(3); + } + else if ((rd >>> 24) == 0 || (rd >>> 24) == 0xFF) { + this.IOCore.wait.CPUInternalCyclePrefetch(4); + } + else { + this.IOCore.wait.CPUInternalCyclePrefetch(5); + } + return this.calculateMUL32(rs | 0, rd | 0) | 0; +} +GameBoyAdvanceCPU.prototype.performMUL64 = function (rs, rd) { + rs = rs | 0; + rd = rd | 0; + //Predict the internal cycle time: + if ((rd >>> 8) == 0 || (rd >>> 8) == 0xFFFFFF) { + this.IOCore.wait.CPUInternalCyclePrefetch(2); + } + else if ((rd >>> 16) == 0 || (rd >>> 16) == 0xFFFF) { + this.IOCore.wait.CPUInternalCyclePrefetch(3); + } + else if ((rd >>> 24) == 0 || (rd >>> 24) == 0xFF) { + this.IOCore.wait.CPUInternalCyclePrefetch(4); + } + else { + this.IOCore.wait.CPUInternalCyclePrefetch(5); + } + //Solve for the high word (Do FPU double divide to bring down high word into the low word): + this.mul64ResultHigh = Math.floor((rs * rd) / 0x100000000) | 0; + this.mul64ResultLow = this.calculateMUL32(rs | 0, rd | 0) | 0; +} +GameBoyAdvanceCPU.prototype.performMLA64 = function (rs, rd, mlaHigh, mlaLow) { + rs = rs | 0; + rd = rd | 0; + mlaHigh = mlaHigh | 0; + mlaLow = mlaLow | 0; + //Predict the internal cycle time: + if ((rd >>> 8) == 0 || (rd >>> 8) == 0xFFFFFF) { + this.IOCore.wait.CPUInternalCyclePrefetch(3); + } + else if ((rd >>> 16) == 0 || (rd >>> 16) == 0xFFFF) { + this.IOCore.wait.CPUInternalCyclePrefetch(4); + } + else if ((rd >>> 24) == 0 || (rd >>> 24) == 0xFF) { + this.IOCore.wait.CPUInternalCyclePrefetch(5); + } + else { + this.IOCore.wait.CPUInternalCyclePrefetch(6); + } + //Solve for the high word (Do FPU double divide to bring down high word into the low word): + var mulTop = Math.floor((rs * rd) / 0x100000000) | 0; + var dirty = (this.calculateMUL32(rs | 0, rd | 0) >>> 0) + (mlaLow >>> 0); + this.mul64ResultHigh = ((mulTop | 0) + (mlaHigh | 0) + Math.floor(dirty / 0x100000000)) | 0; + this.mul64ResultLow = dirty | 0; +} +GameBoyAdvanceCPU.prototype.performUMUL64 = function (rs, rd) { + rs = rs | 0; + rd = rd | 0; + //Predict the internal cycle time: + if ((rd >>> 8) == 0) { + this.IOCore.wait.CPUInternalCyclePrefetch(2); + } + else if ((rd >>> 16) == 0) { + this.IOCore.wait.CPUInternalCyclePrefetch(3); + } + else if ((rd >>> 24) == 0) { + this.IOCore.wait.CPUInternalCyclePrefetch(4); + } + else { + this.IOCore.wait.CPUInternalCyclePrefetch(5); + } + //Solve for the high word (Do FPU double divide to bring down high word into the low word): + this.mul64ResultHigh = (((rs >>> 0) * (rd >>> 0)) / 0x100000000) | 0; + this.mul64ResultLow = this.calculateMUL32(rs | 0, rd | 0) | 0; +} +GameBoyAdvanceCPU.prototype.performUMLA64 = function (rs, rd, mlaHigh, mlaLow) { + rs = rs | 0; + rd = rd | 0; + mlaHigh = mlaHigh | 0; + mlaLow = mlaLow | 0; + //Predict the internal cycle time: + if ((rd >>> 8) == 0) { + this.IOCore.wait.CPUInternalCyclePrefetch(3); + } + else if ((rd >>> 16) == 0) { + this.IOCore.wait.CPUInternalCyclePrefetch(4); + } + else if ((rd >>> 24) == 0) { + this.IOCore.wait.CPUInternalCyclePrefetch(5); + } + else { + this.IOCore.wait.CPUInternalCyclePrefetch(6); + } + //Solve for the high word (Do FPU double divide to bring down high word into the low word): + var mulTop = Math.floor(((rs >>> 0) * (rd >>> 0)) / 0x100000000) | 0; + var dirty = (this.calculateMUL32(rs | 0, rd | 0) >>> 0) + (mlaLow >>> 0); + this.mul64ResultHigh = ((mulTop | 0) + (mlaHigh | 0) + Math.floor(dirty / 0x100000000)) | 0; + this.mul64ResultLow = dirty | 0; +} +GameBoyAdvanceCPU.prototype.write32 = function (address, data) { + address = address | 0; + data = data | 0; + //Updating the address bus away from PC fetch: + this.IOCore.wait.NonSequentialBroadcast(); + this.memory.memoryWrite32(address | 0, data | 0); + //Updating the address bus back to PC fetch: + this.IOCore.wait.NonSequentialBroadcast(); +} +GameBoyAdvanceCPU.prototype.write16 = function (address, data) { + address = address | 0; + data = data | 0; + //Updating the address bus away from PC fetch: + this.IOCore.wait.NonSequentialBroadcast(); + this.memory.memoryWrite16(address | 0, data | 0); + //Updating the address bus back to PC fetch: + this.IOCore.wait.NonSequentialBroadcast(); +} +GameBoyAdvanceCPU.prototype.write8 = function (address, data) { + address = address | 0; + data = data | 0; + //Updating the address bus away from PC fetch: + this.IOCore.wait.NonSequentialBroadcast(); + this.memory.memoryWrite8(address | 0, data | 0); + //Updating the address bus back to PC fetch: + this.IOCore.wait.NonSequentialBroadcast(); +} +GameBoyAdvanceCPU.prototype.read32 = function (address) { + address = address | 0; + //Updating the address bus away from PC fetch: + this.IOCore.wait.NonSequentialBroadcast(); + var data = this.memory.memoryRead32(address | 0) | 0; + //Unaligned access gets data rotated right: + if ((address & 0x3) != 0) { + //Rotate word right: + data = (data << ((4 - (address & 0x3)) << 3)) | (data >>> ((address & 0x3) << 3)); + } + //Updating the address bus back to PC fetch: + this.IOCore.wait.NonSequentialBroadcast(); + return data | 0; +} +GameBoyAdvanceCPU.prototype.read16 = function (address) { + address = address | 0; + //Updating the address bus away from PC fetch: + this.IOCore.wait.NonSequentialBroadcast(); + var data = this.memory.memoryRead16(address | 0) | 0; + //Unaligned access gets data rotated right: + if ((address & 0x1) != 0) { + //Rotate word right: + data = (data << 24) | (data >>> 8); + } + //Updating the address bus back to PC fetch: + this.IOCore.wait.NonSequentialBroadcast(); + return data | 0; +} +GameBoyAdvanceCPU.prototype.read8 = function (address) { + address = address | 0; + //Updating the address bus away from PC fetch: + this.IOCore.wait.NonSequentialBroadcast(); + var data = this.memory.memoryRead8(address | 0) | 0; + //Updating the address bus back to PC fetch: + this.IOCore.wait.NonSequentialBroadcast(); + return data | 0; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/CPU/ARM.js b/public/gfiles/gba/IodineGBA/core/CPU/ARM.js new file mode 100644 index 00000000..ebbac1ad --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/CPU/ARM.js @@ -0,0 +1,3687 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function ARMInstructionSet(CPUCore) { + this.CPUCore = CPUCore; + this.initialize(); +} +ARMInstructionSet.prototype.initialize = function () { + this.wait = this.CPUCore.wait; + this.registers = this.CPUCore.registers; + this.registersUSR = this.CPUCore.registersUSR; + this.branchFlags = this.CPUCore.branchFlags; + this.fetch = 0; + this.decode = 0; + this.execute = 0; + this.memory = this.CPUCore.memory; +} +ARMInstructionSet.prototype.executeIteration = function () { + //Push the new fetch access: + this.fetch = this.memory.memoryReadCPU32(this.readPC() | 0) | 0; + //Execute Conditional Instruction: + this.executeConditionalCode(); + //Update the pipelining state: + this.execute = this.decode | 0; + this.decode = this.fetch | 0; +} +ARMInstructionSet.prototype.executeConditionalCode = function () { + //LSB of condition code is used to reverse the test logic: + if (((this.execute << 3) ^ this.branchFlags.checkConditionalCode(this.execute | 0)) >= 0) { + //Passed the condition code test, so execute: + this.executeDecoded(); + } + else { + //Increment the program counter if we failed the test: + this.incrementProgramCounter(); + } +} + +ARMInstructionSet.prototype.executeBubble = function () { + //Push the new fetch access: + this.fetch = this.memory.memoryReadCPU32(this.readPC() | 0) | 0; + //Update the Program Counter: + this.incrementProgramCounter(); + //Update the pipelining state: + this.execute = this.decode | 0; + this.decode = this.fetch | 0; +} +ARMInstructionSet.prototype.incrementProgramCounter = function () { + //Increment The Program Counter: + this.registers[15] = ((this.registers[15] | 0) + 4) | 0; +} +ARMInstructionSet.prototype.getLR = function () { + return ((this.readPC() | 0) - 4) | 0; +} +ARMInstructionSet.prototype.getIRQLR = function () { + return this.getLR() | 0; +} +ARMInstructionSet.prototype.getCurrentFetchValue = function () { + return this.fetch | 0; +} +ARMInstructionSet.prototype.getSWICode = function () { + return (this.execute >> 16) & 0xFF; +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + ARMInstructionSet.prototype.getPopCount = function () { + var temp = this.execute & 0xFFFF; + temp = ((temp | 0) - ((temp >> 1) & 0x5555)) | 0; + temp = ((temp & 0x3333) + ((temp >> 2) & 0x3333)) | 0; + temp = (((temp | 0) + (temp >> 4)) & 0xF0F) | 0; + temp = Math.imul(temp | 0, 0x1010101) >> 24; + return temp | 0; + } +} +else { + //Math.imul not found, use the compatibility method: + ARMInstructionSet.prototype.getPopCount = function () { + var temp = this.execute & 0xFFFF; + temp = ((temp | 0) - ((temp >> 1) & 0x5555)) | 0; + temp = ((temp & 0x3333) + ((temp >> 2) & 0x3333)) | 0; + temp = (((temp | 0) + (temp >> 4)) & 0xF0F) | 0; + temp = (temp * 0x1010101) >> 24; + return temp | 0; + } +} +ARMInstructionSet.prototype.getNegativeOffsetStartAddress = function (currentAddress) { + //Used for LDMD/STMD: + currentAddress = currentAddress | 0; + var offset = this.getPopCount() << 2; + currentAddress = ((currentAddress | 0) - (offset | 0)) | 0; + return currentAddress | 0; +} +ARMInstructionSet.prototype.getPositiveOffsetStartAddress = function (currentAddress) { + //Used for LDMD/STMD: + currentAddress = currentAddress | 0; + var offset = this.getPopCount() << 2; + currentAddress = ((currentAddress | 0) + (offset | 0)) | 0; + return currentAddress | 0; +} +ARMInstructionSet.prototype.writeRegister = function (address, data) { + //Unguarded non-pc register write: + address = address | 0; + data = data | 0; + this.registers[address & 0xF] = data | 0; +} +ARMInstructionSet.prototype.writeUserRegister = function (address, data) { + //Unguarded non-pc user mode register write: + address = address | 0; + data = data | 0; + this.registersUSR[address & 0x7] = data | 0; +} +ARMInstructionSet.prototype.guardRegisterWrite = function (address, data) { + //Guarded register write: + address = address | 0; + data = data | 0; + if ((address | 0) < 0xF) { + //Non-PC Write: + this.writeRegister(address | 0, data | 0); + } + else { + //We performed a branch: + this.CPUCore.branch(data & -4); + } +} +ARMInstructionSet.prototype.multiplyGuard12OffsetRegisterWrite = function (data) { + //Writes to R15 ignored in the multiply instruction! + data = data | 0; + var address = (this.execute >> 0xC) & 0xF; + if ((address | 0) != 0xF) { + this.writeRegister(address | 0, data | 0); + } +} +ARMInstructionSet.prototype.multiplyGuard16OffsetRegisterWrite = function (data) { + //Writes to R15 ignored in the multiply instruction! + data = data | 0; + var address = (this.execute >> 0x10) & 0xF; + this.incrementProgramCounter(); + if ((address | 0) != 0xF) { + this.writeRegister(address | 0, data | 0); + } +} +ARMInstructionSet.prototype.performMUL32 = function () { + var result = 0; + if (((this.execute >> 16) & 0xF) != (this.execute & 0xF)) { + /* + http://www.chiark.greenend.org.uk/~theom/riscos/docs/ultimate/a252armc.txt + + Due to the way that Booth's algorithm has been implemented, certain + combinations of operand registers should be avoided. (The assembler will + issue a warning if these restrictions are overlooked.) + The destination register (Rd) should not be the same as the Rm operand + register, as Rd is used to hold intermediate values and Rm is used + repeatedly during the multiply. A MUL will give a zero result if Rm=Rd, and + a MLA will give a meaningless result. + */ + result = this.CPUCore.performMUL32(this.read0OffsetRegister() | 0, this.read8OffsetRegister() | 0) | 0; + } + return result | 0; +} +ARMInstructionSet.prototype.performMUL32MLA = function () { + var result = 0; + if (((this.execute >> 16) & 0xF) != (this.execute & 0xF)) { + /* + http://www.chiark.greenend.org.uk/~theom/riscos/docs/ultimate/a252armc.txt + + Due to the way that Booth's algorithm has been implemented, certain + combinations of operand registers should be avoided. (The assembler will + issue a warning if these restrictions are overlooked.) + The destination register (Rd) should not be the same as the Rm operand + register, as Rd is used to hold intermediate values and Rm is used + repeatedly during the multiply. A MUL will give a zero result if Rm=Rd, and + a MLA will give a meaningless result. + */ + result = this.CPUCore.performMUL32MLA(this.read0OffsetRegister() | 0, this.read8OffsetRegister() | 0) | 0; + } + return result | 0; +} +ARMInstructionSet.prototype.guard12OffsetRegisterWrite = function (data) { + data = data | 0; + this.incrementProgramCounter(); + this.guard12OffsetRegisterWrite2(data | 0); +} +ARMInstructionSet.prototype.guard12OffsetRegisterWrite2 = function (data) { + data = data | 0; + this.guardRegisterWrite((this.execute >> 0xC) & 0xF, data | 0); +} +ARMInstructionSet.prototype.guard16OffsetRegisterWrite = function (data) { + data = data | 0; + this.guardRegisterWrite((this.execute >> 0x10) & 0xF, data | 0); +} +ARMInstructionSet.prototype.guard16OffsetUserRegisterWrite = function (data) { + data = data | 0; + var address = (this.execute >> 0x10) & 0xF; + if ((address | 0) < 0xF) { + //Non-PC Write: + this.guardUserRegisterWrite(address | 0, data | 0); + } + else { + //We performed a branch: + this.CPUCore.branch(data & -4); + } +} +ARMInstructionSet.prototype.guardProgramCounterRegisterWriteCPSR = function (data) { + data = data | 0; + //Restore SPSR to CPSR: + data = data & (-4 >> (this.CPUCore.SPSRtoCPSR() >> 5)); + //We performed a branch: + this.CPUCore.branch(data | 0); +} +ARMInstructionSet.prototype.guardRegisterWriteCPSR = function (address, data) { + //Guard for possible pc write with cpsr update: + address = address | 0; + data = data | 0; + if ((address | 0) < 0xF) { + //Non-PC Write: + this.writeRegister(address | 0, data | 0); + } + else { + //Restore SPSR to CPSR: + this.guardProgramCounterRegisterWriteCPSR(data | 0); + } +} +ARMInstructionSet.prototype.guard12OffsetRegisterWriteCPSR = function (data) { + data = data | 0; + this.incrementProgramCounter(); + this.guard12OffsetRegisterWriteCPSR2(data | 0); +} +ARMInstructionSet.prototype.guard12OffsetRegisterWriteCPSR2 = function (data) { + data = data | 0; + this.guardRegisterWriteCPSR((this.execute >> 0xC) & 0xF, data | 0); +} +ARMInstructionSet.prototype.guard16OffsetRegisterWriteCPSR = function (data) { + data = data | 0; + this.guardRegisterWriteCPSR((this.execute >> 0x10) & 0xF, data | 0); +} +ARMInstructionSet.prototype.guardUserRegisterWrite = function (address, data) { + //Guard only on user access, not PC!: + address = address | 0; + data = data | 0; + switch (this.CPUCore.modeFlags & 0x1f) { + case 0x10: + case 0x1F: + this.writeRegister(address | 0, data | 0); + break; + case 0x11: + if ((address | 0) < 8) { + this.writeRegister(address | 0, data | 0); + } + else { + //User-Mode Register Write Inside Non-User-Mode: + this.writeUserRegister(address | 0, data | 0); + } + break; + default: + if ((address | 0) < 13) { + this.writeRegister(address | 0, data | 0); + } + else { + //User-Mode Register Write Inside Non-User-Mode: + this.writeUserRegister(address | 0, data | 0); + } + } +} +ARMInstructionSet.prototype.guardRegisterWriteLDM = function (address, data) { + //Proxy guarded register write for LDM: + address = address | 0; + data = data | 0; + this.guardRegisterWrite(address | 0, data | 0); +} +ARMInstructionSet.prototype.guardUserRegisterWriteLDM = function (address, data) { + //Proxy guarded user mode register write with PC guard for LDM: + address = address | 0; + data = data | 0; + if ((address | 0) < 0xF) { + if ((this.execute & 0x8000) != 0) { + //PC is in the list, don't do user-mode: + this.writeRegister(address | 0, data | 0); + } + else { + //PC isn't in the list, do user-mode: + this.guardUserRegisterWrite(address | 0, data | 0); + } + } + else { + this.guardProgramCounterRegisterWriteCPSR(data | 0); + } +} +ARMInstructionSet.prototype.readPC = function () { + //PC register read: + return this.registers[0xF] | 0; +} +ARMInstructionSet.prototype.readRegister = function (address) { + //Unguarded register read: + address = address | 0; + return this.registers[address & 0xF] | 0; +} +ARMInstructionSet.prototype.readUserRegister = function (address) { + //Unguarded user mode register read: + address = address | 0; + var data = 0; + if ((address | 0) < 0xF) { + data = this.registersUSR[address & 0x7] | 0; + } + else { + //Get Special Case PC Read: + data = this.readPC() | 0; + } + return data | 0; +} +ARMInstructionSet.prototype.read0OffsetRegister = function () { + //Unguarded register read at position 0: + return this.readRegister(this.execute | 0) | 0; +} +ARMInstructionSet.prototype.read8OffsetRegister = function () { + //Unguarded register read at position 0x8: + return this.readRegister(this.execute >> 0x8) | 0; +} +ARMInstructionSet.prototype.read12OffsetRegister = function () { + //Unguarded register read at position 0xC: + return this.readRegister(this.execute >> 0xC) | 0; +} +ARMInstructionSet.prototype.read16OffsetRegister = function () { + //Unguarded register read at position 0x10: + return this.readRegister(this.execute >> 0x10) | 0; +} +ARMInstructionSet.prototype.read16OffsetUserRegister = function () { + //Guarded register read at position 0x10: + return this.guardUserRegisterRead(this.execute >> 0x10) | 0; +} +ARMInstructionSet.prototype.guard12OffsetRegisterRead = function () { + this.incrementProgramCounter(); + return this.readRegister((this.execute >> 12) & 0xF) | 0; +} +ARMInstructionSet.prototype.guardUserRegisterRead = function (address) { + //Guard only on user access, not PC!: + address = address | 0; + var data = 0; + switch (this.CPUCore.modeFlags & 0x1F) { + case 0x10: + case 0x1F: + data = this.readRegister(address | 0) | 0; + break; + case 0x11: + if ((address | 0) < 8) { + data = this.readRegister(address | 0) | 0; + } + else { + //User-Mode Register Read Inside Non-User-Mode: + data = this.readUserRegister(address | 0) | 0; + } + break; + default: + if ((address | 0) < 13) { + data = this.readRegister(address | 0) | 0; + } + else { + //User-Mode Register Read Inside Non-User-Mode: + data = this.readUserRegister(address | 0) | 0; + } + } + return data | 0; +} +ARMInstructionSet.prototype.BX = function () { + //Branch & eXchange: + var address = this.read0OffsetRegister() | 0; + if ((address & 0x1) == 0) { + //Stay in ARM mode: + this.CPUCore.branch(address & -4); + } + else { + //Enter THUMB mode: + this.CPUCore.enterTHUMB(); + this.CPUCore.branch(address & -2); + } +} +ARMInstructionSet.prototype.B = function () { + //Branch: + this.CPUCore.branch(((this.readPC() | 0) + ((this.execute << 8) >> 6)) | 0); +} +ARMInstructionSet.prototype.BL = function () { + //Branch with Link: + this.writeRegister(0xE, this.getLR() | 0); + this.B(); +} +ARMInstructionSet.prototype.AND = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Perform bitwise AND: + //Update destination register: + this.guard12OffsetRegisterWrite(operand1 & operand2); +} +ARMInstructionSet.prototype.AND2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Perform bitwise AND: + //Update destination register: + this.guard12OffsetRegisterWrite2(operand1 & operand2); +} +ARMInstructionSet.prototype.ANDS = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing2() | 0; + //Perform bitwise AND: + var result = operand1 & operand2; + this.branchFlags.setNZInt(result | 0); + //Update destination register and guard CPSR for PC: + this.guard12OffsetRegisterWriteCPSR(result | 0); +} +ARMInstructionSet.prototype.ANDS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing4() | 0; + //Perform bitwise AND: + var result = operand1 & operand2; + this.branchFlags.setNZInt(result | 0); + //Update destination register and guard CPSR for PC: + this.guard12OffsetRegisterWriteCPSR2(result | 0); +} +ARMInstructionSet.prototype.EOR = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Perform bitwise EOR: + //Update destination register: + this.guard12OffsetRegisterWrite(operand1 ^ operand2); +} +ARMInstructionSet.prototype.EOR2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Perform bitwise EOR: + //Update destination register: + this.guard12OffsetRegisterWrite2(operand1 ^ operand2); +} +ARMInstructionSet.prototype.EORS = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing2() | 0; + //Perform bitwise EOR: + var result = operand1 ^ operand2; + this.branchFlags.setNZInt(result | 0); + //Update destination register and guard CPSR for PC: + this.guard12OffsetRegisterWriteCPSR(result | 0); +} +ARMInstructionSet.prototype.EORS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing4() | 0; + //Perform bitwise EOR: + var result = operand1 ^ operand2; + this.branchFlags.setNZInt(result | 0); + //Update destination register and guard CPSR for PC: + this.guard12OffsetRegisterWriteCPSR2(result | 0); +} +ARMInstructionSet.prototype.SUB = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Perform Subtraction: + //Update destination register: + this.guard12OffsetRegisterWrite(((operand1 | 0) - (operand2 | 0)) | 0); +} +ARMInstructionSet.prototype.SUB2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Perform Subtraction: + //Update destination register: + this.guard12OffsetRegisterWrite2(((operand1 | 0) - (operand2 | 0)) | 0); +} +ARMInstructionSet.prototype.SUBS = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Update destination register: + this.guard12OffsetRegisterWriteCPSR(this.branchFlags.setSUBFlags(operand1 | 0, operand2 | 0) | 0); +} +ARMInstructionSet.prototype.SUBS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Update destination register: + this.guard12OffsetRegisterWriteCPSR2(this.branchFlags.setSUBFlags(operand1 | 0, operand2 | 0) | 0); +} +ARMInstructionSet.prototype.RSB = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Perform Subtraction: + //Update destination register: + this.guard12OffsetRegisterWrite(((operand2 | 0) - (operand1 | 0)) | 0); +} +ARMInstructionSet.prototype.RSB2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Perform Subtraction: + //Update destination register: + this.guard12OffsetRegisterWrite2(((operand2 | 0) - (operand1 | 0)) | 0); +} +ARMInstructionSet.prototype.RSBS = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Update destination register: + this.guard12OffsetRegisterWriteCPSR(this.branchFlags.setSUBFlags(operand2 | 0, operand1 | 0) | 0); +} +ARMInstructionSet.prototype.RSBS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Update destination register: + this.guard12OffsetRegisterWriteCPSR2(this.branchFlags.setSUBFlags(operand2 | 0, operand1 | 0) | 0); +} +ARMInstructionSet.prototype.ADD = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Perform Addition: + //Update destination register: + this.guard12OffsetRegisterWrite(((operand1 | 0) + (operand2 | 0)) | 0); +} +ARMInstructionSet.prototype.ADD2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Perform Addition: + //Update destination register: + this.guard12OffsetRegisterWrite2(((operand1 | 0) + (operand2 | 0)) | 0); +} +ARMInstructionSet.prototype.ADDS = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Update destination register: + this.guard12OffsetRegisterWriteCPSR(this.branchFlags.setADDFlags(operand1 | 0, operand2 | 0) | 0); +} +ARMInstructionSet.prototype.ADDS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Update destination register: + this.guard12OffsetRegisterWriteCPSR2(this.branchFlags.setADDFlags(operand1 | 0, operand2 | 0) | 0); +} +ARMInstructionSet.prototype.ADC = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Perform Addition w/ Carry: + //Update destination register: + operand1 = ((operand1 | 0) + (operand2 | 0)) | 0; + operand1 = ((operand1 | 0) + (this.branchFlags.getCarry() >>> 31)) | 0; + this.guard12OffsetRegisterWrite(operand1 | 0); +} +ARMInstructionSet.prototype.ADC2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Perform Addition w/ Carry: + //Update destination register: + operand1 = ((operand1 | 0) + (operand2 | 0)) | 0; + operand1 = ((operand1 | 0) + (this.branchFlags.getCarry() >>> 31)) | 0; + this.guard12OffsetRegisterWrite2(operand1 | 0); +} +ARMInstructionSet.prototype.ADCS = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Update destination register: + this.guard12OffsetRegisterWriteCPSR(this.branchFlags.setADCFlags(operand1 | 0, operand2 | 0) | 0); +} +ARMInstructionSet.prototype.ADCS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Update destination register: + this.guard12OffsetRegisterWriteCPSR2(this.branchFlags.setADCFlags(operand1 | 0, operand2 | 0) | 0); +} +ARMInstructionSet.prototype.SBC = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Perform Subtraction w/ Carry: + //Update destination register: + operand1 = ((operand1 | 0) - (operand2 | 0)) | 0; + operand1 = ((operand1 | 0) - (this.branchFlags.getCarryReverse() >>> 31)) | 0; + this.guard12OffsetRegisterWrite(operand1 | 0); +} +ARMInstructionSet.prototype.SBC2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Perform Subtraction w/ Carry: + //Update destination register: + operand1 = ((operand1 | 0) - (operand2 | 0)) | 0; + operand1 = ((operand1 | 0) - (this.branchFlags.getCarryReverse() >>> 31)) | 0; + this.guard12OffsetRegisterWrite2(operand1 | 0); +} +ARMInstructionSet.prototype.SBCS = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Update destination register: + this.guard12OffsetRegisterWriteCPSR(this.branchFlags.setSBCFlags(operand1 | 0, operand2 | 0) | 0); +} +ARMInstructionSet.prototype.SBCS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Update destination register: + this.guard12OffsetRegisterWriteCPSR2(this.branchFlags.setSBCFlags(operand1 | 0, operand2 | 0) | 0); +} +ARMInstructionSet.prototype.RSC = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Perform Reverse Subtraction w/ Carry: + //Update destination register: + operand1 = ((operand2 | 0) - (operand1 | 0)) | 0; + operand1 = ((operand1 | 0) - (this.branchFlags.getCarryReverse() >>> 31)) | 0; + this.guard12OffsetRegisterWrite(operand1 | 0); +} +ARMInstructionSet.prototype.RSC2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Perform Reverse Subtraction w/ Carry: + //Update destination register: + operand1 = ((operand2 | 0) - (operand1 | 0)) | 0; + operand1 = ((operand1 | 0) - (this.branchFlags.getCarryReverse() >>> 31)) | 0; + this.guard12OffsetRegisterWrite2(operand1 | 0); +} +ARMInstructionSet.prototype.RSCS = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Update destination register: + this.guard12OffsetRegisterWriteCPSR(this.branchFlags.setSBCFlags(operand2 | 0, operand1 | 0) | 0); +} +ARMInstructionSet.prototype.RSCS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Update destination register: + this.guard12OffsetRegisterWriteCPSR2(this.branchFlags.setSBCFlags(operand2 | 0, operand1 | 0) | 0); +} +ARMInstructionSet.prototype.TSTS = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing2() | 0; + //Perform bitwise AND: + var result = operand1 & operand2; + this.branchFlags.setNZInt(result | 0); + //Increment PC: + this.incrementProgramCounter(); +} +ARMInstructionSet.prototype.TSTS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing4() | 0; + //Perform bitwise AND: + var result = operand1 & operand2; + this.branchFlags.setNZInt(result | 0); +} +ARMInstructionSet.prototype.TEQS = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing2() | 0; + //Perform bitwise EOR: + var result = operand1 ^ operand2; + this.branchFlags.setNZInt(result | 0); + //Increment PC: + this.incrementProgramCounter(); +} +ARMInstructionSet.prototype.TEQS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing4() | 0; + //Perform bitwise EOR: + var result = operand1 ^ operand2; + this.branchFlags.setNZInt(result | 0); +} +ARMInstructionSet.prototype.CMPS = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + this.branchFlags.setCMPFlags(operand1 | 0, operand2 | 0); + //Increment PC: + this.incrementProgramCounter(); +} +ARMInstructionSet.prototype.CMPS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + this.branchFlags.setCMPFlags(operand1 | 0, operand2 | 0); +} +ARMInstructionSet.prototype.CMNS = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1(); + this.branchFlags.setCMNFlags(operand1 | 0, operand2 | 0); + //Increment PC: + this.incrementProgramCounter(); +} +ARMInstructionSet.prototype.CMNS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3(); + this.branchFlags.setCMNFlags(operand1 | 0, operand2 | 0); +} +ARMInstructionSet.prototype.ORR = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing1() | 0; + //Perform bitwise OR: + //Update destination register: + this.guard12OffsetRegisterWrite(operand1 | operand2); +} +ARMInstructionSet.prototype.ORR2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing3() | 0; + //Perform bitwise OR: + //Update destination register: + this.guard12OffsetRegisterWrite2(operand1 | operand2); +} +ARMInstructionSet.prototype.ORRS = function () { + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing2() | 0; + //Perform bitwise OR: + var result = operand1 | operand2; + this.branchFlags.setNZInt(result | 0); + //Update destination register and guard CPSR for PC: + this.guard12OffsetRegisterWriteCPSR(result | 0); +} +ARMInstructionSet.prototype.ORRS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + var operand2 = this.operand2OP_DataProcessing4() | 0; + //Perform bitwise OR: + var result = operand1 | operand2; + this.branchFlags.setNZInt(result | 0); + //Update destination register and guard CPSR for PC: + this.guard12OffsetRegisterWriteCPSR2(result | 0); +} +ARMInstructionSet.prototype.MOV = function () { + //Perform move: + //Update destination register: + this.guard12OffsetRegisterWrite(this.operand2OP_DataProcessing1() | 0); +} +ARMInstructionSet.prototype.MOV2 = function () { + //Increment PC: + this.incrementProgramCounter(); + //Perform move: + //Update destination register: + this.guard12OffsetRegisterWrite2(this.operand2OP_DataProcessing3() | 0); +} +ARMInstructionSet.prototype.MOVS = function () { + var operand2 = this.operand2OP_DataProcessing2() | 0; + //Perform move: + this.branchFlags.setNZInt(operand2 | 0); + //Update destination register and guard CPSR for PC: + this.guard12OffsetRegisterWriteCPSR(operand2 | 0); +} +ARMInstructionSet.prototype.MOVS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand2 = this.operand2OP_DataProcessing4() | 0; + //Perform move: + this.branchFlags.setNZInt(operand2 | 0); + //Update destination register and guard CPSR for PC: + this.guard12OffsetRegisterWriteCPSR2(operand2 | 0); +} +ARMInstructionSet.prototype.BIC = function () { + var operand1 = this.read16OffsetRegister() | 0; + //NOT operand 2: + var operand2 = ~this.operand2OP_DataProcessing1(); + //Perform bitwise AND: + //Update destination register: + this.guard12OffsetRegisterWrite(operand1 & operand2); +} +ARMInstructionSet.prototype.BIC2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + //NOT operand 2: + var operand2 = ~this.operand2OP_DataProcessing3(); + //Perform bitwise AND: + //Update destination register: + this.guard12OffsetRegisterWrite2(operand1 & operand2); +} +ARMInstructionSet.prototype.BICS = function () { + var operand1 = this.read16OffsetRegister() | 0; + //NOT operand 2: + var operand2 = ~this.operand2OP_DataProcessing2(); + //Perform bitwise AND: + var result = operand1 & operand2; + this.branchFlags.setNZInt(result | 0); + //Update destination register and guard CPSR for PC: + this.guard12OffsetRegisterWriteCPSR(result | 0); +} +ARMInstructionSet.prototype.BICS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand1 = this.read16OffsetRegister() | 0; + //NOT operand 2: + var operand2 = ~this.operand2OP_DataProcessing4(); + //Perform bitwise AND: + var result = operand1 & operand2; + this.branchFlags.setNZInt(result | 0); + //Update destination register and guard CPSR for PC: + this.guard12OffsetRegisterWriteCPSR2(result | 0); +} +ARMInstructionSet.prototype.MVN = function () { + //Perform move negative: + //Update destination register: + this.guard12OffsetRegisterWrite(~this.operand2OP_DataProcessing1()); +} +ARMInstructionSet.prototype.MVN2 = function () { + //Increment PC: + this.incrementProgramCounter(); + //Perform move negative: + //Update destination register: + this.guard12OffsetRegisterWrite2(~this.operand2OP_DataProcessing3()); +} +ARMInstructionSet.prototype.MVNS = function () { + var operand2 = ~this.operand2OP_DataProcessing2(); + //Perform move negative: + this.branchFlags.setNZInt(operand2 | 0); + //Update destination register and guard CPSR for PC: + this.guard12OffsetRegisterWriteCPSR(operand2 | 0); +} +ARMInstructionSet.prototype.MVNS2 = function () { + //Increment PC: + this.incrementProgramCounter(); + var operand2 = ~this.operand2OP_DataProcessing4(); + //Perform move negative: + this.branchFlags.setNZInt(operand2 | 0); + //Update destination register and guard CPSR for PC: + this.guard12OffsetRegisterWriteCPSR2(operand2 | 0); +} +ARMInstructionSet.prototype.MRS = function () { + //Transfer PSR to Register: + var psr = 0; + if ((this.execute & 0x400000) == 0) { + //CPSR->Register + psr = this.rc() | 0; + } + else { + //SPSR->Register + psr = this.rs() | 0; + } + this.guard12OffsetRegisterWrite(psr | 0); +} +ARMInstructionSet.prototype.MSR = function () { + switch (this.execute & 0x2400000) { + case 0: + //Reg->CPSR + this.MSR1(); + break; + case 0x400000: + //Reg->SPSR + this.MSR2(); + break; + case 0x2000000: + //Immediate->CPSR + this.MSR3(); + break; + default: + //Immediate->SPSR + this.MSR4(); + } + //Increment PC: + this.incrementProgramCounter(); +} +ARMInstructionSet.prototype.MSR1 = function () { + var newcpsr = this.read0OffsetRegister() | 0; + this.branchFlags.setNZCV(newcpsr | 0); + if ((this.execute & 0x10000) != 0 && (this.CPUCore.modeFlags & 0x1F) != 0x10) { + this.CPUCore.switchRegisterBank(newcpsr & 0x1F); + this.CPUCore.modeFlags = newcpsr & 0xDF; + this.CPUCore.assertIRQ(); + } +} +ARMInstructionSet.prototype.MSR2 = function () { + var operand = this.read0OffsetRegister() | 0; + var bank = 1; + switch (this.CPUCore.modeFlags & 0x1F) { + case 0x12: //IRQ + break; + case 0x13: //Supervisor + bank = 2; + break; + case 0x11: //FIQ + bank = 0; + break; + case 0x17: //Abort + bank = 3; + break; + case 0x1B: //Undefined + bank = 4; + break; + default: + return; + } + var spsr = (operand >> 20) & 0xF00; + if ((this.execute & 0x10000) != 0) { + spsr = spsr | (operand & 0xFF); + } + else { + spsr = spsr | (this.CPUCore.SPSR[bank | 0] & 0xFF); + } + this.CPUCore.SPSR[bank | 0] = spsr | 0; +} +ARMInstructionSet.prototype.MSR3 = function () { + var operand = this.imm() | 0; + this.branchFlags.setNZCV(operand | 0); +} +ARMInstructionSet.prototype.MSR4 = function () { + var operand = this.imm() >> 20; + var bank = 1; + switch (this.CPUCore.modeFlags & 0x1F) { + case 0x12: //IRQ + break; + case 0x13: //Supervisor + bank = 2; + break; + case 0x11: //FIQ + bank = 0; + break; + case 0x17: //Abort + bank = 3; + break; + case 0x1B: //Undefined + bank = 4; + break; + default: + return; + } + var spsr = this.CPUCore.SPSR[bank | 0] & 0xFF; + this.CPUCore.SPSR[bank | 0] = spsr | (operand & 0xF00); +} +ARMInstructionSet.prototype.MUL = function () { + //Perform multiplication: + var result = this.performMUL32() | 0; + //Update destination register: + this.multiplyGuard16OffsetRegisterWrite(result | 0); +} +ARMInstructionSet.prototype.MULS = function () { + //Perform multiplication: + var result = this.performMUL32() | 0; + this.branchFlags.setCarryFalse(); + this.branchFlags.setNZInt(result | 0); + //Update destination register and guard CPSR for PC: + this.multiplyGuard16OffsetRegisterWrite(result | 0); +} +ARMInstructionSet.prototype.MLA = function () { + //Perform multiplication: + var result = this.performMUL32MLA() | 0; + //Perform addition: + result = ((result | 0) + (this.read12OffsetRegister() | 0)) | 0; + //Update destination register: + this.multiplyGuard16OffsetRegisterWrite(result | 0); +} +ARMInstructionSet.prototype.MLAS = function () { + //Perform multiplication: + var result = this.performMUL32MLA() | 0; + //Perform addition: + result = ((result | 0) + (this.read12OffsetRegister() | 0)) | 0; + this.branchFlags.setCarryFalse(); + this.branchFlags.setNZInt(result | 0); + //Update destination register and guard CPSR for PC: + this.multiplyGuard16OffsetRegisterWrite(result | 0); +} +ARMInstructionSet.prototype.UMULL = function () { + //Perform multiplication: + this.CPUCore.performUMUL64(this.read0OffsetRegister() | 0, this.read8OffsetRegister() | 0); + //Update destination register: + this.multiplyGuard16OffsetRegisterWrite(this.CPUCore.mul64ResultHigh | 0); + this.multiplyGuard12OffsetRegisterWrite(this.CPUCore.mul64ResultLow | 0); +} +ARMInstructionSet.prototype.UMULLS = function () { + //Perform multiplication: + this.CPUCore.performUMUL64(this.read0OffsetRegister() | 0, this.read8OffsetRegister() | 0); + this.branchFlags.setCarryFalse(); + this.branchFlags.setNegative(this.CPUCore.mul64ResultHigh | 0); + this.branchFlags.setZero(this.CPUCore.mul64ResultHigh | this.CPUCore.mul64ResultLow); + //Update destination register and guard CPSR for PC: + this.multiplyGuard16OffsetRegisterWrite(this.CPUCore.mul64ResultHigh | 0); + this.multiplyGuard12OffsetRegisterWrite(this.CPUCore.mul64ResultLow | 0); +} +ARMInstructionSet.prototype.UMLAL = function () { + //Perform multiplication: + this.CPUCore.performUMLA64(this.read0OffsetRegister() | 0, this.read8OffsetRegister() | 0, this.read16OffsetRegister() | 0, this.read12OffsetRegister() | 0); + //Update destination register: + this.multiplyGuard16OffsetRegisterWrite(this.CPUCore.mul64ResultHigh | 0); + this.multiplyGuard12OffsetRegisterWrite(this.CPUCore.mul64ResultLow | 0); +} +ARMInstructionSet.prototype.UMLALS = function () { + //Perform multiplication: + this.CPUCore.performUMLA64(this.read0OffsetRegister() | 0, this.read8OffsetRegister() | 0, this.read16OffsetRegister() | 0, this.read12OffsetRegister() | 0); + this.branchFlags.setCarryFalse(); + this.branchFlags.setNegative(this.CPUCore.mul64ResultHigh | 0); + this.branchFlags.setZero(this.CPUCore.mul64ResultHigh | this.CPUCore.mul64ResultLow); + //Update destination register and guard CPSR for PC: + this.multiplyGuard16OffsetRegisterWrite(this.CPUCore.mul64ResultHigh | 0); + this.multiplyGuard12OffsetRegisterWrite(this.CPUCore.mul64ResultLow | 0); +} +ARMInstructionSet.prototype.SMULL = function () { + //Perform multiplication: + this.CPUCore.performMUL64(this.read0OffsetRegister() | 0, this.read8OffsetRegister() | 0); + //Update destination register: + this.multiplyGuard16OffsetRegisterWrite(this.CPUCore.mul64ResultHigh | 0); + this.multiplyGuard12OffsetRegisterWrite(this.CPUCore.mul64ResultLow | 0); +} +ARMInstructionSet.prototype.SMULLS = function () { + //Perform multiplication: + this.CPUCore.performMUL64(this.read0OffsetRegister() | 0, this.read8OffsetRegister() | 0); + this.branchFlags.setCarryFalse(); + this.branchFlags.setNegative(this.CPUCore.mul64ResultHigh | 0); + this.branchFlags.setZero(this.CPUCore.mul64ResultHigh | this.CPUCore.mul64ResultLow); + //Update destination register and guard CPSR for PC: + this.multiplyGuard16OffsetRegisterWrite(this.CPUCore.mul64ResultHigh | 0); + this.multiplyGuard12OffsetRegisterWrite(this.CPUCore.mul64ResultLow | 0); +} +ARMInstructionSet.prototype.SMLAL = function () { + //Perform multiplication: + this.CPUCore.performMLA64(this.read0OffsetRegister() | 0, this.read8OffsetRegister() | 0, this.read16OffsetRegister() | 0, this.read12OffsetRegister() | 0); + //Update destination register: + this.multiplyGuard16OffsetRegisterWrite(this.CPUCore.mul64ResultHigh | 0); + this.multiplyGuard12OffsetRegisterWrite(this.CPUCore.mul64ResultLow | 0); +} +ARMInstructionSet.prototype.SMLALS = function () { + //Perform multiplication: + this.CPUCore.performMLA64(this.read0OffsetRegister() | 0, this.read8OffsetRegister() | 0, this.read16OffsetRegister() | 0, this.read12OffsetRegister() | 0); + this.branchFlags.setCarryFalse(); + this.branchFlags.setNegative(this.CPUCore.mul64ResultHigh | 0); + this.branchFlags.setZero(this.CPUCore.mul64ResultHigh | this.CPUCore.mul64ResultLow); + //Update destination register and guard CPSR for PC: + this.multiplyGuard16OffsetRegisterWrite(this.CPUCore.mul64ResultHigh | 0); + this.multiplyGuard12OffsetRegisterWrite(this.CPUCore.mul64ResultLow | 0); +} +ARMInstructionSet.prototype.STRH = function () { + //Perform halfword store calculations: + var address = this.operand2OP_LoadStore1() | 0; + //Write to memory location: + this.CPUCore.write16(address | 0, this.guard12OffsetRegisterRead() | 0); +} +ARMInstructionSet.prototype.LDRH = function () { + //Perform halfword load calculations: + var address = this.operand2OP_LoadStore1() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite(this.CPUCore.read16(address | 0) | 0); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDRSH = function () { + //Perform signed halfword load calculations: + var address = this.operand2OP_LoadStore1() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite((this.CPUCore.read16(address | 0) << 16) >> 16); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDRSB = function () { + //Perform signed byte load calculations: + var address = this.operand2OP_LoadStore1() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite((this.CPUCore.read8(address | 0) << 24) >> 24); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.STRH2 = function () { + //Perform halfword store calculations: + var address = this.operand2OP_LoadStore2() | 0; + //Write to memory location: + this.CPUCore.write16(address | 0, this.guard12OffsetRegisterRead() | 0); +} +ARMInstructionSet.prototype.LDRH2 = function () { + //Perform halfword load calculations: + var address = this.operand2OP_LoadStore2() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite(this.CPUCore.read16(address | 0) | 0); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDRSH2 = function () { + //Perform signed halfword load calculations: + var address = this.operand2OP_LoadStore2() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite((this.CPUCore.read16(address | 0) << 16) >> 16); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDRSB2 = function () { + //Perform signed byte load calculations: + var address = this.operand2OP_LoadStore2() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite((this.CPUCore.read8(address | 0) << 24) >> 24); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.STR = function () { + //Perform word store calculations: + var address = this.operand2OP_LoadStore3Normal() | 0; + //Write to memory location: + this.CPUCore.write32(address | 0, this.guard12OffsetRegisterRead() | 0); +} +ARMInstructionSet.prototype.LDR = function () { + //Perform word load calculations: + var address = this.operand2OP_LoadStore3Normal() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite(this.CPUCore.read32(address | 0) | 0); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.STRB = function () { + //Perform byte store calculations: + var address = this.operand2OP_LoadStore3Normal() | 0; + //Write to memory location: + this.CPUCore.write8(address | 0, this.guard12OffsetRegisterRead() | 0); +} +ARMInstructionSet.prototype.LDRB = function () { + //Perform byte store calculations: + var address = this.operand2OP_LoadStore3Normal() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite(this.CPUCore.read8(address | 0) | 0); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.STR4 = function () { + //Perform word store calculations: + var address = this.operand2OP_LoadStore4() | 0; + //Write to memory location: + this.CPUCore.write32(address | 0, this.guard12OffsetRegisterRead() | 0); +} +ARMInstructionSet.prototype.LDR4 = function () { + //Perform word load calculations: + var address = this.operand2OP_LoadStore4() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite(this.CPUCore.read32(address | 0) | 0); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.STRB4 = function () { + //Perform byte store calculations: + var address = this.operand2OP_LoadStore4() | 0; + //Write to memory location: + this.CPUCore.write8(address | 0, this.guard12OffsetRegisterRead() | 0); +} +ARMInstructionSet.prototype.LDRB4 = function () { + //Perform byte store calculations: + var address = this.operand2OP_LoadStore4() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite(this.CPUCore.read8(address | 0) | 0); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.STRT = function () { + //Perform word store calculations (forced user-mode): + var address = this.operand2OP_LoadStore3User() | 0; + //Write to memory location: + this.CPUCore.write32(address | 0, this.guard12OffsetRegisterRead() | 0); +} +ARMInstructionSet.prototype.LDRT = function () { + //Perform word load calculations (forced user-mode): + var address = this.operand2OP_LoadStore3User() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite(this.CPUCore.read32(address | 0) | 0); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.STRBT = function () { + //Perform byte store calculations (forced user-mode): + var address = this.operand2OP_LoadStore3User() | 0; + //Write to memory location: + this.CPUCore.write8(address | 0, this.guard12OffsetRegisterRead() | 0); +} +ARMInstructionSet.prototype.LDRBT = function () { + //Perform byte load calculations (forced user-mode): + var address = this.operand2OP_LoadStore3User() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite(this.CPUCore.read8(address | 0) | 0); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.STR2 = function () { + //Perform word store calculations: + var address = this.operand2OP_LoadStore5Normal() | 0; + //Write to memory location: + this.CPUCore.write32(address | 0, this.guard12OffsetRegisterRead() | 0); +} +ARMInstructionSet.prototype.LDR2 = function () { + //Perform word load calculations: + var address = this.operand2OP_LoadStore5Normal() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite(this.CPUCore.read32(address | 0) | 0); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.STRB2 = function () { + //Perform byte store calculations: + var address = this.operand2OP_LoadStore5Normal() | 0; + //Write to memory location: + this.CPUCore.write8(address | 0, this.guard12OffsetRegisterRead() | 0); +} +ARMInstructionSet.prototype.LDRB2 = function () { + //Perform byte store calculations: + var address = this.operand2OP_LoadStore5Normal() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite(this.CPUCore.read8(address | 0) | 0); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.STRT2 = function () { + //Perform word store calculations (forced user-mode): + var address = this.operand2OP_LoadStore5User() | 0; + //Write to memory location: + this.CPUCore.write32(address | 0, this.guard12OffsetRegisterRead() | 0); +} +ARMInstructionSet.prototype.LDRT2 = function () { + //Perform word load calculations (forced user-mode): + var address = this.operand2OP_LoadStore5User() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite(this.CPUCore.read32(address | 0) | 0); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.STRBT2 = function () { + //Perform byte store calculations (forced user-mode): + var address = this.operand2OP_LoadStore5User() | 0; + //Write to memory location: + this.CPUCore.write8(address | 0, this.guard12OffsetRegisterRead() | 0); +} +ARMInstructionSet.prototype.LDRBT2 = function () { + //Perform byte load calculations (forced user-mode): + var address = this.operand2OP_LoadStore5User() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite(this.CPUCore.read8(address | 0) | 0); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.STR3 = function () { + //Perform word store calculations: + var address = this.operand2OP_LoadStore6() | 0; + //Write to memory location: + this.CPUCore.write32(address | 0, this.guard12OffsetRegisterRead() | 0); +} +ARMInstructionSet.prototype.LDR3 = function () { + //Perform word load calculations: + var address = this.operand2OP_LoadStore6() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite(this.CPUCore.read32(address | 0) | 0); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.STRB3 = function () { + //Perform byte store calculations: + var address = this.operand2OP_LoadStore6() | 0; + //Write to memory location: + this.CPUCore.write8(address | 0, this.guard12OffsetRegisterRead() | 0); +} +ARMInstructionSet.prototype.LDRB3 = function () { + //Perform byte store calculations: + var address = this.operand2OP_LoadStore6() | 0; + //Read from memory location: + this.guard12OffsetRegisterWrite(this.CPUCore.read8(address | 0) | 0); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.STMIA = function () { + //Only initialize the STMIA sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + this.memory.memoryWrite32(currentAddress | 0, this.readRegister(rListPosition | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMIAW = function () { + //Only initialize the STMIA sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + var finalAddress = this.getPositiveOffsetStartAddress(currentAddress | 0) | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + var count = 0; + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + this.memory.memoryWrite32(currentAddress | 0, this.readRegister(rListPosition | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + //Compute writeback immediately after the first register load: + if ((count | 0) == 0) { + count = 1; + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(finalAddress | 0); + } + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMDA = function () { + //Only initialize the STMDA sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Get offset start address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + for (var rListPosition = 0; (rListPosition | 0) < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.memory.memoryWrite32(currentAddress | 0, this.readRegister(rListPosition | 0) | 0); + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMDAW = function () { + //Only initialize the STMDA sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Get offset start address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + var finalAddress = currentAddress | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + var count = 0; + for (var rListPosition = 0; (rListPosition | 0) < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.memory.memoryWrite32(currentAddress | 0, this.readRegister(rListPosition | 0) | 0); + //Compute writeback immediately after the first register load: + if ((count | 0) == 0) { + count = 1; + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(finalAddress | 0); + } + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMIB = function () { + //Only initialize the STMIB sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.memory.memoryWrite32(currentAddress | 0, this.readRegister(rListPosition | 0) | 0); + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMIBW = function () { + //Only initialize the STMIB sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + var finalAddress = this.getPositiveOffsetStartAddress(currentAddress | 0) | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + var count = 0; + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.memory.memoryWrite32(currentAddress | 0, this.readRegister(rListPosition | 0) | 0); + //Compute writeback immediately after the first register load: + if ((count | 0) == 0) { + count = 1; + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(finalAddress | 0); + } + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMDB = function () { + //Only initialize the STMDB sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Get offset start address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + for (var rListPosition = 0; (rListPosition | 0) < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + this.memory.memoryWrite32(currentAddress | 0, this.readRegister(rListPosition | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMDBW = function () { + //Only initialize the STMDB sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Get offset start address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + var finalAddress = currentAddress | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + var count = 0; + for (var rListPosition = 0; (rListPosition | 0) < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + this.memory.memoryWrite32(currentAddress | 0, this.readRegister(rListPosition | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + //Compute writeback immediately after the first register load: + if ((count | 0) == 0) { + count = 1; + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(finalAddress | 0); + } + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMIAG = function () { + //Only initialize the STMIA sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterRead(rListPosition | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMIAWG = function () { + //Only initialize the STMIA sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + var finalAddress = this.getPositiveOffsetStartAddress(currentAddress | 0) | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + var count = 0; + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterRead(rListPosition | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + //Compute writeback immediately after the first register load: + if ((count | 0) == 0) { + count = 1; + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(finalAddress | 0); + } + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMDAG = function () { + //Only initialize the STMDA sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Get offset start address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + for (var rListPosition = 0; (rListPosition | 0) < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterRead(rListPosition | 0) | 0); + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMDAWG = function () { + //Only initialize the STMDA sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Get offset start address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + var finalAddress = currentAddress | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + var count = 0; + for (var rListPosition = 0; (rListPosition | 0) < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterRead(rListPosition | 0) | 0); + //Compute writeback immediately after the first register load: + if ((count | 0) == 0) { + count = 1; + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(finalAddress | 0); + } + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMIBG = function () { + //Only initialize the STMIB sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterRead(rListPosition | 0) | 0); + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMIBWG = function () { + //Only initialize the STMIB sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + var finalAddress = this.getPositiveOffsetStartAddress(currentAddress | 0) | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + var count = 0; + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterRead(rListPosition | 0) | 0); + //Compute writeback immediately after the first register load: + if ((count | 0) == 0) { + count = 1; + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(finalAddress | 0); + } + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMDBG = function () { + //Only initialize the STMDB sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Get offset start address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + for (var rListPosition = 0; (rListPosition | 0) < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterRead(rListPosition | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.STMDBWG = function () { + //Only initialize the STMDB sequence if the register list is non-empty: + if ((this.execute & 0xFFFF) > 0) { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Get offset start address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + var finalAddress = currentAddress | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + var count = 0; + for (var rListPosition = 0; (rListPosition | 0) < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterRead(rListPosition | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + //Compute writeback immediately after the first register load: + if ((count | 0) == 0) { + count = 1; + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(finalAddress | 0); + } + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } +} +ARMInstructionSet.prototype.LDMIA = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Load register(s) from memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + this.guardRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + } + else { + //Empty reglist loads PC: + this.guardRegisterWriteLDM(0xF, this.memory.memoryRead32(currentAddress | 0) | 0); + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMIAW = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Load register(s) from memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + this.guardRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + } + else { + //Empty reglist loads PC: + this.guardRegisterWriteLDM(0xF, this.memory.memoryRead32(currentAddress | 0) | 0); + currentAddress = ((currentAddress | 0) + 0x40) | 0; + } + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(currentAddress | 0); + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMDA = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Get the offset address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + //Load register(s) from memory: + for (var rListPosition = 0; (rListPosition | 0) < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.guardRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } + else { + //Empty reglist loads PC: + this.guardRegisterWriteLDM(0xF, this.memory.memoryRead32(currentAddress | 0) | 0); + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMDAW = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Get the offset address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + var writebackAddress = currentAddress | 0; + //Load register(s) from memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.guardRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + } + } + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(writebackAddress | 0); + } + else { + //Empty reglist loads PC: + this.guardRegisterWriteLDM(0xF, this.memory.memoryRead32(currentAddress | 0) | 0); + currentAddress = ((currentAddress | 0) - 0x40) | 0; + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(currentAddress | 0); + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMIB = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Load register(s) from memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.guardRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + } + } + } + else { + //Empty reglist loads PC: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.guardRegisterWriteLDM(0xF, this.memory.memoryRead32(currentAddress | 0) | 0); + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMIBW = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Load register(s) from memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.guardRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + } + } + } + else { + //Empty reglist loads PC: + currentAddress = ((currentAddress | 0) + 0x40) | 0; + this.guardRegisterWriteLDM(0xF, this.memory.memoryRead32(currentAddress | 0) | 0); + } + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(currentAddress | 0); + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMDB = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Get the offset address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + //Load register(s) from memory: + for (var rListPosition = 0; (rListPosition | 0) < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + this.guardRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + } + else { + //Empty reglist loads PC: + currentAddress = ((currentAddress | 0) - 4) | 0; + this.guardRegisterWriteLDM(0xF, this.memory.memoryRead32(currentAddress | 0) | 0); + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMDBW = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Get the offset address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + var writebackAddress = currentAddress | 0; + //Load register(s) from memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + this.guardRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(writebackAddress | 0); + } + else { + //Empty reglist loads PC: + currentAddress = ((currentAddress | 0) - 0x40) | 0; + this.guardRegisterWriteLDM(0xF, this.memory.memoryRead32(currentAddress | 0) | 0); + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(currentAddress | 0); + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMIAG = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Load register(s) from memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + this.guardUserRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + } + else { + //Empty reglist loads PC: + this.guardProgramCounterRegisterWriteCPSR(this.memory.memoryRead32(currentAddress | 0) | 0); + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMIAWG = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Load register(s) from memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + this.guardUserRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + } + else { + //Empty reglist loads PC: + this.guardProgramCounterRegisterWriteCPSR(this.memory.memoryRead32(currentAddress | 0) | 0); + currentAddress = ((currentAddress | 0) + 0x40) | 0; + } + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(currentAddress | 0); + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMDAG = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Get the offset address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Load register(s) from memory: + for (var rListPosition = 0; (rListPosition | 0) < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.guardUserRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + } + } + } + else { + //Empty reglist loads PC: + this.guardUserRegisterWriteLDM(0xF, this.memory.memoryRead32(currentAddress | 0) | 0); + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMDAWG = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Get the offset address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + var writebackAddress = currentAddress | 0; + //Load register(s) from memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.guardUserRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + } + } + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(writebackAddress | 0); + } + else { + //Empty reglist loads PC: + this.guardProgramCounterRegisterWriteCPSR(this.memory.memoryRead32(currentAddress | 0) | 0); + currentAddress = ((currentAddress | 0) - 0x40) | 0; + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(currentAddress | 0); + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMIBG = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Load register(s) from memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.guardUserRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + } + } + } + else { + //Empty reglist loads PC: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.guardProgramCounterRegisterWriteCPSR(this.memory.memoryRead32(currentAddress | 0) | 0); + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMIBWG = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Load register(s) from memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + currentAddress = ((currentAddress | 0) + 4) | 0; + this.guardUserRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + } + } + } + else { + //Empty reglist loads PC: + currentAddress = ((currentAddress | 0) + 0x40) | 0; + this.guardProgramCounterRegisterWriteCPSR(this.memory.memoryRead32(currentAddress | 0) | 0); + } + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(currentAddress | 0); + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMDBG = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Get the offset address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + //Load register(s) from memory: + for (var rListPosition = 0; (rListPosition | 0) < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + this.guardUserRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + } + else { + //Empty reglist loads PC: + currentAddress = ((currentAddress | 0) - 4) | 0; + this.guardProgramCounterRegisterWriteCPSR(this.memory.memoryRead32(currentAddress | 0) | 0); + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LDMDBWG = function () { + //Get the base address: + var currentAddress = this.read16OffsetRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + if ((this.execute & 0xFFFF) > 0) { + //Get the offset address: + currentAddress = this.getNegativeOffsetStartAddress(currentAddress | 0) | 0; + var writebackAddress = currentAddress | 0; + //Load register(s) from memory: + for (var rListPosition = 0; rListPosition < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + this.guardUserRegisterWriteLDM(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(writebackAddress | 0); + } + else { + //Empty reglist loads PC: + currentAddress = ((currentAddress | 0) - 0x40) | 0; + this.guardProgramCounterRegisterWriteCPSR(this.memory.memoryRead32(currentAddress | 0) | 0); + //Store the updated base address back into register: + this.guard16OffsetRegisterWrite(currentAddress | 0); + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +ARMInstructionSet.prototype.LoadStoreMultiple = function () { + this.incrementProgramCounter(); + switch ((this.execute >> 20) & 0x1F) { + case 0: + this.STMDA(); + break; + case 0x1: + this.LDMDA(); + break; + case 0x2: + this.STMDAW(); + break; + case 0x3: + this.LDMDAW(); + break; + case 0x4: + this.STMDAG(); + break; + case 0x5: + this.LDMDAG(); + break; + case 0x6: + this.STMDAWG(); + break; + case 0x7: + this.LDMDAWG(); + break; + case 0x8: + this.STMIA(); + break; + case 0x9: + this.LDMIA(); + break; + case 0xA: + this.STMIAW(); + break; + case 0xB: + this.LDMIAW(); + break; + case 0xC: + this.STMIAG(); + break; + case 0xD: + this.LDMIAG(); + break; + case 0xE: + this.STMIAWG(); + break; + case 0xF: + this.LDMIAWG(); + break; + case 0x10: + this.STMDB(); + break; + case 0x11: + this.LDMDB(); + break; + case 0x12: + this.STMDBW(); + break; + case 0x13: + this.LDMDBW(); + break; + case 0x14: + this.STMDBG(); + break; + case 0x15: + this.LDMDBG(); + break; + case 0x16: + this.STMDBWG(); + break; + case 0x17: + this.LDMDBWG(); + break; + case 0x18: + this.STMIB(); + break; + case 0x19: + this.LDMIB(); + break; + case 0x1A: + this.STMIBW(); + break; + case 0x1B: + this.LDMIBW(); + break; + case 0x1C: + this.STMIBG(); + break; + case 0x1D: + this.LDMIBG(); + break; + case 0x1E: + this.STMIBWG(); + break; + default: + this.LDMIBWG(); + } +} +ARMInstructionSet.prototype.SWP = function () { + var base = this.read16OffsetRegister() | 0; + var data = this.CPUCore.read32(base | 0) | 0; + //Clock a cycle for the processing delaying the CPU: + this.wait.CPUInternalSingleCyclePrefetch(); + this.CPUCore.write32(base | 0, this.read0OffsetRegister() | 0); + this.guard12OffsetRegisterWrite(data | 0); +} +ARMInstructionSet.prototype.SWPB = function () { + var base = this.read16OffsetRegister() | 0; + var data = this.CPUCore.read8(base | 0) | 0; + //Clock a cycle for the processing delaying the CPU: + this.wait.CPUInternalSingleCyclePrefetch(); + this.CPUCore.write8(base | 0, this.read0OffsetRegister() | 0); + this.guard12OffsetRegisterWrite(data | 0); +} +ARMInstructionSet.prototype.SWI = function () { + //Software Interrupt: + this.CPUCore.SWI(); +} +ARMInstructionSet.prototype.UNDEFINED = function () { + //Undefined Exception: + this.CPUCore.UNDEFINED(); +} +ARMInstructionSet.prototype.operand2OP_DataProcessing1 = function () { + var data = 0; + switch ((this.execute & 0x2000060) >> 5) { + case 0: + data = this.lli() | 0; + break; + case 1: + data = this.lri() | 0; + break; + case 2: + data = this.ari() | 0; + break; + case 3: + data = this.rri() | 0; + break; + default: + data = this.imm() | 0; + } + return data | 0; +} +ARMInstructionSet.prototype.operand2OP_DataProcessing2 = function () { + var data = 0; + switch ((this.execute & 0x2000060) >> 5) { + case 0: + data = this.llis() | 0; + break; + case 1: + data = this.lris() | 0; + break; + case 2: + data = this.aris() | 0; + break; + case 3: + data = this.rris() | 0; + break; + default: + data = this.imms() | 0; + } + return data | 0; +} +ARMInstructionSet.prototype.operand2OP_DataProcessing3 = function () { + var data = 0; + switch ((this.execute >> 5) & 0x3) { + case 0: + data = this.llr() | 0; + break; + case 1: + data = this.lrr() | 0; + break; + case 2: + data = this.arr() | 0; + break; + default: + data = this.rrr() | 0; + } + return data | 0; +} +ARMInstructionSet.prototype.operand2OP_DataProcessing4 = function () { + var data = 0; + switch ((this.execute >> 5) & 0x3) { + case 0: + data = this.llrs() | 0; + break; + case 1: + data = this.lrrs() | 0; + break; + case 2: + data = this.arrs() | 0; + break; + default: + data = this.rrrs() | 0; + } + return data | 0; +} +ARMInstructionSet.prototype.operand2OP_LoadStoreOffsetGen = function () { + var data = 0; + switch ((this.execute >> 5) & 0x3) { + case 0: + data = this.lli() | 0; + break; + case 1: + data = this.lri() | 0; + break; + case 2: + data = this.ari() | 0; + break; + default: + data = this.rri() | 0; + } + return data | 0; +} +ARMInstructionSet.prototype.operand2OP_LoadStoreOperandDetermine = function () { + var offset = 0; + if ((this.execute & 0x400000) == 0) { + offset = this.read0OffsetRegister() | 0; + } + else { + offset = ((this.execute & 0xF00) >> 4) | (this.execute & 0xF); + } + return offset | 0; +} +ARMInstructionSet.prototype.operand2OP_LoadStorePostTUser = function (offset) { + offset = offset | 0; + var base = this.read16OffsetUserRegister() | 0; + if ((this.execute & 0x800000) == 0) { + offset = ((base | 0) - (offset | 0)) | 0; + } + else { + offset = ((base | 0) + (offset | 0)) | 0; + } + this.guard16OffsetUserRegisterWrite(offset | 0); + return base | 0; +} +ARMInstructionSet.prototype.operand2OP_LoadStorePostTNormal = function (offset) { + offset = offset | 0; + var base = this.read16OffsetRegister() | 0; + if ((this.execute & 0x800000) == 0) { + offset = ((base | 0) - (offset | 0)) | 0; + } + else { + offset = ((base | 0) + (offset | 0)) | 0; + } + this.guard16OffsetRegisterWrite(offset | 0); + return base | 0; +} +ARMInstructionSet.prototype.operand2OP_LoadStoreNotT = function (offset) { + offset = offset | 0; + var base = this.read16OffsetRegister() | 0; + if ((this.execute & 0x800000) == 0) { + offset = ((base | 0) - (offset | 0)) | 0; + } + else { + offset = ((base | 0) + (offset | 0)) | 0; + } + if ((this.execute & 0x200000) != 0) { + this.guard16OffsetRegisterWrite(offset | 0); + } + return offset | 0; +} +ARMInstructionSet.prototype.operand2OP_LoadStore1 = function () { + return this.operand2OP_LoadStorePostTNormal(this.operand2OP_LoadStoreOperandDetermine() | 0) | 0; +} +ARMInstructionSet.prototype.operand2OP_LoadStore2 = function () { + return this.operand2OP_LoadStoreNotT(this.operand2OP_LoadStoreOperandDetermine() | 0) | 0; +} +ARMInstructionSet.prototype.operand2OP_LoadStore3Normal = function () { + return this.operand2OP_LoadStorePostTNormal(this.execute & 0xFFF) | 0; +} +ARMInstructionSet.prototype.operand2OP_LoadStore3User = function () { + return this.operand2OP_LoadStorePostTUser(this.execute & 0xFFF) | 0; +} +ARMInstructionSet.prototype.operand2OP_LoadStore4 = function () { + return this.operand2OP_LoadStoreNotT(this.execute & 0xFFF) | 0; +} +ARMInstructionSet.prototype.operand2OP_LoadStore5Normal = function () { + return this.operand2OP_LoadStorePostTNormal(this.operand2OP_LoadStoreOffsetGen() | 0) | 0; +} +ARMInstructionSet.prototype.operand2OP_LoadStore5User = function () { + return this.operand2OP_LoadStorePostTUser(this.operand2OP_LoadStoreOffsetGen() | 0) | 0; +} +ARMInstructionSet.prototype.operand2OP_LoadStore6 = function () { + return this.operand2OP_LoadStoreNotT(this.operand2OP_LoadStoreOffsetGen() | 0) | 0; +} +ARMInstructionSet.prototype.lli = function () { + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Shift the register data left: + var shifter = (this.execute >> 7) & 0x1F; + return register << (shifter | 0); +} +ARMInstructionSet.prototype.llis = function () { + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Get the shift amount: + var shifter = (this.execute >> 7) & 0x1F; + //Check to see if we need to update CPSR: + if ((shifter | 0) > 0) { + this.branchFlags.setCarry(register << (((shifter | 0) - 1) | 0)); + } + //Shift the register data left: + return register << (shifter | 0); +} +ARMInstructionSet.prototype.llr = function () { + //Logical Left Shift with Register: + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Clock a cycle for the shift delaying the CPU: + this.wait.CPUInternalSingleCyclePrefetch(); + //Shift the register data left: + var shifter = this.read8OffsetRegister() & 0xFF; + if ((shifter | 0) < 0x20) { + register = register << (shifter | 0); + } + else { + register = 0; + } + return register | 0; +} +ARMInstructionSet.prototype.llrs = function () { + //Logical Left Shift with Register and CPSR: + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Clock a cycle for the shift delaying the CPU: + this.wait.CPUInternalSingleCyclePrefetch(); + //Get the shift amount: + var shifter = this.read8OffsetRegister() & 0xFF; + //Check to see if we need to update CPSR: + if ((shifter | 0) > 0) { + if ((shifter | 0) < 0x20) { + //Shift the register data left: + this.branchFlags.setCarry(register << (((shifter | 0) - 1) | 0)); + register = register << (shifter | 0); + } + else { + if ((shifter | 0) == 0x20) { + //Shift bit 0 into carry: + this.branchFlags.setCarry(register << 31); + } + else { + //Everything Zero'd: + this.branchFlags.setCarryFalse(); + } + register = 0; + } + } + //If shift is 0, just return the register without mod: + return register | 0; +} +ARMInstructionSet.prototype.lri = function () { + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Shift the register data right logically: + var shifter = (this.execute >> 7) & 0x1F; + if ((shifter | 0) == 0) { + //Return 0: + register = 0; + } + else { + register = (register >>> (shifter | 0)) | 0; + } + return register | 0; +} +ARMInstructionSet.prototype.lris = function () { + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Get the shift amount: + var shifter = (this.execute >> 7) & 0x1F; + //Check to see if we need to update CPSR: + if ((shifter | 0) > 0) { + this.branchFlags.setCarry((register >> (((shifter | 0) - 1) | 0)) << 31); + //Shift the register data right logically: + register = (register >>> (shifter | 0)) | 0; + } + else { + this.branchFlags.setCarry(register | 0); + //Return 0: + register = 0; + } + return register | 0; +} +ARMInstructionSet.prototype.lrr = function () { + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Clock a cycle for the shift delaying the CPU: + this.wait.CPUInternalSingleCyclePrefetch(); + //Shift the register data right logically: + var shifter = this.read8OffsetRegister() & 0xFF; + if ((shifter | 0) < 0x20) { + register = (register >>> (shifter | 0)) | 0; + } + else { + register = 0; + } + return register | 0; +} +ARMInstructionSet.prototype.lrrs = function () { + //Logical Right Shift with Register and CPSR: + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Clock a cycle for the shift delaying the CPU: + this.wait.CPUInternalSingleCyclePrefetch(); + //Get the shift amount: + var shifter = this.read8OffsetRegister() & 0xFF; + //Check to see if we need to update CPSR: + if ((shifter | 0) > 0) { + if ((shifter | 0) < 0x20) { + //Shift the register data right logically: + this.branchFlags.setCarry((register >> (((shifter | 0) - 1) | 0)) << 31); + register = (register >>> (shifter | 0)) | 0; + } + else { + if ((shifter | 0) == 0x20) { + //Shift bit 31 into carry: + this.branchFlags.setCarry(register | 0); + } + else { + //Everything Zero'd: + this.branchFlags.setCarryFalse(); + } + register = 0; + } + } + //If shift is 0, just return the register without mod: + return register | 0; +} +ARMInstructionSet.prototype.ari = function () { + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Get the shift amount: + var shifter = (this.execute >> 7) & 0x1F; + if ((shifter | 0) == 0) { + //Shift full length if shifter is zero: + shifter = 0x1F; + } + //Shift the register data right: + return register >> (shifter | 0); +} +ARMInstructionSet.prototype.aris = function () { + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Get the shift amount: + var shifter = (this.execute >> 7) & 0x1F; + //Check to see if we need to update CPSR: + if ((shifter | 0) > 0) { + this.branchFlags.setCarry((register >> (((shifter | 0) - 1) | 0)) << 31); + } + else { + //Shift full length if shifter is zero: + shifter = 0x1F; + this.branchFlags.setCarry(register | 0); + } + //Shift the register data right: + return register >> (shifter | 0); +} +ARMInstructionSet.prototype.arr = function () { + //Arithmetic Right Shift with Register: + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Clock a cycle for the shift delaying the CPU: + this.wait.CPUInternalSingleCyclePrefetch(); + //Shift the register data right: + return register >> Math.min(this.read8OffsetRegister() & 0xFF, 0x1F); +} +ARMInstructionSet.prototype.arrs = function () { + //Arithmetic Right Shift with Register and CPSR: + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Clock a cycle for the shift delaying the CPU: + this.wait.CPUInternalSingleCyclePrefetch(); + //Get the shift amount: + var shifter = this.read8OffsetRegister() & 0xFF; + //Check to see if we need to update CPSR: + if ((shifter | 0) > 0) { + if ((shifter | 0) < 0x20) { + //Shift the register data right arithmetically: + this.branchFlags.setCarry((register >> (((shifter | 0) - 1) | 0)) << 31); + register = register >> (shifter | 0); + } + else { + //Set all bits with bit 31: + this.branchFlags.setCarry(register | 0); + register = register >> 0x1F; + } + } + //If shift is 0, just return the register without mod: + return register | 0; +} +ARMInstructionSet.prototype.rri = function () { + //Rotate Right with Immediate: + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Rotate the register right: + var shifter = (this.execute >> 7) & 0x1F; + if ((shifter | 0) > 0) { + //ROR + register = (register << (0x20 - (shifter | 0))) | (register >>> (shifter | 0)); + } + else { + //RRX + register = (this.branchFlags.getCarry() & 0x80000000) | (register >>> 0x1); + } + return register | 0; +} +ARMInstructionSet.prototype.rris = function () { + //Rotate Right with Immediate and CPSR: + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Rotate the register right: + var shifter = (this.execute >> 7) & 0x1F; + if ((shifter | 0) > 0) { + //ROR + this.branchFlags.setCarry((register >> (((shifter | 0) - 1) | 0)) << 31); + register = (register << (0x20 - (shifter | 0))) | (register >>> (shifter | 0)); + } + else { + //RRX + var rrxValue = (this.branchFlags.getCarry() & 0x80000000) | (register >>> 0x1); + this.branchFlags.setCarry(register << 31); + register = rrxValue | 0; + } + return register | 0; +} +ARMInstructionSet.prototype.rrr = function () { + //Rotate Right with Register: + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Clock a cycle for the shift delaying the CPU: + this.wait.CPUInternalSingleCyclePrefetch(); + //Rotate the register right: + var shifter = this.read8OffsetRegister() & 0x1F; + if ((shifter | 0) > 0) { + //ROR + register = (register << (0x20 - (shifter | 0))) | (register >>> (shifter | 0)); + } + //If shift is 0, just return the register without mod: + return register | 0; +} +ARMInstructionSet.prototype.rrrs = function () { + //Rotate Right with Register and CPSR: + //Get the register data to be shifted: + var register = this.read0OffsetRegister() | 0; + //Clock a cycle for the shift delaying the CPU: + this.wait.CPUInternalSingleCyclePrefetch(); + //Rotate the register right: + var shifter = this.read8OffsetRegister() & 0xFF; + if ((shifter | 0) > 0) { + shifter = shifter & 0x1F; + if ((shifter | 0) > 0) { + //ROR + this.branchFlags.setCarry((register >> (((shifter | 0) - 1) | 0)) << 31); + register = (register << (0x20 - (shifter | 0))) | (register >>> (shifter | 0)); + } + else { + //No shift, but make carry set to bit 31: + this.branchFlags.setCarry(register | 0); + } + } + //If shift is 0, just return the register without mod: + return register | 0; +} +ARMInstructionSet.prototype.imm = function () { + //Get the immediate data to be shifted: + var immediate = this.execute & 0xFF; + //Rotate the immediate right: + var shifter = (this.execute >> 7) & 0x1E; + if ((shifter | 0) > 0) { + immediate = (immediate << (0x20 - (shifter | 0))) | (immediate >>> (shifter | 0)); + } + return immediate | 0; +} +ARMInstructionSet.prototype.imms = function () { + //Get the immediate data to be shifted: + var immediate = this.execute & 0xFF; + //Rotate the immediate right: + var shifter = (this.execute >> 7) & 0x1E; + if ((shifter | 0) > 0) { + immediate = (immediate << (0x20 - (shifter | 0))) | (immediate >>> (shifter | 0)); + this.branchFlags.setCarry(immediate | 0); + } + return immediate | 0; +} +ARMInstructionSet.prototype.rc = function () { + return (this.branchFlags.getNZCV() | this.CPUCore.modeFlags); +} +ARMInstructionSet.prototype.rs = function () { + var spsr = 0; + switch (this.CPUCore.modeFlags & 0x1f) { + case 0x12: //IRQ + spsr = this.CPUCore.SPSR[1] | 0; + break; + case 0x13: //Supervisor + spsr = this.CPUCore.SPSR[2] | 0; + break; + case 0x11: //FIQ + spsr = this.CPUCore.SPSR[0] | 0; + break; + case 0x17: //Abort + spsr = this.CPUCore.SPSR[3] | 0; + break; + case 0x1B: //Undefined + spsr = this.CPUCore.SPSR[4] | 0; + break; + default: + //Instruction hit an invalid SPSR request: + return this.rc() | 0; + } + return ((spsr & 0xF00) << 20) | (spsr & 0xFF); +} +function compileARMInstructionDecodeMap() { + var pseudoCodes = [ + "LDRH", + "MOVS2", + "MUL", + "MSR", + "LDRSH", + "MVN2", + "SMLAL", + "RSCS", + "CMPS", + "MRS", + "RSBS2", + "ADDS", + "SUBS", + "RSB", + "SUBS2", + "MULS", + "SMLALS", + "STRB", + "CMNS2", + "UMLALS", + "ORR2", + "BX", + "RSBS", + "LDRSB", + "LoadStoreMultiple", + "ANDS2", + "BIC", + "ADD2", + "SBC2", + "AND", + "TSTS2", + "MOV2", + "MOVS", + "EOR", + "ORRS2", + "RSC", + "LDR2", + "SMULLS", + "LDRSB2", + "LDRB4", + "BL", + "LDRB3", + "SBCS2", + "MVNS2", + "MLAS", + "MVN", + "BICS2", + "UMLAL", + "CMPS2", + "LDRB", + "RSC2", + "ADC2", + "LDRSH2", + "ORR", + "ADDS2", + "EOR2", + "STR3", + "UMULL", + "ADD", + "LDRH2", + "STRB4", + "LDR4", + "EORS", + "ORRS", + "BICS", + "SMULL", + "EORS2", + "B", + "STR", + "STRH", + "TEQS", + "STR2", + "STRH2", + "AND2", + "SUB", + "MVNS", + "ADC", + "ADCS", + "MOV", + "CMNS", + "ADCS2", + "TSTS", + "RSCS2", + "ANDS", + "STRB3", + "SBC", + "STR4", + "LDR", + "LDR3", + "SUB2", + "STRB2", + "SWP", + "TEQS2", + "UMULLS", + "BIC2", + "MLA", + "SWI", + "LDRB2", + "SBCS", + "RSB2", + "SWPB", + "STRT", + "LDRT", + "STRBT", + "LDRBT", + "STRT2", + "LDRT2", + "STRBT2", + "LDRBT2" + ]; + function compileARMInstructionDecodeOpcodeMap(codeMap) { + var opcodeIndice = 0; + var instructionMap = getUint8Array(4096); + function generateMap1(instruction) { + for (var index = 0; index < 0x10; ++index) { + instructionMap[opcodeIndice++] = codeMap[instruction[index]]; + } + } + function generateMap2(instruction) { + var translatedOpcode = codeMap[instruction]; + for (var index = 0; index < 0x10; ++index) { + instructionMap[opcodeIndice++] = translatedOpcode; + } + } + function generateMap3(instruction) { + var translatedOpcode = codeMap[instruction]; + for (var index = 0; index < 0x100; ++index) { + instructionMap[opcodeIndice++] = translatedOpcode; + } + } + function generateMap4(instruction) { + var translatedOpcode = codeMap[instruction]; + for (var index = 0; index < 0x200; ++index) { + instructionMap[opcodeIndice++] = translatedOpcode; + } + } + function generateMap5(instruction) { + var translatedOpcode = codeMap[instruction]; + for (var index = 0; index < 0x300; ++index) { + instructionMap[opcodeIndice++] = translatedOpcode; + } + } + function generateStoreLoadInstructionSector1() { + var instrMap = [ + "STR2", + "LDR2", + "STRT2", + "LDRT2", + "STRB2", + "LDRB2", + "STRBT2", + "LDRBT2" + ]; + for (var instrIndex = 0; instrIndex < 0x10; ++instrIndex) { + for (var dataIndex = 0; dataIndex < 0x10; ++dataIndex) { + if ((dataIndex & 0x1) == 0) { + instructionMap[opcodeIndice++] = codeMap[instrMap[instrIndex & 0x7]]; + } + else { + instructionMap[opcodeIndice++] = codeMap["UNDEFINED"]; + } + } + } + } + function generateStoreLoadInstructionSector2() { + var instrMap = [ + "STR3", + "LDR3", + "STRB3", + "LDRB3" + ]; + for (var instrIndex = 0; instrIndex < 0x10; ++instrIndex) { + for (var dataIndex = 0; dataIndex < 0x10; ++dataIndex) { + if ((dataIndex & 0x1) == 0) { + instructionMap[opcodeIndice++] = codeMap[instrMap[((instrIndex >> 1) & 0x2) | (instrIndex & 0x1)]]; + } + else { + instructionMap[opcodeIndice++] = codeMap["UNDEFINED"]; + } + } + } + } + //0 + generateMap1([ + "AND", + "AND2", + "AND", + "AND2", + "AND", + "AND2", + "AND", + "AND2", + "AND", + "MUL", + "AND", + "STRH", + "AND", + "UNDEFINED", + "AND", + "UNDEFINED" + ]); + //1 + generateMap1([ + "ANDS", + "ANDS2", + "ANDS", + "ANDS2", + "ANDS", + "ANDS2", + "ANDS", + "ANDS2", + "ANDS", + "MULS", + "ANDS", + "LDRH", + "ANDS", + "LDRSB", + "ANDS", + "LDRSH" + ]); + //2 + generateMap1([ + "EOR", + "EOR2", + "EOR", + "EOR2", + "EOR", + "EOR2", + "EOR", + "EOR2", + "EOR", + "MLA", + "EOR", + "STRH", + "EOR", + "UNDEFINED", + "EOR", + "UNDEFINED" + ]); + //3 + generateMap1([ + "EORS", + "EORS2", + "EORS", + "EORS2", + "EORS", + "EORS2", + "EORS", + "EORS2", + "EORS", + "MLAS", + "EORS", + "LDRH", + "EORS", + "LDRSB", + "EORS", + "LDRSH" + ]); + //4 + generateMap1([ + "SUB", + "SUB2", + "SUB", + "SUB2", + "SUB", + "SUB2", + "SUB", + "SUB2", + "SUB", + "UNDEFINED", + "SUB", + "STRH", + "SUB", + "UNDEFINED", + "SUB", + "UNDEFINED" + ]); + //5 + generateMap1([ + "SUBS", + "SUBS2", + "SUBS", + "SUBS2", + "SUBS", + "SUBS2", + "SUBS", + "SUBS2", + "SUBS", + "UNDEFINED", + "SUBS", + "LDRH", + "SUBS", + "LDRSB", + "SUBS", + "LDRSH" + ]); + //6 + generateMap1([ + "RSB", + "RSB2", + "RSB", + "RSB2", + "RSB", + "RSB2", + "RSB", + "RSB2", + "RSB", + "UNDEFINED", + "RSB", + "STRH", + "RSB", + "UNDEFINED", + "RSB", + "UNDEFINED" + ]); + //7 + generateMap1([ + "RSBS", + "RSBS2", + "RSBS", + "RSBS2", + "RSBS", + "RSBS2", + "RSBS", + "RSBS2", + "RSBS", + "UNDEFINED", + "RSBS", + "LDRH", + "RSBS", + "LDRSB", + "RSBS", + "LDRSH" + ]); + //8 + generateMap1([ + "ADD", + "ADD2", + "ADD", + "ADD2", + "ADD", + "ADD2", + "ADD", + "ADD2", + "ADD", + "UMULL", + "ADD", + "STRH", + "ADD", + "UNDEFINED", + "ADD", + "UNDEFINED" + ]); + //9 + generateMap1([ + "ADDS", + "ADDS2", + "ADDS", + "ADDS2", + "ADDS", + "ADDS2", + "ADDS", + "ADDS2", + "ADDS", + "UMULLS", + "ADDS", + "LDRH", + "ADDS", + "LDRSB", + "ADDS", + "LDRSH" + ]); + //A + generateMap1([ + "ADC", + "ADC2", + "ADC", + "ADC2", + "ADC", + "ADC2", + "ADC", + "ADC2", + "ADC", + "UMLAL", + "ADC", + "STRH", + "ADC", + "UNDEFINED", + "ADC", + "UNDEFINED" + ]); + //B + generateMap1([ + "ADCS", + "ADCS2", + "ADCS", + "ADCS2", + "ADCS", + "ADCS2", + "ADCS", + "ADCS2", + "ADCS", + "UMLALS", + "ADCS", + "LDRH", + "ADCS", + "LDRSB", + "ADCS", + "LDRSH" + ]); + //C + generateMap1([ + "SBC", + "SBC2", + "SBC", + "SBC2", + "SBC", + "SBC2", + "SBC", + "SBC2", + "SBC", + "SMULL", + "SBC", + "STRH", + "SBC", + "UNDEFINED", + "SBC", + "UNDEFINED" + ]); + //D + generateMap1([ + "SBCS", + "SBCS2", + "SBCS", + "SBCS2", + "SBCS", + "SBCS2", + "SBCS", + "SBCS2", + "SBCS", + "SMULLS", + "SBCS", + "LDRH", + "SBCS", + "LDRSB", + "SBCS", + "LDRSH" + ]); + //E + generateMap1([ + "RSC", + "RSC2", + "RSC", + "RSC2", + "RSC", + "RSC2", + "RSC", + "RSC2", + "RSC", + "SMLAL", + "RSC", + "STRH", + "RSC", + "UNDEFINED", + "RSC", + "UNDEFINED" + ]); + //F + generateMap1([ + "RSCS", + "RSCS2", + "RSCS", + "RSCS2", + "RSCS", + "RSCS2", + "RSCS", + "RSCS2", + "RSCS", + "SMLALS", + "RSCS", + "LDRH", + "RSCS", + "LDRSB", + "RSCS", + "LDRSH" + ]); + //10 + generateMap1([ + "MRS", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "SWP", + "UNDEFINED", + "STRH2", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED" + ]); + //11 + generateMap1([ + "TSTS", + "TSTS2", + "TSTS", + "TSTS2", + "TSTS", + "TSTS2", + "TSTS", + "TSTS2", + "TSTS", + "UNDEFINED", + "TSTS", + "LDRH2", + "TSTS", + "LDRSB2", + "TSTS", + "LDRSH2" + ]); + //12 + generateMap1([ + "MSR", + "BX", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "STRH2", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED" + ]); + //13 + generateMap1([ + "TEQS", + "TEQS2", + "TEQS", + "TEQS2", + "TEQS", + "TEQS2", + "TEQS", + "TEQS2", + "TEQS", + "UNDEFINED", + "TEQS", + "LDRH2", + "TEQS", + "LDRSB2", + "TEQS", + "LDRSH2" + ]); + //14 + generateMap1([ + "MRS", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "SWPB", + "UNDEFINED", + "STRH2", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED" + ]); + //15 + generateMap1([ + "CMPS", + "CMPS2", + "CMPS", + "CMPS2", + "CMPS", + "CMPS2", + "CMPS", + "CMPS2", + "CMPS", + "UNDEFINED", + "CMPS", + "LDRH2", + "CMPS", + "LDRSB2", + "CMPS", + "LDRSH2" + ]); + //16 + generateMap1([ + "MSR", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "STRH2", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED", + "UNDEFINED" + ]); + //17 + generateMap1([ + "CMNS", + "CMNS2", + "CMNS", + "CMNS2", + "CMNS", + "CMNS2", + "CMNS", + "CMNS2", + "CMNS", + "UNDEFINED", + "CMNS", + "LDRH2", + "CMNS", + "LDRSB2", + "CMNS", + "LDRSH2" + ]); + //18 + generateMap1([ + "ORR", + "ORR2", + "ORR", + "ORR2", + "ORR", + "ORR2", + "ORR", + "ORR2", + "ORR", + "UNDEFINED", + "ORR", + "STRH2", + "ORR", + "UNDEFINED", + "ORR", + "UNDEFINED" + ]); + //19 + generateMap1([ + "ORRS", + "ORRS2", + "ORRS", + "ORRS2", + "ORRS", + "ORRS2", + "ORRS", + "ORRS2", + "ORRS", + "UNDEFINED", + "ORRS", + "LDRH2", + "ORRS", + "LDRSB2", + "ORRS", + "LDRSH2" + ]); + //1A + generateMap1([ + "MOV", + "MOV2", + "MOV", + "MOV2", + "MOV", + "MOV2", + "MOV", + "MOV2", + "MOV", + "UNDEFINED", + "MOV", + "STRH2", + "MOV", + "UNDEFINED", + "MOV", + "UNDEFINED" + ]); + //1B + generateMap1([ + "MOVS", + "MOVS2", + "MOVS", + "MOVS2", + "MOVS", + "MOVS2", + "MOVS", + "MOVS2", + "MOVS", + "UNDEFINED", + "MOVS", + "LDRH2", + "MOVS", + "LDRSB2", + "MOVS", + "LDRSH2" + ]); + //1C + generateMap1([ + "BIC", + "BIC2", + "BIC", + "BIC2", + "BIC", + "BIC2", + "BIC", + "BIC2", + "BIC", + "UNDEFINED", + "BIC", + "STRH2", + "BIC", + "UNDEFINED", + "BIC", + "UNDEFINED" + ]); + //1D + generateMap1([ + "BICS", + "BICS2", + "BICS", + "BICS2", + "BICS", + "BICS2", + "BICS", + "BICS2", + "BICS", + "UNDEFINED", + "BICS", + "LDRH2", + "BICS", + "LDRSB2", + "BICS", + "LDRSH2" + ]); + //1E + generateMap1([ + "MVN", + "MVN2", + "MVN", + "MVN2", + "MVN", + "MVN2", + "MVN", + "MVN2", + "MVN", + "UNDEFINED", + "MVN", + "STRH2", + "MVN", + "UNDEFINED", + "MVN", + "UNDEFINED" + ]); + //1F + generateMap1([ + "MVNS", + "MVNS2", + "MVNS", + "MVNS2", + "MVNS", + "MVNS2", + "MVNS", + "MVNS2", + "MVNS", + "UNDEFINED", + "MVNS", + "LDRH2", + "MVNS", + "LDRSB2", + "MVNS", + "LDRSH2" + ]); + //20 + generateMap2("AND"); + //21 + generateMap2("ANDS"); + //22 + generateMap2("EOR"); + //23 + generateMap2("EORS"); + //24 + generateMap2("SUB"); + //25 + generateMap2("SUBS"); + //26 + generateMap2("RSB"); + //27 + generateMap2("RSBS"); + //28 + generateMap2("ADD"); + //29 + generateMap2("ADDS"); + //2A + generateMap2("ADC"); + //2B + generateMap2("ADCS"); + //2C + generateMap2("SBC"); + //2D + generateMap2("SBCS"); + //2E + generateMap2("RSC"); + //2F + generateMap2("RSCS"); + //30 + generateMap2("UNDEFINED"); + //31 + generateMap2("TSTS"); + //32 + generateMap2("MSR"); + //33 + generateMap2("TEQS"); + //34 + generateMap2("UNDEFINED"); + //35 + generateMap2("CMPS"); + //36 + generateMap2("MSR"); + //37 + generateMap2("CMNS"); + //38 + generateMap2("ORR"); + //39 + generateMap2("ORRS"); + //3A + generateMap2("MOV"); + //3B + generateMap2("MOVS"); + //3C + generateMap2("BIC"); + //3D + generateMap2("BICS"); + //3E + generateMap2("MVN"); + //3F + generateMap2("MVNS"); + //40 + generateMap2("STR"); + //41 + generateMap2("LDR"); + //42 + generateMap2("STRT"); + //43 + generateMap2("LDRT"); + //44 + generateMap2("STRB"); + //45 + generateMap2("LDRB"); + //46 + generateMap2("STRBT"); + //47 + generateMap2("LDRBT"); + //48 + generateMap2("STR"); + //49 + generateMap2("LDR"); + //4A + generateMap2("STRT"); + //4B + generateMap2("LDRT"); + //4C + generateMap2("STRB"); + //4D + generateMap2("LDRB"); + //4E + generateMap2("STRBT"); + //4F + generateMap2("LDRBT"); + //50 + generateMap2("STR4"); + //51 + generateMap2("LDR4"); + //52 + generateMap2("STR4"); + //53 + generateMap2("LDR4"); + //54 + generateMap2("STRB4"); + //55 + generateMap2("LDRB4"); + //56 + generateMap2("STRB4"); + //57 + generateMap2("LDRB4"); + //58 + generateMap2("STR4"); + //59 + generateMap2("LDR4"); + //5A + generateMap2("STR4"); + //5B + generateMap2("LDR4"); + //5C + generateMap2("STRB4"); + //5D + generateMap2("LDRB4"); + //5E + generateMap2("STRB4"); + //5F + generateMap2("LDRB4"); + //60-6F + generateStoreLoadInstructionSector1(); + //70-7F + generateStoreLoadInstructionSector2(); + //80-9F + generateMap4("LoadStoreMultiple"); + //A0-AF + generateMap3("B"); + //B0-BF + generateMap3("BL"); + //C0-EF + generateMap5("UNDEFINED"); + //F0-FF + generateMap3("SWI"); + //Set to prototype: + ARMInstructionSet.prototype.instructionMap = instructionMap; + } + function compileARMInstructionDecodeOpcodeSwitch() { + var opcodeNameMap = {}; + //ARMInstructionSet.prototype.opcodeCount = []; + var opcodeCount = pseudoCodes.length | 0; + var code = "switch (this.instructionMap[((this.execute >> 16) & 0xFF0) | ((this.execute >> 4) & 0xF)] & 0xFF) {"; + for (var opcodeNumber = 0; (opcodeNumber | 0) < (opcodeCount | 0); opcodeNumber = ((opcodeNumber | 0) + 1) | 0) { + var opcodeName = pseudoCodes[opcodeNumber | 0]; + opcodeNameMap[opcodeName] = opcodeNumber | 0; + /*code += "case " + (opcodeNumber | 0) + ":{this." + opcodeName + "();++ARMInstructionSet.prototype.opcodeCount[" + opcodeNumber + "][1];break};"; + ARMInstructionSet.prototype.opcodeCount[opcodeNumber | 0] = [opcodeName, 0];*/ + code += "case " + (opcodeNumber | 0) + ":{this." + opcodeName + "();break};"; + } + code += "default:{this.UNDEFINED()}}"; + opcodeNameMap["UNDEFINED"] = opcodeNumber | 0; + ARMInstructionSet.prototype.executeDecoded = Function(code); + return opcodeNameMap; + } + compileARMInstructionDecodeOpcodeMap(compileARMInstructionDecodeOpcodeSwitch()); +} +compileARMInstructionDecodeMap(); +/*function sortPriority() { + var sorted = ARMInstructionSet.prototype.opcodeCount.sort(function(a, b) { + return b[1] - a[1]; + }); + var output = "[\n"; + var length = sorted.length - 1; + for (var index = 0; index <= length; index++) { + output += "\t\"" + sorted[index][0] + "\"" + ((index < length) ? "," : "") + "\n"; + } + output += "];"; + console.log(output); +}*/ diff --git a/public/gfiles/gba/IodineGBA/core/CPU/CPSR.js b/public/gfiles/gba/IodineGBA/core/CPU/CPSR.js new file mode 100644 index 00000000..a4acbcd7 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/CPU/CPSR.js @@ -0,0 +1,249 @@ +"use strict"; +/* + Copyright (C) 2012-2014 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function ARMCPSRAttributeTable() { + //"use asm"; + var negative = 0; + var zero = 1; + var carry = 0; + var overflow = 0; + function setNegative(toSet) { + toSet = toSet | 0; + negative = toSet | 0; + }; + function setNegativeFalse() { + negative = 0; + }; + function getNegative() { + return negative | 0; + }; + function setZero(toSet) { + toSet = toSet | 0; + zero = toSet | 0; + }; + function setZeroTrue() { + zero = 0; + }; + function setZeroFalse() { + zero = 1; + }; + function getZero() { + return zero | 0; + }; + function setOverflowTrue() { + overflow = -1; + }; + function setOverflowFalse() { + overflow = 0; + }; + function getOverflow() { + return overflow | 0; + }; + function setCarry(toSet) { + toSet = toSet | 0; + carry = toSet | 0; + }; + function setCarryFalse() { + carry = 0; + }; + function getCarry() { + return carry | 0; + }; + function getCarryReverse() { + return (~carry) | 0; + }; + function checkConditionalCode(execute) { + execute = execute | 0; + /* + Instruction Decode Pattern: + C = Conditional Code Bit; + X = Possible opcode bit; + N = Data Bit, definitely not an opcode bit + OPCODE: CCCCXXXXXXXXXXXXNNNNNNNNXXXXNNNN + + For this function, we decode the top 3 bits for the conditional code test: + */ + switch ((execute >>> 29) | 0) { + case 0x4: + if ((zero | 0) == 0) { + execute = -1; + break; + } + case 0x1: + execute = ~carry; + break; + case 0x2: + execute = ~negative; + break; + case 0x3: + execute = ~overflow; + break; + case 0x6: + if ((zero | 0) == 0) { + execute = -1; + break; + } + case 0x5: + execute = negative ^ overflow; + break; + case 0x0: + if ((zero | 0) != 0) { + execute = -1; + break; + } + default: + execute = 0; + } + return execute | 0; + }; + function setNZInt(toSet) { + toSet = toSet | 0; + negative = toSet | 0; + zero = toSet | 0; + }; + function setNZCV(toSet) { + toSet = toSet | 0; + negative = toSet | 0; + zero = (~toSet) & 0x40000000; + carry = toSet << 2; + overflow = toSet << 3; + }; + function getNZCV() { + var toSet = 0; + toSet = negative & 0x80000000; + if ((zero | 0) == 0) { + toSet = toSet | 0x40000000; + } + toSet = toSet | ((carry >>> 31) << 29); + toSet = toSet | ((overflow >>> 31) << 28); + return toSet | 0; + }; + function setADDFlags(operand1, operand2) { + //Update flags for an addition operation: + operand1 = operand1 | 0; + operand2 = operand2 | 0; + negative = ((operand1 | 0) + (operand2 | 0)) | 0; + zero = negative | 0; + if ((negative >>> 0) < (operand1 >>> 0)) { + carry = -1; + } + else { + carry = 0; + } + overflow = (~(operand1 ^ operand2)) & (operand1 ^ negative); + return negative | 0; + }; + function setADCFlags(operand1, operand2) { + //Update flags for an addition operation: + operand1 = operand1 | 0; + operand2 = operand2 | 0; + negative = ((operand1 | 0) + (operand2 | 0)) | 0; + negative = ((negative | 0) + (carry >>> 31)) | 0; + zero = negative | 0; + if ((negative >>> 0) < (operand1 >>> 0)) { + carry = -1; + } + else if ((negative >>> 0) > (operand1 >>> 0)) { + carry = 0; + } + overflow = (~(operand1 ^ operand2)) & (operand1 ^ negative); + return negative | 0; + }; + function setSUBFlags(operand1, operand2) { + //Update flags for a subtraction operation: + operand1 = operand1 | 0; + operand2 = operand2 | 0; + negative = ((operand1 | 0) - (operand2 | 0)) | 0; + zero = negative | 0; + if ((operand1 >>> 0) >= (operand2 >>> 0)) { + carry = -1; + } + else { + carry = 0; + } + overflow = (operand1 ^ operand2) & (operand1 ^ negative); + return negative | 0; + }; + function setSBCFlags(operand1, operand2) { + //Update flags for a subtraction operation: + operand1 = operand1 | 0; + operand2 = operand2 | 0; + negative = ((operand1 | 0) - (operand2 | 0)) | 0; + negative = ((negative | 0) - ((~carry) >>> 31)) | 0 + zero = negative | 0; + if ((negative >>> 0) < (operand1 >>> 0)) { + carry = -1; + } + else if ((negative >>> 0) > (operand1 >>> 0)) { + carry = 0; + } + overflow = (operand1 ^ operand2) & (operand1 ^ negative); + return negative | 0; + }; + function setCMPFlags(operand1, operand2) { + //Update flags for a subtraction operation: + operand1 = operand1 | 0; + operand2 = operand2 | 0; + negative = ((operand1 | 0) - (operand2 | 0)) | 0; + zero = negative | 0; + if ((operand1 >>> 0) >= (operand2 >>> 0)) { + carry = -1; + } + else { + carry = 0; + } + overflow = (operand1 ^ operand2) & (operand1 ^ negative); + }; + function setCMNFlags(operand1, operand2) { + //Update flags for an addition operation: + operand1 = operand1 | 0; + operand2 = operand2 | 0; + negative = ((operand1 | 0) + (operand2 | 0)) | 0; + zero = negative | 0; + if ((negative >>> 0) < (operand1 >>> 0)) { + carry = -1; + } + else { + carry = 0; + } + overflow = (~(operand1 ^ operand2)) & (operand1 ^ negative); + }; + function BGE() { + //Branch if Negative equal to Overflow + return (negative ^ overflow) | 0; + }; + return { + setNegative:setNegative, + setNegativeFalse:setNegativeFalse, + getNegative:getNegative, + setZero:setZero, + setZeroTrue:setZeroTrue, + setZeroFalse:setZeroFalse, + getZero:getZero, + setOverflowTrue:setOverflowTrue, + setOverflowFalse:setOverflowFalse, + getOverflow:getOverflow, + setCarry:setCarry, + setCarryFalse:setCarryFalse, + getCarry:getCarry, + getCarryReverse:getCarryReverse, + checkConditionalCode:checkConditionalCode, + setNZInt:setNZInt, + setNZCV:setNZCV, + getNZCV:getNZCV, + setADDFlags:setADDFlags, + setADCFlags:setADCFlags, + setSUBFlags:setSUBFlags, + setSBCFlags:setSBCFlags, + setCMPFlags:setCMPFlags, + setCMNFlags:setCMNFlags, + BGE:BGE + }; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/CPU/THUMB.js b/public/gfiles/gba/IodineGBA/core/CPU/THUMB.js new file mode 100644 index 00000000..8c9b2250 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/CPU/THUMB.js @@ -0,0 +1,1493 @@ +"use strict"; +/* + Copyright (C) 2012-2014 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function THUMBInstructionSet(CPUCore) { + this.CPUCore = CPUCore; + this.initialize(); +} +THUMBInstructionSet.prototype.initialize = function () { + this.wait = this.CPUCore.wait; + this.registers = this.CPUCore.registers; + this.branchFlags = this.CPUCore.branchFlags; + this.fetch = 0; + this.decode = 0; + this.execute = 0; + this.memory = this.CPUCore.memory; +} +THUMBInstructionSet.prototype.executeIteration = function () { + //Push the new fetch access: + this.fetch = this.memory.memoryReadCPU16(this.readPC() | 0) | 0; + //Execute Instruction: + this.executeDecoded(); + //Update the pipelining state: + this.execute = this.decode | 0; + this.decode = this.fetch | 0; +} +THUMBInstructionSet.prototype.executeDecoded = function () { + /* + Instruction Decode Pattern: + X = Possible opcode bit; N = Data Bit, definitely not an opcode bit + OPCODE: XXXXXXXXXXNNNNNN + + Since many of those "X"s are redundant and possibly data, we can "process" + it and use a table to further decide what unique opcode it is, leaving us with + a dense switch statement. Not "processing" the opcode beforehand would leave us + with a 10 bit wide switch, which is slow in JS, and using a function in array computed + goto trick is not optimal in JavaScript. + */ + switch (this.instructionMap[this.execute >> 6] & 0xFF) { //Leave the "& 0xFF" there, it's a uint8 type guard. + case 0: + this.CMPimm8(); + break; + case 1: + this.BEQ(); + break; + case 2: + this.MOVH_LH(); + break; + case 3: + this.LDRimm5(); + break; + case 4: + this.AND(); + break; + case 5: + this.LDRBimm5(); + break; + case 6: + this.LSLimm(); + break; + case 7: + this.LSRimm(); + break; + case 8: + this.MOVimm8(); + break; + case 9: + this.CMP(); + break; + case 10: + this.LDRSP(); + break; + case 11: + this.ADDimm3(); + break; + case 12: + this.ADDreg(); + break; + case 13: + this.STRSP(); + break; + case 14: + this.B(); + break; + case 15: + this.LDRPC(); + break; + case 16: + this.MOVH_HL(); + break; + case 17: + this.ADDimm8(); + break; + case 18: + this.SUBreg(); + break; + case 19: + this.BCC(); + break; + case 20: + this.STRimm5(); + break; + case 21: + this.ORR(); + break; + case 22: + this.LDRHimm5(); + break; + case 23: + this.BCS(); + break; + case 24: + this.BNE(); + break; + case 25: + this.BGE(); + break; + case 26: + this.POP(); + break; + case 27: + this.ADDH_HL(); + break; + case 28: + this.STRHimm5(); + break; + case 29: + this.BLE(); + break; + case 30: + this.ASRimm(); + break; + case 31: + this.MUL(); + break; + case 32: + this.BLsetup(); + break; + case 33: + this.BLoff(); + break; + case 34: + this.BGT(); + break; + case 35: + this.STRHreg(); + break; + case 36: + this.LDRHreg(); + break; + case 37: + this.BX_L(); + break; + case 38: + this.BLT(); + break; + case 39: + this.ADDSPimm7(); + break; + case 40: + this.PUSHlr(); + break; + case 41: + this.PUSH(); + break; + case 42: + this.SUBimm8(); + break; + case 43: + this.ROR(); + break; + case 44: + this.LDRSHreg(); + break; + case 45: + this.STRBimm5(); + break; + case 46: + this.NEG(); + break; + case 47: + this.BHI(); + break; + case 48: + this.TST(); + break; + case 49: + this.BX_H(); + break; + case 50: + this.STMIA(); + break; + case 51: + this.BLS(); + break; + case 52: + this.SWI(); + break; + case 53: + this.LDMIA(); + break; + case 54: + this.MOVH_HH(); + break; + case 55: + this.LSL(); + break; + case 56: + this.POPpc(); + break; + case 57: + this.LSR(); + break; + case 58: + this.CMPH_LH(); + break; + case 59: + this.EOR(); + break; + case 60: + this.SUBimm3(); + break; + case 61: + this.ADDH_LH(); + break; + case 62: + this.BPL(); + break; + case 63: + this.CMPH_HL(); + break; + case 64: + this.ADDPC(); + break; + case 65: + this.LDRSBreg(); + break; + case 66: + this.BIC(); + break; + case 67: + this.ADDSP(); + break; + case 68: + this.MVN(); + break; + case 69: + this.ASR(); + break; + case 70: + this.LDRreg(); + break; + case 71: + this.ADC(); + break; + case 72: + this.SBC(); + break; + case 73: + this.BMI(); + break; + case 74: + this.STRreg(); + break; + case 75: + this.CMN(); + break; + case 76: + this.LDRBreg(); + break; + case 77: + this.ADDH_HH(); + break; + case 78: + this.CMPH_HH(); + break; + case 79: + this.STRBreg(); + break; + case 80: + this.BVS(); + break; + case 81: + this.BVC(); + break; + default: + this.UNDEFINED(); + } +} +THUMBInstructionSet.prototype.executeBubble = function () { + //Push the new fetch access: + this.fetch = this.memory.memoryReadCPU16(this.readPC() | 0) | 0; + //Update the Program Counter: + this.incrementProgramCounter(); + //Update the pipelining state: + this.execute = this.decode | 0; + this.decode = this.fetch | 0; +} +THUMBInstructionSet.prototype.incrementProgramCounter = function () { + //Increment The Program Counter: + this.registers[15] = ((this.registers[15] | 0) + 2) | 0; +} +THUMBInstructionSet.prototype.readLowRegister = function (address) { + //Low register read: + address = address | 0; + return this.registers[address & 0x7] | 0; +} +THUMBInstructionSet.prototype.read0OffsetLowRegister = function () { + //Low register read at 0 bit offset: + return this.readLowRegister(this.execute | 0) | 0; +} +THUMBInstructionSet.prototype.read3OffsetLowRegister = function () { + //Low register read at 3 bit offset: + return this.readLowRegister(this.execute >> 3) | 0; +} +THUMBInstructionSet.prototype.read6OffsetLowRegister = function () { + //Low register read at 6 bit offset: + return this.readLowRegister(this.execute >> 6) | 0; +} +THUMBInstructionSet.prototype.read8OffsetLowRegister = function () { + //Low register read at 8 bit offset: + return this.readLowRegister(this.execute >> 8) | 0; +} +THUMBInstructionSet.prototype.readHighRegister = function (address) { + //High register read: + address = address | 0x8; + return this.registers[address & 0xF] | 0; +} +THUMBInstructionSet.prototype.writeLowRegister = function (address, data) { + //Low register write: + address = address | 0; + data = data | 0; + this.registers[address & 0x7] = data | 0; +} +THUMBInstructionSet.prototype.write0OffsetLowRegister = function (data) { + //Low register write at 0 bit offset: + data = data | 0; + this.writeLowRegister(this.execute | 0, data | 0); +} +THUMBInstructionSet.prototype.write8OffsetLowRegister = function (data) { + //Low register write at 8 bit offset: + data = data | 0; + this.writeLowRegister(this.execute >> 8, data | 0); +} +THUMBInstructionSet.prototype.guardHighRegisterWrite = function (data) { + data = data | 0; + var address = 0x8 | (this.execute & 0x7); + if ((address | 0) == 0xF) { + //We performed a branch: + this.CPUCore.branch(data & -2); + } + else { + //Regular Data Write: + this.registers[address & 0xF] = data | 0; + //Update PC: + this.incrementProgramCounter(); + } +} +THUMBInstructionSet.prototype.writeSP = function (data) { + //Update the stack pointer: + data = data | 0; + this.registers[0xD] = data | 0; +} +THUMBInstructionSet.prototype.SPDecrementWord = function () { + //Decrement the stack pointer by one word: + this.registers[0xD] = ((this.registers[0xD] | 0) - 4) | 0; +} +THUMBInstructionSet.prototype.SPIncrementWord = function () { + //Increment the stack pointer by one word: + this.registers[0xD] = ((this.registers[0xD] | 0) + 4) | 0; +} +THUMBInstructionSet.prototype.writeLR = function (data) { + //Update the link register: + data = data | 0; + this.registers[0xE] = data | 0; +} +THUMBInstructionSet.prototype.writePC = function (data) { + data = data | 0; + //We performed a branch: + //Update the program counter to branch address: + this.CPUCore.branch(data & -2); +} +THUMBInstructionSet.prototype.offsetPC = function () { + //We performed a branch: + //Update the program counter to branch address: + this.CPUCore.branch(((this.readPC() | 0) + ((this.execute << 24) >> 23)) | 0); +} +THUMBInstructionSet.prototype.getLR = function () { + //Read back the value for the LR register upon Exception: + return ((this.readPC() | 0) - 2) | 0; +} +THUMBInstructionSet.prototype.getIRQLR = function () { + //Read back the value for the LR register upon IRQ: + return this.readPC() | 0; +} +THUMBInstructionSet.prototype.readSP = function () { + //Read back the current SP: + return this.registers[0xD] | 0; +} +THUMBInstructionSet.prototype.readLR = function () { + //Read back the current LR: + return this.registers[0xE] | 0; +} +THUMBInstructionSet.prototype.readPC = function () { + //Read back the current PC: + return this.registers[0xF] | 0; +} +THUMBInstructionSet.prototype.getCurrentFetchValue = function () { + return this.fetch | (this.fetch << 16); +} +THUMBInstructionSet.prototype.getSWICode = function () { + return this.execute & 0xFF; +} +THUMBInstructionSet.prototype.LSLimm = function () { + var source = this.read3OffsetLowRegister() | 0; + var offset = (this.execute >> 6) & 0x1F; + if ((offset | 0) > 0) { + //CPSR Carry is set by the last bit shifted out: + this.branchFlags.setCarry((source << (((offset | 0) - 1) | 0)) | 0); + //Perform shift: + source = source << (offset | 0); + } + //Perform CPSR updates for N and Z (But not V): + this.branchFlags.setNZInt(source | 0); + //Update destination register: + this.write0OffsetLowRegister(source | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.LSRimm = function () { + var source = this.read3OffsetLowRegister() | 0; + var offset = (this.execute >> 6) & 0x1F; + if ((offset | 0) > 0) { + //CPSR Carry is set by the last bit shifted out: + this.branchFlags.setCarry((source >> (((offset | 0) - 1) | 0)) << 31); + //Perform shift: + source = (source >>> (offset | 0)) | 0; + } + else { + this.branchFlags.setCarry(source | 0); + source = 0; + } + //Perform CPSR updates for N and Z (But not V): + this.branchFlags.setNZInt(source | 0); + //Update destination register: + this.write0OffsetLowRegister(source | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.ASRimm = function () { + var source = this.read3OffsetLowRegister() | 0; + var offset = (this.execute >> 6) & 0x1F; + if ((offset | 0) > 0) { + //CPSR Carry is set by the last bit shifted out: + this.branchFlags.setCarry((source >> (((offset | 0) - 1) | 0)) << 31); + //Perform shift: + source = source >> (offset | 0); + } + else { + this.branchFlags.setCarry(source | 0); + source = source >> 0x1F; + } + //Perform CPSR updates for N and Z (But not V): + this.branchFlags.setNZInt(source | 0); + //Update destination register: + this.write0OffsetLowRegister(source | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.ADDreg = function () { + var operand1 = this.read3OffsetLowRegister() | 0; + var operand2 = this.read6OffsetLowRegister() | 0; + //Update destination register: + this.write0OffsetLowRegister(this.branchFlags.setADDFlags(operand1 | 0, operand2 | 0) | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.SUBreg = function () { + var operand1 = this.read3OffsetLowRegister() | 0; + var operand2 = this.read6OffsetLowRegister() | 0; + //Update destination register: + this.write0OffsetLowRegister(this.branchFlags.setSUBFlags(operand1 | 0, operand2 | 0) | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.ADDimm3 = function () { + var operand1 = this.read3OffsetLowRegister() | 0; + var operand2 = (this.execute >> 6) & 0x7; + //Update destination register: + this.write0OffsetLowRegister(this.branchFlags.setADDFlags(operand1 | 0, operand2 | 0) | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.SUBimm3 = function () { + var operand1 = this.read3OffsetLowRegister() | 0; + var operand2 = (this.execute >> 6) & 0x7; + //Update destination register: + this.write0OffsetLowRegister(this.branchFlags.setSUBFlags(operand1 | 0, operand2 | 0) | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.MOVimm8 = function () { + //Get the 8-bit value to move into the register: + var result = this.execute & 0xFF; + this.branchFlags.setNegativeFalse(); + this.branchFlags.setZero(result | 0); + //Update destination register: + this.write8OffsetLowRegister(result | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.CMPimm8 = function () { + //Compare an 8-bit immediate value with a register: + var operand1 = this.read8OffsetLowRegister() | 0; + var operand2 = this.execute & 0xFF; + this.branchFlags.setCMPFlags(operand1 | 0, operand2 | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.ADDimm8 = function () { + //Add an 8-bit immediate value with a register: + var operand1 = this.read8OffsetLowRegister() | 0; + var operand2 = this.execute & 0xFF; + this.write8OffsetLowRegister(this.branchFlags.setADDFlags(operand1 | 0, operand2 | 0) | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.SUBimm8 = function () { + //Subtract an 8-bit immediate value from a register: + var operand1 = this.read8OffsetLowRegister() | 0; + var operand2 = this.execute & 0xFF; + this.write8OffsetLowRegister(this.branchFlags.setSUBFlags(operand1 | 0, operand2 | 0) | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.AND = function () { + var source = this.read3OffsetLowRegister() | 0; + var destination = this.read0OffsetLowRegister() | 0; + //Perform bitwise AND: + var result = source & destination; + this.branchFlags.setNZInt(result | 0); + //Update destination register: + this.write0OffsetLowRegister(result | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.EOR = function () { + var source = this.read3OffsetLowRegister() | 0; + var destination = this.read0OffsetLowRegister() | 0; + //Perform bitwise EOR: + var result = source ^ destination; + this.branchFlags.setNZInt(result | 0); + //Update destination register: + this.write0OffsetLowRegister(result | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.LSL = function () { + var source = this.read3OffsetLowRegister() & 0xFF; + var destination = this.read0OffsetLowRegister() | 0; + //Check to see if we need to update CPSR: + if ((source | 0) > 0) { + if ((source | 0) < 0x20) { + //Shift the register data left: + this.branchFlags.setCarry(destination << (((source | 0) - 1) | 0)); + destination = destination << (source | 0); + } + else if ((source | 0) == 0x20) { + //Shift bit 0 into carry: + this.branchFlags.setCarry(destination << 31); + destination = 0; + } + else { + //Everything Zero'd: + this.branchFlags.setCarryFalse(); + destination = 0; + } + } + //Perform CPSR updates for N and Z (But not V): + this.branchFlags.setNZInt(destination | 0); + //Update destination register: + this.write0OffsetLowRegister(destination | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.LSR = function () { + var source = this.read3OffsetLowRegister() & 0xFF; + var destination = this.read0OffsetLowRegister() | 0; + //Check to see if we need to update CPSR: + if ((source | 0) > 0) { + if ((source | 0) < 0x20) { + //Shift the register data right logically: + this.branchFlags.setCarry((destination >> (((source | 0) - 1) | 0)) << 31); + destination = (destination >>> (source | 0)) | 0; + } + else if (source == 0x20) { + //Shift bit 31 into carry: + this.branchFlags.setCarry(destination | 0); + destination = 0; + } + else { + //Everything Zero'd: + this.branchFlags.setCarryFalse(); + destination = 0; + } + } + //Perform CPSR updates for N and Z (But not V): + this.branchFlags.setNZInt(destination | 0); + //Update destination register: + this.write0OffsetLowRegister(destination | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.ASR = function () { + var source = this.read3OffsetLowRegister() & 0xFF; + var destination = this.read0OffsetLowRegister() | 0; + //Check to see if we need to update CPSR: + if ((source | 0) > 0) { + if ((source | 0) < 0x20) { + //Shift the register data right arithmetically: + this.branchFlags.setCarry((destination >> (((source | 0) - 1) | 0)) << 31); + destination = destination >> (source | 0); + } + else { + //Set all bits with bit 31: + this.branchFlags.setCarry(destination | 0); + destination = destination >> 0x1F; + } + } + //Perform CPSR updates for N and Z (But not V): + this.branchFlags.setNZInt(destination | 0); + //Update destination register: + this.write0OffsetLowRegister(destination | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.ADC = function () { + var operand1 = this.read0OffsetLowRegister() | 0; + var operand2 = this.read3OffsetLowRegister() | 0; + //Update destination register: + this.write0OffsetLowRegister(this.branchFlags.setADCFlags(operand1 | 0, operand2 | 0) | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.SBC = function () { + var operand1 = this.read0OffsetLowRegister() | 0; + var operand2 = this.read3OffsetLowRegister() | 0; + //Update destination register: + this.write0OffsetLowRegister(this.branchFlags.setSBCFlags(operand1 | 0, operand2 | 0) | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.ROR = function () { + var source = this.read3OffsetLowRegister() & 0xFF; + var destination = this.read0OffsetLowRegister() | 0; + if ((source | 0) > 0) { + source = source & 0x1F; + if ((source | 0) > 0) { + //CPSR Carry is set by the last bit shifted out: + this.branchFlags.setCarry((destination >> ((source - 1) | 0)) << 31); + //Perform rotate: + destination = (destination << ((0x20 - (source | 0)) | 0)) | (destination >>> (source | 0)); + } + else { + this.branchFlags.setCarry(destination | 0); + } + } + //Perform CPSR updates for N and Z (But not V): + this.branchFlags.setNZInt(destination | 0); + //Update destination register: + this.write0OffsetLowRegister(destination | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.TST = function () { + var source = this.read3OffsetLowRegister() | 0; + var destination = this.read0OffsetLowRegister() | 0; + //Perform bitwise AND: + var result = source & destination; + this.branchFlags.setNZInt(result | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.NEG = function () { + var source = this.read3OffsetLowRegister() | 0; + if ((source | 0) != -0x80000000) { + //Perform Subtraction: + source = (-(source | 0)) | 0; + this.branchFlags.setOverflowFalse(); + } + else { + //Negation of MIN_INT overflows! + this.branchFlags.setOverflowTrue(); + } + this.branchFlags.setNZInt(source | 0); + //Update destination register: + this.write0OffsetLowRegister(source | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.CMP = function () { + //Compare two registers: + var operand1 = this.read0OffsetLowRegister() | 0; + var operand2 = this.read3OffsetLowRegister() | 0; + this.branchFlags.setCMPFlags(operand1 | 0, operand2 | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.CMN = function () { + //Compare two registers: + var operand1 = this.read0OffsetLowRegister() | 0; + var operand2 = this.read3OffsetLowRegister() | 0; + this.branchFlags.setCMNFlags(operand1 | 0, operand2 | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.ORR = function () { + var source = this.read3OffsetLowRegister() | 0; + var destination = this.read0OffsetLowRegister() | 0; + //Perform bitwise OR: + var result = source | destination; + this.branchFlags.setNZInt(result | 0); + //Update destination register: + this.write0OffsetLowRegister(result | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.MUL = function () { + var source = this.read3OffsetLowRegister() | 0; + var destination = this.read0OffsetLowRegister() | 0; + //Perform MUL32: + var result = this.CPUCore.performMUL32(source | 0, destination | 0, 0) | 0; + this.branchFlags.setCarryFalse(); + this.branchFlags.setNZInt(result | 0); + //Update destination register: + this.write0OffsetLowRegister(result | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.BIC = function () { + var source = this.read3OffsetLowRegister() | 0; + var destination = this.read0OffsetLowRegister() | 0; + //Perform bitwise AND with a bitwise NOT on source: + var result = (~source) & destination; + this.branchFlags.setNZInt(result | 0); + //Update destination register: + this.write0OffsetLowRegister(result | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.MVN = function () { + //Perform bitwise NOT on source: + var source = ~this.read3OffsetLowRegister(); + this.branchFlags.setNZInt(source | 0); + //Update destination register: + this.write0OffsetLowRegister(source | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.ADDH_LH = function () { + var operand1 = this.read0OffsetLowRegister() | 0; + var operand2 = this.readHighRegister(this.execute >> 3) | 0; + //Perform Addition: + //Update destination register: + this.write0OffsetLowRegister(((operand1 | 0) + (operand2 | 0)) | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.ADDH_HL = function () { + var operand1 = this.readHighRegister(this.execute | 0) | 0; + var operand2 = this.read3OffsetLowRegister() | 0; + //Perform Addition: + //Update destination register: + this.guardHighRegisterWrite(((operand1 | 0) + (operand2 | 0)) | 0); +} +THUMBInstructionSet.prototype.ADDH_HH = function () { + var operand1 = this.readHighRegister(this.execute | 0) | 0; + var operand2 = this.readHighRegister(this.execute >> 3) | 0; + //Perform Addition: + //Update destination register: + this.guardHighRegisterWrite(((operand1 | 0) + (operand2 | 0)) | 0); +} +THUMBInstructionSet.prototype.CMPH_LH = function () { + //Compare two registers: + var operand1 = this.read0OffsetLowRegister() | 0; + var operand2 = this.readHighRegister(this.execute >> 3) | 0; + this.branchFlags.setCMPFlags(operand1 | 0, operand2 | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.CMPH_HL = function () { + //Compare two registers: + var operand1 = this.readHighRegister(this.execute | 0) | 0; + var operand2 = this.read3OffsetLowRegister() | 0; + this.branchFlags.setCMPFlags(operand1 | 0, operand2 | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.CMPH_HH = function () { + //Compare two registers: + var operand1 = this.readHighRegister(this.execute | 0) | 0; + var operand2 = this.readHighRegister(this.execute >> 3) | 0; + this.branchFlags.setCMPFlags(operand1 | 0, operand2 | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.MOVH_LH = function () { + //Move a register to another register: + this.write0OffsetLowRegister(this.readHighRegister(this.execute >> 3) | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.MOVH_HL = function () { + //Move a register to another register: + this.guardHighRegisterWrite(this.read3OffsetLowRegister() | 0); +} +THUMBInstructionSet.prototype.MOVH_HH = function () { + //Move a register to another register: + this.guardHighRegisterWrite(this.readHighRegister(this.execute >> 3) | 0); +} +THUMBInstructionSet.prototype.BX_L = function () { + //Branch & eXchange: + var address = this.read3OffsetLowRegister() | 0; + if ((address & 0x1) == 0) { + //Enter ARM mode: + this.CPUCore.enterARM(); + this.CPUCore.branch(address & -0x4); + } + else { + //Stay in THUMB mode: + this.CPUCore.branch(address & -0x2); + } +} +THUMBInstructionSet.prototype.BX_H = function () { + //Branch & eXchange: + var address = this.readHighRegister(this.execute >> 3) | 0; + if ((address & 0x1) == 0) { + //Enter ARM mode: + this.CPUCore.enterARM(); + this.CPUCore.branch(address & -0x4); + } + else { + //Stay in THUMB mode: + this.CPUCore.branch(address & -0x2); + } +} +THUMBInstructionSet.prototype.LDRPC = function () { + //PC-Relative Load + var data = this.CPUCore.read32(((this.readPC() & -3) + ((this.execute & 0xFF) << 2)) | 0) | 0; + this.write8OffsetLowRegister(data | 0); + //Update PC: + this.incrementProgramCounter(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +THUMBInstructionSet.prototype.STRreg = function () { + //Store Word From Register + var address = ((this.read6OffsetLowRegister() | 0) + (this.read3OffsetLowRegister() | 0)) | 0; + this.CPUCore.write32(address | 0, this.read0OffsetLowRegister() | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.STRHreg = function () { + //Store Half-Word From Register + var address = ((this.read6OffsetLowRegister() | 0) + (this.read3OffsetLowRegister() | 0)) | 0; + this.CPUCore.write16(address | 0, this.read0OffsetLowRegister() | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.STRBreg = function () { + //Store Byte From Register + var address = ((this.read6OffsetLowRegister() | 0) + (this.read3OffsetLowRegister() | 0)) | 0; + this.CPUCore.write8(address | 0, this.read0OffsetLowRegister() | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.LDRSBreg = function () { + //Load Signed Byte Into Register + var data = (this.CPUCore.read8(((this.read6OffsetLowRegister() | 0) + (this.read3OffsetLowRegister() | 0)) | 0) << 24) >> 24; + this.write0OffsetLowRegister(data | 0); + //Update PC: + this.incrementProgramCounter(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +THUMBInstructionSet.prototype.LDRreg = function () { + //Load Word Into Register + var data = this.CPUCore.read32(((this.read6OffsetLowRegister() | 0) + (this.read3OffsetLowRegister() | 0)) | 0) | 0; + this.write0OffsetLowRegister(data | 0); + //Update PC: + this.incrementProgramCounter(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +THUMBInstructionSet.prototype.LDRHreg = function () { + //Load Half-Word Into Register + var data = this.CPUCore.read16(((this.read6OffsetLowRegister() | 0) + (this.read3OffsetLowRegister() | 0)) | 0) | 0; + this.write0OffsetLowRegister(data | 0); + //Update PC: + this.incrementProgramCounter(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +THUMBInstructionSet.prototype.LDRBreg = function () { + //Load Byte Into Register + var data = this.CPUCore.read8(((this.read6OffsetLowRegister() | 0) + (this.read3OffsetLowRegister() | 0)) | 0) | 0; + this.write0OffsetLowRegister(data | 0); + //Update PC: + this.incrementProgramCounter(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +THUMBInstructionSet.prototype.LDRSHreg = function () { + //Load Signed Half-Word Into Register + var data = (this.CPUCore.read16(((this.read6OffsetLowRegister() | 0) + (this.read3OffsetLowRegister() | 0)) | 0) << 16) >> 16; + this.write0OffsetLowRegister(data | 0); + //Update PC: + this.incrementProgramCounter(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +THUMBInstructionSet.prototype.STRimm5 = function () { + //Store Word From Register + var address = (((this.execute >> 4) & 0x7C) + (this.read3OffsetLowRegister() | 0)) | 0; + this.CPUCore.write32(address | 0, this.read0OffsetLowRegister() | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.LDRimm5 = function () { + //Load Word Into Register + var data = this.CPUCore.read32((((this.execute >> 4) & 0x7C) + (this.read3OffsetLowRegister() | 0)) | 0) | 0; + this.write0OffsetLowRegister(data | 0); + //Update PC: + this.incrementProgramCounter(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +THUMBInstructionSet.prototype.STRBimm5 = function () { + //Store Byte From Register + var address = (((this.execute >> 6) & 0x1F) + (this.read3OffsetLowRegister() | 0)) | 0; + this.CPUCore.write8(address | 0, this.read0OffsetLowRegister() | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.LDRBimm5 = function () { + //Load Byte Into Register + var data = this.CPUCore.read8((((this.execute >> 6) & 0x1F) + (this.read3OffsetLowRegister() | 0)) | 0) | 0; + this.write0OffsetLowRegister(data | 0); + //Update PC: + this.incrementProgramCounter(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +THUMBInstructionSet.prototype.STRHimm5 = function () { + //Store Half-Word From Register + var address = (((this.execute >> 5) & 0x3E) + (this.read3OffsetLowRegister() | 0)) | 0; + this.CPUCore.write16(address | 0, this.read0OffsetLowRegister() | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.LDRHimm5 = function () { + //Load Half-Word Into Register + var data = this.CPUCore.read16((((this.execute >> 5) & 0x3E) + (this.read3OffsetLowRegister() | 0)) | 0) | 0; + this.write0OffsetLowRegister(data | 0); + //Update PC: + this.incrementProgramCounter(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +THUMBInstructionSet.prototype.STRSP = function () { + //Store Word From Register + var address = (((this.execute & 0xFF) << 2) + (this.readSP() | 0)) | 0; + this.CPUCore.write32(address | 0, this.read8OffsetLowRegister() | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.LDRSP = function () { + //Load Word Into Register + var data = this.CPUCore.read32((((this.execute & 0xFF) << 2) + (this.readSP() | 0)) | 0) | 0; + this.write8OffsetLowRegister(data | 0); + //Update PC: + this.incrementProgramCounter(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +THUMBInstructionSet.prototype.ADDPC = function () { + //Add PC With Offset Into Register + var data = ((this.readPC() & -3) + ((this.execute & 0xFF) << 2)) | 0; + this.write8OffsetLowRegister(data | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.ADDSP = function () { + //Add SP With Offset Into Register + var data = (((this.execute & 0xFF) << 2) + (this.readSP() | 0)) | 0; + this.write8OffsetLowRegister(data | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.ADDSPimm7 = function () { + //Add Signed Offset Into SP + if ((this.execute & 0x80) != 0) { + this.writeSP(((this.readSP() | 0) - ((this.execute & 0x7F) << 2)) | 0); + } + else { + this.writeSP(((this.readSP() | 0) + ((this.execute & 0x7F) << 2)) | 0); + } + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.PUSH = function () { + //Only initialize the PUSH sequence if the register list is non-empty: + if ((this.execute & 0xFF) > 0) { + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) onto the stack: + for (var rListPosition = 7; (rListPosition | 0) > -1; rListPosition = ((rListPosition | 0) - 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push register onto the stack: + this.SPDecrementWord(); + this.memory.memoryWrite32(this.readSP() | 0, this.readLowRegister(rListPosition | 0) | 0); + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.PUSHlr = function () { + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push link register onto the stack: + this.SPDecrementWord(); + this.memory.memoryWrite32(this.readSP() | 0, this.readLR() | 0); + //Push register(s) onto the stack: + for (var rListPosition = 7; (rListPosition | 0) > -1; rListPosition = ((rListPosition | 0) - 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push register onto the stack: + this.SPDecrementWord(); + this.memory.memoryWrite32(this.readSP() | 0, this.readLowRegister(rListPosition | 0) | 0); + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.POP = function () { + //Only initialize the POP sequence if the register list is non-empty: + if ((this.execute & 0xFF) > 0) { + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //POP stack into register(s): + for (var rListPosition = 0; (rListPosition | 0) < 8; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //POP stack into a register: + this.writeLowRegister(rListPosition | 0, this.memory.memoryRead32(this.readSP() | 0) | 0); + this.SPIncrementWord(); + } + } + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } + //Update PC: + this.incrementProgramCounter(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +THUMBInstructionSet.prototype.POPpc = function () { + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //POP stack into register(s): + for (var rListPosition = 0; (rListPosition | 0) < 8; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //POP stack into a register: + this.writeLowRegister(rListPosition | 0, this.memory.memoryRead32(this.readSP() | 0) | 0); + this.SPIncrementWord(); + } + } + //POP stack into the program counter (r15): + this.writePC(this.memory.memoryRead32(this.readSP() | 0) | 0); + this.SPIncrementWord(); + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +THUMBInstructionSet.prototype.STMIA = function () { + //Only initialize the STMIA sequence if the register list is non-empty: + if ((this.execute & 0xFF) > 0) { + //Get the base address: + var currentAddress = this.read8OffsetLowRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Push register(s) into memory: + for (var rListPosition = 0; (rListPosition | 0) < 8; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Push a register into memory: + this.memory.memoryWrite32(currentAddress | 0, this.readLowRegister(rListPosition | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + //Store the updated base address back into register: + this.write8OffsetLowRegister(currentAddress | 0); + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.LDMIA = function () { + //Only initialize the LDMIA sequence if the register list is non-empty: + if ((this.execute & 0xFF) > 0) { + //Get the base address: + var currentAddress = this.read8OffsetLowRegister() | 0; + //Updating the address bus away from PC fetch: + this.wait.NonSequentialBroadcast(); + //Load register(s) from memory: + for (var rListPosition = 0; (rListPosition | 0) < 8; rListPosition = ((rListPosition | 0) + 1) | 0) { + if ((this.execute & (1 << rListPosition)) != 0) { + //Load a register from memory: + this.writeLowRegister(rListPosition | 0, this.memory.memoryRead32(currentAddress | 0) | 0); + currentAddress = ((currentAddress | 0) + 4) | 0; + } + } + //Store the updated base address back into register: + this.write8OffsetLowRegister(currentAddress | 0); + //Updating the address bus back to PC fetch: + this.wait.NonSequentialBroadcast(); + } + //Update PC: + this.incrementProgramCounter(); + //Internal Cycle: + this.wait.CPUInternalSingleCyclePrefetch(); +} +THUMBInstructionSet.prototype.BEQ = function () { + //Branch if EQual: + if ((this.branchFlags.getZero() | 0) == 0) { + this.offsetPC(); + } + else { + //Update PC: + this.incrementProgramCounter(); + } +} +THUMBInstructionSet.prototype.BNE = function () { + //Branch if Not Equal: + if ((this.branchFlags.getZero() | 0) != 0) { + this.offsetPC(); + } + else { + //Update PC: + this.incrementProgramCounter(); + } +} +THUMBInstructionSet.prototype.BCS = function () { + //Branch if Carry Set: + if ((this.branchFlags.getCarry() | 0) < 0) { + this.offsetPC(); + } + else { + //Update PC: + this.incrementProgramCounter(); + } +} +THUMBInstructionSet.prototype.BCC = function () { + //Branch if Carry Clear: + if ((this.branchFlags.getCarry() | 0) >= 0) { + this.offsetPC(); + } + else { + //Update PC: + this.incrementProgramCounter(); + } +} +THUMBInstructionSet.prototype.BMI = function () { + //Branch if Negative Set: + if ((this.branchFlags.getNegative() | 0) < 0) { + this.offsetPC(); + } + else { + //Update PC: + this.incrementProgramCounter(); + } +} +THUMBInstructionSet.prototype.BPL = function () { + //Branch if Negative Clear: + if ((this.branchFlags.getNegative() | 0) >= 0) { + this.offsetPC(); + } + else { + //Update PC: + this.incrementProgramCounter(); + } +} +THUMBInstructionSet.prototype.BVS = function () { + //Branch if Overflow Set: + if ((this.branchFlags.getOverflow() | 0) < 0) { + this.offsetPC(); + } + else { + //Update PC: + this.incrementProgramCounter(); + } +} +THUMBInstructionSet.prototype.BVC = function () { + //Branch if Overflow Clear: + if ((this.branchFlags.getOverflow() | 0) >= 0) { + this.offsetPC(); + } + else { + //Update PC: + this.incrementProgramCounter(); + } +} +THUMBInstructionSet.prototype.BHI = function () { + //Branch if Carry & Non-Zero: + if ((this.branchFlags.getCarry() | 0) < 0 && (this.branchFlags.getZero() | 0) != 0) { + this.offsetPC(); + } + else { + //Update PC: + this.incrementProgramCounter(); + } +} +THUMBInstructionSet.prototype.BLS = function () { + //Branch if Carry Clear or is Zero Set: + if ((this.branchFlags.getCarry() | 0) < 0 && (this.branchFlags.getZero() | 0) != 0) { + //Update PC: + this.incrementProgramCounter(); + } + else { + this.offsetPC(); + } +} +THUMBInstructionSet.prototype.BGE = function () { + //Branch if Negative equal to Overflow + if ((this.branchFlags.BGE() | 0) >= 0) { + this.offsetPC(); + } + else { + //Update PC: + this.incrementProgramCounter(); + } +} +THUMBInstructionSet.prototype.BLT = function () { + //Branch if Negative NOT equal to Overflow + if ((this.branchFlags.BGE() | 0) < 0) { + this.offsetPC(); + } + else { + //Update PC: + this.incrementProgramCounter(); + } +} +THUMBInstructionSet.prototype.BGT = function () { + //Branch if Zero Clear and Negative equal to Overflow + if ((this.branchFlags.getZero() | 0) != 0 && (this.branchFlags.BGE() | 0) >= 0) { + this.offsetPC(); + } + else { + //Update PC: + this.incrementProgramCounter(); + } +} +THUMBInstructionSet.prototype.BLE = function () { + //Branch if Zero Set or Negative NOT equal to Overflow + if ((this.branchFlags.getZero() | 0) != 0 && (this.branchFlags.BGE() | 0) >= 0) { + //Update PC: + this.incrementProgramCounter(); + } + else { + this.offsetPC(); + } +} +THUMBInstructionSet.prototype.SWI = function () { + //Software Interrupt: + this.CPUCore.SWI(); +} +THUMBInstructionSet.prototype.B = function () { + //Unconditional Branch: + //Update the program counter to branch address: + this.CPUCore.branch(((this.readPC() | 0) + ((this.execute << 21) >> 20)) | 0); +} +THUMBInstructionSet.prototype.BLsetup = function () { + //Brank with Link (High offset) + //Update the link register to branch address: + this.writeLR(((this.readPC() | 0) + ((this.execute << 21) >> 9)) | 0); + //Update PC: + this.incrementProgramCounter(); +} +THUMBInstructionSet.prototype.BLoff = function () { + //Brank with Link (Low offset) + //Update the link register to branch address: + this.writeLR(((this.readLR() | 0) + ((this.execute & 0x7FF) << 1)) | 0); + //Copy LR to PC: + var oldPC = this.readPC() | 0; + //Flush Pipeline & Block PC Increment: + this.CPUCore.branch(this.readLR() & -0x2); + //Set bit 0 of LR high: + this.writeLR(((oldPC | 0) - 0x2) | 0x1); +} +THUMBInstructionSet.prototype.UNDEFINED = function () { + //Undefined Exception: + this.CPUCore.UNDEFINED(); +} +function compileTHUMBInstructionDecodeMap() { + var opcodeIndice = 0; + var instructionMap = getUint8Array(1024); + function generateLowMap(instruction) { + for (var index = 0; index < 0x20; ++index) { + instructionMap[opcodeIndice++] = instruction; + } + } + function generateLowMap2(instruction) { + for (var index = 0; index < 0x8; ++index) { + instructionMap[opcodeIndice++] = instruction; + } + } + function generateLowMap3(instruction) { + for (var index = 0; index < 0x4; ++index) { + instructionMap[opcodeIndice++] = instruction; + } + } + function generateLowMap4(instruction1, instruction2, instruction3, instruction4) { + instructionMap[opcodeIndice++] = instruction1; + instructionMap[opcodeIndice++] = instruction2; + instructionMap[opcodeIndice++] = instruction3; + instructionMap[opcodeIndice++] = instruction4; + } + //0-7 + generateLowMap(6); + //8-F + generateLowMap(7); + //10-17 + generateLowMap(30); + //18-19 + generateLowMap2(12); + //1A-1B + generateLowMap2(18); + //1C-1D + generateLowMap2(11); + //1E-1F + generateLowMap2(60); + //20-27 + generateLowMap(8); + //28-2F + generateLowMap(0); + //30-37 + generateLowMap(17); + //38-3F + generateLowMap(42); + //40 + generateLowMap4(4, 59, 55, 57); + //41 + generateLowMap4(69, 71, 72, 43); + //42 + generateLowMap4(48, 46, 9, 75); + //43 + generateLowMap4(21, 31, 66, 68); + //44 + generateLowMap4(82, 61, 27, 77); + //45 + generateLowMap4(82, 58, 63, 78); + //46 + generateLowMap4(82, 2, 16, 54); + //47 + generateLowMap4(37, 49, 82, 82); + //48-4F + generateLowMap(15); + //50-51 + generateLowMap2(74); + //52-53 + generateLowMap2(35); + //54-55 + generateLowMap2(79); + //56-57 + generateLowMap2(65); + //58-59 + generateLowMap2(70); + //5A-5B + generateLowMap2(36); + //5C-5D + generateLowMap2(76); + //5E-5F + generateLowMap2(44); + //60-67 + generateLowMap(20); + //68-6F + generateLowMap(3); + //70-77 + generateLowMap(45); + //78-7F + generateLowMap(5); + //80-87 + generateLowMap(28); + //88-8F + generateLowMap(22); + //90-97 + generateLowMap(13); + //98-9F + generateLowMap(10); + //A0-A7 + generateLowMap(64); + //A8-AF + generateLowMap(67); + //B0 + generateLowMap3(39); + //B1 + generateLowMap3(82); + //B2 + generateLowMap3(82); + //B3 + generateLowMap3(82); + //B4 + generateLowMap3(41); + //B5 + generateLowMap3(40); + //B6 + generateLowMap3(82); + //B7 + generateLowMap3(82); + //B8 + generateLowMap3(82); + //B9 + generateLowMap3(82); + //BA + generateLowMap3(82); + //BB + generateLowMap3(82); + //BC + generateLowMap3(26); + //BD + generateLowMap3(56); + //BE + generateLowMap3(82); + //BF + generateLowMap3(82); + //C0-C7 + generateLowMap(50); + //C8-CF + generateLowMap(53); + //D0 + generateLowMap3(1); + //D1 + generateLowMap3(24); + //D2 + generateLowMap3(23); + //D3 + generateLowMap3(19); + //D4 + generateLowMap3(73); + //D5 + generateLowMap3(62); + //D6 + generateLowMap3(80); + //D7 + generateLowMap3(81); + //D8 + generateLowMap3(47); + //D9 + generateLowMap3(51); + //DA + generateLowMap3(25); + //DB + generateLowMap3(38); + //DC + generateLowMap3(34); + //DD + generateLowMap3(29); + //DE + generateLowMap3(82); + //DF + generateLowMap3(52); + //E0-E7 + generateLowMap(14); + //E8-EF + generateLowMap(82); + //F0-F7 + generateLowMap(32); + //F8-FF + generateLowMap(33); + //Set to prototype: + THUMBInstructionSet.prototype.instructionMap = instructionMap; +} +compileTHUMBInstructionDecodeMap(); \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/Cartridge.js b/public/gfiles/gba/IodineGBA/core/Cartridge.js new file mode 100644 index 00000000..f45778f3 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/Cartridge.js @@ -0,0 +1,278 @@ +"use strict"; +/* + Copyright (C) 2012-2014 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceCartridge(IOCore) { + this.IOCore = IOCore; +} +GameBoyAdvanceCartridge.prototype.initialize = function () { + this.flash_is128 = false; + this.flash_isAtmel = false; + this.ROM = this.getROMArray(this.IOCore.ROM); + this.ROM16 = getUint16View(this.ROM); + this.ROM32 = getInt32View(this.ROM); + this.decodeName(); + this.decodeFlashType(); +} +GameBoyAdvanceCartridge.prototype.getROMArray = function (old_array) { + this.ROMLength = Math.min((old_array.length >> 2) << 2, 0x2000000); + this.EEPROMStart = ((this.ROMLength | 0) > 0x1000000) ? Math.max(this.ROMLength | 0, 0x1FFFF00) : 0x1000000; + var newArray = getUint8Array(this.ROMLength | 0); + for (var index = 0; (index | 0) < (this.ROMLength | 0); index = ((index | 0) + 1) | 0) { + newArray[index | 0] = old_array[index | 0] | 0; + } + return newArray; +} +GameBoyAdvanceCartridge.prototype.decodeName = function () { + this.name = "GUID_"; + if ((this.ROMLength | 0) >= 0xC0) { + for (var address = 0xAC; (address | 0) < 0xB3; address = ((address | 0) + 1) | 0) { + if ((this.ROM[address | 0] | 0) > 0) { + this.name += String.fromCharCode(this.ROM[address | 0] | 0); + } + else { + this.name += "_"; + } + } + } +} +GameBoyAdvanceCartridge.prototype.decodeFlashType = function () { + this.flash_is128 = false; + this.flash_isAtmel = false; + var flash_types = 0; + var F = ("F").charCodeAt(0) & 0xFF; + var L = ("L").charCodeAt(0) & 0xFF; + var A = ("A").charCodeAt(0) & 0xFF; + var S = ("S").charCodeAt(0) & 0xFF; + var H = ("H").charCodeAt(0) & 0xFF; + var underScore = ("_").charCodeAt(0) & 0xFF; + var five = ("5").charCodeAt(0) & 0xFF; + var one = ("1").charCodeAt(0) & 0xFF; + var two = ("2").charCodeAt(0) & 0xFF; + var M = ("M").charCodeAt(0) & 0xFF; + var V = ("V").charCodeAt(0) & 0xFF; + var length = ((this.ROM.length | 0) - 12) | 0; + for (var index = 0; (index | 0) < (length | 0); index = ((index | 0) + 4) | 0) { + if ((this.ROM[index | 0] | 0) == (F | 0)) { + if ((this.ROM[index | 1] | 0) == (L | 0)) { + if ((this.ROM[index | 2] | 0) == (A | 0)) { + if ((this.ROM[index | 3] | 0) == (S | 0)) { + var tempIndex = ((index | 0) + 4) | 0; + if ((this.ROM[tempIndex | 0] | 0) == (H | 0)) { + if ((this.ROM[tempIndex | 1] | 0) == (underScore | 0)) { + if ((this.ROM[tempIndex | 2] | 0) == (V | 0)) { + flash_types |= 1; + } + } + else if ((this.ROM[tempIndex | 1] | 0) == (five | 0)) { + if ((this.ROM[tempIndex | 2] | 0) == (one | 0)) { + if ((this.ROM[tempIndex | 3] | 0) == (two | 0)) { + tempIndex = ((tempIndex | 0) + 4) | 0; + if ((this.ROM[tempIndex | 0] | 0) == (underScore | 0)) { + if ((this.ROM[tempIndex | 1] | 0) == (V | 0)) { + flash_types |= 2; + } + } + } + } + } + else if ((this.ROM[tempIndex | 1] | 0) == (one | 0)) { + if ((this.ROM[tempIndex | 2] | 0) == (M | 0)) { + if ((this.ROM[tempIndex | 3] | 0) == (underScore | 0)) { + tempIndex = ((tempIndex | 0) + 4) | 0; + if ((this.ROM[tempIndex | 0] | 0) == (V | 0)) { + flash_types |= 4; + break; + } + } + } + } + } + } + } + } + } + } + this.flash_is128 = ((flash_types | 0) >= 4); + this.flash_isAtmel = ((flash_types | 0) <= 1); +} +GameBoyAdvanceCartridge.prototype.readROMOnly8 = function (address) { + address = address | 0; + var data = 0; + if ((address | 0) < (this.ROMLength | 0)) { + data = this.ROM[address & 0x1FFFFFF] | 0; + } + return data | 0; +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceCartridge.prototype.readROMOnly16 = function (address) { + address = address | 0; + var data = 0; + if ((address | 0) < (this.ROMLength | 0)) { + data = this.ROM16[(address >> 1) & 0xFFFFFF] | 0; + } + return data | 0; + } + GameBoyAdvanceCartridge.prototype.readROMOnly32 = function (address) { + address = address | 0; + var data = 0; + if ((address | 0) < (this.ROMLength | 0)) { + data = this.ROM32[(address >> 2) & 0x7FFFFF] | 0; + } + return data | 0; + } +} +else { + GameBoyAdvanceCartridge.prototype.readROMOnly16 = function (address) { + address = address | 0; + var data = 0; + if ((address | 0) < (this.ROMLength | 0)) { + data = this.ROM[address] | (this.ROM[address | 1] << 8); + } + return data | 0; + } + GameBoyAdvanceCartridge.prototype.readROMOnly32 = function (address) { + address = address | 0; + var data = 0; + if ((address | 0) < (this.ROMLength | 0)) { + data = this.ROM[address] | (this.ROM[address | 1] << 8) | (this.ROM[address | 2] << 16) | (this.ROM[address | 3] << 24); + } + return data | 0; + } +} +GameBoyAdvanceCartridge.prototype.readROM8 = function (address) { + address = address | 0; + var data = 0; + if ((address | 0) > 0xC9) { + //Definitely ROM: + data = this.readROMOnly8(address | 0) | 0; + } + else { + //Possibly GPIO: + data = this.IOCore.saves.readGPIO8(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceCartridge.prototype.readROM16 = function (address) { + address = address | 0; + var data = 0; + if ((address | 0) > 0xC9) { + //Definitely ROM: + data = this.readROMOnly16(address | 0) | 0; + } + else { + //Possibly GPIO: + data = this.IOCore.saves.readGPIO16(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceCartridge.prototype.readROM32 = function (address) { + address = address | 0; + var data = 0; + if ((address | 0) > 0xC9) { + //Definitely ROM: + data = this.readROMOnly32(address | 0) | 0; + } + else { + //Possibly GPIO: + data = this.IOCore.saves.readGPIO32(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceCartridge.prototype.readROM8Space2 = function (address) { + address = address | 0; + var data = 0; + if ((address | 0) >= 0xC4 && (address | 0) < 0xCA) { + //Possibly GPIO: + data = this.IOCore.saves.readGPIO8(address | 0) | 0; + } + else if ((address | 0) >= (this.EEPROMStart | 0)) { + //Possibly EEPROM: + data = this.IOCore.saves.readEEPROM8(address | 0) | 0; + } + else { + //Definitely ROM: + data = this.readROMOnly8(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceCartridge.prototype.readROM16Space2 = function (address) { + address = address | 0; + var data = 0; + if ((address | 0) >= 0xC4 && (address | 0) < 0xCA) { + //Possibly GPIO: + data = this.IOCore.saves.readGPIO16(address | 0) | 0; + } + else if ((address | 0) >= (this.EEPROMStart | 0)) { + //Possibly EEPROM: + data = this.IOCore.saves.readEEPROM16(address | 0) | 0; + } + else { + //Definitely ROM: + data = this.readROMOnly16(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceCartridge.prototype.readROM32Space2 = function (address) { + address = address | 0; + var data = 0; + if ((address | 0) >= 0xC4 && (address | 0) < 0xCA) { + //Possibly GPIO: + data = this.IOCore.saves.readGPIO32(address | 0) | 0; + } + else if ((address | 0) >= (this.EEPROMStart | 0)) { + //Possibly EEPROM: + data = this.IOCore.saves.readEEPROM32(address | 0) | 0; + } + else { + //Definitely ROM: + data = this.readROMOnly32(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceCartridge.prototype.writeROM8 = function (address, data) { + address = address | 0; + data = data | 0; + if ((address | 0) >= 0xC4 && (address | 0) < 0xCA) { + //GPIO Chip (RTC): + this.IOCore.saves.writeGPIO8(address | 0, data | 0); + } +} +GameBoyAdvanceCartridge.prototype.writeROM16 = function (address, data) { + address = address | 0; + data = data | 0; + if ((address | 0) >= 0xC4 && (address | 0) < 0xCA) { + //GPIO Chip (RTC): + this.IOCore.saves.writeGPIO16(address | 0, data | 0); + } +} +GameBoyAdvanceCartridge.prototype.writeROM16DMA = function (address, data) { + address = address | 0; + data = data | 0; + if ((address | 0) >= 0xC4 && (address | 0) < 0xCA) { + //GPIO Chip (RTC): + this.IOCore.saves.writeGPIO16(address | 0, data | 0); + } + else if ((address | 0) >= (this.EEPROMStart | 0)) { + //Possibly EEPROM: + this.IOCore.saves.writeEEPROM16(address | 0, data | 0); + } +} +GameBoyAdvanceCartridge.prototype.writeROM32 = function (address, data) { + address = address | 0; + data = data | 0; + if ((address | 0) >= 0xC4 && (address | 0) < 0xCA) { + //GPIO Chip (RTC): + this.IOCore.saves.writeGPIO32(address | 0, data | 0); + } +} +GameBoyAdvanceCartridge.prototype.nextIRQEventTime = function () { + //Nothing yet implement that would fire an IRQ: + return 0x7FFFFFFF; +} diff --git a/public/gfiles/gba/IodineGBA/core/DMA.js b/public/gfiles/gba/IodineGBA/core/DMA.js new file mode 100644 index 00000000..2843eb92 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/DMA.js @@ -0,0 +1,98 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceDMA(IOCore) { + this.IOCore = IOCore; +} +GameBoyAdvanceDMA.prototype.initialize = function () { + this.dmaChannel0 = this.IOCore.dmaChannel0; + this.dmaChannel1 = this.IOCore.dmaChannel1; + this.dmaChannel2 = this.IOCore.dmaChannel2; + this.dmaChannel3 = this.IOCore.dmaChannel3; + this.currentMatch = -1; + this.fetch = 0; +} +GameBoyAdvanceDMA.prototype.getCurrentFetchValue = function () { + return this.fetch | 0; +} +GameBoyAdvanceDMA.prototype.gfxHBlankRequest = function () { + //Pass H-Blank signal to all DMA channels: + this.requestDMA(0x4); +} +GameBoyAdvanceDMA.prototype.gfxVBlankRequest = function () { + //Pass V-Blank signal to all DMA channels: + this.requestDMA(0x2); +} +GameBoyAdvanceDMA.prototype.requestDMA = function (DMAType) { + DMAType = DMAType | 0; + this.dmaChannel0.requestDMA(DMAType | 0); + this.dmaChannel1.requestDMA(DMAType | 0); + this.dmaChannel2.requestDMA(DMAType | 0); + this.dmaChannel3.requestDMA(DMAType | 0); +} +GameBoyAdvanceDMA.prototype.findLowestDMA = function () { + if ((this.dmaChannel0.getMatchStatus() | 0) != 0) { + return 0; + } + if ((this.dmaChannel1.getMatchStatus() | 0) != 0) { + return 1; + } + if ((this.dmaChannel2.getMatchStatus() | 0) != 0) { + return 2; + } + if ((this.dmaChannel3.getMatchStatus() | 0) != 0) { + return 3; + } + return 4; +} +GameBoyAdvanceDMA.prototype.update = function () { + var lowestDMAFound = this.findLowestDMA(); + if ((lowestDMAFound | 0) < 4) { + //Found an active DMA: + if ((this.currentMatch | 0) == -1) { + this.IOCore.flagDMA(); + } + if ((this.currentMatch | 0) != (lowestDMAFound | 0)) { + //Re-broadcasting on address bus, so non-seq: + this.IOCore.wait.NonSequentialBroadcast(); + this.currentMatch = lowestDMAFound | 0; + } + } + else if ((this.currentMatch | 0) != -1) { + //No active DMA found: + this.currentMatch = -1; + this.IOCore.deflagDMA(); + this.IOCore.updateCoreSpill(); + } +} +GameBoyAdvanceDMA.prototype.perform = function () { + //Call the correct channel to process: + switch (this.currentMatch | 0) { + case 0: + this.dmaChannel0.handleDMACopy(); + break; + case 1: + this.dmaChannel1.handleDMACopy(); + break; + case 2: + this.dmaChannel2.handleDMACopy(); + break; + default: + this.dmaChannel3.handleDMACopy(); + } +} +GameBoyAdvanceDMA.prototype.updateFetch = function (data) { + data = data | 0; + this.fetch = data | 0; +} +GameBoyAdvanceDMA.prototype.nextEventTime = function () { + var clocks = Math.min(this.dmaChannel0.nextEventTime() | 0, this.dmaChannel1.nextEventTime() | 0, this.dmaChannel2.nextEventTime() | 0, this.dmaChannel3.nextEventTime() | 0) | 0; + return clocks | 0; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/Emulator.js b/public/gfiles/gba/IodineGBA/core/Emulator.js new file mode 100644 index 00000000..7e2ce241 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/Emulator.js @@ -0,0 +1,469 @@ +"use strict"; +/* + Copyright (C) 2012-2019 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceEmulator() { + this.settings = { + SKIPBoot:false, //Skip the BIOS boot screen. + audioBufferUnderrunLimit:100, //Audio buffer minimum span amount over x milliseconds. + audioBufferDynamicLimit:32, //Audio buffer dynamic minimum span amount over x milliseconds. + audioBufferSize:300, //Audio buffer maximum span amount over x milliseconds. + emulatorSpeed:1.0, //Speed multiplier of the emulator. + metricCollectionMinimum:500, //How many milliseconds of cycling to count before determining speed. + dynamicSpeed:false, //Whether to actively change the target speed for best user experience. + overclockBlockLimit:200, //Whether to throttle clocks in audio adjustment. + offthreadGfxEnabled:true //Whether to allow offthread graphics rendering if support is present. + }; + this.audioFound = 0; //Do we have audio output sink found yet? + this.emulatorStatus = 0x10; //{paused, saves loaded, fault found, loaded} + this.BIOS = []; //Initialize BIOS as not existing. + this.ROM = []; //Initialize BIOS as not existing. + this.audioUpdateState = 1; //Do we need to update the sound core with new info? + this.saveExportHandler = null; //Save export handler attached by GUI. + this.saveImportHandler = null; //Save import handler attached by GUI. + this.speedCallback = null; //Speed report handler attached by GUI. + this.playStatusCallback = null; //Play status change handler attached by GUI. + this.startCallbacks = []; //Some jobs to run at iteration head. + this.endCallbacks = []; //Some jobs to run at iteration end. + this.terminationCallbacks = []; //Some jobs to run if the emulation core is killed. + this.timerIntervalRate = 16; //How often the emulator core is called into (in milliseconds). + this.lastTimestamp = 0; //Track the last time given in milliseconds. + this.dynamicSpeedRefresh = false; //Whether speed is allowed to be changed dynamically in the current cycle. + this.calculateTimings(); //Calculate some multipliers against the core emulator timer. + this.generateCoreExposed(); //Generate a limit API for the core to call this shell object. +} +GameBoyAdvanceEmulator.prototype.generateCoreExposed = function () { + var parentObj = this; + this.coreExposed = { + outputAudio:function (l, r) { + parentObj.outputAudio(l, r); + }, + graphicsHandle:null, + appendStartIterationSync:function (callback) { + parentObj.startCallbacks.push(callback); + }, + appendEndIterationSync:function (callback) { + parentObj.endCallbacks.push(callback); + }, + appendTerminationSync:function (callback) { + parentObj.terminationCallbacks.push(callback); + }, + offthreadGfxEnabled:function () { + return !!parentObj.settings.offthreadGfxEnabled; + } + } +} +GameBoyAdvanceEmulator.prototype.play = function () { + if ((this.emulatorStatus | 0) >= 0x10) { + this.emulatorStatus = this.emulatorStatus & 0xF; + if ((this.emulatorStatus & 0x1) == 0 && this.BIOS && this.ROM) { + if ((this.initializeCore() | 0) == 0) { + //Failure to initialize: + this.pause(); + return; + } + this.importSave(); + } + this.invalidateMetrics(); + this.setBufferSpace(); + //Report new status back: + this.playStatusCallback(1); + } +} +GameBoyAdvanceEmulator.prototype.pause = function () { + if ((this.emulatorStatus | 0) < 0x10) { + this.exportSave(); + this.emulatorStatus = this.emulatorStatus | 0x10; + //Report new status back: + this.playStatusCallback(0); + } +} +GameBoyAdvanceEmulator.prototype.stop = function () { + this.emulatorStatus = this.emulatorStatus & 0x1C; + this.audioUpdateState = 1; + this.pause(); +} +GameBoyAdvanceEmulator.prototype.restart = function () { + if ((this.emulatorStatus & 0x1) == 0x1) { + this.emulatorStatus = this.emulatorStatus & 0x1D; + this.exportSave(); + if ((this.initializeCore() | 0) == 0) { + //Failure to initialize: + this.pause(); + return; + } + this.importSave(); + this.audioUpdateState = 1; + this.processNewSpeed(1); + this.setBufferSpace(); + } +} +GameBoyAdvanceEmulator.prototype.timerCallback = function (lastTimestamp) { + //Callback passes us a reference timestamp: + this.lastTimestamp = lastTimestamp >>> 0; + switch (this.emulatorStatus | 0) { + //Core initialized and saves loaded: + case 5: + this.iterationStartSequence(); //Run start of iteration stuff. + this.IOCore.enter(this.CPUCyclesTotal | 0); //Step through the emulation core loop. + this.iterationEndSequence(); //Run end of iteration stuff. + break; + //Core initialized, but saves still loading: + case 1: + break; + default: + //Some pending error is preventing execution, so pause: + this.pause(); + } +} +GameBoyAdvanceEmulator.prototype.iterationStartSequence = function () { + this.calculateSpeedPercentage(); //Calculate the emulator realtime run speed heuristics. + this.emulatorStatus = this.emulatorStatus | 0x2; //If the end routine doesn't unset this, then we are marked as having crashed. + this.audioUnderrunAdjustment(); //If audio is enabled, look to see how much we should overclock by to maintain the audio buffer. + this.audioPushNewState(); //Check to see if we need to update the audio core for any output changes. + this.runStartJobs(); //Run various callbacks assigned from internal components. +} +GameBoyAdvanceEmulator.prototype.iterationEndSequence = function () { + this.emulatorStatus = this.emulatorStatus & 0x1D; //If core did not throw while running, unset the fatal error flag. + this.clockCyclesSinceStart = ((this.clockCyclesSinceStart | 0) + (this.CPUCyclesTotal | 0)) | 0; //Accumulate tracking. + this.submitAudioBuffer(); //Flush audio buffer to output. + this.runEndJobs(); //Run various callbacks assigned from internal components. +} +GameBoyAdvanceEmulator.prototype.runStartJobs = function () { + var length = this.startCallbacks.length | 0; + //Loop through all jobs: + for (var index = 0; (index | 0) < (length | 0); index = ((index | 0) + 1) | 0) { + //Run job: + this.startCallbacks[index | 0](); + } +} +GameBoyAdvanceEmulator.prototype.runEndJobs = function () { + var length = this.endCallbacks.length | 0; + //Loop through all jobs: + for (var index = 0; (index | 0) < (length | 0); index = ((index | 0) + 1) | 0) { + //Run job: + this.endCallbacks[index | 0](); + } +} +GameBoyAdvanceEmulator.prototype.runTerminationJobs = function () { + var length = this.terminationCallbacks.length | 0; + //Loop through all jobs: + for (var index = 0; (index | 0) < (length | 0); index = ((index | 0) + 1) | 0) { + //Run job: + this.terminationCallbacks[index | 0](); + } + //Remove old jobs: + this.startCallbacks = []; + this.endCallbacks = []; + this.terminationCallbacks = []; +} +GameBoyAdvanceEmulator.prototype.attachROM = function (ROM) { + this.stop(); + this.ROM = ROM; +} +GameBoyAdvanceEmulator.prototype.attachBIOS = function (BIOS) { + this.stop(); + this.BIOS = BIOS; +} +GameBoyAdvanceEmulator.prototype.getGameName = function () { + if ((this.emulatorStatus & 0x3) == 0x1) { + return this.IOCore.cartridge.name; + } + else { + return ""; + } +} +GameBoyAdvanceEmulator.prototype.attachSaveExportHandler = function (handler) { + if (typeof handler == "function") { + this.saveExportHandler = handler; + } +} +GameBoyAdvanceEmulator.prototype.attachSaveImportHandler = function (handler) { + if (typeof handler == "function") { + this.saveImportHandler = handler; + } +} +GameBoyAdvanceEmulator.prototype.attachSpeedHandler = function (handler) { + if (typeof handler == "function") { + this.speedCallback = handler; + } +} +GameBoyAdvanceEmulator.prototype.attachPlayStatusHandler = function (handler) { + if (typeof handler == "function") { + this.playStatusCallback = handler; + } +} +GameBoyAdvanceEmulator.prototype.importSave = function () { + if (this.saveImportHandler) { + var name = this.getGameName(); + if (name != "") { + var parentObj = this; + this.emulatorStatus = this.emulatorStatus & 0x1B; + this.saveImportHandler(name, function (save) { + parentObj.emulatorStatus = parentObj.emulatorStatus & 0x1B; + parentObj.saveImportHandler("TYPE_" + name, function (saveType) { + if (save && saveType && (parentObj.emulatorStatus & 0x3) == 0x1) { + var length = save.length | 0; + var convertedSave = getUint8Array(length | 0); + if ((length | 0) > 0) { + for (var index = 0; (index | 0) < (length | 0); index = ((index | 0) + 1) | 0) { + convertedSave[index | 0] = save[index | 0] & 0xFF; + } + //We used to save this code wrong, fix the error in old saves: + if ((saveType.length | 0) != 1) { + //0 is fallthrough "UNKNOWN" aka autodetect type: + parentObj.IOCore.saves.importSave(convertedSave, 0); + } + else { + parentObj.IOCore.saves.importSave(convertedSave, saveType[0] & 0xFF); + } + parentObj.emulatorStatus = parentObj.emulatorStatus | 0x4; + } + } + }, function (){parentObj.emulatorStatus = parentObj.emulatorStatus | 0x4;}); + }, function (){parentObj.emulatorStatus = parentObj.emulatorStatus | 0x4;}); + return; + } + } + this.emulatorStatus = this.emulatorStatus | 0x4; +} +GameBoyAdvanceEmulator.prototype.exportSave = function () { + if (this.saveExportHandler && (this.emulatorStatus & 0x3) == 0x1) { + var save = this.IOCore.saves.exportSave(); + var saveType = this.IOCore.saves.exportSaveType() | 0; + if (save != null) { + this.saveExportHandler(this.IOCore.cartridge.name, save); + this.saveExportHandler("TYPE_" + this.IOCore.cartridge.name, [saveType | 0]); + } + } +} +GameBoyAdvanceEmulator.prototype.setSpeed = function (speed) { + speed = +speed; + //Dynamic Speed overrides custom speed levels: + if (!this.settings.dynamicSpeed) { + this.processNewSpeed(+speed); + } +} +GameBoyAdvanceEmulator.prototype.processNewSpeed = function (speed) { + speed = +speed; + //0.003 for the integer resampler limitations, 0x3F for int math limitations: + speed = +Math.min(Math.max(+speed, 0.003), 0x3F); + if ((+speed) != (+this.settings.emulatorSpeed)) { + this.settings.emulatorSpeed = +speed; + this.calculateTimings(); + } +} +GameBoyAdvanceEmulator.prototype.incrementSpeed = function (delta) { + delta = +delta; + this.setSpeed((+delta) + (+this.settings.emulatorSpeed)); +} +GameBoyAdvanceEmulator.prototype.getSpeed = function () { + return +this.settings.emulatorSpeed; +} +GameBoyAdvanceEmulator.prototype.invalidateMetrics = function () { + this.clockCyclesSinceStart = 0; + this.metricStart = 0; +} +GameBoyAdvanceEmulator.prototype.resetMetrics = function () { + this.clockCyclesSinceStart = 0; + this.metricStart = this.lastTimestamp >>> 0; +} +GameBoyAdvanceEmulator.prototype.calculateTimings = function () { + this.clocksPerSecond = Math.min((+this.settings.emulatorSpeed) * 0x1000000, 0x3F000000) | 0; + this.clocksPerMilliSecond = +((this.clocksPerSecond | 0) / 1000); + this.CPUCyclesPerIteration = ((+this.clocksPerMilliSecond) * (+this.timerIntervalRate)) | 0; + this.CPUCyclesTotal = this.CPUCyclesPerIteration | 0; + this.initializeAudioLogic(); + this.reinitializeAudio(); + this.invalidateMetrics(); +} +GameBoyAdvanceEmulator.prototype.setIntervalRate = function (intervalRate) { + intervalRate = +intervalRate; + if ((+intervalRate) > 0 && (+intervalRate) < 1000) { + if ((+intervalRate) != (+this.timerIntervalRate)) { + this.timerIntervalRate = +intervalRate; + this.calculateTimings(); + } + } +} +GameBoyAdvanceEmulator.prototype.calculateSpeedPercentage = function () { + if ((this.metricStart >>> 0) != 0) { + var timeDiff = Math.max(((this.lastTimestamp >>> 0) - (this.metricStart >>> 0)) | 0, 1) >>> 0; + if ((timeDiff >>> 0) >= (this.settings.metricCollectionMinimum | 0)) { + if (this.speedCallback) { + var result = ((this.clockCyclesSinceStart | 0) * 100000) / ((timeDiff >>> 0) * 0x1000000); + this.speedCallback(+result); + } + //Reset counter for speed check: + this.resetMetrics(); + //Do a computation for dynamic speed this iteration: + this.dynamicSpeedRefresh = true; + } + else { + //Postpone any dynamic speed changes this iteration: + this.dynamicSpeedRefresh = false; + } + } + else { + //Reset counter for speed check: + this.resetMetrics(); + //Postpone any dynamic speed changes this iteration: + this.dynamicSpeedRefresh = false; + } +} +GameBoyAdvanceEmulator.prototype.initializeCore = function () { + //Wrap up any old internal instance callbacks: + this.runTerminationJobs(); + //Setup a new instance of the i/o core: + this.IOCore = new GameBoyAdvanceIO(this.settings.SKIPBoot, this.coreExposed, this.BIOS, this.ROM); + //Call the initalization procedure and get status code: + var allowInit = this.IOCore.initialize() | 0; + //Append status code as play status flag for emulator runtime: + this.emulatorStatus = this.emulatorStatus | allowInit; + return allowInit | 0; +} +GameBoyAdvanceEmulator.prototype.keyDown = function (keyPressed) { + keyPressed = keyPressed | 0; + if ((this.emulatorStatus | 0) < 0x10 && (keyPressed | 0) >= 0 && (keyPressed | 0) <= 9) { + this.IOCore.joypad.keyPress(keyPressed | 0); + } +} +GameBoyAdvanceEmulator.prototype.keyUp = function (keyReleased) { + keyReleased = keyReleased | 0; + if ((this.emulatorStatus | 0) < 0x10 && (keyReleased | 0) >= 0 && (keyReleased | 0) <= 9) { + this.IOCore.joypad.keyRelease(keyReleased | 0); + } +} +GameBoyAdvanceEmulator.prototype.attachGraphicsFrameHandler = function (handler) { + if (typeof handler == "object") { + this.coreExposed.graphicsHandle = handler; + } +} +GameBoyAdvanceEmulator.prototype.attachAudioHandler = function (mixerInputHandler) { + if (mixerInputHandler) { + this.audio = mixerInputHandler; + } +} +GameBoyAdvanceEmulator.prototype.enableAudio = function () { + if ((this.audioFound | 0) == 0 && this.audio) { + this.audioFound = 1; //Set audio to 'found' by default. + //Attempt to enable audio: + var parentObj = this; + this.audio.initialize(2, (this.clocksPerSecond | 0) / (this.audioResamplerFirstPassFactor | 0), Math.max((+this.clocksPerMilliSecond) * (this.settings.audioBufferSize | 0) / (this.audioResamplerFirstPassFactor | 0), 4) | 0, function () { + //Not needed + }, function () { + //We manually check at the start of each timer interval, so not needed here. + }, function () { + //Disable audio in the callback here: + parentObj.disableAudio(); + }); + this.audio.register(); + } +} +GameBoyAdvanceEmulator.prototype.disableAudio = function () { + if ((this.audioFound | 0) != 0) { + this.audio.unregister(); + this.audioFound = 0; + } +} +GameBoyAdvanceEmulator.prototype.reinitializeAudio = function () { + if ((this.audioFound | 0) != 0) { + this.disableAudio(); + this.enableAudio(); + } +} +GameBoyAdvanceEmulator.prototype.initializeAudioLogic = function () { + //Calculate the variables for the preliminary downsampler first: + this.audioResamplerFirstPassFactor = Math.min(Math.floor((this.clocksPerSecond | 0) / 44100), Math.floor(0x7FFFFFFF / 0x3FF)) | 0; + this.audioDownSampleInputDivider = +((2 / 0x3FF) / (this.audioResamplerFirstPassFactor | 0)); + this.initializeAudioBuffering(); + //Need to push the new resample factor: + this.audioUpdateState = 1; +} +GameBoyAdvanceEmulator.prototype.initializeAudioBuffering = function () { + this.audioDestinationPosition = 0; + this.audioBufferContainAmount = Math.max((+this.clocksPerMilliSecond) * (this.settings.audioBufferUnderrunLimit | 0) / (this.audioResamplerFirstPassFactor | 0), 3) << 1; + this.audioBufferOverclockBlockAmount = Math.max((+this.clocksPerMilliSecond) * (this.settings.overclockBlockLimit | 0) / (this.audioResamplerFirstPassFactor | 0), 3) << 1; + this.audioBufferDynamicContainAmount = Math.max((+this.clocksPerMilliSecond) * (this.settings.audioBufferDynamicLimit | 0) / (this.audioResamplerFirstPassFactor | 0), 2) << 1; + //Underrun logic will request at most 32 milliseconds of runtime per iteration, so set buffer size to 64 ms: + var audioNumSamplesTotal = Math.max(((+this.clocksPerMilliSecond) / (this.audioResamplerFirstPassFactor | 0)) << 6, 4) << 1; + if (!this.audioBuffer || ((audioNumSamplesTotal | 0) > (this.audioBuffer.length | 0))) { + //Only regen buffer if the size is increased: + this.audioBuffer = getFloat32Array(audioNumSamplesTotal | 0); + } +} +GameBoyAdvanceEmulator.prototype.outputAudio = function (downsampleInputLeft, downsampleInputRight) { + downsampleInputLeft = downsampleInputLeft | 0; + downsampleInputRight = downsampleInputRight | 0; + this.audioBuffer[this.audioDestinationPosition | 0] = ((downsampleInputLeft | 0) * (+this.audioDownSampleInputDivider)) - 1; + this.audioDestinationPosition = ((this.audioDestinationPosition | 0) + 1) | 0; + this.audioBuffer[this.audioDestinationPosition | 0] = ((downsampleInputRight | 0) * (+this.audioDownSampleInputDivider)) - 1; + this.audioDestinationPosition = ((this.audioDestinationPosition | 0) + 1) | 0; +} +GameBoyAdvanceEmulator.prototype.submitAudioBuffer = function () { + if ((this.audioFound | 0) != 0) { + this.audio.push(this.audioBuffer, 0, this.audioDestinationPosition | 0); + } + this.audioDestinationPosition = 0; +} +GameBoyAdvanceEmulator.prototype.audioUnderrunAdjustment = function () { + this.CPUCyclesTotal = this.CPUCyclesPerIteration | 0; + if ((this.audioFound | 0) != 0) { + var remainingAmount = this.audio.remainingBuffer(); + if (typeof remainingAmount == "number") { + remainingAmount = Math.max(remainingAmount | 0, 0) | 0; + var underrunAmount = ((this.audioBufferContainAmount | 0) - (remainingAmount | 0)) | 0; + if ((underrunAmount | 0) > 0) { + if (this.dynamicSpeedRefresh && this.settings.dynamicSpeed) { + if (((this.audioBufferDynamicContainAmount | 0) - (remainingAmount | 0)) > 0) { + var speed = +this.getSpeed(); + speed = Math.max((+speed) - 0.1, 0.003); + this.processNewSpeed(+speed); + } + } + this.CPUCyclesTotal = Math.min(((this.CPUCyclesTotal | 0) + ((underrunAmount >> 1) * (this.audioResamplerFirstPassFactor | 0))) | 0, (+this.clocksPerMilliSecond) << 5) | 0; + } + else { + if (this.dynamicSpeedRefresh && this.settings.dynamicSpeed) { + var speed = +this.getSpeed(); + if ((+speed) < 1) { + speed = +Math.min((+speed) + 0.01, 1); + this.processNewSpeed(+speed); + } + } + var overrunAmount = ((remainingAmount | 0) - (this.audioBufferOverclockBlockAmount | 0)) | 0; + if ((overrunAmount | 0) > 0) { + this.CPUCyclesTotal = Math.max(((this.CPUCyclesTotal | 0) - ((overrunAmount >> 1) * (this.audioResamplerFirstPassFactor | 0))) | 0, 0) | 0; + } + } + } + } +} +GameBoyAdvanceEmulator.prototype.audioPushNewState = function () { + if ((this.audioUpdateState | 0) != 0) { + this.IOCore.sound.initializeOutput(this.audioResamplerFirstPassFactor | 0); + this.audioUpdateState = 0; + } +} +GameBoyAdvanceEmulator.prototype.setBufferSpace = function () { + if ((this.audioFound | 0) != 0) { + //Fill the audio system with zeros for buffer stabilization on start: + this.audio.setBufferSpace(this.audioBufferContainAmount | 0); + } +} +GameBoyAdvanceEmulator.prototype.toggleSkipBootROM = function (SKIPBoot) { + this.settings.SKIPBoot = !!SKIPBoot; +} +GameBoyAdvanceEmulator.prototype.toggleDynamicSpeed = function (dynamicSpeed) { + this.settings.dynamicSpeed = !!dynamicSpeed; + this.processNewSpeed(1); +} +GameBoyAdvanceEmulator.prototype.toggleOffthreadGraphics = function (offthreadGfxEnabled) { + this.settings.offthreadGfxEnabled = !!offthreadGfxEnabled; +} diff --git a/public/gfiles/gba/IodineGBA/core/Graphics.js b/public/gfiles/gba/IodineGBA/core/Graphics.js new file mode 100644 index 00000000..98715ae3 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/Graphics.js @@ -0,0 +1,304 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceGraphics(IOCore) { + //Build references: + this.IOCore = IOCore; +} +GameBoyAdvanceGraphics.prototype.initialize = function () { + this.gfxRenderer = this.IOCore.gfxRenderer; + this.dma = this.IOCore.dma; + this.dmaChannel3 = this.IOCore.dmaChannel3; + this.irq = this.IOCore.irq; + this.wait = this.IOCore.wait; + this.initializeState(); +} +GameBoyAdvanceGraphics.prototype.initializeState = function () { + //Initialize Pre-Boot: + this.renderedScanLine = false; + this.statusFlags = 0; + this.IRQFlags = 0; + this.VCounter = 0; + this.currentScanLine = 0; + this.LCDTicks = 0; + if (this.IOCore.SKIPBoot) { + //BIOS entered the ROM at line 0x7C: + this.currentScanLine = 0x7C; + } +} +GameBoyAdvanceGraphics.prototype.addClocks = function (clocks) { + clocks = clocks | 0; + //Call this when clocking the state some more: + this.LCDTicks = ((this.LCDTicks | 0) + (clocks | 0)) | 0; + this.clockLCDState(); +} +GameBoyAdvanceGraphics.prototype.clockLCDState = function () { + if ((this.LCDTicks | 0) >= 960) { + this.clockScanLine(); //Line finishes drawing at clock 960. + this.clockLCDStatePostRender(); //Check for hblank and clocking into next line. + } +} +GameBoyAdvanceGraphics.prototype.clockScanLine = function () { + if (!this.renderedScanLine) { //If we rendered the scanline, don't run this again. + this.renderedScanLine = true; //Mark rendering. + if ((this.currentScanLine | 0) < 160) { + this.gfxRenderer.incrementScanLineQueue(); //Tell the gfx JIT to queue another line to draw. + } + } +} +GameBoyAdvanceGraphics.prototype.clockLCDStatePostRender = function () { + if ((this.LCDTicks | 0) >= 1006) { + //HBlank Event Occurred: + this.updateHBlank(); + if ((this.LCDTicks | 0) >= 1232) { + //Clocking to next line occurred: + this.clockLCDNextLine(); + } + } +} +GameBoyAdvanceGraphics.prototype.clockLCDNextLine = function () { + /*We've now overflowed the LCD scan line state machine counter, + which tells us we need to be on a new scan-line and refresh over.*/ + this.renderedScanLine = false; //Unmark line render. + this.statusFlags = this.statusFlags & 0x5; //Un-mark HBlank. + //De-clock for starting on new scan-line: + this.LCDTicks = ((this.LCDTicks | 0) - 1232) | 0; //We start out at the beginning of the next line. + //Increment scanline counter: + this.currentScanLine = ((this.currentScanLine | 0) + 1) | 0; //Increment to the next scan line. + //Handle switching in/out of vblank: + if ((this.currentScanLine | 0) >= 160) { + //Handle special case scan lines of vblank: + switch (this.currentScanLine | 0) { + case 160: + this.updateVBlankStart(); //Update state for start of vblank. + case 161: + this.dmaChannel3.gfxDisplaySyncRequest(); //Display Sync. DMA trigger. + break; + case 162: + this.dmaChannel3.gfxDisplaySyncEnableCheck(); //Display Sync. DMA reset on start of line 162. + break; + case 227: + this.statusFlags = this.statusFlags & 0x6; //Un-mark VBlank on start of last vblank line. + break; + case 228: + this.currentScanLine = 0; //Reset scan-line to zero (First line of draw). + } + } + else if ((this.currentScanLine | 0) > 1) { + this.dmaChannel3.gfxDisplaySyncRequest(); //Display Sync. DMA trigger. + } + this.checkVCounter(); //We're on a new scan line, so check the VCounter for match. + this.isRenderingCheckPreprocess(); //Update a check value. + //Recursive clocking of the LCD state: + this.clockLCDState(); +} +GameBoyAdvanceGraphics.prototype.updateHBlank = function () { + if ((this.statusFlags & 0x2) == 0) { //If we were last in HBlank, don't run this again. + this.statusFlags = this.statusFlags | 0x2; //Mark HBlank. + if ((this.IRQFlags & 0x10) != 0) { + this.irq.requestIRQ(0x2); //Check for IRQ. + } + if ((this.currentScanLine | 0) < 160) { + this.dma.gfxHBlankRequest(); //Check for HDMA Trigger. + } + this.isRenderingCheckPreprocess(); //Update a check value. + } +} +GameBoyAdvanceGraphics.prototype.checkVCounter = function () { + if ((this.currentScanLine | 0) == (this.VCounter | 0)) { //Check for VCounter match. + this.statusFlags = this.statusFlags | 0x4; + if ((this.IRQFlags & 0x20) != 0) { //Check for VCounter IRQ. + this.irq.requestIRQ(0x4); + } + } + else { + this.statusFlags = this.statusFlags & 0x3; + } +} +GameBoyAdvanceGraphics.prototype.nextVBlankIRQEventTime = function () { + var nextEventTime = 0x7FFFFFFF; + if ((this.IRQFlags & 0x8) != 0) { + //Only give a time if we're allowed to irq: + nextEventTime = this.nextVBlankEventTime() | 0; + } + return nextEventTime | 0; +} +GameBoyAdvanceGraphics.prototype.nextHBlankEventTime = function () { + var time = this.LCDTicks | 0; + if ((time | 0) < 1006) { + //Haven't reached hblank yet, so hblank offset - current: + time = (1006 - (time | 0)) | 0; + } + else { + //We're in hblank, so it's end clock - current + next scanline hblank offset: + time = (2238 - (time | 0)) | 0; + } + return time | 0; +} +GameBoyAdvanceGraphics.prototype.nextHBlankIRQEventTime = function () { + var nextEventTime = 0x7FFFFFFF; + if ((this.IRQFlags & 0x10) != 0) { + //Only give a time if we're allowed to irq: + nextEventTime = this.nextHBlankEventTime() | 0; + } + return nextEventTime | 0; +} +GameBoyAdvanceGraphics.prototype.nextVCounterIRQEventTime = function () { + var nextEventTime = 0x7FFFFFFF; + if ((this.IRQFlags & 0x20) != 0) { + //Only give a time if we're allowed to irq: + nextEventTime = this.nextVCounterEventTime() | 0; + } + return nextEventTime | 0; +} +GameBoyAdvanceGraphics.prototype.nextVBlankEventTime = function () { + var nextEventTime = this.currentScanLine | 0; + if ((nextEventTime | 0) < 160) { + //Haven't reached vblank yet, so vblank offset - current: + nextEventTime = (160 - (nextEventTime | 0)) | 0; + } + else { + //We're in vblank, so it's end clock - current + next frame vblank offset: + nextEventTime = (388 - (nextEventTime | 0)) | 0; + } + //Convert line count to clocks: + nextEventTime = this.convertScanlineToClocks(nextEventTime | 0) | 0; + //Subtract scanline offset from clocks: + nextEventTime = ((nextEventTime | 0) - (this.LCDTicks | 0)) | 0; + return nextEventTime | 0; +} +GameBoyAdvanceGraphics.prototype.nextHBlankDMAEventTime = function () { + var nextEventTime = this.nextHBlankEventTime() | 0; + if ((this.currentScanLine | 0) > 159 || ((this.currentScanLine | 0) == 159 && (this.LCDTicks | 0) >= 1006)) { + //No HBlank DMA in VBlank: + var linesToSkip = (227 - (this.currentScanLine | 0)) | 0; + linesToSkip = this.convertScanlineToClocks(linesToSkip | 0) | 0; + nextEventTime = ((nextEventTime | 0) + (linesToSkip | 0)) | 0; + } + return nextEventTime | 0; +} +GameBoyAdvanceGraphics.prototype.nextVCounterEventTime = function () { + var nextEventTime = 0x7FFFFFFF; + if ((this.VCounter | 0) <= 227) { + //Only match lines within screen or vblank: + nextEventTime = ((this.VCounter | 0) - (this.currentScanLine | 0)) | 0; + if ((nextEventTime | 0) <= 0) { + nextEventTime = ((nextEventTime | 0) + 228) | 0; + } + nextEventTime = this.convertScanlineToClocks(nextEventTime | 0) | 0; + nextEventTime = ((nextEventTime | 0) - (this.LCDTicks | 0)) | 0; + } + return nextEventTime | 0; +} +GameBoyAdvanceGraphics.prototype.nextDisplaySyncEventTime = function (delay) { + delay = delay | 0; + var nextEventTime = 0x7FFFFFFF; + if ((this.currentScanLine | 0) >= 161 || (delay | 0) != 0) { + //Skip to line 2 metrics: + nextEventTime = (230 - (this.currentScanLine | 0)) | 0; + nextEventTime = this.convertScanlineToClocks(nextEventTime | 0) | 0; + nextEventTime = ((nextEventTime | 0) - (this.LCDTicks | 0)) | 0; + } + else if ((this.currentScanLine | 0) == 0) { + //Doesn't start until line 2: + nextEventTime = (2464 - (this.LCDTicks | 0)) | 0; + } + else { + //Line 2 through line 161: + nextEventTime = (1232 - (this.LCDTicks | 0)) | 0; + } + return nextEventTime | 0; +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceGraphics.prototype.convertScanlineToClocks = function (lines) { + lines = lines | 0; + lines = Math.imul(lines | 0, 1232) | 0; + return lines | 0; + } +} +else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceGraphics.prototype.convertScanlineToClocks = function (lines) { + lines = lines | 0; + lines = ((lines | 0) * 1232) | 0; + return lines | 0; + } +} +GameBoyAdvanceGraphics.prototype.updateVBlankStart = function () { + this.statusFlags = this.statusFlags | 0x1; //Mark VBlank. + if ((this.IRQFlags & 0x8) != 0) { //Check for VBlank IRQ. + this.irq.requestIRQ(0x1); + } + this.gfxRenderer.ensureFraming(); + this.dma.gfxVBlankRequest(); +} +GameBoyAdvanceGraphics.prototype.isRenderingCheckPreprocess = function () { + var isInVisibleLines = ((this.gfxRenderer.IOData8[0] & 0x80) == 0 && (this.statusFlags & 0x1) == 0); + var isRendering = (isInVisibleLines && (this.statusFlags & 0x2) == 0) ? 2 : 1; + var isOAMRendering = (isInVisibleLines && ((this.statusFlags & 0x2) == 0 || (this.gfxRenderer.IOData8[0] & 0x20) == 0)) ? 2 : 1; + this.wait.updateRenderStatus(isRendering | 0, isOAMRendering | 0); +} +GameBoyAdvanceGraphics.prototype.writeDISPSTAT8_0 = function (data) { + data = data | 0; + this.IOCore.updateCoreClocking(); + //VBlank flag read only. + //HBlank flag read only. + //V-Counter flag read only. + //Only LCD IRQ generation enablers can be set here: + this.IRQFlags = data & 0x38; + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceGraphics.prototype.writeDISPSTAT8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + //V-Counter match value: + if ((data | 0) != (this.VCounter | 0)) { + this.IOCore.updateCoreClocking(); + this.VCounter = data | 0; + this.checkVCounter(); + this.IOCore.updateCoreEventTime(); + } +} +GameBoyAdvanceGraphics.prototype.writeDISPSTAT16 = function (data) { + data = data | 0; + this.IOCore.updateCoreClocking(); + //VBlank flag read only. + //HBlank flag read only. + //V-Counter flag read only. + //Only LCD IRQ generation enablers can be set here: + this.IRQFlags = data & 0x38; + data = (data >> 8) & 0xFF; + //V-Counter match value: + if ((data | 0) != (this.VCounter | 0)) { + this.VCounter = data | 0; + this.checkVCounter(); + } + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceGraphics.prototype.readDISPSTAT8_0 = function () { + this.IOCore.updateGraphicsClocking(); + return (this.statusFlags | this.IRQFlags); +} +GameBoyAdvanceGraphics.prototype.readDISPSTAT8_1 = function () { + return this.VCounter | 0; +} +GameBoyAdvanceGraphics.prototype.readDISPSTAT8_2 = function () { + this.IOCore.updateGraphicsClocking(); + return this.currentScanLine | 0; +} +GameBoyAdvanceGraphics.prototype.readDISPSTAT16_0 = function () { + this.IOCore.updateGraphicsClocking(); + return ((this.VCounter << 8) | this.statusFlags | this.IRQFlags); +} +GameBoyAdvanceGraphics.prototype.readDISPSTAT32 = function () { + this.IOCore.updateGraphicsClocking(); + return ((this.currentScanLine << 16) | (this.VCounter << 8) | this.statusFlags | this.IRQFlags); +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/IRQ.js b/public/gfiles/gba/IodineGBA/core/IRQ.js new file mode 100644 index 00000000..1dd9bd65 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/IRQ.js @@ -0,0 +1,180 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceIRQ(IOCore) { + //Build references: + this.IOCore = IOCore; +} +GameBoyAdvanceIRQ.prototype.initialize = function () { + this.interruptsEnabled = 0; + this.interruptsRequested = 0; + this.IME = 0; + this.gfxState = this.IOCore.gfxState; + this.timer = this.IOCore.timer; + this.dmaChannel0 = this.IOCore.dmaChannel0; + this.dmaChannel1 = this.IOCore.dmaChannel1; + this.dmaChannel2 = this.IOCore.dmaChannel2; + this.dmaChannel3 = this.IOCore.dmaChannel3; +} +GameBoyAdvanceIRQ.prototype.IRQMatch = function () { + //Used to exit HALT: + return (this.interruptsEnabled & this.interruptsRequested); +} +GameBoyAdvanceIRQ.prototype.checkForIRQFire = function () { + //Tell the CPU core when the emulated hardware is triggering an IRQ: + this.IOCore.cpu.triggerIRQ(this.interruptsEnabled & this.interruptsRequested & this.IME); +} +GameBoyAdvanceIRQ.prototype.requestIRQ = function (irqLineToSet) { + irqLineToSet = irqLineToSet | 0; + this.interruptsRequested = this.interruptsRequested | irqLineToSet; + this.checkForIRQFire(); +} +GameBoyAdvanceIRQ.prototype.writeIME = function (data) { + data = data | 0; + this.IOCore.updateCoreClocking(); + this.IME = (data << 31) >> 31; + this.checkForIRQFire(); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceIRQ.prototype.writeIE8_0 = function (data) { + data = data | 0; + this.IOCore.updateCoreClocking(); + var oldValue = this.interruptsEnabled & 0x3F00; + data = data & 0xFF; + data = data | oldValue; + this.interruptsEnabled = data | 0; + this.checkForIRQFire(); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceIRQ.prototype.writeIE8_1 = function (data) { + data = data | 0; + this.IOCore.updateCoreClocking(); + var oldValue = this.interruptsEnabled & 0xFF; + data = (data & 0x3F) << 8; + data = data | oldValue; + this.interruptsEnabled = data | 0; + this.checkForIRQFire(); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceIRQ.prototype.writeIE16 = function (data) { + data = data | 0; + this.IOCore.updateCoreClocking(); + this.interruptsEnabled = data & 0x3FFF; + this.checkForIRQFire(); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceIRQ.prototype.writeIF8_0 = function (data) { + data = data | 0; + this.IOCore.updateCoreClocking(); + data = ~(data & 0xFF); + this.interruptsRequested = this.interruptsRequested & data; + this.checkForIRQFire(); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceIRQ.prototype.writeIF8_1 = function (data) { + data = data | 0; + this.IOCore.updateCoreClocking(); + data = ~((data & 0xFF) << 8); + this.interruptsRequested = this.interruptsRequested & data; + this.checkForIRQFire(); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceIRQ.prototype.writeIF16 = function (data) { + data = data | 0; + this.IOCore.updateCoreClocking(); + data = ~data; + this.interruptsRequested = this.interruptsRequested & data; + this.checkForIRQFire(); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceIRQ.prototype.writeIRQ32 = function (data) { + data = data | 0; + this.IOCore.updateCoreClocking(); + this.interruptsEnabled = data & 0x3FFF; + data = ~(data >> 16); + this.interruptsRequested = this.interruptsRequested & data; + this.checkForIRQFire(); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceIRQ.prototype.readIME = function () { + var data = this.IME & 0x1; + return data | 0; +} +GameBoyAdvanceIRQ.prototype.readIE8_0 = function () { + var data = this.interruptsEnabled & 0xFF; + return data | 0; +} +GameBoyAdvanceIRQ.prototype.readIE8_1 = function () { + var data = this.interruptsEnabled >> 8; + return data | 0; +} +GameBoyAdvanceIRQ.prototype.readIE16 = function () { + var data = this.interruptsEnabled | 0; + return data | 0; +} +GameBoyAdvanceIRQ.prototype.readIF8_0 = function () { + this.IOCore.updateCoreSpillRetain(); + var data = this.interruptsRequested & 0xFF; + return data | 0; +} +GameBoyAdvanceIRQ.prototype.readIF8_1 = function () { + this.IOCore.updateCoreSpillRetain(); + var data = this.interruptsRequested >> 8; + return data | 0; +} +GameBoyAdvanceIRQ.prototype.readIF16 = function () { + this.IOCore.updateCoreSpillRetain(); + var data = this.interruptsRequested | 0; + return data | 0; +} +GameBoyAdvanceIRQ.prototype.readIRQ32 = function () { + this.IOCore.updateCoreSpillRetain(); + var data = (this.interruptsRequested << 16) | this.interruptsEnabled; + return data | 0; +} +GameBoyAdvanceIRQ.prototype.nextEventTime = function () { + var clocks = 0x7FFFFFFF; + if ((this.interruptsEnabled & 0x1) != 0) { + clocks = this.gfxState.nextVBlankIRQEventTime() | 0; + } + if ((this.interruptsEnabled & 0x2) != 0) { + clocks = Math.min(clocks | 0, this.gfxState.nextHBlankIRQEventTime() | 0) | 0; + } + if ((this.interruptsEnabled & 0x4) != 0) { + clocks = Math.min(clocks | 0, this.gfxState.nextVCounterIRQEventTime() | 0) | 0; + } + if ((this.interruptsEnabled & 0x8) != 0) { + clocks = Math.min(clocks | 0, this.timer.nextTimer0IRQEventTime() | 0) | 0; + } + if ((this.interruptsEnabled & 0x10) != 0) { + clocks = Math.min(clocks | 0, this.timer.nextTimer1IRQEventTime() | 0) | 0; + } + if ((this.interruptsEnabled & 0x20) != 0) { + clocks = Math.min(clocks | 0, this.timer.nextTimer2IRQEventTime() | 0) | 0; + } + if ((this.interruptsEnabled & 0x40) != 0) { + clocks = Math.min(clocks | 0, this.timer.nextTimer3IRQEventTime() | 0) | 0; + } + /*if ((this.interruptsEnabled & 0x80) != 0) { + clocks = Math.min(clocks | 0, this.IOCore.serial.nextIRQEventTime() | 0) | 0; + } + if ((this.interruptsEnabled & 0x2000) != 0) { + clocks = Math.min(clocks | 0, this.IOCore.cartridge.nextIRQEventTime() | 0) | 0; + }*/ + return clocks | 0; +} +GameBoyAdvanceIRQ.prototype.nextIRQEventTime = function () { + var clocks = 0x7FFFFFFF; + //Checks IME: + if ((this.IME | 0) != 0) { + clocks = this.nextEventTime() | 0; + } + return clocks | 0; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/JoyPad.js b/public/gfiles/gba/IodineGBA/core/JoyPad.js new file mode 100644 index 00000000..77a176e1 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/JoyPad.js @@ -0,0 +1,83 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceJoyPad(IOCore) { + this.IOCore = IOCore; +} +GameBoyAdvanceJoyPad.prototype.initialize = function () { + this.keyInput = 0x3FF; + this.keyInterrupt = 0; +} +GameBoyAdvanceJoyPad.prototype.keyPress = function (keyPressed) { + keyPressed = keyPressed | 0; + keyPressed = 1 << (keyPressed | 0); + this.keyInput = this.keyInput & (~keyPressed); + this.checkForMatch(); +} +GameBoyAdvanceJoyPad.prototype.keyRelease = function (keyReleased) { + keyReleased = keyReleased | 0; + keyReleased = 1 << (keyReleased | 0); + this.keyInput = this.keyInput | keyReleased; + this.checkForMatch(); +} +GameBoyAdvanceJoyPad.prototype.checkForMatch = function () { + if ((this.keyInterrupt & 0x8000) != 0) { + if (((~this.keyInput) & this.keyInterrupt & 0x3FF) == (this.keyInterrupt & 0x3FF)) { + this.IOCore.deflagStop(); + this.checkForIRQ(); + } + } + else if (((~this.keyInput) & this.keyInterrupt & 0x3FF) != 0) { + this.IOCore.deflagStop(); + this.checkForIRQ(); + } +} +GameBoyAdvanceJoyPad.prototype.checkForIRQ = function () { + if ((this.keyInterrupt & 0x4000) != 0) { + this.IOCore.irq.requestIRQ(0x1000); + } +} +GameBoyAdvanceJoyPad.prototype.readKeyStatus8_0 = function () { + return this.keyInput & 0xFF; +} +GameBoyAdvanceJoyPad.prototype.readKeyStatus8_1 = function () { + return (this.keyInput >> 8) | 0; +} +GameBoyAdvanceJoyPad.prototype.readKeyStatus16 = function () { + return this.keyInput | 0; +} +GameBoyAdvanceJoyPad.prototype.writeKeyControl8_0 = function (data) { + data = data | 0; + this.keyInterrupt = this.keyInterrupt & 0xC300; + data = data & 0xFF; + this.keyInterrupt = this.keyInterrupt | data; +} +GameBoyAdvanceJoyPad.prototype.writeKeyControl8_1 = function (data) { + data = data | 0; + this.keyInterrupt = this.keyInterrupt & 0xFF; + data = data & 0xC3; + this.keyInterrupt = this.keyInterrupt | (data << 8); +} +GameBoyAdvanceJoyPad.prototype.writeKeyControl16 = function (data) { + data = data | 0; + this.keyInterrupt = data & 0xC3FF; +} +GameBoyAdvanceJoyPad.prototype.readKeyControl8_0 = function () { + return this.keyInterrupt & 0xFF; +} +GameBoyAdvanceJoyPad.prototype.readKeyControl8_1 = function () { + return (this.keyInterrupt >> 8) | 0; +} +GameBoyAdvanceJoyPad.prototype.readKeyControl16 = function () { + return this.keyInterrupt | 0; +} +GameBoyAdvanceJoyPad.prototype.readKeyStatusControl32 = function () { + return this.keyInput | (this.keyInterrupt << 16); +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/Memory.js b/public/gfiles/gba/IodineGBA/core/Memory.js new file mode 100644 index 00000000..1eea60d8 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/Memory.js @@ -0,0 +1,5223 @@ +"use strict"; +/* + Copyright (C) 2012-2016 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceMemory(IOCore) { + //Reference to the emulator core: + this.IOCore = IOCore; +} +GameBoyAdvanceMemory.prototype.initialize = function () { + var allowInit = 1; + //Load the BIOS: + this.BIOS = getUint8Array(0x4000); + this.BIOS16 = getUint16View(this.BIOS); + this.BIOS32 = getInt32View(this.BIOS); + if ((this.loadBIOS() | 0) == 1) { + this.initializeRAM(); + } + else { + allowInit = 0; + } + return allowInit | 0; +} +GameBoyAdvanceMemory.prototype.initializeRAM = function () { + //WRAM Map Control Stuff: + this.WRAMControlFlags = 0x20; + //Initialize Some RAM: + this.externalRAM = getUint8Array(0x40000); + this.externalRAM16 = getUint16View(this.externalRAM); + this.externalRAM32 = getInt32View(this.externalRAM); + this.internalRAM = getUint8Array(0x8000); + this.internalRAM16 = getUint16View(this.internalRAM); + this.internalRAM32 = getInt32View(this.internalRAM); + this.lastBIOSREAD = 0; //BIOS read bus last. + //Initialize the various handler objects: + this.dma = this.IOCore.dma; + this.dmaChannel0 = this.IOCore.dmaChannel0; + this.dmaChannel1 = this.IOCore.dmaChannel1; + this.dmaChannel2 = this.IOCore.dmaChannel2; + this.dmaChannel3 = this.IOCore.dmaChannel3; + this.gfxState = this.IOCore.gfxState; + this.gfxRenderer = this.IOCore.gfxRenderer; + this.sound = this.IOCore.sound; + this.timer = this.IOCore.timer; + this.irq = this.IOCore.irq; + this.serial = this.IOCore.serial; + this.joypad = this.IOCore.joypad; + this.cartridge = this.IOCore.cartridge; + this.wait = this.IOCore.wait; + this.cpu = this.IOCore.cpu; + this.saves = this.IOCore.saves; +} +GameBoyAdvanceMemory.prototype.writeExternalWRAM8 = function (address, data) { + address = address | 0; + data = data | 0; + //External WRAM: + this.wait.WRAMAccess(); + this.externalRAM[address & 0x3FFFF] = data & 0xFF; +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceMemory.prototype.writeExternalWRAM16 = function (address, data) { + address = address | 0; + data = data | 0; + //External WRAM: + this.wait.WRAMAccess(); + this.externalRAM16[(address >> 1) & 0x1FFFF] = data & 0xFFFF; + } + GameBoyAdvanceMemory.prototype.writeExternalWRAM32 = function (address, data) { + address = address | 0; + data = data | 0; + //External WRAM: + this.wait.WRAMAccess32(); + this.externalRAM32[(address >> 2) & 0xFFFF] = data | 0; + } +} +else { + GameBoyAdvanceMemory.prototype.writeExternalWRAM16 = function (address, data) { + //External WRAM: + this.wait.WRAMAccess(); + address &= 0x3FFFE; + this.externalRAM[address++] = data & 0xFF; + this.externalRAM[address] = (data >> 8) & 0xFF; + } + GameBoyAdvanceMemory.prototype.writeExternalWRAM32 = function (address, data) { + //External WRAM: + this.wait.WRAMAccess32(); + address &= 0x3FFFC; + this.externalRAM[address++] = data & 0xFF; + this.externalRAM[address++] = (data >> 8) & 0xFF; + this.externalRAM[address++] = (data >> 16) & 0xFF; + this.externalRAM[address] = data >>> 24; + } +} +GameBoyAdvanceMemory.prototype.writeInternalWRAM8 = function (address, data) { + address = address | 0; + data = data | 0; + //Internal WRAM: + this.wait.singleClock(); + this.internalRAM[address & 0x7FFF] = data & 0xFF; +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceMemory.prototype.writeInternalWRAM16 = function (address, data) { + address = address | 0; + data = data | 0; + //Internal WRAM: + this.wait.singleClock(); + this.internalRAM16[(address >> 1) & 0x3FFF] = data & 0xFFFF; + } + GameBoyAdvanceMemory.prototype.writeInternalWRAM32 = function (address, data) { + address = address | 0; + data = data | 0; + //Internal WRAM: + this.wait.singleClock(); + this.internalRAM32[(address >> 2) & 0x1FFF] = data | 0; + } +} +else { + GameBoyAdvanceMemory.prototype.writeInternalWRAM16 = function (address, data) { + //Internal WRAM: + this.wait.singleClock(); + address &= 0x7FFE; + this.internalRAM[address++] = data & 0xFF; + this.internalRAM[address] = (data >> 8) & 0xFF; + } + GameBoyAdvanceMemory.prototype.writeInternalWRAM32 = function (address, data) { + //Internal WRAM: + this.wait.singleClock(); + address &= 0x7FFC; + this.internalRAM[address++] = data & 0xFF; + this.internalRAM[address++] = (data >> 8) & 0xFF; + this.internalRAM[address++] = (data >> 16) & 0xFF; + this.internalRAM[address] = data >>> 24; + } +} +GameBoyAdvanceMemory.prototype.writeIODispatch8 = function (address, data) { + address = address | 0; + data = data | 0; + this.wait.singleClock(); + switch (address | 0) { + //4000000h - DISPCNT - LCD Control (Read/Write) + case 0x4000000: + this.gfxRenderer.writeDISPCNT8_0(data | 0); + break; + //4000001h - DISPCNT - LCD Control (Read/Write) + case 0x4000001: + this.gfxRenderer.writeDISPCNT8_1(data | 0); + break; + //4000002h - Undocumented - Green Swap (R/W) + case 0x4000002: + this.gfxRenderer.writeDISPCNT8_2(data | 0); + break; + //4000003h - Undocumented - Green Swap (R/W) + //4000004h - DISPSTAT - General LCD Status (Read/Write) + case 0x4000004: + this.gfxState.writeDISPSTAT8_0(data | 0); + break; + //4000005h - DISPSTAT - General LCD Status (Read/Write) + case 0x4000005: + this.gfxState.writeDISPSTAT8_1(data | 0); + break; + //4000006h - VCOUNT - Vertical Counter (Read only) + //4000007h - VCOUNT - Vertical Counter (Read only) + //4000008h - BG0CNT - BG0 Control (R/W) (BG Modes 0,1 only) + case 0x4000008: + this.gfxRenderer.writeBG0CNT8_0(data | 0); + break; + //4000009h - BG0CNT - BG0 Control (R/W) (BG Modes 0,1 only) + case 0x4000009: + this.gfxRenderer.writeBG0CNT8_1(data | 0); + break; + //400000Ah - BG1CNT - BG1 Control (R/W) (BG Modes 0,1 only) + case 0x400000A: + this.gfxRenderer.writeBG1CNT8_0(data | 0); + break; + //400000Bh - BG1CNT - BG1 Control (R/W) (BG Modes 0,1 only) + case 0x400000B: + this.gfxRenderer.writeBG1CNT8_1(data | 0); + break; + //400000Ch - BG2CNT - BG2 Control (R/W) (BG Modes 0,1,2 only) + case 0x400000C: + this.gfxRenderer.writeBG2CNT8_0(data | 0); + break; + //400000Dh - BG2CNT - BG2 Control (R/W) (BG Modes 0,1,2 only) + case 0x400000D: + this.gfxRenderer.writeBG2CNT8_1(data | 0); + break; + //400000Eh - BG3CNT - BG3 Control (R/W) (BG Modes 0,2 only) + case 0x400000E: + this.gfxRenderer.writeBG3CNT8_0(data | 0); + break; + //400000Fh - BG3CNT - BG3 Control (R/W) (BG Modes 0,2 only) + case 0x400000F: + this.gfxRenderer.writeBG3CNT8_1(data | 0); + break; + //4000010h - BG0HOFS - BG0 X-Offset (W) + case 0x4000010: + this.gfxRenderer.writeBG0HOFS8_0(data | 0); + break; + //4000011h - BG0HOFS - BG0 X-Offset (W) + case 0x4000011: + this.gfxRenderer.writeBG0HOFS8_1(data | 0); + break; + //4000012h - BG0VOFS - BG0 Y-Offset (W) + case 0x4000012: + this.gfxRenderer.writeBG0VOFS8_0(data | 0); + break; + //4000013h - BG0VOFS - BG0 Y-Offset (W) + case 0x4000013: + this.gfxRenderer.writeBG0VOFS8_1(data | 0); + break; + //4000014h - BG1HOFS - BG1 X-Offset (W) + case 0x4000014: + this.gfxRenderer.writeBG1HOFS8_0(data | 0); + break; + //4000015h - BG1HOFS - BG1 X-Offset (W) + case 0x4000015: + this.gfxRenderer.writeBG1HOFS8_1(data | 0); + break; + //4000016h - BG1VOFS - BG1 Y-Offset (W) + case 0x4000016: + this.gfxRenderer.writeBG1VOFS8_0(data | 0); + break; + //4000017h - BG1VOFS - BG1 Y-Offset (W) + case 0x4000017: + this.gfxRenderer.writeBG1VOFS8_1(data | 0); + break; + //4000018h - BG2HOFS - BG2 X-Offset (W) + case 0x4000018: + this.gfxRenderer.writeBG2HOFS8_0(data | 0); + break; + //4000019h - BG2HOFS - BG2 X-Offset (W) + case 0x4000019: + this.gfxRenderer.writeBG2HOFS8_1(data | 0); + break; + //400001Ah - BG2VOFS - BG2 Y-Offset (W) + case 0x400001A: + this.gfxRenderer.writeBG2VOFS8_0(data | 0); + break; + //400001Bh - BG2VOFS - BG2 Y-Offset (W) + case 0x400001B: + this.gfxRenderer.writeBG2VOFS8_1(data | 0); + break; + //400001Ch - BG3HOFS - BG3 X-Offset (W) + case 0x400001C: + this.gfxRenderer.writeBG3HOFS8_0(data | 0); + break; + //400001Dh - BG3HOFS - BG3 X-Offset (W) + case 0x400001D: + this.gfxRenderer.writeBG3HOFS8_1(data | 0); + break; + //400001Eh - BG3VOFS - BG3 Y-Offset (W) + case 0x400001E: + this.gfxRenderer.writeBG3VOFS8_0(data | 0); + break; + //400001Fh - BG3VOFS - BG3 Y-Offset (W) + case 0x400001F: + this.gfxRenderer.writeBG3VOFS8_1(data | 0); + break; + //4000020h - BG2PA - BG2 Rotation/Scaling Parameter A (alias dx) (W) + case 0x4000020: + this.gfxRenderer.writeBG2PA8_0(data | 0); + break; + //4000021h - BG2PA - BG2 Rotation/Scaling Parameter A (alias dx) (W) + case 0x4000021: + this.gfxRenderer.writeBG2PA8_1(data | 0); + break; + //4000022h - BG2PB - BG2 Rotation/Scaling Parameter B (alias dmx) (W) + case 0x4000022: + this.gfxRenderer.writeBG2PB8_0(data | 0); + break; + //4000023h - BG2PB - BG2 Rotation/Scaling Parameter B (alias dmx) (W) + case 0x4000023: + this.gfxRenderer.writeBG2PB8_1(data | 0); + break; + //4000024h - BG2PC - BG2 Rotation/Scaling Parameter C (alias dy) (W) + case 0x4000024: + this.gfxRenderer.writeBG2PC8_0(data | 0); + break; + //4000025h - BG2PC - BG2 Rotation/Scaling Parameter C (alias dy) (W) + case 0x4000025: + this.gfxRenderer.writeBG2PC8_1(data | 0); + break; + //4000026h - BG2PD - BG2 Rotation/Scaling Parameter D (alias dmy) (W) + case 0x4000026: + this.gfxRenderer.writeBG2PD8_0(data | 0); + break; + //4000027h - BG2PD - BG2 Rotation/Scaling Parameter D (alias dmy) (W) + case 0x4000027: + this.gfxRenderer.writeBG2PD8_1(data | 0); + break; + //4000028h - BG2X_L - BG2 Reference Point X-Coordinate, lower 16 bit (W) + case 0x4000028: + this.gfxRenderer.writeBG2X8_0(data | 0); + break; + //4000029h - BG2X_L - BG2 Reference Point X-Coordinate, lower 16 bit (W) + case 0x4000029: + this.gfxRenderer.writeBG2X8_1(data | 0); + break; + //400002Ah - BG2X_H - BG2 Reference Point X-Coordinate, upper 12 bit (W) + case 0x400002A: + this.gfxRenderer.writeBG2X8_2(data | 0); + break; + //400002Bh - BG2X_H - BG2 Reference Point X-Coordinate, upper 12 bit (W) + case 0x400002B: + this.gfxRenderer.writeBG2X8_3(data | 0); + break; + //400002Ch - BG2Y_L - BG2 Reference Point Y-Coordinate, lower 16 bit (W) + case 0x400002C: + this.gfxRenderer.writeBG2Y8_0(data | 0); + break; + //400002Dh - BG2Y_L - BG2 Reference Point Y-Coordinate, lower 16 bit (W) + case 0x400002D: + this.gfxRenderer.writeBG2Y8_1(data | 0); + break; + //400002Eh - BG2Y_H - BG2 Reference Point Y-Coordinate, upper 12 bit (W) + case 0x400002E: + this.gfxRenderer.writeBG2Y8_2(data | 0); + break; + //400002Fh - BG2Y_H - BG2 Reference Point Y-Coordinate, upper 12 bit (W) + case 0x400002F: + this.gfxRenderer.writeBG2Y8_3(data | 0); + break; + //4000030h - BG3PA - BG3 Rotation/Scaling Parameter A (alias dx) (W) + case 0x4000030: + this.gfxRenderer.writeBG3PA8_0(data | 0); + break; + //4000031h - BG3PA - BG3 Rotation/Scaling Parameter A (alias dx) (W) + case 0x4000031: + this.gfxRenderer.writeBG3PA8_1(data | 0); + break; + //4000032h - BG3PB - BG3 Rotation/Scaling Parameter B (alias dmx) (W) + case 0x4000032: + this.gfxRenderer.writeBG3PB8_0(data | 0); + break; + //4000033h - BG3PB - BG3 Rotation/Scaling Parameter B (alias dmx) (W) + case 0x4000033: + this.gfxRenderer.writeBG3PB8_1(data | 0); + break; + //4000034h - BG3PC - BG3 Rotation/Scaling Parameter C (alias dy) (W) + case 0x4000034: + this.gfxRenderer.writeBG3PC8_0(data | 0); + break; + //4000035h - BG3PC - BG3 Rotation/Scaling Parameter C (alias dy) (W) + case 0x4000035: + this.gfxRenderer.writeBG3PC8_1(data | 0); + break; + //4000036h - BG3PD - BG3 Rotation/Scaling Parameter D (alias dmy) (W) + case 0x4000036: + this.gfxRenderer.writeBG3PD8_0(data | 0); + break; + //4000037h - BG3PD - BG3 Rotation/Scaling Parameter D (alias dmy) (W) + case 0x4000037: + this.gfxRenderer.writeBG3PD8_1(data | 0); + break; + //4000038h - BG3X_L - BG3 Reference Point X-Coordinate, lower 16 bit (W) + case 0x4000038: + this.gfxRenderer.writeBG3X8_0(data | 0); + break; + //4000039h - BG3X_L - BG3 Reference Point X-Coordinate, lower 16 bit (W) + case 0x4000039: + this.gfxRenderer.writeBG3X8_1(data | 0); + break; + //400003Ah - BG3X_H - BG3 Reference Point X-Coordinate, upper 12 bit (W) + case 0x400003A: + this.gfxRenderer.writeBG3X8_2(data | 0); + break; + //400003Bh - BG3X_H - BG3 Reference Point X-Coordinate, upper 12 bit (W) + case 0x400003B: + this.gfxRenderer.writeBG3X8_3(data | 0); + break; + //400003Ch - BG3Y_L - BG3 Reference Point Y-Coordinate, lower 16 bit (W) + case 0x400003C: + this.gfxRenderer.writeBG3Y8_0(data | 0); + break; + //400003Dh - BGY_L - BG3 Reference Point Y-Coordinate, lower 16 bit (W) + case 0x400003D: + this.gfxRenderer.writeBG3Y8_1(data | 0); + break; + //400003Eh - BG3Y_H - BG3 Reference Point Y-Coordinate, upper 12 bit (W) + case 0x400003E: + this.gfxRenderer.writeBG3Y8_2(data | 0); + break; + //400003Fh - BG3Y_H - BG3 Reference Point Y-Coordinate, upper 12 bit (W) + case 0x400003F: + this.gfxRenderer.writeBG3Y8_3(data | 0); + break; + //4000040h - WIN0H - Window 0 Horizontal Dimensions (W) + case 0x4000040: + this.gfxRenderer.writeWIN0XCOORDRight8(data | 0); + break; + //4000041h - WIN0H - Window 0 Horizontal Dimensions (W) + case 0x4000041: + this.gfxRenderer.writeWIN0XCOORDLeft8(data | 0); + break; + //4000042h - WIN1H - Window 1 Horizontal Dimensions (W) + case 0x4000042: + this.gfxRenderer.writeWIN1XCOORDRight8(data | 0); + break; + //4000043h - WIN1H - Window 1 Horizontal Dimensions (W) + case 0x4000043: + this.gfxRenderer.writeWIN1XCOORDLeft8(data | 0); + break; + //4000044h - WIN0V - Window 0 Vertical Dimensions (W) + case 0x4000044: + this.gfxRenderer.writeWIN0YCOORDBottom8(data | 0); + break; + //4000045h - WIN0V - Window 0 Vertical Dimensions (W) + case 0x4000045: + this.gfxRenderer.writeWIN0YCOORDTop8(data | 0); + break; + //4000046h - WIN1V - Window 1 Vertical Dimensions (W) + case 0x4000046: + this.gfxRenderer.writeWIN1YCOORDBottom8(data | 0); + break; + //4000047h - WIN1V - Window 1 Vertical Dimensions (W) + case 0x4000047: + this.gfxRenderer.writeWIN1YCOORDTop8(data | 0); + break; + //4000048h - WININ - Control of Inside of Window(s) (R/W) + case 0x4000048: + this.gfxRenderer.writeWIN0IN8(data | 0); + break; + //4000049h - WININ - Control of Inside of Window(s) (R/W) + case 0x4000049: + this.gfxRenderer.writeWIN1IN8(data | 0); + break; + //400004Ah- WINOUT - Control of Outside of Windows (R/W) + case 0x400004A: + this.gfxRenderer.writeWINOUT8(data | 0); + break; + //400004AB- WINOUT - Inside of OBJ Window (R/W) + case 0x400004B: + this.gfxRenderer.writeWINOBJIN8(data | 0); + break; + //400004Ch - MOSAIC - Mosaic Size (W) + case 0x400004C: + this.gfxRenderer.writeMOSAIC8_0(data | 0); + break; + //400004Dh - MOSAIC - Mosaic Size (W) + case 0x400004D: + this.gfxRenderer.writeMOSAIC8_1(data | 0); + break; + //400004Eh - NOT USED - ZERO + //400004Fh - NOT USED - ZERO + //4000050h - BLDCNT - Color Special Effects Selection (R/W) + case 0x4000050: + this.gfxRenderer.writeBLDCNT8_0(data | 0); + break; + //4000051h - BLDCNT - Color Special Effects Selection (R/W) + case 0x4000051: + this.gfxRenderer.writeBLDCNT8_1(data | 0); + break; + //4000052h - BLDALPHA - Alpha Blending Coefficients (R/W) + case 0x4000052: + this.gfxRenderer.writeBLDALPHA8_0(data | 0); + break; + //4000053h - BLDALPHA - Alpha Blending Coefficients (R/W) + case 0x4000053: + this.gfxRenderer.writeBLDALPHA8_1(data | 0); + break; + //4000054h - BLDY - Brightness (Fade-In/Out) Coefficient (W) + case 0x4000054: + this.gfxRenderer.writeBLDY8(data | 0); + break; + //4000055h through 400005Fh - NOT USED - ZERO/GLITCHED + //4000060h - SOUND1CNT_L (NR10) - Channel 1 Sweep register (R/W) + case 0x4000060: + //NR10: + this.sound.writeSOUND1CNT8_0(data | 0); + break; + //4000061h - NOT USED - ZERO + //4000062h - SOUND1CNT_H (NR11, NR12) - Channel 1 Duty/Len/Envelope (R/W) + case 0x4000062: + //NR11: + this.sound.writeSOUND1CNT8_2(data | 0); + break; + //4000063h - SOUND1CNT_H (NR11, NR12) - Channel 1 Duty/Len/Envelope (R/W) + case 0x4000063: + //NR12: + this.sound.writeSOUND1CNT8_3(data | 0); + break; + //4000064h - SOUND1CNT_X (NR13, NR14) - Channel 1 Frequency/Control (R/W) + case 0x4000064: + //NR13: + this.sound.writeSOUND1CNTX8_0(data | 0); + break; + //4000065h - SOUND1CNT_X (NR13, NR14) - Channel 1 Frequency/Control (R/W) + case 0x4000065: + //NR14: + this.sound.writeSOUND1CNTX8_1(data | 0); + break; + //4000066h - NOT USED - ZERO + //4000067h - NOT USED - ZERO + //4000068h - SOUND2CNT_L (NR21, NR22) - Channel 2 Duty/Length/Envelope (R/W) + case 0x4000068: + //NR21: + this.sound.writeSOUND2CNTL8_0(data | 0); + break; + //4000069h - SOUND2CNT_L (NR21, NR22) - Channel 2 Duty/Length/Envelope (R/W) + case 0x4000069: + //NR22: + this.sound.writeSOUND2CNTL8_1(data | 0); + break; + //400006Ah - NOT USED - ZERO + //400006Bh - NOT USED - ZERO + //400006Ch - SOUND2CNT_H (NR23, NR24) - Channel 2 Frequency/Control (R/W) + case 0x400006C: + //NR23: + this.sound.writeSOUND2CNTH8_0(data | 0); + break; + //400006Dh - SOUND2CNT_H (NR23, NR24) - Channel 2 Frequency/Control (R/W) + case 0x400006D: + //NR24: + this.sound.writeSOUND2CNTH8_1(data | 0); + break; + //400006Eh - NOT USED - ZERO + //400006Fh - NOT USED - ZERO + //4000070h - SOUND3CNT_L (NR30) - Channel 3 Stop/Wave RAM select (R/W) + case 0x4000070: + //NR30: + this.sound.writeSOUND3CNT8_0(data | 0); + break; + //4000071h - SOUND3CNT_L (NR30) - Channel 3 Stop/Wave RAM select (R/W) + //4000072h - SOUND3CNT_H (NR31, NR32) - Channel 3 Length/Volume (R/W) + case 0x4000072: + //NR31: + this.sound.writeSOUND3CNT8_2(data | 0); + break; + //4000073h - SOUND3CNT_H (NR31, NR32) - Channel 3 Length/Volume (R/W) + case 0x4000073: + //NR32: + this.sound.writeSOUND3CNT8_3(data | 0); + break; + //4000074h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W) + case 0x4000074: + //NR33: + this.sound.writeSOUND3CNTX8_0(data | 0); + break; + //4000075h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W) + case 0x4000075: + //NR34: + this.sound.writeSOUND3CNTX8_1(data | 0); + break; + //4000076h - NOT USED - ZERO + //4000077h - NOT USED - ZERO + //4000078h - SOUND4CNT_L (NR41, NR42) - Channel 4 Length/Envelope (R/W) + case 0x4000078: + //NR41: + this.sound.writeSOUND4CNTL8_0(data | 0); + break; + //4000079h - SOUND4CNT_L (NR41, NR42) - Channel 4 Length/Envelope (R/W) + case 0x4000079: + //NR42: + this.sound.writeSOUND4CNTL8_1(data | 0); + break; + //400007Ah - NOT USED - ZERO + //400007Bh - NOT USED - ZERO + //400007Ch - SOUND4CNT_H (NR43, NR44) - Channel 4 Frequency/Control (R/W) + case 0x400007C: + //NR43: + this.sound.writeSOUND4CNTH8_0(data | 0); + break; + //400007Dh - SOUND4CNT_H (NR43, NR44) - Channel 4 Frequency/Control (R/W) + case 0x400007D: + //NR44: + this.sound.writeSOUND4CNTH8_1(data | 0); + break; + //400007Eh - NOT USED - ZERO + //400007Fh - NOT USED - ZERO + //4000080h - SOUNDCNT_L (NR50, NR51) - Channel L/R Volume/Enable (R/W) + case 0x4000080: + //NR50: + this.sound.writeSOUNDCNTL8_0(data | 0); + break; + //4000081h - SOUNDCNT_L (NR50, NR51) - Channel L/R Volume/Enable (R/W) + case 0x4000081: + //NR51: + this.sound.writeSOUNDCNTL8_1(data | 0); + break; + //4000082h - SOUNDCNT_H (GBA only) - DMA Sound Control/Mixing (R/W) + case 0x4000082: + this.sound.writeSOUNDCNTH8_0(data | 0); + break; + //4000083h - SOUNDCNT_H (GBA only) - DMA Sound Control/Mixing (R/W) + case 0x4000083: + this.sound.writeSOUNDCNTH8_1(data | 0); + break; + //4000084h - SOUNDCNT_X (NR52) - Sound on/off (R/W) + case 0x4000084: + this.sound.writeSOUNDCNTX8(data | 0); + break; + //4000085h - NOT USED - ZERO + //4000086h - NOT USED - ZERO + //4000087h - NOT USED - ZERO + //4000088h - SOUNDBIAS - Sound PWM Control (R/W) + case 0x4000088: + this.sound.writeSOUNDBIAS8_0(data | 0); + break; + //4000089h - SOUNDBIAS - Sound PWM Control (R/W) + case 0x4000089: + this.sound.writeSOUNDBIAS8_1(data | 0); + break; + //400008Ah through 400008Fh - NOT USED - ZERO/GLITCHED + //4000090h - WAVE_RAM0_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000090: + //4000091h - WAVE_RAM0_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000091: + //4000092h - WAVE_RAM0_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000092: + //4000093h - WAVE_RAM0_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000093: + //4000094h - WAVE_RAM1_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000094: + //4000095h - WAVE_RAM1_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000095: + //4000096h - WAVE_RAM1_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000096: + //4000097h - WAVE_RAM1_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000097: + //4000098h - WAVE_RAM2_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000098: + //4000099h - WAVE_RAM2_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000099: + //400009Ah - WAVE_RAM2_H - Channel 3 Wave Pattern RAM (W/R) + case 0x400009A: + //400009Bh - WAVE_RAM2_H - Channel 3 Wave Pattern RAM (W/R) + case 0x400009B: + //400009Ch - WAVE_RAM3_L - Channel 3 Wave Pattern RAM (W/R) + case 0x400009C: + //400009Dh - WAVE_RAM3_L - Channel 3 Wave Pattern RAM (W/R) + case 0x400009D: + //400009Eh - WAVE_RAM3_H - Channel 3 Wave Pattern RAM (W/R) + case 0x400009E: + //400009Fh - WAVE_RAM3_H - Channel 3 Wave Pattern RAM (W/R) + case 0x400009F: + this.sound.writeWAVE8(address & 0xF, data | 0); + break; + //40000A0h - FIFO_A_L - FIFO Channel A First Word (W) + case 0x40000A0: + //40000A1h - FIFO_A_L - FIFO Channel A First Word (W) + case 0x40000A1: + //40000A2h - FIFO_A_H - FIFO Channel A Second Word (W) + case 0x40000A2: + //40000A3h - FIFO_A_H - FIFO Channel A Second Word (W) + case 0x40000A3: + this.sound.writeFIFOA8(data | 0); + break; + //40000A4h - FIFO_B_L - FIFO Channel B First Word (W) + case 0x40000A4: + //40000A5h - FIFO_B_L - FIFO Channel B First Word (W) + case 0x40000A5: + //40000A6h - FIFO_B_H - FIFO Channel B Second Word (W) + case 0x40000A6: + //40000A7h - FIFO_B_H - FIFO Channel B Second Word (W) + case 0x40000A7: + this.sound.writeFIFOB8(data | 0); + break; + //40000A8h through 40000AFh - NOT USED - GLITCHED + //40000B0h - DMA0SAD - DMA 0 Source Address (W) (internal memory) + case 0x40000B0: + this.dmaChannel0.writeDMASource8_0(data | 0); + break; + //40000B1h - DMA0SAD - DMA 0 Source Address (W) (internal memory) + case 0x40000B1: + this.dmaChannel0.writeDMASource8_1(data | 0); + break; + //40000B2h - DMA0SAH - DMA 0 Source Address (W) (internal memory) + case 0x40000B2: + this.dmaChannel0.writeDMASource8_2(data | 0); + break; + //40000B3h - DMA0SAH - DMA 0 Source Address (W) (internal memory) + case 0x40000B3: + this.dmaChannel0.writeDMASource8_3(data | 0); + break; + //40000B4h - DMA0DAD - DMA 0 Destination Address (W) (internal memory) + case 0x40000B4: + this.dmaChannel0.writeDMADestination8_0(data | 0); + break; + //40000B5h - DMA0DAD - DMA 0 Destination Address (W) (internal memory) + case 0x40000B5: + this.dmaChannel0.writeDMADestination8_1(data | 0); + break; + //40000B6h - DMA0DAH - DMA 0 Destination Address (W) (internal memory) + case 0x40000B6: + this.dmaChannel0.writeDMADestination8_2(data | 0); + break; + //40000B7h - DMA0DAH - DMA 0 Destination Address (W) (internal memory) + case 0x40000B7: + this.dmaChannel0.writeDMADestination8_3(data | 0); + break; + //40000B8h - DMA0CNT_L - DMA 0 Word Count (W) (14 bit, 1..4000h) + case 0x40000B8: + this.dmaChannel0.writeDMAWordCount8_0(data | 0); + break; + //40000B9h - DMA0CNT_L - DMA 0 Word Count (W) (14 bit, 1..4000h) + case 0x40000B9: + this.dmaChannel0.writeDMAWordCount8_1(data | 0); + break; + //40000BAh - DMA0CNT_H - DMA 0 Control (R/W) + case 0x40000BA: + this.dmaChannel0.writeDMAControl8_0(data | 0); + break; + //40000BBh - DMA0CNT_H - DMA 0 Control (R/W) + case 0x40000BB: + this.dmaChannel0.writeDMAControl8_1(data | 0); + break; + //40000BCh - DMA1SAD - DMA 1 Source Address (W) (internal memory) + case 0x40000BC: + this.dmaChannel1.writeDMASource8_0(data | 0); + break; + //40000BDh - DMA1SAD - DMA 1 Source Address (W) (internal memory) + case 0x40000BD: + this.dmaChannel1.writeDMASource8_1(data | 0); + break; + //40000BEh - DMA1SAH - DMA 1 Source Address (W) (internal memory) + case 0x40000BE: + this.dmaChannel1.writeDMASource8_2(data | 0); + break; + //40000BFh - DMA1SAH - DMA 1 Source Address (W) (internal memory) + case 0x40000BF: + this.dmaChannel1.writeDMASource8_3(data | 0); + break; + //40000C0h - DMA1DAD - DMA 1 Destination Address (W) (internal memory) + case 0x40000C0: + this.dmaChannel1.writeDMADestination8_0(data | 0); + break; + //40000C1h - DMA1DAD - DMA 1 Destination Address (W) (internal memory) + case 0x40000C1: + this.dmaChannel1.writeDMADestination8_1(data | 0); + break; + //40000C2h - DMA1DAH - DMA 1 Destination Address (W) (internal memory) + case 0x40000C2: + this.dmaChannel1.writeDMADestination8_2(data | 0); + break; + //40000C3h - DMA1DAH - DMA 1 Destination Address (W) (internal memory) + case 0x40000C3: + this.dmaChannel1.writeDMADestination8_3(data | 0); + break; + //40000C4h - DMA1CNT_L - DMA 1 Word Count (W) (14 bit, 1..4000h) + case 0x40000C4: + this.dmaChannel1.writeDMAWordCount8_0(data | 0); + break; + //40000C5h - DMA1CNT_L - DMA 1 Word Count (W) (14 bit, 1..4000h) + case 0x40000C5: + this.dmaChannel1.writeDMAWordCount8_1(data | 0); + break; + //40000C6h - DMA1CNT_H - DMA 1 Control (R/W) + case 0x40000C6: + this.dmaChannel1.writeDMAControl8_0(data | 0); + break; + //40000C7h - DMA1CNT_H - DMA 1 Control (R/W) + case 0x40000C7: + this.dmaChannel1.writeDMAControl8_1(data | 0); + break; + //40000C8h - DMA2SAD - DMA 2 Source Address (W) (internal memory) + case 0x40000C8: + this.dmaChannel2.writeDMASource8_0(data | 0); + break; + //40000C9h - DMA2SAD - DMA 2 Source Address (W) (internal memory) + case 0x40000C9: + this.dmaChannel2.writeDMASource8_1(data | 0); + break; + //40000CAh - DMA2SAH - DMA 2 Source Address (W) (internal memory) + case 0x40000CA: + this.dmaChannel2.writeDMASource8_2(data | 0); + break; + //40000CBh - DMA2SAH - DMA 2 Source Address (W) (internal memory) + case 0x40000CB: + this.dmaChannel2.writeDMASource8_3(data | 0); + break; + //40000CCh - DMA2DAD - DMA 2 Destination Address (W) (internal memory) + case 0x40000CC: + this.dmaChannel2.writeDMADestination8_0(data | 0); + break; + //40000CDh - DMA2DAD - DMA 2 Destination Address (W) (internal memory) + case 0x40000CD: + this.dmaChannel2.writeDMADestination8_1(data | 0); + break; + //40000CEh - DMA2DAH - DMA 2 Destination Address (W) (internal memory) + case 0x40000CE: + this.dmaChannel2.writeDMADestination8_2(data | 0); + break; + //40000CFh - DMA2DAH - DMA 2 Destination Address (W) (internal memory) + case 0x40000CF: + this.dmaChannel2.writeDMADestination8_3(data | 0); + break; + //40000D0h - DMA2CNT_L - DMA 2 Word Count (W) (14 bit, 1..4000h) + case 0x40000D0: + this.dmaChannel2.writeDMAWordCount8_0(data | 0); + break; + //40000D1h - DMA2CNT_L - DMA 2 Word Count (W) (14 bit, 1..4000h) + case 0x40000D1: + this.dmaChannel2.writeDMAWordCount8_1(data | 0); + break; + //40000D2h - DMA2CNT_H - DMA 2 Control (R/W) + case 0x40000D2: + this.dmaChannel2.writeDMAControl8_0(data | 0); + break; + //40000D3h - DMA2CNT_H - DMA 2 Control (R/W) + case 0x40000D3: + this.dmaChannel2.writeDMAControl8_1(data | 0); + break; + //40000D4h - DMA3SAD - DMA 3 Source Address (W) (internal memory) + case 0x40000D4: + this.dmaChannel3.writeDMASource8_0(data | 0); + break; + //40000D5h - DMA3SAD - DMA 3 Source Address (W) (internal memory) + case 0x40000D5: + this.dmaChannel3.writeDMASource8_1(data | 0); + break; + //40000D6h - DMA3SAH - DMA 3 Source Address (W) (internal memory) + case 0x40000D6: + this.dmaChannel3.writeDMASource8_2(data | 0); + break; + //40000D7h - DMA3SAH - DMA 3 Source Address (W) (internal memory) + case 0x40000D7: + this.dmaChannel3.writeDMASource8_3(data | 0); + break; + //40000D8h - DMA3DAD - DMA 3 Destination Address (W) (internal memory) + case 0x40000D8: + this.dmaChannel3.writeDMADestination8_0(data | 0); + break; + //40000D9h - DMA3DAD - DMA 3 Destination Address (W) (internal memory) + case 0x40000D9: + this.dmaChannel3.writeDMADestination8_1(data | 0); + break; + //40000DAh - DMA3DAH - DMA 3 Destination Address (W) (internal memory) + case 0x40000DA: + this.dmaChannel3.writeDMADestination8_2(data | 0); + break; + //40000DBh - DMA3DAH - DMA 3 Destination Address (W) (internal memory) + case 0x40000DB: + this.dmaChannel3.writeDMADestination8_3(data | 0); + break; + //40000DCh - DMA3CNT_L - DMA 3 Word Count (W) (16 bit, 1..10000h) + case 0x40000DC: + this.dmaChannel3.writeDMAWordCount8_0(data | 0); + break; + //40000DDh - DMA3CNT_L - DMA 3 Word Count (W) (16 bit, 1..10000h) + case 0x40000DD: + this.dmaChannel3.writeDMAWordCount8_1(data | 0); + break; + //40000DEh - DMA3CNT_H - DMA 3 Control (R/W) + case 0x40000DE: + this.dmaChannel3.writeDMAControl8_0(data | 0); + break; + //40000DFh - DMA3CNT_H - DMA 3 Control (R/W) + case 0x40000DF: + this.dmaChannel3.writeDMAControl8_1(data | 0); + break; + //40000E0h through 40000FFh - NOT USED - GLITCHED + //4000100h - TM0CNT_L - Timer 0 Counter/Reload (R/W) + case 0x4000100: + this.timer.writeTM0CNT8_0(data | 0); + break; + //4000101h - TM0CNT_L - Timer 0 Counter/Reload (R/W) + case 0x4000101: + this.timer.writeTM0CNT8_1(data | 0); + break; + //4000102h - TM0CNT_H - Timer 0 Control (R/W) + case 0x4000102: + this.timer.writeTM0CNT8_2(data | 0); + break; + //4000103h - TM0CNT_H - Timer 0 Control (R/W) + //4000104h - TM1CNT_L - Timer 1 Counter/Reload (R/W) + case 0x4000104: + this.timer.writeTM1CNT8_0(data | 0); + break; + //4000105h - TM1CNT_L - Timer 1 Counter/Reload (R/W) + case 0x4000105: + this.timer.writeTM1CNT8_1(data | 0); + break; + //4000106h - TM1CNT_H - Timer 1 Control (R/W) + case 0x4000106: + this.timer.writeTM1CNT8_2(data | 0); + break; + //4000107h - TM1CNT_H - Timer 1 Control (R/W) + //4000108h - TM2CNT_L - Timer 2 Counter/Reload (R/W) + case 0x4000108: + this.timer.writeTM2CNT8_0(data | 0); + break; + //4000109h - TM2CNT_L - Timer 2 Counter/Reload (R/W) + case 0x4000109: + this.timer.writeTM2CNT8_1(data | 0); + break; + //400010Ah - TM2CNT_H - Timer 2 Control (R/W) + case 0x400010A: + this.timer.writeTM2CNT8_2(data | 0); + break; + //400010Bh - TM2CNT_H - Timer 2 Control (R/W) + //400010Ch - TM3CNT_L - Timer 3 Counter/Reload (R/W) + case 0x400010C: + this.timer.writeTM3CNT8_0(data | 0); + break; + //400010Dh - TM3CNT_L - Timer 3 Counter/Reload (R/W) + case 0x400010D: + this.timer.writeTM3CNT8_1(data | 0); + break; + //400010Eh - TM3CNT_H - Timer 3 Control (R/W) + case 0x400010E: + this.timer.writeTM3CNT8_2(data | 0); + break; + //400010Fh - TM3CNT_H - Timer 3 Control (R/W) + //4000110h through 400011Fh - NOT USED - GLITCHED + //4000120h - Serial Data A (R/W) + case 0x4000120: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA_A0(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000121h - Serial Data A (R/W) + case 0x4000121: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA_A1(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000122h - Serial Data B (R/W) + case 0x4000122: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA_B0(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000123h - Serial Data B (R/W) + case 0x4000123: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA_B1(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000124h - Serial Data C (R/W) + case 0x4000124: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA_C0(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000125h - Serial Data C (R/W) + case 0x4000125: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA_C1(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000126h - Serial Data D (R/W) + case 0x4000126: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA_D0(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000127h - Serial Data D (R/W) + case 0x4000127: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA_D1(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000128h - SIOCNT - SIO Sub Mode Control (R/W) + case 0x4000128: + this.IOCore.updateSerialClocking(); + this.serial.writeSIOCNT0(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000129h - SIOCNT - SIO Sub Mode Control (R/W) + case 0x4000129: + this.IOCore.updateSerialClocking(); + this.serial.writeSIOCNT1(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //400012Ah - SIOMLT_SEND - Data Send Register (R/W) + case 0x400012A: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA8_0(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //400012Bh - SIOMLT_SEND - Data Send Register (R/W) + case 0x400012B: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA8_1(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //400012Ch through 400012Fh - NOT USED - GLITCHED + //4000130h - KEYINPUT - Key Status (R) + //4000131h - KEYINPUT - Key Status (R) + //4000132h - KEYCNT - Key Interrupt Control (R/W) + case 0x4000132: + this.joypad.writeKeyControl8_0(data | 0); + break; + //4000133h - KEYCNT - Key Interrupt Control (R/W) + case 0x4000133: + this.joypad.writeKeyControl8_1(data | 0); + break; + //4000134h - RCNT (R/W) - Mode Selection + case 0x4000134: + this.IOCore.updateSerialClocking(); + this.serial.writeRCNT0(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000135h - RCNT (R/W) - Mode Selection + case 0x4000135: + this.IOCore.updateSerialClocking(); + this.serial.writeRCNT1(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000136h through 400013Fh - NOT USED - GLITCHED + //4000140h - JOYCNT - JOY BUS Control Register (R/W) + case 0x4000140: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYCNT(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000141h - JOYCNT - JOY BUS Control Register (R/W) + //4000142h through 400014Fh - NOT USED - GLITCHED + //4000150h - JoyBus Receive (R/W) + case 0x4000150: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_RECV0(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000151h - JoyBus Receive (R/W) + case 0x4000151: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_RECV1(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000152h - JoyBus Receive (R/W) + case 0x4000152: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_RECV2(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000153h - JoyBus Receive (R/W) + case 0x4000153: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_RECV3(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000154h - JoyBus Send (R/W) + case 0x4000154: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_SEND0(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000155h - JoyBus Send (R/W) + case 0x4000155: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_SEND1(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000156h - JoyBus Send (R/W) + case 0x4000156: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_SEND2(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000157h - JoyBus Send (R/W) + case 0x4000157: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_SEND3(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000158h - JoyBus Stat (R/W) + case 0x4000158: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_STAT(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000159h through 40001FFh - NOT USED - GLITCHED + //4000200h - IE - Interrupt Enable Register (R/W) + case 0x4000200: + this.irq.writeIE8_0(data | 0); + break; + //4000201h - IE - Interrupt Enable Register (R/W) + case 0x4000201: + this.irq.writeIE8_1(data | 0); + break; + //4000202h - IF - Interrupt Request Flags / IRQ Acknowledge + case 0x4000202: + this.irq.writeIF8_0(data | 0); + break; + //4000203h - IF - Interrupt Request Flags / IRQ Acknowledge + case 0x4000203: + this.irq.writeIF8_1(data | 0); + break; + //4000204h - WAITCNT - Waitstate Control (R/W) + case 0x4000204: + this.wait.writeWAITCNT8_0(data | 0); + break; + //4000205h - WAITCNT - Waitstate Control (R/W) + case 0x4000205: + this.wait.writeWAITCNT8_1(data | 0); + break; + //4000206h - WAITCNT - Waitstate Control (R/W) + //4000207h - WAITCNT - Waitstate Control (R/W) + //4000208h - IME - Interrupt Master Enable Register (R/W) + case 0x4000208: + this.irq.writeIME(data | 0); + break; + //4000209h through 40002FFh - NOT USED - GLITCHED + //4000300h - POSTFLG - BYTE - Undocumented - Post Boot / Debug Control (R/W) + case 0x4000300: + this.wait.writePOSTBOOT(data | 0); + break; + //4000301h - HALTCNT - BYTE - Undocumented - Low Power Mode Control (W) + case 0x4000301: + this.wait.writeHALTCNT(data | 0); + break; + default: + if ((address & 0xFFFC) == 0x800) { + //WRAM wait state control: + this.wait.writeConfigureWRAM8(address | 0, data | 0); + } + } +} +GameBoyAdvanceMemory.prototype.writeIODispatch16 = function (address, data) { + address = address | 0; + data = data | 0; + this.wait.singleClock(); + switch (address & -2) { + //4000000h - DISPCNT - LCD Control (Read/Write) + case 0x4000000: + this.gfxRenderer.writeDISPCNT16(data | 0); + break; + //4000002h - Undocumented - Green Swap (R/W) + case 0x4000002: + this.gfxRenderer.writeDISPCNT8_2(data | 0); + break; + //4000004h - DISPSTAT - General LCD Status (Read/Write) + case 0x4000004: + this.gfxState.writeDISPSTAT16(data | 0); + break; + //4000006h - VCOUNT - Vertical Counter (Read only) + //4000008h - BG0CNT - BG0 Control (R/W) (BG Modes 0,1 only) + case 0x4000008: + this.gfxRenderer.writeBG0CNT16(data | 0); + break; + //400000Ah - BG1CNT - BG1 Control (R/W) (BG Modes 0,1 only) + case 0x400000A: + this.gfxRenderer.writeBG1CNT16(data | 0); + break; + //400000Ch - BG2CNT - BG2 Control (R/W) (BG Modes 0,1,2 only) + case 0x400000C: + this.gfxRenderer.writeBG2CNT16(data | 0); + break; + //400000Eh - BG3CNT - BG3 Control (R/W) (BG Modes 0,2 only) + case 0x400000E: + this.gfxRenderer.writeBG3CNT16(data | 0); + break; + //4000010h - BG0HOFS - BG0 X-Offset (W) + case 0x4000010: + this.gfxRenderer.writeBG0HOFS16(data | 0); + break; + //4000012h - BG0VOFS - BG0 Y-Offset (W) + case 0x4000012: + this.gfxRenderer.writeBG0VOFS16(data | 0); + break; + //4000014h - BG1HOFS - BG1 X-Offset (W) + case 0x4000014: + this.gfxRenderer.writeBG1HOFS16(data | 0); + break; + //4000016h - BG1VOFS - BG1 Y-Offset (W) + case 0x4000016: + this.gfxRenderer.writeBG1VOFS16(data | 0); + break; + //4000018h - BG2HOFS - BG2 X-Offset (W) + case 0x4000018: + this.gfxRenderer.writeBG2HOFS16(data | 0); + break; + //400001Ah - BG2VOFS - BG2 Y-Offset (W) + case 0x400001A: + this.gfxRenderer.writeBG2VOFS16(data | 0); + break; + //400001Ch - BG3HOFS - BG3 X-Offset (W) + case 0x400001C: + this.gfxRenderer.writeBG3HOFS16(data | 0); + break; + //400001Eh - BG3VOFS - BG3 Y-Offset (W) + case 0x400001E: + this.gfxRenderer.writeBG3VOFS16(data | 0); + break; + //4000020h - BG2PA - BG2 Rotation/Scaling Parameter A (alias dx) (W) + case 0x4000020: + this.gfxRenderer.writeBG2PA16(data | 0); + break; + //4000022h - BG2PB - BG2 Rotation/Scaling Parameter B (alias dmx) (W) + case 0x4000022: + this.gfxRenderer.writeBG2PB16(data | 0); + break; + //4000024h - BG2PC - BG2 Rotation/Scaling Parameter C (alias dy) (W) + case 0x4000024: + this.gfxRenderer.writeBG2PC16(data | 0); + break; + //4000026h - BG2PD - BG2 Rotation/Scaling Parameter D (alias dmy) (W) + case 0x4000026: + this.gfxRenderer.writeBG2PD16(data | 0); + break; + //4000028h - BG2X_L - BG2 Reference Point X-Coordinate, lower 16 bit (W) + case 0x4000028: + this.gfxRenderer.writeBG2X16_0(data | 0); + break; + //400002Ah - BG2X_H - BG2 Reference Point X-Coordinate, upper 12 bit (W) + case 0x400002A: + this.gfxRenderer.writeBG2X16_1(data | 0); + break; + //400002Ch - BG2Y_L - BG2 Reference Point Y-Coordinate, lower 16 bit (W) + case 0x400002C: + this.gfxRenderer.writeBG2Y16_0(data | 0); + break; + //400002Eh - BG2Y_H - BG2 Reference Point Y-Coordinate, upper 12 bit (W) + case 0x400002E: + this.gfxRenderer.writeBG2Y16_1(data | 0); + break; + //4000030h - BG3PA - BG3 Rotation/Scaling Parameter A (alias dx) (W) + case 0x4000030: + this.gfxRenderer.writeBG3PA16(data | 0); + break; + //4000032h - BG3PB - BG3 Rotation/Scaling Parameter B (alias dmx) (W) + case 0x4000032: + this.gfxRenderer.writeBG3PB16(data | 0); + break; + //4000034h - BG3PC - BG3 Rotation/Scaling Parameter C (alias dy) (W) + case 0x4000034: + this.gfxRenderer.writeBG3PC16(data | 0); + break; + //4000036h - BG3PD - BG3 Rotation/Scaling Parameter D (alias dmy) (W) + case 0x4000036: + this.gfxRenderer.writeBG3PD16(data | 0); + break; + //4000038h - BG3X_L - BG3 Reference Point X-Coordinate, lower 16 bit (W) + case 0x4000038: + this.gfxRenderer.writeBG3X16_0(data | 0); + break; + //400003Ah - BG3X_H - BG3 Reference Point X-Coordinate, upper 12 bit (W) + case 0x400003A: + this.gfxRenderer.writeBG3X16_1(data | 0); + break; + //400003Ch - BG3Y_L - BG3 Reference Point Y-Coordinate, lower 16 bit (W) + case 0x400003C: + this.gfxRenderer.writeBG3Y16_0(data | 0); + break; + //400003Eh - BG3Y_H - BG3 Reference Point Y-Coordinate, upper 12 bit (W) + case 0x400003E: + this.gfxRenderer.writeBG3Y16_1(data | 0); + break; + //4000040h - WIN0H - Window 0 Horizontal Dimensions (W) + case 0x4000040: + this.gfxRenderer.writeWIN0XCOORD16(data | 0); + break; + //4000042h - WIN1H - Window 1 Horizontal Dimensions (W) + case 0x4000042: + this.gfxRenderer.writeWIN1XCOORD16(data | 0); + break; + //4000044h - WIN0V - Window 0 Vertical Dimensions (W) + case 0x4000044: + this.gfxRenderer.writeWIN0YCOORD16(data | 0); + break; + //4000046h - WIN1V - Window 1 Vertical Dimensions (W) + case 0x4000046: + this.gfxRenderer.writeWIN1YCOORD16(data | 0); + break; + //4000048h - WININ - Control of Inside of Window(s) (R/W) + case 0x4000048: + this.gfxRenderer.writeWININ16(data | 0); + break; + //400004Ah- WINOUT - Control of Outside of Windows & Inside of OBJ Window (R/W) + case 0x400004A: + this.gfxRenderer.writeWINOUT16(data | 0); + break; + //400004Ch - MOSAIC - Mosaic Size (W) + case 0x400004C: + this.gfxRenderer.writeMOSAIC16(data | 0); + break; + //400004Eh - NOT USED - ZERO + //4000050h - BLDCNT - Color Special Effects Selection (R/W) + case 0x4000050: + this.gfxRenderer.writeBLDCNT16(data | 0); + break; + //4000052h - BLDALPHA - Alpha Blending Coefficients (R/W) + case 0x4000052: + this.gfxRenderer.writeBLDALPHA16(data | 0); + break; + //4000054h - BLDY - Brightness (Fade-In/Out) Coefficient (W) + case 0x4000054: + this.gfxRenderer.writeBLDY8(data | 0); + break; + //4000055h through 400005Fh - NOT USED - ZERO/GLITCHED + //4000060h - SOUND1CNT_L (NR10) - Channel 1 Sweep register (R/W) + case 0x4000060: + //NR10: + this.sound.writeSOUND1CNT8_0(data | 0); + break; + //4000062h - SOUND1CNT_H (NR11, NR12) - Channel 1 Duty/Len/Envelope (R/W) + case 0x4000062: + this.sound.writeSOUND1CNT16(data | 0); + break; + //4000064h - SOUND1CNT_X (NR13, NR14) - Channel 1 Frequency/Control (R/W) + case 0x4000064: + //NR13: + //NR14: + this.sound.writeSOUND1CNTX16(data | 0); + break; + //4000066h - NOT USED - ZERO + //4000068h - SOUND2CNT_L (NR21, NR22) - Channel 2 Duty/Length/Envelope (R/W) + case 0x4000068: + //NR21: + //NR22: + this.sound.writeSOUND2CNTL16(data | 0); + break; + //400006Ah - NOT USED - ZERO + //400006Ch - SOUND2CNT_H (NR23, NR24) - Channel 2 Frequency/Control (R/W) + case 0x400006C: + //NR23: + //NR24: + this.sound.writeSOUND2CNTH16(data | 0); + break; + //400006Eh - NOT USED - ZERO + //4000070h - SOUND3CNT_L (NR30) - Channel 3 Stop/Wave RAM select (R/W) + case 0x4000070: + //NR30: + this.sound.writeSOUND3CNT8_0(data | 0); + break; + //4000072h - SOUND3CNT_H (NR31, NR32) - Channel 3 Length/Volume (R/W) + case 0x4000072: + //NR31: + //NR32: + this.sound.writeSOUND3CNT16(data | 0); + break; + //4000074h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W) + case 0x4000074: + //NR33: + //NR34: + this.sound.writeSOUND3CNTX16(data | 0); + break; + //4000076h - NOT USED - ZERO + //4000078h - SOUND4CNT_L (NR41, NR42) - Channel 4 Length/Envelope (R/W) + case 0x4000078: + //NR41: + //NR42: + this.sound.writeSOUND4CNTL16(data | 0); + break; + //400007Ah - NOT USED - ZERO + //400007Ch - SOUND4CNT_H (NR43, NR44) - Channel 4 Frequency/Control (R/W) + case 0x400007C: + //NR43: + //NR44: + this.sound.writeSOUND4CNTH16(data | 0); + break; + //400007Eh - NOT USED - ZERO + //4000080h - SOUNDCNT_L (NR50, NR51) - Channel L/R Volume/Enable (R/W) + case 0x4000080: + //NR50: + //NR51: + this.sound.writeSOUNDCNTL16(data | 0); + break; + //4000082h - SOUNDCNT_H (GBA only) - DMA Sound Control/Mixing (R/W) + case 0x4000082: + this.sound.writeSOUNDCNTH16(data | 0); + break; + //4000084h - SOUNDCNT_X (NR52) - Sound on/off (R/W) + case 0x4000084: + this.sound.writeSOUNDCNTX8(data | 0); + break; + //4000086h - NOT USED - ZERO + //4000088h - SOUNDBIAS - Sound PWM Control (R/W) + case 0x4000088: + this.sound.writeSOUNDBIAS16(data | 0); + break; + //400008Ah through 400008Fh - NOT USED - ZERO/GLITCHED + //4000090h - WAVE_RAM0_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000090: + //4000092h - WAVE_RAM0_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000092: + //4000094h - WAVE_RAM1_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000094: + //4000096h - WAVE_RAM1_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000096: + //4000098h - WAVE_RAM2_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000098: + //400009Ah - WAVE_RAM2_H - Channel 3 Wave Pattern RAM (W/R) + case 0x400009A: + //400009Ch - WAVE_RAM3_L - Channel 3 Wave Pattern RAM (W/R) + case 0x400009C: + //400009Eh - WAVE_RAM3_H - Channel 3 Wave Pattern RAM (W/R) + case 0x400009E: + this.sound.writeWAVE16(address & 0xE, data | 0); + break; + //40000A0h - FIFO_A_L - FIFO Channel A First Word (W) + case 0x40000A0: + //40000A2h - FIFO_A_H - FIFO Channel A Second Word (W) + case 0x40000A2: + this.sound.writeFIFOA16(data | 0); + break; + //40000A4h - FIFO_B_L - FIFO Channel B First Word (W) + case 0x40000A4: + //40000A6h - FIFO_B_H - FIFO Channel B Second Word (W) + case 0x40000A6: + this.sound.writeFIFOB16(data | 0); + break; + //40000A8h through 40000AFh - NOT USED - GLITCHED + //40000B0h - DMA0SAD - DMA 0 Source Address (W) (internal memory) + case 0x40000B0: + this.dmaChannel0.writeDMASource16_0(data | 0); + break; + //40000B2h - DMA0SAH - DMA 0 Source Address (W) (internal memory) + case 0x40000B2: + this.dmaChannel0.writeDMASource16_1(data | 0); + break; + //40000B4h - DMA0DAD - DMA 0 Destination Address (W) (internal memory) + case 0x40000B4: + this.dmaChannel0.writeDMADestination16_0(data | 0); + break; + //40000B6h - DMA0DAH - DMA 0 Destination Address (W) (internal memory) + case 0x40000B6: + this.dmaChannel0.writeDMADestination16_1(data | 0); + break; + //40000B8h - DMA0CNT_L - DMA 0 Word Count (W) (14 bit, 1..4000h) + case 0x40000B8: + this.dmaChannel0.writeDMAWordCount16(data | 0); + break; + //40000BAh - DMA0CNT_H - DMA 0 Control (R/W) + case 0x40000BA: + this.dmaChannel0.writeDMAControl16(data | 0); + break; + //40000BCh - DMA1SAD - DMA 1 Source Address (W) (internal memory) + case 0x40000BC: + this.dmaChannel1.writeDMASource16_0(data | 0); + break; + //40000BEh - DMA1SAH - DMA 1 Source Address (W) (internal memory) + case 0x40000BE: + this.dmaChannel1.writeDMASource16_1(data | 0); + break; + //40000C0h - DMA1DAD - DMA 1 Destination Address (W) (internal memory) + case 0x40000C0: + this.dmaChannel1.writeDMADestination16_0(data | 0); + break; + //40000C2h - DMA1DAH - DMA 1 Destination Address (W) (internal memory) + case 0x40000C2: + this.dmaChannel1.writeDMADestination16_1(data | 0); + break; + //40000C4h - DMA1CNT_L - DMA 1 Word Count (W) (14 bit, 1..4000h) + case 0x40000C4: + this.dmaChannel1.writeDMAWordCount16(data | 0); + break; + //40000C6h - DMA1CNT_H - DMA 1 Control (R/W) + case 0x40000C6: + this.dmaChannel1.writeDMAControl16(data | 0); + break; + //40000C8h - DMA2SAD - DMA 2 Source Address (W) (internal memory) + case 0x40000C8: + this.dmaChannel2.writeDMASource16_0(data | 0); + break; + //40000CAh - DMA2SAH - DMA 2 Source Address (W) (internal memory) + case 0x40000CA: + this.dmaChannel2.writeDMASource16_1(data | 0); + break; + //40000CCh - DMA2DAD - DMA 2 Destination Address (W) (internal memory) + case 0x40000CC: + this.dmaChannel2.writeDMADestination16_0(data | 0); + break; + //40000CEh - DMA2DAH - DMA 2 Destination Address (W) (internal memory) + case 0x40000CE: + this.dmaChannel2.writeDMADestination16_1(data | 0); + break; + //40000D0h - DMA2CNT_L - DMA 2 Word Count (W) (14 bit, 1..4000h) + case 0x40000D0: + this.dmaChannel2.writeDMAWordCount16(data | 0); + break; + //40000D2h - DMA2CNT_H - DMA 2 Control (R/W) + case 0x40000D2: + this.dmaChannel2.writeDMAControl16(data | 0); + break; + //40000D4h - DMA3SAD - DMA 3 Source Address (W) (internal memory) + case 0x40000D4: + this.dmaChannel3.writeDMASource16_0(data | 0); + break; + //40000D6h - DMA3SAH - DMA 3 Source Address (W) (internal memory) + case 0x40000D6: + this.dmaChannel3.writeDMASource16_1(data | 0); + break; + //40000D8h - DMA3DAD - DMA 3 Destination Address (W) (internal memory) + case 0x40000D8: + this.dmaChannel3.writeDMADestination16_0(data | 0); + break; + //40000DAh - DMA3DAH - DMA 3 Destination Address (W) (internal memory) + case 0x40000DA: + this.dmaChannel3.writeDMADestination16_1(data | 0); + break; + //40000DCh - DMA3CNT_L - DMA 3 Word Count (W) (16 bit, 1..10000h) + case 0x40000DC: + this.dmaChannel3.writeDMAWordCount16(data | 0); + break; + //40000DEh - DMA3CNT_H - DMA 3 Control (R/W) + case 0x40000DE: + this.dmaChannel3.writeDMAControl16(data | 0); + break; + //40000E0h through 40000FFh - NOT USED - GLITCHED + //4000100h - TM0CNT_L - Timer 0 Counter/Reload (R/W) + case 0x4000100: + this.timer.writeTM0CNT16(data | 0); + break; + //4000102h - TM0CNT_H - Timer 0 Control (R/W) + case 0x4000102: + this.timer.writeTM0CNT8_2(data | 0); + break; + //4000104h - TM1CNT_L - Timer 1 Counter/Reload (R/W) + case 0x4000104: + this.timer.writeTM1CNT16(data | 0); + break; + //4000106h - TM1CNT_H - Timer 1 Control (R/W) + case 0x4000106: + this.timer.writeTM1CNT8_2(data | 0); + break; + //4000108h - TM2CNT_L - Timer 2 Counter/Reload (R/W) + case 0x4000108: + this.timer.writeTM2CNT16(data | 0); + break; + //400010Ah - TM2CNT_H - Timer 2 Control (R/W) + case 0x400010A: + this.timer.writeTM2CNT8_2(data | 0); + break; + //400010Ch - TM3CNT_L - Timer 3 Counter/Reload (R/W) + case 0x400010C: + this.timer.writeTM3CNT16(data | 0); + break; + //400010Eh - TM3CNT_H - Timer 3 Control (R/W) + case 0x400010E: + this.timer.writeTM3CNT8_2(data | 0); + break; + //4000110h through 400011Fh - NOT USED - GLITCHED + //4000120h - Serial Data A (R/W) + case 0x4000120: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA_A0(data & 0xFF); + this.serial.writeSIODATA_A1((data >> 8) & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000122h - Serial Data B (R/W) + case 0x4000122: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA_B0(data & 0xFF); + this.serial.writeSIODATA_B1((data >> 8) & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000124h - Serial Data C (R/W) + case 0x4000124: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA_C0(data & 0xFF); + this.serial.writeSIODATA_C1((data >> 8) & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000126h - Serial Data D (R/W) + case 0x4000126: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA_D0(data & 0xFF); + this.serial.writeSIODATA_D1((data >> 8) & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000128h - SIOCNT - SIO Sub Mode Control (R/W) + case 0x4000128: + this.IOCore.updateSerialClocking(); + this.serial.writeSIOCNT0(data & 0xFF); + this.serial.writeSIOCNT1((data >> 8) & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //400012Ah - SIOMLT_SEND - Data Send Register (R/W) + case 0x400012A: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA8_0(data & 0xFF); + this.serial.writeSIODATA8_1((data >> 8) & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //400012Ch through 400012Fh - NOT USED - GLITCHED + //4000130h - KEYINPUT - Key Status (R) + //4000132h - KEYCNT - Key Interrupt Control (R/W) + case 0x4000132: + this.joypad.writeKeyControl16(data | 0); + break; + //4000134h - RCNT (R/W) - Mode Selection + case 0x4000134: + this.IOCore.updateSerialClocking(); + this.serial.writeRCNT0(data & 0xFF); + this.serial.writeRCNT1((data >> 8) & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000136h through 400013Fh - NOT USED - GLITCHED + //4000140h - JOYCNT - JOY BUS Control Register (R/W) + case 0x4000140: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYCNT(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000142h through 400014Fh - NOT USED - GLITCHED + //4000150h - JoyBus Receive (R/W) + case 0x4000150: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_RECV0(data & 0xFF); + this.serial.writeJOYBUS_RECV1((data >> 8) & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000152h - JoyBus Receive (R/W) + case 0x4000152: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_RECV2(data & 0xFF); + this.serial.writeJOYBUS_RECV3((data >> 8) & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000154h - JoyBus Send (R/W) + case 0x4000154: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_SEND0(data & 0xFF); + this.serial.writeJOYBUS_SEND1((data >> 8) & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000156h - JoyBus Send (R/W) + case 0x4000156: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_SEND2(data & 0xFF); + this.serial.writeJOYBUS_SEND3((data >> 8) & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000158h - JoyBus Stat (R/W) + case 0x4000158: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_STAT(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000159h through 40001FFh - NOT USED - GLITCHED + //4000200h - IE - Interrupt Enable Register (R/W) + case 0x4000200: + this.irq.writeIE16(data | 0); + break; + //4000202h - IF - Interrupt Request Flags / IRQ Acknowledge + case 0x4000202: + this.irq.writeIF16(data | 0); + break; + //4000204h - WAITCNT - Waitstate Control (R/W) + case 0x4000204: + this.wait.writeWAITCNT16(data | 0); + break; + //4000206h - WAITCNT - Waitstate Control (R/W) + //4000208h - IME - Interrupt Master Enable Register (R/W) + case 0x4000208: + this.irq.writeIME(data | 0); + break; + //4000209h through 40002FFh - NOT USED - GLITCHED + //4000300h - POSTFLG - BYTE - Undocumented - Post Boot / Debug Control (R/W) + case 0x4000300: + this.wait.writeHALT16(data | 0); + break; + default: + if ((address & 0xFFFC) == 0x800) { + //WRAM wait state control: + this.wait.writeConfigureWRAM16(address | 0, data | 0); + } + } +} +GameBoyAdvanceMemory.prototype.writeIODispatch32 = function (address, data) { + address = address | 0; + data = data | 0; + this.wait.singleClock(); + switch (address & -4) { + //4000000h - DISPCNT - LCD Control (Read/Write) + //4000002h - Undocumented - Green Swap (R/W) + case 0x4000000: + this.gfxRenderer.writeDISPCNT32(data | 0); + break; + //4000004h - DISPSTAT - General LCD Status (Read/Write) + //4000006h - VCOUNT - Vertical Counter (Read only) + case 0x4000004: + this.gfxState.writeDISPSTAT16(data | 0); + break; + //4000008h - BG0CNT - BG0 Control (R/W) (BG Modes 0,1 only) + //400000Ah - BG1CNT - BG1 Control (R/W) (BG Modes 0,1 only) + case 0x4000008: + this.gfxRenderer.writeBG0BG1CNT32(data | 0); + break; + //400000Ch - BG2CNT - BG2 Control (R/W) (BG Modes 0,1,2 only) + //400000Eh - BG3CNT - BG3 Control (R/W) (BG Modes 0,2 only) + case 0x400000C: + this.gfxRenderer.writeBG2BG3CNT32(data | 0); + break; + //4000010h - BG0HOFS - BG0 X-Offset (W) + //4000012h - BG0VOFS - BG0 Y-Offset (W) + case 0x4000010: + this.gfxRenderer.writeBG0OFS32(data | 0); + break; + //4000014h - BG1HOFS - BG1 X-Offset (W) + //4000016h - BG1VOFS - BG1 Y-Offset (W) + case 0x4000014: + this.gfxRenderer.writeBG1OFS32(data | 0); + break; + //4000018h - BG2HOFS - BG2 X-Offset (W) + //400001Ah - BG2VOFS - BG2 Y-Offset (W) + case 0x4000018: + this.gfxRenderer.writeBG2OFS32(data | 0); + break; + //400001Ch - BG3HOFS - BG3 X-Offset (W) + //400001Eh - BG3VOFS - BG3 Y-Offset (W) + case 0x400001C: + this.gfxRenderer.writeBG3OFS32(data | 0); + break; + //4000020h - BG2PA - BG2 Rotation/Scaling Parameter A (alias dx) (W) + //4000022h - BG2PB - BG2 Rotation/Scaling Parameter B (alias dmx) (W) + case 0x4000020: + this.gfxRenderer.writeBG2PAB32(data | 0); + break; + //4000024h - BG2PC - BG2 Rotation/Scaling Parameter C (alias dy) (W) + //4000026h - BG2PD - BG2 Rotation/Scaling Parameter D (alias dmy) (W) + case 0x4000024: + this.gfxRenderer.writeBG2PCD32(data | 0); + break; + //4000028h - BG2X_L - BG2 Reference Point X-Coordinate, lower 16 bit (W) + //400002Ah - BG2X_H - BG2 Reference Point X-Coordinate, upper 12 bit (W) + case 0x4000028: + this.gfxRenderer.writeBG2X32(data | 0); + break; + //400002Ch - BG2Y_L - BG2 Reference Point Y-Coordinate, lower 16 bit (W) + //400002Eh - BG2Y_H - BG2 Reference Point Y-Coordinate, upper 12 bit (W) + case 0x400002C: + this.gfxRenderer.writeBG2Y32(data | 0); + break; + //4000030h - BG3PA - BG3 Rotation/Scaling Parameter A (alias dx) (W) + //4000032h - BG3PB - BG3 Rotation/Scaling Parameter B (alias dmx) (W) + case 0x4000030: + this.gfxRenderer.writeBG3PAB32(data | 0); + break; + //4000034h - BG3PC - BG3 Rotation/Scaling Parameter C (alias dy) (W) + //4000036h - BG3PD - BG3 Rotation/Scaling Parameter D (alias dmy) (W) + case 0x4000034: + this.gfxRenderer.writeBG3PCD32(data | 0); + break; + //4000038h - BG3X_L - BG3 Reference Point X-Coordinate, lower 16 bit (W) + //400003Ah - BG3X_H - BG3 Reference Point X-Coordinate, upper 12 bit (W) + case 0x4000038: + this.gfxRenderer.writeBG3X32(data | 0); + break; + //400003Ch - BG3Y_L - BG3 Reference Point Y-Coordinate, lower 16 bit (W) + //400003Eh - BG3Y_H - BG3 Reference Point Y-Coordinate, upper 12 bit (W) + case 0x400003C: + this.gfxRenderer.writeBG3Y32(data | 0); + break; + //4000040h - WIN0H - Window 0 Horizontal Dimensions (W) + //4000042h - WIN1H - Window 1 Horizontal Dimensions (W) + case 0x4000040: + this.gfxRenderer.writeWINXCOORD32(data | 0); + break; + //4000044h - WIN0V - Window 0 Vertical Dimensions (W) + //4000046h - WIN1V - Window 1 Vertical Dimensions (W) + case 0x4000044: + this.gfxRenderer.writeWINYCOORD32(data | 0); + break; + //4000048h - WININ - Control of Inside of Window(s) (R/W) + //400004Ah- WINOUT - Control of Outside of Windows & Inside of OBJ Window (R/W) + case 0x4000048: + this.gfxRenderer.writeWINCONTROL32(data | 0); + break; + //400004Ch - MOSAIC - Mosaic Size (W) + //400004Eh - NOT USED - ZERO + case 0x400004C: + this.gfxRenderer.writeMOSAIC16(data | 0); + break; + //4000050h - BLDCNT - Color Special Effects Selection (R/W) + //4000052h - BLDALPHA - Alpha Blending Coefficients (R/W) + case 0x4000050: + this.gfxRenderer.writeBLDCNT32(data | 0); + break; + //4000054h - BLDY - Brightness (Fade-In/Out) Coefficient (W) + case 0x4000054: + this.gfxRenderer.writeBLDY8(data | 0); + break; + //4000055h through 400005Fh - NOT USED - ZERO/GLITCHED + //4000060h - SOUND1CNT_L (NR10) - Channel 1 Sweep register (R/W) + //4000062h - SOUND1CNT_H (NR11, NR12) - Channel 1 Duty/Len/Envelope (R/W) + case 0x4000060: + //NR10: + this.sound.writeSOUND1CNT32(data | 0); + break; + //4000064h - SOUND1CNT_X (NR13, NR14) - Channel 1 Frequency/Control (R/W) + //4000066h - NOT USED - ZERO + case 0x4000064: + //NR13: + //NR14: + this.sound.writeSOUND1CNTX16(data | 0); + break; + //4000068h - SOUND2CNT_L (NR21, NR22) - Channel 2 Duty/Length/Envelope (R/W) + //400006Ah - NOT USED - ZERO + case 0x4000068: + //NR21: + //NR22: + this.sound.writeSOUND2CNTL16(data | 0); + break; + //400006Ch - SOUND2CNT_H (NR23, NR24) - Channel 2 Frequency/Control (R/W) + //400006Eh - NOT USED - ZERO + case 0x400006C: + //NR23: + //NR24: + this.sound.writeSOUND2CNTH16(data | 0); + break; + //4000070h - SOUND3CNT_L (NR30) - Channel 3 Stop/Wave RAM select (R/W) + //4000072h - SOUND3CNT_H (NR31, NR32) - Channel 3 Length/Volume (R/W) + case 0x4000070: + //NR30: + //NR31: + //NR32: + this.sound.writeSOUND3CNT32(data | 0); + break; + //4000074h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W) + //4000076h - NOT USED - ZERO + case 0x4000074: + //NR33: + //NR34: + this.sound.writeSOUND3CNTX16(data | 0); + break; + //4000078h - SOUND4CNT_L (NR41, NR42) - Channel 4 Length/Envelope (R/W) + //400007Ah - NOT USED - ZERO + case 0x4000078: + //NR41: + //NR42: + this.sound.writeSOUND4CNTL16(data | 0); + break; + //400007Ch - SOUND4CNT_H (NR43, NR44) - Channel 4 Frequency/Control (R/W) + //400007Eh - NOT USED - ZERO + case 0x400007C: + //NR43: + //NR44: + this.sound.writeSOUND4CNTH16(data | 0); + break; + //4000080h - SOUNDCNT_L (NR50, NR51) - Channel L/R Volume/Enable (R/W) + //4000082h - SOUNDCNT_H (GBA only) - DMA Sound Control/Mixing (R/W) + case 0x4000080: + //NR50: + //NR51: + this.sound.writeSOUNDCNT32(data | 0); + break; + //4000084h - SOUNDCNT_X (NR52) - Sound on/off (R/W) + //4000086h - NOT USED - ZERO + case 0x4000084: + this.sound.writeSOUNDCNTX8(data | 0); + break; + //4000088h - SOUNDBIAS - Sound PWM Control (R/W) + case 0x4000088: + this.sound.writeSOUNDBIAS16(data | 0); + break; + //400008Ah through 400008Fh - NOT USED - ZERO/GLITCHED + //4000090h - WAVE_RAM0_L - Channel 3 Wave Pattern RAM (W/R) + //4000092h - WAVE_RAM0_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000090: + //4000094h - WAVE_RAM1_L - Channel 3 Wave Pattern RAM (W/R) + //4000096h - WAVE_RAM1_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000094: + //4000098h - WAVE_RAM2_L - Channel 3 Wave Pattern RAM (W/R) + //400009Ah - WAVE_RAM2_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000098: + //400009Ch - WAVE_RAM3_L - Channel 3 Wave Pattern RAM (W/R) + //400009Eh - WAVE_RAM3_H - Channel 3 Wave Pattern RAM (W/R) + case 0x400009C: + this.sound.writeWAVE32(address & 0xC, data | 0); + break; + //40000A0h - FIFO_A_L - FIFO Channel A First Word (W) + //40000A2h - FIFO_A_H - FIFO Channel A Second Word (W) + case 0x40000A0: + this.sound.writeFIFOA32(data | 0); + break; + //40000A4h - FIFO_B_L - FIFO Channel B First Word (W) + //40000A6h - FIFO_B_H - FIFO Channel B Second Word (W) + case 0x40000A4: + this.sound.writeFIFOB32(data | 0); + break; + //40000A8h through 40000AFh - NOT USED - GLITCHED + //40000B0h - DMA0SAH - DMA 0 Source Address (W) (internal memory) + //40000B2h - DMA0SAD - DMA 0 Source Address (W) (internal memory) + case 0x40000B0: + this.dmaChannel0.writeDMASource32(data | 0); + break; + //40000B4h - DMA0DAD - DMA 0 Destination Address (W) (internal memory) + //40000B6h - DMA0DAH - DMA 0 Destination Address (W) (internal memory) + case 0x40000B4: + this.dmaChannel0.writeDMADestination32(data | 0); + break; + //40000B8h - DMA0CNT_L - DMA 0 Word Count (W) (14 bit, 1..4000h) + //40000BAh - DMA0CNT_H - DMA 0 Control (R/W) + case 0x40000B8: + this.dmaChannel0.writeDMAControl32(data | 0); + break; + //40000BCh - DMA1SAD - DMA 1 Source Address (W) (internal memory) + //40000BEh - DMA1SAH - DMA 1 Source Address (W) (internal memory) + case 0x40000BC: + this.dmaChannel1.writeDMASource32(data | 0); + break; + //40000C0h - DMA1DAD - DMA 1 Destination Address (W) (internal memory) + //40000C2h - DMA1DAH - DMA 1 Destination Address (W) (internal memory) + case 0x40000C0: + this.dmaChannel1.writeDMADestination32(data | 0); + break; + //40000C4h - DMA1CNT_L - DMA 1 Word Count (W) (14 bit, 1..4000h) + //40000C6h - DMA1CNT_H - DMA 1 Control (R/W) + case 0x40000C4: + this.dmaChannel1.writeDMAControl32(data | 0); + break; + //40000C8h - DMA2SAD - DMA 2 Source Address (W) (internal memory) + //40000CAh - DMA2SAH - DMA 2 Source Address (W) (internal memory) + case 0x40000C8: + this.dmaChannel2.writeDMASource32(data | 0); + break; + //40000CCh - DMA2DAD - DMA 2 Destination Address (W) (internal memory) + //40000CEh - DMA2DAH - DMA 2 Destination Address (W) (internal memory) + case 0x40000CC: + this.dmaChannel2.writeDMADestination32(data | 0); + break; + //40000D0h - DMA2CNT_L - DMA 2 Word Count (W) (14 bit, 1..4000h) + //40000D2h - DMA2CNT_H - DMA 2 Control (R/W) + case 0x40000D0: + this.dmaChannel2.writeDMAControl32(data | 0); + break; + //40000D4h - DMA3SAD - DMA 3 Source Address (W) (internal memory) + //40000D6h - DMA3SAH - DMA 3 Source Address (W) (internal memory) + case 0x40000D4: + this.dmaChannel3.writeDMASource32(data | 0); + break; + //40000D8h - DMA3DAD - DMA 3 Destination Address (W) (internal memory) + //40000DAh - DMA3DAH - DMA 3 Destination Address (W) (internal memory) + case 0x40000D8: + this.dmaChannel3.writeDMADestination32(data | 0); + break; + //40000DCh - DMA3CNT_L - DMA 3 Word Count (W) (16 bit, 1..10000h) + //40000DEh - DMA3CNT_H - DMA 3 Control (R/W) + case 0x40000DC: + this.dmaChannel3.writeDMAControl32(data | 0); + break; + //40000E0h through 40000FFh - NOT USED - GLITCHED + //4000100h - TM0CNT_L - Timer 0 Counter/Reload (R/W) + //4000102h - TM0CNT_H - Timer 0 Control (R/W) + case 0x4000100: + this.timer.writeTM0CNT32(data | 0); + break; + //4000104h - TM1CNT_L - Timer 1 Counter/Reload (R/W) + //4000106h - TM1CNT_H - Timer 1 Control (R/W) + case 0x4000104: + this.timer.writeTM1CNT32(data | 0); + break; + //4000108h - TM2CNT_L - Timer 2 Counter/Reload (R/W) + //400010Ah - TM2CNT_H - Timer 2 Control (R/W) + case 0x4000108: + this.timer.writeTM2CNT32(data | 0); + break; + //400010Ch - TM3CNT_L - Timer 3 Counter/Reload (R/W) + //400010Eh - TM3CNT_H - Timer 3 Control (R/W) + case 0x400010C: + this.timer.writeTM3CNT32(data | 0); + break; + //4000110h through 400011Fh - NOT USED - GLITCHED + //4000120h - Serial Data A (R/W) + //4000122h - Serial Data B (R/W) + case 0x4000120: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA_A0(data & 0xFF); + this.serial.writeSIODATA_A1((data >> 8) & 0xFF); + this.serial.writeSIODATA_B0((data >> 16) & 0xFF); + this.serial.writeSIODATA_B1(data >>> 24); + this.IOCore.updateCoreEventTime(); + break; + //4000124h - Serial Data C (R/W) + //4000126h - Serial Data D (R/W) + case 0x4000124: + this.IOCore.updateSerialClocking(); + this.serial.writeSIODATA_C0(data & 0xFF); + this.serial.writeSIODATA_C1((data >> 8) & 0xFF); + this.serial.writeSIODATA_D0((data >> 16) & 0xFF); + this.serial.writeSIODATA_D1(data >>> 24); + this.IOCore.updateCoreEventTime(); + break; + //4000128h - SIOCNT - SIO Sub Mode Control (R/W) + //400012Ah - SIOMLT_SEND - Data Send Register (R/W) + case 0x4000128: + this.IOCore.updateSerialClocking(); + this.serial.writeSIOCNT0(data & 0xFF); + this.serial.writeSIOCNT1((data >> 8) & 0xFF); + this.serial.writeSIODATA8_0((data >> 16) & 0xFF); + this.serial.writeSIODATA8_1(data >>> 24); + this.IOCore.updateCoreEventTime(); + break; + //400012Ch through 400012Fh - NOT USED - GLITCHED + //4000130h - KEYINPUT - Key Status (R) + //4000132h - KEYCNT - Key Interrupt Control (R/W) + case 0x4000130: + this.joypad.writeKeyControl16(data >> 16); + break; + //4000134h - RCNT (R/W) - Mode Selection + case 0x4000134: + this.IOCore.updateSerialClocking(); + this.serial.writeRCNT0(data & 0xFF); + this.serial.writeRCNT1((data >> 8) & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000136h through 400013Fh - NOT USED - GLITCHED + //4000140h - JOYCNT - JOY BUS Control Register (R/W) + case 0x4000140: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYCNT(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000142h through 400014Fh - NOT USED - GLITCHED + //4000150h - JoyBus Receive (R/W) + //4000152h - JoyBus Receive (R/W) + case 0x4000150: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_RECV0(data & 0xFF); + this.serial.writeJOYBUS_RECV1((data >> 8) & 0xFF); + this.serial.writeJOYBUS_RECV2((data >> 16) & 0xFF); + this.serial.writeJOYBUS_RECV3(data >>> 24); + this.IOCore.updateCoreEventTime(); + break; + //4000154h - JoyBus Send (R/W) + //4000156h - JoyBus Send (R/W) + case 0x4000154: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_SEND0(data & 0xFF); + this.serial.writeJOYBUS_SEND1((data >> 8) & 0xFF); + this.serial.writeJOYBUS_SEND2((data >> 16) & 0xFF); + this.serial.writeJOYBUS_SEND3(data >>> 24); + this.IOCore.updateCoreEventTime(); + break; + //4000158h - JoyBus Stat (R/W) + case 0x4000158: + this.IOCore.updateSerialClocking(); + this.serial.writeJOYBUS_STAT(data & 0xFF); + this.IOCore.updateCoreEventTime(); + break; + //4000159h through 40001FFh - NOT USED - GLITCHED + //4000200h - IE - Interrupt Enable Register (R/W) + //4000202h - IF - Interrupt Request Flags / IRQ Acknowledge + case 0x4000200: + this.irq.writeIRQ32(data | 0); + break; + //4000204h - WAITCNT - Waitstate Control (R/W) + //4000206h - WAITCNT - Waitstate Control (R/W) + case 0x4000204: + this.wait.writeWAITCNT16(data | 0); + break; + //4000208h - IME - Interrupt Master Enable Register (R/W) + case 0x4000208: + this.irq.writeIME(data | 0); + break; + //4000209h through 40002FFh - NOT USED - GLITCHED + //4000300h - POSTFLG - BYTE - Undocumented - Post Boot / Debug Control (R/W) + //4000302h - NOT USED - ZERO + case 0x4000300: + this.wait.writeHALT16(data | 0); + break; + default: + if ((address & 0xFFFC) == 0x800) { + //WRAM wait state control: + this.wait.writeConfigureWRAM32(data | 0); + } + } +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceMemory.prototype.writeVRAM8Preliminary = function (address, data) { + address = address | 0; + data = data | 0; + this.IOCore.updateGraphicsClocking(); + switch (address >> 24) { + case 0x5: + this.wait.VRAMAccess(); + this.gfxRenderer.writePalette16(address & 0x3FE, Math.imul(data & 0xFF, 0x101) | 0); + break; + case 0x6: + this.wait.VRAMAccess(); + this.gfxRenderer.writeVRAM8(address | 0, data | 0); + break; + default: + this.wait.OAMAccess(); + } + } +} +else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceMemory.prototype.writeVRAM8Preliminary = function (address, data) { + this.IOCore.updateGraphicsClocking(); + switch (address >> 24) { + case 0x5: + this.wait.VRAMAccess(); + this.gfxRenderer.writePalette16(address & 0x3FE, (data & 0xFF) * 0x101); + break; + case 0x6: + this.wait.VRAMAccess(); + this.gfxRenderer.writeVRAM8(address, data); + break; + default: + this.wait.OAMAccess(); + } + } +} +GameBoyAdvanceMemory.prototype.writePalette16 = function (address, data) { + address = address | 0; + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.wait.VRAMAccess(); + this.gfxRenderer.writePalette16(address & 0x3FE, data & 0xFFFF); +} +GameBoyAdvanceMemory.prototype.writeVRAM16 = function (address, data) { + address = address | 0; + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.wait.VRAMAccess(); + this.gfxRenderer.writeVRAM16(address | 0, data | 0); +} +GameBoyAdvanceMemory.prototype.writeOBJ16 = function (address, data) { + address = address | 0; + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.wait.OAMAccess(); + this.gfxRenderer.writeOAM16(address | 0, data | 0); +} +GameBoyAdvanceMemory.prototype.writePalette32 = function (address, data) { + address = address | 0; + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.wait.VRAMAccess32(); + this.gfxRenderer.writePalette32(address & 0x3FC, data | 0); +} +GameBoyAdvanceMemory.prototype.writeVRAM32 = function (address, data) { + address = address | 0; + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.wait.VRAMAccess32(); + this.gfxRenderer.writeVRAM32(address | 0, data | 0); +} +GameBoyAdvanceMemory.prototype.writeOBJ32 = function (address, data) { + address = address | 0; + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.wait.OAMAccess(); + this.gfxRenderer.writeOAM32(address | 0, data | 0); +} +GameBoyAdvanceMemory.prototype.writeROM8 = function (address, data) { + address = address | 0; + data = data | 0; + this.wait.ROMAccess(address | 0); + this.cartridge.writeROM8(address & 0x1FFFFFF, data & 0xFF); +} +GameBoyAdvanceMemory.prototype.writeROM16 = function (address, data) { + address = address | 0; + data = data | 0; + this.wait.ROMAccess(address | 0); + this.cartridge.writeROM16(address & 0x1FFFFFE, data & 0xFFFF); +} +GameBoyAdvanceMemory.prototype.writeROM16DMA = function (address, data) { + address = address | 0; + data = data | 0; + this.wait.ROMAccess(address | 0); + this.cartridge.writeROM16DMA(address & 0x1FFFFFE, data & 0xFFFF); +} +GameBoyAdvanceMemory.prototype.writeROM32 = function (address, data) { + address = address | 0; + data = data | 0; + this.wait.ROMAccess32(address | 0); + this.cartridge.writeROM32(address & 0x1FFFFFC, data | 0); +} +GameBoyAdvanceMemory.prototype.writeSRAM8 = function (address, data) { + address = address | 0; + data = data | 0; + this.wait.SRAMAccess(); + this.saves.writeSRAM(address & 0xFFFF, data & 0xFF); +} +GameBoyAdvanceMemory.prototype.writeSRAM16 = function (address, data) { + address = address | 0; + data = data | 0; + this.wait.SRAMAccess(); + this.saves.writeSRAM(address & 0xFFFE, (data >> ((address & 0x2) << 3)) & 0xFF); +} +GameBoyAdvanceMemory.prototype.writeSRAM32 = function (address, data) { + address = address | 0; + data = data | 0; + this.wait.SRAMAccess(); + this.saves.writeSRAM(address & 0xFFFC, data & 0xFF); +} +GameBoyAdvanceMemory.prototype.writeUnused = function () { + //Ignore the data write... + this.wait.singleClock(); +} +GameBoyAdvanceMemory.prototype.remapWRAM = function (data) { + data = data & 0x21; + if ((data | 0) != (this.WRAMControlFlags | 0)) { + switch (data | 0) { + case 0: + //Mirror Internal RAM to External: + this.memoryRead8 = this.memoryRead8Generated[0]; + this.memoryWrite8 = this.memoryWrite8Generated[0]; + this.memoryRead16 = this.memoryRead16Generated[0]; + this.memoryReadDMA16 = this.memoryReadDMA16Generated[0]; + this.memoryReadDMAFull16 = this.memoryReadDMA16FullGenerated[0]; + this.memoryReadCPU16 = this.memoryReadCPU16Generated[0]; + this.memoryWrite16 = this.memoryWrite16Generated[0]; + this.memoryWriteDMA16 = this.memoryWriteDMA16Generated[0]; + this.memoryWriteDMAFull16 = this.memoryWriteDMA16FullGenerated[0]; + this.memoryRead32 = this.memoryRead32Generated[0]; + this.memoryReadDMA32 = this.memoryReadDMA32Generated[0]; + this.memoryReadDMAFull32 = this.memoryReadDMA32FullGenerated[0]; + this.memoryReadCPU32 = this.memoryReadCPU32Generated[0]; + this.memoryWrite32 = this.memoryWrite32Generated[0]; + this.memoryWriteDMA32 = this.memoryWriteDMA32Generated[0]; + this.memoryWriteDMAFull32 = this.memoryWriteDMA32FullGenerated[0]; + break; + case 0x20: + //Use External RAM: + this.memoryRead8 = this.memoryRead8Generated[1]; + this.memoryWrite8 = this.memoryWrite8Generated[1]; + this.memoryRead16 = this.memoryRead16Generated[1]; + this.memoryReadDMA16 = this.memoryReadDMA16Generated[1]; + this.memoryReadDMAFull16 = this.memoryReadDMA16FullGenerated[1]; + this.memoryReadCPU16 = this.memoryReadCPU16Generated[1]; + this.memoryWrite16 = this.memoryWrite16Generated[1]; + this.memoryWriteDMA16 = this.memoryWriteDMA16Generated[1]; + this.memoryWriteDMAFull16 = this.memoryWriteDMA16FullGenerated[1]; + this.memoryRead32 = this.memoryRead32Generated[1]; + this.memoryReadDMA32 = this.memoryReadDMA32Generated[1]; + this.memoryReadDMAFull32 = this.memoryReadDMA32FullGenerated[1]; + this.memoryReadCPU32 = this.memoryReadCPU32Generated[1]; + this.memoryWrite32 = this.memoryWrite32Generated[1]; + this.memoryWriteDMA32 = this.memoryWriteDMA32Generated[1]; + this.memoryWriteDMAFull32 = this.memoryWriteDMA32FullGenerated[1]; + break; + default: + //WRAM Disabled: + this.memoryRead8 = this.memoryRead8Generated[2]; + this.memoryWrite8 = this.memoryWrite8Generated[2]; + this.memoryRead16 = this.memoryRead16Generated[2]; + this.memoryReadDMA16 = this.memoryReadDMA16Generated[2]; + this.memoryReadDMAFull16 = this.memoryReadDMA16FullGenerated[2]; + this.memoryReadCPU16 = this.memoryReadCPU16Generated[2]; + this.memoryWrite16 = this.memoryWrite16Generated[2]; + this.memoryWriteDMA16 = this.memoryWriteDMA16Generated[2]; + this.memoryWriteDMAFull16 = this.memoryWriteDMA16FullGenerated[2]; + this.memoryRead32 = this.memoryRead32Generated[2]; + this.memoryReadDMA32 = this.memoryReadDMA32Generated[2]; + this.memoryReadDMAFull32 = this.memoryReadDMA32FullGenerated[2]; + this.memoryReadCPU32 = this.memoryReadCPU32Generated[2]; + this.memoryWrite32 = this.memoryWrite32Generated[2]; + this.memoryWriteDMA32 = this.memoryWriteDMA32Generated[2]; + this.memoryWriteDMAFull32 = this.memoryWriteDMA32FullGenerated[2]; + } + this.WRAMControlFlags = data | 0; + } +} +GameBoyAdvanceMemory.prototype.readBIOS8 = function (address) { + address = address | 0; + var data = 0; + this.wait.singleClock(); + if ((address | 0) < 0x4000) { + if ((this.cpu.registers[15] | 0) < 0x4000) { + //If reading from BIOS while executing it: + data = this.BIOS[address & 0x3FFF] | 0; + } + else { + //Not allowed to read from BIOS while executing outside of it: + data = (this.lastBIOSREAD >> ((address & 0x3) << 3)) & 0xFF; + } + } + else { + data = this.readUnused8CPUBase(address | 0) | 0; + } + return data | 0; +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceMemory.prototype.readBIOS16 = function (address) { + address = address | 0; + var data = 0; + this.wait.singleClock(); + if ((address | 0) < 0x4000) { + address = address >> 1; + if ((this.cpu.registers[15] | 0) < 0x4000) { + //If reading from BIOS while executing it: + data = this.BIOS16[address & 0x1FFF] | 0; + } + else { + //Not allowed to read from BIOS while executing outside of it: + data = (this.lastBIOSREAD >> ((address & 0x1) << 4)) & 0xFFFF; + } + } + else { + data = this.readUnused16CPUBase(address | 0) | 0; + } + return data | 0; + } + GameBoyAdvanceMemory.prototype.readBIOS16DMA = function (address) { + address = address | 0; + var data = 0; + this.wait.singleClock(); + if ((address | 0) < 0x4000) { + address = address >> 1; + if ((this.cpu.registers[15] | 0) < 0x4000) { + //If reading from BIOS while executing it: + data = this.BIOS16[address & 0x1FFF] | 0; + } + } + else { + data = this.readUnused16DMABase(address | 0) | 0; + } + return data | 0; + } + GameBoyAdvanceMemory.prototype.readBIOS16CPU = function (address) { + address = address | 0; + var data = 0; + this.IOCore.updateCoreSingle(); + if ((address | 0) < 0x4000) { + address = address >> 1; + //If reading from BIOS while executing it: + data = this.BIOS16[address & 0x1FFF] | 0; + this.lastBIOSREAD = data | 0; + } + else { + data = this.readUnused16CPUBase(address | 0) | 0; + } + return data | 0; + } + GameBoyAdvanceMemory.prototype.readBIOS32 = function (address) { + address = address | 0; + var data = 0; + this.wait.singleClock(); + if ((address | 0) < 0x4000) { + address = address >> 2; + if ((this.cpu.registers[15] | 0) < 0x4000) { + //If reading from BIOS while executing it: + data = this.BIOS32[address & 0xFFF] | 0; + } + else { + //Not allowed to read from BIOS while executing outside of it: + data = this.lastBIOSREAD | 0; + } + } + else { + data = this.cpu.getCurrentFetchValue() | 0; + } + return data | 0; + } + GameBoyAdvanceMemory.prototype.readBIOS32DMA = function (address) { + address = address | 0; + var data = 0; + this.wait.singleClock(); + if ((address | 0) < 0x4000) { + address = address >> 2; + if ((this.cpu.registers[15] | 0) < 0x4000) { + //If reading from BIOS while executing it: + data = this.BIOS32[address & 0xFFF] | 0; + } + } + else { + data = this.dma.getCurrentFetchValue() | 0; + } + return data | 0; + } + GameBoyAdvanceMemory.prototype.readBIOS32CPU = function (address) { + address = address | 0; + var data = 0; + this.IOCore.updateCoreSingle(); + if ((address | 0) < 0x4000) { + address = address >> 2; + //If reading from BIOS while executing it: + data = this.BIOS32[address & 0xFFF] | 0; + this.lastBIOSREAD = data | 0; + } + else { + data = this.cpu.getCurrentFetchValue() | 0; + } + return data | 0; + } +} +else { + GameBoyAdvanceMemory.prototype.readBIOS16 = function (address) { + this.wait.singleClock(); + if (address < 0x4000) { + if (this.cpu.registers[15] < 0x4000) { + //If reading from BIOS while executing it: + return this.BIOS[address & -2] | (this.BIOS[address | 1] << 8); + } + else { + //Not allowed to read from BIOS while executing outside of it: + return (this.lastBIOSREAD >> ((address & 0x2) << 3)) & 0xFFFF; + } + } + else { + return this.readUnused16CPUBase(address); + } + } + GameBoyAdvanceMemory.prototype.readBIOS16DMA = function (address) { + this.wait.singleClock(); + if (address < 0x4000) { + if (this.cpu.registers[15] < 0x4000) { + //If reading from BIOS while executing it: + return this.BIOS[address & -2] | (this.BIOS[address | 1] << 8); + } + else { + //Not allowed to read from BIOS while executing outside of it: + return 0; + } + } + else { + return this.readUnused16DMABase(address); + } + } + GameBoyAdvanceMemory.prototype.readBIOS16CPU = function (address) { + this.IOCore.updateCoreSingle(); + if (address < 0x4000) { + //If reading from BIOS while executing it: + var data = this.BIOS[address & -2] | (this.BIOS[address | 1] << 8); + this.lastBIOSREAD = data; + return data; + } + else { + return this.readUnused16CPUBase(address); + } + } + GameBoyAdvanceMemory.prototype.readBIOS32 = function (address) { + this.wait.singleClock(); + if (address < 0x4000) { + if (this.cpu.registers[15] < 0x4000) { + //If reading from BIOS while executing it: + address &= -4; + return this.BIOS[address] | (this.BIOS[address + 1] << 8) | (this.BIOS[address + 2] << 16) | (this.BIOS[address + 3] << 24); + } + else { + //Not allowed to read from BIOS while executing outside of it: + return this.lastBIOSREAD; + } + } + else { + return this.cpu.getCurrentFetchValue(); + } + } + GameBoyAdvanceMemory.prototype.readBIOS32DMA = function (address) { + this.wait.singleClock(); + if (address < 0x4000) { + if (this.cpu.registers[15] < 0x4000) { + //If reading from BIOS while executing it: + address &= -4; + return this.BIOS[address] | (this.BIOS[address + 1] << 8) | (this.BIOS[address + 2] << 16) | (this.BIOS[address + 3] << 24); + } + else { + //Not allowed to read from BIOS while executing outside of it: + return 0; + } + } + else { + return this.dma.getCurrentFetchValue(); + } + } + GameBoyAdvanceMemory.prototype.readBIOS32CPU = function (address) { + this.IOCore.updateCoreSingle(); + if (address < 0x4000) { + //If reading from BIOS while executing it: + address &= -4; + var data = this.BIOS[address] | (this.BIOS[address + 1] << 8) | (this.BIOS[address + 2] << 16) | (this.BIOS[address + 3] << 24); + this.lastBIOSREAD = data; + return data; + } + else { + return this.cpu.getCurrentFetchValue(); + } + } +} +GameBoyAdvanceMemory.prototype.readExternalWRAM8 = function (address) { + address = address | 0; + //External WRAM: + this.wait.WRAMAccess(); + return this.externalRAM[address & 0x3FFFF] | 0; +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceMemory.prototype.readExternalWRAM16 = function (address) { + address = address | 0; + //External WRAM: + this.wait.WRAMAccess(); + return this.externalRAM16[(address >> 1) & 0x1FFFF] | 0; + } + GameBoyAdvanceMemory.prototype.readExternalWRAM16CPU = function (address) { + address = address | 0; + //External WRAM: + this.wait.WRAMAccess16CPU(); + return this.externalRAM16[(address >> 1) & 0x1FFFF] | 0; + } + GameBoyAdvanceMemory.prototype.readExternalWRAM32 = function (address) { + address = address | 0; + //External WRAM: + this.wait.WRAMAccess32(); + return this.externalRAM32[(address >> 2) & 0xFFFF] | 0; + } + GameBoyAdvanceMemory.prototype.readExternalWRAM32CPU = function (address) { + address = address | 0; + //External WRAM: + this.wait.WRAMAccess32CPU(); + return this.externalRAM32[(address >> 2) & 0xFFFF] | 0; + } +} +else { + GameBoyAdvanceMemory.prototype.readExternalWRAM16 = function (address) { + //External WRAM: + this.wait.WRAMAccess(); + address &= 0x3FFFE; + return this.externalRAM[address] | (this.externalRAM[address + 1] << 8); + } + GameBoyAdvanceMemory.prototype.readExternalWRAM16CPU = function (address) { + //External WRAM: + this.wait.WRAMAccess16CPU(); + address &= 0x3FFFE; + return this.externalRAM[address] | (this.externalRAM[address + 1] << 8); + } + GameBoyAdvanceMemory.prototype.readExternalWRAM32 = function (address) { + //External WRAM: + this.wait.WRAMAccess32(); + address &= 0x3FFFC; + return this.externalRAM[address] | (this.externalRAM[address + 1] << 8) | (this.externalRAM[address + 2] << 16) | (this.externalRAM[address + 3] << 24); + } + GameBoyAdvanceMemory.prototype.readExternalWRAM32CPU = function (address) { + //External WRAM: + this.wait.WRAMAccess32CPU(); + address &= 0x3FFFC; + return this.externalRAM[address] | (this.externalRAM[address + 1] << 8) | (this.externalRAM[address + 2] << 16) | (this.externalRAM[address + 3] << 24); + } +} +GameBoyAdvanceMemory.prototype.readInternalWRAM8 = function (address) { + address = address | 0; + //Internal WRAM: + this.wait.singleClock(); + return this.internalRAM[address & 0x7FFF] | 0; +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceMemory.prototype.readInternalWRAM16 = function (address) { + address = address | 0; + //Internal WRAM: + this.wait.singleClock(); + return this.internalRAM16[(address >> 1) & 0x3FFF] | 0; + } + GameBoyAdvanceMemory.prototype.readInternalWRAM16CPU = function (address) { + address = address | 0; + //Internal WRAM: + this.IOCore.updateCoreSingle(); + return this.internalRAM16[(address >> 1) & 0x3FFF] | 0; + } + GameBoyAdvanceMemory.prototype.readInternalWRAM32 = function (address) { + address = address | 0; + //Internal WRAM: + this.wait.singleClock(); + return this.internalRAM32[(address >> 2) & 0x1FFF] | 0; + } + GameBoyAdvanceMemory.prototype.readInternalWRAM32CPU = function (address) { + address = address | 0; + //Internal WRAM: + this.IOCore.updateCoreSingle(); + return this.internalRAM32[(address >> 2) & 0x1FFF] | 0; + } +} +else { + GameBoyAdvanceMemory.prototype.readInternalWRAM16 = function (address) { + //Internal WRAM: + this.wait.singleClock(); + address &= 0x7FFE; + return this.internalRAM[address] | (this.internalRAM[address + 1] << 8); + } + GameBoyAdvanceMemory.prototype.readInternalWRAM16CPU = function (address) { + //Internal WRAM: + this.IOCore.updateCoreSingle(); + address &= 0x7FFE; + return this.internalRAM[address] | (this.internalRAM[address + 1] << 8); + } + GameBoyAdvanceMemory.prototype.readInternalWRAM32 = function (address) { + //Internal WRAM: + this.wait.singleClock(); + address &= 0x7FFC; + return this.internalRAM[address] | (this.internalRAM[address + 1] << 8) | (this.internalRAM[address + 2] << 16) | (this.internalRAM[address + 3] << 24); + } + GameBoyAdvanceMemory.prototype.readInternalWRAM32CPU = function (address) { + //Internal WRAM: + this.IOCore.updateCoreSingle(); + address &= 0x7FFC; + return this.internalRAM[address] | (this.internalRAM[address + 1] << 8) | (this.internalRAM[address + 2] << 16) | (this.internalRAM[address + 3] << 24); + } +} +GameBoyAdvanceMemory.prototype.readIODispatch8 = function (address) { + address = address | 0; + this.wait.singleClock(); + var data = 0; + switch (address | 0) { + //4000000h - DISPCNT - LCD Control (Read/Write) + case 0x4000000: + data = this.gfxRenderer.readDISPCNT8_0() | 0; + break; + //4000001h - DISPCNT - LCD Control (Read/Write) + case 0x4000001: + data = this.gfxRenderer.readDISPCNT8_1() | 0; + break; + //4000002h - Undocumented - Green Swap (R/W) + case 0x4000002: + data = this.gfxRenderer.readDISPCNT8_2() | 0; + break; + //4000004h - DISPSTAT - General LCD Status (Read/Write) + case 0x4000004: + data = this.gfxState.readDISPSTAT8_0() | 0; + break; + //4000005h - DISPSTAT - General LCD Status (Read/Write) + case 0x4000005: + data = this.gfxState.readDISPSTAT8_1() | 0; + break; + //4000006h - VCOUNT - Vertical Counter (Read only) + case 0x4000006: + data = this.gfxState.readDISPSTAT8_2() | 0; + break; + //4000008h - BG0CNT - BG0 Control (R/W) (BG Modes 0,1 only) + case 0x4000008: + data = this.gfxRenderer.readBG0CNT8_0() | 0; + break; + //4000009h - BG0CNT - BG0 Control (R/W) (BG Modes 0,1 only) + case 0x4000009: + data = this.gfxRenderer.readBG0CNT8_1() | 0; + break; + //400000Ah - BG1CNT - BG1 Control (R/W) (BG Modes 0,1 only) + case 0x400000A: + data = this.gfxRenderer.readBG1CNT8_0() | 0; + break; + //400000Bh - BG1CNT - BG1 Control (R/W) (BG Modes 0,1 only) + case 0x400000B: + data = this.gfxRenderer.readBG1CNT8_1() | 0; + break; + //400000Ch - BG2CNT - BG2 Control (R/W) (BG Modes 0,1,2 only) + case 0x400000C: + data = this.gfxRenderer.readBG2CNT8_0() | 0; + break; + //400000Dh - BG2CNT - BG2 Control (R/W) (BG Modes 0,1,2 only) + case 0x400000D: + data = this.gfxRenderer.readBG2CNT8_1() | 0; + break; + //400000Eh - BG3CNT - BG3 Control (R/W) (BG Modes 0,2 only) + case 0x400000E: + data = this.gfxRenderer.readBG3CNT8_0() | 0; + break; + //400000Fh - BG3CNT - BG3 Control (R/W) (BG Modes 0,2 only) + case 0x400000F: + data = this.gfxRenderer.readBG3CNT8_1() | 0; + break; + //4000010h through 4000047h - WRITE ONLY + //4000048h - WININ - Control of Inside of Window(s) (R/W) + case 0x4000048: + data = this.gfxRenderer.readWIN0IN8() | 0; + break; + //4000049h - WININ - Control of Inside of Window(s) (R/W) + case 0x4000049: + data = this.gfxRenderer.readWIN1IN8() | 0; + break; + //400004Ah- WINOUT - Control of Outside of Windows & Inside of OBJ Window (R/W) + case 0x400004A: + data = this.gfxRenderer.readWINOUT8() | 0; + break; + //400004AB- WINOUT - Control of Outside of Windows & Inside of OBJ Window (R/W) + case 0x400004B: + data = this.gfxRenderer.readWINOBJIN8() | 0; + break; + //4000050h - BLDCNT - Color Special Effects Selection (R/W) + case 0x4000050: + data = this.gfxRenderer.readBLDCNT8_0() | 0; + break; + //4000051h - BLDCNT - Color Special Effects Selection (R/W) + case 0x4000051: + data = this.gfxRenderer.readBLDCNT8_1() | 0; + break; + //4000052h - BLDALPHA - Alpha Blending Coefficients (R/W) + case 0x4000052: + data = this.gfxRenderer.readBLDALPHA8_0() | 0; + break; + //4000053h - BLDALPHA - Alpha Blending Coefficients (R/W) + case 0x4000053: + data = this.gfxRenderer.readBLDALPHA8_1() | 0; + break; + //4000054h through 400005Fh - NOT USED - GLITCHED + //4000060h - SOUND1CNT_L (NR10) - Channel 1 Sweep register (R/W) + case 0x4000060: + //NR10: + data = this.sound.readSOUND1CNT8_0() | 0; + break; + //4000062h - SOUND1CNT_H (NR11, NR12) - Channel 1 Duty/Len/Envelope (R/W) + case 0x4000062: + //NR11: + data = this.sound.readSOUND1CNT8_2() | 0; + break; + //4000063h - SOUND1CNT_H (NR11, NR12) - Channel 1 Duty/Len/Envelope (R/W) + case 0x4000063: + //NR12: + data = this.sound.readSOUND1CNT8_3() | 0; + break; + //4000065h - SOUND1CNT_X (NR13, NR14) - Channel 1 Frequency/Control (R/W) + case 0x4000065: + //NR14: + data = this.sound.readSOUND1CNTX8() | 0; + break; + //4000068h - SOUND2CNT_L (NR21, NR22) - Channel 2 Duty/Length/Envelope (R/W) + case 0x4000068: + //NR21: + data = this.sound.readSOUND2CNTL8_0() | 0; + break; + //4000069h - SOUND2CNT_L (NR21, NR22) - Channel 2 Duty/Length/Envelope (R/W) + case 0x4000069: + //NR22: + data = this.sound.readSOUND2CNTL8_1() | 0; + break; + //400006Dh - SOUND2CNT_H (NR23, NR24) - Channel 2 Frequency/Control (R/W) + case 0x400006D: + //NR24: + data = this.sound.readSOUND2CNTH8() | 0; + break; + //4000070h - SOUND3CNT_L (NR30) - Channel 3 Stop/Wave RAM select (R/W) + case 0x4000070: + //NR30: + data = this.sound.readSOUND3CNT8_0() | 0; + break; + //4000073h - SOUND3CNT_H (NR31, NR32) - Channel 3 Length/Volume (R/W) + case 0x4000073: + //NR32: + data = this.sound.readSOUND3CNT8_3() | 0; + break; + //4000075h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W) + case 0x4000075: + //NR34: + data = this.sound.readSOUND3CNTX8() | 0; + break; + //4000079h - SOUND4CNT_L (NR41, NR42) - Channel 4 Length/Envelope (R/W) + case 0x4000079: + //NR42: + data = this.sound.readSOUND4CNTL8() | 0; + break; + //400007Ch - SOUND4CNT_H (NR43, NR44) - Channel 4 Frequency/Control (R/W) + case 0x400007C: + //NR43: + data = this.sound.readSOUND4CNTH8_0() | 0; + break; + //400007Dh - SOUND4CNT_H (NR43, NR44) - Channel 4 Frequency/Control (R/W) + case 0x400007D: + //NR44: + data = this.sound.readSOUND4CNTH8_1() | 0; + break; + //4000080h - SOUNDCNT_L (NR50, NR51) - Channel L/R Volume/Enable (R/W) + case 0x4000080: + //NR50: + data = this.sound.readSOUNDCNTL8_0() | 0; + break; + //4000081h - SOUNDCNT_L (NR50, NR51) - Channel L/R Volume/Enable (R/W) + case 0x4000081: + //NR51: + data = this.sound.readSOUNDCNTL8_1() | 0; + break; + //4000082h - SOUNDCNT_H (GBA only) - DMA Sound Control/Mixing (R/W) + case 0x4000082: + data = this.sound.readSOUNDCNTH8_0() | 0; + break; + //4000083h - SOUNDCNT_H (GBA only) - DMA Sound Control/Mixing (R/W) + case 0x4000083: + data = this.sound.readSOUNDCNTH8_1() | 0; + break; + //4000084h - SOUNDCNT_X (NR52) - Sound on/off (R/W) + case 0x4000084: + data = this.sound.readSOUNDCNTX8() | 0; + break; + //4000088h - SOUNDBIAS - Sound PWM Control (R/W, see below) + case 0x4000088: + data = this.sound.readSOUNDBIAS8_0() | 0; + break; + //4000089h - SOUNDBIAS - Sound PWM Control (R/W, see below) + case 0x4000089: + data = this.sound.readSOUNDBIAS8_1() | 0; + break; + //400008Ch - NOT USED - GLITCHED + //400008Dh - NOT USED - GLITCHED + //400008Eh - NOT USED - GLITCHED + //400008Fh - NOT USED - GLITCHED + //4000090h - WAVE_RAM0_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000090: + //4000091h - WAVE_RAM0_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000091: + //4000092h - WAVE_RAM0_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000092: + //4000093h - WAVE_RAM0_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000093: + //4000094h - WAVE_RAM1_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000094: + //4000095h - WAVE_RAM1_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000095: + //4000096h - WAVE_RAM1_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000096: + //4000097h - WAVE_RAM1_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000097: + //4000098h - WAVE_RAM2_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000098: + //4000099h - WAVE_RAM2_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000099: + //400009Ah - WAVE_RAM2_H - Channel 3 Wave Pattern RAM (W/R) + case 0x400009A: + //400009Bh - WAVE_RAM2_H - Channel 3 Wave Pattern RAM (W/R) + case 0x400009B: + //400009Ch - WAVE_RAM3_L - Channel 3 Wave Pattern RAM (W/R) + case 0x400009C: + //400009Dh - WAVE_RAM3_L - Channel 3 Wave Pattern RAM (W/R) + case 0x400009D: + //400009Eh - WAVE_RAM3_H - Channel 3 Wave Pattern RAM (W/R) + case 0x400009E: + //400009Fh - WAVE_RAM3_H - Channel 3 Wave Pattern RAM (W/R) + case 0x400009F: + data = this.sound.readWAVE8(address & 0xF) | 0; + break; + //40000A0h through 40000B9h - WRITE ONLY + //40000BAh - DMA0CNT_H - DMA 0 Control (R/W) + case 0x40000BA: + data = this.dmaChannel0.readDMAControl8_0() | 0; + break; + //40000BBh - DMA0CNT_H - DMA 0 Control (R/W) + case 0x40000BB: + data = this.dmaChannel0.readDMAControl8_1() | 0; + break; + //40000BCh through 40000C5h - WRITE ONLY + //40000C6h - DMA1CNT_H - DMA 1 Control (R/W) + case 0x40000C6: + data = this.dmaChannel1.readDMAControl8_0() | 0; + break; + //40000C7h - DMA1CNT_H - DMA 1 Control (R/W) + case 0x40000C7: + data = this.dmaChannel1.readDMAControl8_1() | 0; + break; + //40000C8h through 40000D1h - WRITE ONLY + //40000D2h - DMA2CNT_H - DMA 2 Control (R/W) + case 0x40000D2: + data = this.dmaChannel2.readDMAControl8_0() | 0; + break; + //40000D3h - DMA2CNT_H - DMA 2 Control (R/W) + case 0x40000D3: + data = this.dmaChannel2.readDMAControl8_1() | 0; + break; + //40000D4h through 40000DDh - WRITE ONLY + //40000DEh - DMA3CNT_H - DMA 3 Control (R/W) + case 0x40000DE: + data = this.dmaChannel3.readDMAControl8_0() | 0; + break; + //40000DFh - DMA3CNT_H - DMA 3 Control (R/W) + case 0x40000DF: + data = this.dmaChannel3.readDMAControl8_1() | 0; + break; + //40000E0h through 40000FFh - NOT USED - GLITCHED + //4000100h - TM0CNT_L - Timer 0 Counter/Reload (R/W) + case 0x4000100: + data = this.timer.readTM0CNT8_0() | 0; + break; + //4000101h - TM0CNT_L - Timer 0 Counter/Reload (R/W) + case 0x4000101: + data = this.timer.readTM0CNT8_1() | 0; + break; + //4000102h - TM0CNT_H - Timer 0 Control (R/W) + case 0x4000102: + data = this.timer.readTM0CNT8_2() | 0; + break; + //4000104h - TM1CNT_L - Timer 1 Counter/Reload (R/W) + case 0x4000104: + data = this.timer.readTM1CNT8_0() | 0; + break; + //4000105h - TM1CNT_L - Timer 1 Counter/Reload (R/W) + case 0x4000105: + data = this.timer.readTM1CNT8_1() | 0; + break; + //4000106h - TM1CNT_H - Timer 1 Control (R/W) + case 0x4000106: + data = this.timer.readTM1CNT8_2() | 0; + break; + //4000108h - TM2CNT_L - Timer 2 Counter/Reload (R/W) + case 0x4000108: + data = this.timer.readTM2CNT8_0() | 0; + break; + //4000109h - TM2CNT_L - Timer 2 Counter/Reload (R/W) + case 0x4000109: + data = this.timer.readTM2CNT8_1() | 0; + break; + //400010Ah - TM2CNT_H - Timer 2 Control (R/W) + case 0x400010A: + data = this.timer.readTM2CNT8_2() | 0; + break; + //400010Ch - TM3CNT_L - Timer 3 Counter/Reload (R/W) + case 0x400010C: + data = this.timer.readTM3CNT8_0() | 0; + break; + //400010Dh - TM3CNT_L - Timer 3 Counter/Reload (R/W) + case 0x400010D: + data = this.timer.readTM3CNT8_1() | 0; + break; + //400010Eh - TM3CNT_H - Timer 3 Control (R/W) + case 0x400010E: + data = this.timer.readTM3CNT8_2() | 0; + break; + //4000110h through 400011Fh - NOT USED - GLITCHED + //4000120h - Serial Data A (R/W) + case 0x4000120: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA_A0() | 0; + break; + //4000121h - Serial Data A (R/W) + case 0x4000121: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA_A1() | 0; + break; + //4000122h - Serial Data B (R/W) + case 0x4000122: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA_B0() | 0; + break; + //4000123h - Serial Data B (R/W) + case 0x4000123: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA_B1() | 0; + break; + //4000124h - Serial Data C (R/W) + case 0x4000124: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA_C0() | 0; + break; + //4000125h - Serial Data C (R/W) + case 0x4000125: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA_C1() | 0; + break; + //4000126h - Serial Data D (R/W) + case 0x4000126: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA_D0() | 0; + break; + //4000127h - Serial Data D (R/W) + case 0x4000127: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA_D1() | 0; + break; + //4000128h - SIOCNT - SIO Sub Mode Control (R/W) + case 0x4000128: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIOCNT0() | 0; + break; + //4000129h - SIOCNT - SIO Sub Mode Control (R/W) + case 0x4000129: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIOCNT1() | 0; + break; + //400012Ah - SIOMLT_SEND - Data Send Register (R/W) + case 0x400012A: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA8_0() | 0; + break; + //400012Bh - SIOMLT_SEND - Data Send Register (R/W) + case 0x400012B: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA8_1() | 0; + break; + //400012Ch through 400012Fh - NOT USED - GLITCHED + //4000130h - KEYINPUT - Key Status (R) + case 0x4000130: + data = this.joypad.readKeyStatus8_0() | 0; + break; + //4000131h - KEYINPUT - Key Status (R) + case 0x4000131: + data = this.joypad.readKeyStatus8_1() | 0; + break; + //4000132h - KEYCNT - Key Interrupt Control (R/W) + case 0x4000132: + data = this.joypad.readKeyControl8_0() | 0; + break; + //4000133h - KEYCNT - Key Interrupt Control (R/W) + case 0x4000133: + data = this.joypad.readKeyControl8_1() | 0; + break; + //4000134h - RCNT (R/W) - Mode Selection + case 0x4000134: + this.IOCore.updateSerialClocking(); + data = this.serial.readRCNT0() | 0; + break; + //4000135h - RCNT (R/W) - Mode Selection + case 0x4000135: + this.IOCore.updateSerialClocking(); + data = this.serial.readRCNT1() | 0; + break; + //4000138h through 400013Fh - NOT USED - GLITCHED + //4000140h - JOYCNT - JOY BUS Control Register (R/W) + case 0x4000140: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYCNT() | 0; + break; + //4000144h through 400014Fh - NOT USED - GLITCHED + //4000150h - JoyBus Receive (R/W) + case 0x4000150: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_RECV0() | 0; + break; + //4000151h - JoyBus Receive (R/W) + case 0x4000151: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_RECV1() | 0; + break; + //4000152h - JoyBus Receive (R/W) + case 0x4000152: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_RECV2() | 0; + break; + //4000153h - JoyBus Receive (R/W) + case 0x4000153: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_RECV3() | 0; + break; + //4000154h - JoyBus Send (R/W) + case 0x4000154: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_SEND0() | 0; + break; + //4000155h - JoyBus Send (R/W) + case 0x4000155: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_SEND1() | 0; + break; + //4000156h - JoyBus Send (R/W) + case 0x4000156: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_SEND2() | 0; + break; + //4000157h - JoyBus Send (R/W) + case 0x4000157: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_SEND3() | 0; + break; + //4000158h - JoyBus Stat (R/W) + case 0x4000158: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_STAT() | 0; + break; + //400015Ch through 40001FFh - NOT USED - GLITCHED + //4000200h - IE - Interrupt Enable Register (R/W) + case 0x4000200: + data = this.irq.readIE8_0() | 0; + break; + //4000201h - IE - Interrupt Enable Register (R/W) + case 0x4000201: + data = this.irq.readIE8_1() | 0; + break; + //4000202h - IF - Interrupt Request Flags / IRQ Acknowledge + case 0x4000202: + data = this.irq.readIF8_0() | 0; + break; + //4000203h - IF - Interrupt Request Flags / IRQ Acknowledge + case 0x4000203: + data = this.irq.readIF8_1() | 0; + break; + //4000204h - WAITCNT - Waitstate Control (R/W) + case 0x4000204: + data = this.wait.readWAITCNT8_0() | 0; + break; + //4000205h - WAITCNT - Waitstate Control (R/W) + case 0x4000205: + data = this.wait.readWAITCNT8_1() | 0; + break; + //4000208h - IME - Interrupt Master Enable Register (R/W) + case 0x4000208: + data = this.irq.readIME() | 0; + break; + //400020Ch through 40002FFh - NOT USED - GLITCHED + //4000300h - POSTFLG - BYTE - Undocumented - Post Boot / Debug Control (R/W) + case 0x4000300: + data = this.wait.readPOSTBOOT() | 0; + break; + default: + data = this.readIO8LessCalled(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceMemory.prototype.readIO8LessCalled = function (address) { + address = address | 0; + var data = 0; + switch (address | 0) { + //4000003h - Undocumented - Green Swap (R/W) + case 0x4000003: + //4000007h - VCOUNT - Vertical Counter (Read only) + case 0x4000007: + //400004Ch - MOSAIC - Mosaic Size (W) + case 0x400004C: + //400004Dh - MOSAIC - Mosaic Size (W) + case 0x400004D: + //400004Eh - NOT USED - ZERO + case 0x400004E: + //400004Fh - NOT USED - ZERO + case 0x400004F: + //4000061h - NOT USED - ZERO + case 0x4000061: + //4000064h - SOUND1CNT_X (NR13, NR14) - Channel 1 Frequency/Control (R/W) + case 0x4000064: + //4000066h - NOT USED - ZERO + case 0x4000066: + //4000067h - NOT USED - ZERO + case 0x4000067: + //400006Ah - NOT USED - ZERO + case 0x400006A: + //400006Bh - NOT USED - ZERO + case 0x400006B: + //400006Ch - SOUND2CNT_H (NR23, NR24) - Channel 2 Frequency/Control (R/W) + case 0x400006C: + //400006Eh - NOT USED - ZERO + case 0x400006E: + //400006Fh - NOT USED - ZERO + case 0x400006F: + //4000071h - SOUND3CNT_L (NR30) - Channel 3 Stop/Wave RAM select (R/W) + case 0x4000071: + //4000072h - SOUND3CNT_H (NR31, NR32) - Channel 3 Length/Volume (R/W) + case 0x4000072: + //4000074h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W) + case 0x4000074: + //4000076h - NOT USED - ZERO + case 0x4000076: + //4000077h - NOT USED - ZERO + case 0x4000077: + //4000078h - SOUND4CNT_L (NR41, NR42) - Channel 4 Length/Envelope (R/W) + case 0x4000078: + //400007Ah - NOT USED - ZERO + case 0x400007A: + //400007Bh - NOT USED - ZERO + case 0x400007B: + //400007Eh - NOT USED - ZERO + case 0x400007E: + //400007Fh - NOT USED - ZERO + case 0x400007F: + //4000085h - NOT USED - ZERO + case 0x4000085: + //4000086h - NOT USED - ZERO + case 0x4000086: + //4000087h - NOT USED - ZERO + case 0x4000087: + //400008Ah - NOT USED - ZERO + case 0x400008A: + //400008Bh - NOT USED - ZERO + case 0x400008B: + //4000103h - TM0CNT_H - Timer 0 Control (R/W) + case 0x4000103: + //4000107h - TM1CNT_H - Timer 1 Control (R/W) + case 0x4000107: + //400010Bh - TM2CNT_H - Timer 2 Control (R/W) + case 0x400010B: + //400010Fh - TM3CNT_H - Timer 3 Control (R/W) + case 0x400010F: + //4000136h - NOT USED - ZERO + case 0x4000136: + //4000137h - NOT USED - ZERO + case 0x4000137: + //4000141h - JOYCNT - JOY BUS Control Register (R/W) + case 0x4000141: + //4000142h - NOT USED - ZERO + case 0x4000142: + //4000143h - NOT USED - ZERO + case 0x4000143: + //4000159h - JoyBus Stat (R/W) + case 0x4000159: + //400015Ah - NOT USED - ZERO + case 0x400015A: + //400015Bh - NOT USED - ZERO + case 0x400015B: + //4000206h - NOT USED - ZERO + case 0x4000206: + //4000207h - NOT USED - ZERO + case 0x4000207: + //4000209h - IME - Interrupt Master Enable Register (R/W) + case 0x4000209: + //400020Ah - NOT USED - ZERO + case 0x400020A: + //400020Bh - NOT USED - ZERO + case 0x400020B: + //4000301h - HALTCNT - BYTE - Undocumented - Low Power Mode Control (W) + case 0x4000301: + //4000302h - NOT USED - ZERO + case 0x4000302: + //4000303h - NOT USED - ZERO + case 0x4000303: + break; + default: + if ((address & 0xFFFC) == 0x800) { + //WRAM wait state control: + data = this.wait.readConfigureWRAM8(address | 0) | 0; + } + else { + //Undefined Illegal I/O: + data = this.readUnused8CPUBase(address | 0) | 0; + } + } + return data | 0; +} +GameBoyAdvanceMemory.prototype.readIODispatch16 = function (address) { + address = address | 0; + var data = 0; + this.wait.singleClock(); + var data = this.readIO16(address | 0) | 0; + return data | 0; +} +GameBoyAdvanceMemory.prototype.readIODispatch16CPU = function (address) { + address = address | 0; + this.IOCore.updateCoreSingle(); + var data = this.readIO16(address | 0) | 0; + return data | 0; +} +GameBoyAdvanceMemory.prototype.readIO16 = function (address) { + address = address | 0; + var data = 0; + switch (address & -2) { + //4000000h - DISPCNT - LCD Control (Read/Write) + case 0x4000000: + data = this.gfxRenderer.readDISPCNT16() | 0; + break; + //4000002h - Undocumented - Green Swap (R/W) + case 0x4000002: + data = this.gfxRenderer.readDISPCNT8_2() | 0; + break; + //4000004h - DISPSTAT - General LCD Status (Read/Write) + case 0x4000004: + data = this.gfxState.readDISPSTAT16_0() | 0; + break; + //4000006h - VCOUNT - Vertical Counter (Read only) + case 0x4000006: + data = this.gfxState.readDISPSTAT8_2() | 0; + break; + //4000008h - BG0CNT - BG0 Control (R/W) (BG Modes 0,1 only) + case 0x4000008: + data = this.gfxRenderer.readBG0CNT16() | 0; + break; + //400000Ah - BG1CNT - BG1 Control (R/W) (BG Modes 0,1 only) + case 0x400000A: + data = this.gfxRenderer.readBG1CNT16() | 0; + break; + //400000Ch - BG2CNT - BG2 Control (R/W) (BG Modes 0,1,2 only) + case 0x400000C: + data = this.gfxRenderer.readBG2CNT16() | 0; + break; + //400000Eh - BG3CNT - BG3 Control (R/W) (BG Modes 0,2 only) + case 0x400000E: + data = this.gfxRenderer.readBG3CNT16() | 0; + break; + //4000010h through 4000047h - WRITE ONLY + //4000048h - WININ - Control of Inside of Window(s) (R/W) + case 0x4000048: + data = this.gfxRenderer.readWININ16() | 0; + break; + //400004Ah- WINOUT - Control of Outside of Windows & Inside of OBJ Window (R/W) + case 0x400004A: + data = this.gfxRenderer.readWINOUT16() | 0; + break; + //4000050h - BLDCNT - Color Special Effects Selection (R/W) + case 0x4000050: + data = this.gfxRenderer.readBLDCNT16() | 0; + break; + //4000052h - BLDALPHA - Alpha Blending Coefficients (R/W) + case 0x4000052: + data = this.gfxRenderer.readBLDALPHA16() | 0; + break; + //4000054h through 400005Fh - NOT USED - GLITCHED + //4000060h - SOUND1CNT_L (NR10) - Channel 1 Sweep register (R/W) + case 0x4000060: + //NR10: + data = this.sound.readSOUND1CNT8_0() | 0; + break; + //4000062h - SOUND1CNT_H (NR11, NR12) - Channel 1 Duty/Len/Envelope (R/W) + case 0x4000062: + //NR11: + //NR12: + data = this.sound.readSOUND1CNT8_2() | (this.sound.readSOUND1CNT8_3() << 8); + break; + //4000064h - SOUND1CNT_X (NR13, NR14) - Channel 1 Frequency/Control (R/W) + case 0x4000064: + //NR14: + data = this.sound.readSOUND1CNTX16() | 0; + break; + //4000068h - SOUND2CNT_L (NR21, NR22) - Channel 2 Duty/Length/Envelope (R/W) + case 0x4000068: + //NR21: + //NR22: + data = this.sound.readSOUND2CNTL16() | 0; + break; + //400006Ch - SOUND2CNT_H (NR23, NR24) - Channel 2 Frequency/Control (R/W) + case 0x400006C: + //NR24: + data = this.sound.readSOUND2CNTH16() | 0; + break; + //4000070h - SOUND3CNT_L (NR30) - Channel 3 Stop/Wave RAM select (R/W) + case 0x4000070: + //NR30: + data = this.sound.readSOUND3CNT8_0() | 0; + break; + //4000073h - SOUND3CNT_H (NR31, NR32) - Channel 3 Length/Volume (R/W) + case 0x4000072: + //NR32: + data = this.sound.readSOUND3CNT16_1() | 0; + break; + //4000074h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W) + case 0x4000074: + //NR34: + data = this.sound.readSOUND3CNTX16() | 0; + break; + //4000078h - SOUND4CNT_L (NR41, NR42) - Channel 4 Length/Envelope (R/W) + case 0x4000078: + //NR42: + data = this.sound.readSOUND4CNTL16() | 0; + break; + //400007Ch - SOUND4CNT_H (NR43, NR44) - Channel 4 Frequency/Control (R/W) + case 0x400007C: + //NR43: + //NR44: + data = this.sound.readSOUND4CNTH16() | 0; + break; + //4000080h - SOUNDCNT_L (NR50, NR51) - Channel L/R Volume/Enable (R/W) + case 0x4000080: + //NR50: + //NR51: + data = this.sound.readSOUNDCNTL16() | 0; + break; + //4000082h - SOUNDCNT_H (GBA only) - DMA Sound Control/Mixing (R/W) + case 0x4000082: + data = this.sound.readSOUNDCNTH16() | 0; + break; + //4000084h - SOUNDCNT_X (NR52) - Sound on/off (R/W) + case 0x4000084: + data = this.sound.readSOUNDCNTX8() | 0; + break; + //4000088h - SOUNDBIAS - Sound PWM Control (R/W, see below) + case 0x4000088: + data = this.sound.readSOUNDBIAS16() | 0; + break; + //400008Ch - NOT USED - GLITCHED + //400008Eh - NOT USED - GLITCHED + //4000090h - WAVE_RAM0_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000090: + //4000092h - WAVE_RAM0_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000092: + //4000094h - WAVE_RAM1_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000094: + //4000096h - WAVE_RAM1_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000096: + //4000098h - WAVE_RAM2_L - Channel 3 Wave Pattern RAM (W/R) + case 0x4000098: + //400009Ah - WAVE_RAM2_H - Channel 3 Wave Pattern RAM (W/R) + case 0x400009A: + //400009Ch - WAVE_RAM3_L - Channel 3 Wave Pattern RAM (W/R) + case 0x400009C: + //400009Eh - WAVE_RAM3_H - Channel 3 Wave Pattern RAM (W/R) + case 0x400009E: + data = this.sound.readWAVE16(address & 0xF) | 0; + break; + //40000A0h through 40000B9h - WRITE ONLY + //40000BAh - DMA0CNT_H - DMA 0 Control (R/W) + case 0x40000BA: + data = this.dmaChannel0.readDMAControl16() | 0; + break; + //40000BCh through 40000C5h - WRITE ONLY + //40000C6h - DMA1CNT_H - DMA 1 Control (R/W) + case 0x40000C6: + data = this.dmaChannel1.readDMAControl16() | 0; + break; + //40000C8h through 40000D1h - WRITE ONLY + //40000D2h - DMA2CNT_H - DMA 2 Control (R/W) + case 0x40000D2: + data = this.dmaChannel2.readDMAControl16() | 0; + break; + //40000D4h through 40000DDh - WRITE ONLY + //40000DEh - DMA3CNT_H - DMA 3 Control (R/W) + case 0x40000DE: + data = this.dmaChannel3.readDMAControl16() | 0; + break; + //40000E0h through 40000FFh - NOT USED - GLITCHED + //4000100h - TM0CNT_L - Timer 0 Counter/Reload (R/W) + case 0x4000100: + data = this.timer.readTM0CNT16() | 0; + break; + //4000102h - TM0CNT_H - Timer 0 Control (R/W) + case 0x4000102: + data = this.timer.readTM0CNT8_2() | 0; + break; + //4000104h - TM1CNT_L - Timer 1 Counter/Reload (R/W) + case 0x4000104: + data = this.timer.readTM1CNT16() | 0; + break; + //4000106h - TM1CNT_H - Timer 1 Control (R/W) + case 0x4000106: + data = this.timer.readTM1CNT8_2() | 0; + break; + //4000108h - TM2CNT_L - Timer 2 Counter/Reload (R/W) + case 0x4000108: + data = this.timer.readTM2CNT16() | 0; + break; + //400010Ah - TM2CNT_H - Timer 2 Control (R/W) + case 0x400010A: + data = this.timer.readTM2CNT8_2() | 0; + break; + //400010Ch - TM3CNT_L - Timer 3 Counter/Reload (R/W) + case 0x400010C: + data = this.timer.readTM3CNT16() | 0; + break; + //400010Eh - TM3CNT_H - Timer 3 Control (R/W) + case 0x400010E: + data = this.timer.readTM3CNT8_2() | 0; + break; + //4000110h through 400011Fh - NOT USED - GLITCHED + //4000120h - Serial Data A (R/W) + case 0x4000120: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA_A0() | (this.serial.readSIODATA_A1() << 8); + break; + //4000122h - Serial Data B (R/W) + case 0x4000122: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA_B0() | (this.serial.readSIODATA_B1() << 8); + break; + //4000124h - Serial Data C (R/W) + case 0x4000124: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA_C0() | (this.serial.readSIODATA_C1() << 8); + break; + //4000126h - Serial Data D (R/W) + case 0x4000126: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA_D0() | (this.serial.readSIODATA_D1() << 8); + break; + //4000128h - SIOCNT - SIO Sub Mode Control (R/W) + case 0x4000128: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIOCNT0() | (this.serial.readSIOCNT1() << 8); + break; + //400012Ah - SIOMLT_SEND - Data Send Register (R/W) + case 0x400012A: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA8_0() | (this.serial.readSIODATA8_1() << 8); + break; + //400012Ch through 400012Fh - NOT USED - GLITCHED + //4000130h - KEYINPUT - Key Status (R) + case 0x4000130: + data = this.joypad.readKeyStatus16() | 0; + break; + //4000132h - KEYCNT - Key Interrupt Control (R/W) + case 0x4000132: + data = this.joypad.readKeyControl16() | 0; + break; + //4000134h - RCNT (R/W) - Mode Selection + case 0x4000134: + this.IOCore.updateSerialClocking(); + data = this.serial.readRCNT0() | (this.serial.readRCNT1() << 8); + break; + //4000138h through 400013Fh - NOT USED - GLITCHED + //4000140h - JOYCNT - JOY BUS Control Register (R/W) + case 0x4000140: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYCNT() | 0; + break; + //4000144h through 400014Fh - NOT USED - GLITCHED + //4000150h - JoyBus Receive (R/W) + case 0x4000150: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_RECV0() | (this.serial.readJOYBUS_RECV1() << 8); + break; + //4000152h - JoyBus Receive (R/W) + case 0x4000152: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_RECV2() | (this.serial.readJOYBUS_RECV3() << 8); + break; + //4000154h - JoyBus Send (R/W) + case 0x4000154: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_SEND0() | (this.serial.readJOYBUS_SEND1() << 8); + break; + //4000156h - JoyBus Send (R/W) + case 0x4000156: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_SEND2() | (this.serial.readJOYBUS_SEND3() << 8); + break; + //4000158h - JoyBus Stat (R/W) + case 0x4000158: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_STAT() | 0; + break; + //400015Ch through 40001FFh - NOT USED - GLITCHED + //4000200h - IE - Interrupt Enable Register (R/W) + case 0x4000200: + data = this.irq.readIE16() | 0; + break; + //4000202h - IF - Interrupt Request Flags / IRQ Acknowledge + case 0x4000202: + data = this.irq.readIF16() | 0; + break; + //4000204h - WAITCNT - Waitstate Control (R/W) + case 0x4000204: + data = this.wait.readWAITCNT16() | 0; + break; + //4000208h - IME - Interrupt Master Enable Register (R/W) + case 0x4000208: + data = this.irq.readIME() | 0; + break; + //400020Ch through 40002FFh - NOT USED - GLITCHED + //4000300h - POSTFLG - BYTE - Undocumented - Post Boot / Debug Control (R/W) + case 0x4000300: + data = this.wait.readPOSTBOOT() | 0; + break; + default: + data = this.readIO16LessCalled(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceMemory.prototype.readIO16LessCalled = function (address) { + address = address | 0; + var data = 0; + switch (address & -2) { + //400004Ch - MOSAIC - Mosaic Size (W) + case 0x400004C: + //400004Eh - NOT USED - ZERO + case 0x400004E: + //4000066h - NOT USED - ZERO + case 0x4000066: + //400006Ah - NOT USED - ZERO + case 0x400006A: + //400006Eh - NOT USED - ZERO + case 0x400006E: + //4000076h - NOT USED - ZERO + case 0x4000076: + //400007Ah - NOT USED - ZERO + case 0x400007A: + //400007Eh - NOT USED - ZERO + case 0x400007E: + //4000086h - NOT USED - ZERO + case 0x4000086: + //400008Ah - NOT USED - ZERO + case 0x400008A: + //4000136h - NOT USED - ZERO + case 0x4000136: + //4000142h - NOT USED - ZERO + case 0x4000142: + //400015Ah - NOT USED - ZERO + case 0x400015A: + //4000206h - NOT USED - ZERO + case 0x4000206: + //400020Ah - NOT USED - ZERO + case 0x400020A: + //4000302h - NOT USED - ZERO + case 0x4000302: + break; + default: + if ((address & 0xFFFC) == 0x800) { + //WRAM wait state control: + data = this.wait.readConfigureWRAM16(address | 0) | 0; + } + else { + //Undefined Illegal I/O: + data = this.readUnused16MultiBase(address | 0) | 0; + } + } + return data | 0; +} +GameBoyAdvanceMemory.prototype.readIODispatch32 = function (address) { + address = address | 0; + this.wait.singleClock(); + var data = this.readIO32(address | 0) | 0; + return data | 0; +} +GameBoyAdvanceMemory.prototype.readIODispatch32CPU = function (address) { + address = address | 0; + this.IOCore.updateCoreSingle(); + var data = this.readIO32(address | 0) | 0; + return data | 0; +} +GameBoyAdvanceMemory.prototype.readIO32 = function (address) { + address = address | 0; + var data = 0; + switch (address & -4) { + //4000000h - DISPCNT - LCD Control (Read/Write) + //4000002h - Undocumented - Green Swap (R/W) + case 0x4000000: + data = this.gfxRenderer.readDISPCNT32() | 0; + break; + //4000004h - DISPSTAT - General LCD Status (Read/Write) + //4000006h - VCOUNT - Vertical Counter (Read only) + case 0x4000004: + data = this.gfxState.readDISPSTAT32() | 0; + break; + //4000008h - BG0CNT - BG0 Control (R/W) (BG Modes 0,1 only) + //400000Ah - BG1CNT - BG1 Control (R/W) (BG Modes 0,1 only) + case 0x4000008: + data = this.gfxRenderer.readBG0BG1CNT32() | 0; + break; + //400000Ch - BG2CNT - BG2 Control (R/W) (BG Modes 0,1,2 only) + //400000Eh - BG3CNT - BG3 Control (R/W) (BG Modes 0,2 only) + case 0x400000C: + data = this.gfxRenderer.readBG2BG3CNT32() | 0; + break; + //4000010h through 4000047h - WRITE ONLY + //4000048h - WININ - Control of Inside of Window(s) (R/W) + //400004Ah- WINOUT - Control of Outside of Windows & Inside of OBJ Window (R/W) + case 0x4000048: + data = this.gfxRenderer.readWINCONTROL32() | 0; + break; + //400004Ch - MOSAIC - Mosaic Size (W) + //4000050h - BLDCNT - Color Special Effects Selection (R/W) + //4000052h - BLDALPHA - Alpha Blending Coefficients (R/W) + case 0x4000050: + data = this.gfxRenderer.readBLDCNT32() | 0; + break; + //4000054h through 400005Fh - NOT USED - GLITCHED + //4000060h - SOUND1CNT_L (NR10) - Channel 1 Sweep register (R/W) + //4000062h - SOUND1CNT_H (NR11, NR12) - Channel 1 Duty/Len/Envelope (R/W) + case 0x4000060: + //NR10: + //NR11: + //NR12: + data = this.sound.readSOUND1CNT32() | 0; + break; + //4000064h - SOUND1CNT_X (NR13, NR14) - Channel 1 Frequency/Control (R/W) + //4000066h - NOT USED - ZERO + case 0x4000064: + //NR14: + data = this.sound.readSOUND1CNTX16() | 0; + break; + //4000068h - SOUND2CNT_L (NR21, NR22) - Channel 2 Duty/Length/Envelope (R/W) + //400006Ah - NOT USED - ZERO + case 0x4000068: + //NR21: + //NR22: + data = this.sound.readSOUND2CNTL16() | 0; + break; + //400006Ch - SOUND2CNT_H (NR23, NR24) - Channel 2 Frequency/Control (R/W) + //400006Eh - NOT USED - ZERO + case 0x400006C: + //NR24: + data = this.sound.readSOUND2CNTH16() | 0; + break; + //4000070h - SOUND3CNT_L (NR30) - Channel 3 Stop/Wave RAM select (R/W) + //4000073h - SOUND3CNT_H (NR31, NR32) - Channel 3 Length/Volume (R/W) + case 0x4000070: + //NR30: + //NR32: + data = this.sound.readSOUND3CNT32() | 0; + break; + //4000074h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W) + //4000076h - NOT USED - ZERO + case 0x4000074: + //NR34: + data = this.sound.readSOUND3CNTX16() | 0; + break; + //4000078h - SOUND4CNT_L (NR41, NR42) - Channel 4 Length/Envelope (R/W) + //400007Ah - NOT USED - ZERO + case 0x4000078: + //NR42: + data = this.sound.readSOUND4CNTL16() | 0; + break; + //400007Ch - SOUND4CNT_H (NR43, NR44) - Channel 4 Frequency/Control (R/W) + //400007Eh - NOT USED - ZERO + case 0x400007C: + //NR43: + //NR44: + data = this.sound.readSOUND4CNTH16() | 0; + break; + //4000080h - SOUNDCNT_L (NR50, NR51) - Channel L/R Volume/Enable (R/W) + //4000082h - SOUNDCNT_H (GBA only) - DMA Sound Control/Mixing (R/W) + case 0x4000080: + //NR50: + //NR51: + data = this.sound.readSOUNDCNT32() | 0; + break; + //4000084h - SOUNDCNT_X (NR52) - Sound on/off (R/W) + //4000086h - NOT USED - ZERO + case 0x4000084: + data = this.sound.readSOUNDCNTX8() | 0; + break; + //4000088h - SOUNDBIAS - Sound PWM Control (R/W, see below) + //400008Ah - NOT USED - ZERO + case 0x4000088: + data = this.sound.readSOUNDBIAS16() | 0; + break; + //400008Ch - NOT USED - GLITCHED + //400008Eh - NOT USED - GLITCHED + //4000090h - WAVE_RAM0_L - Channel 3 Wave Pattern RAM (W/R) + //4000092h - WAVE_RAM0_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000090: + //4000094h - WAVE_RAM1_L - Channel 3 Wave Pattern RAM (W/R) + //4000096h - WAVE_RAM1_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000094: + //4000098h - WAVE_RAM2_L - Channel 3 Wave Pattern RAM (W/R) + //400009Ah - WAVE_RAM2_H - Channel 3 Wave Pattern RAM (W/R) + case 0x4000098: + //400009Ch - WAVE_RAM3_L - Channel 3 Wave Pattern RAM (W/R) + //400009Eh - WAVE_RAM3_H - Channel 3 Wave Pattern RAM (W/R) + case 0x400009C: + data = this.sound.readWAVE32(address & 0xF) | 0; + break; + //40000A0h through 40000B9h - WRITE ONLY + //40000BAh - DMA0CNT_H - DMA 0 Control (R/W) + case 0x40000B8: + data = this.dmaChannel0.readDMAControl16() << 16; + break; + //40000BCh through 40000C5h - WRITE ONLY + //40000C6h - DMA1CNT_H - DMA 1 Control (R/W) + case 0x40000C4: + data = this.dmaChannel1.readDMAControl16() << 16; + break; + //40000C8h through 40000D1h - WRITE ONLY + //40000D2h - DMA2CNT_H - DMA 2 Control (R/W) + case 0x40000D0: + data = this.dmaChannel2.readDMAControl16() << 16; + break; + //40000D4h through 40000DDh - WRITE ONLY + //40000DEh - DMA3CNT_H - DMA 3 Control (R/W) + case 0x40000DC: + data = this.dmaChannel3.readDMAControl16() << 16; + break; + //40000E0h through 40000FFh - NOT USED - GLITCHED + //4000100h - TM0CNT_L - Timer 0 Counter/Reload (R/W) + //4000102h - TM0CNT_H - Timer 0 Control (R/W) + case 0x4000100: + data = this.timer.readTM0CNT32() | 0; + break; + //4000104h - TM1CNT_L - Timer 1 Counter/Reload (R/W) + //4000106h - TM1CNT_H - Timer 1 Control (R/W) + case 0x4000104: + data = this.timer.readTM1CNT32() | 0; + break; + //4000108h - TM2CNT_L - Timer 2 Counter/Reload (R/W) + //400010Ah - TM2CNT_H - Timer 2 Control (R/W) + case 0x4000108: + data = this.timer.readTM2CNT32() | 0; + break; + //400010Ch - TM3CNT_L - Timer 3 Counter/Reload (R/W) + //400010Eh - TM3CNT_H - Timer 3 Control (R/W) + case 0x400010C: + data = this.timer.readTM3CNT32() | 0; + break; + //4000110h through 400011Fh - NOT USED - GLITCHED + //4000120h - Serial Data A (R/W) + //4000122h - Serial Data B (R/W) + case 0x4000110: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA_A0() | + (this.serial.readSIODATA_A1() << 8) | + (this.serial.readSIODATA_B0() << 16) | + (this.serial.readSIODATA_B1() << 24); + break; + //4000124h - Serial Data C (R/W) + //4000126h - Serial Data D (R/W) + case 0x4000124: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIODATA_C0() | + (this.serial.readSIODATA_C1() << 8) | + (this.serial.readSIODATA_D0() << 16) | + (this.serial.readSIODATA_D1() << 24); + break; + //4000128h - SIOCNT - SIO Sub Mode Control (R/W) + //400012Ah - SIOMLT_SEND - Data Send Register (R/W) + case 0x4000128: + this.IOCore.updateSerialClocking(); + data = this.serial.readSIOCNT0() | + (this.serial.readSIOCNT1() << 8) | + (this.serial.readSIODATA8_0() << 16) | + (this.serial.readSIODATA8_1() << 24); + break; + //400012Ch through 400012Fh - NOT USED - GLITCHED + //4000130h - KEYINPUT - Key Status (R) + //4000132h - KEYCNT - Key Interrupt Control (R/W) + case 0x4000130: + data = this.joypad.readKeyStatusControl32() | 0; + break; + //4000134h - RCNT (R/W) - Mode Selection + //4000136h - NOT USED - ZERO + case 0x4000134: + this.IOCore.updateSerialClocking(); + data = this.serial.readRCNT0() | (this.serial.readRCNT1() << 8); + break; + //4000138h through 400013Fh - NOT USED - GLITCHED + //4000140h - JOYCNT - JOY BUS Control Register (R/W) + //4000142h - NOT USED - ZERO + case 0x4000138: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYCNT() | 0; + break; + //4000144h through 400014Fh - NOT USED - GLITCHED + //4000150h - JoyBus Receive (R/W) + //4000152h - JoyBus Receive (R/W) + case 0x4000144: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_RECV0() | + (this.serial.readJOYBUS_RECV1() << 8) | + (this.serial.readJOYBUS_RECV2() << 16) | + (this.serial.readJOYBUS_RECV3() << 24); + break; + //4000154h - JoyBus Send (R/W) + //4000156h - JoyBus Send (R/W) + case 0x4000154: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_SEND0() | + (this.serial.readJOYBUS_SEND1() << 8) | + (this.serial.readJOYBUS_SEND2() << 16) | + (this.serial.readJOYBUS_SEND3() << 24); + break; + //4000158h - JoyBus Stat (R/W) + //400015Ah - NOT USED - ZERO + case 0x4000158: + this.IOCore.updateSerialClocking(); + data = this.serial.readJOYBUS_STAT() | 0; + break; + //400015Ch through 40001FFh - NOT USED - GLITCHED + //4000200h - IE - Interrupt Enable Register (R/W) + //4000202h - IF - Interrupt Request Flags / IRQ Acknowledge + case 0x4000200: + data = this.irq.readIRQ32() | 0; + break; + //4000204h - WAITCNT - Waitstate Control (R/W) + //4000206h - NOT USED - ZERO + case 0x4000204: + data = this.wait.readWAITCNT16() | 0; + break; + //4000208h - IME - Interrupt Master Enable Register (R/W) + //400020Ah - NOT USED - ZERO + case 0x4000208: + data = this.irq.readIME() | 0; + break; + //400020Ch through 40002FFh - NOT USED - GLITCHED + //4000300h - POSTFLG - BYTE - Undocumented - Post Boot / Debug Control (R/W) + //4000302h - NOT USED - ZERO + case 0x4000300: + data = this.wait.readPOSTBOOT() | 0; + break; + //UNDEFINED / ILLEGAL: + default: + if ((address & 0xFFFC) == 0x800) { + //WRAM wait state control: + data = this.wait.readConfigureWRAM32() | 0; + } + else { + //Undefined Illegal I/O: + data = this.readUnused32MultiBase() | 0; + } + } + return data | 0; +} +GameBoyAdvanceMemory.prototype.readVRAM8Preliminary = function (address) { + address = address | 0; + this.IOCore.updateGraphicsClocking(); + var data = 0; + switch (address >> 24) { + case 0x5: + this.wait.VRAMAccess(); + data = this.gfxRenderer.readPalette8(address | 0) | 0; + break; + case 0x6: + this.wait.VRAMAccess(); + data = this.gfxRenderer.readVRAM8(address | 0) | 0; + break; + default: + this.wait.OAMAccess(); + data = this.gfxRenderer.readOAM(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceMemory.prototype.readVRAM16Preliminary = function (address) { + address = address | 0; + this.IOCore.updateGraphicsClocking(); + var data = 0; + switch (address >> 24) { + case 0x5: + this.wait.VRAMAccess(); + data = this.gfxRenderer.readPalette16(address | 0) | 0; + break; + case 0x6: + this.wait.VRAMAccess(); + data = this.gfxRenderer.readVRAM16(address | 0) | 0; + break; + default: + this.wait.OAMAccess(); + data = this.gfxRenderer.readOAM16(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceMemory.prototype.readVRAM16CPUPreliminary = function (address) { + address = address | 0; + this.IOCore.updateGraphicsClocking(); + var data = 0; + switch (address >> 24) { + case 0x5: + this.wait.VRAMAccess16CPU(); + data = this.gfxRenderer.readPalette16(address | 0) | 0; + break; + case 0x6: + this.wait.VRAMAccess16CPU(); + data = this.gfxRenderer.readVRAM16(address | 0) | 0; + break; + default: + this.wait.OAMAccessCPU(); + data = this.gfxRenderer.readOAM16(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceMemory.prototype.readVRAM32Preliminary = function (address) { + address = address | 0; + this.IOCore.updateGraphicsClocking(); + var data = 0; + switch (address >> 24) { + case 0x5: + this.wait.VRAMAccess32(); + data = this.gfxRenderer.readPalette32(address | 0) | 0; + break; + case 0x6: + this.wait.VRAMAccess32(); + data = this.gfxRenderer.readVRAM32(address | 0) | 0; + break; + default: + this.wait.OAMAccess(); + data = this.gfxRenderer.readOAM32(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceMemory.prototype.readVRAM32CPUPreliminary = function (address) { + address = address | 0; + this.IOCore.updateGraphicsClocking(); + var data = 0; + switch (address >> 24) { + case 0x5: + this.wait.VRAMAccess32CPU(); + data = this.gfxRenderer.readPalette32(address | 0) | 0; + break; + case 0x6: + this.wait.VRAMAccess32CPU(); + data = this.gfxRenderer.readVRAM32(address | 0) | 0; + break; + default: + this.wait.OAMAccessCPU(); + data = this.gfxRenderer.readOAM32(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceMemory.prototype.readROM8 = function (address) { + address = address | 0; + this.wait.ROMAccess(address | 0); + return this.cartridge.readROM8(address & 0x1FFFFFF) | 0; +} +GameBoyAdvanceMemory.prototype.readROM16 = function (address) { + address = address | 0; + this.wait.ROMAccess(address | 0); + return this.cartridge.readROM16(address & 0x1FFFFFE) | 0; +} +GameBoyAdvanceMemory.prototype.readROM16CPU = function (address) { + address = address | 0; + this.wait.ROMAccess16CPU(address | 0); + return this.cartridge.readROM16(address & 0x1FFFFFE) | 0; +} +GameBoyAdvanceMemory.prototype.readROM32 = function (address) { + address = address | 0; + this.wait.ROMAccess32(address | 0); + return this.cartridge.readROM32(address & 0x1FFFFFC) | 0; +} +GameBoyAdvanceMemory.prototype.readROM32CPU = function (address) { + address = address | 0; + this.wait.ROMAccess32CPU(address | 0); + return this.cartridge.readROM32(address & 0x1FFFFFC) | 0; +} +GameBoyAdvanceMemory.prototype.readROM28 = function (address) { + address = address | 0; + this.wait.ROMAccess(address | 0); + return this.cartridge.readROM8Space2(address & 0x1FFFFFF) | 0; +} +GameBoyAdvanceMemory.prototype.readROM216 = function (address) { + address = address | 0; + this.wait.ROMAccess(address | 0); + return this.cartridge.readROM16Space2(address & 0x1FFFFFE) | 0; +} +GameBoyAdvanceMemory.prototype.readROM216CPU = function (address) { + address = address | 0; + this.wait.ROMAccess16CPU(address | 0); + return this.cartridge.readROM16Space2(address & 0x1FFFFFE) | 0; +} +GameBoyAdvanceMemory.prototype.readROM232 = function (address) { + address = address | 0; + this.wait.ROMAccess32(address | 0); + return this.cartridge.readROM32Space2(address & 0x1FFFFFC) | 0; +} +GameBoyAdvanceMemory.prototype.readROM232CPU = function (address) { + address = address | 0; + this.wait.ROMAccess32CPU(address | 0); + return this.cartridge.readROM32Space2(address & 0x1FFFFFC) | 0; +} +GameBoyAdvanceMemory.prototype.readSRAM8 = function (address) { + address = address | 0; + this.wait.SRAMAccess(); + return this.saves.readSRAM(address & 0xFFFF) | 0; +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceMemory.prototype.readSRAM16 = function (address) { + address = address | 0; + this.wait.SRAMAccess(); + return Math.imul(this.saves.readSRAM(address & 0xFFFE) | 0, 0x101) | 0; + } + GameBoyAdvanceMemory.prototype.readSRAM16CPU = function (address) { + address = address | 0; + this.wait.SRAMAccessCPU(); + return Math.imul(this.saves.readSRAM(address & 0xFFFE) | 0, 0x101) | 0; + } + GameBoyAdvanceMemory.prototype.readSRAM32 = function (address) { + address = address | 0; + this.wait.SRAMAccess(); + return Math.imul(this.saves.readSRAM(address & 0xFFFC) | 0, 0x1010101) | 0; + } + GameBoyAdvanceMemory.prototype.readSRAM32CPU = function (address) { + address = address | 0; + this.wait.SRAMAccessCPU(); + return Math.imul(this.saves.readSRAM(address & 0xFFFC) | 0, 0x1010101) | 0; + } +} +else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceMemory.prototype.readSRAM16 = function (address) { + address = address | 0; + this.wait.SRAMAccess(); + return ((this.saves.readSRAM(address & 0xFFFE) | 0) * 0x101) | 0; + } + GameBoyAdvanceMemory.prototype.readSRAM16CPU = function (address) { + address = address | 0; + this.wait.SRAMAccessCPU(); + return ((this.saves.readSRAM(address & 0xFFFE) | 0) * 0x101) | 0; + } + GameBoyAdvanceMemory.prototype.readSRAM32 = function (address) { + address = address | 0; + this.wait.SRAMAccess(); + return ((this.saves.readSRAM(address & 0xFFFC) | 0) * 0x1010101) | 0; + } + GameBoyAdvanceMemory.prototype.readSRAM32CPU = function (address) { + address = address | 0; + this.wait.SRAMAccessCPU(); + return ((this.saves.readSRAM(address & 0xFFFC) | 0) * 0x1010101) | 0; + } +} +GameBoyAdvanceMemory.prototype.readUnused8 = function (address) { + address = address | 0; + this.wait.singleClock(); + return this.readUnused8CPUBase(address | 0) | 0; +} +GameBoyAdvanceMemory.prototype.readUnused8CPUBase = function (address) { + address = address | 0; + return (this.cpu.getCurrentFetchValue() >> ((address & 0x3) << 3)) & 0xFF; +} +GameBoyAdvanceMemory.prototype.readUnused16 = function (address) { + address = address | 0; + this.wait.singleClock(); + return this.readUnused16CPUBase(address | 0) | 0; +} +GameBoyAdvanceMemory.prototype.readUnused16CPU = function (address) { + address = address | 0; + this.IOCore.updateCoreSingle(); + return this.readUnused16CPUBase(address | 0) | 0; +} +GameBoyAdvanceMemory.prototype.readUnused16CPUBase = function (address) { + address = address | 0; + return (this.cpu.getCurrentFetchValue() >> ((address & 0x2) << 3)) & 0xFFFF; +} +GameBoyAdvanceMemory.prototype.readUnused16DMA = function (address) { + address = address | 0; + this.wait.singleClock(); + return this.readUnused16DMABase(address | 0) | 0; +} +GameBoyAdvanceMemory.prototype.readUnused16DMABase = function (address) { + address = address | 0; + return (this.dma.getCurrentFetchValue() >> ((address & 0x2) << 3)) & 0xFFFF; +} +GameBoyAdvanceMemory.prototype.readUnused16MultiBase = function (address) { + address = address | 0; + return (this.readUnused32MultiBase() >> ((address & 0x2) << 3)) & 0xFFFF; +} +GameBoyAdvanceMemory.prototype.readUnused32 = function () { + this.wait.singleClock(); + return this.cpu.getCurrentFetchValue() | 0; +} +GameBoyAdvanceMemory.prototype.readUnused32CPU = function () { + this.IOCore.updateCoreSingle(); + return this.cpu.getCurrentFetchValue() | 0; +} +GameBoyAdvanceMemory.prototype.readUnused32DMA = function () { + this.wait.singleClock(); + return this.dma.getCurrentFetchValue() | 0; +} +GameBoyAdvanceMemory.prototype.readUnused32MultiBase = function () { + return this.IOCore.getCurrentFetchValue() | 0; +} +GameBoyAdvanceMemory.prototype.loadBIOS = function () { + var allowInit = 1; + //Ensure BIOS is of correct length: + if ((this.IOCore.BIOS.length | 0) == 0x4000) { + //this.IOCore.BIOSFound = true; + for (var index = 0; (index | 0) < 0x4000; index = ((index | 0) + 1) | 0) { + this.BIOS[index & 0x3FFF] = this.IOCore.BIOS[index & 0x3FFF] & 0xFF; + } + } + else { + //this.IOCore.BIOSFound = false; + this.IOCore.SKIPBoot = true; + //Kill init, rather than allow HLE for now: + allowInit = 0; + } + return allowInit | 0; +} +function generateMemoryTopLevelDispatch() { + //Generic memory read dispatch generator: + function compileMemoryReadDispatch(readUnused, readExternalWRAM, readInternalWRAM, + readIODispatch, readVRAM, readROM, readROM2, readSRAM, readBIOS) { + var code = "address = address | 0;var data = 0;switch (address >> 24) {"; + /* + Decoder for the nibble at bits 24-27 + (Top 4 bits of the address falls through to default (unused), + so the next nibble down is used for dispatch.): + */ + /* + BIOS Area (00000000-00003FFF) + Unused (00004000-01FFFFFF) + */ + code += "case 0:{data = this." + readBIOS + "(address | 0) | 0;break};"; + /* + Unused (00004000-01FFFFFF) + */ + /* + WRAM - On-board Work RAM (02000000-0203FFFF) + Unused (02040000-02FFFFFF) + */ + if (readExternalWRAM.slice(0, 10) != "readUnused") { + code += "case 0x2:"; + if (readExternalWRAM.slice(0, 12) != "readInternal") { + code += "{data = this." + readExternalWRAM + "(address | 0) | 0;break};"; + } + } + /* + WRAM - In-Chip Work RAM (03000000-03007FFF) + Unused (03008000-03FFFFFF) + */ + if (readInternalWRAM.slice(0, 10) != "readUnused") { + code += "case 0x3:{data = this." + readInternalWRAM + "(address | 0) | 0;break};"; + } + /* + I/O Registers (04000000-040003FE) + Unused (04000400-04FFFFFF) + */ + code += "case 0x4:{data = this." + readIODispatch + "(address | 0) | 0;break};"; + /* + BG/OBJ Palette RAM (05000000-050003FF) + Unused (05000400-05FFFFFF) + */ + code += "case 0x5:"; + /* + VRAM - Video RAM (06000000-06017FFF) + Unused (06018000-06FFFFFF) + */ + code += "case 0x6:"; + /* + OAM - OBJ Attributes (07000000-070003FF) + Unused (07000400-07FFFFFF) + */ + code += "case 0x7:{data = this." + readVRAM + "(address | 0) | 0;break};"; + /* + Game Pak ROM (max 16MB) - Wait State 0 (08000000-08FFFFFF) + */ + code += "case 0x8:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 0 (09000000-09FFFFFF) + */ + code += "case 0x9:"; + /* + Game Pak ROM (max 16MB) - Wait State 1 (0A000000-0AFFFFFF) + */ + code += "case 0xA:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 1 (0B000000-0BFFFFFF) + */ + code += "case 0xB:{data = this." + readROM + "(address | 0) | 0;break};"; + /* + Game Pak ROM (max 16MB) - Wait State 2 (0C000000-0CFFFFFF) + */ + code += "case 0xC:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 2 (0D000000-0DFFFFFF) + */ + code += "case 0xD:{data = this." + readROM2 + "(address | 0) | 0;break};"; + /* + Game Pak SRAM (max 64 KBytes) - 8bit Bus width (0E000000-0E00FFFF) + */ + code += "case 0xE:"; + /* + Game Pak SRAM (max 64 KBytes) - 8bit Bus width (0E000000-0E00FFFF) + Mirrored up to 0FFFFFFF + */ + code += "case 0xF:{data = this." + readSRAM + "(address | 0) | 0;break};"; + /* + Unused (10000000-FFFFFFFF) + */ + code += "default:{data = this." + readUnused + "(" + ((readUnused.slice(0, 12) == "readUnused32") ? "" : "address | 0") + ") | 0};"; + //Generate the function: + code += "}return data | 0;"; + return Function("address", code); + } + //Optimized for DMA 0: + function compileMemoryDMA0ReadDispatch(readUnused, readExternalWRAM, readInternalWRAM, + readIODispatch, readVRAM, readBIOS) { + var code = "address = address | 0;var data = 0;switch (address >> 24) {"; + /* + Decoder for the nibble at bits 24-27 + (Top 4 bits of the address falls through to default (unused), + so the next nibble down is used for dispatch.): + */ + /* + BIOS Area (00000000-00003FFF) + Unused (00004000-01FFFFFF) + */ + code += "case 0:{data = this." + readBIOS + "(address | 0) | 0;break};"; + /* + Unused (00004000-01FFFFFF) + */ + /* + WRAM - On-board Work RAM (02000000-0203FFFF) + Unused (02040000-02FFFFFF) + */ + if (readExternalWRAM.slice(0, 10) != "readUnused") { + code += "case 0x2:"; + if (readExternalWRAM.slice(0, 12) != "readInternal") { + code += "{data = this." + readExternalWRAM + "(address | 0) | 0;break};"; + } + } + /* + WRAM - In-Chip Work RAM (03000000-03007FFF) + Unused (03008000-03FFFFFF) + */ + if (readInternalWRAM.slice(0, 10) != "readUnused") { + code += "case 0x3:{data = this." + readInternalWRAM + "(address | 0) | 0;break};"; + } + /* + I/O Registers (04000000-040003FE) + Unused (04000400-04FFFFFF) + */ + code += "case 0x4:{data = this." + readIODispatch + "(address | 0) | 0;break};"; + /* + BG/OBJ Palette RAM (05000000-050003FF) + Unused (05000400-05FFFFFF) + */ + code += "case 0x5:"; + /* + VRAM - Video RAM (06000000-06017FFF) + Unused (06018000-06FFFFFF) + */ + code += "case 0x6:"; + /* + OAM - OBJ Attributes (07000000-070003FF) + Unused (07000400-07FFFFFF) + */ + code += "case 0x7:{data = this." + readVRAM + "(address | 0) | 0;break};"; + /* + Unused, DMA 0 cannot read past 07FFFFFF: + */ + code += "default:{data = this." + readUnused + "(" + ((readUnused.slice(0, 12) == "readUnused32") ? "" : "address | 0") + ") | 0};"; + //Generate the function: + code += "}return data | 0;"; + return Function("address", code); + } + //Optimized for DMA 1-3: + function compileMemoryDMAReadDispatch(readUnused, readExternalWRAM, readInternalWRAM, + readIODispatch, readVRAM, readROM, readROM2, readBIOS) { + var code = "address = address | 0;var data = 0;switch (address >> 24) {"; + /* + Decoder for the nibble at bits 24-27 + (Top 4 bits of the address falls through to default (unused), + so the next nibble down is used for dispatch.): + */ + /* + BIOS Area (00000000-00003FFF) + Unused (00004000-01FFFFFF) + */ + code += "case 0:{data = this." + readBIOS + "(address | 0) | 0;break};"; + /* + Unused (00004000-01FFFFFF) + */ + /* + WRAM - On-board Work RAM (02000000-0203FFFF) + Unused (02040000-02FFFFFF) + */ + if (readExternalWRAM.slice(0, 10) != "readUnused") { + code += "case 0x2:"; + if (readExternalWRAM.slice(0, 12) != "readInternal") { + code += "{data = this." + readExternalWRAM + "(address | 0) | 0;break};"; + } + } + /* + WRAM - In-Chip Work RAM (03000000-03007FFF) + Unused (03008000-03FFFFFF) + */ + if (readInternalWRAM.slice(0, 10) != "readUnused") { + code += "case 0x3:{data = this." + readInternalWRAM + "(address | 0) | 0;break};"; + } + /* + I/O Registers (04000000-040003FE) + Unused (04000400-04FFFFFF) + */ + code += "case 0x4:{data = this." + readIODispatch + "(address | 0) | 0;break};"; + /* + BG/OBJ Palette RAM (05000000-050003FF) + Unused (05000400-05FFFFFF) + */ + code += "case 0x5:"; + /* + VRAM - Video RAM (06000000-06017FFF) + Unused (06018000-06FFFFFF) + */ + code += "case 0x6:"; + /* + OAM - OBJ Attributes (07000000-070003FF) + Unused (07000400-07FFFFFF) + */ + code += "case 0x7:{data = this." + readVRAM + "(address | 0) | 0;break};"; + /* + Game Pak ROM (max 16MB) - Wait State 0 (08000000-08FFFFFF) + */ + code += "case 0x8:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 0 (09000000-09FFFFFF) + */ + code += "case 0x9:"; + /* + Game Pak ROM (max 16MB) - Wait State 1 (0A000000-0AFFFFFF) + */ + code += "case 0xA:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 1 (0B000000-0BFFFFFF) + */ + code += "case 0xB:{data = this." + readROM + "(address | 0) | 0;break};"; + /* + Game Pak ROM (max 16MB) - Wait State 2 (0C000000-0CFFFFFF) + */ + code += "case 0xC:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 2 (0D000000-0DFFFFFF) + */ + code += "case 0xD:{data = this." + readROM2 + "(address | 0) | 0;break};"; + /* + Unused, DMA 1-3 cannot read past 0DFFFFFF: + */ + code += "default:{data = this." + readUnused + "(" + ((readUnused.slice(0, 12) == "readUnused32") ? "" : "address | 0") + ") | 0};"; + //Generate the function: + code += "}return data | 0;"; + return Function("address", code); + } + //Graphics should not be handled as often for this one: + function compileMemoryWriteDispatch(writeUnused, writeExternalWRAM, writeInternalWRAM, + writeIODispatch, writeVRAM, writeROM, writeSRAM) { + var code = "address = address | 0;data = data | 0;switch (address >> 24) {"; + /* + Decoder for the nibble at bits 24-27 + (Top 4 bits of the address falls through to default (unused), + so the next nibble down is used for dispatch.): + */ + /* + BIOS Area (00000000-00003FFF) + Unused (00004000-01FFFFFF) + */ + /* + Unused (00004000-01FFFFFF) + */ + /* + WRAM - On-board Work RAM (02000000-0203FFFF) + Unused (02040000-02FFFFFF) + */ + if (writeExternalWRAM != "writeUnused") { + code += "case 0x2:"; + if (writeExternalWRAM.slice(0, 13) != "writeInternal") { + code += "{this." + writeExternalWRAM + "(address | 0, data | 0);break};"; + } + } + /* + WRAM - In-Chip Work RAM (03000000-03007FFF) + Unused (03008000-03FFFFFF) + */ + if (writeInternalWRAM != "writeUnused") { + code += "case 0x3:{this." + writeInternalWRAM + "(address | 0, data | 0);break};"; + } + /* + I/O Registers (04000000-040003FE) + Unused (04000400-04FFFFFF) + */ + code += "case 0x4:{this." + writeIODispatch + "(address | 0, data | 0);break};"; + /* + BG/OBJ Palette RAM (05000000-050003FF) + Unused (05000400-05FFFFFF) + */ + code += "case 0x5:"; + /* + VRAM - Video RAM (06000000-06017FFF) + Unused (06018000-06FFFFFF) + */ + code += "case 0x6:"; + /* + OAM - OBJ Attributes (07000000-070003FF) + Unused (07000400-07FFFFFF) + */ + code += "case 0x7:{this." + writeVRAM + "(address | 0, data | 0);break};"; + /* + Game Pak ROM (max 16MB) - Wait State 0 (08000000-08FFFFFF) + */ + code += "case 0x8:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 0 (09000000-09FFFFFF) + */ + code += "case 0x9:"; + /* + Game Pak ROM (max 16MB) - Wait State 1 (0A000000-0AFFFFFF) + */ + code += "case 0xA:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 1 (0B000000-0BFFFFFF) + */ + code += "case 0xB:"; + /* + Game Pak ROM (max 16MB) - Wait State 2 (0C000000-0CFFFFFF) + */ + code += "case 0xC:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 2 (0D000000-0DFFFFFF) + */ + code += "case 0xD:{this." + writeROM + "(address | 0, data | 0);break};"; + /* + Game Pak SRAM (max 64 KBytes) - 8bit Bus width (0E000000-0E00FFFF) + */ + code += "case 0xE:"; + /* + Game Pak SRAM (max 64 KBytes) - 8bit Bus width (0E000000-0E00FFFF) + Mirrored up to 0FFFFFFF + */ + code += "case 0xF:{this." + writeSRAM + "(address | 0, data | 0);break};"; + /* + Unused (10000000-FFFFFFFF) + */ + code += "default:{this." + writeUnused + "()}"; + //Generate the function: + code += "}"; + return Function("address", "data", code); + } + //Graphics calls slightly faster in this one, at the expense of other calls: + function compileMemoryWriteDispatch2(writeUnused, writeExternalWRAM, writeInternalWRAM, + writeIODispatch, writePalette, writeVRAM, writeOAM, writeROM, writeSRAM) { + var code = "address = address | 0;data = data | 0;switch (address >> 24) {"; + /* + Decoder for the nibble at bits 24-27 + (Top 4 bits of the address falls through to default (unused), + so the next nibble down is used for dispatch.): + */ + /* + BIOS Area (00000000-00003FFF) + Unused (00004000-01FFFFFF) + */ + /* + Unused (00004000-01FFFFFF) + */ + /* + WRAM - On-board Work RAM (02000000-0203FFFF) + Unused (02040000-02FFFFFF) + */ + if (writeExternalWRAM != "writeUnused") { + code += "case 0x2:"; + if (writeExternalWRAM.slice(0, 13) != "writeInternal") { + code += "{this." + writeExternalWRAM + "(address | 0, data | 0);break};"; + } + } + /* + WRAM - In-Chip Work RAM (03000000-03007FFF) + Unused (03008000-03FFFFFF) + */ + if (writeInternalWRAM != "writeUnused") { + code += "case 0x3:{this." + writeInternalWRAM + "(address | 0, data | 0);break};"; + } + /* + I/O Registers (04000000-040003FE) + Unused (04000400-04FFFFFF) + */ + code += "case 0x4:{this." + writeIODispatch + "(address | 0, data | 0);break};"; + /* + BG/OBJ Palette RAM (05000000-050003FF) + Unused (05000400-05FFFFFF) + */ + code += "case 0x5:{this." + writePalette + "(address | 0, data | 0);break};"; + /* + VRAM - Video RAM (06000000-06017FFF) + Unused (06018000-06FFFFFF) + */ + code += "case 0x6:{this." + writeVRAM + "(address | 0, data | 0);break};"; + /* + OAM - OBJ Attributes (07000000-070003FF) + Unused (07000400-07FFFFFF) + */ + code += "case 0x7:{this." + writeOAM + "(address | 0, data | 0);break};"; + /* + Game Pak ROM (max 16MB) - Wait State 0 (08000000-08FFFFFF) + */ + code += "case 0x8:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 0 (09000000-09FFFFFF) + */ + code += "case 0x9:"; + /* + Game Pak ROM (max 16MB) - Wait State 1 (0A000000-0AFFFFFF) + */ + code += "case 0xA:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 1 (0B000000-0BFFFFFF) + */ + code += "case 0xB:"; + /* + Game Pak ROM (max 16MB) - Wait State 2 (0C000000-0CFFFFFF) + */ + code += "case 0xC:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 2 (0D000000-0DFFFFFF) + */ + code += "case 0xD:{this." + writeROM + "(address | 0, data | 0);break};"; + /* + Game Pak SRAM (max 64 KBytes) - 8bit Bus width (0E000000-0E00FFFF) + */ + code += "case 0xE:"; + /* + Game Pak SRAM (max 64 KBytes) - 8bit Bus width (0E000000-0E00FFFF) + Mirrored up to 0FFFFFFF + */ + code += "case 0xF:{this." + writeSRAM + "(address | 0, data | 0);break};"; + /* + Unused (10000000-FFFFFFFF) + */ + code += "default:{this." + writeUnused + "()}"; + //Generate the function: + code += "}"; + return Function("address", "data", code); + } + //Optimized for DMA 0-2: + function compileMemoryDMAWriteDispatch(writeUnused, writeExternalWRAM, writeInternalWRAM, + writeIODispatch, writePalette, writeVRAM, writeOAM) { + var code = "address = address | 0;data = data | 0;switch (address >> 24) {"; + /* + Decoder for the nibble at bits 24-27 + (Top 4 bits of the address falls through to default (unused), + so the next nibble down is used for dispatch.): + */ + /* + BIOS Area (00000000-00003FFF) + Unused (00004000-01FFFFFF) + */ + /* + Unused (00004000-01FFFFFF) + */ + /* + WRAM - On-board Work RAM (02000000-0203FFFF) + Unused (02040000-02FFFFFF) + */ + if (writeExternalWRAM != "writeUnused") { + code += "case 0x2:"; + if (writeExternalWRAM.slice(0, 13) != "writeInternal") { + code += "{this." + writeExternalWRAM + "(address | 0, data | 0);break};"; + } + } + /* + WRAM - In-Chip Work RAM (03000000-03007FFF) + Unused (03008000-03FFFFFF) + */ + if (writeInternalWRAM != "writeUnused") { + code += "case 0x3:{this." + writeInternalWRAM + "(address | 0, data | 0);break};"; + } + /* + I/O Registers (04000000-040003FE) + Unused (04000400-04FFFFFF) + */ + code += "case 0x4:{this." + writeIODispatch + "(address | 0, data | 0);break};"; + /* + BG/OBJ Palette RAM (05000000-050003FF) + Unused (05000400-05FFFFFF) + */ + code += "case 0x5:{this." + writePalette + "(address | 0, data | 0);break};"; + /* + VRAM - Video RAM (06000000-06017FFF) + Unused (06018000-06FFFFFF) + */ + code += "case 0x6:{this." + writeVRAM + "(address | 0, data | 0);break};"; + /* + OAM - OBJ Attributes (07000000-070003FF) + Unused (07000400-07FFFFFF) + */ + code += "case 0x7:{this." + writeOAM + "(address | 0, data | 0);break};"; + /* + Unused, DMA 0-2 cannot write past 07FFFFFF: + */ + code += "default:{this." + writeUnused + "()}"; + //Generate the function: + code += "}"; + return Function("address", "data", code); + } + //Optimized for DMA 3: + function compileMemoryDMA3WriteDispatch(writeUnused, writeExternalWRAM, writeInternalWRAM, + writeIODispatch, writePalette, writeVRAM, writeOAM, writeROM) { + var code = "address = address | 0;data = data | 0;switch (address >> 24) {"; + /* + Decoder for the nibble at bits 24-27 + (Top 4 bits of the address falls through to default (unused), + so the next nibble down is used for dispatch.): + */ + /* + BIOS Area (00000000-00003FFF) + Unused (00004000-01FFFFFF) + */ + /* + Unused (00004000-01FFFFFF) + */ + /* + WRAM - On-board Work RAM (02000000-0203FFFF) + Unused (02040000-02FFFFFF) + */ + if (writeExternalWRAM != "writeUnused") { + code += "case 0x2:"; + if (writeExternalWRAM.slice(0, 13) != "writeInternal") { + code += "{this." + writeExternalWRAM + "(address | 0, data | 0);break};"; + } + } + /* + WRAM - In-Chip Work RAM (03000000-03007FFF) + Unused (03008000-03FFFFFF) + */ + if (writeInternalWRAM != "writeUnused") { + code += "case 0x3:{this." + writeInternalWRAM + "(address | 0, data | 0);break};"; + } + /* + I/O Registers (04000000-040003FE) + Unused (04000400-04FFFFFF) + */ + code += "case 0x4:{this." + writeIODispatch + "(address | 0, data | 0);break};"; + /* + BG/OBJ Palette RAM (05000000-050003FF) + Unused (05000400-05FFFFFF) + */ + code += "case 0x5:{this." + writePalette + "(address | 0, data | 0);break};"; + /* + VRAM - Video RAM (06000000-06017FFF) + Unused (06018000-06FFFFFF) + */ + code += "case 0x6:{this." + writeVRAM + "(address | 0, data | 0);break};"; + /* + OAM - OBJ Attributes (07000000-070003FF) + Unused (07000400-07FFFFFF) + */ + code += "case 0x7:{this." + writeOAM + "(address | 0, data | 0);break};"; + /* + Game Pak ROM (max 16MB) - Wait State 0 (08000000-08FFFFFF) + */ + code += "case 0x8:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 0 (09000000-09FFFFFF) + */ + code += "case 0x9:"; + /* + Game Pak ROM (max 16MB) - Wait State 1 (0A000000-0AFFFFFF) + */ + code += "case 0xA:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 1 (0B000000-0BFFFFFF) + */ + code += "case 0xB:"; + /* + Game Pak ROM (max 16MB) - Wait State 2 (0C000000-0CFFFFFF) + */ + code += "case 0xC:"; + /* + Game Pak ROM/FlashROM (max 16MB) - Wait State 2 (0D000000-0DFFFFFF) + */ + code += "case 0xD:{this." + writeROM + "(address | 0, data | 0);break};"; + /* + Unused, DMA 3 cannot write past 0DFFFFFF: + */ + code += "default:{this." + writeUnused + "()}"; + //Generate the function: + code += "}"; + return Function("address", "data", code); + } + //Generic 8-Bit Read Dispatch: + GameBoyAdvanceMemory.prototype.memoryRead8Generated = [ + compileMemoryReadDispatch( + "readUnused8", + "readInternalWRAM8", + "readInternalWRAM8", + "readIODispatch8", + "readVRAM8Preliminary", + "readROM8", + "readROM28", + "readSRAM8", + "readBIOS8" + ), + compileMemoryReadDispatch( + "readUnused8", + "readExternalWRAM8", + "readInternalWRAM8", + "readIODispatch8", + "readVRAM8Preliminary", + "readROM8", + "readROM28", + "readSRAM8", + "readBIOS8" + ), + compileMemoryReadDispatch( + "readUnused8", + "readUnused8", + "readUnused8", + "readIODispatch8", + "readVRAM8Preliminary", + "readROM8", + "readROM28", + "readSRAM8", + "readBIOS8" + ) + ]; + //Generic 8-Bit Write Dispatch: + GameBoyAdvanceMemory.prototype.memoryWrite8Generated = [ + compileMemoryWriteDispatch( + "writeUnused", + "writeInternalWRAM8", + "writeInternalWRAM8", + "writeIODispatch8", + "writeVRAM8Preliminary", + "writeROM8", + "writeSRAM8" + ), + compileMemoryWriteDispatch( + "writeUnused", + "writeExternalWRAM8", + "writeInternalWRAM8", + "writeIODispatch8", + "writeVRAM8Preliminary", + "writeROM8", + "writeSRAM8" + ), + compileMemoryWriteDispatch( + "writeUnused", + "writeUnused", + "writeUnused", + "writeIODispatch8", + "writeVRAM8Preliminary", + "writeROM8", + "writeSRAM8" + ) + ]; + //Generic 16-Bit Read Dispatch: + GameBoyAdvanceMemory.prototype.memoryRead16Generated = [ + compileMemoryReadDispatch( + "readUnused16", + "readInternalWRAM16", + "readInternalWRAM16", + "readIODispatch16", + "readVRAM16Preliminary", + "readROM16", + "readROM216", + "readSRAM16", + "readBIOS16" + ), + compileMemoryReadDispatch( + "readUnused16", + "readExternalWRAM16", + "readInternalWRAM16", + "readIODispatch16", + "readVRAM16Preliminary", + "readROM16", + "readROM216", + "readSRAM16", + "readBIOS16" + ), + compileMemoryReadDispatch( + "readUnused16", + "readUnused16", + "readUnused16", + "readIODispatch16", + "readVRAM16Preliminary", + "readROM16", + "readROM216", + "readSRAM16", + "readBIOS16" + ) + ]; + //DMA 0 Optimized 16-Bit Read Dispatch: + GameBoyAdvanceMemory.prototype.memoryReadDMA16Generated = [ + compileMemoryDMA0ReadDispatch( + "readUnused16DMA", + "readInternalWRAM16", + "readInternalWRAM16", + "readIODispatch16", + "readVRAM16Preliminary", + "readBIOS16DMA" + ), + compileMemoryDMA0ReadDispatch( + "readUnused16DMA", + "readExternalWRAM16", + "readInternalWRAM16", + "readIODispatch16", + "readVRAM16Preliminary", + "readBIOS16DMA" + ), + compileMemoryDMA0ReadDispatch( + "readUnused16DMA", + "readUnused16DMA", + "readUnused16DMA", + "readIODispatch16", + "readVRAM16Preliminary", + "readBIOS16DMA" + ) + ]; + //DMA 1-3 Optimized 16-Bit Read Dispatch: + GameBoyAdvanceMemory.prototype.memoryReadDMA16FullGenerated = [ + compileMemoryDMAReadDispatch( + "readUnused16DMA", + "readInternalWRAM16", + "readInternalWRAM16", + "readIODispatch16", + "readVRAM16Preliminary", + "readROM16", + "readROM216", + "readBIOS16DMA" + ), + compileMemoryDMAReadDispatch( + "readUnused16DMA", + "readExternalWRAM16", + "readInternalWRAM16", + "readIODispatch16", + "readVRAM16Preliminary", + "readROM16", + "readROM216", + "readBIOS16DMA" + ), + compileMemoryDMAReadDispatch( + "readUnused16DMA", + "readUnused16DMA", + "readUnused16DMA", + "readIODispatch16", + "readVRAM16Preliminary", + "readROM16", + "readROM216", + "readBIOS16DMA" + ) + ]; + //Generic 16-Bit Instruction Read Dispatch: + GameBoyAdvanceMemory.prototype.memoryReadCPU16Generated = [ + compileMemoryReadDispatch( + "readUnused16CPU", + "readInternalWRAM16CPU", + "readInternalWRAM16CPU", + "readIODispatch16CPU", + "readVRAM16CPUPreliminary", + "readROM16CPU", + "readROM216CPU", + "readSRAM16CPU", + "readBIOS16CPU" + ), + compileMemoryReadDispatch( + "readUnused16CPU", + "readExternalWRAM16CPU", + "readInternalWRAM16CPU", + "readIODispatch16CPU", + "readVRAM16CPUPreliminary", + "readROM16CPU", + "readROM216CPU", + "readSRAM16CPU", + "readBIOS16CPU" + ), + compileMemoryReadDispatch( + "readUnused16CPU", + "readUnused16CPU", + "readUnused16CPU", + "readIODispatch16CPU", + "readVRAM16CPUPreliminary", + "readROM16CPU", + "readROM216CPU", + "readSRAM16CPU", + "readBIOS16CPU" + ) + ]; + //Generic 16-Bit Write Dispatch: + GameBoyAdvanceMemory.prototype.memoryWrite16Generated = [ + compileMemoryWriteDispatch2( + "writeUnused", + "writeInternalWRAM16", + "writeInternalWRAM16", + "writeIODispatch16", + "writePalette16", + "writeVRAM16", + "writeOBJ16", + "writeROM16", + "writeSRAM16" + ), + compileMemoryWriteDispatch2( + "writeUnused", + "writeExternalWRAM16", + "writeInternalWRAM16", + "writeIODispatch16", + "writePalette16", + "writeVRAM16", + "writeOBJ16", + "writeROM16", + "writeSRAM16" + ), + compileMemoryWriteDispatch2( + "writeUnused", + "writeUnused", + "writeUnused", + "writeIODispatch16", + "writePalette16", + "writeVRAM16", + "writeOBJ16", + "writeROM16", + "writeSRAM16" + ) + ]; + //DMA 0-2 Optimized 16-Bit Write Dispatch: + GameBoyAdvanceMemory.prototype.memoryWriteDMA16Generated = [ + compileMemoryDMAWriteDispatch( + "writeUnused", + "writeInternalWRAM16", + "writeInternalWRAM16", + "writeIODispatch16", + "writePalette16", + "writeVRAM16", + "writeOBJ16" + ), + compileMemoryDMAWriteDispatch( + "writeUnused", + "writeExternalWRAM16", + "writeInternalWRAM16", + "writeIODispatch16", + "writePalette16", + "writeVRAM16", + "writeOBJ16" + ), + compileMemoryDMAWriteDispatch( + "writeUnused", + "writeUnused", + "writeUnused", + "writeIODispatch16", + "writePalette16", + "writeVRAM16", + "writeOBJ16" + ) + ]; + //DMA 3 Optimized 16-Bit Write Dispatch: + GameBoyAdvanceMemory.prototype.memoryWriteDMA16FullGenerated = [ + compileMemoryDMA3WriteDispatch( + "writeUnused", + "writeInternalWRAM16", + "writeInternalWRAM16", + "writeIODispatch16", + "writePalette16", + "writeVRAM16", + "writeOBJ16", + "writeROM16DMA" + ), + compileMemoryDMA3WriteDispatch( + "writeUnused", + "writeExternalWRAM16", + "writeInternalWRAM16", + "writeIODispatch16", + "writePalette16", + "writeVRAM16", + "writeOBJ16", + "writeROM16DMA" + ), + compileMemoryDMA3WriteDispatch( + "writeUnused", + "writeUnused", + "writeUnused", + "writeIODispatch16", + "writePalette16", + "writeVRAM16", + "writeOBJ16", + "writeROM16DMA" + ) + ]; + //Generic 32-Bit Read Dispatch: + GameBoyAdvanceMemory.prototype.memoryRead32Generated = [ + compileMemoryReadDispatch( + "readUnused32", + "readInternalWRAM32", + "readInternalWRAM32", + "readIODispatch32", + "readVRAM32Preliminary", + "readROM32", + "readROM232", + "readSRAM32", + "readBIOS32" + ), + compileMemoryReadDispatch( + "readUnused32", + "readExternalWRAM32", + "readInternalWRAM32", + "readIODispatch32", + "readVRAM32Preliminary", + "readROM32", + "readROM232", + "readSRAM32", + "readBIOS32" + ), + compileMemoryReadDispatch( + "readUnused32", + "readUnused32", + "readUnused32", + "readIODispatch32", + "readVRAM32Preliminary", + "readROM32", + "readROM232", + "readSRAM32", + "readBIOS32" + ) + ]; + //DMA 0 Optimized 32-Bit Read Dispatch: + GameBoyAdvanceMemory.prototype.memoryReadDMA32Generated = [ + compileMemoryDMA0ReadDispatch( + "readUnused32DMA", + "readInternalWRAM32", + "readInternalWRAM32", + "readIODispatch32", + "readVRAM32Preliminary", + "readBIOS32DMA" + ), + compileMemoryDMA0ReadDispatch( + "readUnused32DMA", + "readExternalWRAM32", + "readInternalWRAM32", + "readIODispatch32", + "readVRAM32Preliminary", + "readBIOS32DMA" + ), + compileMemoryDMA0ReadDispatch( + "readUnused32DMA", + "readUnused32DMA", + "readUnused32DMA", + "readIODispatch32", + "readVRAM32Preliminary", + "readBIOS32DMA" + ) + ]; + //DMA 1-3 Optimized 32-Bit Read Dispatch: + GameBoyAdvanceMemory.prototype.memoryReadDMA32FullGenerated = [ + compileMemoryDMAReadDispatch( + "readUnused32DMA", + "readInternalWRAM32", + "readInternalWRAM32", + "readIODispatch32", + "readVRAM32Preliminary", + "readROM32", + "readROM232", + "readBIOS32DMA" + ), + compileMemoryDMAReadDispatch( + "readUnused32DMA", + "readExternalWRAM32", + "readInternalWRAM32", + "readIODispatch32", + "readVRAM32Preliminary", + "readROM32", + "readROM232", + "readBIOS32DMA" + ), + compileMemoryDMAReadDispatch( + "readUnused32DMA", + "readUnused32DMA", + "readUnused32DMA", + "readIODispatch32", + "readVRAM32Preliminary", + "readROM32", + "readROM232", + "readBIOS32DMA" + ) + ]; + //Generic 32-Bit Instruction Read Dispatch: + GameBoyAdvanceMemory.prototype.memoryReadCPU32Generated = [ + compileMemoryReadDispatch( + "readUnused32CPU", + "readInternalWRAM32CPU", + "readInternalWRAM32CPU", + "readIODispatch32CPU", + "readVRAM32CPUPreliminary", + "readROM32CPU", + "readROM232CPU", + "readSRAM32CPU", + "readBIOS32CPU" + ), + compileMemoryReadDispatch( + "readUnused32CPU", + "readExternalWRAM32CPU", + "readInternalWRAM32CPU", + "readIODispatch32CPU", + "readVRAM32CPUPreliminary", + "readROM32CPU", + "readROM232CPU", + "readSRAM32CPU", + "readBIOS32CPU" + ), + compileMemoryReadDispatch( + "readUnused32CPU", + "readUnused32CPU", + "readUnused32CPU", + "readIODispatch32CPU", + "readVRAM32CPUPreliminary", + "readROM32CPU", + "readROM232CPU", + "readSRAM32CPU", + "readBIOS32CPU" + ) + ]; + //Generic 32-Bit Write Dispatch: + GameBoyAdvanceMemory.prototype.memoryWrite32Generated = [ + compileMemoryWriteDispatch2( + "writeUnused", + "writeInternalWRAM32", + "writeInternalWRAM32", + "writeIODispatch32", + "writePalette32", + "writeVRAM32", + "writeOBJ32", + "writeROM32", + "writeSRAM32" + ), + compileMemoryWriteDispatch2( + "writeUnused", + "writeExternalWRAM32", + "writeInternalWRAM32", + "writeIODispatch32", + "writePalette32", + "writeVRAM32", + "writeOBJ32", + "writeROM32", + "writeSRAM32" + ), + compileMemoryWriteDispatch2( + "writeUnused", + "writeUnused", + "writeUnused", + "writeIODispatch32", + "writePalette32", + "writeVRAM32", + "writeOBJ32", + "writeROM32", + "writeSRAM32" + ) + ]; + //DMA 0-2 Optimized 32-Bit Write Dispatch: + GameBoyAdvanceMemory.prototype.memoryWriteDMA32Generated = [ + compileMemoryDMAWriteDispatch( + "writeUnused", + "writeInternalWRAM32", + "writeInternalWRAM32", + "writeIODispatch32", + "writePalette32", + "writeVRAM32", + "writeOBJ32" + ), + compileMemoryDMAWriteDispatch( + "writeUnused", + "writeExternalWRAM32", + "writeInternalWRAM32", + "writeIODispatch32", + "writePalette32", + "writeVRAM32", + "writeOBJ32" + ), + compileMemoryDMAWriteDispatch( + "writeUnused", + "writeUnused", + "writeUnused", + "writeIODispatch32", + "writePalette32", + "writeVRAM32", + "writeOBJ32" + ) + ]; + //DMA 3 Optimized 32-Bit Write Dispatch: + GameBoyAdvanceMemory.prototype.memoryWriteDMA32FullGenerated = [ + compileMemoryDMA3WriteDispatch( + "writeUnused", + "writeInternalWRAM32", + "writeInternalWRAM32", + "writeIODispatch32", + "writePalette32", + "writeVRAM32", + "writeOBJ32", + "writeROM32" + ), + compileMemoryDMA3WriteDispatch( + "writeUnused", + "writeExternalWRAM32", + "writeInternalWRAM32", + "writeIODispatch32", + "writePalette32", + "writeVRAM32", + "writeOBJ32", + "writeROM32" + ), + compileMemoryDMA3WriteDispatch( + "writeUnused", + "writeUnused", + "writeUnused", + "writeIODispatch32", + "writePalette32", + "writeVRAM32", + "writeOBJ32", + "writeROM32" + ) + ]; + //Initialize to default memory map: + GameBoyAdvanceMemory.prototype.memoryRead8 = GameBoyAdvanceMemory.prototype.memoryRead8Generated[1]; + GameBoyAdvanceMemory.prototype.memoryWrite8 = GameBoyAdvanceMemory.prototype.memoryWrite8Generated[1]; + GameBoyAdvanceMemory.prototype.memoryRead16 = GameBoyAdvanceMemory.prototype.memoryRead16Generated[1]; + GameBoyAdvanceMemory.prototype.memoryReadDMA16 = GameBoyAdvanceMemory.prototype.memoryReadDMA16Generated[1]; + GameBoyAdvanceMemory.prototype.memoryReadDMAFull16 = GameBoyAdvanceMemory.prototype.memoryReadDMA16FullGenerated[1]; + GameBoyAdvanceMemory.prototype.memoryReadCPU16 = GameBoyAdvanceMemory.prototype.memoryReadCPU16Generated[1]; + GameBoyAdvanceMemory.prototype.memoryWrite16 = GameBoyAdvanceMemory.prototype.memoryWrite16Generated[1]; + GameBoyAdvanceMemory.prototype.memoryWriteDMA16 = GameBoyAdvanceMemory.prototype.memoryWriteDMA16Generated[1]; + GameBoyAdvanceMemory.prototype.memoryWriteDMAFull16 = GameBoyAdvanceMemory.prototype.memoryWriteDMA16FullGenerated[1]; + GameBoyAdvanceMemory.prototype.memoryRead32 = GameBoyAdvanceMemory.prototype.memoryRead32Generated[1]; + GameBoyAdvanceMemory.prototype.memoryReadDMA32 = GameBoyAdvanceMemory.prototype.memoryReadDMA32Generated[1]; + GameBoyAdvanceMemory.prototype.memoryReadDMAFull32 = GameBoyAdvanceMemory.prototype.memoryReadDMA32FullGenerated[1]; + GameBoyAdvanceMemory.prototype.memoryReadCPU32 = GameBoyAdvanceMemory.prototype.memoryReadCPU32Generated[1]; + GameBoyAdvanceMemory.prototype.memoryWrite32 = GameBoyAdvanceMemory.prototype.memoryWrite32Generated[1]; + GameBoyAdvanceMemory.prototype.memoryWriteDMA32 = GameBoyAdvanceMemory.prototype.memoryWriteDMA32Generated[1]; + GameBoyAdvanceMemory.prototype.memoryWriteDMAFull32 = GameBoyAdvanceMemory.prototype.memoryWriteDMA32FullGenerated[1]; +} +generateMemoryTopLevelDispatch(); diff --git a/public/gfiles/gba/IodineGBA/core/RunLoop.js b/public/gfiles/gba/IodineGBA/core/RunLoop.js new file mode 100644 index 00000000..c40c7502 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/RunLoop.js @@ -0,0 +1,428 @@ +"use strict"; +/* + Copyright (C) 2012-2016 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceIO(SKIPBoot, coreExposed, BIOS, ROM) { + //State Machine Tracking: + this.systemStatus = 0; + this.cyclesToIterate = 0; + this.cyclesOveriteratedPreviously = 0; + this.accumulatedClocks = 0; + this.graphicsClocks = 0; + this.timerClocks = 0; + this.serialClocks = 0; + this.nextEventClocks = 0; + //this.BIOSFound = false; + //Do we skip the BIOS Boot Intro? + this.SKIPBoot = !!SKIPBoot; + //References passed to us: + this.coreExposed = coreExposed; + this.BIOS = BIOS; + this.ROM = ROM; + //Build the core object layout: + this.memory = new GameBoyAdvanceMemory(this); + this.dma = new GameBoyAdvanceDMA(this); + this.dmaChannel0 = new GameBoyAdvanceDMA0(this); + this.dmaChannel1 = new GameBoyAdvanceDMA1(this); + this.dmaChannel2 = new GameBoyAdvanceDMA2(this); + this.dmaChannel3 = new GameBoyAdvanceDMA3(this); + this.gfxState = new GameBoyAdvanceGraphics(this); + this.gfxRenderer = new GameBoyAdvanceRendererProxy(this); + this.sound = new GameBoyAdvanceSound(this); + this.timer = new GameBoyAdvanceTimer(this); + this.irq = new GameBoyAdvanceIRQ(this); + this.serial = new GameBoyAdvanceSerial(this); + this.joypad = new GameBoyAdvanceJoyPad(this); + this.cartridge = new GameBoyAdvanceCartridge(this); + this.saves = new GameBoyAdvanceSaves(this); + this.wait = new GameBoyAdvanceWait(this); + this.cpu = new GameBoyAdvanceCPU(this); +} +GameBoyAdvanceIO.prototype.initialize = function () { + var allowInit = 1; + //Now initialize each component: + if ((this.memory.initialize() | 0) == 1) { + //BIOS loaded in OK, so initialize the rest: + this.dma.initialize(); + this.dmaChannel0.initialize(); + this.dmaChannel1.initialize(); + this.dmaChannel2.initialize(); + this.dmaChannel3.initialize(); + this.gfxState.initialize(); + this.gfxRenderer.initialize(); + this.sound.initialize(); + this.timer.initialize(); + this.irq.initialize(); + this.serial.initialize(); + this.joypad.initialize(); + this.cartridge.initialize(); + this.saves.initialize(); + this.wait.initialize(); + this.cpu.initialize(); + } + else { + allowInit = 0; + } + return allowInit | 0; +} +GameBoyAdvanceIO.prototype.assignInstructionCoreReferences = function (ARM, THUMB) { + //Passed here once the CPU component is initialized: + this.ARM = ARM; + this.THUMB = THUMB; +} +GameBoyAdvanceIO.prototype.enter = function (CPUCyclesTotal) { + //Find out how many clocks to iterate through this run: + this.cyclesToIterate = ((CPUCyclesTotal | 0) + (this.cyclesOveriteratedPreviously | 0)) | 0; + //An extra check to make sure we don't do stuff if we did too much last run: + if ((this.cyclesToIterate | 0) > 0) { + //Update our core event prediction: + this.updateCoreEventTime(); + //If clocks remaining, run iterator: + this.run(); + //Spill our core event clocking: + this.updateCoreClocking(); + //Ensure audio buffers at least once per iteration: + this.sound.audioJIT(); + } + //If we clocked just a little too much, subtract the extra from the next run: + this.cyclesOveriteratedPreviously = this.cyclesToIterate | 0; +} +GameBoyAdvanceIO.prototype.run = function () { + //Clock through the state machine: + while (true) { + //Dispatch to optimized run loops: + switch (this.systemStatus & 0x84) { + case 0: + //ARM instruction set: + this.runARM(); + break; + case 0x4: + //THUMB instruction set: + this.runTHUMB(); + break; + default: + //End of stepping: + this.deflagIterationEnd(); + return; + } + } +} +GameBoyAdvanceIO.prototype.runARM = function () { + //Clock through the state machine: + while (true) { + //Handle the current system state selected: + switch (this.systemStatus | 0) { + case 0: //CPU Handle State (Normal ARM) + this.ARM.executeIteration(); + break; + case 1: + case 2: //CPU Handle State (Bubble ARM) + this.ARM.executeBubble(); + this.tickBubble(); + break; + default: //Handle lesser called / End of stepping + //Dispatch on IRQ/DMA/HALT/STOP/END bit flags + switch (this.systemStatus >> 2) { + case 0x2: + //IRQ Handle State: + this.handleIRQARM(); + break; + case 0x4: + case 0x6: + //DMA Handle State + case 0xC: + case 0xE: + //DMA Inside Halt State + this.handleDMA(); + break; + case 0x8: + case 0xA: + //Handle Halt State + this.handleHalt(); + break; + default: //Handle Stop State + //THUMB flagged stuff falls to here intentionally: + //End of Stepping and/or CPU run loop switch: + if ((this.systemStatus & 0x84) != 0) { + return; + } + this.handleStop(); + } + } + } +} +GameBoyAdvanceIO.prototype.runTHUMB = function () { + //Clock through the state machine: + while (true) { + //Handle the current system state selected: + switch (this.systemStatus | 0) { + case 4: //CPU Handle State (Normal THUMB) + this.THUMB.executeIteration(); + break; + case 5: + case 6: //CPU Handle State (Bubble THUMB) + this.THUMB.executeBubble(); + this.tickBubble(); + break; + default: //Handle lesser called / End of stepping + //Dispatch on IRQ/DMA/HALT/STOP/END bit flags + switch (this.systemStatus >> 2) { + case 0x3: + //IRQ Handle State: + this.handleIRQThumb(); + break; + case 0x5: + case 0x7: + //DMA Handle State + case 0xD: + case 0xF: + //DMA Inside Halt State + this.handleDMA(); + break; + case 0x9: + case 0x11: + //Handle Halt State + this.handleHalt(); + break; + default: //Handle Stop State + //ARM flagged stuff falls to here intentionally: + //End of Stepping and/or CPU run loop switch: + if ((this.systemStatus & 0x84) != 0x4) { + return; + } + this.handleStop(); + } + } + } +} +GameBoyAdvanceIO.prototype.updateCore = function (clocks) { + clocks = clocks | 0; + //This is used during normal/dma modes of operation: + this.accumulatedClocks = ((this.accumulatedClocks | 0) + (clocks | 0)) | 0; + if ((this.accumulatedClocks | 0) >= (this.nextEventClocks | 0)) { + this.updateCoreSpill(); + } +} +GameBoyAdvanceIO.prototype.updateCoreForce = function (clocks) { + clocks = clocks | 0; + //This is used during halt mode of operation: + this.accumulatedClocks = ((this.accumulatedClocks | 0) + (clocks | 0)) | 0; + this.updateCoreSpill(); +} +GameBoyAdvanceIO.prototype.updateCoreNegative = function (clocks) { + clocks = clocks | 0; + //This is used during normal/dma modes of operation: + this.accumulatedClocks = ((this.accumulatedClocks | 0) - (clocks | 0)) | 0; + if ((this.accumulatedClocks | 0) >= (this.nextEventClocks | 0)) { + this.updateCoreSpill(); + } +} +GameBoyAdvanceIO.prototype.updateCoreSingle = function () { + //This is used during normal/dma modes of operation: + this.accumulatedClocks = ((this.accumulatedClocks | 0) + 1) | 0; + if ((this.accumulatedClocks | 0) >= (this.nextEventClocks | 0)) { + this.updateCoreSpill(); + } +} +GameBoyAdvanceIO.prototype.updateCoreSpill = function () { + //Invalidate & recompute new event times: + this.updateCoreClocking(); + this.updateCoreEventTime(); +} +GameBoyAdvanceIO.prototype.updateCoreSpillRetain = function () { + //Keep the last prediction, just decrement it out, as it's still valid: + this.nextEventClocks = ((this.nextEventClocks | 0) - (this.accumulatedClocks | 0)) | 0; + this.updateCoreClocking(); +} +GameBoyAdvanceIO.prototype.updateCoreClocking = function () { + var clocks = this.accumulatedClocks | 0; + //Decrement the clocks per iteration counter: + this.cyclesToIterate = ((this.cyclesToIterate | 0) - (clocks | 0)) | 0; + //Clock all components: + this.gfxState.addClocks(((clocks | 0) - (this.graphicsClocks | 0)) | 0); + this.timer.addClocks(((clocks | 0) - (this.timerClocks | 0)) | 0); + this.serial.addClocks(((clocks | 0) - (this.serialClocks | 0)) | 0); + this.accumulatedClocks = 0; + this.graphicsClocks = 0; + this.timerClocks = 0; + this.serialClocks = 0; +} +GameBoyAdvanceIO.prototype.updateGraphicsClocking = function () { + //Clock gfx component: + this.gfxState.addClocks(((this.accumulatedClocks | 0) - (this.graphicsClocks | 0)) | 0); + this.graphicsClocks = this.accumulatedClocks | 0; +} +GameBoyAdvanceIO.prototype.updateTimerClocking = function () { + //Clock timer component: + this.timer.addClocks(((this.accumulatedClocks | 0) - (this.timerClocks | 0)) | 0); + this.timerClocks = this.accumulatedClocks | 0; +} +GameBoyAdvanceIO.prototype.updateSerialClocking = function () { + //Clock serial component: + this.serial.addClocks(((this.accumulatedClocks | 0) - (this.serialClocks | 0)) | 0); + this.serialClocks = this.accumulatedClocks | 0; +} +GameBoyAdvanceIO.prototype.updateCoreEventTime = function () { + //Predict how many clocks until the next DMA or IRQ event: + this.nextEventClocks = this.cyclesUntilNextEvent() | 0; +} +GameBoyAdvanceIO.prototype.getRemainingCycles = function () { + //Return the number of cycles left until iteration end: + if ((this.cyclesToIterate | 0) < 1) { + //Change our stepper to our end sequence: + this.flagIterationEnd(); + return 0; + } + return this.cyclesToIterate | 0; +} +GameBoyAdvanceIO.prototype.handleIRQARM = function () { + if ((this.systemStatus | 0) > 0x8) { + //CPU Handle State (Bubble ARM) + this.ARM.executeBubble(); + this.tickBubble(); + } + else { + //CPU Handle State (IRQ) + this.cpu.IRQinARM(); + } +} +GameBoyAdvanceIO.prototype.handleIRQThumb = function () { + if ((this.systemStatus | 0) > 0xC) { + //CPU Handle State (Bubble THUMB) + this.THUMB.executeBubble(); + this.tickBubble(); + } + else { + //CPU Handle State (IRQ) + this.cpu.IRQinTHUMB(); + } +} +GameBoyAdvanceIO.prototype.handleDMA = function () { + /* + Loop our state status in here as + an optimized iteration, as DMA stepping instances + happen in quick succession of each other, and + aren't often done for one memory word only. + */ + do { + //Perform a DMA read and write: + this.dma.perform(); + } while ((this.systemStatus & 0x90) == 0x10); +} +GameBoyAdvanceIO.prototype.handleHalt = function () { + if ((this.irq.IRQMatch() | 0) == 0) { + //Clock up to next IRQ match or DMA: + this.updateCoreForce(this.cyclesUntilNextHALTEvent() | 0); + } + else { + //Exit HALT promptly: + this.deflagHalt(); + } +} +GameBoyAdvanceIO.prototype.handleStop = function () { + //Update sound system to add silence to buffer: + this.sound.addClocks(this.getRemainingCycles() | 0); + this.cyclesToIterate = 0; + //Exits when user presses joypad or from an external irq outside of GBA internal. +} +GameBoyAdvanceIO.prototype.cyclesUntilNextHALTEvent = function () { + //Find the clocks to the next HALT leave or DMA event: + var haltClocks = this.irq.nextEventTime() | 0; + var dmaClocks = this.dma.nextEventTime() | 0; + return this.solveClosestTime(haltClocks | 0, dmaClocks | 0) | 0; +} +GameBoyAdvanceIO.prototype.cyclesUntilNextEvent = function () { + //Find the clocks to the next IRQ or DMA event: + var irqClocks = this.irq.nextIRQEventTime() | 0; + var dmaClocks = this.dma.nextEventTime() | 0; + return this.solveClosestTime(irqClocks | 0, dmaClocks | 0) | 0; +} +GameBoyAdvanceIO.prototype.solveClosestTime = function (clocks1, clocks2) { + clocks1 = clocks1 | 0; + clocks2 = clocks2 | 0; + //Find the clocks closest to the next event: + var clocks = this.getRemainingCycles() | 0; + clocks = Math.min(clocks | 0, clocks1 | 0, clocks2 | 0); + return clocks | 0; +} +GameBoyAdvanceIO.prototype.flagBubble = function () { + //Flag a CPU pipeline bubble to step through: + this.systemStatus = this.systemStatus | 0x2; +} +GameBoyAdvanceIO.prototype.tickBubble = function () { + //Tick down a CPU pipeline bubble to step through: + this.systemStatus = ((this.systemStatus | 0) - 1) | 0; +} +GameBoyAdvanceIO.prototype.flagTHUMB = function () { + //Flag a CPU IRQ to step through: + this.systemStatus = this.systemStatus | 0x4; +} +GameBoyAdvanceIO.prototype.deflagTHUMB = function () { + //Deflag a CPU IRQ to step through: + this.systemStatus = this.systemStatus & 0xFB; +} +GameBoyAdvanceIO.prototype.flagIRQ = function () { + //Flag THUMB CPU mode to step through: + this.systemStatus = this.systemStatus | 0x8; +} +GameBoyAdvanceIO.prototype.deflagIRQ = function () { + //Deflag THUMB CPU mode to step through: + this.systemStatus = this.systemStatus & 0xF7; +} +GameBoyAdvanceIO.prototype.flagDMA = function () { + //Flag a DMA event to step through: + this.systemStatus = this.systemStatus | 0x10; +} +GameBoyAdvanceIO.prototype.deflagDMA = function () { + //Deflag a DMA event to step through: + this.systemStatus = this.systemStatus & 0xEF; +} +GameBoyAdvanceIO.prototype.flagHalt = function () { + //Flag a halt event to step through: + this.systemStatus = this.systemStatus | 0x20; +} +GameBoyAdvanceIO.prototype.deflagHalt = function () { + //Deflag a halt event to step through: + this.systemStatus = this.systemStatus & 0xDF; +} +GameBoyAdvanceIO.prototype.flagStop = function () { + //Flag a halt event to step through: + this.systemStatus = this.systemStatus | 0x40; +} +GameBoyAdvanceIO.prototype.deflagStop = function () { + //Deflag a halt event to step through: + this.systemStatus = this.systemStatus & 0xBF; +} +GameBoyAdvanceIO.prototype.flagIterationEnd = function () { + //Flag a run loop kill event to step through: + this.systemStatus = this.systemStatus | 0x80; +} +GameBoyAdvanceIO.prototype.deflagIterationEnd = function () { + //Deflag a run loop kill event to step through: + this.systemStatus = this.systemStatus & 0x7F; +} +GameBoyAdvanceIO.prototype.isStopped = function () { + //Sound system uses this to emulate a unpowered audio output: + return ((this.systemStatus & 0x40) != 0); +} +GameBoyAdvanceIO.prototype.inDMA = function () { + //Save system uses this to detect dma: + return ((this.systemStatus & 0x10) != 0); +} +GameBoyAdvanceIO.prototype.getCurrentFetchValue = function () { + //Last valid value output for bad reads: + var fetch = 0; + if ((this.systemStatus & 0x10) == 0) { + fetch = this.cpu.getCurrentFetchValue() | 0; + } + else { + fetch = this.dma.getCurrentFetchValue() | 0; + } + return fetch | 0; +} diff --git a/public/gfiles/gba/IodineGBA/core/Saves.js b/public/gfiles/gba/IodineGBA/core/Saves.js new file mode 100644 index 00000000..a19a97e5 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/Saves.js @@ -0,0 +1,239 @@ +"use strict"; +/* + Copyright (C) 2012-2016 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceSaves(IOCore) { + this.cartridge = IOCore.cartridge; +} +GameBoyAdvanceSaves.prototype.initialize = function () { + this.saveType = 0; + this.GPIOChip = new GameBoyAdvanceGPIOChip(); + this.UNDETERMINED = new GameBoyAdvanceSaveDeterminer(this); + this.SRAMChip = new GameBoyAdvanceSRAMChip(); + this.FLASHChip = new GameBoyAdvanceFLASHChip(this.cartridge.flash_is128, this.cartridge.flash_isAtmel); + this.EEPROMChip = new GameBoyAdvanceEEPROMChip(this.cartridge.IOCore); + this.currentChip = this.UNDETERMINED; + this.referenceSave(this.saveType); +} +GameBoyAdvanceSaves.prototype.referenceSave = function (saveType) { + saveType = saveType | 0; + switch (saveType | 0) { + case 0: + this.currentChip = this.UNDETERMINED; + break; + case 1: + this.currentChip = this.SRAMChip; + break; + case 2: + this.currentChip = this.FLASHChip; + break; + case 3: + this.currentChip = this.EEPROMChip; + } + this.currentChip.initialize(); + this.saveType = saveType | 0; +} +GameBoyAdvanceSaves.prototype.importSave = function (saves, saveType) { + saveType = saveType | 0; + this.UNDETERMINED.load(saves); + this.SRAMChip.load(saves); + this.FLASHChip.load(saves); + this.EEPROMChip.load(saves); + this.referenceSave(saveType | 0); +} +GameBoyAdvanceSaves.prototype.importRTC = function (saves) { + this.GPIOChip.loadRTC(saves); +} +GameBoyAdvanceSaves.prototype.importGPIOType = function (gpioType) { + gpioType = gpioType | 0; + this.GPIOChip.loadType(gpioType | 0); +} +GameBoyAdvanceSaves.prototype.exportSave = function () { + return this.currentChip.saves; +} +GameBoyAdvanceSaves.prototype.exportSaveType = function () { + return this.saveType | 0; +} +GameBoyAdvanceSaves.prototype.readGPIO8 = function (address) { + address = address | 0; + var data = 0; + if ((this.GPIOChip.getType() | 0) > 0) { + //GPIO: + data = this.GPIOChip.read8(address | 0) | 0; + } + else { + //ROM: + data = this.cartridge.readROMOnly8(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceSaves.prototype.readEEPROM8 = function (address) { + address = address | 0; + var data = 0; + if ((this.saveType | 0) == 3) { + //EEPROM: + data = this.EEPROMChip.read8() | 0; + } + else { + //UNKNOWN: + data = this.UNDETERMINED.readEEPROM8(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceSaves.prototype.readGPIO16 = function (address) { + address = address | 0; + var data = 0; + if ((this.GPIOChip.getType() | 0) > 0) { + //GPIO: + data = this.GPIOChip.read16(address | 0) | 0; + } + else { + //ROM: + data = this.cartridge.readROMOnly16(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceSaves.prototype.readEEPROM16 = function (address) { + address = address | 0; + var data = 0; + if ((this.saveType | 0) == 3) { + //EEPROM: + data = this.EEPROMChip.read16() | 0; + } + else { + //UNKNOWN: + data = this.UNDETERMINED.readEEPROM16(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceSaves.prototype.readGPIO32 = function (address) { + address = address | 0; + var data = 0; + if ((this.GPIOChip.getType() | 0) > 0) { + //GPIO: + data = this.GPIOChip.read32(address | 0) | 0; + } + else { + //ROM: + data = this.cartridge.readROMOnly32(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceSaves.prototype.readEEPROM32 = function (address) { + address = address | 0; + var data = 0; + if ((this.saveType | 0) == 3) { + //EEPROM: + data = this.EEPROMChip.read32() | 0; + } + else { + //UNKNOWN: + data = this.UNDETERMINED.readEEPROM32(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceSaves.prototype.readSRAM = function (address) { + address = address | 0; + var data = 0; + switch (this.saveType | 0) { + case 0: + //UNKNOWN: + data = this.UNDETERMINED.readSRAM(address | 0) | 0; + break; + case 1: + //SRAM: + data = this.SRAMChip.read(address | 0) | 0; + break; + case 2: + //FLASH: + data = this.FLASHChip.read(address | 0) | 0; + } + return data | 0; +} +GameBoyAdvanceSaves.prototype.writeGPIO8 = function (address, data) { + address = address | 0; + data = data | 0; + if ((this.GPIOChip.getType() | 0) > 0) { + //GPIO: + this.GPIOChip.write8(address | 0, data | 0); + } + else { + //Unknown: + this.UNDETERMINED.writeGPIO8(address | 0, data | 0); + } +} +GameBoyAdvanceSaves.prototype.writeGPIO16 = function (address, data) { + address = address | 0; + data = data | 0; + if ((this.GPIOChip.getType() | 0) > 0) { + //GPIO: + this.GPIOChip.write16(address | 0, data | 0); + } + else { + //Unknown: + this.UNDETERMINED.writeGPIO16(address | 0, data | 0); + } +} +GameBoyAdvanceSaves.prototype.writeEEPROM16 = function (address, data) { + address = address | 0; + data = data | 0; + if ((this.saveType | 0) == 3) { + //EEPROM: + this.EEPROMChip.write16(data | 0); + } + else { + //Unknown: + this.UNDETERMINED.writeEEPROM16(address | 0, data | 0); + } +} +GameBoyAdvanceSaves.prototype.writeGPIO32 = function (address, data) { + address = address | 0; + data = data | 0; + if ((this.GPIOChip.getType() | 0) > 0) { + //GPIO: + this.GPIOChip.write32(address | 0, data | 0); + } + else { + //Unknown: + this.UNDETERMINED.writeGPIO32(address | 0, data | 0); + } +} +GameBoyAdvanceSaves.prototype.writeSRAM = function (address, data) { + address = address | 0; + data = data | 0; + switch (this.saveType | 0) { + case 0: + //Unknown: + this.UNDETERMINED.writeSRAM(address | 0, data | 0); + break; + case 1: + //SRAM: + this.SRAMChip.write(address | 0, data | 0); + break; + case 2: + //FLASH: + this.FLASHChip.write(address | 0, data | 0); + } +} +GameBoyAdvanceSaves.prototype.writeSRAMIfDefined = function (address, data) { + address = address | 0; + data = data | 0; + switch (this.saveType | 0) { + case 0: + //UNKNOWN: + this.SRAMChip.initialize(); + case 1: + //SRAM: + this.SRAMChip.write(address | 0, data | 0); + break; + case 2: + //FLASH: + this.FLASHChip.write(address | 0, data | 0); + } +} diff --git a/public/gfiles/gba/IodineGBA/core/Serial.js b/public/gfiles/gba/IodineGBA/core/Serial.js new file mode 100644 index 00000000..88aa8383 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/Serial.js @@ -0,0 +1,437 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceSerial(IOCore) { + this.IOCore = IOCore; +} +GameBoyAdvanceSerial.prototype.initialize = function () { + this.SIODATA_A = 0xFFFF; + this.SIODATA_B = 0xFFFF; + this.SIODATA_C = 0xFFFF; + this.SIODATA_D = 0xFFFF; + this.SIOShiftClockExternal = 0; + this.SIOShiftClockDivider = 0x40; + this.SIOCNT0_DATA = 0x0C; + this.SIOTransferStarted = false; + this.SIOMULT_PLAYER_NUMBER = 0; + this.SIOCOMMERROR = false; + this.SIOBaudRate = 0; + this.SIOCNT_UART_CTS = false; + this.SIOCNT_UART_MISC = 0; + this.SIOCNT_UART_FIFO = 0; + this.SIOCNT_IRQ = 0; + this.SIOCNT_MODE = 0; + this.SIOCNT_UART_RECV_ENABLE = false; + this.SIOCNT_UART_SEND_ENABLE = false; + this.SIOCNT_UART_PARITY_ENABLE = false; + this.SIOCNT_UART_FIFO_ENABLE = false; + this.SIODATA8 = 0xFFFF; + this.RCNTMode = 0; + this.RCNTIRQ = false; + this.RCNTDataBits = 0; + this.RCNTDataBitFlow = 0; + this.JOYBUS_IRQ = 0; + this.JOYBUS_CNTL_FLAGS = 0; + this.JOYBUS_RECV0 = 0xFF; + this.JOYBUS_RECV1 = 0xFF; + this.JOYBUS_RECV2 = 0xFF; + this.JOYBUS_RECV3 = 0xFF; + this.JOYBUS_SEND0 = 0xFF; + this.JOYBUS_SEND1 = 0xFF; + this.JOYBUS_SEND2 = 0xFF; + this.JOYBUS_SEND3 = 0xFF; + this.JOYBUS_STAT = 0; + this.shiftClocks = 0; + this.serialBitsShifted = 0; +} +GameBoyAdvanceSerial.prototype.SIOMultiplayerBaudRate = [ + 9600, + 38400, + 57600, + 115200 +]; +GameBoyAdvanceSerial.prototype.addClocks = function (clocks) { + clocks = clocks | 0; + if ((this.RCNTMode | 0) < 2) { + switch (this.SIOCNT_MODE | 0) { + case 0: + case 1: + if (this.SIOTransferStarted && (this.SIOShiftClockExternal | 0) == 0) { + this.shiftClocks = ((this.shiftClocks | 0) + (clocks | 0)) | 0; + while ((this.shiftClocks | 0) >= (this.SIOShiftClockDivider | 0)) { + this.shiftClocks = ((this.shiftClocks | 0) - (this.SIOShiftClockDivider | 0)) | 0; + this.clockSerial(); + } + } + break; + case 2: + if (this.SIOTransferStarted && (this.SIOMULT_PLAYER_NUMBER | 0) == 0) { + this.shiftClocks = ((this.shiftClocks | 0) + (clocks | 0)) | 0; + while ((this.shiftClocks | 0) >= (this.SIOShiftClockDivider | 0)) { + this.shiftClocks = ((this.shiftClocks | 0) - (this.SIOShiftClockDivider | 0)) | 0; + this.clockMultiplayer(); + } + } + break; + case 3: + if (this.SIOCNT_UART_SEND_ENABLE && !this.SIOCNT_UART_CTS) { + this.shiftClocks = ((this.shiftClocks | 0) + (clocks | 0)) | 0; + while ((this.shiftClocks | 0) >= (this.SIOShiftClockDivider | 0)) { + this.shiftClocks = ((this.shiftClocks | 0) - (this.SIOShiftClockDivider | 0)) | 0; + this.clockUART(); + } + } + } + } +} +GameBoyAdvanceSerial.prototype.clockSerial = function () { + //Emulate as if no slaves connected: + this.serialBitsShifted = ((this.serialBitsShifted | 0) + 1) | 0; + if ((this.SIOCNT_MODE | 0) == 0) { + //8-bit + this.SIODATA8 = ((this.SIODATA8 << 1) | 1) & 0xFFFF; + if ((this.serialBitsShifted | 0) == 8) { + this.SIOTransferStarted = false; + this.serialBitsShifted = 0; + if ((this.SIOCNT_IRQ | 0) != 0) { + //this.IOCore.irq.requestIRQ(0x80); + } + } + } + else { + //32-bit + this.SIODATA_D = ((this.SIODATA_D << 1) & 0xFE) | (this.SIODATA_C >> 7); + this.SIODATA_C = ((this.SIODATA_C << 1) & 0xFE) | (this.SIODATA_B >> 7); + this.SIODATA_B = ((this.SIODATA_B << 1) & 0xFE) | (this.SIODATA_A >> 7); + this.SIODATA_A = ((this.SIODATA_A << 1) & 0xFE) | 1; + if ((this.serialBitsShifted | 0) == 32) { + this.SIOTransferStarted = false; + this.serialBitsShifted = 0; + if ((this.SIOCNT_IRQ | 0) != 0) { + //this.IOCore.irq.requestIRQ(0x80); + } + } + } +} +GameBoyAdvanceSerial.prototype.clockMultiplayer = function () { + //Emulate as if no slaves connected: + this.SIODATA_A = this.SIODATA8 | 0; + this.SIODATA_B = 0xFFFF; + this.SIODATA_C = 0xFFFF; + this.SIODATA_D = 0xFFFF; + this.SIOTransferStarted = false; + this.SIOCOMMERROR = true; + if ((this.SIOCNT_IRQ | 0) != 0) { + //this.IOCore.irq.requestIRQ(0x80); + } +} +GameBoyAdvanceSerial.prototype.clockUART = function () { + this.serialBitsShifted = ((this.serialBitsShifted | 0) + 1) | 0; + if (this.SIOCNT_UART_FIFO_ENABLE) { + if ((this.serialBitsShifted | 0) == 8) { + this.serialBitsShifted = 0; + this.SIOCNT_UART_FIFO = Math.max(((this.SIOCNT_UART_FIFO | 0) - 1) | 0, 0) | 0; + if ((this.SIOCNT_UART_FIFO | 0) == 0 && (this.SIOCNT_IRQ | 0) != 0) { + //this.IOCore.irq.requestIRQ(0x80); + } + } + } + else { + if ((this.serialBitsShifted | 0) == 8) { + this.serialBitsShifted = 0; + if ((this.SIOCNT_IRQ | 0) != 0) { + //this.IOCore.irq.requestIRQ(0x80); + } + } + } +} +GameBoyAdvanceSerial.prototype.writeSIODATA_A0 = function (data) { + data = data | 0; + this.SIODATA_A = (this.SIODATA_A & 0xFF00) | data; +} +GameBoyAdvanceSerial.prototype.readSIODATA_A0 = function () { + return this.SIODATA_A & 0xFF; +} +GameBoyAdvanceSerial.prototype.writeSIODATA_A1 = function (data) { + data = data | 0; + this.SIODATA_A = (this.SIODATA_A & 0xFF) | (data << 8); +} +GameBoyAdvanceSerial.prototype.readSIODATA_A1 = function () { + return this.SIODATA_A >> 8; +} +GameBoyAdvanceSerial.prototype.writeSIODATA_B0 = function (data) { + data = data | 0; + this.SIODATA_B = (this.SIODATA_B & 0xFF00) | data; +} +GameBoyAdvanceSerial.prototype.readSIODATA_B0 = function () { + return this.SIODATA_B & 0xFF; +} +GameBoyAdvanceSerial.prototype.writeSIODATA_B1 = function (data) { + data = data | 0; + this.SIODATA_B = (this.SIODATA_B & 0xFF) | (data << 8); +} +GameBoyAdvanceSerial.prototype.readSIODATA_B1 = function () { + return this.SIODATA_B >> 8; +} +GameBoyAdvanceSerial.prototype.writeSIODATA_C0 = function (data) { + data = data | 0; + this.SIODATA_C = (this.SIODATA_C & 0xFF00) | data; +} +GameBoyAdvanceSerial.prototype.readSIODATA_C0 = function () { + return this.SIODATA_C & 0xFF; +} +GameBoyAdvanceSerial.prototype.writeSIODATA_C1 = function (data) { + data = data | 0; + this.SIODATA_C = (this.SIODATA_C & 0xFF) | (data << 8); +} +GameBoyAdvanceSerial.prototype.readSIODATA_C1 = function () { + return this.SIODATA_C >> 8; +} +GameBoyAdvanceSerial.prototype.writeSIODATA_D0 = function (data) { + data = data | 0; + this.SIODATA_D = (this.SIODATA_D & 0xFF00) | data; +} +GameBoyAdvanceSerial.prototype.readSIODATA_D0 = function () { + return this.SIODATA_D & 0xFF; +} +GameBoyAdvanceSerial.prototype.writeSIODATA_D1 = function (data) { + data = data | 0; + this.SIODATA_D = (this.SIODATA_D & 0xFF) | (data << 8); +} +GameBoyAdvanceSerial.prototype.readSIODATA_D1 = function () { + return this.SIODATA_D >> 8; +} +GameBoyAdvanceSerial.prototype.writeSIOCNT0 = function (data) { + if ((this.RCNTMode | 0) < 0x2) { + switch (this.SIOCNT_MODE | 0) { + //8-Bit: + case 0: + //32-Bit: + case 1: + this.SIOShiftClockExternal = data & 0x1; + this.SIOShiftClockDivider = ((data & 0x2) != 0) ? 0x8 : 0x40; + this.SIOCNT0_DATA = data & 0xB; + if ((data & 0x80) != 0) { + if (!this.SIOTransferStarted) { + this.SIOTransferStarted = true; + this.serialBitsShifted = 0; + this.shiftClocks = 0; + } + } + else { + this.SIOTransferStarted = false; + } + break; + //Multiplayer: + case 2: + this.SIOBaudRate = data & 0x3; + this.SIOShiftClockDivider = this.SIOMultiplayerBaudRate[this.SIOBaudRate | 0] | 0; + this.SIOMULT_PLAYER_NUMBER = (data >> 4) & 0x3; + this.SIOCOMMERROR = ((data & 0x40) != 0); + if ((data & 0x80) != 0) { + if (!this.SIOTransferStarted) { + this.SIOTransferStarted = true; + if ((this.SIOMULT_PLAYER_NUMBER | 0) == 0) { + this.SIODATA_A = 0xFFFF; + this.SIODATA_B = 0xFFFF; + this.SIODATA_C = 0xFFFF; + this.SIODATA_D = 0xFFFF; + } + this.serialBitsShifted = 0; + this.shiftClocks = 0; + } + } + else { + this.SIOTransferStarted = false; + } + break; + //UART: + case 3: + this.SIOBaudRate = data & 0x3; + this.SIOShiftClockDivider = this.SIOMultiplayerBaudRate[this.SIOBaudRate | 0] | 0; + this.SIOCNT_UART_MISC = (data & 0xCF) >> 2; + this.SIOCNT_UART_CTS = ((data & 0x4) != 0); + } + } +} +GameBoyAdvanceSerial.prototype.readSIOCNT0 = function () { + if (this.RCNTMode < 0x2) { + switch (this.SIOCNT_MODE) { + //8-Bit: + case 0: + //32-Bit: + case 1: + return ((this.SIOTransferStarted) ? 0x80 : 0) | 0x74 | this.SIOCNT0_DATA; + //Multiplayer: + case 2: + return ((this.SIOTransferStarted) ? 0x80 : 0) | ((this.SIOCOMMERROR) ? 0x40 : 0) | (this.SIOMULT_PLAYER_NUMBER << 4) | this.SIOBaudRate; + //UART: + case 3: + return (this.SIOCNT_UART_MISC << 2) | ((this.SIOCNT_UART_FIFO == 4) ? 0x30 : 0x20) | this.SIOBaudRate; + } + } + return 0xFF; +} +GameBoyAdvanceSerial.prototype.writeSIOCNT1 = function (data) { + this.SIOCNT_IRQ = data & 0x40; + this.SIOCNT_MODE = (data >> 4) & 0x3; + this.SIOCNT_UART_RECV_ENABLE = ((data & 0x8) != 0); + this.SIOCNT_UART_SEND_ENABLE = ((data & 0x4) != 0); + this.SIOCNT_UART_PARITY_ENABLE = ((data & 0x2) != 0); + this.SIOCNT_UART_FIFO_ENABLE = ((data & 0x1) != 0); +} +GameBoyAdvanceSerial.prototype.readSIOCNT1 = function () { + return (0x80 | this.SIOCNT_IRQ | (this.SIOCNT_MODE << 4) | ((this.SIOCNT_UART_RECV_ENABLE) ? 0x8 : 0) | + ((this.SIOCNT_UART_SEND_ENABLE) ? 0x4 : 0) | ((this.SIOCNT_UART_PARITY_ENABLE) ? 0x2 : 0) | ((this.SIOCNT_UART_FIFO_ENABLE) ? 0x2 : 0)); +} +GameBoyAdvanceSerial.prototype.writeSIODATA8_0 = function (data) { + data = data | 0; + this.SIODATA8 = (this.SIODATA8 & 0xFF00) | data; + if ((this.RCNTMode | 0) < 0x2 && (this.SIOCNT_MODE | 0) == 3 && this.SIOCNT_UART_FIFO_ENABLE) { + this.SIOCNT_UART_FIFO = Math.min(((this.SIOCNT_UART_FIFO | 0) + 1) | 0, 4) | 0; + } +} +GameBoyAdvanceSerial.prototype.readSIODATA8_0 = function () { + return this.SIODATA8 & 0xFF; +} +GameBoyAdvanceSerial.prototype.writeSIODATA8_1 = function (data) { + data = data | 0; + this.SIODATA8 = (this.SIODATA8 & 0xFF) | (data << 8); +} +GameBoyAdvanceSerial.prototype.readSIODATA8_1 = function () { + return this.SIODATA8 >> 8; +} +GameBoyAdvanceSerial.prototype.writeRCNT0 = function (data) { + if ((this.RCNTMode | 0) == 0x2) { + //General Comm: + var oldDataBits = this.RCNTDataBits | 0; + this.RCNTDataBits = data & 0xF; //Device manually controls SI/SO/SC/SD here. + this.RCNTDataBitFlow = data >> 4; + if (this.RCNTIRQ && ((oldDataBits ^ this.RCNTDataBits) & oldDataBits & 0x4) != 0) { + //SI fell low, trigger IRQ: + //this.IOCore.irq.requestIRQ(0x80); + } + } +} +GameBoyAdvanceSerial.prototype.readRCNT0 = function () { + return (this.RCNTDataBitFlow << 4) | this.RCNTDataBits; +} +GameBoyAdvanceSerial.prototype.writeRCNT1 = function (data) { + this.RCNTMode = data >> 6; + this.RCNTIRQ = ((data & 0x1) != 0); + if ((this.RCNTMode | 0) != 0x2) { + //Force SI/SO/SC/SD to low as we're never "hooked" up: + this.RCNTDataBits = 0; + this.RCNTDataBitFlow = 0; + } +} +GameBoyAdvanceSerial.prototype.readRCNT1 = function () { + return (this.RCNTMode << 6) | ((this.RCNTIRQ) ? 0x3F : 0x3E); +} +GameBoyAdvanceSerial.prototype.writeJOYCNT = function (data) { + this.JOYBUS_IRQ = (data << 25) >> 31; + this.JOYBUS_CNTL_FLAGS &= ~(data & 0x7); +} +GameBoyAdvanceSerial.prototype.readJOYCNT = function () { + return (this.JOYBUS_CNTL_FLAGS | 0x40) | (0xB8 & this.JOYBUS_IRQ); +} +GameBoyAdvanceSerial.prototype.writeJOYBUS_RECV0 = function (data) { + this.JOYBUS_RECV0 = data | 0; +} +GameBoyAdvanceSerial.prototype.readJOYBUS_RECV0 = function () { + this.JOYBUS_STAT = this.JOYBUS_STAT & 0xF7; + return this.JOYBUS_RECV0 | 0; +} +GameBoyAdvanceSerial.prototype.writeJOYBUS_RECV1 = function (data) { + this.JOYBUS_RECV1 = data | 0; +} +GameBoyAdvanceSerial.prototype.readJOYBUS_RECV1 = function () { + this.JOYBUS_STAT = this.JOYBUS_STAT & 0xF7; + return this.JOYBUS_RECV1 | 0; +} +GameBoyAdvanceSerial.prototype.writeJOYBUS_RECV2 = function (data) { + this.JOYBUS_RECV2 = data | 0; +} +GameBoyAdvanceSerial.prototype.readJOYBUS_RECV2 = function () { + this.JOYBUS_STAT = this.JOYBUS_STAT & 0xF7; + return this.JOYBUS_RECV2 | 0; +} +GameBoyAdvanceSerial.prototype.writeJOYBUS_RECV3 = function (data) { + this.JOYBUS_RECV3 = data | 0; +} +GameBoyAdvanceSerial.prototype.readJOYBUS_RECV3 = function () { + this.JOYBUS_STAT = this.JOYBUS_STAT & 0xF7; + return this.JOYBUS_RECV3 | 0; +} +GameBoyAdvanceSerial.prototype.writeJOYBUS_SEND0 = function (data) { + this.JOYBUS_SEND0 = data | 0; + this.JOYBUS_STAT = this.JOYBUS_STAT | 0x2; +} +GameBoyAdvanceSerial.prototype.readJOYBUS_SEND0 = function () { + return this.JOYBUS_SEND0 | 0; +} +GameBoyAdvanceSerial.prototype.writeJOYBUS_SEND1 = function (data) { + this.JOYBUS_SEND1 = data | 0; + this.JOYBUS_STAT = this.JOYBUS_STAT | 0x2; +} +GameBoyAdvanceSerial.prototype.readJOYBUS_SEND1 = function () { + return this.JOYBUS_SEND1 | 0; +} +GameBoyAdvanceSerial.prototype.writeJOYBUS_SEND2 = function (data) { + this.JOYBUS_SEND2 = data | 0; + this.JOYBUS_STAT = this.JOYBUS_STAT | 0x2; +} +GameBoyAdvanceSerial.prototype.readJOYBUS_SEND2 = function () { + return this.JOYBUS_SEND2 | 0; +} +GameBoyAdvanceSerial.prototype.writeJOYBUS_SEND3 = function (data) { + this.JOYBUS_SEND3 = data | 0; + this.JOYBUS_STAT = this.JOYBUS_STAT | 0x2; +} +GameBoyAdvanceSerial.prototype.readJOYBUS_SEND3 = function () { + return this.JOYBUS_SEND3 | 0; +} +GameBoyAdvanceSerial.prototype.writeJOYBUS_STAT = function (data) { + this.JOYBUS_STAT = data | 0; +} +GameBoyAdvanceSerial.prototype.readJOYBUS_STAT = function () { + return 0xC5 | this.JOYBUS_STAT; +} +/*GameBoyAdvanceSerial.prototype.nextIRQEventTime = function (clocks) { + if ((this.SIOCNT_IRQ | 0) != 0 && (this.RCNTMode | 0) < 2) { + switch (this.SIOCNT_MODE | 0) { + case 0: + case 1: + if (this.SIOTransferStarted && (this.SIOShiftClockExternal | 0) == 0) { + return ((((this.SIOCNT_MODE == 1) ? 31 : 7) - this.serialBitsShifted) * this.SIOShiftClockDivider) + (this.SIOShiftClockDivider - this.shiftClocks); + } + else { + return 0x7FFFFFFF; + } + case 2: + if (this.SIOTransferStarted && this.SIOMULT_PLAYER_NUMBER == 0) { + return this.SIOShiftClockDivider - this.shiftClocks; + } + else { + return 0x7FFFFFFF; + } + case 3: + if (this.SIOCNT_UART_SEND_ENABLE && !this.SIOCNT_UART_CTS) { + return (Math.max(((this.SIOCNT_UART_FIFO_ENABLE) ? (this.SIOCNT_UART_FIFO * 8) : 8) - 1, 0) * this.SIOShiftClockDivider) + (this.SIOShiftClockDivider - this.shiftClocks); + } + else { + return 0x7FFFFFFF; + } + } + } + else { + return 0x7FFFFFFF; + } +}*/ \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/Sound.js b/public/gfiles/gba/IodineGBA/core/Sound.js new file mode 100644 index 00000000..bd98dbcf --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/Sound.js @@ -0,0 +1,1082 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceSound(IOCore) { + //Build references: + this.IOCore = IOCore; +} +GameBoyAdvanceSound.prototype.initialize = function () { + this.coreExposed = this.IOCore.coreExposed; + this.dmaChannel1 = this.IOCore.dmaChannel1; + this.dmaChannel2 = this.IOCore.dmaChannel2; + //Initialize start: + this.audioTicks = 0; + this.initializeSampling(380); + this.initializeAudioStartState(); +} +GameBoyAdvanceSound.prototype.initializeOutput = function (audioResamplerFirstPassFactor) { + audioResamplerFirstPassFactor = audioResamplerFirstPassFactor | 0; + if ((audioResamplerFirstPassFactor | 0) != (this.audioResamplerFirstPassFactor | 0)) { + this.initializeSampling(audioResamplerFirstPassFactor | 0); + } +} +GameBoyAdvanceSound.prototype.initializeSampling = function (audioResamplerFirstPassFactor) { + audioResamplerFirstPassFactor = audioResamplerFirstPassFactor | 0; + this.audioIndex = 0; + this.downsampleInputLeft = 0; + this.downsampleInputRight = 0; + this.audioResamplerFirstPassFactor = audioResamplerFirstPassFactor | 0; +} +GameBoyAdvanceSound.prototype.initializeAudioStartState = function () { + //NOTE: NR 60-63 never get reset in audio halting: + this.nr60 = 0; + this.nr61 = 0; + this.nr62 = (!this.IOCore.SKIPBoot) ? 0 : 0xFF; + this.nr63 = (!this.IOCore.SKIPBoot) ? 0 : 0x2; + this.soundMasterEnabled = !!this.IOCore.SKIPBoot; + this.mixerSoundBIAS = (!this.IOCore.SKIPBoot) ? 0 : 0x200; + this.channel1 = new GameBoyAdvanceChannel1Synth(this); + this.channel2 = new GameBoyAdvanceChannel2Synth(this); + this.channel3 = new GameBoyAdvanceChannel3Synth(this); + this.channel4 = new GameBoyAdvanceChannel4Synth(this); + this.CGBMixerOutputCacheLeft = 0; + this.CGBMixerOutputCacheLeftFolded = 0; + this.CGBMixerOutputCacheRight = 0; + this.CGBMixerOutputCacheRightFolded = 0; + this.AGBDirectSoundATimer = 0; + this.AGBDirectSoundBTimer = 0; + this.AGBDirectSoundA = 0; + this.AGBDirectSoundAFolded = 0; + this.AGBDirectSoundB = 0; + this.AGBDirectSoundBFolded = 0; + this.AGBDirectSoundAShifter = 0; + this.AGBDirectSoundBShifter = 0; + this.AGBDirectSoundALeftCanPlay = false; + this.AGBDirectSoundBLeftCanPlay = false; + this.AGBDirectSoundARightCanPlay = false; + this.AGBDirectSoundBRightCanPlay = false; + this.CGBOutputRatio = 2; + this.FIFOABuffer = new GameBoyAdvanceFIFO(); + this.FIFOBBuffer = new GameBoyAdvanceFIFO(); + this.audioDisabled(); //Clear legacy PAPU registers: +} +GameBoyAdvanceSound.prototype.audioDisabled = function () { + this.channel1.disabled(); + this.channel2.disabled(); + this.channel3.disabled(); + this.channel4.disabled(); + //Clear FIFO: + this.AGBDirectSoundAFIFOClear(); + this.AGBDirectSoundBFIFOClear(); + //Clear NR50: + this.nr50 = 0; + this.VinLeftChannelMasterVolume = 1; + this.VinRightChannelMasterVolume = 1; + //Clear NR51: + this.nr51 = 0; + //Clear NR52: + this.nr52 = 0; + this.soundMasterEnabled = false; + this.mixerOutputCacheLeft = this.mixerSoundBIAS | 0; + this.mixerOutputCacheRight = this.mixerSoundBIAS | 0; + this.audioClocksUntilNextEventCounter = 0; + this.audioClocksUntilNextEvent = 0; + this.sequencePosition = 0; + this.sequencerClocks = 0x8000; + this.PWMWidth = 0x200; + this.PWMWidthOld = 0x200; + this.PWMWidthShadow = 0x200; + this.PWMBitDepthMask = 0x3FE; + this.PWMBitDepthMaskShadow = 0x3FE; + this.channel1.outputLevelCache(); + this.channel2.outputLevelCache(); + this.channel3.updateCache(); + this.channel4.updateCache(); +} +GameBoyAdvanceSound.prototype.audioEnabled = function () { + //Set NR52: + this.nr52 = 0x80; + this.soundMasterEnabled = true; +} +GameBoyAdvanceSound.prototype.addClocks = function (clocks) { + clocks = clocks | 0; + this.audioTicks = ((this.audioTicks | 0) + (clocks | 0)) | 0; +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceSound.prototype.generateAudio = function (numSamples) { + numSamples = numSamples | 0; + var multiplier = 0; + if (this.soundMasterEnabled && !this.IOCore.isStopped()) { + for (var clockUpTo = 0; (numSamples | 0) > 0;) { + clockUpTo = Math.min(this.PWMWidth | 0, numSamples | 0) | 0; + this.PWMWidth = ((this.PWMWidth | 0) - (clockUpTo | 0)) | 0; + numSamples = ((numSamples | 0) - (clockUpTo | 0)) | 0; + while ((clockUpTo | 0) > 0) { + multiplier = Math.min(clockUpTo | 0, ((this.audioResamplerFirstPassFactor | 0) - (this.audioIndex | 0)) | 0) | 0; + clockUpTo = ((clockUpTo | 0) - (multiplier | 0)) | 0; + this.audioIndex = ((this.audioIndex | 0) + (multiplier | 0)) | 0; + this.downsampleInputLeft = ((this.downsampleInputLeft | 0) + Math.imul(this.mixerOutputCacheLeft | 0, multiplier | 0)) | 0; + this.downsampleInputRight = ((this.downsampleInputRight | 0) + Math.imul(this.mixerOutputCacheRight | 0, multiplier | 0)) | 0; + if ((this.audioIndex | 0) == (this.audioResamplerFirstPassFactor | 0)) { + this.audioIndex = 0; + this.coreExposed.outputAudio(this.downsampleInputLeft | 0, this.downsampleInputRight | 0); + this.downsampleInputLeft = 0; + this.downsampleInputRight = 0; + } + } + if ((this.PWMWidth | 0) == 0) { + this.computeNextPWMInterval(); + this.PWMWidthOld = this.PWMWidthShadow | 0; + this.PWMWidth = this.PWMWidthShadow | 0; + } + } + } + else { + //SILENT OUTPUT: + while ((numSamples | 0) > 0) { + multiplier = Math.min(numSamples | 0, ((this.audioResamplerFirstPassFactor | 0) - (this.audioIndex | 0)) | 0) | 0; + numSamples = ((numSamples | 0) - (multiplier | 0)) | 0; + this.audioIndex = ((this.audioIndex | 0) + (multiplier | 0)) | 0; + if ((this.audioIndex | 0) == (this.audioResamplerFirstPassFactor | 0)) { + this.audioIndex = 0; + this.coreExposed.outputAudio(this.downsampleInputLeft | 0, this.downsampleInputRight | 0); + this.downsampleInputLeft = 0; + this.downsampleInputRight = 0; + } + } + } + } +} +else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceSound.prototype.generateAudio = function (numSamples) { + var multiplier = 0; + if (this.soundMasterEnabled && !this.IOCore.isStopped()) { + for (var clockUpTo = 0; numSamples > 0;) { + clockUpTo = Math.min(this.PWMWidth, numSamples); + this.PWMWidth = this.PWMWidth - clockUpTo; + numSamples -= clockUpTo; + while (clockUpTo > 0) { + multiplier = Math.min(clockUpTo, this.audioResamplerFirstPassFactor - this.audioIndex); + clockUpTo -= multiplier; + this.audioIndex += multiplier; + this.downsampleInputLeft += this.mixerOutputCacheLeft * multiplier; + this.downsampleInputRight += this.mixerOutputCacheRight * multiplier; + if (this.audioIndex == this.audioResamplerFirstPassFactor) { + this.audioIndex = 0; + this.coreExposed.outputAudio(this.downsampleInputLeft, this.downsampleInputRight); + this.downsampleInputLeft = 0; + this.downsampleInputRight = 0; + } + } + if (this.PWMWidth == 0) { + this.computeNextPWMInterval(); + this.PWMWidthOld = this.PWMWidthShadow; + this.PWMWidth = this.PWMWidthShadow; + } + } + } + else { + //SILENT OUTPUT: + while (numSamples > 0) { + multiplier = Math.min(numSamples, this.audioResamplerFirstPassFactor - this.audioIndex); + numSamples -= multiplier; + this.audioIndex += multiplier; + if (this.audioIndex == this.audioResamplerFirstPassFactor) { + this.audioIndex = 0; + this.coreExposed.outputAudio(this.downsampleInputLeft, this.downsampleInputRight); + this.downsampleInputLeft = 0; + this.downsampleInputRight = 0; + } + } + } + } +} +GameBoyAdvanceSound.prototype.audioJIT = function () { + //Audio Sample Generation Timing: + this.generateAudio(this.audioTicks | 0); + this.audioTicks = 0; +} +GameBoyAdvanceSound.prototype.audioPSGJIT = function () { + //Clock PCM timer logic: + this.IOCore.updateTimerClocking(); + //Clock audio state machine: + this.audioJIT(); +} +GameBoyAdvanceSound.prototype.computeNextPWMInterval = function () { + //Clock down the PSG system: + for (var numSamples = this.PWMWidthOld | 0, clockUpTo = 0; (numSamples | 0) > 0; numSamples = ((numSamples | 0) - 1) | 0) { + clockUpTo = Math.min(this.audioClocksUntilNextEventCounter | 0, this.sequencerClocks | 0, numSamples | 0) | 0; + this.audioClocksUntilNextEventCounter = ((this.audioClocksUntilNextEventCounter | 0) - (clockUpTo | 0)) | 0; + this.sequencerClocks = ((this.sequencerClocks | 0) - (clockUpTo | 0)) | 0; + numSamples = ((numSamples | 0) - (clockUpTo | 0)) | 0; + if ((this.sequencerClocks | 0) == 0) { + this.audioComputeSequencer(); + this.sequencerClocks = 0x8000; + } + if ((this.audioClocksUntilNextEventCounter | 0) == 0) { + this.computeAudioChannels(); + } + } + //Copy the new bit-depth mask for the next counter interval: + this.PWMBitDepthMask = this.PWMBitDepthMaskShadow | 0; + //Compute next sample for the PWM output: + this.channel1.outputLevelCache(); + this.channel2.outputLevelCache(); + this.channel3.updateCache(); + this.channel4.updateCache(); + this.CGBMixerOutputLevelCache(); + this.mixerOutputLevelCache(); +} +GameBoyAdvanceSound.prototype.audioComputeSequencer = function () { + switch (this.sequencePosition++) { + case 0: + this.clockAudioLength(); + break; + case 2: + this.clockAudioLength(); + this.channel1.clockAudioSweep(); + break; + case 4: + this.clockAudioLength(); + break; + case 6: + this.clockAudioLength(); + this.channel1.clockAudioSweep(); + break; + case 7: + this.clockAudioEnvelope(); + this.sequencePosition = 0; + } +} +GameBoyAdvanceSound.prototype.clockAudioLength = function () { + //Channel 1: + this.channel1.clockAudioLength(); + //Channel 2: + this.channel2.clockAudioLength(); + //Channel 3: + this.channel3.clockAudioLength(); + //Channel 4: + this.channel4.clockAudioLength(); +} +GameBoyAdvanceSound.prototype.clockAudioEnvelope = function () { + //Channel 1: + this.channel1.clockAudioEnvelope(); + //Channel 2: + this.channel2.clockAudioEnvelope(); + //Channel 4: + this.channel4.clockAudioEnvelope(); +} +GameBoyAdvanceSound.prototype.computeAudioChannels = function () { + //Clock down the four audio channels to the next closest audio event: + this.channel1.FrequencyCounter = ((this.channel1.FrequencyCounter | 0) - (this.audioClocksUntilNextEvent | 0)) | 0; + this.channel2.FrequencyCounter = ((this.channel2.FrequencyCounter | 0) - (this.audioClocksUntilNextEvent | 0)) | 0; + this.channel3.counter = ((this.channel3.counter | 0) - (this.audioClocksUntilNextEvent | 0)) | 0; + this.channel4.counter = ((this.channel4.counter | 0) - (this.audioClocksUntilNextEvent | 0)) | 0; + //Channel 1 counter: + this.channel1.computeAudioChannel(); + //Channel 2 counter: + this.channel2.computeAudioChannel(); + //Channel 3 counter: + this.channel3.computeAudioChannel(); + //Channel 4 counter: + this.channel4.computeAudioChannel(); + //Find the number of clocks to next closest counter event: + this.audioClocksUntilNextEventCounter = this.audioClocksUntilNextEvent = Math.min(this.channel1.FrequencyCounter | 0, this.channel2.FrequencyCounter | 0, this.channel3.counter | 0, this.channel4.counter | 0) | 0; +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceSound.prototype.CGBMixerOutputLevelCache = function () { + this.CGBMixerOutputCacheLeft = Math.imul(((this.channel1.currentSampleLeft | 0) + (this.channel2.currentSampleLeft | 0) + (this.channel3.currentSampleLeft | 0) + (this.channel4.currentSampleLeft | 0)) | 0, this.VinLeftChannelMasterVolume | 0) | 0; + this.CGBMixerOutputCacheRight = Math.imul(((this.channel1.currentSampleRight | 0) + (this.channel2.currentSampleRight | 0) + (this.channel3.currentSampleRight | 0) + (this.channel4.currentSampleRight | 0)) | 0, this.VinRightChannelMasterVolume | 0) | 0; + this.CGBFolder(); + } +} +else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceSound.prototype.CGBMixerOutputLevelCache = function () { + this.CGBMixerOutputCacheLeft = (this.channel1.currentSampleLeft + this.channel2.currentSampleLeft + this.channel3.currentSampleLeft + this.channel4.currentSampleLeft) * this.VinLeftChannelMasterVolume; + this.CGBMixerOutputCacheRight = (this.channel1.currentSampleRight + this.channel2.currentSampleRight + this.channel3.currentSampleRight + this.channel4.currentSampleRight) * this.VinRightChannelMasterVolume; + this.CGBFolder(); + } +} +GameBoyAdvanceSound.prototype.writeWAVE8 = function (address, data) { + address = address | 0; + data = data | 0; + this.audioPSGJIT(); + this.channel3.writeWAVE8(address | 0, data | 0); +} +GameBoyAdvanceSound.prototype.readWAVE8 = function (address) { + address = address | 0; + this.audioPSGJIT(); + return this.channel3.readWAVE8(address | 0) | 0; +} +GameBoyAdvanceSound.prototype.writeWAVE16 = function (address, data) { + address = address | 0; + data = data | 0; + this.audioPSGJIT(); + this.channel3.writeWAVE16(address >> 1, data | 0); +} +GameBoyAdvanceSound.prototype.readWAVE16 = function (address) { + address = address | 0; + this.audioPSGJIT(); + return this.channel3.readWAVE16(address >> 1) | 0; +} +GameBoyAdvanceSound.prototype.writeWAVE32 = function (address, data) { + address = address | 0; + data = data | 0; + this.audioPSGJIT(); + this.channel3.writeWAVE32(address >> 2, data | 0); +} +GameBoyAdvanceSound.prototype.readWAVE32 = function (address) { + address = address | 0; + this.audioPSGJIT(); + return this.channel3.readWAVE32(address >> 2) | 0; +} +GameBoyAdvanceSound.prototype.writeFIFOA8 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.FIFOABuffer.push8(data | 0); + this.checkFIFOAPendingSignal(); +} +GameBoyAdvanceSound.prototype.writeFIFOB8 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.FIFOBBuffer.push8(data | 0); + this.checkFIFOBPendingSignal(); +} +GameBoyAdvanceSound.prototype.writeFIFOA16 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.FIFOABuffer.push16(data | 0); + this.checkFIFOAPendingSignal(); +} +GameBoyAdvanceSound.prototype.writeFIFOB16 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.FIFOBBuffer.push16(data | 0); + this.checkFIFOBPendingSignal(); +} +GameBoyAdvanceSound.prototype.writeFIFOA32 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.FIFOABuffer.push32(data | 0); + this.checkFIFOAPendingSignal(); +} +GameBoyAdvanceSound.prototype.writeFIFOB32 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.FIFOBBuffer.push32(data | 0); + this.checkFIFOBPendingSignal(); +} +GameBoyAdvanceSound.prototype.checkFIFOAPendingSignal = function () { + if (this.FIFOABuffer.requestingDMA()) { + this.dmaChannel1.soundFIFOARequest(); + } +} +GameBoyAdvanceSound.prototype.checkFIFOBPendingSignal = function () { + if (this.FIFOBBuffer.requestingDMA()) { + this.dmaChannel2.soundFIFOBRequest(); + } +} +GameBoyAdvanceSound.prototype.AGBDirectSoundAFIFOClear = function () { + this.FIFOABuffer.clear(); + this.AGBDirectSoundATimerIncrement(); +} +GameBoyAdvanceSound.prototype.AGBDirectSoundBFIFOClear = function () { + this.FIFOBBuffer.clear(); + this.AGBDirectSoundBTimerIncrement(); +} +GameBoyAdvanceSound.prototype.AGBDirectSoundTimer0ClockTick = function () { + this.audioJIT(); + if (this.soundMasterEnabled) { + if ((this.AGBDirectSoundATimer | 0) == 0) { + this.AGBDirectSoundATimerIncrement(); + } + if ((this.AGBDirectSoundBTimer | 0) == 0) { + this.AGBDirectSoundBTimerIncrement(); + } + } +} +GameBoyAdvanceSound.prototype.AGBDirectSoundTimer1ClockTick = function () { + this.audioJIT(); + if (this.soundMasterEnabled) { + if ((this.AGBDirectSoundATimer | 0) == 1) { + this.AGBDirectSoundATimerIncrement(); + } + if ((this.AGBDirectSoundBTimer | 0) == 1) { + this.AGBDirectSoundBTimerIncrement(); + } + } +} +GameBoyAdvanceSound.prototype.nextFIFOAEventTime = function () { + var nextEventTime = 0x7FFFFFFF; + if (this.soundMasterEnabled) { + if (!this.FIFOABuffer.requestingDMA()) { + var samplesUntilDMA = this.FIFOABuffer.samplesUntilDMATrigger() | 0; + if ((this.AGBDirectSoundATimer | 0) == 0) { + nextEventTime = this.IOCore.timer.nextTimer0Overflow(samplesUntilDMA | 0) | 0; + } + else { + nextEventTime = this.IOCore.timer.nextTimer1Overflow(samplesUntilDMA | 0) | 0; + } + } + else { + nextEventTime = 0; + } + } + return nextEventTime | 0; +} +GameBoyAdvanceSound.prototype.nextFIFOBEventTime = function () { + var nextEventTime = 0x7FFFFFFF; + if (this.soundMasterEnabled) { + if (!this.FIFOBBuffer.requestingDMA()) { + var samplesUntilDMA = this.FIFOBBuffer.samplesUntilDMATrigger() | 0; + if ((this.AGBDirectSoundBTimer | 0) == 0) { + nextEventTime = this.IOCore.timer.nextTimer0Overflow(samplesUntilDMA | 0) | 0; + } + else { + nextEventTime = this.IOCore.timer.nextTimer1Overflow(samplesUntilDMA | 0) | 0; + } + } + else { + nextEventTime = 0; + } + } + return nextEventTime | 0; +} +GameBoyAdvanceSound.prototype.AGBDirectSoundATimerIncrement = function () { + this.AGBDirectSoundA = this.FIFOABuffer.shift() | 0; + this.checkFIFOAPendingSignal(); + this.AGBFIFOAFolder(); +} +GameBoyAdvanceSound.prototype.AGBDirectSoundBTimerIncrement = function () { + this.AGBDirectSoundB = this.FIFOBBuffer.shift() | 0; + this.checkFIFOBPendingSignal(); + this.AGBFIFOBFolder(); +} +GameBoyAdvanceSound.prototype.AGBFIFOAFolder = function () { + this.AGBDirectSoundAFolded = this.AGBDirectSoundA >> (this.AGBDirectSoundAShifter | 0); +} +GameBoyAdvanceSound.prototype.AGBFIFOBFolder = function () { + this.AGBDirectSoundBFolded = this.AGBDirectSoundB >> (this.AGBDirectSoundBShifter | 0); +} +GameBoyAdvanceSound.prototype.CGBFolder = function () { + this.CGBMixerOutputCacheLeftFolded = (this.CGBMixerOutputCacheLeft << (this.CGBOutputRatio | 0)) >> 1; + this.CGBMixerOutputCacheRightFolded = (this.CGBMixerOutputCacheRight << (this.CGBOutputRatio | 0)) >> 1; +} +GameBoyAdvanceSound.prototype.mixerOutputLevelCache = function () { + this.mixerOutputCacheLeft = Math.min(Math.max((((this.AGBDirectSoundALeftCanPlay) ? (this.AGBDirectSoundAFolded | 0) : 0) + + ((this.AGBDirectSoundBLeftCanPlay) ? (this.AGBDirectSoundBFolded | 0) : 0) + + (this.CGBMixerOutputCacheLeftFolded | 0) + (this.mixerSoundBIAS | 0)) | 0, 0) | 0, 0x3FF) & this.PWMBitDepthMask; + this.mixerOutputCacheRight = Math.min(Math.max((((this.AGBDirectSoundARightCanPlay) ? (this.AGBDirectSoundAFolded | 0) : 0) + + ((this.AGBDirectSoundBRightCanPlay) ? (this.AGBDirectSoundBFolded | 0) : 0) + + (this.CGBMixerOutputCacheRightFolded | 0) + (this.mixerSoundBIAS | 0)) | 0, 0) | 0, 0x3FF) & this.PWMBitDepthMask; +} +GameBoyAdvanceSound.prototype.setNR52 = function (data) { + data = data | 0; + this.nr52 = data | this.nr52; +} +GameBoyAdvanceSound.prototype.unsetNR52 = function (data) { + data = data | 0; + this.nr52 = data & this.nr52; +} +GameBoyAdvanceSound.prototype.readSOUND1CNT8_0 = function () { + //NR10: + return this.channel1.readSOUND1CNT8_0() | 0; +} +GameBoyAdvanceSound.prototype.writeSOUND1CNT8_0 = function (data) { + //NR10: + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel1.writeSOUND1CNT8_0(data | 0); + } +} +GameBoyAdvanceSound.prototype.readSOUND1CNT8_2 = function () { + //NR11: + return this.channel1.readSOUND1CNT8_2() | 0; +} +GameBoyAdvanceSound.prototype.writeSOUND1CNT8_2 = function (data) { + //NR11: + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel1.writeSOUND1CNT8_2(data | 0); + } +} +GameBoyAdvanceSound.prototype.readSOUND1CNT8_3 = function () { + //NR12: + return this.channel1.readSOUND1CNT8_3() | 0; +} +GameBoyAdvanceSound.prototype.writeSOUND1CNT8_3 = function (data) { + //NR12: + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel1.writeSOUND1CNT8_3(data | 0); + } +} +GameBoyAdvanceSound.prototype.writeSOUND1CNT16 = function (data) { + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + //NR11: + this.channel1.writeSOUND1CNT8_2(data | 0); + //NR12: + this.channel1.writeSOUND1CNT8_3(data >> 8); + } +} +GameBoyAdvanceSound.prototype.writeSOUND1CNT32 = function (data) { + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + //NR10: + this.channel1.writeSOUND1CNT8_0(data | 0); + //NR11: + this.channel1.writeSOUND1CNT8_2(data >> 16); + //NR12: + this.channel1.writeSOUND1CNT8_3(data >> 24); + } +} +GameBoyAdvanceSound.prototype.readSOUND1CNT32 = function () { + return this.channel1.readSOUND1CNT8_0() | + (this.channel1.readSOUND1CNT8_2() << 16) | + (this.channel1.readSOUND1CNT8_3() << 24); +} +GameBoyAdvanceSound.prototype.writeSOUND1CNTX8_0 = function (data) { + //NR13: + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel1.writeSOUND1CNT_X0(data | 0); + } +} +GameBoyAdvanceSound.prototype.readSOUND1CNTX8 = function () { + //NR14: + return this.channel1.readSOUND1CNTX8() | 0; +} +GameBoyAdvanceSound.prototype.readSOUND1CNTX16 = function () { + //NR14: + return this.channel1.readSOUND1CNTX8() << 8; +} +GameBoyAdvanceSound.prototype.writeSOUND1CNTX8_1 = function (data) { + //NR14: + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel1.writeSOUND1CNT_X1(data | 0); + } +} +GameBoyAdvanceSound.prototype.writeSOUND1CNTX16 = function (data) { + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + //NR13: + this.channel1.writeSOUND1CNT_X0(data | 0); + //NR14: + this.channel1.writeSOUND1CNT_X1(data >> 8); + } +} +GameBoyAdvanceSound.prototype.readSOUND2CNTL8_0 = function () { + //NR21: + return this.channel2.readSOUND2CNT_L0() | 0; +} +GameBoyAdvanceSound.prototype.writeSOUND2CNTL8_0 = function (data) { + data = data | 0; + //NR21: + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel2.writeSOUND2CNT_L0(data | 0); + } +} +GameBoyAdvanceSound.prototype.readSOUND2CNTL8_1 = function () { + //NR22: + return this.channel2.readSOUND2CNT_L1() | 0; +} +GameBoyAdvanceSound.prototype.writeSOUND2CNTL8_1 = function (data) { + data = data | 0; + //NR22: + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel2.writeSOUND2CNT_L1(data | 0); + } +} +GameBoyAdvanceSound.prototype.writeSOUND2CNTL16 = function (data) { + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + //NR21: + this.channel2.writeSOUND2CNT_L0(data | 0); + //NR22: + this.channel2.writeSOUND2CNT_L1(data >> 8); + } +} +GameBoyAdvanceSound.prototype.writeSOUND2CNTH8_0 = function (data) { + data = data | 0; + //NR23: + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel2.writeSOUND2CNT_H0(data | 0); + } +} +GameBoyAdvanceSound.prototype.readSOUND2CNTH8 = function () { + //NR24: + return this.channel2.readSOUND2CNT_H() | 0; +} +GameBoyAdvanceSound.prototype.writeSOUND2CNTH8_1 = function (data) { + data = data | 0; + //NR24: + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel2.writeSOUND2CNT_H1(data | 0); + } +} +GameBoyAdvanceSound.prototype.readSOUND2CNTL16 = function () { + //NR21: + //NR22: + var data = this.channel2.readSOUND2CNT_L0() | 0; + data = data | (this.channel2.readSOUND2CNT_L1() << 8); + return data | 0; +} +GameBoyAdvanceSound.prototype.readSOUND2CNTH16 = function () { + //NR24: + return this.channel2.readSOUND2CNT_H() << 8; +} +GameBoyAdvanceSound.prototype.writeSOUND2CNTH16 = function (data) { + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + //NR23: + this.channel2.writeSOUND2CNT_H0(data | 0); + //NR24: + this.channel2.writeSOUND2CNT_H1(data >> 8); + } +} +GameBoyAdvanceSound.prototype.readSOUND3CNT8_0 = function () { + //NR30: + return this.channel3.readSOUND3CNT_L() | 0; +} +GameBoyAdvanceSound.prototype.writeSOUND3CNT8_0 = function (data) { + //NR30: + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel3.writeSOUND3CNT_L(data | 0); + } +} +GameBoyAdvanceSound.prototype.writeSOUND3CNT8_2 = function (data) { + //NR31: + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel3.writeSOUND3CNT_H0(data | 0); + } +} +GameBoyAdvanceSound.prototype.readSOUND3CNT8_3 = function () { + //NR32: + return this.channel3.readSOUND3CNT_H() | 0; +} +GameBoyAdvanceSound.prototype.writeSOUND3CNT8_3 = function (data) { + //NR32: + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel3.writeSOUND3CNT_H1(data | 0); + } +} +GameBoyAdvanceSound.prototype.readSOUND3CNT16_1 = function () { + //NR30: + return this.channel3.readSOUND3CNT_H() << 8; +} +GameBoyAdvanceSound.prototype.readSOUND3CNT32 = function () { + //NR30: + var data = this.channel3.readSOUND3CNT_L() | 0; + //NR32: + data = data | (this.channel3.readSOUND3CNT_H() << 24); + return data | 0; +} +GameBoyAdvanceSound.prototype.writeSOUND3CNT16 = function (data) { + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + //NR31: + this.channel3.writeSOUND3CNT_H0(data | 0); + //NR32: + this.channel3.writeSOUND3CNT_H1(data >> 8); + } +} +GameBoyAdvanceSound.prototype.writeSOUND3CNT32 = function (data) { + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + //NR30: + this.channel3.writeSOUND3CNT_L(data | 0); + //NR31: + this.channel3.writeSOUND3CNT_H0(data >> 16); + //NR32: + this.channel3.writeSOUND3CNT_H1(data >> 24); + } +} +GameBoyAdvanceSound.prototype.writeSOUND3CNTX8_0 = function (data) { + //NR33: + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel3.writeSOUND3CNT_X0(data | 0); + } +} +GameBoyAdvanceSound.prototype.readSOUND3CNTX8 = function () { + //NR34: + return this.channel3.readSOUND3CNT_X() | 0; +} +GameBoyAdvanceSound.prototype.readSOUND3CNTX16 = function () { + //NR34: + return this.channel3.readSOUND3CNT_X() << 8; +} +GameBoyAdvanceSound.prototype.writeSOUND3CNTX8_1 = function (data) { + //NR34: + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel3.writeSOUND3CNT_X1(data | 0); + } +} +GameBoyAdvanceSound.prototype.writeSOUND3CNTX16 = function (data) { + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + //NR33: + this.channel3.writeSOUND3CNT_X0(data | 0); + //NR34: + this.channel3.writeSOUND3CNT_X1(data >> 8); + } +} +GameBoyAdvanceSound.prototype.writeSOUND4CNTL8_0 = function (data) { + //NR41: + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel4.writeSOUND4CNT_L0(data | 0); + } +} +GameBoyAdvanceSound.prototype.writeSOUND4CNTL8_1 = function (data) { + //NR42: + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel4.writeSOUND4CNT_L1(data | 0); + } +} +GameBoyAdvanceSound.prototype.writeSOUND4CNTL16 = function (data) { + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + //NR41: + this.channel4.writeSOUND4CNT_L0(data | 0); + //NR42: + this.channel4.writeSOUND4CNT_L1(data >> 8); + } +} +GameBoyAdvanceSound.prototype.readSOUND4CNTL8 = function () { + //NR42: + return this.channel4.readSOUND4CNT_L() | 0; +} +GameBoyAdvanceSound.prototype.readSOUND4CNTL16 = function () { + //NR42: + return this.channel4.readSOUND4CNT_L() << 8; +} +GameBoyAdvanceSound.prototype.writeSOUND4CNTH8_0 = function (data) { + //NR43: + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel4.writeSOUND4CNT_H0(data | 0); + } +} +GameBoyAdvanceSound.prototype.readSOUND4CNTH8_0 = function () { + //NR43: + return this.channel4.readSOUND4CNT_H0() | 0; +} +GameBoyAdvanceSound.prototype.writeSOUND4CNTH8_1 = function (data) { + //NR44: + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + this.channel4.writeSOUND4CNT_H1(data | 0); + } +} +GameBoyAdvanceSound.prototype.writeSOUND4CNTH16 = function (data) { + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + //NR43: + this.channel4.writeSOUND4CNT_H0(data | 0); + //NR44: + this.channel4.writeSOUND4CNT_H1(data >> 8); + } +} +GameBoyAdvanceSound.prototype.readSOUND4CNTH8_1 = function () { + //NR44: + return this.channel4.readSOUND4CNT_H1() | 0; +} +GameBoyAdvanceSound.prototype.readSOUND4CNTH16 = function () { + //NR43: + var data = this.channel4.readSOUND4CNT_H0() | 0; + //NR44: + data = data | (this.channel4.readSOUND4CNT_H1() << 8); + return data | 0; +} +GameBoyAdvanceSound.prototype.writeSOUNDCNTL8_0 = function (data) { + //NR50: + data = data | 0; + data = data & 0xFF; + if (this.soundMasterEnabled && (this.nr50 | 0) != (data | 0)) { + this.audioPSGJIT(); + this.nr50 = data | 0; + this.VinLeftChannelMasterVolume = (((data >> 4) & 0x07) + 1) | 0; + this.VinRightChannelMasterVolume = ((data & 0x07) + 1) | 0; + } +} +GameBoyAdvanceSound.prototype.readSOUNDCNTL8_0 = function () { + //NR50: + return 0x88 | this.nr50; +} +GameBoyAdvanceSound.prototype.writeSOUNDCNTL8_1 = function (data) { + //NR51: + data = data | 0; + data = data & 0xFF; + if (this.soundMasterEnabled && (this.nr51 | 0) != (data | 0)) { + this.audioPSGJIT(); + this.nr51 = data | 0; + this.channel1.setChannelOutputEnable(data | 0); + this.channel2.setChannelOutputEnable(data | 0); + this.channel3.setChannelOutputEnable(data | 0); + this.channel4.setChannelOutputEnable(data | 0); + } +} +GameBoyAdvanceSound.prototype.readSOUNDCNTL8_1 = function () { + //NR51: + return this.nr51 | 0; +} +GameBoyAdvanceSound.prototype.writeSOUNDCNTL16 = function (data) { + data = data | 0; + if (this.soundMasterEnabled) { + this.audioPSGJIT(); + //NR50: + if ((this.nr50 | 0) != (data & 0xFF)) { + this.nr50 = data & 0xFF; + this.VinLeftChannelMasterVolume = (((data >> 4) & 0x07) + 1) | 0; + this.VinRightChannelMasterVolume = ((data & 0x07) + 1) | 0; + } + data = (data >> 8) & 0xFF; + //NR51: + if ((this.nr51 | 0) != (data | 0)) { + this.nr51 = data | 0; + this.channel1.setChannelOutputEnable(data | 0); + this.channel2.setChannelOutputEnable(data | 0); + this.channel3.setChannelOutputEnable(data | 0); + this.channel4.setChannelOutputEnable(data | 0); + } + } +} +GameBoyAdvanceSound.prototype.readSOUNDCNTL16 = function () { + //NR50: + var data = 0x88 | this.nr50; + //NR51: + data = data | (this.nr51 << 8); + return data | 0; +} +GameBoyAdvanceSound.prototype.writeSOUNDCNTH8_0 = function (data) { + //NR60: + data = data | 0; + this.audioPSGJIT(); + this.CGBOutputRatio = data & 0x3; + this.AGBDirectSoundAShifter = (data & 0x04) >> 2; + this.AGBDirectSoundBShifter = (data & 0x08) >> 3; + this.nr60 = data & 0xFF; +} +GameBoyAdvanceSound.prototype.readSOUNDCNTH8_0 = function () { + //NR60: + return this.nr60 | 0; +} +GameBoyAdvanceSound.prototype.writeSOUNDCNTH8_1 = function (data) { + //NR61: + data = data | 0; + this.audioPSGJIT(); + this.AGBDirectSoundARightCanPlay = ((data & 0x1) != 0); + this.AGBDirectSoundALeftCanPlay = ((data & 0x2) != 0); + this.AGBDirectSoundATimer = (data & 0x4) >> 2; + if ((data & 0x08) != 0) { + this.AGBDirectSoundAFIFOClear(); + } + this.AGBDirectSoundBRightCanPlay = ((data & 0x10) != 0); + this.AGBDirectSoundBLeftCanPlay = ((data & 0x20) != 0); + this.AGBDirectSoundBTimer = (data & 0x40) >> 6; + if ((data & 0x80) != 0) { + this.AGBDirectSoundBFIFOClear(); + } + this.nr61 = data & 0xFF; + this.IOCore.updateCoreClocking(); +} +GameBoyAdvanceSound.prototype.readSOUNDCNTH8_1 = function () { + //NR61: + return this.nr61 | 0; +} +GameBoyAdvanceSound.prototype.writeSOUNDCNTH16 = function (data) { + //NR60: + data = data | 0; + this.audioPSGJIT(); + this.CGBOutputRatio = data & 0x3; + this.AGBDirectSoundAShifter = (data & 0x04) >> 2; + this.AGBDirectSoundBShifter = (data & 0x08) >> 3; + this.nr60 = data & 0xFF; + //NR61: + data = data >> 8; + this.AGBDirectSoundARightCanPlay = ((data & 0x1) != 0); + this.AGBDirectSoundALeftCanPlay = ((data & 0x2) != 0); + this.AGBDirectSoundATimer = (data & 0x4) >> 2; + if ((data & 0x08) != 0) { + this.AGBDirectSoundAFIFOClear(); + } + this.AGBDirectSoundBRightCanPlay = ((data & 0x10) != 0); + this.AGBDirectSoundBLeftCanPlay = ((data & 0x20) != 0); + this.AGBDirectSoundBTimer = (data & 0x40) >> 6; + if ((data & 0x80) != 0) { + this.AGBDirectSoundBFIFOClear(); + } + this.nr61 = data & 0xFF; + this.IOCore.updateCoreClocking(); +} +GameBoyAdvanceSound.prototype.readSOUNDCNTH16 = function () { + //NR60: + var data = this.nr60 | 0; + //NR61: + data = data | (this.nr61 << 8); + return data | 0; +} +GameBoyAdvanceSound.prototype.writeSOUNDCNT32 = function (data) { + data = data | 0; + this.audioPSGJIT(); + if (this.soundMasterEnabled) { + //NR50: + if ((this.nr50 | 0) != (data & 0xFF)) { + this.nr50 = data & 0xFF; + this.VinLeftChannelMasterVolume = (((data >> 4) & 0x07) + 1) | 0; + this.VinRightChannelMasterVolume = ((data & 0x07) + 1) | 0; + } + var data2 = (data >> 8) & 0xFF; + //NR51: + if ((this.nr51 | 0) != (data2 | 0)) { + this.nr51 = data2 | 0; + this.channel1.setChannelOutputEnable(data2 | 0); + this.channel2.setChannelOutputEnable(data2 | 0); + this.channel3.setChannelOutputEnable(data2 | 0); + this.channel4.setChannelOutputEnable(data2 | 0); + } + } + //NR60: + data = data >> 16; + this.CGBOutputRatio = data & 0x3; + this.AGBDirectSoundAShifter = (data & 0x04) >> 2; + this.AGBDirectSoundBShifter = (data & 0x08) >> 3; + this.nr60 = data & 0xFF; + //NR61: + data = data >> 8; + this.AGBDirectSoundARightCanPlay = ((data & 0x1) != 0); + this.AGBDirectSoundALeftCanPlay = ((data & 0x2) != 0); + this.AGBDirectSoundATimer = (data & 0x4) >> 2; + if ((data & 0x08) != 0) { + this.AGBDirectSoundAFIFOClear(); + } + this.AGBDirectSoundBRightCanPlay = ((data & 0x10) != 0); + this.AGBDirectSoundBLeftCanPlay = ((data & 0x20) != 0); + this.AGBDirectSoundBTimer = (data & 0x40) >> 6; + if ((data & 0x80) != 0) { + this.AGBDirectSoundBFIFOClear(); + } + this.nr61 = data & 0xFF; + this.IOCore.updateCoreClocking(); +} +GameBoyAdvanceSound.prototype.readSOUNDCNT32 = function () { + //NR50: + var data = 0x88 | this.nr50; + //NR51: + data = data | (this.nr51 << 8); + //NR60: + data = data | (this.nr60 << 16); + //NR61: + data = data | (this.nr61 << 24); + return data | 0; +} +GameBoyAdvanceSound.prototype.writeSOUNDCNTX8 = function (data) { + //NR52: + data = data | 0; + if (!this.soundMasterEnabled && (data & 0x80) != 0) { + this.audioPSGJIT(); + this.audioEnabled(); + this.IOCore.updateCoreClocking(); + } + else if (this.soundMasterEnabled && (data & 0x80) == 0) { + this.audioPSGJIT(); + this.audioDisabled(); + this.IOCore.updateCoreClocking(); + } +} +GameBoyAdvanceSound.prototype.readSOUNDCNTX8 = function () { + //NR52: + this.audioPSGJIT(); + return this.nr52 | 0; +} +GameBoyAdvanceSound.prototype.writeSOUNDBIAS8_0 = function (data) { + //NR62: + data = data | 0; + this.audioPSGJIT(); + this.mixerSoundBIAS = this.mixerSoundBIAS & 0x300; + this.mixerSoundBIAS = this.mixerSoundBIAS | (data & 0xFF); + this.nr62 = data & 0xFF; +} +GameBoyAdvanceSound.prototype.readSOUNDBIAS8_0 = function () { + //NR62: + return this.nr62 | 0; +} +GameBoyAdvanceSound.prototype.writeSOUNDBIAS8_1 = function (data) { + //NR63: + data = data | 0; + this.audioPSGJIT(); + this.mixerSoundBIAS = this.mixerSoundBIAS & 0xFF; + this.mixerSoundBIAS = this.mixerSoundBIAS | ((data & 0x3) << 8); + this.PWMWidthShadow = 0x200 >> ((data & 0xC0) >> 6); + this.PWMBitDepthMaskShadow = ((this.PWMWidthShadow | 0) - 1) << (1 + ((data & 0xC0) >> 6)); + this.nr63 = data & 0xFF; +} +GameBoyAdvanceSound.prototype.writeSOUNDBIAS16 = function (data) { + //NR62: + data = data | 0; + this.audioPSGJIT(); + this.mixerSoundBIAS = data & 0x3FF; + this.nr62 = data & 0xFF; + //NR63: + this.PWMWidthShadow = 0x200 >> ((data & 0xC000) >> 14); + this.PWMBitDepthMaskShadow = ((this.PWMWidthShadow | 0) - 1) << (1 + ((data & 0xC000) >> 14)); + this.nr63 = (data >> 8) & 0xFF; +} +GameBoyAdvanceSound.prototype.readSOUNDBIAS8_1 = function () { + //NR63: + return this.nr63 | 0; +} +GameBoyAdvanceSound.prototype.readSOUNDBIAS16 = function () { + //NR62: + var data = this.nr62 | 0; + //NR63: + data = data | (this.nr63 << 8); + return data | 0; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/Timer.js b/public/gfiles/gba/IodineGBA/core/Timer.js new file mode 100644 index 00000000..aab4e345 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/Timer.js @@ -0,0 +1,846 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceTimer(IOCore) { + //Build references: + this.IOCore = IOCore; +} +GameBoyAdvanceTimer.prototype.prescalarLookup = [ + 0, + 0x6, + 0x8, + 0xA +]; +GameBoyAdvanceTimer.prototype.initialize = function () { + this.timer0Counter = 0; + this.timer0Reload = 0; + this.timer0Control = 0; + this.timer0Enabled = false; + this.timer0IRQ = false; + this.timer0Precounter = 0; + this.timer0Prescalar = 1; + this.timer0PrescalarShifted = 0; + this.timer1Counter = 0; + this.timer1Reload = 0; + this.timer1Control = 0; + this.timer1Enabled = false; + this.timer1IRQ = false; + this.timer1Precounter = 0; + this.timer1Prescalar = 1; + this.timer1PrescalarShifted = 0; + this.timer1CountUp = false; + this.timer2Counter = 0; + this.timer2Reload = 0; + this.timer2Control = 0; + this.timer2Enabled = false; + this.timer2IRQ = false; + this.timer2Precounter = 0; + this.timer2Prescalar = 1; + this.timer2PrescalarShifted = 0; + this.timer2CountUp = false; + this.timer3Counter = 0; + this.timer3Reload = 0; + this.timer3Control = 0; + this.timer3Enabled = false; + this.timer3IRQ = false; + this.timer3Precounter = 0; + this.timer3Prescalar = 1; + this.timer3PrescalarShifted = 0; + this.timer3CountUp = false; + this.timer1UseMainClocks = false; + this.timer1UseChainedClocks = false; + this.timer2UseMainClocks = false; + this.timer2UseChainedClocks = false; + this.timer3UseMainClocks = false; + this.timer3UseChainedClocks = false; +} +GameBoyAdvanceTimer.prototype.addClocks = function (clocks) { + clocks = clocks | 0; + //See if timer channels 0 and 1 are enabled: + this.clockSoundTimers(clocks | 0); + //See if timer channel 2 is enabled: + this.clockTimer2(clocks | 0); + //See if timer channel 3 is enabled: + this.clockTimer3(clocks | 0); +} +GameBoyAdvanceTimer.prototype.clockSoundTimers = function (audioClocks) { + audioClocks = audioClocks | 0; + for (var predictedClocks = 0, overflowClocks = 0; (audioClocks | 0) > 0; audioClocks = ((audioClocks | 0) - (predictedClocks | 0)) | 0) { + overflowClocks = this.nextAudioTimerOverflow() | 0; + predictedClocks = Math.min(audioClocks | 0, overflowClocks | 0) | 0; + //See if timer channel 0 is enabled: + this.clockTimer0(predictedClocks | 0); + //See if timer channel 1 is enabled: + this.clockTimer1(predictedClocks | 0); + //Clock audio system up to latest timer: + this.IOCore.sound.addClocks(predictedClocks | 0); + //Only jit if overflow was seen: + if ((overflowClocks | 0) == (predictedClocks | 0)) { + this.IOCore.sound.audioJIT(); + } + } +} +GameBoyAdvanceTimer.prototype.clockTimer0 = function (clocks) { + clocks = clocks | 0; + if (this.timer0Enabled) { + this.timer0Precounter = ((this.timer0Precounter | 0) + (clocks | 0)) | 0; + while ((this.timer0Precounter | 0) >= (this.timer0Prescalar | 0)) { + var iterations = Math.min(this.timer0Precounter >> (this.timer0PrescalarShifted | 0), (0x10000 - (this.timer0Counter | 0)) | 0) | 0; + this.timer0Precounter = ((this.timer0Precounter | 0) - ((iterations | 0) << (this.timer0PrescalarShifted | 0))) | 0; + this.timer0Counter = ((this.timer0Counter | 0) + (iterations | 0)) | 0; + if ((this.timer0Counter | 0) > 0xFFFF) { + this.timer0Counter = this.timer0Reload | 0; + this.timer0ExternalTriggerCheck(); + this.timer1ClockUpTickCheck(); + } + } + } +} +GameBoyAdvanceTimer.prototype.clockTimer1 = function (clocks) { + clocks = clocks | 0; + if (this.timer1UseMainClocks) { + this.timer1Precounter = ((this.timer1Precounter | 0) + (clocks | 0)) | 0; + while ((this.timer1Precounter | 0) >= (this.timer1Prescalar | 0)) { + var iterations = Math.min(this.timer1Precounter >> (this.timer1PrescalarShifted | 0), (0x10000 - (this.timer1Counter | 0)) | 0) | 0; + this.timer1Precounter = ((this.timer1Precounter | 0) - ((iterations | 0) << (this.timer1PrescalarShifted | 0))) | 0; + this.timer1Counter = ((this.timer1Counter | 0) + (iterations | 0)) | 0; + if ((this.timer1Counter | 0) > 0xFFFF) { + this.timer1Counter = this.timer1Reload | 0; + this.timer1ExternalTriggerCheck(); + this.timer2ClockUpTickCheck(); + } + } + } +} +GameBoyAdvanceTimer.prototype.clockTimer2 = function (clocks) { + clocks = clocks | 0; + if (this.timer2UseMainClocks) { + this.timer2Precounter = ((this.timer2Precounter | 0) + (clocks | 0)) | 0; + while ((this.timer2Precounter | 0) >= (this.timer2Prescalar | 0)) { + var iterations = Math.min(this.timer2Precounter >> (this.timer2PrescalarShifted | 0), (0x10000 - (this.timer2Counter | 0)) | 0) | 0; + this.timer2Precounter = ((this.timer2Precounter | 0) - ((iterations | 0) << (this.timer2PrescalarShifted | 0))) | 0; + this.timer2Counter = ((this.timer2Counter | 0) + (iterations | 0)) | 0; + if ((this.timer2Counter | 0) > 0xFFFF) { + this.timer2Counter = this.timer2Reload | 0; + this.timer2ExternalTriggerCheck(); + this.timer3ClockUpTickCheck(); + } + } + } +} +GameBoyAdvanceTimer.prototype.clockTimer3 = function (clocks) { + clocks = clocks | 0; + if (this.timer3UseMainClocks) { + this.timer3Precounter = ((this.timer3Precounter | 0) + (clocks | 0)) | 0; + while ((this.timer3Precounter | 0) >= (this.timer3Prescalar | 0)) { + var iterations = Math.min(this.timer3Precounter >> (this.timer3PrescalarShifted | 0), (0x10000 - (this.timer3Counter | 0)) | 0) | 0; + this.timer3Precounter = ((this.timer3Precounter | 0) - ((iterations | 0) << (this.timer3PrescalarShifted | 0))) | 0; + this.timer3Counter = ((this.timer3Counter | 0) + (iterations | 0)) | 0; + if ((this.timer3Counter | 0) > 0xFFFF) { + this.timer3Counter = this.timer3Reload | 0; + this.timer3ExternalTriggerCheck(); + } + } + } +} +GameBoyAdvanceTimer.prototype.timer1ClockUpTickCheck = function () { + if (this.timer1UseChainedClocks) { + this.timer1Counter = ((this.timer1Counter | 0) + 1) | 0; + if ((this.timer1Counter | 0) > 0xFFFF) { + this.timer1Counter = this.timer1Reload | 0; + this.timer1ExternalTriggerCheck(); + this.timer2ClockUpTickCheck(); + } + } +} +GameBoyAdvanceTimer.prototype.timer2ClockUpTickCheck = function () { + if (this.timer2UseChainedClocks) { + this.timer2Counter = ((this.timer2Counter | 0) + 1) | 0; + if ((this.timer2Counter | 0) > 0xFFFF) { + this.timer2Counter = this.timer2Reload | 0; + this.timer2ExternalTriggerCheck(); + this.timer3ClockUpTickCheck(); + } + } +} +GameBoyAdvanceTimer.prototype.timer3ClockUpTickCheck = function () { + if (this.timer3UseChainedClocks) { + this.timer3Counter = ((this.timer3Counter | 0) + 1) | 0; + if ((this.timer3Counter | 0) > 0xFFFF) { + this.timer3Counter = this.timer3Reload | 0; + this.timer3ExternalTriggerCheck(); + } + } +} +GameBoyAdvanceTimer.prototype.timer0ExternalTriggerCheck = function () { + if (this.timer0IRQ) { + this.IOCore.irq.requestIRQ(0x08); + } + this.IOCore.sound.AGBDirectSoundTimer0ClockTick(); +} +GameBoyAdvanceTimer.prototype.timer1ExternalTriggerCheck = function () { + if (this.timer1IRQ) { + this.IOCore.irq.requestIRQ(0x10); + } + this.IOCore.sound.AGBDirectSoundTimer1ClockTick(); +} +GameBoyAdvanceTimer.prototype.timer2ExternalTriggerCheck = function () { + if (this.timer2IRQ) { + this.IOCore.irq.requestIRQ(0x20); + } +} +GameBoyAdvanceTimer.prototype.timer3ExternalTriggerCheck = function () { + if (this.timer3IRQ) { + this.IOCore.irq.requestIRQ(0x40); + } +} +GameBoyAdvanceTimer.prototype.writeTM0CNT8_0 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.IOCore.sound.audioJIT(); + this.timer0Reload = this.timer0Reload & 0xFF00; + data = data & 0xFF; + this.timer0Reload = this.timer0Reload | data; + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM0CNT8_1 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.IOCore.sound.audioJIT(); + this.timer0Reload = this.timer0Reload & 0xFF; + data = data & 0xFF; + this.timer0Reload = this.timer0Reload | (data << 8); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM0CNT8_2 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.IOCore.sound.audioJIT(); + this.timer0Control = data & 0xFF; + if ((data & 0x80) != 0) { + if (!this.timer0Enabled) { + this.timer0Counter = this.timer0Reload | 0; + this.timer0Enabled = true; + this.timer0Precounter = 0; + } + } + else { + this.timer0Enabled = false; + } + this.timer0IRQ = ((data & 0x40) != 0); + this.timer0PrescalarShifted = this.prescalarLookup[data & 0x03] | 0; + this.timer0Prescalar = 1 << (this.timer0PrescalarShifted | 0); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM0CNT16 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.IOCore.sound.audioJIT(); + this.timer0Reload = data & 0xFFFF; + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM0CNT32 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.IOCore.sound.audioJIT(); + this.timer0Reload = data & 0xFFFF; + this.timer0Control = data >> 16; + if ((data & 0x800000) != 0) { + if (!this.timer0Enabled) { + this.timer0Counter = this.timer0Reload | 0; + this.timer0Enabled = true; + this.timer0Precounter = 0; + } + } + else { + this.timer0Enabled = false; + } + this.timer0IRQ = ((data & 0x400000) != 0); + this.timer0PrescalarShifted = this.prescalarLookup[(data >> 16) & 0x03] | 0; + this.timer0Prescalar = 1 << (this.timer0PrescalarShifted | 0); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.readTM0CNT8_0 = function () { + this.IOCore.updateTimerClocking(); + return this.timer0Counter & 0xFF; +} +GameBoyAdvanceTimer.prototype.readTM0CNT8_1 = function () { + this.IOCore.updateTimerClocking(); + return (this.timer0Counter & 0xFF00) >> 8; +} +GameBoyAdvanceTimer.prototype.readTM0CNT8_2 = function () { + return this.timer0Control & 0xFF; +} +GameBoyAdvanceTimer.prototype.readTM0CNT16 = function () { + this.IOCore.updateTimerClocking(); + return this.timer0Counter | 0; +} +GameBoyAdvanceTimer.prototype.readTM0CNT32 = function () { + this.IOCore.updateTimerClocking(); + var data = (this.timer0Control & 0xFF) << 16; + data = data | this.timer0Counter; + return data | 0; +} +GameBoyAdvanceTimer.prototype.writeTM1CNT8_0 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.IOCore.sound.audioJIT(); + this.timer1Reload = this.timer1Reload & 0xFF00; + data = data & 0xFF; + this.timer1Reload = this.timer1Reload | data; + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM1CNT8_1 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.IOCore.sound.audioJIT(); + this.timer1Reload = this.timer1Reload & 0xFF; + data = data & 0xFF; + this.timer1Reload = this.timer1Reload | (data << 8); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM1CNT8_2 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.IOCore.sound.audioJIT(); + this.timer1Control = data & 0xFF; + if ((data & 0x80) != 0) { + if (!this.timer1Enabled) { + this.timer1Counter = this.timer1Reload | 0; + this.timer1Enabled = true; + this.timer1Precounter = 0; + } + } + else { + this.timer1Enabled = false; + } + this.timer1IRQ = ((data & 0x40) != 0); + this.timer1CountUp = ((data & 0x4) != 0); + this.timer1PrescalarShifted = this.prescalarLookup[data & 0x03] | 0; + this.timer1Prescalar = 1 << (this.timer1PrescalarShifted | 0); + this.preprocessTimer1(); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM1CNT16 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.IOCore.sound.audioJIT(); + this.timer1Reload = data & 0xFFFF; + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM1CNT32 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.IOCore.sound.audioJIT(); + this.timer1Reload = data & 0xFFFF; + this.timer1Control = data >> 16; + if ((data & 0x800000) != 0) { + if (!this.timer1Enabled) { + this.timer1Counter = this.timer1Reload | 0; + this.timer1Enabled = true; + this.timer1Precounter = 0; + } + } + else { + this.timer1Enabled = false; + } + this.timer1IRQ = ((data & 0x400000) != 0); + this.timer1CountUp = ((data & 0x40000) != 0); + this.timer1PrescalarShifted = this.prescalarLookup[(data >> 16) & 0x03] | 0; + this.timer1Prescalar = 1 << (this.timer1PrescalarShifted | 0); + this.preprocessTimer1(); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.readTM1CNT8_0 = function () { + this.IOCore.updateTimerClocking(); + return this.timer1Counter & 0xFF; +} +GameBoyAdvanceTimer.prototype.readTM1CNT8_1 = function () { + this.IOCore.updateTimerClocking(); + return (this.timer1Counter & 0xFF00) >> 8; +} +GameBoyAdvanceTimer.prototype.readTM1CNT8_2 = function () { + return this.timer1Control & 0xFF; +} +GameBoyAdvanceTimer.prototype.readTM1CNT16 = function () { + this.IOCore.updateTimerClocking(); + return this.timer1Counter | 0; +} +GameBoyAdvanceTimer.prototype.readTM1CNT32 = function () { + this.IOCore.updateTimerClocking(); + var data = (this.timer1Control & 0xFF) << 16; + data = data | this.timer1Counter; + return data | 0; +} +GameBoyAdvanceTimer.prototype.writeTM2CNT8_0 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.timer2Reload = this.timer2Reload & 0xFF00; + data = data & 0xFF; + this.timer2Reload = this.timer2Reload | data; + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM2CNT8_1 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.timer2Reload = this.timer2Reload & 0xFF; + data = data & 0xFF; + this.timer2Reload = this.timer2Reload | (data << 8); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM2CNT8_2 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.timer2Control = data & 0xFF; + if ((data & 0x80) != 0) { + if (!this.timer2Enabled) { + this.timer2Counter = this.timer2Reload | 0; + this.timer2Enabled = true; + this.timer2Precounter = 0; + } + } + else { + this.timer2Enabled = false; + } + this.timer2IRQ = ((data & 0x40) != 0); + this.timer2CountUp = ((data & 0x4) != 0); + this.timer2PrescalarShifted = this.prescalarLookup[data & 0x03] | 0; + this.timer2Prescalar = 1 << (this.timer2PrescalarShifted | 0); + this.preprocessTimer2(); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM2CNT16 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.timer2Reload = data & 0xFFFF; + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM2CNT32 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.timer2Reload = data & 0xFFFF; + this.timer2Control = data >> 16; + if ((data & 0x800000) != 0) { + if (!this.timer2Enabled) { + this.timer2Counter = this.timer2Reload | 0; + this.timer2Enabled = true; + this.timer2Precounter = 0; + } + } + else { + this.timer2Enabled = false; + } + this.timer2IRQ = ((data & 0x400000) != 0); + this.timer2CountUp = ((data & 0x40000) != 0); + this.timer2PrescalarShifted = this.prescalarLookup[(data >> 16) & 0x03] | 0; + this.timer2Prescalar = 1 << (this.timer2PrescalarShifted | 0); + this.preprocessTimer2(); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.readTM2CNT8_0 = function () { + this.IOCore.updateTimerClocking(); + return this.timer2Counter & 0xFF; +} +GameBoyAdvanceTimer.prototype.readTM2CNT8_1 = function () { + this.IOCore.updateTimerClocking(); + return (this.timer2Counter & 0xFF00) >> 8; +} +GameBoyAdvanceTimer.prototype.readTM2CNT8_2 = function () { + return this.timer2Control & 0xFF; +} +GameBoyAdvanceTimer.prototype.readTM2CNT16 = function () { + this.IOCore.updateTimerClocking(); + return this.timer2Counter | 0; +} +GameBoyAdvanceTimer.prototype.readTM2CNT32 = function () { + this.IOCore.updateTimerClocking(); + var data = (this.timer2Control & 0xFF) << 16; + data = data | this.timer2Counter; + return data | 0; +} +GameBoyAdvanceTimer.prototype.writeTM3CNT8_0 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.timer3Reload = this.timer3Reload & 0xFF00; + data = data & 0xFF; + this.timer3Reload = this.timer3Reload | data; + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM3CNT8_1 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.timer3Reload = this.timer3Reload & 0xFF; + data = data & 0xFF; + this.timer3Reload = this.timer3Reload | (data << 8); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM3CNT8_2 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.timer3Control = data & 0xFF; + if ((data & 0x80) != 0) { + if (!this.timer3Enabled) { + this.timer3Counter = this.timer3Reload | 0; + this.timer3Enabled = true; + this.timer3Precounter = 0; + } + } + else { + this.timer3Enabled = false; + } + this.timer3IRQ = ((data & 0x40) != 0); + this.timer3CountUp = ((data & 0x4) != 0); + this.timer3PrescalarShifted = this.prescalarLookup[data & 0x03] | 0; + this.timer3Prescalar = 1 << (this.timer3PrescalarShifted | 0); + this.preprocessTimer3(); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM3CNT16 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.timer3Reload = data & 0xFFFF; + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.writeTM3CNT32 = function (data) { + data = data | 0; + this.IOCore.updateTimerClocking(); + this.timer3Reload = data & 0xFFFF; + this.timer3Control = data >> 16; + if ((data & 0x800000) != 0) { + if (!this.timer3Enabled) { + this.timer3Counter = this.timer3Reload | 0; + this.timer3Enabled = true; + this.timer3Precounter = 0; + } + } + else { + this.timer3Enabled = false; + } + this.timer3IRQ = ((data & 0x400000) != 0); + this.timer3CountUp = ((data & 0x40000) != 0); + this.timer3PrescalarShifted = this.prescalarLookup[(data >> 16) & 0x03] | 0; + this.timer3Prescalar = 1 << (this.timer3PrescalarShifted | 0); + this.preprocessTimer3(); + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceTimer.prototype.readTM3CNT8_0 = function () { + this.IOCore.updateTimerClocking(); + return this.timer3Counter & 0xFF; +} +GameBoyAdvanceTimer.prototype.readTM3CNT8_1 = function () { + this.IOCore.updateTimerClocking(); + return (this.timer3Counter & 0xFF00) >> 8; +} +GameBoyAdvanceTimer.prototype.readTM3CNT8_2 = function () { + return this.timer3Control & 0xFF; +} +GameBoyAdvanceTimer.prototype.readTM3CNT16 = function () { + this.IOCore.updateTimerClocking(); + return this.timer3Counter | 0; +} +GameBoyAdvanceTimer.prototype.readTM3CNT32 = function () { + this.IOCore.updateTimerClocking(); + var data = (this.timer3Control & 0xFF) << 16; + data = data | this.timer3Counter; + return data | 0; +} +GameBoyAdvanceTimer.prototype.preprocessTimer1 = function () { + this.timer1UseMainClocks = (this.timer1Enabled && !this.timer1CountUp); + this.timer1UseChainedClocks = (this.timer1Enabled && this.timer1CountUp); +} +GameBoyAdvanceTimer.prototype.preprocessTimer2 = function () { + this.timer2UseMainClocks = (this.timer2Enabled && !this.timer2CountUp); + this.timer2UseChainedClocks = (this.timer2Enabled && this.timer2CountUp); +} +GameBoyAdvanceTimer.prototype.preprocessTimer3 = function () { + this.timer3UseMainClocks = (this.timer3Enabled && !this.timer3CountUp); + this.timer3UseChainedClocks = (this.timer3Enabled && this.timer3CountUp); +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceTimer.prototype.nextTimer0OverflowBase = function () { + var countUntilReload = (0x10000 - (this.timer0Counter | 0)) | 0; + countUntilReload = Math.imul(countUntilReload | 0, this.timer0Prescalar | 0) | 0; + countUntilReload = ((countUntilReload | 0) - (this.timer0Precounter | 0)) | 0; + return countUntilReload | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer0OverflowSingle = function () { + var eventTime = 0x7FFFFFFF; + if (this.timer0Enabled) { + eventTime = this.nextTimer0OverflowBase() | 0; + } + return eventTime | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer0Overflow = function (numOverflows) { + numOverflows = numOverflows | 0; + var eventTime = 0x7FFFFFFF; + if (this.timer0Enabled) { + numOverflows = ((numOverflows | 0) - 1) | 0; + var countUntilReload = this.nextTimer0OverflowBase() | 0; + var reloadClocks = (0x10000 - (this.timer0Reload | 0)) | 0; + reloadClocks = Math.imul(reloadClocks | 0, this.timer0Prescalar | 0) | 0; + reloadClocks = (reloadClocks | 0) * (numOverflows | 0); + eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0; + } + return eventTime | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer1OverflowBase = function () { + var countUntilReload = (0x10000 - (this.timer1Counter | 0)) | 0; + countUntilReload = Math.imul(countUntilReload | 0, this.timer1Prescalar | 0) | 0; + countUntilReload = ((countUntilReload | 0) - (this.timer1Precounter | 0)) | 0; + return countUntilReload | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer1Overflow = function (numOverflows) { + numOverflows = numOverflows | 0; + var eventTime = 0x7FFFFFFF; + if (this.timer1Enabled) { + var reloadClocks = (0x10000 - (this.timer1Reload | 0)) | 0; + if (this.timer1CountUp) { + var countUntilReload = (0x10000 - (this.timer1Counter | 0)) | 0; + reloadClocks = (reloadClocks | 0) * (numOverflows | 0); + eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0; + eventTime = this.nextTimer0Overflow(eventTime | 0) | 0; + } + else { + numOverflows = ((numOverflows | 0) - 1) | 0; + var countUntilReload = this.nextTimer1OverflowBase() | 0; + reloadClocks = Math.imul(reloadClocks | 0, this.timer1Prescalar | 0) | 0; + reloadClocks = reloadClocks * numOverflows; + eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0; + } + } + return eventTime | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer1OverflowSingle = function () { + var eventTime = 0x7FFFFFFF; + if (this.timer1Enabled) { + if (this.timer1CountUp) { + var countUntilReload = (0x10000 - (this.timer1Counter | 0)) | 0; + eventTime = this.nextTimer0Overflow(countUntilReload | 0) | 0; + } + else { + eventTime = this.nextTimer1OverflowBase() | 0; + } + } + return eventTime | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer2OverflowBase = function () { + var countUntilReload = (0x10000 - (this.timer2Counter | 0)) | 0; + countUntilReload = Math.imul(countUntilReload | 0, this.timer2Prescalar | 0) | 0; + countUntilReload = ((countUntilReload | 0) - (this.timer2Precounter | 0)) | 0; + return countUntilReload | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer2Overflow = function (numOverflows) { + numOverflows = numOverflows | 0; + var eventTime = 0x7FFFFFFF; + if (this.timer2Enabled) { + var reloadClocks = (0x10000 - (this.timer2Reload | 0)) | 0; + if (this.timer2CountUp) { + var countUntilReload = (0x10000 - (this.timer2Counter | 0)) | 0; + reloadClocks = (reloadClocks | 0) * (numOverflows | 0); + eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0; + eventTime = this.nextTimer1Overflow(eventTime | 0) | 0; + } + else { + numOverflows = ((numOverflows | 0) - 1) | 0; + var countUntilReload = this.nextTimer2OverflowBase() | 0; + reloadClocks = Math.imul(reloadClocks | 0, this.timer2Prescalar | 0) | 0; + reloadClocks = reloadClocks * numOverflows; + eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0; + } + } + return eventTime | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer2OverflowSingle = function () { + var eventTime = 0x7FFFFFFF; + if (this.timer2Enabled) { + if (this.timer2CountUp) { + var countUntilReload = (0x10000 - (this.timer2Counter | 0)) | 0; + eventTime = this.nextTimer1Overflow(countUntilReload | 0) | 0; + } + else { + eventTime = this.nextTimer2OverflowBase() | 0; + } + } + return eventTime | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer3OverflowSingle = function () { + var eventTime = 0x7FFFFFFF; + if (this.timer3Enabled) { + if (this.timer3CountUp) { + var countUntilReload = (0x10000 - (this.timer3Counter | 0)) | 0; + eventTime = this.nextTimer2Overflow(countUntilReload | 0) | 0; + } + else { + eventTime = (0x10000 - (this.timer3Counter | 0)) | 0; + eventTime = Math.imul(eventTime | 0, this.timer3Prescalar | 0) | 0; + eventTime = ((eventTime | 0) - (this.timer3Precounter | 0)) | 0; + } + } + return eventTime | 0; + } +} +else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceTimer.prototype.nextTimer0OverflowBase = function () { + var countUntilReload = (0x10000 - (this.timer0Counter | 0)) | 0; + countUntilReload = ((countUntilReload | 0) * (this.timer0Prescalar | 0)) | 0; + countUntilReload = ((countUntilReload | 0) - (this.timer0Precounter | 0)) | 0; + return countUntilReload | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer0OverflowSingle = function () { + var eventTime = 0x7FFFFFFF; + if (this.timer0Enabled) { + eventTime = this.nextTimer0OverflowBase() | 0; + } + return eventTime | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer0Overflow = function (numOverflows) { + numOverflows = numOverflows | 0; + var eventTime = 0x7FFFFFFF; + if (this.timer0Enabled) { + numOverflows = ((numOverflows | 0) - 1) | 0; + var countUntilReload = this.nextTimer0OverflowBase() | 0; + var reloadClocks = (0x10000 - (this.timer0Reload | 0)) | 0; + reloadClocks = ((reloadClocks | 0) * (this.timer0Prescalar | 0)) | 0; + reloadClocks = (reloadClocks | 0) * (numOverflows | 0); + eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0; + } + return eventTime | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer1OverflowBase = function () { + var countUntilReload = (0x10000 - (this.timer1Counter | 0)) | 0; + countUntilReload = ((countUntilReload | 0) * (this.timer1Prescalar | 0)) | 0; + countUntilReload = ((countUntilReload | 0) - (this.timer1Precounter | 0)) | 0; + return countUntilReload | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer1Overflow = function (numOverflows) { + numOverflows = numOverflows | 0; + var eventTime = 0x7FFFFFFF; + if (this.timer1Enabled) { + var reloadClocks = (0x10000 - (this.timer1Reload | 0)) | 0; + if (this.timer1CountUp) { + var countUntilReload = (0x10000 - (this.timer1Counter | 0)) | 0; + reloadClocks = (reloadClocks | 0) * (numOverflows | 0); + eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0; + eventTime = this.nextTimer0Overflow(eventTime | 0) | 0; + } + else { + numOverflows = ((numOverflows | 0) - 1) | 0; + var countUntilReload = this.nextTimer1OverflowBase() | 0; + reloadClocks = ((reloadClocks | 0) * (this.timer1Prescalar | 0)) | 0; + reloadClocks = reloadClocks * numOverflows; + eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0; + } + } + return eventTime | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer1OverflowSingle = function () { + var eventTime = 0x7FFFFFFF; + if (this.timer1Enabled) { + if (this.timer1CountUp) { + var countUntilReload = (0x10000 - (this.timer1Counter | 0)) | 0; + eventTime = this.nextTimer0Overflow(countUntilReload | 0) | 0; + } + else { + eventTime = this.nextTimer1OverflowBase() | 0; + } + } + return eventTime | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer2OverflowBase = function () { + var countUntilReload = (0x10000 - (this.timer2Counter | 0)) | 0; + countUntilReload = ((countUntilReload | 0) * (this.timer2Prescalar | 0)) | 0; + countUntilReload = ((countUntilReload | 0) - (this.timer2Precounter | 0)) | 0; + return countUntilReload | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer2Overflow = function (numOverflows) { + numOverflows = numOverflows | 0; + var eventTime = 0x7FFFFFFF; + if (this.timer2Enabled) { + var reloadClocks = (0x10000 - (this.timer2Reload | 0)) | 0; + if (this.timer2CountUp) { + var countUntilReload = (0x10000 - (this.timer2Counter | 0)) | 0; + reloadClocks = (reloadClocks | 0) * (numOverflows | 0); + eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0; + eventTime = this.nextTimer1Overflow(eventTime | 0) | 0; + } + else { + numOverflows = ((numOverflows | 0) - 1) | 0; + var countUntilReload = this.nextTimer2OverflowBase() | 0; + reloadClocks = ((reloadClocks | 0) * (this.timer2Prescalar | 0)) | 0; + reloadClocks = reloadClocks * numOverflows; + eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0; + } + } + return eventTime | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer2OverflowSingle = function () { + var eventTime = 0x7FFFFFFF; + if (this.timer2Enabled) { + if (this.timer2CountUp) { + var countUntilReload = (0x10000 - (this.timer2Counter | 0)) | 0; + eventTime = this.nextTimer1Overflow(countUntilReload | 0) | 0; + } + else { + eventTime = this.nextTimer2OverflowBase() | 0; + } + } + return eventTime | 0; + } + GameBoyAdvanceTimer.prototype.nextTimer3OverflowSingle = function () { + var eventTime = 0x7FFFFFFF; + if (this.timer3Enabled) { + if (this.timer3CountUp) { + var countUntilReload = (0x10000 - (this.timer3Counter | 0)) | 0; + eventTime = this.nextTimer2Overflow(countUntilReload | 0) | 0; + } + else { + eventTime = (0x10000 - (this.timer3Counter | 0)) | 0; + eventTime = ((eventTime | 0) * (this.timer3Prescalar | 0)) | 0; + eventTime = ((eventTime | 0) - (this.timer3Precounter | 0)) | 0; + } + } + return eventTime | 0; + } +} +GameBoyAdvanceTimer.prototype.nextAudioTimerOverflow = function () { + var timer0 = this.nextTimer0OverflowSingle() | 0; + var timer1 = this.nextTimer1OverflowSingle() | 0; + return Math.min(timer0 | 0, timer1 | 0) | 0; +} +GameBoyAdvanceTimer.prototype.nextTimer0IRQEventTime = function () { + var clocks = 0x7FFFFFFF; + if (this.timer0Enabled && this.timer0IRQ) { + clocks = this.nextTimer0OverflowSingle() | 0; + } + return clocks | 0; +} +GameBoyAdvanceTimer.prototype.nextTimer1IRQEventTime = function () { + var clocks = 0x7FFFFFFF; + if (this.timer1Enabled && this.timer1IRQ) { + clocks = this.nextTimer1OverflowSingle() | 0; + } + return clocks | 0; +} +GameBoyAdvanceTimer.prototype.nextTimer2IRQEventTime = function () { + var clocks = 0x7FFFFFFF; + if (this.timer2Enabled && this.timer2IRQ) { + clocks = this.nextTimer2OverflowSingle() | 0; + } + return clocks | 0; +} +GameBoyAdvanceTimer.prototype.nextTimer3IRQEventTime = function () { + var clocks = 0x7FFFFFFF; + if (this.timer3Enabled && this.timer3IRQ) { + clocks = this.nextTimer3OverflowSingle() | 0; + } + return clocks | 0; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/Wait.js b/public/gfiles/gba/IodineGBA/core/Wait.js new file mode 100644 index 00000000..2a8d9dd7 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/Wait.js @@ -0,0 +1,484 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceWait(IOCore) { + //Build references: + this.IOCore = IOCore; +} +GameBoyAdvanceWait.prototype.initialize = function () { + this.memory = this.IOCore.memory; + this.cpu = this.IOCore.cpu; + this.WRAMConfiguration = 0xD000020; //WRAM configuration control register current data. + this.WRAMWaitState = 3; //External WRAM wait state. + this.SRAMWaitState = 5; //SRAM wait state. + this.WAITCNT0 = 0; //WAITCNT0 control register data. + this.WAITCNT1 = 0; //WAITCNT1 control register data. + this.POSTBOOT = 0; //POSTBOOT control register data. + this.isRendering = 1; //Are we doing memory during screen draw? + this.isOAMRendering = 1; //Are we doing memory during OAM draw? + this.nonSequential = 0x10; //Non-sequential access bit-flag. + this.buffer = 0; //Tracking of the size of the prebuffer cache. + this.clocks = 0; //Tracking clocks for prebuffer cache. + //Create the wait state address translation cache: + this.waitStateClocks16 = getUint8Array(0x20); + this.waitStateClocks32 = getUint8Array(0x20); + //Wait State 0: + this.setWaitState(0, 0); + //Wait State 1: + this.setWaitState(1, 0); + //Wait State 2: + this.setWaitState(2, 0); + //Initialize out some dynamic references: + this.getROMRead16 = this.getROMRead16NoPrefetch; + this.getROMRead32 = this.getROMRead32NoPrefetch; + this.CPUInternalCyclePrefetch = this.CPUInternalCycleNoPrefetch; + this.CPUInternalSingleCyclePrefetch = this.CPUInternalSingleCycleNoPrefetch; +} +GameBoyAdvanceWait.prototype.getWaitStateFirstAccess = function (data) { + //Get the first access timing: + data = data | 0; + data = data & 0x3; + if ((data | 0) < 0x3) { + data = (5 - (data | 0)) | 0; + } + else { + data = 9; + } + return data | 0; +} +GameBoyAdvanceWait.prototype.getWaitStateSecondAccess = function (region, data) { + //Get the second access timing: + region = region | 0; + data = data | 0; + if ((data & 0x4) == 0) { + data = 0x2 << (region | 0); + data = ((data | 0) + 1) | 0; + } + else { + data = 0x2; + } + return data | 0; +} +GameBoyAdvanceWait.prototype.setWaitState = function (region, data) { + region = region | 0; + data = data | 0; + //Wait State First Access: + var firstAccess = this.getWaitStateFirstAccess(data & 0x3) | 0; + //Wait State Second Access: + var secondAccess = this.getWaitStateSecondAccess(region | 0, data | 0) | 0; + region = region << 1; + //Computing First Access: + //8-16 bit access: + this.waitStateClocks16[0x18 | region] = firstAccess | 0; + this.waitStateClocks16[0x19 | region] = firstAccess | 0; + //32 bit access: + var accessTime = ((firstAccess | 0) + (secondAccess | 0)) | 0; + this.waitStateClocks32[0x18 | region] = accessTime | 0; + this.waitStateClocks32[0x19 | region] = accessTime | 0; + //Computing Second Access: + //8-16 bit access: + this.waitStateClocks16[0x8 | region] = secondAccess | 0; + this.waitStateClocks16[0x9 | region] = secondAccess | 0; + //32 bit access: + this.waitStateClocks32[0x8 | region] = secondAccess << 1; + this.waitStateClocks32[0x9 | region] = secondAccess << 1; +} +GameBoyAdvanceWait.prototype.writeWAITCNT8_0 = function (data) { + data = data | 0; + //Set SRAM Wait State: + if ((data & 0x3) < 0x3) { + this.SRAMWaitState = (5 - (data & 0x3)) | 0; + } + else { + this.SRAMWaitState = 9; + } + data = data & 0xFF; + //Set Wait State 0: + this.setWaitState(0, data >> 2); + //Set Wait State 1: + this.setWaitState(1, data >> 5); + this.WAITCNT0 = data | 0; +} +GameBoyAdvanceWait.prototype.readWAITCNT8_0 = function () { + return this.WAITCNT0 | 0; +} +GameBoyAdvanceWait.prototype.writeWAITCNT8_1 = function (data) { + data = data | 0; + //Set Wait State 2: + this.setWaitState(2, data & 0xFF); + //Set Prefetch Mode: + if ((data & 0x40) == 0) { + //No Prefetch: + this.resetPrebuffer(); + this.getROMRead16 = this.getROMRead16NoPrefetch; + this.getROMRead32 = this.getROMRead32NoPrefetch; + this.CPUInternalCyclePrefetch = this.CPUInternalCycleNoPrefetch; + this.CPUInternalSingleCyclePrefetch = this.CPUInternalSingleCycleNoPrefetch; + } + else { + //Prefetch Enabled: + this.getROMRead16 = this.getROMRead16Prefetch; + this.getROMRead32 = this.getROMRead32Prefetch; + this.CPUInternalCyclePrefetch = this.multiClock; + this.CPUInternalSingleCyclePrefetch = this.singleClock; + } + this.WAITCNT1 = data & 0x5F; +} +GameBoyAdvanceWait.prototype.readWAITCNT8_1 = function () { + return this.WAITCNT1 | 0; +} +GameBoyAdvanceWait.prototype.writeWAITCNT16 = function (data) { + this.writeWAITCNT8_0(data | 0); + this.writeWAITCNT8_1(data >> 8); +} +GameBoyAdvanceWait.prototype.readWAITCNT16 = function () { + var data = this.WAITCNT0 | 0; + data = data | (this.WAITCNT1 << 8); + return data | 0; +} +GameBoyAdvanceWait.prototype.writePOSTBOOT = function (data) { + this.POSTBOOT = data & 0xFF; +} +GameBoyAdvanceWait.prototype.readPOSTBOOT = function () { + return this.POSTBOOT | 0; +} +GameBoyAdvanceWait.prototype.writeHALTCNT = function (data) { + data = data | 0; + this.IOCore.updateCoreSpillRetain(); + //HALT/STOP mode entrance: + if ((data & 0x80) == 0) { + //Halt: + this.IOCore.flagHalt(); + } + else { + //Stop: + this.IOCore.flagStop(); + } +} +GameBoyAdvanceWait.prototype.writeHALT16 = function (data) { + data = data | 0; + this.POSTBOOT = data & 0xFF; + this.IOCore.updateCoreSpillRetain(); + //HALT/STOP mode entrance: + if ((data & 0x8000) == 0) { + //Halt: + this.IOCore.flagHalt(); + } + else { + //Stop: + this.IOCore.flagStop(); + } +} +GameBoyAdvanceWait.prototype.writeConfigureWRAM8 = function (address, data) { + address = address | 0; + data = data | 0; + switch (address & 0x3) { + case 0: + this.memory.remapWRAM(data & 0x21); + this.WRAMConfiguration = (this.WRAMConfiguration & 0xFFFFFF00) | (data & 0xFF); + break; + case 1: + this.WRAMConfiguration = (this.WRAMConfiguration & 0xFFFF00FF) | ((data & 0xFF) << 8); + break; + case 2: + this.WRAMConfiguration = (this.WRAMConfiguration & 0xFF00FFFF) | ((data & 0xFF) << 16); + break; + default: + this.WRAMWaitState = (0x10 - (data & 0xF)) | 0; + this.WRAMConfiguration = (this.WRAMConfiguration & 0xFFFFFF) | (data << 24); + } +} +GameBoyAdvanceWait.prototype.writeConfigureWRAM16 = function (address, data) { + address = address | 0; + data = data | 0; + if ((address & 0x2) == 0) { + this.WRAMConfiguration = (this.WRAMConfiguration & 0xFFFF0000) | (data & 0xFFFF); + this.memory.remapWRAM(data & 0x21); + } + else { + this.WRAMConfiguration = (data << 16) | (this.WRAMConfiguration & 0xFFFF); + this.WRAMWaitState = (0x10 - ((data >> 8) & 0xF)) | 0; + } +} +GameBoyAdvanceWait.prototype.writeConfigureWRAM32 = function (data) { + data = data | 0; + this.WRAMConfiguration = data | 0; + this.WRAMWaitState = (0x10 - ((data >> 24) & 0xF)) | 0; + this.memory.remapWRAM(data & 0x21); +} +GameBoyAdvanceWait.prototype.readConfigureWRAM8 = function (address) { + address = address | 0; + var data = 0; + switch (address & 0x3) { + case 0: + data = this.WRAMConfiguration & 0x2F; + break; + case 3: + data = this.WRAMConfiguration >>> 24; + } + return data | 0; +} +GameBoyAdvanceWait.prototype.readConfigureWRAM16 = function (address) { + address = address | 0; + var data = 0; + if ((address & 0x2) == 0) { + data = this.WRAMConfiguration & 0x2F; + } + else { + data = (this.WRAMConfiguration >> 16) & 0xFF00; + } + return data | 0; +} +GameBoyAdvanceWait.prototype.readConfigureWRAM32 = function () { + return this.WRAMConfiguration & 0xFF00002F; +} +GameBoyAdvanceWait.prototype.CPUInternalCycleNoPrefetch = function (clocks) { + clocks = clocks | 0; + //Clock for idle CPU time: + this.IOCore.updateCore(clocks | 0); + //Prebuffer bug: + this.checkPrebufferBug(); +} +GameBoyAdvanceWait.prototype.CPUInternalSingleCycleNoPrefetch = function () { + //Clock for idle CPU time: + this.IOCore.updateCoreSingle(); + //Not enough time for prebuffer buffering, so skip it. + //Prebuffer bug: + this.checkPrebufferBug(); +} +GameBoyAdvanceWait.prototype.checkPrebufferBug = function () { + //Issue a non-sequential cycle for the next read if we did an I-cycle: + var address = this.cpu.registers[15] | 0; + if ((address | 0) >= 0x8000000 && (address | 0) < 0xE000000) { + this.NonSequentialBroadcast(); + } +} +GameBoyAdvanceWait.prototype.NonSequentialBroadcast = function () { + //Flag as N cycle: + this.nonSequential = 0x10; +} +GameBoyAdvanceWait.prototype.NonSequentialBroadcastClear = function () { + //PC branched: + this.NonSequentialBroadcast(); + this.resetPrebuffer(); +} +GameBoyAdvanceWait.prototype.check128kAlignmentBug = function (address) { + address = address | 0; + if ((address & 0x1FFFF) == 0) { + this.NonSequentialBroadcast(); + } +} +GameBoyAdvanceWait.prototype.multiClock = function (clocks) { + clocks = clocks | 0; + this.IOCore.updateCore(clocks | 0); + var address = this.cpu.registers[15] | 0; + if ((address | 0) >= 0x8000000 && (address | 0) < 0xE000000) { + if ((this.clocks | 0) < 0xFF) { + this.clocks = ((this.clocks | 0) + (clocks | 0)) | 0; + } + } + else { + this.resetPrebuffer(); + } +} +GameBoyAdvanceWait.prototype.singleClock = function () { + this.IOCore.updateCoreSingle(); + var address = this.cpu.registers[15] | 0; + if ((address | 0) >= 0x8000000 && (address | 0) < 0xE000000) { + if ((this.clocks | 0) < 0xFF) { + this.clocks = ((this.clocks | 0) + 1) | 0; + } + } + else { + this.resetPrebuffer(); + } +} +GameBoyAdvanceWait.prototype.addPrebufferSingleClock = function () { + this.clocks = ((this.clocks | 0) + 1) | 0; +} +GameBoyAdvanceWait.prototype.decrementBufferSingle = function () { + this.buffer = ((this.buffer | 0) - 1) | 0; +} +GameBoyAdvanceWait.prototype.decrementBufferDouble = function () { + this.buffer = ((this.buffer | 0) - 2) | 0; +} +GameBoyAdvanceWait.prototype.resetPrebuffer = function () { + //Reset the buffering: + this.clocks = 0; + this.buffer = 0; +} +GameBoyAdvanceWait.prototype.drainOverdueClocks = function () { + if ((this.clocks | 0) > 0 && (this.buffer | 0) < 8) { + var address = this.cpu.registers[15] >>> 24; + //Convert built up clocks to 16 bit word buffer units: + do { + this.clocks = ((this.clocks | 0) - (this.waitStateClocks16[address | 0] | 0)) | 0; + this.buffer = ((this.buffer | 0) + 1) | 0; + } while ((this.clocks | 0) > 0 && (this.buffer | 0) < 8); + //If we're deficient in clocks, fit them in before the access: + if ((this.clocks | 0) < 0) { + this.IOCore.updateCoreNegative(this.clocks | 0); + this.clocks = 0; + } + } +} +GameBoyAdvanceWait.prototype.computeClocks = function (address) { + address = address | 0; + //Convert built up clocks to 16 bit word buffer units: + while ((this.buffer | 0) < 8 && (this.clocks | 0) >= (this.waitStateClocks16[address | 0] | 0)) { + this.clocks = ((this.clocks | 0) - (this.waitStateClocks16[address | 0] | 0)) | 0; + this.buffer = ((this.buffer | 0) + 1) | 0; + } +} +GameBoyAdvanceWait.prototype.drainOverdueClocksCPU = function () { + if ((this.clocks | 0) < 0) { + //Compute "overdue" clocks: + this.IOCore.updateCoreNegative(this.clocks | 0); + this.clocks = 0; + } + else { + //Buffer satiated, clock 1: + this.IOCore.updateCoreSingle(); + } +} +GameBoyAdvanceWait.prototype.doGamePakFetch16 = function (address) { + address = address | 0; + //Fetch 16 bit word into buffer: + this.clocks = ((this.clocks | 0) - (this.waitStateClocks16[address | this.nonSequential] | 0)) | 0; + this.nonSequential = 0; +} +GameBoyAdvanceWait.prototype.doGamePakFetch32 = function (address) { + address = address | 0; + //Fetch 16 bit word into buffer: + this.clocks = ((this.clocks | 0) - (this.waitStateClocks32[address | this.nonSequential] | 0)) | 0; + this.nonSequential = 0; +} +GameBoyAdvanceWait.prototype.getROMRead16Prefetch = function (address) { + //Caching enabled: + address = address | 0; + //Resolve clocks to buffer units: + this.computeClocks(address | 0); + //Need 16 bits minimum buffered: + switch (this.buffer | 0) { + case 0: + //Fetch 16 bit word into buffer: + this.doGamePakFetch16(address | 0); + break; + default: + //Instruction fetch is 1 clock wide minimum: + this.addPrebufferSingleClock(); + //Decrement the buffer: + this.decrementBufferSingle(); + } + //Clock the state: + this.drainOverdueClocksCPU(); +} +GameBoyAdvanceWait.prototype.getROMRead16NoPrefetch = function (address) { + //Caching disabled: + address = address | 0; + this.IOCore.updateCore(this.waitStateClocks16[address | this.nonSequential] | 0); + this.nonSequential = 0; +} +GameBoyAdvanceWait.prototype.getROMRead32Prefetch = function (address) { + //Caching enabled: + address = address | 0; + //Resolve clocks to buffer units: + this.computeClocks(address | 0); + //Need 32 bits minimum buffered: + switch (this.buffer | 0) { + case 0: + //Fetch two 16 bit words into buffer: + this.doGamePakFetch32(address | 0); + break; + case 1: + //Fetch a 16 bit word into buffer: + this.doGamePakFetch16(address | 0); + this.buffer = 0; + break; + default: + //Instruction fetch is 1 clock wide minimum: + this.addPrebufferSingleClock(); + //Decrement the buffer: + this.decrementBufferDouble(); + } + //Clock the state: + this.drainOverdueClocksCPU(); +} +GameBoyAdvanceWait.prototype.getROMRead32NoPrefetch = function (address) { + //Caching disabled: + address = address | 0; + this.IOCore.updateCore(this.waitStateClocks32[address | this.nonSequential] | 0); + this.nonSequential = 0; +} +GameBoyAdvanceWait.prototype.WRAMAccess = function () { + this.multiClock(this.WRAMWaitState | 0); +} +GameBoyAdvanceWait.prototype.WRAMAccess16CPU = function () { + this.IOCore.updateCore(this.WRAMWaitState | 0); +} +GameBoyAdvanceWait.prototype.WRAMAccess32 = function () { + this.multiClock(this.WRAMWaitState << 1); +} +GameBoyAdvanceWait.prototype.WRAMAccess32CPU = function () { + this.IOCore.updateCore(this.WRAMWaitState << 1); +} +GameBoyAdvanceWait.prototype.ROMAccess = function (address) { + address = address | 0; + this.drainOverdueClocks(); + this.check128kAlignmentBug(address | 0); + this.IOCore.updateCore(this.waitStateClocks16[(address >> 24) | this.nonSequential] | 0); + this.nonSequential = 0; +} +GameBoyAdvanceWait.prototype.ROMAccess16CPU = function (address) { + address = address | 0; + this.check128kAlignmentBug(address | 0); + this.getROMRead16(address >> 24); +} +GameBoyAdvanceWait.prototype.ROMAccess32 = function (address) { + address = address | 0; + this.drainOverdueClocks(); + this.check128kAlignmentBug(address | 0); + this.IOCore.updateCore(this.waitStateClocks32[(address >> 24) | this.nonSequential] | 0); + this.nonSequential = 0; +} +GameBoyAdvanceWait.prototype.ROMAccess32CPU = function (address) { + address = address | 0; + this.check128kAlignmentBug(address | 0); + this.getROMRead32(address >> 24); +} +GameBoyAdvanceWait.prototype.SRAMAccess = function () { + this.multiClock(this.SRAMWaitState | 0); +} +GameBoyAdvanceWait.prototype.SRAMAccessCPU = function () { + this.resetPrebuffer(); + this.IOCore.updateCore(this.SRAMWaitState | 0); +} +GameBoyAdvanceWait.prototype.VRAMAccess = function () { + this.multiClock(this.isRendering | 0); +} +GameBoyAdvanceWait.prototype.VRAMAccess16CPU = function () { + this.IOCore.updateCore(this.isRendering | 0); +} +GameBoyAdvanceWait.prototype.VRAMAccess32 = function () { + this.multiClock(this.isRendering << 1); +} +GameBoyAdvanceWait.prototype.VRAMAccess32CPU = function () { + this.IOCore.updateCore(this.isRendering << 1); +} +GameBoyAdvanceWait.prototype.OAMAccess = function () { + this.multiClock(this.isOAMRendering | 0); +} +GameBoyAdvanceWait.prototype.OAMAccessCPU = function () { + this.IOCore.updateCore(this.isOAMRendering | 0); +} +GameBoyAdvanceWait.prototype.updateRenderStatus = function (isRendering, isOAMRendering) { + this.isRendering = isRendering | 0; + this.isOAMRendering = isOAMRendering | 0; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/Worker.js b/public/gfiles/gba/IodineGBA/core/Worker.js new file mode 100644 index 00000000..e3000f6a --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/Worker.js @@ -0,0 +1,300 @@ +"use strict"; +/* + Copyright (C) 2012-2019 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +importScripts("../includes/TypedArrayShim.js"); +importScripts("Cartridge.js"); +importScripts("DMA.js"); +importScripts("Emulator.js"); +importScripts("Graphics.js"); +importScripts("RunLoop.js"); +importScripts("Memory.js"); +importScripts("IRQ.js"); +importScripts("JoyPad.js"); +importScripts("Serial.js"); +importScripts("Sound.js"); +importScripts("Timer.js"); +importScripts("Wait.js"); +importScripts("CPU.js"); +importScripts("Saves.js"); +importScripts("sound/FIFO.js"); +importScripts("sound/Channel1.js"); +importScripts("sound/Channel2.js"); +importScripts("sound/Channel3.js"); +importScripts("sound/Channel4.js"); +importScripts("CPU/ARM.js"); +importScripts("CPU/THUMB.js"); +importScripts("CPU/CPSR.js"); +importScripts("graphics/RendererProxy.js"); +importScripts("graphics/RendererShim.js"); +importScripts("graphics/Renderer.js"); +importScripts("graphics/BGTEXT.js"); +importScripts("graphics/BG2FrameBuffer.js"); +importScripts("graphics/BGMatrix.js"); +importScripts("graphics/AffineBG.js"); +importScripts("graphics/ColorEffects.js"); +importScripts("graphics/Mosaic.js"); +importScripts("graphics/OBJ.js"); +importScripts("graphics/OBJWindow.js"); +importScripts("graphics/Window.js"); +importScripts("graphics/Compositor.js"); +importScripts("memory/DMA0.js"); +importScripts("memory/DMA1.js"); +importScripts("memory/DMA2.js"); +importScripts("memory/DMA3.js"); +importScripts("cartridge/SaveDeterminer.js"); +importScripts("cartridge/SRAM.js"); +importScripts("cartridge/FLASH.js"); +importScripts("cartridge/EEPROM.js"); +importScripts("cartridge/GPIO.js"); +var Iodine = new GameBoyAdvanceEmulator(); +//Save callbacks waiting to be satisfied: +var saveImportPool = []; +//Graphics Buffers: +var gfxBuffers = [getSharedUint8Array(160 * 240 * 3), + getSharedUint8Array(160 * 240 * 3)]; +var gfxCounters = getSharedInt32Array(3); +//Audio Buffers: +var audioBuffer = null; +var audioCounters = null; +var audioBufferSize = 0; +var audioBufferSizeMask = 0; +var audioSamplesRemaining = getSharedInt32Array(1); +//Time Stamp tracking: +var timestamp = getSharedUint32Array(1); +//Interval Timer handle: +var timerHandle = null; +var timerRate = 0; +//Pass the shared array buffers: +try { + postMessage({messageID:0, gfxBuffer1:gfxBuffers[0], gfxBuffer2:gfxBuffers[1], gfxCounters:gfxCounters, audioSamplesRemaining:audioSamplesRemaining, timestamp:timestamp}, [gfxBuffers[0].buffer, gfxBuffers[1].buffer, gfxCounters.buffer, audioSamplesRemaining.buffer, timestamp.buffer]); +} +catch (e) { + postMessage({messageID:0, gfxBuffer1:gfxBuffers[0], gfxBuffer2:gfxBuffers[1], gfxCounters:gfxCounters, audioSamplesRemaining:audioSamplesRemaining, timestamp:timestamp}); +} +//Event decoding: +self.onmessage = function (event) { + var data = event.data; + switch (data.messageID | 0) { + case 0: + Iodine.play(); + break; + case 1: + Iodine.pause(); + break; + case 2: + Iodine.restart(); + break; + case 3: + Iodine.setIntervalRate(+data.payload); + changeTimer(data.payload | 0); + break; + case 4: + Iodine.attachGraphicsFrameHandler(graphicsFrameHandler); + break; + case 5: + Iodine.attachAudioHandler(audioHandler); + break; + case 6: + Iodine.enableAudio(); + break; + case 7: + Iodine.disableAudio(); + break; + case 8: + Iodine.toggleSkipBootROM(!!data.payload); + break; + case 9: + Iodine.toggleDynamicSpeed(!!data.payload); + break; + case 10: + Iodine.attachSpeedHandler(speedHandler); + break; + case 11: + Iodine.keyDown(data.payload | 0); + break; + case 12: + Iodine.keyUp(data.payload | 0); + break; + case 13: + Iodine.incrementSpeed(+data.payload); + break; + case 14: + Iodine.setSpeed(+data.payload); + break; + case 15: + Iodine.attachBIOS(data.payload); + break; + case 16: + Iodine.attachROM(data.payload); + break; + case 17: + Iodine.exportSave(); + break; + case 18: + Iodine.attachSaveExportHandler(saveExportHandler); + break; + case 19: + Iodine.attachSaveImportHandler(saveImportHandler); + break; + case 20: + processSaveImportSuccess(data.payload); + break; + case 21: + processSaveImportFail(); + break; + case 22: + Iodine.toggleOffthreadGraphics(!!data.payload); + break; + case 23: + Iodine.attachPlayStatusHandler(playStatusHandler); + } +} +var graphicsFrameHandler = { + //Function only called if graphics is THIS thread: + copyBuffer:function (swizzledFrame) { + //Push a frame of graphics to the blitter handle: + //Load the counter values: + var start = gfxCounters[0] | 0; //Written by the other thread. + var end = gfxCounters[1] | 0; //Written by this thread. + //Check if buffer is full: + if ((end | 0) == (((start | 0) + 2) | 0)) { + //Skip copying a frame out: + return; + } + //Copy samples into the ring buffer: + //Hardcoded for 2 buffers for a triple buffer effect: + gfxBuffers[end & 0x1].set(swizzledFrame); + //Increment the ending position counter by 1: + //Atomic to commit the counter to memory: + Atomics.store(gfxCounters, 1, ((end | 0) + 1) | 0); + } +}; +//Shim for our audio api: +var audioHandler = { + initialize:function (channels, sampleRate, bufferLimit, call1, call2, call3) { + //Initialize the audio mixer input: + channels = channels | 0; + sampleRate = +sampleRate; + bufferLimit = bufferLimit | 0; + //Generate an audio buffer: + audioBufferSize = ((bufferLimit | 0) * (channels | 0)) | 0; + audioBufferSizeMask = 1; + while ((audioBufferSize | 0) >= (audioBufferSizeMask | 0)) { + audioBufferSizeMask = (audioBufferSizeMask << 1) | 1; + } + audioBufferSize = ((audioBufferSizeMask | 0) + 1) | 0; + audioBuffer = getSharedFloat32Array(audioBufferSize | 0); + audioCounters = getSharedInt32Array(2); + try { + postMessage({messageID:1, channels:channels | 0, sampleRate:+sampleRate, bufferLimit:bufferLimit | 0, audioBuffer:audioBuffer, audioCounters:audioCounters}, [audioBuffer.buffer, audioCounters.buffer]); + } + catch (e) { + postMessage({messageID:1, channels:channels | 0, sampleRate:+sampleRate, bufferLimit:bufferLimit | 0, audioBuffer:audioBuffer, audioCounters:audioCounters}); + } + }, + push:function (buffer, startPos, endPos) { + startPos = startPos | 0; + endPos = endPos | 0; + //Push audio to the audio mixer input handle: + //Load the counter values: + var start = audioCounters[0] | 0; //Written to by the other thread. + var end = audioCounters[1] | 0; //Written by this thread. + var endCorrected = ((end | 0) & (audioBufferSizeMask | 0)) | 0; + var freeBufferSpace = ((end | 0) - (start | 0)) | 0; + freeBufferSpace = ((audioBufferSize | 0) - (freeBufferSpace | 0)) | 0; + var amountToSend = ((endPos | 0) - (startPos | 0)) | 0; + amountToSend = Math.min(amountToSend | 0, freeBufferSpace | 0) | 0; + endPos = ((startPos | 0) + (amountToSend | 0)) | 0; + //Push audio into buffer: + for (; (startPos | 0) < (endPos | 0); startPos = ((startPos | 0) + 1) | 0) { + audioBuffer[endCorrected | 0] = +buffer[startPos | 0]; + endCorrected = ((endCorrected | 0) + 1) | 0; + if ((endCorrected | 0) == (audioBufferSize | 0)) { + endCorrected = 0; + } + } + //Update the cross thread buffering count: + end = ((end | 0) + (amountToSend | 0)) | 0; + //Atomic store to commit writes to memory: + Atomics.store(audioCounters, 1, end | 0); + }, + register:function () { + //Register into the audio mixer: + postMessage({messageID:2}); + }, + unregister:function () { + //Unregister from audio mixer: + postMessage({messageID:3}); + }, + setBufferSpace:function (spaceContain) { + //Ensure buffering minimum levels for the audio: + postMessage({messageID:4, audioBufferContainAmount:spaceContain | 0}); + }, + remainingBuffer:function () { + //Report the amount of audio samples in-flight: + var ringBufferCount = this.remainingBufferShared() | 0; + var audioSysCount = audioSamplesRemaining[0] | 0; + return ((ringBufferCount | 0) + (audioSysCount | 0)) | 0; + }, + remainingBufferShared:function () { + //Reported the sample count left in the shared buffer: + var start = audioCounters[0] | 0; + var end = audioCounters[1] | 0; + var ringBufferCount = ((end | 0) - (start | 0)) | 0; + return ringBufferCount | 0; + } +}; +function saveImportHandler(saveID, saveCallback, noSaveCallback) { + postMessage({messageID:5, saveID:saveID}); + saveImportPool.push([saveCallback, noSaveCallback]); +} +function saveExportHandler(saveID, saveData) { + postMessage({messageID:6, saveID:saveID, saveData:saveData}); +} +function speedHandler(speed) { + postMessage({messageID:7, speed:speed}); +} +function processSaveImportSuccess(saveData) { + saveImportPool.shift()[0](saveData); +} +function processSaveImportFail() { + saveImportPool.shift()[1](); +} +function playStatusHandler(isPlaying) { + isPlaying = isPlaying | 0; + postMessage({messageID:8, playing:(isPlaying | 0)}); + if ((isPlaying | 0) == 0) { + if (timerHandle) { + clearInterval(timerHandle); + timerHandle = null; + } + } + else { + if (!timerHandle) { + initTimer(timerRate | 0); + } + } +} +function changeTimer(rate) { + rate = rate | 0; + if (timerHandle) { + clearInterval(timerHandle); + initTimer(rate | 0); + } + timerRate = rate | 0; +} +function initTimer(rate) { + rate = rate | 0; + if ((rate | 0) > 0) { + timerHandle = setInterval(function() { + Iodine.timerCallback(timestamp[0] >>> 0); + }, rate | 0); + } +} diff --git a/public/gfiles/gba/IodineGBA/core/cartridge/EEPROM.js b/public/gfiles/gba/IodineGBA/core/cartridge/EEPROM.js new file mode 100644 index 00000000..e9b05cc5 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/cartridge/EEPROM.js @@ -0,0 +1,190 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceEEPROMChip(IOCore) { + this.saves = null; + this.largestSizePossible = 0x200; + this.mode = 0; + this.bitsProcessed = 0; + this.address = 0; + this.buffer = getUint8Array(8); + this.IOCore = IOCore; + //Special note to emulator authors: EEPROM command ending bit "0" can also be a "1"... +} +GameBoyAdvanceEEPROMChip.prototype.initialize = function () { + this.allocate(); +} +GameBoyAdvanceEEPROMChip.prototype.allocate = function () { + if (this.saves == null || (this.saves.length | 0) < (this.largestSizePossible | 0)) { + //Allocate the new array: + var newSave = getUint8Array(this.largestSizePossible | 0); + //Init to default value: + for (var index = 0; (index | 0) < (this.largestSizePossible | 0); index = ((index | 0) + 1) | 0) { + newSave[index | 0] = 0xFF; + } + //Copy the old save data out: + if (this.saves != null) { + for (var index = 0; (index | 0) < (this.saves.length | 0); index = ((index | 0) + 1) | 0) { + newSave[index | 0] = this.saves[index | 0] | 0; + } + } + //Assign the new array out: + this.saves = newSave; + } +} +GameBoyAdvanceEEPROMChip.prototype.load = function (save) { + if ((save.length | 0) == 0x200 || (save.length | 0) == 0x2000) { + this.saves = save; + } +} +GameBoyAdvanceEEPROMChip.prototype.read8 = function () { + //Can't do real reading with 8-bit reads: + return 0x1; +} +GameBoyAdvanceEEPROMChip.prototype.read16 = function () { + var data = 1; + switch (this.mode | 0) { + case 0x7: + //Return 4 junk 0 bits: + data = 0; + if ((this.bitsProcessed | 0) < 3) { + //Increment our bits counter: + this.bitsProcessed = ((this.bitsProcessed | 0) + 1) | 0; + } + else { + //Reset our bits counter: + this.bitsProcessed = 0; + //Change mode for the actual reads: + this.mode = 8; + } + break; + case 0x8: + //Return actual serial style data: + var address = ((this.bitsProcessed >> 3) + (this.address | 0)) | 0; + data = (this.saves[address | 0] >> ((0x7 - (this.bitsProcessed & 0x7)) | 0)) & 0x1; + //Check for end of read: + if ((this.bitsProcessed | 0) < 0x3F) { + //Increment our bits counter: + this.bitsProcessed = ((this.bitsProcessed | 0) + 1) | 0; + } + else { + //Finished read and now idle: + this.resetMode(); + } + } + return data | 0; +} +GameBoyAdvanceEEPROMChip.prototype.read32 = function () { + //Can't do real reading with 32-bit reads: + return 0x10001; +} +GameBoyAdvanceEEPROMChip.prototype.write16 = function (data) { + data = data | 0; + data = data & 0x1; + //Writes only work in DMA: + switch (this.mode | 0) { + //Idle Mode: + case 0: + this.mode = data | 0; + break; + //Select Mode: + case 0x1: + this.selectMode(data | 0); + break; + //Address Mode (Write): + case 0x2: + //Address Mode (Read): + case 0x3: + this.addressMode(data | 0); + break; + //Write Mode: + case 0x4: + this.writeMode(data | 0); + break; + //Ending bit of addressing: + case 0x5: + case 0x6: + this.endAddressing(); + break; + //Read Mode: + default: + this.resetMode(); + } +} +GameBoyAdvanceEEPROMChip.prototype.selectMode = function (data) { + data = data | 0; + //Reset our address: + this.address = 0; + //Reset our bits counter: + this.bitsProcessed = 0; + //Read the mode bit: + this.mode = 0x2 | data; +} +GameBoyAdvanceEEPROMChip.prototype.addressMode = function (data) { + data = data | 0; + //Shift in our address bit: + this.address = (this.address << 1) | data; + //Increment our bits counter: + this.bitsProcessed = ((this.bitsProcessed | 0) + 1) | 0; + //Check for how many bits we've shifted in: + switch (this.bitsProcessed | 0) { + //6 bit address mode: + case 0x6: + if ((this.IOCore.dmaChannel3.wordCountShadow | 0) >= (((this.mode | 0) == 2) ? 0x4A : 0xA)) { + this.largestSizePossible = 0x2000; + this.allocate(); + break; + } + //14 bit address mode: + case 0xE: + this.changeModeToActive(); + } +} +GameBoyAdvanceEEPROMChip.prototype.changeModeToActive = function () { + //Ensure the address range: + this.address &= 0x3FF; + //Addressing in units of 8 bytes: + this.address <<= 3; + //Reset our bits counter: + this.bitsProcessed = 0; + //Change to R/W Mode: + this.mode = ((this.mode | 0) + 2) | 0; +} +GameBoyAdvanceEEPROMChip.prototype.writeMode = function (data) { + data = data | 0; + //Push a bit into the buffer: + this.pushBuffer(data | 0); + //Save on last write bit push: + if ((this.bitsProcessed | 0) == 0x40) { + //64 bits buffered, so copy our buffer to the save data: + this.copyBuffer(); + this.mode = 6; + } +} +GameBoyAdvanceEEPROMChip.prototype.pushBuffer = function (data) { + data = data | 0; + //Push a bit through our serial buffer: + var bufferPosition = this.bitsProcessed >> 3; + this.buffer[bufferPosition & 0x7] = ((this.buffer[bufferPosition & 0x7] << 1) & 0xFE) | data; + this.bitsProcessed = ((this.bitsProcessed | 0) + 1) | 0; +} +GameBoyAdvanceEEPROMChip.prototype.copyBuffer = function () { + //Copy 8 bytes from buffer to EEPROM save data starting at address offset: + for (var index = 0; (index | 0) < 8; index = ((index | 0) + 1) | 0) { + this.saves[this.address | index] = this.buffer[index & 0x7] & 0xFF; + } +} +GameBoyAdvanceEEPROMChip.prototype.endAddressing = function () { + this.mode = ((this.mode | 0) + 2) | 0; +} +GameBoyAdvanceEEPROMChip.prototype.resetMode = function () { + //Reset back to idle: + this.mode = 0; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/cartridge/FLASH.js b/public/gfiles/gba/IodineGBA/core/cartridge/FLASH.js new file mode 100644 index 00000000..915c4083 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/cartridge/FLASH.js @@ -0,0 +1,210 @@ +"use strict"; +/* + Copyright (C) 2012-2013 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceFLASHChip(is128, isAteml) { + this.largestSizePossible = (!!is128) ? 0x20000 : 0x10000; + this.notATMEL = !isAteml; + this.saves = null; + this.BANKOffset = 0; + this.flashCommandUnlockStage = 0; + this.flashCommand = 0; + this.writeBytesLeft = 0; +} +GameBoyAdvanceFLASHChip.prototype.initialize = function () { + this.allocate(); +} +GameBoyAdvanceFLASHChip.prototype.allocate = function () { + if (this.saves == null || (this.saves.length | 0) < (this.largestSizePossible | 0)) { + //Allocate the new array: + var newSave = getUint8Array(this.largestSizePossible | 0); + //Init to default value: + for (var index = 0; (index | 0) < (this.largestSizePossible | 0); index = ((index | 0) + 1) | 0) { + newSave[index | 0] = 0xFF; + } + //Copy the old save data out: + if (this.saves != null) { + for (var index = 0; (index | 0) < (this.saves.length | 0); index = ((index | 0) + 1) | 0) { + newSave[index | 0] = this.saves[index | 0] | 0; + } + } + //Assign the new array out: + this.saves = newSave; + } +} +GameBoyAdvanceFLASHChip.prototype.load = function (save) { + if ((save.length | 0) == 0x10000 || (save.length | 0) == 0x20000) { + this.saves = save; + if ((save.length | 0) == 0x20000) { + this.notATMEL = true; + } + } +} +GameBoyAdvanceFLASHChip.prototype.read = function (address) { + address = address | 0; + var data = 0; + if ((this.flashCommand | 0) != 2 || (address | 0) > 1) { + data = this.saves[address | this.BANKOffset] | 0; + } + else { + if ((address | 0) == 0) { + if (this.notATMEL) { + data = ((this.largestSizePossible | 0) == 0x20000) ? 0x62 : 0xBF; + } + else { + data = 0x1F; + } + } + else { + if (this.notATMEL) { + data = ((this.largestSizePossible | 0) == 0x20000) ? 0x13 : 0xD4; + } + else { + data = 0x3D; + } + } + } + return data | 0; +} +GameBoyAdvanceFLASHChip.prototype.write = function (address, data) { + address = address | 0; + data = data | 0; + switch (this.writeBytesLeft | 0) { + case 0: + this.writeControlBits(address | 0, data | 0); + break; + case 0x80: + var addressToErase = (address & 0xFF80) | this.BANKOffset; + for (var index = 0; (index | 0) < 0x80; index = ((index | 0) + 1) | 0) { + this.saves[addressToErase | index] = 0xFF; + } + default: + this.writeByte(address | 0, data | 0); + + } +} +GameBoyAdvanceFLASHChip.prototype.writeControlBits = function (address, data) { + address = address | 0; + data = data | 0; + switch (address | 0) { + case 0: + this.sectorEraseOrBankSwitch(address | 0, data | 0); + break; + case 0x5555: + this.controlWriteStage2(data | 0); + break; + case 0x2AAA: + this.controlWriteStageIncrement(data | 0); + break; + default: + this.sectorErase(address | 0, data | 0); + } +} +GameBoyAdvanceFLASHChip.prototype.writeByte = function (address, data) { + address = address | 0; + data = data | 0; + this.saves[address | this.BANKOffset] = data | 0; + this.writeBytesLeft = ((this.writeBytesLeft | 0) - 1) | 0; +} +GameBoyAdvanceFLASHChip.prototype.selectBank = function (bankNumber) { + bankNumber = bankNumber | 0; + this.BANKOffset = (bankNumber & 0x1) << 16; + this.largestSizePossible = Math.max((0x10000 + (this.BANKOffset | 0)) | 0, this.largestSizePossible | 0) | 0; + this.notATMEL = true; + this.allocate(); +} +GameBoyAdvanceFLASHChip.prototype.controlWriteStage2 = function (data) { + data = data | 0; + if ((data | 0) == 0xAA) { + //Initial Command: + this.flashCommandUnlockStage = 1; + } + else if ((this.flashCommandUnlockStage | 0) == 2) { + switch (data | 0) { + case 0x10: + //Command Erase Chip: + if ((this.flashCommand | 0) == 1) { + for (var index = 0; (index | 0) < (this.largestSizePossible | 0); index = ((index | 0) + 1) | 0) { + this.saves[index | 0] = 0xFF; + } + } + this.flashCommand = 0; + break; + case 0x80: + //Command Erase: + this.flashCommand = 1; + break; + case 0x90: + //Command ID: + this.flashCommand = 2; + break; + case 0xA0: + //Command Write: + this.writeCommandTrigger(); + break; + case 0xB0: + //Command Bank Switch: + this.flashCommand = 3; + break; + default: + this.flashCommand = 0; + } + //Reset the command state: + this.flashCommandUnlockStage = 0; + } + else if ((data | 0) == 0xF0) { + //Command Clear: + this.flashCommand = 0; + this.flashCommandUnlockStage = 0; + this.notATMEL = true; + } +} +GameBoyAdvanceFLASHChip.prototype.writeCommandTrigger = function () { + if ((this.flashCommandUnlockStage | 0) == 2) { + if (this.notATMEL) { + this.writeBytesLeft = 1; + } + else { + this.writeBytesLeft = 0x80; + } + } +} +GameBoyAdvanceFLASHChip.prototype.sectorErase = function (address, data) { + address = (address << 12) >> 12; + data = data | 0; + if ((this.flashCommand | 0) == 1 && (this.flashCommandUnlockStage | 0) == 2 && ((data | 0) == 0x30)) { + var addressEnd = ((address | this.BANKOffset) + 0x1000) | 0; + for (var index = address | this.BANKOffset; (index | 0) < (addressEnd | 0); index = ((index | 0) + 1) | 0) { + this.saves[index | 0] = 0xFF; + } + this.notATMEL = true; + } + this.flashCommand = 0; + this.flashCommandUnlockStage = 0; +} +GameBoyAdvanceFLASHChip.prototype.sectorEraseOrBankSwitch = function (address, data) { + address = address | 0; + data = data | 0; + if ((this.flashCommandUnlockStage | 0) == 2) { + this.sectorErase(address | 0, data | 0); + } + else if ((this.flashCommand | 0) == 3 && (this.flashCommandUnlockStage | 0) == 0) { + this.selectBank(data & 0x1); + } + this.flashCommand = 0; + this.flashCommandUnlockStage = 0; +} +GameBoyAdvanceFLASHChip.prototype.controlWriteStageIncrement = function (data) { + if ((data | 0) == 0x55 && (this.flashCommandUnlockStage | 0) == 1) { + this.flashCommandUnlockStage = ((this.flashCommandUnlockStage | 0) + 1) | 0; + } + else { + this.flashCommandUnlockStage = 0; + } +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/cartridge/GPIO.js b/public/gfiles/gba/IodineGBA/core/cartridge/GPIO.js new file mode 100644 index 00000000..15c4f7e0 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/cartridge/GPIO.js @@ -0,0 +1,56 @@ +"use strict"; +/* + Copyright (C) 2012-2016 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceGPIOChip() { + this.type = 0; + this.data = 0; + this.direction = 0; + this.readWrite = 0; +} +GameBoyAdvanceGPIOChip.prototype.getType = function () { + return this.type | 0; +} +GameBoyAdvanceGPIOChip.prototype.setType = function (type) { + type = type | 0; + this.type = type | 0; +} +GameBoyAdvanceGPIOChip.prototype.read = function (address) { + address = address | 0; + var data = 0; + if (this.readWrite | 0) { + switch (address & 0xF) { + case 0x4: + this.readTick(); + data = this.data | 0; + break; + case 0x6: + data = this.direction | 0; + break; + case 0x8: + data = this.readWrite | 0; + } + } + return data | 0; +} +GameBoyAdvanceGPIOChip.prototype.write = function (address, data) { + address = address | 0; + data = data | 0; + switch (address & 0xF) { + case 0x4: + this.data = data & 0xF; + this.writeTick(data | 0); + break; + case 0x6: + this.direction = data & 0xF; + break; + case 0x8: + this.readWrite = data & 0x1; + } +} diff --git a/public/gfiles/gba/IodineGBA/core/cartridge/SRAM.js b/public/gfiles/gba/IodineGBA/core/cartridge/SRAM.js new file mode 100644 index 00000000..0c48ad26 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/cartridge/SRAM.js @@ -0,0 +1,85 @@ +"use strict"; +/* + Copyright (C) 2012-2013 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceSRAMChip() { + this.saves = null; + this.TILTChip = null; + this.TILTChipUnlocked = 0; +} +GameBoyAdvanceSRAMChip.prototype.initialize = function () { + if (this.saves == null || (this.saves.length | 0) != 0x8000) { + this.saves = getUint8Array(0x8000); + } +} +GameBoyAdvanceSRAMChip.prototype.load = function (save) { + if ((save.length | 0) == 0x8000) { + this.saves = save; + } +} +GameBoyAdvanceSRAMChip.prototype.read = function (address) { + address = address | 0; + var data = 0; + if ((address | 0) < 0x8000 || (this.TILTChipUnlocked | 0) != 3) { + data = this.saves[address & 0x7FFF] | 0; + } + else { + switch (address | 0) { + case 0x8200: + data = this.TILTChip.readXLow() | 0; + break; + case 0x8300: + data = this.TILTChip.readXHigh() | 0; + break; + case 0x8400: + data = this.TILTChip.readYLow() | 0; + break; + case 0x8500: + data = this.TILTChip.readYHigh() | 0; + break; + default: + data = this.saves[address & 0x7FFF] | 0; + } + } + return data | 0; +} +GameBoyAdvanceSRAMChip.prototype.write = function (address, data) { + address = address | 0; + data = data | 0; + if ((address | 0) < 0x8000 || (this.TILTChipUnlocked | 0) >= 4) { + //Normal SRAM write: + this.saves[address & 0x7FFF] = data | 0; + } + else { + switch (address | 0) { + case 0x8000: + if ((data | 0) == 0x55) { //Magic Combo. + this.TILTChipUnlocked |= 0x1; //Tilt unlock stage 1. + } + else { + this.TILTChipUnlocked |= 0x4; //Definitely not using a tilt sensor. + } + break; + case 0x8100: + if ((data | 0) == 0xAA) { //Magic Combo. + this.TILTChipUnlocked |= 0x2; //Tilt unlock stage 2. + } + else { + this.TILTChipUnlocked |= 0x4; //Definitely not using a tilt sensor. + } + break; + default: + //Check for mirroring while not tilt chip: + if ((this.TILTChipUnlocked | 0) == 0) { + this.saves[address & 0x7FFF] = data | 0; + this.TILTChipUnlocked |= 0x4; //Definitely not using a tilt sensor. + } + } + } +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/cartridge/SaveDeterminer.js b/public/gfiles/gba/IodineGBA/core/cartridge/SaveDeterminer.js new file mode 100644 index 00000000..ef51b245 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/cartridge/SaveDeterminer.js @@ -0,0 +1,158 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceSaveDeterminer(saveCore) { + this.saves = null; + this.saveCore = saveCore; + this.possible = 0x7; +} +GameBoyAdvanceSaveDeterminer.prototype.flags = { + SRAM: 1, + FLASH: 2, + EEPROM: 4 +} +GameBoyAdvanceSaveDeterminer.prototype.initialize = function () { + +} +GameBoyAdvanceSaveDeterminer.prototype.load = function (save) { + this.saves = save; + var length = save.length | 0; + switch (length | 0) { + case 0x200: + case 0x2000: + this.possible = this.flags.EEPROM | 0; + break; + case 0x8000: + this.possible = this.flags.SRAM | 0; + break; + case 0x10000: + case 0x20000: + this.possible = this.flags.FLASH | 0; + } + this.checkDetermination(); +} +GameBoyAdvanceSaveDeterminer.prototype.checkDetermination = function () { + switch (this.possible) { + case 0x1: + this.saveCore.referenceSave(0x1); + break; + case 0x2: + this.saveCore.referenceSave(0x2); + break; + case 0x4: + this.saveCore.referenceSave(0x3); + } +} +GameBoyAdvanceSaveDeterminer.prototype.readSRAM = function (address) { + address = address | 0; + var data = 0; + //Is not EEPROM: + this.possible &= ~this.flags.EEPROM; + if (this.saves != null) { + if ((this.possible & this.flags.FLASH) == (this.flags.FLASH | 0) || (this.possible & this.flags.SRAM) == (this.flags.SRAM | 0)) { + //Read is the same between SRAM and FLASH for the most part: + data = this.saves[(address | 0) % (this.saves.length | 0)] | 0; + } + } + return data | 0; +} +GameBoyAdvanceSaveDeterminer.prototype.writeGPIO8 = function (address, data) { + address = address | 0; + data = data | 0; + //GPIO (TODO): +} +GameBoyAdvanceSaveDeterminer.prototype.writeGPIO16 = function (address, data) { + address = address | 0; + data = data | 0; + //GPIO (TODO): +} +GameBoyAdvanceSaveDeterminer.prototype.writeGPIO32 = function (address, data) { + address = address | 0; + data = data | 0; + //GPIO (TODO): +} +GameBoyAdvanceSaveDeterminer.prototype.writeEEPROM16 = function (address, data) { + address = address | 0; + data = data | 0; + if ((this.possible & this.flags.EEPROM) == (this.flags.EEPROM | 0)) { + //EEPROM: + this.possible = this.flags.EEPROM | 0; + this.checkDetermination(); + this.saveCore.writeEEPROM16(address | 0, data | 0); + } +} +GameBoyAdvanceSaveDeterminer.prototype.readEEPROM8 = function (address) { + address = address | 0; + var data = 0; + if ((this.possible & this.flags.EEPROM) == (this.flags.EEPROM | 0)) { + //EEPROM: + this.possible = this.flags.EEPROM | 0; + this.checkDetermination(); + return this.saveCore.readEEPROM8(address | 0) | 0; + } +} +GameBoyAdvanceSaveDeterminer.prototype.readEEPROM16 = function (address) { + address = address | 0; + var data = 0; + if ((this.possible & this.flags.EEPROM) == (this.flags.EEPROM | 0)) { + //EEPROM: + this.possible = this.flags.EEPROM | 0; + this.checkDetermination(); + return this.saveCore.readEEPROM16(address | 0) | 0; + } +} +GameBoyAdvanceSaveDeterminer.prototype.readEEPROM32 = function (address) { + address = address | 0; + var data = 0; + if ((this.possible & this.flags.EEPROM) == (this.flags.EEPROM | 0)) { + //EEPROM: + this.possible = this.flags.EEPROM | 0; + this.checkDetermination(); + return this.saveCore.readEEPROM32(address | 0) | 0; + } +} +GameBoyAdvanceSaveDeterminer.prototype.writeSRAM = function (address, data) { + address = address | 0; + data = data | 0; + //Is not EEPROM: + this.possible &= ~this.flags.EEPROM; + if ((this.possible & this.flags.FLASH) == (this.flags.FLASH | 0)) { + if ((this.possible & this.flags.SRAM) == (this.flags.SRAM | 0)) { + if ((address | 0) == 0x5555) { + if ((data | 0) == 0xAA) { + //FLASH + this.possible = this.flags.FLASH | 0; + } + else { + //SRAM + this.possible = this.flags.SRAM | 0; + } + } + } + else { + if ((address | 0) == 0x5555) { + if ((data | 0) == 0xAA) { + //FLASH + this.possible = this.flags.FLASH | 0; + } + else { + //Is not Flash: + this.possible &= ~this.flags.FLASH; + } + } + } + } + else if ((this.possible & this.flags.SRAM) == (this.flags.SRAM | 0)) { + //SRAM + this.possible = this.flags.SRAM | 0; + } + this.checkDetermination(); + this.saveCore.writeSRAMIfDefined(address | 0, data | 0); +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/graphics/AffineBG.js b/public/gfiles/gba/IodineGBA/core/graphics/AffineBG.js new file mode 100644 index 00000000..d3c82076 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/graphics/AffineBG.js @@ -0,0 +1,392 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceAffineBGRenderer(gfx, BGLayer) { + BGLayer = BGLayer | 0; + this.gfx = gfx; + this.BGLayer = BGLayer | 0; + this.offset = ((this.BGLayer << 8) + 0x100) | 0; +} +if (__VIEWS_SUPPORTED__) { + GameBoyAdvanceAffineBGRenderer.prototype.initialize = function () { + this.bg2MatrixRenderer = this.gfx.bg2MatrixRenderer; + this.bg3MatrixRenderer = this.gfx.bg3MatrixRenderer; + this.bg2FrameBufferRenderer = this.gfx.bg2FrameBufferRenderer; + this.scratchBuffer = getInt32ViewCustom(this.gfx.buffer, this.offset | 0, ((this.offset | 0) + 240) | 0); + this.BGdx = 0x100; + this.BGdmx = 0; + this.BGdy = 0; + this.BGdmy = 0x100; + this.BGReferenceX = 0; + this.BGReferenceY = 0; + this.pb = 0; + this.pd = 0; + this.doMosaic = 0; + this.priorityPreprocess(0); + this.offsetReferenceCounters(); + } + if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2M = function (line) { + line = line | 0; + var x = this.pb | 0; + var y = this.pd | 0; + if ((this.doMosaic | 0) != 0) { + //Correct line number for mosaic: + var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line | 0) | 0; + x = ((x | 0) - Math.imul(this.BGdmx | 0, mosaicY | 0)) | 0; + y = ((y | 0) - Math.imul(this.BGdmy | 0, mosaicY | 0)) | 0; + } + for (var position = 0; (position | 0) < 240; position = ((position | 0) + 1) | 0, x = ((x | 0) + (this.BGdx | 0)) | 0, y = ((y | 0) + (this.BGdy | 0)) | 0) { + //Fetch pixel: + this.scratchBuffer[position | 0] = this.priorityFlag | this.bg2MatrixRenderer.fetchPixel(x >> 8, y >> 8); + } + if ((this.doMosaic | 0) != 0) { + //Pixelize the line horizontally: + this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset | 0); + } + } + GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine3M = function (line) { + line = line | 0; + var x = this.pb | 0; + var y = this.pd | 0; + if ((this.doMosaic | 0) != 0) { + //Correct line number for mosaic: + var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line | 0) | 0; + x = ((x | 0) - Math.imul(this.BGdmx | 0, mosaicY | 0)) | 0; + y = ((y | 0) - Math.imul(this.BGdmy | 0, mosaicY | 0)) | 0; + } + for (var position = 0; (position | 0) < 240; position = ((position | 0) + 1) | 0, x = ((x | 0) + (this.BGdx | 0)) | 0, y = ((y | 0) + (this.BGdy | 0)) | 0) { + //Fetch pixel: + this.scratchBuffer[position | 0] = this.priorityFlag | this.bg3MatrixRenderer.fetchPixel(x >> 8, y >> 8); + } + if ((this.doMosaic | 0) != 0) { + //Pixelize the line horizontally: + this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset | 0); + } + } + GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2F = function (line) { + line = line | 0; + var x = this.pb | 0; + var y = this.pd | 0; + if ((this.doMosaic | 0) != 0) { + //Correct line number for mosaic: + var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line | 0) | 0; + x = ((x | 0) - Math.imul(this.BGdmx | 0, mosaicY | 0)) | 0; + y = ((y | 0) - Math.imul(this.BGdmy | 0, mosaicY | 0)) | 0; + } + for (var position = 0; (position | 0) < 240; position = ((position | 0) + 1) | 0, x = ((x | 0) + (this.BGdx | 0)) | 0, y = ((y | 0) + (this.BGdy | 0)) | 0) { + //Fetch pixel: + this.scratchBuffer[position | 0] = this.priorityFlag | this.bg2FrameBufferRenderer.fetchPixel(x >> 8, y >> 8); + } + if ((this.doMosaic | 0) != 0) { + //Pixelize the line horizontally: + this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset | 0); + } + } + GameBoyAdvanceAffineBGRenderer.prototype.offsetReferenceCounters = function () { + var end = this.gfx.lastUnrenderedLine | 0; + this.pb = Math.imul(((this.pb | 0) + (this.BGdmx | 0)) | 0, end | 0) | 0; + this.pd = Math.imul(((this.pd | 0) + (this.BGdmy | 0)) | 0, end | 0) | 0; + } + } + else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2M = function (line) { + var x = this.pb; + var y = this.pd; + if (this.doMosaic != 0) { + //Correct line number for mosaic: + var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line); + x -= this.BGdmx * mosaicY; + y -= this.BGdmy * mosaicY; + } + for (var position = 0; position < 240; ++position, x += this.BGdx, y += this.BGdy) { + //Fetch pixel: + this.scratchBuffer[position] = this.priorityFlag | this.bg2MatrixRenderer.fetchPixel(x >> 8, y >> 8); + } + if (this.doMosaic != 0) { + //Pixelize the line horizontally: + this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset); + } + } + GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine3M = function (line) { + var x = this.pb; + var y = this.pd; + if (this.doMosaic != 0) { + //Correct line number for mosaic: + var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line); + x -= this.BGdmx * mosaicY; + y -= this.BGdmy * mosaicY; + } + for (var position = 0; position < 240; ++position, x += this.BGdx, y += this.BGdy) { + //Fetch pixel: + this.scratchBuffer[position] = this.priorityFlag | this.bg3MatrixRenderer.fetchPixel(x >> 8, y >> 8); + } + if (this.doMosaic != 0) { + //Pixelize the line horizontally: + this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset); + } + } + GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2F = function (line) { + var x = this.pb; + var y = this.pd; + if (this.doMosaic != 0) { + //Correct line number for mosaic: + var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line); + x -= this.BGdmx * mosaicY; + y -= this.BGdmy * mosaicY; + } + for (var position = 0; position < 240; ++position, x += this.BGdx, y += this.BGdy) { + //Fetch pixel: + this.scratchBuffer[position] = this.priorityFlag | this.bg2FrameBufferRenderer.fetchPixel(x >> 8, y >> 8); + } + if (this.doMosaic != 0) { + //Pixelize the line horizontally: + this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset); + } + } + GameBoyAdvanceAffineBGRenderer.prototype.offsetReferenceCounters = function () { + var end = this.gfx.lastUnrenderedLine | 0; + this.pb = (((this.pb | 0) + (this.BGdmx | 0)) * (end | 0)) | 0; + this.pd = (((this.pd | 0) + (this.BGdmy | 0)) * (end | 0)) | 0; + } + } +} +else { + GameBoyAdvanceAffineBGRenderer.prototype.initialize = function () { + this.bg2MatrixRenderer = this.gfx.bg2MatrixRenderer; + this.bg3MatrixRenderer = this.gfx.bg3MatrixRenderer; + this.bg2FrameBufferRenderer = this.gfx.bg2FrameBufferRenderer; + this.scratchBuffer = this.gfx.buffer; + this.BGdx = 0x100; + this.BGdmx = 0; + this.BGdy = 0; + this.BGdmy = 0x100; + this.BGReferenceX = 0; + this.BGReferenceY = 0; + this.pb = 0; + this.pd = 0; + this.doMosaic = 0; + this.priorityPreprocess(0); + this.offsetReferenceCounters(); + } + GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2M = function (line) { + var x = this.pb; + var y = this.pd; + if (this.doMosaic != 0) { + //Correct line number for mosaic: + var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line); + x -= this.BGdmx * mosaicY; + y -= this.BGdmy * mosaicY; + } + for (var position = 0; position < 240; ++position, x += this.BGdx, y += this.BGdy) { + //Fetch pixel: + this.scratchBuffer[this.offset + position] = this.priorityFlag | this.bg2MatrixRenderer.fetchPixel(x >> 8, y >> 8); + } + if (this.doMosaic != 0) { + //Pixelize the line horizontally: + this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset); + } + } + GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine3M = function (line) { + var x = this.pb; + var y = this.pd; + if (this.doMosaic != 0) { + //Correct line number for mosaic: + var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line); + x -= this.BGdmx * mosaicY; + y -= this.BGdmy * mosaicY; + } + for (var position = 0; position < 240; ++position, x += this.BGdx, y += this.BGdy) { + //Fetch pixel: + this.scratchBuffer[this.offset + position] = this.priorityFlag | this.bg3MatrixRenderer.fetchPixel(x >> 8, y >> 8); + } + if (this.doMosaic != 0) { + //Pixelize the line horizontally: + this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset); + } + } + GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2F = function (line) { + var x = this.pb; + var y = this.pd; + if (this.doMosaic != 0) { + //Correct line number for mosaic: + var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line); + x -= this.BGdmx * mosaicY; + y -= this.BGdmy * mosaicY; + } + for (var position = 0; position < 240; ++position, x += this.BGdx, y += this.BGdy) { + //Fetch pixel: + this.scratchBuffer[this.offset + position] = this.priorityFlag | this.bg2FrameBufferRenderer.fetchPixel(x >> 8, y >> 8); + } + if (this.doMosaic != 0) { + //Pixelize the line horizontally: + this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset); + } + } + GameBoyAdvanceAffineBGRenderer.prototype.offsetReferenceCounters = function () { + var end = this.gfx.lastUnrenderedLine | 0; + this.pb = (((this.pb | 0) + (this.BGdmx | 0)) * (end | 0)) | 0; + this.pd = (((this.pd | 0) + (this.BGdmy | 0)) * (end | 0)) | 0; + } +} +GameBoyAdvanceAffineBGRenderer.prototype.incrementReferenceCounters = function () { + this.pb = ((this.pb | 0) + (this.BGdmx | 0)) | 0; + this.pd = ((this.pd | 0) + (this.BGdmy | 0)) | 0; +} +GameBoyAdvanceAffineBGRenderer.prototype.resetReferenceCounters = function () { + this.pb = this.BGReferenceX | 0; + this.pd = this.BGReferenceY | 0; +} +GameBoyAdvanceAffineBGRenderer.prototype.setMosaicEnable = function (doMosaic) { + doMosaic = doMosaic | 0; + this.doMosaic = doMosaic | 0; +} +GameBoyAdvanceAffineBGRenderer.prototype.priorityPreprocess = function (BGPriority) { + BGPriority = BGPriority | 0; + this.priorityFlag = (BGPriority << 23) | (1 << (this.BGLayer | 0x10)); +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGPA8_0 = function (data) { + data = data | 0; + this.BGdx = (this.BGdx & 0xFFFFFF00) | data; +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGPA8_1 = function (data) { + data = data | 0; + data = (data << 24) >> 16; + this.BGdx = data | (this.BGdx & 0xFF); +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGPA16 = function (data) { + data = data | 0; + this.BGdx = (data << 16) >> 16; +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGPB8_0 = function (data) { + data = data | 0; + this.BGdmx = (this.BGdmx & 0xFFFFFF00) | data; +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGPB8_1 = function (data) { + data = data | 0; + data = (data << 24) >> 16; + this.BGdmx = data | (this.BGdmx & 0xFF); +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGPB16 = function (data) { + data = data | 0; + this.BGdmx = (data << 16) >> 16; +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGPAB32 = function (data) { + data = data | 0; + this.BGdx = (data << 16) >> 16; + this.BGdmx = data >> 16; +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGPC8_0 = function (data) { + data = data | 0; + this.BGdy = (this.BGdy & 0xFFFFFF00) | data; +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGPC8_1 = function (data) { + data = data | 0; + data = (data << 24) >> 16; + this.BGdy = data | (this.BGdy & 0xFF); +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGPC16 = function (data) { + data = data | 0; + this.BGdy = (data << 16) >> 16; +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGPD8_0 = function (data) { + data = data | 0; + this.BGdmy = (this.BGdmy & 0xFFFFFF00) | data; +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGPD8_1 = function (data) { + data = data | 0; + data = (data << 24) >> 16; + this.BGdmy = data | (this.BGdmy & 0xFF); +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGPD16 = function (data) { + data = data | 0; + this.BGdmy = (data << 16) >> 16; +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGPCD32 = function (data) { + data = data | 0; + this.BGdy = (data << 16) >> 16; + this.BGdmy = data >> 16; +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGX8_0 = function (data) { + data = data | 0; + this.BGReferenceX = (this.BGReferenceX & 0xFFFFFF00) | data; + //Writing to the x reference doesn't reset the counters during draw! +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGX8_1 = function (data) { + data = data | 0; + this.BGReferenceX = (data << 8) | (this.BGReferenceX & 0xFFFF00FF); + //Writing to the x reference doesn't reset the counters during draw! +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGX8_2 = function (data) { + data = data | 0; + this.BGReferenceX = (data << 16) | (this.BGReferenceX & 0xFF00FFFF); + //Writing to the x reference doesn't reset the counters during draw! +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGX8_3 = function (data) { + data = data | 0; + data = (data << 28) >> 4; + this.BGReferenceX = data | (this.BGReferenceX & 0xFFFFFF); + //Writing to the x reference doesn't reset the counters during draw! +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGX16_0 = function (data) { + data = data | 0; + this.BGReferenceX = (this.BGReferenceX & 0xFFFF0000) | data; + //Writing to the x reference doesn't reset the counters during draw! +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGX16_1 = function (data) { + data = data | 0; + data = (data << 20) >> 4; + this.BGReferenceX = (this.BGReferenceX & 0xFFFF) | data; + //Writing to the x reference doesn't reset the counters during draw! +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGX32 = function (data) { + data = data | 0; + this.BGReferenceX = (data << 4) >> 4; + //Writing to the x reference doesn't reset the counters during draw! +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGY8_0 = function (data) { + data = data | 0; + this.BGReferenceY = (this.BGReferenceY & 0xFFFFFF00) | data; + this.resetReferenceCounters(); +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGY8_1 = function (data) { + data = data | 0; + this.BGReferenceY = (data << 8) | (this.BGReferenceY & 0xFFFF00FF); + this.resetReferenceCounters(); +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGY8_2 = function (data) { + data = data | 0; + this.BGReferenceY = (data << 16) | (this.BGReferenceY & 0xFF00FFFF); + this.resetReferenceCounters(); +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGY8_3 = function (data) { + data = data | 0; + data = (data << 28) >> 4; + this.BGReferenceY = data | (this.BGReferenceY & 0xFFFFFF); + this.resetReferenceCounters(); +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGY16_0 = function (data) { + data = data | 0; + this.BGReferenceY = (this.BGReferenceY & 0xFFFF0000) | data; + this.resetReferenceCounters(); +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGY16_1 = function (data) { + data = data | 0; + data = (data << 20) >> 4; + this.BGReferenceY = (this.BGReferenceY & 0xFFFF) | data; + this.resetReferenceCounters(); +} +GameBoyAdvanceAffineBGRenderer.prototype.writeBGY32 = function (data) { + data = data | 0; + this.BGReferenceY = (data << 4) >> 4; + this.resetReferenceCounters(); +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/graphics/BG2FrameBuffer.js b/public/gfiles/gba/IodineGBA/core/graphics/BG2FrameBuffer.js new file mode 100644 index 00000000..84308c89 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/graphics/BG2FrameBuffer.js @@ -0,0 +1,162 @@ +"use strict"; +/* + Copyright (C) 2012-2016 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceBG2FrameBufferRenderer(gfx) { + this.gfx = gfx; +} +GameBoyAdvanceBG2FrameBufferRenderer.prototype.initialize = function () { + this.palette = this.gfx.palette256; + this.VRAM = this.gfx.VRAM; + this.VRAM16 = this.gfx.VRAM16; + this.fetchPixel = this.fetchMode3Pixel; + this.frameSelect = 0; +} +GameBoyAdvanceBG2FrameBufferRenderer.prototype.selectMode = function (mode) { + mode = mode | 0; + switch (mode | 0) { + case 3: + this.fetchPixel = this.fetchMode3Pixel; + break; + case 4: + this.fetchPixel = this.fetchMode4Pixel; + break; + case 5: + this.fetchPixel = this.fetchMode5Pixel; + } +} +if (__LITTLE_ENDIAN__) { + if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode3Pixel = function (x, y) { + x = x | 0; + y = y | 0; + //Output pixel: + if ((x >>> 0) < 240 && (y >>> 0) < 160) { + var address = (Math.imul(y | 0, 240) + (x | 0)) | 0; + return this.VRAM16[address & 0xFFFF] & 0x7FFF; + } + //Out of range, output transparency: + return 0x3800000; + } + GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode5Pixel = function (x, y) { + x = x | 0; + y = y | 0; + //Output pixel: + if ((x >>> 0) < 160 && (y >>> 0) < 128) { + var address = ((this.frameSelect | 0) + Math.imul(y | 0, 160) + (x | 0)) | 0; + return this.VRAM16[address & 0xFFFF] & 0x7FFF; + } + //Out of range, output transparency: + return 0x3800000; + } + } + else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode3Pixel = function (x, y) { + x = x | 0; + y = y | 0; + //Output pixel: + if ((x >>> 0) < 240 && (y >>> 0) < 160) { + var address = (((y * 240) | 0) + (x | 0)) | 0; + return this.VRAM16[address & 0xFFFF] & 0x7FFF; + } + //Out of range, output transparency: + return 0x3800000; + } + GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode5Pixel = function (x, y) { + x = x | 0; + y = y | 0; + //Output pixel: + if ((x >>> 0) < 160 && (y >>> 0) < 128) { + var address = ((this.frameSelect | 0) + ((y * 160) | 0) + (x | 0)) | 0; + return this.VRAM16[address & 0xFFFF] & 0x7FFF; + } + //Out of range, output transparency: + return 0x3800000; + } + } +} +else { + if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode3Pixel = function (x, y) { + x = x | 0; + y = y | 0; + //Output pixel: + if ((x >>> 0) < 240 && (y >>> 0) < 160) { + var address = (Math.imul(y | 0, 240) + (x | 0)) << 1; + return ((this.VRAM[address | 1] << 8) | this.VRAM[address | 0]) & 0x7FFF; + } + //Out of range, output transparency: + return 0x3800000; + } + GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode5Pixel = function (x, y) { + x = x | 0; + y = y | 0; + //Output pixel: + if ((x >>> 0) < 160 && (y >>> 0) < 128) { + var address = ((this.frameSelect | 0) + ((Math.imul(y | 0, 160) + (x | 0)) << 1)) | 0; + return ((this.VRAM[address | 1] << 8) | this.VRAM[address | 0]) & 0x7FFF; + } + //Out of range, output transparency: + return 0x3800000; + } + } + else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode3Pixel = function (x, y) { + //Output pixel: + if ((x >>> 0) < 240 && (y >>> 0) < 160) { + var address = ((y * 240) + x) << 1; + return ((this.VRAM[address | 1] << 8) | this.VRAM[address]) & 0x7FFF; + } + //Out of range, output transparency: + return 0x3800000; + } + GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode5Pixel = function (x, y) { + //Output pixel: + if ((x >>> 0) < 160 && (y >>> 0) < 128) { + var address = this.frameSelect + (((y * 160) + x) << 1); + return ((this.VRAM[address | 1] << 8) | this.VRAM[address]) & 0x7FFF; + } + //Out of range, output transparency: + return 0x3800000; + } + } +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode4Pixel = function (x, y) { + x = x | 0; + y = y | 0; + //Output pixel: + if ((x >>> 0) < 240 && (y >>> 0) < 160) { + var address = ((this.frameSelect | 0) + (Math.imul(y | 0, 240) | 0) + (x | 0)) | 0; + return this.palette[this.VRAM[address | 0] & 0xFF] | 0; + } + //Out of range, output transparency: + return 0x3800000; + } +} +else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode4Pixel = function (x, y) { + //Output pixel: + if ((x >>> 0) < 240 && (y >>> 0) < 160) { + return this.palette[this.VRAM[this.frameSelect + (y * 240) + x]]; + } + //Out of range, output transparency: + return 0x3800000; + } +} +GameBoyAdvanceBG2FrameBufferRenderer.prototype.writeFrameSelect = function (frameSelect) { + frameSelect = frameSelect >> 31; + this.frameSelect = frameSelect & 0xA000; +} diff --git a/public/gfiles/gba/IodineGBA/core/graphics/BGMatrix.js b/public/gfiles/gba/IodineGBA/core/graphics/BGMatrix.js new file mode 100644 index 00000000..1d372b56 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/graphics/BGMatrix.js @@ -0,0 +1,99 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceBGMatrixRenderer(gfx) { + this.gfx = gfx; +} +GameBoyAdvanceBGMatrixRenderer.prototype.initialize = function () { + this.VRAM = this.gfx.VRAM; + this.palette = this.gfx.palette256; + this.screenSizePreprocess(0); + this.screenBaseBlockPreprocess(0); + this.characterBaseBlockPreprocess(0); + this.displayOverflowProcess(0); +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceBGMatrixRenderer.prototype.fetchTile = function (x, y) { + //Compute address for tile VRAM to address: + x = x | 0; + y = y | 0; + var tileNumber = ((x | 0) + Math.imul(y | 0, this.mapSize | 0)) | 0; + return this.VRAM[((tileNumber | 0) + (this.BGScreenBaseBlock | 0)) & 0xFFFF] | 0; + } +} +else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceBGMatrixRenderer.prototype.fetchTile = function (x, y) { + //Compute address for tile VRAM to address: + var tileNumber = x + (y * this.mapSize); + return this.VRAM[(tileNumber + this.BGScreenBaseBlock) & 0xFFFF]; + } +} +GameBoyAdvanceBGMatrixRenderer.prototype.computeScreenAddress = function (x, y) { + //Compute address for character VRAM to address: + x = x | 0; + y = y | 0; + var address = this.fetchTile(x >> 3, y >> 3) << 6; + address = ((address | 0) + (this.BGCharacterBaseBlock | 0)) | 0; + address = ((address | 0) + ((y & 0x7) << 3)) | 0; + address = ((address | 0) + (x & 0x7)) | 0; + return address | 0; +} +GameBoyAdvanceBGMatrixRenderer.prototype.fetchPixelOverflow = function (x, y) { + //Fetch the pixel: + x = x | 0; + y = y | 0; + //Output pixel: + var address = this.computeScreenAddress(x & this.mapSizeComparer, y & this.mapSizeComparer) | 0; + return this.palette[this.VRAM[address & 0xFFFF] & 0xFF] | 0; +} +GameBoyAdvanceBGMatrixRenderer.prototype.fetchPixelNoOverflow = function (x, y) { + //Fetch the pixel: + x = x | 0; + y = y | 0; + //Output pixel: + if ((x | 0) != (x & this.mapSizeComparer) || (y | 0) != (y & this.mapSizeComparer)) { + //Overflow Handling: + //Out of bounds with no overflow allowed: + return 0x3800000; + } + var address = this.computeScreenAddress(x | 0, y | 0) | 0; + return this.palette[this.VRAM[address & 0xFFFF] & 0xFF] | 0; +} +GameBoyAdvanceBGMatrixRenderer.prototype.screenBaseBlockPreprocess = function (BGScreenBaseBlock) { + BGScreenBaseBlock = BGScreenBaseBlock | 0; + this.BGScreenBaseBlock = BGScreenBaseBlock << 11; +} +GameBoyAdvanceBGMatrixRenderer.prototype.characterBaseBlockPreprocess = function (BGCharacterBaseBlock) { + BGCharacterBaseBlock = BGCharacterBaseBlock | 0; + this.BGCharacterBaseBlock = BGCharacterBaseBlock << 14; +} +GameBoyAdvanceBGMatrixRenderer.prototype.screenSizePreprocess = function (BGScreenSize) { + BGScreenSize = BGScreenSize | 0; + this.mapSize = 0x10 << (BGScreenSize | 0); + this.mapSizeComparer = ((this.mapSize << 3) - 1) | 0; +} +GameBoyAdvanceBGMatrixRenderer.prototype.displayOverflowPreprocess = function (doOverflow) { + doOverflow = doOverflow | 0; + if ((doOverflow | 0) != (this.BGDisplayOverflow | 0)) { + this.displayOverflowProcess(doOverflow | 0); + } +} +GameBoyAdvanceBGMatrixRenderer.prototype.displayOverflowProcess = function (doOverflow) { + doOverflow = doOverflow | 0; + this.BGDisplayOverflow = doOverflow | 0; + if ((doOverflow | 0) != 0) { + this.fetchPixel = this.fetchPixelOverflow; + } + else { + this.fetchPixel = this.fetchPixelNoOverflow; + } +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/graphics/BGTEXT.js b/public/gfiles/gba/IodineGBA/core/graphics/BGTEXT.js new file mode 100644 index 00000000..6dab8dbb --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/graphics/BGTEXT.js @@ -0,0 +1,577 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceBGTEXTRenderer(gfx, BGLayer) { + BGLayer = BGLayer | 0; + this.gfx = gfx; + this.BGLayer = BGLayer | 0; +} +if (__VIEWS_SUPPORTED__) { + GameBoyAdvanceBGTEXTRenderer.prototype.initialize = function () { + this.VRAM = this.gfx.VRAM; + this.VRAM16 = this.gfx.VRAM16; + this.VRAM32 = this.gfx.VRAM32; + this.palette16 = this.gfx.palette16; + this.palette256 = this.gfx.palette256; + this.offset = ((this.BGLayer << 8) + 0x100) | 0; + this.scratchBuffer = getInt32ViewCustom(this.gfx.buffer, this.offset | 0, ((this.offset | 0) + 248) | 0); + this.tileFetched = getInt32ViewCustom(this.gfx.buffer, ((this.offset | 0) + 0xF8) | 0, ((this.offset | 0) + 0x100) | 0); + this.BGXCoord = 0; + this.BGYCoord = 0; + this.do256 = 0; + this.doMosaic = 0; + this.screenSizePreprocess(0); + this.priorityPreprocess(0); + this.screenBaseBlockPreprocess(0); + this.characterBaseBlockPreprocess(0); + } + GameBoyAdvanceBGTEXTRenderer.prototype.renderWholeTiles8BIT = function (xTileStart, yTileStart, yTileOffset) { + xTileStart = xTileStart | 0; + yTileStart = yTileStart | 0; + yTileOffset = yTileOffset | 0; + //Process full 8 pixels at a time: + for (var position = (8 - (this.BGXCoord & 0x7)) | 0; (position | 0) < 240; position = ((position | 0) + 8) | 0) { + //Fetch tile attributes: + //Get 8 pixels of data: + this.process8BitVRAM(this.fetchTile(yTileStart | 0, xTileStart | 0) | 0, yTileOffset | 0); + //Copy the buffered tile to line: + this.scratchBuffer[position | 0] = this.tileFetched[0] | 0; + this.scratchBuffer[((position | 0) + 1) | 0] = this.tileFetched[1] | 0; + this.scratchBuffer[((position | 0) + 2) | 0] = this.tileFetched[2] | 0; + this.scratchBuffer[((position | 0) + 3) | 0] = this.tileFetched[3] | 0; + this.scratchBuffer[((position | 0) + 4) | 0] = this.tileFetched[4] | 0; + this.scratchBuffer[((position | 0) + 5) | 0] = this.tileFetched[5] | 0; + this.scratchBuffer[((position | 0) + 6) | 0] = this.tileFetched[6] | 0; + this.scratchBuffer[((position | 0) + 7) | 0] = this.tileFetched[7] | 0; + //Increment a tile counter: + xTileStart = ((xTileStart | 0) + 1) | 0; + } + } + GameBoyAdvanceBGTEXTRenderer.prototype.renderWholeTiles4BIT = function (xTileStart, yTileStart, yTileOffset) { + xTileStart = xTileStart | 0; + yTileStart = yTileStart | 0; + yTileOffset = yTileOffset | 0; + //Process full 8 pixels at a time: + for (var position = (8 - (this.BGXCoord & 0x7)) | 0; (position | 0) < 240; position = ((position | 0) + 8) | 0) { + //Fetch tile attributes: + //Get 8 pixels of data: + this.process4BitVRAM(this.fetchTile(yTileStart | 0, xTileStart | 0) | 0, yTileOffset | 0); + //Copy the buffered tile to line: + this.scratchBuffer[position | 0] = this.tileFetched[0] | 0; + this.scratchBuffer[((position | 0) + 1) | 0] = this.tileFetched[1] | 0; + this.scratchBuffer[((position | 0) + 2) | 0] = this.tileFetched[2] | 0; + this.scratchBuffer[((position | 0) + 3) | 0] = this.tileFetched[3] | 0; + this.scratchBuffer[((position | 0) + 4) | 0] = this.tileFetched[4] | 0; + this.scratchBuffer[((position | 0) + 5) | 0] = this.tileFetched[5] | 0; + this.scratchBuffer[((position | 0) + 6) | 0] = this.tileFetched[6] | 0; + this.scratchBuffer[((position | 0) + 7) | 0] = this.tileFetched[7] | 0; + //Increment a tile counter: + xTileStart = ((xTileStart | 0) + 1) | 0; + } + } + GameBoyAdvanceBGTEXTRenderer.prototype.fetchVRAMStart = function () { + //Handle the the first tile of the scan-line specially: + var pixelPipelinePosition = this.BGXCoord & 0x7; + switch (pixelPipelinePosition | 0) { + case 0: + this.scratchBuffer[0] = this.tileFetched[0] | 0; + case 1: + this.scratchBuffer[(1 - (pixelPipelinePosition | 0)) | 0] = this.tileFetched[1] | 0; + case 2: + this.scratchBuffer[(2 - (pixelPipelinePosition | 0)) | 0] = this.tileFetched[2] | 0; + case 3: + this.scratchBuffer[(3 - (pixelPipelinePosition | 0)) | 0] = this.tileFetched[3] | 0; + case 4: + this.scratchBuffer[(4 - (pixelPipelinePosition | 0)) | 0] = this.tileFetched[4] | 0; + case 5: + this.scratchBuffer[(5 - (pixelPipelinePosition | 0)) | 0] = this.tileFetched[5] | 0; + case 6: + this.scratchBuffer[(6 - (pixelPipelinePosition | 0)) | 0] = this.tileFetched[6] | 0; + default: + this.scratchBuffer[(7 - (pixelPipelinePosition | 0)) | 0] = this.tileFetched[7] | 0; + } + } +} +else { + GameBoyAdvanceBGTEXTRenderer.prototype.initialize = function () { + this.VRAM = this.gfx.VRAM; + this.VRAM16 = this.gfx.VRAM16; + this.VRAM32 = this.gfx.VRAM32; + this.palette16 = this.gfx.palette16; + this.palette256 = this.gfx.palette256; + this.offset = (this.BGLayer << 8) + 0x100; + this.offsetEnd = this.offset + 240; + this.scratchBuffer = this.gfx.buffer; + this.tileFetched = getInt32Array(8); + this.BGXCoord = 0; + this.BGYCoord = 0; + this.do256 = 0; + this.doMosaic = 0; + this.screenSizePreprocess(0); + this.priorityPreprocess(0); + this.screenBaseBlockPreprocess(0); + this.characterBaseBlockPreprocess(0); + } + GameBoyAdvanceBGTEXTRenderer.prototype.renderWholeTiles8BIT = function (xTileStart, yTileStart, yTileOffset) { + //Process full 8 pixels at a time: + for (var position = 8 - (this.BGXCoord & 0x7) + this.offset; position < this.offsetEnd;) { + //Fetch tile attributes: + //Get 8 pixels of data: + this.process8BitVRAM(this.fetchTile(yTileStart, xTileStart++), yTileOffset); + //Copy the buffered tile to line: + this.scratchBuffer[position++] = this.tileFetched[0]; + this.scratchBuffer[position++] = this.tileFetched[1]; + this.scratchBuffer[position++] = this.tileFetched[2]; + this.scratchBuffer[position++] = this.tileFetched[3]; + this.scratchBuffer[position++] = this.tileFetched[4]; + this.scratchBuffer[position++] = this.tileFetched[5]; + this.scratchBuffer[position++] = this.tileFetched[6]; + this.scratchBuffer[position++] = this.tileFetched[7]; + } + } + GameBoyAdvanceBGTEXTRenderer.prototype.renderWholeTiles4BIT = function (xTileStart, yTileStart, yTileOffset) { + //Process full 8 pixels at a time: + for (var position = 8 - (this.BGXCoord & 0x7) + this.offset; position < this.offsetEnd;) { + //Fetch tile attributes: + //Get 8 pixels of data: + this.process4BitVRAM(this.fetchTile(yTileStart, xTileStart++), yTileOffset); + //Copy the buffered tile to line: + this.scratchBuffer[position++] = this.tileFetched[0]; + this.scratchBuffer[position++] = this.tileFetched[1]; + this.scratchBuffer[position++] = this.tileFetched[2]; + this.scratchBuffer[position++] = this.tileFetched[3]; + this.scratchBuffer[position++] = this.tileFetched[4]; + this.scratchBuffer[position++] = this.tileFetched[5]; + this.scratchBuffer[position++] = this.tileFetched[6]; + this.scratchBuffer[position++] = this.tileFetched[7]; + } + } + GameBoyAdvanceBGTEXTRenderer.prototype.fetchVRAMStart = function () { + //Handle the the first tile of the scan-line specially: + var pixelPipelinePosition = this.BGXCoord & 0x7; + var offset = pixelPipelinePosition - this.offset; + switch (pixelPipelinePosition | 0) { + case 0: + this.scratchBuffer[offset] = this.tileFetched[0]; + case 1: + this.scratchBuffer[1 - offset] = this.tileFetched[1]; + case 2: + this.scratchBuffer[2 - offset] = this.tileFetched[2]; + case 3: + this.scratchBuffer[3 - offset] = this.tileFetched[3]; + case 4: + this.scratchBuffer[4 - offset] = this.tileFetched[4]; + case 5: + this.scratchBuffer[5 - offset] = this.tileFetched[5]; + case 6: + this.scratchBuffer[6 - offset] = this.tileFetched[6]; + default: + this.scratchBuffer[7 - offset] = this.tileFetched[7]; + } + } +} +GameBoyAdvanceBGTEXTRenderer.prototype.renderScanLine = function (line) { + line = line | 0; + if ((this.doMosaic | 0) != 0) { + //Correct line number for mosaic: + line = ((line | 0) - (this.gfx.mosaicRenderer.getMosaicYOffset(line | 0) | 0)) | 0; + } + var yTileOffset = ((line | 0) + (this.BGYCoord | 0)) & 0x7; + var yTileStart = ((line | 0) + (this.BGYCoord | 0)) >> 3; + var xTileStart = this.BGXCoord >> 3; + //Render the tiles: + if ((this.do256 | 0) != 0) { + //8-bit palette mode: + this.render8BITLine(yTileStart | 0, xTileStart | 0, yTileOffset | 0); + } + else { + //4-bit palette mode: + this.render4BITLine(yTileStart | 0, xTileStart | 0, yTileOffset | 0); + } + if ((this.doMosaic | 0) != 0) { + //Pixelize the line horizontally: + this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset | 0); + } +} +GameBoyAdvanceBGTEXTRenderer.prototype.render8BITLine = function (yTileStart, xTileStart, yTileOffset) { + yTileStart = yTileStart | 0; + xTileStart = xTileStart | 0; + yTileOffset = yTileOffset | 0; + //Fetch tile attributes: + var chrData = this.fetchTile(yTileStart | 0, xTileStart | 0) | 0; + xTileStart = ((xTileStart | 0) + 1) | 0; + //Get 8 pixels of data: + this.process8BitVRAM(chrData | 0, yTileOffset | 0); + //Copy the buffered tile to line: + this.fetchVRAMStart(); + //Render the rest of the tiles fast: + this.renderWholeTiles8BIT(xTileStart | 0, yTileStart | 0, yTileOffset | 0); +} +GameBoyAdvanceBGTEXTRenderer.prototype.render4BITLine = function (yTileStart, xTileStart, yTileOffset) { + //Fetch tile attributes: + var chrData = this.fetchTile(yTileStart | 0, xTileStart | 0) | 0; + xTileStart = ((xTileStart | 0) + 1) | 0; + //Get 8 pixels of data: + this.process4BitVRAM(chrData | 0, yTileOffset | 0); + //Copy the buffered tile to line: + this.fetchVRAMStart(); + //Render the rest of the tiles fast: + this.renderWholeTiles4BIT(xTileStart | 0, yTileStart | 0, yTileOffset | 0); +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceBGTEXTRenderer.prototype.fetchTile = function (yTileStart, xTileStart) { + yTileStart = yTileStart | 0; + xTileStart = xTileStart | 0; + //Find the tile code to locate the tile block: + var address = ((this.computeTileNumber(yTileStart | 0, xTileStart | 0) | 0) + (this.BGScreenBaseBlock | 0)) | 0; + return this.VRAM16[address & 0x7FFF] | 0; + } +} +else { + GameBoyAdvanceBGTEXTRenderer.prototype.fetchTile = function (yTileStart, xTileStart) { + //Find the tile code to locate the tile block: + var address = ((this.computeTileNumber(yTileStart, xTileStart) + this.BGScreenBaseBlock) << 1) & 0xFFFF; + return (this.VRAM[address | 1] << 8) | this.VRAM[address]; + } +} +GameBoyAdvanceBGTEXTRenderer.prototype.computeTileNumber = function (yTile, xTile) { + //Return the true tile number: + yTile = yTile | 0; + xTile = xTile | 0; + var tileNumber = xTile & 0x1F; + switch (this.tileMode | 0) { + //1x1 + case 0: + tileNumber = tileNumber | ((yTile & 0x1F) << 5); + break; + //2x1 + case 1: + tileNumber = tileNumber | (((xTile & 0x20) | (yTile & 0x1F)) << 5); + break; + //1x2 + case 2: + tileNumber = tileNumber | ((yTile & 0x3F) << 5); + break; + //2x2 + default: + tileNumber = tileNumber | (((xTile & 0x20) | (yTile & 0x1F)) << 5) | ((yTile & 0x20) << 6); + } + return tileNumber | 0; +} +GameBoyAdvanceBGTEXTRenderer.prototype.process4BitVRAM = function (chrData, yOffset) { + //16 color tile mode: + chrData = chrData | 0; + yOffset = yOffset | 0; + //Parse flip attributes, grab palette, and then output pixel: + var address = (chrData & 0x3FF) << 3; + address = ((address | 0) + (this.BGCharacterBaseBlock | 0)) | 0; + if ((chrData & 0x800) == 0) { + //No vertical flip: + address = ((address | 0) + (yOffset | 0)) | 0; + + } + else { + //Vertical flip: + address = ((address | 0) + 7) | 0; + address = ((address | 0) - (yOffset | 0)) | 0; + } + //Copy out our pixels: + this.render4BitVRAM(chrData >> 8, address | 0); +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceBGTEXTRenderer.prototype.render4BitVRAM = function (chrData, address) { + chrData = chrData | 0; + address = address | 0; + //Unrolled data tile line fetch: + if ((address | 0) < 0x4000) { + //Tile address valid: + var paletteOffset = chrData & 0xF0; + var data = this.VRAM32[address | 0] | 0; + if ((chrData & 0x4) == 0) { + //Normal Horizontal: + this.tileFetched[0] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag; + this.tileFetched[1] = this.palette16[paletteOffset | ((data >> 4) & 0xF)] | this.priorityFlag; + this.tileFetched[2] = this.palette16[paletteOffset | ((data >> 8) & 0xF)] | this.priorityFlag; + this.tileFetched[3] = this.palette16[paletteOffset | ((data >> 12) & 0xF)] | this.priorityFlag; + this.tileFetched[4] = this.palette16[paletteOffset | ((data >> 16) & 0xF)] | this.priorityFlag; + this.tileFetched[5] = this.palette16[paletteOffset | ((data >> 20) & 0xF)] | this.priorityFlag; + this.tileFetched[6] = this.palette16[paletteOffset | ((data >> 24) & 0xF)] | this.priorityFlag; + this.tileFetched[7] = this.palette16[paletteOffset | (data >>> 28)] | this.priorityFlag; + } + else { + //Flipped Horizontally: + this.tileFetched[0] = this.palette16[paletteOffset | (data >>> 28)] | this.priorityFlag; + this.tileFetched[1] = this.palette16[paletteOffset | ((data >> 24) & 0xF)] | this.priorityFlag; + this.tileFetched[2] = this.palette16[paletteOffset | ((data >> 20) & 0xF)] | this.priorityFlag; + this.tileFetched[3] = this.palette16[paletteOffset | ((data >> 16) & 0xF)] | this.priorityFlag; + this.tileFetched[4] = this.palette16[paletteOffset | ((data >> 12) & 0xF)] | this.priorityFlag; + this.tileFetched[5] = this.palette16[paletteOffset | ((data >> 8) & 0xF)] | this.priorityFlag; + this.tileFetched[6] = this.palette16[paletteOffset | ((data >> 4) & 0xF)] | this.priorityFlag; + this.tileFetched[7] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag; + } + } + else { + //Tile address invalid: + this.addressInvalidRender(); + } + } +} +else { + GameBoyAdvanceBGTEXTRenderer.prototype.render4BitVRAM = function (chrData, address) { + address <<= 2; + //Unrolled data tile line fetch: + if (address < 0x10000) { + //Tile address valid: + var paletteOffset = chrData & 0xF0; + var data = this.VRAM[address]; + if ((chrData & 0x4) == 0) { + //Normal Horizontal: + this.tileFetched[0] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag; + this.tileFetched[1] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag; + data = this.VRAM[address | 1]; + this.tileFetched[2] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag; + this.tileFetched[3] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag; + data = this.VRAM[address | 2]; + this.tileFetched[4] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag; + this.tileFetched[5] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag; + data = this.VRAM[address | 3]; + this.tileFetched[6] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag; + this.tileFetched[7] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag; + } + else { + //Flipped Horizontally: + this.tileFetched[7] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag; + this.tileFetched[6] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag; + data = this.VRAM[address | 1]; + this.tileFetched[5] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag; + this.tileFetched[4] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag; + data = this.VRAM[address | 2]; + this.tileFetched[3] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag; + this.tileFetched[2] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag; + data = this.VRAM[address | 3]; + this.tileFetched[1] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag; + this.tileFetched[0] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag; + } + } + else { + //Tile address invalid: + this.addressInvalidRender(); + } + } +} +/* + If there was 64 bit typed array support, + then process8BitVRAM, render8BitVRAMNormal, + and render8BitVRAMFlipped could be optimized further. + Namely make one fetch for tile data instead of two, + and cancel a y-offset shift. + */ +GameBoyAdvanceBGTEXTRenderer.prototype.process8BitVRAM = function (chrData, yOffset) { + //16 color tile mode: + chrData = chrData | 0; + yOffset = yOffset | 0; + //Parse flip attributes, grab palette, and then output pixel: + var address = (chrData & 0x3FF) << 4; + address = ((address | 0) + (this.BGCharacterBaseBlock | 0)) | 0; + //Copy out our pixels: + switch (chrData & 0xC00) { + //No Flip: + case 0: + address = ((address | 0) + (yOffset << 1)) | 0; + this.render8BitVRAMNormal(address | 0); + break; + //Horizontal Flip: + case 0x400: + address = ((address | 0) + (yOffset << 1)) | 0; + this.render8BitVRAMFlipped(address | 0); + break; + //Vertical Flip: + case 0x800: + address = ((address | 0) + 14) | 0; + address = ((address | 0) - (yOffset << 1)) | 0; + this.render8BitVRAMNormal(address | 0); + break; + //Horizontal & Vertical Flip: + default: + address = ((address | 0) + 14) | 0; + address = ((address | 0) - (yOffset << 1)) | 0; + this.render8BitVRAMFlipped(address | 0); + } +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceBGTEXTRenderer.prototype.render8BitVRAMNormal = function (address) { + address = address | 0; + if ((address | 0) < 0x4000) { + //Tile address valid: + //Normal Horizontal: + var data = this.VRAM32[address | 0] | 0; + this.tileFetched[0] = this.palette256[data & 0xFF] | this.priorityFlag; + this.tileFetched[1] = this.palette256[(data >> 8) & 0xFF] | this.priorityFlag; + this.tileFetched[2] = this.palette256[(data >> 16) & 0xFF] | this.priorityFlag; + this.tileFetched[3] = this.palette256[data >>> 24] | this.priorityFlag; + data = this.VRAM32[address | 1] | 0; + this.tileFetched[4] = this.palette256[data & 0xFF] | this.priorityFlag; + this.tileFetched[5] = this.palette256[(data >> 8) & 0xFF] | this.priorityFlag; + this.tileFetched[6] = this.palette256[(data >> 16) & 0xFF] | this.priorityFlag; + this.tileFetched[7] = this.palette256[data >>> 24] | this.priorityFlag; + } + else { + //Tile address invalid: + this.addressInvalidRender(); + } + } + GameBoyAdvanceBGTEXTRenderer.prototype.render8BitVRAMFlipped = function (address) { + address = address | 0; + if ((address | 0) < 0x4000) { + //Tile address valid: + //Flipped Horizontally: + var data = this.VRAM32[address | 0] | 0; + this.tileFetched[4] = this.palette256[data >>> 24] | this.priorityFlag; + this.tileFetched[5] = this.palette256[(data >> 16) & 0xFF] | this.priorityFlag; + this.tileFetched[6] = this.palette256[(data >> 8) & 0xFF] | this.priorityFlag; + this.tileFetched[7] = this.palette256[data & 0xFF] | this.priorityFlag; + data = this.VRAM32[address | 1] | 0; + this.tileFetched[0] = this.palette256[data >>> 24] | this.priorityFlag; + this.tileFetched[1] = this.palette256[(data >> 16) & 0xFF] | this.priorityFlag; + this.tileFetched[2] = this.palette256[(data >> 8) & 0xFF] | this.priorityFlag; + this.tileFetched[3] = this.palette256[data & 0xFF] | this.priorityFlag; + } + else { + //Tile address invalid: + this.addressInvalidRender(); + } + } +} +else { + GameBoyAdvanceBGTEXTRenderer.prototype.render8BitVRAMNormal = function (address) { + address <<= 2; + if (address < 0x10000) { + //Tile address valid: + //Normal Horizontal: + this.tileFetched[0] = this.palette256[this.VRAM[address]] | this.priorityFlag; + this.tileFetched[1] = this.palette256[this.VRAM[address | 1]] | this.priorityFlag; + this.tileFetched[2] = this.palette256[this.VRAM[address | 2]] | this.priorityFlag; + this.tileFetched[3] = this.palette256[this.VRAM[address | 3]] | this.priorityFlag; + this.tileFetched[4] = this.palette256[this.VRAM[address | 4]] | this.priorityFlag; + this.tileFetched[5] = this.palette256[this.VRAM[address | 5]] | this.priorityFlag; + this.tileFetched[6] = this.palette256[this.VRAM[address | 6]] | this.priorityFlag; + this.tileFetched[7] = this.palette256[this.VRAM[address | 7]] | this.priorityFlag; + } + else { + //Tile address invalid: + this.addressInvalidRender(); + } + } + GameBoyAdvanceBGTEXTRenderer.prototype.render8BitVRAMFlipped = function (address) { + address <<= 2; + if (address < 0x10000) { + //Tile address valid: + //Flipped Horizontally: + this.tileFetched[7] = this.palette256[this.VRAM[address]] | this.priorityFlag; + this.tileFetched[6] = this.palette256[this.VRAM[address | 1]] | this.priorityFlag; + this.tileFetched[5] = this.palette256[this.VRAM[address | 2]] | this.priorityFlag; + this.tileFetched[4] = this.palette256[this.VRAM[address | 3]] | this.priorityFlag; + this.tileFetched[3] = this.palette256[this.VRAM[address | 4]] | this.priorityFlag; + this.tileFetched[2] = this.palette256[this.VRAM[address | 5]] | this.priorityFlag; + this.tileFetched[1] = this.palette256[this.VRAM[address | 6]] | this.priorityFlag; + this.tileFetched[0] = this.palette256[this.VRAM[address | 7]] | this.priorityFlag; + } + else { + //Tile address invalid: + this.addressInvalidRender(); + } + } +} +GameBoyAdvanceBGTEXTRenderer.prototype.addressInvalidRender = function () { + //In GBA mode on NDS, we display transparency on invalid tiles: + var data = this.gfx.transparency | this.priorityFlag; + this.tileFetched[0] = data | 0; + this.tileFetched[1] = data | 0; + this.tileFetched[2] = data | 0; + this.tileFetched[3] = data | 0; + this.tileFetched[4] = data | 0; + this.tileFetched[5] = data | 0; + this.tileFetched[6] = data | 0; + this.tileFetched[7] = data | 0; +} +GameBoyAdvanceBGTEXTRenderer.prototype.setMosaicEnable = function (doMosaic) { + doMosaic = doMosaic | 0; + this.doMosaic = doMosaic | 0; +} +GameBoyAdvanceBGTEXTRenderer.prototype.paletteModeSelect = function (do256) { + do256 = do256 | 0; + this.do256 = do256 | 0; +} +GameBoyAdvanceBGTEXTRenderer.prototype.screenSizePreprocess = function (BGScreenSize) { + BGScreenSize = BGScreenSize | 0; + this.tileMode = BGScreenSize | 0; +} +GameBoyAdvanceBGTEXTRenderer.prototype.priorityPreprocess = function (BGPriority) { + BGPriority = BGPriority | 0; + this.priorityFlag = (BGPriority << 23) | (1 << (this.BGLayer | 0x10)); +} +GameBoyAdvanceBGTEXTRenderer.prototype.screenBaseBlockPreprocess = function (BGScreenBaseBlock) { + BGScreenBaseBlock = BGScreenBaseBlock | 0; + this.BGScreenBaseBlock = BGScreenBaseBlock << 10; +} +GameBoyAdvanceBGTEXTRenderer.prototype.characterBaseBlockPreprocess = function (BGCharacterBaseBlock) { + BGCharacterBaseBlock = BGCharacterBaseBlock | 0; + this.BGCharacterBaseBlock = BGCharacterBaseBlock << 12; +} +GameBoyAdvanceBGTEXTRenderer.prototype.writeBGCNT8_0 = function (data) { + data = data | 0; + this.setMosaicEnable(data & 0x40); + this.paletteModeSelect(data & 0x80); + this.priorityPreprocess(data & 0x3); + this.characterBaseBlockPreprocess((data & 0xC) >> 2); +} +GameBoyAdvanceBGTEXTRenderer.prototype.writeBGCNT8_1 = function (data) { + data = data | 0; + this.screenSizePreprocess((data & 0xC0) >> 6); + this.screenBaseBlockPreprocess(data & 0x1F); +} +GameBoyAdvanceBGTEXTRenderer.prototype.writeBGCNT16 = function (data) { + data = data | 0; + this.setMosaicEnable(data & 0x40); + this.paletteModeSelect(data & 0x80); + this.priorityPreprocess(data & 0x3); + this.characterBaseBlockPreprocess((data & 0xC) >> 2); + this.screenSizePreprocess((data & 0xC000) >> 14); + this.screenBaseBlockPreprocess((data >> 8) & 0x1F); +} +GameBoyAdvanceBGTEXTRenderer.prototype.writeBGHOFS8_0 = function (data) { + data = data | 0; + this.BGXCoord = (this.BGXCoord & 0x100) | data; +} +GameBoyAdvanceBGTEXTRenderer.prototype.writeBGHOFS8_1 = function (data) { + data = data | 0; + this.BGXCoord = (data << 8) | (this.BGXCoord & 0xFF); +} +GameBoyAdvanceBGTEXTRenderer.prototype.writeBGHOFS16 = function (data) { + data = data | 0; + this.BGXCoord = data | 0; +} +GameBoyAdvanceBGTEXTRenderer.prototype.writeBGVOFS8_0 = function (data) { + data = data | 0; + this.BGYCoord = (this.BGYCoord & 0x100) | data; +} +GameBoyAdvanceBGTEXTRenderer.prototype.writeBGVOFS8_1 = function (data) { + data = data | 0; + this.BGYCoord = (data << 8) | (this.BGYCoord & 0xFF); +} +GameBoyAdvanceBGTEXTRenderer.prototype.writeBGVOFS16 = function (data) { + data = data | 0; + this.BGYCoord = data | 0; +} +GameBoyAdvanceBGTEXTRenderer.prototype.writeBGOFS32 = function (data) { + data = data | 0; + this.BGXCoord = data & 0x1FF; + this.BGYCoord = data >> 16; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/graphics/ColorEffects.js b/public/gfiles/gba/IodineGBA/core/graphics/ColorEffects.js new file mode 100644 index 00000000..9e816681 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/graphics/ColorEffects.js @@ -0,0 +1,347 @@ +"use strict"; +/* + Copyright (C) 2012-2016 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceColorEffectsRenderer(buffer) { + this.effectsTarget1 = 0; + this.colorEffectsType = 0; + this.effectsTarget2 = 0; + this.initialize(buffer); +} +if (typeof SIMD == "object" && typeof SIMD.Int32x4 == "function") { + //SIMD support found, insert the optimized SIMD path in: + GameBoyAdvanceColorEffectsRenderer.prototype.initialize = function (buffer) { + this.alphaBlendAmountTarget1 = SIMD.Int32x4.splat(0); + this.alphaBlendAmountTarget2 = SIMD.Int32x4.splat(0); + this.brightnessEffectAmount = SIMD.Int32x4.splat(0); + this.brightnessEffectAmountReverse = SIMD.Int32x4.splat(0x10); + this.buffer = buffer; + } + GameBoyAdvanceColorEffectsRenderer.prototype.pixelMask = SIMD.Int32x4.splat(0x1F); + GameBoyAdvanceColorEffectsRenderer.prototype.temporaryPixelBuffer = new Int32Array(4); + GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlend = function (topPixel, lowerPixel) { + topPixel = topPixel | 0; + lowerPixel = lowerPixel | 0; + var p1 = SIMD.Int32x4(topPixel >> 10, topPixel >> 5, topPixel, 0); + var p2 = SIMD.Int32x4(lowerPixel >> 10, lowerPixel >> 5, lowerPixel, 0); + p1 = SIMD.Int32x4.and(p1, this.pixelMask); + p2 = SIMD.Int32x4.and(p2, this.pixelMask); + p1 = SIMD.Int32x4.mul(p1, this.alphaBlendAmountTarget1); + p2 = SIMD.Int32x4.mul(p2, this.alphaBlendAmountTarget2); + var presult = SIMD.Int32x4.add(p1, p2); + presult = SIMD.Int32x4.shiftRightByScalar(presult, 4); + var selectMask = SIMD.Int32x4.lessThanOrEqual(presult, this.pixelMask); + presult = SIMD.Int32x4.select(selectMask, presult, this.pixelMask); + SIMD.Int32x4.store(this.temporaryPixelBuffer, 0, presult); + return (this.temporaryPixelBuffer[0] << 10) | (this.temporaryPixelBuffer[1] << 5) | this.temporaryPixelBuffer[2]; + } + GameBoyAdvanceColorEffectsRenderer.prototype.brightnessIncrease = function (topPixel) { + topPixel = topPixel | 0; + var p1 = SIMD.Int32x4(topPixel >> 10, topPixel >> 5, topPixel, 0); + p1 = SIMD.Int32x4.and(p1, this.pixelMask); + var pTemp = SIMD.Int32x4.sub(this.pixelMask, p1); + pTemp = SIMD.Int32x4.mul(pTemp, this.brightnessEffectAmount); + pTemp = SIMD.Int32x4.shiftRightByScalar(pTemp, 4); + p1 = SIMD.Int32x4.add(p1, pTemp); + SIMD.Int32x4.store(this.temporaryPixelBuffer, 0, p1); + return (this.temporaryPixelBuffer[0] << 10) | (this.temporaryPixelBuffer[1] << 5) | this.temporaryPixelBuffer[2]; + } + GameBoyAdvanceColorEffectsRenderer.prototype.brightnessDecrease = function (topPixel) { + topPixel = topPixel | 0; + var p1 = SIMD.Int32x4(topPixel >> 10, topPixel >> 5, topPixel, 0); + p1 = SIMD.Int32x4.and(p1, this.pixelMask); + p1 = SIMD.Int32x4.mul(p1, this.brightnessEffectAmountReverse); + p1 = SIMD.Int32x4.shiftRightByScalar(p1, 4); + SIMD.Int32x4.store(this.temporaryPixelBuffer, 0, p1); + return (this.temporaryPixelBuffer[0] << 10) | (this.temporaryPixelBuffer[1] << 5) | this.temporaryPixelBuffer[2]; + } + GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA8_0 = function (data) { + data = data | 0; + var alphaBlendAmountTarget1Scratch = data & 0x1F; + this.alphaBlendAmountTarget1 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0); + } + GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA8_1 = function (data) { + data = data | 0; + var alphaBlendAmountTarget2Scratch = data & 0x1F; + this.alphaBlendAmountTarget2 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0); + } + GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA16 = function (data) { + data = data | 0; + var alphaBlendAmountTarget1Scratch = data & 0x1F; + this.alphaBlendAmountTarget1 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0); + var alphaBlendAmountTarget2Scratch = (data >> 8) & 0x1F; + this.alphaBlendAmountTarget2 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0); + } + GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDCNT32 = function (data) { + data = data | 0; + //Select target 1 and color effects mode: + this.effectsTarget1 = (data & 0x3F) << 16; + this.colorEffectsType = (data >> 6) & 0x3; + //Select target 2: + this.effectsTarget2 = (data & 0x3F00) << 8; + var alphaBlendAmountTarget1Scratch = (data >> 16) & 0x1F; + this.alphaBlendAmountTarget1 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0); + var alphaBlendAmountTarget2Scratch = (data >> 24) & 0x1F; + this.alphaBlendAmountTarget2 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0); + } + GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDY8 = function (data) { + data = data | 0; + var data = Math.min(data & 0x1F, 0x10) | 0; + this.brightnessEffectAmount = SIMD.Int32x4.splat(data | 0); + this.brightnessEffectAmountReverse = SIMD.Int32x4.splat((0x10 - (data | 0)) | 0); + } + GameBoyAdvanceColorEffectsRenderer.prototype.processFullNormalEffectsNoSprites = function () { + for (var index = 0; (index | 0) < 240; index = ((index | 0) + 4) | 0) { + this.buffer[index | 0] = this.processPixelNormal(this.buffer[index | 0x700] | 0, this.buffer[index | 0x800] | 0); + this.buffer[index | 1] = this.processPixelNormal(this.buffer[index | 0x701] | 0, this.buffer[index | 0x801] | 0); + this.buffer[index | 2] = this.processPixelNormal(this.buffer[index | 0x702] | 0, this.buffer[index | 0x802] | 0); + this.buffer[index | 3] = this.processPixelNormal(this.buffer[index | 0x703] | 0, this.buffer[index | 0x803] | 0); + } + } + GameBoyAdvanceColorEffectsRenderer.prototype.processWindowNormalEffectsNoSprites = function (xStart, xEnd) { + xStart = xStart | 0; + xEnd = xEnd | 0; + while ((xStart | 0) < (xEnd | 0)) { + this.buffer[xStart | 0] = this.processPixelNormal(this.buffer[xStart | 0x700] | 0, this.buffer[xStart | 0x800] | 0); + xStart = ((xStart | 0) + 1) | 0; + } + } + GameBoyAdvanceColorEffectsRenderer.prototype.processFullNormalEffectsWithSprites = function () { + for (var index = 0; (index | 0) < 240; index = ((index | 0) + 4) | 0) { + this.buffer[index | 0] = this.processPixelTestFull(this.buffer[index | 0x700] | 0, this.buffer[index | 0x800] | 0); + this.buffer[index | 1] = this.processPixelTestFull(this.buffer[index | 0x701] | 0, this.buffer[index | 0x801] | 0); + this.buffer[index | 2] = this.processPixelTestFull(this.buffer[index | 0x702] | 0, this.buffer[index | 0x802] | 0); + this.buffer[index | 3] = this.processPixelTestFull(this.buffer[index | 0x703] | 0, this.buffer[index | 0x803] | 0); + } + } + GameBoyAdvanceColorEffectsRenderer.prototype.processWindowNormalEffectsWithSprites = function (xStart, xEnd) { + xStart = xStart | 0; + xEnd = xEnd | 0; + while ((xStart | 0) < (xEnd | 0)) { + this.buffer[xStart | 0] = this.processPixelTestFull(this.buffer[xStart | 0x700] | 0, this.buffer[xStart | 0x800] | 0); + xStart = ((xStart | 0) + 1) | 0; + } + } + GameBoyAdvanceColorEffectsRenderer.prototype.processFullNoEffectsWithSprites = function () { + for (var index = 0; (index | 0) < 240; index = ((index | 0) + 4) | 0) { + this.buffer[index | 0] = this.processPixelTestSprite(this.buffer[index | 0x700] | 0, this.buffer[index | 0x800] | 0); + this.buffer[index | 1] = this.processPixelTestSprite(this.buffer[index | 0x701] | 0, this.buffer[index | 0x801] | 0); + this.buffer[index | 2] = this.processPixelTestSprite(this.buffer[index | 0x702] | 0, this.buffer[index | 0x802] | 0); + this.buffer[index | 3] = this.processPixelTestSprite(this.buffer[index | 0x703] | 0, this.buffer[index | 0x803] | 0); + } + } + GameBoyAdvanceColorEffectsRenderer.prototype.processWindowNoEffectsWithSprites = function (xStart, xEnd) { + xStart = xStart | 0; + xEnd = xEnd | 0; + while ((xStart | 0) < (xEnd | 0)) { + this.buffer[xStart | 0] = this.processPixelTestSprite(this.buffer[xStart | 0x700] | 0, this.buffer[xStart | 0x800] | 0); + xStart = ((xStart | 0) + 1) | 0; + } + } + GameBoyAdvanceColorEffectsRenderer.prototype.processPixelTestFull = function (lowerPixel, currentPixel) { + lowerPixel = lowerPixel | 0; + currentPixel = currentPixel | 0; + if ((currentPixel & 0x400000) == 0) { + return this.processPixelNormal(lowerPixel | 0, currentPixel | 0) | 0; + } + else { + return this.processPixelSprite(lowerPixel | 0, currentPixel | 0) | 0; + } + } + GameBoyAdvanceColorEffectsRenderer.prototype.processPixelTestSprite = function (lowerPixel, currentPixel) { + lowerPixel = lowerPixel | 0; + currentPixel = currentPixel | 0; + if ((currentPixel & 0x400000) == 0) { + return currentPixel | 0; + } + else { + return this.processPixelSprite(lowerPixel | 0, currentPixel | 0) | 0; + } + } +} +else { + //No SIMD support found, use the scalar path instead: + GameBoyAdvanceColorEffectsRenderer.prototype.initialize = function (buffer) { + this.alphaBlendAmountTarget1 = 0; + this.alphaBlendAmountTarget2 = 0; + this.brightnessEffectAmount = 0; + } + if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlend = function (topPixel, lowerPixel) { + topPixel = topPixel | 0; + lowerPixel = lowerPixel | 0; + var b1 = (topPixel >> 10) & 0x1F; + var g1 = (topPixel >> 5) & 0x1F; + var r1 = topPixel & 0x1F; + var b2 = (lowerPixel >> 10) & 0x1F; + var g2 = (lowerPixel >> 5) & 0x1F; + var r2 = lowerPixel & 0x1F; + b1 = Math.imul(b1 | 0, this.alphaBlendAmountTarget1 | 0) | 0; + g1 = Math.imul(g1 | 0, this.alphaBlendAmountTarget1 | 0) | 0; + r1 = Math.imul(r1 | 0, this.alphaBlendAmountTarget1 | 0) | 0; + b2 = Math.imul(b2 | 0, this.alphaBlendAmountTarget2 | 0) | 0; + g2 = Math.imul(g2 | 0, this.alphaBlendAmountTarget2 | 0) | 0; + r2 = Math.imul(r2 | 0, this.alphaBlendAmountTarget2 | 0) | 0; + //Keep this not inlined in the return, firefox 22 grinds on it: + var b = Math.min(((b1 | 0) + (b2 | 0)) >> 4, 0x1F) | 0; + var g = Math.min(((g1 | 0) + (g2 | 0)) >> 4, 0x1F) | 0; + var r = Math.min(((r1 | 0) + (r2 | 0)) >> 4, 0x1F) | 0; + return (b << 10) | (g << 5) | r; + } + GameBoyAdvanceColorEffectsRenderer.prototype.brightnessIncrease = function (topPixel) { + topPixel = topPixel | 0; + var b1 = (topPixel >> 10) & 0x1F; + var g1 = (topPixel >> 5) & 0x1F; + var r1 = topPixel & 0x1F; + b1 = ((b1 | 0) + (Math.imul((0x1F - (b1 | 0)) | 0, this.brightnessEffectAmount | 0) >> 4)) | 0; + g1 = ((g1 | 0) + (Math.imul((0x1F - (g1 | 0)) | 0, this.brightnessEffectAmount | 0) >> 4)) | 0; + r1 = ((r1 | 0) + (Math.imul((0x1F - (r1 | 0)) | 0, this.brightnessEffectAmount | 0) >> 4)) | 0; + return (b1 << 10) | (g1 << 5) | r1; + } + GameBoyAdvanceColorEffectsRenderer.prototype.brightnessDecrease = function (topPixel) { + topPixel = topPixel | 0; + var b1 = (topPixel >> 10) & 0x1F; + var g1 = (topPixel >> 5) & 0x1F; + var r1 = topPixel & 0x1F; + var decreaseMultiplier = (0x10 - (this.brightnessEffectAmount | 0)) | 0; + b1 = Math.imul(b1 | 0, decreaseMultiplier | 0) >> 4; + g1 = Math.imul(g1 | 0, decreaseMultiplier | 0) >> 4; + r1 = Math.imul(r1 | 0, decreaseMultiplier | 0) >> 4; + return (b1 << 10) | (g1 << 5) | r1; + } + } + else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlend = function (topPixel, lowerPixel) { + topPixel = topPixel | 0; + lowerPixel = lowerPixel | 0; + var b1 = (topPixel >> 10) & 0x1F; + var g1 = (topPixel >> 5) & 0x1F; + var r1 = (topPixel & 0x1F); + var b2 = (lowerPixel >> 10) & 0x1F; + var g2 = (lowerPixel >> 5) & 0x1F; + var r2 = lowerPixel & 0x1F; + b1 = b1 * this.alphaBlendAmountTarget1; + g1 = g1 * this.alphaBlendAmountTarget1; + r1 = r1 * this.alphaBlendAmountTarget1; + b2 = b2 * this.alphaBlendAmountTarget2; + g2 = g2 * this.alphaBlendAmountTarget2; + r2 = r2 * this.alphaBlendAmountTarget2; + return (Math.min((b1 + b2) >> 4, 0x1F) << 10) | (Math.min((g1 + g2) >> 4, 0x1F) << 5) | Math.min((r1 + r2) >> 4, 0x1F); + } + GameBoyAdvanceColorEffectsRenderer.prototype.brightnessIncrease = function (topPixel) { + topPixel = topPixel | 0; + var b1 = (topPixel >> 10) & 0x1F; + var g1 = (topPixel >> 5) & 0x1F; + var r1 = topPixel & 0x1F; + b1 += ((0x1F - b1) * this.brightnessEffectAmount) >> 4; + g1 += ((0x1F - g1) * this.brightnessEffectAmount) >> 4; + r1 += ((0x1F - r1) * this.brightnessEffectAmount) >> 4; + return (b1 << 10) | (g1 << 5) | r1; + } + GameBoyAdvanceColorEffectsRenderer.prototype.brightnessDecrease = function (topPixel) { + topPixel = topPixel | 0; + var b1 = (topPixel >> 10) & 0x1F; + var g1 = (topPixel >> 5) & 0x1F; + var r1 = topPixel & 0x1F; + var decreaseMultiplier = 0x10 - this.brightnessEffectAmount; + b1 = (b1 * decreaseMultiplier) >> 4; + g1 = (g1 * decreaseMultiplier) >> 4; + r1 = (r1 * decreaseMultiplier) >> 4; + return (b1 << 10) | (g1 << 5) | r1; + } + } + GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA8_0 = function (data) { + data = data | 0; + var alphaBlendAmountTarget1Scratch = data & 0x1F; + this.alphaBlendAmountTarget1 = Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0; + } + GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA8_1 = function (data) { + data = data | 0; + var alphaBlendAmountTarget2Scratch = data & 0x1F; + this.alphaBlendAmountTarget2 = Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0; + } + GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA16 = function (data) { + data = data | 0; + var alphaBlendAmountTarget1Scratch = data & 0x1F; + this.alphaBlendAmountTarget1 = Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0; + var alphaBlendAmountTarget2Scratch = (data >> 8) & 0x1F; + this.alphaBlendAmountTarget2 = Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0; + } + GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDCNT32 = function (data) { + data = data | 0; + //Select target 1 and color effects mode: + this.effectsTarget1 = (data & 0x3F) << 16; + this.colorEffectsType = (data >> 6) & 0x3; + //Select target 2: + this.effectsTarget2 = (data & 0x3F00) << 8; + var alphaBlendAmountTarget1Scratch = (data >> 16) & 0x1F; + this.alphaBlendAmountTarget1 = Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0; + var alphaBlendAmountTarget2Scratch = (data >> 24) & 0x1F; + this.alphaBlendAmountTarget2 = Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0; + } + GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDY8 = function (data) { + data = data | 0; + this.brightnessEffectAmount = Math.min(data & 0x1F, 0x10) | 0; + } +} +GameBoyAdvanceColorEffectsRenderer.prototype.processPixelNormal = function (lowerPixel, topPixel) { + lowerPixel = lowerPixel | 0; + topPixel = topPixel | 0; + if (((topPixel | 0) & (this.effectsTarget1 | 0)) != 0) { + switch (this.colorEffectsType | 0) { + case 1: + if (((lowerPixel | 0) & (this.effectsTarget2 | 0)) != 0 && (topPixel | 0) != (lowerPixel | 0)) { + return this.alphaBlend(topPixel | 0, lowerPixel | 0) | 0; + } + break; + case 2: + return this.brightnessIncrease(topPixel | 0) | 0; + case 3: + return this.brightnessDecrease(topPixel | 0) | 0; + } + } + return topPixel | 0; +} +GameBoyAdvanceColorEffectsRenderer.prototype.processPixelSprite = function (lowerPixel, topPixel) { + lowerPixel = lowerPixel | 0; + topPixel = topPixel | 0; + if (((lowerPixel | 0) & (this.effectsTarget2 | 0)) != 0) { + return this.alphaBlend(topPixel | 0, lowerPixel | 0) | 0; + } + else if (((topPixel | 0) & (this.effectsTarget1 | 0)) != 0) { + switch (this.colorEffectsType | 0) { + case 2: + return this.brightnessIncrease(topPixel | 0) | 0; + case 3: + return this.brightnessDecrease(topPixel | 0) | 0; + } + } + return topPixel | 0; +} +GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDCNT8_0 = function (data) { + data = data | 0; + //Select target 1 and color effects mode: + this.effectsTarget1 = (data & 0x3F) << 16; + this.colorEffectsType = data >> 6; +} +GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDCNT8_1 = function (data) { + data = data | 0; + //Select target 2: + this.effectsTarget2 = (data & 0x3F) << 16; +} +GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDCNT16 = function (data) { + data = data | 0; + //Select target 1 and color effects mode: + this.effectsTarget1 = (data & 0x3F) << 16; + this.colorEffectsType = (data >> 6) & 0x3; + //Select target 2: + this.effectsTarget2 = (data & 0x3F00) << 8; +} diff --git a/public/gfiles/gba/IodineGBA/core/graphics/Compositor.js b/public/gfiles/gba/IodineGBA/core/graphics/Compositor.js new file mode 100644 index 00000000..fa023cdd --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/graphics/Compositor.js @@ -0,0 +1,949 @@ +"use strict"; +/* + Copyright (C) 2012-2016 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceCompositor(gfx) { + this.gfx = gfx; + this.doEffects = 0; +} +function GameBoyAdvanceWindowCompositor(gfx) { + this.gfx = gfx; + this.doEffects = 0; +} +function GameBoyAdvanceOBJWindowCompositor(gfx) { + this.gfx = gfx; + this.doEffects = 0; +} +GameBoyAdvanceCompositor.prototype.initialize = GameBoyAdvanceWindowCompositor.prototype.initialize = function () { + this.buffer = this.gfx.buffer; + this.colorEffectsRenderer = this.gfx.colorEffectsRenderer; +} +GameBoyAdvanceOBJWindowCompositor.prototype.initialize = function () { + this.buffer = this.gfx.buffer; + this.colorEffectsRenderer = this.gfx.colorEffectsRenderer; + this.OBJWindowBuffer = this.gfx.objRenderer.scratchWindowBuffer; +} +GameBoyAdvanceCompositor.prototype.preprocess = GameBoyAdvanceWindowCompositor.prototype.preprocess = GameBoyAdvanceOBJWindowCompositor.prototype.preprocess = function (doEffects) { + doEffects = doEffects | 0; + this.doEffects = doEffects | 0; +} +GameBoyAdvanceOBJWindowCompositor.prototype.renderScanLine = GameBoyAdvanceCompositor.prototype.renderScanLine = function (layers) { + layers = layers | 0; + if ((this.doEffects | 0) == 0) { + this.renderNormalScanLine(layers | 0); + } + else { + this.renderScanLineWithEffects(layers | 0); + } +} +GameBoyAdvanceWindowCompositor.prototype.renderScanLine = function (xStart, xEnd, layers) { + xStart = xStart | 0; + xEnd = xEnd | 0; + layers = layers | 0; + if ((this.doEffects | 0) == 0) { + this.renderNormalScanLine(xStart | 0, xEnd | 0, layers | 0); + } + else { + this.renderScanLineWithEffects(xStart | 0, xEnd | 0, layers | 0); + } +} +//Check for SIMD support: +if (typeof SIMD == "object" && typeof SIMD.Int32x4 == "function") { + GameBoyAdvanceCompositor.prototype.mask0 = SIMD.Int32x4.splat(0); + GameBoyAdvanceCompositor.prototype.mask1 = SIMD.Int32x4.splat(0x2000000); + GameBoyAdvanceCompositor.prototype.mask2 = SIMD.Int32x4.splat(0x3800000); + GameBoyAdvanceCompositor.prototype.mask3 = SIMD.Int32x4.splat(0x1800000); + GameBoyAdvanceCompositor.prototype.mask4 = SIMD.Bool32x4.splat(true); +} +function generateIodineGBAGFXCompositors() { + function generateCompositors() { + function generateLoop(compositeType, doEffects, layers) { + function generateLoopHead(useVectorized, compositeType) { + function generateLocalScopeInit(useVectorized, layers, alreadyDeclared) { + function checkDeclared(alreadyDeclared) { + var code = ""; + if (!alreadyDeclared) { + code += "var "; + } + return code; + } + //Declare the necessary temporary variables: + var code = ""; + if (useVectorized) { + //Declare the necessary temporary variables: + code += + "var backdrop = SIMD.Int32x4.splat(this.gfx.backdrop | 0);"; + switch (layers) { + case 0: + //Don't need any if no layers to process: + break; + default: + //Need this temp for more than one layer: + code += + checkDeclared(alreadyDeclared) + "workingPixel = this.mask0;" + + "var test1 = this.mask4;" + + "var test2 = this.mask4;"; + case 0x1: + case 0x2: + case 0x4: + case 0x8: + case 0x10: + //Need these temps for one or more layers: + code += + checkDeclared(alreadyDeclared) + "currentPixel = this.mask0;" + + checkDeclared(alreadyDeclared) + "lowerPixel = this.mask0;"; + } + } + else { + switch (layers) { + case 0: + //Don't need any if no layers to process: + break; + default: + //Need this temp for more than one layer: + code += + checkDeclared(alreadyDeclared) + "workingPixel = 0;"; + case 0x1: + case 0x2: + case 0x4: + case 0x8: + case 0x10: + //Need these temps for one or more layers: + code += + checkDeclared(alreadyDeclared) + "currentPixel = 0;" + + checkDeclared(alreadyDeclared) + "lowerPixel = 0;"; + } + } + return code; + } + function generateLoopBody(useVectorized, doEffects, layers) { + function getSingleLayerPrefix(useVectorized) { + //Pass initialization if processing only 1 layer: + var code = ""; + if (useVectorized) { + code += + "lowerPixel = backdrop;"; + } + else { + code += + "lowerPixel = this.gfx.backdrop | 0;"; + } + return code; + } + function getMultiLayerPrefix(useVectorized) { + //Pass initialization if processing more than 1 layer: + var code = ""; + if (useVectorized) { + code += + "lowerPixel = backdrop;" + + "currentPixel = lowerPixel;"; + } + else { + code += + "lowerPixel = this.gfx.backdrop | 0;" + + "currentPixel = lowerPixel | 0;"; + } + return code; + } + function generateLayerCompareSingle(useVectorized, layerOffset) { + //Only 1 layer specified to be rendered: + var code = ""; + if (useVectorized) { + code += + "currentPixel = SIMD.Int32x4.load(this.buffer, xStart | " + layerOffset + ");" + + "currentPixel = SIMD.Int32x4.select(" + + "SIMD.Int32x4.notEqual(" + + "this.mask0," + + "SIMD.Int32x4.and(currentPixel, this.mask1)" + + ")," + + "lowerPixel," + + "currentPixel" + + ");"; + } + else { + code += + "currentPixel = this.buffer[xStart | " + layerOffset + "] | 0;" + + "if ((currentPixel & 0x2000000) != 0) {" + + "currentPixel = lowerPixel | 0;" + + "}"; + } + return code; + } + function generateLayerCompare(useVectorized, layerOffset) { + //Code unit to be used when rendering more than 1 layer: + var code = ""; + if (useVectorized) { + code += + "workingPixel = SIMD.Int32x4.load(this.buffer, xStart | " + layerOffset + ");" + + "test1 = SIMD.Int32x4.lessThanOrEqual(" + + "SIMD.Int32x4.and(workingPixel, this.mask2)," + + "SIMD.Int32x4.and(currentPixel, this.mask3)" + + ");" + + "lowerPixel = SIMD.Int32x4.select(test1, currentPixel, lowerPixel);" + + "currentPixel = SIMD.Int32x4.select(test1, workingPixel, currentPixel);" + + "test2 = SIMD.Int32x4.lessThanOrEqual(" + + "SIMD.Int32x4.and(workingPixel, this.mask2)," + + "SIMD.Int32x4.and(lowerPixel, this.mask3)" + + ");" + + "lowerPixel = SIMD.Int32x4.select(" + + "test1," + + "lowerPixel," + + "SIMD.Int32x4.select(test2, workingPixel, lowerPixel)" + + ");"; + } + else { + code += + "workingPixel = this.buffer[xStart | " + layerOffset + "] | 0;" + + "if ((workingPixel & 0x3800000) <= (currentPixel & 0x1800000)) {" + + "lowerPixel = currentPixel | 0;" + + "currentPixel = workingPixel | 0;" + + "}" + + "else if ((workingPixel & 0x3800000) <= (lowerPixel & 0x1800000)) {" + + "lowerPixel = workingPixel | 0;" + + "}"; + } + return code; + } + function getColorEffects0Layers(useVectorized, doEffects) { + //Handle checks for color effects here: + var code = ""; + if (useVectorized) { + //No layers: + if (doEffects) { + //Color effects enabled: + code += + "SIMD.Int32x4.store(this.buffer, xStart | 0x700, this.mask0);" + + "SIMD.Int32x4.store(this.buffer, xStart | 0x800, backdrop);"; + } + else { + //No effects enabled: + code += + "SIMD.Int32x4.store(this.buffer, xStart | 0, backdrop);"; + } + } + else { + //No layers: + if (doEffects) { + //Color effects enabled: + code += + "this.buffer[xStart | 0] = this.colorEffectsRenderer.processPixelNormal(0, this.gfx.backdrop | 0) | 0;"; + } + else { + //No effects enabled: + code += + "this.buffer[xStart | 0] = this.gfx.backdrop | 0;" + } + } + return code; + } + function getColorEffectsNoSprites(useVectorized, doEffects) { + //Handle checks for color effects here: + var code = ""; + if (useVectorized) { + //Rendering with no sprite layer: + if (doEffects) { + //Color effects enabled: + code += + "SIMD.Int32x4.store(this.buffer, xStart | 0x700, lowerPixel);" + + "SIMD.Int32x4.store(this.buffer, xStart | 0x800, currentPixel);"; + } + else { + //No effects enabled: + code += + "SIMD.Int32x4.store(this.buffer, xStart | 0, currentPixel);"; + } + } + else { + //Rendering with no sprite layer: + if (doEffects) { + //Color effects enabled: + code += + "this.buffer[xStart | 0] = this.colorEffectsRenderer.processPixelNormal(lowerPixel | 0, currentPixel | 0) | 0;"; + } + else { + //No effects enabled: + code += + "this.buffer[xStart | 0] = currentPixel | 0;"; + } + } + return code; + } + function getColorEffectsWithSprites(useVectorized, doEffects) { + //Handle checks for color effects here: + var code = ""; + if (useVectorized) { + //Rendering with a sprite layer: + code += + "SIMD.Int32x4.store(this.buffer, xStart | 0x700, lowerPixel);" + + "SIMD.Int32x4.store(this.buffer, xStart | 0x800, currentPixel);"; + } + else { + //Rendering with a sprite layer: + code += + "if ((currentPixel & 0x400000) == 0) {"; + if (doEffects) { + //Color effects enabled: + code += + "this.buffer[xStart | 0] = this.colorEffectsRenderer.processPixelNormal(lowerPixel | 0, currentPixel | 0) | 0;"; + } + else { + //No effects enabled: + code += + "this.buffer[xStart | 0] = currentPixel | 0;"; + } + code += + "}" + + "else {" + + //Must handle for semi-transparent sprite case: + "this.buffer[xStart | 0] = this.colorEffectsRenderer.processPixelSprite(lowerPixel | 0, currentPixel | 0) | 0;" + + "}"; + } + return code; + } + function generatePass(useVectorized, doEffects, layers) { + var code = ""; + //Special case each possible layer combination: + switch (layers) { + case 0: + //Backdrop only: + //Color Effects Post Processing: + code += getColorEffects0Layers(useVectorized, doEffects); + break; + case 1: + //Generate temps: + code += getSingleLayerPrefix(useVectorized); + //BG0: + code += generateLayerCompareSingle(useVectorized, 0x100); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 2: + //Generate temps: + code += getSingleLayerPrefix(useVectorized); + //BG1: + code += generateLayerCompareSingle(useVectorized, 0x200); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 3: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 4: + //Generate temps: + code += getSingleLayerPrefix(useVectorized); + //BG2: + code += generateLayerCompareSingle(useVectorized, 0x300); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 5: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 6: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 7: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 8: + //Generate temps: + code += getSingleLayerPrefix(useVectorized); + //BG2: + code += generateLayerCompareSingle(useVectorized, 0x400); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 9: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 0xA: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 0xB: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 0xC: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 0xD: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 0xE: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 0xF: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //Color Effects Post Processing: + code += getColorEffectsNoSprites(useVectorized, doEffects); + break; + case 0x10: + //Generate temps: + code += getSingleLayerPrefix(useVectorized); + //OBJ: + code += generateLayerCompareSingle(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + case 0x11: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + case 0x12: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + case 0x13: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + case 0x14: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + case 0x15: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + case 0x16: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + case 0x17: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + case 0x18: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + case 0x19: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + case 0x1A: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + case 0x1B: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + case 0x1C: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + case 0x1D: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + case 0x1E: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + break; + default: + //Generate temps: + code += getMultiLayerPrefix(useVectorized); + //BG3: + code += generateLayerCompare(useVectorized, 0x400); + //BG2: + code += generateLayerCompare(useVectorized, 0x300); + //BG1: + code += generateLayerCompare(useVectorized, 0x200); + //BG0: + code += generateLayerCompare(useVectorized, 0x100); + //OBJ: + code += generateLayerCompare(useVectorized, 0x500); + //Color Effects Post Processing: + code += getColorEffectsWithSprites(useVectorized, doEffects); + } + return code; + } + //Build the code to put inside a loop: + return generatePass(useVectorized, doEffects, layers); + } + function generateSIMDColorEffectsExternalCall(useVectorized, layers, compositeType) { + var code = ""; + if (useVectorized) { + switch (compositeType) { + case 0: + //Check if we're processing the sprite layer: + if (layers < 0x10) { + //Don't need color effects processing for the else case: + if (doEffects) { + //Effects handling: + code += + ";this.colorEffectsRenderer.processFullNormalEffectsNoSprites()"; + } + } + else { + if (doEffects) { + //Effects + semi-transparency handling: + code += + ";this.colorEffectsRenderer.processFullNormalEffectsWithSprites()"; + } + else { + //Sprite semi-transparency handling: + code += + ";this.colorEffectsRenderer.processFullNoEffectsWithSprites()"; + } + } + break; + case 1: + //Check if we're processing the sprite layer: + if (layers < 0x10) { + //Don't need color effects processing for the else case: + if (doEffects) { + //Effects handling: + code += + ";this.colorEffectsRenderer.processWindowNormalEffectsNoSprites(xStartCopy | 0, xEnd & -4)"; + } + } + else { + if (doEffects) { + //Effects + semi-transparency handling: + code += + ";this.colorEffectsRenderer.processWindowNormalEffectsWithSprites(xStartCopy | 0, xEnd & -4)"; + } + else { + //Sprite semi-transparency handling: + code += + ";this.colorEffectsRenderer.processWindowNoEffectsWithSprites(xStartCopy | 0, xEnd & -4)"; + } + } + break; + } + } + return code; + } + var code = ""; + switch (compositeType) { + //Loop for normal compositor: + case 0: + if (useVectorized) { + code += + generateLocalScopeInit(true, layers, false) + + "for (var xStart = 0; (xStart | 0) < 240; xStart = ((xStart | 0) + 4) | 0) {" + + generateLoopBody(true, doEffects, layers) + + "}"; + } + else { + code += + generateLocalScopeInit(false, layers, false) + + "for (var xStart = 0; (xStart | 0) < 240; xStart = ((xStart | 0) + 1) | 0) {" + + generateLoopBody(false, doEffects, layers) + + "}"; + } + break; + //Loop for window compositor: + case 1: + code += + "xStart = xStart | 0;" + + "xEnd = xEnd | 0;"; + if (useVectorized) { + code += + generateLocalScopeInit(false, layers, false) + + "while ((xStart | 0) < (xEnd | 0) && (xStart | 0) <= (xStart | 0x3)) {" + + generateLoopBody(false, doEffects, layers) + + "xStart = ((xStart | 0) + 1) | 0;" + + "}" + + "var xStartCopy = xStart | 0;" + + generateLocalScopeInit(true, layers, true) + + "while ((xStart | 0) < (xEnd & -4)) {" + + generateLoopBody(true, doEffects, layers) + + "xStart = ((xStart | 0) + 4) | 0;" + + "}" + + generateLocalScopeInit(false, layers, true) + + "while ((xStart | 0) < (xEnd | 0)) {" + + generateLoopBody(false, doEffects, layers) + + "xStart = ((xStart | 0) + 1) | 0;" + + "}"; + } + else { + code += + generateLocalScopeInit(false, layers, false) + + "while ((xStart | 0) < (xEnd | 0)) {" + + generateLoopBody(false, doEffects, layers) + + "xStart = ((xStart | 0) + 1) | 0;" + + "}"; + } + break; + //Loop for OBJ window compositor: + case 2: + code += + generateLocalScopeInit(false, layers, false) + + "for (var xStart = 0; (xStart | 0) < 240; xStart = ((xStart | 0) + 1) | 0) {" + + "if ((this.OBJWindowBuffer[xStart | 0] | 0) < 0x3800000) {" + + generateLoopBody(false, doEffects, layers) + + "}" + + "}"; + } + code += generateSIMDColorEffectsExternalCall(useVectorized, layers, compositeType); + return code; + } + //Build the loop: + return generateLoopHead(typeof SIMD == "object" && typeof SIMD.Int32x4 == "function", compositeType); + } + function generateCompositor(compositeType, doEffects) { + //Get function suffix we'll use depending on color effects usage: + var effectsPrefix = (doEffects) ? "special" : "normal"; + //Loop through all possible combinations of layers: + for (var layers = 0; layers < 0x20; layers++) { + //Codegen the loop: + var code = generateLoop(compositeType, doEffects, layers); + //Compile the code and assign to appropriate compositor object: + switch (compositeType) { + case 0: + //Normal compositor: + GameBoyAdvanceCompositor.prototype[effectsPrefix + layers] = Function(code); + break; + case 1: + //Window compositor: + GameBoyAdvanceWindowCompositor.prototype[effectsPrefix + layers] = Function("xStart", "xEnd", code); + break; + default: + //OBJ window compositor: + GameBoyAdvanceOBJWindowCompositor.prototype[effectsPrefix + layers] = Function(code); + } + } + } + //Build the functions for each of the three compositors: + for (var compositeType = 0; compositeType < 3; compositeType++) { + //Build for the no special effects processing case: + generateCompositor(compositeType, false); + //Build for the special effects processing case: + generateCompositor(compositeType, true); + } + } + function generateDispatches() { + function generateDispatch(coordsSpecified, doEffects) { + function generateDispatchBlock(coordsSpecified, doEffects) { + function generateDispatchHead(coordsSpecified) { + //Initialize some local variables: + var code = ""; + if (coordsSpecified) { + code += + "xStart = xStart | 0;" + + "xEnd = xEnd | 0;"; + } + code += + "layers = layers | 0;"; + return code; + } + function generateDispatchBody(coordsSpecified, doEffects) { + function generateSwitchHead(bodyCode) { + //We're building a switch statement: + var code = + "switch (layers | 0) {" + + bodyCode + + "}"; + return code; + } + function generateSwitchBody(coordsSpecified, doEffects) { + function generateCases(coordsSpecified, doEffects) { + function generateCase(layers, coordsSpecified, doEffects) { + function generateCaseHead(layers, bodyCode) { + //Building the case label: + var code = ""; + if (layers < 0x1F) { + //Not the last case in the list, so specify number: + code += + "case " + layers + ":" + + "{" + + bodyCode + ";" + + "break" + + "};"; + } + else { + //Last case in the list, so place it as default case: + code += + "default:" + + "{" + + bodyCode + + "}"; + } + return code; + } + function generateCaseBody(layers, coordsSpecified, doEffects) { + //Build the function call: + var code = + "this."; + if (doEffects) { + //Special effects: + code += + "special"; + } + else { + //No special effects: + code += + "normal"; + } + code += + layers + + "("; + if (coordsSpecified) { + //Passing some xcoords as parameters: + code += + "xStart | 0, xEnd | 0"; + } + code += + ")"; + return code; + } + //Build the full case unit: + return generateCaseHead(layers, generateCaseBody(layers, coordsSpecified, doEffects)); + } + function generateList(coordsSpecified, doEffects) { + var code = ""; + //Loop through all combinations to build: + for (var layers = 0; layers < 0x20; layers++) { + //Build a case for the specified combination: + code += generateCase(layers, coordsSpecified, doEffects); + } + return code; + } + //Build the entire switch: + return generateList(coordsSpecified, doEffects); + } + //Build the entire switch: + return generateCases(coordsSpecified, doEffects); + } + //Build the switch block: + return generateSwitchHead(generateSwitchBody(coordsSpecified, doEffects)); + } + function generateDispatchCode(coordsSpecified, doEffects) { + //Build the dispatch block: + var code = ""; + code += generateDispatchHead(coordsSpecified); + code += generateDispatchBody(coordsSpecified, doEffects); + return code; + } + return generateDispatchCode(coordsSpecified, doEffects); + } + function generateDispatchFunc(coordsSpecified, code) { + if (coordsSpecified) { + return Function("xStart", "xEnd", "layers", code); + } + else { + return Function("layers", code); + } + } + //Generate the function: + return generateDispatchFunc(coordsSpecified, generateDispatchBlock(coordsSpecified, doEffects)); + } + //Build the functions for each of the six dispatches: + GameBoyAdvanceOBJWindowCompositor.prototype.renderNormalScanLine = GameBoyAdvanceCompositor.prototype.renderNormalScanLine = generateDispatch(false, false); + GameBoyAdvanceWindowCompositor.prototype.renderNormalScanLine = generateDispatch(true, false); + GameBoyAdvanceOBJWindowCompositor.prototype.renderScanLineWithEffects = GameBoyAdvanceCompositor.prototype.renderScanLineWithEffects = generateDispatch(false, true); + GameBoyAdvanceWindowCompositor.prototype.renderScanLineWithEffects = generateDispatch(true, true); + } + //Build and compile the compositors for every possible mode/layer/effect combination: + generateCompositors(); + //Build and compile the dispatches for every possible mode/effect combination: + generateDispatches(); +} +generateIodineGBAGFXCompositors(); diff --git a/public/gfiles/gba/IodineGBA/core/graphics/Mosaic.js b/public/gfiles/gba/IodineGBA/core/graphics/Mosaic.js new file mode 100644 index 00000000..fe958ea6 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/graphics/Mosaic.js @@ -0,0 +1,93 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceMosaicRenderer(buffer) { + this.BGMosaicHSize = 0; + this.BGMosaicVSize = 0; + this.OBJMosaicHSize = 0; + this.OBJMosaicVSize = 0; + this.buffer = buffer; +} +GameBoyAdvanceMosaicRenderer.prototype.attachOBJBuffer = function (objBuffer) { + //Function only called if no typed array view support: + this.objBuffer = objBuffer; +} +GameBoyAdvanceMosaicRenderer.prototype.renderMosaicHorizontal = function (offset) { + offset = offset | 0; + var currentPixel = 0; + var mosaicBlur = ((this.BGMosaicHSize | 0) + 1) | 0; + if ((mosaicBlur | 0) > 1) { //Don't perform a useless loop. + for (var position = 0; (position | 0) < 240; position = ((position | 0) + 1) | 0) { + if ((((position | 0) % (mosaicBlur | 0)) | 0) == 0) { + currentPixel = this.buffer[position | offset] | 0; + } + else { + this.buffer[position | offset] = currentPixel | 0; + } + } + } +} +if (__VIEWS_SUPPORTED__) { + GameBoyAdvanceMosaicRenderer.prototype.renderOBJMosaicHorizontal = function (xOffset, xSize) { + xOffset = xOffset | 0; + xSize = xSize | 0; + var currentPixel = 0x3800000; + var mosaicBlur = ((this.OBJMosaicHSize | 0) + 1) | 0; + if ((mosaicBlur | 0) > 1) { //Don't perform a useless loop. + for (var position = ((xOffset | 0) % (mosaicBlur | 0)) | 0; (position | 0) < (xSize | 0); position = ((position | 0) + 1) | 0) { + if ((((position | 0) % (mosaicBlur | 0)) | 0) == 0) { + currentPixel = this.buffer[position | 0x600] | 0; + } + this.buffer[position | 0x600] = currentPixel | 0; + } + } + } +} +else { + GameBoyAdvanceMosaicRenderer.prototype.renderOBJMosaicHorizontal = function (xOffset, xSize) { + xOffset = xOffset | 0; + xSize = xSize | 0; + var currentPixel = 0x3800000; + var mosaicBlur = ((this.OBJMosaicHSize | 0) + 1) | 0; + if ((mosaicBlur | 0) > 1) { //Don't perform a useless loop. + for (var position = ((xOffset | 0) % (mosaicBlur | 0)) | 0; (position | 0) < (xSize | 0); position = ((position | 0) + 1) | 0) { + if ((((position | 0) % (mosaicBlur | 0)) | 0) == 0) { + currentPixel = this.objBuffer[position | 0] | 0; + } + this.objBuffer[position | 0] = currentPixel | 0; + } + } + } +} +GameBoyAdvanceMosaicRenderer.prototype.getMosaicYOffset = function (line) { + line = line | 0; + return ((line | 0) % (((this.BGMosaicVSize | 0) + 1) | 0)) | 0; +} +GameBoyAdvanceMosaicRenderer.prototype.getOBJMosaicYOffset = function (line) { + line = line | 0; + return ((line | 0) % (((this.OBJMosaicVSize | 0) + 1) | 0)) | 0; +} +GameBoyAdvanceMosaicRenderer.prototype.writeMOSAIC8_0 = function (data) { + data = data | 0; + this.BGMosaicHSize = data & 0xF; + this.BGMosaicVSize = data >> 4; +} +GameBoyAdvanceMosaicRenderer.prototype.writeMOSAIC8_1 = function (data) { + data = data | 0; + this.OBJMosaicHSize = data & 0xF; + this.OBJMosaicVSize = data >> 4; +} +GameBoyAdvanceMosaicRenderer.prototype.writeMOSAIC16 = function (data) { + data = data | 0; + this.BGMosaicHSize = data & 0xF; + this.BGMosaicVSize = (data >> 4) & 0xF; + this.OBJMosaicHSize = (data >> 8) & 0xF; + this.OBJMosaicVSize = data >> 12; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/graphics/OBJ.js b/public/gfiles/gba/IodineGBA/core/graphics/OBJ.js new file mode 100644 index 00000000..5fb364c9 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/graphics/OBJ.js @@ -0,0 +1,1024 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceOBJRenderer(gfx) { + this.gfx = gfx; +} +GameBoyAdvanceOBJRenderer.prototype.lookupXSize = [ + //Square: + 8, 16, 32, 64, + //Vertical Rectangle: + 16, 32, 32, 64, + //Horizontal Rectangle: + 8, 8, 16, 32 +]; +GameBoyAdvanceOBJRenderer.prototype.lookupYSize = [ + //Square: + 8, 16, 32, 64, + //Vertical Rectangle: + 8, 8, 16, 32, + //Horizontal Rectangle: + 16, 32, 32, 64 +]; +if (__VIEWS_SUPPORTED__) { + if (typeof getUint8Array(1).fill == "function") { + GameBoyAdvanceOBJRenderer.prototype.initialize = function () { + this.paletteOBJ256 = this.gfx.paletteOBJ256; + this.paletteOBJ16 = this.gfx.paletteOBJ16; + this.VRAM = this.gfx.VRAM; + this.cyclesToRender = 1210; + this.VRAM32 = this.gfx.VRAM32; + this.OAMRAM = getUint8Array(0x400); + this.OAMRAM16 = getUint16View(this.OAMRAM); + this.OAMRAM32 = getInt32View(this.OAMRAM); + this.scratchBuffer = getInt32ViewCustom(this.gfx.buffer, 0x500, 0x5F0); + this.scratchWindowBuffer = getInt32Array(240); + this.scratchOBJBuffer = getInt32ViewCustom(this.gfx.buffer, 0x600, 0x680); + this.OBJMatrixParameters = getInt32Array(0x80); + this.initializeOAMTable(); + } + GameBoyAdvanceOBJRenderer.prototype.clearScratch = function () { + this.scratchBuffer.fill(0x3800000); + this.scratchWindowBuffer.fill(0x3800000); + } + } + else { + GameBoyAdvanceOBJRenderer.prototype.initialize = function () { + this.paletteOBJ256 = this.gfx.paletteOBJ256; + this.paletteOBJ16 = this.gfx.paletteOBJ16; + this.VRAM = this.gfx.VRAM; + this.cyclesToRender = 1210; + this.VRAM32 = this.gfx.VRAM32; + this.OAMRAM = getUint8Array(0x400); + this.OAMRAM16 = getUint16View(this.OAMRAM); + this.OAMRAM32 = getInt32View(this.OAMRAM); + this.scratchBuffer = getInt32ViewCustom(this.gfx.buffer, 0x500, 0x5F0); + this.scratchWindowBuffer = getInt32Array(240); + this.scratchOBJBuffer = getInt32ViewCustom(this.gfx.buffer, 0x600, 0x680); + this.clearingBuffer = getInt32Array(240); + this.initializeClearingBuffer(); + this.OBJMatrixParameters = getInt32Array(0x80); + this.initializeOAMTable(); + } + GameBoyAdvanceOBJRenderer.prototype.clearScratch = function () { + this.scratchBuffer.set(this.clearingBuffer); + this.scratchWindowBuffer.set(this.clearingBuffer); + } + GameBoyAdvanceOBJRenderer.prototype.initializeClearingBuffer = function () { + for (var position = 0; (position | 0) < 240; position = ((position | 0) + 1) | 0) { + this.clearingBuffer[position | 0] = 0x3800000; + } + } + } + GameBoyAdvanceOBJRenderer.prototype.outputSpriteNormal = function (xcoord, xcoordEnd, bitFlags) { + xcoord = xcoord | 0; + xcoordEnd = xcoordEnd | 0; + bitFlags = bitFlags | 0; + for (var xSource = 0; (xcoord | 0) < (xcoordEnd | 0); xcoord = ((xcoord | 0) + 1) | 0, xSource = ((xSource | 0) + 1) | 0) { + var pixel = bitFlags | this.scratchOBJBuffer[xSource | 0]; + //Overwrite by priority: + if ((xcoord | 0) > -1 && (pixel & 0x3800000) < (this.scratchBuffer[xcoord | 0] & 0x3800000)) { + this.scratchBuffer[xcoord | 0] = pixel | 0; + } + } + } + GameBoyAdvanceOBJRenderer.prototype.outputSpriteFlipped = function (xcoord, xcoordEnd, bitFlags, xSize) { + xcoord = xcoord | 0; + xcoordEnd = xcoordEnd | 0; + bitFlags = bitFlags | 0; + xSize = xSize | 0; + for (var xSource = ((xSize | 0) - 1) | 0; (xcoord | 0) < (xcoordEnd | 0); xcoord = ((xcoord | 0) + 1) | 0, xSource = ((xSource | 0) - 1) | 0) { + var pixel = bitFlags | this.scratchOBJBuffer[xSource | 0]; + //Overwrite by priority: + if ((xcoord | 0) > -1 && (pixel & 0x3800000) < (this.scratchBuffer[xcoord | 0] & 0x3800000)) { + this.scratchBuffer[xcoord | 0] = pixel | 0; + } + } + } + GameBoyAdvanceOBJRenderer.prototype.outputSpriteNormalOBJWIN = function (xcoord, xcoordEnd) { + xcoord = xcoord | 0; + xcoordEnd = xcoordEnd | 0; + for (var xSource = 0; (xcoord | 0) < (xcoordEnd | 0); xcoord = ((xcoord | 0) + 1) | 0, xSource = ((xSource | 0) + 1) | 0) { + if ((xcoord | 0) > -1 && (this.scratchOBJBuffer[xSource | 0] | 0) != 0) { + this.scratchWindowBuffer[xcoord | 0] = 0; + } + } + } + GameBoyAdvanceOBJRenderer.prototype.outputSpriteFlippedOBJWIN = function (xcoord, xcoordEnd, xSize) { + xcoord = xcoord | 0; + xcoordEnd = xcoordEnd | 0; + xSize = xSize | 0; + for (var xSource = ((xSize | 0) - 1) | 0; (xcoord | 0) < (xcoordEnd | 0); xcoord = ((xcoord | 0) + 1) | 0, xSource = ((xSource | 0) - 1) | 0) { + if ((xcoord | 0) > -1 && (this.scratchOBJBuffer[xSource | 0] | 0) != 0) { + this.scratchWindowBuffer[xcoord | 0] = 0; + } + } + } +} +else { + GameBoyAdvanceOBJRenderer.prototype.initialize = function () { + this.paletteOBJ256 = this.gfx.paletteOBJ256; + this.paletteOBJ16 = this.gfx.paletteOBJ16; + this.VRAM = this.gfx.VRAM; + this.cyclesToRender = 1210; + this.OAMRAM = getUint8Array(0x400); + this.offset = 0x500; + this.scratchBuffer = this.gfx.buffer; + this.scratchWindowBuffer = getInt32Array(240); + this.scratchOBJBuffer = getInt32Array(128); + this.gfx.mosaicRenderer.attachOBJBuffer(this.scratchOBJBuffer); + this.OBJMatrixParameters = getInt32Array(0x80); + this.initializeOAMTable(); + } + GameBoyAdvanceOBJRenderer.prototype.clearScratch = function () { + for (var position = 0; position < 240; ++position) { + this.scratchBuffer[position | this.offset] = 0x3800000; + this.scratchWindowBuffer[position] = 0x3800000; + } + } + GameBoyAdvanceOBJRenderer.prototype.outputSpriteNormal = function (xcoord, xcoordEnd, bitFlags) { + for (var xSource = 0; xcoord < xcoordEnd; ++xcoord, ++xSource) { + var pixel = bitFlags | this.scratchOBJBuffer[xSource]; + //Overwrite by priority: + if ((xcoord | 0) > -1 && (pixel & 0x3800000) < (this.scratchBuffer[xcoord | this.offset] & 0x3800000)) { + this.scratchBuffer[xcoord | this.offset] = pixel; + } + } + } + GameBoyAdvanceOBJRenderer.prototype.outputSpriteFlipped = function (xcoord, xcoordEnd, bitFlags, xSize) { + for (var xSource = xSize - 1; xcoord < xcoordEnd; ++xcoord, --xSource) { + var pixel = bitFlags | this.scratchOBJBuffer[xSource]; + //Overwrite by priority: + if (xcoord > -1 && (pixel & 0x3800000) < (this.scratchBuffer[xcoord | this.offset] & 0x3800000)) { + this.scratchBuffer[xcoord | this.offset] = pixel; + } + } + } + GameBoyAdvanceOBJRenderer.prototype.outputSpriteNormalOBJWIN = function (xcoord, xcoordEnd) { + for (var xSource = 0; xcoord < xcoordEnd; ++xcoord, ++xSource) { + if (xcoord > -1 && this.scratchOBJBuffer[xSource] != 0) { + this.scratchWindowBuffer[xcoord] = 0; + } + } + } + GameBoyAdvanceOBJRenderer.prototype.outputSpriteFlippedOBJWIN = function (xcoord, xcoordEnd, xSize) { + for (var xSource = xSize - 1; xcoord < xcoordEnd; ++xcoord, --xSource) { + if (xcoord > -1 && this.scratchOBJBuffer[xSource] != 0) { + this.scratchWindowBuffer[xcoord] = 0; + } + } + } +} +GameBoyAdvanceOBJRenderer.prototype.initializeOAMTable = function () { + this.OAMTable = []; + for (var spriteNumber = 0; (spriteNumber | 0) < 128; spriteNumber = ((spriteNumber | 0) + 1) | 0) { + this.OAMTable[spriteNumber | 0] = this.makeOAMAttributeTable(); + } +} +if (typeof TypedObject == "object" && typeof TypedObject.StructType == "object") { + GameBoyAdvanceOBJRenderer.prototype.makeOAMAttributeTable = function () { + return new TypedObject.StructType({ + ycoord:TypedObject.int32, + matrix2D:TypedObject.int32, + doubleSizeOrDisabled:TypedObject.int32, + mode:TypedObject.int32, + mosaic:TypedObject.int32, + monolithicPalette:TypedObject.int32, + shape:TypedObject.int32, + xcoord:TypedObject.int32, + matrixParameters:TypedObject.int32, + horizontalFlip:TypedObject.int32, + verticalFlip:TypedObject.int32, + size:TypedObject.int32, + tileNumber:TypedObject.int32, + priority:TypedObject.int32, + paletteNumber:TypedObject.int32 + }); + } +} +else { + GameBoyAdvanceOBJRenderer.prototype.makeOAMAttributeTable = function () { + return { + ycoord:0, + matrix2D:0, + doubleSizeOrDisabled:0, + mode:0, + mosaic:0, + monolithicPalette:0, + shape:0, + xcoord:0, + matrixParameters:0, + horizontalFlip:0, + verticalFlip:0, + size:0, + tileNumber:0, + priority:0, + paletteNumber:0 + }; + } +} +GameBoyAdvanceOBJRenderer.prototype.renderScanLine = function (line) { + line = line | 0; + this.performRenderLoop(line | 0); +} +GameBoyAdvanceOBJRenderer.prototype.performRenderLoop = function (line) { + line = line | 0; + this.clearScratch(); + var cycles = this.cyclesToRender | 0; + for (var objNumber = 0; (objNumber | 0) < 0x80; objNumber = ((objNumber | 0) + 1) | 0) { + cycles = this.renderSprite(line | 0, this.OAMTable[objNumber | 0], cycles | 0) | 0; + } +} +GameBoyAdvanceOBJRenderer.prototype.renderSprite = function (line, sprite, cycles) { + line = line | 0; + cycles = cycles | 0; + if (this.isDrawable(sprite)) { + if ((sprite.mosaic | 0) != 0) { + //Correct line number for mosaic: + line = ((line | 0) - (this.gfx.mosaicRenderer.getOBJMosaicYOffset(line | 0) | 0)) | 0; + } + //Obtain horizontal size info: + var xSize = this.lookupXSize[(sprite.shape << 2) | sprite.size] << (sprite.doubleSizeOrDisabled | 0); + //Obtain vertical size info: + var ySize = this.lookupYSize[(sprite.shape << 2) | sprite.size] << (sprite.doubleSizeOrDisabled | 0); + //Obtain some offsets: + var ycoord = sprite.ycoord | 0; + var yOffset = ((line | 0) - (ycoord | 0)) | 0; + //Overflow Correction: + if ((yOffset | 0) < 0 || (((ycoord | 0) + (ySize | 0)) | 0) > 0x100) { + /* + HW re-offsets any "negative" y-coord values to on-screen unsigned. + Also a bug triggers this on 8-bit ending coordinate overflow from large sprites. + */ + yOffset = ((yOffset | 0) + 0x100) | 0; + } + //Make a sprite line: + ySize = ((ySize | 0) - 1) | 0; + if ((yOffset & ySize) == (yOffset | 0)) { + //Compute clocks required to draw the sprite: + cycles = this.computeCycles(cycles | 0, sprite.matrix2D | 0, xSize | 0) | 0; + //If there's enough cycles, render: + if ((cycles | 0) >= 0) { + switch (sprite.mode | 0) { + case 0: + //Normal/Semi-transparent Sprite: + this.renderRegularSprite(sprite, xSize | 0, ySize | 0, yOffset | 0); + break; + case 1: + //Semi-transparent Sprite: + this.renderSemiTransparentSprite(sprite, xSize | 0, ySize | 0, yOffset | 0); + break; + case 2: + //OBJ-WIN Sprite: + this.renderOBJWINSprite(sprite, xSize | 0, ySize | 0, yOffset | 0); + } + } + } + } + return cycles | 0; +} +GameBoyAdvanceOBJRenderer.prototype.computeCycles = function (cycles, matrix2D, cyclesToSubtract) { + cycles = cycles | 0; + matrix2D = matrix2D | 0; + cyclesToSubtract = cyclesToSubtract | 0; + if ((matrix2D | 0) != 0) { + //Scale & Rotation: + cyclesToSubtract = cyclesToSubtract << 1; + cyclesToSubtract = ((cyclesToSubtract | 0) + 10) | 0; + cycles = ((cycles | 0) - (cyclesToSubtract | 0)) | 0; + + } + else { + //Regular Scrolling: + cycles = ((cycles | 0) - (cyclesToSubtract | 0)) | 0; + } + return cycles | 0; +} +GameBoyAdvanceOBJRenderer.prototype.renderRegularSprite = function (sprite, xSize, ySize, yOffset) { + xSize = xSize | 0; + ySize = ySize | 0; + yOffset = yOffset | 0; + if ((sprite.matrix2D | 0) != 0) { + //Scale & Rotation: + this.renderMatrixSprite(sprite, xSize | 0, ((ySize | 0) + 1) | 0, yOffset | 0); + } + else { + //Regular Scrolling: + this.renderNormalSprite(sprite, xSize | 0, ySize | 0, yOffset | 0); + } + //Copy OBJ scratch buffer to scratch line buffer: + this.outputSpriteToScratch(sprite, xSize | 0); +} +GameBoyAdvanceOBJRenderer.prototype.renderSemiTransparentSprite = function (sprite, xSize, ySize, yOffset) { + xSize = xSize | 0; + ySize = ySize | 0; + yOffset = yOffset | 0; + if ((sprite.matrix2D | 0) != 0) { + //Scale & Rotation: + this.renderMatrixSprite(sprite, xSize | 0, ((ySize | 0) + 1) | 0, yOffset | 0); + } + else { + //Regular Scrolling: + this.renderNormalSprite(sprite, xSize | 0, ySize | 0, yOffset | 0); + } + //Copy OBJ scratch buffer to scratch line buffer: + this.outputSemiTransparentSpriteToScratch(sprite, xSize | 0); +} +GameBoyAdvanceOBJRenderer.prototype.renderOBJWINSprite = function (sprite, xSize, ySize, yOffset) { + xSize = xSize | 0; + ySize = ySize | 0; + yOffset = yOffset | 0; + if ((sprite.matrix2D | 0) != 0) { + //Scale & Rotation: + this.renderMatrixSpriteOBJWIN(sprite, xSize | 0, ((ySize | 0) + 1) | 0, yOffset | 0); + } + else { + //Regular Scrolling: + this.renderNormalSpriteOBJWIN(sprite, xSize | 0, ySize | 0, yOffset | 0); + } + //Copy OBJ scratch buffer to scratch obj-window line buffer: + this.outputSpriteToOBJWINScratch(sprite, xSize | 0); +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceOBJRenderer.prototype.renderMatrixSprite = function (sprite, xSize, ySize, yOffset) { + xSize = xSize | 0; + ySize = ySize | 0; + yOffset = yOffset | 0; + var xDiff = (-(xSize >> 1)) | 0; + var yDiff = ((yOffset | 0) - (ySize >> 1)) | 0; + var xSizeOriginal = xSize >> (sprite.doubleSizeOrDisabled | 0); + var xSizeFixed = xSizeOriginal << 8; + var ySizeOriginal = ySize >> (sprite.doubleSizeOrDisabled | 0); + var ySizeFixed = ySizeOriginal << 8; + var dx = this.OBJMatrixParameters[sprite.matrixParameters | 0] | 0; + var dmx = this.OBJMatrixParameters[sprite.matrixParameters | 1] | 0; + var dy = this.OBJMatrixParameters[sprite.matrixParameters | 2] | 0; + var dmy = this.OBJMatrixParameters[sprite.matrixParameters | 3] | 0; + var pa = Math.imul(dx | 0, xDiff | 0) | 0; + var pb = Math.imul(dmx | 0, yDiff | 0) | 0; + var pc = Math.imul(dy | 0, xDiff | 0) | 0; + var pd = Math.imul(dmy | 0, yDiff | 0) | 0; + var x = ((pa | 0) + (pb | 0)) | 0; + x = ((x | 0) + (xSizeFixed >> 1)) | 0; + var y = ((pc | 0) + (pd | 0)) | 0; + y = ((y | 0) + (ySizeFixed >> 1)) | 0; + for (var position = 0; (position | 0) < (xSize | 0); position = (position + 1) | 0, x = ((x | 0) + (dx | 0)) | 0, y = ((y | 0) + (dy | 0)) | 0) { + if ((x | 0) >= 0 && (y | 0) >= 0 && (x | 0) < (xSizeFixed | 0) && (y | 0) < (ySizeFixed | 0)) { + //Coordinates in range, fetch pixel: + this.scratchOBJBuffer[position | 0] = this.fetchMatrixPixel(sprite, x >> 8, y >> 8, xSizeOriginal | 0) | 0; + } + else { + //Coordinates outside of range, transparency defaulted: + this.scratchOBJBuffer[position | 0] = 0x3800000; + } + } + } + GameBoyAdvanceOBJRenderer.prototype.renderMatrixSpriteOBJWIN = function (sprite, xSize, ySize, yOffset) { + xSize = xSize | 0; + ySize = ySize | 0; + yOffset = yOffset | 0; + var xDiff = (-(xSize >> 1)) | 0; + var yDiff = ((yOffset | 0) - (ySize >> 1)) | 0; + var xSizeOriginal = xSize >> (sprite.doubleSizeOrDisabled | 0); + var xSizeFixed = xSizeOriginal << 8; + var ySizeOriginal = ySize >> (sprite.doubleSizeOrDisabled | 0); + var ySizeFixed = ySizeOriginal << 8; + var dx = this.OBJMatrixParameters[sprite.matrixParameters | 0] | 0; + var dmx = this.OBJMatrixParameters[sprite.matrixParameters | 1] | 0; + var dy = this.OBJMatrixParameters[sprite.matrixParameters | 2] | 0; + var dmy = this.OBJMatrixParameters[sprite.matrixParameters | 3] | 0; + var pa = Math.imul(dx | 0, xDiff | 0) | 0; + var pb = Math.imul(dmx | 0, yDiff | 0) | 0; + var pc = Math.imul(dy | 0, xDiff | 0) | 0; + var pd = Math.imul(dmy | 0, yDiff | 0) | 0; + var x = ((pa | 0) + (pb | 0)) | 0; + x = ((x | 0) + (xSizeFixed >> 1)) | 0; + var y = ((pc | 0) + (pd | 0)) | 0; + y = ((y | 0) + (ySizeFixed >> 1)) | 0; + for (var position = 0; (position | 0) < (xSize | 0); position = (position + 1) | 0, x = ((x | 0) + (dx | 0)) | 0, y = ((y | 0) + (dy | 0)) | 0) { + if ((x | 0) >= 0 && (y | 0) >= 0 && (x | 0) < (xSizeFixed | 0) && (y | 0) < (ySizeFixed | 0)) { + //Coordinates in range, fetch pixel: + this.scratchOBJBuffer[position | 0] = this.fetchMatrixPixelOBJWIN(sprite, x >> 8, y >> 8, xSizeOriginal | 0) | 0; + } + else { + //Coordinates outside of range, transparency defaulted: + this.scratchOBJBuffer[position | 0] = 0; + } + } + } +} +else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceOBJRenderer.prototype.renderMatrixSprite = function (sprite, xSize, ySize, yOffset) { + var xDiff = -(xSize >> 1); + var yDiff = yOffset - (ySize >> 1); + var xSizeOriginal = xSize >> sprite.doubleSizeOrDisabled; + var xSizeFixed = xSizeOriginal << 8; + var ySizeOriginal = ySize >> sprite.doubleSizeOrDisabled; + var ySizeFixed = ySizeOriginal << 8; + var dx = this.OBJMatrixParameters[sprite.matrixParameters]; + var dmx = this.OBJMatrixParameters[sprite.matrixParameters | 1]; + var dy = this.OBJMatrixParameters[sprite.matrixParameters | 2]; + var dmy = this.OBJMatrixParameters[sprite.matrixParameters | 3]; + var pa = dx * xDiff; + var pb = dmx * yDiff; + var pc = dy * xDiff; + var pd = dmy * yDiff; + var x = pa + pb + (xSizeFixed >> 1); + var y = pc + pd + (ySizeFixed >> 1); + for (var position = 0; position < xSize; ++position, x += dx, y += dy) { + if (x >= 0 && y >= 0 && x < xSizeFixed && y < ySizeFixed) { + //Coordinates in range, fetch pixel: + this.scratchOBJBuffer[position] = this.fetchMatrixPixel(sprite, x >> 8, y >> 8, xSizeOriginal); + } + else { + //Coordinates outside of range, transparency defaulted: + this.scratchOBJBuffer[position] = 0x3800000; + } + } + } + GameBoyAdvanceOBJRenderer.prototype.renderMatrixSpriteOBJWIN = function (sprite, xSize, ySize, yOffset) { + var xDiff = -(xSize >> 1); + var yDiff = yOffset - (ySize >> 1); + var xSizeOriginal = xSize >> sprite.doubleSizeOrDisabled; + var xSizeFixed = xSizeOriginal << 8; + var ySizeOriginal = ySize >> sprite.doubleSizeOrDisabled; + var ySizeFixed = ySizeOriginal << 8; + var dx = this.OBJMatrixParameters[sprite.matrixParameters]; + var dmx = this.OBJMatrixParameters[sprite.matrixParameters | 1]; + var dy = this.OBJMatrixParameters[sprite.matrixParameters | 2]; + var dmy = this.OBJMatrixParameters[sprite.matrixParameters | 3]; + var pa = dx * xDiff; + var pb = dmx * yDiff; + var pc = dy * xDiff; + var pd = dmy * yDiff; + var x = pa + pb + (xSizeFixed >> 1); + var y = pc + pd + (ySizeFixed >> 1); + for (var position = 0; position < xSize; ++position, x += dx, y += dy) { + if (x >= 0 && y >= 0 && x < xSizeFixed && y < ySizeFixed) { + //Coordinates in range, fetch pixel: + this.scratchOBJBuffer[position] = this.fetchMatrixPixelOBJWIN(sprite, x >> 8, y >> 8, xSizeOriginal); + } + else { + //Coordinates outside of range, transparency defaulted: + this.scratchOBJBuffer[position] = 0; + } + } + } +} +GameBoyAdvanceOBJRenderer.prototype.fetchMatrixPixel = function (sprite, x, y, xSize) { + x = x | 0; + y = y | 0; + xSize = xSize | 0; + if ((sprite.monolithicPalette | 0) != 0) { + //256 Colors / 1 Palette: + var address = this.tileNumberToAddress256(sprite.tileNumber | 0, xSize | 0, y | 0) | 0; + address = ((address | 0) + (this.tileRelativeAddressOffset(x | 0, y | 0) | 0)) | 0; + return this.paletteOBJ256[this.VRAM[address | 0] | 0] | 0; + } + else { + //16 Colors / 16 palettes: + var address = this.tileNumberToAddress16(sprite.tileNumber | 0, xSize | 0, y | 0) | 0; + address = ((address | 0) + ((this.tileRelativeAddressOffset(x | 0, y | 0) >> 1) | 0)) | 0; + if ((x & 0x1) == 0) { + return this.paletteOBJ16[sprite.paletteNumber | this.VRAM[address | 0] & 0xF] | 0; + } + else { + return this.paletteOBJ16[sprite.paletteNumber | this.VRAM[address | 0] >> 4] | 0; + } + } +} +GameBoyAdvanceOBJRenderer.prototype.fetchMatrixPixelOBJWIN = function (sprite, x, y, xSize) { + x = x | 0; + y = y | 0; + xSize = xSize | 0; + if ((sprite.monolithicPalette | 0) != 0) { + //256 Colors / 1 Palette: + var address = this.tileNumberToAddress256(sprite.tileNumber | 0, xSize | 0, y | 0) | 0; + address = ((address | 0) + (this.tileRelativeAddressOffset(x | 0, y | 0) | 0)) | 0; + return this.VRAM[address | 0] | 0; + } + else { + //16 Colors / 16 palettes: + var address = this.tileNumberToAddress16(sprite.tileNumber | 0, xSize | 0, y | 0) | 0; + address = ((address | 0) + ((this.tileRelativeAddressOffset(x | 0, y | 0) >> 1) | 0)) | 0; + if ((x & 0x1) == 0) { + return this.VRAM[address | 0] & 0xF; + } + else { + return this.VRAM[address | 0] >> 4; + } + } +} +GameBoyAdvanceOBJRenderer.prototype.tileRelativeAddressOffset = function (x, y) { + x = x | 0; + y = y | 0; + return ((((y & 7) + (x & -8)) << 3) + (x & 0x7)) | 0; +} +GameBoyAdvanceOBJRenderer.prototype.renderNormalSprite = function (sprite, xSize, ySize, yOffset) { + xSize = xSize | 0; + ySize = ySize | 0; + yOffset = yOffset | 0; + if ((sprite.verticalFlip | 0) != 0) { + //Flip y-coordinate offset: + yOffset = ((ySize | 0) - (yOffset | 0)) | 0; + } + if ((sprite.monolithicPalette | 0) != 0) { + //256 Colors / 1 Palette: + var address = this.tileNumberToAddress256(sprite.tileNumber | 0, xSize | 0, yOffset | 0) | 0; + address = ((address | 0) + ((yOffset & 7) << 3)) | 0; + this.render256ColorPaletteSprite(address | 0, xSize | 0); + } + else { + //16 Colors / 16 palettes: + var address = this.tileNumberToAddress16(sprite.tileNumber | 0, xSize | 0, yOffset | 0) | 0; + address = ((address | 0) + ((yOffset & 7) << 2)) | 0; + this.render16ColorPaletteSprite(address | 0, xSize | 0, sprite.paletteNumber | 0); + } +} +GameBoyAdvanceOBJRenderer.prototype.renderNormalSpriteOBJWIN = function (sprite, xSize, ySize, yOffset) { + xSize = xSize | 0; + ySize = ySize | 0; + yOffset = yOffset | 0; + if ((sprite.verticalFlip | 0) != 0) { + //Flip y-coordinate offset: + yOffset = ((ySize | 0) - (yOffset | 0)) | 0; + } + if ((sprite.monolithicPalette | 0) != 0) { + //256 Colors / 1 Palette: + var address = this.tileNumberToAddress256(sprite.tileNumber | 0, xSize | 0, yOffset | 0) | 0; + address = ((address | 0) + ((yOffset & 7) << 3)) | 0; + this.render256ColorPaletteSpriteOBJWIN(address | 0, xSize | 0); + } + else { + //16 Colors / 16 palettes: + var address = this.tileNumberToAddress16(sprite.tileNumber | 0, xSize | 0, yOffset | 0) | 0; + address = ((address | 0) + ((yOffset & 7) << 2)) | 0; + this.render16ColorPaletteSpriteOBJWIN(address | 0, xSize | 0); + } +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceOBJRenderer.prototype.render256ColorPaletteSprite = function (address, xSize) { + address = address >> 2; + xSize = xSize | 0; + var data = 0; + for (var objBufferPos = 0; (objBufferPos | 0) < (xSize | 0); objBufferPos = ((objBufferPos | 0) + 8) | 0) { + data = this.VRAM32[address | 0] | 0; + this.scratchOBJBuffer[objBufferPos | 0] = this.paletteOBJ256[data & 0xFF] | 0; + this.scratchOBJBuffer[objBufferPos | 1] = this.paletteOBJ256[(data >> 8) & 0xFF] | 0; + this.scratchOBJBuffer[objBufferPos | 2] = this.paletteOBJ256[(data >> 16) & 0xFF] | 0; + this.scratchOBJBuffer[objBufferPos | 3] = this.paletteOBJ256[data >>> 24] | 0; + data = this.VRAM32[address | 1] | 0; + this.scratchOBJBuffer[objBufferPos | 4] = this.paletteOBJ256[data & 0xFF] | 0; + this.scratchOBJBuffer[objBufferPos | 5] = this.paletteOBJ256[(data >> 8) & 0xFF] | 0; + this.scratchOBJBuffer[objBufferPos | 6] = this.paletteOBJ256[(data >> 16) & 0xFF] | 0; + this.scratchOBJBuffer[objBufferPos | 7] = this.paletteOBJ256[data >>> 24] | 0; + address = ((address | 0) + 0x10) | 0; + } + } + GameBoyAdvanceOBJRenderer.prototype.render256ColorPaletteSpriteOBJWIN = function (address, xSize) { + address = address >> 2; + xSize = xSize | 0; + var data = 0; + for (var objBufferPos = 0; (objBufferPos | 0) < (xSize | 0); objBufferPos = ((objBufferPos | 0) + 8) | 0) { + data = this.VRAM32[address | 0] | 0; + this.scratchOBJBuffer[objBufferPos | 0] = data & 0xFF; + this.scratchOBJBuffer[objBufferPos | 1] = (data >> 8) & 0xFF; + this.scratchOBJBuffer[objBufferPos | 2] = (data >> 16) & 0xFF; + this.scratchOBJBuffer[objBufferPos | 3] = data >>> 24; + data = this.VRAM32[address | 1] | 0; + this.scratchOBJBuffer[objBufferPos | 4] = data & 0xFF; + this.scratchOBJBuffer[objBufferPos | 5] = (data >> 8) & 0xFF; + this.scratchOBJBuffer[objBufferPos | 6] = (data >> 16) & 0xFF; + this.scratchOBJBuffer[objBufferPos | 7] = data >>> 24; + address = ((address | 0) + 0x10) | 0; + } + } + GameBoyAdvanceOBJRenderer.prototype.render16ColorPaletteSprite = function (address, xSize, paletteOffset) { + address = address >> 2; + xSize = xSize | 0; + paletteOffset = paletteOffset | 0; + var data = 0; + for (var objBufferPos = 0; (objBufferPos | 0) < (xSize | 0); objBufferPos = ((objBufferPos | 0) + 8) | 0) { + data = this.VRAM32[address | 0] | 0; + this.scratchOBJBuffer[objBufferPos | 0] = this.paletteOBJ16[paletteOffset | (data & 0xF)] | 0; + this.scratchOBJBuffer[objBufferPos | 1] = this.paletteOBJ16[paletteOffset | ((data >> 4) & 0xF)] | 0; + this.scratchOBJBuffer[objBufferPos | 2] = this.paletteOBJ16[paletteOffset | ((data >> 8) & 0xF)] | 0; + this.scratchOBJBuffer[objBufferPos | 3] = this.paletteOBJ16[paletteOffset | ((data >> 12) & 0xF)] | 0; + this.scratchOBJBuffer[objBufferPos | 4] = this.paletteOBJ16[paletteOffset | ((data >> 16) & 0xF)] | 0; + this.scratchOBJBuffer[objBufferPos | 5] = this.paletteOBJ16[paletteOffset | ((data >> 20) & 0xF)] | 0; + this.scratchOBJBuffer[objBufferPos | 6] = this.paletteOBJ16[paletteOffset | ((data >> 24) & 0xF)] | 0; + this.scratchOBJBuffer[objBufferPos | 7] = this.paletteOBJ16[paletteOffset | (data >>> 28)] | 0; + address = ((address | 0) + 0x8) | 0; + } + } + GameBoyAdvanceOBJRenderer.prototype.render16ColorPaletteSpriteOBJWIN = function (address, xSize) { + address = address >> 2; + xSize = xSize | 0; + var data = 0; + for (var objBufferPos = 0; (objBufferPos | 0) < (xSize | 0); objBufferPos = ((objBufferPos | 0) + 8) | 0) { + data = this.VRAM32[address | 0] | 0; + this.scratchOBJBuffer[objBufferPos | 0] = data & 0xF; + this.scratchOBJBuffer[objBufferPos | 1] = (data >> 4) & 0xF; + this.scratchOBJBuffer[objBufferPos | 2] = (data >> 8) & 0xF; + this.scratchOBJBuffer[objBufferPos | 3] = (data >> 12) & 0xF; + this.scratchOBJBuffer[objBufferPos | 4] = (data >> 16) & 0xF; + this.scratchOBJBuffer[objBufferPos | 5] = (data >> 20) & 0xF; + this.scratchOBJBuffer[objBufferPos | 6] = (data >> 24) & 0xF; + this.scratchOBJBuffer[objBufferPos | 7] = data >>> 28; + address = ((address | 0) + 0x8) | 0; + } + } +} +else { + GameBoyAdvanceOBJRenderer.prototype.render256ColorPaletteSprite = function (address, xSize) { + for (var objBufferPos = 0; objBufferPos < xSize;) { + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ256[this.VRAM[address++]]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ256[this.VRAM[address++]]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ256[this.VRAM[address++]]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ256[this.VRAM[address++]]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ256[this.VRAM[address++]]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ256[this.VRAM[address++]]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ256[this.VRAM[address++]]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ256[this.VRAM[address]]; + address += 0x39; + } + } + GameBoyAdvanceOBJRenderer.prototype.render256ColorPaletteSpriteOBJWIN = function (address, xSize) { + for (var objBufferPos = 0; objBufferPos < xSize;) { + this.scratchOBJBuffer[objBufferPos++] = this.VRAM[address++]; + this.scratchOBJBuffer[objBufferPos++] = this.VRAM[address++]; + this.scratchOBJBuffer[objBufferPos++] = this.VRAM[address++]; + this.scratchOBJBuffer[objBufferPos++] = this.VRAM[address++]; + this.scratchOBJBuffer[objBufferPos++] = this.VRAM[address++]; + this.scratchOBJBuffer[objBufferPos++] = this.VRAM[address++]; + this.scratchOBJBuffer[objBufferPos++] = this.VRAM[address++]; + this.scratchOBJBuffer[objBufferPos++] = this.VRAM[address]; + address += 0x39; + } + } + GameBoyAdvanceOBJRenderer.prototype.render16ColorPaletteSprite = function (address, xSize, paletteOffset) { + var data = 0; + for (var objBufferPos = 0; objBufferPos < xSize;) { + data = this.VRAM[address++]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ16[paletteOffset | (data & 0xF)]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ16[paletteOffset | (data >> 4)]; + data = this.VRAM[address++]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ16[paletteOffset | (data & 0xF)]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ16[paletteOffset | (data >> 4)]; + data = this.VRAM[address++]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ16[paletteOffset | (data & 0xF)]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ16[paletteOffset | (data >> 4)]; + data = this.VRAM[address]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ16[paletteOffset | (data & 0xF)]; + this.scratchOBJBuffer[objBufferPos++] = this.paletteOBJ16[paletteOffset | (data >> 4)]; + address += 0x1D; + } + } + GameBoyAdvanceOBJRenderer.prototype.render16ColorPaletteSpriteOBJWIN = function (address, xSize) { + var data = 0; + for (var objBufferPos = 0; objBufferPos < xSize;) { + data = this.VRAM[address++]; + this.scratchOBJBuffer[objBufferPos++] = data & 0xF; + this.scratchOBJBuffer[objBufferPos++] = data >> 4; + data = this.VRAM[address++]; + this.scratchOBJBuffer[objBufferPos++] = data & 0xF; + this.scratchOBJBuffer[objBufferPos++] = data >> 4; + data = this.VRAM[address++]; + this.scratchOBJBuffer[objBufferPos++] = data & 0xF; + this.scratchOBJBuffer[objBufferPos++] = data >> 4; + data = this.VRAM[address]; + this.scratchOBJBuffer[objBufferPos++] = data & 0xF; + this.scratchOBJBuffer[objBufferPos++] = data >> 4; + address += 0x1D; + } + } +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceOBJRenderer.prototype.tileNumberToAddress256 = function (tileNumber, xSize, yOffset) { + tileNumber = tileNumber | 0; + xSize = xSize | 0; + yOffset = yOffset | 0; + if ((this.gfx.displayControl & 0x40) == 0) { + //2D Mapping (32 8x8 tiles by 32 8x8 tiles): + //Hardware ignores the LSB in this case: + tileNumber = ((tileNumber & -2) + (Math.imul(yOffset >> 3, 0x20) | 0)) | 0; + } + else { + //1D Mapping: + //256 Color Palette: + tileNumber = ((tileNumber | 0) + (Math.imul(yOffset >> 3, xSize >> 2) | 0)) | 0; + } + //Starting address of currently drawing sprite line: + return ((tileNumber << 5) + 0x10000) | 0; + } + GameBoyAdvanceOBJRenderer.prototype.tileNumberToAddress16 = function (tileNumber, xSize, yOffset) { + tileNumber = tileNumber | 0; + xSize = xSize | 0; + yOffset = yOffset | 0; + if ((this.gfx.displayControl & 0x40) == 0) { + //2D Mapping (32 8x8 tiles by 32 8x8 tiles): + tileNumber = ((tileNumber | 0) + (Math.imul(yOffset >> 3, 0x20) | 0)) | 0; + } + else { + //1D Mapping: + //16 Color Palette: + tileNumber = ((tileNumber | 0) + (Math.imul(yOffset >> 3, xSize >> 3) | 0)) | 0; + } + //Starting address of currently drawing sprite line: + return ((tileNumber << 5) + 0x10000) | 0; + } +} +else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceOBJRenderer.prototype.tileNumberToAddress256 = function (tileNumber, xSize, yOffset) { + if ((this.gfx.displayControl & 0x40) == 0) { + //2D Mapping (32 8x8 tiles by 32 8x8 tiles): + //Hardware ignores the LSB in this case: + tileNumber &= -2; + tileNumber += (yOffset >> 3) * 0x20; + } + else { + //1D Mapping: + tileNumber += (yOffset >> 3) * (xSize >> 2); + } + //Starting address of currently drawing sprite line: + return (tileNumber << 5) + 0x10000; + } + GameBoyAdvanceOBJRenderer.prototype.tileNumberToAddress16 = function (tileNumber, xSize, yOffset) { + if ((this.gfx.displayControl & 0x40) == 0) { + //2D Mapping (32 8x8 tiles by 32 8x8 tiles): + tileNumber += (yOffset >> 3) * 0x20; + } + else { + //1D Mapping: + tileNumber += (yOffset >> 3) * (xSize >> 3); + } + //Starting address of currently drawing sprite line: + return (tileNumber << 5) + 0x10000; + } +} +GameBoyAdvanceOBJRenderer.prototype.outputSpriteToScratch = function (sprite, xSize) { + xSize = xSize | 0; + //Simulate x-coord wrap around logic: + var xcoord = sprite.xcoord | 0; + if ((xcoord | 0) > ((0x200 - (xSize | 0)) | 0)) { + xcoord = ((xcoord | 0) - 0x200) | 0; + } + //Perform the mosaic transform: + if ((sprite.mosaic | 0) != 0) { + this.gfx.mosaicRenderer.renderOBJMosaicHorizontal(xcoord | 0, xSize | 0); + } + //Resolve end point: + var xcoordEnd = Math.min(((xcoord | 0) + (xSize | 0)) | 0, 240) | 0; + //Flag for compositor to ID the pixels as OBJ: + var bitFlags = (sprite.priority << 23) | 0x100000; + if ((sprite.horizontalFlip | 0) == 0 || (sprite.matrix2D | 0) != 0) { + //Normal: + this.outputSpriteNormal(xcoord | 0, xcoordEnd | 0, bitFlags | 0); + } + else { + //Flipped Horizontally: + this.outputSpriteFlipped(xcoord | 0, xcoordEnd | 0, bitFlags | 0, xSize | 0); + } +} +GameBoyAdvanceOBJRenderer.prototype.outputSemiTransparentSpriteToScratch = function (sprite, xSize) { + xSize = xSize | 0; + //Simulate x-coord wrap around logic: + var xcoord = sprite.xcoord | 0; + if ((xcoord | 0) > ((0x200 - (xSize | 0)) | 0)) { + xcoord = ((xcoord | 0) - 0x200) | 0; + } + //Perform the mosaic transform: + if ((sprite.mosaic | 0) != 0) { + this.gfx.mosaicRenderer.renderOBJMosaicHorizontal(xcoord | 0, xSize | 0); + } + //Resolve end point: + var xcoordEnd = Math.min(((xcoord | 0) + (xSize | 0)) | 0, 240) | 0; + //Flag for compositor to ID the pixels as OBJ: + var bitFlags = (sprite.priority << 23) | 0x500000; + if ((sprite.horizontalFlip | 0) == 0 || (sprite.matrix2D | 0) != 0) { + //Normal: + this.outputSpriteNormal(xcoord | 0, xcoordEnd | 0, bitFlags | 0); + } + else { + //Flipped Horizontally: + this.outputSpriteFlipped(xcoord | 0, xcoordEnd | 0, bitFlags | 0, xSize | 0); + } +} +GameBoyAdvanceOBJRenderer.prototype.outputSpriteToOBJWINScratch = function (sprite, xSize) { + xSize = xSize | 0; + //Simulate x-coord wrap around logic: + var xcoord = sprite.xcoord | 0; + if ((xcoord | 0) > ((0x200 - (xSize | 0)) | 0)) { + xcoord = ((xcoord | 0) - 0x200) | 0; + } + //Perform the mosaic transform: + if ((sprite.mosaic | 0) != 0) { + this.gfx.mosaicRenderer.renderOBJMosaicHorizontal(xcoord | 0, xSize | 0); + } + //Resolve end point: + var xcoordEnd = Math.min(((xcoord | 0) + (xSize | 0)) | 0, 240) | 0; + if ((sprite.horizontalFlip | 0) == 0 || (sprite.matrix2D | 0) != 0) { + //Normal: + this.outputSpriteNormalOBJWIN(xcoord | 0, xcoordEnd | 0); + } + else { + //Flipped Horizontally: + this.outputSpriteFlippedOBJWIN(xcoord | 0, xcoordEnd | 0, xSize | 0); + } +} +GameBoyAdvanceOBJRenderer.prototype.isDrawable = function (sprite) { + //Make sure we pass some checks that real hardware does: + if ((sprite.mode | 0) <= 2) { + if ((sprite.doubleSizeOrDisabled | 0) == 0 || (sprite.matrix2D | 0) != 0) { + if ((sprite.shape | 0) < 3) { + if ((this.gfx.displayControl & 0x7) < 3 || (sprite.tileNumber | 0) >= 0x200) { + return true; + } + } + } + } + return false; +} +GameBoyAdvanceOBJRenderer.prototype.setHBlankIntervalFreeStatus = function (data) { + data = data | 0; + if ((data | 0) != 0) { + this.cyclesToRender = 954; + } + else { + this.cyclesToRender = 1210; + } +} +GameBoyAdvanceOBJRenderer.prototype.readOAM = function (address) { + return this.OAMRAM[address & 0x3FF] | 0; +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceOBJRenderer.prototype.writeOAM16 = function (address, data) { + address = address | 0; + data = data | 0; + var OAMTable = this.OAMTable[address >> 2]; + switch (address & 0x3) { + //Attrib 0: + case 0: + OAMTable.ycoord = data & 0xFF; + OAMTable.matrix2D = data & 0x100; + OAMTable.doubleSizeOrDisabled = (data & 0x200) >> 9; + OAMTable.mode = (data >> 10) & 0x3; + OAMTable.mosaic = data & 0x1000; + OAMTable.monolithicPalette = data & 0x2000; + OAMTable.shape = data >> 14; + break; + //Attrib 1: + case 1: + OAMTable.xcoord = data & 0x1FF; + OAMTable.matrixParameters = (data >> 7) & 0x7C; + OAMTable.horizontalFlip = data & 0x1000; + OAMTable.verticalFlip = data & 0x2000; + OAMTable.size = data >> 14; + break; + //Attrib 2: + case 2: + OAMTable.tileNumber = data & 0x3FF; + OAMTable.priority = (data >> 10) & 0x3; + OAMTable.paletteNumber = (data >> 8) & 0xF0; + break; + //Scaling/Rotation Parameter: + default: + this.OBJMatrixParameters[address >> 2] = (data << 16) >> 16; + } + this.OAMRAM16[address | 0] = data & 0xFFFF; + } + GameBoyAdvanceOBJRenderer.prototype.writeOAM32 = function (address, data) { + address = address | 0; + data = data | 0; + var OAMTable = this.OAMTable[address >> 1]; + if ((address & 0x1) == 0) { + //Attrib 0: + OAMTable.ycoord = data & 0xFF; + OAMTable.matrix2D = data & 0x100; + OAMTable.doubleSizeOrDisabled = (data & 0x200) >> 9; + OAMTable.mode = (data >> 10) & 0x3; + OAMTable.mosaic = data & 0x1000; + OAMTable.monolithicPalette = data & 0x2000; + OAMTable.shape = (data >> 14) & 0x3; + //Attrib 1: + OAMTable.xcoord = (data >> 16) & 0x1FF; + OAMTable.matrixParameters = (data >> 23) & 0x7C; + OAMTable.horizontalFlip = data & 0x10000000; + OAMTable.verticalFlip = data & 0x20000000; + OAMTable.size = data >>> 30; + } + else { + //Attrib 2: + OAMTable.tileNumber = data & 0x3FF; + OAMTable.priority = (data >> 10) & 0x3; + OAMTable.paletteNumber = (data >> 8) & 0xF0; + //Scaling/Rotation Parameter: + this.OBJMatrixParameters[address >> 1] = data >> 16; + } + this.OAMRAM32[address | 0] = data | 0; + } + GameBoyAdvanceOBJRenderer.prototype.readOAM16 = function (address) { + address = address | 0; + return this.OAMRAM16[address & 0x1FF] | 0; + } + GameBoyAdvanceOBJRenderer.prototype.readOAM32 = function (address) { + address = address | 0; + return this.OAMRAM32[address & 0xFF] | 0; + } +} +else { + GameBoyAdvanceOBJRenderer.prototype.writeOAM16 = function (address, data) { + address = address | 0; + data = data | 0; + address = address & 0x1FF; + var OAMTable = this.OAMTable[address >> 2]; + switch (address & 0x3) { + //Attrib 0: + case 0: + OAMTable.ycoord = data & 0xFF; + OAMTable.matrix2D = data & 0x100; + OAMTable.doubleSizeOrDisabled = (data & 0x200) >> 9; + OAMTable.mode = (data >> 10) & 0x3; + OAMTable.mosaic = data & 0x1000; + OAMTable.monolithicPalette = data & 0x2000; + OAMTable.shape = data >> 14; + break; + //Attrib 1: + case 1: + OAMTable.xcoord = data & 0x1FF; + OAMTable.matrixParameters = (data >> 7) & 0x7C; + OAMTable.horizontalFlip = data & 0x1000; + OAMTable.verticalFlip = data & 0x2000; + OAMTable.size = data >> 14; + break; + //Attrib 2: + case 2: + OAMTable.tileNumber = data & 0x3FF; + OAMTable.priority = (data >> 10) & 0x3; + OAMTable.paletteNumber = (data >> 8) & 0xF0; + break; + //Scaling/Rotation Parameter: + default: + this.OBJMatrixParameters[address >> 2] = (data << 16) >> 16; + } + address = address << 1; + this.OAMRAM[address | 0] = data & 0xFF; + this.OAMRAM[address | 1] = data >> 8; + } + GameBoyAdvanceOBJRenderer.prototype.writeOAM32 = function (address, data) { + address = address | 0; + data = data | 0; + address = address & 0xFF; + var OAMTable = this.OAMTable[address >> 1]; + if ((address & 0x1) == 0) { + //Attrib 0: + OAMTable.ycoord = data & 0xFF; + OAMTable.matrix2D = data & 0x100; + OAMTable.doubleSizeOrDisabled = (data & 0x200) >> 9; + OAMTable.mode = (data >> 10) & 0x3; + OAMTable.mosaic = data & 0x1000; + OAMTable.monolithicPalette = data & 0x2000; + OAMTable.shape = (data >> 14) & 0x3; + //Attrib 1: + OAMTable.xcoord = (data >> 16) & 0x1FF; + OAMTable.matrixParameters = (data >> 23) & 0x7C; + OAMTable.horizontalFlip = data & 0x10000000; + OAMTable.verticalFlip = data & 0x20000000; + OAMTable.size = data >>> 30; + } + else { + //Attrib 2: + OAMTable.tileNumber = data & 0x3FF; + OAMTable.priority = (data >> 10) & 0x3; + OAMTable.paletteNumber = (data >> 8) & 0xF0; + //Scaling/Rotation Parameter: + this.OBJMatrixParameters[address >> 1] = data >> 16; + } + address = address << 2; + this.OAMRAM[address | 0] = data & 0xFF; + this.OAMRAM[address | 1] = (data >> 8) & 0xFF; + this.OAMRAM[address | 2] = (data >> 16) & 0xFF; + this.OAMRAM[address | 3] = data >>> 24; + } + GameBoyAdvanceOBJRenderer.prototype.readOAM16 = function (address) { + address &= 0x1FF; + address <<= 1; + return this.OAMRAM[address] | (this.OAMRAM[address | 1] << 8); + } + GameBoyAdvanceOBJRenderer.prototype.readOAM32 = function (address) { + address &= 0xFF; + address <<= 2; + return this.OAMRAM[address] | (this.OAMRAM[address | 1] << 8) | (this.OAMRAM[address | 2] << 16) | (this.OAMRAM[address | 3] << 24); + } +} diff --git a/public/gfiles/gba/IodineGBA/core/graphics/OBJWindow.js b/public/gfiles/gba/IodineGBA/core/graphics/OBJWindow.js new file mode 100644 index 00000000..e8c695bd --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/graphics/OBJWindow.js @@ -0,0 +1,41 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceOBJWindowRenderer(compositor) { + //Get a layer compositor that we'll send our parameters for windowing to: + this.compositor = compositor; +} +GameBoyAdvanceOBJWindowRenderer.prototype.initialize = function () { + //Initialize the compositor: + this.compositor.initialize(); + //Layer masking & color effects control: + this.WINOBJOutside = 0; + //Need to update the color effects status in the compositor: + this.preprocess(); +} +GameBoyAdvanceOBJWindowRenderer.prototype.renderScanLine = function (line, toRender) { + line = line | 0; + toRender = toRender | 0; + //Windowing can disable out further layers: + toRender = toRender & this.WINOBJOutside; + //Windowing occurs where there is a non-transparent "obj-win" sprite: + this.compositor.renderScanLine(toRender | 0); +} +GameBoyAdvanceOBJWindowRenderer.prototype.writeWINOBJIN8 = function (data) { + data = data | 0; + //Layer masking & color effects control: + this.WINOBJOutside = data | 0; + //Need to update the color effects status in the compositor: + this.preprocess(); +} +GameBoyAdvanceOBJWindowRenderer.prototype.preprocess = function () { + //Update the color effects status in the compositor: + this.compositor.preprocess(this.WINOBJOutside & 0x20); +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/graphics/Renderer.js b/public/gfiles/gba/IodineGBA/core/graphics/Renderer.js new file mode 100644 index 00000000..a4013a82 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/graphics/Renderer.js @@ -0,0 +1,1409 @@ +"use strict"; +/* + Copyright (C) 2012-2016 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceGraphicsRenderer(coreExposed, skippingBIOS) { + this.coreExposed = coreExposed; + this.initializeIO(!!skippingBIOS); + this.initializePaletteStorage(); + this.generateRenderers(); + this.initializeRenderers(); +} +function GameBoyAdvanceGraphicsRendererOffthread(skippingBIOS) { + this.initializeIO(!!skippingBIOS); + this.initializePaletteStorage(); + this.generateRenderers(); + this.initializeRenderers(); +} +if (__VIEWS_SUPPORTED__) { + GameBoyAdvanceGraphicsRendererOffthread.prototype.initializeIO = GameBoyAdvanceGraphicsRenderer.prototype.initializeIO = function (skippingBIOS) { + //Initialize Pre-Boot: + this.displayControl = 0x80; + this.display = 0; + this.greenSwap = 0; + this.WINOutside = 0; + this.paletteRAM = getUint8Array(0x400); + this.VRAM = getUint8Array(0x18000); + this.VRAM16 = getUint16View(this.VRAM); + this.VRAM32 = getInt32View(this.VRAM); + this.paletteRAM16 = getUint16View(this.paletteRAM); + this.paletteRAM32 = getInt32View(this.paletteRAM); + //Check for SIMD support: + if (typeof SIMD == "object" && typeof SIMD.Int32x4 == "function") { + //We bounce effects logic through some copies: + this.buffer = getInt32Array(0x900); + } + else { + this.buffer = getInt32Array(0x680); + } + this.lineBuffer = getInt32ViewCustom(this.buffer, 0, 240); + this.frameBuffer = getInt32Array(38400); //The internal buffer to composite to. + this.swizzledFrame = getUint8Array(115200); //The swizzled output buffer that syncs to the internal framebuffer on v-blank. + this.totalLinesPassed = 0; + this.queuedScanLines = 0; + this.lastUnrenderedLine = 0; + if (skippingBIOS) { + //BIOS entered the ROM at line 0x7C: + this.lastUnrenderedLine = 0x7C; + } + this.backdrop = 0x3A00000; + } +} +else { + GameBoyAdvanceGraphicsRendererOffthread.prototype.initializeIO = GameBoyAdvanceGraphicsRenderer.prototype.initializeIO = function (skippingBIOS) { + //Initialize Pre-Boot: + this.displayControl = 0x80; + this.display = 0; + this.greenSwap = 0; + this.WINOutside = 0; + this.paletteRAM = getUint8Array(0x400); + this.VRAM = getUint8Array(0x18000); + this.VRAM16 = getUint16View(this.VRAM); + this.VRAM32 = getInt32View(this.VRAM); + this.paletteRAM16 = getUint16View(this.paletteRAM); + this.paletteRAM32 = getInt32View(this.paletteRAM); + this.buffer = getInt32Array(0x680); + this.frameBuffer = getInt32Array(38400); //The internal buffer to composite to. + this.swizzledFrame = getUint8Array(115200); //The swizzled output buffer that syncs to the internal framebuffer on v-blank. + this.totalLinesPassed = 0; + this.queuedScanLines = 0; + this.lastUnrenderedLine = 0; + if (skippingBIOS) { + //BIOS entered the ROM at line 0x7C: + this.lastUnrenderedLine = 0x7C; + } + this.backdrop = 0x3A00000; + } +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.generateRenderers = GameBoyAdvanceGraphicsRenderer.prototype.generateRenderers = function () { + this.compositor = new GameBoyAdvanceCompositor(this); + this.bg0Renderer = new GameBoyAdvanceBGTEXTRenderer(this, 0); + this.bg1Renderer = new GameBoyAdvanceBGTEXTRenderer(this, 1); + this.bg2TextRenderer = new GameBoyAdvanceBGTEXTRenderer(this, 2); + this.bg3TextRenderer = new GameBoyAdvanceBGTEXTRenderer(this, 3); + this.bgAffineRenderer0 = new GameBoyAdvanceAffineBGRenderer(this, 2); + this.bgAffineRenderer1 = new GameBoyAdvanceAffineBGRenderer(this, 3); + this.bg2MatrixRenderer = new GameBoyAdvanceBGMatrixRenderer(this); + this.bg3MatrixRenderer = new GameBoyAdvanceBGMatrixRenderer(this); + this.bg2FrameBufferRenderer = new GameBoyAdvanceBG2FrameBufferRenderer(this); + this.objRenderer = new GameBoyAdvanceOBJRenderer(this); + this.window0Renderer = new GameBoyAdvanceWindowRenderer(new GameBoyAdvanceWindowCompositor(this)); + this.window1Renderer = new GameBoyAdvanceWindowRenderer(new GameBoyAdvanceWindowCompositor(this)); + this.objWindowRenderer = new GameBoyAdvanceOBJWindowRenderer(new GameBoyAdvanceOBJWindowCompositor(this)); + this.mosaicRenderer = new GameBoyAdvanceMosaicRenderer(this.buffer); + this.colorEffectsRenderer = new GameBoyAdvanceColorEffectsRenderer(this.buffer); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.initializeRenderers = GameBoyAdvanceGraphicsRenderer.prototype.initializeRenderers = function () { + this.compositor.initialize(); + this.compositorPreprocess(); + this.bg0Renderer.initialize(); + this.bg1Renderer.initialize(); + this.bg2TextRenderer.initialize(); + this.bg3TextRenderer.initialize(); + this.bgAffineRenderer0.initialize(); + this.bgAffineRenderer1.initialize(); + this.bg2MatrixRenderer.initialize(); + this.bg3MatrixRenderer.initialize(); + this.bg2FrameBufferRenderer.initialize(); + this.objRenderer.initialize(); + this.window0Renderer.initialize(); + this.window1Renderer.initialize(); + this.objWindowRenderer.initialize(); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.initializePaletteStorage = GameBoyAdvanceGraphicsRenderer.prototype.initializePaletteStorage = function () { + //Both BG and OAM in unified storage: + this.palette256 = getInt32Array(0x100); + this.palette256[0] = 0x3800000; + this.paletteOBJ256 = getInt32Array(0x100); + this.paletteOBJ256[0] = 0x3800000; + this.palette16 = getInt32Array(0x100); + this.paletteOBJ16 = getInt32Array(0x100); + for (var index = 0; (index | 0) < 0x10; index = ((index | 0) + 1) | 0) { + this.palette16[index << 4] = 0x3800000; + this.paletteOBJ16[index << 4] = 0x3800000; + } +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.swizzleFrameBuffer = GameBoyAdvanceGraphicsRenderer.prototype.swizzleFrameBuffer = function () { + //Convert our dirty 15-bit (15-bit, with internal render flags above it) framebuffer to an 8-bit buffer with separate indices for the RGB channels: + var bufferIndex = 0; + for (var canvasIndex = 0; (canvasIndex | 0) < 115200; bufferIndex = ((bufferIndex | 0) + 1) | 0) { + this.swizzledFrame[canvasIndex | 0] = (this.frameBuffer[bufferIndex | 0] & 0x1F) << 3; //Red + canvasIndex = ((canvasIndex | 0) + 1) | 0; + this.swizzledFrame[canvasIndex | 0] = (this.frameBuffer[bufferIndex | 0] & 0x3E0) >> 2; //Green + canvasIndex = ((canvasIndex | 0) + 1) | 0; + this.swizzledFrame[canvasIndex | 0] = (this.frameBuffer[bufferIndex | 0] & 0x7C00) >> 7; //Blue + canvasIndex = ((canvasIndex | 0) + 1) | 0; + } +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.prepareFrame = GameBoyAdvanceGraphicsRenderer.prototype.prepareFrame = function () { + //Copy the internal frame buffer to the output buffer: + this.swizzleFrameBuffer(); + this.requestDraw(); +} +GameBoyAdvanceGraphicsRenderer.prototype.requestDraw = function () { + if (this.coreExposed.graphicsHandle) { + //We actually updated the graphics internally, so copy out: + this.coreExposed.graphicsHandle.copyBuffer(this.swizzledFrame); + } +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.requestDraw = function () { + //We actually updated the graphics internally, so copy out: + copyBuffer(this.swizzledFrame); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.graphicsJIT = function () { + //Not needed for offthread rendering. +} +GameBoyAdvanceGraphicsRenderer.prototype.graphicsJIT = function () { + this.totalLinesPassed = 0; //Mark frame for ensuring a JIT pass for the next framebuffer output. + this.graphicsJITScanlineGroup(); +} +GameBoyAdvanceGraphicsRenderer.prototype.graphicsJITVBlank = function () { + //JIT the graphics to v-blank framing: + this.totalLinesPassed = ((this.totalLinesPassed | 0) + (this.queuedScanLines | 0)) | 0; + this.graphicsJITScanlineGroup(); +} +GameBoyAdvanceGraphicsRenderer.prototype.graphicsJITScanlineGroup = function () { + //Normal rendering JIT, where we try to do groups of scanlines at once: + while ((this.queuedScanLines | 0) > 0) { + this.renderScanLine(); + this.incrementScanLine(); + this.queuedScanLines = ((this.queuedScanLines | 0) - 1) | 0; + } +} +GameBoyAdvanceGraphicsRenderer.prototype.incrementScanLineQueue = function () { + if ((this.queuedScanLines | 0) < 160) { + this.queuedScanLines = ((this.queuedScanLines | 0) + 1) | 0; + } + else { + this.incrementScanLine(); + } +} +GameBoyAdvanceGraphicsRenderer.prototype.ensureFraming = function () { + //Ensure JIT framing alignment: + if ((this.totalLinesPassed | 0) < 160) { + //Make sure our gfx are up-to-date: + this.graphicsJITVBlank(); + //Draw the frame: + this.prepareFrame(); + } +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.renderScanLine = GameBoyAdvanceGraphicsRenderer.prototype.renderScanLine = function () { + var line = this.lastUnrenderedLine | 0; + if ((this.displayControl & 0x80) == 0) { + //Render with the current mode selected: + switch (this.displayControl & 0x7) { + case 0: + //Mode 0: + this.renderMode0(line | 0); + break; + case 1: + //Mode 1: + this.renderMode1(line | 0); + break; + case 2: + //Mode 2: + this.renderMode2(line | 0); + break; + default: + //Modes 3-5: + this.renderModeFrameBuffer(line | 0); + } + //Copy line to our framebuffer: + this.copyLineToFrameBuffer(line | 0); + } + else { + //Forced blank is on, rendering disabled: + this.renderForcedBlank(line | 0); + } + //Update the affine bg counters: + this.updateReferenceCounters(); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.incrementScanLine = GameBoyAdvanceGraphicsRenderer.prototype.incrementScanLine = function () { + if ((this.lastUnrenderedLine | 0) < 159) { + this.lastUnrenderedLine = ((this.lastUnrenderedLine | 0) + 1) | 0; + } + else { + this.lastUnrenderedLine = 0; + } +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.renderMode0 = GameBoyAdvanceGraphicsRenderer.prototype.renderMode0 = function (line) { + line = line | 0; + //Mode 0 Rendering Selected: + var toRender = this.display & 0x1F; + if ((toRender & 0x1) != 0) { + //Render the BG0 layer: + this.bg0Renderer.renderScanLine(line | 0); + } + if ((toRender & 0x2) != 0) { + //Render the BG1 layer: + this.bg1Renderer.renderScanLine(line | 0); + } + if ((toRender & 0x4) != 0) { + //Render the BG2 layer: + this.bg2TextRenderer.renderScanLine(line | 0); + } + if ((toRender & 0x8) != 0) { + //Render the BG3 layer: + this.bg3TextRenderer.renderScanLine(line | 0); + } + if ((toRender & 0x10) != 0) { + //Render the sprite layer: + this.objRenderer.renderScanLine(line | 0); + } + //Composite the non-windowed result: + this.compositeLayers(toRender | 0); + //Composite the windowed result: + this.compositeWindowedLayers(line | 0, toRender | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.renderMode1 = GameBoyAdvanceGraphicsRenderer.prototype.renderMode1 = function (line) { + line = line | 0; + //Mode 1 Rendering Selected: + var toRender = this.display & 0x17; + if ((toRender & 0x1) != 0) { + //Render the BG0 layer: + this.bg0Renderer.renderScanLine(line | 0); + } + if ((toRender & 0x2) != 0) { + //Render the BG1 layer: + this.bg1Renderer.renderScanLine(line | 0); + } + if ((toRender & 0x4) != 0) { + //Render the BG2 layer: + this.bgAffineRenderer0.renderScanLine2M(line | 0); + } + if ((toRender & 0x10) != 0) { + //Render the sprite layer: + this.objRenderer.renderScanLine(line | 0); + } + //Composite the non-windowed result: + this.compositeLayers(toRender | 0); + //Composite the windowed result: + this.compositeWindowedLayers(line | 0, toRender | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.renderMode2 = GameBoyAdvanceGraphicsRenderer.prototype.renderMode2 = function (line) { + line = line | 0; + //Mode 2 Rendering Selected: + var toRender = this.display & 0x1C; + if ((toRender & 0x4) != 0) { + //Render the BG2 layer: + this.bgAffineRenderer0.renderScanLine2M(line | 0); + } + if ((toRender & 0x8) != 0) { + //Render the BG3 layer: + this.bgAffineRenderer1.renderScanLine3M(line | 0); + } + if ((toRender & 0x10) != 0) { + //Render the sprite layer: + this.objRenderer.renderScanLine(line | 0); + } + //Composite the non-windowed result: + this.compositeLayers(toRender | 0); + //Composite the windowed result: + this.compositeWindowedLayers(line | 0, toRender | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.renderModeFrameBuffer = GameBoyAdvanceGraphicsRenderer.prototype.renderModeFrameBuffer = function (line) { + line = line | 0; + //Mode 3/4/5 Rendering Selected: + var toRender = this.display & 0x14; + if ((toRender & 0x4) != 0) { + this.bgAffineRenderer0.renderScanLine2F(line | 0); + } + if ((toRender & 0x10) != 0) { + //Render the sprite layer: + this.objRenderer.renderScanLine(line | 0); + } + //Composite the non-windowed result: + this.compositeLayers(toRender | 0); + //Composite the windowed result: + this.compositeWindowedLayers(line | 0, toRender | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.compositeLayers = GameBoyAdvanceGraphicsRenderer.prototype.compositeLayers = function (toRender) { + toRender = toRender | 0; + if ((this.display & 0xE0) > 0) { + //Window registers can further disable background layers if one or more window layers enabled: + toRender = toRender & this.WINOutside; + } + //Composite the non-windowed result: + this.compositor.renderScanLine(toRender | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.compositeWindowedLayers = GameBoyAdvanceGraphicsRenderer.prototype.compositeWindowedLayers = function (line, toRender) { + line = line | 0; + toRender = toRender | 0; + //Composite the windowed result: + if ((this.display & 0x90) == 0x90) { + //Object Window: + this.objWindowRenderer.renderScanLine(line | 0, toRender | 0); + } + if ((this.display & 0x40) != 0) { + //Window 1: + this.window1Renderer.renderScanLine(line | 0, toRender | 0); + } + if ((this.display & 0x20) != 0) { + //Window 0: + this.window0Renderer.renderScanLine(line | 0, toRender | 0); + } +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceGraphicsRendererOffthread.prototype.copyLineToFrameBuffer = GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBuffer = function (line) { + line = line | 0; + var offsetStart = Math.imul(line | 0, 240) | 0; + if ((this.greenSwap | 0) == 0) { + //Blit normally: + this.copyLineToFrameBufferNormal(offsetStart | 0); + } + else { + //Blit with green swap: + this.copyLineToFrameBufferGreenSwapped(offsetStart | 0); + } + } + if (__LITTLE_ENDIAN__ && typeof Uint8Array.prototype.fill == "function") { + GameBoyAdvanceGraphicsRendererOffthread.prototype.renderForcedBlank = GameBoyAdvanceGraphicsRenderer.prototype.renderForcedBlank = function (line) { + line = line | 0; + var offsetStart = Math.imul(line | 0, 240) | 0; + //Render a blank line: + var offsetEnd = ((offsetStart | 0) + 240) | 0; + this.frameBuffer.fill(0x7FFF, offsetStart | 0, offsetEnd | 0); + } + } + else { + GameBoyAdvanceGraphicsRendererOffthread.prototype.renderForcedBlank = GameBoyAdvanceGraphicsRenderer.prototype.renderForcedBlank = function (line) { + line = line | 0; + var offsetStart = Math.imul(line | 0, 240) | 0; + //Render a blank line: + for (var position = 0; (position | 0) < 240; position = ((position | 0) + 1) | 0) { + this.frameBuffer[offsetStart | 0] = 0x7FFF; + offsetStart = ((offsetStart | 0) + 1) | 0; + } + } + } +} +else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceGraphicsRendererOffthread.prototype.copyLineToFrameBuffer = GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBuffer = function (line) { + var offsetStart = line * 240; + if (this.greenSwap == 0) { + //Blit normally: + this.copyLineToFrameBufferNormal(offsetStart); + } + else { + //Blit with green swap: + this.copyLineToFrameBufferGreenSwapped(offsetStart); + } + } + GameBoyAdvanceGraphicsRendererOffthread.prototype.renderForcedBlank = GameBoyAdvanceGraphicsRenderer.prototype.renderForcedBlank = function (line) { + var offsetStart = line * 240; + //Render a blank line: + for (var position = 0; position < 240; ++position) { + this.frameBuffer[offsetStart++] = 0x7FFF; + } + } +} +if (__VIEWS_SUPPORTED__ && typeof Uint8Array.prototype.set == "function") { + GameBoyAdvanceGraphicsRendererOffthread.prototype.copyLineToFrameBufferNormal = GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBufferNormal = function (offsetStart) { + offsetStart = offsetStart | 0; + //Render a line: + this.frameBuffer.set(this.lineBuffer, offsetStart | 0); + } +} +else { + GameBoyAdvanceGraphicsRendererOffthread.prototype.copyLineToFrameBufferNormal = GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBufferNormal = function (offsetStart) { + offsetStart = offsetStart | 0; + //Render a line: + for (var position = 0; (position | 0) < 240; position = ((position | 0) + 1) | 0) { + this.frameBuffer[offsetStart | 0] = this.buffer[position | 0] | 0; + offsetStart = ((offsetStart | 0) + 1) | 0; + } + } +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.copyLineToFrameBufferGreenSwapped = GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBufferGreenSwapped = function (offsetStart) { + offsetStart = offsetStart | 0; + //Render a line with green swap effect: + var position = 0; + var pixel0 = 0; + var pixel1 = 0; + while ((position | 0) < 240) { + pixel0 = this.buffer[position | 0] | 0; + position = ((position | 0) + 1) | 0; + pixel1 = this.buffer[position | 0] | 0; + position = ((position | 0) + 1) | 0; + this.frameBuffer[offsetStart | 0] = (pixel0 & 0x7C1F) | (pixel1 & 0x3E0); + offsetStart = ((offsetStart | 0) + 1) | 0; + this.frameBuffer[offsetStart | 0] = (pixel1 & 0x7C1F) | (pixel0 & 0x3E0); + offsetStart = ((offsetStart | 0) + 1) | 0; + } +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.updateReferenceCounters = GameBoyAdvanceGraphicsRenderer.prototype.updateReferenceCounters = function () { + if ((this.lastUnrenderedLine | 0) == 159) { + //Reset some affine bg counters on roll-over to line 0: + this.bgAffineRenderer0.resetReferenceCounters(); + this.bgAffineRenderer1.resetReferenceCounters(); + } + else { + //Increment the affine bg counters: + this.bgAffineRenderer0.incrementReferenceCounters(); + this.bgAffineRenderer1.incrementReferenceCounters(); + } +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.compositorPreprocess = GameBoyAdvanceGraphicsRenderer.prototype.compositorPreprocess = function () { + var controlBits = this.WINOutside & 0x20; + if ((this.display & 0xE0) == 0) { + controlBits = controlBits | 1; + } + this.compositor.preprocess(controlBits | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.frameBufferModePreprocess = GameBoyAdvanceGraphicsRenderer.prototype.frameBufferModePreprocess = function (displayControl) { + displayControl = displayControl | 0; + displayControl = Math.min(displayControl & 0x7, 5) | 0; + //Set up pixel fetcher ahead of time: + if ((displayControl | 0) > 2) { + this.bg2FrameBufferRenderer.selectMode(displayControl | 0); + } +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeDISPCNT8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg2FrameBufferRenderer.writeFrameSelect((data & 0x10) << 27); + this.objRenderer.setHBlankIntervalFreeStatus(data & 0x20); + this.frameBufferModePreprocess(data | 0); + this.displayControl = data | 0; +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeDISPCNT8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.display = data & 0xFF; + this.compositorPreprocess(); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeDISPCNT8_2 = GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT8_2 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.greenSwap = data & 0x01; +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeDISPCNT16 = GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg2FrameBufferRenderer.writeFrameSelect((data & 0x10) << 27); + this.objRenderer.setHBlankIntervalFreeStatus(data & 0x20); + this.frameBufferModePreprocess(data | 0); + this.displayControl = data | 0; + this.display = data >> 8; + this.compositorPreprocess(); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeDISPCNT32 = GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg2FrameBufferRenderer.writeFrameSelect((data & 0x10) << 27); + this.objRenderer.setHBlankIntervalFreeStatus(data & 0x20); + this.frameBufferModePreprocess(data | 0); + this.displayControl = data | 0; + this.display = (data >> 8) & 0xFF; + this.compositorPreprocess(); + this.greenSwap = data & 0x10000; +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0CNT8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0CNT8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + //Bits 5-6 always 0. + this.bg0Renderer.writeBGCNT8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0CNT8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0CNT8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg0Renderer.writeBGCNT8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0CNT16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0CNT16 = function (data) { + data = data | 0; + this.graphicsJIT(); + //Bits 5-6 always 0. + this.bg0Renderer.writeBGCNT16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1CNT8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1CNT8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + //Bits 5-6 always 0. + this.bg1Renderer.writeBGCNT8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1CNT8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1CNT8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg1Renderer.writeBGCNT8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1CNT16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1CNT16 = function (data) { + data = data | 0; + this.graphicsJIT(); + //Bits 5-6 always 0. + this.bg1Renderer.writeBGCNT16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0BG1CNT32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0BG1CNT32 = function (data) { + data = data | 0; + this.graphicsJIT(); + //Bits 5-6 always 0. + this.bg0Renderer.writeBGCNT16(data | 0); + //Bits 5-6 always 0. + this.bg1Renderer.writeBGCNT16(data >> 16); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2CNT8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2CNT8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + //Bits 5-6 always 0. + this.bg2TextRenderer.writeBGCNT8_0(data | 0); + this.bgAffineRenderer0.setMosaicEnable(data & 0x40); + this.bgAffineRenderer0.priorityPreprocess(data & 0x3); + this.bg2MatrixRenderer.characterBaseBlockPreprocess((data & 0xC) >> 2); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2CNT8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2CNT8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg2TextRenderer.writeBGCNT8_1(data | 0); + this.bg2MatrixRenderer.screenSizePreprocess((data & 0xC0) >> 6); + this.bg2MatrixRenderer.screenBaseBlockPreprocess(data & 0x1F); + this.bg2MatrixRenderer.displayOverflowPreprocess(data & 0x20); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2CNT16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2CNT16 = function (data) { + data = data | 0; + this.graphicsJIT(); + //Bits 5-6 always 0. + this.bg2TextRenderer.writeBGCNT16(data | 0); + this.bgAffineRenderer0.setMosaicEnable(data & 0x40); + this.bgAffineRenderer0.priorityPreprocess(data & 0x3); + this.bg2MatrixRenderer.characterBaseBlockPreprocess((data & 0xC) >> 2); + this.bg2MatrixRenderer.screenSizePreprocess((data & 0xC000) >> 14); + data = data >> 8; + this.bg2MatrixRenderer.screenBaseBlockPreprocess(data & 0x1F); + this.bg2MatrixRenderer.displayOverflowPreprocess(data & 0x20); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3CNT8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3CNT8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + //Bits 5-6 always 0. + this.bg3TextRenderer.writeBGCNT8_0(data | 0); + this.bgAffineRenderer1.setMosaicEnable(data & 0x40); + this.bgAffineRenderer1.priorityPreprocess(data & 0x3); + this.bg3MatrixRenderer.characterBaseBlockPreprocess((data & 0xC) >> 2); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3CNT8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3CNT8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg3TextRenderer.writeBGCNT8_1(data | 0); + this.bg3MatrixRenderer.screenSizePreprocess((data & 0xC0) >> 6); + this.bg3MatrixRenderer.screenBaseBlockPreprocess(data & 0x1F); + this.bg3MatrixRenderer.displayOverflowPreprocess(data & 0x20); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3CNT16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3CNT16 = function (data) { + data = data | 0; + this.graphicsJIT(); + //Bits 5-6 always 0. + this.bg3TextRenderer.writeBGCNT16(data | 0); + this.bgAffineRenderer1.setMosaicEnable(data & 0x40); + this.bgAffineRenderer1.priorityPreprocess(data & 0x3); + this.bg3MatrixRenderer.characterBaseBlockPreprocess((data & 0xC) >> 2); + this.bg3MatrixRenderer.screenSizePreprocess((data & 0xC000) >> 14); + data = data >> 8; + this.bg3MatrixRenderer.screenBaseBlockPreprocess(data & 0x1F); + this.bg3MatrixRenderer.displayOverflowPreprocess(data & 0x20); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2BG3CNT32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2BG3CNT32 = function (data) { + data = data | 0; + this.graphicsJIT(); + //Bits 5-6 always 0. + this.bg2TextRenderer.writeBGCNT16(data | 0); + this.bgAffineRenderer0.setMosaicEnable(data & 0x40); + this.bgAffineRenderer0.priorityPreprocess(data & 0x3); + this.bg2MatrixRenderer.characterBaseBlockPreprocess((data & 0xC) >> 2); + this.bg2MatrixRenderer.screenSizePreprocess((data & 0xC000) >> 14); + this.bg2MatrixRenderer.screenBaseBlockPreprocess((data >> 8) & 0x1F); + this.bg2MatrixRenderer.displayOverflowPreprocess((data >> 8) & 0x20); + //Bits 5-6 always 0. + data = data >> 16; + this.bg3TextRenderer.writeBGCNT16(data | 0); + this.bgAffineRenderer1.setMosaicEnable(data & 0x40); + this.bgAffineRenderer1.priorityPreprocess(data & 0x3); + this.bg3MatrixRenderer.characterBaseBlockPreprocess((data & 0xC) >> 2); + this.bg3MatrixRenderer.screenSizePreprocess((data & 0xC000) >> 14); + data = data >> 8; + this.bg3MatrixRenderer.screenBaseBlockPreprocess(data & 0x1F); + this.bg3MatrixRenderer.displayOverflowPreprocess(data & 0x20); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0HOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0HOFS8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg0Renderer.writeBGHOFS8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0HOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0HOFS8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg0Renderer.writeBGHOFS8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0HOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0HOFS16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg0Renderer.writeBGHOFS16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0VOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0VOFS8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg0Renderer.writeBGVOFS8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0VOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0VOFS8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg0Renderer.writeBGVOFS8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0VOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0VOFS16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg0Renderer.writeBGVOFS16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0OFS32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0OFS32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg0Renderer.writeBGOFS32(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1HOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1HOFS8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg1Renderer.writeBGHOFS8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1HOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1HOFS8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg1Renderer.writeBGHOFS8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1HOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1HOFS16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg1Renderer.writeBGHOFS16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1VOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1VOFS8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg1Renderer.writeBGVOFS8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1VOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1VOFS8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg1Renderer.writeBGVOFS8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1VOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1VOFS16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg1Renderer.writeBGVOFS16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1OFS32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1OFS32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg1Renderer.writeBGOFS32(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2HOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2HOFS8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg2TextRenderer.writeBGHOFS8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2HOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2HOFS8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg2TextRenderer.writeBGHOFS8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2HOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2HOFS16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg2TextRenderer.writeBGHOFS16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2VOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2VOFS8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg2TextRenderer.writeBGVOFS8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2VOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2VOFS8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg2TextRenderer.writeBGVOFS8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2VOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2VOFS16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg2TextRenderer.writeBGVOFS16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2OFS32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2OFS32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg2TextRenderer.writeBGOFS32(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3HOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3HOFS8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg3TextRenderer.writeBGHOFS8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3HOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3HOFS8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg3TextRenderer.writeBGHOFS8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3HOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3HOFS16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg3TextRenderer.writeBGHOFS16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3VOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3VOFS8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg3TextRenderer.writeBGVOFS8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3VOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3VOFS8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg3TextRenderer.writeBGVOFS8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3VOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3VOFS16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg3TextRenderer.writeBGVOFS16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3OFS32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3OFS32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bg3TextRenderer.writeBGOFS32(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PA8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PA8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGPA8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PA8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PA8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGPA8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PA16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PA16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGPA16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PB8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PB8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGPB8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PB8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PB8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGPB8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PB16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PB16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGPB16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PAB32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PAB32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGPAB32(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PC8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PC8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGPC8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PC8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PC8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGPC8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PC16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PC16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGPC16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PD8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PD8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGPD8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PD8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PD8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGPD8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PD16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PD16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGPD16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PCD32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PCD32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGPCD32(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PA8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PA8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGPA8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PA8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PA8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGPA8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PA16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PA16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGPA16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PB8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PB8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGPB8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PB8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PB8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGPB8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PB16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PB16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGPB16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PAB32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PAB32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGPAB32(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PC8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PC8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGPC8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PC8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PC8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGPC8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PC16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PC16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGPC16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PD8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PD8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGPD8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PD8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PD8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGPD8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PD16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PD16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGPD16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PCD32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PCD32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGPCD32(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2X8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGX8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2X8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGX8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2X8_2 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X8_2 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGX8_2(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2X8_3 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X8_3 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGX8_3(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2X16_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X16_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGX16_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2X16_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X16_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGX16_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2X32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGX32(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2Y8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGY8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2Y8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGY8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2Y8_2 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y8_2 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGY8_2(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2Y8_3 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y8_3 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGY8_3(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2Y16_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y16_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGY16_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2Y16_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y16_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGY16_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2Y32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer0.writeBGY32(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3X8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGX8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3X8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGX8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3X8_2 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X8_2 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGX8_2(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3X8_3 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X8_3 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGX8_3(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3X16_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X16_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGX16_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3X16_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X16_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGX16_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3X32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGX32(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3Y8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGY8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3Y8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGY8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3Y8_2 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y8_2 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGY8_2(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3Y8_3 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y8_3 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGY8_3(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3Y16_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y16_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGY16_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3Y16_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y16_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGY16_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3Y32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.bgAffineRenderer1.writeBGY32(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN0XCOORDRight8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0XCOORDRight8 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.window0Renderer.writeWINXCOORDRight8(data | 0); //Window x-coord goes up to this minus 1. +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN0XCOORDLeft8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0XCOORDLeft8 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.window0Renderer.writeWINXCOORDLeft8(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN0XCOORD16 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0XCOORD16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.window0Renderer.writeWINXCOORD16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN1XCOORDRight8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1XCOORDRight8 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.window1Renderer.writeWINXCOORDRight8(data | 0); //Window x-coord goes up to this minus 1. +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN1XCOORDLeft8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1XCOORDLeft8 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.window1Renderer.writeWINXCOORDLeft8(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN1XCOORD16 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1XCOORD16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.window1Renderer.writeWINXCOORD16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWINXCOORD32 = GameBoyAdvanceGraphicsRenderer.prototype.writeWINXCOORD32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.window0Renderer.writeWINXCOORD16(data & 0xFFFF); + this.window1Renderer.writeWINXCOORD16(data >>> 16); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN0YCOORDBottom8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0YCOORDBottom8 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.window0Renderer.writeWINYCOORDBottom8(data | 0); //Window y-coord goes up to this minus 1. +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN0YCOORDTop8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0YCOORDTop8 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.window0Renderer.writeWINYCOORDTop8(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN0YCOORD16 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0YCOORD16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.window0Renderer.writeWINYCOORD16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN1YCOORDBottom8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1YCOORDBottom8 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.window1Renderer.writeWINYCOORDBottom8(data | 0); //Window y-coord goes up to this minus 1. +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN1YCOORDTop8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1YCOORDTop8 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.window1Renderer.writeWINYCOORDTop8(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN1YCOORD16 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1YCOORD16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.window1Renderer.writeWINYCOORD16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWINYCOORD32 = GameBoyAdvanceGraphicsRenderer.prototype.writeWINYCOORD32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.window0Renderer.writeWINYCOORD16(data & 0xFFFF); + this.window1Renderer.writeWINYCOORD16(data >>> 16); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN0IN8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0IN8 = function (data) { + data = data | 0; + //Window 0: + this.graphicsJIT(); + this.window0Renderer.writeWININ8(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN1IN8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1IN8 = function (data) { + data = data | 0; + //Window 1: + this.graphicsJIT(); + this.window1Renderer.writeWININ8(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWININ16 = GameBoyAdvanceGraphicsRenderer.prototype.writeWININ16 = function (data) { + data = data | 0; + //Window 0: + this.graphicsJIT(); + this.window0Renderer.writeWININ8(data & 0xFF); + //Window 1: + this.window1Renderer.writeWININ8(data >> 8); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWINOUT8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWINOUT8 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.WINOutside = data | 0; + this.compositorPreprocess(); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWINOBJIN8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWINOBJIN8 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.objWindowRenderer.writeWINOBJIN8(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWINOUT16 = GameBoyAdvanceGraphicsRenderer.prototype.writeWINOUT16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.WINOutside = data | 0; + this.compositorPreprocess(); + this.objWindowRenderer.writeWINOBJIN8(data >> 8); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWINCONTROL32 = GameBoyAdvanceGraphicsRenderer.prototype.writeWINCONTROL32 = function (data) { + data = data | 0; + //Window 0: + this.graphicsJIT(); + this.window0Renderer.writeWININ8(data & 0xFF); + //Window 1: + this.window1Renderer.writeWININ8((data >> 8) & 0xFF); + this.WINOutside = data >> 16; + this.compositorPreprocess(); + this.objWindowRenderer.writeWINOBJIN8(data >>> 24); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeMOSAIC8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeMOSAIC8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.mosaicRenderer.writeMOSAIC8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeMOSAIC8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeMOSAIC8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.mosaicRenderer.writeMOSAIC8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeMOSAIC16 = GameBoyAdvanceGraphicsRenderer.prototype.writeMOSAIC16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.mosaicRenderer.writeMOSAIC16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDCNT8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDCNT8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.colorEffectsRenderer.writeBLDCNT8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDCNT8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDCNT8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.colorEffectsRenderer.writeBLDCNT8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDCNT16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDCNT16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.colorEffectsRenderer.writeBLDCNT16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDALPHA8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDALPHA8_0 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.colorEffectsRenderer.writeBLDALPHA8_0(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDALPHA8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDALPHA8_1 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.colorEffectsRenderer.writeBLDALPHA8_1(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDALPHA16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDALPHA16 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.colorEffectsRenderer.writeBLDALPHA16(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDCNT32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDCNT32 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.colorEffectsRenderer.writeBLDCNT32(data | 0); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDY8 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDY8 = function (data) { + data = data | 0; + this.graphicsJIT(); + this.colorEffectsRenderer.writeBLDY8(data | 0); +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceGraphicsRendererOffthread.prototype.writeVRAM8 = GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM8 = + GameBoyAdvanceGraphicsRendererOffthread.prototype.writeVRAM16 = GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM16 = function (address, data) { + address = address | 0; + data = data | 0; + this.graphicsJIT(); + this.VRAM16[address & 0xFFFF] = data & 0xFFFF; + } + GameBoyAdvanceGraphicsRendererOffthread.prototype.writeVRAM32 = GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM32 = function (address, data) { + address = address | 0; + data = data | 0; + this.graphicsJIT(); + this.VRAM32[address & 0x7FFF] = data | 0; + } + GameBoyAdvanceGraphicsRenderer.prototype.readVRAM16 = function (address) { + address = address | 0; + return this.VRAM16[address & 0xFFFF] | 0; + } + GameBoyAdvanceGraphicsRenderer.prototype.readVRAM32 = function (address) { + address = address | 0; + return this.VRAM32[address & 0x7FFF] | 0; + } + GameBoyAdvanceGraphicsRendererOffthread.prototype.writePalette16 = GameBoyAdvanceGraphicsRenderer.prototype.writePalette16 = function (address, data) { + data = data | 0; + address = address | 0; + this.graphicsJIT(); + this.paletteRAM16[address & 0x1FF] = data & 0xFFFF; + data = data & 0x7FFF; + this.writePalette256Color(address | 0, data | 0); + this.writePalette16Color(address | 0, data | 0); + } + GameBoyAdvanceGraphicsRendererOffthread.prototype.writePalette32 = GameBoyAdvanceGraphicsRenderer.prototype.writePalette32 = function (address, data) { + data = data | 0; + address = address | 0; + this.graphicsJIT(); + this.paletteRAM32[address & 0xFF] = data | 0; + address = address << 1; + var palette = data & 0x7FFF; + this.writePalette256Color(address | 0, palette | 0); + this.writePalette16Color(address | 0, palette | 0); + palette = (data >> 16) & 0x7FFF; + this.writePalette256Color(address | 1, palette | 0); + this.writePalette16Color(address | 1, palette | 0); + } + GameBoyAdvanceGraphicsRenderer.prototype.readPalette16 = function (address) { + address = address | 0; + return this.paletteRAM16[address & 0x1FF] | 0; + } + GameBoyAdvanceGraphicsRenderer.prototype.readPalette32 = function (address) { + address = address | 0; + return this.paletteRAM32[address & 0xFF] | 0; + } +} +else { + GameBoyAdvanceGraphicsRendererOffthread.prototype.writeVRAM8 = GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM8 = + GameBoyAdvanceGraphicsRendererOffthread.prototype.writeVRAM16 = GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM16 = function (address, data) { + address <<= 1; + address &= 0x1FFFE; + this.graphicsJIT(); + this.VRAM[address++] = data & 0xFF; + this.VRAM[address] = (data >> 8) & 0xFF; + } + GameBoyAdvanceGraphicsRendererOffthread.prototype.writeVRAM32 = GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM32 = function (address, data) { + address <<= 2; + address &= 0x1FFFC; + this.graphicsJIT(); + this.VRAM[address++] = data & 0xFF; + this.VRAM[address++] = (data >> 8) & 0xFF; + this.VRAM[address++] = (data >> 16) & 0xFF; + this.VRAM[address] = data >>> 24; + } + GameBoyAdvanceGraphicsRenderer.prototype.readVRAM16 = function (address) { + address <<= 1; + address &= 0x1FFFE; + return this.VRAM[address] | (this.VRAM[address + 1] << 8); + } + GameBoyAdvanceGraphicsRenderer.prototype.readVRAM32 = function (address) { + address <<= 2; + address &= 0x1FFFC; + return this.VRAM[address] | (this.VRAM[address + 1] << 8) | (this.VRAM[address + 2] << 16) | (this.VRAM[address + 3] << 24); + } + GameBoyAdvanceGraphicsRendererOffthread.prototype.writePalette16 = GameBoyAdvanceGraphicsRenderer.prototype.writePalette16 = function (address, data) { + this.graphicsJIT(); + address <<= 1; + this.paletteRAM[address] = data & 0xFF; + this.paletteRAM[address | 1] = data >> 8; + data &= 0x7FFF; + address >>= 1; + this.writePalette256Color(address, data); + this.writePalette16Color(address, data); + } + GameBoyAdvanceGraphicsRendererOffthread.prototype.writePalette32 = GameBoyAdvanceGraphicsRenderer.prototype.writePalette32 = function (address, data) { + this.graphicsJIT(); + address <<= 2; + this.paletteRAM[address] = data & 0xFF; + this.paletteRAM[address | 1] = (data >> 8) & 0xFF; + this.paletteRAM[address | 2] = (data >> 16) & 0xFF; + this.paletteRAM[address | 3] = data >>> 24; + address >>= 1; + var palette = data & 0x7FFF; + this.writePalette256Color(address, palette); + this.writePalette16Color(address, palette); + palette = (data >> 16) & 0x7FFF; + address |= 1; + this.writePalette256Color(address, palette); + this.writePalette16Color(address, palette); + } + GameBoyAdvanceGraphicsRenderer.prototype.readPalette16 = function (address) { + address <<= 1; + address &= 0x3FE; + return this.paletteRAM[address] | (this.paletteRAM[address | 1] << 8); + } + GameBoyAdvanceGraphicsRenderer.prototype.readPalette32 = function (address) { + address <<= 2; + address &= 0x3FC; + return this.paletteRAM[address] | (this.paletteRAM[address | 1] << 8) | (this.paletteRAM[address | 2] << 16) | (this.paletteRAM[address | 3] << 24); + } +} +GameBoyAdvanceGraphicsRenderer.prototype.readVRAM8 = function (address) { + address = address | 0; + return this.VRAM[address & 0x1FFFF] | 0; +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeOAM16 = GameBoyAdvanceGraphicsRenderer.prototype.writeOAM16 = function (address, data) { + address = address | 0; + data = data | 0; + this.graphicsJIT(); + this.objRenderer.writeOAM16(address & 0x1FF, data & 0xFFFF); +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeOAM32 = GameBoyAdvanceGraphicsRenderer.prototype.writeOAM32 = function (address, data) { + address = address | 0; + data = data | 0; + this.graphicsJIT(); + this.objRenderer.writeOAM32(address & 0xFF, data | 0); +} +GameBoyAdvanceGraphicsRenderer.prototype.readOAM = function (address) { + address = address | 0; + return this.objRenderer.readOAM(address | 0) | 0; +} +GameBoyAdvanceGraphicsRenderer.prototype.readOAM16 = function (address) { + address = address | 0; + return this.objRenderer.readOAM16(address | 0) | 0; +} +GameBoyAdvanceGraphicsRenderer.prototype.readOAM32 = function (address) { + address = address | 0; + return this.objRenderer.readOAM32(address | 0) | 0; +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writePalette256Color = GameBoyAdvanceGraphicsRenderer.prototype.writePalette256Color = function (address, palette) { + address = address | 0; + palette = palette | 0; + if ((address & 0xFF) == 0) { + palette = 0x3800000 | palette; + if (address == 0) { + this.backdrop = palette | 0x200000; + } + } + if ((address | 0) < 0x100) { + this.palette256[address & 0xFF] = palette | 0; + } + else { + this.paletteOBJ256[address & 0xFF] = palette | 0; + } +} +GameBoyAdvanceGraphicsRendererOffthread.prototype.writePalette16Color = GameBoyAdvanceGraphicsRenderer.prototype.writePalette16Color = function (address, palette) { + address = address | 0; + palette = palette | 0; + if ((address & 0xF) == 0) { + palette = 0x3800000 | palette; + } + if ((address | 0) < 0x100) { + //BG Layer Palette: + this.palette16[address & 0xFF] = palette | 0; + } + else { + //OBJ Layer Palette: + this.paletteOBJ16[address & 0xFF] = palette | 0; + } +} +GameBoyAdvanceGraphicsRenderer.prototype.readPalette8 = function (address) { + address = address | 0; + return this.paletteRAM[address & 0x3FF] | 0; +} diff --git a/public/gfiles/gba/IodineGBA/core/graphics/RendererProxy.js b/public/gfiles/gba/IodineGBA/core/graphics/RendererProxy.js new file mode 100644 index 00000000..e8e3b21e --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/graphics/RendererProxy.js @@ -0,0 +1,1253 @@ +"use strict"; +/* + Copyright (C) 2012-2016 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceRendererProxy(IOCore) { + //Build references: + this.IOCore = IOCore; +} +GameBoyAdvanceRendererProxy.prototype.initialize = function () { + this.IOData8 = getUint8Array(20); + this.IOData16 = getUint16View(this.IOData8); + this.IOData32 = getInt32View(this.IOData8); + this.gfxState = this.IOCore.gfxState; + this.renderer = getGameBoyAdvanceGraphicsRenderer(this.IOCore.coreExposed, !!this.IOCore.SKIPBoot); +} +GameBoyAdvanceRendererProxy.prototype.incrementScanLineQueue = function () { + this.renderer.incrementScanLineQueue(); +} +GameBoyAdvanceRendererProxy.prototype.ensureFraming = function () { + this.renderer.ensureFraming(); +} +GameBoyAdvanceRendererProxy.prototype.writeDISPCNT8_0 = function (data) { + data = data | 0; + data = data & 0xF7; + this.IOCore.updateGraphicsClocking(); + this.IOData8[0] = data | 0; + this.renderer.writeDISPCNT8_0(data | 0); + this.gfxState.isRenderingCheckPreprocess(); +} +GameBoyAdvanceRendererProxy.prototype.readDISPCNT8_0 = function () { + return this.IOData8[0] | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeDISPCNT8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[1] = data | 0; + this.renderer.writeDISPCNT8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readDISPCNT8_1 = function () { + return this.IOData8[1] | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeDISPCNT8_2 = function (data) { + data = data | 0; + data = data & 0x1; + this.IOCore.updateGraphicsClocking(); + this.IOData8[2] = data | 0; + this.renderer.writeDISPCNT8_2(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readDISPCNT8_2 = function () { + return this.IOData8[2] | 0; +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceRendererProxy.prototype.writeDISPCNT16 = function (data) { + data = data | 0; + data = data & 0xFFF7; + this.IOCore.updateGraphicsClocking(); + this.IOData16[0] = data | 0; + this.renderer.writeDISPCNT16(data | 0); + this.gfxState.isRenderingCheckPreprocess(); + } + GameBoyAdvanceRendererProxy.prototype.readDISPCNT16 = function () { + return this.IOData16[0] | 0; + } + GameBoyAdvanceRendererProxy.prototype.writeDISPCNT32 = function (data) { + data = data | 0; + data = data & 0x1FFF7; + this.IOCore.updateGraphicsClocking(); + this.IOData32[0] = data | 0; + this.renderer.writeDISPCNT32(data | 0); + this.gfxState.isRenderingCheckPreprocess(); + } + GameBoyAdvanceRendererProxy.prototype.readDISPCNT32 = function () { + return this.IOData32[0] | 0; + } +} +else { + GameBoyAdvanceRendererProxy.prototype.writeDISPCNT16 = function (data) { + data = data | 0; + data = data & 0xFFF7; + this.IOCore.updateGraphicsClocking(); + this.IOData8[0] = data & 0xF7; + this.IOData8[1] = data >> 8; + this.renderer.writeDISPCNT16(data | 0); + this.gfxState.isRenderingCheckPreprocess(); + } + GameBoyAdvanceRendererProxy.prototype.readDISPCNT16 = function () { + return this.IOData8[0] | (this.IOData8[1] << 8); + } + GameBoyAdvanceRendererProxy.prototype.writeDISPCNT32 = function (data) { + data = data | 0; + data = data & 0x1FFF7; + this.IOCore.updateGraphicsClocking(); + this.IOData8[0] = data & 0xF7; + this.IOData8[1] = (data >> 8) & 0xFF; + this.IOData8[2] = data >> 16; + this.renderer.writeDISPCNT32(data | 0); + this.gfxState.isRenderingCheckPreprocess(); + } + GameBoyAdvanceRendererProxy.prototype.readDISPCNT32 = function () { + return this.IOData8[0] | (this.IOData8[1] << 8) | (this.IOData8[2] << 16); + } +} +GameBoyAdvanceRendererProxy.prototype.writeBG0CNT8_0 = function (data) { + data = data | 0; + data = data & 0xCF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[4] = data | 0; + this.renderer.writeBG0CNT8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readBG0CNT8_0 = function () { + return this.IOData8[4] | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeBG0CNT8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[5] = data | 0; + this.renderer.writeBG0CNT8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readBG0CNT8_1 = function () { + return this.IOData8[5] | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeBG1CNT8_0 = function (data) { + data = data | 0; + data = data & 0xCF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[6] = data | 0; + this.renderer.writeBG1CNT8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readBG1CNT8_0 = function () { + return this.IOData8[6] | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeBG1CNT8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[7] = data | 0; + this.renderer.writeBG1CNT8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readBG1CNT8_1 = function () { + return this.IOData8[7] | 0; +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceRendererProxy.prototype.writeBG0CNT16 = function (data) { + data = data | 0; + data = data & 0xFFCF; + this.IOCore.updateGraphicsClocking(); + this.IOData16[2] = data | 0; + this.renderer.writeBG0CNT16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeBG1CNT16 = function (data) { + data = data | 0; + data = data & 0xFFCF; + this.IOCore.updateGraphicsClocking(); + this.IOData16[3] = data | 0; + this.renderer.writeBG1CNT16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeBG0BG1CNT32 = function (data) { + data = data | 0; + data = data & 0xFFCFFFCF; + this.IOCore.updateGraphicsClocking(); + this.IOData32[1] = data | 0; + this.renderer.writeBG0BG1CNT32(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.readBG0CNT16 = function () { + return this.IOData16[2] | 0; + } + GameBoyAdvanceRendererProxy.prototype.readBG1CNT16 = function () { + return this.IOData16[3] | 0; + } + GameBoyAdvanceRendererProxy.prototype.readBG0BG1CNT32 = function () { + return this.IOData32[1] | 0; + } +} +else { + GameBoyAdvanceRendererProxy.prototype.writeBG0CNT16 = function (data) { + data = data | 0; + data = data & 0xFFCF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[4] = data & 0xFF; + this.IOData8[5] = data >> 8; + this.renderer.writeBG0CNT16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeBG1CNT16 = function (data) { + data = data | 0; + data = data & 0xFFCF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[6] = data & 0xFF; + this.IOData8[7] = data >> 8; + this.renderer.writeBG1CNT16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeBG0BG1CNT32 = function (data) { + data = data | 0; + data = data & 0xFFCFFFCF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[4] = data & 0xFF; + this.IOData8[5] = (data >> 8) & 0xFF; + this.IOData8[6] = (data >> 16) & 0xFF; + this.IOData8[7] = data >>> 24; + this.renderer.writeBG0BG1CNT32(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.readBG0CNT16 = function () { + return this.IOData8[4] | (this.IOData8[5] << 8); + } + GameBoyAdvanceRendererProxy.prototype.readBG1CNT16 = function () { + return this.IOData8[6] | (this.IOData8[7] << 8); + } + GameBoyAdvanceRendererProxy.prototype.readBG0BG1CNT32 = function () { + return this.IOData8[4] | (this.IOData8[5] << 8) | (this.IOData8[6] << 16) | (this.IOData8[7] << 24); + } +} +GameBoyAdvanceRendererProxy.prototype.writeBG2CNT8_0 = function (data) { + data = data | 0; + data = data & 0xCF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[8] = data | 0; + this.renderer.writeBG2CNT8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readBG2CNT8_0 = function () { + return this.IOData8[8] | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeBG2CNT8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[9] = data | 0; + this.renderer.writeBG2CNT8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readBG2CNT8_1 = function () { + return this.IOData8[9] | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeBG3CNT8_0 = function (data) { + data = data | 0; + data = data & 0xCF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[10] = data | 0; + this.renderer.writeBG3CNT8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readBG3CNT8_0 = function () { + return this.IOData8[10] | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeBG3CNT8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[11] = data | 0; + this.renderer.writeBG3CNT8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readBG3CNT8_1 = function () { + return this.IOData8[11] | 0; +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceRendererProxy.prototype.writeBG2CNT16 = function (data) { + data = data | 0; + data = data & 0xFFCF; + this.IOCore.updateGraphicsClocking(); + this.IOData16[4] = data | 0; + this.renderer.writeBG2CNT16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeBG3CNT16 = function (data) { + data = data | 0; + data = data & 0xFFCF; + this.IOCore.updateGraphicsClocking(); + this.IOData16[5] = data | 0; + this.renderer.writeBG3CNT16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeBG2BG3CNT32 = function (data) { + data = data | 0; + data = data & 0xFFCFFFCF; + this.IOCore.updateGraphicsClocking(); + this.IOData32[2] = data | 0; + this.renderer.writeBG2BG3CNT32(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.readBG2CNT16 = function () { + return this.IOData16[4] | 0; + } + GameBoyAdvanceRendererProxy.prototype.readBG3CNT16 = function () { + return this.IOData16[5] | 0; + } + GameBoyAdvanceRendererProxy.prototype.readBG2BG3CNT32 = function () { + return this.IOData32[2] | 0; + } +} +else { + GameBoyAdvanceRendererProxy.prototype.writeBG2CNT16 = function (data) { + data = data | 0; + data = data & 0xFFCF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[8] = data & 0xFF; + this.IOData8[9] = data >> 8; + this.renderer.writeBG2CNT16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeBG3CNT16 = function (data) { + data = data | 0; + data = data & 0xFFCF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[10] = data & 0xFF; + this.IOData8[11] = data >> 8; + this.renderer.writeBG3CNT16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeBG2BG3CNT32 = function (data) { + data = data | 0; + data = data & 0xFFCFFFCF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[8] = data & 0xFF; + this.IOData8[9] = (data >> 8) & 0xFF; + this.IOData8[10] = (data >> 16) & 0xFF; + this.IOData8[11] = data >>> 24; + this.renderer.writeBG2BG3CNT32(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.readBG2CNT16 = function () { + return this.IOData8[8] | (this.IOData8[9] << 8); + } + GameBoyAdvanceRendererProxy.prototype.readBG3CNT16 = function () { + return this.IOData8[10] | (this.IOData8[11] << 8); + } + GameBoyAdvanceRendererProxy.prototype.readBG2BG3CNT32 = function () { + return this.IOData8[8] | (this.IOData8[9] << 8) | (this.IOData8[10] << 16) | (this.IOData8[11] << 24); + } +} +GameBoyAdvanceRendererProxy.prototype.writeBG0HOFS8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG0HOFS8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG0HOFS8_1 = function (data) { + data = data | 0; + data = data & 0x1; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG0HOFS8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG0HOFS16 = function (data) { + data = data | 0; + data = data & 0x1FF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG0HOFS16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG0VOFS8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG0VOFS8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG0VOFS8_1 = function (data) { + data = data | 0; + data = data & 0x1; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG0VOFS8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG0VOFS16 = function (data) { + data = data | 0; + data = data & 0x1FF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG0VOFS16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG0OFS32 = function (data) { + data = data | 0; + data = data & 0x1FF01FF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG0OFS32(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG1HOFS8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG1HOFS8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG1HOFS8_1 = function (data) { + data = data | 0; + data = data & 0x1; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG1HOFS8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG1HOFS16 = function (data) { + data = data | 0; + data = data & 0x1FF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG1HOFS16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG1VOFS8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG1VOFS8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG1VOFS8_1 = function (data) { + data = data | 0; + data = data & 0x1; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG1VOFS8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG1VOFS16 = function (data) { + data = data | 0; + data = data & 0x1FF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG1VOFS16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG1OFS32 = function (data) { + data = data | 0; + data = data & 0x1FF01FF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG1OFS32(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2HOFS8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2HOFS8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2HOFS8_1 = function (data) { + data = data | 0; + data = data & 0x1; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2HOFS8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2HOFS16 = function (data) { + data = data | 0; + data = data & 0x1FF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2HOFS16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2VOFS8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2VOFS8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2VOFS8_1 = function (data) { + data = data | 0; + data = data & 0x1; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2VOFS8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2VOFS16 = function (data) { + data = data | 0; + data = data & 0x1FF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2VOFS16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2OFS32 = function (data) { + data = data | 0; + data = data & 0x1FF01FF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2OFS32(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3HOFS8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3HOFS8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3HOFS8_1 = function (data) { + data = data | 0; + data = data & 0x1; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3HOFS8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3HOFS16 = function (data) { + data = data | 0; + data = data & 0x1FF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3HOFS16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3VOFS8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3VOFS8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3VOFS8_1 = function (data) { + data = data | 0; + data = data & 0x1; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3VOFS8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3VOFS16 = function (data) { + data = data | 0; + data = data & 0x1FF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3VOFS16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3OFS32 = function (data) { + data = data | 0; + data = data & 0x1FF01FF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3OFS32(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2PA8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2PA8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2PA8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2PA8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2PA16 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2PA16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2PB8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2PB8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2PB8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2PB8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2PB16 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2PB16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2PAB32 = function (data) { + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2PAB32(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2PC8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2PC8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2PC8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2PC8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2PC16 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2PC16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2PD8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2PD8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2PD8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2PD8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2PD16 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2PD16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2PCD32 = function (data) { + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2PCD32(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3PA8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3PA8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3PA8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3PA8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3PA16 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3PA16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3PB8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3PB8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3PB8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3PB8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3PB16 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3PB16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3PAB32 = function (data) { + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3PAB32(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3PC8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3PC8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3PC8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3PC8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3PC16 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3PC16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3PD8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3PD8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3PD8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3PD8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3PD16 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3PD16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3PCD32 = function (data) { + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3PCD32(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2X8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2X8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2X8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2X8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2X8_2 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2X8_2(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2X8_3 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2X8_3(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2X16_0 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2X16_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2X16_1 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2X16_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2X32 = function (data) { + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2X32(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2Y8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2Y8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2Y8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2Y8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2Y8_2 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2Y8_2(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2Y8_3 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2Y8_3(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2Y16_0 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2Y16_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2Y16_1 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2Y16_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG2Y32 = function (data) { + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG2Y32(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3X8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3X8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3X8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3X8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3X8_2 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3X8_2(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3X8_3 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3X8_3(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3X16_0 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3X16_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3X16_1 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3X16_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3X32 = function (data) { + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3X32(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3Y8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3Y8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3Y8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3Y8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3Y8_2 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3Y8_2(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3Y8_3 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3Y8_3(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3Y16_0 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3Y16_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3Y16_1 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3Y16_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBG3Y32 = function (data) { + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBG3Y32(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWIN0XCOORDRight8 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeWIN0XCOORDRight8(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWIN0XCOORDLeft8 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeWIN0XCOORDLeft8(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWIN0XCOORD16 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeWIN0XCOORD16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWIN1XCOORDRight8 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeWIN1XCOORDRight8(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWIN1XCOORDLeft8 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeWIN1XCOORDLeft8(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWIN1XCOORD16 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeWIN1XCOORD16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWINXCOORD32 = function (data) { + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeWINXCOORD32(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWIN0YCOORDBottom8 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeWIN0YCOORDBottom8(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWIN0YCOORDTop8 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeWIN0YCOORDTop8(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWIN0YCOORD16 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeWIN0YCOORD16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWIN1YCOORDBottom8 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeWIN1YCOORDBottom8(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWIN1YCOORDTop8 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeWIN1YCOORDTop8(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWIN1YCOORD16 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeWIN1YCOORD16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWINYCOORD32 = function (data) { + data = data | 0; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeWINYCOORD32(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeWIN0IN8 = function (data) { + data = data | 0; + data = data & 0x3F; + this.IOCore.updateGraphicsClocking(); + this.IOData8[12] = data | 0; + this.renderer.writeWIN0IN8(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readWIN0IN8 = function () { + return this.IOData8[12] | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeWIN1IN8 = function (data) { + data = data | 0; + data = data & 0x3F; + this.IOCore.updateGraphicsClocking(); + this.IOData8[13] = data | 0; + this.renderer.writeWIN1IN8(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readWIN1IN8 = function () { + return this.IOData8[13] | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeWINOUT8 = function (data) { + data = data | 0; + data = data & 0x3F; + this.IOCore.updateGraphicsClocking(); + this.IOData8[14] = data | 0; + this.renderer.writeWINOUT8(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readWINOUT8 = function () { + return this.IOData8[14] | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeWINOBJIN8 = function (data) { + data = data | 0; + data = data & 0x3F; + this.IOCore.updateGraphicsClocking(); + this.IOData8[15] = data | 0; + this.renderer.writeWINOBJIN8(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readWINOBJIN8 = function () { + return this.IOData8[15] | 0; +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceRendererProxy.prototype.writeWININ16 = function (data) { + data = data | 0; + data = data & 0x3F3F; + this.IOCore.updateGraphicsClocking(); + this.IOData16[6] = data | 0; + this.renderer.writeWININ16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeWINOUT16 = function (data) { + data = data | 0; + data = data & 0x3F3F; + this.IOCore.updateGraphicsClocking(); + this.IOData16[7] = data | 0; + this.renderer.writeWINOUT16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeWINCONTROL32 = function (data) { + data = data | 0; + data = data & 0x3F3F3F3F; + this.IOCore.updateGraphicsClocking(); + this.IOData32[3] = data | 0; + this.renderer.writeWINCONTROL32(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.readWININ16 = function () { + return this.IOData16[6] | 0; + } + GameBoyAdvanceRendererProxy.prototype.readWINOUT16 = function () { + return this.IOData16[7] | 0; + } + GameBoyAdvanceRendererProxy.prototype.readWINCONTROL32 = function () { + return this.IOData32[3] | 0; + } +} +else { + GameBoyAdvanceRendererProxy.prototype.writeWININ16 = function (data) { + data = data | 0; + data = data & 0x3F3F; + this.IOCore.updateGraphicsClocking(); + this.IOData8[12] = data & 0xFF; + this.IOData8[13] = data >> 8; + this.renderer.writeWININ16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeWINOUT16 = function (data) { + data = data | 0; + data = data & 0x3F3F; + this.IOCore.updateGraphicsClocking(); + this.IOData8[14] = data & 0xFF; + this.IOData8[15] = data >> 8; + this.renderer.writeWINOUT16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeWINCONTROL32 = function (data) { + data = data | 0; + data = data & 0x3F3F3F3F; + this.IOCore.updateGraphicsClocking(); + this.IOData8[12] = data & 0xFF; + this.IOData8[13] = (data >> 8) & 0xFF; + this.IOData8[14] = (data >> 16) & 0xFF; + this.IOData8[15] = data >>> 24; + this.renderer.writeWINCONTROL32(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.readWININ16 = function () { + return this.IOData8[12] | (this.IOData8[13] << 8); + } + GameBoyAdvanceRendererProxy.prototype.readWINOUT16 = function () { + return this.IOData8[14] | (this.IOData8[15] << 8); + } + GameBoyAdvanceRendererProxy.prototype.readWINCONTROL32 = function () { + return this.IOData8[12] | (this.IOData8[13] << 8) | (this.IOData8[14] << 16) | (this.IOData8[15] << 24); + } +} +GameBoyAdvanceRendererProxy.prototype.writeMOSAIC8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeMOSAIC8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeMOSAIC8_1 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeMOSAIC8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeMOSAIC16 = function (data) { + data = data | 0; + data = data & 0xFFFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeMOSAIC16(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeBLDCNT8_0 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[16] = data | 0; + this.renderer.writeBLDCNT8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readBLDCNT8_0 = function () { + return this.IOData8[16] | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeBLDCNT8_1 = function (data) { + data = data | 0; + data = data & 0x3F; + this.IOCore.updateGraphicsClocking(); + this.IOData8[17] = data | 0; + this.renderer.writeBLDCNT8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readBLDCNT8_1 = function () { + return this.IOData8[17] | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeBLDALPHA8_0 = function (data) { + data = data | 0; + data = data & 0x1F; + this.IOCore.updateGraphicsClocking(); + this.IOData8[18] = data | 0; + this.renderer.writeBLDALPHA8_0(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readBLDALPHA8_0 = function () { + return this.IOData8[18] | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeBLDALPHA8_1 = function (data) { + data = data | 0; + data = data & 0x1F; + this.IOCore.updateGraphicsClocking(); + this.IOData8[19] = data | 0; + this.renderer.writeBLDALPHA8_1(data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readBLDALPHA8_1 = function () { + return this.IOData8[19] | 0; +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceRendererProxy.prototype.writeBLDCNT16 = function (data) { + data = data | 0; + data = data & 0x3FFF; + this.IOCore.updateGraphicsClocking(); + this.IOData16[8] = data | 0; + this.renderer.writeBLDCNT16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeBLDALPHA16 = function (data) { + data = data | 0; + data = data & 0x1F1F; + this.IOCore.updateGraphicsClocking(); + this.IOData16[9] = data | 0; + this.renderer.writeBLDALPHA16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeBLDCNT32 = function (data) { + data = data | 0; + data = data & 0x1F1F3FFF; + this.IOCore.updateGraphicsClocking(); + this.IOData32[4] = data | 0; + this.renderer.writeBLDCNT32(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.readBLDCNT16 = function () { + return this.IOData16[8] | 0; + } + GameBoyAdvanceRendererProxy.prototype.readBLDALPHA16 = function () { + return this.IOData16[9] | 0; + } + GameBoyAdvanceRendererProxy.prototype.readBLDCNT32 = function () { + return this.IOData32[4] | 0; + } +} +else { + GameBoyAdvanceRendererProxy.prototype.writeBLDCNT16 = function (data) { + data = data | 0; + data = data & 0x3FFF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[16] = data & 0xFF; + this.IOData8[17] = data >> 8; + this.renderer.writeBLDCNT16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeBLDALPHA16 = function (data) { + data = data | 0; + data = data & 0x1F1F; + this.IOCore.updateGraphicsClocking(); + this.IOData8[18] = data & 0xFF; + this.IOData8[19] = data >> 8; + this.renderer.writeBLDALPHA16(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.writeBLDCNT32 = function (data) { + data = data | 0; + data = data & 0x1F1F3FFF; + this.IOCore.updateGraphicsClocking(); + this.IOData8[16] = data & 0xFF; + this.IOData8[17] = (data >> 8) & 0xFF; + this.IOData8[18] = (data >> 16) & 0xFF; + this.IOData8[19] = data >>> 24; + this.renderer.writeBLDCNT32(data | 0); + } + GameBoyAdvanceRendererProxy.prototype.readBLDCNT16 = function () { + return this.IOData8[16] | (this.IOData8[17] << 8); + } + GameBoyAdvanceRendererProxy.prototype.readBLDALPHA16 = function () { + return this.IOData8[18] | (this.IOData8[19] << 8); + } + GameBoyAdvanceRendererProxy.prototype.readBLDCNT32 = function () { + return this.IOData8[16] | (this.IOData8[17] << 8) | (this.IOData8[18] << 16) | (this.IOData8[19] << 24); + } +} +GameBoyAdvanceRendererProxy.prototype.writeBLDY8 = function (data) { + data = data | 0; + data = data & 0xFF; + this.IOCore.updateGraphicsClocking(); + this.renderer.writeBLDY8(data | 0); +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceRendererProxy.prototype.writeVRAM8 = function (address, data) { + address = address | 0; + data = data | 0; + if ((address & 0x10000) == 0 || ((address & 0x17FFF) < 0x14000 && (this.IOData8[0] & 0x7) >= 3)) { + address = address & (((address & 0x10000) >> 1) ^ address); + address = address >> 1; + data = Math.imul(data & 0xFF, 0x101) | 0; + this.renderer.writeVRAM8(address | 0, data | 0); + } + } +} +else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceRendererProxy.prototype.writeVRAM8 = function (address, data) { + address = address | 0; + data = data | 0; + if ((address & 0x10000) == 0 || ((address & 0x17FFF) < 0x14000 && (this.IOData8[0] & 0x7) >= 3)) { + address = address & (((address & 0x10000) >> 1) ^ address); + address = address >> 1; + data = (data & 0xFF) * 0x101; + this.renderer.writeVRAM8(address | 0, data | 0); + } + } +} +GameBoyAdvanceRendererProxy.prototype.writeVRAM16 = function (address, data) { + address = address | 0; + data = data | 0; + address = address & (((address & 0x10000) >> 1) ^ address); + this.renderer.writeVRAM16(address >> 1, data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeVRAM32 = function (address, data) { + address = address | 0; + data = data | 0; + address = address & (((address & 0x10000) >> 1) ^ address); + this.renderer.writeVRAM32(address >> 2, data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readVRAM16 = function (address) { + address = address | 0; + address = address & (((address & 0x10000) >> 1) ^ address); + var data = this.renderer.readVRAM16(address >> 1) | 0; + return data | 0; +} +GameBoyAdvanceRendererProxy.prototype.readVRAM32 = function (address) { + address = address | 0; + address = address & (((address & 0x10000) >> 1) ^ address); + var data = this.renderer.readVRAM32(address >> 2) | 0; + return data | 0; +} +GameBoyAdvanceRendererProxy.prototype.writePalette16 = function (address, data) { + data = data | 0; + address = address | 0; + this.renderer.writePalette16(address >> 1, data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writePalette32 = function (address, data) { + data = data | 0; + address = address | 0; + this.renderer.writePalette32(address >> 2, data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readPalette16 = function (address) { + address = address | 0; + var data = this.renderer.readPalette16(address >> 1) | 0; + return data | 0; +} +GameBoyAdvanceRendererProxy.prototype.readPalette32 = function (address) { + address = address | 0; + var data = this.renderer.readPalette32(address >> 2) | 0; + return data | 0; +} +GameBoyAdvanceRendererProxy.prototype.readVRAM8 = function (address) { + address = address | 0; + address = address & (((address & 0x10000) >> 1) ^ address); + var data = this.renderer.readVRAM8(address | 0) | 0; + return data | 0; +} +GameBoyAdvanceRendererProxy.prototype.writeOAM16 = function (address, data) { + address = address | 0; + data = data | 0; + this.renderer.writeOAM16(address >> 1, data | 0); +} +GameBoyAdvanceRendererProxy.prototype.writeOAM32 = function (address, data) { + address = address | 0; + data = data | 0; + this.renderer.writeOAM32(address >> 2, data | 0); +} +GameBoyAdvanceRendererProxy.prototype.readOAM = function (address) { + address = address | 0; + var data = this.renderer.readOAM(address | 0) | 0; + return data | 0; +} +GameBoyAdvanceRendererProxy.prototype.readOAM16 = function (address) { + address = address | 0; + var data = this.renderer.readOAM16(address >> 1) | 0; + return data | 0; +} +GameBoyAdvanceRendererProxy.prototype.readOAM32 = function (address) { + address = address | 0; + var data = this.renderer.readOAM32(address >> 2) | 0; + return data | 0; +} +GameBoyAdvanceRendererProxy.prototype.readPalette8 = function (address) { + address = address | 0; + var data = this.renderer.readPalette8(address | 0) | 0; + return data | 0; +} diff --git a/public/gfiles/gba/IodineGBA/core/graphics/RendererShim.js b/public/gfiles/gba/IodineGBA/core/graphics/RendererShim.js new file mode 100644 index 00000000..255f312d --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/graphics/RendererShim.js @@ -0,0 +1,919 @@ +"use strict"; +/* + Copyright (C) 2012-2016 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + function getGameBoyAdvanceGraphicsRenderer(coreExposed, skippingBIOS) { + if (!coreExposed.offthreadGfxEnabled() || typeof SharedArrayBuffer != "function" || typeof Atomics != "object") { + return new GameBoyAdvanceGraphicsRenderer(coreExposed, skippingBIOS); + } + else { + try { + //Some browsers don't allow webworkers via file:/// + return new GameBoyAdvanceGraphicsRendererShim(coreExposed, skippingBIOS); + } + catch (error) { + return new GameBoyAdvanceGraphicsRenderer(coreExposed, skippingBIOS); + } + } + } + function GameBoyAdvanceGraphicsRendererShim(coreExposed, skippingBIOS) { + this.coreExposed = coreExposed; + this.initializeWorker(skippingBIOS); + this.appendAtomicSync(); + this.initializeBuffers(); + this.shareStaticBuffers(); + this.shareDynamicBuffers(); + } + GameBoyAdvanceGraphicsRendererShim.prototype.initializeWorker = function (skippingBIOS) { + skippingBIOS = !!skippingBIOS; + //Apparently running on localhost is nearly impossible for webworkers in a cross-browser manner: + var loc = location.href; + var loc = loc.split("/"); + loc = loc.slice(0, loc.length - 1).join("/"); + try { + if (typeof WorkerGlobalScope === 'undefined' || !(self instanceof WorkerGlobalScope)) { + //Use the catch block: + throw null; + } + //Firefox: + var loc2 = loc + "/graphics/Worker.js"; + this.worker = new Worker(loc2); + } + catch (e) { + //Google Chrome: + var loc3 = loc + "/IodineGBA/core/graphics/Worker.js"; + this.worker = new Worker(loc3); + } + this.worker.postMessage({ + messageID:1, + skippingBIOS:!!skippingBIOS + }); + } + GameBoyAdvanceGraphicsRendererShim.prototype.initializeBuffers = function () { + //Graphics Buffers: + this.gfxCommandBufferLength = 0x80000; + this.gfxCommandBufferMask = ((this.gfxCommandBufferLength | 0) - 1) | 0; + this.gfxCommandBuffer = getSharedInt32Array(this.gfxCommandBufferLength | 0); + this.gfxCommandCounters = getSharedInt32Array(3); + this.gfxLineCounter = getSharedInt32Array(1); + this.start = 0; + this.end = 0; + this.linesPassed = 0; + this.OAMRAM = getUint8Array(0x400); + this.OAMRAM16 = getUint16View(this.OAMRAM); + this.OAMRAM32 = getInt32View(this.OAMRAM); + this.paletteRAM = getUint8Array(0x400); + this.VRAM = getUint8Array(0x18000); + this.VRAM16 = getUint16View(this.VRAM); + this.VRAM32 = getInt32View(this.VRAM); + this.paletteRAM16 = getUint16View(this.paletteRAM); + this.paletteRAM32 = getInt32View(this.paletteRAM); + } + GameBoyAdvanceGraphicsRendererShim.prototype.increaseCommandBufferCapacity = function () { + //Tell the other thread to break for receiving the new buffer: + Atomics.store(this.gfxCommandCounters, 2, 1); + //Double the size to the next power of 2: + this.gfxCommandBufferLength = this.gfxCommandBufferLength << 1; + this.gfxCommandBufferMask = ((this.gfxCommandBufferLength | 0) - 1) | 0; + this.gfxCommandBuffer = getSharedInt32Array(this.gfxCommandBufferLength | 0); + this.gfxCommandCounters = getSharedInt32Array(3); + this.start = 0; + this.end = 0; + //Share our new buffers: + this.shareDynamicBuffers(); + } + GameBoyAdvanceGraphicsRendererShim.prototype.appendAtomicSync = function () { + //Command buffer counters get synchronized with emulator runtime head/end for efficiency: + var parentObj = this; + this.coreExposed.appendStartIterationSync(function () { + parentObj.synchronizeReader(); + }); + this.coreExposed.appendEndIterationSync(function () { + parentObj.synchronizeWriter(); + }); + this.coreExposed.appendTerminationSync(function () { + //Core instance being replaced, kill the worker thread: + parentObj.worker.terminate(); + }); + } +GameBoyAdvanceGraphicsRendererShim.prototype.shareStaticBuffers = function () { + try { + this.worker.postMessage({ + messageID:0, + gfxBuffers:gfxBuffers, + gfxCounters:gfxCounters, + gfxLineCounter:this.gfxLineCounter + }, [ + gfxBuffers[0].buffer, + gfxBuffers[1].buffer, + gfxCounters.buffer, + this.gfxLineCounter.buffer + ]); + } + catch (e) { + this.worker.postMessage({ + messageID:0, + gfxBuffers:gfxBuffers, + gfxCounters:gfxCounters, + gfxLineCounter:this.gfxLineCounter + }); + } +} +GameBoyAdvanceGraphicsRendererShim.prototype.shareDynamicBuffers = function () { + try { + this.worker.postMessage({ + messageID:2, + gfxCommandBuffer:this.gfxCommandBuffer, + gfxCommandCounters:this.gfxCommandCounters + }, [ + this.gfxCommandBuffer.buffer, + this.gfxCommandCounters.buffer + ]); + } + catch (e) { + this.worker.postMessage({ + messageID:2, + gfxCommandBuffer:this.gfxCommandBuffer, + gfxCommandCounters:this.gfxCommandCounters + }); + } + //Wake up the producer "GPU" thread: + Atomics.notify(gfxCounters, 2, 1); +} +GameBoyAdvanceGraphicsRendererShim.prototype.pushCommand = function (command, data) { + command = command | 0; + data = data | 0; + //Check if buffer is full: + this.checkCommandBufferFull(); + //Get the write offset into the ring buffer: + var endCorrected = this.end & this.gfxCommandBufferMask; + //Push command into buffer: + this.gfxCommandBuffer[endCorrected | 0] = command | 0; + //Push data into buffer: + this.gfxCommandBuffer[endCorrected | 1] = data | 0; + //Update the cross thread buffering count: + this.end = ((this.end | 0) + 2) | 0; +} +GameBoyAdvanceGraphicsRendererShim.prototype.checkCommandBufferFull = function () { + if ((this.start | 0) == (((this.end | 0) - (this.gfxCommandBufferLength | 0)) | 0)) { + //Give the reader our updated counter and tell it to run: + this.synchronizeWriter(); + //Increase buffer size: + this.increaseCommandBufferCapacity(); + //Reload reader counter value: + this.synchronizeReader(); + } +} +GameBoyAdvanceGraphicsRendererShim.prototype.synchronizeWriter = function () { + //Store command buffer writer counter value: + Atomics.store(this.gfxCommandCounters, 1, this.end | 0); + Atomics.store(this.gfxLineCounter, 0, this.linesPassed | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.synchronizeReader = function () { + //Load command buffer reader counter value: + this.start = Atomics.load(this.gfxCommandCounters, 0) | 0; +} +GameBoyAdvanceGraphicsRendererShim.prototype.pushVRAM16 = function (address, data) { + address = address | 0; + data = data | 0; + address = address & 0xFFFF; + this.pushCommand(0x10000 | address, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.pushVRAM32 = function (address, data) { + address = address | 0; + data = data | 0; + address = address & 0x7FFF; + this.pushCommand(0x20000 | address, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.pushPAL16 = function (address, data) { + address = address | 0; + data = data | 0; + address = address & 0x1FF; + this.pushCommand(0x30000 | address, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.pushPAL32 = function (address, data) { + address = address | 0; + data = data | 0; + address = address & 0xFF; + this.pushCommand(0x40000 | address, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.pushOAM16 = function (address, data) { + address = address | 0; + data = data | 0; + address = address & 0x1FF; + this.pushCommand(0x50000 | address, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.pushOAM32 = function (address, data) { + address = address | 0; + data = data | 0; + address = address & 0xFF; + this.pushCommand(0x60000 | address, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.incrementScanLineQueue = function () { + //Increment scan line command: + this.pushCommand(0, 0); + //Increment how many scanlines we've pushed out: + this.linesPassed = ((this.linesPassed | 0) + 1) | 0; +} +GameBoyAdvanceGraphicsRendererShim.prototype.ensureFraming = function () { + //Vertical blank synchronization command: + this.pushCommand(0, 1); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeDISPCNT8_0 = function (data) { + data = data | 0; + this.pushCommand(1, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeDISPCNT8_1 = function (data) { + data = data | 0; + this.pushCommand(2, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeDISPCNT8_2 = function (data) { + data = data | 0; + this.pushCommand(3, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeDISPCNT16 = function (data) { + data = data | 0; + this.pushCommand(4, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeDISPCNT32 = function (data) { + data = data | 0; + this.pushCommand(5, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG0CNT8_0 = function (data) { + data = data | 0; + this.pushCommand(6, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG0CNT8_1 = function (data) { + data = data | 0; + this.pushCommand(7, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG0CNT16 = function (data) { + data = data | 0; + this.pushCommand(8, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG1CNT8_0 = function (data) { + data = data | 0; + this.pushCommand(9, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG1CNT8_1 = function (data) { + data = data | 0; + this.pushCommand(10, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG1CNT16 = function (data) { + data = data | 0; + this.pushCommand(11, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG0BG1CNT32 = function (data) { + data = data | 0; + this.pushCommand(12, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2CNT8_0 = function (data) { + data = data | 0; + this.pushCommand(13, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2CNT8_1 = function (data) { + data = data | 0; + this.pushCommand(14, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2CNT16 = function (data) { + data = data | 0; + this.pushCommand(15, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3CNT8_0 = function (data) { + data = data | 0; + this.pushCommand(16, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3CNT8_1 = function (data) { + data = data | 0; + this.pushCommand(17, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3CNT16 = function (data) { + data = data | 0; + this.pushCommand(18, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2BG3CNT32 = function (data) { + data = data | 0; + this.pushCommand(19, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG0HOFS8_0 = function (data) { + data = data | 0; + this.pushCommand(20, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG0HOFS8_1 = function (data) { + data = data | 0; + this.pushCommand(21, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG0HOFS16 = function (data) { + data = data | 0; + this.pushCommand(22, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG0VOFS8_0 = function (data) { + data = data | 0; + this.pushCommand(23, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG0VOFS8_1 = function (data) { + data = data | 0; + this.pushCommand(24, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG0VOFS16 = function (data) { + data = data | 0; + this.pushCommand(25, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG0OFS32 = function (data) { + data = data | 0; + this.pushCommand(26, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG1HOFS8_0 = function (data) { + data = data | 0; + this.pushCommand(27, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG1HOFS8_1 = function (data) { + data = data | 0; + this.pushCommand(28, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG1HOFS16 = function (data) { + data = data | 0; + this.pushCommand(29, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG1VOFS8_0 = function (data) { + data = data | 0; + this.pushCommand(30, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG1VOFS8_1 = function (data) { + data = data | 0; + this.pushCommand(31, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG1VOFS16 = function (data) { + data = data | 0; + this.pushCommand(32, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG1OFS32 = function (data) { + data = data | 0; + this.pushCommand(33, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2HOFS8_0 = function (data) { + data = data | 0; + this.pushCommand(34, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2HOFS8_1 = function (data) { + data = data | 0; + this.pushCommand(35, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2HOFS16 = function (data) { + data = data | 0; + this.pushCommand(36, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2VOFS8_0 = function (data) { + data = data | 0; + this.pushCommand(37, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2VOFS8_1 = function (data) { + data = data | 0; + this.pushCommand(38, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2VOFS16 = function (data) { + data = data | 0; + this.pushCommand(39, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2OFS32 = function (data) { + data = data | 0; + this.pushCommand(40, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3HOFS8_0 = function (data) { + data = data | 0; + this.pushCommand(41, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3HOFS8_1 = function (data) { + data = data | 0; + this.pushCommand(42, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3HOFS16 = function (data) { + data = data | 0; + this.pushCommand(43, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3VOFS8_0 = function (data) { + data = data | 0; + this.pushCommand(44, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3VOFS8_1 = function (data) { + data = data | 0; + this.pushCommand(45, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3VOFS16 = function (data) { + data = data | 0; + this.pushCommand(46, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3OFS32 = function (data) { + data = data | 0; + this.pushCommand(47, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2PA8_0 = function (data) { + data = data | 0; + this.pushCommand(48, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2PA8_1 = function (data) { + data = data | 0; + this.pushCommand(49, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2PA16 = function (data) { + data = data | 0; + this.pushCommand(50, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2PB8_0 = function (data) { + data = data | 0; + this.pushCommand(51, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2PB8_1 = function (data) { + data = data | 0; + this.pushCommand(52, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2PB16 = function (data) { + data = data | 0; + this.pushCommand(53, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2PAB32 = function (data) { + data = data | 0; + this.pushCommand(54, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2PC8_0 = function (data) { + data = data | 0; + this.pushCommand(55, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2PC8_1 = function (data) { + data = data | 0; + this.pushCommand(56, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2PC16 = function (data) { + data = data | 0; + this.pushCommand(57, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2PD8_0 = function (data) { + data = data | 0; + this.pushCommand(58, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2PD8_1 = function (data) { + data = data | 0; + this.pushCommand(59, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2PD16 = function (data) { + data = data | 0; + this.pushCommand(60, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2PCD32 = function (data) { + data = data | 0; + this.pushCommand(61, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3PA8_0 = function (data) { + data = data | 0; + this.pushCommand(62, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3PA8_1 = function (data) { + data = data | 0; + this.pushCommand(63, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3PA16 = function (data) { + data = data | 0; + this.pushCommand(64, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3PB8_0 = function (data) { + data = data | 0; + this.pushCommand(65, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3PB8_1 = function (data) { + data = data | 0; + this.pushCommand(66, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3PB16 = function (data) { + data = data | 0; + this.pushCommand(67, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3PAB32 = function (data) { + data = data | 0; + this.pushCommand(68, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3PC8_0 = function (data) { + data = data | 0; + this.pushCommand(69, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3PC8_1 = function (data) { + data = data | 0; + this.pushCommand(70, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3PC16 = function (data) { + data = data | 0; + this.pushCommand(71, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3PD8_0 = function (data) { + data = data | 0; + this.pushCommand(72, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3PD8_1 = function (data) { + data = data | 0; + this.pushCommand(73, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3PD16 = function (data) { + data = data | 0; + this.pushCommand(74, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3PCD32 = function (data) { + data = data | 0; + this.pushCommand(75, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2X8_0 = function (data) { + data = data | 0; + this.pushCommand(76, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2X8_1 = function (data) { + data = data | 0; + this.pushCommand(77, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2X8_2 = function (data) { + data = data | 0; + this.pushCommand(78, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2X8_3 = function (data) { + data = data | 0; + this.pushCommand(79, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2X16_0 = function (data) { + data = data | 0; + this.pushCommand(80, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2X16_1 = function (data) { + data = data | 0; + this.pushCommand(81, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2X32 = function (data) { + data = data | 0; + this.pushCommand(82, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2Y8_0 = function (data) { + data = data | 0; + this.pushCommand(83, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2Y8_1 = function (data) { + data = data | 0; + this.pushCommand(84, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2Y8_2 = function (data) { + data = data | 0; + this.pushCommand(85, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2Y8_3 = function (data) { + data = data | 0; + this.pushCommand(86, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2Y16_0 = function (data) { + data = data | 0; + this.pushCommand(87, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2Y16_1 = function (data) { + data = data | 0; + this.pushCommand(88, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG2Y32 = function (data) { + data = data | 0; + this.pushCommand(89, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3X8_0 = function (data) { + data = data | 0; + this.pushCommand(90, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3X8_1 = function (data) { + data = data | 0; + this.pushCommand(91, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3X8_2 = function (data) { + data = data | 0; + this.pushCommand(92, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3X8_3 = function (data) { + data = data | 0; + this.pushCommand(93, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3X16_0 = function (data) { + data = data | 0; + this.pushCommand(94, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3X16_1 = function (data) { + data = data | 0; + this.pushCommand(95, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3X32 = function (data) { + data = data | 0; + this.pushCommand(96, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3Y8_0 = function (data) { + data = data | 0; + this.pushCommand(97, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3Y8_1 = function (data) { + data = data | 0; + this.pushCommand(98, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3Y8_2 = function (data) { + data = data | 0; + this.pushCommand(99, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3Y8_3 = function (data) { + data = data | 0; + this.pushCommand(100, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3Y16_0 = function (data) { + data = data | 0; + this.pushCommand(101, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3Y16_1 = function (data) { + data = data | 0; + this.pushCommand(102, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBG3Y32 = function (data) { + data = data | 0; + this.pushCommand(103, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWIN0XCOORDRight8 = function (data) { + data = data | 0; + this.pushCommand(104, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWIN0XCOORDLeft8 = function (data) { + data = data | 0; + this.pushCommand(105, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWIN0XCOORD16 = function (data) { + data = data | 0; + this.pushCommand(106, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWIN1XCOORDRight8 = function (data) { + data = data | 0; + this.pushCommand(107, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWIN1XCOORDLeft8 = function (data) { + data = data | 0; + this.pushCommand(108, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWIN1XCOORD16 = function (data) { + data = data | 0; + this.pushCommand(109, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWINXCOORD32 = function (data) { + data = data | 0; + this.pushCommand(110, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWIN0YCOORDBottom8 = function (data) { + data = data | 0; + this.pushCommand(111, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWIN0YCOORDTop8 = function (data) { + data = data | 0; + this.pushCommand(112, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWIN0YCOORD16 = function (data) { + data = data | 0; + this.pushCommand(113, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWIN1YCOORDBottom8 = function (data) { + data = data | 0; + this.pushCommand(114, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWIN1YCOORDTop8 = function (data) { + data = data | 0; + this.pushCommand(115, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWIN1YCOORD16 = function (data) { + data = data | 0; + this.pushCommand(116, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWINYCOORD32 = function (data) { + data = data | 0; + this.pushCommand(117, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWIN0IN8 = function (data) { + data = data | 0; + this.pushCommand(118, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWIN1IN8 = function (data) { + data = data | 0; + this.pushCommand(119, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWININ16 = function (data) { + data = data | 0; + this.pushCommand(120, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWINOUT8 = function (data) { + data = data | 0; + this.pushCommand(121, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWINOBJIN8 = function (data) { + data = data | 0; + this.pushCommand(122, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWINOUT16 = function (data) { + data = data | 0; + this.pushCommand(123, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeWINCONTROL32 = function (data) { + data = data | 0; + this.pushCommand(124, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeMOSAIC8_0 = function (data) { + data = data | 0; + this.pushCommand(125, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeMOSAIC8_1 = function (data) { + data = data | 0; + this.pushCommand(126, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeMOSAIC16 = function (data) { + data = data | 0; + this.pushCommand(127, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBLDCNT8_0 = function (data) { + data = data | 0; + this.pushCommand(128, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBLDCNT8_1 = function (data) { + data = data | 0; + this.pushCommand(129, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBLDCNT16 = function (data) { + data = data | 0; + this.pushCommand(130, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBLDALPHA8_0 = function (data) { + data = data | 0; + this.pushCommand(131, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBLDALPHA8_1 = function (data) { + data = data | 0; + this.pushCommand(132, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBLDALPHA16 = function (data) { + data = data | 0; + this.pushCommand(133, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBLDCNT32 = function (data) { + data = data | 0; + this.pushCommand(134, data | 0); +} +GameBoyAdvanceGraphicsRendererShim.prototype.writeBLDY8 = function (data) { + data = data | 0; + this.pushCommand(135, data | 0); +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceGraphicsRendererShim.prototype.writeVRAM8 = + GameBoyAdvanceGraphicsRendererShim.prototype.writeVRAM16 = function (address, data) { + address = address | 0; + data = data | 0; + this.VRAM16[address & 0xFFFF] = data & 0xFFFF; + this.pushVRAM16(address | 0, data | 0); + } + GameBoyAdvanceGraphicsRendererShim.prototype.writeVRAM32 = function (address, data) { + address = address | 0; + data = data | 0; + this.VRAM32[address & 0x7FFF] = data | 0; + this.pushVRAM32(address | 0, data | 0); + } + GameBoyAdvanceGraphicsRendererShim.prototype.readVRAM16 = function (address) { + address = address | 0; + return this.VRAM16[address & 0xFFFF] | 0; + } + GameBoyAdvanceGraphicsRendererShim.prototype.readVRAM32 = function (address) { + address = address | 0; + return this.VRAM32[address & 0x7FFF] | 0; + } + GameBoyAdvanceGraphicsRendererShim.prototype.writePalette16 = function (address, data) { + data = data | 0; + address = address | 0; + this.paletteRAM16[address & 0x1FF] = data & 0xFFFF; + this.pushPAL16(address | 0, data | 0); + } + GameBoyAdvanceGraphicsRendererShim.prototype.writePalette32 = function (address, data) { + data = data | 0; + address = address | 0; + this.paletteRAM32[address & 0xFF] = data | 0; + this.pushPAL32(address | 0, data | 0); + } + GameBoyAdvanceGraphicsRendererShim.prototype.readPalette16 = function (address) { + address = address | 0; + return this.paletteRAM16[address & 0x1FF] | 0; + } + GameBoyAdvanceGraphicsRendererShim.prototype.readPalette32 = function (address) { + address = address | 0; + return this.paletteRAM32[address & 0xFF] | 0; + } + GameBoyAdvanceGraphicsRendererShim.prototype.writeOAM16 = function (address, data) { + address = address | 0; + data = data | 0; + this.OAMRAM16[address & 0x1FF] = data & 0xFFFF; + this.pushOAM16(address | 0, data | 0); + } + GameBoyAdvanceGraphicsRendererShim.prototype.writeOAM32 = function (address, data) { + address = address | 0; + data = data | 0; + this.OAMRAM32[address & 0xFF] = data | 0; + this.pushOAM32(address | 0, data | 0); + } + GameBoyAdvanceGraphicsRendererShim.prototype.readOAM16 = function (address) { + address = address | 0; + return this.OAMRAM16[address & 0x1FF] | 0; + } + GameBoyAdvanceGraphicsRendererShim.prototype.readOAM32 = function (address) { + address = address | 0; + return this.OAMRAM32[address & 0xFF] | 0; + } +} +else { + GameBoyAdvanceGraphicsRendererShim.prototype.writeVRAM8 = + GameBoyAdvanceGraphicsRendererShim.prototype.writeVRAM16 = function (address, data) { + address <<= 1; + address &= 0x1FFFE; + this.VRAM[address] = data & 0xFF; + this.VRAM[address + 1] = (data >> 8) & 0xFF; + this.pushVRAM16(address, data); + } + GameBoyAdvanceGraphicsRendererShim.prototype.writeVRAM32 = function (address, data) { + address <<= 2; + address &= 0x1FFFC; + this.VRAM[address] = data & 0xFF; + this.VRAM[address + 1] = (data >> 8) & 0xFF; + this.VRAM[address + 2] = (data >> 16) & 0xFF; + this.VRAM[address + 3] = data >>> 24; + this.pushVRAM32(address, data); + } + GameBoyAdvanceGraphicsRendererShim.prototype.readVRAM16 = function (address) { + address <<= 1; + address &= 0x1FFFE; + return this.VRAM[address] | (this.VRAM[address + 1] << 8); + } + GameBoyAdvanceGraphicsRendererShim.prototype.readVRAM32 = function (address) { + address <<= 2; + address &= 0x1FFFC; + return this.VRAM[address] | (this.VRAM[address + 1] << 8) | (this.VRAM[address + 2] << 16) | (this.VRAM[address + 3] << 24); + } + GameBoyAdvanceGraphicsRendererShim.prototype.writePalette16 = function (address, data) { + this.paletteRAM[address << 1] = data & 0xFF; + this.paletteRAM[(address << 1) + 1] = data >> 8; + this.pushPAL16(address, data); + } + GameBoyAdvanceGraphicsRendererShim.prototype.writePalette32 = function (address, data) { + address <<= 2; + this.paletteRAM[address] = data & 0xFF; + this.paletteRAM[address | 1] = (data >> 8) & 0xFF; + this.paletteRAM[address | 2] = (data >> 16) & 0xFF; + this.paletteRAM[address | 3] = data >>> 24; + address >>= 2; + this.pushPAL32(address, data); + } + GameBoyAdvanceGraphicsRendererShim.prototype.readPalette16 = function (address) { + address &= 0x3FE; + return this.paletteRAM[address] | (this.paletteRAM[address | 1] << 8); + } + GameBoyAdvanceGraphicsRendererShim.prototype.readPalette32 = function (address) { + address &= 0x3FC; + return this.paletteRAM[address] | (this.paletteRAM[address | 1] << 8) | (this.paletteRAM[address | 2] << 16) | (this.paletteRAM[address | 3] << 24); + } + GameBoyAdvanceGraphicsRendererShim.prototype.writeOAM16 = function (address, data) { + address &= 0x1FF; + this.OAMRAM[address << 1] = data & 0xFF; + this.OAMRAM[(address << 1) | 1] = data >> 8; + this.pushOAM16(address, data); + } + GameBoyAdvanceGraphicsRendererShim.prototype.writeOAM32 = function (address, data) { + address &= 0xFF; + address <<= 2; + this.OAMRAM[address] = data & 0xFF; + this.OAMRAM[address + 1] = (data >> 8) & 0xFF; + this.OAMRAM[address + 2] = (data >> 16) & 0xFF; + this.OAMRAM[address + 3] = data >>> 24; + address >>= 2; + this.pushOAM32(address, data); + } + GameBoyAdvanceGraphicsRendererShim.prototype.readOAM16 = function (address) { + address &= 0x1FF; + address <<= 1; + return this.OAMRAM[address] | (this.OAMRAM[address | 1] << 8); + } + GameBoyAdvanceGraphicsRendererShim.prototype.readOAM32 = function (address) { + address &= 0xFF; + address <<= 2; + return this.OAMRAM[address] | (this.OAMRAM[address | 1] << 8) | (this.OAMRAM[address | 2] << 16) | (this.OAMRAM[address | 3] << 24); + } +} +GameBoyAdvanceGraphicsRendererShim.prototype.readVRAM8 = function (address) { + address = address | 0; + return this.VRAM[address & 0x1FFFF] | 0; +} +GameBoyAdvanceGraphicsRendererShim.prototype.readOAM = function (address) { + address = address | 0; + return this.OAMRAM[address & 0x3FF] | 0; +} +GameBoyAdvanceGraphicsRendererShim.prototype.readPalette8 = function (address) { + address = address | 0; + return this.paletteRAM[address & 0x3FF] | 0; +} diff --git a/public/gfiles/gba/IodineGBA/core/graphics/Window.js b/public/gfiles/gba/IodineGBA/core/graphics/Window.js new file mode 100644 index 00000000..631f4ed2 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/graphics/Window.js @@ -0,0 +1,116 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceWindowRenderer(compositor) { + //Get a layer compositor that we'll send our parameters for windowing to: + this.compositor = compositor; +} +GameBoyAdvanceWindowRenderer.prototype.initialize = function () { + //Initialize the compositor: + this.compositor.initialize(); + //Right windowing coordinate: + this.WINXCoordRight = 0; + //Left windowing coordinate: + this.WINXCoordLeft = 0; + //Bottom windowing coordinate: + this.WINYCoordBottom = 0; + //Top windowing coordinate: + this.WINYCoordTop = 0; + //Layer masking & color effects control: + this.windowDisplayControl = 0; + //Need to update the color effects status in the compositor: + this.preprocess(); +} +GameBoyAdvanceWindowRenderer.prototype.renderScanLine = function (line, toRender) { + line = line | 0; + toRender = toRender | 0; + //Windowing can disable out further layers: + toRender = toRender & this.windowDisplayControl; + //Check if we're doing windowing for the current line: + if (this.checkYRange(line | 0)) { + //Windowing is active for the current line: + var right = this.WINXCoordRight | 0; + var left = this.WINXCoordLeft | 0; + if ((left | 0) <= (right | 0)) { + //Windowing is left to right like expected: + left = Math.min(left | 0, 240) | 0; + right = Math.min(right | 0, 240) | 0; + //Render left coordinate to right coordinate: + this.compositor.renderScanLine(left | 0, right | 0, toRender | 0); + } + else { + //Invalid horizontal windowing coordinates, so invert horizontal windowing range: + left = Math.min(left | 0, 240) | 0; + right = Math.min(right | 0, 240) | 0; + //Render pixel 0 to right coordinate: + this.compositor.renderScanLine(0, right | 0, toRender | 0); + //Render left coordinate to last pixel: + this.compositor.renderScanLine(left | 0, 240, toRender | 0); + } + } +} +GameBoyAdvanceWindowRenderer.prototype.checkYRange = function (line) { + line = line | 0; + var bottom = this.WINYCoordBottom | 0; + var top = this.WINYCoordTop | 0; + if ((top | 0) <= (bottom | 0)) { + //Windowing is top to bottom like expected: + return ((line | 0) >= (top | 0) && (line | 0) < (bottom | 0)); + } + else { + //Invalid vertical windowing coordinates, so invert vertical windowing range: + return ((line | 0) < (top | 0) || (line | 0) >= (bottom | 0)); + } +} +GameBoyAdvanceWindowRenderer.prototype.preprocess = function () { + //Update the color effects status in the compositor: + this.compositor.preprocess(this.windowDisplayControl & 0x20); +} +GameBoyAdvanceWindowRenderer.prototype.writeWINXCOORDRight8 = function (data) { + data = data | 0; + //Right windowing coordinate: + this.WINXCoordRight = data | 0; +} +GameBoyAdvanceWindowRenderer.prototype.writeWINXCOORDLeft8 = function (data) { + data = data | 0; + //Left windowing coordinate: + this.WINXCoordLeft = data | 0; +} +GameBoyAdvanceWindowRenderer.prototype.writeWINYCOORDBottom8 = function (data) { + data = data | 0; + //Bottom windowing coordinate: + this.WINYCoordBottom = data | 0; +} +GameBoyAdvanceWindowRenderer.prototype.writeWINYCOORDTop8 = function (data) { + data = data | 0; + //Top windowing coordinate: + this.WINYCoordTop = data | 0; +} +GameBoyAdvanceWindowRenderer.prototype.writeWINXCOORD16 = function (data) { + data = data | 0; + //Right windowing coordinate: + this.WINXCoordRight = data & 0xFF; + //Left windowing coordinate: + this.WINXCoordLeft = data >> 8; +} +GameBoyAdvanceWindowRenderer.prototype.writeWINYCOORD16 = function (data) { + data = data | 0; + //Bottom windowing coordinate: + this.WINYCoordBottom = data & 0xFF; + //Top windowing coordinate: + this.WINYCoordTop = data >> 8; +} +GameBoyAdvanceWindowRenderer.prototype.writeWININ8 = function (data) { + data = data | 0; + //Layer masking & color effects control: + this.windowDisplayControl = data | 0; + //Need to update the color effects status in the compositor: + this.preprocess(); +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/graphics/Worker.js b/public/gfiles/gba/IodineGBA/core/graphics/Worker.js new file mode 100644 index 00000000..8dce9a06 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/graphics/Worker.js @@ -0,0 +1,587 @@ +"use strict"; +/* + Copyright (C) 2012-2016 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +importScripts("../../includes/TypedArrayShim.js"); +importScripts("Renderer.js"); +importScripts("BGTEXT.js"); +importScripts("BG2FrameBuffer.js"); +importScripts("BGMatrix.js"); +importScripts("AffineBG.js"); +importScripts("ColorEffects.js"); +importScripts("Mosaic.js"); +importScripts("OBJ.js"); +importScripts("OBJWindow.js"); +importScripts("Window.js"); +importScripts("Compositor.js"); +var renderer = null; +var gfxBuffers = null; +var gfxCounters = null; +var gfxCommandBuffer = null; +var gfxCommandCounters = null; +var gfxCommandBufferMask = 1; +var gfxLineCounter = null; +var gfxLinesCPU = 0; +var gfxLinesGPU = 0; +self.onmessage = function (event) { + var data = event.data; + switch (data.messageID | 0) { + case 0: + assignStaticBuffers(data.gfxBuffers, data.gfxCounters, data.gfxLineCounter); + break; + case 1: + initializeRenderer(!!data.skippingBIOS); + break; + default: + assignDynamicBuffers(data.gfxCommandBuffer, data.gfxCommandCounters); + waitForVSync(); + } +} +function copyBuffer(swizzledFrame) { + //Push a frame of graphics to the blitter handle: + //Load the counter values: + var start = Atomics.load(gfxCounters, 0) | 0; //Written by the other thread. + var end = gfxCounters[1] | 0; //Written by this thread. + //Check if buffer is full: + if ((end | 0) == (((start | 0) + 2) | 0)) { + //Skip copying a frame out: + return; + } + //Copy samples into the ring buffer: + //Hardcoded for 2 buffers for a triple buffer effect: + gfxBuffers[end & 0x1].set(swizzledFrame); + //Increment the ending position counter by 1: + //Atomic to commit the counter to memory: + Atomics.store(gfxCounters, 1, ((end | 0) + 1) | 0); +} +function waitForVSync() { + //Only breaks if the buffer gets resized: + while ((Atomics.load(gfxCommandCounters, 2) | 0) == 0) { + //Process the current buffer: + processCommands(); + //Main thread calls wake to unblock us here: + Atomics.wait(gfxCounters, 2, 0); + } + //Empty old buffer before getting a new buffer to refer to: + processCommands(); +} +function initializeRenderer(skippingBIOS) { + skippingBIOS = !!skippingBIOS; + renderer = new GameBoyAdvanceGraphicsRendererOffthread(!!skippingBIOS); +} +function assignStaticBuffers(gfxb, gfxc, cmdl) { + gfxBuffers = gfxb; + gfxCounters = gfxc; + gfxLineCounter = cmdl; +} +function assignDynamicBuffers(cmdb, cmdc) { + gfxCommandBuffer = cmdb; + gfxCommandCounters = cmdc; + var gfxCommandBufferLength = gfxCommandBuffer.length | 0; + gfxCommandBufferMask = ((gfxCommandBufferLength | 0) - 1) | 0; +} +function processCommands() { + //Load the counter values: + var start = gfxCommandCounters[0] | 0; //Written by this thread. + var end = Atomics.load(gfxCommandCounters, 1) | 0; //Written by the other thread. + gfxLinesCPU = Atomics.load(gfxLineCounter, 0) | 0; //Keep atomic; IonMonkey thinks this is dead code if it's removed. + //Don't process if nothing to process: + if ((end | 0) == (start | 0)) { + //Buffer is empty: + return; + } + //Dispatch commands: + var startCorrected = start & gfxCommandBufferMask; + var endCorrected = end & gfxCommandBufferMask; + do { + //Read a command: + dispatchCommand(gfxCommandBuffer[startCorrected | 0] | 0, gfxCommandBuffer[startCorrected | 1] | 0); + //Increment by two since we're reading the command code and corresponding data after it: + startCorrected = ((startCorrected | 0) + 2) & gfxCommandBufferMask; + } while ((startCorrected | 0) != (endCorrected | 0)); + //Update the starting position counter to match the end position: + Atomics.store(gfxCommandCounters, 0, end | 0); +} +function dispatchCommand(command, data) { + command = command | 0; + data = data | 0; + /* + We squeeze some address bits as a portion of the command code. + The top bits will be the actual command, the bottom ones will be the address, + unless of course the top bits are zero, for which then it's purely a command code: + */ + switch (command >> 16) { + //IO: + case 0: + dispatchIOCommand(command | 0, data | 0); + break; + //VRAM 16-BIT: + case 1: + renderer.writeVRAM16(command & 0xFFFF, data | 0); + break; + //VRAM 32-BIT: + case 2: + renderer.writeVRAM32(command & 0x7FFF, data | 0); + break; + //Palette 16-BIT: + case 3: + renderer.writePalette16(command & 0x1FF, data | 0); + break; + //Palette 32-BIT: + case 4: + renderer.writePalette32(command & 0xFF, data | 0); + break; + //OAM 16-BIT: + case 5: + renderer.writeOAM16(command & 0x1FF, data | 0); + break; + //OAM 32-BIT: + default: + renderer.writeOAM32(command & 0xFF, data | 0); + } +} +function dispatchIOCommand(command, data) { + command = command | 0; + data = data | 0; + switch (command | 0) { + case 0: + decodeInternalCommand(data | 0); + break; + case 1: + renderer.writeDISPCNT8_0(data | 0); + break; + case 2: + renderer.writeDISPCNT8_1(data | 0); + break; + case 3: + renderer.writeDISPCNT8_2(data | 0); + break; + case 4: + renderer.writeDISPCNT16(data | 0); + break; + case 5: + renderer.writeDISPCNT32(data | 0); + break; + case 6: + renderer.writeBG0CNT8_0(data | 0); + break; + case 7: + renderer.writeBG0CNT8_1(data | 0); + break; + case 8: + renderer.writeBG0CNT16(data | 0); + break; + case 9: + renderer.writeBG1CNT8_0(data | 0); + break; + case 10: + renderer.writeBG1CNT8_1(data | 0); + break; + case 11: + renderer.writeBG1CNT16(data | 0); + break; + case 12: + renderer.writeBG0BG1CNT32(data | 0); + break; + case 13: + renderer.writeBG2CNT8_0(data | 0); + break; + case 14: + renderer.writeBG2CNT8_1(data | 0); + break; + case 15: + renderer.writeBG2CNT16(data | 0); + break; + case 16: + renderer.writeBG3CNT8_0(data | 0); + break; + case 17: + renderer.writeBG3CNT8_1(data | 0); + break; + case 18: + renderer.writeBG3CNT16(data | 0); + break; + case 19: + renderer.writeBG2BG3CNT32(data | 0); + break; + case 20: + renderer.writeBG0HOFS8_0(data | 0); + break; + case 21: + renderer.writeBG0HOFS8_1(data | 0); + break; + case 22: + renderer.writeBG0HOFS16(data | 0); + break; + case 23: + renderer.writeBG0VOFS8_0(data | 0); + break; + case 24: + renderer.writeBG0VOFS8_1(data | 0); + break; + case 25: + renderer.writeBG0VOFS16(data | 0); + break; + case 26: + renderer.writeBG0OFS32(data | 0); + break; + case 27: + renderer.writeBG1HOFS8_0(data | 0); + break; + case 28: + renderer.writeBG1HOFS8_1(data | 0); + break; + case 29: + renderer.writeBG1HOFS16(data | 0); + break; + case 30: + renderer.writeBG1VOFS8_0(data | 0); + break; + case 31: + renderer.writeBG1VOFS8_1(data | 0); + break; + case 32: + renderer.writeBG1VOFS16(data | 0); + break; + case 33: + renderer.writeBG1OFS32(data | 0); + break; + case 34: + renderer.writeBG2HOFS8_0(data | 0); + break; + case 35: + renderer.writeBG2HOFS8_1(data | 0); + break; + case 36: + renderer.writeBG2HOFS16(data | 0); + break; + case 37: + renderer.writeBG2VOFS8_0(data | 0); + break; + case 38: + renderer.writeBG2VOFS8_1(data | 0); + break; + case 39: + renderer.writeBG2VOFS16(data | 0); + break; + case 40: + renderer.writeBG2OFS32(data | 0); + break; + case 41: + renderer.writeBG3HOFS8_0(data | 0); + break; + case 42: + renderer.writeBG3HOFS8_1(data | 0); + break; + case 43: + renderer.writeBG3HOFS16(data | 0); + break; + case 44: + renderer.writeBG3VOFS8_0(data | 0); + break; + case 45: + renderer.writeBG3VOFS8_1(data | 0); + break; + case 46: + renderer.writeBG3VOFS16(data | 0); + break; + case 47: + renderer.writeBG3OFS32(data | 0); + break; + case 48: + renderer.writeBG2PA8_0(data | 0); + break; + case 49: + renderer.writeBG2PA8_1(data | 0); + break; + case 50: + renderer.writeBG2PA16(data | 0); + break; + case 51: + renderer.writeBG2PB8_0(data | 0); + break; + case 52: + renderer.writeBG2PB8_1(data | 0); + break; + case 53: + renderer.writeBG2PB16(data | 0); + break; + case 54: + renderer.writeBG2PAB32(data | 0); + break; + case 55: + renderer.writeBG2PC8_0(data | 0); + break; + case 56: + renderer.writeBG2PC8_1(data | 0); + break; + case 57: + renderer.writeBG2PC16(data | 0); + break; + case 58: + renderer.writeBG2PD8_0(data | 0); + break; + case 59: + renderer.writeBG2PD8_1(data | 0); + break; + case 60: + renderer.writeBG2PD16(data | 0); + break; + case 61: + renderer.writeBG2PCD32(data | 0); + break; + case 62: + renderer.writeBG3PA8_0(data | 0); + break; + case 63: + renderer.writeBG3PA8_1(data | 0); + break; + case 64: + renderer.writeBG3PA16(data | 0); + break; + case 65: + renderer.writeBG3PB8_0(data | 0); + break; + case 66: + renderer.writeBG3PB8_1(data | 0); + break; + case 67: + renderer.writeBG3PB16(data | 0); + break; + case 68: + renderer.writeBG3PAB32(data | 0); + break; + case 69: + renderer.writeBG3PC8_0(data | 0); + break; + case 70: + renderer.writeBG3PC8_1(data | 0); + break; + case 71: + renderer.writeBG3PC16(data | 0); + break; + case 72: + renderer.writeBG3PD8_0(data | 0); + break; + case 73: + renderer.writeBG3PD8_1(data | 0); + break; + case 74: + renderer.writeBG3PD16(data | 0); + break; + case 75: + renderer.writeBG3PCD32(data | 0); + break; + case 76: + renderer.writeBG2X8_0(data | 0); + break; + case 77: + renderer.writeBG2X8_1(data | 0); + break; + case 78: + renderer.writeBG2X8_2(data | 0); + break; + case 79: + renderer.writeBG2X8_3(data | 0); + break; + case 80: + renderer.writeBG2X16_0(data | 0); + break; + case 81: + renderer.writeBG2X16_1(data | 0); + break; + case 82: + renderer.writeBG2X32(data | 0); + break; + case 83: + renderer.writeBG2Y8_0(data | 0); + break; + case 84: + renderer.writeBG2Y8_1(data | 0); + break; + case 85: + renderer.writeBG2Y8_2(data | 0); + break; + case 86: + renderer.writeBG2Y8_3(data | 0); + break; + case 87: + renderer.writeBG2Y16_0(data | 0); + break; + case 88: + renderer.writeBG2Y16_1(data | 0); + break; + case 89: + renderer.writeBG2Y32(data | 0); + break; + case 90: + renderer.writeBG3X8_0(data | 0); + break; + case 91: + renderer.writeBG3X8_1(data | 0); + break; + case 92: + renderer.writeBG3X8_2(data | 0); + break; + case 93: + renderer.writeBG3X8_3(data | 0); + break; + case 94: + renderer.writeBG3X16_0(data | 0); + break; + case 95: + renderer.writeBG3X16_1(data | 0); + break; + case 96: + renderer.writeBG3X32(data | 0); + break; + case 97: + renderer.writeBG3Y8_0(data | 0); + break; + case 98: + renderer.writeBG3Y8_1(data | 0); + break; + case 99: + renderer.writeBG3Y8_2(data | 0); + break; + case 100: + renderer.writeBG3Y8_3(data | 0); + break; + case 101: + renderer.writeBG3Y16_0(data | 0); + break; + case 102: + renderer.writeBG3Y16_1(data | 0); + break; + case 103: + renderer.writeBG3Y32(data | 0); + break; + case 104: + renderer.writeWIN0XCOORDRight8(data | 0); + break; + case 105: + renderer.writeWIN0XCOORDLeft8(data | 0); + break; + case 106: + renderer.writeWIN0XCOORD16(data | 0); + break; + case 107: + renderer.writeWIN1XCOORDRight8(data | 0); + break; + case 108: + renderer.writeWIN1XCOORDLeft8(data | 0); + break; + case 109: + renderer.writeWIN1XCOORD16(data | 0); + break; + case 110: + renderer.writeWINXCOORD32(data | 0); + break; + case 111: + renderer.writeWIN0YCOORDBottom8(data | 0); + break; + case 112: + renderer.writeWIN0YCOORDTop8(data | 0); + break; + case 113: + renderer.writeWIN0YCOORD16(data | 0); + break; + case 114: + renderer.writeWIN1YCOORDBottom8(data | 0); + break; + case 115: + renderer.writeWIN1YCOORDTop8(data | 0); + break; + case 116: + renderer.writeWIN1YCOORD16(data | 0); + break; + case 117: + renderer.writeWINYCOORD32(data | 0); + break; + case 118: + renderer.writeWIN0IN8(data | 0); + break; + case 119: + renderer.writeWIN1IN8(data | 0); + break; + case 120: + renderer.writeWININ16(data | 0); + break; + case 121: + renderer.writeWINOUT8(data | 0); + break; + case 122: + renderer.writeWINOBJIN8(data | 0); + break; + case 123: + renderer.writeWINOUT16(data | 0); + break; + case 124: + renderer.writeWINCONTROL32(data | 0); + break; + case 125: + renderer.writeMOSAIC8_0(data | 0); + break; + case 126: + renderer.writeMOSAIC8_1(data | 0); + break; + case 127: + renderer.writeMOSAIC16(data | 0); + break; + case 128: + renderer.writeBLDCNT8_0(data | 0); + break; + case 129: + renderer.writeBLDCNT8_1(data | 0); + break; + case 130: + renderer.writeBLDCNT16(data | 0); + break; + case 131: + renderer.writeBLDALPHA8_0(data | 0); + break; + case 132: + renderer.writeBLDALPHA8_1(data | 0); + break; + case 133: + renderer.writeBLDALPHA16(data | 0); + break; + case 134: + renderer.writeBLDCNT32(data | 0); + break; + default: + renderer.writeBLDY8(data | 0); + } +} +function decodeInternalCommand(data) { + data = data | 0; + switch (data | 0) { + case 0: + //Check to see if we need to skip rendering to catch up: + if ((((gfxLinesCPU | 0) - (gfxLinesGPU | 0)) | 0) < 480) { + //Render a scanline: + renderer.renderScanLine(); + } + else { + //Update some internal counters to maintain state: + renderer.updateReferenceCounters(); + } + //Clock the scanline counter: + renderer.incrementScanLine(); + //Increment how many scanlines we've received out: + gfxLinesGPU = ((gfxLinesGPU | 0) + 1) | 0; + break; + default: + //Check to see if we need to skip rendering to catch up: + if ((((gfxLinesCPU | 0) - (gfxLinesGPU | 0)) | 0) < 480) { + //Push out a frame of graphics: + renderer.prepareFrame(); + } + } +} diff --git a/public/gfiles/gba/IodineGBA/core/memory/DMA0.js b/public/gfiles/gba/IodineGBA/core/memory/DMA0.js new file mode 100644 index 00000000..0c28b885 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/memory/DMA0.js @@ -0,0 +1,401 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceDMA0(IOCore) { + this.IOCore = IOCore; +} +GameBoyAdvanceDMA0.prototype.DMA_ENABLE_TYPE = [ //DMA Channel 0 Mapping: + 0x1, + 0x2, + 0x4, + 0x40 +]; +GameBoyAdvanceDMA0.prototype.initialize = function () { + this.enabled = 0; + this.pending = 0; + this.source = 0; + this.sourceShadow = 0; + this.destination = 0; + this.destinationShadow = 0; + this.wordCount = 0; + this.wordCountShadow = 0; + this.irqFlagging = 0; + this.dmaType = 0; + this.is32Bit = 0; + this.repeat = 0; + this.sourceControl = 0; + this.destinationControl = 0; + this.DMACore = this.IOCore.dma; + this.memory = this.IOCore.memory; + this.gfxState = this.IOCore.gfxState; + this.irq = this.IOCore.irq; +} +GameBoyAdvanceDMA0.prototype.validateDMASource = function (address) { + address = address | 0; + if ((address | 0) >= 0x2000000) { + if ((address | 0) <= 0x7FFFFFF || (address | 0) >= 0xE000000) { + this.source = address | 0; + } + } +} +GameBoyAdvanceDMA0.prototype.validateDMADestination = function (address) { + address = address | 0; + if ((address | 0) <= 0x7FFFFFF) { + this.destination = address | 0; + } +} +GameBoyAdvanceDMA0.prototype.writeDMASource8_0 = function (data) { + data = data | 0; + var source = this.source & 0xFFFFF00; + data = data & 0xFF; + source = source | data; + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA0.prototype.writeDMASource8_1 = function (data) { + data = data | 0; + var source = this.source & 0xFFF00FF; + data = data & 0xFF; + source = source | (data << 8); + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA0.prototype.writeDMASource8_2 = function (data) { + data = data | 0; + var source = this.source & 0xF00FFFF; + data = data & 0xFF; + source = source | (data << 16); + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA0.prototype.writeDMASource8_3 = function (data) { + data = data | 0; + var source = this.source & 0xFFFFFF; + data = data & 0xF; + source = source | (data << 24); + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA0.prototype.writeDMASource16_0 = function (data) { + data = data | 0; + var source = this.source & 0xFFF0000; + data = data & 0xFFFF; + source = source | data; + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA0.prototype.writeDMASource16_1 = function (data) { + data = data | 0; + var source = this.source & 0xFFFF; + data = data & 0xFFF; + source = source | (data << 16); + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA0.prototype.writeDMASource32 = function (data) { + data = data | 0; + var source = data & 0xFFFFFFF; + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA0.prototype.writeDMADestination8_0 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFFFF00; + data = data & 0xFF; + destination = destination | data; + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA0.prototype.writeDMADestination8_1 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFF00FF; + data = data & 0xFF; + destination = destination | (data << 8); + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA0.prototype.writeDMADestination8_2 = function (data) { + data = data | 0; + var destination = this.destination & 0xF00FFFF; + data = data & 0xFF; + destination = destination | (data << 16); + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA0.prototype.writeDMADestination8_3 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFFFFF; + data = data & 0xF; + destination = destination | (data << 24); + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA0.prototype.writeDMADestination16_0 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFF0000; + data = data & 0xFFFF; + destination = destination | data; + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA0.prototype.writeDMADestination16_1 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFFF; + data = data & 0xFFF; + destination = destination | (data << 16); + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA0.prototype.writeDMADestination32 = function (data) { + data = data | 0; + var destination = data & 0xFFFFFFF; + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA0.prototype.writeDMAWordCount8_0 = function (data) { + data = data | 0; + this.wordCount = this.wordCount & 0x3F00; + data = data & 0xFF; + this.wordCount = this.wordCount | data; +} +GameBoyAdvanceDMA0.prototype.writeDMAWordCount8_1 = function (data) { + data = data | 0; + this.wordCount = this.wordCount & 0xFF; + data = data & 0x3F; + this.wordCount = this.wordCount | (data << 8); +} +GameBoyAdvanceDMA0.prototype.writeDMAWordCount16 = function (data) { + data = data | 0; + this.wordCount = data & 0x3FFF; +} +GameBoyAdvanceDMA0.prototype.writeDMAControl8_0 = function (data) { + data = data | 0; + this.destinationControl = (data >> 5) & 0x3; + this.sourceControl = this.sourceControl & 0x2; + this.sourceControl = this.sourceControl | ((data >> 7) & 0x1); +} +GameBoyAdvanceDMA0.prototype.writeDMAControl8_1 = function (data) { + data = data | 0; + //Spill state machine clocks: + this.IOCore.updateCoreClocking(); + this.sourceControl = (this.sourceControl & 0x1) | ((data & 0x1) << 1); + this.repeat = data & 0x2; + this.is32Bit = data & 0x4; + this.dmaType = (data >> 4) & 0x3; + this.irqFlagging = data & 0x40; + this.enableDMAChannel(data & 0x80); + //Calculate next event: + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceDMA0.prototype.writeDMAControl16 = function (data) { + data = data | 0; + //Spill state machine clocks: + this.IOCore.updateCoreClocking(); + this.destinationControl = (data >> 5) & 0x3; + this.sourceControl = (data >> 7) & 0x3; + this.repeat = (data >> 8) & 0x2; + this.is32Bit = (data >> 8) & 0x4; + this.dmaType = (data >> 12) & 0x3; + this.irqFlagging = (data >> 8) & 0x40; + this.enableDMAChannel(data & 0x8000); + //Calculate next event: + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceDMA0.prototype.writeDMAControl32 = function (data) { + data = data | 0; + this.writeDMAWordCount16(data | 0); + this.writeDMAControl16(data >> 16); +} +GameBoyAdvanceDMA0.prototype.readDMAControl8_0 = function () { + var data = this.destinationControl << 5; + data = data | ((this.sourceControl & 0x1) << 7); + return data | 0; +} +GameBoyAdvanceDMA0.prototype.readDMAControl8_1 = function () { + var data = this.sourceControl >> 1; + data = data | this.repeat; + data = data | this.is32Bit; + data = data | (this.dmaType << 4); + data = data | this.irqFlagging; + if ((this.enabled | 0) != 0) { + data = data | 0x80; + } + return data | 0; +} +GameBoyAdvanceDMA0.prototype.readDMAControl16 = function () { + var data = this.destinationControl << 5; + data = data | (this.sourceControl << 7); + data = data | (this.repeat << 8); + data = data | (this.is32Bit << 8); + data = data | (this.dmaType << 12); + data = data | (this.irqFlagging << 8); + if ((this.enabled | 0) != 0) { + data = data | 0x8000; + } + return data | 0; +} +GameBoyAdvanceDMA0.prototype.getMatchStatus = function () { + return this.enabled & this.pending; +} +GameBoyAdvanceDMA0.prototype.requestDMA = function (DMAType) { + DMAType = DMAType | 0; + if ((this.enabled & DMAType) != 0) { + this.pending = DMAType | 0; + this.DMACore.update(); + } +} +GameBoyAdvanceDMA0.prototype.enableDMAChannel = function (enabled) { + enabled = enabled | 0; + if ((enabled | 0) != 0) { + //If DMA was previously disabled, reload control registers: + if ((this.enabled | 0) == 0) { + //Flag immediate DMA transfers for processing now: + this.pending = 0x1; + //Shadow copy the word count: + this.wordCountShadow = this.wordCount | 0; + //Shadow copy the source address: + this.sourceShadow = this.source | 0; + //Shadow copy the destination address: + this.destinationShadow = this.destination | 0; + } + //DMA type changed: + this.enabled = this.DMA_ENABLE_TYPE[this.dmaType | 0] | 0; + this.pending = this.pending & this.enabled; + } + else { + //DMA Disabled: + this.enabled = 0; + } + //Run some DMA channel activity checks: + this.DMACore.update(); +} +GameBoyAdvanceDMA0.prototype.handleDMACopy = function () { + //Get the addesses: + var source = this.sourceShadow | 0; + var destination = this.destinationShadow | 0; + //Transfer Data: + if ((this.is32Bit | 0) == 4) { + //32-bit Transfer: + this.copy32(source | 0, destination | 0); + } + else { + //16-bit Transfer: + this.copy16(source | 0, destination | 0); + } +} +GameBoyAdvanceDMA0.prototype.copy16 = function (source, destination) { + source = source | 0; + destination = destination | 0; + var data = this.memory.memoryReadDMA16(source | 0) | 0; + this.memory.memoryWriteDMA16(destination | 0, data | 0); + this.decrementWordCount(source | 0, destination | 0, 2); + this.DMACore.updateFetch(data | (data << 16)); +} +GameBoyAdvanceDMA0.prototype.copy32 = function (source, destination) { + source = source | 0; + destination = destination | 0; + var data = this.memory.memoryReadDMA32(source | 0) | 0; + this.memory.memoryWriteDMA32(destination | 0, data | 0); + this.decrementWordCount(source | 0, destination | 0, 4); + this.DMACore.updateFetch(data | 0); +} +GameBoyAdvanceDMA0.prototype.decrementWordCount = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + //Decrement the word count: + var wordCountShadow = ((this.wordCountShadow | 0) - 1) & 0x3FFF; + if ((wordCountShadow | 0) == 0) { + //DMA transfer ended, handle accordingly: + wordCountShadow = this.finalizeDMA(source | 0, destination | 0, transferred | 0) | 0; + } + else { + //Update addresses: + this.incrementDMAAddresses(source | 0, destination | 0, transferred | 0); + } + //Save the new word count: + this.wordCountShadow = wordCountShadow | 0; +} +GameBoyAdvanceDMA0.prototype.finalizeDMA = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + var wordCountShadow = 0; + //Reset pending requests: + this.pending = 0; + //Check Repeat Status: + if ((this.repeat | 0) == 0 || (this.enabled | 0) == 0x1) { + //Disable the enable bit: + this.enabled = 0; + } + else { + //Reload word count: + wordCountShadow = this.wordCount | 0; + } + //Run the DMA channel checks: + this.DMACore.update(); + //Check to see if we should flag for IRQ: + this.checkIRQTrigger(); + //Update addresses: + this.finalDMAAddresses(source | 0, destination | 0, transferred | 0); + return wordCountShadow | 0; +} +GameBoyAdvanceDMA0.prototype.checkIRQTrigger = function () { + if ((this.irqFlagging | 0) != 0) { + this.irq.requestIRQ(0x100); + } +} +GameBoyAdvanceDMA0.prototype.finalDMAAddresses = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + //Update source address: + switch (this.sourceControl | 0) { + case 0: //Increment + case 3: //Forbidden (VBA has it increment) + this.sourceShadow = ((source | 0) + (transferred | 0)) | 0; + break; + case 1: //Decrement + this.sourceShadow = ((source | 0) - (transferred | 0)) | 0; + } + //Update destination address: + switch (this.destinationControl | 0) { + case 0: //Increment + this.destinationShadow = ((destination | 0) + (transferred | 0)) | 0; + break; + case 1: //Decrement + this.destinationShadow = ((destination | 0) - (transferred | 0)) | 0; + break; + case 3: //Reload + this.destinationShadow = this.destination | 0; + } +} +GameBoyAdvanceDMA0.prototype.incrementDMAAddresses = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + //Update source address: + switch (this.sourceControl | 0) { + case 0: //Increment + case 3: //Forbidden (VBA has it increment) + this.sourceShadow = ((source | 0) + (transferred | 0)) | 0; + break; + case 1: + this.sourceShadow = ((source | 0) - (transferred | 0)) | 0; + } + //Update destination address: + switch (this.destinationControl | 0) { + case 0: //Increment + case 3: //Increment + this.destinationShadow = ((destination | 0) + (transferred | 0)) | 0; + break; + case 1: //Decrement + this.destinationShadow = ((destination | 0) - (transferred | 0)) | 0; + } +} +GameBoyAdvanceDMA0.prototype.nextEventTime = function () { + var clocks = 0x7FFFFFFF; + switch (this.enabled | 0) { + //V_BLANK + case 0x2: + clocks = this.gfxState.nextVBlankEventTime() | 0; + break; + //H_BLANK: + case 0x4: + clocks = this.gfxState.nextHBlankDMAEventTime() | 0; + } + return clocks | 0; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/memory/DMA1.js b/public/gfiles/gba/IodineGBA/core/memory/DMA1.js new file mode 100644 index 00000000..52f3d83b --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/memory/DMA1.js @@ -0,0 +1,473 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceDMA1(IOCore) { + this.IOCore = IOCore; +} +GameBoyAdvanceDMA1.prototype.DMA_ENABLE_TYPE = [ //DMA Channel 1 Mapping: + 0x1, + 0x2, + 0x4, + 0x8 +]; +GameBoyAdvanceDMA1.prototype.initialize = function () { + this.enabled = 0; + this.pending = 0; + this.source = 0; + this.sourceShadow = 0; + this.destination = 0; + this.destinationShadow = 0; + this.wordCount = 0; + this.wordCountShadow = 0; + this.irqFlagging = 0; + this.dmaType = 0; + this.is32Bit = 0; + this.repeat = 0; + this.sourceControl = 0; + this.destinationControl = 0; + this.DMACore = this.IOCore.dma; + this.memory = this.IOCore.memory; + this.gfxState = this.IOCore.gfxState; + this.irq = this.IOCore.irq; + this.sound = this.IOCore.sound; + this.wait = this.IOCore.wait; +} +GameBoyAdvanceDMA1.prototype.validateDMASource = function (address) { + address = address | 0; + if ((address | 0) >= 0x2000000) { + this.source = address | 0; + } +} +GameBoyAdvanceDMA1.prototype.validateDMADestination = function (address) { + address = address | 0; + if ((address | 0) <= 0x7FFFFFF) { + this.destination = address | 0; + } +} +GameBoyAdvanceDMA1.prototype.writeDMASource8_0 = function (data) { + data = data | 0; + var source = this.source & 0xFFFFF00; + data = data & 0xFF; + source = source | data; + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA1.prototype.writeDMASource8_1 = function (data) { + data = data | 0; + var source = this.source & 0xFFF00FF; + data = data & 0xFF; + source = source | (data << 8); + this.validateDMASource(source | 0) +} +GameBoyAdvanceDMA1.prototype.writeDMASource8_2 = function (data) { + data = data | 0; + var source = this.source & 0xF00FFFF; + data = data & 0xFF; + source = ource | (data << 16); + this.validateDMASource(source | 0) +} +GameBoyAdvanceDMA1.prototype.writeDMASource8_3 = function (data) { + data = data | 0; + var source = this.source & 0xFFFFFF; + data = data & 0xF; + source = source | (data << 24); + this.validateDMASource(source | 0) +} +GameBoyAdvanceDMA1.prototype.writeDMASource16_0 = function (data) { + data = data | 0; + var source = this.source & 0xFFF0000; + data = data & 0xFFFF; + source = source | data; + this.validateDMASource(source | 0) +} +GameBoyAdvanceDMA1.prototype.writeDMASource16_1 = function (data) { + data = data | 0; + var source = this.source & 0xFFFF; + data = data & 0xFFF; + source = source | (data << 16); + this.validateDMASource(source | 0) +} +GameBoyAdvanceDMA1.prototype.writeDMASource32 = function (data) { + data = data | 0; + var source = data & 0xFFFFFFF; + this.validateDMASource(source | 0) +} +GameBoyAdvanceDMA1.prototype.writeDMADestination8_0 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFFFF00; + data = data & 0xFF; + destination = destination | data; + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA1.prototype.writeDMADestination8_1 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFF00FF; + data = data & 0xFF; + destination = destination | (data << 8); + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA1.prototype.writeDMADestination8_2 = function (data) { + data = data | 0; + var destination = this.destination & 0xF00FFFF; + data = data & 0xFF; + destination = destination | (data << 16); + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA1.prototype.writeDMADestination8_3 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFFFFF; + data = data & 0xF; + destination = destination | (data << 24); + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA1.prototype.writeDMADestination16_0 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFF0000; + data = data & 0xFFFF; + destination = destination | data; + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA1.prototype.writeDMADestination16_1 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFFF; + data = data & 0xFFF; + destination = destination | (data << 16); + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA1.prototype.writeDMADestination32 = function (data) { + data = data | 0; + var destination = data & 0xFFFFFFF; + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA1.prototype.writeDMAWordCount8_0 = function (data) { + data = data | 0; + this.wordCount = this.wordCount & 0x3F00; + data = data & 0xFF; + this.wordCount = this.wordCount | data; +} +GameBoyAdvanceDMA1.prototype.writeDMAWordCount8_1 = function (data) { + data = data | 0; + this.wordCount = this.wordCount & 0xFF; + data = data & 0x3F; + this.wordCount = this.wordCount | (data << 8); +} +GameBoyAdvanceDMA1.prototype.writeDMAWordCount16 = function (data) { + data = data | 0; + this.wordCount = data & 0x3FFF; +} +GameBoyAdvanceDMA1.prototype.writeDMAControl8_0 = function (data) { + data = data | 0; + this.destinationControl = (data >> 5) & 0x3; + this.sourceControl = this.sourceControl & 0x2; + this.sourceControl = this.sourceControl | ((data >> 7) & 0x1); +} +GameBoyAdvanceDMA1.prototype.writeDMAControl8_1 = function (data) { + data = data | 0; + //Spill state machine clocks: + this.IOCore.updateCoreClocking(); + this.sourceControl = (this.sourceControl & 0x1) | ((data & 0x1) << 1); + this.repeat = data & 0x2; + this.is32Bit = data & 0x4; + this.dmaType = (data >> 4) & 0x3; + this.irqFlagging = data & 0x40; + this.enableDMAChannel(data & 0x80); + //Calculate next event: + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceDMA1.prototype.writeDMAControl16 = function (data) { + data = data | 0; + //Spill state machine clocks: + this.IOCore.updateCoreClocking(); + this.destinationControl = (data >> 5) & 0x3; + this.sourceControl = (data >> 7) & 0x3; + this.repeat = (data >> 8) & 0x2; + this.is32Bit = (data >> 8) & 0x4; + this.dmaType = (data >> 12) & 0x3; + this.irqFlagging = (data >> 8) & 0x40; + this.enableDMAChannel(data & 0x8000); + //Calculate next event: + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceDMA1.prototype.writeDMAControl32 = function (data) { + data = data | 0; + this.writeDMAWordCount16(data | 0); + this.writeDMAControl16(data >> 16); +} +GameBoyAdvanceDMA1.prototype.readDMAControl8_0 = function () { + var data = this.destinationControl << 5; + data = data | ((this.sourceControl & 0x1) << 7); + return data | 0; +} +GameBoyAdvanceDMA1.prototype.readDMAControl8_1 = function () { + var data = this.sourceControl >> 1; + data = data | this.repeat; + data = data | this.is32Bit; + data = data | (this.dmaType << 4); + data = data | this.irqFlagging; + if ((this.enabled | 0) != 0) { + data = data | 0x80; + } + return data | 0; +} +GameBoyAdvanceDMA1.prototype.readDMAControl16 = function () { + var data = this.destinationControl << 5; + data = data | (this.sourceControl << 7); + data = data | (this.repeat << 8); + data = data | (this.is32Bit << 8); + data = data | (this.dmaType << 12); + data = data | (this.irqFlagging << 8); + if ((this.enabled | 0) != 0) { + data = data | 0x8000; + } + return data | 0; +} +GameBoyAdvanceDMA1.prototype.getMatchStatus = function () { + return this.enabled & this.pending; +} +GameBoyAdvanceDMA1.prototype.soundFIFOARequest = function () { + this.requestDMA(0x8); +} +GameBoyAdvanceDMA1.prototype.requestDMA = function (DMAType) { + DMAType = DMAType | 0; + if ((this.enabled & DMAType) != 0) { + this.pending = DMAType | 0; + this.DMACore.update(); + } +} +GameBoyAdvanceDMA1.prototype.enableDMAChannel = function (enabled) { + enabled = enabled | 0; + if ((enabled | 0) != 0) { + //If DMA was previously disabled, reload control registers: + if ((this.enabled | 0) == 0) { + switch (this.dmaType | 0) { + case 0x3: + //Direct Sound DMA Hardwired To Wordcount Of 4: + this.wordCountShadow = 0x4; + break; + case 0: + //Flag immediate DMA transfers for processing now: + this.pending = 0x1; + default: + //Shadow copy the word count: + this.wordCountShadow = this.wordCount | 0; + + } + //Shadow copy the source address: + this.sourceShadow = this.source | 0; + //Shadow copy the destination address: + this.destinationShadow = this.destination | 0; + } + //DMA type changed: + this.enabled = this.DMA_ENABLE_TYPE[this.dmaType | 0] | 0; + this.pending = this.pending & this.enabled; + //Assert the FIFO A DMA request signal: + this.sound.checkFIFOAPendingSignal(); + } + else { + //DMA Disabled: + this.enabled = 0; + } + //Run some DMA channel activity checks: + this.DMACore.update(); +} +GameBoyAdvanceDMA1.prototype.handleDMACopy = function () { + //Get the source addess: + var source = this.sourceShadow | 0; + //Transfer Data: + if ((this.enabled | 0) == 0x8) { + //32-bit Transfer: + this.copySound(source | 0); + } + else { + //Get the destination address: + var destination = this.destinationShadow | 0; + if ((this.is32Bit | 0) == 4) { + //32-bit Transfer: + this.copy32(source | 0, destination | 0); + } + else { + //16-bit Transfer: + this.copy16(source | 0, destination | 0); + } + } +} +GameBoyAdvanceDMA1.prototype.copy16 = function (source, destination) { + source = source | 0; + destination = destination | 0; + var data = this.memory.memoryReadDMAFull16(source | 0) | 0; + this.memory.memoryWriteDMA16(destination | 0, data | 0); + this.decrementWordCount(source | 0, destination | 0, 2); + this.DMACore.updateFetch(data | (data << 16)); +} +GameBoyAdvanceDMA1.prototype.copy32 = function (source, destination) { + source = source | 0; + destination = destination | 0; + var data = this.memory.memoryReadDMAFull32(source | 0) | 0; + this.memory.memoryWriteDMA32(destination | 0, data | 0); + this.decrementWordCount(source | 0, destination | 0, 4); + this.DMACore.updateFetch(data | 0); +} +GameBoyAdvanceDMA1.prototype.copySound = function (source) { + source = source | 0; + var data = this.memory.memoryReadDMAFull32(source | 0) | 0; + this.wait.singleClock(); + this.IOCore.updateTimerClocking(); + this.sound.writeFIFOA32(data | 0); + this.soundDMAUpdate(source | 0); + this.DMACore.updateFetch(data | 0); +} +GameBoyAdvanceDMA1.prototype.decrementWordCount = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + //Decrement the word count: + var wordCountShadow = ((this.wordCountShadow | 0) - 1) & 0x3FFF; + if ((wordCountShadow | 0) == 0) { + //DMA transfer ended, handle accordingly: + wordCountShadow = this.finalizeDMA(source | 0, destination | 0, transferred | 0) | 0; + } + else { + //Update addresses: + this.incrementDMAAddresses(source | 0, destination | 0, transferred | 0); + } + //Save the new word count: + this.wordCountShadow = wordCountShadow | 0; +} +GameBoyAdvanceDMA1.prototype.soundDMAUpdate = function (source) { + source = source | 0; + //Decrement the word count: + this.wordCountShadow = ((this.wordCountShadow | 0) - 1) & 0x3FFF; + if ((this.wordCountShadow | 0) == 0) { + //DMA transfer ended, handle accordingly: + //Reset pending requests: + this.pending = 0; + //Check Repeat Status: + if ((this.repeat | 0) == 0) { + //Disable the enable bit: + this.enabled = 0; + } + else { + //Repeating the dma: + //Direct Sound DMA Hardwired To Wordcount Of 4: + this.wordCountShadow = 0x4; + } + //Assert the FIFO A DMA request signal: + this.sound.checkFIFOAPendingSignal(); + //Run the DMA channel checks: + this.DMACore.update(); + //Check to see if we should flag for IRQ: + this.checkIRQTrigger(); + } + //Update source address: + switch (this.sourceControl | 0) { + case 0: //Increment + case 3: //Forbidden (VBA has it increment) + this.sourceShadow = ((source | 0) + 4) | 0; + break; + case 1: + this.sourceShadow = ((source | 0) - 4) | 0; + } +} +GameBoyAdvanceDMA1.prototype.finalizeDMA = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + var wordCountShadow = 0; + //Reset pending requests: + this.pending = 0; + //Check Repeat Status: + if ((this.repeat | 0) == 0 || (this.enabled | 0) == 0x1) { + //Disable the enable bit: + this.enabled = 0; + } + else { + //Repeating the dma: + //Reload word count: + wordCountShadow = this.wordCount | 0; + } + //Assert the FIFO A DMA request signal: + this.sound.checkFIFOAPendingSignal(); + //Run the DMA channel checks: + this.DMACore.update(); + //Check to see if we should flag for IRQ: + this.checkIRQTrigger(); + //Update addresses: + this.finalDMAAddresses(source | 0, destination | 0, transferred | 0); + return wordCountShadow | 0; +} +GameBoyAdvanceDMA1.prototype.checkIRQTrigger = function () { + if ((this.irqFlagging | 0) != 0) { + this.irq.requestIRQ(0x200); + } +} +GameBoyAdvanceDMA1.prototype.finalDMAAddresses = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + //Update source address: + switch (this.sourceControl | 0) { + case 0: //Increment + case 3: //Forbidden (VBA has it increment) + this.sourceShadow = ((source | 0) + (transferred | 0)) | 0; + break; + case 1: //Decrement + this.sourceShadow = ((source | 0) - (transferred | 0)) | 0; + } + //Update destination address: + switch (this.destinationControl | 0) { + case 0: //Increment + this.destinationShadow = ((destination | 0) + (transferred | 0)) | 0; + break; + case 1: //Decrement + this.destinationShadow = ((destination | 0) - (transferred | 0)) | 0; + break; + case 3: //Reload + this.destinationShadow = this.destination | 0; + } +} +GameBoyAdvanceDMA1.prototype.incrementDMAAddresses = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + //Update source address: + switch (this.sourceControl | 0) { + case 0: //Increment + case 3: //Forbidden (VBA has it increment) + this.sourceShadow = ((source | 0) + (transferred | 0)) | 0; + break; + case 1: + this.sourceShadow = ((source | 0) - (transferred | 0)) | 0; + } + //Update destination address: + switch (this.destinationControl | 0) { + case 0: //Increment + case 3: //Increment + this.destinationShadow = ((destination | 0) + (transferred | 0)) | 0; + break; + case 1: //Decrement + this.destinationShadow = ((destination | 0) - (transferred | 0)) | 0; + } +} +GameBoyAdvanceDMA1.prototype.nextEventTime = function () { + var clocks = 0x7FFFFFFF; + switch (this.enabled | 0) { + //V_BLANK + case 0x2: + clocks = this.gfxState.nextVBlankEventTime() | 0; + break; + //H_BLANK: + case 0x4: + clocks = this.gfxState.nextHBlankDMAEventTime() | 0; + break; + //FIFO_A: + case 0x8: + clocks = this.sound.nextFIFOAEventTime() | 0; + } + return clocks | 0; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/memory/DMA2.js b/public/gfiles/gba/IodineGBA/core/memory/DMA2.js new file mode 100644 index 00000000..206c6b59 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/memory/DMA2.js @@ -0,0 +1,472 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceDMA2(IOCore) { + this.IOCore = IOCore; +} +GameBoyAdvanceDMA2.prototype.DMA_ENABLE_TYPE = [ //DMA Channel 2 Mapping: + 0x1, + 0x2, + 0x4, + 0x10 +]; +GameBoyAdvanceDMA2.prototype.initialize = function () { + this.enabled = 0; + this.pending = 0; + this.source = 0; + this.sourceShadow = 0; + this.destination = 0; + this.destinationShadow = 0; + this.wordCount = 0; + this.wordCountShadow = 0; + this.irqFlagging = 0; + this.dmaType = 0; + this.is32Bit = 0; + this.repeat = 0; + this.sourceControl = 0; + this.destinationControl = 0; + this.DMACore = this.IOCore.dma; + this.memory = this.IOCore.memory; + this.gfxState = this.IOCore.gfxState; + this.irq = this.IOCore.irq; + this.sound = this.IOCore.sound; + this.wait = this.IOCore.wait; +} +GameBoyAdvanceDMA2.prototype.validateDMASource = function (address) { + address = address | 0; + if ((address | 0) >= 0x2000000) { + this.source = address | 0; + } +} +GameBoyAdvanceDMA2.prototype.validateDMADestination = function (address) { + address = address | 0; + if ((address | 0) <= 0x7FFFFFF) { + this.destination = address | 0; + } +} +GameBoyAdvanceDMA2.prototype.writeDMASource8_0 = function (data) { + data = data | 0; + var source = this.source & 0xFFFFF00; + data = data & 0xFF; + source = source | data; + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA2.prototype.writeDMASource8_1 = function (data) { + data = data | 0; + var source = this.source & 0xFFF00FF; + data = data & 0xFF; + source = source | (data << 8); + this.validateDMASource(source | 0) +} +GameBoyAdvanceDMA2.prototype.writeDMASource8_2 = function (data) { + data = data | 0; + var source = this.source & 0xF00FFFF; + data = data & 0xFF; + source = ource | (data << 16); + this.validateDMASource(source | 0) +} +GameBoyAdvanceDMA2.prototype.writeDMASource8_3 = function (data) { + data = data | 0; + var source = this.source & 0xFFFFFF; + data = data & 0xF; + source = source | (data << 24); + this.validateDMASource(source | 0) +} +GameBoyAdvanceDMA2.prototype.writeDMASource16_0 = function (data) { + data = data | 0; + var source = this.source & 0xFFF0000; + data = data & 0xFFFF; + source = source | data; + this.validateDMASource(source | 0) +} +GameBoyAdvanceDMA2.prototype.writeDMASource16_1 = function (data) { + data = data | 0; + var source = this.source & 0xFFFF; + data = data & 0xFFF; + source = source | (data << 16); + this.validateDMASource(source | 0) +} +GameBoyAdvanceDMA2.prototype.writeDMASource32 = function (data) { + data = data | 0; + var source = data & 0xFFFFFFF; + this.validateDMASource(source | 0) +} +GameBoyAdvanceDMA2.prototype.writeDMADestination8_0 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFFFF00; + data = data & 0xFF; + destination = destination | data; + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA2.prototype.writeDMADestination8_1 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFF00FF; + data = data & 0xFF; + destination = destination | (data << 8); + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA2.prototype.writeDMADestination8_2 = function (data) { + data = data | 0; + var destination = this.destination & 0xF00FFFF; + data = data & 0xFF; + destination = destination | (data << 16); + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA2.prototype.writeDMADestination8_3 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFFFFF; + data = data & 0xF; + destination = destination | (data << 24); + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA2.prototype.writeDMADestination16_0 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFF0000; + data = data & 0xFFFF; + destination = destination | data; + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA2.prototype.writeDMADestination16_1 = function (data) { + data = data | 0; + var destination = this.destination & 0xFFFF; + data = data & 0xFFF; + destination = destination | (data << 16); + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA2.prototype.writeDMADestination32 = function (data) { + data = data | 0; + var destination = data & 0xFFFFFFF; + this.validateDMADestination(destination | 0); +} +GameBoyAdvanceDMA2.prototype.writeDMAWordCount8_0 = function (data) { + data = data | 0; + this.wordCount = this.wordCount & 0x3F00; + data = data & 0xFF; + this.wordCount = this.wordCount | data; +} +GameBoyAdvanceDMA2.prototype.writeDMAWordCount8_1 = function (data) { + data = data | 0; + this.wordCount = this.wordCount & 0xFF; + data = data & 0x3F; + this.wordCount = this.wordCount | (data << 8); +} +GameBoyAdvanceDMA2.prototype.writeDMAWordCount16 = function (data) { + data = data | 0; + this.wordCount = data & 0x3FFF; +} +GameBoyAdvanceDMA2.prototype.writeDMAControl8_0 = function (data) { + data = data | 0; + this.destinationControl = (data >> 5) & 0x3; + this.sourceControl = this.sourceControl & 0x2; + this.sourceControl = this.sourceControl | ((data >> 7) & 0x1); +} +GameBoyAdvanceDMA2.prototype.writeDMAControl8_1 = function (data) { + data = data | 0; + //Spill state machine clocks: + this.IOCore.updateCoreClocking(); + this.sourceControl = (this.sourceControl & 0x1) | ((data & 0x1) << 1); + this.repeat = data & 0x2; + this.is32Bit = data & 0x4; + this.dmaType = (data >> 4) & 0x3; + this.irqFlagging = data & 0x40; + this.enableDMAChannel(data & 0x80); + //Calculate next event: + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceDMA2.prototype.writeDMAControl16 = function (data) { + data = data | 0; + //Spill state machine clocks: + this.IOCore.updateCoreClocking(); + this.destinationControl = (data >> 5) & 0x3; + this.sourceControl = (data >> 7) & 0x3; + this.repeat = (data >> 8) & 0x2; + this.is32Bit = (data >> 8) & 0x4; + this.dmaType = (data >> 12) & 0x3; + this.irqFlagging = (data >> 8) & 0x40; + this.enableDMAChannel(data & 0x8000); + //Calculate next event: + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceDMA2.prototype.writeDMAControl32 = function (data) { + data = data | 0; + this.writeDMAWordCount16(data | 0); + this.writeDMAControl16(data >> 16); +} +GameBoyAdvanceDMA2.prototype.readDMAControl8_0 = function () { + var data = this.destinationControl << 5; + data = data | ((this.sourceControl & 0x1) << 7); + return data | 0; +} +GameBoyAdvanceDMA2.prototype.readDMAControl8_1 = function () { + var data = this.sourceControl >> 1; + data = data | this.repeat; + data = data | this.is32Bit; + data = data | (this.dmaType << 4); + data = data | this.irqFlagging; + if ((this.enabled | 0) != 0) { + data = data | 0x80; + } + return data | 0; +} +GameBoyAdvanceDMA2.prototype.readDMAControl16 = function () { + var data = this.destinationControl << 5; + data = data | (this.sourceControl << 7); + data = data | (this.repeat << 8); + data = data | (this.is32Bit << 8); + data = data | (this.dmaType << 12); + data = data | (this.irqFlagging << 8); + if ((this.enabled | 0) != 0) { + data = data | 0x8000; + } + return data | 0; +} +GameBoyAdvanceDMA2.prototype.getMatchStatus = function () { + return this.enabled & this.pending; +} +GameBoyAdvanceDMA2.prototype.soundFIFOBRequest = function () { + this.requestDMA(0x10); +} +GameBoyAdvanceDMA2.prototype.requestDMA = function (DMAType) { + DMAType = DMAType | 0; + if ((this.enabled & DMAType) != 0) { + this.pending = DMAType | 0; + this.DMACore.update(); + } +} +GameBoyAdvanceDMA2.prototype.enableDMAChannel = function (enabled) { + enabled = enabled | 0; + if ((enabled | 0) != 0) { + //If DMA was previously disabled, reload control registers: + if ((this.enabled | 0) == 0) { + switch (this.dmaType | 0) { + case 0x3: + //Direct Sound DMA Hardwired To Wordcount Of 4: + this.wordCountShadow = 0x4; + break; + case 0: + //Flag immediate DMA transfers for processing now: + this.pending = 0x1; + default: + //Shadow copy the word count: + this.wordCountShadow = this.wordCount | 0; + } + //Shadow copy the source address: + this.sourceShadow = this.source | 0; + //Shadow copy the destination address: + this.destinationShadow = this.destination | 0; + } + //DMA type changed: + this.enabled = this.DMA_ENABLE_TYPE[this.dmaType | 0] | 0; + //this.pending = this.pending & this.enabled; + //Assert the FIFO A DMA request signal: + this.sound.checkFIFOBPendingSignal(); + } + else { + //DMA Disabled: + this.enabled = 0; + } + //Run some DMA channel activity checks: + this.DMACore.update(); +} +GameBoyAdvanceDMA2.prototype.handleDMACopy = function () { + //Get the source addess: + var source = this.sourceShadow | 0; + //Transfer Data: + if ((this.enabled | 0) == 0x10) { + //32-bit Transfer: + this.copySound(source | 0); + } + else { + //Get the destination address: + var destination = this.destinationShadow | 0; + if ((this.is32Bit | 0) == 4) { + //32-bit Transfer: + this.copy32(source | 0, destination | 0); + } + else { + //16-bit Transfer: + this.copy16(source | 0, destination | 0); + } + } +} +GameBoyAdvanceDMA2.prototype.copy16 = function (source, destination) { + source = source | 0; + destination = destination | 0; + var data = this.memory.memoryReadDMAFull16(source | 0) | 0; + this.memory.memoryWriteDMA16(destination | 0, data | 0); + this.decrementWordCount(source | 0, destination | 0, 2); + this.DMACore.updateFetch(data | (data << 16)); +} +GameBoyAdvanceDMA2.prototype.copy32 = function (source, destination) { + source = source | 0; + destination = destination | 0; + var data = this.memory.memoryReadDMAFull32(source | 0) | 0; + this.memory.memoryWriteDMA32(destination | 0, data | 0); + this.decrementWordCount(source | 0, destination | 0, 4); + this.DMACore.updateFetch(data | 0); +} +GameBoyAdvanceDMA2.prototype.copySound = function (source) { + source = source | 0; + var data = this.memory.memoryReadDMAFull32(source | 0) | 0; + this.wait.singleClock(); + this.IOCore.updateTimerClocking(); + this.sound.writeFIFOB32(data | 0); + this.soundDMAUpdate(source | 0); + this.DMACore.updateFetch(data | 0); +} +GameBoyAdvanceDMA2.prototype.decrementWordCount = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + //Decrement the word count: + var wordCountShadow = ((this.wordCountShadow | 0) - 1) & 0x3FFF; + if ((wordCountShadow | 0) == 0) { + //DMA transfer ended, handle accordingly: + wordCountShadow = this.finalizeDMA(source | 0, destination | 0, transferred | 0) | 0; + } + else { + //Update addresses: + this.incrementDMAAddresses(source | 0, destination | 0, transferred | 0); + } + //Save the new word count: + this.wordCountShadow = wordCountShadow | 0; +} +GameBoyAdvanceDMA2.prototype.soundDMAUpdate = function (source) { + source = source | 0; + //Decrement the word count: + this.wordCountShadow = ((this.wordCountShadow | 0) - 1) & 0x3FFF; + if ((this.wordCountShadow | 0) == 0) { + //DMA transfer ended, handle accordingly: + //Reset pending requests: + this.pending = 0; + //Check Repeat Status: + if ((this.repeat | 0) == 0) { + //Disable the enable bit: + this.enabled = 0; + } + else { + //Repeating the dma: + //Direct Sound DMA Hardwired To Wordcount Of 4: + this.wordCountShadow = 0x4; + } + //Assert the FIFO B DMA request signal: + this.sound.checkFIFOBPendingSignal(); + //Run the DMA channel checks: + this.DMACore.update(); + //Check to see if we should flag for IRQ: + this.checkIRQTrigger(); + } + //Update source address: + switch (this.sourceControl | 0) { + case 0: //Increment + case 3: //Forbidden (VBA has it increment) + this.sourceShadow = ((source | 0) + 4) | 0; + break; + case 1: + this.sourceShadow = ((source | 0) - 4) | 0; + } +} +GameBoyAdvanceDMA2.prototype.finalizeDMA = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + var wordCountShadow = 0; + //Reset pending requests: + this.pending = 0; + //Check Repeat Status: + if ((this.repeat | 0) == 0 || (this.enabled | 0) == 0x1) { + //Disable the enable bit: + this.enabled = 0; + } + else { + //Repeating the dma: + //Reload word count: + wordCountShadow = this.wordCount | 0; + } + //Assert the FIFO B DMA request signal: + this.sound.checkFIFOBPendingSignal(); + //Run the DMA channel checks: + this.DMACore.update(); + //Check to see if we should flag for IRQ: + this.checkIRQTrigger(); + //Update addresses: + this.finalDMAAddresses(source | 0, destination | 0, transferred | 0); + return wordCountShadow | 0; +} +GameBoyAdvanceDMA2.prototype.checkIRQTrigger = function () { + if ((this.irqFlagging | 0) != 0) { + this.irq.requestIRQ(0x400); + } +} +GameBoyAdvanceDMA2.prototype.finalDMAAddresses = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + //Update source address: + switch (this.sourceControl | 0) { + case 0: //Increment + case 3: //Forbidden (VBA has it increment) + this.sourceShadow = ((source | 0) + (transferred | 0)) | 0; + break; + case 1: //Decrement + this.sourceShadow = ((source | 0) - (transferred | 0)) | 0; + } + //Update destination address: + switch (this.destinationControl | 0) { + case 0: //Increment + this.destinationShadow = ((destination | 0) + (transferred | 0)) | 0; + break; + case 1: //Decrement + this.destinationShadow = ((destination | 0) - (transferred | 0)) | 0; + break; + case 3: //Reload + this.destinationShadow = this.destination | 0; + } +} +GameBoyAdvanceDMA2.prototype.incrementDMAAddresses = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + //Update source address: + switch (this.sourceControl | 0) { + case 0: //Increment + case 3: //Forbidden (VBA has it increment) + this.sourceShadow = ((source | 0) + (transferred | 0)) | 0; + break; + case 1: + this.sourceShadow = ((source | 0) - (transferred | 0)) | 0; + } + //Update destination address: + switch (this.destinationControl | 0) { + case 0: //Increment + case 3: //Increment + this.destinationShadow = ((destination | 0) + (transferred | 0)) | 0; + break; + case 1: //Decrement + this.destinationShadow = ((destination | 0) - (transferred | 0)) | 0; + } +} +GameBoyAdvanceDMA2.prototype.nextEventTime = function () { + var clocks = 0x7FFFFFFF; + switch (this.enabled | 0) { + //V_BLANK + case 0x2: + clocks = this.gfxState.nextVBlankEventTime() | 0; + break; + //H_BLANK: + case 0x4: + clocks = this.gfxState.nextHBlankDMAEventTime() | 0; + break; + //FIFO_B: + case 0x10: + clocks = this.sound.nextFIFOBEventTime() | 0; + } + return clocks | 0; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/memory/DMA3.js b/public/gfiles/gba/IodineGBA/core/memory/DMA3.js new file mode 100644 index 00000000..aea67594 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/memory/DMA3.js @@ -0,0 +1,418 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceDMA3(IOCore) { + this.IOCore = IOCore; +} +GameBoyAdvanceDMA3.prototype.DMA_ENABLE_TYPE = [ //DMA Channel 3 Mapping: + 0x1, + 0x2, + 0x4, + 0x20 +]; +GameBoyAdvanceDMA3.prototype.initialize = function () { + this.enabled = 0; + this.pending = 0; + this.source = 0; + this.sourceShadow = 0; + this.destination = 0; + this.destinationShadow = 0; + this.wordCount = 0; + this.wordCountShadow = 0; + this.irqFlagging = 0; + this.dmaType = 0; + this.is32Bit = 0; + this.repeat = 0; + this.sourceControl = 0; + this.destinationControl = 0; + this.gamePakDMA = 0; + this.displaySyncEnableDelay = 0; + this.DMACore = this.IOCore.dma; + this.memory = this.IOCore.memory; + this.gfxState = this.IOCore.gfxState; + this.irq = this.IOCore.irq; +} +GameBoyAdvanceDMA3.prototype.validateDMASource = function (address) { + address = address | 0; + if ((address | 0) >= 0x2000000) { + this.source = address | 0; + } +} +GameBoyAdvanceDMA3.prototype.writeDMASource8_0 = function (data) { + data = data | 0; + var source = this.source & 0xFFFFF00; + data = data & 0xFF; + source = source | data; + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA3.prototype.writeDMASource8_1 = function (data) { + data = data | 0; + var source = this.source & 0xFFF00FF; + data = data & 0xFF; + source = source | (data << 8); + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA3.prototype.writeDMASource8_2 = function (data) { + data = data | 0; + var source = this.source & 0xF00FFFF; + data = data & 0xFF; + source = source | (data << 16); + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA3.prototype.writeDMASource8_3 = function (data) { + data = data | 0; + var source = this.source & 0xFFFFFF; + data = data & 0xF; + source = source | (data << 24); + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA3.prototype.writeDMASource16_0 = function (data) { + data = data | 0; + var source = this.source & 0xFFF0000; + data = data & 0xFFFF; + source = source | data; + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA3.prototype.writeDMASource16_1 = function (data) { + data = data | 0; + var source = this.source & 0xFFFF; + data = data & 0xFFF; + source = source | (data << 16); + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA3.prototype.writeDMASource32 = function (data) { + data = data | 0; + var source = data & 0xFFFFFFF; + this.validateDMASource(source | 0); +} +GameBoyAdvanceDMA3.prototype.writeDMADestination8_0 = function (data) { + data = data | 0; + this.destination = this.destination & 0xFFFFF00; + data = data & 0xFF; + this.destination = this.destination | data; +} +GameBoyAdvanceDMA3.prototype.writeDMADestination8_1 = function (data) { + data = data | 0; + this.destination = this.destination & 0xFFF00FF; + data = data & 0xFF; + this.destination = this.destination | (data << 8); +} +GameBoyAdvanceDMA3.prototype.writeDMADestination8_2 = function (data) { + data = data | 0; + this.destination = this.destination & 0xF00FFFF; + data = data & 0xFF; + this.destination = this.destination | (data << 16); +} +GameBoyAdvanceDMA3.prototype.writeDMADestination8_3 = function (data) { + data = data | 0; + this.destination = this.destination & 0xFFFFFF; + data = data & 0xF; + this.destination = this.destination | (data << 24); +} +GameBoyAdvanceDMA3.prototype.writeDMADestination16_0 = function (data) { + data = data | 0; + this.destination = this.destination & 0xFFF0000; + data = data & 0xFFFF; + this.destination = this.destination | data; +} +GameBoyAdvanceDMA3.prototype.writeDMADestination16_1 = function (data) { + data = data | 0; + this.destination = this.destination & 0xFFFF; + data = data & 0xFFF; + this.destination = this.destination | (data << 16); +} +GameBoyAdvanceDMA3.prototype.writeDMADestination32 = function (data) { + data = data | 0; + this.destination = data & 0xFFFFFFF; +} +GameBoyAdvanceDMA3.prototype.writeDMAWordCount8_0 = function (data) { + data = data | 0; + this.wordCount = this.wordCount & 0xFF00; + data = data & 0xFF; + this.wordCount = this.wordCount | data; +} +GameBoyAdvanceDMA3.prototype.writeDMAWordCount8_1 = function (data) { + data = data | 0; + this.wordCount = this.wordCount & 0xFF; + data = data & 0xFF; + this.wordCount = this.wordCount | (data << 8); +} +GameBoyAdvanceDMA3.prototype.writeDMAWordCount16 = function (data) { + data = data | 0; + this.wordCount = data & 0xFFFF; +} +GameBoyAdvanceDMA3.prototype.writeDMAControl8_0 = function (data) { + data = data | 0; + this.destinationControl = (data >> 5) & 0x3; + this.sourceControl = this.sourceControl & 0x2; + this.sourceControl = this.sourceControl | ((data >> 7) & 0x1); +} +GameBoyAdvanceDMA3.prototype.writeDMAControl8_1 = function (data) { + data = data | 0; + //Spill state machine clocks: + this.IOCore.updateCoreClocking(); + this.sourceControl = (this.sourceControl & 0x1) | ((data & 0x1) << 1); + this.repeat = data & 0x2; + this.is32Bit = data & 0x4; + this.gamePakDMA = data & 0x8; + this.dmaType = (data >> 4) & 0x3; + this.irqFlagging = data & 0x40; + this.enableDMAChannel(data & 0x80); + //Calculate next event: + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceDMA3.prototype.writeDMAControl16 = function (data) { + data = data | 0; + //Spill state machine clocks: + this.IOCore.updateCoreClocking(); + this.destinationControl = (data >> 5) & 0x3; + this.sourceControl = (data >> 7) & 0x3; + this.repeat = (data >> 8) & 0x2; + this.is32Bit = (data >> 8) & 0x4; + this.gamePakDMA = (data >> 8) & 0x8; + this.dmaType = (data >> 12) & 0x3; + this.irqFlagging = (data >> 8) & 0x40; + this.enableDMAChannel(data & 0x8000); + //Calculate next event: + this.IOCore.updateCoreEventTime(); +} +GameBoyAdvanceDMA3.prototype.writeDMAControl32 = function (data) { + data = data | 0; + this.writeDMAWordCount16(data | 0); + this.writeDMAControl16(data >> 16); +} +GameBoyAdvanceDMA3.prototype.readDMAControl8_0 = function () { + var data = this.destinationControl << 5; + data = data | ((this.sourceControl & 0x1) << 7); + return data | 0; +} +GameBoyAdvanceDMA3.prototype.readDMAControl8_1 = function () { + var data = this.sourceControl >> 1; + data = data | this.repeat; + data = data | this.is32Bit; + data = data | this.gamePakDMA; + data = data | (this.dmaType << 4); + data = data | this.irqFlagging; + if ((this.enabled | 0) != 0) { + data = data | 0x80; + } + return data | 0; +} +GameBoyAdvanceDMA3.prototype.readDMAControl16 = function () { + var data = this.destinationControl << 5; + data = data | (this.sourceControl << 7); + data = data | (this.repeat << 8); + data = data | (this.is32Bit << 8); + data = data | (this.gamePakDMA << 8); + data = data | (this.dmaType << 12); + data = data | (this.irqFlagging << 8); + if ((this.enabled | 0) != 0) { + data = data | 0x8000; + } + return data | 0; +} +GameBoyAdvanceDMA3.prototype.getMatchStatus = function () { + return this.enabled & this.pending; +} +GameBoyAdvanceDMA3.prototype.gfxDisplaySyncRequest = function () { + this.requestDMA(0x20 ^ this.displaySyncEnableDelay); +} +GameBoyAdvanceDMA3.prototype.gfxDisplaySyncEnableCheck = function () { + //Reset the display sync & reassert DMA enable line: + if ((this.enabled | 0) == 0x20) { + if ((this.displaySyncEnableDelay | 0) == 0x20) { + this.displaySyncEnableDelay = 0; + } + else { + this.enabled = 0; + this.DMACore.update(); + } + } +} +GameBoyAdvanceDMA3.prototype.requestDMA = function (DMAType) { + DMAType = DMAType | 0; + if ((this.enabled & DMAType) != 0) { + this.pending = DMAType | 0; + this.DMACore.update(); + } +} +GameBoyAdvanceDMA3.prototype.enableDMAChannel = function (enabled) { + enabled = enabled | 0; + if ((enabled | 0) != 0) { + //If DMA was previously disabled, reload control registers: + if ((this.enabled | 0) == 0) { + switch (this.dmaType | 0) { + case 0: + //Flag immediate DMA transfers for processing now: + this.pending = 0x1; + break; + case 0x3: + //Trigger display sync DMA shadow enable and auto-check on line 162: + this.displaySyncEnableDelay = 0x20; + } + //Shadow copy the word count: + this.wordCountShadow = this.wordCount | 0; + //Shadow copy the source address: + this.sourceShadow = this.source | 0; + //Shadow copy the destination address: + this.destinationShadow = this.destination | 0; + } + //DMA type changed: + this.enabled = this.DMA_ENABLE_TYPE[this.dmaType | 0] | 0; + this.pending = this.pending & this.enabled; + } + else { + //DMA Disabled: + this.enabled = 0; + } + //Run some DMA channel activity checks: + this.DMACore.update(); +} +GameBoyAdvanceDMA3.prototype.handleDMACopy = function () { + //Get the addesses: + var source = this.sourceShadow | 0; + var destination = this.destinationShadow | 0; + //Transfer Data: + if ((this.is32Bit | 0) == 4) { + //32-bit Transfer: + this.copy32(source | 0, destination | 0); + } + else { + //16-bit Transfer: + this.copy16(source | 0, destination | 0); + } +} +GameBoyAdvanceDMA3.prototype.copy16 = function (source, destination) { + source = source | 0; + destination = destination | 0; + var data = this.memory.memoryReadDMAFull16(source | 0) | 0; + this.memory.memoryWriteDMAFull16(destination | 0, data | 0); + this.decrementWordCount(source | 0, destination | 0, 2); + this.DMACore.updateFetch(data | (data << 16)); +} +GameBoyAdvanceDMA3.prototype.copy32 = function (source, destination) { + source = source | 0; + destination = destination | 0; + var data = this.memory.memoryReadDMAFull32(source | 0) | 0; + this.memory.memoryWriteDMAFull32(destination | 0, data | 0); + this.decrementWordCount(source | 0, destination | 0, 4); + this.DMACore.updateFetch(data | 0); +} +GameBoyAdvanceDMA3.prototype.decrementWordCount = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + //Decrement the word count: + var wordCountShadow = ((this.wordCountShadow | 0) - 1) & 0xFFFF; + if ((wordCountShadow | 0) == 0) { + //DMA transfer ended, handle accordingly: + wordCountShadow = this.finalizeDMA(source | 0, destination | 0, transferred | 0) | 0; + } + else { + //Update addresses: + this.incrementDMAAddresses(source | 0, destination | 0, transferred | 0); + } + //Save the new word count: + this.wordCountShadow = wordCountShadow | 0; +} +GameBoyAdvanceDMA3.prototype.finalizeDMA = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + var wordCountShadow = 0; + //Reset pending requests: + this.pending = 0; + //Check Repeat Status: + if ((this.repeat | 0) == 0 || (this.enabled | 0) == 0x1) { + //Disable the enable bit: + this.enabled = 0; + } + else { + //Reload word count: + wordCountShadow = this.wordCount | 0; + } + //Run the DMA channel checks: + this.DMACore.update(); + //Check to see if we should flag for IRQ: + this.checkIRQTrigger(); + //Update addresses: + this.finalDMAAddresses(source | 0, destination | 0, transferred | 0); + return wordCountShadow | 0; +} +GameBoyAdvanceDMA3.prototype.checkIRQTrigger = function () { + if ((this.irqFlagging | 0) != 0) { + this.irq.requestIRQ(0x800); + } +} +GameBoyAdvanceDMA3.prototype.finalDMAAddresses = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + //Update source address: + switch (this.sourceControl | 0) { + case 0: //Increment + case 3: //Forbidden (VBA has it increment) + this.sourceShadow = ((source | 0) + (transferred | 0)) | 0; + break; + case 1: //Decrement + this.sourceShadow = ((source | 0) - (transferred | 0)) | 0; + } + //Update destination address: + switch (this.destinationControl | 0) { + case 0: //Increment + this.destinationShadow = ((destination | 0) + (transferred | 0)) | 0; + break; + case 1: //Decrement + this.destinationShadow = ((destination | 0) - (transferred | 0)) | 0; + break; + case 3: //Reload + this.destinationShadow = this.destination | 0; + } +} +GameBoyAdvanceDMA3.prototype.incrementDMAAddresses = function (source, destination, transferred) { + source = source | 0; + destination = destination | 0; + transferred = transferred | 0; + //Update source address: + switch (this.sourceControl | 0) { + case 0: //Increment + case 3: //Forbidden (VBA has it increment) + this.sourceShadow = ((source | 0) + (transferred | 0)) | 0; + break; + case 1: + this.sourceShadow = ((source | 0) - (transferred | 0)) | 0; + } + //Update destination address: + switch (this.destinationControl | 0) { + case 0: //Increment + case 3: //Increment + this.destinationShadow = ((destination | 0) + (transferred | 0)) | 0; + break; + case 1: //Decrement + this.destinationShadow = ((destination | 0) - (transferred | 0)) | 0; + } +} +GameBoyAdvanceDMA3.prototype.nextEventTime = function () { + var clocks = 0x7FFFFFFF; + switch (this.enabled | 0) { + //V_BLANK + case 0x2: + clocks = this.gfxState.nextVBlankEventTime() | 0; + break; + //H_BLANK: + case 0x4: + clocks = this.gfxState.nextHBlankDMAEventTime() | 0; + break; + //DISPLAY_SYNC: + case 0x20: + clocks = this.gfxState.nextDisplaySyncEventTime(this.displaySyncEnableDelay | 0) | 0; + } + return clocks | 0; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/sound/Channel1.js b/public/gfiles/gba/IodineGBA/core/sound/Channel1.js new file mode 100644 index 00000000..0099bf92 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/sound/Channel1.js @@ -0,0 +1,298 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceChannel1Synth(sound) { + this.sound = sound; + this.currentSampleLeft = 0; + this.currentSampleRight = 0; + this.SweepFault = false; + this.lastTimeSweep = 0; + this.timeSweep = 0; + this.frequencySweepDivider = 0; + this.decreaseSweep = false; + this.nr11 = 0; + this.CachedDuty = 0xF0000000; + this.totalLength = 0x40; + this.nr12 = 0; + this.envelopeVolume = 0; + this.frequency = 0; + this.FrequencyTracker = 0x8000; + this.nr14 = 0; + this.consecutive = true; + this.ShadowFrequency = 0x8000; + this.canPlay = false; + this.Enabled = 0; + this.envelopeSweeps = 0; + this.envelopeSweepsLast = -1; + this.FrequencyCounter = 0; + this.DutyTracker = 0; + this.Swept = false; + this.leftEnable = 0; + this.rightEnable = 0; +} +GameBoyAdvanceChannel1Synth.prototype.disabled = function () { + //Clear NR10: + this.nr10 = 0; + this.SweepFault = false; + this.lastTimeSweep = 0; + this.timeSweep = 0; + this.frequencySweepDivider = 0; + this.decreaseSweep = false; + //Clear NR11: + this.nr11 = 0; + this.CachedDuty = 0xF0000000; + this.totalLength = 0x40; + //Clear NR12: + this.nr12 = 0; + this.envelopeVolume = 0; + //Clear NR13: + this.frequency = 0; + this.FrequencyTracker = 0x8000; + //Clear NR14: + this.nr14 = 0; + this.consecutive = true; + this.ShadowFrequency = 0x8000; + this.canPlay = false; + this.Enabled = 0; + this.envelopeSweeps = 0; + this.envelopeSweepsLast = -1; + this.FrequencyCounter = 0; + this.DutyTracker = 0; +} +GameBoyAdvanceChannel1Synth.prototype.clockAudioLength = function () { + if ((this.totalLength | 0) > 1) { + this.totalLength = ((this.totalLength | 0) - 1) | 0; + } + else if ((this.totalLength | 0) == 1) { + this.totalLength = 0; + this.enableCheck(); + this.sound.unsetNR52(0xFE); //Channel #1 On Flag Off + } +} +GameBoyAdvanceChannel1Synth.prototype.enableCheck = function () { + if ((this.consecutive || (this.totalLength | 0) > 0) && !this.SweepFault && this.canPlay) { + this.Enabled = 0xF; + } + else { + this.Enabled = 0; + } +} +GameBoyAdvanceChannel1Synth.prototype.volumeEnableCheck = function () { + this.canPlay = ((this.nr12 | 0) > 7); + this.enableCheck(); +} +GameBoyAdvanceChannel1Synth.prototype.outputLevelCache = function () { + var duty = this.CachedDuty >> (this.DutyTracker | 0); + var envelopeVolume = this.envelopeVolume & this.Enabled & duty; + this.currentSampleLeft = this.leftEnable & envelopeVolume; + this.currentSampleRight = this.rightEnable & envelopeVolume; +} +GameBoyAdvanceChannel1Synth.prototype.setChannelOutputEnable = function (data) { + data = data | 0; + //Set by NR51 handler: + this.rightEnable = (data << 31) >> 31; + this.leftEnable = (data << 27) >> 31; +} +GameBoyAdvanceChannel1Synth.prototype.clockAudioSweep = function () { + //Channel 1: + if (!this.SweepFault && (this.timeSweep | 0) > 0) { + this.timeSweep = ((this.timeSweep | 0) - 1) | 0 + if ((this.timeSweep | 0) == 0) { + this.runAudioSweep(); + } + } +} +GameBoyAdvanceChannel1Synth.prototype.runAudioSweep = function () { + //Channel 1: + if ((this.lastTimeSweep | 0) > 0) { + if ((this.frequencySweepDivider | 0) > 0) { + this.Swept = true; + if (this.decreaseSweep) { + this.ShadowFrequency = ((this.ShadowFrequency | 0) - (this.ShadowFrequency >> (this.frequencySweepDivider | 0))) | 0; + this.frequency = this.ShadowFrequency & 0x7FF; + this.FrequencyTracker = (0x800 - (this.frequency | 0)) << 4; + } + else { + this.ShadowFrequency = ((this.ShadowFrequency | 0) + (this.ShadowFrequency >> (this.frequencySweepDivider | 0))) | 0; + this.frequency = this.ShadowFrequency | 0; + if ((this.ShadowFrequency | 0) <= 0x7FF) { + this.FrequencyTracker = (0x800 - (this.frequency | 0)) << 4; + //Run overflow check twice: + if ((((this.ShadowFrequency | 0) + (this.ShadowFrequency >> (this.frequencySweepDivider | 0))) | 0) > 0x7FF) { + this.SweepFault = true; + this.enableCheck(); + this.sound.unsetNR52(0xFE); //Channel #1 On Flag Off + } + } + else { + this.frequency &= 0x7FF; + this.SweepFault = true; + this.enableCheck(); + this.sound.unsetNR52(0xFE); //Channel #1 On Flag Off + } + } + this.timeSweep = this.lastTimeSweep | 0; + } + else { + //Channel has sweep disabled and timer becomes a length counter: + this.SweepFault = true; + this.enableCheck(); + } + } +} +GameBoyAdvanceChannel1Synth.prototype.audioSweepPerformDummy = function () { + //Channel 1: + if ((this.frequencySweepDivider | 0) > 0) { + if (!this.decreaseSweep) { + var channel1ShadowFrequency = ((this.ShadowFrequency | 0) + (this.ShadowFrequency >> (this.frequencySweepDivider | 0))) | 0; + if ((channel1ShadowFrequency | 0) <= 0x7FF) { + //Run overflow check twice: + if ((((channel1ShadowFrequency | 0) + (channel1ShadowFrequency >> (this.frequencySweepDivider | 0))) | 0) > 0x7FF) { + this.SweepFault = true; + this.enableCheck(); + this.sound.unsetNR52(0xFE); //Channel #1 On Flag Off + } + } + else { + this.SweepFault = true; + this.enableCheck(); + this.sound.unsetNR52(0xFE); //Channel #1 On Flag Off + } + } + } +} +GameBoyAdvanceChannel1Synth.prototype.clockAudioEnvelope = function () { + if ((this.envelopeSweepsLast | 0) > -1) { + if ((this.envelopeSweeps | 0) > 0) { + this.envelopeSweeps = ((this.envelopeSweeps | 0) - 1) | 0; + } + else { + if (!this.envelopeType) { + if ((this.envelopeVolume | 0) > 0) { + this.envelopeVolume = ((this.envelopeVolume | 0) - 1) | 0; + this.envelopeSweeps = this.envelopeSweepsLast | 0; + } + else { + this.envelopeSweepsLast = -1; + } + } + else if ((this.envelopeVolume | 0) < 0xF) { + this.envelopeVolume = ((this.envelopeVolume | 0) + 1) | 0; + this.envelopeSweeps = this.envelopeSweepsLast | 0; + } + else { + this.envelopeSweepsLast = -1; + } + } + } +} +GameBoyAdvanceChannel1Synth.prototype.computeAudioChannel = function () { + if ((this.FrequencyCounter | 0) == 0) { + this.FrequencyCounter = this.FrequencyTracker | 0; + this.DutyTracker = ((this.DutyTracker | 0) + 4) & 0x1C; + } +} +GameBoyAdvanceChannel1Synth.prototype.readSOUND1CNT8_0 = function () { + //NR10: + return this.nr10 | 0; +} +GameBoyAdvanceChannel1Synth.prototype.writeSOUND1CNT8_0 = function (data) { + data = data | 0; + //NR10: + if (this.decreaseSweep && (data & 0x08) == 0) { + if (this.Swept) { + this.SweepFault = true; + } + } + this.lastTimeSweep = (data & 0x70) >> 4; + this.frequencySweepDivider = data & 0x07; + this.decreaseSweep = ((data & 0x08) != 0); + this.nr10 = data & 0xFF; + this.enableCheck(); +} +GameBoyAdvanceChannel1Synth.prototype.readSOUND1CNT8_2 = function () { + //NR11: + return this.nr11 | 0; +} +GameBoyAdvanceChannel1Synth.prototype.writeSOUND1CNT8_2 = function (data) { + data = data | 0; + //NR11: + switch ((data >> 6) & 0x3) { + case 0: + this.CachedDuty = 0xF0000000; + break; + case 1: + this.CachedDuty = 0xF000000F; + break; + case 2: + this.CachedDuty = 0xFFF0000F; + break; + default: + this.CachedDuty = 0x0FFFFFF0; + } + this.totalLength = (0x40 - (data & 0x3F)) | 0; + this.nr11 = data & 0xFF; + this.enableCheck(); +} +GameBoyAdvanceChannel1Synth.prototype.readSOUND1CNT8_3 = function () { + //NR12: + return this.nr12 | 0; +} +GameBoyAdvanceChannel1Synth.prototype.writeSOUND1CNT8_3 = function (data) { + data = data | 0; + //NR12: + this.envelopeType = ((data & 0x08) != 0); + this.nr12 = data & 0xFF; + this.volumeEnableCheck(); +} +GameBoyAdvanceChannel1Synth.prototype.writeSOUND1CNT_X0 = function (data) { + data = data | 0; + //NR13: + this.frequency = (this.frequency & 0x700) | (data & 0xFF); + this.FrequencyTracker = (0x800 - (this.frequency | 0)) << 4; +} +GameBoyAdvanceChannel1Synth.prototype.readSOUND1CNTX8 = function () { + //NR14: + return this.nr14 | 0; +} +GameBoyAdvanceChannel1Synth.prototype.writeSOUND1CNT_X1 = function (data) { + data = data | 0; + //NR14: + this.consecutive = ((data & 0x40) == 0); + this.frequency = ((data & 0x7) << 8) | (this.frequency & 0xFF); + this.FrequencyTracker = (0x800 - (this.frequency | 0)) << 4; + if ((data & 0x80) != 0) { + //Reload nr10: + this.timeSweep = this.lastTimeSweep | 0; + this.Swept = false; + //Reload nr12: + this.envelopeVolume = this.nr12 >> 4; + this.envelopeSweepsLast = ((this.nr12 & 0x7) - 1) | 0; + if ((this.totalLength | 0) == 0) { + this.totalLength = 0x40; + } + if ((this.lastTimeSweep | 0) > 0 || (this.frequencySweepDivider | 0) > 0) { + this.sound.setNR52(0x1); + } + else { + this.sound.unsetNR52(0xFE); + } + if ((data & 0x40) != 0) { + this.sound.setNR52(0x1); + } + this.ShadowFrequency = this.frequency | 0; + //Reset frequency overflow check + frequency sweep type check: + this.SweepFault = false; + //Supposed to run immediately: + this.audioSweepPerformDummy(); + } + this.enableCheck(); + this.nr14 = data & 0xFF; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/sound/Channel2.js b/public/gfiles/gba/IodineGBA/core/sound/Channel2.js new file mode 100644 index 00000000..43d47486 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/sound/Channel2.js @@ -0,0 +1,186 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceChannel2Synth(sound) { + this.sound = sound; + this.currentSampleLeft = 0; + this.currentSampleRight = 0; + this.CachedDuty = 0xF0000000; + this.totalLength = 0x40; + this.envelopeVolume = 0; + this.frequency = 0; + this.FrequencyTracker = 0x8000; + this.consecutive = true; + this.ShadowFrequency = 0x8000; + this.canPlay = false; + this.Enabled = 0; + this.envelopeSweeps = 0; + this.envelopeSweepsLast = -1; + this.FrequencyCounter = 0; + this.DutyTracker = 0; + this.leftEnable = 0; + this.rightEnable = 0; + this.nr21 = 0; + this.nr22 = 0; + this.nr23 = 0; + this.nr24 = 0; +} +GameBoyAdvanceChannel2Synth.prototype.disabled = function () { + //Clear NR21: + this.nr21 = 0; + this.CachedDuty = 0xF0000000; + this.totalLength = 0x40; + //Clear NR22: + this.nr22 = 0; + this.envelopeVolume = 0; + //Clear NR23: + this.nr23 = 0; + this.frequency = 0; + this.FrequencyTracker = 0x8000; + //Clear NR24: + this.nr24 = 0; + this.consecutive = true; + this.canPlay = false; + this.Enabled = 0; + this.envelopeSweeps = 0; + this.envelopeSweepsLast = -1; + this.FrequencyCounter = 0; + this.DutyTracker = 0; +} +GameBoyAdvanceChannel2Synth.prototype.clockAudioLength = function () { + if ((this.totalLength | 0) > 1) { + this.totalLength = ((this.totalLength | 0) - 1) | 0; + } + else if ((this.totalLength | 0) == 1) { + this.totalLength = 0; + this.enableCheck(); + this.sound.unsetNR52(0xFD); //Channel #2 On Flag Off + } +} +GameBoyAdvanceChannel2Synth.prototype.clockAudioEnvelope = function () { + if ((this.envelopeSweepsLast | 0) > -1) { + if ((this.envelopeSweeps | 0) > 0) { + this.envelopeSweeps = ((this.envelopeSweeps | 0) - 1) | 0; + } + else { + if (!this.envelopeType) { + if ((this.envelopeVolume | 0) > 0) { + this.envelopeVolume = ((this.envelopeVolume | 0) - 1) | 0; + this.envelopeSweeps = this.envelopeSweepsLast | 0; + } + else { + this.envelopeSweepsLast = -1; + } + } + else if ((this.envelopeVolume | 0) < 0xF) { + this.envelopeVolume = ((this.envelopeVolume | 0) + 1) | 0; + this.envelopeSweeps = this.envelopeSweepsLast | 0; + } + else { + this.envelopeSweepsLast = -1; + } + } + } +} +GameBoyAdvanceChannel2Synth.prototype.computeAudioChannel = function () { + if ((this.FrequencyCounter | 0) == 0) { + this.FrequencyCounter = this.FrequencyTracker | 0; + this.DutyTracker = ((this.DutyTracker | 0) + 4) & 0x1C; + } +} +GameBoyAdvanceChannel2Synth.prototype.enableCheck = function () { + if ((this.consecutive || (this.totalLength | 0) > 0) && this.canPlay) { + this.Enabled = 0xF; + } + else { + this.Enabled = 0; + } +} +GameBoyAdvanceChannel2Synth.prototype.volumeEnableCheck = function () { + this.canPlay = ((this.nr22 | 0) > 7); + this.enableCheck(); +} +GameBoyAdvanceChannel2Synth.prototype.outputLevelCache = function () { + var duty = this.CachedDuty >> (this.DutyTracker | 0); + var envelopeVolume = this.envelopeVolume & this.Enabled & duty; + this.currentSampleLeft = this.leftEnable & envelopeVolume; + this.currentSampleRight = this.rightEnable & envelopeVolume; +} +GameBoyAdvanceChannel2Synth.prototype.setChannelOutputEnable = function (data) { + data = data | 0; + //Set by NR51 handler: + this.rightEnable = (data << 30) >> 31; + this.leftEnable = (data << 26) >> 31; +} +GameBoyAdvanceChannel2Synth.prototype.readSOUND2CNT_L0 = function () { + //NR21: + return this.nr21 | 0; +} +GameBoyAdvanceChannel2Synth.prototype.writeSOUND2CNT_L0 = function (data) { + data = data | 0; + //NR21: + switch ((data >> 6) & 0x3) { + case 0: + this.CachedDuty = 0xF0000000; + break; + case 1: + this.CachedDuty = 0xF000000F; + break; + case 2: + this.CachedDuty = 0xFFF0000F; + break; + default: + this.CachedDuty = 0x0FFFFFF0; + } + this.totalLength = (0x40 - (data & 0x3F)) | 0; + this.nr21 = data & 0xFF; + this.enableCheck(); +} +GameBoyAdvanceChannel2Synth.prototype.readSOUND2CNT_L1 = function () { + //NR22: + return this.nr22 | 0; +} +GameBoyAdvanceChannel2Synth.prototype.writeSOUND2CNT_L1 = function (data) { + data = data | 0; + //NR22: + this.envelopeType = ((data & 0x08) != 0); + this.nr22 = data & 0xFF; + this.volumeEnableCheck(); +} +GameBoyAdvanceChannel2Synth.prototype.writeSOUND2CNT_H0 = function (data) { + data = data | 0; + //NR23: + this.frequency = (this.frequency & 0x700) | (data & 0xFF); + this.FrequencyTracker = (0x800 - (this.frequency | 0)) << 4; +} +GameBoyAdvanceChannel2Synth.prototype.readSOUND2CNT_H = function () { + //NR24: + return this.nr24 | 0; +} +GameBoyAdvanceChannel2Synth.prototype.writeSOUND2CNT_H1 = function (data) { + data = data | 0; + //NR24: + if ((data & 0x80) != 0) { + //Reload nr22: + this.envelopeVolume = this.nr22 >> 4; + this.envelopeSweepsLast = ((this.nr22 & 0x7) - 1) | 0; + if ((this.totalLength | 0) == 0) { + this.totalLength = 0x40; + } + if ((data & 0x40) != 0) { + this.sound.setNR52(0x2); //Channel #1 On Flag Off + } + } + this.consecutive = ((data & 0x40) == 0x0); + this.frequency = ((data & 0x7) << 8) | (this.frequency & 0xFF); + this.FrequencyTracker = (0x800 - (this.frequency | 0)) << 4; + this.nr24 = data & 0xFF; + this.enableCheck(); +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/sound/Channel3.js b/public/gfiles/gba/IodineGBA/core/sound/Channel3.js new file mode 100644 index 00000000..24482d95 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/sound/Channel3.js @@ -0,0 +1,301 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceChannel3Synth(sound) { + this.sound = sound; + this.currentSampleLeft = 0; + this.currentSampleRight = 0; + this.lastSampleLookup = 0; + this.canPlay = false; + this.WAVERAMBankSpecified = 0; + this.WAVERAMBankAccessed = 0x20; + this.WaveRAMBankSize = 0x1F; + this.totalLength = 0x100; + this.patternType = 4; + this.frequency = 0; + this.FrequencyPeriod = 0x4000; + this.consecutive = true; + this.Enabled = 0; + this.leftEnable = 0; + this.rightEnable = 0; + this.nr30 = 0; + this.nr31 = 0; + this.nr32 = 0; + this.nr33 = 0; + this.nr34 = 0; + this.cachedSample = 0; + this.PCM = getInt8Array(0x40); + this.PCM16 = getUint16View(this.PCM); + this.PCM32 = getInt32View(this.PCM); + this.WAVERAM8 = getUint8Array(0x20); + this.WAVERAM16 = getUint16View(this.WAVERAM8); + this.WAVERAM32 = getInt32View(this.WAVERAM8); +} +GameBoyAdvanceChannel3Synth.prototype.disabled = function () { + //Clear NR30: + this.nr30 = 0; + this.lastSampleLookup = 0; + this.canPlay = false; + this.WAVERAMBankSpecified = 0; + this.WAVERAMBankAccessed = 0x20; + this.WaveRAMBankSize = 0x1F; + //Clear NR31: + this.totalLength = 0x100; + //Clear NR32: + this.nr32 = 0; + this.patternType = 4; + //Clear NR33: + this.nr33 = 0; + this.frequency = 0; + this.FrequencyPeriod = 0x4000; + //Clear NR34: + this.nr34 = 0; + this.consecutive = true; + this.Enabled = 0; + this.counter = 0; +} +if (typeof Math.imul == "function") { + //Math.imul found, insert the optimized path in: + GameBoyAdvanceChannel3Synth.prototype.updateCache = function () { + if ((this.patternType | 0) != 3) { + this.cachedSample = this.PCM[this.lastSampleLookup | 0] >> (this.patternType | 0); + } + else { + this.cachedSample = Math.imul(this.PCM[this.lastSampleLookup | 0] | 0, 3) >> 2; + } + this.outputLevelCache(); + } +} +else { + //Math.imul not found, use the compatibility method: + GameBoyAdvanceChannel3Synth.prototype.updateCache = function () { + if ((this.patternType | 0) != 3) { + this.cachedSample = this.PCM[this.lastSampleLookup | 0] >> (this.patternType | 0); + } + else { + this.cachedSample = (this.PCM[this.lastSampleLookup | 0] * 0.75) | 0; + } + this.outputLevelCache(); + } +} +GameBoyAdvanceChannel3Synth.prototype.outputLevelCache = function () { + var cachedSample = this.cachedSample & this.Enabled; + this.currentSampleLeft = this.leftEnable & cachedSample; + this.currentSampleRight = this.rightEnable & cachedSample; +} +GameBoyAdvanceChannel3Synth.prototype.setChannelOutputEnable = function (data) { + data = data | 0; + //Set by NR51 handler: + this.rightEnable = (data << 29) >> 31; + this.leftEnable = (data << 25) >> 31; +} +GameBoyAdvanceChannel3Synth.prototype.readWAVE8 = function (address) { + address = ((address | 0) + (this.WAVERAMBankAccessed >> 1)) | 0; + return this.WAVERAM8[address | 0] | 0; +} +if (__LITTLE_ENDIAN__) { + GameBoyAdvanceChannel3Synth.prototype.writeWAVE8 = function (address, data) { + address = address | 0; + data = data | 0; + address = ((address | 0) + (this.WAVERAMBankAccessed >> 1)) | 0; + this.WAVERAM8[address | 0] = data & 0xFF; + var temp = ((data >> 4) & 0xF); + temp = temp | ((data & 0xF) << 8); + this.PCM16[address | 0] = temp | 0; + } + GameBoyAdvanceChannel3Synth.prototype.writeWAVE16 = function (address, data) { + address = address | 0; + data = data | 0; + address = ((address | 0) + (this.WAVERAMBankAccessed >> 2)) | 0; + this.WAVERAM16[address | 0] = data & 0xFFFF; + var temp = ((data >> 4) & 0xF); + temp = temp | ((data & 0xF) << 8); + temp = temp | ((data & 0xF000) << 4); + temp = temp | ((data & 0xF00) << 16); + this.PCM32[address | 0] = temp | 0; + } + GameBoyAdvanceChannel3Synth.prototype.writeWAVE32 = function (address, data) { + address = address | 0; + data = data | 0; + address = ((address | 0) + (this.WAVERAMBankAccessed >> 3)) | 0; + this.WAVERAM32[address | 0] = data | 0; + var temp = (data >> 4) & 0xF; + temp = temp | ((data & 0xF) << 8); + temp = temp | ((data & 0xF000) << 4); + temp = temp | ((data & 0xF00) << 16); + address = address << 1; + this.PCM32[address | 0] = temp | 0; + temp = (data >> 20) & 0xF; + temp = temp | ((data >> 8) & 0xF00); + temp = temp | ((data >> 12) & 0xF0000); + temp = temp | (data & 0xF000000); + this.PCM32[address | 1] = temp | 0; + } + GameBoyAdvanceChannel3Synth.prototype.readWAVE16 = function (address) { + address = ((address | 0) + (this.WAVERAMBankAccessed >> 2)) | 0; + return this.WAVERAM16[address | 0] | 0; + } + GameBoyAdvanceChannel3Synth.prototype.readWAVE32 = function (address) { + address = ((address | 0) + (this.WAVERAMBankAccessed >> 3)) | 0; + return this.WAVERAM32[address | 0] | 0; + } +} +else { + GameBoyAdvanceChannel3Synth.prototype.writeWAVE8 = function (address, data) { + address += this.WAVERAMBankAccessed >> 1; + this.WAVERAM8[address] = data & 0xFF; + address <<= 1; + this.PCM[address] = (data >> 4) & 0xF; + this.PCM[address | 1] = data & 0xF; + } + GameBoyAdvanceChannel3Synth.prototype.writeWAVE16 = function (address, data) { + address += this.WAVERAMBankAccessed >> 2; + address <<= 1; + this.WAVERAM8[address] = data & 0xFF; + this.WAVERAM8[address | 1] = (data >> 8) & 0xFF; + address <<= 1; + this.PCM[address] = (data >> 4) & 0xF; + this.PCM[address | 1] = data & 0xF; + this.PCM[address | 2] = (data >> 12) & 0xF; + this.PCM[address | 3] = (data >> 8) & 0xF; + } + GameBoyAdvanceChannel3Synth.prototype.writeWAVE32 = function (address, data) { + address += this.WAVERAMBankAccessed >> 3; + address <<= 2; + this.WAVERAM8[address] = data & 0xFF; + this.WAVERAM8[address | 1] = (data >> 8) & 0xFF; + this.WAVERAM8[address | 2] = (data >> 16) & 0xFF; + this.WAVERAM8[address | 3] = data >>> 24; + address <<= 1; + this.PCM[address] = (data >> 4) & 0xF; + this.PCM[address | 1] = data & 0xF; + this.PCM[address | 2] = (data >> 12) & 0xF; + this.PCM[address | 3] = (data >> 8) & 0xF; + this.PCM[address | 4] = (data >> 20) & 0xF; + this.PCM[address | 5] = (data >> 16) & 0xF; + this.PCM[address | 6] = data >>> 28; + this.PCM[address | 7] = (data >> 24) & 0xF; + } + GameBoyAdvanceChannel3Synth.prototype.readWAVE16 = function (address) { + address += this.WAVERAMBankAccessed >> 1; + return (this.WAVERAM8[address] | (this.WAVERAM8[address | 1] << 8)); + } + GameBoyAdvanceChannel3Synth.prototype.readWAVE32 = function (address) { + address += this.WAVERAMBankAccessed >> 1; + return (this.WAVERAM8[address] | (this.WAVERAM8[address | 1] << 8) | + (this.WAVERAM8[address | 2] << 16) | (this.WAVERAM8[address | 3] << 24)); + } +} +GameBoyAdvanceChannel3Synth.prototype.enableCheck = function () { + if (/*this.canPlay && */(this.consecutive || (this.totalLength | 0) > 0)) { + this.Enabled = 0xF; + } + else { + this.Enabled = 0; + } +} +GameBoyAdvanceChannel3Synth.prototype.clockAudioLength = function () { + if ((this.totalLength | 0) > 1) { + this.totalLength = ((this.totalLength | 0) - 1) | 0; + } + else if ((this.totalLength | 0) == 1) { + this.totalLength = 0; + this.enableCheck(); + this.sound.unsetNR52(0xFB); //Channel #3 On Flag Off + } +} +GameBoyAdvanceChannel3Synth.prototype.computeAudioChannel = function () { + if ((this.counter | 0) == 0) { + if (this.canPlay) { + this.lastSampleLookup = (((this.lastSampleLookup | 0) + 1) & this.WaveRAMBankSize) | this.WAVERAMBankSpecified; + } + this.counter = this.FrequencyPeriod | 0; + } +} + +GameBoyAdvanceChannel3Synth.prototype.readSOUND3CNT_L = function () { + //NR30: + return this.nr30 | 0; +} +GameBoyAdvanceChannel3Synth.prototype.writeSOUND3CNT_L = function (data) { + data = data | 0; + //NR30: + if (!this.canPlay && (data & 0x80) != 0) { + this.lastSampleLookup = 0; + } + this.canPlay = ((data & 0x80) != 0); + this.WaveRAMBankSize = (data & 0x20) | 0x1F; + this.WAVERAMBankSpecified = ((data & 0x40) >> 1) ^ (data & 0x20); + this.WAVERAMBankAccessed = ((data & 0x40) >> 1) ^ 0x20; + if (this.canPlay && (this.nr30 & 0x80) != 0 && !this.consecutive) { + this.sound.setNR52(0x4); + } + this.nr30 = data & 0xFF; +} +GameBoyAdvanceChannel3Synth.prototype.writeSOUND3CNT_H0 = function (data) { + data = data | 0; + //NR31: + this.totalLength = (0x100 - (data & 0xFF)) | 0; + this.enableCheck(); +} +GameBoyAdvanceChannel3Synth.prototype.readSOUND3CNT_H = function () { + //NR32: + return this.nr32 | 0; +} +GameBoyAdvanceChannel3Synth.prototype.writeSOUND3CNT_H1 = function (data) { + data = data | 0; + //NR32: + data = data & 0xFF; + switch (data >> 5) { + case 0: + this.patternType = 4; + break; + case 1: + this.patternType = 0; + break; + case 2: + this.patternType = 1; + break; + case 3: + this.patternType = 2; + break; + default: + this.patternType = 3; + } + this.nr32 = data | 0; +} +GameBoyAdvanceChannel3Synth.prototype.writeSOUND3CNT_X0 = function (data) { + data = data | 0; + //NR33: + this.frequency = (this.frequency & 0x700) | (data & 0xFF); + this.FrequencyPeriod = (0x800 - (this.frequency | 0)) << 3; +} +GameBoyAdvanceChannel3Synth.prototype.readSOUND3CNT_X = function () { + //NR34: + return this.nr34 | 0; +} +GameBoyAdvanceChannel3Synth.prototype.writeSOUND3CNT_X1 = function (data) { + data = data | 0; + //NR34: + if ((data & 0x80) != 0) { + if ((this.totalLength | 0) == 0) { + this.totalLength = 0x100; + } + this.lastSampleLookup = 0; + if ((data & 0x40) != 0) { + this.sound.setNR52(0x4); + } + } + this.consecutive = ((data & 0x40) == 0x0); + this.frequency = ((data & 0x7) << 8) | (this.frequency & 0xFF); + this.FrequencyPeriod = (0x800 - (this.frequency | 0)) << 3; + this.enableCheck(); + this.nr34 = data & 0xFF; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/sound/Channel4.js b/public/gfiles/gba/IodineGBA/core/sound/Channel4.js new file mode 100644 index 00000000..410ee49f --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/sound/Channel4.js @@ -0,0 +1,243 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceChannel4Synth(sound) { + this.sound = sound; + this.currentSampleLeft = 0; + this.currentSampleRight = 0; + this.totalLength = 0x40; + this.envelopeVolume = 0; + this.FrequencyPeriod = 32; + this.lastSampleLookup = 0; + this.BitRange = 0x7FFF; + this.VolumeShifter = 15; + this.currentVolume = 0; + this.consecutive = true; + this.envelopeSweeps = 0; + this.envelopeSweepsLast = -1; + this.canPlay = false; + this.Enabled = 0; + this.counter = 0; + this.leftEnable = 0; + this.rightEnable = 0; + this.nr42 = 0; + this.nr43 = 0; + this.nr44 = 0; + this.cachedSample = 0; + this.intializeWhiteNoise(); + this.noiseSampleTable = this.LSFR15Table; +} +GameBoyAdvanceChannel4Synth.prototype.intializeWhiteNoise = function () { + //Noise Sample Tables: + var randomFactor = 1; + //15-bit LSFR Cache Generation: + this.LSFR15Table = getInt8Array(0x80000); + var LSFR = 0x7FFF; //Seed value has all its bits set. + var LSFRShifted = 0x3FFF; + for (var index = 0; index < 0x8000; ++index) { + //Normalize the last LSFR value for usage: + randomFactor = 1 - (LSFR & 1); //Docs say it's the inverse. + //Cache the different volume level results: + this.LSFR15Table[0x08000 | index] = randomFactor; + this.LSFR15Table[0x10000 | index] = randomFactor * 0x2; + this.LSFR15Table[0x18000 | index] = randomFactor * 0x3; + this.LSFR15Table[0x20000 | index] = randomFactor * 0x4; + this.LSFR15Table[0x28000 | index] = randomFactor * 0x5; + this.LSFR15Table[0x30000 | index] = randomFactor * 0x6; + this.LSFR15Table[0x38000 | index] = randomFactor * 0x7; + this.LSFR15Table[0x40000 | index] = randomFactor * 0x8; + this.LSFR15Table[0x48000 | index] = randomFactor * 0x9; + this.LSFR15Table[0x50000 | index] = randomFactor * 0xA; + this.LSFR15Table[0x58000 | index] = randomFactor * 0xB; + this.LSFR15Table[0x60000 | index] = randomFactor * 0xC; + this.LSFR15Table[0x68000 | index] = randomFactor * 0xD; + this.LSFR15Table[0x70000 | index] = randomFactor * 0xE; + this.LSFR15Table[0x78000 | index] = randomFactor * 0xF; + //Recompute the LSFR algorithm: + LSFRShifted = LSFR >> 1; + LSFR = LSFRShifted | (((LSFRShifted ^ LSFR) & 0x1) << 14); + } + //7-bit LSFR Cache Generation: + this.LSFR7Table = getInt8Array(0x800); + LSFR = 0x7F; //Seed value has all its bits set. + for (index = 0; index < 0x80; ++index) { + //Normalize the last LSFR value for usage: + randomFactor = 1 - (LSFR & 1); //Docs say it's the inverse. + //Cache the different volume level results: + this.LSFR7Table[0x080 | index] = randomFactor; + this.LSFR7Table[0x100 | index] = randomFactor * 0x2; + this.LSFR7Table[0x180 | index] = randomFactor * 0x3; + this.LSFR7Table[0x200 | index] = randomFactor * 0x4; + this.LSFR7Table[0x280 | index] = randomFactor * 0x5; + this.LSFR7Table[0x300 | index] = randomFactor * 0x6; + this.LSFR7Table[0x380 | index] = randomFactor * 0x7; + this.LSFR7Table[0x400 | index] = randomFactor * 0x8; + this.LSFR7Table[0x480 | index] = randomFactor * 0x9; + this.LSFR7Table[0x500 | index] = randomFactor * 0xA; + this.LSFR7Table[0x580 | index] = randomFactor * 0xB; + this.LSFR7Table[0x600 | index] = randomFactor * 0xC; + this.LSFR7Table[0x680 | index] = randomFactor * 0xD; + this.LSFR7Table[0x700 | index] = randomFactor * 0xE; + this.LSFR7Table[0x780 | index] = randomFactor * 0xF; + //Recompute the LSFR algorithm: + LSFRShifted = LSFR >> 1; + LSFR = LSFRShifted | (((LSFRShifted ^ LSFR) & 0x1) << 6); + } +} +GameBoyAdvanceChannel4Synth.prototype.disabled = function () { + //Clear NR41: + this.totalLength = 0x40; + //Clear NR42: + this.nr42 = 0; + this.envelopeVolume = 0; + //Clear NR43: + this.nr43 = 0; + this.FrequencyPeriod = 32; + this.lastSampleLookup = 0; + this.BitRange = 0x7FFF; + this.VolumeShifter = 15; + this.currentVolume = 0; + this.noiseSampleTable = this.LSFR15Table; + //Clear NR44: + this.nr44 = 0; + this.consecutive = true; + this.envelopeSweeps = 0; + this.envelopeSweepsLast = -1; + this.canPlay = false; + this.Enabled = 0; + this.counter = 0; +} +GameBoyAdvanceChannel4Synth.prototype.clockAudioLength = function () { + if ((this.totalLength | 0) > 1) { + this.totalLength = ((this.totalLength | 0) - 1) | 0; + } + else if ((this.totalLength | 0) == 1) { + this.totalLength = 0; + this.enableCheck(); + this.sound.unsetNR52(0xF7); //Channel #4 On Flag Off + } +} +GameBoyAdvanceChannel4Synth.prototype.clockAudioEnvelope = function () { + if ((this.envelopeSweepsLast | 0) > -1) { + if ((this.envelopeSweeps | 0) > 0) { + this.envelopeSweeps = ((this.envelopeSweeps | 0) - 1) | 0; + } + else { + if (!this.envelopeType) { + if ((this.envelopeVolume | 0) > 0) { + this.envelopeVolume = ((this.envelopeVolume | 0) - 1) | 0; + this.currentVolume = (this.envelopeVolume | 0) << (this.VolumeShifter | 0); + this.envelopeSweeps = this.envelopeSweepsLast | 0; + } + else { + this.envelopeSweepsLast = -1; + } + } + else if ((this.envelopeVolume | 0) < 0xF) { + this.envelopeVolume = ((this.envelopeVolume | 0) + 1) | 0; + this.currentVolume = (this.envelopeVolume | 0) << (this.VolumeShifter | 0); + this.envelopeSweeps = this.envelopeSweepsLast | 0; + } + else { + this.envelopeSweepsLast = -1; + } + } + } +} +GameBoyAdvanceChannel4Synth.prototype.computeAudioChannel = function () { + if ((this.counter | 0) == 0) { + this.lastSampleLookup = ((this.lastSampleLookup | 0) + 1) & this.BitRange; + this.counter = this.FrequencyPeriod | 0; + } +} +GameBoyAdvanceChannel4Synth.prototype.enableCheck = function () { + if ((this.consecutive || (this.totalLength | 0) > 0) && this.canPlay) { + this.Enabled = 0xF; + } + else { + this.Enabled = 0; + } +} +GameBoyAdvanceChannel4Synth.prototype.volumeEnableCheck = function () { + this.canPlay = ((this.nr42 | 0) > 7); + this.enableCheck(); +} +GameBoyAdvanceChannel4Synth.prototype.outputLevelCache = function () { + var cachedSample = this.cachedSample & this.Enabled; + this.currentSampleLeft = this.leftEnable & cachedSample; + this.currentSampleRight = this.rightEnable & cachedSample; +} +GameBoyAdvanceChannel4Synth.prototype.setChannelOutputEnable = function (data) { + data = data | 0; + //Set by NR51 handler: + this.rightEnable = (data << 28) >> 31; + this.leftEnable = (data << 24) >> 31; +} +GameBoyAdvanceChannel4Synth.prototype.updateCache = function () { + this.cachedSample = this.noiseSampleTable[this.currentVolume | this.lastSampleLookup] | 0; + this.outputLevelCache(); +} +GameBoyAdvanceChannel4Synth.prototype.writeSOUND4CNT_L0 = function (data) { + data = data | 0; + //NR41: + this.totalLength = (0x40 - (data & 0x3F)) | 0; + this.enableCheck(); +} +GameBoyAdvanceChannel4Synth.prototype.writeSOUND4CNT_L1 = function (data) { + data = data | 0; + //NR42: + this.envelopeType = ((data & 0x08) != 0); + this.nr42 = data & 0xFF; + this.volumeEnableCheck(); +} +GameBoyAdvanceChannel4Synth.prototype.readSOUND4CNT_L = function () { + //NR42: + return this.nr42 | 0; +} +GameBoyAdvanceChannel4Synth.prototype.writeSOUND4CNT_H0 = function (data) { + data = data | 0; + //NR43: + this.FrequencyPeriod = Math.max((data & 0x7) << 4, 8) << ((((data >> 4) & 0xF) + 2) | 0); + var bitWidth = data & 0x8; + if (((bitWidth | 0) == 0x8 && (this.BitRange | 0) == 0x7FFF) || ((bitWidth | 0) == 0 && (this.BitRange | 0) == 0x7F)) { + this.lastSampleLookup = 0; + this.BitRange = ((bitWidth | 0) == 0x8) ? 0x7F : 0x7FFF; + this.VolumeShifter = ((bitWidth | 0) == 0x8) ? 7 : 15; + this.currentVolume = this.envelopeVolume << (this.VolumeShifter | 0); + this.noiseSampleTable = ((bitWidth | 0) == 0x8) ? this.LSFR7Table : this.LSFR15Table; + } + this.nr43 = data & 0xFF; +} +GameBoyAdvanceChannel4Synth.prototype.readSOUND4CNT_H0 = function () { + //NR43: + return this.nr43 | 0; +} +GameBoyAdvanceChannel4Synth.prototype.writeSOUND4CNT_H1 = function (data) { + data = data | 0; + //NR44: + this.nr44 = data & 0xFF; + this.consecutive = ((data & 0x40) == 0x0); + if ((data & 0x80) != 0) { + this.envelopeVolume = this.nr42 >> 4; + this.currentVolume = this.envelopeVolume << (this.VolumeShifter | 0); + this.envelopeSweepsLast = ((this.nr42 & 0x7) - 1) | 0; + if ((this.totalLength | 0) == 0) { + this.totalLength = 0x40; + } + if ((data & 0x40) != 0) { + this.sound.setNR52(0x8); + } + } + this.enableCheck(); +} +GameBoyAdvanceChannel4Synth.prototype.readSOUND4CNT_H1 = function () { + //NR44: + return this.nr44 | 0; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/core/sound/FIFO.js b/public/gfiles/gba/IodineGBA/core/sound/FIFO.js new file mode 100644 index 00000000..296dac36 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/core/sound/FIFO.js @@ -0,0 +1,63 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GameBoyAdvanceFIFO() { + this.count = 0; + this.position = 0; + this.buffer = getInt8Array(0x20); +} +GameBoyAdvanceFIFO.prototype.push = function (sample) { + sample = sample | 0; + var writePosition = ((this.position | 0) + (this.count | 0)) | 0; + this.buffer[writePosition & 0x1F] = (sample << 24) >> 24; + if ((this.count | 0) < 0x20) { + //Should we cap at 0x20 or overflow back to 0 and reset queue? + this.count = ((this.count | 0) + 1) | 0; + } +} +GameBoyAdvanceFIFO.prototype.push8 = function (sample) { + sample = sample | 0; + this.push(sample | 0); + this.push(sample | 0); + this.push(sample | 0); + this.push(sample | 0); +} +GameBoyAdvanceFIFO.prototype.push16 = function (sample) { + sample = sample | 0; + this.push(sample | 0); + this.push(sample >> 8); + this.push(sample | 0); + this.push(sample >> 8); +} +GameBoyAdvanceFIFO.prototype.push32 = function (sample) { + sample = sample | 0; + this.push(sample | 0); + this.push(sample >> 8); + this.push(sample >> 16); + this.push(sample >> 24); +} +GameBoyAdvanceFIFO.prototype.shift = function () { + var output = 0; + if ((this.count | 0) > 0) { + this.count = ((this.count | 0) - 1) | 0; + output = this.buffer[this.position & 0x1F] << 3; + this.position = ((this.position | 0) + 1) & 0x1F; + } + return output | 0; +} +GameBoyAdvanceFIFO.prototype.requestingDMA = function () { + return ((this.count | 0) <= 0x10); +} +GameBoyAdvanceFIFO.prototype.samplesUntilDMATrigger = function () { + return ((this.count | 0) - 0x10) | 0; +} +GameBoyAdvanceFIFO.prototype.clear = function () { + this.count = 0; +} \ No newline at end of file diff --git a/public/gfiles/gba/IodineGBA/includes/TypedArrayShim.js b/public/gfiles/gba/IodineGBA/includes/TypedArrayShim.js new file mode 100644 index 00000000..23c15dc0 --- /dev/null +++ b/public/gfiles/gba/IodineGBA/includes/TypedArrayShim.js @@ -0,0 +1,186 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function getInt8Array(size_t) { + try { + return new Int8Array(size_t); + } + catch (error) { + return getArray(size_t); + } +} +function getUint8Array(size_t) { + try { + return new Uint8Array(size_t); + } + catch (error) { + return getArray(size_t); + } +} +function getUint8View(typed_array) { + try { + return new Uint8Array(typed_array.buffer); + } + catch (error) { + return null; + } +} +function getSharedUint8Array(size_t) { + try { + //Compatibility for older Firefox Nightlies: + return new SharedUint8Array(size_t); + } + catch (error) { + return new Uint8Array(new SharedArrayBuffer(size_t)); + } +} +function getInt16Array(size_t) { + try { + return new Int16Array(size_t); + } + catch (error) { + return getArray(size_t); + } +} +function getUint16Array(size_t) { + try { + return new Uint16Array(size_t); + } + catch (error) { + return getArray(size_t); + } +} +function getUint16View(typed_array) { + try { + return new Uint16Array(typed_array.buffer); + } + catch (error) { + return null; + } +} +function getInt32Array(size_t) { + try { + return new Int32Array(size_t); + } + catch (error) { + return getArray(size_t); + } +} +function getInt32View(typed_array) { + try { + return new Int32Array(typed_array.buffer); + } + catch (error) { + return null; + } +} +function getInt32ViewCustom(typed_array, start, end) { + try { + typed_array = getInt32View(typed_array); + return typed_array.subarray(start, end); + } + catch (error) { + try { + //Nightly Firefox 4 used to have the subarray function named as slice: + return typed_array.slice(start, end); + } + catch (error) { + return null; + } + } +} +function getSharedInt32Array(size_t) { + try { + //Compatibility for older Firefox Nightlies: + return new SharedInt32Array(size_t); + } + catch (error) { + return new Int32Array(new SharedArrayBuffer(size_t << 2)); + } +} +function getUint8ViewCustom(typed_array, start, end) { + try { + typed_array = getUint8View(typed_array); + return typed_array.subarray(start, end); + } + catch (error) { + try { + //Nightly Firefox 4 used to have the subarray function named as slice: + return typed_array.slice(start, end); + } + catch (error) { + return null; + } + } +} +function getUint32Array(size_t) { + try { + return new Uint32Array(size_t); + } + catch (error) { + return getArray(size_t); + } +} +function getSharedUint32Array(size_t) { + try { + //Compatibility for older Firefox Nightlies: + return new SharedUint32Array(size_t); + } + catch (error) { + return new Uint32Array(new SharedArrayBuffer(size_t << 2)); + } +} +function getFloat32Array(size_t) { + try { + return new Float32Array(size_t); + } + catch (error) { + return getArray(size_t); + } +} +function getSharedFloat32Array(size_t) { + try { + //Compatibility for older Firefox Nightlies: + return new SharedFloat32Array(size_t); + } + catch (error) { + return new Float32Array(new SharedArrayBuffer(size_t << 2)); + } +} +function getArray(size_t) { + var genericArray = []; + for (var size_index = 0; size_index < size_t; ++size_index) { + genericArray[size_index] = 0; + } + return genericArray; +} +var __VIEWS_SUPPORTED__ = getUint16View(getInt32Array(1)) !== null; +var __LITTLE_ENDIAN__ = (function () { + if (__VIEWS_SUPPORTED__) { + var test = getInt32Array(1); + test[0] = 1; + var test2 = getUint16View(test); + if (test2[0] == 1) { + return true; + } + } + return false; +})(); +if (typeof Atomics == "object") { + if (typeof Atomics.futexWait == "function" && typeof Atomics.wait == "undefined") { + //Polyfill in deprecated call names: + Atomics.wait = Atomics.futexWait; + Atomics.notify = Atomics.futexWake; + } + else if (typeof Atomics.wake == "function" && typeof Atomics.notify == "undefined") { + //Polyfill in deprecated call names: + Atomics.notify = Atomics.wake; + } +} diff --git a/public/gfiles/gba/bios.bin b/public/gfiles/gba/bios.bin new file mode 100644 index 00000000..2a78f2c5 Binary files /dev/null and b/public/gfiles/gba/bios.bin differ diff --git a/public/gfiles/gba/index.html b/public/gfiles/gba/index.html new file mode 100644 index 00000000..7ae8e868 --- /dev/null +++ b/public/gfiles/gba/index.html @@ -0,0 +1,128 @@ + + + + GBA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ +
+
+
+
+ +
+ +
+
+ + +
+
+ +
+ + +
+
+ +
+ + diff --git a/public/gfiles/gba/user_css/main.css b/public/gfiles/gba/user_css/main.css new file mode 100644 index 00000000..d8627c84 --- /dev/null +++ b/public/gfiles/gba/user_css/main.css @@ -0,0 +1,281 @@ +/* +|----------------------------------------- +| Core CSS +|----------------------------------------- + */ + +html, body { + font-family: Open Sans, Arial, sans-serif; + height: 100%; + width:100%; + margin: 0; + padding: 0; + background: #000; + overflow: hidden; + box-sizing: border-box; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* reset all list items */ +ul{ + list-style: none; + padding: 0; + margin: 0; +} + +#container { + width: 100%; + height: 100%; +} + +#main { + position:absolute; + top: 0px; + left: 0px; + right: 0px; + bottom: 0px; + background-color: black; +} + +/* +|----------------------------------------- +| Canvas +|----------------------------------------- +| +| Classes are automatically overwritten +| by the filter styles, therefore only +| attributes and id's can be used. +| + */ + +canvas { + margin: auto; + display: block; + padding: 0px; + background-color: black; +} + +canvas.textureSmooth { + image-rendering: auto; + image-rendering: optimizeQuality; + -ms-interpolation-mode: bicubic; +} + +canvas.texturePixelated { + image-rendering: -webkit-optimize-contrast; + image-rendering: -o-crisp-edges; + image-rendering: -moz-crisp-edges; + image-rendering: crisp-edges; + image-rendering: pixelated; + -ms-interpolation-mode: nearest-neighbor; +} + +/* +|----------------------------------------- +| Messages +|----------------------------------------- + */ + +.message { + background: #6cc27d; + padding: 1em 2em; + text-align: center; + color: #fff; + bottom: 0px; + position: fixed; + left: 0px; + display: none; + font-weight: bold; + vertical-align: bottom; + font-family: monospace; + z-index: 1; +} + +/* +|----------------------------------------- +| Main Menu +|----------------------------------------- + */ + +/* top level menu */ +div#menu { + display: none !important; + width: 0px; + height: 0px; +} + +ul{ + list-style: none; + padding: 0; + margin: 0; +} + +.menu { + position: fixed; + display: flex; + flex-wrap: wrap; + background: #fff; + transition: .3s ease; + cursor: pointer; + font-family: sans-serif; + z-index: 1; + opacity: 0; + height: 42px; +} + +.menu:hover{opacity: 0.9;} + +.menu ul{ + visibility: hidden; + opacity: 0; + position: absolute; + top:100%; + left:0; + background-color: rgb(245, 245, 245); + box-shadow: 0 5px 10px 0 rgba(0,0,0,.1); + transition: .3s ease; + z-index: 1; +} + +.menu ul li { + padding: .3em 1em !important; +} + +.menu li{ + white-space: nowrap; + position: relative; + cursor:pointer; + padding: .7em 1em; +} + +.menu li:hover { + background: rgba(0,0,0,.1); +} + +.menu li:hover > ul{ + visibility: visible; + opacity: 1; +} + +li #smooth { + transform: scale(1.5); + margin: 0px 13px 0px 0px; +} + +#import { + display: none; +} + +#import + label { + border: 1px solid rgb(118, 118, 118); + border-radius: 2px; + padding: 4px; + margin: 2px; + font-size: 13px; +} + +#export { + color: black; + text-decoration: none; +} + +#ffd { + z-index: 5; + height: 200px; + width: 400px; + position: absolute; + background: #ddd; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + text-align: center; + font-family: sans-serif; + border-radius: 2px; +} + +#upload { + display: none; +} + +#upload + label { + border: 1px solid rgb(118, 118, 118); + border-radius: 2px; + padding: 4px; + margin: 10px; + font-size: 13px; + background-color: white; + display: inline-block; + transition: ease .1s; +} + +#upload + label:hover { + background-color: #eee; +} + +/* +|----------------------------------------- +| Touch Controls +|----------------------------------------- + */ + +.touch-controls{ + display: flex; + padding: 1em; + position: absolute; + bottom: 0px; + left: 0px; + right: 0px; +} + +.touch-controls button{ + display: inline-block; + -webkit-appearance: none; + border:0; + outline: 0; + background: #fff; + opacity: 0.7; + width: 3em; + height: 3em; + line-height: 3em; + text-align: center; + cursor: pointer; + transition: .1s ease; + margin:.5em; +} + +.touch-controls button:active{ + transform: scale(0.95); + box-shadow: 0 0 10px 0 rgba(0,0,0,0.4) inset; +} + +.touch-dpad, .touch-buttons{ + flex-grow: 1; + text-align: center; +} + +.touch-buttons{ + align-self: flex-end; +} + +.touch-dpad--up{ + flex-grow: 1; + width: 100%; +} + +/* Only show controls on portrait mode screens */ +@media screen and (min-aspect-ratio: 1/1) { + .touch-controls{ + display: none; + } + #main { + display: flex; + justify-content: center; + flex-grow: 1; + align-items: center; + } +} \ No newline at end of file diff --git a/public/gfiles/gba/user_scripts/AudioGlueCode.js b/public/gfiles/gba/user_scripts/AudioGlueCode.js new file mode 100644 index 00000000..1b3a23d8 --- /dev/null +++ b/public/gfiles/gba/user_scripts/AudioGlueCode.js @@ -0,0 +1,283 @@ +"use strict"; +/* + Copyright (C) 2012-2015 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GlueCodeMixer(playButton) { + var parentObj = this; + this.audio = new XAudioServer(2, this.sampleRate, 0, this.bufferAmount, null, function () { + parentObj.checkHeartbeats(); + }, function () { + parentObj.checkPostHeartbeats(); + }, 1, function () { + //Disable audio in the callback here: + parentObj.disableAudio(); + }, playButton); + this.outputUnits = []; + this.outputUnitsValid = []; + this.initializeBuffer(); +} +GlueCodeMixer.prototype.sampleRate = 44100; +GlueCodeMixer.prototype.bufferAmount = 44100; +GlueCodeMixer.prototype.channelCount = 2; +GlueCodeMixer.prototype.initializeBuffer = function () { + this.buffer = new AudioSimpleBuffer(this.channelCount, + this.bufferAmount); +} +GlueCodeMixer.prototype.appendInput = function (inUnit) { + if (this.audio) { + for (var index = 0; index < this.outputUnits.length; index++) { + if (!this.outputUnits[index]) { + break; + } + } + this.outputUnits[index] = inUnit; + this.outputUnitsValid.push(inUnit); + inUnit.registerStackPosition(index); + } + else if (typeof inUnit.errorCallback == "function") { + inUnit.errorCallback(); + } +} +GlueCodeMixer.prototype.unregister = function (stackPosition) { + this.outputUnits[stackPosition] = null; + this.outputUnitsValid = []; + for (var index = 0, length = this.outputUnits.length; index < length; ++index) { + if (this.outputUnits[index]) { + this.outputUnitsValid.push(this.outputUnits); + } + } +} +GlueCodeMixer.prototype.checkHeartbeats = function () { + var inputCount = this.outputUnitsValid.length; + for (var inputIndex = 0, output = 0; inputIndex < inputCount; ++inputIndex) { + this.outputUnitsValid[inputIndex].heartBeatCallback(); + } +} +GlueCodeMixer.prototype.checkPostHeartbeats = function () { + var inputCount = this.outputUnitsValid.length; + for (var inputIndex = 0, output = 0; inputIndex < inputCount; ++inputIndex) { + this.outputUnitsValid[inputIndex].postHeartBeatCallback(); + } +} +GlueCodeMixer.prototype.checkAudio = function () { + if (this.audio) { + var inputCount = this.outputUnitsValid.length; + for (var inputIndex = 0, output = 0; inputIndex < inputCount; ++inputIndex) { + this.outputUnitsValid[inputIndex].prepareShift(); + } + for (var count = 0, requested = this.findLowestBufferCount(); count < requested; ++count) { + for (var inputIndex = 0, output = 0; inputIndex < inputCount; ++inputIndex) { + output += this.outputUnitsValid[inputIndex].shift(); + } + this.buffer.push(output); + } + var bufferLength = this.buffer.count(); + this.audio.writeAudioNoCallback(this.buffer.buffer, bufferLength); + this.buffer.reset(); + } +} +GlueCodeMixer.prototype.findLowestBufferCount = function () { + var count = 0; + for (var inputIndex = 0, inputCount = this.outputUnitsValid.length; inputIndex < inputCount; ++inputIndex) { + var tempCount = this.outputUnitsValid[inputIndex].buffer.resampledSamplesLeft(); + if (tempCount > 0) { + if (count > 0) { + count = Math.min(count, tempCount); + } + else { + count = tempCount; + } + } + } + return Math.min(count, this.channelCount * this.bufferAmount); +} +GlueCodeMixer.prototype.disableAudio = function () { + this.audio = null; +} +function GlueCodeMixerInput(mixer) { + this.mixer = mixer; + this.volume = 1; +} +GlueCodeMixerInput.prototype.initialize = function (channelCount, sampleRate, bufferAmount, heartBeatCallback, postHeartBeatCallback, errorCallback) { + this.channelCount = channelCount; + this.sampleRate = sampleRate; + this.bufferAmount = bufferAmount; + this.heartBeatCallback = heartBeatCallback; + this.postHeartBeatCallback = postHeartBeatCallback; + this.errorCallback = errorCallback; + var oldBuffer = this.buffer; + this.buffer = new AudioBufferWrapper(this.channelCount, + this.mixer.channelCount, + this.bufferAmount, + this.sampleRate, + this.mixer.sampleRate); + if (oldBuffer) { + //If re-using same mixer input node, copy old buffer contents into the new buffer: + this.buffer.copyOld(oldBuffer); + } +} +GlueCodeMixerInput.prototype.register = function () { + this.mixer.appendInput(this); +} +GlueCodeMixerInput.prototype.setVolume = function (volume) { + this.volume = Math.min(Math.max(volume, 0), 1); +} +GlueCodeMixerInput.prototype.prepareShift = function () { + this.buffer.resampleRefill(); +} +GlueCodeMixerInput.prototype.shift = function () { + return this.buffer.shift() * this.volume; +} +GlueCodeMixerInput.prototype.push = function (buffer, start, end) { + this.buffer.push(buffer, start, end); + this.mixer.checkAudio(); +} +GlueCodeMixerInput.prototype.pushDeferred = function (buffer, start, end) { + this.buffer.push(buffer, start, end); +} +GlueCodeMixerInput.prototype.flush = function () { + this.mixer.checkAudio(); +} +GlueCodeMixerInput.prototype.remainingBuffer = function () { + return this.buffer.remainingBuffer() + (Math.floor((this.mixer.audio.remainingBuffer() * this.sampleRate / this.mixer.sampleRate) / this.mixer.channelCount) * this.mixer.channelCount); +} +GlueCodeMixerInput.prototype.registerStackPosition = function (stackPosition) { + this.stackPosition = stackPosition; +} +GlueCodeMixerInput.prototype.unregister = function () { + this.mixer.unregister(this.stackPosition); +} +GlueCodeMixerInput.prototype.setBufferSpace = function (bufferAmount) { + this.buffer.setBufferSpace(bufferAmount); +} +function AudioBufferWrapper(channelCount, + mixerChannelCount, + bufferAmount, + sampleRate, + mixerSampleRate) { + this.channelCount = channelCount; + this.mixerChannelCount = mixerChannelCount; + this.bufferAmount = bufferAmount; + this.sampleRate = sampleRate; + this.mixerSampleRate = mixerSampleRate; + this.initialize(); +} +AudioBufferWrapper.prototype.initialize = function () { + this.inBufferSize = this.bufferAmount * this.mixerChannelCount; + this.inBuffer = getFloat32Array(this.inBufferSize); + this.resampler = new Resampler(this.sampleRate, this.mixerSampleRate, this.mixerChannelCount, this.inBuffer); + this.outBufferSize = this.resampler.outputBuffer.length; + this.outBuffer = getFloat32Array(this.outBufferSize); + this.inputOffset = 0; + this.resampleBufferStart = 0; + this.resampleBufferEnd = 0; +} +AudioBufferWrapper.prototype.copyOld = function (oldBuffer) { + this.resampleRefill(); + while (oldBuffer.resampleBufferStart != oldBuffer.resampleBufferEnd) { + this.outBuffer[this.resampleBufferEnd++] = oldBuffer.outBuffer[oldBuffer.resampleBufferStart++]; + if (this.resampleBufferEnd == this.outBufferSize) { + this.resampleBufferEnd = 0; + } + if (this.resampleBufferStart == this.resampleBufferEnd) { + this.resampleBufferStart += this.mixerChannelCount; + if (this.resampleBufferStart == this.outBufferSize) { + this.resampleBufferStart = 0; + } + } + if (oldBuffer.resampleBufferStart == oldBuffer.outBufferSize) { + oldBuffer.resampleBufferStart = 0; + } + } +} +AudioBufferWrapper.prototype.push = function (buffer, start, end) { + var length = Math.min(buffer.length, end); + if (this.channelCount < this.mixerChannelCount) { + for (; start < length && this.inputOffset < this.inBufferSize;) { + for (var index = this.channelCount; index < this.mixerChannelCount; ++index) { + this.inBuffer[this.inputOffset++] = buffer[start]; + } + for (index = 0; index < this.channelCount && start < length; ++index) { + this.inBuffer[this.inputOffset++] = buffer[start++]; + } + } + } + else if (this.channelCount == this.mixerChannelCount) { + for (; start < length && this.inputOffset < this.inBufferSize;) { + this.inBuffer[this.inputOffset++] = buffer[start++]; + } + } + else { + for (; start < length && this.inputOffset < this.inBufferSize;) { + for (index = 0; index < this.mixerChannelCount && start < length; ++index) { + this.inBuffer[this.inputOffset++] = buffer[start++]; + } + start += this.channelCount - this.mixerChannelCount; + } + } +} +AudioBufferWrapper.prototype.shift = function () { + var output = 0; + if (this.resampleBufferStart != this.resampleBufferEnd) { + output = this.outBuffer[this.resampleBufferStart++]; + if (this.resampleBufferStart == this.outBufferSize) { + this.resampleBufferStart = 0; + } + } + return output; +} +AudioBufferWrapper.prototype.resampleRefill = function () { + if (this.inputOffset > 0) { + //Resample a chunk of audio: + var resampleLength = this.resampler.resampler(this.inputOffset); + var resampledResult = this.resampler.outputBuffer; + for (var index2 = 0; index2 < resampleLength;) { + this.outBuffer[this.resampleBufferEnd++] = resampledResult[index2++]; + if (this.resampleBufferEnd == this.outBufferSize) { + this.resampleBufferEnd = 0; + } + if (this.resampleBufferStart == this.resampleBufferEnd) { + this.resampleBufferStart += this.mixerChannelCount; + if (this.resampleBufferStart == this.outBufferSize) { + this.resampleBufferStart = 0; + } + } + } + this.inputOffset = 0; + } +} +AudioBufferWrapper.prototype.setBufferSpace = function (bufferAmount) { + while (this.inputOffset < bufferAmount && this.inputOffset < this.inBufferSize) { + this.inBuffer[this.inputOffset++] = 0; + } +} +AudioBufferWrapper.prototype.remainingBuffer = function () { + return (Math.floor((this.resampledSamplesLeft() * this.resampler.ratioWeight) / this.mixerChannelCount) * this.mixerChannelCount) + this.inputOffset; +} +AudioBufferWrapper.prototype.resampledSamplesLeft = function () { + return ((this.resampleBufferStart <= this.resampleBufferEnd) ? 0 : this.outBufferSize) + this.resampleBufferEnd - this.resampleBufferStart; +} +function AudioSimpleBuffer(channelCount, bufferAmount) { + this.channelCount = channelCount; + this.bufferAmount = bufferAmount; + this.outBufferSize = this.channelCount * this.bufferAmount; + this.stackLength = 0; + this.buffer = getFloat32Array(this.outBufferSize); +} +AudioSimpleBuffer.prototype.push = function (data) { + if (this.stackLength < this.outBufferSize) { + this.buffer[this.stackLength++] = data; + } +} +AudioSimpleBuffer.prototype.count = function () { + return this.stackLength; +} +AudioSimpleBuffer.prototype.reset = function () { + this.stackLength = 0; +} diff --git a/public/gfiles/gba/user_scripts/CoreGlueCode.js b/public/gfiles/gba/user_scripts/CoreGlueCode.js new file mode 100644 index 00000000..3ed74d9b --- /dev/null +++ b/public/gfiles/gba/user_scripts/CoreGlueCode.js @@ -0,0 +1,178 @@ +"use strict"; +/* + Copyright (C) 2012-2019 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +var IodineGUI = { + Iodine:null, + Blitter:null, + coreTimerID:null, + GUITimerID: null, + toMap:null, + toMapIndice:0, + suspended:false, + isPlaying:false, + startTime:(+(new Date()).getTime()), + mixerInput:null, + currentSpeed:[false,0], + defaults:{ + timerRate:8, + sound:true, + volume:1, + skipBoot:false, + toggleSmoothScaling:true, + toggleDynamicSpeed:false, + toggleOffthreadGraphics:true, + toggleOffthreadCPU:(navigator.userAgent.indexOf('AppleWebKit') == -1 || (navigator.userAgent.indexOf('Windows NT 10.0') != -1 && navigator.userAgent.indexOf('Trident') == -1)), + keyZonesGBA:[ + //Use this to control the GBA key mapping: + //A: + 68, + //B: + 67, + //Select: + 32, + //Start: + 13, + //Right: + 39, + //Left: + 37, + //Up: + 38, + //Down: + 40, + //R: + 88, + //L: + 83 + //Why are these switched lol + ], + keyZonesControl:[ + //Use this to control the emulator function key mapping: + //Volume Down: + 55, + //Volume Up: + 56, + //Speed Up: + 52, + //Slow Down: + 51, + //Reset Speed: + 53, + //Toggle Fullscreen: + 54, + //Play/Pause: + 80, + //Restart: + 82 + ] + } +}; +window.onload = function () { + //Populate settings: + registerDefaultSettings(); + //Initialize Iodine: + registerIodineHandler(); + //Initialize the timer: + calculateTiming(); + //Initialize the graphics: + registerBlitterHandler(); + //Initialize the audio: + registerAudioHandler(); + //Register the save handler callbacks: + registerSaveHandlers(); + //Register the GUI controls. + registerGUIEvents(); + //Register GUI settings. + registerGUISettings(); + //Additional things. + IodineGUI.Iodine.toggleSkipBootROM(true); + IodineGUI.Blitter.setSmoothScaling(false); + //File things. + fileThings(); +} + +function registerIodineHandler() { + try { + /* + We utilize SharedArrayBuffer and Atomics API, + which browsers prior to 2016 do not support: + */ + if (typeof SharedArrayBuffer != "function" || typeof Atomics != "object") { + throw null; + } + else if (!IodineGUI.defaults.toggleOffthreadCPU && IodineGUI.defaults.toggleOffthreadGraphics) { + //Try starting Iodine normally, but initialize offthread gfx: + IodineGUI.Iodine = new IodineGBAWorkerGfxShim(); + } + else if (IodineGUI.defaults.toggleOffthreadGraphics) { + //Try starting Iodine in a webworker: + IodineGUI.Iodine = new IodineGBAWorkerShim(); + //In order for save on page unload, this needs to be done: + addEvent("beforeunload", window, registerBeforeUnloadHandler); + } + else { + throw null; + } + } + catch (e) { + //Otherwise just run on-thread: + IodineGUI.Iodine = new GameBoyAdvanceEmulator(); + } +} +function registerBeforeUnloadHandler(e) { + IodineGUI.Iodine.pause(); + if (e.preventDefault) { + e.preventDefault(); + } + return "IodineGBA needs to process your save data, leaving now may result in not saving current data."; +} +function initTimer() { + IodineGUI.Iodine.setIntervalRate(+IodineGUI.defaults.timerRate); + IodineGUI.coreTimerID = setInterval(function () { + IodineGUI.Iodine.timerCallback(((+(new Date()).getTime()) - (+IodineGUI.startTime)) >>> 0); + }, IodineGUI.defaults.timerRate | 0); +} +function calculateTiming() { + IodineGUI.Iodine.setIntervalRate(+IodineGUI.defaults.timerRate); +} +function startTimer() { + IodineGUI.coreTimerID = setInterval(function () { + IodineGUI.Iodine.timerCallback(((+(new Date()).getTime()) - (+IodineGUI.startTime)) >>> 0); + }, IodineGUI.defaults.timerRate | 0); +} +function updateTimer(newRate) { + newRate = newRate | 0; + if ((newRate | 0) != (IodineGUI.defaults.timerRate | 0)) { + IodineGUI.defaults.timerRate = newRate | 0; + IodineGUI.Iodine.setIntervalRate(+IodineGUI.defaults.timerRate); + if (IodineGUI.isPlaying) { + if (IodineGUI.coreTimerID) { + clearInterval(IodineGUI.coreTimerID); + } + initTimer(); + } + } +} +function registerBlitterHandler() { + IodineGUI.Blitter = new GfxGlueCode(240, 160); + IodineGUI.Blitter.attachCanvas(document.getElementById("emulator_target")); + IodineGUI.Iodine.attachGraphicsFrameHandler(IodineGUI.Blitter); + IodineGUI.Blitter.attachGfxPostCallback(function () { + if (IodineGUI.currentSpeed[0]) { + var speedDOM = document.getElementById("speed"); + speedDOM.textContent = "Speed: " + IodineGUI.currentSpeed[1] + "%"; + } + }); +} +function registerAudioHandler() { + var Mixer = new GlueCodeMixer(document.getElementById("play")); + IodineGUI.mixerInput = new GlueCodeMixerInput(Mixer); + IodineGUI.Iodine.attachAudioHandler(IodineGUI.mixerInput); +} \ No newline at end of file diff --git a/public/gfiles/gba/user_scripts/GUIGlueCode.js b/public/gfiles/gba/user_scripts/GUIGlueCode.js new file mode 100644 index 00000000..8167f2f5 --- /dev/null +++ b/public/gfiles/gba/user_scripts/GUIGlueCode.js @@ -0,0 +1,667 @@ +"use strict"; +/* + Copyright (C) 2012-2019 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function registerGUIEvents() { + //Catch any play status changes: + IodineGUI.Iodine.attachPlayStatusHandler(updatePlayButton); + //Add DOM events: + addEvent("keydown", document, keyDown); + addEvent("keyup", document, keyUpPreprocess); + addEvent("change", document.getElementById("rom_load"), fileLoadROM); + addEvent("change", document.getElementById("bios_load"), fileLoadBIOS); + addEvent("click", document.getElementById("play"), function (e) { + IodineGUI.Iodine.play(); + }); + addEvent("click", document.getElementById("pause"), function (e) { + IodineGUI.Iodine.pause(); + }); + addEvent("click", document.getElementById("restart"), function (e) { + IodineGUI.Iodine.restart(); + }); + addEvent("click", document.getElementById("sound"), function () { + setValue("sound", !!this.checked); + if (this.checked) { + IodineGUI.Iodine.enableAudio(); + } + else { + IodineGUI.Iodine.disableAudio(); + } + }); + addEvent("click", document.getElementById("skip_boot"), function () { + setValue("skipBoot", !!this.checked); + IodineGUI.Iodine.toggleSkipBootROM(this.checked); + }); + addEvent("click", document.getElementById("toggleDynamicSpeed"), function () { + setValue("toggleDynamicSpeed", !!this.checked); + IodineGUI.Iodine.toggleDynamicSpeed(this.checked); + }); + addEvent("click", document.getElementById("offthread-gpu"), function () { + setValue("toggleOffthreadGraphics", !!this.checked); + IodineGUI.Iodine.toggleOffthreadGraphics(this.checked); + }); + addEvent("click", document.getElementById("offthread-cpu"), function () { + setValue("toggleOffthreadCPU", !!this.checked); + //Can't do anything until reload of page. + }); + addEvent("change", document.getElementById("speedset"), speedChangeFunc); + addEvent("input", document.getElementById("speedset"), speedChangeFunc); + addEvent("click", document.getElementById("fullscreen"), toggleFullScreen); + addEvent("click", document.getElementById("key_a"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA; + IodineGUI.toMapIndice = 0; + }); + addEvent("mousedown", document.getElementById("touch-a"), function () { + IodineGUI.Iodine.keyDown(0); + }); + addEvent("mouseup", document.getElementById("touch-a"), function () { + IodineGUI.Iodine.keyUp(0); + }); + addEvent("click", document.getElementById("key_b"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA; + IodineGUI.toMapIndice = 1; + }); + addEvent("mousedown", document.getElementById("touch-b"), function () { + IodineGUI.Iodine.keyDown(1); + }); + addEvent("mouseup", document.getElementById("touch-b"), function () { + IodineGUI.Iodine.keyUp(1); + }); + addEvent("click", document.getElementById("key_select"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA; + IodineGUI.toMapIndice = 2; + }); + addEvent("mousedown", document.getElementById("touch-select"), function () { + IodineGUI.Iodine.keyDown(2); + }); + addEvent("mouseup", document.getElementById("touch-select"), function () { + IodineGUI.Iodine.keyUp(2); + }); + addEvent("click", document.getElementById("key_start"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA; + IodineGUI.toMapIndice = 3; + }); + addEvent("mousedown", document.getElementById("touch-start"), function () { + IodineGUI.Iodine.keyDown(3); + }); + addEvent("mouseup", document.getElementById("touch-start"), function () { + IodineGUI.Iodine.keyUp(3); + }); + addEvent("click", document.getElementById("key_right"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA; + IodineGUI.toMapIndice = 4; + }); + addEvent("mousedown", document.getElementById("touch-right"), function () { + IodineGUI.Iodine.keyDown(4); + }); + addEvent("mouseup", document.getElementById("touch-right"), function () { + IodineGUI.Iodine.keyUp(4); + }); + addEvent("click", document.getElementById("key_left"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA; + IodineGUI.toMapIndice = 5; + }); + addEvent("mousedown", document.getElementById("touch-left"), function () { + IodineGUI.Iodine.keyDown(5); + }); + addEvent("mouseup", document.getElementById("touch-left"), function () { + IodineGUI.Iodine.keyUp(5); + }); + addEvent("click", document.getElementById("key_up"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA; + IodineGUI.toMapIndice = 6; + }); + addEvent("mousedown", document.getElementById("touch-up"), function () { + IodineGUI.Iodine.keyDown(6); + }); + addEvent("mouseup", document.getElementById("touch-up"), function () { + IodineGUI.Iodine.keyUp(6); + }); + addEvent("click", document.getElementById("key_down"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA; + IodineGUI.toMapIndice = 7; + }); + addEvent("mousedown", document.getElementById("touch-down"), function () { + IodineGUI.Iodine.keyDown(7); + }); + addEvent("mouseup", document.getElementById("touch-down"), function () { + IodineGUI.Iodine.keyUp(7); + }); + addEvent("click", document.getElementById("key_r"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA; + IodineGUI.toMapIndice = 8; + }); + addEvent("mousedown", document.getElementById("touch-r"), function () { + IodineGUI.Iodine.keyDown(8); + }); + addEvent("mouseup", document.getElementById("touch-r"), function () { + IodineGUI.Iodine.keyUp(8); + }); + addEvent("click", document.getElementById("key_l"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA; + IodineGUI.toMapIndice = 9; + }); + addEvent("mousedown", document.getElementById("touch-l"), function () { + IodineGUI.Iodine.keyDown(9); + }); + addEvent("mouseup", document.getElementById("touch-l"), function () { + IodineGUI.Iodine.keyUp(9); + }); + addEvent("click", document.getElementById("key_volumedown"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesControl; + IodineGUI.toMapIndice = 0; + }); + addEvent("click", document.getElementById("key_volumeup"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesControl; + IodineGUI.toMapIndice = 1; + }); + addEvent("click", document.getElementById("key_speedup"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesControl; + IodineGUI.toMapIndice = 2; + }); + addEvent("click", document.getElementById("key_slowdown"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesControl; + IodineGUI.toMapIndice = 3; + }); + addEvent("click", document.getElementById("key_speedreset"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesControl; + IodineGUI.toMapIndice = 4; + }); + addEvent("click", document.getElementById("key_fullscreen"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesControl; + IodineGUI.toMapIndice = 5; + }); + addEvent("click", document.getElementById("key_playpause"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesControl; + IodineGUI.toMapIndice = 6; + }); + addEvent("click", document.getElementById("key_restart"), function () { + IodineGUI.toMap = IodineGUI.defaults.keyZonesControl; + IodineGUI.toMapIndice = 7; + }); + addEvent("change", document.getElementById("import"), function (e) { + if (typeof this.files != "undefined") { + try { + if (this.files.length >= 1) { + writeRedTemporaryText("Reading the local file \"" + this.files[0].name + "\" for importing."); + try { + //Gecko 1.9.2+ (Standard Method) + var binaryHandle = new FileReader(); + binaryHandle.onload = function () { + if (this.readyState == 2) { + writeRedTemporaryText("file imported."); + try { + import_save(this.result); + } + catch (error) { + writeRedTemporaryText(error.message + " file: " + error.fileName + " line: " + error.lineNumber); + } + } + else { + writeRedTemporaryText("importing file, please wait..."); + } + } + binaryHandle.readAsBinaryString(this.files[this.files.length - 1]); + } + catch (error) { + //Gecko 1.9.0, 1.9.1 (Non-Standard Method) + var romImageString = this.files[this.files.length - 1].getAsBinary(); + try { + import_save(romImageString); + } + catch (error) { + writeRedTemporaryText(error.message + " file: " + error.fileName + " line: " + error.lineNumber); + } + } + } + else { + writeRedTemporaryText("Incorrect number of files selected for local loading."); + } + } + catch (error) { + writeRedTemporaryText("Could not load in a locally stored ROM file."); + } + } + else { + writeRedTemporaryText("could not find the handle on the file to open."); + } + if (e.preventDefault) { + e.preventDefault(); + } + }); + addEvent("click", document.getElementById("export"), refreshStorageListing); + addEvent("unload", window, ExportSave); + IodineGUI.Iodine.attachSpeedHandler(function (speed) { + speed = speed.toFixed(2); + if (speed != IodineGUI.currentSpeed[1]) { + IodineGUI.currentSpeed[1] = speed; + IodineGUI.currentSpeed[0] = true; + } + }); + addEvent("change", document.getElementById("volume"), volChangeFunc); + addEvent("input", document.getElementById("volume"), volChangeFunc); + addEvent("resize", window, resizeCanvasFunc); + addEvent("mouseover", document.getElementById("saves_menu"), rebuildSavesMenu); + if (typeof document.hidden !== "undefined") { + addEvent("visibilitychange", document, visibilityChangeHandle); + } + else if (typeof document.mozHidden !== "undefined") { + addEvent("mozvisibilitychange", document, mozVisibilityChangeHandle); + } + else if (typeof document.msHidden !== "undefined") { + addEvent("msvisibilitychange", document, msVisibilityChangeHandle); + } + else if (typeof document.webkitHidden !== "undefined") { + addEvent("webkitvisibilitychange", document, webkitVisibilityChangeHandle); + } + //Run on init as well: + resizeCanvasFunc(); +} +function registerDefaultSettings() { + if (findValue("sound") === null) { + setValue("sound", !!IodineGUI.defaults.sound); + } + else { + IodineGUI.defaults.sound = !!findValue("sound"); + } + if (findValue("volume") === null) { + setValue("volume", +IodineGUI.defaults.volume); + } + else { + IodineGUI.defaults.volume = +findValue("volume"); + } + document.getElementById("volume").value = Math.round(IodineGUI.defaults.volume * 100); + document.getElementById("speedset").value = 50; + if (findValue("skipBoot") === null) { + setValue("skipBoot", !!IodineGUI.defaults.skipBoot); + } + else { + IodineGUI.defaults.skipBoot = !!findValue("skipBoot"); + } + if (findValue("toggleSmoothScaling") === null) { + setValue("toggleSmoothScaling", !!IodineGUI.defaults.toggleSmoothScaling); + } + else { + IodineGUI.defaults.toggleSmoothScaling = !!findValue("toggleSmoothScaling"); + } + if (findValue("toggleDynamicSpeed") === null) { + setValue("toggleDynamicSpeed", !!IodineGUI.defaults.toggleDynamicSpeed); + } + else { + IodineGUI.defaults.toggleDynamicSpeed = !!findValue("toggleDynamicSpeed"); + } + if (findValue("toggleOffthreadGraphics") === null) { + setValue("toggleOffthreadGraphics", !!IodineGUI.defaults.toggleOffthreadGraphics); + } + else { + IodineGUI.defaults.toggleOffthreadGraphics = !!findValue("toggleOffthreadGraphics"); + } + if (findValue("toggleOffthreadCPU") === null) { + setValue("toggleOffthreadCPU", !!IodineGUI.defaults.toggleOffthreadCPU); + } + else { + IodineGUI.defaults.toggleOffthreadCPU = !!findValue("toggleOffthreadCPU"); + } + // Why is this even a thing??? + /*if (findValue("key_a") === null) { + setValue("key_a", IodineGUI.defaults.keyZonesGBA[0] | 0); + } + else { + IodineGUI.defaults.keyZonesGBA[0] = findValue("key_a"); + } + if (findValue("key_b") === null) { + setValue("key_b", IodineGUI.defaults.keyZonesGBA[1] | 0); + } + else { + IodineGUI.defaults.keyZonesGBA[1] = findValue("key_b"); + } + if (findValue("key_select") === null) { + setValue("key_select", IodineGUI.defaults.keyZonesGBA[2] | 0); + } + else { + IodineGUI.defaults.keyZonesGBA[2] = findValue("key_select"); + } + if (findValue("key_start") === null) { + setValue("key_start", IodineGUI.defaults.keyZonesGBA[3] | 0); + } + else { + IodineGUI.defaults.keyZonesGBA[3] = findValue("key_start"); + } + if (findValue("key_right") === null) { + setValue("key_right", IodineGUI.defaults.keyZonesGBA[4] | 0); + } + else { + IodineGUI.defaults.keyZonesGBA[4] = findValue("key_right"); + } + if (findValue("key_left") === null) { + setValue("key_left", IodineGUI.defaults.keyZonesGBA[5] | 0); + } + else { + IodineGUI.defaults.keyZonesGBA[5] = findValue("key_left"); + } + if (findValue("key_up") === null) { + setValue("key_up", IodineGUI.defaults.keyZonesGBA[6] | 0); + } + else { + IodineGUI.defaults.keyZonesGBA[6] = findValue("key_up"); + } + if (findValue("key_down") === null) { + setValue("key_down", IodineGUI.defaults.keyZonesGBA[7] | 0); + } + else { + IodineGUI.defaults.keyZonesGBA[7] = findValue("key_down"); + } + if (findValue("key_r") === null) { + setValue("key_r", IodineGUI.defaults.keyZonesGBA[8] | 0); + } + else { + IodineGUI.defaults.keyZonesGBA[8] = findValue("key_r"); + } + if (findValue("key_l") === null) { + setValue("key_l", IodineGUI.defaults.keyZonesGBA[9] | 0); + } + else { + IodineGUI.defaults.keyZonesGBA[9] = findValue("key_l"); + }*/ + if (findValue("key_volumedown") === null) { + setValue("key_volumedown", IodineGUI.defaults.keyZonesControl[0] | 0); + } + else { + IodineGUI.defaults.keyZonesControl[0] = findValue("key_volumedown"); + } + if (findValue("key_volumeup") === null) { + setValue("key_volumeup", IodineGUI.defaults.keyZonesControl[1] | 0); + } + else { + IodineGUI.defaults.keyZonesControl[1] = findValue("key_volumeup"); + } + if (findValue("key_speedup") === null) { + setValue("key_speedup", IodineGUI.defaults.keyZonesControl[2] | 0); + } + else { + IodineGUI.defaults.keyZonesControl[2] = findValue("key_speedup"); + } + if (findValue("key_slowdown") === null) { + setValue("key_slowdown", IodineGUI.defaults.keyZonesControl[3] | 0); + } + else { + IodineGUI.defaults.keyZonesControl[3] = findValue("key_slowdown"); + } + if (findValue("key_speedreset") === null) { + setValue("key_speedreset", IodineGUI.defaults.keyZonesControl[4] | 0); + } + else { + IodineGUI.defaults.keyZonesControl[4] = findValue("key_speedreset"); + } + if (findValue("key_fullscreen") === null) { + setValue("key_fullscreen", IodineGUI.defaults.keyZonesControl[5] | 0); + } + else { + IodineGUI.defaults.keyZonesControl[5] = findValue("key_fullscreen"); + } + if (findValue("key_playpause") === null) { + setValue("key_playpause", IodineGUI.defaults.keyZonesControl[6] | 0); + } + else { + IodineGUI.defaults.keyZonesControl[6] = findValue("key_playpause"); + } + if (findValue("key_restart") === null) { + setValue("key_restart", IodineGUI.defaults.keyZonesControl[7] | 0); + } + else { + IodineGUI.defaults.keyZonesControl[7] = findValue("key_restart"); + } +} +function saveKeyBindings() { + setValue("key_a", IodineGUI.defaults.keyZonesGBA[0] | 0); + setValue("key_b", IodineGUI.defaults.keyZonesGBA[1] | 0); + setValue("key_select", IodineGUI.defaults.keyZonesGBA[2] | 0); + setValue("key_start", IodineGUI.defaults.keyZonesGBA[3] | 0); + setValue("key_right", IodineGUI.defaults.keyZonesGBA[4] | 0); + setValue("key_left", IodineGUI.defaults.keyZonesGBA[5] | 0); + setValue("key_up", IodineGUI.defaults.keyZonesGBA[6] | 0); + setValue("key_down", IodineGUI.defaults.keyZonesGBA[7] | 0); + setValue("key_r", IodineGUI.defaults.keyZonesGBA[8] | 0); + setValue("key_l", IodineGUI.defaults.keyZonesGBA[9] | 0); + setValue("key_volumedown", IodineGUI.defaults.keyZonesControl[0] | 0); + setValue("key_volumeup", IodineGUI.defaults.keyZonesControl[1] | 0); + setValue("key_speedup", IodineGUI.defaults.keyZonesControl[2] | 0); + setValue("key_slowdown", IodineGUI.defaults.keyZonesControl[3] | 0); + setValue("key_speedreset", IodineGUI.defaults.keyZonesControl[4] | 0); + setValue("key_fullscreen", IodineGUI.defaults.keyZonesControl[5] | 0); + setValue("key_playpause", IodineGUI.defaults.keyZonesControl[6] | 0); + setValue("key_restart", IodineGUI.defaults.keyZonesControl[7] | 0); +} +function registerGUISettings() { + document.getElementById("sound").checked = IodineGUI.defaults.sound; + if (IodineGUI.defaults.sound) { + IodineGUI.Iodine.enableAudio(); + } + try { + var volControl = document.getElementById("volume"); + volControl.min = 0; + volControl.max = 100; + volControl.step = 1; + volControl.value = IodineGUI.defaults.volume * 100; + } + catch (e) {} + IodineGUI.mixerInput.setVolume(IodineGUI.defaults.volume); + document.getElementById("skip_boot").checked = IodineGUI.defaults.skipBoot; + IodineGUI.Iodine.toggleSkipBootROM(IodineGUI.defaults.skipBoot); + document.getElementById("toggleSmoothScaling").checked = IodineGUI.defaults.toggleSmoothScaling; + IodineGUI.Blitter.setSmoothScaling(IodineGUI.defaults.toggleSmoothScaling); + document.getElementById("toggleDynamicSpeed").checked = IodineGUI.defaults.toggleDynamicSpeed; + IodineGUI.Iodine.toggleDynamicSpeed(IodineGUI.defaults.toggleDynamicSpeed); + document.getElementById("offthread-gpu").checked = IodineGUI.defaults.toggleOffthreadGraphics; + IodineGUI.Iodine.toggleOffthreadGraphics(IodineGUI.defaults.toggleOffthreadGraphics); + document.getElementById("offthread-cpu").checked = IodineGUI.defaults.toggleOffthreadCPU; + if (typeof SharedArrayBuffer != "function" || typeof Atomics != "object") { + document.getElementById("offthread-gpu").disabled = true; + document.getElementById("offthread-cpu").disabled = true; + } +} +function updatePlayButton(isPlaying) { + isPlaying = isPlaying | 0; + if ((isPlaying | 0) == 1) { + document.getElementById("play").className = "hide"; + document.getElementById("pause").className = "show"; + document.getElementById("menu").className = "playing"; + if (!IodineGUI.coreTimerID) { + startTimer(); + } + IodineGUI.isPlaying = true; + } + else { + document.getElementById("pause").className = "hide"; + document.getElementById("play").className = "show"; + document.getElementById("menu").className = "paused"; + if (IodineGUI.coreTimerID) { + clearInterval(IodineGUI.coreTimerID); + IodineGUI.coreTimerID = null; + } + IodineGUI.isPlaying = false; + } +} +function visibilityChangeHandle() { + processVisibilityChange(document.hidden); +} +function mozVisibilityChangeHandle() { + processVisibilityChange(document.mozHidden); +} +function msVisibilityChangeHandle() { + processVisibilityChange(document.msHidden); +} +function webkitVisibilityChangeHandle() { + processVisibilityChange(document.webkitHidden); +} +function processVisibilityChange(isHidden) { + if (!isHidden) { + if (IodineGUI.suspended) { + IodineGUI.suspended = false; + IodineGUI.Iodine.play(); + } + } + else { + if (document.getElementById("play").className == "hide") { + IodineGUI.Iodine.pause(); + IodineGUI.suspended = true; + } + } +} +function stepVolume(delta) { + var volume = document.getElementById("volume").value / 100; + volume = Math.min(Math.max(volume + delta, 0), 1); + IodineGUI.mixerInput.setVolume(volume); + document.getElementById("volume").value = Math.round(volume * 100); +} +function volChangeFunc() { + var volume = Math.min(Math.max(parseInt(this.value), 0), 100) * 0.01; + setValue("volume", +volume); + IodineGUI.mixerInput.setVolume(+volume); +}; +function speedChangeFunc() { + var speed = Math.min(Math.max(parseInt(this.value), 0), 100) / 50; + speed = speed * speed; + IodineGUI.Iodine.setSpeed(+speed); +} +function writeRedTemporaryText(textString) { + //lol + /*if (IodineGUI.GUITimerID) { + clearTimeout(IodineGUI.GUITimerID); + } + document.getElementById("tempMessage").style.display = "block"; + document.getElementById("tempMessage").textContent = textString; + IodineGUI.GUITimerID = setTimeout(clearTempString, 5000);*/ +} +function clearTempString() { + document.getElementById("tempMessage").style.display = "none"; +} +function resizeCanvasFunc() { + var container = document.getElementById("main"); + var containerHeight = container.clientHeight || container.offsetHeight || 0; + var containerWidth = container.clientWidth || container.offsetWidth || 0; + if (containerHeight > 0 && containerWidth > 0) { + var canvas = document.getElementById("emulator_target"); + var maxWidth = Math.floor(containerHeight * 1.5); + var maxHeight = Math.floor(containerWidth / 1.5); + var height = Math.min(maxHeight, containerHeight); + var width = Math.min(maxWidth, containerWidth); + canvas.style.width = width + "px"; + canvas.style.height = height + "px"; + } +} +function rebuildSavesMenu(e) { + if (didNotEnter(document.getElementById("saves_menu_container"), e)) { + ExportSave(); + rebuildExistingSaves(); + if (e.preventDefault) { + e.preventDefault(); + } + } +} +function rebuildExistingSaves() { + var menu = document.getElementById("existing_saves_list"); + ExportSave(); + removeChildNodes(menu); + var keys = getSavesKeys(); + while (keys.length > 0) { + addExistingSaveItem(menu, keys.shift()); + } +} +function addExistingSaveItem(menu, key) { + var listItem = document.createElement("li"); + listItem.className = "nowrap"; + var spanItem = document.createElement("span"); + spanItem.textContent = decodeKeyType(key); + listItem.appendChild(spanItem); + var submenu = document.createElement("ul"); + var submenuItem = document.createElement("li"); + submenuItem.className = "nowrap"; + addEvent("click", submenuItem, function () { + deleteValue(key); + rebuildExistingSaves(); + }); + var submenuSpan = document.createElement("span"); + submenuSpan.textContent = "Delete"; + submenuItem.appendChild(submenuSpan); + submenu.appendChild(submenuItem); + var submenuItem2 = document.createElement("li"); + submenuItem2.className = "nowrap"; + var link1 = document.createElement("a"); + link1.href = "data:application/octet-stream;base64," + base64(generateBlob(key, findValue(key))); + link1.download = key + "_" + ((new Date()).getTime()) + ".export"; + link1.textContent = "Download as import compatible"; + submenuItem2.appendChild(link1); + submenu.appendChild(submenuItem2); + var submenuItem3 = document.createElement("li"); + submenuItem3.className = "nowrap"; + var link2 = document.createElement("a"); + //Saves are already encoded in base64: + link2.href = "data:application/octet-stream;base64," + findValue(key); + link2.download = key + "_" + ((new Date()).getTime()) + ".sav"; + link2.textContent = "Download as raw binary"; + submenuItem3.appendChild(link2); + submenu.appendChild(submenuItem3); + listItem.appendChild(submenu); + menu.appendChild(listItem); +} +function decodeKeyType(key) { + if (key.substring(0, 15) == "SAVE_TYPE_GUID_") { + return "Game \"" + key.substring(15) + "\" Type Code"; + } + else if (key.substring(0, 10) == "SAVE_GUID_") { + return "Game \"" + key.substring(10) + "\" Cartridge Data"; + } + else if (key.substring(0, 15) == "SAVE_RTC_GUID_") { + return "Game \"" + key.substring(15) + "\" RTC Data"; + } + return key; +} +//Some wrappers and extensions for non-DOM3 browsers: +function removeChildNodes(node) { + while (node.firstChild) { + node.removeChild(node.firstChild); + } +} +function didNotEnter(oElement, event) { + var target = (typeof event.target != "undefined") ? event.target : event.srcElement; + while (target) { + if (isSameNode(target, oElement)) { + return false; + } + target = target.parentElement; + } + return true; +} +function isSameNode(oCheck1, oCheck2) { + return (typeof oCheck1.isSameNode == "function") ? oCheck1.isSameNode(oCheck2) : (oCheck1 === oCheck2); +} +function addEvent(sEvent, oElement, fListener) { + try { + oElement.addEventListener(sEvent, fListener, false); + } + catch (error) { + oElement.attachEvent("on" + sEvent, fListener); //Pity for IE. + } +} +function removeEvent(sEvent, oElement, fListener) { + try { + oElement.removeEventListener(sEvent, fListener, false); + } + catch (error) { + oElement.detachEvent("on" + sEvent, fListener); //Pity for IE. + } +} + +function setSmooth(){ + var chekd = document.getElementById("smooth").checked;setValue("toggleSmoothScaling", !!chekd); + if (IodineGUI.Blitter) { + IodineGUI.Blitter.setSmoothScaling(chekd); + } +} \ No newline at end of file diff --git a/public/gfiles/gba/user_scripts/GfxGlueCode.js b/public/gfiles/gba/user_scripts/GfxGlueCode.js new file mode 100644 index 00000000..ad220f71 --- /dev/null +++ b/public/gfiles/gba/user_scripts/GfxGlueCode.js @@ -0,0 +1,209 @@ +"use strict"; +/* + Copyright (C) 2010-2016 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function GfxGlueCode(width, height) { + this.graphicsFound = false; //Do we have graphics output sink found yet? + this.gfxCallback = null; //Optional callback user-supplied for vsync eventing. + this.doSmoothing = true; //Texture filter the framebuffer? + this.offscreenWidth = width; //Width of the screen. + this.offscreenHeight = height; //Height of the screen. + this.offscreenRGBCount = this.offscreenWidth * this.offscreenHeight * 3; + this.offscreenRGBACount = this.offscreenWidth * this.offscreenHeight * 4; + this.initializeVSync(); //Setup the vsync event. + this.initializeBuffers(); //Initialize the buffer storage. +} +GfxGlueCode.prototype.initializeVSync = function () { + window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; + var parentObj = this; + if (!window.requestAnimationFrame) { + //Fallback timer eventing: + setInterval(function () { + parentObj.vsync(); + }, 16); + } + else { + //Initialize the rAF eventing: + window.requestAnimationFrame( + function () { + parentObj.vsync(); + parentObj.rAFKeepAlive(); + } + ) + } +} +GfxGlueCode.prototype.rAFKeepAlive = function () { + //Keep the vsync event requested: + var parentObj = this; + window.requestAnimationFrame(function () { + parentObj.vsync(); + parentObj.rAFKeepAlive(); + }); +} +GfxGlueCode.prototype.attachCanvas = function (canvas) { + this.canvas = canvas; + this.graphicsFound = this.initializeCanvasTarget(); +} +GfxGlueCode.prototype.detachCanvas = function () { + this.canvas = null; +} +GfxGlueCode.prototype.attachGfxCallback = function (gfxCallback) { + if (typeof gfxCallback == "function") { + this.gfxCallback = gfxCallback; + } +} +GfxGlueCode.prototype.attachGfxPostCallback = function (gfxPostCallback) { + if (typeof gfxPostCallback == "function") { + this.gfxPostCallback = gfxPostCallback; + } +} +GfxGlueCode.prototype.vsync = function () { + if (this.graphicsFound) { + if (typeof this.gfxCallback == "function") { + //Let the user supplied code prepare a frame or two: + this.gfxCallback(); + } + //Draw a frame, if ready: + this.requestDraw(); + } +} +GfxGlueCode.prototype.initializeBuffers = function () { + this.swizzledFrameFree = [getUint8Array(this.offscreenRGBCount), getUint8Array(this.offscreenRGBCount)]; + this.swizzledFrameReady = []; +} +GfxGlueCode.prototype.recomputeDimension = function () { + //Cache some dimension info: + this.canvasLastWidth = this.canvas.clientWidth; + this.canvasLastHeight = this.canvas.clientHeight; + if ((navigator.userAgent.toLowerCase().indexOf("gecko") != -1 && navigator.userAgent.toLowerCase().indexOf("like gecko") == -1)) { //Sniff out firefox for selecting this path. + //Set target as unscaled: + this.onscreenWidth = this.canvas.width = this.offscreenWidth; + this.onscreenHeight = this.canvas.height = this.offscreenHeight; + } + else { + //Set target canvas as scaled: + this.onscreenWidth = this.canvas.width = this.canvas.clientWidth; + this.onscreenHeight = this.canvas.height = this.canvas.clientHeight; + } +} +GfxGlueCode.prototype.initializeCanvasTarget = function () { + try { + //Obtain dimensional information: + this.recomputeDimension(); + //Get handles on the canvases: + this.canvasOffscreen = document.createElement("canvas"); + this.canvasOffscreen.width = this.offscreenWidth; + this.canvasOffscreen.height = this.offscreenHeight; + this.drawContextOffscreen = this.canvasOffscreen.getContext("2d"); + this.drawContextOnscreen = this.canvas.getContext("2d"); + //Initialize the canvas backing buffer: + this.initializeCanvasBuffer(); + //Success: + return true; + } + catch (error) { + //Failure: + return false; + } +} +GfxGlueCode.prototype.initializeCanvasBuffer = function () { + //Get a CanvasPixelArray buffer: + this.canvasBuffer = this.getBuffer(this.drawContextOffscreen, this.offscreenWidth, this.offscreenHeight); + //Initialize Alpha Channel: + this.initializeAlpha(this.canvasBuffer.data); +} +GfxGlueCode.prototype.initializeAlpha = function (canvasData) { + var length = canvasData.length; + for (var indexGFXIterate = 3; indexGFXIterate < length; indexGFXIterate += 4) { + canvasData[indexGFXIterate] = 0xFF; + } +} +GfxGlueCode.prototype.getBuffer = function (canvasContext, width, height) { + //Get a CanvasPixelArray buffer: + var buffer = null; + try { + buffer = this.drawContextOffscreen.createImageData(width, height); + } + catch (error) { + buffer = this.drawContextOffscreen.getImageData(0, 0, width, height); + } + return buffer; +} +if (__VIEWS_SUPPORTED__) { + GfxGlueCode.prototype.copyBuffer = function (buffer) { + if (this.graphicsFound) { + if (this.swizzledFrameFree.length == 0) { + this.swizzledFrameFree.push(this.swizzledFrameReady.shift()); + } + var swizzledFrame = this.swizzledFrameFree.shift(); + swizzledFrame.set(buffer); + this.swizzledFrameReady.push(swizzledFrame); + } + } +} +else { + GfxGlueCode.prototype.copyBuffer = function (buffer) { + if (this.graphicsFound) { + if (this.swizzledFrameFree.length == 0) { + this.swizzledFrameFree.push(this.swizzledFrameReady.shift()); + } + var swizzledFrame = this.swizzledFrameFree.shift(); + for (var bufferIndex = 0; bufferIndex < this.offscreenRGBCount; bufferIndex++) { + swizzledFrame[bufferIndex] = buffer[bufferIndex]; + } + this.swizzledFrameReady.push(swizzledFrame); + } + } +} +GfxGlueCode.prototype.requestDraw = function () { + if (this.swizzledFrameReady.length > 0) { + var canvasData = this.canvasBuffer.data; + var swizzledFrame = this.swizzledFrameReady.shift(); + for (var canvasIndex = 0, bufferIndex = 0; canvasIndex < this.offscreenRGBACount; ++canvasIndex) { + canvasData[canvasIndex++] = swizzledFrame[bufferIndex++]; + canvasData[canvasIndex++] = swizzledFrame[bufferIndex++]; + canvasData[canvasIndex++] = swizzledFrame[bufferIndex++]; + } + this.swizzledFrameFree.push(swizzledFrame); + this.graphicsBlit(); + if (typeof this.gfxPostCallback == "function") { + //Some UI element redraw: + this.gfxPostCallback(); + } + } +} +GfxGlueCode.prototype.graphicsBlit = function () { + if (this.canvasLastWidth != this.canvas.clientWidth || this.canvasLastHeight != this.canvas.clientHeight) { + this.recomputeDimension(); + this.processSmoothing(); + } + if (this.offscreenWidth == this.onscreenWidth && this.offscreenHeight == this.onscreenHeight) { + //Canvas does not need to scale, draw directly to final: + this.drawContextOnscreen.putImageData(this.canvasBuffer, 0, 0); + } + else { + //Canvas needs to scale, draw to offscreen first: + this.drawContextOffscreen.putImageData(this.canvasBuffer, 0, 0); + //Scale offscreen canvas image onto the final: + this.drawContextOnscreen.drawImage(this.canvasOffscreen, 0, 0, this.onscreenWidth, this.onscreenHeight); + } +} +GfxGlueCode.prototype.setSmoothScaling = function (doSmoothing) { + this.doSmoothing = !!doSmoothing; + this.processSmoothing(); +} +GfxGlueCode.prototype.processSmoothing = function () { + if (this.graphicsFound) { + this.canvas.className = (this.doSmoothing) ? "textureSmooth" : "texturePixelated"; + this.drawContextOnscreen.mozImageSmoothingEnabled = this.doSmoothing; + this.drawContextOnscreen.webkitImageSmoothingEnabled = this.doSmoothing; + this.drawContextOnscreen.imageSmoothingEnabled = this.doSmoothing; + } +} diff --git a/public/gfiles/gba/user_scripts/JoyPadGlueCode.js b/public/gfiles/gba/user_scripts/JoyPadGlueCode.js new file mode 100644 index 00000000..08e6750c --- /dev/null +++ b/public/gfiles/gba/user_scripts/JoyPadGlueCode.js @@ -0,0 +1,124 @@ +"use strict"; +/* + Copyright (C) 2012-2016 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function keyDown(e) { + var keyCode = e.keyCode | 0; + for (var keyMapIndex = 0; (keyMapIndex | 0) < 10; keyMapIndex = ((keyMapIndex | 0) + 1) | 0) { + if ((IodineGUI.defaults.keyZonesGBA[keyMapIndex | 0] | 0) == (keyCode | 0)) { + IodineGUI.Iodine.keyDown(keyMapIndex | 0); + if (e.preventDefault) { + e.preventDefault(); + } + return; + } + } +} +function keyUpGBA(keyCode) { + keyCode = keyCode | 0; + for (var keyMapIndex = 0; (keyMapIndex | 0) < 10; keyMapIndex = ((keyMapIndex | 0) + 1) | 0) { + if ((IodineGUI.defaults.keyZonesGBA[keyMapIndex | 0] | 0) == (keyCode | 0)) { + IodineGUI.Iodine.keyUp(keyMapIndex | 0); + return; + } + } +} +function keyUp(keyCode) { + keyCode = keyCode | 0; + for (var keyMapIndex = 0; (keyMapIndex | 0) < 8; keyMapIndex = ((keyMapIndex | 0) + 1) | 0) { + if ((IodineGUI.defaults.keyZonesControl[keyMapIndex | 0] | 0) == (keyCode | 0)) { + keyboardEmulatorControl(keyMapIndex | 0); + return true; + } + } + return false; +} +function keyUpPreprocess(e) { + var keyCode = e.keyCode | 0; + //If we're not mapping a key: + if (!IodineGUI.toMap) { + //Check for emulator binding: + if (!keyUp(keyCode | 0)) { + //Check for GBA binding: + keyUpGBA(keyCode); + } + } + else { + //Map a key binding: + IodineGUI.toMap[IodineGUI.toMapIndice | 0] = keyCode | 0; + IodineGUI.toMap = null; + saveKeyBindings(); + } +} +function keyboardEmulatorControl(keyCode) { + keyCode = keyCode | 0; + switch (keyCode | 0) { + case 0: + stepVolume(-0.04); + break; + case 1: + stepVolume(0.04); + break; + case 2: + IodineGUI.Iodine.incrementSpeed(0.05); + break; + case 3: + IodineGUI.Iodine.incrementSpeed(-0.05); + break; + case 4: + IodineGUI.Iodine.setSpeed(1); + break; + case 5: + toggleFullScreen(); + break; + case 6: + togglePlayState(); + break; + case 7: + IodineGUI.Iodine.restart(); + } +} +function toggleFullScreen() { + if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement ) { + if (document.documentElement.requestFullscreen) { + document.documentElement.requestFullscreen(); + } + else if (document.documentElement.msRequestFullscreen) { + document.documentElement.msRequestFullscreen(); + } + else if (document.documentElement.mozRequestFullScreen) { + document.documentElement.mozRequestFullScreen(); + } + else if (document.documentElement.webkitRequestFullscreen) { + document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + } + } + else { + if (document.exitFullscreen) { + document.exitFullscreen(); + } + else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } + else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } + else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } + } +} +function togglePlayState() { + if (IodineGUI.isPlaying) { + IodineGUI.Iodine.pause(); + } + else { + IodineGUI.Iodine.play(); + } +} \ No newline at end of file diff --git a/public/gfiles/gba/user_scripts/ROMLoadGlueCode.js b/public/gfiles/gba/user_scripts/ROMLoadGlueCode.js new file mode 100644 index 00000000..aa1fba51 --- /dev/null +++ b/public/gfiles/gba/user_scripts/ROMLoadGlueCode.js @@ -0,0 +1,115 @@ +"use strict"; +/* + Copyright (C) 2012-2016 Grant Galitz + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function fileThings() { + if(window.location.hash){ + downloadBIOS(); + downloadROM(); + } else { + document.getElementById('ffd').style.display = "block"; + downloadBIOS(); + } +} +function yayPlay() { + document.getElementById("play").click(); +} +function gba_upload(upload) { + document.getElementById('ffd').style.display = "none"; + fileLoadShimCode(upload.files, attachROM); +} +function downloadBIOS() { + downloadFile("bios.bin", "BIOS"); +} +function downloadROM() { + var romloc = "./roms/" + window.location.hash.substring(1) + ".gba"; + downloadFile(romloc, "ROM"); +} +function attachBIOS(BIOS) { + try { + IodineGUI.Iodine.attachBIOS(new Uint8Array(BIOS)); + } + catch (error) { + IodineGUI.Iodine.attachBIOS(BIOS); + } +} +function attachROM(ROM) { + try { + IodineGUI.Iodine.attachROM(new Uint8Array(ROM)); + } + catch (error) { + IodineGUI.Iodine.attachROM(ROM); + } +} +function fileLoadShimCode(files, ROMHandler) { + if (typeof files != "undefined") { + if (files.length >= 1) { + //Gecko 1.9.2+ (Standard Method) + try { + var binaryHandle = new FileReader(); + binaryHandle.onloadend = function () { + ROMHandler(this.result); + yayPlay(); + } + binaryHandle.readAsArrayBuffer(files[files.length - 1]); + } + catch (error) { + try { + var result = files[files.length - 1].getAsBinary(); + var resultConverted = []; + for (var index = 0; index < result.length; ++index) { + resultConverted[index] = result.charCodeAt(index) & 0xFF; + } + ROMHandler(resultConverted); + yayPlay(); + } + catch (error) { + alert("Could not load the processed ROM file!"); + } + } + } + } +} +function fileLoadBIOS() { + fileLoadShimCode(this.files, attachBIOS); +} +function fileLoadROM() { + fileLoadShimCode(this.files, attachROM); +} +function downloadFile(fileName, reg) { + var ajax = new XMLHttpRequest(); + ajax.onload = function(){ + if(ajax.status=="404"){ + document.getElementById('ffd').style.display = "block"; + alert("Could not find " + fileName.substring(2)); + } else { + if(reg=="BIOS"){ + processDownload(this, attachBIOS); + } else if(reg=="ROM"){ + processDownload(this, attachROM); + yayPlay(); + } + } + } + ajax.open("GET", "./" + fileName, true); + ajax.responseType = "arraybuffer"; + ajax.overrideMimeType("text/plain; charset=x-user-defined"); + ajax.send(null); +} +function processDownload(parentObj, attachHandler) { + try { + attachHandler(new Uint8Array(parentObj.response)); + } + catch (error) { + var data = parentObj.responseText; + var length = data.length; + var dataArray = []; + for (var index = 0; index < length; index++) { + dataArray[index] = data.charCodeAt(index) & 0xFF; + } + attachHandler(dataArray); + } +} \ No newline at end of file diff --git a/public/gfiles/gba/user_scripts/SavesGlueCode.js b/public/gfiles/gba/user_scripts/SavesGlueCode.js new file mode 100644 index 00000000..105d646d --- /dev/null +++ b/public/gfiles/gba/user_scripts/SavesGlueCode.js @@ -0,0 +1,289 @@ +"use strict"; +/* + Copyright (C) 2010-2017 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function ImportSaveCallback(name, callbackFunc, callbackFuncNoSave) { + try { + var save = findValue("SAVE_" + name); + if (save != null) { + writeRedTemporaryText("Loaded save."); + callbackFunc(base64ToArray(save)); + return; + } + } + catch (error) { + writeRedTemporaryText("Could not read save: " + error.message); + } + callbackFuncNoSave(); +} +function ExportSave() { + IodineGUI.Iodine.exportSave(); +} +function ExportSaveCallback(name, save) { + if (name != "") { + try { + setValue("SAVE_" + name, arrayToBase64(save)); + } + catch (error) { + writeRedTemporaryText("Could not store save: " + error.message); + } + } +} +function registerSaveHandlers() { + IodineGUI.Iodine.attachSaveExportHandler(ExportSaveCallback); + IodineGUI.Iodine.attachSaveImportHandler(ImportSaveCallback); +} +function import_save(blobData) { + blobData = decodeBlob(blobData); + if (blobData && blobData.blobs) { + if (blobData.blobs.length > 0) { + for (var index = 0; index < blobData.blobs.length; ++index) { + var blobID = blobData.blobs[index].blobID; + var blobContent = blobData.blobs[index].blobContent; + writeRedTemporaryText("Importing blob \"" + blobID + "\""); + if (blobContent) { + setValue(blobID, blobContent); + } + else if (blobID) { + writeRedTemporaryText("Save file imported had blob \"" + blobID + "\" with no blob data interpretable."); + } + else { + writeRedTemporaryText("Blob chunk information missing completely."); + } + } + } + else { + writeRedTemporaryText("Could not decode the imported file."); + } + } + else { + writeRedTemporaryText("Could not decode the imported file."); + } +} +function generateBlob(keyName, encodedData) { + //Append the file format prefix: + var saveString = "EMULATOR_DATA"; + var consoleID = "GameBoyAdvance"; + //Figure out the length: + var totalLength = (saveString.length + 4 + (1 + consoleID.length)) + ((1 + keyName.length) + (4 + encodedData.length)); + //Append the total length in bytes: + saveString += to_little_endian_word(totalLength); + //Append the console ID text's length: + saveString += to_byte(consoleID.length); + //Append the console ID text: + saveString += consoleID; + //Append the blob ID: + saveString += to_byte(keyName.length); + saveString += keyName; + //Now append the save data: + saveString += to_little_endian_word(encodedData.length); + saveString += encodedData; + return saveString; +} +function generateMultiBlob(blobPairs) { + var consoleID = "GameBoyAdvance"; + //Figure out the initial length: + var totalLength = 13 + 4 + 1 + consoleID.length; + //Append the console ID text's length: + var saveString = to_byte(consoleID.length); + //Append the console ID text: + saveString += consoleID; + var keyName = ""; + var encodedData = ""; + //Now append all the blobs: + for (var index = 0; index < blobPairs.length; ++index) { + keyName = blobPairs[index][0]; + encodedData = blobPairs[index][1]; + //Append the blob ID: + saveString += to_byte(keyName.length); + saveString += keyName; + //Now append the save data: + saveString += to_little_endian_word(encodedData.length); + saveString += encodedData; + //Update the total length: + totalLength += 1 + keyName.length + 4 + encodedData.length; + } + //Now add the prefix: + saveString = "EMULATOR_DATA" + to_little_endian_word(totalLength) + saveString; + return saveString; +} +function decodeBlob(blobData) { + /*Format is as follows: + - 13 byte string "EMULATOR_DATA" + - 4 byte total size (including these 4 bytes). + - 1 byte Console type ID length + - Console type ID text of 8 bit size + blobs { + - 1 byte blob ID length + - blob ID text (Used to say what the data is (SRAM/freeze state/etc...)) + - 4 byte blob length + - blob of 32 bit length size + - Blob itself is encoded in base64. + } + */ + var length = blobData.length; + var blobProperties = {}; + blobProperties.consoleID = null; + var blobsCount = -1; + blobProperties.blobs = []; + if (length > 17) { + if (blobData.substring(0, 13) == "EMULATOR_DATA") { + var length = Math.min(((blobData.charCodeAt(16) & 0xFF) << 24) | ((blobData.charCodeAt(15) & 0xFF) << 16) | ((blobData.charCodeAt(14) & 0xFF) << 8) | (blobData.charCodeAt(13) & 0xFF), length); + var consoleIDLength = blobData.charCodeAt(17) & 0xFF; + if (length > 17 + consoleIDLength) { + blobProperties.consoleID = blobData.substring(18, 18 + consoleIDLength); + var blobIDLength = 0; + var blobLength = 0; + for (var index = 18 + consoleIDLength; index < length;) { + blobIDLength = blobData.charCodeAt(index++) & 0xFF; + if (index + blobIDLength < length) { + blobProperties.blobs[++blobsCount] = {}; + blobProperties.blobs[blobsCount].blobID = blobData.substring(index, index + blobIDLength); + index += blobIDLength; + if (index + 4 < length) { + blobLength = ((blobData.charCodeAt(index + 3) & 0xFF) << 24) | ((blobData.charCodeAt(index + 2) & 0xFF) << 16) | ((blobData.charCodeAt(index + 1) & 0xFF) << 8) | (blobData.charCodeAt(index) & 0xFF); + index += 4; + if (index + blobLength <= length) { + blobProperties.blobs[blobsCount].blobContent = blobData.substring(index, index + blobLength); + index += blobLength; + } + else { + writeRedTemporaryText("Blob length check failed, blob determined to be incomplete."); + break; + } + } + else { + writeRedTemporaryText("Blob was incomplete, bailing out."); + break; + } + } + else { + writeRedTemporaryText("Blob was incomplete, bailing out."); + break; + } + } + } + } + } + return blobProperties; +} +function refreshStorageListing() { + var keys = getSavesKeys(); + var blobPairs = []; + for (var index = 0; index < keys.length; ++index) { + blobPairs[index] = [keys[index], findValue(keys[index])]; + } + this.href = "data:application/octet-stream;base64," + base64(generateMultiBlob(blobPairs)); + this.download = "gameboy_advance_saves_" + ((new Date()).getTime()) + ".export"; +} +function checkStorageLength() { + try { + if (window.localStorage) { + return window.localStorage.length; + } + } + catch (error) { + //An older Gecko 1.8.1/1.9.0 method of storage (Deprecated due to the obvious security hole): + if (window.globalStorage && location.hostname) { + return window.globalStorage[location.hostname].length; + } + } + return 0; +} +function getSavesKeys() { + var storageLength = checkStorageLength(); + var keysFound = []; + var index = 0; + var nextKey = null; + while (index < storageLength) { + nextKey = findKey(index++); + if (nextKey !== null && nextKey.length > 0) { + if (nextKey.substring(0, 15) == "IodineGBA_SAVE_") { + keysFound.push(nextKey.substring(10)); + } + } + else { + break; + } + } + return keysFound; +} +function findKey(keyNum) { + try { + if (window.localStorage) { + return window.localStorage.key(keyNum); + } + } + catch (error) { + //An older Gecko 1.8.1/1.9.0 method of storage (Deprecated due to the obvious security hole): + if (window.globalStorage && location.hostname) { + return window.globalStorage[location.hostname].key(keyNum); + } + } + return null; +} +function to_little_endian_word(str) { + return to_little_endian_hword(str) + to_little_endian_hword(str >> 16); +} +function to_little_endian_hword(str) { + return to_byte(str) + to_byte(str >> 8); +} +function to_byte(str) { + return String.fromCharCode(str & 0xFF); +} +//Wrapper for localStorage getItem, so that data can be retrieved in various types. +function findValue(key) { + key = "IodineGBA_" + key; + try { + if (window.localStorage) { + if (window.localStorage.getItem(key) != null) { + return JSON.parse(window.localStorage.getItem(key)); + } + } + } + catch (error) { + //An older Gecko 1.8.1/1.9.0 method of storage (Deprecated due to the obvious security hole): + if (window.globalStorage && location.hostname) { + if (window.globalStorage[location.hostname].getItem(key) != null) { + return JSON.parse(window.globalStorage[location.hostname].getItem(key)); + } + } + } + return null; +} +//Wrapper for localStorage setItem, so that data can be set in various types. +function setValue(key, value) { + key = "IodineGBA_" + key; + try { + if (window.localStorage) { + window.localStorage.setItem(key, JSON.stringify(value)); + } + } + catch (error) { + //An older Gecko 1.8.1/1.9.0 method of storage (Deprecated due to the obvious security hole): + if (window.globalStorage && location.hostname) { + window.globalStorage[location.hostname].setItem(key, JSON.stringify(value)); + } + } +} +//Wrapper for localStorage removeItem, so that data can be set in various types. +function deleteValue(key) { + key = "IodineGBA_" + key; + try { + if (window.localStorage) { + window.localStorage.removeItem(key); + } + } + catch (error) { + //An older Gecko 1.8.1/1.9.0 method of storage (Deprecated due to the obvious security hole): + if (window.globalStorage && location.hostname) { + window.globalStorage[location.hostname].removeItem(key); + } + } +} diff --git a/public/gfiles/gba/user_scripts/WorkerGfxGlueCode.js b/public/gfiles/gba/user_scripts/WorkerGfxGlueCode.js new file mode 100644 index 00000000..cfa08f5c --- /dev/null +++ b/public/gfiles/gba/user_scripts/WorkerGfxGlueCode.js @@ -0,0 +1,130 @@ +"use strict"; +/* + Copyright (C) 2012-2019 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +var gfxBuffers = null; +var gfxCounters = null; +function IodineGBAWorkerGfxShim() { + this.gfx = null; + gfxBuffers = [getSharedUint8Array(160 * 240 * 3), + getSharedUint8Array(160 * 240 * 3)]; + gfxCounters = getSharedInt32Array(3); + this.Iodine = new GameBoyAdvanceEmulator(); +} +IodineGBAWorkerGfxShim.prototype.play = function () { + this.Iodine.play(); +} +IodineGBAWorkerGfxShim.prototype.pause = function () { + this.Iodine.pause(); +} +IodineGBAWorkerGfxShim.prototype.restart = function () { + this.Iodine.restart(); +} +IodineGBAWorkerGfxShim.prototype.setIntervalRate = function (rate) { + rate = +rate; + this.Iodine.setIntervalRate(+rate); +} +IodineGBAWorkerGfxShim.prototype.timerCallback = function (timestamp) { + timestamp = timestamp >>> 0; + this.Iodine.timerCallback(timestamp); +} +IodineGBAWorkerGfxShim.prototype.attachGraphicsFrameHandler = function (gfx) { + this.gfx = gfx; + var parentObj = this; + this.gfx.attachGfxCallback(function () { + parentObj.graphicsHeartBeat(); + }); + this.Iodine.attachGraphicsFrameHandler(gfx); +} +IodineGBAWorkerGfxShim.prototype.attachAudioHandler = function (audio) { + this.Iodine.attachAudioHandler(audio); +} +IodineGBAWorkerGfxShim.prototype.enableAudio = function () { + this.Iodine.enableAudio(); +} +IodineGBAWorkerGfxShim.prototype.disableAudio = function () { + this.Iodine.disableAudio(); +} +IodineGBAWorkerGfxShim.prototype.toggleSkipBootROM = function (doEnable) { + doEnable = doEnable | 0; + this.Iodine.toggleSkipBootROM(doEnable | 0); +} +IodineGBAWorkerGfxShim.prototype.toggleDynamicSpeed = function (doEnable) { + doEnable = doEnable | 0; + this.Iodine.toggleDynamicSpeed(doEnable | 0); +} +IodineGBAWorkerGfxShim.prototype.toggleOffthreadGraphics = function (doEnable) { + doEnable = doEnable | 0; + this.Iodine.toggleOffthreadGraphics(doEnable | 0); +} +IodineGBAWorkerGfxShim.prototype.attachSpeedHandler = function (speed) { + this.Iodine.attachSpeedHandler(speed); +} +IodineGBAWorkerGfxShim.prototype.attachPlayStatusHandler = function (playStatus) { + this.Iodine.attachPlayStatusHandler(playStatus); +} +IodineGBAWorkerGfxShim.prototype.keyDown = function (keyCode) { + keyCode = keyCode | 0; + this.Iodine.keyDown(keyCode | 0); +} +IodineGBAWorkerGfxShim.prototype.keyUp = function (keyCode) { + keyCode = keyCode | 0; + this.Iodine.keyUp(keyCode | 0); +} +IodineGBAWorkerGfxShim.prototype.incrementSpeed = function (newSpeed) { + newSpeed = +newSpeed; + this.Iodine.incrementSpeed(+newSpeed); +} +IodineGBAWorkerGfxShim.prototype.setSpeed = function (newSpeed) { + newSpeed = +newSpeed; + this.Iodine.setSpeed(+newSpeed); +} +IodineGBAWorkerGfxShim.prototype.attachBIOS = function (BIOS) { + this.Iodine.attachBIOS(BIOS); +} +IodineGBAWorkerGfxShim.prototype.attachROM = function (ROM) { + this.Iodine.attachROM(ROM); +} +IodineGBAWorkerGfxShim.prototype.exportSave = function () { + this.Iodine.exportSave(); +} +IodineGBAWorkerGfxShim.prototype.attachSaveExportHandler = function (saveExport) { + this.Iodine.attachSaveExportHandler(saveExport); +} +IodineGBAWorkerGfxShim.prototype.attachSaveImportHandler = function (saveImport) { + this.Iodine.attachSaveImportHandler(saveImport); +} +IodineGBAWorkerGfxShim.prototype.graphicsHeartBeat = function () { + //If graphics callback handle provided and we got a buffer reference: + if (this.gfx && gfxCounters) { + //Copy the buffer out to local: + this.consumeGraphicsBuffer(); + //Wake up the producer thread: + Atomics.notify(gfxCounters, 2, 1); + } +} +IodineGBAWorkerGfxShim.prototype.consumeGraphicsBuffer = function () { + //Load the counter values: + var start = gfxCounters[0] | 0; //Written by this thread. + var end = Atomics.load(gfxCounters, 1) | 0; //Written by the other thread. + //Don't process if nothing to process: + if ((end | 0) == (start | 0)) { + //Buffer is empty: + return; + } + //Copy samples out from the ring buffer: + do { + //Hardcoded for 2 buffers for a triple buffer effect: + this.gfx.copyBuffer(gfxBuffers[start & 0x1]); + start = ((start | 0) + 1) | 0; + } while ((start | 0) != (end | 0)); + //Update the starting position counter to match the end position: + //Let the other Atomic loads/stores naturally flush this value: + gfxCounters[0] = end | 0; +} diff --git a/public/gfiles/gba/user_scripts/WorkerGlueCode.js b/public/gfiles/gba/user_scripts/WorkerGlueCode.js new file mode 100644 index 00000000..2a697eda --- /dev/null +++ b/public/gfiles/gba/user_scripts/WorkerGlueCode.js @@ -0,0 +1,328 @@ +"use strict"; +/* + Copyright (C) 2012-2019 Grant Galitz + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function IodineGBAWorkerShim() { + this.playStatus = null; + this.gfx = null; + this.audio = null; + this.speed = null; + this.saveExport = null; + this.saveImport = null; + this.worker = null; + this.gfxBuffers = null; + this.gfxCounters = null; + this.audioBuffer = null; + this.audioCounters = null; + this.audioSamplesRemaining = null; + this.audioBufferSize = 0; + this.audioBufferSizeMask = 0; + this.audioInitialized = false; + this.timestamp = null; + this.initialize(); +} +var tempvar = document.getElementsByTagName("script"); +IodineGBAWorkerShim.prototype.filepath = tempvar[tempvar.length-1].src; +IodineGBAWorkerShim.prototype.initialize = function () { + var parentObj = this; + var loc = this.filepath.split("/"); + loc = loc.slice(0, loc.length - 2).join("/"); + loc += "/IodineGBA/core/Worker.js"; + this.worker = new Worker(loc); + this.worker.onmessage = function (event) { + parentObj.decodeMessage(event.data); + } +} +IodineGBAWorkerShim.prototype.sendMessageSingle = function (eventCode) { + eventCode = eventCode | 0; + this.worker.postMessage({messageID:eventCode}); +} +IodineGBAWorkerShim.prototype.sendMessageDouble = function (eventCode, eventData) { + eventCode = eventCode | 0; + this.worker.postMessage({messageID:eventCode, payload:eventData}); +} +IodineGBAWorkerShim.prototype.play = function () { + this.sendMessageSingle(0); +} +IodineGBAWorkerShim.prototype.pause = function () { + this.sendMessageSingle(1); +} +IodineGBAWorkerShim.prototype.restart = function () { + this.sendMessageSingle(2); +} +IodineGBAWorkerShim.prototype.setIntervalRate = function (rate) { + rate = +rate; + this.sendMessageDouble(3, +rate); +} +IodineGBAWorkerShim.prototype.timerCallback = function (timestamp) { + timestamp = timestamp >>> 0; + //If memory location provided for timestamp buffering: + if (this.timestamp) { + //Forward latest timestamp to worker: + Atomics.store(this.timestamp, 0, timestamp >>> 0); + } +} +IodineGBAWorkerShim.prototype.attachPlayStatusHandler = function (playStatus) { + this.playStatus = playStatus; + this.sendMessageSingle(23); +} +IodineGBAWorkerShim.prototype.issuePlayStatus = function (isPlaying) { + isPlaying = isPlaying | 0; + if (this.playStatus) { + this.playStatus(isPlaying | 0); + } +} +IodineGBAWorkerShim.prototype.attachGraphicsFrameHandler = function (gfx) { + this.gfx = gfx; + var parentObj = this; + this.gfx.attachGfxCallback(function () { + parentObj.graphicsHeartBeat(); + }); + this.sendMessageSingle(4); +} +IodineGBAWorkerShim.prototype.attachAudioHandler = function (audio) { + this.audio = audio; + this.sendMessageSingle(5); +} +IodineGBAWorkerShim.prototype.enableAudio = function () { + if (this.audio) { + this.sendMessageSingle(6); + } +} +IodineGBAWorkerShim.prototype.disableAudio = function () { + if (this.audio) { + this.sendMessageSingle(7); + } +} +IodineGBAWorkerShim.prototype.toggleSkipBootROM = function (doEnable) { + doEnable = doEnable | 0; + this.sendMessageDouble(8, doEnable | 0); +} +IodineGBAWorkerShim.prototype.toggleDynamicSpeed = function (doEnable) { + doEnable = doEnable | 0; + this.sendMessageDouble(9, doEnable | 0); +} +IodineGBAWorkerShim.prototype.toggleOffthreadGraphics = function (doEnable) { + doEnable = doEnable | 0; + this.sendMessageDouble(22, doEnable | 0); +} +IodineGBAWorkerShim.prototype.attachSpeedHandler = function (speed) { + this.speed = speed; + this.sendMessageSingle(10); +} +IodineGBAWorkerShim.prototype.keyDown = function (keyCode) { + keyCode = keyCode | 0; + this.sendMessageDouble(11, keyCode | 0); +} +IodineGBAWorkerShim.prototype.keyUp = function (keyCode) { + keyCode = keyCode | 0; + this.sendMessageDouble(12, keyCode | 0); +} +IodineGBAWorkerShim.prototype.incrementSpeed = function (newSpeed) { + newSpeed = +newSpeed; + this.sendMessageDouble(13, +newSpeed); +} +IodineGBAWorkerShim.prototype.setSpeed = function (newSpeed) { + newSpeed = +newSpeed; + this.sendMessageDouble(14, +newSpeed); +} +IodineGBAWorkerShim.prototype.attachBIOS = function (BIOS) { + this.sendMessageDouble(15, BIOS); +} +IodineGBAWorkerShim.prototype.attachROM = function (ROM) { + this.sendMessageDouble(16, ROM); +} +IodineGBAWorkerShim.prototype.exportSave = function () { + this.sendMessageSingle(17); +} +IodineGBAWorkerShim.prototype.attachSaveExportHandler = function (saveExport) { + this.saveExport = saveExport; + this.sendMessageSingle(18); +} +IodineGBAWorkerShim.prototype.attachSaveImportHandler = function (saveImport) { + this.saveImport = saveImport; + this.sendMessageSingle(19); +} +IodineGBAWorkerShim.prototype.decodeMessage = function (data) { + switch (data.messageID | 0) { + case 0: + this.buffersInitialize(data.gfxBuffer1, data.gfxBuffer2, data.gfxCounters, data.audioSamplesRemaining, data.timestamp); + break; + case 1: + this.audioInitialize(data.channels | 0, +data.sampleRate, data.bufferLimit | 0, data.audioBuffer, data.audioCounters); + break; + case 2: + this.audioRegister(); + break; + case 3: + this.audioUnregister(); + break; + case 4: + this.audioSetBufferSpace(data.audioBufferContainAmount | 0); + break; + case 5: + this.saveImportRequest(data.saveID); + break; + case 6: + this.saveExportRequest(data.saveID, data.saveData); + break; + case 7: + this.speedPush(+data.speed); + break; + default: + this.issuePlayStatus(data.playing | 0); + } +} +IodineGBAWorkerShim.prototype.audioInitialize = function (channels, sampleRate, bufferLimit, audioBuffer, audioCounters) { + channels = channels | 0; + sampleRate = +sampleRate; + bufferLimit = bufferLimit | 0; + var parentObj = this; + if (this.audio) { + //(Re-)Initialize: + this.audio.initialize(channels | 0, +sampleRate, bufferLimit | 0, function () { + //Empty buffers inside the provided audio event callback: + parentObj.audioHeartBeat(); + }, function () { + //Get the remaining sample count: + parentObj.audioPostHeartBeat(); + },function () { + //Disable audio in the callback here: + parentObj.disableAudio(); + }); + this.audioInitialized = true; + } + //Grab the new buffer: + this.audioBuffer = audioBuffer; + this.audioCounters = audioCounters; + this.audioBufferSize = audioBuffer.length | 0; + this.audioBufferSizeMask = ((this.audioBufferSize | 0) - 1) | 0; +} +IodineGBAWorkerShim.prototype.audioHeartBeat = function () { + //If audio API handle provided and we got a buffer reference: + if (this.audioInitialized) { + //Empty the buffer out: + this.consumeAudioBuffer(); + } +} +IodineGBAWorkerShim.prototype.consumeAudioBuffer = function () { + //Load the counter values: + var start = this.audioCounters[0] | 0; //Written by this thread. + var end = Atomics.load(this.audioCounters, 1) | 0; //Written to by the other thread. + //Don't process if nothing to process: + if ((end | 0) == (start | 0)) { + //Buffer is empty: + return; + } + //Copy samples out from the ring buffer: + this.copyAudioBuffer(start | 0, end | 0); + //Update the sample count reported by the audio mixer: + //Done before updating ring buffer counter, so we don't over-produce: + this.audioPostHeartBeat(); + //Update the starting position counter to match the end position: + //Atomic store, because the sample count by the audio system needs to be reported prior to this: + Atomics.store(this.audioCounters, 0, end | 0); + //Tell audio mixer input to flush to audio mixer: + this.audio.flush(); +} +IodineGBAWorkerShim.prototype.copyAudioBuffer = function (start, end) { + start = start | 0; + end = end | 0; + //Compute the positions in the ring buffer: + var startCorrected = ((start | 0) & (this.audioBufferSizeMask | 0)) | 0; + var endCorrected = ((end | 0) & (this.audioBufferSizeMask | 0)) | 0; + //Copy samples out to audio mixer input (but don't process them yet): + if ((startCorrected | 0) >= (endCorrected | 0)) { + //Handle looping to start of buffer: + this.audio.pushDeferred(this.audioBuffer, startCorrected | 0, this.audioBufferSize | 0); + this.audio.pushDeferred(this.audioBuffer, 0, endCorrected | 0); + } + else { + this.audio.pushDeferred(this.audioBuffer, startCorrected | 0, endCorrected | 0); + } +} +IodineGBAWorkerShim.prototype.audioPostHeartBeat = function () { + //Push latest audio metrics with no buffering: + this.audioSamplesRemaining[0] = this.audio.remainingBuffer() | 0; +} +IodineGBAWorkerShim.prototype.graphicsHeartBeat = function () { + //If graphics callback handle provided and we got a buffer reference: + if (this.gfx && this.gfxCounters) { + //Copy the buffer out to local: + this.consumeGraphicsBuffer(); + //Wake up the producer thread: + Atomics.notify(this.gfxCounters, 2, 1); + } +} +IodineGBAWorkerShim.prototype.consumeGraphicsBuffer = function () { + //Load the counter values: + var start = this.gfxCounters[0] | 0; //Written by this thread. + var end = Atomics.load(this.gfxCounters, 1) | 0; //Written by the other thread. + //Don't process if nothing to process: + if ((end | 0) == (start | 0)) { + //Buffer is empty: + return; + } + //Copy samples out from the ring buffer: + do { + //Hardcoded for 2 buffers for a triple buffer effect: + this.gfx.copyBuffer(this.gfxBuffers[start & 0x1]); + start = ((start | 0) + 1) | 0; + } while ((start | 0) != (end | 0)); + //Update the starting position counter to match the end position: + //Let the other Atomic loads/stores naturally flush this value: + this.gfxCounters[0] = end | 0; +} +IodineGBAWorkerShim.prototype.audioRegister = function () { + if (this.audio) { + this.audio.register(); + } +} +IodineGBAWorkerShim.prototype.audioUnregister = function () { + if (this.audio) { + //Empty the existing buffer: + this.audioHeartBeat(); + //Unregister from mixer: + this.audio.unregister(); + } +} +IodineGBAWorkerShim.prototype.audioSetBufferSpace = function (bufferSpace) { + bufferSpace = bufferSpace | 0; + if (this.audio) { + this.audio.setBufferSpace(bufferSpace | 0); + } +} +IodineGBAWorkerShim.prototype.buffersInitialize = function (gfxBuffer1, gfxBuffer2, gfxCounters, audioSamplesRemaining, timestamp) { + this.gfxBuffers = [gfxBuffer1, gfxBuffer2]; + this.gfxCounters = gfxCounters; + this.audioSamplesRemaining = audioSamplesRemaining; + this.timestamp = timestamp; +} +IodineGBAWorkerShim.prototype.speedPush = function (speed) { + speed = +speed; + if (this.speed) { + this.speed(+speed); + } +} +IodineGBAWorkerShim.prototype.saveImportRequest = function (saveID) { + if (this.saveImport) { + var parentObj = this; + this.saveImport(saveID, function (saveData) { + parentObj.sendMessageDouble(20, saveData); + }, + function () { + parentObj.sendMessageSingle(21); + }); + } +} +IodineGBAWorkerShim.prototype.saveExportRequest = function (saveID, saveData) { + if (this.saveExport) { + this.saveExport(saveID, saveData); + } +} diff --git a/public/gfiles/gba/user_scripts/XAudioJS/README.md b/public/gfiles/gba/user_scripts/XAudioJS/README.md new file mode 100644 index 00000000..9d6923d2 --- /dev/null +++ b/public/gfiles/gba/user_scripts/XAudioJS/README.md @@ -0,0 +1,71 @@ +

XAudioJS

+

A minimal cross-browser API for writing PCM audio samples:

+

This simple JavaScript library abstracts the push-for-audio API of Mozilla Audio, and the passive callback API of Web Audio. +This library introduces an abstraction layer that provides a push-for-audio and a callback API in one. We even provide a flash fallback to bring us to a total of 3 APIs supported.

+
+This software is hereby placed in the public domain for anyone to use. +
+

How To Initialize:

+
+
new XAudioServer(int channels, double sampleRate, int bufferLow, int bufferHigh, function underRunCallback, function heartbeatCallback, function postheartbeatCallback, double volume, function failureCallback, object userEventLatch);
+
Make sure only one instance of XAudioServer is running at any time.
+
bufferLow MUST be less than bufferHigh.
+
bufferHigh sets the internal FIFO buffer length for all APIs except the Mozilla Audio Data API. Overfill on FIFO causes the oldest samples to be dropped first.
+
+

Array underRunCallback (int samplesRequested)

+
+ Arguments: Passed the number of samples that are needed to replenish the internal audio buffer back to bufferLow.

+ Functionality: JS developer set callback that can pass back any number of samples to replenish the audio buffer with.

+ Return: Array of samples to be passed into the underlying audio buffer. MUST be divisible by number of channels used (Whole frames required.). The return array length DOES NOT NEED to be of length samplesRequested. +
+
+
+

void heartbeatCallback (void)

+
+ Functionality: JS developers set this callback as a way to program against an audio clock, firing inside an audio event. +
+
+
+

void postheartbeatCallback (void)

+
+ Functionality: JS developers set this callback as a way to program against an audio clock, firing immediately after an audio event. +
+
+
volume is the output volume.
+
+

void failureCallback (void)

+
+ Functionality: JS developers set this callback to handle no audio support being available from the browser. +
+
+
+

object userEventLatch

+
+ Functionality: JS developers set this DOM object for the Web Audio API to enable audio from. +
+
+
+

Function Reference:

+
+
void writeAudio (Array buffer, Integer upTo)
+
Arguments: Pass an array of audio samples that is divisible by the number of audio channels utilized (buffer % channels == 0), and an integer length delimiter that follows the same restriction.
+
Functionality: Passes the audio samples directly into the underlying audio subsystem, and can call the specified sample buffer under-run callback as needed (Does the equivalent of executeCallback in addition to the forced sample input.).
+
Return: void (None).
+
void writeAudioNoCallback (Array buffer, Integer upTo)
+
Arguments: Pass an array of audio samples that is divisible by the number of audio channels utilized (buffer % channels == 0), and an integer length delimiter that follows the same restriction.
+
Functionality: Passes the audio samples directly into the underlying audio subsystem.
+
Return: void (None).
+
int remainingBuffer (void)
+
Arguments: void (None).
+
Functionality: Returns the number of samples left in the audio system before running out of playable samples.
+
Return (On valid): int samples_remaining (CAN BE NEGATIVE)
+
Return (On invalid): null
+
void executeCallback (void)
+
Arguments: void (None).
+
Functionality: Executes the audio sample under-run callback if the samples remaining is below the set buffer low limit.
+
Return: void (None).
+
void changeVolume (double volume)
+
Arguments: double float between 0 and 1 specifying the volume.
+
Functionality: Changes the volume. Will affect samples in buffer, so has a low-latency effect (Use this to do a fast-mute).
+
Return: void (None).
+
\ No newline at end of file diff --git a/public/gfiles/gba/user_scripts/XAudioJS/XAudioJS.as b/public/gfiles/gba/user_scripts/XAudioJS/XAudioJS.as new file mode 100644 index 00000000..2e8ab708 --- /dev/null +++ b/public/gfiles/gba/user_scripts/XAudioJS/XAudioJS.as @@ -0,0 +1,86 @@ +package { + import flash.media.Sound; + import flash.events.SampleDataEvent; + import flash.display.Sprite; + import flash.external.ExternalInterface; + public class XAudioJS extends Sprite { + public var sound:Sound = null; + public var channelBuffer:Vector. = new Vector.(8192, true); + public var channels:int = 0; + public var volume:Number = 0; + public var samplesFound:int = 0; + public function XAudioJS() { + ExternalInterface.addCallback('initialize', initialize); + ExternalInterface.addCallback('changeVolume', changeVolume); + } + //Initialization function for the flash backend of XAudioJS: + public function initialize(channels:Number, newVolume:Number):void { + //Initialize the new settings: + this.channels = (int(channels) == 2) ? 2 : 1; + this.changeVolume(newVolume); + this.checkForSound(); + } + //Volume changing function for the flash backend of XAudioJS: + public function changeVolume(newVolume:Number):void { + //Set the new volume: + this.volume = Math.min(Math.max(newVolume, 0), 1); + } + //Calls the JavaScript function responsible for the polyfill: + public function requestSamples():Boolean { + //Call the javascript callback function: + var buffer:String = ExternalInterface.call("XAudioJSFlashAudioEvent"); + //If we received an appropriate response: + if (buffer !== null) { + if ((buffer.length % this.channels) == 0) { //Outsmart bad programmers from messing us up. :/ + var channelSample:Number = 0; + this.samplesFound = Math.min(buffer.length, 4096 * this.channels); + for (var index:int = 0; index < this.samplesFound; ++index) { + //Get the unsigned 15-bit encoded sample value at +0x3000 offset: + channelSample = buffer.charCodeAt(index); + //Range-check the sample frame value and convert it: + this.channelBuffer[index] = (channelSample >= 0x3000 && channelSample < 0xAFFF) ? (this.volume * (((channelSample - 0x3000) / 0x3FFF) - 1)) : 0; + } + return true; + } + } + return false; + } + //Check to make sure the audio stream is enabled: + public function checkForSound():void { + if (this.sound == null) { + this.sound = new Sound(); + this.sound.addEventListener( + SampleDataEvent.SAMPLE_DATA, + soundCallback + ); + this.sound.play(); + } + } + //Flash Audio Refill Callback + public function soundCallback(e:SampleDataEvent):void { + var index:int = 0; + if (this.requestSamples()) { + if (this.channels == 2) { + //Stereo: + while (index < this.samplesFound) { + e.data.writeFloat(this.channelBuffer[index++]); + e.data.writeFloat(this.channelBuffer[index++]); + } + index >>= 1; + } + else { + //Mono: + while (index < this.samplesFound) { + e.data.writeFloat(this.channelBuffer[index]); + e.data.writeFloat(this.channelBuffer[index++]); + } + } + } + //Write some silence if not enough samples are found: + while (++index <= 2048) { + e.data.writeFloat(0); + e.data.writeFloat(0); + } + } + } +} \ No newline at end of file diff --git a/public/gfiles/gba/user_scripts/XAudioJS/XAudioJS.swf b/public/gfiles/gba/user_scripts/XAudioJS/XAudioJS.swf new file mode 100644 index 00000000..0b84014e Binary files /dev/null and b/public/gfiles/gba/user_scripts/XAudioJS/XAudioJS.swf differ diff --git a/public/gfiles/gba/user_scripts/XAudioJS/XAudioServer.js b/public/gfiles/gba/user_scripts/XAudioJS/XAudioServer.js new file mode 100644 index 00000000..4168f2ee --- /dev/null +++ b/public/gfiles/gba/user_scripts/XAudioJS/XAudioServer.js @@ -0,0 +1,530 @@ +//XAudioJS realtime audio output compatibility library +//Copyright (C) 2010-2015 Grant Galitz +//Released to Public Domain +var XAudioJSscriptsHandle = document.getElementsByTagName("script"); +var XAudioJSsourceHandle = XAudioJSscriptsHandle[XAudioJSscriptsHandle.length-1].src; +function XAudioServer(channels, sampleRate, minBufferSize, maxBufferSize, underRunCallback, heartbeatCallback, postheartbeatCallback, volume, failureCallback, userEventLatch) { + XAudioJSChannelsAllocated = Math.max(channels, 1); + this.XAudioJSSampleRate = Math.abs(sampleRate); + XAudioJSMinBufferSize = (minBufferSize >= (XAudioJSSamplesPerCallback * XAudioJSChannelsAllocated) && minBufferSize < maxBufferSize) ? (minBufferSize & (-XAudioJSChannelsAllocated)) : (XAudioJSSamplesPerCallback * XAudioJSChannelsAllocated); + XAudioJSMaxBufferSize = (Math.floor(maxBufferSize) > XAudioJSMinBufferSize + XAudioJSChannelsAllocated) ? (maxBufferSize & (-XAudioJSChannelsAllocated)) : (XAudioJSMinBufferSize * XAudioJSChannelsAllocated); + this.underRunCallback = (typeof underRunCallback == "function") ? underRunCallback : function () {}; + XAudioJSCallbackAPIEventNotificationCallback = (typeof heartbeatCallback == "function") ? heartbeatCallback : null; + XAudioJSCallbackAPIEventNotificationCallback2 = (typeof postheartbeatCallback == "function") ? postheartbeatCallback : null; + XAudioJSVolume = (volume >= 0 && volume <= 1) ? volume : 1; + this.failureCallback = (typeof failureCallback == "function") ? failureCallback : function () { throw(new Error("XAudioJS has encountered a fatal error.")); }; + this.userEventLatch = (typeof userEventLatch == "object") ? userEventLatch : null; + this.initializeAudio(); +} +XAudioServer.prototype.MOZWriteAudioNoCallback = function (buffer, upTo) { + //Resample before passing to the moz audio api: + var bufferLength = Math.min(buffer.length, upTo); + for (var bufferIndex = 0; bufferIndex < bufferLength;) { + var sliceLength = Math.min(bufferLength - bufferIndex, XAudioJSMaxBufferSize); + for (var sliceIndex = 0; sliceIndex < sliceLength; ++sliceIndex) { + XAudioJSAudioContextSampleBuffer[sliceIndex] = buffer[bufferIndex++]; + } + var resampleLength = XAudioJSResampleControl.resampler(sliceIndex); + if (resampleLength > 0) { + var resampledResult = XAudioJSResampleControl.outputBuffer; + var resampledBuffer = XAudioJSGetArraySlice(resampledResult, resampleLength); + this.samplesAlreadyWritten += this.audioHandleMoz.mozWriteAudio(resampledBuffer); + } + } +} +XAudioServer.prototype.callbackBasedWriteAudioNoCallback = function (buffer, upTo) { + //Callback-centered audio APIs: + var bufferLength = Math.min(buffer.length, upTo); + for (var bufferCounter = 0; bufferCounter < bufferLength && XAudioJSAudioBufferSize < XAudioJSMaxBufferSize;) { + XAudioJSAudioContextSampleBuffer[XAudioJSAudioBufferSize++] = buffer[bufferCounter++]; + } +} +/*Pass your samples into here! +Pack your samples as a one-dimenional array +With the channel samples packed uniformly. +examples: + mono - [left, left, left, left] + stereo - [left, right, left, right, left, right, left, right] +*/ +XAudioServer.prototype.writeAudio = function (buffer, upTo) { + switch (this.audioType) { + case 0: + this.MOZWriteAudioNoCallback(buffer, upTo); + this.MOZExecuteCallback(); + break; + case 2: + this.checkFlashInit(); + case 1: + this.callbackBasedWriteAudioNoCallback(buffer, upTo); + this.callbackBasedExecuteCallback(); + break; + default: + this.failureCallback(); + } +} +/*Pass your samples into here if you don't want automatic callback calling: +Pack your samples as a one-dimenional array +With the channel samples packed uniformly. +examples: + mono - [left, left, left, left] + stereo - [left, right, left, right, left, right, left, right] +Useful in preventing infinite recursion issues with calling writeAudio inside your callback. +*/ +XAudioServer.prototype.writeAudioNoCallback = function (buffer, upTo) { + switch (this.audioType) { + case 0: + this.MOZWriteAudioNoCallback(buffer, upTo); + break; + case 2: + this.checkFlashInit(); + case 1: + this.callbackBasedWriteAudioNoCallback(buffer, upTo); + break; + default: + this.failureCallback(); + } +} +//Developer can use this to see how many samples to write (example: minimum buffer allotment minus remaining samples left returned from this function to make sure maximum buffering is done...) +//If null is returned, then that means metric could not be done. +XAudioServer.prototype.remainingBuffer = function () { + switch (this.audioType) { + case 0: + return Math.floor((this.samplesAlreadyWritten - this.audioHandleMoz.mozCurrentSampleOffset()) * XAudioJSResampleControl.ratioWeight / XAudioJSChannelsAllocated) * XAudioJSChannelsAllocated; + case 2: + this.checkFlashInit(); + case 1: + return (Math.floor((XAudioJSResampledSamplesLeft() * XAudioJSResampleControl.ratioWeight) / XAudioJSChannelsAllocated) * XAudioJSChannelsAllocated) + XAudioJSAudioBufferSize; + default: + this.failureCallback(); + return null; + } +} +XAudioServer.prototype.MOZExecuteCallback = function () { + //mozAudio: + var samplesRequested = XAudioJSMinBufferSize - this.remainingBuffer(); + if (samplesRequested > 0) { + var buffer = this.underRunCallback(samplesRequested); + this.MOZWriteAudioNoCallback(buffer, buffer.length); + } +} +XAudioServer.prototype.callbackBasedExecuteCallback = function () { + //WebKit /Flash Audio: + var samplesRequested = XAudioJSMinBufferSize - this.remainingBuffer(); + if (samplesRequested > 0) { + var buffer = this.underRunCallback(samplesRequested); + this.callbackBasedWriteAudioNoCallback(buffer, buffer.length); + } +} +//If you just want your callback called for any possible refill (Execution of callback is still conditional): +XAudioServer.prototype.executeCallback = function () { + switch (this.audioType) { + case 0: + this.MOZExecuteCallback(); + break; + case 2: + this.checkFlashInit(); + case 1: + this.callbackBasedExecuteCallback(); + break; + default: + this.failureCallback(); + } +} +//DO NOT CALL THIS, the lib calls this internally! +XAudioServer.prototype.initializeAudio = function () { + try { + this.initializeMozAudio(); + } + catch (error) { + try { + this.initializeWebAudio(); + } + catch (error) { + try { + this.initializeFlashAudio(); + } + catch (error) { + this.audioType = -1; + this.failureCallback(); + } + } + } +} +XAudioServer.prototype.initializeMozAudio = function () { + this.audioHandleMoz = new Audio(); + this.audioHandleMoz.mozSetup(XAudioJSChannelsAllocated, XAudioJSMozAudioSampleRate); + this.audioHandleMoz.volume = XAudioJSVolume; + this.samplesAlreadyWritten = 0; + this.audioType = 0; + //if (navigator.platform != "MacIntel" && navigator.platform != "MacPPC") { + //Add some additional buffering space to workaround a moz audio api issue: + var bufferAmount = (this.XAudioJSSampleRate * XAudioJSChannelsAllocated / 10) | 0; + bufferAmount -= bufferAmount % XAudioJSChannelsAllocated; + this.samplesAlreadyWritten -= bufferAmount; + //} + this.initializeResampler(XAudioJSMozAudioSampleRate); +} +XAudioServer.prototype.initializeWebAudio = function () { + if (typeof AudioContext == "undefined" || typeof XAudioJSWebAudioContextHandle == "undefined") { + throw null; + } + else { + if (!this.userEventLatch) { + this.setupWebAudio(); + } + else { + var parentObj = this; + var XAudioJSWebAudioDelayedEvent = 0; + this.userEventLatch.addEventListener("click", function () { + if (XAudioJSWebAudioDelayedEvent == 0) { + parentObj.setupWebAudio(); + XAudioJSWebAudioDelayedEvent |= 1; + } + }, false); + this.userEventLatch.addEventListener("touchstart", function () { + if (XAudioJSWebAudioDelayedEvent < 2) { + parentObj.setupWebAudio(); + XAudioJSWebAudioDelayedEvent |= 2; + } + }, false); + this.userEventLatch.addEventListener("touchend", function () { + if (XAudioJSWebAudioDelayedEvent < 4) { + parentObj.setupWebAudio(); + XAudioJSWebAudioDelayedEvent |= 4; + } + }, false); + //TODO: Restructure API to not have to potentially lie to end client about + //the samples in buffer before user driven event callback that actually starts WA. + this.resetCallbackAPIAudioBuffer(44100); + } + this.audioType = 1; + } +} +XAudioServer.prototype.setupWebAudio = function () { + if (XAudioJSWebAudioLaunchedContext) { + XAudioJSWebAudioContextHandle.close(); + } + try { + XAudioJSWebAudioContextHandle = new AudioContext(); //Create a system audio context. + } + catch (error) { + XAudioJSWebAudioContextHandle = new webkitAudioContext(); //Create a system audio context. + } + XAudioJSWebAudioLaunchedContext = true; + if (XAudioJSWebAudioAudioNode) { + XAudioJSWebAudioAudioNode.disconnect(); + XAudioJSWebAudioAudioNode.onaudioprocess = null; + XAudioJSWebAudioAudioNode = null; + } + try { + XAudioJSWebAudioAudioNode = XAudioJSWebAudioContextHandle.createScriptProcessor(XAudioJSSamplesPerCallback, 0, XAudioJSChannelsAllocated); //Create the js event node. + } + catch (error) { + XAudioJSWebAudioAudioNode = XAudioJSWebAudioContextHandle.createJavaScriptNode(XAudioJSSamplesPerCallback, 0, XAudioJSChannelsAllocated); //Create the js event node. + } + XAudioJSWebAudioAudioNode.onaudioprocess = XAudioJSWebAudioEvent; //Connect the audio processing event to a handling function so we can manipulate output + XAudioJSWebAudioAudioNode.connect(XAudioJSWebAudioContextHandle.destination); //Send and chain the output of the audio manipulation to the system audio output. + this.resetCallbackAPIAudioBuffer(XAudioJSWebAudioContextHandle.sampleRate); + /* + Firefox has a bug in its web audio implementation... + The node may randomly stop playing on Mac OS X for no + good reason. Keep a watchdog timer to restart the failed + node if it glitches. Google Chrome never had this issue. + */ + XAudioJSWebAudioWatchDogLast = (new Date()).getTime(); + if (!XAudioJSWebAudioWatchDogTimer && navigator.userAgent.indexOf('Gecko/') > -1) { + if (XAudioJSWebAudioWatchDogTimer) { + clearInterval(XAudioJSWebAudioWatchDogTimer); + } + var parentObj = this; + XAudioJSWebAudioWatchDogTimer = setInterval(function () { + if(typeof XAudioJSWebAudioContextHandle.state != "undefined") { + if (XAudioJSWebAudioContextHandle.state === 'suspended') { + XAudioJSWebAudioWatchDogLast = (new Date()).getTime(); + try { + XAudioJSWebAudioContextHandle.resume(); + } + catch (e) {} + } + else { + var timeDiff = (new Date()).getTime() - XAudioJSWebAudioWatchDogLast; + if (timeDiff > 500) { + parentObj.setupWebAudio(); + } + } + } + }, 500); + } +} +XAudioServer.prototype.initializeFlashAudio = function () { + var existingFlashload = document.getElementById("XAudioJS"); + this.flashInitialized = false; + this.resetCallbackAPIAudioBuffer(44100); + switch (XAudioJSChannelsAllocated) { + case 1: + XAudioJSFlashTransportEncoder = XAudioJSGenerateFlashMonoString; + break; + case 2: + XAudioJSFlashTransportEncoder = XAudioJSGenerateFlashStereoString; + break; + default: + XAudioJSFlashTransportEncoder = XAudioJSGenerateFlashSurroundString; + } + if (existingFlashload == null) { + this.audioHandleFlash = null; + var thisObj = this; + var mainContainerNode = document.createElement("div"); + mainContainerNode.setAttribute("style", "position: fixed; bottom: 0px; right: 0px; margin: 0px; padding: 0px; border: none; width: 8px; height: 8px; overflow: hidden; z-index: -1000; "); + var containerNode = document.createElement("div"); + containerNode.setAttribute("style", "position: static; border: none; width: 0px; height: 0px; visibility: hidden; margin: 8px; padding: 0px;"); + containerNode.setAttribute("id", "XAudioJS"); + mainContainerNode.appendChild(containerNode); + document.getElementsByTagName("body")[0].appendChild(mainContainerNode); + swfobject.embedSWF( + XAudioJSsourceHandle.substring(0, XAudioJSsourceHandle.length - 9) + "JS.swf", + "XAudioJS", + "8", + "8", + "9.0.0", + "", + {}, + {"allowscriptaccess":"always"}, + {"style":"position: static; visibility: hidden; margin: 8px; padding: 0px; border: none"}, + function (event) { + if (event.success) { + thisObj.audioHandleFlash = event.ref; + thisObj.checkFlashInit(); + } + else { + thisObj.failureCallback(); + thisObj.audioType = -1; + } + } + ); + } + else { + this.audioHandleFlash = existingFlashload; + this.checkFlashInit(); + } + this.audioType = 2; +} +XAudioServer.prototype.changeVolume = function (newVolume) { + if (newVolume >= 0 && newVolume <= 1) { + XAudioJSVolume = newVolume; + switch (this.audioType) { + case 0: + this.audioHandleMoz.volume = XAudioJSVolume; + case 1: + break; + case 2: + if (this.flashInitialized) { + this.audioHandleFlash.changeVolume(XAudioJSVolume); + } + else { + this.checkFlashInit(); + } + break; + default: + this.failureCallback(); + } + } +} +//Checks to see if the NPAPI Adobe Flash bridge is ready yet: +XAudioServer.prototype.checkFlashInit = function () { + if (!this.flashInitialized) { + try { + if (this.audioHandleFlash && this.audioHandleFlash.initialize) { + this.flashInitialized = true; + this.audioHandleFlash.initialize(XAudioJSChannelsAllocated, XAudioJSVolume); + } + } + catch (error) { + this.flashInitialized = false; + } + } +} +//Set up the resampling: +XAudioServer.prototype.resetCallbackAPIAudioBuffer = function (APISampleRate) { + XAudioJSAudioBufferSize = XAudioJSResampleBufferEnd = XAudioJSResampleBufferStart = 0; + this.initializeResampler(APISampleRate); + XAudioJSResampledBuffer = this.getFloat32(XAudioJSResampleBufferSize); +} +XAudioServer.prototype.initializeResampler = function (sampleRate) { + XAudioJSAudioContextSampleBuffer = this.getFloat32(XAudioJSMaxBufferSize); + XAudioJSResampleControl = new Resampler(this.XAudioJSSampleRate, sampleRate, XAudioJSChannelsAllocated, XAudioJSAudioContextSampleBuffer); + XAudioJSResampleBufferSize = XAudioJSResampleControl.outputBuffer.length; +} +XAudioServer.prototype.getFloat32 = function (size) { + try { + return new Float32Array(size); + } + catch (error) { + return []; + } +} +function XAudioJSFlashAudioEvent() { //The callback that flash calls... + XAudioJSCallbackAPIEventNotificationCallbackCompatTimerClear(); + XAudioJSCallbackAPIEventNotification(); + XAudioJSResampleRefill(); + var outputStr = XAudioJSFlashTransportEncoder(); + XAudioJSCallbackAPIEventNotification2(); + return outputStr; +} +function XAudioJSGenerateFlashSurroundString() { //Convert the arrays to one long string for speed. + var XAudioJSTotalSamples = XAudioJSSamplesPerCallback << 1; + if (XAudioJSBinaryString.length > XAudioJSTotalSamples) { + XAudioJSBinaryString = []; + } + XAudioJSTotalSamples = 0; + for (var index = 0; index < XAudioJSSamplesPerCallback && XAudioJSResampleBufferStart != XAudioJSResampleBufferEnd; ++index) { + //Sanitize the buffer: + XAudioJSBinaryString[XAudioJSTotalSamples++] = String.fromCharCode(((Math.min(Math.max(XAudioJSResampledBuffer[XAudioJSResampleBufferStart++] + 1, 0), 2) * 0x3FFF) | 0) + 0x3000); + XAudioJSBinaryString[XAudioJSTotalSamples++] = String.fromCharCode(((Math.min(Math.max(XAudioJSResampledBuffer[XAudioJSResampleBufferStart++] + 1, 0), 2) * 0x3FFF) | 0) + 0x3000); + XAudioJSResampleBufferStart += XAudioJSChannelsAllocated - 2; + if (XAudioJSResampleBufferStart == XAudioJSResampleBufferSize) { + XAudioJSResampleBufferStart = 0; + } + } + return XAudioJSBinaryString.join(""); +} +function XAudioJSGenerateFlashStereoString() { //Convert the arrays to one long string for speed. + var XAudioJSTotalSamples = XAudioJSSamplesPerCallback << 1; + if (XAudioJSBinaryString.length > XAudioJSTotalSamples) { + XAudioJSBinaryString = []; + } + for (var index = 0; index < XAudioJSTotalSamples && XAudioJSResampleBufferStart != XAudioJSResampleBufferEnd;) { + //Sanitize the buffer: + XAudioJSBinaryString[index++] = String.fromCharCode(((Math.min(Math.max(XAudioJSResampledBuffer[XAudioJSResampleBufferStart++] + 1, 0), 2) * 0x3FFF) | 0) + 0x3000); + XAudioJSBinaryString[index++] = String.fromCharCode(((Math.min(Math.max(XAudioJSResampledBuffer[XAudioJSResampleBufferStart++] + 1, 0), 2) * 0x3FFF) | 0) + 0x3000); + if (XAudioJSResampleBufferStart == XAudioJSResampleBufferSize) { + XAudioJSResampleBufferStart = 0; + } + } + return XAudioJSBinaryString.join(""); +} +function XAudioJSGenerateFlashMonoString() { //Convert the array to one long string for speed. + if (XAudioJSBinaryString.length > XAudioJSSamplesPerCallback) { + XAudioJSBinaryString = []; + } + for (var index = 0; index < XAudioJSSamplesPerCallback && XAudioJSResampleBufferStart != XAudioJSResampleBufferEnd;) { + //Sanitize the buffer: + XAudioJSBinaryString[index++] = String.fromCharCode(((Math.min(Math.max(XAudioJSResampledBuffer[XAudioJSResampleBufferStart++] + 1, 0), 2) * 0x3FFF) | 0) + 0x3000); + if (XAudioJSResampleBufferStart == XAudioJSResampleBufferSize) { + XAudioJSResampleBufferStart = 0; + } + } + return XAudioJSBinaryString.join(""); +} +//Some Required Globals: +var XAudioJSWebAudioContextHandle = null; +var XAudioJSWebAudioAudioNode = null; +var XAudioJSWebAudioWatchDogTimer = null; +var XAudioJSCallbackAPIEventNotificationCallback = null; +var XAudioJSCallbackAPIEventNotificationCallback2 = null; +var XAudioJSCallbackAPIEventNotificationCallbackCompatTimer = setInterval(XAudioJSCallbackAPIEventNotificationDual, 16); +var XAudioJSWebAudioWatchDogLast = false; +var XAudioJSWebAudioLaunchedContext = false; +var XAudioJSAudioContextSampleBuffer = []; +var XAudioJSResampledBuffer = []; +var XAudioJSMinBufferSize = 15000; +var XAudioJSMaxBufferSize = 25000; +var XAudioJSChannelsAllocated = 1; +var XAudioJSVolume = 1; +var XAudioJSResampleControl = null; +var XAudioJSAudioBufferSize = 0; +var XAudioJSResampleBufferStart = 0; +var XAudioJSResampleBufferEnd = 0; +var XAudioJSResampleBufferSize = 0; +var XAudioJSMozAudioSampleRate = 44100; +var XAudioJSSamplesPerCallback = 2048; //Has to be between 2048 and 4096 (If over, then samples are ignored, if under then silence is added). +var XAudioJSFlashTransportEncoder = null; +var XAudioJSBinaryString = []; +function XAudioJSWebAudioEvent(event) { //Web Audio API callback... + if (XAudioJSWebAudioWatchDogTimer) { + XAudioJSWebAudioWatchDogLast = (new Date()).getTime(); + } + //Find all output channels: + for (var bufferCount = 0, buffers = []; bufferCount < XAudioJSChannelsAllocated; ++bufferCount) { + buffers[bufferCount] = event.outputBuffer.getChannelData(bufferCount); + } + XAudioJSCallbackAPIEventNotificationCallbackCompatTimerClear(); + XAudioJSCallbackAPIEventNotification(); + //Make sure we have resampled samples ready: + XAudioJSResampleRefill(); + //Copy samples from XAudioJS to the Web Audio API: + for (var index = 0; index < XAudioJSSamplesPerCallback && XAudioJSResampleBufferStart != XAudioJSResampleBufferEnd; ++index) { + for (bufferCount = 0; bufferCount < XAudioJSChannelsAllocated; ++bufferCount) { + buffers[bufferCount][index] = XAudioJSResampledBuffer[XAudioJSResampleBufferStart++] * XAudioJSVolume; + } + if (XAudioJSResampleBufferStart == XAudioJSResampleBufferSize) { + XAudioJSResampleBufferStart = 0; + } + } + //Pad with silence if we're underrunning: + while (index < XAudioJSSamplesPerCallback) { + for (bufferCount = 0; bufferCount < XAudioJSChannelsAllocated; ++bufferCount) { + buffers[bufferCount][index] = 0; + } + ++index; + } + XAudioJSCallbackAPIEventNotification2(); +} +function XAudioJSResampleRefill() { + if (XAudioJSAudioBufferSize > 0) { + //Resample a chunk of audio: + var resampleLength = XAudioJSResampleControl.resampler(XAudioJSAudioBufferSize); + var resampledResult = XAudioJSResampleControl.outputBuffer; + for (var index2 = 0; index2 < resampleLength;) { + XAudioJSResampledBuffer[XAudioJSResampleBufferEnd++] = resampledResult[index2++]; + if (XAudioJSResampleBufferEnd == XAudioJSResampleBufferSize) { + XAudioJSResampleBufferEnd = 0; + } + if (XAudioJSResampleBufferStart == XAudioJSResampleBufferEnd) { + XAudioJSResampleBufferStart += XAudioJSChannelsAllocated; + if (XAudioJSResampleBufferStart == XAudioJSResampleBufferSize) { + XAudioJSResampleBufferStart = 0; + } + } + } + XAudioJSAudioBufferSize = 0; + } +} +function XAudioJSCallbackAPIEventNotificationCallbackCompatTimerClear() { + if (XAudioJSCallbackAPIEventNotificationCallbackCompatTimer) { + clearInterval(XAudioJSCallbackAPIEventNotificationCallbackCompatTimer); + } +} +function XAudioJSCallbackAPIEventNotification() { + if (typeof XAudioJSCallbackAPIEventNotificationCallback == "function") { + XAudioJSCallbackAPIEventNotificationCallback(); + } +} +function XAudioJSCallbackAPIEventNotification2() { + if (typeof XAudioJSCallbackAPIEventNotificationCallback2 == "function") { + XAudioJSCallbackAPIEventNotificationCallback2(); + } +} +function XAudioJSCallbackAPIEventNotificationDual() { + XAudioJSCallbackAPIEventNotification(); + XAudioJSCallbackAPIEventNotification2(); +} +function XAudioJSResampledSamplesLeft() { + return ((XAudioJSResampleBufferStart <= XAudioJSResampleBufferEnd) ? 0 : XAudioJSResampleBufferSize) + XAudioJSResampleBufferEnd - XAudioJSResampleBufferStart; +} +function XAudioJSGetArraySlice(buffer, lengthOf) { + //Typed array and normal array buffer section referencing: + try { + return buffer.subarray(0, lengthOf); + } + catch (error) { + try { + //Regular array pass: + buffer.length = lengthOf; + return buffer; + } + catch (error) { + //Nightly Firefox 4 used to have the subarray function named as slice: + return buffer.slice(0, lengthOf); + } + } +} \ No newline at end of file diff --git a/public/gfiles/gba/user_scripts/XAudioJS/resampler.js b/public/gfiles/gba/user_scripts/XAudioJS/resampler.js new file mode 100644 index 00000000..a04f18df --- /dev/null +++ b/public/gfiles/gba/user_scripts/XAudioJS/resampler.js @@ -0,0 +1,173 @@ +"use strict"; +//JavaScript Audio Resampler +//Copyright (C) 2011-2015 Grant Galitz +//Released to Public Domain +function Resampler(fromSampleRate, toSampleRate, channels, inputBuffer) { + //Input Sample Rate: + this.fromSampleRate = +fromSampleRate; + //Output Sample Rate: + this.toSampleRate = +toSampleRate; + //Number of channels: + this.channels = channels | 0; + //Type checking the input buffer: + if (typeof inputBuffer != "object") { + throw(new Error("inputBuffer is not an object.")); + } + if (!(inputBuffer instanceof Array) && !(inputBuffer instanceof Float32Array) && !(inputBuffer instanceof Float64Array)) { + throw(new Error("inputBuffer is not an array or a float32 or a float64 array.")); + } + this.inputBuffer = inputBuffer; + //Initialize the resampler: + this.initialize(); +} +Resampler.prototype.initialize = function () { + //Perform some checks: + if (this.fromSampleRate > 0 && this.toSampleRate > 0 && this.channels > 0) { + if (this.fromSampleRate == this.toSampleRate) { + //Setup a resampler bypass: + this.resampler = this.bypassResampler; //Resampler just returns what was passed through. + this.ratioWeight = 1; + this.outputBuffer = this.inputBuffer; + } + else { + this.ratioWeight = this.fromSampleRate / this.toSampleRate; + if (this.fromSampleRate < this.toSampleRate) { + /* + Use generic linear interpolation if upsampling, + as linear interpolation produces a gradient that we want + and works fine with two input sample points per output in this case. + */ + this.compileLinearInterpolationFunction(); + this.lastWeight = 1; + } + else { + /* + Custom resampler I wrote that doesn't skip samples + like standard linear interpolation in high downsampling. + This is more accurate than linear interpolation on downsampling. + */ + this.compileMultiTapFunction(); + this.tailExists = false; + this.lastWeight = 0; + } + this.initializeBuffers(); + } + } + else { + throw(new Error("Invalid settings specified for the resampler.")); + } +} +Resampler.prototype.compileLinearInterpolationFunction = function () { + var toCompile = "var outputOffset = 0;\ + if (bufferLength > 0) {\ + var buffer = this.inputBuffer;\ + var weight = this.lastWeight;\ + var firstWeight = 0;\ + var secondWeight = 0;\ + var sourceOffset = 0;\ + var outputOffset = 0;\ + var outputBuffer = this.outputBuffer;\ + for (; weight < 1; weight += " + this.ratioWeight + ") {\ + secondWeight = weight % 1;\ + firstWeight = 1 - secondWeight;"; + for (var channel = 0; channel < this.channels; ++channel) { + toCompile += "outputBuffer[outputOffset++] = (this.lastOutput[" + channel + "] * firstWeight) + (buffer[" + channel + "] * secondWeight);"; + } + toCompile += "}\ + weight -= 1;\ + for (bufferLength -= " + this.channels + ", sourceOffset = Math.floor(weight) * " + this.channels + "; sourceOffset < bufferLength;) {\ + secondWeight = weight % 1;\ + firstWeight = 1 - secondWeight;"; + for (var channel = 0; channel < this.channels; ++channel) { + toCompile += "outputBuffer[outputOffset++] = (buffer[sourceOffset" + ((channel > 0) ? (" + " + channel) : "") + "] * firstWeight) + (buffer[sourceOffset + " + (this.channels + channel) + "] * secondWeight);"; + } + toCompile += "weight += " + this.ratioWeight + ";\ + sourceOffset = Math.floor(weight) * " + this.channels + ";\ + }"; + for (var channel = 0; channel < this.channels; ++channel) { + toCompile += "this.lastOutput[" + channel + "] = buffer[sourceOffset++];"; + } + toCompile += "this.lastWeight = weight % 1;\ + }\ + return outputOffset;"; + this.resampler = Function("bufferLength", toCompile); +} +Resampler.prototype.compileMultiTapFunction = function () { + var toCompile = "var outputOffset = 0;\ + if (bufferLength > 0) {\ + var buffer = this.inputBuffer;\ + var weight = 0;"; + for (var channel = 0; channel < this.channels; ++channel) { + toCompile += "var output" + channel + " = 0;" + } + toCompile += "var actualPosition = 0;\ + var amountToNext = 0;\ + var alreadyProcessedTail = !this.tailExists;\ + this.tailExists = false;\ + var outputBuffer = this.outputBuffer;\ + var currentPosition = 0;\ + do {\ + if (alreadyProcessedTail) {\ + weight = " + this.ratioWeight + ";"; + for (channel = 0; channel < this.channels; ++channel) { + toCompile += "output" + channel + " = 0;" + } + toCompile += "}\ + else {\ + weight = this.lastWeight;"; + for (channel = 0; channel < this.channels; ++channel) { + toCompile += "output" + channel + " = this.lastOutput[" + channel + "];" + } + toCompile += "alreadyProcessedTail = true;\ + }\ + while (weight > 0 && actualPosition < bufferLength) {\ + amountToNext = 1 + actualPosition - currentPosition;\ + if (weight >= amountToNext) {"; + for (channel = 0; channel < this.channels; ++channel) { + toCompile += "output" + channel + " += buffer[actualPosition++] * amountToNext;" + } + toCompile += "currentPosition = actualPosition;\ + weight -= amountToNext;\ + }\ + else {"; + for (channel = 0; channel < this.channels; ++channel) { + toCompile += "output" + channel + " += buffer[actualPosition" + ((channel > 0) ? (" + " + channel) : "") + "] * weight;" + } + toCompile += "currentPosition += weight;\ + weight = 0;\ + break;\ + }\ + }\ + if (weight <= 0) {"; + for (channel = 0; channel < this.channels; ++channel) { + toCompile += "outputBuffer[outputOffset++] = output" + channel + " / " + this.ratioWeight + ";" + } + toCompile += "}\ + else {\ + this.lastWeight = weight;"; + for (channel = 0; channel < this.channels; ++channel) { + toCompile += "this.lastOutput[" + channel + "] = output" + channel + ";" + } + toCompile += "this.tailExists = true;\ + break;\ + }\ + } while (actualPosition < bufferLength);\ + }\ + return outputOffset;"; + this.resampler = Function("bufferLength", toCompile); +} +Resampler.prototype.bypassResampler = function (upTo) { + return upTo; +} +Resampler.prototype.initializeBuffers = function () { + //Initialize the internal buffer: + var outputBufferSize = (Math.ceil(this.inputBuffer.length * this.toSampleRate / this.fromSampleRate / this.channels * 1.000000476837158203125) * this.channels) + this.channels; + try { + this.outputBuffer = new Float32Array(outputBufferSize); + this.lastOutput = new Float32Array(this.channels); + } + catch (error) { + this.outputBuffer = []; + this.lastOutput = []; + } +} \ No newline at end of file diff --git a/public/gfiles/gba/user_scripts/XAudioJS/swfobject.js b/public/gfiles/gba/user_scripts/XAudioJS/swfobject.js new file mode 100644 index 00000000..8eafe9dd --- /dev/null +++ b/public/gfiles/gba/user_scripts/XAudioJS/swfobject.js @@ -0,0 +1,4 @@ +/* SWFObject v2.2 + is released under the MIT License +*/ +var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab 0) { + var bytes = [0, 0, 0]; + var index = 0; + var remainder = dataLength % 3; + while (data.length % 3 > 0) { + //Make sure we don't do fuzzy math in the next loop... + data[data.length] = " "; + } + while (index < dataLength) { + //Keep this loop small for speed. + bytes = [data.charCodeAt(index++) & 0xFF, data.charCodeAt(index++) & 0xFF, data.charCodeAt(index++) & 0xFF]; + base64 += toBase64[bytes[0] >> 2] + toBase64[((bytes[0] & 0x3) << 4) | (bytes[1] >> 4)] + toBase64[((bytes[1] & 0xF) << 2) | (bytes[2] >> 6)] + toBase64[bytes[2] & 0x3F]; + } + if (remainder > 0) { + //Fill in the padding and recalulate the trailing six-bit group... + base64[base64.length - 1] = "="; + if (remainder == 2) { + base64[base64.length - 2] = "="; + base64[base64.length - 3] = toBase64[(bytes[0] & 0x3) << 4]; + } + else { + base64[base64.length - 2] = toBase64[(bytes[1] & 0xF) << 2]; + } + } + } + } + return base64; +} +function base64_decode(data) { + try { + var decode64 = window.atob(data); //Use this native function when it's available, as it's a magnitude faster than the non-native code below. + } + catch (error) { + //Defaulting to non-native base64 decoding... + var decode64 = ""; + var dataLength = data.length; + if (dataLength > 3 && dataLength % 4 == 0) { + var sixbits = [0, 0, 0, 0]; //Declare this out of the loop, to speed up the ops. + var index = 0; + while (index < dataLength) { + //Keep this loop small for speed. + sixbits = [fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++))]; + decode64 += String.fromCharCode((sixbits[0] << 2) | (sixbits[1] >> 4)) + String.fromCharCode(((sixbits[1] & 0x0F) << 4) | (sixbits[2] >> 2)) + String.fromCharCode(((sixbits[2] & 0x03) << 6) | sixbits[3]); + } + //Check for the '=' character after the loop, so we don't hose it up. + if (sixbits[3] >= 0x40) { + decode64.length -= 1; + if (sixbits[2] >= 0x40) { + decode64.length -= 1; + } + } + } + } + return decode64; +} +function arrayToBase64(arrayIn) { + var binString = ""; + var length = arrayIn.length; + for (var index = 0; index < length; ++index) { + if (typeof arrayIn[index] == "number") { + binString += String.fromCharCode(arrayIn[index]); + } + } + return base64(binString); +} +function base64ToArray(b64String) { + var binString = base64_decode(b64String); + var outArray = []; + var length = binString.length; + for (var index = 0; index < length;) { + outArray.push(binString.charCodeAt(index++) & 0xFF); + } + return outArray; +} \ No newline at end of file diff --git a/public/gfiles/html5games/2048/index.html b/public/gfiles/html5games/2048/index.html new file mode 100644 index 00000000..3e17ee03 --- /dev/null +++ b/public/gfiles/html5games/2048/index.html @@ -0,0 +1,93 @@ + + + + + + 2048 + + + + + + + + + + + + + + +
+
+

2048

+
+
0
+
0
+
+
+ +
+

Join the numbers and get to the 2048 tile!

+ New Game +
+ +
+
+

+ +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+
+ +

+ How to play: Use your arrow keys to move the tiles. When two tiles with the same number touch, they merge into one! +

+
+ + + + + + + + + + + + + + diff --git a/public/gfiles/html5games/asteroids/index.html b/public/gfiles/html5games/asteroids/index.html new file mode 100644 index 00000000..299ba5c7 --- /dev/null +++ b/public/gfiles/html5games/asteroids/index.html @@ -0,0 +1,32 @@ + + + + + + + + + +
+ +
+
THRUST
+
LEFT
+ +
+
+
FIRE
+
+
+ + diff --git a/public/gfiles/html5games/astray/ball.png b/public/gfiles/html5games/astray/ball.png new file mode 100644 index 00000000..d63f1945 Binary files /dev/null and b/public/gfiles/html5games/astray/ball.png differ diff --git a/public/gfiles/html5games/astray/brick.png b/public/gfiles/html5games/astray/brick.png new file mode 100644 index 00000000..270c478f Binary files /dev/null and b/public/gfiles/html5games/astray/brick.png differ diff --git a/public/gfiles/html5games/astray/concrete.png b/public/gfiles/html5games/astray/concrete.png new file mode 100644 index 00000000..538409b2 Binary files /dev/null and b/public/gfiles/html5games/astray/concrete.png differ diff --git a/public/gfiles/html5games/astray/index.html b/public/gfiles/html5games/astray/index.html new file mode 100644 index 00000000..967c83b7 --- /dev/null +++ b/public/gfiles/html5games/astray/index.html @@ -0,0 +1,372 @@ + + + + + + + + + + + + + + + + + + +
+ How to play Astray: +

+ Use the arrow keys to move the ball and find the exit to the maze. +

+ Vim trainees: h, j, k, l +
+ +
+ Hold down the 'I' key for instructions. +
+ +
+ Level 1 +
+ + + + + diff --git a/public/gfiles/html5games/backcountry/index.html b/public/gfiles/html5games/backcountry/index.html new file mode 100644 index 00000000..b8745622 --- /dev/null +++ b/public/gfiles/html5games/backcountry/index.html @@ -0,0 +1 @@ +Backcountry
diff --git a/public/gfiles/html5games/backcountry/models.tfu b/public/gfiles/html5games/backcountry/models.tfu new file mode 100644 index 00000000..aa603ea1 Binary files /dev/null and b/public/gfiles/html5games/backcountry/models.tfu differ diff --git a/public/gfiles/html5games/bounceback/index.html b/public/gfiles/html5games/bounceback/index.html new file mode 100644 index 00000000..c89058f3 --- /dev/null +++ b/public/gfiles/html5games/bounceback/index.html @@ -0,0 +1,66 @@ + + + + + + +Bounce Back + + + + + + + + + + \ No newline at end of file diff --git a/public/gfiles/html5games/bounceback/tiles.png b/public/gfiles/html5games/bounceback/tiles.png new file mode 100644 index 00000000..d67fcda3 Binary files /dev/null and b/public/gfiles/html5games/bounceback/tiles.png differ diff --git a/public/gfiles/html5games/breaklock/index.html b/public/gfiles/html5games/breaklock/index.html new file mode 100644 index 00000000..540f8b77 --- /dev/null +++ b/public/gfiles/html5games/breaklock/index.html @@ -0,0 +1,122 @@ + + + + + + BreakLock + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ $ ./breaklock start
+ Loading... + +

+ + +
+
+ +

Link the dots to find the lock pattern. After every attempt the game will tell you how many dots you got right.

+ + + + + + + + + +
a dot occurs in the pattern and is in the correct order
a dot occurs in the pattern but in the wrong order
+

The difficulty setting changes the amount of dots to connect. Easy is 4 dots, medium is 5 dots and hard is 6 dots.

+

Good luck!_

+

by / on

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/gfiles/html5games/breakout/index.html b/public/gfiles/html5games/breakout/index.html new file mode 100644 index 00000000..353e21fa --- /dev/null +++ b/public/gfiles/html5games/breakout/index.html @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + +
+
+ + +
+
+ + + + + \ No newline at end of file diff --git a/public/gfiles/html5games/chess/canvas3dengine/camera.jsfb b/public/gfiles/html5games/chess/canvas3dengine/camera.jsfb new file mode 100644 index 00000000..12205c5f --- /dev/null +++ b/public/gfiles/html5games/chess/canvas3dengine/camera.jsfb @@ -0,0 +1,293 @@ +/* + * Javascript/Canvas Textured 3D Renderer v0.3.1 + * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com + * This software is free to use for non-commercial purposes. For anything else, please contact the author. + * This is a version modified by Stefano Gioffre'. + */ + +Canvas3D.Camera = function() { + + this._oPosition = new Canvas3D.Vec3(0,0,0); + + this._oSideVec = new Canvas3D.Vec3(1,0,0); + this._oUpVec = new Canvas3D.Vec3(0,1,0); + this._oOutVec = new Canvas3D.Vec3(0,0,1); + + this._oRotMat = new Canvas3D.Matrix3(); + + this._bDirty = false; + this._fFocal = 500; + this._fFocalDistance = this._fFocal; + + this._bReverseX = false; + this._bReverseY = false; + + this._bTarget = true; + + this._iClipNear = 1; + this._iClipFar = 10000000; + + this._fScale = 1; + + this._oLookAt = new Canvas3D.Vec3(0,0,0); + +}; + +var proto = Canvas3D.Camera.prototype; + +proto.getDirty = function() { + return this._bDirty; +} + +proto.setDirty = function(bDirty) { + this._bDirty = bDirty; +} + +proto.setPosition = function(oPos) { + this._oPosition.set(oPos.x, oPos.y, oPos.z); + this._bDirty = true; +} + +proto.getPosition = function() { + return this._oPosition; +} + +proto.setScale = function(fScale) { + this._fScale = fScale; + this._bDirty = true; +} + +proto.getScale = function() { + return this._fScale; +} + +proto.getSide = function() { + return this._oSideVec; +} + +proto.getUp = function() { + return this._oUpVec; +} + +proto.setUp = function(oVec) { + this._oUpVec = oVec; +} + +proto.getOut = function() { + return this._oOutVec; +} + +proto.moveSideways = function(d) { + this._oPosition.x += this._oSideVec.x * d; + this._oPosition.y += this._oSideVec.y * d; + this._oPosition.z += this._oSideVec.z * d; + this.setDirty(true); +} + +proto.moveUpwards = function(d) { + this._oPosition.x += this._oUpVec.x * d; + this._oPosition.y += this._oUpVec.y * d; + this._oPosition.z += this._oUpVec.z * d; + this.setDirty(true); +} + +proto.moveForward = function(d) { + this._oPosition.x += this._oOutVec.x * d; + this._oPosition.y += this._oOutVec.y * d; + this._oPosition.z += this._oOutVec.z * d; + this.setDirty(true); +} + +// rotate around the camera's side axis with a target center point (uses camera target if oTarget is null) +proto.pitchAroundTarget = function(fTheta, oTarget) { + var M = new Canvas3D.Matrix3(); + var oPos = this.getPosition(); + oTarget = oTarget || this.getLookAt(); + + // translate position to target space + oPos.subVector(oTarget); + + // rotate around side axis + M.loadRotationAxis(this._oSideVec, Math.sin(fTheta * Math.PI / 180.0), Math.cos(fTheta * Math.PI / 180.0)); + oPos = M.multiplyVector(oPos); + + // translate position out of target space + oPos.addVector(oTarget); + + this.setPosition(oPos); + this.setDirty(true); +} + +proto.yaw = function(fTheta) { + var M = new Canvas3D.Matrix3(); + M.loadRotationAxis(this._oUpVec, Math.sin(fTheta), Math.cos(fTheta)); + this._oSideVec = M.multiplyVector(this._oSideVec); + this._oOutVec = M.multiplyVector(this._oOutVec); + this.setDirty(true); +} + +// rotate around the camera's up axis with a target center point (uses camera target if oTarget is null) +proto.yawAroundTarget = function(fTheta, oTarget) { + var M = new Canvas3D.Matrix3(); + var oPos = this.getPosition(); + oTarget = oTarget || this.getLookAt(); + + // translate position to target space + oPos.subVector(oTarget); + + // rotate around up axis + M.loadRotationAxis(this._oUpVec, Math.sin(fTheta * Math.PI / 180.0), Math.cos(fTheta * Math.PI / 180.0)); + oPos = M.multiplyVector(oPos); + + // translate position out of target space + oPos.addVector(oTarget); + + this.setPosition(oPos); + this.setDirty(true); +} + +// rotate around the camera's out axis with a target center point (uses camera target if oTarget is null) +proto.rollAroundTarget = function(fTheta, oTarget) { + var M = new Canvas3D.Matrix3(); + var oPos = this.getPosition(); + oTarget = oTarget || this.getLookAt(); + + // translate position to target space + oPos.subVector(oTarget); + + // rotate around out axis + M.loadRotationAxis(this._oOutVec, Math.sin(fTheta * Math.PI / 180.0), Math.cos(fTheta * Math.PI / 180.0)); + oPos = M.multiplyVector(oPos); + + // translate position out of target space + oPos.addVector(oTarget); + + this.setPosition(oPos); + this.setDirty(true); +} + +proto.rotateY = function(sine, cosine) { + var M = new Canvas3D.Matrix3(); + M.loadRotationY(sine, cosine); + this._oSideVec = M.multiplyVector(this._oSideVec); + this._oUpVec = M.multiplyVector(this._oUpVec); + this._oOutVec = M.multiplyVector(this._oOutVec); + this.setDirty(true); +} + + +proto.lookAt = function(P, Up) { + Up = Up || this._oUpVec; + this._oOutVec = P.returnSub(this._oPosition).unit(); + + //this._oSideVec = this._oOutVec.cross(new Canvas3D.Vec3 (0.0, 1.0, 0.0)).unit(); + //this._oSideVec = this._oOutVec.cross(this._oUpVec).unit(); + this._oSideVec = this._oOutVec.cross(Up).unit(); + this._oUpVec = this._oSideVec.cross(this._oOutVec).unit(); + this._vecLookAt = P.clone(); + this.setDirty(true); +} + +proto.getLookAt = function() { + return this._vecLookAt; +} + +proto.updateRotationMatrix = function() { + var e0 = this._oRotMat.e[0]; + var e1 = this._oRotMat.e[1]; + var e2 = this._oRotMat.e[2]; + + e0[0] = this._oSideVec.x; + e0[1] = this._oSideVec.y; + e0[2] = this._oSideVec.z; + + e1[0] = this._oUpVec.x; + e1[1] = this._oUpVec.y; + e1[2] = this._oUpVec.z; + + e2[0] = this._oOutVec.x; + e2[1] = this._oOutVec.y; + e2[2] = this._oOutVec.z; +} + +proto.transformPoint = function(P) { + var e = this._oRotMat.e; + var oPos = this._oPosition; + + var e0 = e[0]; + var e1 = e[1]; + var e2 = e[2]; + + var vx = P.x - oPos.x; + var vy = P.y - oPos.y; + var vz = P.z - oPos.z; + + return new Canvas3D.Vec3( + (vx * e0[0] + vy * e0[1] + vz * e0[2]), + (vx * e1[0] + vy * e1[1] + vz * e1[2]), + (vx * e2[0] + vy * e2[1] + vz * e2[2]) + ); +} + +proto.project = function(P) { + var fFocal = this._fFocal; + return { + x: P.x * fFocal / (P.z + this._fFocalDistance) * this._fScale * (this._bReverseX?-1:1), + y: -P.y * fFocal / (P.z + this._fFocalDistance) * this._fScale * (this._bReverseY?-1:1) + }; +} + +proto.clip = function(P) { + if (P.z < 0) { + return true; + } + return false; +} + +proto.isBehind = function(P) { + if (P.z > 0) return false; + return false; +} + +proto.getClipNear = function() { + return this._iClipNear; +} + +proto.getClipFar = function() { + return this._iClipFar; +} + +proto.clip = function(P) { + if (P.z > this._iClipNear && P.z < this._iClipFar) { + return false; + } else { + return true; + } +} + +proto.setFOV = function(fFOV) { + this._fFOV = fFOV; + var fFocal = 1 / Math.tan(105 * Math.PI*Math.PI / (180*180 * 2)); + this._fFocal = fFocal; + this._fFocalDistance = fFocal; +} + +proto.getFOV = function() { + return this._fFOV; +} + +proto.getFocal = function() { + return this._fFocal; +} + +proto.setFocalDistance = function(fValue) { + this._fFocalDistance = fValue; +} + +proto.setReverseX = function(bEnable) { + this._bReverseX = bEnable; +} + +proto.setReverseY = function(bEnable) { + this._bReverseY = bEnable; +} diff --git a/public/gfiles/html5games/chess/canvas3dengine/light.jsfb b/public/gfiles/html5games/chess/canvas3dengine/light.jsfb new file mode 100644 index 00000000..140c5720 --- /dev/null +++ b/public/gfiles/html5games/chess/canvas3dengine/light.jsfb @@ -0,0 +1,75 @@ +/* + * Javascript/Canvas Textured 3D Renderer v0.3 + * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com + * This software is free to use for non-commercial purposes. For anything else, please contact the author. + * This is a version modified by Stefano Gioffre'. + */ + +var __iLightIDCounter = 0; + +Canvas3D.Light = function() { + this._oPosition = new Canvas3D.Vec3(0,0,0); + this._oColor = {r:255,g:255,b:255}; + this._fIntensity = 1.0; + + this._iLightID = ++__iLightIDCounter; +}; + +var proto = Canvas3D.Light.prototype; + +proto.setPosition = function(oPos) { + this._oPosition = oPos; + this._bDirty = true; +} + +proto.getPosition = function() { + return this._oPosition; +} + +proto.setColor = function(oColor) { + this._oColor = oColor; + this._bDirty = true; +} + +proto.getColor = function() { + return this._oColor; +} + +proto.setIntensity = function(fIntensity) { + this._fIntensity = fIntensity; + this._bDirty = true; +} + +proto.getIntensity = function(fIntensity) { + return this._fIntensity; +} + +proto.setScene = function(oScene) { + if (this._oScene != oScene) { + this._oScene = oScene; + this._bDirty = true; + } +} + +proto.draw = function(oContext, iOffsetX, iOffsetY) { + var oScene = this._oScene; + var oCam = oScene.getActiveCamera(); + + var oPos2D = oCam.transform2D(oCam.transformPoint(this._oPosition)); + + var iRadius = 3; + + oContext.beginPath(); + oContext.moveTo(oPos2D.x + iOffsetX + iRadius, oPos2D.y + iOffsetY); + oContext.arc(oPos2D.x + iOffsetX, oPos2D.y + iOffsetY, iRadius, 0, 360, false); + oContext.fillStyle = "rgb(255,255,0)"; + oContext.fill(); +} + +proto.getDirty = function() { + return this._bDirty; +} + +proto.setDirty = function(bDirty) { + this._bDirty = bDirty; +} diff --git a/public/gfiles/html5games/chess/canvas3dengine/matrix3.jsfb b/public/gfiles/html5games/chess/canvas3dengine/matrix3.jsfb new file mode 100644 index 00000000..e26b45cb --- /dev/null +++ b/public/gfiles/html5games/chess/canvas3dengine/matrix3.jsfb @@ -0,0 +1,122 @@ +/* + * Javascript/Canvas Textured 3D Renderer v0.3 + * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com + * This software is free to use for non-commercial purposes. For anything else, please contact the author. + * This is a version modified by Stefano Gioffre'. + */ + +Canvas3D.Matrix3 = function() { + this.e = [ + [1.0, 0.0, 0.0], + [0.0, 1.0, 0.0], + [0.0, 0.0, 1.0] + ]; +} + +Canvas3D.Matrix3.prototype.multiplyVector = function(v) { + var p = new Canvas3D.Vec3; + var e0 = this.e[0], e1 = this.e[1], e2 = this.e[2]; + + var vx = v.x, vy = v.y, vz = v.z; + + p.x = vx * e0[0] + vy * e0[1] + vz * e0[2]; + p.y = vx * e1[0] + vy * e1[1] + vz * e1[2]; + p.z = vx * e2[0] + vy * e2[1] + vz * e2[2]; + return p; +} + +Canvas3D.Matrix3.prototype.multiplyMatrix = function(M) { + var p = new Canvas3D.Matrix3(); + var e0 = this.e[0], e1 = this.e[1], e2 = this.e[2]; + + var m0 = M.e[0], m1 = M.e[1], m2 = M.e[2]; + + var e00 = e0[0], e01 = e0[1], e02 = e0[2]; + var e10 = e1[0], e11 = e1[1], e12 = e1[2]; + var e20 = e2[0], e21 = e2[1], e22 = e2[2]; + + var m00 = m0[0], m01 = m0[1], m02 = m0[2]; + var m10 = m1[0], m11 = m1[1], m12 = m1[2]; + var m20 = m2[0], m21 = m2[1], m22 = m2[2]; + + p.e[0][0] = m00 * e00 + m10 * e01 + m20 * e02; + p.e[0][1] = m01 * e00 + m11 * e01 + m21 * e02; + p.e[0][2] = m02 * e00 + m12 * e01 + m22 * e02; + + p.e[1][0] = m00 * e10 + m10 * e11 + m20 * e12; + p.e[1][1] = m01 * e10 + m11 * e11 + m21 * e12; + p.e[1][2] = m02 * e10 + m12 * e11 + m22 * e12; + + p.e[2][0] = m00 * e20 + m10 * e21 + m20 * e22; + p.e[2][1] = m01 * e20 + m11 * e21 + m21 * e22; + p.e[2][2] = m02 * e20 + m12 * e21 + m22 * e22; + + return p; +} + +Canvas3D.Matrix3.prototype.transpose = function() { + var t = new Canvas3D.Matrix3(); + t.e[0][0] = this.e[0][0]; + t.e[0][1] = this.e[1][0]; + t.e[0][2] = this.e[2][0]; + t.e[1][0] = this.e[0][1]; + t.e[1][1] = this.e[1][1]; + t.e[1][2] = this.e[2][1]; + t.e[2][0] = this.e[0][2]; + t.e[2][1] = this.e[1][2]; + t.e[2][2] = this.e[2][2]; + return t; +} + +Canvas3D.Matrix3.prototype.loadIdentity = function() { + var e0 = this.e[0], e1 = this.e[1], e2 = this.e[2]; + e0[0] = 1; e0[1] = 0; e0[2] = 0; + e1[0] = 0; e1[1] = 1; e1[2] = 0; + e2[0] = 0; e2[1] = 0; e2[2] = 1; +} + +Canvas3D.Matrix3.prototype.loadRotationX = function(s, c) { + var e0 = this.e[0], e1 = this.e[1], e2 = this.e[2]; + e0[0] = 1; e0[1] = 0; e0[2] = 0; + e1[0] = 0; e1[1] = c; e1[2] = -s; + e2[0] = 0; e2[1] = s; e2[2] = c; +} + +Canvas3D.Matrix3.prototype.loadRotationY = function(s, c) { + var e0 = this.e[0], e1 = this.e[1], e2 = this.e[2]; + e0[0] = c; e0[1] = 0; e0[2] = s; + e1[0] = 0; e1[1] = 1; e1[2] = 0; + e2[0] = -s; e2[1] = 0; e2[2] = c; +} + +Canvas3D.Matrix3.prototype.loadRotationZ = function(s, c) { + var e0 = this.e[0], e1 = this.e[1], e2 = this.e[2]; + e0[0] = c; e0[1] = -s; e0[2] = 0; + e1[0] = s; e1[1] = c; e1[2] = 0; + e2[0] = 0; e2[1] = 0; e2[2] = 1; +} + +Canvas3D.Matrix3.prototype.loadRotationAxis = function(A, s, c) { + var t = 1 - c; + var tx = t * A.x; + var ty = t * A.y; + var txx = tx * A.x; + var txy = tx * A.y; + var txz = tx * A.z; + var tyy = ty * A.y; + var tyz = ty * A.z; + var tzz = t * A.z * A.z; + var sx = s * A.x; + var sy = s * A.y; + var sz = s * A.z; + + this.e[0][0] = txx + c; + this.e[0][1] = txy - sz; + this.e[0][2] = txz + sy; + this.e[1][0] = txy + sz; + this.e[1][1] = tyy + c; + this.e[1][2] = tyz - sx; + this.e[2][0] = txz - sy; + this.e[2][1] = tyz + sx; + this.e[2][2] = tzz + c; +} diff --git a/public/gfiles/html5games/chess/canvas3dengine/mesh.jsfb b/public/gfiles/html5games/chess/canvas3dengine/mesh.jsfb new file mode 100644 index 00000000..2a54a316 --- /dev/null +++ b/public/gfiles/html5games/chess/canvas3dengine/mesh.jsfb @@ -0,0 +1,906 @@ +/* + * Javascript/Canvas Textured 3D Renderer v0.3.1 + * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com + * This software is free to use for non-commercial purposes. For anything else, please contact the author. + * This is a version modified by Stefano Gioffre'. + */ + +Canvas3D.Mesh = function() { + + this._oPosition = new Canvas3D.Vec3(0,0,0); + this._oRotation = new Canvas3D.Vec3(0,0,0); + + this._aVertices = []; // vertex positions in object space + this._aGlobalVertices = []; // vertices translated to global space + this._aFaces = []; + this._aNormals = []; + this._aMaterials = []; + + this._bDirty = true; + + this._bVisible = true; + + this._iForcedZ = -1; + this._bHideWhenRotating = false; + + this._oDefaultColor = {r:155,g:155,b:155}; + this._oDefaultMaterial = {}; + this._iSize = 1; + + this._fScale = 1; + + this._bFill = true; // render filled triangles + this._bWire = false; // render wireframe + this._bShading = true; // shade/light filled triangles + this._bBackfaceCull = true; // only draw triangles facing the camera + this._bZSort = true; // sort triangles back-to-front + this._bExpandClipPath = true; // expand clip path by 1px, to minimize gaps + + this._bTexture = false; // render textured triangles (must enable bFill as well) + this._bTextureShading = false; // render shading on textured triangles (must enable bShading as well) + + // sometimes the exported normals from Max are messed up, or they're imported wrong or whatever, I don't know. + // We can recalculate them after loading. + this._bCalcNormals = true; + + // only allow textures if canvas is available + var oCanvas = document.createElement("canvas"); + this._bCanTexture = false; + this._bCanTextureUV = false; + if (oCanvas.getContext && oCanvas.getContext("2d")) { + this._bCanTexture = true; + if (oCanvas.getContext("2d").getImageData) { + this._bCanTextureUV = true; + } + } + +}; + +// parse the mesh data +// the mesh data (vertices, faces, texture coordinates, materials) are read from a JSON object structure +// and copied into local arrays +// normals are recalculated, if enabled. +Canvas3D.Mesh.prototype.setMeshData = function(oMeshData, oScene) { + this._oMeshData = oMeshData; + + this._aVertices = []; + this._aFaces = []; + this._aNormals = []; + this._aMaterials = []; + + var oPos = this._oPosition; + + var me = this; + + if (this._oMeshData.mat) { + for (var m=0;m= iSrcWidth) iSrcX = iSrcWidth-1; + if (iSrcY >= iSrcHeight) iSrcY = iSrcHeight-1; + + var iDstPixOffset = iDstYOffset + iDstX*4; + var iSrcPixOffset = (iSrcY*iSrcWidth + iSrcX)*4; + + aDstData[iDstPixOffset] = aSrcData[iSrcPixOffset]; + aDstData[iDstPixOffset+1] = aSrcData[iSrcPixOffset+1]; + aDstData[iDstPixOffset+2] = aSrcData[iSrcPixOffset+2]; + + aDstData[iDstPixOffset+3] = oMat.texalpha ? aSrcData[iSrcPixOffset+3] : 255; + + } while (--x); + + iOffsetX++; + iDstXOffset++; + + } while (--y); + + iTexOffsetX += iTexRes; + + } while (--f); + + oDstCtx.putImageData(oDstDataObj, 0, 0); + oDstCtx.fillRect(0,0,0,0); // Opera doesn't update until we draw something? + oMat.facecanvas = oTexCanvas; +} + +var fncZSort = function(a, b) { + return a.transcenter.z - b.transcenter.z; +} + +// math and misc shortcuts +var sin = Math.sin; +var cos = Math.cos; +var asin = Math.asin; +var acos = Math.acos; +var pow = Math.pow; +var sqrt = Math.sqrt; + +var fRadDeg = 180 / Math.PI; +var fDegRad = Math.PI / 180; +var fDegRad45 = fDegRad*45; +var fDegRad90 = fDegRad*90; +var fDegRad180 = fDegRad*180; +var fSqrt2Div2 = sqrt(2) / 2; + +// this functions draws a textured (and shaded) triangle on the canvas +// this is done by rotating/scaling a triangular section in place, setting up a clipping path and drawing the image. +// if UV coords are enabled, only the correct part of the triangle-strip texture is drawn +// if not, the entire texture is drawn for each face +// some of the code used for skewing the image was inspired by the AS function found here: +// http://www.senocular.com/flash/actionscript.php?file=ActionScript_1.0/Prototypes/MovieClip/skew.as + +Canvas3D.Mesh.prototype._drawTextureTriangle = function(oContext, oMat, oPoint1, oPoint2, oPoint3, iOffsetX, iOffsetY, fShade, oNormal, iIdx) { + if (!oMat.image) { + return; + } + if (!oMat.image.complete) { + return; + } + + var oMatImage = oMat.image; + + if (!oMatImage.canvas) { + // first time around, we paint the texture image to a canvas + // drawing the triangle later on is slightly faster using another canvas object rather than an image object + // this should be moved to someplace else + + var iTexWidth = 50; + var iTexHeight = 50; + + var oTextureCanvas = document.createElement("canvas"); + + oTextureCanvas.width = iTexWidth; + oTextureCanvas.height = iTexHeight; + + oTextureCanvas.style.width = iTexWidth + "px"; + oTextureCanvas.style.height = iTexHeight + "px"; + + var oTexCtx = oTextureCanvas.getContext("2d"); + oTexCtx.drawImage(oMatImage, 0, 0, iTexWidth, iTexHeight); + + oMatImage.canvas = oTextureCanvas; + } + + var x1 = oPoint1.x; + var y1 = oPoint1.y; + var x2 = oPoint2.x; + var y2 = oPoint2.y; + var x3 = oPoint3.x; + var y3 = oPoint3.y; + + // trig to calc the angle we need to rotate in order get our texturetriangle in place + var dx = x3 - x2; + var dy = y3 - y2; + var a = sqrt((dx*dx + dy*dy)); + + dx = x3 - x1; + dy = y3 - y1; + var b = sqrt((dx*dx + dy*dy)); + + dx = x2 - x1; + dy = y2 - y1; + var c = sqrt((dx*dx + dy*dy)); + + var aa = a*a, bb = b*b, cc = c*c; + + var fCosB = (aa + cc - bb) / (2*a*c); + var fAngleB = acos(fCosB); + if (isNaN(fAngleB)) return; + + var fCosC = (aa + bb - cc) / (2*a*b); + var fAngleC = acos(fCosC); + if (isNaN(fAngleC)) return; + + if ((fAngleB + fAngleC) == 0) return; + + var fSkewX = -(fDegRad90 - (fAngleB + fAngleC)); + + var fTriRotation = -(asin((y2 - y1) / c)); + + if (x2 > x1) { // rotate the other way around if triangle is flipped + fTriRotation = fDegRad180 - fTriRotation; + } + + if (fSkewX == fDegRad90) fSkewX = fDegRad*89.99; + if (fSkewX == -fDegRad90) fSkewX = -fDegRad*89.99; + + var fCosX = cos(fSkewX); + + var fRotation = fDegRad45 + fSkewX * 0.5; + + var fDiv = 1 / (sin(fRotation) * fSqrt2Div2); + + var fScaleX = fCosX * fDiv; + var fScaleY = (sin(fSkewX) + 1) * fDiv; + + // setup the clipping path, so only texture within the triangle is drawn. + var iClipX1 = x1 + iOffsetX; + var iClipY1 = y1 + iOffsetY; + + var iClipX2 = x2 + iOffsetX; + var iClipY2 = y2 + iOffsetY; + + var iClipX3 = x3 + iOffsetX; + var iClipY3 = y3 + iOffsetY; + + // here we try to expand the clip path by 1 pixel to get rid of (some of the) gaps between the triangles + // we do this simply by moving the topmost point 1px up, the leftmost point 1px left, and so on. + // later, we also render the triangle itself 1 px too big + // drawbacks are that the contour of the object will appear a bit "pointy". + if (this._bExpandClipPath && false) { + if (iClipY1 < iClipY2 && iClipY1 < iClipY3) + iClipY1--; + else if (iClipY2 < iClipY1 && iClipY2 < iClipY3) + iClipY2--; + else if (iClipY3 < iClipY1 && iClipY3 < iClipY2) + iClipY3--; + + if (iClipY1 > iClipY2 && iClipY1 > iClipY3) + iClipY1++; + else if (iClipY2 > iClipY1 && iClipY2 > iClipY3) + iClipY2++; + else if (iClipY3 > iClipY1 && iClipY3 > iClipY2) + iClipY3++; + + if (iClipX1 < iClipX2 && iClipX1 < iClipX3) + iClipX1--; + else if (iClipX2 < iClipX1 && iClipX2 < iClipX3) + iClipX2--; + else if (iClipX3 < iClipX1 && iClipX3 < iClipX2) + iClipX3--; + + if (iClipX1 > iClipX2 && iClipX1 > iClipX3) + iClipX1++; + else if (iClipX2 > iClipX1 && iClipX2 > iClipX3) + iClipX2++; + else if (iClipX3 > iClipX1 && iClipX3 > iClipX2) + iClipX3++; + } + + oContext.save(); + + // do the clip path + oContext.beginPath(); + oContext.moveTo(iClipX1, iClipY1); + oContext.lineTo(iClipX2, iClipY2); + oContext.lineTo(iClipX3, iClipY3); + oContext.closePath(); + oContext.clip(); + + // setup the skew/rotation transformation + oContext.translate(x2 + iOffsetX, y2 + iOffsetY); + oContext.rotate(fRotation + fTriRotation); + oContext.scale(fScaleX, fScaleY); + oContext.rotate(-fDegRad45); + + var fTriScaleX = c / 2; // 100 * 50; + var fTriScaleY = b / 2; // 100 * 50; + + if (oMat.uv) { + // we are using UV coordinates for texturing + if (this._bCanTextureUV && oMat.facecanvas) { + var iTexRes = oMat.facecanvas.resolution; + // draw our texture + // there will be a small gap between the triangles. Drawing the texture at offset (-1,-1) gets rid of some of it. + oContext.drawImage( + oMat.facecanvas, + iIdx * iTexRes + iIdx*2+1, 1, iTexRes, iTexRes, + -1, -1, + fTriScaleX + 2, + fTriScaleY + 2 + ); + } + } else { + // no UV, just draw the same texture for all faces + oContext.drawImage( + oMatImage.canvas, + -1, -1, + fTriScaleX + 2, + fTriScaleY + 2 + ); + } + + oContext.restore(); + + // if shading is turned on, render a semi-transparent black triangle on top. + // that means that a fully lit triangle will just be the raw texture, and the less lit a triangle is, the darker it gets. + // we could render semi-transparent white on top to make it brighter, but it doesn't look right, so we settle for that. + if (this._bTextureShading && fShade > 0) { + oContext.beginPath(); + oContext.moveTo(iClipX1, iClipY1); + oContext.lineTo(iClipX2, iClipY2); + oContext.lineTo(iClipX3, iClipY3); + oContext.closePath(); + + oContext.fillStyle = "rgba(0,0,0," + (fShade*0.5) + ")"; + oContext.fill(); + } +} + +// draw the mesh on the oContext canvas context, at offset [iOffsetX, iOffsetY] +Canvas3D.Mesh.prototype.draw = function(oContext, iOffsetX, iOffsetY) { + if (!this._bVisible) return; + + var oScene = this._oScene; + + var oCam = oScene.getActiveCamera(); + var oAmbient = oScene.getAmbientLight() + + // if shading is enabled, calculate the direction vectors to all light sources + var bLightDirty = false; + if (this._bShading && this._bFill) { + var aLights = oScene.getLights(); + var aLightDirections = []; + for (var l=0;l oPoint2.x)){ + bDraw = true; + } + } else { + bDraw = true; + } + + if (oCam.clip(aTransVertices[oFace.a]) || oCam.clip(aTransVertices[oFace.b]) || oCam.clip(aTransVertices[oFace.c])) { + bDraw = false; + } + + + // if triangle is facing camera, draw it + if (bDraw) { + + // get the material for this face + var oFaceMat = this._aMaterials[oFace.mat]; + if (oFaceMat) { + oFaceColor = oFaceMat; + } else { + oFaceMat = this._oDefaultMaterial; + oFaceColor = this._oDefaultColor; + } + var bFaceTexture = this._bTexture && oFaceMat.t; + + // save the original color + var oFaceOrgColor = {r:oFaceColor.r, g:oFaceColor.g, b:oFaceColor.b}; + + var fLight = 0; + var fShade = 1; + + if (this._bFill) { + // setup ambient face color + if (!bFaceTexture) { + if (bLightDirty || this._bDirty) { + var oFaceColorAmb = { + r:(oAmbient.r / 255) * oFaceColor.r, + g:(oAmbient.g / 255) * oFaceColor.g, + b:(oAmbient.b / 255) * oFaceColor.b + }; + } + } + + // do lighting + if (this._bShading) { + if (bLightDirty || this._bDirty) { + for (var l=0;l 0) { + //fDot = Math.sqrt(fDot); + fLight = fDot * aLights[l].getIntensity(); + fShade = fShade - fLight; + + // lighten the face by the light intensity + if (!bFaceTexture) { + oFaceColorAmb = { + r: oFaceColorAmb.r + oFaceColor.r * fLight, + g: oFaceColorAmb.g + oFaceColor.g * fLight, + b: oFaceColorAmb.b + oFaceColor.b * fLight + }; + } + } + } + + oFaceColorAmb.r = Math.floor(oFaceColorAmb.r); + oFaceColorAmb.g = Math.floor(oFaceColorAmb.g); + oFaceColorAmb.b = Math.floor(oFaceColorAmb.b); + + if (oFaceColorAmb.r < 0) oFaceColorAmb.r = 0; + if (oFaceColorAmb.g < 0) oFaceColorAmb.g = 0; + if (oFaceColorAmb.b < 0) oFaceColorAmb.b = 0; + + if (oFaceColorAmb.r > 255) oFaceColorAmb.r = 255; + if (oFaceColorAmb.g > 255) oFaceColorAmb.g = 255; + if (oFaceColorAmb.b > 255) oFaceColorAmb.b = 255; + + oFace.calccolor = oFaceColorAmb; + oFace.shade = fShade; + } + oFaceColorAmb = oFace.calccolor; + fShade = oFace.shade; + } + if (!bFaceTexture) { + oFaceColor = oFaceColorAmb; + } + } + + oContext.beginPath(); + oContext.moveTo(oPoint1.x + iOffsetX, oPoint1.y + iOffsetY); + oContext.lineTo(oPoint2.x + iOffsetX, oPoint2.y + iOffsetY) + oContext.lineTo(oPoint3.x + iOffsetX, oPoint3.y + iOffsetY) + oContext.closePath(); + + if (this._bFill) { + if (this._bCanTexture && this._bTexture && oFaceMat.image) { + this._drawTextureTriangle(oContext, oFaceMat, oPoint1, oPoint2, oPoint3, iOffsetX, iOffsetY, fShade, oNormal, oFace.texidx); + } else { + oContext.fillStyle = "rgb(" + oFaceColor.r + "," + oFaceColor.g + "," + oFaceColor.b + ")"; + oContext.fill(); + if (!this._bWire) { + oContext.lineWidth = 0.7; + oContext.strokeStyle = "rgb(" + oFaceColor.r + "," + oFaceColor.g + "," + oFaceColor.b + ")"; + oContext.stroke(); + } + } + } + + if (this._bWire) { + oFaceOrgColor.r = Math.min(Math.max(Math.round(oFaceOrgColor.r),0),255); + oFaceOrgColor.g = Math.min(Math.max(Math.round(oFaceOrgColor.g),0),255); + oFaceOrgColor.b = Math.min(Math.max(Math.round(oFaceOrgColor.b),0),255); + + oContext.lineWidth = 1; + oContext.strokeStyle = "rgb(" + oFaceOrgColor.r + "," + oFaceOrgColor.g + "," + oFaceOrgColor.b + ")"; + oContext.stroke(); + } + + } + } while (--f); + this._bDirty = false; +} + +Canvas3D.Mesh.prototype.setScene = function(oScene) { + if (this._oScene != oScene) { + this._oScene = oScene; + } +} + +Canvas3D.Mesh.prototype.setLighting = function(bEnable) { + this._bShading = bEnable; +} + +Canvas3D.Mesh.prototype.setBackfaceCull = function(bEnable) { + this._bBackfaceCull = bEnable; +} + +Canvas3D.Mesh.prototype.setZSort = function(bEnable) { + this._bZSort = bEnable; +} + +Canvas3D.Mesh.prototype.setFill = function(bEnable) { + this._bFill = bEnable; +} + +Canvas3D.Mesh.prototype.setWire = function(bEnable) { + this._bWire = bEnable; +} + +Canvas3D.Mesh.prototype.setTexture = function(bEnable) { + this._bTexture = bEnable; +} + +Canvas3D.Mesh.prototype.setTextureShading = function(bEnable) { + this._bTextureShading = bEnable; +} + +Canvas3D.Mesh.prototype._updateGlobalVertices = function() { + var oRot = this._oRotation; + var oPos = this._oPosition; + + for (var i = 0; i < this._aVertices.length; i++) { + var oRotatedVertex = new Canvas3D.Vec3( + this._aVertices[i].x, + this._aVertices[i].y, + this._aVertices[i].z + ); + + if (oRot.x) + oRotatedVertex.rotateX(oRot.x); + if (oRot.y) + oRotatedVertex.rotateY(oRot.y); + if (oRot.z) + oRotatedVertex.rotateZ(oRot.z); + + this._aGlobalVertices[i].x = oRotatedVertex.x * this._fScale + oPos.x; + this._aGlobalVertices[i].y = oRotatedVertex.y * this._fScale + oPos.y; + this._aGlobalVertices[i].z = oRotatedVertex.z * this._fScale + oPos.z; + } + + this._recalcNormals(); +} + +Canvas3D.Mesh.prototype._recalcNormals = function() { + for (var f = 0; f < this._aFaces.length; f++) { + var oFace = this._aFaces[f]; + + var oPoint1 = this._aGlobalVertices[oFace.a]; + var oPoint2 = this._aGlobalVertices[oFace.b]; + var oPoint3 = this._aGlobalVertices[oFace.c]; + + var oCenter = new Canvas3D.Vec3( + (oPoint1.x + oPoint2.x + oPoint3.x) / 3, + (oPoint1.y + oPoint2.y + oPoint3.y) / 3, + (oPoint1.z + oPoint2.z + oPoint3.z) / 3 + ); + + oFace.center = oCenter; + + var oNormal = new Canvas3D.Vec3( + ((oPoint1.y - oPoint2.y) * (oPoint1.z - oPoint3.z)) - ((oPoint1.z - oPoint2.z) * (oPoint1.y - oPoint3.y)), + ((oPoint1.z - oPoint2.z) * (oPoint1.x - oPoint3.x)) - ((oPoint1.x - oPoint2.x) * (oPoint1.z - oPoint3.z)), + ((oPoint1.x - oPoint2.x) * (oPoint1.y - oPoint3.y)) - ((oPoint1.y - oPoint2.y) * (oPoint1.x - oPoint3.x)) + ).unit(); + oFace.normal = oNormal; + } +} + +Canvas3D.Mesh.prototype.setPosition = function(oVec) { + if (oVec.x != this._oPosition.x || oVec.y != this._oPosition.y || oVec.z != this._oPosition.z) { + this._oPosition = oVec; + this._updateGlobalVertices(); + this._bDirty = true; + } +} + +Canvas3D.Mesh.prototype.setRotation = function(oVec) { + this._oRotation = oVec; + this._updateGlobalVertices(); + + this._bDirty = true; + +} + +Canvas3D.Mesh.prototype.getPosition = function(oVec) { + return this._oPosition; +} + +Canvas3D.Mesh.prototype.setForcedZ = function(iZ) { + this._iForcedZ = iZ; +} + +Canvas3D.Mesh.prototype.getForcedZ = function() { + return this._iForcedZ; +} + +Canvas3D.Mesh.prototype.getHideWhenRotating = function() { + return this._bHideWhenRotating; +} + +Canvas3D.Mesh.prototype.setHideWhenRotating = function(bEnable) { + this._bHideWhenRotating = bEnable; +} + +Canvas3D.Mesh.prototype.getDirty = function() { + return this._bDirty; +} + +Canvas3D.Mesh.prototype.hide = function() { + this._bVisible = false; + this._bDirty = true; +} + +Canvas3D.Mesh.prototype.show = function() { + this._bVisible = true; + this._bDirty = true; +} + +Canvas3D.Mesh.prototype.isVisible = function() { + return this._bVisible; +} + +Canvas3D.Mesh.prototype.setScale = function(fScale) { + this._fScale = fScale; + this._bDirty = true; + this._updateGlobalVertices(); +} + +Canvas3D.Mesh.prototype.getScale = function() { + return this._fScale; +} diff --git a/public/gfiles/html5games/chess/canvas3dengine/scene.jsfb b/public/gfiles/html5games/chess/canvas3dengine/scene.jsfb new file mode 100644 index 00000000..64a460a6 --- /dev/null +++ b/public/gfiles/html5games/chess/canvas3dengine/scene.jsfb @@ -0,0 +1,290 @@ +/* + * Javascript/Canvas Textured 3D Renderer v0.3.1 + * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com + * This software is free to use for non-commercial purposes. For anything else, please contact the author. + * This is a version modified by Stefano Gioffre'. + */ + +Canvas3D.Scene = function(oContainer, iWidth, iHeight, bObjectCanvas) { + this._oContainer = oContainer; + this._iWidth = iWidth; + this._iHeight = iHeight; + var oCanvas = this._oDrawCanvas = document.createElement("canvas"); + this._oDrawContext = oCanvas.getContext("2d"); + Canvas3D.addEvent(oCanvas, "selectstart", function() { return(false); }); + oCanvas.style.position = "absolute"; + + this._oDrawCanvas.width = iWidth; + this._oDrawCanvas.height = iHeight; + this._oDrawCanvas.style.width = iWidth + "px"; + this._oDrawCanvas.style.height = iHeight + "px"; + + oContainer.appendChild(this._oDrawCanvas); + + this._oActiveCamera = null; + + this._aObjects = []; + + this._bRunning = false; + + // if true, each object will be rendered on its own canvas + this._bUseObjectCanvas = bObjectCanvas; + + this._bMouseRotate = true; + this._bMouseRotateY = true; + this._bMouseRotateX = true; + + this._oUpVector = new Canvas3D.Vec3(0,1,0); + + this._oAmbientLight = {r:50,g:50,b:50}; + + this._bDrawLights = false; + this._aLights = []; + + this._iMaxZ = 10000000; + + var oInputOverlay = this._oInputOverlay = document.createElement("span"); + oInputOverlay.style.width = iWidth + "px"; + oInputOverlay.style.height = iHeight + "px"; + oInputOverlay.style.zIndex = this._iMaxZ + 10000000; + oInputOverlay.style.position = "absolute"; + this._oContainer.appendChild(oInputOverlay); + + Canvas3D.addEvent(oInputOverlay, "selectstart", function() { return(false); }); + +}; + +Canvas3D.Scene.prototype.getInputLayer = function() { return(this._oInputOverlay); }; + +Canvas3D.Scene.prototype.setUpVector = function(oVec) { this._oUpVector = oVec; }; + +Canvas3D.Scene.prototype.getUpVector = function() { return(this._oUpVector); }; + +Canvas3D.Scene.prototype.getAmbientLight = function() { return(this._oAmbientLight); }; + +Canvas3D.Scene.prototype.zoomCamera = function(fZoom) { this.getActiveCamera().moveForward(fZoom); }; + +Canvas3D.Scene.prototype.getObjects = function() { return(this._aObjects); }; + +Canvas3D.Scene.prototype.addObject = function(obj) { + if (this._bUseObjectCanvas) { + var oObjectCanvas = document.createElement("canvas"); + if (!oObjectCanvas.getContext) return; + + var oObjectContext = oObjectCanvas.getContext("2d"); + + Canvas3D.addEvent(oObjectCanvas, "selectstart", function() { return(false); }); + + oObjectCanvas.width = this._iWidth; + oObjectCanvas.height = this._iHeight; + + oObjectCanvas.style.width = this._iWidth + "px"; + oObjectCanvas.style.height = this._iHeight + "px"; + + oObjectCanvas.style.position = "absolute"; + this._oContainer.appendChild(oObjectCanvas); + + this._aObjects.push({ + "canvas": oObjectCanvas, + "context": oObjectContext, + "object": obj + + }); + } else { + this._aObjects.push({ "object": obj }); + } + obj.setScene(this); + this.setDirty(true); + return(obj); +}; +/* +Canvas3D.Scene.prototype.setWidth = function(iWidth) { + for (var iCnvsObj = 0; iCnvsObj < this._aObjects.length; iCnvsObj++) { + if (this._aObjects[iCnvsObj].hasOwnProperty("canvas")) { + this._aObjects[iCnvsObj].canvas.width = iWidth; + this._aObjects[iCnvsObj].canvas.style.width = iWidth + "px"; + } + } + this._iWidth = iWidth; + this._oDrawCanvas.width = iWidth; + this._oDrawCanvas.style.width = iWidth + "px"; + this._oInputOverlay.style.width = iWidth + "px"; + +}; + +Canvas3D.Scene.prototype.setHeight = function(iHeight) { + for (var iCnvsObj = 0; iCnvsObj < this._aObjects.length; iCnvsObj++) { + if (this._aObjects[iCnvsObj].hasOwnProperty("canvas")) { + this._aObjects[iCnvsObj].canvas.height = iHeight; + this._aObjects[iCnvsObj].canvas.style.height = iHeight + "px"; + } + } + this._iHeight = iHeight; + this._oDrawCanvas.height = iHeight; + this._oDrawCanvas.style.height = iHeight + "px"; + this._oInputOverlay.style.height = iHeight + "px"; +}; +*/ +Canvas3D.Scene.prototype.setDimensions = function(iWidth, iHeight) { + for (var iCnvsObj = 0; iCnvsObj < this._aObjects.length; iCnvsObj++) { + if (this._aObjects[iCnvsObj].hasOwnProperty("canvas")) { + this._aObjects[iCnvsObj].canvas.width = iWidth; + this._aObjects[iCnvsObj].canvas.height = iHeight; + this._aObjects[iCnvsObj].canvas.style.width = iWidth + "px"; + this._aObjects[iCnvsObj].canvas.style.height = iHeight + "px"; + } + } + this._iWidth = iWidth; + this._iHeight = iHeight; + this._oDrawCanvas.height = iHeight; + this._oDrawCanvas.width = iWidth; + this._oDrawCanvas.style.width = iWidth + "px"; + this._oDrawCanvas.style.height = iHeight + "px"; + this._oInputOverlay.style.width = iWidth + "px"; + this._oInputOverlay.style.height = iHeight + "px"; +}; + +Canvas3D.Scene.prototype.removeObject = function(oObject) { + for (var i=0;i -1) { + oObject.canvas.style.zIndex = oObject.object.getForcedZ(); + } else { + var oObjectPos = oCam.transformPoint(oObject.object.getPosition()); + oObject.canvas.style.zIndex = 10000000 - Math.round(oObjectPos.z*100); + } + oObject.context.clearRect(0,0,this._iWidth,this._iHeight); + if (oObject.object.draw(oObject.context, iOffsetX, iOffsetY)) { + iObjDrawn++; + } + oObject.canvas.style.display = "block"; + } + } else { + if (bCamDirty || this.getDirty()) { + if (oObject.object.draw(this._oDrawContext, iOffsetX, iOffsetY)) { + iObjDrawn++; + } + } + } + } else { + if (this._bUseObjectCanvas) { + oObject.canvas.style.display = "none"; + } + } + } + } + + if (this._bDrawLights) { + for (var c=0;c> 2, nDiffX = nTargetX - nPosX, nDiffY = nTargetY - nPosY; + switch (nPieceType) { + case 1: // pawn + if (((nDiffY | 7) - 3) >> 2 !== nWay) { return(false); } + if (nDiffX === 0) { + if ((nDiffY + 1 | 2) !== 2 && (nDiffY + 2 | 4) !== 4) { return(false); } + if (nTarget > 0) { return(false); } + if (nTargetY === nPosY + (2 * nWay)) { + if (bHasMoved) { return(false); } + if (this.lookAt(nTargetX, nTargetY - nWay) > 0) { return(false); } + } + } else if ((nDiffX + 1 | 2) === 2) { + if (nDiffY !== nWay) { return(false); } + if ((nTarget < 1 || flagTgColor === flagPcColor) && (/* not en passant: */ nPosY !== 7 + nWay >> 1 || /* if our pawn is not on the opening, or if it is but... */ nPawnStride % 10 - 1 !== nTargetX /* ...not near him another pawn has moved for first time. */)) { return(false); } + } else { return(false); } + break; + case 3: // knight + if (((nDiffY + 1 | 2) - 2 | (nDiffX + 2 | 4) - 2) !== 2 && ((nDiffY + 2 | 4) - 2 | (nDiffX + 1 | 2) - 2) !== 2) { return(false); } + if (nTarget > 0 && flagTgColor === flagPcColor) { return(false); } + break; + case 6: // queen + if (nTargetY !== nPosY && nTargetX !== nPosX && Math.abs(nDiffX) !== Math.abs(nDiffY)) { return(false); } + break; + case 5: // rook + if (nTargetY !== nPosY && nTargetX !== nPosX) { return(false); } + break; + case 4: // bishop + if (Math.abs(nDiffX) !== Math.abs(nDiffY)) { return(false); } + break; + case 2: // king + var ourRook; + if ((nDiffY === 0 || (nDiffY + 1 | 2) === 2) && (nDiffX === 0 || (nDiffX + 1 | 2) === 2)) { + if (nTarget > 0 && flagTgColor === flagPcColor) { return(false); } + } else if (ourRook = this.lookAt(30 - nDiffX >> 2 & 7, nTargetY), (nDiffX + 2 | 4) === 4 && nDiffY === 0 && !bCheck && !bHasMoved && ourRook > 0 && Boolean(ourRook & 16)) { // castling + for (var passX = nDiffX * 3 + 14 >> 2; passX < nDiffX * 3 + 22 >> 2; passX++) { if (this.lookAt(passX, nTargetY) > 0 || isThreatened(passX, nTargetY, nTargetY / 7 << 3 ^ 1)) { return(false); } } + if (nDiffX + 2 === 0 && this.aBoard[nTargetY * 10 + 22] > 0) { return(false); } + } else { return(false); } + break; + } + if (nPieceType === 5 || nPieceType === 6) { + if (nTargetY === nPosY) { + if (nPosX < nTargetX) { + for (var iOrthogX = nPosX + 1; iOrthogX < nTargetX; iOrthogX++) { if (this.lookAt(iOrthogX, nTargetY) > 0) { return(false); } } + } else { + for (var iOrthogX = nPosX - 1; iOrthogX > nTargetX; iOrthogX--) { if (this.lookAt(iOrthogX, nTargetY) > 0) { return(false); } } + } + } + if (nTargetX === nPosX) { + if (nPosY < nTargetY) { + for (var iOrthogY = nPosY + 1; iOrthogY < nTargetY; iOrthogY++) { if (this.lookAt(nTargetX, iOrthogY) > 0) { return(false); } } + } else { + for (var iOrthogY = nPosY - 1; iOrthogY > nTargetY; iOrthogY--) { if (this.lookAt(nTargetX, iOrthogY) > 0) { return(false); } } + } + } + if (nTarget > 0 && flagTgColor === flagPcColor) { return(false); } + } + if (nPieceType === 4 || nPieceType === 6) { + if (nTargetY > nPosY) { + var iObliqueY = nPosY + 1; + if (nPosX < nTargetX) { + for (var iObliqueX = nPosX + 1; iObliqueX < nTargetX; iObliqueX++) { + if (this.lookAt(iObliqueX, iObliqueY) > 0) { return(false); } + iObliqueY++; + } + } else { + for (var iObliqueX = nPosX - 1; iObliqueX > nTargetX; iObliqueX--) { + if (this.lookAt(iObliqueX, iObliqueY) > 0) { return(false); } + iObliqueY++; + } + } + } + if (nTargetY < nPosY) { + var iObliqueY = nPosY - 1; + if (nPosX < nTargetX) { + for (var iObliqueX = nPosX + 1; iObliqueX < nTargetX; iObliqueX++) { + if (this.lookAt(iObliqueX, iObliqueY) > 0) { return(false); } + iObliqueY--; + } + } else { + for (var iObliqueX = nPosX - 1; iObliqueX > nTargetX; iObliqueX--) { + if (this.lookAt(iObliqueX, iObliqueY) > 0) { return(false); } + iObliqueY--; + } + } + } + if (nTarget > 0 && flagTgColor === flagPcColor) { return(false); } + } + /* Although it might seem impossible that the target is the opponent's king, this condition is needed for certain hypothesis. */ + if (nTarget + 6 & 7) { + var bKingInCheck = false, oKing = nPieceType === 2 ? endSq : kings[flagPcColor >> 3]; + this.aBoard[startSq] = 0; + this.aBoard[endSq] = nPiece; + if (isThreatened(oKing % 10 - 1, (oKing - oKing % 10) / 10 - 2, flagPcColor ^ 8)) { bKingInCheck = true; } + this.aBoard[startSq] = nPiece; + this.aBoard[endSq] = nTarget; + if (bKingInCheck) { return(false); } + } + return(true); + }, + makeSelection: function(nSquareId, bFromSolid) { + if (!bReady) { return; } + fourBtsLastPc = (etc.aBoard[nSquareId] ^ flagWhoMoved) & 15; + if (fourBtsLastPc > 8) { + if (etc.bSolidView) { oSolidBoard.selectPiece(nSquareId, true, bFromSolid); } + if (etc.bFlatView) { + if (nFrstFocus) { squareFocus(nFrstFocus, false); } + if (!bFromSolid) { squareFocus(nSquareId, true); } + } + nFrstFocus = nSquareId; + } + else if (nFrstFocus && fourBtsLastPc < 9) { + if (iHistPointr + 1 < aHistory.length && etc.isValidMove(nFrstFocus % 10 - 1, (nFrstFocus - nFrstFocus % 10) / 10 - 2, nSquareId % 10 - 1, (nSquareId - nSquareId % 10) / 10 - 2)) { + if (confirm("Moving now all subsequent moves will be lost. Are you sure?")) { trimHistory(); } + else { return; } + } + nScndFocus = nSquareId; + fourBtsLastPc = etc.aBoard[nFrstFocus] & 15; + if ((fourBtsLastPc & 7) === 1 & (nScndFocus < 29 | nScndFocus > 90)) { fourBtsLastPc = 14 - etc.nPromotion ^ flagWhoMoved; } + consider(0, 0, 0, 21, nPawnStride, 1); + if (etc.bSolidView) { oSolidBoard.selectPiece(nSquareId, false, bFromSolid); } + if (etc.bFlatView) { + squareFocus(nFrstFocus, false); + writeFlatPieces(); + } + if (bAI && flagWhoMoved === flagHumanBlack && fourBtsLastPc - flagHumanBlack < 9) { + bReady = false; + window.setTimeout(engineMove, 250); + } + } + } + }; + + function newPGNHeader() { + var sOpp = bAI ? "HTMLChess" : "?"; + for (var iOldKey in oGameInfo) { delete oGameInfo[iOldKey]; } + oGameInfo.Event = "No name match"; + oGameInfo.Site = document.domain || "?"; + oGameInfo.Date = (new Date()).toLocaleDateString(); + oGameInfo.Round = bAI ? String(iRound++) : "1"; + if (flagHumanBlack) { oGameInfo.White = sOpp; oGameInfo.Black = "Human"; } + else { oGameInfo.White = "Human"; oGameInfo.Black = sOpp; } + oGameInfo.Result = "*"; + updatePGNHeader(); + } + + function isThreatened(nPieceX, nPieceY, flagFromColor) { + var iMenacing, bIsThrtnd = false; + for (var iMenaceY = 0; iMenaceY < 8; iMenaceY++) { + for (var iMenaceX = 0; iMenaceX < 8; iMenaceX++) { + iMenacing = etc.aBoard[iMenaceY * 10 + iMenaceX + 21]; + if (iMenacing > 0 && (iMenacing & 8) === flagFromColor && etc.isValidMove(iMenaceX, iMenaceY, nPieceX, nPieceY)) { bIsThrtnd = true; break; } + } + if (bIsThrtnd) { break; } + } + return(bIsThrtnd); + } + + function getInCheckPieces() { + var iExamX, iExamY, iExamPc, bNoMoreMoves = true, myKing = kings[flagWhoMoved >> 3 ^ 1]; + bCheck = isThreatened(myKing % 10 - 1, (myKing - myKing % 10) / 10 - 2, flagWhoMoved); + etc.aThreats.splice(0); + for (var iExamSq = 21; iExamSq < 99; iExamSq += iExamSq % 10 < 8 ? 1 : 3) { + iExamX = iExamSq % 10 - 1; + iExamY = (iExamSq - iExamSq % 10) / 10 - 2; + iExamPc = etc.aBoard[iExamSq]; + if (bNoMoreMoves && iExamPc > 0 && (iExamPc & 8 ^ 8) === flagWhoMoved) { + for (var iWaySq = 21; iWaySq < 99; iWaySq += iWaySq % 10 < 8 ? 1 : 3) { + if (etc.isValidMove(iExamX, iExamY, iWaySq % 10 - 1, (iWaySq - iWaySq % 10) / 10 - 2)) { bNoMoreMoves = false; break; } + } + } + if ((!bCheck || (iExamPc & 7) === 2) && iExamPc > 0 && (iExamPc & 8 ^ 8) === flagWhoMoved && isThreatened(iExamX, iExamY, flagWhoMoved)) { etc.aThreats.push(iExamSq); } + } + if (bNoMoreMoves) { + if (bCheck) { + var sWinner = flagWhoMoved ? "Black" : "White"; + oGameInfo.Result = flagWhoMoved ? "0-1" : "1-0"; + sendMsg((oGameInfo.hasOwnProperty(sWinner) ? oGameInfo[sWinner] : sWinner) + " wins.", "The king is threatened and can not move (checkmate<\/em>).", 10000); + sMovesList = sMovesList.replace(/\+$/, "#"); + } else { + oGameInfo.Result = "1/2-1/2"; + sendMsg("Drawn game", "The opponent can not move (draw<\/em>).", 10000); + } + bGameNotOver = false; + } else if (oGameInfo.hasOwnProperty("Result") && oGameInfo.Result.search(/^(\d+\-\d+)$/) > -1 && iHistPointr === aHistory.length - 1) { + var sWinner = oGameInfo.Result.valueOf() === "1-0" ? "White" : "Black"; + sendMsg((oGameInfo.hasOwnProperty(sWinner) ? oGameInfo[sWinner] : sWinner) + " wins.", "The opponent has withdrawn.", 10000); + bGameNotOver = false; + } else { oGameInfo.Result = "*"; bGameNotOver = true; } + } + + function getPcByParams(nParamId, nWhere) { + var nPieceId = aParams[nParamId]; + if ((nPieceId & 7) === 2) { kings[nParamId >> 3 & 1] = nWhere; } + return(nPieceId); + } + + function resetBoard() { + var iParamId = 0; + nFrstFocus = fourBtsLastPc = nPawnStride = lastStart = lastEnd = 0; flagWhoMoved = 8; iHistPointr = -1; + aHistory.splice(0); + etc.aThreats.splice(0); + for (var iPosition = 1; iPosition < 121; iPosition++) { etc.aBoard[iPosition - 1] = iPosition % 10 ? iPosition / 10 % 10 < 2 | iPosition % 10 < 2 ? 7 : iPosition / 10 & 4 ? 0 : getPcByParams(iParamId++, iPosition - 1) | 16 : 7; } + sMovesList = new String(); + oMovesSelect.innerHTML = "