From fa961aa319501cd01c1e16de14a3fa88b0a6b70a Mon Sep 17 00:00:00 2001 From: jsemu2 <> Date: Tue, 23 Apr 2019 19:59:17 -0400 Subject: [PATCH] update --- .gitmodules | 3 + IodineGBA/core/CPU.js | 2 +- IodineGBA/core/CPU/ARM.js | 408 ++--- IodineGBA/core/Cartridge.js | 8 +- IodineGBA/core/Emulator.js | 508 +++--- IodineGBA/core/Graphics.js | 2 +- IodineGBA/core/Memory.js | 355 ++-- IodineGBA/core/RunLoop.js | 60 +- IodineGBA/core/Saves.js | 33 +- IodineGBA/core/Sound.js | 518 ++++-- IodineGBA/core/Wait.js | 42 +- IodineGBA/core/Worker.js | 300 ++++ IodineGBA/core/cartridge/GPIO.js | 56 + IodineGBA/core/graphics/AffineBG.js | 164 +- IodineGBA/core/graphics/BG2FrameBuffer.js | 35 +- IodineGBA/core/graphics/BGMatrix.js | 35 +- IodineGBA/core/graphics/BGTEXT.js | 67 +- IodineGBA/core/graphics/ColorEffects.js | 612 ++++--- IodineGBA/core/graphics/Compositor.js | 1443 ++++++++++------- IodineGBA/core/graphics/Mosaic.js | 42 +- IodineGBA/core/graphics/OBJ.js | 85 +- IodineGBA/core/graphics/Renderer.js | 742 ++++----- IodineGBA/core/graphics/RendererProxy.js | 70 +- IodineGBA/core/graphics/RendererShim.js | 919 +++++++++++ IodineGBA/core/graphics/Worker.js | 587 +++++++ IodineGBA/core/memory/DMA0.js | 88 +- IodineGBA/core/memory/DMA1.js | 82 +- IodineGBA/core/memory/DMA2.js | 82 +- IodineGBA/core/memory/DMA3.js | 39 +- IodineGBA/core/sound/Channel1.js | 83 +- IodineGBA/core/sound/Channel2.js | 85 +- IodineGBA/core/sound/Channel3.js | 70 +- IodineGBA/core/sound/Channel4.js | 45 +- IodineGBA/includes/TypedArrayShim.js | 75 +- README.md | 7 - launcher.html | 282 +++- user_css/main.css | 233 ++- ...neGBAAudioGlueCode.js => AudioGlueCode.js} | 171 +- user_scripts/CoreGlueCode.js | 267 +++ user_scripts/GUIGlueCode.js | 664 ++++++++ user_scripts/GfxGlueCode.js | 209 +++ user_scripts/IodineGBACoreGlueCode.js | 214 --- user_scripts/IodineGBAGraphicsGlueCode.js | 193 --- user_scripts/IodineGBAJoyPadGlueCode.js | 80 - user_scripts/IodineGBASavesGlueCode.js | 75 - user_scripts/JoyPadGlueCode.js | 124 ++ ...AROMLoadGlueCode.js => ROMLoadGlueCode.js} | 18 +- user_scripts/SavesGlueCode.js | 289 ++++ user_scripts/WorkerGfxGlueCode.js | 130 ++ user_scripts/WorkerGlueCode.js | 328 ++++ user_scripts/XAudioJS/README.md | 71 + user_scripts/XAudioJS/XAudioJS.as | 86 + user_scripts/XAudioJS/XAudioJS.swf | Bin 0 -> 1348 bytes user_scripts/XAudioJS/XAudioServer.js | 153 +- user_scripts/XAudioJS/resampler.js | 275 ++-- user_scripts/base64.js | 152 +- 56 files changed, 8235 insertions(+), 3531 deletions(-) create mode 100644 .gitmodules create mode 100644 IodineGBA/core/Worker.js create mode 100644 IodineGBA/core/cartridge/GPIO.js create mode 100644 IodineGBA/core/graphics/RendererShim.js create mode 100644 IodineGBA/core/graphics/Worker.js delete mode 100644 README.md rename user_scripts/{IodineGBAAudioGlueCode.js => AudioGlueCode.js} (65%) create mode 100644 user_scripts/CoreGlueCode.js create mode 100644 user_scripts/GUIGlueCode.js create mode 100644 user_scripts/GfxGlueCode.js delete mode 100644 user_scripts/IodineGBACoreGlueCode.js delete mode 100644 user_scripts/IodineGBAGraphicsGlueCode.js delete mode 100644 user_scripts/IodineGBAJoyPadGlueCode.js delete mode 100644 user_scripts/IodineGBASavesGlueCode.js create mode 100644 user_scripts/JoyPadGlueCode.js rename user_scripts/{IodineGBAROMLoadGlueCode.js => ROMLoadGlueCode.js} (92%) create mode 100644 user_scripts/SavesGlueCode.js create mode 100644 user_scripts/WorkerGfxGlueCode.js create mode 100644 user_scripts/WorkerGlueCode.js create mode 100644 user_scripts/XAudioJS/README.md create mode 100644 user_scripts/XAudioJS/XAudioJS.as create mode 100644 user_scripts/XAudioJS/XAudioJS.swf diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0e4b85f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "user_scripts/XAudioJS"] + path = user_scripts/XAudioJS + url = https://github.com/taisel/XAudioJS.git diff --git a/IodineGBA/core/CPU.js b/IodineGBA/core/CPU.js index 5ebcd5e..c893b94 100644 --- a/IodineGBA/core/CPU.js +++ b/IodineGBA/core/CPU.js @@ -58,7 +58,7 @@ GameBoyAdvanceCPU.prototype.initializeRegisters = function () { this.SPSR[4] = 0xD3; //Undefined this.triggeredIRQ = 0; //Pending IRQ found. //Pre-initialize stack pointers if no BIOS loaded: - if (!this.IOCore.BIOSFound || this.IOCore.settings.SKIPBoot) { + if (this.IOCore.SKIPBoot) { this.HLEReset(); } //Start in fully bubbled pipeline mode: diff --git a/IodineGBA/core/CPU/ARM.js b/IodineGBA/core/CPU/ARM.js index 3e04958..ebbac1a 100644 --- a/IodineGBA/core/CPU/ARM.js +++ b/IodineGBA/core/CPU/ARM.js @@ -1,11 +1,11 @@ "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) { @@ -72,9 +72,9 @@ 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) & 0x55555555)) | 0; - temp = ((temp & 0x33333333) + ((temp >> 2) & 0x33333333)) | 0; - temp = (((temp | 0) + (temp >> 4)) & 0xF0F0F0F) | 0; + 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; } @@ -83,9 +83,9 @@ else { //Math.imul not found, use the compatibility method: ARMInstructionSet.prototype.getPopCount = function () { var temp = this.execute & 0xFFFF; - temp = ((temp | 0) - ((temp >> 1) & 0x55555555)) | 0; - temp = ((temp & 0x33333333) + ((temp >> 2) & 0x33333333)) | 0; - temp = (((temp | 0) + (temp >> 4)) & 0xF0F0F0F) | 0; + 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; } @@ -151,7 +151,7 @@ ARMInstructionSet.prototype.performMUL32 = function () { 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.) @@ -169,7 +169,7 @@ ARMInstructionSet.prototype.performMUL32MLA = function () { 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.) @@ -195,6 +195,18 @@ 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: @@ -280,18 +292,6 @@ ARMInstructionSet.prototype.guardUserRegisterWriteLDM = function (address, data) this.guardProgramCounterRegisterWriteCPSR(data | 0); } } -ARMInstructionSet.prototype.baseRegisterWrite = function (data, userMode) { - //Update writeback for offset+base modes: - data = data | 0; - userMode = userMode | 0; - var address = (this.execute >> 16) & 0xF; - if ((address | userMode) == 0xF) { - this.guardRegisterWrite(address | 0, data | 0); - } - else { - this.guardUserRegisterWrite(address | 0, data | 0); - } -} ARMInstructionSet.prototype.readPC = function () { //PC register read: return this.registers[0xF] | 0; @@ -304,7 +304,15 @@ ARMInstructionSet.prototype.readRegister = function (address) { ARMInstructionSet.prototype.readUserRegister = function (address) { //Unguarded user mode register read: address = address | 0; - return this.registersUSR[address & 0x7] | 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: @@ -322,6 +330,10 @@ 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; @@ -329,50 +341,31 @@ ARMInstructionSet.prototype.guard12OffsetRegisterRead = function () { ARMInstructionSet.prototype.guardUserRegisterRead = function (address) { //Guard only on user access, not PC!: address = address | 0; - switch (this.CPUCore.modeFlags & 0x1f) { + var data = 0; + switch (this.CPUCore.modeFlags & 0x1F) { case 0x10: case 0x1F: - return this.readRegister(address | 0) | 0; + data = this.readRegister(address | 0) | 0; + break; case 0x11: if ((address | 0) < 8) { - return this.readRegister(address | 0) | 0; + data = this.readRegister(address | 0) | 0; } else { //User-Mode Register Read Inside Non-User-Mode: - return this.readUserRegister(address | 0) | 0; + data = this.readUserRegister(address | 0) | 0; } break; default: if ((address | 0) < 13) { - return this.readRegister(address | 0) | 0; + data = this.readRegister(address | 0) | 0; } else { //User-Mode Register Read Inside Non-User-Mode: - return this.readUserRegister(address | 0) | 0; + data = this.readUserRegister(address | 0) | 0; } } -} -ARMInstructionSet.prototype.guardUserRegisterReadSTM = function (address) { - //Proxy guarded user mode read (used by STM*): - address = address | 0; - if ((address | 0) < 0xF) { - return this.guardUserRegisterRead(address | 0) | 0; - } - else { - //Get Special Case PC Read: - return this.readPC() | 0; - } -} -ARMInstructionSet.prototype.baseRegisterRead = function (userMode) { - //Read specially for offset+base modes: - userMode = userMode | 0; - var address = (this.execute >> 16) & 0xF; - if ((address | userMode) == 0xF) { - return this.readRegister(address | 0) | 0; - } - else { - return this.guardUserRegisterRead(address | 0) | 0; - } + return data | 0; } ARMInstructionSet.prototype.BX = function () { //Branch & eXchange: @@ -893,16 +886,16 @@ ARMInstructionSet.prototype.MSR = function () { ARMInstructionSet.prototype.MSR1 = function () { var newcpsr = this.read0OffsetRegister() | 0; this.branchFlags.setNZCV(newcpsr | 0); - if ((this.execute & 0x10000) == 0x10000 && (this.CPUCore.modeFlags & 0x1f) != 0x10) { + if ((this.execute & 0x10000) != 0 && (this.CPUCore.modeFlags & 0x1F) != 0x10) { this.CPUCore.switchRegisterBank(newcpsr & 0x1F); - this.CPUCore.modeFlags = newcpsr & 0xdf; + 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) { + switch (this.CPUCore.modeFlags & 0x1F) { case 0x12: //IRQ break; case 0x13: //Supervisor @@ -921,7 +914,7 @@ ARMInstructionSet.prototype.MSR2 = function () { return; } var spsr = (operand >> 20) & 0xF00; - if ((this.execute & 0x10000) == 0x10000) { + if ((this.execute & 0x10000) != 0) { spsr = spsr | (operand & 0xFF); } else { @@ -936,7 +929,7 @@ ARMInstructionSet.prototype.MSR3 = function () { ARMInstructionSet.prototype.MSR4 = function () { var operand = this.imm() >> 20; var bank = 1; - switch (this.CPUCore.modeFlags & 0x1f) { + switch (this.CPUCore.modeFlags & 0x1F) { case 0x12: //IRQ break; case 0x13: //Supervisor @@ -1119,13 +1112,13 @@ ARMInstructionSet.prototype.LDRSB2 = function () { } ARMInstructionSet.prototype.STR = function () { //Perform word store calculations: - var address = this.operand2OP_LoadStore3(0) | 0; + 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_LoadStore3(0) | 0; + var address = this.operand2OP_LoadStore3Normal() | 0; //Read from memory location: this.guard12OffsetRegisterWrite(this.CPUCore.read32(address | 0) | 0); //Internal Cycle: @@ -1133,13 +1126,13 @@ ARMInstructionSet.prototype.LDR = function () { } ARMInstructionSet.prototype.STRB = function () { //Perform byte store calculations: - var address = this.operand2OP_LoadStore3(0) | 0; + 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_LoadStore3(0) | 0; + var address = this.operand2OP_LoadStore3Normal() | 0; //Read from memory location: this.guard12OffsetRegisterWrite(this.CPUCore.read8(address | 0) | 0); //Internal Cycle: @@ -1175,13 +1168,13 @@ ARMInstructionSet.prototype.LDRB4 = function () { } ARMInstructionSet.prototype.STRT = function () { //Perform word store calculations (forced user-mode): - var address = this.operand2OP_LoadStore3(0xF) | 0; + 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_LoadStore3(0xF) | 0; + var address = this.operand2OP_LoadStore3User() | 0; //Read from memory location: this.guard12OffsetRegisterWrite(this.CPUCore.read32(address | 0) | 0); //Internal Cycle: @@ -1189,13 +1182,13 @@ ARMInstructionSet.prototype.LDRT = function () { } ARMInstructionSet.prototype.STRBT = function () { //Perform byte store calculations (forced user-mode): - var address = this.operand2OP_LoadStore3(0xF) | 0; + 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_LoadStore3(0xF) | 0; + var address = this.operand2OP_LoadStore3User() | 0; //Read from memory location: this.guard12OffsetRegisterWrite(this.CPUCore.read8(address | 0) | 0); //Internal Cycle: @@ -1203,13 +1196,13 @@ ARMInstructionSet.prototype.LDRBT = function () { } ARMInstructionSet.prototype.STR2 = function () { //Perform word store calculations: - var address = this.operand2OP_LoadStore5(0) | 0; + 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_LoadStore5(0) | 0; + var address = this.operand2OP_LoadStore5Normal() | 0; //Read from memory location: this.guard12OffsetRegisterWrite(this.CPUCore.read32(address | 0) | 0); //Internal Cycle: @@ -1217,13 +1210,13 @@ ARMInstructionSet.prototype.LDR2 = function () { } ARMInstructionSet.prototype.STRB2 = function () { //Perform byte store calculations: - var address = this.operand2OP_LoadStore5(0) | 0; + 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_LoadStore5(0) | 0; + var address = this.operand2OP_LoadStore5Normal() | 0; //Read from memory location: this.guard12OffsetRegisterWrite(this.CPUCore.read8(address | 0) | 0); //Internal Cycle: @@ -1231,13 +1224,13 @@ ARMInstructionSet.prototype.LDRB2 = function () { } ARMInstructionSet.prototype.STRT2 = function () { //Perform word store calculations (forced user-mode): - var address = this.operand2OP_LoadStore5(0xF) | 0; + 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_LoadStore5(0xF) | 0; + var address = this.operand2OP_LoadStore5User() | 0; //Read from memory location: this.guard12OffsetRegisterWrite(this.CPUCore.read32(address | 0) | 0); //Internal Cycle: @@ -1245,13 +1238,13 @@ ARMInstructionSet.prototype.LDRT2 = function () { } ARMInstructionSet.prototype.STRBT2 = function () { //Perform byte store calculations (forced user-mode): - var address = this.operand2OP_LoadStore5(0xF) | 0; + 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_LoadStore5(0xF) | 0; + var address = this.operand2OP_LoadStore5User() | 0; //Read from memory location: this.guard12OffsetRegisterWrite(this.CPUCore.read8(address | 0) | 0); //Internal Cycle: @@ -1488,7 +1481,7 @@ ARMInstructionSet.prototype.STMIAG = function () { 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.guardUserRegisterReadSTM(rListPosition | 0) | 0); + this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterRead(rListPosition | 0) | 0); currentAddress = ((currentAddress | 0) + 4) | 0; } } @@ -1509,7 +1502,7 @@ ARMInstructionSet.prototype.STMIAWG = function () { 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.guardUserRegisterReadSTM(rListPosition | 0) | 0); + 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) { @@ -1537,7 +1530,7 @@ ARMInstructionSet.prototype.STMDAG = function () { if ((this.execute & (1 << rListPosition)) != 0) { //Push a register into memory: currentAddress = ((currentAddress | 0) + 4) | 0; - this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterReadSTM(rListPosition | 0) | 0); + this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterRead(rListPosition | 0) | 0); } } //Updating the address bus back to PC fetch: @@ -1560,7 +1553,7 @@ ARMInstructionSet.prototype.STMDAWG = function () { if ((this.execute & (1 << rListPosition)) != 0) { //Push a register into memory: currentAddress = ((currentAddress | 0) + 4) | 0; - this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterReadSTM(rListPosition | 0) | 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; @@ -1585,7 +1578,7 @@ ARMInstructionSet.prototype.STMIBG = function () { if ((this.execute & (1 << rListPosition)) != 0) { //Push a register into memory: currentAddress = ((currentAddress | 0) + 4) | 0; - this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterReadSTM(rListPosition | 0) | 0); + this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterRead(rListPosition | 0) | 0); } } //Updating the address bus back to PC fetch: @@ -1606,7 +1599,7 @@ ARMInstructionSet.prototype.STMIBWG = function () { if ((this.execute & (1 << rListPosition)) != 0) { //Push a register into memory: currentAddress = ((currentAddress | 0) + 4) | 0; - this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterReadSTM(rListPosition | 0) | 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; @@ -1632,7 +1625,7 @@ ARMInstructionSet.prototype.STMDBG = function () { 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.guardUserRegisterReadSTM(rListPosition | 0) | 0); + this.memory.memoryWrite32(currentAddress | 0, this.guardUserRegisterRead(rListPosition | 0) | 0); currentAddress = ((currentAddress | 0) + 4) | 0; } } @@ -1655,7 +1648,7 @@ ARMInstructionSet.prototype.STMDBWG = function () { 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.guardUserRegisterReadSTM(rListPosition | 0) | 0); + 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) { @@ -2336,17 +2329,28 @@ ARMInstructionSet.prototype.operand2OP_LoadStoreOperandDetermine = function () { } return offset | 0; } -ARMInstructionSet.prototype.operand2OP_LoadStorePostT = function (offset, userMode) { +ARMInstructionSet.prototype.operand2OP_LoadStorePostTUser = function (offset) { offset = offset | 0; - userMode = userMode | 0; - var base = this.baseRegisterRead(userMode | 0) | 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.baseRegisterWrite(offset | 0, userMode | 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) { @@ -2358,27 +2362,31 @@ ARMInstructionSet.prototype.operand2OP_LoadStoreNotT = function (offset) { else { offset = ((base | 0) + (offset | 0)) | 0; } - if ((this.execute & 0x200000) == 0x200000) { + if ((this.execute & 0x200000) != 0) { this.guard16OffsetRegisterWrite(offset | 0); } return offset | 0; } ARMInstructionSet.prototype.operand2OP_LoadStore1 = function () { - return this.operand2OP_LoadStorePostT(this.operand2OP_LoadStoreOperandDetermine() | 0, 0) | 0; + 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_LoadStore3 = function (userMode) { - userMode = userMode | 0; - return this.operand2OP_LoadStorePostT(this.execute & 0xFFF, userMode | 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_LoadStore5 = function (userMode) { - userMode = userMode | 0; - return this.operand2OP_LoadStorePostT(this.operand2OP_LoadStoreOffsetGen() | 0, userMode | 0) | 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; @@ -2710,115 +2718,115 @@ ARMInstructionSet.prototype.rs = function () { } function compileARMInstructionDecodeMap() { var pseudoCodes = [ - "BX", - "B", - "BL", - "AND", - "AND2", - "ANDS", - "ANDS2", - "EOR", - "EOR2", - "EORS", - "EORS2", - "SUB", - "SUB2", - "SUBS", - "SUBS2", - "RSB", - "RSB2", - "RSBS", - "RSBS2", - "ADD", - "ADD2", - "ADDS", - "ADDS2", - "ADC", - "ADC2", - "ADCS", - "ADCS2", - "SBC", - "SBC2", - "SBCS", - "SBCS2", - "RSC", - "RSC2", + "LDRH", + "MOVS2", + "MUL", + "MSR", + "LDRSH", + "MVN2", + "SMLAL", "RSCS", - "RSCS2", - "TSTS", - "TSTS2", - "TEQS", - "TEQS2", "CMPS", - "CMPS2", - "CMNS", + "MRS", + "RSBS2", + "ADDS", + "SUBS", + "RSB", + "SUBS2", + "MULS", + "SMLALS", + "STRB", "CMNS2", - "ORR", + "UMLALS", "ORR2", - "ORRS", - "ORRS2", - "MOV", + "BX", + "RSBS", + "LDRSB", + "LoadStoreMultiple", + "ANDS2", + "BIC", + "ADD2", + "SBC2", + "AND", + "TSTS2", "MOV2", "MOVS", - "MOVS2", - "BIC", - "BIC2", - "BICS", - "BICS2", - "MVN", - "MVN2", - "MVNS", - "MVNS2", - "MRS", - "MSR", - "MUL", - "MULS", - "MLA", - "MLAS", - "UMULL", - "UMULLS", - "UMLAL", - "UMLALS", - "SMULL", + "EOR", + "ORRS2", + "RSC", + "LDR2", "SMULLS", - "SMLAL", - "SMLALS", - "STRH", - "LDRH", - "LDRSH", - "LDRSB", - "STRH2", - "LDRH2", - "LDRSH2", "LDRSB2", - "STR", - "LDR", - "STRB", + "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", - "STR2", - "LDR2", - "STRB2", - "LDRB2", "STRT2", "LDRT2", "STRBT2", - "LDRBT2", - "STR3", - "LDR3", - "STRB3", - "LDRB3", - "STR4", - "LDR4", - "STRB4", - "LDRB4", - "LoadStoreMultiple", - "SWP", - "SWPB", - "SWI" + "LDRBT2" ]; function compileARMInstructionDecodeOpcodeMap(codeMap) { var opcodeIndice = 0; @@ -3647,17 +3655,33 @@ function compileARMInstructionDecodeMap() { } 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 < pseudoCodes.length; ++opcodeNumber) { - var opcodeName = pseudoCodes[opcodeNumber]; - opcodeNameMap[opcodeName] = opcodeNumber; - code += "case " + opcodeNumber + ":{this." + opcodeName + "();break};"; + 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; + opcodeNameMap["UNDEFINED"] = opcodeNumber | 0; ARMInstructionSet.prototype.executeDecoded = Function(code); return opcodeNameMap; } compileARMInstructionDecodeOpcodeMap(compileARMInstructionDecodeOpcodeSwitch()); } -compileARMInstructionDecodeMap(); \ No newline at end of file +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/IodineGBA/core/Cartridge.js b/IodineGBA/core/Cartridge.js index e68ab2d..f45778f 100644 --- a/IodineGBA/core/Cartridge.js +++ b/IodineGBA/core/Cartridge.js @@ -1,11 +1,11 @@ "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) { @@ -275,4 +275,4 @@ GameBoyAdvanceCartridge.prototype.writeROM32 = function (address, data) { GameBoyAdvanceCartridge.prototype.nextIRQEventTime = function () { //Nothing yet implement that would fire an IRQ: return 0x7FFFFFFF; -} \ No newline at end of file +} diff --git a/IodineGBA/core/Emulator.js b/IodineGBA/core/Emulator.js index 8517fcc..b230174 100644 --- a/IodineGBA/core/Emulator.js +++ b/IodineGBA/core/Emulator.js @@ -1,128 +1,167 @@ "use strict"; /* - Copyright (C) 2012-2015 Grant Galitz - + 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 GameBoyAdvanceEmulator() { this.settings = { - "SKIPBoot":false, //Skip the BIOS boot screen. - "audioVolume":1, //Starting audio volume. - "audioBufferUnderrunLimit":8, //Audio buffer minimum span amount over x interpreter iterations. - "audioBufferDynamicLimit":2, //Audio buffer dynamic minimum span amount over x interpreter iterations. - "audioBufferSize":20, //Audio buffer maximum span amount over x interpreter iterations. - "timerIntervalRate":16, //How often the emulator core is called into (in milliseconds). - "emulatorSpeed":1, //Speed multiplier of the emulator. - "metricCollectionMinimum":30, //How many cycles to collect before determining speed. - "dynamicSpeed":true //Whether to actively change the target speed for best user experience. - } - this.audioFound = false; //Do we have audio output sink found yet? - this.loaded = false; //Did we initialize IodineGBA? - this.faultFound = false; //Did we run into a fatal error? - this.paused = true; //Are we paused? - this.offscreenWidth = 240; //Width of the GBA screen. - this.offscreenHeight = 160; //Height of the GBA screen. + 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. - //Cache some frame buffer lengths: - this.offscreenRGBCount = ((this.offscreenWidth | 0) * (this.offscreenHeight | 0) * 3) | 0; - //Graphics buffers to generate in advance: - this.frameBuffer = getInt32Array(this.offscreenRGBCount | 0); //The internal buffer to composite to. - this.swizzledFrame = getUint8Array(this.offscreenRGBCount | 0); //The swizzled output buffer that syncs to the internal framebuffer on v-blank. - this.audioUpdateState = false; //Do we need to update the sound core with new info? + 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.graphicsFrameCallback = null; //Graphics blitter handler attached by GUI. - this.clockCyclesSinceStart = 0; //Clocking hueristics reference - this.metricCollectionCounted = 0; //Clocking hueristics reference - this.metricStart = null; //Date object reference. - this.dynamicSpeedCounter = 0; //Rate limiter counter for dynamic speed. - this.audioNumSamplesTotal = 0; //Buffer size. + 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) { + outputAudio:function (l, r) { parentObj.outputAudio(l, r); }, - "frameBuffer":parentObj.frameBuffer, - "prepareFrame":function () { - parentObj.prepareFrame(); + 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.paused) { - this.startTimer(); - this.paused = false; - if (!this.loaded && this.BIOS && this.ROM) { - this.initializeCore(); - this.loaded = true; + 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.paused) { - this.clearTimer(); + if ((this.emulatorStatus | 0) < 0x10) { this.exportSave(); - this.paused = true; + this.emulatorStatus = this.emulatorStatus | 0x10; + //Report new status back: + this.playStatusCallback(0); } } GameBoyAdvanceEmulator.prototype.stop = function () { - this.faultFound = false; - this.loaded = false; - this.audioUpdateState = this.audioFound; + this.emulatorStatus = this.emulatorStatus & 0x1C; + this.audioUpdateState = 1; this.pause(); } GameBoyAdvanceEmulator.prototype.restart = function () { - if (this.loaded) { - this.faultFound = false; + if ((this.emulatorStatus & 0x1) == 0x1) { + this.emulatorStatus = this.emulatorStatus & 0x1D; this.exportSave(); - this.initializeCore(); + if ((this.initializeCore() | 0) == 0) { + //Failure to initialize: + this.pause(); + return; + } this.importSave(); - this.audioUpdateState = this.audioFound; - this.setSpeed(1); + this.audioUpdateState = 1; + this.processNewSpeed(1); + this.setBufferSpace(); } } -GameBoyAdvanceEmulator.prototype.clearTimer = function () { - clearInterval(this.timer); - this.resetMetrics(); -} -GameBoyAdvanceEmulator.prototype.startTimer = function () { - this.clearTimer(); - var parentObj = this; - this.timer = setInterval(function (){parentObj.timerCallback()}, this.settings.timerIntervalRate); -} -GameBoyAdvanceEmulator.prototype.timerCallback = function () { - //Check to see if web view is not hidden, if hidden don't run due to JS timers being inaccurate on page hide: - if (!document.hidden && !document.msHidden && !document.mozHidden && !document.webkitHidden) { - if (!this.faultFound && this.loaded) { //Any error pending or no ROM loaded is a show-stopper! +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.IOCore.enter(this.CPUCyclesTotal | 0); //Step through the emulation core loop. this.iterationEndSequence(); //Run end of iteration stuff. - } - else { - this.pause(); //Some pending error is preventing execution, so pause. - } + 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.faultFound = true; //If the end routine doesn't unset this, then we are marked as having crashed. + 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.faultFound = false; //If core did not throw while running, unset the fatal error flag. + 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(); @@ -133,7 +172,7 @@ GameBoyAdvanceEmulator.prototype.attachBIOS = function (BIOS) { this.BIOS = BIOS; } GameBoyAdvanceEmulator.prototype.getGameName = function () { - if (!this.faultFound && this.loaded) { + if ((this.emulatorStatus & 0x3) == 0x1) { return this.IOCore.cartridge.name; } else { @@ -155,105 +194,155 @@ GameBoyAdvanceEmulator.prototype.attachSpeedHandler = function (handler) { 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 save = this.saveImportHandler(name); - var saveType = this.saveImportHandler("TYPE_" + name); - if (save && saveType && !this.faultFound && this.loaded) { - 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; + 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; + } } - this.IOCore.saves.importSave(convertedSave, saveType | 0); - } - } + }, 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.faultFound && this.loaded) { + if (this.saveExportHandler && (this.emulatorStatus & 0x3) == 0x1) { var save = this.IOCore.saves.exportSave(); - var saveType = this.IOCore.saves.exportSaveType(); - if (save != null && saveType != null) { + 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); + this.saveExportHandler("TYPE_" + this.IOCore.cartridge.name, [saveType | 0]); } } } GameBoyAdvanceEmulator.prototype.setSpeed = function (speed) { - var speed = Math.min(Math.max(parseFloat(speed), 0.01), 10); - this.resetMetrics(); - if (speed != this.settings.emulatorSpeed) { - this.settings.emulatorSpeed = 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(); - this.reinitializeAudio(); } } GameBoyAdvanceEmulator.prototype.incrementSpeed = function (delta) { - this.setSpeed(parseFloat(delta) + this.settings.emulatorSpeed); + delta = +delta; + this.setSpeed((+delta) + (+this.settings.emulatorSpeed)); } -GameBoyAdvanceEmulator.prototype.getSpeed = function (speed) { - return this.settings.emulatorSpeed; +GameBoyAdvanceEmulator.prototype.getSpeed = function () { + return +this.settings.emulatorSpeed; } -GameBoyAdvanceEmulator.prototype.changeCoreTimer = function (newTimerIntervalRate) { - this.settings.timerIntervalRate = Math.max(parseInt(newTimerIntervalRate, 10), 1); - if (!this.paused) { //Set up the timer again if running. - this.clearTimer(); - this.startTimer(); - } - this.calculateTimings(); - this.reinitializeAudio(); +GameBoyAdvanceEmulator.prototype.invalidateMetrics = function () { + this.clockCyclesSinceStart = 0; + this.metricStart = 0; } GameBoyAdvanceEmulator.prototype.resetMetrics = function () { this.clockCyclesSinceStart = 0; - this.metricCollectionCounted = 0; - this.metricStart = new Date(); + this.metricStart = this.lastTimestamp >>> 0; } GameBoyAdvanceEmulator.prototype.calculateTimings = function () { - this.clocksPerSecond = this.settings.emulatorSpeed * 0x1000000; - this.CPUCyclesTotal = this.CPUCyclesPerIteration = (this.clocksPerSecond / 1000 * this.settings.timerIntervalRate) | 0; - this.dynamicSpeedCounter = 0; + this.clocksPerSecond = Math.min((+this.settings.emulatorSpeed) * 0x1000000, 0x3F000000) | 0; + this.clocksPerMilliSecond = +((this.clocksPerSecond | 0) / 1000); + this.CPUCyclesPerIteration = ((+this.clocksPerMilliSecond) * (this.timerIntervalRate | 0)) | 0; + this.CPUCyclesTotal = this.CPUCyclesPerIteration | 0; + this.initializeAudioLogic(); + this.reinitializeAudio(); + this.invalidateMetrics(); +} +GameBoyAdvanceEmulator.prototype.setIntervalRate = function (intervalRate) { + intervalRate = intervalRate | 0; + if ((intervalRate | 0) > 0 && (intervalRate | 0) < 1000) { + if ((intervalRate | 0) != (this.timerIntervalRate | 0)) { + this.timerIntervalRate = intervalRate | 0; + this.calculateTimings(); + } + } } GameBoyAdvanceEmulator.prototype.calculateSpeedPercentage = function () { - if (this.metricStart) { - if ((this.metricCollectionCounted | 0) >= (this.settings.metricCollectionMinimum | 0)) { + 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 metricEnd = new Date(); - var timeDiff = Math.max(metricEnd.getTime() - this.metricStart.getTime(), 1); - var result = ((this.settings.timerIntervalRate * (this.clockCyclesSinceStart | 0) / timeDiff) / (this.CPUCyclesPerIteration | 0)) * 100 * this.settings.emulatorSpeed; - this.speedCallback(result.toFixed(2) + "%"); + 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; } - this.metricCollectionCounted = ((this.metricCollectionCounted | 0) + 1) | 0; } 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, this.coreExposed, this.BIOS, this.ROM); + 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.paused && (keyPressed | 0) >= 0 && (keyPressed | 0) <= 9) { + 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.paused && (keyReleased | 0) >= 0 && (keyReleased | 0) <= 9) { + 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 == "function") { - this.graphicsFrameCallback = handler; + if (typeof handler == "object") { + this.coreExposed.graphicsHandle = handler; } } GameBoyAdvanceEmulator.prototype.attachAudioHandler = function (mixerInputHandler) { @@ -261,153 +350,120 @@ GameBoyAdvanceEmulator.prototype.attachAudioHandler = function (mixerInputHandle this.audio = mixerInputHandler; } } -GameBoyAdvanceEmulator.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) < (this.offscreenRGBCount | 0); 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; - } -} -GameBoyAdvanceEmulator.prototype.prepareFrame = function () { - //Copy the internal frame buffer to the output buffer: - this.swizzleFrameBuffer(); - this.requestDraw(); -} -GameBoyAdvanceEmulator.prototype.requestDraw = function () { - if (this.graphicsFrameCallback) { - //We actually updated the graphics internally, so copy out: - this.graphicsFrameCallback(this.swizzledFrame); - } -} GameBoyAdvanceEmulator.prototype.enableAudio = function () { - if (!this.audioFound && this.audio) { - //Calculate the variables for the preliminary downsampler first: - this.audioResamplerFirstPassFactor = Math.max(Math.min(Math.floor(this.clocksPerSecond / 44100), Math.floor(0x7FFFFFFF / 0x3FF)), 1); - this.audioDownSampleInputDivider = (2 / 0x3FF) / this.audioResamplerFirstPassFactor; - this.audioSetState(true); //Set audio to 'found' by default. + 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 / this.audioResamplerFirstPassFactor, Math.max((this.CPUCyclesPerIteration | 0) * this.settings.audioBufferSize / this.audioResamplerFirstPassFactor, 8192) << 1, this.settings.audioVolume, function () { - //Disable audio in the callback here: - parentObj.disableAudio(); + 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(); - if (this.audioFound) { - //Only run this if audio was found to save memory on disabled output: - this.initializeAudioBuffering(); - } } } GameBoyAdvanceEmulator.prototype.disableAudio = function () { - if (this.audioFound) { + if ((this.audioFound | 0) != 0) { this.audio.unregister(); - this.audioSetState(false); - this.calculateTimings(); //Re-Fix timing if it was adjusted by our audio code. + 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.CPUCyclesPerIteration | 0) * (this.settings.audioBufferUnderrunLimit | 0) / this.audioResamplerFirstPassFactor, 4096) << 1; - this.audioBufferDynamicContainAmount = Math.max((this.CPUCyclesPerIteration | 0) * (this.settings.audioBufferDynamicLimit | 0) / this.audioResamplerFirstPassFactor, 2048) << 1; - var audioNumSamplesTotal = Math.max((this.CPUCyclesPerIteration | 0) / this.audioResamplerFirstPassFactor, 1) << 1; - if ((audioNumSamplesTotal | 0) != (this.audioNumSamplesTotal | 0)) { - this.audioNumSamplesTotal = audioNumSamplesTotal | 0; - this.audioBuffer = getFloat32Array(this.audioNumSamplesTotal | 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.changeVolume = function (newVolume) { - this.settings.audioVolume = Math.min(Math.max(parseFloat(newVolume), 0), 1); - if (this.audioFound) { - this.audio.changeVolume(this.settings.audioVolume); - } -} -GameBoyAdvanceEmulator.prototype.incrementVolume = function (delta) { - this.changeVolume(parseFloat(delta) + this.settings.audioVolume); -} GameBoyAdvanceEmulator.prototype.outputAudio = function (downsampleInputLeft, downsampleInputRight) { downsampleInputLeft = downsampleInputLeft | 0; downsampleInputRight = downsampleInputRight | 0; - this.audioBuffer[this.audioDestinationPosition | 0] = (downsampleInputLeft * this.audioDownSampleInputDivider) - 1; + this.audioBuffer[this.audioDestinationPosition | 0] = ((downsampleInputLeft | 0) * (+this.audioDownSampleInputDivider)) - 1; this.audioDestinationPosition = ((this.audioDestinationPosition | 0) + 1) | 0; - this.audioBuffer[this.audioDestinationPosition | 0] = (downsampleInputRight * this.audioDownSampleInputDivider) - 1; + this.audioBuffer[this.audioDestinationPosition | 0] = ((downsampleInputRight | 0) * (+this.audioDownSampleInputDivider)) - 1; this.audioDestinationPosition = ((this.audioDestinationPosition | 0) + 1) | 0; - if ((this.audioDestinationPosition | 0) == (this.audioNumSamplesTotal | 0)) { - this.audio.push(this.audioBuffer); - this.audioDestinationPosition = 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) { + 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.settings.dynamicSpeed) { - if ((this.dynamicSpeedCounter | 0) > (this.settings.metricCollectionMinimum | 0)) { - if (((this.audioBufferDynamicContainAmount | 0) - (remainingAmount | 0)) > 0) { - var speed = this.getSpeed(); - speed = Math.max(speed - 0.1, 0.1); - this.setSpeed(speed); - } - else { - this.dynamicSpeedCounter = this.settings.metricCollectionMinimum | 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.dynamicSpeedCounter = ((this.dynamicSpeedCounter | 0) + 1) | 0; } - this.CPUCyclesTotal = Math.min(((this.CPUCyclesTotal | 0) + ((underrunAmount >> 1) * this.audioResamplerFirstPassFactor)) | 0, this.CPUCyclesTotal << 1, 0x7FFFFFFF) | 0; + this.CPUCyclesTotal = Math.min(((this.CPUCyclesTotal | 0) + ((underrunAmount >> 1) * (this.audioResamplerFirstPassFactor | 0))) | 0, (+this.clocksPerMilliSecond) << 5) | 0; } - else if (this.settings.dynamicSpeed) { - if ((this.dynamicSpeedCounter | 0) > (this.settings.metricCollectionMinimum | 0)) { - var speed = this.getSpeed(); - if (speed < 1) { - speed = Math.min(speed + 0.05, 1); - this.setSpeed(speed); - } - else { - this.dynamicSpeedCounter = this.settings.metricCollectionMinimum | 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); } } - this.dynamicSpeedCounter = ((this.dynamicSpeedCounter | 0) + 1) | 0; + 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) { - this.IOCore.sound.initializeOutput(this.audioFound, this.audioResamplerFirstPassFactor); - this.audioUpdateState = false; + if ((this.audioUpdateState | 0) != 0) { + this.IOCore.sound.initializeOutput(this.audioResamplerFirstPassFactor | 0); + this.audioUpdateState = 0; } } -GameBoyAdvanceEmulator.prototype.audioSetState = function (audioFound) { - if (this.audioFound != audioFound) { - this.audioFound = audioFound; - this.audioUpdateState = true; +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.reinitializeAudio = function () { - if (this.audioFound) { //Set up the audio again if enabled. - this.disableAudio(); - this.enableAudio(); - } +GameBoyAdvanceEmulator.prototype.toggleSkipBootROM = function (SKIPBoot) { + this.settings.SKIPBoot = !!SKIPBoot; } -GameBoyAdvanceEmulator.prototype.enableSkipBootROM = function () { - this.settings.SKIPBoot = true; +GameBoyAdvanceEmulator.prototype.toggleDynamicSpeed = function (dynamicSpeed) { + this.settings.dynamicSpeed = !!dynamicSpeed; + this.processNewSpeed(1); } -GameBoyAdvanceEmulator.prototype.disableSkipBootROM = function () { - this.settings.SKIPBoot = false; +GameBoyAdvanceEmulator.prototype.toggleOffthreadGraphics = function (offthreadGfxEnabled) { + this.settings.offthreadGfxEnabled = !!offthreadGfxEnabled; } -GameBoyAdvanceEmulator.prototype.enableDynamicSpeed = function () { - this.settings.dynamicSpeed = true; -} -GameBoyAdvanceEmulator.prototype.disableDynamicSpeed = function () { - this.settings.dynamicSpeed = false; - this.setSpeed(1); -} \ No newline at end of file diff --git a/IodineGBA/core/Graphics.js b/IodineGBA/core/Graphics.js index d8218ef..98715ae 100644 --- a/IodineGBA/core/Graphics.js +++ b/IodineGBA/core/Graphics.js @@ -28,7 +28,7 @@ GameBoyAdvanceGraphics.prototype.initializeState = function () { this.VCounter = 0; this.currentScanLine = 0; this.LCDTicks = 0; - if (!this.IOCore.BIOSFound || this.IOCore.settings.SKIPBoot) { + if (this.IOCore.SKIPBoot) { //BIOS entered the ROM at line 0x7C: this.currentScanLine = 0x7C; } diff --git a/IodineGBA/core/Memory.js b/IodineGBA/core/Memory.js index ec325bc..1eea60d 100644 --- a/IodineGBA/core/Memory.js +++ b/IodineGBA/core/Memory.js @@ -1,11 +1,11 @@ "use strict"; /* - Copyright (C) 2012-2015 Grant Galitz - + 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) { @@ -13,13 +13,22 @@ function GameBoyAdvanceMemory(IOCore) { this.IOCore = IOCore; } GameBoyAdvanceMemory.prototype.initialize = function () { - //WRAM Map Control Stuff: - this.WRAMControlFlags = 0x20; + var allowInit = 1; //Load the BIOS: this.BIOS = getUint8Array(0x4000); this.BIOS16 = getUint16View(this.BIOS); this.BIOS32 = getInt32View(this.BIOS); - this.loadBIOS(); + 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); @@ -28,23 +37,6 @@ GameBoyAdvanceMemory.prototype.initialize = function () { this.internalRAM16 = getUint16View(this.internalRAM); this.internalRAM32 = getInt32View(this.internalRAM); this.lastBIOSREAD = 0; //BIOS read bus last. - //After all sub-objects initialized, initialize dispatches: - 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]; //Initialize the various handler objects: this.dma = this.IOCore.dma; this.dmaChannel0 = this.IOCore.dmaChannel0; @@ -495,146 +487,123 @@ GameBoyAdvanceMemory.prototype.writeIODispatch8 = function (address, data) { //4000064h - SOUND1CNT_X (NR13, NR14) - Channel 1 Frequency/Control (R/W) case 0x4000064: //NR13: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND1CNT_X0(data & 0xFF); + this.sound.writeSOUND1CNTX8_0(data | 0); break; //4000065h - SOUND1CNT_X (NR13, NR14) - Channel 1 Frequency/Control (R/W) case 0x4000065: //NR14: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND1CNT_X1(data & 0xFF); + 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.IOCore.updateTimerClocking(); - this.sound.writeSOUND2CNT_L0(data & 0xFF); + this.sound.writeSOUND2CNTL8_0(data | 0); break; //4000069h - SOUND2CNT_L (NR21, NR22) - Channel 2 Duty/Length/Envelope (R/W) case 0x4000069: //NR22: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND2CNT_L1(data & 0xFF); + 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.IOCore.updateTimerClocking(); - this.sound.writeSOUND2CNT_H0(data & 0xFF); + this.sound.writeSOUND2CNTH8_0(data | 0); break; //400006Dh - SOUND2CNT_H (NR23, NR24) - Channel 2 Frequency/Control (R/W) case 0x400006D: //NR24: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND2CNT_H1(data & 0xFF); + 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.IOCore.updateTimerClocking(); - this.sound.writeSOUND3CNT_L(data & 0xFF); + 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.IOCore.updateTimerClocking(); - this.sound.writeSOUND3CNT_H0(data & 0xFF); + this.sound.writeSOUND3CNT8_2(data | 0); break; //4000073h - SOUND3CNT_H (NR31, NR32) - Channel 3 Length/Volume (R/W) case 0x4000073: //NR32: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND3CNT_H1(data & 0xFF); + this.sound.writeSOUND3CNT8_3(data | 0); break; //4000074h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W) case 0x4000074: //NR33: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND3CNT_X0(data & 0xFF); + this.sound.writeSOUND3CNTX8_0(data | 0); break; //4000075h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W) case 0x4000075: //NR34: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND3CNT_X1(data & 0xFF); + 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.IOCore.updateTimerClocking(); - this.sound.writeSOUND4CNT_L0(data & 0xFF); + this.sound.writeSOUND4CNTL8_0(data | 0); break; //4000079h - SOUND4CNT_L (NR41, NR42) - Channel 4 Length/Envelope (R/W) case 0x4000079: //NR42: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND4CNT_L1(data & 0xFF); + 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.IOCore.updateTimerClocking(); - this.sound.writeSOUND4CNT_H0(data & 0xFF); + this.sound.writeSOUND4CNTH8_0(data | 0); break; //400007Dh - SOUND4CNT_H (NR43, NR44) - Channel 4 Frequency/Control (R/W) case 0x400007D: //NR44: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND4CNT_H1(data & 0xFF); + 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.IOCore.updateTimerClocking(); - this.sound.writeSOUNDCNT_L0(data & 0xFF); + this.sound.writeSOUNDCNTL8_0(data | 0); break; //4000081h - SOUNDCNT_L (NR50, NR51) - Channel L/R Volume/Enable (R/W) case 0x4000081: //NR51: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUNDCNT_L1(data & 0xFF); + this.sound.writeSOUNDCNTL8_1(data | 0); break; //4000082h - SOUNDCNT_H (GBA only) - DMA Sound Control/Mixing (R/W) case 0x4000082: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUNDCNT_H0(data & 0xFF); + this.sound.writeSOUNDCNTH8_0(data | 0); break; //4000083h - SOUNDCNT_H (GBA only) - DMA Sound Control/Mixing (R/W) case 0x4000083: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUNDCNT_H1(data & 0xFF); - this.IOCore.updateCoreEventTime(); + this.sound.writeSOUNDCNTH8_1(data | 0); break; //4000084h - SOUNDCNT_X (NR52) - Sound on/off (R/W) case 0x4000084: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUNDCNT_X(data & 0xFF); + 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.IOCore.updateTimerClocking(); - this.sound.writeSOUNDBIAS0(data & 0xFF); + this.sound.writeSOUNDBIAS8_0(data | 0); break; //4000089h - SOUNDBIAS - Sound PWM Control (R/W) case 0x4000089: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUNDBIAS1(data & 0xFF); + 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) @@ -1115,11 +1084,11 @@ GameBoyAdvanceMemory.prototype.writeIODispatch8 = function (address, data) { break; //4000204h - WAITCNT - Waitstate Control (R/W) case 0x4000204: - this.wait.writeWAITCNT0(data & 0xFF); + this.wait.writeWAITCNT8_0(data | 0); break; //4000205h - WAITCNT - Waitstate Control (R/W) case 0x4000205: - this.wait.writeWAITCNT1(data & 0xFF); + this.wait.writeWAITCNT8_1(data | 0); break; //4000206h - WAITCNT - Waitstate Control (R/W) //4000207h - WAITCNT - Waitstate Control (R/W) @@ -1130,16 +1099,16 @@ GameBoyAdvanceMemory.prototype.writeIODispatch8 = function (address, data) { //4000209h through 40002FFh - NOT USED - GLITCHED //4000300h - POSTFLG - BYTE - Undocumented - Post Boot / Debug Control (R/W) case 0x4000300: - this.wait.writePOSTBOOT(data & 0xFF); + this.wait.writePOSTBOOT(data | 0); break; //4000301h - HALTCNT - BYTE - Undocumented - Low Power Mode Control (W) case 0x4000301: - this.wait.writeHALTCNT(data & 0xFF); + this.wait.writeHALTCNT(data | 0); break; default: if ((address & 0xFFFC) == 0x800) { //WRAM wait state control: - this.wait.writeConfigureWRAM8(address | 0, data & 0xFF); + this.wait.writeConfigureWRAM8(address | 0, data | 0); } } } @@ -1327,98 +1296,74 @@ GameBoyAdvanceMemory.prototype.writeIODispatch16 = function (address, data) { //4000064h - SOUND1CNT_X (NR13, NR14) - Channel 1 Frequency/Control (R/W) case 0x4000064: //NR13: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND1CNT_X0(data & 0xFF); //NR14: - this.sound.writeSOUND1CNT_X1((data >> 8) & 0xFF); + 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: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND2CNT_L0(data & 0xFF); //NR22: - this.sound.writeSOUND2CNT_L1((data >> 8) & 0xFF); + this.sound.writeSOUND2CNTL16(data | 0); break; //400006Ah - NOT USED - ZERO //400006Ch - SOUND2CNT_H (NR23, NR24) - Channel 2 Frequency/Control (R/W) case 0x400006C: //NR23: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND2CNT_H0(data & 0xFF); //NR24: - this.sound.writeSOUND2CNT_H1((data >> 8) & 0xFF); + 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.IOCore.updateTimerClocking(); - this.sound.writeSOUND3CNT_L(data & 0xFF); + this.sound.writeSOUND3CNT8_0(data | 0); break; //4000072h - SOUND3CNT_H (NR31, NR32) - Channel 3 Length/Volume (R/W) case 0x4000072: //NR31: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND3CNT_H0(data & 0xFF); //NR32: - this.sound.writeSOUND3CNT_H1((data >> 8) & 0xFF); + this.sound.writeSOUND3CNT16(data | 0); break; //4000074h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W) case 0x4000074: //NR33: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND3CNT_X0(data & 0xFF); //NR34: - this.sound.writeSOUND3CNT_X1((data >> 8) & 0xFF); + this.sound.writeSOUND3CNTX16(data | 0); break; //4000076h - NOT USED - ZERO //4000078h - SOUND4CNT_L (NR41, NR42) - Channel 4 Length/Envelope (R/W) case 0x4000078: //NR41: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND4CNT_L0(data & 0xFF); //NR42: - this.sound.writeSOUND4CNT_L1((data >> 8) & 0xFF); + this.sound.writeSOUND4CNTL16(data | 0); break; //400007Ah - NOT USED - ZERO //400007Ch - SOUND4CNT_H (NR43, NR44) - Channel 4 Frequency/Control (R/W) case 0x400007C: //NR43: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND4CNT_H0(data & 0xFF); //NR44: - this.sound.writeSOUND4CNT_H1((data >> 8) & 0xFF); + 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: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUNDCNT_L0(data & 0xFF); //NR51: - this.sound.writeSOUNDCNT_L1((data >> 8) & 0xFF); + this.sound.writeSOUNDCNTL16(data | 0); break; //4000082h - SOUNDCNT_H (GBA only) - DMA Sound Control/Mixing (R/W) case 0x4000082: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUNDCNT_H0(data & 0xFF); - this.sound.writeSOUNDCNT_H1((data >> 8) & 0xFF); - this.IOCore.updateCoreEventTime(); + this.sound.writeSOUNDCNTH16(data | 0); break; //4000084h - SOUNDCNT_X (NR52) - Sound on/off (R/W) case 0x4000084: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUNDCNT_X(data & 0xFF); - this.IOCore.updateCoreEventTime(); + this.sound.writeSOUNDCNTX8(data | 0); break; //4000086h - NOT USED - ZERO //4000088h - SOUNDBIAS - Sound PWM Control (R/W) case 0x4000088: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUNDBIAS0(data & 0xFF); - this.sound.writeSOUNDBIAS1((data >> 8) & 0xFF); + this.sound.writeSOUNDBIAS16(data | 0); break; //400008Ah through 400008Fh - NOT USED - ZERO/GLITCHED //4000090h - WAVE_RAM0_L - Channel 3 Wave Pattern RAM (W/R) @@ -1437,7 +1382,7 @@ GameBoyAdvanceMemory.prototype.writeIODispatch16 = function (address, data) { case 0x400009C: //400009Eh - WAVE_RAM3_H - Channel 3 Wave Pattern RAM (W/R) case 0x400009E: - this.sound.writeWAVE16(address & 0xF, data | 0); + this.sound.writeWAVE16(address & 0xE, data | 0); break; //40000A0h - FIFO_A_L - FIFO Channel A First Word (W) case 0x40000A0: @@ -1690,8 +1635,7 @@ GameBoyAdvanceMemory.prototype.writeIODispatch16 = function (address, data) { break; //4000204h - WAITCNT - Waitstate Control (R/W) case 0x4000204: - this.wait.writeWAITCNT0(data & 0xFF); - this.wait.writeWAITCNT1((data >> 8) & 0xFF); + this.wait.writeWAITCNT16(data | 0); break; //4000206h - WAITCNT - Waitstate Control (R/W) //4000208h - IME - Interrupt Master Enable Register (R/W) @@ -1701,13 +1645,12 @@ GameBoyAdvanceMemory.prototype.writeIODispatch16 = function (address, data) { //4000209h through 40002FFh - NOT USED - GLITCHED //4000300h - POSTFLG - BYTE - Undocumented - Post Boot / Debug Control (R/W) case 0x4000300: - this.wait.writePOSTBOOT(data & 0xFF); - this.wait.writeHALTCNT((data >> 8) & 0xFF); + this.wait.writeHALT16(data | 0); break; default: if ((address & 0xFFFC) == 0x800) { //WRAM wait state control: - this.wait.writeConfigureWRAM16(address | 0, data & 0xFFFF); + this.wait.writeConfigureWRAM16(address | 0, data | 0); } } } @@ -1836,90 +1779,67 @@ GameBoyAdvanceMemory.prototype.writeIODispatch32 = function (address, data) { //4000066h - NOT USED - ZERO case 0x4000064: //NR13: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND1CNT_X0(data & 0xFF); //NR14: - this.sound.writeSOUND1CNT_X1((data >> 8) & 0xFF); + 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: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND2CNT_L0(data & 0xFF); //NR22: - this.sound.writeSOUND2CNT_L1((data >> 8) & 0xFF); + this.sound.writeSOUND2CNTL16(data | 0); break; //400006Ch - SOUND2CNT_H (NR23, NR24) - Channel 2 Frequency/Control (R/W) //400006Eh - NOT USED - ZERO case 0x400006C: //NR23: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND2CNT_H0(data & 0xFF); //NR24: - this.sound.writeSOUND2CNT_H1((data >> 8) & 0xFF); + 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: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND3CNT_L(data & 0xFF); //NR31: - this.sound.writeSOUND3CNT_H0((data >> 16) & 0xFF); //NR32: - this.sound.writeSOUND3CNT_H1(data >>> 24); + this.sound.writeSOUND3CNT32(data | 0); break; //4000074h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W) //4000076h - NOT USED - ZERO case 0x4000074: //NR33: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND3CNT_X0(data & 0xFF); //NR34: - this.sound.writeSOUND3CNT_X1((data >> 8) & 0xFF); + this.sound.writeSOUND3CNTX16(data | 0); break; //4000078h - SOUND4CNT_L (NR41, NR42) - Channel 4 Length/Envelope (R/W) //400007Ah - NOT USED - ZERO case 0x4000078: //NR41: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND4CNT_L0(data & 0xFF); //NR42: - this.sound.writeSOUND4CNT_L1((data >> 8) & 0xFF); + this.sound.writeSOUND4CNTL16(data | 0); break; //400007Ch - SOUND4CNT_H (NR43, NR44) - Channel 4 Frequency/Control (R/W) //400007Eh - NOT USED - ZERO case 0x400007C: //NR43: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUND4CNT_H0(data & 0xFF); //NR44: - this.sound.writeSOUND4CNT_H1((data >> 8) & 0xFF); + 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: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUNDCNT_L0(data & 0xFF); //NR51: - this.sound.writeSOUNDCNT_L1((data >> 8) & 0xFF); - this.sound.writeSOUNDCNT_H0((data >> 16) & 0xFF); - this.sound.writeSOUNDCNT_H1(data >>> 24); - this.IOCore.updateCoreEventTime(); + this.sound.writeSOUNDCNT32(data | 0); break; //4000084h - SOUNDCNT_X (NR52) - Sound on/off (R/W) //4000086h - NOT USED - ZERO case 0x4000084: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUNDCNT_X(data & 0xFF); + this.sound.writeSOUNDCNTX8(data | 0); break; //4000088h - SOUNDBIAS - Sound PWM Control (R/W) case 0x4000088: - this.IOCore.updateTimerClocking(); - this.sound.writeSOUNDBIAS0(data & 0xFF); - this.sound.writeSOUNDBIAS1((data >> 8) & 0xFF); + this.sound.writeSOUNDBIAS16(data | 0); break; //400008Ah through 400008Fh - NOT USED - ZERO/GLITCHED //4000090h - WAVE_RAM0_L - Channel 3 Wave Pattern RAM (W/R) @@ -1934,7 +1854,7 @@ GameBoyAdvanceMemory.prototype.writeIODispatch32 = function (address, data) { //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 & 0xF, data | 0); + 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) @@ -2115,8 +2035,7 @@ GameBoyAdvanceMemory.prototype.writeIODispatch32 = function (address, data) { //4000204h - WAITCNT - Waitstate Control (R/W) //4000206h - WAITCNT - Waitstate Control (R/W) case 0x4000204: - this.wait.writeWAITCNT0(data & 0xFF); - this.wait.writeWAITCNT1((data >> 8) & 0xFF); + this.wait.writeWAITCNT16(data | 0); break; //4000208h - IME - Interrupt Master Enable Register (R/W) case 0x4000208: @@ -2126,8 +2045,7 @@ GameBoyAdvanceMemory.prototype.writeIODispatch32 = function (address, data) { //4000300h - POSTFLG - BYTE - Undocumented - Post Boot / Debug Control (R/W) //4000302h - NOT USED - ZERO case 0x4000300: - this.wait.writePOSTBOOT(data & 0xFF); - this.wait.writeHALTCNT((data >> 8) & 0xFF); + this.wait.writeHALT16(data | 0); break; default: if ((address & 0xFFFC) == 0x800) { @@ -2193,7 +2111,7 @@ GameBoyAdvanceMemory.prototype.writeOBJ16 = function (address, data) { data = data | 0; this.IOCore.updateGraphicsClocking(); this.wait.OAMAccess(); - this.gfxRenderer.writeOAM16(address & 0x3FE, data & 0xFFFF); + this.gfxRenderer.writeOAM16(address | 0, data | 0); } GameBoyAdvanceMemory.prototype.writePalette32 = function (address, data) { address = address | 0; @@ -2214,7 +2132,7 @@ GameBoyAdvanceMemory.prototype.writeOBJ32 = function (address, data) { data = data | 0; this.IOCore.updateGraphicsClocking(); this.wait.OAMAccess(); - this.gfxRenderer.writeOAM32(address & 0x3FC, data | 0); + this.gfxRenderer.writeOAM32(address | 0, data | 0); } GameBoyAdvanceMemory.prototype.writeROM8 = function (address, data) { address = address | 0; @@ -2771,83 +2689,82 @@ GameBoyAdvanceMemory.prototype.readIODispatch8 = function (address) { //4000065h - SOUND1CNT_X (NR13, NR14) - Channel 1 Frequency/Control (R/W) case 0x4000065: //NR14: - data = this.sound.readSOUND1CNT_X() | 0; + data = this.sound.readSOUND1CNTX8() | 0; break; //4000068h - SOUND2CNT_L (NR21, NR22) - Channel 2 Duty/Length/Envelope (R/W) case 0x4000068: //NR21: - data = this.sound.readSOUND2CNT_L0() | 0; + 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.readSOUND2CNT_L1() | 0; + data = this.sound.readSOUND2CNTL8_1() | 0; break; //400006Dh - SOUND2CNT_H (NR23, NR24) - Channel 2 Frequency/Control (R/W) case 0x400006D: //NR24: - data = this.sound.readSOUND2CNT_H() | 0; + data = this.sound.readSOUND2CNTH8() | 0; break; //4000070h - SOUND3CNT_L (NR30) - Channel 3 Stop/Wave RAM select (R/W) case 0x4000070: //NR30: - data = this.sound.readSOUND3CNT_L() | 0; + data = this.sound.readSOUND3CNT8_0() | 0; break; //4000073h - SOUND3CNT_H (NR31, NR32) - Channel 3 Length/Volume (R/W) case 0x4000073: //NR32: - data = this.sound.readSOUND3CNT_H() | 0; + data = this.sound.readSOUND3CNT8_3() | 0; break; //4000075h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W) case 0x4000075: //NR34: - data = this.sound.readSOUND3CNT_X() | 0; + data = this.sound.readSOUND3CNTX8() | 0; break; //4000079h - SOUND4CNT_L (NR41, NR42) - Channel 4 Length/Envelope (R/W) case 0x4000079: //NR42: - data = this.sound.readSOUND4CNT_L() | 0; + data = this.sound.readSOUND4CNTL8() | 0; break; //400007Ch - SOUND4CNT_H (NR43, NR44) - Channel 4 Frequency/Control (R/W) case 0x400007C: //NR43: - data = this.sound.readSOUND4CNT_H0() | 0; + data = this.sound.readSOUND4CNTH8_0() | 0; break; //400007Dh - SOUND4CNT_H (NR43, NR44) - Channel 4 Frequency/Control (R/W) case 0x400007D: //NR44: - data = this.sound.readSOUND4CNT_H1() | 0; + 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.readSOUNDCNT_L0() | 0; + 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.readSOUNDCNT_L1() | 0; + data = this.sound.readSOUNDCNTL8_1() | 0; break; //4000082h - SOUNDCNT_H (GBA only) - DMA Sound Control/Mixing (R/W) case 0x4000082: - data = this.sound.readSOUNDCNT_H0() | 0; + data = this.sound.readSOUNDCNTH8_0() | 0; break; //4000083h - SOUNDCNT_H (GBA only) - DMA Sound Control/Mixing (R/W) case 0x4000083: - data = this.sound.readSOUNDCNT_H1() | 0; + data = this.sound.readSOUNDCNTH8_1() | 0; break; //4000084h - SOUNDCNT_X (NR52) - Sound on/off (R/W) case 0x4000084: - this.IOCore.updateTimerClocking(); - data = this.sound.readSOUNDCNT_X() | 0; + data = this.sound.readSOUNDCNTX8() | 0; break; //4000088h - SOUNDBIAS - Sound PWM Control (R/W, see below) case 0x4000088: - data = this.sound.readSOUNDBIAS0() | 0; + data = this.sound.readSOUNDBIAS8_0() | 0; break; //4000089h - SOUNDBIAS - Sound PWM Control (R/W, see below) case 0x4000089: - data = this.sound.readSOUNDBIAS1() | 0; + data = this.sound.readSOUNDBIAS8_1() | 0; break; //400008Ch - NOT USED - GLITCHED //400008Dh - NOT USED - GLITCHED @@ -3131,11 +3048,11 @@ GameBoyAdvanceMemory.prototype.readIODispatch8 = function (address) { break; //4000204h - WAITCNT - Waitstate Control (R/W) case 0x4000204: - data = this.wait.readWAITCNT0() | 0; + data = this.wait.readWAITCNT8_0() | 0; break; //4000205h - WAITCNT - Waitstate Control (R/W) case 0x4000205: - data = this.wait.readWAITCNT1() | 0; + data = this.wait.readWAITCNT8_1() | 0; break; //4000208h - IME - Interrupt Master Enable Register (R/W) case 0x4000208: @@ -3349,63 +3266,62 @@ GameBoyAdvanceMemory.prototype.readIO16 = function (address) { //4000064h - SOUND1CNT_X (NR13, NR14) - Channel 1 Frequency/Control (R/W) case 0x4000064: //NR14: - data = this.sound.readSOUND1CNT_X() << 8; + 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.readSOUND2CNT_L0() | (this.sound.readSOUND2CNT_L1() << 8); + data = this.sound.readSOUND2CNTL16() | 0; break; //400006Ch - SOUND2CNT_H (NR23, NR24) - Channel 2 Frequency/Control (R/W) case 0x400006C: //NR24: - data = this.sound.readSOUND2CNT_H() << 8; + data = this.sound.readSOUND2CNTH16() | 0; break; //4000070h - SOUND3CNT_L (NR30) - Channel 3 Stop/Wave RAM select (R/W) case 0x4000070: //NR30: - data = this.sound.readSOUND3CNT_L() | 0; + data = this.sound.readSOUND3CNT8_0() | 0; break; //4000073h - SOUND3CNT_H (NR31, NR32) - Channel 3 Length/Volume (R/W) case 0x4000072: //NR32: - data = this.sound.readSOUND3CNT_H() << 8; + data = this.sound.readSOUND3CNT16_1() | 0; break; //4000074h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W) case 0x4000074: //NR34: - data = this.sound.readSOUND3CNT_X() << 8; + data = this.sound.readSOUND3CNTX16() | 0; break; //4000078h - SOUND4CNT_L (NR41, NR42) - Channel 4 Length/Envelope (R/W) case 0x4000078: //NR42: - data = this.sound.readSOUND4CNT_L() << 8; + data = this.sound.readSOUND4CNTL16() | 0; break; //400007Ch - SOUND4CNT_H (NR43, NR44) - Channel 4 Frequency/Control (R/W) case 0x400007C: //NR43: //NR44: - data = this.sound.readSOUND4CNT_H0() | (this.sound.readSOUND4CNT_H1() << 8); + 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.readSOUNDCNT_L0() | (this.sound.readSOUNDCNT_L1() << 8); + data = this.sound.readSOUNDCNTL16() | 0; break; //4000082h - SOUNDCNT_H (GBA only) - DMA Sound Control/Mixing (R/W) case 0x4000082: - data = this.sound.readSOUNDCNT_H0() | (this.sound.readSOUNDCNT_H1() << 8); + data = this.sound.readSOUNDCNTH16() | 0; break; //4000084h - SOUNDCNT_X (NR52) - Sound on/off (R/W) case 0x4000084: - this.IOCore.updateTimerClocking(); - data = this.sound.readSOUNDCNT_X() | 0; + data = this.sound.readSOUNDCNTX8() | 0; break; //4000088h - SOUNDBIAS - Sound PWM Control (R/W, see below) case 0x4000088: - data = this.sound.readSOUNDBIAS0() | (this.sound.readSOUNDBIAS1() << 8); + data = this.sound.readSOUNDBIAS16() | 0; break; //400008Ch - NOT USED - GLITCHED //400008Eh - NOT USED - GLITCHED @@ -3568,7 +3484,7 @@ GameBoyAdvanceMemory.prototype.readIO16 = function (address) { break; //4000204h - WAITCNT - Waitstate Control (R/W) case 0x4000204: - data = this.wait.readWAITCNT0() | (this.wait.readWAITCNT1() << 8); + data = this.wait.readWAITCNT16() | 0; break; //4000208h - IME - Interrupt Master Enable Register (R/W) case 0x4000208: @@ -3688,75 +3604,69 @@ GameBoyAdvanceMemory.prototype.readIO32 = function (address) { //NR10: //NR11: //NR12: - data = this.sound.readSOUND1CNT8_0() | - (this.sound.readSOUND1CNT8_2() << 16) | - (this.sound.readSOUND1CNT8_3() << 24); + 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.readSOUND1CNT_X() << 8; + 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.readSOUND2CNT_L0() | (this.sound.readSOUND2CNT_L1() << 8); + 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.readSOUND2CNT_H() << 8; + 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.readSOUND3CNT_L() | (this.sound.readSOUND3CNT_H() << 24); + 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.readSOUND3CNT_X() << 8; + 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.readSOUND4CNT_L() << 8; + 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.readSOUND4CNT_H0() | (this.sound.readSOUND4CNT_H1() << 8); + 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.readSOUNDCNT_L0() | - (this.sound.readSOUNDCNT_L1() << 8) | - (this.sound.readSOUNDCNT_H0() << 16) | - (this.sound.readSOUNDCNT_H1() << 24); + data = this.sound.readSOUNDCNT32() | 0; break; //4000084h - SOUNDCNT_X (NR52) - Sound on/off (R/W) //4000086h - NOT USED - ZERO case 0x4000084: - this.IOCore.updateTimerClocking(); - data = this.sound.readSOUNDCNT_X() | 0; + data = this.sound.readSOUNDCNTX8() | 0; break; //4000088h - SOUNDBIAS - Sound PWM Control (R/W, see below) //400008Ah - NOT USED - ZERO case 0x4000088: - data = this.sound.readSOUNDBIAS0() | (this.sound.readSOUNDBIAS1() << 8); + data = this.sound.readSOUNDBIAS16() | 0; break; //400008Ch - NOT USED - GLITCHED //400008Eh - NOT USED - GLITCHED @@ -3896,7 +3806,7 @@ GameBoyAdvanceMemory.prototype.readIO32 = function (address) { //4000204h - WAITCNT - Waitstate Control (R/W) //4000206h - NOT USED - ZERO case 0x4000204: - data = this.wait.readWAITCNT0() | (this.wait.readWAITCNT1() << 8); + data = this.wait.readWAITCNT16() | 0; break; //4000208h - IME - Interrupt Master Enable Register (R/W) //400020Ah - NOT USED - ZERO @@ -4170,17 +4080,21 @@ 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; + //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; - throw(new Error("BIOS invalid.")); + //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: @@ -5288,5 +5202,22 @@ function generateMemoryTopLevelDispatch() { "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(); \ No newline at end of file +generateMemoryTopLevelDispatch(); diff --git a/IodineGBA/core/RunLoop.js b/IodineGBA/core/RunLoop.js index ec01336..c40c750 100644 --- a/IodineGBA/core/RunLoop.js +++ b/IodineGBA/core/RunLoop.js @@ -1,14 +1,14 @@ "use strict"; /* - Copyright (C) 2012-2015 Grant Galitz - + 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(settings, coreExposed, BIOS, ROM) { +function GameBoyAdvanceIO(SKIPBoot, coreExposed, BIOS, ROM) { //State Machine Tracking: this.systemStatus = 0; this.cyclesToIterate = 0; @@ -18,9 +18,10 @@ function GameBoyAdvanceIO(settings, coreExposed, BIOS, ROM) { this.timerClocks = 0; this.serialClocks = 0; this.nextEventClocks = 0; - this.BIOSFound = false; + //this.BIOSFound = false; + //Do we skip the BIOS Boot Intro? + this.SKIPBoot = !!SKIPBoot; //References passed to us: - this.settings = settings; this.coreExposed = coreExposed; this.BIOS = BIOS; this.ROM = ROM; @@ -42,24 +43,33 @@ function GameBoyAdvanceIO(settings, coreExposed, BIOS, ROM) { 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: - this.memory.initialize(); - 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(); + 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: @@ -415,4 +425,4 @@ GameBoyAdvanceIO.prototype.getCurrentFetchValue = function () { fetch = this.dma.getCurrentFetchValue() | 0; } return fetch | 0; -} \ No newline at end of file +} diff --git a/IodineGBA/core/Saves.js b/IodineGBA/core/Saves.js index b290bc1..a19a97e 100644 --- a/IodineGBA/core/Saves.js +++ b/IodineGBA/core/Saves.js @@ -1,11 +1,11 @@ "use strict"; /* - Copyright (C) 2012-2015 Grant Galitz - + 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) { @@ -13,8 +13,7 @@ function GameBoyAdvanceSaves(IOCore) { } GameBoyAdvanceSaves.prototype.initialize = function () { this.saveType = 0; - this.gpioType = 0; - this.GPIOChip = null; + 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); @@ -41,12 +40,20 @@ GameBoyAdvanceSaves.prototype.referenceSave = function (saveType) { 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; } @@ -56,7 +63,7 @@ GameBoyAdvanceSaves.prototype.exportSaveType = function () { GameBoyAdvanceSaves.prototype.readGPIO8 = function (address) { address = address | 0; var data = 0; - if ((this.gpioType | 0) > 0) { + if ((this.GPIOChip.getType() | 0) > 0) { //GPIO: data = this.GPIOChip.read8(address | 0) | 0; } @@ -82,7 +89,7 @@ GameBoyAdvanceSaves.prototype.readEEPROM8 = function (address) { GameBoyAdvanceSaves.prototype.readGPIO16 = function (address) { address = address | 0; var data = 0; - if ((this.gpioType | 0) > 0) { + if ((this.GPIOChip.getType() | 0) > 0) { //GPIO: data = this.GPIOChip.read16(address | 0) | 0; } @@ -108,7 +115,7 @@ GameBoyAdvanceSaves.prototype.readEEPROM16 = function (address) { GameBoyAdvanceSaves.prototype.readGPIO32 = function (address) { address = address | 0; var data = 0; - if ((this.gpioType | 0) > 0) { + if ((this.GPIOChip.getType() | 0) > 0) { //GPIO: data = this.GPIOChip.read32(address | 0) | 0; } @@ -152,7 +159,7 @@ GameBoyAdvanceSaves.prototype.readSRAM = function (address) { GameBoyAdvanceSaves.prototype.writeGPIO8 = function (address, data) { address = address | 0; data = data | 0; - if ((this.gpioType | 0) > 0) { + if ((this.GPIOChip.getType() | 0) > 0) { //GPIO: this.GPIOChip.write8(address | 0, data | 0); } @@ -164,7 +171,7 @@ GameBoyAdvanceSaves.prototype.writeGPIO8 = function (address, data) { GameBoyAdvanceSaves.prototype.writeGPIO16 = function (address, data) { address = address | 0; data = data | 0; - if ((this.gpioType | 0) > 0) { + if ((this.GPIOChip.getType() | 0) > 0) { //GPIO: this.GPIOChip.write16(address | 0, data | 0); } @@ -188,7 +195,7 @@ GameBoyAdvanceSaves.prototype.writeEEPROM16 = function (address, data) { GameBoyAdvanceSaves.prototype.writeGPIO32 = function (address, data) { address = address | 0; data = data | 0; - if ((this.gpioType | 0) > 0) { + if ((this.GPIOChip.getType() | 0) > 0) { //GPIO: this.GPIOChip.write32(address | 0, data | 0); } @@ -229,4 +236,4 @@ GameBoyAdvanceSaves.prototype.writeSRAMIfDefined = function (address, data) { //FLASH: this.FLASHChip.write(address | 0, data | 0); } -} \ No newline at end of file +} diff --git a/IodineGBA/core/Sound.js b/IodineGBA/core/Sound.js index 46a63fc..bd98dbc 100644 --- a/IodineGBA/core/Sound.js +++ b/IodineGBA/core/Sound.js @@ -16,14 +16,19 @@ GameBoyAdvanceSound.prototype.initialize = function () { this.coreExposed = this.IOCore.coreExposed; this.dmaChannel1 = this.IOCore.dmaChannel1; this.dmaChannel2 = this.IOCore.dmaChannel2; - //Did the emulator core initialize us for output yet? - this.preprocessInitialization(false); //Initialize start: this.audioTicks = 0; + this.initializeSampling(380); this.initializeAudioStartState(); } -GameBoyAdvanceSound.prototype.initializeOutput = function (enabled, audioResamplerFirstPassFactor) { - this.preprocessInitialization(enabled); +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; @@ -33,10 +38,10 @@ GameBoyAdvanceSound.prototype.initializeAudioStartState = function () { //NOTE: NR 60-63 never get reset in audio halting: this.nr60 = 0; this.nr61 = 0; - this.nr62 = (this.IOCore.BIOSFound && !this.IOCore.settings.SKIPBoot) ? 0 : 0xFF; - this.nr63 = (this.IOCore.BIOSFound && !this.IOCore.settings.SKIPBoot) ? 0 : 0x2; - this.soundMasterEnabled = (!this.IOCore.BIOSFound || this.IOCore.settings.SKIPBoot); - this.mixerSoundBIAS = (this.IOCore.BIOSFound && !this.IOCore.settings.SKIPBoot) ? 0 : 0x200; + 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); @@ -76,14 +81,6 @@ GameBoyAdvanceSound.prototype.audioDisabled = function () { this.VinRightChannelMasterVolume = 1; //Clear NR51: this.nr51 = 0; - this.rightChannel1 = false; - this.rightChannel2 = false; - this.rightChannel3 = false; - this.rightChannel4 = false; - this.leftChannel1 = false; - this.leftChannel2 = false; - this.leftChannel3 = false; - this.leftChannel4 = false; //Clear NR52: this.nr52 = 0; this.soundMasterEnabled = false; @@ -114,7 +111,7 @@ GameBoyAdvanceSound.prototype.addClocks = function (clocks) { } if (typeof Math.imul == "function") { //Math.imul found, insert the optimized path in: - GameBoyAdvanceSound.prototype.generateAudioReal = function (numSamples) { + GameBoyAdvanceSound.prototype.generateAudio = function (numSamples) { numSamples = numSamples | 0; var multiplier = 0; if (this.soundMasterEnabled && !this.IOCore.isStopped()) { @@ -160,7 +157,7 @@ if (typeof Math.imul == "function") { } else { //Math.imul not found, use the compatibility method: - GameBoyAdvanceSound.prototype.generateAudioReal = function (numSamples) { + GameBoyAdvanceSound.prototype.generateAudio = function (numSamples) { var multiplier = 0; if (this.soundMasterEnabled && !this.IOCore.isStopped()) { for (var clockUpTo = 0; numSamples > 0;) { @@ -203,37 +200,17 @@ else { } } } -//Generate audio, but don't actually output it (Used for when sound is disabled by user/browser): -GameBoyAdvanceSound.prototype.generateAudioFake = function (numSamples) { - numSamples = numSamples | 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; - if ((this.PWMWidth | 0) == 0) { - this.computeNextPWMInterval(); - this.PWMWidthOld = this.PWMWidthShadow | 0; - this.PWMWidth = this.PWMWidthShadow | 0; - } - } - } -} -GameBoyAdvanceSound.prototype.preprocessInitialization = function (audioInitialized) { - if (audioInitialized) { - this.generateAudio = this.generateAudioReal; - this.audioInitialized = true; - } - else { - this.generateAudio = this.generateAudioFake; - this.audioInitialized = false; - } -} 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) { @@ -318,50 +295,50 @@ GameBoyAdvanceSound.prototype.computeAudioChannels = function () { if (typeof Math.imul == "function") { //Math.imul found, insert the optimized path in: GameBoyAdvanceSound.prototype.CGBMixerOutputLevelCache = function () { - this.CGBMixerOutputCacheLeft = Math.imul(((this.channel1.currentSampleLeftTrimary | 0) + (this.channel2.currentSampleLeftTrimary | 0) + (this.channel3.currentSampleLeftSecondary | 0) + (this.channel4.currentSampleLeftSecondary | 0)) | 0, this.VinLeftChannelMasterVolume | 0) | 0; - this.CGBMixerOutputCacheRight = Math.imul(((this.channel1.currentSampleRightTrimary | 0) + (this.channel2.currentSampleRightTrimary | 0) + (this.channel3.currentSampleRightSecondary | 0) + (this.channel4.currentSampleRightSecondary | 0)) | 0, this.VinRightChannelMasterVolume | 0) | 0; + 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.currentSampleLeftTrimary + this.channel2.currentSampleLeftTrimary + this.channel3.currentSampleLeftSecondary + this.channel4.currentSampleLeftSecondary) * this.VinLeftChannelMasterVolume; - this.CGBMixerOutputCacheRight = (this.channel1.currentSampleRightTrimary + this.channel2.currentSampleRightTrimary + this.channel3.currentSampleRightSecondary + this.channel4.currentSampleRightSecondary) * this.VinRightChannelMasterVolume; + 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.IOCore.updateTimerClocking(); + this.audioPSGJIT(); this.channel3.writeWAVE8(address | 0, data | 0); } GameBoyAdvanceSound.prototype.readWAVE8 = function (address) { address = address | 0; - this.IOCore.updateTimerClocking(); + this.audioPSGJIT(); return this.channel3.readWAVE8(address | 0) | 0; } GameBoyAdvanceSound.prototype.writeWAVE16 = function (address, data) { address = address | 0; data = data | 0; - this.IOCore.updateTimerClocking(); + this.audioPSGJIT(); this.channel3.writeWAVE16(address >> 1, data | 0); } GameBoyAdvanceSound.prototype.readWAVE16 = function (address) { address = address | 0; - this.IOCore.updateTimerClocking(); + this.audioPSGJIT(); return this.channel3.readWAVE16(address >> 1) | 0; } GameBoyAdvanceSound.prototype.writeWAVE32 = function (address, data) { address = address | 0; data = data | 0; - this.IOCore.updateTimerClocking(); + this.audioPSGJIT(); this.channel3.writeWAVE32(address >> 2, data | 0); } GameBoyAdvanceSound.prototype.readWAVE32 = function (address) { address = address | 0; - this.IOCore.updateTimerClocking(); + this.audioPSGJIT(); return this.channel3.readWAVE32(address >> 2) | 0; } GameBoyAdvanceSound.prototype.writeFIFOA8 = function (data) { @@ -520,8 +497,7 @@ GameBoyAdvanceSound.prototype.writeSOUND1CNT8_0 = function (data) { //NR10: data = data | 0; if (this.soundMasterEnabled) { - this.IOCore.updateTimerClocking(); - this.audioJIT(); + this.audioPSGJIT(); this.channel1.writeSOUND1CNT8_0(data | 0); } } @@ -533,8 +509,7 @@ GameBoyAdvanceSound.prototype.writeSOUND1CNT8_2 = function (data) { //NR11: data = data | 0; if (this.soundMasterEnabled) { - this.IOCore.updateTimerClocking(); - this.audioJIT(); + this.audioPSGJIT(); this.channel1.writeSOUND1CNT8_2(data | 0); } } @@ -546,16 +521,14 @@ GameBoyAdvanceSound.prototype.writeSOUND1CNT8_3 = function (data) { //NR12: data = data | 0; if (this.soundMasterEnabled) { - this.IOCore.updateTimerClocking(); - this.audioJIT(); + this.audioPSGJIT(); this.channel1.writeSOUND1CNT8_3(data | 0); } } GameBoyAdvanceSound.prototype.writeSOUND1CNT16 = function (data) { data = data | 0; if (this.soundMasterEnabled) { - this.IOCore.updateTimerClocking(); - this.audioJIT(); + this.audioPSGJIT(); //NR11: this.channel1.writeSOUND1CNT8_2(data | 0); //NR12: @@ -565,8 +538,7 @@ GameBoyAdvanceSound.prototype.writeSOUND1CNT16 = function (data) { GameBoyAdvanceSound.prototype.writeSOUND1CNT32 = function (data) { data = data | 0; if (this.soundMasterEnabled) { - this.IOCore.updateTimerClocking(); - this.audioJIT(); + this.audioPSGJIT(); //NR10: this.channel1.writeSOUND1CNT8_0(data | 0); //NR11: @@ -575,217 +547,371 @@ GameBoyAdvanceSound.prototype.writeSOUND1CNT32 = function (data) { this.channel1.writeSOUND1CNT8_3(data >> 24); } } -GameBoyAdvanceSound.prototype.writeSOUND1CNT_X0 = function (data) { +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.audioJIT(); + this.audioPSGJIT(); this.channel1.writeSOUND1CNT_X0(data | 0); } } -GameBoyAdvanceSound.prototype.readSOUND1CNT_X = function () { +GameBoyAdvanceSound.prototype.readSOUND1CNTX8 = function () { //NR14: - return this.channel1.readSOUND1CNT_X() | 0; + return this.channel1.readSOUND1CNTX8() | 0; } -GameBoyAdvanceSound.prototype.writeSOUND1CNT_X1 = function (data) { +GameBoyAdvanceSound.prototype.readSOUND1CNTX16 = function () { + //NR14: + return this.channel1.readSOUND1CNTX8() << 8; +} +GameBoyAdvanceSound.prototype.writeSOUND1CNTX8_1 = function (data) { //NR14: data = data | 0; if (this.soundMasterEnabled) { - this.audioJIT(); + this.audioPSGJIT(); this.channel1.writeSOUND1CNT_X1(data | 0); } } -GameBoyAdvanceSound.prototype.readSOUND2CNT_L0 = function () { +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.writeSOUND2CNT_L0 = function (data) { +GameBoyAdvanceSound.prototype.writeSOUND2CNTL8_0 = function (data) { data = data | 0; //NR21: if (this.soundMasterEnabled) { - this.audioJIT(); + this.audioPSGJIT(); this.channel2.writeSOUND2CNT_L0(data | 0); } } -GameBoyAdvanceSound.prototype.readSOUND2CNT_L1 = function () { +GameBoyAdvanceSound.prototype.readSOUND2CNTL8_1 = function () { //NR22: return this.channel2.readSOUND2CNT_L1() | 0; } -GameBoyAdvanceSound.prototype.writeSOUND2CNT_L1 = function (data) { +GameBoyAdvanceSound.prototype.writeSOUND2CNTL8_1 = function (data) { data = data | 0; //NR22: if (this.soundMasterEnabled) { - this.audioJIT(); + this.audioPSGJIT(); this.channel2.writeSOUND2CNT_L1(data | 0); } } -GameBoyAdvanceSound.prototype.writeSOUND2CNT_H0 = function (data) { +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.audioJIT(); + this.audioPSGJIT(); this.channel2.writeSOUND2CNT_H0(data | 0); } } -GameBoyAdvanceSound.prototype.readSOUND2CNT_H = function () { +GameBoyAdvanceSound.prototype.readSOUND2CNTH8 = function () { //NR24: return this.channel2.readSOUND2CNT_H() | 0; } -GameBoyAdvanceSound.prototype.writeSOUND2CNT_H1 = function (data) { +GameBoyAdvanceSound.prototype.writeSOUND2CNTH8_1 = function (data) { data = data | 0; //NR24: if (this.soundMasterEnabled) { - this.audioJIT(); + this.audioPSGJIT(); this.channel2.writeSOUND2CNT_H1(data | 0); } } -GameBoyAdvanceSound.prototype.readSOUND3CNT_L = function () { +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.writeSOUND3CNT_L = function (data) { +GameBoyAdvanceSound.prototype.writeSOUND3CNT8_0 = function (data) { //NR30: data = data | 0; if (this.soundMasterEnabled) { - this.audioJIT(); + this.audioPSGJIT(); this.channel3.writeSOUND3CNT_L(data | 0); } } -GameBoyAdvanceSound.prototype.writeSOUND3CNT_H0 = function (data) { +GameBoyAdvanceSound.prototype.writeSOUND3CNT8_2 = function (data) { //NR31: data = data | 0; if (this.soundMasterEnabled) { - this.audioJIT(); + this.audioPSGJIT(); this.channel3.writeSOUND3CNT_H0(data | 0); } } -GameBoyAdvanceSound.prototype.readSOUND3CNT_H = function () { +GameBoyAdvanceSound.prototype.readSOUND3CNT8_3 = function () { //NR32: return this.channel3.readSOUND3CNT_H() | 0; } -GameBoyAdvanceSound.prototype.writeSOUND3CNT_H1 = function (data) { +GameBoyAdvanceSound.prototype.writeSOUND3CNT8_3 = function (data) { //NR32: data = data | 0; if (this.soundMasterEnabled) { - this.audioJIT(); + this.audioPSGJIT(); this.channel3.writeSOUND3CNT_H1(data | 0); } } -GameBoyAdvanceSound.prototype.writeSOUND3CNT_X0 = function (data) { +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.audioJIT(); + this.audioPSGJIT(); this.channel3.writeSOUND3CNT_X0(data | 0); } } -GameBoyAdvanceSound.prototype.readSOUND3CNT_X = function () { +GameBoyAdvanceSound.prototype.readSOUND3CNTX8 = function () { //NR34: return this.channel3.readSOUND3CNT_X() | 0; } -GameBoyAdvanceSound.prototype.writeSOUND3CNT_X1 = function (data) { +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.audioJIT(); + this.audioPSGJIT(); this.channel3.writeSOUND3CNT_X1(data | 0); } } -GameBoyAdvanceSound.prototype.writeSOUND4CNT_L0 = function (data) { +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.audioJIT(); + this.audioPSGJIT(); this.channel4.writeSOUND4CNT_L0(data | 0); } } -GameBoyAdvanceSound.prototype.writeSOUND4CNT_L1 = function (data) { +GameBoyAdvanceSound.prototype.writeSOUND4CNTL8_1 = function (data) { //NR42: data = data | 0; if (this.soundMasterEnabled) { - this.audioJIT(); + this.audioPSGJIT(); this.channel4.writeSOUND4CNT_L1(data | 0); } } -GameBoyAdvanceSound.prototype.readSOUND4CNT_L = function () { +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.writeSOUND4CNT_H0 = function (data) { +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.audioJIT(); + this.audioPSGJIT(); this.channel4.writeSOUND4CNT_H0(data | 0); } } -GameBoyAdvanceSound.prototype.readSOUND4CNT_H0 = function () { +GameBoyAdvanceSound.prototype.readSOUND4CNTH8_0 = function () { //NR43: return this.channel4.readSOUND4CNT_H0() | 0; } -GameBoyAdvanceSound.prototype.writeSOUND4CNT_H1 = function (data) { +GameBoyAdvanceSound.prototype.writeSOUND4CNTH8_1 = function (data) { //NR44: data = data | 0; if (this.soundMasterEnabled) { - this.audioJIT(); + this.audioPSGJIT(); this.channel4.writeSOUND4CNT_H1(data | 0); } } -GameBoyAdvanceSound.prototype.readSOUND4CNT_H1 = function () { +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.writeSOUNDCNT_L0 = function (data) { +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.audioJIT(); + this.audioPSGJIT(); this.nr50 = data | 0; this.VinLeftChannelMasterVolume = (((data >> 4) & 0x07) + 1) | 0; this.VinRightChannelMasterVolume = ((data & 0x07) + 1) | 0; } } -GameBoyAdvanceSound.prototype.readSOUNDCNT_L0 = function () { +GameBoyAdvanceSound.prototype.readSOUNDCNTL8_0 = function () { //NR50: return 0x88 | this.nr50; } -GameBoyAdvanceSound.prototype.writeSOUNDCNT_L1 = function (data) { +GameBoyAdvanceSound.prototype.writeSOUNDCNTL8_1 = function (data) { //NR51: data = data | 0; + data = data & 0xFF; if (this.soundMasterEnabled && (this.nr51 | 0) != (data | 0)) { - this.audioJIT(); + this.audioPSGJIT(); this.nr51 = data | 0; - this.rightChannel1 = ((data & 0x01) == 0x01); - this.rightChannel2 = ((data & 0x02) == 0x02); - this.rightChannel3 = ((data & 0x04) == 0x04); - this.rightChannel4 = ((data & 0x08) == 0x08); - this.leftChannel1 = ((data & 0x10) == 0x10); - this.leftChannel2 = ((data & 0x20) == 0x20); - this.leftChannel3 = ((data & 0x40) == 0x40); - this.leftChannel4 = (data > 0x7F); + this.channel1.setChannelOutputEnable(data | 0); + this.channel2.setChannelOutputEnable(data | 0); + this.channel3.setChannelOutputEnable(data | 0); + this.channel4.setChannelOutputEnable(data | 0); } } -GameBoyAdvanceSound.prototype.readSOUNDCNT_L1 = function () { +GameBoyAdvanceSound.prototype.readSOUNDCNTL8_1 = function () { //NR51: return this.nr51 | 0; } -GameBoyAdvanceSound.prototype.writeSOUNDCNT_H0 = function (data) { +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.audioJIT(); + this.audioPSGJIT(); this.CGBOutputRatio = data & 0x3; this.AGBDirectSoundAShifter = (data & 0x04) >> 2; this.AGBDirectSoundBShifter = (data & 0x08) >> 3; - this.nr60 = data | 0; + this.nr60 = data & 0xFF; } -GameBoyAdvanceSound.prototype.readSOUNDCNT_H0 = function () { +GameBoyAdvanceSound.prototype.readSOUNDCNTH8_0 = function () { //NR60: return this.nr60 | 0; } -GameBoyAdvanceSound.prototype.writeSOUNDCNT_H1 = function (data) { +GameBoyAdvanceSound.prototype.writeSOUNDCNTH8_1 = function (data) { //NR61: data = data | 0; - this.audioJIT(); + this.audioPSGJIT(); this.AGBDirectSoundARightCanPlay = ((data & 0x1) != 0); this.AGBDirectSoundALeftCanPlay = ((data & 0x2) != 0); this.AGBDirectSoundATimer = (data & 0x4) >> 2; @@ -798,51 +924,159 @@ GameBoyAdvanceSound.prototype.writeSOUNDCNT_H1 = function (data) { if ((data & 0x80) != 0) { this.AGBDirectSoundBFIFOClear(); } - this.nr61 = data | 0; + this.nr61 = data & 0xFF; + this.IOCore.updateCoreClocking(); } -GameBoyAdvanceSound.prototype.readSOUNDCNT_H1 = function () { +GameBoyAdvanceSound.prototype.readSOUNDCNTH8_1 = function () { //NR61: return this.nr61 | 0; } -GameBoyAdvanceSound.prototype.writeSOUNDCNT_X = function (data) { +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 | 0) > 0x7F) { - this.audioJIT(); + if (!this.soundMasterEnabled && (data & 0x80) != 0) { + this.audioPSGJIT(); this.audioEnabled(); + this.IOCore.updateCoreClocking(); } - else if (this.soundMasterEnabled && (data | 0) < 0x80) { - this.audioJIT(); + else if (this.soundMasterEnabled && (data & 0x80) == 0) { + this.audioPSGJIT(); this.audioDisabled(); + this.IOCore.updateCoreClocking(); } } -GameBoyAdvanceSound.prototype.readSOUNDCNT_X = function () { +GameBoyAdvanceSound.prototype.readSOUNDCNTX8 = function () { //NR52: + this.audioPSGJIT(); return this.nr52 | 0; } -GameBoyAdvanceSound.prototype.writeSOUNDBIAS0 = function (data) { +GameBoyAdvanceSound.prototype.writeSOUNDBIAS8_0 = function (data) { //NR62: data = data | 0; - this.audioJIT(); + this.audioPSGJIT(); this.mixerSoundBIAS = this.mixerSoundBIAS & 0x300; - this.mixerSoundBIAS = this.mixerSoundBIAS | data; - this.nr62 = data | 0; + this.mixerSoundBIAS = this.mixerSoundBIAS | (data & 0xFF); + this.nr62 = data & 0xFF; } -GameBoyAdvanceSound.prototype.readSOUNDBIAS0 = function () { +GameBoyAdvanceSound.prototype.readSOUNDBIAS8_0 = function () { //NR62: return this.nr62 | 0; } -GameBoyAdvanceSound.prototype.writeSOUNDBIAS1 = function (data) { +GameBoyAdvanceSound.prototype.writeSOUNDBIAS8_1 = function (data) { //NR63: data = data | 0; - this.audioJIT(); + 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 | 0; + this.nr63 = data & 0xFF; } -GameBoyAdvanceSound.prototype.readSOUNDBIAS1 = function () { +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/IodineGBA/core/Wait.js b/IodineGBA/core/Wait.js index a8c7388..2a8d9dd 100644 --- a/IodineGBA/core/Wait.js +++ b/IodineGBA/core/Wait.js @@ -90,7 +90,7 @@ GameBoyAdvanceWait.prototype.setWaitState = function (region, data) { this.waitStateClocks32[0x8 | region] = secondAccess << 1; this.waitStateClocks32[0x9 | region] = secondAccess << 1; } -GameBoyAdvanceWait.prototype.writeWAITCNT0 = function (data) { +GameBoyAdvanceWait.prototype.writeWAITCNT8_0 = function (data) { data = data | 0; //Set SRAM Wait State: if ((data & 0x3) < 0x3) { @@ -99,19 +99,20 @@ GameBoyAdvanceWait.prototype.writeWAITCNT0 = function (data) { 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.readWAITCNT0 = function () { +GameBoyAdvanceWait.prototype.readWAITCNT8_0 = function () { return this.WAITCNT0 | 0; } -GameBoyAdvanceWait.prototype.writeWAITCNT1 = function (data) { +GameBoyAdvanceWait.prototype.writeWAITCNT8_1 = function (data) { data = data | 0; //Set Wait State 2: - this.setWaitState(2, data | 0); + this.setWaitState(2, data & 0xFF); //Set Prefetch Mode: if ((data & 0x40) == 0) { //No Prefetch: @@ -130,11 +131,20 @@ GameBoyAdvanceWait.prototype.writeWAITCNT1 = function (data) { } this.WAITCNT1 = data & 0x5F; } -GameBoyAdvanceWait.prototype.readWAITCNT1 = function () { +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 | 0; + this.POSTBOOT = data & 0xFF; } GameBoyAdvanceWait.prototype.readPOSTBOOT = function () { return this.POSTBOOT | 0; @@ -152,19 +162,33 @@ GameBoyAdvanceWait.prototype.writeHALTCNT = function (data) { 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; + this.WRAMConfiguration = (this.WRAMConfiguration & 0xFFFFFF00) | (data & 0xFF); break; case 1: - this.WRAMConfiguration = (this.WRAMConfiguration & 0xFFFF00FF) | (data << 8); + this.WRAMConfiguration = (this.WRAMConfiguration & 0xFFFF00FF) | ((data & 0xFF) << 8); break; case 2: - this.WRAMConfiguration = (this.WRAMConfiguration & 0xFF00FFFF) | (data << 16); + this.WRAMConfiguration = (this.WRAMConfiguration & 0xFF00FFFF) | ((data & 0xFF) << 16); break; default: this.WRAMWaitState = (0x10 - (data & 0xF)) | 0; diff --git a/IodineGBA/core/Worker.js b/IodineGBA/core/Worker.js new file mode 100644 index 0000000..8231842 --- /dev/null +++ b/IodineGBA/core/Worker.js @@ -0,0 +1,300 @@ +"use strict"; +/* + Copyright (C) 2012-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. + */ +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 | 0); + 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/IodineGBA/core/cartridge/GPIO.js b/IodineGBA/core/cartridge/GPIO.js new file mode 100644 index 0000000..15c4f7e --- /dev/null +++ b/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/IodineGBA/core/graphics/AffineBG.js b/IodineGBA/core/graphics/AffineBG.js index 4f6a929..d3c8207 100644 --- a/IodineGBA/core/graphics/AffineBG.js +++ b/IodineGBA/core/graphics/AffineBG.js @@ -12,10 +12,13 @@ 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.offset = ((this.BGLayer << 8) + 0x100) | 0; + 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; @@ -25,16 +28,17 @@ if (__VIEWS_SUPPORTED__) { this.BGReferenceY = 0; this.pb = 0; this.pd = 0; - this.priorityPreprocess(); + this.doMosaic = 0; + this.priorityPreprocess(0); this.offsetReferenceCounters(); } if (typeof Math.imul == "function") { //Math.imul found, insert the optimized path in: - GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine = function (line, BGObject) { + GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2M = function (line) { line = line | 0; var x = this.pb | 0; var y = this.pd | 0; - if ((this.gfx.BGMosaic[this.BGLayer & 3] | 0) != 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; @@ -42,9 +46,47 @@ if (__VIEWS_SUPPORTED__) { } 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 | BGObject.fetchPixel(x >> 8, y >> 8); + this.scratchBuffer[position | 0] = this.priorityFlag | this.bg2MatrixRenderer.fetchPixel(x >> 8, y >> 8); } - if ((this.gfx.BGMosaic[this.BGLayer & 3] | 0) != 0) { + 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); } @@ -57,22 +99,58 @@ if (__VIEWS_SUPPORTED__) { } else { //Math.imul not found, use the compatibility method: - GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine = function (line, BGObject) { + GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2M = function (line) { var x = this.pb; var y = this.pd; - if ((this.gfx.BGMosaic[this.BGLayer & 3] | 0) != 0) { + if (this.doMosaic != 0) { //Correct line number for mosaic: - var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line | 0); + 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 | BGObject.fetchPixel(x >> 8, y >> 8); + this.scratchBuffer[position] = this.priorityFlag | this.bg2MatrixRenderer.fetchPixel(x >> 8, y >> 8); } - if ((this.gfx.BGMosaic[this.BGLayer & 3] | 0) != 0) { + if (this.doMosaic != 0) { //Pixelize the line horizontally: - this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset | 0); + 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 () { @@ -84,7 +162,9 @@ if (__VIEWS_SUPPORTED__) { } else { GameBoyAdvanceAffineBGRenderer.prototype.initialize = function () { - this.offset = (this.BGLayer << 8) + 0x100; + 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; @@ -94,23 +174,60 @@ else { this.BGReferenceY = 0; this.pb = 0; this.pd = 0; - this.priorityPreprocess(); + this.doMosaic = 0; + this.priorityPreprocess(0); this.offsetReferenceCounters(); } - GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine = function (line, BGObject) { + GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2M = function (line) { var x = this.pb; var y = this.pd; - if ((this.gfx.BGMosaic[this.BGLayer & 3] | 0) != 0) { + if (this.doMosaic != 0) { //Correct line number for mosaic: - var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line | 0); + 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 | BGObject.fetchPixel(x >> 8, y >> 8); + this.scratchBuffer[this.offset + position] = this.priorityFlag | this.bg2MatrixRenderer.fetchPixel(x >> 8, y >> 8); } - if ((this.gfx.BGMosaic[this.BGLayer & 3] | 0) != 0) { + 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); } @@ -129,8 +246,13 @@ GameBoyAdvanceAffineBGRenderer.prototype.resetReferenceCounters = function () { this.pb = this.BGReferenceX | 0; this.pd = this.BGReferenceY | 0; } -GameBoyAdvanceAffineBGRenderer.prototype.priorityPreprocess = function () { - this.priorityFlag = (this.gfx.BGPriority[this.BGLayer | 0] << 23) | (1 << (this.BGLayer | 0x10)); +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; diff --git a/IodineGBA/core/graphics/BG2FrameBuffer.js b/IodineGBA/core/graphics/BG2FrameBuffer.js index 29c6d02..84308c8 100644 --- a/IodineGBA/core/graphics/BG2FrameBuffer.js +++ b/IodineGBA/core/graphics/BG2FrameBuffer.js @@ -1,11 +1,11 @@ "use strict"; /* - Copyright (C) 2012-2015 Grant Galitz - + 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) { @@ -16,7 +16,6 @@ GameBoyAdvanceBG2FrameBufferRenderer.prototype.initialize = function () { this.VRAM = this.gfx.VRAM; this.VRAM16 = this.gfx.VRAM16; this.fetchPixel = this.fetchMode3Pixel; - this.bgAffineRenderer = this.gfx.bgAffineRenderer0; this.frameSelect = 0; } GameBoyAdvanceBG2FrameBufferRenderer.prototype.selectMode = function (mode) { @@ -32,10 +31,6 @@ GameBoyAdvanceBG2FrameBufferRenderer.prototype.selectMode = function (mode) { this.fetchPixel = this.fetchMode5Pixel; } } -GameBoyAdvanceBG2FrameBufferRenderer.prototype.renderScanLine = function (line) { - line = line | 0; - this.bgAffineRenderer.renderScanLine(line | 0, this); -} if (__LITTLE_ENDIAN__) { if (typeof Math.imul == "function") { //Math.imul found, insert the optimized path in: @@ -43,7 +38,7 @@ if (__LITTLE_ENDIAN__) { x = x | 0; y = y | 0; //Output pixel: - if ((x | 0) > -1 && (y | 0) > -1 && (x | 0) < 240 && (y | 0) < 160) { + if ((x >>> 0) < 240 && (y >>> 0) < 160) { var address = (Math.imul(y | 0, 240) + (x | 0)) | 0; return this.VRAM16[address & 0xFFFF] & 0x7FFF; } @@ -54,7 +49,7 @@ if (__LITTLE_ENDIAN__) { x = x | 0; y = y | 0; //Output pixel: - if ((x | 0) > -1 && (y | 0) > -1 && (x | 0) < 160 && (y | 0) < 128) { + 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; } @@ -68,7 +63,7 @@ if (__LITTLE_ENDIAN__) { x = x | 0; y = y | 0; //Output pixel: - if ((x | 0) > -1 && (y | 0) > -1 && (x | 0) < 240 && (y | 0) < 160) { + if ((x >>> 0) < 240 && (y >>> 0) < 160) { var address = (((y * 240) | 0) + (x | 0)) | 0; return this.VRAM16[address & 0xFFFF] & 0x7FFF; } @@ -79,7 +74,7 @@ if (__LITTLE_ENDIAN__) { x = x | 0; y = y | 0; //Output pixel: - if ((x | 0) > -1 && (y | 0) > -1 && (x | 0) < 160 && (y | 0) < 128) { + if ((x >>> 0) < 160 && (y >>> 0) < 128) { var address = ((this.frameSelect | 0) + ((y * 160) | 0) + (x | 0)) | 0; return this.VRAM16[address & 0xFFFF] & 0x7FFF; } @@ -95,7 +90,7 @@ else { x = x | 0; y = y | 0; //Output pixel: - if ((x | 0) > -1 && (y | 0) > -1 && (x | 0) < 240 && (y | 0) < 160) { + 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; } @@ -106,7 +101,7 @@ else { x = x | 0; y = y | 0; //Output pixel: - if ((x | 0) > -1 && (y | 0) > -1 && (x | 0) < 160 && (y | 0) < 128) { + 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; } @@ -118,7 +113,7 @@ else { //Math.imul not found, use the compatibility method: GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode3Pixel = function (x, y) { //Output pixel: - if (x > -1 && y > -1 && x < 240 && y < 160) { + if ((x >>> 0) < 240 && (y >>> 0) < 160) { var address = ((y * 240) + x) << 1; return ((this.VRAM[address | 1] << 8) | this.VRAM[address]) & 0x7FFF; } @@ -127,7 +122,7 @@ else { } GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode5Pixel = function (x, y) { //Output pixel: - if (x > -1 && y > -1 && x < 160 && y < 128) { + 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; } @@ -142,7 +137,7 @@ if (typeof Math.imul == "function") { x = x | 0; y = y | 0; //Output pixel: - if ((x | 0) > -1 && (y | 0) > -1 && (x | 0) < 240 && (y | 0) < 160) { + 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; } @@ -154,7 +149,7 @@ else { //Math.imul not found, use the compatibility method: GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode4Pixel = function (x, y) { //Output pixel: - if (x > -1 && y > -1 && x < 240 && y < 160) { + if ((x >>> 0) < 240 && (y >>> 0) < 160) { return this.palette[this.VRAM[this.frameSelect + (y * 240) + x]]; } //Out of range, output transparency: @@ -164,4 +159,4 @@ else { GameBoyAdvanceBG2FrameBufferRenderer.prototype.writeFrameSelect = function (frameSelect) { frameSelect = frameSelect >> 31; this.frameSelect = frameSelect & 0xA000; -} \ No newline at end of file +} diff --git a/IodineGBA/core/graphics/BGMatrix.js b/IodineGBA/core/graphics/BGMatrix.js index ec6e165..1d372b5 100644 --- a/IodineGBA/core/graphics/BGMatrix.js +++ b/IodineGBA/core/graphics/BGMatrix.js @@ -8,29 +8,17 @@ 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, BGLayer) { - BGLayer = BGLayer | 0; +function GameBoyAdvanceBGMatrixRenderer(gfx) { this.gfx = gfx; - this.BGLayer = BGLayer | 0; } GameBoyAdvanceBGMatrixRenderer.prototype.initialize = function () { this.VRAM = this.gfx.VRAM; this.palette = this.gfx.palette256; - if ((this.BGLayer & 0x1) == 0) { - this.bgAffineRenderer = this.gfx.bgAffineRenderer0; - } - else { - this.bgAffineRenderer = this.gfx.bgAffineRenderer1; - } - this.screenSizePreprocess(); - this.screenBaseBlockPreprocess(); - this.characterBaseBlockPreprocess(); + this.screenSizePreprocess(0); + this.screenBaseBlockPreprocess(0); + this.characterBaseBlockPreprocess(0); this.displayOverflowProcess(0); } -GameBoyAdvanceBGMatrixRenderer.prototype.renderScanLine = function (line) { - line = line | 0; - this.bgAffineRenderer.renderScanLine(line | 0, this); -} if (typeof Math.imul == "function") { //Math.imul found, insert the optimized path in: GameBoyAdvanceBGMatrixRenderer.prototype.fetchTile = function (x, y) { @@ -80,14 +68,17 @@ GameBoyAdvanceBGMatrixRenderer.prototype.fetchPixelNoOverflow = function (x, y) var address = this.computeScreenAddress(x | 0, y | 0) | 0; return this.palette[this.VRAM[address & 0xFFFF] & 0xFF] | 0; } -GameBoyAdvanceBGMatrixRenderer.prototype.screenBaseBlockPreprocess = function () { - this.BGScreenBaseBlock = this.gfx.BGScreenBaseBlock[this.BGLayer & 3] << 11; +GameBoyAdvanceBGMatrixRenderer.prototype.screenBaseBlockPreprocess = function (BGScreenBaseBlock) { + BGScreenBaseBlock = BGScreenBaseBlock | 0; + this.BGScreenBaseBlock = BGScreenBaseBlock << 11; } -GameBoyAdvanceBGMatrixRenderer.prototype.characterBaseBlockPreprocess = function () { - this.BGCharacterBaseBlock = this.gfx.BGCharacterBaseBlock[this.BGLayer & 3] << 14; +GameBoyAdvanceBGMatrixRenderer.prototype.characterBaseBlockPreprocess = function (BGCharacterBaseBlock) { + BGCharacterBaseBlock = BGCharacterBaseBlock | 0; + this.BGCharacterBaseBlock = BGCharacterBaseBlock << 14; } -GameBoyAdvanceBGMatrixRenderer.prototype.screenSizePreprocess = function () { - this.mapSize = 0x10 << (this.gfx.BGScreenSize[this.BGLayer & 3] | 0); +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) { diff --git a/IodineGBA/core/graphics/BGTEXT.js b/IodineGBA/core/graphics/BGTEXT.js index f8ada8e..6dab8db 100644 --- a/IodineGBA/core/graphics/BGTEXT.js +++ b/IodineGBA/core/graphics/BGTEXT.js @@ -26,10 +26,11 @@ if (__VIEWS_SUPPORTED__) { this.BGXCoord = 0; this.BGYCoord = 0; this.do256 = 0; - this.screenSizePreprocess(); - this.priorityPreprocess(); - this.screenBaseBlockPreprocess(); - this.characterBaseBlockPreprocess(); + 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; @@ -112,10 +113,11 @@ else { this.BGXCoord = 0; this.BGYCoord = 0; this.do256 = 0; - this.screenSizePreprocess(); - this.priorityPreprocess(); - this.screenBaseBlockPreprocess(); - this.characterBaseBlockPreprocess(); + 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: @@ -177,7 +179,7 @@ else { } GameBoyAdvanceBGTEXTRenderer.prototype.renderScanLine = function (line) { line = line | 0; - if ((this.gfx.BGMosaic[this.BGLayer & 3] | 0) != 0) { + if ((this.doMosaic | 0) != 0) { //Correct line number for mosaic: line = ((line | 0) - (this.gfx.mosaicRenderer.getMosaicYOffset(line | 0) | 0)) | 0; } @@ -193,7 +195,7 @@ GameBoyAdvanceBGTEXTRenderer.prototype.renderScanLine = function (line) { //4-bit palette mode: this.render4BITLine(yTileStart | 0, xTileStart | 0, yTileOffset | 0); } - if ((this.gfx.BGMosaic[this.BGLayer & 3] | 0) != 0) { + if ((this.doMosaic | 0) != 0) { //Pixelize the line horizontally: this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset | 0); } @@ -499,21 +501,50 @@ GameBoyAdvanceBGTEXTRenderer.prototype.addressInvalidRender = function () { 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 () { - this.tileMode = this.gfx.BGScreenSize[this.BGLayer & 0x3] | 0; +GameBoyAdvanceBGTEXTRenderer.prototype.screenSizePreprocess = function (BGScreenSize) { + BGScreenSize = BGScreenSize | 0; + this.tileMode = BGScreenSize | 0; } -GameBoyAdvanceBGTEXTRenderer.prototype.priorityPreprocess = function () { - this.priorityFlag = (this.gfx.BGPriority[this.BGLayer & 3] << 23) | (1 << (this.BGLayer | 0x10)); +GameBoyAdvanceBGTEXTRenderer.prototype.priorityPreprocess = function (BGPriority) { + BGPriority = BGPriority | 0; + this.priorityFlag = (BGPriority << 23) | (1 << (this.BGLayer | 0x10)); } -GameBoyAdvanceBGTEXTRenderer.prototype.screenBaseBlockPreprocess = function () { - this.BGScreenBaseBlock = this.gfx.BGScreenBaseBlock[this.BGLayer & 3] << 10; +GameBoyAdvanceBGTEXTRenderer.prototype.screenBaseBlockPreprocess = function (BGScreenBaseBlock) { + BGScreenBaseBlock = BGScreenBaseBlock | 0; + this.BGScreenBaseBlock = BGScreenBaseBlock << 10; } -GameBoyAdvanceBGTEXTRenderer.prototype.characterBaseBlockPreprocess = function () { - this.BGCharacterBaseBlock = this.gfx.BGCharacterBaseBlock[this.BGLayer & 3] << 12; +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; diff --git a/IodineGBA/core/graphics/ColorEffects.js b/IodineGBA/core/graphics/ColorEffects.js index 356a57b..9e81668 100644 --- a/IodineGBA/core/graphics/ColorEffects.js +++ b/IodineGBA/core/graphics/ColorEffects.js @@ -1,39 +1,298 @@ "use strict"; /* - Copyright (C) 2012-2015 Grant Galitz - + 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() { - this.alphaBlendAmountTarget1 = 0; - this.alphaBlendAmountTarget2 = 0; +function GameBoyAdvanceColorEffectsRenderer(buffer) { this.effectsTarget1 = 0; this.colorEffectsType = 0; this.effectsTarget2 = 0; - this.brightnessEffectAmount = 0; - this.alphaBlendOptimizationChecks(); + this.initialize(buffer); } -GameBoyAdvanceColorEffectsRenderer.prototype.processOAMSemiTransparent = function (lowerPixel, topPixel) { - lowerPixel = lowerPixel | 0; - topPixel = topPixel | 0; - if (((lowerPixel | 0) & (this.effectsTarget2 | 0)) != 0) { - return this.alphaBlend(topPixel | 0, lowerPixel | 0) | 0; +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; } - 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; + 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; } } - return topPixel | 0; } -GameBoyAdvanceColorEffectsRenderer.prototype.process = function (lowerPixel, topPixel) { +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) { @@ -51,229 +310,21 @@ GameBoyAdvanceColorEffectsRenderer.prototype.process = function (lowerPixel, top } return topPixel | 0; } -if (typeof Math.imul == "function") { - //Math.imul found, insert the optimized path in: - GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlendNormal = 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.alphaBlendTop = function (topPixel, lowerPixel) { - topPixel = topPixel | 0; - lowerPixel = lowerPixel | 0; - var b = (topPixel >> 10) & 0x1F; - var g = (topPixel >> 5) & 0x1F; - var r = topPixel & 0x1F; - b = Math.imul(b | 0, this.alphaBlendAmountTarget1 | 0) | 0; - g = Math.imul(g | 0, this.alphaBlendAmountTarget1 | 0) | 0; - r = Math.imul(r | 0, this.alphaBlendAmountTarget1 | 0) | 0; - return ((b >> 4) << 10) | ((g >> 4) << 5) | (r >> 4); - } - GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlendLow = function (topPixel, lowerPixel) { - topPixel = topPixel | 0; - lowerPixel = lowerPixel | 0; - var b = (lowerPixel >> 10) & 0x1F; - var g = (lowerPixel >> 5) & 0x1F; - var r = lowerPixel & 0x1F; - b = Math.imul(b | 0, this.alphaBlendAmountTarget2 | 0) | 0; - g = Math.imul(g | 0, this.alphaBlendAmountTarget2 | 0) | 0; - r = Math.imul(r | 0, this.alphaBlendAmountTarget2 | 0) | 0; - return ((b >> 4) << 10) | ((g >> 4) << 5) | (r >> 4); - } - GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlendAddLow = 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; - //Keep this not inlined in the return, firefox 22 grinds on it: - var b = Math.min(((b1 | 0) + (b2 << 4)) >> 4, 0x1F) | 0; - var g = Math.min(((g1 | 0) + (g2 << 4)) >> 4, 0x1F) | 0; - var r = Math.min(((r1 | 0) + (r2 << 4)) >> 4, 0x1F) | 0; - return (b << 10) | (g << 5) | r; - } - GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlendAddTop = 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; - 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 << 4) + (b2 | 0)) >> 4, 0x1F) | 0; - var g = Math.min(((g1 << 4) + (g2 | 0)) >> 4, 0x1F) | 0; - var r = Math.min(((r1 << 4) + (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.alphaBlendNormal = 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.alphaBlendTop = function (topPixel, lowerPixel) { - topPixel = topPixel | 0; - lowerPixel = lowerPixel | 0; - var b = (topPixel >> 10) & 0x1F; - var g = (topPixel >> 5) & 0x1F; - var r = (topPixel & 0x1F); - b = b * this.alphaBlendAmountTarget1; - g = g * this.alphaBlendAmountTarget1; - r = r * this.alphaBlendAmountTarget1; - return ((b >> 4) << 10) | ((g >> 4) << 5) | (r >> 4); - } - GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlendLow = function (topPixel, lowerPixel) { - topPixel = topPixel | 0; - lowerPixel = lowerPixel | 0; - var b = (lowerPixel >> 10) & 0x1F; - var g = (lowerPixel >> 5) & 0x1F; - var r = (lowerPixel & 0x1F); - b = b * this.alphaBlendAmountTarget2; - g = g * this.alphaBlendAmountTarget2; - r = r * this.alphaBlendAmountTarget2; - return ((b >> 4) << 10) | ((g >> 4) << 5) | (r >> 4); - } - GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlendAddLow = 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 << 4; - g2 = g2 << 4; - r2 = r2 << 4; - return (Math.min((b1 + b2) >> 4, 0x1F) << 10) | (Math.min((g1 + g2) >> 4, 0x1F) << 5) | Math.min((r1 + r2) >> 4, 0x1F); - } - GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlendAddTop = 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 << 4; - g1 = g1 << 4; - r1 = r1 << 4; - 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.alphaBlendTopPass = function (topPixel, lowerPixel) { - return topPixel | 0; -} -GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlendBottomPass = function (topPixel, lowerPixel) { - return lowerPixel | 0; -} -GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlendZero = function (topPixel, lowerPixel) { - return 0; -} -GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlendAddBoth = function (topPixel, lowerPixel) { - topPixel = topPixel | 0; +GameBoyAdvanceColorEffectsRenderer.prototype.processPixelSprite = function (lowerPixel, topPixel) { 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; - //Keep this not inlined in the return, firefox 22 grinds on it: - var b = Math.min(((b1 << 4) + (b2 << 4)) >> 4, 0x1F) | 0; - var g = Math.min(((g1 << 4) + (g2 << 4)) >> 4, 0x1F) | 0; - var r = Math.min(((r1 << 4) + (r2 << 4)) >> 4, 0x1F) | 0; - return (b << 10) | (g << 5) | r; + 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; @@ -294,76 +345,3 @@ GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDCNT16 = function (data) { //Select target 2: this.effectsTarget2 = (data & 0x3F00) << 8; } -GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA8_0 = function (data) { - data = data | 0; - this.alphaBlendAmountTarget1Scratch = data & 0x1F; - this.alphaBlendAmountTarget1 = Math.min(this.alphaBlendAmountTarget1Scratch | 0, 0x10) | 0; - this.alphaBlendOptimizationChecks(); -} -GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA8_1 = function (data) { - data = data | 0; - this.alphaBlendAmountTarget2Scratch = data & 0x1F; - this.alphaBlendAmountTarget2 = Math.min(this.alphaBlendAmountTarget2Scratch | 0, 0x10) | 0; - this.alphaBlendOptimizationChecks(); -} -GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA16 = function (data) { - data = data | 0; - this.alphaBlendAmountTarget1Scratch = data & 0x1F; - this.alphaBlendAmountTarget1 = Math.min(this.alphaBlendAmountTarget1Scratch | 0, 0x10) | 0; - this.alphaBlendAmountTarget2Scratch = (data >> 8) & 0x1F; - this.alphaBlendAmountTarget2 = Math.min(this.alphaBlendAmountTarget2Scratch | 0, 0x10) | 0; - this.alphaBlendOptimizationChecks(); -} -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; - this.alphaBlendAmountTarget1Scratch = (data >> 16) & 0x1F; - this.alphaBlendAmountTarget1 = Math.min(this.alphaBlendAmountTarget1Scratch | 0, 0x10) | 0; - this.alphaBlendAmountTarget2Scratch = (data >> 24) & 0x1F; - this.alphaBlendAmountTarget2 = Math.min(this.alphaBlendAmountTarget2Scratch | 0, 0x10) | 0; - this.alphaBlendOptimizationChecks(); -} -GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlendOptimizationChecks = function () { - //Check for ways to speed up blending: - if ((this.alphaBlendAmountTarget1 | 0) == 0) { - if ((this.alphaBlendAmountTarget2 | 0) == 0) { - this.alphaBlend = this.alphaBlendZero; - } - else if ((this.alphaBlendAmountTarget2 | 0) < 0x10) { - this.alphaBlend = this.alphaBlendLow; - } - else { - this.alphaBlend = this.alphaBlendBottomPass; - } - } - else if ((this.alphaBlendAmountTarget1 | 0) < 0x10) { - if ((this.alphaBlendAmountTarget2 | 0) == 0) { - this.alphaBlend = this.alphaBlendTop; - } - else if ((this.alphaBlendAmountTarget2 | 0) < 0x10) { - this.alphaBlend = this.alphaBlendNormal; - } - else { - this.alphaBlend = this.alphaBlendAddLow; - } - } - else { - if ((this.alphaBlendAmountTarget2 | 0) == 0) { - this.alphaBlend = this.alphaBlendTopPass; - } - else if ((this.alphaBlendAmountTarget2 | 0) < 0x10) { - this.alphaBlend = this.alphaBlendAddTop; - } - else { - this.alphaBlend = this.alphaBlendAddBoth; - } - } -} -GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDY8 = function (data) { - data = data | 0; - this.brightnessEffectAmount = Math.min(data & 0x1F, 0x10) | 0; -} \ No newline at end of file diff --git a/IodineGBA/core/graphics/Compositor.js b/IodineGBA/core/graphics/Compositor.js index 3a198bf..fa023cd 100644 --- a/IodineGBA/core/graphics/Compositor.js +++ b/IodineGBA/core/graphics/Compositor.js @@ -1,11 +1,11 @@ "use strict"; /* - Copyright (C) 2012-2015 Grant Galitz - + 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) { @@ -53,562 +53,897 @@ GameBoyAdvanceWindowCompositor.prototype.renderScanLine = function (xStart, xEnd this.renderScanLineWithEffects(xStart | 0, xEnd | 0, layers | 0); } } -GameBoyAdvanceOBJWindowCompositor.prototype.renderNormalScanLine = GameBoyAdvanceCompositor.prototype.renderNormalScanLine = function (layers) { - layers = layers | 0; - switch (layers | 0) { - case 0: - this.normal0(); - break; - case 1: - this.normal1(); - break; - case 2: - this.normal2(); - break; - case 3: - this.normal3(); - break; - case 4: - this.normal4(); - break; - case 5: - this.normal5(); - break; - case 6: - this.normal6(); - break; - case 7: - this.normal7(); - break; - case 8: - this.normal8(); - break; - case 9: - this.normal9(); - break; - case 10: - this.normal10(); - break; - case 11: - this.normal11(); - break; - case 12: - this.normal12(); - break; - case 13: - this.normal13(); - break; - case 14: - this.normal14(); - break; - case 15: - this.normal15(); - break; - case 16: - this.normal16(); - break; - case 17: - this.normal17(); - break; - case 18: - this.normal18(); - break; - case 19: - this.normal19(); - break; - case 20: - this.normal20(); - break; - case 21: - this.normal21(); - break; - case 22: - this.normal22(); - break; - case 23: - this.normal23(); - break; - case 24: - this.normal24(); - break; - case 25: - this.normal25(); - break; - case 26: - this.normal26(); - break; - case 27: - this.normal27(); - break; - case 28: - this.normal28(); - break; - case 29: - this.normal29(); - break; - case 30: - this.normal30(); - break; - default: - this.normal31(); - } +//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); } -GameBoyAdvanceWindowCompositor.prototype.renderNormalScanLine = function (xStart, xEnd, layers) { - xStart = xStart | 0; - xEnd = xEnd | 0; - layers = layers | 0; - switch (layers | 0) { - case 0: - this.normal0(xStart | 0, xEnd | 0); - break; - case 1: - this.normal1(xStart | 0, xEnd | 0); - break; - case 2: - this.normal2(xStart | 0, xEnd | 0); - break; - case 3: - this.normal3(xStart | 0, xEnd | 0); - break; - case 4: - this.normal4(xStart | 0, xEnd | 0); - break; - case 5: - this.normal5(xStart | 0, xEnd | 0); - break; - case 6: - this.normal6(xStart | 0, xEnd | 0); - break; - case 7: - this.normal7(xStart | 0, xEnd | 0); - break; - case 8: - this.normal8(xStart | 0, xEnd | 0); - break; - case 9: - this.normal9(xStart | 0, xEnd | 0); - break; - case 10: - this.normal10(xStart | 0, xEnd | 0); - break; - case 11: - this.normal11(xStart | 0, xEnd | 0); - break; - case 12: - this.normal12(xStart | 0, xEnd | 0); - break; - case 13: - this.normal13(xStart | 0, xEnd | 0); - break; - case 14: - this.normal14(xStart | 0, xEnd | 0); - break; - case 15: - this.normal15(xStart | 0, xEnd | 0); - break; - case 16: - this.normal16(xStart | 0, xEnd | 0); - break; - case 17: - this.normal17(xStart | 0, xEnd | 0); - break; - case 18: - this.normal18(xStart | 0, xEnd | 0); - break; - case 19: - this.normal19(xStart | 0, xEnd | 0); - break; - case 20: - this.normal20(xStart | 0, xEnd | 0); - break; - case 21: - this.normal21(xStart | 0, xEnd | 0); - break; - case 22: - this.normal22(xStart | 0, xEnd | 0); - break; - case 23: - this.normal23(xStart | 0, xEnd | 0); - break; - case 24: - this.normal24(xStart | 0, xEnd | 0); - break; - case 25: - this.normal25(xStart | 0, xEnd | 0); - break; - case 26: - this.normal26(xStart | 0, xEnd | 0); - break; - case 27: - this.normal27(xStart | 0, xEnd | 0); - break; - case 28: - this.normal28(xStart | 0, xEnd | 0); - break; - case 29: - this.normal29(xStart | 0, xEnd | 0); - break; - case 30: - this.normal30(xStart | 0, xEnd | 0); - break; - default: - this.normal31(xStart | 0, xEnd | 0); - } -} -GameBoyAdvanceOBJWindowCompositor.prototype.renderScanLineWithEffects = GameBoyAdvanceCompositor.prototype.renderScanLineWithEffects = function (layers) { - layers = layers | 0; - switch (layers | 0) { - case 0: - this.special0(); - break; - case 1: - this.special1(); - break; - case 2: - this.special2(); - break; - case 3: - this.special3(); - break; - case 4: - this.special4(); - break; - case 5: - this.special5(); - break; - case 6: - this.special6(); - break; - case 7: - this.special7(); - break; - case 8: - this.special8(); - break; - case 9: - this.special9(); - break; - case 10: - this.special10(); - break; - case 11: - this.special11(); - break; - case 12: - this.special12(); - break; - case 13: - this.special13(); - break; - case 14: - this.special14(); - break; - case 15: - this.special15(); - break; - case 16: - this.special16(); - break; - case 17: - this.special17(); - break; - case 18: - this.special18(); - break; - case 19: - this.special19(); - break; - case 20: - this.special20(); - break; - case 21: - this.special21(); - break; - case 22: - this.special22(); - break; - case 23: - this.special23(); - break; - case 24: - this.special24(); - break; - case 25: - this.special25(); - break; - case 26: - this.special26(); - break; - case 27: - this.special27(); - break; - case 28: - this.special28(); - break; - case 29: - this.special29(); - break; - case 30: - this.special30(); - break; - default: - this.special31(); - } -} -GameBoyAdvanceWindowCompositor.prototype.renderScanLineWithEffects = function (xStart, xEnd, layers) { - xStart = xStart | 0; - xEnd = xEnd | 0; - layers = layers | 0; - switch (layers | 0) { - case 0: - this.special0(xStart | 0, xEnd | 0); - break; - case 1: - this.special1(xStart | 0, xEnd | 0); - break; - case 2: - this.special2(xStart | 0, xEnd | 0); - break; - case 3: - this.special3(xStart | 0, xEnd | 0); - break; - case 4: - this.special4(xStart | 0, xEnd | 0); - break; - case 5: - this.special5(xStart | 0, xEnd | 0); - break; - case 6: - this.special6(xStart | 0, xEnd | 0); - break; - case 7: - this.special7(xStart | 0, xEnd | 0); - break; - case 8: - this.special8(xStart | 0, xEnd | 0); - break; - case 9: - this.special9(xStart | 0, xEnd | 0); - break; - case 10: - this.special10(xStart | 0, xEnd | 0); - break; - case 11: - this.special11(xStart | 0, xEnd | 0); - break; - case 12: - this.special12(xStart | 0, xEnd | 0); - break; - case 13: - this.special13(xStart | 0, xEnd | 0); - break; - case 14: - this.special14(xStart | 0, xEnd | 0); - break; - case 15: - this.special15(xStart | 0, xEnd | 0); - break; - case 16: - this.special16(xStart | 0, xEnd | 0); - break; - case 17: - this.special17(xStart | 0, xEnd | 0); - break; - case 18: - this.special18(xStart | 0, xEnd | 0); - break; - case 19: - this.special19(xStart | 0, xEnd | 0); - break; - case 20: - this.special20(xStart | 0, xEnd | 0); - break; - case 21: - this.special21(xStart | 0, xEnd | 0); - break; - case 22: - this.special22(xStart | 0, xEnd | 0); - break; - case 23: - this.special23(xStart | 0, xEnd | 0); - break; - case 24: - this.special24(xStart | 0, xEnd | 0); - break; - case 25: - this.special25(xStart | 0, xEnd | 0); - break; - case 26: - this.special26(xStart | 0, xEnd | 0); - break; - case 27: - this.special27(xStart | 0, xEnd | 0); - break; - case 28: - this.special28(xStart | 0, xEnd | 0); - break; - case 29: - this.special29(xStart | 0, xEnd | 0); - break; - case 30: - this.special30(xStart | 0, xEnd | 0); - break; - default: - this.special31(xStart | 0, xEnd | 0); - } -} -function generateCompositor() { - function generateLocalScopeInit(count) { - var code = ""; - switch (count | 0) { - case 0: - break; - default: - code = "var workingPixel = 0;"; - case 1: - code += "var currentPixel = 0;"; - code += "var lowerPixel = 0;"; - } - return code; - } - function generateLoopHead(compositeType, count, bodyCode) { - var code = generateLocalScopeInit(count); - switch (compositeType) { - case 0: - code += "for (var xStart = 0; (xStart | 0) < 240; xStart = ((xStart | 0) + 1) | 0) {" + bodyCode + "}"; - break; - case 1: - code = "xStart = xStart | 0; xEnd = xEnd | 0;" + code; - code += "while ((xStart | 0) < (xEnd | 0)) {" + bodyCode + "xStart = ((xStart | 0) + 1) | 0;}"; - break; - case 2: - code += "for (var xStart = 0; (xStart | 0) < 240; xStart = ((xStart | 0) + 1) | 0) {" + - "if ((this.OBJWindowBuffer[xStart | 0] | 0) < 0x3800000) {" + - bodyCode + - "}"+ - "}"; - } - return code; - } - function generateLayerCompare(layerOffset) { - var 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 generateLayerCompareSingle(layerOffset) { - var code = "currentPixel = this.buffer[xStart | " + layerOffset + "] | 0;"; - code += "if ((currentPixel & 0x2000000) != 0) {"; - code += "currentPixel = lowerPixel | 0;"; - code += "}"; - return code; - } - function generateColorEffects(doEffects, layerCount) { - if (layerCount > 0) { - var code = "if ((currentPixel & 0x400000) == 0) {"; - if (doEffects) { - code += "this.buffer[xStart | 0] = this.colorEffectsRenderer.process(lowerPixel | 0, currentPixel | 0) | 0;"; +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; } - else { - code += "this.buffer[xStart | 0] = currentPixel | 0;"; - } - code += "}" - code += "else {" - code += "this.buffer[xStart | 0] = this.colorEffectsRenderer.processOAMSemiTransparent(lowerPixel | 0, currentPixel | 0) | 0;" - code += "}"; - return code; + //Build the loop: + return generateLoopHead(typeof SIMD == "object" && typeof SIMD.Int32x4 == "function", compositeType); } - else { - if (doEffects) { - return "this.buffer[xStart | 0] = this.colorEffectsRenderer.process(0, this.gfx.backdrop | 0) | 0;"; - } - else { - return "this.buffer[xStart | 0] = this.gfx.backdrop | 0;" + 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); + } } } - } - function generateLoopBody(compositeType, doEffects, layers) { - var code = ""; - var count = 0; - if ((layers & 0x1F) == 0x8) { - count++; - code += generateLayerCompareSingle(0x400); - } - else if ((layers & 0x8) != 0) { - count++; - code += generateLayerCompare(0x400); - } - if ((layers & 0x1F) == 0x4) { - count++; - code += generateLayerCompareSingle(0x300); - } - else if ((layers & 0x4) != 0) { - count++; - code += generateLayerCompare(0x300); - } - if ((layers & 0x1F) == 0x2) { - count++; - code += generateLayerCompareSingle(0x200); - } - else if ((layers & 0x2) != 0) { - count++; - code += generateLayerCompare(0x200); - } - if ((layers & 0x1F) == 0x1) { - count++; - code += generateLayerCompareSingle(0x100); - } - else if ((layers & 0x1) != 0) { - count++; - code += generateLayerCompare(0x100); - } - if ((layers & 0x1F) == 0x10) { - count++; - code += generateLayerCompareSingle(0x500); - } - else if ((layers & 0x10) != 0) { - count++; - code += generateLayerCompare(0x500); - } - switch (count) { - case 0: - break; - case 1: - code = "lowerPixel = this.gfx.backdrop | 0;" + code; - break; - default: - code = "lowerPixel = this.gfx.backdrop | 0; currentPixel = lowerPixel | 0;" + code; - } - code += generateColorEffects(doEffects, count); - return generateLoopHead(compositeType, count, code); - } - function generateBlock(compositeType, doEffects) { - var effectsPrefix = (doEffects) ? "special" : "normal"; - for (var layers = 0; layers < 0x20; layers++) { - var code = generateLoopBody(compositeType, doEffects, layers); - switch (compositeType) { - case 0: - GameBoyAdvanceCompositor.prototype[effectsPrefix + layers] = Function(code); - break; - case 1: - GameBoyAdvanceWindowCompositor.prototype[effectsPrefix + layers] = Function("xStart", "xEnd", code); - break; - default: - GameBoyAdvanceOBJWindowCompositor.prototype[effectsPrefix + layers] = Function(code); - } - } - } - function generateAll() { + //Build the functions for each of the three compositors: for (var compositeType = 0; compositeType < 3; compositeType++) { - generateBlock(compositeType, false); - generateBlock(compositeType, true); + //Build for the no special effects processing case: + generateCompositor(compositeType, false); + //Build for the special effects processing case: + generateCompositor(compositeType, true); } } - generateAll(); + 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(); } -generateCompositor(); \ No newline at end of file +generateIodineGBAGFXCompositors(); diff --git a/IodineGBA/core/graphics/Mosaic.js b/IodineGBA/core/graphics/Mosaic.js index 8fdba60..fe958ea 100644 --- a/IodineGBA/core/graphics/Mosaic.js +++ b/IodineGBA/core/graphics/Mosaic.js @@ -15,6 +15,10 @@ function GameBoyAdvanceMosaicRenderer(buffer) { 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; @@ -30,17 +34,35 @@ GameBoyAdvanceMosaicRenderer.prototype.renderMosaicHorizontal = function (offset } } } -GameBoyAdvanceMosaicRenderer.prototype.renderOBJMosaicHorizontal = function (layer, 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 = layer[position | 0] | 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; } - layer[position | 0] = currentPixel | 0; } } } diff --git a/IodineGBA/core/graphics/OBJ.js b/IodineGBA/core/graphics/OBJ.js index c3e0345..5fb364c 100644 --- a/IodineGBA/core/graphics/OBJ.js +++ b/IodineGBA/core/graphics/OBJ.js @@ -1,11 +1,11 @@ "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) { @@ -38,10 +38,9 @@ if (__VIEWS_SUPPORTED__) { this.OAMRAM = getUint8Array(0x400); this.OAMRAM16 = getUint16View(this.OAMRAM); this.OAMRAM32 = getInt32View(this.OAMRAM); - this.offset = 0x500; - this.scratchBuffer = getInt32ViewCustom(this.gfx.buffer, this.offset | 0, ((this.offset | 0) + 240) | 0); + this.scratchBuffer = getInt32ViewCustom(this.gfx.buffer, 0x500, 0x5F0); this.scratchWindowBuffer = getInt32Array(240); - this.scratchOBJBuffer = getInt32Array(128); + this.scratchOBJBuffer = getInt32ViewCustom(this.gfx.buffer, 0x600, 0x680); this.OBJMatrixParameters = getInt32Array(0x80); this.initializeOAMTable(); } @@ -60,10 +59,9 @@ if (__VIEWS_SUPPORTED__) { this.OAMRAM = getUint8Array(0x400); this.OAMRAM16 = getUint16View(this.OAMRAM); this.OAMRAM32 = getInt32View(this.OAMRAM); - this.offset = 0x500; - this.scratchBuffer = getInt32ViewCustom(this.gfx.buffer, this.offset | 0, ((this.offset | 0) + 240) | 0); + this.scratchBuffer = getInt32ViewCustom(this.gfx.buffer, 0x500, 0x5F0); this.scratchWindowBuffer = getInt32Array(240); - this.scratchOBJBuffer = getInt32Array(128); + this.scratchOBJBuffer = getInt32ViewCustom(this.gfx.buffer, 0x600, 0x680); this.clearingBuffer = getInt32Array(240); this.initializeClearingBuffer(); this.OBJMatrixParameters = getInt32Array(0x80); @@ -104,6 +102,25 @@ if (__VIEWS_SUPPORTED__) { } } } + 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 () { @@ -116,6 +133,7 @@ else { 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(); } @@ -143,6 +161,20 @@ else { } } } + 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 = []; @@ -261,7 +293,7 @@ GameBoyAdvanceOBJRenderer.prototype.computeCycles = function (cycles, matrix2D, cyclesToSubtract = cyclesToSubtract << 1; cyclesToSubtract = ((cyclesToSubtract | 0) + 10) | 0; cycles = ((cycles | 0) - (cyclesToSubtract | 0)) | 0; - + } else { //Regular Scrolling: @@ -744,7 +776,7 @@ GameBoyAdvanceOBJRenderer.prototype.outputSpriteToScratch = function (sprite, xS } //Perform the mosaic transform: if ((sprite.mosaic | 0) != 0) { - this.gfx.mosaicRenderer.renderOBJMosaicHorizontal(this.scratchOBJBuffer, xcoord | 0, xSize | 0); + this.gfx.mosaicRenderer.renderOBJMosaicHorizontal(xcoord | 0, xSize | 0); } //Resolve end point: var xcoordEnd = Math.min(((xcoord | 0) + (xSize | 0)) | 0, 240) | 0; @@ -768,7 +800,7 @@ GameBoyAdvanceOBJRenderer.prototype.outputSemiTransparentSpriteToScratch = funct } //Perform the mosaic transform: if ((sprite.mosaic | 0) != 0) { - this.gfx.mosaicRenderer.renderOBJMosaicHorizontal(this.scratchOBJBuffer, xcoord | 0, xSize | 0); + this.gfx.mosaicRenderer.renderOBJMosaicHorizontal(xcoord | 0, xSize | 0); } //Resolve end point: var xcoordEnd = Math.min(((xcoord | 0) + (xSize | 0)) | 0, 240) | 0; @@ -792,14 +824,17 @@ GameBoyAdvanceOBJRenderer.prototype.outputSpriteToOBJWINScratch = function (spri } //Perform the mosaic transform: if ((sprite.mosaic | 0) != 0) { - this.gfx.mosaicRenderer.renderOBJMosaicHorizontal(this.scratchOBJBuffer, xcoord | 0, xSize | 0); + this.gfx.mosaicRenderer.renderOBJMosaicHorizontal(xcoord | 0, xSize | 0); } //Resolve end point: var xcoordEnd = Math.min(((xcoord | 0) + (xSize | 0)) | 0, 240) | 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; - } + 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) { @@ -861,7 +896,7 @@ if (__LITTLE_ENDIAN__) { default: this.OBJMatrixParameters[address >> 2] = (data << 16) >> 16; } - this.OAMRAM16[address | 0] = data | 0; + this.OAMRAM16[address | 0] = data & 0xFFFF; } GameBoyAdvanceOBJRenderer.prototype.writeOAM32 = function (address, data) { address = address | 0; @@ -895,17 +930,18 @@ if (__LITTLE_ENDIAN__) { } GameBoyAdvanceOBJRenderer.prototype.readOAM16 = function (address) { address = address | 0; - return this.OAMRAM16[(address >> 1) & 0x1FF] | 0; + return this.OAMRAM16[address & 0x1FF] | 0; } GameBoyAdvanceOBJRenderer.prototype.readOAM32 = function (address) { address = address | 0; - return this.OAMRAM32[(address >> 2) & 0xFF] | 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: @@ -943,6 +979,7 @@ else { 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: @@ -975,11 +1012,13 @@ else { this.OAMRAM[address | 3] = data >>> 24; } GameBoyAdvanceOBJRenderer.prototype.readOAM16 = function (address) { - address &= 0x3FE; + address &= 0x1FF; + address <<= 1; return this.OAMRAM[address] | (this.OAMRAM[address | 1] << 8); } GameBoyAdvanceOBJRenderer.prototype.readOAM32 = function (address) { - address &= 0x3FC; + address &= 0xFF; + address <<= 2; return this.OAMRAM[address] | (this.OAMRAM[address | 1] << 8) | (this.OAMRAM[address | 2] << 16) | (this.OAMRAM[address | 3] << 24); } -} \ No newline at end of file +} diff --git a/IodineGBA/core/graphics/Renderer.js b/IodineGBA/core/graphics/Renderer.js index 6be202c..a4013a8 100644 --- a/IodineGBA/core/graphics/Renderer.js +++ b/IodineGBA/core/graphics/Renderer.js @@ -1,31 +1,32 @@ "use strict"; /* - Copyright (C) 2012-2015 Grant Galitz - + 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.initializeIO(!!skippingBIOS); + this.initializePaletteStorage(); + this.generateRenderers(); + this.initializeRenderers(); +} +function GameBoyAdvanceGraphicsRendererOffthread(skippingBIOS) { + this.initializeIO(!!skippingBIOS); this.initializePaletteStorage(); this.generateRenderers(); this.initializeRenderers(); } if (__VIEWS_SUPPORTED__) { - GameBoyAdvanceGraphicsRenderer.prototype.initializeIO = function (skippingBIOS) { + GameBoyAdvanceGraphicsRendererOffthread.prototype.initializeIO = GameBoyAdvanceGraphicsRenderer.prototype.initializeIO = function (skippingBIOS) { //Initialize Pre-Boot: this.displayControl = 0x80; this.display = 0; this.greenSwap = 0; - this.BGPriority = getUint8Array(0x4); - this.BGCharacterBaseBlock = getUint8Array(0x4); - this.BGMosaic = getUint8Array(0x4); - this.BGScreenBaseBlock = getUint8Array(0x4); - this.BGScreenSize = getUint8Array(0x4); this.WINOutside = 0; this.paletteRAM = getUint8Array(0x400); this.VRAM = getUint8Array(0x18000); @@ -33,9 +34,17 @@ if (__VIEWS_SUPPORTED__) { this.VRAM32 = getInt32View(this.VRAM); this.paletteRAM16 = getUint16View(this.paletteRAM); this.paletteRAM32 = getInt32View(this.paletteRAM); - this.buffer = getInt32Array(0x600); + //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 = this.coreExposed.frameBuffer; + 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; @@ -47,16 +56,11 @@ if (__VIEWS_SUPPORTED__) { } } else { - GameBoyAdvanceGraphicsRenderer.prototype.initializeIO = function (skippingBIOS) { + GameBoyAdvanceGraphicsRendererOffthread.prototype.initializeIO = GameBoyAdvanceGraphicsRenderer.prototype.initializeIO = function (skippingBIOS) { //Initialize Pre-Boot: this.displayControl = 0x80; this.display = 0; this.greenSwap = 0; - this.BGPriority = getUint8Array(0x4); - this.BGCharacterBaseBlock = getUint8Array(0x4); - this.BGMosaic = [false, false, false, false]; - this.BGScreenBaseBlock = getUint8Array(0x4); - this.BGScreenSize = getUint8Array(0x4); this.WINOutside = 0; this.paletteRAM = getUint8Array(0x400); this.VRAM = getUint8Array(0x18000); @@ -64,8 +68,9 @@ else { this.VRAM32 = getInt32View(this.VRAM); this.paletteRAM16 = getUint16View(this.paletteRAM); this.paletteRAM32 = getInt32View(this.paletteRAM); - this.buffer = getInt32Array(0x600); - this.frameBuffer = this.coreExposed.frameBuffer; + 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; @@ -76,7 +81,7 @@ else { this.backdrop = 0x3A00000; } } -GameBoyAdvanceGraphicsRenderer.prototype.generateRenderers = function () { +GameBoyAdvanceGraphicsRendererOffthread.prototype.generateRenderers = GameBoyAdvanceGraphicsRenderer.prototype.generateRenderers = function () { this.compositor = new GameBoyAdvanceCompositor(this); this.bg0Renderer = new GameBoyAdvanceBGTEXTRenderer(this, 0); this.bg1Renderer = new GameBoyAdvanceBGTEXTRenderer(this, 1); @@ -84,17 +89,17 @@ GameBoyAdvanceGraphicsRenderer.prototype.generateRenderers = function () { this.bg3TextRenderer = new GameBoyAdvanceBGTEXTRenderer(this, 3); this.bgAffineRenderer0 = new GameBoyAdvanceAffineBGRenderer(this, 2); this.bgAffineRenderer1 = new GameBoyAdvanceAffineBGRenderer(this, 3); - this.bg2MatrixRenderer = new GameBoyAdvanceBGMatrixRenderer(this, 2); - this.bg3MatrixRenderer = new GameBoyAdvanceBGMatrixRenderer(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.colorEffectsRenderer = new GameBoyAdvanceColorEffectsRenderer(this.buffer); } -GameBoyAdvanceGraphicsRenderer.prototype.initializeRenderers = function () { +GameBoyAdvanceGraphicsRendererOffthread.prototype.initializeRenderers = GameBoyAdvanceGraphicsRenderer.prototype.initializeRenderers = function () { this.compositor.initialize(); this.compositorPreprocess(); this.bg0Renderer.initialize(); @@ -111,7 +116,7 @@ GameBoyAdvanceGraphicsRenderer.prototype.initializeRenderers = function () { this.window1Renderer.initialize(); this.objWindowRenderer.initialize(); } -GameBoyAdvanceGraphicsRenderer.prototype.initializePaletteStorage = function () { +GameBoyAdvanceGraphicsRendererOffthread.prototype.initializePaletteStorage = GameBoyAdvanceGraphicsRenderer.prototype.initializePaletteStorage = function () { //Both BG and OAM in unified storage: this.palette256 = getInt32Array(0x100); this.palette256[0] = 0x3800000; @@ -124,15 +129,36 @@ GameBoyAdvanceGraphicsRenderer.prototype.initializePaletteStorage = function () this.paletteOBJ16[index << 4] = 0x3800000; } } -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.coreExposed.prepareFrame(); +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(); @@ -146,12 +172,7 @@ GameBoyAdvanceGraphicsRenderer.prototype.graphicsJITScanlineGroup = function () //Normal rendering JIT, where we try to do groups of scanlines at once: while ((this.queuedScanLines | 0) > 0) { this.renderScanLine(); - if ((this.lastUnrenderedLine | 0) < 159) { - this.lastUnrenderedLine = ((this.lastUnrenderedLine | 0) + 1) | 0; - } - else { - this.lastUnrenderedLine = 0; - } + this.incrementScanLine(); this.queuedScanLines = ((this.queuedScanLines | 0) - 1) | 0; } } @@ -160,15 +181,19 @@ GameBoyAdvanceGraphicsRenderer.prototype.incrementScanLineQueue = function () { this.queuedScanLines = ((this.queuedScanLines | 0) + 1) | 0; } else { - if ((this.lastUnrenderedLine | 0) < 159) { - this.lastUnrenderedLine = ((this.lastUnrenderedLine | 0) + 1) | 0; - } - else { - this.lastUnrenderedLine = 0; - } + this.incrementScanLine(); } } -GameBoyAdvanceGraphicsRenderer.prototype.renderScanLine = function () { +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: @@ -199,7 +224,15 @@ GameBoyAdvanceGraphicsRenderer.prototype.renderScanLine = function () { //Update the affine bg counters: this.updateReferenceCounters(); } -GameBoyAdvanceGraphicsRenderer.prototype.renderMode0 = function (line) { +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; @@ -228,7 +261,7 @@ GameBoyAdvanceGraphicsRenderer.prototype.renderMode0 = function (line) { //Composite the windowed result: this.compositeWindowedLayers(line | 0, toRender | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.renderMode1 = function (line) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.renderMode1 = GameBoyAdvanceGraphicsRenderer.prototype.renderMode1 = function (line) { line = line | 0; //Mode 1 Rendering Selected: var toRender = this.display & 0x17; @@ -242,7 +275,7 @@ GameBoyAdvanceGraphicsRenderer.prototype.renderMode1 = function (line) { } if ((toRender & 0x4) != 0) { //Render the BG2 layer: - this.bg2MatrixRenderer.renderScanLine(line | 0); + this.bgAffineRenderer0.renderScanLine2M(line | 0); } if ((toRender & 0x10) != 0) { //Render the sprite layer: @@ -253,17 +286,17 @@ GameBoyAdvanceGraphicsRenderer.prototype.renderMode1 = function (line) { //Composite the windowed result: this.compositeWindowedLayers(line | 0, toRender | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.renderMode2 = function (line) { +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.bg2MatrixRenderer.renderScanLine(line | 0); + this.bgAffineRenderer0.renderScanLine2M(line | 0); } if ((toRender & 0x8) != 0) { //Render the BG3 layer: - this.bg3MatrixRenderer.renderScanLine(line | 0); + this.bgAffineRenderer1.renderScanLine3M(line | 0); } if ((toRender & 0x10) != 0) { //Render the sprite layer: @@ -274,12 +307,12 @@ GameBoyAdvanceGraphicsRenderer.prototype.renderMode2 = function (line) { //Composite the windowed result: this.compositeWindowedLayers(line | 0, toRender | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.renderModeFrameBuffer = function (line) { +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.bg2FrameBufferRenderer.renderScanLine(line | 0); + this.bgAffineRenderer0.renderScanLine2F(line | 0); } if ((toRender & 0x10) != 0) { //Render the sprite layer: @@ -290,7 +323,7 @@ GameBoyAdvanceGraphicsRenderer.prototype.renderModeFrameBuffer = function (line) //Composite the windowed result: this.compositeWindowedLayers(line | 0, toRender | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.compositeLayers = function (toRender) { +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: @@ -299,7 +332,7 @@ GameBoyAdvanceGraphicsRenderer.prototype.compositeLayers = function (toRender) { //Composite the non-windowed result: this.compositor.renderScanLine(toRender | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.compositeWindowedLayers = function (line, toRender) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.compositeWindowedLayers = GameBoyAdvanceGraphicsRenderer.prototype.compositeWindowedLayers = function (line, toRender) { line = line | 0; toRender = toRender | 0; //Composite the windowed result: @@ -318,7 +351,7 @@ GameBoyAdvanceGraphicsRenderer.prototype.compositeWindowedLayers = function (lin } if (typeof Math.imul == "function") { //Math.imul found, insert the optimized path in: - GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBuffer = function (line) { + GameBoyAdvanceGraphicsRendererOffthread.prototype.copyLineToFrameBuffer = GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBuffer = function (line) { line = line | 0; var offsetStart = Math.imul(line | 0, 240) | 0; if ((this.greenSwap | 0) == 0) { @@ -331,7 +364,7 @@ if (typeof Math.imul == "function") { } } if (__LITTLE_ENDIAN__ && typeof Uint8Array.prototype.fill == "function") { - GameBoyAdvanceGraphicsRenderer.prototype.renderForcedBlank = function (line) { + GameBoyAdvanceGraphicsRendererOffthread.prototype.renderForcedBlank = GameBoyAdvanceGraphicsRenderer.prototype.renderForcedBlank = function (line) { line = line | 0; var offsetStart = Math.imul(line | 0, 240) | 0; //Render a blank line: @@ -340,7 +373,7 @@ if (typeof Math.imul == "function") { } } else { - GameBoyAdvanceGraphicsRenderer.prototype.renderForcedBlank = function (line) { + GameBoyAdvanceGraphicsRendererOffthread.prototype.renderForcedBlank = GameBoyAdvanceGraphicsRenderer.prototype.renderForcedBlank = function (line) { line = line | 0; var offsetStart = Math.imul(line | 0, 240) | 0; //Render a blank line: @@ -353,7 +386,7 @@ if (typeof Math.imul == "function") { } else { //Math.imul not found, use the compatibility method: - GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBuffer = function (line) { + GameBoyAdvanceGraphicsRendererOffthread.prototype.copyLineToFrameBuffer = GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBuffer = function (line) { var offsetStart = line * 240; if (this.greenSwap == 0) { //Blit normally: @@ -364,7 +397,7 @@ else { this.copyLineToFrameBufferGreenSwapped(offsetStart); } } - GameBoyAdvanceGraphicsRenderer.prototype.renderForcedBlank = function (line) { + GameBoyAdvanceGraphicsRendererOffthread.prototype.renderForcedBlank = GameBoyAdvanceGraphicsRenderer.prototype.renderForcedBlank = function (line) { var offsetStart = line * 240; //Render a blank line: for (var position = 0; position < 240; ++position) { @@ -373,14 +406,14 @@ else { } } if (__VIEWS_SUPPORTED__ && typeof Uint8Array.prototype.set == "function") { - GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBufferNormal = function (offsetStart) { + GameBoyAdvanceGraphicsRendererOffthread.prototype.copyLineToFrameBufferNormal = GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBufferNormal = function (offsetStart) { offsetStart = offsetStart | 0; //Render a line: this.frameBuffer.set(this.lineBuffer, offsetStart | 0); } } else { - GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBufferNormal = function (offsetStart) { + 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) { @@ -389,7 +422,7 @@ else { } } } -GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBufferGreenSwapped = function (offsetStart) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.copyLineToFrameBufferGreenSwapped = GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBufferGreenSwapped = function (offsetStart) { offsetStart = offsetStart | 0; //Render a line with green swap effect: var position = 0; @@ -406,7 +439,7 @@ GameBoyAdvanceGraphicsRenderer.prototype.copyLineToFrameBufferGreenSwapped = fun offsetStart = ((offsetStart | 0) + 1) | 0; } } -GameBoyAdvanceGraphicsRenderer.prototype.updateReferenceCounters = function () { +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(); @@ -418,14 +451,14 @@ GameBoyAdvanceGraphicsRenderer.prototype.updateReferenceCounters = function () { this.bgAffineRenderer1.incrementReferenceCounters(); } } -GameBoyAdvanceGraphicsRenderer.prototype.compositorPreprocess = function () { +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); } -GameBoyAdvanceGraphicsRenderer.prototype.frameBufferModePreprocess = function (displayControl) { +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: @@ -433,7 +466,7 @@ GameBoyAdvanceGraphicsRenderer.prototype.frameBufferModePreprocess = function (d this.bg2FrameBufferRenderer.selectMode(displayControl | 0); } } -GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeDISPCNT8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bg2FrameBufferRenderer.writeFrameSelect((data & 0x10) << 27); @@ -441,18 +474,18 @@ GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT8_0 = function (data) { this.frameBufferModePreprocess(data | 0); this.displayControl = data | 0; } -GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeDISPCNT8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.display = data & 0xFF; this.compositorPreprocess(); } -GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT8_2 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeDISPCNT8_2 = GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT8_2 = function (data) { data = data | 0; this.graphicsJIT(); this.greenSwap = data & 0x01; } -GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeDISPCNT16 = GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT16 = function (data) { data = data | 0; this.graphicsJIT(); this.bg2FrameBufferRenderer.writeFrameSelect((data & 0x10) << 27); @@ -462,7 +495,7 @@ GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT16 = function (data) { this.display = data >> 8; this.compositorPreprocess(); } -GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeDISPCNT32 = GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT32 = function (data) { data = data | 0; this.graphicsJIT(); this.bg2FrameBufferRenderer.writeFrameSelect((data & 0x10) << 27); @@ -473,727 +506,635 @@ GameBoyAdvanceGraphicsRenderer.prototype.writeDISPCNT32 = function (data) { this.compositorPreprocess(); this.greenSwap = data & 0x10000; } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG0CNT8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0CNT8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0CNT8_0 = function (data) { data = data | 0; this.graphicsJIT(); - this.BGPriority[0] = data & 0x3; - this.BGCharacterBaseBlock[0] = (data & 0xC) >> 2; //Bits 5-6 always 0. - this.BGMosaic[0] = data & 0x40; - this.bg0Renderer.paletteModeSelect(data & 0x80); - this.bg0Renderer.priorityPreprocess(); - this.bg0Renderer.characterBaseBlockPreprocess(); + this.bg0Renderer.writeBGCNT8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG0CNT8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0CNT8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0CNT8_1 = function (data) { data = data | 0; this.graphicsJIT(); - this.BGScreenBaseBlock[0] = data & 0x1F; - this.BGScreenSize[0] = (data & 0xC0) >> 6; - this.bg0Renderer.screenSizePreprocess(); - this.bg0Renderer.screenBaseBlockPreprocess(); + this.bg0Renderer.writeBGCNT8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG0CNT16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0CNT16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0CNT16 = function (data) { data = data | 0; this.graphicsJIT(); - this.BGPriority[0] = data & 0x3; - this.BGCharacterBaseBlock[0] = (data & 0xC) >> 2; //Bits 5-6 always 0. - this.BGMosaic[0] = data & 0x40; - this.bg0Renderer.paletteModeSelect(data & 0x80); - this.bg0Renderer.priorityPreprocess(); - this.bg0Renderer.characterBaseBlockPreprocess(); - this.BGScreenBaseBlock[0] = (data >> 8) & 0x1F; - this.BGScreenSize[0] = (data & 0xC000) >> 14; - this.bg0Renderer.screenSizePreprocess(); - this.bg0Renderer.screenBaseBlockPreprocess(); + this.bg0Renderer.writeBGCNT16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG1CNT8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1CNT8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1CNT8_0 = function (data) { data = data | 0; this.graphicsJIT(); - this.BGPriority[1] = data & 0x3; - this.BGCharacterBaseBlock[1] = (data & 0xC) >> 2; //Bits 5-6 always 0. - this.BGMosaic[1] = data & 0x40; - this.bg1Renderer.paletteModeSelect(data & 0x80); - this.bg1Renderer.priorityPreprocess(); - this.bg1Renderer.characterBaseBlockPreprocess(); + this.bg1Renderer.writeBGCNT8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG1CNT8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1CNT8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1CNT8_1 = function (data) { data = data | 0; this.graphicsJIT(); - this.BGScreenBaseBlock[1] = data & 0x1F; - this.BGScreenSize[1] = (data & 0xC0) >> 6; - this.bg1Renderer.screenSizePreprocess(); - this.bg1Renderer.screenBaseBlockPreprocess(); + this.bg1Renderer.writeBGCNT8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG1CNT16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1CNT16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1CNT16 = function (data) { data = data | 0; this.graphicsJIT(); - this.BGPriority[1] = data & 0x3; - this.BGCharacterBaseBlock[1] = (data & 0xC) >> 2; //Bits 5-6 always 0. - this.BGMosaic[1] = data & 0x40; - this.bg1Renderer.paletteModeSelect(data & 0x80); - this.bg1Renderer.priorityPreprocess(); - this.bg1Renderer.characterBaseBlockPreprocess(); - this.BGScreenBaseBlock[1] = (data >> 8) & 0x1F; - this.BGScreenSize[1] = (data & 0xC000) >> 14; - this.bg1Renderer.screenSizePreprocess(); - this.bg1Renderer.screenBaseBlockPreprocess(); + this.bg1Renderer.writeBGCNT16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG0BG1CNT32 = function (data) { - this.BGPriority[0] = data & 0x3; - this.BGCharacterBaseBlock[0] = (data & 0xC) >> 2; - //Bits 5-6 always 0. - this.BGMosaic[0] = data & 0x40; - this.bg0Renderer.paletteModeSelect(data & 0x80); - this.bg0Renderer.priorityPreprocess(); - this.bg0Renderer.characterBaseBlockPreprocess(); - this.BGScreenBaseBlock[0] = (data >> 8) & 0x1F; - this.BGScreenSize[0] = (data & 0xC000) >> 14; - this.bg0Renderer.screenSizePreprocess(); - this.bg0Renderer.screenBaseBlockPreprocess(); - this.BGPriority[1] = (data >> 16) & 0x3; - this.BGCharacterBaseBlock[1] = (data & 0xC0000) >> 18; - //Bits 5-6 always 0. - this.BGMosaic[1] = data & 0x400000; - this.bg1Renderer.paletteModeSelect((data >> 16) & 0x80); - this.bg1Renderer.priorityPreprocess(); - this.bg1Renderer.characterBaseBlockPreprocess(); - this.BGScreenBaseBlock[1] = (data >> 24) & 0x1F; - this.BGScreenSize[1] = data >>> 30; - this.bg1Renderer.screenSizePreprocess(); - this.bg1Renderer.screenBaseBlockPreprocess(); -} -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2CNT8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0BG1CNT32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0BG1CNT32 = function (data) { data = data | 0; this.graphicsJIT(); - this.BGPriority[2] = data & 0x3; - this.BGCharacterBaseBlock[2] = (data & 0xC) >> 2; //Bits 5-6 always 0. - this.BGMosaic[2] = data & 0x40; - this.bg2TextRenderer.paletteModeSelect(data & 0x80); - this.bg2TextRenderer.priorityPreprocess(); - this.bgAffineRenderer0.priorityPreprocess(); - this.bg2TextRenderer.characterBaseBlockPreprocess(); - this.bg2MatrixRenderer.characterBaseBlockPreprocess(); + this.bg0Renderer.writeBGCNT16(data | 0); + //Bits 5-6 always 0. + this.bg1Renderer.writeBGCNT16(data >> 16); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2CNT8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2CNT8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2CNT8_0 = function (data) { data = data | 0; this.graphicsJIT(); - this.BGScreenBaseBlock[2] = data & 0x1F; - this.BGScreenSize[2] = (data & 0xC0) >> 6; - this.bg2TextRenderer.screenSizePreprocess(); - this.bg2MatrixRenderer.screenSizePreprocess(); - this.bg2TextRenderer.screenBaseBlockPreprocess(); - this.bg2MatrixRenderer.screenBaseBlockPreprocess(); + //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); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2CNT16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2CNT16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2CNT16 = function (data) { data = data | 0; this.graphicsJIT(); - this.BGPriority[2] = data & 0x3; - this.BGCharacterBaseBlock[2] = (data & 0xC) >> 2; //Bits 5-6 always 0. - this.BGMosaic[2] = data & 0x40; - this.bg2TextRenderer.paletteModeSelect(data & 0x80); - this.bg2TextRenderer.priorityPreprocess(); - this.bgAffineRenderer0.priorityPreprocess(); - this.bg2TextRenderer.characterBaseBlockPreprocess(); - this.bg2MatrixRenderer.characterBaseBlockPreprocess(); - this.BGScreenBaseBlock[2] = (data >> 8) & 0x1F; - this.BGScreenSize[2] = (data & 0xC000) >> 14; - this.bg2TextRenderer.screenSizePreprocess(); - this.bg2MatrixRenderer.screenSizePreprocess(); - this.bg2TextRenderer.screenBaseBlockPreprocess(); - this.bg2MatrixRenderer.screenBaseBlockPreprocess(); - this.bg2MatrixRenderer.displayOverflowPreprocess((data >> 8) & 0x20); + 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); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3CNT8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3CNT8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3CNT8_0 = function (data) { data = data | 0; this.graphicsJIT(); - this.BGPriority[3] = data & 0x3; - this.BGCharacterBaseBlock[3] = (data & 0xC) >> 2; //Bits 5-6 always 0. - this.BGMosaic[3] = data & 0x40; - this.bg3TextRenderer.paletteModeSelect(data & 0x80); - this.bg3TextRenderer.priorityPreprocess(); - this.bgAffineRenderer1.priorityPreprocess(); - this.bg3TextRenderer.characterBaseBlockPreprocess(); - this.bg3MatrixRenderer.characterBaseBlockPreprocess(); + this.bg3TextRenderer.writeBGCNT8_0(data | 0); + this.bgAffineRenderer1.setMosaicEnable(data & 0x40); + this.bgAffineRenderer1.priorityPreprocess(data & 0x3); + this.bg3MatrixRenderer.characterBaseBlockPreprocess((data & 0xC) >> 2); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3CNT8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3CNT8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3CNT8_1 = function (data) { data = data | 0; this.graphicsJIT(); - this.BGScreenBaseBlock[3] = data & 0x1F; - this.BGScreenSize[3] = (data & 0xC0) >> 6; - this.bg3TextRenderer.screenSizePreprocess(); - this.bg3MatrixRenderer.screenSizePreprocess(); - this.bg3TextRenderer.screenBaseBlockPreprocess(); - this.bg3MatrixRenderer.screenBaseBlockPreprocess(); + this.bg3TextRenderer.writeBGCNT8_1(data | 0); + this.bg3MatrixRenderer.screenSizePreprocess((data & 0xC0) >> 6); + this.bg3MatrixRenderer.screenBaseBlockPreprocess(data & 0x1F); this.bg3MatrixRenderer.displayOverflowPreprocess(data & 0x20); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3CNT16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3CNT16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3CNT16 = function (data) { data = data | 0; this.graphicsJIT(); - this.BGPriority[3] = data & 0x3; - this.BGCharacterBaseBlock[3] = (data & 0xC) >> 2; //Bits 5-6 always 0. - this.BGMosaic[3] = data & 0x40; - this.bg3TextRenderer.paletteModeSelect(data & 0x80); - this.bg3TextRenderer.priorityPreprocess(); - this.bgAffineRenderer1.priorityPreprocess(); - this.bg3TextRenderer.characterBaseBlockPreprocess(); - this.bg3MatrixRenderer.characterBaseBlockPreprocess(); - this.BGScreenBaseBlock[3] = (data >> 8) & 0x1F; - this.BGScreenSize[3] = (data & 0xC000) >> 14; - this.bg3TextRenderer.screenSizePreprocess(); - this.bg3MatrixRenderer.screenSizePreprocess(); - this.bg3TextRenderer.screenBaseBlockPreprocess(); - this.bg3MatrixRenderer.screenBaseBlockPreprocess(); - this.bg3MatrixRenderer.displayOverflowPreprocess((data >> 8) & 0x20); + 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); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2BG3CNT32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2BG3CNT32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2BG3CNT32 = function (data) { data = data | 0; this.graphicsJIT(); - this.BGPriority[2] = data & 0x3; - this.BGCharacterBaseBlock[2] = (data & 0xC) >> 2; //Bits 5-6 always 0. - this.BGMosaic[2] = data & 0x40; - this.bg2TextRenderer.paletteModeSelect(data & 0x80); - this.bg2TextRenderer.priorityPreprocess(); - this.bgAffineRenderer0.priorityPreprocess(); - this.bg2TextRenderer.characterBaseBlockPreprocess(); - this.bg2MatrixRenderer.characterBaseBlockPreprocess(); - this.BGScreenBaseBlock[2] = (data >> 8) & 0x1F; - this.BGScreenSize[2] = (data & 0xC000) >> 14; - this.bg2TextRenderer.screenSizePreprocess(); - this.bg2MatrixRenderer.screenSizePreprocess(); - this.bg2TextRenderer.screenBaseBlockPreprocess(); - this.bg2MatrixRenderer.screenBaseBlockPreprocess(); + 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); - this.BGPriority[3] = (data >> 16) & 0x3; - this.BGCharacterBaseBlock[3] = (data & 0xC0000) >> 18; //Bits 5-6 always 0. - this.BGMosaic[3] = data & 0x400000; - this.bg3TextRenderer.paletteModeSelect((data >> 16) & 0x80); - this.bg3TextRenderer.priorityPreprocess(); - this.bgAffineRenderer1.priorityPreprocess(); - this.bg3TextRenderer.characterBaseBlockPreprocess(); - this.bg3MatrixRenderer.characterBaseBlockPreprocess(); - this.BGScreenBaseBlock[3] = (data >> 24) & 0x1F; - this.BGScreenSize[3] = data >>> 30; - this.bg3TextRenderer.screenSizePreprocess(); - this.bg3MatrixRenderer.screenSizePreprocess(); - this.bg3TextRenderer.screenBaseBlockPreprocess(); - this.bg3MatrixRenderer.screenBaseBlockPreprocess(); - this.bg3MatrixRenderer.displayOverflowPreprocess((data >> 24) & 0x20); + 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); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG0HOFS8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0HOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0HOFS8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bg0Renderer.writeBGHOFS8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG0HOFS8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0HOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0HOFS8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bg0Renderer.writeBGHOFS8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG0HOFS16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0HOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0HOFS16 = function (data) { data = data | 0; this.graphicsJIT(); this.bg0Renderer.writeBGHOFS16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG0VOFS8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0VOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0VOFS8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bg0Renderer.writeBGVOFS8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG0VOFS8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0VOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0VOFS8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bg0Renderer.writeBGVOFS8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG0VOFS16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0VOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0VOFS16 = function (data) { data = data | 0; this.graphicsJIT(); this.bg0Renderer.writeBGVOFS16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG0OFS32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG0OFS32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG0OFS32 = function (data) { data = data | 0; this.graphicsJIT(); this.bg0Renderer.writeBGOFS32(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG1HOFS8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1HOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1HOFS8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bg1Renderer.writeBGHOFS8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG1HOFS8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1HOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1HOFS8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bg1Renderer.writeBGHOFS8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG1HOFS16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1HOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1HOFS16 = function (data) { data = data | 0; this.graphicsJIT(); this.bg1Renderer.writeBGHOFS16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG1VOFS8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1VOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1VOFS8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bg1Renderer.writeBGVOFS8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG1VOFS8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1VOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1VOFS8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bg1Renderer.writeBGVOFS8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG1VOFS16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1VOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1VOFS16 = function (data) { data = data | 0; this.graphicsJIT(); this.bg1Renderer.writeBGVOFS16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG1OFS32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG1OFS32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG1OFS32 = function (data) { data = data | 0; this.graphicsJIT(); this.bg1Renderer.writeBGOFS32(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2HOFS8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2HOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2HOFS8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bg2TextRenderer.writeBGHOFS8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2HOFS8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2HOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2HOFS8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bg2TextRenderer.writeBGHOFS8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2HOFS16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2HOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2HOFS16 = function (data) { data = data | 0; this.graphicsJIT(); this.bg2TextRenderer.writeBGHOFS16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2VOFS8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2VOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2VOFS8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bg2TextRenderer.writeBGVOFS8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2VOFS8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2VOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2VOFS8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bg2TextRenderer.writeBGVOFS8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2VOFS16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2VOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2VOFS16 = function (data) { data = data | 0; this.graphicsJIT(); this.bg2TextRenderer.writeBGVOFS16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2OFS32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2OFS32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2OFS32 = function (data) { data = data | 0; this.graphicsJIT(); this.bg2TextRenderer.writeBGOFS32(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3HOFS8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3HOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3HOFS8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bg3TextRenderer.writeBGHOFS8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3HOFS8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3HOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3HOFS8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bg3TextRenderer.writeBGHOFS8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3HOFS16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3HOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3HOFS16 = function (data) { data = data | 0; this.graphicsJIT(); this.bg3TextRenderer.writeBGHOFS16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3VOFS8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3VOFS8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3VOFS8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bg3TextRenderer.writeBGVOFS8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3VOFS8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3VOFS8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3VOFS8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bg3TextRenderer.writeBGVOFS8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3VOFS16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3VOFS16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3VOFS16 = function (data) { data = data | 0; this.graphicsJIT(); this.bg3TextRenderer.writeBGVOFS16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3OFS32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3OFS32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3OFS32 = function (data) { data = data | 0; this.graphicsJIT(); this.bg3TextRenderer.writeBGOFS32(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PA8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PA8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PA8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGPA8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PA8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PA8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PA8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGPA8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PA16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PA16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PA16 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGPA16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PB8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PB8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PB8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGPB8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PB8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PB8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PB8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGPB8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PB16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PB16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PB16 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGPB16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PAB32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PAB32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PAB32 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGPAB32(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PC8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PC8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PC8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGPC8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PC8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PC8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PC8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGPC8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PC16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PC16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PC16 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGPC16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PD8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PD8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PD8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGPD8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PD8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PD8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PD8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGPD8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PD16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PD16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PD16 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGPD16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PCD32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2PCD32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2PCD32 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGPCD32(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PA8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PA8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PA8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGPA8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PA8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PA8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PA8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGPA8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PA16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PA16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PA16 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGPA16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PB8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PB8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PB8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGPB8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PB8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PB8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PB8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGPB8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PB16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PB16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PB16 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGPB16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PAB32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PAB32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PAB32 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGPAB32(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PC8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PC8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PC8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGPC8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PC8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PC8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PC8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGPC8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PC16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PC16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PC16 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGPC16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PD8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PD8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PD8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGPD8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PD8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PD8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PD8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGPD8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PD16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PD16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PD16 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGPD16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PCD32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3PCD32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3PCD32 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGPCD32(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2X8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGX8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2X8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGX8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X8_2 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2X8_2 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X8_2 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGX8_2(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X8_3 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2X8_3 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X8_3 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGX8_3(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X16_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2X16_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X16_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGX16_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X16_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2X16_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X16_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGX16_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2X32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2X32 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGX32(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2Y8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGY8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2Y8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGY8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y8_2 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2Y8_2 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y8_2 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGY8_2(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y8_3 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2Y8_3 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y8_3 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGY8_3(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y16_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2Y16_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y16_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGY16_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y16_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2Y16_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y16_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGY16_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG2Y32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG2Y32 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer0.writeBGY32(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3X8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGX8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3X8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGX8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X8_2 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3X8_2 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X8_2 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGX8_2(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X8_3 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3X8_3 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X8_3 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGX8_3(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X16_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3X16_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X16_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGX16_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X16_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3X16_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X16_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGX16_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3X32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3X32 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGX32(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3Y8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGY8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3Y8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGY8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y8_2 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3Y8_2 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y8_2 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGY8_2(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y8_3 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3Y8_3 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y8_3 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGY8_3(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y16_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3Y16_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y16_0 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGY16_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y16_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3Y16_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y16_1 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGY16_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBG3Y32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBG3Y32 = function (data) { data = data | 0; this.graphicsJIT(); this.bgAffineRenderer1.writeBGY32(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0XCOORDRight8 = function (data) { +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. } -GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0XCOORDLeft8 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN0XCOORDLeft8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0XCOORDLeft8 = function (data) { data = data | 0; this.graphicsJIT(); this.window0Renderer.writeWINXCOORDLeft8(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0XCOORD16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN0XCOORD16 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0XCOORD16 = function (data) { data = data | 0; this.graphicsJIT(); this.window0Renderer.writeWINXCOORD16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1XCOORDRight8 = function (data) { +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. } -GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1XCOORDLeft8 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN1XCOORDLeft8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1XCOORDLeft8 = function (data) { data = data | 0; this.graphicsJIT(); this.window1Renderer.writeWINXCOORDLeft8(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1XCOORD16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN1XCOORD16 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1XCOORD16 = function (data) { data = data | 0; this.graphicsJIT(); this.window1Renderer.writeWINXCOORD16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWINXCOORD32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWINXCOORD32 = GameBoyAdvanceGraphicsRenderer.prototype.writeWINXCOORD32 = function (data) { data = data | 0; this.graphicsJIT(); this.window0Renderer.writeWINXCOORD16(data & 0xFFFF); this.window1Renderer.writeWINXCOORD16(data >>> 16); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0YCOORDBottom8 = function (data) { +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. } -GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0YCOORDTop8 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN0YCOORDTop8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0YCOORDTop8 = function (data) { data = data | 0; this.graphicsJIT(); this.window0Renderer.writeWINYCOORDTop8(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0YCOORD16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN0YCOORD16 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0YCOORD16 = function (data) { data = data | 0; this.graphicsJIT(); this.window0Renderer.writeWINYCOORD16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1YCOORDBottom8 = function (data) { +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. } -GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1YCOORDTop8 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN1YCOORDTop8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1YCOORDTop8 = function (data) { data = data | 0; this.graphicsJIT(); this.window1Renderer.writeWINYCOORDTop8(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1YCOORD16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN1YCOORD16 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1YCOORD16 = function (data) { data = data | 0; this.graphicsJIT(); this.window1Renderer.writeWINYCOORD16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWINYCOORD32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWINYCOORD32 = GameBoyAdvanceGraphicsRenderer.prototype.writeWINYCOORD32 = function (data) { data = data | 0; this.graphicsJIT(); this.window0Renderer.writeWINYCOORD16(data & 0xFFFF); this.window1Renderer.writeWINYCOORD16(data >>> 16); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0IN8 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN0IN8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN0IN8 = function (data) { data = data | 0; //Window 0: this.graphicsJIT(); this.window0Renderer.writeWININ8(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1IN8 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWIN1IN8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWIN1IN8 = function (data) { data = data | 0; //Window 1: this.graphicsJIT(); this.window1Renderer.writeWININ8(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWININ16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWININ16 = GameBoyAdvanceGraphicsRenderer.prototype.writeWININ16 = function (data) { data = data | 0; //Window 0: this.graphicsJIT(); @@ -1201,25 +1142,25 @@ GameBoyAdvanceGraphicsRenderer.prototype.writeWININ16 = function (data) { //Window 1: this.window1Renderer.writeWININ8(data >> 8); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWINOUT8 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWINOUT8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWINOUT8 = function (data) { data = data | 0; this.graphicsJIT(); this.WINOutside = data | 0; this.compositorPreprocess(); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWINOBJIN8 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWINOBJIN8 = GameBoyAdvanceGraphicsRenderer.prototype.writeWINOBJIN8 = function (data) { data = data | 0; this.graphicsJIT(); this.objWindowRenderer.writeWINOBJIN8(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWINOUT16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWINOUT16 = GameBoyAdvanceGraphicsRenderer.prototype.writeWINOUT16 = function (data) { data = data | 0; this.graphicsJIT(); this.WINOutside = data | 0; this.compositorPreprocess(); this.objWindowRenderer.writeWINOBJIN8(data >> 8); } -GameBoyAdvanceGraphicsRenderer.prototype.writeWINCONTROL32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeWINCONTROL32 = GameBoyAdvanceGraphicsRenderer.prototype.writeWINCONTROL32 = function (data) { data = data | 0; //Window 0: this.graphicsJIT(); @@ -1230,109 +1171,98 @@ GameBoyAdvanceGraphicsRenderer.prototype.writeWINCONTROL32 = function (data) { this.compositorPreprocess(); this.objWindowRenderer.writeWINOBJIN8(data >>> 24); } -GameBoyAdvanceGraphicsRenderer.prototype.writeMOSAIC8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeMOSAIC8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeMOSAIC8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.mosaicRenderer.writeMOSAIC8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeMOSAIC8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeMOSAIC8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeMOSAIC8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.mosaicRenderer.writeMOSAIC8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeMOSAIC16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeMOSAIC16 = GameBoyAdvanceGraphicsRenderer.prototype.writeMOSAIC16 = function (data) { data = data | 0; this.graphicsJIT(); this.mosaicRenderer.writeMOSAIC16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBLDCNT8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDCNT8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDCNT8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.colorEffectsRenderer.writeBLDCNT8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBLDCNT8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDCNT8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDCNT8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.colorEffectsRenderer.writeBLDCNT8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBLDCNT16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDCNT16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDCNT16 = function (data) { data = data | 0; this.graphicsJIT(); this.colorEffectsRenderer.writeBLDCNT16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBLDALPHA8_0 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDALPHA8_0 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDALPHA8_0 = function (data) { data = data | 0; this.graphicsJIT(); this.colorEffectsRenderer.writeBLDALPHA8_0(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBLDALPHA8_1 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDALPHA8_1 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDALPHA8_1 = function (data) { data = data | 0; this.graphicsJIT(); this.colorEffectsRenderer.writeBLDALPHA8_1(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBLDALPHA16 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDALPHA16 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDALPHA16 = function (data) { data = data | 0; this.graphicsJIT(); this.colorEffectsRenderer.writeBLDALPHA16(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBLDCNT32 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDCNT32 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDCNT32 = function (data) { data = data | 0; this.graphicsJIT(); this.colorEffectsRenderer.writeBLDCNT32(data | 0); } -GameBoyAdvanceGraphicsRenderer.prototype.writeBLDY8 = function (data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeBLDY8 = GameBoyAdvanceGraphicsRenderer.prototype.writeBLDY8 = function (data) { data = data | 0; this.graphicsJIT(); this.colorEffectsRenderer.writeBLDY8(data | 0); } if (__LITTLE_ENDIAN__) { - GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM8 = function (address, data) { - address = address | 0; - data = data | 0; - if ((address & 0x10000) == 0 || ((address & 0x17FFF) < 0x14000 && (this.displayControl & 0x7) >= 3)) { - this.graphicsJIT(); - address = address & (((address & 0x10000) >> 1) ^ address); - this.VRAM16[(address >> 1) & 0xFFFF] = Math.imul(data & 0xFF, 0x101) | 0; - } - } - GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM16 = function (address, data) { + GameBoyAdvanceGraphicsRendererOffthread.prototype.writeVRAM8 = GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM8 = + GameBoyAdvanceGraphicsRendererOffthread.prototype.writeVRAM16 = GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM16 = function (address, data) { address = address | 0; data = data | 0; this.graphicsJIT(); - address = address & (((address & 0x10000) >> 1) ^ address); - this.VRAM16[(address >> 1) & 0xFFFF] = data & 0xFFFF; + this.VRAM16[address & 0xFFFF] = data & 0xFFFF; } - GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM32 = function (address, data) { + GameBoyAdvanceGraphicsRendererOffthread.prototype.writeVRAM32 = GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM32 = function (address, data) { address = address | 0; data = data | 0; this.graphicsJIT(); - address = address & (((address & 0x10000) >> 1) ^ address); - this.VRAM32[(address >> 2) & 0x7FFF] = data | 0; + this.VRAM32[address & 0x7FFF] = data | 0; } GameBoyAdvanceGraphicsRenderer.prototype.readVRAM16 = function (address) { address = address | 0; - address = address & (((address & 0x10000) >> 1) ^ address); - return this.VRAM16[(address >> 1) & 0xFFFF] | 0; + return this.VRAM16[address & 0xFFFF] | 0; } GameBoyAdvanceGraphicsRenderer.prototype.readVRAM32 = function (address) { address = address | 0; - address = address & (((address & 0x10000) >> 1) ^ address); - return this.VRAM32[(address >> 2) & 0x7FFF] | 0; + return this.VRAM32[address & 0x7FFF] | 0; } - GameBoyAdvanceGraphicsRenderer.prototype.writePalette16 = function (address, data) { + GameBoyAdvanceGraphicsRendererOffthread.prototype.writePalette16 = GameBoyAdvanceGraphicsRenderer.prototype.writePalette16 = function (address, data) { data = data | 0; - address = address >> 1; + address = address | 0; this.graphicsJIT(); - this.paletteRAM16[address & 0x1FF] = data | 0; + this.paletteRAM16[address & 0x1FF] = data & 0xFFFF; data = data & 0x7FFF; this.writePalette256Color(address | 0, data | 0); this.writePalette16Color(address | 0, data | 0); } - GameBoyAdvanceGraphicsRenderer.prototype.writePalette32 = function (address, data) { + GameBoyAdvanceGraphicsRendererOffthread.prototype.writePalette32 = GameBoyAdvanceGraphicsRenderer.prototype.writePalette32 = function (address, data) { data = data | 0; - address = address >> 1; + address = address | 0; this.graphicsJIT(); - this.paletteRAM32[(address >> 1) & 0xFF] = data | 0; + 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); @@ -1342,30 +1272,25 @@ if (__LITTLE_ENDIAN__) { } GameBoyAdvanceGraphicsRenderer.prototype.readPalette16 = function (address) { address = address | 0; - return this.paletteRAM16[(address >> 1) & 0x1FF] | 0; + return this.paletteRAM16[address & 0x1FF] | 0; } GameBoyAdvanceGraphicsRenderer.prototype.readPalette32 = function (address) { address = address | 0; - return this.paletteRAM32[(address >> 2) & 0xFF] | 0; + return this.paletteRAM32[address & 0xFF] | 0; } } else { - GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM8 = function (address, data) { - address &= 0x1FFFE & (((address & 0x10000) >> 1) ^ address); - if (address < 0x10000 || ((address & 0x17FFF) < 0x14000 && (this.displayControl & 0x7) >= 3)) { - this.graphicsJIT(); - this.VRAM[address++] = data & 0xFF; - this.VRAM[address] = data & 0xFF; - } - } - GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM16 = function (address, data) { - address &= 0x1FFFE & (((address & 0x10000) >> 1) ^ address); + 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; } - GameBoyAdvanceGraphicsRenderer.prototype.writeVRAM32 = function (address, data) { - address &= 0x1FFFC & (((address & 0x10000) >> 1) ^ address); + 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; @@ -1373,15 +1298,18 @@ else { this.VRAM[address] = data >>> 24; } GameBoyAdvanceGraphicsRenderer.prototype.readVRAM16 = function (address) { - address &= 0x1FFFE & (((address & 0x10000) >> 1) ^ address); + address <<= 1; + address &= 0x1FFFE; return this.VRAM[address] | (this.VRAM[address + 1] << 8); } GameBoyAdvanceGraphicsRenderer.prototype.readVRAM32 = function (address) { - address &= 0x1FFFC & (((address & 0x10000) >> 1) ^ address); + address <<= 2; + address &= 0x1FFFC; return this.VRAM[address] | (this.VRAM[address + 1] << 8) | (this.VRAM[address + 2] << 16) | (this.VRAM[address + 3] << 24); } - GameBoyAdvanceGraphicsRenderer.prototype.writePalette16 = function (address, data) { + 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; @@ -1389,8 +1317,9 @@ else { this.writePalette256Color(address, data); this.writePalette16Color(address, data); } - GameBoyAdvanceGraphicsRenderer.prototype.writePalette32 = function (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; @@ -1405,41 +1334,45 @@ else { 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; - address = address & (((address & 0x10000) >> 1) ^ address); return this.VRAM[address & 0x1FFFF] | 0; } -GameBoyAdvanceGraphicsRenderer.prototype.writeOAM16 = function (address, data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeOAM16 = GameBoyAdvanceGraphicsRenderer.prototype.writeOAM16 = function (address, data) { address = address | 0; data = data | 0; this.graphicsJIT(); - this.objRenderer.writeOAM16(address >> 1, data | 0); + this.objRenderer.writeOAM16(address & 0x1FF, data & 0xFFFF); } -GameBoyAdvanceGraphicsRenderer.prototype.writeOAM32 = function (address, data) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writeOAM32 = GameBoyAdvanceGraphicsRenderer.prototype.writeOAM32 = function (address, data) { address = address | 0; data = data | 0; this.graphicsJIT(); - this.objRenderer.writeOAM32(address >> 2, data | 0); + 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; } -GameBoyAdvanceGraphicsRenderer.prototype.writePalette256Color = function (address, palette) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writePalette256Color = GameBoyAdvanceGraphicsRenderer.prototype.writePalette256Color = function (address, palette) { address = address | 0; palette = palette | 0; if ((address & 0xFF) == 0) { @@ -1455,7 +1388,7 @@ GameBoyAdvanceGraphicsRenderer.prototype.writePalette256Color = function (addres this.paletteOBJ256[address & 0xFF] = palette | 0; } } -GameBoyAdvanceGraphicsRenderer.prototype.writePalette16Color = function (address, palette) { +GameBoyAdvanceGraphicsRendererOffthread.prototype.writePalette16Color = GameBoyAdvanceGraphicsRenderer.prototype.writePalette16Color = function (address, palette) { address = address | 0; palette = palette | 0; if ((address & 0xF) == 0) { @@ -1471,5 +1404,6 @@ GameBoyAdvanceGraphicsRenderer.prototype.writePalette16Color = function (address } } GameBoyAdvanceGraphicsRenderer.prototype.readPalette8 = function (address) { + address = address | 0; return this.paletteRAM[address & 0x3FF] | 0; -} \ No newline at end of file +} diff --git a/IodineGBA/core/graphics/RendererProxy.js b/IodineGBA/core/graphics/RendererProxy.js index 35ae3b0..e8e3b21 100644 --- a/IodineGBA/core/graphics/RendererProxy.js +++ b/IodineGBA/core/graphics/RendererProxy.js @@ -1,11 +1,11 @@ "use strict"; /* - Copyright (C) 2012-2015 Grant Galitz - + 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) { @@ -17,7 +17,7 @@ GameBoyAdvanceRendererProxy.prototype.initialize = function () { this.IOData16 = getUint16View(this.IOData8); this.IOData32 = getInt32View(this.IOData8); this.gfxState = this.IOCore.gfxState; - this.renderer = new GameBoyAdvanceGraphicsRenderer(this.IOCore.coreExposed, !this.IOCore.BIOSFound || this.IOCore.settings.SKIPBoot); + this.renderer = getGameBoyAdvanceGraphicsRenderer(this.IOCore.coreExposed, !!this.IOCore.SKIPBoot); } GameBoyAdvanceRendererProxy.prototype.incrementScanLineQueue = function () { this.renderer.incrementScanLineQueue(); @@ -1145,65 +1145,91 @@ GameBoyAdvanceRendererProxy.prototype.writeBLDY8 = function (data) { this.IOCore.updateGraphicsClocking(); this.renderer.writeBLDY8(data | 0); } -GameBoyAdvanceRendererProxy.prototype.writeVRAM8 = function (address, data) { - address = address | 0; - data = data | 0; - this.renderer.writeVRAM8(address | 0, 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; - this.renderer.writeVRAM16(address | 0, 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; - this.renderer.writeVRAM32(address | 0, data | 0); + address = address & (((address & 0x10000) >> 1) ^ address); + this.renderer.writeVRAM32(address >> 2, data | 0); } GameBoyAdvanceRendererProxy.prototype.readVRAM16 = function (address) { address = address | 0; - var data = this.renderer.readVRAM16(address | 0) | 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; - var data = this.renderer.readVRAM32(address | 0) | 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 | 0, data | 0); + this.renderer.writePalette16(address >> 1, data | 0); } GameBoyAdvanceRendererProxy.prototype.writePalette32 = function (address, data) { data = data | 0; address = address | 0; - this.renderer.writePalette32(address | 0, data | 0); + this.renderer.writePalette32(address >> 2, data | 0); } GameBoyAdvanceRendererProxy.prototype.readPalette16 = function (address) { address = address | 0; - var data = this.renderer.readPalette16(address | 0) | 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 | 0) | 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 | 0, 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 | 0, data | 0); + this.renderer.writeOAM32(address >> 2, data | 0); } GameBoyAdvanceRendererProxy.prototype.readOAM = function (address) { address = address | 0; @@ -1212,16 +1238,16 @@ GameBoyAdvanceRendererProxy.prototype.readOAM = function (address) { } GameBoyAdvanceRendererProxy.prototype.readOAM16 = function (address) { address = address | 0; - var data = this.renderer.readOAM16(address | 0) | 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 | 0) | 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; -} \ No newline at end of file +} diff --git a/IodineGBA/core/graphics/RendererShim.js b/IodineGBA/core/graphics/RendererShim.js new file mode 100644 index 0000000..4973a5c --- /dev/null +++ b/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.wake(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/IodineGBA/core/graphics/Worker.js b/IodineGBA/core/graphics/Worker.js new file mode 100644 index 0000000..8dce9a0 --- /dev/null +++ b/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/IodineGBA/core/memory/DMA0.js b/IodineGBA/core/memory/DMA0.js index 13c38c2..0c28b88 100644 --- a/IodineGBA/core/memory/DMA0.js +++ b/IodineGBA/core/memory/DMA0.js @@ -37,85 +37,113 @@ GameBoyAdvanceDMA0.prototype.initialize = function () { 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; - this.source = this.source & 0x7FFFF00; + var source = this.source & 0xFFFFF00; data = data & 0xFF; - this.source = this.source | data; + source = source | data; + this.validateDMASource(source | 0); } GameBoyAdvanceDMA0.prototype.writeDMASource8_1 = function (data) { data = data | 0; - this.source = this.source & 0x7FF00FF; + var source = this.source & 0xFFF00FF; data = data & 0xFF; - this.source = this.source | (data << 8); + source = source | (data << 8); + this.validateDMASource(source | 0); } GameBoyAdvanceDMA0.prototype.writeDMASource8_2 = function (data) { data = data | 0; - this.source = this.source & 0x700FFFF; + var source = this.source & 0xF00FFFF; data = data & 0xFF; - this.source = this.source | (data << 16); + source = source | (data << 16); + this.validateDMASource(source | 0); } GameBoyAdvanceDMA0.prototype.writeDMASource8_3 = function (data) { data = data | 0; - this.source = this.source & 0xFFFFFF; - data = data & 0x7; - this.source = this.source | (data << 24); + 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; - this.source = this.source & 0x7FF0000; + var source = this.source & 0xFFF0000; data = data & 0xFFFF; - this.source = this.source | data; + source = source | data; + this.validateDMASource(source | 0); } GameBoyAdvanceDMA0.prototype.writeDMASource16_1 = function (data) { data = data | 0; - this.source = this.source & 0xFFFF; - data = data & 0x7FF; - this.source = this.source | (data << 16); + var source = this.source & 0xFFFF; + data = data & 0xFFF; + source = source | (data << 16); + this.validateDMASource(source | 0); } GameBoyAdvanceDMA0.prototype.writeDMASource32 = function (data) { data = data | 0; - this.source = data & 0x7FFFFFF; + var source = data & 0xFFFFFFF; + this.validateDMASource(source | 0); } GameBoyAdvanceDMA0.prototype.writeDMADestination8_0 = function (data) { data = data | 0; - this.destination = this.destination & 0x7FFFF00; + var destination = this.destination & 0xFFFFF00; data = data & 0xFF; - this.destination = this.destination | data; + destination = destination | data; + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA0.prototype.writeDMADestination8_1 = function (data) { data = data | 0; - this.destination = this.destination & 0x7FF00FF; + var destination = this.destination & 0xFFF00FF; data = data & 0xFF; - this.destination = this.destination | (data << 8); + destination = destination | (data << 8); + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA0.prototype.writeDMADestination8_2 = function (data) { data = data | 0; - this.destination = this.destination & 0x700FFFF; + var destination = this.destination & 0xF00FFFF; data = data & 0xFF; - this.destination = this.destination | (data << 16); + destination = destination | (data << 16); + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA0.prototype.writeDMADestination8_3 = function (data) { data = data | 0; - this.destination = this.destination & 0xFFFFFF; - data = data & 0x7; - this.destination = this.destination | (data << 24); + 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; - this.destination = this.destination & 0x7FF0000; + var destination = this.destination & 0xFFF0000; data = data & 0xFFFF; - this.destination = this.destination | data; + destination = destination | data; + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA0.prototype.writeDMADestination16_1 = function (data) { data = data | 0; - this.destination = this.destination & 0xFFFF; - data = data & 0x7FF; - this.destination = this.destination | (data << 16); + var destination = this.destination & 0xFFFF; + data = data & 0xFFF; + destination = destination | (data << 16); + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA0.prototype.writeDMADestination32 = function (data) { data = data | 0; - this.destination = data & 0x7FFFFFF; + var destination = data & 0xFFFFFFF; + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA0.prototype.writeDMAWordCount8_0 = function (data) { data = data | 0; diff --git a/IodineGBA/core/memory/DMA1.js b/IodineGBA/core/memory/DMA1.js index 397f45f..52f3d83 100644 --- a/IodineGBA/core/memory/DMA1.js +++ b/IodineGBA/core/memory/DMA1.js @@ -39,85 +39,111 @@ GameBoyAdvanceDMA1.prototype.initialize = function () { 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; - this.source = this.source & 0xFFFFF00; + var source = this.source & 0xFFFFF00; data = data & 0xFF; - this.source = this.source | data; + source = source | data; + this.validateDMASource(source | 0); } GameBoyAdvanceDMA1.prototype.writeDMASource8_1 = function (data) { data = data | 0; - this.source = this.source & 0xFFF00FF; + var source = this.source & 0xFFF00FF; data = data & 0xFF; - this.source = this.source | (data << 8); + source = source | (data << 8); + this.validateDMASource(source | 0) } GameBoyAdvanceDMA1.prototype.writeDMASource8_2 = function (data) { data = data | 0; - this.source = this.source & 0xF00FFFF; + var source = this.source & 0xF00FFFF; data = data & 0xFF; - this.source = this.source | (data << 16); + source = ource | (data << 16); + this.validateDMASource(source | 0) } GameBoyAdvanceDMA1.prototype.writeDMASource8_3 = function (data) { data = data | 0; - this.source = this.source & 0xFFFFFF; + var source = this.source & 0xFFFFFF; data = data & 0xF; - this.source = this.source | (data << 24); + source = source | (data << 24); + this.validateDMASource(source | 0) } GameBoyAdvanceDMA1.prototype.writeDMASource16_0 = function (data) { data = data | 0; - this.source = this.source & 0xFFF0000; + var source = this.source & 0xFFF0000; data = data & 0xFFFF; - this.source = this.source | data; + source = source | data; + this.validateDMASource(source | 0) } GameBoyAdvanceDMA1.prototype.writeDMASource16_1 = function (data) { data = data | 0; - this.source = this.source & 0xFFFF; + var source = this.source & 0xFFFF; data = data & 0xFFF; - this.source = this.source | (data << 16); + source = source | (data << 16); + this.validateDMASource(source | 0) } GameBoyAdvanceDMA1.prototype.writeDMASource32 = function (data) { data = data | 0; - this.source = data & 0xFFFFFFF; + var source = data & 0xFFFFFFF; + this.validateDMASource(source | 0) } GameBoyAdvanceDMA1.prototype.writeDMADestination8_0 = function (data) { data = data | 0; - this.destination = this.destination & 0x7FFFF00; + var destination = this.destination & 0xFFFFF00; data = data & 0xFF; - this.destination = this.destination | data; + destination = destination | data; + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA1.prototype.writeDMADestination8_1 = function (data) { data = data | 0; - this.destination = this.destination & 0x7FF00FF; + var destination = this.destination & 0xFFF00FF; data = data & 0xFF; - this.destination = this.destination | (data << 8); + destination = destination | (data << 8); + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA1.prototype.writeDMADestination8_2 = function (data) { data = data | 0; - this.destination = this.destination & 0x700FFFF; + var destination = this.destination & 0xF00FFFF; data = data & 0xFF; - this.destination = this.destination | (data << 16); + destination = destination | (data << 16); + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA1.prototype.writeDMADestination8_3 = function (data) { data = data | 0; - this.destination = this.destination & 0xFFFFFF; - data = data & 0x7; - this.destination = this.destination | (data << 24); + 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; - this.destination = this.destination & 0x7FF0000; + var destination = this.destination & 0xFFF0000; data = data & 0xFFFF; - this.destination = this.destination | data; + destination = destination | data; + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA1.prototype.writeDMADestination16_1 = function (data) { data = data | 0; - this.destination = this.destination & 0xFFFF; - data = data & 0x7FF; - this.destination = this.destination | (data << 16); + var destination = this.destination & 0xFFFF; + data = data & 0xFFF; + destination = destination | (data << 16); + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA1.prototype.writeDMADestination32 = function (data) { data = data | 0; - this.destination = data & 0x7FFFFFF; + var destination = data & 0xFFFFFFF; + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA1.prototype.writeDMAWordCount8_0 = function (data) { data = data | 0; diff --git a/IodineGBA/core/memory/DMA2.js b/IodineGBA/core/memory/DMA2.js index 2277a4f..206c6b5 100644 --- a/IodineGBA/core/memory/DMA2.js +++ b/IodineGBA/core/memory/DMA2.js @@ -39,85 +39,111 @@ GameBoyAdvanceDMA2.prototype.initialize = function () { 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; - this.source = this.source & 0xFFFFF00; + var source = this.source & 0xFFFFF00; data = data & 0xFF; - this.source = this.source | data; + source = source | data; + this.validateDMASource(source | 0); } GameBoyAdvanceDMA2.prototype.writeDMASource8_1 = function (data) { data = data | 0; - this.source = this.source & 0xFFF00FF; + var source = this.source & 0xFFF00FF; data = data & 0xFF; - this.source = this.source | (data << 8); + source = source | (data << 8); + this.validateDMASource(source | 0) } GameBoyAdvanceDMA2.prototype.writeDMASource8_2 = function (data) { data = data | 0; - this.source = this.source & 0xF00FFFF; + var source = this.source & 0xF00FFFF; data = data & 0xFF; - this.source = this.source | (data << 16); + source = ource | (data << 16); + this.validateDMASource(source | 0) } GameBoyAdvanceDMA2.prototype.writeDMASource8_3 = function (data) { data = data | 0; - this.source = this.source & 0xFFFFFF; + var source = this.source & 0xFFFFFF; data = data & 0xF; - this.source = this.source | (data << 24); + source = source | (data << 24); + this.validateDMASource(source | 0) } GameBoyAdvanceDMA2.prototype.writeDMASource16_0 = function (data) { data = data | 0; - this.source = this.source & 0xFFF0000; + var source = this.source & 0xFFF0000; data = data & 0xFFFF; - this.source = this.source | data; + source = source | data; + this.validateDMASource(source | 0) } GameBoyAdvanceDMA2.prototype.writeDMASource16_1 = function (data) { data = data | 0; - this.source = this.source & 0xFFFF; + var source = this.source & 0xFFFF; data = data & 0xFFF; - this.source = this.source | (data << 16); + source = source | (data << 16); + this.validateDMASource(source | 0) } GameBoyAdvanceDMA2.prototype.writeDMASource32 = function (data) { data = data | 0; - this.source = data & 0xFFFFFFF; + var source = data & 0xFFFFFFF; + this.validateDMASource(source | 0) } GameBoyAdvanceDMA2.prototype.writeDMADestination8_0 = function (data) { data = data | 0; - this.destination = this.destination & 0x7FFFF00; + var destination = this.destination & 0xFFFFF00; data = data & 0xFF; - this.destination = this.destination | data; + destination = destination | data; + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA2.prototype.writeDMADestination8_1 = function (data) { data = data | 0; - this.destination = this.destination & 0x7FF00FF; + var destination = this.destination & 0xFFF00FF; data = data & 0xFF; - this.destination = this.destination | (data << 8); + destination = destination | (data << 8); + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA2.prototype.writeDMADestination8_2 = function (data) { data = data | 0; - this.destination = this.destination & 0x700FFFF; + var destination = this.destination & 0xF00FFFF; data = data & 0xFF; - this.destination = this.destination | (data << 16); + destination = destination | (data << 16); + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA2.prototype.writeDMADestination8_3 = function (data) { data = data | 0; - this.destination = this.destination & 0xFFFFFF; - data = data & 0x7; - this.destination = this.destination | (data << 24); + 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; - this.destination = this.destination & 0x7FF0000; + var destination = this.destination & 0xFFF0000; data = data & 0xFFFF; - this.destination = this.destination | data; + destination = destination | data; + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA2.prototype.writeDMADestination16_1 = function (data) { data = data | 0; - this.destination = this.destination & 0xFFFF; - data = data & 0x7FF; - this.destination = this.destination | (data << 16); + var destination = this.destination & 0xFFFF; + data = data & 0xFFF; + destination = destination | (data << 16); + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA2.prototype.writeDMADestination32 = function (data) { data = data | 0; - this.destination = data & 0x7FFFFFF; + var destination = data & 0xFFFFFFF; + this.validateDMADestination(destination | 0); } GameBoyAdvanceDMA2.prototype.writeDMAWordCount8_0 = function (data) { data = data | 0; diff --git a/IodineGBA/core/memory/DMA3.js b/IodineGBA/core/memory/DMA3.js index a74128d..aea6759 100644 --- a/IodineGBA/core/memory/DMA3.js +++ b/IodineGBA/core/memory/DMA3.js @@ -39,45 +39,58 @@ GameBoyAdvanceDMA3.prototype.initialize = function () { 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; - this.source = this.source & 0xFFFFF00; + var source = this.source & 0xFFFFF00; data = data & 0xFF; - this.source = this.source | data; + source = source | data; + this.validateDMASource(source | 0); } GameBoyAdvanceDMA3.prototype.writeDMASource8_1 = function (data) { data = data | 0; - this.source = this.source & 0xFFF00FF; + var source = this.source & 0xFFF00FF; data = data & 0xFF; - this.source = this.source | (data << 8); + source = source | (data << 8); + this.validateDMASource(source | 0); } GameBoyAdvanceDMA3.prototype.writeDMASource8_2 = function (data) { data = data | 0; - this.source = this.source & 0xF00FFFF; + var source = this.source & 0xF00FFFF; data = data & 0xFF; - this.source = this.source | (data << 16); + source = source | (data << 16); + this.validateDMASource(source | 0); } GameBoyAdvanceDMA3.prototype.writeDMASource8_3 = function (data) { data = data | 0; - this.source = this.source & 0xFFFFFF; + var source = this.source & 0xFFFFFF; data = data & 0xF; - this.source = this.source | (data << 24); + source = source | (data << 24); + this.validateDMASource(source | 0); } GameBoyAdvanceDMA3.prototype.writeDMASource16_0 = function (data) { data = data | 0; - this.source = this.source & 0xFFF0000; + var source = this.source & 0xFFF0000; data = data & 0xFFFF; - this.source = this.source | data; + source = source | data; + this.validateDMASource(source | 0); } GameBoyAdvanceDMA3.prototype.writeDMASource16_1 = function (data) { data = data | 0; - this.source = this.source & 0xFFFF; + var source = this.source & 0xFFFF; data = data & 0xFFF; - this.source = this.source | (data << 16); + source = source | (data << 16); + this.validateDMASource(source | 0); } GameBoyAdvanceDMA3.prototype.writeDMASource32 = function (data) { data = data | 0; - this.source = data & 0xFFFFFFF; + var source = data & 0xFFFFFFF; + this.validateDMASource(source | 0); } GameBoyAdvanceDMA3.prototype.writeDMADestination8_0 = function (data) { data = data | 0; diff --git a/IodineGBA/core/sound/Channel1.js b/IodineGBA/core/sound/Channel1.js index 0958afc..0099bf9 100644 --- a/IodineGBA/core/sound/Channel1.js +++ b/IodineGBA/core/sound/Channel1.js @@ -11,18 +11,14 @@ function GameBoyAdvanceChannel1Synth(sound) { this.sound = sound; this.currentSampleLeft = 0; - this.currentSampleLeftSecondary = 0; - this.currentSampleLeftTrimary = 0; this.currentSampleRight = 0; - this.currentSampleRightSecondary = 0; - this.currentSampleRightTrimary = 0; this.SweepFault = false; this.lastTimeSweep = 0; this.timeSweep = 0; this.frequencySweepDivider = 0; this.decreaseSweep = false; this.nr11 = 0; - this.CachedDuty = this.dutyLookup[0]; + this.CachedDuty = 0xF0000000; this.totalLength = 0x40; this.nr12 = 0; this.envelopeVolume = 0; @@ -32,19 +28,15 @@ function GameBoyAdvanceChannel1Synth(sound) { this.consecutive = true; this.ShadowFrequency = 0x8000; this.canPlay = false; - this.Enabled = 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.dutyLookup = [ - [false, false, false, false, false, false, false, true], - [true, false, false, false, false, false, false, true], - [true, false, false, false, false, true, true, true], - [false, true, true, true, true, true, true, false] -]; GameBoyAdvanceChannel1Synth.prototype.disabled = function () { //Clear NR10: this.nr10 = 0; @@ -55,7 +47,7 @@ GameBoyAdvanceChannel1Synth.prototype.disabled = function () { this.decreaseSweep = false; //Clear NR11: this.nr11 = 0; - this.CachedDuty = this.dutyLookup[0]; + this.CachedDuty = 0xF0000000; this.totalLength = 0x40; //Clear NR12: this.nr12 = 0; @@ -68,7 +60,7 @@ GameBoyAdvanceChannel1Synth.prototype.disabled = function () { this.consecutive = true; this.ShadowFrequency = 0x8000; this.canPlay = false; - this.Enabled = false; + this.Enabled = 0; this.envelopeSweeps = 0; this.envelopeSweepsLast = -1; this.FrequencyCounter = 0; @@ -85,37 +77,28 @@ GameBoyAdvanceChannel1Synth.prototype.clockAudioLength = function () { } } GameBoyAdvanceChannel1Synth.prototype.enableCheck = function () { - this.Enabled = ((this.consecutive || (this.totalLength | 0) > 0) && !this.SweepFault && this.canPlay); + 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 () { - this.currentSampleLeft = (this.sound.leftChannel1) ? (this.envelopeVolume | 0) : 0; - this.currentSampleRight = (this.sound.rightChannel1) ? (this.envelopeVolume | 0) : 0; - this.outputLevelSecondaryCache(); + 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.outputLevelSecondaryCache = function () { - if (this.Enabled) { - this.currentSampleLeftSecondary = this.currentSampleLeft | 0; - this.currentSampleRightSecondary = this.currentSampleRight | 0; - } - else { - this.currentSampleLeftSecondary = 0; - this.currentSampleRightSecondary = 0; - } - this.outputLevelTrimaryCache(); -} -GameBoyAdvanceChannel1Synth.prototype.outputLevelTrimaryCache = function () { - if (this.CachedDuty[this.DutyTracker | 0]) { - this.currentSampleLeftTrimary = this.currentSampleLeftSecondary | 0; - this.currentSampleRightTrimary = this.currentSampleRightSecondary | 0; - } - else { - this.currentSampleLeftTrimary = 0; - this.currentSampleRightTrimary = 0; - } +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: @@ -213,7 +196,7 @@ GameBoyAdvanceChannel1Synth.prototype.clockAudioEnvelope = function () { GameBoyAdvanceChannel1Synth.prototype.computeAudioChannel = function () { if ((this.FrequencyCounter | 0) == 0) { this.FrequencyCounter = this.FrequencyTracker | 0; - this.DutyTracker = ((this.DutyTracker | 0) + 1) & 0x7; + this.DutyTracker = ((this.DutyTracker | 0) + 4) & 0x1C; } } GameBoyAdvanceChannel1Synth.prototype.readSOUND1CNT8_0 = function () { @@ -241,7 +224,19 @@ GameBoyAdvanceChannel1Synth.prototype.readSOUND1CNT8_2 = function () { GameBoyAdvanceChannel1Synth.prototype.writeSOUND1CNT8_2 = function (data) { data = data | 0; //NR11: - this.CachedDuty = this.dutyLookup[(data >> 6) & 0x3]; + 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(); @@ -260,10 +255,10 @@ GameBoyAdvanceChannel1Synth.prototype.writeSOUND1CNT8_3 = function (data) { GameBoyAdvanceChannel1Synth.prototype.writeSOUND1CNT_X0 = function (data) { data = data | 0; //NR13: - this.frequency = (this.frequency & 0x700) | data; + this.frequency = (this.frequency & 0x700) | (data & 0xFF); this.FrequencyTracker = (0x800 - (this.frequency | 0)) << 4; } -GameBoyAdvanceChannel1Synth.prototype.readSOUND1CNT_X = function () { +GameBoyAdvanceChannel1Synth.prototype.readSOUND1CNTX8 = function () { //NR14: return this.nr14 | 0; } @@ -273,7 +268,7 @@ GameBoyAdvanceChannel1Synth.prototype.writeSOUND1CNT_X1 = function (data) { this.consecutive = ((data & 0x40) == 0); this.frequency = ((data & 0x7) << 8) | (this.frequency & 0xFF); this.FrequencyTracker = (0x800 - (this.frequency | 0)) << 4; - if (data > 0x7F) { + if ((data & 0x80) != 0) { //Reload nr10: this.timeSweep = this.lastTimeSweep | 0; this.Swept = false; @@ -299,5 +294,5 @@ GameBoyAdvanceChannel1Synth.prototype.writeSOUND1CNT_X1 = function (data) { this.audioSweepPerformDummy(); } this.enableCheck(); - this.nr14 = data | 0; + this.nr14 = data & 0xFF; } \ No newline at end of file diff --git a/IodineGBA/core/sound/Channel2.js b/IodineGBA/core/sound/Channel2.js index 6bb649b..43d4748 100644 --- a/IodineGBA/core/sound/Channel2.js +++ b/IodineGBA/core/sound/Channel2.js @@ -11,12 +11,8 @@ function GameBoyAdvanceChannel2Synth(sound) { this.sound = sound; this.currentSampleLeft = 0; - this.currentSampleLeftSecondary = 0; - this.currentSampleLeftTrimary = 0; this.currentSampleRight = 0; - this.currentSampleRightSecondary = 0; - this.currentSampleRightTrimary = 0; - this.CachedDuty = this.dutyLookup[0]; + this.CachedDuty = 0xF0000000; this.totalLength = 0x40; this.envelopeVolume = 0; this.frequency = 0; @@ -24,26 +20,22 @@ function GameBoyAdvanceChannel2Synth(sound) { this.consecutive = true; this.ShadowFrequency = 0x8000; this.canPlay = false; - this.Enabled = 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.dutyLookup = [ - [false, false, false, false, false, false, false, true], - [true, false, false, false, false, false, false, true], - [true, false, false, false, false, true, true, true], - [false, true, true, true, true, true, true, false] -]; GameBoyAdvanceChannel2Synth.prototype.disabled = function () { //Clear NR21: this.nr21 = 0; - this.CachedDuty = this.dutyLookup[0]; + this.CachedDuty = 0xF0000000; this.totalLength = 0x40; //Clear NR22: this.nr22 = 0; @@ -56,7 +48,7 @@ GameBoyAdvanceChannel2Synth.prototype.disabled = function () { this.nr24 = 0; this.consecutive = true; this.canPlay = false; - this.Enabled = false; + this.Enabled = 0; this.envelopeSweeps = 0; this.envelopeSweepsLast = -1; this.FrequencyCounter = 0; @@ -100,41 +92,32 @@ GameBoyAdvanceChannel2Synth.prototype.clockAudioEnvelope = function () { GameBoyAdvanceChannel2Synth.prototype.computeAudioChannel = function () { if ((this.FrequencyCounter | 0) == 0) { this.FrequencyCounter = this.FrequencyTracker | 0; - this.DutyTracker = ((this.DutyTracker | 0) + 1) & 0x7; + this.DutyTracker = ((this.DutyTracker | 0) + 4) & 0x1C; } } GameBoyAdvanceChannel2Synth.prototype.enableCheck = function () { - this.Enabled = ((this.consecutive || (this.totalLength | 0) > 0) && this.canPlay); + 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 () { - this.currentSampleLeft = (this.sound.leftChannel2) ? (this.envelopeVolume | 0) : 0; - this.currentSampleRight = (this.sound.rightChannel2) ? (this.envelopeVolume | 0) : 0; - this.outputLevelSecondaryCache(); + 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.outputLevelSecondaryCache = function () { - if (this.Enabled) { - this.currentSampleLeftSecondary = this.currentSampleLeft | 0; - this.currentSampleRightSecondary = this.currentSampleRight | 0; - } - else { - this.currentSampleLeftSecondary = 0; - this.currentSampleRightSecondary = 0; - } - this.outputLevelTrimaryCache(); -} -GameBoyAdvanceChannel2Synth.prototype.outputLevelTrimaryCache = function () { - if (this.CachedDuty[this.DutyTracker | 0]) { - this.currentSampleLeftTrimary = this.currentSampleLeftSecondary | 0; - this.currentSampleRightTrimary = this.currentSampleRightSecondary | 0; - } - else { - this.currentSampleLeftTrimary = 0; - this.currentSampleRightTrimary = 0; - } +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: @@ -143,9 +126,21 @@ GameBoyAdvanceChannel2Synth.prototype.readSOUND2CNT_L0 = function () { GameBoyAdvanceChannel2Synth.prototype.writeSOUND2CNT_L0 = function (data) { data = data | 0; //NR21: - this.CachedDuty = this.dutyLookup[data >> 6]; + 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 | 0; + this.nr21 = data & 0xFF; this.enableCheck(); } GameBoyAdvanceChannel2Synth.prototype.readSOUND2CNT_L1 = function () { @@ -156,13 +151,13 @@ GameBoyAdvanceChannel2Synth.prototype.writeSOUND2CNT_L1 = function (data) { data = data | 0; //NR22: this.envelopeType = ((data & 0x08) != 0); - this.nr22 = data | 0; + this.nr22 = data & 0xFF; this.volumeEnableCheck(); } GameBoyAdvanceChannel2Synth.prototype.writeSOUND2CNT_H0 = function (data) { data = data | 0; //NR23: - this.frequency = (this.frequency & 0x700) | data; + this.frequency = (this.frequency & 0x700) | (data & 0xFF); this.FrequencyTracker = (0x800 - (this.frequency | 0)) << 4; } GameBoyAdvanceChannel2Synth.prototype.readSOUND2CNT_H = function () { @@ -172,7 +167,7 @@ GameBoyAdvanceChannel2Synth.prototype.readSOUND2CNT_H = function () { GameBoyAdvanceChannel2Synth.prototype.writeSOUND2CNT_H1 = function (data) { data = data | 0; //NR24: - if (data > 0x7F) { + if ((data & 0x80) != 0) { //Reload nr22: this.envelopeVolume = this.nr22 >> 4; this.envelopeSweepsLast = ((this.nr22 & 0x7) - 1) | 0; @@ -186,6 +181,6 @@ GameBoyAdvanceChannel2Synth.prototype.writeSOUND2CNT_H1 = function (data) { this.consecutive = ((data & 0x40) == 0x0); this.frequency = ((data & 0x7) << 8) | (this.frequency & 0xFF); this.FrequencyTracker = (0x800 - (this.frequency | 0)) << 4; - this.nr24 = data | 0; + this.nr24 = data & 0xFF; this.enableCheck(); } \ No newline at end of file diff --git a/IodineGBA/core/sound/Channel3.js b/IodineGBA/core/sound/Channel3.js index 28ddf3e..24482d9 100644 --- a/IodineGBA/core/sound/Channel3.js +++ b/IodineGBA/core/sound/Channel3.js @@ -11,9 +11,7 @@ function GameBoyAdvanceChannel3Synth(sound) { this.sound = sound; this.currentSampleLeft = 0; - this.currentSampleLeftSecondary = 0; this.currentSampleRight = 0; - this.currentSampleRightSecondary = 0; this.lastSampleLookup = 0; this.canPlay = false; this.WAVERAMBankSpecified = 0; @@ -24,7 +22,9 @@ function GameBoyAdvanceChannel3Synth(sound) { this.frequency = 0; this.FrequencyPeriod = 0x4000; this.consecutive = true; - this.Enabled = false; + this.Enabled = 0; + this.leftEnable = 0; + this.rightEnable = 0; this.nr30 = 0; this.nr31 = 0; this.nr32 = 0; @@ -58,7 +58,7 @@ GameBoyAdvanceChannel3Synth.prototype.disabled = function () { //Clear NR34: this.nr34 = 0; this.consecutive = true; - this.Enabled = false; + this.Enabled = 0; this.counter = 0; } if (typeof Math.imul == "function") { @@ -86,19 +86,15 @@ else { } } GameBoyAdvanceChannel3Synth.prototype.outputLevelCache = function () { - this.currentSampleLeft = (this.sound.leftChannel3) ? (this.cachedSample | 0) : 0; - this.currentSampleRight = (this.sound.rightChannel3) ? (this.cachedSample | 0) : 0; - this.outputLevelSecondaryCache(); + var cachedSample = this.cachedSample & this.Enabled; + this.currentSampleLeft = this.leftEnable & cachedSample; + this.currentSampleRight = this.rightEnable & cachedSample; } -GameBoyAdvanceChannel3Synth.prototype.outputLevelSecondaryCache = function () { - if (this.Enabled) { - this.currentSampleLeftSecondary = this.currentSampleLeft | 0; - this.currentSampleRightSecondary = this.currentSampleRight | 0; - } - else { - this.currentSampleLeftSecondary = 0; - this.currentSampleRightSecondary = 0; - } +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; @@ -108,9 +104,6 @@ if (__LITTLE_ENDIAN__) { GameBoyAdvanceChannel3Synth.prototype.writeWAVE8 = function (address, data) { address = address | 0; data = data | 0; - if (this.canPlay) { - this.sound.audioJIT(); - } address = ((address | 0) + (this.WAVERAMBankAccessed >> 1)) | 0; this.WAVERAM8[address | 0] = data & 0xFF; var temp = ((data >> 4) & 0xF); @@ -120,9 +113,6 @@ if (__LITTLE_ENDIAN__) { GameBoyAdvanceChannel3Synth.prototype.writeWAVE16 = function (address, data) { address = address | 0; data = data | 0; - if (this.canPlay) { - this.sound.audioJIT(); - } address = ((address | 0) + (this.WAVERAMBankAccessed >> 2)) | 0; this.WAVERAM16[address | 0] = data & 0xFFFF; var temp = ((data >> 4) & 0xF); @@ -134,9 +124,6 @@ if (__LITTLE_ENDIAN__) { GameBoyAdvanceChannel3Synth.prototype.writeWAVE32 = function (address, data) { address = address | 0; data = data | 0; - if (this.canPlay) { - this.sound.audioJIT(); - } address = ((address | 0) + (this.WAVERAMBankAccessed >> 3)) | 0; this.WAVERAM32[address | 0] = data | 0; var temp = (data >> 4) & 0xF; @@ -162,9 +149,6 @@ if (__LITTLE_ENDIAN__) { } else { GameBoyAdvanceChannel3Synth.prototype.writeWAVE8 = function (address, data) { - if (this.canPlay) { - this.sound.audioJIT(); - } address += this.WAVERAMBankAccessed >> 1; this.WAVERAM8[address] = data & 0xFF; address <<= 1; @@ -172,9 +156,6 @@ else { this.PCM[address | 1] = data & 0xF; } GameBoyAdvanceChannel3Synth.prototype.writeWAVE16 = function (address, data) { - if (this.canPlay) { - this.sound.audioJIT(); - } address += this.WAVERAMBankAccessed >> 2; address <<= 1; this.WAVERAM8[address] = data & 0xFF; @@ -186,9 +167,6 @@ else { this.PCM[address | 3] = (data >> 8) & 0xF; } GameBoyAdvanceChannel3Synth.prototype.writeWAVE32 = function (address, data) { - if (this.canPlay) { - this.sound.audioJIT(); - } address += this.WAVERAMBankAccessed >> 3; address <<= 2; this.WAVERAM8[address] = data & 0xFF; @@ -216,7 +194,12 @@ else { } } GameBoyAdvanceChannel3Synth.prototype.enableCheck = function () { - this.Enabled = (/*this.canPlay && */(this.consecutive || (this.totalLength | 0) > 0)); + 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) { @@ -244,22 +227,22 @@ GameBoyAdvanceChannel3Synth.prototype.readSOUND3CNT_L = function () { GameBoyAdvanceChannel3Synth.prototype.writeSOUND3CNT_L = function (data) { data = data | 0; //NR30: - if (!this.canPlay && (data | 0) >= 0x80) { + if (!this.canPlay && (data & 0x80) != 0) { this.lastSampleLookup = 0; } - this.canPlay = (data > 0x7F); + 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 | 0) > 0x7F && !this.consecutive) { + if (this.canPlay && (this.nr30 & 0x80) != 0 && !this.consecutive) { this.sound.setNR52(0x4); } - this.nr30 = data | 0; + this.nr30 = data & 0xFF; } GameBoyAdvanceChannel3Synth.prototype.writeSOUND3CNT_H0 = function (data) { data = data | 0; //NR31: - this.totalLength = (0x100 - (data | 0)) | 0; + this.totalLength = (0x100 - (data & 0xFF)) | 0; this.enableCheck(); } GameBoyAdvanceChannel3Synth.prototype.readSOUND3CNT_H = function () { @@ -269,6 +252,7 @@ GameBoyAdvanceChannel3Synth.prototype.readSOUND3CNT_H = function () { GameBoyAdvanceChannel3Synth.prototype.writeSOUND3CNT_H1 = function (data) { data = data | 0; //NR32: + data = data & 0xFF; switch (data >> 5) { case 0: this.patternType = 4; @@ -290,7 +274,7 @@ GameBoyAdvanceChannel3Synth.prototype.writeSOUND3CNT_H1 = function (data) { GameBoyAdvanceChannel3Synth.prototype.writeSOUND3CNT_X0 = function (data) { data = data | 0; //NR33: - this.frequency = (this.frequency & 0x700) | data; + this.frequency = (this.frequency & 0x700) | (data & 0xFF); this.FrequencyPeriod = (0x800 - (this.frequency | 0)) << 3; } GameBoyAdvanceChannel3Synth.prototype.readSOUND3CNT_X = function () { @@ -300,7 +284,7 @@ GameBoyAdvanceChannel3Synth.prototype.readSOUND3CNT_X = function () { GameBoyAdvanceChannel3Synth.prototype.writeSOUND3CNT_X1 = function (data) { data = data | 0; //NR34: - if ((data | 0) > 0x7F) { + if ((data & 0x80) != 0) { if ((this.totalLength | 0) == 0) { this.totalLength = 0x100; } @@ -313,5 +297,5 @@ GameBoyAdvanceChannel3Synth.prototype.writeSOUND3CNT_X1 = function (data) { this.frequency = ((data & 0x7) << 8) | (this.frequency & 0xFF); this.FrequencyPeriod = (0x800 - (this.frequency | 0)) << 3; this.enableCheck(); - this.nr34 = data | 0; + this.nr34 = data & 0xFF; } \ No newline at end of file diff --git a/IodineGBA/core/sound/Channel4.js b/IodineGBA/core/sound/Channel4.js index 469140f..410ee49 100644 --- a/IodineGBA/core/sound/Channel4.js +++ b/IodineGBA/core/sound/Channel4.js @@ -11,9 +11,7 @@ function GameBoyAdvanceChannel4Synth(sound) { this.sound = sound; this.currentSampleLeft = 0; - this.currentSampleLeftSecondary = 0; this.currentSampleRight = 0; - this.currentSampleRightSecondary = 0; this.totalLength = 0x40; this.envelopeVolume = 0; this.FrequencyPeriod = 32; @@ -25,8 +23,10 @@ function GameBoyAdvanceChannel4Synth(sound) { this.envelopeSweeps = 0; this.envelopeSweepsLast = -1; this.canPlay = false; - this.Enabled = false; + this.Enabled = 0; this.counter = 0; + this.leftEnable = 0; + this.rightEnable = 0; this.nr42 = 0; this.nr43 = 0; this.nr44 = 0; @@ -111,7 +111,7 @@ GameBoyAdvanceChannel4Synth.prototype.disabled = function () { this.envelopeSweeps = 0; this.envelopeSweepsLast = -1; this.canPlay = false; - this.Enabled = false; + this.Enabled = 0; this.counter = 0; } GameBoyAdvanceChannel4Synth.prototype.clockAudioLength = function () { @@ -158,26 +158,27 @@ GameBoyAdvanceChannel4Synth.prototype.computeAudioChannel = function () { } } GameBoyAdvanceChannel4Synth.prototype.enableCheck = function () { - this.Enabled = ((this.consecutive || (this.totalLength | 0) > 0) && this.canPlay); + 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 () { - this.currentSampleLeft = (this.sound.leftChannel4) ? (this.cachedSample | 0) : 0; - this.currentSampleRight = (this.sound.rightChannel4) ? (this.cachedSample | 0) : 0; - this.outputLevelSecondaryCache(); + var cachedSample = this.cachedSample & this.Enabled; + this.currentSampleLeft = this.leftEnable & cachedSample; + this.currentSampleRight = this.rightEnable & cachedSample; } -GameBoyAdvanceChannel4Synth.prototype.outputLevelSecondaryCache = function () { - if (this.Enabled) { - this.currentSampleLeftSecondary = this.currentSampleLeft | 0; - this.currentSampleRightSecondary = this.currentSampleRight | 0; - } - else { - this.currentSampleLeftSecondary = 0; - this.currentSampleRightSecondary = 0; - } +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; @@ -193,7 +194,7 @@ GameBoyAdvanceChannel4Synth.prototype.writeSOUND4CNT_L1 = function (data) { data = data | 0; //NR42: this.envelopeType = ((data & 0x08) != 0); - this.nr42 = data | 0; + this.nr42 = data & 0xFF; this.volumeEnableCheck(); } GameBoyAdvanceChannel4Synth.prototype.readSOUND4CNT_L = function () { @@ -203,7 +204,7 @@ GameBoyAdvanceChannel4Synth.prototype.readSOUND4CNT_L = function () { GameBoyAdvanceChannel4Synth.prototype.writeSOUND4CNT_H0 = function (data) { data = data | 0; //NR43: - this.FrequencyPeriod = Math.max((data & 0x7) << 4, 8) << (((data >> 4) + 2) | 0); + 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; @@ -212,7 +213,7 @@ GameBoyAdvanceChannel4Synth.prototype.writeSOUND4CNT_H0 = function (data) { this.currentVolume = this.envelopeVolume << (this.VolumeShifter | 0); this.noiseSampleTable = ((bitWidth | 0) == 0x8) ? this.LSFR7Table : this.LSFR15Table; } - this.nr43 = data | 0; + this.nr43 = data & 0xFF; } GameBoyAdvanceChannel4Synth.prototype.readSOUND4CNT_H0 = function () { //NR43: @@ -221,9 +222,9 @@ GameBoyAdvanceChannel4Synth.prototype.readSOUND4CNT_H0 = function () { GameBoyAdvanceChannel4Synth.prototype.writeSOUND4CNT_H1 = function (data) { data = data | 0; //NR44: - this.nr44 = data | 0; + this.nr44 = data & 0xFF; this.consecutive = ((data & 0x40) == 0x0); - if ((data | 0) > 0x7F) { + if ((data & 0x80) != 0) { this.envelopeVolume = this.nr42 >> 4; this.currentVolume = this.envelopeVolume << (this.VolumeShifter | 0); this.envelopeSweepsLast = ((this.nr42 & 0x7) - 1) | 0; diff --git a/IodineGBA/includes/TypedArrayShim.js b/IodineGBA/includes/TypedArrayShim.js index fda8aa2..5926584 100644 --- a/IodineGBA/includes/TypedArrayShim.js +++ b/IodineGBA/includes/TypedArrayShim.js @@ -1,11 +1,11 @@ "use strict"; /* - Copyright (C) 2012-2014 Grant Galitz - + 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) { @@ -24,6 +24,23 @@ function getUint8Array(size_t) { 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); @@ -66,6 +83,31 @@ function getInt32View(typed_array) { } 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) { @@ -86,6 +128,15 @@ function getUint32Array(size_t) { 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); @@ -94,6 +145,15 @@ function getFloat32Array(size_t) { 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) { @@ -113,3 +173,10 @@ var __LITTLE_ENDIAN__ = (function () { } 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.wake = Atomics.futexWake; + } +} diff --git a/README.md b/README.md deleted file mode 100644 index 9fbc49f..0000000 --- a/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# GBA Games Page: - -https://jsemu2.github.io/gba - -# GBA Emulator Core Used: - -https://github.com/taisel/IodineGBA diff --git a/launcher.html b/launcher.html index ac3c7c9..3027fe3 100644 --- a/launcher.html +++ b/launcher.html @@ -3,65 +3,229 @@