Initial commit
@@ -1,2 +0,0 @@
|
||||
# ComfyUI_GB_Player
|
||||
Simple ComfyUI Game Boy [Advance] Player based on gbaONLiNE and IodineGBA
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import os
|
||||
|
||||
|
||||
WEB_DIRECTORY = os.path.join(os.path.dirname(__file__), "web")
|
||||
|
||||
class ComfyGameBoyPlayer:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {}}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
OUTPUT_NODE = True
|
||||
FUNCTION = "run"
|
||||
CATEGORY = "fun"
|
||||
|
||||
def run(self):
|
||||
return ()
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"ComfyGameBoyPlayer": ComfyGameBoyPlayer,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ComfyGameBoyPlayer": "CGB Player",
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
[project]
|
||||
name = "comfyui-gb-player"
|
||||
version = "0.0.1"
|
||||
description = "Simple ComfyUI Game Boy [Advance] Player based on gbaONLiNE and IodineGBA"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
license = { text = "MIT License" }
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent"
|
||||
]
|
||||
dynamic = ["dependencies"]
|
||||
|
||||
[tool.setuptools.dynamic]
|
||||
dependencies = { file = ["requirements.txt"] }
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://github.com/WASasquatch/ComfyUI_GB_Player"
|
||||
"Bug Tracker" = "https://github.com/WASasquatch/ComfyUI_GB_Player/issues"
|
||||
|
||||
[tool.comfy]
|
||||
PublisherId = "was"
|
||||
DisplayName = "ComfyUI GB Player"
|
||||
requires-comfyui = ">=1.0.0"
|
||||
@@ -0,0 +1,232 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
const EXT_NAME = "WAS.GameBoyPlayer";
|
||||
const NODE_NAME = "ComfyGameBoyPlayer";
|
||||
|
||||
const DEFAULT_NODE_SIZE = [730, 480];
|
||||
|
||||
const STATE = {
|
||||
container: null,
|
||||
nodeIdToIframe: new Map(),
|
||||
};
|
||||
|
||||
function readCssVar(style, name) {
|
||||
const v = style.getPropertyValue(name);
|
||||
return v ? String(v).trim() : "";
|
||||
}
|
||||
|
||||
function computeThemeTokens() {
|
||||
const rootStyle = getComputedStyle(document.documentElement);
|
||||
const bodyStyle = getComputedStyle(document.body);
|
||||
|
||||
const bg =
|
||||
readCssVar(rootStyle, "--comfy-menu-bg") ||
|
||||
readCssVar(rootStyle, "--background-color") ||
|
||||
readCssVar(rootStyle, "--bg-color") ||
|
||||
bodyStyle.backgroundColor ||
|
||||
"";
|
||||
const fg =
|
||||
readCssVar(rootStyle, "--input-text") ||
|
||||
readCssVar(rootStyle, "--text-color") ||
|
||||
readCssVar(rootStyle, "--fg-color") ||
|
||||
bodyStyle.color ||
|
||||
"";
|
||||
const border =
|
||||
readCssVar(rootStyle, "--border-color") ||
|
||||
readCssVar(rootStyle, "--comfy-border-color") ||
|
||||
"";
|
||||
const accent =
|
||||
readCssVar(rootStyle, "--primary-color") ||
|
||||
readCssVar(rootStyle, "--accent-color") ||
|
||||
readCssVar(rootStyle, "--comfy-accent") ||
|
||||
"";
|
||||
|
||||
return {
|
||||
bg,
|
||||
fg,
|
||||
border,
|
||||
accent,
|
||||
};
|
||||
}
|
||||
|
||||
function postThemeToIframe(iframe) {
|
||||
try {
|
||||
const payload = {
|
||||
type: "was-gba-theme",
|
||||
tokens: computeThemeTokens(),
|
||||
};
|
||||
iframe.contentWindow?.postMessage(payload, "*");
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
function attachThemeSync(iframe) {
|
||||
if (iframe.__wasThemeSyncAttached) return;
|
||||
iframe.__wasThemeSyncAttached = true;
|
||||
|
||||
iframe.addEventListener("load", () => {
|
||||
postThemeToIframe(iframe);
|
||||
});
|
||||
|
||||
const onMsg = (ev) => {
|
||||
try {
|
||||
if (ev?.source !== iframe.contentWindow) return;
|
||||
if (!ev?.data || typeof ev.data !== "object") return;
|
||||
if (ev.data.type !== "was-gba-request-theme") return;
|
||||
postThemeToIframe(iframe);
|
||||
} catch (e) {
|
||||
}
|
||||
};
|
||||
window.addEventListener("message", onMsg);
|
||||
|
||||
const mo = new MutationObserver(() => postThemeToIframe(iframe));
|
||||
mo.observe(document.documentElement, { attributes: true, attributeFilter: ["class", "style"] });
|
||||
}
|
||||
|
||||
function getContainer() {
|
||||
if (STATE.container) return STATE.container;
|
||||
const el = document.createElement("div");
|
||||
el.id = "was-gba-overlay";
|
||||
el.style.position = "absolute";
|
||||
el.style.left = "0";
|
||||
el.style.top = "0";
|
||||
el.style.width = "0";
|
||||
el.style.height = "0";
|
||||
el.style.pointerEvents = "none";
|
||||
el.style.zIndex = "1000";
|
||||
document.body.appendChild(el);
|
||||
STATE.container = el;
|
||||
return el;
|
||||
}
|
||||
|
||||
function ensureIframeForNode(node) {
|
||||
const key = String(node.id);
|
||||
const existing = STATE.nodeIdToIframe.get(key);
|
||||
if (existing) return existing;
|
||||
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.setAttribute("sandbox", "allow-scripts allow-forms allow-downloads");
|
||||
iframe.style.position = "absolute";
|
||||
iframe.style.border = "0";
|
||||
iframe.style.pointerEvents = "auto";
|
||||
iframe.style.background = "#000";
|
||||
iframe.style.left = "0px";
|
||||
iframe.style.top = "0px";
|
||||
iframe.style.width = "0px";
|
||||
iframe.style.height = "0px";
|
||||
iframe.style.display = "none";
|
||||
iframe.style.borderRadius = "8px";
|
||||
|
||||
const src = "extensions/ComfyUI_GB_Player/gba/player/index.html";
|
||||
iframe.src = src;
|
||||
|
||||
attachThemeSync(iframe);
|
||||
|
||||
getContainer().appendChild(iframe);
|
||||
STATE.nodeIdToIframe.set(key, iframe);
|
||||
|
||||
const oldOnRemoved = node.onRemoved;
|
||||
node.onRemoved = function () {
|
||||
try {
|
||||
const i = STATE.nodeIdToIframe.get(String(this.id));
|
||||
if (i) i.remove();
|
||||
STATE.nodeIdToIframe.delete(String(this.id));
|
||||
} catch (e) {
|
||||
}
|
||||
return oldOnRemoved ? oldOnRemoved.apply(this, arguments) : undefined;
|
||||
};
|
||||
|
||||
return iframe;
|
||||
}
|
||||
|
||||
function updateIframeRect(node, iframe) {
|
||||
const canvas = app.canvas;
|
||||
const canvasEl = canvas?.canvas;
|
||||
if (!canvasEl) return;
|
||||
|
||||
const rect = canvasEl.getBoundingClientRect();
|
||||
const ds = canvas.ds;
|
||||
const scale = ds?.scale ?? 1;
|
||||
const offset = ds?.offset ?? [0, 0];
|
||||
|
||||
const x = rect.left + (node.pos[0] + offset[0]) * scale;
|
||||
const y = rect.top + (node.pos[1] + offset[1]) * scale;
|
||||
const w = node.size[0] * scale;
|
||||
const h = node.size[1] * scale;
|
||||
|
||||
const titleHRaw =
|
||||
(typeof node?.title_height === "number" && Number.isFinite(node.title_height) && node.title_height) ||
|
||||
(typeof node?.constructor?.title_height === "number" &&
|
||||
Number.isFinite(node.constructor.title_height) &&
|
||||
node.constructor.title_height) ||
|
||||
globalThis?.LiteGraph?.NODE_TITLE_HEIGHT ||
|
||||
30;
|
||||
const titleH = titleHRaw * scale;
|
||||
const insetX = 8 * scale;
|
||||
const insetTop = 0;
|
||||
const insetBottom = 8 * scale;
|
||||
const headerAdjust = 20 * scale;
|
||||
const innerX = x + insetX;
|
||||
const innerY = y + titleH + insetTop - headerAdjust;
|
||||
const innerW = w - insetX * 2;
|
||||
const innerH = h - titleH - insetTop - insetBottom + headerAdjust;
|
||||
|
||||
const nx = Number.isFinite(innerX) ? innerX : 0;
|
||||
const ny = Number.isFinite(innerY) ? innerY : 0;
|
||||
const nw = Number.isFinite(innerW) ? innerW : 0;
|
||||
const nh = Number.isFinite(innerH) ? innerH : 0;
|
||||
|
||||
iframe.style.left = `${Math.round(nx)}px`;
|
||||
iframe.style.top = `${Math.round(ny)}px`;
|
||||
iframe.style.width = `${Math.max(0, Math.round(nw))}px`;
|
||||
iframe.style.height = `${Math.max(0, Math.round(nh))}px`;
|
||||
|
||||
const isCollapsed = !!node.flags?.collapsed;
|
||||
const hasArea = nw >= 2 && nh >= 2;
|
||||
iframe.style.display = !isCollapsed && hasArea ? "block" : "none";
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: EXT_NAME,
|
||||
async beforeRegisterNodeDef(nodeType, nodeData) {
|
||||
if (nodeData?.name !== NODE_NAME) return;
|
||||
|
||||
const oldOnNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
const r = oldOnNodeCreated ? oldOnNodeCreated.apply(this, arguments) : undefined;
|
||||
try {
|
||||
if (!Array.isArray(this.size) || this.size.length < 2) {
|
||||
this.size = [...DEFAULT_NODE_SIZE];
|
||||
} else {
|
||||
this.size[0] = Math.max(this.size[0] ?? 0, DEFAULT_NODE_SIZE[0]);
|
||||
this.size[1] = Math.max(this.size[1] ?? 0, DEFAULT_NODE_SIZE[1]);
|
||||
}
|
||||
this.setDirtyCanvas?.(true, true);
|
||||
} catch (e) {
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
const oldOnDrawForeground = nodeType.prototype.onDrawForeground;
|
||||
nodeType.prototype.onDrawForeground = function (ctx) {
|
||||
const r = oldOnDrawForeground ? oldOnDrawForeground.apply(this, arguments) : undefined;
|
||||
try {
|
||||
const iframe = ensureIframeForNode(this);
|
||||
updateIframeRect(this, iframe);
|
||||
} catch (e) {
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
const oldOnResize = nodeType.prototype.onResize;
|
||||
nodeType.prototype.onResize = function (size) {
|
||||
const r = oldOnResize ? oldOnResize.apply(this, arguments) : undefined;
|
||||
try {
|
||||
const iframe = ensureIframeForNode(this);
|
||||
updateIframeRect(this, iframe);
|
||||
} catch (e) {
|
||||
}
|
||||
return r;
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
<img width="150" height="150" src="https://raw.githubusercontent.com/ayvacs/gba.js.org/gh-pages/assets/macos-11x/apple-touch-icon.png" alt="logo">
|
||||
<br>
|
||||
<h3>gba.js.org</h3>
|
||||
<sup>MIT License</sup>
|
||||
<br>
|
||||
<sup>Copyright © 2021-present AYVACS. - All rights reserved.</sup>
|
||||
<br> <br>
|
||||
<sup>Last Revised: December 20th, 2021</sup>
|
||||
|
||||
---
|
||||
|
||||
### Terms
|
||||
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:
|
||||
|
||||
---
|
||||
|
||||
### License Inclusion
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
---
|
||||
|
||||
### Liability
|
||||
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 OTHER
|
||||
|
After Width: | Height: | Size: 714 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 562 KiB |
@@ -0,0 +1,14 @@
|
||||
JavaScript GameBoy Advance Emulator
|
||||
=================================
|
||||
|
||||
**Copyright (C) 2010 - 2017 Grant Galitz**
|
||||
|
||||
A GameBoy Advance emulator that utilizes HTML5 canvas and JavaScript audio APIs to provide a full emulation of the console.
|
||||
|
||||
**License:**
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,653 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2015 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceCPU(IOCore) {
|
||||
this.IOCore = IOCore;
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.initialize = function () {
|
||||
this.memory = this.IOCore.memory;
|
||||
this.wait = this.IOCore.wait;
|
||||
this.mul64ResultHigh = 0; //Scratch MUL64.
|
||||
this.mul64ResultLow = 0; //Scratch MUL64.
|
||||
this.initializeRegisters();
|
||||
this.ARM = new ARMInstructionSet(this);
|
||||
this.THUMB = new THUMBInstructionSet(this);
|
||||
//this.swi = new GameBoyAdvanceSWI(this);
|
||||
this.IOCore.assignInstructionCoreReferences(this.ARM, this.THUMB);
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.initializeRegisters = function () {
|
||||
/*
|
||||
R0-R7 Are known as the low registers.
|
||||
R8-R12 Are the high registers.
|
||||
R13 is the stack pointer.
|
||||
R14 is the link register.
|
||||
R15 is the program counter.
|
||||
CPSR is the program status register.
|
||||
SPSR is the saved program status register.
|
||||
*/
|
||||
//Normal R0-R15 Registers:
|
||||
this.registers = getInt32Array(16);
|
||||
//Used to copy back the R8-R14 state for normal operations:
|
||||
this.registersUSR = getInt32Array(7);
|
||||
//Fast IRQ mode registers (R8-R14):
|
||||
this.registersFIQ = getInt32Array(7);
|
||||
//Supervisor mode registers (R13-R14):
|
||||
this.registersSVC = getInt32Array(2);
|
||||
//Abort mode registers (R13-R14):
|
||||
this.registersABT = getInt32Array(2);
|
||||
//IRQ mode registers (R13-R14):
|
||||
this.registersIRQ = getInt32Array(2);
|
||||
//Undefined mode registers (R13-R14):
|
||||
this.registersUND = getInt32Array(2);
|
||||
//CPSR Register:
|
||||
this.branchFlags = ARMCPSRAttributeTable();
|
||||
this.modeFlags = 0xD3;
|
||||
//Banked SPSR Registers:
|
||||
this.SPSR = getUint16Array(5);
|
||||
this.SPSR[0] = 0xD3; //FIQ
|
||||
this.SPSR[1] = 0xD3; //IRQ
|
||||
this.SPSR[2] = 0xD3; //Supervisor
|
||||
this.SPSR[3] = 0xD3; //Abort
|
||||
this.SPSR[4] = 0xD3; //Undefined
|
||||
this.triggeredIRQ = 0; //Pending IRQ found.
|
||||
//Pre-initialize stack pointers if no BIOS loaded:
|
||||
if (this.IOCore.SKIPBoot) {
|
||||
this.HLEReset();
|
||||
}
|
||||
//Start in fully bubbled pipeline mode:
|
||||
this.IOCore.flagBubble();
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.HLEReset = function () {
|
||||
this.registersSVC[0] = 0x3007FE0;
|
||||
this.registersIRQ[0] = 0x3007FA0;
|
||||
this.registers[13] = 0x3007F00;
|
||||
this.registers[15] = 0x8000000;
|
||||
this.modeFlags = this.modeFlags | 0x1f;
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.branch = function (branchTo) {
|
||||
branchTo = branchTo | 0;
|
||||
//if ((branchTo | 0) > 0x3FFF || this.IOCore.BIOSFound) {
|
||||
//Branch to new address:
|
||||
this.registers[15] = branchTo | 0;
|
||||
//Mark pipeline as invalid:
|
||||
this.IOCore.flagBubble();
|
||||
//Next PC fetch has to update the address bus:
|
||||
this.wait.NonSequentialBroadcastClear();
|
||||
/*}
|
||||
else {
|
||||
//We're branching into BIOS, handle specially:
|
||||
if ((branchTo | 0) == 0x130) {
|
||||
//IRQ mode exit handling:
|
||||
//ROM IRQ handling returns back from its own subroutine back to BIOS at this address.
|
||||
this.HLEIRQExit();
|
||||
}
|
||||
else {
|
||||
//Reset to start of ROM if no BIOS ROM found:
|
||||
this.HLEReset();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.triggerIRQ = function (didFire) {
|
||||
this.triggeredIRQ = didFire | 0;
|
||||
this.assertIRQ();
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.assertIRQ = function () {
|
||||
if ((this.triggeredIRQ | 0) != 0 && (this.modeFlags & 0x80) == 0) {
|
||||
this.IOCore.flagIRQ();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.getCurrentFetchValue = function () {
|
||||
if ((this.modeFlags & 0x20) != 0) {
|
||||
return this.THUMB.getCurrentFetchValue() | 0;
|
||||
}
|
||||
else {
|
||||
return this.ARM.getCurrentFetchValue() | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.enterARM = function () {
|
||||
this.modeFlags = this.modeFlags & 0xdf;
|
||||
this.THUMBBitModify(false);
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.enterTHUMB = function () {
|
||||
this.modeFlags = this.modeFlags | 0x20;
|
||||
this.THUMBBitModify(true);
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.getLR = function () {
|
||||
//Get the previous instruction address:
|
||||
if ((this.modeFlags & 0x20) != 0) {
|
||||
return this.THUMB.getLR() | 0;
|
||||
}
|
||||
else {
|
||||
return this.ARM.getLR() | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.THUMBBitModify = function (isThumb) {
|
||||
if (isThumb) {
|
||||
this.IOCore.flagTHUMB();
|
||||
}
|
||||
else {
|
||||
this.IOCore.deflagTHUMB();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.IRQinARM = function () {
|
||||
//Mode bits are set to IRQ:
|
||||
this.switchMode(0x12);
|
||||
//Save link register:
|
||||
this.registers[14] = this.ARM.getIRQLR() | 0;
|
||||
//Disable IRQ:
|
||||
this.modeFlags = this.modeFlags | 0x80;
|
||||
//if (this.IOCore.BIOSFound) {
|
||||
//IRQ exception vector:
|
||||
this.branch(0x18);
|
||||
/*}
|
||||
else {
|
||||
//HLE the IRQ entrance:
|
||||
this.HLEIRQEnter();
|
||||
}*/
|
||||
//Deflag IRQ from state:
|
||||
this.IOCore.deflagIRQ();
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.IRQinTHUMB = function () {
|
||||
//Mode bits are set to IRQ:
|
||||
this.switchMode(0x12);
|
||||
//Save link register:
|
||||
this.registers[14] = this.THUMB.getIRQLR() | 0;
|
||||
//Disable IRQ:
|
||||
this.modeFlags = this.modeFlags | 0x80;
|
||||
//Exception always enter ARM mode:
|
||||
this.enterARM();
|
||||
//if (this.IOCore.BIOSFound) {
|
||||
//IRQ exception vector:
|
||||
this.branch(0x18);
|
||||
/*}
|
||||
else {
|
||||
//HLE the IRQ entrance:
|
||||
this.HLEIRQEnter();
|
||||
}*/
|
||||
//Deflag IRQ from state:
|
||||
this.IOCore.deflagIRQ();
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.HLEIRQEnter = function () {
|
||||
//Get the base address:
|
||||
var currentAddress = this.registers[0xD] | 0;
|
||||
//Updating the address bus away from PC fetch:
|
||||
this.wait.NonSequentialBroadcast();
|
||||
//Push register(s) into memory:
|
||||
for (var rListPosition = 0xF; (rListPosition | 0) > -1; rListPosition = ((rListPosition | 0) - 1) | 0) {
|
||||
if ((0x500F & (1 << (rListPosition | 0))) != 0) {
|
||||
//Push a register into memory:
|
||||
currentAddress = ((currentAddress | 0) - 4) | 0;
|
||||
this.memory.memoryWrite32(currentAddress | 0, this.registers[rListPosition | 0] | 0);
|
||||
}
|
||||
}
|
||||
//Store the updated base address back into register:
|
||||
this.registers[0xD] = currentAddress | 0;
|
||||
//Updating the address bus back to PC fetch:
|
||||
this.wait.NonSequentialBroadcast();
|
||||
this.registers[0] = 0x4000000;
|
||||
//Save link register:
|
||||
this.registers[14] = 0x130;
|
||||
//Skip BIOS ROM processing:
|
||||
this.branch(this.read32(0x3FFFFFC) & -0x4);
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.HLEIRQExit = function () {
|
||||
//Get the base address:
|
||||
var currentAddress = this.registers[0xD] | 0;
|
||||
//Updating the address bus away from PC fetch:
|
||||
this.wait.NonSequentialBroadcast();
|
||||
//Load register(s) from memory:
|
||||
for (var rListPosition = 0; (rListPosition | 0) < 0x10; rListPosition = ((rListPosition | 0) + 1) | 0) {
|
||||
if ((0x500F & (1 << (rListPosition | 0))) != 0) {
|
||||
//Load a register from memory:
|
||||
this.registers[rListPosition & 0xF] = this.memory.memoryRead32(currentAddress | 0) | 0;
|
||||
currentAddress = ((currentAddress | 0) + 4) | 0;
|
||||
}
|
||||
}
|
||||
//Store the updated base address back into register:
|
||||
this.registers[0xD] = currentAddress | 0;
|
||||
//Updating the address bus back to PC fetch:
|
||||
this.wait.NonSequentialBroadcast();
|
||||
//Return from an exception mode:
|
||||
var data = this.branchFlags.setSUBFlags(this.registers[0xE] | 0, 4) | 0;
|
||||
//Restore SPSR to CPSR:
|
||||
data = data & (-4 >> (this.SPSRtoCPSR() >> 5));
|
||||
//We performed a branch:
|
||||
this.branch(data | 0);
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.SWI = function () {
|
||||
//if (this.IOCore.BIOSFound) {
|
||||
//Mode bits are set to SWI:
|
||||
this.switchMode(0x13);
|
||||
//Save link register:
|
||||
this.registers[14] = this.getLR() | 0;
|
||||
//Disable IRQ:
|
||||
this.modeFlags = this.modeFlags | 0x80;
|
||||
//Exception always enter ARM mode:
|
||||
this.enterARM();
|
||||
//SWI exception vector:
|
||||
this.branch(0x8);
|
||||
/*}
|
||||
else {
|
||||
if ((this.modeFlags & 0x20) != 0) {
|
||||
this.THUMB.incrementProgramCounter();
|
||||
//HLE the SWI command:
|
||||
this.swi.execute(this.THUMB.getSWICode() | 0);
|
||||
}
|
||||
else {
|
||||
this.ARM.incrementProgramCounter();
|
||||
//HLE the SWI command:
|
||||
this.swi.execute(this.ARM.getSWICode() | 0);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.UNDEFINED = function () {
|
||||
//Only process undefined instruction if BIOS loaded:
|
||||
//if (this.IOCore.BIOSFound) {
|
||||
//Mode bits are set to SWI:
|
||||
this.switchMode(0x1B);
|
||||
//Save link register:
|
||||
this.registers[14] = this.getLR() | 0;
|
||||
//Disable IRQ:
|
||||
this.modeFlags = this.modeFlags | 0x80;
|
||||
//Exception always enter ARM mode:
|
||||
this.enterARM();
|
||||
//Undefined exception vector:
|
||||
this.branch(0x4);
|
||||
/*}
|
||||
else {
|
||||
//Pretend we didn't execute the bad instruction then:
|
||||
if ((this.modeFlags & 0x20) != 0) {
|
||||
this.THUMB.incrementProgramCounter();
|
||||
}
|
||||
else {
|
||||
this.ARM.incrementProgramCounter();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.SPSRtoCPSR = function () {
|
||||
//Used for leaving an exception and returning to the previous state:
|
||||
var bank = 1;
|
||||
switch (this.modeFlags & 0x1f) {
|
||||
case 0x12: //IRQ
|
||||
break;
|
||||
case 0x13: //Supervisor
|
||||
bank = 2;
|
||||
break;
|
||||
case 0x11: //FIQ
|
||||
bank = 0;
|
||||
break;
|
||||
case 0x17: //Abort
|
||||
bank = 3;
|
||||
break;
|
||||
case 0x1B: //Undefined
|
||||
bank = 4;
|
||||
break;
|
||||
default: //User & system lacks SPSR
|
||||
return this.modeFlags & 0x20;
|
||||
}
|
||||
var spsr = this.SPSR[bank | 0] | 0;
|
||||
this.branchFlags.setNZCV(spsr << 20);
|
||||
this.switchRegisterBank(spsr & 0x1F);
|
||||
this.modeFlags = spsr & 0xFF;
|
||||
this.assertIRQ();
|
||||
this.THUMBBitModify((spsr & 0x20) != 0);
|
||||
return spsr & 0x20;
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.switchMode = function (newMode) {
|
||||
newMode = newMode | 0;
|
||||
this.CPSRtoSPSR(newMode | 0);
|
||||
this.switchRegisterBank(newMode | 0);
|
||||
this.modeFlags = (this.modeFlags & 0xe0) | (newMode | 0);
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.CPSRtoSPSR = function (newMode) {
|
||||
//Used for entering an exception and saving the previous state:
|
||||
var spsr = this.modeFlags & 0xFF;
|
||||
spsr = spsr | (this.branchFlags.getNZCV() >> 20);
|
||||
switch (newMode | 0) {
|
||||
case 0x12: //IRQ
|
||||
this.SPSR[1] = spsr | 0;
|
||||
break;
|
||||
case 0x13: //Supervisor
|
||||
this.SPSR[2] = spsr | 0;
|
||||
break;
|
||||
case 0x11: //FIQ
|
||||
this.SPSR[0] = spsr | 0;
|
||||
break;
|
||||
case 0x17: //Abort
|
||||
this.SPSR[3] = spsr | 0;
|
||||
break;
|
||||
case 0x1B: //Undefined
|
||||
this.SPSR[4] = spsr | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.switchRegisterBank = function (newMode) {
|
||||
newMode = newMode | 0;
|
||||
switch (this.modeFlags & 0x1F) {
|
||||
case 0x10:
|
||||
case 0x1F:
|
||||
this.registersUSR[0] = this.registers[8] | 0;
|
||||
this.registersUSR[1] = this.registers[9] | 0;
|
||||
this.registersUSR[2] = this.registers[10] | 0;
|
||||
this.registersUSR[3] = this.registers[11] | 0;
|
||||
this.registersUSR[4] = this.registers[12] | 0;
|
||||
this.registersUSR[5] = this.registers[13] | 0;
|
||||
this.registersUSR[6] = this.registers[14] | 0;
|
||||
break;
|
||||
case 0x11:
|
||||
this.registersFIQ[0] = this.registers[8] | 0;
|
||||
this.registersFIQ[1] = this.registers[9] | 0;
|
||||
this.registersFIQ[2] = this.registers[10] | 0;
|
||||
this.registersFIQ[3] = this.registers[11] | 0;
|
||||
this.registersFIQ[4] = this.registers[12] | 0;
|
||||
this.registersFIQ[5] = this.registers[13] | 0;
|
||||
this.registersFIQ[6] = this.registers[14] | 0;
|
||||
break;
|
||||
case 0x12:
|
||||
this.registersUSR[0] = this.registers[8] | 0;
|
||||
this.registersUSR[1] = this.registers[9] | 0;
|
||||
this.registersUSR[2] = this.registers[10] | 0;
|
||||
this.registersUSR[3] = this.registers[11] | 0;
|
||||
this.registersUSR[4] = this.registers[12] | 0;
|
||||
this.registersIRQ[0] = this.registers[13] | 0;
|
||||
this.registersIRQ[1] = this.registers[14] | 0;
|
||||
break;
|
||||
case 0x13:
|
||||
this.registersUSR[0] = this.registers[8] | 0;
|
||||
this.registersUSR[1] = this.registers[9] | 0;
|
||||
this.registersUSR[2] = this.registers[10] | 0;
|
||||
this.registersUSR[3] = this.registers[11] | 0;
|
||||
this.registersUSR[4] = this.registers[12] | 0;
|
||||
this.registersSVC[0] = this.registers[13] | 0;
|
||||
this.registersSVC[1] = this.registers[14] | 0;
|
||||
break;
|
||||
case 0x17:
|
||||
this.registersUSR[0] = this.registers[8] | 0;
|
||||
this.registersUSR[1] = this.registers[9] | 0;
|
||||
this.registersUSR[2] = this.registers[10] | 0;
|
||||
this.registersUSR[3] = this.registers[11] | 0;
|
||||
this.registersUSR[4] = this.registers[12] | 0;
|
||||
this.registersABT[0] = this.registers[13] | 0;
|
||||
this.registersABT[1] = this.registers[14] | 0;
|
||||
break;
|
||||
case 0x1B:
|
||||
this.registersUSR[0] = this.registers[8] | 0;
|
||||
this.registersUSR[1] = this.registers[9] | 0;
|
||||
this.registersUSR[2] = this.registers[10] | 0;
|
||||
this.registersUSR[3] = this.registers[11] | 0;
|
||||
this.registersUSR[4] = this.registers[12] | 0;
|
||||
this.registersUND[0] = this.registers[13] | 0;
|
||||
this.registersUND[1] = this.registers[14] | 0;
|
||||
}
|
||||
switch (newMode | 0) {
|
||||
case 0x10:
|
||||
case 0x1F:
|
||||
this.registers[8] = this.registersUSR[0] | 0;
|
||||
this.registers[9] = this.registersUSR[1] | 0;
|
||||
this.registers[10] = this.registersUSR[2] | 0;
|
||||
this.registers[11] = this.registersUSR[3] | 0;
|
||||
this.registers[12] = this.registersUSR[4] | 0;
|
||||
this.registers[13] = this.registersUSR[5] | 0;
|
||||
this.registers[14] = this.registersUSR[6] | 0;
|
||||
break;
|
||||
case 0x11:
|
||||
this.registers[8] = this.registersFIQ[0] | 0;
|
||||
this.registers[9] = this.registersFIQ[1] | 0;
|
||||
this.registers[10] = this.registersFIQ[2] | 0;
|
||||
this.registers[11] = this.registersFIQ[3] | 0;
|
||||
this.registers[12] = this.registersFIQ[4] | 0;
|
||||
this.registers[13] = this.registersFIQ[5] | 0;
|
||||
this.registers[14] = this.registersFIQ[6] | 0;
|
||||
break;
|
||||
case 0x12:
|
||||
this.registers[8] = this.registersUSR[0] | 0;
|
||||
this.registers[9] = this.registersUSR[1] | 0;
|
||||
this.registers[10] = this.registersUSR[2] | 0;
|
||||
this.registers[11] = this.registersUSR[3] | 0;
|
||||
this.registers[12] = this.registersUSR[4] | 0;
|
||||
this.registers[13] = this.registersIRQ[0] | 0;
|
||||
this.registers[14] = this.registersIRQ[1] | 0;
|
||||
break;
|
||||
case 0x13:
|
||||
this.registers[8] = this.registersUSR[0] | 0;
|
||||
this.registers[9] = this.registersUSR[1] | 0;
|
||||
this.registers[10] = this.registersUSR[2] | 0;
|
||||
this.registers[11] = this.registersUSR[3] | 0;
|
||||
this.registers[12] = this.registersUSR[4] | 0;
|
||||
this.registers[13] = this.registersSVC[0] | 0;
|
||||
this.registers[14] = this.registersSVC[1] | 0;
|
||||
break;
|
||||
case 0x17:
|
||||
this.registers[8] = this.registersUSR[0] | 0;
|
||||
this.registers[9] = this.registersUSR[1] | 0;
|
||||
this.registers[10] = this.registersUSR[2] | 0;
|
||||
this.registers[11] = this.registersUSR[3] | 0;
|
||||
this.registers[12] = this.registersUSR[4] | 0;
|
||||
this.registers[13] = this.registersABT[0] | 0;
|
||||
this.registers[14] = this.registersABT[1] | 0;
|
||||
break;
|
||||
case 0x1B:
|
||||
this.registers[8] = this.registersUSR[0] | 0;
|
||||
this.registers[9] = this.registersUSR[1] | 0;
|
||||
this.registers[10] = this.registersUSR[2] | 0;
|
||||
this.registers[11] = this.registersUSR[3] | 0;
|
||||
this.registers[12] = this.registersUSR[4] | 0;
|
||||
this.registers[13] = this.registersUND[0] | 0;
|
||||
this.registers[14] = this.registersUND[1] | 0;
|
||||
}
|
||||
}
|
||||
if (typeof Math.imul == "function") {
|
||||
//Math.imul found, insert the optimized path in:
|
||||
GameBoyAdvanceCPU.prototype.calculateMUL32 = Math.imul;
|
||||
}
|
||||
else {
|
||||
//Math.imul not found, use the compatibility method:
|
||||
GameBoyAdvanceCPU.prototype.calculateMUL32 = function (rs, rd) {
|
||||
rs = rs | 0;
|
||||
rd = rd | 0;
|
||||
/*
|
||||
We have to split up the 32 bit multiplication,
|
||||
as JavaScript does multiplication on the FPU
|
||||
as double floats, which drops the low bits
|
||||
rather than the high bits.
|
||||
*/
|
||||
var lowMul = (rs & 0xFFFF) * rd;
|
||||
var highMul = (rs >> 16) * rd;
|
||||
//Cut off bits above bit 31 and return with proper sign:
|
||||
return ((highMul << 16) + lowMul) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.performMUL32 = function (rs, rd) {
|
||||
rs = rs | 0;
|
||||
rd = rd | 0;
|
||||
//Predict the internal cycle time:
|
||||
if ((rd >>> 8) == 0 || (rd >>> 8) == 0xFFFFFF) {
|
||||
this.IOCore.wait.CPUInternalSingleCyclePrefetch();
|
||||
}
|
||||
else if ((rd >>> 16) == 0 || (rd >>> 16) == 0xFFFF) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(2);
|
||||
}
|
||||
else if ((rd >>> 24) == 0 || (rd >>> 24) == 0xFF) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(3);
|
||||
}
|
||||
else {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(4);
|
||||
}
|
||||
return this.calculateMUL32(rs | 0, rd | 0) | 0;
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.performMUL32MLA = function (rs, rd) {
|
||||
rs = rs | 0;
|
||||
rd = rd | 0;
|
||||
//Predict the internal cycle time:
|
||||
if ((rd >>> 8) == 0 || (rd >>> 8) == 0xFFFFFF) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(2);
|
||||
}
|
||||
else if ((rd >>> 16) == 0 || (rd >>> 16) == 0xFFFF) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(3);
|
||||
}
|
||||
else if ((rd >>> 24) == 0 || (rd >>> 24) == 0xFF) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(4);
|
||||
}
|
||||
else {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(5);
|
||||
}
|
||||
return this.calculateMUL32(rs | 0, rd | 0) | 0;
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.performMUL64 = function (rs, rd) {
|
||||
rs = rs | 0;
|
||||
rd = rd | 0;
|
||||
//Predict the internal cycle time:
|
||||
if ((rd >>> 8) == 0 || (rd >>> 8) == 0xFFFFFF) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(2);
|
||||
}
|
||||
else if ((rd >>> 16) == 0 || (rd >>> 16) == 0xFFFF) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(3);
|
||||
}
|
||||
else if ((rd >>> 24) == 0 || (rd >>> 24) == 0xFF) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(4);
|
||||
}
|
||||
else {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(5);
|
||||
}
|
||||
//Solve for the high word (Do FPU double divide to bring down high word into the low word):
|
||||
this.mul64ResultHigh = Math.floor((rs * rd) / 0x100000000) | 0;
|
||||
this.mul64ResultLow = this.calculateMUL32(rs | 0, rd | 0) | 0;
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.performMLA64 = function (rs, rd, mlaHigh, mlaLow) {
|
||||
rs = rs | 0;
|
||||
rd = rd | 0;
|
||||
mlaHigh = mlaHigh | 0;
|
||||
mlaLow = mlaLow | 0;
|
||||
//Predict the internal cycle time:
|
||||
if ((rd >>> 8) == 0 || (rd >>> 8) == 0xFFFFFF) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(3);
|
||||
}
|
||||
else if ((rd >>> 16) == 0 || (rd >>> 16) == 0xFFFF) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(4);
|
||||
}
|
||||
else if ((rd >>> 24) == 0 || (rd >>> 24) == 0xFF) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(5);
|
||||
}
|
||||
else {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(6);
|
||||
}
|
||||
//Solve for the high word (Do FPU double divide to bring down high word into the low word):
|
||||
var mulTop = Math.floor((rs * rd) / 0x100000000) | 0;
|
||||
var dirty = (this.calculateMUL32(rs | 0, rd | 0) >>> 0) + (mlaLow >>> 0);
|
||||
this.mul64ResultHigh = ((mulTop | 0) + (mlaHigh | 0) + Math.floor(dirty / 0x100000000)) | 0;
|
||||
this.mul64ResultLow = dirty | 0;
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.performUMUL64 = function (rs, rd) {
|
||||
rs = rs | 0;
|
||||
rd = rd | 0;
|
||||
//Predict the internal cycle time:
|
||||
if ((rd >>> 8) == 0) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(2);
|
||||
}
|
||||
else if ((rd >>> 16) == 0) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(3);
|
||||
}
|
||||
else if ((rd >>> 24) == 0) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(4);
|
||||
}
|
||||
else {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(5);
|
||||
}
|
||||
//Solve for the high word (Do FPU double divide to bring down high word into the low word):
|
||||
this.mul64ResultHigh = (((rs >>> 0) * (rd >>> 0)) / 0x100000000) | 0;
|
||||
this.mul64ResultLow = this.calculateMUL32(rs | 0, rd | 0) | 0;
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.performUMLA64 = function (rs, rd, mlaHigh, mlaLow) {
|
||||
rs = rs | 0;
|
||||
rd = rd | 0;
|
||||
mlaHigh = mlaHigh | 0;
|
||||
mlaLow = mlaLow | 0;
|
||||
//Predict the internal cycle time:
|
||||
if ((rd >>> 8) == 0) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(3);
|
||||
}
|
||||
else if ((rd >>> 16) == 0) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(4);
|
||||
}
|
||||
else if ((rd >>> 24) == 0) {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(5);
|
||||
}
|
||||
else {
|
||||
this.IOCore.wait.CPUInternalCyclePrefetch(6);
|
||||
}
|
||||
//Solve for the high word (Do FPU double divide to bring down high word into the low word):
|
||||
var mulTop = Math.floor(((rs >>> 0) * (rd >>> 0)) / 0x100000000) | 0;
|
||||
var dirty = (this.calculateMUL32(rs | 0, rd | 0) >>> 0) + (mlaLow >>> 0);
|
||||
this.mul64ResultHigh = ((mulTop | 0) + (mlaHigh | 0) + Math.floor(dirty / 0x100000000)) | 0;
|
||||
this.mul64ResultLow = dirty | 0;
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.write32 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
//Updating the address bus away from PC fetch:
|
||||
this.IOCore.wait.NonSequentialBroadcast();
|
||||
this.memory.memoryWrite32(address | 0, data | 0);
|
||||
//Updating the address bus back to PC fetch:
|
||||
this.IOCore.wait.NonSequentialBroadcast();
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.write16 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
//Updating the address bus away from PC fetch:
|
||||
this.IOCore.wait.NonSequentialBroadcast();
|
||||
this.memory.memoryWrite16(address | 0, data | 0);
|
||||
//Updating the address bus back to PC fetch:
|
||||
this.IOCore.wait.NonSequentialBroadcast();
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.write8 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
//Updating the address bus away from PC fetch:
|
||||
this.IOCore.wait.NonSequentialBroadcast();
|
||||
this.memory.memoryWrite8(address | 0, data | 0);
|
||||
//Updating the address bus back to PC fetch:
|
||||
this.IOCore.wait.NonSequentialBroadcast();
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.read32 = function (address) {
|
||||
address = address | 0;
|
||||
//Updating the address bus away from PC fetch:
|
||||
this.IOCore.wait.NonSequentialBroadcast();
|
||||
var data = this.memory.memoryRead32(address | 0) | 0;
|
||||
//Unaligned access gets data rotated right:
|
||||
if ((address & 0x3) != 0) {
|
||||
//Rotate word right:
|
||||
data = (data << ((4 - (address & 0x3)) << 3)) | (data >>> ((address & 0x3) << 3));
|
||||
}
|
||||
//Updating the address bus back to PC fetch:
|
||||
this.IOCore.wait.NonSequentialBroadcast();
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.read16 = function (address) {
|
||||
address = address | 0;
|
||||
//Updating the address bus away from PC fetch:
|
||||
this.IOCore.wait.NonSequentialBroadcast();
|
||||
var data = this.memory.memoryRead16(address | 0) | 0;
|
||||
//Unaligned access gets data rotated right:
|
||||
if ((address & 0x1) != 0) {
|
||||
//Rotate word right:
|
||||
data = (data << 24) | (data >>> 8);
|
||||
}
|
||||
//Updating the address bus back to PC fetch:
|
||||
this.IOCore.wait.NonSequentialBroadcast();
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceCPU.prototype.read8 = function (address) {
|
||||
address = address | 0;
|
||||
//Updating the address bus away from PC fetch:
|
||||
this.IOCore.wait.NonSequentialBroadcast();
|
||||
var data = this.memory.memoryRead8(address | 0) | 0;
|
||||
//Updating the address bus back to PC fetch:
|
||||
this.IOCore.wait.NonSequentialBroadcast();
|
||||
return data | 0;
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2014 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function ARMCPSRAttributeTable() {
|
||||
//"use asm";
|
||||
var negative = 0;
|
||||
var zero = 1;
|
||||
var carry = 0;
|
||||
var overflow = 0;
|
||||
function setNegative(toSet) {
|
||||
toSet = toSet | 0;
|
||||
negative = toSet | 0;
|
||||
};
|
||||
function setNegativeFalse() {
|
||||
negative = 0;
|
||||
};
|
||||
function getNegative() {
|
||||
return negative | 0;
|
||||
};
|
||||
function setZero(toSet) {
|
||||
toSet = toSet | 0;
|
||||
zero = toSet | 0;
|
||||
};
|
||||
function setZeroTrue() {
|
||||
zero = 0;
|
||||
};
|
||||
function setZeroFalse() {
|
||||
zero = 1;
|
||||
};
|
||||
function getZero() {
|
||||
return zero | 0;
|
||||
};
|
||||
function setOverflowTrue() {
|
||||
overflow = -1;
|
||||
};
|
||||
function setOverflowFalse() {
|
||||
overflow = 0;
|
||||
};
|
||||
function getOverflow() {
|
||||
return overflow | 0;
|
||||
};
|
||||
function setCarry(toSet) {
|
||||
toSet = toSet | 0;
|
||||
carry = toSet | 0;
|
||||
};
|
||||
function setCarryFalse() {
|
||||
carry = 0;
|
||||
};
|
||||
function getCarry() {
|
||||
return carry | 0;
|
||||
};
|
||||
function getCarryReverse() {
|
||||
return (~carry) | 0;
|
||||
};
|
||||
function checkConditionalCode(execute) {
|
||||
execute = execute | 0;
|
||||
/*
|
||||
Instruction Decode Pattern:
|
||||
C = Conditional Code Bit;
|
||||
X = Possible opcode bit;
|
||||
N = Data Bit, definitely not an opcode bit
|
||||
OPCODE: CCCCXXXXXXXXXXXXNNNNNNNNXXXXNNNN
|
||||
|
||||
For this function, we decode the top 3 bits for the conditional code test:
|
||||
*/
|
||||
switch ((execute >>> 29) | 0) {
|
||||
case 0x4:
|
||||
if ((zero | 0) == 0) {
|
||||
execute = -1;
|
||||
break;
|
||||
}
|
||||
case 0x1:
|
||||
execute = ~carry;
|
||||
break;
|
||||
case 0x2:
|
||||
execute = ~negative;
|
||||
break;
|
||||
case 0x3:
|
||||
execute = ~overflow;
|
||||
break;
|
||||
case 0x6:
|
||||
if ((zero | 0) == 0) {
|
||||
execute = -1;
|
||||
break;
|
||||
}
|
||||
case 0x5:
|
||||
execute = negative ^ overflow;
|
||||
break;
|
||||
case 0x0:
|
||||
if ((zero | 0) != 0) {
|
||||
execute = -1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
execute = 0;
|
||||
}
|
||||
return execute | 0;
|
||||
};
|
||||
function setNZInt(toSet) {
|
||||
toSet = toSet | 0;
|
||||
negative = toSet | 0;
|
||||
zero = toSet | 0;
|
||||
};
|
||||
function setNZCV(toSet) {
|
||||
toSet = toSet | 0;
|
||||
negative = toSet | 0;
|
||||
zero = (~toSet) & 0x40000000;
|
||||
carry = toSet << 2;
|
||||
overflow = toSet << 3;
|
||||
};
|
||||
function getNZCV() {
|
||||
var toSet = 0;
|
||||
toSet = negative & 0x80000000;
|
||||
if ((zero | 0) == 0) {
|
||||
toSet = toSet | 0x40000000;
|
||||
}
|
||||
toSet = toSet | ((carry >>> 31) << 29);
|
||||
toSet = toSet | ((overflow >>> 31) << 28);
|
||||
return toSet | 0;
|
||||
};
|
||||
function setADDFlags(operand1, operand2) {
|
||||
//Update flags for an addition operation:
|
||||
operand1 = operand1 | 0;
|
||||
operand2 = operand2 | 0;
|
||||
negative = ((operand1 | 0) + (operand2 | 0)) | 0;
|
||||
zero = negative | 0;
|
||||
if ((negative >>> 0) < (operand1 >>> 0)) {
|
||||
carry = -1;
|
||||
}
|
||||
else {
|
||||
carry = 0;
|
||||
}
|
||||
overflow = (~(operand1 ^ operand2)) & (operand1 ^ negative);
|
||||
return negative | 0;
|
||||
};
|
||||
function setADCFlags(operand1, operand2) {
|
||||
//Update flags for an addition operation:
|
||||
operand1 = operand1 | 0;
|
||||
operand2 = operand2 | 0;
|
||||
negative = ((operand1 | 0) + (operand2 | 0)) | 0;
|
||||
negative = ((negative | 0) + (carry >>> 31)) | 0;
|
||||
zero = negative | 0;
|
||||
if ((negative >>> 0) < (operand1 >>> 0)) {
|
||||
carry = -1;
|
||||
}
|
||||
else if ((negative >>> 0) > (operand1 >>> 0)) {
|
||||
carry = 0;
|
||||
}
|
||||
overflow = (~(operand1 ^ operand2)) & (operand1 ^ negative);
|
||||
return negative | 0;
|
||||
};
|
||||
function setSUBFlags(operand1, operand2) {
|
||||
//Update flags for a subtraction operation:
|
||||
operand1 = operand1 | 0;
|
||||
operand2 = operand2 | 0;
|
||||
negative = ((operand1 | 0) - (operand2 | 0)) | 0;
|
||||
zero = negative | 0;
|
||||
if ((operand1 >>> 0) >= (operand2 >>> 0)) {
|
||||
carry = -1;
|
||||
}
|
||||
else {
|
||||
carry = 0;
|
||||
}
|
||||
overflow = (operand1 ^ operand2) & (operand1 ^ negative);
|
||||
return negative | 0;
|
||||
};
|
||||
function setSBCFlags(operand1, operand2) {
|
||||
//Update flags for a subtraction operation:
|
||||
operand1 = operand1 | 0;
|
||||
operand2 = operand2 | 0;
|
||||
negative = ((operand1 | 0) - (operand2 | 0)) | 0;
|
||||
negative = ((negative | 0) - ((~carry) >>> 31)) | 0
|
||||
zero = negative | 0;
|
||||
if ((negative >>> 0) < (operand1 >>> 0)) {
|
||||
carry = -1;
|
||||
}
|
||||
else if ((negative >>> 0) > (operand1 >>> 0)) {
|
||||
carry = 0;
|
||||
}
|
||||
overflow = (operand1 ^ operand2) & (operand1 ^ negative);
|
||||
return negative | 0;
|
||||
};
|
||||
function setCMPFlags(operand1, operand2) {
|
||||
//Update flags for a subtraction operation:
|
||||
operand1 = operand1 | 0;
|
||||
operand2 = operand2 | 0;
|
||||
negative = ((operand1 | 0) - (operand2 | 0)) | 0;
|
||||
zero = negative | 0;
|
||||
if ((operand1 >>> 0) >= (operand2 >>> 0)) {
|
||||
carry = -1;
|
||||
}
|
||||
else {
|
||||
carry = 0;
|
||||
}
|
||||
overflow = (operand1 ^ operand2) & (operand1 ^ negative);
|
||||
};
|
||||
function setCMNFlags(operand1, operand2) {
|
||||
//Update flags for an addition operation:
|
||||
operand1 = operand1 | 0;
|
||||
operand2 = operand2 | 0;
|
||||
negative = ((operand1 | 0) + (operand2 | 0)) | 0;
|
||||
zero = negative | 0;
|
||||
if ((negative >>> 0) < (operand1 >>> 0)) {
|
||||
carry = -1;
|
||||
}
|
||||
else {
|
||||
carry = 0;
|
||||
}
|
||||
overflow = (~(operand1 ^ operand2)) & (operand1 ^ negative);
|
||||
};
|
||||
function BGE() {
|
||||
//Branch if Negative equal to Overflow
|
||||
return (negative ^ overflow) | 0;
|
||||
};
|
||||
return {
|
||||
setNegative:setNegative,
|
||||
setNegativeFalse:setNegativeFalse,
|
||||
getNegative:getNegative,
|
||||
setZero:setZero,
|
||||
setZeroTrue:setZeroTrue,
|
||||
setZeroFalse:setZeroFalse,
|
||||
getZero:getZero,
|
||||
setOverflowTrue:setOverflowTrue,
|
||||
setOverflowFalse:setOverflowFalse,
|
||||
getOverflow:getOverflow,
|
||||
setCarry:setCarry,
|
||||
setCarryFalse:setCarryFalse,
|
||||
getCarry:getCarry,
|
||||
getCarryReverse:getCarryReverse,
|
||||
checkConditionalCode:checkConditionalCode,
|
||||
setNZInt:setNZInt,
|
||||
setNZCV:setNZCV,
|
||||
getNZCV:getNZCV,
|
||||
setADDFlags:setADDFlags,
|
||||
setADCFlags:setADCFlags,
|
||||
setSUBFlags:setSUBFlags,
|
||||
setSBCFlags:setSBCFlags,
|
||||
setCMPFlags:setCMPFlags,
|
||||
setCMNFlags:setCMNFlags,
|
||||
BGE:BGE
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2014 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceCartridge(IOCore) {
|
||||
this.IOCore = IOCore;
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.initialize = function () {
|
||||
this.flash_is128 = false;
|
||||
this.flash_isAtmel = false;
|
||||
this.ROM = this.getROMArray(this.IOCore.ROM);
|
||||
this.ROM16 = getUint16View(this.ROM);
|
||||
this.ROM32 = getInt32View(this.ROM);
|
||||
this.decodeName();
|
||||
this.decodeFlashType();
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.getROMArray = function (old_array) {
|
||||
this.ROMLength = Math.min((old_array.length >> 2) << 2, 0x2000000);
|
||||
this.EEPROMStart = ((this.ROMLength | 0) > 0x1000000) ? Math.max(this.ROMLength | 0, 0x1FFFF00) : 0x1000000;
|
||||
var newArray = getUint8Array(this.ROMLength | 0);
|
||||
for (var index = 0; (index | 0) < (this.ROMLength | 0); index = ((index | 0) + 1) | 0) {
|
||||
newArray[index | 0] = old_array[index | 0] | 0;
|
||||
}
|
||||
return newArray;
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.decodeName = function () {
|
||||
this.name = "GUID_";
|
||||
if ((this.ROMLength | 0) >= 0xC0) {
|
||||
for (var address = 0xAC; (address | 0) < 0xB3; address = ((address | 0) + 1) | 0) {
|
||||
if ((this.ROM[address | 0] | 0) > 0) {
|
||||
this.name += String.fromCharCode(this.ROM[address | 0] | 0);
|
||||
}
|
||||
else {
|
||||
this.name += "_";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.decodeFlashType = function () {
|
||||
this.flash_is128 = false;
|
||||
this.flash_isAtmel = false;
|
||||
var flash_types = 0;
|
||||
var F = ("F").charCodeAt(0) & 0xFF;
|
||||
var L = ("L").charCodeAt(0) & 0xFF;
|
||||
var A = ("A").charCodeAt(0) & 0xFF;
|
||||
var S = ("S").charCodeAt(0) & 0xFF;
|
||||
var H = ("H").charCodeAt(0) & 0xFF;
|
||||
var underScore = ("_").charCodeAt(0) & 0xFF;
|
||||
var five = ("5").charCodeAt(0) & 0xFF;
|
||||
var one = ("1").charCodeAt(0) & 0xFF;
|
||||
var two = ("2").charCodeAt(0) & 0xFF;
|
||||
var M = ("M").charCodeAt(0) & 0xFF;
|
||||
var V = ("V").charCodeAt(0) & 0xFF;
|
||||
var length = ((this.ROM.length | 0) - 12) | 0;
|
||||
for (var index = 0; (index | 0) < (length | 0); index = ((index | 0) + 4) | 0) {
|
||||
if ((this.ROM[index | 0] | 0) == (F | 0)) {
|
||||
if ((this.ROM[index | 1] | 0) == (L | 0)) {
|
||||
if ((this.ROM[index | 2] | 0) == (A | 0)) {
|
||||
if ((this.ROM[index | 3] | 0) == (S | 0)) {
|
||||
var tempIndex = ((index | 0) + 4) | 0;
|
||||
if ((this.ROM[tempIndex | 0] | 0) == (H | 0)) {
|
||||
if ((this.ROM[tempIndex | 1] | 0) == (underScore | 0)) {
|
||||
if ((this.ROM[tempIndex | 2] | 0) == (V | 0)) {
|
||||
flash_types |= 1;
|
||||
}
|
||||
}
|
||||
else if ((this.ROM[tempIndex | 1] | 0) == (five | 0)) {
|
||||
if ((this.ROM[tempIndex | 2] | 0) == (one | 0)) {
|
||||
if ((this.ROM[tempIndex | 3] | 0) == (two | 0)) {
|
||||
tempIndex = ((tempIndex | 0) + 4) | 0;
|
||||
if ((this.ROM[tempIndex | 0] | 0) == (underScore | 0)) {
|
||||
if ((this.ROM[tempIndex | 1] | 0) == (V | 0)) {
|
||||
flash_types |= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((this.ROM[tempIndex | 1] | 0) == (one | 0)) {
|
||||
if ((this.ROM[tempIndex | 2] | 0) == (M | 0)) {
|
||||
if ((this.ROM[tempIndex | 3] | 0) == (underScore | 0)) {
|
||||
tempIndex = ((tempIndex | 0) + 4) | 0;
|
||||
if ((this.ROM[tempIndex | 0] | 0) == (V | 0)) {
|
||||
flash_types |= 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.flash_is128 = ((flash_types | 0) >= 4);
|
||||
this.flash_isAtmel = ((flash_types | 0) <= 1);
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.readROMOnly8 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((address | 0) < (this.ROMLength | 0)) {
|
||||
data = this.ROM[address & 0x1FFFFFF] | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
if (__LITTLE_ENDIAN__) {
|
||||
GameBoyAdvanceCartridge.prototype.readROMOnly16 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((address | 0) < (this.ROMLength | 0)) {
|
||||
data = this.ROM16[(address >> 1) & 0xFFFFFF] | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.readROMOnly32 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((address | 0) < (this.ROMLength | 0)) {
|
||||
data = this.ROM32[(address >> 2) & 0x7FFFFF] | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
GameBoyAdvanceCartridge.prototype.readROMOnly16 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((address | 0) < (this.ROMLength | 0)) {
|
||||
data = this.ROM[address] | (this.ROM[address | 1] << 8);
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.readROMOnly32 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((address | 0) < (this.ROMLength | 0)) {
|
||||
data = this.ROM[address] | (this.ROM[address | 1] << 8) | (this.ROM[address | 2] << 16) | (this.ROM[address | 3] << 24);
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.readROM8 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((address | 0) > 0xC9) {
|
||||
//Definitely ROM:
|
||||
data = this.readROMOnly8(address | 0) | 0;
|
||||
}
|
||||
else {
|
||||
//Possibly GPIO:
|
||||
data = this.IOCore.saves.readGPIO8(address | 0) | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.readROM16 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((address | 0) > 0xC9) {
|
||||
//Definitely ROM:
|
||||
data = this.readROMOnly16(address | 0) | 0;
|
||||
}
|
||||
else {
|
||||
//Possibly GPIO:
|
||||
data = this.IOCore.saves.readGPIO16(address | 0) | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.readROM32 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((address | 0) > 0xC9) {
|
||||
//Definitely ROM:
|
||||
data = this.readROMOnly32(address | 0) | 0;
|
||||
}
|
||||
else {
|
||||
//Possibly GPIO:
|
||||
data = this.IOCore.saves.readGPIO32(address | 0) | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.readROM8Space2 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((address | 0) >= 0xC4 && (address | 0) < 0xCA) {
|
||||
//Possibly GPIO:
|
||||
data = this.IOCore.saves.readGPIO8(address | 0) | 0;
|
||||
}
|
||||
else if ((address | 0) >= (this.EEPROMStart | 0)) {
|
||||
//Possibly EEPROM:
|
||||
data = this.IOCore.saves.readEEPROM8(address | 0) | 0;
|
||||
}
|
||||
else {
|
||||
//Definitely ROM:
|
||||
data = this.readROMOnly8(address | 0) | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.readROM16Space2 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((address | 0) >= 0xC4 && (address | 0) < 0xCA) {
|
||||
//Possibly GPIO:
|
||||
data = this.IOCore.saves.readGPIO16(address | 0) | 0;
|
||||
}
|
||||
else if ((address | 0) >= (this.EEPROMStart | 0)) {
|
||||
//Possibly EEPROM:
|
||||
data = this.IOCore.saves.readEEPROM16(address | 0) | 0;
|
||||
}
|
||||
else {
|
||||
//Definitely ROM:
|
||||
data = this.readROMOnly16(address | 0) | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.readROM32Space2 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((address | 0) >= 0xC4 && (address | 0) < 0xCA) {
|
||||
//Possibly GPIO:
|
||||
data = this.IOCore.saves.readGPIO32(address | 0) | 0;
|
||||
}
|
||||
else if ((address | 0) >= (this.EEPROMStart | 0)) {
|
||||
//Possibly EEPROM:
|
||||
data = this.IOCore.saves.readEEPROM32(address | 0) | 0;
|
||||
}
|
||||
else {
|
||||
//Definitely ROM:
|
||||
data = this.readROMOnly32(address | 0) | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.writeROM8 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
if ((address | 0) >= 0xC4 && (address | 0) < 0xCA) {
|
||||
//GPIO Chip (RTC):
|
||||
this.IOCore.saves.writeGPIO8(address | 0, data | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.writeROM16 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
if ((address | 0) >= 0xC4 && (address | 0) < 0xCA) {
|
||||
//GPIO Chip (RTC):
|
||||
this.IOCore.saves.writeGPIO16(address | 0, data | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.writeROM16DMA = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
if ((address | 0) >= 0xC4 && (address | 0) < 0xCA) {
|
||||
//GPIO Chip (RTC):
|
||||
this.IOCore.saves.writeGPIO16(address | 0, data | 0);
|
||||
}
|
||||
else if ((address | 0) >= (this.EEPROMStart | 0)) {
|
||||
//Possibly EEPROM:
|
||||
this.IOCore.saves.writeEEPROM16(address | 0, data | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.writeROM32 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
if ((address | 0) >= 0xC4 && (address | 0) < 0xCA) {
|
||||
//GPIO Chip (RTC):
|
||||
this.IOCore.saves.writeGPIO32(address | 0, data | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceCartridge.prototype.nextIRQEventTime = function () {
|
||||
//Nothing yet implement that would fire an IRQ:
|
||||
return 0x7FFFFFFF;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2015 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceDMA(IOCore) {
|
||||
this.IOCore = IOCore;
|
||||
}
|
||||
GameBoyAdvanceDMA.prototype.initialize = function () {
|
||||
this.dmaChannel0 = this.IOCore.dmaChannel0;
|
||||
this.dmaChannel1 = this.IOCore.dmaChannel1;
|
||||
this.dmaChannel2 = this.IOCore.dmaChannel2;
|
||||
this.dmaChannel3 = this.IOCore.dmaChannel3;
|
||||
this.currentMatch = -1;
|
||||
this.fetch = 0;
|
||||
}
|
||||
GameBoyAdvanceDMA.prototype.getCurrentFetchValue = function () {
|
||||
return this.fetch | 0;
|
||||
}
|
||||
GameBoyAdvanceDMA.prototype.gfxHBlankRequest = function () {
|
||||
//Pass H-Blank signal to all DMA channels:
|
||||
this.requestDMA(0x4);
|
||||
}
|
||||
GameBoyAdvanceDMA.prototype.gfxVBlankRequest = function () {
|
||||
//Pass V-Blank signal to all DMA channels:
|
||||
this.requestDMA(0x2);
|
||||
}
|
||||
GameBoyAdvanceDMA.prototype.requestDMA = function (DMAType) {
|
||||
DMAType = DMAType | 0;
|
||||
this.dmaChannel0.requestDMA(DMAType | 0);
|
||||
this.dmaChannel1.requestDMA(DMAType | 0);
|
||||
this.dmaChannel2.requestDMA(DMAType | 0);
|
||||
this.dmaChannel3.requestDMA(DMAType | 0);
|
||||
}
|
||||
GameBoyAdvanceDMA.prototype.findLowestDMA = function () {
|
||||
if ((this.dmaChannel0.getMatchStatus() | 0) != 0) {
|
||||
return 0;
|
||||
}
|
||||
if ((this.dmaChannel1.getMatchStatus() | 0) != 0) {
|
||||
return 1;
|
||||
}
|
||||
if ((this.dmaChannel2.getMatchStatus() | 0) != 0) {
|
||||
return 2;
|
||||
}
|
||||
if ((this.dmaChannel3.getMatchStatus() | 0) != 0) {
|
||||
return 3;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
GameBoyAdvanceDMA.prototype.update = function () {
|
||||
var lowestDMAFound = this.findLowestDMA();
|
||||
if ((lowestDMAFound | 0) < 4) {
|
||||
//Found an active DMA:
|
||||
if ((this.currentMatch | 0) == -1) {
|
||||
this.IOCore.flagDMA();
|
||||
}
|
||||
if ((this.currentMatch | 0) != (lowestDMAFound | 0)) {
|
||||
//Re-broadcasting on address bus, so non-seq:
|
||||
this.IOCore.wait.NonSequentialBroadcast();
|
||||
this.currentMatch = lowestDMAFound | 0;
|
||||
}
|
||||
}
|
||||
else if ((this.currentMatch | 0) != -1) {
|
||||
//No active DMA found:
|
||||
this.currentMatch = -1;
|
||||
this.IOCore.deflagDMA();
|
||||
this.IOCore.updateCoreSpill();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceDMA.prototype.perform = function () {
|
||||
//Call the correct channel to process:
|
||||
switch (this.currentMatch | 0) {
|
||||
case 0:
|
||||
this.dmaChannel0.handleDMACopy();
|
||||
break;
|
||||
case 1:
|
||||
this.dmaChannel1.handleDMACopy();
|
||||
break;
|
||||
case 2:
|
||||
this.dmaChannel2.handleDMACopy();
|
||||
break;
|
||||
default:
|
||||
this.dmaChannel3.handleDMACopy();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceDMA.prototype.updateFetch = function (data) {
|
||||
data = data | 0;
|
||||
this.fetch = data | 0;
|
||||
}
|
||||
GameBoyAdvanceDMA.prototype.nextEventTime = function () {
|
||||
var clocks = Math.min(this.dmaChannel0.nextEventTime() | 0, this.dmaChannel1.nextEventTime() | 0, this.dmaChannel2.nextEventTime() | 0, this.dmaChannel3.nextEventTime() | 0) | 0;
|
||||
return clocks | 0;
|
||||
}
|
||||
@@ -0,0 +1,469 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2019 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceEmulator() {
|
||||
this.settings = {
|
||||
SKIPBoot:false, //Skip the BIOS boot screen.
|
||||
audioBufferUnderrunLimit:100, //Audio buffer minimum span amount over x milliseconds.
|
||||
audioBufferDynamicLimit:32, //Audio buffer dynamic minimum span amount over x milliseconds.
|
||||
audioBufferSize:300, //Audio buffer maximum span amount over x milliseconds.
|
||||
emulatorSpeed:1.0, //Speed multiplier of the emulator.
|
||||
metricCollectionMinimum:500, //How many milliseconds of cycling to count before determining speed.
|
||||
dynamicSpeed:false, //Whether to actively change the target speed for best user experience.
|
||||
overclockBlockLimit:200, //Whether to throttle clocks in audio adjustment.
|
||||
offthreadGfxEnabled:true //Whether to allow offthread graphics rendering if support is present.
|
||||
};
|
||||
this.audioFound = 0; //Do we have audio output sink found yet?
|
||||
this.emulatorStatus = 0x10; //{paused, saves loaded, fault found, loaded}
|
||||
this.BIOS = []; //Initialize BIOS as not existing.
|
||||
this.ROM = []; //Initialize BIOS as not existing.
|
||||
this.audioUpdateState = 1; //Do we need to update the sound core with new info?
|
||||
this.saveExportHandler = null; //Save export handler attached by GUI.
|
||||
this.saveImportHandler = null; //Save import handler attached by GUI.
|
||||
this.speedCallback = null; //Speed report handler attached by GUI.
|
||||
this.playStatusCallback = null; //Play status change handler attached by GUI.
|
||||
this.startCallbacks = []; //Some jobs to run at iteration head.
|
||||
this.endCallbacks = []; //Some jobs to run at iteration end.
|
||||
this.terminationCallbacks = []; //Some jobs to run if the emulation core is killed.
|
||||
this.timerIntervalRate = 16; //How often the emulator core is called into (in milliseconds).
|
||||
this.lastTimestamp = 0; //Track the last time given in milliseconds.
|
||||
this.dynamicSpeedRefresh = false; //Whether speed is allowed to be changed dynamically in the current cycle.
|
||||
this.calculateTimings(); //Calculate some multipliers against the core emulator timer.
|
||||
this.generateCoreExposed(); //Generate a limit API for the core to call this shell object.
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.generateCoreExposed = function () {
|
||||
var parentObj = this;
|
||||
this.coreExposed = {
|
||||
outputAudio:function (l, r) {
|
||||
parentObj.outputAudio(l, r);
|
||||
},
|
||||
graphicsHandle:null,
|
||||
appendStartIterationSync:function (callback) {
|
||||
parentObj.startCallbacks.push(callback);
|
||||
},
|
||||
appendEndIterationSync:function (callback) {
|
||||
parentObj.endCallbacks.push(callback);
|
||||
},
|
||||
appendTerminationSync:function (callback) {
|
||||
parentObj.terminationCallbacks.push(callback);
|
||||
},
|
||||
offthreadGfxEnabled:function () {
|
||||
return !!parentObj.settings.offthreadGfxEnabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.play = function () {
|
||||
if ((this.emulatorStatus | 0) >= 0x10) {
|
||||
this.emulatorStatus = this.emulatorStatus & 0xF;
|
||||
if ((this.emulatorStatus & 0x1) == 0 && this.BIOS && this.ROM) {
|
||||
if ((this.initializeCore() | 0) == 0) {
|
||||
//Failure to initialize:
|
||||
this.pause();
|
||||
return;
|
||||
}
|
||||
this.importSave();
|
||||
}
|
||||
this.invalidateMetrics();
|
||||
this.setBufferSpace();
|
||||
//Report new status back:
|
||||
this.playStatusCallback(1);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.pause = function () {
|
||||
if ((this.emulatorStatus | 0) < 0x10) {
|
||||
this.exportSave();
|
||||
this.emulatorStatus = this.emulatorStatus | 0x10;
|
||||
//Report new status back:
|
||||
this.playStatusCallback(0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.stop = function () {
|
||||
this.emulatorStatus = this.emulatorStatus & 0x1C;
|
||||
this.audioUpdateState = 1;
|
||||
this.pause();
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.restart = function () {
|
||||
if ((this.emulatorStatus & 0x1) == 0x1) {
|
||||
this.emulatorStatus = this.emulatorStatus & 0x1D;
|
||||
this.exportSave();
|
||||
if ((this.initializeCore() | 0) == 0) {
|
||||
//Failure to initialize:
|
||||
this.pause();
|
||||
return;
|
||||
}
|
||||
this.importSave();
|
||||
this.audioUpdateState = 1;
|
||||
this.processNewSpeed(1);
|
||||
this.setBufferSpace();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.timerCallback = function (lastTimestamp) {
|
||||
//Callback passes us a reference timestamp:
|
||||
this.lastTimestamp = lastTimestamp >>> 0;
|
||||
switch (this.emulatorStatus | 0) {
|
||||
//Core initialized and saves loaded:
|
||||
case 5:
|
||||
this.iterationStartSequence(); //Run start of iteration stuff.
|
||||
this.IOCore.enter(this.CPUCyclesTotal | 0); //Step through the emulation core loop.
|
||||
this.iterationEndSequence(); //Run end of iteration stuff.
|
||||
break;
|
||||
//Core initialized, but saves still loading:
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
//Some pending error is preventing execution, so pause:
|
||||
this.pause();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.iterationStartSequence = function () {
|
||||
this.calculateSpeedPercentage(); //Calculate the emulator realtime run speed heuristics.
|
||||
this.emulatorStatus = this.emulatorStatus | 0x2; //If the end routine doesn't unset this, then we are marked as having crashed.
|
||||
this.audioUnderrunAdjustment(); //If audio is enabled, look to see how much we should overclock by to maintain the audio buffer.
|
||||
this.audioPushNewState(); //Check to see if we need to update the audio core for any output changes.
|
||||
this.runStartJobs(); //Run various callbacks assigned from internal components.
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.iterationEndSequence = function () {
|
||||
this.emulatorStatus = this.emulatorStatus & 0x1D; //If core did not throw while running, unset the fatal error flag.
|
||||
this.clockCyclesSinceStart = ((this.clockCyclesSinceStart | 0) + (this.CPUCyclesTotal | 0)) | 0; //Accumulate tracking.
|
||||
this.submitAudioBuffer(); //Flush audio buffer to output.
|
||||
this.runEndJobs(); //Run various callbacks assigned from internal components.
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.runStartJobs = function () {
|
||||
var length = this.startCallbacks.length | 0;
|
||||
//Loop through all jobs:
|
||||
for (var index = 0; (index | 0) < (length | 0); index = ((index | 0) + 1) | 0) {
|
||||
//Run job:
|
||||
this.startCallbacks[index | 0]();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.runEndJobs = function () {
|
||||
var length = this.endCallbacks.length | 0;
|
||||
//Loop through all jobs:
|
||||
for (var index = 0; (index | 0) < (length | 0); index = ((index | 0) + 1) | 0) {
|
||||
//Run job:
|
||||
this.endCallbacks[index | 0]();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.runTerminationJobs = function () {
|
||||
var length = this.terminationCallbacks.length | 0;
|
||||
//Loop through all jobs:
|
||||
for (var index = 0; (index | 0) < (length | 0); index = ((index | 0) + 1) | 0) {
|
||||
//Run job:
|
||||
this.terminationCallbacks[index | 0]();
|
||||
}
|
||||
//Remove old jobs:
|
||||
this.startCallbacks = [];
|
||||
this.endCallbacks = [];
|
||||
this.terminationCallbacks = [];
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.attachROM = function (ROM) {
|
||||
this.stop();
|
||||
this.ROM = ROM;
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.attachBIOS = function (BIOS) {
|
||||
this.stop();
|
||||
this.BIOS = BIOS;
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.getGameName = function () {
|
||||
if ((this.emulatorStatus & 0x3) == 0x1) {
|
||||
return this.IOCore.cartridge.name;
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.attachSaveExportHandler = function (handler) {
|
||||
if (typeof handler == "function") {
|
||||
this.saveExportHandler = handler;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.attachSaveImportHandler = function (handler) {
|
||||
if (typeof handler == "function") {
|
||||
this.saveImportHandler = handler;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.attachSpeedHandler = function (handler) {
|
||||
if (typeof handler == "function") {
|
||||
this.speedCallback = handler;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.attachPlayStatusHandler = function (handler) {
|
||||
if (typeof handler == "function") {
|
||||
this.playStatusCallback = handler;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.importSave = function () {
|
||||
if (this.saveImportHandler) {
|
||||
var name = this.getGameName();
|
||||
if (name != "") {
|
||||
var parentObj = this;
|
||||
this.emulatorStatus = this.emulatorStatus & 0x1B;
|
||||
this.saveImportHandler(name, function (save) {
|
||||
parentObj.emulatorStatus = parentObj.emulatorStatus & 0x1B;
|
||||
parentObj.saveImportHandler("TYPE_" + name, function (saveType) {
|
||||
if (save && saveType && (parentObj.emulatorStatus & 0x3) == 0x1) {
|
||||
var length = save.length | 0;
|
||||
var convertedSave = getUint8Array(length | 0);
|
||||
if ((length | 0) > 0) {
|
||||
for (var index = 0; (index | 0) < (length | 0); index = ((index | 0) + 1) | 0) {
|
||||
convertedSave[index | 0] = save[index | 0] & 0xFF;
|
||||
}
|
||||
//We used to save this code wrong, fix the error in old saves:
|
||||
if ((saveType.length | 0) != 1) {
|
||||
//0 is fallthrough "UNKNOWN" aka autodetect type:
|
||||
parentObj.IOCore.saves.importSave(convertedSave, 0);
|
||||
}
|
||||
else {
|
||||
parentObj.IOCore.saves.importSave(convertedSave, saveType[0] & 0xFF);
|
||||
}
|
||||
parentObj.emulatorStatus = parentObj.emulatorStatus | 0x4;
|
||||
}
|
||||
}
|
||||
}, function (){parentObj.emulatorStatus = parentObj.emulatorStatus | 0x4;});
|
||||
}, function (){parentObj.emulatorStatus = parentObj.emulatorStatus | 0x4;});
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.emulatorStatus = this.emulatorStatus | 0x4;
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.exportSave = function () {
|
||||
if (this.saveExportHandler && (this.emulatorStatus & 0x3) == 0x1) {
|
||||
var save = this.IOCore.saves.exportSave();
|
||||
var saveType = this.IOCore.saves.exportSaveType() | 0;
|
||||
if (save != null) {
|
||||
this.saveExportHandler(this.IOCore.cartridge.name, save);
|
||||
this.saveExportHandler("TYPE_" + this.IOCore.cartridge.name, [saveType | 0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.setSpeed = function (speed) {
|
||||
speed = +speed;
|
||||
//Dynamic Speed overrides custom speed levels:
|
||||
if (!this.settings.dynamicSpeed) {
|
||||
this.processNewSpeed(+speed);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.processNewSpeed = function (speed) {
|
||||
speed = +speed;
|
||||
//0.003 for the integer resampler limitations, 0x3F for int math limitations:
|
||||
speed = +Math.min(Math.max(+speed, 0.003), 0x3F);
|
||||
if ((+speed) != (+this.settings.emulatorSpeed)) {
|
||||
this.settings.emulatorSpeed = +speed;
|
||||
this.calculateTimings();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.incrementSpeed = function (delta) {
|
||||
delta = +delta;
|
||||
this.setSpeed((+delta) + (+this.settings.emulatorSpeed));
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.getSpeed = function () {
|
||||
return +this.settings.emulatorSpeed;
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.invalidateMetrics = function () {
|
||||
this.clockCyclesSinceStart = 0;
|
||||
this.metricStart = 0;
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.resetMetrics = function () {
|
||||
this.clockCyclesSinceStart = 0;
|
||||
this.metricStart = this.lastTimestamp >>> 0;
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.calculateTimings = function () {
|
||||
this.clocksPerSecond = Math.min((+this.settings.emulatorSpeed) * 0x1000000, 0x3F000000) | 0;
|
||||
this.clocksPerMilliSecond = +((this.clocksPerSecond | 0) / 1000);
|
||||
this.CPUCyclesPerIteration = ((+this.clocksPerMilliSecond) * (+this.timerIntervalRate)) | 0;
|
||||
this.CPUCyclesTotal = this.CPUCyclesPerIteration | 0;
|
||||
this.initializeAudioLogic();
|
||||
this.reinitializeAudio();
|
||||
this.invalidateMetrics();
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.setIntervalRate = function (intervalRate) {
|
||||
intervalRate = +intervalRate;
|
||||
if ((+intervalRate) > 0 && (+intervalRate) < 1000) {
|
||||
if ((+intervalRate) != (+this.timerIntervalRate)) {
|
||||
this.timerIntervalRate = +intervalRate;
|
||||
this.calculateTimings();
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.calculateSpeedPercentage = function () {
|
||||
if ((this.metricStart >>> 0) != 0) {
|
||||
var timeDiff = Math.max(((this.lastTimestamp >>> 0) - (this.metricStart >>> 0)) | 0, 1) >>> 0;
|
||||
if ((timeDiff >>> 0) >= (this.settings.metricCollectionMinimum | 0)) {
|
||||
if (this.speedCallback) {
|
||||
var result = ((this.clockCyclesSinceStart | 0) * 100000) / ((timeDiff >>> 0) * 0x1000000);
|
||||
this.speedCallback(+result);
|
||||
}
|
||||
//Reset counter for speed check:
|
||||
this.resetMetrics();
|
||||
//Do a computation for dynamic speed this iteration:
|
||||
this.dynamicSpeedRefresh = true;
|
||||
}
|
||||
else {
|
||||
//Postpone any dynamic speed changes this iteration:
|
||||
this.dynamicSpeedRefresh = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Reset counter for speed check:
|
||||
this.resetMetrics();
|
||||
//Postpone any dynamic speed changes this iteration:
|
||||
this.dynamicSpeedRefresh = false;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.initializeCore = function () {
|
||||
//Wrap up any old internal instance callbacks:
|
||||
this.runTerminationJobs();
|
||||
//Setup a new instance of the i/o core:
|
||||
this.IOCore = new GameBoyAdvanceIO(this.settings.SKIPBoot, this.coreExposed, this.BIOS, this.ROM);
|
||||
//Call the initalization procedure and get status code:
|
||||
var allowInit = this.IOCore.initialize() | 0;
|
||||
//Append status code as play status flag for emulator runtime:
|
||||
this.emulatorStatus = this.emulatorStatus | allowInit;
|
||||
return allowInit | 0;
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.keyDown = function (keyPressed) {
|
||||
keyPressed = keyPressed | 0;
|
||||
if ((this.emulatorStatus | 0) < 0x10 && (keyPressed | 0) >= 0 && (keyPressed | 0) <= 9) {
|
||||
this.IOCore.joypad.keyPress(keyPressed | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.keyUp = function (keyReleased) {
|
||||
keyReleased = keyReleased | 0;
|
||||
if ((this.emulatorStatus | 0) < 0x10 && (keyReleased | 0) >= 0 && (keyReleased | 0) <= 9) {
|
||||
this.IOCore.joypad.keyRelease(keyReleased | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.attachGraphicsFrameHandler = function (handler) {
|
||||
if (typeof handler == "object") {
|
||||
this.coreExposed.graphicsHandle = handler;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.attachAudioHandler = function (mixerInputHandler) {
|
||||
if (mixerInputHandler) {
|
||||
this.audio = mixerInputHandler;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.enableAudio = function () {
|
||||
if ((this.audioFound | 0) == 0 && this.audio) {
|
||||
this.audioFound = 1; //Set audio to 'found' by default.
|
||||
//Attempt to enable audio:
|
||||
var parentObj = this;
|
||||
this.audio.initialize(2, (this.clocksPerSecond | 0) / (this.audioResamplerFirstPassFactor | 0), Math.max((+this.clocksPerMilliSecond) * (this.settings.audioBufferSize | 0) / (this.audioResamplerFirstPassFactor | 0), 4) | 0, function () {
|
||||
//Not needed
|
||||
}, function () {
|
||||
//We manually check at the start of each timer interval, so not needed here.
|
||||
}, function () {
|
||||
//Disable audio in the callback here:
|
||||
parentObj.disableAudio();
|
||||
});
|
||||
this.audio.register();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.disableAudio = function () {
|
||||
if ((this.audioFound | 0) != 0) {
|
||||
this.audio.unregister();
|
||||
this.audioFound = 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.reinitializeAudio = function () {
|
||||
if ((this.audioFound | 0) != 0) {
|
||||
this.disableAudio();
|
||||
this.enableAudio();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.initializeAudioLogic = function () {
|
||||
//Calculate the variables for the preliminary downsampler first:
|
||||
this.audioResamplerFirstPassFactor = Math.min(Math.floor((this.clocksPerSecond | 0) / 44100), Math.floor(0x7FFFFFFF / 0x3FF)) | 0;
|
||||
this.audioDownSampleInputDivider = +((2 / 0x3FF) / (this.audioResamplerFirstPassFactor | 0));
|
||||
this.initializeAudioBuffering();
|
||||
//Need to push the new resample factor:
|
||||
this.audioUpdateState = 1;
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.initializeAudioBuffering = function () {
|
||||
this.audioDestinationPosition = 0;
|
||||
this.audioBufferContainAmount = Math.max((+this.clocksPerMilliSecond) * (this.settings.audioBufferUnderrunLimit | 0) / (this.audioResamplerFirstPassFactor | 0), 3) << 1;
|
||||
this.audioBufferOverclockBlockAmount = Math.max((+this.clocksPerMilliSecond) * (this.settings.overclockBlockLimit | 0) / (this.audioResamplerFirstPassFactor | 0), 3) << 1;
|
||||
this.audioBufferDynamicContainAmount = Math.max((+this.clocksPerMilliSecond) * (this.settings.audioBufferDynamicLimit | 0) / (this.audioResamplerFirstPassFactor | 0), 2) << 1;
|
||||
//Underrun logic will request at most 32 milliseconds of runtime per iteration, so set buffer size to 64 ms:
|
||||
var audioNumSamplesTotal = Math.max(((+this.clocksPerMilliSecond) / (this.audioResamplerFirstPassFactor | 0)) << 6, 4) << 1;
|
||||
if (!this.audioBuffer || ((audioNumSamplesTotal | 0) > (this.audioBuffer.length | 0))) {
|
||||
//Only regen buffer if the size is increased:
|
||||
this.audioBuffer = getFloat32Array(audioNumSamplesTotal | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.outputAudio = function (downsampleInputLeft, downsampleInputRight) {
|
||||
downsampleInputLeft = downsampleInputLeft | 0;
|
||||
downsampleInputRight = downsampleInputRight | 0;
|
||||
this.audioBuffer[this.audioDestinationPosition | 0] = ((downsampleInputLeft | 0) * (+this.audioDownSampleInputDivider)) - 1;
|
||||
this.audioDestinationPosition = ((this.audioDestinationPosition | 0) + 1) | 0;
|
||||
this.audioBuffer[this.audioDestinationPosition | 0] = ((downsampleInputRight | 0) * (+this.audioDownSampleInputDivider)) - 1;
|
||||
this.audioDestinationPosition = ((this.audioDestinationPosition | 0) + 1) | 0;
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.submitAudioBuffer = function () {
|
||||
if ((this.audioFound | 0) != 0) {
|
||||
this.audio.push(this.audioBuffer, 0, this.audioDestinationPosition | 0);
|
||||
}
|
||||
this.audioDestinationPosition = 0;
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.audioUnderrunAdjustment = function () {
|
||||
this.CPUCyclesTotal = this.CPUCyclesPerIteration | 0;
|
||||
if ((this.audioFound | 0) != 0) {
|
||||
var remainingAmount = this.audio.remainingBuffer();
|
||||
if (typeof remainingAmount == "number") {
|
||||
remainingAmount = Math.max(remainingAmount | 0, 0) | 0;
|
||||
var underrunAmount = ((this.audioBufferContainAmount | 0) - (remainingAmount | 0)) | 0;
|
||||
if ((underrunAmount | 0) > 0) {
|
||||
if (this.dynamicSpeedRefresh && this.settings.dynamicSpeed) {
|
||||
if (((this.audioBufferDynamicContainAmount | 0) - (remainingAmount | 0)) > 0) {
|
||||
var speed = +this.getSpeed();
|
||||
speed = Math.max((+speed) - 0.1, 0.003);
|
||||
this.processNewSpeed(+speed);
|
||||
}
|
||||
}
|
||||
this.CPUCyclesTotal = Math.min(((this.CPUCyclesTotal | 0) + ((underrunAmount >> 1) * (this.audioResamplerFirstPassFactor | 0))) | 0, (+this.clocksPerMilliSecond) << 5) | 0;
|
||||
}
|
||||
else {
|
||||
if (this.dynamicSpeedRefresh && this.settings.dynamicSpeed) {
|
||||
var speed = +this.getSpeed();
|
||||
if ((+speed) < 1) {
|
||||
speed = +Math.min((+speed) + 0.01, 1);
|
||||
this.processNewSpeed(+speed);
|
||||
}
|
||||
}
|
||||
var overrunAmount = ((remainingAmount | 0) - (this.audioBufferOverclockBlockAmount | 0)) | 0;
|
||||
if ((overrunAmount | 0) > 0) {
|
||||
this.CPUCyclesTotal = Math.max(((this.CPUCyclesTotal | 0) - ((overrunAmount >> 1) * (this.audioResamplerFirstPassFactor | 0))) | 0, 0) | 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.audioPushNewState = function () {
|
||||
if ((this.audioUpdateState | 0) != 0) {
|
||||
this.IOCore.sound.initializeOutput(this.audioResamplerFirstPassFactor | 0);
|
||||
this.audioUpdateState = 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.setBufferSpace = function () {
|
||||
if ((this.audioFound | 0) != 0) {
|
||||
//Fill the audio system with zeros for buffer stabilization on start:
|
||||
this.audio.setBufferSpace(this.audioBufferContainAmount | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.toggleSkipBootROM = function (SKIPBoot) {
|
||||
this.settings.SKIPBoot = !!SKIPBoot;
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.toggleDynamicSpeed = function (dynamicSpeed) {
|
||||
this.settings.dynamicSpeed = !!dynamicSpeed;
|
||||
this.processNewSpeed(1);
|
||||
}
|
||||
GameBoyAdvanceEmulator.prototype.toggleOffthreadGraphics = function (offthreadGfxEnabled) {
|
||||
this.settings.offthreadGfxEnabled = !!offthreadGfxEnabled;
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2015 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceGraphics(IOCore) {
|
||||
//Build references:
|
||||
this.IOCore = IOCore;
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.initialize = function () {
|
||||
this.gfxRenderer = this.IOCore.gfxRenderer;
|
||||
this.dma = this.IOCore.dma;
|
||||
this.dmaChannel3 = this.IOCore.dmaChannel3;
|
||||
this.irq = this.IOCore.irq;
|
||||
this.wait = this.IOCore.wait;
|
||||
this.initializeState();
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.initializeState = function () {
|
||||
//Initialize Pre-Boot:
|
||||
this.renderedScanLine = false;
|
||||
this.statusFlags = 0;
|
||||
this.IRQFlags = 0;
|
||||
this.VCounter = 0;
|
||||
this.currentScanLine = 0;
|
||||
this.LCDTicks = 0;
|
||||
if (this.IOCore.SKIPBoot) {
|
||||
//BIOS entered the ROM at line 0x7C:
|
||||
this.currentScanLine = 0x7C;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.addClocks = function (clocks) {
|
||||
clocks = clocks | 0;
|
||||
//Call this when clocking the state some more:
|
||||
this.LCDTicks = ((this.LCDTicks | 0) + (clocks | 0)) | 0;
|
||||
this.clockLCDState();
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.clockLCDState = function () {
|
||||
if ((this.LCDTicks | 0) >= 960) {
|
||||
this.clockScanLine(); //Line finishes drawing at clock 960.
|
||||
this.clockLCDStatePostRender(); //Check for hblank and clocking into next line.
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.clockScanLine = function () {
|
||||
if (!this.renderedScanLine) { //If we rendered the scanline, don't run this again.
|
||||
this.renderedScanLine = true; //Mark rendering.
|
||||
if ((this.currentScanLine | 0) < 160) {
|
||||
this.gfxRenderer.incrementScanLineQueue(); //Tell the gfx JIT to queue another line to draw.
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.clockLCDStatePostRender = function () {
|
||||
if ((this.LCDTicks | 0) >= 1006) {
|
||||
//HBlank Event Occurred:
|
||||
this.updateHBlank();
|
||||
if ((this.LCDTicks | 0) >= 1232) {
|
||||
//Clocking to next line occurred:
|
||||
this.clockLCDNextLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.clockLCDNextLine = function () {
|
||||
/*We've now overflowed the LCD scan line state machine counter,
|
||||
which tells us we need to be on a new scan-line and refresh over.*/
|
||||
this.renderedScanLine = false; //Unmark line render.
|
||||
this.statusFlags = this.statusFlags & 0x5; //Un-mark HBlank.
|
||||
//De-clock for starting on new scan-line:
|
||||
this.LCDTicks = ((this.LCDTicks | 0) - 1232) | 0; //We start out at the beginning of the next line.
|
||||
//Increment scanline counter:
|
||||
this.currentScanLine = ((this.currentScanLine | 0) + 1) | 0; //Increment to the next scan line.
|
||||
//Handle switching in/out of vblank:
|
||||
if ((this.currentScanLine | 0) >= 160) {
|
||||
//Handle special case scan lines of vblank:
|
||||
switch (this.currentScanLine | 0) {
|
||||
case 160:
|
||||
this.updateVBlankStart(); //Update state for start of vblank.
|
||||
case 161:
|
||||
this.dmaChannel3.gfxDisplaySyncRequest(); //Display Sync. DMA trigger.
|
||||
break;
|
||||
case 162:
|
||||
this.dmaChannel3.gfxDisplaySyncEnableCheck(); //Display Sync. DMA reset on start of line 162.
|
||||
break;
|
||||
case 227:
|
||||
this.statusFlags = this.statusFlags & 0x6; //Un-mark VBlank on start of last vblank line.
|
||||
break;
|
||||
case 228:
|
||||
this.currentScanLine = 0; //Reset scan-line to zero (First line of draw).
|
||||
}
|
||||
}
|
||||
else if ((this.currentScanLine | 0) > 1) {
|
||||
this.dmaChannel3.gfxDisplaySyncRequest(); //Display Sync. DMA trigger.
|
||||
}
|
||||
this.checkVCounter(); //We're on a new scan line, so check the VCounter for match.
|
||||
this.isRenderingCheckPreprocess(); //Update a check value.
|
||||
//Recursive clocking of the LCD state:
|
||||
this.clockLCDState();
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.updateHBlank = function () {
|
||||
if ((this.statusFlags & 0x2) == 0) { //If we were last in HBlank, don't run this again.
|
||||
this.statusFlags = this.statusFlags | 0x2; //Mark HBlank.
|
||||
if ((this.IRQFlags & 0x10) != 0) {
|
||||
this.irq.requestIRQ(0x2); //Check for IRQ.
|
||||
}
|
||||
if ((this.currentScanLine | 0) < 160) {
|
||||
this.dma.gfxHBlankRequest(); //Check for HDMA Trigger.
|
||||
}
|
||||
this.isRenderingCheckPreprocess(); //Update a check value.
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.checkVCounter = function () {
|
||||
if ((this.currentScanLine | 0) == (this.VCounter | 0)) { //Check for VCounter match.
|
||||
this.statusFlags = this.statusFlags | 0x4;
|
||||
if ((this.IRQFlags & 0x20) != 0) { //Check for VCounter IRQ.
|
||||
this.irq.requestIRQ(0x4);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.statusFlags = this.statusFlags & 0x3;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.nextVBlankIRQEventTime = function () {
|
||||
var nextEventTime = 0x7FFFFFFF;
|
||||
if ((this.IRQFlags & 0x8) != 0) {
|
||||
//Only give a time if we're allowed to irq:
|
||||
nextEventTime = this.nextVBlankEventTime() | 0;
|
||||
}
|
||||
return nextEventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.nextHBlankEventTime = function () {
|
||||
var time = this.LCDTicks | 0;
|
||||
if ((time | 0) < 1006) {
|
||||
//Haven't reached hblank yet, so hblank offset - current:
|
||||
time = (1006 - (time | 0)) | 0;
|
||||
}
|
||||
else {
|
||||
//We're in hblank, so it's end clock - current + next scanline hblank offset:
|
||||
time = (2238 - (time | 0)) | 0;
|
||||
}
|
||||
return time | 0;
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.nextHBlankIRQEventTime = function () {
|
||||
var nextEventTime = 0x7FFFFFFF;
|
||||
if ((this.IRQFlags & 0x10) != 0) {
|
||||
//Only give a time if we're allowed to irq:
|
||||
nextEventTime = this.nextHBlankEventTime() | 0;
|
||||
}
|
||||
return nextEventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.nextVCounterIRQEventTime = function () {
|
||||
var nextEventTime = 0x7FFFFFFF;
|
||||
if ((this.IRQFlags & 0x20) != 0) {
|
||||
//Only give a time if we're allowed to irq:
|
||||
nextEventTime = this.nextVCounterEventTime() | 0;
|
||||
}
|
||||
return nextEventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.nextVBlankEventTime = function () {
|
||||
var nextEventTime = this.currentScanLine | 0;
|
||||
if ((nextEventTime | 0) < 160) {
|
||||
//Haven't reached vblank yet, so vblank offset - current:
|
||||
nextEventTime = (160 - (nextEventTime | 0)) | 0;
|
||||
}
|
||||
else {
|
||||
//We're in vblank, so it's end clock - current + next frame vblank offset:
|
||||
nextEventTime = (388 - (nextEventTime | 0)) | 0;
|
||||
}
|
||||
//Convert line count to clocks:
|
||||
nextEventTime = this.convertScanlineToClocks(nextEventTime | 0) | 0;
|
||||
//Subtract scanline offset from clocks:
|
||||
nextEventTime = ((nextEventTime | 0) - (this.LCDTicks | 0)) | 0;
|
||||
return nextEventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.nextHBlankDMAEventTime = function () {
|
||||
var nextEventTime = this.nextHBlankEventTime() | 0;
|
||||
if ((this.currentScanLine | 0) > 159 || ((this.currentScanLine | 0) == 159 && (this.LCDTicks | 0) >= 1006)) {
|
||||
//No HBlank DMA in VBlank:
|
||||
var linesToSkip = (227 - (this.currentScanLine | 0)) | 0;
|
||||
linesToSkip = this.convertScanlineToClocks(linesToSkip | 0) | 0;
|
||||
nextEventTime = ((nextEventTime | 0) + (linesToSkip | 0)) | 0;
|
||||
}
|
||||
return nextEventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.nextVCounterEventTime = function () {
|
||||
var nextEventTime = 0x7FFFFFFF;
|
||||
if ((this.VCounter | 0) <= 227) {
|
||||
//Only match lines within screen or vblank:
|
||||
nextEventTime = ((this.VCounter | 0) - (this.currentScanLine | 0)) | 0;
|
||||
if ((nextEventTime | 0) <= 0) {
|
||||
nextEventTime = ((nextEventTime | 0) + 228) | 0;
|
||||
}
|
||||
nextEventTime = this.convertScanlineToClocks(nextEventTime | 0) | 0;
|
||||
nextEventTime = ((nextEventTime | 0) - (this.LCDTicks | 0)) | 0;
|
||||
}
|
||||
return nextEventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.nextDisplaySyncEventTime = function (delay) {
|
||||
delay = delay | 0;
|
||||
var nextEventTime = 0x7FFFFFFF;
|
||||
if ((this.currentScanLine | 0) >= 161 || (delay | 0) != 0) {
|
||||
//Skip to line 2 metrics:
|
||||
nextEventTime = (230 - (this.currentScanLine | 0)) | 0;
|
||||
nextEventTime = this.convertScanlineToClocks(nextEventTime | 0) | 0;
|
||||
nextEventTime = ((nextEventTime | 0) - (this.LCDTicks | 0)) | 0;
|
||||
}
|
||||
else if ((this.currentScanLine | 0) == 0) {
|
||||
//Doesn't start until line 2:
|
||||
nextEventTime = (2464 - (this.LCDTicks | 0)) | 0;
|
||||
}
|
||||
else {
|
||||
//Line 2 through line 161:
|
||||
nextEventTime = (1232 - (this.LCDTicks | 0)) | 0;
|
||||
}
|
||||
return nextEventTime | 0;
|
||||
}
|
||||
if (typeof Math.imul == "function") {
|
||||
//Math.imul found, insert the optimized path in:
|
||||
GameBoyAdvanceGraphics.prototype.convertScanlineToClocks = function (lines) {
|
||||
lines = lines | 0;
|
||||
lines = Math.imul(lines | 0, 1232) | 0;
|
||||
return lines | 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Math.imul not found, use the compatibility method:
|
||||
GameBoyAdvanceGraphics.prototype.convertScanlineToClocks = function (lines) {
|
||||
lines = lines | 0;
|
||||
lines = ((lines | 0) * 1232) | 0;
|
||||
return lines | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.updateVBlankStart = function () {
|
||||
this.statusFlags = this.statusFlags | 0x1; //Mark VBlank.
|
||||
if ((this.IRQFlags & 0x8) != 0) { //Check for VBlank IRQ.
|
||||
this.irq.requestIRQ(0x1);
|
||||
}
|
||||
this.gfxRenderer.ensureFraming();
|
||||
this.dma.gfxVBlankRequest();
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.isRenderingCheckPreprocess = function () {
|
||||
var isInVisibleLines = ((this.gfxRenderer.IOData8[0] & 0x80) == 0 && (this.statusFlags & 0x1) == 0);
|
||||
var isRendering = (isInVisibleLines && (this.statusFlags & 0x2) == 0) ? 2 : 1;
|
||||
var isOAMRendering = (isInVisibleLines && ((this.statusFlags & 0x2) == 0 || (this.gfxRenderer.IOData8[0] & 0x20) == 0)) ? 2 : 1;
|
||||
this.wait.updateRenderStatus(isRendering | 0, isOAMRendering | 0);
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.writeDISPSTAT8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateCoreClocking();
|
||||
//VBlank flag read only.
|
||||
//HBlank flag read only.
|
||||
//V-Counter flag read only.
|
||||
//Only LCD IRQ generation enablers can be set here:
|
||||
this.IRQFlags = data & 0x38;
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.writeDISPSTAT8_1 = function (data) {
|
||||
data = data | 0;
|
||||
data = data & 0xFF;
|
||||
//V-Counter match value:
|
||||
if ((data | 0) != (this.VCounter | 0)) {
|
||||
this.IOCore.updateCoreClocking();
|
||||
this.VCounter = data | 0;
|
||||
this.checkVCounter();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.writeDISPSTAT16 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateCoreClocking();
|
||||
//VBlank flag read only.
|
||||
//HBlank flag read only.
|
||||
//V-Counter flag read only.
|
||||
//Only LCD IRQ generation enablers can be set here:
|
||||
this.IRQFlags = data & 0x38;
|
||||
data = (data >> 8) & 0xFF;
|
||||
//V-Counter match value:
|
||||
if ((data | 0) != (this.VCounter | 0)) {
|
||||
this.VCounter = data | 0;
|
||||
this.checkVCounter();
|
||||
}
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.readDISPSTAT8_0 = function () {
|
||||
this.IOCore.updateGraphicsClocking();
|
||||
return (this.statusFlags | this.IRQFlags);
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.readDISPSTAT8_1 = function () {
|
||||
return this.VCounter | 0;
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.readDISPSTAT8_2 = function () {
|
||||
this.IOCore.updateGraphicsClocking();
|
||||
return this.currentScanLine | 0;
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.readDISPSTAT16_0 = function () {
|
||||
this.IOCore.updateGraphicsClocking();
|
||||
return ((this.VCounter << 8) | this.statusFlags | this.IRQFlags);
|
||||
}
|
||||
GameBoyAdvanceGraphics.prototype.readDISPSTAT32 = function () {
|
||||
this.IOCore.updateGraphicsClocking();
|
||||
return ((this.currentScanLine << 16) | (this.VCounter << 8) | this.statusFlags | this.IRQFlags);
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2015 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceIRQ(IOCore) {
|
||||
//Build references:
|
||||
this.IOCore = IOCore;
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.initialize = function () {
|
||||
this.interruptsEnabled = 0;
|
||||
this.interruptsRequested = 0;
|
||||
this.IME = 0;
|
||||
this.gfxState = this.IOCore.gfxState;
|
||||
this.timer = this.IOCore.timer;
|
||||
this.dmaChannel0 = this.IOCore.dmaChannel0;
|
||||
this.dmaChannel1 = this.IOCore.dmaChannel1;
|
||||
this.dmaChannel2 = this.IOCore.dmaChannel2;
|
||||
this.dmaChannel3 = this.IOCore.dmaChannel3;
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.IRQMatch = function () {
|
||||
//Used to exit HALT:
|
||||
return (this.interruptsEnabled & this.interruptsRequested);
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.checkForIRQFire = function () {
|
||||
//Tell the CPU core when the emulated hardware is triggering an IRQ:
|
||||
this.IOCore.cpu.triggerIRQ(this.interruptsEnabled & this.interruptsRequested & this.IME);
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.requestIRQ = function (irqLineToSet) {
|
||||
irqLineToSet = irqLineToSet | 0;
|
||||
this.interruptsRequested = this.interruptsRequested | irqLineToSet;
|
||||
this.checkForIRQFire();
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.writeIME = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateCoreClocking();
|
||||
this.IME = (data << 31) >> 31;
|
||||
this.checkForIRQFire();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.writeIE8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateCoreClocking();
|
||||
var oldValue = this.interruptsEnabled & 0x3F00;
|
||||
data = data & 0xFF;
|
||||
data = data | oldValue;
|
||||
this.interruptsEnabled = data | 0;
|
||||
this.checkForIRQFire();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.writeIE8_1 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateCoreClocking();
|
||||
var oldValue = this.interruptsEnabled & 0xFF;
|
||||
data = (data & 0x3F) << 8;
|
||||
data = data | oldValue;
|
||||
this.interruptsEnabled = data | 0;
|
||||
this.checkForIRQFire();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.writeIE16 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateCoreClocking();
|
||||
this.interruptsEnabled = data & 0x3FFF;
|
||||
this.checkForIRQFire();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.writeIF8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateCoreClocking();
|
||||
data = ~(data & 0xFF);
|
||||
this.interruptsRequested = this.interruptsRequested & data;
|
||||
this.checkForIRQFire();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.writeIF8_1 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateCoreClocking();
|
||||
data = ~((data & 0xFF) << 8);
|
||||
this.interruptsRequested = this.interruptsRequested & data;
|
||||
this.checkForIRQFire();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.writeIF16 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateCoreClocking();
|
||||
data = ~data;
|
||||
this.interruptsRequested = this.interruptsRequested & data;
|
||||
this.checkForIRQFire();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.writeIRQ32 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateCoreClocking();
|
||||
this.interruptsEnabled = data & 0x3FFF;
|
||||
data = ~(data >> 16);
|
||||
this.interruptsRequested = this.interruptsRequested & data;
|
||||
this.checkForIRQFire();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.readIME = function () {
|
||||
var data = this.IME & 0x1;
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.readIE8_0 = function () {
|
||||
var data = this.interruptsEnabled & 0xFF;
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.readIE8_1 = function () {
|
||||
var data = this.interruptsEnabled >> 8;
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.readIE16 = function () {
|
||||
var data = this.interruptsEnabled | 0;
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.readIF8_0 = function () {
|
||||
this.IOCore.updateCoreSpillRetain();
|
||||
var data = this.interruptsRequested & 0xFF;
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.readIF8_1 = function () {
|
||||
this.IOCore.updateCoreSpillRetain();
|
||||
var data = this.interruptsRequested >> 8;
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.readIF16 = function () {
|
||||
this.IOCore.updateCoreSpillRetain();
|
||||
var data = this.interruptsRequested | 0;
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.readIRQ32 = function () {
|
||||
this.IOCore.updateCoreSpillRetain();
|
||||
var data = (this.interruptsRequested << 16) | this.interruptsEnabled;
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.nextEventTime = function () {
|
||||
var clocks = 0x7FFFFFFF;
|
||||
if ((this.interruptsEnabled & 0x1) != 0) {
|
||||
clocks = this.gfxState.nextVBlankIRQEventTime() | 0;
|
||||
}
|
||||
if ((this.interruptsEnabled & 0x2) != 0) {
|
||||
clocks = Math.min(clocks | 0, this.gfxState.nextHBlankIRQEventTime() | 0) | 0;
|
||||
}
|
||||
if ((this.interruptsEnabled & 0x4) != 0) {
|
||||
clocks = Math.min(clocks | 0, this.gfxState.nextVCounterIRQEventTime() | 0) | 0;
|
||||
}
|
||||
if ((this.interruptsEnabled & 0x8) != 0) {
|
||||
clocks = Math.min(clocks | 0, this.timer.nextTimer0IRQEventTime() | 0) | 0;
|
||||
}
|
||||
if ((this.interruptsEnabled & 0x10) != 0) {
|
||||
clocks = Math.min(clocks | 0, this.timer.nextTimer1IRQEventTime() | 0) | 0;
|
||||
}
|
||||
if ((this.interruptsEnabled & 0x20) != 0) {
|
||||
clocks = Math.min(clocks | 0, this.timer.nextTimer2IRQEventTime() | 0) | 0;
|
||||
}
|
||||
if ((this.interruptsEnabled & 0x40) != 0) {
|
||||
clocks = Math.min(clocks | 0, this.timer.nextTimer3IRQEventTime() | 0) | 0;
|
||||
}
|
||||
/*if ((this.interruptsEnabled & 0x80) != 0) {
|
||||
clocks = Math.min(clocks | 0, this.IOCore.serial.nextIRQEventTime() | 0) | 0;
|
||||
}
|
||||
if ((this.interruptsEnabled & 0x2000) != 0) {
|
||||
clocks = Math.min(clocks | 0, this.IOCore.cartridge.nextIRQEventTime() | 0) | 0;
|
||||
}*/
|
||||
return clocks | 0;
|
||||
}
|
||||
GameBoyAdvanceIRQ.prototype.nextIRQEventTime = function () {
|
||||
var clocks = 0x7FFFFFFF;
|
||||
//Checks IME:
|
||||
if ((this.IME | 0) != 0) {
|
||||
clocks = this.nextEventTime() | 0;
|
||||
}
|
||||
return clocks | 0;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2015 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceJoyPad(IOCore) {
|
||||
this.IOCore = IOCore;
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.initialize = function () {
|
||||
this.keyInput = 0x3FF;
|
||||
this.keyInterrupt = 0;
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.keyPress = function (keyPressed) {
|
||||
keyPressed = keyPressed | 0;
|
||||
keyPressed = 1 << (keyPressed | 0);
|
||||
this.keyInput = this.keyInput & (~keyPressed);
|
||||
this.checkForMatch();
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.keyRelease = function (keyReleased) {
|
||||
keyReleased = keyReleased | 0;
|
||||
keyReleased = 1 << (keyReleased | 0);
|
||||
this.keyInput = this.keyInput | keyReleased;
|
||||
this.checkForMatch();
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.checkForMatch = function () {
|
||||
if ((this.keyInterrupt & 0x8000) != 0) {
|
||||
if (((~this.keyInput) & this.keyInterrupt & 0x3FF) == (this.keyInterrupt & 0x3FF)) {
|
||||
this.IOCore.deflagStop();
|
||||
this.checkForIRQ();
|
||||
}
|
||||
}
|
||||
else if (((~this.keyInput) & this.keyInterrupt & 0x3FF) != 0) {
|
||||
this.IOCore.deflagStop();
|
||||
this.checkForIRQ();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.checkForIRQ = function () {
|
||||
if ((this.keyInterrupt & 0x4000) != 0) {
|
||||
this.IOCore.irq.requestIRQ(0x1000);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.readKeyStatus8_0 = function () {
|
||||
return this.keyInput & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.readKeyStatus8_1 = function () {
|
||||
return (this.keyInput >> 8) | 0;
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.readKeyStatus16 = function () {
|
||||
return this.keyInput | 0;
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.writeKeyControl8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.keyInterrupt = this.keyInterrupt & 0xC300;
|
||||
data = data & 0xFF;
|
||||
this.keyInterrupt = this.keyInterrupt | data;
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.writeKeyControl8_1 = function (data) {
|
||||
data = data | 0;
|
||||
this.keyInterrupt = this.keyInterrupt & 0xFF;
|
||||
data = data & 0xC3;
|
||||
this.keyInterrupt = this.keyInterrupt | (data << 8);
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.writeKeyControl16 = function (data) {
|
||||
data = data | 0;
|
||||
this.keyInterrupt = data & 0xC3FF;
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.readKeyControl8_0 = function () {
|
||||
return this.keyInterrupt & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.readKeyControl8_1 = function () {
|
||||
return (this.keyInterrupt >> 8) | 0;
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.readKeyControl16 = function () {
|
||||
return this.keyInterrupt | 0;
|
||||
}
|
||||
GameBoyAdvanceJoyPad.prototype.readKeyStatusControl32 = function () {
|
||||
return this.keyInput | (this.keyInterrupt << 16);
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2016 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceIO(SKIPBoot, coreExposed, BIOS, ROM) {
|
||||
//State Machine Tracking:
|
||||
this.systemStatus = 0;
|
||||
this.cyclesToIterate = 0;
|
||||
this.cyclesOveriteratedPreviously = 0;
|
||||
this.accumulatedClocks = 0;
|
||||
this.graphicsClocks = 0;
|
||||
this.timerClocks = 0;
|
||||
this.serialClocks = 0;
|
||||
this.nextEventClocks = 0;
|
||||
//this.BIOSFound = false;
|
||||
//Do we skip the BIOS Boot Intro?
|
||||
this.SKIPBoot = !!SKIPBoot;
|
||||
//References passed to us:
|
||||
this.coreExposed = coreExposed;
|
||||
this.BIOS = BIOS;
|
||||
this.ROM = ROM;
|
||||
//Build the core object layout:
|
||||
this.memory = new GameBoyAdvanceMemory(this);
|
||||
this.dma = new GameBoyAdvanceDMA(this);
|
||||
this.dmaChannel0 = new GameBoyAdvanceDMA0(this);
|
||||
this.dmaChannel1 = new GameBoyAdvanceDMA1(this);
|
||||
this.dmaChannel2 = new GameBoyAdvanceDMA2(this);
|
||||
this.dmaChannel3 = new GameBoyAdvanceDMA3(this);
|
||||
this.gfxState = new GameBoyAdvanceGraphics(this);
|
||||
this.gfxRenderer = new GameBoyAdvanceRendererProxy(this);
|
||||
this.sound = new GameBoyAdvanceSound(this);
|
||||
this.timer = new GameBoyAdvanceTimer(this);
|
||||
this.irq = new GameBoyAdvanceIRQ(this);
|
||||
this.serial = new GameBoyAdvanceSerial(this);
|
||||
this.joypad = new GameBoyAdvanceJoyPad(this);
|
||||
this.cartridge = new GameBoyAdvanceCartridge(this);
|
||||
this.saves = new GameBoyAdvanceSaves(this);
|
||||
this.wait = new GameBoyAdvanceWait(this);
|
||||
this.cpu = new GameBoyAdvanceCPU(this);
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.initialize = function () {
|
||||
var allowInit = 1;
|
||||
//Now initialize each component:
|
||||
if ((this.memory.initialize() | 0) == 1) {
|
||||
//BIOS loaded in OK, so initialize the rest:
|
||||
this.dma.initialize();
|
||||
this.dmaChannel0.initialize();
|
||||
this.dmaChannel1.initialize();
|
||||
this.dmaChannel2.initialize();
|
||||
this.dmaChannel3.initialize();
|
||||
this.gfxState.initialize();
|
||||
this.gfxRenderer.initialize();
|
||||
this.sound.initialize();
|
||||
this.timer.initialize();
|
||||
this.irq.initialize();
|
||||
this.serial.initialize();
|
||||
this.joypad.initialize();
|
||||
this.cartridge.initialize();
|
||||
this.saves.initialize();
|
||||
this.wait.initialize();
|
||||
this.cpu.initialize();
|
||||
}
|
||||
else {
|
||||
allowInit = 0;
|
||||
}
|
||||
return allowInit | 0;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.assignInstructionCoreReferences = function (ARM, THUMB) {
|
||||
//Passed here once the CPU component is initialized:
|
||||
this.ARM = ARM;
|
||||
this.THUMB = THUMB;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.enter = function (CPUCyclesTotal) {
|
||||
//Find out how many clocks to iterate through this run:
|
||||
this.cyclesToIterate = ((CPUCyclesTotal | 0) + (this.cyclesOveriteratedPreviously | 0)) | 0;
|
||||
//An extra check to make sure we don't do stuff if we did too much last run:
|
||||
if ((this.cyclesToIterate | 0) > 0) {
|
||||
//Update our core event prediction:
|
||||
this.updateCoreEventTime();
|
||||
//If clocks remaining, run iterator:
|
||||
this.run();
|
||||
//Spill our core event clocking:
|
||||
this.updateCoreClocking();
|
||||
//Ensure audio buffers at least once per iteration:
|
||||
this.sound.audioJIT();
|
||||
}
|
||||
//If we clocked just a little too much, subtract the extra from the next run:
|
||||
this.cyclesOveriteratedPreviously = this.cyclesToIterate | 0;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.run = function () {
|
||||
//Clock through the state machine:
|
||||
while (true) {
|
||||
//Dispatch to optimized run loops:
|
||||
switch (this.systemStatus & 0x84) {
|
||||
case 0:
|
||||
//ARM instruction set:
|
||||
this.runARM();
|
||||
break;
|
||||
case 0x4:
|
||||
//THUMB instruction set:
|
||||
this.runTHUMB();
|
||||
break;
|
||||
default:
|
||||
//End of stepping:
|
||||
this.deflagIterationEnd();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.runARM = function () {
|
||||
//Clock through the state machine:
|
||||
while (true) {
|
||||
//Handle the current system state selected:
|
||||
switch (this.systemStatus | 0) {
|
||||
case 0: //CPU Handle State (Normal ARM)
|
||||
this.ARM.executeIteration();
|
||||
break;
|
||||
case 1:
|
||||
case 2: //CPU Handle State (Bubble ARM)
|
||||
this.ARM.executeBubble();
|
||||
this.tickBubble();
|
||||
break;
|
||||
default: //Handle lesser called / End of stepping
|
||||
//Dispatch on IRQ/DMA/HALT/STOP/END bit flags
|
||||
switch (this.systemStatus >> 2) {
|
||||
case 0x2:
|
||||
//IRQ Handle State:
|
||||
this.handleIRQARM();
|
||||
break;
|
||||
case 0x4:
|
||||
case 0x6:
|
||||
//DMA Handle State
|
||||
case 0xC:
|
||||
case 0xE:
|
||||
//DMA Inside Halt State
|
||||
this.handleDMA();
|
||||
break;
|
||||
case 0x8:
|
||||
case 0xA:
|
||||
//Handle Halt State
|
||||
this.handleHalt();
|
||||
break;
|
||||
default: //Handle Stop State
|
||||
//THUMB flagged stuff falls to here intentionally:
|
||||
//End of Stepping and/or CPU run loop switch:
|
||||
if ((this.systemStatus & 0x84) != 0) {
|
||||
return;
|
||||
}
|
||||
this.handleStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.runTHUMB = function () {
|
||||
//Clock through the state machine:
|
||||
while (true) {
|
||||
//Handle the current system state selected:
|
||||
switch (this.systemStatus | 0) {
|
||||
case 4: //CPU Handle State (Normal THUMB)
|
||||
this.THUMB.executeIteration();
|
||||
break;
|
||||
case 5:
|
||||
case 6: //CPU Handle State (Bubble THUMB)
|
||||
this.THUMB.executeBubble();
|
||||
this.tickBubble();
|
||||
break;
|
||||
default: //Handle lesser called / End of stepping
|
||||
//Dispatch on IRQ/DMA/HALT/STOP/END bit flags
|
||||
switch (this.systemStatus >> 2) {
|
||||
case 0x3:
|
||||
//IRQ Handle State:
|
||||
this.handleIRQThumb();
|
||||
break;
|
||||
case 0x5:
|
||||
case 0x7:
|
||||
//DMA Handle State
|
||||
case 0xD:
|
||||
case 0xF:
|
||||
//DMA Inside Halt State
|
||||
this.handleDMA();
|
||||
break;
|
||||
case 0x9:
|
||||
case 0x11:
|
||||
//Handle Halt State
|
||||
this.handleHalt();
|
||||
break;
|
||||
default: //Handle Stop State
|
||||
//ARM flagged stuff falls to here intentionally:
|
||||
//End of Stepping and/or CPU run loop switch:
|
||||
if ((this.systemStatus & 0x84) != 0x4) {
|
||||
return;
|
||||
}
|
||||
this.handleStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.updateCore = function (clocks) {
|
||||
clocks = clocks | 0;
|
||||
//This is used during normal/dma modes of operation:
|
||||
this.accumulatedClocks = ((this.accumulatedClocks | 0) + (clocks | 0)) | 0;
|
||||
if ((this.accumulatedClocks | 0) >= (this.nextEventClocks | 0)) {
|
||||
this.updateCoreSpill();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.updateCoreForce = function (clocks) {
|
||||
clocks = clocks | 0;
|
||||
//This is used during halt mode of operation:
|
||||
this.accumulatedClocks = ((this.accumulatedClocks | 0) + (clocks | 0)) | 0;
|
||||
this.updateCoreSpill();
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.updateCoreNegative = function (clocks) {
|
||||
clocks = clocks | 0;
|
||||
//This is used during normal/dma modes of operation:
|
||||
this.accumulatedClocks = ((this.accumulatedClocks | 0) - (clocks | 0)) | 0;
|
||||
if ((this.accumulatedClocks | 0) >= (this.nextEventClocks | 0)) {
|
||||
this.updateCoreSpill();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.updateCoreSingle = function () {
|
||||
//This is used during normal/dma modes of operation:
|
||||
this.accumulatedClocks = ((this.accumulatedClocks | 0) + 1) | 0;
|
||||
if ((this.accumulatedClocks | 0) >= (this.nextEventClocks | 0)) {
|
||||
this.updateCoreSpill();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.updateCoreSpill = function () {
|
||||
//Invalidate & recompute new event times:
|
||||
this.updateCoreClocking();
|
||||
this.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.updateCoreSpillRetain = function () {
|
||||
//Keep the last prediction, just decrement it out, as it's still valid:
|
||||
this.nextEventClocks = ((this.nextEventClocks | 0) - (this.accumulatedClocks | 0)) | 0;
|
||||
this.updateCoreClocking();
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.updateCoreClocking = function () {
|
||||
var clocks = this.accumulatedClocks | 0;
|
||||
//Decrement the clocks per iteration counter:
|
||||
this.cyclesToIterate = ((this.cyclesToIterate | 0) - (clocks | 0)) | 0;
|
||||
//Clock all components:
|
||||
this.gfxState.addClocks(((clocks | 0) - (this.graphicsClocks | 0)) | 0);
|
||||
this.timer.addClocks(((clocks | 0) - (this.timerClocks | 0)) | 0);
|
||||
this.serial.addClocks(((clocks | 0) - (this.serialClocks | 0)) | 0);
|
||||
this.accumulatedClocks = 0;
|
||||
this.graphicsClocks = 0;
|
||||
this.timerClocks = 0;
|
||||
this.serialClocks = 0;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.updateGraphicsClocking = function () {
|
||||
//Clock gfx component:
|
||||
this.gfxState.addClocks(((this.accumulatedClocks | 0) - (this.graphicsClocks | 0)) | 0);
|
||||
this.graphicsClocks = this.accumulatedClocks | 0;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.updateTimerClocking = function () {
|
||||
//Clock timer component:
|
||||
this.timer.addClocks(((this.accumulatedClocks | 0) - (this.timerClocks | 0)) | 0);
|
||||
this.timerClocks = this.accumulatedClocks | 0;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.updateSerialClocking = function () {
|
||||
//Clock serial component:
|
||||
this.serial.addClocks(((this.accumulatedClocks | 0) - (this.serialClocks | 0)) | 0);
|
||||
this.serialClocks = this.accumulatedClocks | 0;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.updateCoreEventTime = function () {
|
||||
//Predict how many clocks until the next DMA or IRQ event:
|
||||
this.nextEventClocks = this.cyclesUntilNextEvent() | 0;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.getRemainingCycles = function () {
|
||||
//Return the number of cycles left until iteration end:
|
||||
if ((this.cyclesToIterate | 0) < 1) {
|
||||
//Change our stepper to our end sequence:
|
||||
this.flagIterationEnd();
|
||||
return 0;
|
||||
}
|
||||
return this.cyclesToIterate | 0;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.handleIRQARM = function () {
|
||||
if ((this.systemStatus | 0) > 0x8) {
|
||||
//CPU Handle State (Bubble ARM)
|
||||
this.ARM.executeBubble();
|
||||
this.tickBubble();
|
||||
}
|
||||
else {
|
||||
//CPU Handle State (IRQ)
|
||||
this.cpu.IRQinARM();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.handleIRQThumb = function () {
|
||||
if ((this.systemStatus | 0) > 0xC) {
|
||||
//CPU Handle State (Bubble THUMB)
|
||||
this.THUMB.executeBubble();
|
||||
this.tickBubble();
|
||||
}
|
||||
else {
|
||||
//CPU Handle State (IRQ)
|
||||
this.cpu.IRQinTHUMB();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.handleDMA = function () {
|
||||
/*
|
||||
Loop our state status in here as
|
||||
an optimized iteration, as DMA stepping instances
|
||||
happen in quick succession of each other, and
|
||||
aren't often done for one memory word only.
|
||||
*/
|
||||
do {
|
||||
//Perform a DMA read and write:
|
||||
this.dma.perform();
|
||||
} while ((this.systemStatus & 0x90) == 0x10);
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.handleHalt = function () {
|
||||
if ((this.irq.IRQMatch() | 0) == 0) {
|
||||
//Clock up to next IRQ match or DMA:
|
||||
this.updateCoreForce(this.cyclesUntilNextHALTEvent() | 0);
|
||||
}
|
||||
else {
|
||||
//Exit HALT promptly:
|
||||
this.deflagHalt();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.handleStop = function () {
|
||||
//Update sound system to add silence to buffer:
|
||||
this.sound.addClocks(this.getRemainingCycles() | 0);
|
||||
this.cyclesToIterate = 0;
|
||||
//Exits when user presses joypad or from an external irq outside of GBA internal.
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.cyclesUntilNextHALTEvent = function () {
|
||||
//Find the clocks to the next HALT leave or DMA event:
|
||||
var haltClocks = this.irq.nextEventTime() | 0;
|
||||
var dmaClocks = this.dma.nextEventTime() | 0;
|
||||
return this.solveClosestTime(haltClocks | 0, dmaClocks | 0) | 0;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.cyclesUntilNextEvent = function () {
|
||||
//Find the clocks to the next IRQ or DMA event:
|
||||
var irqClocks = this.irq.nextIRQEventTime() | 0;
|
||||
var dmaClocks = this.dma.nextEventTime() | 0;
|
||||
return this.solveClosestTime(irqClocks | 0, dmaClocks | 0) | 0;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.solveClosestTime = function (clocks1, clocks2) {
|
||||
clocks1 = clocks1 | 0;
|
||||
clocks2 = clocks2 | 0;
|
||||
//Find the clocks closest to the next event:
|
||||
var clocks = this.getRemainingCycles() | 0;
|
||||
clocks = Math.min(clocks | 0, clocks1 | 0, clocks2 | 0);
|
||||
return clocks | 0;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.flagBubble = function () {
|
||||
//Flag a CPU pipeline bubble to step through:
|
||||
this.systemStatus = this.systemStatus | 0x2;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.tickBubble = function () {
|
||||
//Tick down a CPU pipeline bubble to step through:
|
||||
this.systemStatus = ((this.systemStatus | 0) - 1) | 0;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.flagTHUMB = function () {
|
||||
//Flag a CPU IRQ to step through:
|
||||
this.systemStatus = this.systemStatus | 0x4;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.deflagTHUMB = function () {
|
||||
//Deflag a CPU IRQ to step through:
|
||||
this.systemStatus = this.systemStatus & 0xFB;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.flagIRQ = function () {
|
||||
//Flag THUMB CPU mode to step through:
|
||||
this.systemStatus = this.systemStatus | 0x8;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.deflagIRQ = function () {
|
||||
//Deflag THUMB CPU mode to step through:
|
||||
this.systemStatus = this.systemStatus & 0xF7;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.flagDMA = function () {
|
||||
//Flag a DMA event to step through:
|
||||
this.systemStatus = this.systemStatus | 0x10;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.deflagDMA = function () {
|
||||
//Deflag a DMA event to step through:
|
||||
this.systemStatus = this.systemStatus & 0xEF;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.flagHalt = function () {
|
||||
//Flag a halt event to step through:
|
||||
this.systemStatus = this.systemStatus | 0x20;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.deflagHalt = function () {
|
||||
//Deflag a halt event to step through:
|
||||
this.systemStatus = this.systemStatus & 0xDF;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.flagStop = function () {
|
||||
//Flag a halt event to step through:
|
||||
this.systemStatus = this.systemStatus | 0x40;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.deflagStop = function () {
|
||||
//Deflag a halt event to step through:
|
||||
this.systemStatus = this.systemStatus & 0xBF;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.flagIterationEnd = function () {
|
||||
//Flag a run loop kill event to step through:
|
||||
this.systemStatus = this.systemStatus | 0x80;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.deflagIterationEnd = function () {
|
||||
//Deflag a run loop kill event to step through:
|
||||
this.systemStatus = this.systemStatus & 0x7F;
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.isStopped = function () {
|
||||
//Sound system uses this to emulate a unpowered audio output:
|
||||
return ((this.systemStatus & 0x40) != 0);
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.inDMA = function () {
|
||||
//Save system uses this to detect dma:
|
||||
return ((this.systemStatus & 0x10) != 0);
|
||||
}
|
||||
GameBoyAdvanceIO.prototype.getCurrentFetchValue = function () {
|
||||
//Last valid value output for bad reads:
|
||||
var fetch = 0;
|
||||
if ((this.systemStatus & 0x10) == 0) {
|
||||
fetch = this.cpu.getCurrentFetchValue() | 0;
|
||||
}
|
||||
else {
|
||||
fetch = this.dma.getCurrentFetchValue() | 0;
|
||||
}
|
||||
return fetch | 0;
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2016 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceSaves(IOCore) {
|
||||
this.cartridge = IOCore.cartridge;
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.initialize = function () {
|
||||
this.saveType = 0;
|
||||
this.GPIOChip = new GameBoyAdvanceGPIOChip();
|
||||
this.UNDETERMINED = new GameBoyAdvanceSaveDeterminer(this);
|
||||
this.SRAMChip = new GameBoyAdvanceSRAMChip();
|
||||
this.FLASHChip = new GameBoyAdvanceFLASHChip(this.cartridge.flash_is128, this.cartridge.flash_isAtmel);
|
||||
this.EEPROMChip = new GameBoyAdvanceEEPROMChip(this.cartridge.IOCore);
|
||||
this.currentChip = this.UNDETERMINED;
|
||||
this.referenceSave(this.saveType);
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.referenceSave = function (saveType) {
|
||||
saveType = saveType | 0;
|
||||
switch (saveType | 0) {
|
||||
case 0:
|
||||
this.currentChip = this.UNDETERMINED;
|
||||
break;
|
||||
case 1:
|
||||
this.currentChip = this.SRAMChip;
|
||||
break;
|
||||
case 2:
|
||||
this.currentChip = this.FLASHChip;
|
||||
break;
|
||||
case 3:
|
||||
this.currentChip = this.EEPROMChip;
|
||||
}
|
||||
this.currentChip.initialize();
|
||||
this.saveType = saveType | 0;
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.importSave = function (saves, saveType) {
|
||||
saveType = saveType | 0;
|
||||
this.UNDETERMINED.load(saves);
|
||||
this.SRAMChip.load(saves);
|
||||
this.FLASHChip.load(saves);
|
||||
this.EEPROMChip.load(saves);
|
||||
this.referenceSave(saveType | 0);
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.importRTC = function (saves) {
|
||||
this.GPIOChip.loadRTC(saves);
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.importGPIOType = function (gpioType) {
|
||||
gpioType = gpioType | 0;
|
||||
this.GPIOChip.loadType(gpioType | 0);
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.exportSave = function () {
|
||||
return this.currentChip.saves;
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.exportSaveType = function () {
|
||||
return this.saveType | 0;
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.readGPIO8 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((this.GPIOChip.getType() | 0) > 0) {
|
||||
//GPIO:
|
||||
data = this.GPIOChip.read8(address | 0) | 0;
|
||||
}
|
||||
else {
|
||||
//ROM:
|
||||
data = this.cartridge.readROMOnly8(address | 0) | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.readEEPROM8 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((this.saveType | 0) == 3) {
|
||||
//EEPROM:
|
||||
data = this.EEPROMChip.read8() | 0;
|
||||
}
|
||||
else {
|
||||
//UNKNOWN:
|
||||
data = this.UNDETERMINED.readEEPROM8(address | 0) | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.readGPIO16 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((this.GPIOChip.getType() | 0) > 0) {
|
||||
//GPIO:
|
||||
data = this.GPIOChip.read16(address | 0) | 0;
|
||||
}
|
||||
else {
|
||||
//ROM:
|
||||
data = this.cartridge.readROMOnly16(address | 0) | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.readEEPROM16 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((this.saveType | 0) == 3) {
|
||||
//EEPROM:
|
||||
data = this.EEPROMChip.read16() | 0;
|
||||
}
|
||||
else {
|
||||
//UNKNOWN:
|
||||
data = this.UNDETERMINED.readEEPROM16(address | 0) | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.readGPIO32 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((this.GPIOChip.getType() | 0) > 0) {
|
||||
//GPIO:
|
||||
data = this.GPIOChip.read32(address | 0) | 0;
|
||||
}
|
||||
else {
|
||||
//ROM:
|
||||
data = this.cartridge.readROMOnly32(address | 0) | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.readEEPROM32 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((this.saveType | 0) == 3) {
|
||||
//EEPROM:
|
||||
data = this.EEPROMChip.read32() | 0;
|
||||
}
|
||||
else {
|
||||
//UNKNOWN:
|
||||
data = this.UNDETERMINED.readEEPROM32(address | 0) | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.readSRAM = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
switch (this.saveType | 0) {
|
||||
case 0:
|
||||
//UNKNOWN:
|
||||
data = this.UNDETERMINED.readSRAM(address | 0) | 0;
|
||||
break;
|
||||
case 1:
|
||||
//SRAM:
|
||||
data = this.SRAMChip.read(address | 0) | 0;
|
||||
break;
|
||||
case 2:
|
||||
//FLASH:
|
||||
data = this.FLASHChip.read(address | 0) | 0;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.writeGPIO8 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
if ((this.GPIOChip.getType() | 0) > 0) {
|
||||
//GPIO:
|
||||
this.GPIOChip.write8(address | 0, data | 0);
|
||||
}
|
||||
else {
|
||||
//Unknown:
|
||||
this.UNDETERMINED.writeGPIO8(address | 0, data | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.writeGPIO16 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
if ((this.GPIOChip.getType() | 0) > 0) {
|
||||
//GPIO:
|
||||
this.GPIOChip.write16(address | 0, data | 0);
|
||||
}
|
||||
else {
|
||||
//Unknown:
|
||||
this.UNDETERMINED.writeGPIO16(address | 0, data | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.writeEEPROM16 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
if ((this.saveType | 0) == 3) {
|
||||
//EEPROM:
|
||||
this.EEPROMChip.write16(data | 0);
|
||||
}
|
||||
else {
|
||||
//Unknown:
|
||||
this.UNDETERMINED.writeEEPROM16(address | 0, data | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.writeGPIO32 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
if ((this.GPIOChip.getType() | 0) > 0) {
|
||||
//GPIO:
|
||||
this.GPIOChip.write32(address | 0, data | 0);
|
||||
}
|
||||
else {
|
||||
//Unknown:
|
||||
this.UNDETERMINED.writeGPIO32(address | 0, data | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.writeSRAM = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
switch (this.saveType | 0) {
|
||||
case 0:
|
||||
//Unknown:
|
||||
this.UNDETERMINED.writeSRAM(address | 0, data | 0);
|
||||
break;
|
||||
case 1:
|
||||
//SRAM:
|
||||
this.SRAMChip.write(address | 0, data | 0);
|
||||
break;
|
||||
case 2:
|
||||
//FLASH:
|
||||
this.FLASHChip.write(address | 0, data | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSaves.prototype.writeSRAMIfDefined = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
switch (this.saveType | 0) {
|
||||
case 0:
|
||||
//UNKNOWN:
|
||||
this.SRAMChip.initialize();
|
||||
case 1:
|
||||
//SRAM:
|
||||
this.SRAMChip.write(address | 0, data | 0);
|
||||
break;
|
||||
case 2:
|
||||
//FLASH:
|
||||
this.FLASHChip.write(address | 0, data | 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2015 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceSerial(IOCore) {
|
||||
this.IOCore = IOCore;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.initialize = function () {
|
||||
this.SIODATA_A = 0xFFFF;
|
||||
this.SIODATA_B = 0xFFFF;
|
||||
this.SIODATA_C = 0xFFFF;
|
||||
this.SIODATA_D = 0xFFFF;
|
||||
this.SIOShiftClockExternal = 0;
|
||||
this.SIOShiftClockDivider = 0x40;
|
||||
this.SIOCNT0_DATA = 0x0C;
|
||||
this.SIOTransferStarted = false;
|
||||
this.SIOMULT_PLAYER_NUMBER = 0;
|
||||
this.SIOCOMMERROR = false;
|
||||
this.SIOBaudRate = 0;
|
||||
this.SIOCNT_UART_CTS = false;
|
||||
this.SIOCNT_UART_MISC = 0;
|
||||
this.SIOCNT_UART_FIFO = 0;
|
||||
this.SIOCNT_IRQ = 0;
|
||||
this.SIOCNT_MODE = 0;
|
||||
this.SIOCNT_UART_RECV_ENABLE = false;
|
||||
this.SIOCNT_UART_SEND_ENABLE = false;
|
||||
this.SIOCNT_UART_PARITY_ENABLE = false;
|
||||
this.SIOCNT_UART_FIFO_ENABLE = false;
|
||||
this.SIODATA8 = 0xFFFF;
|
||||
this.RCNTMode = 0;
|
||||
this.RCNTIRQ = false;
|
||||
this.RCNTDataBits = 0;
|
||||
this.RCNTDataBitFlow = 0;
|
||||
this.JOYBUS_IRQ = 0;
|
||||
this.JOYBUS_CNTL_FLAGS = 0;
|
||||
this.JOYBUS_RECV0 = 0xFF;
|
||||
this.JOYBUS_RECV1 = 0xFF;
|
||||
this.JOYBUS_RECV2 = 0xFF;
|
||||
this.JOYBUS_RECV3 = 0xFF;
|
||||
this.JOYBUS_SEND0 = 0xFF;
|
||||
this.JOYBUS_SEND1 = 0xFF;
|
||||
this.JOYBUS_SEND2 = 0xFF;
|
||||
this.JOYBUS_SEND3 = 0xFF;
|
||||
this.JOYBUS_STAT = 0;
|
||||
this.shiftClocks = 0;
|
||||
this.serialBitsShifted = 0;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.SIOMultiplayerBaudRate = [
|
||||
9600,
|
||||
38400,
|
||||
57600,
|
||||
115200
|
||||
];
|
||||
GameBoyAdvanceSerial.prototype.addClocks = function (clocks) {
|
||||
clocks = clocks | 0;
|
||||
if ((this.RCNTMode | 0) < 2) {
|
||||
switch (this.SIOCNT_MODE | 0) {
|
||||
case 0:
|
||||
case 1:
|
||||
if (this.SIOTransferStarted && (this.SIOShiftClockExternal | 0) == 0) {
|
||||
this.shiftClocks = ((this.shiftClocks | 0) + (clocks | 0)) | 0;
|
||||
while ((this.shiftClocks | 0) >= (this.SIOShiftClockDivider | 0)) {
|
||||
this.shiftClocks = ((this.shiftClocks | 0) - (this.SIOShiftClockDivider | 0)) | 0;
|
||||
this.clockSerial();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (this.SIOTransferStarted && (this.SIOMULT_PLAYER_NUMBER | 0) == 0) {
|
||||
this.shiftClocks = ((this.shiftClocks | 0) + (clocks | 0)) | 0;
|
||||
while ((this.shiftClocks | 0) >= (this.SIOShiftClockDivider | 0)) {
|
||||
this.shiftClocks = ((this.shiftClocks | 0) - (this.SIOShiftClockDivider | 0)) | 0;
|
||||
this.clockMultiplayer();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (this.SIOCNT_UART_SEND_ENABLE && !this.SIOCNT_UART_CTS) {
|
||||
this.shiftClocks = ((this.shiftClocks | 0) + (clocks | 0)) | 0;
|
||||
while ((this.shiftClocks | 0) >= (this.SIOShiftClockDivider | 0)) {
|
||||
this.shiftClocks = ((this.shiftClocks | 0) - (this.SIOShiftClockDivider | 0)) | 0;
|
||||
this.clockUART();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.clockSerial = function () {
|
||||
//Emulate as if no slaves connected:
|
||||
this.serialBitsShifted = ((this.serialBitsShifted | 0) + 1) | 0;
|
||||
if ((this.SIOCNT_MODE | 0) == 0) {
|
||||
//8-bit
|
||||
this.SIODATA8 = ((this.SIODATA8 << 1) | 1) & 0xFFFF;
|
||||
if ((this.serialBitsShifted | 0) == 8) {
|
||||
this.SIOTransferStarted = false;
|
||||
this.serialBitsShifted = 0;
|
||||
if ((this.SIOCNT_IRQ | 0) != 0) {
|
||||
//this.IOCore.irq.requestIRQ(0x80);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
//32-bit
|
||||
this.SIODATA_D = ((this.SIODATA_D << 1) & 0xFE) | (this.SIODATA_C >> 7);
|
||||
this.SIODATA_C = ((this.SIODATA_C << 1) & 0xFE) | (this.SIODATA_B >> 7);
|
||||
this.SIODATA_B = ((this.SIODATA_B << 1) & 0xFE) | (this.SIODATA_A >> 7);
|
||||
this.SIODATA_A = ((this.SIODATA_A << 1) & 0xFE) | 1;
|
||||
if ((this.serialBitsShifted | 0) == 32) {
|
||||
this.SIOTransferStarted = false;
|
||||
this.serialBitsShifted = 0;
|
||||
if ((this.SIOCNT_IRQ | 0) != 0) {
|
||||
//this.IOCore.irq.requestIRQ(0x80);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.clockMultiplayer = function () {
|
||||
//Emulate as if no slaves connected:
|
||||
this.SIODATA_A = this.SIODATA8 | 0;
|
||||
this.SIODATA_B = 0xFFFF;
|
||||
this.SIODATA_C = 0xFFFF;
|
||||
this.SIODATA_D = 0xFFFF;
|
||||
this.SIOTransferStarted = false;
|
||||
this.SIOCOMMERROR = true;
|
||||
if ((this.SIOCNT_IRQ | 0) != 0) {
|
||||
//this.IOCore.irq.requestIRQ(0x80);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.clockUART = function () {
|
||||
this.serialBitsShifted = ((this.serialBitsShifted | 0) + 1) | 0;
|
||||
if (this.SIOCNT_UART_FIFO_ENABLE) {
|
||||
if ((this.serialBitsShifted | 0) == 8) {
|
||||
this.serialBitsShifted = 0;
|
||||
this.SIOCNT_UART_FIFO = Math.max(((this.SIOCNT_UART_FIFO | 0) - 1) | 0, 0) | 0;
|
||||
if ((this.SIOCNT_UART_FIFO | 0) == 0 && (this.SIOCNT_IRQ | 0) != 0) {
|
||||
//this.IOCore.irq.requestIRQ(0x80);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((this.serialBitsShifted | 0) == 8) {
|
||||
this.serialBitsShifted = 0;
|
||||
if ((this.SIOCNT_IRQ | 0) != 0) {
|
||||
//this.IOCore.irq.requestIRQ(0x80);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeSIODATA_A0 = function (data) {
|
||||
data = data | 0;
|
||||
this.SIODATA_A = (this.SIODATA_A & 0xFF00) | data;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readSIODATA_A0 = function () {
|
||||
return this.SIODATA_A & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeSIODATA_A1 = function (data) {
|
||||
data = data | 0;
|
||||
this.SIODATA_A = (this.SIODATA_A & 0xFF) | (data << 8);
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readSIODATA_A1 = function () {
|
||||
return this.SIODATA_A >> 8;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeSIODATA_B0 = function (data) {
|
||||
data = data | 0;
|
||||
this.SIODATA_B = (this.SIODATA_B & 0xFF00) | data;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readSIODATA_B0 = function () {
|
||||
return this.SIODATA_B & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeSIODATA_B1 = function (data) {
|
||||
data = data | 0;
|
||||
this.SIODATA_B = (this.SIODATA_B & 0xFF) | (data << 8);
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readSIODATA_B1 = function () {
|
||||
return this.SIODATA_B >> 8;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeSIODATA_C0 = function (data) {
|
||||
data = data | 0;
|
||||
this.SIODATA_C = (this.SIODATA_C & 0xFF00) | data;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readSIODATA_C0 = function () {
|
||||
return this.SIODATA_C & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeSIODATA_C1 = function (data) {
|
||||
data = data | 0;
|
||||
this.SIODATA_C = (this.SIODATA_C & 0xFF) | (data << 8);
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readSIODATA_C1 = function () {
|
||||
return this.SIODATA_C >> 8;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeSIODATA_D0 = function (data) {
|
||||
data = data | 0;
|
||||
this.SIODATA_D = (this.SIODATA_D & 0xFF00) | data;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readSIODATA_D0 = function () {
|
||||
return this.SIODATA_D & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeSIODATA_D1 = function (data) {
|
||||
data = data | 0;
|
||||
this.SIODATA_D = (this.SIODATA_D & 0xFF) | (data << 8);
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readSIODATA_D1 = function () {
|
||||
return this.SIODATA_D >> 8;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeSIOCNT0 = function (data) {
|
||||
if ((this.RCNTMode | 0) < 0x2) {
|
||||
switch (this.SIOCNT_MODE | 0) {
|
||||
//8-Bit:
|
||||
case 0:
|
||||
//32-Bit:
|
||||
case 1:
|
||||
this.SIOShiftClockExternal = data & 0x1;
|
||||
this.SIOShiftClockDivider = ((data & 0x2) != 0) ? 0x8 : 0x40;
|
||||
this.SIOCNT0_DATA = data & 0xB;
|
||||
if ((data & 0x80) != 0) {
|
||||
if (!this.SIOTransferStarted) {
|
||||
this.SIOTransferStarted = true;
|
||||
this.serialBitsShifted = 0;
|
||||
this.shiftClocks = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.SIOTransferStarted = false;
|
||||
}
|
||||
break;
|
||||
//Multiplayer:
|
||||
case 2:
|
||||
this.SIOBaudRate = data & 0x3;
|
||||
this.SIOShiftClockDivider = this.SIOMultiplayerBaudRate[this.SIOBaudRate | 0] | 0;
|
||||
this.SIOMULT_PLAYER_NUMBER = (data >> 4) & 0x3;
|
||||
this.SIOCOMMERROR = ((data & 0x40) != 0);
|
||||
if ((data & 0x80) != 0) {
|
||||
if (!this.SIOTransferStarted) {
|
||||
this.SIOTransferStarted = true;
|
||||
if ((this.SIOMULT_PLAYER_NUMBER | 0) == 0) {
|
||||
this.SIODATA_A = 0xFFFF;
|
||||
this.SIODATA_B = 0xFFFF;
|
||||
this.SIODATA_C = 0xFFFF;
|
||||
this.SIODATA_D = 0xFFFF;
|
||||
}
|
||||
this.serialBitsShifted = 0;
|
||||
this.shiftClocks = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.SIOTransferStarted = false;
|
||||
}
|
||||
break;
|
||||
//UART:
|
||||
case 3:
|
||||
this.SIOBaudRate = data & 0x3;
|
||||
this.SIOShiftClockDivider = this.SIOMultiplayerBaudRate[this.SIOBaudRate | 0] | 0;
|
||||
this.SIOCNT_UART_MISC = (data & 0xCF) >> 2;
|
||||
this.SIOCNT_UART_CTS = ((data & 0x4) != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readSIOCNT0 = function () {
|
||||
if (this.RCNTMode < 0x2) {
|
||||
switch (this.SIOCNT_MODE) {
|
||||
//8-Bit:
|
||||
case 0:
|
||||
//32-Bit:
|
||||
case 1:
|
||||
return ((this.SIOTransferStarted) ? 0x80 : 0) | 0x74 | this.SIOCNT0_DATA;
|
||||
//Multiplayer:
|
||||
case 2:
|
||||
return ((this.SIOTransferStarted) ? 0x80 : 0) | ((this.SIOCOMMERROR) ? 0x40 : 0) | (this.SIOMULT_PLAYER_NUMBER << 4) | this.SIOBaudRate;
|
||||
//UART:
|
||||
case 3:
|
||||
return (this.SIOCNT_UART_MISC << 2) | ((this.SIOCNT_UART_FIFO == 4) ? 0x30 : 0x20) | this.SIOBaudRate;
|
||||
}
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeSIOCNT1 = function (data) {
|
||||
this.SIOCNT_IRQ = data & 0x40;
|
||||
this.SIOCNT_MODE = (data >> 4) & 0x3;
|
||||
this.SIOCNT_UART_RECV_ENABLE = ((data & 0x8) != 0);
|
||||
this.SIOCNT_UART_SEND_ENABLE = ((data & 0x4) != 0);
|
||||
this.SIOCNT_UART_PARITY_ENABLE = ((data & 0x2) != 0);
|
||||
this.SIOCNT_UART_FIFO_ENABLE = ((data & 0x1) != 0);
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readSIOCNT1 = function () {
|
||||
return (0x80 | this.SIOCNT_IRQ | (this.SIOCNT_MODE << 4) | ((this.SIOCNT_UART_RECV_ENABLE) ? 0x8 : 0) |
|
||||
((this.SIOCNT_UART_SEND_ENABLE) ? 0x4 : 0) | ((this.SIOCNT_UART_PARITY_ENABLE) ? 0x2 : 0) | ((this.SIOCNT_UART_FIFO_ENABLE) ? 0x2 : 0));
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeSIODATA8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.SIODATA8 = (this.SIODATA8 & 0xFF00) | data;
|
||||
if ((this.RCNTMode | 0) < 0x2 && (this.SIOCNT_MODE | 0) == 3 && this.SIOCNT_UART_FIFO_ENABLE) {
|
||||
this.SIOCNT_UART_FIFO = Math.min(((this.SIOCNT_UART_FIFO | 0) + 1) | 0, 4) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readSIODATA8_0 = function () {
|
||||
return this.SIODATA8 & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeSIODATA8_1 = function (data) {
|
||||
data = data | 0;
|
||||
this.SIODATA8 = (this.SIODATA8 & 0xFF) | (data << 8);
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readSIODATA8_1 = function () {
|
||||
return this.SIODATA8 >> 8;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeRCNT0 = function (data) {
|
||||
if ((this.RCNTMode | 0) == 0x2) {
|
||||
//General Comm:
|
||||
var oldDataBits = this.RCNTDataBits | 0;
|
||||
this.RCNTDataBits = data & 0xF; //Device manually controls SI/SO/SC/SD here.
|
||||
this.RCNTDataBitFlow = data >> 4;
|
||||
if (this.RCNTIRQ && ((oldDataBits ^ this.RCNTDataBits) & oldDataBits & 0x4) != 0) {
|
||||
//SI fell low, trigger IRQ:
|
||||
//this.IOCore.irq.requestIRQ(0x80);
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readRCNT0 = function () {
|
||||
return (this.RCNTDataBitFlow << 4) | this.RCNTDataBits;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeRCNT1 = function (data) {
|
||||
this.RCNTMode = data >> 6;
|
||||
this.RCNTIRQ = ((data & 0x1) != 0);
|
||||
if ((this.RCNTMode | 0) != 0x2) {
|
||||
//Force SI/SO/SC/SD to low as we're never "hooked" up:
|
||||
this.RCNTDataBits = 0;
|
||||
this.RCNTDataBitFlow = 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readRCNT1 = function () {
|
||||
return (this.RCNTMode << 6) | ((this.RCNTIRQ) ? 0x3F : 0x3E);
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeJOYCNT = function (data) {
|
||||
this.JOYBUS_IRQ = (data << 25) >> 31;
|
||||
this.JOYBUS_CNTL_FLAGS &= ~(data & 0x7);
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readJOYCNT = function () {
|
||||
return (this.JOYBUS_CNTL_FLAGS | 0x40) | (0xB8 & this.JOYBUS_IRQ);
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeJOYBUS_RECV0 = function (data) {
|
||||
this.JOYBUS_RECV0 = data | 0;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readJOYBUS_RECV0 = function () {
|
||||
this.JOYBUS_STAT = this.JOYBUS_STAT & 0xF7;
|
||||
return this.JOYBUS_RECV0 | 0;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeJOYBUS_RECV1 = function (data) {
|
||||
this.JOYBUS_RECV1 = data | 0;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readJOYBUS_RECV1 = function () {
|
||||
this.JOYBUS_STAT = this.JOYBUS_STAT & 0xF7;
|
||||
return this.JOYBUS_RECV1 | 0;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeJOYBUS_RECV2 = function (data) {
|
||||
this.JOYBUS_RECV2 = data | 0;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readJOYBUS_RECV2 = function () {
|
||||
this.JOYBUS_STAT = this.JOYBUS_STAT & 0xF7;
|
||||
return this.JOYBUS_RECV2 | 0;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeJOYBUS_RECV3 = function (data) {
|
||||
this.JOYBUS_RECV3 = data | 0;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readJOYBUS_RECV3 = function () {
|
||||
this.JOYBUS_STAT = this.JOYBUS_STAT & 0xF7;
|
||||
return this.JOYBUS_RECV3 | 0;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeJOYBUS_SEND0 = function (data) {
|
||||
this.JOYBUS_SEND0 = data | 0;
|
||||
this.JOYBUS_STAT = this.JOYBUS_STAT | 0x2;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readJOYBUS_SEND0 = function () {
|
||||
return this.JOYBUS_SEND0 | 0;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeJOYBUS_SEND1 = function (data) {
|
||||
this.JOYBUS_SEND1 = data | 0;
|
||||
this.JOYBUS_STAT = this.JOYBUS_STAT | 0x2;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readJOYBUS_SEND1 = function () {
|
||||
return this.JOYBUS_SEND1 | 0;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeJOYBUS_SEND2 = function (data) {
|
||||
this.JOYBUS_SEND2 = data | 0;
|
||||
this.JOYBUS_STAT = this.JOYBUS_STAT | 0x2;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readJOYBUS_SEND2 = function () {
|
||||
return this.JOYBUS_SEND2 | 0;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeJOYBUS_SEND3 = function (data) {
|
||||
this.JOYBUS_SEND3 = data | 0;
|
||||
this.JOYBUS_STAT = this.JOYBUS_STAT | 0x2;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readJOYBUS_SEND3 = function () {
|
||||
return this.JOYBUS_SEND3 | 0;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.writeJOYBUS_STAT = function (data) {
|
||||
this.JOYBUS_STAT = data | 0;
|
||||
}
|
||||
GameBoyAdvanceSerial.prototype.readJOYBUS_STAT = function () {
|
||||
return 0xC5 | this.JOYBUS_STAT;
|
||||
}
|
||||
/*GameBoyAdvanceSerial.prototype.nextIRQEventTime = function (clocks) {
|
||||
if ((this.SIOCNT_IRQ | 0) != 0 && (this.RCNTMode | 0) < 2) {
|
||||
switch (this.SIOCNT_MODE | 0) {
|
||||
case 0:
|
||||
case 1:
|
||||
if (this.SIOTransferStarted && (this.SIOShiftClockExternal | 0) == 0) {
|
||||
return ((((this.SIOCNT_MODE == 1) ? 31 : 7) - this.serialBitsShifted) * this.SIOShiftClockDivider) + (this.SIOShiftClockDivider - this.shiftClocks);
|
||||
}
|
||||
else {
|
||||
return 0x7FFFFFFF;
|
||||
}
|
||||
case 2:
|
||||
if (this.SIOTransferStarted && this.SIOMULT_PLAYER_NUMBER == 0) {
|
||||
return this.SIOShiftClockDivider - this.shiftClocks;
|
||||
}
|
||||
else {
|
||||
return 0x7FFFFFFF;
|
||||
}
|
||||
case 3:
|
||||
if (this.SIOCNT_UART_SEND_ENABLE && !this.SIOCNT_UART_CTS) {
|
||||
return (Math.max(((this.SIOCNT_UART_FIFO_ENABLE) ? (this.SIOCNT_UART_FIFO * 8) : 8) - 1, 0) * this.SIOShiftClockDivider) + (this.SIOShiftClockDivider - this.shiftClocks);
|
||||
}
|
||||
else {
|
||||
return 0x7FFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return 0x7FFFFFFF;
|
||||
}
|
||||
}*/
|
||||
@@ -0,0 +1,846 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2015 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceTimer(IOCore) {
|
||||
//Build references:
|
||||
this.IOCore = IOCore;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.prescalarLookup = [
|
||||
0,
|
||||
0x6,
|
||||
0x8,
|
||||
0xA
|
||||
];
|
||||
GameBoyAdvanceTimer.prototype.initialize = function () {
|
||||
this.timer0Counter = 0;
|
||||
this.timer0Reload = 0;
|
||||
this.timer0Control = 0;
|
||||
this.timer0Enabled = false;
|
||||
this.timer0IRQ = false;
|
||||
this.timer0Precounter = 0;
|
||||
this.timer0Prescalar = 1;
|
||||
this.timer0PrescalarShifted = 0;
|
||||
this.timer1Counter = 0;
|
||||
this.timer1Reload = 0;
|
||||
this.timer1Control = 0;
|
||||
this.timer1Enabled = false;
|
||||
this.timer1IRQ = false;
|
||||
this.timer1Precounter = 0;
|
||||
this.timer1Prescalar = 1;
|
||||
this.timer1PrescalarShifted = 0;
|
||||
this.timer1CountUp = false;
|
||||
this.timer2Counter = 0;
|
||||
this.timer2Reload = 0;
|
||||
this.timer2Control = 0;
|
||||
this.timer2Enabled = false;
|
||||
this.timer2IRQ = false;
|
||||
this.timer2Precounter = 0;
|
||||
this.timer2Prescalar = 1;
|
||||
this.timer2PrescalarShifted = 0;
|
||||
this.timer2CountUp = false;
|
||||
this.timer3Counter = 0;
|
||||
this.timer3Reload = 0;
|
||||
this.timer3Control = 0;
|
||||
this.timer3Enabled = false;
|
||||
this.timer3IRQ = false;
|
||||
this.timer3Precounter = 0;
|
||||
this.timer3Prescalar = 1;
|
||||
this.timer3PrescalarShifted = 0;
|
||||
this.timer3CountUp = false;
|
||||
this.timer1UseMainClocks = false;
|
||||
this.timer1UseChainedClocks = false;
|
||||
this.timer2UseMainClocks = false;
|
||||
this.timer2UseChainedClocks = false;
|
||||
this.timer3UseMainClocks = false;
|
||||
this.timer3UseChainedClocks = false;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.addClocks = function (clocks) {
|
||||
clocks = clocks | 0;
|
||||
//See if timer channels 0 and 1 are enabled:
|
||||
this.clockSoundTimers(clocks | 0);
|
||||
//See if timer channel 2 is enabled:
|
||||
this.clockTimer2(clocks | 0);
|
||||
//See if timer channel 3 is enabled:
|
||||
this.clockTimer3(clocks | 0);
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.clockSoundTimers = function (audioClocks) {
|
||||
audioClocks = audioClocks | 0;
|
||||
for (var predictedClocks = 0, overflowClocks = 0; (audioClocks | 0) > 0; audioClocks = ((audioClocks | 0) - (predictedClocks | 0)) | 0) {
|
||||
overflowClocks = this.nextAudioTimerOverflow() | 0;
|
||||
predictedClocks = Math.min(audioClocks | 0, overflowClocks | 0) | 0;
|
||||
//See if timer channel 0 is enabled:
|
||||
this.clockTimer0(predictedClocks | 0);
|
||||
//See if timer channel 1 is enabled:
|
||||
this.clockTimer1(predictedClocks | 0);
|
||||
//Clock audio system up to latest timer:
|
||||
this.IOCore.sound.addClocks(predictedClocks | 0);
|
||||
//Only jit if overflow was seen:
|
||||
if ((overflowClocks | 0) == (predictedClocks | 0)) {
|
||||
this.IOCore.sound.audioJIT();
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.clockTimer0 = function (clocks) {
|
||||
clocks = clocks | 0;
|
||||
if (this.timer0Enabled) {
|
||||
this.timer0Precounter = ((this.timer0Precounter | 0) + (clocks | 0)) | 0;
|
||||
while ((this.timer0Precounter | 0) >= (this.timer0Prescalar | 0)) {
|
||||
var iterations = Math.min(this.timer0Precounter >> (this.timer0PrescalarShifted | 0), (0x10000 - (this.timer0Counter | 0)) | 0) | 0;
|
||||
this.timer0Precounter = ((this.timer0Precounter | 0) - ((iterations | 0) << (this.timer0PrescalarShifted | 0))) | 0;
|
||||
this.timer0Counter = ((this.timer0Counter | 0) + (iterations | 0)) | 0;
|
||||
if ((this.timer0Counter | 0) > 0xFFFF) {
|
||||
this.timer0Counter = this.timer0Reload | 0;
|
||||
this.timer0ExternalTriggerCheck();
|
||||
this.timer1ClockUpTickCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.clockTimer1 = function (clocks) {
|
||||
clocks = clocks | 0;
|
||||
if (this.timer1UseMainClocks) {
|
||||
this.timer1Precounter = ((this.timer1Precounter | 0) + (clocks | 0)) | 0;
|
||||
while ((this.timer1Precounter | 0) >= (this.timer1Prescalar | 0)) {
|
||||
var iterations = Math.min(this.timer1Precounter >> (this.timer1PrescalarShifted | 0), (0x10000 - (this.timer1Counter | 0)) | 0) | 0;
|
||||
this.timer1Precounter = ((this.timer1Precounter | 0) - ((iterations | 0) << (this.timer1PrescalarShifted | 0))) | 0;
|
||||
this.timer1Counter = ((this.timer1Counter | 0) + (iterations | 0)) | 0;
|
||||
if ((this.timer1Counter | 0) > 0xFFFF) {
|
||||
this.timer1Counter = this.timer1Reload | 0;
|
||||
this.timer1ExternalTriggerCheck();
|
||||
this.timer2ClockUpTickCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.clockTimer2 = function (clocks) {
|
||||
clocks = clocks | 0;
|
||||
if (this.timer2UseMainClocks) {
|
||||
this.timer2Precounter = ((this.timer2Precounter | 0) + (clocks | 0)) | 0;
|
||||
while ((this.timer2Precounter | 0) >= (this.timer2Prescalar | 0)) {
|
||||
var iterations = Math.min(this.timer2Precounter >> (this.timer2PrescalarShifted | 0), (0x10000 - (this.timer2Counter | 0)) | 0) | 0;
|
||||
this.timer2Precounter = ((this.timer2Precounter | 0) - ((iterations | 0) << (this.timer2PrescalarShifted | 0))) | 0;
|
||||
this.timer2Counter = ((this.timer2Counter | 0) + (iterations | 0)) | 0;
|
||||
if ((this.timer2Counter | 0) > 0xFFFF) {
|
||||
this.timer2Counter = this.timer2Reload | 0;
|
||||
this.timer2ExternalTriggerCheck();
|
||||
this.timer3ClockUpTickCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.clockTimer3 = function (clocks) {
|
||||
clocks = clocks | 0;
|
||||
if (this.timer3UseMainClocks) {
|
||||
this.timer3Precounter = ((this.timer3Precounter | 0) + (clocks | 0)) | 0;
|
||||
while ((this.timer3Precounter | 0) >= (this.timer3Prescalar | 0)) {
|
||||
var iterations = Math.min(this.timer3Precounter >> (this.timer3PrescalarShifted | 0), (0x10000 - (this.timer3Counter | 0)) | 0) | 0;
|
||||
this.timer3Precounter = ((this.timer3Precounter | 0) - ((iterations | 0) << (this.timer3PrescalarShifted | 0))) | 0;
|
||||
this.timer3Counter = ((this.timer3Counter | 0) + (iterations | 0)) | 0;
|
||||
if ((this.timer3Counter | 0) > 0xFFFF) {
|
||||
this.timer3Counter = this.timer3Reload | 0;
|
||||
this.timer3ExternalTriggerCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.timer1ClockUpTickCheck = function () {
|
||||
if (this.timer1UseChainedClocks) {
|
||||
this.timer1Counter = ((this.timer1Counter | 0) + 1) | 0;
|
||||
if ((this.timer1Counter | 0) > 0xFFFF) {
|
||||
this.timer1Counter = this.timer1Reload | 0;
|
||||
this.timer1ExternalTriggerCheck();
|
||||
this.timer2ClockUpTickCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.timer2ClockUpTickCheck = function () {
|
||||
if (this.timer2UseChainedClocks) {
|
||||
this.timer2Counter = ((this.timer2Counter | 0) + 1) | 0;
|
||||
if ((this.timer2Counter | 0) > 0xFFFF) {
|
||||
this.timer2Counter = this.timer2Reload | 0;
|
||||
this.timer2ExternalTriggerCheck();
|
||||
this.timer3ClockUpTickCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.timer3ClockUpTickCheck = function () {
|
||||
if (this.timer3UseChainedClocks) {
|
||||
this.timer3Counter = ((this.timer3Counter | 0) + 1) | 0;
|
||||
if ((this.timer3Counter | 0) > 0xFFFF) {
|
||||
this.timer3Counter = this.timer3Reload | 0;
|
||||
this.timer3ExternalTriggerCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.timer0ExternalTriggerCheck = function () {
|
||||
if (this.timer0IRQ) {
|
||||
this.IOCore.irq.requestIRQ(0x08);
|
||||
}
|
||||
this.IOCore.sound.AGBDirectSoundTimer0ClockTick();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.timer1ExternalTriggerCheck = function () {
|
||||
if (this.timer1IRQ) {
|
||||
this.IOCore.irq.requestIRQ(0x10);
|
||||
}
|
||||
this.IOCore.sound.AGBDirectSoundTimer1ClockTick();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.timer2ExternalTriggerCheck = function () {
|
||||
if (this.timer2IRQ) {
|
||||
this.IOCore.irq.requestIRQ(0x20);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.timer3ExternalTriggerCheck = function () {
|
||||
if (this.timer3IRQ) {
|
||||
this.IOCore.irq.requestIRQ(0x40);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM0CNT8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.IOCore.sound.audioJIT();
|
||||
this.timer0Reload = this.timer0Reload & 0xFF00;
|
||||
data = data & 0xFF;
|
||||
this.timer0Reload = this.timer0Reload | data;
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM0CNT8_1 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.IOCore.sound.audioJIT();
|
||||
this.timer0Reload = this.timer0Reload & 0xFF;
|
||||
data = data & 0xFF;
|
||||
this.timer0Reload = this.timer0Reload | (data << 8);
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM0CNT8_2 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.IOCore.sound.audioJIT();
|
||||
this.timer0Control = data & 0xFF;
|
||||
if ((data & 0x80) != 0) {
|
||||
if (!this.timer0Enabled) {
|
||||
this.timer0Counter = this.timer0Reload | 0;
|
||||
this.timer0Enabled = true;
|
||||
this.timer0Precounter = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.timer0Enabled = false;
|
||||
}
|
||||
this.timer0IRQ = ((data & 0x40) != 0);
|
||||
this.timer0PrescalarShifted = this.prescalarLookup[data & 0x03] | 0;
|
||||
this.timer0Prescalar = 1 << (this.timer0PrescalarShifted | 0);
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM0CNT16 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.IOCore.sound.audioJIT();
|
||||
this.timer0Reload = data & 0xFFFF;
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM0CNT32 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.IOCore.sound.audioJIT();
|
||||
this.timer0Reload = data & 0xFFFF;
|
||||
this.timer0Control = data >> 16;
|
||||
if ((data & 0x800000) != 0) {
|
||||
if (!this.timer0Enabled) {
|
||||
this.timer0Counter = this.timer0Reload | 0;
|
||||
this.timer0Enabled = true;
|
||||
this.timer0Precounter = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.timer0Enabled = false;
|
||||
}
|
||||
this.timer0IRQ = ((data & 0x400000) != 0);
|
||||
this.timer0PrescalarShifted = this.prescalarLookup[(data >> 16) & 0x03] | 0;
|
||||
this.timer0Prescalar = 1 << (this.timer0PrescalarShifted | 0);
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM0CNT8_0 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
return this.timer0Counter & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM0CNT8_1 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
return (this.timer0Counter & 0xFF00) >> 8;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM0CNT8_2 = function () {
|
||||
return this.timer0Control & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM0CNT16 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
return this.timer0Counter | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM0CNT32 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
var data = (this.timer0Control & 0xFF) << 16;
|
||||
data = data | this.timer0Counter;
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM1CNT8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.IOCore.sound.audioJIT();
|
||||
this.timer1Reload = this.timer1Reload & 0xFF00;
|
||||
data = data & 0xFF;
|
||||
this.timer1Reload = this.timer1Reload | data;
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM1CNT8_1 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.IOCore.sound.audioJIT();
|
||||
this.timer1Reload = this.timer1Reload & 0xFF;
|
||||
data = data & 0xFF;
|
||||
this.timer1Reload = this.timer1Reload | (data << 8);
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM1CNT8_2 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.IOCore.sound.audioJIT();
|
||||
this.timer1Control = data & 0xFF;
|
||||
if ((data & 0x80) != 0) {
|
||||
if (!this.timer1Enabled) {
|
||||
this.timer1Counter = this.timer1Reload | 0;
|
||||
this.timer1Enabled = true;
|
||||
this.timer1Precounter = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.timer1Enabled = false;
|
||||
}
|
||||
this.timer1IRQ = ((data & 0x40) != 0);
|
||||
this.timer1CountUp = ((data & 0x4) != 0);
|
||||
this.timer1PrescalarShifted = this.prescalarLookup[data & 0x03] | 0;
|
||||
this.timer1Prescalar = 1 << (this.timer1PrescalarShifted | 0);
|
||||
this.preprocessTimer1();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM1CNT16 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.IOCore.sound.audioJIT();
|
||||
this.timer1Reload = data & 0xFFFF;
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM1CNT32 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.IOCore.sound.audioJIT();
|
||||
this.timer1Reload = data & 0xFFFF;
|
||||
this.timer1Control = data >> 16;
|
||||
if ((data & 0x800000) != 0) {
|
||||
if (!this.timer1Enabled) {
|
||||
this.timer1Counter = this.timer1Reload | 0;
|
||||
this.timer1Enabled = true;
|
||||
this.timer1Precounter = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.timer1Enabled = false;
|
||||
}
|
||||
this.timer1IRQ = ((data & 0x400000) != 0);
|
||||
this.timer1CountUp = ((data & 0x40000) != 0);
|
||||
this.timer1PrescalarShifted = this.prescalarLookup[(data >> 16) & 0x03] | 0;
|
||||
this.timer1Prescalar = 1 << (this.timer1PrescalarShifted | 0);
|
||||
this.preprocessTimer1();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM1CNT8_0 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
return this.timer1Counter & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM1CNT8_1 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
return (this.timer1Counter & 0xFF00) >> 8;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM1CNT8_2 = function () {
|
||||
return this.timer1Control & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM1CNT16 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
return this.timer1Counter | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM1CNT32 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
var data = (this.timer1Control & 0xFF) << 16;
|
||||
data = data | this.timer1Counter;
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM2CNT8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.timer2Reload = this.timer2Reload & 0xFF00;
|
||||
data = data & 0xFF;
|
||||
this.timer2Reload = this.timer2Reload | data;
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM2CNT8_1 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.timer2Reload = this.timer2Reload & 0xFF;
|
||||
data = data & 0xFF;
|
||||
this.timer2Reload = this.timer2Reload | (data << 8);
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM2CNT8_2 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.timer2Control = data & 0xFF;
|
||||
if ((data & 0x80) != 0) {
|
||||
if (!this.timer2Enabled) {
|
||||
this.timer2Counter = this.timer2Reload | 0;
|
||||
this.timer2Enabled = true;
|
||||
this.timer2Precounter = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.timer2Enabled = false;
|
||||
}
|
||||
this.timer2IRQ = ((data & 0x40) != 0);
|
||||
this.timer2CountUp = ((data & 0x4) != 0);
|
||||
this.timer2PrescalarShifted = this.prescalarLookup[data & 0x03] | 0;
|
||||
this.timer2Prescalar = 1 << (this.timer2PrescalarShifted | 0);
|
||||
this.preprocessTimer2();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM2CNT16 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.timer2Reload = data & 0xFFFF;
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM2CNT32 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.timer2Reload = data & 0xFFFF;
|
||||
this.timer2Control = data >> 16;
|
||||
if ((data & 0x800000) != 0) {
|
||||
if (!this.timer2Enabled) {
|
||||
this.timer2Counter = this.timer2Reload | 0;
|
||||
this.timer2Enabled = true;
|
||||
this.timer2Precounter = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.timer2Enabled = false;
|
||||
}
|
||||
this.timer2IRQ = ((data & 0x400000) != 0);
|
||||
this.timer2CountUp = ((data & 0x40000) != 0);
|
||||
this.timer2PrescalarShifted = this.prescalarLookup[(data >> 16) & 0x03] | 0;
|
||||
this.timer2Prescalar = 1 << (this.timer2PrescalarShifted | 0);
|
||||
this.preprocessTimer2();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM2CNT8_0 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
return this.timer2Counter & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM2CNT8_1 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
return (this.timer2Counter & 0xFF00) >> 8;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM2CNT8_2 = function () {
|
||||
return this.timer2Control & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM2CNT16 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
return this.timer2Counter | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM2CNT32 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
var data = (this.timer2Control & 0xFF) << 16;
|
||||
data = data | this.timer2Counter;
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM3CNT8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.timer3Reload = this.timer3Reload & 0xFF00;
|
||||
data = data & 0xFF;
|
||||
this.timer3Reload = this.timer3Reload | data;
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM3CNT8_1 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.timer3Reload = this.timer3Reload & 0xFF;
|
||||
data = data & 0xFF;
|
||||
this.timer3Reload = this.timer3Reload | (data << 8);
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM3CNT8_2 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.timer3Control = data & 0xFF;
|
||||
if ((data & 0x80) != 0) {
|
||||
if (!this.timer3Enabled) {
|
||||
this.timer3Counter = this.timer3Reload | 0;
|
||||
this.timer3Enabled = true;
|
||||
this.timer3Precounter = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.timer3Enabled = false;
|
||||
}
|
||||
this.timer3IRQ = ((data & 0x40) != 0);
|
||||
this.timer3CountUp = ((data & 0x4) != 0);
|
||||
this.timer3PrescalarShifted = this.prescalarLookup[data & 0x03] | 0;
|
||||
this.timer3Prescalar = 1 << (this.timer3PrescalarShifted | 0);
|
||||
this.preprocessTimer3();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM3CNT16 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.timer3Reload = data & 0xFFFF;
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.writeTM3CNT32 = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateTimerClocking();
|
||||
this.timer3Reload = data & 0xFFFF;
|
||||
this.timer3Control = data >> 16;
|
||||
if ((data & 0x800000) != 0) {
|
||||
if (!this.timer3Enabled) {
|
||||
this.timer3Counter = this.timer3Reload | 0;
|
||||
this.timer3Enabled = true;
|
||||
this.timer3Precounter = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.timer3Enabled = false;
|
||||
}
|
||||
this.timer3IRQ = ((data & 0x400000) != 0);
|
||||
this.timer3CountUp = ((data & 0x40000) != 0);
|
||||
this.timer3PrescalarShifted = this.prescalarLookup[(data >> 16) & 0x03] | 0;
|
||||
this.timer3Prescalar = 1 << (this.timer3PrescalarShifted | 0);
|
||||
this.preprocessTimer3();
|
||||
this.IOCore.updateCoreEventTime();
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM3CNT8_0 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
return this.timer3Counter & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM3CNT8_1 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
return (this.timer3Counter & 0xFF00) >> 8;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM3CNT8_2 = function () {
|
||||
return this.timer3Control & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM3CNT16 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
return this.timer3Counter | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.readTM3CNT32 = function () {
|
||||
this.IOCore.updateTimerClocking();
|
||||
var data = (this.timer3Control & 0xFF) << 16;
|
||||
data = data | this.timer3Counter;
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.preprocessTimer1 = function () {
|
||||
this.timer1UseMainClocks = (this.timer1Enabled && !this.timer1CountUp);
|
||||
this.timer1UseChainedClocks = (this.timer1Enabled && this.timer1CountUp);
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.preprocessTimer2 = function () {
|
||||
this.timer2UseMainClocks = (this.timer2Enabled && !this.timer2CountUp);
|
||||
this.timer2UseChainedClocks = (this.timer2Enabled && this.timer2CountUp);
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.preprocessTimer3 = function () {
|
||||
this.timer3UseMainClocks = (this.timer3Enabled && !this.timer3CountUp);
|
||||
this.timer3UseChainedClocks = (this.timer3Enabled && this.timer3CountUp);
|
||||
}
|
||||
if (typeof Math.imul == "function") {
|
||||
//Math.imul found, insert the optimized path in:
|
||||
GameBoyAdvanceTimer.prototype.nextTimer0OverflowBase = function () {
|
||||
var countUntilReload = (0x10000 - (this.timer0Counter | 0)) | 0;
|
||||
countUntilReload = Math.imul(countUntilReload | 0, this.timer0Prescalar | 0) | 0;
|
||||
countUntilReload = ((countUntilReload | 0) - (this.timer0Precounter | 0)) | 0;
|
||||
return countUntilReload | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer0OverflowSingle = function () {
|
||||
var eventTime = 0x7FFFFFFF;
|
||||
if (this.timer0Enabled) {
|
||||
eventTime = this.nextTimer0OverflowBase() | 0;
|
||||
}
|
||||
return eventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer0Overflow = function (numOverflows) {
|
||||
numOverflows = numOverflows | 0;
|
||||
var eventTime = 0x7FFFFFFF;
|
||||
if (this.timer0Enabled) {
|
||||
numOverflows = ((numOverflows | 0) - 1) | 0;
|
||||
var countUntilReload = this.nextTimer0OverflowBase() | 0;
|
||||
var reloadClocks = (0x10000 - (this.timer0Reload | 0)) | 0;
|
||||
reloadClocks = Math.imul(reloadClocks | 0, this.timer0Prescalar | 0) | 0;
|
||||
reloadClocks = (reloadClocks | 0) * (numOverflows | 0);
|
||||
eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0;
|
||||
}
|
||||
return eventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer1OverflowBase = function () {
|
||||
var countUntilReload = (0x10000 - (this.timer1Counter | 0)) | 0;
|
||||
countUntilReload = Math.imul(countUntilReload | 0, this.timer1Prescalar | 0) | 0;
|
||||
countUntilReload = ((countUntilReload | 0) - (this.timer1Precounter | 0)) | 0;
|
||||
return countUntilReload | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer1Overflow = function (numOverflows) {
|
||||
numOverflows = numOverflows | 0;
|
||||
var eventTime = 0x7FFFFFFF;
|
||||
if (this.timer1Enabled) {
|
||||
var reloadClocks = (0x10000 - (this.timer1Reload | 0)) | 0;
|
||||
if (this.timer1CountUp) {
|
||||
var countUntilReload = (0x10000 - (this.timer1Counter | 0)) | 0;
|
||||
reloadClocks = (reloadClocks | 0) * (numOverflows | 0);
|
||||
eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0;
|
||||
eventTime = this.nextTimer0Overflow(eventTime | 0) | 0;
|
||||
}
|
||||
else {
|
||||
numOverflows = ((numOverflows | 0) - 1) | 0;
|
||||
var countUntilReload = this.nextTimer1OverflowBase() | 0;
|
||||
reloadClocks = Math.imul(reloadClocks | 0, this.timer1Prescalar | 0) | 0;
|
||||
reloadClocks = reloadClocks * numOverflows;
|
||||
eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0;
|
||||
}
|
||||
}
|
||||
return eventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer1OverflowSingle = function () {
|
||||
var eventTime = 0x7FFFFFFF;
|
||||
if (this.timer1Enabled) {
|
||||
if (this.timer1CountUp) {
|
||||
var countUntilReload = (0x10000 - (this.timer1Counter | 0)) | 0;
|
||||
eventTime = this.nextTimer0Overflow(countUntilReload | 0) | 0;
|
||||
}
|
||||
else {
|
||||
eventTime = this.nextTimer1OverflowBase() | 0;
|
||||
}
|
||||
}
|
||||
return eventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer2OverflowBase = function () {
|
||||
var countUntilReload = (0x10000 - (this.timer2Counter | 0)) | 0;
|
||||
countUntilReload = Math.imul(countUntilReload | 0, this.timer2Prescalar | 0) | 0;
|
||||
countUntilReload = ((countUntilReload | 0) - (this.timer2Precounter | 0)) | 0;
|
||||
return countUntilReload | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer2Overflow = function (numOverflows) {
|
||||
numOverflows = numOverflows | 0;
|
||||
var eventTime = 0x7FFFFFFF;
|
||||
if (this.timer2Enabled) {
|
||||
var reloadClocks = (0x10000 - (this.timer2Reload | 0)) | 0;
|
||||
if (this.timer2CountUp) {
|
||||
var countUntilReload = (0x10000 - (this.timer2Counter | 0)) | 0;
|
||||
reloadClocks = (reloadClocks | 0) * (numOverflows | 0);
|
||||
eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0;
|
||||
eventTime = this.nextTimer1Overflow(eventTime | 0) | 0;
|
||||
}
|
||||
else {
|
||||
numOverflows = ((numOverflows | 0) - 1) | 0;
|
||||
var countUntilReload = this.nextTimer2OverflowBase() | 0;
|
||||
reloadClocks = Math.imul(reloadClocks | 0, this.timer2Prescalar | 0) | 0;
|
||||
reloadClocks = reloadClocks * numOverflows;
|
||||
eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0;
|
||||
}
|
||||
}
|
||||
return eventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer2OverflowSingle = function () {
|
||||
var eventTime = 0x7FFFFFFF;
|
||||
if (this.timer2Enabled) {
|
||||
if (this.timer2CountUp) {
|
||||
var countUntilReload = (0x10000 - (this.timer2Counter | 0)) | 0;
|
||||
eventTime = this.nextTimer1Overflow(countUntilReload | 0) | 0;
|
||||
}
|
||||
else {
|
||||
eventTime = this.nextTimer2OverflowBase() | 0;
|
||||
}
|
||||
}
|
||||
return eventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer3OverflowSingle = function () {
|
||||
var eventTime = 0x7FFFFFFF;
|
||||
if (this.timer3Enabled) {
|
||||
if (this.timer3CountUp) {
|
||||
var countUntilReload = (0x10000 - (this.timer3Counter | 0)) | 0;
|
||||
eventTime = this.nextTimer2Overflow(countUntilReload | 0) | 0;
|
||||
}
|
||||
else {
|
||||
eventTime = (0x10000 - (this.timer3Counter | 0)) | 0;
|
||||
eventTime = Math.imul(eventTime | 0, this.timer3Prescalar | 0) | 0;
|
||||
eventTime = ((eventTime | 0) - (this.timer3Precounter | 0)) | 0;
|
||||
}
|
||||
}
|
||||
return eventTime | 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Math.imul not found, use the compatibility method:
|
||||
GameBoyAdvanceTimer.prototype.nextTimer0OverflowBase = function () {
|
||||
var countUntilReload = (0x10000 - (this.timer0Counter | 0)) | 0;
|
||||
countUntilReload = ((countUntilReload | 0) * (this.timer0Prescalar | 0)) | 0;
|
||||
countUntilReload = ((countUntilReload | 0) - (this.timer0Precounter | 0)) | 0;
|
||||
return countUntilReload | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer0OverflowSingle = function () {
|
||||
var eventTime = 0x7FFFFFFF;
|
||||
if (this.timer0Enabled) {
|
||||
eventTime = this.nextTimer0OverflowBase() | 0;
|
||||
}
|
||||
return eventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer0Overflow = function (numOverflows) {
|
||||
numOverflows = numOverflows | 0;
|
||||
var eventTime = 0x7FFFFFFF;
|
||||
if (this.timer0Enabled) {
|
||||
numOverflows = ((numOverflows | 0) - 1) | 0;
|
||||
var countUntilReload = this.nextTimer0OverflowBase() | 0;
|
||||
var reloadClocks = (0x10000 - (this.timer0Reload | 0)) | 0;
|
||||
reloadClocks = ((reloadClocks | 0) * (this.timer0Prescalar | 0)) | 0;
|
||||
reloadClocks = (reloadClocks | 0) * (numOverflows | 0);
|
||||
eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0;
|
||||
}
|
||||
return eventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer1OverflowBase = function () {
|
||||
var countUntilReload = (0x10000 - (this.timer1Counter | 0)) | 0;
|
||||
countUntilReload = ((countUntilReload | 0) * (this.timer1Prescalar | 0)) | 0;
|
||||
countUntilReload = ((countUntilReload | 0) - (this.timer1Precounter | 0)) | 0;
|
||||
return countUntilReload | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer1Overflow = function (numOverflows) {
|
||||
numOverflows = numOverflows | 0;
|
||||
var eventTime = 0x7FFFFFFF;
|
||||
if (this.timer1Enabled) {
|
||||
var reloadClocks = (0x10000 - (this.timer1Reload | 0)) | 0;
|
||||
if (this.timer1CountUp) {
|
||||
var countUntilReload = (0x10000 - (this.timer1Counter | 0)) | 0;
|
||||
reloadClocks = (reloadClocks | 0) * (numOverflows | 0);
|
||||
eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0;
|
||||
eventTime = this.nextTimer0Overflow(eventTime | 0) | 0;
|
||||
}
|
||||
else {
|
||||
numOverflows = ((numOverflows | 0) - 1) | 0;
|
||||
var countUntilReload = this.nextTimer1OverflowBase() | 0;
|
||||
reloadClocks = ((reloadClocks | 0) * (this.timer1Prescalar | 0)) | 0;
|
||||
reloadClocks = reloadClocks * numOverflows;
|
||||
eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0;
|
||||
}
|
||||
}
|
||||
return eventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer1OverflowSingle = function () {
|
||||
var eventTime = 0x7FFFFFFF;
|
||||
if (this.timer1Enabled) {
|
||||
if (this.timer1CountUp) {
|
||||
var countUntilReload = (0x10000 - (this.timer1Counter | 0)) | 0;
|
||||
eventTime = this.nextTimer0Overflow(countUntilReload | 0) | 0;
|
||||
}
|
||||
else {
|
||||
eventTime = this.nextTimer1OverflowBase() | 0;
|
||||
}
|
||||
}
|
||||
return eventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer2OverflowBase = function () {
|
||||
var countUntilReload = (0x10000 - (this.timer2Counter | 0)) | 0;
|
||||
countUntilReload = ((countUntilReload | 0) * (this.timer2Prescalar | 0)) | 0;
|
||||
countUntilReload = ((countUntilReload | 0) - (this.timer2Precounter | 0)) | 0;
|
||||
return countUntilReload | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer2Overflow = function (numOverflows) {
|
||||
numOverflows = numOverflows | 0;
|
||||
var eventTime = 0x7FFFFFFF;
|
||||
if (this.timer2Enabled) {
|
||||
var reloadClocks = (0x10000 - (this.timer2Reload | 0)) | 0;
|
||||
if (this.timer2CountUp) {
|
||||
var countUntilReload = (0x10000 - (this.timer2Counter | 0)) | 0;
|
||||
reloadClocks = (reloadClocks | 0) * (numOverflows | 0);
|
||||
eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0;
|
||||
eventTime = this.nextTimer1Overflow(eventTime | 0) | 0;
|
||||
}
|
||||
else {
|
||||
numOverflows = ((numOverflows | 0) - 1) | 0;
|
||||
var countUntilReload = this.nextTimer2OverflowBase() | 0;
|
||||
reloadClocks = ((reloadClocks | 0) * (this.timer2Prescalar | 0)) | 0;
|
||||
reloadClocks = reloadClocks * numOverflows;
|
||||
eventTime = Math.min((countUntilReload | 0) + reloadClocks, 0x7FFFFFFF) | 0;
|
||||
}
|
||||
}
|
||||
return eventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer2OverflowSingle = function () {
|
||||
var eventTime = 0x7FFFFFFF;
|
||||
if (this.timer2Enabled) {
|
||||
if (this.timer2CountUp) {
|
||||
var countUntilReload = (0x10000 - (this.timer2Counter | 0)) | 0;
|
||||
eventTime = this.nextTimer1Overflow(countUntilReload | 0) | 0;
|
||||
}
|
||||
else {
|
||||
eventTime = this.nextTimer2OverflowBase() | 0;
|
||||
}
|
||||
}
|
||||
return eventTime | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer3OverflowSingle = function () {
|
||||
var eventTime = 0x7FFFFFFF;
|
||||
if (this.timer3Enabled) {
|
||||
if (this.timer3CountUp) {
|
||||
var countUntilReload = (0x10000 - (this.timer3Counter | 0)) | 0;
|
||||
eventTime = this.nextTimer2Overflow(countUntilReload | 0) | 0;
|
||||
}
|
||||
else {
|
||||
eventTime = (0x10000 - (this.timer3Counter | 0)) | 0;
|
||||
eventTime = ((eventTime | 0) * (this.timer3Prescalar | 0)) | 0;
|
||||
eventTime = ((eventTime | 0) - (this.timer3Precounter | 0)) | 0;
|
||||
}
|
||||
}
|
||||
return eventTime | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextAudioTimerOverflow = function () {
|
||||
var timer0 = this.nextTimer0OverflowSingle() | 0;
|
||||
var timer1 = this.nextTimer1OverflowSingle() | 0;
|
||||
return Math.min(timer0 | 0, timer1 | 0) | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer0IRQEventTime = function () {
|
||||
var clocks = 0x7FFFFFFF;
|
||||
if (this.timer0Enabled && this.timer0IRQ) {
|
||||
clocks = this.nextTimer0OverflowSingle() | 0;
|
||||
}
|
||||
return clocks | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer1IRQEventTime = function () {
|
||||
var clocks = 0x7FFFFFFF;
|
||||
if (this.timer1Enabled && this.timer1IRQ) {
|
||||
clocks = this.nextTimer1OverflowSingle() | 0;
|
||||
}
|
||||
return clocks | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer2IRQEventTime = function () {
|
||||
var clocks = 0x7FFFFFFF;
|
||||
if (this.timer2Enabled && this.timer2IRQ) {
|
||||
clocks = this.nextTimer2OverflowSingle() | 0;
|
||||
}
|
||||
return clocks | 0;
|
||||
}
|
||||
GameBoyAdvanceTimer.prototype.nextTimer3IRQEventTime = function () {
|
||||
var clocks = 0x7FFFFFFF;
|
||||
if (this.timer3Enabled && this.timer3IRQ) {
|
||||
clocks = this.nextTimer3OverflowSingle() | 0;
|
||||
}
|
||||
return clocks | 0;
|
||||
}
|
||||
@@ -0,0 +1,484 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2015 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceWait(IOCore) {
|
||||
//Build references:
|
||||
this.IOCore = IOCore;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.initialize = function () {
|
||||
this.memory = this.IOCore.memory;
|
||||
this.cpu = this.IOCore.cpu;
|
||||
this.WRAMConfiguration = 0xD000020; //WRAM configuration control register current data.
|
||||
this.WRAMWaitState = 3; //External WRAM wait state.
|
||||
this.SRAMWaitState = 5; //SRAM wait state.
|
||||
this.WAITCNT0 = 0; //WAITCNT0 control register data.
|
||||
this.WAITCNT1 = 0; //WAITCNT1 control register data.
|
||||
this.POSTBOOT = 0; //POSTBOOT control register data.
|
||||
this.isRendering = 1; //Are we doing memory during screen draw?
|
||||
this.isOAMRendering = 1; //Are we doing memory during OAM draw?
|
||||
this.nonSequential = 0x10; //Non-sequential access bit-flag.
|
||||
this.buffer = 0; //Tracking of the size of the prebuffer cache.
|
||||
this.clocks = 0; //Tracking clocks for prebuffer cache.
|
||||
//Create the wait state address translation cache:
|
||||
this.waitStateClocks16 = getUint8Array(0x20);
|
||||
this.waitStateClocks32 = getUint8Array(0x20);
|
||||
//Wait State 0:
|
||||
this.setWaitState(0, 0);
|
||||
//Wait State 1:
|
||||
this.setWaitState(1, 0);
|
||||
//Wait State 2:
|
||||
this.setWaitState(2, 0);
|
||||
//Initialize out some dynamic references:
|
||||
this.getROMRead16 = this.getROMRead16NoPrefetch;
|
||||
this.getROMRead32 = this.getROMRead32NoPrefetch;
|
||||
this.CPUInternalCyclePrefetch = this.CPUInternalCycleNoPrefetch;
|
||||
this.CPUInternalSingleCyclePrefetch = this.CPUInternalSingleCycleNoPrefetch;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.getWaitStateFirstAccess = function (data) {
|
||||
//Get the first access timing:
|
||||
data = data | 0;
|
||||
data = data & 0x3;
|
||||
if ((data | 0) < 0x3) {
|
||||
data = (5 - (data | 0)) | 0;
|
||||
}
|
||||
else {
|
||||
data = 9;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.getWaitStateSecondAccess = function (region, data) {
|
||||
//Get the second access timing:
|
||||
region = region | 0;
|
||||
data = data | 0;
|
||||
if ((data & 0x4) == 0) {
|
||||
data = 0x2 << (region | 0);
|
||||
data = ((data | 0) + 1) | 0;
|
||||
}
|
||||
else {
|
||||
data = 0x2;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.setWaitState = function (region, data) {
|
||||
region = region | 0;
|
||||
data = data | 0;
|
||||
//Wait State First Access:
|
||||
var firstAccess = this.getWaitStateFirstAccess(data & 0x3) | 0;
|
||||
//Wait State Second Access:
|
||||
var secondAccess = this.getWaitStateSecondAccess(region | 0, data | 0) | 0;
|
||||
region = region << 1;
|
||||
//Computing First Access:
|
||||
//8-16 bit access:
|
||||
this.waitStateClocks16[0x18 | region] = firstAccess | 0;
|
||||
this.waitStateClocks16[0x19 | region] = firstAccess | 0;
|
||||
//32 bit access:
|
||||
var accessTime = ((firstAccess | 0) + (secondAccess | 0)) | 0;
|
||||
this.waitStateClocks32[0x18 | region] = accessTime | 0;
|
||||
this.waitStateClocks32[0x19 | region] = accessTime | 0;
|
||||
//Computing Second Access:
|
||||
//8-16 bit access:
|
||||
this.waitStateClocks16[0x8 | region] = secondAccess | 0;
|
||||
this.waitStateClocks16[0x9 | region] = secondAccess | 0;
|
||||
//32 bit access:
|
||||
this.waitStateClocks32[0x8 | region] = secondAccess << 1;
|
||||
this.waitStateClocks32[0x9 | region] = secondAccess << 1;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.writeWAITCNT8_0 = function (data) {
|
||||
data = data | 0;
|
||||
//Set SRAM Wait State:
|
||||
if ((data & 0x3) < 0x3) {
|
||||
this.SRAMWaitState = (5 - (data & 0x3)) | 0;
|
||||
}
|
||||
else {
|
||||
this.SRAMWaitState = 9;
|
||||
}
|
||||
data = data & 0xFF;
|
||||
//Set Wait State 0:
|
||||
this.setWaitState(0, data >> 2);
|
||||
//Set Wait State 1:
|
||||
this.setWaitState(1, data >> 5);
|
||||
this.WAITCNT0 = data | 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.readWAITCNT8_0 = function () {
|
||||
return this.WAITCNT0 | 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.writeWAITCNT8_1 = function (data) {
|
||||
data = data | 0;
|
||||
//Set Wait State 2:
|
||||
this.setWaitState(2, data & 0xFF);
|
||||
//Set Prefetch Mode:
|
||||
if ((data & 0x40) == 0) {
|
||||
//No Prefetch:
|
||||
this.resetPrebuffer();
|
||||
this.getROMRead16 = this.getROMRead16NoPrefetch;
|
||||
this.getROMRead32 = this.getROMRead32NoPrefetch;
|
||||
this.CPUInternalCyclePrefetch = this.CPUInternalCycleNoPrefetch;
|
||||
this.CPUInternalSingleCyclePrefetch = this.CPUInternalSingleCycleNoPrefetch;
|
||||
}
|
||||
else {
|
||||
//Prefetch Enabled:
|
||||
this.getROMRead16 = this.getROMRead16Prefetch;
|
||||
this.getROMRead32 = this.getROMRead32Prefetch;
|
||||
this.CPUInternalCyclePrefetch = this.multiClock;
|
||||
this.CPUInternalSingleCyclePrefetch = this.singleClock;
|
||||
}
|
||||
this.WAITCNT1 = data & 0x5F;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.readWAITCNT8_1 = function () {
|
||||
return this.WAITCNT1 | 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.writeWAITCNT16 = function (data) {
|
||||
this.writeWAITCNT8_0(data | 0);
|
||||
this.writeWAITCNT8_1(data >> 8);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.readWAITCNT16 = function () {
|
||||
var data = this.WAITCNT0 | 0;
|
||||
data = data | (this.WAITCNT1 << 8);
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.writePOSTBOOT = function (data) {
|
||||
this.POSTBOOT = data & 0xFF;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.readPOSTBOOT = function () {
|
||||
return this.POSTBOOT | 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.writeHALTCNT = function (data) {
|
||||
data = data | 0;
|
||||
this.IOCore.updateCoreSpillRetain();
|
||||
//HALT/STOP mode entrance:
|
||||
if ((data & 0x80) == 0) {
|
||||
//Halt:
|
||||
this.IOCore.flagHalt();
|
||||
}
|
||||
else {
|
||||
//Stop:
|
||||
this.IOCore.flagStop();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.writeHALT16 = function (data) {
|
||||
data = data | 0;
|
||||
this.POSTBOOT = data & 0xFF;
|
||||
this.IOCore.updateCoreSpillRetain();
|
||||
//HALT/STOP mode entrance:
|
||||
if ((data & 0x8000) == 0) {
|
||||
//Halt:
|
||||
this.IOCore.flagHalt();
|
||||
}
|
||||
else {
|
||||
//Stop:
|
||||
this.IOCore.flagStop();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.writeConfigureWRAM8 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
switch (address & 0x3) {
|
||||
case 0:
|
||||
this.memory.remapWRAM(data & 0x21);
|
||||
this.WRAMConfiguration = (this.WRAMConfiguration & 0xFFFFFF00) | (data & 0xFF);
|
||||
break;
|
||||
case 1:
|
||||
this.WRAMConfiguration = (this.WRAMConfiguration & 0xFFFF00FF) | ((data & 0xFF) << 8);
|
||||
break;
|
||||
case 2:
|
||||
this.WRAMConfiguration = (this.WRAMConfiguration & 0xFF00FFFF) | ((data & 0xFF) << 16);
|
||||
break;
|
||||
default:
|
||||
this.WRAMWaitState = (0x10 - (data & 0xF)) | 0;
|
||||
this.WRAMConfiguration = (this.WRAMConfiguration & 0xFFFFFF) | (data << 24);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.writeConfigureWRAM16 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
if ((address & 0x2) == 0) {
|
||||
this.WRAMConfiguration = (this.WRAMConfiguration & 0xFFFF0000) | (data & 0xFFFF);
|
||||
this.memory.remapWRAM(data & 0x21);
|
||||
}
|
||||
else {
|
||||
this.WRAMConfiguration = (data << 16) | (this.WRAMConfiguration & 0xFFFF);
|
||||
this.WRAMWaitState = (0x10 - ((data >> 8) & 0xF)) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.writeConfigureWRAM32 = function (data) {
|
||||
data = data | 0;
|
||||
this.WRAMConfiguration = data | 0;
|
||||
this.WRAMWaitState = (0x10 - ((data >> 24) & 0xF)) | 0;
|
||||
this.memory.remapWRAM(data & 0x21);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.readConfigureWRAM8 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
switch (address & 0x3) {
|
||||
case 0:
|
||||
data = this.WRAMConfiguration & 0x2F;
|
||||
break;
|
||||
case 3:
|
||||
data = this.WRAMConfiguration >>> 24;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.readConfigureWRAM16 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((address & 0x2) == 0) {
|
||||
data = this.WRAMConfiguration & 0x2F;
|
||||
}
|
||||
else {
|
||||
data = (this.WRAMConfiguration >> 16) & 0xFF00;
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.readConfigureWRAM32 = function () {
|
||||
return this.WRAMConfiguration & 0xFF00002F;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.CPUInternalCycleNoPrefetch = function (clocks) {
|
||||
clocks = clocks | 0;
|
||||
//Clock for idle CPU time:
|
||||
this.IOCore.updateCore(clocks | 0);
|
||||
//Prebuffer bug:
|
||||
this.checkPrebufferBug();
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.CPUInternalSingleCycleNoPrefetch = function () {
|
||||
//Clock for idle CPU time:
|
||||
this.IOCore.updateCoreSingle();
|
||||
//Not enough time for prebuffer buffering, so skip it.
|
||||
//Prebuffer bug:
|
||||
this.checkPrebufferBug();
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.checkPrebufferBug = function () {
|
||||
//Issue a non-sequential cycle for the next read if we did an I-cycle:
|
||||
var address = this.cpu.registers[15] | 0;
|
||||
if ((address | 0) >= 0x8000000 && (address | 0) < 0xE000000) {
|
||||
this.NonSequentialBroadcast();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.NonSequentialBroadcast = function () {
|
||||
//Flag as N cycle:
|
||||
this.nonSequential = 0x10;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.NonSequentialBroadcastClear = function () {
|
||||
//PC branched:
|
||||
this.NonSequentialBroadcast();
|
||||
this.resetPrebuffer();
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.check128kAlignmentBug = function (address) {
|
||||
address = address | 0;
|
||||
if ((address & 0x1FFFF) == 0) {
|
||||
this.NonSequentialBroadcast();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.multiClock = function (clocks) {
|
||||
clocks = clocks | 0;
|
||||
this.IOCore.updateCore(clocks | 0);
|
||||
var address = this.cpu.registers[15] | 0;
|
||||
if ((address | 0) >= 0x8000000 && (address | 0) < 0xE000000) {
|
||||
if ((this.clocks | 0) < 0xFF) {
|
||||
this.clocks = ((this.clocks | 0) + (clocks | 0)) | 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.resetPrebuffer();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.singleClock = function () {
|
||||
this.IOCore.updateCoreSingle();
|
||||
var address = this.cpu.registers[15] | 0;
|
||||
if ((address | 0) >= 0x8000000 && (address | 0) < 0xE000000) {
|
||||
if ((this.clocks | 0) < 0xFF) {
|
||||
this.clocks = ((this.clocks | 0) + 1) | 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.resetPrebuffer();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.addPrebufferSingleClock = function () {
|
||||
this.clocks = ((this.clocks | 0) + 1) | 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.decrementBufferSingle = function () {
|
||||
this.buffer = ((this.buffer | 0) - 1) | 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.decrementBufferDouble = function () {
|
||||
this.buffer = ((this.buffer | 0) - 2) | 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.resetPrebuffer = function () {
|
||||
//Reset the buffering:
|
||||
this.clocks = 0;
|
||||
this.buffer = 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.drainOverdueClocks = function () {
|
||||
if ((this.clocks | 0) > 0 && (this.buffer | 0) < 8) {
|
||||
var address = this.cpu.registers[15] >>> 24;
|
||||
//Convert built up clocks to 16 bit word buffer units:
|
||||
do {
|
||||
this.clocks = ((this.clocks | 0) - (this.waitStateClocks16[address | 0] | 0)) | 0;
|
||||
this.buffer = ((this.buffer | 0) + 1) | 0;
|
||||
} while ((this.clocks | 0) > 0 && (this.buffer | 0) < 8);
|
||||
//If we're deficient in clocks, fit them in before the access:
|
||||
if ((this.clocks | 0) < 0) {
|
||||
this.IOCore.updateCoreNegative(this.clocks | 0);
|
||||
this.clocks = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.computeClocks = function (address) {
|
||||
address = address | 0;
|
||||
//Convert built up clocks to 16 bit word buffer units:
|
||||
while ((this.buffer | 0) < 8 && (this.clocks | 0) >= (this.waitStateClocks16[address | 0] | 0)) {
|
||||
this.clocks = ((this.clocks | 0) - (this.waitStateClocks16[address | 0] | 0)) | 0;
|
||||
this.buffer = ((this.buffer | 0) + 1) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.drainOverdueClocksCPU = function () {
|
||||
if ((this.clocks | 0) < 0) {
|
||||
//Compute "overdue" clocks:
|
||||
this.IOCore.updateCoreNegative(this.clocks | 0);
|
||||
this.clocks = 0;
|
||||
}
|
||||
else {
|
||||
//Buffer satiated, clock 1:
|
||||
this.IOCore.updateCoreSingle();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.doGamePakFetch16 = function (address) {
|
||||
address = address | 0;
|
||||
//Fetch 16 bit word into buffer:
|
||||
this.clocks = ((this.clocks | 0) - (this.waitStateClocks16[address | this.nonSequential] | 0)) | 0;
|
||||
this.nonSequential = 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.doGamePakFetch32 = function (address) {
|
||||
address = address | 0;
|
||||
//Fetch 16 bit word into buffer:
|
||||
this.clocks = ((this.clocks | 0) - (this.waitStateClocks32[address | this.nonSequential] | 0)) | 0;
|
||||
this.nonSequential = 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.getROMRead16Prefetch = function (address) {
|
||||
//Caching enabled:
|
||||
address = address | 0;
|
||||
//Resolve clocks to buffer units:
|
||||
this.computeClocks(address | 0);
|
||||
//Need 16 bits minimum buffered:
|
||||
switch (this.buffer | 0) {
|
||||
case 0:
|
||||
//Fetch 16 bit word into buffer:
|
||||
this.doGamePakFetch16(address | 0);
|
||||
break;
|
||||
default:
|
||||
//Instruction fetch is 1 clock wide minimum:
|
||||
this.addPrebufferSingleClock();
|
||||
//Decrement the buffer:
|
||||
this.decrementBufferSingle();
|
||||
}
|
||||
//Clock the state:
|
||||
this.drainOverdueClocksCPU();
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.getROMRead16NoPrefetch = function (address) {
|
||||
//Caching disabled:
|
||||
address = address | 0;
|
||||
this.IOCore.updateCore(this.waitStateClocks16[address | this.nonSequential] | 0);
|
||||
this.nonSequential = 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.getROMRead32Prefetch = function (address) {
|
||||
//Caching enabled:
|
||||
address = address | 0;
|
||||
//Resolve clocks to buffer units:
|
||||
this.computeClocks(address | 0);
|
||||
//Need 32 bits minimum buffered:
|
||||
switch (this.buffer | 0) {
|
||||
case 0:
|
||||
//Fetch two 16 bit words into buffer:
|
||||
this.doGamePakFetch32(address | 0);
|
||||
break;
|
||||
case 1:
|
||||
//Fetch a 16 bit word into buffer:
|
||||
this.doGamePakFetch16(address | 0);
|
||||
this.buffer = 0;
|
||||
break;
|
||||
default:
|
||||
//Instruction fetch is 1 clock wide minimum:
|
||||
this.addPrebufferSingleClock();
|
||||
//Decrement the buffer:
|
||||
this.decrementBufferDouble();
|
||||
}
|
||||
//Clock the state:
|
||||
this.drainOverdueClocksCPU();
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.getROMRead32NoPrefetch = function (address) {
|
||||
//Caching disabled:
|
||||
address = address | 0;
|
||||
this.IOCore.updateCore(this.waitStateClocks32[address | this.nonSequential] | 0);
|
||||
this.nonSequential = 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.WRAMAccess = function () {
|
||||
this.multiClock(this.WRAMWaitState | 0);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.WRAMAccess16CPU = function () {
|
||||
this.IOCore.updateCore(this.WRAMWaitState | 0);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.WRAMAccess32 = function () {
|
||||
this.multiClock(this.WRAMWaitState << 1);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.WRAMAccess32CPU = function () {
|
||||
this.IOCore.updateCore(this.WRAMWaitState << 1);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.ROMAccess = function (address) {
|
||||
address = address | 0;
|
||||
this.drainOverdueClocks();
|
||||
this.check128kAlignmentBug(address | 0);
|
||||
this.IOCore.updateCore(this.waitStateClocks16[(address >> 24) | this.nonSequential] | 0);
|
||||
this.nonSequential = 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.ROMAccess16CPU = function (address) {
|
||||
address = address | 0;
|
||||
this.check128kAlignmentBug(address | 0);
|
||||
this.getROMRead16(address >> 24);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.ROMAccess32 = function (address) {
|
||||
address = address | 0;
|
||||
this.drainOverdueClocks();
|
||||
this.check128kAlignmentBug(address | 0);
|
||||
this.IOCore.updateCore(this.waitStateClocks32[(address >> 24) | this.nonSequential] | 0);
|
||||
this.nonSequential = 0;
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.ROMAccess32CPU = function (address) {
|
||||
address = address | 0;
|
||||
this.check128kAlignmentBug(address | 0);
|
||||
this.getROMRead32(address >> 24);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.SRAMAccess = function () {
|
||||
this.multiClock(this.SRAMWaitState | 0);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.SRAMAccessCPU = function () {
|
||||
this.resetPrebuffer();
|
||||
this.IOCore.updateCore(this.SRAMWaitState | 0);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.VRAMAccess = function () {
|
||||
this.multiClock(this.isRendering | 0);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.VRAMAccess16CPU = function () {
|
||||
this.IOCore.updateCore(this.isRendering | 0);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.VRAMAccess32 = function () {
|
||||
this.multiClock(this.isRendering << 1);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.VRAMAccess32CPU = function () {
|
||||
this.IOCore.updateCore(this.isRendering << 1);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.OAMAccess = function () {
|
||||
this.multiClock(this.isOAMRendering | 0);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.OAMAccessCPU = function () {
|
||||
this.IOCore.updateCore(this.isOAMRendering | 0);
|
||||
}
|
||||
GameBoyAdvanceWait.prototype.updateRenderStatus = function (isRendering, isOAMRendering) {
|
||||
this.isRendering = isRendering | 0;
|
||||
this.isOAMRendering = isOAMRendering | 0;
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2019 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
importScripts("../includes/TypedArrayShim.js.txt");
|
||||
importScripts("Cartridge.js.txt");
|
||||
importScripts("DMA.js.txt");
|
||||
importScripts("Emulator.js.txt");
|
||||
importScripts("Graphics.js.txt");
|
||||
importScripts("RunLoop.js.txt");
|
||||
importScripts("Memory.js.txt");
|
||||
importScripts("IRQ.js.txt");
|
||||
importScripts("JoyPad.js.txt");
|
||||
importScripts("Serial.js.txt");
|
||||
importScripts("Sound.js.txt");
|
||||
importScripts("Timer.js.txt");
|
||||
importScripts("Wait.js.txt");
|
||||
importScripts("CPU.js.txt");
|
||||
importScripts("Saves.js.txt");
|
||||
importScripts("sound/FIFO.js.txt");
|
||||
importScripts("sound/Channel1.js.txt");
|
||||
importScripts("sound/Channel2.js.txt");
|
||||
importScripts("sound/Channel3.js.txt");
|
||||
importScripts("sound/Channel4.js.txt");
|
||||
importScripts("CPU/ARM.js.txt");
|
||||
importScripts("CPU/THUMB.js.txt");
|
||||
importScripts("CPU/CPSR.js.txt");
|
||||
importScripts("graphics/RendererProxy.js.txt");
|
||||
importScripts("graphics/RendererShim.js.txt");
|
||||
importScripts("graphics/Renderer.js.txt");
|
||||
importScripts("graphics/BGTEXT.js.txt");
|
||||
importScripts("graphics/BG2FrameBuffer.js.txt");
|
||||
importScripts("graphics/BGMatrix.js.txt");
|
||||
importScripts("graphics/AffineBG.js.txt");
|
||||
importScripts("graphics/ColorEffects.js.txt");
|
||||
importScripts("graphics/Mosaic.js.txt");
|
||||
importScripts("graphics/OBJ.js.txt");
|
||||
importScripts("graphics/OBJWindow.js.txt");
|
||||
importScripts("graphics/Window.js.txt");
|
||||
importScripts("graphics/Compositor.js.txt");
|
||||
importScripts("memory/DMA0.js.txt");
|
||||
importScripts("memory/DMA1.js.txt");
|
||||
importScripts("memory/DMA2.js.txt");
|
||||
importScripts("memory/DMA3.js.txt");
|
||||
importScripts("cartridge/SaveDeterminer.js.txt");
|
||||
importScripts("cartridge/SRAM.js.txt");
|
||||
importScripts("cartridge/FLASH.js.txt");
|
||||
importScripts("cartridge/EEPROM.js.txt");
|
||||
importScripts("cartridge/GPIO.js.txt");
|
||||
var Iodine = new GameBoyAdvanceEmulator();
|
||||
//Save callbacks waiting to be satisfied:
|
||||
var saveImportPool = [];
|
||||
//Graphics Buffers:
|
||||
var gfxBuffers = [getSharedUint8Array(160 * 240 * 3),
|
||||
getSharedUint8Array(160 * 240 * 3)];
|
||||
var gfxCounters = getSharedInt32Array(3);
|
||||
//Audio Buffers:
|
||||
var audioBuffer = null;
|
||||
var audioCounters = null;
|
||||
var audioBufferSize = 0;
|
||||
var audioBufferSizeMask = 0;
|
||||
var audioSamplesRemaining = getSharedInt32Array(1);
|
||||
//Time Stamp tracking:
|
||||
var timestamp = getSharedUint32Array(1);
|
||||
//Interval Timer handle:
|
||||
var timerHandle = null;
|
||||
var timerRate = 0;
|
||||
//Pass the shared array buffers:
|
||||
try {
|
||||
postMessage({messageID:0, gfxBuffer1:gfxBuffers[0], gfxBuffer2:gfxBuffers[1], gfxCounters:gfxCounters, audioSamplesRemaining:audioSamplesRemaining, timestamp:timestamp}, [gfxBuffers[0].buffer, gfxBuffers[1].buffer, gfxCounters.buffer, audioSamplesRemaining.buffer, timestamp.buffer]);
|
||||
}
|
||||
catch (e) {
|
||||
postMessage({messageID:0, gfxBuffer1:gfxBuffers[0], gfxBuffer2:gfxBuffers[1], gfxCounters:gfxCounters, audioSamplesRemaining:audioSamplesRemaining, timestamp:timestamp});
|
||||
}
|
||||
//Event decoding:
|
||||
self.onmessage = function (event) {
|
||||
var data = event.data;
|
||||
switch (data.messageID | 0) {
|
||||
case 0:
|
||||
Iodine.play();
|
||||
break;
|
||||
case 1:
|
||||
Iodine.pause();
|
||||
break;
|
||||
case 2:
|
||||
Iodine.restart();
|
||||
break;
|
||||
case 3:
|
||||
Iodine.setIntervalRate(+data.payload);
|
||||
changeTimer(data.payload | 0);
|
||||
break;
|
||||
case 4:
|
||||
Iodine.attachGraphicsFrameHandler(graphicsFrameHandler);
|
||||
break;
|
||||
case 5:
|
||||
Iodine.attachAudioHandler(audioHandler);
|
||||
break;
|
||||
case 6:
|
||||
Iodine.enableAudio();
|
||||
break;
|
||||
case 7:
|
||||
Iodine.disableAudio();
|
||||
break;
|
||||
case 8:
|
||||
Iodine.toggleSkipBootROM(!!data.payload);
|
||||
break;
|
||||
case 9:
|
||||
Iodine.toggleDynamicSpeed(!!data.payload);
|
||||
break;
|
||||
case 10:
|
||||
Iodine.attachSpeedHandler(speedHandler);
|
||||
break;
|
||||
case 11:
|
||||
Iodine.keyDown(data.payload | 0);
|
||||
break;
|
||||
case 12:
|
||||
Iodine.keyUp(data.payload | 0);
|
||||
break;
|
||||
case 13:
|
||||
Iodine.incrementSpeed(+data.payload);
|
||||
break;
|
||||
case 14:
|
||||
Iodine.setSpeed(+data.payload);
|
||||
break;
|
||||
case 15:
|
||||
Iodine.attachBIOS(data.payload);
|
||||
break;
|
||||
case 16:
|
||||
Iodine.attachROM(data.payload);
|
||||
break;
|
||||
case 17:
|
||||
Iodine.exportSave();
|
||||
break;
|
||||
case 18:
|
||||
Iodine.attachSaveExportHandler(saveExportHandler);
|
||||
break;
|
||||
case 19:
|
||||
Iodine.attachSaveImportHandler(saveImportHandler);
|
||||
break;
|
||||
case 20:
|
||||
processSaveImportSuccess(data.payload);
|
||||
break;
|
||||
case 21:
|
||||
processSaveImportFail();
|
||||
break;
|
||||
case 22:
|
||||
Iodine.toggleOffthreadGraphics(!!data.payload);
|
||||
break;
|
||||
case 23:
|
||||
Iodine.attachPlayStatusHandler(playStatusHandler);
|
||||
}
|
||||
}
|
||||
var graphicsFrameHandler = {
|
||||
//Function only called if graphics is THIS thread:
|
||||
copyBuffer:function (swizzledFrame) {
|
||||
//Push a frame of graphics to the blitter handle:
|
||||
//Load the counter values:
|
||||
var start = gfxCounters[0] | 0; //Written by the other thread.
|
||||
var end = gfxCounters[1] | 0; //Written by this thread.
|
||||
//Check if buffer is full:
|
||||
if ((end | 0) == (((start | 0) + 2) | 0)) {
|
||||
//Skip copying a frame out:
|
||||
return;
|
||||
}
|
||||
//Copy samples into the ring buffer:
|
||||
//Hardcoded for 2 buffers for a triple buffer effect:
|
||||
gfxBuffers[end & 0x1].set(swizzledFrame);
|
||||
//Increment the ending position counter by 1:
|
||||
//Atomic to commit the counter to memory:
|
||||
Atomics.store(gfxCounters, 1, ((end | 0) + 1) | 0);
|
||||
}
|
||||
};
|
||||
//Shim for our audio api:
|
||||
var audioHandler = {
|
||||
initialize:function (channels, sampleRate, bufferLimit, call1, call2, call3) {
|
||||
//Initialize the audio mixer input:
|
||||
channels = channels | 0;
|
||||
sampleRate = +sampleRate;
|
||||
bufferLimit = bufferLimit | 0;
|
||||
//Generate an audio buffer:
|
||||
audioBufferSize = ((bufferLimit | 0) * (channels | 0)) | 0;
|
||||
audioBufferSizeMask = 1;
|
||||
while ((audioBufferSize | 0) >= (audioBufferSizeMask | 0)) {
|
||||
audioBufferSizeMask = (audioBufferSizeMask << 1) | 1;
|
||||
}
|
||||
audioBufferSize = ((audioBufferSizeMask | 0) + 1) | 0;
|
||||
audioBuffer = getSharedFloat32Array(audioBufferSize | 0);
|
||||
audioCounters = getSharedInt32Array(2);
|
||||
try {
|
||||
postMessage({messageID:1, channels:channels | 0, sampleRate:+sampleRate, bufferLimit:bufferLimit | 0, audioBuffer:audioBuffer, audioCounters:audioCounters}, [audioBuffer.buffer, audioCounters.buffer]);
|
||||
}
|
||||
catch (e) {
|
||||
postMessage({messageID:1, channels:channels | 0, sampleRate:+sampleRate, bufferLimit:bufferLimit | 0, audioBuffer:audioBuffer, audioCounters:audioCounters});
|
||||
}
|
||||
},
|
||||
push:function (buffer, startPos, endPos) {
|
||||
startPos = startPos | 0;
|
||||
endPos = endPos | 0;
|
||||
//Push audio to the audio mixer input handle:
|
||||
//Load the counter values:
|
||||
var start = audioCounters[0] | 0; //Written to by the other thread.
|
||||
var end = audioCounters[1] | 0; //Written by this thread.
|
||||
var endCorrected = ((end | 0) & (audioBufferSizeMask | 0)) | 0;
|
||||
var freeBufferSpace = ((end | 0) - (start | 0)) | 0;
|
||||
freeBufferSpace = ((audioBufferSize | 0) - (freeBufferSpace | 0)) | 0;
|
||||
var amountToSend = ((endPos | 0) - (startPos | 0)) | 0;
|
||||
amountToSend = Math.min(amountToSend | 0, freeBufferSpace | 0) | 0;
|
||||
endPos = ((startPos | 0) + (amountToSend | 0)) | 0;
|
||||
//Push audio into buffer:
|
||||
for (; (startPos | 0) < (endPos | 0); startPos = ((startPos | 0) + 1) | 0) {
|
||||
audioBuffer[endCorrected | 0] = +buffer[startPos | 0];
|
||||
endCorrected = ((endCorrected | 0) + 1) | 0;
|
||||
if ((endCorrected | 0) == (audioBufferSize | 0)) {
|
||||
endCorrected = 0;
|
||||
}
|
||||
}
|
||||
//Update the cross thread buffering count:
|
||||
end = ((end | 0) + (amountToSend | 0)) | 0;
|
||||
//Atomic store to commit writes to memory:
|
||||
Atomics.store(audioCounters, 1, end | 0);
|
||||
},
|
||||
register:function () {
|
||||
//Register into the audio mixer:
|
||||
postMessage({messageID:2});
|
||||
},
|
||||
unregister:function () {
|
||||
//Unregister from audio mixer:
|
||||
postMessage({messageID:3});
|
||||
},
|
||||
setBufferSpace:function (spaceContain) {
|
||||
//Ensure buffering minimum levels for the audio:
|
||||
postMessage({messageID:4, audioBufferContainAmount:spaceContain | 0});
|
||||
},
|
||||
remainingBuffer:function () {
|
||||
//Report the amount of audio samples in-flight:
|
||||
var ringBufferCount = this.remainingBufferShared() | 0;
|
||||
var audioSysCount = audioSamplesRemaining[0] | 0;
|
||||
return ((ringBufferCount | 0) + (audioSysCount | 0)) | 0;
|
||||
},
|
||||
remainingBufferShared:function () {
|
||||
//Reported the sample count left in the shared buffer:
|
||||
var start = audioCounters[0] | 0;
|
||||
var end = audioCounters[1] | 0;
|
||||
var ringBufferCount = ((end | 0) - (start | 0)) | 0;
|
||||
return ringBufferCount | 0;
|
||||
}
|
||||
};
|
||||
function saveImportHandler(saveID, saveCallback, noSaveCallback) {
|
||||
postMessage({messageID:5, saveID:saveID});
|
||||
saveImportPool.push([saveCallback, noSaveCallback]);
|
||||
}
|
||||
function saveExportHandler(saveID, saveData) {
|
||||
postMessage({messageID:6, saveID:saveID, saveData:saveData});
|
||||
}
|
||||
function speedHandler(speed) {
|
||||
postMessage({messageID:7, speed:speed});
|
||||
}
|
||||
function processSaveImportSuccess(saveData) {
|
||||
saveImportPool.shift()[0](saveData);
|
||||
}
|
||||
function processSaveImportFail() {
|
||||
saveImportPool.shift()[1]();
|
||||
}
|
||||
function playStatusHandler(isPlaying) {
|
||||
isPlaying = isPlaying | 0;
|
||||
postMessage({messageID:8, playing:(isPlaying | 0)});
|
||||
if ((isPlaying | 0) == 0) {
|
||||
if (timerHandle) {
|
||||
clearInterval(timerHandle);
|
||||
timerHandle = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!timerHandle) {
|
||||
initTimer(timerRate | 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
function changeTimer(rate) {
|
||||
rate = rate | 0;
|
||||
if (timerHandle) {
|
||||
clearInterval(timerHandle);
|
||||
initTimer(rate | 0);
|
||||
}
|
||||
timerRate = rate | 0;
|
||||
}
|
||||
function initTimer(rate) {
|
||||
rate = rate | 0;
|
||||
if ((rate | 0) > 0) {
|
||||
timerHandle = setInterval(function() {
|
||||
Iodine.timerCallback(timestamp[0] >>> 0);
|
||||
}, rate | 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2015 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceEEPROMChip(IOCore) {
|
||||
this.saves = null;
|
||||
this.largestSizePossible = 0x200;
|
||||
this.mode = 0;
|
||||
this.bitsProcessed = 0;
|
||||
this.address = 0;
|
||||
this.buffer = getUint8Array(8);
|
||||
this.IOCore = IOCore;
|
||||
//Special note to emulator authors: EEPROM command ending bit "0" can also be a "1"...
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.initialize = function () {
|
||||
this.allocate();
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.allocate = function () {
|
||||
if (this.saves == null || (this.saves.length | 0) < (this.largestSizePossible | 0)) {
|
||||
//Allocate the new array:
|
||||
var newSave = getUint8Array(this.largestSizePossible | 0);
|
||||
//Init to default value:
|
||||
for (var index = 0; (index | 0) < (this.largestSizePossible | 0); index = ((index | 0) + 1) | 0) {
|
||||
newSave[index | 0] = 0xFF;
|
||||
}
|
||||
//Copy the old save data out:
|
||||
if (this.saves != null) {
|
||||
for (var index = 0; (index | 0) < (this.saves.length | 0); index = ((index | 0) + 1) | 0) {
|
||||
newSave[index | 0] = this.saves[index | 0] | 0;
|
||||
}
|
||||
}
|
||||
//Assign the new array out:
|
||||
this.saves = newSave;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.load = function (save) {
|
||||
if ((save.length | 0) == 0x200 || (save.length | 0) == 0x2000) {
|
||||
this.saves = save;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.read8 = function () {
|
||||
//Can't do real reading with 8-bit reads:
|
||||
return 0x1;
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.read16 = function () {
|
||||
var data = 1;
|
||||
switch (this.mode | 0) {
|
||||
case 0x7:
|
||||
//Return 4 junk 0 bits:
|
||||
data = 0;
|
||||
if ((this.bitsProcessed | 0) < 3) {
|
||||
//Increment our bits counter:
|
||||
this.bitsProcessed = ((this.bitsProcessed | 0) + 1) | 0;
|
||||
}
|
||||
else {
|
||||
//Reset our bits counter:
|
||||
this.bitsProcessed = 0;
|
||||
//Change mode for the actual reads:
|
||||
this.mode = 8;
|
||||
}
|
||||
break;
|
||||
case 0x8:
|
||||
//Return actual serial style data:
|
||||
var address = ((this.bitsProcessed >> 3) + (this.address | 0)) | 0;
|
||||
data = (this.saves[address | 0] >> ((0x7 - (this.bitsProcessed & 0x7)) | 0)) & 0x1;
|
||||
//Check for end of read:
|
||||
if ((this.bitsProcessed | 0) < 0x3F) {
|
||||
//Increment our bits counter:
|
||||
this.bitsProcessed = ((this.bitsProcessed | 0) + 1) | 0;
|
||||
}
|
||||
else {
|
||||
//Finished read and now idle:
|
||||
this.resetMode();
|
||||
}
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.read32 = function () {
|
||||
//Can't do real reading with 32-bit reads:
|
||||
return 0x10001;
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.write16 = function (data) {
|
||||
data = data | 0;
|
||||
data = data & 0x1;
|
||||
//Writes only work in DMA:
|
||||
switch (this.mode | 0) {
|
||||
//Idle Mode:
|
||||
case 0:
|
||||
this.mode = data | 0;
|
||||
break;
|
||||
//Select Mode:
|
||||
case 0x1:
|
||||
this.selectMode(data | 0);
|
||||
break;
|
||||
//Address Mode (Write):
|
||||
case 0x2:
|
||||
//Address Mode (Read):
|
||||
case 0x3:
|
||||
this.addressMode(data | 0);
|
||||
break;
|
||||
//Write Mode:
|
||||
case 0x4:
|
||||
this.writeMode(data | 0);
|
||||
break;
|
||||
//Ending bit of addressing:
|
||||
case 0x5:
|
||||
case 0x6:
|
||||
this.endAddressing();
|
||||
break;
|
||||
//Read Mode:
|
||||
default:
|
||||
this.resetMode();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.selectMode = function (data) {
|
||||
data = data | 0;
|
||||
//Reset our address:
|
||||
this.address = 0;
|
||||
//Reset our bits counter:
|
||||
this.bitsProcessed = 0;
|
||||
//Read the mode bit:
|
||||
this.mode = 0x2 | data;
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.addressMode = function (data) {
|
||||
data = data | 0;
|
||||
//Shift in our address bit:
|
||||
this.address = (this.address << 1) | data;
|
||||
//Increment our bits counter:
|
||||
this.bitsProcessed = ((this.bitsProcessed | 0) + 1) | 0;
|
||||
//Check for how many bits we've shifted in:
|
||||
switch (this.bitsProcessed | 0) {
|
||||
//6 bit address mode:
|
||||
case 0x6:
|
||||
if ((this.IOCore.dmaChannel3.wordCountShadow | 0) >= (((this.mode | 0) == 2) ? 0x4A : 0xA)) {
|
||||
this.largestSizePossible = 0x2000;
|
||||
this.allocate();
|
||||
break;
|
||||
}
|
||||
//14 bit address mode:
|
||||
case 0xE:
|
||||
this.changeModeToActive();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.changeModeToActive = function () {
|
||||
//Ensure the address range:
|
||||
this.address &= 0x3FF;
|
||||
//Addressing in units of 8 bytes:
|
||||
this.address <<= 3;
|
||||
//Reset our bits counter:
|
||||
this.bitsProcessed = 0;
|
||||
//Change to R/W Mode:
|
||||
this.mode = ((this.mode | 0) + 2) | 0;
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.writeMode = function (data) {
|
||||
data = data | 0;
|
||||
//Push a bit into the buffer:
|
||||
this.pushBuffer(data | 0);
|
||||
//Save on last write bit push:
|
||||
if ((this.bitsProcessed | 0) == 0x40) {
|
||||
//64 bits buffered, so copy our buffer to the save data:
|
||||
this.copyBuffer();
|
||||
this.mode = 6;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.pushBuffer = function (data) {
|
||||
data = data | 0;
|
||||
//Push a bit through our serial buffer:
|
||||
var bufferPosition = this.bitsProcessed >> 3;
|
||||
this.buffer[bufferPosition & 0x7] = ((this.buffer[bufferPosition & 0x7] << 1) & 0xFE) | data;
|
||||
this.bitsProcessed = ((this.bitsProcessed | 0) + 1) | 0;
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.copyBuffer = function () {
|
||||
//Copy 8 bytes from buffer to EEPROM save data starting at address offset:
|
||||
for (var index = 0; (index | 0) < 8; index = ((index | 0) + 1) | 0) {
|
||||
this.saves[this.address | index] = this.buffer[index & 0x7] & 0xFF;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.endAddressing = function () {
|
||||
this.mode = ((this.mode | 0) + 2) | 0;
|
||||
}
|
||||
GameBoyAdvanceEEPROMChip.prototype.resetMode = function () {
|
||||
//Reset back to idle:
|
||||
this.mode = 0;
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2013 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceFLASHChip(is128, isAteml) {
|
||||
this.largestSizePossible = (!!is128) ? 0x20000 : 0x10000;
|
||||
this.notATMEL = !isAteml;
|
||||
this.saves = null;
|
||||
this.BANKOffset = 0;
|
||||
this.flashCommandUnlockStage = 0;
|
||||
this.flashCommand = 0;
|
||||
this.writeBytesLeft = 0;
|
||||
}
|
||||
GameBoyAdvanceFLASHChip.prototype.initialize = function () {
|
||||
this.allocate();
|
||||
}
|
||||
GameBoyAdvanceFLASHChip.prototype.allocate = function () {
|
||||
if (this.saves == null || (this.saves.length | 0) < (this.largestSizePossible | 0)) {
|
||||
//Allocate the new array:
|
||||
var newSave = getUint8Array(this.largestSizePossible | 0);
|
||||
//Init to default value:
|
||||
for (var index = 0; (index | 0) < (this.largestSizePossible | 0); index = ((index | 0) + 1) | 0) {
|
||||
newSave[index | 0] = 0xFF;
|
||||
}
|
||||
//Copy the old save data out:
|
||||
if (this.saves != null) {
|
||||
for (var index = 0; (index | 0) < (this.saves.length | 0); index = ((index | 0) + 1) | 0) {
|
||||
newSave[index | 0] = this.saves[index | 0] | 0;
|
||||
}
|
||||
}
|
||||
//Assign the new array out:
|
||||
this.saves = newSave;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceFLASHChip.prototype.load = function (save) {
|
||||
if ((save.length | 0) == 0x10000 || (save.length | 0) == 0x20000) {
|
||||
this.saves = save;
|
||||
if ((save.length | 0) == 0x20000) {
|
||||
this.notATMEL = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceFLASHChip.prototype.read = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((this.flashCommand | 0) != 2 || (address | 0) > 1) {
|
||||
data = this.saves[address | this.BANKOffset] | 0;
|
||||
}
|
||||
else {
|
||||
if ((address | 0) == 0) {
|
||||
if (this.notATMEL) {
|
||||
data = ((this.largestSizePossible | 0) == 0x20000) ? 0x62 : 0xBF;
|
||||
}
|
||||
else {
|
||||
data = 0x1F;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this.notATMEL) {
|
||||
data = ((this.largestSizePossible | 0) == 0x20000) ? 0x13 : 0xD4;
|
||||
}
|
||||
else {
|
||||
data = 0x3D;
|
||||
}
|
||||
}
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceFLASHChip.prototype.write = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
switch (this.writeBytesLeft | 0) {
|
||||
case 0:
|
||||
this.writeControlBits(address | 0, data | 0);
|
||||
break;
|
||||
case 0x80:
|
||||
var addressToErase = (address & 0xFF80) | this.BANKOffset;
|
||||
for (var index = 0; (index | 0) < 0x80; index = ((index | 0) + 1) | 0) {
|
||||
this.saves[addressToErase | index] = 0xFF;
|
||||
}
|
||||
default:
|
||||
this.writeByte(address | 0, data | 0);
|
||||
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceFLASHChip.prototype.writeControlBits = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
switch (address | 0) {
|
||||
case 0:
|
||||
this.sectorEraseOrBankSwitch(address | 0, data | 0);
|
||||
break;
|
||||
case 0x5555:
|
||||
this.controlWriteStage2(data | 0);
|
||||
break;
|
||||
case 0x2AAA:
|
||||
this.controlWriteStageIncrement(data | 0);
|
||||
break;
|
||||
default:
|
||||
this.sectorErase(address | 0, data | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceFLASHChip.prototype.writeByte = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
this.saves[address | this.BANKOffset] = data | 0;
|
||||
this.writeBytesLeft = ((this.writeBytesLeft | 0) - 1) | 0;
|
||||
}
|
||||
GameBoyAdvanceFLASHChip.prototype.selectBank = function (bankNumber) {
|
||||
bankNumber = bankNumber | 0;
|
||||
this.BANKOffset = (bankNumber & 0x1) << 16;
|
||||
this.largestSizePossible = Math.max((0x10000 + (this.BANKOffset | 0)) | 0, this.largestSizePossible | 0) | 0;
|
||||
this.notATMEL = true;
|
||||
this.allocate();
|
||||
}
|
||||
GameBoyAdvanceFLASHChip.prototype.controlWriteStage2 = function (data) {
|
||||
data = data | 0;
|
||||
if ((data | 0) == 0xAA) {
|
||||
//Initial Command:
|
||||
this.flashCommandUnlockStage = 1;
|
||||
}
|
||||
else if ((this.flashCommandUnlockStage | 0) == 2) {
|
||||
switch (data | 0) {
|
||||
case 0x10:
|
||||
//Command Erase Chip:
|
||||
if ((this.flashCommand | 0) == 1) {
|
||||
for (var index = 0; (index | 0) < (this.largestSizePossible | 0); index = ((index | 0) + 1) | 0) {
|
||||
this.saves[index | 0] = 0xFF;
|
||||
}
|
||||
}
|
||||
this.flashCommand = 0;
|
||||
break;
|
||||
case 0x80:
|
||||
//Command Erase:
|
||||
this.flashCommand = 1;
|
||||
break;
|
||||
case 0x90:
|
||||
//Command ID:
|
||||
this.flashCommand = 2;
|
||||
break;
|
||||
case 0xA0:
|
||||
//Command Write:
|
||||
this.writeCommandTrigger();
|
||||
break;
|
||||
case 0xB0:
|
||||
//Command Bank Switch:
|
||||
this.flashCommand = 3;
|
||||
break;
|
||||
default:
|
||||
this.flashCommand = 0;
|
||||
}
|
||||
//Reset the command state:
|
||||
this.flashCommandUnlockStage = 0;
|
||||
}
|
||||
else if ((data | 0) == 0xF0) {
|
||||
//Command Clear:
|
||||
this.flashCommand = 0;
|
||||
this.flashCommandUnlockStage = 0;
|
||||
this.notATMEL = true;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceFLASHChip.prototype.writeCommandTrigger = function () {
|
||||
if ((this.flashCommandUnlockStage | 0) == 2) {
|
||||
if (this.notATMEL) {
|
||||
this.writeBytesLeft = 1;
|
||||
}
|
||||
else {
|
||||
this.writeBytesLeft = 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceFLASHChip.prototype.sectorErase = function (address, data) {
|
||||
address = (address << 12) >> 12;
|
||||
data = data | 0;
|
||||
if ((this.flashCommand | 0) == 1 && (this.flashCommandUnlockStage | 0) == 2 && ((data | 0) == 0x30)) {
|
||||
var addressEnd = ((address | this.BANKOffset) + 0x1000) | 0;
|
||||
for (var index = address | this.BANKOffset; (index | 0) < (addressEnd | 0); index = ((index | 0) + 1) | 0) {
|
||||
this.saves[index | 0] = 0xFF;
|
||||
}
|
||||
this.notATMEL = true;
|
||||
}
|
||||
this.flashCommand = 0;
|
||||
this.flashCommandUnlockStage = 0;
|
||||
}
|
||||
GameBoyAdvanceFLASHChip.prototype.sectorEraseOrBankSwitch = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
if ((this.flashCommandUnlockStage | 0) == 2) {
|
||||
this.sectorErase(address | 0, data | 0);
|
||||
}
|
||||
else if ((this.flashCommand | 0) == 3 && (this.flashCommandUnlockStage | 0) == 0) {
|
||||
this.selectBank(data & 0x1);
|
||||
}
|
||||
this.flashCommand = 0;
|
||||
this.flashCommandUnlockStage = 0;
|
||||
}
|
||||
GameBoyAdvanceFLASHChip.prototype.controlWriteStageIncrement = function (data) {
|
||||
if ((data | 0) == 0x55 && (this.flashCommandUnlockStage | 0) == 1) {
|
||||
this.flashCommandUnlockStage = ((this.flashCommandUnlockStage | 0) + 1) | 0;
|
||||
}
|
||||
else {
|
||||
this.flashCommandUnlockStage = 0;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2013 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceSRAMChip() {
|
||||
this.saves = null;
|
||||
this.TILTChip = null;
|
||||
this.TILTChipUnlocked = 0;
|
||||
}
|
||||
GameBoyAdvanceSRAMChip.prototype.initialize = function () {
|
||||
if (this.saves == null || (this.saves.length | 0) != 0x8000) {
|
||||
this.saves = getUint8Array(0x8000);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSRAMChip.prototype.load = function (save) {
|
||||
if ((save.length | 0) == 0x8000) {
|
||||
this.saves = save;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSRAMChip.prototype.read = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((address | 0) < 0x8000 || (this.TILTChipUnlocked | 0) != 3) {
|
||||
data = this.saves[address & 0x7FFF] | 0;
|
||||
}
|
||||
else {
|
||||
switch (address | 0) {
|
||||
case 0x8200:
|
||||
data = this.TILTChip.readXLow() | 0;
|
||||
break;
|
||||
case 0x8300:
|
||||
data = this.TILTChip.readXHigh() | 0;
|
||||
break;
|
||||
case 0x8400:
|
||||
data = this.TILTChip.readYLow() | 0;
|
||||
break;
|
||||
case 0x8500:
|
||||
data = this.TILTChip.readYHigh() | 0;
|
||||
break;
|
||||
default:
|
||||
data = this.saves[address & 0x7FFF] | 0;
|
||||
}
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceSRAMChip.prototype.write = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
if ((address | 0) < 0x8000 || (this.TILTChipUnlocked | 0) >= 4) {
|
||||
//Normal SRAM write:
|
||||
this.saves[address & 0x7FFF] = data | 0;
|
||||
}
|
||||
else {
|
||||
switch (address | 0) {
|
||||
case 0x8000:
|
||||
if ((data | 0) == 0x55) { //Magic Combo.
|
||||
this.TILTChipUnlocked |= 0x1; //Tilt unlock stage 1.
|
||||
}
|
||||
else {
|
||||
this.TILTChipUnlocked |= 0x4; //Definitely not using a tilt sensor.
|
||||
}
|
||||
break;
|
||||
case 0x8100:
|
||||
if ((data | 0) == 0xAA) { //Magic Combo.
|
||||
this.TILTChipUnlocked |= 0x2; //Tilt unlock stage 2.
|
||||
}
|
||||
else {
|
||||
this.TILTChipUnlocked |= 0x4; //Definitely not using a tilt sensor.
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//Check for mirroring while not tilt chip:
|
||||
if ((this.TILTChipUnlocked | 0) == 0) {
|
||||
this.saves[address & 0x7FFF] = data | 0;
|
||||
this.TILTChipUnlocked |= 0x4; //Definitely not using a tilt sensor.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2015 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceSaveDeterminer(saveCore) {
|
||||
this.saves = null;
|
||||
this.saveCore = saveCore;
|
||||
this.possible = 0x7;
|
||||
}
|
||||
GameBoyAdvanceSaveDeterminer.prototype.flags = {
|
||||
SRAM: 1,
|
||||
FLASH: 2,
|
||||
EEPROM: 4
|
||||
}
|
||||
GameBoyAdvanceSaveDeterminer.prototype.initialize = function () {
|
||||
|
||||
}
|
||||
GameBoyAdvanceSaveDeterminer.prototype.load = function (save) {
|
||||
this.saves = save;
|
||||
var length = save.length | 0;
|
||||
switch (length | 0) {
|
||||
case 0x200:
|
||||
case 0x2000:
|
||||
this.possible = this.flags.EEPROM | 0;
|
||||
break;
|
||||
case 0x8000:
|
||||
this.possible = this.flags.SRAM | 0;
|
||||
break;
|
||||
case 0x10000:
|
||||
case 0x20000:
|
||||
this.possible = this.flags.FLASH | 0;
|
||||
}
|
||||
this.checkDetermination();
|
||||
}
|
||||
GameBoyAdvanceSaveDeterminer.prototype.checkDetermination = function () {
|
||||
switch (this.possible) {
|
||||
case 0x1:
|
||||
this.saveCore.referenceSave(0x1);
|
||||
break;
|
||||
case 0x2:
|
||||
this.saveCore.referenceSave(0x2);
|
||||
break;
|
||||
case 0x4:
|
||||
this.saveCore.referenceSave(0x3);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSaveDeterminer.prototype.readSRAM = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
//Is not EEPROM:
|
||||
this.possible &= ~this.flags.EEPROM;
|
||||
if (this.saves != null) {
|
||||
if ((this.possible & this.flags.FLASH) == (this.flags.FLASH | 0) || (this.possible & this.flags.SRAM) == (this.flags.SRAM | 0)) {
|
||||
//Read is the same between SRAM and FLASH for the most part:
|
||||
data = this.saves[(address | 0) % (this.saves.length | 0)] | 0;
|
||||
}
|
||||
}
|
||||
return data | 0;
|
||||
}
|
||||
GameBoyAdvanceSaveDeterminer.prototype.writeGPIO8 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
//GPIO (TODO):
|
||||
}
|
||||
GameBoyAdvanceSaveDeterminer.prototype.writeGPIO16 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
//GPIO (TODO):
|
||||
}
|
||||
GameBoyAdvanceSaveDeterminer.prototype.writeGPIO32 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
//GPIO (TODO):
|
||||
}
|
||||
GameBoyAdvanceSaveDeterminer.prototype.writeEEPROM16 = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
if ((this.possible & this.flags.EEPROM) == (this.flags.EEPROM | 0)) {
|
||||
//EEPROM:
|
||||
this.possible = this.flags.EEPROM | 0;
|
||||
this.checkDetermination();
|
||||
this.saveCore.writeEEPROM16(address | 0, data | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSaveDeterminer.prototype.readEEPROM8 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((this.possible & this.flags.EEPROM) == (this.flags.EEPROM | 0)) {
|
||||
//EEPROM:
|
||||
this.possible = this.flags.EEPROM | 0;
|
||||
this.checkDetermination();
|
||||
return this.saveCore.readEEPROM8(address | 0) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSaveDeterminer.prototype.readEEPROM16 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((this.possible & this.flags.EEPROM) == (this.flags.EEPROM | 0)) {
|
||||
//EEPROM:
|
||||
this.possible = this.flags.EEPROM | 0;
|
||||
this.checkDetermination();
|
||||
return this.saveCore.readEEPROM16(address | 0) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSaveDeterminer.prototype.readEEPROM32 = function (address) {
|
||||
address = address | 0;
|
||||
var data = 0;
|
||||
if ((this.possible & this.flags.EEPROM) == (this.flags.EEPROM | 0)) {
|
||||
//EEPROM:
|
||||
this.possible = this.flags.EEPROM | 0;
|
||||
this.checkDetermination();
|
||||
return this.saveCore.readEEPROM32(address | 0) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceSaveDeterminer.prototype.writeSRAM = function (address, data) {
|
||||
address = address | 0;
|
||||
data = data | 0;
|
||||
//Is not EEPROM:
|
||||
this.possible &= ~this.flags.EEPROM;
|
||||
if ((this.possible & this.flags.FLASH) == (this.flags.FLASH | 0)) {
|
||||
if ((this.possible & this.flags.SRAM) == (this.flags.SRAM | 0)) {
|
||||
if ((address | 0) == 0x5555) {
|
||||
if ((data | 0) == 0xAA) {
|
||||
//FLASH
|
||||
this.possible = this.flags.FLASH | 0;
|
||||
}
|
||||
else {
|
||||
//SRAM
|
||||
this.possible = this.flags.SRAM | 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((address | 0) == 0x5555) {
|
||||
if ((data | 0) == 0xAA) {
|
||||
//FLASH
|
||||
this.possible = this.flags.FLASH | 0;
|
||||
}
|
||||
else {
|
||||
//Is not Flash:
|
||||
this.possible &= ~this.flags.FLASH;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((this.possible & this.flags.SRAM) == (this.flags.SRAM | 0)) {
|
||||
//SRAM
|
||||
this.possible = this.flags.SRAM | 0;
|
||||
}
|
||||
this.checkDetermination();
|
||||
this.saveCore.writeSRAMIfDefined(address | 0, data | 0);
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2015 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceAffineBGRenderer(gfx, BGLayer) {
|
||||
BGLayer = BGLayer | 0;
|
||||
this.gfx = gfx;
|
||||
this.BGLayer = BGLayer | 0;
|
||||
this.offset = ((this.BGLayer << 8) + 0x100) | 0;
|
||||
}
|
||||
if (__VIEWS_SUPPORTED__) {
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.initialize = function () {
|
||||
this.bg2MatrixRenderer = this.gfx.bg2MatrixRenderer;
|
||||
this.bg3MatrixRenderer = this.gfx.bg3MatrixRenderer;
|
||||
this.bg2FrameBufferRenderer = this.gfx.bg2FrameBufferRenderer;
|
||||
this.scratchBuffer = getInt32ViewCustom(this.gfx.buffer, this.offset | 0, ((this.offset | 0) + 240) | 0);
|
||||
this.BGdx = 0x100;
|
||||
this.BGdmx = 0;
|
||||
this.BGdy = 0;
|
||||
this.BGdmy = 0x100;
|
||||
this.BGReferenceX = 0;
|
||||
this.BGReferenceY = 0;
|
||||
this.pb = 0;
|
||||
this.pd = 0;
|
||||
this.doMosaic = 0;
|
||||
this.priorityPreprocess(0);
|
||||
this.offsetReferenceCounters();
|
||||
}
|
||||
if (typeof Math.imul == "function") {
|
||||
//Math.imul found, insert the optimized path in:
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2M = function (line) {
|
||||
line = line | 0;
|
||||
var x = this.pb | 0;
|
||||
var y = this.pd | 0;
|
||||
if ((this.doMosaic | 0) != 0) {
|
||||
//Correct line number for mosaic:
|
||||
var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line | 0) | 0;
|
||||
x = ((x | 0) - Math.imul(this.BGdmx | 0, mosaicY | 0)) | 0;
|
||||
y = ((y | 0) - Math.imul(this.BGdmy | 0, mosaicY | 0)) | 0;
|
||||
}
|
||||
for (var position = 0; (position | 0) < 240; position = ((position | 0) + 1) | 0, x = ((x | 0) + (this.BGdx | 0)) | 0, y = ((y | 0) + (this.BGdy | 0)) | 0) {
|
||||
//Fetch pixel:
|
||||
this.scratchBuffer[position | 0] = this.priorityFlag | this.bg2MatrixRenderer.fetchPixel(x >> 8, y >> 8);
|
||||
}
|
||||
if ((this.doMosaic | 0) != 0) {
|
||||
//Pixelize the line horizontally:
|
||||
this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine3M = function (line) {
|
||||
line = line | 0;
|
||||
var x = this.pb | 0;
|
||||
var y = this.pd | 0;
|
||||
if ((this.doMosaic | 0) != 0) {
|
||||
//Correct line number for mosaic:
|
||||
var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line | 0) | 0;
|
||||
x = ((x | 0) - Math.imul(this.BGdmx | 0, mosaicY | 0)) | 0;
|
||||
y = ((y | 0) - Math.imul(this.BGdmy | 0, mosaicY | 0)) | 0;
|
||||
}
|
||||
for (var position = 0; (position | 0) < 240; position = ((position | 0) + 1) | 0, x = ((x | 0) + (this.BGdx | 0)) | 0, y = ((y | 0) + (this.BGdy | 0)) | 0) {
|
||||
//Fetch pixel:
|
||||
this.scratchBuffer[position | 0] = this.priorityFlag | this.bg3MatrixRenderer.fetchPixel(x >> 8, y >> 8);
|
||||
}
|
||||
if ((this.doMosaic | 0) != 0) {
|
||||
//Pixelize the line horizontally:
|
||||
this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2F = function (line) {
|
||||
line = line | 0;
|
||||
var x = this.pb | 0;
|
||||
var y = this.pd | 0;
|
||||
if ((this.doMosaic | 0) != 0) {
|
||||
//Correct line number for mosaic:
|
||||
var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line | 0) | 0;
|
||||
x = ((x | 0) - Math.imul(this.BGdmx | 0, mosaicY | 0)) | 0;
|
||||
y = ((y | 0) - Math.imul(this.BGdmy | 0, mosaicY | 0)) | 0;
|
||||
}
|
||||
for (var position = 0; (position | 0) < 240; position = ((position | 0) + 1) | 0, x = ((x | 0) + (this.BGdx | 0)) | 0, y = ((y | 0) + (this.BGdy | 0)) | 0) {
|
||||
//Fetch pixel:
|
||||
this.scratchBuffer[position | 0] = this.priorityFlag | this.bg2FrameBufferRenderer.fetchPixel(x >> 8, y >> 8);
|
||||
}
|
||||
if ((this.doMosaic | 0) != 0) {
|
||||
//Pixelize the line horizontally:
|
||||
this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.offsetReferenceCounters = function () {
|
||||
var end = this.gfx.lastUnrenderedLine | 0;
|
||||
this.pb = Math.imul(((this.pb | 0) + (this.BGdmx | 0)) | 0, end | 0) | 0;
|
||||
this.pd = Math.imul(((this.pd | 0) + (this.BGdmy | 0)) | 0, end | 0) | 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Math.imul not found, use the compatibility method:
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2M = function (line) {
|
||||
var x = this.pb;
|
||||
var y = this.pd;
|
||||
if (this.doMosaic != 0) {
|
||||
//Correct line number for mosaic:
|
||||
var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line);
|
||||
x -= this.BGdmx * mosaicY;
|
||||
y -= this.BGdmy * mosaicY;
|
||||
}
|
||||
for (var position = 0; position < 240; ++position, x += this.BGdx, y += this.BGdy) {
|
||||
//Fetch pixel:
|
||||
this.scratchBuffer[position] = this.priorityFlag | this.bg2MatrixRenderer.fetchPixel(x >> 8, y >> 8);
|
||||
}
|
||||
if (this.doMosaic != 0) {
|
||||
//Pixelize the line horizontally:
|
||||
this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine3M = function (line) {
|
||||
var x = this.pb;
|
||||
var y = this.pd;
|
||||
if (this.doMosaic != 0) {
|
||||
//Correct line number for mosaic:
|
||||
var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line);
|
||||
x -= this.BGdmx * mosaicY;
|
||||
y -= this.BGdmy * mosaicY;
|
||||
}
|
||||
for (var position = 0; position < 240; ++position, x += this.BGdx, y += this.BGdy) {
|
||||
//Fetch pixel:
|
||||
this.scratchBuffer[position] = this.priorityFlag | this.bg3MatrixRenderer.fetchPixel(x >> 8, y >> 8);
|
||||
}
|
||||
if (this.doMosaic != 0) {
|
||||
//Pixelize the line horizontally:
|
||||
this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2F = function (line) {
|
||||
var x = this.pb;
|
||||
var y = this.pd;
|
||||
if (this.doMosaic != 0) {
|
||||
//Correct line number for mosaic:
|
||||
var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line);
|
||||
x -= this.BGdmx * mosaicY;
|
||||
y -= this.BGdmy * mosaicY;
|
||||
}
|
||||
for (var position = 0; position < 240; ++position, x += this.BGdx, y += this.BGdy) {
|
||||
//Fetch pixel:
|
||||
this.scratchBuffer[position] = this.priorityFlag | this.bg2FrameBufferRenderer.fetchPixel(x >> 8, y >> 8);
|
||||
}
|
||||
if (this.doMosaic != 0) {
|
||||
//Pixelize the line horizontally:
|
||||
this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.offsetReferenceCounters = function () {
|
||||
var end = this.gfx.lastUnrenderedLine | 0;
|
||||
this.pb = (((this.pb | 0) + (this.BGdmx | 0)) * (end | 0)) | 0;
|
||||
this.pd = (((this.pd | 0) + (this.BGdmy | 0)) * (end | 0)) | 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.initialize = function () {
|
||||
this.bg2MatrixRenderer = this.gfx.bg2MatrixRenderer;
|
||||
this.bg3MatrixRenderer = this.gfx.bg3MatrixRenderer;
|
||||
this.bg2FrameBufferRenderer = this.gfx.bg2FrameBufferRenderer;
|
||||
this.scratchBuffer = this.gfx.buffer;
|
||||
this.BGdx = 0x100;
|
||||
this.BGdmx = 0;
|
||||
this.BGdy = 0;
|
||||
this.BGdmy = 0x100;
|
||||
this.BGReferenceX = 0;
|
||||
this.BGReferenceY = 0;
|
||||
this.pb = 0;
|
||||
this.pd = 0;
|
||||
this.doMosaic = 0;
|
||||
this.priorityPreprocess(0);
|
||||
this.offsetReferenceCounters();
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2M = function (line) {
|
||||
var x = this.pb;
|
||||
var y = this.pd;
|
||||
if (this.doMosaic != 0) {
|
||||
//Correct line number for mosaic:
|
||||
var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line);
|
||||
x -= this.BGdmx * mosaicY;
|
||||
y -= this.BGdmy * mosaicY;
|
||||
}
|
||||
for (var position = 0; position < 240; ++position, x += this.BGdx, y += this.BGdy) {
|
||||
//Fetch pixel:
|
||||
this.scratchBuffer[this.offset + position] = this.priorityFlag | this.bg2MatrixRenderer.fetchPixel(x >> 8, y >> 8);
|
||||
}
|
||||
if (this.doMosaic != 0) {
|
||||
//Pixelize the line horizontally:
|
||||
this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine3M = function (line) {
|
||||
var x = this.pb;
|
||||
var y = this.pd;
|
||||
if (this.doMosaic != 0) {
|
||||
//Correct line number for mosaic:
|
||||
var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line);
|
||||
x -= this.BGdmx * mosaicY;
|
||||
y -= this.BGdmy * mosaicY;
|
||||
}
|
||||
for (var position = 0; position < 240; ++position, x += this.BGdx, y += this.BGdy) {
|
||||
//Fetch pixel:
|
||||
this.scratchBuffer[this.offset + position] = this.priorityFlag | this.bg3MatrixRenderer.fetchPixel(x >> 8, y >> 8);
|
||||
}
|
||||
if (this.doMosaic != 0) {
|
||||
//Pixelize the line horizontally:
|
||||
this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.renderScanLine2F = function (line) {
|
||||
var x = this.pb;
|
||||
var y = this.pd;
|
||||
if (this.doMosaic != 0) {
|
||||
//Correct line number for mosaic:
|
||||
var mosaicY = this.gfx.mosaicRenderer.getMosaicYOffset(line);
|
||||
x -= this.BGdmx * mosaicY;
|
||||
y -= this.BGdmy * mosaicY;
|
||||
}
|
||||
for (var position = 0; position < 240; ++position, x += this.BGdx, y += this.BGdy) {
|
||||
//Fetch pixel:
|
||||
this.scratchBuffer[this.offset + position] = this.priorityFlag | this.bg2FrameBufferRenderer.fetchPixel(x >> 8, y >> 8);
|
||||
}
|
||||
if (this.doMosaic != 0) {
|
||||
//Pixelize the line horizontally:
|
||||
this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.offsetReferenceCounters = function () {
|
||||
var end = this.gfx.lastUnrenderedLine | 0;
|
||||
this.pb = (((this.pb | 0) + (this.BGdmx | 0)) * (end | 0)) | 0;
|
||||
this.pd = (((this.pd | 0) + (this.BGdmy | 0)) * (end | 0)) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.incrementReferenceCounters = function () {
|
||||
this.pb = ((this.pb | 0) + (this.BGdmx | 0)) | 0;
|
||||
this.pd = ((this.pd | 0) + (this.BGdmy | 0)) | 0;
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.resetReferenceCounters = function () {
|
||||
this.pb = this.BGReferenceX | 0;
|
||||
this.pd = this.BGReferenceY | 0;
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.setMosaicEnable = function (doMosaic) {
|
||||
doMosaic = doMosaic | 0;
|
||||
this.doMosaic = doMosaic | 0;
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.priorityPreprocess = function (BGPriority) {
|
||||
BGPriority = BGPriority | 0;
|
||||
this.priorityFlag = (BGPriority << 23) | (1 << (this.BGLayer | 0x10));
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGPA8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGdx = (this.BGdx & 0xFFFFFF00) | data;
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGPA8_1 = function (data) {
|
||||
data = data | 0;
|
||||
data = (data << 24) >> 16;
|
||||
this.BGdx = data | (this.BGdx & 0xFF);
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGPA16 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGdx = (data << 16) >> 16;
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGPB8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGdmx = (this.BGdmx & 0xFFFFFF00) | data;
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGPB8_1 = function (data) {
|
||||
data = data | 0;
|
||||
data = (data << 24) >> 16;
|
||||
this.BGdmx = data | (this.BGdmx & 0xFF);
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGPB16 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGdmx = (data << 16) >> 16;
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGPAB32 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGdx = (data << 16) >> 16;
|
||||
this.BGdmx = data >> 16;
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGPC8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGdy = (this.BGdy & 0xFFFFFF00) | data;
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGPC8_1 = function (data) {
|
||||
data = data | 0;
|
||||
data = (data << 24) >> 16;
|
||||
this.BGdy = data | (this.BGdy & 0xFF);
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGPC16 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGdy = (data << 16) >> 16;
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGPD8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGdmy = (this.BGdmy & 0xFFFFFF00) | data;
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGPD8_1 = function (data) {
|
||||
data = data | 0;
|
||||
data = (data << 24) >> 16;
|
||||
this.BGdmy = data | (this.BGdmy & 0xFF);
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGPD16 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGdmy = (data << 16) >> 16;
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGPCD32 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGdy = (data << 16) >> 16;
|
||||
this.BGdmy = data >> 16;
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGX8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGReferenceX = (this.BGReferenceX & 0xFFFFFF00) | data;
|
||||
//Writing to the x reference doesn't reset the counters during draw!
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGX8_1 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGReferenceX = (data << 8) | (this.BGReferenceX & 0xFFFF00FF);
|
||||
//Writing to the x reference doesn't reset the counters during draw!
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGX8_2 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGReferenceX = (data << 16) | (this.BGReferenceX & 0xFF00FFFF);
|
||||
//Writing to the x reference doesn't reset the counters during draw!
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGX8_3 = function (data) {
|
||||
data = data | 0;
|
||||
data = (data << 28) >> 4;
|
||||
this.BGReferenceX = data | (this.BGReferenceX & 0xFFFFFF);
|
||||
//Writing to the x reference doesn't reset the counters during draw!
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGX16_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGReferenceX = (this.BGReferenceX & 0xFFFF0000) | data;
|
||||
//Writing to the x reference doesn't reset the counters during draw!
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGX16_1 = function (data) {
|
||||
data = data | 0;
|
||||
data = (data << 20) >> 4;
|
||||
this.BGReferenceX = (this.BGReferenceX & 0xFFFF) | data;
|
||||
//Writing to the x reference doesn't reset the counters during draw!
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGX32 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGReferenceX = (data << 4) >> 4;
|
||||
//Writing to the x reference doesn't reset the counters during draw!
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGY8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGReferenceY = (this.BGReferenceY & 0xFFFFFF00) | data;
|
||||
this.resetReferenceCounters();
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGY8_1 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGReferenceY = (data << 8) | (this.BGReferenceY & 0xFFFF00FF);
|
||||
this.resetReferenceCounters();
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGY8_2 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGReferenceY = (data << 16) | (this.BGReferenceY & 0xFF00FFFF);
|
||||
this.resetReferenceCounters();
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGY8_3 = function (data) {
|
||||
data = data | 0;
|
||||
data = (data << 28) >> 4;
|
||||
this.BGReferenceY = data | (this.BGReferenceY & 0xFFFFFF);
|
||||
this.resetReferenceCounters();
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGY16_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGReferenceY = (this.BGReferenceY & 0xFFFF0000) | data;
|
||||
this.resetReferenceCounters();
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGY16_1 = function (data) {
|
||||
data = data | 0;
|
||||
data = (data << 20) >> 4;
|
||||
this.BGReferenceY = (this.BGReferenceY & 0xFFFF) | data;
|
||||
this.resetReferenceCounters();
|
||||
}
|
||||
GameBoyAdvanceAffineBGRenderer.prototype.writeBGY32 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGReferenceY = (data << 4) >> 4;
|
||||
this.resetReferenceCounters();
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2016 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceBG2FrameBufferRenderer(gfx) {
|
||||
this.gfx = gfx;
|
||||
}
|
||||
GameBoyAdvanceBG2FrameBufferRenderer.prototype.initialize = function () {
|
||||
this.palette = this.gfx.palette256;
|
||||
this.VRAM = this.gfx.VRAM;
|
||||
this.VRAM16 = this.gfx.VRAM16;
|
||||
this.fetchPixel = this.fetchMode3Pixel;
|
||||
this.frameSelect = 0;
|
||||
}
|
||||
GameBoyAdvanceBG2FrameBufferRenderer.prototype.selectMode = function (mode) {
|
||||
mode = mode | 0;
|
||||
switch (mode | 0) {
|
||||
case 3:
|
||||
this.fetchPixel = this.fetchMode3Pixel;
|
||||
break;
|
||||
case 4:
|
||||
this.fetchPixel = this.fetchMode4Pixel;
|
||||
break;
|
||||
case 5:
|
||||
this.fetchPixel = this.fetchMode5Pixel;
|
||||
}
|
||||
}
|
||||
if (__LITTLE_ENDIAN__) {
|
||||
if (typeof Math.imul == "function") {
|
||||
//Math.imul found, insert the optimized path in:
|
||||
GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode3Pixel = function (x, y) {
|
||||
x = x | 0;
|
||||
y = y | 0;
|
||||
//Output pixel:
|
||||
if ((x >>> 0) < 240 && (y >>> 0) < 160) {
|
||||
var address = (Math.imul(y | 0, 240) + (x | 0)) | 0;
|
||||
return this.VRAM16[address & 0xFFFF] & 0x7FFF;
|
||||
}
|
||||
//Out of range, output transparency:
|
||||
return 0x3800000;
|
||||
}
|
||||
GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode5Pixel = function (x, y) {
|
||||
x = x | 0;
|
||||
y = y | 0;
|
||||
//Output pixel:
|
||||
if ((x >>> 0) < 160 && (y >>> 0) < 128) {
|
||||
var address = ((this.frameSelect | 0) + Math.imul(y | 0, 160) + (x | 0)) | 0;
|
||||
return this.VRAM16[address & 0xFFFF] & 0x7FFF;
|
||||
}
|
||||
//Out of range, output transparency:
|
||||
return 0x3800000;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Math.imul not found, use the compatibility method:
|
||||
GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode3Pixel = function (x, y) {
|
||||
x = x | 0;
|
||||
y = y | 0;
|
||||
//Output pixel:
|
||||
if ((x >>> 0) < 240 && (y >>> 0) < 160) {
|
||||
var address = (((y * 240) | 0) + (x | 0)) | 0;
|
||||
return this.VRAM16[address & 0xFFFF] & 0x7FFF;
|
||||
}
|
||||
//Out of range, output transparency:
|
||||
return 0x3800000;
|
||||
}
|
||||
GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode5Pixel = function (x, y) {
|
||||
x = x | 0;
|
||||
y = y | 0;
|
||||
//Output pixel:
|
||||
if ((x >>> 0) < 160 && (y >>> 0) < 128) {
|
||||
var address = ((this.frameSelect | 0) + ((y * 160) | 0) + (x | 0)) | 0;
|
||||
return this.VRAM16[address & 0xFFFF] & 0x7FFF;
|
||||
}
|
||||
//Out of range, output transparency:
|
||||
return 0x3800000;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (typeof Math.imul == "function") {
|
||||
//Math.imul found, insert the optimized path in:
|
||||
GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode3Pixel = function (x, y) {
|
||||
x = x | 0;
|
||||
y = y | 0;
|
||||
//Output pixel:
|
||||
if ((x >>> 0) < 240 && (y >>> 0) < 160) {
|
||||
var address = (Math.imul(y | 0, 240) + (x | 0)) << 1;
|
||||
return ((this.VRAM[address | 1] << 8) | this.VRAM[address | 0]) & 0x7FFF;
|
||||
}
|
||||
//Out of range, output transparency:
|
||||
return 0x3800000;
|
||||
}
|
||||
GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode5Pixel = function (x, y) {
|
||||
x = x | 0;
|
||||
y = y | 0;
|
||||
//Output pixel:
|
||||
if ((x >>> 0) < 160 && (y >>> 0) < 128) {
|
||||
var address = ((this.frameSelect | 0) + ((Math.imul(y | 0, 160) + (x | 0)) << 1)) | 0;
|
||||
return ((this.VRAM[address | 1] << 8) | this.VRAM[address | 0]) & 0x7FFF;
|
||||
}
|
||||
//Out of range, output transparency:
|
||||
return 0x3800000;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Math.imul not found, use the compatibility method:
|
||||
GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode3Pixel = function (x, y) {
|
||||
//Output pixel:
|
||||
if ((x >>> 0) < 240 && (y >>> 0) < 160) {
|
||||
var address = ((y * 240) + x) << 1;
|
||||
return ((this.VRAM[address | 1] << 8) | this.VRAM[address]) & 0x7FFF;
|
||||
}
|
||||
//Out of range, output transparency:
|
||||
return 0x3800000;
|
||||
}
|
||||
GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode5Pixel = function (x, y) {
|
||||
//Output pixel:
|
||||
if ((x >>> 0) < 160 && (y >>> 0) < 128) {
|
||||
var address = this.frameSelect + (((y * 160) + x) << 1);
|
||||
return ((this.VRAM[address | 1] << 8) | this.VRAM[address]) & 0x7FFF;
|
||||
}
|
||||
//Out of range, output transparency:
|
||||
return 0x3800000;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof Math.imul == "function") {
|
||||
//Math.imul found, insert the optimized path in:
|
||||
GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode4Pixel = function (x, y) {
|
||||
x = x | 0;
|
||||
y = y | 0;
|
||||
//Output pixel:
|
||||
if ((x >>> 0) < 240 && (y >>> 0) < 160) {
|
||||
var address = ((this.frameSelect | 0) + (Math.imul(y | 0, 240) | 0) + (x | 0)) | 0;
|
||||
return this.palette[this.VRAM[address | 0] & 0xFF] | 0;
|
||||
}
|
||||
//Out of range, output transparency:
|
||||
return 0x3800000;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Math.imul not found, use the compatibility method:
|
||||
GameBoyAdvanceBG2FrameBufferRenderer.prototype.fetchMode4Pixel = function (x, y) {
|
||||
//Output pixel:
|
||||
if ((x >>> 0) < 240 && (y >>> 0) < 160) {
|
||||
return this.palette[this.VRAM[this.frameSelect + (y * 240) + x]];
|
||||
}
|
||||
//Out of range, output transparency:
|
||||
return 0x3800000;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceBG2FrameBufferRenderer.prototype.writeFrameSelect = function (frameSelect) {
|
||||
frameSelect = frameSelect >> 31;
|
||||
this.frameSelect = frameSelect & 0xA000;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2015 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceBGMatrixRenderer(gfx) {
|
||||
this.gfx = gfx;
|
||||
}
|
||||
GameBoyAdvanceBGMatrixRenderer.prototype.initialize = function () {
|
||||
this.VRAM = this.gfx.VRAM;
|
||||
this.palette = this.gfx.palette256;
|
||||
this.screenSizePreprocess(0);
|
||||
this.screenBaseBlockPreprocess(0);
|
||||
this.characterBaseBlockPreprocess(0);
|
||||
this.displayOverflowProcess(0);
|
||||
}
|
||||
if (typeof Math.imul == "function") {
|
||||
//Math.imul found, insert the optimized path in:
|
||||
GameBoyAdvanceBGMatrixRenderer.prototype.fetchTile = function (x, y) {
|
||||
//Compute address for tile VRAM to address:
|
||||
x = x | 0;
|
||||
y = y | 0;
|
||||
var tileNumber = ((x | 0) + Math.imul(y | 0, this.mapSize | 0)) | 0;
|
||||
return this.VRAM[((tileNumber | 0) + (this.BGScreenBaseBlock | 0)) & 0xFFFF] | 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Math.imul not found, use the compatibility method:
|
||||
GameBoyAdvanceBGMatrixRenderer.prototype.fetchTile = function (x, y) {
|
||||
//Compute address for tile VRAM to address:
|
||||
var tileNumber = x + (y * this.mapSize);
|
||||
return this.VRAM[(tileNumber + this.BGScreenBaseBlock) & 0xFFFF];
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceBGMatrixRenderer.prototype.computeScreenAddress = function (x, y) {
|
||||
//Compute address for character VRAM to address:
|
||||
x = x | 0;
|
||||
y = y | 0;
|
||||
var address = this.fetchTile(x >> 3, y >> 3) << 6;
|
||||
address = ((address | 0) + (this.BGCharacterBaseBlock | 0)) | 0;
|
||||
address = ((address | 0) + ((y & 0x7) << 3)) | 0;
|
||||
address = ((address | 0) + (x & 0x7)) | 0;
|
||||
return address | 0;
|
||||
}
|
||||
GameBoyAdvanceBGMatrixRenderer.prototype.fetchPixelOverflow = function (x, y) {
|
||||
//Fetch the pixel:
|
||||
x = x | 0;
|
||||
y = y | 0;
|
||||
//Output pixel:
|
||||
var address = this.computeScreenAddress(x & this.mapSizeComparer, y & this.mapSizeComparer) | 0;
|
||||
return this.palette[this.VRAM[address & 0xFFFF] & 0xFF] | 0;
|
||||
}
|
||||
GameBoyAdvanceBGMatrixRenderer.prototype.fetchPixelNoOverflow = function (x, y) {
|
||||
//Fetch the pixel:
|
||||
x = x | 0;
|
||||
y = y | 0;
|
||||
//Output pixel:
|
||||
if ((x | 0) != (x & this.mapSizeComparer) || (y | 0) != (y & this.mapSizeComparer)) {
|
||||
//Overflow Handling:
|
||||
//Out of bounds with no overflow allowed:
|
||||
return 0x3800000;
|
||||
}
|
||||
var address = this.computeScreenAddress(x | 0, y | 0) | 0;
|
||||
return this.palette[this.VRAM[address & 0xFFFF] & 0xFF] | 0;
|
||||
}
|
||||
GameBoyAdvanceBGMatrixRenderer.prototype.screenBaseBlockPreprocess = function (BGScreenBaseBlock) {
|
||||
BGScreenBaseBlock = BGScreenBaseBlock | 0;
|
||||
this.BGScreenBaseBlock = BGScreenBaseBlock << 11;
|
||||
}
|
||||
GameBoyAdvanceBGMatrixRenderer.prototype.characterBaseBlockPreprocess = function (BGCharacterBaseBlock) {
|
||||
BGCharacterBaseBlock = BGCharacterBaseBlock | 0;
|
||||
this.BGCharacterBaseBlock = BGCharacterBaseBlock << 14;
|
||||
}
|
||||
GameBoyAdvanceBGMatrixRenderer.prototype.screenSizePreprocess = function (BGScreenSize) {
|
||||
BGScreenSize = BGScreenSize | 0;
|
||||
this.mapSize = 0x10 << (BGScreenSize | 0);
|
||||
this.mapSizeComparer = ((this.mapSize << 3) - 1) | 0;
|
||||
}
|
||||
GameBoyAdvanceBGMatrixRenderer.prototype.displayOverflowPreprocess = function (doOverflow) {
|
||||
doOverflow = doOverflow | 0;
|
||||
if ((doOverflow | 0) != (this.BGDisplayOverflow | 0)) {
|
||||
this.displayOverflowProcess(doOverflow | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceBGMatrixRenderer.prototype.displayOverflowProcess = function (doOverflow) {
|
||||
doOverflow = doOverflow | 0;
|
||||
this.BGDisplayOverflow = doOverflow | 0;
|
||||
if ((doOverflow | 0) != 0) {
|
||||
this.fetchPixel = this.fetchPixelOverflow;
|
||||
}
|
||||
else {
|
||||
this.fetchPixel = this.fetchPixelNoOverflow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,577 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2015 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceBGTEXTRenderer(gfx, BGLayer) {
|
||||
BGLayer = BGLayer | 0;
|
||||
this.gfx = gfx;
|
||||
this.BGLayer = BGLayer | 0;
|
||||
}
|
||||
if (__VIEWS_SUPPORTED__) {
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.initialize = function () {
|
||||
this.VRAM = this.gfx.VRAM;
|
||||
this.VRAM16 = this.gfx.VRAM16;
|
||||
this.VRAM32 = this.gfx.VRAM32;
|
||||
this.palette16 = this.gfx.palette16;
|
||||
this.palette256 = this.gfx.palette256;
|
||||
this.offset = ((this.BGLayer << 8) + 0x100) | 0;
|
||||
this.scratchBuffer = getInt32ViewCustom(this.gfx.buffer, this.offset | 0, ((this.offset | 0) + 248) | 0);
|
||||
this.tileFetched = getInt32ViewCustom(this.gfx.buffer, ((this.offset | 0) + 0xF8) | 0, ((this.offset | 0) + 0x100) | 0);
|
||||
this.BGXCoord = 0;
|
||||
this.BGYCoord = 0;
|
||||
this.do256 = 0;
|
||||
this.doMosaic = 0;
|
||||
this.screenSizePreprocess(0);
|
||||
this.priorityPreprocess(0);
|
||||
this.screenBaseBlockPreprocess(0);
|
||||
this.characterBaseBlockPreprocess(0);
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.renderWholeTiles8BIT = function (xTileStart, yTileStart, yTileOffset) {
|
||||
xTileStart = xTileStart | 0;
|
||||
yTileStart = yTileStart | 0;
|
||||
yTileOffset = yTileOffset | 0;
|
||||
//Process full 8 pixels at a time:
|
||||
for (var position = (8 - (this.BGXCoord & 0x7)) | 0; (position | 0) < 240; position = ((position | 0) + 8) | 0) {
|
||||
//Fetch tile attributes:
|
||||
//Get 8 pixels of data:
|
||||
this.process8BitVRAM(this.fetchTile(yTileStart | 0, xTileStart | 0) | 0, yTileOffset | 0);
|
||||
//Copy the buffered tile to line:
|
||||
this.scratchBuffer[position | 0] = this.tileFetched[0] | 0;
|
||||
this.scratchBuffer[((position | 0) + 1) | 0] = this.tileFetched[1] | 0;
|
||||
this.scratchBuffer[((position | 0) + 2) | 0] = this.tileFetched[2] | 0;
|
||||
this.scratchBuffer[((position | 0) + 3) | 0] = this.tileFetched[3] | 0;
|
||||
this.scratchBuffer[((position | 0) + 4) | 0] = this.tileFetched[4] | 0;
|
||||
this.scratchBuffer[((position | 0) + 5) | 0] = this.tileFetched[5] | 0;
|
||||
this.scratchBuffer[((position | 0) + 6) | 0] = this.tileFetched[6] | 0;
|
||||
this.scratchBuffer[((position | 0) + 7) | 0] = this.tileFetched[7] | 0;
|
||||
//Increment a tile counter:
|
||||
xTileStart = ((xTileStart | 0) + 1) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.renderWholeTiles4BIT = function (xTileStart, yTileStart, yTileOffset) {
|
||||
xTileStart = xTileStart | 0;
|
||||
yTileStart = yTileStart | 0;
|
||||
yTileOffset = yTileOffset | 0;
|
||||
//Process full 8 pixels at a time:
|
||||
for (var position = (8 - (this.BGXCoord & 0x7)) | 0; (position | 0) < 240; position = ((position | 0) + 8) | 0) {
|
||||
//Fetch tile attributes:
|
||||
//Get 8 pixels of data:
|
||||
this.process4BitVRAM(this.fetchTile(yTileStart | 0, xTileStart | 0) | 0, yTileOffset | 0);
|
||||
//Copy the buffered tile to line:
|
||||
this.scratchBuffer[position | 0] = this.tileFetched[0] | 0;
|
||||
this.scratchBuffer[((position | 0) + 1) | 0] = this.tileFetched[1] | 0;
|
||||
this.scratchBuffer[((position | 0) + 2) | 0] = this.tileFetched[2] | 0;
|
||||
this.scratchBuffer[((position | 0) + 3) | 0] = this.tileFetched[3] | 0;
|
||||
this.scratchBuffer[((position | 0) + 4) | 0] = this.tileFetched[4] | 0;
|
||||
this.scratchBuffer[((position | 0) + 5) | 0] = this.tileFetched[5] | 0;
|
||||
this.scratchBuffer[((position | 0) + 6) | 0] = this.tileFetched[6] | 0;
|
||||
this.scratchBuffer[((position | 0) + 7) | 0] = this.tileFetched[7] | 0;
|
||||
//Increment a tile counter:
|
||||
xTileStart = ((xTileStart | 0) + 1) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.fetchVRAMStart = function () {
|
||||
//Handle the the first tile of the scan-line specially:
|
||||
var pixelPipelinePosition = this.BGXCoord & 0x7;
|
||||
switch (pixelPipelinePosition | 0) {
|
||||
case 0:
|
||||
this.scratchBuffer[0] = this.tileFetched[0] | 0;
|
||||
case 1:
|
||||
this.scratchBuffer[(1 - (pixelPipelinePosition | 0)) | 0] = this.tileFetched[1] | 0;
|
||||
case 2:
|
||||
this.scratchBuffer[(2 - (pixelPipelinePosition | 0)) | 0] = this.tileFetched[2] | 0;
|
||||
case 3:
|
||||
this.scratchBuffer[(3 - (pixelPipelinePosition | 0)) | 0] = this.tileFetched[3] | 0;
|
||||
case 4:
|
||||
this.scratchBuffer[(4 - (pixelPipelinePosition | 0)) | 0] = this.tileFetched[4] | 0;
|
||||
case 5:
|
||||
this.scratchBuffer[(5 - (pixelPipelinePosition | 0)) | 0] = this.tileFetched[5] | 0;
|
||||
case 6:
|
||||
this.scratchBuffer[(6 - (pixelPipelinePosition | 0)) | 0] = this.tileFetched[6] | 0;
|
||||
default:
|
||||
this.scratchBuffer[(7 - (pixelPipelinePosition | 0)) | 0] = this.tileFetched[7] | 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.initialize = function () {
|
||||
this.VRAM = this.gfx.VRAM;
|
||||
this.VRAM16 = this.gfx.VRAM16;
|
||||
this.VRAM32 = this.gfx.VRAM32;
|
||||
this.palette16 = this.gfx.palette16;
|
||||
this.palette256 = this.gfx.palette256;
|
||||
this.offset = (this.BGLayer << 8) + 0x100;
|
||||
this.offsetEnd = this.offset + 240;
|
||||
this.scratchBuffer = this.gfx.buffer;
|
||||
this.tileFetched = getInt32Array(8);
|
||||
this.BGXCoord = 0;
|
||||
this.BGYCoord = 0;
|
||||
this.do256 = 0;
|
||||
this.doMosaic = 0;
|
||||
this.screenSizePreprocess(0);
|
||||
this.priorityPreprocess(0);
|
||||
this.screenBaseBlockPreprocess(0);
|
||||
this.characterBaseBlockPreprocess(0);
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.renderWholeTiles8BIT = function (xTileStart, yTileStart, yTileOffset) {
|
||||
//Process full 8 pixels at a time:
|
||||
for (var position = 8 - (this.BGXCoord & 0x7) + this.offset; position < this.offsetEnd;) {
|
||||
//Fetch tile attributes:
|
||||
//Get 8 pixels of data:
|
||||
this.process8BitVRAM(this.fetchTile(yTileStart, xTileStart++), yTileOffset);
|
||||
//Copy the buffered tile to line:
|
||||
this.scratchBuffer[position++] = this.tileFetched[0];
|
||||
this.scratchBuffer[position++] = this.tileFetched[1];
|
||||
this.scratchBuffer[position++] = this.tileFetched[2];
|
||||
this.scratchBuffer[position++] = this.tileFetched[3];
|
||||
this.scratchBuffer[position++] = this.tileFetched[4];
|
||||
this.scratchBuffer[position++] = this.tileFetched[5];
|
||||
this.scratchBuffer[position++] = this.tileFetched[6];
|
||||
this.scratchBuffer[position++] = this.tileFetched[7];
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.renderWholeTiles4BIT = function (xTileStart, yTileStart, yTileOffset) {
|
||||
//Process full 8 pixels at a time:
|
||||
for (var position = 8 - (this.BGXCoord & 0x7) + this.offset; position < this.offsetEnd;) {
|
||||
//Fetch tile attributes:
|
||||
//Get 8 pixels of data:
|
||||
this.process4BitVRAM(this.fetchTile(yTileStart, xTileStart++), yTileOffset);
|
||||
//Copy the buffered tile to line:
|
||||
this.scratchBuffer[position++] = this.tileFetched[0];
|
||||
this.scratchBuffer[position++] = this.tileFetched[1];
|
||||
this.scratchBuffer[position++] = this.tileFetched[2];
|
||||
this.scratchBuffer[position++] = this.tileFetched[3];
|
||||
this.scratchBuffer[position++] = this.tileFetched[4];
|
||||
this.scratchBuffer[position++] = this.tileFetched[5];
|
||||
this.scratchBuffer[position++] = this.tileFetched[6];
|
||||
this.scratchBuffer[position++] = this.tileFetched[7];
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.fetchVRAMStart = function () {
|
||||
//Handle the the first tile of the scan-line specially:
|
||||
var pixelPipelinePosition = this.BGXCoord & 0x7;
|
||||
var offset = pixelPipelinePosition - this.offset;
|
||||
switch (pixelPipelinePosition | 0) {
|
||||
case 0:
|
||||
this.scratchBuffer[offset] = this.tileFetched[0];
|
||||
case 1:
|
||||
this.scratchBuffer[1 - offset] = this.tileFetched[1];
|
||||
case 2:
|
||||
this.scratchBuffer[2 - offset] = this.tileFetched[2];
|
||||
case 3:
|
||||
this.scratchBuffer[3 - offset] = this.tileFetched[3];
|
||||
case 4:
|
||||
this.scratchBuffer[4 - offset] = this.tileFetched[4];
|
||||
case 5:
|
||||
this.scratchBuffer[5 - offset] = this.tileFetched[5];
|
||||
case 6:
|
||||
this.scratchBuffer[6 - offset] = this.tileFetched[6];
|
||||
default:
|
||||
this.scratchBuffer[7 - offset] = this.tileFetched[7];
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.renderScanLine = function (line) {
|
||||
line = line | 0;
|
||||
if ((this.doMosaic | 0) != 0) {
|
||||
//Correct line number for mosaic:
|
||||
line = ((line | 0) - (this.gfx.mosaicRenderer.getMosaicYOffset(line | 0) | 0)) | 0;
|
||||
}
|
||||
var yTileOffset = ((line | 0) + (this.BGYCoord | 0)) & 0x7;
|
||||
var yTileStart = ((line | 0) + (this.BGYCoord | 0)) >> 3;
|
||||
var xTileStart = this.BGXCoord >> 3;
|
||||
//Render the tiles:
|
||||
if ((this.do256 | 0) != 0) {
|
||||
//8-bit palette mode:
|
||||
this.render8BITLine(yTileStart | 0, xTileStart | 0, yTileOffset | 0);
|
||||
}
|
||||
else {
|
||||
//4-bit palette mode:
|
||||
this.render4BITLine(yTileStart | 0, xTileStart | 0, yTileOffset | 0);
|
||||
}
|
||||
if ((this.doMosaic | 0) != 0) {
|
||||
//Pixelize the line horizontally:
|
||||
this.gfx.mosaicRenderer.renderMosaicHorizontal(this.offset | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.render8BITLine = function (yTileStart, xTileStart, yTileOffset) {
|
||||
yTileStart = yTileStart | 0;
|
||||
xTileStart = xTileStart | 0;
|
||||
yTileOffset = yTileOffset | 0;
|
||||
//Fetch tile attributes:
|
||||
var chrData = this.fetchTile(yTileStart | 0, xTileStart | 0) | 0;
|
||||
xTileStart = ((xTileStart | 0) + 1) | 0;
|
||||
//Get 8 pixels of data:
|
||||
this.process8BitVRAM(chrData | 0, yTileOffset | 0);
|
||||
//Copy the buffered tile to line:
|
||||
this.fetchVRAMStart();
|
||||
//Render the rest of the tiles fast:
|
||||
this.renderWholeTiles8BIT(xTileStart | 0, yTileStart | 0, yTileOffset | 0);
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.render4BITLine = function (yTileStart, xTileStart, yTileOffset) {
|
||||
//Fetch tile attributes:
|
||||
var chrData = this.fetchTile(yTileStart | 0, xTileStart | 0) | 0;
|
||||
xTileStart = ((xTileStart | 0) + 1) | 0;
|
||||
//Get 8 pixels of data:
|
||||
this.process4BitVRAM(chrData | 0, yTileOffset | 0);
|
||||
//Copy the buffered tile to line:
|
||||
this.fetchVRAMStart();
|
||||
//Render the rest of the tiles fast:
|
||||
this.renderWholeTiles4BIT(xTileStart | 0, yTileStart | 0, yTileOffset | 0);
|
||||
}
|
||||
if (__LITTLE_ENDIAN__) {
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.fetchTile = function (yTileStart, xTileStart) {
|
||||
yTileStart = yTileStart | 0;
|
||||
xTileStart = xTileStart | 0;
|
||||
//Find the tile code to locate the tile block:
|
||||
var address = ((this.computeTileNumber(yTileStart | 0, xTileStart | 0) | 0) + (this.BGScreenBaseBlock | 0)) | 0;
|
||||
return this.VRAM16[address & 0x7FFF] | 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.fetchTile = function (yTileStart, xTileStart) {
|
||||
//Find the tile code to locate the tile block:
|
||||
var address = ((this.computeTileNumber(yTileStart, xTileStart) + this.BGScreenBaseBlock) << 1) & 0xFFFF;
|
||||
return (this.VRAM[address | 1] << 8) | this.VRAM[address];
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.computeTileNumber = function (yTile, xTile) {
|
||||
//Return the true tile number:
|
||||
yTile = yTile | 0;
|
||||
xTile = xTile | 0;
|
||||
var tileNumber = xTile & 0x1F;
|
||||
switch (this.tileMode | 0) {
|
||||
//1x1
|
||||
case 0:
|
||||
tileNumber = tileNumber | ((yTile & 0x1F) << 5);
|
||||
break;
|
||||
//2x1
|
||||
case 1:
|
||||
tileNumber = tileNumber | (((xTile & 0x20) | (yTile & 0x1F)) << 5);
|
||||
break;
|
||||
//1x2
|
||||
case 2:
|
||||
tileNumber = tileNumber | ((yTile & 0x3F) << 5);
|
||||
break;
|
||||
//2x2
|
||||
default:
|
||||
tileNumber = tileNumber | (((xTile & 0x20) | (yTile & 0x1F)) << 5) | ((yTile & 0x20) << 6);
|
||||
}
|
||||
return tileNumber | 0;
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.process4BitVRAM = function (chrData, yOffset) {
|
||||
//16 color tile mode:
|
||||
chrData = chrData | 0;
|
||||
yOffset = yOffset | 0;
|
||||
//Parse flip attributes, grab palette, and then output pixel:
|
||||
var address = (chrData & 0x3FF) << 3;
|
||||
address = ((address | 0) + (this.BGCharacterBaseBlock | 0)) | 0;
|
||||
if ((chrData & 0x800) == 0) {
|
||||
//No vertical flip:
|
||||
address = ((address | 0) + (yOffset | 0)) | 0;
|
||||
|
||||
}
|
||||
else {
|
||||
//Vertical flip:
|
||||
address = ((address | 0) + 7) | 0;
|
||||
address = ((address | 0) - (yOffset | 0)) | 0;
|
||||
}
|
||||
//Copy out our pixels:
|
||||
this.render4BitVRAM(chrData >> 8, address | 0);
|
||||
}
|
||||
if (__LITTLE_ENDIAN__) {
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.render4BitVRAM = function (chrData, address) {
|
||||
chrData = chrData | 0;
|
||||
address = address | 0;
|
||||
//Unrolled data tile line fetch:
|
||||
if ((address | 0) < 0x4000) {
|
||||
//Tile address valid:
|
||||
var paletteOffset = chrData & 0xF0;
|
||||
var data = this.VRAM32[address | 0] | 0;
|
||||
if ((chrData & 0x4) == 0) {
|
||||
//Normal Horizontal:
|
||||
this.tileFetched[0] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[1] = this.palette16[paletteOffset | ((data >> 4) & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[2] = this.palette16[paletteOffset | ((data >> 8) & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[3] = this.palette16[paletteOffset | ((data >> 12) & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[4] = this.palette16[paletteOffset | ((data >> 16) & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[5] = this.palette16[paletteOffset | ((data >> 20) & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[6] = this.palette16[paletteOffset | ((data >> 24) & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[7] = this.palette16[paletteOffset | (data >>> 28)] | this.priorityFlag;
|
||||
}
|
||||
else {
|
||||
//Flipped Horizontally:
|
||||
this.tileFetched[0] = this.palette16[paletteOffset | (data >>> 28)] | this.priorityFlag;
|
||||
this.tileFetched[1] = this.palette16[paletteOffset | ((data >> 24) & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[2] = this.palette16[paletteOffset | ((data >> 20) & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[3] = this.palette16[paletteOffset | ((data >> 16) & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[4] = this.palette16[paletteOffset | ((data >> 12) & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[5] = this.palette16[paletteOffset | ((data >> 8) & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[6] = this.palette16[paletteOffset | ((data >> 4) & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[7] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Tile address invalid:
|
||||
this.addressInvalidRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.render4BitVRAM = function (chrData, address) {
|
||||
address <<= 2;
|
||||
//Unrolled data tile line fetch:
|
||||
if (address < 0x10000) {
|
||||
//Tile address valid:
|
||||
var paletteOffset = chrData & 0xF0;
|
||||
var data = this.VRAM[address];
|
||||
if ((chrData & 0x4) == 0) {
|
||||
//Normal Horizontal:
|
||||
this.tileFetched[0] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[1] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag;
|
||||
data = this.VRAM[address | 1];
|
||||
this.tileFetched[2] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[3] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag;
|
||||
data = this.VRAM[address | 2];
|
||||
this.tileFetched[4] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[5] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag;
|
||||
data = this.VRAM[address | 3];
|
||||
this.tileFetched[6] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[7] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag;
|
||||
}
|
||||
else {
|
||||
//Flipped Horizontally:
|
||||
this.tileFetched[7] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[6] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag;
|
||||
data = this.VRAM[address | 1];
|
||||
this.tileFetched[5] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[4] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag;
|
||||
data = this.VRAM[address | 2];
|
||||
this.tileFetched[3] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[2] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag;
|
||||
data = this.VRAM[address | 3];
|
||||
this.tileFetched[1] = this.palette16[paletteOffset | (data & 0xF)] | this.priorityFlag;
|
||||
this.tileFetched[0] = this.palette16[paletteOffset | (data >> 4)] | this.priorityFlag;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Tile address invalid:
|
||||
this.addressInvalidRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
If there was 64 bit typed array support,
|
||||
then process8BitVRAM, render8BitVRAMNormal,
|
||||
and render8BitVRAMFlipped could be optimized further.
|
||||
Namely make one fetch for tile data instead of two,
|
||||
and cancel a y-offset shift.
|
||||
*/
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.process8BitVRAM = function (chrData, yOffset) {
|
||||
//16 color tile mode:
|
||||
chrData = chrData | 0;
|
||||
yOffset = yOffset | 0;
|
||||
//Parse flip attributes, grab palette, and then output pixel:
|
||||
var address = (chrData & 0x3FF) << 4;
|
||||
address = ((address | 0) + (this.BGCharacterBaseBlock | 0)) | 0;
|
||||
//Copy out our pixels:
|
||||
switch (chrData & 0xC00) {
|
||||
//No Flip:
|
||||
case 0:
|
||||
address = ((address | 0) + (yOffset << 1)) | 0;
|
||||
this.render8BitVRAMNormal(address | 0);
|
||||
break;
|
||||
//Horizontal Flip:
|
||||
case 0x400:
|
||||
address = ((address | 0) + (yOffset << 1)) | 0;
|
||||
this.render8BitVRAMFlipped(address | 0);
|
||||
break;
|
||||
//Vertical Flip:
|
||||
case 0x800:
|
||||
address = ((address | 0) + 14) | 0;
|
||||
address = ((address | 0) - (yOffset << 1)) | 0;
|
||||
this.render8BitVRAMNormal(address | 0);
|
||||
break;
|
||||
//Horizontal & Vertical Flip:
|
||||
default:
|
||||
address = ((address | 0) + 14) | 0;
|
||||
address = ((address | 0) - (yOffset << 1)) | 0;
|
||||
this.render8BitVRAMFlipped(address | 0);
|
||||
}
|
||||
}
|
||||
if (__LITTLE_ENDIAN__) {
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.render8BitVRAMNormal = function (address) {
|
||||
address = address | 0;
|
||||
if ((address | 0) < 0x4000) {
|
||||
//Tile address valid:
|
||||
//Normal Horizontal:
|
||||
var data = this.VRAM32[address | 0] | 0;
|
||||
this.tileFetched[0] = this.palette256[data & 0xFF] | this.priorityFlag;
|
||||
this.tileFetched[1] = this.palette256[(data >> 8) & 0xFF] | this.priorityFlag;
|
||||
this.tileFetched[2] = this.palette256[(data >> 16) & 0xFF] | this.priorityFlag;
|
||||
this.tileFetched[3] = this.palette256[data >>> 24] | this.priorityFlag;
|
||||
data = this.VRAM32[address | 1] | 0;
|
||||
this.tileFetched[4] = this.palette256[data & 0xFF] | this.priorityFlag;
|
||||
this.tileFetched[5] = this.palette256[(data >> 8) & 0xFF] | this.priorityFlag;
|
||||
this.tileFetched[6] = this.palette256[(data >> 16) & 0xFF] | this.priorityFlag;
|
||||
this.tileFetched[7] = this.palette256[data >>> 24] | this.priorityFlag;
|
||||
}
|
||||
else {
|
||||
//Tile address invalid:
|
||||
this.addressInvalidRender();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.render8BitVRAMFlipped = function (address) {
|
||||
address = address | 0;
|
||||
if ((address | 0) < 0x4000) {
|
||||
//Tile address valid:
|
||||
//Flipped Horizontally:
|
||||
var data = this.VRAM32[address | 0] | 0;
|
||||
this.tileFetched[4] = this.palette256[data >>> 24] | this.priorityFlag;
|
||||
this.tileFetched[5] = this.palette256[(data >> 16) & 0xFF] | this.priorityFlag;
|
||||
this.tileFetched[6] = this.palette256[(data >> 8) & 0xFF] | this.priorityFlag;
|
||||
this.tileFetched[7] = this.palette256[data & 0xFF] | this.priorityFlag;
|
||||
data = this.VRAM32[address | 1] | 0;
|
||||
this.tileFetched[0] = this.palette256[data >>> 24] | this.priorityFlag;
|
||||
this.tileFetched[1] = this.palette256[(data >> 16) & 0xFF] | this.priorityFlag;
|
||||
this.tileFetched[2] = this.palette256[(data >> 8) & 0xFF] | this.priorityFlag;
|
||||
this.tileFetched[3] = this.palette256[data & 0xFF] | this.priorityFlag;
|
||||
}
|
||||
else {
|
||||
//Tile address invalid:
|
||||
this.addressInvalidRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.render8BitVRAMNormal = function (address) {
|
||||
address <<= 2;
|
||||
if (address < 0x10000) {
|
||||
//Tile address valid:
|
||||
//Normal Horizontal:
|
||||
this.tileFetched[0] = this.palette256[this.VRAM[address]] | this.priorityFlag;
|
||||
this.tileFetched[1] = this.palette256[this.VRAM[address | 1]] | this.priorityFlag;
|
||||
this.tileFetched[2] = this.palette256[this.VRAM[address | 2]] | this.priorityFlag;
|
||||
this.tileFetched[3] = this.palette256[this.VRAM[address | 3]] | this.priorityFlag;
|
||||
this.tileFetched[4] = this.palette256[this.VRAM[address | 4]] | this.priorityFlag;
|
||||
this.tileFetched[5] = this.palette256[this.VRAM[address | 5]] | this.priorityFlag;
|
||||
this.tileFetched[6] = this.palette256[this.VRAM[address | 6]] | this.priorityFlag;
|
||||
this.tileFetched[7] = this.palette256[this.VRAM[address | 7]] | this.priorityFlag;
|
||||
}
|
||||
else {
|
||||
//Tile address invalid:
|
||||
this.addressInvalidRender();
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.render8BitVRAMFlipped = function (address) {
|
||||
address <<= 2;
|
||||
if (address < 0x10000) {
|
||||
//Tile address valid:
|
||||
//Flipped Horizontally:
|
||||
this.tileFetched[7] = this.palette256[this.VRAM[address]] | this.priorityFlag;
|
||||
this.tileFetched[6] = this.palette256[this.VRAM[address | 1]] | this.priorityFlag;
|
||||
this.tileFetched[5] = this.palette256[this.VRAM[address | 2]] | this.priorityFlag;
|
||||
this.tileFetched[4] = this.palette256[this.VRAM[address | 3]] | this.priorityFlag;
|
||||
this.tileFetched[3] = this.palette256[this.VRAM[address | 4]] | this.priorityFlag;
|
||||
this.tileFetched[2] = this.palette256[this.VRAM[address | 5]] | this.priorityFlag;
|
||||
this.tileFetched[1] = this.palette256[this.VRAM[address | 6]] | this.priorityFlag;
|
||||
this.tileFetched[0] = this.palette256[this.VRAM[address | 7]] | this.priorityFlag;
|
||||
}
|
||||
else {
|
||||
//Tile address invalid:
|
||||
this.addressInvalidRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.addressInvalidRender = function () {
|
||||
//In GBA mode on NDS, we display transparency on invalid tiles:
|
||||
var data = this.gfx.transparency | this.priorityFlag;
|
||||
this.tileFetched[0] = data | 0;
|
||||
this.tileFetched[1] = data | 0;
|
||||
this.tileFetched[2] = data | 0;
|
||||
this.tileFetched[3] = data | 0;
|
||||
this.tileFetched[4] = data | 0;
|
||||
this.tileFetched[5] = data | 0;
|
||||
this.tileFetched[6] = data | 0;
|
||||
this.tileFetched[7] = data | 0;
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.setMosaicEnable = function (doMosaic) {
|
||||
doMosaic = doMosaic | 0;
|
||||
this.doMosaic = doMosaic | 0;
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.paletteModeSelect = function (do256) {
|
||||
do256 = do256 | 0;
|
||||
this.do256 = do256 | 0;
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.screenSizePreprocess = function (BGScreenSize) {
|
||||
BGScreenSize = BGScreenSize | 0;
|
||||
this.tileMode = BGScreenSize | 0;
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.priorityPreprocess = function (BGPriority) {
|
||||
BGPriority = BGPriority | 0;
|
||||
this.priorityFlag = (BGPriority << 23) | (1 << (this.BGLayer | 0x10));
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.screenBaseBlockPreprocess = function (BGScreenBaseBlock) {
|
||||
BGScreenBaseBlock = BGScreenBaseBlock | 0;
|
||||
this.BGScreenBaseBlock = BGScreenBaseBlock << 10;
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.characterBaseBlockPreprocess = function (BGCharacterBaseBlock) {
|
||||
BGCharacterBaseBlock = BGCharacterBaseBlock | 0;
|
||||
this.BGCharacterBaseBlock = BGCharacterBaseBlock << 12;
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.writeBGCNT8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.setMosaicEnable(data & 0x40);
|
||||
this.paletteModeSelect(data & 0x80);
|
||||
this.priorityPreprocess(data & 0x3);
|
||||
this.characterBaseBlockPreprocess((data & 0xC) >> 2);
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.writeBGCNT8_1 = function (data) {
|
||||
data = data | 0;
|
||||
this.screenSizePreprocess((data & 0xC0) >> 6);
|
||||
this.screenBaseBlockPreprocess(data & 0x1F);
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.writeBGCNT16 = function (data) {
|
||||
data = data | 0;
|
||||
this.setMosaicEnable(data & 0x40);
|
||||
this.paletteModeSelect(data & 0x80);
|
||||
this.priorityPreprocess(data & 0x3);
|
||||
this.characterBaseBlockPreprocess((data & 0xC) >> 2);
|
||||
this.screenSizePreprocess((data & 0xC000) >> 14);
|
||||
this.screenBaseBlockPreprocess((data >> 8) & 0x1F);
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.writeBGHOFS8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGXCoord = (this.BGXCoord & 0x100) | data;
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.writeBGHOFS8_1 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGXCoord = (data << 8) | (this.BGXCoord & 0xFF);
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.writeBGHOFS16 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGXCoord = data | 0;
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.writeBGVOFS8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGYCoord = (this.BGYCoord & 0x100) | data;
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.writeBGVOFS8_1 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGYCoord = (data << 8) | (this.BGYCoord & 0xFF);
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.writeBGVOFS16 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGYCoord = data | 0;
|
||||
}
|
||||
GameBoyAdvanceBGTEXTRenderer.prototype.writeBGOFS32 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGXCoord = data & 0x1FF;
|
||||
this.BGYCoord = data >> 16;
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2016 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceColorEffectsRenderer(buffer) {
|
||||
this.effectsTarget1 = 0;
|
||||
this.colorEffectsType = 0;
|
||||
this.effectsTarget2 = 0;
|
||||
this.initialize(buffer);
|
||||
}
|
||||
if (typeof SIMD == "object" && typeof SIMD.Int32x4 == "function") {
|
||||
//SIMD support found, insert the optimized SIMD path in:
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.initialize = function (buffer) {
|
||||
this.alphaBlendAmountTarget1 = SIMD.Int32x4.splat(0);
|
||||
this.alphaBlendAmountTarget2 = SIMD.Int32x4.splat(0);
|
||||
this.brightnessEffectAmount = SIMD.Int32x4.splat(0);
|
||||
this.brightnessEffectAmountReverse = SIMD.Int32x4.splat(0x10);
|
||||
this.buffer = buffer;
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.pixelMask = SIMD.Int32x4.splat(0x1F);
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.temporaryPixelBuffer = new Int32Array(4);
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlend = function (topPixel, lowerPixel) {
|
||||
topPixel = topPixel | 0;
|
||||
lowerPixel = lowerPixel | 0;
|
||||
var p1 = SIMD.Int32x4(topPixel >> 10, topPixel >> 5, topPixel, 0);
|
||||
var p2 = SIMD.Int32x4(lowerPixel >> 10, lowerPixel >> 5, lowerPixel, 0);
|
||||
p1 = SIMD.Int32x4.and(p1, this.pixelMask);
|
||||
p2 = SIMD.Int32x4.and(p2, this.pixelMask);
|
||||
p1 = SIMD.Int32x4.mul(p1, this.alphaBlendAmountTarget1);
|
||||
p2 = SIMD.Int32x4.mul(p2, this.alphaBlendAmountTarget2);
|
||||
var presult = SIMD.Int32x4.add(p1, p2);
|
||||
presult = SIMD.Int32x4.shiftRightByScalar(presult, 4);
|
||||
var selectMask = SIMD.Int32x4.lessThanOrEqual(presult, this.pixelMask);
|
||||
presult = SIMD.Int32x4.select(selectMask, presult, this.pixelMask);
|
||||
SIMD.Int32x4.store(this.temporaryPixelBuffer, 0, presult);
|
||||
return (this.temporaryPixelBuffer[0] << 10) | (this.temporaryPixelBuffer[1] << 5) | this.temporaryPixelBuffer[2];
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.brightnessIncrease = function (topPixel) {
|
||||
topPixel = topPixel | 0;
|
||||
var p1 = SIMD.Int32x4(topPixel >> 10, topPixel >> 5, topPixel, 0);
|
||||
p1 = SIMD.Int32x4.and(p1, this.pixelMask);
|
||||
var pTemp = SIMD.Int32x4.sub(this.pixelMask, p1);
|
||||
pTemp = SIMD.Int32x4.mul(pTemp, this.brightnessEffectAmount);
|
||||
pTemp = SIMD.Int32x4.shiftRightByScalar(pTemp, 4);
|
||||
p1 = SIMD.Int32x4.add(p1, pTemp);
|
||||
SIMD.Int32x4.store(this.temporaryPixelBuffer, 0, p1);
|
||||
return (this.temporaryPixelBuffer[0] << 10) | (this.temporaryPixelBuffer[1] << 5) | this.temporaryPixelBuffer[2];
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.brightnessDecrease = function (topPixel) {
|
||||
topPixel = topPixel | 0;
|
||||
var p1 = SIMD.Int32x4(topPixel >> 10, topPixel >> 5, topPixel, 0);
|
||||
p1 = SIMD.Int32x4.and(p1, this.pixelMask);
|
||||
p1 = SIMD.Int32x4.mul(p1, this.brightnessEffectAmountReverse);
|
||||
p1 = SIMD.Int32x4.shiftRightByScalar(p1, 4);
|
||||
SIMD.Int32x4.store(this.temporaryPixelBuffer, 0, p1);
|
||||
return (this.temporaryPixelBuffer[0] << 10) | (this.temporaryPixelBuffer[1] << 5) | this.temporaryPixelBuffer[2];
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA8_0 = function (data) {
|
||||
data = data | 0;
|
||||
var alphaBlendAmountTarget1Scratch = data & 0x1F;
|
||||
this.alphaBlendAmountTarget1 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0);
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA8_1 = function (data) {
|
||||
data = data | 0;
|
||||
var alphaBlendAmountTarget2Scratch = data & 0x1F;
|
||||
this.alphaBlendAmountTarget2 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0);
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA16 = function (data) {
|
||||
data = data | 0;
|
||||
var alphaBlendAmountTarget1Scratch = data & 0x1F;
|
||||
this.alphaBlendAmountTarget1 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0);
|
||||
var alphaBlendAmountTarget2Scratch = (data >> 8) & 0x1F;
|
||||
this.alphaBlendAmountTarget2 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0);
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDCNT32 = function (data) {
|
||||
data = data | 0;
|
||||
//Select target 1 and color effects mode:
|
||||
this.effectsTarget1 = (data & 0x3F) << 16;
|
||||
this.colorEffectsType = (data >> 6) & 0x3;
|
||||
//Select target 2:
|
||||
this.effectsTarget2 = (data & 0x3F00) << 8;
|
||||
var alphaBlendAmountTarget1Scratch = (data >> 16) & 0x1F;
|
||||
this.alphaBlendAmountTarget1 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0);
|
||||
var alphaBlendAmountTarget2Scratch = (data >> 24) & 0x1F;
|
||||
this.alphaBlendAmountTarget2 = SIMD.Int32x4.splat(Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0);
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDY8 = function (data) {
|
||||
data = data | 0;
|
||||
var data = Math.min(data & 0x1F, 0x10) | 0;
|
||||
this.brightnessEffectAmount = SIMD.Int32x4.splat(data | 0);
|
||||
this.brightnessEffectAmountReverse = SIMD.Int32x4.splat((0x10 - (data | 0)) | 0);
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processFullNormalEffectsNoSprites = function () {
|
||||
for (var index = 0; (index | 0) < 240; index = ((index | 0) + 4) | 0) {
|
||||
this.buffer[index | 0] = this.processPixelNormal(this.buffer[index | 0x700] | 0, this.buffer[index | 0x800] | 0);
|
||||
this.buffer[index | 1] = this.processPixelNormal(this.buffer[index | 0x701] | 0, this.buffer[index | 0x801] | 0);
|
||||
this.buffer[index | 2] = this.processPixelNormal(this.buffer[index | 0x702] | 0, this.buffer[index | 0x802] | 0);
|
||||
this.buffer[index | 3] = this.processPixelNormal(this.buffer[index | 0x703] | 0, this.buffer[index | 0x803] | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processWindowNormalEffectsNoSprites = function (xStart, xEnd) {
|
||||
xStart = xStart | 0;
|
||||
xEnd = xEnd | 0;
|
||||
while ((xStart | 0) < (xEnd | 0)) {
|
||||
this.buffer[xStart | 0] = this.processPixelNormal(this.buffer[xStart | 0x700] | 0, this.buffer[xStart | 0x800] | 0);
|
||||
xStart = ((xStart | 0) + 1) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processFullNormalEffectsWithSprites = function () {
|
||||
for (var index = 0; (index | 0) < 240; index = ((index | 0) + 4) | 0) {
|
||||
this.buffer[index | 0] = this.processPixelTestFull(this.buffer[index | 0x700] | 0, this.buffer[index | 0x800] | 0);
|
||||
this.buffer[index | 1] = this.processPixelTestFull(this.buffer[index | 0x701] | 0, this.buffer[index | 0x801] | 0);
|
||||
this.buffer[index | 2] = this.processPixelTestFull(this.buffer[index | 0x702] | 0, this.buffer[index | 0x802] | 0);
|
||||
this.buffer[index | 3] = this.processPixelTestFull(this.buffer[index | 0x703] | 0, this.buffer[index | 0x803] | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processWindowNormalEffectsWithSprites = function (xStart, xEnd) {
|
||||
xStart = xStart | 0;
|
||||
xEnd = xEnd | 0;
|
||||
while ((xStart | 0) < (xEnd | 0)) {
|
||||
this.buffer[xStart | 0] = this.processPixelTestFull(this.buffer[xStart | 0x700] | 0, this.buffer[xStart | 0x800] | 0);
|
||||
xStart = ((xStart | 0) + 1) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processFullNoEffectsWithSprites = function () {
|
||||
for (var index = 0; (index | 0) < 240; index = ((index | 0) + 4) | 0) {
|
||||
this.buffer[index | 0] = this.processPixelTestSprite(this.buffer[index | 0x700] | 0, this.buffer[index | 0x800] | 0);
|
||||
this.buffer[index | 1] = this.processPixelTestSprite(this.buffer[index | 0x701] | 0, this.buffer[index | 0x801] | 0);
|
||||
this.buffer[index | 2] = this.processPixelTestSprite(this.buffer[index | 0x702] | 0, this.buffer[index | 0x802] | 0);
|
||||
this.buffer[index | 3] = this.processPixelTestSprite(this.buffer[index | 0x703] | 0, this.buffer[index | 0x803] | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processWindowNoEffectsWithSprites = function (xStart, xEnd) {
|
||||
xStart = xStart | 0;
|
||||
xEnd = xEnd | 0;
|
||||
while ((xStart | 0) < (xEnd | 0)) {
|
||||
this.buffer[xStart | 0] = this.processPixelTestSprite(this.buffer[xStart | 0x700] | 0, this.buffer[xStart | 0x800] | 0);
|
||||
xStart = ((xStart | 0) + 1) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processPixelTestFull = function (lowerPixel, currentPixel) {
|
||||
lowerPixel = lowerPixel | 0;
|
||||
currentPixel = currentPixel | 0;
|
||||
if ((currentPixel & 0x400000) == 0) {
|
||||
return this.processPixelNormal(lowerPixel | 0, currentPixel | 0) | 0;
|
||||
}
|
||||
else {
|
||||
return this.processPixelSprite(lowerPixel | 0, currentPixel | 0) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processPixelTestSprite = function (lowerPixel, currentPixel) {
|
||||
lowerPixel = lowerPixel | 0;
|
||||
currentPixel = currentPixel | 0;
|
||||
if ((currentPixel & 0x400000) == 0) {
|
||||
return currentPixel | 0;
|
||||
}
|
||||
else {
|
||||
return this.processPixelSprite(lowerPixel | 0, currentPixel | 0) | 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
//No SIMD support found, use the scalar path instead:
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.initialize = function (buffer) {
|
||||
this.alphaBlendAmountTarget1 = 0;
|
||||
this.alphaBlendAmountTarget2 = 0;
|
||||
this.brightnessEffectAmount = 0;
|
||||
}
|
||||
if (typeof Math.imul == "function") {
|
||||
//Math.imul found, insert the optimized path in:
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlend = function (topPixel, lowerPixel) {
|
||||
topPixel = topPixel | 0;
|
||||
lowerPixel = lowerPixel | 0;
|
||||
var b1 = (topPixel >> 10) & 0x1F;
|
||||
var g1 = (topPixel >> 5) & 0x1F;
|
||||
var r1 = topPixel & 0x1F;
|
||||
var b2 = (lowerPixel >> 10) & 0x1F;
|
||||
var g2 = (lowerPixel >> 5) & 0x1F;
|
||||
var r2 = lowerPixel & 0x1F;
|
||||
b1 = Math.imul(b1 | 0, this.alphaBlendAmountTarget1 | 0) | 0;
|
||||
g1 = Math.imul(g1 | 0, this.alphaBlendAmountTarget1 | 0) | 0;
|
||||
r1 = Math.imul(r1 | 0, this.alphaBlendAmountTarget1 | 0) | 0;
|
||||
b2 = Math.imul(b2 | 0, this.alphaBlendAmountTarget2 | 0) | 0;
|
||||
g2 = Math.imul(g2 | 0, this.alphaBlendAmountTarget2 | 0) | 0;
|
||||
r2 = Math.imul(r2 | 0, this.alphaBlendAmountTarget2 | 0) | 0;
|
||||
//Keep this not inlined in the return, firefox 22 grinds on it:
|
||||
var b = Math.min(((b1 | 0) + (b2 | 0)) >> 4, 0x1F) | 0;
|
||||
var g = Math.min(((g1 | 0) + (g2 | 0)) >> 4, 0x1F) | 0;
|
||||
var r = Math.min(((r1 | 0) + (r2 | 0)) >> 4, 0x1F) | 0;
|
||||
return (b << 10) | (g << 5) | r;
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.brightnessIncrease = function (topPixel) {
|
||||
topPixel = topPixel | 0;
|
||||
var b1 = (topPixel >> 10) & 0x1F;
|
||||
var g1 = (topPixel >> 5) & 0x1F;
|
||||
var r1 = topPixel & 0x1F;
|
||||
b1 = ((b1 | 0) + (Math.imul((0x1F - (b1 | 0)) | 0, this.brightnessEffectAmount | 0) >> 4)) | 0;
|
||||
g1 = ((g1 | 0) + (Math.imul((0x1F - (g1 | 0)) | 0, this.brightnessEffectAmount | 0) >> 4)) | 0;
|
||||
r1 = ((r1 | 0) + (Math.imul((0x1F - (r1 | 0)) | 0, this.brightnessEffectAmount | 0) >> 4)) | 0;
|
||||
return (b1 << 10) | (g1 << 5) | r1;
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.brightnessDecrease = function (topPixel) {
|
||||
topPixel = topPixel | 0;
|
||||
var b1 = (topPixel >> 10) & 0x1F;
|
||||
var g1 = (topPixel >> 5) & 0x1F;
|
||||
var r1 = topPixel & 0x1F;
|
||||
var decreaseMultiplier = (0x10 - (this.brightnessEffectAmount | 0)) | 0;
|
||||
b1 = Math.imul(b1 | 0, decreaseMultiplier | 0) >> 4;
|
||||
g1 = Math.imul(g1 | 0, decreaseMultiplier | 0) >> 4;
|
||||
r1 = Math.imul(r1 | 0, decreaseMultiplier | 0) >> 4;
|
||||
return (b1 << 10) | (g1 << 5) | r1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Math.imul not found, use the compatibility method:
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.alphaBlend = function (topPixel, lowerPixel) {
|
||||
topPixel = topPixel | 0;
|
||||
lowerPixel = lowerPixel | 0;
|
||||
var b1 = (topPixel >> 10) & 0x1F;
|
||||
var g1 = (topPixel >> 5) & 0x1F;
|
||||
var r1 = (topPixel & 0x1F);
|
||||
var b2 = (lowerPixel >> 10) & 0x1F;
|
||||
var g2 = (lowerPixel >> 5) & 0x1F;
|
||||
var r2 = lowerPixel & 0x1F;
|
||||
b1 = b1 * this.alphaBlendAmountTarget1;
|
||||
g1 = g1 * this.alphaBlendAmountTarget1;
|
||||
r1 = r1 * this.alphaBlendAmountTarget1;
|
||||
b2 = b2 * this.alphaBlendAmountTarget2;
|
||||
g2 = g2 * this.alphaBlendAmountTarget2;
|
||||
r2 = r2 * this.alphaBlendAmountTarget2;
|
||||
return (Math.min((b1 + b2) >> 4, 0x1F) << 10) | (Math.min((g1 + g2) >> 4, 0x1F) << 5) | Math.min((r1 + r2) >> 4, 0x1F);
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.brightnessIncrease = function (topPixel) {
|
||||
topPixel = topPixel | 0;
|
||||
var b1 = (topPixel >> 10) & 0x1F;
|
||||
var g1 = (topPixel >> 5) & 0x1F;
|
||||
var r1 = topPixel & 0x1F;
|
||||
b1 += ((0x1F - b1) * this.brightnessEffectAmount) >> 4;
|
||||
g1 += ((0x1F - g1) * this.brightnessEffectAmount) >> 4;
|
||||
r1 += ((0x1F - r1) * this.brightnessEffectAmount) >> 4;
|
||||
return (b1 << 10) | (g1 << 5) | r1;
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.brightnessDecrease = function (topPixel) {
|
||||
topPixel = topPixel | 0;
|
||||
var b1 = (topPixel >> 10) & 0x1F;
|
||||
var g1 = (topPixel >> 5) & 0x1F;
|
||||
var r1 = topPixel & 0x1F;
|
||||
var decreaseMultiplier = 0x10 - this.brightnessEffectAmount;
|
||||
b1 = (b1 * decreaseMultiplier) >> 4;
|
||||
g1 = (g1 * decreaseMultiplier) >> 4;
|
||||
r1 = (r1 * decreaseMultiplier) >> 4;
|
||||
return (b1 << 10) | (g1 << 5) | r1;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA8_0 = function (data) {
|
||||
data = data | 0;
|
||||
var alphaBlendAmountTarget1Scratch = data & 0x1F;
|
||||
this.alphaBlendAmountTarget1 = Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0;
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA8_1 = function (data) {
|
||||
data = data | 0;
|
||||
var alphaBlendAmountTarget2Scratch = data & 0x1F;
|
||||
this.alphaBlendAmountTarget2 = Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0;
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDALPHA16 = function (data) {
|
||||
data = data | 0;
|
||||
var alphaBlendAmountTarget1Scratch = data & 0x1F;
|
||||
this.alphaBlendAmountTarget1 = Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0;
|
||||
var alphaBlendAmountTarget2Scratch = (data >> 8) & 0x1F;
|
||||
this.alphaBlendAmountTarget2 = Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0;
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDCNT32 = function (data) {
|
||||
data = data | 0;
|
||||
//Select target 1 and color effects mode:
|
||||
this.effectsTarget1 = (data & 0x3F) << 16;
|
||||
this.colorEffectsType = (data >> 6) & 0x3;
|
||||
//Select target 2:
|
||||
this.effectsTarget2 = (data & 0x3F00) << 8;
|
||||
var alphaBlendAmountTarget1Scratch = (data >> 16) & 0x1F;
|
||||
this.alphaBlendAmountTarget1 = Math.min(alphaBlendAmountTarget1Scratch | 0, 0x10) | 0;
|
||||
var alphaBlendAmountTarget2Scratch = (data >> 24) & 0x1F;
|
||||
this.alphaBlendAmountTarget2 = Math.min(alphaBlendAmountTarget2Scratch | 0, 0x10) | 0;
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDY8 = function (data) {
|
||||
data = data | 0;
|
||||
this.brightnessEffectAmount = Math.min(data & 0x1F, 0x10) | 0;
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processPixelNormal = function (lowerPixel, topPixel) {
|
||||
lowerPixel = lowerPixel | 0;
|
||||
topPixel = topPixel | 0;
|
||||
if (((topPixel | 0) & (this.effectsTarget1 | 0)) != 0) {
|
||||
switch (this.colorEffectsType | 0) {
|
||||
case 1:
|
||||
if (((lowerPixel | 0) & (this.effectsTarget2 | 0)) != 0 && (topPixel | 0) != (lowerPixel | 0)) {
|
||||
return this.alphaBlend(topPixel | 0, lowerPixel | 0) | 0;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
return this.brightnessIncrease(topPixel | 0) | 0;
|
||||
case 3:
|
||||
return this.brightnessDecrease(topPixel | 0) | 0;
|
||||
}
|
||||
}
|
||||
return topPixel | 0;
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.processPixelSprite = function (lowerPixel, topPixel) {
|
||||
lowerPixel = lowerPixel | 0;
|
||||
topPixel = topPixel | 0;
|
||||
if (((lowerPixel | 0) & (this.effectsTarget2 | 0)) != 0) {
|
||||
return this.alphaBlend(topPixel | 0, lowerPixel | 0) | 0;
|
||||
}
|
||||
else if (((topPixel | 0) & (this.effectsTarget1 | 0)) != 0) {
|
||||
switch (this.colorEffectsType | 0) {
|
||||
case 2:
|
||||
return this.brightnessIncrease(topPixel | 0) | 0;
|
||||
case 3:
|
||||
return this.brightnessDecrease(topPixel | 0) | 0;
|
||||
}
|
||||
}
|
||||
return topPixel | 0;
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDCNT8_0 = function (data) {
|
||||
data = data | 0;
|
||||
//Select target 1 and color effects mode:
|
||||
this.effectsTarget1 = (data & 0x3F) << 16;
|
||||
this.colorEffectsType = data >> 6;
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDCNT8_1 = function (data) {
|
||||
data = data | 0;
|
||||
//Select target 2:
|
||||
this.effectsTarget2 = (data & 0x3F) << 16;
|
||||
}
|
||||
GameBoyAdvanceColorEffectsRenderer.prototype.writeBLDCNT16 = function (data) {
|
||||
data = data | 0;
|
||||
//Select target 1 and color effects mode:
|
||||
this.effectsTarget1 = (data & 0x3F) << 16;
|
||||
this.colorEffectsType = (data >> 6) & 0x3;
|
||||
//Select target 2:
|
||||
this.effectsTarget2 = (data & 0x3F00) << 8;
|
||||
}
|
||||
@@ -0,0 +1,949 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2016 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceCompositor(gfx) {
|
||||
this.gfx = gfx;
|
||||
this.doEffects = 0;
|
||||
}
|
||||
function GameBoyAdvanceWindowCompositor(gfx) {
|
||||
this.gfx = gfx;
|
||||
this.doEffects = 0;
|
||||
}
|
||||
function GameBoyAdvanceOBJWindowCompositor(gfx) {
|
||||
this.gfx = gfx;
|
||||
this.doEffects = 0;
|
||||
}
|
||||
GameBoyAdvanceCompositor.prototype.initialize = GameBoyAdvanceWindowCompositor.prototype.initialize = function () {
|
||||
this.buffer = this.gfx.buffer;
|
||||
this.colorEffectsRenderer = this.gfx.colorEffectsRenderer;
|
||||
}
|
||||
GameBoyAdvanceOBJWindowCompositor.prototype.initialize = function () {
|
||||
this.buffer = this.gfx.buffer;
|
||||
this.colorEffectsRenderer = this.gfx.colorEffectsRenderer;
|
||||
this.OBJWindowBuffer = this.gfx.objRenderer.scratchWindowBuffer;
|
||||
}
|
||||
GameBoyAdvanceCompositor.prototype.preprocess = GameBoyAdvanceWindowCompositor.prototype.preprocess = GameBoyAdvanceOBJWindowCompositor.prototype.preprocess = function (doEffects) {
|
||||
doEffects = doEffects | 0;
|
||||
this.doEffects = doEffects | 0;
|
||||
}
|
||||
GameBoyAdvanceOBJWindowCompositor.prototype.renderScanLine = GameBoyAdvanceCompositor.prototype.renderScanLine = function (layers) {
|
||||
layers = layers | 0;
|
||||
if ((this.doEffects | 0) == 0) {
|
||||
this.renderNormalScanLine(layers | 0);
|
||||
}
|
||||
else {
|
||||
this.renderScanLineWithEffects(layers | 0);
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceWindowCompositor.prototype.renderScanLine = function (xStart, xEnd, layers) {
|
||||
xStart = xStart | 0;
|
||||
xEnd = xEnd | 0;
|
||||
layers = layers | 0;
|
||||
if ((this.doEffects | 0) == 0) {
|
||||
this.renderNormalScanLine(xStart | 0, xEnd | 0, layers | 0);
|
||||
}
|
||||
else {
|
||||
this.renderScanLineWithEffects(xStart | 0, xEnd | 0, layers | 0);
|
||||
}
|
||||
}
|
||||
//Check for SIMD support:
|
||||
if (typeof SIMD == "object" && typeof SIMD.Int32x4 == "function") {
|
||||
GameBoyAdvanceCompositor.prototype.mask0 = SIMD.Int32x4.splat(0);
|
||||
GameBoyAdvanceCompositor.prototype.mask1 = SIMD.Int32x4.splat(0x2000000);
|
||||
GameBoyAdvanceCompositor.prototype.mask2 = SIMD.Int32x4.splat(0x3800000);
|
||||
GameBoyAdvanceCompositor.prototype.mask3 = SIMD.Int32x4.splat(0x1800000);
|
||||
GameBoyAdvanceCompositor.prototype.mask4 = SIMD.Bool32x4.splat(true);
|
||||
}
|
||||
function generateIodineGBAGFXCompositors() {
|
||||
function generateCompositors() {
|
||||
function generateLoop(compositeType, doEffects, layers) {
|
||||
function generateLoopHead(useVectorized, compositeType) {
|
||||
function generateLocalScopeInit(useVectorized, layers, alreadyDeclared) {
|
||||
function checkDeclared(alreadyDeclared) {
|
||||
var code = "";
|
||||
if (!alreadyDeclared) {
|
||||
code += "var ";
|
||||
}
|
||||
return code;
|
||||
}
|
||||
//Declare the necessary temporary variables:
|
||||
var code = "";
|
||||
if (useVectorized) {
|
||||
//Declare the necessary temporary variables:
|
||||
code +=
|
||||
"var backdrop = SIMD.Int32x4.splat(this.gfx.backdrop | 0);";
|
||||
switch (layers) {
|
||||
case 0:
|
||||
//Don't need any if no layers to process:
|
||||
break;
|
||||
default:
|
||||
//Need this temp for more than one layer:
|
||||
code +=
|
||||
checkDeclared(alreadyDeclared) + "workingPixel = this.mask0;" +
|
||||
"var test1 = this.mask4;" +
|
||||
"var test2 = this.mask4;";
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
case 0x4:
|
||||
case 0x8:
|
||||
case 0x10:
|
||||
//Need these temps for one or more layers:
|
||||
code +=
|
||||
checkDeclared(alreadyDeclared) + "currentPixel = this.mask0;" +
|
||||
checkDeclared(alreadyDeclared) + "lowerPixel = this.mask0;";
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (layers) {
|
||||
case 0:
|
||||
//Don't need any if no layers to process:
|
||||
break;
|
||||
default:
|
||||
//Need this temp for more than one layer:
|
||||
code +=
|
||||
checkDeclared(alreadyDeclared) + "workingPixel = 0;";
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
case 0x4:
|
||||
case 0x8:
|
||||
case 0x10:
|
||||
//Need these temps for one or more layers:
|
||||
code +=
|
||||
checkDeclared(alreadyDeclared) + "currentPixel = 0;" +
|
||||
checkDeclared(alreadyDeclared) + "lowerPixel = 0;";
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
function generateLoopBody(useVectorized, doEffects, layers) {
|
||||
function getSingleLayerPrefix(useVectorized) {
|
||||
//Pass initialization if processing only 1 layer:
|
||||
var code = "";
|
||||
if (useVectorized) {
|
||||
code +=
|
||||
"lowerPixel = backdrop;";
|
||||
}
|
||||
else {
|
||||
code +=
|
||||
"lowerPixel = this.gfx.backdrop | 0;";
|
||||
}
|
||||
return code;
|
||||
}
|
||||
function getMultiLayerPrefix(useVectorized) {
|
||||
//Pass initialization if processing more than 1 layer:
|
||||
var code = "";
|
||||
if (useVectorized) {
|
||||
code +=
|
||||
"lowerPixel = backdrop;" +
|
||||
"currentPixel = lowerPixel;";
|
||||
}
|
||||
else {
|
||||
code +=
|
||||
"lowerPixel = this.gfx.backdrop | 0;" +
|
||||
"currentPixel = lowerPixel | 0;";
|
||||
}
|
||||
return code;
|
||||
}
|
||||
function generateLayerCompareSingle(useVectorized, layerOffset) {
|
||||
//Only 1 layer specified to be rendered:
|
||||
var code = "";
|
||||
if (useVectorized) {
|
||||
code +=
|
||||
"currentPixel = SIMD.Int32x4.load(this.buffer, xStart | " + layerOffset + ");" +
|
||||
"currentPixel = SIMD.Int32x4.select(" +
|
||||
"SIMD.Int32x4.notEqual(" +
|
||||
"this.mask0," +
|
||||
"SIMD.Int32x4.and(currentPixel, this.mask1)" +
|
||||
")," +
|
||||
"lowerPixel," +
|
||||
"currentPixel" +
|
||||
");";
|
||||
}
|
||||
else {
|
||||
code +=
|
||||
"currentPixel = this.buffer[xStart | " + layerOffset + "] | 0;" +
|
||||
"if ((currentPixel & 0x2000000) != 0) {" +
|
||||
"currentPixel = lowerPixel | 0;" +
|
||||
"}";
|
||||
}
|
||||
return code;
|
||||
}
|
||||
function generateLayerCompare(useVectorized, layerOffset) {
|
||||
//Code unit to be used when rendering more than 1 layer:
|
||||
var code = "";
|
||||
if (useVectorized) {
|
||||
code +=
|
||||
"workingPixel = SIMD.Int32x4.load(this.buffer, xStart | " + layerOffset + ");" +
|
||||
"test1 = SIMD.Int32x4.lessThanOrEqual(" +
|
||||
"SIMD.Int32x4.and(workingPixel, this.mask2)," +
|
||||
"SIMD.Int32x4.and(currentPixel, this.mask3)" +
|
||||
");" +
|
||||
"lowerPixel = SIMD.Int32x4.select(test1, currentPixel, lowerPixel);" +
|
||||
"currentPixel = SIMD.Int32x4.select(test1, workingPixel, currentPixel);" +
|
||||
"test2 = SIMD.Int32x4.lessThanOrEqual(" +
|
||||
"SIMD.Int32x4.and(workingPixel, this.mask2)," +
|
||||
"SIMD.Int32x4.and(lowerPixel, this.mask3)" +
|
||||
");" +
|
||||
"lowerPixel = SIMD.Int32x4.select(" +
|
||||
"test1," +
|
||||
"lowerPixel," +
|
||||
"SIMD.Int32x4.select(test2, workingPixel, lowerPixel)" +
|
||||
");";
|
||||
}
|
||||
else {
|
||||
code +=
|
||||
"workingPixel = this.buffer[xStart | " + layerOffset + "] | 0;" +
|
||||
"if ((workingPixel & 0x3800000) <= (currentPixel & 0x1800000)) {" +
|
||||
"lowerPixel = currentPixel | 0;" +
|
||||
"currentPixel = workingPixel | 0;" +
|
||||
"}" +
|
||||
"else if ((workingPixel & 0x3800000) <= (lowerPixel & 0x1800000)) {" +
|
||||
"lowerPixel = workingPixel | 0;" +
|
||||
"}";
|
||||
}
|
||||
return code;
|
||||
}
|
||||
function getColorEffects0Layers(useVectorized, doEffects) {
|
||||
//Handle checks for color effects here:
|
||||
var code = "";
|
||||
if (useVectorized) {
|
||||
//No layers:
|
||||
if (doEffects) {
|
||||
//Color effects enabled:
|
||||
code +=
|
||||
"SIMD.Int32x4.store(this.buffer, xStart | 0x700, this.mask0);" +
|
||||
"SIMD.Int32x4.store(this.buffer, xStart | 0x800, backdrop);";
|
||||
}
|
||||
else {
|
||||
//No effects enabled:
|
||||
code +=
|
||||
"SIMD.Int32x4.store(this.buffer, xStart | 0, backdrop);";
|
||||
}
|
||||
}
|
||||
else {
|
||||
//No layers:
|
||||
if (doEffects) {
|
||||
//Color effects enabled:
|
||||
code +=
|
||||
"this.buffer[xStart | 0] = this.colorEffectsRenderer.processPixelNormal(0, this.gfx.backdrop | 0) | 0;";
|
||||
}
|
||||
else {
|
||||
//No effects enabled:
|
||||
code +=
|
||||
"this.buffer[xStart | 0] = this.gfx.backdrop | 0;"
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
function getColorEffectsNoSprites(useVectorized, doEffects) {
|
||||
//Handle checks for color effects here:
|
||||
var code = "";
|
||||
if (useVectorized) {
|
||||
//Rendering with no sprite layer:
|
||||
if (doEffects) {
|
||||
//Color effects enabled:
|
||||
code +=
|
||||
"SIMD.Int32x4.store(this.buffer, xStart | 0x700, lowerPixel);" +
|
||||
"SIMD.Int32x4.store(this.buffer, xStart | 0x800, currentPixel);";
|
||||
}
|
||||
else {
|
||||
//No effects enabled:
|
||||
code +=
|
||||
"SIMD.Int32x4.store(this.buffer, xStart | 0, currentPixel);";
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Rendering with no sprite layer:
|
||||
if (doEffects) {
|
||||
//Color effects enabled:
|
||||
code +=
|
||||
"this.buffer[xStart | 0] = this.colorEffectsRenderer.processPixelNormal(lowerPixel | 0, currentPixel | 0) | 0;";
|
||||
}
|
||||
else {
|
||||
//No effects enabled:
|
||||
code +=
|
||||
"this.buffer[xStart | 0] = currentPixel | 0;";
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
function getColorEffectsWithSprites(useVectorized, doEffects) {
|
||||
//Handle checks for color effects here:
|
||||
var code = "";
|
||||
if (useVectorized) {
|
||||
//Rendering with a sprite layer:
|
||||
code +=
|
||||
"SIMD.Int32x4.store(this.buffer, xStart | 0x700, lowerPixel);" +
|
||||
"SIMD.Int32x4.store(this.buffer, xStart | 0x800, currentPixel);";
|
||||
}
|
||||
else {
|
||||
//Rendering with a sprite layer:
|
||||
code +=
|
||||
"if ((currentPixel & 0x400000) == 0) {";
|
||||
if (doEffects) {
|
||||
//Color effects enabled:
|
||||
code +=
|
||||
"this.buffer[xStart | 0] = this.colorEffectsRenderer.processPixelNormal(lowerPixel | 0, currentPixel | 0) | 0;";
|
||||
}
|
||||
else {
|
||||
//No effects enabled:
|
||||
code +=
|
||||
"this.buffer[xStart | 0] = currentPixel | 0;";
|
||||
}
|
||||
code +=
|
||||
"}" +
|
||||
"else {" +
|
||||
//Must handle for semi-transparent sprite case:
|
||||
"this.buffer[xStart | 0] = this.colorEffectsRenderer.processPixelSprite(lowerPixel | 0, currentPixel | 0) | 0;" +
|
||||
"}";
|
||||
}
|
||||
return code;
|
||||
}
|
||||
function generatePass(useVectorized, doEffects, layers) {
|
||||
var code = "";
|
||||
//Special case each possible layer combination:
|
||||
switch (layers) {
|
||||
case 0:
|
||||
//Backdrop only:
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffects0Layers(useVectorized, doEffects);
|
||||
break;
|
||||
case 1:
|
||||
//Generate temps:
|
||||
code += getSingleLayerPrefix(useVectorized);
|
||||
//BG0:
|
||||
code += generateLayerCompareSingle(useVectorized, 0x100);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 2:
|
||||
//Generate temps:
|
||||
code += getSingleLayerPrefix(useVectorized);
|
||||
//BG1:
|
||||
code += generateLayerCompareSingle(useVectorized, 0x200);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 3:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 4:
|
||||
//Generate temps:
|
||||
code += getSingleLayerPrefix(useVectorized);
|
||||
//BG2:
|
||||
code += generateLayerCompareSingle(useVectorized, 0x300);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 5:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 6:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 7:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 8:
|
||||
//Generate temps:
|
||||
code += getSingleLayerPrefix(useVectorized);
|
||||
//BG2:
|
||||
code += generateLayerCompareSingle(useVectorized, 0x400);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 9:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0xA:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0xB:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0xC:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0xD:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0xE:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0xF:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsNoSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x10:
|
||||
//Generate temps:
|
||||
code += getSingleLayerPrefix(useVectorized);
|
||||
//OBJ:
|
||||
code += generateLayerCompareSingle(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x11:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x12:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x13:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x14:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x15:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x16:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x17:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x18:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x19:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x1A:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x1B:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x1C:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x1D:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
case 0x1E:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
break;
|
||||
default:
|
||||
//Generate temps:
|
||||
code += getMultiLayerPrefix(useVectorized);
|
||||
//BG3:
|
||||
code += generateLayerCompare(useVectorized, 0x400);
|
||||
//BG2:
|
||||
code += generateLayerCompare(useVectorized, 0x300);
|
||||
//BG1:
|
||||
code += generateLayerCompare(useVectorized, 0x200);
|
||||
//BG0:
|
||||
code += generateLayerCompare(useVectorized, 0x100);
|
||||
//OBJ:
|
||||
code += generateLayerCompare(useVectorized, 0x500);
|
||||
//Color Effects Post Processing:
|
||||
code += getColorEffectsWithSprites(useVectorized, doEffects);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
//Build the code to put inside a loop:
|
||||
return generatePass(useVectorized, doEffects, layers);
|
||||
}
|
||||
function generateSIMDColorEffectsExternalCall(useVectorized, layers, compositeType) {
|
||||
var code = "";
|
||||
if (useVectorized) {
|
||||
switch (compositeType) {
|
||||
case 0:
|
||||
//Check if we're processing the sprite layer:
|
||||
if (layers < 0x10) {
|
||||
//Don't need color effects processing for the else case:
|
||||
if (doEffects) {
|
||||
//Effects handling:
|
||||
code +=
|
||||
";this.colorEffectsRenderer.processFullNormalEffectsNoSprites()";
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (doEffects) {
|
||||
//Effects + semi-transparency handling:
|
||||
code +=
|
||||
";this.colorEffectsRenderer.processFullNormalEffectsWithSprites()";
|
||||
}
|
||||
else {
|
||||
//Sprite semi-transparency handling:
|
||||
code +=
|
||||
";this.colorEffectsRenderer.processFullNoEffectsWithSprites()";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
//Check if we're processing the sprite layer:
|
||||
if (layers < 0x10) {
|
||||
//Don't need color effects processing for the else case:
|
||||
if (doEffects) {
|
||||
//Effects handling:
|
||||
code +=
|
||||
";this.colorEffectsRenderer.processWindowNormalEffectsNoSprites(xStartCopy | 0, xEnd & -4)";
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (doEffects) {
|
||||
//Effects + semi-transparency handling:
|
||||
code +=
|
||||
";this.colorEffectsRenderer.processWindowNormalEffectsWithSprites(xStartCopy | 0, xEnd & -4)";
|
||||
}
|
||||
else {
|
||||
//Sprite semi-transparency handling:
|
||||
code +=
|
||||
";this.colorEffectsRenderer.processWindowNoEffectsWithSprites(xStartCopy | 0, xEnd & -4)";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
var code = "";
|
||||
switch (compositeType) {
|
||||
//Loop for normal compositor:
|
||||
case 0:
|
||||
if (useVectorized) {
|
||||
code +=
|
||||
generateLocalScopeInit(true, layers, false) +
|
||||
"for (var xStart = 0; (xStart | 0) < 240; xStart = ((xStart | 0) + 4) | 0) {" +
|
||||
generateLoopBody(true, doEffects, layers) +
|
||||
"}";
|
||||
}
|
||||
else {
|
||||
code +=
|
||||
generateLocalScopeInit(false, layers, false) +
|
||||
"for (var xStart = 0; (xStart | 0) < 240; xStart = ((xStart | 0) + 1) | 0) {" +
|
||||
generateLoopBody(false, doEffects, layers) +
|
||||
"}";
|
||||
}
|
||||
break;
|
||||
//Loop for window compositor:
|
||||
case 1:
|
||||
code +=
|
||||
"xStart = xStart | 0;" +
|
||||
"xEnd = xEnd | 0;";
|
||||
if (useVectorized) {
|
||||
code +=
|
||||
generateLocalScopeInit(false, layers, false) +
|
||||
"while ((xStart | 0) < (xEnd | 0) && (xStart | 0) <= (xStart | 0x3)) {" +
|
||||
generateLoopBody(false, doEffects, layers) +
|
||||
"xStart = ((xStart | 0) + 1) | 0;" +
|
||||
"}" +
|
||||
"var xStartCopy = xStart | 0;" +
|
||||
generateLocalScopeInit(true, layers, true) +
|
||||
"while ((xStart | 0) < (xEnd & -4)) {" +
|
||||
generateLoopBody(true, doEffects, layers) +
|
||||
"xStart = ((xStart | 0) + 4) | 0;" +
|
||||
"}" +
|
||||
generateLocalScopeInit(false, layers, true) +
|
||||
"while ((xStart | 0) < (xEnd | 0)) {" +
|
||||
generateLoopBody(false, doEffects, layers) +
|
||||
"xStart = ((xStart | 0) + 1) | 0;" +
|
||||
"}";
|
||||
}
|
||||
else {
|
||||
code +=
|
||||
generateLocalScopeInit(false, layers, false) +
|
||||
"while ((xStart | 0) < (xEnd | 0)) {" +
|
||||
generateLoopBody(false, doEffects, layers) +
|
||||
"xStart = ((xStart | 0) + 1) | 0;" +
|
||||
"}";
|
||||
}
|
||||
break;
|
||||
//Loop for OBJ window compositor:
|
||||
case 2:
|
||||
code +=
|
||||
generateLocalScopeInit(false, layers, false) +
|
||||
"for (var xStart = 0; (xStart | 0) < 240; xStart = ((xStart | 0) + 1) | 0) {" +
|
||||
"if ((this.OBJWindowBuffer[xStart | 0] | 0) < 0x3800000) {" +
|
||||
generateLoopBody(false, doEffects, layers) +
|
||||
"}" +
|
||||
"}";
|
||||
}
|
||||
code += generateSIMDColorEffectsExternalCall(useVectorized, layers, compositeType);
|
||||
return code;
|
||||
}
|
||||
//Build the loop:
|
||||
return generateLoopHead(typeof SIMD == "object" && typeof SIMD.Int32x4 == "function", compositeType);
|
||||
}
|
||||
function generateCompositor(compositeType, doEffects) {
|
||||
//Get function suffix we'll use depending on color effects usage:
|
||||
var effectsPrefix = (doEffects) ? "special" : "normal";
|
||||
//Loop through all possible combinations of layers:
|
||||
for (var layers = 0; layers < 0x20; layers++) {
|
||||
//Codegen the loop:
|
||||
var code = generateLoop(compositeType, doEffects, layers);
|
||||
//Compile the code and assign to appropriate compositor object:
|
||||
switch (compositeType) {
|
||||
case 0:
|
||||
//Normal compositor:
|
||||
GameBoyAdvanceCompositor.prototype[effectsPrefix + layers] = Function(code);
|
||||
break;
|
||||
case 1:
|
||||
//Window compositor:
|
||||
GameBoyAdvanceWindowCompositor.prototype[effectsPrefix + layers] = Function("xStart", "xEnd", code);
|
||||
break;
|
||||
default:
|
||||
//OBJ window compositor:
|
||||
GameBoyAdvanceOBJWindowCompositor.prototype[effectsPrefix + layers] = Function(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
//Build the functions for each of the three compositors:
|
||||
for (var compositeType = 0; compositeType < 3; compositeType++) {
|
||||
//Build for the no special effects processing case:
|
||||
generateCompositor(compositeType, false);
|
||||
//Build for the special effects processing case:
|
||||
generateCompositor(compositeType, true);
|
||||
}
|
||||
}
|
||||
function generateDispatches() {
|
||||
function generateDispatch(coordsSpecified, doEffects) {
|
||||
function generateDispatchBlock(coordsSpecified, doEffects) {
|
||||
function generateDispatchHead(coordsSpecified) {
|
||||
//Initialize some local variables:
|
||||
var code = "";
|
||||
if (coordsSpecified) {
|
||||
code +=
|
||||
"xStart = xStart | 0;" +
|
||||
"xEnd = xEnd | 0;";
|
||||
}
|
||||
code +=
|
||||
"layers = layers | 0;";
|
||||
return code;
|
||||
}
|
||||
function generateDispatchBody(coordsSpecified, doEffects) {
|
||||
function generateSwitchHead(bodyCode) {
|
||||
//We're building a switch statement:
|
||||
var code =
|
||||
"switch (layers | 0) {" +
|
||||
bodyCode +
|
||||
"}";
|
||||
return code;
|
||||
}
|
||||
function generateSwitchBody(coordsSpecified, doEffects) {
|
||||
function generateCases(coordsSpecified, doEffects) {
|
||||
function generateCase(layers, coordsSpecified, doEffects) {
|
||||
function generateCaseHead(layers, bodyCode) {
|
||||
//Building the case label:
|
||||
var code = "";
|
||||
if (layers < 0x1F) {
|
||||
//Not the last case in the list, so specify number:
|
||||
code +=
|
||||
"case " + layers + ":" +
|
||||
"{" +
|
||||
bodyCode + ";" +
|
||||
"break" +
|
||||
"};";
|
||||
}
|
||||
else {
|
||||
//Last case in the list, so place it as default case:
|
||||
code +=
|
||||
"default:" +
|
||||
"{" +
|
||||
bodyCode +
|
||||
"}";
|
||||
}
|
||||
return code;
|
||||
}
|
||||
function generateCaseBody(layers, coordsSpecified, doEffects) {
|
||||
//Build the function call:
|
||||
var code =
|
||||
"this.";
|
||||
if (doEffects) {
|
||||
//Special effects:
|
||||
code +=
|
||||
"special";
|
||||
}
|
||||
else {
|
||||
//No special effects:
|
||||
code +=
|
||||
"normal";
|
||||
}
|
||||
code +=
|
||||
layers +
|
||||
"(";
|
||||
if (coordsSpecified) {
|
||||
//Passing some xcoords as parameters:
|
||||
code +=
|
||||
"xStart | 0, xEnd | 0";
|
||||
}
|
||||
code +=
|
||||
")";
|
||||
return code;
|
||||
}
|
||||
//Build the full case unit:
|
||||
return generateCaseHead(layers, generateCaseBody(layers, coordsSpecified, doEffects));
|
||||
}
|
||||
function generateList(coordsSpecified, doEffects) {
|
||||
var code = "";
|
||||
//Loop through all combinations to build:
|
||||
for (var layers = 0; layers < 0x20; layers++) {
|
||||
//Build a case for the specified combination:
|
||||
code += generateCase(layers, coordsSpecified, doEffects);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
//Build the entire switch:
|
||||
return generateList(coordsSpecified, doEffects);
|
||||
}
|
||||
//Build the entire switch:
|
||||
return generateCases(coordsSpecified, doEffects);
|
||||
}
|
||||
//Build the switch block:
|
||||
return generateSwitchHead(generateSwitchBody(coordsSpecified, doEffects));
|
||||
}
|
||||
function generateDispatchCode(coordsSpecified, doEffects) {
|
||||
//Build the dispatch block:
|
||||
var code = "";
|
||||
code += generateDispatchHead(coordsSpecified);
|
||||
code += generateDispatchBody(coordsSpecified, doEffects);
|
||||
return code;
|
||||
}
|
||||
return generateDispatchCode(coordsSpecified, doEffects);
|
||||
}
|
||||
function generateDispatchFunc(coordsSpecified, code) {
|
||||
if (coordsSpecified) {
|
||||
return Function("xStart", "xEnd", "layers", code);
|
||||
}
|
||||
else {
|
||||
return Function("layers", code);
|
||||
}
|
||||
}
|
||||
//Generate the function:
|
||||
return generateDispatchFunc(coordsSpecified, generateDispatchBlock(coordsSpecified, doEffects));
|
||||
}
|
||||
//Build the functions for each of the six dispatches:
|
||||
GameBoyAdvanceOBJWindowCompositor.prototype.renderNormalScanLine = GameBoyAdvanceCompositor.prototype.renderNormalScanLine = generateDispatch(false, false);
|
||||
GameBoyAdvanceWindowCompositor.prototype.renderNormalScanLine = generateDispatch(true, false);
|
||||
GameBoyAdvanceOBJWindowCompositor.prototype.renderScanLineWithEffects = GameBoyAdvanceCompositor.prototype.renderScanLineWithEffects = generateDispatch(false, true);
|
||||
GameBoyAdvanceWindowCompositor.prototype.renderScanLineWithEffects = generateDispatch(true, true);
|
||||
}
|
||||
//Build and compile the compositors for every possible mode/layer/effect combination:
|
||||
generateCompositors();
|
||||
//Build and compile the dispatches for every possible mode/effect combination:
|
||||
generateDispatches();
|
||||
}
|
||||
generateIodineGBAGFXCompositors();
|
||||
@@ -0,0 +1,93 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012-2015 Grant Galitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function GameBoyAdvanceMosaicRenderer(buffer) {
|
||||
this.BGMosaicHSize = 0;
|
||||
this.BGMosaicVSize = 0;
|
||||
this.OBJMosaicHSize = 0;
|
||||
this.OBJMosaicVSize = 0;
|
||||
this.buffer = buffer;
|
||||
}
|
||||
GameBoyAdvanceMosaicRenderer.prototype.attachOBJBuffer = function (objBuffer) {
|
||||
//Function only called if no typed array view support:
|
||||
this.objBuffer = objBuffer;
|
||||
}
|
||||
GameBoyAdvanceMosaicRenderer.prototype.renderMosaicHorizontal = function (offset) {
|
||||
offset = offset | 0;
|
||||
var currentPixel = 0;
|
||||
var mosaicBlur = ((this.BGMosaicHSize | 0) + 1) | 0;
|
||||
if ((mosaicBlur | 0) > 1) { //Don't perform a useless loop.
|
||||
for (var position = 0; (position | 0) < 240; position = ((position | 0) + 1) | 0) {
|
||||
if ((((position | 0) % (mosaicBlur | 0)) | 0) == 0) {
|
||||
currentPixel = this.buffer[position | offset] | 0;
|
||||
}
|
||||
else {
|
||||
this.buffer[position | offset] = currentPixel | 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (__VIEWS_SUPPORTED__) {
|
||||
GameBoyAdvanceMosaicRenderer.prototype.renderOBJMosaicHorizontal = function (xOffset, xSize) {
|
||||
xOffset = xOffset | 0;
|
||||
xSize = xSize | 0;
|
||||
var currentPixel = 0x3800000;
|
||||
var mosaicBlur = ((this.OBJMosaicHSize | 0) + 1) | 0;
|
||||
if ((mosaicBlur | 0) > 1) { //Don't perform a useless loop.
|
||||
for (var position = ((xOffset | 0) % (mosaicBlur | 0)) | 0; (position | 0) < (xSize | 0); position = ((position | 0) + 1) | 0) {
|
||||
if ((((position | 0) % (mosaicBlur | 0)) | 0) == 0) {
|
||||
currentPixel = this.buffer[position | 0x600] | 0;
|
||||
}
|
||||
this.buffer[position | 0x600] = currentPixel | 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
GameBoyAdvanceMosaicRenderer.prototype.renderOBJMosaicHorizontal = function (xOffset, xSize) {
|
||||
xOffset = xOffset | 0;
|
||||
xSize = xSize | 0;
|
||||
var currentPixel = 0x3800000;
|
||||
var mosaicBlur = ((this.OBJMosaicHSize | 0) + 1) | 0;
|
||||
if ((mosaicBlur | 0) > 1) { //Don't perform a useless loop.
|
||||
for (var position = ((xOffset | 0) % (mosaicBlur | 0)) | 0; (position | 0) < (xSize | 0); position = ((position | 0) + 1) | 0) {
|
||||
if ((((position | 0) % (mosaicBlur | 0)) | 0) == 0) {
|
||||
currentPixel = this.objBuffer[position | 0] | 0;
|
||||
}
|
||||
this.objBuffer[position | 0] = currentPixel | 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GameBoyAdvanceMosaicRenderer.prototype.getMosaicYOffset = function (line) {
|
||||
line = line | 0;
|
||||
return ((line | 0) % (((this.BGMosaicVSize | 0) + 1) | 0)) | 0;
|
||||
}
|
||||
GameBoyAdvanceMosaicRenderer.prototype.getOBJMosaicYOffset = function (line) {
|
||||
line = line | 0;
|
||||
return ((line | 0) % (((this.OBJMosaicVSize | 0) + 1) | 0)) | 0;
|
||||
}
|
||||
GameBoyAdvanceMosaicRenderer.prototype.writeMOSAIC8_0 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGMosaicHSize = data & 0xF;
|
||||
this.BGMosaicVSize = data >> 4;
|
||||
}
|
||||
GameBoyAdvanceMosaicRenderer.prototype.writeMOSAIC8_1 = function (data) {
|
||||
data = data | 0;
|
||||
this.OBJMosaicHSize = data & 0xF;
|
||||
this.OBJMosaicVSize = data >> 4;
|
||||
}
|
||||
GameBoyAdvanceMosaicRenderer.prototype.writeMOSAIC16 = function (data) {
|
||||
data = data | 0;
|
||||
this.BGMosaicHSize = data & 0xF;
|
||||
this.BGMosaicVSize = (data >> 4) & 0xF;
|
||||
this.OBJMosaicHSize = (data >> 8) & 0xF;
|
||||
this.OBJMosaicVSize = data >> 12;
|
||||
}
|
||||