mirror of
https://github.com/DPS2004/gba.git
synced 2025-01-22 12:32:54 -03:00
update
This commit is contained in:
parent
7e8457058a
commit
fa961aa319
56 changed files with 8235 additions and 3531 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[submodule "user_scripts/XAudioJS"]
|
||||
path = user_scripts/XAudioJS
|
||||
url = https://github.com/taisel/XAudioJS.git
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
/*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);
|
||||
}*/
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"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:
|
||||
|
||||
|
@ -10,119 +10,158 @@
|
|||
*/
|
||||
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.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 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;
|
||||
}
|
||||
this.IOCore.saves.importSave(convertedSave, saveType | 0);
|
||||
//We used to save this code wrong, fix the error in old saves:
|
||||
if ((saveType.length | 0) != 1) {
|
||||
//0 is fallthrough "UNKNOWN" aka autodetect type:
|
||||
parentObj.IOCore.saves.importSave(convertedSave, 0);
|
||||
}
|
||||
else {
|
||||
parentObj.IOCore.saves.importSave(convertedSave, saveType[0] & 0xFF);
|
||||
}
|
||||
parentObj.emulatorStatus = parentObj.emulatorStatus | 0x4;
|
||||
}
|
||||
}
|
||||
}, function (){parentObj.emulatorStatus = parentObj.emulatorStatus | 0x4;});
|
||||
}, function (){parentObj.emulatorStatus = parentObj.emulatorStatus | 0x4;});
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.emulatorStatus = this.emulatorStatus | 0x4;
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.exportSave = function () {
|
||||
if (this.saveExportHandler && !this.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 () {
|
||||
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.dynamicSpeedRefresh && this.settings.dynamicSpeed) {
|
||||
if (((this.audioBufferDynamicContainAmount | 0) - (remainingAmount | 0)) > 0) {
|
||||
var speed = this.getSpeed();
|
||||
speed = Math.max(speed - 0.1, 0.1);
|
||||
this.setSpeed(speed);
|
||||
var speed = +this.getSpeed();
|
||||
speed = Math.max((+speed) - 0.1, 0.003);
|
||||
this.processNewSpeed(+speed);
|
||||
}
|
||||
}
|
||||
this.CPUCyclesTotal = Math.min(((this.CPUCyclesTotal | 0) + ((underrunAmount >> 1) * (this.audioResamplerFirstPassFactor | 0))) | 0, (+this.clocksPerMilliSecond) << 5) | 0;
|
||||
}
|
||||
else {
|
||||
this.dynamicSpeedCounter = this.settings.metricCollectionMinimum | 0;
|
||||
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;
|
||||
}
|
||||
this.CPUCyclesTotal = Math.min(((this.CPUCyclesTotal | 0) + ((underrunAmount >> 1) * this.audioResamplerFirstPassFactor)) | 0, this.CPUCyclesTotal << 1, 0x7FFFFFFF) | 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;
|
||||
}
|
||||
}
|
||||
this.dynamicSpeedCounter = ((this.dynamicSpeedCounter | 0) + 1) | 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.enableDynamicSpeed = function () {
|
||||
this.settings.dynamicSpeed = true;
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.disableDynamicSpeed = function () {
|
||||
this.settings.dynamicSpeed = false;
|
||||
this.setSpeed(1);
|
||||
GameBoyAdvanceEmulator.prototype.toggleOffthreadGraphics = function (offthreadGfxEnabled) {
|
||||
this.settings.offthreadGfxEnabled = !!offthreadGfxEnabled;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"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:
|
||||
|
||||
|
@ -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();
|
|
@ -1,6 +1,6 @@
|
|||
"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:
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
|||
|
||||
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,8 +43,12 @@ 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();
|
||||
if ((this.memory.initialize() | 0) == 1) {
|
||||
//BIOS loaded in OK, so initialize the rest:
|
||||
this.dma.initialize();
|
||||
this.dmaChannel0.initialize();
|
||||
this.dmaChannel1.initialize();
|
||||
|
@ -60,6 +65,11 @@ function GameBoyAdvanceIO(settings, coreExposed, BIOS, ROM) {
|
|||
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:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"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:
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
300
IodineGBA/core/Worker.js
Normal file
300
IodineGBA/core/Worker.js
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
56
IodineGBA/core/cartridge/GPIO.js
Normal file
56
IodineGBA/core/cartridge/GPIO.js
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"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:
|
||||
|
||||
|
@ -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:
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"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:
|
||||
|
||||
|
@ -8,52 +8,173 @@
|
|||
|
||||
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.initialize(buffer);
|
||||
}
|
||||
if (typeof SIMD == "object" && typeof SIMD.Int32x4 == "function") {
|
||||
//SIMD support found, insert the optimized SIMD path in:
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.initialize = function (buffer) {
|
||||
this.alphaBlendAmountTarget1 = SIMD.Int32x4.splat(0);
|
||||
this.alphaBlendAmountTarget2 = SIMD.Int32x4.splat(0);
|
||||
this.brightnessEffectAmount = SIMD.Int32x4.splat(0);
|
||||
this.brightnessEffectAmountReverse = SIMD.Int32x4.splat(0x10);
|
||||
this.buffer = buffer;
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.pixelMask = SIMD.Int32x4.splat(0x1F);
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.temporaryPixelBuffer = new Int32Array(4);
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlend = function (topPixel, lowerPixel) {
|
||||
topPixel = topPixel | 0;
|
||||
lowerPixel = lowerPixel | 0;
|
||||
var p1 = SIMD.Int32x4(topPixel >> 10, topPixel >> 5, topPixel, 0);
|
||||
var p2 = SIMD.Int32x4(lowerPixel >> 10, lowerPixel >> 5, lowerPixel, 0);
|
||||
p1 = SIMD.Int32x4.and(p1, this.pixelMask);
|
||||
p2 = SIMD.Int32x4.and(p2, this.pixelMask);
|
||||
p1 = SIMD.Int32x4.mul(p1, this.alphaBlendAmountTarget1);
|
||||
p2 = SIMD.Int32x4.mul(p2, this.alphaBlendAmountTarget2);
|
||||
var presult = SIMD.Int32x4.add(p1, p2);
|
||||
presult = SIMD.Int32x4.shiftRightByScalar(presult, 4);
|
||||
var selectMask = SIMD.Int32x4.lessThanOrEqual(presult, this.pixelMask);
|
||||
presult = SIMD.Int32x4.select(selectMask, presult, this.pixelMask);
|
||||
SIMD.Int32x4.store(this.temporaryPixelBuffer, 0, presult);
|
||||
return (this.temporaryPixelBuffer[0] << 10) | (this.temporaryPixelBuffer[1] << 5) | this.temporaryPixelBuffer[2];
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.brightnessIncrease = function (topPixel) {
|
||||
topPixel = topPixel | 0;
|
||||
var p1 = SIMD.Int32x4(topPixel >> 10, topPixel >> 5, topPixel, 0);
|
||||
p1 = SIMD.Int32x4.and(p1, this.pixelMask);
|
||||
var pTemp = SIMD.Int32x4.sub(this.pixelMask, p1);
|
||||
pTemp = SIMD.Int32x4.mul(pTemp, this.brightnessEffectAmount);
|
||||
pTemp = SIMD.Int32x4.shiftRightByScalar(pTemp, 4);
|
||||
p1 = SIMD.Int32x4.add(p1, pTemp);
|
||||
SIMD.Int32x4.store(this.temporaryPixelBuffer, 0, p1);
|
||||
return (this.temporaryPixelBuffer[0] << 10) | (this.temporaryPixelBuffer[1] << 5) | this.temporaryPixelBuffer[2];
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.brightnessDecrease = function (topPixel) {
|
||||
topPixel = topPixel | 0;
|
||||
var p1 = SIMD.Int32x4(topPixel >> 10, topPixel >> 5, topPixel, 0);
|
||||
p1 = SIMD.Int32x4.and(p1, this.pixelMask);
|
||||
p1 = SIMD.Int32x4.mul(p1, this.brightnessEffectAmountReverse);
|
||||
p1 = SIMD.Int32x4.shiftRightByScalar(p1, 4);
|
||||
SIMD.Int32x4.store(this.temporaryPixelBuffer, 0, p1);
|
||||
return (this.temporaryPixelBuffer[0] << 10) | (this.temporaryPixelBuffer[1] << 5) | this.temporaryPixelBuffer[2];
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA8_0 = function (data) {
|
||||
data = data | 0;
|
||||
var alphaBlendAmountTarget1Scratch = data & 0x1F;
|
||||
this.alphaBlendAmountTarget1 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0);
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA8_1 = function (data) {
|
||||
data = data | 0;
|
||||
var alphaBlendAmountTarget2Scratch = data & 0x1F;
|
||||
this.alphaBlendAmountTarget2 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0);
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA16 = function (data) {
|
||||
data = data | 0;
|
||||
var alphaBlendAmountTarget1Scratch = data & 0x1F;
|
||||
this.alphaBlendAmountTarget1 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0);
|
||||
var alphaBlendAmountTarget2Scratch = (data >> 8) & 0x1F;
|
||||
this.alphaBlendAmountTarget2 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0);
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDCNT32 = function (data) {
|
||||
data = data | 0;
|
||||
//Select target 1 and color effects mode:
|
||||
this.effectsTarget1 = (data & 0x3F) << 16;
|
||||
this.colorEffectsType = (data >> 6) & 0x3;
|
||||
//Select target 2:
|
||||
this.effectsTarget2 = (data & 0x3F00) << 8;
|
||||
var alphaBlendAmountTarget1Scratch = (data >> 16) & 0x1F;
|
||||
this.alphaBlendAmountTarget1 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0);
|
||||
var alphaBlendAmountTarget2Scratch = (data >> 24) & 0x1F;
|
||||
this.alphaBlendAmountTarget2 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0);
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDY8 = function (data) {
|
||||
data = data | 0;
|
||||
var data = Math.min(data & 0x1F, 0x10) | 0;
|
||||
this.brightnessEffectAmount = SIMD.Int32x4.splat(data | 0);
|
||||
this.brightnessEffectAmountReverse = SIMD.Int32x4.splat((0x10 - (data | 0)) | 0);
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processFullNormalEffectsNoSprites = function () {
|
||||
for (var index = 0; (index | 0) < 240; index = ((index | 0) + 4) | 0) {
|
||||
this.buffer[index | 0] = this.processPixelNormal(this.buffer[index | 0x700] | 0, this.buffer[index | 0x800] | 0);
|
||||
this.buffer[index | 1] = this.processPixelNormal(this.buffer[index | 0x701] | 0, this.buffer[index | 0x801] | 0);
|
||||
this.buffer[index | 2] = this.processPixelNormal(this.buffer[index | 0x702] | 0, this.buffer[index | 0x802] | 0);
|
||||
this.buffer[index | 3] = this.processPixelNormal(this.buffer[index | 0x703] | 0, this.buffer[index | 0x803] | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processWindowNormalEffectsNoSprites = function (xStart, xEnd) {
|
||||
xStart = xStart | 0;
|
||||
xEnd = xEnd | 0;
|
||||
while ((xStart | 0) < (xEnd | 0)) {
|
||||
this.buffer[xStart | 0] = this.processPixelNormal(this.buffer[xStart | 0x700] | 0, this.buffer[xStart | 0x800] | 0);
|
||||
xStart = ((xStart | 0) + 1) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processFullNormalEffectsWithSprites = function () {
|
||||
for (var index = 0; (index | 0) < 240; index = ((index | 0) + 4) | 0) {
|
||||
this.buffer[index | 0] = this.processPixelTestFull(this.buffer[index | 0x700] | 0, this.buffer[index | 0x800] | 0);
|
||||
this.buffer[index | 1] = this.processPixelTestFull(this.buffer[index | 0x701] | 0, this.buffer[index | 0x801] | 0);
|
||||
this.buffer[index | 2] = this.processPixelTestFull(this.buffer[index | 0x702] | 0, this.buffer[index | 0x802] | 0);
|
||||
this.buffer[index | 3] = this.processPixelTestFull(this.buffer[index | 0x703] | 0, this.buffer[index | 0x803] | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processWindowNormalEffectsWithSprites = function (xStart, xEnd) {
|
||||
xStart = xStart | 0;
|
||||
xEnd = xEnd | 0;
|
||||
while ((xStart | 0) < (xEnd | 0)) {
|
||||
this.buffer[xStart | 0] = this.processPixelTestFull(this.buffer[xStart | 0x700] | 0, this.buffer[xStart | 0x800] | 0);
|
||||
xStart = ((xStart | 0) + 1) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processFullNoEffectsWithSprites = function () {
|
||||
for (var index = 0; (index | 0) < 240; index = ((index | 0) + 4) | 0) {
|
||||
this.buffer[index | 0] = this.processPixelTestSprite(this.buffer[index | 0x700] | 0, this.buffer[index | 0x800] | 0);
|
||||
this.buffer[index | 1] = this.processPixelTestSprite(this.buffer[index | 0x701] | 0, this.buffer[index | 0x801] | 0);
|
||||
this.buffer[index | 2] = this.processPixelTestSprite(this.buffer[index | 0x702] | 0, this.buffer[index | 0x802] | 0);
|
||||
this.buffer[index | 3] = this.processPixelTestSprite(this.buffer[index | 0x703] | 0, this.buffer[index | 0x803] | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processWindowNoEffectsWithSprites = function (xStart, xEnd) {
|
||||
xStart = xStart | 0;
|
||||
xEnd = xEnd | 0;
|
||||
while ((xStart | 0) < (xEnd | 0)) {
|
||||
this.buffer[xStart | 0] = this.processPixelTestSprite(this.buffer[xStart | 0x700] | 0, this.buffer[xStart | 0x800] | 0);
|
||||
xStart = ((xStart | 0) + 1) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processPixelTestFull = function (lowerPixel, currentPixel) {
|
||||
lowerPixel = lowerPixel | 0;
|
||||
currentPixel = currentPixel | 0;
|
||||
if ((currentPixel & 0x400000) == 0) {
|
||||
return this.processPixelNormal(lowerPixel | 0, currentPixel | 0) | 0;
|
||||
}
|
||||
else {
|
||||
return this.processPixelSprite(lowerPixel | 0, currentPixel | 0) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processPixelTestSprite = function (lowerPixel, currentPixel) {
|
||||
lowerPixel = lowerPixel | 0;
|
||||
currentPixel = currentPixel | 0;
|
||||
if ((currentPixel & 0x400000) == 0) {
|
||||
return currentPixel | 0;
|
||||
}
|
||||
else {
|
||||
return this.processPixelSprite(lowerPixel | 0, currentPixel | 0) | 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
//No SIMD support found, use the scalar path instead:
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.initialize = function (buffer) {
|
||||
this.alphaBlendAmountTarget1 = 0;
|
||||
this.alphaBlendAmountTarget2 = 0;
|
||||
this.brightnessEffectAmount = 0;
|
||||
this.alphaBlendOptimizationChecks();
|
||||
}
|
||||
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;
|
||||
}
|
||||
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.process = function (lowerPixel, topPixel) {
|
||||
lowerPixel = lowerPixel | 0;
|
||||
topPixel = topPixel | 0;
|
||||
if (((topPixel | 0) & (this.effectsTarget1 | 0)) != 0) {
|
||||
switch (this.colorEffectsType | 0) {
|
||||
case 1:
|
||||
if (((lowerPixel | 0) & (this.effectsTarget2 | 0)) != 0 && (topPixel | 0) != (lowerPixel | 0)) {
|
||||
return this.alphaBlend(topPixel | 0, lowerPixel | 0) | 0;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
return this.brightnessIncrease(topPixel | 0) | 0;
|
||||
case 3:
|
||||
return this.brightnessDecrease(topPixel | 0) | 0;
|
||||
}
|
||||
}
|
||||
return topPixel | 0;
|
||||
}
|
||||
if (typeof Math.imul == "function") {
|
||||
if (typeof Math.imul == "function") {
|
||||
//Math.imul found, insert the optimized path in:
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlendNormal = function (topPixel, lowerPixel) {
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlend = function (topPixel, lowerPixel) {
|
||||
topPixel = topPixel | 0;
|
||||
lowerPixel = lowerPixel | 0;
|
||||
var b1 = (topPixel >> 10) & 0x1F;
|
||||
|
@ -74,64 +195,6 @@ if (typeof Math.imul == "function") {
|
|||
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;
|
||||
|
@ -153,10 +216,10 @@ if (typeof Math.imul == "function") {
|
|||
r1 = Math.imul(r1 | 0, decreaseMultiplier | 0) >> 4;
|
||||
return (b1 << 10) | (g1 << 5) | r1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
}
|
||||
else {
|
||||
//Math.imul not found, use the compatibility method:
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlendNormal = function (topPixel, lowerPixel) {
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlend = function (topPixel, lowerPixel) {
|
||||
topPixel = topPixel | 0;
|
||||
lowerPixel = lowerPixel | 0;
|
||||
var b1 = (topPixel >> 10) & 0x1F;
|
||||
|
@ -173,62 +236,6 @@ else {
|
|||
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;
|
||||
|
@ -250,30 +257,74 @@ else {
|
|||
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.alphaBlendTopPass = function (topPixel, lowerPixel) {
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processPixelNormal = function (lowerPixel, topPixel) {
|
||||
lowerPixel = lowerPixel | 0;
|
||||
topPixel = topPixel | 0;
|
||||
if (((topPixel | 0) & (this.effectsTarget1 | 0)) != 0) {
|
||||
switch (this.colorEffectsType | 0) {
|
||||
case 1:
|
||||
if (((lowerPixel | 0) & (this.effectsTarget2 | 0)) != 0 && (topPixel | 0) != (lowerPixel | 0)) {
|
||||
return this.alphaBlend(topPixel | 0, lowerPixel | 0) | 0;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
return this.brightnessIncrease(topPixel | 0) | 0;
|
||||
case 3:
|
||||
return this.brightnessDecrease(topPixel | 0) | 0;
|
||||
}
|
||||
}
|
||||
return topPixel | 0;
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.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;
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -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,7 +34,8 @@ GameBoyAdvanceMosaicRenderer.prototype.renderMosaicHorizontal = function (offset
|
|||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceMosaicRenderer.prototype.renderOBJMosaicHorizontal = function (layer, xOffset, xSize) {
|
||||
if (__VIEWS_SUPPORTED__) {
|
||||
GameBoyAdvanceMosaicRenderer.prototype.renderOBJMosaicHorizontal = function (xOffset, xSize) {
|
||||
xOffset = xOffset | 0;
|
||||
xSize = xSize | 0;
|
||||
var currentPixel = 0x3800000;
|
||||
|
@ -38,9 +43,26 @@ GameBoyAdvanceMosaicRenderer.prototype.renderOBJMosaicHorizontal = function (lay
|
|||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = [];
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
"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:
|
||||
|
||||
|
@ -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) {
|
||||
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,12 +1238,12 @@ 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) {
|
||||
|
|
919
IodineGBA/core/graphics/RendererShim.js
Normal file
919
IodineGBA/core/graphics/RendererShim.js
Normal file
|
@ -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;
|
||||
}
|
587
IodineGBA/core/graphics/Worker.js
Normal file
587
IodineGBA/core/graphics/Worker.js
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"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:
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
# GBA Games Page:
|
||||
|
||||
https://jsemu2.github.io/gba
|
||||
|
||||
# GBA Emulator Core Used:
|
||||
|
||||
https://github.com/taisel/IodineGBA
|
182
launcher.html
182
launcher.html
|
@ -3,6 +3,8 @@
|
|||
<head>
|
||||
<title>GBA GameCenter</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=160">
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1">
|
||||
<!--Required Scripts-->
|
||||
<script src="IodineGBA/includes/TypedArrayShim.js"></script>
|
||||
<script src="IodineGBA/core/Cartridge.js"></script>
|
||||
|
@ -28,6 +30,7 @@
|
|||
<script src="IodineGBA/core/CPU/THUMB.js"></script>
|
||||
<script src="IodineGBA/core/CPU/CPSR.js"></script>
|
||||
<script src="IodineGBA/core/graphics/Renderer.js"></script>
|
||||
<script src="IodineGBA/core/graphics/RendererShim.js"></script>
|
||||
<script src="IodineGBA/core/graphics/RendererProxy.js"></script>
|
||||
<script src="IodineGBA/core/graphics/BGTEXT.js"></script>
|
||||
<script src="IodineGBA/core/graphics/BG2FrameBuffer.js"></script>
|
||||
|
@ -47,21 +50,182 @@
|
|||
<script src="IodineGBA/core/cartridge/SRAM.js"></script>
|
||||
<script src="IodineGBA/core/cartridge/FLASH.js"></script>
|
||||
<script src="IodineGBA/core/cartridge/EEPROM.js"></script>
|
||||
<script src="IodineGBA/core/cartridge/GPIO.js"></script>
|
||||
<!--Add your webpage scripts below-->
|
||||
<script src="user_scripts/AudioGlueCode.js"></script>
|
||||
<script src="user_scripts/base64.js"></script>
|
||||
<script src="user_scripts/CoreGlueCode.js"></script>
|
||||
<script src="user_scripts/GfxGlueCode.js"></script>
|
||||
<script src="user_scripts/GUIGlueCode.js"></script>
|
||||
<script src="user_scripts/JoyPadGlueCode.js"></script>
|
||||
<script src="user_scripts/ROMLoadGlueCode.js"></script>
|
||||
<script src="user_scripts/SavesGlueCode.js"></script>
|
||||
<script src="user_scripts/WorkerGfxGlueCode.js"></script>
|
||||
<script src="user_scripts/WorkerGlueCode.js"></script>
|
||||
<script src="user_scripts/XAudioJS/swfobject.js"></script>
|
||||
<script src="user_scripts/XAudioJS/resampler.js"></script>
|
||||
<script src="user_scripts/XAudioJS/XAudioServer.js"></script>
|
||||
<script src="user_scripts/IodineGBAROMLoadGlueCode.js"></script>
|
||||
<script src="user_scripts/IodineGBAJoyPadGlueCode.js"></script>
|
||||
<script src="user_scripts/IodineGBASavesGlueCode.js"></script>
|
||||
<script src="user_scripts/IodineGBAGraphicsGlueCode.js"></script>
|
||||
<script src="user_scripts/IodineGBAAudioGlueCode.js"></script>
|
||||
<script src="user_scripts/IodineGBACoreGlueCode.js"></script>
|
||||
<script src="user_scripts/base64.js"></script>
|
||||
<link rel="stylesheet" href="user_css/main.css">
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="emulator_target" width="240px" height="160px"></canvas>
|
||||
<span id="tempMessage"></span>
|
||||
<div id="container">
|
||||
<div id="menu" class="paused">
|
||||
<ul class="menu" id="menu_top">
|
||||
<li>
|
||||
File
|
||||
<ul>
|
||||
<li><span>BIOS: </span> <input type="file" id="bios_load" class="files"></li>
|
||||
<li><span>Game: </span> <input type="file" id="rom_load" class="files"></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li id="play" class="show">Play</li>
|
||||
<li id="pause" class="hide">Pause</li>
|
||||
<li id="restart">Restart</li>
|
||||
<li>
|
||||
Settings
|
||||
<ul>
|
||||
<li>
|
||||
<input type="checkbox" id="skip_boot"> Skip Boot Intro
|
||||
</li>
|
||||
<li>
|
||||
<input type="checkbox" id="toggleSmoothScaling" checked="checked"> Smooth Scaling
|
||||
</li>
|
||||
<li>
|
||||
<input type="checkbox" id="toggleDynamicSpeed"> Dynamic Speed
|
||||
</li>
|
||||
<li>
|
||||
<input type="checkbox" id="offthread-cpu" checked="checked"> CPU off-thread
|
||||
</li>
|
||||
<li>
|
||||
<input type="checkbox" id="offthread-gpu" checked="checked"> GPU off-thread
|
||||
</li>
|
||||
<li>
|
||||
<input type="checkbox" id="sound"> Sound
|
||||
</li>
|
||||
<li>
|
||||
GBA Bindings
|
||||
<ul>
|
||||
<li id="key_a">
|
||||
<span>A</span>
|
||||
</li>
|
||||
<li id="key_b">
|
||||
<span>B</span>
|
||||
</li>
|
||||
<li id="key_l">
|
||||
<span>L</span>
|
||||
</li>
|
||||
<li id="key_r">
|
||||
<span>R</span>
|
||||
</li>
|
||||
<li id="key_start">
|
||||
<span>Start</span>
|
||||
</li>
|
||||
<li id="key_select">
|
||||
<span>Select</span>
|
||||
</li>
|
||||
<li id="key_up">
|
||||
<span>↑</span>
|
||||
</li>
|
||||
<li id="key_down">
|
||||
<span>↓</span>
|
||||
</li>
|
||||
<li id="key_left">
|
||||
<span>←</span>
|
||||
</li>
|
||||
<li id="key_right">
|
||||
<span>→</span>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
Emulator Bindings
|
||||
<ul>
|
||||
<li id="key_volumeup">
|
||||
<span>Volume Up</span>
|
||||
</li>
|
||||
<li id="key_volumedown">
|
||||
<span>Volume Down</span>
|
||||
</li>
|
||||
<li id="key_speedup">
|
||||
<span>Speed Up</span>
|
||||
</li>
|
||||
<li id="key_slowdown">
|
||||
<span>Slow Down</span>
|
||||
</li>
|
||||
<li id="key_speedreset">
|
||||
<span>Speed Reset</span>
|
||||
</li>
|
||||
<li id="key_fullscreen">
|
||||
<span>Fullscreen</span>
|
||||
</li>
|
||||
<li id="key_playpause">
|
||||
<span>Play/Pause</span>
|
||||
</li>
|
||||
<li id="key_restart">
|
||||
<span>Restart</span>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
Volume
|
||||
<ul>
|
||||
<li>
|
||||
<input type="range" id="volume">
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li id="saves_menu">
|
||||
Saves
|
||||
<ul id="saves_menu_container">
|
||||
<li>
|
||||
<span>Import:</span><input type="file" id="import" class="files">
|
||||
</li>
|
||||
<li id="existing_saves">
|
||||
<span>Existing Saves</span>
|
||||
<ul id="existing_saves_list">
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="./" id="export" target="_new">Export All Saves</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li id="fullscreen">Fullscreen</li>
|
||||
<li>
|
||||
<span id="speed">Speed</span>
|
||||
<ul>
|
||||
<li>
|
||||
<input type="range" id="speedset">
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<canvas class="canvas" id="emulator_target" width="240" height="160"></canvas>
|
||||
</div>
|
||||
<div class="touch-controls">
|
||||
<div class="touch-dpad">
|
||||
<button id="touch-up">↑</button><br>
|
||||
<button id="touch-left">←</button>
|
||||
<button id="touch-right">→</button><br>
|
||||
<button id="touch-down">↓</button>
|
||||
</div>
|
||||
<div class="touch-buttons">
|
||||
<button id="touch-select">SELECT</button>
|
||||
<button id="touch-start">START</button>
|
||||
</div>
|
||||
<div class="touch-buttons">
|
||||
<button id="touch-a">A</button>
|
||||
<button id="touch-b">B</button><br>
|
||||
<button id="touch-l">L</button>
|
||||
<button id="touch-r">R</button>
|
||||
</div>
|
||||
</div>
|
||||
<span class="message" id="tempMessage"></span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,27 +1,234 @@
|
|||
/*
|
||||
|-----------------------------------------
|
||||
| Core CSS
|
||||
|-----------------------------------------
|
||||
*/
|
||||
|
||||
html, body {
|
||||
font-family: Open Sans, Arial, sans-serif;
|
||||
height: 100%;
|
||||
width:100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #000;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* reset all list items */
|
||||
ul{
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#main {
|
||||
position:absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
/*
|
||||
|-----------------------------------------
|
||||
| Canvas
|
||||
|-----------------------------------------
|
||||
|
|
||||
| Classes are automatically overwritten
|
||||
| by the filter styles, therefore only
|
||||
| attributes and id's can be used.
|
||||
|
|
||||
*/
|
||||
|
||||
canvas {
|
||||
margin: auto;
|
||||
display: block;
|
||||
padding: 0px;
|
||||
background-color: rgb(248, 248, 248);
|
||||
cursor: none;
|
||||
}
|
||||
canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
canvas.textureSmooth {
|
||||
image-rendering: auto;
|
||||
image-rendering: optimizeQuality;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
}
|
||||
span {
|
||||
color: red;
|
||||
|
||||
canvas.texturePixelated {
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
image-rendering: -o-crisp-edges;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
image-rendering: crisp-edges;
|
||||
image-rendering: pixelated;
|
||||
-ms-interpolation-mode: nearest-neighbor;
|
||||
}
|
||||
|
||||
/*
|
||||
|-----------------------------------------
|
||||
| Messages
|
||||
|-----------------------------------------
|
||||
*/
|
||||
|
||||
.message {
|
||||
background: #6cc27d;
|
||||
padding: 1em 2em;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
bottom: 0px;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
display: none;
|
||||
font-size: 40pt;
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
vertical-align: bottom;
|
||||
font-family: monospace;
|
||||
display:none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|-----------------------------------------
|
||||
| Main Menu
|
||||
|-----------------------------------------
|
||||
*/
|
||||
|
||||
/* top level menu */
|
||||
div#menu {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 0px;
|
||||
}
|
||||
.menu {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid rgba(0,0,0,.1);
|
||||
transition: .3s ease;
|
||||
}
|
||||
|
||||
/* auto hide menu while playing */
|
||||
.playing .menu{opacity: 0;}
|
||||
.playing .menu:hover{opacity: 0.9;}
|
||||
|
||||
/* sub level menu */
|
||||
.menu ul{
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top:100%;
|
||||
left:0;
|
||||
background-color: rgb(245, 245, 245);
|
||||
box-shadow: 0 5px 10px 0 rgba(0,0,0,.1);
|
||||
transition: .3s ease;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
li.hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* bottom level nav */
|
||||
.menu ul ul{
|
||||
top:0;
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
#menu_top li {
|
||||
padding: .7em 1em;
|
||||
}
|
||||
|
||||
.menu ul li {
|
||||
padding: .3em 1em !important;
|
||||
}
|
||||
|
||||
.menu li{
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.menu li:hover{
|
||||
background: rgba(0,0,0,.1);
|
||||
}
|
||||
|
||||
/* show sub nav on hover */
|
||||
.menu li:hover > ul{
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|-----------------------------------------
|
||||
| Touch Controls
|
||||
|-----------------------------------------
|
||||
*/
|
||||
|
||||
.touch-controls{
|
||||
display: flex;
|
||||
padding: 1em;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.touch-controls button{
|
||||
display: inline-block;
|
||||
-webkit-appearance: none;
|
||||
border:0;
|
||||
outline: 0;
|
||||
background: #fff;
|
||||
opacity: 0.7;
|
||||
width: 3em;
|
||||
height: 3em;
|
||||
line-height: 3em;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: .1s ease;
|
||||
margin:.5em;
|
||||
}
|
||||
|
||||
.touch-controls button:active{
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 0 10px 0 rgba(0,0,0,0.4) inset;
|
||||
}
|
||||
|
||||
.touch-dpad, .touch-buttons{
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.touch-buttons{
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.touch-dpad--up{
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Only show controls on portrait mode screens */
|
||||
@media screen and (min-aspect-ratio: 1/1) {
|
||||
.touch-controls{
|
||||
display: none;
|
||||
}
|
||||
#main {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
"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:
|
||||
|
||||
|
@ -8,15 +8,18 @@
|
|||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GlueCodeMixer() {
|
||||
function GlueCodeMixer(playButton) {
|
||||
var parentObj = this;
|
||||
this.audio = new XAudioServer(2, this.sampleRate, 0, this.bufferAmount, null, 1, function () {
|
||||
this.audio = new XAudioServer(2, this.sampleRate, 0, this.bufferAmount, null, function () {
|
||||
parentObj.checkHeartbeats();
|
||||
}, function () {
|
||||
parentObj.checkPostHeartbeats();
|
||||
}, 1, function () {
|
||||
//Disable audio in the callback here:
|
||||
parentObj.disableAudio();
|
||||
});
|
||||
}, playButton);
|
||||
this.outputUnits = [];
|
||||
this.outputUnitsValid = [];
|
||||
setInterval(function(){parentObj.checkAudio();}, 16);
|
||||
this.initializeBuffer();
|
||||
}
|
||||
GlueCodeMixer.prototype.sampleRate = 44100;
|
||||
|
@ -50,6 +53,18 @@ GlueCodeMixer.prototype.unregister = function (stackPosition) {
|
|||
}
|
||||
}
|
||||
}
|
||||
GlueCodeMixer.prototype.checkHeartbeats = function () {
|
||||
var inputCount = this.outputUnitsValid.length;
|
||||
for (var inputIndex = 0, output = 0; inputIndex < inputCount; ++inputIndex) {
|
||||
this.outputUnitsValid[inputIndex].heartBeatCallback();
|
||||
}
|
||||
}
|
||||
GlueCodeMixer.prototype.checkPostHeartbeats = function () {
|
||||
var inputCount = this.outputUnitsValid.length;
|
||||
for (var inputIndex = 0, output = 0; inputIndex < inputCount; ++inputIndex) {
|
||||
this.outputUnitsValid[inputIndex].postHeartBeatCallback();
|
||||
}
|
||||
}
|
||||
GlueCodeMixer.prototype.checkAudio = function () {
|
||||
if (this.audio) {
|
||||
var inputCount = this.outputUnitsValid.length;
|
||||
|
@ -62,13 +77,15 @@ GlueCodeMixer.prototype.checkAudio = function () {
|
|||
}
|
||||
this.buffer.push(output);
|
||||
}
|
||||
this.audio.writeAudioNoCallback(this.buffer.getSlice());
|
||||
var bufferLength = this.buffer.count();
|
||||
this.audio.writeAudioNoCallback(this.buffer.buffer, bufferLength);
|
||||
this.buffer.reset();
|
||||
}
|
||||
}
|
||||
GlueCodeMixer.prototype.findLowestBufferCount = function () {
|
||||
var count = 0;
|
||||
for (var inputIndex = 0, inputCount = this.outputUnitsValid.length; inputIndex < inputCount; ++inputIndex) {
|
||||
var tempCount = this.outputUnitsValid[inputIndex].buffer.remainingBuffer();
|
||||
var tempCount = this.outputUnitsValid[inputIndex].buffer.resampledSamplesLeft();
|
||||
if (tempCount > 0) {
|
||||
if (count > 0) {
|
||||
count = Math.min(count, tempCount);
|
||||
|
@ -78,32 +95,38 @@ GlueCodeMixer.prototype.findLowestBufferCount = function () {
|
|||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
return Math.min(count, this.channelCount * this.bufferAmount);
|
||||
}
|
||||
GlueCodeMixer.prototype.disableAudio = function () {
|
||||
this.audio = null;
|
||||
}
|
||||
function GlueCodeMixerInput(mixer) {
|
||||
this.mixer = mixer;
|
||||
this.volume = 1;
|
||||
}
|
||||
GlueCodeMixerInput.prototype.initialize = function (channelCount, sampleRate, bufferAmount, startingVolume, errorCallback) {
|
||||
GlueCodeMixerInput.prototype.initialize = function (channelCount, sampleRate, bufferAmount, heartBeatCallback, postHeartBeatCallback, errorCallback) {
|
||||
this.channelCount = channelCount;
|
||||
this.sampleRate = sampleRate;
|
||||
this.bufferAmount = bufferAmount;
|
||||
this.volume = startingVolume;
|
||||
this.heartBeatCallback = heartBeatCallback;
|
||||
this.postHeartBeatCallback = postHeartBeatCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
var oldBuffer = this.buffer;
|
||||
this.buffer = new AudioBufferWrapper(this.channelCount,
|
||||
this.mixer.channelCount,
|
||||
this.bufferAmount,
|
||||
this.sampleRate,
|
||||
this.mixer.sampleRate);
|
||||
|
||||
if (oldBuffer) {
|
||||
//If re-using same mixer input node, copy old buffer contents into the new buffer:
|
||||
this.buffer.copyOld(oldBuffer);
|
||||
}
|
||||
}
|
||||
GlueCodeMixerInput.prototype.register = function (volume) {
|
||||
GlueCodeMixerInput.prototype.register = function () {
|
||||
this.mixer.appendInput(this);
|
||||
}
|
||||
GlueCodeMixerInput.prototype.changeVolume = function (volume) {
|
||||
this.volume = volume;
|
||||
GlueCodeMixerInput.prototype.setVolume = function (volume) {
|
||||
this.volume = Math.min(Math.max(volume, 0), 1);
|
||||
}
|
||||
GlueCodeMixerInput.prototype.prepareShift = function () {
|
||||
this.buffer.resampleRefill();
|
||||
|
@ -111,8 +134,14 @@ GlueCodeMixerInput.prototype.prepareShift = function () {
|
|||
GlueCodeMixerInput.prototype.shift = function () {
|
||||
return this.buffer.shift() * this.volume;
|
||||
}
|
||||
GlueCodeMixerInput.prototype.push = function (buffer) {
|
||||
this.buffer.push(buffer);
|
||||
GlueCodeMixerInput.prototype.push = function (buffer, start, end) {
|
||||
this.buffer.push(buffer, start, end);
|
||||
this.mixer.checkAudio();
|
||||
}
|
||||
GlueCodeMixerInput.prototype.pushDeferred = function (buffer, start, end) {
|
||||
this.buffer.push(buffer, start, end);
|
||||
}
|
||||
GlueCodeMixerInput.prototype.flush = function () {
|
||||
this.mixer.checkAudio();
|
||||
}
|
||||
GlueCodeMixerInput.prototype.remainingBuffer = function () {
|
||||
|
@ -124,6 +153,9 @@ GlueCodeMixerInput.prototype.registerStackPosition = function (stackPosition) {
|
|||
GlueCodeMixerInput.prototype.unregister = function () {
|
||||
this.mixer.unregister(this.stackPosition);
|
||||
}
|
||||
GlueCodeMixerInput.prototype.setBufferSpace = function (bufferAmount) {
|
||||
this.buffer.setBufferSpace(bufferAmount);
|
||||
}
|
||||
function AudioBufferWrapper(channelCount,
|
||||
mixerChannelCount,
|
||||
bufferAmount,
|
||||
|
@ -139,36 +171,54 @@ function AudioBufferWrapper(channelCount,
|
|||
AudioBufferWrapper.prototype.initialize = function () {
|
||||
this.inBufferSize = this.bufferAmount * this.mixerChannelCount;
|
||||
this.inBuffer = getFloat32Array(this.inBufferSize);
|
||||
this.outBufferSize = (Math.ceil(this.inBufferSize * this.mixerSampleRate / this.sampleRate / this.mixerChannelCount) * this.mixerChannelCount) + this.mixerChannelCount;
|
||||
this.resampler = new Resampler(this.sampleRate, this.mixerSampleRate, this.mixerChannelCount, this.inBuffer);
|
||||
this.outBufferSize = this.resampler.outputBuffer.length;
|
||||
this.outBuffer = getFloat32Array(this.outBufferSize);
|
||||
this.resampler = new Resampler(this.sampleRate, this.mixerSampleRate, this.mixerChannelCount, this.outBufferSize, true);
|
||||
this.inputOffset = 0;
|
||||
this.resampleBufferStart = 0;
|
||||
this.resampleBufferEnd = 0;
|
||||
}
|
||||
AudioBufferWrapper.prototype.push = function (buffer) {
|
||||
var length = buffer.length;
|
||||
if (this.channelCount < this.mixerChannelCount) {
|
||||
for (var bufferCounter = 0; bufferCounter < length && this.inputOffset < this.inBufferSize;) {
|
||||
for (var index = this.channelCount; index < this.mixerChannelCount; ++index) {
|
||||
this.inBuffer[this.inputOffset++] = buffer[bufferCounter];
|
||||
AudioBufferWrapper.prototype.copyOld = function (oldBuffer) {
|
||||
this.resampleRefill();
|
||||
while (oldBuffer.resampleBufferStart != oldBuffer.resampleBufferEnd) {
|
||||
this.outBuffer[this.resampleBufferEnd++] = oldBuffer.outBuffer[oldBuffer.resampleBufferStart++];
|
||||
if (this.resampleBufferEnd == this.outBufferSize) {
|
||||
this.resampleBufferEnd = 0;
|
||||
}
|
||||
for (index = 0; index < this.channelCount && bufferCounter < length; ++index) {
|
||||
this.inBuffer[this.inputOffset++] = buffer[bufferCounter++];
|
||||
if (this.resampleBufferStart == this.resampleBufferEnd) {
|
||||
this.resampleBufferStart += this.mixerChannelCount;
|
||||
if (this.resampleBufferStart == this.outBufferSize) {
|
||||
this.resampleBufferStart = 0;
|
||||
}
|
||||
}
|
||||
if (oldBuffer.resampleBufferStart == oldBuffer.outBufferSize) {
|
||||
oldBuffer.resampleBufferStart = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
AudioBufferWrapper.prototype.push = function (buffer, start, end) {
|
||||
var length = Math.min(buffer.length, end);
|
||||
if (this.channelCount < this.mixerChannelCount) {
|
||||
for (; start < length && this.inputOffset < this.inBufferSize;) {
|
||||
for (var index = this.channelCount; index < this.mixerChannelCount; ++index) {
|
||||
this.inBuffer[this.inputOffset++] = buffer[start];
|
||||
}
|
||||
for (index = 0; index < this.channelCount && start < length; ++index) {
|
||||
this.inBuffer[this.inputOffset++] = buffer[start++];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (this.channelCount == this.mixerChannelCount) {
|
||||
for (var bufferCounter = 0; bufferCounter < length && this.inputOffset < this.inBufferSize;) {
|
||||
this.inBuffer[this.inputOffset++] = buffer[bufferCounter++];
|
||||
for (; start < length && this.inputOffset < this.inBufferSize;) {
|
||||
this.inBuffer[this.inputOffset++] = buffer[start++];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (var bufferCounter = 0; bufferCounter < length && this.inputOffset < this.inBufferSize;) {
|
||||
for (index = 0; index < this.mixerChannelCount && bufferCounter < length; ++index) {
|
||||
this.inBuffer[this.inputOffset++] = buffer[bufferCounter++];
|
||||
for (; start < length && this.inputOffset < this.inBufferSize;) {
|
||||
for (index = 0; index < this.mixerChannelCount && start < length; ++index) {
|
||||
this.inBuffer[this.inputOffset++] = buffer[start++];
|
||||
}
|
||||
bufferCounter += this.channelCount - this.mixerChannelCount;
|
||||
start += this.channelCount - this.mixerChannelCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +235,7 @@ AudioBufferWrapper.prototype.shift = function () {
|
|||
AudioBufferWrapper.prototype.resampleRefill = function () {
|
||||
if (this.inputOffset > 0) {
|
||||
//Resample a chunk of audio:
|
||||
var resampleLength = this.resampler.resampler(this.getSlice(this.inBuffer, this.inputOffset));
|
||||
var resampleLength = this.resampler.resampler(this.inputOffset);
|
||||
var resampledResult = this.resampler.outputBuffer;
|
||||
for (var index2 = 0; index2 < resampleLength;) {
|
||||
this.outBuffer[this.resampleBufferEnd++] = resampledResult[index2++];
|
||||
|
@ -202,29 +252,17 @@ AudioBufferWrapper.prototype.resampleRefill = function () {
|
|||
this.inputOffset = 0;
|
||||
}
|
||||
}
|
||||
AudioBufferWrapper.prototype.setBufferSpace = function (bufferAmount) {
|
||||
while (this.inputOffset < bufferAmount && this.inputOffset < this.inBufferSize) {
|
||||
this.inBuffer[this.inputOffset++] = 0;
|
||||
}
|
||||
}
|
||||
AudioBufferWrapper.prototype.remainingBuffer = function () {
|
||||
return (Math.floor((this.resampledSamplesLeft() * this.resampler.ratioWeight) / this.mixerChannelCount) * this.mixerChannelCount) + this.inputOffset;
|
||||
}
|
||||
AudioBufferWrapper.prototype.resampledSamplesLeft = function () {
|
||||
return ((this.resampleBufferStart <= this.resampleBufferEnd) ? 0 : this.outBufferSize) + this.resampleBufferEnd - this.resampleBufferStart;
|
||||
}
|
||||
AudioBufferWrapper.prototype.getSlice = function (buffer, lengthOf) {
|
||||
//Typed array and normal array buffer section referencing:
|
||||
try {
|
||||
return buffer.subarray(0, lengthOf);
|
||||
}
|
||||
catch (error) {
|
||||
try {
|
||||
//Regular array pass:
|
||||
buffer.length = lengthOf;
|
||||
return buffer;
|
||||
}
|
||||
catch (error) {
|
||||
//Nightly Firefox 4 used to have the subarray function named as slice:
|
||||
return buffer.slice(0, lengthOf);
|
||||
}
|
||||
}
|
||||
}
|
||||
function AudioSimpleBuffer(channelCount, bufferAmount) {
|
||||
this.channelCount = channelCount;
|
||||
this.bufferAmount = bufferAmount;
|
||||
|
@ -237,22 +275,9 @@ AudioSimpleBuffer.prototype.push = function (data) {
|
|||
this.buffer[this.stackLength++] = data;
|
||||
}
|
||||
}
|
||||
AudioSimpleBuffer.prototype.getSlice = function () {
|
||||
var lengthOf = this.stackLength;
|
||||
this.stackLength = 0;
|
||||
//Typed array and normal array buffer section referencing:
|
||||
try {
|
||||
return this.buffer.subarray(0, lengthOf);
|
||||
}
|
||||
catch (error) {
|
||||
try {
|
||||
//Regular array pass:
|
||||
this.buffer.length = lengthOf;
|
||||
return this.buffer;
|
||||
}
|
||||
catch (error) {
|
||||
//Nightly Firefox 4 used to have the subarray function named as slice:
|
||||
return this.buffer.slice(0, lengthOf);
|
||||
}
|
||||
}
|
||||
AudioSimpleBuffer.prototype.count = function () {
|
||||
return this.stackLength;
|
||||
}
|
||||
AudioSimpleBuffer.prototype.reset = function () {
|
||||
this.stackLength = 0;
|
||||
}
|
267
user_scripts/CoreGlueCode.js
Normal file
267
user_scripts/CoreGlueCode.js
Normal file
|
@ -0,0 +1,267 @@
|
|||
"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.
|
||||
*/
|
||||
var games = {
|
||||
"advancewars":"Advance Wars",
|
||||
"advancewars2":"Advance Wars 2",
|
||||
"aladdin":"Aladdin",
|
||||
"alienhominid":"Alien Hominid",
|
||||
"bomberman_max2blue":"Bomberman Max 2 - Blue Advance",
|
||||
"bomberman_tournament":"Bomberman Tournament",
|
||||
"bubblebobble":"Bubble Bobble",
|
||||
"croket1":"Croket! - Yume no Banker Survival!",
|
||||
"croket2":"Croket! 2 - Yami no Bank to Banqueen",
|
||||
"croket3":"Croket! 3 - Granu Oukoku no Nazo",
|
||||
"croket4":"Croket! 4 - Bank no Mori no Mamorigami",
|
||||
"digimon_racing":"Digimon Racing",
|
||||
"dbz_supersonic":"Dragon Ball Z - Supersonic Warriors",
|
||||
"drilldozer":"Drill Dozer",
|
||||
"earthwormjim":"Earthworm Jim",
|
||||
"earthwormjim2":"Earthworm Jim 2",
|
||||
"ff1and2":"Final Fantasy 1 & 2 Advance",
|
||||
"ff4S":"Final Fantasy IV Advance (Sound Restoration Mod)",
|
||||
"ff6":"Final Fantasy VI Advance",
|
||||
"final_fantasy_tactics":"Final Fantasy Tactics Advance",
|
||||
"fire_emblem":"Fire Emblem",
|
||||
"frogger1":"Frogger Advance - The Great Quest",
|
||||
"frogger2":"Frogger's Adventures - Temple of the Frog",
|
||||
"frogger3":"Frogger's Adventures 2 - The Lost Wand",
|
||||
"fzero_gp":"F-Zero - GP Legend",
|
||||
"fzero_max":"F-Zero - Maximum Velocity",
|
||||
"gamewatch4":"Game & Watch Gallery 4",
|
||||
"goldensun":"Golden Sun",
|
||||
"gunstar_super_heroes":"Gunstar Super Heroes",
|
||||
"hamtaro_heartbreak":"Hamtaro - Ham-Ham Heartbreak",
|
||||
"kirbymirror":"Kirby & The Amazing Mirror",
|
||||
"kirbynightmare":"Kirby: Nightmare in Dreamland",
|
||||
"mariokart":"Mario Kart: Super Circuit",
|
||||
"marioparty":"Mario Party Advance",
|
||||
"mariopinball":"Mario Pinball Land",
|
||||
"megamanbass":"Megaman & Bass",
|
||||
"megaman_battle1":"Megaman Battle Network 1",
|
||||
"megaman_battle2":"Megaman Battle Network 2",
|
||||
"megaman_battle3_blue":"Megaman Battle Network 3 Blue",
|
||||
"megaman_battle4_blue":"Megaman Battle Network 4 Blue Moon",
|
||||
"megaman_battle4_red":"Megaman Battle Network 4 Red Sun",
|
||||
"megaman_battle5":"Megaman Battle Network 5 Team Protoman",
|
||||
"megaman_battle6":"Megaman Battle Network 6 Cybeast Falzar",
|
||||
"megaman_zero1":"Megaman Zero",
|
||||
"megaman_zero2":"Megaman Zero 2",
|
||||
"megaman_zero3":"Megaman Zero 3",
|
||||
"megaman_zero4":"Megaman Zero 4",
|
||||
"metalslug":"Metal Slug Advance",
|
||||
"metroid_fusion":"Metroid Fusion",
|
||||
"momotarou_dentetsu":"Momotarou Dentetsu G Gold Deck wo Tsukure!",
|
||||
"monopoly":"Monopoly",
|
||||
"monster_force":"Monster Force",
|
||||
"mortal_kombat":"Mortal Kombat Advance",
|
||||
"pacman_world":"Pacman World",
|
||||
"pacman_world2":"Pacman World 2",
|
||||
"pokemonflorasky":"Pokemon Flora Sky Rom Hack",
|
||||
"pokemonemerald":"Pokemon Emerald",
|
||||
"pokemongreen":"Pokemon Leaf Green",
|
||||
"mysteryred":"Pokemon Mystery Dungeon Red",
|
||||
"pokemonruby":"Pokemon Ruby",
|
||||
"pokemonsapphire":"Pokemon Sapphire",
|
||||
"pokemonred":"Pokemon Fire Red",
|
||||
"gba_video_pokemon_1":"Pokemon Video Pak 1",
|
||||
"gba_video_pokemon_2":"Pokemon Video Pak 2",
|
||||
"gba_video_pokemon_3":"Pokemon Video Pak 3",
|
||||
"gba_video_pokemon_4":"Pokemon Video Pak 4",
|
||||
"sonic_advance":"Sonic Advance",
|
||||
"sonic_advance2":"Sonic Advance 2",
|
||||
"sonic_advance3":"Sonic Advance 3",
|
||||
"sonicbattle":"Sonic Battle",
|
||||
"supermonkeyballjr":"Super Monkey Ball Jr",
|
||||
"superstar":"Mario & Luigi: Superstar Saga",
|
||||
"supermarioadvance":"Super Mario Advance",
|
||||
"supermarioadvance2":"Super Mario Advance 2",
|
||||
"supermarioadvance3":"Super Mario Advance 3",
|
||||
"supermarioadvance4":"Super Mario Advance 4",
|
||||
"simpsons":"The Simpsons: Road Rage",
|
||||
"sonicpinball":"Sonic Pinball",
|
||||
"super_street_fighter_2_turbo_revival":"Super Street Fighter II: Turbo Revival",
|
||||
"super_street_fighter_3_alpha":"Super Street Fighter III: Alpha",
|
||||
"tales_of_phantasia":"Tales of Phantasia",
|
||||
"tak2_staff_of_dreams":"Tak 2: The Staff of Dreams",
|
||||
"tetris_worlds":"Tetris Worlds",
|
||||
"tmnt":"Teenage Mutant Ninja Turtles",
|
||||
"sims_bustin_out":"The Sims: Bustin' Out",
|
||||
"sims2":"The Sims 2",
|
||||
"spyro_adventure":"Spyro Adventure",
|
||||
"spyro_ice":"Spyro: Season of Ice",
|
||||
"spyro_flame":"Spyro 2: Season of Flame",
|
||||
"turok_evolution":"Turok Evolution",
|
||||
"warioland4":"Wario Land 4",
|
||||
"wario_ware":"Wario Ware Inc",
|
||||
"zelda_past":"The Legend of Zelda: A Link to the Past",
|
||||
"zelda_minish":"The Legend of Zelda: The Minish Cap"
|
||||
};
|
||||
var IodineGUI = {
|
||||
Iodine:null,
|
||||
Blitter:null,
|
||||
coreTimerID:null,
|
||||
GUITimerID: null,
|
||||
toMap:null,
|
||||
toMapIndice:0,
|
||||
suspended:false,
|
||||
isPlaying:false,
|
||||
startTime:(+(new Date()).getTime()),
|
||||
mixerInput:null,
|
||||
currentSpeed:[false,0],
|
||||
defaults:{
|
||||
timerRate:16,
|
||||
sound:true,
|
||||
volume:1,
|
||||
skipBoot:false,
|
||||
toggleSmoothScaling:true,
|
||||
toggleDynamicSpeed:false,
|
||||
toggleOffthreadGraphics:true,
|
||||
toggleOffthreadCPU:(navigator.userAgent.indexOf('AppleWebKit') == -1 || (navigator.userAgent.indexOf('Windows NT 10.0') != -1 && navigator.userAgent.indexOf('Trident') == -1)),
|
||||
keyZonesGBA:[
|
||||
//Use this to control the GBA key mapping:
|
||||
//A:
|
||||
88,
|
||||
//B:
|
||||
90,
|
||||
//Select:
|
||||
16,
|
||||
//Start:
|
||||
13,
|
||||
//Right:
|
||||
39,
|
||||
//Left:
|
||||
37,
|
||||
//Up:
|
||||
38,
|
||||
//Down:
|
||||
40,
|
||||
//R:
|
||||
83,
|
||||
//L:
|
||||
65
|
||||
],
|
||||
keyZonesControl:[
|
||||
//Use this to control the emulator function key mapping:
|
||||
//Volume Down:
|
||||
55,
|
||||
//Volume Up:
|
||||
56,
|
||||
//Speed Up:
|
||||
52,
|
||||
//Slow Down:
|
||||
51,
|
||||
//Reset Speed:
|
||||
53,
|
||||
//Toggle Fullscreen:
|
||||
54,
|
||||
//Play/Pause:
|
||||
80,
|
||||
//Restart:
|
||||
82
|
||||
]
|
||||
}
|
||||
};
|
||||
window.onload = function () {
|
||||
if (!games[location.hash.substr(1)]) {
|
||||
alert("Invalid game request!");
|
||||
return;
|
||||
}
|
||||
//Populate settings:
|
||||
registerDefaultSettings();
|
||||
//Initialize Iodine:
|
||||
registerIodineHandler();
|
||||
//Initialize the timer:
|
||||
registerTimerHandler();
|
||||
//Initialize the graphics:
|
||||
registerBlitterHandler();
|
||||
//Initialize the audio:
|
||||
registerAudioHandler();
|
||||
//Register the save handler callbacks:
|
||||
registerSaveHandlers();
|
||||
//Register the GUI controls.
|
||||
registerGUIEvents();
|
||||
//Register GUI settings.
|
||||
registerGUISettings();
|
||||
//Download the BIOS:
|
||||
downloadBIOS();
|
||||
}
|
||||
function downloadBIOS() {
|
||||
downloadFile("Binaries/gba_bios.bin", registerBIOS);
|
||||
}
|
||||
function registerBIOS() {
|
||||
processDownload(this, attachBIOS);
|
||||
downloadROM(location.hash.substr(1));
|
||||
}
|
||||
function downloadROM(gamename) {
|
||||
writeRedTemporaryText("Downloading \"" + games[gamename] + ".\"");
|
||||
downloadFile("Binaries/" + gamename + ".gba", registerROM);
|
||||
}
|
||||
function registerROM() {
|
||||
clearTempString();
|
||||
processDownload(this, attachROM);
|
||||
}
|
||||
function registerIodineHandler() {
|
||||
try {
|
||||
/*
|
||||
We utilize SharedArrayBuffer and Atomics API,
|
||||
which browsers prior to 2016 do not support:
|
||||
*/
|
||||
if (typeof SharedArrayBuffer != "function" || typeof Atomics != "object") {
|
||||
throw null;
|
||||
}
|
||||
else if (!IodineGUI.defaults.toggleOffthreadCPU) {
|
||||
//Try starting Iodine normally, but initialize offthread gfx:
|
||||
IodineGUI.Iodine = new IodineGBAWorkerGfxShim();
|
||||
}
|
||||
else {
|
||||
//Try starting Iodine in a webworker:
|
||||
IodineGUI.Iodine = new IodineGBAWorkerShim();
|
||||
//In order for save on page unload, this needs to be done:
|
||||
addEvent("beforeunload", window, registerBeforeUnloadHandler);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
//Otherwise just run on-thread:
|
||||
IodineGUI.Iodine = new GameBoyAdvanceEmulator();
|
||||
}
|
||||
}
|
||||
function registerBeforeUnloadHandler(e) {
|
||||
IodineGUI.Iodine.pause();
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
return "IodineGBA needs to process your save data, leaving now may result in not saving current data.";
|
||||
}
|
||||
function registerTimerHandler() {
|
||||
IodineGUI.defaults.timerRate = 16;
|
||||
IodineGUI.Iodine.setIntervalRate(IodineGUI.defaults.timerRate | 0);
|
||||
}
|
||||
function initTimer() {
|
||||
IodineGUI.coreTimerID = setInterval(function () {
|
||||
IodineGUI.Iodine.timerCallback(((+(new Date()).getTime()) - (+IodineGUI.startTime)) >>> 0);
|
||||
}, IodineGUI.defaults.timerRate | 0);
|
||||
}
|
||||
function registerBlitterHandler() {
|
||||
IodineGUI.Blitter = new GfxGlueCode(240, 160);
|
||||
IodineGUI.Blitter.attachCanvas(document.getElementById("emulator_target"));
|
||||
IodineGUI.Iodine.attachGraphicsFrameHandler(IodineGUI.Blitter);
|
||||
IodineGUI.Blitter.attachGfxPostCallback(function () {
|
||||
if (IodineGUI.currentSpeed[0]) {
|
||||
var speedDOM = document.getElementById("speed");
|
||||
speedDOM.textContent = "Speed: " + IodineGUI.currentSpeed[1] + "%";
|
||||
}
|
||||
});
|
||||
}
|
||||
function registerAudioHandler() {
|
||||
var Mixer = new GlueCodeMixer(document.getElementById("play"));
|
||||
IodineGUI.mixerInput = new GlueCodeMixerInput(Mixer);
|
||||
IodineGUI.Iodine.attachAudioHandler(IodineGUI.mixerInput);
|
||||
}
|
664
user_scripts/GUIGlueCode.js
Normal file
664
user_scripts/GUIGlueCode.js
Normal file
|
@ -0,0 +1,664 @@
|
|||
"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.
|
||||
*/
|
||||
function registerGUIEvents() {
|
||||
//Catch any play status changes:
|
||||
IodineGUI.Iodine.attachPlayStatusHandler(updatePlayButton);
|
||||
//Add DOM events:
|
||||
addEvent("keydown", document, keyDown);
|
||||
addEvent("keyup", document, keyUpPreprocess);
|
||||
addEvent("change", document.getElementById("rom_load"), fileLoadROM);
|
||||
addEvent("change", document.getElementById("bios_load"), fileLoadBIOS);
|
||||
addEvent("click", document.getElementById("play"), function (e) {
|
||||
IodineGUI.Iodine.play();
|
||||
});
|
||||
addEvent("click", document.getElementById("pause"), function (e) {
|
||||
IodineGUI.Iodine.pause();
|
||||
});
|
||||
addEvent("click", document.getElementById("restart"), function (e) {
|
||||
IodineGUI.Iodine.restart();
|
||||
});
|
||||
addEvent("click", document.getElementById("sound"), function () {
|
||||
setValue("sound", !!this.checked);
|
||||
if (this.checked) {
|
||||
IodineGUI.Iodine.enableAudio();
|
||||
}
|
||||
else {
|
||||
IodineGUI.Iodine.disableAudio();
|
||||
}
|
||||
});
|
||||
addEvent("click", document.getElementById("skip_boot"), function () {
|
||||
setValue("skipBoot", !!this.checked);
|
||||
IodineGUI.Iodine.toggleSkipBootROM(this.checked);
|
||||
});
|
||||
addEvent("click", document.getElementById("toggleSmoothScaling"), function () {
|
||||
setValue("toggleSmoothScaling", !!this.checked);
|
||||
if (IodineGUI.Blitter) {
|
||||
IodineGUI.Blitter.setSmoothScaling(this.checked);
|
||||
}
|
||||
});
|
||||
addEvent("click", document.getElementById("toggleDynamicSpeed"), function () {
|
||||
setValue("toggleDynamicSpeed", !!this.checked);
|
||||
IodineGUI.Iodine.toggleDynamicSpeed(this.checked);
|
||||
});
|
||||
addEvent("click", document.getElementById("offthread-gpu"), function () {
|
||||
setValue("toggleOffthreadGraphics", !!this.checked);
|
||||
IodineGUI.Iodine.toggleOffthreadGraphics(this.checked);
|
||||
});
|
||||
addEvent("click", document.getElementById("offthread-cpu"), function () {
|
||||
setValue("toggleOffthreadCPU", !!this.checked);
|
||||
//Can't do anything until reload of page.
|
||||
});
|
||||
addEvent("change", document.getElementById("speedset"), speedChangeFunc);
|
||||
addEvent("input", document.getElementById("speedset"), speedChangeFunc);
|
||||
addEvent("click", document.getElementById("fullscreen"), toggleFullScreen);
|
||||
addEvent("click", document.getElementById("key_a"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA;
|
||||
IodineGUI.toMapIndice = 0;
|
||||
});
|
||||
addEvent("mousedown", document.getElementById("touch-a"), function () {
|
||||
IodineGUI.Iodine.keyDown(0);
|
||||
});
|
||||
addEvent("mouseup", document.getElementById("touch-a"), function () {
|
||||
IodineGUI.Iodine.keyUp(0);
|
||||
});
|
||||
addEvent("click", document.getElementById("key_b"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA;
|
||||
IodineGUI.toMapIndice = 1;
|
||||
});
|
||||
addEvent("mousedown", document.getElementById("touch-b"), function () {
|
||||
IodineGUI.Iodine.keyDown(1);
|
||||
});
|
||||
addEvent("mouseup", document.getElementById("touch-b"), function () {
|
||||
IodineGUI.Iodine.keyUp(1);
|
||||
});
|
||||
addEvent("click", document.getElementById("key_select"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA;
|
||||
IodineGUI.toMapIndice = 2;
|
||||
});
|
||||
addEvent("mousedown", document.getElementById("touch-select"), function () {
|
||||
IodineGUI.Iodine.keyDown(2);
|
||||
});
|
||||
addEvent("mouseup", document.getElementById("touch-select"), function () {
|
||||
IodineGUI.Iodine.keyUp(2);
|
||||
});
|
||||
addEvent("click", document.getElementById("key_start"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA;
|
||||
IodineGUI.toMapIndice = 3;
|
||||
});
|
||||
addEvent("mousedown", document.getElementById("touch-start"), function () {
|
||||
IodineGUI.Iodine.keyDown(3);
|
||||
});
|
||||
addEvent("mouseup", document.getElementById("touch-start"), function () {
|
||||
IodineGUI.Iodine.keyUp(3);
|
||||
});
|
||||
addEvent("click", document.getElementById("key_right"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA;
|
||||
IodineGUI.toMapIndice = 4;
|
||||
});
|
||||
addEvent("mousedown", document.getElementById("touch-right"), function () {
|
||||
IodineGUI.Iodine.keyDown(4);
|
||||
});
|
||||
addEvent("mouseup", document.getElementById("touch-right"), function () {
|
||||
IodineGUI.Iodine.keyUp(4);
|
||||
});
|
||||
addEvent("click", document.getElementById("key_left"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA;
|
||||
IodineGUI.toMapIndice = 5;
|
||||
});
|
||||
addEvent("mousedown", document.getElementById("touch-left"), function () {
|
||||
IodineGUI.Iodine.keyDown(5);
|
||||
});
|
||||
addEvent("mouseup", document.getElementById("touch-left"), function () {
|
||||
IodineGUI.Iodine.keyUp(5);
|
||||
});
|
||||
addEvent("click", document.getElementById("key_up"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA;
|
||||
IodineGUI.toMapIndice = 6;
|
||||
});
|
||||
addEvent("mousedown", document.getElementById("touch-up"), function () {
|
||||
IodineGUI.Iodine.keyDown(6);
|
||||
});
|
||||
addEvent("mouseup", document.getElementById("touch-up"), function () {
|
||||
IodineGUI.Iodine.keyUp(6);
|
||||
});
|
||||
addEvent("click", document.getElementById("key_down"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA;
|
||||
IodineGUI.toMapIndice = 7;
|
||||
});
|
||||
addEvent("mousedown", document.getElementById("touch-down"), function () {
|
||||
IodineGUI.Iodine.keyDown(7);
|
||||
});
|
||||
addEvent("mouseup", document.getElementById("touch-down"), function () {
|
||||
IodineGUI.Iodine.keyUp(7);
|
||||
});
|
||||
addEvent("click", document.getElementById("key_r"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA;
|
||||
IodineGUI.toMapIndice = 8;
|
||||
});
|
||||
addEvent("mousedown", document.getElementById("touch-r"), function () {
|
||||
IodineGUI.Iodine.keyDown(8);
|
||||
});
|
||||
addEvent("mouseup", document.getElementById("touch-r"), function () {
|
||||
IodineGUI.Iodine.keyUp(8);
|
||||
});
|
||||
addEvent("click", document.getElementById("key_l"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesGBA;
|
||||
IodineGUI.toMapIndice = 9;
|
||||
});
|
||||
addEvent("mousedown", document.getElementById("touch-l"), function () {
|
||||
IodineGUI.Iodine.keyDown(9);
|
||||
});
|
||||
addEvent("mouseup", document.getElementById("touch-l"), function () {
|
||||
IodineGUI.Iodine.keyUp(9);
|
||||
});
|
||||
addEvent("click", document.getElementById("key_volumedown"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesControl;
|
||||
IodineGUI.toMapIndice = 0;
|
||||
});
|
||||
addEvent("click", document.getElementById("key_volumeup"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesControl;
|
||||
IodineGUI.toMapIndice = 1;
|
||||
});
|
||||
addEvent("click", document.getElementById("key_speedup"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesControl;
|
||||
IodineGUI.toMapIndice = 2;
|
||||
});
|
||||
addEvent("click", document.getElementById("key_slowdown"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesControl;
|
||||
IodineGUI.toMapIndice = 3;
|
||||
});
|
||||
addEvent("click", document.getElementById("key_speedreset"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesControl;
|
||||
IodineGUI.toMapIndice = 4;
|
||||
});
|
||||
addEvent("click", document.getElementById("key_fullscreen"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesControl;
|
||||
IodineGUI.toMapIndice = 5;
|
||||
});
|
||||
addEvent("click", document.getElementById("key_playpause"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesControl;
|
||||
IodineGUI.toMapIndice = 6;
|
||||
});
|
||||
addEvent("click", document.getElementById("key_restart"), function () {
|
||||
IodineGUI.toMap = IodineGUI.defaults.keyZonesControl;
|
||||
IodineGUI.toMapIndice = 7;
|
||||
});
|
||||
addEvent("change", document.getElementById("import"), function (e) {
|
||||
if (typeof this.files != "undefined") {
|
||||
try {
|
||||
if (this.files.length >= 1) {
|
||||
writeRedTemporaryText("Reading the local file \"" + this.files[0].name + "\" for importing.");
|
||||
try {
|
||||
//Gecko 1.9.2+ (Standard Method)
|
||||
var binaryHandle = new FileReader();
|
||||
binaryHandle.onload = function () {
|
||||
if (this.readyState == 2) {
|
||||
writeRedTemporaryText("file imported.");
|
||||
try {
|
||||
import_save(this.result);
|
||||
}
|
||||
catch (error) {
|
||||
writeRedTemporaryText(error.message + " file: " + error.fileName + " line: " + error.lineNumber);
|
||||
}
|
||||
}
|
||||
else {
|
||||
writeRedTemporaryText("importing file, please wait...");
|
||||
}
|
||||
}
|
||||
binaryHandle.readAsBinaryString(this.files[this.files.length - 1]);
|
||||
}
|
||||
catch (error) {
|
||||
//Gecko 1.9.0, 1.9.1 (Non-Standard Method)
|
||||
var romImageString = this.files[this.files.length - 1].getAsBinary();
|
||||
try {
|
||||
import_save(romImageString);
|
||||
}
|
||||
catch (error) {
|
||||
writeRedTemporaryText(error.message + " file: " + error.fileName + " line: " + error.lineNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
writeRedTemporaryText("Incorrect number of files selected for local loading.");
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
writeRedTemporaryText("Could not load in a locally stored ROM file.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
writeRedTemporaryText("could not find the handle on the file to open.");
|
||||
}
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
addEvent("click", document.getElementById("export"), refreshStorageListing);
|
||||
addEvent("unload", window, ExportSave);
|
||||
IodineGUI.Iodine.attachSpeedHandler(function (speed) {
|
||||
speed = speed.toFixed(2);
|
||||
if (speed != IodineGUI.currentSpeed[1]) {
|
||||
IodineGUI.currentSpeed[1] = speed;
|
||||
IodineGUI.currentSpeed[0] = true;
|
||||
}
|
||||
});
|
||||
addEvent("change", document.getElementById("volume"), volChangeFunc);
|
||||
addEvent("input", document.getElementById("volume"), volChangeFunc);
|
||||
addEvent("resize", window, resizeCanvasFunc);
|
||||
addEvent("mouseover", document.getElementById("saves_menu"), rebuildSavesMenu);
|
||||
if (typeof document.hidden !== "undefined") {
|
||||
addEvent("visibilitychange", document, visibilityChangeHandle);
|
||||
}
|
||||
else if (typeof document.mozHidden !== "undefined") {
|
||||
addEvent("mozvisibilitychange", document, mozVisibilityChangeHandle);
|
||||
}
|
||||
else if (typeof document.msHidden !== "undefined") {
|
||||
addEvent("msvisibilitychange", document, msVisibilityChangeHandle);
|
||||
}
|
||||
else if (typeof document.webkitHidden !== "undefined") {
|
||||
addEvent("webkitvisibilitychange", document, webkitVisibilityChangeHandle);
|
||||
}
|
||||
//Run on init as well:
|
||||
resizeCanvasFunc();
|
||||
}
|
||||
function registerDefaultSettings() {
|
||||
if (findValue("sound") === null) {
|
||||
setValue("sound", !!IodineGUI.defaults.sound);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.sound = !!findValue("sound");
|
||||
}
|
||||
if (findValue("volume") === null) {
|
||||
setValue("volume", +IodineGUI.defaults.volume);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.volume = +findValue("volume");
|
||||
}
|
||||
document.getElementById("volume").value = Math.round(IodineGUI.defaults.volume * 100);
|
||||
document.getElementById("speedset").value = 50;
|
||||
if (findValue("skipBoot") === null) {
|
||||
setValue("skipBoot", !!IodineGUI.defaults.skipBoot);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.skipBoot = !!findValue("skipBoot");
|
||||
}
|
||||
if (findValue("toggleSmoothScaling") === null) {
|
||||
setValue("toggleSmoothScaling", !!IodineGUI.defaults.toggleSmoothScaling);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.toggleSmoothScaling = !!findValue("toggleSmoothScaling");
|
||||
}
|
||||
if (findValue("toggleDynamicSpeed") === null) {
|
||||
setValue("toggleDynamicSpeed", !!IodineGUI.defaults.toggleDynamicSpeed);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.toggleDynamicSpeed = !!findValue("toggleDynamicSpeed");
|
||||
}
|
||||
if (findValue("toggleOffthreadGraphics") === null) {
|
||||
setValue("toggleOffthreadGraphics", !!IodineGUI.defaults.toggleOffthreadGraphics);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.toggleOffthreadGraphics = !!findValue("toggleOffthreadGraphics");
|
||||
}
|
||||
if (findValue("toggleOffthreadCPU") === null) {
|
||||
setValue("toggleOffthreadCPU", !!IodineGUI.defaults.toggleOffthreadCPU);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.toggleOffthreadCPU = !!findValue("toggleOffthreadCPU");
|
||||
}
|
||||
if (findValue("key_a") === null) {
|
||||
setValue("key_a", IodineGUI.defaults.keyZonesGBA[0] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesGBA[0] = findValue("key_a");
|
||||
}
|
||||
if (findValue("key_b") === null) {
|
||||
setValue("key_b", IodineGUI.defaults.keyZonesGBA[1] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesGBA[1] = findValue("key_b");
|
||||
}
|
||||
if (findValue("key_select") === null) {
|
||||
setValue("key_select", IodineGUI.defaults.keyZonesGBA[2] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesGBA[2] = findValue("key_select");
|
||||
}
|
||||
if (findValue("key_start") === null) {
|
||||
setValue("key_start", IodineGUI.defaults.keyZonesGBA[3] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesGBA[3] = findValue("key_start");
|
||||
}
|
||||
if (findValue("key_right") === null) {
|
||||
setValue("key_right", IodineGUI.defaults.keyZonesGBA[4] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesGBA[4] = findValue("key_right");
|
||||
}
|
||||
if (findValue("key_left") === null) {
|
||||
setValue("key_left", IodineGUI.defaults.keyZonesGBA[5] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesGBA[5] = findValue("key_left");
|
||||
}
|
||||
if (findValue("key_up") === null) {
|
||||
setValue("key_up", IodineGUI.defaults.keyZonesGBA[6] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesGBA[6] = findValue("key_up");
|
||||
}
|
||||
if (findValue("key_down") === null) {
|
||||
setValue("key_down", IodineGUI.defaults.keyZonesGBA[7] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesGBA[7] = findValue("key_down");
|
||||
}
|
||||
if (findValue("key_r") === null) {
|
||||
setValue("key_r", IodineGUI.defaults.keyZonesGBA[8] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesGBA[8] = findValue("key_r");
|
||||
}
|
||||
if (findValue("key_l") === null) {
|
||||
setValue("key_l", IodineGUI.defaults.keyZonesGBA[9] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesGBA[9] = findValue("key_l");
|
||||
}
|
||||
if (findValue("key_volumedown") === null) {
|
||||
setValue("key_volumedown", IodineGUI.defaults.keyZonesControl[0] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesControl[0] = findValue("key_volumedown");
|
||||
}
|
||||
if (findValue("key_volumeup") === null) {
|
||||
setValue("key_volumeup", IodineGUI.defaults.keyZonesControl[1] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesControl[1] = findValue("key_volumeup");
|
||||
}
|
||||
if (findValue("key_speedup") === null) {
|
||||
setValue("key_speedup", IodineGUI.defaults.keyZonesControl[2] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesControl[2] = findValue("key_speedup");
|
||||
}
|
||||
if (findValue("key_slowdown") === null) {
|
||||
setValue("key_slowdown", IodineGUI.defaults.keyZonesControl[3] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesControl[3] = findValue("key_slowdown");
|
||||
}
|
||||
if (findValue("key_speedreset") === null) {
|
||||
setValue("key_speedreset", IodineGUI.defaults.keyZonesControl[4] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesControl[4] = findValue("key_speedreset");
|
||||
}
|
||||
if (findValue("key_fullscreen") === null) {
|
||||
setValue("key_fullscreen", IodineGUI.defaults.keyZonesControl[5] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesControl[5] = findValue("key_fullscreen");
|
||||
}
|
||||
if (findValue("key_playpause") === null) {
|
||||
setValue("key_playpause", IodineGUI.defaults.keyZonesControl[6] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesControl[6] = findValue("key_playpause");
|
||||
}
|
||||
if (findValue("key_restart") === null) {
|
||||
setValue("key_restart", IodineGUI.defaults.keyZonesControl[7] | 0);
|
||||
}
|
||||
else {
|
||||
IodineGUI.defaults.keyZonesControl[7] = findValue("key_restart");
|
||||
}
|
||||
}
|
||||
function saveKeyBindings() {
|
||||
setValue("key_a", IodineGUI.defaults.keyZonesGBA[0] | 0);
|
||||
setValue("key_b", IodineGUI.defaults.keyZonesGBA[1] | 0);
|
||||
setValue("key_select", IodineGUI.defaults.keyZonesGBA[2] | 0);
|
||||
setValue("key_start", IodineGUI.defaults.keyZonesGBA[3] | 0);
|
||||
setValue("key_right", IodineGUI.defaults.keyZonesGBA[4] | 0);
|
||||
setValue("key_left", IodineGUI.defaults.keyZonesGBA[5] | 0);
|
||||
setValue("key_up", IodineGUI.defaults.keyZonesGBA[6] | 0);
|
||||
setValue("key_down", IodineGUI.defaults.keyZonesGBA[7] | 0);
|
||||
setValue("key_r", IodineGUI.defaults.keyZonesGBA[8] | 0);
|
||||
setValue("key_l", IodineGUI.defaults.keyZonesGBA[9] | 0);
|
||||
setValue("key_volumedown", IodineGUI.defaults.keyZonesControl[0] | 0);
|
||||
setValue("key_volumeup", IodineGUI.defaults.keyZonesControl[1] | 0);
|
||||
setValue("key_speedup", IodineGUI.defaults.keyZonesControl[2] | 0);
|
||||
setValue("key_slowdown", IodineGUI.defaults.keyZonesControl[3] | 0);
|
||||
setValue("key_speedreset", IodineGUI.defaults.keyZonesControl[4] | 0);
|
||||
setValue("key_fullscreen", IodineGUI.defaults.keyZonesControl[5] | 0);
|
||||
setValue("key_playpause", IodineGUI.defaults.keyZonesControl[6] | 0);
|
||||
setValue("key_restart", IodineGUI.defaults.keyZonesControl[7] | 0);
|
||||
}
|
||||
function registerGUISettings() {
|
||||
document.getElementById("sound").checked = IodineGUI.defaults.sound;
|
||||
if (IodineGUI.defaults.sound) {
|
||||
IodineGUI.Iodine.enableAudio();
|
||||
}
|
||||
try {
|
||||
var volControl = document.getElementById("volume");
|
||||
volControl.min = 0;
|
||||
volControl.max = 100;
|
||||
volControl.step = 1;
|
||||
volControl.value = IodineGUI.defaults.volume * 100;
|
||||
}
|
||||
catch (e) {}
|
||||
IodineGUI.mixerInput.setVolume(IodineGUI.defaults.volume);
|
||||
document.getElementById("skip_boot").checked = IodineGUI.defaults.skipBoot;
|
||||
IodineGUI.Iodine.toggleSkipBootROM(IodineGUI.defaults.skipBoot);
|
||||
document.getElementById("toggleSmoothScaling").checked = IodineGUI.defaults.toggleSmoothScaling;
|
||||
IodineGUI.Blitter.setSmoothScaling(IodineGUI.defaults.toggleSmoothScaling);
|
||||
document.getElementById("toggleDynamicSpeed").checked = IodineGUI.defaults.toggleDynamicSpeed;
|
||||
IodineGUI.Iodine.toggleDynamicSpeed(IodineGUI.defaults.toggleDynamicSpeed);
|
||||
document.getElementById("offthread-gpu").checked = IodineGUI.defaults.toggleOffthreadGraphics;
|
||||
IodineGUI.Iodine.toggleOffthreadGraphics(IodineGUI.defaults.toggleOffthreadGraphics);
|
||||
document.getElementById("offthread-cpu").checked = IodineGUI.defaults.toggleOffthreadCPU;
|
||||
if (typeof SharedArrayBuffer != "function" || typeof Atomics != "object") {
|
||||
document.getElementById("offthread-gpu").disabled = true;
|
||||
document.getElementById("offthread-cpu").disabled = true;
|
||||
}
|
||||
}
|
||||
function updatePlayButton(isPlaying) {
|
||||
isPlaying = isPlaying | 0;
|
||||
if ((isPlaying | 0) == 1) {
|
||||
document.getElementById("play").className = "hide";
|
||||
document.getElementById("pause").className = "show";
|
||||
document.getElementById("menu").className = "playing";
|
||||
if (!IodineGUI.coreTimerID) {
|
||||
initTimer();
|
||||
}
|
||||
IodineGUI.isPlaying = true;
|
||||
}
|
||||
else {
|
||||
document.getElementById("pause").className = "hide";
|
||||
document.getElementById("play").className = "show";
|
||||
document.getElementById("menu").className = "paused";
|
||||
if (IodineGUI.coreTimerID) {
|
||||
clearInterval(IodineGUI.coreTimerID);
|
||||
IodineGUI.coreTimerID = null;
|
||||
}
|
||||
IodineGUI.isPlaying = false;
|
||||
}
|
||||
}
|
||||
function visibilityChangeHandle() {
|
||||
processVisibilityChange(document.hidden);
|
||||
}
|
||||
function mozVisibilityChangeHandle() {
|
||||
processVisibilityChange(document.mozHidden);
|
||||
}
|
||||
function msVisibilityChangeHandle() {
|
||||
processVisibilityChange(document.msHidden);
|
||||
}
|
||||
function webkitVisibilityChangeHandle() {
|
||||
processVisibilityChange(document.webkitHidden);
|
||||
}
|
||||
function processVisibilityChange(isHidden) {
|
||||
if (!isHidden) {
|
||||
if (IodineGUI.suspended) {
|
||||
IodineGUI.suspended = false;
|
||||
IodineGUI.Iodine.play();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (document.getElementById("play").className == "hide") {
|
||||
IodineGUI.Iodine.pause();
|
||||
IodineGUI.suspended = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
function stepVolume(delta) {
|
||||
var volume = document.getElementById("volume").value / 100;
|
||||
volume = Math.min(Math.max(volume + delta, 0), 1);
|
||||
IodineGUI.mixerInput.setVolume(volume);
|
||||
document.getElementById("volume").value = Math.round(volume * 100);
|
||||
}
|
||||
function volChangeFunc() {
|
||||
var volume = Math.min(Math.max(parseInt(this.value), 0), 100) * 0.01;
|
||||
setValue("volume", +volume);
|
||||
IodineGUI.mixerInput.setVolume(+volume);
|
||||
};
|
||||
function speedChangeFunc() {
|
||||
var speed = Math.min(Math.max(parseInt(this.value), 0), 100) / 50;
|
||||
speed = speed * speed;
|
||||
IodineGUI.Iodine.setSpeed(+speed);
|
||||
}
|
||||
function writeRedTemporaryText(textString) {
|
||||
if (IodineGUI.GUITimerID) {
|
||||
clearTimeout(IodineGUI.GUITimerID);
|
||||
}
|
||||
document.getElementById("tempMessage").style.display = "block";
|
||||
document.getElementById("tempMessage").textContent = textString;
|
||||
IodineGUI.GUITimerID = setTimeout(clearTempString, 5000);
|
||||
}
|
||||
function clearTempString() {
|
||||
document.getElementById("tempMessage").style.display = "none";
|
||||
}
|
||||
function resizeCanvasFunc() {
|
||||
var container = document.getElementById("main");
|
||||
var containerHeight = container.clientHeight || container.offsetHeight || 0;
|
||||
var containerWidth = container.clientWidth || container.offsetWidth || 0;
|
||||
if (containerHeight > 0 && containerWidth > 0) {
|
||||
var canvas = document.getElementById("emulator_target");
|
||||
var maxWidth = Math.floor(containerHeight * 1.5);
|
||||
var maxHeight = Math.floor(containerWidth / 1.5);
|
||||
var height = Math.min(maxHeight, containerHeight);
|
||||
var width = Math.min(maxWidth, containerWidth);
|
||||
canvas.style.width = width + "px";
|
||||
canvas.style.height = height + "px";
|
||||
}
|
||||
}
|
||||
function rebuildSavesMenu(e) {
|
||||
if (didNotEnter(document.getElementById("saves_menu_container"), e)) {
|
||||
ExportSave();
|
||||
rebuildExistingSaves();
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
function rebuildExistingSaves() {
|
||||
var menu = document.getElementById("existing_saves_list");
|
||||
ExportSave();
|
||||
removeChildNodes(menu);
|
||||
var keys = getSavesKeys();
|
||||
while (keys.length > 0) {
|
||||
addExistingSaveItem(menu, keys.shift());
|
||||
}
|
||||
}
|
||||
function addExistingSaveItem(menu, key) {
|
||||
var listItem = document.createElement("li");
|
||||
listItem.className = "nowrap";
|
||||
var spanItem = document.createElement("span");
|
||||
spanItem.textContent = decodeKeyType(key);
|
||||
listItem.appendChild(spanItem);
|
||||
var submenu = document.createElement("ul");
|
||||
var submenuItem = document.createElement("li");
|
||||
submenuItem.className = "nowrap";
|
||||
addEvent("click", submenuItem, function () {
|
||||
deleteValue(key);
|
||||
rebuildExistingSaves();
|
||||
});
|
||||
var submenuSpan = document.createElement("span");
|
||||
submenuSpan.textContent = "Delete";
|
||||
submenuItem.appendChild(submenuSpan);
|
||||
submenu.appendChild(submenuItem);
|
||||
var submenuItem2 = document.createElement("li");
|
||||
submenuItem2.className = "nowrap";
|
||||
var link1 = document.createElement("a");
|
||||
link1.href = "data:application/octet-stream;base64," + base64(generateBlob(key, findValue(key)));
|
||||
link1.download = key + "_" + ((new Date()).getTime()) + ".export";
|
||||
link1.textContent = "Download as import compatible";
|
||||
submenuItem2.appendChild(link1);
|
||||
submenu.appendChild(submenuItem2);
|
||||
var submenuItem3 = document.createElement("li");
|
||||
submenuItem3.className = "nowrap";
|
||||
var link2 = document.createElement("a");
|
||||
//Saves are already encoded in base64:
|
||||
link2.href = "data:application/octet-stream;base64," + findValue(key);
|
||||
link2.download = key + "_" + ((new Date()).getTime()) + ".sav";
|
||||
link2.textContent = "Download as raw binary";
|
||||
submenuItem3.appendChild(link2);
|
||||
submenu.appendChild(submenuItem3);
|
||||
listItem.appendChild(submenu);
|
||||
menu.appendChild(listItem);
|
||||
}
|
||||
function decodeKeyType(key) {
|
||||
if (key.substring(0, 15) == "SAVE_TYPE_GUID_") {
|
||||
return "Game \"" + key.substring(15) + "\" Type Code";
|
||||
}
|
||||
else if (key.substring(0, 10) == "SAVE_GUID_") {
|
||||
return "Game \"" + key.substring(10) + "\" Cartridge Data";
|
||||
}
|
||||
else if (key.substring(0, 15) == "SAVE_RTC_GUID_") {
|
||||
return "Game \"" + key.substring(15) + "\" RTC Data";
|
||||
}
|
||||
return key;
|
||||
}
|
||||
//Some wrappers and extensions for non-DOM3 browsers:
|
||||
function removeChildNodes(node) {
|
||||
while (node.firstChild) {
|
||||
node.removeChild(node.firstChild);
|
||||
}
|
||||
}
|
||||
function didNotEnter(oElement, event) {
|
||||
var target = (typeof event.target != "undefined") ? event.target : event.srcElement;
|
||||
while (target) {
|
||||
if (isSameNode(target, oElement)) {
|
||||
return false;
|
||||
}
|
||||
target = target.parentElement;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function isSameNode(oCheck1, oCheck2) {
|
||||
return (typeof oCheck1.isSameNode == "function") ? oCheck1.isSameNode(oCheck2) : (oCheck1 === oCheck2);
|
||||
}
|
||||
function addEvent(sEvent, oElement, fListener) {
|
||||
try {
|
||||
oElement.addEventListener(sEvent, fListener, false);
|
||||
}
|
||||
catch (error) {
|
||||
oElement.attachEvent("on" + sEvent, fListener); //Pity for IE.
|
||||
}
|
||||
}
|
||||
function removeEvent(sEvent, oElement, fListener) {
|
||||
try {
|
||||
oElement.removeEventListener(sEvent, fListener, false);
|
||||
}
|
||||
catch (error) {
|
||||
oElement.detachEvent("on" + sEvent, fListener); //Pity for IE.
|
||||
}
|
||||
}
|
209
user_scripts/GfxGlueCode.js
Normal file
209
user_scripts/GfxGlueCode.js
Normal file
|
@ -0,0 +1,209 @@
|
|||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2010-2016 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GfxGlueCode(width, height) {
|
||||
this.graphicsFound = false; //Do we have graphics output sink found yet?
|
||||
this.gfxCallback = null; //Optional callback user-supplied for vsync eventing.
|
||||
this.doSmoothing = true; //Texture filter the framebuffer?
|
||||
this.offscreenWidth = width; //Width of the screen.
|
||||
this.offscreenHeight = height; //Height of the screen.
|
||||
this.offscreenRGBCount = this.offscreenWidth * this.offscreenHeight * 3;
|
||||
this.offscreenRGBACount = this.offscreenWidth * this.offscreenHeight * 4;
|
||||
this.initializeVSync(); //Setup the vsync event.
|
||||
this.initializeBuffers(); //Initialize the buffer storage.
|
||||
}
|
||||
GfxGlueCode.prototype.initializeVSync = function () {
|
||||
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
|
||||
var parentObj = this;
|
||||
if (!window.requestAnimationFrame) {
|
||||
//Fallback timer eventing:
|
||||
setInterval(function () {
|
||||
parentObj.vsync();
|
||||
}, 16);
|
||||
}
|
||||
else {
|
||||
//Initialize the rAF eventing:
|
||||
window.requestAnimationFrame(
|
||||
function () {
|
||||
parentObj.vsync();
|
||||
parentObj.rAFKeepAlive();
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
GfxGlueCode.prototype.rAFKeepAlive = function () {
|
||||
//Keep the vsync event requested:
|
||||
var parentObj = this;
|
||||
window.requestAnimationFrame(function () {
|
||||
parentObj.vsync();
|
||||
parentObj.rAFKeepAlive();
|
||||
});
|
||||
}
|
||||
GfxGlueCode.prototype.attachCanvas = function (canvas) {
|
||||
this.canvas = canvas;
|
||||
this.graphicsFound = this.initializeCanvasTarget();
|
||||
}
|
||||
GfxGlueCode.prototype.detachCanvas = function () {
|
||||
this.canvas = null;
|
||||
}
|
||||
GfxGlueCode.prototype.attachGfxCallback = function (gfxCallback) {
|
||||
if (typeof gfxCallback == "function") {
|
||||
this.gfxCallback = gfxCallback;
|
||||
}
|
||||
}
|
||||
GfxGlueCode.prototype.attachGfxPostCallback = function (gfxPostCallback) {
|
||||
if (typeof gfxPostCallback == "function") {
|
||||
this.gfxPostCallback = gfxPostCallback;
|
||||
}
|
||||
}
|
||||
GfxGlueCode.prototype.vsync = function () {
|
||||
if (this.graphicsFound) {
|
||||
if (typeof this.gfxCallback == "function") {
|
||||
//Let the user supplied code prepare a frame or two:
|
||||
this.gfxCallback();
|
||||
}
|
||||
//Draw a frame, if ready:
|
||||
this.requestDraw();
|
||||
}
|
||||
}
|
||||
GfxGlueCode.prototype.initializeBuffers = function () {
|
||||
this.swizzledFrameFree = [getUint8Array(this.offscreenRGBCount), getUint8Array(this.offscreenRGBCount)];
|
||||
this.swizzledFrameReady = [];
|
||||
}
|
||||
GfxGlueCode.prototype.recomputeDimension = function () {
|
||||
//Cache some dimension info:
|
||||
this.canvasLastWidth = this.canvas.clientWidth;
|
||||
this.canvasLastHeight = this.canvas.clientHeight;
|
||||
if ((navigator.userAgent.toLowerCase().indexOf("gecko") != -1 && navigator.userAgent.toLowerCase().indexOf("like gecko") == -1)) { //Sniff out firefox for selecting this path.
|
||||
//Set target as unscaled:
|
||||
this.onscreenWidth = this.canvas.width = this.offscreenWidth;
|
||||
this.onscreenHeight = this.canvas.height = this.offscreenHeight;
|
||||
}
|
||||
else {
|
||||
//Set target canvas as scaled:
|
||||
this.onscreenWidth = this.canvas.width = this.canvas.clientWidth;
|
||||
this.onscreenHeight = this.canvas.height = this.canvas.clientHeight;
|
||||
}
|
||||
}
|
||||
GfxGlueCode.prototype.initializeCanvasTarget = function () {
|
||||
try {
|
||||
//Obtain dimensional information:
|
||||
this.recomputeDimension();
|
||||
//Get handles on the canvases:
|
||||
this.canvasOffscreen = document.createElement("canvas");
|
||||
this.canvasOffscreen.width = this.offscreenWidth;
|
||||
this.canvasOffscreen.height = this.offscreenHeight;
|
||||
this.drawContextOffscreen = this.canvasOffscreen.getContext("2d");
|
||||
this.drawContextOnscreen = this.canvas.getContext("2d");
|
||||
//Initialize the canvas backing buffer:
|
||||
this.initializeCanvasBuffer();
|
||||
//Success:
|
||||
return true;
|
||||
}
|
||||
catch (error) {
|
||||
//Failure:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
GfxGlueCode.prototype.initializeCanvasBuffer = function () {
|
||||
//Get a CanvasPixelArray buffer:
|
||||
this.canvasBuffer = this.getBuffer(this.drawContextOffscreen, this.offscreenWidth, this.offscreenHeight);
|
||||
//Initialize Alpha Channel:
|
||||
this.initializeAlpha(this.canvasBuffer.data);
|
||||
}
|
||||
GfxGlueCode.prototype.initializeAlpha = function (canvasData) {
|
||||
var length = canvasData.length;
|
||||
for (var indexGFXIterate = 3; indexGFXIterate < length; indexGFXIterate += 4) {
|
||||
canvasData[indexGFXIterate] = 0xFF;
|
||||
}
|
||||
}
|
||||
GfxGlueCode.prototype.getBuffer = function (canvasContext, width, height) {
|
||||
//Get a CanvasPixelArray buffer:
|
||||
var buffer = null;
|
||||
try {
|
||||
buffer = this.drawContextOffscreen.createImageData(width, height);
|
||||
}
|
||||
catch (error) {
|
||||
buffer = this.drawContextOffscreen.getImageData(0, 0, width, height);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
if (__VIEWS_SUPPORTED__) {
|
||||
GfxGlueCode.prototype.copyBuffer = function (buffer) {
|
||||
if (this.graphicsFound) {
|
||||
if (this.swizzledFrameFree.length == 0) {
|
||||
this.swizzledFrameFree.push(this.swizzledFrameReady.shift());
|
||||
}
|
||||
var swizzledFrame = this.swizzledFrameFree.shift();
|
||||
swizzledFrame.set(buffer);
|
||||
this.swizzledFrameReady.push(swizzledFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
GfxGlueCode.prototype.copyBuffer = function (buffer) {
|
||||
if (this.graphicsFound) {
|
||||
if (this.swizzledFrameFree.length == 0) {
|
||||
this.swizzledFrameFree.push(this.swizzledFrameReady.shift());
|
||||
}
|
||||
var swizzledFrame = this.swizzledFrameFree.shift();
|
||||
for (var bufferIndex = 0; bufferIndex < this.offscreenRGBCount; bufferIndex++) {
|
||||
swizzledFrame[bufferIndex] = buffer[bufferIndex];
|
||||
}
|
||||
this.swizzledFrameReady.push(swizzledFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
GfxGlueCode.prototype.requestDraw = function () {
|
||||
if (this.swizzledFrameReady.length > 0) {
|
||||
var canvasData = this.canvasBuffer.data;
|
||||
var swizzledFrame = this.swizzledFrameReady.shift();
|
||||
for (var canvasIndex = 0, bufferIndex = 0; canvasIndex < this.offscreenRGBACount; ++canvasIndex) {
|
||||
canvasData[canvasIndex++] = swizzledFrame[bufferIndex++];
|
||||
canvasData[canvasIndex++] = swizzledFrame[bufferIndex++];
|
||||
canvasData[canvasIndex++] = swizzledFrame[bufferIndex++];
|
||||
}
|
||||
this.swizzledFrameFree.push(swizzledFrame);
|
||||
this.graphicsBlit();
|
||||
if (typeof this.gfxPostCallback == "function") {
|
||||
//Some UI element redraw:
|
||||
this.gfxPostCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
GfxGlueCode.prototype.graphicsBlit = function () {
|
||||
if (this.canvasLastWidth != this.canvas.clientWidth || this.canvasLastHeight != this.canvas.clientHeight) {
|
||||
this.recomputeDimension();
|
||||
this.processSmoothing();
|
||||
}
|
||||
if (this.offscreenWidth == this.onscreenWidth && this.offscreenHeight == this.onscreenHeight) {
|
||||
//Canvas does not need to scale, draw directly to final:
|
||||
this.drawContextOnscreen.putImageData(this.canvasBuffer, 0, 0);
|
||||
}
|
||||
else {
|
||||
//Canvas needs to scale, draw to offscreen first:
|
||||
this.drawContextOffscreen.putImageData(this.canvasBuffer, 0, 0);
|
||||
//Scale offscreen canvas image onto the final:
|
||||
this.drawContextOnscreen.drawImage(this.canvasOffscreen, 0, 0, this.onscreenWidth, this.onscreenHeight);
|
||||
}
|
||||
}
|
||||
GfxGlueCode.prototype.setSmoothScaling = function (doSmoothing) {
|
||||
this.doSmoothing = !!doSmoothing;
|
||||
this.processSmoothing();
|
||||
}
|
||||
GfxGlueCode.prototype.processSmoothing = function () {
|
||||
if (this.graphicsFound) {
|
||||
this.canvas.className = (this.doSmoothing) ? "textureSmooth" : "texturePixelated";
|
||||
this.drawContextOnscreen.mozImageSmoothingEnabled = this.doSmoothing;
|
||||
this.drawContextOnscreen.webkitImageSmoothingEnabled = this.doSmoothing;
|
||||
this.drawContextOnscreen.imageSmoothingEnabled = this.doSmoothing;
|
||||
}
|
||||
}
|
|
@ -1,214 +0,0 @@
|
|||
"use strict";
|
||||
/*
|
||||
* This file is part of IodineGBA
|
||||
*
|
||||
* Copyright (C) 2012-2013 Grant Galitz
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
* The full license is available at http://www.gnu.org/licenses/gpl.html
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
var games = {
|
||||
"advancewars":"Advance Wars",
|
||||
"advancewars2":"Advance Wars 2",
|
||||
"aladdin":"Aladdin",
|
||||
"alienhominid":"Alien Hominid",
|
||||
"bomberman_max2blue":"Bomberman Max 2 - Blue Advance",
|
||||
"bomberman_tournament":"Bomberman Tournament",
|
||||
"bubblebobble":"Bubble Bobble",
|
||||
"croket1":"Croket! - Yume no Banker Survival!",
|
||||
"croket2":"Croket! 2 - Yami no Bank to Banqueen",
|
||||
"croket3":"Croket! 3 - Granu Oukoku no Nazo",
|
||||
"croket4":"Croket! 4 - Bank no Mori no Mamorigami",
|
||||
"digimon_racing":"Digimon Racing",
|
||||
"dbz_supersonic":"Dragon Ball Z - Supersonic Warriors",
|
||||
"drilldozer":"Drill Dozer",
|
||||
"earthwormjim":"Earthworm Jim",
|
||||
"earthwormjim2":"Earthworm Jim 2",
|
||||
"ff1and2":"Final Fantasy 1 & 2 Advance",
|
||||
"ff4S":"Final Fantasy IV Advance (Sound Restoration Mod)",
|
||||
"ff6":"Final Fantasy VI Advance",
|
||||
"final_fantasy_tactics":"Final Fantasy Tactics Advance",
|
||||
"fire_emblem":"Fire Emblem",
|
||||
"frogger1":"Frogger Advance - The Great Quest",
|
||||
"frogger2":"Frogger's Adventures - Temple of the Frog",
|
||||
"frogger3":"Frogger's Adventures 2 - The Lost Wand",
|
||||
"fzero_gp":"F-Zero - GP Legend",
|
||||
"fzero_max":"F-Zero - Maximum Velocity",
|
||||
"gamewatch4":"Game & Watch Gallery 4",
|
||||
"goldensun":"Golden Sun",
|
||||
"gunstar_super_heroes":"Gunstar Super Heroes",
|
||||
"hamtaro_heartbreak":"Hamtaro - Ham-Ham Heartbreak",
|
||||
"kirbymirror":"Kirby & The Amazing Mirror",
|
||||
"kirbynightmare":"Kirby: Nightmare in Dreamland",
|
||||
"mariokart":"Mario Kart: Super Circuit",
|
||||
"marioparty":"Mario Party Advance",
|
||||
"mariopinball":"Mario Pinball Land",
|
||||
"megamanbass":"Megaman & Bass",
|
||||
"megaman_battle1":"Megaman Battle Network 1",
|
||||
"megaman_battle2":"Megaman Battle Network 2",
|
||||
"megaman_battle3_blue":"Megaman Battle Network 3 Blue",
|
||||
"megaman_battle4_blue":"Megaman Battle Network 4 Blue Moon",
|
||||
"megaman_battle4_red":"Megaman Battle Network 4 Red Sun",
|
||||
"megaman_battle5":"Megaman Battle Network 5 Team Protoman",
|
||||
"megaman_battle6":"Megaman Battle Network 6 Cybeast Falzar",
|
||||
"megaman_zero1":"Megaman Zero",
|
||||
"megaman_zero2":"Megaman Zero 2",
|
||||
"megaman_zero3":"Megaman Zero 3",
|
||||
"megaman_zero4":"Megaman Zero 4",
|
||||
"metalslug":"Metal Slug Advance",
|
||||
"metroid_fusion":"Metroid Fusion",
|
||||
"momotarou_dentetsu":"Momotarou Dentetsu G Gold Deck wo Tsukure!",
|
||||
"monopoly":"Monopoly",
|
||||
"monster_force":"Monster Force",
|
||||
"mortal_kombat":"Mortal Kombat Advance",
|
||||
"pacman_world":"Pacman World",
|
||||
"pacman_world2":"Pacman World 2",
|
||||
"pokemonflorasky":"Pokemon Flora Sky Rom Hack",
|
||||
"pokemonemerald":"Pokemon Emerald",
|
||||
"pokemongreen":"Pokemon Leaf Green",
|
||||
"mysteryred":"Pokemon Mystery Dungeon Red",
|
||||
"pokemonruby":"Pokemon Ruby",
|
||||
"pokemonsapphire":"Pokemon Sapphire",
|
||||
"pokemonred":"Pokemon Fire Red",
|
||||
"gba_video_pokemon_1":"Pokemon Video Pak 1",
|
||||
"gba_video_pokemon_2":"Pokemon Video Pak 2",
|
||||
"gba_video_pokemon_3":"Pokemon Video Pak 3",
|
||||
"gba_video_pokemon_4":"Pokemon Video Pak 4",
|
||||
"sonic_advance":"Sonic Advance",
|
||||
"sonic_advance2":"Sonic Advance 2",
|
||||
"sonic_advance3":"Sonic Advance 3",
|
||||
"sonicbattle":"Sonic Battle",
|
||||
"supermonkeyballjr":"Super Monkey Ball Jr",
|
||||
"superstar":"Mario & Luigi: Superstar Saga",
|
||||
"supermarioadvance":"Super Mario Advance",
|
||||
"supermarioadvance2":"Super Mario Advance 2",
|
||||
"supermarioadvance3":"Super Mario Advance 3",
|
||||
"supermarioadvance4":"Super Mario Advance 4",
|
||||
"simpsons":"The Simpsons: Road Rage",
|
||||
"sonicpinball":"Sonic Pinball",
|
||||
"super_street_fighter_2_turbo_revival":"Super Street Fighter II: Turbo Revival",
|
||||
"super_street_fighter_3_alpha":"Super Street Fighter III: Alpha",
|
||||
"tales_of_phantasia":"Tales of Phantasia",
|
||||
"tak2_staff_of_dreams":"Tak 2: The Staff of Dreams",
|
||||
"tetris_worlds":"Tetris Worlds",
|
||||
"tmnt":"Teenage Mutant Ninja Turtles",
|
||||
"sims_bustin_out":"The Sims: Bustin' Out",
|
||||
"sims2":"The Sims 2",
|
||||
"spyro_adventure":"Spyro Adventure",
|
||||
"spyro_ice":"Spyro: Season of Ice",
|
||||
"spyro_flame":"Spyro 2: Season of Flame",
|
||||
"turok_evolution":"Turok Evolution",
|
||||
"warioland4":"Wario Land 4",
|
||||
"wario_ware":"Wario Ware Inc",
|
||||
"zelda_past":"The Legend of Zelda: A Link to the Past",
|
||||
"zelda_minish":"The Legend of Zelda: The Minish Cap"
|
||||
};
|
||||
var Iodine = null;
|
||||
var Blitter = null;
|
||||
var Mixer = null;
|
||||
var MixerInput = null;
|
||||
var timerID = null;
|
||||
window.onload = function () {
|
||||
if (!games[location.hash.substr(1)]) {
|
||||
alert("Invalid game request!");
|
||||
return;
|
||||
}
|
||||
//Initialize Iodine:
|
||||
Iodine = new GameBoyAdvanceEmulator();
|
||||
//Initialize the graphics:
|
||||
registerBlitterHandler();
|
||||
//Initialize the audio:
|
||||
registerAudioHandler();
|
||||
//Register the save handler callbacks:
|
||||
registerSaveHandlers();
|
||||
//Hook the GUI controls.
|
||||
registerGUIEvents();
|
||||
//Enable Sound:
|
||||
Iodine.enableAudio();
|
||||
//Download the BIOS:
|
||||
downloadBIOS();
|
||||
}
|
||||
function downloadBIOS() {
|
||||
downloadFile("Binaries/gba_bios.bin", registerBIOS);
|
||||
}
|
||||
function registerBIOS() {
|
||||
processDownload(this, attachBIOS);
|
||||
downloadROM(location.hash.substr(1));
|
||||
}
|
||||
function downloadROM(gamename) {
|
||||
Iodine.pause();
|
||||
showTempString("Downloading \"" + games[gamename] + ".\"");
|
||||
downloadFile("Binaries/" + gamename + ".gba", registerROM);
|
||||
}
|
||||
function registerROM() {
|
||||
clearTempString();
|
||||
processDownload(this, attachROM);
|
||||
if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPod/i) || navigator.userAgent.match(/iPad/i)) {
|
||||
Iodine.disableAudio();
|
||||
}
|
||||
Iodine.play();
|
||||
}
|
||||
function registerBlitterHandler() {
|
||||
Blitter = new GlueCodeGfx();
|
||||
Blitter.attachCanvas(document.getElementById("emulator_target"));
|
||||
Blitter.setSmoothScaling(false);
|
||||
Iodine.attachGraphicsFrameHandler(function (buffer) {Blitter.copyBuffer(buffer);});
|
||||
}
|
||||
function registerAudioHandler() {
|
||||
Mixer = new GlueCodeMixer();
|
||||
MixerInput = new GlueCodeMixerInput(Mixer);
|
||||
Iodine.attachAudioHandler(MixerInput);
|
||||
}
|
||||
function registerGUIEvents() {
|
||||
addEvent("keydown", document, keyDown);
|
||||
addEvent("keyup", document, keyUpPreprocess);
|
||||
addEvent("unload", window, ExportSave);
|
||||
Iodine.attachSpeedHandler(function (speed) {
|
||||
document.title = games[location.hash.substr(1)] + " - " + speed;
|
||||
});
|
||||
}
|
||||
function lowerVolume() {
|
||||
Iodine.incrementVolume(-0.04);
|
||||
}
|
||||
function raiseVolume() {
|
||||
Iodine.incrementVolume(0.04);
|
||||
}
|
||||
function writeRedTemporaryText(textString) {
|
||||
if (timerID) {
|
||||
clearTimeout(timerID);
|
||||
}
|
||||
showTempString(textString);
|
||||
timerID = setTimeout(clearTempString, 5000);
|
||||
}
|
||||
function showTempString(textString) {
|
||||
document.getElementById("tempMessage").style.display = "block";
|
||||
document.getElementById("tempMessage").textContent = textString;
|
||||
}
|
||||
function clearTempString() {
|
||||
document.getElementById("tempMessage").style.display = "none";
|
||||
}
|
||||
//Some wrappers and extensions for non-DOM3 browsers:
|
||||
function addEvent(sEvent, oElement, fListener) {
|
||||
try {
|
||||
oElement.addEventListener(sEvent, fListener, false);
|
||||
}
|
||||
catch (error) {
|
||||
oElement.attachEvent("on" + sEvent, fListener); //Pity for IE.
|
||||
}
|
||||
}
|
||||
function removeEvent(sEvent, oElement, fListener) {
|
||||
try {
|
||||
oElement.removeEventListener(sEvent, fListener, false);
|
||||
}
|
||||
catch (error) {
|
||||
oElement.detachEvent("on" + sEvent, fListener); //Pity for IE.
|
||||
}
|
||||
}
|
|
@ -1,193 +0,0 @@
|
|||
"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 GlueCodeGfx() {
|
||||
this.didRAF = false; //Set when rAF has been used.
|
||||
this.graphicsFound = 0; //Do we have graphics output sink found yet?
|
||||
this.offscreenWidth = 240; //Width of the GBA screen.
|
||||
this.offscreenHeight = 160; //Height of the GBA screen.
|
||||
this.doSmoothing = true;
|
||||
//Cache some frame buffer lengths:
|
||||
var offscreenRGBCount = this.offscreenWidth * this.offscreenHeight * 3;
|
||||
this.swizzledFrameFree = [getUint8Array(offscreenRGBCount), getUint8Array(offscreenRGBCount)];
|
||||
this.swizzledFrameReady = [];
|
||||
this.initializeGraphicsBuffer(); //Pre-set the swizzled buffer for first frame.
|
||||
}
|
||||
GlueCodeGfx.prototype.attachCanvas = function (canvas) {
|
||||
this.canvas = canvas;
|
||||
this.graphicsFound = this.initializeCanvasTarget();
|
||||
this.setSmoothScaling(this.doSmoothing);
|
||||
}
|
||||
GlueCodeGfx.prototype.detachCanvas = function () {
|
||||
this.canvas = null;
|
||||
}
|
||||
GlueCodeGfx.prototype.recomputeDimension = function () {
|
||||
//Cache some dimension info:
|
||||
this.canvasLastWidth = this.canvas.clientWidth;
|
||||
this.canvasLastHeight = this.canvas.clientHeight;
|
||||
if (window.mozRequestAnimationFrame) { //Sniff out firefox for selecting this path.
|
||||
//Set target as unscaled:
|
||||
this.onscreenWidth = this.canvas.width = this.offscreenWidth;
|
||||
this.onscreenHeight = this.canvas.height = this.offscreenHeight;
|
||||
}
|
||||
else {
|
||||
//Set target canvas as scaled:
|
||||
this.onscreenWidth = this.canvas.width = this.canvas.clientWidth;
|
||||
this.onscreenHeight = this.canvas.height = this.canvas.clientHeight;
|
||||
}
|
||||
}
|
||||
GlueCodeGfx.prototype.initializeCanvasTarget = function () {
|
||||
try {
|
||||
//Obtain dimensional information:
|
||||
this.recomputeDimension();
|
||||
//Get handles on the canvases:
|
||||
this.canvasOffscreen = document.createElement("canvas");
|
||||
this.canvasOffscreen.width = this.offscreenWidth;
|
||||
this.canvasOffscreen.height = this.offscreenHeight;
|
||||
this.drawContextOffscreen = this.canvasOffscreen.getContext("2d");
|
||||
this.drawContextOnscreen = this.canvas.getContext("2d");
|
||||
//Get a CanvasPixelArray buffer:
|
||||
this.canvasBuffer = this.getBuffer(this.drawContextOffscreen, this.offscreenWidth, this.offscreenHeight);
|
||||
//Initialize Alpha Channel:
|
||||
this.initializeAlpha(this.canvasBuffer.data);
|
||||
//Draw swizzled buffer out as a test:
|
||||
this.requestDraw();
|
||||
this.checkRAF();
|
||||
//Success:
|
||||
return true;
|
||||
}
|
||||
catch (error) {
|
||||
//Failure:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
GlueCodeGfx.prototype.setSmoothScaling = function (doSmoothing) {
|
||||
this.doSmoothing = doSmoothing;
|
||||
if (this.graphicsFound) {
|
||||
this.canvas.setAttribute("style", (this.canvas.getAttribute("style") || "") + "; image-rendering: " + ((doSmoothing) ? "auto" : "-webkit-optimize-contrast") + ";" +
|
||||
"image-rendering: " + ((doSmoothing) ? "optimizeQuality" : "-o-crisp-edges") + ";" +
|
||||
"image-rendering: " + ((doSmoothing) ? "optimizeQuality" : "-moz-crisp-edges") + ";" +
|
||||
"-ms-interpolation-mode: " + ((doSmoothing) ? "bicubic" : "nearest-neighbor") + ";");
|
||||
this.drawContextOnscreen.mozImageSmoothingEnabled = doSmoothing;
|
||||
this.drawContextOnscreen.webkitImageSmoothingEnabled = doSmoothing;
|
||||
this.drawContextOnscreen.imageSmoothingEnabled = doSmoothing;
|
||||
}
|
||||
}
|
||||
GlueCodeGfx.prototype.initializeAlpha = function (canvasData) {
|
||||
var length = canvasData.length;
|
||||
for (var indexGFXIterate = 3; indexGFXIterate < length; indexGFXIterate += 4) {
|
||||
canvasData[indexGFXIterate] = 0xFF;
|
||||
}
|
||||
}
|
||||
GlueCodeGfx.prototype.getBuffer = function (canvasContext, width, height) {
|
||||
//Get a CanvasPixelArray buffer:
|
||||
var buffer = null;
|
||||
try {
|
||||
buffer = this.drawContextOffscreen.createImageData(width, height);
|
||||
}
|
||||
catch (error) {
|
||||
buffer = this.drawContextOffscreen.getImageData(0, 0, width, height);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
GlueCodeGfx.prototype.copyBuffer = function (buffer) {
|
||||
if (this.graphicsFound) {
|
||||
if (this.swizzledFrameFree.length == 0) {
|
||||
if (this.didRAF) {
|
||||
this.requestDrawSingle();
|
||||
}
|
||||
else {
|
||||
this.swizzledFrameFree.push(this.swizzledFrameReady.shift());
|
||||
}
|
||||
}
|
||||
var swizzledFrame = this.swizzledFrameFree.shift();
|
||||
var length = swizzledFrame.length;
|
||||
if (buffer.buffer) {
|
||||
swizzledFrame.set(buffer);
|
||||
}
|
||||
else {
|
||||
for (var bufferIndex = 0; bufferIndex < length; ++bufferIndex) {
|
||||
swizzledFrame[bufferIndex] = buffer[bufferIndex];
|
||||
}
|
||||
}
|
||||
this.swizzledFrameReady.push(swizzledFrame);
|
||||
if (!window.requestAnimationFrame) {
|
||||
this.requestDraw();
|
||||
}
|
||||
else if (!this.didRAF) {
|
||||
//Prime RAF draw:
|
||||
var parentObj = this;
|
||||
window.requestAnimationFrame(function () {
|
||||
if (parentObj.canvas) {
|
||||
parentObj.requestRAFDraw();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
GlueCodeGfx.prototype.requestRAFDraw = function () {
|
||||
this.didRAF = true;
|
||||
this.requestDraw();
|
||||
}
|
||||
GlueCodeGfx.prototype.requestDrawSingle = function () {
|
||||
if (this.swizzledFrameReady.length > 0) {
|
||||
var canvasData = this.canvasBuffer.data;
|
||||
var bufferIndex = 0;
|
||||
var swizzledFrame = this.swizzledFrameReady.shift();
|
||||
var length = canvasData.length;
|
||||
for (var canvasIndex = 0; canvasIndex < length; ++canvasIndex) {
|
||||
canvasData[canvasIndex++] = swizzledFrame[bufferIndex++];
|
||||
canvasData[canvasIndex++] = swizzledFrame[bufferIndex++];
|
||||
canvasData[canvasIndex++] = swizzledFrame[bufferIndex++];
|
||||
}
|
||||
this.swizzledFrameFree.push(swizzledFrame);
|
||||
this.graphicsBlit();
|
||||
}
|
||||
}
|
||||
GlueCodeGfx.prototype.requestDraw = function () {
|
||||
this.requestDrawSingle();
|
||||
if (this.didRAF) {
|
||||
var parentObj = this;
|
||||
window.requestAnimationFrame(function () {
|
||||
if (parentObj.canvas) {
|
||||
parentObj.requestDraw();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
GlueCodeGfx.prototype.graphicsBlit = function () {
|
||||
if (this.canvasLastWidth != this.canvas.clientWidth || this.canvasLastHeight != this.canvas.clientHeight) {
|
||||
this.recomputeDimension();
|
||||
this.setSmoothScaling(this.doSmoothing);
|
||||
}
|
||||
if (this.offscreenWidth == this.onscreenWidth && this.offscreenHeight == this.onscreenHeight) {
|
||||
//Canvas does not need to scale, draw directly to final:
|
||||
this.drawContextOnscreen.putImageData(this.canvasBuffer, 0, 0);
|
||||
}
|
||||
else {
|
||||
//Canvas needs to scale, draw to offscreen first:
|
||||
this.drawContextOffscreen.putImageData(this.canvasBuffer, 0, 0);
|
||||
//Scale offscreen canvas image onto the final:
|
||||
this.drawContextOnscreen.drawImage(this.canvasOffscreen, 0, 0, this.onscreenWidth, this.onscreenHeight);
|
||||
}
|
||||
}
|
||||
GlueCodeGfx.prototype.initializeGraphicsBuffer = function () {
|
||||
//Initialize the first frame to a white screen:
|
||||
var swizzledFrame = this.swizzledFrameFree.shift();
|
||||
var length = swizzledFrame.length;
|
||||
for (var bufferIndex = 0; bufferIndex < length; ++bufferIndex) {
|
||||
swizzledFrame[bufferIndex] = 0xF8;
|
||||
}
|
||||
this.swizzledFrameReady.push(swizzledFrame);
|
||||
}
|
||||
GlueCodeGfx.prototype.checkRAF = function () {
|
||||
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
"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.
|
||||
*/
|
||||
var keyZones = [
|
||||
//Use this to control the key mapping:
|
||||
//A:
|
||||
[88, 74],
|
||||
//B:
|
||||
[90, 81, 89],
|
||||
//Select:
|
||||
[16],
|
||||
//Start:
|
||||
[13],
|
||||
//Right:
|
||||
[39],
|
||||
//Left:
|
||||
[37],
|
||||
//Up:
|
||||
[38],
|
||||
//Down:
|
||||
[40],
|
||||
//R:
|
||||
[50],
|
||||
//L:
|
||||
[49]
|
||||
];
|
||||
function keyDown(e) {
|
||||
var keyCode = e.keyCode | 0;
|
||||
for (var keyMapIndex = 0; (keyMapIndex | 0) < 10; keyMapIndex = ((keyMapIndex | 0) + 1) | 0) {
|
||||
var keysMapped = keyZones[keyMapIndex | 0];
|
||||
var keysTotal = keysMapped.length | 0;
|
||||
for (var matchingIndex = 0; (matchingIndex | 0) < (keysTotal | 0); matchingIndex = ((matchingIndex | 0) + 1) | 0) {
|
||||
if ((keysMapped[matchingIndex | 0] | 0) == (keyCode | 0)) {
|
||||
Iodine.keyDown(keyMapIndex | 0);
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function keyUp(keyCode) {
|
||||
keyCode = keyCode | 0;
|
||||
for (var keyMapIndex = 0; (keyMapIndex | 0) < 10; keyMapIndex = ((keyMapIndex | 0) + 1) | 0) {
|
||||
var keysMapped = keyZones[keyMapIndex | 0];
|
||||
var keysTotal = keysMapped.length | 0;
|
||||
for (var matchingIndex = 0; (matchingIndex | 0) < (keysTotal | 0); matchingIndex = ((matchingIndex | 0) + 1) | 0) {
|
||||
if ((keysMapped[matchingIndex | 0] | 0) == (keyCode | 0)) {
|
||||
Iodine.keyUp(keyMapIndex | 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function keyUpPreprocess(e) {
|
||||
var keyCode = e.keyCode | 0;
|
||||
switch (keyCode | 0) {
|
||||
case 68:
|
||||
lowerVolume();
|
||||
break;
|
||||
case 82:
|
||||
raiseVolume();
|
||||
break;
|
||||
case 51:
|
||||
Iodine.incrementSpeed(0.10);
|
||||
break;
|
||||
case 52:
|
||||
Iodine.incrementSpeed(-0.10);
|
||||
break;
|
||||
default:
|
||||
//Control keys / other
|
||||
keyUp(keyCode);
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2013 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function ImportSaveCallback(name) {
|
||||
try {
|
||||
var save = findValue("SAVE_" + name);
|
||||
if (save != null) {
|
||||
writeRedTemporaryText("Loaded save.");
|
||||
return base64ToArray(save);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
alert("Could not read save: " + error.message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function ExportSave() {
|
||||
Iodine.exportSave();
|
||||
}
|
||||
function ExportSaveCallback(name, save) {
|
||||
if (name != "") {
|
||||
try {
|
||||
setValue("SAVE_" + name, arrayToBase64(save));
|
||||
}
|
||||
catch (error) {
|
||||
alert("Could not store save: " + error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
function registerSaveHandlers() {
|
||||
Iodine.attachSaveExportHandler(ExportSaveCallback);
|
||||
Iodine.attachSaveImportHandler(ImportSaveCallback);
|
||||
}
|
||||
//Wrapper for localStorage getItem, so that data can be retrieved in various types.
|
||||
function findValue(key) {
|
||||
try {
|
||||
if (window.localStorage.getItem(key) != null) {
|
||||
return JSON.parse(window.localStorage.getItem(key));
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
//An older Gecko 1.8.1/1.9.0 method of storage (Deprecated due to the obvious security hole):
|
||||
if (window.globalStorage[location.hostname].getItem(key) != null) {
|
||||
return JSON.parse(window.globalStorage[location.hostname].getItem(key));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//Wrapper for localStorage setItem, so that data can be set in various types.
|
||||
function setValue(key, value) {
|
||||
try {
|
||||
window.localStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
catch (error) {
|
||||
//An older Gecko 1.8.1/1.9.0 method of storage (Deprecated due to the obvious security hole):
|
||||
window.globalStorage[location.hostname].setItem(key, JSON.stringify(value));
|
||||
}
|
||||
}
|
||||
//Wrapper for localStorage removeItem, so that data can be set in various types.
|
||||
function deleteValue(key) {
|
||||
try {
|
||||
window.localStorage.removeItem(key);
|
||||
}
|
||||
catch (error) {
|
||||
//An older Gecko 1.8.1/1.9.0 method of storage (Deprecated due to the obvious security hole):
|
||||
window.globalStorage[location.hostname].removeItem(key);
|
||||
}
|
||||
}
|
124
user_scripts/JoyPadGlueCode.js
Normal file
124
user_scripts/JoyPadGlueCode.js
Normal file
|
@ -0,0 +1,124 @@
|
|||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2016 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function keyDown(e) {
|
||||
var keyCode = e.keyCode | 0;
|
||||
for (var keyMapIndex = 0; (keyMapIndex | 0) < 10; keyMapIndex = ((keyMapIndex | 0) + 1) | 0) {
|
||||
if ((IodineGUI.defaults.keyZonesGBA[keyMapIndex | 0] | 0) == (keyCode | 0)) {
|
||||
IodineGUI.Iodine.keyDown(keyMapIndex | 0);
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
function keyUpGBA(keyCode) {
|
||||
keyCode = keyCode | 0;
|
||||
for (var keyMapIndex = 0; (keyMapIndex | 0) < 10; keyMapIndex = ((keyMapIndex | 0) + 1) | 0) {
|
||||
if ((IodineGUI.defaults.keyZonesGBA[keyMapIndex | 0] | 0) == (keyCode | 0)) {
|
||||
IodineGUI.Iodine.keyUp(keyMapIndex | 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
function keyUp(keyCode) {
|
||||
keyCode = keyCode | 0;
|
||||
for (var keyMapIndex = 0; (keyMapIndex | 0) < 8; keyMapIndex = ((keyMapIndex | 0) + 1) | 0) {
|
||||
if ((IodineGUI.defaults.keyZonesControl[keyMapIndex | 0] | 0) == (keyCode | 0)) {
|
||||
keyboardEmulatorControl(keyMapIndex | 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function keyUpPreprocess(e) {
|
||||
var keyCode = e.keyCode | 0;
|
||||
//If we're not mapping a key:
|
||||
if (!IodineGUI.toMap) {
|
||||
//Check for emulator binding:
|
||||
if (!keyUp(keyCode | 0)) {
|
||||
//Check for GBA binding:
|
||||
keyUpGBA(keyCode);
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Map a key binding:
|
||||
IodineGUI.toMap[IodineGUI.toMapIndice | 0] = keyCode | 0;
|
||||
IodineGUI.toMap = null;
|
||||
saveKeyBindings();
|
||||
}
|
||||
}
|
||||
function keyboardEmulatorControl(keyCode) {
|
||||
keyCode = keyCode | 0;
|
||||
switch (keyCode | 0) {
|
||||
case 0:
|
||||
stepVolume(-0.04);
|
||||
break;
|
||||
case 1:
|
||||
stepVolume(0.04);
|
||||
break;
|
||||
case 2:
|
||||
IodineGUI.Iodine.incrementSpeed(0.05);
|
||||
break;
|
||||
case 3:
|
||||
IodineGUI.Iodine.incrementSpeed(-0.05);
|
||||
break;
|
||||
case 4:
|
||||
IodineGUI.Iodine.setSpeed(1);
|
||||
break;
|
||||
case 5:
|
||||
toggleFullScreen();
|
||||
break;
|
||||
case 6:
|
||||
togglePlayState();
|
||||
break;
|
||||
case 7:
|
||||
IodineGUI.Iodine.restart();
|
||||
}
|
||||
}
|
||||
function toggleFullScreen() {
|
||||
if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement ) {
|
||||
if (document.documentElement.requestFullscreen) {
|
||||
document.documentElement.requestFullscreen();
|
||||
}
|
||||
else if (document.documentElement.msRequestFullscreen) {
|
||||
document.documentElement.msRequestFullscreen();
|
||||
}
|
||||
else if (document.documentElement.mozRequestFullScreen) {
|
||||
document.documentElement.mozRequestFullScreen();
|
||||
}
|
||||
else if (document.documentElement.webkitRequestFullscreen) {
|
||||
document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
}
|
||||
else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
function togglePlayState() {
|
||||
if (IodineGUI.isPlaying) {
|
||||
IodineGUI.Iodine.pause();
|
||||
}
|
||||
else {
|
||||
IodineGUI.Iodine.play();
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2013 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:
|
||||
|
||||
|
@ -10,18 +10,18 @@
|
|||
*/
|
||||
function attachBIOS(BIOS) {
|
||||
try {
|
||||
Iodine.attachBIOS(new Uint8Array(BIOS));
|
||||
IodineGUI.Iodine.attachBIOS(new Uint8Array(BIOS));
|
||||
}
|
||||
catch (error) {
|
||||
Iodine.attachBIOS(BIOS);
|
||||
IodineGUI.Iodine.attachBIOS(BIOS);
|
||||
}
|
||||
}
|
||||
function attachROM(ROM) {
|
||||
try {
|
||||
Iodine.attachROM(new Uint8Array(ROM));
|
||||
IodineGUI.Iodine.attachROM(new Uint8Array(ROM));
|
||||
}
|
||||
catch (error) {
|
||||
Iodine.attachROM(ROM);
|
||||
IodineGUI.Iodine.attachROM(ROM);
|
||||
}
|
||||
}
|
||||
function fileLoadShimCode(files, ROMHandler) {
|
289
user_scripts/SavesGlueCode.js
Normal file
289
user_scripts/SavesGlueCode.js
Normal file
|
@ -0,0 +1,289 @@
|
|||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2010-2017 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function ImportSaveCallback(name, callbackFunc, callbackFuncNoSave) {
|
||||
try {
|
||||
var save = findValue("SAVE_" + name);
|
||||
if (save != null) {
|
||||
writeRedTemporaryText("Loaded save.");
|
||||
callbackFunc(base64ToArray(save));
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
writeRedTemporaryText("Could not read save: " + error.message);
|
||||
}
|
||||
callbackFuncNoSave();
|
||||
}
|
||||
function ExportSave() {
|
||||
IodineGUI.Iodine.exportSave();
|
||||
}
|
||||
function ExportSaveCallback(name, save) {
|
||||
if (name != "") {
|
||||
try {
|
||||
setValue("SAVE_" + name, arrayToBase64(save));
|
||||
}
|
||||
catch (error) {
|
||||
writeRedTemporaryText("Could not store save: " + error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
function registerSaveHandlers() {
|
||||
IodineGUI.Iodine.attachSaveExportHandler(ExportSaveCallback);
|
||||
IodineGUI.Iodine.attachSaveImportHandler(ImportSaveCallback);
|
||||
}
|
||||
function import_save(blobData) {
|
||||
blobData = decodeBlob(blobData);
|
||||
if (blobData && blobData.blobs) {
|
||||
if (blobData.blobs.length > 0) {
|
||||
for (var index = 0; index < blobData.blobs.length; ++index) {
|
||||
var blobID = blobData.blobs[index].blobID;
|
||||
var blobContent = blobData.blobs[index].blobContent;
|
||||
writeRedTemporaryText("Importing blob \"" + blobID + "\"");
|
||||
if (blobContent) {
|
||||
setValue(blobID, blobContent);
|
||||
}
|
||||
else if (blobID) {
|
||||
writeRedTemporaryText("Save file imported had blob \"" + blobID + "\" with no blob data interpretable.");
|
||||
}
|
||||
else {
|
||||
writeRedTemporaryText("Blob chunk information missing completely.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
writeRedTemporaryText("Could not decode the imported file.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
writeRedTemporaryText("Could not decode the imported file.");
|
||||
}
|
||||
}
|
||||
function generateBlob(keyName, encodedData) {
|
||||
//Append the file format prefix:
|
||||
var saveString = "EMULATOR_DATA";
|
||||
var consoleID = "GameBoyAdvance";
|
||||
//Figure out the length:
|
||||
var totalLength = (saveString.length + 4 + (1 + consoleID.length)) + ((1 + keyName.length) + (4 + encodedData.length));
|
||||
//Append the total length in bytes:
|
||||
saveString += to_little_endian_word(totalLength);
|
||||
//Append the console ID text's length:
|
||||
saveString += to_byte(consoleID.length);
|
||||
//Append the console ID text:
|
||||
saveString += consoleID;
|
||||
//Append the blob ID:
|
||||
saveString += to_byte(keyName.length);
|
||||
saveString += keyName;
|
||||
//Now append the save data:
|
||||
saveString += to_little_endian_word(encodedData.length);
|
||||
saveString += encodedData;
|
||||
return saveString;
|
||||
}
|
||||
function generateMultiBlob(blobPairs) {
|
||||
var consoleID = "GameBoyAdvance";
|
||||
//Figure out the initial length:
|
||||
var totalLength = 13 + 4 + 1 + consoleID.length;
|
||||
//Append the console ID text's length:
|
||||
var saveString = to_byte(consoleID.length);
|
||||
//Append the console ID text:
|
||||
saveString += consoleID;
|
||||
var keyName = "";
|
||||
var encodedData = "";
|
||||
//Now append all the blobs:
|
||||
for (var index = 0; index < blobPairs.length; ++index) {
|
||||
keyName = blobPairs[index][0];
|
||||
encodedData = blobPairs[index][1];
|
||||
//Append the blob ID:
|
||||
saveString += to_byte(keyName.length);
|
||||
saveString += keyName;
|
||||
//Now append the save data:
|
||||
saveString += to_little_endian_word(encodedData.length);
|
||||
saveString += encodedData;
|
||||
//Update the total length:
|
||||
totalLength += 1 + keyName.length + 4 + encodedData.length;
|
||||
}
|
||||
//Now add the prefix:
|
||||
saveString = "EMULATOR_DATA" + to_little_endian_word(totalLength) + saveString;
|
||||
return saveString;
|
||||
}
|
||||
function decodeBlob(blobData) {
|
||||
/*Format is as follows:
|
||||
- 13 byte string "EMULATOR_DATA"
|
||||
- 4 byte total size (including these 4 bytes).
|
||||
- 1 byte Console type ID length
|
||||
- Console type ID text of 8 bit size
|
||||
blobs {
|
||||
- 1 byte blob ID length
|
||||
- blob ID text (Used to say what the data is (SRAM/freeze state/etc...))
|
||||
- 4 byte blob length
|
||||
- blob of 32 bit length size
|
||||
- Blob itself is encoded in base64.
|
||||
}
|
||||
*/
|
||||
var length = blobData.length;
|
||||
var blobProperties = {};
|
||||
blobProperties.consoleID = null;
|
||||
var blobsCount = -1;
|
||||
blobProperties.blobs = [];
|
||||
if (length > 17) {
|
||||
if (blobData.substring(0, 13) == "EMULATOR_DATA") {
|
||||
var length = Math.min(((blobData.charCodeAt(16) & 0xFF) << 24) | ((blobData.charCodeAt(15) & 0xFF) << 16) | ((blobData.charCodeAt(14) & 0xFF) << 8) | (blobData.charCodeAt(13) & 0xFF), length);
|
||||
var consoleIDLength = blobData.charCodeAt(17) & 0xFF;
|
||||
if (length > 17 + consoleIDLength) {
|
||||
blobProperties.consoleID = blobData.substring(18, 18 + consoleIDLength);
|
||||
var blobIDLength = 0;
|
||||
var blobLength = 0;
|
||||
for (var index = 18 + consoleIDLength; index < length;) {
|
||||
blobIDLength = blobData.charCodeAt(index++) & 0xFF;
|
||||
if (index + blobIDLength < length) {
|
||||
blobProperties.blobs[++blobsCount] = {};
|
||||
blobProperties.blobs[blobsCount].blobID = blobData.substring(index, index + blobIDLength);
|
||||
index += blobIDLength;
|
||||
if (index + 4 < length) {
|
||||
blobLength = ((blobData.charCodeAt(index + 3) & 0xFF) << 24) | ((blobData.charCodeAt(index + 2) & 0xFF) << 16) | ((blobData.charCodeAt(index + 1) & 0xFF) << 8) | (blobData.charCodeAt(index) & 0xFF);
|
||||
index += 4;
|
||||
if (index + blobLength <= length) {
|
||||
blobProperties.blobs[blobsCount].blobContent = blobData.substring(index, index + blobLength);
|
||||
index += blobLength;
|
||||
}
|
||||
else {
|
||||
writeRedTemporaryText("Blob length check failed, blob determined to be incomplete.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
writeRedTemporaryText("Blob was incomplete, bailing out.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
writeRedTemporaryText("Blob was incomplete, bailing out.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return blobProperties;
|
||||
}
|
||||
function refreshStorageListing() {
|
||||
var keys = getSavesKeys();
|
||||
var blobPairs = [];
|
||||
for (var index = 0; index < keys.length; ++index) {
|
||||
blobPairs[index] = [keys[index], findValue(keys[index])];
|
||||
}
|
||||
this.href = "data:application/octet-stream;base64," + base64(generateMultiBlob(blobPairs));
|
||||
this.download = "gameboy_advance_saves_" + ((new Date()).getTime()) + ".export";
|
||||
}
|
||||
function checkStorageLength() {
|
||||
try {
|
||||
if (window.localStorage) {
|
||||
return window.localStorage.length;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
//An older Gecko 1.8.1/1.9.0 method of storage (Deprecated due to the obvious security hole):
|
||||
if (window.globalStorage && location.hostname) {
|
||||
return window.globalStorage[location.hostname].length;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
function getSavesKeys() {
|
||||
var storageLength = checkStorageLength();
|
||||
var keysFound = [];
|
||||
var index = 0;
|
||||
var nextKey = null;
|
||||
while (index < storageLength) {
|
||||
nextKey = findKey(index++);
|
||||
if (nextKey !== null && nextKey.length > 0) {
|
||||
if (nextKey.substring(0, 15) == "IodineGBA_SAVE_") {
|
||||
keysFound.push(nextKey.substring(10));
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return keysFound;
|
||||
}
|
||||
function findKey(keyNum) {
|
||||
try {
|
||||
if (window.localStorage) {
|
||||
return window.localStorage.key(keyNum);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
//An older Gecko 1.8.1/1.9.0 method of storage (Deprecated due to the obvious security hole):
|
||||
if (window.globalStorage && location.hostname) {
|
||||
return window.globalStorage[location.hostname].key(keyNum);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function to_little_endian_word(str) {
|
||||
return to_little_endian_hword(str) + to_little_endian_hword(str >> 16);
|
||||
}
|
||||
function to_little_endian_hword(str) {
|
||||
return to_byte(str) + to_byte(str >> 8);
|
||||
}
|
||||
function to_byte(str) {
|
||||
return String.fromCharCode(str & 0xFF);
|
||||
}
|
||||
//Wrapper for localStorage getItem, so that data can be retrieved in various types.
|
||||
function findValue(key) {
|
||||
key = "IodineGBA_" + key;
|
||||
try {
|
||||
if (window.localStorage) {
|
||||
if (window.localStorage.getItem(key) != null) {
|
||||
return JSON.parse(window.localStorage.getItem(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
//An older Gecko 1.8.1/1.9.0 method of storage (Deprecated due to the obvious security hole):
|
||||
if (window.globalStorage && location.hostname) {
|
||||
if (window.globalStorage[location.hostname].getItem(key) != null) {
|
||||
return JSON.parse(window.globalStorage[location.hostname].getItem(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//Wrapper for localStorage setItem, so that data can be set in various types.
|
||||
function setValue(key, value) {
|
||||
key = "IodineGBA_" + key;
|
||||
try {
|
||||
if (window.localStorage) {
|
||||
window.localStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
//An older Gecko 1.8.1/1.9.0 method of storage (Deprecated due to the obvious security hole):
|
||||
if (window.globalStorage && location.hostname) {
|
||||
window.globalStorage[location.hostname].setItem(key, JSON.stringify(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
//Wrapper for localStorage removeItem, so that data can be set in various types.
|
||||
function deleteValue(key) {
|
||||
key = "IodineGBA_" + key;
|
||||
try {
|
||||
if (window.localStorage) {
|
||||
window.localStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
//An older Gecko 1.8.1/1.9.0 method of storage (Deprecated due to the obvious security hole):
|
||||
if (window.globalStorage && location.hostname) {
|
||||
window.globalStorage[location.hostname].removeItem(key);
|
||||
}
|
||||
}
|
||||
}
|
130
user_scripts/WorkerGfxGlueCode.js
Normal file
130
user_scripts/WorkerGfxGlueCode.js
Normal file
|
@ -0,0 +1,130 @@
|
|||
"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.
|
||||
*/
|
||||
var gfxBuffers = null;
|
||||
var gfxCounters = null;
|
||||
function IodineGBAWorkerGfxShim() {
|
||||
this.gfx = null;
|
||||
gfxBuffers = [getSharedUint8Array(160 * 240 * 3),
|
||||
getSharedUint8Array(160 * 240 * 3)];
|
||||
gfxCounters = getSharedInt32Array(3);
|
||||
this.Iodine = new GameBoyAdvanceEmulator();
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.play = function () {
|
||||
this.Iodine.play();
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.pause = function () {
|
||||
this.Iodine.pause();
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.restart = function () {
|
||||
this.Iodine.restart();
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.setIntervalRate = function (rate) {
|
||||
rate = rate | 0;
|
||||
this.Iodine.setIntervalRate(rate | 0);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.timerCallback = function (timestamp) {
|
||||
timestamp = timestamp >>> 0;
|
||||
this.Iodine.timerCallback(timestamp);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.attachGraphicsFrameHandler = function (gfx) {
|
||||
this.gfx = gfx;
|
||||
var parentObj = this;
|
||||
this.gfx.attachGfxCallback(function () {
|
||||
parentObj.graphicsHeartBeat();
|
||||
});
|
||||
this.Iodine.attachGraphicsFrameHandler(gfx);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.attachAudioHandler = function (audio) {
|
||||
this.Iodine.attachAudioHandler(audio);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.enableAudio = function () {
|
||||
this.Iodine.enableAudio();
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.disableAudio = function () {
|
||||
this.Iodine.disableAudio();
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.toggleSkipBootROM = function (doEnable) {
|
||||
doEnable = doEnable | 0;
|
||||
this.Iodine.toggleSkipBootROM(doEnable | 0);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.toggleDynamicSpeed = function (doEnable) {
|
||||
doEnable = doEnable | 0;
|
||||
this.Iodine.toggleDynamicSpeed(doEnable | 0);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.toggleOffthreadGraphics = function (doEnable) {
|
||||
doEnable = doEnable | 0;
|
||||
this.Iodine.toggleOffthreadGraphics(doEnable | 0);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.attachSpeedHandler = function (speed) {
|
||||
this.Iodine.attachSpeedHandler(speed);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.attachPlayStatusHandler = function (playStatus) {
|
||||
this.Iodine.attachPlayStatusHandler(playStatus);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.keyDown = function (keyCode) {
|
||||
keyCode = keyCode | 0;
|
||||
this.Iodine.keyDown(keyCode | 0);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.keyUp = function (keyCode) {
|
||||
keyCode = keyCode | 0;
|
||||
this.Iodine.keyUp(keyCode | 0);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.incrementSpeed = function (newSpeed) {
|
||||
newSpeed = +newSpeed;
|
||||
this.Iodine.incrementSpeed(+newSpeed);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.setSpeed = function (newSpeed) {
|
||||
newSpeed = +newSpeed;
|
||||
this.Iodine.setSpeed(+newSpeed);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.attachBIOS = function (BIOS) {
|
||||
this.Iodine.attachBIOS(BIOS);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.attachROM = function (ROM) {
|
||||
this.Iodine.attachROM(ROM);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.exportSave = function () {
|
||||
this.Iodine.exportSave();
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.attachSaveExportHandler = function (saveExport) {
|
||||
this.Iodine.attachSaveExportHandler(saveExport);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.attachSaveImportHandler = function (saveImport) {
|
||||
this.Iodine.attachSaveImportHandler(saveImport);
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.graphicsHeartBeat = function () {
|
||||
//If graphics callback handle provided and we got a buffer reference:
|
||||
if (this.gfx && gfxCounters) {
|
||||
//Copy the buffer out to local:
|
||||
this.consumeGraphicsBuffer();
|
||||
//Wake up the producer thread:
|
||||
Atomics.wake(gfxCounters, 2, 1);
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerGfxShim.prototype.consumeGraphicsBuffer = function () {
|
||||
//Load the counter values:
|
||||
var start = gfxCounters[0] | 0; //Written by this thread.
|
||||
var end = Atomics.load(gfxCounters, 1) | 0; //Written by the other thread.
|
||||
//Don't process if nothing to process:
|
||||
if ((end | 0) == (start | 0)) {
|
||||
//Buffer is empty:
|
||||
return;
|
||||
}
|
||||
//Copy samples out from the ring buffer:
|
||||
do {
|
||||
//Hardcoded for 2 buffers for a triple buffer effect:
|
||||
this.gfx.copyBuffer(gfxBuffers[start & 0x1]);
|
||||
start = ((start | 0) + 1) | 0;
|
||||
} while ((start | 0) != (end | 0));
|
||||
//Update the starting position counter to match the end position:
|
||||
//Let the other Atomic loads/stores naturally flush this value:
|
||||
gfxCounters[0] = end | 0;
|
||||
}
|
328
user_scripts/WorkerGlueCode.js
Normal file
328
user_scripts/WorkerGlueCode.js
Normal file
|
@ -0,0 +1,328 @@
|
|||
"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.
|
||||
*/
|
||||
function IodineGBAWorkerShim() {
|
||||
this.playStatus = null;
|
||||
this.gfx = null;
|
||||
this.audio = null;
|
||||
this.speed = null;
|
||||
this.saveExport = null;
|
||||
this.saveImport = null;
|
||||
this.worker = null;
|
||||
this.gfxBuffers = null;
|
||||
this.gfxCounters = null;
|
||||
this.audioBuffer = null;
|
||||
this.audioCounters = null;
|
||||
this.audioSamplesRemaining = null;
|
||||
this.audioBufferSize = 0;
|
||||
this.audioBufferSizeMask = 0;
|
||||
this.audioInitialized = false;
|
||||
this.timestamp = null;
|
||||
this.initialize();
|
||||
}
|
||||
var tempvar = document.getElementsByTagName("script");
|
||||
IodineGBAWorkerShim.prototype.filepath = tempvar[tempvar.length-1].src;
|
||||
IodineGBAWorkerShim.prototype.initialize = function () {
|
||||
var parentObj = this;
|
||||
var loc = this.filepath.split("/");
|
||||
loc = loc.slice(0, loc.length - 2).join("/");
|
||||
loc += "/IodineGBA/core/Worker.js";
|
||||
this.worker = new Worker(loc);
|
||||
this.worker.onmessage = function (event) {
|
||||
parentObj.decodeMessage(event.data);
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.sendMessageSingle = function (eventCode) {
|
||||
eventCode = eventCode | 0;
|
||||
this.worker.postMessage({messageID:eventCode});
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.sendMessageDouble = function (eventCode, eventData) {
|
||||
eventCode = eventCode | 0;
|
||||
this.worker.postMessage({messageID:eventCode, payload:eventData});
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.play = function () {
|
||||
this.sendMessageSingle(0);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.pause = function () {
|
||||
this.sendMessageSingle(1);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.restart = function () {
|
||||
this.sendMessageSingle(2);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.setIntervalRate = function (rate) {
|
||||
rate = rate | 0;
|
||||
this.sendMessageDouble(3, rate | 0);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.timerCallback = function (timestamp) {
|
||||
timestamp = timestamp >>> 0;
|
||||
//If memory location provided for timestamp buffering:
|
||||
if (this.timestamp) {
|
||||
//Forward latest timestamp to worker:
|
||||
Atomics.store(this.timestamp, 0, timestamp >>> 0);
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.attachPlayStatusHandler = function (playStatus) {
|
||||
this.playStatus = playStatus;
|
||||
this.sendMessageSingle(23);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.issuePlayStatus = function (isPlaying) {
|
||||
isPlaying = isPlaying | 0;
|
||||
if (this.playStatus) {
|
||||
this.playStatus(isPlaying | 0);
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.attachGraphicsFrameHandler = function (gfx) {
|
||||
this.gfx = gfx;
|
||||
var parentObj = this;
|
||||
this.gfx.attachGfxCallback(function () {
|
||||
parentObj.graphicsHeartBeat();
|
||||
});
|
||||
this.sendMessageSingle(4);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.attachAudioHandler = function (audio) {
|
||||
this.audio = audio;
|
||||
this.sendMessageSingle(5);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.enableAudio = function () {
|
||||
if (this.audio) {
|
||||
this.sendMessageSingle(6);
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.disableAudio = function () {
|
||||
if (this.audio) {
|
||||
this.sendMessageSingle(7);
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.toggleSkipBootROM = function (doEnable) {
|
||||
doEnable = doEnable | 0;
|
||||
this.sendMessageDouble(8, doEnable | 0);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.toggleDynamicSpeed = function (doEnable) {
|
||||
doEnable = doEnable | 0;
|
||||
this.sendMessageDouble(9, doEnable | 0);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.toggleOffthreadGraphics = function (doEnable) {
|
||||
doEnable = doEnable | 0;
|
||||
this.sendMessageDouble(22, doEnable | 0);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.attachSpeedHandler = function (speed) {
|
||||
this.speed = speed;
|
||||
this.sendMessageSingle(10);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.keyDown = function (keyCode) {
|
||||
keyCode = keyCode | 0;
|
||||
this.sendMessageDouble(11, keyCode | 0);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.keyUp = function (keyCode) {
|
||||
keyCode = keyCode | 0;
|
||||
this.sendMessageDouble(12, keyCode | 0);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.incrementSpeed = function (newSpeed) {
|
||||
newSpeed = +newSpeed;
|
||||
this.sendMessageDouble(13, +newSpeed);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.setSpeed = function (newSpeed) {
|
||||
newSpeed = +newSpeed;
|
||||
this.sendMessageDouble(14, +newSpeed);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.attachBIOS = function (BIOS) {
|
||||
this.sendMessageDouble(15, BIOS);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.attachROM = function (ROM) {
|
||||
this.sendMessageDouble(16, ROM);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.exportSave = function () {
|
||||
this.sendMessageSingle(17);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.attachSaveExportHandler = function (saveExport) {
|
||||
this.saveExport = saveExport;
|
||||
this.sendMessageSingle(18);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.attachSaveImportHandler = function (saveImport) {
|
||||
this.saveImport = saveImport;
|
||||
this.sendMessageSingle(19);
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.decodeMessage = function (data) {
|
||||
switch (data.messageID | 0) {
|
||||
case 0:
|
||||
this.buffersInitialize(data.gfxBuffer1, data.gfxBuffer2, data.gfxCounters, data.audioSamplesRemaining, data.timestamp);
|
||||
break;
|
||||
case 1:
|
||||
this.audioInitialize(data.channels | 0, +data.sampleRate, data.bufferLimit | 0, data.audioBuffer, data.audioCounters);
|
||||
break;
|
||||
case 2:
|
||||
this.audioRegister();
|
||||
break;
|
||||
case 3:
|
||||
this.audioUnregister();
|
||||
break;
|
||||
case 4:
|
||||
this.audioSetBufferSpace(data.audioBufferContainAmount | 0);
|
||||
break;
|
||||
case 5:
|
||||
this.saveImportRequest(data.saveID);
|
||||
break;
|
||||
case 6:
|
||||
this.saveExportRequest(data.saveID, data.saveData);
|
||||
break;
|
||||
case 7:
|
||||
this.speedPush(+data.speed);
|
||||
break;
|
||||
default:
|
||||
this.issuePlayStatus(data.playing | 0);
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.audioInitialize = function (channels, sampleRate, bufferLimit, audioBuffer, audioCounters) {
|
||||
channels = channels | 0;
|
||||
sampleRate = +sampleRate;
|
||||
bufferLimit = bufferLimit | 0;
|
||||
var parentObj = this;
|
||||
if (this.audio) {
|
||||
//(Re-)Initialize:
|
||||
this.audio.initialize(channels | 0, +sampleRate, bufferLimit | 0, function () {
|
||||
//Empty buffers inside the provided audio event callback:
|
||||
parentObj.audioHeartBeat();
|
||||
}, function () {
|
||||
//Get the remaining sample count:
|
||||
parentObj.audioPostHeartBeat();
|
||||
},function () {
|
||||
//Disable audio in the callback here:
|
||||
parentObj.disableAudio();
|
||||
});
|
||||
this.audioInitialized = true;
|
||||
}
|
||||
//Grab the new buffer:
|
||||
this.audioBuffer = audioBuffer;
|
||||
this.audioCounters = audioCounters;
|
||||
this.audioBufferSize = audioBuffer.length | 0;
|
||||
this.audioBufferSizeMask = ((this.audioBufferSize | 0) - 1) | 0;
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.audioHeartBeat = function () {
|
||||
//If audio API handle provided and we got a buffer reference:
|
||||
if (this.audioInitialized) {
|
||||
//Empty the buffer out:
|
||||
this.consumeAudioBuffer();
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.consumeAudioBuffer = function () {
|
||||
//Load the counter values:
|
||||
var start = this.audioCounters[0] | 0; //Written by this thread.
|
||||
var end = Atomics.load(this.audioCounters, 1) | 0; //Written to by the other thread.
|
||||
//Don't process if nothing to process:
|
||||
if ((end | 0) == (start | 0)) {
|
||||
//Buffer is empty:
|
||||
return;
|
||||
}
|
||||
//Copy samples out from the ring buffer:
|
||||
this.copyAudioBuffer(start | 0, end | 0);
|
||||
//Update the sample count reported by the audio mixer:
|
||||
//Done before updating ring buffer counter, so we don't over-produce:
|
||||
this.audioPostHeartBeat();
|
||||
//Update the starting position counter to match the end position:
|
||||
//Atomic store, because the sample count by the audio system needs to be reported prior to this:
|
||||
Atomics.store(this.audioCounters, 0, end | 0);
|
||||
//Tell audio mixer input to flush to audio mixer:
|
||||
this.audio.flush();
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.copyAudioBuffer = function (start, end) {
|
||||
start = start | 0;
|
||||
end = end | 0;
|
||||
//Compute the positions in the ring buffer:
|
||||
var startCorrected = ((start | 0) & (this.audioBufferSizeMask | 0)) | 0;
|
||||
var endCorrected = ((end | 0) & (this.audioBufferSizeMask | 0)) | 0;
|
||||
//Copy samples out to audio mixer input (but don't process them yet):
|
||||
if ((startCorrected | 0) >= (endCorrected | 0)) {
|
||||
//Handle looping to start of buffer:
|
||||
this.audio.pushDeferred(this.audioBuffer, startCorrected | 0, this.audioBufferSize | 0);
|
||||
this.audio.pushDeferred(this.audioBuffer, 0, endCorrected | 0);
|
||||
}
|
||||
else {
|
||||
this.audio.pushDeferred(this.audioBuffer, startCorrected | 0, endCorrected | 0);
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.audioPostHeartBeat = function () {
|
||||
//Push latest audio metrics with no buffering:
|
||||
this.audioSamplesRemaining[0] = this.audio.remainingBuffer() | 0;
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.graphicsHeartBeat = function () {
|
||||
//If graphics callback handle provided and we got a buffer reference:
|
||||
if (this.gfx && this.gfxCounters) {
|
||||
//Copy the buffer out to local:
|
||||
this.consumeGraphicsBuffer();
|
||||
//Wake up the producer thread:
|
||||
Atomics.wake(this.gfxCounters, 2, 1);
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.consumeGraphicsBuffer = function () {
|
||||
//Load the counter values:
|
||||
var start = this.gfxCounters[0] | 0; //Written by this thread.
|
||||
var end = Atomics.load(this.gfxCounters, 1) | 0; //Written by the other thread.
|
||||
//Don't process if nothing to process:
|
||||
if ((end | 0) == (start | 0)) {
|
||||
//Buffer is empty:
|
||||
return;
|
||||
}
|
||||
//Copy samples out from the ring buffer:
|
||||
do {
|
||||
//Hardcoded for 2 buffers for a triple buffer effect:
|
||||
this.gfx.copyBuffer(this.gfxBuffers[start & 0x1]);
|
||||
start = ((start | 0) + 1) | 0;
|
||||
} while ((start | 0) != (end | 0));
|
||||
//Update the starting position counter to match the end position:
|
||||
//Let the other Atomic loads/stores naturally flush this value:
|
||||
this.gfxCounters[0] = end | 0;
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.audioRegister = function () {
|
||||
if (this.audio) {
|
||||
this.audio.register();
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.audioUnregister = function () {
|
||||
if (this.audio) {
|
||||
//Empty the existing buffer:
|
||||
this.audioHeartBeat();
|
||||
//Unregister from mixer:
|
||||
this.audio.unregister();
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.audioSetBufferSpace = function (bufferSpace) {
|
||||
bufferSpace = bufferSpace | 0;
|
||||
if (this.audio) {
|
||||
this.audio.setBufferSpace(bufferSpace | 0);
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.buffersInitialize = function (gfxBuffer1, gfxBuffer2, gfxCounters, audioSamplesRemaining, timestamp) {
|
||||
this.gfxBuffers = [gfxBuffer1, gfxBuffer2];
|
||||
this.gfxCounters = gfxCounters;
|
||||
this.audioSamplesRemaining = audioSamplesRemaining;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.speedPush = function (speed) {
|
||||
speed = +speed;
|
||||
if (this.speed) {
|
||||
this.speed(+speed);
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.saveImportRequest = function (saveID) {
|
||||
if (this.saveImport) {
|
||||
var parentObj = this;
|
||||
this.saveImport(saveID, function (saveData) {
|
||||
parentObj.sendMessageDouble(20, saveData);
|
||||
},
|
||||
function () {
|
||||
parentObj.sendMessageSingle(21);
|
||||
});
|
||||
}
|
||||
}
|
||||
IodineGBAWorkerShim.prototype.saveExportRequest = function (saveID, saveData) {
|
||||
if (this.saveExport) {
|
||||
this.saveExport(saveID, saveData);
|
||||
}
|
||||
}
|
71
user_scripts/XAudioJS/README.md
Normal file
71
user_scripts/XAudioJS/README.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
<h1>XAudioJS</h1>
|
||||
<h3>A minimal cross-browser API for writing PCM audio samples:</h3>
|
||||
<p>This simple JavaScript library abstracts the push-for-audio API of Mozilla Audio, and the passive callback API of Web Audio.
|
||||
This library introduces an abstraction layer that provides a push-for-audio and a callback API in one. We even provide a flash fallback to bring us to a total of 3 APIs supported.</p>
|
||||
<br>
|
||||
<b>This software is hereby placed in the public domain for anyone to use.</b>
|
||||
<br>
|
||||
<h3>How To Initialize:</h3>
|
||||
<dl>
|
||||
<dt>new XAudioServer(int channels, double sampleRate, int bufferLow, int bufferHigh, function underRunCallback, function heartbeatCallback, function postheartbeatCallback, double volume, function failureCallback, object userEventLatch);</dt>
|
||||
<dd>Make sure only one instance of XAudioServer is running at any time.</dd>
|
||||
<dd>bufferLow MUST be less than bufferHigh.</dd>
|
||||
<dd>bufferHigh sets the internal FIFO buffer length for all APIs except the Mozilla Audio Data API. Overfill on FIFO causes the oldest samples to be dropped first.</dd>
|
||||
<dd>
|
||||
<h4>Array underRunCallback (int samplesRequested)</h4>
|
||||
<blockquote>
|
||||
Arguments: Passed the number of samples that are needed to replenish the internal audio buffer back to bufferLow.<br><br>
|
||||
Functionality: JS developer set callback that can pass back any number of samples to replenish the audio buffer with.<br><br>
|
||||
Return: Array of samples to be passed into the underlying audio buffer. MUST be divisible by number of channels used (Whole frames required.). The return array length DOES NOT NEED to be of length samplesRequested.
|
||||
</blockquote>
|
||||
</dd>
|
||||
<dd>
|
||||
<h4>void heartbeatCallback (void)</h4>
|
||||
<blockquote>
|
||||
Functionality: JS developers set this callback as a way to program against an audio clock, firing inside an audio event.
|
||||
</blockquote>
|
||||
</dd>
|
||||
<dd>
|
||||
<h4>void postheartbeatCallback (void)</h4>
|
||||
<blockquote>
|
||||
Functionality: JS developers set this callback as a way to program against an audio clock, firing immediately after an audio event.
|
||||
</blockquote>
|
||||
</dd>
|
||||
<dd>volume is the output volume.</dd>
|
||||
<dd>
|
||||
<h4>void failureCallback (void)</h4>
|
||||
<blockquote>
|
||||
Functionality: JS developers set this callback to handle no audio support being available from the browser.
|
||||
</blockquote>
|
||||
</dd>
|
||||
<dd>
|
||||
<h4>object userEventLatch</h4>
|
||||
<blockquote>
|
||||
Functionality: JS developers set this DOM object for the Web Audio API to enable audio from.
|
||||
</blockquote>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Function Reference:</h3>
|
||||
<dl>
|
||||
<dt>void writeAudio (Array buffer, Integer upTo)</dt>
|
||||
<dd>Arguments: Pass an array of audio samples that is divisible by the number of audio channels utilized (buffer % channels == 0), and an integer length delimiter that follows the same restriction.</dd>
|
||||
<dd>Functionality: Passes the audio samples directly into the underlying audio subsystem, <b>and can call the specified sample buffer under-run callback as needed (Does the equivalent of executeCallback in addition to the forced sample input.)<b>.</dd>
|
||||
<dd>Return: void (None).</dd>
|
||||
<dt>void writeAudioNoCallback (Array buffer, Integer upTo)</dt>
|
||||
<dd>Arguments: Pass an array of audio samples that is divisible by the number of audio channels utilized (buffer % channels == 0), and an integer length delimiter that follows the same restriction.</dd>
|
||||
<dd>Functionality: Passes the audio samples directly into the underlying audio subsystem.</dd>
|
||||
<dd>Return: void (None).</dd>
|
||||
<dt>int remainingBuffer (void)</dt>
|
||||
<dd>Arguments: void (None).</dd>
|
||||
<dd>Functionality: Returns the number of samples left in the audio system before running out of playable samples.</dd>
|
||||
<dd>Return (On valid): int samples_remaining (<b>CAN BE NEGATIVE<b>)</dd>
|
||||
<dd>Return (On invalid): null</dd>
|
||||
<dt>void executeCallback (void)</dt>
|
||||
<dd>Arguments: void (None).</dd>
|
||||
<dd>Functionality: Executes the audio sample under-run callback if the samples remaining is below the set buffer low limit.</dd>
|
||||
<dd>Return: void (None).</dd>
|
||||
<dt>void changeVolume (double volume)</dt>
|
||||
<dd>Arguments: double float between 0 and 1 specifying the volume.</dd>
|
||||
<dd>Functionality: Changes the volume. Will affect samples in buffer, so has a low-latency effect (Use this to do a fast-mute).</dd>
|
||||
<dd>Return: void (None).</dd>
|
||||
</dl>
|
86
user_scripts/XAudioJS/XAudioJS.as
Normal file
86
user_scripts/XAudioJS/XAudioJS.as
Normal file
|
@ -0,0 +1,86 @@
|
|||
package {
|
||||
import flash.media.Sound;
|
||||
import flash.events.SampleDataEvent;
|
||||
import flash.display.Sprite;
|
||||
import flash.external.ExternalInterface;
|
||||
public class XAudioJS extends Sprite {
|
||||
public var sound:Sound = null;
|
||||
public var channelBuffer:Vector.<Number> = new Vector.<Number>(8192, true);
|
||||
public var channels:int = 0;
|
||||
public var volume:Number = 0;
|
||||
public var samplesFound:int = 0;
|
||||
public function XAudioJS() {
|
||||
ExternalInterface.addCallback('initialize', initialize);
|
||||
ExternalInterface.addCallback('changeVolume', changeVolume);
|
||||
}
|
||||
//Initialization function for the flash backend of XAudioJS:
|
||||
public function initialize(channels:Number, newVolume:Number):void {
|
||||
//Initialize the new settings:
|
||||
this.channels = (int(channels) == 2) ? 2 : 1;
|
||||
this.changeVolume(newVolume);
|
||||
this.checkForSound();
|
||||
}
|
||||
//Volume changing function for the flash backend of XAudioJS:
|
||||
public function changeVolume(newVolume:Number):void {
|
||||
//Set the new volume:
|
||||
this.volume = Math.min(Math.max(newVolume, 0), 1);
|
||||
}
|
||||
//Calls the JavaScript function responsible for the polyfill:
|
||||
public function requestSamples():Boolean {
|
||||
//Call the javascript callback function:
|
||||
var buffer:String = ExternalInterface.call("XAudioJSFlashAudioEvent");
|
||||
//If we received an appropriate response:
|
||||
if (buffer !== null) {
|
||||
if ((buffer.length % this.channels) == 0) { //Outsmart bad programmers from messing us up. :/
|
||||
var channelSample:Number = 0;
|
||||
this.samplesFound = Math.min(buffer.length, 4096 * this.channels);
|
||||
for (var index:int = 0; index < this.samplesFound; ++index) {
|
||||
//Get the unsigned 15-bit encoded sample value at +0x3000 offset:
|
||||
channelSample = buffer.charCodeAt(index);
|
||||
//Range-check the sample frame value and convert it:
|
||||
this.channelBuffer[index] = (channelSample >= 0x3000 && channelSample < 0xAFFF) ? (this.volume * (((channelSample - 0x3000) / 0x3FFF) - 1)) : 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//Check to make sure the audio stream is enabled:
|
||||
public function checkForSound():void {
|
||||
if (this.sound == null) {
|
||||
this.sound = new Sound();
|
||||
this.sound.addEventListener(
|
||||
SampleDataEvent.SAMPLE_DATA,
|
||||
soundCallback
|
||||
);
|
||||
this.sound.play();
|
||||
}
|
||||
}
|
||||
//Flash Audio Refill Callback
|
||||
public function soundCallback(e:SampleDataEvent):void {
|
||||
var index:int = 0;
|
||||
if (this.requestSamples()) {
|
||||
if (this.channels == 2) {
|
||||
//Stereo:
|
||||
while (index < this.samplesFound) {
|
||||
e.data.writeFloat(this.channelBuffer[index++]);
|
||||
e.data.writeFloat(this.channelBuffer[index++]);
|
||||
}
|
||||
index >>= 1;
|
||||
}
|
||||
else {
|
||||
//Mono:
|
||||
while (index < this.samplesFound) {
|
||||
e.data.writeFloat(this.channelBuffer[index]);
|
||||
e.data.writeFloat(this.channelBuffer[index++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//Write some silence if not enough samples are found:
|
||||
while (++index <= 2048) {
|
||||
e.data.writeFloat(0);
|
||||
e.data.writeFloat(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
user_scripts/XAudioJS/XAudioJS.swf
Normal file
BIN
user_scripts/XAudioJS/XAudioJS.swf
Normal file
Binary file not shown.
|
@ -1,25 +1,30 @@
|
|||
//2010-2013 Grant Galitz - XAudioJS realtime audio output compatibility library:
|
||||
//XAudioJS realtime audio output compatibility library
|
||||
//Copyright (C) 2010-2015 Grant Galitz
|
||||
//Released to Public Domain
|
||||
var XAudioJSscriptsHandle = document.getElementsByTagName("script");
|
||||
var XAudioJSsourceHandle = XAudioJSscriptsHandle[XAudioJSscriptsHandle.length-1].src;
|
||||
function XAudioServer(channels, sampleRate, minBufferSize, maxBufferSize, underRunCallback, volume, failureCallback) {
|
||||
function XAudioServer(channels, sampleRate, minBufferSize, maxBufferSize, underRunCallback, heartbeatCallback, postheartbeatCallback, volume, failureCallback, userEventLatch) {
|
||||
XAudioJSChannelsAllocated = Math.max(channels, 1);
|
||||
this.XAudioJSSampleRate = Math.abs(sampleRate);
|
||||
XAudioJSMinBufferSize = (minBufferSize >= (XAudioJSSamplesPerCallback * XAudioJSChannelsAllocated) && minBufferSize < maxBufferSize) ? (minBufferSize & (-XAudioJSChannelsAllocated)) : (XAudioJSSamplesPerCallback * XAudioJSChannelsAllocated);
|
||||
XAudioJSMaxBufferSize = (Math.floor(maxBufferSize) > XAudioJSMinBufferSize + XAudioJSChannelsAllocated) ? (maxBufferSize & (-XAudioJSChannelsAllocated)) : (XAudioJSMinBufferSize * XAudioJSChannelsAllocated);
|
||||
this.underRunCallback = (typeof underRunCallback == "function") ? underRunCallback : function () {};
|
||||
XAudioJSCallbackAPIEventNotificationCallback = (typeof heartbeatCallback == "function") ? heartbeatCallback : null;
|
||||
XAudioJSCallbackAPIEventNotificationCallback2 = (typeof postheartbeatCallback == "function") ? postheartbeatCallback : null;
|
||||
XAudioJSVolume = (volume >= 0 && volume <= 1) ? volume : 1;
|
||||
this.failureCallback = (typeof failureCallback == "function") ? failureCallback : function () { throw(new Error("XAudioJS has encountered a fatal error.")); };
|
||||
this.userEventLatch = (typeof userEventLach == "object") ? userEventLatch : null;
|
||||
this.initializeAudio();
|
||||
}
|
||||
XAudioServer.prototype.MOZWriteAudioNoCallback = function (buffer) {
|
||||
XAudioServer.prototype.MOZWriteAudioNoCallback = function (buffer, upTo) {
|
||||
//Resample before passing to the moz audio api:
|
||||
var bufferLength = buffer.length;
|
||||
var bufferLength = Math.min(buffer.length, upTo);
|
||||
for (var bufferIndex = 0; bufferIndex < bufferLength;) {
|
||||
var sliceLength = Math.min(bufferLength - bufferIndex, XAudioJSMaxBufferSize);
|
||||
for (var sliceIndex = 0; sliceIndex < sliceLength; ++sliceIndex) {
|
||||
XAudioJSAudioContextSampleBuffer[sliceIndex] = buffer[bufferIndex++];
|
||||
}
|
||||
var resampleLength = XAudioJSResampleControl.resampler(XAudioJSGetArraySlice(XAudioJSAudioContextSampleBuffer, sliceIndex));
|
||||
var resampleLength = XAudioJSResampleControl.resampler(sliceIndex);
|
||||
if (resampleLength > 0) {
|
||||
var resampledResult = XAudioJSResampleControl.outputBuffer;
|
||||
var resampledBuffer = XAudioJSGetArraySlice(resampledResult, resampleLength);
|
||||
|
@ -27,10 +32,10 @@ XAudioServer.prototype.MOZWriteAudioNoCallback = function (buffer) {
|
|||
}
|
||||
}
|
||||
}
|
||||
XAudioServer.prototype.callbackBasedWriteAudioNoCallback = function (buffer) {
|
||||
XAudioServer.prototype.callbackBasedWriteAudioNoCallback = function (buffer, upTo) {
|
||||
//Callback-centered audio APIs:
|
||||
var length = buffer.length;
|
||||
for (var bufferCounter = 0; bufferCounter < length && XAudioJSAudioBufferSize < XAudioJSMaxBufferSize;) {
|
||||
var bufferLength = Math.min(buffer.length, upTo);
|
||||
for (var bufferCounter = 0; bufferCounter < bufferLength && XAudioJSAudioBufferSize < XAudioJSMaxBufferSize;) {
|
||||
XAudioJSAudioContextSampleBuffer[XAudioJSAudioBufferSize++] = buffer[bufferCounter++];
|
||||
}
|
||||
}
|
||||
|
@ -41,17 +46,16 @@ examples:
|
|||
mono - [left, left, left, left]
|
||||
stereo - [left, right, left, right, left, right, left, right]
|
||||
*/
|
||||
XAudioServer.prototype.writeAudio = function (buffer) {
|
||||
XAudioServer.prototype.writeAudio = function (buffer, upTo) {
|
||||
switch (this.audioType) {
|
||||
case 0:
|
||||
this.MOZWriteAudioNoCallback(buffer);
|
||||
this.MOZWriteAudioNoCallback(buffer, upTo);
|
||||
this.MOZExecuteCallback();
|
||||
break;
|
||||
case 2:
|
||||
this.checkFlashInit();
|
||||
case 1:
|
||||
case 3:
|
||||
this.callbackBasedWriteAudioNoCallback(buffer);
|
||||
this.callbackBasedWriteAudioNoCallback(buffer, upTo);
|
||||
this.callbackBasedExecuteCallback();
|
||||
break;
|
||||
default:
|
||||
|
@ -66,16 +70,15 @@ examples:
|
|||
stereo - [left, right, left, right, left, right, left, right]
|
||||
Useful in preventing infinite recursion issues with calling writeAudio inside your callback.
|
||||
*/
|
||||
XAudioServer.prototype.writeAudioNoCallback = function (buffer) {
|
||||
XAudioServer.prototype.writeAudioNoCallback = function (buffer, upTo) {
|
||||
switch (this.audioType) {
|
||||
case 0:
|
||||
this.MOZWriteAudioNoCallback(buffer);
|
||||
this.MOZWriteAudioNoCallback(buffer, upTo);
|
||||
break;
|
||||
case 2:
|
||||
this.checkFlashInit();
|
||||
case 1:
|
||||
case 3:
|
||||
this.callbackBasedWriteAudioNoCallback(buffer);
|
||||
this.callbackBasedWriteAudioNoCallback(buffer, upTo);
|
||||
break;
|
||||
default:
|
||||
this.failureCallback();
|
||||
|
@ -90,7 +93,6 @@ XAudioServer.prototype.remainingBuffer = function () {
|
|||
case 2:
|
||||
this.checkFlashInit();
|
||||
case 1:
|
||||
case 3:
|
||||
return (Math.floor((XAudioJSResampledSamplesLeft() * XAudioJSResampleControl.ratioWeight) / XAudioJSChannelsAllocated) * XAudioJSChannelsAllocated) + XAudioJSAudioBufferSize;
|
||||
default:
|
||||
this.failureCallback();
|
||||
|
@ -101,14 +103,16 @@ XAudioServer.prototype.MOZExecuteCallback = function () {
|
|||
//mozAudio:
|
||||
var samplesRequested = XAudioJSMinBufferSize - this.remainingBuffer();
|
||||
if (samplesRequested > 0) {
|
||||
this.MOZWriteAudioNoCallback(this.underRunCallback(samplesRequested));
|
||||
var buffer = this.underRunCallback(samplesRequested);
|
||||
this.MOZWriteAudioNoCallback(buffer, buffer.length);
|
||||
}
|
||||
}
|
||||
XAudioServer.prototype.callbackBasedExecuteCallback = function () {
|
||||
//WebKit /Flash Audio:
|
||||
var samplesRequested = XAudioJSMinBufferSize - this.remainingBuffer();
|
||||
if (samplesRequested > 0) {
|
||||
this.callbackBasedWriteAudioNoCallback(this.underRunCallback(samplesRequested));
|
||||
var buffer = this.underRunCallback(samplesRequested);
|
||||
this.callbackBasedWriteAudioNoCallback(buffer, buffer.length);
|
||||
}
|
||||
}
|
||||
//If you just want your callback called for any possible refill (Execution of callback is still conditional):
|
||||
|
@ -120,7 +124,6 @@ XAudioServer.prototype.executeCallback = function () {
|
|||
case 2:
|
||||
this.checkFlashInit();
|
||||
case 1:
|
||||
case 3:
|
||||
this.callbackBasedExecuteCallback();
|
||||
break;
|
||||
default:
|
||||
|
@ -136,10 +139,6 @@ XAudioServer.prototype.initializeAudio = function () {
|
|||
try {
|
||||
this.initializeWebAudio();
|
||||
}
|
||||
catch (error) {
|
||||
try {
|
||||
this.initializeMediaStream();
|
||||
}
|
||||
catch (error) {
|
||||
try {
|
||||
this.initializeFlashAudio();
|
||||
|
@ -150,23 +149,6 @@ XAudioServer.prototype.initializeAudio = function () {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
XAudioServer.prototype.initializeMediaStream = function () {
|
||||
this.audioHandleMediaStream = new Audio();
|
||||
this.resetCallbackAPIAudioBuffer(XAudioJSMediaStreamSampleRate);
|
||||
if (XAudioJSMediaStreamWorker) {
|
||||
//WebWorker is not GC'd, so manually collect it:
|
||||
XAudioJSMediaStreamWorker.terminate();
|
||||
}
|
||||
XAudioJSMediaStreamWorker = new Worker(XAudioJSsourceHandle.substring(0, XAudioJSsourceHandle.length - 3) + "MediaStreamWorker.js");
|
||||
this.audioHandleMediaStreamProcessing = new ProcessedMediaStream(XAudioJSMediaStreamWorker, XAudioJSMediaStreamSampleRate, XAudioJSChannelsAllocated);
|
||||
this.audioHandleMediaStream.src = this.audioHandleMediaStreamProcessing;
|
||||
this.audioHandleMediaStream.volume = XAudioJSVolume;
|
||||
XAudioJSMediaStreamWorker.onmessage = XAudioJSMediaStreamPushAudio;
|
||||
XAudioJSMediaStreamWorker.postMessage([1, XAudioJSResampleBufferSize, XAudioJSChannelsAllocated]);
|
||||
this.audioHandleMediaStream.play();
|
||||
this.audioType = 3;
|
||||
}
|
||||
XAudioServer.prototype.initializeMozAudio = function () {
|
||||
this.audioHandleMoz = new Audio();
|
||||
|
@ -226,6 +208,13 @@ XAudioServer.prototype.initializeWebAudio = function () {
|
|||
}
|
||||
}, 500);
|
||||
}
|
||||
if (this.userEventLatch) {
|
||||
this.userEventLatch.addEventListener("click", function () {
|
||||
if(XAudioJSWebAudioContextHandle.state === 'suspended') {
|
||||
XAudioJSWebAudioContextHandle.resume();
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
XAudioServer.prototype.initializeFlashAudio = function () {
|
||||
var existingFlashload = document.getElementById("XAudioJS");
|
||||
|
@ -295,9 +284,6 @@ XAudioServer.prototype.changeVolume = function (newVolume) {
|
|||
this.checkFlashInit();
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
this.audioHandleMediaStream.volume = XAudioJSVolume;
|
||||
break;
|
||||
default:
|
||||
this.failureCallback();
|
||||
}
|
||||
|
@ -325,8 +311,8 @@ XAudioServer.prototype.resetCallbackAPIAudioBuffer = function (APISampleRate) {
|
|||
}
|
||||
XAudioServer.prototype.initializeResampler = function (sampleRate) {
|
||||
XAudioJSAudioContextSampleBuffer = this.getFloat32(XAudioJSMaxBufferSize);
|
||||
XAudioJSResampleBufferSize = Math.max(XAudioJSMaxBufferSize * Math.ceil(sampleRate / this.XAudioJSSampleRate) + XAudioJSChannelsAllocated, XAudioJSSamplesPerCallback * XAudioJSChannelsAllocated);
|
||||
XAudioJSResampleControl = new Resampler(this.XAudioJSSampleRate, sampleRate, XAudioJSChannelsAllocated, XAudioJSResampleBufferSize, true);
|
||||
XAudioJSResampleControl = new Resampler(this.XAudioJSSampleRate, sampleRate, XAudioJSChannelsAllocated, XAudioJSAudioContextSampleBuffer);
|
||||
XAudioJSResampleBufferSize = XAudioJSResampleControl.outputBuffer.length;
|
||||
}
|
||||
XAudioServer.prototype.getFloat32 = function (size) {
|
||||
try {
|
||||
|
@ -337,8 +323,12 @@ XAudioServer.prototype.getFloat32 = function (size) {
|
|||
}
|
||||
}
|
||||
function XAudioJSFlashAudioEvent() { //The callback that flash calls...
|
||||
XAudioJSCallbackAPIEventNotificationCallbackCompatTimerClear();
|
||||
XAudioJSCallbackAPIEventNotification();
|
||||
XAudioJSResampleRefill();
|
||||
return XAudioJSFlashTransportEncoder();
|
||||
var outputStr = XAudioJSFlashTransportEncoder();
|
||||
XAudioJSCallbackAPIEventNotification2();
|
||||
return outputStr;
|
||||
}
|
||||
function XAudioJSGenerateFlashSurroundString() { //Convert the arrays to one long string for speed.
|
||||
var XAudioJSTotalSamples = XAudioJSSamplesPerCallback << 1;
|
||||
|
@ -389,6 +379,9 @@ function XAudioJSGenerateFlashMonoString() { //Convert the array to one long str
|
|||
var XAudioJSWebAudioContextHandle = null;
|
||||
var XAudioJSWebAudioAudioNode = null;
|
||||
var XAudioJSWebAudioWatchDogTimer = null;
|
||||
var XAudioJSCallbackAPIEventNotificationCallback = null;
|
||||
var XAudioJSCallbackAPIEventNotificationCallback2 = null;
|
||||
var XAudioJSCallbackAPIEventNotificationCallbackCompatTimer = setInterval(XAudioJSCallbackAPIEventNotificationDual, 16);
|
||||
var XAudioJSWebAudioWatchDogLast = false;
|
||||
var XAudioJSWebAudioLaunchedContext = false;
|
||||
var XAudioJSAudioContextSampleBuffer = [];
|
||||
|
@ -402,13 +395,9 @@ var XAudioJSAudioBufferSize = 0;
|
|||
var XAudioJSResampleBufferStart = 0;
|
||||
var XAudioJSResampleBufferEnd = 0;
|
||||
var XAudioJSResampleBufferSize = 0;
|
||||
var XAudioJSMediaStreamWorker = null;
|
||||
var XAudioJSMediaStreamBuffer = [];
|
||||
var XAudioJSMediaStreamSampleRate = 44100;
|
||||
var XAudioJSMozAudioSampleRate = 44100;
|
||||
var XAudioJSSamplesPerCallback = 2048; //Has to be between 2048 and 4096 (If over, then samples are ignored, if under then silence is added).
|
||||
var XAudioJSFlashTransportEncoder = null;
|
||||
var XAudioJSMediaStreamLengthAliasCounter = 0;
|
||||
var XAudioJSBinaryString = [];
|
||||
function XAudioJSWebAudioEvent(event) { //Web Audio API callback...
|
||||
if (XAudioJSWebAudioWatchDogTimer) {
|
||||
|
@ -418,6 +407,8 @@ function XAudioJSWebAudioEvent(event) { //Web Audio API callback...
|
|||
for (var bufferCount = 0, buffers = []; bufferCount < XAudioJSChannelsAllocated; ++bufferCount) {
|
||||
buffers[bufferCount] = event.outputBuffer.getChannelData(bufferCount);
|
||||
}
|
||||
XAudioJSCallbackAPIEventNotificationCallbackCompatTimerClear();
|
||||
XAudioJSCallbackAPIEventNotification();
|
||||
//Make sure we have resampled samples ready:
|
||||
XAudioJSResampleRefill();
|
||||
//Copy samples from XAudioJS to the Web Audio API:
|
||||
|
@ -436,36 +427,12 @@ function XAudioJSWebAudioEvent(event) { //Web Audio API callback...
|
|||
}
|
||||
++index;
|
||||
}
|
||||
}
|
||||
//MediaStream API buffer push
|
||||
function XAudioJSMediaStreamPushAudio(event) {
|
||||
var index = 0;
|
||||
var audioLengthRequested = event.data;
|
||||
var samplesPerCallbackAll = XAudioJSSamplesPerCallback * XAudioJSChannelsAllocated;
|
||||
var XAudioJSMediaStreamLengthAlias = audioLengthRequested % XAudioJSSamplesPerCallback;
|
||||
audioLengthRequested = audioLengthRequested - (XAudioJSMediaStreamLengthAliasCounter - (XAudioJSMediaStreamLengthAliasCounter % XAudioJSSamplesPerCallback)) - XAudioJSMediaStreamLengthAlias + XAudioJSSamplesPerCallback;
|
||||
XAudioJSMediaStreamLengthAliasCounter -= XAudioJSMediaStreamLengthAliasCounter - (XAudioJSMediaStreamLengthAliasCounter % XAudioJSSamplesPerCallback);
|
||||
XAudioJSMediaStreamLengthAliasCounter += XAudioJSSamplesPerCallback - XAudioJSMediaStreamLengthAlias;
|
||||
if (XAudioJSMediaStreamBuffer.length != samplesPerCallbackAll) {
|
||||
XAudioJSMediaStreamBuffer = new Float32Array(samplesPerCallbackAll);
|
||||
}
|
||||
XAudioJSResampleRefill();
|
||||
while (index < audioLengthRequested) {
|
||||
var index2 = 0;
|
||||
while (index2 < samplesPerCallbackAll && XAudioJSResampleBufferStart != XAudioJSResampleBufferEnd) {
|
||||
XAudioJSMediaStreamBuffer[index2++] = XAudioJSResampledBuffer[XAudioJSResampleBufferStart++];
|
||||
if (XAudioJSResampleBufferStart == XAudioJSResampleBufferSize) {
|
||||
XAudioJSResampleBufferStart = 0;
|
||||
}
|
||||
}
|
||||
XAudioJSMediaStreamWorker.postMessage([0, XAudioJSMediaStreamBuffer]);
|
||||
index += XAudioJSSamplesPerCallback;
|
||||
}
|
||||
XAudioJSCallbackAPIEventNotification2();
|
||||
}
|
||||
function XAudioJSResampleRefill() {
|
||||
if (XAudioJSAudioBufferSize > 0) {
|
||||
//Resample a chunk of audio:
|
||||
var resampleLength = XAudioJSResampleControl.resampler(XAudioJSGetBufferSamples());
|
||||
var resampleLength = XAudioJSResampleControl.resampler(XAudioJSAudioBufferSize);
|
||||
var resampledResult = XAudioJSResampleControl.outputBuffer;
|
||||
for (var index2 = 0; index2 < resampleLength;) {
|
||||
XAudioJSResampledBuffer[XAudioJSResampleBufferEnd++] = resampledResult[index2++];
|
||||
|
@ -482,12 +449,28 @@ function XAudioJSResampleRefill() {
|
|||
XAudioJSAudioBufferSize = 0;
|
||||
}
|
||||
}
|
||||
function XAudioJSCallbackAPIEventNotificationCallbackCompatTimerClear() {
|
||||
if (XAudioJSCallbackAPIEventNotificationCallbackCompatTimer) {
|
||||
clearInterval(XAudioJSCallbackAPIEventNotificationCallbackCompatTimer);
|
||||
}
|
||||
}
|
||||
function XAudioJSCallbackAPIEventNotification() {
|
||||
if (typeof XAudioJSCallbackAPIEventNotificationCallback == "function") {
|
||||
XAudioJSCallbackAPIEventNotificationCallback();
|
||||
}
|
||||
}
|
||||
function XAudioJSCallbackAPIEventNotification2() {
|
||||
if (typeof XAudioJSCallbackAPIEventNotificationCallback2 == "function") {
|
||||
XAudioJSCallbackAPIEventNotificationCallback2();
|
||||
}
|
||||
}
|
||||
function XAudioJSCallbackAPIEventNotificationDual() {
|
||||
XAudioJSCallbackAPIEventNotification();
|
||||
XAudioJSCallbackAPIEventNotification2();
|
||||
}
|
||||
function XAudioJSResampledSamplesLeft() {
|
||||
return ((XAudioJSResampleBufferStart <= XAudioJSResampleBufferEnd) ? 0 : XAudioJSResampleBufferSize) + XAudioJSResampleBufferEnd - XAudioJSResampleBufferStart;
|
||||
}
|
||||
function XAudioJSGetBufferSamples() {
|
||||
return XAudioJSGetArraySlice(XAudioJSAudioContextSampleBuffer, XAudioJSAudioBufferSize);
|
||||
}
|
||||
function XAudioJSGetArraySlice(buffer, lengthOf) {
|
||||
//Typed array and normal array buffer section referencing:
|
||||
try {
|
||||
|
|
|
@ -1,10 +1,23 @@
|
|||
//JavaScript Audio Resampler (c) 2011 - Grant Galitz
|
||||
function Resampler(fromSampleRate, toSampleRate, channels, outputBufferSize, noReturn) {
|
||||
this.fromSampleRate = fromSampleRate;
|
||||
this.toSampleRate = toSampleRate;
|
||||
"use strict";
|
||||
//JavaScript Audio Resampler
|
||||
//Copyright (C) 2011-2015 Grant Galitz
|
||||
//Released to Public Domain
|
||||
function Resampler(fromSampleRate, toSampleRate, channels, inputBuffer) {
|
||||
//Input Sample Rate:
|
||||
this.fromSampleRate = +fromSampleRate;
|
||||
//Output Sample Rate:
|
||||
this.toSampleRate = +toSampleRate;
|
||||
//Number of channels:
|
||||
this.channels = channels | 0;
|
||||
this.outputBufferSize = outputBufferSize;
|
||||
this.noReturn = !!noReturn;
|
||||
//Type checking the input buffer:
|
||||
if (typeof inputBuffer != "object") {
|
||||
throw(new Error("inputBuffer is not an object."));
|
||||
}
|
||||
if (!(inputBuffer instanceof Array) && !(inputBuffer instanceof Float32Array) && !(inputBuffer instanceof Float64Array)) {
|
||||
throw(new Error("inputBuffer is not an array or a float32 or a float64 array."));
|
||||
}
|
||||
this.inputBuffer = inputBuffer;
|
||||
//Initialize the resampler:
|
||||
this.initialize();
|
||||
}
|
||||
Resampler.prototype.initialize = function () {
|
||||
|
@ -14,6 +27,7 @@ Resampler.prototype.initialize = function () {
|
|||
//Setup a resampler bypass:
|
||||
this.resampler = this.bypassResampler; //Resampler just returns what was passed through.
|
||||
this.ratioWeight = 1;
|
||||
this.outputBuffer = this.inputBuffer;
|
||||
}
|
||||
else {
|
||||
this.ratioWeight = this.fromSampleRate / this.toSampleRate;
|
||||
|
@ -44,10 +58,9 @@ Resampler.prototype.initialize = function () {
|
|||
}
|
||||
}
|
||||
Resampler.prototype.compileLinearInterpolationFunction = function () {
|
||||
var toCompile = "var bufferLength = buffer.length;\
|
||||
var outLength = this.outputBufferSize;\
|
||||
if ((bufferLength % " + this.channels + ") == 0) {\
|
||||
var toCompile = "var outputOffset = 0;\
|
||||
if (bufferLength > 0) {\
|
||||
var buffer = this.inputBuffer;\
|
||||
var weight = this.lastWeight;\
|
||||
var firstWeight = 0;\
|
||||
var secondWeight = 0;\
|
||||
|
@ -62,7 +75,7 @@ Resampler.prototype.compileLinearInterpolationFunction = function () {
|
|||
}
|
||||
toCompile += "}\
|
||||
weight -= 1;\
|
||||
for (bufferLength -= " + this.channels + ", sourceOffset = Math.floor(weight) * " + this.channels + "; outputOffset < outLength && sourceOffset < bufferLength;) {\
|
||||
for (bufferLength -= " + this.channels + ", sourceOffset = Math.floor(weight) * " + this.channels + "; sourceOffset < bufferLength;) {\
|
||||
secondWeight = weight % 1;\
|
||||
firstWeight = 1 - secondWeight;";
|
||||
for (var channel = 0; channel < this.channels; ++channel) {
|
||||
|
@ -75,22 +88,14 @@ Resampler.prototype.compileLinearInterpolationFunction = function () {
|
|||
toCompile += "this.lastOutput[" + channel + "] = buffer[sourceOffset++];";
|
||||
}
|
||||
toCompile += "this.lastWeight = weight % 1;\
|
||||
return this.bufferSlice(outputOffset);\
|
||||
}\
|
||||
else {\
|
||||
return (this.noReturn) ? 0 : [];\
|
||||
}\
|
||||
}\
|
||||
else {\
|
||||
throw(new Error(\"Buffer was of incorrect sample length.\"));\
|
||||
}";
|
||||
this.resampler = Function("buffer", toCompile);
|
||||
return outputOffset;";
|
||||
this.resampler = Function("bufferLength", toCompile);
|
||||
}
|
||||
Resampler.prototype.compileMultiTapFunction = function () {
|
||||
var toCompile = "var bufferLength = buffer.length;\
|
||||
var outLength = this.outputBufferSize;\
|
||||
if ((bufferLength % " + this.channels + ") == 0) {\
|
||||
var toCompile = "var outputOffset = 0;\
|
||||
if (bufferLength > 0) {\
|
||||
var buffer = this.inputBuffer;\
|
||||
var weight = 0;";
|
||||
for (var channel = 0; channel < this.channels; ++channel) {
|
||||
toCompile += "var output" + channel + " = 0;"
|
||||
|
@ -100,7 +105,6 @@ Resampler.prototype.compileMultiTapFunction = function () {
|
|||
var alreadyProcessedTail = !this.tailExists;\
|
||||
this.tailExists = false;\
|
||||
var outputBuffer = this.outputBuffer;\
|
||||
var outputOffset = 0;\
|
||||
var currentPosition = 0;\
|
||||
do {\
|
||||
if (alreadyProcessedTail) {\
|
||||
|
@ -147,56 +151,19 @@ Resampler.prototype.compileMultiTapFunction = function () {
|
|||
toCompile += "this.tailExists = true;\
|
||||
break;\
|
||||
}\
|
||||
} while (actualPosition < bufferLength && outputOffset < outLength);\
|
||||
return this.bufferSlice(outputOffset);\
|
||||
} while (actualPosition < bufferLength);\
|
||||
}\
|
||||
else {\
|
||||
return (this.noReturn) ? 0 : [];\
|
||||
}\
|
||||
}\
|
||||
else {\
|
||||
throw(new Error(\"Buffer was of incorrect sample length.\"));\
|
||||
}";
|
||||
this.resampler = Function("buffer", toCompile);
|
||||
return outputOffset;";
|
||||
this.resampler = Function("bufferLength", toCompile);
|
||||
}
|
||||
Resampler.prototype.bypassResampler = function (buffer) {
|
||||
if (this.noReturn) {
|
||||
//Set the buffer passed as our own, as we don't need to resample it:
|
||||
this.outputBuffer = buffer;
|
||||
return buffer.length;
|
||||
}
|
||||
else {
|
||||
//Just return the buffer passsed:
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
Resampler.prototype.bufferSlice = function (sliceAmount) {
|
||||
if (this.noReturn) {
|
||||
//If we're going to access the properties directly from this object:
|
||||
return sliceAmount;
|
||||
}
|
||||
else {
|
||||
//Typed array and normal array buffer section referencing:
|
||||
try {
|
||||
return this.outputBuffer.subarray(0, sliceAmount);
|
||||
}
|
||||
catch (error) {
|
||||
try {
|
||||
//Regular array pass:
|
||||
this.outputBuffer.length = sliceAmount;
|
||||
return this.outputBuffer;
|
||||
}
|
||||
catch (error) {
|
||||
//Nightly Firefox 4 used to have the subarray function named as slice:
|
||||
return this.outputBuffer.slice(0, sliceAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
Resampler.prototype.bypassResampler = function (upTo) {
|
||||
return upTo;
|
||||
}
|
||||
Resampler.prototype.initializeBuffers = function () {
|
||||
//Initialize the internal buffer:
|
||||
var outputBufferSize = (Math.ceil(this.inputBuffer.length * this.toSampleRate / this.fromSampleRate / this.channels * 1.000000476837158203125) * this.channels) + this.channels;
|
||||
try {
|
||||
this.outputBuffer = new Float32Array(this.outputBufferSize);
|
||||
this.outputBuffer = new Float32Array(outputBufferSize);
|
||||
this.lastOutput = new Float32Array(this.channels);
|
||||
}
|
||||
catch (error) {
|
||||
|
|
Loading…
Add table
Reference in a new issue