blob: 5e98592c7976e32f6b49abdd1d109dedf348755e [file] [log] [blame]
Keun young Park33b34142012-10-30 17:51:52 -07001// 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.