1// Portions copyright 2012 Google, Inc
2
3// Copyright (C) 2010 - 2012 Grant Galitz
4// This program is free software; you can redistribute it and/or modify
5// it under the terms of the GNU General Public License version 2 as
6// published by the Free Software Foundation.
7// The full license is available at http://www.gnu.org/licenses/gpl.html
8// This program is distributed in the hope that it will be useful, but
9// WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11// See the GNU General Public License for more details.
12
13// The code has been adapted for use as a benchmark by Google.
14
15var GameboyBenchmark = new BenchmarkSuite('Gameboy', 18000000,
16                                          [new Benchmark('Gameboy',
17                                                         runGameboy,
18                                                         setupGameboy,
19                                                         tearDownGameboy,
20                                                         4)]);
21
22var decoded_gameboy_rom = null;
23
24function setupGameboy() {
25
26  // Check if all the types required by the code are supported.
27  // If not, throw exception and quit.
28  if (!(typeof Uint8Array != "undefined" &&
29      typeof Int8Array != "undefined" &&
30      typeof Float32Array != "undefined" &&
31      typeof Int32Array != "undefined") ) {
32    throw "TypedArrayUnsupported";
33  }
34  decoded_gameboy_rom = base64_decode(gameboy_rom);
35  rom = null;
36}
37
38function runGameboy() {
39  start(new GameBoyCanvas(), decoded_gameboy_rom);
40
41  gameboy.instructions = 0;
42  gameboy.totalInstructions = 250000;
43
44  while (gameboy.instructions <= gameboy.totalInstructions) {
45    gameboy.run();
46    GameBoyAudioNode.run();
47  }
48
49  resetGlobalVariables();
50}
51
52function tearDownGameboy() {
53  decoded_gameboy_rom = null;
54  expectedGameboyStateStr = null;
55}
56
57var expectedGameboyStateStr =
58  '{"registerA":160,"registerB":255,"registerC":255,"registerE":11,' +
59  '"registersHL":51600,"programCounter":24309,"stackPointer":49706,' +
60  '"sumROM":10171578,"sumMemory":3435856,"sumMBCRam":234598,"sumVRam":0}';
61
62// Start of browser emulation.
63
64GameBoyWindow = { };
65
66function GameBoyContext() {
67  this.createBuffer = function() {
68    return new Buffer();
69  }
70  this.createImageData = function (w, h) {
71    var result = {};
72    result.data = new Uint8Array(w * h);
73    return result;
74  }
75  this.putImageData = function (buffer, x, y) {
76    var sum = 0;
77    for (var i = 0; i < buffer.data.length; i++) {
78      sum += i * buffer.data[i];
79      sum = sum % 1000;
80    }
81  }
82  this.drawImage = function () { }
83};
84
85function GameBoyCanvas() {
86  this.getContext = function() {
87    return new GameBoyContext();
88  }
89  this.width = 160;
90  this.height = 144;
91  this.style = { visibility: "visibile" };
92}
93
94function cout(message, colorIndex) {
95}
96
97function clear_terminal() {
98}
99
100var GameBoyAudioNode = {
101  bufferSize : 0,
102  onaudioprocess : null ,
103  connect : function () {},
104  run: function() {
105    var event = {outputBuffer : this.outputBuffer};
106    this.onaudioprocess(event);
107  }
108};
109
110function GameBoyAudioContext () {
111  this.createBufferSource = function() {
112    return { noteOn : function () {}, connect : function() {}};
113  }
114  this.sampleRate = 48000;
115  this.destination = {}
116  this.createBuffer = function (channels, len, sampleRate) {
117    return { gain : 1,
118             numberOfChannels : 1,
119             length : 1,
120             duration : 0.000020833333110203966,
121             sampleRate : 48000}
122  }
123  this.createJavaScriptNode = function (bufferSize, inputChannels, outputChannels) {
124    GameBoyAudioNode.bufferSize = bufferSize;
125    GameBoyAudioNode.outputBuffer = {
126        getChannelData : function (i) {return this.channelData[i];},
127        channelData    : []
128    };
129    for (var i = 0; i < outputChannels; i++) {
130      GameBoyAudioNode.outputBuffer.channelData[i] = new Float32Array(bufferSize);
131    }
132    return GameBoyAudioNode;
133  }
134}
135
136var mock_date_time_counter = 0;
137
138function new_Date() {
139  return {
140    getTime: function() {
141      mock_date_time_counter += 16;
142      return mock_date_time_counter;
143    }
144  };
145}
146
147// End of browser emulation.
148
149// Start of helper functions.
150
151function checkFinalState() {
152  function sum(a) {
153    var result = 0;
154    for (var i = 0; i < a.length; i++) {
155      result += a[i];
156    }
157    return result;
158  }
159  var state = {
160    registerA: gameboy.registerA,
161    registerB: gameboy.registerB,
162    registerC: gameboy.registerC,
163    registerE: gameboy.registerE,
164    registerF: gameboy.registerF,
165    registersHL: gameboy.registersHL,
166    programCounter: gameboy.programCounter,
167    stackPointer: gameboy.stackPointer,
168    sumROM : sum(gameboy.fromTypedArray(gameboy.ROM)),
169    sumMemory: sum(gameboy.fromTypedArray(gameboy.memory)),
170    sumMBCRam: sum(gameboy.fromTypedArray(gameboy.MBCRam)),
171    sumVRam: sum(gameboy.fromTypedArray(gameboy.VRam))
172  }
173  var stateStr = JSON.stringify(state);
174  if (typeof expectedGameboyStateStr != "undefined") {
175    if (stateStr != expectedGameboyStateStr) {
176      alert("Incorrect final state of processor:\n" +
177            " actual   " + stateStr + "\n" +
178            " expected " + expectedGameboyStateStr);
179    }
180  } else {
181    alert(stateStr);
182  }
183}
184
185
186function resetGlobalVariables () {
187  //Audio API Event Handler:
188  audioContextHandle = null;
189  audioNode = null;
190  audioSource = null;
191  launchedContext = false;
192  audioContextSampleBuffer = [];
193  resampled = [];
194  webAudioMinBufferSize = 15000;
195  webAudioMaxBufferSize = 25000;
196  webAudioActualSampleRate = 44100;
197  XAudioJSSampleRate = 0;
198  webAudioMono = false;
199  XAudioJSVolume = 1;
200  resampleControl = null;
201  audioBufferSize = 0;
202  resampleBufferStart = 0;
203  resampleBufferEnd = 0;
204  resampleBufferSize = 2;
205
206  gameboy = null;           //GameBoyCore object.
207  gbRunInterval = null;       //GameBoyCore Timer
208}
209
210
211// End of helper functions.
212
213// Original code from Grant Galitz follows.
214// Modifications by Google are marked in comments.
215
216// Start of js/other/base64.js file.
217
218var toBase64 = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
219  "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
220  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+" , "/", "="];
221var fromBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
222function base64(data) {
223  try {
224    // The following line was modified for benchmarking:
225    var base64 = GameBoyWindow.btoa(data);  //Use this native function when it's available, as it's a magnitude faster than the non-native code below.
226  }
227  catch (error) {
228    //Defaulting to non-native base64 encoding...
229    var base64 = "";
230    var dataLength = data.length;
231    if (dataLength > 0) {
232      var bytes = [0, 0, 0];
233      var index = 0;
234      var remainder = dataLength % 3;
235      while (data.length % 3 > 0) {
236        //Make sure we don't do fuzzy math in the next loop...
237        data[data.length] = " ";
238      }
239      while (index < dataLength) {
240        //Keep this loop small for speed.
241        bytes = [data.charCodeAt(index++) & 0xFF, data.charCodeAt(index++) & 0xFF, data.charCodeAt(index++) & 0xFF];
242        base64 += toBase64[bytes[0] >> 2] + toBase64[((bytes[0] & 0x3) << 4) | (bytes[1] >> 4)] + toBase64[((bytes[1] & 0xF) << 2) | (bytes[2] >> 6)] + toBase64[bytes[2] & 0x3F];
243      }
244      if (remainder > 0) {
245        //Fill in the padding and recalulate the trailing six-bit group...
246        base64[base64.length - 1] = "=";
247        if (remainder == 2) {
248          base64[base64.length - 2] = "=";
249          base64[base64.length - 3] = toBase64[(bytes[0] & 0x3) << 4];
250        }
251        else {
252          base64[base64.length - 2] = toBase64[(bytes[1] & 0xF) << 2];
253        }
254      }
255    }
256  }
257  return base64;
258}
259function base64_decode(data) {
260  try {
261    // The following line was modified for benchmarking:
262    var decode64 = GameBoyWindow.atob(data);  //Use this native function when it's available, as it's a magnitude faster than the non-native code below.
263  }
264  catch (error) {
265    //Defaulting to non-native base64 decoding...
266    var decode64 = "";
267    var dataLength = data.length;
268    if (dataLength > 3 && dataLength % 4 == 0) {
269      var sixbits = [0, 0, 0, 0];  //Declare this out of the loop, to speed up the ops.
270      var index = 0;
271      while (index < dataLength) {
272        //Keep this loop small for speed.
273        sixbits = [fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++))];
274        decode64 += String.fromCharCode((sixbits[0] << 2) | (sixbits[1] >> 4)) + String.fromCharCode(((sixbits[1] & 0x0F) << 4) | (sixbits[2] >> 2)) + String.fromCharCode(((sixbits[2] & 0x03) << 6) | sixbits[3]);
275      }
276      //Check for the '=' character after the loop, so we don't hose it up.
277      if (sixbits[3] >= 0x40) {
278        decode64.length -= 1;
279        if (sixbits[2] >= 0x40) {
280          decode64.length -= 1;
281        }
282      }
283    }
284  }
285  return decode64;
286}
287function to_little_endian_dword(str) {
288  return to_little_endian_word(str) + String.fromCharCode((str >> 16) & 0xFF, (str >> 24) & 0xFF);
289}
290function to_little_endian_word(str) {
291  return to_byte(str) + String.fromCharCode((str >> 8) & 0xFF);
292}
293function to_byte(str) {
294  return String.fromCharCode(str & 0xFF);
295}
296function arrayToBase64(arrayIn) {
297  var binString = "";
298  var length = arrayIn.length;
299  for (var index = 0; index < length; ++index) {
300    if (typeof arrayIn[index] == "number") {
301      binString += String.fromCharCode(arrayIn[index]);
302    }
303  }
304  return base64(binString);
305}
306function base64ToArray(b64String) {
307  var binString = base64_decode(b64String);
308  var outArray = [];
309  var length = binString.length;
310  for (var index = 0; index < length;) {
311    outArray.push(binString.charCodeAt(index++) & 0xFF);
312  }
313  return outArray;
314}
315
316// End of js/other/base64.js file.
317
318// Start of js/other/resampler.js file.
319
320//JavaScript Audio Resampler (c) 2011 - Grant Galitz
321function Resampler(fromSampleRate, toSampleRate, channels, outputBufferSize, noReturn) {
322  this.fromSampleRate = fromSampleRate;
323  this.toSampleRate = toSampleRate;
324  this.channels = channels | 0;
325  this.outputBufferSize = outputBufferSize;
326  this.noReturn = !!noReturn;
327  this.initialize();
328}
329Resampler.prototype.initialize = function () {
330  //Perform some checks:
331  if (this.fromSampleRate > 0 && this.toSampleRate > 0 && this.channels > 0) {
332    if (this.fromSampleRate == this.toSampleRate) {
333      //Setup a resampler bypass:
334      this.resampler = this.bypassResampler;    //Resampler just returns what was passed through.
335      this.ratioWeight = 1;
336    }
337    else {
338      //Setup the interpolation resampler:
339      this.compileInterpolationFunction();
340      this.resampler = this.interpolate;      //Resampler is a custom quality interpolation algorithm.
341      this.ratioWeight = this.fromSampleRate / this.toSampleRate;
342      this.tailExists = false;
343      this.lastWeight = 0;
344      this.initializeBuffers();
345    }
346  }
347  else {
348    throw(new Error("Invalid settings specified for the resampler."));
349  }
350}
351Resampler.prototype.compileInterpolationFunction = function () {
352  var toCompile = "var bufferLength = Math.min(buffer.length, this.outputBufferSize);\
353  if ((bufferLength % " + this.channels + ") == 0) {\
354    if (bufferLength > 0) {\
355      var ratioWeight = this.ratioWeight;\
356      var weight = 0;";
357  for (var channel = 0; channel < this.channels; ++channel) {
358    toCompile += "var output" + channel + " = 0;"
359  }
360  toCompile += "var actualPosition = 0;\
361      var amountToNext = 0;\
362      var alreadyProcessedTail = !this.tailExists;\
363      this.tailExists = false;\
364      var outputBuffer = this.outputBuffer;\
365      var outputOffset = 0;\
366      var currentPosition = 0;\
367      do {\
368        if (alreadyProcessedTail) {\
369          weight = ratioWeight;";
370  for (channel = 0; channel < this.channels; ++channel) {
371    toCompile += "output" + channel + " = 0;"
372  }
373  toCompile += "}\
374        else {\
375          weight = this.lastWeight;";
376  for (channel = 0; channel < this.channels; ++channel) {
377    toCompile += "output" + channel + " = this.lastOutput[" + channel + "];"
378  }
379  toCompile += "alreadyProcessedTail = true;\
380        }\
381        while (weight > 0 && actualPosition < bufferLength) {\
382          amountToNext = 1 + actualPosition - currentPosition;\
383          if (weight >= amountToNext) {";
384  for (channel = 0; channel < this.channels; ++channel) {
385    toCompile += "output" + channel + " += buffer[actualPosition++] * amountToNext;"
386  }
387  toCompile += "currentPosition = actualPosition;\
388            weight -= amountToNext;\
389          }\
390          else {";
391  for (channel = 0; channel < this.channels; ++channel) {
392    toCompile += "output" + channel + " += buffer[actualPosition" + ((channel > 0) ? (" + " + channel) : "") + "] * weight;"
393  }
394  toCompile += "currentPosition += weight;\
395            weight = 0;\
396            break;\
397          }\
398        }\
399        if (weight == 0) {";
400  for (channel = 0; channel < this.channels; ++channel) {
401    toCompile += "outputBuffer[outputOffset++] = output" + channel + " / ratioWeight;"
402  }
403  toCompile += "}\
404        else {\
405          this.lastWeight = weight;";
406  for (channel = 0; channel < this.channels; ++channel) {
407    toCompile += "this.lastOutput[" + channel + "] = output" + channel + ";"
408  }
409  toCompile += "this.tailExists = true;\
410          break;\
411        }\
412      } while (actualPosition < bufferLength);\
413      return this.bufferSlice(outputOffset);\
414    }\
415    else {\
416      return (this.noReturn) ? 0 : [];\
417    }\
418  }\
419  else {\
420    throw(new Error(\"Buffer was of incorrect sample length.\"));\
421  }";
422  this.interpolate = Function("buffer", toCompile);
423}
424Resampler.prototype.bypassResampler = function (buffer) {
425  if (this.noReturn) {
426    //Set the buffer passed as our own, as we don't need to resample it:
427    this.outputBuffer = buffer;
428    return buffer.length;
429  }
430  else {
431    //Just return the buffer passsed:
432    return buffer;
433  }
434}
435Resampler.prototype.bufferSlice = function (sliceAmount) {
436  if (this.noReturn) {
437    //If we're going to access the properties directly from this object:
438    return sliceAmount;
439  }
440  else {
441    //Typed array and normal array buffer section referencing:
442    try {
443      return this.outputBuffer.subarray(0, sliceAmount);
444    }
445    catch (error) {
446      try {
447        //Regular array pass:
448        this.outputBuffer.length = sliceAmount;
449        return this.outputBuffer;
450      }
451      catch (error) {
452        //Nightly Firefox 4 used to have the subarray function named as slice:
453        return this.outputBuffer.slice(0, sliceAmount);
454      }
455    }
456  }
457}
458Resampler.prototype.initializeBuffers = function () {
459  //Initialize the internal buffer:
460  try {
461    this.outputBuffer = new Float32Array(this.outputBufferSize);
462    this.lastOutput = new Float32Array(this.channels);
463  }
464  catch (error) {
465    this.outputBuffer = [];
466    this.lastOutput = [];
467  }
468}
469
470// End of js/other/resampler.js file.
471
472// Start of js/other/XAudioServer.js file.
473
474/*Initialize here first:
475  Example:
476    Stereo audio with a sample rate of 70 khz, a minimum buffer of 15000 samples total, a maximum buffer of 25000 samples total and a starting volume level of 1.
477      var parentObj = this;
478      this.audioHandle = new XAudioServer(2, 70000, 15000, 25000, function (sampleCount) {
479        return parentObj.audioUnderRun(sampleCount);
480      }, 1);
481
482  The callback is passed the number of samples requested, while it can return any number of samples it wants back.
483*/
484function XAudioServer(channels, sampleRate, minBufferSize, maxBufferSize, underRunCallback, volume) {
485  this.audioChannels = (channels == 2) ? 2 : 1;
486  webAudioMono = (this.audioChannels == 1);
487  XAudioJSSampleRate = (sampleRate > 0 && sampleRate <= 0xFFFFFF) ? sampleRate : 44100;
488  webAudioMinBufferSize = (minBufferSize >= (samplesPerCallback << 1) && minBufferSize < maxBufferSize) ? (minBufferSize & ((webAudioMono) ? 0xFFFFFFFF : 0xFFFFFFFE)) : (samplesPerCallback << 1);
489  webAudioMaxBufferSize = (Math.floor(maxBufferSize) > webAudioMinBufferSize + this.audioChannels) ? (maxBufferSize & ((webAudioMono) ? 0xFFFFFFFF : 0xFFFFFFFE)) : (minBufferSize << 1);
490  this.underRunCallback = (typeof underRunCallback == "function") ? underRunCallback : function () {};
491  XAudioJSVolume = (volume >= 0 && volume <= 1) ? volume : 1;
492  this.audioType = -1;
493  this.mozAudioTail = [];
494  this.audioHandleMoz = null;
495  this.audioHandleFlash = null;
496  this.flashInitialized = false;
497  this.mozAudioFound = false;
498  this.initializeAudio();
499}
500XAudioServer.prototype.MOZWriteAudio = function (buffer) {
501  //mozAudio:
502  this.MOZWriteAudioNoCallback(buffer);
503  this.MOZExecuteCallback();
504}
505XAudioServer.prototype.MOZWriteAudioNoCallback = function (buffer) {
506  //mozAudio:
507  this.writeMozAudio(buffer);
508}
509XAudioServer.prototype.callbackBasedWriteAudio = function (buffer) {
510  //Callback-centered audio APIs:
511  this.callbackBasedWriteAudioNoCallback(buffer);
512  this.callbackBasedExecuteCallback();
513}
514XAudioServer.prototype.callbackBasedWriteAudioNoCallback = function (buffer) {
515  //Callback-centered audio APIs:
516  var length = buffer.length;
517  for (var bufferCounter = 0; bufferCounter < length && audioBufferSize < webAudioMaxBufferSize;) {
518    audioContextSampleBuffer[audioBufferSize++] = buffer[bufferCounter++];
519  }
520}
521/*Pass your samples into here!
522Pack your samples as a one-dimenional array
523With the channel samplea packed uniformly.
524examples:
525    mono - [left, left, left, left]
526    stereo - [left, right, left, right, left, right, left, right]
527*/
528XAudioServer.prototype.writeAudio = function (buffer) {
529  if (this.audioType == 0) {
530    this.MOZWriteAudio(buffer);
531  }
532  else if (this.audioType == 1) {
533    this.callbackBasedWriteAudio(buffer);
534  }
535  else if (this.audioType == 2) {
536    if (this.checkFlashInit() || launchedContext) {
537      this.callbackBasedWriteAudio(buffer);
538    }
539    else if (this.mozAudioFound) {
540      this.MOZWriteAudio(buffer);
541    }
542  }
543}
544/*Pass your samples into here if you don't want automatic callback calling:
545Pack your samples as a one-dimenional array
546With the channel samplea packed uniformly.
547examples:
548    mono - [left, left, left, left]
549    stereo - [left, right, left, right, left, right, left, right]
550Useful in preventing infinite recursion issues with calling writeAudio inside your callback.
551*/
552XAudioServer.prototype.writeAudioNoCallback = function (buffer) {
553  if (this.audioType == 0) {
554    this.MOZWriteAudioNoCallback(buffer);
555  }
556  else if (this.audioType == 1) {
557    this.callbackBasedWriteAudioNoCallback(buffer);
558  }
559  else if (this.audioType == 2) {
560    if (this.checkFlashInit() || launchedContext) {
561      this.callbackBasedWriteAudioNoCallback(buffer);
562    }
563    else if (this.mozAudioFound) {
564      this.MOZWriteAudioNoCallback(buffer);
565    }
566  }
567}
568//Developer can use this to see how many samples to write (example: minimum buffer allotment minus remaining samples left returned from this function to make sure maximum buffering is done...)
569//If -1 is returned, then that means metric could not be done.
570XAudioServer.prototype.remainingBuffer = function () {
571  if (this.audioType == 0) {
572    //mozAudio:
573    return this.samplesAlreadyWritten - this.audioHandleMoz.mozCurrentSampleOffset();
574  }
575  else if (this.audioType == 1) {
576    //WebKit Audio:
577    return (((resampledSamplesLeft() * resampleControl.ratioWeight) >> (this.audioChannels - 1)) << (this.audioChannels - 1)) + audioBufferSize;
578  }
579  else if (this.audioType == 2) {
580    if (this.checkFlashInit() || launchedContext) {
581      //Webkit Audio / Flash Plugin Audio:
582      return (((resampledSamplesLeft() * resampleControl.ratioWeight) >> (this.audioChannels - 1)) << (this.audioChannels - 1)) + audioBufferSize;
583    }
584    else if (this.mozAudioFound) {
585      //mozAudio:
586      return this.samplesAlreadyWritten - this.audioHandleMoz.mozCurrentSampleOffset();
587    }
588  }
589  //Default return:
590  return 0;
591}
592XAudioServer.prototype.MOZExecuteCallback = function () {
593  //mozAudio:
594  var samplesRequested = webAudioMinBufferSize - this.remainingBuffer();
595  if (samplesRequested > 0) {
596    this.writeMozAudio(this.underRunCallback(samplesRequested));
597  }
598}
599XAudioServer.prototype.callbackBasedExecuteCallback = function () {
600  //WebKit /Flash Audio:
601  var samplesRequested = webAudioMinBufferSize - this.remainingBuffer();
602  if (samplesRequested > 0) {
603    this.callbackBasedWriteAudioNoCallback(this.underRunCallback(samplesRequested));
604  }
605}
606//If you just want your callback called for any possible refill (Execution of callback is still conditional):
607XAudioServer.prototype.executeCallback = function () {
608  if (this.audioType == 0) {
609    this.MOZExecuteCallback();
610  }
611  else if (this.audioType == 1) {
612    this.callbackBasedExecuteCallback();
613  }
614  else if (this.audioType == 2) {
615    if (this.checkFlashInit() || launchedContext) {
616      this.callbackBasedExecuteCallback();
617    }
618    else if (this.mozAudioFound) {
619      this.MOZExecuteCallback();
620    }
621  }
622}
623//DO NOT CALL THIS, the lib calls this internally!
624XAudioServer.prototype.initializeAudio = function () {
625  try {
626    throw (new Error("Select initializeWebAudio case"));  // Line added for benchmarking.
627    this.preInitializeMozAudio();
628    if (navigator.platform == "Linux i686") {
629      //Block out mozaudio usage for Linux Firefox due to moz bugs:
630      throw(new Error(""));
631    }
632    this.initializeMozAudio();
633  }
634  catch (error) {
635    try {
636      this.initializeWebAudio();
637    }
638    catch (error) {
639      try {
640        this.initializeFlashAudio();
641      }
642      catch (error) {
643        throw(new Error("Browser does not support real time audio output."));
644      }
645    }
646  }
647}
648XAudioServer.prototype.preInitializeMozAudio = function () {
649  //mozAudio - Synchronous Audio API
650  this.audioHandleMoz = new Audio();
651  this.audioHandleMoz.mozSetup(this.audioChannels, XAudioJSSampleRate);
652  this.samplesAlreadyWritten = 0;
653  var emptySampleFrame = (this.audioChannels == 2) ? [0, 0] : [0];
654  var prebufferAmount = 0;
655  if (navigator.platform != "MacIntel" && navigator.platform != "MacPPC") {  //Mac OS X doesn't experience this moz-bug!
656    while (this.audioHandleMoz.mozCurrentSampleOffset() == 0) {
657      //Mozilla Audio Bugginess Workaround (Firefox freaks out if we don't give it a prebuffer under certain OSes):
658      prebufferAmount += this.audioHandleMoz.mozWriteAudio(emptySampleFrame);
659    }
660    var samplesToDoubleBuffer = prebufferAmount / this.audioChannels;
661    //Double the prebuffering for windows:
662    for (var index = 0; index < samplesToDoubleBuffer; index++) {
663      this.samplesAlreadyWritten += this.audioHandleMoz.mozWriteAudio(emptySampleFrame);
664    }
665  }
666  this.samplesAlreadyWritten += prebufferAmount;
667  webAudioMinBufferSize += this.samplesAlreadyWritten;
668  this.mozAudioFound = true;
669}
670XAudioServer.prototype.initializeMozAudio = function () {
671  //Fill in our own buffering up to the minimum specified:
672  this.writeMozAudio(getFloat32(webAudioMinBufferSize));
673  this.audioType = 0;
674}
675XAudioServer.prototype.initializeWebAudio = function () {
676  if (launchedContext) {
677    resetCallbackAPIAudioBuffer(webAudioActualSampleRate, samplesPerCallback);
678    this.audioType = 1;
679  }
680  else {
681    throw(new Error(""));
682  }
683}
684XAudioServer.prototype.initializeFlashAudio = function () {
685  var existingFlashload = document.getElementById("XAudioJS");
686  if (existingFlashload == null) {
687    var thisObj = this;
688    var mainContainerNode = document.createElement("div");
689    mainContainerNode.setAttribute("style", "position: fixed; bottom: 0px; right: 0px; margin: 0px; padding: 0px; border: none; width: 8px; height: 8px; overflow: hidden; z-index: -1000; ");
690    var containerNode = document.createElement("div");
691    containerNode.setAttribute("style", "position: static; border: none; width: 0px; height: 0px; visibility: hidden; margin: 8px; padding: 0px;");
692    containerNode.setAttribute("id", "XAudioJS");
693    mainContainerNode.appendChild(containerNode);
694    document.getElementsByTagName("body")[0].appendChild(mainContainerNode);
695    swfobject.embedSWF(
696      "XAudioJS.swf",
697      "XAudioJS",
698      "8",
699      "8",
700      "9.0.0",
701      "",
702      {},
703      {"allowscriptaccess":"always"},
704      {"style":"position: static; visibility: hidden; margin: 8px; padding: 0px; border: none"},
705      function (event) {
706        if (event.success) {
707          thisObj.audioHandleFlash = event.ref;
708        }
709        else {
710          thisObj.audioType = 1;
711        }
712      }
713    );
714  }
715  else {
716    this.audioHandleFlash = existingFlashload;
717  }
718  this.audioType = 2;
719}
720XAudioServer.prototype.changeVolume = function (newVolume) {
721  if (newVolume >= 0 && newVolume <= 1) {
722    XAudioJSVolume = newVolume;
723    if (this.checkFlashInit()) {
724      this.audioHandleFlash.changeVolume(XAudioJSVolume);
725    }
726    if (this.mozAudioFound) {
727      this.audioHandleMoz.volume = XAudioJSVolume;
728    }
729  }
730}
731//Moz Audio Buffer Writing Handler:
732XAudioServer.prototype.writeMozAudio = function (buffer) {
733  var length = this.mozAudioTail.length;
734  if (length > 0) {
735    var samplesAccepted = this.audioHandleMoz.mozWriteAudio(this.mozAudioTail);
736    this.samplesAlreadyWritten += samplesAccepted;
737    this.mozAudioTail.splice(0, samplesAccepted);
738  }
739  length = Math.min(buffer.length, webAudioMaxBufferSize - this.samplesAlreadyWritten + this.audioHandleMoz.mozCurrentSampleOffset());
740  var samplesAccepted = this.audioHandleMoz.mozWriteAudio(buffer);
741  this.samplesAlreadyWritten += samplesAccepted;
742  for (var index = 0; length > samplesAccepted; --length) {
743    //Moz Audio wants us saving the tail:
744    this.mozAudioTail.push(buffer[index++]);
745  }
746}
747//Checks to see if the NPAPI Adobe Flash bridge is ready yet:
748XAudioServer.prototype.checkFlashInit = function () {
749  if (!this.flashInitialized && this.audioHandleFlash && this.audioHandleFlash.initialize) {
750    this.flashInitialized = true;
751    this.audioHandleFlash.initialize(this.audioChannels, XAudioJSVolume);
752    resetCallbackAPIAudioBuffer(44100, samplesPerCallback);
753  }
754  return this.flashInitialized;
755}
756/////////END LIB
757function getFloat32(size) {
758  try {
759    return new Float32Array(size);
760  }
761  catch (error) {
762    return new Array(size);
763  }
764}
765function getFloat32Flat(size) {
766  try {
767    var newBuffer = new Float32Array(size);
768  }
769  catch (error) {
770    var newBuffer = new Array(size);
771    var audioSampleIndice = 0;
772    do {
773      newBuffer[audioSampleIndice] = 0;
774    } while (++audioSampleIndice < size);
775  }
776  return newBuffer;
777}
778//Flash NPAPI Event Handler:
779var samplesPerCallback = 2048;      //Has to be between 2048 and 4096 (If over, then samples are ignored, if under then silence is added).
780var outputConvert = null;
781function audioOutputFlashEvent() {    //The callback that flash calls...
782  resampleRefill();
783  return outputConvert();
784}
785function generateFlashStereoString() {  //Convert the arrays to one long string for speed.
786  var copyBinaryStringLeft = "";
787  var copyBinaryStringRight = "";
788  for (var index = 0; index < samplesPerCallback && resampleBufferStart != resampleBufferEnd; ++index) {
789    //Sanitize the buffer:
790    copyBinaryStringLeft += String.fromCharCode(((Math.min(Math.max(resampled[resampleBufferStart++] + 1, 0), 2) * 0x3FFF) | 0) + 0x3000);
791    copyBinaryStringRight += String.fromCharCode(((Math.min(Math.max(resampled[resampleBufferStart++] + 1, 0), 2) * 0x3FFF) | 0) + 0x3000);
792    if (resampleBufferStart == resampleBufferSize) {
793      resampleBufferStart = 0;
794    }
795  }
796  return copyBinaryStringLeft + copyBinaryStringRight;
797}
798function generateFlashMonoString() {  //Convert the array to one long string for speed.
799  var copyBinaryString = "";
800  for (var index = 0; index < samplesPerCallback && resampleBufferStart != resampleBufferEnd; ++index) {
801    //Sanitize the buffer:
802    copyBinaryString += String.fromCharCode(((Math.min(Math.max(resampled[resampleBufferStart++] + 1, 0), 2) * 0x3FFF) | 0) + 0x3000);
803    if (resampleBufferStart == resampleBufferSize) {
804      resampleBufferStart = 0;
805    }
806  }
807  return copyBinaryString;
808}
809//Audio API Event Handler:
810var audioContextHandle = null;
811var audioNode = null;
812var audioSource = null;
813var launchedContext = false;
814var audioContextSampleBuffer = [];
815var resampled = [];
816var webAudioMinBufferSize = 15000;
817var webAudioMaxBufferSize = 25000;
818var webAudioActualSampleRate = 44100;
819var XAudioJSSampleRate = 0;
820var webAudioMono = false;
821var XAudioJSVolume = 1;
822var resampleControl = null;
823var audioBufferSize = 0;
824var resampleBufferStart = 0;
825var resampleBufferEnd = 0;
826var resampleBufferSize = 2;
827function audioOutputEvent(event) {    //Web Audio API callback...
828  var index = 0;
829  var buffer1 = event.outputBuffer.getChannelData(0);
830  var buffer2 = event.outputBuffer.getChannelData(1);
831  resampleRefill();
832  if (!webAudioMono) {
833    //STEREO:
834    while (index < samplesPerCallback && resampleBufferStart != resampleBufferEnd) {
835      buffer1[index] = resampled[resampleBufferStart++] * XAudioJSVolume;
836      buffer2[index++] = resampled[resampleBufferStart++] * XAudioJSVolume;
837      if (resampleBufferStart == resampleBufferSize) {
838        resampleBufferStart = 0;
839      }
840    }
841  }
842  else {
843    //MONO:
844    while (index < samplesPerCallback && resampleBufferStart != resampleBufferEnd) {
845      buffer2[index] = buffer1[index] = resampled[resampleBufferStart++] * XAudioJSVolume;
846      ++index;
847      if (resampleBufferStart == resampleBufferSize) {
848        resampleBufferStart = 0;
849      }
850    }
851  }
852  //Pad with silence if we're underrunning:
853  while (index < samplesPerCallback) {
854    buffer2[index] = buffer1[index] = 0;
855    ++index;
856  }
857}
858function resampleRefill() {
859  if (audioBufferSize > 0) {
860    //Resample a chunk of audio:
861    var resampleLength = resampleControl.resampler(getBufferSamples());
862    var resampledResult = resampleControl.outputBuffer;
863    for (var index2 = 0; index2 < resampleLength; ++index2) {
864      resampled[resampleBufferEnd++] = resampledResult[index2];
865      if (resampleBufferEnd == resampleBufferSize) {
866        resampleBufferEnd = 0;
867      }
868      if (resampleBufferStart == resampleBufferEnd) {
869        ++resampleBufferStart;
870        if (resampleBufferStart == resampleBufferSize) {
871          resampleBufferStart = 0;
872        }
873      }
874    }
875    audioBufferSize = 0;
876  }
877}
878function resampledSamplesLeft() {
879  return ((resampleBufferStart <= resampleBufferEnd) ? 0 : resampleBufferSize) + resampleBufferEnd - resampleBufferStart;
880}
881function getBufferSamples() {
882  //Typed array and normal array buffer section referencing:
883  try {
884    return audioContextSampleBuffer.subarray(0, audioBufferSize);
885  }
886  catch (error) {
887    try {
888      //Regular array pass:
889      audioContextSampleBuffer.length = audioBufferSize;
890      return audioContextSampleBuffer;
891    }
892    catch (error) {
893      //Nightly Firefox 4 used to have the subarray function named as slice:
894      return audioContextSampleBuffer.slice(0, audioBufferSize);
895    }
896  }
897}
898//Initialize WebKit Audio /Flash Audio Buffer:
899function resetCallbackAPIAudioBuffer(APISampleRate, bufferAlloc) {
900  audioContextSampleBuffer = getFloat32(webAudioMaxBufferSize);
901  audioBufferSize = webAudioMaxBufferSize;
902  resampleBufferStart = 0;
903  resampleBufferEnd = 0;
904  resampleBufferSize = Math.max(webAudioMaxBufferSize * Math.ceil(XAudioJSSampleRate / APISampleRate), samplesPerCallback) << 1;
905  if (webAudioMono) {
906    //MONO Handling:
907    resampled = getFloat32Flat(resampleBufferSize);
908    resampleControl = new Resampler(XAudioJSSampleRate, APISampleRate, 1, resampleBufferSize, true);
909    outputConvert = generateFlashMonoString;
910  }
911  else {
912    //STEREO Handling:
913    resampleBufferSize  <<= 1;
914    resampled = getFloat32Flat(resampleBufferSize);
915    resampleControl = new Resampler(XAudioJSSampleRate, APISampleRate, 2, resampleBufferSize, true);
916    outputConvert = generateFlashStereoString;
917  }
918}
919//Initialize WebKit Audio:
920(function () {
921  if (!launchedContext) {
922    try {
923      // The following line was modified for benchmarking:
924      audioContextHandle = new GameBoyAudioContext();              //Create a system audio context.
925    }
926    catch (error) {
927      try {
928        audioContextHandle = new AudioContext();                //Create a system audio context.
929      }
930      catch (error) {
931        return;
932      }
933    }
934    try {
935      audioSource = audioContextHandle.createBufferSource();            //We need to create a false input to get the chain started.
936      audioSource.loop = false;  //Keep this alive forever (Event handler will know when to ouput.)
937      XAudioJSSampleRate = webAudioActualSampleRate = audioContextHandle.sampleRate;
938      audioSource.buffer = audioContextHandle.createBuffer(1, 1, webAudioActualSampleRate);  //Create a zero'd input buffer for the input to be valid.
939      audioNode = audioContextHandle.createJavaScriptNode(samplesPerCallback, 1, 2);      //Create 2 outputs and ignore the input buffer (Just copy buffer 1 over if mono)
940      audioNode.onaudioprocess = audioOutputEvent;                //Connect the audio processing event to a handling function so we can manipulate output
941      audioSource.connect(audioNode);                        //Send and chain the input to the audio manipulation.
942      audioNode.connect(audioContextHandle.destination);              //Send and chain the output of the audio manipulation to the system audio output.
943      audioSource.noteOn(0);                            //Start the loop!
944    }
945    catch (error) {
946      return;
947    }
948    launchedContext = true;
949  }
950})();
951
952// End of js/other/XAudioServer.js file.
953
954// Start of js/other/resize.js file.
955
956//JavaScript Image Resizer (c) 2012 - Grant Galitz
957function Resize(widthOriginal, heightOriginal, targetWidth, targetHeight, blendAlpha, interpolationPass) {
958  this.widthOriginal = Math.abs(parseInt(widthOriginal) || 0);
959  this.heightOriginal = Math.abs(parseInt(heightOriginal) || 0);
960  this.targetWidth = Math.abs(parseInt(targetWidth) || 0);
961  this.targetHeight = Math.abs(parseInt(targetHeight) || 0);
962  this.colorChannels = (!!blendAlpha) ? 4 : 3;
963  this.interpolationPass = !!interpolationPass;
964  this.targetWidthMultipliedByChannels = this.targetWidth * this.colorChannels;
965  this.originalWidthMultipliedByChannels = this.widthOriginal * this.colorChannels;
966  this.originalHeightMultipliedByChannels = this.heightOriginal * this.colorChannels;
967  this.widthPassResultSize = this.targetWidthMultipliedByChannels * this.heightOriginal;
968  this.finalResultSize = this.targetWidthMultipliedByChannels * this.targetHeight;
969  this.initialize();
970}
971Resize.prototype.initialize = function () {
972  //Perform some checks:
973  if (this.widthOriginal > 0 && this.heightOriginal > 0 && this.targetWidth > 0 && this.targetHeight > 0) {
974    if (this.widthOriginal == this.targetWidth) {
975      //Bypass the width resizer pass:
976      this.resizeWidth = this.bypassResizer;
977    }
978    else {
979      //Setup the width resizer pass:
980      this.ratioWeightWidthPass = this.widthOriginal / this.targetWidth;
981      if (this.ratioWeightWidthPass < 1 && this.interpolationPass) {
982        this.initializeFirstPassBuffers(true);
983        this.resizeWidth = (this.colorChannels == 4) ? this.resizeWidthInterpolatedRGBA : this.resizeWidthInterpolatedRGB;
984      }
985      else {
986        this.initializeFirstPassBuffers(false);
987        this.resizeWidth = (this.colorChannels == 4) ? this.resizeWidthRGBA : this.resizeWidthRGB;
988      }
989    }
990    if (this.heightOriginal == this.targetHeight) {
991      //Bypass the height resizer pass:
992      this.resizeHeight = this.bypassResizer;
993    }
994    else {
995      //Setup the height resizer pass:
996      this.ratioWeightHeightPass = this.heightOriginal / this.targetHeight;
997      if (this.ratioWeightHeightPass < 1 && this.interpolationPass) {
998        this.initializeSecondPassBuffers(true);
999        this.resizeHeight = this.resizeHeightInterpolated;
1000      }
1001      else {
1002        this.initializeSecondPassBuffers(false);
1003        this.resizeHeight = (this.colorChannels == 4) ? this.resizeHeightRGBA : this.resizeHeightRGB;
1004      }
1005    }
1006  }
1007  else {
1008    throw(new Error("Invalid settings specified for the resizer."));
1009  }
1010}
1011Resize.prototype.resizeWidthRGB = function (buffer) {
1012  var ratioWeight = this.ratioWeightWidthPass;
1013  var weight = 0;
1014  var amountToNext = 0;
1015  var actualPosition = 0;
1016  var currentPosition = 0;
1017  var line = 0;
1018  var pixelOffset = 0;
1019  var outputOffset = 0;
1020  var nextLineOffsetOriginalWidth = this.originalWidthMultipliedByChannels - 2;
1021  var nextLineOffsetTargetWidth = this.targetWidthMultipliedByChannels - 2;
1022  var output = this.outputWidthWorkBench;
1023  var outputBuffer = this.widthBuffer;
1024  do {
1025    for (line = 0; line < this.originalHeightMultipliedByChannels;) {
1026      output[line++] = 0;
1027      output[line++] = 0;
1028      output[line++] = 0;
1029    }
1030    weight = ratioWeight;
1031    do {
1032      amountToNext = 1 + actualPosition - currentPosition;
1033      if (weight >= amountToNext) {
1034        for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
1035          output[line++] += buffer[pixelOffset++] * amountToNext;
1036          output[line++] += buffer[pixelOffset++] * amountToNext;
1037          output[line++] += buffer[pixelOffset] * amountToNext;
1038        }
1039        currentPosition = actualPosition = actualPosition + 3;
1040        weight -= amountToNext;
1041      }
1042      else {
1043        for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
1044          output[line++] += buffer[pixelOffset++] * weight;
1045          output[line++] += buffer[pixelOffset++] * weight;
1046          output[line++] += buffer[pixelOffset] * weight;
1047        }
1048        currentPosition += weight;
1049        break;
1050      }
1051    } while (weight > 0 && actualPosition < this.originalWidthMultipliedByChannels);
1052    for (line = 0, pixelOffset = outputOffset; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetTargetWidth) {
1053      outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
1054      outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
1055      outputBuffer[pixelOffset] = output[line++] / ratioWeight;
1056    }
1057    outputOffset += 3;
1058  } while (outputOffset < this.targetWidthMultipliedByChannels);
1059  return outputBuffer;
1060}
1061Resize.prototype.resizeWidthInterpolatedRGB = function (buffer) {
1062  var ratioWeight = (this.widthOriginal - 1) / this.targetWidth;
1063  var weight = 0;
1064  var finalOffset = 0;
1065  var pixelOffset = 0;
1066  var outputBuffer = this.widthBuffer;
1067  for (var targetPosition = 0; targetPosition < this.targetWidthMultipliedByChannels; targetPosition += 3, weight += ratioWeight) {
1068    //Calculate weightings:
1069    secondWeight = weight % 1;
1070    firstWeight = 1 - secondWeight;
1071    //Interpolate:
1072    for (finalOffset = targetPosition, pixelOffset = Math.floor(weight) * 3; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
1073      outputBuffer[finalOffset] = (buffer[pixelOffset] * firstWeight) + (buffer[pixelOffset + 3] * secondWeight);
1074      outputBuffer[finalOffset + 1] = (buffer[pixelOffset + 1] * firstWeight) + (buffer[pixelOffset + 4] * secondWeight);
1075      outputBuffer[finalOffset + 2] = (buffer[pixelOffset + 2] * firstWeight) + (buffer[pixelOffset + 5] * secondWeight);
1076    }
1077  }
1078  return outputBuffer;
1079}
1080Resize.prototype.resizeWidthRGBA = function (buffer) {
1081  var ratioWeight = this.ratioWeightWidthPass;
1082  var weight = 0;
1083  var amountToNext = 0;
1084  var actualPosition = 0;
1085  var currentPosition = 0;
1086  var line = 0;
1087  var pixelOffset = 0;
1088  var outputOffset = 0;
1089  var nextLineOffsetOriginalWidth = this.originalWidthMultipliedByChannels - 3;
1090  var nextLineOffsetTargetWidth = this.targetWidthMultipliedByChannels - 3;
1091  var output = this.outputWidthWorkBench;
1092  var outputBuffer = this.widthBuffer;
1093  do {
1094    for (line = 0; line < this.originalHeightMultipliedByChannels;) {
1095      output[line++] = 0;
1096      output[line++] = 0;
1097      output[line++] = 0;
1098      output[line++] = 0;
1099    }
1100    weight = ratioWeight;
1101    do {
1102      amountToNext = 1 + actualPosition - currentPosition;
1103      if (weight >= amountToNext) {
1104        for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
1105          output[line++] += buffer[pixelOffset++] * amountToNext;
1106          output[line++] += buffer[pixelOffset++] * amountToNext;
1107          output[line++] += buffer[pixelOffset++] * amountToNext;
1108          output[line++] += buffer[pixelOffset] * amountToNext;
1109        }
1110        currentPosition = actualPosition = actualPosition + 4;
1111        weight -= amountToNext;
1112      }
1113      else {
1114        for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
1115          output[line++] += buffer[pixelOffset++] * weight;
1116          output[line++] += buffer[pixelOffset++] * weight;
1117          output[line++] += buffer[pixelOffset++] * weight;
1118          output[line++] += buffer[pixelOffset] * weight;
1119        }
1120        currentPosition += weight;
1121        break;
1122      }
1123    } while (weight > 0 && actualPosition < this.originalWidthMultipliedByChannels);
1124    for (line = 0, pixelOffset = outputOffset; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetTargetWidth) {
1125      outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
1126      outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
1127      outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
1128      outputBuffer[pixelOffset] = output[line++] / ratioWeight;
1129    }
1130    outputOffset += 4;
1131  } while (outputOffset < this.targetWidthMultipliedByChannels);
1132  return outputBuffer;
1133}
1134Resize.prototype.resizeWidthInterpolatedRGBA = function (buffer) {
1135  var ratioWeight = (this.widthOriginal - 1) / this.targetWidth;
1136  var weight = 0;
1137  var finalOffset = 0;
1138  var pixelOffset = 0;
1139  var outputBuffer = this.widthBuffer;
1140  for (var targetPosition = 0; targetPosition < this.targetWidthMultipliedByChannels; targetPosition += 4, weight += ratioWeight) {
1141    //Calculate weightings:
1142    secondWeight = weight % 1;
1143    firstWeight = 1 - secondWeight;
1144    //Interpolate:
1145    for (finalOffset = targetPosition, pixelOffset = Math.floor(weight) * 4; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
1146      outputBuffer[finalOffset] = (buffer[pixelOffset] * firstWeight) + (buffer[pixelOffset + 4] * secondWeight);
1147      outputBuffer[finalOffset + 1] = (buffer[pixelOffset + 1] * firstWeight) + (buffer[pixelOffset + 5] * secondWeight);
1148      outputBuffer[finalOffset + 2] = (buffer[pixelOffset + 2] * firstWeight) + (buffer[pixelOffset + 6] * secondWeight);
1149      outputBuffer[finalOffset + 3] = (buffer[pixelOffset + 3] * firstWeight) + (buffer[pixelOffset + 7] * secondWeight);
1150    }
1151  }
1152  return outputBuffer;
1153}
1154Resize.prototype.resizeHeightRGB = function (buffer) {
1155  var ratioWeight = this.ratioWeightHeightPass;
1156  var weight = 0;
1157  var amountToNext = 0;
1158  var actualPosition = 0;
1159  var currentPosition = 0;
1160  var pixelOffset = 0;
1161  var outputOffset = 0;
1162  var output = this.outputHeightWorkBench;
1163  var outputBuffer = this.heightBuffer;
1164  do {
1165    for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
1166      output[pixelOffset++] = 0;
1167      output[pixelOffset++] = 0;
1168      output[pixelOffset++] = 0;
1169    }
1170    weight = ratioWeight;
1171    do {
1172      amountToNext = 1 + actualPosition - currentPosition;
1173      if (weight >= amountToNext) {
1174        for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
1175          output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
1176          output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
1177          output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
1178        }
1179        currentPosition = actualPosition;
1180        weight -= amountToNext;
1181      }
1182      else {
1183        for (pixelOffset = 0, amountToNext = actualPosition; pixelOffset < this.targetWidthMultipliedByChannels;) {
1184          output[pixelOffset++] += buffer[amountToNext++] * weight;
1185          output[pixelOffset++] += buffer[amountToNext++] * weight;
1186          output[pixelOffset++] += buffer[amountToNext++] * weight;
1187        }
1188        currentPosition += weight;
1189        break;
1190      }
1191    } while (weight > 0 && actualPosition < this.widthPassResultSize);
1192    for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
1193      outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
1194      outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
1195      outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
1196    }
1197  } while (outputOffset < this.finalResultSize);
1198  return outputBuffer;
1199}
1200Resize.prototype.resizeHeightInterpolated = function (buffer) {
1201  var ratioWeight = (this.heightOriginal - 1) / this.targetHeight;
1202  var weight = 0;
1203  var finalOffset = 0;
1204  var pixelOffset = 0;
1205  var pixelOffsetAccumulated = 0;
1206  var pixelOffsetAccumulated2 = 0;
1207  var outputBuffer = this.heightBuffer;
1208  do {
1209    //Calculate weightings:
1210    secondWeight = weight % 1;
1211    firstWeight = 1 - secondWeight;
1212    //Interpolate:
1213    pixelOffsetAccumulated = Math.floor(weight) * this.targetWidthMultipliedByChannels;
1214    pixelOffsetAccumulated2 = pixelOffsetAccumulated + this.targetWidthMultipliedByChannels;
1215    for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels; ++pixelOffset) {
1216      outputBuffer[finalOffset++] = (buffer[pixelOffsetAccumulated + pixelOffset] * firstWeight) + (buffer[pixelOffsetAccumulated2 + pixelOffset] * secondWeight);
1217    }
1218    weight += ratioWeight;
1219  } while (finalOffset < this.finalResultSize);
1220  return outputBuffer;
1221}
1222Resize.prototype.resizeHeightRGBA = function (buffer) {
1223  var ratioWeight = this.ratioWeightHeightPass;
1224  var weight = 0;
1225  var amountToNext = 0;
1226  var actualPosition = 0;
1227  var currentPosition = 0;
1228  var pixelOffset = 0;
1229  var outputOffset = 0;
1230  var output = this.outputHeightWorkBench;
1231  var outputBuffer = this.heightBuffer;
1232  do {
1233    for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
1234      output[pixelOffset++] = 0;
1235      output[pixelOffset++] = 0;
1236      output[pixelOffset++] = 0;
1237      output[pixelOffset++] = 0;
1238    }
1239    weight = ratioWeight;
1240    do {
1241      amountToNext = 1 + actualPosition - currentPosition;
1242      if (weight >= amountToNext) {
1243        for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
1244          output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
1245          output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
1246          output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
1247          output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
1248        }
1249        currentPosition = actualPosition;
1250        weight -= amountToNext;
1251      }
1252      else {
1253        for (pixelOffset = 0, amountToNext = actualPosition; pixelOffset < this.targetWidthMultipliedByChannels;) {
1254          output[pixelOffset++] += buffer[amountToNext++] * weight;
1255          output[pixelOffset++] += buffer[amountToNext++] * weight;
1256          output[pixelOffset++] += buffer[amountToNext++] * weight;
1257          output[pixelOffset++] += buffer[amountToNext++] * weight;
1258        }
1259        currentPosition += weight;
1260        break;
1261      }
1262    } while (weight > 0 && actualPosition < this.widthPassResultSize);
1263    for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
1264      outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
1265      outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
1266      outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
1267      outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
1268    }
1269  } while (outputOffset < this.finalResultSize);
1270  return outputBuffer;
1271}
1272Resize.prototype.resizeHeightInterpolatedRGBA = function (buffer) {
1273  var ratioWeight = (this.heightOriginal - 1) / this.targetHeight;
1274  var weight = 0;
1275  var finalOffset = 0;
1276  var pixelOffset = 0;
1277  var outputBuffer = this.heightBuffer;
1278  while (pixelOffset < this.finalResultSize) {
1279    //Calculate weightings:
1280    secondWeight = weight % 1;
1281    firstWeight = 1 - secondWeight;
1282    //Interpolate:
1283    for (pixelOffset = Math.floor(weight) * 4; pixelOffset < this.targetWidthMultipliedByChannels; pixelOffset += 4) {
1284      outputBuffer[finalOffset++] = (buffer[pixelOffset] * firstWeight) + (buffer[pixelOffset + 4] * secondWeight);
1285      outputBuffer[finalOffset++] = (buffer[pixelOffset + 1] * firstWeight) + (buffer[pixelOffset + 5] * secondWeight);
1286      outputBuffer[finalOffset++] = (buffer[pixelOffset + 2] * firstWeight) + (buffer[pixelOffset + 6] * secondWeight);
1287      outputBuffer[finalOffset++] = (buffer[pixelOffset + 3] * firstWeight) + (buffer[pixelOffset + 7] * secondWeight);
1288    }
1289    weight += ratioWeight;
1290  }
1291  return outputBuffer;
1292}
1293Resize.prototype.resize = function (buffer) {
1294  return this.resizeHeight(this.resizeWidth(buffer));
1295}
1296Resize.prototype.bypassResizer = function (buffer) {
1297  //Just return the buffer passsed:
1298  return buffer;
1299}
1300Resize.prototype.initializeFirstPassBuffers = function (BILINEARAlgo) {
1301  //Initialize the internal width pass buffers:
1302  this.widthBuffer = this.generateFloatBuffer(this.widthPassResultSize);
1303  if (!BILINEARAlgo) {
1304    this.outputWidthWorkBench = this.generateFloatBuffer(this.originalHeightMultipliedByChannels);
1305  }
1306}
1307Resize.prototype.initializeSecondPassBuffers = function (BILINEARAlgo) {
1308  //Initialize the internal height pass buffers:
1309  this.heightBuffer = this.generateUint8Buffer(this.finalResultSize);
1310  if (!BILINEARAlgo) {
1311    this.outputHeightWorkBench = this.generateFloatBuffer(this.targetWidthMultipliedByChannels);
1312  }
1313}
1314Resize.prototype.generateFloatBuffer = function (bufferLength) {
1315  //Generate a float32 typed array buffer:
1316  try {
1317    return new Float32Array(bufferLength);
1318  }
1319  catch (error) {
1320    return [];
1321  }
1322}
1323Resize.prototype.generateUint8Buffer = function (bufferLength) {
1324  //Generate a uint8 typed array buffer:
1325  try {
1326    return this.checkForOperaMathBug(new Uint8Array(bufferLength));
1327  }
1328  catch (error) {
1329    return [];
1330  }
1331}
1332Resize.prototype.checkForOperaMathBug = function (typedArray) {
1333  typedArray[0] = -1;
1334  typedArray[0] >>= 0;
1335  if (typedArray[0] != 0xFF) {
1336    return [];
1337  }
1338  else {
1339    return typedArray;
1340  }
1341}
1342
1343// End of js/other/resize.js file.
1344
1345// Start of js/GameBoyCore.js file.
1346
1347"use strict";
1348/*
1349 * JavaScript GameBoy Color Emulator
1350 * Copyright (C) 2010 - 2012 Grant Galitz
1351 *
1352 * This program is free software; you can redistribute it and/or
1353 * modify it under the terms of the GNU General Public License
1354 * version 2 as published by the Free Software Foundation.
1355 * The full license is available at http://www.gnu.org/licenses/gpl.html
1356 *
1357 * This program is distributed in the hope that it will be useful,
1358 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1359 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1360 * GNU General Public License for more details.
1361 *
1362 */
1363function GameBoyCore(canvas, ROMImage) {
1364  //Params, etc...
1365  this.canvas = canvas;            //Canvas DOM object for drawing out the graphics to.
1366  this.drawContext = null;          // LCD Context
1367  this.ROMImage = ROMImage;          //The game's ROM.
1368  //CPU Registers and Flags:
1369  this.registerA = 0x01;             //Register A (Accumulator)
1370  this.FZero = true;               //Register F  - Result was zero
1371  this.FSubtract = false;            //Register F  - Subtraction was executed
1372  this.FHalfCarry = true;            //Register F  - Half carry or half borrow
1373  this.FCarry = true;              //Register F  - Carry or borrow
1374  this.registerB = 0x00;            //Register B
1375  this.registerC = 0x13;            //Register C
1376  this.registerD = 0x00;            //Register D
1377  this.registerE = 0xD8;            //Register E
1378  this.registersHL = 0x014D;          //Registers H and L combined
1379  this.stackPointer = 0xFFFE;          //Stack Pointer
1380  this.programCounter = 0x0100;        //Program Counter
1381  //Some CPU Emulation State Variables:
1382  this.CPUCyclesTotal = 0;          //Relative CPU clocking to speed set, rounded appropriately.
1383  this.CPUCyclesTotalBase = 0;        //Relative CPU clocking to speed set base.
1384  this.CPUCyclesTotalCurrent = 0;        //Relative CPU clocking to speed set, the directly used value.
1385  this.CPUCyclesTotalRoundoff = 0;      //Clocking per iteration rounding catch.
1386  this.baseCPUCyclesPerIteration = 0;    //CPU clocks per iteration at 1x speed.
1387  this.remainingClocks = 0;          //HALT clocking overrun carry over.
1388  this.inBootstrap = true;          //Whether we're in the GBC boot ROM.
1389  this.usedBootROM = false;          //Updated upon ROM loading...
1390  this.usedGBCBootROM = false;        //Did we boot to the GBC boot ROM?
1391  this.halt = false;              //Has the CPU been suspended until the next interrupt?
1392  this.skipPCIncrement = false;        //Did we trip the DMG Halt bug?
1393  this.stopEmulator = 3;            //Has the emulation been paused or a frame has ended?
1394  this.IME = true;              //Are interrupts enabled?
1395  this.IRQLineMatched = 0;          //CPU IRQ assertion.
1396  this.interruptsRequested = 0;        //IF Register
1397  this.interruptsEnabled = 0;          //IE Register
1398  this.hdmaRunning = false;          //HDMA Transfer Flag - GBC only
1399  this.CPUTicks = 0;              //The number of clock cycles emulated.
1400  this.doubleSpeedShifter = 0;        //GBC double speed clocking shifter.
1401  this.JoyPad = 0xFF;              //Joypad State (two four-bit states actually)
1402  this.CPUStopped = false;          //CPU STOP status.
1403  //Main RAM, MBC RAM, GBC Main RAM, VRAM, etc.
1404  this.memoryReader = [];            //Array of functions mapped to read back memory
1405  this.memoryWriter = [];            //Array of functions mapped to write to memory
1406  this.memoryHighReader = [];          //Array of functions mapped to read back 0xFFXX memory
1407  this.memoryHighWriter = [];          //Array of functions mapped to write to 0xFFXX memory
1408  this.ROM = [];                //The full ROM file dumped to an array.
1409  this.memory = [];              //Main Core Memory
1410  this.MBCRam = [];              //Switchable RAM (Used by games for more RAM) for the main memory range 0xA000 - 0xC000.
1411  this.VRAM = [];                //Extra VRAM bank for GBC.
1412  this.GBCMemory = [];            //GBC main RAM Banks
1413  this.MBC1Mode = false;            //MBC1 Type (4/32, 16/8)
1414  this.MBCRAMBanksEnabled = false;      //MBC RAM Access Control.
1415  this.currMBCRAMBank = 0;          //MBC Currently Indexed RAM Bank
1416  this.currMBCRAMBankPosition = -0xA000;    //MBC Position Adder;
1417  this.cGBC = false;              //GameBoy Color detection.
1418  this.gbcRamBank = 1;            //Currently Switched GameBoy Color ram bank
1419  this.gbcRamBankPosition = -0xD000;      //GBC RAM offset from address start.
1420  this.gbcRamBankPositionECHO = -0xF000;    //GBC RAM (ECHO mirroring) offset from address start.
1421  this.RAMBanks = [0, 1, 2, 4, 16];      //Used to map the RAM banks to maximum size the MBC used can do.
1422  this.ROMBank1offs = 0;            //Offset of the ROM bank switching.
1423  this.currentROMBank = 0;          //The parsed current ROM bank selection.
1424  this.cartridgeType = 0;            //Cartridge Type
1425  this.name = "";                //Name of the game
1426  this.gameCode = "";              //Game code (Suffix for older games)
1427  this.fromSaveState = false;          //A boolean to see if this was loaded in as a save state.
1428  this.savedStateFileName = "";        //When loaded in as a save state, this will not be empty.
1429  this.STATTracker = 0;            //Tracker for STAT triggering.
1430  this.modeSTAT = 0;              //The scan line mode (for lines 1-144 it's 2-3-0, for 145-154 it's 1)
1431  this.spriteCount = 252;            //Mode 3 extra clocking counter (Depends on how many sprites are on the current line.).
1432  this.LYCMatchTriggerSTAT = false;      //Should we trigger an interrupt if LY==LYC?
1433  this.mode2TriggerSTAT = false;        //Should we trigger an interrupt if in mode 2?
1434  this.mode1TriggerSTAT = false;        //Should we trigger an interrupt if in mode 1?
1435  this.mode0TriggerSTAT = false;        //Should we trigger an interrupt if in mode 0?
1436  this.LCDisOn = false;            //Is the emulated LCD controller on?
1437  this.LINECONTROL = [];            //Array of functions to handle each scan line we do (onscreen + offscreen)
1438  this.DISPLAYOFFCONTROL = [function (parentObj) {
1439    //Array of line 0 function to handle the LCD controller when it's off (Do nothing!).
1440  }];
1441  this.LCDCONTROL = null;            //Pointer to either LINECONTROL or DISPLAYOFFCONTROL.
1442  this.initializeLCDController();        //Compile the LCD controller functions.
1443  //RTC (Real Time Clock for MBC3):
1444  this.RTCisLatched = false;
1445  this.latchedSeconds = 0;          //RTC latched seconds.
1446  this.latchedMinutes = 0;          //RTC latched minutes.
1447  this.latchedHours = 0;            //RTC latched hours.
1448  this.latchedLDays = 0;            //RTC latched lower 8-bits of the day counter.
1449  this.latchedHDays = 0;            //RTC latched high-bit of the day counter.
1450  this.RTCSeconds = 0;            //RTC seconds counter.
1451  this.RTCMinutes = 0;            //RTC minutes counter.
1452  this.RTCHours = 0;              //RTC hours counter.
1453  this.RTCDays = 0;              //RTC days counter.
1454  this.RTCDayOverFlow = false;        //Did the RTC overflow and wrap the day counter?
1455  this.RTCHALT = false;            //Is the RTC allowed to clock up?
1456  //Gyro:
1457  this.highX = 127;
1458  this.lowX = 127;
1459  this.highY = 127;
1460  this.lowY = 127;
1461  //Sound variables:
1462  this.audioHandle = null;            //XAudioJS handle
1463  this.numSamplesTotal = 0;            //Length of the sound buffers.
1464  this.sampleSize = 0;              //Length of the sound buffer for one channel.
1465  this.dutyLookup = [                //Map the duty values given to ones we can work with.
1466    [false, false, false, false, false, false, false, true],
1467    [true, false, false, false, false, false, false, true],
1468    [true, false, false, false, false, true, true, true],
1469    [false, true, true, true, true, true, true, false]
1470  ];
1471  this.currentBuffer = [];            //The audio buffer we're working on.
1472  this.bufferContainAmount = 0;          //Buffer maintenance metric.
1473  this.LSFR15Table = null;
1474  this.LSFR7Table = null;
1475  this.noiseSampleTable = null;
1476  this.initializeAudioStartState();
1477  this.soundMasterEnabled = false;      //As its name implies
1478  this.channel3PCM = null;          //Channel 3 adjusted sample buffer.
1479  //Vin Shit:
1480  this.VinLeftChannelMasterVolume = 8;    //Computed post-mixing volume.
1481  this.VinRightChannelMasterVolume = 8;    //Computed post-mixing volume.
1482  //Channel paths enabled:
1483  this.leftChannel1 = false;
1484  this.leftChannel2 = false;
1485  this.leftChannel3 = false;
1486  this.leftChannel4 = false;
1487  this.rightChannel1 = false;
1488  this.rightChannel2 = false;
1489  this.rightChannel3 = false;
1490  this.rightChannel4 = false;
1491  //Channel output level caches:
1492  this.channel1currentSampleLeft = 0;
1493  this.channel1currentSampleRight = 0;
1494  this.channel2currentSampleLeft = 0;
1495  this.channel2currentSampleRight = 0;
1496  this.channel3currentSampleLeft = 0;
1497  this.channel3currentSampleRight = 0;
1498  this.channel4currentSampleLeft = 0;
1499  this.channel4currentSampleRight = 0;
1500  this.channel1currentSampleLeftSecondary = 0;
1501  this.channel1currentSampleRightSecondary = 0;
1502  this.channel2currentSampleLeftSecondary = 0;
1503  this.channel2currentSampleRightSecondary = 0;
1504  this.channel3currentSampleLeftSecondary = 0;
1505  this.channel3currentSampleRightSecondary = 0;
1506  this.channel4currentSampleLeftSecondary = 0;
1507  this.channel4currentSampleRightSecondary = 0;
1508  this.channel1currentSampleLeftTrimary = 0;
1509  this.channel1currentSampleRightTrimary = 0;
1510  this.channel2currentSampleLeftTrimary = 0;
1511  this.channel2currentSampleRightTrimary = 0;
1512  this.mixerOutputCache = 0;
1513  //Pre-multipliers to cache some calculations:
1514  this.initializeTiming();
1515  this.machineOut = 0;        //Premultiplier for audio samples per instruction.
1516  //Audio generation counters:
1517  this.audioTicks = 0;        //Used to sample the audio system every x CPU instructions.
1518  this.audioIndex = 0;        //Used to keep alignment on audio generation.
1519  this.rollover = 0;          //Used to keep alignment on the number of samples to output (Realign from counter alias).
1520  //Timing Variables
1521  this.emulatorTicks = 0;        //Times for how many instructions to execute before ending the loop.
1522  this.DIVTicks = 56;          //DIV Ticks Counter (Invisible lower 8-bit)
1523  this.LCDTicks = 60;          //Counter for how many instructions have been executed on a scanline so far.
1524  this.timerTicks = 0;        //Counter for the TIMA timer.
1525  this.TIMAEnabled = false;      //Is TIMA enabled?
1526  this.TACClocker = 1024;        //Timer Max Ticks
1527  this.serialTimer = 0;        //Serial IRQ Timer
1528  this.serialShiftTimer = 0;      //Serial Transfer Shift Timer
1529  this.serialShiftTimerAllocated = 0;  //Serial Transfer Shift Timer Refill
1530  this.IRQEnableDelay = 0;      //Are the interrupts on queue to be enabled?
1531  var dateVar = new_Date();     // The line is changed for benchmarking.
1532  this.lastIteration = dateVar.getTime();//The last time we iterated the main loop.
1533  dateVar = new_Date();         // The line is changed for benchmarking.
1534  this.firstIteration = dateVar.getTime();
1535  this.iterations = 0;
1536  this.actualScanLine = 0;      //Actual scan line...
1537  this.lastUnrenderedLine = 0;    //Last rendered scan line...
1538  this.queuedScanLines = 0;
1539  this.totalLinesPassed = 0;
1540  this.haltPostClocks = 0;      //Post-Halt clocking.
1541  //ROM Cartridge Components:
1542  this.cMBC1 = false;          //Does the cartridge use MBC1?
1543  this.cMBC2 = false;          //Does the cartridge use MBC2?
1544  this.cMBC3 = false;          //Does the cartridge use MBC3?
1545  this.cMBC5 = false;          //Does the cartridge use MBC5?
1546  this.cMBC7 = false;          //Does the cartridge use MBC7?
1547  this.cSRAM = false;          //Does the cartridge use save RAM?
1548  this.cMMMO1 = false;        //...
1549  this.cRUMBLE = false;        //Does the cartridge use the RUMBLE addressing (modified MBC5)?
1550  this.cCamera = false;        //Is the cartridge actually a GameBoy Camera?
1551  this.cTAMA5 = false;        //Does the cartridge use TAMA5? (Tamagotchi Cartridge)
1552  this.cHuC3 = false;          //Does the cartridge use HuC3 (Hudson Soft / modified MBC3)?
1553  this.cHuC1 = false;          //Does the cartridge use HuC1 (Hudson Soft / modified MBC1)?
1554  this.cTIMER = false;        //Does the cartridge have an RTC?
1555  this.ROMBanks = [          // 1 Bank = 16 KBytes = 256 Kbits
1556    2, 4, 8, 16, 32, 64, 128, 256, 512
1557  ];
1558  this.ROMBanks[0x52] = 72;
1559  this.ROMBanks[0x53] = 80;
1560  this.ROMBanks[0x54] = 96;
1561  this.numRAMBanks = 0;          //How many RAM banks were actually allocated?
1562  ////Graphics Variables
1563  this.currVRAMBank = 0;          //Current VRAM bank for GBC.
1564  this.backgroundX = 0;          //Register SCX (X-Scroll)
1565  this.backgroundY = 0;          //Register SCY (Y-Scroll)
1566  this.gfxWindowDisplay = false;      //Is the windows enabled?
1567  this.gfxSpriteShow = false;        //Are sprites enabled?
1568  this.gfxSpriteNormalHeight = true;    //Are we doing 8x8 or 8x16 sprites?
1569  this.bgEnabled = true;          //Is the BG enabled?
1570  this.BGPriorityEnabled = true;      //Can we flag the BG for priority over sprites?
1571  this.gfxWindowCHRBankPosition = 0;    //The current bank of the character map the window uses.
1572  this.gfxBackgroundCHRBankPosition = 0;  //The current bank of the character map the BG uses.
1573  this.gfxBackgroundBankOffset = 0x80;  //Fast mapping of the tile numbering/
1574  this.windowY = 0;            //Current Y offset of the window.
1575  this.windowX = 0;            //Current X offset of the window.
1576  this.drewBlank = 0;            //To prevent the repeating of drawing a blank screen.
1577  this.drewFrame = false;          //Throttle how many draws we can do to once per iteration.
1578  this.midScanlineOffset = -1;      //mid-scanline rendering offset.
1579  this.pixelEnd = 0;            //track the x-coord limit for line rendering (mid-scanline usage).
1580  this.currentX = 0;            //The x-coord we left off at for mid-scanline rendering.
1581  //BG Tile Pointer Caches:
1582  this.BGCHRBank1 = null;
1583  this.BGCHRBank2 = null;
1584  this.BGCHRCurrentBank = null;
1585  //Tile Data Cache:
1586  this.tileCache = null;
1587  //Palettes:
1588  this.colors = [0xEFFFDE, 0xADD794, 0x529273, 0x183442];      //"Classic" GameBoy palette colors.
1589  this.OBJPalette = null;
1590  this.BGPalette = null;
1591  this.gbcOBJRawPalette = null;
1592  this.gbcBGRawPalette = null;
1593  this.gbOBJPalette = null;
1594  this.gbBGPalette = null;
1595  this.gbcOBJPalette = null;
1596  this.gbcBGPalette = null;
1597  this.gbBGColorizedPalette = null;
1598  this.gbOBJColorizedPalette = null;
1599  this.cachedBGPaletteConversion = null;
1600  this.cachedOBJPaletteConversion = null;
1601  this.updateGBBGPalette = this.updateGBRegularBGPalette;
1602  this.updateGBOBJPalette = this.updateGBRegularOBJPalette;
1603  this.colorizedGBPalettes = false;
1604  this.BGLayerRender = null;      //Reference to the BG rendering function.
1605  this.WindowLayerRender = null;    //Reference to the window rendering function.
1606  this.SpriteLayerRender = null;    //Reference to the OAM rendering function.
1607  this.frameBuffer = [];        //The internal frame-buffer.
1608  this.swizzledFrame = null;      //The secondary gfx buffer that holds the converted RGBA values.
1609  this.canvasBuffer = null;      //imageData handle
1610  this.pixelStart = 0;        //Temp variable for holding the current working framebuffer offset.
1611  //Variables used for scaling in JS:
1612  this.onscreenWidth = this.offscreenWidth = 160;
1613  this.onscreenHeight = this.offScreenheight = 144;
1614  this.offscreenRGBCount = this.onscreenWidth * this.onscreenHeight * 4;
1615  //Initialize the white noise cache tables ahead of time:
1616  this.intializeWhiteNoise();
1617}
1618
1619// Start of code changed for benchmarking (removed ROM):
1620GameBoyCore.prototype.GBBOOTROM = [];
1621GameBoyCore.prototype.GBCBOOTROM = [];
1622// End of code changed for benchmarking.
1623
1624GameBoyCore.prototype.ffxxDump = [  //Dump of the post-BOOT I/O register state (From gambatte):
1625  0x0F, 0x00, 0x7C, 0xFF, 0x00, 0x00, 0x00, 0xF8,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01,
1626  0x80, 0xBF, 0xF3, 0xFF, 0xBF, 0xFF, 0x3F, 0x00,   0xFF, 0xBF, 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, 0xFF,
1627  0xFF, 0x00, 0x00, 0xBF, 0x77, 0xF3, 0xF1, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1628  0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,   0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
1629  0x91, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC,   0x00, 0x00, 0x00, 0x00, 0xFF, 0x7E, 0xFF, 0xFE,
1630  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1631  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xC0, 0xFF, 0xC1, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
1632  0xF8, 0xFF, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1633  0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B,   0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D,
1634  0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E,   0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99,
1635  0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC,   0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E,
1636  0x45, 0xEC, 0x52, 0xFA, 0x08, 0xB7, 0x07, 0x5D,   0x01, 0xFD, 0xC0, 0xFF, 0x08, 0xFC, 0x00, 0xE5,
1637  0x0B, 0xF8, 0xC2, 0xCE, 0xF4, 0xF9, 0x0F, 0x7F,   0x45, 0x6D, 0x3D, 0xFE, 0x46, 0x97, 0x33, 0x5E,
1638  0x08, 0xEF, 0xF1, 0xFF, 0x86, 0x83, 0x24, 0x74,   0x12, 0xFC, 0x00, 0x9F, 0xB4, 0xB7, 0x06, 0xD5,
1639  0xD0, 0x7A, 0x00, 0x9E, 0x04, 0x5F, 0x41, 0x2F,   0x1D, 0x77, 0x36, 0x75, 0x81, 0xAA, 0x70, 0x3A,
1640  0x98, 0xD1, 0x71, 0x02, 0x4D, 0x01, 0xC1, 0xFF,   0x0D, 0x00, 0xD3, 0x05, 0xF9, 0x00, 0x0B, 0x00
1641];
1642GameBoyCore.prototype.OPCODE = [
1643  //NOP
1644  //#0x00:
1645  function (parentObj) {
1646    //Do Nothing...
1647  },
1648  //LD BC, nn
1649  //#0x01:
1650  function (parentObj) {
1651    parentObj.registerC = parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
1652    parentObj.registerB = parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF);
1653    parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
1654  },
1655  //LD (BC), A
1656  //#0x02:
1657  function (parentObj) {
1658    parentObj.memoryWrite((parentObj.registerB << 8) | parentObj.registerC, parentObj.registerA);
1659  },
1660  //INC BC
1661  //#0x03:
1662  function (parentObj) {
1663    var temp_var = ((parentObj.registerB << 8) | parentObj.registerC) + 1;
1664    parentObj.registerB = (temp_var >> 8) & 0xFF;
1665    parentObj.registerC = temp_var & 0xFF;
1666  },
1667  //INC B
1668  //#0x04:
1669  function (parentObj) {
1670    parentObj.registerB = (parentObj.registerB + 1) & 0xFF;
1671    parentObj.FZero = (parentObj.registerB == 0);
1672    parentObj.FHalfCarry = ((parentObj.registerB & 0xF) == 0);
1673    parentObj.FSubtract = false;
1674  },
1675  //DEC B
1676  //#0x05:
1677  function (parentObj) {
1678    parentObj.registerB = (parentObj.registerB - 1) & 0xFF;
1679    parentObj.FZero = (parentObj.registerB == 0);
1680    parentObj.FHalfCarry = ((parentObj.registerB & 0xF) == 0xF);
1681    parentObj.FSubtract = true;
1682  },
1683  //LD B, n
1684  //#0x06:
1685  function (parentObj) {
1686    parentObj.registerB = parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
1687    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
1688  },
1689  //RLCA
1690  //#0x07:
1691  function (parentObj) {
1692    parentObj.FCarry = (parentObj.registerA > 0x7F);
1693    parentObj.registerA = ((parentObj.registerA << 1) & 0xFF) | (parentObj.registerA >> 7);
1694    parentObj.FZero = parentObj.FSubtract = parentObj.FHalfCarry = false;
1695  },
1696  //LD (nn), SP
1697  //#0x08:
1698  function (parentObj) {
1699    var temp_var = (parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
1700    parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
1701    parentObj.memoryWrite(temp_var, parentObj.stackPointer & 0xFF);
1702    parentObj.memoryWrite((temp_var + 1) & 0xFFFF, parentObj.stackPointer >> 8);
1703  },
1704  //ADD HL, BC
1705  //#0x09:
1706  function (parentObj) {
1707    var dirtySum = parentObj.registersHL + ((parentObj.registerB << 8) | parentObj.registerC);
1708    parentObj.FHalfCarry = ((parentObj.registersHL & 0xFFF) > (dirtySum & 0xFFF));
1709    parentObj.FCarry = (dirtySum > 0xFFFF);
1710    parentObj.registersHL = dirtySum & 0xFFFF;
1711    parentObj.FSubtract = false;
1712  },
1713  //LD A, (BC)
1714  //#0x0A:
1715  function (parentObj) {
1716    parentObj.registerA = parentObj.memoryRead((parentObj.registerB << 8) | parentObj.registerC);
1717  },
1718  //DEC BC
1719  //#0x0B:
1720  function (parentObj) {
1721    var temp_var = (((parentObj.registerB << 8) | parentObj.registerC) - 1) & 0xFFFF;
1722    parentObj.registerB = temp_var >> 8;
1723    parentObj.registerC = temp_var & 0xFF;
1724  },
1725  //INC C
1726  //#0x0C:
1727  function (parentObj) {
1728    parentObj.registerC = (parentObj.registerC + 1) & 0xFF;
1729    parentObj.FZero = (parentObj.registerC == 0);
1730    parentObj.FHalfCarry = ((parentObj.registerC & 0xF) == 0);
1731    parentObj.FSubtract = false;
1732  },
1733  //DEC C
1734  //#0x0D:
1735  function (parentObj) {
1736    parentObj.registerC = (parentObj.registerC - 1) & 0xFF;
1737    parentObj.FZero = (parentObj.registerC == 0);
1738    parentObj.FHalfCarry = ((parentObj.registerC & 0xF) == 0xF);
1739    parentObj.FSubtract = true;
1740  },
1741  //LD C, n
1742  //#0x0E:
1743  function (parentObj) {
1744    parentObj.registerC = parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
1745    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
1746  },
1747  //RRCA
1748  //#0x0F:
1749  function (parentObj) {
1750    parentObj.registerA = (parentObj.registerA >> 1) | ((parentObj.registerA & 1) << 7);
1751    parentObj.FCarry = (parentObj.registerA > 0x7F);
1752    parentObj.FZero = parentObj.FSubtract = parentObj.FHalfCarry = false;
1753  },
1754  //STOP
1755  //#0x10:
1756  function (parentObj) {
1757    if (parentObj.cGBC) {
1758      if ((parentObj.memory[0xFF4D] & 0x01) == 0x01) {    //Speed change requested.
1759        if (parentObj.memory[0xFF4D] > 0x7F) {        //Go back to single speed mode.
1760          cout("Going into single clock speed mode.", 0);
1761          parentObj.doubleSpeedShifter = 0;
1762          parentObj.memory[0xFF4D] &= 0x7F;        //Clear the double speed mode flag.
1763        }
1764        else {                        //Go to double speed mode.
1765          cout("Going into double clock speed mode.", 0);
1766          parentObj.doubleSpeedShifter = 1;
1767          parentObj.memory[0xFF4D] |= 0x80;        //Set the double speed mode flag.
1768        }
1769        parentObj.memory[0xFF4D] &= 0xFE;          //Reset the request bit.
1770      }
1771      else {
1772        parentObj.handleSTOP();
1773      }
1774    }
1775    else {
1776      parentObj.handleSTOP();
1777    }
1778  },
1779  //LD DE, nn
1780  //#0x11:
1781  function (parentObj) {
1782    parentObj.registerE = parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
1783    parentObj.registerD = parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF);
1784    parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
1785  },
1786  //LD (DE), A
1787  //#0x12:
1788  function (parentObj) {
1789    parentObj.memoryWrite((parentObj.registerD << 8) | parentObj.registerE, parentObj.registerA);
1790  },
1791  //INC DE
1792  //#0x13:
1793  function (parentObj) {
1794    var temp_var = ((parentObj.registerD << 8) | parentObj.registerE) + 1;
1795    parentObj.registerD = (temp_var >> 8) & 0xFF;
1796    parentObj.registerE = temp_var & 0xFF;
1797  },
1798  //INC D
1799  //#0x14:
1800  function (parentObj) {
1801    parentObj.registerD = (parentObj.registerD + 1) & 0xFF;
1802    parentObj.FZero = (parentObj.registerD == 0);
1803    parentObj.FHalfCarry = ((parentObj.registerD & 0xF) == 0);
1804    parentObj.FSubtract = false;
1805  },
1806  //DEC D
1807  //#0x15:
1808  function (parentObj) {
1809    parentObj.registerD = (parentObj.registerD - 1) & 0xFF;
1810    parentObj.FZero = (parentObj.registerD == 0);
1811    parentObj.FHalfCarry = ((parentObj.registerD & 0xF) == 0xF);
1812    parentObj.FSubtract = true;
1813  },
1814  //LD D, n
1815  //#0x16:
1816  function (parentObj) {
1817    parentObj.registerD = parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
1818    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
1819  },
1820  //RLA
1821  //#0x17:
1822  function (parentObj) {
1823    var carry_flag = (parentObj.FCarry) ? 1 : 0;
1824    parentObj.FCarry = (parentObj.registerA > 0x7F);
1825    parentObj.registerA = ((parentObj.registerA << 1) & 0xFF) | carry_flag;
1826    parentObj.FZero = parentObj.FSubtract = parentObj.FHalfCarry = false;
1827  },
1828  //JR n
1829  //#0x18:
1830  function (parentObj) {
1831    parentObj.programCounter = (parentObj.programCounter + ((parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter) << 24) >> 24) + 1) & 0xFFFF;
1832  },
1833  //ADD HL, DE
1834  //#0x19:
1835  function (parentObj) {
1836    var dirtySum = parentObj.registersHL + ((parentObj.registerD << 8) | parentObj.registerE);
1837    parentObj.FHalfCarry = ((parentObj.registersHL & 0xFFF) > (dirtySum & 0xFFF));
1838    parentObj.FCarry = (dirtySum > 0xFFFF);
1839    parentObj.registersHL = dirtySum & 0xFFFF;
1840    parentObj.FSubtract = false;
1841  },
1842  //LD A, (DE)
1843  //#0x1A:
1844  function (parentObj) {
1845    parentObj.registerA = parentObj.memoryRead((parentObj.registerD << 8) | parentObj.registerE);
1846  },
1847  //DEC DE
1848  //#0x1B:
1849  function (parentObj) {
1850    var temp_var = (((parentObj.registerD << 8) | parentObj.registerE) - 1) & 0xFFFF;
1851    parentObj.registerD = temp_var >> 8;
1852    parentObj.registerE = temp_var & 0xFF;
1853  },
1854  //INC E
1855  //#0x1C:
1856  function (parentObj) {
1857    parentObj.registerE = (parentObj.registerE + 1) & 0xFF;
1858    parentObj.FZero = (parentObj.registerE == 0);
1859    parentObj.FHalfCarry = ((parentObj.registerE & 0xF) == 0);
1860    parentObj.FSubtract = false;
1861  },
1862  //DEC E
1863  //#0x1D:
1864  function (parentObj) {
1865    parentObj.registerE = (parentObj.registerE - 1) & 0xFF;
1866    parentObj.FZero = (parentObj.registerE == 0);
1867    parentObj.FHalfCarry = ((parentObj.registerE & 0xF) == 0xF);
1868    parentObj.FSubtract = true;
1869  },
1870  //LD E, n
1871  //#0x1E:
1872  function (parentObj) {
1873    parentObj.registerE = parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
1874    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
1875  },
1876  //RRA
1877  //#0x1F:
1878  function (parentObj) {
1879    var carry_flag = (parentObj.FCarry) ? 0x80 : 0;
1880    parentObj.FCarry = ((parentObj.registerA & 1) == 1);
1881    parentObj.registerA = (parentObj.registerA >> 1) | carry_flag;
1882    parentObj.FZero = parentObj.FSubtract = parentObj.FHalfCarry = false;
1883  },
1884  //JR NZ, n
1885  //#0x20:
1886  function (parentObj) {
1887    if (!parentObj.FZero) {
1888      parentObj.programCounter = (parentObj.programCounter + ((parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter) << 24) >> 24) + 1) & 0xFFFF;
1889      parentObj.CPUTicks += 4;
1890    }
1891    else {
1892      parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
1893    }
1894  },
1895  //LD HL, nn
1896  //#0x21:
1897  function (parentObj) {
1898    parentObj.registersHL = (parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
1899    parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
1900  },
1901  //LDI (HL), A
1902  //#0x22:
1903  function (parentObj) {
1904    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.registerA);
1905    parentObj.registersHL = (parentObj.registersHL + 1) & 0xFFFF;
1906  },
1907  //INC HL
1908  //#0x23:
1909  function (parentObj) {
1910    parentObj.registersHL = (parentObj.registersHL + 1) & 0xFFFF;
1911  },
1912  //INC H
1913  //#0x24:
1914  function (parentObj) {
1915    var H = ((parentObj.registersHL >> 8) + 1) & 0xFF;
1916    parentObj.FZero = (H == 0);
1917    parentObj.FHalfCarry = ((H & 0xF) == 0);
1918    parentObj.FSubtract = false;
1919    parentObj.registersHL = (H << 8) | (parentObj.registersHL & 0xFF);
1920  },
1921  //DEC H
1922  //#0x25:
1923  function (parentObj) {
1924    var H = ((parentObj.registersHL >> 8) - 1) & 0xFF;
1925    parentObj.FZero = (H == 0);
1926    parentObj.FHalfCarry = ((H & 0xF) == 0xF);
1927    parentObj.FSubtract = true;
1928    parentObj.registersHL = (H << 8) | (parentObj.registersHL & 0xFF);
1929  },
1930  //LD H, n
1931  //#0x26:
1932  function (parentObj) {
1933    parentObj.registersHL = (parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter) << 8) | (parentObj.registersHL & 0xFF);
1934    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
1935  },
1936  //DAA
1937  //#0x27:
1938  function (parentObj) {
1939    if (!parentObj.FSubtract) {
1940      if (parentObj.FCarry || parentObj.registerA > 0x99) {
1941        parentObj.registerA = (parentObj.registerA + 0x60) & 0xFF;
1942        parentObj.FCarry = true;
1943      }
1944      if (parentObj.FHalfCarry || (parentObj.registerA & 0xF) > 0x9) {
1945        parentObj.registerA = (parentObj.registerA + 0x06) & 0xFF;
1946        parentObj.FHalfCarry = false;
1947      }
1948    }
1949    else if (parentObj.FCarry && parentObj.FHalfCarry) {
1950      parentObj.registerA = (parentObj.registerA + 0x9A) & 0xFF;
1951      parentObj.FHalfCarry = false;
1952    }
1953    else if (parentObj.FCarry) {
1954      parentObj.registerA = (parentObj.registerA + 0xA0) & 0xFF;
1955    }
1956    else if (parentObj.FHalfCarry) {
1957      parentObj.registerA = (parentObj.registerA + 0xFA) & 0xFF;
1958      parentObj.FHalfCarry = false;
1959    }
1960    parentObj.FZero = (parentObj.registerA == 0);
1961  },
1962  //JR Z, n
1963  //#0x28:
1964  function (parentObj) {
1965    if (parentObj.FZero) {
1966      parentObj.programCounter = (parentObj.programCounter + ((parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter) << 24) >> 24) + 1) & 0xFFFF;
1967      parentObj.CPUTicks += 4;
1968    }
1969    else {
1970      parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
1971    }
1972  },
1973  //ADD HL, HL
1974  //#0x29:
1975  function (parentObj) {
1976    parentObj.FHalfCarry = ((parentObj.registersHL & 0xFFF) > 0x7FF);
1977    parentObj.FCarry = (parentObj.registersHL > 0x7FFF);
1978    parentObj.registersHL = (parentObj.registersHL << 1) & 0xFFFF;
1979    parentObj.FSubtract = false;
1980  },
1981  //LDI A, (HL)
1982  //#0x2A:
1983  function (parentObj) {
1984    parentObj.registerA = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
1985    parentObj.registersHL = (parentObj.registersHL + 1) & 0xFFFF;
1986  },
1987  //DEC HL
1988  //#0x2B:
1989  function (parentObj) {
1990    parentObj.registersHL = (parentObj.registersHL - 1) & 0xFFFF;
1991  },
1992  //INC L
1993  //#0x2C:
1994  function (parentObj) {
1995    var L = (parentObj.registersHL + 1) & 0xFF;
1996    parentObj.FZero = (L == 0);
1997    parentObj.FHalfCarry = ((L & 0xF) == 0);
1998    parentObj.FSubtract = false;
1999    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | L;
2000  },
2001  //DEC L
2002  //#0x2D:
2003  function (parentObj) {
2004    var L = (parentObj.registersHL - 1) & 0xFF;
2005    parentObj.FZero = (L == 0);
2006    parentObj.FHalfCarry = ((L & 0xF) == 0xF);
2007    parentObj.FSubtract = true;
2008    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | L;
2009  },
2010  //LD L, n
2011  //#0x2E:
2012  function (parentObj) {
2013    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
2014    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
2015  },
2016  //CPL
2017  //#0x2F:
2018  function (parentObj) {
2019    parentObj.registerA ^= 0xFF;
2020    parentObj.FSubtract = parentObj.FHalfCarry = true;
2021  },
2022  //JR NC, n
2023  //#0x30:
2024  function (parentObj) {
2025    if (!parentObj.FCarry) {
2026      parentObj.programCounter = (parentObj.programCounter + ((parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter) << 24) >> 24) + 1) & 0xFFFF;
2027      parentObj.CPUTicks += 4;
2028    }
2029    else {
2030      parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
2031    }
2032  },
2033  //LD SP, nn
2034  //#0x31:
2035  function (parentObj) {
2036    parentObj.stackPointer = (parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
2037    parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
2038  },
2039  //LDD (HL), A
2040  //#0x32:
2041  function (parentObj) {
2042    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.registerA);
2043    parentObj.registersHL = (parentObj.registersHL - 1) & 0xFFFF;
2044  },
2045  //INC SP
2046  //#0x33:
2047  function (parentObj) {
2048    parentObj.stackPointer = (parentObj.stackPointer + 1) & 0xFFFF;
2049  },
2050  //INC (HL)
2051  //#0x34:
2052  function (parentObj) {
2053    var temp_var = (parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) + 1) & 0xFF;
2054    parentObj.FZero = (temp_var == 0);
2055    parentObj.FHalfCarry = ((temp_var & 0xF) == 0);
2056    parentObj.FSubtract = false;
2057    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, temp_var);
2058  },
2059  //DEC (HL)
2060  //#0x35:
2061  function (parentObj) {
2062    var temp_var = (parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) - 1) & 0xFF;
2063    parentObj.FZero = (temp_var == 0);
2064    parentObj.FHalfCarry = ((temp_var & 0xF) == 0xF);
2065    parentObj.FSubtract = true;
2066    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, temp_var);
2067  },
2068  //LD (HL), n
2069  //#0x36:
2070  function (parentObj) {
2071    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter));
2072    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
2073  },
2074  //SCF
2075  //#0x37:
2076  function (parentObj) {
2077    parentObj.FCarry = true;
2078    parentObj.FSubtract = parentObj.FHalfCarry = false;
2079  },
2080  //JR C, n
2081  //#0x38:
2082  function (parentObj) {
2083    if (parentObj.FCarry) {
2084      parentObj.programCounter = (parentObj.programCounter + ((parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter) << 24) >> 24) + 1) & 0xFFFF;
2085      parentObj.CPUTicks += 4;
2086    }
2087    else {
2088      parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
2089    }
2090  },
2091  //ADD HL, SP
2092  //#0x39:
2093  function (parentObj) {
2094    var dirtySum = parentObj.registersHL + parentObj.stackPointer;
2095    parentObj.FHalfCarry = ((parentObj.registersHL & 0xFFF) > (dirtySum & 0xFFF));
2096    parentObj.FCarry = (dirtySum > 0xFFFF);
2097    parentObj.registersHL = dirtySum & 0xFFFF;
2098    parentObj.FSubtract = false;
2099  },
2100  //LDD A, (HL)
2101  //#0x3A:
2102  function (parentObj) {
2103    parentObj.registerA = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
2104    parentObj.registersHL = (parentObj.registersHL - 1) & 0xFFFF;
2105  },
2106  //DEC SP
2107  //#0x3B:
2108  function (parentObj) {
2109    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
2110  },
2111  //INC A
2112  //#0x3C:
2113  function (parentObj) {
2114    parentObj.registerA = (parentObj.registerA + 1) & 0xFF;
2115    parentObj.FZero = (parentObj.registerA == 0);
2116    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) == 0);
2117    parentObj.FSubtract = false;
2118  },
2119  //DEC A
2120  //#0x3D:
2121  function (parentObj) {
2122    parentObj.registerA = (parentObj.registerA - 1) & 0xFF;
2123    parentObj.FZero = (parentObj.registerA == 0);
2124    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) == 0xF);
2125    parentObj.FSubtract = true;
2126  },
2127  //LD A, n
2128  //#0x3E:
2129  function (parentObj) {
2130    parentObj.registerA = parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
2131    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
2132  },
2133  //CCF
2134  //#0x3F:
2135  function (parentObj) {
2136    parentObj.FCarry = !parentObj.FCarry;
2137    parentObj.FSubtract = parentObj.FHalfCarry = false;
2138  },
2139  //LD B, B
2140  //#0x40:
2141  function (parentObj) {
2142    //Do nothing...
2143  },
2144  //LD B, C
2145  //#0x41:
2146  function (parentObj) {
2147    parentObj.registerB = parentObj.registerC;
2148  },
2149  //LD B, D
2150  //#0x42:
2151  function (parentObj) {
2152    parentObj.registerB = parentObj.registerD;
2153  },
2154  //LD B, E
2155  //#0x43:
2156  function (parentObj) {
2157    parentObj.registerB = parentObj.registerE;
2158  },
2159  //LD B, H
2160  //#0x44:
2161  function (parentObj) {
2162    parentObj.registerB = parentObj.registersHL >> 8;
2163  },
2164  //LD B, L
2165  //#0x45:
2166  function (parentObj) {
2167    parentObj.registerB = parentObj.registersHL & 0xFF;
2168  },
2169  //LD B, (HL)
2170  //#0x46:
2171  function (parentObj) {
2172    parentObj.registerB = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
2173  },
2174  //LD B, A
2175  //#0x47:
2176  function (parentObj) {
2177    parentObj.registerB = parentObj.registerA;
2178  },
2179  //LD C, B
2180  //#0x48:
2181  function (parentObj) {
2182    parentObj.registerC = parentObj.registerB;
2183  },
2184  //LD C, C
2185  //#0x49:
2186  function (parentObj) {
2187    //Do nothing...
2188  },
2189  //LD C, D
2190  //#0x4A:
2191  function (parentObj) {
2192    parentObj.registerC = parentObj.registerD;
2193  },
2194  //LD C, E
2195  //#0x4B:
2196  function (parentObj) {
2197    parentObj.registerC = parentObj.registerE;
2198  },
2199  //LD C, H
2200  //#0x4C:
2201  function (parentObj) {
2202    parentObj.registerC = parentObj.registersHL >> 8;
2203  },
2204  //LD C, L
2205  //#0x4D:
2206  function (parentObj) {
2207    parentObj.registerC = parentObj.registersHL & 0xFF;
2208  },
2209  //LD C, (HL)
2210  //#0x4E:
2211  function (parentObj) {
2212    parentObj.registerC = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
2213  },
2214  //LD C, A
2215  //#0x4F:
2216  function (parentObj) {
2217    parentObj.registerC = parentObj.registerA;
2218  },
2219  //LD D, B
2220  //#0x50:
2221  function (parentObj) {
2222    parentObj.registerD = parentObj.registerB;
2223  },
2224  //LD D, C
2225  //#0x51:
2226  function (parentObj) {
2227    parentObj.registerD = parentObj.registerC;
2228  },
2229  //LD D, D
2230  //#0x52:
2231  function (parentObj) {
2232    //Do nothing...
2233  },
2234  //LD D, E
2235  //#0x53:
2236  function (parentObj) {
2237    parentObj.registerD = parentObj.registerE;
2238  },
2239  //LD D, H
2240  //#0x54:
2241  function (parentObj) {
2242    parentObj.registerD = parentObj.registersHL >> 8;
2243  },
2244  //LD D, L
2245  //#0x55:
2246  function (parentObj) {
2247    parentObj.registerD = parentObj.registersHL & 0xFF;
2248  },
2249  //LD D, (HL)
2250  //#0x56:
2251  function (parentObj) {
2252    parentObj.registerD = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
2253  },
2254  //LD D, A
2255  //#0x57:
2256  function (parentObj) {
2257    parentObj.registerD = parentObj.registerA;
2258  },
2259  //LD E, B
2260  //#0x58:
2261  function (parentObj) {
2262    parentObj.registerE = parentObj.registerB;
2263  },
2264  //LD E, C
2265  //#0x59:
2266  function (parentObj) {
2267    parentObj.registerE = parentObj.registerC;
2268  },
2269  //LD E, D
2270  //#0x5A:
2271  function (parentObj) {
2272    parentObj.registerE = parentObj.registerD;
2273  },
2274  //LD E, E
2275  //#0x5B:
2276  function (parentObj) {
2277    //Do nothing...
2278  },
2279  //LD E, H
2280  //#0x5C:
2281  function (parentObj) {
2282    parentObj.registerE = parentObj.registersHL >> 8;
2283  },
2284  //LD E, L
2285  //#0x5D:
2286  function (parentObj) {
2287    parentObj.registerE = parentObj.registersHL & 0xFF;
2288  },
2289  //LD E, (HL)
2290  //#0x5E:
2291  function (parentObj) {
2292    parentObj.registerE = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
2293  },
2294  //LD E, A
2295  //#0x5F:
2296  function (parentObj) {
2297    parentObj.registerE = parentObj.registerA;
2298  },
2299  //LD H, B
2300  //#0x60:
2301  function (parentObj) {
2302    parentObj.registersHL = (parentObj.registerB << 8) | (parentObj.registersHL & 0xFF);
2303  },
2304  //LD H, C
2305  //#0x61:
2306  function (parentObj) {
2307    parentObj.registersHL = (parentObj.registerC << 8) | (parentObj.registersHL & 0xFF);
2308  },
2309  //LD H, D
2310  //#0x62:
2311  function (parentObj) {
2312    parentObj.registersHL = (parentObj.registerD << 8) | (parentObj.registersHL & 0xFF);
2313  },
2314  //LD H, E
2315  //#0x63:
2316  function (parentObj) {
2317    parentObj.registersHL = (parentObj.registerE << 8) | (parentObj.registersHL & 0xFF);
2318  },
2319  //LD H, H
2320  //#0x64:
2321  function (parentObj) {
2322    //Do nothing...
2323  },
2324  //LD H, L
2325  //#0x65:
2326  function (parentObj) {
2327    parentObj.registersHL = (parentObj.registersHL & 0xFF) * 0x101;
2328  },
2329  //LD H, (HL)
2330  //#0x66:
2331  function (parentObj) {
2332    parentObj.registersHL = (parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) << 8) | (parentObj.registersHL & 0xFF);
2333  },
2334  //LD H, A
2335  //#0x67:
2336  function (parentObj) {
2337    parentObj.registersHL = (parentObj.registerA << 8) | (parentObj.registersHL & 0xFF);
2338  },
2339  //LD L, B
2340  //#0x68:
2341  function (parentObj) {
2342    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | parentObj.registerB;
2343  },
2344  //LD L, C
2345  //#0x69:
2346  function (parentObj) {
2347    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | parentObj.registerC;
2348  },
2349  //LD L, D
2350  //#0x6A:
2351  function (parentObj) {
2352    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | parentObj.registerD;
2353  },
2354  //LD L, E
2355  //#0x6B:
2356  function (parentObj) {
2357    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | parentObj.registerE;
2358  },
2359  //LD L, H
2360  //#0x6C:
2361  function (parentObj) {
2362    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | (parentObj.registersHL >> 8);
2363  },
2364  //LD L, L
2365  //#0x6D:
2366  function (parentObj) {
2367    //Do nothing...
2368  },
2369  //LD L, (HL)
2370  //#0x6E:
2371  function (parentObj) {
2372    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
2373  },
2374  //LD L, A
2375  //#0x6F:
2376  function (parentObj) {
2377    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | parentObj.registerA;
2378  },
2379  //LD (HL), B
2380  //#0x70:
2381  function (parentObj) {
2382    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.registerB);
2383  },
2384  //LD (HL), C
2385  //#0x71:
2386  function (parentObj) {
2387    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.registerC);
2388  },
2389  //LD (HL), D
2390  //#0x72:
2391  function (parentObj) {
2392    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.registerD);
2393  },
2394  //LD (HL), E
2395  //#0x73:
2396  function (parentObj) {
2397    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.registerE);
2398  },
2399  //LD (HL), H
2400  //#0x74:
2401  function (parentObj) {
2402    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.registersHL >> 8);
2403  },
2404  //LD (HL), L
2405  //#0x75:
2406  function (parentObj) {
2407    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.registersHL & 0xFF);
2408  },
2409  //HALT
2410  //#0x76:
2411  function (parentObj) {
2412    //See if there's already an IRQ match:
2413    if ((parentObj.interruptsEnabled & parentObj.interruptsRequested & 0x1F) > 0) {
2414      if (!parentObj.cGBC && !parentObj.usedBootROM) {
2415        //HALT bug in the DMG CPU model (Program Counter fails to increment for one instruction after HALT):
2416        parentObj.skipPCIncrement = true;
2417      }
2418      else {
2419        //CGB gets around the HALT PC bug by doubling the hidden NOP.
2420        parentObj.CPUTicks += 4;
2421      }
2422    }
2423    else {
2424      //CPU is stalled until the next IRQ match:
2425      parentObj.calculateHALTPeriod();
2426    }
2427  },
2428  //LD (HL), A
2429  //#0x77:
2430  function (parentObj) {
2431    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.registerA);
2432  },
2433  //LD A, B
2434  //#0x78:
2435  function (parentObj) {
2436    parentObj.registerA = parentObj.registerB;
2437  },
2438  //LD A, C
2439  //#0x79:
2440  function (parentObj) {
2441    parentObj.registerA = parentObj.registerC;
2442  },
2443  //LD A, D
2444  //#0x7A:
2445  function (parentObj) {
2446    parentObj.registerA = parentObj.registerD;
2447  },
2448  //LD A, E
2449  //#0x7B:
2450  function (parentObj) {
2451    parentObj.registerA = parentObj.registerE;
2452  },
2453  //LD A, H
2454  //#0x7C:
2455  function (parentObj) {
2456    parentObj.registerA = parentObj.registersHL >> 8;
2457  },
2458  //LD A, L
2459  //#0x7D:
2460  function (parentObj) {
2461    parentObj.registerA = parentObj.registersHL & 0xFF;
2462  },
2463  //LD, A, (HL)
2464  //#0x7E:
2465  function (parentObj) {
2466    parentObj.registerA = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
2467  },
2468  //LD A, A
2469  //#0x7F:
2470  function (parentObj) {
2471    //Do Nothing...
2472  },
2473  //ADD A, B
2474  //#0x80:
2475  function (parentObj) {
2476    var dirtySum = parentObj.registerA + parentObj.registerB;
2477    parentObj.FHalfCarry = ((dirtySum & 0xF) < (parentObj.registerA & 0xF));
2478    parentObj.FCarry = (dirtySum > 0xFF);
2479    parentObj.registerA = dirtySum & 0xFF;
2480    parentObj.FZero = (parentObj.registerA == 0);
2481    parentObj.FSubtract = false;
2482  },
2483  //ADD A, C
2484  //#0x81:
2485  function (parentObj) {
2486    var dirtySum = parentObj.registerA + parentObj.registerC;
2487    parentObj.FHalfCarry = ((dirtySum & 0xF) < (parentObj.registerA & 0xF));
2488    parentObj.FCarry = (dirtySum > 0xFF);
2489    parentObj.registerA = dirtySum & 0xFF;
2490    parentObj.FZero = (parentObj.registerA == 0);
2491    parentObj.FSubtract = false;
2492  },
2493  //ADD A, D
2494  //#0x82:
2495  function (parentObj) {
2496    var dirtySum = parentObj.registerA + parentObj.registerD;
2497    parentObj.FHalfCarry = ((dirtySum & 0xF) < (parentObj.registerA & 0xF));
2498    parentObj.FCarry = (dirtySum > 0xFF);
2499    parentObj.registerA = dirtySum & 0xFF;
2500    parentObj.FZero = (parentObj.registerA == 0);
2501    parentObj.FSubtract = false;
2502  },
2503  //ADD A, E
2504  //#0x83:
2505  function (parentObj) {
2506    var dirtySum = parentObj.registerA + parentObj.registerE;
2507    parentObj.FHalfCarry = ((dirtySum & 0xF) < (parentObj.registerA & 0xF));
2508    parentObj.FCarry = (dirtySum > 0xFF);
2509    parentObj.registerA = dirtySum & 0xFF;
2510    parentObj.FZero = (parentObj.registerA == 0);
2511    parentObj.FSubtract = false;
2512  },
2513  //ADD A, H
2514  //#0x84:
2515  function (parentObj) {
2516    var dirtySum = parentObj.registerA + (parentObj.registersHL >> 8);
2517    parentObj.FHalfCarry = ((dirtySum & 0xF) < (parentObj.registerA & 0xF));
2518    parentObj.FCarry = (dirtySum > 0xFF);
2519    parentObj.registerA = dirtySum & 0xFF;
2520    parentObj.FZero = (parentObj.registerA == 0);
2521    parentObj.FSubtract = false;
2522  },
2523  //ADD A, L
2524  //#0x85:
2525  function (parentObj) {
2526    var dirtySum = parentObj.registerA + (parentObj.registersHL & 0xFF);
2527    parentObj.FHalfCarry = ((dirtySum & 0xF) < (parentObj.registerA & 0xF));
2528    parentObj.FCarry = (dirtySum > 0xFF);
2529    parentObj.registerA = dirtySum & 0xFF;
2530    parentObj.FZero = (parentObj.registerA == 0);
2531    parentObj.FSubtract = false;
2532  },
2533  //ADD A, (HL)
2534  //#0x86:
2535  function (parentObj) {
2536    var dirtySum = parentObj.registerA + parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
2537    parentObj.FHalfCarry = ((dirtySum & 0xF) < (parentObj.registerA & 0xF));
2538    parentObj.FCarry = (dirtySum > 0xFF);
2539    parentObj.registerA = dirtySum & 0xFF;
2540    parentObj.FZero = (parentObj.registerA == 0);
2541    parentObj.FSubtract = false;
2542  },
2543  //ADD A, A
2544  //#0x87:
2545  function (parentObj) {
2546    parentObj.FHalfCarry = ((parentObj.registerA & 0x8) == 0x8);
2547    parentObj.FCarry = (parentObj.registerA > 0x7F);
2548    parentObj.registerA = (parentObj.registerA << 1) & 0xFF;
2549    parentObj.FZero = (parentObj.registerA == 0);
2550    parentObj.FSubtract = false;
2551  },
2552  //ADC A, B
2553  //#0x88:
2554  function (parentObj) {
2555    var dirtySum = parentObj.registerA + parentObj.registerB + ((parentObj.FCarry) ? 1 : 0);
2556    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) + (parentObj.registerB & 0xF) + ((parentObj.FCarry) ? 1 : 0) > 0xF);
2557    parentObj.FCarry = (dirtySum > 0xFF);
2558    parentObj.registerA = dirtySum & 0xFF;
2559    parentObj.FZero = (parentObj.registerA == 0);
2560    parentObj.FSubtract = false;
2561  },
2562  //ADC A, C
2563  //#0x89:
2564  function (parentObj) {
2565    var dirtySum = parentObj.registerA + parentObj.registerC + ((parentObj.FCarry) ? 1 : 0);
2566    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) + (parentObj.registerC & 0xF) + ((parentObj.FCarry) ? 1 : 0) > 0xF);
2567    parentObj.FCarry = (dirtySum > 0xFF);
2568    parentObj.registerA = dirtySum & 0xFF;
2569    parentObj.FZero = (parentObj.registerA == 0);
2570    parentObj.FSubtract = false;
2571  },
2572  //ADC A, D
2573  //#0x8A:
2574  function (parentObj) {
2575    var dirtySum = parentObj.registerA + parentObj.registerD + ((parentObj.FCarry) ? 1 : 0);
2576    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) + (parentObj.registerD & 0xF) + ((parentObj.FCarry) ? 1 : 0) > 0xF);
2577    parentObj.FCarry = (dirtySum > 0xFF);
2578    parentObj.registerA = dirtySum & 0xFF;
2579    parentObj.FZero = (parentObj.registerA == 0);
2580    parentObj.FSubtract = false;
2581  },
2582  //ADC A, E
2583  //#0x8B:
2584  function (parentObj) {
2585    var dirtySum = parentObj.registerA + parentObj.registerE + ((parentObj.FCarry) ? 1 : 0);
2586    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) + (parentObj.registerE & 0xF) + ((parentObj.FCarry) ? 1 : 0) > 0xF);
2587    parentObj.FCarry = (dirtySum > 0xFF);
2588    parentObj.registerA = dirtySum & 0xFF;
2589    parentObj.FZero = (parentObj.registerA == 0);
2590    parentObj.FSubtract = false;
2591  },
2592  //ADC A, H
2593  //#0x8C:
2594  function (parentObj) {
2595    var tempValue = (parentObj.registersHL >> 8);
2596    var dirtySum = parentObj.registerA + tempValue + ((parentObj.FCarry) ? 1 : 0);
2597    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) + (tempValue & 0xF) + ((parentObj.FCarry) ? 1 : 0) > 0xF);
2598    parentObj.FCarry = (dirtySum > 0xFF);
2599    parentObj.registerA = dirtySum & 0xFF;
2600    parentObj.FZero = (parentObj.registerA == 0);
2601    parentObj.FSubtract = false;
2602  },
2603  //ADC A, L
2604  //#0x8D:
2605  function (parentObj) {
2606    var tempValue = (parentObj.registersHL & 0xFF);
2607    var dirtySum = parentObj.registerA + tempValue + ((parentObj.FCarry) ? 1 : 0);
2608    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) + (tempValue & 0xF) + ((parentObj.FCarry) ? 1 : 0) > 0xF);
2609    parentObj.FCarry = (dirtySum > 0xFF);
2610    parentObj.registerA = dirtySum & 0xFF;
2611    parentObj.FZero = (parentObj.registerA == 0);
2612    parentObj.FSubtract = false;
2613  },
2614  //ADC A, (HL)
2615  //#0x8E:
2616  function (parentObj) {
2617    var tempValue = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
2618    var dirtySum = parentObj.registerA + tempValue + ((parentObj.FCarry) ? 1 : 0);
2619    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) + (tempValue & 0xF) + ((parentObj.FCarry) ? 1 : 0) > 0xF);
2620    parentObj.FCarry = (dirtySum > 0xFF);
2621    parentObj.registerA = dirtySum & 0xFF;
2622    parentObj.FZero = (parentObj.registerA == 0);
2623    parentObj.FSubtract = false;
2624  },
2625  //ADC A, A
2626  //#0x8F:
2627  function (parentObj) {
2628    //shift left register A one bit for some ops here as an optimization:
2629    var dirtySum = (parentObj.registerA << 1) | ((parentObj.FCarry) ? 1 : 0);
2630    parentObj.FHalfCarry = ((((parentObj.registerA << 1) & 0x1E) | ((parentObj.FCarry) ? 1 : 0)) > 0xF);
2631    parentObj.FCarry = (dirtySum > 0xFF);
2632    parentObj.registerA = dirtySum & 0xFF;
2633    parentObj.FZero = (parentObj.registerA == 0);
2634    parentObj.FSubtract = false;
2635  },
2636  //SUB A, B
2637  //#0x90:
2638  function (parentObj) {
2639    var dirtySum = parentObj.registerA - parentObj.registerB;
2640    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) < (dirtySum & 0xF));
2641    parentObj.FCarry = (dirtySum < 0);
2642    parentObj.registerA = dirtySum & 0xFF;
2643    parentObj.FZero = (dirtySum == 0);
2644    parentObj.FSubtract = true;
2645  },
2646  //SUB A, C
2647  //#0x91:
2648  function (parentObj) {
2649    var dirtySum = parentObj.registerA - parentObj.registerC;
2650    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) < (dirtySum & 0xF));
2651    parentObj.FCarry = (dirtySum < 0);
2652    parentObj.registerA = dirtySum & 0xFF;
2653    parentObj.FZero = (dirtySum == 0);
2654    parentObj.FSubtract = true;
2655  },
2656  //SUB A, D
2657  //#0x92:
2658  function (parentObj) {
2659    var dirtySum = parentObj.registerA - parentObj.registerD;
2660    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) < (dirtySum & 0xF));
2661    parentObj.FCarry = (dirtySum < 0);
2662    parentObj.registerA = dirtySum & 0xFF;
2663    parentObj.FZero = (dirtySum == 0);
2664    parentObj.FSubtract = true;
2665  },
2666  //SUB A, E
2667  //#0x93:
2668  function (parentObj) {
2669    var dirtySum = parentObj.registerA - parentObj.registerE;
2670    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) < (dirtySum & 0xF));
2671    parentObj.FCarry = (dirtySum < 0);
2672    parentObj.registerA = dirtySum & 0xFF;
2673    parentObj.FZero = (dirtySum == 0);
2674    parentObj.FSubtract = true;
2675  },
2676  //SUB A, H
2677  //#0x94:
2678  function (parentObj) {
2679    var dirtySum = parentObj.registerA - (parentObj.registersHL >> 8);
2680    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) < (dirtySum & 0xF));
2681    parentObj.FCarry = (dirtySum < 0);
2682    parentObj.registerA = dirtySum & 0xFF;
2683    parentObj.FZero = (dirtySum == 0);
2684    parentObj.FSubtract = true;
2685  },
2686  //SUB A, L
2687  //#0x95:
2688  function (parentObj) {
2689    var dirtySum = parentObj.registerA - (parentObj.registersHL & 0xFF);
2690    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) < (dirtySum & 0xF));
2691    parentObj.FCarry = (dirtySum < 0);
2692    parentObj.registerA = dirtySum & 0xFF;
2693    parentObj.FZero = (dirtySum == 0);
2694    parentObj.FSubtract = true;
2695  },
2696  //SUB A, (HL)
2697  //#0x96:
2698  function (parentObj) {
2699    var dirtySum = parentObj.registerA - parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
2700    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) < (dirtySum & 0xF));
2701    parentObj.FCarry = (dirtySum < 0);
2702    parentObj.registerA = dirtySum & 0xFF;
2703    parentObj.FZero = (dirtySum == 0);
2704    parentObj.FSubtract = true;
2705  },
2706  //SUB A, A
2707  //#0x97:
2708  function (parentObj) {
2709    //number - same number == 0
2710    parentObj.registerA = 0;
2711    parentObj.FHalfCarry = parentObj.FCarry = false;
2712    parentObj.FZero = parentObj.FSubtract = true;
2713  },
2714  //SBC A, B
2715  //#0x98:
2716  function (parentObj) {
2717    var dirtySum = parentObj.registerA - parentObj.registerB - ((parentObj.FCarry) ? 1 : 0);
2718    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) - (parentObj.registerB & 0xF) - ((parentObj.FCarry) ? 1 : 0) < 0);
2719    parentObj.FCarry = (dirtySum < 0);
2720    parentObj.registerA = dirtySum & 0xFF;
2721    parentObj.FZero = (parentObj.registerA == 0);
2722    parentObj.FSubtract = true;
2723  },
2724  //SBC A, C
2725  //#0x99:
2726  function (parentObj) {
2727    var dirtySum = parentObj.registerA - parentObj.registerC - ((parentObj.FCarry) ? 1 : 0);
2728    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) - (parentObj.registerC & 0xF) - ((parentObj.FCarry) ? 1 : 0) < 0);
2729    parentObj.FCarry = (dirtySum < 0);
2730    parentObj.registerA = dirtySum & 0xFF;
2731    parentObj.FZero = (parentObj.registerA == 0);
2732    parentObj.FSubtract = true;
2733  },
2734  //SBC A, D
2735  //#0x9A:
2736  function (parentObj) {
2737    var dirtySum = parentObj.registerA - parentObj.registerD - ((parentObj.FCarry) ? 1 : 0);
2738    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) - (parentObj.registerD & 0xF) - ((parentObj.FCarry) ? 1 : 0) < 0);
2739    parentObj.FCarry = (dirtySum < 0);
2740    parentObj.registerA = dirtySum & 0xFF;
2741    parentObj.FZero = (parentObj.registerA == 0);
2742    parentObj.FSubtract = true;
2743  },
2744  //SBC A, E
2745  //#0x9B:
2746  function (parentObj) {
2747    var dirtySum = parentObj.registerA - parentObj.registerE - ((parentObj.FCarry) ? 1 : 0);
2748    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) - (parentObj.registerE & 0xF) - ((parentObj.FCarry) ? 1 : 0) < 0);
2749    parentObj.FCarry = (dirtySum < 0);
2750    parentObj.registerA = dirtySum & 0xFF;
2751    parentObj.FZero = (parentObj.registerA == 0);
2752    parentObj.FSubtract = true;
2753  },
2754  //SBC A, H
2755  //#0x9C:
2756  function (parentObj) {
2757    var temp_var = parentObj.registersHL >> 8;
2758    var dirtySum = parentObj.registerA - temp_var - ((parentObj.FCarry) ? 1 : 0);
2759    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) - (temp_var & 0xF) - ((parentObj.FCarry) ? 1 : 0) < 0);
2760    parentObj.FCarry = (dirtySum < 0);
2761    parentObj.registerA = dirtySum & 0xFF;
2762    parentObj.FZero = (parentObj.registerA == 0);
2763    parentObj.FSubtract = true;
2764  },
2765  //SBC A, L
2766  //#0x9D:
2767  function (parentObj) {
2768    var dirtySum = parentObj.registerA - (parentObj.registersHL & 0xFF) - ((parentObj.FCarry) ? 1 : 0);
2769    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) - (parentObj.registersHL & 0xF) - ((parentObj.FCarry) ? 1 : 0) < 0);
2770    parentObj.FCarry = (dirtySum < 0);
2771    parentObj.registerA = dirtySum & 0xFF;
2772    parentObj.FZero = (parentObj.registerA == 0);
2773    parentObj.FSubtract = true;
2774  },
2775  //SBC A, (HL)
2776  //#0x9E:
2777  function (parentObj) {
2778    var temp_var = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
2779    var dirtySum = parentObj.registerA - temp_var - ((parentObj.FCarry) ? 1 : 0);
2780    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) - (temp_var & 0xF) - ((parentObj.FCarry) ? 1 : 0) < 0);
2781    parentObj.FCarry = (dirtySum < 0);
2782    parentObj.registerA = dirtySum & 0xFF;
2783    parentObj.FZero = (parentObj.registerA == 0);
2784    parentObj.FSubtract = true;
2785  },
2786  //SBC A, A
2787  //#0x9F:
2788  function (parentObj) {
2789    //Optimized SBC A:
2790    if (parentObj.FCarry) {
2791      parentObj.FZero = false;
2792      parentObj.FSubtract = parentObj.FHalfCarry = parentObj.FCarry = true;
2793      parentObj.registerA = 0xFF;
2794    }
2795    else {
2796      parentObj.FHalfCarry = parentObj.FCarry = false;
2797      parentObj.FSubtract = parentObj.FZero = true;
2798      parentObj.registerA = 0;
2799    }
2800  },
2801  //AND B
2802  //#0xA0:
2803  function (parentObj) {
2804    parentObj.registerA &= parentObj.registerB;
2805    parentObj.FZero = (parentObj.registerA == 0);
2806    parentObj.FHalfCarry = true;
2807    parentObj.FSubtract = parentObj.FCarry = false;
2808  },
2809  //AND C
2810  //#0xA1:
2811  function (parentObj) {
2812    parentObj.registerA &= parentObj.registerC;
2813    parentObj.FZero = (parentObj.registerA == 0);
2814    parentObj.FHalfCarry = true;
2815    parentObj.FSubtract = parentObj.FCarry = false;
2816  },
2817  //AND D
2818  //#0xA2:
2819  function (parentObj) {
2820    parentObj.registerA &= parentObj.registerD;
2821    parentObj.FZero = (parentObj.registerA == 0);
2822    parentObj.FHalfCarry = true;
2823    parentObj.FSubtract = parentObj.FCarry = false;
2824  },
2825  //AND E
2826  //#0xA3:
2827  function (parentObj) {
2828    parentObj.registerA &= parentObj.registerE;
2829    parentObj.FZero = (parentObj.registerA == 0);
2830    parentObj.FHalfCarry = true;
2831    parentObj.FSubtract = parentObj.FCarry = false;
2832  },
2833  //AND H
2834  //#0xA4:
2835  function (parentObj) {
2836    parentObj.registerA &= (parentObj.registersHL >> 8);
2837    parentObj.FZero = (parentObj.registerA == 0);
2838    parentObj.FHalfCarry = true;
2839    parentObj.FSubtract = parentObj.FCarry = false;
2840  },
2841  //AND L
2842  //#0xA5:
2843  function (parentObj) {
2844    parentObj.registerA &= parentObj.registersHL;
2845    parentObj.FZero = (parentObj.registerA == 0);
2846    parentObj.FHalfCarry = true;
2847    parentObj.FSubtract = parentObj.FCarry = false;
2848  },
2849  //AND (HL)
2850  //#0xA6:
2851  function (parentObj) {
2852    parentObj.registerA &= parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
2853    parentObj.FZero = (parentObj.registerA == 0);
2854    parentObj.FHalfCarry = true;
2855    parentObj.FSubtract = parentObj.FCarry = false;
2856  },
2857  //AND A
2858  //#0xA7:
2859  function (parentObj) {
2860    //number & same number = same number
2861    parentObj.FZero = (parentObj.registerA == 0);
2862    parentObj.FHalfCarry = true;
2863    parentObj.FSubtract = parentObj.FCarry = false;
2864  },
2865  //XOR B
2866  //#0xA8:
2867  function (parentObj) {
2868    parentObj.registerA ^= parentObj.registerB;
2869    parentObj.FZero = (parentObj.registerA == 0);
2870    parentObj.FSubtract = parentObj.FHalfCarry = parentObj.FCarry = false;
2871  },
2872  //XOR C
2873  //#0xA9:
2874  function (parentObj) {
2875    parentObj.registerA ^= parentObj.registerC;
2876    parentObj.FZero = (parentObj.registerA == 0);
2877    parentObj.FSubtract = parentObj.FHalfCarry = parentObj.FCarry = false;
2878  },
2879  //XOR D
2880  //#0xAA:
2881  function (parentObj) {
2882    parentObj.registerA ^= parentObj.registerD;
2883    parentObj.FZero = (parentObj.registerA == 0);
2884    parentObj.FSubtract = parentObj.FHalfCarry = parentObj.FCarry = false;
2885  },
2886  //XOR E
2887  //#0xAB:
2888  function (parentObj) {
2889    parentObj.registerA ^= parentObj.registerE;
2890    parentObj.FZero = (parentObj.registerA == 0);
2891    parentObj.FSubtract = parentObj.FHalfCarry = parentObj.FCarry = false;
2892  },
2893  //XOR H
2894  //#0xAC:
2895  function (parentObj) {
2896    parentObj.registerA ^= (parentObj.registersHL >> 8);
2897    parentObj.FZero = (parentObj.registerA == 0);
2898    parentObj.FSubtract = parentObj.FHalfCarry = parentObj.FCarry = false;
2899  },
2900  //XOR L
2901  //#0xAD:
2902  function (parentObj) {
2903    parentObj.registerA ^= (parentObj.registersHL & 0xFF);
2904    parentObj.FZero = (parentObj.registerA == 0);
2905    parentObj.FSubtract = parentObj.FHalfCarry = parentObj.FCarry = false;
2906  },
2907  //XOR (HL)
2908  //#0xAE:
2909  function (parentObj) {
2910    parentObj.registerA ^= parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
2911    parentObj.FZero = (parentObj.registerA == 0);
2912    parentObj.FSubtract = parentObj.FHalfCarry = parentObj.FCarry = false;
2913  },
2914  //XOR A
2915  //#0xAF:
2916  function (parentObj) {
2917    //number ^ same number == 0
2918    parentObj.registerA = 0;
2919    parentObj.FZero = true;
2920    parentObj.FSubtract = parentObj.FHalfCarry = parentObj.FCarry = false;
2921  },
2922  //OR B
2923  //#0xB0:
2924  function (parentObj) {
2925    parentObj.registerA |= parentObj.registerB;
2926    parentObj.FZero = (parentObj.registerA == 0);
2927    parentObj.FSubtract = parentObj.FCarry = parentObj.FHalfCarry = false;
2928  },
2929  //OR C
2930  //#0xB1:
2931  function (parentObj) {
2932    parentObj.registerA |= parentObj.registerC;
2933    parentObj.FZero = (parentObj.registerA == 0);
2934    parentObj.FSubtract = parentObj.FCarry = parentObj.FHalfCarry = false;
2935  },
2936  //OR D
2937  //#0xB2:
2938  function (parentObj) {
2939    parentObj.registerA |= parentObj.registerD;
2940    parentObj.FZero = (parentObj.registerA == 0);
2941    parentObj.FSubtract = parentObj.FCarry = parentObj.FHalfCarry = false;
2942  },
2943  //OR E
2944  //#0xB3:
2945  function (parentObj) {
2946    parentObj.registerA |= parentObj.registerE;
2947    parentObj.FZero = (parentObj.registerA == 0);
2948    parentObj.FSubtract = parentObj.FCarry = parentObj.FHalfCarry = false;
2949  },
2950  //OR H
2951  //#0xB4:
2952  function (parentObj) {
2953    parentObj.registerA |= (parentObj.registersHL >> 8);
2954    parentObj.FZero = (parentObj.registerA == 0);
2955    parentObj.FSubtract = parentObj.FCarry = parentObj.FHalfCarry = false;
2956  },
2957  //OR L
2958  //#0xB5:
2959  function (parentObj) {
2960    parentObj.registerA |= (parentObj.registersHL & 0xFF);
2961    parentObj.FZero = (parentObj.registerA == 0);
2962    parentObj.FSubtract = parentObj.FCarry = parentObj.FHalfCarry = false;
2963  },
2964  //OR (HL)
2965  //#0xB6:
2966  function (parentObj) {
2967    parentObj.registerA |= parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
2968    parentObj.FZero = (parentObj.registerA == 0);
2969    parentObj.FSubtract = parentObj.FCarry = parentObj.FHalfCarry = false;
2970  },
2971  //OR A
2972  //#0xB7:
2973  function (parentObj) {
2974    //number | same number == same number
2975    parentObj.FZero = (parentObj.registerA == 0);
2976    parentObj.FSubtract = parentObj.FCarry = parentObj.FHalfCarry = false;
2977  },
2978  //CP B
2979  //#0xB8:
2980  function (parentObj) {
2981    var dirtySum = parentObj.registerA - parentObj.registerB;
2982    parentObj.FHalfCarry = ((dirtySum & 0xF) > (parentObj.registerA & 0xF));
2983    parentObj.FCarry = (dirtySum < 0);
2984    parentObj.FZero = (dirtySum == 0);
2985    parentObj.FSubtract = true;
2986  },
2987  //CP C
2988  //#0xB9:
2989  function (parentObj) {
2990    var dirtySum = parentObj.registerA - parentObj.registerC;
2991    parentObj.FHalfCarry = ((dirtySum & 0xF) > (parentObj.registerA & 0xF));
2992    parentObj.FCarry = (dirtySum < 0);
2993    parentObj.FZero = (dirtySum == 0);
2994    parentObj.FSubtract = true;
2995  },
2996  //CP D
2997  //#0xBA:
2998  function (parentObj) {
2999    var dirtySum = parentObj.registerA - parentObj.registerD;
3000    parentObj.FHalfCarry = ((dirtySum & 0xF) > (parentObj.registerA & 0xF));
3001    parentObj.FCarry = (dirtySum < 0);
3002    parentObj.FZero = (dirtySum == 0);
3003    parentObj.FSubtract = true;
3004  },
3005  //CP E
3006  //#0xBB:
3007  function (parentObj) {
3008    var dirtySum = parentObj.registerA - parentObj.registerE;
3009    parentObj.FHalfCarry = ((dirtySum & 0xF) > (parentObj.registerA & 0xF));
3010    parentObj.FCarry = (dirtySum < 0);
3011    parentObj.FZero = (dirtySum == 0);
3012    parentObj.FSubtract = true;
3013  },
3014  //CP H
3015  //#0xBC:
3016  function (parentObj) {
3017    var dirtySum = parentObj.registerA - (parentObj.registersHL >> 8);
3018    parentObj.FHalfCarry = ((dirtySum & 0xF) > (parentObj.registerA & 0xF));
3019    parentObj.FCarry = (dirtySum < 0);
3020    parentObj.FZero = (dirtySum == 0);
3021    parentObj.FSubtract = true;
3022  },
3023  //CP L
3024  //#0xBD:
3025  function (parentObj) {
3026    var dirtySum = parentObj.registerA - (parentObj.registersHL & 0xFF);
3027    parentObj.FHalfCarry = ((dirtySum & 0xF) > (parentObj.registerA & 0xF));
3028    parentObj.FCarry = (dirtySum < 0);
3029    parentObj.FZero = (dirtySum == 0);
3030    parentObj.FSubtract = true;
3031  },
3032  //CP (HL)
3033  //#0xBE:
3034  function (parentObj) {
3035    var dirtySum = parentObj.registerA - parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
3036    parentObj.FHalfCarry = ((dirtySum & 0xF) > (parentObj.registerA & 0xF));
3037    parentObj.FCarry = (dirtySum < 0);
3038    parentObj.FZero = (dirtySum == 0);
3039    parentObj.FSubtract = true;
3040  },
3041  //CP A
3042  //#0xBF:
3043  function (parentObj) {
3044    parentObj.FHalfCarry = parentObj.FCarry = false;
3045    parentObj.FZero = parentObj.FSubtract = true;
3046  },
3047  //RET !FZ
3048  //#0xC0:
3049  function (parentObj) {
3050    if (!parentObj.FZero) {
3051      parentObj.programCounter = (parentObj.memoryRead((parentObj.stackPointer + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.stackPointer](parentObj, parentObj.stackPointer);
3052      parentObj.stackPointer = (parentObj.stackPointer + 2) & 0xFFFF;
3053      parentObj.CPUTicks += 12;
3054    }
3055  },
3056  //POP BC
3057  //#0xC1:
3058  function (parentObj) {
3059    parentObj.registerC = parentObj.memoryReader[parentObj.stackPointer](parentObj, parentObj.stackPointer);
3060    parentObj.registerB = parentObj.memoryRead((parentObj.stackPointer + 1) & 0xFFFF);
3061    parentObj.stackPointer = (parentObj.stackPointer + 2) & 0xFFFF;
3062  },
3063  //JP !FZ, nn
3064  //#0xC2:
3065  function (parentObj) {
3066    if (!parentObj.FZero) {
3067      parentObj.programCounter = (parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3068      parentObj.CPUTicks += 4;
3069    }
3070    else {
3071      parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3072    }
3073  },
3074  //JP nn
3075  //#0xC3:
3076  function (parentObj) {
3077    parentObj.programCounter = (parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3078  },
3079  //CALL !FZ, nn
3080  //#0xC4:
3081  function (parentObj) {
3082    if (!parentObj.FZero) {
3083      var temp_pc = (parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3084      parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3085      parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3086      parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter >> 8);
3087      parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3088      parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter & 0xFF);
3089      parentObj.programCounter = temp_pc;
3090      parentObj.CPUTicks += 12;
3091    }
3092    else {
3093      parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3094    }
3095  },
3096  //PUSH BC
3097  //#0xC5:
3098  function (parentObj) {
3099    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3100    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.registerB);
3101    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3102    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.registerC);
3103  },
3104  //ADD, n
3105  //#0xC6:
3106  function (parentObj) {
3107    var dirtySum = parentObj.registerA + parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3108    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
3109    parentObj.FHalfCarry = ((dirtySum & 0xF) < (parentObj.registerA & 0xF));
3110    parentObj.FCarry = (dirtySum > 0xFF);
3111    parentObj.registerA = dirtySum & 0xFF;
3112    parentObj.FZero = (parentObj.registerA == 0);
3113    parentObj.FSubtract = false;
3114  },
3115  //RST 0
3116  //#0xC7:
3117  function (parentObj) {
3118    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3119    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter >> 8);
3120    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3121    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter & 0xFF);
3122    parentObj.programCounter = 0;
3123  },
3124  //RET FZ
3125  //#0xC8:
3126  function (parentObj) {
3127    if (parentObj.FZero) {
3128      parentObj.programCounter = (parentObj.memoryRead((parentObj.stackPointer + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.stackPointer](parentObj, parentObj.stackPointer);
3129      parentObj.stackPointer = (parentObj.stackPointer + 2) & 0xFFFF;
3130      parentObj.CPUTicks += 12;
3131    }
3132  },
3133  //RET
3134  //#0xC9:
3135  function (parentObj) {
3136    parentObj.programCounter =  (parentObj.memoryRead((parentObj.stackPointer + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.stackPointer](parentObj, parentObj.stackPointer);
3137    parentObj.stackPointer = (parentObj.stackPointer + 2) & 0xFFFF;
3138  },
3139  //JP FZ, nn
3140  //#0xCA:
3141  function (parentObj) {
3142    if (parentObj.FZero) {
3143      parentObj.programCounter = (parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3144      parentObj.CPUTicks += 4;
3145    }
3146    else {
3147      parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3148    }
3149  },
3150  //Secondary OP Code Set:
3151  //#0xCB:
3152  function (parentObj) {
3153    var opcode = parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3154    //Increment the program counter to the next instruction:
3155    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
3156    //Get how many CPU cycles the current 0xCBXX op code counts for:
3157    parentObj.CPUTicks += parentObj.SecondaryTICKTable[opcode];
3158    //Execute secondary OP codes for the 0xCB OP code call.
3159    parentObj.CBOPCODE[opcode](parentObj);
3160  },
3161  //CALL FZ, nn
3162  //#0xCC:
3163  function (parentObj) {
3164    if (parentObj.FZero) {
3165      var temp_pc = (parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3166      parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3167      parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3168      parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter >> 8);
3169      parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3170      parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter & 0xFF);
3171      parentObj.programCounter = temp_pc;
3172      parentObj.CPUTicks += 12;
3173    }
3174    else {
3175      parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3176    }
3177  },
3178  //CALL nn
3179  //#0xCD:
3180  function (parentObj) {
3181    var temp_pc = (parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3182    parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3183    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3184    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter >> 8);
3185    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3186    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter & 0xFF);
3187    parentObj.programCounter = temp_pc;
3188  },
3189  //ADC A, n
3190  //#0xCE:
3191  function (parentObj) {
3192    var tempValue = parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3193    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
3194    var dirtySum = parentObj.registerA + tempValue + ((parentObj.FCarry) ? 1 : 0);
3195    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) + (tempValue & 0xF) + ((parentObj.FCarry) ? 1 : 0) > 0xF);
3196    parentObj.FCarry = (dirtySum > 0xFF);
3197    parentObj.registerA = dirtySum & 0xFF;
3198    parentObj.FZero = (parentObj.registerA == 0);
3199    parentObj.FSubtract = false;
3200  },
3201  //RST 0x8
3202  //#0xCF:
3203  function (parentObj) {
3204    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3205    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter >> 8);
3206    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3207    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter & 0xFF);
3208    parentObj.programCounter = 0x8;
3209  },
3210  //RET !FC
3211  //#0xD0:
3212  function (parentObj) {
3213    if (!parentObj.FCarry) {
3214      parentObj.programCounter = (parentObj.memoryRead((parentObj.stackPointer + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.stackPointer](parentObj, parentObj.stackPointer);
3215      parentObj.stackPointer = (parentObj.stackPointer + 2) & 0xFFFF;
3216      parentObj.CPUTicks += 12;
3217    }
3218  },
3219  //POP DE
3220  //#0xD1:
3221  function (parentObj) {
3222    parentObj.registerE = parentObj.memoryReader[parentObj.stackPointer](parentObj, parentObj.stackPointer);
3223    parentObj.registerD = parentObj.memoryRead((parentObj.stackPointer + 1) & 0xFFFF);
3224    parentObj.stackPointer = (parentObj.stackPointer + 2) & 0xFFFF;
3225  },
3226  //JP !FC, nn
3227  //#0xD2:
3228  function (parentObj) {
3229    if (!parentObj.FCarry) {
3230      parentObj.programCounter = (parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3231      parentObj.CPUTicks += 4;
3232    }
3233    else {
3234      parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3235    }
3236  },
3237  //0xD3 - Illegal
3238  //#0xD3:
3239  function (parentObj) {
3240    cout("Illegal op code 0xD3 called, pausing emulation.", 2);
3241    pause();
3242  },
3243  //CALL !FC, nn
3244  //#0xD4:
3245  function (parentObj) {
3246    if (!parentObj.FCarry) {
3247      var temp_pc = (parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3248      parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3249      parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3250      parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter >> 8);
3251      parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3252      parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter & 0xFF);
3253      parentObj.programCounter = temp_pc;
3254      parentObj.CPUTicks += 12;
3255    }
3256    else {
3257      parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3258    }
3259  },
3260  //PUSH DE
3261  //#0xD5:
3262  function (parentObj) {
3263    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3264    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.registerD);
3265    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3266    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.registerE);
3267  },
3268  //SUB A, n
3269  //#0xD6:
3270  function (parentObj) {
3271    var dirtySum = parentObj.registerA - parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3272    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
3273    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) < (dirtySum & 0xF));
3274    parentObj.FCarry = (dirtySum < 0);
3275    parentObj.registerA = dirtySum & 0xFF;
3276    parentObj.FZero = (dirtySum == 0);
3277    parentObj.FSubtract = true;
3278  },
3279  //RST 0x10
3280  //#0xD7:
3281  function (parentObj) {
3282    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3283    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter >> 8);
3284    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3285    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter & 0xFF);
3286    parentObj.programCounter = 0x10;
3287  },
3288  //RET FC
3289  //#0xD8:
3290  function (parentObj) {
3291    if (parentObj.FCarry) {
3292      parentObj.programCounter = (parentObj.memoryRead((parentObj.stackPointer + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.stackPointer](parentObj, parentObj.stackPointer);
3293      parentObj.stackPointer = (parentObj.stackPointer + 2) & 0xFFFF;
3294      parentObj.CPUTicks += 12;
3295    }
3296  },
3297  //RETI
3298  //#0xD9:
3299  function (parentObj) {
3300    parentObj.programCounter = (parentObj.memoryRead((parentObj.stackPointer + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.stackPointer](parentObj, parentObj.stackPointer);
3301    parentObj.stackPointer = (parentObj.stackPointer + 2) & 0xFFFF;
3302    //Immediate for HALT:
3303    parentObj.IRQEnableDelay = (parentObj.IRQEnableDelay == 2 || parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter) == 0x76) ? 1 : 2;
3304  },
3305  //JP FC, nn
3306  //#0xDA:
3307  function (parentObj) {
3308    if (parentObj.FCarry) {
3309      parentObj.programCounter = (parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3310      parentObj.CPUTicks += 4;
3311    }
3312    else {
3313      parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3314    }
3315  },
3316  //0xDB - Illegal
3317  //#0xDB:
3318  function (parentObj) {
3319    cout("Illegal op code 0xDB called, pausing emulation.", 2);
3320    pause();
3321  },
3322  //CALL FC, nn
3323  //#0xDC:
3324  function (parentObj) {
3325    if (parentObj.FCarry) {
3326      var temp_pc = (parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3327      parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3328      parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3329      parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter >> 8);
3330      parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3331      parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter & 0xFF);
3332      parentObj.programCounter = temp_pc;
3333      parentObj.CPUTicks += 12;
3334    }
3335    else {
3336      parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3337    }
3338  },
3339  //0xDD - Illegal
3340  //#0xDD:
3341  function (parentObj) {
3342    cout("Illegal op code 0xDD called, pausing emulation.", 2);
3343    pause();
3344  },
3345  //SBC A, n
3346  //#0xDE:
3347  function (parentObj) {
3348    var temp_var = parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3349    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
3350    var dirtySum = parentObj.registerA - temp_var - ((parentObj.FCarry) ? 1 : 0);
3351    parentObj.FHalfCarry = ((parentObj.registerA & 0xF) - (temp_var & 0xF) - ((parentObj.FCarry) ? 1 : 0) < 0);
3352    parentObj.FCarry = (dirtySum < 0);
3353    parentObj.registerA = dirtySum & 0xFF;
3354    parentObj.FZero = (parentObj.registerA == 0);
3355    parentObj.FSubtract = true;
3356  },
3357  //RST 0x18
3358  //#0xDF:
3359  function (parentObj) {
3360    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3361    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter >> 8);
3362    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3363    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter & 0xFF);
3364    parentObj.programCounter = 0x18;
3365  },
3366  //LDH (n), A
3367  //#0xE0:
3368  function (parentObj) {
3369    parentObj.memoryHighWrite(parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter), parentObj.registerA);
3370    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
3371  },
3372  //POP HL
3373  //#0xE1:
3374  function (parentObj) {
3375    parentObj.registersHL = (parentObj.memoryRead((parentObj.stackPointer + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.stackPointer](parentObj, parentObj.stackPointer);
3376    parentObj.stackPointer = (parentObj.stackPointer + 2) & 0xFFFF;
3377  },
3378  //LD (0xFF00 + C), A
3379  //#0xE2:
3380  function (parentObj) {
3381    parentObj.memoryHighWriter[parentObj.registerC](parentObj, parentObj.registerC, parentObj.registerA);
3382  },
3383  //0xE3 - Illegal
3384  //#0xE3:
3385  function (parentObj) {
3386    cout("Illegal op code 0xE3 called, pausing emulation.", 2);
3387    pause();
3388  },
3389  //0xE4 - Illegal
3390  //#0xE4:
3391  function (parentObj) {
3392    cout("Illegal op code 0xE4 called, pausing emulation.", 2);
3393    pause();
3394  },
3395  //PUSH HL
3396  //#0xE5:
3397  function (parentObj) {
3398    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3399    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.registersHL >> 8);
3400    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3401    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.registersHL & 0xFF);
3402  },
3403  //AND n
3404  //#0xE6:
3405  function (parentObj) {
3406    parentObj.registerA &= parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3407    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
3408    parentObj.FZero = (parentObj.registerA == 0);
3409    parentObj.FHalfCarry = true;
3410    parentObj.FSubtract = parentObj.FCarry = false;
3411  },
3412  //RST 0x20
3413  //#0xE7:
3414  function (parentObj) {
3415    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3416    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter >> 8);
3417    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3418    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter & 0xFF);
3419    parentObj.programCounter = 0x20;
3420  },
3421  //ADD SP, n
3422  //#0xE8:
3423  function (parentObj) {
3424    var temp_value2 = (parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter) << 24) >> 24;
3425    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
3426    var temp_value = (parentObj.stackPointer + temp_value2) & 0xFFFF;
3427    temp_value2 = parentObj.stackPointer ^ temp_value2 ^ temp_value;
3428    parentObj.stackPointer = temp_value;
3429    parentObj.FCarry = ((temp_value2 & 0x100) == 0x100);
3430    parentObj.FHalfCarry = ((temp_value2 & 0x10) == 0x10);
3431    parentObj.FZero = parentObj.FSubtract = false;
3432  },
3433  //JP, (HL)
3434  //#0xE9:
3435  function (parentObj) {
3436    parentObj.programCounter = parentObj.registersHL;
3437  },
3438  //LD n, A
3439  //#0xEA:
3440  function (parentObj) {
3441    parentObj.memoryWrite((parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter), parentObj.registerA);
3442    parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3443  },
3444  //0xEB - Illegal
3445  //#0xEB:
3446  function (parentObj) {
3447    cout("Illegal op code 0xEB called, pausing emulation.", 2);
3448    pause();
3449  },
3450  //0xEC - Illegal
3451  //#0xEC:
3452  function (parentObj) {
3453    cout("Illegal op code 0xEC called, pausing emulation.", 2);
3454    pause();
3455  },
3456  //0xED - Illegal
3457  //#0xED:
3458  function (parentObj) {
3459    cout("Illegal op code 0xED called, pausing emulation.", 2);
3460    pause();
3461  },
3462  //XOR n
3463  //#0xEE:
3464  function (parentObj) {
3465    parentObj.registerA ^= parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3466    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
3467    parentObj.FZero = (parentObj.registerA == 0);
3468    parentObj.FSubtract = parentObj.FHalfCarry = parentObj.FCarry = false;
3469  },
3470  //RST 0x28
3471  //#0xEF:
3472  function (parentObj) {
3473    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3474    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter >> 8);
3475    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3476    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter & 0xFF);
3477    parentObj.programCounter = 0x28;
3478  },
3479  //LDH A, (n)
3480  //#0xF0:
3481  function (parentObj) {
3482    parentObj.registerA = parentObj.memoryHighRead(parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter));
3483    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
3484  },
3485  //POP AF
3486  //#0xF1:
3487  function (parentObj) {
3488    var temp_var = parentObj.memoryReader[parentObj.stackPointer](parentObj, parentObj.stackPointer);
3489    parentObj.FZero = (temp_var > 0x7F);
3490    parentObj.FSubtract = ((temp_var & 0x40) == 0x40);
3491    parentObj.FHalfCarry = ((temp_var & 0x20) == 0x20);
3492    parentObj.FCarry = ((temp_var & 0x10) == 0x10);
3493    parentObj.registerA = parentObj.memoryRead((parentObj.stackPointer + 1) & 0xFFFF);
3494    parentObj.stackPointer = (parentObj.stackPointer + 2) & 0xFFFF;
3495  },
3496  //LD A, (0xFF00 + C)
3497  //#0xF2:
3498  function (parentObj) {
3499    parentObj.registerA = parentObj.memoryHighReader[parentObj.registerC](parentObj, parentObj.registerC);
3500  },
3501  //DI
3502  //#0xF3:
3503  function (parentObj) {
3504    parentObj.IME = false;
3505    parentObj.IRQEnableDelay = 0;
3506  },
3507  //0xF4 - Illegal
3508  //#0xF4:
3509  function (parentObj) {
3510    cout("Illegal op code 0xF4 called, pausing emulation.", 2);
3511    pause();
3512  },
3513  //PUSH AF
3514  //#0xF5:
3515  function (parentObj) {
3516    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3517    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.registerA);
3518    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3519    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, ((parentObj.FZero) ? 0x80 : 0) | ((parentObj.FSubtract) ? 0x40 : 0) | ((parentObj.FHalfCarry) ? 0x20 : 0) | ((parentObj.FCarry) ? 0x10 : 0));
3520  },
3521  //OR n
3522  //#0xF6:
3523  function (parentObj) {
3524    parentObj.registerA |= parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3525    parentObj.FZero = (parentObj.registerA == 0);
3526    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
3527    parentObj.FSubtract = parentObj.FCarry = parentObj.FHalfCarry = false;
3528  },
3529  //RST 0x30
3530  //#0xF7:
3531  function (parentObj) {
3532    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3533    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter >> 8);
3534    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3535    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter & 0xFF);
3536    parentObj.programCounter = 0x30;
3537  },
3538  //LDHL SP, n
3539  //#0xF8:
3540  function (parentObj) {
3541    var temp_var = (parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter) << 24) >> 24;
3542    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
3543    parentObj.registersHL = (parentObj.stackPointer + temp_var) & 0xFFFF;
3544    temp_var = parentObj.stackPointer ^ temp_var ^ parentObj.registersHL;
3545    parentObj.FCarry = ((temp_var & 0x100) == 0x100);
3546    parentObj.FHalfCarry = ((temp_var & 0x10) == 0x10);
3547    parentObj.FZero = parentObj.FSubtract = false;
3548  },
3549  //LD SP, HL
3550  //#0xF9:
3551  function (parentObj) {
3552    parentObj.stackPointer = parentObj.registersHL;
3553  },
3554  //LD A, (nn)
3555  //#0xFA:
3556  function (parentObj) {
3557    parentObj.registerA = parentObj.memoryRead((parentObj.memoryRead((parentObj.programCounter + 1) & 0xFFFF) << 8) | parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter));
3558    parentObj.programCounter = (parentObj.programCounter + 2) & 0xFFFF;
3559  },
3560  //EI
3561  //#0xFB:
3562  function (parentObj) {
3563    //Immediate for HALT:
3564    parentObj.IRQEnableDelay = (parentObj.IRQEnableDelay == 2 || parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter) == 0x76) ? 1 : 2;
3565  },
3566  //0xFC - Illegal
3567  //#0xFC:
3568  function (parentObj) {
3569    cout("Illegal op code 0xFC called, pausing emulation.", 2);
3570    pause();
3571  },
3572  //0xFD - Illegal
3573  //#0xFD:
3574  function (parentObj) {
3575    cout("Illegal op code 0xFD called, pausing emulation.", 2);
3576    pause();
3577  },
3578  //CP n
3579  //#0xFE:
3580  function (parentObj) {
3581    var dirtySum = parentObj.registerA - parentObj.memoryReader[parentObj.programCounter](parentObj, parentObj.programCounter);
3582    parentObj.programCounter = (parentObj.programCounter + 1) & 0xFFFF;
3583    parentObj.FHalfCarry = ((dirtySum & 0xF) > (parentObj.registerA & 0xF));
3584    parentObj.FCarry = (dirtySum < 0);
3585    parentObj.FZero = (dirtySum == 0);
3586    parentObj.FSubtract = true;
3587  },
3588  //RST 0x38
3589  //#0xFF:
3590  function (parentObj) {
3591    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3592    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter >> 8);
3593    parentObj.stackPointer = (parentObj.stackPointer - 1) & 0xFFFF;
3594    parentObj.memoryWriter[parentObj.stackPointer](parentObj, parentObj.stackPointer, parentObj.programCounter & 0xFF);
3595    parentObj.programCounter = 0x38;
3596  }
3597];
3598GameBoyCore.prototype.CBOPCODE = [
3599  //RLC B
3600  //#0x00:
3601  function (parentObj) {
3602    parentObj.FCarry = (parentObj.registerB > 0x7F);
3603    parentObj.registerB = ((parentObj.registerB << 1) & 0xFF) | ((parentObj.FCarry) ? 1 : 0);
3604    parentObj.FHalfCarry = parentObj.FSubtract = false;
3605    parentObj.FZero = (parentObj.registerB == 0);
3606  }
3607  //RLC C
3608  //#0x01:
3609  ,function (parentObj) {
3610    parentObj.FCarry = (parentObj.registerC > 0x7F);
3611    parentObj.registerC = ((parentObj.registerC << 1) & 0xFF) | ((parentObj.FCarry) ? 1 : 0);
3612    parentObj.FHalfCarry = parentObj.FSubtract = false;
3613    parentObj.FZero = (parentObj.registerC == 0);
3614  }
3615  //RLC D
3616  //#0x02:
3617  ,function (parentObj) {
3618    parentObj.FCarry = (parentObj.registerD > 0x7F);
3619    parentObj.registerD = ((parentObj.registerD << 1) & 0xFF) | ((parentObj.FCarry) ? 1 : 0);
3620    parentObj.FHalfCarry = parentObj.FSubtract = false;
3621    parentObj.FZero = (parentObj.registerD == 0);
3622  }
3623  //RLC E
3624  //#0x03:
3625  ,function (parentObj) {
3626    parentObj.FCarry = (parentObj.registerE > 0x7F);
3627    parentObj.registerE = ((parentObj.registerE << 1) & 0xFF) | ((parentObj.FCarry) ? 1 : 0);
3628    parentObj.FHalfCarry = parentObj.FSubtract = false;
3629    parentObj.FZero = (parentObj.registerE == 0);
3630  }
3631  //RLC H
3632  //#0x04:
3633  ,function (parentObj) {
3634    parentObj.FCarry = (parentObj.registersHL > 0x7FFF);
3635    parentObj.registersHL = ((parentObj.registersHL << 1) & 0xFE00) | ((parentObj.FCarry) ? 0x100 : 0) | (parentObj.registersHL & 0xFF);
3636    parentObj.FHalfCarry = parentObj.FSubtract = false;
3637    parentObj.FZero = (parentObj.registersHL < 0x100);
3638  }
3639  //RLC L
3640  //#0x05:
3641  ,function (parentObj) {
3642    parentObj.FCarry = ((parentObj.registersHL & 0x80) == 0x80);
3643    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | ((parentObj.registersHL << 1) & 0xFF) | ((parentObj.FCarry) ? 1 : 0);
3644    parentObj.FHalfCarry = parentObj.FSubtract = false;
3645    parentObj.FZero = ((parentObj.registersHL & 0xFF) == 0);
3646  }
3647  //RLC (HL)
3648  //#0x06:
3649  ,function (parentObj) {
3650    var temp_var = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
3651    parentObj.FCarry = (temp_var > 0x7F);
3652    temp_var = ((temp_var << 1) & 0xFF) | ((parentObj.FCarry) ? 1 : 0);
3653    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, temp_var);
3654    parentObj.FHalfCarry = parentObj.FSubtract = false;
3655    parentObj.FZero = (temp_var == 0);
3656  }
3657  //RLC A
3658  //#0x07:
3659  ,function (parentObj) {
3660    parentObj.FCarry = (parentObj.registerA > 0x7F);
3661    parentObj.registerA = ((parentObj.registerA << 1) & 0xFF) | ((parentObj.FCarry) ? 1 : 0);
3662    parentObj.FHalfCarry = parentObj.FSubtract = false;
3663    parentObj.FZero = (parentObj.registerA == 0);
3664  }
3665  //RRC B
3666  //#0x08:
3667  ,function (parentObj) {
3668    parentObj.FCarry = ((parentObj.registerB & 0x01) == 0x01);
3669    parentObj.registerB = ((parentObj.FCarry) ? 0x80 : 0) | (parentObj.registerB >> 1);
3670    parentObj.FHalfCarry = parentObj.FSubtract = false;
3671    parentObj.FZero = (parentObj.registerB == 0);
3672  }
3673  //RRC C
3674  //#0x09:
3675  ,function (parentObj) {
3676    parentObj.FCarry = ((parentObj.registerC & 0x01) == 0x01);
3677    parentObj.registerC = ((parentObj.FCarry) ? 0x80 : 0) | (parentObj.registerC >> 1);
3678    parentObj.FHalfCarry = parentObj.FSubtract = false;
3679    parentObj.FZero = (parentObj.registerC == 0);
3680  }
3681  //RRC D
3682  //#0x0A:
3683  ,function (parentObj) {
3684    parentObj.FCarry = ((parentObj.registerD & 0x01) == 0x01);
3685    parentObj.registerD = ((parentObj.FCarry) ? 0x80 : 0) | (parentObj.registerD >> 1);
3686    parentObj.FHalfCarry = parentObj.FSubtract = false;
3687    parentObj.FZero = (parentObj.registerD == 0);
3688  }
3689  //RRC E
3690  //#0x0B:
3691  ,function (parentObj) {
3692    parentObj.FCarry = ((parentObj.registerE & 0x01) == 0x01);
3693    parentObj.registerE = ((parentObj.FCarry) ? 0x80 : 0) | (parentObj.registerE >> 1);
3694    parentObj.FHalfCarry = parentObj.FSubtract = false;
3695    parentObj.FZero = (parentObj.registerE == 0);
3696  }
3697  //RRC H
3698  //#0x0C:
3699  ,function (parentObj) {
3700    parentObj.FCarry = ((parentObj.registersHL & 0x0100) == 0x0100);
3701    parentObj.registersHL = ((parentObj.FCarry) ? 0x8000 : 0) | ((parentObj.registersHL >> 1) & 0xFF00) | (parentObj.registersHL & 0xFF);
3702    parentObj.FHalfCarry = parentObj.FSubtract = false;
3703    parentObj.FZero = (parentObj.registersHL < 0x100);
3704  }
3705  //RRC L
3706  //#0x0D:
3707  ,function (parentObj) {
3708    parentObj.FCarry = ((parentObj.registersHL & 0x01) == 0x01);
3709    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | ((parentObj.FCarry) ? 0x80 : 0) | ((parentObj.registersHL & 0xFF) >> 1);
3710    parentObj.FHalfCarry = parentObj.FSubtract = false;
3711    parentObj.FZero = ((parentObj.registersHL & 0xFF) == 0);
3712  }
3713  //RRC (HL)
3714  //#0x0E:
3715  ,function (parentObj) {
3716    var temp_var = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
3717    parentObj.FCarry = ((temp_var & 0x01) == 0x01);
3718    temp_var = ((parentObj.FCarry) ? 0x80 : 0) | (temp_var >> 1);
3719    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, temp_var);
3720    parentObj.FHalfCarry = parentObj.FSubtract = false;
3721    parentObj.FZero = (temp_var == 0);
3722  }
3723  //RRC A
3724  //#0x0F:
3725  ,function (parentObj) {
3726    parentObj.FCarry = ((parentObj.registerA & 0x01) == 0x01);
3727    parentObj.registerA = ((parentObj.FCarry) ? 0x80 : 0) | (parentObj.registerA >> 1);
3728    parentObj.FHalfCarry = parentObj.FSubtract = false;
3729    parentObj.FZero = (parentObj.registerA == 0);
3730  }
3731  //RL B
3732  //#0x10:
3733  ,function (parentObj) {
3734    var newFCarry = (parentObj.registerB > 0x7F);
3735    parentObj.registerB = ((parentObj.registerB << 1) & 0xFF) | ((parentObj.FCarry) ? 1 : 0);
3736    parentObj.FCarry = newFCarry;
3737    parentObj.FHalfCarry = parentObj.FSubtract = false;
3738    parentObj.FZero = (parentObj.registerB == 0);
3739  }
3740  //RL C
3741  //#0x11:
3742  ,function (parentObj) {
3743    var newFCarry = (parentObj.registerC > 0x7F);
3744    parentObj.registerC = ((parentObj.registerC << 1) & 0xFF) | ((parentObj.FCarry) ? 1 : 0);
3745    parentObj.FCarry = newFCarry;
3746    parentObj.FHalfCarry = parentObj.FSubtract = false;
3747    parentObj.FZero = (parentObj.registerC == 0);
3748  }
3749  //RL D
3750  //#0x12:
3751  ,function (parentObj) {
3752    var newFCarry = (parentObj.registerD > 0x7F);
3753    parentObj.registerD = ((parentObj.registerD << 1) & 0xFF) | ((parentObj.FCarry) ? 1 : 0);
3754    parentObj.FCarry = newFCarry;
3755    parentObj.FHalfCarry = parentObj.FSubtract = false;
3756    parentObj.FZero = (parentObj.registerD == 0);
3757  }
3758  //RL E
3759  //#0x13:
3760  ,function (parentObj) {
3761    var newFCarry = (parentObj.registerE > 0x7F);
3762    parentObj.registerE = ((parentObj.registerE << 1) & 0xFF) | ((parentObj.FCarry) ? 1 : 0);
3763    parentObj.FCarry = newFCarry;
3764    parentObj.FHalfCarry = parentObj.FSubtract = false;
3765    parentObj.FZero = (parentObj.registerE == 0);
3766  }
3767  //RL H
3768  //#0x14:
3769  ,function (parentObj) {
3770    var newFCarry = (parentObj.registersHL > 0x7FFF);
3771    parentObj.registersHL = ((parentObj.registersHL << 1) & 0xFE00) | ((parentObj.FCarry) ? 0x100 : 0) | (parentObj.registersHL & 0xFF);
3772    parentObj.FCarry = newFCarry;
3773    parentObj.FHalfCarry = parentObj.FSubtract = false;
3774    parentObj.FZero = (parentObj.registersHL < 0x100);
3775  }
3776  //RL L
3777  //#0x15:
3778  ,function (parentObj) {
3779    var newFCarry = ((parentObj.registersHL & 0x80) == 0x80);
3780    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | ((parentObj.registersHL << 1) & 0xFF) | ((parentObj.FCarry) ? 1 : 0);
3781    parentObj.FCarry = newFCarry;
3782    parentObj.FHalfCarry = parentObj.FSubtract = false;
3783    parentObj.FZero = ((parentObj.registersHL & 0xFF) == 0);
3784  }
3785  //RL (HL)
3786  //#0x16:
3787  ,function (parentObj) {
3788    var temp_var = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
3789    var newFCarry = (temp_var > 0x7F);
3790    temp_var = ((temp_var << 1) & 0xFF) | ((parentObj.FCarry) ? 1 : 0);
3791    parentObj.FCarry = newFCarry;
3792    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, temp_var);
3793    parentObj.FHalfCarry = parentObj.FSubtract = false;
3794    parentObj.FZero = (temp_var == 0);
3795  }
3796  //RL A
3797  //#0x17:
3798  ,function (parentObj) {
3799    var newFCarry = (parentObj.registerA > 0x7F);
3800    parentObj.registerA = ((parentObj.registerA << 1) & 0xFF) | ((parentObj.FCarry) ? 1 : 0);
3801    parentObj.FCarry = newFCarry;
3802    parentObj.FHalfCarry = parentObj.FSubtract = false;
3803    parentObj.FZero = (parentObj.registerA == 0);
3804  }
3805  //RR B
3806  //#0x18:
3807  ,function (parentObj) {
3808    var newFCarry = ((parentObj.registerB & 0x01) == 0x01);
3809    parentObj.registerB = ((parentObj.FCarry) ? 0x80 : 0) | (parentObj.registerB >> 1);
3810    parentObj.FCarry = newFCarry;
3811    parentObj.FHalfCarry = parentObj.FSubtract = false;
3812    parentObj.FZero = (parentObj.registerB == 0);
3813  }
3814  //RR C
3815  //#0x19:
3816  ,function (parentObj) {
3817    var newFCarry = ((parentObj.registerC & 0x01) == 0x01);
3818    parentObj.registerC = ((parentObj.FCarry) ? 0x80 : 0) | (parentObj.registerC >> 1);
3819    parentObj.FCarry = newFCarry;
3820    parentObj.FHalfCarry = parentObj.FSubtract = false;
3821    parentObj.FZero = (parentObj.registerC == 0);
3822  }
3823  //RR D
3824  //#0x1A:
3825  ,function (parentObj) {
3826    var newFCarry = ((parentObj.registerD & 0x01) == 0x01);
3827    parentObj.registerD = ((parentObj.FCarry) ? 0x80 : 0) | (parentObj.registerD >> 1);
3828    parentObj.FCarry = newFCarry;
3829    parentObj.FHalfCarry = parentObj.FSubtract = false;
3830    parentObj.FZero = (parentObj.registerD == 0);
3831  }
3832  //RR E
3833  //#0x1B:
3834  ,function (parentObj) {
3835    var newFCarry = ((parentObj.registerE & 0x01) == 0x01);
3836    parentObj.registerE = ((parentObj.FCarry) ? 0x80 : 0) | (parentObj.registerE >> 1);
3837    parentObj.FCarry = newFCarry;
3838    parentObj.FHalfCarry = parentObj.FSubtract = false;
3839    parentObj.FZero = (parentObj.registerE == 0);
3840  }
3841  //RR H
3842  //#0x1C:
3843  ,function (parentObj) {
3844    var newFCarry = ((parentObj.registersHL & 0x0100) == 0x0100);
3845    parentObj.registersHL = ((parentObj.FCarry) ? 0x8000 : 0) | ((parentObj.registersHL >> 1) & 0xFF00) | (parentObj.registersHL & 0xFF);
3846    parentObj.FCarry = newFCarry;
3847    parentObj.FHalfCarry = parentObj.FSubtract = false;
3848    parentObj.FZero = (parentObj.registersHL < 0x100);
3849  }
3850  //RR L
3851  //#0x1D:
3852  ,function (parentObj) {
3853    var newFCarry = ((parentObj.registersHL & 0x01) == 0x01);
3854    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | ((parentObj.FCarry) ? 0x80 : 0) | ((parentObj.registersHL & 0xFF) >> 1);
3855    parentObj.FCarry = newFCarry;
3856    parentObj.FHalfCarry = parentObj.FSubtract = false;
3857    parentObj.FZero = ((parentObj.registersHL & 0xFF) == 0);
3858  }
3859  //RR (HL)
3860  //#0x1E:
3861  ,function (parentObj) {
3862    var temp_var = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
3863    var newFCarry = ((temp_var & 0x01) == 0x01);
3864    temp_var = ((parentObj.FCarry) ? 0x80 : 0) | (temp_var >> 1);
3865    parentObj.FCarry = newFCarry;
3866    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, temp_var);
3867    parentObj.FHalfCarry = parentObj.FSubtract = false;
3868    parentObj.FZero = (temp_var == 0);
3869  }
3870  //RR A
3871  //#0x1F:
3872  ,function (parentObj) {
3873    var newFCarry = ((parentObj.registerA & 0x01) == 0x01);
3874    parentObj.registerA = ((parentObj.FCarry) ? 0x80 : 0) | (parentObj.registerA >> 1);
3875    parentObj.FCarry = newFCarry;
3876    parentObj.FHalfCarry = parentObj.FSubtract = false;
3877    parentObj.FZero = (parentObj.registerA == 0);
3878  }
3879  //SLA B
3880  //#0x20:
3881  ,function (parentObj) {
3882    parentObj.FCarry = (parentObj.registerB > 0x7F);
3883    parentObj.registerB = (parentObj.registerB << 1) & 0xFF;
3884    parentObj.FHalfCarry = parentObj.FSubtract = false;
3885    parentObj.FZero = (parentObj.registerB == 0);
3886  }
3887  //SLA C
3888  //#0x21:
3889  ,function (parentObj) {
3890    parentObj.FCarry = (parentObj.registerC > 0x7F);
3891    parentObj.registerC = (parentObj.registerC << 1) & 0xFF;
3892    parentObj.FHalfCarry = parentObj.FSubtract = false;
3893    parentObj.FZero = (parentObj.registerC == 0);
3894  }
3895  //SLA D
3896  //#0x22:
3897  ,function (parentObj) {
3898    parentObj.FCarry = (parentObj.registerD > 0x7F);
3899    parentObj.registerD = (parentObj.registerD << 1) & 0xFF;
3900    parentObj.FHalfCarry = parentObj.FSubtract = false;
3901    parentObj.FZero = (parentObj.registerD == 0);
3902  }
3903  //SLA E
3904  //#0x23:
3905  ,function (parentObj) {
3906    parentObj.FCarry = (parentObj.registerE > 0x7F);
3907    parentObj.registerE = (parentObj.registerE << 1) & 0xFF;
3908    parentObj.FHalfCarry = parentObj.FSubtract = false;
3909    parentObj.FZero = (parentObj.registerE == 0);
3910  }
3911  //SLA H
3912  //#0x24:
3913  ,function (parentObj) {
3914    parentObj.FCarry = (parentObj.registersHL > 0x7FFF);
3915    parentObj.registersHL = ((parentObj.registersHL << 1) & 0xFE00) | (parentObj.registersHL & 0xFF);
3916    parentObj.FHalfCarry = parentObj.FSubtract = false;
3917    parentObj.FZero = (parentObj.registersHL < 0x100);
3918  }
3919  //SLA L
3920  //#0x25:
3921  ,function (parentObj) {
3922    parentObj.FCarry = ((parentObj.registersHL & 0x0080) == 0x0080);
3923    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | ((parentObj.registersHL << 1) & 0xFF);
3924    parentObj.FHalfCarry = parentObj.FSubtract = false;
3925    parentObj.FZero = ((parentObj.registersHL & 0xFF) == 0);
3926  }
3927  //SLA (HL)
3928  //#0x26:
3929  ,function (parentObj) {
3930    var temp_var = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
3931    parentObj.FCarry = (temp_var > 0x7F);
3932    temp_var = (temp_var << 1) & 0xFF;
3933    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, temp_var);
3934    parentObj.FHalfCarry = parentObj.FSubtract = false;
3935    parentObj.FZero = (temp_var == 0);
3936  }
3937  //SLA A
3938  //#0x27:
3939  ,function (parentObj) {
3940    parentObj.FCarry = (parentObj.registerA > 0x7F);
3941    parentObj.registerA = (parentObj.registerA << 1) & 0xFF;
3942    parentObj.FHalfCarry = parentObj.FSubtract = false;
3943    parentObj.FZero = (parentObj.registerA == 0);
3944  }
3945  //SRA B
3946  //#0x28:
3947  ,function (parentObj) {
3948    parentObj.FCarry = ((parentObj.registerB & 0x01) == 0x01);
3949    parentObj.registerB = (parentObj.registerB & 0x80) | (parentObj.registerB >> 1);
3950    parentObj.FHalfCarry = parentObj.FSubtract = false;
3951    parentObj.FZero = (parentObj.registerB == 0);
3952  }
3953  //SRA C
3954  //#0x29:
3955  ,function (parentObj) {
3956    parentObj.FCarry = ((parentObj.registerC & 0x01) == 0x01);
3957    parentObj.registerC = (parentObj.registerC & 0x80) | (parentObj.registerC >> 1);
3958    parentObj.FHalfCarry = parentObj.FSubtract = false;
3959    parentObj.FZero = (parentObj.registerC == 0);
3960  }
3961  //SRA D
3962  //#0x2A:
3963  ,function (parentObj) {
3964    parentObj.FCarry = ((parentObj.registerD & 0x01) == 0x01);
3965    parentObj.registerD = (parentObj.registerD & 0x80) | (parentObj.registerD >> 1);
3966    parentObj.FHalfCarry = parentObj.FSubtract = false;
3967    parentObj.FZero = (parentObj.registerD == 0);
3968  }
3969  //SRA E
3970  //#0x2B:
3971  ,function (parentObj) {
3972    parentObj.FCarry = ((parentObj.registerE & 0x01) == 0x01);
3973    parentObj.registerE = (parentObj.registerE & 0x80) | (parentObj.registerE >> 1);
3974    parentObj.FHalfCarry = parentObj.FSubtract = false;
3975    parentObj.FZero = (parentObj.registerE == 0);
3976  }
3977  //SRA H
3978  //#0x2C:
3979  ,function (parentObj) {
3980    parentObj.FCarry = ((parentObj.registersHL & 0x0100) == 0x0100);
3981    parentObj.registersHL = ((parentObj.registersHL >> 1) & 0xFF00) | (parentObj.registersHL & 0x80FF);
3982    parentObj.FHalfCarry = parentObj.FSubtract = false;
3983    parentObj.FZero = (parentObj.registersHL < 0x100);
3984  }
3985  //SRA L
3986  //#0x2D:
3987  ,function (parentObj) {
3988    parentObj.FCarry = ((parentObj.registersHL & 0x0001) == 0x0001);
3989    parentObj.registersHL = (parentObj.registersHL & 0xFF80) | ((parentObj.registersHL & 0xFF) >> 1);
3990    parentObj.FHalfCarry = parentObj.FSubtract = false;
3991    parentObj.FZero = ((parentObj.registersHL & 0xFF) == 0);
3992  }
3993  //SRA (HL)
3994  //#0x2E:
3995  ,function (parentObj) {
3996    var temp_var = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
3997    parentObj.FCarry = ((temp_var & 0x01) == 0x01);
3998    temp_var = (temp_var & 0x80) | (temp_var >> 1);
3999    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, temp_var);
4000    parentObj.FHalfCarry = parentObj.FSubtract = false;
4001    parentObj.FZero = (temp_var == 0);
4002  }
4003  //SRA A
4004  //#0x2F:
4005  ,function (parentObj) {
4006    parentObj.FCarry = ((parentObj.registerA & 0x01) == 0x01);
4007    parentObj.registerA = (parentObj.registerA & 0x80) | (parentObj.registerA >> 1);
4008    parentObj.FHalfCarry = parentObj.FSubtract = false;
4009    parentObj.FZero = (parentObj.registerA == 0);
4010  }
4011  //SWAP B
4012  //#0x30:
4013  ,function (parentObj) {
4014    parentObj.registerB = ((parentObj.registerB & 0xF) << 4) | (parentObj.registerB >> 4);
4015    parentObj.FZero = (parentObj.registerB == 0);
4016    parentObj.FCarry = parentObj.FHalfCarry = parentObj.FSubtract = false;
4017  }
4018  //SWAP C
4019  //#0x31:
4020  ,function (parentObj) {
4021    parentObj.registerC = ((parentObj.registerC & 0xF) << 4) | (parentObj.registerC >> 4);
4022    parentObj.FZero = (parentObj.registerC == 0);
4023    parentObj.FCarry = parentObj.FHalfCarry = parentObj.FSubtract = false;
4024  }
4025  //SWAP D
4026  //#0x32:
4027  ,function (parentObj) {
4028    parentObj.registerD = ((parentObj.registerD & 0xF) << 4) | (parentObj.registerD >> 4);
4029    parentObj.FZero = (parentObj.registerD == 0);
4030    parentObj.FCarry = parentObj.FHalfCarry = parentObj.FSubtract = false;
4031  }
4032  //SWAP E
4033  //#0x33:
4034  ,function (parentObj) {
4035    parentObj.registerE = ((parentObj.registerE & 0xF) << 4) | (parentObj.registerE >> 4);
4036    parentObj.FZero = (parentObj.registerE == 0);
4037    parentObj.FCarry = parentObj.FHalfCarry = parentObj.FSubtract = false;
4038  }
4039  //SWAP H
4040  //#0x34:
4041  ,function (parentObj) {
4042    parentObj.registersHL = ((parentObj.registersHL & 0xF00) << 4) | ((parentObj.registersHL & 0xF000) >> 4) | (parentObj.registersHL & 0xFF);
4043    parentObj.FZero = (parentObj.registersHL < 0x100);
4044    parentObj.FCarry = parentObj.FHalfCarry = parentObj.FSubtract = false;
4045  }
4046  //SWAP L
4047  //#0x35:
4048  ,function (parentObj) {
4049    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | ((parentObj.registersHL & 0xF) << 4) | ((parentObj.registersHL & 0xF0) >> 4);
4050    parentObj.FZero = ((parentObj.registersHL & 0xFF) == 0);
4051    parentObj.FCarry = parentObj.FHalfCarry = parentObj.FSubtract = false;
4052  }
4053  //SWAP (HL)
4054  //#0x36:
4055  ,function (parentObj) {
4056    var temp_var = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
4057    temp_var = ((temp_var & 0xF) << 4) | (temp_var >> 4);
4058    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, temp_var);
4059    parentObj.FZero = (temp_var == 0);
4060    parentObj.FCarry = parentObj.FHalfCarry = parentObj.FSubtract = false;
4061  }
4062  //SWAP A
4063  //#0x37:
4064  ,function (parentObj) {
4065    parentObj.registerA = ((parentObj.registerA & 0xF) << 4) | (parentObj.registerA >> 4);
4066    parentObj.FZero = (parentObj.registerA == 0);
4067    parentObj.FCarry = parentObj.FHalfCarry = parentObj.FSubtract = false;
4068  }
4069  //SRL B
4070  //#0x38:
4071  ,function (parentObj) {
4072    parentObj.FCarry = ((parentObj.registerB & 0x01) == 0x01);
4073    parentObj.registerB >>= 1;
4074    parentObj.FHalfCarry = parentObj.FSubtract = false;
4075    parentObj.FZero = (parentObj.registerB == 0);
4076  }
4077  //SRL C
4078  //#0x39:
4079  ,function (parentObj) {
4080    parentObj.FCarry = ((parentObj.registerC & 0x01) == 0x01);
4081    parentObj.registerC >>= 1;
4082    parentObj.FHalfCarry = parentObj.FSubtract = false;
4083    parentObj.FZero = (parentObj.registerC == 0);
4084  }
4085  //SRL D
4086  //#0x3A:
4087  ,function (parentObj) {
4088    parentObj.FCarry = ((parentObj.registerD & 0x01) == 0x01);
4089    parentObj.registerD >>= 1;
4090    parentObj.FHalfCarry = parentObj.FSubtract = false;
4091    parentObj.FZero = (parentObj.registerD == 0);
4092  }
4093  //SRL E
4094  //#0x3B:
4095  ,function (parentObj) {
4096    parentObj.FCarry = ((parentObj.registerE & 0x01) == 0x01);
4097    parentObj.registerE >>= 1;
4098    parentObj.FHalfCarry = parentObj.FSubtract = false;
4099    parentObj.FZero = (parentObj.registerE == 0);
4100  }
4101  //SRL H
4102  //#0x3C:
4103  ,function (parentObj) {
4104    parentObj.FCarry = ((parentObj.registersHL & 0x0100) == 0x0100);
4105    parentObj.registersHL = ((parentObj.registersHL >> 1) & 0xFF00) | (parentObj.registersHL & 0xFF);
4106    parentObj.FHalfCarry = parentObj.FSubtract = false;
4107    parentObj.FZero = (parentObj.registersHL < 0x100);
4108  }
4109  //SRL L
4110  //#0x3D:
4111  ,function (parentObj) {
4112    parentObj.FCarry = ((parentObj.registersHL & 0x0001) == 0x0001);
4113    parentObj.registersHL = (parentObj.registersHL & 0xFF00) | ((parentObj.registersHL & 0xFF) >> 1);
4114    parentObj.FHalfCarry = parentObj.FSubtract = false;
4115    parentObj.FZero = ((parentObj.registersHL & 0xFF) == 0);
4116  }
4117  //SRL (HL)
4118  //#0x3E:
4119  ,function (parentObj) {
4120    var temp_var = parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL);
4121    parentObj.FCarry = ((temp_var & 0x01) == 0x01);
4122    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, temp_var >> 1);
4123    parentObj.FHalfCarry = parentObj.FSubtract = false;
4124    parentObj.FZero = (temp_var < 2);
4125  }
4126  //SRL A
4127  //#0x3F:
4128  ,function (parentObj) {
4129    parentObj.FCarry = ((parentObj.registerA & 0x01) == 0x01);
4130    parentObj.registerA >>= 1;
4131    parentObj.FHalfCarry = parentObj.FSubtract = false;
4132    parentObj.FZero = (parentObj.registerA == 0);
4133  }
4134  //BIT 0, B
4135  //#0x40:
4136  ,function (parentObj) {
4137    parentObj.FHalfCarry = true;
4138    parentObj.FSubtract = false;
4139    parentObj.FZero = ((parentObj.registerB & 0x01) == 0);
4140  }
4141  //BIT 0, C
4142  //#0x41:
4143  ,function (parentObj) {
4144    parentObj.FHalfCarry = true;
4145    parentObj.FSubtract = false;
4146    parentObj.FZero = ((parentObj.registerC & 0x01) == 0);
4147  }
4148  //BIT 0, D
4149  //#0x42:
4150  ,function (parentObj) {
4151    parentObj.FHalfCarry = true;
4152    parentObj.FSubtract = false;
4153    parentObj.FZero = ((parentObj.registerD & 0x01) == 0);
4154  }
4155  //BIT 0, E
4156  //#0x43:
4157  ,function (parentObj) {
4158    parentObj.FHalfCarry = true;
4159    parentObj.FSubtract = false;
4160    parentObj.FZero = ((parentObj.registerE & 0x01) == 0);
4161  }
4162  //BIT 0, H
4163  //#0x44:
4164  ,function (parentObj) {
4165    parentObj.FHalfCarry = true;
4166    parentObj.FSubtract = false;
4167    parentObj.FZero = ((parentObj.registersHL & 0x0100) == 0);
4168  }
4169  //BIT 0, L
4170  //#0x45:
4171  ,function (parentObj) {
4172    parentObj.FHalfCarry = true;
4173    parentObj.FSubtract = false;
4174    parentObj.FZero = ((parentObj.registersHL & 0x0001) == 0);
4175  }
4176  //BIT 0, (HL)
4177  //#0x46:
4178  ,function (parentObj) {
4179    parentObj.FHalfCarry = true;
4180    parentObj.FSubtract = false;
4181    parentObj.FZero = ((parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0x01) == 0);
4182  }
4183  //BIT 0, A
4184  //#0x47:
4185  ,function (parentObj) {
4186    parentObj.FHalfCarry = true;
4187    parentObj.FSubtract = false;
4188    parentObj.FZero = ((parentObj.registerA & 0x01) == 0);
4189  }
4190  //BIT 1, B
4191  //#0x48:
4192  ,function (parentObj) {
4193    parentObj.FHalfCarry = true;
4194    parentObj.FSubtract = false;
4195    parentObj.FZero = ((parentObj.registerB & 0x02) == 0);
4196  }
4197  //BIT 1, C
4198  //#0x49:
4199  ,function (parentObj) {
4200    parentObj.FHalfCarry = true;
4201    parentObj.FSubtract = false;
4202    parentObj.FZero = ((parentObj.registerC & 0x02) == 0);
4203  }
4204  //BIT 1, D
4205  //#0x4A:
4206  ,function (parentObj) {
4207    parentObj.FHalfCarry = true;
4208    parentObj.FSubtract = false;
4209    parentObj.FZero = ((parentObj.registerD & 0x02) == 0);
4210  }
4211  //BIT 1, E
4212  //#0x4B:
4213  ,function (parentObj) {
4214    parentObj.FHalfCarry = true;
4215    parentObj.FSubtract = false;
4216    parentObj.FZero = ((parentObj.registerE & 0x02) == 0);
4217  }
4218  //BIT 1, H
4219  //#0x4C:
4220  ,function (parentObj) {
4221    parentObj.FHalfCarry = true;
4222    parentObj.FSubtract = false;
4223    parentObj.FZero = ((parentObj.registersHL & 0x0200) == 0);
4224  }
4225  //BIT 1, L
4226  //#0x4D:
4227  ,function (parentObj) {
4228    parentObj.FHalfCarry = true;
4229    parentObj.FSubtract = false;
4230    parentObj.FZero = ((parentObj.registersHL & 0x0002) == 0);
4231  }
4232  //BIT 1, (HL)
4233  //#0x4E:
4234  ,function (parentObj) {
4235    parentObj.FHalfCarry = true;
4236    parentObj.FSubtract = false;
4237    parentObj.FZero = ((parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0x02) == 0);
4238  }
4239  //BIT 1, A
4240  //#0x4F:
4241  ,function (parentObj) {
4242    parentObj.FHalfCarry = true;
4243    parentObj.FSubtract = false;
4244    parentObj.FZero = ((parentObj.registerA & 0x02) == 0);
4245  }
4246  //BIT 2, B
4247  //#0x50:
4248  ,function (parentObj) {
4249    parentObj.FHalfCarry = true;
4250    parentObj.FSubtract = false;
4251    parentObj.FZero = ((parentObj.registerB & 0x04) == 0);
4252  }
4253  //BIT 2, C
4254  //#0x51:
4255  ,function (parentObj) {
4256    parentObj.FHalfCarry = true;
4257    parentObj.FSubtract = false;
4258    parentObj.FZero = ((parentObj.registerC & 0x04) == 0);
4259  }
4260  //BIT 2, D
4261  //#0x52:
4262  ,function (parentObj) {
4263    parentObj.FHalfCarry = true;
4264    parentObj.FSubtract = false;
4265    parentObj.FZero = ((parentObj.registerD & 0x04) == 0);
4266  }
4267  //BIT 2, E
4268  //#0x53:
4269  ,function (parentObj) {
4270    parentObj.FHalfCarry = true;
4271    parentObj.FSubtract = false;
4272    parentObj.FZero = ((parentObj.registerE & 0x04) == 0);
4273  }
4274  //BIT 2, H
4275  //#0x54:
4276  ,function (parentObj) {
4277    parentObj.FHalfCarry = true;
4278    parentObj.FSubtract = false;
4279    parentObj.FZero = ((parentObj.registersHL & 0x0400) == 0);
4280  }
4281  //BIT 2, L
4282  //#0x55:
4283  ,function (parentObj) {
4284    parentObj.FHalfCarry = true;
4285    parentObj.FSubtract = false;
4286    parentObj.FZero = ((parentObj.registersHL & 0x0004) == 0);
4287  }
4288  //BIT 2, (HL)
4289  //#0x56:
4290  ,function (parentObj) {
4291    parentObj.FHalfCarry = true;
4292    parentObj.FSubtract = false;
4293    parentObj.FZero = ((parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0x04) == 0);
4294  }
4295  //BIT 2, A
4296  //#0x57:
4297  ,function (parentObj) {
4298    parentObj.FHalfCarry = true;
4299    parentObj.FSubtract = false;
4300    parentObj.FZero = ((parentObj.registerA & 0x04) == 0);
4301  }
4302  //BIT 3, B
4303  //#0x58:
4304  ,function (parentObj) {
4305    parentObj.FHalfCarry = true;
4306    parentObj.FSubtract = false;
4307    parentObj.FZero = ((parentObj.registerB & 0x08) == 0);
4308  }
4309  //BIT 3, C
4310  //#0x59:
4311  ,function (parentObj) {
4312    parentObj.FHalfCarry = true;
4313    parentObj.FSubtract = false;
4314    parentObj.FZero = ((parentObj.registerC & 0x08) == 0);
4315  }
4316  //BIT 3, D
4317  //#0x5A:
4318  ,function (parentObj) {
4319    parentObj.FHalfCarry = true;
4320    parentObj.FSubtract = false;
4321    parentObj.FZero = ((parentObj.registerD & 0x08) == 0);
4322  }
4323  //BIT 3, E
4324  //#0x5B:
4325  ,function (parentObj) {
4326    parentObj.FHalfCarry = true;
4327    parentObj.FSubtract = false;
4328    parentObj.FZero = ((parentObj.registerE & 0x08) == 0);
4329  }
4330  //BIT 3, H
4331  //#0x5C:
4332  ,function (parentObj) {
4333    parentObj.FHalfCarry = true;
4334    parentObj.FSubtract = false;
4335    parentObj.FZero = ((parentObj.registersHL & 0x0800) == 0);
4336  }
4337  //BIT 3, L
4338  //#0x5D:
4339  ,function (parentObj) {
4340    parentObj.FHalfCarry = true;
4341    parentObj.FSubtract = false;
4342    parentObj.FZero = ((parentObj.registersHL & 0x0008) == 0);
4343  }
4344  //BIT 3, (HL)
4345  //#0x5E:
4346  ,function (parentObj) {
4347    parentObj.FHalfCarry = true;
4348    parentObj.FSubtract = false;
4349    parentObj.FZero = ((parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0x08) == 0);
4350  }
4351  //BIT 3, A
4352  //#0x5F:
4353  ,function (parentObj) {
4354    parentObj.FHalfCarry = true;
4355    parentObj.FSubtract = false;
4356    parentObj.FZero = ((parentObj.registerA & 0x08) == 0);
4357  }
4358  //BIT 4, B
4359  //#0x60:
4360  ,function (parentObj) {
4361    parentObj.FHalfCarry = true;
4362    parentObj.FSubtract = false;
4363    parentObj.FZero = ((parentObj.registerB & 0x10) == 0);
4364  }
4365  //BIT 4, C
4366  //#0x61:
4367  ,function (parentObj) {
4368    parentObj.FHalfCarry = true;
4369    parentObj.FSubtract = false;
4370    parentObj.FZero = ((parentObj.registerC & 0x10) == 0);
4371  }
4372  //BIT 4, D
4373  //#0x62:
4374  ,function (parentObj) {
4375    parentObj.FHalfCarry = true;
4376    parentObj.FSubtract = false;
4377    parentObj.FZero = ((parentObj.registerD & 0x10) == 0);
4378  }
4379  //BIT 4, E
4380  //#0x63:
4381  ,function (parentObj) {
4382    parentObj.FHalfCarry = true;
4383    parentObj.FSubtract = false;
4384    parentObj.FZero = ((parentObj.registerE & 0x10) == 0);
4385  }
4386  //BIT 4, H
4387  //#0x64:
4388  ,function (parentObj) {
4389    parentObj.FHalfCarry = true;
4390    parentObj.FSubtract = false;
4391    parentObj.FZero = ((parentObj.registersHL & 0x1000) == 0);
4392  }
4393  //BIT 4, L
4394  //#0x65:
4395  ,function (parentObj) {
4396    parentObj.FHalfCarry = true;
4397    parentObj.FSubtract = false;
4398    parentObj.FZero = ((parentObj.registersHL & 0x0010) == 0);
4399  }
4400  //BIT 4, (HL)
4401  //#0x66:
4402  ,function (parentObj) {
4403    parentObj.FHalfCarry = true;
4404    parentObj.FSubtract = false;
4405    parentObj.FZero = ((parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0x10) == 0);
4406  }
4407  //BIT 4, A
4408  //#0x67:
4409  ,function (parentObj) {
4410    parentObj.FHalfCarry = true;
4411    parentObj.FSubtract = false;
4412    parentObj.FZero = ((parentObj.registerA & 0x10) == 0);
4413  }
4414  //BIT 5, B
4415  //#0x68:
4416  ,function (parentObj) {
4417    parentObj.FHalfCarry = true;
4418    parentObj.FSubtract = false;
4419    parentObj.FZero = ((parentObj.registerB & 0x20) == 0);
4420  }
4421  //BIT 5, C
4422  //#0x69:
4423  ,function (parentObj) {
4424    parentObj.FHalfCarry = true;
4425    parentObj.FSubtract = false;
4426    parentObj.FZero = ((parentObj.registerC & 0x20) == 0);
4427  }
4428  //BIT 5, D
4429  //#0x6A:
4430  ,function (parentObj) {
4431    parentObj.FHalfCarry = true;
4432    parentObj.FSubtract = false;
4433    parentObj.FZero = ((parentObj.registerD & 0x20) == 0);
4434  }
4435  //BIT 5, E
4436  //#0x6B:
4437  ,function (parentObj) {
4438    parentObj.FHalfCarry = true;
4439    parentObj.FSubtract = false;
4440    parentObj.FZero = ((parentObj.registerE & 0x20) == 0);
4441  }
4442  //BIT 5, H
4443  //#0x6C:
4444  ,function (parentObj) {
4445    parentObj.FHalfCarry = true;
4446    parentObj.FSubtract = false;
4447    parentObj.FZero = ((parentObj.registersHL & 0x2000) == 0);
4448  }
4449  //BIT 5, L
4450  //#0x6D:
4451  ,function (parentObj) {
4452    parentObj.FHalfCarry = true;
4453    parentObj.FSubtract = false;
4454    parentObj.FZero = ((parentObj.registersHL & 0x0020) == 0);
4455  }
4456  //BIT 5, (HL)
4457  //#0x6E:
4458  ,function (parentObj) {
4459    parentObj.FHalfCarry = true;
4460    parentObj.FSubtract = false;
4461    parentObj.FZero = ((parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0x20) == 0);
4462  }
4463  //BIT 5, A
4464  //#0x6F:
4465  ,function (parentObj) {
4466    parentObj.FHalfCarry = true;
4467    parentObj.FSubtract = false;
4468    parentObj.FZero = ((parentObj.registerA & 0x20) == 0);
4469  }
4470  //BIT 6, B
4471  //#0x70:
4472  ,function (parentObj) {
4473    parentObj.FHalfCarry = true;
4474    parentObj.FSubtract = false;
4475    parentObj.FZero = ((parentObj.registerB & 0x40) == 0);
4476  }
4477  //BIT 6, C
4478  //#0x71:
4479  ,function (parentObj) {
4480    parentObj.FHalfCarry = true;
4481    parentObj.FSubtract = false;
4482    parentObj.FZero = ((parentObj.registerC & 0x40) == 0);
4483  }
4484  //BIT 6, D
4485  //#0x72:
4486  ,function (parentObj) {
4487    parentObj.FHalfCarry = true;
4488    parentObj.FSubtract = false;
4489    parentObj.FZero = ((parentObj.registerD & 0x40) == 0);
4490  }
4491  //BIT 6, E
4492  //#0x73:
4493  ,function (parentObj) {
4494    parentObj.FHalfCarry = true;
4495    parentObj.FSubtract = false;
4496    parentObj.FZero = ((parentObj.registerE & 0x40) == 0);
4497  }
4498  //BIT 6, H
4499  //#0x74:
4500  ,function (parentObj) {
4501    parentObj.FHalfCarry = true;
4502    parentObj.FSubtract = false;
4503    parentObj.FZero = ((parentObj.registersHL & 0x4000) == 0);
4504  }
4505  //BIT 6, L
4506  //#0x75:
4507  ,function (parentObj) {
4508    parentObj.FHalfCarry = true;
4509    parentObj.FSubtract = false;
4510    parentObj.FZero = ((parentObj.registersHL & 0x0040) == 0);
4511  }
4512  //BIT 6, (HL)
4513  //#0x76:
4514  ,function (parentObj) {
4515    parentObj.FHalfCarry = true;
4516    parentObj.FSubtract = false;
4517    parentObj.FZero = ((parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0x40) == 0);
4518  }
4519  //BIT 6, A
4520  //#0x77:
4521  ,function (parentObj) {
4522    parentObj.FHalfCarry = true;
4523    parentObj.FSubtract = false;
4524    parentObj.FZero = ((parentObj.registerA & 0x40) == 0);
4525  }
4526  //BIT 7, B
4527  //#0x78:
4528  ,function (parentObj) {
4529    parentObj.FHalfCarry = true;
4530    parentObj.FSubtract = false;
4531    parentObj.FZero = ((parentObj.registerB & 0x80) == 0);
4532  }
4533  //BIT 7, C
4534  //#0x79:
4535  ,function (parentObj) {
4536    parentObj.FHalfCarry = true;
4537    parentObj.FSubtract = false;
4538    parentObj.FZero = ((parentObj.registerC & 0x80) == 0);
4539  }
4540  //BIT 7, D
4541  //#0x7A:
4542  ,function (parentObj) {
4543    parentObj.FHalfCarry = true;
4544    parentObj.FSubtract = false;
4545    parentObj.FZero = ((parentObj.registerD & 0x80) == 0);
4546  }
4547  //BIT 7, E
4548  //#0x7B:
4549  ,function (parentObj) {
4550    parentObj.FHalfCarry = true;
4551    parentObj.FSubtract = false;
4552    parentObj.FZero = ((parentObj.registerE & 0x80) == 0);
4553  }
4554  //BIT 7, H
4555  //#0x7C:
4556  ,function (parentObj) {
4557    parentObj.FHalfCarry = true;
4558    parentObj.FSubtract = false;
4559    parentObj.FZero = ((parentObj.registersHL & 0x8000) == 0);
4560  }
4561  //BIT 7, L
4562  //#0x7D:
4563  ,function (parentObj) {
4564    parentObj.FHalfCarry = true;
4565    parentObj.FSubtract = false;
4566    parentObj.FZero = ((parentObj.registersHL & 0x0080) == 0);
4567  }
4568  //BIT 7, (HL)
4569  //#0x7E:
4570  ,function (parentObj) {
4571    parentObj.FHalfCarry = true;
4572    parentObj.FSubtract = false;
4573    parentObj.FZero = ((parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0x80) == 0);
4574  }
4575  //BIT 7, A
4576  //#0x7F:
4577  ,function (parentObj) {
4578    parentObj.FHalfCarry = true;
4579    parentObj.FSubtract = false;
4580    parentObj.FZero = ((parentObj.registerA & 0x80) == 0);
4581  }
4582  //RES 0, B
4583  //#0x80:
4584  ,function (parentObj) {
4585    parentObj.registerB &= 0xFE;
4586  }
4587  //RES 0, C
4588  //#0x81:
4589  ,function (parentObj) {
4590    parentObj.registerC &= 0xFE;
4591  }
4592  //RES 0, D
4593  //#0x82:
4594  ,function (parentObj) {
4595    parentObj.registerD &= 0xFE;
4596  }
4597  //RES 0, E
4598  //#0x83:
4599  ,function (parentObj) {
4600    parentObj.registerE &= 0xFE;
4601  }
4602  //RES 0, H
4603  //#0x84:
4604  ,function (parentObj) {
4605    parentObj.registersHL &= 0xFEFF;
4606  }
4607  //RES 0, L
4608  //#0x85:
4609  ,function (parentObj) {
4610    parentObj.registersHL &= 0xFFFE;
4611  }
4612  //RES 0, (HL)
4613  //#0x86:
4614  ,function (parentObj) {
4615    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0xFE);
4616  }
4617  //RES 0, A
4618  //#0x87:
4619  ,function (parentObj) {
4620    parentObj.registerA &= 0xFE;
4621  }
4622  //RES 1, B
4623  //#0x88:
4624  ,function (parentObj) {
4625    parentObj.registerB &= 0xFD;
4626  }
4627  //RES 1, C
4628  //#0x89:
4629  ,function (parentObj) {
4630    parentObj.registerC &= 0xFD;
4631  }
4632  //RES 1, D
4633  //#0x8A:
4634  ,function (parentObj) {
4635    parentObj.registerD &= 0xFD;
4636  }
4637  //RES 1, E
4638  //#0x8B:
4639  ,function (parentObj) {
4640    parentObj.registerE &= 0xFD;
4641  }
4642  //RES 1, H
4643  //#0x8C:
4644  ,function (parentObj) {
4645    parentObj.registersHL &= 0xFDFF;
4646  }
4647  //RES 1, L
4648  //#0x8D:
4649  ,function (parentObj) {
4650    parentObj.registersHL &= 0xFFFD;
4651  }
4652  //RES 1, (HL)
4653  //#0x8E:
4654  ,function (parentObj) {
4655    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0xFD);
4656  }
4657  //RES 1, A
4658  //#0x8F:
4659  ,function (parentObj) {
4660    parentObj.registerA &= 0xFD;
4661  }
4662  //RES 2, B
4663  //#0x90:
4664  ,function (parentObj) {
4665    parentObj.registerB &= 0xFB;
4666  }
4667  //RES 2, C
4668  //#0x91:
4669  ,function (parentObj) {
4670    parentObj.registerC &= 0xFB;
4671  }
4672  //RES 2, D
4673  //#0x92:
4674  ,function (parentObj) {
4675    parentObj.registerD &= 0xFB;
4676  }
4677  //RES 2, E
4678  //#0x93:
4679  ,function (parentObj) {
4680    parentObj.registerE &= 0xFB;
4681  }
4682  //RES 2, H
4683  //#0x94:
4684  ,function (parentObj) {
4685    parentObj.registersHL &= 0xFBFF;
4686  }
4687  //RES 2, L
4688  //#0x95:
4689  ,function (parentObj) {
4690    parentObj.registersHL &= 0xFFFB;
4691  }
4692  //RES 2, (HL)
4693  //#0x96:
4694  ,function (parentObj) {
4695    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0xFB);
4696  }
4697  //RES 2, A
4698  //#0x97:
4699  ,function (parentObj) {
4700    parentObj.registerA &= 0xFB;
4701  }
4702  //RES 3, B
4703  //#0x98:
4704  ,function (parentObj) {
4705    parentObj.registerB &= 0xF7;
4706  }
4707  //RES 3, C
4708  //#0x99:
4709  ,function (parentObj) {
4710    parentObj.registerC &= 0xF7;
4711  }
4712  //RES 3, D
4713  //#0x9A:
4714  ,function (parentObj) {
4715    parentObj.registerD &= 0xF7;
4716  }
4717  //RES 3, E
4718  //#0x9B:
4719  ,function (parentObj) {
4720    parentObj.registerE &= 0xF7;
4721  }
4722  //RES 3, H
4723  //#0x9C:
4724  ,function (parentObj) {
4725    parentObj.registersHL &= 0xF7FF;
4726  }
4727  //RES 3, L
4728  //#0x9D:
4729  ,function (parentObj) {
4730    parentObj.registersHL &= 0xFFF7;
4731  }
4732  //RES 3, (HL)
4733  //#0x9E:
4734  ,function (parentObj) {
4735    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0xF7);
4736  }
4737  //RES 3, A
4738  //#0x9F:
4739  ,function (parentObj) {
4740    parentObj.registerA &= 0xF7;
4741  }
4742  //RES 3, B
4743  //#0xA0:
4744  ,function (parentObj) {
4745    parentObj.registerB &= 0xEF;
4746  }
4747  //RES 4, C
4748  //#0xA1:
4749  ,function (parentObj) {
4750    parentObj.registerC &= 0xEF;
4751  }
4752  //RES 4, D
4753  //#0xA2:
4754  ,function (parentObj) {
4755    parentObj.registerD &= 0xEF;
4756  }
4757  //RES 4, E
4758  //#0xA3:
4759  ,function (parentObj) {
4760    parentObj.registerE &= 0xEF;
4761  }
4762  //RES 4, H
4763  //#0xA4:
4764  ,function (parentObj) {
4765    parentObj.registersHL &= 0xEFFF;
4766  }
4767  //RES 4, L
4768  //#0xA5:
4769  ,function (parentObj) {
4770    parentObj.registersHL &= 0xFFEF;
4771  }
4772  //RES 4, (HL)
4773  //#0xA6:
4774  ,function (parentObj) {
4775    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0xEF);
4776  }
4777  //RES 4, A
4778  //#0xA7:
4779  ,function (parentObj) {
4780    parentObj.registerA &= 0xEF;
4781  }
4782  //RES 5, B
4783  //#0xA8:
4784  ,function (parentObj) {
4785    parentObj.registerB &= 0xDF;
4786  }
4787  //RES 5, C
4788  //#0xA9:
4789  ,function (parentObj) {
4790    parentObj.registerC &= 0xDF;
4791  }
4792  //RES 5, D
4793  //#0xAA:
4794  ,function (parentObj) {
4795    parentObj.registerD &= 0xDF;
4796  }
4797  //RES 5, E
4798  //#0xAB:
4799  ,function (parentObj) {
4800    parentObj.registerE &= 0xDF;
4801  }
4802  //RES 5, H
4803  //#0xAC:
4804  ,function (parentObj) {
4805    parentObj.registersHL &= 0xDFFF;
4806  }
4807  //RES 5, L
4808  //#0xAD:
4809  ,function (parentObj) {
4810    parentObj.registersHL &= 0xFFDF;
4811  }
4812  //RES 5, (HL)
4813  //#0xAE:
4814  ,function (parentObj) {
4815    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0xDF);
4816  }
4817  //RES 5, A
4818  //#0xAF:
4819  ,function (parentObj) {
4820    parentObj.registerA &= 0xDF;
4821  }
4822  //RES 6, B
4823  //#0xB0:
4824  ,function (parentObj) {
4825    parentObj.registerB &= 0xBF;
4826  }
4827  //RES 6, C
4828  //#0xB1:
4829  ,function (parentObj) {
4830    parentObj.registerC &= 0xBF;
4831  }
4832  //RES 6, D
4833  //#0xB2:
4834  ,function (parentObj) {
4835    parentObj.registerD &= 0xBF;
4836  }
4837  //RES 6, E
4838  //#0xB3:
4839  ,function (parentObj) {
4840    parentObj.registerE &= 0xBF;
4841  }
4842  //RES 6, H
4843  //#0xB4:
4844  ,function (parentObj) {
4845    parentObj.registersHL &= 0xBFFF;
4846  }
4847  //RES 6, L
4848  //#0xB5:
4849  ,function (parentObj) {
4850    parentObj.registersHL &= 0xFFBF;
4851  }
4852  //RES 6, (HL)
4853  //#0xB6:
4854  ,function (parentObj) {
4855    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0xBF);
4856  }
4857  //RES 6, A
4858  //#0xB7:
4859  ,function (parentObj) {
4860    parentObj.registerA &= 0xBF;
4861  }
4862  //RES 7, B
4863  //#0xB8:
4864  ,function (parentObj) {
4865    parentObj.registerB &= 0x7F;
4866  }
4867  //RES 7, C
4868  //#0xB9:
4869  ,function (parentObj) {
4870    parentObj.registerC &= 0x7F;
4871  }
4872  //RES 7, D
4873  //#0xBA:
4874  ,function (parentObj) {
4875    parentObj.registerD &= 0x7F;
4876  }
4877  //RES 7, E
4878  //#0xBB:
4879  ,function (parentObj) {
4880    parentObj.registerE &= 0x7F;
4881  }
4882  //RES 7, H
4883  //#0xBC:
4884  ,function (parentObj) {
4885    parentObj.registersHL &= 0x7FFF;
4886  }
4887  //RES 7, L
4888  //#0xBD:
4889  ,function (parentObj) {
4890    parentObj.registersHL &= 0xFF7F;
4891  }
4892  //RES 7, (HL)
4893  //#0xBE:
4894  ,function (parentObj) {
4895    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) & 0x7F);
4896  }
4897  //RES 7, A
4898  //#0xBF:
4899  ,function (parentObj) {
4900    parentObj.registerA &= 0x7F;
4901  }
4902  //SET 0, B
4903  //#0xC0:
4904  ,function (parentObj) {
4905    parentObj.registerB |= 0x01;
4906  }
4907  //SET 0, C
4908  //#0xC1:
4909  ,function (parentObj) {
4910    parentObj.registerC |= 0x01;
4911  }
4912  //SET 0, D
4913  //#0xC2:
4914  ,function (parentObj) {
4915    parentObj.registerD |= 0x01;
4916  }
4917  //SET 0, E
4918  //#0xC3:
4919  ,function (parentObj) {
4920    parentObj.registerE |= 0x01;
4921  }
4922  //SET 0, H
4923  //#0xC4:
4924  ,function (parentObj) {
4925    parentObj.registersHL |= 0x0100;
4926  }
4927  //SET 0, L
4928  //#0xC5:
4929  ,function (parentObj) {
4930    parentObj.registersHL |= 0x01;
4931  }
4932  //SET 0, (HL)
4933  //#0xC6:
4934  ,function (parentObj) {
4935    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) | 0x01);
4936  }
4937  //SET 0, A
4938  //#0xC7:
4939  ,function (parentObj) {
4940    parentObj.registerA |= 0x01;
4941  }
4942  //SET 1, B
4943  //#0xC8:
4944  ,function (parentObj) {
4945    parentObj.registerB |= 0x02;
4946  }
4947  //SET 1, C
4948  //#0xC9:
4949  ,function (parentObj) {
4950    parentObj.registerC |= 0x02;
4951  }
4952  //SET 1, D
4953  //#0xCA:
4954  ,function (parentObj) {
4955    parentObj.registerD |= 0x02;
4956  }
4957  //SET 1, E
4958  //#0xCB:
4959  ,function (parentObj) {
4960    parentObj.registerE |= 0x02;
4961  }
4962  //SET 1, H
4963  //#0xCC:
4964  ,function (parentObj) {
4965    parentObj.registersHL |= 0x0200;
4966  }
4967  //SET 1, L
4968  //#0xCD:
4969  ,function (parentObj) {
4970    parentObj.registersHL |= 0x02;
4971  }
4972  //SET 1, (HL)
4973  //#0xCE:
4974  ,function (parentObj) {
4975    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) | 0x02);
4976  }
4977  //SET 1, A
4978  //#0xCF:
4979  ,function (parentObj) {
4980    parentObj.registerA |= 0x02;
4981  }
4982  //SET 2, B
4983  //#0xD0:
4984  ,function (parentObj) {
4985    parentObj.registerB |= 0x04;
4986  }
4987  //SET 2, C
4988  //#0xD1:
4989  ,function (parentObj) {
4990    parentObj.registerC |= 0x04;
4991  }
4992  //SET 2, D
4993  //#0xD2:
4994  ,function (parentObj) {
4995    parentObj.registerD |= 0x04;
4996  }
4997  //SET 2, E
4998  //#0xD3:
4999  ,function (parentObj) {
5000    parentObj.registerE |= 0x04;
5001  }
5002  //SET 2, H
5003  //#0xD4:
5004  ,function (parentObj) {
5005    parentObj.registersHL |= 0x0400;
5006  }
5007  //SET 2, L
5008  //#0xD5:
5009  ,function (parentObj) {
5010    parentObj.registersHL |= 0x04;
5011  }
5012  //SET 2, (HL)
5013  //#0xD6:
5014  ,function (parentObj) {
5015    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) | 0x04);
5016  }
5017  //SET 2, A
5018  //#0xD7:
5019  ,function (parentObj) {
5020    parentObj.registerA |= 0x04;
5021  }
5022  //SET 3, B
5023  //#0xD8:
5024  ,function (parentObj) {
5025    parentObj.registerB |= 0x08;
5026  }
5027  //SET 3, C
5028  //#0xD9:
5029  ,function (parentObj) {
5030    parentObj.registerC |= 0x08;
5031  }
5032  //SET 3, D
5033  //#0xDA:
5034  ,function (parentObj) {
5035    parentObj.registerD |= 0x08;
5036  }
5037  //SET 3, E
5038  //#0xDB:
5039  ,function (parentObj) {
5040    parentObj.registerE |= 0x08;
5041  }
5042  //SET 3, H
5043  //#0xDC:
5044  ,function (parentObj) {
5045    parentObj.registersHL |= 0x0800;
5046  }
5047  //SET 3, L
5048  //#0xDD:
5049  ,function (parentObj) {
5050    parentObj.registersHL |= 0x08;
5051  }
5052  //SET 3, (HL)
5053  //#0xDE:
5054  ,function (parentObj) {
5055    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) | 0x08);
5056  }
5057  //SET 3, A
5058  //#0xDF:
5059  ,function (parentObj) {
5060    parentObj.registerA |= 0x08;
5061  }
5062  //SET 4, B
5063  //#0xE0:
5064  ,function (parentObj) {
5065    parentObj.registerB |= 0x10;
5066  }
5067  //SET 4, C
5068  //#0xE1:
5069  ,function (parentObj) {
5070    parentObj.registerC |= 0x10;
5071  }
5072  //SET 4, D
5073  //#0xE2:
5074  ,function (parentObj) {
5075    parentObj.registerD |= 0x10;
5076  }
5077  //SET 4, E
5078  //#0xE3:
5079  ,function (parentObj) {
5080    parentObj.registerE |= 0x10;
5081  }
5082  //SET 4, H
5083  //#0xE4:
5084  ,function (parentObj) {
5085    parentObj.registersHL |= 0x1000;
5086  }
5087  //SET 4, L
5088  //#0xE5:
5089  ,function (parentObj) {
5090    parentObj.registersHL |= 0x10;
5091  }
5092  //SET 4, (HL)
5093  //#0xE6:
5094  ,function (parentObj) {
5095    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) | 0x10);
5096  }
5097  //SET 4, A
5098  //#0xE7:
5099  ,function (parentObj) {
5100    parentObj.registerA |= 0x10;
5101  }
5102  //SET 5, B
5103  //#0xE8:
5104  ,function (parentObj) {
5105    parentObj.registerB |= 0x20;
5106  }
5107  //SET 5, C
5108  //#0xE9:
5109  ,function (parentObj) {
5110    parentObj.registerC |= 0x20;
5111  }
5112  //SET 5, D
5113  //#0xEA:
5114  ,function (parentObj) {
5115    parentObj.registerD |= 0x20;
5116  }
5117  //SET 5, E
5118  //#0xEB:
5119  ,function (parentObj) {
5120    parentObj.registerE |= 0x20;
5121  }
5122  //SET 5, H
5123  //#0xEC:
5124  ,function (parentObj) {
5125    parentObj.registersHL |= 0x2000;
5126  }
5127  //SET 5, L
5128  //#0xED:
5129  ,function (parentObj) {
5130    parentObj.registersHL |= 0x20;
5131  }
5132  //SET 5, (HL)
5133  //#0xEE:
5134  ,function (parentObj) {
5135    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) | 0x20);
5136  }
5137  //SET 5, A
5138  //#0xEF:
5139  ,function (parentObj) {
5140    parentObj.registerA |= 0x20;
5141  }
5142  //SET 6, B
5143  //#0xF0:
5144  ,function (parentObj) {
5145    parentObj.registerB |= 0x40;
5146  }
5147  //SET 6, C
5148  //#0xF1:
5149  ,function (parentObj) {
5150    parentObj.registerC |= 0x40;
5151  }
5152  //SET 6, D
5153  //#0xF2:
5154  ,function (parentObj) {
5155    parentObj.registerD |= 0x40;
5156  }
5157  //SET 6, E
5158  //#0xF3:
5159  ,function (parentObj) {
5160    parentObj.registerE |= 0x40;
5161  }
5162  //SET 6, H
5163  //#0xF4:
5164  ,function (parentObj) {
5165    parentObj.registersHL |= 0x4000;
5166  }
5167  //SET 6, L
5168  //#0xF5:
5169  ,function (parentObj) {
5170    parentObj.registersHL |= 0x40;
5171  }
5172  //SET 6, (HL)
5173  //#0xF6:
5174  ,function (parentObj) {
5175    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) | 0x40);
5176  }
5177  //SET 6, A
5178  //#0xF7:
5179  ,function (parentObj) {
5180    parentObj.registerA |= 0x40;
5181  }
5182  //SET 7, B
5183  //#0xF8:
5184  ,function (parentObj) {
5185    parentObj.registerB |= 0x80;
5186  }
5187  //SET 7, C
5188  //#0xF9:
5189  ,function (parentObj) {
5190    parentObj.registerC |= 0x80;
5191  }
5192  //SET 7, D
5193  //#0xFA:
5194  ,function (parentObj) {
5195    parentObj.registerD |= 0x80;
5196  }
5197  //SET 7, E
5198  //#0xFB:
5199  ,function (parentObj) {
5200    parentObj.registerE |= 0x80;
5201  }
5202  //SET 7, H
5203  //#0xFC:
5204  ,function (parentObj) {
5205    parentObj.registersHL |= 0x8000;
5206  }
5207  //SET 7, L
5208  //#0xFD:
5209  ,function (parentObj) {
5210    parentObj.registersHL |= 0x80;
5211  }
5212  //SET 7, (HL)
5213  //#0xFE:
5214  ,function (parentObj) {
5215    parentObj.memoryWriter[parentObj.registersHL](parentObj, parentObj.registersHL, parentObj.memoryReader[parentObj.registersHL](parentObj, parentObj.registersHL) | 0x80);
5216  }
5217  //SET 7, A
5218  //#0xFF:
5219  ,function (parentObj) {
5220    parentObj.registerA |= 0x80;
5221  }
5222];
5223GameBoyCore.prototype.TICKTable = [    //Number of machine cycles for each instruction:
5224/*   0,  1,  2,  3,  4,  5,  6,  7,      8,  9,  A, B,  C,  D, E,  F*/
5225     4, 12,  8,  8,  4,  4,  8,  4,     20,  8,  8, 8,  4,  4, 8,  4,  //0
5226     4, 12,  8,  8,  4,  4,  8,  4,     12,  8,  8, 8,  4,  4, 8,  4,  //1
5227     8, 12,  8,  8,  4,  4,  8,  4,      8,  8,  8, 8,  4,  4, 8,  4,  //2
5228     8, 12,  8,  8, 12, 12, 12,  4,      8,  8,  8, 8,  4,  4, 8,  4,  //3
5229
5230     4,  4,  4,  4,  4,  4,  8,  4,      4,  4,  4, 4,  4,  4, 8,  4,  //4
5231     4,  4,  4,  4,  4,  4,  8,  4,      4,  4,  4, 4,  4,  4, 8,  4,  //5
5232     4,  4,  4,  4,  4,  4,  8,  4,      4,  4,  4, 4,  4,  4, 8,  4,  //6
5233     8,  8,  8,  8,  8,  8,  4,  8,      4,  4,  4, 4,  4,  4, 8,  4,  //7
5234
5235     4,  4,  4,  4,  4,  4,  8,  4,      4,  4,  4, 4,  4,  4, 8,  4,  //8
5236     4,  4,  4,  4,  4,  4,  8,  4,      4,  4,  4, 4,  4,  4, 8,  4,  //9
5237     4,  4,  4,  4,  4,  4,  8,  4,      4,  4,  4, 4,  4,  4, 8,  4,  //A
5238     4,  4,  4,  4,  4,  4,  8,  4,      4,  4,  4, 4,  4,  4, 8,  4,  //B
5239
5240     8, 12, 12, 16, 12, 16,  8, 16,      8, 16, 12, 0, 12, 24, 8, 16,  //C
5241     8, 12, 12,  4, 12, 16,  8, 16,      8, 16, 12, 4, 12,  4, 8, 16,  //D
5242    12, 12,  8,  4,  4, 16,  8, 16,     16,  4, 16, 4,  4,  4, 8, 16,  //E
5243    12, 12,  8,  4,  4, 16,  8, 16,     12,  8, 16, 4,  0,  4, 8, 16   //F
5244];
5245GameBoyCore.prototype.SecondaryTICKTable = [  //Number of machine cycles for each 0xCBXX instruction:
5246/*  0, 1, 2, 3, 4, 5,  6, 7,        8, 9, A, B, C, D,  E, F*/
5247    8, 8, 8, 8, 8, 8, 16, 8,        8, 8, 8, 8, 8, 8, 16, 8,  //0
5248    8, 8, 8, 8, 8, 8, 16, 8,        8, 8, 8, 8, 8, 8, 16, 8,  //1
5249    8, 8, 8, 8, 8, 8, 16, 8,        8, 8, 8, 8, 8, 8, 16, 8,  //2
5250    8, 8, 8, 8, 8, 8, 16, 8,        8, 8, 8, 8, 8, 8, 16, 8,  //3
5251
5252    8, 8, 8, 8, 8, 8, 12, 8,        8, 8, 8, 8, 8, 8, 12, 8,  //4
5253    8, 8, 8, 8, 8, 8, 12, 8,        8, 8, 8, 8, 8, 8, 12, 8,  //5
5254    8, 8, 8, 8, 8, 8, 12, 8,        8, 8, 8, 8, 8, 8, 12, 8,  //6
5255    8, 8, 8, 8, 8, 8, 12, 8,        8, 8, 8, 8, 8, 8, 12, 8,  //7
5256
5257    8, 8, 8, 8, 8, 8, 16, 8,        8, 8, 8, 8, 8, 8, 16, 8,  //8
5258    8, 8, 8, 8, 8, 8, 16, 8,        8, 8, 8, 8, 8, 8, 16, 8,  //9
5259    8, 8, 8, 8, 8, 8, 16, 8,        8, 8, 8, 8, 8, 8, 16, 8,  //A
5260    8, 8, 8, 8, 8, 8, 16, 8,        8, 8, 8, 8, 8, 8, 16, 8,  //B
5261
5262    8, 8, 8, 8, 8, 8, 16, 8,        8, 8, 8, 8, 8, 8, 16, 8,  //C
5263    8, 8, 8, 8, 8, 8, 16, 8,        8, 8, 8, 8, 8, 8, 16, 8,  //D
5264    8, 8, 8, 8, 8, 8, 16, 8,        8, 8, 8, 8, 8, 8, 16, 8,  //E
5265    8, 8, 8, 8, 8, 8, 16, 8,        8, 8, 8, 8, 8, 8, 16, 8   //F
5266];
5267GameBoyCore.prototype.saveSRAMState = function () {
5268  if (!this.cBATT || this.MBCRam.length == 0) {
5269    //No battery backup...
5270    return [];
5271  }
5272  else {
5273    //Return the MBC RAM for backup...
5274    return this.fromTypedArray(this.MBCRam);
5275  }
5276}
5277GameBoyCore.prototype.saveRTCState = function () {
5278  if (!this.cTIMER) {
5279    //No battery backup...
5280    return [];
5281  }
5282  else {
5283    //Return the MBC RAM for backup...
5284    return [
5285      this.lastIteration,
5286      this.RTCisLatched,
5287      this.latchedSeconds,
5288      this.latchedMinutes,
5289      this.latchedHours,
5290      this.latchedLDays,
5291      this.latchedHDays,
5292      this.RTCSeconds,
5293      this.RTCMinutes,
5294      this.RTCHours,
5295      this.RTCDays,
5296      this.RTCDayOverFlow,
5297      this.RTCHALT
5298    ];
5299  }
5300}
5301GameBoyCore.prototype.saveState = function () {
5302  return [
5303    this.fromTypedArray(this.ROM),
5304    this.inBootstrap,
5305    this.registerA,
5306    this.FZero,
5307    this.FSubtract,
5308    this.FHalfCarry,
5309    this.FCarry,
5310    this.registerB,
5311    this.registerC,
5312    this.registerD,
5313    this.registerE,
5314    this.registersHL,
5315    this.stackPointer,
5316    this.programCounter,
5317    this.halt,
5318    this.IME,
5319    this.hdmaRunning,
5320    this.CPUTicks,
5321    this.doubleSpeedShifter,
5322    this.fromTypedArray(this.memory),
5323    this.fromTypedArray(this.MBCRam),
5324    this.fromTypedArray(this.VRAM),
5325    this.currVRAMBank,
5326    this.fromTypedArray(this.GBCMemory),
5327    this.MBC1Mode,
5328    this.MBCRAMBanksEnabled,
5329    this.currMBCRAMBank,
5330    this.currMBCRAMBankPosition,
5331    this.cGBC,
5332    this.gbcRamBank,
5333    this.gbcRamBankPosition,
5334    this.ROMBank1offs,
5335    this.currentROMBank,
5336    this.cartridgeType,
5337    this.name,
5338    this.gameCode,
5339    this.modeSTAT,
5340    this.LYCMatchTriggerSTAT,
5341    this.mode2TriggerSTAT,
5342    this.mode1TriggerSTAT,
5343    this.mode0TriggerSTAT,
5344    this.LCDisOn,
5345    this.gfxWindowCHRBankPosition,
5346    this.gfxWindowDisplay,
5347    this.gfxSpriteShow,
5348    this.gfxSpriteNormalHeight,
5349    this.gfxBackgroundCHRBankPosition,
5350    this.gfxBackgroundBankOffset,
5351    this.TIMAEnabled,
5352    this.DIVTicks,
5353    this.LCDTicks,
5354    this.timerTicks,
5355    this.TACClocker,
5356    this.serialTimer,
5357    this.serialShiftTimer,
5358    this.serialShiftTimerAllocated,
5359    this.IRQEnableDelay,
5360    this.lastIteration,
5361    this.cMBC1,
5362    this.cMBC2,
5363    this.cMBC3,
5364    this.cMBC5,
5365    this.cMBC7,
5366    this.cSRAM,
5367    this.cMMMO1,
5368    this.cRUMBLE,
5369    this.cCamera,
5370    this.cTAMA5,
5371    this.cHuC3,
5372    this.cHuC1,
5373    this.drewBlank,
5374    this.fromTypedArray(this.frameBuffer),
5375    this.bgEnabled,
5376    this.BGPriorityEnabled,
5377    this.channel1FrequencyTracker,
5378    this.channel1FrequencyCounter,
5379    this.channel1totalLength,
5380    this.channel1envelopeVolume,
5381    this.channel1envelopeType,
5382    this.channel1envelopeSweeps,
5383    this.channel1envelopeSweepsLast,
5384    this.channel1consecutive,
5385    this.channel1frequency,
5386    this.channel1SweepFault,
5387    this.channel1ShadowFrequency,
5388    this.channel1timeSweep,
5389    this.channel1lastTimeSweep,
5390    this.channel1numSweep,
5391    this.channel1frequencySweepDivider,
5392    this.channel1decreaseSweep,
5393    this.channel2FrequencyTracker,
5394    this.channel2FrequencyCounter,
5395    this.channel2totalLength,
5396    this.channel2envelopeVolume,
5397    this.channel2envelopeType,
5398    this.channel2envelopeSweeps,
5399    this.channel2envelopeSweepsLast,
5400    this.channel2consecutive,
5401    this.channel2frequency,
5402    this.channel3canPlay,
5403    this.channel3totalLength,
5404    this.channel3patternType,
5405    this.channel3frequency,
5406    this.channel3consecutive,
5407    this.fromTypedArray(this.channel3PCM),
5408    this.channel4FrequencyPeriod,
5409    this.channel4lastSampleLookup,
5410    this.channel4totalLength,
5411    this.channel4envelopeVolume,
5412    this.channel4currentVolume,
5413    this.channel4envelopeType,
5414    this.channel4envelopeSweeps,
5415    this.channel4envelopeSweepsLast,
5416    this.channel4consecutive,
5417    this.channel4BitRange,
5418    this.soundMasterEnabled,
5419    this.VinLeftChannelMasterVolume,
5420    this.VinRightChannelMasterVolume,
5421    this.leftChannel1,
5422    this.leftChannel2,
5423    this.leftChannel3,
5424    this.leftChannel4,
5425    this.rightChannel1,
5426    this.rightChannel2,
5427    this.rightChannel3,
5428    this.rightChannel4,
5429    this.channel1currentSampleLeft,
5430    this.channel1currentSampleRight,
5431    this.channel2currentSampleLeft,
5432    this.channel2currentSampleRight,
5433    this.channel3currentSampleLeft,
5434    this.channel3currentSampleRight,
5435    this.channel4currentSampleLeft,
5436    this.channel4currentSampleRight,
5437    this.channel1currentSampleLeftSecondary,
5438    this.channel1currentSampleRightSecondary,
5439    this.channel2currentSampleLeftSecondary,
5440    this.channel2currentSampleRightSecondary,
5441    this.channel3currentSampleLeftSecondary,
5442    this.channel3currentSampleRightSecondary,
5443    this.channel4currentSampleLeftSecondary,
5444    this.channel4currentSampleRightSecondary,
5445    this.channel1currentSampleLeftTrimary,
5446    this.channel1currentSampleRightTrimary,
5447    this.channel2currentSampleLeftTrimary,
5448    this.channel2currentSampleRightTrimary,
5449    this.mixerOutputCache,
5450    this.channel1DutyTracker,
5451    this.channel1CachedDuty,
5452    this.channel2DutyTracker,
5453    this.channel2CachedDuty,
5454    this.channel1Enabled,
5455    this.channel2Enabled,
5456    this.channel3Enabled,
5457    this.channel4Enabled,
5458    this.sequencerClocks,
5459    this.sequencePosition,
5460    this.channel3Counter,
5461    this.channel4Counter,
5462    this.cachedChannel3Sample,
5463    this.cachedChannel4Sample,
5464    this.channel3FrequencyPeriod,
5465    this.channel3lastSampleLookup,
5466    this.actualScanLine,
5467    this.lastUnrenderedLine,
5468    this.queuedScanLines,
5469    this.RTCisLatched,
5470    this.latchedSeconds,
5471    this.latchedMinutes,
5472    this.latchedHours,
5473    this.latchedLDays,
5474    this.latchedHDays,
5475    this.RTCSeconds,
5476    this.RTCMinutes,
5477    this.RTCHours,
5478    this.RTCDays,
5479    this.RTCDayOverFlow,
5480    this.RTCHALT,
5481    this.usedBootROM,
5482    this.skipPCIncrement,
5483    this.STATTracker,
5484    this.gbcRamBankPositionECHO,
5485    this.numRAMBanks,
5486    this.windowY,
5487    this.windowX,
5488    this.fromTypedArray(this.gbcOBJRawPalette),
5489    this.fromTypedArray(this.gbcBGRawPalette),
5490    this.fromTypedArray(this.gbOBJPalette),
5491    this.fromTypedArray(this.gbBGPalette),
5492    this.fromTypedArray(this.gbcOBJPalette),
5493    this.fromTypedArray(this.gbcBGPalette),
5494    this.fromTypedArray(this.gbBGColorizedPalette),
5495    this.fromTypedArray(this.gbOBJColorizedPalette),
5496    this.fromTypedArray(this.cachedBGPaletteConversion),
5497    this.fromTypedArray(this.cachedOBJPaletteConversion),
5498    this.fromTypedArray(this.BGCHRBank1),
5499    this.fromTypedArray(this.BGCHRBank2),
5500    this.haltPostClocks,
5501    this.interruptsRequested,
5502    this.interruptsEnabled,
5503    this.remainingClocks,
5504    this.colorizedGBPalettes,
5505    this.backgroundY,
5506    this.backgroundX,
5507    this.CPUStopped
5508  ];
5509}
5510GameBoyCore.prototype.returnFromState = function (returnedFrom) {
5511  var index = 0;
5512  var state = returnedFrom.slice(0);
5513  this.ROM = this.toTypedArray(state[index++], "uint8");
5514  this.ROMBankEdge = Math.floor(this.ROM.length / 0x4000);
5515  this.inBootstrap = state[index++];
5516  this.registerA = state[index++];
5517  this.FZero = state[index++];
5518  this.FSubtract = state[index++];
5519  this.FHalfCarry = state[index++];
5520  this.FCarry = state[index++];
5521  this.registerB = state[index++];
5522  this.registerC = state[index++];
5523  this.registerD = state[index++];
5524  this.registerE = state[index++];
5525  this.registersHL = state[index++];
5526  this.stackPointer = state[index++];
5527  this.programCounter = state[index++];
5528  this.halt = state[index++];
5529  this.IME = state[index++];
5530  this.hdmaRunning = state[index++];
5531  this.CPUTicks = state[index++];
5532  this.doubleSpeedShifter = state[index++];
5533  this.memory = this.toTypedArray(state[index++], "uint8");
5534  this.MBCRam = this.toTypedArray(state[index++], "uint8");
5535  this.VRAM = this.toTypedArray(state[index++], "uint8");
5536  this.currVRAMBank = state[index++];
5537  this.GBCMemory = this.toTypedArray(state[index++], "uint8");
5538  this.MBC1Mode = state[index++];
5539  this.MBCRAMBanksEnabled = state[index++];
5540  this.currMBCRAMBank = state[index++];
5541  this.currMBCRAMBankPosition = state[index++];
5542  this.cGBC = state[index++];
5543  this.gbcRamBank = state[index++];
5544  this.gbcRamBankPosition = state[index++];
5545  this.ROMBank1offs = state[index++];
5546  this.currentROMBank = state[index++];
5547  this.cartridgeType = state[index++];
5548  this.name = state[index++];
5549  this.gameCode = state[index++];
5550  this.modeSTAT = state[index++];
5551  this.LYCMatchTriggerSTAT = state[index++];
5552  this.mode2TriggerSTAT = state[index++];
5553  this.mode1TriggerSTAT = state[index++];
5554  this.mode0TriggerSTAT = state[index++];
5555  this.LCDisOn = state[index++];
5556  this.gfxWindowCHRBankPosition = state[index++];
5557  this.gfxWindowDisplay = state[index++];
5558  this.gfxSpriteShow = state[index++];
5559  this.gfxSpriteNormalHeight = state[index++];
5560  this.gfxBackgroundCHRBankPosition = state[index++];
5561  this.gfxBackgroundBankOffset = state[index++];
5562  this.TIMAEnabled = state[index++];
5563  this.DIVTicks = state[index++];
5564  this.LCDTicks = state[index++];
5565  this.timerTicks = state[index++];
5566  this.TACClocker = state[index++];
5567  this.serialTimer = state[index++];
5568  this.serialShiftTimer = state[index++];
5569  this.serialShiftTimerAllocated = state[index++];
5570  this.IRQEnableDelay = state[index++];
5571  this.lastIteration = state[index++];
5572  this.cMBC1 = state[index++];
5573  this.cMBC2 = state[index++];
5574  this.cMBC3 = state[index++];
5575  this.cMBC5 = state[index++];
5576  this.cMBC7 = state[index++];
5577  this.cSRAM = state[index++];
5578  this.cMMMO1 = state[index++];
5579  this.cRUMBLE = state[index++];
5580  this.cCamera = state[index++];
5581  this.cTAMA5 = state[index++];
5582  this.cHuC3 = state[index++];
5583  this.cHuC1 = state[index++];
5584  this.drewBlank = state[index++];
5585  this.frameBuffer = this.toTypedArray(state[index++], "int32");
5586  this.bgEnabled = state[index++];
5587  this.BGPriorityEnabled = state[index++];
5588  this.channel1FrequencyTracker = state[index++];
5589  this.channel1FrequencyCounter = state[index++];
5590  this.channel1totalLength = state[index++];
5591  this.channel1envelopeVolume = state[index++];
5592  this.channel1envelopeType = state[index++];
5593  this.channel1envelopeSweeps = state[index++];
5594  this.channel1envelopeSweepsLast = state[index++];
5595  this.channel1consecutive = state[index++];
5596  this.channel1frequency = state[index++];
5597  this.channel1SweepFault = state[index++];
5598  this.channel1ShadowFrequency = state[index++];
5599  this.channel1timeSweep = state[index++];
5600  this.channel1lastTimeSweep = state[index++];
5601  this.channel1numSweep = state[index++];
5602  this.channel1frequencySweepDivider = state[index++];
5603  this.channel1decreaseSweep = state[index++];
5604  this.channel2FrequencyTracker = state[index++];
5605  this.channel2FrequencyCounter = state[index++];
5606  this.channel2totalLength = state[index++];
5607  this.channel2envelopeVolume = state[index++];
5608  this.channel2envelopeType = state[index++];
5609  this.channel2envelopeSweeps = state[index++];
5610  this.channel2envelopeSweepsLast = state[index++];
5611  this.channel2consecutive = state[index++];
5612  this.channel2frequency = state[index++];
5613  this.channel3canPlay = state[index++];
5614  this.channel3totalLength = state[index++];
5615  this.channel3patternType = state[index++];
5616  this.channel3frequency = state[index++];
5617  this.channel3consecutive = state[index++];
5618  this.channel3PCM = this.toTypedArray(state[index++], "int8");
5619  this.channel4FrequencyPeriod = state[index++];
5620  this.channel4lastSampleLookup = state[index++];
5621  this.channel4totalLength = state[index++];
5622  this.channel4envelopeVolume = state[index++];
5623  this.channel4currentVolume = state[index++];
5624  this.channel4envelopeType = state[index++];
5625  this.channel4envelopeSweeps = state[index++];
5626  this.channel4envelopeSweepsLast = state[index++];
5627  this.channel4consecutive = state[index++];
5628  this.channel4BitRange = state[index++];
5629  this.soundMasterEnabled = state[index++];
5630  this.VinLeftChannelMasterVolume = state[index++];
5631  this.VinRightChannelMasterVolume = state[index++];
5632  this.leftChannel1 = state[index++];
5633  this.leftChannel2 = state[index++];
5634  this.leftChannel3 = state[index++];
5635  this.leftChannel4 = state[index++];
5636  this.rightChannel1 = state[index++];
5637  this.rightChannel2 = state[index++];
5638  this.rightChannel3 = state[index++];
5639  this.rightChannel4 = state[index++];
5640  this.channel1currentSampleLeft = state[index++];
5641  this.channel1currentSampleRight = state[index++];
5642  this.channel2currentSampleLeft = state[index++];
5643  this.channel2currentSampleRight = state[index++];
5644  this.channel3currentSampleLeft = state[index++];
5645  this.channel3currentSampleRight = state[index++];
5646  this.channel4currentSampleLeft = state[index++];
5647  this.channel4currentSampleRight = state[index++];
5648  this.channel1currentSampleLeftSecondary = state[index++];
5649  this.channel1currentSampleRightSecondary = state[index++];
5650  this.channel2currentSampleLeftSecondary = state[index++];
5651  this.channel2currentSampleRightSecondary = state[index++];
5652  this.channel3currentSampleLeftSecondary = state[index++];
5653  this.channel3currentSampleRightSecondary = state[index++];
5654  this.channel4currentSampleLeftSecondary = state[index++];
5655  this.channel4currentSampleRightSecondary = state[index++];
5656  this.channel1currentSampleLeftTrimary = state[index++];
5657  this.channel1currentSampleRightTrimary = state[index++];
5658  this.channel2currentSampleLeftTrimary = state[index++];
5659  this.channel2currentSampleRightTrimary = state[index++];
5660  this.mixerOutputCache = state[index++];
5661  this.channel1DutyTracker = state[index++];
5662  this.channel1CachedDuty = state[index++];
5663  this.channel2DutyTracker = state[index++];
5664  this.channel2CachedDuty = state[index++];
5665  this.channel1Enabled = state[index++];
5666  this.channel2Enabled = state[index++];
5667  this.channel3Enabled = state[index++];
5668  this.channel4Enabled = state[index++];
5669  this.sequencerClocks = state[index++];
5670  this.sequencePosition = state[index++];
5671  this.channel3Counter = state[index++];
5672  this.channel4Counter = state[index++];
5673  this.cachedChannel3Sample = state[index++];
5674  this.cachedChannel4Sample = state[index++];
5675  this.channel3FrequencyPeriod = state[index++];
5676  this.channel3lastSampleLookup = state[index++];
5677  this.actualScanLine = state[index++];
5678  this.lastUnrenderedLine = state[index++];
5679  this.queuedScanLines = state[index++];
5680  this.RTCisLatched = state[index++];
5681  this.latchedSeconds = state[index++];
5682  this.latchedMinutes = state[index++];
5683  this.latchedHours = state[index++];
5684  this.latchedLDays = state[index++];
5685  this.latchedHDays = state[index++];
5686  this.RTCSeconds = state[index++];
5687  this.RTCMinutes = state[index++];
5688  this.RTCHours = state[index++];
5689  this.RTCDays = state[index++];
5690  this.RTCDayOverFlow = state[index++];
5691  this.RTCHALT = state[index++];
5692  this.usedBootROM = state[index++];
5693  this.skipPCIncrement = state[index++];
5694  this.STATTracker = state[index++];
5695  this.gbcRamBankPositionECHO = state[index++];
5696  this.numRAMBanks = state[index++];
5697  this.windowY = state[index++];
5698  this.windowX = state[index++];
5699  this.gbcOBJRawPalette = this.toTypedArray(state[index++], "uint8");
5700  this.gbcBGRawPalette = this.toTypedArray(state[index++], "uint8");
5701  this.gbOBJPalette = this.toTypedArray(state[index++], "int32");
5702  this.gbBGPalette = this.toTypedArray(state[index++], "int32");
5703  this.gbcOBJPalette = this.toTypedArray(state[index++], "int32");
5704  this.gbcBGPalette = this.toTypedArray(state[index++], "int32");
5705  this.gbBGColorizedPalette = this.toTypedArray(state[index++], "int32");
5706  this.gbOBJColorizedPalette = this.toTypedArray(state[index++], "int32");
5707  this.cachedBGPaletteConversion = this.toTypedArray(state[index++], "int32");
5708  this.cachedOBJPaletteConversion = this.toTypedArray(state[index++], "int32");
5709  this.BGCHRBank1 = this.toTypedArray(state[index++], "uint8");
5710  this.BGCHRBank2 = this.toTypedArray(state[index++], "uint8");
5711  this.haltPostClocks = state[index++];
5712  this.interruptsRequested = state[index++];
5713  this.interruptsEnabled = state[index++];
5714  this.checkIRQMatching();
5715  this.remainingClocks = state[index++];
5716  this.colorizedGBPalettes = state[index++];
5717  this.backgroundY = state[index++];
5718  this.backgroundX = state[index++];
5719  this.CPUStopped = state[index];
5720  this.fromSaveState = true;
5721  this.TICKTable = this.toTypedArray(this.TICKTable, "uint8");
5722  this.SecondaryTICKTable = this.toTypedArray(this.SecondaryTICKTable, "uint8");
5723  this.initializeReferencesFromSaveState();
5724  this.memoryReadJumpCompile();
5725  this.memoryWriteJumpCompile();
5726  this.initLCD();
5727  this.initSound();
5728  this.noiseSampleTable = (this.channel4BitRange == 0x7FFF) ? this.LSFR15Table : this.LSFR7Table;
5729  this.channel4VolumeShifter = (this.channel4BitRange == 0x7FFF) ? 15 : 7;
5730}
5731GameBoyCore.prototype.returnFromRTCState = function () {
5732  if (typeof this.openRTC == "function" && this.cTIMER) {
5733    var rtcData = this.openRTC(this.name);
5734    var index = 0;
5735    this.lastIteration = rtcData[index++];
5736    this.RTCisLatched = rtcData[index++];
5737    this.latchedSeconds = rtcData[index++];
5738    this.latchedMinutes = rtcData[index++];
5739    this.latchedHours = rtcData[index++];
5740    this.latchedLDays = rtcData[index++];
5741    this.latchedHDays = rtcData[index++];
5742    this.RTCSeconds = rtcData[index++];
5743    this.RTCMinutes = rtcData[index++];
5744    this.RTCHours = rtcData[index++];
5745    this.RTCDays = rtcData[index++];
5746    this.RTCDayOverFlow = rtcData[index++];
5747    this.RTCHALT = rtcData[index];
5748  }
5749}
5750
5751GameBoyCore.prototype.start = function () {
5752  this.initMemory();  //Write the startup memory.
5753  this.ROMLoad();    //Load the ROM into memory and get cartridge information from it.
5754  this.initLCD();    //Initialize the graphics.
5755  this.initSound();  //Sound object initialization.
5756  this.run();      //Start the emulation.
5757}
5758GameBoyCore.prototype.initMemory = function () {
5759  //Initialize the RAM:
5760  this.memory = this.getTypedArray(0x10000, 0, "uint8");
5761  this.frameBuffer = this.getTypedArray(23040, 0xF8F8F8, "int32");
5762  this.BGCHRBank1 = this.getTypedArray(0x800, 0, "uint8");
5763  this.TICKTable = this.toTypedArray(this.TICKTable, "uint8");
5764  this.SecondaryTICKTable = this.toTypedArray(this.SecondaryTICKTable, "uint8");
5765  this.channel3PCM = this.getTypedArray(0x20, 0, "int8");
5766}
5767GameBoyCore.prototype.generateCacheArray = function (tileAmount) {
5768  var tileArray = [];
5769  var tileNumber = 0;
5770  while (tileNumber < tileAmount) {
5771    tileArray[tileNumber++] = this.getTypedArray(64, 0, "uint8");
5772  }
5773  return tileArray;
5774}
5775GameBoyCore.prototype.initSkipBootstrap = function () {
5776  //Fill in the boot ROM set register values
5777  //Default values to the GB boot ROM values, then fill in the GBC boot ROM values after ROM loading
5778  var index = 0xFF;
5779  while (index >= 0) {
5780    if (index >= 0x30 && index < 0x40) {
5781      this.memoryWrite(0xFF00 | index, this.ffxxDump[index]);
5782    }
5783    else {
5784      switch (index) {
5785        case 0x00:
5786        case 0x01:
5787        case 0x02:
5788        case 0x05:
5789        case 0x07:
5790        case 0x0F:
5791        case 0xFF:
5792          this.memoryWrite(0xFF00 | index, this.ffxxDump[index]);
5793          break;
5794        default:
5795          this.memory[0xFF00 | index] = this.ffxxDump[index];
5796      }
5797    }
5798    --index;
5799  }
5800  if (this.cGBC) {
5801    this.memory[0xFF6C] = 0xFE;
5802    this.memory[0xFF74] = 0xFE;
5803  }
5804  else {
5805    this.memory[0xFF48] = 0xFF;
5806    this.memory[0xFF49] = 0xFF;
5807    this.memory[0xFF6C] = 0xFF;
5808    this.memory[0xFF74] = 0xFF;
5809  }
5810  //Start as an unset device:
5811  cout("Starting without the GBC boot ROM.", 0);
5812  this.registerA = (this.cGBC) ? 0x11 : 0x1;
5813  this.registerB = 0;
5814  this.registerC = 0x13;
5815  this.registerD = 0;
5816  this.registerE = 0xD8;
5817  this.FZero = true;
5818  this.FSubtract = false;
5819  this.FHalfCarry = true;
5820  this.FCarry = true;
5821  this.registersHL = 0x014D;
5822  this.LCDCONTROL = this.LINECONTROL;
5823  this.IME = false;
5824  this.IRQLineMatched = 0;
5825  this.interruptsRequested = 225;
5826  this.interruptsEnabled = 0;
5827  this.hdmaRunning = false;
5828  this.CPUTicks = 12;
5829  this.STATTracker = 0;
5830  this.modeSTAT = 1;
5831  this.spriteCount = 252;
5832  this.LYCMatchTriggerSTAT = false;
5833  this.mode2TriggerSTAT = false;
5834  this.mode1TriggerSTAT = false;
5835  this.mode0TriggerSTAT = false;
5836  this.LCDisOn = true;
5837  this.channel1FrequencyTracker = 0x2000;
5838  this.channel1DutyTracker = 0;
5839  this.channel1CachedDuty = this.dutyLookup[2];
5840  this.channel1totalLength = 0;
5841  this.channel1envelopeVolume = 0;
5842  this.channel1envelopeType = false;
5843  this.channel1envelopeSweeps = 0;
5844  this.channel1envelopeSweepsLast = 0;
5845  this.channel1consecutive = true;
5846  this.channel1frequency = 1985;
5847  this.channel1SweepFault = true;
5848  this.channel1ShadowFrequency = 1985;
5849  this.channel1timeSweep = 1;
5850  this.channel1lastTimeSweep = 0;
5851  this.channel1numSweep = 0;
5852  this.channel1frequencySweepDivider = 0;
5853  this.channel1decreaseSweep = false;
5854  this.channel2FrequencyTracker = 0x2000;
5855  this.channel2DutyTracker = 0;
5856  this.channel2CachedDuty = this.dutyLookup[2];
5857  this.channel2totalLength = 0;
5858  this.channel2envelopeVolume = 0;
5859  this.channel2envelopeType = false;
5860  this.channel2envelopeSweeps = 0;
5861  this.channel2envelopeSweepsLast = 0;
5862  this.channel2consecutive = true;
5863  this.channel2frequency = 0;
5864  this.channel3canPlay = false;
5865  this.channel3totalLength = 0;
5866  this.channel3patternType = 4;
5867  this.channel3frequency = 0;
5868  this.channel3consecutive = true;
5869  this.channel3Counter = 0x418;
5870  this.channel4FrequencyPeriod = 8;
5871  this.channel4totalLength = 0;
5872  this.channel4envelopeVolume = 0;
5873  this.channel4currentVolume = 0;
5874  this.channel4envelopeType = false;
5875  this.channel4envelopeSweeps = 0;
5876  this.channel4envelopeSweepsLast = 0;
5877  this.channel4consecutive = true;
5878  this.channel4BitRange = 0x7FFF;
5879  this.channel4VolumeShifter = 15;
5880  this.channel1FrequencyCounter = 0x200;
5881  this.channel2FrequencyCounter = 0x200;
5882  this.channel3Counter = 0x800;
5883  this.channel3FrequencyPeriod = 0x800;
5884  this.channel3lastSampleLookup = 0;
5885  this.channel4lastSampleLookup = 0;
5886  this.VinLeftChannelMasterVolume = 1;
5887  this.VinRightChannelMasterVolume = 1;
5888  this.soundMasterEnabled = true;
5889  this.leftChannel1 = true;
5890  this.leftChannel2 = true;
5891  this.leftChannel3 = true;
5892  this.leftChannel4 = true;
5893  this.rightChannel1 = true;
5894  this.rightChannel2 = true;
5895  this.rightChannel3 = false;
5896  this.rightChannel4 = false;
5897  this.DIVTicks = 27044;
5898  this.LCDTicks = 160;
5899  this.timerTicks = 0;
5900  this.TIMAEnabled = false;
5901  this.TACClocker = 1024;
5902  this.serialTimer = 0;
5903  this.serialShiftTimer = 0;
5904  this.serialShiftTimerAllocated = 0;
5905  this.IRQEnableDelay = 0;
5906  this.actualScanLine = 144;
5907  this.lastUnrenderedLine = 0;
5908  this.gfxWindowDisplay = false;
5909  this.gfxSpriteShow = false;
5910  this.gfxSpriteNormalHeight = true;
5911  this.bgEnabled = true;
5912  this.BGPriorityEnabled = true;
5913  this.gfxWindowCHRBankPosition = 0;
5914  this.gfxBackgroundCHRBankPosition = 0;
5915  this.gfxBackgroundBankOffset = 0;
5916  this.windowY = 0;
5917  this.windowX = 0;
5918  this.drewBlank = 0;
5919  this.midScanlineOffset = -1;
5920  this.currentX = 0;
5921}
5922GameBoyCore.prototype.initBootstrap = function () {
5923  //Start as an unset device:
5924  cout("Starting the selected boot ROM.", 0);
5925  this.programCounter = 0;
5926  this.stackPointer = 0;
5927  this.IME = false;
5928  this.LCDTicks = 0;
5929  this.DIVTicks = 0;
5930  this.registerA = 0;
5931  this.registerB = 0;
5932  this.registerC = 0;
5933  this.registerD = 0;
5934  this.registerE = 0;
5935  this.FZero = this.FSubtract = this.FHalfCarry = this.FCarry = false;
5936  this.registersHL = 0;
5937  this.leftChannel1 = false;
5938  this.leftChannel2 = false;
5939  this.leftChannel3 = false;
5940  this.leftChannel4 = false;
5941  this.rightChannel1 = false;
5942  this.rightChannel2 = false;
5943  this.rightChannel3 = false;
5944  this.rightChannel4 = false;
5945  this.channel2frequency = this.channel1frequency = 0;
5946  this.channel4consecutive = this.channel2consecutive = this.channel1consecutive = false;
5947  this.VinLeftChannelMasterVolume = 8;
5948  this.VinRightChannelMasterVolume = 8;
5949  this.memory[0xFF00] = 0xF;  //Set the joypad state.
5950}
5951GameBoyCore.prototype.ROMLoad = function () {
5952  //Load the first two ROM banks (0x0000 - 0x7FFF) into regular gameboy memory:
5953  this.ROM = [];
5954  this.usedBootROM = settings[1];
5955  var maxLength = this.ROMImage.length;
5956  if (maxLength < 0x4000) {
5957    throw(new Error("ROM image size too small."));
5958  }
5959  this.ROM = this.getTypedArray(maxLength, 0, "uint8");
5960  var romIndex = 0;
5961  if (this.usedBootROM) {
5962    if (!settings[11]) {
5963      //Patch in the GBC boot ROM into the memory map:
5964      for (; romIndex < 0x100; ++romIndex) {
5965        this.memory[romIndex] = this.GBCBOOTROM[romIndex];                      //Load in the GameBoy Color BOOT ROM.
5966        this.ROM[romIndex] = (this.ROMImage.charCodeAt(romIndex) & 0xFF);              //Decode the ROM binary for the switch out.
5967      }
5968      for (; romIndex < 0x200; ++romIndex) {
5969        this.memory[romIndex] = this.ROM[romIndex] = (this.ROMImage.charCodeAt(romIndex) & 0xFF);  //Load in the game ROM.
5970      }
5971      for (; romIndex < 0x900; ++romIndex) {
5972        this.memory[romIndex] = this.GBCBOOTROM[romIndex - 0x100];                  //Load in the GameBoy Color BOOT ROM.
5973        this.ROM[romIndex] = (this.ROMImage.charCodeAt(romIndex) & 0xFF);              //Decode the ROM binary for the switch out.
5974      }
5975      this.usedGBCBootROM = true;
5976    }
5977    else {
5978      //Patch in the GBC boot ROM into the memory map:
5979      for (; romIndex < 0x100; ++romIndex) {
5980        this.memory[romIndex] = this.GBBOOTROM[romIndex];                      //Load in the GameBoy Color BOOT ROM.
5981        this.ROM[romIndex] = (this.ROMImage.charCodeAt(romIndex) & 0xFF);              //Decode the ROM binary for the switch out.
5982      }
5983    }
5984    for (; romIndex < 0x4000; ++romIndex) {
5985      this.memory[romIndex] = this.ROM[romIndex] = (this.ROMImage.charCodeAt(romIndex) & 0xFF);  //Load in the game ROM.
5986    }
5987  }
5988  else {
5989    //Don't load in the boot ROM:
5990    for (; romIndex < 0x4000; ++romIndex) {
5991      this.memory[romIndex] = this.ROM[romIndex] = (this.ROMImage.charCodeAt(romIndex) & 0xFF);  //Load in the game ROM.
5992    }
5993  }
5994  //Finish the decoding of the ROM binary:
5995  for (; romIndex < maxLength; ++romIndex) {
5996    this.ROM[romIndex] = (this.ROMImage.charCodeAt(romIndex) & 0xFF);
5997  }
5998  this.ROMBankEdge = Math.floor(this.ROM.length / 0x4000);
5999  //Set up the emulator for the cartidge specifics:
6000  this.interpretCartridge();
6001  //Check for IRQ matching upon initialization:
6002  this.checkIRQMatching();
6003}
6004GameBoyCore.prototype.getROMImage = function () {
6005  //Return the binary version of the ROM image currently running:
6006  if (this.ROMImage.length > 0) {
6007    return this.ROMImage.length;
6008  }
6009  var length = this.ROM.length;
6010  for (var index = 0; index < length; index++) {
6011    this.ROMImage += String.fromCharCode(this.ROM[index]);
6012  }
6013  return this.ROMImage;
6014}
6015GameBoyCore.prototype.interpretCartridge = function () {
6016  // ROM name
6017  for (var index = 0x134; index < 0x13F; index++) {
6018    if (this.ROMImage.charCodeAt(index) > 0) {
6019      this.name += this.ROMImage[index];
6020    }
6021  }
6022  // ROM game code (for newer games)
6023  for (var index = 0x13F; index < 0x143; index++) {
6024    if (this.ROMImage.charCodeAt(index) > 0) {
6025      this.gameCode += this.ROMImage[index];
6026    }
6027  }
6028  cout("Game Title: " + this.name + "[" + this.gameCode + "][" + this.ROMImage[0x143] + "]", 0);
6029  cout("Game Code: " + this.gameCode, 0);
6030  // Cartridge type
6031  this.cartridgeType = this.ROM[0x147];
6032  cout("Cartridge type #" + this.cartridgeType, 0);
6033  //Map out ROM cartridge sub-types.
6034  var MBCType = "";
6035  switch (this.cartridgeType) {
6036    case 0x00:
6037      //ROM w/o bank switching
6038      if (!settings[9]) {
6039        MBCType = "ROM";
6040        break;
6041      }
6042    case 0x01:
6043      this.cMBC1 = true;
6044      MBCType = "MBC1";
6045      break;
6046    case 0x02:
6047      this.cMBC1 = true;
6048      this.cSRAM = true;
6049      MBCType = "MBC1 + SRAM";
6050      break;
6051    case 0x03:
6052      this.cMBC1 = true;
6053      this.cSRAM = true;
6054      this.cBATT = true;
6055      MBCType = "MBC1 + SRAM + BATT";
6056      break;
6057    case 0x05:
6058      this.cMBC2 = true;
6059      MBCType = "MBC2";
6060      break;
6061    case 0x06:
6062      this.cMBC2 = true;
6063      this.cBATT = true;
6064      MBCType = "MBC2 + BATT";
6065      break;
6066    case 0x08:
6067      this.cSRAM = true;
6068      MBCType = "ROM + SRAM";
6069      break;
6070    case 0x09:
6071      this.cSRAM = true;
6072      this.cBATT = true;
6073      MBCType = "ROM + SRAM + BATT";
6074      break;
6075    case 0x0B:
6076      this.cMMMO1 = true;
6077      MBCType = "MMMO1";
6078      break;
6079    case 0x0C:
6080      this.cMMMO1 = true;
6081      this.cSRAM = true;
6082      MBCType = "MMMO1 + SRAM";
6083      break;
6084    case 0x0D:
6085      this.cMMMO1 = true;
6086      this.cSRAM = true;
6087      this.cBATT = true;
6088      MBCType = "MMMO1 + SRAM + BATT";
6089      break;
6090    case 0x0F:
6091      this.cMBC3 = true;
6092      this.cTIMER = true;
6093      this.cBATT = true;
6094      MBCType = "MBC3 + TIMER + BATT";
6095      break;
6096    case 0x10:
6097      this.cMBC3 = true;
6098      this.cTIMER = true;
6099      this.cBATT = true;
6100      this.cSRAM = true;
6101      MBCType = "MBC3 + TIMER + BATT + SRAM";
6102      break;
6103    case 0x11:
6104      this.cMBC3 = true;
6105      MBCType = "MBC3";
6106      break;
6107    case 0x12:
6108      this.cMBC3 = true;
6109      this.cSRAM = true;
6110      MBCType = "MBC3 + SRAM";
6111      break;
6112    case 0x13:
6113      this.cMBC3 = true;
6114      this.cSRAM = true;
6115      this.cBATT = true;
6116      MBCType = "MBC3 + SRAM + BATT";
6117      break;
6118    case 0x19:
6119      this.cMBC5 = true;
6120      MBCType = "MBC5";
6121      break;
6122    case 0x1A:
6123      this.cMBC5 = true;
6124      this.cSRAM = true;
6125      MBCType = "MBC5 + SRAM";
6126      break;
6127    case 0x1B:
6128      this.cMBC5 = true;
6129      this.cSRAM = true;
6130      this.cBATT = true;
6131      MBCType = "MBC5 + SRAM + BATT";
6132      break;
6133    case 0x1C:
6134      this.cRUMBLE = true;
6135      MBCType = "RUMBLE";
6136      break;
6137    case 0x1D:
6138      this.cRUMBLE = true;
6139      this.cSRAM = true;
6140      MBCType = "RUMBLE + SRAM";
6141      break;
6142    case 0x1E:
6143      this.cRUMBLE = true;
6144      this.cSRAM = true;
6145      this.cBATT = true;
6146      MBCType = "RUMBLE + SRAM + BATT";
6147      break;
6148    case 0x1F:
6149      this.cCamera = true;
6150      MBCType = "GameBoy Camera";
6151      break;
6152    case 0x22:
6153      this.cMBC7 = true;
6154      this.cSRAM = true;
6155      this.cBATT = true;
6156      MBCType = "MBC7 + SRAM + BATT";
6157      break;
6158    case 0xFD:
6159      this.cTAMA5 = true;
6160      MBCType = "TAMA5";
6161      break;
6162    case 0xFE:
6163      this.cHuC3 = true;
6164      MBCType = "HuC3";
6165      break;
6166    case 0xFF:
6167      this.cHuC1 = true;
6168      MBCType = "HuC1";
6169      break;
6170    default:
6171      MBCType = "Unknown";
6172      cout("Cartridge type is unknown.", 2);
6173      pause();
6174  }
6175  cout("Cartridge Type: " + MBCType + ".", 0);
6176  // ROM and RAM banks
6177  this.numROMBanks = this.ROMBanks[this.ROM[0x148]];
6178  cout(this.numROMBanks + " ROM banks.", 0);
6179  switch (this.RAMBanks[this.ROM[0x149]]) {
6180    case 0:
6181      cout("No RAM banking requested for allocation or MBC is of type 2.", 0);
6182      break;
6183    case 2:
6184      cout("1 RAM bank requested for allocation.", 0);
6185      break;
6186    case 3:
6187      cout("4 RAM banks requested for allocation.", 0);
6188      break;
6189    case 4:
6190      cout("16 RAM banks requested for allocation.", 0);
6191      break;
6192    default:
6193      cout("RAM bank amount requested is unknown, will use maximum allowed by specified MBC type.", 0);
6194  }
6195  //Check the GB/GBC mode byte:
6196  if (!this.usedBootROM) {
6197    switch (this.ROM[0x143]) {
6198      case 0x00:  //Only GB mode
6199        this.cGBC = false;
6200        cout("Only GB mode detected.", 0);
6201        break;
6202      case 0x32:  //Exception to the GBC identifying code:
6203        if (!settings[2] && this.name + this.gameCode + this.ROM[0x143] == "Game and Watch 50") {
6204          this.cGBC = true;
6205          cout("Created a boot exception for Game and Watch Gallery 2 (GBC ID byte is wrong on the cartridge).", 1);
6206        }
6207        else {
6208          this.cGBC = false;
6209        }
6210        break;
6211      case 0x80:  //Both GB + GBC modes
6212        this.cGBC = !settings[2];
6213        cout("GB and GBC mode detected.", 0);
6214        break;
6215      case 0xC0:  //Only GBC mode
6216        this.cGBC = true;
6217        cout("Only GBC mode detected.", 0);
6218        break;
6219      default:
6220        this.cGBC = false;
6221        cout("Unknown GameBoy game type code #" + this.ROM[0x143] + ", defaulting to GB mode (Old games don't have a type code).", 1);
6222    }
6223    this.inBootstrap = false;
6224    this.setupRAM();  //CPU/(V)RAM initialization.
6225    this.initSkipBootstrap();
6226    this.initializeAudioStartState(); // Line added for benchmarking.
6227  }
6228  else {
6229    this.cGBC = this.usedGBCBootROM;  //Allow the GBC boot ROM to run in GBC mode...
6230    this.setupRAM();  //CPU/(V)RAM initialization.
6231    this.initBootstrap();
6232  }
6233  this.initializeModeSpecificArrays();
6234  //License Code Lookup:
6235  var cOldLicense = this.ROM[0x14B];
6236  var cNewLicense = (this.ROM[0x144] & 0xFF00) | (this.ROM[0x145] & 0xFF);
6237  if (cOldLicense != 0x33) {
6238    //Old Style License Header
6239    cout("Old style license code: " + cOldLicense, 0);
6240  }
6241  else {
6242    //New Style License Header
6243    cout("New style license code: " + cNewLicense, 0);
6244  }
6245  this.ROMImage = "";  //Memory consumption reduction.
6246}
6247GameBoyCore.prototype.disableBootROM = function () {
6248  //Remove any traces of the boot ROM from ROM memory.
6249  for (var index = 0; index < 0x100; ++index) {
6250    this.memory[index] = this.ROM[index];  //Replace the GameBoy or GameBoy Color boot ROM with the game ROM.
6251  }
6252  if (this.usedGBCBootROM) {
6253    //Remove any traces of the boot ROM from ROM memory.
6254    for (index = 0x200; index < 0x900; ++index) {
6255      this.memory[index] = this.ROM[index];  //Replace the GameBoy Color boot ROM with the game ROM.
6256    }
6257    if (!this.cGBC) {
6258      //Clean up the post-boot (GB mode only) state:
6259      this.GBCtoGBModeAdjust();
6260    }
6261    else {
6262      this.recompileBootIOWriteHandling();
6263    }
6264  }
6265  else {
6266    this.recompileBootIOWriteHandling();
6267  }
6268}
6269GameBoyCore.prototype.initializeTiming = function () {
6270  //Emulator Timing:
6271  this.baseCPUCyclesPerIteration = 0x80000 / 0x7D * settings[6];
6272  this.CPUCyclesTotalRoundoff = this.baseCPUCyclesPerIteration % 4;
6273  this.CPUCyclesTotalBase = this.CPUCyclesTotal = (this.baseCPUCyclesPerIteration - this.CPUCyclesTotalRoundoff) | 0;
6274  this.CPUCyclesTotalCurrent = 0;
6275}
6276GameBoyCore.prototype.setupRAM = function () {
6277  //Setup the auxilliary/switchable RAM:
6278  if (this.cMBC2) {
6279    this.numRAMBanks = 1 / 16;
6280  }
6281  else if (this.cMBC1 || this.cRUMBLE || this.cMBC3 || this.cHuC3) {
6282    this.numRAMBanks = 4;
6283  }
6284  else if (this.cMBC5) {
6285    this.numRAMBanks = 16;
6286  }
6287  else if (this.cSRAM) {
6288    this.numRAMBanks = 1;
6289  }
6290  if (this.numRAMBanks > 0) {
6291    if (!this.MBCRAMUtilized()) {
6292      //For ROM and unknown MBC cartridges using the external RAM:
6293      this.MBCRAMBanksEnabled = true;
6294    }
6295    //Switched RAM Used
6296    var MBCRam = (typeof this.openMBC == "function") ? this.openMBC(this.name) : [];
6297    if (MBCRam.length > 0) {
6298      //Flash the SRAM into memory:
6299      this.MBCRam = this.toTypedArray(MBCRam, "uint8");
6300    }
6301    else {
6302      this.MBCRam = this.getTypedArray(this.numRAMBanks * 0x2000, 0, "uint8");
6303    }
6304  }
6305  cout("Actual bytes of MBC RAM allocated: " + (this.numRAMBanks * 0x2000), 0);
6306  this.returnFromRTCState();
6307  //Setup the RAM for GBC mode.
6308  if (this.cGBC) {
6309    this.VRAM = this.getTypedArray(0x2000, 0, "uint8");
6310    this.GBCMemory = this.getTypedArray(0x7000, 0, "uint8");
6311  }
6312  this.memoryReadJumpCompile();
6313  this.memoryWriteJumpCompile();
6314}
6315GameBoyCore.prototype.MBCRAMUtilized = function () {
6316  return this.cMBC1 || this.cMBC2 || this.cMBC3 || this.cMBC5 || this.cMBC7 || this.cRUMBLE;
6317}
6318GameBoyCore.prototype.recomputeDimension = function () {
6319  initNewCanvas();
6320  //Cache some dimension info:
6321  this.onscreenWidth = this.canvas.width;
6322  this.onscreenHeight = this.canvas.height;
6323  // The following line was modified for benchmarking:
6324  if (GameBoyWindow && GameBoyWindow.mozRequestAnimationFrame) {
6325    //Firefox slowness hack:
6326    this.canvas.width = this.onscreenWidth = (!settings[12]) ? 160 : this.canvas.width;
6327    this.canvas.height = this.onscreenHeight = (!settings[12]) ? 144 : this.canvas.height;
6328  }
6329  else {
6330    this.onscreenWidth = this.canvas.width;
6331    this.onscreenHeight = this.canvas.height;
6332  }
6333  this.offscreenWidth = (!settings[12]) ? 160 : this.canvas.width;
6334  this.offscreenHeight = (!settings[12]) ? 144 : this.canvas.height;
6335  this.offscreenRGBCount = this.offscreenWidth * this.offscreenHeight * 4;
6336}
6337GameBoyCore.prototype.initLCD = function () {
6338  this.recomputeDimension();
6339  if (this.offscreenRGBCount != 92160) {
6340    //Only create the resizer handle if we need it:
6341    this.compileResizeFrameBufferFunction();
6342  }
6343  else {
6344    //Resizer not needed:
6345    this.resizer = null;
6346  }
6347  try {
6348    this.canvasOffscreen = new GameBoyCanvas();  // Line modified for benchmarking.
6349    this.canvasOffscreen.width = this.offscreenWidth;
6350    this.canvasOffscreen.height = this.offscreenHeight;
6351    this.drawContextOffscreen = this.canvasOffscreen.getContext("2d");
6352    this.drawContextOnscreen = this.canvas.getContext("2d");
6353    //Get a CanvasPixelArray buffer:
6354    try {
6355      this.canvasBuffer = this.drawContextOffscreen.createImageData(this.offscreenWidth, this.offscreenHeight);
6356    }
6357    catch (error) {
6358      cout("Falling back to the getImageData initialization (Error \"" + error.message + "\").", 1);
6359      this.canvasBuffer = this.drawContextOffscreen.getImageData(0, 0, this.offscreenWidth, this.offscreenHeight);
6360    }
6361    var index = this.offscreenRGBCount;
6362    while (index > 0) {
6363      this.canvasBuffer.data[index -= 4] = 0xF8;
6364      this.canvasBuffer.data[index + 1] = 0xF8;
6365      this.canvasBuffer.data[index + 2] = 0xF8;
6366      this.canvasBuffer.data[index + 3] = 0xFF;
6367    }
6368    this.graphicsBlit();
6369    this.canvas.style.visibility = "visible";
6370    if (this.swizzledFrame == null) {
6371      this.swizzledFrame = this.getTypedArray(69120, 0xFF, "uint8");
6372    }
6373    //Test the draw system and browser vblank latching:
6374    this.drewFrame = true;                    //Copy the latest graphics to buffer.
6375    this.requestDraw();
6376  }
6377  catch (error) {
6378    throw(new Error("HTML5 Canvas support required: " + error.message + "file: " + error.fileName + ", line: " + error.lineNumber));
6379  }
6380}
6381GameBoyCore.prototype.graphicsBlit = function () {
6382  if (this.offscreenWidth == this.onscreenWidth && this.offscreenHeight == this.onscreenHeight) {
6383    this.drawContextOnscreen.putImageData(this.canvasBuffer, 0, 0);
6384  }
6385  else {
6386    this.drawContextOffscreen.putImageData(this.canvasBuffer, 0, 0);
6387    this.drawContextOnscreen.drawImage(this.canvasOffscreen, 0, 0, this.onscreenWidth, this.onscreenHeight);
6388  }
6389}
6390GameBoyCore.prototype.JoyPadEvent = function (key, down) {
6391  if (down) {
6392    this.JoyPad &= 0xFF ^ (1 << key);
6393    if (!this.cGBC && (!this.usedBootROM || !this.usedGBCBootROM)) {
6394      this.interruptsRequested |= 0x10;  //A real GBC doesn't set this!
6395      this.remainingClocks = 0;
6396      this.checkIRQMatching();
6397    }
6398  }
6399  else {
6400    this.JoyPad |= (1 << key);
6401  }
6402  this.memory[0xFF00] = (this.memory[0xFF00] & 0x30) + ((((this.memory[0xFF00] & 0x20) == 0) ? (this.JoyPad >> 4) : 0xF) & (((this.memory[0xFF00] & 0x10) == 0) ? (this.JoyPad & 0xF) : 0xF));
6403  this.CPUStopped = false;
6404}
6405GameBoyCore.prototype.GyroEvent = function (x, y) {
6406  x *= -100;
6407  x += 2047;
6408  this.highX = x >> 8;
6409  this.lowX = x & 0xFF;
6410  y *= -100;
6411  y += 2047;
6412  this.highY = y >> 8;
6413  this.lowY = y & 0xFF;
6414}
6415GameBoyCore.prototype.initSound = function () {
6416  this.sampleSize = 0x400000 / 1000 * settings[6];
6417  this.machineOut = settings[13];
6418  if (settings[0]) {
6419    try {
6420      var parentObj = this;
6421      this.audioHandle = new XAudioServer(2, 0x400000 / settings[13], 0, Math.max(this.sampleSize * settings[8] / settings[13], 8192) << 1, null, settings[14]);
6422      this.initAudioBuffer();
6423    }
6424    catch (error) {
6425      cout("Audio system cannot run: " + error.message, 2);
6426      settings[0] = false;
6427    }
6428  }
6429  else if (this.audioHandle) {
6430    //Mute the audio output, as it has an immediate silencing effect:
6431    try {
6432      this.audioHandle.changeVolume(0);
6433    }
6434    catch (error) { }
6435  }
6436}
6437GameBoyCore.prototype.changeVolume = function () {
6438  if (settings[0] && this.audioHandle) {
6439    try {
6440      this.audioHandle.changeVolume(settings[14]);
6441    }
6442    catch (error) { }
6443  }
6444}
6445GameBoyCore.prototype.initAudioBuffer = function () {
6446  this.audioIndex = 0;
6447  this.bufferContainAmount = Math.max(this.sampleSize * settings[7] / settings[13], 4096) << 1;
6448  this.numSamplesTotal = (this.sampleSize - (this.sampleSize % settings[13])) | 0;
6449  this.currentBuffer = this.getTypedArray(this.numSamplesTotal, 0xF0F0, "int32");
6450  this.secondaryBuffer = this.getTypedArray((this.numSamplesTotal << 1) / settings[13], 0, "float32");
6451}
6452GameBoyCore.prototype.intializeWhiteNoise = function () {
6453  //Noise Sample Tables:
6454  var randomFactor = 1;
6455  //15-bit LSFR Cache Generation:
6456  this.LSFR15Table = this.getTypedArray(0x80000, 0, "int8");
6457  var LSFR = 0x7FFF;  //Seed value has all its bits set.
6458  var LSFRShifted = 0x3FFF;
6459  for (var index = 0; index < 0x8000; ++index) {
6460    //Normalize the last LSFR value for usage:
6461    randomFactor = 1 - (LSFR & 1);  //Docs say it's the inverse.
6462    //Cache the different volume level results:
6463    this.LSFR15Table[0x08000 | index] = randomFactor;
6464    this.LSFR15Table[0x10000 | index] = randomFactor * 0x2;
6465    this.LSFR15Table[0x18000 | index] = randomFactor * 0x3;
6466    this.LSFR15Table[0x20000 | index] = randomFactor * 0x4;
6467    this.LSFR15Table[0x28000 | index] = randomFactor * 0x5;
6468    this.LSFR15Table[0x30000 | index] = randomFactor * 0x6;
6469    this.LSFR15Table[0x38000 | index] = randomFactor * 0x7;
6470    this.LSFR15Table[0x40000 | index] = randomFactor * 0x8;
6471    this.LSFR15Table[0x48000 | index] = randomFactor * 0x9;
6472    this.LSFR15Table[0x50000 | index] = randomFactor * 0xA;
6473    this.LSFR15Table[0x58000 | index] = randomFactor * 0xB;
6474    this.LSFR15Table[0x60000 | index] = randomFactor * 0xC;
6475    this.LSFR15Table[0x68000 | index] = randomFactor * 0xD;
6476    this.LSFR15Table[0x70000 | index] = randomFactor * 0xE;
6477    this.LSFR15Table[0x78000 | index] = randomFactor * 0xF;
6478    //Recompute the LSFR algorithm:
6479    LSFRShifted = LSFR >> 1;
6480    LSFR = LSFRShifted | (((LSFRShifted ^ LSFR) & 0x1) << 14);
6481  }
6482  //7-bit LSFR Cache Generation:
6483  this.LSFR7Table = this.getTypedArray(0x800, 0, "int8");
6484  LSFR = 0x7F;  //Seed value has all its bits set.
6485  for (index = 0; index < 0x80; ++index) {
6486    //Normalize the last LSFR value for usage:
6487    randomFactor = 1 - (LSFR & 1);  //Docs say it's the inverse.
6488    //Cache the different volume level results:
6489    this.LSFR7Table[0x080 | index] = randomFactor;
6490    this.LSFR7Table[0x100 | index] = randomFactor * 0x2;
6491    this.LSFR7Table[0x180 | index] = randomFactor * 0x3;
6492    this.LSFR7Table[0x200 | index] = randomFactor * 0x4;
6493    this.LSFR7Table[0x280 | index] = randomFactor * 0x5;
6494    this.LSFR7Table[0x300 | index] = randomFactor * 0x6;
6495    this.LSFR7Table[0x380 | index] = randomFactor * 0x7;
6496    this.LSFR7Table[0x400 | index] = randomFactor * 0x8;
6497    this.LSFR7Table[0x480 | index] = randomFactor * 0x9;
6498    this.LSFR7Table[0x500 | index] = randomFactor * 0xA;
6499    this.LSFR7Table[0x580 | index] = randomFactor * 0xB;
6500    this.LSFR7Table[0x600 | index] = randomFactor * 0xC;
6501    this.LSFR7Table[0x680 | index] = randomFactor * 0xD;
6502    this.LSFR7Table[0x700 | index] = randomFactor * 0xE;
6503    this.LSFR7Table[0x780 | index] = randomFactor * 0xF;
6504    //Recompute the LSFR algorithm:
6505    LSFRShifted = LSFR >> 1;
6506    LSFR = LSFRShifted | (((LSFRShifted ^ LSFR) & 0x1) << 6);
6507  }
6508  if (!this.noiseSampleTable && this.memory.length == 0x10000) {
6509    //If enabling audio for the first time after a game is already running, set up the internal table reference:
6510    this.noiseSampleTable = ((this.memory[0xFF22] & 0x8) == 0x8) ? this.LSFR7Table : this.LSFR15Table;
6511  }
6512}
6513GameBoyCore.prototype.audioUnderrunAdjustment = function () {
6514  if (settings[0]) {
6515    var underrunAmount = this.bufferContainAmount - this.audioHandle.remainingBuffer();
6516    if (underrunAmount > 0) {
6517      this.CPUCyclesTotalCurrent += (underrunAmount >> 1) * this.machineOut;
6518      this.recalculateIterationClockLimit();
6519    }
6520  }
6521}
6522GameBoyCore.prototype.initializeAudioStartState = function () {
6523  this.channel1FrequencyTracker = 0x2000;
6524  this.channel1DutyTracker = 0;
6525  this.channel1CachedDuty = this.dutyLookup[2];
6526  this.channel1totalLength = 0;
6527  this.channel1envelopeVolume = 0;
6528  this.channel1envelopeType = false;
6529  this.channel1envelopeSweeps = 0;
6530  this.channel1envelopeSweepsLast = 0;
6531  this.channel1consecutive = true;
6532  this.channel1frequency = 0;
6533  this.channel1SweepFault = false;
6534  this.channel1ShadowFrequency = 0;
6535  this.channel1timeSweep = 1;
6536  this.channel1lastTimeSweep = 0;
6537  this.channel1numSweep = 0;
6538  this.channel1frequencySweepDivider = 0;
6539  this.channel1decreaseSweep = false;
6540  this.channel2FrequencyTracker = 0x2000;
6541  this.channel2DutyTracker = 0;
6542  this.channel2CachedDuty = this.dutyLookup[2];
6543  this.channel2totalLength = 0;
6544  this.channel2envelopeVolume = 0;
6545  this.channel2envelopeType = false;
6546  this.channel2envelopeSweeps = 0;
6547  this.channel2envelopeSweepsLast = 0;
6548  this.channel2consecutive = true;
6549  this.channel2frequency = 0;
6550  this.channel3canPlay = false;
6551  this.channel3totalLength = 0;
6552  this.channel3patternType = 4;
6553  this.channel3frequency = 0;
6554  this.channel3consecutive = true;
6555  this.channel3Counter = 0x800;
6556  this.channel4FrequencyPeriod = 8;
6557  this.channel4totalLength = 0;
6558  this.channel4envelopeVolume = 0;
6559  this.channel4currentVolume = 0;
6560  this.channel4envelopeType = false;
6561  this.channel4envelopeSweeps = 0;
6562  this.channel4envelopeSweepsLast = 0;
6563  this.channel4consecutive = true;
6564  this.channel4BitRange = 0x7FFF;
6565  this.noiseSampleTable = this.LSFR15Table;
6566  this.channel4VolumeShifter = 15;
6567  this.channel1FrequencyCounter = 0x2000;
6568  this.channel2FrequencyCounter = 0x2000;
6569  this.channel3Counter = 0x800;
6570  this.channel3FrequencyPeriod = 0x800;
6571  this.channel3lastSampleLookup = 0;
6572  this.channel4lastSampleLookup = 0;
6573  this.VinLeftChannelMasterVolume = 8;
6574  this.VinRightChannelMasterVolume = 8;
6575  this.mixerOutputCache = 0;
6576  this.sequencerClocks = 0x2000;
6577  this.sequencePosition = 0;
6578  this.channel4FrequencyPeriod = 8;
6579  this.channel4Counter = 8;
6580  this.cachedChannel3Sample = 0;
6581  this.cachedChannel4Sample = 0;
6582  this.channel1Enabled = false;
6583  this.channel2Enabled = false;
6584  this.channel3Enabled = false;
6585  this.channel4Enabled = false;
6586  this.channel1canPlay = false;
6587  this.channel2canPlay = false;
6588  this.channel4canPlay = false;
6589  this.channel1OutputLevelCache();
6590  this.channel2OutputLevelCache();
6591  this.channel3OutputLevelCache();
6592  this.channel4OutputLevelCache();
6593}
6594GameBoyCore.prototype.outputAudio = function () {
6595  var sampleFactor = 0;
6596  var dirtySample = 0;
6597  var averageL = 0;
6598  var averageR = 0;
6599  var destinationPosition = 0;
6600  var divisor1 = settings[13];
6601  var divisor2 = divisor1 * 0xF0;
6602  for (var sourcePosition = 0; sourcePosition < this.numSamplesTotal;) {
6603    for (sampleFactor = averageL = averageR = 0; sampleFactor < divisor1; ++sampleFactor) {
6604      dirtySample = this.currentBuffer[sourcePosition++];
6605      averageL += dirtySample >> 9;
6606      averageR += dirtySample & 0x1FF;
6607    }
6608    this.secondaryBuffer[destinationPosition++] = averageL / divisor2 - 1;
6609    this.secondaryBuffer[destinationPosition++] = averageR / divisor2 - 1;
6610  }
6611  this.audioHandle.writeAudioNoCallback(this.secondaryBuffer);
6612}
6613//Below are the audio generation functions timed against the CPU:
6614GameBoyCore.prototype.generateAudio = function (numSamples) {
6615  if (this.soundMasterEnabled && !this.CPUStopped) {
6616    for (var samplesToGenerate = 0; numSamples > 0;) {
6617      samplesToGenerate = (numSamples < this.sequencerClocks) ? numSamples : this.sequencerClocks;
6618      this.sequencerClocks -= samplesToGenerate;
6619      numSamples -= samplesToGenerate;
6620      while (--samplesToGenerate > -1) {
6621        this.computeAudioChannels();
6622        this.currentBuffer[this.audioIndex++] = this.mixerOutputCache;
6623        if (this.audioIndex == this.numSamplesTotal) {
6624          this.audioIndex = 0;
6625          this.outputAudio();
6626        }
6627      }
6628      if (this.sequencerClocks == 0) {
6629        this.audioComputeSequencer();
6630        this.sequencerClocks = 0x2000;
6631      }
6632    }
6633  }
6634  else {
6635    //SILENT OUTPUT:
6636    while (--numSamples > -1) {
6637      this.currentBuffer[this.audioIndex++] = 0xF0F0;
6638      if (this.audioIndex == this.numSamplesTotal) {
6639        this.audioIndex = 0;
6640        this.outputAudio();
6641      }
6642    }
6643  }
6644}
6645//Generate audio, but don't actually output it (Used for when sound is disabled by user/browser):
6646GameBoyCore.prototype.generateAudioFake = function (numSamples) {
6647  if (this.soundMasterEnabled && !this.CPUStopped) {
6648    while (--numSamples > -1) {
6649      this.computeAudioChannels();
6650      if (--this.sequencerClocks == 0) {
6651        this.audioComputeSequencer();
6652        this.sequencerClocks = 0x2000;
6653      }
6654    }
6655  }
6656}
6657GameBoyCore.prototype.audioJIT = function () {
6658  //Audio Sample Generation Timing:
6659  if (settings[0]) {
6660    this.generateAudio(this.audioTicks);
6661  }
6662  else {
6663    this.generateAudioFake(this.audioTicks);
6664  }
6665  this.audioTicks = 0;
6666}
6667GameBoyCore.prototype.audioComputeSequencer = function () {
6668  switch (this.sequencePosition++) {
6669    case 0:
6670      this.clockAudioLength();
6671      break;
6672    case 2:
6673      this.clockAudioLength();
6674      this.clockAudioSweep();
6675      break;
6676    case 4:
6677      this.clockAudioLength();
6678      break;
6679    case 6:
6680      this.clockAudioLength();
6681      this.clockAudioSweep();
6682      break;
6683    case 7:
6684      this.clockAudioEnvelope();
6685      this.sequencePosition = 0;
6686  }
6687}
6688GameBoyCore.prototype.clockAudioLength = function () {
6689  //Channel 1:
6690  if (this.channel1totalLength > 1) {
6691    --this.channel1totalLength;
6692  }
6693  else if (this.channel1totalLength == 1) {
6694    this.channel1totalLength = 0;
6695    this.channel1EnableCheck();
6696    this.memory[0xFF26] &= 0xFE;  //Channel #1 On Flag Off
6697  }
6698  //Channel 2:
6699  if (this.channel2totalLength > 1) {
6700    --this.channel2totalLength;
6701  }
6702  else if (this.channel2totalLength == 1) {
6703    this.channel2totalLength = 0;
6704    this.channel2EnableCheck();
6705    this.memory[0xFF26] &= 0xFD;  //Channel #2 On Flag Off
6706  }
6707  //Channel 3:
6708  if (this.channel3totalLength > 1) {
6709    --this.channel3totalLength;
6710  }
6711  else if (this.channel3totalLength == 1) {
6712    this.channel3totalLength = 0;
6713    this.channel3EnableCheck();
6714    this.memory[0xFF26] &= 0xFB;  //Channel #3 On Flag Off
6715  }
6716  //Channel 4:
6717  if (this.channel4totalLength > 1) {
6718    --this.channel4totalLength;
6719  }
6720  else if (this.channel4totalLength == 1) {
6721    this.channel4totalLength = 0;
6722    this.channel4EnableCheck();
6723    this.memory[0xFF26] &= 0xF7;  //Channel #4 On Flag Off
6724  }
6725}
6726GameBoyCore.prototype.clockAudioSweep = function () {
6727  //Channel 1:
6728  if (!this.channel1SweepFault && this.channel1timeSweep > 0) {
6729    if (--this.channel1timeSweep == 0) {
6730      this.runAudioSweep();
6731    }
6732  }
6733}
6734GameBoyCore.prototype.runAudioSweep = function () {
6735  //Channel 1:
6736  if (this.channel1lastTimeSweep > 0) {
6737    if (this.channel1frequencySweepDivider > 0) {
6738      if (this.channel1numSweep > 0) {
6739        --this.channel1numSweep;
6740        if (this.channel1decreaseSweep) {
6741          this.channel1ShadowFrequency -= this.channel1ShadowFrequency >> this.channel1frequencySweepDivider;
6742          this.channel1frequency = this.channel1ShadowFrequency & 0x7FF;
6743          this.channel1FrequencyTracker = (0x800 - this.channel1frequency) << 2;
6744        }
6745        else {
6746          this.channel1ShadowFrequency += this.channel1ShadowFrequency >> this.channel1frequencySweepDivider;
6747          this.channel1frequency = this.channel1ShadowFrequency;
6748          if (this.channel1ShadowFrequency <= 0x7FF) {
6749            this.channel1FrequencyTracker = (0x800 - this.channel1frequency) << 2;
6750            //Run overflow check twice:
6751            if ((this.channel1ShadowFrequency + (this.channel1ShadowFrequency >> this.channel1frequencySweepDivider)) > 0x7FF) {
6752              this.channel1SweepFault = true;
6753              this.channel1EnableCheck();
6754              this.memory[0xFF26] &= 0xFE;  //Channel #1 On Flag Off
6755            }
6756          }
6757          else {
6758            this.channel1frequency &= 0x7FF;
6759            this.channel1SweepFault = true;
6760            this.channel1EnableCheck();
6761            this.memory[0xFF26] &= 0xFE;  //Channel #1 On Flag Off
6762          }
6763        }
6764      }
6765      this.channel1timeSweep = this.channel1lastTimeSweep;
6766    }
6767    else {
6768      //Channel has sweep disabled and timer becomes a length counter:
6769      this.channel1SweepFault = true;
6770      this.channel1EnableCheck();
6771    }
6772  }
6773}
6774GameBoyCore.prototype.clockAudioEnvelope = function () {
6775  //Channel 1:
6776  if (this.channel1envelopeSweepsLast > -1) {
6777    if (this.channel1envelopeSweeps > 0) {
6778      --this.channel1envelopeSweeps;
6779    }
6780    else {
6781      if (!this.channel1envelopeType) {
6782        if (this.channel1envelopeVolume > 0) {
6783          --this.channel1envelopeVolume;
6784          this.channel1envelopeSweeps = this.channel1envelopeSweepsLast;
6785          this.channel1OutputLevelCache();
6786        }
6787        else {
6788          this.channel1envelopeSweepsLast = -1;
6789        }
6790      }
6791      else if (this.channel1envelopeVolume < 0xF) {
6792        ++this.channel1envelopeVolume;
6793        this.channel1envelopeSweeps = this.channel1envelopeSweepsLast;
6794        this.channel1OutputLevelCache();
6795      }
6796      else {
6797        this.channel1envelopeSweepsLast = -1;
6798      }
6799    }
6800  }
6801  //Channel 2:
6802  if (this.channel2envelopeSweepsLast > -1) {
6803    if (this.channel2envelopeSweeps > 0) {
6804      --this.channel2envelopeSweeps;
6805    }
6806    else {
6807      if (!this.channel2envelopeType) {
6808        if (this.channel2envelopeVolume > 0) {
6809          --this.channel2envelopeVolume;
6810          this.channel2envelopeSweeps = this.channel2envelopeSweepsLast;
6811          this.channel2OutputLevelCache();
6812        }
6813        else {
6814          this.channel2envelopeSweepsLast = -1;
6815        }
6816      }
6817      else if (this.channel2envelopeVolume < 0xF) {
6818        ++this.channel2envelopeVolume;
6819        this.channel2envelopeSweeps = this.channel2envelopeSweepsLast;
6820        this.channel2OutputLevelCache();
6821      }
6822      else {
6823        this.channel2envelopeSweepsLast = -1;
6824      }
6825    }
6826  }
6827  //Channel 4:
6828  if (this.channel4envelopeSweepsLast > -1) {
6829    if (this.channel4envelopeSweeps > 0) {
6830      --this.channel4envelopeSweeps;
6831    }
6832    else {
6833      if (!this.channel4envelopeType) {
6834        if (this.channel4envelopeVolume > 0) {
6835          this.channel4currentVolume = --this.channel4envelopeVolume << this.channel4VolumeShifter;
6836          this.channel4envelopeSweeps = this.channel4envelopeSweepsLast;
6837          this.channel4UpdateCache();
6838        }
6839        else {
6840          this.channel4envelopeSweepsLast = -1;
6841        }
6842      }
6843      else if (this.channel4envelopeVolume < 0xF) {
6844        this.channel4currentVolume = ++this.channel4envelopeVolume << this.channel4VolumeShifter;
6845        this.channel4envelopeSweeps = this.channel4envelopeSweepsLast;
6846        this.channel4UpdateCache();
6847      }
6848      else {
6849        this.channel4envelopeSweepsLast = -1;
6850      }
6851    }
6852  }
6853}
6854GameBoyCore.prototype.computeAudioChannels = function () {
6855  //Channel 1 counter:
6856  if (--this.channel1FrequencyCounter == 0) {
6857    this.channel1FrequencyCounter = this.channel1FrequencyTracker;
6858    this.channel1DutyTracker = (this.channel1DutyTracker + 1) & 0x7;
6859    this.channel1OutputLevelTrimaryCache();
6860  }
6861  //Channel 2 counter:
6862  if (--this.channel2FrequencyCounter == 0) {
6863    this.channel2FrequencyCounter = this.channel2FrequencyTracker;
6864    this.channel2DutyTracker = (this.channel2DutyTracker + 1) & 0x7;
6865    this.channel2OutputLevelTrimaryCache();
6866  }
6867  //Channel 3 counter:
6868  if (--this.channel3Counter == 0) {
6869    if (this.channel3canPlay) {
6870      this.channel3lastSampleLookup = (this.channel3lastSampleLookup + 1) & 0x1F;
6871    }
6872    this.channel3Counter = this.channel3FrequencyPeriod;
6873    this.channel3UpdateCache();
6874  }
6875  //Channel 4 counter:
6876  if (--this.channel4Counter == 0) {
6877    this.channel4lastSampleLookup = (this.channel4lastSampleLookup + 1) & this.channel4BitRange;
6878    this.channel4Counter = this.channel4FrequencyPeriod;
6879    this.channel4UpdateCache();
6880  }
6881}
6882GameBoyCore.prototype.channel1EnableCheck = function () {
6883  this.channel1Enabled = ((this.channel1consecutive || this.channel1totalLength > 0) && !this.channel1SweepFault && this.channel1canPlay);
6884  this.channel1OutputLevelSecondaryCache();
6885}
6886GameBoyCore.prototype.channel1VolumeEnableCheck = function () {
6887  this.channel1canPlay = (this.memory[0xFF12] > 7);
6888  this.channel1EnableCheck();
6889  this.channel1OutputLevelSecondaryCache();
6890}
6891GameBoyCore.prototype.channel1OutputLevelCache = function () {
6892  this.channel1currentSampleLeft = (this.leftChannel1) ? this.channel1envelopeVolume : 0;
6893  this.channel1currentSampleRight = (this.rightChannel1) ? this.channel1envelopeVolume : 0;
6894  this.channel1OutputLevelSecondaryCache();
6895}
6896GameBoyCore.prototype.channel1OutputLevelSecondaryCache = function () {
6897  if (this.channel1Enabled) {
6898    this.channel1currentSampleLeftSecondary = this.channel1currentSampleLeft;
6899    this.channel1currentSampleRightSecondary = this.channel1currentSampleRight;
6900  }
6901  else {
6902    this.channel1currentSampleLeftSecondary = 0;
6903    this.channel1currentSampleRightSecondary = 0;
6904  }
6905  this.channel1OutputLevelTrimaryCache();
6906}
6907GameBoyCore.prototype.channel1OutputLevelTrimaryCache = function () {
6908  if (this.channel1CachedDuty[this.channel1DutyTracker]) {
6909    this.channel1currentSampleLeftTrimary = this.channel1currentSampleLeftSecondary;
6910    this.channel1currentSampleRightTrimary = this.channel1currentSampleRightSecondary;
6911  }
6912  else {
6913    this.channel1currentSampleLeftTrimary = 0;
6914    this.channel1currentSampleRightTrimary = 0;
6915  }
6916  this.mixerOutputLevelCache();
6917}
6918GameBoyCore.prototype.channel2EnableCheck = function () {
6919  this.channel2Enabled = ((this.channel2consecutive || this.channel2totalLength > 0) && this.channel2canPlay);
6920  this.channel2OutputLevelSecondaryCache();
6921}
6922GameBoyCore.prototype.channel2VolumeEnableCheck = function () {
6923  this.channel2canPlay = (this.memory[0xFF17] > 7);
6924  this.channel2EnableCheck();
6925  this.channel2OutputLevelSecondaryCache();
6926}
6927GameBoyCore.prototype.channel2OutputLevelCache = function () {
6928  this.channel2currentSampleLeft = (this.leftChannel2) ? this.channel2envelopeVolume : 0;
6929  this.channel2currentSampleRight = (this.rightChannel2) ? this.channel2envelopeVolume : 0;
6930  this.channel2OutputLevelSecondaryCache();
6931}
6932GameBoyCore.prototype.channel2OutputLevelSecondaryCache = function () {
6933  if (this.channel2Enabled) {
6934    this.channel2currentSampleLeftSecondary = this.channel2currentSampleLeft;
6935    this.channel2currentSampleRightSecondary = this.channel2currentSampleRight;
6936  }
6937  else {
6938    this.channel2currentSampleLeftSecondary = 0;
6939    this.channel2currentSampleRightSecondary = 0;
6940  }
6941  this.channel2OutputLevelTrimaryCache();
6942}
6943GameBoyCore.prototype.channel2OutputLevelTrimaryCache = function () {
6944  if (this.channel2CachedDuty[this.channel2DutyTracker]) {
6945    this.channel2currentSampleLeftTrimary = this.channel2currentSampleLeftSecondary;
6946    this.channel2currentSampleRightTrimary = this.channel2currentSampleRightSecondary;
6947  }
6948  else {
6949    this.channel2currentSampleLeftTrimary = 0;
6950    this.channel2currentSampleRightTrimary = 0;
6951  }
6952  this.mixerOutputLevelCache();
6953}
6954GameBoyCore.prototype.channel3EnableCheck = function () {
6955  this.channel3Enabled = (/*this.channel3canPlay && */(this.channel3consecutive || this.channel3totalLength > 0));
6956  this.channel3OutputLevelSecondaryCache();
6957}
6958GameBoyCore.prototype.channel3OutputLevelCache = function () {
6959  this.channel3currentSampleLeft = (this.leftChannel3) ? this.cachedChannel3Sample : 0;
6960  this.channel3currentSampleRight = (this.rightChannel3) ? this.cachedChannel3Sample : 0;
6961  this.channel3OutputLevelSecondaryCache();
6962}
6963GameBoyCore.prototype.channel3OutputLevelSecondaryCache = function () {
6964  if (this.channel3Enabled) {
6965    this.channel3currentSampleLeftSecondary = this.channel3currentSampleLeft;
6966    this.channel3currentSampleRightSecondary = this.channel3currentSampleRight;
6967  }
6968  else {
6969    this.channel3currentSampleLeftSecondary = 0;
6970    this.channel3currentSampleRightSecondary = 0;
6971  }
6972  this.mixerOutputLevelCache();
6973}
6974GameBoyCore.prototype.channel4EnableCheck = function () {
6975  this.channel4Enabled = ((this.channel4consecutive || this.channel4totalLength > 0) && this.channel4canPlay);
6976  this.channel4OutputLevelSecondaryCache();
6977}
6978GameBoyCore.prototype.channel4VolumeEnableCheck = function () {
6979  this.channel4canPlay = (this.memory[0xFF21] > 7);
6980  this.channel4EnableCheck();
6981  this.channel4OutputLevelSecondaryCache();
6982}
6983GameBoyCore.prototype.channel4OutputLevelCache = function () {
6984  this.channel4currentSampleLeft = (this.leftChannel4) ? this.cachedChannel4Sample : 0;
6985  this.channel4currentSampleRight = (this.rightChannel4) ? this.cachedChannel4Sample : 0;
6986  this.channel4OutputLevelSecondaryCache();
6987}
6988GameBoyCore.prototype.channel4OutputLevelSecondaryCache = function () {
6989  if (this.channel4Enabled) {
6990    this.channel4currentSampleLeftSecondary = this.channel4currentSampleLeft;
6991    this.channel4currentSampleRightSecondary = this.channel4currentSampleRight;
6992  }
6993  else {
6994    this.channel4currentSampleLeftSecondary = 0;
6995    this.channel4currentSampleRightSecondary = 0;
6996  }
6997  this.mixerOutputLevelCache();
6998}
6999GameBoyCore.prototype.mixerOutputLevelCache = function () {
7000  this.mixerOutputCache = ((((this.channel1currentSampleLeftTrimary + this.channel2currentSampleLeftTrimary + this.channel3currentSampleLeftSecondary + this.channel4currentSampleLeftSecondary) * this.VinLeftChannelMasterVolume) << 9) +
7001  ((this.channel1currentSampleRightTrimary + this.channel2currentSampleRightTrimary + this.channel3currentSampleRightSecondary + this.channel4currentSampleRightSecondary) * this.VinRightChannelMasterVolume));
7002}
7003GameBoyCore.prototype.channel3UpdateCache = function () {
7004  this.cachedChannel3Sample = this.channel3PCM[this.channel3lastSampleLookup] >> this.channel3patternType;
7005  this.channel3OutputLevelCache();
7006}
7007GameBoyCore.prototype.channel3WriteRAM = function (address, data) {
7008  if (this.channel3canPlay) {
7009    this.audioJIT();
7010    //address = this.channel3lastSampleLookup >> 1;
7011  }
7012  this.memory[0xFF30 | address] = data;
7013  address <<= 1;
7014  this.channel3PCM[address] = data >> 4;
7015  this.channel3PCM[address | 1] = data & 0xF;
7016}
7017GameBoyCore.prototype.channel4UpdateCache = function () {
7018  this.cachedChannel4Sample = this.noiseSampleTable[this.channel4currentVolume | this.channel4lastSampleLookup];
7019  this.channel4OutputLevelCache();
7020}
7021GameBoyCore.prototype.run = function () {
7022  //The preprocessing before the actual iteration loop:
7023  if ((this.stopEmulator & 2) == 0) {
7024    if ((this.stopEmulator & 1) == 1) {
7025      if (!this.CPUStopped) {
7026        this.stopEmulator = 0;
7027        this.drewFrame = false;
7028        this.audioUnderrunAdjustment();
7029        this.clockUpdate();      //RTC clocking.
7030        if (!this.halt) {
7031          this.executeIteration();
7032        }
7033        else {            //Finish the HALT rundown execution.
7034          this.CPUTicks = 0;
7035          this.calculateHALTPeriod();
7036          if (this.halt) {
7037            this.updateCoreFull();
7038          }
7039          else {
7040            this.executeIteration();
7041          }
7042        }
7043        //Request the graphics target to be updated:
7044        this.requestDraw();
7045      }
7046      else {
7047        this.audioUnderrunAdjustment();
7048        this.audioTicks += this.CPUCyclesTotal;
7049        this.audioJIT();
7050        this.stopEmulator |= 1;      //End current loop.
7051      }
7052    }
7053    else {    //We can only get here if there was an internal error, but the loop was restarted.
7054      cout("Iterator restarted a faulted core.", 2);
7055      pause();
7056    }
7057  }
7058}
7059
7060GameBoyCore.prototype.executeIteration = function () {
7061  //Iterate the interpreter loop:
7062  var opcodeToExecute = 0;
7063  var timedTicks = 0;
7064  while (this.stopEmulator == 0) {
7065    //Interrupt Arming:
7066    switch (this.IRQEnableDelay) {
7067      case 1:
7068        this.IME = true;
7069        this.checkIRQMatching();
7070      case 2:
7071        --this.IRQEnableDelay;
7072    }
7073    //Is an IRQ set to fire?:
7074    if (this.IRQLineMatched > 0) {
7075      //IME is true and and interrupt was matched:
7076      this.launchIRQ();
7077    }
7078    //Fetch the current opcode:
7079    opcodeToExecute = this.memoryReader[this.programCounter](this, this.programCounter);
7080    //Increment the program counter to the next instruction:
7081    this.programCounter = (this.programCounter + 1) & 0xFFFF;
7082    //Check for the program counter quirk:
7083    if (this.skipPCIncrement) {
7084      this.programCounter = (this.programCounter - 1) & 0xFFFF;
7085      this.skipPCIncrement = false;
7086    }
7087    //Get how many CPU cycles the current instruction counts for:
7088    this.CPUTicks = this.TICKTable[opcodeToExecute];
7089    //Execute the current instruction:
7090    this.OPCODE[opcodeToExecute](this);
7091    //Update the state (Inlined updateCoreFull manually here):
7092    //Update the clocking for the LCD emulation:
7093    this.LCDTicks += this.CPUTicks >> this.doubleSpeedShifter;  //LCD Timing
7094    this.LCDCONTROL[this.actualScanLine](this);          //Scan Line and STAT Mode Control
7095    //Single-speed relative timing for A/V emulation:
7096    timedTicks = this.CPUTicks >> this.doubleSpeedShifter;    //CPU clocking can be updated from the LCD handling.
7097    this.audioTicks += timedTicks;                //Audio Timing
7098    this.emulatorTicks += timedTicks;              //Emulator Timing
7099    //CPU Timers:
7100    this.DIVTicks += this.CPUTicks;                //DIV Timing
7101    if (this.TIMAEnabled) {                    //TIMA Timing
7102      this.timerTicks += this.CPUTicks;
7103      while (this.timerTicks >= this.TACClocker) {
7104        this.timerTicks -= this.TACClocker;
7105        if (++this.memory[0xFF05] == 0x100) {
7106          this.memory[0xFF05] = this.memory[0xFF06];
7107          this.interruptsRequested |= 0x4;
7108          this.checkIRQMatching();
7109        }
7110      }
7111    }
7112    if (this.serialTimer > 0) {                    //Serial Timing
7113      //IRQ Counter:
7114      this.serialTimer -= this.CPUTicks;
7115      if (this.serialTimer <= 0) {
7116        this.interruptsRequested |= 0x8;
7117        this.checkIRQMatching();
7118      }
7119      //Bit Shit Counter:
7120      this.serialShiftTimer -= this.CPUTicks;
7121      if (this.serialShiftTimer <= 0) {
7122        this.serialShiftTimer = this.serialShiftTimerAllocated;
7123        this.memory[0xFF01] = ((this.memory[0xFF01] << 1) & 0xFE) | 0x01;  //We could shift in actual link data here if we were to implement such!!!
7124      }
7125    }
7126    //End of iteration routine:
7127    if (this.emulatorTicks >= this.CPUCyclesTotal) {
7128      this.iterationEndRoutine();
7129    }
7130    // Start of code added for benchmarking:
7131    this.instructions += 1;
7132    if (this.instructions > this.totalInstructions) {
7133      this.iterationEndRoutine();
7134      this.stopEmulator |= 2;
7135      checkFinalState();
7136    }
7137    // End of code added for benchmarking.
7138  }
7139}
7140GameBoyCore.prototype.iterationEndRoutine = function () {
7141  if ((this.stopEmulator & 0x1) == 0) {
7142    this.audioJIT();  //Make sure we at least output once per iteration.
7143    //Update DIV Alignment (Integer overflow safety):
7144    this.memory[0xFF04] = (this.memory[0xFF04] + (this.DIVTicks >> 8)) & 0xFF;
7145    this.DIVTicks &= 0xFF;
7146    //Update emulator flags:
7147    this.stopEmulator |= 1;      //End current loop.
7148    this.emulatorTicks -= this.CPUCyclesTotal;
7149    this.CPUCyclesTotalCurrent += this.CPUCyclesTotalRoundoff;
7150    this.recalculateIterationClockLimit();
7151  }
7152}
7153GameBoyCore.prototype.handleSTOP = function () {
7154  this.CPUStopped = true;            //Stop CPU until joypad input changes.
7155  this.iterationEndRoutine();
7156  if (this.emulatorTicks < 0) {
7157    this.audioTicks -= this.emulatorTicks;
7158    this.audioJIT();
7159  }
7160}
7161GameBoyCore.prototype.recalculateIterationClockLimit = function () {
7162  var endModulus = this.CPUCyclesTotalCurrent % 4;
7163  this.CPUCyclesTotal = this.CPUCyclesTotalBase + this.CPUCyclesTotalCurrent - endModulus;
7164  this.CPUCyclesTotalCurrent = endModulus;
7165}
7166GameBoyCore.prototype.scanLineMode2 = function () {  //OAM Search Period
7167  if (this.STATTracker != 1) {
7168    if (this.mode2TriggerSTAT) {
7169      this.interruptsRequested |= 0x2;
7170      this.checkIRQMatching();
7171    }
7172    this.STATTracker = 1;
7173    this.modeSTAT = 2;
7174  }
7175}
7176GameBoyCore.prototype.scanLineMode3 = function () {  //Scan Line Drawing Period
7177  if (this.modeSTAT != 3) {
7178    if (this.STATTracker == 0 && this.mode2TriggerSTAT) {
7179      this.interruptsRequested |= 0x2;
7180      this.checkIRQMatching();
7181    }
7182    this.STATTracker = 1;
7183    this.modeSTAT = 3;
7184  }
7185}
7186GameBoyCore.prototype.scanLineMode0 = function () {  //Horizontal Blanking Period
7187  if (this.modeSTAT != 0) {
7188    if (this.STATTracker != 2) {
7189      if (this.STATTracker == 0) {
7190        if (this.mode2TriggerSTAT) {
7191          this.interruptsRequested |= 0x2;
7192          this.checkIRQMatching();
7193        }
7194        this.modeSTAT = 3;
7195      }
7196      this.incrementScanLineQueue();
7197      this.updateSpriteCount(this.actualScanLine);
7198      this.STATTracker = 2;
7199    }
7200    if (this.LCDTicks >= this.spriteCount) {
7201      if (this.hdmaRunning) {
7202        this.executeHDMA();
7203      }
7204      if (this.mode0TriggerSTAT) {
7205        this.interruptsRequested |= 0x2;
7206        this.checkIRQMatching();
7207      }
7208      this.STATTracker = 3;
7209      this.modeSTAT = 0;
7210    }
7211  }
7212}
7213GameBoyCore.prototype.clocksUntilLYCMatch = function () {
7214  if (this.memory[0xFF45] != 0) {
7215    if (this.memory[0xFF45] > this.actualScanLine) {
7216      return 456 * (this.memory[0xFF45] - this.actualScanLine);
7217    }
7218    return 456 * (154 - this.actualScanLine + this.memory[0xFF45]);
7219  }
7220  return (456 * ((this.actualScanLine == 153 && this.memory[0xFF44] == 0) ? 154 : (153 - this.actualScanLine))) + 8;
7221}
7222GameBoyCore.prototype.clocksUntilMode0 = function () {
7223  switch (this.modeSTAT) {
7224    case 0:
7225      if (this.actualScanLine == 143) {
7226        this.updateSpriteCount(0);
7227        return this.spriteCount + 5016;
7228      }
7229      this.updateSpriteCount(this.actualScanLine + 1);
7230      return this.spriteCount + 456;
7231    case 2:
7232    case 3:
7233      this.updateSpriteCount(this.actualScanLine);
7234      return this.spriteCount;
7235    case 1:
7236      this.updateSpriteCount(0);
7237      return this.spriteCount + (456 * (154 - this.actualScanLine));
7238  }
7239}
7240GameBoyCore.prototype.updateSpriteCount = function (line) {
7241  this.spriteCount = 252;
7242  if (this.cGBC && this.gfxSpriteShow) {                    //Is the window enabled and are we in CGB mode?
7243    var lineAdjusted = line + 0x10;
7244    var yoffset = 0;
7245    var yCap = (this.gfxSpriteNormalHeight) ? 0x8 : 0x10;
7246    for (var OAMAddress = 0xFE00; OAMAddress < 0xFEA0 && this.spriteCount < 312; OAMAddress += 4) {
7247      yoffset = lineAdjusted - this.memory[OAMAddress];
7248      if (yoffset > -1 && yoffset < yCap) {
7249        this.spriteCount += 6;
7250      }
7251    }
7252  }
7253}
7254GameBoyCore.prototype.matchLYC = function () {  //LYC Register Compare
7255  if (this.memory[0xFF44] == this.memory[0xFF45]) {
7256    this.memory[0xFF41] |= 0x04;
7257    if (this.LYCMatchTriggerSTAT) {
7258      this.interruptsRequested |= 0x2;
7259      this.checkIRQMatching();
7260    }
7261  }
7262  else {
7263    this.memory[0xFF41] &= 0x7B;
7264  }
7265}
7266GameBoyCore.prototype.updateCore = function () {
7267  //Update the clocking for the LCD emulation:
7268  this.LCDTicks += this.CPUTicks >> this.doubleSpeedShifter;  //LCD Timing
7269  this.LCDCONTROL[this.actualScanLine](this);          //Scan Line and STAT Mode Control
7270  //Single-speed relative timing for A/V emulation:
7271  var timedTicks = this.CPUTicks >> this.doubleSpeedShifter;  //CPU clocking can be updated from the LCD handling.
7272  this.audioTicks += timedTicks;                //Audio Timing
7273  this.emulatorTicks += timedTicks;              //Emulator Timing
7274  //CPU Timers:
7275  this.DIVTicks += this.CPUTicks;                //DIV Timing
7276  if (this.TIMAEnabled) {                    //TIMA Timing
7277    this.timerTicks += this.CPUTicks;
7278    while (this.timerTicks >= this.TACClocker) {
7279      this.timerTicks -= this.TACClocker;
7280      if (++this.memory[0xFF05] == 0x100) {
7281        this.memory[0xFF05] = this.memory[0xFF06];
7282        this.interruptsRequested |= 0x4;
7283        this.checkIRQMatching();
7284      }
7285    }
7286  }
7287  if (this.serialTimer > 0) {                    //Serial Timing
7288    //IRQ Counter:
7289    this.serialTimer -= this.CPUTicks;
7290    if (this.serialTimer <= 0) {
7291      this.interruptsRequested |= 0x8;
7292      this.checkIRQMatching();
7293    }
7294    //Bit Shit Counter:
7295    this.serialShiftTimer -= this.CPUTicks;
7296    if (this.serialShiftTimer <= 0) {
7297      this.serialShiftTimer = this.serialShiftTimerAllocated;
7298      this.memory[0xFF01] = ((this.memory[0xFF01] << 1) & 0xFE) | 0x01;  //We could shift in actual link data here if we were to implement such!!!
7299    }
7300  }
7301}
7302GameBoyCore.prototype.updateCoreFull = function () {
7303  //Update the state machine:
7304  this.updateCore();
7305  //End of iteration routine:
7306  if (this.emulatorTicks >= this.CPUCyclesTotal) {
7307    this.iterationEndRoutine();
7308  }
7309}
7310GameBoyCore.prototype.initializeLCDController = function () {
7311  //Display on hanlding:
7312  var line = 0;
7313  while (line < 154) {
7314    if (line < 143) {
7315      //We're on a normal scan line:
7316      this.LINECONTROL[line] = function (parentObj) {
7317        if (parentObj.LCDTicks < 80) {
7318          parentObj.scanLineMode2();
7319        }
7320        else if (parentObj.LCDTicks < 252) {
7321          parentObj.scanLineMode3();
7322        }
7323        else if (parentObj.LCDTicks < 456) {
7324          parentObj.scanLineMode0();
7325        }
7326        else {
7327          //We're on a new scan line:
7328          parentObj.LCDTicks -= 456;
7329          if (parentObj.STATTracker != 3) {
7330            //Make sure the mode 0 handler was run at least once per scan line:
7331            if (parentObj.STATTracker != 2) {
7332              if (parentObj.STATTracker == 0 && parentObj.mode2TriggerSTAT) {
7333                parentObj.interruptsRequested |= 0x2;
7334              }
7335              parentObj.incrementScanLineQueue();
7336            }
7337            if (parentObj.hdmaRunning) {
7338              parentObj.executeHDMA();
7339            }
7340            if (parentObj.mode0TriggerSTAT) {
7341              parentObj.interruptsRequested |= 0x2;
7342            }
7343          }
7344          //Update the scanline registers and assert the LYC counter:
7345          parentObj.actualScanLine = ++parentObj.memory[0xFF44];
7346          //Perform a LYC counter assert:
7347          if (parentObj.actualScanLine == parentObj.memory[0xFF45]) {
7348            parentObj.memory[0xFF41] |= 0x04;
7349            if (parentObj.LYCMatchTriggerSTAT) {
7350              parentObj.interruptsRequested |= 0x2;
7351            }
7352          }
7353          else {
7354            parentObj.memory[0xFF41] &= 0x7B;
7355          }
7356          parentObj.checkIRQMatching();
7357          //Reset our mode contingency variables:
7358          parentObj.STATTracker = 0;
7359          parentObj.modeSTAT = 2;
7360          parentObj.LINECONTROL[parentObj.actualScanLine](parentObj);  //Scan Line and STAT Mode Control.
7361        }
7362      }
7363    }
7364    else if (line == 143) {
7365      //We're on the last visible scan line of the LCD screen:
7366      this.LINECONTROL[143] = function (parentObj) {
7367        if (parentObj.LCDTicks < 80) {
7368          parentObj.scanLineMode2();
7369        }
7370        else if (parentObj.LCDTicks < 252) {
7371          parentObj.scanLineMode3();
7372        }
7373        else if (parentObj.LCDTicks < 456) {
7374          parentObj.scanLineMode0();
7375        }
7376        else {
7377          //Starting V-Blank:
7378          //Just finished the last visible scan line:
7379          parentObj.LCDTicks -= 456;
7380          if (parentObj.STATTracker != 3) {
7381            //Make sure the mode 0 handler was run at least once per scan line:
7382            if (parentObj.STATTracker != 2) {
7383              if (parentObj.STATTracker == 0 && parentObj.mode2TriggerSTAT) {
7384                parentObj.interruptsRequested |= 0x2;
7385              }
7386              parentObj.incrementScanLineQueue();
7387            }
7388            if (parentObj.hdmaRunning) {
7389              parentObj.executeHDMA();
7390            }
7391            if (parentObj.mode0TriggerSTAT) {
7392              parentObj.interruptsRequested |= 0x2;
7393            }
7394          }
7395          //Update the scanline registers and assert the LYC counter:
7396          parentObj.actualScanLine = parentObj.memory[0xFF44] = 144;
7397          //Perform a LYC counter assert:
7398          if (parentObj.memory[0xFF45] == 144) {
7399            parentObj.memory[0xFF41] |= 0x04;
7400            if (parentObj.LYCMatchTriggerSTAT) {
7401              parentObj.interruptsRequested |= 0x2;
7402            }
7403          }
7404          else {
7405            parentObj.memory[0xFF41] &= 0x7B;
7406          }
7407          //Reset our mode contingency variables:
7408          parentObj.STATTracker = 0;
7409          //Update our state for v-blank:
7410          parentObj.modeSTAT = 1;
7411          parentObj.interruptsRequested |= (parentObj.mode1TriggerSTAT) ? 0x3 : 0x1;
7412          parentObj.checkIRQMatching();
7413          //Attempt to blit out to our canvas:
7414          if (parentObj.drewBlank == 0) {
7415            //Ensure JIT framing alignment:
7416            if (parentObj.totalLinesPassed < 144 || (parentObj.totalLinesPassed == 144 && parentObj.midScanlineOffset > -1)) {
7417              //Make sure our gfx are up-to-date:
7418              parentObj.graphicsJITVBlank();
7419              //Draw the frame:
7420              parentObj.prepareFrame();
7421            }
7422          }
7423          else {
7424            //LCD off takes at least 2 frames:
7425            --parentObj.drewBlank;
7426          }
7427          parentObj.LINECONTROL[144](parentObj);  //Scan Line and STAT Mode Control.
7428        }
7429      }
7430    }
7431    else if (line < 153) {
7432      //In VBlank
7433      this.LINECONTROL[line] = function (parentObj) {
7434        if (parentObj.LCDTicks >= 456) {
7435          //We're on a new scan line:
7436          parentObj.LCDTicks -= 456;
7437          parentObj.actualScanLine = ++parentObj.memory[0xFF44];
7438          //Perform a LYC counter assert:
7439          if (parentObj.actualScanLine == parentObj.memory[0xFF45]) {
7440            parentObj.memory[0xFF41] |= 0x04;
7441            if (parentObj.LYCMatchTriggerSTAT) {
7442              parentObj.interruptsRequested |= 0x2;
7443              parentObj.checkIRQMatching();
7444            }
7445          }
7446          else {
7447            parentObj.memory[0xFF41] &= 0x7B;
7448          }
7449          parentObj.LINECONTROL[parentObj.actualScanLine](parentObj);  //Scan Line and STAT Mode Control.
7450        }
7451      }
7452    }
7453    else {
7454      //VBlank Ending (We're on the last actual scan line)
7455      this.LINECONTROL[153] = function (parentObj) {
7456        if (parentObj.LCDTicks >= 8) {
7457          if (parentObj.STATTracker != 4 && parentObj.memory[0xFF44] == 153) {
7458            parentObj.memory[0xFF44] = 0;  //LY register resets to 0 early.
7459            //Perform a LYC counter assert:
7460            if (parentObj.memory[0xFF45] == 0) {
7461              parentObj.memory[0xFF41] |= 0x04;
7462              if (parentObj.LYCMatchTriggerSTAT) {
7463                parentObj.interruptsRequested |= 0x2;
7464                parentObj.checkIRQMatching();
7465              }
7466            }
7467            else {
7468              parentObj.memory[0xFF41] &= 0x7B;
7469            }
7470            parentObj.STATTracker = 4;
7471          }
7472          if (parentObj.LCDTicks >= 456) {
7473            //We reset back to the beginning:
7474            parentObj.LCDTicks -= 456;
7475            parentObj.STATTracker = parentObj.actualScanLine = 0;
7476            parentObj.LINECONTROL[0](parentObj);  //Scan Line and STAT Mode Control.
7477          }
7478        }
7479      }
7480    }
7481    ++line;
7482  }
7483}
7484GameBoyCore.prototype.DisplayShowOff = function () {
7485  if (this.drewBlank == 0) {
7486    //Output a blank screen to the output framebuffer:
7487    this.clearFrameBuffer();
7488    this.drewFrame = true;
7489  }
7490  this.drewBlank = 2;
7491}
7492GameBoyCore.prototype.executeHDMA = function () {
7493  this.DMAWrite(1);
7494  if (this.halt) {
7495    if ((this.LCDTicks - this.spriteCount) < ((4 >> this.doubleSpeedShifter) | 0x20)) {
7496      //HALT clocking correction:
7497      this.CPUTicks = 4 + ((0x20 + this.spriteCount) << this.doubleSpeedShifter);
7498      this.LCDTicks = this.spriteCount + ((4 >> this.doubleSpeedShifter) | 0x20);
7499    }
7500  }
7501  else {
7502    this.LCDTicks += (4 >> this.doubleSpeedShifter) | 0x20;      //LCD Timing Update For HDMA.
7503  }
7504  if (this.memory[0xFF55] == 0) {
7505    this.hdmaRunning = false;
7506    this.memory[0xFF55] = 0xFF;  //Transfer completed ("Hidden last step," since some ROMs don't imply this, but most do).
7507  }
7508  else {
7509    --this.memory[0xFF55];
7510  }
7511}
7512GameBoyCore.prototype.clockUpdate = function () {
7513  if (this.cTIMER) {
7514    var dateObj = new_Date(); // The line is changed for benchmarking.
7515    var newTime = dateObj.getTime();
7516    var timeElapsed = newTime - this.lastIteration;  //Get the numnber of milliseconds since this last executed.
7517    this.lastIteration = newTime;
7518    if (this.cTIMER && !this.RTCHALT) {
7519      //Update the MBC3 RTC:
7520      this.RTCSeconds += timeElapsed / 1000;
7521      while (this.RTCSeconds >= 60) {  //System can stutter, so the seconds difference can get large, thus the "while".
7522        this.RTCSeconds -= 60;
7523        ++this.RTCMinutes;
7524        if (this.RTCMinutes >= 60) {
7525          this.RTCMinutes -= 60;
7526          ++this.RTCHours;
7527          if (this.RTCHours >= 24) {
7528            this.RTCHours -= 24
7529            ++this.RTCDays;
7530            if (this.RTCDays >= 512) {
7531              this.RTCDays -= 512;
7532              this.RTCDayOverFlow = true;
7533            }
7534          }
7535        }
7536      }
7537    }
7538  }
7539}
7540GameBoyCore.prototype.prepareFrame = function () {
7541  //Copy the internal frame buffer to the output buffer:
7542  this.swizzleFrameBuffer();
7543  this.drewFrame = true;
7544}
7545GameBoyCore.prototype.requestDraw = function () {
7546  if (this.drewFrame) {
7547    this.dispatchDraw();
7548  }
7549}
7550GameBoyCore.prototype.dispatchDraw = function () {
7551  var canvasRGBALength = this.offscreenRGBCount;
7552  if (canvasRGBALength > 0) {
7553    //We actually updated the graphics internally, so copy out:
7554    var frameBuffer = (canvasRGBALength == 92160) ? this.swizzledFrame : this.resizeFrameBuffer();
7555    var canvasData = this.canvasBuffer.data;
7556    var bufferIndex = 0;
7557    for (var canvasIndex = 0; canvasIndex < canvasRGBALength; ++canvasIndex) {
7558      canvasData[canvasIndex++] = frameBuffer[bufferIndex++];
7559      canvasData[canvasIndex++] = frameBuffer[bufferIndex++];
7560      canvasData[canvasIndex++] = frameBuffer[bufferIndex++];
7561    }
7562    this.graphicsBlit();
7563  }
7564}
7565GameBoyCore.prototype.swizzleFrameBuffer = function () {
7566  //Convert our dirty 24-bit (24-bit, with internal render flags above it) framebuffer to an 8-bit buffer with separate indices for the RGB channels:
7567  var frameBuffer = this.frameBuffer;
7568  var swizzledFrame = this.swizzledFrame;
7569  var bufferIndex = 0;
7570  for (var canvasIndex = 0; canvasIndex < 69120;) {
7571    swizzledFrame[canvasIndex++] = (frameBuffer[bufferIndex] >> 16) & 0xFF;    //Red
7572    swizzledFrame[canvasIndex++] = (frameBuffer[bufferIndex] >> 8) & 0xFF;    //Green
7573    swizzledFrame[canvasIndex++] = frameBuffer[bufferIndex++] & 0xFF;      //Blue
7574  }
7575}
7576GameBoyCore.prototype.clearFrameBuffer = function () {
7577  var bufferIndex = 0;
7578  var frameBuffer = this.swizzledFrame;
7579  if (this.cGBC || this.colorizedGBPalettes) {
7580    while (bufferIndex < 69120) {
7581      frameBuffer[bufferIndex++] = 248;
7582    }
7583  }
7584  else {
7585    while (bufferIndex < 69120) {
7586      frameBuffer[bufferIndex++] = 239;
7587      frameBuffer[bufferIndex++] = 255;
7588      frameBuffer[bufferIndex++] = 222;
7589    }
7590  }
7591}
7592GameBoyCore.prototype.resizeFrameBuffer = function () {
7593  //Return a reference to the generated resized framebuffer:
7594  return this.resizer.resize(this.swizzledFrame);
7595}
7596GameBoyCore.prototype.compileResizeFrameBufferFunction = function () {
7597  if (this.offscreenRGBCount > 0) {
7598    this.resizer = new Resize(160, 144, this.offscreenWidth, this.offscreenHeight, false, true);
7599  }
7600}
7601GameBoyCore.prototype.renderScanLine = function (scanlineToRender) {
7602  this.pixelStart = scanlineToRender * 160;
7603  if (this.bgEnabled) {
7604    this.pixelEnd = 160;
7605    this.BGLayerRender(scanlineToRender);
7606    this.WindowLayerRender(scanlineToRender);
7607  }
7608  else {
7609    var pixelLine = (scanlineToRender + 1) * 160;
7610    var defaultColor = (this.cGBC || this.colorizedGBPalettes) ? 0xF8F8F8 : 0xEFFFDE;
7611    for (var pixelPosition = (scanlineToRender * 160) + this.currentX; pixelPosition < pixelLine; pixelPosition++) {
7612      this.frameBuffer[pixelPosition] = defaultColor;
7613    }
7614  }
7615  this.SpriteLayerRender(scanlineToRender);
7616  this.currentX = 0;
7617  this.midScanlineOffset = -1;
7618}
7619GameBoyCore.prototype.renderMidScanLine = function () {
7620  if (this.actualScanLine < 144 && this.modeSTAT == 3) {
7621    //TODO: Get this accurate:
7622    if (this.midScanlineOffset == -1) {
7623      this.midScanlineOffset = this.backgroundX & 0x7;
7624    }
7625    if (this.LCDTicks >= 82) {
7626      this.pixelEnd = this.LCDTicks - 74;
7627      this.pixelEnd = Math.min(this.pixelEnd - this.midScanlineOffset - (this.pixelEnd % 0x8), 160);
7628      if (this.bgEnabled) {
7629        this.pixelStart = this.lastUnrenderedLine * 160;
7630        this.BGLayerRender(this.lastUnrenderedLine);
7631        this.WindowLayerRender(this.lastUnrenderedLine);
7632        //TODO: Do midscanline JIT for sprites...
7633      }
7634      else {
7635        var pixelLine = (this.lastUnrenderedLine * 160) + this.pixelEnd;
7636        var defaultColor = (this.cGBC || this.colorizedGBPalettes) ? 0xF8F8F8 : 0xEFFFDE;
7637        for (var pixelPosition = (this.lastUnrenderedLine * 160) + this.currentX; pixelPosition < pixelLine; pixelPosition++) {
7638          this.frameBuffer[pixelPosition] = defaultColor;
7639        }
7640      }
7641      this.currentX = this.pixelEnd;
7642    }
7643  }
7644}
7645GameBoyCore.prototype.initializeModeSpecificArrays = function () {
7646  this.LCDCONTROL = (this.LCDisOn) ? this.LINECONTROL : this.DISPLAYOFFCONTROL;
7647  if (this.cGBC) {
7648    this.gbcOBJRawPalette = this.getTypedArray(0x40, 0, "uint8");
7649    this.gbcBGRawPalette = this.getTypedArray(0x40, 0, "uint8");
7650    this.gbcOBJPalette = this.getTypedArray(0x20, 0x1000000, "int32");
7651    this.gbcBGPalette = this.getTypedArray(0x40, 0, "int32");
7652    this.BGCHRBank2 = this.getTypedArray(0x800, 0, "uint8");
7653    this.BGCHRCurrentBank = (this.currVRAMBank > 0) ? this.BGCHRBank2 : this.BGCHRBank1;
7654    this.tileCache = this.generateCacheArray(0xF80);
7655  }
7656  else {
7657    this.gbOBJPalette = this.getTypedArray(8, 0, "int32");
7658    this.gbBGPalette = this.getTypedArray(4, 0, "int32");
7659    this.BGPalette = this.gbBGPalette;
7660    this.OBJPalette = this.gbOBJPalette;
7661    this.tileCache = this.generateCacheArray(0x700);
7662    this.sortBuffer = this.getTypedArray(0x100, 0, "uint8");
7663    this.OAMAddressCache = this.getTypedArray(10, 0, "int32");
7664  }
7665  this.renderPathBuild();
7666}
7667GameBoyCore.prototype.GBCtoGBModeAdjust = function () {
7668  cout("Stepping down from GBC mode.", 0);
7669  this.VRAM = this.GBCMemory = this.BGCHRCurrentBank = this.BGCHRBank2 = null;
7670  this.tileCache.length = 0x700;
7671  if (settings[4]) {
7672    this.gbBGColorizedPalette = this.getTypedArray(4, 0, "int32");
7673    this.gbOBJColorizedPalette = this.getTypedArray(8, 0, "int32");
7674    this.cachedBGPaletteConversion = this.getTypedArray(4, 0, "int32");
7675    this.cachedOBJPaletteConversion = this.getTypedArray(8, 0, "int32");
7676    this.BGPalette = this.gbBGColorizedPalette;
7677    this.OBJPalette = this.gbOBJColorizedPalette;
7678    this.gbOBJPalette = this.gbBGPalette = null;
7679    this.getGBCColor();
7680  }
7681  else {
7682    this.gbOBJPalette = this.getTypedArray(8, 0, "int32");
7683    this.gbBGPalette = this.getTypedArray(4, 0, "int32");
7684    this.BGPalette = this.gbBGPalette;
7685    this.OBJPalette = this.gbOBJPalette;
7686  }
7687  this.sortBuffer = this.getTypedArray(0x100, 0, "uint8");
7688  this.OAMAddressCache = this.getTypedArray(10, 0, "int32");
7689  this.renderPathBuild();
7690  this.memoryReadJumpCompile();
7691  this.memoryWriteJumpCompile();
7692}
7693GameBoyCore.prototype.renderPathBuild = function () {
7694  if (!this.cGBC) {
7695    this.BGLayerRender = this.BGGBLayerRender;
7696    this.WindowLayerRender = this.WindowGBLayerRender;
7697    this.SpriteLayerRender = this.SpriteGBLayerRender;
7698  }
7699  else {
7700    this.priorityFlaggingPathRebuild();
7701    this.SpriteLayerRender = this.SpriteGBCLayerRender;
7702  }
7703}
7704GameBoyCore.prototype.priorityFlaggingPathRebuild = function () {
7705  if (this.BGPriorityEnabled) {
7706    this.BGLayerRender = this.BGGBCLayerRender;
7707    this.WindowLayerRender = this.WindowGBCLayerRender;
7708  }
7709  else {
7710    this.BGLayerRender = this.BGGBCLayerRenderNoPriorityFlagging;
7711    this.WindowLayerRender = this.WindowGBCLayerRenderNoPriorityFlagging;
7712  }
7713}
7714GameBoyCore.prototype.initializeReferencesFromSaveState = function () {
7715  this.LCDCONTROL = (this.LCDisOn) ? this.LINECONTROL : this.DISPLAYOFFCONTROL;
7716  var tileIndex = 0;
7717  if (!this.cGBC) {
7718    if (this.colorizedGBPalettes) {
7719      this.BGPalette = this.gbBGColorizedPalette;
7720      this.OBJPalette = this.gbOBJColorizedPalette;
7721      this.updateGBBGPalette = this.updateGBColorizedBGPalette;
7722      this.updateGBOBJPalette = this.updateGBColorizedOBJPalette;
7723
7724    }
7725    else {
7726      this.BGPalette = this.gbBGPalette;
7727      this.OBJPalette = this.gbOBJPalette;
7728    }
7729    this.tileCache = this.generateCacheArray(0x700);
7730    for (tileIndex = 0x8000; tileIndex < 0x9000; tileIndex += 2) {
7731      this.generateGBOAMTileLine(tileIndex);
7732    }
7733    for (tileIndex = 0x9000; tileIndex < 0x9800; tileIndex += 2) {
7734      this.generateGBTileLine(tileIndex);
7735    }
7736    this.sortBuffer = this.getTypedArray(0x100, 0, "uint8");
7737    this.OAMAddressCache = this.getTypedArray(10, 0, "int32");
7738  }
7739  else {
7740    this.BGCHRCurrentBank = (this.currVRAMBank > 0) ? this.BGCHRBank2 : this.BGCHRBank1;
7741    this.tileCache = this.generateCacheArray(0xF80);
7742    for (; tileIndex < 0x1800; tileIndex += 0x10) {
7743      this.generateGBCTileBank1(tileIndex);
7744      this.generateGBCTileBank2(tileIndex);
7745    }
7746  }
7747  this.renderPathBuild();
7748}
7749GameBoyCore.prototype.RGBTint = function (value) {
7750  //Adjustment for the GBC's tinting (According to Gambatte):
7751  var r = value & 0x1F;
7752  var g = (value >> 5) & 0x1F;
7753  var b = (value >> 10) & 0x1F;
7754  return ((r * 13 + g * 2 + b) >> 1) << 16 | (g * 3 + b) << 9 | (r * 3 + g * 2 + b * 11) >> 1;
7755}
7756GameBoyCore.prototype.getGBCColor = function () {
7757  //GBC Colorization of DMG ROMs:
7758  //BG
7759  for (var counter = 0; counter < 4; counter++) {
7760    var adjustedIndex = counter << 1;
7761    //BG
7762    this.cachedBGPaletteConversion[counter] = this.RGBTint((this.gbcBGRawPalette[adjustedIndex | 1] << 8) | this.gbcBGRawPalette[adjustedIndex]);
7763    //OBJ 1
7764    this.cachedOBJPaletteConversion[counter] = this.RGBTint((this.gbcOBJRawPalette[adjustedIndex | 1] << 8) | this.gbcOBJRawPalette[adjustedIndex]);
7765  }
7766  //OBJ 2
7767  for (counter = 4; counter < 8; counter++) {
7768    adjustedIndex = counter << 1;
7769    this.cachedOBJPaletteConversion[counter] = this.RGBTint((this.gbcOBJRawPalette[adjustedIndex | 1] << 8) | this.gbcOBJRawPalette[adjustedIndex]);
7770  }
7771  //Update the palette entries:
7772  this.updateGBBGPalette = this.updateGBColorizedBGPalette;
7773  this.updateGBOBJPalette = this.updateGBColorizedOBJPalette;
7774  this.updateGBBGPalette(this.memory[0xFF47]);
7775  this.updateGBOBJPalette(0, this.memory[0xFF48]);
7776  this.updateGBOBJPalette(1, this.memory[0xFF49]);
7777  this.colorizedGBPalettes = true;
7778}
7779GameBoyCore.prototype.updateGBRegularBGPalette = function (data) {
7780  this.gbBGPalette[0] = this.colors[data & 0x03] | 0x2000000;
7781  this.gbBGPalette[1] = this.colors[(data >> 2) & 0x03];
7782  this.gbBGPalette[2] = this.colors[(data >> 4) & 0x03];
7783  this.gbBGPalette[3] = this.colors[data >> 6];
7784}
7785GameBoyCore.prototype.updateGBColorizedBGPalette = function (data) {
7786  //GB colorization:
7787  this.gbBGColorizedPalette[0] = this.cachedBGPaletteConversion[data & 0x03] | 0x2000000;
7788  this.gbBGColorizedPalette[1] = this.cachedBGPaletteConversion[(data >> 2) & 0x03];
7789  this.gbBGColorizedPalette[2] = this.cachedBGPaletteConversion[(data >> 4) & 0x03];
7790  this.gbBGColorizedPalette[3] = this.cachedBGPaletteConversion[data >> 6];
7791}
7792GameBoyCore.prototype.updateGBRegularOBJPalette = function (index, data) {
7793  this.gbOBJPalette[index | 1] = this.colors[(data >> 2) & 0x03];
7794  this.gbOBJPalette[index | 2] = this.colors[(data >> 4) & 0x03];
7795  this.gbOBJPalette[index | 3] = this.colors[data >> 6];
7796}
7797GameBoyCore.prototype.updateGBColorizedOBJPalette = function (index, data) {
7798  //GB colorization:
7799  this.gbOBJColorizedPalette[index | 1] = this.cachedOBJPaletteConversion[index | ((data >> 2) & 0x03)];
7800  this.gbOBJColorizedPalette[index | 2] = this.cachedOBJPaletteConversion[index | ((data >> 4) & 0x03)];
7801  this.gbOBJColorizedPalette[index | 3] = this.cachedOBJPaletteConversion[index | (data >> 6)];
7802}
7803GameBoyCore.prototype.updateGBCBGPalette = function (index, data) {
7804  if (this.gbcBGRawPalette[index] != data) {
7805    this.midScanLineJIT();
7806    //Update the color palette for BG tiles since it changed:
7807    this.gbcBGRawPalette[index] = data;
7808    if ((index & 0x06) == 0) {
7809      //Palette 0 (Special tile Priority stuff)
7810      data = 0x2000000 | this.RGBTint((this.gbcBGRawPalette[index | 1] << 8) | this.gbcBGRawPalette[index & 0x3E]);
7811      index >>= 1;
7812      this.gbcBGPalette[index] = data;
7813      this.gbcBGPalette[0x20 | index] = 0x1000000 | data;
7814    }
7815    else {
7816      //Regular Palettes (No special crap)
7817      data = this.RGBTint((this.gbcBGRawPalette[index | 1] << 8) | this.gbcBGRawPalette[index & 0x3E]);
7818      index >>= 1;
7819      this.gbcBGPalette[index] = data;
7820      this.gbcBGPalette[0x20 | index] = 0x1000000 | data;
7821    }
7822  }
7823}
7824GameBoyCore.prototype.updateGBCOBJPalette = function (index, data) {
7825  if (this.gbcOBJRawPalette[index] != data) {
7826    //Update the color palette for OBJ tiles since it changed:
7827    this.gbcOBJRawPalette[index] = data;
7828    if ((index & 0x06) > 0) {
7829      //Regular Palettes (No special crap)
7830      this.midScanLineJIT();
7831      this.gbcOBJPalette[index >> 1] = 0x1000000 | this.RGBTint((this.gbcOBJRawPalette[index | 1] << 8) | this.gbcOBJRawPalette[index & 0x3E]);
7832    }
7833  }
7834}
7835GameBoyCore.prototype.BGGBLayerRender = function (scanlineToRender) {
7836  var scrollYAdjusted = (this.backgroundY + scanlineToRender) & 0xFF;            //The line of the BG we're at.
7837  var tileYLine = (scrollYAdjusted & 7) << 3;
7838  var tileYDown = this.gfxBackgroundCHRBankPosition | ((scrollYAdjusted & 0xF8) << 2);  //The row of cached tiles we're fetching from.
7839  var scrollXAdjusted = (this.backgroundX + this.currentX) & 0xFF;            //The scroll amount of the BG.
7840  var pixelPosition = this.pixelStart + this.currentX;                  //Current pixel we're working on.
7841  var pixelPositionEnd = this.pixelStart + ((this.gfxWindowDisplay && (scanlineToRender - this.windowY) >= 0) ? Math.min(Math.max(this.windowX, 0) + this.currentX, this.pixelEnd) : this.pixelEnd);  //Make sure we do at most 160 pixels a scanline.
7842  var tileNumber = tileYDown + (scrollXAdjusted >> 3);
7843  var chrCode = this.BGCHRBank1[tileNumber];
7844  if (chrCode < this.gfxBackgroundBankOffset) {
7845    chrCode |= 0x100;
7846  }
7847  var tile = this.tileCache[chrCode];
7848  for (var texel = (scrollXAdjusted & 0x7); texel < 8 && pixelPosition < pixelPositionEnd && scrollXAdjusted < 0x100; ++scrollXAdjusted) {
7849    this.frameBuffer[pixelPosition++] = this.BGPalette[tile[tileYLine | texel++]];
7850  }
7851  var scrollXAdjustedAligned = Math.min(pixelPositionEnd - pixelPosition, 0x100 - scrollXAdjusted) >> 3;
7852  scrollXAdjusted += scrollXAdjustedAligned << 3;
7853  scrollXAdjustedAligned += tileNumber;
7854  while (tileNumber < scrollXAdjustedAligned) {
7855    chrCode = this.BGCHRBank1[++tileNumber];
7856    if (chrCode < this.gfxBackgroundBankOffset) {
7857      chrCode |= 0x100;
7858    }
7859    tile = this.tileCache[chrCode];
7860    texel = tileYLine;
7861    this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
7862    this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
7863    this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
7864    this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
7865    this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
7866    this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
7867    this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
7868    this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel]];
7869  }
7870  if (pixelPosition < pixelPositionEnd) {
7871    if (scrollXAdjusted < 0x100) {
7872      chrCode = this.BGCHRBank1[++tileNumber];
7873      if (chrCode < this.gfxBackgroundBankOffset) {
7874        chrCode |= 0x100;
7875      }
7876      tile = this.tileCache[chrCode];
7877      for (texel = tileYLine - 1; pixelPosition < pixelPositionEnd && scrollXAdjusted < 0x100; ++scrollXAdjusted) {
7878        this.frameBuffer[pixelPosition++] = this.BGPalette[tile[++texel]];
7879      }
7880    }
7881    scrollXAdjustedAligned = ((pixelPositionEnd - pixelPosition) >> 3) + tileYDown;
7882    while (tileYDown < scrollXAdjustedAligned) {
7883      chrCode = this.BGCHRBank1[tileYDown++];
7884      if (chrCode < this.gfxBackgroundBankOffset) {
7885        chrCode |= 0x100;
7886      }
7887      tile = this.tileCache[chrCode];
7888      texel = tileYLine;
7889      this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
7890      this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
7891      this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
7892      this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
7893      this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
7894      this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
7895      this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
7896      this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel]];
7897    }
7898    if (pixelPosition < pixelPositionEnd) {
7899      chrCode = this.BGCHRBank1[tileYDown];
7900      if (chrCode < this.gfxBackgroundBankOffset) {
7901        chrCode |= 0x100;
7902      }
7903      tile = this.tileCache[chrCode];
7904      switch (pixelPositionEnd - pixelPosition) {
7905        case 7:
7906          this.frameBuffer[pixelPosition + 6] = this.BGPalette[tile[tileYLine | 6]];
7907        case 6:
7908          this.frameBuffer[pixelPosition + 5] = this.BGPalette[tile[tileYLine | 5]];
7909        case 5:
7910          this.frameBuffer[pixelPosition + 4] = this.BGPalette[tile[tileYLine | 4]];
7911        case 4:
7912          this.frameBuffer[pixelPosition + 3] = this.BGPalette[tile[tileYLine | 3]];
7913        case 3:
7914          this.frameBuffer[pixelPosition + 2] = this.BGPalette[tile[tileYLine | 2]];
7915        case 2:
7916          this.frameBuffer[pixelPosition + 1] = this.BGPalette[tile[tileYLine | 1]];
7917        case 1:
7918          this.frameBuffer[pixelPosition] = this.BGPalette[tile[tileYLine]];
7919      }
7920    }
7921  }
7922}
7923GameBoyCore.prototype.BGGBCLayerRender = function (scanlineToRender) {
7924  var scrollYAdjusted = (this.backgroundY + scanlineToRender) & 0xFF;            //The line of the BG we're at.
7925  var tileYLine = (scrollYAdjusted & 7) << 3;
7926  var tileYDown = this.gfxBackgroundCHRBankPosition | ((scrollYAdjusted & 0xF8) << 2);  //The row of cached tiles we're fetching from.
7927  var scrollXAdjusted = (this.backgroundX + this.currentX) & 0xFF;            //The scroll amount of the BG.
7928  var pixelPosition = this.pixelStart + this.currentX;                  //Current pixel we're working on.
7929  var pixelPositionEnd = this.pixelStart + ((this.gfxWindowDisplay && (scanlineToRender - this.windowY) >= 0) ? Math.min(Math.max(this.windowX, 0) + this.currentX, this.pixelEnd) : this.pixelEnd);  //Make sure we do at most 160 pixels a scanline.
7930  var tileNumber = tileYDown + (scrollXAdjusted >> 3);
7931  var chrCode = this.BGCHRBank1[tileNumber];
7932  if (chrCode < this.gfxBackgroundBankOffset) {
7933    chrCode |= 0x100;
7934  }
7935  var attrCode = this.BGCHRBank2[tileNumber];
7936  var tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
7937  var palette = ((attrCode & 0x7) << 2) | ((attrCode & 0x80) >> 2);
7938  for (var texel = (scrollXAdjusted & 0x7); texel < 8 && pixelPosition < pixelPositionEnd && scrollXAdjusted < 0x100; ++scrollXAdjusted) {
7939    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[tileYLine | texel++]];
7940  }
7941  var scrollXAdjustedAligned = Math.min(pixelPositionEnd - pixelPosition, 0x100 - scrollXAdjusted) >> 3;
7942  scrollXAdjusted += scrollXAdjustedAligned << 3;
7943  scrollXAdjustedAligned += tileNumber;
7944  while (tileNumber < scrollXAdjustedAligned) {
7945    chrCode = this.BGCHRBank1[++tileNumber];
7946    if (chrCode < this.gfxBackgroundBankOffset) {
7947      chrCode |= 0x100;
7948    }
7949    attrCode = this.BGCHRBank2[tileNumber];
7950    tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
7951    palette = ((attrCode & 0x7) << 2) | ((attrCode & 0x80) >> 2);
7952    texel = tileYLine;
7953    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
7954    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
7955    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
7956    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
7957    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
7958    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
7959    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
7960    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel]];
7961  }
7962  if (pixelPosition < pixelPositionEnd) {
7963    if (scrollXAdjusted < 0x100) {
7964      chrCode = this.BGCHRBank1[++tileNumber];
7965      if (chrCode < this.gfxBackgroundBankOffset) {
7966        chrCode |= 0x100;
7967      }
7968      attrCode = this.BGCHRBank2[tileNumber];
7969      tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
7970      palette = ((attrCode & 0x7) << 2) | ((attrCode & 0x80) >> 2);
7971      for (texel = tileYLine - 1; pixelPosition < pixelPositionEnd && scrollXAdjusted < 0x100; ++scrollXAdjusted) {
7972        this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[++texel]];
7973      }
7974    }
7975    scrollXAdjustedAligned = ((pixelPositionEnd - pixelPosition) >> 3) + tileYDown;
7976    while (tileYDown < scrollXAdjustedAligned) {
7977      chrCode = this.BGCHRBank1[tileYDown];
7978      if (chrCode < this.gfxBackgroundBankOffset) {
7979        chrCode |= 0x100;
7980      }
7981      attrCode = this.BGCHRBank2[tileYDown++];
7982      tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
7983      palette = ((attrCode & 0x7) << 2) | ((attrCode & 0x80) >> 2);
7984      texel = tileYLine;
7985      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
7986      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
7987      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
7988      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
7989      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
7990      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
7991      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
7992      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel]];
7993    }
7994    if (pixelPosition < pixelPositionEnd) {
7995      chrCode = this.BGCHRBank1[tileYDown];
7996      if (chrCode < this.gfxBackgroundBankOffset) {
7997        chrCode |= 0x100;
7998      }
7999      attrCode = this.BGCHRBank2[tileYDown];
8000      tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
8001      palette = ((attrCode & 0x7) << 2) | ((attrCode & 0x80) >> 2);
8002      switch (pixelPositionEnd - pixelPosition) {
8003        case 7:
8004          this.frameBuffer[pixelPosition + 6] = this.gbcBGPalette[palette | tile[tileYLine | 6]];
8005        case 6:
8006          this.frameBuffer[pixelPosition + 5] = this.gbcBGPalette[palette | tile[tileYLine | 5]];
8007        case 5:
8008          this.frameBuffer[pixelPosition + 4] = this.gbcBGPalette[palette | tile[tileYLine | 4]];
8009        case 4:
8010          this.frameBuffer[pixelPosition + 3] = this.gbcBGPalette[palette | tile[tileYLine | 3]];
8011        case 3:
8012          this.frameBuffer[pixelPosition + 2] = this.gbcBGPalette[palette | tile[tileYLine | 2]];
8013        case 2:
8014          this.frameBuffer[pixelPosition + 1] = this.gbcBGPalette[palette | tile[tileYLine | 1]];
8015        case 1:
8016          this.frameBuffer[pixelPosition] = this.gbcBGPalette[palette | tile[tileYLine]];
8017      }
8018    }
8019  }
8020}
8021GameBoyCore.prototype.BGGBCLayerRenderNoPriorityFlagging = function (scanlineToRender) {
8022  var scrollYAdjusted = (this.backgroundY + scanlineToRender) & 0xFF;            //The line of the BG we're at.
8023  var tileYLine = (scrollYAdjusted & 7) << 3;
8024  var tileYDown = this.gfxBackgroundCHRBankPosition | ((scrollYAdjusted & 0xF8) << 2);  //The row of cached tiles we're fetching from.
8025  var scrollXAdjusted = (this.backgroundX + this.currentX) & 0xFF;            //The scroll amount of the BG.
8026  var pixelPosition = this.pixelStart + this.currentX;                  //Current pixel we're working on.
8027  var pixelPositionEnd = this.pixelStart + ((this.gfxWindowDisplay && (scanlineToRender - this.windowY) >= 0) ? Math.min(Math.max(this.windowX, 0) + this.currentX, this.pixelEnd) : this.pixelEnd);  //Make sure we do at most 160 pixels a scanline.
8028  var tileNumber = tileYDown + (scrollXAdjusted >> 3);
8029  var chrCode = this.BGCHRBank1[tileNumber];
8030  if (chrCode < this.gfxBackgroundBankOffset) {
8031    chrCode |= 0x100;
8032  }
8033  var attrCode = this.BGCHRBank2[tileNumber];
8034  var tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
8035  var palette = (attrCode & 0x7) << 2;
8036  for (var texel = (scrollXAdjusted & 0x7); texel < 8 && pixelPosition < pixelPositionEnd && scrollXAdjusted < 0x100; ++scrollXAdjusted) {
8037    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[tileYLine | texel++]];
8038  }
8039  var scrollXAdjustedAligned = Math.min(pixelPositionEnd - pixelPosition, 0x100 - scrollXAdjusted) >> 3;
8040  scrollXAdjusted += scrollXAdjustedAligned << 3;
8041  scrollXAdjustedAligned += tileNumber;
8042  while (tileNumber < scrollXAdjustedAligned) {
8043    chrCode = this.BGCHRBank1[++tileNumber];
8044    if (chrCode < this.gfxBackgroundBankOffset) {
8045      chrCode |= 0x100;
8046    }
8047    attrCode = this.BGCHRBank2[tileNumber];
8048    tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
8049    palette = (attrCode & 0x7) << 2;
8050    texel = tileYLine;
8051    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8052    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8053    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8054    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8055    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8056    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8057    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8058    this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel]];
8059  }
8060  if (pixelPosition < pixelPositionEnd) {
8061    if (scrollXAdjusted < 0x100) {
8062      chrCode = this.BGCHRBank1[++tileNumber];
8063      if (chrCode < this.gfxBackgroundBankOffset) {
8064        chrCode |= 0x100;
8065      }
8066      attrCode = this.BGCHRBank2[tileNumber];
8067      tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
8068      palette = (attrCode & 0x7) << 2;
8069      for (texel = tileYLine - 1; pixelPosition < pixelPositionEnd && scrollXAdjusted < 0x100; ++scrollXAdjusted) {
8070        this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[++texel]];
8071      }
8072    }
8073    scrollXAdjustedAligned = ((pixelPositionEnd - pixelPosition) >> 3) + tileYDown;
8074    while (tileYDown < scrollXAdjustedAligned) {
8075      chrCode = this.BGCHRBank1[tileYDown];
8076      if (chrCode < this.gfxBackgroundBankOffset) {
8077        chrCode |= 0x100;
8078      }
8079      attrCode = this.BGCHRBank2[tileYDown++];
8080      tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
8081      palette = (attrCode & 0x7) << 2;
8082      texel = tileYLine;
8083      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8084      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8085      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8086      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8087      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8088      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8089      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8090      this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel]];
8091    }
8092    if (pixelPosition < pixelPositionEnd) {
8093      chrCode = this.BGCHRBank1[tileYDown];
8094      if (chrCode < this.gfxBackgroundBankOffset) {
8095        chrCode |= 0x100;
8096      }
8097      attrCode = this.BGCHRBank2[tileYDown];
8098      tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
8099      palette = (attrCode & 0x7) << 2;
8100      switch (pixelPositionEnd - pixelPosition) {
8101        case 7:
8102          this.frameBuffer[pixelPosition + 6] = this.gbcBGPalette[palette | tile[tileYLine | 6]];
8103        case 6:
8104          this.frameBuffer[pixelPosition + 5] = this.gbcBGPalette[palette | tile[tileYLine | 5]];
8105        case 5:
8106          this.frameBuffer[pixelPosition + 4] = this.gbcBGPalette[palette | tile[tileYLine | 4]];
8107        case 4:
8108          this.frameBuffer[pixelPosition + 3] = this.gbcBGPalette[palette | tile[tileYLine | 3]];
8109        case 3:
8110          this.frameBuffer[pixelPosition + 2] = this.gbcBGPalette[palette | tile[tileYLine | 2]];
8111        case 2:
8112          this.frameBuffer[pixelPosition + 1] = this.gbcBGPalette[palette | tile[tileYLine | 1]];
8113        case 1:
8114          this.frameBuffer[pixelPosition] = this.gbcBGPalette[palette | tile[tileYLine]];
8115      }
8116    }
8117  }
8118}
8119GameBoyCore.prototype.WindowGBLayerRender = function (scanlineToRender) {
8120  if (this.gfxWindowDisplay) {                  //Is the window enabled?
8121    var scrollYAdjusted = scanlineToRender - this.windowY;    //The line of the BG we're at.
8122    if (scrollYAdjusted >= 0) {
8123      var scrollXRangeAdjusted = (this.windowX > 0) ? (this.windowX + this.currentX) : this.currentX;
8124      var pixelPosition = this.pixelStart + scrollXRangeAdjusted;
8125      var pixelPositionEnd = this.pixelStart + this.pixelEnd;
8126      if (pixelPosition < pixelPositionEnd) {
8127        var tileYLine = (scrollYAdjusted & 0x7) << 3;
8128        var tileNumber = (this.gfxWindowCHRBankPosition | ((scrollYAdjusted & 0xF8) << 2)) + (this.currentX >> 3);
8129        var chrCode = this.BGCHRBank1[tileNumber];
8130        if (chrCode < this.gfxBackgroundBankOffset) {
8131          chrCode |= 0x100;
8132        }
8133        var tile = this.tileCache[chrCode];
8134        var texel = (scrollXRangeAdjusted - this.windowX) & 0x7;
8135        scrollXRangeAdjusted = Math.min(8, texel + pixelPositionEnd - pixelPosition);
8136        while (texel < scrollXRangeAdjusted) {
8137          this.frameBuffer[pixelPosition++] = this.BGPalette[tile[tileYLine | texel++]];
8138        }
8139        scrollXRangeAdjusted = tileNumber + ((pixelPositionEnd - pixelPosition) >> 3);
8140        while (tileNumber < scrollXRangeAdjusted) {
8141          chrCode = this.BGCHRBank1[++tileNumber];
8142          if (chrCode < this.gfxBackgroundBankOffset) {
8143            chrCode |= 0x100;
8144          }
8145          tile = this.tileCache[chrCode];
8146          texel = tileYLine;
8147          this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
8148          this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
8149          this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
8150          this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
8151          this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
8152          this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
8153          this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel++]];
8154          this.frameBuffer[pixelPosition++] = this.BGPalette[tile[texel]];
8155        }
8156        if (pixelPosition < pixelPositionEnd) {
8157          chrCode = this.BGCHRBank1[++tileNumber];
8158          if (chrCode < this.gfxBackgroundBankOffset) {
8159            chrCode |= 0x100;
8160          }
8161          tile = this.tileCache[chrCode];
8162          switch (pixelPositionEnd - pixelPosition) {
8163            case 7:
8164              this.frameBuffer[pixelPosition + 6] = this.BGPalette[tile[tileYLine | 6]];
8165            case 6:
8166              this.frameBuffer[pixelPosition + 5] = this.BGPalette[tile[tileYLine | 5]];
8167            case 5:
8168              this.frameBuffer[pixelPosition + 4] = this.BGPalette[tile[tileYLine | 4]];
8169            case 4:
8170              this.frameBuffer[pixelPosition + 3] = this.BGPalette[tile[tileYLine | 3]];
8171            case 3:
8172              this.frameBuffer[pixelPosition + 2] = this.BGPalette[tile[tileYLine | 2]];
8173            case 2:
8174              this.frameBuffer[pixelPosition + 1] = this.BGPalette[tile[tileYLine | 1]];
8175            case 1:
8176              this.frameBuffer[pixelPosition] = this.BGPalette[tile[tileYLine]];
8177          }
8178        }
8179      }
8180    }
8181  }
8182}
8183GameBoyCore.prototype.WindowGBCLayerRender = function (scanlineToRender) {
8184  if (this.gfxWindowDisplay) {                  //Is the window enabled?
8185    var scrollYAdjusted = scanlineToRender - this.windowY;    //The line of the BG we're at.
8186    if (scrollYAdjusted >= 0) {
8187      var scrollXRangeAdjusted = (this.windowX > 0) ? (this.windowX + this.currentX) : this.currentX;
8188      var pixelPosition = this.pixelStart + scrollXRangeAdjusted;
8189      var pixelPositionEnd = this.pixelStart + this.pixelEnd;
8190      if (pixelPosition < pixelPositionEnd) {
8191        var tileYLine = (scrollYAdjusted & 0x7) << 3;
8192        var tileNumber = (this.gfxWindowCHRBankPosition | ((scrollYAdjusted & 0xF8) << 2)) + (this.currentX >> 3);
8193        var chrCode = this.BGCHRBank1[tileNumber];
8194        if (chrCode < this.gfxBackgroundBankOffset) {
8195          chrCode |= 0x100;
8196        }
8197        var attrCode = this.BGCHRBank2[tileNumber];
8198        var tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
8199        var palette = ((attrCode & 0x7) << 2) | ((attrCode & 0x80) >> 2);
8200        var texel = (scrollXRangeAdjusted - this.windowX) & 0x7;
8201        scrollXRangeAdjusted = Math.min(8, texel + pixelPositionEnd - pixelPosition);
8202        while (texel < scrollXRangeAdjusted) {
8203          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[tileYLine | texel++]];
8204        }
8205        scrollXRangeAdjusted = tileNumber + ((pixelPositionEnd - pixelPosition) >> 3);
8206        while (tileNumber < scrollXRangeAdjusted) {
8207          chrCode = this.BGCHRBank1[++tileNumber];
8208          if (chrCode < this.gfxBackgroundBankOffset) {
8209            chrCode |= 0x100;
8210          }
8211          attrCode = this.BGCHRBank2[tileNumber];
8212          tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
8213          palette = ((attrCode & 0x7) << 2) | ((attrCode & 0x80) >> 2);
8214          texel = tileYLine;
8215          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8216          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8217          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8218          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8219          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8220          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8221          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8222          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel]];
8223        }
8224        if (pixelPosition < pixelPositionEnd) {
8225          chrCode = this.BGCHRBank1[++tileNumber];
8226          if (chrCode < this.gfxBackgroundBankOffset) {
8227            chrCode |= 0x100;
8228          }
8229          attrCode = this.BGCHRBank2[tileNumber];
8230          tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
8231          palette = ((attrCode & 0x7) << 2) | ((attrCode & 0x80) >> 2);
8232          switch (pixelPositionEnd - pixelPosition) {
8233            case 7:
8234              this.frameBuffer[pixelPosition + 6] = this.gbcBGPalette[palette | tile[tileYLine | 6]];
8235            case 6:
8236              this.frameBuffer[pixelPosition + 5] = this.gbcBGPalette[palette | tile[tileYLine | 5]];
8237            case 5:
8238              this.frameBuffer[pixelPosition + 4] = this.gbcBGPalette[palette | tile[tileYLine | 4]];
8239            case 4:
8240              this.frameBuffer[pixelPosition + 3] = this.gbcBGPalette[palette | tile[tileYLine | 3]];
8241            case 3:
8242              this.frameBuffer[pixelPosition + 2] = this.gbcBGPalette[palette | tile[tileYLine | 2]];
8243            case 2:
8244              this.frameBuffer[pixelPosition + 1] = this.gbcBGPalette[palette | tile[tileYLine | 1]];
8245            case 1:
8246              this.frameBuffer[pixelPosition] = this.gbcBGPalette[palette | tile[tileYLine]];
8247          }
8248        }
8249      }
8250    }
8251  }
8252}
8253GameBoyCore.prototype.WindowGBCLayerRenderNoPriorityFlagging = function (scanlineToRender) {
8254  if (this.gfxWindowDisplay) {                  //Is the window enabled?
8255    var scrollYAdjusted = scanlineToRender - this.windowY;    //The line of the BG we're at.
8256    if (scrollYAdjusted >= 0) {
8257      var scrollXRangeAdjusted = (this.windowX > 0) ? (this.windowX + this.currentX) : this.currentX;
8258      var pixelPosition = this.pixelStart + scrollXRangeAdjusted;
8259      var pixelPositionEnd = this.pixelStart + this.pixelEnd;
8260      if (pixelPosition < pixelPositionEnd) {
8261        var tileYLine = (scrollYAdjusted & 0x7) << 3;
8262        var tileNumber = (this.gfxWindowCHRBankPosition | ((scrollYAdjusted & 0xF8) << 2)) + (this.currentX >> 3);
8263        var chrCode = this.BGCHRBank1[tileNumber];
8264        if (chrCode < this.gfxBackgroundBankOffset) {
8265          chrCode |= 0x100;
8266        }
8267        var attrCode = this.BGCHRBank2[tileNumber];
8268        var tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
8269        var palette = (attrCode & 0x7) << 2;
8270        var texel = (scrollXRangeAdjusted - this.windowX) & 0x7;
8271        scrollXRangeAdjusted = Math.min(8, texel + pixelPositionEnd - pixelPosition);
8272        while (texel < scrollXRangeAdjusted) {
8273          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[tileYLine | texel++]];
8274        }
8275        scrollXRangeAdjusted = tileNumber + ((pixelPositionEnd - pixelPosition) >> 3);
8276        while (tileNumber < scrollXRangeAdjusted) {
8277          chrCode = this.BGCHRBank1[++tileNumber];
8278          if (chrCode < this.gfxBackgroundBankOffset) {
8279            chrCode |= 0x100;
8280          }
8281          attrCode = this.BGCHRBank2[tileNumber];
8282          tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
8283          palette = (attrCode & 0x7) << 2;
8284          texel = tileYLine;
8285          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8286          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8287          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8288          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8289          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8290          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8291          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel++]];
8292          this.frameBuffer[pixelPosition++] = this.gbcBGPalette[palette | tile[texel]];
8293        }
8294        if (pixelPosition < pixelPositionEnd) {
8295          chrCode = this.BGCHRBank1[++tileNumber];
8296          if (chrCode < this.gfxBackgroundBankOffset) {
8297            chrCode |= 0x100;
8298          }
8299          attrCode = this.BGCHRBank2[tileNumber];
8300          tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | chrCode];
8301          palette = (attrCode & 0x7) << 2;
8302          switch (pixelPositionEnd - pixelPosition) {
8303            case 7:
8304              this.frameBuffer[pixelPosition + 6] = this.gbcBGPalette[palette | tile[tileYLine | 6]];
8305            case 6:
8306              this.frameBuffer[pixelPosition + 5] = this.gbcBGPalette[palette | tile[tileYLine | 5]];
8307            case 5:
8308              this.frameBuffer[pixelPosition + 4] = this.gbcBGPalette[palette | tile[tileYLine | 4]];
8309            case 4:
8310              this.frameBuffer[pixelPosition + 3] = this.gbcBGPalette[palette | tile[tileYLine | 3]];
8311            case 3:
8312              this.frameBuffer[pixelPosition + 2] = this.gbcBGPalette[palette | tile[tileYLine | 2]];
8313            case 2:
8314              this.frameBuffer[pixelPosition + 1] = this.gbcBGPalette[palette | tile[tileYLine | 1]];
8315            case 1:
8316              this.frameBuffer[pixelPosition] = this.gbcBGPalette[palette | tile[tileYLine]];
8317          }
8318        }
8319      }
8320    }
8321  }
8322}
8323GameBoyCore.prototype.SpriteGBLayerRender = function (scanlineToRender) {
8324  if (this.gfxSpriteShow) {                    //Are sprites enabled?
8325    var lineAdjusted = scanlineToRender + 0x10;
8326    var OAMAddress = 0xFE00;
8327    var yoffset = 0;
8328    var xcoord = 1;
8329    var xCoordStart = 0;
8330    var xCoordEnd = 0;
8331    var attrCode = 0;
8332    var palette = 0;
8333    var tile = null;
8334    var data = 0;
8335    var spriteCount = 0;
8336    var length = 0;
8337    var currentPixel = 0;
8338    var linePixel = 0;
8339    //Clear our x-coord sort buffer:
8340    while (xcoord < 168) {
8341      this.sortBuffer[xcoord++] = 0xFF;
8342    }
8343    if (this.gfxSpriteNormalHeight) {
8344      //Draw the visible sprites:
8345      for (var length = this.findLowestSpriteDrawable(lineAdjusted, 0x7); spriteCount < length; ++spriteCount) {
8346        OAMAddress = this.OAMAddressCache[spriteCount];
8347        yoffset = (lineAdjusted - this.memory[OAMAddress]) << 3;
8348        attrCode = this.memory[OAMAddress | 3];
8349        palette = (attrCode & 0x10) >> 2;
8350        tile = this.tileCache[((attrCode & 0x60) << 4) | this.memory[OAMAddress | 0x2]];
8351        linePixel = xCoordStart = this.memory[OAMAddress | 1];
8352        xCoordEnd = Math.min(168 - linePixel, 8);
8353        xcoord = (linePixel > 7) ? 0 : (8 - linePixel);
8354        for (currentPixel = this.pixelStart + ((linePixel > 8) ? (linePixel - 8) : 0); xcoord < xCoordEnd; ++xcoord, ++currentPixel, ++linePixel) {
8355          if (this.sortBuffer[linePixel] > xCoordStart) {
8356            if (this.frameBuffer[currentPixel] >= 0x2000000) {
8357              data = tile[yoffset | xcoord];
8358              if (data > 0) {
8359                this.frameBuffer[currentPixel] = this.OBJPalette[palette | data];
8360                this.sortBuffer[linePixel] = xCoordStart;
8361              }
8362            }
8363            else if (this.frameBuffer[currentPixel] < 0x1000000) {
8364              data = tile[yoffset | xcoord];
8365              if (data > 0 && attrCode < 0x80) {
8366                this.frameBuffer[currentPixel] = this.OBJPalette[palette | data];
8367                this.sortBuffer[linePixel] = xCoordStart;
8368              }
8369            }
8370          }
8371        }
8372      }
8373    }
8374    else {
8375      //Draw the visible sprites:
8376      for (var length = this.findLowestSpriteDrawable(lineAdjusted, 0xF); spriteCount < length; ++spriteCount) {
8377        OAMAddress = this.OAMAddressCache[spriteCount];
8378        yoffset = (lineAdjusted - this.memory[OAMAddress]) << 3;
8379        attrCode = this.memory[OAMAddress | 3];
8380        palette = (attrCode & 0x10) >> 2;
8381        if ((attrCode & 0x40) == (0x40 & yoffset)) {
8382          tile = this.tileCache[((attrCode & 0x60) << 4) | (this.memory[OAMAddress | 0x2] & 0xFE)];
8383        }
8384        else {
8385          tile = this.tileCache[((attrCode & 0x60) << 4) | this.memory[OAMAddress | 0x2] | 1];
8386        }
8387        yoffset &= 0x3F;
8388        linePixel = xCoordStart = this.memory[OAMAddress | 1];
8389        xCoordEnd = Math.min(168 - linePixel, 8);
8390        xcoord = (linePixel > 7) ? 0 : (8 - linePixel);
8391        for (currentPixel = this.pixelStart + ((linePixel > 8) ? (linePixel - 8) : 0); xcoord < xCoordEnd; ++xcoord, ++currentPixel, ++linePixel) {
8392          if (this.sortBuffer[linePixel] > xCoordStart) {
8393            if (this.frameBuffer[currentPixel] >= 0x2000000) {
8394              data = tile[yoffset | xcoord];
8395              if (data > 0) {
8396                this.frameBuffer[currentPixel] = this.OBJPalette[palette | data];
8397                this.sortBuffer[linePixel] = xCoordStart;
8398              }
8399            }
8400            else if (this.frameBuffer[currentPixel] < 0x1000000) {
8401              data = tile[yoffset | xcoord];
8402              if (data > 0 && attrCode < 0x80) {
8403                this.frameBuffer[currentPixel] = this.OBJPalette[palette | data];
8404                this.sortBuffer[linePixel] = xCoordStart;
8405              }
8406            }
8407          }
8408        }
8409      }
8410    }
8411  }
8412}
8413GameBoyCore.prototype.findLowestSpriteDrawable = function (scanlineToRender, drawableRange) {
8414  var address = 0xFE00;
8415  var spriteCount = 0;
8416  var diff = 0;
8417  while (address < 0xFEA0 && spriteCount < 10) {
8418    diff = scanlineToRender - this.memory[address];
8419    if ((diff & drawableRange) == diff) {
8420      this.OAMAddressCache[spriteCount++] = address;
8421    }
8422    address += 4;
8423  }
8424  return spriteCount;
8425}
8426GameBoyCore.prototype.SpriteGBCLayerRender = function (scanlineToRender) {
8427  if (this.gfxSpriteShow) {                    //Are sprites enabled?
8428    var OAMAddress = 0xFE00;
8429    var lineAdjusted = scanlineToRender + 0x10;
8430    var yoffset = 0;
8431    var xcoord = 0;
8432    var endX = 0;
8433    var xCounter = 0;
8434    var attrCode = 0;
8435    var palette = 0;
8436    var tile = null;
8437    var data = 0;
8438    var currentPixel = 0;
8439    var spriteCount = 0;
8440    if (this.gfxSpriteNormalHeight) {
8441      for (; OAMAddress < 0xFEA0 && spriteCount < 10; OAMAddress += 4) {
8442        yoffset = lineAdjusted - this.memory[OAMAddress];
8443        if ((yoffset & 0x7) == yoffset) {
8444          xcoord = this.memory[OAMAddress | 1] - 8;
8445          endX = Math.min(160, xcoord + 8);
8446          attrCode = this.memory[OAMAddress | 3];
8447          palette = (attrCode & 7) << 2;
8448          tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | this.memory[OAMAddress | 2]];
8449          xCounter = (xcoord > 0) ? xcoord : 0;
8450          xcoord -= yoffset << 3;
8451          for (currentPixel = this.pixelStart + xCounter; xCounter < endX; ++xCounter, ++currentPixel) {
8452            if (this.frameBuffer[currentPixel] >= 0x2000000) {
8453              data = tile[xCounter - xcoord];
8454              if (data > 0) {
8455                this.frameBuffer[currentPixel] = this.gbcOBJPalette[palette | data];
8456              }
8457            }
8458            else if (this.frameBuffer[currentPixel] < 0x1000000) {
8459              data = tile[xCounter - xcoord];
8460              if (data > 0 && attrCode < 0x80) {    //Don't optimize for attrCode, as LICM-capable JITs should optimize its checks.
8461                this.frameBuffer[currentPixel] = this.gbcOBJPalette[palette | data];
8462              }
8463            }
8464          }
8465          ++spriteCount;
8466        }
8467      }
8468    }
8469    else {
8470      for (; OAMAddress < 0xFEA0 && spriteCount < 10; OAMAddress += 4) {
8471        yoffset = lineAdjusted - this.memory[OAMAddress];
8472        if ((yoffset & 0xF) == yoffset) {
8473          xcoord = this.memory[OAMAddress | 1] - 8;
8474          endX = Math.min(160, xcoord + 8);
8475          attrCode = this.memory[OAMAddress | 3];
8476          palette = (attrCode & 7) << 2;
8477          if ((attrCode & 0x40) == (0x40 & (yoffset << 3))) {
8478            tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | (this.memory[OAMAddress | 0x2] & 0xFE)];
8479          }
8480          else {
8481            tile = this.tileCache[((attrCode & 0x08) << 8) | ((attrCode & 0x60) << 4) | this.memory[OAMAddress | 0x2] | 1];
8482          }
8483          xCounter = (xcoord > 0) ? xcoord : 0;
8484          xcoord -= (yoffset & 0x7) << 3;
8485          for (currentPixel = this.pixelStart + xCounter; xCounter < endX; ++xCounter, ++currentPixel) {
8486            if (this.frameBuffer[currentPixel] >= 0x2000000) {
8487              data = tile[xCounter - xcoord];
8488              if (data > 0) {
8489                this.frameBuffer[currentPixel] = this.gbcOBJPalette[palette | data];
8490              }
8491            }
8492            else if (this.frameBuffer[currentPixel] < 0x1000000) {
8493              data = tile[xCounter - xcoord];
8494              if (data > 0 && attrCode < 0x80) {    //Don't optimize for attrCode, as LICM-capable JITs should optimize its checks.
8495                this.frameBuffer[currentPixel] = this.gbcOBJPalette[palette | data];
8496              }
8497            }
8498          }
8499          ++spriteCount;
8500        }
8501      }
8502    }
8503  }
8504}
8505//Generate only a single tile line for the GB tile cache mode:
8506GameBoyCore.prototype.generateGBTileLine = function (address) {
8507  var lineCopy = (this.memory[0x1 | address] << 8) | this.memory[0x9FFE & address];
8508  var tileBlock = this.tileCache[(address & 0x1FF0) >> 4];
8509  address = (address & 0xE) << 2;
8510  tileBlock[address | 7] = ((lineCopy & 0x100) >> 7) | (lineCopy & 0x1);
8511  tileBlock[address | 6] = ((lineCopy & 0x200) >> 8) | ((lineCopy & 0x2) >> 1);
8512  tileBlock[address | 5] = ((lineCopy & 0x400) >> 9) | ((lineCopy & 0x4) >> 2);
8513  tileBlock[address | 4] = ((lineCopy & 0x800) >> 10) | ((lineCopy & 0x8) >> 3);
8514  tileBlock[address | 3] = ((lineCopy & 0x1000) >> 11) | ((lineCopy & 0x10) >> 4);
8515  tileBlock[address | 2] = ((lineCopy & 0x2000) >> 12) | ((lineCopy & 0x20) >> 5);
8516  tileBlock[address | 1] = ((lineCopy & 0x4000) >> 13) | ((lineCopy & 0x40) >> 6);
8517  tileBlock[address] = ((lineCopy & 0x8000) >> 14) | ((lineCopy & 0x80) >> 7);
8518}
8519//Generate only a single tile line for the GBC tile cache mode (Bank 1):
8520GameBoyCore.prototype.generateGBCTileLineBank1 = function (address) {
8521  var lineCopy = (this.memory[0x1 | address] << 8) | this.memory[0x9FFE & address];
8522  address &= 0x1FFE;
8523  var tileBlock1 = this.tileCache[address >> 4];
8524  var tileBlock2 = this.tileCache[0x200 | (address >> 4)];
8525  var tileBlock3 = this.tileCache[0x400 | (address >> 4)];
8526  var tileBlock4 = this.tileCache[0x600 | (address >> 4)];
8527  address = (address & 0xE) << 2;
8528  var addressFlipped = 0x38 - address;
8529  tileBlock4[addressFlipped] = tileBlock2[address] = tileBlock3[addressFlipped | 7] = tileBlock1[address | 7] = ((lineCopy & 0x100) >> 7) | (lineCopy & 0x1);
8530  tileBlock4[addressFlipped | 1] = tileBlock2[address | 1] = tileBlock3[addressFlipped | 6] = tileBlock1[address | 6] = ((lineCopy & 0x200) >> 8) | ((lineCopy & 0x2) >> 1);
8531  tileBlock4[addressFlipped | 2] = tileBlock2[address | 2] = tileBlock3[addressFlipped | 5] = tileBlock1[address | 5] = ((lineCopy & 0x400) >> 9) | ((lineCopy & 0x4) >> 2);
8532  tileBlock4[addressFlipped | 3] = tileBlock2[address | 3] = tileBlock3[addressFlipped | 4] = tileBlock1[address | 4] = ((lineCopy & 0x800) >> 10) | ((lineCopy & 0x8) >> 3);
8533  tileBlock4[addressFlipped | 4] = tileBlock2[address | 4] = tileBlock3[addressFlipped | 3] = tileBlock1[address | 3] = ((lineCopy & 0x1000) >> 11) | ((lineCopy & 0x10) >> 4);
8534  tileBlock4[addressFlipped | 5] = tileBlock2[address | 5] = tileBlock3[addressFlipped | 2] = tileBlock1[address | 2] = ((lineCopy & 0x2000) >> 12) | ((lineCopy & 0x20) >> 5);
8535  tileBlock4[addressFlipped | 6] = tileBlock2[address | 6] = tileBlock3[addressFlipped | 1] = tileBlock1[address | 1] = ((lineCopy & 0x4000) >> 13) | ((lineCopy & 0x40) >> 6);
8536  tileBlock4[addressFlipped | 7] = tileBlock2[address | 7] = tileBlock3[addressFlipped] = tileBlock1[address] = ((lineCopy & 0x8000) >> 14) | ((lineCopy & 0x80) >> 7);
8537}
8538//Generate all the flip combinations for a full GBC VRAM bank 1 tile:
8539GameBoyCore.prototype.generateGBCTileBank1 = function (vramAddress) {
8540  var address = vramAddress >> 4;
8541  var tileBlock1 = this.tileCache[address];
8542  var tileBlock2 = this.tileCache[0x200 | address];
8543  var tileBlock3 = this.tileCache[0x400 | address];
8544  var tileBlock4 = this.tileCache[0x600 | address];
8545  var lineCopy = 0;
8546  vramAddress |= 0x8000;
8547  address = 0;
8548  var addressFlipped = 56;
8549  do {
8550    lineCopy = (this.memory[0x1 | vramAddress] << 8) | this.memory[vramAddress];
8551    tileBlock4[addressFlipped] = tileBlock2[address] = tileBlock3[addressFlipped | 7] = tileBlock1[address | 7] = ((lineCopy & 0x100) >> 7) | (lineCopy & 0x1);
8552    tileBlock4[addressFlipped | 1] = tileBlock2[address | 1] = tileBlock3[addressFlipped | 6] = tileBlock1[address | 6] = ((lineCopy & 0x200) >> 8) | ((lineCopy & 0x2) >> 1);
8553    tileBlock4[addressFlipped | 2] = tileBlock2[address | 2] = tileBlock3[addressFlipped | 5] = tileBlock1[address | 5] = ((lineCopy & 0x400) >> 9) | ((lineCopy & 0x4) >> 2);
8554    tileBlock4[addressFlipped | 3] = tileBlock2[address | 3] = tileBlock3[addressFlipped | 4] = tileBlock1[address | 4] = ((lineCopy & 0x800) >> 10) | ((lineCopy & 0x8) >> 3);
8555    tileBlock4[addressFlipped | 4] = tileBlock2[address | 4] = tileBlock3[addressFlipped | 3] = tileBlock1[address | 3] = ((lineCopy & 0x1000) >> 11) | ((lineCopy & 0x10) >> 4);
8556    tileBlock4[addressFlipped | 5] = tileBlock2[address | 5] = tileBlock3[addressFlipped | 2] = tileBlock1[address | 2] = ((lineCopy & 0x2000) >> 12) | ((lineCopy & 0x20) >> 5);
8557    tileBlock4[addressFlipped | 6] = tileBlock2[address | 6] = tileBlock3[addressFlipped | 1] = tileBlock1[address | 1] = ((lineCopy & 0x4000) >> 13) | ((lineCopy & 0x40) >> 6);
8558    tileBlock4[addressFlipped | 7] = tileBlock2[address | 7] = tileBlock3[addressFlipped] = tileBlock1[address] = ((lineCopy & 0x8000) >> 14) | ((lineCopy & 0x80) >> 7);
8559    address += 8;
8560    addressFlipped -= 8;
8561    vramAddress += 2;
8562  } while (addressFlipped > -1);
8563}
8564//Generate only a single tile line for the GBC tile cache mode (Bank 2):
8565GameBoyCore.prototype.generateGBCTileLineBank2 = function (address) {
8566  var lineCopy = (this.VRAM[0x1 | address] << 8) | this.VRAM[0x1FFE & address];
8567  var tileBlock1 = this.tileCache[0x800 | (address >> 4)];
8568  var tileBlock2 = this.tileCache[0xA00 | (address >> 4)];
8569  var tileBlock3 = this.tileCache[0xC00 | (address >> 4)];
8570  var tileBlock4 = this.tileCache[0xE00 | (address >> 4)];
8571  address = (address & 0xE) << 2;
8572  var addressFlipped = 0x38 - address;
8573  tileBlock4[addressFlipped] = tileBlock2[address] = tileBlock3[addressFlipped | 7] = tileBlock1[address | 7] = ((lineCopy & 0x100) >> 7) | (lineCopy & 0x1);
8574  tileBlock4[addressFlipped | 1] = tileBlock2[address | 1] = tileBlock3[addressFlipped | 6] = tileBlock1[address | 6] = ((lineCopy & 0x200) >> 8) | ((lineCopy & 0x2) >> 1);
8575  tileBlock4[addressFlipped | 2] = tileBlock2[address | 2] = tileBlock3[addressFlipped | 5] = tileBlock1[address | 5] = ((lineCopy & 0x400) >> 9) | ((lineCopy & 0x4) >> 2);
8576  tileBlock4[addressFlipped | 3] = tileBlock2[address | 3] = tileBlock3[addressFlipped | 4] = tileBlock1[address | 4] = ((lineCopy & 0x800) >> 10) | ((lineCopy & 0x8) >> 3);
8577  tileBlock4[addressFlipped | 4] = tileBlock2[address | 4] = tileBlock3[addressFlipped | 3] = tileBlock1[address | 3] = ((lineCopy & 0x1000) >> 11) | ((lineCopy & 0x10) >> 4);
8578  tileBlock4[addressFlipped | 5] = tileBlock2[address | 5] = tileBlock3[addressFlipped | 2] = tileBlock1[address | 2] = ((lineCopy & 0x2000) >> 12) | ((lineCopy & 0x20) >> 5);
8579  tileBlock4[addressFlipped | 6] = tileBlock2[address | 6] = tileBlock3[addressFlipped | 1] = tileBlock1[address | 1] = ((lineCopy & 0x4000) >> 13) | ((lineCopy & 0x40) >> 6);
8580  tileBlock4[addressFlipped | 7] = tileBlock2[address | 7] = tileBlock3[addressFlipped] = tileBlock1[address] = ((lineCopy & 0x8000) >> 14) | ((lineCopy & 0x80) >> 7);
8581}
8582//Generate all the flip combinations for a full GBC VRAM bank 2 tile:
8583GameBoyCore.prototype.generateGBCTileBank2 = function (vramAddress) {
8584  var address = vramAddress >> 4;
8585  var tileBlock1 = this.tileCache[0x800 | address];
8586  var tileBlock2 = this.tileCache[0xA00 | address];
8587  var tileBlock3 = this.tileCache[0xC00 | address];
8588  var tileBlock4 = this.tileCache[0xE00 | address];
8589  var lineCopy = 0;
8590  address = 0;
8591  var addressFlipped = 56;
8592  do {
8593    lineCopy = (this.VRAM[0x1 | vramAddress] << 8) | this.VRAM[vramAddress];
8594    tileBlock4[addressFlipped] = tileBlock2[address] = tileBlock3[addressFlipped | 7] = tileBlock1[address | 7] = ((lineCopy & 0x100) >> 7) | (lineCopy & 0x1);
8595    tileBlock4[addressFlipped | 1] = tileBlock2[address | 1] = tileBlock3[addressFlipped | 6] = tileBlock1[address | 6] = ((lineCopy & 0x200) >> 8) | ((lineCopy & 0x2) >> 1);
8596    tileBlock4[addressFlipped | 2] = tileBlock2[address | 2] = tileBlock3[addressFlipped | 5] = tileBlock1[address | 5] = ((lineCopy & 0x400) >> 9) | ((lineCopy & 0x4) >> 2);
8597    tileBlock4[addressFlipped | 3] = tileBlock2[address | 3] = tileBlock3[addressFlipped | 4] = tileBlock1[address | 4] = ((lineCopy & 0x800) >> 10) | ((lineCopy & 0x8) >> 3);
8598    tileBlock4[addressFlipped | 4] = tileBlock2[address | 4] = tileBlock3[addressFlipped | 3] = tileBlock1[address | 3] = ((lineCopy & 0x1000) >> 11) | ((lineCopy & 0x10) >> 4);
8599    tileBlock4[addressFlipped | 5] = tileBlock2[address | 5] = tileBlock3[addressFlipped | 2] = tileBlock1[address | 2] = ((lineCopy & 0x2000) >> 12) | ((lineCopy & 0x20) >> 5);
8600    tileBlock4[addressFlipped | 6] = tileBlock2[address | 6] = tileBlock3[addressFlipped | 1] = tileBlock1[address | 1] = ((lineCopy & 0x4000) >> 13) | ((lineCopy & 0x40) >> 6);
8601    tileBlock4[addressFlipped | 7] = tileBlock2[address | 7] = tileBlock3[addressFlipped] = tileBlock1[address] = ((lineCopy & 0x8000) >> 14) | ((lineCopy & 0x80) >> 7);
8602    address += 8;
8603    addressFlipped -= 8;
8604    vramAddress += 2;
8605  } while (addressFlipped > -1);
8606}
8607//Generate only a single tile line for the GB tile cache mode (OAM accessible range):
8608GameBoyCore.prototype.generateGBOAMTileLine = function (address) {
8609  var lineCopy = (this.memory[0x1 | address] << 8) | this.memory[0x9FFE & address];
8610  address &= 0x1FFE;
8611  var tileBlock1 = this.tileCache[address >> 4];
8612  var tileBlock2 = this.tileCache[0x200 | (address >> 4)];
8613  var tileBlock3 = this.tileCache[0x400 | (address >> 4)];
8614  var tileBlock4 = this.tileCache[0x600 | (address >> 4)];
8615  address = (address & 0xE) << 2;
8616  var addressFlipped = 0x38 - address;
8617  tileBlock4[addressFlipped] = tileBlock2[address] = tileBlock3[addressFlipped | 7] = tileBlock1[address | 7] = ((lineCopy & 0x100) >> 7) | (lineCopy & 0x1);
8618  tileBlock4[addressFlipped | 1] = tileBlock2[address | 1] = tileBlock3[addressFlipped | 6] = tileBlock1[address | 6] = ((lineCopy & 0x200) >> 8) | ((lineCopy & 0x2) >> 1);
8619  tileBlock4[addressFlipped | 2] = tileBlock2[address | 2] = tileBlock3[addressFlipped | 5] = tileBlock1[address | 5] = ((lineCopy & 0x400) >> 9) | ((lineCopy & 0x4) >> 2);
8620  tileBlock4[addressFlipped | 3] = tileBlock2[address | 3] = tileBlock3[addressFlipped | 4] = tileBlock1[address | 4] = ((lineCopy & 0x800) >> 10) | ((lineCopy & 0x8) >> 3);
8621  tileBlock4[addressFlipped | 4] = tileBlock2[address | 4] = tileBlock3[addressFlipped | 3] = tileBlock1[address | 3] = ((lineCopy & 0x1000) >> 11) | ((lineCopy & 0x10) >> 4);
8622  tileBlock4[addressFlipped | 5] = tileBlock2[address | 5] = tileBlock3[addressFlipped | 2] = tileBlock1[address | 2] = ((lineCopy & 0x2000) >> 12) | ((lineCopy & 0x20) >> 5);
8623  tileBlock4[addressFlipped | 6] = tileBlock2[address | 6] = tileBlock3[addressFlipped | 1] = tileBlock1[address | 1] = ((lineCopy & 0x4000) >> 13) | ((lineCopy & 0x40) >> 6);
8624  tileBlock4[addressFlipped | 7] = tileBlock2[address | 7] = tileBlock3[addressFlipped] = tileBlock1[address] = ((lineCopy & 0x8000) >> 14) | ((lineCopy & 0x80) >> 7);
8625}
8626GameBoyCore.prototype.graphicsJIT = function () {
8627  if (this.LCDisOn) {
8628    this.totalLinesPassed = 0;      //Mark frame for ensuring a JIT pass for the next framebuffer output.
8629    this.graphicsJITScanlineGroup();
8630  }
8631}
8632GameBoyCore.prototype.graphicsJITVBlank = function () {
8633  //JIT the graphics to v-blank framing:
8634  this.totalLinesPassed += this.queuedScanLines;
8635  this.graphicsJITScanlineGroup();
8636}
8637GameBoyCore.prototype.graphicsJITScanlineGroup = function () {
8638  //Normal rendering JIT, where we try to do groups of scanlines at once:
8639  while (this.queuedScanLines > 0) {
8640    this.renderScanLine(this.lastUnrenderedLine);
8641    if (this.lastUnrenderedLine < 143) {
8642      ++this.lastUnrenderedLine;
8643    }
8644    else {
8645      this.lastUnrenderedLine = 0;
8646    }
8647    --this.queuedScanLines;
8648  }
8649}
8650GameBoyCore.prototype.incrementScanLineQueue = function () {
8651  if (this.queuedScanLines < 144) {
8652    ++this.queuedScanLines;
8653  }
8654  else {
8655    this.currentX = 0;
8656    this.midScanlineOffset = -1;
8657    if (this.lastUnrenderedLine < 143) {
8658      ++this.lastUnrenderedLine;
8659    }
8660    else {
8661      this.lastUnrenderedLine = 0;
8662    }
8663  }
8664}
8665GameBoyCore.prototype.midScanLineJIT = function () {
8666  this.graphicsJIT();
8667  this.renderMidScanLine();
8668}
8669//Check for the highest priority IRQ to fire:
8670GameBoyCore.prototype.launchIRQ = function () {
8671  var bitShift = 0;
8672  var testbit = 1;
8673  do {
8674    //Check to see if an interrupt is enabled AND requested.
8675    if ((testbit & this.IRQLineMatched) == testbit) {
8676      this.IME = false;            //Reset the interrupt enabling.
8677      this.interruptsRequested -= testbit;  //Reset the interrupt request.
8678      this.IRQLineMatched = 0;        //Reset the IRQ assertion.
8679      //Interrupts have a certain clock cycle length:
8680      this.CPUTicks = 20;
8681      //Set the stack pointer to the current program counter value:
8682      this.stackPointer = (this.stackPointer - 1) & 0xFFFF;
8683      this.memoryWriter[this.stackPointer](this, this.stackPointer, this.programCounter >> 8);
8684      this.stackPointer = (this.stackPointer - 1) & 0xFFFF;
8685      this.memoryWriter[this.stackPointer](this, this.stackPointer, this.programCounter & 0xFF);
8686      //Set the program counter to the interrupt's address:
8687      this.programCounter = 0x40 | (bitShift << 3);
8688      //Clock the core for mid-instruction updates:
8689      this.updateCore();
8690      return;                  //We only want the highest priority interrupt.
8691    }
8692    testbit = 1 << ++bitShift;
8693  } while (bitShift < 5);
8694}
8695/*
8696  Check for IRQs to be fired while not in HALT:
8697*/
8698GameBoyCore.prototype.checkIRQMatching = function () {
8699  if (this.IME) {
8700    this.IRQLineMatched = this.interruptsEnabled & this.interruptsRequested & 0x1F;
8701  }
8702}
8703/*
8704  Handle the HALT opcode by predicting all IRQ cases correctly,
8705  then selecting the next closest IRQ firing from the prediction to
8706  clock up to. This prevents hacky looping that doesn't predict, but
8707  instead just clocks through the core update procedure by one which
8708  is very slow. Not many emulators do this because they have to cover
8709  all the IRQ prediction cases and they usually get them wrong.
8710*/
8711GameBoyCore.prototype.calculateHALTPeriod = function () {
8712  //Initialize our variables and start our prediction:
8713  if (!this.halt) {
8714    this.halt = true;
8715    var currentClocks = -1;
8716    var temp_var = 0;
8717    if (this.LCDisOn) {
8718      //If the LCD is enabled, then predict the LCD IRQs enabled:
8719      if ((this.interruptsEnabled & 0x1) == 0x1) {
8720        currentClocks = ((456 * (((this.modeSTAT == 1) ? 298 : 144) - this.actualScanLine)) - this.LCDTicks) << this.doubleSpeedShifter;
8721      }
8722      if ((this.interruptsEnabled & 0x2) == 0x2) {
8723        if (this.mode0TriggerSTAT) {
8724          temp_var = (this.clocksUntilMode0() - this.LCDTicks) << this.doubleSpeedShifter;
8725          if (temp_var <= currentClocks || currentClocks == -1) {
8726            currentClocks = temp_var;
8727          }
8728        }
8729        if (this.mode1TriggerSTAT && (this.interruptsEnabled & 0x1) == 0) {
8730          temp_var = ((456 * (((this.modeSTAT == 1) ? 298 : 144) - this.actualScanLine)) - this.LCDTicks) << this.doubleSpeedShifter;
8731          if (temp_var <= currentClocks || currentClocks == -1) {
8732            currentClocks = temp_var;
8733          }
8734        }
8735        if (this.mode2TriggerSTAT) {
8736          temp_var = (((this.actualScanLine >= 143) ? (456 * (154 - this.actualScanLine)) : 456) - this.LCDTicks) << this.doubleSpeedShifter;
8737          if (temp_var <= currentClocks || currentClocks == -1) {
8738            currentClocks = temp_var;
8739          }
8740        }
8741        if (this.LYCMatchTriggerSTAT && this.memory[0xFF45] <= 153) {
8742          temp_var = (this.clocksUntilLYCMatch() - this.LCDTicks) << this.doubleSpeedShifter;
8743          if (temp_var <= currentClocks || currentClocks == -1) {
8744            currentClocks = temp_var;
8745          }
8746        }
8747      }
8748    }
8749    if (this.TIMAEnabled && (this.interruptsEnabled & 0x4) == 0x4) {
8750      //CPU timer IRQ prediction:
8751      temp_var = ((0x100 - this.memory[0xFF05]) * this.TACClocker) - this.timerTicks;
8752      if (temp_var <= currentClocks || currentClocks == -1) {
8753        currentClocks = temp_var;
8754      }
8755    }
8756    if (this.serialTimer > 0 && (this.interruptsEnabled & 0x8) == 0x8) {
8757      //Serial IRQ prediction:
8758      if (this.serialTimer <= currentClocks || currentClocks == -1) {
8759        currentClocks = this.serialTimer;
8760      }
8761    }
8762  }
8763  else {
8764    var currentClocks = this.remainingClocks;
8765  }
8766  var maxClocks = (this.CPUCyclesTotal - this.emulatorTicks) << this.doubleSpeedShifter;
8767  if (currentClocks >= 0) {
8768    if (currentClocks <= maxClocks) {
8769      //Exit out of HALT normally:
8770      this.CPUTicks = Math.max(currentClocks, this.CPUTicks);
8771      this.updateCoreFull();
8772      this.halt = false;
8773      this.CPUTicks = 0;
8774    }
8775    else {
8776      //Still in HALT, clock only up to the clocks specified per iteration:
8777      this.CPUTicks = Math.max(maxClocks, this.CPUTicks);
8778      this.remainingClocks = currentClocks - this.CPUTicks;
8779    }
8780  }
8781  else {
8782    //Still in HALT, clock only up to the clocks specified per iteration:
8783    //Will stay in HALT forever (Stuck in HALT forever), but the APU and LCD are still clocked, so don't pause:
8784    this.CPUTicks += maxClocks;
8785  }
8786}
8787//Memory Reading:
8788GameBoyCore.prototype.memoryRead = function (address) {
8789  //Act as a wrapper for reading the returns from the compiled jumps to memory.
8790  return this.memoryReader[address](this, address);  //This seems to be faster than the usual if/else.
8791}
8792GameBoyCore.prototype.memoryHighRead = function (address) {
8793  //Act as a wrapper for reading the returns from the compiled jumps to memory.
8794  return this.memoryHighReader[address](this, address);  //This seems to be faster than the usual if/else.
8795}
8796GameBoyCore.prototype.memoryReadJumpCompile = function () {
8797  //Faster in some browsers, since we are doing less conditionals overall by implementing them in advance.
8798  for (var index = 0x0000; index <= 0xFFFF; index++) {
8799    if (index < 0x4000) {
8800      this.memoryReader[index] = this.memoryReadNormal;
8801    }
8802    else if (index < 0x8000) {
8803      this.memoryReader[index] = this.memoryReadROM;
8804    }
8805    else if (index < 0x9800) {
8806      this.memoryReader[index] = (this.cGBC) ? this.VRAMDATAReadCGBCPU : this.VRAMDATAReadDMGCPU;
8807    }
8808    else if (index < 0xA000) {
8809      this.memoryReader[index] = (this.cGBC) ? this.VRAMCHRReadCGBCPU : this.VRAMCHRReadDMGCPU;
8810    }
8811    else if (index >= 0xA000 && index < 0xC000) {
8812      if ((this.numRAMBanks == 1 / 16 && index < 0xA200) || this.numRAMBanks >= 1) {
8813        if (this.cMBC7) {
8814          this.memoryReader[index] = this.memoryReadMBC7;
8815        }
8816        else if (!this.cMBC3) {
8817          this.memoryReader[index] = this.memoryReadMBC;
8818        }
8819        else {
8820          //MBC3 RTC + RAM:
8821          this.memoryReader[index] = this.memoryReadMBC3;
8822        }
8823      }
8824      else {
8825        this.memoryReader[index] = this.memoryReadBAD;
8826      }
8827    }
8828    else if (index >= 0xC000 && index < 0xE000) {
8829      if (!this.cGBC || index < 0xD000) {
8830        this.memoryReader[index] = this.memoryReadNormal;
8831      }
8832      else {
8833        this.memoryReader[index] = this.memoryReadGBCMemory;
8834      }
8835    }
8836    else if (index >= 0xE000 && index < 0xFE00) {
8837      if (!this.cGBC || index < 0xF000) {
8838        this.memoryReader[index] = this.memoryReadECHONormal;
8839      }
8840      else {
8841        this.memoryReader[index] = this.memoryReadECHOGBCMemory;
8842      }
8843    }
8844    else if (index < 0xFEA0) {
8845      this.memoryReader[index] = this.memoryReadOAM;
8846    }
8847    else if (this.cGBC && index >= 0xFEA0 && index < 0xFF00) {
8848      this.memoryReader[index] = this.memoryReadNormal;
8849    }
8850    else if (index >= 0xFF00) {
8851      switch (index) {
8852        case 0xFF00:
8853          //JOYPAD:
8854          this.memoryHighReader[0] = this.memoryReader[0xFF00] = function (parentObj, address) {
8855            return 0xC0 | parentObj.memory[0xFF00];  //Top nibble returns as set.
8856          }
8857          break;
8858        case 0xFF01:
8859          //SB
8860          this.memoryHighReader[0x01] = this.memoryReader[0xFF01] = function (parentObj, address) {
8861            return (parentObj.memory[0xFF02] < 0x80) ? parentObj.memory[0xFF01] : 0xFF;
8862          }
8863          break;
8864        case 0xFF02:
8865          //SC
8866          if (this.cGBC) {
8867            this.memoryHighReader[0x02] = this.memoryReader[0xFF02] = function (parentObj, address) {
8868              return ((parentObj.serialTimer <= 0) ? 0x7C : 0xFC) | parentObj.memory[0xFF02];
8869            }
8870          }
8871          else {
8872            this.memoryHighReader[0x02] = this.memoryReader[0xFF02] = function (parentObj, address) {
8873              return ((parentObj.serialTimer <= 0) ? 0x7E : 0xFE) | parentObj.memory[0xFF02];
8874            }
8875          }
8876          break;
8877        case 0xFF04:
8878          //DIV
8879          this.memoryHighReader[0x04] = this.memoryReader[0xFF04] = function (parentObj, address) {
8880            parentObj.memory[0xFF04] = (parentObj.memory[0xFF04] + (parentObj.DIVTicks >> 8)) & 0xFF;
8881            parentObj.DIVTicks &= 0xFF;
8882            return parentObj.memory[0xFF04];
8883
8884          }
8885          break;
8886        case 0xFF07:
8887          this.memoryHighReader[0x07] = this.memoryReader[0xFF07] = function (parentObj, address) {
8888            return 0xF8 | parentObj.memory[0xFF07];
8889          }
8890          break;
8891        case 0xFF0F:
8892          //IF
8893          this.memoryHighReader[0x0F] = this.memoryReader[0xFF0F] = function (parentObj, address) {
8894            return 0xE0 | parentObj.interruptsRequested;
8895          }
8896          break;
8897        case 0xFF10:
8898          this.memoryHighReader[0x10] = this.memoryReader[0xFF10] = function (parentObj, address) {
8899            return 0x80 | parentObj.memory[0xFF10];
8900          }
8901          break;
8902        case 0xFF11:
8903          this.memoryHighReader[0x11] = this.memoryReader[0xFF11] = function (parentObj, address) {
8904            return 0x3F | parentObj.memory[0xFF11];
8905          }
8906          break;
8907        case 0xFF13:
8908          this.memoryHighReader[0x13] = this.memoryReader[0xFF13] = this.memoryReadBAD;
8909          break;
8910        case 0xFF14:
8911          this.memoryHighReader[0x14] = this.memoryReader[0xFF14] = function (parentObj, address) {
8912            return 0xBF | parentObj.memory[0xFF14];
8913          }
8914          break;
8915        case 0xFF16:
8916          this.memoryHighReader[0x16] = this.memoryReader[0xFF16] = function (parentObj, address) {
8917            return 0x3F | parentObj.memory[0xFF16];
8918          }
8919          break;
8920        case 0xFF18:
8921          this.memoryHighReader[0x18] = this.memoryReader[0xFF18] = this.memoryReadBAD;
8922          break;
8923        case 0xFF19:
8924          this.memoryHighReader[0x19] = this.memoryReader[0xFF19] = function (parentObj, address) {
8925            return 0xBF | parentObj.memory[0xFF19];
8926          }
8927          break;
8928        case 0xFF1A:
8929          this.memoryHighReader[0x1A] = this.memoryReader[0xFF1A] = function (parentObj, address) {
8930            return 0x7F | parentObj.memory[0xFF1A];
8931          }
8932          break;
8933        case 0xFF1B:
8934          this.memoryHighReader[0x1B] = this.memoryReader[0xFF1B] = this.memoryReadBAD;
8935          break;
8936        case 0xFF1C:
8937          this.memoryHighReader[0x1C] = this.memoryReader[0xFF1C] = function (parentObj, address) {
8938            return 0x9F | parentObj.memory[0xFF1C];
8939          }
8940          break;
8941        case 0xFF1D:
8942          this.memoryHighReader[0x1D] = this.memoryReader[0xFF1D] = function (parentObj, address) {
8943            return 0xFF;
8944          }
8945          break;
8946        case 0xFF1E:
8947          this.memoryHighReader[0x1E] = this.memoryReader[0xFF1E] = function (parentObj, address) {
8948            return 0xBF | parentObj.memory[0xFF1E];
8949          }
8950          break;
8951        case 0xFF1F:
8952        case 0xFF20:
8953          this.memoryHighReader[index & 0xFF] = this.memoryReader[index] = this.memoryReadBAD;
8954          break;
8955        case 0xFF23:
8956          this.memoryHighReader[0x23] = this.memoryReader[0xFF23] = function (parentObj, address) {
8957            return 0xBF | parentObj.memory[0xFF23];
8958          }
8959          break;
8960        case 0xFF26:
8961          this.memoryHighReader[0x26] = this.memoryReader[0xFF26] = function (parentObj, address) {
8962            parentObj.audioJIT();
8963            return 0x70 | parentObj.memory[0xFF26];
8964          }
8965          break;
8966        case 0xFF27:
8967        case 0xFF28:
8968        case 0xFF29:
8969        case 0xFF2A:
8970        case 0xFF2B:
8971        case 0xFF2C:
8972        case 0xFF2D:
8973        case 0xFF2E:
8974        case 0xFF2F:
8975          this.memoryHighReader[index & 0xFF] = this.memoryReader[index] = this.memoryReadBAD;
8976          break;
8977        case 0xFF30:
8978        case 0xFF31:
8979        case 0xFF32:
8980        case 0xFF33:
8981        case 0xFF34:
8982        case 0xFF35:
8983        case 0xFF36:
8984        case 0xFF37:
8985        case 0xFF38:
8986        case 0xFF39:
8987        case 0xFF3A:
8988        case 0xFF3B:
8989        case 0xFF3C:
8990        case 0xFF3D:
8991        case 0xFF3E:
8992        case 0xFF3F:
8993          this.memoryReader[index] = function (parentObj, address) {
8994            return (parentObj.channel3canPlay) ? parentObj.memory[0xFF00 | (parentObj.channel3lastSampleLookup >> 1)] : parentObj.memory[address];
8995          }
8996          this.memoryHighReader[index & 0xFF] = function (parentObj, address) {
8997            return (parentObj.channel3canPlay) ? parentObj.memory[0xFF00 | (parentObj.channel3lastSampleLookup >> 1)] : parentObj.memory[0xFF00 | address];
8998          }
8999          break;
9000        case 0xFF41:
9001          this.memoryHighReader[0x41] = this.memoryReader[0xFF41] = function (parentObj, address) {
9002            return 0x80 | parentObj.memory[0xFF41] | parentObj.modeSTAT;
9003          }
9004          break;
9005        case 0xFF42:
9006          this.memoryHighReader[0x42] = this.memoryReader[0xFF42] = function (parentObj, address) {
9007            return parentObj.backgroundY;
9008          }
9009          break;
9010        case 0xFF43:
9011          this.memoryHighReader[0x43] = this.memoryReader[0xFF43] = function (parentObj, address) {
9012            return parentObj.backgroundX;
9013          }
9014          break;
9015        case 0xFF44:
9016          this.memoryHighReader[0x44] = this.memoryReader[0xFF44] = function (parentObj, address) {
9017            return ((parentObj.LCDisOn) ? parentObj.memory[0xFF44] : 0);
9018          }
9019          break;
9020        case 0xFF4A:
9021          //WY
9022          this.memoryHighReader[0x4A] = this.memoryReader[0xFF4A] = function (parentObj, address) {
9023            return parentObj.windowY;
9024          }
9025          break;
9026        case 0xFF4F:
9027          this.memoryHighReader[0x4F] = this.memoryReader[0xFF4F] = function (parentObj, address) {
9028            return parentObj.currVRAMBank;
9029          }
9030          break;
9031        case 0xFF55:
9032          if (this.cGBC) {
9033            this.memoryHighReader[0x55] = this.memoryReader[0xFF55] = function (parentObj, address) {
9034              if (!parentObj.LCDisOn && parentObj.hdmaRunning) {  //Undocumented behavior alert: HDMA becomes GDMA when LCD is off (Worms Armageddon Fix).
9035                //DMA
9036                parentObj.DMAWrite((parentObj.memory[0xFF55] & 0x7F) + 1);
9037                parentObj.memory[0xFF55] = 0xFF;  //Transfer completed.
9038                parentObj.hdmaRunning = false;
9039              }
9040              return parentObj.memory[0xFF55];
9041            }
9042          }
9043          else {
9044            this.memoryReader[0xFF55] = this.memoryReadNormal;
9045            this.memoryHighReader[0x55] = this.memoryHighReadNormal;
9046          }
9047          break;
9048        case 0xFF56:
9049          if (this.cGBC) {
9050            this.memoryHighReader[0x56] = this.memoryReader[0xFF56] = function (parentObj, address) {
9051              //Return IR "not connected" status:
9052              return 0x3C | ((parentObj.memory[0xFF56] >= 0xC0) ? (0x2 | (parentObj.memory[0xFF56] & 0xC1)) : (parentObj.memory[0xFF56] & 0xC3));
9053            }
9054          }
9055          else {
9056            this.memoryReader[0xFF56] = this.memoryReadNormal;
9057            this.memoryHighReader[0x56] = this.memoryHighReadNormal;
9058          }
9059          break;
9060        case 0xFF6C:
9061          if (this.cGBC) {
9062            this.memoryHighReader[0x6C] = this.memoryReader[0xFF6C] = function (parentObj, address) {
9063              return 0xFE | parentObj.memory[0xFF6C];
9064            }
9065          }
9066          else {
9067            this.memoryHighReader[0x6C] = this.memoryReader[0xFF6C] = this.memoryReadBAD;
9068          }
9069          break;
9070        case 0xFF70:
9071          if (this.cGBC) {
9072            //SVBK
9073            this.memoryHighReader[0x70] = this.memoryReader[0xFF70] = function (parentObj, address) {
9074              return 0x40 | parentObj.memory[0xFF70];
9075            }
9076          }
9077          else {
9078            this.memoryHighReader[0x70] = this.memoryReader[0xFF70] = this.memoryReadBAD;
9079          }
9080          break;
9081        case 0xFF75:
9082          this.memoryHighReader[0x75] = this.memoryReader[0xFF75] = function (parentObj, address) {
9083            return 0x8F | parentObj.memory[0xFF75];
9084          }
9085          break;
9086        case 0xFF76:
9087        case 0xFF77:
9088          this.memoryHighReader[index & 0xFF] = this.memoryReader[index] = function (parentObj, address) {
9089            return 0;
9090          }
9091          break;
9092        case 0xFFFF:
9093          //IE
9094          this.memoryHighReader[0xFF] = this.memoryReader[0xFFFF] = function (parentObj, address) {
9095            return parentObj.interruptsEnabled;
9096          }
9097          break;
9098        default:
9099          this.memoryReader[index] = this.memoryReadNormal;
9100          this.memoryHighReader[index & 0xFF] = this.memoryHighReadNormal;
9101      }
9102    }
9103    else {
9104      this.memoryReader[index] = this.memoryReadBAD;
9105    }
9106  }
9107}
9108GameBoyCore.prototype.memoryReadNormal = function (parentObj, address) {
9109  return parentObj.memory[address];
9110}
9111GameBoyCore.prototype.memoryHighReadNormal = function (parentObj, address) {
9112  return parentObj.memory[0xFF00 | address];
9113}
9114GameBoyCore.prototype.memoryReadROM = function (parentObj, address) {
9115  return parentObj.ROM[parentObj.currentROMBank + address];
9116}
9117GameBoyCore.prototype.memoryReadMBC = function (parentObj, address) {
9118  //Switchable RAM
9119  if (parentObj.MBCRAMBanksEnabled || settings[10]) {
9120    return parentObj.MBCRam[address + parentObj.currMBCRAMBankPosition];
9121  }
9122  //cout("Reading from disabled RAM.", 1);
9123  return 0xFF;
9124}
9125GameBoyCore.prototype.memoryReadMBC7 = function (parentObj, address) {
9126  //Switchable RAM
9127  if (parentObj.MBCRAMBanksEnabled || settings[10]) {
9128    switch (address) {
9129      case 0xA000:
9130      case 0xA060:
9131      case 0xA070:
9132        return 0;
9133      case 0xA080:
9134        //TODO: Gyro Control Register
9135        return 0;
9136      case 0xA050:
9137        //Y High Byte
9138        return parentObj.highY;
9139      case 0xA040:
9140        //Y Low Byte
9141        return parentObj.lowY;
9142      case 0xA030:
9143        //X High Byte
9144        return parentObj.highX;
9145      case 0xA020:
9146        //X Low Byte:
9147        return parentObj.lowX;
9148      default:
9149        return parentObj.MBCRam[address + parentObj.currMBCRAMBankPosition];
9150    }
9151  }
9152  //cout("Reading from disabled RAM.", 1);
9153  return 0xFF;
9154}
9155GameBoyCore.prototype.memoryReadMBC3 = function (parentObj, address) {
9156  //Switchable RAM
9157  if (parentObj.MBCRAMBanksEnabled || settings[10]) {
9158    switch (parentObj.currMBCRAMBank) {
9159      case 0x00:
9160      case 0x01:
9161      case 0x02:
9162      case 0x03:
9163        return parentObj.MBCRam[address + parentObj.currMBCRAMBankPosition];
9164        break;
9165      case 0x08:
9166        return parentObj.latchedSeconds;
9167        break;
9168      case 0x09:
9169        return parentObj.latchedMinutes;
9170        break;
9171      case 0x0A:
9172        return parentObj.latchedHours;
9173        break;
9174      case 0x0B:
9175        return parentObj.latchedLDays;
9176        break;
9177      case 0x0C:
9178        return (((parentObj.RTCDayOverFlow) ? 0x80 : 0) + ((parentObj.RTCHALT) ? 0x40 : 0)) + parentObj.latchedHDays;
9179    }
9180  }
9181  //cout("Reading from invalid or disabled RAM.", 1);
9182  return 0xFF;
9183}
9184GameBoyCore.prototype.memoryReadGBCMemory = function (parentObj, address) {
9185  return parentObj.GBCMemory[address + parentObj.gbcRamBankPosition];
9186}
9187GameBoyCore.prototype.memoryReadOAM = function (parentObj, address) {
9188  return (parentObj.modeSTAT > 1) ?  0xFF : parentObj.memory[address];
9189}
9190GameBoyCore.prototype.memoryReadECHOGBCMemory = function (parentObj, address) {
9191  return parentObj.GBCMemory[address + parentObj.gbcRamBankPositionECHO];
9192}
9193GameBoyCore.prototype.memoryReadECHONormal = function (parentObj, address) {
9194  return parentObj.memory[address - 0x2000];
9195}
9196GameBoyCore.prototype.memoryReadBAD = function (parentObj, address) {
9197  return 0xFF;
9198}
9199GameBoyCore.prototype.VRAMDATAReadCGBCPU = function (parentObj, address) {
9200  //CPU Side Reading The VRAM (Optimized for GameBoy Color)
9201  return (parentObj.modeSTAT > 2) ? 0xFF : ((parentObj.currVRAMBank == 0) ? parentObj.memory[address] : parentObj.VRAM[address & 0x1FFF]);
9202}
9203GameBoyCore.prototype.VRAMDATAReadDMGCPU = function (parentObj, address) {
9204  //CPU Side Reading The VRAM (Optimized for classic GameBoy)
9205  return (parentObj.modeSTAT > 2) ? 0xFF : parentObj.memory[address];
9206}
9207GameBoyCore.prototype.VRAMCHRReadCGBCPU = function (parentObj, address) {
9208  //CPU Side Reading the Character Data Map:
9209  return (parentObj.modeSTAT > 2) ? 0xFF : parentObj.BGCHRCurrentBank[address & 0x7FF];
9210}
9211GameBoyCore.prototype.VRAMCHRReadDMGCPU = function (parentObj, address) {
9212  //CPU Side Reading the Character Data Map:
9213  return (parentObj.modeSTAT > 2) ? 0xFF : parentObj.BGCHRBank1[address & 0x7FF];
9214}
9215GameBoyCore.prototype.setCurrentMBC1ROMBank = function () {
9216  //Read the cartridge ROM data from RAM memory:
9217  switch (this.ROMBank1offs) {
9218    case 0x00:
9219    case 0x20:
9220    case 0x40:
9221    case 0x60:
9222      //Bank calls for 0x00, 0x20, 0x40, and 0x60 are really for 0x01, 0x21, 0x41, and 0x61.
9223      this.currentROMBank = (this.ROMBank1offs % this.ROMBankEdge) << 14;
9224      break;
9225    default:
9226      this.currentROMBank = ((this.ROMBank1offs % this.ROMBankEdge) - 1) << 14;
9227  }
9228}
9229GameBoyCore.prototype.setCurrentMBC2AND3ROMBank = function () {
9230  //Read the cartridge ROM data from RAM memory:
9231  //Only map bank 0 to bank 1 here (MBC2 is like MBC1, but can only do 16 banks, so only the bank 0 quirk appears for MBC2):
9232  this.currentROMBank = Math.max((this.ROMBank1offs % this.ROMBankEdge) - 1, 0) << 14;
9233}
9234GameBoyCore.prototype.setCurrentMBC5ROMBank = function () {
9235  //Read the cartridge ROM data from RAM memory:
9236  this.currentROMBank = ((this.ROMBank1offs % this.ROMBankEdge) - 1) << 14;
9237}
9238//Memory Writing:
9239GameBoyCore.prototype.memoryWrite = function (address, data) {
9240  //Act as a wrapper for writing by compiled jumps to specific memory writing functions.
9241  this.memoryWriter[address](this, address, data);
9242}
9243//0xFFXX fast path:
9244GameBoyCore.prototype.memoryHighWrite = function (address, data) {
9245  //Act as a wrapper for writing by compiled jumps to specific memory writing functions.
9246  this.memoryHighWriter[address](this, address, data);
9247}
9248GameBoyCore.prototype.memoryWriteJumpCompile = function () {
9249  //Faster in some browsers, since we are doing less conditionals overall by implementing them in advance.
9250  for (var index = 0x0000; index <= 0xFFFF; index++) {
9251    if (index < 0x8000) {
9252      if (this.cMBC1) {
9253        if (index < 0x2000) {
9254          this.memoryWriter[index] = this.MBCWriteEnable;
9255        }
9256        else if (index < 0x4000) {
9257          this.memoryWriter[index] = this.MBC1WriteROMBank;
9258        }
9259        else if (index < 0x6000) {
9260          this.memoryWriter[index] = this.MBC1WriteRAMBank;
9261        }
9262        else {
9263          this.memoryWriter[index] = this.MBC1WriteType;
9264        }
9265      }
9266      else if (this.cMBC2) {
9267        if (index < 0x1000) {
9268          this.memoryWriter[index] = this.MBCWriteEnable;
9269        }
9270        else if (index >= 0x2100 && index < 0x2200) {
9271          this.memoryWriter[index] = this.MBC2WriteROMBank;
9272        }
9273        else {
9274          this.memoryWriter[index] = this.cartIgnoreWrite;
9275        }
9276      }
9277      else if (this.cMBC3) {
9278        if (index < 0x2000) {
9279          this.memoryWriter[index] = this.MBCWriteEnable;
9280        }
9281        else if (index < 0x4000) {
9282          this.memoryWriter[index] = this.MBC3WriteROMBank;
9283        }
9284        else if (index < 0x6000) {
9285          this.memoryWriter[index] = this.MBC3WriteRAMBank;
9286        }
9287        else {
9288          this.memoryWriter[index] = this.MBC3WriteRTCLatch;
9289        }
9290      }
9291      else if (this.cMBC5 || this.cRUMBLE || this.cMBC7) {
9292        if (index < 0x2000) {
9293          this.memoryWriter[index] = this.MBCWriteEnable;
9294        }
9295        else if (index < 0x3000) {
9296          this.memoryWriter[index] = this.MBC5WriteROMBankLow;
9297        }
9298        else if (index < 0x4000) {
9299          this.memoryWriter[index] = this.MBC5WriteROMBankHigh;
9300        }
9301        else if (index < 0x6000) {
9302          this.memoryWriter[index] = (this.cRUMBLE) ? this.RUMBLEWriteRAMBank : this.MBC5WriteRAMBank;
9303        }
9304        else {
9305          this.memoryWriter[index] = this.cartIgnoreWrite;
9306        }
9307      }
9308      else if (this.cHuC3) {
9309        if (index < 0x2000) {
9310          this.memoryWriter[index] = this.MBCWriteEnable;
9311        }
9312        else if (index < 0x4000) {
9313          this.memoryWriter[index] = this.MBC3WriteROMBank;
9314        }
9315        else if (index < 0x6000) {
9316          this.memoryWriter[index] = this.HuC3WriteRAMBank;
9317        }
9318        else {
9319          this.memoryWriter[index] = this.cartIgnoreWrite;
9320        }
9321      }
9322      else {
9323        this.memoryWriter[index] = this.cartIgnoreWrite;
9324      }
9325    }
9326    else if (index < 0x9000) {
9327      this.memoryWriter[index] = (this.cGBC) ? this.VRAMGBCDATAWrite : this.VRAMGBDATAWrite;
9328    }
9329    else if (index < 0x9800) {
9330      this.memoryWriter[index] = (this.cGBC) ? this.VRAMGBCDATAWrite : this.VRAMGBDATAUpperWrite;
9331    }
9332    else if (index < 0xA000) {
9333      this.memoryWriter[index] = (this.cGBC) ? this.VRAMGBCCHRMAPWrite : this.VRAMGBCHRMAPWrite;
9334    }
9335    else if (index < 0xC000) {
9336      if ((this.numRAMBanks == 1 / 16 && index < 0xA200) || this.numRAMBanks >= 1) {
9337        if (!this.cMBC3) {
9338          this.memoryWriter[index] = this.memoryWriteMBCRAM;
9339        }
9340        else {
9341          //MBC3 RTC + RAM:
9342          this.memoryWriter[index] = this.memoryWriteMBC3RAM;
9343        }
9344      }
9345      else {
9346        this.memoryWriter[index] = this.cartIgnoreWrite;
9347      }
9348    }
9349    else if (index < 0xE000) {
9350      if (this.cGBC && index >= 0xD000) {
9351        this.memoryWriter[index] = this.memoryWriteGBCRAM;
9352      }
9353      else {
9354        this.memoryWriter[index] = this.memoryWriteNormal;
9355      }
9356    }
9357    else if (index < 0xFE00) {
9358      if (this.cGBC && index >= 0xF000) {
9359        this.memoryWriter[index] = this.memoryWriteECHOGBCRAM;
9360      }
9361      else {
9362        this.memoryWriter[index] = this.memoryWriteECHONormal;
9363      }
9364    }
9365    else if (index <= 0xFEA0) {
9366      this.memoryWriter[index] = this.memoryWriteOAMRAM;
9367    }
9368    else if (index < 0xFF00) {
9369      if (this.cGBC) {                      //Only GBC has access to this RAM.
9370        this.memoryWriter[index] = this.memoryWriteNormal;
9371      }
9372      else {
9373        this.memoryWriter[index] = this.cartIgnoreWrite;
9374      }
9375    }
9376    else {
9377      //Start the I/O initialization by filling in the slots as normal memory:
9378      this.memoryWriter[index] = this.memoryWriteNormal;
9379      this.memoryHighWriter[index & 0xFF] = this.memoryHighWriteNormal;
9380    }
9381  }
9382  this.registerWriteJumpCompile();        //Compile the I/O write functions separately...
9383}
9384GameBoyCore.prototype.MBCWriteEnable = function (parentObj, address, data) {
9385  //MBC RAM Bank Enable/Disable:
9386  parentObj.MBCRAMBanksEnabled = ((data & 0x0F) == 0x0A);  //If lower nibble is 0x0A, then enable, otherwise disable.
9387}
9388GameBoyCore.prototype.MBC1WriteROMBank = function (parentObj, address, data) {
9389  //MBC1 ROM bank switching:
9390  parentObj.ROMBank1offs = (parentObj.ROMBank1offs & 0x60) | (data & 0x1F);
9391  parentObj.setCurrentMBC1ROMBank();
9392}
9393GameBoyCore.prototype.MBC1WriteRAMBank = function (parentObj, address, data) {
9394  //MBC1 RAM bank switching
9395  if (parentObj.MBC1Mode) {
9396    //4/32 Mode
9397    parentObj.currMBCRAMBank = data & 0x03;
9398    parentObj.currMBCRAMBankPosition = (parentObj.currMBCRAMBank << 13) - 0xA000;
9399  }
9400  else {
9401    //16/8 Mode
9402    parentObj.ROMBank1offs = ((data & 0x03) << 5) | (parentObj.ROMBank1offs & 0x1F);
9403    parentObj.setCurrentMBC1ROMBank();
9404  }
9405}
9406GameBoyCore.prototype.MBC1WriteType = function (parentObj, address, data) {
9407  //MBC1 mode setting:
9408  parentObj.MBC1Mode = ((data & 0x1) == 0x1);
9409  if (parentObj.MBC1Mode) {
9410    parentObj.ROMBank1offs &= 0x1F;
9411    parentObj.setCurrentMBC1ROMBank();
9412  }
9413  else {
9414    parentObj.currMBCRAMBank = 0;
9415    parentObj.currMBCRAMBankPosition = -0xA000;
9416  }
9417}
9418GameBoyCore.prototype.MBC2WriteROMBank = function (parentObj, address, data) {
9419  //MBC2 ROM bank switching:
9420  parentObj.ROMBank1offs = data & 0x0F;
9421  parentObj.setCurrentMBC2AND3ROMBank();
9422}
9423GameBoyCore.prototype.MBC3WriteROMBank = function (parentObj, address, data) {
9424  //MBC3 ROM bank switching:
9425  parentObj.ROMBank1offs = data & 0x7F;
9426  parentObj.setCurrentMBC2AND3ROMBank();
9427}
9428GameBoyCore.prototype.MBC3WriteRAMBank = function (parentObj, address, data) {
9429  parentObj.currMBCRAMBank = data;
9430  if (data < 4) {
9431    //MBC3 RAM bank switching
9432    parentObj.currMBCRAMBankPosition = (parentObj.currMBCRAMBank << 13) - 0xA000;
9433  }
9434}
9435GameBoyCore.prototype.MBC3WriteRTCLatch = function (parentObj, address, data) {
9436  if (data == 0) {
9437    parentObj.RTCisLatched = false;
9438  }
9439  else if (!parentObj.RTCisLatched) {
9440    //Copy over the current RTC time for reading.
9441    parentObj.RTCisLatched = true;
9442    parentObj.latchedSeconds = parentObj.RTCSeconds | 0;
9443    parentObj.latchedMinutes = parentObj.RTCMinutes;
9444    parentObj.latchedHours = parentObj.RTCHours;
9445    parentObj.latchedLDays = (parentObj.RTCDays & 0xFF);
9446    parentObj.latchedHDays = parentObj.RTCDays >> 8;
9447  }
9448}
9449GameBoyCore.prototype.MBC5WriteROMBankLow = function (parentObj, address, data) {
9450  //MBC5 ROM bank switching:
9451  parentObj.ROMBank1offs = (parentObj.ROMBank1offs & 0x100) | data;
9452  parentObj.setCurrentMBC5ROMBank();
9453}
9454GameBoyCore.prototype.MBC5WriteROMBankHigh = function (parentObj, address, data) {
9455  //MBC5 ROM bank switching (by least significant bit):
9456  parentObj.ROMBank1offs  = ((data & 0x01) << 8) | (parentObj.ROMBank1offs & 0xFF);
9457  parentObj.setCurrentMBC5ROMBank();
9458}
9459GameBoyCore.prototype.MBC5WriteRAMBank = function (parentObj, address, data) {
9460  //MBC5 RAM bank switching
9461  parentObj.currMBCRAMBank = data & 0xF;
9462  parentObj.currMBCRAMBankPosition = (parentObj.currMBCRAMBank << 13) - 0xA000;
9463}
9464GameBoyCore.prototype.RUMBLEWriteRAMBank = function (parentObj, address, data) {
9465  //MBC5 RAM bank switching
9466  //Like MBC5, but bit 3 of the lower nibble is used for rumbling and bit 2 is ignored.
9467  parentObj.currMBCRAMBank = data & 0x03;
9468  parentObj.currMBCRAMBankPosition = (parentObj.currMBCRAMBank << 13) - 0xA000;
9469}
9470GameBoyCore.prototype.HuC3WriteRAMBank = function (parentObj, address, data) {
9471  //HuC3 RAM bank switching
9472  parentObj.currMBCRAMBank = data & 0x03;
9473  parentObj.currMBCRAMBankPosition = (parentObj.currMBCRAMBank << 13) - 0xA000;
9474}
9475GameBoyCore.prototype.cartIgnoreWrite = function (parentObj, address, data) {
9476  //We might have encountered illegal RAM writing or such, so just do nothing...
9477}
9478GameBoyCore.prototype.memoryWriteNormal = function (parentObj, address, data) {
9479  parentObj.memory[address] = data;
9480}
9481GameBoyCore.prototype.memoryHighWriteNormal = function (parentObj, address, data) {
9482  parentObj.memory[0xFF00 | address] = data;
9483}
9484GameBoyCore.prototype.memoryWriteMBCRAM = function (parentObj, address, data) {
9485  if (parentObj.MBCRAMBanksEnabled || settings[10]) {
9486    parentObj.MBCRam[address + parentObj.currMBCRAMBankPosition] = data;
9487  }
9488}
9489GameBoyCore.prototype.memoryWriteMBC3RAM = function (parentObj, address, data) {
9490  if (parentObj.MBCRAMBanksEnabled || settings[10]) {
9491    switch (parentObj.currMBCRAMBank) {
9492      case 0x00:
9493      case 0x01:
9494      case 0x02:
9495      case 0x03:
9496        parentObj.MBCRam[address + parentObj.currMBCRAMBankPosition] = data;
9497        break;
9498      case 0x08:
9499        if (data < 60) {
9500          parentObj.RTCSeconds = data;
9501        }
9502        else {
9503          cout("(Bank #" + parentObj.currMBCRAMBank + ") RTC write out of range: " + data, 1);
9504        }
9505        break;
9506      case 0x09:
9507        if (data < 60) {
9508          parentObj.RTCMinutes = data;
9509        }
9510        else {
9511          cout("(Bank #" + parentObj.currMBCRAMBank + ") RTC write out of range: " + data, 1);
9512        }
9513        break;
9514      case 0x0A:
9515        if (data < 24) {
9516          parentObj.RTCHours = data;
9517        }
9518        else {
9519          cout("(Bank #" + parentObj.currMBCRAMBank + ") RTC write out of range: " + data, 1);
9520        }
9521        break;
9522      case 0x0B:
9523        parentObj.RTCDays = (data & 0xFF) | (parentObj.RTCDays & 0x100);
9524        break;
9525      case 0x0C:
9526        parentObj.RTCDayOverFlow = (data > 0x7F);
9527        parentObj.RTCHalt = (data & 0x40) == 0x40;
9528        parentObj.RTCDays = ((data & 0x1) << 8) | (parentObj.RTCDays & 0xFF);
9529        break;
9530      default:
9531        cout("Invalid MBC3 bank address selected: " + parentObj.currMBCRAMBank, 0);
9532    }
9533  }
9534}
9535GameBoyCore.prototype.memoryWriteGBCRAM = function (parentObj, address, data) {
9536  parentObj.GBCMemory[address + parentObj.gbcRamBankPosition] = data;
9537}
9538GameBoyCore.prototype.memoryWriteOAMRAM = function (parentObj, address, data) {
9539  if (parentObj.modeSTAT < 2) {    //OAM RAM cannot be written to in mode 2 & 3
9540    if (parentObj.memory[address] != data) {
9541      parentObj.graphicsJIT();
9542      parentObj.memory[address] = data;
9543    }
9544  }
9545}
9546GameBoyCore.prototype.memoryWriteECHOGBCRAM = function (parentObj, address, data) {
9547  parentObj.GBCMemory[address + parentObj.gbcRamBankPositionECHO] = data;
9548}
9549GameBoyCore.prototype.memoryWriteECHONormal = function (parentObj, address, data) {
9550  parentObj.memory[address - 0x2000] = data;
9551}
9552GameBoyCore.prototype.VRAMGBDATAWrite = function (parentObj, address, data) {
9553  if (parentObj.modeSTAT < 3) {  //VRAM cannot be written to during mode 3
9554    if (parentObj.memory[address] != data) {
9555      //JIT the graphics render queue:
9556      parentObj.graphicsJIT();
9557      parentObj.memory[address] = data;
9558      parentObj.generateGBOAMTileLine(address);
9559    }
9560  }
9561}
9562GameBoyCore.prototype.VRAMGBDATAUpperWrite = function (parentObj, address, data) {
9563  if (parentObj.modeSTAT < 3) {  //VRAM cannot be written to during mode 3
9564    if (parentObj.memory[address] != data) {
9565      //JIT the graphics render queue:
9566      parentObj.graphicsJIT();
9567      parentObj.memory[address] = data;
9568      parentObj.generateGBTileLine(address);
9569    }
9570  }
9571}
9572GameBoyCore.prototype.VRAMGBCDATAWrite = function (parentObj, address, data) {
9573  if (parentObj.modeSTAT < 3) {  //VRAM cannot be written to during mode 3
9574    if (parentObj.currVRAMBank == 0) {
9575      if (parentObj.memory[address] != data) {
9576        //JIT the graphics render queue:
9577        parentObj.graphicsJIT();
9578        parentObj.memory[address] = data;
9579        parentObj.generateGBCTileLineBank1(address);
9580      }
9581    }
9582    else {
9583      address &= 0x1FFF;
9584      if (parentObj.VRAM[address] != data) {
9585        //JIT the graphics render queue:
9586        parentObj.graphicsJIT();
9587        parentObj.VRAM[address] = data;
9588        parentObj.generateGBCTileLineBank2(address);
9589      }
9590    }
9591  }
9592}
9593GameBoyCore.prototype.VRAMGBCHRMAPWrite = function (parentObj, address, data) {
9594  if (parentObj.modeSTAT < 3) {  //VRAM cannot be written to during mode 3
9595    address &= 0x7FF;
9596    if (parentObj.BGCHRBank1[address] != data) {
9597      //JIT the graphics render queue:
9598      parentObj.graphicsJIT();
9599      parentObj.BGCHRBank1[address] = data;
9600    }
9601  }
9602}
9603GameBoyCore.prototype.VRAMGBCCHRMAPWrite = function (parentObj, address, data) {
9604  if (parentObj.modeSTAT < 3) {  //VRAM cannot be written to during mode 3
9605    address &= 0x7FF;
9606    if (parentObj.BGCHRCurrentBank[address] != data) {
9607      //JIT the graphics render queue:
9608      parentObj.graphicsJIT();
9609      parentObj.BGCHRCurrentBank[address] = data;
9610    }
9611  }
9612}
9613GameBoyCore.prototype.DMAWrite = function (tilesToTransfer) {
9614  if (!this.halt) {
9615    //Clock the CPU for the DMA transfer (CPU is halted during the transfer):
9616    this.CPUTicks += 4 | ((tilesToTransfer << 5) << this.doubleSpeedShifter);
9617  }
9618  //Source address of the transfer:
9619  var source = (this.memory[0xFF51] << 8) | this.memory[0xFF52];
9620  //Destination address in the VRAM memory range:
9621  var destination = (this.memory[0xFF53] << 8) | this.memory[0xFF54];
9622  //Creating some references:
9623  var memoryReader = this.memoryReader;
9624  //JIT the graphics render queue:
9625  this.graphicsJIT();
9626  var memory = this.memory;
9627  //Determining which bank we're working on so we can optimize:
9628  if (this.currVRAMBank == 0) {
9629    //DMA transfer for VRAM bank 0:
9630    do {
9631      if (destination < 0x1800) {
9632        memory[0x8000 | destination] = memoryReader[source](this, source++);
9633        memory[0x8001 | destination] = memoryReader[source](this, source++);
9634        memory[0x8002 | destination] = memoryReader[source](this, source++);
9635        memory[0x8003 | destination] = memoryReader[source](this, source++);
9636        memory[0x8004 | destination] = memoryReader[source](this, source++);
9637        memory[0x8005 | destination] = memoryReader[source](this, source++);
9638        memory[0x8006 | destination] = memoryReader[source](this, source++);
9639        memory[0x8007 | destination] = memoryReader[source](this, source++);
9640        memory[0x8008 | destination] = memoryReader[source](this, source++);
9641        memory[0x8009 | destination] = memoryReader[source](this, source++);
9642        memory[0x800A | destination] = memoryReader[source](this, source++);
9643        memory[0x800B | destination] = memoryReader[source](this, source++);
9644        memory[0x800C | destination] = memoryReader[source](this, source++);
9645        memory[0x800D | destination] = memoryReader[source](this, source++);
9646        memory[0x800E | destination] = memoryReader[source](this, source++);
9647        memory[0x800F | destination] = memoryReader[source](this, source++);
9648        this.generateGBCTileBank1(destination);
9649        destination += 0x10;
9650      }
9651      else {
9652        destination &= 0x7F0;
9653        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9654        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9655        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9656        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9657        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9658        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9659        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9660        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9661        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9662        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9663        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9664        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9665        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9666        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9667        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9668        this.BGCHRBank1[destination++] = memoryReader[source](this, source++);
9669        destination = (destination + 0x1800) & 0x1FF0;
9670      }
9671      source &= 0xFFF0;
9672      --tilesToTransfer;
9673    } while (tilesToTransfer > 0);
9674  }
9675  else {
9676    var VRAM = this.VRAM;
9677    //DMA transfer for VRAM bank 1:
9678    do {
9679      if (destination < 0x1800) {
9680        VRAM[destination] = memoryReader[source](this, source++);
9681        VRAM[destination | 0x1] = memoryReader[source](this, source++);
9682        VRAM[destination | 0x2] = memoryReader[source](this, source++);
9683        VRAM[destination | 0x3] = memoryReader[source](this, source++);
9684        VRAM[destination | 0x4] = memoryReader[source](this, source++);
9685        VRAM[destination | 0x5] = memoryReader[source](this, source++);
9686        VRAM[destination | 0x6] = memoryReader[source](this, source++);
9687        VRAM[destination | 0x7] = memoryReader[source](this, source++);
9688        VRAM[destination | 0x8] = memoryReader[source](this, source++);
9689        VRAM[destination | 0x9] = memoryReader[source](this, source++);
9690        VRAM[destination | 0xA] = memoryReader[source](this, source++);
9691        VRAM[destination | 0xB] = memoryReader[source](this, source++);
9692        VRAM[destination | 0xC] = memoryReader[source](this, source++);
9693        VRAM[destination | 0xD] = memoryReader[source](this, source++);
9694        VRAM[destination | 0xE] = memoryReader[source](this, source++);
9695        VRAM[destination | 0xF] = memoryReader[source](this, source++);
9696        this.generateGBCTileBank2(destination);
9697        destination += 0x10;
9698      }
9699      else {
9700        destination &= 0x7F0;
9701        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9702        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9703        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9704        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9705        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9706        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9707        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9708        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9709        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9710        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9711        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9712        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9713        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9714        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9715        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9716        this.BGCHRBank2[destination++] = memoryReader[source](this, source++);
9717        destination = (destination + 0x1800) & 0x1FF0;
9718      }
9719      source &= 0xFFF0;
9720      --tilesToTransfer;
9721    } while (tilesToTransfer > 0);
9722  }
9723  //Update the HDMA registers to their next addresses:
9724  memory[0xFF51] = source >> 8;
9725  memory[0xFF52] = source & 0xF0;
9726  memory[0xFF53] = destination >> 8;
9727  memory[0xFF54] = destination & 0xF0;
9728}
9729GameBoyCore.prototype.registerWriteJumpCompile = function () {
9730  //I/O Registers (GB + GBC):
9731  //JoyPad
9732  this.memoryHighWriter[0] = this.memoryWriter[0xFF00] = function (parentObj, address, data) {
9733    parentObj.memory[0xFF00] = (data & 0x30) | ((((data & 0x20) == 0) ? (parentObj.JoyPad >> 4) : 0xF) & (((data & 0x10) == 0) ? (parentObj.JoyPad & 0xF) : 0xF));
9734  }
9735  //SB (Serial Transfer Data)
9736  this.memoryHighWriter[0x1] = this.memoryWriter[0xFF01] = function (parentObj, address, data) {
9737    if (parentObj.memory[0xFF02] < 0x80) {  //Cannot write while a serial transfer is active.
9738      parentObj.memory[0xFF01] = data;
9739    }
9740  }
9741  //DIV
9742  this.memoryHighWriter[0x4] = this.memoryWriter[0xFF04] = function (parentObj, address, data) {
9743    parentObj.DIVTicks &= 0xFF;  //Update DIV for realignment.
9744    parentObj.memory[0xFF04] = 0;
9745  }
9746  //TIMA
9747  this.memoryHighWriter[0x5] = this.memoryWriter[0xFF05] = function (parentObj, address, data) {
9748    parentObj.memory[0xFF05] = data;
9749  }
9750  //TMA
9751  this.memoryHighWriter[0x6] = this.memoryWriter[0xFF06] = function (parentObj, address, data) {
9752    parentObj.memory[0xFF06] = data;
9753  }
9754  //TAC
9755  this.memoryHighWriter[0x7] = this.memoryWriter[0xFF07] = function (parentObj, address, data) {
9756    parentObj.memory[0xFF07] = data & 0x07;
9757    parentObj.TIMAEnabled = (data & 0x04) == 0x04;
9758    parentObj.TACClocker = Math.pow(4, ((data & 0x3) != 0) ? (data & 0x3) : 4) << 2;  //TODO: Find a way to not make a conditional in here...
9759  }
9760  //IF (Interrupt Request)
9761  this.memoryHighWriter[0xF] = this.memoryWriter[0xFF0F] = function (parentObj, address, data) {
9762    parentObj.interruptsRequested = data;
9763    parentObj.checkIRQMatching();
9764  }
9765  this.memoryHighWriter[0x10] = this.memoryWriter[0xFF10] = function (parentObj, address, data) {
9766    if (parentObj.soundMasterEnabled) {
9767      parentObj.audioJIT();
9768      if (parentObj.channel1decreaseSweep && (data & 0x08) == 0) {
9769        if (parentObj.channel1numSweep != parentObj.channel1frequencySweepDivider) {
9770          parentObj.channel1SweepFault = true;
9771        }
9772      }
9773      parentObj.channel1lastTimeSweep = (data & 0x70) >> 4;
9774      parentObj.channel1frequencySweepDivider = data & 0x07;
9775      parentObj.channel1decreaseSweep = ((data & 0x08) == 0x08);
9776      parentObj.memory[0xFF10] = data;
9777      parentObj.channel1EnableCheck();
9778    }
9779  }
9780  this.memoryHighWriter[0x11] = this.memoryWriter[0xFF11] = function (parentObj, address, data) {
9781    if (parentObj.soundMasterEnabled || !parentObj.cGBC) {
9782      if (parentObj.soundMasterEnabled) {
9783        parentObj.audioJIT();
9784      }
9785      else {
9786        data &= 0x3F;
9787      }
9788      parentObj.channel1CachedDuty = parentObj.dutyLookup[data >> 6];
9789      parentObj.channel1totalLength = 0x40 - (data & 0x3F);
9790      parentObj.memory[0xFF11] = data & 0xC0;
9791      parentObj.channel1EnableCheck();
9792    }
9793  }
9794  this.memoryHighWriter[0x12] = this.memoryWriter[0xFF12] = function (parentObj, address, data) {
9795    if (parentObj.soundMasterEnabled) {
9796      parentObj.audioJIT();
9797      if (parentObj.channel1Enabled && parentObj.channel1envelopeSweeps == 0) {
9798        //Zombie Volume PAPU Bug:
9799        if (((parentObj.memory[0xFF12] ^ data) & 0x8) == 0x8) {
9800          if ((parentObj.memory[0xFF12] & 0x8) == 0) {
9801            if ((parentObj.memory[0xFF12] & 0x7) == 0x7) {
9802              parentObj.channel1envelopeVolume += 2;
9803            }
9804            else {
9805              ++parentObj.channel1envelopeVolume;
9806            }
9807          }
9808          parentObj.channel1envelopeVolume = (16 - parentObj.channel1envelopeVolume) & 0xF;
9809        }
9810        else if ((parentObj.memory[0xFF12] & 0xF) == 0x8) {
9811          parentObj.channel1envelopeVolume = (1 + parentObj.channel1envelopeVolume) & 0xF;
9812        }
9813        parentObj.channel1OutputLevelCache();
9814      }
9815      parentObj.channel1envelopeType = ((data & 0x08) == 0x08);
9816      parentObj.memory[0xFF12] = data;
9817      parentObj.channel1VolumeEnableCheck();
9818    }
9819  }
9820  this.memoryHighWriter[0x13] = this.memoryWriter[0xFF13] = function (parentObj, address, data) {
9821    if (parentObj.soundMasterEnabled) {
9822      parentObj.audioJIT();
9823      parentObj.channel1frequency = (parentObj.channel1frequency & 0x700) | data;
9824      parentObj.channel1FrequencyTracker = (0x800 - parentObj.channel1frequency) << 2;
9825      parentObj.memory[0xFF13] = data;
9826    }
9827  }
9828  this.memoryHighWriter[0x14] = this.memoryWriter[0xFF14] = function (parentObj, address, data) {
9829    if (parentObj.soundMasterEnabled) {
9830      parentObj.audioJIT();
9831      parentObj.channel1consecutive = ((data & 0x40) == 0x0);
9832      parentObj.channel1frequency = ((data & 0x7) << 8) | (parentObj.channel1frequency & 0xFF);
9833      parentObj.channel1FrequencyTracker = (0x800 - parentObj.channel1frequency) << 2;
9834      if (data > 0x7F) {
9835        //Reload 0xFF10:
9836        parentObj.channel1timeSweep = parentObj.channel1lastTimeSweep;
9837        parentObj.channel1numSweep = parentObj.channel1frequencySweepDivider;
9838        //Reload 0xFF12:
9839        var nr12 = parentObj.memory[0xFF12];
9840        parentObj.channel1envelopeVolume = nr12 >> 4;
9841        parentObj.channel1OutputLevelCache();
9842        parentObj.channel1envelopeSweepsLast = (nr12 & 0x7) - 1;
9843        if (parentObj.channel1totalLength == 0) {
9844          parentObj.channel1totalLength = 0x40;
9845        }
9846        if (parentObj.channel1lastTimeSweep > 0 || parentObj.channel1frequencySweepDivider > 0) {
9847          parentObj.memory[0xFF26] |= 0x1;
9848        }
9849        else {
9850          parentObj.memory[0xFF26] &= 0xFE;
9851        }
9852        if ((data & 0x40) == 0x40) {
9853          parentObj.memory[0xFF26] |= 0x1;
9854        }
9855        parentObj.channel1ShadowFrequency = parentObj.channel1frequency;
9856        //Reset frequency overflow check + frequency sweep type check:
9857        parentObj.channel1SweepFault = false;
9858        //Supposed to run immediately:
9859        parentObj.runAudioSweep();
9860      }
9861      parentObj.channel1EnableCheck();
9862      parentObj.memory[0xFF14] = data & 0x40;
9863    }
9864  }
9865  this.memoryHighWriter[0x16] = this.memoryWriter[0xFF16] = function (parentObj, address, data) {
9866    if (parentObj.soundMasterEnabled || !parentObj.cGBC) {
9867      if (parentObj.soundMasterEnabled) {
9868        parentObj.audioJIT();
9869      }
9870      else {
9871        data &= 0x3F;
9872      }
9873      parentObj.channel2CachedDuty = parentObj.dutyLookup[data >> 6];
9874      parentObj.channel2totalLength = 0x40 - (data & 0x3F);
9875      parentObj.memory[0xFF16] = data & 0xC0;
9876      parentObj.channel2EnableCheck();
9877    }
9878  }
9879  this.memoryHighWriter[0x17] = this.memoryWriter[0xFF17] = function (parentObj, address, data) {
9880    if (parentObj.soundMasterEnabled) {
9881      parentObj.audioJIT();
9882      if (parentObj.channel2Enabled && parentObj.channel2envelopeSweeps == 0) {
9883        //Zombie Volume PAPU Bug:
9884        if (((parentObj.memory[0xFF17] ^ data) & 0x8) == 0x8) {
9885          if ((parentObj.memory[0xFF17] & 0x8) == 0) {
9886            if ((parentObj.memory[0xFF17] & 0x7) == 0x7) {
9887              parentObj.channel2envelopeVolume += 2;
9888            }
9889            else {
9890              ++parentObj.channel2envelopeVolume;
9891            }
9892          }
9893          parentObj.channel2envelopeVolume = (16 - parentObj.channel2envelopeVolume) & 0xF;
9894        }
9895        else if ((parentObj.memory[0xFF17] & 0xF) == 0x8) {
9896          parentObj.channel2envelopeVolume = (1 + parentObj.channel2envelopeVolume) & 0xF;
9897        }
9898        parentObj.channel2OutputLevelCache();
9899      }
9900      parentObj.channel2envelopeType = ((data & 0x08) == 0x08);
9901      parentObj.memory[0xFF17] = data;
9902      parentObj.channel2VolumeEnableCheck();
9903    }
9904  }
9905  this.memoryHighWriter[0x18] = this.memoryWriter[0xFF18] = function (parentObj, address, data) {
9906    if (parentObj.soundMasterEnabled) {
9907      parentObj.audioJIT();
9908      parentObj.channel2frequency = (parentObj.channel2frequency & 0x700) | data;
9909      parentObj.channel2FrequencyTracker = (0x800 - parentObj.channel2frequency) << 2;
9910      parentObj.memory[0xFF18] = data;
9911    }
9912  }
9913  this.memoryHighWriter[0x19] = this.memoryWriter[0xFF19] = function (parentObj, address, data) {
9914    if (parentObj.soundMasterEnabled) {
9915      parentObj.audioJIT();
9916      if (data > 0x7F) {
9917        //Reload 0xFF17:
9918        var nr22 = parentObj.memory[0xFF17];
9919        parentObj.channel2envelopeVolume = nr22 >> 4;
9920        parentObj.channel2OutputLevelCache();
9921        parentObj.channel2envelopeSweepsLast = (nr22 & 0x7) - 1;
9922        if (parentObj.channel2totalLength == 0) {
9923          parentObj.channel2totalLength = 0x40;
9924        }
9925        if ((data & 0x40) == 0x40) {
9926          parentObj.memory[0xFF26] |= 0x2;
9927        }
9928      }
9929      parentObj.channel2consecutive = ((data & 0x40) == 0x0);
9930      parentObj.channel2frequency = ((data & 0x7) << 8) | (parentObj.channel2frequency & 0xFF);
9931      parentObj.channel2FrequencyTracker = (0x800 - parentObj.channel2frequency) << 2;
9932      parentObj.memory[0xFF19] = data & 0x40;
9933      parentObj.channel2EnableCheck();
9934    }
9935  }
9936  this.memoryHighWriter[0x1A] = this.memoryWriter[0xFF1A] = function (parentObj, address, data) {
9937    if (parentObj.soundMasterEnabled) {
9938      parentObj.audioJIT();
9939      if (!parentObj.channel3canPlay && data >= 0x80) {
9940        parentObj.channel3lastSampleLookup = 0;
9941        parentObj.channel3UpdateCache();
9942      }
9943      parentObj.channel3canPlay = (data > 0x7F);
9944      if (parentObj.channel3canPlay && parentObj.memory[0xFF1A] > 0x7F && !parentObj.channel3consecutive) {
9945        parentObj.memory[0xFF26] |= 0x4;
9946      }
9947      parentObj.memory[0xFF1A] = data & 0x80;
9948      //parentObj.channel3EnableCheck();
9949    }
9950  }
9951  this.memoryHighWriter[0x1B] = this.memoryWriter[0xFF1B] = function (parentObj, address, data) {
9952    if (parentObj.soundMasterEnabled || !parentObj.cGBC) {
9953      if (parentObj.soundMasterEnabled) {
9954        parentObj.audioJIT();
9955      }
9956      parentObj.channel3totalLength = 0x100 - data;
9957      parentObj.memory[0xFF1B] = data;
9958      parentObj.channel3EnableCheck();
9959    }
9960  }
9961  this.memoryHighWriter[0x1C] = this.memoryWriter[0xFF1C] = function (parentObj, address, data) {
9962    if (parentObj.soundMasterEnabled) {
9963      parentObj.audioJIT();
9964      data &= 0x60;
9965      parentObj.memory[0xFF1C] = data;
9966      parentObj.channel3patternType = (data == 0) ? 4 : ((data >> 5) - 1);
9967    }
9968  }
9969  this.memoryHighWriter[0x1D] = this.memoryWriter[0xFF1D] = function (parentObj, address, data) {
9970    if (parentObj.soundMasterEnabled) {
9971      parentObj.audioJIT();
9972      parentObj.channel3frequency = (parentObj.channel3frequency & 0x700) | data;
9973      parentObj.channel3FrequencyPeriod = (0x800 - parentObj.channel3frequency) << 1;
9974      parentObj.memory[0xFF1D] = data;
9975    }
9976  }
9977  this.memoryHighWriter[0x1E] = this.memoryWriter[0xFF1E] = function (parentObj, address, data) {
9978    if (parentObj.soundMasterEnabled) {
9979      parentObj.audioJIT();
9980      if (data > 0x7F) {
9981        if (parentObj.channel3totalLength == 0) {
9982          parentObj.channel3totalLength = 0x100;
9983        }
9984        parentObj.channel3lastSampleLookup = 0;
9985        if ((data & 0x40) == 0x40) {
9986          parentObj.memory[0xFF26] |= 0x4;
9987        }
9988      }
9989      parentObj.channel3consecutive = ((data & 0x40) == 0x0);
9990      parentObj.channel3frequency = ((data & 0x7) << 8) | (parentObj.channel3frequency & 0xFF);
9991      parentObj.channel3FrequencyPeriod = (0x800 - parentObj.channel3frequency) << 1;
9992      parentObj.memory[0xFF1E] = data & 0x40;
9993      parentObj.channel3EnableCheck();
9994    }
9995  }
9996  this.memoryHighWriter[0x20] = this.memoryWriter[0xFF20] = function (parentObj, address, data) {
9997    if (parentObj.soundMasterEnabled || !parentObj.cGBC) {
9998      if (parentObj.soundMasterEnabled) {
9999        parentObj.audioJIT();
10000      }
10001      parentObj.channel4totalLength = 0x40 - (data & 0x3F);
10002      parentObj.memory[0xFF20] = data | 0xC0;
10003      parentObj.channel4EnableCheck();
10004    }
10005  }
10006  this.memoryHighWriter[0x21] = this.memoryWriter[0xFF21] = function (parentObj, address, data) {
10007    if (parentObj.soundMasterEnabled) {
10008      parentObj.audioJIT();
10009      if (parentObj.channel4Enabled && parentObj.channel4envelopeSweeps == 0) {
10010        //Zombie Volume PAPU Bug:
10011        if (((parentObj.memory[0xFF21] ^ data) & 0x8) == 0x8) {
10012          if ((parentObj.memory[0xFF21] & 0x8) == 0) {
10013            if ((parentObj.memory[0xFF21] & 0x7) == 0x7) {
10014              parentObj.channel4envelopeVolume += 2;
10015            }
10016            else {
10017              ++parentObj.channel4envelopeVolume;
10018            }
10019          }
10020          parentObj.channel4envelopeVolume = (16 - parentObj.channel4envelopeVolume) & 0xF;
10021        }
10022        else if ((parentObj.memory[0xFF21] & 0xF) == 0x8) {
10023          parentObj.channel4envelopeVolume = (1 + parentObj.channel4envelopeVolume) & 0xF;
10024        }
10025        parentObj.channel4currentVolume = parentObj.channel4envelopeVolume << parentObj.channel4VolumeShifter;
10026      }
10027      parentObj.channel4envelopeType = ((data & 0x08) == 0x08);
10028      parentObj.memory[0xFF21] = data;
10029      parentObj.channel4UpdateCache();
10030      parentObj.channel4VolumeEnableCheck();
10031    }
10032  }
10033  this.memoryHighWriter[0x22] = this.memoryWriter[0xFF22] = function (parentObj, address, data) {
10034    if (parentObj.soundMasterEnabled) {
10035      parentObj.audioJIT();
10036      parentObj.channel4FrequencyPeriod = Math.max((data & 0x7) << 4, 8) << (data >> 4);
10037      var bitWidth = (data & 0x8);
10038      if ((bitWidth == 0x8 && parentObj.channel4BitRange == 0x7FFF) || (bitWidth == 0 && parentObj.channel4BitRange == 0x7F)) {
10039        parentObj.channel4lastSampleLookup = 0;
10040        parentObj.channel4BitRange = (bitWidth == 0x8) ? 0x7F : 0x7FFF;
10041        parentObj.channel4VolumeShifter = (bitWidth == 0x8) ? 7 : 15;
10042        parentObj.channel4currentVolume = parentObj.channel4envelopeVolume << parentObj.channel4VolumeShifter;
10043        parentObj.noiseSampleTable = (bitWidth == 0x8) ? parentObj.LSFR7Table : parentObj.LSFR15Table;
10044      }
10045      parentObj.memory[0xFF22] = data;
10046      parentObj.channel4UpdateCache();
10047    }
10048  }
10049  this.memoryHighWriter[0x23] = this.memoryWriter[0xFF23] = function (parentObj, address, data) {
10050    if (parentObj.soundMasterEnabled) {
10051      parentObj.audioJIT();
10052      parentObj.memory[0xFF23] = data;
10053      parentObj.channel4consecutive = ((data & 0x40) == 0x0);
10054      if (data > 0x7F) {
10055        var nr42 = parentObj.memory[0xFF21];
10056        parentObj.channel4envelopeVolume = nr42 >> 4;
10057        parentObj.channel4currentVolume = parentObj.channel4envelopeVolume << parentObj.channel4VolumeShifter;
10058        parentObj.channel4envelopeSweepsLast = (nr42 & 0x7) - 1;
10059        if (parentObj.channel4totalLength == 0) {
10060          parentObj.channel4totalLength = 0x40;
10061        }
10062        if ((data & 0x40) == 0x40) {
10063          parentObj.memory[0xFF26] |= 0x8;
10064        }
10065      }
10066      parentObj.channel4EnableCheck();
10067    }
10068  }
10069  this.memoryHighWriter[0x24] = this.memoryWriter[0xFF24] = function (parentObj, address, data) {
10070    if (parentObj.soundMasterEnabled && parentObj.memory[0xFF24] != data) {
10071      parentObj.audioJIT();
10072      parentObj.memory[0xFF24] = data;
10073      parentObj.VinLeftChannelMasterVolume = ((data >> 4) & 0x07) + 1;
10074      parentObj.VinRightChannelMasterVolume = (data & 0x07) + 1;
10075      parentObj.mixerOutputLevelCache();
10076    }
10077  }
10078  this.memoryHighWriter[0x25] = this.memoryWriter[0xFF25] = function (parentObj, address, data) {
10079    if (parentObj.soundMasterEnabled && parentObj.memory[0xFF25] != data) {
10080      parentObj.audioJIT();
10081      parentObj.memory[0xFF25] = data;
10082      parentObj.rightChannel1 = ((data & 0x01) == 0x01);
10083      parentObj.rightChannel2 = ((data & 0x02) == 0x02);
10084      parentObj.rightChannel3 = ((data & 0x04) == 0x04);
10085      parentObj.rightChannel4 = ((data & 0x08) == 0x08);
10086      parentObj.leftChannel1 = ((data & 0x10) == 0x10);
10087      parentObj.leftChannel2 = ((data & 0x20) == 0x20);
10088      parentObj.leftChannel3 = ((data & 0x40) == 0x40);
10089      parentObj.leftChannel4 = (data > 0x7F);
10090      parentObj.channel1OutputLevelCache();
10091      parentObj.channel2OutputLevelCache();
10092      parentObj.channel3OutputLevelCache();
10093      parentObj.channel4OutputLevelCache();
10094    }
10095  }
10096  this.memoryHighWriter[0x26] = this.memoryWriter[0xFF26] = function (parentObj, address, data) {
10097    parentObj.audioJIT();
10098    if (!parentObj.soundMasterEnabled && data > 0x7F) {
10099      parentObj.memory[0xFF26] = 0x80;
10100      parentObj.soundMasterEnabled = true;
10101      parentObj.initializeAudioStartState();
10102    }
10103    else if (parentObj.soundMasterEnabled && data < 0x80) {
10104      parentObj.memory[0xFF26] = 0;
10105      parentObj.soundMasterEnabled = false;
10106      //GBDev wiki says the registers are written with zeros on power off:
10107      for (var index = 0xFF10; index < 0xFF26; index++) {
10108        parentObj.memoryWriter[index](parentObj, index, 0);
10109      }
10110    }
10111  }
10112  //0xFF27 to 0xFF2F don't do anything...
10113  this.memoryHighWriter[0x27] = this.memoryWriter[0xFF27] = this.cartIgnoreWrite;
10114  this.memoryHighWriter[0x28] = this.memoryWriter[0xFF28] = this.cartIgnoreWrite;
10115  this.memoryHighWriter[0x29] = this.memoryWriter[0xFF29] = this.cartIgnoreWrite;
10116  this.memoryHighWriter[0x2A] = this.memoryWriter[0xFF2A] = this.cartIgnoreWrite;
10117  this.memoryHighWriter[0x2B] = this.memoryWriter[0xFF2B] = this.cartIgnoreWrite;
10118  this.memoryHighWriter[0x2C] = this.memoryWriter[0xFF2C] = this.cartIgnoreWrite;
10119  this.memoryHighWriter[0x2D] = this.memoryWriter[0xFF2D] = this.cartIgnoreWrite;
10120  this.memoryHighWriter[0x2E] = this.memoryWriter[0xFF2E] = this.cartIgnoreWrite;
10121  this.memoryHighWriter[0x2F] = this.memoryWriter[0xFF2F] = this.cartIgnoreWrite;
10122  //WAVE PCM RAM:
10123  this.memoryHighWriter[0x30] = this.memoryWriter[0xFF30] = function (parentObj, address, data) {
10124    parentObj.channel3WriteRAM(0, data);
10125  }
10126  this.memoryHighWriter[0x31] = this.memoryWriter[0xFF31] = function (parentObj, address, data) {
10127    parentObj.channel3WriteRAM(0x1, data);
10128  }
10129  this.memoryHighWriter[0x32] = this.memoryWriter[0xFF32] = function (parentObj, address, data) {
10130    parentObj.channel3WriteRAM(0x2, data);
10131  }
10132  this.memoryHighWriter[0x33] = this.memoryWriter[0xFF33] = function (parentObj, address, data) {
10133    parentObj.channel3WriteRAM(0x3, data);
10134  }
10135  this.memoryHighWriter[0x34] = this.memoryWriter[0xFF34] = function (parentObj, address, data) {
10136    parentObj.channel3WriteRAM(0x4, data);
10137  }
10138  this.memoryHighWriter[0x35] = this.memoryWriter[0xFF35] = function (parentObj, address, data) {
10139    parentObj.channel3WriteRAM(0x5, data);
10140  }
10141  this.memoryHighWriter[0x36] = this.memoryWriter[0xFF36] = function (parentObj, address, data) {
10142    parentObj.channel3WriteRAM(0x6, data);
10143  }
10144  this.memoryHighWriter[0x37] = this.memoryWriter[0xFF37] = function (parentObj, address, data) {
10145    parentObj.channel3WriteRAM(0x7, data);
10146  }
10147  this.memoryHighWriter[0x38] = this.memoryWriter[0xFF38] = function (parentObj, address, data) {
10148    parentObj.channel3WriteRAM(0x8, data);
10149  }
10150  this.memoryHighWriter[0x39] = this.memoryWriter[0xFF39] = function (parentObj, address, data) {
10151    parentObj.channel3WriteRAM(0x9, data);
10152  }
10153  this.memoryHighWriter[0x3A] = this.memoryWriter[0xFF3A] = function (parentObj, address, data) {
10154    parentObj.channel3WriteRAM(0xA, data);
10155  }
10156  this.memoryHighWriter[0x3B] = this.memoryWriter[0xFF3B] = function (parentObj, address, data) {
10157    parentObj.channel3WriteRAM(0xB, data);
10158  }
10159  this.memoryHighWriter[0x3C] = this.memoryWriter[0xFF3C] = function (parentObj, address, data) {
10160    parentObj.channel3WriteRAM(0xC, data);
10161  }
10162  this.memoryHighWriter[0x3D] = this.memoryWriter[0xFF3D] = function (parentObj, address, data) {
10163    parentObj.channel3WriteRAM(0xD, data);
10164  }
10165  this.memoryHighWriter[0x3E] = this.memoryWriter[0xFF3E] = function (parentObj, address, data) {
10166    parentObj.channel3WriteRAM(0xE, data);
10167  }
10168  this.memoryHighWriter[0x3F] = this.memoryWriter[0xFF3F] = function (parentObj, address, data) {
10169    parentObj.channel3WriteRAM(0xF, data);
10170  }
10171  //SCY
10172  this.memoryHighWriter[0x42] = this.memoryWriter[0xFF42] = function (parentObj, address, data) {
10173    if (parentObj.backgroundY != data) {
10174      parentObj.midScanLineJIT();
10175      parentObj.backgroundY = data;
10176    }
10177  }
10178  //SCX
10179  this.memoryHighWriter[0x43] = this.memoryWriter[0xFF43] = function (parentObj, address, data) {
10180    if (parentObj.backgroundX != data) {
10181      parentObj.midScanLineJIT();
10182      parentObj.backgroundX = data;
10183    }
10184  }
10185  //LY
10186  this.memoryHighWriter[0x44] = this.memoryWriter[0xFF44] = function (parentObj, address, data) {
10187    //Read Only:
10188    if (parentObj.LCDisOn) {
10189      //Gambatte says to do this:
10190      parentObj.modeSTAT = 2;
10191      parentObj.midScanlineOffset = -1;
10192      parentObj.totalLinesPassed = parentObj.currentX = parentObj.queuedScanLines = parentObj.lastUnrenderedLine = parentObj.LCDTicks = parentObj.STATTracker = parentObj.actualScanLine = parentObj.memory[0xFF44] = 0;
10193    }
10194  }
10195  //LYC
10196  this.memoryHighWriter[0x45] = this.memoryWriter[0xFF45] = function (parentObj, address, data) {
10197    if (parentObj.memory[0xFF45] != data) {
10198      parentObj.memory[0xFF45] = data;
10199      if (parentObj.LCDisOn) {
10200        parentObj.matchLYC();  //Get the compare of the first scan line.
10201      }
10202    }
10203  }
10204  //WY
10205  this.memoryHighWriter[0x4A] = this.memoryWriter[0xFF4A] = function (parentObj, address, data) {
10206    if (parentObj.windowY != data) {
10207      parentObj.midScanLineJIT();
10208      parentObj.windowY = data;
10209    }
10210  }
10211  //WX
10212  this.memoryHighWriter[0x4B] = this.memoryWriter[0xFF4B] = function (parentObj, address, data) {
10213    if (parentObj.memory[0xFF4B] != data) {
10214      parentObj.midScanLineJIT();
10215      parentObj.memory[0xFF4B] = data;
10216      parentObj.windowX = data - 7;
10217    }
10218  }
10219  this.memoryHighWriter[0x72] = this.memoryWriter[0xFF72] = function (parentObj, address, data) {
10220    parentObj.memory[0xFF72] = data;
10221  }
10222  this.memoryHighWriter[0x73] = this.memoryWriter[0xFF73] = function (parentObj, address, data) {
10223    parentObj.memory[0xFF73] = data;
10224  }
10225  this.memoryHighWriter[0x75] = this.memoryWriter[0xFF75] = function (parentObj, address, data) {
10226    parentObj.memory[0xFF75] = data;
10227  }
10228  this.memoryHighWriter[0x76] = this.memoryWriter[0xFF76] = this.cartIgnoreWrite;
10229  this.memoryHighWriter[0x77] = this.memoryWriter[0xFF77] = this.cartIgnoreWrite;
10230  //IE (Interrupt Enable)
10231  this.memoryHighWriter[0xFF] = this.memoryWriter[0xFFFF] = function (parentObj, address, data) {
10232    parentObj.interruptsEnabled = data;
10233    parentObj.checkIRQMatching();
10234  }
10235  this.recompileModelSpecificIOWriteHandling();
10236  this.recompileBootIOWriteHandling();
10237}
10238GameBoyCore.prototype.recompileModelSpecificIOWriteHandling = function () {
10239  if (this.cGBC) {
10240    //GameBoy Color Specific I/O:
10241    //SC (Serial Transfer Control Register)
10242    this.memoryHighWriter[0x2] = this.memoryWriter[0xFF02] = function (parentObj, address, data) {
10243      if (((data & 0x1) == 0x1)) {
10244        //Internal clock:
10245        parentObj.memory[0xFF02] = (data & 0x7F);
10246        parentObj.serialTimer = ((data & 0x2) == 0) ? 4096 : 128;  //Set the Serial IRQ counter.
10247        parentObj.serialShiftTimer = parentObj.serialShiftTimerAllocated = ((data & 0x2) == 0) ? 512 : 16;  //Set the transfer data shift counter.
10248      }
10249      else {
10250        //External clock:
10251        parentObj.memory[0xFF02] = data;
10252        parentObj.serialShiftTimer = parentObj.serialShiftTimerAllocated = parentObj.serialTimer = 0;  //Zero the timers, since we're emulating as if nothing is connected.
10253      }
10254    }
10255    this.memoryHighWriter[0x40] = this.memoryWriter[0xFF40] = function (parentObj, address, data) {
10256      if (parentObj.memory[0xFF40] != data) {
10257        parentObj.midScanLineJIT();
10258        var temp_var = (data > 0x7F);
10259        if (temp_var != parentObj.LCDisOn) {
10260          //When the display mode changes...
10261          parentObj.LCDisOn = temp_var;
10262          parentObj.memory[0xFF41] &= 0x78;
10263          parentObj.midScanlineOffset = -1;
10264          parentObj.totalLinesPassed = parentObj.currentX = parentObj.queuedScanLines = parentObj.lastUnrenderedLine = parentObj.STATTracker = parentObj.LCDTicks = parentObj.actualScanLine = parentObj.memory[0xFF44] = 0;
10265          if (parentObj.LCDisOn) {
10266            parentObj.modeSTAT = 2;
10267            parentObj.matchLYC();  //Get the compare of the first scan line.
10268            parentObj.LCDCONTROL = parentObj.LINECONTROL;
10269          }
10270          else {
10271            parentObj.modeSTAT = 0;
10272            parentObj.LCDCONTROL = parentObj.DISPLAYOFFCONTROL;
10273            parentObj.DisplayShowOff();
10274          }
10275          parentObj.interruptsRequested &= 0xFD;
10276        }
10277        parentObj.gfxWindowCHRBankPosition = ((data & 0x40) == 0x40) ? 0x400 : 0;
10278        parentObj.gfxWindowDisplay = ((data & 0x20) == 0x20);
10279        parentObj.gfxBackgroundBankOffset = ((data & 0x10) == 0x10) ? 0 : 0x80;
10280        parentObj.gfxBackgroundCHRBankPosition = ((data & 0x08) == 0x08) ? 0x400 : 0;
10281        parentObj.gfxSpriteNormalHeight = ((data & 0x04) == 0);
10282        parentObj.gfxSpriteShow = ((data & 0x02) == 0x02);
10283        parentObj.BGPriorityEnabled = ((data & 0x01) == 0x01);
10284        parentObj.priorityFlaggingPathRebuild();  //Special case the priority flagging as an optimization.
10285        parentObj.memory[0xFF40] = data;
10286      }
10287    }
10288    this.memoryHighWriter[0x41] = this.memoryWriter[0xFF41] = function (parentObj, address, data) {
10289      parentObj.LYCMatchTriggerSTAT = ((data & 0x40) == 0x40);
10290      parentObj.mode2TriggerSTAT = ((data & 0x20) == 0x20);
10291      parentObj.mode1TriggerSTAT = ((data & 0x10) == 0x10);
10292      parentObj.mode0TriggerSTAT = ((data & 0x08) == 0x08);
10293      parentObj.memory[0xFF41] = data & 0x78;
10294    }
10295    this.memoryHighWriter[0x46] = this.memoryWriter[0xFF46] = function (parentObj, address, data) {
10296      parentObj.memory[0xFF46] = data;
10297      if (data < 0xE0) {
10298        data <<= 8;
10299        address = 0xFE00;
10300        var stat = parentObj.modeSTAT;
10301        parentObj.modeSTAT = 0;
10302        var newData = 0;
10303        do {
10304          newData = parentObj.memoryReader[data](parentObj, data++);
10305          if (newData != parentObj.memory[address]) {
10306            //JIT the graphics render queue:
10307            parentObj.modeSTAT = stat;
10308            parentObj.graphicsJIT();
10309            parentObj.modeSTAT = 0;
10310            parentObj.memory[address++] = newData;
10311            break;
10312          }
10313        } while (++address < 0xFEA0);
10314        if (address < 0xFEA0) {
10315          do {
10316            parentObj.memory[address++] = parentObj.memoryReader[data](parentObj, data++);
10317            parentObj.memory[address++] = parentObj.memoryReader[data](parentObj, data++);
10318            parentObj.memory[address++] = parentObj.memoryReader[data](parentObj, data++);
10319            parentObj.memory[address++] = parentObj.memoryReader[data](parentObj, data++);
10320          } while (address < 0xFEA0);
10321        }
10322        parentObj.modeSTAT = stat;
10323      }
10324    }
10325    //KEY1
10326    this.memoryHighWriter[0x4D] = this.memoryWriter[0xFF4D] = function (parentObj, address, data) {
10327      parentObj.memory[0xFF4D] = (data & 0x7F) | (parentObj.memory[0xFF4D] & 0x80);
10328    }
10329    this.memoryHighWriter[0x4F] = this.memoryWriter[0xFF4F] = function (parentObj, address, data) {
10330      parentObj.currVRAMBank = data & 0x01;
10331      if (parentObj.currVRAMBank > 0) {
10332        parentObj.BGCHRCurrentBank = parentObj.BGCHRBank2;
10333      }
10334      else {
10335        parentObj.BGCHRCurrentBank = parentObj.BGCHRBank1;
10336      }
10337      //Only writable by GBC.
10338    }
10339    this.memoryHighWriter[0x51] = this.memoryWriter[0xFF51] = function (parentObj, address, data) {
10340      if (!parentObj.hdmaRunning) {
10341        parentObj.memory[0xFF51] = data;
10342      }
10343    }
10344    this.memoryHighWriter[0x52] = this.memoryWriter[0xFF52] = function (parentObj, address, data) {
10345      if (!parentObj.hdmaRunning) {
10346        parentObj.memory[0xFF52] = data & 0xF0;
10347      }
10348    }
10349    this.memoryHighWriter[0x53] = this.memoryWriter[0xFF53] = function (parentObj, address, data) {
10350      if (!parentObj.hdmaRunning) {
10351        parentObj.memory[0xFF53] = data & 0x1F;
10352      }
10353    }
10354    this.memoryHighWriter[0x54] = this.memoryWriter[0xFF54] = function (parentObj, address, data) {
10355      if (!parentObj.hdmaRunning) {
10356        parentObj.memory[0xFF54] = data & 0xF0;
10357      }
10358    }
10359    this.memoryHighWriter[0x55] = this.memoryWriter[0xFF55] = function (parentObj, address, data) {
10360      if (!parentObj.hdmaRunning) {
10361        if ((data & 0x80) == 0) {
10362          //DMA
10363          parentObj.DMAWrite((data & 0x7F) + 1);
10364          parentObj.memory[0xFF55] = 0xFF;  //Transfer completed.
10365        }
10366        else {
10367          //H-Blank DMA
10368          parentObj.hdmaRunning = true;
10369          parentObj.memory[0xFF55] = data & 0x7F;
10370        }
10371      }
10372      else if ((data & 0x80) == 0) {
10373        //Stop H-Blank DMA
10374        parentObj.hdmaRunning = false;
10375        parentObj.memory[0xFF55] |= 0x80;
10376      }
10377      else {
10378        parentObj.memory[0xFF55] = data & 0x7F;
10379      }
10380    }
10381    this.memoryHighWriter[0x68] = this.memoryWriter[0xFF68] = function (parentObj, address, data) {
10382      parentObj.memory[0xFF69] = parentObj.gbcBGRawPalette[data & 0x3F];
10383      parentObj.memory[0xFF68] = data;
10384    }
10385    this.memoryHighWriter[0x69] = this.memoryWriter[0xFF69] = function (parentObj, address, data) {
10386      parentObj.updateGBCBGPalette(parentObj.memory[0xFF68] & 0x3F, data);
10387      if (parentObj.memory[0xFF68] > 0x7F) { // high bit = autoincrement
10388        var next = ((parentObj.memory[0xFF68] + 1) & 0x3F);
10389        parentObj.memory[0xFF68] = (next | 0x80);
10390        parentObj.memory[0xFF69] = parentObj.gbcBGRawPalette[next];
10391      }
10392      else {
10393        parentObj.memory[0xFF69] = data;
10394      }
10395    }
10396    this.memoryHighWriter[0x6A] = this.memoryWriter[0xFF6A] = function (parentObj, address, data) {
10397      parentObj.memory[0xFF6B] = parentObj.gbcOBJRawPalette[data & 0x3F];
10398      parentObj.memory[0xFF6A] = data;
10399    }
10400    this.memoryHighWriter[0x6B] = this.memoryWriter[0xFF6B] = function (parentObj, address, data) {
10401      parentObj.updateGBCOBJPalette(parentObj.memory[0xFF6A] & 0x3F, data);
10402      if (parentObj.memory[0xFF6A] > 0x7F) { // high bit = autoincrement
10403        var next = ((parentObj.memory[0xFF6A] + 1) & 0x3F);
10404        parentObj.memory[0xFF6A] = (next | 0x80);
10405        parentObj.memory[0xFF6B] = parentObj.gbcOBJRawPalette[next];
10406      }
10407      else {
10408        parentObj.memory[0xFF6B] = data;
10409      }
10410    }
10411    //SVBK
10412    this.memoryHighWriter[0x70] = this.memoryWriter[0xFF70] = function (parentObj, address, data) {
10413      var addressCheck = (parentObj.memory[0xFF51] << 8) | parentObj.memory[0xFF52];  //Cannot change the RAM bank while WRAM is the source of a running HDMA.
10414      if (!parentObj.hdmaRunning || addressCheck < 0xD000 || addressCheck >= 0xE000) {
10415        parentObj.gbcRamBank = Math.max(data & 0x07, 1);  //Bank range is from 1-7
10416        parentObj.gbcRamBankPosition = ((parentObj.gbcRamBank - 1) << 12) - 0xD000;
10417        parentObj.gbcRamBankPositionECHO = parentObj.gbcRamBankPosition - 0x2000;
10418      }
10419      parentObj.memory[0xFF70] = data;  //Bit 6 cannot be written to.
10420    }
10421    this.memoryHighWriter[0x74] = this.memoryWriter[0xFF74] = function (parentObj, address, data) {
10422      parentObj.memory[0xFF74] = data;
10423    }
10424  }
10425  else {
10426    //Fill in the GameBoy Color I/O registers as normal RAM for GameBoy compatibility:
10427    //SC (Serial Transfer Control Register)
10428    this.memoryHighWriter[0x2] = this.memoryWriter[0xFF02] = function (parentObj, address, data) {
10429      if (((data & 0x1) == 0x1)) {
10430        //Internal clock:
10431        parentObj.memory[0xFF02] = (data & 0x7F);
10432        parentObj.serialTimer = 4096;  //Set the Serial IRQ counter.
10433        parentObj.serialShiftTimer = parentObj.serialShiftTimerAllocated = 512;  //Set the transfer data shift counter.
10434      }
10435      else {
10436        //External clock:
10437        parentObj.memory[0xFF02] = data;
10438        parentObj.serialShiftTimer = parentObj.serialShiftTimerAllocated = parentObj.serialTimer = 0;  //Zero the timers, since we're emulating as if nothing is connected.
10439      }
10440    }
10441    this.memoryHighWriter[0x40] = this.memoryWriter[0xFF40] = function (parentObj, address, data) {
10442      if (parentObj.memory[0xFF40] != data) {
10443        parentObj.midScanLineJIT();
10444        var temp_var = (data > 0x7F);
10445        if (temp_var != parentObj.LCDisOn) {
10446          //When the display mode changes...
10447          parentObj.LCDisOn = temp_var;
10448          parentObj.memory[0xFF41] &= 0x78;
10449          parentObj.midScanlineOffset = -1;
10450          parentObj.totalLinesPassed = parentObj.currentX = parentObj.queuedScanLines = parentObj.lastUnrenderedLine = parentObj.STATTracker = parentObj.LCDTicks = parentObj.actualScanLine = parentObj.memory[0xFF44] = 0;
10451          if (parentObj.LCDisOn) {
10452            parentObj.modeSTAT = 2;
10453            parentObj.matchLYC();  //Get the compare of the first scan line.
10454            parentObj.LCDCONTROL = parentObj.LINECONTROL;
10455          }
10456          else {
10457            parentObj.modeSTAT = 0;
10458            parentObj.LCDCONTROL = parentObj.DISPLAYOFFCONTROL;
10459            parentObj.DisplayShowOff();
10460          }
10461          parentObj.interruptsRequested &= 0xFD;
10462        }
10463        parentObj.gfxWindowCHRBankPosition = ((data & 0x40) == 0x40) ? 0x400 : 0;
10464        parentObj.gfxWindowDisplay = (data & 0x20) == 0x20;
10465        parentObj.gfxBackgroundBankOffset = ((data & 0x10) == 0x10) ? 0 : 0x80;
10466        parentObj.gfxBackgroundCHRBankPosition = ((data & 0x08) == 0x08) ? 0x400 : 0;
10467        parentObj.gfxSpriteNormalHeight = ((data & 0x04) == 0);
10468        parentObj.gfxSpriteShow = (data & 0x02) == 0x02;
10469        parentObj.bgEnabled = ((data & 0x01) == 0x01);
10470        parentObj.memory[0xFF40] = data;
10471      }
10472    }
10473    this.memoryHighWriter[0x41] = this.memoryWriter[0xFF41] = function (parentObj, address, data) {
10474      parentObj.LYCMatchTriggerSTAT = ((data & 0x40) == 0x40);
10475      parentObj.mode2TriggerSTAT = ((data & 0x20) == 0x20);
10476      parentObj.mode1TriggerSTAT = ((data & 0x10) == 0x10);
10477      parentObj.mode0TriggerSTAT = ((data & 0x08) == 0x08);
10478      parentObj.memory[0xFF41] = data & 0x78;
10479      if ((!parentObj.usedBootROM || !parentObj.usedGBCBootROM) && parentObj.LCDisOn && parentObj.modeSTAT < 2) {
10480        parentObj.interruptsRequested |= 0x2;
10481        parentObj.checkIRQMatching();
10482      }
10483    }
10484    this.memoryHighWriter[0x46] = this.memoryWriter[0xFF46] = function (parentObj, address, data) {
10485      parentObj.memory[0xFF46] = data;
10486      if (data > 0x7F && data < 0xE0) {  //DMG cannot DMA from the ROM banks.
10487        data <<= 8;
10488        address = 0xFE00;
10489        var stat = parentObj.modeSTAT;
10490        parentObj.modeSTAT = 0;
10491        var newData = 0;
10492        do {
10493          newData = parentObj.memoryReader[data](parentObj, data++);
10494          if (newData != parentObj.memory[address]) {
10495            //JIT the graphics render queue:
10496            parentObj.modeSTAT = stat;
10497            parentObj.graphicsJIT();
10498            parentObj.modeSTAT = 0;
10499            parentObj.memory[address++] = newData;
10500            break;
10501          }
10502        } while (++address < 0xFEA0);
10503        if (address < 0xFEA0) {
10504          do {
10505            parentObj.memory[address++] = parentObj.memoryReader[data](parentObj, data++);
10506            parentObj.memory[address++] = parentObj.memoryReader[data](parentObj, data++);
10507            parentObj.memory[address++] = parentObj.memoryReader[data](parentObj, data++);
10508            parentObj.memory[address++] = parentObj.memoryReader[data](parentObj, data++);
10509          } while (address < 0xFEA0);
10510        }
10511        parentObj.modeSTAT = stat;
10512      }
10513    }
10514    this.memoryHighWriter[0x47] = this.memoryWriter[0xFF47] = function (parentObj, address, data) {
10515      if (parentObj.memory[0xFF47] != data) {
10516        parentObj.midScanLineJIT();
10517        parentObj.updateGBBGPalette(data);
10518        parentObj.memory[0xFF47] = data;
10519      }
10520    }
10521    this.memoryHighWriter[0x48] = this.memoryWriter[0xFF48] = function (parentObj, address, data) {
10522      if (parentObj.memory[0xFF48] != data) {
10523        parentObj.midScanLineJIT();
10524        parentObj.updateGBOBJPalette(0, data);
10525        parentObj.memory[0xFF48] = data;
10526      }
10527    }
10528    this.memoryHighWriter[0x49] = this.memoryWriter[0xFF49] = function (parentObj, address, data) {
10529      if (parentObj.memory[0xFF49] != data) {
10530        parentObj.midScanLineJIT();
10531        parentObj.updateGBOBJPalette(4, data);
10532        parentObj.memory[0xFF49] = data;
10533      }
10534    }
10535    this.memoryHighWriter[0x4D] = this.memoryWriter[0xFF4D] = function (parentObj, address, data) {
10536      parentObj.memory[0xFF4D] = data;
10537    }
10538    this.memoryHighWriter[0x4F] = this.memoryWriter[0xFF4F] = this.cartIgnoreWrite;  //Not writable in DMG mode.
10539    this.memoryHighWriter[0x55] = this.memoryWriter[0xFF55] = this.cartIgnoreWrite;
10540    this.memoryHighWriter[0x68] = this.memoryWriter[0xFF68] = this.cartIgnoreWrite;
10541    this.memoryHighWriter[0x69] = this.memoryWriter[0xFF69] = this.cartIgnoreWrite;
10542    this.memoryHighWriter[0x6A] = this.memoryWriter[0xFF6A] = this.cartIgnoreWrite;
10543    this.memoryHighWriter[0x6B] = this.memoryWriter[0xFF6B] = this.cartIgnoreWrite;
10544    this.memoryHighWriter[0x6C] = this.memoryWriter[0xFF6C] = this.cartIgnoreWrite;
10545    this.memoryHighWriter[0x70] = this.memoryWriter[0xFF70] = this.cartIgnoreWrite;
10546    this.memoryHighWriter[0x74] = this.memoryWriter[0xFF74] = this.cartIgnoreWrite;
10547  }
10548}
10549GameBoyCore.prototype.recompileBootIOWriteHandling = function () {
10550  //Boot I/O Registers:
10551  if (this.inBootstrap) {
10552    this.memoryHighWriter[0x50] = this.memoryWriter[0xFF50] = function (parentObj, address, data) {
10553      cout("Boot ROM reads blocked: Bootstrap process has ended.", 0);
10554      parentObj.inBootstrap = false;
10555      parentObj.disableBootROM();      //Fill in the boot ROM ranges with ROM  bank 0 ROM ranges
10556      parentObj.memory[0xFF50] = data;  //Bits are sustained in memory?
10557    }
10558    if (this.cGBC) {
10559      this.memoryHighWriter[0x6C] = this.memoryWriter[0xFF6C] = function (parentObj, address, data) {
10560        if (parentObj.inBootstrap) {
10561          parentObj.cGBC = ((data & 0x1) == 0);
10562          //Exception to the GBC identifying code:
10563          if (parentObj.name + parentObj.gameCode + parentObj.ROM[0x143] == "Game and Watch 50") {
10564            parentObj.cGBC = true;
10565            cout("Created a boot exception for Game and Watch Gallery 2 (GBC ID byte is wrong on the cartridge).", 1);
10566          }
10567          cout("Booted to GBC Mode: " + parentObj.cGBC, 0);
10568        }
10569        parentObj.memory[0xFF6C] = data;
10570      }
10571    }
10572  }
10573  else {
10574    //Lockout the ROMs from accessing the BOOT ROM control register:
10575    this.memoryHighWriter[0x50] = this.memoryWriter[0xFF50] = this.cartIgnoreWrite;
10576  }
10577}
10578//Helper Functions
10579GameBoyCore.prototype.toTypedArray = function (baseArray, memtype) {
10580  try {
10581    // The following line was modified for benchmarking:
10582    if (settings[5] || (memtype != "float32" && GameBoyWindow.opera && this.checkForOperaMathBug())) {
10583      return baseArray;
10584    }
10585    if (!baseArray || !baseArray.length) {
10586      return [];
10587    }
10588    var length = baseArray.length;
10589    switch (memtype) {
10590      case "uint8":
10591        var typedArrayTemp = new Uint8Array(length);
10592        break;
10593      case "int8":
10594        var typedArrayTemp = new Int8Array(length);
10595        break;
10596      case "int32":
10597        var typedArrayTemp = new Int32Array(length);
10598        break;
10599      case "float32":
10600        var typedArrayTemp = new Float32Array(length);
10601    }
10602    for (var index = 0; index < length; index++) {
10603      typedArrayTemp[index] = baseArray[index];
10604    }
10605    return typedArrayTemp;
10606  }
10607  catch (error) {
10608    cout("Could not convert an array to a typed array: " + error.message, 1);
10609    return baseArray;
10610  }
10611}
10612GameBoyCore.prototype.fromTypedArray = function (baseArray) {
10613  try {
10614    if (!baseArray || !baseArray.length) {
10615      return [];
10616    }
10617    var arrayTemp = [];
10618    for (var index = 0; index < baseArray.length; ++index) {
10619      arrayTemp[index] = baseArray[index];
10620    }
10621    return arrayTemp;
10622  }
10623  catch (error) {
10624    cout("Conversion from a typed array failed: " + error.message, 1);
10625    return baseArray;
10626  }
10627}
10628GameBoyCore.prototype.getTypedArray = function (length, defaultValue, numberType) {
10629  try {
10630    if (settings[5]) {
10631      throw(new Error(""));
10632    }
10633    // The following line was modified for benchmarking:
10634    if (numberType != "float32" && GameBoyWindow.opera && this.checkForOperaMathBug()) {
10635      //Caught Opera breaking typed array math:
10636      throw(new Error(""));
10637    }
10638    switch (numberType) {
10639      case "int8":
10640        var arrayHandle = new Int8Array(length);
10641        break;
10642      case "uint8":
10643        var arrayHandle = new Uint8Array(length);
10644        break;
10645      case "int32":
10646        var arrayHandle = new Int32Array(length);
10647        break;
10648      case "float32":
10649        var arrayHandle = new Float32Array(length);
10650    }
10651    if (defaultValue != 0) {
10652      var index = 0;
10653      while (index < length) {
10654        arrayHandle[index++] = defaultValue;
10655      }
10656    }
10657  }
10658  catch (error) {
10659    cout("Could not convert an array to a typed array: " + error.message, 1);
10660    var arrayHandle = [];
10661    var index = 0;
10662    while (index < length) {
10663      arrayHandle[index++] = defaultValue;
10664    }
10665  }
10666  return arrayHandle;
10667}
10668GameBoyCore.prototype.checkForOperaMathBug = function () {
10669  var testTypedArray = new Uint8Array(1);
10670  testTypedArray[0] = -1;
10671  testTypedArray[0] >>= 0;
10672  if (testTypedArray[0] != 0xFF) {
10673    cout("Detected faulty math by your browser.", 2);
10674    return true;
10675  }
10676  else {
10677    return false;
10678  }
10679}
10680
10681// End of js/GameBoyCore.js file.
10682
10683// Start of js/GameBoyIO.js file.
10684
10685"use strict";
10686var gameboy = null;            //GameBoyCore object.
10687var gbRunInterval = null;        //GameBoyCore Timer
10688var settings = [            //Some settings.
10689  true,                 //Turn on sound.
10690  false,                //Boot with boot ROM first? (set to false for benchmarking)
10691  false,                //Give priority to GameBoy mode
10692  [39, 37, 38, 40, 88, 90, 16, 13],  //Keyboard button map.
10693  true,                //Colorize GB mode?
10694  false,                //Disallow typed arrays?
10695  4,                  //Interval for the emulator loop.
10696  15,                  //Audio buffer minimum span amount over x interpreter iterations.
10697  30,                  //Audio buffer maximum span amount over x interpreter iterations.
10698  false,                //Override to allow for MBC1 instead of ROM only (compatibility for broken 3rd-party cartridges).
10699  false,                //Override MBC RAM disabling and always allow reading and writing to the banks.
10700  false,                //Use the GameBoy boot ROM instead of the GameBoy Color boot ROM.
10701  false,                //Scale the canvas in JS, or let the browser scale the canvas?
10702  0x10,                //Internal audio buffer pre-interpolation factor.
10703  1                  //Volume level set.
10704];
10705function start(canvas, ROM) {
10706  clearLastEmulation();
10707  autoSave();  //If we are about to load a new game, then save the last one...
10708  gameboy = new GameBoyCore(canvas, ROM);
10709  gameboy.openMBC = openSRAM;
10710  gameboy.openRTC = openRTC;
10711  gameboy.start();
10712  run();
10713}
10714function run() {
10715  if (GameBoyEmulatorInitialized()) {
10716    if (!GameBoyEmulatorPlaying()) {
10717      gameboy.stopEmulator &= 1;
10718      cout("Starting the iterator.", 0);
10719      var dateObj = new_Date();  // The line is changed for benchmarking.
10720      gameboy.firstIteration = dateObj.getTime();
10721      gameboy.iterations = 0;
10722      // The following lines are commented out for benchmarking.
10723      // gbRunInterval = setInterval(function () {
10724      //  if (!document.hidden && !document.msHidden && !document.mozHidden && !document.webkitHidden) {
10725      //    gameboy.run();
10726      // }
10727      // }, settings[6]);
10728    }
10729    else {
10730      cout("The GameBoy core is already running.", 1);
10731    }
10732  }
10733  else {
10734    cout("GameBoy core cannot run while it has not been initialized.", 1);
10735  }
10736}
10737function pause() {
10738  if (GameBoyEmulatorInitialized()) {
10739    if (GameBoyEmulatorPlaying()) {
10740      clearLastEmulation();
10741    }
10742    else {
10743      cout("GameBoy core has already been paused.", 1);
10744    }
10745  }
10746  else {
10747    cout("GameBoy core cannot be paused while it has not been initialized.", 1);
10748  }
10749}
10750function clearLastEmulation() {
10751  if (GameBoyEmulatorInitialized() && GameBoyEmulatorPlaying()) {
10752    clearInterval(gbRunInterval);
10753    gameboy.stopEmulator |= 2;
10754    cout("The previous emulation has been cleared.", 0);
10755  }
10756  else {
10757    cout("No previous emulation was found to be cleared.", 0);
10758  }
10759}
10760function save() {
10761  if (GameBoyEmulatorInitialized()) {
10762    try {
10763      var state_suffix = 0;
10764      while (findValue("FREEZE_" + gameboy.name + "_" + state_suffix) != null) {
10765        state_suffix++;
10766      }
10767      setValue("FREEZE_" + gameboy.name + "_" + state_suffix, gameboy.saveState());
10768      cout("Saved the current state as: FREEZE_" + gameboy.name + "_" + state_suffix, 0);
10769    }
10770    catch (error) {
10771      cout("Could not save the current emulation state(\"" + error.message + "\").", 2);
10772    }
10773  }
10774  else {
10775    cout("GameBoy core cannot be saved while it has not been initialized.", 1);
10776  }
10777}
10778function saveSRAM() {
10779  if (GameBoyEmulatorInitialized()) {
10780    if (gameboy.cBATT) {
10781      try {
10782        var sram = gameboy.saveSRAMState();
10783        if (sram.length > 0) {
10784          cout("Saving the SRAM...", 0);
10785          if (findValue("SRAM_" + gameboy.name) != null) {
10786            //Remove the outdated storage format save:
10787            cout("Deleting the old SRAM save due to outdated format.", 0);
10788            deleteValue("SRAM_" + gameboy.name);
10789          }
10790          setValue("B64_SRAM_" + gameboy.name, arrayToBase64(sram));
10791        }
10792        else {
10793          cout("SRAM could not be saved because it was empty.", 1);
10794        }
10795      }
10796      catch (error) {
10797        cout("Could not save the current emulation state(\"" + error.message + "\").", 2);
10798      }
10799    }
10800    else {
10801      cout("Cannot save a game that does not have battery backed SRAM specified.", 1);
10802    }
10803    saveRTC();
10804  }
10805  else {
10806    cout("GameBoy core cannot be saved while it has not been initialized.", 1);
10807  }
10808}
10809function saveRTC() {  //Execute this when SRAM is being saved as well.
10810  if (GameBoyEmulatorInitialized()) {
10811    if (gameboy.cTIMER) {
10812      try {
10813        cout("Saving the RTC...", 0);
10814        setValue("RTC_" + gameboy.name, gameboy.saveRTCState());
10815      }
10816      catch (error) {
10817        cout("Could not save the RTC of the current emulation state(\"" + error.message + "\").", 2);
10818      }
10819    }
10820  }
10821  else {
10822    cout("GameBoy core cannot be saved while it has not been initialized.", 1);
10823  }
10824}
10825function autoSave() {
10826  if (GameBoyEmulatorInitialized()) {
10827    cout("Automatically saving the SRAM.", 0);
10828    saveSRAM();
10829    saveRTC();
10830  }
10831}
10832function openSRAM(filename) {
10833  try {
10834    if (findValue("B64_SRAM_" + filename) != null) {
10835      cout("Found a previous SRAM state (Will attempt to load).", 0);
10836      return base64ToArray(findValue("B64_SRAM_" + filename));
10837    }
10838    else if (findValue("SRAM_" + filename) != null) {
10839      cout("Found a previous SRAM state (Will attempt to load).", 0);
10840      return findValue("SRAM_" + filename);
10841    }
10842    else {
10843      cout("Could not find any previous SRAM copy for the current ROM.", 0);
10844    }
10845  }
10846  catch (error) {
10847    cout("Could not open the  SRAM of the saved emulation state.", 2);
10848  }
10849  return [];
10850}
10851function openRTC(filename) {
10852  try {
10853    if (findValue("RTC_" + filename) != null) {
10854      cout("Found a previous RTC state (Will attempt to load).", 0);
10855      return findValue("RTC_" + filename);
10856    }
10857    else {
10858      cout("Could not find any previous RTC copy for the current ROM.", 0);
10859    }
10860  }
10861  catch (error) {
10862    cout("Could not open the RTC data of the saved emulation state.", 2);
10863  }
10864  return [];
10865}
10866function openState(filename, canvas) {
10867  try {
10868    if (findValue(filename) != null) {
10869      try {
10870        clearLastEmulation();
10871        cout("Attempting to run a saved emulation state.", 0);
10872        gameboy = new GameBoyCore(canvas, "");
10873        gameboy.savedStateFileName = filename;
10874        gameboy.returnFromState(findValue(filename));
10875        run();
10876      }
10877      catch (error) {
10878        alert(error.message + " file: " + error.fileName + " line: " + error.lineNumber);
10879      }
10880    }
10881    else {
10882      cout("Could not find the save state " + filename + "\".", 2);
10883    }
10884  }
10885  catch (error) {
10886    cout("Could not open the saved emulation state.", 2);
10887  }
10888}
10889function import_save(blobData) {
10890  blobData = decodeBlob(blobData);
10891  if (blobData && blobData.blobs) {
10892    if (blobData.blobs.length > 0) {
10893      for (var index = 0; index < blobData.blobs.length; ++index) {
10894        cout("Importing blob \"" + blobData.blobs[index].blobID + "\"", 0);
10895        if (blobData.blobs[index].blobContent) {
10896          if (blobData.blobs[index].blobID.substring(0, 5) == "SRAM_") {
10897            setValue("B64_" + blobData.blobs[index].blobID, base64(blobData.blobs[index].blobContent));
10898          }
10899          else {
10900            setValue(blobData.blobs[index].blobID, JSON.parse(blobData.blobs[index].blobContent));
10901          }
10902        }
10903        else if (blobData.blobs[index].blobID) {
10904          cout("Save file imported had blob \"" + blobData.blobs[index].blobID + "\" with no blob data interpretable.", 2);
10905        }
10906        else {
10907          cout("Blob chunk information missing completely.", 2);
10908        }
10909      }
10910    }
10911    else {
10912      cout("Could not decode the imported file.", 2);
10913    }
10914  }
10915  else {
10916    cout("Could not decode the imported file.", 2);
10917  }
10918}
10919function generateBlob(keyName, encodedData) {
10920  //Append the file format prefix:
10921  var saveString = "EMULATOR_DATA";
10922  var consoleID = "GameBoy";
10923  //Figure out the length:
10924  var totalLength = (saveString.length + 4 + (1 + consoleID.length)) + ((1 + keyName.length) + (4 + encodedData.length));
10925  //Append the total length in bytes:
10926  saveString += to_little_endian_dword(totalLength);
10927  //Append the console ID text's length:
10928  saveString += to_byte(consoleID.length);
10929  //Append the console ID text:
10930  saveString += consoleID;
10931  //Append the blob ID:
10932  saveString += to_byte(keyName.length);
10933  saveString += keyName;
10934  //Now append the save data:
10935  saveString += to_little_endian_dword(encodedData.length);
10936  saveString += encodedData;
10937  return saveString;
10938}
10939function generateMultiBlob(blobPairs) {
10940  var consoleID = "GameBoy";
10941  //Figure out the initial length:
10942  var totalLength = 13 + 4 + 1 + consoleID.length;
10943  //Append the console ID text's length:
10944  var saveString = to_byte(consoleID.length);
10945  //Append the console ID text:
10946  saveString += consoleID;
10947  var keyName = "";
10948  var encodedData = "";
10949  //Now append all the blobs:
10950  for (var index = 0; index < blobPairs.length; ++index) {
10951    keyName = blobPairs[index][0];
10952    encodedData = blobPairs[index][1];
10953    //Append the blob ID:
10954    saveString += to_byte(keyName.length);
10955    saveString += keyName;
10956    //Now append the save data:
10957    saveString += to_little_endian_dword(encodedData.length);
10958    saveString += encodedData;
10959    //Update the total length:
10960    totalLength += 1 + keyName.length + 4 + encodedData.length;
10961  }
10962  //Now add the prefix:
10963  saveString = "EMULATOR_DATA" + to_little_endian_dword(totalLength) + saveString;
10964  return saveString;
10965}
10966function decodeBlob(blobData) {
10967  /*Format is as follows:
10968    - 13 byte string "EMULATOR_DATA"
10969    - 4 byte total size (including these 4 bytes).
10970    - 1 byte Console type ID length
10971    - Console type ID text of 8 bit size
10972    blobs {
10973      - 1 byte blob ID length
10974      - blob ID text (Used to say what the data is (SRAM/freeze state/etc...))
10975      - 4 byte blob length
10976      - blob length of 32 bit size
10977    }
10978  */
10979  var length = blobData.length;
10980  var blobProperties = {};
10981  blobProperties.consoleID = null;
10982  var blobsCount = -1;
10983  blobProperties.blobs = [];
10984  if (length > 17) {
10985    if (blobData.substring(0, 13) == "EMULATOR_DATA") {
10986      var length = Math.min(((blobData.charCodeAt(16) & 0xFF) << 24) | ((blobData.charCodeAt(15) & 0xFF) << 16) | ((blobData.charCodeAt(14) & 0xFF) << 8) | (blobData.charCodeAt(13) & 0xFF), length);
10987      var consoleIDLength = blobData.charCodeAt(17) & 0xFF;
10988      if (length > 17 + consoleIDLength) {
10989        blobProperties.consoleID = blobData.substring(18, 18 + consoleIDLength);
10990        var blobIDLength = 0;
10991        var blobLength = 0;
10992        for (var index = 18 + consoleIDLength; index < length;) {
10993          blobIDLength = blobData.charCodeAt(index++) & 0xFF;
10994          if (index + blobIDLength < length) {
10995            blobProperties.blobs[++blobsCount] = {};
10996            blobProperties.blobs[blobsCount].blobID = blobData.substring(index, index + blobIDLength);
10997            index += blobIDLength;
10998            if (index + 4 < length) {
10999              blobLength = ((blobData.charCodeAt(index + 3) & 0xFF) << 24) | ((blobData.charCodeAt(index + 2) & 0xFF) << 16) | ((blobData.charCodeAt(index + 1) & 0xFF) << 8) | (blobData.charCodeAt(index) & 0xFF);
11000              index += 4;
11001              if (index + blobLength <= length) {
11002                blobProperties.blobs[blobsCount].blobContent =  blobData.substring(index, index + blobLength);
11003                index += blobLength;
11004              }
11005              else {
11006                cout("Blob length check failed, blob determined to be incomplete.", 2);
11007                break;
11008              }
11009            }
11010            else {
11011              cout("Blob was incomplete, bailing out.", 2);
11012              break;
11013            }
11014          }
11015          else {
11016            cout("Blob was incomplete, bailing out.", 2);
11017            break;
11018          }
11019        }
11020      }
11021    }
11022  }
11023  return blobProperties;
11024}
11025function matchKey(key) {  //Maps a keyboard key to a gameboy key.
11026  //Order: Right, Left, Up, Down, A, B, Select, Start
11027  for (var index = 0; index < settings[3].length; index++) {
11028    if (settings[3][index] == key) {
11029      return index;
11030    }
11031  }
11032  return -1;
11033}
11034function GameBoyEmulatorInitialized() {
11035  return (typeof gameboy == "object" && gameboy != null);
11036}
11037function GameBoyEmulatorPlaying() {
11038  return ((gameboy.stopEmulator & 2) == 0);
11039}
11040function GameBoyKeyDown(e) {
11041  if (GameBoyEmulatorInitialized() && GameBoyEmulatorPlaying()) {
11042    var keycode = matchKey(e.keyCode);
11043    if (keycode >= 0 && keycode < 8) {
11044      gameboy.JoyPadEvent(keycode, true);
11045      try {
11046        e.preventDefault();
11047      }
11048      catch (error) { }
11049    }
11050  }
11051}
11052function GameBoyKeyUp(e) {
11053  if (GameBoyEmulatorInitialized() && GameBoyEmulatorPlaying()) {
11054    var keycode = matchKey(e.keyCode);
11055    if (keycode >= 0 && keycode < 8) {
11056      gameboy.JoyPadEvent(keycode, false);
11057      try {
11058        e.preventDefault();
11059      }
11060      catch (error) { }
11061    }
11062  }
11063}
11064function GameBoyGyroSignalHandler(e) {
11065  if (GameBoyEmulatorInitialized() && GameBoyEmulatorPlaying()) {
11066    if (e.gamma || e.beta) {
11067      gameboy.GyroEvent(e.gamma * Math.PI / 180, e.beta * Math.PI / 180);
11068    }
11069    else {
11070      gameboy.GyroEvent(e.x, e.y);
11071    }
11072    try {
11073      e.preventDefault();
11074    }
11075    catch (error) { }
11076  }
11077}
11078//The emulator will call this to sort out the canvas properties for (re)initialization.
11079function initNewCanvas() {
11080  if (GameBoyEmulatorInitialized()) {
11081    gameboy.canvas.width = gameboy.canvas.clientWidth;
11082    gameboy.canvas.height = gameboy.canvas.clientHeight;
11083  }
11084}
11085//Call this when resizing the canvas:
11086function initNewCanvasSize() {
11087  if (GameBoyEmulatorInitialized()) {
11088    if (!settings[12]) {
11089      if (gameboy.onscreenWidth != 160 || gameboy.onscreenHeight != 144) {
11090        gameboy.initLCD();
11091      }
11092    }
11093    else {
11094      if (gameboy.onscreenWidth != gameboy.canvas.clientWidth || gameboy.onscreenHeight != gameboy.canvas.clientHeight) {
11095        gameboy.initLCD();
11096      }
11097    }
11098  }
11099}
11100
11101// End of js/GameBoyIO.js file.
11102
11103// Start of realtime.js file.
11104// ROM code from Public Domain LPC2000 Demo "realtime" by AGO.
11105
11106gameboy_rom='r+BPyZiEZwA+AeBPySAobeEq6gAgKlYj5WJv6SRmZjjhKuXqACDJ/////////////////////////////////xgHZwCYhGcA2fX6/3/1xdXlIRPKNgHN9f/h0cHx6gAg+hLKtyAC8cnwgLcoF/CC7hjgUT6Q4FOv4FLgVOCAPv/gVfHZ8IG3IALx2fBA7gjgQA8PD+YB7gHgT/CC4FHuEOCCPojgU6/gUuBU4IE+/uBV4ID6NMs86jTL8dkKCgoKbWFkZSBieSBhZ28uIGVtYWlsOmdvYnV6b3ZAeWFob28uY29tCnVybDogc3BlY2N5LmRhLnJ1CgoKCv///////wDDSgnO7WZmzA0ACwNzAIMADAANAAgRH4iJAA7czG7m3d3Zmbu7Z2NuDuzM3dyZn7u5Mz5BR08nUyBSRUFMVElNRSCAAAAAAgEDADMBSTQeIUD/y37I8P/1y4fg//BE/pEg+su+8eD/yT7A4EY+KD0g/cnF1eWvEQPK1RITEhMGAyEAyuXFTgYAIWAMCQkqEhMqEhPB4SMFIOrhrwYIzYsU4dHByf////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8AAgMFBggJCwwOEBETFBYXGBobHR4fISIjJSYnKSorLC0uLzAxMjM0NTY3ODg5Ojo7PDw9PT4+Pj8/Pz9AQEBAQEBAQEBAPz8/Pz4+PT08PDs7Ojk5ODc2NTU0MzIxMC8uLCsqKSgmJSQjISAfHRwaGRcWFRMSEA8NCwoIBwUEAgH//fz6+ff29PPx8O7t6+ro5+Xk4uHg3t3c2tnY19bU09LR0M/OzczLysnJyMfGxsXFxMPDw8LCwcHBwcDAwMDAwMDAwMDBwcHBwsLDw8PExcXGxsfIycnKy8zNzs/Q0dLT1NXX2Nna3N3e4OHi5OXn6Onr7O7v8fL09vf5+vz9AAEECRAZJDFAUWR5kKnE4QAhRGmQueQRQHGk2RBJhMEAQYTJEFmk8UCR5DmQ6UShAGHEKZD5ZNFAsSSZEIkEgQCBBIkQmSSxQNFk+ZApxGEAoUTpkDnkkUDxpFkQyYRBAMGESRDZpHFAEeS5kGlEIQDhxKmQeWRRQDEkGRAJBAEAAQQJEBkkMUBRZHmQqcThACFEaZC55BFAcaTZEEmEwQBBhMkQWaTxQJHkOZDpRKEAYcQpkPlk0UCxJJkQiQSBAIEEiRCZJLFA0WT5kCnEYQChROmQOeSRQPGkWRDJhEEAwYRJENmkcUAR5LmQaUQhAOHEqZB5ZFFAMSQZEAkEAQAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAgICAgIDAwMDBAQEBAUFBQUGBgYHBwcICAkJCQoKCgsLDAwNDQ4ODw8QEBEREhITExQUFRUWFxcYGRkaGhscHB0eHh8gISEiIyQkJSYnJygpKisrLC0uLzAxMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdISUpLTE1OT1FSU1RVVldZWltcXV9gYWJkZWZnaWprbG5vcHJzdHZ3eXp7fX5/gYKEhYeIiouNjpCRk5SWl5manJ2foKKkpaepqqytr7GytLa3ubu9vsDCxMXHycvMztDS1NXX2dvd3+Hi5Obo6uzu8PL09vj6/P4A//z38Ofcz8CvnIdwVzwfAN+8l3BHHO/Aj1wn8Ld8PwC/fDfwp1wPwG8cx3AXvF8AnzzXcAecL8BP3Gfwd/x/AH/8d/Bn3E/AL5wHcNc8nwBfvBdwxxxvwA9cp/A3fL8AP3y38Cdcj8DvHEdwl7zfAB88V3CHnK/Az9zn8Pf8/wD//Pfw59zPwK+ch3BXPB8A37yXcEcc78CPXCfwt3w/AL98N/CnXA/AbxzHcBe8XwCfPNdwB5wvwE/cZ/B3/H8Af/x38GfcT8AvnAdw1zyfAF+8F3DHHG/AD1yn8Dd8vwA/fLfwJ1yPwO8cR3CXvN8AHzxXcIecr8DP3Ofw9/z/AP/////////////////////+/v7+/v79/f39/fz8/Pz8+/v7+vr6+vn5+fj4+Pf39/b29fX19PTz8/Ly8fHw8PDv7u7t7ezs6+vq6uno6Ofn5uXl5OPj4uHh4N/e3t3c3Nva2djY19bV1NTT0tHQz8/OzczLysnIx8bFxMPCwcDAvr28u7q5uLe2tbSzsrGwr62sq6qpqKalpKOioJ+enZyamZiWlZSTkZCPjYyLiYiHhYSCgYB+fXt6eHd1dHJxcG5sa2loZmVjYmBfXVtaWFdVU1JQTk1LSUhGREJBPz08Ojg2NDMxLy0rKigmJCIgHx0bGRcVExEPDQsJBwUDAf9/Px8PBwMBgEAgEAgEAgEAAQEBAQEBAQEBAQEA//////////////+AEAcAAQABAAEBAAEBAAEA/wD//wD//wD/AP+AKwcBAAEAAQD/AP8A/wD/AP8A/wABAAEAAQCARgcBAQEBAQD//////////////wABAQEBAQGAYQf///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+AwODw+Pz+/xEAwAGxwj4E9cU+BfUKbwMKZ37+gCALI34LAiN+AwILGOsahhIDHBwcHPE9IN7BIRAAGVRdPgX1Cm8DCmcalhIjfAILfQIDAx0dHR3xPSDnIRgAGVRd8T0grskRAcAB6cI+BPUKbwMKZ37+gCALI34LAiN+AwILGOs+CvUahhIcHBwc8T0g9CN8Agt9AgMD8T0g0MkgIEZJTExFRCAgIFBPTFlHT05TIEhFTElDT1BURVJJTiBBQ1RJT04gIQDADgpwLHQsGhPWICI2ACwNIPE+oOoQyngBCQDlYmsJVF3hDMYKR3AsdCwaG9YgIjYALA0g8a/qEcrJ+hDK/jDI1gTqEMpHPqCQ/lA4Aj5QDgAM1ggw+3ghAcARBAB3xggZDSD5+hHKg+oRykf+UDgCPlAOAAzWCDD7eC4td9YIGQ0g+ckh9grzMf/PzVABr+Am4P/gD+BD4EL2SOBFPkDgQT4E4AfN9RM+CuoAAA4HeeBwJqCvIstsKPsNIPIh/v8yy30g+wEKABH1/yFpAc3kE+cCAVYAEQDBIVt2zeQTrwYYIWsOzYsUIYsOzaQUxwGwAxEAgCGhF8XlzeQT4cERAIjN5BMhAJgRAwABYMDHcc9yIwUg+BQdIPHN9RMhuxUGAc2WE82JEz5E4EGv4EU+A+D/+z4B6hLK4E0QAAB4zccTBSD6zZATxwEACFkhAIhzIwt4sSD5IQDHPv9FdyRwJCJ3JXclcCwg8x5/IQCYx3PPNgDL1DYIx3PLlCPLVCjuPoABDxARIAAhIpjF5XfL1HfLlDwZDSD1POEswQUg7D486jPLr+o0yz3qL8s+oOCCPgLqG8vNiRM+ROBBr+BFPgPg/68+ACEXyyI+CiI+IHev6h7L4ITgluodyz4B6h/L6g/D6g3KBlARnAjNxAjNcwsBLAHFzTsLzQAJwQt4sSDzzZATxwEACFkhAIhzIwt4sSD5zfUTeQYQIYMOzYsUPv/qKcsGgBGwCM3ECM2JEwEsAcXNbAzNAAnBC3ixIPOv6hLKzZATPpDgU/PHAbADEQCIIaEXzeQTzfUTIQIWBgHNlhPNiRM+ROBBr+BFPgPg//sY/j4D6gAgzcRGBgMhF8t+gCJ+gDwifoB3zckP+jDLb/oxy2fNtgs+AeCB8IG3IPv6Dcq3KAPNcwHJ+h3LBgARTg2Hb2AZKmZvTgkq4ItfKjzgjD1PKuCNe4eHg0cRAMUqEhwFIPp5h4eBRxEAxCoSHAUg+n3qMMt86jHLyfCL4I7wjOCP8I3gkBEAw9XlzcoQ4dHwpeaAEhwBAwAJ8JA94JAg6CEAxQYPKk+gXxq3IB95yzegXxq3IBYqT6BfGrcgD3nLN6BfGrcgBiwsLBhHLOXNyhDwlrcoKwYB8KXGP0/LfygBBcXwpMY/Vx4AzZMOe8H18KPGP1ceAM2TDsHhJCJwGAzhJPCjxj8i8KTGPyIsJRbDBg/wjj3gjsLiCz4C6gAgw1JhfBjcHwAL7mpIYL9vBgMhF8t+gCJ+gDwifoB3zckPIcsNEQDGzf4MI+U+A+oAICEgy83+DPocy9YIb+ocy82vYAYDESDLIWIOxeXVzcoQ4fCjxhQi8KQiNg8jVF3hIyMjwQUg5M3ERsE+AeoAIAr+/ygiEQDGbyYAKRnlAwoDbyYAKRleI1bhKmZvxc0xHMwAQMEY2T4B4IHwgbcg+8l+PMjl1c3KEAYB8KVPy38oAQXF8KTLf/UoAi88Vx4AzZMO8XsgAi88xn/B9fCjy3/1KAIvPFceAM2TDvF7KAIvPMZ/wdESE3gSE+EjIyMYsFANAgAIDAYCRCgoFANEKAAUE0QAABQSRAAoFAJVKCjsA1UoAOwTVQAA7BJVACjsAAAEBQAAAAEFAAEBAwIGAQEDBwYCAgAHAwICAAcEAwMBAgYDAwEFBgQEAAECBAQAAwIFBQQFBgUFBAcGMgAAzgAAADIAAM4AAAAyAADOKAAAHhEAChEAAAAACu8AHu8AFAAKFAD2FAAPCgAF6AAC4gAQ3gAQ4gD+CgD74g4C3Q4C4QAC4vIC3fIC4AAM4PsM4PsQ4/sJ3fsJ/wABAQICAwMEBAUFAAAGAQYCBgMGBAYFBgAHAQcCBwMHBAcFBwYICQoKCwsMDA0NDgoPDxAQEQoSEhMTERQVFRYVFxUYCBkIGggb/yAAD/AbD/DlD/9//3+XEQAAAGD/f5cRAAAYAP9/lxEAAIB8lxH/f/9/QHz/f18IAADLI8sSeC9HeS9PAyEAAH2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEn2Pb3yPZwk4BWd9kW+3yxPLEssoyxkJ0BPJ+hfLJgJvfuCcLzzgnn3GQG9+4Jvgn6/gmOCZ4JrgneChPkDgl/oYy29OfcZAb0bFeOCgeeCizdMQ8KPgpvCk4KnwpeCsr+Cg4KI+QOChzdMQ8KPgp/Ck4KrwpeCtwXkvPOCgr+CheOCizdMQ8KPgmfCk4JzwpeCf8Kbgl/Cp4JrwrOCd8KfgmPCq4JvwreCe+hnLJgJvTn3GQG9GxXjgoHkvPOChr+CizdMQ8KPgpvCk4KnwpeCswXngoHjgoa/gos3TEPCj4KfwpOCq8KXgra/goOChPkDgos3TEPCj4JnwpOCc8KXgn/Cm4JfwqeCa8KzgnfCn4JjwquCb8K3gnskq4KAq4KEq4KLwl1/woCYGV8t6ICDLe3soJy88X3qTMAIvPG96g1YlXiVvfiVuZxl8LzwYH3ovPFfLeyjhey88X5IwAi88b3qDViVeJW9+JW5nGXxH8Jhf8KEmBlfLeiAgy3t7KCcvPF96kzACLzxveoNWJV4lb34lbmcZfC88GB96LzxXy3so4XsvPF+SMAIvPG96g1YlXiVvfiVuZxl8T/CZX/CiJgZXy3ogIMt7eygnLzxfepMwAi88b3qDViVeJW9+JW5nGXwvPBgfei88V8t7KOF7LzxfkjACLzxveoNWJV4lb34lbmcZfICB4KPwml/woCYGV8t6ICDLe3soJy88X3qTMAIvPG96g1YlXiVvfiVuZxl8LzwYH3ovPFfLeyjhey88X5IwAi88b3qDViVeJW9+JW5nGXxH8Jtf8KEmBlfLeiAgy3t7KCcvPF96kzACLzxveoNWJV4lb34lbmcZfC88GB96LzxXy3so4XsvPF+SMAIvPG96g1YlXiVvfiVuZxl8T/CcX/CiJgZXy3ogIMt7eygnLzxfepMwAi88b3qDViVeJW9+JW5nGXwvPBgfei88V8t7KOF7LzxfkjACLzxveoNWJV4lb34lbmcZfICB4KTwnV/woCYGV8t6ICDLe3soJy88X3qTMAIvPG96g1YlXiVvfiVuZxl8LzwYH3ovPFfLeyjhey88X5IwAi88b3qDViVeJW9+JW5nGXxH8J5f8KEmBlfLeiAgy3t7KCcvPF96kzACLzxveoNWJV4lb34lbmcZfC88GB96LzxXy3so4XsvPF+SMAIvPG96g1YlXiVvfiVuZxl8T/CfX/CiJgZXy3ogIMt7eygnLzxfepMwAi88b3qDViVeJW9+JW5nGXwvPBgfei88V8t7KOF7LzxfkjACLzxveoNWJV4lb34lbmcZfICB4KXJ9T6D4EDxyfWv4EDxyfXF1eXHKv7/KFD+FiAaTiMqh4eHVF1vJgApKXgGmAlHelRne11vGNzGYBLPeBIcGNN2ACETyjQ1KPc1yfvFBmR2AAUg+8HJ+3YABSD7yfXF1eUqEhMLeLEg+OHRwfHJxeUBAKAhAMDNAxThwcnF5XEjBSD74cHJxdXlAQCAIZXKzQMU4dHBycXV5a/qFcuwIAwaEyIaEzIEDXjqFcvlxRq+EyAPIxq+IAkTIw0gCMHhGBkrGyMjBSDmecFPBBoTIhoTIiEVyzThDSDS+hXL4dHBydVfzXIUuzD60cnF9cH6FMrLD6mAR/CLkR+AR/AFqOoUysHJ9cXltxcXF/aA4Ggq4GkFIPo+5OBH4cHxyfXF5bcXFxf2gOBqKuBrBSD6PuTgSOBJ4cHxyT4Q4ADwAC/LN+bwRz4g4ADwAC/mD7DqFsvJzyEAgK8GIE8+CCINIPwFIPnHIQCABiBPIg0g/AUg+cnFzQMVSs0eFcHJxc0RFUjNGRVLzSMVwcnFBgHNKxXBycUGABj2xQYDGPHFBgLNKxXByfXlh4eAJsBvceHxyfXlh4cmwG9GI04jXiNW4fHJ9cXV5eCDKjzK8BPWIF/wg835FF95xghPezwY6PXF1eXF1c13FdHBex4FIS3LGNUBKssR8NjNlRURGPzNlRURnP/NlRUR9v/NlRUR//8+LzwZOPwCA3ovV3svXxMZyTAwRlBT/zAwUE5UU/8wMExJTkVT/xYFB1dFTENPTUUgVE8WBQgtUkVBTFRJTUUtFgAJREVNTyBNQURFIEVTUEVDSUFMTFkWAQpGT1IgTENQJzIwMDAgUEFSVFn/FgAAR1JFRVRJTlg6ICAgICAgICAgICAWAAFEU0MsUEFOLFNBQixGQVRBTElUWRYAAkpFRkYgRlJPSFdFSU4sSUNBUlVTFgADRE9YLFFVQU5HLEFCWVNTICAgICAWAAQgICAgICAgICAgICAgICAgICAgIBYABUNSRURJVFM6ICAgICAgICAgICAgFgAGQUxMIEdGWCZDT0RFIEJZIEFHTyAWAAdIRUxJQ09QVEVSIDNEIE1PREVMIBYACENSRUFURUQgQlkgQlVTWSAgICAgFgAJICAgICAgICAgICAgICAgICAgICAWAApVU0VEIFNPRlRXQVJFOiAgICAgIBYAC1JHQkRTLE5PJENBU0gsRkFSICAgFgAMICAgICAgICAgICAgICAgICAgICAWAA1DT05UQUNUOiAgICAgICAgICAgIBYADkdPQlVaT1ZAWUFIT08uQ09NICAgFgAPSFRUUDovL1NQRUNDWS5EQS5SVSAWABAgICAgICAgICAgICAgICAgICAgIBYAEVNFRSBZT1UgT04gR0JERVYyMDAw/wAAAAAAAAAAAAAAAAAAAAAICBwUHBQ4KDgoMDBwUCAgKCh8VHxUKCgAAAAAAAAAABQUPip/QT4qfFT+gnxUKCgICDw0fkL8rP6Cfmr8hHhYJCR+Wn5SPCR4SPyU/LRISBgYPCR+Wjwkflr8tH5KNDQQEDgocFAgIAAAAAAAAAAABAQOChwUOCg4KDgoHBQICBAQOCgcFBwUHBQ4KHBQICAAABQUPio8NH5CPCx8VCgoAAAICBwUPDR+QjwsOCgQEAAAAAAAAAAAEBA4KHBQcFAAAAAAAAB8fP6CfHwAAAAAAAAAAAAAAAAwMHhIeEgwMAQEDgoeEjwkeEjwkOCgQEAYGDwkflr+qv6q/LR4SDAwGBg8JHxUPDQ4KHxs/oJ8fBwcPiJ+Wjw0eEj8vP6CfHwcHD4iflo8NE5K/LR4SDAwJCR+Wn5afFT8tP6CfGwQEBwcPiJ8XPyEfnr8tHhIMDAYGDwkeFj8pP66/LR4SDAwPDx+Qv66XFQ4KHBQcFAgIBwcPiJ+Wjwkflr8tPiIcHAcHD4iflr+sn5KfHT4iHBwAAAAAAgIHBQICBAQOCgQEAAACAgcFAgIEBA4KDgocFAAAAAAHBQ4KHBQcFA4KAAAAAAAADw8fkJ8fPyEeHgAAAAAAAA4KBwUHBQ4KHBQAAAYGDwkflr8tHhoEBA4KBAQHBw+In5a/rL8pPi4+IhwcBwcPiJ+Wv66/oL+uvy0SEg4OHxEflr8pP6a/LT4iHBwHBw+In5a5qbgoP6y/IxwcDAweEh8VH5a7qr+uvyEeHgcHD4ifFx8RHhY/Lz+gnx8HBw+Inxc/IT4uOCg4KBAQBwcPiJ+Wvy8/qL+uvyEeHgkJH5a/rr+gv66/LT8tEhIPDx+QjwsOChwUHhY/IR4eDw8fkI+Og4KXFT8tHhIMDAkJH5afFR+Qv66/LT8tEhIICBwUHBQ4KDkpP66fEQ4OCgofFR+Qv6q/rr8tPy0SEgkJH5a/pr+qv6y7qr8tEhIHBw+In5a7qruqvy0+IhwcBwcPiJ+Wv66/IT4uOCgQEAcHD4iflr+uv6q/LT+inZ2HBw+In5a/LT4iPy0/LRISBwcPiJ8XP6Cfnr8tPiIcHB8fP6CfGw4KHBQcFBwUCAgJCR+Wn5a7qruqvy0eEgwMERE7qruqnxUfFR4SHBQICAkJH5aflr+uv6q/KR8VCgoJCR+WnxUOCg8JH5a/LRISCQkflr8tPy0eEhwUHBQICA8PH5C/LT46Dwsflr8hHh4HBw+IjwsOChwUHhYfEQ4OEBA4KDwkHhIPCQeEg4KBAQ4OHxEPDQcFDgoeGj4iHBwGBg8JH5a7qpERAAAAAAAAAAAAAAAAAAAAAB8fP6CfHx81rdPfJJne5X1MAIvPEevyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxnLEcsXlDABhMsRyxeUMAGEyxHLF5QwAYTLEcsXlDABhMsRyxeUMAGEyxHLF5QwAYTLEcsXlDABhMsRyxeUMAGEeRcvT/F5MAIvPIVvJrcBAAA+t7zLEbrLED6/vcsRu8sQPj+8P8sRuj/LEL0/yxG7P8sQeLHIeKHAebcgB3xiV31rX3jLH9L/HD5AlU97lW96lPUwAi88R6/LGTABgB/LGTABgB/LGTABgB/LGTABgB/LGTABgB/LGTABgB/LGTABgB/LGTABgB/LGcsRyxeVMAGFyxHLF5UwAYXLEcsXlTABhcsRyxeVMAGFyxHLF5UwAYXLEcsXlTABhcsRyxeVMAGFyxHLF5UwAYV5Fy9P8XkwAi88hGcuQMMxHMsf0pcdPkCUT3qUZ3uV9TACLzxHr8sZMAGAH8sZMAGAH8sZMAGAH8sZMAGAH8sZMAGAH8sZMAGAH8sZMAGAH8sZMAGAH8sZyxHLF5QwAYTLEcsXlDABhMsRyxeUMAGEyxHLF5QwAYTLEcsXlDABhMsRyxeUMAGEyxHLF5QwAYTLEcsXlDABhHkXL0/xeTACLzyFbyZAwzEcyx/SoRt91r9PfZNvepT1MAIvPEevyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxnLEcsXlTABhcsRyxeVMAGFyxHLF5UwAYXLEcsXlTABhcsRyxeVMAGFyxHLF5UwAYXLEcsXlTABhcsRyxeVMAGFeRcvT/F5MAIvPIRnLr/DMRz//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////3q8MAVUZ3tdb3u90pdAfZNPepRfkTA+V3nLPy88g+CDPn+R5YdPbyYARCkpKQkBkVIJweV41kAXb3nWQB8fH+YPZ/CChGd55gcGB/YITwpP8INHLMl5S1+RV3nLPy88g+CDPneR5YdPbyYARCkpKQkBklsJweV41kAXb3nWQB8fH+YPZ/CChGd55gcGB/YITwpP8INHLMmVT3qUX5EwPld5yz8vPIPggz5/keWHT28mAEQpKSkJAR9BCcHleNZAF2951kAfHx/mD2fwgoRneeYHBgf2CE8KT/CDRyzJeUtfkVd5yz8vPIPggz53keWHT28mAEQpKSkJASBKCcHleNZAF2951kAfHx/mD2fwgoRneeYHBgf2CE8KT/CDRyzJfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkfrF3e8t4IAN6LCyAR8sJMAEkyX6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALH6xInvLeCAGessJMAEkgEcALMl+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASV+sXd7y3ggA3osLIBHywEwASXJfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsfrF3e8t4IAZ6ywEwASWARywsyf///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wHRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyLRe7o4A1pXewYHoE8K9XqgTwQK4JF6Hx8f5g9X8JNnTixGLXsfHx/mD1/wgoNne5LRxADB8JGiVy+mX3qhsyJ6L6ZfeqCzItF7ujgDWld7BgegTwr1eqBPBArgkXofHx/mD1fwk2dOLEYtex8fH+YPX/CCg2d7ktHEAMHwkaJXL6ZfeqGzInovpl96oLMi0Xu6OANaV3sGB6BPCvV6oE8ECuCReh8fH+YPV/CTZ04sRi17Hx8f5g9f8IKDZ3uS0cQAwfCRolcvpl96obMiei+mX3qgsyIxDsrh+eEWwxgNIf3Er+oLyuoMyiwsLPCPPcjgj14sGrcqKPDGeeCT+g3Ktygm+gvKPP4DIAI+AeoLyiAH+gzKPOoMyvoMyl8WyvCT1nkSe8bH4JMqTypHKuUmxl+Hh4M8PG8qX1Z5h4eBPDxveE4sh4eARjw8bypmb3y6OAViV31rX3y4OAVgR31pT3q4OAVQR3tZT3iU4JR8h+CV5dXFr+CSzUpifeCS0eHVzUpi0eE+AeCSzUpi8JRfPniTZy5Hr8sdMAGEH8sdMAGEH8sdMAGEH8sdMAGEH8sdMAGEH8sdMAGEH8sdMAGEH8sdMAGEH8sdxkBnCA7KMQDC5fCVb8l7vTBVfZNPepRfkTAkV3nLPy88Rz5/kU3Fh09vJgBEKSkJAfdiCcHlJsLwkm94BoDJeUtfkVd5yz8vPEc+d5FNxYdPbyYARCkpCQH4ZwnB5SbC8JJveAaAyZVPepRfkTAkV3nLPy88Rz5/kU3Fh09vJgBEKSkJAalsCcHlJsLwkm94BoDJeUtfkVd5yz8vPEc+d5FNxYdPbyYARCkpCQGqcQnB5SbC8JJveAaAyYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNg7gwBZOCcSwsDYO4MAWTgnEsLA2DuDAFk4JxLCwNyXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDXEsLIO4MAOTgg1xLCyDuDADk4INcSwsg7gwA5OCDcmDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDIO4MAWTgnEsLAyDuDAFk4JxLCwMg7gwBZOCcSwsDMlxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggxxLCyDuDADk4IMcSwsg7gwA5OCDHEsLIO4MAOTggzJxg+Hh+oawXovpl96obMiei+mX3qgszIkeRgAInAtJCJwLSQicC0kInAtJCJwLSQicC0kInAtJCJwLSQicC0kInAtJCJwLSQicC0kInAtJCJwLSQW/8n///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+qqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVACEzDDPAABIAEjPAMwwAIQAhMwwzwAASABIzwDMMACEAITMMM8AAEgASM8AzDAAhACEzDDPAABIAEjPAMwwAIQAhMwwzwAASABIzwDMMACEAITMMM8AAEgASM8AzDAAhACEzDDPAABIAEjPAMwwAIQAhMwwzwAASABIzwDMMACEAITMMM8AAEgASM8AzDAAhACEzDDPAABIAEjPAMwwAIQAhMwwzwAASABIzwDMMACEAITMMM8AAEgASM8AzDAAhACEzDDPAABIAEjPAMwwAIQAhMwwzwAASABIzwDMMACEAITMMM8AAEgASM8AzDAAhACEzDDPAABIAEjPAMwwAIQj8GH4y/WT7wO+B50CzINkI/Bh+Mv1k+8DvgedAsyDZCPwYfjL9ZPvA74HnQLMg2Qj8GH4y/WT7wO+B50CzINkI/Bh+Mv1k+8DvgedAsyDZCPwYfjL9ZPvA74HnQLMg2Qj8GH4y/WT7wO+B50CzINkI/Bh+Mv1k+8DvgedAsyDZCPwYfjL9ZPvA74HnQLMg2Qj8GH4y/WT7wO+B50CzINkI/Bh+Mv1k+8DvgedAsyDZCPwYfjL9ZPvA74HnQLMg2Qj8GH4y/WT7wO+B50CzINkI/Bh+Mv1k+8DvgedAsyDZCPwYfjL9ZPvA74HnQLMg2Qj8GH4y/WT7wO+B50CzINnMzMzMMzMzM8zMzMwzMzMzzMzMzDMzMzPMzMzMMzMzM8zMzMwzMzMzzMzMzDMzMzPMzMzMMzMzM8zMzMwzMzMzzMzMzDMzMzPMzMzMMzMzM8zMzMwzMzMzzMzMzDMzMzPMzMzMMzMzM8zMzMwzMzMzzMzMzDMzMzPMzMzMMzMzM8zMzMwzMzMzzMzMzDMzMzPMzMzMMzMzM8zMzMwzMzMzzMzMzDMzMzPMzMzMMzMzM8zMzMwzMzMzzMzMzDMzMzPMzMzMMzMzM8zMzMwzMzMzzMzMzDMzMzPMzMzMMzMzM8zMzMwzMzMzzMzMzDMzMzPMzMzMMzMzM8zMzMwzMzMzwMDAwAwMDAzAwMDADAwMDMDAwMAMDAwMwMDAwAwMDAzAwMDADAwMDMDAwMAMDAwMwMDAwAwMDAzAwMDADAwMDMDAwMAMDAwMwMDAwAwMDAzAwMDADAwMDMDAwMAMDAwMwMDAwAwMDAzAwMDADAwMDMDAwMAMDAwMwMDAwAwMDAzAwMDADAwMDMDAwMAMDAwMwMDAwAwMDAzAwMDADAwMDMDAwMAMDAwMwMDAwAwMDAzAwMDADAwMDMDAwMAMDAwMwMDAwAwMDAzAwMDADAwMDMDAwMAMDAwMwMDAwAwMDAzAwMDADAwMDMDAwMAMDAwMwMDAwAwMDAzAwMDADAwMDPHxAQEBAQEBHx8QEBAQEBDx8QEBAQEBAR8fEBAQEBAQ8fEBAQEBAQEfHxAQEBAQEPHxAQEBAQEBHx8QEBAQEBDx8QEBAQEBAR8fEBAQEBAQ8fEBAQEBAQEfHxAQEBAQEPHxAQEBAQEBHx8QEBAQEBDx8QEBAQEBAR8fEBAQEBAQ8fEBAQEBAQEfHxAQEBAQEPHxAQEBAQEBHx8QEBAQEBDx8QEBAQEBAR8fEBAQEBAQ8fEBAQEBAQEfHxAQEBAQEPHxAQEBAQEBHx8QEBAQEBDx8QEBAQEBAR8fEBAQEBAQ8fEBAQEBAQEfHxAQEBAQEPHxAQEBAQEBHx8QEBAQEBCqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlVVqqpVVaqqVVWqqlUC4XIscAl7InAJInAJInAJInAJInAJInAJInAJInAJInAJInAJInAJInAJInAJInAJInAJLCwly2XIJGjJycnJyeEicAlyLHAJeyJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSwsJctlyCRoycnJycnhInAJInAJcixwCXsicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAksLCXLZcgkaMnJycnJ4SJwCSJwCSJwCXIscAl7InAJInAJInAJInAJInAJInAJInAJInAJInAJInAJInAJInAJLCwly2XIJGjJycnJyeEicAkicAkicAkicAlyLHAJeyJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSwsJctlyCRoycnJycnhInAJInAJInAJInAJInAJcixwCXsicAkicAkicAkicAkicAkicAkicAkicAkicAkicAksLCXLZcgkaMnJycnJ4SJwCSJwCSJwCSJwCSJwCSJwCXIscAl7InAJInAJInAJInAJInAJInAJInAJInAJInAJLCwly2XIJGjJycnJyeEicAkicAkicAkicAkicAkicAkicAlyLHAJeyJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSwsJctlyCRoycnJycnhInAJInAJInAJInAJInAJInAJInAJInAJcixwCXsicAkicAkicAkicAkicAkicAkicAksLCXLZcgkaMnJycnJ4SJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCXIscAl7InAJInAJInAJInAJInAJInAJLCwly2XIJGjJycnJyeEicAkicAkicAkicAkicAkicAkicAkicAkicAkicAlyLHAJeyJwCSJwCSJwCSJwCSJwCSwsJctlyCRoycnJycnhInAJInAJInAJInAJInAJInAJInAJInAJInAJInAJInAJcixwCXsicAkicAkicAkicAksLCXLZcgkaMnJycnJ4SJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCXIscAl7InAJInAJInAJLCwly2XIJGjJycnJyeEicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAlyLHAJeyJwCSJwCSwsJctlyCRoycnJycnhInAJInAJInAJInAJInAJInAJInAJInAJInAJInAJInAJInAJInAJInAJcixwCXsicAksLCXLZcgkaMnJycnJ4SJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCSJwCXIscAl7LCwly2XIJGjJycnJydE+t5LI4IXmB8RSRPCFHx8focjlzTJE4XkicCwicCwicCwicCwicCwicCwicCwicCzJ+ABUXWhHeZAfyx1nATNZCfCCMQCv/qAoAzEAvwH/AOlHPgeQVF1HDjOvyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxkwAYAfyxlHIbRXCeViaz7/AQ8Ayfoay2/6G8uFZ/4UIAU+/y0YBtbsIAU8LOoby3zqGsvNr2AhlEbNyhDwpMagV/Cjxn9f1SGXRs3KEPCkxqBn8KPGf2/RzTEcKAsf2hhZH9oYWcPERny6OAViV31rX+XNmkbh1Xu90sxFe9ZA4Ih9k0884Il6lF/ghjzgij2RMGvgh3nLPy88g+CF8IIBDwBvVHzWQBfLN6GFZ3rmBxdvGAjwij3KAETgivCJX/CGV/CFGASCHSgLy38g+Ffwh4LghR3NYkUY2nvgifCIg1/l5gf2CG8mB1Z7aB8fHx/LHR/LHeYDxkBnrx7/6XnghpPgh3vLPy88geCF8IIBDwBvVHzWQBfLN6GFZ3rmBxdv8Ilf8IZX8IXLfyAHV/CHgh0YAYLghc1iRfCKPcoAROCKGN171kDgiHuVTzzgiXqUX+CGPOCKPZEwa+CHecs/LzyD4IXwggEPAG9UfNZAF8s3oYVneuYHF28YCPCKPcoAROCK8Ilf8IZX8IUYBIIdKAvLfyD4V/CHguCFHc0qRhjae+CJ8IiTX+XmB/YQbyYHVntoHx8fH8sdH8sd5gPGQGc+/1jpeeCGk+CHe8s/LzyB4IXwggEPAG9UfNZAF8s3oYVneuYHF2/wiV/whlfwhct/IAdX8IeCHRgBguCFzSpG8Io9ygBE4IoY3UYAALoAAHzWQMhPHx8f5h9HeeYHKAsE/gUwBvUhylblBT4PkCHJRoRn5fCCZ69vyfCCZ69vIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIsnxAQ8APcqEVz0odj0oOj0idwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwksLCXLZSgCJGgidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwksLCXLZSgCJGgidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwksLCXLZSgCJGgidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkidwkid8kicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAksLCUicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAksLCUicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAksLCUicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAksLCUicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAksLCUicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAksLCUicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAkicAloyfgAVF3wgjEAr/6gKAMxAL8B/wDFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcVia/nJJgJ+4JovPOCYfcZAb37gl+Cbr+CZ4JzgneCePkDgn8n/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////Aw==';
11107
11108// End of realtime.js file.
11109