Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Game Genie support #89

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
26 changes: 18 additions & 8 deletions src/cpu.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ CPU.prototype = {
this.irqRequested = false;
}

var opinf = this.opdata[this.nes.mmap.load(this.REG_PC + 1)];
var opinf = this.opdata[this.loadFromCartridge(this.REG_PC + 1)];
var cycleCount = opinf >> 24;
var cycleAdd = 0;

Expand Down Expand Up @@ -264,8 +264,8 @@ CPU.prototype = {
(this.mem[(addr & 0xff00) | (((addr & 0xff) + 1) & 0xff)] << 8); // Read from address given in op
} else {
addr =
this.nes.mmap.load(addr) +
(this.nes.mmap.load(
this.loadFromCartridge(addr) +
(this.loadFromCartridge(
(addr & 0xff00) | (((addr & 0xff) + 1) & 0xff)
) <<
8);
Expand Down Expand Up @@ -1035,19 +1035,29 @@ CPU.prototype = {
return cycleCount;
},

loadFromCartridge: function(addr) {
var value = this.nes.mmap.load(addr);

if (this.nes.gg.enabled) {
value = this.nes.gg.applyCodes(addr, value);
}

return value;
},

load: function(addr) {
if (addr < 0x2000) {
return this.mem[addr & 0x7ff];
} else {
return this.nes.mmap.load(addr);
return this.loadFromCartridge(addr);
}
},

load16bit: function(addr) {
if (addr < 0x1fff) {
return this.mem[addr & 0x7ff] | (this.mem[(addr + 1) & 0x7ff] << 8);
} else {
return this.nes.mmap.load(addr) | (this.nes.mmap.load(addr + 1) << 8);
return this.loadFromCartridge(addr) | (this.loadFromCartridge(addr + 1) << 8);
}
},

Expand Down Expand Up @@ -1105,14 +1115,14 @@ CPU.prototype = {
this.push(status);

this.REG_PC_NEW =
this.nes.mmap.load(0xfffa) | (this.nes.mmap.load(0xfffb) << 8);
this.loadFromCartridge(0xfffa) | (this.loadFromCartridge(0xfffb) << 8);
this.REG_PC_NEW--;
}
},

doResetInterrupt: function() {
this.REG_PC_NEW =
this.nes.mmap.load(0xfffc) | (this.nes.mmap.load(0xfffd) << 8);
this.loadFromCartridge(0xfffc) | (this.loadFromCartridge(0xfffd) << 8);
this.REG_PC_NEW--;
},

Expand All @@ -1125,7 +1135,7 @@ CPU.prototype = {
this.F_BRK_NEW = 0;

this.REG_PC_NEW =
this.nes.mmap.load(0xfffe) | (this.nes.mmap.load(0xffff) << 8);
this.loadFromCartridge(0xfffe) | (this.loadFromCartridge(0xffff) << 8);
this.REG_PC_NEW--;
},

Expand Down
124 changes: 124 additions & 0 deletions src/gg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@

var GG = function(nes) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to abbreviate - GameGenie is much clearer.

this.nes = nes;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.nes is unused, I think, so can be removed.

this.patches = [];
this.enabled = true;
};

var LETTER_VALUES = 'APZLGITYEOXUKSVN';

function toDigit(letter) {
return LETTER_VALUES.indexOf(letter);
}

function toLetter(digit) {
return LETTER_VALUES.substr(digit, 1);
}

function toHex(n, width) {
var s = n.toString(16);
return '0000'.substring(0, width - s.length) + s;
}


GG.prototype = {
setEnabled: function(enabled) {
this.enabled = enabled;
},

addCode: function(code) {
this.patches.push(this.decode(code));
},

addPatch: function(addr, value, key) {
this.patches.push({addr: addr, value: value, key: key});
},

applyCodes: function(addr, value) {
if (!this.enabled) return value;

for (var i = 0; i < this.patches.length; ++i) { // TODO: optimize data structure?
if (this.patches[i].addr === (addr & 0x7fff)) {
if (this.patches[i].key === undefined || this.patches[i].key === value) {
return this.patches[i].value;
}
}
}
return value;
},

decode: function(code) {
if (code.indexOf(':') !== -1) return this.decodeHex(code);

var digits = code.toUpperCase().split('').map(toDigit);

var value = ((digits[0] & 8) << 4) + ((digits[1] & 7) << 4) + (digits[0] & 7);
var addr = ((digits[3] & 7) << 12) + ((digits[4] & 8) << 8) + ((digits[5] & 7) << 8) +
((digits[1] & 8) << 4) + ((digits[2] & 7) << 4) + (digits[3] & 8) + (digits[4] & 7);
var key;

if (digits.length === 8) {
value += (digits[7] & 8);
key = ((digits[6] & 8) << 4) + ((digits[7] & 7) << 4) + (digits[5] & 8) + (digits[6] & 7);
} else {
value += (digits[5] & 8);
}

var wantskey = !!(digits[2] >> 3);

return { value: value, addr: addr, wantskey: wantskey, key: key };
},

encodeHex: function(addr, value, key, wantskey) {
var s = toHex(addr, 4) + ':' + toHex(value, 2);

if (key !== undefined || wantskey) {
s += '?';
}

if (key !== undefined) {
s += toHex(key, 2);
}

return s;
},

decodeHex: function(s) {
var match = s.match(/([0-9a-fA-F]+):([0-9a-fA-F]+)(\?[0-9a-fA-F]*)?/);
if (!match) return null;

var addr = parseInt(match[1], 16);
var value = parseInt(match[2], 16);
var wantskey = match[3] !== undefined;
var key = (match[3] !== undefined && match[3].length > 1) ? parseInt(match[3].substring(1), 16) : undefined;

return { value: value, addr: addr, wantskey: wantskey, key: key };
},

encode: function(addr, value, key, wantskey) {
var digits = Array(6);

digits[0] = (value & 7) + ((value >> 4) & 8);
digits[1] = ((value >> 4) & 7) + ((addr >> 4) & 8);
digits[2] = ((addr >> 4) & 7);
digits[3] = (addr >> 12) + (addr & 8);
digits[4] = (addr & 7) + ((addr >> 8) & 8);
digits[5] = ((addr >> 8) & 7);

if (key === undefined) {
digits[5] += value & 8;
if (wantskey) digits[2] += 8;
} else {
digits[2] += 8;
digits[5] += key & 8;
digits[6] = (key & 7) + ((key >> 4) & 8);
digits[7] = ((key >> 4) & 7) + (value & 8);
}

var code = digits.map(toLetter).join('');

return code;
},
};

module.exports = GG;
2 changes: 2 additions & 0 deletions src/nes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ var CPU = require("./cpu");
var Controller = require("./controller");
var PPU = require("./ppu");
var PAPU = require("./papu");
var GG = require("./gg");
var ROM = require("./rom");

var NES = function(opts) {
Expand Down Expand Up @@ -34,6 +35,7 @@ var NES = function(opts) {
this.cpu = new CPU(this);
this.ppu = new PPU(this);
this.papu = new PAPU(this);
this.gg = new GG(this);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, can be this.gameGenie

this.mmap = null; // set in loadROM()
this.controllers = {
1: new Controller(),
Expand Down