summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGabriel Rodrigues Couto <gabrielrcouto@gmail.com>2016-02-21 09:42:33 -0300
committerGabriel Rodrigues Couto <gabrielrcouto@gmail.com>2016-02-21 09:42:33 -0300
commite8d613fb4757fd08806bdc4e506a9c483d8394a3 (patch)
treeeef74fd742e4bf0c9b94f65e361551ff274a9e12
downloadphp-terminal-gameboy-emulator-e8d613fb4757fd08806bdc4e506a9c483d8394a3.zip
php-terminal-gameboy-emulator-e8d613fb4757fd08806bdc4e506a9c483d8394a3.tar.gz
php-terminal-gameboy-emulator-e8d613fb4757fd08806bdc4e506a9c483d8394a3.tar.bz2
Initial commit
It Works! \o/
-rw-r--r--boot.php29
-rw-r--r--composer.json24
-rw-r--r--composer.lock73
-rwxr-xr-xdrmario.gbbin0 -> 32768 bytes
-rw-r--r--src/Gameboy/Cbopcode.php1145
-rw-r--r--src/Gameboy/Core.php3510
-rw-r--r--src/Gameboy/DrawContext.php52
-rw-r--r--src/Gameboy/Opcode.php1995
-rw-r--r--src/Gameboy/Settings.php77
-rw-r--r--src/Gameboy/TickTables.php55
-rw-r--r--teste.txtbin0 -> 13085 bytes
-rw-r--r--vendor/autoload.php7
-rw-r--r--vendor/composer/ClassLoader.php413
-rw-r--r--vendor/composer/LICENSE21
-rw-r--r--vendor/composer/autoload_classmap.php9
-rw-r--r--vendor/composer/autoload_namespaces.php11
-rw-r--r--vendor/composer/autoload_psr4.php9
-rw-r--r--vendor/composer/autoload_real.php45
-rw-r--r--vendor/composer/installed.json56
-rw-r--r--vendor/whatthejeff/drawille/.gitignore4
-rw-r--r--vendor/whatthejeff/drawille/.travis.yml12
-rw-r--r--vendor/whatthejeff/drawille/ChangeLog.md13
-rw-r--r--vendor/whatthejeff/drawille/LICENSE19
-rw-r--r--vendor/whatthejeff/drawille/README.md76
-rw-r--r--vendor/whatthejeff/drawille/composer.json29
-rw-r--r--vendor/whatthejeff/drawille/docs/images/octocat.pngbin0 -> 223075 bytes
-rw-r--r--vendor/whatthejeff/drawille/docs/images/sin.pngbin0 -> 93577 bytes
-rw-r--r--vendor/whatthejeff/drawille/docs/images/turtle.pngbin0 -> 262471 bytes
-rw-r--r--vendor/whatthejeff/drawille/examples/ImagePrinter.php76
-rw-r--r--vendor/whatthejeff/drawille/examples/basic.php52
-rw-r--r--vendor/whatthejeff/drawille/examples/composer.json31
-rwxr-xr-xvendor/whatthejeff/drawille/examples/img2term.php92
-rwxr-xr-xvendor/whatthejeff/drawille/examples/octocat2term.php125
-rw-r--r--vendor/whatthejeff/drawille/examples/turtle.php27
-rwxr-xr-xvendor/whatthejeff/drawille/examples/xkcd2term.php108
-rw-r--r--vendor/whatthejeff/drawille/phpunit.xml.dist9
-rw-r--r--vendor/whatthejeff/drawille/src/Drawille/Canvas.php237
-rw-r--r--vendor/whatthejeff/drawille/src/Drawille/Turtle.php242
-rw-r--r--vendor/whatthejeff/drawille/tests/Drawille/CanvasTest.php75
-rw-r--r--vendor/whatthejeff/drawille/tests/Drawille/TurtleTest.php57
40 files changed, 8815 insertions, 0 deletions
diff --git a/boot.php b/boot.php
new file mode 100644
index 0000000..3941a53
--- /dev/null
+++ b/boot.php
@@ -0,0 +1,29 @@
+<?php
+
+require_once __DIR__.'/vendor/autoload.php';
+
+use GameBoy\Core;
+use GameBoy\Settings;
+
+$rom = base64_decode(file_get_contents('drmario.rom'));
+
+$core = new Core(null, null, $rom);
+$core->start();
+
+if ($core->stopEmulator & 2 == 2) {
+ $core->stopEmulator &= 1;
+ $core->lastIteration = microtime(true);
+ // echo "Starting the iterator." . PHP_EOL;
+
+ //gbRunInterval = setInterval(continueCPU, settings[20]);
+
+ while (true) {
+ sleep(Settings::$settings[20] / 1000);
+ $core->run();
+ }
+} else if (($core->stopEmulator & 2) == 0) {
+ echo "The GameBoy core is already running." . PHP_EOL;
+}
+else {
+ echo "GameBoy core cannot run while it has not been initialized." . PHP_EOL;
+} \ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..028121e
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,24 @@
+{
+ "name": "gabrielrcouto/php-terminal-gameboy-emulator",
+ "description": "Terminal Gameboy Emular",
+ "keywords": ["terminal","console","gameboy","emulator"],
+ "homepage": "http://github.com/gabrielrcouto/php-terminal-gameboy-emulator",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Gabriel Rodrigues Couto",
+ "email": "gabrielrcouto@gmail.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.4.0",
+ "whatthejeff/drawille": "^1.0"
+ },
+ "require-dev": {
+ },
+ "autoload": {
+ "psr-0": {
+ "GameBoy": "src/"
+ }
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..5e1e806
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,73 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "This file is @generated automatically"
+ ],
+ "hash": "86366dfab291cc63b913e9fd405622b8",
+ "content-hash": "9d3f3f2a6403b572b1354b63d683c63a",
+ "packages": [
+ {
+ "name": "whatthejeff/drawille",
+ "version": "v1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/whatthejeff/php-drawille.git",
+ "reference": "bafea427f5bf2445413f6807000a95f70cc83bfd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/whatthejeff/php-drawille/zipball/bafea427f5bf2445413f6807000a95f70cc83bfd",
+ "reference": "bafea427f5bf2445413f6807000a95f70cc83bfd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.1.*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Drawille": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ }
+ ],
+ "description": "Terminal drawing with braille",
+ "homepage": "http://github.com/whatthejeff/php-drawille",
+ "keywords": [
+ "Turtle",
+ "braille",
+ "console",
+ "drawing",
+ "terminal"
+ ],
+ "time": "2014-05-26 13:45:31"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=5.4.0"
+ },
+ "platform-dev": []
+}
diff --git a/drmario.gb b/drmario.gb
new file mode 100755
index 0000000..937b548
--- /dev/null
+++ b/drmario.gb
Binary files differ
diff --git a/src/Gameboy/Cbopcode.php b/src/Gameboy/Cbopcode.php
new file mode 100644
index 0000000..062a691
--- /dev/null
+++ b/src/Gameboy/Cbopcode.php
@@ -0,0 +1,1145 @@
+<?php
+namespace GameBoy;
+
+class Cbopcode
+{
+ public $functionsArray = [];
+
+ public function __construct()
+ {
+ //#0x00:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerB & 0x80) == 0x80);
+ $parentObj->registerB = (($parentObj->registerB << 1) & 0xFF) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerB == 0);
+ };
+
+ //#0x01:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerC & 0x80) == 0x80);
+ $parentObj->registerC = (($parentObj->registerC << 1) & 0xFF) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerC == 0);
+ };
+ //#0x02:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerD & 0x80) == 0x80);
+ $parentObj->registerD = (($parentObj->registerD << 1) & 0xFF) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerD == 0);
+ };
+ //#0x03:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerE & 0x80) == 0x80);
+ $parentObj->registerE = (($parentObj->registerE << 1) & 0xFF) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerE == 0);
+ };
+ //#0x04:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registersHL & 0x8000) == 0x8000);
+ $parentObj->registersHL = (($parentObj->registersHL << 1) & 0xFE00) + (($parentObj->FCarry) ? 0x100 : 0) + ($parentObj->registersHL & 0xFF);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registersHL <= 0xFF);
+ };
+ //#0x05:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registersHL & 0x80) == 0x80);
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + (($parentObj->registersHL << 1) & 0xFF) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0xFF) == 0x00);
+ };
+ //#0x06:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $parentObj->FCarry = (($temp_var & 0x80) == 0x80);
+ $temp_var = (($temp_var << 1) & 0xFF) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->memoryWrite($parentObj->registersHL, $temp_var);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($temp_var == 0x00);
+ };
+ //#0x07:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerA & 0x80) == 0x80);
+ $parentObj->registerA = (($parentObj->registerA << 1) & 0xFF) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerA == 0x00);
+ };
+ //#0x08:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerB & 0x01) == 0x01);
+ $parentObj->registerB = (($parentObj->FCarry) ? 0x80 : 0) + ($parentObj->registerB >> 1);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerB == 0);
+ };
+ //#0x09:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerC & 0x01) == 0x01);
+ $parentObj->registerC = (($parentObj->FCarry) ? 0x80 : 0) + ($parentObj->registerC >> 1);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerC == 0);
+ };
+ //#0x0A:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerD & 0x01) == 0x01);
+ $parentObj->registerD = (($parentObj->FCarry) ? 0x80 : 0) + ($parentObj->registerD >> 1);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerD == 0);
+ };
+ //#0x0B:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerE & 0x01) == 0x01);
+ $parentObj->registerE = (($parentObj->FCarry) ? 0x80 : 0) + ($parentObj->registerE >> 1);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerE == 0);
+ };
+ //#0x0C:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registersHL & 0x0100) == 0x0100);
+ $parentObj->registersHL = (($parentObj->FCarry) ? 0x8000 : 0) + (($parentObj->registersHL >> 1) & 0xFF00) + ($parentObj->registersHL & 0xFF);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registersHL <= 0xFF);
+ };
+ //#0x0D:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registersHL & 0x01) == 0x01);
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + (($parentObj->FCarry) ? 0x80 : 0) + (($parentObj->registersHL & 0xFF) >> 1);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0xFF) == 0x00);
+ };
+ //#0x0E:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $parentObj->FCarry = (($temp_var & 0x01) == 0x01);
+ $temp_var = (($parentObj->FCarry) ? 0x80 : 0) + ($temp_var >> 1);
+ $parentObj->memoryWrite($parentObj->registersHL, $temp_var);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($temp_var == 0x00);
+ };
+ //#0x0F:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerA & 0x01) == 0x01);
+ $parentObj->registerA = (($parentObj->FCarry) ? 0x80 : 0) + ($parentObj->registerA >> 1);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerA == 0x00);
+ };
+ //#0x10:
+ $this->functionsArray[] = function ($parentObj) {
+ $newFCarry = (($parentObj->registerB & 0x80) == 0x80);
+ $parentObj->registerB = (($parentObj->registerB << 1) & 0xFF) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerB == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $newFCarry = (($parentObj->registerC & 0x80) == 0x80);
+ $parentObj->registerC = (($parentObj->registerC << 1) & 0xFF) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerC == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $newFCarry = (($parentObj->registerD & 0x80) == 0x80);
+ $parentObj->registerD = (($parentObj->registerD << 1) & 0xFF) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerD == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $newFCarry = (($parentObj->registerE & 0x80) == 0x80);
+ $parentObj->registerE = (($parentObj->registerE << 1) & 0xFF) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerE == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $newFCarry = (($parentObj->registersHL & 0x8000) == 0x8000);
+ $parentObj->registersHL = (($parentObj->registersHL << 1) & 0xFE00) + (($parentObj->FCarry) ? 0x100 : 0) + ($parentObj->registersHL & 0xFF);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registersHL <= 0xFF);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $newFCarry = (($parentObj->registersHL & 0x80) == 0x80);
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + (($parentObj->registersHL << 1) & 0xFF) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0xFF) == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $newFCarry = (($temp_var & 0x80) == 0x80);
+ $temp_var = (($temp_var << 1) & 0xFF) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->memoryWrite($parentObj->registersHL, $temp_var);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($temp_var == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $newFCarry = (($parentObj->registerA & 0x80) == 0x80);
+ $parentObj->registerA = (($parentObj->registerA << 1) & 0xFF) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerA == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $newFCarry = (($parentObj->registerB & 0x01) == 0x01);
+ $parentObj->registerB = (($parentObj->FCarry) ? 0x80 : 0) + ($parentObj->registerB >> 1);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerB == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $newFCarry = (($parentObj->registerC & 0x01) == 0x01);
+ $parentObj->registerC = (($parentObj->FCarry) ? 0x80 : 0) + ($parentObj->registerC >> 1);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerC == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $newFCarry = (($parentObj->registerD & 0x01) == 0x01);
+ $parentObj->registerD = (($parentObj->FCarry) ? 0x80 : 0) + ($parentObj->registerD >> 1);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerD == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $newFCarry = (($parentObj->registerE & 0x01) == 0x01);
+ $parentObj->registerE = (($parentObj->FCarry) ? 0x80 : 0) + ($parentObj->registerE >> 1);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerE == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $newFCarry = (($parentObj->registersHL & 0x0100) == 0x0100);
+ $parentObj->registersHL = (($parentObj->FCarry) ? 0x8000 : 0) + (($parentObj->registersHL >> 1) & 0xFF00) + ($parentObj->registersHL & 0xFF);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registersHL <= 0xFF);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $newFCarry = (($parentObj->registersHL & 0x01) == 0x01);
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + (($parentObj->FCarry) ? 0x80 : 0) + (($parentObj->registersHL & 0xFF) >> 1);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0xFF) == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $newFCarry = (($temp_var & 0x01) == 0x01);
+ $temp_var = (($parentObj->FCarry) ? 0x80 : 0) + ($temp_var >> 1);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->memoryWrite($parentObj->registersHL, $temp_var);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($temp_var == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $newFCarry = (($parentObj->registerA & 0x01) == 0x01);
+ $parentObj->registerA = (($parentObj->FCarry) ? 0x80 : 0) + ($parentObj->registerA >> 1);
+ $parentObj->FCarry = $newFCarry;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerA == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerB & 0x80) == 0x80);
+ $parentObj->registerB = ($parentObj->registerB << 1) & 0xFF;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerB == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerC & 0x80) == 0x80);
+ $parentObj->registerC = ($parentObj->registerC << 1) & 0xFF;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerC == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerD & 0x80) == 0x80);
+ $parentObj->registerD = ($parentObj->registerD << 1) & 0xFF;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerD == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerE & 0x80) == 0x80);
+ $parentObj->registerE = ($parentObj->registerE << 1) & 0xFF;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerE == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registersHL & 0x8000) == 0x8000);
+ $parentObj->registersHL = (($parentObj->registersHL << 1) & 0xFE00) + ($parentObj->registersHL & 0xFF);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registersHL <= 0xFF);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registersHL & 0x0080) == 0x0080);
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + (($parentObj->registersHL << 1) & 0xFF);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0xFF) == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $parentObj->FCarry = (($temp_var & 0x80) == 0x80);
+ $temp_var = ($temp_var << 1) & 0xFF;
+ $parentObj->memoryWrite($parentObj->registersHL, $temp_var);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($temp_var == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerA & 0x80) == 0x80);
+ $parentObj->registerA = ($parentObj->registerA << 1) & 0xFF;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerA == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerB & 0x01) == 0x01);
+ $parentObj->registerB = ($parentObj->registerB & 0x80) + ($parentObj->registerB >> 1);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerB == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerC & 0x01) == 0x01);
+ $parentObj->registerC = ($parentObj->registerC & 0x80) + ($parentObj->registerC >> 1);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerC == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerD & 0x01) == 0x01);
+ $parentObj->registerD = ($parentObj->registerD & 0x80) + ($parentObj->registerD >> 1);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerD == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerE & 0x01) == 0x01);
+ $parentObj->registerE = ($parentObj->registerE & 0x80) + ($parentObj->registerE >> 1);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerE == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registersHL & 0x0100) == 0x0100);
+ $parentObj->registersHL = (($parentObj->registersHL >> 1) & 0xFF00) + ($parentObj->registersHL & 0x80FF);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registersHL <= 0xFF);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registersHL & 0x0001) == 0x0001);
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF80) + (($parentObj->registersHL & 0xFF) >> 1);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0xFF) == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $parentObj->FCarry = (($temp_var & 0x01) == 0x01);
+ $temp_var = ($temp_var & 0x80) + ($temp_var >> 1);
+ $parentObj->memoryWrite($parentObj->registersHL, $temp_var);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($temp_var == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerA & 0x01) == 0x01);
+ $parentObj->registerA = ($parentObj->registerA & 0x80) + ($parentObj->registerA >> 1);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerA == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB = (($parentObj->registerB & 0xF) << 4) + ($parentObj->registerB >> 4);
+ $parentObj->FZero = ($parentObj->registerB == 0);
+ $parentObj->FCarry = $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC = (($parentObj->registerC & 0xF) << 4) + ($parentObj->registerC >> 4);
+ $parentObj->FZero = ($parentObj->registerC == 0);
+ $parentObj->FCarry = $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD = (($parentObj->registerD & 0xF) << 4) + ($parentObj->registerD >> 4);
+ $parentObj->FZero = ($parentObj->registerD == 0);
+ $parentObj->FCarry = $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE = (($parentObj->registerE & 0xF) << 4) + ($parentObj->registerE >> 4);
+ $parentObj->FZero = ($parentObj->registerE == 0);
+ $parentObj->FCarry = $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = (($parentObj->registersHL & 0xF00) << 4) + (($parentObj->registersHL & 0xF000) >> 4) + ($parentObj->registersHL & 0xFF);
+ $parentObj->FZero = ($parentObj->registersHL <= 0xFF);
+ $parentObj->FCarry = $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + (($parentObj->registersHL & 0xF) << 4) + (($parentObj->registersHL & 0xF0) >> 4);
+ $parentObj->FZero = (($parentObj->registersHL & 0xFF) == 0);
+ $parentObj->FCarry = $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $temp_var = (($temp_var & 0xF) << 4) + ($temp_var >> 4);
+ $parentObj->memoryWrite($parentObj->registersHL, $temp_var);
+ $parentObj->FZero = ($temp_var == 0);
+ $parentObj->FCarry = $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = (($parentObj->registerA & 0xF) << 4) + ($parentObj->registerA >> 4);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FCarry = $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerB & 0x01) == 0x01);
+ $parentObj->registerB >>= 1;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerB == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerC & 0x01) == 0x01);
+ $parentObj->registerC >>= 1;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerC == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerD & 0x01) == 0x01);
+ $parentObj->registerD >>= 1;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerD == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerE & 0x01) == 0x01);
+ $parentObj->registerE >>= 1;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerE == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registersHL & 0x0100) == 0x0100);
+ $parentObj->registersHL = (($parentObj->registersHL >> 1) & 0xFF00) + ($parentObj->registersHL & 0xFF);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registersHL <= 0xFF);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registersHL & 0x0001) == 0x0001);
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + (($parentObj->registersHL & 0xFF) >> 1);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0xFF) == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $parentObj->FCarry = (($temp_var & 0x01) == 0x01);
+ $parentObj->memoryWrite($parentObj->registersHL, $temp_var >>= 1);
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($temp_var == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerA & 0x01) == 0x01);
+ $parentObj->registerA >>= 1;
+ $parentObj->FHalfCarry = $parentObj->FSubtract = false;
+ $parentObj->FZero = ($parentObj->registerA == 0x00);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerB & 0x01) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerC & 0x01) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerD & 0x01) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerE & 0x01) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x0100) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x0001) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0x01) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerA & 0x01) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerB & 0x02) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerC & 0x02) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerD & 0x02) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerE & 0x02) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x0200) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x0002) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0x02) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerA & 0x02) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerB & 0x04) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerC & 0x04) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerD & 0x04) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerE & 0x04) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x0400) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x0004) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0x04) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerA & 0x04) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerB & 0x08) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerC & 0x08) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerD & 0x08) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerE & 0x08) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x0800) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x0008) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0x08) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerA & 0x08) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerB & 0x10) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerC & 0x10) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerD & 0x10) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerE & 0x10) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x1000) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x0010) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0x10) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerA & 0x10) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerB & 0x20) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerC & 0x20) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerD & 0x20) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerE & 0x20) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x2000) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x0020) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0x20) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerA & 0x20) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerB & 0x40) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerC & 0x40) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerD & 0x40) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerE & 0x40) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x4000) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x0040) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0x40) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerA & 0x40) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerB & 0x80) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerC & 0x80) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerD & 0x80) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerE & 0x80) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x8000) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registersHL & 0x0080) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0x80) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = false;
+ $parentObj->FZero = (($parentObj->registerA & 0x80) == 0);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB &= 0xFE;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC &= 0xFE;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD &= 0xFE;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE &= 0xFE;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xFEFF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xFFFE;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0xFE);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= 0xFE;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB &= 0xFD;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC &= 0xFD;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD &= 0xFD;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE &= 0xFD;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xFDFF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xFFFD;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0xFD);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= 0xFD;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB &= 0xFB;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC &= 0xFB;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD &= 0xFB;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE &= 0xFB;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xFBFF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xFFFB;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0xFB);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= 0xFB;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB &= 0xF7;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC &= 0xF7;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD &= 0xF7;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE &= 0xF7;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xF7FF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xFFF7;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0xF7);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= 0xF7;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB &= 0xEF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC &= 0xEF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD &= 0xEF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE &= 0xEF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xEFFF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xFFEF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0xEF);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= 0xEF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB &= 0xDF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC &= 0xDF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD &= 0xDF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE &= 0xDF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xDFFF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xFFDF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0xDF);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= 0xDF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB &= 0xBF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC &= 0xBF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD &= 0xBF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE &= 0xBF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xBFFF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xFFBF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0xBF);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= 0xBF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB &= 0x7F;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC &= 0x7F;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD &= 0x7F;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE &= 0x7F;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0x7FFF;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL &= 0xFF7F;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) & 0x7F);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= 0x7F;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB |= 0x01;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC |= 0x01;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD |= 0x01;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE |= 0x01;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x0100;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x01;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) | 0x01);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= 0x01;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB |= 0x02;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC |= 0x02;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD |= 0x02;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE |= 0x02;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x0200;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x02;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) | 0x02);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= 0x02;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB |= 0x04;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC |= 0x04;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD |= 0x04;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE |= 0x04;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x0400;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x04;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) | 0x04);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= 0x04;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB |= 0x08;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC |= 0x08;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD |= 0x08;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE |= 0x08;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x0800;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x08;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) | 0x08);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= 0x08;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB |= 0x10;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC |= 0x10;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD |= 0x10;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE |= 0x10;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x1000;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x10;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) | 0x10);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= 0x10;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB |= 0x20;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC |= 0x20;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD |= 0x20;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE |= 0x20;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x2000;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x20;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) | 0x20);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= 0x20;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB |= 0x40;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC |= 0x40;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD |= 0x40;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE |= 0x40;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x4000;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x40;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) | 0x40);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= 0x40;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB |= 0x80;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC |= 0x80;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD |= 0x80;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE |= 0x80;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x8000;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL |= 0x80;
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) | 0x80);
+ };
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= 0x80;
+ };
+ }
+
+ public function get()
+ {
+ return $this->functionsArray;
+ }
+} \ No newline at end of file
diff --git a/src/Gameboy/Core.php b/src/Gameboy/Core.php
new file mode 100644
index 0000000..32438bc
--- /dev/null
+++ b/src/Gameboy/Core.php
@@ -0,0 +1,3510 @@
+<?php
+namespace GameBoy;
+
+class Core
+{
+ // Canvas DOM object for drawing out the graphics to.
+ protected $canvas;
+
+ // Image DOM object for drawing out the graphics to as an alternate means.
+ protected $canvasAlt;
+
+ // Used for external scripts to tell if we're really using the canvas or not (Helpful with fullscreen switching).
+ public $canvasFallbackHappened = false;
+
+ // LCD Context
+ public $drawContext = null;
+
+ //The game's ROM.
+ public $ROMImage;
+
+ //The full ROM file dumped to an array.
+ public $ROM = [];
+
+ //Whether we're in the GBC boot ROM.
+ public $inBootstrap = true;
+
+ //Updated upon ROM loading...
+ public $usedBootROM = false;
+
+ // Accumulator (default is GB mode)
+ public $registerA = 0x01;
+
+ // bit 7 - Zero
+ public $FZero = true;
+
+ // bit 6 - Sub
+ public $FSubtract = false;
+
+ // bit 5 - Half Carry
+ public $FHalfCarry = true;
+
+ // bit 4 - Carry
+ public $FCarry = true;
+
+ // Register B
+ public $registerB = 0x00;
+
+ // Register C
+ public $registerC = 0x13;
+
+ // Register D
+ public $registerD = 0x00;
+
+ // Register E
+ public $registerE = 0xD8;
+
+ // Registers H and L
+ public $registersHL = 0x014D;
+
+ //Array of functions mapped to read back memory
+ public $memoryReader = [];
+
+ //Array of functions mapped to write to memory
+ public $memoryWriter = [];
+
+ // Stack Pointer
+ public $stackPointer = 0xFFFE;
+
+ // Program Counter
+ public $programCounter = 0x0100;
+
+ //Has the CPU been suspended until the next interrupt?
+ public $halt = false;
+
+ //Did we trip the DMG Halt bug?
+ public $skipPCIncrement = false;
+
+ //Has the emulation been paused or a frame has ended?
+ public $stopEmulator = 3;
+
+ //Are interrupts enabled?
+ public $IME = true;
+
+ //HDMA Transfer Flag - GBC only
+ public $hdmaRunning = false;
+
+ //The number of clock cycles emulated.
+ public $CPUTicks = 0;
+
+ //GBC Speed Multiplier
+ public $multiplier = 1;
+
+ //
+ //Main RAM, MBC RAM, GBC Main RAM, VRAM, etc.
+ //
+
+ //Main Core Memory
+ public $memory = [];
+
+ //Switchable RAM (Used by games for more RAM) for the main memory range 0xA000 - 0xC000.
+ public $MBCRam = [];
+
+ //Extra VRAM bank for GBC.
+ public $VRAM = [];
+
+ //Current VRAM bank for GBC.
+ public $currVRAMBank = 0;
+
+ //GBC main RAM Banks
+ public $GBCMemory = [];
+
+ //MBC1 Type (4/32, 16/8)
+ public $MBC1Mode = false;
+
+ //MBC RAM Access Control.
+ public $MBCRAMBanksEnabled = false;
+
+ //MBC Currently Indexed RAM Bank
+ public $currMBCRAMBank = 0;
+
+ //MBC Position Adder;
+ public $currMBCRAMBankPosition = -0xA000;
+
+ //GameBoy Color detection.
+ public $cGBC = false;
+
+ //Currently Switched GameBoy Color ram bank
+ public $gbcRamBank = 1;
+
+ //GBC RAM offset from address start.
+ public $gbcRamBankPosition = -0xD000;
+
+ //GBC RAM (ECHO mirroring) offset from address start.
+ public $gbcRamBankPositionECHO = -0xF000;
+
+ //Used to map the RAM banks to maximum size the MBC used can do.
+ public $RAMBanks = [0, 1, 2, 4, 16];
+
+ //Offset of the ROM bank switching.
+ public $ROMBank1offs = 0;
+
+ //The parsed current ROM bank selection.
+ public $currentROMBank = 0;
+
+ //Cartridge Type
+ public $cartridgeType = 0;
+
+ //Name of the game
+ public $name = "";
+
+ //Game code (Suffix for older games)
+ public $gameCode = "";
+
+ //A boolean to see if this was loaded in as a save state.
+ public $fromSaveState = false;
+
+ //When loaded in as a save state, this will not be empty.
+ public $savedStateFileName = "";
+
+ //Tracker for STAT triggering.
+ public $STATTracker = 0;
+
+ //The scan line mode (for lines 1-144 it's 2-3-0, for 145-154 it's 1)
+ public $modeSTAT = 0;
+
+ //Should we trigger an interrupt if LY==LYC?
+ public $LYCMatchTriggerSTAT = false;
+
+ //Should we trigger an interrupt if in mode 2?
+ public $mode2TriggerSTAT = false;
+
+ //Should we trigger an interrupt if in mode 1?
+ public $mode1TriggerSTAT = false;
+
+ //Should we trigger an interrupt if in mode 0?
+ public $mode0TriggerSTAT = false;
+
+ //Is the emulated LCD controller on?
+ public $LCDisOn = false;
+
+ //Array of functions to handle each scan line we do (onscreen + offscreen)
+ public $LINECONTROL;
+
+ public $DISPLAYOFFCONTROL = [];
+
+ //Pointer to either LINECONTROL or DISPLAYOFFCONTROL.
+ public $LCDCONTROL = null;
+
+ public $gfxWindowY = false;
+
+ public $gfxWindowDisplay = false;
+
+ public $gfxSpriteShow = false;
+
+ public $gfxSpriteDouble = false;
+
+ public $gfxBackgroundY = false;
+
+ public $gfxBackgroundX = false;
+
+ public $TIMAEnabled = false;
+
+ //Joypad State (two four-bit states actually)
+ public $JoyPad = 0xFF;
+
+ //
+ //RTC:
+ //
+ public $RTCisLatched = true;
+
+ public $latchedSeconds = 0;
+
+ public $latchedMinutes = 0;
+
+ public $latchedHours = 0;
+
+ public $latchedLDays = 0;
+
+ public $latchedHDays = 0;
+
+ public $RTCSeconds = 0;
+
+ public $RTCMinutes = 0;
+
+ public $RTCHours = 0;
+
+ public $RTCDays = 0;
+
+ public $RTCDayOverFlow = false;
+
+ public $RTCHALT = false;
+
+ //
+ //Sound variables:
+ //
+
+ //Audio object or the WAV PCM generator wrapper
+ public $audioHandle = null;
+
+ //Buffering counter for the WAVE PCM output.
+ public $outTracker = 0;
+
+ //Buffering limiter for WAVE PCM output.
+ public $outTrackerLimit = 0;
+
+ //Length of the sound buffers.
+ public $numSamplesTotal = 0;
+
+ //Length of the sound buffer for one channel.
+ public $sampleSize = 0;
+
+ public $dutyLookup = [0.125, 0.25, 0.5, 0.75];
+
+ //The audio buffer we're working on (When not overflowing).
+ public $audioSamples = [];
+
+ //Audio overflow buffer.
+ public $audioBackup = [];
+
+ //Pointer to the sample workbench.
+ public $currentBuffer = null;
+
+ //How many channels are being fed into the left side stereo / mono.
+ public $channelLeftCount = 0;
+
+ //How many channels are being fed into the right side stereo.
+ public $channelRightCount = 0;
+
+ public $noiseTableLookup = null;
+
+ public $smallNoiseTable = [0x80];
+
+ public $largeNoiseTable = [0x8000];
+
+ //As its name implies
+ public $soundMasterEnabled = false;
+
+ //Track what method we're using for audio output.
+ public $audioType = -1;
+
+ //
+ //Vin Shit:
+ //
+
+ public $VinLeftChannelEnabled = false;
+
+ public $VinRightChannelEnabled = false;
+
+ public $VinLeftChannelMasterVolume = 0;
+
+ public $VinRightChannelMasterVolume = 0;
+
+ public $vinLeft = 1;
+
+ public $vinRight = 1;
+
+ //Channels Enabled:
+
+ //Which channels are enabled for left side stereo / mono?
+ public $leftChannel = [true, true, true, false];
+
+ //Which channels are enabled for right side stereo?
+ public $rightChannel = [true, true, true, false];
+
+
+ //Current Samples Being Computed:
+ public $currentSampleLeft = 0;
+
+ public $currentSampleRight = 0;
+
+ public $channel3Tracker = 0;
+
+ //
+ //Pre-multipliers to cache some calculations:
+ //
+ public $preChewedAudioComputationMultiplier;
+
+ public $preChewedWAVEAudioComputationMultiplier;
+
+ public $whiteNoiseFrequencyPreMultiplier;
+
+ //Premultiplier for audio samples per instructions.
+ public $samplesOut = 0;
+
+ public $volumeEnvelopePreMultiplier;
+
+ public $channel1TimeSweepPreMultiplier;
+
+ public $channel1lastTotalLength;
+
+ public $channel1lastTimeSweep;
+
+ public $channel1frequency;
+
+ public $channel2lastTotalLength;
+
+ public $channel2frequency;
+
+ public $channel3frequency;
+
+ public $channel4lastTotalLength;
+
+ public $audioTotalLengthMultiplier;
+
+ //
+ //Audio generation counters:
+ //
+
+ public $audioOverflow = false;
+
+ //Used to sample the audio system every x CPU instructions.
+ public $audioTicks = 0;
+
+ //Used to keep alignment on audio generation.
+ public $audioIndex = 0;
+
+ //Used to keep alignment on the number of samples to output (Realign from counter alias).
+ public $rollover = 0;
+
+ //
+ //Timing Variables
+ //
+
+ //Times for how many instructions to execute before ending the loop.
+ public $emulatorTicks = 0;
+
+ // DIV Ticks Counter (Invisible lower 8-bit)
+ public $DIVTicks = 14;
+
+ // ScanLine Counter
+ public $LCDTicks = 15;
+
+ // Timer Ticks Count
+ public $timerTicks = 0;
+
+ // Timer Max Ticks
+ public $TACClocker = 256;
+
+ //Are the interrupts on queue to be enabled?
+ public $untilEnable = 0;
+
+ //The last time we iterated the main loop.
+ public $lastIteration = 0;
+
+ //Actual scan line...
+ public $actualScanLine = 0;
+
+ //
+ //ROM Cartridge Components:
+ //
+
+ //Does the cartridge use MBC1?
+ public $cMBC1 = false;
+
+ //Does the cartridge use MBC2?
+ public $cMBC2 = false;
+
+ //Does the cartridge use MBC3?
+ public $cMBC3 = false;
+
+ //Does the cartridge use MBC5?
+ public $cMBC5 = false;
+
+ //Does the cartridge use save RAM?
+ public $cSRAM = false;
+
+ public $cMMMO1 = false;
+
+ //Does the cartridge use the RUMBLE addressing (modified MBC5)?
+ public $cRUMBLE = false;
+
+ public $cCamera = false;
+
+ public $cTAMA5 = false;
+
+ public $cHuC3 = false;
+
+ public $cHuC1 = false;
+
+ // 1 Bank = 16 KBytes = 256 Kbits
+ public $ROMBanks = [
+ 2, 4, 8, 16, 32, 64, 128, 256, 512
+ ];
+
+ //How many RAM banks were actually allocated?
+ public $numRAMBanks = 0;
+
+ //
+ //Graphics Variables
+ //
+
+ //To prevent the repeating of drawing a blank screen.
+ public $drewBlank = 0;
+
+ // tile data arrays
+ public $tileData = [];
+
+ public $frameBuffer = [];
+
+ public $scaledFrameBuffer = [];
+
+ public $canvasBuffer;
+
+ public $gbcRawPalette = [];
+
+ //GB: 384, GBC: 384 * 2
+ public $tileCount = 384;
+
+ public $tileCountInvalidator;
+
+ public $colorCount = 12;
+
+ public $gbPalette = [];
+
+ public $gbColorizedPalette = [];
+
+ public $gbcPalette = [];
+
+ // min "attrib" value where transparency can occur (Default is 4 (GB mode))
+ public $transparentCutoff = 4;
+
+ public $bgEnabled = true;
+
+ public $spritePriorityEnabled = true;
+
+ // true if there are any images to be invalidated
+ public $tileReadState = [];
+
+ public $windowSourceLine = 0;
+
+ //"Classic" GameBoy palette colors.
+ public $colors = [0x80EFFFDE, 0x80ADD794, 0x80529273, 0x80183442];
+
+ //Frame skip tracker
+ public $frameCount;
+
+ public $weaveLookup = [];
+
+ public $width = 160;
+
+ public $height = 144;
+
+ public $pixelCount;
+
+ public $rgbCount;
+
+ public $widthRatio;
+
+ public $heightRatio;
+
+ //Pointer to the current palette we're using (Used for palette switches during boot or so it can be done anytime)
+ public $palette = null;
+
+ //
+ //Data
+ //
+
+ public $DAATable;
+
+ public $GBCBOOTROM;
+
+ public $ffxxDump;
+
+ public $OPCODE;
+
+ public $CBOPCODE;
+
+ public $TICKTable;
+
+ public $SecondaryTICKTable;
+
+ // Added
+
+ public $cTIMER = null;
+
+ public function __construct($canvas, $canvasAlt, $ROMImage)
+ {
+ $this->canvas = $canvas;
+ $this->canvasAlt = $canvasAlt;
+ $this->ROMImage = $ROMImage;
+
+ $this->DISPLAYOFFCONTROL[] = function ($parentObj) {
+ //Array of line 0 function to handle the LCD controller when it's off (Do nothing!).
+ };
+
+ $this->preChewedAudioComputationMultiplier = 0x20000 / Settings::$settings[14];
+ $this->preChewedWAVEAudioComputationMultiplier = 0x200000 / Settings::$settings[14];
+ $this->whiteNoiseFrequencyPreMultiplier = 4194300 / Settings::$settings[14] / 8;
+
+ $this->volumeEnvelopePreMultiplier = Settings::$settings[14] / 0x40;
+ $this->channel1TimeSweepPreMultiplier = Settings::$settings[14] / 0x80;
+ $this->audioTotalLengthMultiplier = Settings::$settings[14] / 0x100;
+
+ $this->tileCountInvalidator = $this->tileCount * 4;
+
+ $this->ROMBanks[0x52] = 72;
+ $this->ROMBanks[0x53] = 80;
+ $this->ROMBanks[0x54] = 96;
+
+ $this->frameCount = Settings::$settings[12];
+ $this->pixelCount = $this->width * $this->height;
+ $this->rgbCount = $this->pixelCount * 4;
+ $this->widthRatio = 160 / $this->width;
+ $this->heightRatio = 144 / $this->height;
+
+ // Copy Data
+ $this->DAATable = Data::$DAATable;
+ $this->GBCBOOTROM = Data::$GBCBOOTROM;
+ $this->ffxxDump = Data::$ffxxDump;
+
+ $opcode = new Opcode();
+ $this->OPCODE = $opcode->get();
+
+ $cbopcode = new Cbopcode();
+ $this->CBOPCODE = $cbopcode->get();
+
+ $this->TICKTable = TICKTables::$primary;
+ $this->SecondaryTICKTable = TICKTables::$secondary;
+
+ $this->LINECONTROL = array_fill(0, 154, null);
+
+ // $this->initializeStartState();
+ }
+
+ public function saveState()
+ {
+ return [
+ $this->fromTypedArray($this->ROM),
+ $this->inBootstrap,
+ $this->registerA,
+ $this->FZero,
+ $this->FSubtract,
+ $this->FHalfCarry,
+ $this->FCarry,
+ $this->registerB,
+ $this->registerC,
+ $this->registerD,
+ $this->registerE,
+ $this->registersHL,
+ $this->stackPointer,
+ $this->programCounter,
+ $this->halt,
+ $this->IME,
+ $this->hdmaRunning,
+ $this->CPUTicks,
+ $this->multiplier,
+ $this->fromTypedArray($this->memory),
+ $this->fromTypedArray($this->MBCRam),
+ $this->fromTypedArray($this->VRAM),
+ $this->currVRAMBank,
+ $this->fromTypedArray($this->GBCMemory),
+ $this->MBC1Mode,
+ $this->MBCRAMBanksEnabled,
+ $this->currMBCRAMBank,
+ $this->currMBCRAMBankPosition,
+ $this->cGBC,
+ $this->gbcRamBank,
+ $this->gbcRamBankPosition,
+ $this->ROMBank1offs,
+ $this->currentROMBank,
+ $this->cartridgeType,
+ $this->name,
+ $this->gameCode,
+ $this->modeSTAT,
+ $this->LYCMatchTriggerSTAT,
+ $this->mode2TriggerSTAT,
+ $this->mode1TriggerSTAT,
+ $this->mode0TriggerSTAT,
+ $this->LCDisOn,
+ $this->gfxWindowY,
+ $this->gfxWindowDisplay,
+ $this->gfxSpriteShow,
+ $this->gfxSpriteDouble,
+ $this->gfxBackgroundY,
+ $this->gfxBackgroundX,
+ $this->TIMAEnabled,
+ $this->DIVTicks,
+ $this->LCDTicks,
+ $this->timerTicks,
+ $this->TACClocker,
+ $this->untilEnable,
+ $this->lastIteration,
+ $this->cMBC1,
+ $this->cMBC2,
+ $this->cMBC3,
+ $this->cMBC5,
+ $this->cSRAM,
+ $this->cMMMO1,
+ $this->cRUMBLE,
+ $this->cCamera,
+ $this->cTAMA5,
+ $this->cHuC3,
+ $this->cHuC1,
+ $this->drewBlank,
+ $this->tileData.slice(0),
+ $this->fromTypedArray($this->frameBuffer),
+ $this->tileCount,
+ $this->colorCount,
+ $this->gbPalette,
+ $this->gbcRawPalette,
+ $this->gbcPalette,
+ $this->transparentCutoff,
+ $this->bgEnabled,
+ $this->spritePriorityEnabled,
+ $this->fromTypedArray($this->tileReadState),
+ $this->windowSourceLine,
+ $this->channel1adjustedFrequencyPrep,
+ $this->channel1duty,
+ $this->channel1lastSampleLookup,
+ $this->channel1adjustedDuty,
+ $this->channel1totalLength,
+ $this->channel1envelopeVolume,
+ $this->channel1currentVolume,
+ $this->channel1envelopeType,
+ $this->channel1envelopeSweeps,
+ $this->channel1consecutive,
+ $this->channel1frequency,
+ $this->channel1volumeEnvTime,
+ $this->channel1lastTotalLength,
+ $this->channel1timeSweep,
+ $this->channel1lastTimeSweep,
+ $this->channel1numSweep,
+ $this->channel1frequencySweepDivider,
+ $this->channel1decreaseSweep,
+ $this->channel2adjustedFrequencyPrep,
+ $this->channel2duty,
+ $this->channel2lastSampleLookup,
+ $this->channel2adjustedDuty,
+ $this->channel2totalLength,
+ $this->channel2envelopeVolume,
+ $this->channel2currentVolume,
+ $this->channel2envelopeType,
+ $this->channel2envelopeSweeps,
+ $this->channel2consecutive,
+ $this->channel2frequency,
+ $this->channel2volumeEnvTime,
+ $this->channel2lastTotalLength,
+ $this->channel3canPlay,
+ $this->channel3totalLength,
+ $this->channel3lastTotalLength,
+ $this->channel3patternType,
+ $this->channel3frequency,
+ $this->channel3consecutive,
+ $this->channel3PCM,
+ $this->channel3adjustedFrequencyPrep,
+ $this->channel4adjustedFrequencyPrep,
+ $this->channel4lastSampleLookup,
+ $this->channel4totalLength,
+ $this->channel4envelopeVolume,
+ $this->channel4currentVolume,
+ $this->channel4envelopeType,
+ $this->channel4envelopeSweeps,
+ $this->channel4consecutive,
+ $this->channel4volumeEnvTime,
+ $this->channel4lastTotalLength,
+ $this->soundMasterEnabled,
+ $this->VinLeftChannelEnabled,
+ $this->VinRightChannelEnabled,
+ $this->VinLeftChannelMasterVolume,
+ $this->VinRightChannelMasterVolume,
+ $this->vinLeft,
+ $this->vinRight,
+ $this->leftChannel,
+ $this->rightChannel,
+ $this->actualScanLine,
+ $this->RTCisLatched,
+ $this->latchedSeconds,
+ $this->latchedMinutes,
+ $this->latchedHours,
+ $this->latchedLDays,
+ $this->latchedHDays,
+ $this->RTCSeconds,
+ $this->RTCMinutes,
+ $this->RTCHours,
+ $this->RTCDays,
+ $this->RTCDayOverFlow,
+ $this->RTCHALT,
+ $this->gbColorizedPalette,
+ $this->usedBootROM,
+ $this->skipPCIncrement,
+ $this->STATTracker,
+ $this->gbcRamBankPositionECHO,
+ $this->numRAMBanks
+ ];
+ }
+
+ public function returnFromState($returnedFrom)
+ {
+ $index = 0;
+ $state = $returnedFrom->slice(0);
+
+ $this->ROM = $this->toTypedArray($state[$index++], false, false);
+ $this->inBootstrap = $state[$index++];
+ $this->registerA = $state[$index++];
+ $this->FZero = $state[$index++];
+ $this->FSubtract = $state[$index++];
+ $this->FHalfCarry = $state[$index++];
+ $this->FCarry = $state[$index++];
+ $this->registerB = $state[$index++];
+ $this->registerC = $state[$index++];
+ $this->registerD = $state[$index++];
+ $this->registerE = $state[$index++];
+ $this->registersHL = $state[$index++];
+ $this->stackPointer = $state[$index++];
+ $this->programCounter = $state[$index++];
+ $this->halt = $state[$index++];
+ $this->IME = $state[$index++];
+ $this->hdmaRunning = $state[$index++];
+ $this->CPUTicks = $state[$index++];
+ $this->multiplier = $state[$index++];
+ $this->memory = $this->toTypedArray($state[$index++], false, false);
+ $this->MBCRam = $this->toTypedArray($state[$index++], false, false);
+ $this->VRAM = $this->toTypedArray($state[$index++], false, false);
+ $this->currVRAMBank = $state[$index++];
+ $this->GBCMemory = $this->toTypedArray($state[$index++], false, false);
+ $this->MBC1Mode = $state[$index++];
+ $this->MBCRAMBanksEnabled = $state[$index++];
+ $this->currMBCRAMBank = $state[$index++];
+ $this->currMBCRAMBankPosition = $state[$index++];
+ $this->cGBC = $state[$index++];
+ $this->gbcRamBank = $state[$index++];
+ $this->gbcRamBankPosition = $state[$index++];
+ $this->ROMBank1offs = $state[$index++];
+ $this->currentROMBank = $state[$index++];
+ $this->cartridgeType = $state[$index++];
+ $this->name = $state[$index++];
+ $this->gameCode = $state[$index++];
+ $this->modeSTAT = $state[$index++];
+ $this->LYCMatchTriggerSTAT = $state[$index++];
+ $this->mode2TriggerSTAT = $state[$index++];
+ $this->mode1TriggerSTAT = $state[$index++];
+ $this->mode0TriggerSTAT = $state[$index++];
+ $this->LCDisOn = $state[$index++];
+ $this->gfxWindowY = $state[$index++];
+ $this->gfxWindowDisplay = $state[$index++];
+ $this->gfxSpriteShow = $state[$index++];
+ $this->gfxSpriteDouble = $state[$index++];
+ $this->gfxBackgroundY = $state[$index++];
+ $this->gfxBackgroundX = $state[$index++];
+ $this->TIMAEnabled = $state[$index++];
+ $this->DIVTicks = $state[$index++];
+ $this->LCDTicks = $state[$index++];
+ $this->timerTicks = $state[$index++];
+ $this->TACClocker = $state[$index++];
+ $this->untilEnable = $state[$index++];
+ $this->lastIteration = $state[$index++];
+ $this->cMBC1 = $state[$index++];
+ $this->cMBC2 = $state[$index++];
+ $this->cMBC3 = $state[$index++];
+ $this->cMBC5 = $state[$index++];
+ $this->cSRAM = $state[$index++];
+ $this->cMMMO1 = $state[$index++];
+ $this->cRUMBLE = $state[$index++];
+ $this->cCamera = $state[$index++];
+ $this->cTAMA5 = $state[$index++];
+ $this->cHuC3 = $state[$index++];
+ $this->cHuC1 = $state[$index++];
+ $this->drewBlank = $state[$index++];
+ $this->tileData = $state[$index++];
+ $this->frameBuffer = $this->toTypedArray($state[$index++], true, false);
+ $this->tileCount = $state[$index++];
+ $this->colorCount = $state[$index++];
+ $this->gbPalette = $state[$index++];
+ $this->gbcRawPalette = $state[$index++];
+ $this->gbcPalette = $state[$index++];
+ $this->transparentCutoff = $state[$index++];
+ $this->bgEnabled = $state[$index++];
+ $this->spritePriorityEnabled = $state[$index++];
+ $this->tileReadState = $this->toTypedArray($state[$index++], false, false);
+ $this->windowSourceLine = $state[$index++];
+ $this->channel1adjustedFrequencyPrep = $state[$index++];
+ $this->channel1duty = $state[$index++];
+ $this->channel1lastSampleLookup = $state[$index++];
+ $this->channel1adjustedDuty = $state[$index++];
+ $this->channel1totalLength = $state[$index++];
+ $this->channel1envelopeVolume = $state[$index++];
+ $this->channel1currentVolume = $state[$index++];
+ $this->channel1envelopeType = $state[$index++];
+ $this->channel1envelopeSweeps = $state[$index++];
+ $this->channel1consecutive = $state[$index++];
+ $this->channel1frequency = $state[$index++];
+ $this->channel1volumeEnvTime = $state[$index++];
+ $this->channel1lastTotalLength = $state[$index++];
+ $this->channel1timeSweep = $state[$index++];
+ $this->channel1lastTimeSweep = $state[$index++];
+ $this->channel1numSweep = $state[$index++];
+ $this->channel1frequencySweepDivider = $state[$index++];
+ $this->channel1decreaseSweep = $state[$index++];
+ $this->channel2adjustedFrequencyPrep = $state[$index++];
+ $this->channel2duty = $state[$index++];
+ $this->channel2lastSampleLookup = $state[$index++];
+ $this->channel2adjustedDuty = $state[$index++];
+ $this->channel2totalLength = $state[$index++];
+ $this->channel2envelopeVolume = $state[$index++];
+ $this->channel2currentVolume = $state[$index++];
+ $this->channel2envelopeType = $state[$index++];
+ $this->channel2envelopeSweeps = $state[$index++];
+ $this->channel2consecutive = $state[$index++];
+ $this->channel2frequency = $state[$index++];
+ $this->channel2volumeEnvTime = $state[$index++];
+ $this->channel2lastTotalLength = $state[$index++];
+ $this->channel3canPlay = $state[$index++];
+ $this->channel3totalLength = $state[$index++];
+ $this->channel3lastTotalLength = $state[$index++];
+ $this->channel3patternType = $state[$index++];
+ $this->channel3frequency = $state[$index++];
+ $this->channel3consecutive = $state[$index++];
+ $this->channel3PCM = $state[$index++];
+ $this->channel3adjustedFrequencyPrep = $state[$index++];
+ $this->channel4adjustedFrequencyPrep = $state[$index++];
+ $this->channel4lastSampleLookup = $state[$index++];
+ $this->channel4totalLength = $state[$index++];
+ $this->channel4envelopeVolume = $state[$index++];
+ $this->channel4currentVolume = $state[$index++];
+ $this->channel4envelopeType = $state[$index++];
+ $this->channel4envelopeSweeps = $state[$index++];
+ $this->channel4consecutive = $state[$index++];
+ $this->channel4volumeEnvTime = $state[$index++];
+ $this->channel4lastTotalLength = $state[$index++];
+ $this->soundMasterEnabled = $state[$index++];
+ $this->VinLeftChannelEnabled = $state[$index++];
+ $this->VinRightChannelEnabled = $state[$index++];
+ $this->VinLeftChannelMasterVolume = $state[$index++];
+ $this->VinRightChannelMasterVolume = $state[$index++];
+ $this->vinLeft = $state[$index++];
+ $this->vinRight = $state[$index++];
+ $this->leftChannel = $state[$index++];
+ $this->rightChannel = $state[$index++];
+ $this->actualScanLine = $state[$index++];
+ $this->RTCisLatched = $state[$index++];
+ $this->latchedSeconds = $state[$index++];
+ $this->latchedMinutes = $state[$index++];
+ $this->latchedHours = $state[$index++];
+ $this->latchedLDays = $state[$index++];
+ $this->latchedHDays = $state[$index++];
+ $this->RTCSeconds = $state[$index++];
+ $this->RTCMinutes = $state[$index++];
+ $this->RTCHours = $state[$index++];
+ $this->RTCDays = $state[$index++];
+ $this->RTCDayOverFlow = $state[$index++];
+ $this->RTCHALT = $state[$index++];
+ $this->gbColorizedPalette = $state[$index++];
+ $this->usedBootROM = $state[$index++];
+ $this->skipPCIncrement = $state[$index++];
+ $this->STATTracker = $state[$index++];
+ $this->gbcRamBankPositionECHO = $state[$index++];
+ $this->numRAMBanks = $state[$index];
+ $this->tileCountInvalidator = $this->tileCount * 4;
+ $this->fromSaveState = true;
+ $this->checkPaletteType();
+ $this->convertAuxilliary();
+ $this->initializeLCDController();
+ $this->memoryReadJumpCompile();
+ $this->memoryWriteJumpCompile();
+ $this->initLCD();
+ $this->initSound();
+ $this->drawToCanvas();
+ }
+
+ public function start()
+ {
+ Settings::$settings[4] = 0; //Reset the frame skip setting.
+ $this->initializeLCDController(); //Compile the LCD controller functions.
+ $this->initMemory(); //Write the startup memory.
+ $this->ROMLoad(); //Load the ROM into memory and get cartridge information from it.
+ $this->initLCD(); //Initializae the graphics.
+ $this->initSound(); //Sound object initialization.
+ $this->run(); //Start the emulation.
+ }
+
+ public function convertAuxilliary()
+ {
+ try {
+ // @TODO - Uint16
+ // Its OK, tested
+ $this->DAATable = $this->DAATable;
+ $this->TICKTable = $this->TICKTable;
+ $this->SecondaryTICKTable = $this->SecondaryTICKTable;
+ } catch (\Exception $e) {
+ echo 'Could not convert the auxilliary arrays to typed arrays' . PHP_EOL;
+ }
+ }
+
+ public function initMemory() {
+ //Initialize the RAM:
+ $this->memory = $this->getTypedArray(0x10000, 0, 'uint8');
+ $this->frameBuffer = $this->getTypedArray(23040, 0x00FFFFFF, 'int32');
+ $this->gbPalette = $this->ArrayPad(12, 0); //32-bit signed
+ $this->gbColorizedPalette = $this->ArrayPad(12, 0); //32-bit signed
+ $this->gbcRawPalette = $this->ArrayPad(0x80, -1000); //32-bit signed
+ $this->gbcPalette = [0x40]; //32-bit signed
+ $this->convertAuxilliary();
+ //Initialize the GBC Palette:
+ $index = 0x3F;
+
+ while ($index >= 0) {
+ $this->gbcPalette[$index] = ($index < 0x20) ? -1 : 0;
+ $index--;
+ }
+ }
+
+ public function initSkipBootstrap() {
+ //Start as an unset device:
+ echo 'Starting without the GBC boot ROM' . PHP_EOL;
+
+ $this->programCounter = 0x100;
+ $this->stackPointer = 0xFFFE;
+ $this->IME = true;
+ $this->LCDTicks = 15;
+ $this->DIVTicks = 14;
+ $this->registerA = ($this->cGBC) ? 0x11 : 0x1;
+ $this->registerB = 0;
+ $this->registerC = 0x13;
+ $this->registerD = 0;
+ $this->registerE = 0xD8;
+ $this->FZero = true;
+ $this->FSubtract = false;
+ $this->FHalfCarry = true;
+ $this->FCarry = true;
+ $this->registersHL = 0x014D;
+ $this->leftChannel = [true, true, true, false];
+ $this->rightChannel = [true, true, true, false];
+
+ //Fill in the boot ROM set register values
+ //Default values to the GB boot ROM values, then fill in the GBC boot ROM values after ROM loading
+ $index = 0xFF;
+
+ while ($index >= 0) {
+ if ($index >= 0x30 && $index < 0x40) {
+ $this->memoryWrite(0xFF00 + $index, $this->ffxxDump[$index]);
+ } else {
+ switch ($index) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x07:
+ case 0x0F:
+ case 0x40:
+ case 0xFF:
+ $this->memoryWrite(0xFF00 + $index, $this->ffxxDump[$index]);
+ break;
+ default:
+ $this->memory[0xFF00 + $index] = $this->ffxxDump[$index];
+ }
+ }
+ $index--;
+ }
+ }
+
+ public function initBootstrap() {
+ //Start as an unset device:
+ echo 'Starting the GBC boot ROM.' . PHP_EOL;
+
+ $this->programCounter = 0;
+ $this->stackPointer = 0;
+ $this->IME = false;
+ $this->LCDTicks = 0;
+ $this->DIVTicks = 0;
+ $this->registerA = 0;
+ $this->registerB = 0;
+ $this->registerC = 0;
+ $this->registerD = 0;
+ $this->registerE = 0;
+ $this->FZero = $this->FSubtract = $this->FHalfCarry = $this->FCarry = false;
+ $this->registersHL = 0;
+ $this->leftChannel = $this->ArrayPad(4, false);
+ $this->rightChannel = $this->ArrayPad(4, false);
+ $this->channel2frequency = $this->channel1frequency = 0;
+ $this->channel2volumeEnvTime = $this->channel1volumeEnvTime = 0;
+ $this->channel2consecutive = $this->channel1consecutive = true;
+ $this->memory[0xFF00] = 0xF; //Set the joypad state.
+ }
+
+ public function ROMLoad() {
+ //Load the first two ROM banks (0x0000 - 0x7FFF) into regular gameboy memory:
+ $this->ROM = $this->getTypedArray(strlen($this->ROMImage), 0, "uint8");
+
+ $this->usedBootROM = Settings::$settings[16];
+
+ for ($romIndex = 0; $romIndex < strlen($this->ROMImage); $romIndex++) {
+
+ $this->ROM[$romIndex] = (ord($this->ROMImage[$romIndex]) & 0xFF);
+ if ($romIndex < 0x8000) {
+ if (!$this->usedBootROM || $romIndex >= 0x900 || ($romIndex >= 0x100 && $romIndex < 0x200)) {
+ $this->memory[$romIndex] = $this->ROM[$romIndex]; //Load in the game ROM.
+ }
+ else {
+ $this->memory[$romIndex] = $this->GBCBOOTROM[$romIndex]; //Load in the GameBoy Color BOOT ROM.
+ }
+ }
+ }
+ // ROM name
+ for ($index = 0x134; $index < 0x13F; $index++) {
+ if (ord($this->ROMImage[$index]) > 0) {
+ $this->name .= $this->ROMImage[$index];
+ }
+ }
+
+ // ROM game code (for newer games)
+ for ($index = 0x13F; $index < 0x143; $index++) {
+ if (ord($this->ROMImage[$index]) > 0) {
+ $this->gameCode .= $this->ROMImage[$index];
+ }
+ }
+
+ echo "Game Title: " . $this->name . "[" . $this->gameCode . "][" . $this->ROMImage[0x143] . "]" . PHP_EOL;
+
+ echo "Game Code: " . $this->gameCode . PHP_EOL;
+
+ // Cartridge type
+ $this->cartridgeType = $this->ROM[0x147];
+ echo "Cartridge type #" . $this->cartridgeType . PHP_EOL;
+
+ //Map out ROM cartridge sub-types.
+ $MBCType = "";
+
+ switch ($this->cartridgeType) {
+ case 0x00:
+ //ROM w/o bank switching
+ if (!Settings::$settings[9]) {
+ $MBCType = "ROM";
+ break;
+ }
+ case 0x01:
+ $this->cMBC1 = true;
+ $MBCType = "MBC1";
+ break;
+ case 0x02:
+ $this->cMBC1 = true;
+ $this->cSRAM = true;
+ $MBCType = "MBC1 + SRAM";
+ break;
+ case 0x03:
+ $this->cMBC1 = true;
+ $this->cSRAM = true;
+ $this->cBATT = true;
+ $MBCType = "MBC1 + SRAM + BATT";
+ break;
+ case 0x05:
+ $this->cMBC2 = true;
+ $MBCType = "MBC2";
+ break;
+ case 0x06:
+ $this->cMBC2 = true;
+ $this->cBATT = true;
+ $MBCType = "MBC2 + BATT";
+ break;
+ case 0x08:
+ $this->cSRAM = true;
+ $MBCType = "ROM + SRAM";
+ break;
+ case 0x09:
+ $this->cSRAM = true;
+ $this->cBATT = true;
+ $MBCType = "ROM + SRAM + BATT";
+ break;
+ case 0x0B:
+ $this->cMMMO1 = true;
+ $MBCType = "MMMO1";
+ break;
+ case 0x0C:
+ $this->cMMMO1 = true;
+ $this->cSRAM = true;
+ $MBCType = "MMMO1 + SRAM";
+ break;
+ case 0x0D:
+ $this->cMMMO1 = true;
+ $this->cSRAM = true;
+ $this->cBATT = true;
+ $MBCType = "MMMO1 + SRAM + BATT";
+ break;
+ case 0x0F:
+ $this->cMBC3 = true;
+ $this->cTIMER = true;
+ $this->cBATT = true;
+ $MBCType = "MBC3 + TIMER + BATT";
+ break;
+ case 0x10:
+ $this->cMBC3 = true;
+ $this->cTIMER = true;
+ $this->cBATT = true;
+ $this->cSRAM = true;
+ $MBCType = "MBC3 + TIMER + BATT + SRAM";
+ break;
+ case 0x11:
+ $this->cMBC3 = true;
+ $MBCType = "MBC3";
+ break;
+ case 0x12:
+ $this->cMBC3 = true;
+ $this->cSRAM = true;
+ $MBCType = "MBC3 + SRAM";
+ break;
+ case 0x13:
+ $this->cMBC3 = true;
+ $this->cSRAM = true;
+ $this->cBATT = true;
+ $MBCType = "MBC3 + SRAM + BATT";
+ break;
+ case 0x19:
+ $this->cMBC5 = true;
+ $MBCType = "MBC5";
+ break;
+ case 0x1A:
+ $this->cMBC5 = true;
+ $this->cSRAM = true;
+ $MBCType = "MBC5 + SRAM";
+ break;
+ case 0x1B:
+ $this->cMBC5 = true;
+ $this->cSRAM = true;
+ $this->cBATT = true;
+ $MBCType = "MBC5 + SRAM + BATT";
+ break;
+ case 0x1C:
+ $this->cRUMBLE = true;
+ $MBCType = "RUMBLE";
+ break;
+ case 0x1D:
+ $this->cRUMBLE = true;
+ $this->cSRAM = true;
+ $MBCType = "RUMBLE + SRAM";
+ break;
+ case 0x1E:
+ $this->cRUMBLE = true;
+ $this->cSRAM = true;
+ $this->cBATT = true;
+ $MBCType = "RUMBLE + SRAM + BATT";
+ break;
+ case 0x1F:
+ $this->cCamera = true;
+ $MBCType = "GameBoy Camera";
+ break;
+ case 0xFD:
+ $this->cTAMA5 = true;
+ $MBCType = "TAMA5";
+ break;
+ case 0xFE:
+ $this->cHuC3 = true;
+ $MBCType = "HuC3";
+ break;
+ case 0xFF:
+ $this->cHuC1 = true;
+ $MBCType = "HuC1";
+ break;
+ default:
+ $MBCType = "Unknown";
+ echo "Cartridge type is unknown." . PHP_EOL;
+
+ // @TODO
+ //pause();
+ }
+
+ echo "Cartridge Type: " . $MBCType . PHP_EOL;
+
+ // ROM and RAM banks
+ $this->numROMBanks = $this->ROMBanks[$this->ROM[0x148]];
+
+ echo $this->numROMBanks . " ROM banks." . PHP_EOL;
+
+ switch ($this->RAMBanks[$this->ROM[0x149]]) {
+ case 0:
+ echo "No RAM banking requested for allocation or MBC is of type 2." . PHP_EOL;
+ break;
+ case 2:
+ echo "1 RAM bank requested for allocation." . PHP_EOL;
+ break;
+ case 3:
+ echo "4 RAM banks requested for allocation." . PHP_EOL;
+ break;
+ case 4:
+ echo "16 RAM banks requested for allocation." . PHP_EOL;
+ break;
+ default:
+ echo "RAM bank amount requested is unknown, will use maximum allowed by specified MBC type." . PHP_EOL;
+ }
+
+ //Check the GB/GBC mode byte:
+ if (!$this->usedBootROM) {
+ switch ($this->ROM[0x143]) {
+ case 0x00: //Only GB mode
+ $this->cGBC = false;
+ echo "Only GB mode detected." . PHP_EOL;
+ break;
+ case 0x80: //Both GB + GBC modes
+ $this->cGBC = ! Settings::$settings[2];
+ echo "GB and GBC mode detected." . PHP_EOL;
+ break;
+ case 0xC0: //Only GBC mode
+ $this->cGBC = true;
+ echo "Only GBC mode detected." . PHP_EOL;
+ break;
+ default:
+ $this->cGBC = false;
+ echo "Unknown GameBoy game type code #" . $this->ROM[0x143] . ", defaulting to GB mode (Old games don't have a type code)." . PHP_EOL;
+ }
+
+ $this->inBootstrap = false;
+ $this->setupRAM(); //CPU/(V)RAM initialization.
+ $this->initSkipBootstrap();
+ }
+ else {
+ $this->cGBC = true; //Allow the GBC boot ROM to run in GBC mode...
+ $this->setupRAM(); //CPU/(V)RAM initialization.
+ $this->initBootstrap();
+ }
+ $this->checkPaletteType();
+ //License Code Lookup:
+ $cOldLicense = $this->ROM[0x14B];
+ $cNewLicense = ($this->ROM[0x144] & 0xFF00) | ($this->ROM[0x145] & 0xFF);
+ if ($cOldLicense != 0x33) {
+ //Old Style License Header
+ echo "Old style license code: " . $cOldLicense . PHP_EOL;
+ }
+ else {
+ //New Style License Header
+ echo "New style license code: " . $cNewLicense . PHP_EOL;
+ }
+ }
+
+ public function disableBootROM() {
+ //Remove any traces of the boot ROM from ROM memory.
+ for ($index = 0; $index < 0x900; $index++) {
+ if ($index < 0x100 || $index >= 0x200) { //Skip the already loaded in ROM header.
+ $this->memory[$index] = $this->ROM[$index]; //Replace the GameBoy Color boot ROM with the game ROM.
+ }
+ }
+ $this->checkPaletteType();
+
+ if (!$this->cGBC) {
+ //Clean up the post-boot (GB mode only) state:
+ echo "Stepping down from GBC mode." . PHP_EOL;
+ $this->tileCount /= 2;
+ $this->tileCountInvalidator = $this->tileCount * 4;
+ if (!Settings::$settings[17]) {
+ $this->transparentCutoff = 4;
+ }
+ $this->colorCount = 12;
+
+ // @TODO
+ // $this->tileData.length = $this->tileCount * $this->colorCount;
+
+ unset($this->VRAM);
+ unset($this->GBCMemory);
+ //Possible Extra: shorten some gfx arrays to the length that we need (Remove the unused indices)
+ }
+
+ $this->memoryReadJumpCompile();
+ $this->memoryWriteJumpCompile();
+ }
+
+ public function setupRAM() {
+ //Setup the auxilliary/switchable RAM to their maximum possible size (Bad headers can lie).
+ if ($this->cMBC2) {
+ $this->numRAMBanks = 1 / 16;
+ }
+ else if ($this->cMBC1 || $this->cRUMBLE || $this->cMBC3 || $this->cHuC3) {
+ $this->numRAMBanks = 4;
+ }
+ else if ($this->cMBC5) {
+ $this->numRAMBanks = 16;
+ }
+ else if ($this->cSRAM) {
+ $this->numRAMBanks = 1;
+ }
+ if ($this->numRAMBanks > 0) {
+ if (!$this->MBCRAMUtilized()) {
+ //For ROM and unknown MBC cartridges using the external RAM:
+ $this->MBCRAMBanksEnabled = true;
+ }
+ //Switched RAM Used
+ $this->MBCRam = $this->getTypedArray($this->numRAMBanks * 0x2000, 0, "uint8");
+ }
+ echo "Actual bytes of MBC RAM allocated: " . ($this->numRAMBanks * 0x2000) . PHP_EOL;
+ //Setup the RAM for GBC mode.
+ if ($this->cGBC) {
+ $this->VRAM = $this->getTypedArray(0x2000, 0, "uint8");
+ $this->GBCMemory = $this->getTypedArray(0x7000, 0, "uint8");
+ $this->tileCount *= 2;
+ $this->tileCountInvalidator = $this->tileCount * 4;
+ $this->colorCount = 64;
+ $this->transparentCutoff = 32;
+ }
+ $this->tileData = $this->ArrayPad($this->tileCount * $this->colorCount, null);
+ $this->tileReadState = $this->getTypedArray($this->tileCount, 0, "uint8");
+ $this->memoryReadJumpCompile();
+ $this->memoryWriteJumpCompile();
+ }
+
+ public function MBCRAMUtilized() {
+ return $this->cMBC1 || $this->cMBC2 || $this->cMBC3 || $this->cMBC5 || $this->cRUMBLE;
+ }
+
+ public function initLCD() {
+ $this->scaledFrameBuffer = $this->getTypedArray($this->pixelCount, 0, "int32"); //Used for software side scaling...
+ $this->transparentCutoff = (Settings::$settings[17] || $this->cGBC) ? 32 : 4;
+ if (count($this->weaveLookup) == 0) {
+ //Setup the image decoding lookup table:
+ $this->weaveLookup = $this->getTypedArray(256, 0, "uint16");
+ for ($i_ = 0x1; $i_ <= 0xFF; $i_++) {
+ for ($d_ = 0; $d_ < 0x8; $d_++) {
+ $this->weaveLookup[$i_] += (($i_ >> $d_) & 1) << ($d_ * 2);
+ }
+ }
+ }
+ try {
+ if (Settings::$settings[5]) {
+ //Nasty since we are throwing on purpose to force a try/catch fallback
+ // throw(new Error(""));
+ throw new \Exception("");
+ }
+
+ // @TODO
+ //Create a white screen
+ // $this->drawContext = $this->canvas.getContext("2d");
+ // $this->drawContext->fillStyle = "rgb(255, 255, 255)";
+ // $this->drawContext->fillRect(0, 0, $this->width, $this->height);
+
+ $this->drawContext = new DrawContext();
+
+ // NEW
+ $this->width = 160;
+ $this->height = 144;
+
+ //Get a CanvasPixelArray buffer:
+ try {
+ //$this->canvasBuffer = $this->drawContext->createImageData($this->width, $this->height);
+ $this->canvasBuffer = new \stdClass();
+ $this->canvasBuffer->data = array_fill(0, 4 * $this->width * $this->height, 255);
+ }
+ catch (\Exception $error) {
+ echo "Falling back to the getImageData initialization" . PHP_EOL;
+
+ $this->canvasBuffer = $this->drawContext->getImageData(0, 0, $this->width, $this->height);
+ }
+
+ $index = $this->pixelCount;
+ $index2 = $this->rgbCount;
+
+ while ($index > 0) {
+ $this->frameBuffer[--$index] = 0x00FFFFFF;
+ $this->canvasBuffer->data[$index2 -= 4] = 0xFF;
+ $this->canvasBuffer->data[$index2 + 1] = 0xFF;
+ $this->canvasBuffer->data[$index2 + 2] = 0xFF;
+ $this->canvasBuffer->data[$index2 + 3] = 0xFF;
+ }
+
+ $this->drawContext->putImageData($this->canvasBuffer, 0, 0); //Throws any browser that won't support this later on.
+ // $this->canvasAlt->style->visibility = "hidden"; //Make sure, if restarted, that the fallback images aren't going cover the canvas.
+ // $this->canvas->style->visibility = "visible";
+ $this->canvasFallbackHappened = false;
+ } catch (\Exception $error) {
+ //Falling back to an experimental data URI BMP file canvas alternative:
+ echo "Falling back to BMP imaging as a canvas alternative." . PHP_EOL;
+
+ $this->width = 160;
+ $this->height = 144;
+ $this->canvasFallbackHappened = true;
+ $this->drawContext = new BMPCanvas($this->canvasAlt, 160, 144, Settings::$settings[6][0], Settings::$settings[6][1]);
+ $this->canvasBuffer = new Object();
+
+ $index = 23040;
+ while ($index > 0) {
+ $this->frameBuffer[--$index] = 0x00FFFFFF;
+ }
+ $this->canvasBuffer->data = $this->ArrayPad(92160, 0xFF);
+ $this->drawContext->putImageData($this->canvasBuffer, 0, 0);
+ //Make visible only after the images have been initialized.
+ $this->canvasAlt->style->visibility = "visible";
+ $this->canvas->style->visibility = "hidden"; //Speedier layout in some browsers.
+ }
+ }
+
+ public function JoyPadEvent($key, $down)
+ {
+ if ($down) {
+ $this->JoyPad &= 0xFF ^ (1 << $key);
+ /*if (!$this->cGBC) {
+ $this->memory[0xFF0F] |= 0x10; //A real GBC doesn't set this!
+ }*/
+ } else {
+ $this->JoyPad |= (1 << $key);
+ }
+ $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));
+ }
+
+ public function initSound() {
+ // Not implemented in PHP
+ return;
+ }
+
+ public function initAudioBuffer() {
+ // Not implemented in PHP
+ return;
+ }
+
+ public function playAudio() {
+ // Not implemented in PHP
+ return;
+ }
+
+ public function audioUpdate() {
+ // Not implemented in PHP
+ return;
+ }
+
+ public function initializeStartState() {
+ // Not implemented in PHP
+ return;
+ }
+
+ public function generateAudio() {
+ // Not implemented in PHP
+ return;
+ }
+
+ public function channel1Compute() {
+ // Not implemented in PHP
+ return;
+ }
+
+ public function channel2Compute() {
+ // Not implemented in PHP
+ return;
+ }
+
+ public function channel3Compute() {
+ // Not implemented in PHP
+ return;
+ }
+
+ public function channel4Compute() {
+ // Not implemented in PHP
+ return;
+ }
+
+ public function run() {
+ //The preprocessing before the actual iteration loop:
+ try {
+ if (($this->stopEmulator & 2) == 0) {
+ if (($this->stopEmulator & 1) == 1) {
+ $this->stopEmulator = 0;
+ $this->clockUpdate(); //Frame skip and RTC code.
+ // $this->audioUpdate(); //Lookup the rollover buffer and output WAVE PCM samples if sound is on and have fallen back to it.
+ if (!$this->halt) { //If no HALT... Execute normally
+ $this->executeIteration();
+ }
+ else { //If we bailed out of a halt because the iteration ran down its timing.
+ $this->CPUTicks = 1;
+ $this->OPCODE[0x76]($this);
+ //Execute Interrupt:
+ $this->runInterrupt();
+ //Timing:
+ $this->updateCore();
+ $this->executeIteration();
+ }
+ }
+ else { //We can only get here if there was an internal error, but the loop was restarted.
+ echo "Iterator restarted a faulted core." . PHP_EOL;
+ pause();
+ }
+ }
+ } catch (\Exception $error) {
+ if ($error->getMessage() != "HALT_OVERRUN") {
+ echo 'GameBoy runtime error' . PHP_EOL;
+ }
+ }
+ }
+
+ public function executeIteration() {
+ //Iterate the interpreter loop:
+ $op = 0;
+
+ while ($this->stopEmulator == 0) {
+ //Fetch the current opcode.
+ $op = $this->memoryRead($this->programCounter);
+ if (!$this->skipPCIncrement) {
+ //Increment the program counter to the next instruction:
+ $this->programCounter = ($this->programCounter + 1) & 0xFFFF;
+ }
+ $this->skipPCIncrement = false;
+ //Get how many CPU cycles the current op code counts for:
+ $this->CPUTicks = $this->TICKTable[$op];
+ //Execute the OP code instruction:
+ $this->OPCODE[$op]($this);
+ //Interrupt Arming:
+ switch ($this->untilEnable) {
+ case 1:
+ $this->IME = true;
+ case 2:
+ $this->untilEnable--;
+ }
+ //Execute Interrupt:
+ if ($this->IME) {
+ $this->runInterrupt();
+ }
+ //Timing:
+ $this->updateCore();
+ }
+ }
+
+ public function runInterrupt() {
+ $bitShift = 0;
+ $testbit = 1;
+ $interrupts = $this->memory[0xFFFF] & $this->memory[0xFF0F];
+
+ while ($bitShift < 5) {
+ //Check to see if an interrupt is enabled AND requested.
+ if (($testbit & $interrupts) == $testbit) {
+ $this->IME = false; //Reset the interrupt enabling.
+ $this->memory[0xFF0F] -= $testbit; //Reset the interrupt request.
+ //Set the stack pointer to the current program counter value:
+ $this->stackPointer = $this->unswtuw($this->stackPointer - 1);
+ $this->memoryWrite($this->stackPointer, $this->programCounter >> 8);
+ $this->stackPointer = $this->unswtuw($this->stackPointer - 1);
+ $this->memoryWrite($this->stackPointer, $this->programCounter & 0xFF);
+ //Set the program counter to the interrupt's address:
+ $this->programCounter = 0x0040 + ($bitShift * 0x08);
+ //Interrupts have a certain clock cycle length:
+ $this->CPUTicks += 5; //People say it's around 5.
+ break; //We only want the highest priority interrupt.
+ }
+
+ $testbit = 1 << ++$bitShift;
+ }
+ }
+
+ public function scanLineMode2() { // OAM in use
+ if ($this->modeSTAT != 2) {
+ if ($this->mode2TriggerSTAT) {
+ $this->memory[0xFF0F] |= 0x2;// set IF bit 1
+ }
+ $this->STATTracker = 1;
+ $this->modeSTAT = 2;
+ }
+ }
+
+ public function scanLineMode3() { // OAM in use
+ if ($this->modeSTAT != 3) {
+ if ($this->mode2TriggerSTAT && $this->STATTracker == 0) {
+ $this->memory[0xFF0F] |= 0x2;// set IF bit 1
+ }
+ $this->STATTracker = 1;
+ $this->modeSTAT = 3;
+ }
+ }
+
+ public function scanLineMode0() { // H-Blank
+ if ($this->modeSTAT != 0) {
+ if ($this->hdmaRunning && !$this->halt && $this->LCDisOn) {
+ $this->performHdma(); //H-Blank DMA
+ }
+ if ($this->mode0TriggerSTAT || ($this->mode2TriggerSTAT && $this->STATTracker == 0)) {
+ $this->memory[0xFF0F] |= 0x2; // if STAT bit 3 -> set IF bit1
+ }
+ $this->notifyScanline();
+ $this->STATTracker = 2;
+ $this->modeSTAT = 0;
+ }
+ }
+
+ public function matchLYC() { // LY - LYC Compare
+ if ($this->memory[0xFF44] == $this->memory[0xFF45]) { // If LY==LCY
+ $this->memory[0xFF41] |= 0x04; // set STAT bit 2: LY-LYC coincidence flag
+ if ($this->LYCMatchTriggerSTAT) {
+ $this->memory[0xFF0F] |= 0x2; // set IF bit 1
+ }
+ }
+ else {
+ $this->memory[0xFF41] &= 0xFB; // reset STAT bit 2 (LY!=LYC)
+ }
+ }
+
+ public function updateCore() {
+ // DIV control
+ $this->DIVTicks += $this->CPUTicks;
+ if ($this->DIVTicks >= 0x40) {
+ $this->DIVTicks -= 0x40;
+ $this->memory[0xFF04] = ($this->memory[0xFF04] + 1) & 0xFF; // inc DIV
+ }
+ //LCD Controller Ticks
+ $timedTicks = $this->CPUTicks / $this->multiplier;
+ // LCD Timing
+ $this->LCDTicks += $timedTicks; //LCD timing
+ $this->LCDCONTROL[$this->actualScanLine]($this); //Scan Line and STAT Mode Control
+ //Audio Timing
+ $this->audioTicks += $timedTicks; //Not the same as the LCD timing (Cannot be altered by display on/off changes!!!).
+ if ($this->audioTicks >= Settings::$settings[11]) { //Are we past the granularity setting?
+ $amount = $this->audioTicks * $this->samplesOut;
+ $actual = floor($amount);
+ $this->rollover += $amount - $actual;
+ if ($this->rollover >= 1) {
+ $this->rollover -= 1;
+ $actual += 1;
+ }
+ if (!$this->audioOverflow && $actual > 0) {
+ $this->generateAudio($actual);
+ }
+ //Emulator Timing (Timed against audio for optimization):
+ $this->emulatorTicks += $this->audioTicks;
+ if ($this->emulatorTicks >= Settings::$settings[13]) {
+ if (($this->stopEmulator & 1) == 0) { //Make sure we don't overdo the audio.
+ $this->playAudio(); //Output all the samples built up.
+ if ($this->drewBlank == 0) { //LCD off takes at least 2 frames.
+ $this->drawToCanvas(); //Display frame
+ }
+ }
+ $this->stopEmulator |= 1; //End current loop.
+ $this->emulatorTicks = 0;
+ }
+ $this->audioTicks = 0;
+ }
+ // Internal Timer
+ if ($this->TIMAEnabled) {
+ $this->timerTicks += $this->CPUTicks;
+ while ($this->timerTicks >= $this->TACClocker) {
+ $this->timerTicks -= $this->TACClocker;
+ if ($this->memory[0xFF05] == 0xFF) {
+ $this->memory[0xFF05] = $this->memory[0xFF06];
+ $this->memory[0xFF0F] |= 0x4; // set IF bit 2
+ }
+ else {
+ $this->memory[0xFF05]++;
+ }
+ }
+ }
+ }
+
+ public function initializeLCDController() {
+ //Display on hanlding:
+ $line = 0;
+
+ while ($line < 154) {
+ if ($line < 143) {
+ //We're on a normal scan line:
+ $this->LINECONTROL[$line] = function ($parentObj) {
+ if ($parentObj->LCDTicks < 20) {
+ $parentObj->scanLineMode2(); // mode2: 80 cycles
+ }
+ else if ($parentObj->LCDTicks < 63) {
+ $parentObj->scanLineMode3(); // mode3: 172 cycles
+ }
+ else if ($parentObj->LCDTicks < 114) {
+ $parentObj->scanLineMode0(); // mode0: 204 cycles
+ }
+ else {
+ //We're on a new scan line:
+ $parentObj->LCDTicks -= 114;
+ $parentObj->actualScanLine = ++$parentObj->memory[0xFF44];
+ $parentObj->matchLYC();
+ if ($parentObj->STATTracker != 2) {
+ if ($parentObj->hdmaRunning && !$parentObj->halt && $parentObj->LCDisOn) {
+ $parentObj->performHdma(); //H-Blank DMA
+ }
+ if ($parentObj->mode0TriggerSTAT) {
+ $parentObj->memory[0xFF0F] |= 0x2;// set IF bit 1
+ }
+ }
+ $parentObj->STATTracker = 0;
+ $parentObj->scanLineMode2(); // mode2: 80 cycles
+ if ($parentObj->LCDTicks >= 114) {
+ //We need to skip 1 or more scan lines:
+ $parentObj->notifyScanline();
+ $parentObj->LCDCONTROL[$parentObj->actualScanLine]($parentObj); //Scan Line and STAT Mode Control
+ }
+ }
+ };
+ } else if ($line == 143) {
+ //We're on the last visible scan line of the LCD screen:
+ $this->LINECONTROL[143] = function ($parentObj) {
+ if ($parentObj->LCDTicks < 20) {
+ $parentObj->scanLineMode2(); // mode2: 80 cycles
+ }
+ else if ($parentObj->LCDTicks < 63) {
+ $parentObj->scanLineMode3(); // mode3: 172 cycles
+ }
+ else if ($parentObj->LCDTicks < 114) {
+ $parentObj->scanLineMode0(); // mode0: 204 cycles
+ }
+ else {
+ //Starting V-Blank:
+ //Just finished the last visible scan line:
+ $parentObj->LCDTicks -= 114;
+ $parentObj->actualScanLine = ++$parentObj->memory[0xFF44];
+ $parentObj->matchLYC();
+ if ($parentObj->mode1TriggerSTAT) {
+ $parentObj->memory[0xFF0F] |= 0x2;// set IF bit 1
+ }
+ if ($parentObj->STATTracker != 2) {
+ if ($parentObj->hdmaRunning && !$parentObj->halt && $parentObj->LCDisOn) {
+ $parentObj->performHdma(); //H-Blank DMA
+ }
+ if ($parentObj->mode0TriggerSTAT) {
+ $parentObj->memory[0xFF0F] |= 0x2;// set IF bit 1
+ }
+ }
+ $parentObj->STATTracker = 0;
+ $parentObj->modeSTAT = 1;
+ $parentObj->memory[0xFF0F] |= 0x1; // set IF flag 0
+ if ($parentObj->drewBlank > 0) { //LCD off takes at least 2 frames.
+ $parentObj->drewBlank--;
+ }
+ if ($parentObj->LCDTicks >= 114) {
+ //We need to skip 1 or more scan lines:
+ $parentObj->LCDCONTROL[$parentObj->actualScanLine]($parentObj); //Scan Line and STAT Mode Control
+ }
+ }
+ };
+ } else if ($line < 153) {
+ //In VBlank
+ $this->LINECONTROL[$line] = function ($parentObj) {
+ if ($parentObj->LCDTicks >= 114) {
+ //We're on a new scan line:
+ $parentObj->LCDTicks -= 114;
+ $parentObj->actualScanLine = ++$parentObj->memory[0xFF44];
+ $parentObj->matchLYC();
+ if ($parentObj->LCDTicks >= 114) {
+ //We need to skip 1 or more scan lines:
+ $parentObj->LCDCONTROL[$parentObj->actualScanLine]($parentObj); //Scan Line and STAT Mode Control
+ }
+ }
+ };
+ }
+ else {
+ //VBlank Ending (We're on the last actual scan line)
+ $this->LINECONTROL[153] = function ($parentObj) {
+ if ($parentObj->memory[0xFF44] == 153) {
+ $parentObj->memory[0xFF44] = 0; //LY register resets to 0 early.
+ $parentObj->matchLYC(); //LY==LYC Test is early here (Fixes specific one-line glitches (example: Kirby2 intro)).
+ }
+ if ($parentObj->LCDTicks >= 114) {
+ //We reset back to the beginning:
+ $parentObj->LCDTicks -= 114;
+ $parentObj->actualScanLine = 0;
+ $parentObj->scanLineMode2(); // mode2: 80 cycles
+ if ($parentObj->LCDTicks >= 114) {
+ //We need to skip 1 or more scan lines:
+ $parentObj->LCDCONTROL[$parentObj->actualScanLine]($parentObj); //Scan Line and STAT Mode Control
+ }
+ }
+ };
+ }
+ $line++;
+ }
+ $this->LCDCONTROL = ($this->LCDisOn) ? $this->LINECONTROL : $this->DISPLAYOFFCONTROL;
+ }
+
+ public function DisplayShowOff() {
+ if ($this->drewBlank == 0) {
+ //Draw a blank screen:
+ try {
+ // @TODO
+ // $this->drawContext->fillStyle = "white";
+ $this->drawContext->fillRect(0, 0, $this->width, $this->height);
+ } catch (\Exception $e) {
+ //cout("Could not use fillStyle / fillRect.", 2);
+ $index = $this->pixelCount;
+
+ while ($index > 0) {
+ $this->canvasBuffer->data[--$index] = 0xFF;
+ }
+
+ $this->drawContext->putImageData($this->canvasBuffer, 0, 0);
+ }
+ $this->drewBlank = 2;
+ }
+ }
+
+ public function performHdma() {
+ $this->CPUTicks += 1 + (8 * $this->multiplier);
+
+ $dmaSrc = ($this->memory[0xFF51] << 8) + $this->memory[0xFF52];
+ $dmaDstRelative = ($this->memory[0xFF53] << 8) + $this->memory[0xFF54];
+ $dmaDstFinal = $dmaDstRelative + 0x10;
+ $tileRelative = $this->tileData->length - $this->tileCount;
+
+ if ($this->currVRAMBank == 1) {
+ while ($dmaDstRelative < $dmaDstFinal) {
+ if ($dmaDstRelative < 0x1800) { // Bkg Tile data area
+ $tileIndex = ($dmaDstRelative >> 4) + 384;
+ if ($this->tileReadState[$tileIndex] == 1) {
+ $r = $tileRelative + $tileIndex;
+ do {
+ $this->tileData[$r] = null;
+ $r -= $this->tileCount;
+ } while ($r >= 0);
+ $this->tileReadState[$tileIndex] = 0;
+ }
+ }
+ $this->VRAM[$dmaDstRelative++] = $this->memoryRead($dmaSrc++);
+ }
+ } else {
+ while ($dmaDstRelative < $dmaDstFinal) {
+ if ($dmaDstRelative < 0x1800) { // Bkg Tile data area
+ $tileIndex = $dmaDstRelative >> 4;
+ if ($this->tileReadState[$tileIndex] == 1) {
+ $r = $tileRelative + $tileIndex;
+
+ do {
+ $this->tileData[$r] = null;
+ $r -= $this->tileCount;
+ } while ($r >= 0);
+
+ $this->tileReadState[$tileIndex] = 0;
+ }
+ }
+ $this->memory[0x8000 + $dmaDstRelative++] = $this->memoryRead($dmaSrc++);
+ }
+ }
+
+ $this->memory[0xFF51] = (($dmaSrc & 0xFF00) >> 8);
+ $this->memory[0xFF52] = ($dmaSrc & 0x00F0);
+ $this->memory[0xFF53] = (($dmaDstFinal & 0x1F00) >> 8);
+ $this->memory[0xFF54] = ($dmaDstFinal & 0x00F0);
+ if ($this->memory[0xFF55] == 0) {
+ $this->hdmaRunning = false;
+ $this->memory[0xFF55] = 0xFF; //Transfer completed ("Hidden last step," since some ROMs don't imply this, but most do).
+ }
+ else {
+ $this->memory[0xFF55]--;
+ }
+ }
+
+ public function clockUpdate() {
+ //We're tying in the same timer for RTC and frame skipping, since we can and this reduces load.
+ if (Settings::$settings[7] || $this->cTIMER) {
+ $timeElapsed = microtime(true) - $this->lastIteration; //Get the numnber of milliseconds since this last executed.
+ if ($this->cTIMER && !$this->RTCHALT) {
+ //Update the MBC3 RTC:
+ $this->RTCSeconds += $timeElapsed / 1000;
+ while ($this->RTCSeconds >= 60) { //System can stutter, so the seconds difference can get large, thus the "while".
+ $this->RTCSeconds -= 60;
+ $this->RTCMinutes++;
+ if ($this->RTCMinutes >= 60) {
+ $this->RTCMinutes -= 60;
+ $this->RTCHours++;
+ if ($this->RTCHours >= 24) {
+ $this->RTCHours -= 24;
+ $this->RTCDays++;
+ if ($this->RTCDays >= 512) {
+ $this->RTCDays -= 512;
+ $this->RTCDayOverFlow = true;
+ }
+ }
+ }
+ }
+ }
+ if (Settings::$settings[7]) {
+ //Auto Frame Skip:
+ if ($timeElapsed > Settings::$settings[20]) {
+ //Did not finish in time...
+ if (Settings::$settings[4] < Settings::$settings[8]) {
+ Settings::$settings[4]++;
+ }
+ }
+ else if (Settings::$settings[4] > 0) {
+ //We finished on time, decrease frame skipping (throttle to somewhere just below full speed)...
+ Settings::$settings[4]--;
+ }
+ }
+ $this->lastIteration = microtime();
+ }
+ }
+
+ public function drawToCanvas() {
+ //Draw the frame buffer to the canvas:
+ if (Settings::$settings[4] == 0 || $this->frameCount > 0) {
+ //Copy and convert the framebuffer data to the CanvasPixelArray format.
+ $canvasData = $this->canvasBuffer->data;
+ $frameBuffer = (Settings::$settings[21] && $this->pixelCount > 0 && $this->width != 160 && $this->height != 144) ? $this->resizeFrameBuffer() : $this->frameBuffer;
+ $bufferIndex = $this->pixelCount;
+ $canvasIndex = $this->rgbCount;
+
+ while ($canvasIndex > 3) {
+ $canvasData[$canvasIndex -= 4] = ($frameBuffer[--$bufferIndex] >> 16) & 0xFF; //Red
+ $canvasData[$canvasIndex + 1] = ($frameBuffer[$bufferIndex] >> 8) & 0xFF; //Green
+ $canvasData[$canvasIndex + 2] = $frameBuffer[$bufferIndex] & 0xFF; //Blue
+ }
+
+ $this->canvasBuffer->data = $canvasData;
+
+ // @TODO
+ //Draw out the CanvasPixelArray data:
+ $this->drawContext->putImageData($this->canvasBuffer, 0, 0);
+
+ if (Settings::$settings[4] > 0) {
+ //Increment the frameskip counter:
+ $this->frameCount -= Settings::$settings[4];
+ }
+ }
+ else {
+ //Reset the frameskip counter:
+ $this->frameCount += Settings::$settings[12];
+ }
+ }
+
+ public function resizeFrameBuffer() {
+ //Attempt to resize the canvas in software instead of in CSS:
+ $column = 0;
+ $rowOffset = 0;
+ for ($row = 0; $row < $this->height; $row++) {
+ $rowOffset = floor($row * $this->heightRatio) * 160;
+ for ($column = 0; $column < $this->width; $column++) {
+ $this->scaledFrameBuffer[($row * $this->width) + $column] = $this->frameBuffer[$rowOffset + floor($column * $this->widthRatio)];
+ }
+ }
+ return $this->scaledFrameBuffer;
+ }
+
+ public function invalidateAll($pal) {
+ $stop = ($pal + 1) * $this->tileCountInvalidator;
+ for ($r = $pal * $this->tileCountInvalidator; $r < $stop; $r++) {
+ $this->tileData[$r] = null;
+ }
+ }
+
+ public function setGBCPalettePre($index_, $data) {
+ if ($this->gbcRawPalette[$index_] == $data) {
+ return;
+ }
+ $this->gbcRawPalette[$index_] = $data;
+ if ($index_ >= 0x40 && ($index_ & 0x6) == 0) {
+ // stay transparent
+ return;
+ }
+ $value = ($this->gbcRawPalette[$index_ | 1] << 8) + $this->gbcRawPalette[$index_ & -2];
+ $this->gbcPalette[$index_ >> 1] = 0x80000000 + (($value & 0x1F) << 19) + (($value & 0x3E0) << 6) + (($value & 0x7C00) >> 7);
+ $this->invalidateAll($index_ >> 3);
+ }
+
+ public function setGBCPalette($index_, $data) {
+ $this->setGBCPalettePre($index_, $data);
+ if (($index_ & 0x6) == 0) {
+ $this->gbcPalette[$index_ >> 1] &= 0x00FFFFFF;
+ }
+ }
+
+ public function decodePalette($startIndex, $data) {
+ if (!$this->cGBC) {
+ $this->gbPalette[$startIndex] = $this->colors[$data & 0x03] & 0x00FFFFFF; // color 0: transparent
+ $this->gbPalette[$startIndex + 1] = $this->colors[($data >> 2) & 0x03];
+ $this->gbPalette[$startIndex + 2] = $this->colors[($data >> 4) & 0x03];
+ $this->gbPalette[$startIndex + 3] = $this->colors[$data >> 6];
+
+ if ($this->usedBootROM) { //Do palette conversions if we did the GBC bootup:
+ //GB colorization:
+ $startOffset = ($startIndex >= 4) ? 0x20 : 0;
+ $pal2 = $this->gbcPalette[$startOffset + (($data >> 2) & 0x03)];
+ $pal3 = $this->gbcPalette[$startOffset + (($data >> 4) & 0x03)];
+ $pal4 = $this->gbcPalette[$startOffset + ($data >> 6)];
+ $this->gbColorizedPalette[$startIndex] = $this->gbcPalette[$startOffset + ($data & 0x03)] & 0x00FFFFFF;
+ $this->gbColorizedPalette[$startIndex + 1] = ($pal2 >= 0x80000000) ? $pal2 : 0xFFFFFFFF;
+ $this->gbColorizedPalette[$startIndex + 2] = ($pal3 >= 0x80000000) ? $pal3 : 0xFFFFFFFF;
+ $this->gbColorizedPalette[$startIndex + 3] = ($pal4 >= 0x80000000) ? $pal4 : 0xFFFFFFFF;
+ }
+
+ //@TODO - Need to copy the new palette
+ $this->checkPaletteType();
+ }
+ }
+
+ public function notifyScanline() {
+ if ($this->actualScanLine == 0) {
+ $this->windowSourceLine = 0;
+ }
+ // determine the left edge of the window (160 if window is inactive)
+ $windowLeft = ($this->gfxWindowDisplay && $this->memory[0xFF4A] <= $this->actualScanLine) ? min(160, $this->memory[0xFF4B] - 7) : 160;
+ // step 1: background+window
+ $skippedAnything = $this->drawBackgroundForLine($this->actualScanLine, $windowLeft, 0);
+ // At this point, the high (alpha) byte in the frameBuffer is 0xff for colors 1,2,3 and
+ // 0x00 for color 0. Foreground sprites draw on all colors, background sprites draw on
+ // top of color 0 only.
+ // step 2: sprites
+ $this->drawSpritesForLine($this->actualScanLine);
+ // step 3: prio tiles+window
+ if ($skippedAnything) {
+ $this->drawBackgroundForLine($this->actualScanLine, $windowLeft, 0x80);
+ }
+ if ($windowLeft < 160) {
+ $this->windowSourceLine++;
+ }
+ }
+
+ public function drawBackgroundForLine($line, $windowLeft, $priority) {
+ $skippedTile = false;
+ $tileNum = 0;
+ $tileXCoord = 0;
+ $tileAttrib = 0;
+ $sourceY = $line + $this->memory[0xFF42];
+ $sourceImageLine = $sourceY & 0x7;
+ $tileX = $this->memory[0xFF43] >> 3;
+ $memStart = (($this->gfxBackgroundY) ? 0x1C00 : 0x1800) + (($sourceY & 0xF8) << 2);
+ $screenX = -($this->memory[0xFF43] & 7);
+
+ for (; $screenX < $windowLeft; $tileX++, $screenX += 8) {
+ $tileXCoord = ($tileX & 0x1F);
+ $baseaddr = $this->memory[0x8000 + $memStart + $tileXCoord];
+ $tileNum = ($this->gfxBackgroundX) ? $baseaddr : (($baseaddr > 0x7F) ? (($baseaddr & 0x7F) + 0x80) : ($baseaddr + 0x100));
+ if ($this->cGBC) {
+ $mapAttrib = $this->VRAM[$memStart + $tileXCoord];
+ if (($mapAttrib & 0x80) != $priority) {
+ $skippedTile = true;
+ continue;
+ }
+ $tileAttrib = (($mapAttrib & 0x07) << 2) + (($mapAttrib >> 5) & 0x03);
+ $tileNum += 384 * (($mapAttrib >> 3) & 0x01); // tile vram bank
+ }
+ $this->drawPartCopy($tileNum, $screenX, $line, $sourceImageLine, $tileAttrib);
+ }
+
+ if ($windowLeft < 160) {
+ // window!
+ $windowStartAddress = ($this->gfxWindowY) ? 0x1C00 : 0x1800;
+ $windowSourceTileY = $this->windowSourceLine >> 3;
+ $tileAddress = $windowStartAddress + ($windowSourceTileY * 0x20);
+ $windowSourceTileLine = $this->windowSourceLine & 0x7;
+ for ($screenX = $windowLeft; $screenX < 160; $tileAddress++, $screenX += 8) {
+ $baseaddr = $this->memory[0x8000 + $tileAddress];
+ $tileNum = ($this->gfxBackgroundX) ? $baseaddr : (($baseaddr > 0x7F) ? (($baseaddr & 0x7F) + 0x80) : ($baseaddr + 0x100));
+ if ($this->cGBC) {
+ $mapAttrib = $this->VRAM[$tileAddress];
+ if (($mapAttrib & 0x80) != $priority) {
+ $skippedTile = true;
+ continue;
+ }
+ $tileAttrib = (($mapAttrib & 0x07) << 2) + (($mapAttrib >> 5) & 0x03); // mirroring
+ $tileNum += 384 * (($mapAttrib >> 3) & 0x01); // tile vram bank
+ }
+ $this->drawPartCopy($tileNum, $screenX, $line, $windowSourceTileLine, $tileAttrib);
+ }
+ }
+ return $skippedTile;
+ }
+
+ public function drawPartCopy($tileIndex, $x, $y, $sourceLine, $attribs) {
+ $image = $this->tileData[$tileIndex + $this->tileCount * $attribs] ? $this->tileData[$tileIndex + $this->tileCount * $attribs] : $this->updateImage($tileIndex, $attribs);
+ $dst = $x + $y * 160;
+ $src = $sourceLine * 8;
+ $dstEnd = ($x > 152) ? (($y + 1) * 160) : ($dst + 8);
+ if ($x < 0) { // adjust left
+ $dst -= $x;
+ $src -= $x;
+ }
+
+ while ($dst < $dstEnd) {
+ $this->frameBuffer[$dst++] = $image[$src++];
+ }
+ }
+
+ public function checkPaletteType() {
+ //Reference the correct palette ahead of time...
+ $this->palette = ($this->cGBC) ? $this->gbcPalette : (($this->usedBootROM && Settings::$settings[17]) ? $this->gbColorizedPalette : $this->gbPalette);
+ }
+
+ public function updateImage($tileIndex, $attribs) {
+ $index_ = $tileIndex + $this->tileCount * $attribs;
+ $otherBank = ($tileIndex >= 384);
+ $offset = $otherBank ? (($tileIndex - 384) << 4) : ($tileIndex << 4);
+ $paletteStart = $attribs & 0xFC;
+ $transparent = $attribs >= $this->transparentCutoff;
+ $pixix = 0;
+ $pixixdx = 1;
+ $pixixdy = 0;
+ $tempPix = $this->getTypedArray(64, 0, "int32");
+ if (($attribs & 2) != 0) {
+ $pixixdy = -16;
+ $pixix = 56;
+ }
+ if (($attribs & 1) == 0) {
+ $pixixdx = -1;
+ $pixix += 7;
+ $pixixdy += 16;
+ }
+ for ($y = 8; --$y >= 0;) {
+ $num = $this->weaveLookup[$this->VRAMReadGFX($offset++, $otherBank)] + ($this->weaveLookup[$this->VRAMReadGFX($offset++, $otherBank)] << 1);
+ if ($num != 0) {
+ $transparent = false;
+ }
+ for ($x = 8; --$x >= 0;) {
+ $tempPix[$pixix] = $this->palette[$paletteStart + ($num & 3)] & -1;
+ $pixix += $pixixdx;
+ $num >>= 2;
+ }
+ $pixix += $pixixdy;
+ }
+ $this->tileData[$index_] = ($transparent) ? true : $tempPix;
+
+ $this->tileReadState[$tileIndex] = 1;
+ return $this->tileData[$index_];
+ }
+
+ public function drawSpritesForLine($line) {
+ if (!$this->gfxSpriteShow) {
+ return;
+ }
+ $minSpriteY = $line - (($this->gfxSpriteDouble) ? 15 : 7);
+ // either only do priorityFlag == 0 (all foreground),
+ // or first 0x80 (background) and then 0 (foreground)
+ $priorityFlag = $this->spritePriorityEnabled ? 0x80 : 0;
+ for (; $priorityFlag >= 0; $priorityFlag -= 0x80) {
+ $oamIx = 159;
+ while ($oamIx >= 0) {
+ $attributes = 0xFF & $this->memory[0xFE00 + $oamIx--];
+ if (($attributes & 0x80) == $priorityFlag || !$this->spritePriorityEnabled) {
+ $tileNum = (0xFF & $this->memory[0xFE00 + $oamIx--]);
+ $spriteX = (0xFF & $this->memory[0xFE00 + $oamIx--]) - 8;
+ $spriteY = (0xFF & $this->memory[0xFE00 + $oamIx--]) - 16;
+ $offset = $line - $spriteY;
+ if ($spriteX >= 160 || $spriteY < $minSpriteY || $offset < 0) {
+ continue;
+ }
+ if ($this->gfxSpriteDouble) {
+ $tileNum = $tileNum & 0xFE;
+ }
+ $spriteAttrib = ($attributes >> 5) & 0x03; // flipx: from bit 0x20 to 0x01, flipy: from bit 0x40 to 0x02
+ if ($this->cGBC) {
+ $spriteAttrib += 0x20 + (($attributes & 0x07) << 2); // palette
+ $tileNum += (384 >> 3) * ($attributes & 0x08); // tile vram bank
+ }
+ else {
+ // attributes 0x10: 0x00 = OBJ1 palette, 0x10 = OBJ2 palette
+ // spriteAttrib: 0x04: OBJ1 palette, 0x08: OBJ2 palette
+ $spriteAttrib += 0x4 + (($attributes & 0x10) >> 2);
+ }
+ if ($priorityFlag == 0x80) {
+ // background
+ if ($this->gfxSpriteDouble) {
+ if (($spriteAttrib & 2) != 0) {
+ $this->drawPartBgSprite(($tileNum | 1) - ($offset >> 3), $spriteX, $line, $offset & 7, $spriteAttrib);
+ }
+ else {
+ $this->drawPartBgSprite(($tileNum & -2) + ($offset >> 3), $spriteX, $line, $offset & 7, $spriteAttrib);
+ }
+ }
+ else {
+ $this->drawPartBgSprite($tileNum, $spriteX, $line, $offset, $spriteAttrib);
+ }
+ }
+ else {
+ // foreground
+ if ($this->gfxSpriteDouble) {
+ if (($spriteAttrib & 2) != 0) {
+ $this->drawPartFgSprite(($tileNum | 1) - ($offset >> 3), $spriteX, $line, $offset & 7, $spriteAttrib);
+ }
+ else {
+ $this->drawPartFgSprite(($tileNum & -2) + ($offset >> 3), $spriteX, $line, $offset & 7, $spriteAttrib);
+ }
+ }
+ else {
+ $this->drawPartFgSprite($tileNum, $spriteX, $line, $offset, $spriteAttrib);
+ }
+ }
+ }
+ else {
+ $oamIx -= 3;
+ }
+ }
+ }
+ }
+
+ public function drawPartFgSprite($tileIndex, $x, $y, $sourceLine, $attribs) {
+ $im = $this->tileData[$tileIndex + $this->tileCount * $attribs] ? $this->tileData[$tileIndex + $this->tileCount * $attribs] : $this->updateImage($tileIndex, $attribs);
+ if ($im === true) {
+ return;
+ }
+ $dst = $x + $y * 160;
+ $src = $sourceLine * 8;
+ $dstEnd = ($x > 152) ? (($y + 1) * 160) : ($dst + 8);
+ if ($x < 0) { // adjust left
+ $dst -= $x;
+ $src -= $x;
+ }
+ while ($dst < $dstEnd) {
+ if ($im[$src] < 0) {
+ $this->frameBuffer[$dst] = $im[$src];
+ }
+ $dst++;
+ $src++;
+ }
+ }
+
+ public function drawPartBgSprite($tileIndex, $x, $y, $sourceLine, $attribs) {
+ $im = $this->tileData[$tileIndex + $this->tileCount * $attribs] ? $this->tileData[$tileIndex + $this->tileCount * $attribs] : $this->updateImage($tileIndex, $attribs);
+ if ($im === true) {
+ return;
+ }
+ $dst = $x + $y * 160;
+ $src = $sourceLine * 8;
+ $dstEnd = ($x > 152) ? (($y + 1) * 160) : ($dst + 8);
+ if ($x < 0) { // adjust left
+ $dst -= $x;
+ $src -= $x;
+ }
+ while ($dst < $dstEnd) {
+ if ($im[$src] < 0 && $this->frameBuffer[$dst] >= 0) {
+ $this->frameBuffer[$dst] = $im[$src];
+ }
+ $dst++;
+ $src++;
+ }
+ }
+
+ //Memory Reading:
+ public function memoryRead($address) {
+ //Act as a wrapper for reading the returns from the compiled jumps to memory.
+ return $this->memoryReader[$address]($this, $address); //This seems to be faster than the usual if/else.
+ }
+
+ public function memoryReadJumpCompile() {
+ //Faster in some browsers, since we are doing less conditionals overall by implementing them in advance.
+ for ($index = 0x0000; $index <= 0xFFFF; $index++) {
+ if ($index < 0x4000) {
+ $this->memoryReader[$index] = function ($parentObj, $address) { //memoryReadNormal
+ return $parentObj->memory[$address];
+ };
+ }
+ else if ($index < 0x8000) {
+ $this->memoryReader[$index] = function ($parentObj, $address) { //memoryReadROM
+ return $parentObj->ROM[$parentObj->currentROMBank + $address];
+ };
+ }
+ else if ($index >= 0x8000 && $index < 0xA000) {
+ $VRAMReadCGBCPU = function ($parentObj, $address) {
+ //CPU Side Reading The VRAM (Optimized for GameBoy Color)
+ return ($parentObj->modeSTAT > 2) ? 0xFF : (($parentObj->currVRAMBank == 0) ? $parentObj->memory[$address] : $parentObj->VRAM[$address - 0x8000]);
+ };
+
+ $VRAMReadDMGCPU = function ($parentObj, $address) {
+ //CPU Side Reading The VRAM (Optimized for classic GameBoy)
+ return ($parentObj->modeSTAT > 2) ? 0xFF : $parentObj->memory[$address];
+ };
+
+ $this->memoryReader[$index] = ($this->cGBC) ? $VRAMReadCGBCPU : $VRAMReadDMGCPU;
+ }
+ else if ($index >= 0xA000 && $index < 0xC000) {
+ if (($this->numRAMBanks == 1 / 16 && $index < 0xA200) || $this->numRAMBanks >= 1) {
+ if (!$this->cMBC3) {
+ $this->memoryReader[$index] = function ($parentObj, $address) { //memoryReadMBC
+ //Switchable RAM
+ if ($parentObj->MBCRAMBanksEnabled || Settings::$settings[10]) {
+ return $parentObj->MBCRam[$address + $parentObj->currMBCRAMBankPosition];
+ }
+ //cout("Reading from disabled RAM.", 1);
+ return 0xFF;
+ };
+ }
+ else {
+ //MBC3 RTC + RAM:
+ $this->memoryReader[$index] = function ($parentObj, $address) { //memoryReadMBC3
+ //Switchable RAM
+ if ($parentObj->MBCRAMBanksEnabled || Settings::$settings[10]) {
+ switch ($parentObj->currMBCRAMBank) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ return $parentObj->MBCRam[$address + $parentObj->currMBCRAMBankPosition];
+ break;
+ case 0x08:
+ return $parentObj->latchedSeconds;
+ break;
+ case 0x09:
+ return $parentObj->latchedMinutes;
+ break;
+ case 0x0A:
+ return $parentObj->latchedHours;
+ break;
+ case 0x0B:
+ return $parentObj->latchedLDays;
+ break;
+ case 0x0C:
+ return ((($parentObj->RTCDayOverFlow) ? 0x80 : 0) + (($parentObj->RTCHALT) ? 0x40 : 0)) + $parentObj->latchedHDays;
+ }
+ }
+ //cout("Reading from invalid or disabled RAM.", 1);
+ return 0xFF;
+ };
+ }
+ }
+ else {
+ $this->memoryReader[$index] = function ($parentObj, $address) { //memoryReadBAD
+ return 0xFF;
+ };
+ }
+ }
+ else if ($index >= 0xC000 && $index < 0xE000) {
+ if (!$this->cGBC || $index < 0xD000) {
+ $this->memoryReader[$index] = function ($parentObj, $address) { //memoryReadNormal
+ return $parentObj->memory[$address];
+ };
+ }
+ else {
+ $this->memoryReader[$index] = function ($parentObj, $address) { //memoryReadGBCMemory
+ return $parentObj->GBCMemory[$address + $parentObj->gbcRamBankPosition];
+ };
+ }
+ }
+ else if ($index >= 0xE000 && $index < 0xFE00) {
+ if (!$this->cGBC || $index < 0xF000) {
+ $this->memoryReader[$index] = function ($parentObj, $address) { //memoryReadECHONormal
+ return $parentObj->memory[$address - 0x2000];
+ };
+ }
+ else {
+ $this->memoryReader[$index] = function ($parentObj, $address) { //memoryReadECHOGBCMemory
+ return $parentObj->GBCMemory[$address + $parentObj->gbcRamBankPositionECHO];
+ };
+ }
+ }
+ else if ($index < 0xFEA0) {
+ $this->memoryReader[$index] = function ($parentObj, $address) { //memoryReadOAM
+ return ($parentObj->modeSTAT > 1) ? 0xFF : $parentObj->memory[$address];
+ };
+ }
+ else if ($this->cGBC && $index >= 0xFEA0 && $index < 0xFF00) {
+ $this->memoryReader[$index] = function ($parentObj, $address) { //memoryReadNormal
+ return $parentObj->memory[$address];
+ };
+ }
+ else if ($index >= 0xFF00) {
+ switch ($index) {
+ case 0xFF00:
+ $this->memoryReader[0xFF00] = function ($parentObj, $address) {
+ return 0xC0 | $parentObj->memory[0xFF00]; //Top nibble returns as set.
+ };
+ break;
+ case 0xFF01:
+ $this->memoryReader[0xFF01] = function ($parentObj, $address) {
+ return (($parentObj->memory[0xFF02] & 0x1) == 0x1) ? 0xFF : $parentObj->memory[0xFF01];
+ };
+ break;
+ case 0xFF02:
+ if ($this->cGBC) {
+ $this->memoryReader[0xFF02] = function ($parentObj, $address) {
+ return 0x7C | $parentObj->memory[0xFF02];
+ };
+ }
+ else {
+ $this->memoryReader[0xFF02] = function ($parentObj, $address) {
+ return 0x7E | $parentObj->memory[0xFF02];
+ };
+ }
+ break;
+ case 0xFF07:
+ $this->memoryReader[0xFF07] = function ($parentObj, $address) {
+ return 0xF8 | $parentObj->memory[0xFF07];
+ };
+ break;
+ case 0xFF0F:
+ $this->memoryReader[0xFF0F] = function ($parentObj, $address) {
+ return 0xE0 | $parentObj->memory[0xFF0F];
+ };
+ break;
+ case 0xFF10:
+ $this->memoryReader[0xFF10] = function ($parentObj, $address) {
+ return 0x80 | $parentObj->memory[0xFF10];
+ };
+ break;
+ case 0xFF11:
+ $this->memoryReader[0xFF11] = function ($parentObj, $address) {
+ return 0x3F | $parentObj->memory[0xFF11];
+ };
+ break;
+ case 0xFF14:
+ $this->memoryReader[0xFF14] = function ($parentObj, $address) {
+ return 0xBF | $parentObj->memory[0xFF14];
+ };
+ break;
+ case 0xFF16:
+ $this->memoryReader[0xFF16] = function ($parentObj, $address) {
+ return 0x3F | $parentObj->memory[0xFF16];
+ };
+ break;
+ case 0xFF19:
+ $this->memoryReader[0xFF19] = function ($parentObj, $address) {
+ return 0xBF | $parentObj->memory[0xFF19];
+ };
+ break;
+ case 0xFF1A:
+ $this->memoryReader[0xFF1A] = function ($parentObj, $address) {
+ return 0x7F | $parentObj->memory[0xFF1A];
+ };
+ break;
+ case 0xFF1B:
+ $this->memoryReader[0xFF1B] = function ($parentObj, $address) {
+ return 0xFF;
+ };
+ break;
+ case 0xFF1C:
+ $this->memoryReader[0xFF1C] = function ($parentObj, $address) {
+ return 0x9F | $parentObj->memory[0xFF1C];
+ };
+ break;
+ case 0xFF1E:
+ $this->memoryReader[0xFF1E] = function ($parentObj, $address) {
+ return 0xBF | $parentObj->memory[0xFF1E];
+ };
+ break;
+ case 0xFF20:
+ $this->memoryReader[0xFF20] = function ($parentObj, $address) {
+ return 0xFF;
+ };
+ break;
+ case 0xFF23:
+ $this->memoryReader[0xFF23] = function ($parentObj, $address) {
+ return 0xBF | $parentObj->memory[0xFF23];
+ };
+ break;
+ case 0xFF26:
+ $this->memoryReader[0xFF26] = function ($parentObj, $address) {
+ return 0x70 | $parentObj->memory[0xFF26];
+ };
+ break;
+ case 0xFF30:
+ case 0xFF31:
+ case 0xFF32:
+ case 0xFF33:
+ case 0xFF34:
+ case 0xFF35:
+ case 0xFF36:
+ case 0xFF37:
+ case 0xFF38:
+ case 0xFF39:
+ case 0xFF3A:
+ case 0xFF3B:
+ case 0xFF3C:
+ case 0xFF3D:
+ case 0xFF3E:
+ case 0xFF3F:
+ $this->memoryReader[$index] = function ($parentObj, $address) {
+ return (($parentObj->memory[0xFF26] & 0x4) == 0x4) ? 0xFF : $parentObj->memory[$address];
+ };
+ break;
+ case 0xFF41:
+ $this->memoryReader[0xFF41] = function ($parentObj, $address) {
+ return 0x80 | $parentObj->memory[0xFF41] | $parentObj->modeSTAT;
+ };
+ break;
+ case 0xFF44:
+ $this->memoryReader[0xFF44] = function ($parentObj, $address) {
+ return (($parentObj->LCDisOn) ? $parentObj->memory[0xFF44] : 0);
+ };
+ break;
+ case 0xFF4F:
+ $this->memoryReader[0xFF4F] = function ($parentObj, $address) {
+ return $parentObj->currVRAMBank;
+ };
+ break;
+ default:
+ $this->memoryReader[$index] = function ($parentObj, $address) { //memoryReadNormal
+ return $parentObj->memory[$address];
+ };
+ }
+ }
+ else {
+ $this->memoryReader[$index] = function ($parentObj, $address) { //memoryReadBAD
+ return 0xFF;
+ };
+ }
+ }
+ }
+
+ public function VRAMReadGFX($address, $gbcBank) {
+ //Graphics Side Reading The VRAM
+ return ((!$gbcBank) ? $this->memory[0x8000 + $address] : $this->VRAM[$address]);
+ }
+
+ public function setCurrentMBC1ROMBank() {
+ //Read the cartridge ROM data from RAM memory:
+ switch ($this->ROMBank1offs) {
+ case 0x00:
+ case 0x20:
+ case 0x40:
+ case 0x60:
+ //Bank calls for 0x00, 0x20, 0x40, and 0x60 are really for 0x01, 0x21, 0x41, and 0x61.
+ $this->currentROMBank = $this->ROMBank1offs * 0x4000;
+ break;
+ default:
+ $this->currentROMBank = ($this->ROMBank1offs - 1) * 0x4000;
+ }
+ while ($this->currentROMBank + 0x4000 >= count($this->ROM)) {
+ $this->currentROMBank -= count($this->ROM);
+ }
+ }
+
+ public function setCurrentMBC2AND3ROMBank() {
+ //Read the cartridge ROM data from RAM memory:
+ //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):
+ $this->currentROMBank = max($this->ROMBank1offs - 1, 0) * 0x4000;
+ while ($this->currentROMBank + 0x4000 >= count($this->ROM)) {
+ $this->currentROMBank -= count($this->ROM);
+ }
+ }
+ public function setCurrentMBC5ROMBank() {
+ //Read the cartridge ROM data from RAM memory:
+ $this->currentROMBank = ($this->ROMBank1offs - 1) * 0x4000;
+ while ($this->currentROMBank + 0x4000 >= count($this->ROM)) {
+ $this->currentROMBank -= count($this->ROM);
+ }
+ }
+
+ //Memory Writing:
+ public function memoryWrite($address, $data) {
+ //Act as a wrapper for writing by compiled jumps to specific memory writing functions.
+ $this->memoryWriter[$address]($this, $address, $data);
+ }
+
+ public function memoryWriteJumpCompile() {
+ $MBCWriteEnable = function ($parentObj, $address, $data) {
+ //MBC RAM Bank Enable/Disable:
+ $parentObj->MBCRAMBanksEnabled = (($data & 0x0F) == 0x0A); //If lower nibble is 0x0A, then enable, otherwise disable.
+ };
+
+ $MBC3WriteROMBank = function ($parentObj, $address, $data) {
+ //MBC3 ROM bank switching:
+ $parentObj->ROMBank1offs = $data & 0x7F;
+ $parentObj->setCurrentMBC2AND3ROMBank();
+ };
+
+ $cartIgnoreWrite = function ($parentObj, $address, $data) {
+ //We might have encountered illegal RAM writing or such, so just do nothing...
+ };
+
+ //Faster in some browsers, since we are doing less conditionals overall by implementing them in advance.
+ for ($index = 0x0000; $index <= 0xFFFF; $index++) {
+ if ($index < 0x8000) {
+ if ($this->cMBC1) {
+ if ($index < 0x2000) {
+ $this->memoryWriter[$index] = $MBCWriteEnable;
+ }
+ else if ($index < 0x4000) {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { // MBC1WriteROMBank
+ //MBC1 ROM bank switching:
+ $parentObj->ROMBank1offs = ($parentObj->ROMBank1offs & 0x60) | ($data & 0x1F);
+ $parentObj->setCurrentMBC1ROMBank();
+ };
+ }
+ else if ($index < 0x6000) {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //MBC1WriteRAMBank
+ //MBC1 RAM bank switching
+ if ($parentObj->MBC1Mode) {
+ //4/32 Mode
+ $parentObj->currMBCRAMBank = $data & 0x3;
+ $parentObj->currMBCRAMBankPosition = ($parentObj->currMBCRAMBank << 13) - 0xA000;
+ }
+ else {
+ //16/8 Mode
+ $parentObj->ROMBank1offs = (($data & 0x03) << 5) | ($parentObj->ROMBank1offs & 0x1F);
+ $parentObj->setCurrentMBC1ROMBank();
+ }
+ };
+ }
+ else {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //MBC1WriteType
+ //MBC1 mode setting:
+ $parentObj->MBC1Mode = (($data & 0x1) == 0x1);
+ };
+ }
+ }
+ else if ($this->cMBC2) {
+ if ($index < 0x1000) {
+ $this->memoryWriter[$index] = $MBCWriteEnable;
+ }
+ else if ($index >= 0x2100 && $index < 0x2200) {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //MBC2WriteROMBank
+ //MBC2 ROM bank switching:
+ $parentObj->ROMBank1offs = $data & 0x0F;
+ $parentObj->setCurrentMBC2AND3ROMBank();
+ };
+ }
+ else {
+ $this->memoryWriter[$index] = $cartIgnoreWrite;
+ }
+ }
+ else if ($this->cMBC3) {
+ if ($index < 0x2000) {
+ $this->memoryWriter[$index] = $MBCWriteEnable;
+ }
+ else if ($index < 0x4000) {
+ $this->memoryWriter[$index] = $MBC3WriteROMBank;
+ }
+ else if ($index < 0x6000) {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //MBC3WriteRAMBank
+ $parentObj->currMBCRAMBank = $data;
+ if ($data < 4) {
+ //MBC3 RAM bank switching
+ $parentObj->currMBCRAMBankPosition = ($parentObj->currMBCRAMBank << 13) - 0xA000;
+ }
+ };
+ }
+ else {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //MBC3WriteRTCLatch
+ if ($data == 0) {
+ $parentObj->RTCisLatched = false;
+ }
+ else if (!$parentObj->RTCisLatched) {
+ //Copy over the current RTC time for reading.
+ $parentObj->RTCisLatched = true;
+ $parentObj->latchedSeconds = floor($parentObj->RTCSeconds);
+ $parentObj->latchedMinutes = $parentObj->RTCMinutes;
+ $parentObj->latchedHours = $parentObj->RTCHours;
+ $parentObj->latchedLDays = ($parentObj->RTCDays & 0xFF);
+ $parentObj->latchedHDays = $parentObj->RTCDays >> 8;
+ }
+ };
+ }
+ }
+ else if ($this->cMBC5 || $this->cRUMBLE) {
+ if ($index < 0x2000) {
+ $this->memoryWriter[$index] = $MBCWriteEnable;
+ }
+ else if ($index < 0x3000) {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //MBC5WriteROMBankLow
+ //MBC5 ROM bank switching:
+ $parentObj->ROMBank1offs = ($parentObj->ROMBank1offs & 0x100) | $data;
+ $parentObj->setCurrentMBC5ROMBank();
+ };
+ }
+ else if ($index < 0x4000) {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //MBC5WriteROMBankHigh
+ //MBC5 ROM bank switching (by least significant bit):
+ $parentObj->ROMBank1offs = (($data & 0x01) << 8) | ($parentObj->ROMBank1offs & 0xFF);
+ $parentObj->setCurrentMBC5ROMBank();
+ };
+ }
+ else if ($index < 0x6000) {
+ $RUMBLEWriteRAMBank = function ($parentObj, $address, $data) {
+ //MBC5 RAM bank switching
+ //Like MBC5, but bit 3 of the lower nibble is used for rumbling and bit 2 is ignored.
+ $parentObj->currMBCRAMBank = $data & 0x3;
+ $parentObj->currMBCRAMBankPosition = ($parentObj->currMBCRAMBank << 13) - 0xA000;
+ };
+
+ $MBC5WriteRAMBank = function ($parentObj, $address, $data) {
+ //MBC5 RAM bank switching
+ $parentObj->currMBCRAMBank = $data & 0xF;
+ $parentObj->currMBCRAMBankPosition = ($parentObj->currMBCRAMBank << 13) - 0xA000;
+ };
+
+ $this->memoryWriter[$index] = ($this->cRUMBLE) ? $RUMBLEWriteRAMBank : $MBC5WriteRAMBank;
+ }
+ else {
+ $this->memoryWriter[$index] = $cartIgnoreWrite;
+ }
+ }
+ else if ($this->cHuC3) {
+ if ($index < 0x2000) {
+ $this->memoryWriter[$index] = $MBCWriteEnable;
+ }
+ else if ($index < 0x4000) {
+ $this->memoryWriter[$index] = $MBC3WriteROMBank;
+ }
+ else if ($index < 0x6000) {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //HuC3WriteRAMBank
+ //HuC3 RAM bank switching
+ $parentObj->currMBCRAMBank = $data & 0x03;
+ $parentObj->currMBCRAMBankPosition = ($parentObj->currMBCRAMBank << 13) - 0xA000;
+ };
+ }
+ else {
+ $this->memoryWriter[$index] = $cartIgnoreWrite;
+ }
+ }
+ else {
+ $this->memoryWriter[$index] = $cartIgnoreWrite;
+ }
+ }
+ else if ($index < 0xA000) {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { // VRAMWrite
+ if ($parentObj->modeSTAT < 3) { //VRAM cannot be written to during mode 3
+ if ($address < 0x9800) { // Bkg Tile data area
+ $tileIndex = (($address - 0x8000) >> 4) + (384 * $parentObj->currVRAMBank);
+ if ($parentObj->tileReadState[$tileIndex] == 1) {
+ $r = count($parentObj->tileData) - $parentObj->tileCount + $tileIndex;
+ do {
+ $parentObj->tileData[$r] = null;
+ $r -= $parentObj->tileCount;
+ } while ($r >= 0);
+ $parentObj->tileReadState[$tileIndex] = 0;
+ }
+ }
+ if ($parentObj->currVRAMBank == 0) {
+ $parentObj->memory[$address] = $data;
+ }
+ else {
+ $parentObj->VRAM[$address - 0x8000] = $data;
+ }
+ }
+ };
+ }
+ else if ($index < 0xC000) {
+ if (($this->numRAMBanks == 1 / 16 && $index < 0xA200) || $this->numRAMBanks >= 1) {
+ if (!$this->cMBC3) {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //memoryWriteMBCRAM
+ if ($parentObj->MBCRAMBanksEnabled || Settings::$settings[10]) {
+ $parentObj->MBCRam[$address + $parentObj->currMBCRAMBankPosition] = $data;
+ }
+ };
+ }
+ else {
+ //MBC3 RTC + RAM:
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //memoryWriteMBC3RAM
+ if ($parentObj->MBCRAMBanksEnabled || Settings::$settings[10]) {
+ switch ($parentObj->currMBCRAMBank) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ $parentObj->MBCRam[$address + $parentObj->currMBCRAMBankPosition] = $data;
+ break;
+ case 0x08:
+ if ($data < 60) {
+ $parentObj->RTCSeconds = $data;
+ }
+ else {
+ echo "(Bank #" + $parentObj->currMBCRAMBank + ") RTC write out of range: " + $data . PHP_EOL;
+ }
+ break;
+ case 0x09:
+ if ($data < 60) {
+ $parentObj->RTCMinutes = $data;
+ }
+ else {
+ echo "(Bank #" + $parentObj->currMBCRAMBank + ") RTC write out of range: " + $data . PHP_EOL;
+ }
+ break;
+ case 0x0A:
+ if ($data < 24) {
+ $parentObj->RTCHours = $data;
+ }
+ else {
+ echo "(Bank #" + $parentObj->currMBCRAMBank + ") RTC write out of range: " + $data . PHP_EOL;
+ }
+ break;
+ case 0x0B:
+ $parentObj->RTCDays = ($data & 0xFF) | ($parentObj->RTCDays & 0x100);
+ break;
+ case 0x0C:
+ $parentObj->RTCDayOverFlow = ($data & 0x80) == 0x80;
+ $parentObj->RTCHalt = ($data & 0x40) == 0x40;
+ $parentObj->RTCDays = (($data & 0x1) << 8) | ($parentObj->RTCDays & 0xFF);
+ break;
+ default:
+ echo "Invalid MBC3 bank address selected: " + $parentObj->currMBCRAMBank . PHP_EOL;
+ }
+ }
+ };
+ }
+ }
+ else {
+ $this->memoryWriter[$index] = $cartIgnoreWrite;
+ }
+ }
+ else if ($index < 0xE000) {
+ if ($this->cGBC && $index >= 0xD000) {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //memoryWriteGBCRAM
+ $parentObj->GBCMemory[$address + $parentObj->gbcRamBankPosition] = $data;
+ };
+ }
+ else {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //memoryWriteNormal
+ $parentObj->memory[$address] = $data;
+ };
+ }
+ }
+ else if ($index < 0xFE00) {
+ if ($this->cGBC && $index >= 0xF000) {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //memoryWriteECHOGBCRAM
+ $parentObj->GBCMemory[$address + $parentObj->gbcRamBankPositionECHO] = $data;
+ };
+ }
+ else {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //memoryWriteECHONormal
+ $parentObj->memory[$address - 0x2000] = $data;
+ };
+ }
+ }
+ else if ($index <= 0xFEA0) {
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //memoryWriteOAMRAM
+ if ($parentObj->modeSTAT < 2) { //OAM RAM cannot be written to in mode 2 & 3
+ $parentObj->memory[$address] = $data;
+ }
+ };
+ }
+ else if ($index < 0xFF00) {
+ if ($this->cGBC) { //Only GBC has access to this RAM.
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //memoryWriteNormal
+ $parentObj->memory[$address] = $data;
+ };
+ }
+ else {
+ $this->memoryWriter[$index] = $cartIgnoreWrite;
+ }
+ }
+ else {
+ //Start the I/O initialization by filling in the slots as normal memory:
+ $this->memoryWriter[$index] = function ($parentObj, $address, $data) { //memoryWriteNormal
+ $parentObj->memory[$address] = $data;
+ };
+ }
+ }
+ $this->registerWriteJumpCompile(); //Compile the I/O write functions separately...
+ }
+
+ public function registerWriteJumpCompile() {
+ //I/O Registers (GB + GBC):
+ $this->memoryWriter[0xFF00] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF00] = ($data & 0x30) | (((($data & 0x20) == 0) ? ($parentObj->JoyPad >> 4) : 0xF) & ((($data & 0x10) == 0) ? ($parentObj->JoyPad & 0xF) : 0xF));
+ };
+ $this->memoryWriter[0xFF02] = function ($parentObj, $address, $data) {
+ if ((($data & 0x1) == 0x1)) {
+ //Internal clock:
+ $parentObj->memory[0xFF02] = ($data & 0x7F);
+ $parentObj->memory[0xFF0F] |= 0x8; //Get this time delayed...
+ }
+ else {
+ //External clock:
+ $parentObj->memory[0xFF02] = $data;
+ //No connected serial device, so don't trigger interrupt...
+ }
+ };
+ $this->memoryWriter[0xFF04] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF04] = 0;
+ };
+ $this->memoryWriter[0xFF07] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF07] = $data & 0x07;
+ $parentObj->TIMAEnabled = ($data & 0x04) == 0x04;
+ $parentObj->TACClocker = pow(4, (($data & 0x3) != 0) ? ($data & 0x3) : 4); //TODO: Find a way to not make a conditional in here...
+ };
+ $this->memoryWriter[0xFF10] = function ($parentObj, $address, $data) {
+ $parentObj->channel1lastTimeSweep = $parentObj->channel1timeSweep = floor((($data & 0x70) >> 4) * $parentObj->channel1TimeSweepPreMultiplier);
+ $parentObj->channel1numSweep = $data & 0x07;
+ $parentObj->channel1frequencySweepDivider = 1 << $parentObj->channel1numSweep;
+ $parentObj->channel1decreaseSweep = (($data & 0x08) == 0x08);
+ $parentObj->memory[0xFF10] = $data;
+ };
+ $this->memoryWriter[0xFF11] = function ($parentObj, $address, $data) {
+ $parentObj->channel1duty = $data >> 6;
+ $parentObj->channel1adjustedDuty = $parentObj->dutyLookup[$parentObj->channel1duty];
+ $parentObj->channel1lastTotalLength = $parentObj->channel1totalLength = (0x40 - ($data & 0x3F)) * $parentObj->audioTotalLengthMultiplier;
+ $parentObj->memory[0xFF11] = $data & 0xC0;
+ };
+ $this->memoryWriter[0xFF12] = function ($parentObj, $address, $data) {
+ $parentObj->channel1envelopeVolume = $data >> 4;
+ $parentObj->channel1currentVolume = $parentObj->channel1envelopeVolume / 0xF;
+ $parentObj->channel1envelopeType = (($data & 0x08) == 0x08);
+ $parentObj->channel1envelopeSweeps = $data & 0x7;
+ $parentObj->channel1volumeEnvTime = $parentObj->channel1envelopeSweeps * $parentObj->volumeEnvelopePreMultiplier;
+ $parentObj->memory[0xFF12] = $data;
+ };
+ $this->memoryWriter[0xFF13] = function ($parentObj, $address, $data) {
+ $parentObj->channel1frequency = ($parentObj->channel1frequency & 0x700) | $data;
+ //Pre-calculate the frequency computation outside the waveform generator for speed:
+ $parentObj->channel1adjustedFrequencyPrep = $parentObj->preChewedAudioComputationMultiplier / (0x800 - $parentObj->channel1frequency);
+ $parentObj->memory[0xFF13] = $data;
+ };
+ $this->memoryWriter[0xFF14] = function ($parentObj, $address, $data) {
+ if (($data & 0x80) == 0x80) {
+ $parentObj->channel1envelopeVolume = $parentObj->memory[0xFF12] >> 4;
+ $parentObj->channel1currentVolume = $parentObj->channel1envelopeVolume / 0xF;
+ $parentObj->channel1envelopeSweeps = $parentObj->memory[0xFF12] & 0x7;
+ $parentObj->channel1volumeEnvTime = $parentObj->channel1envelopeSweeps * $parentObj->volumeEnvelopePreMultiplier;
+ $parentObj->channel1totalLength = $parentObj->channel1lastTotalLength;
+ $parentObj->channel1timeSweep = $parentObj->channel1lastTimeSweep;
+ $parentObj->channel1numSweep = $parentObj->memory[0xFF10] & 0x07;
+ $parentObj->channel1frequencySweepDivider = 1 << $parentObj->channel1numSweep;
+ if (($data & 0x40) == 0x40) {
+ $parentObj->memory[0xFF26] |= 0x1;
+ }
+ }
+ $parentObj->channel1consecutive = (($data & 0x40) == 0x0);
+ $parentObj->channel1frequency = (($data & 0x7) << 8) | ($parentObj->channel1frequency & 0xFF);
+ //Pre-calculate the frequency computation outside the waveform generator for speed:
+ $parentObj->channel1adjustedFrequencyPrep = $parentObj->preChewedAudioComputationMultiplier / (0x800 - $parentObj->channel1frequency);
+ $parentObj->memory[0xFF14] = $data & 0x40;
+ };
+ $this->memoryWriter[0xFF16] = function ($parentObj, $address, $data) {
+ $parentObj->channel2duty = $data >> 6;
+ $parentObj->channel2adjustedDuty = $parentObj->dutyLookup[$parentObj->channel2duty];
+ $parentObj->channel2lastTotalLength = $parentObj->channel2totalLength = (0x40 - ($data & 0x3F)) * $parentObj->audioTotalLengthMultiplier;
+ $parentObj->memory[0xFF16] = $data & 0xC0;
+ };
+ $this->memoryWriter[0xFF17] = function ($parentObj, $address, $data) {
+ $parentObj->channel2envelopeVolume = $data >> 4;
+ $parentObj->channel2currentVolume = $parentObj->channel2envelopeVolume / 0xF;
+ $parentObj->channel2envelopeType = (($data & 0x08) == 0x08);
+ $parentObj->channel2envelopeSweeps = $data & 0x7;
+ $parentObj->channel2volumeEnvTime = $parentObj->channel2envelopeSweeps * $parentObj->volumeEnvelopePreMultiplier;
+ $parentObj->memory[0xFF17] = $data;
+ };
+ $this->memoryWriter[0xFF18] = function ($parentObj, $address, $data) {
+ $parentObj->channel2frequency = ($parentObj->channel2frequency & 0x700) | $data;
+ //Pre-calculate the frequency computation outside the waveform generator for speed:
+ $parentObj->channel2adjustedFrequencyPrep = $parentObj->preChewedAudioComputationMultiplier / (0x800 - $parentObj->channel2frequency);
+ $parentObj->memory[0xFF18] = $data;
+ };
+ $this->memoryWriter[0xFF19] = function ($parentObj, $address, $data) {
+ if (($data & 0x80) == 0x80) {
+ $parentObj->channel2envelopeVolume = $parentObj->memory[0xFF17] >> 4;
+ $parentObj->channel2currentVolume = $parentObj->channel2envelopeVolume / 0xF;
+ $parentObj->channel2envelopeSweeps = $parentObj->memory[0xFF17] & 0x7;
+ $parentObj->channel2volumeEnvTime = $parentObj->channel2envelopeSweeps * $parentObj->volumeEnvelopePreMultiplier;
+ $parentObj->channel2totalLength = $parentObj->channel2lastTotalLength;
+ if (($data & 0x40) == 0x40) {
+ $parentObj->memory[0xFF26] |= 0x2;
+ }
+ }
+ $parentObj->channel2consecutive = (($data & 0x40) == 0x0);
+ $parentObj->channel2frequency = (($data & 0x7) << 8) | ($parentObj->channel2frequency & 0xFF);
+ //Pre-calculate the frequency computation outside the waveform generator for speed:
+ $parentObj->channel2adjustedFrequencyPrep = $parentObj->preChewedAudioComputationMultiplier / (0x800 - $parentObj->channel2frequency);
+ $parentObj->memory[0xFF19] = $data & 0x40;
+ };
+ $this->memoryWriter[0xFF1A] = function ($parentObj, $address, $data) {
+ $parentObj->channel3canPlay = ($data >= 0x80);
+ if ($parentObj->channel3canPlay && ($parentObj->memory[0xFF1A] & 0x80) == 0x80) {
+ $parentObj->channel3totalLength = $parentObj->channel3lastTotalLength;
+ if (!$parentObj->channel3consecutive) {
+ $parentObj->memory[0xFF26] |= 0x4;
+ }
+ }
+ $parentObj->memory[0xFF1A] = $data & 0x80;
+ };
+ $this->memoryWriter[0xFF1B] = function ($parentObj, $address, $data) {
+ $parentObj->channel3lastTotalLength = $parentObj->channel3totalLength = (0x100 - $data) * $parentObj->audioTotalLengthMultiplier;
+ $parentObj->memory[0xFF1B] = $data;
+ };
+ $this->memoryWriter[0xFF1C] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF1C] = $data & 0x60;
+ $parentObj->channel3patternType = $parentObj->memory[0xFF1C] >> 5;
+ };
+ $this->memoryWriter[0xFF1D] = function ($parentObj, $address, $data) {
+ $parentObj->channel3frequency = ($parentObj->channel3frequency & 0x700) | $data;
+ $parentObj->channel3adjustedFrequencyPrep = $parentObj->preChewedWAVEAudioComputationMultiplier / (0x800 - $parentObj->channel3frequency);
+ $parentObj->memory[0xFF1D] = $data;
+ };
+ $this->memoryWriter[0xFF1E] = function ($parentObj, $address, $data) {
+ if (($data & 0x80) == 0x80) {
+ $parentObj->channel3totalLength = $parentObj->channel3lastTotalLength;
+ if (($data & 0x40) == 0x40) {
+ $parentObj->memory[0xFF26] |= 0x4;
+ }
+ }
+ $parentObj->channel3consecutive = (($data & 0x40) == 0x0);
+ $parentObj->channel3frequency = (($data & 0x7) << 8) | ($parentObj->channel3frequency & 0xFF);
+ $parentObj->channel3adjustedFrequencyPrep = $parentObj->preChewedWAVEAudioComputationMultiplier / (0x800 - $parentObj->channel3frequency);
+ $parentObj->memory[0xFF1E] = $data & 0x40;
+ };
+ $this->memoryWriter[0xFF20] = function ($parentObj, $address, $data) {
+ $parentObj->channel4lastTotalLength = $parentObj->channel4totalLength = (0x40 - ($data & 0x3F)) * $parentObj->audioTotalLengthMultiplier;
+ $parentObj->memory[0xFF20] = $data | 0xC0;
+ };
+ $this->memoryWriter[0xFF21] = function ($parentObj, $address, $data) {
+ $parentObj->channel4envelopeVolume = $data >> 4;
+ $parentObj->channel4currentVolume = $parentObj->channel4envelopeVolume / 0xF;
+ $parentObj->channel4envelopeType = (($data & 0x08) == 0x08);
+ $parentObj->channel4envelopeSweeps = $data & 0x7;
+ $parentObj->channel4volumeEnvTime = $parentObj->channel4envelopeSweeps * $parentObj->volumeEnvelopePreMultiplier;
+ $parentObj->memory[0xFF21] = $data;
+ };
+ $this->memoryWriter[0xFF22] = function ($parentObj, $address, $data) {
+ $parentObj->channel4lastSampleLookup = 0;
+ $parentObj->channel4adjustedFrequencyPrep = $parentObj->whiteNoiseFrequencyPreMultiplier / max($data & 0x7, 0.5) / pow(2, ($data >> 4) + 1);
+ $parentObj->noiseTableLookup = (($data & 0x8) == 0x8) ? $parentObj->smallNoiseTable : $parentObj->largeNoiseTable;
+ $parentObj->memory[0xFF22] = $data;
+ };
+ $this->memoryWriter[0xFF23] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF23] = $data;
+ $parentObj->channel4consecutive = (($data & 0x40) == 0x0);
+ if (($data & 0x80) == 0x80) {
+ $parentObj->channel4lastSampleLookup = 0;
+ $parentObj->channel4envelopeVolume = $parentObj->memory[0xFF21] >> 4;
+ $parentObj->channel4currentVolume = $parentObj->channel4envelopeVolume / 0xF;
+ $parentObj->channel4envelopeSweeps = $parentObj->memory[0xFF21] & 0x7;
+ $parentObj->channel4volumeEnvTime = $parentObj->channel4envelopeSweeps * $parentObj->volumeEnvelopePreMultiplier;
+ $parentObj->channel4totalLength = $parentObj->channel4lastTotalLength;
+ if (($data & 0x40) == 0x40) {
+ $parentObj->memory[0xFF26] |= 0x8;
+ }
+ }
+ };
+ $this->memoryWriter[0xFF24] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF24] = $data;
+ /*$parentObj->VinLeftChannelEnabled = (($data >> 7) == 0x1);
+ $parentObj->VinRightChannelEnabled = ((($data >> 3) & 0x1) == 0x1);
+ $parentObj->VinLeftChannelMasterVolume = (($data >> 4) & 0x07);
+ $parentObj->VinRightChannelMasterVolume = ($data & 0x07);
+ $parentObj->vinLeft = ($parentObj->VinLeftChannelEnabled) ? $parentObj->VinLeftChannelMasterVolume / 7 : 1;
+ $parentObj->vinRight = ($parentObj->VinRightChannelEnabled) ? $parentObj->VinRightChannelMasterVolume / 7 : 1;*/
+ };
+ $this->memoryWriter[0xFF25] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF25] = $data;
+ $parentObj->leftChannel = [($data & 0x01) == 0x01, ($data & 0x02) == 0x02, ($data & 0x04) == 0x04, ($data & 0x08) == 0x08];
+ $parentObj->rightChannel = [($data & 0x10) == 0x10, ($data & 0x20) == 0x20, ($data & 0x40) == 0x40, ($data & 0x80) == 0x80];
+ };
+ $this->memoryWriter[0xFF26] = function ($parentObj, $address, $data) {
+ $soundEnabled = ($data & 0x80);
+ $parentObj->memory[0xFF26] = $soundEnabled | ($parentObj->memory[0xFF26] & 0xF);
+ $parentObj->soundMasterEnabled = ($soundEnabled == 0x80);
+ if (!$parentObj->soundMasterEnabled) {
+ $parentObj->memory[0xFF26] = 0;
+ $parentObj->initializeStartState();
+ for ($address = 0xFF30; $address < 0xFF40; $address++) {
+ $parentObj->memory[$address] = 0;
+ }
+ }
+ };
+ $this->memoryWriter[0xFF30] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[0] = $data >> 4;
+ $parentObj->channel3PCM[1] = $data & 0xF;
+ $parentObj->memory[0xFF30] = $data;
+ };
+ $this->memoryWriter[0xFF31] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[2] = $data >> 4;
+ $parentObj->channel3PCM[3] = $data & 0xF;
+ $parentObj->memory[0xFF31] = $data;
+ };
+ $this->memoryWriter[0xFF32] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[4] = $data >> 4;
+ $parentObj->channel3PCM[5] = $data & 0xF;
+ $parentObj->memory[0xFF32] = $data;
+ };
+ $this->memoryWriter[0xFF33] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[6] = $data >> 4;
+ $parentObj->channel3PCM[7] = $data & 0xF;
+ $parentObj->memory[0xFF33] = $data;
+ };
+ $this->memoryWriter[0xFF34] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[8] = $data >> 4;
+ $parentObj->channel3PCM[9] = $data & 0xF;
+ $parentObj->memory[0xFF34] = $data;
+ };
+ $this->memoryWriter[0xFF35] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[10] = $data >> 4;
+ $parentObj->channel3PCM[11] = $data & 0xF;
+ $parentObj->memory[0xFF35] = $data;
+ };
+ $this->memoryWriter[0xFF36] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[12] = $data >> 4;
+ $parentObj->channel3PCM[13] = $data & 0xF;
+ $parentObj->memory[0xFF36] = $data;
+ };
+ $this->memoryWriter[0xFF37] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[14] = $data >> 4;
+ $parentObj->channel3PCM[15] = $data & 0xF;
+ $parentObj->memory[0xFF37] = $data;
+ };
+ $this->memoryWriter[0xFF38] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[16] = $data >> 4;
+ $parentObj->channel3PCM[17] = $data & 0xF;
+ $parentObj->memory[0xFF38] = $data;
+ };
+ $this->memoryWriter[0xFF39] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[18] = $data >> 4;
+ $parentObj->channel3PCM[19] = $data & 0xF;
+ $parentObj->memory[0xFF39] = $data;
+ };
+ $this->memoryWriter[0xFF3A] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[20] = $data >> 4;
+ $parentObj->channel3PCM[21] = $data & 0xF;
+ $parentObj->memory[0xFF3A] = $data;
+ };
+ $this->memoryWriter[0xFF3B] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[22] = $data >> 4;
+ $parentObj->channel3PCM[23] = $data & 0xF;
+ $parentObj->memory[0xFF3B] = $data;
+ };
+ $this->memoryWriter[0xFF3C] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[24] = $data >> 4;
+ $parentObj->channel3PCM[25] = $data & 0xF;
+ $parentObj->memory[0xFF3C] = $data;
+ };
+ $this->memoryWriter[0xFF3D] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[26] = $data >> 4;
+ $parentObj->channel3PCM[27] = $data & 0xF;
+ $parentObj->memory[0xFF3D] = $data;
+ };
+ $this->memoryWriter[0xFF3E] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[28] = $data >> 4;
+ $parentObj->channel3PCM[29] = $data & 0xF;
+ $parentObj->memory[0xFF3E] = $data;
+ };
+ $this->memoryWriter[0xFF3F] = function ($parentObj, $address, $data) {
+ $parentObj->channel3PCM[30] = $data >> 4;
+ $parentObj->channel3PCM[31] = $data & 0xF;
+ $parentObj->memory[0xFF3F] = $data;
+ };
+ $this->memoryWriter[0xFF44] = function ($parentObj, $address, $data) {
+ //Read only
+ };
+ $this->memoryWriter[0xFF45] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF45] = $data;
+ if ($parentObj->LCDisOn) {
+ $parentObj->matchLYC(); //Get the compare of the first scan line.
+ }
+ };
+ $this->memoryWriter[0xFF46] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF46] = $data;
+ if ($parentObj->cGBC || $data > 0x7F) { //DMG cannot DMA from the ROM banks.
+ $data <<= 8;
+ $address = 0xFE00;
+ while ($address < 0xFEA0) {
+ $parentObj->memory[$address++] = $parentObj->memoryReader[$data]($parentObj, $data++);
+ }
+ }
+ };
+ $this->memoryWriter[0xFF47] = function ($parentObj, $address, $data) {
+ $parentObj->decodePalette(0, $data);
+ if ($parentObj->memory[0xFF47] != $data) {
+ $parentObj->memory[0xFF47] = $data;
+ $parentObj->invalidateAll(0);
+ }
+ };
+ $this->memoryWriter[0xFF48] = function ($parentObj, $address, $data) {
+ $parentObj->decodePalette(4, $data);
+ if ($parentObj->memory[0xFF48] != $data) {
+ $parentObj->memory[0xFF48] = $data;
+ $parentObj->invalidateAll(1);
+ }
+ };
+ $this->memoryWriter[0xFF49] = function ($parentObj, $address, $data) {
+ $parentObj->decodePalette(8, $data);
+ if ($parentObj->memory[0xFF49] != $data) {
+ $parentObj->memory[0xFF49] = $data;
+ $parentObj->invalidateAll(2);
+ }
+ };
+ if ($this->cGBC) {
+ //GameBoy Color Specific I/O:
+ $this->memoryWriter[0xFF40] = function ($parentObj, $address, $data) {
+ $temp_var = ($data & 0x80) == 0x80;
+ if ($temp_var != $parentObj->LCDisOn) {
+ //When the display mode changes...
+ $parentObj->LCDisOn = $temp_var;
+ $parentObj->memory[0xFF41] &= 0xF8;
+ $parentObj->STATTracker = $parentObj->modeSTAT = $parentObj->LCDTicks = $parentObj->actualScanLine = $parentObj->memory[0xFF44] = 0;
+ if ($parentObj->LCDisOn) {
+ $parentObj->matchLYC(); //Get the compare of the first scan line.
+ $parentObj->LCDCONTROL = $parentObj->LINECONTROL;
+ }
+ else {
+ $parentObj->LCDCONTROL = $parentObj->DISPLAYOFFCONTROL;
+ $parentObj->DisplayShowOff();
+ }
+ $parentObj->memory[0xFF0F] &= 0xFD;
+ }
+ $parentObj->gfxWindowY = ($data & 0x40) == 0x40;
+ $parentObj->gfxWindowDisplay = ($data & 0x20) == 0x20;
+ $parentObj->gfxBackgroundX = ($data & 0x10) == 0x10;
+ $parentObj->gfxBackgroundY = ($data & 0x08) == 0x08;
+ $parentObj->gfxSpriteDouble = ($data & 0x04) == 0x04;
+ $parentObj->gfxSpriteShow = ($data & 0x02) == 0x02;
+ $parentObj->spritePriorityEnabled = ($data & 0x01) == 0x01;
+ $parentObj->memory[0xFF40] = $data;
+ };
+ $this->memoryWriter[0xFF41] = function ($parentObj, $address, $data) {
+ $parentObj->LYCMatchTriggerSTAT = (($data & 0x40) == 0x40);
+ $parentObj->mode2TriggerSTAT = (($data & 0x20) == 0x20);
+ $parentObj->mode1TriggerSTAT = (($data & 0x10) == 0x10);
+ $parentObj->mode0TriggerSTAT = (($data & 0x08) == 0x08);
+ $parentObj->memory[0xFF41] = ($data & 0xF8);
+ };
+ $this->memoryWriter[0xFF4D] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF4D] = ($data & 0x7F) + ($parentObj->memory[0xFF4D] & 0x80);
+ };
+ $this->memoryWriter[0xFF4F] = function ($parentObj, $address, $data) {
+ $parentObj->currVRAMBank = $data & 0x01;
+ //Only writable by GBC.
+ };
+ $this->memoryWriter[0xFF51] = function ($parentObj, $address, $data) {
+ if (!$parentObj->hdmaRunning) {
+ $parentObj->memory[0xFF51] = $data;
+ }
+ };
+ $this->memoryWriter[0xFF52] = function ($parentObj, $address, $data) {
+ if (!$parentObj->hdmaRunning) {
+ $parentObj->memory[0xFF52] = $data & 0xF0;
+ }
+ };
+ $this->memoryWriter[0xFF53] = function ($parentObj, $address, $data) {
+ if (!$parentObj->hdmaRunning) {
+ $parentObj->memory[0xFF53] = $data & 0x1F;
+ }
+ };
+ $this->memoryWriter[0xFF54] = function ($parentObj, $address, $data) {
+ if (!$parentObj->hdmaRunning) {
+ $parentObj->memory[0xFF54] = $data & 0xF0;
+ }
+ };
+ $this->memoryWriter[0xFF55] = function ($parentObj, $address, $data) {
+ if (!$parentObj->hdmaRunning) {
+ if (($data & 0x80) == 0) {
+ //DMA
+ $parentObj->CPUTicks += 1 + ((8 * (($data & 0x7F) + 1)) * $parentObj->multiplier);
+ $dmaSrc = ($parentObj->memory[0xFF51] << 8) + $parentObj->memory[0xFF52];
+ $dmaDst = 0x8000 + ($parentObj->memory[0xFF53] << 8) + $parentObj->memory[0xFF54];
+ $endAmount = ((($data & 0x7F) * 0x10) + 0x10);
+ for ($loopAmount = 0; $loopAmount < $endAmount; $loopAmount++) {
+ $parentObj->memoryWrite($dmaDst++, $parentObj->memoryRead($dmaSrc++));
+ }
+ $parentObj->memory[0xFF51] = (($dmaSrc & 0xFF00) >> 8);
+ $parentObj->memory[0xFF52] = ($dmaSrc & 0x00F0);
+ $parentObj->memory[0xFF53] = (($dmaDst & 0x1F00) >> 8);
+ $parentObj->memory[0xFF54] = ($dmaDst & 0x00F0);
+ $parentObj->memory[0xFF55] = 0xFF; //Transfer completed.
+ }
+ else {
+ //H-Blank DMA
+ if ($data > 0x80) {
+ $parentObj->hdmaRunning = true;
+ $parentObj->memory[0xFF55] = $data & 0x7F;
+ }
+ else {
+ $parentObj->memory[0xFF55] = 0xFF;
+ }
+ }
+ }
+ else if (($data & 0x80) == 0) {
+ //Stop H-Blank DMA
+ $parentObj->hdmaRunning = false;
+ $parentObj->memory[0xFF55] |= 0x80;
+ }
+ };
+ $this->memoryWriter[0xFF68] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF69] = 0xFF & $parentObj->gbcRawPalette[$data & 0x3F];
+ $parentObj->memory[0xFF68] = $data;
+ };
+ $this->memoryWriter[0xFF69] = function ($parentObj, $address, $data) {
+ $parentObj->setGBCPalette($parentObj->memory[0xFF68] & 0x3F, $data);
+ if ($parentObj->usbtsb($parentObj->memory[0xFF68]) < 0) { // high bit = autoincrement
+ $next = (($parentObj->usbtsb($parentObj->memory[0xFF68]) + 1) & 0x3F);
+ $parentObj->memory[0xFF68] = ($next | 0x80);
+ $parentObj->memory[0xFF69] = 0xFF & $parentObj->gbcRawPalette[$next];
+ }
+ else {
+ $parentObj->memory[0xFF69] = $data;
+ }
+ };
+ $this->memoryWriter[0xFF6A] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF6B] = 0xFF & $parentObj->gbcRawPalette[($data & 0x3F) | 0x40];
+ $parentObj->memory[0xFF6A] = $data;
+ };
+ $this->memoryWriter[0xFF6B] = function ($parentObj, $address, $data) {
+ $parentObj->setGBCPalette(($parentObj->memory[0xFF6A] & 0x3F) + 0x40, $data);
+ if ($parentObj->usbtsb($parentObj->memory[0xFF6A]) < 0) { // high bit = autoincrement
+ $next = (($parentObj->memory[0xFF6A] + 1) & 0x3F);
+ $parentObj->memory[0xFF6A] = ($next | 0x80);
+ $parentObj->memory[0xFF6B] = 0xFF & $parentObj->gbcRawPalette[$next | 0x40];
+ }
+ else {
+ $parentObj->memory[0xFF6B] = $data;
+ }
+ };
+ $this->memoryWriter[0xFF70] = function ($parentObj, $address, $data) {
+ $addressCheck = ($parentObj->memory[0xFF51] << 8) | $parentObj->memory[0xFF52]; //Cannot change the RAM bank while WRAM is the source of a running HDMA.
+ if (!$parentObj->hdmaRunning || $addressCheck < 0xD000 || $addressCheck >= 0xE000) {
+ $parentObj->gbcRamBank = max($data & 0x07, 1); //Bank range is from 1-7
+ $parentObj->gbcRamBankPosition = (($parentObj->gbcRamBank - 1) * 0x1000) - 0xD000;
+ $parentObj->gbcRamBankPositionECHO = (($parentObj->gbcRamBank - 1) * 0x1000) - 0xF000;
+ }
+ $parentObj->memory[0xFF70] = ($data | 0x40); //Bit 6 cannot be written to.
+ };
+ }
+ else {
+ //Fill in the GameBoy Color I/O registers as normal RAM for GameBoy compatibility:
+ $this->memoryWriter[0xFF40] = function ($parentObj, $address, $data) {
+ $temp_var = ($data & 0x80) == 0x80;
+ if ($temp_var != $parentObj->LCDisOn) {
+ //When the display mode changes...
+ $parentObj->LCDisOn = $temp_var;
+ $parentObj->memory[0xFF41] &= 0xF8;
+ $parentObj->STATTracker = $parentObj->modeSTAT = $parentObj->LCDTicks = $parentObj->actualScanLine = $parentObj->memory[0xFF44] = 0;
+ if ($parentObj->LCDisOn) {
+ $parentObj->matchLYC(); //Get the compare of the first scan line.
+ $parentObj->LCDCONTROL = $parentObj->LINECONTROL;
+ }
+ else {
+ $parentObj->LCDCONTROL = $parentObj->DISPLAYOFFCONTROL;
+ $parentObj->DisplayShowOff();
+ }
+ $parentObj->memory[0xFF0F] &= 0xFD;
+ }
+ $parentObj->gfxWindowY = ($data & 0x40) == 0x40;
+ $parentObj->gfxWindowDisplay = ($data & 0x20) == 0x20;
+ $parentObj->gfxBackgroundX = ($data & 0x10) == 0x10;
+ $parentObj->gfxBackgroundY = ($data & 0x08) == 0x08;
+ $parentObj->gfxSpriteDouble = ($data & 0x04) == 0x04;
+ $parentObj->gfxSpriteShow = ($data & 0x02) == 0x02;
+ if (($data & 0x01) == 0) {
+ // this emulates the gbc-in-gb-mode, not the original gb-mode
+ $parentObj->bgEnabled = false;
+ $parentObj->gfxWindowDisplay = false;
+ }
+ else {
+ $parentObj->bgEnabled = true;
+ }
+ $parentObj->memory[0xFF40] = $data;
+ };
+ $this->memoryWriter[0xFF41] = function ($parentObj, $address, $data) {
+ $parentObj->LYCMatchTriggerSTAT = (($data & 0x40) == 0x40);
+ $parentObj->mode2TriggerSTAT = (($data & 0x20) == 0x20);
+ $parentObj->mode1TriggerSTAT = (($data & 0x10) == 0x10);
+ $parentObj->mode0TriggerSTAT = (($data & 0x08) == 0x08);
+ $parentObj->memory[0xFF41] = ($data & 0xF8);
+ if ($parentObj->LCDisOn && $parentObj->modeSTAT < 2) {
+ $parentObj->memory[0xFF0F] |= 0x2;
+ }
+ };
+ $this->memoryWriter[0xFF4D] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF4D] = $data;
+ };
+ $this->memoryWriter[0xFF4F] = function ($parentObj, $address, $data) {
+ //Not writable in DMG mode.
+ };
+ $this->memoryWriter[0xFF55] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF55] = $data;
+ };
+ $this->memoryWriter[0xFF68] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF68] = $data;
+ };
+ $this->memoryWriter[0xFF69] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF69] = $data;
+ };
+ $this->memoryWriter[0xFF6A] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF6A] = $data;
+ };
+ $this->memoryWriter[0xFF6B] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF6B] = $data;
+ };
+ $this->memoryWriter[0xFF70] = function ($parentObj, $address, $data) {
+ $parentObj->memory[0xFF70] = $data;
+ };
+ }
+ //Boot I/O Registers:
+ if ($this->inBootstrap) {
+ $this->memoryWriter[0xFF50] = function ($parentObj, $address, $data) {
+ echo "Boot ROM reads blocked: Bootstrap process has ended." . PHP_EOL;
+ $parentObj->inBootstrap = false;
+ $parentObj->disableBootROM(); //Fill in the boot ROM ranges with ROM bank 0 ROM ranges
+ $parentObj->memory[0xFF50] = $data; //Bits are sustained in memory?
+ };
+ $this->memoryWriter[0xFF6C] = function ($parentObj, $address, $data) {
+ if ($parentObj->inBootstrap) {
+ $parentObj->cGBC = ($data == 0x80);
+ echo "Booted to GBC Mode: " + $parentObj->cGBC . PHP_EOL;
+ }
+ $parentObj->memory[0xFF6C] = $data;
+ };
+ }
+ else {
+ //Lockout the ROMs from accessing the BOOT ROM control register:
+ $this->memoryWriter[0xFF6C] = $this->memoryWriter[0xFF50] = function ($parentObj, $address, $data) { };
+ }
+ }
+ //Helper Functions
+ public function usbtsb($ubyte) {
+ //Unsigned byte to signed byte:
+ return ($ubyte > 0x7F) ? (($ubyte & 0x7F) - 0x80) : $ubyte;
+ }
+
+ public function unsbtub($ubyte) {
+ //Keep an unsigned byte unsigned:
+ if ($ubyte < 0) {
+ $ubyte += 0x100;
+ }
+ return $ubyte; //If this function is called, no wrapping requested.
+ }
+
+ public function nswtuw($uword) {
+ //Keep an unsigned word unsigned:
+ if ($uword < 0) {
+ $uword += 0x10000;
+ }
+ return $uword & 0xFFFF; //Wrap also...
+ }
+
+ public function unswtuw($uword) {
+ //Keep an unsigned word unsigned:
+ if ($uword < 0) {
+ $uword += 0x10000;
+ }
+ return $uword; //If this function is called, no wrapping requested.
+ }
+
+ public function toTypedArray($baseArray, $bit32, $unsigned) {
+ try {
+ $typedArrayTemp = ($bit32) ? (($unsigned) ? new Uint32Array(count($baseArray)) : new Int32Array(count($baseArray))) : new Uint8Array(count($baseArray));
+ for ($index = 0; $index < count($baseArray); $index++) {
+ $typedArrayTemp[$index] = $baseArray[$index];
+ }
+ return $typedArrayTemp;
+ }
+ catch (\Exception $error) {
+ echo "Could not convert an array to a typed array" . PHP_EOL;
+ return $baseArray;
+ }
+ }
+
+ public function fromTypedArray($baseArray) {
+ try {
+ $arrayTemp = array_fill(0, count($baseArray), 0);
+ for ($index = 0; $index < count($baseArray); $index++) {
+ $arrayTemp[$index] = $baseArray[$index];
+ }
+ return $arrayTemp;
+ }
+ catch (\Exception $error) {
+ return $baseArray;
+ }
+ }
+
+ public function getTypedArray($length, $defaultValue, $numberType) {
+ try {
+ if (Settings::$settings[22]) {
+ throw(new Error(""));
+ }
+
+ /*
+ switch ($numberType) {
+ case "uint8":
+ $arrayHandle = new Uint8Array($length);
+ break;
+ case "int8":
+ $arrayHandle = new Int8Array($length);
+ break;
+ case "uint16":
+ $arrayHandle = new Uint16Array($length);
+ break;
+ case "int16":
+ $arrayHandle = new Int16Array($length);
+ break;
+ case "uint32":
+ $arrayHandle = new Uint32Array($length);
+ break;
+ case "int32":
+ $arrayHandle = new Int32Array($length);
+ break;
+ case "float32":
+ $arrayHandle = new Float32Array($length);
+ }
+ */
+
+ $arrayHandle = array_fill(0, $length, 0);
+
+ if ($defaultValue > 0) {
+ $index = 0;
+ while ($index < $length) {
+ $arrayHandle[$index++] = $defaultValue;
+ }
+ }
+ }
+ catch (\Exception $error) {
+ $arrayHandle = array_fill(0, $length, 0);
+ $index = 0;
+ while ($index < $length) {
+ $arrayHandle[$index++] = $defaultValue;
+ }
+ }
+ return $arrayHandle;
+ }
+
+ public function ArrayPad($length, $defaultValue) {
+ $arrayHandle = array_fill(0, $length, 0);
+ $index = 0;
+ while ($index < $length) {
+ $arrayHandle[$index++] = $defaultValue;
+ }
+ return $arrayHandle;
+ }
+} \ No newline at end of file
diff --git a/src/Gameboy/DrawContext.php b/src/Gameboy/DrawContext.php
new file mode 100644
index 0000000..82c243d
--- /dev/null
+++ b/src/Gameboy/DrawContext.php
@@ -0,0 +1,52 @@
+<?php
+namespace GameBoy;
+
+use Drawille\Canvas;
+
+class DrawContext
+{
+ // 160 x 144
+
+ protected $canvas;
+
+ public function __construct()
+ {
+ $this->canvas = new Canvas();
+ }
+
+ /**
+ * Put image on canvas
+ * @param Object $canvasBuffer $data = Each pixel = 4 items on array (RGBA)
+ * @param int $left
+ * @param int $top
+ */
+ public function putImageData($canvasBuffer, $left, $top)
+ {
+ $canvasBuffer = $canvasBuffer->data;
+
+ for ($i = 0; $i < count($canvasBuffer); $i = $i + 4) {
+ // IGNORE ALPHA
+ $total = $canvasBuffer[$i] + $canvasBuffer[$i + 1] + $canvasBuffer[$i + 2]; // + $canvasBuffer[$i + 3];
+
+ $x = ($i / 4) % 160;
+ $y = ceil(($i / 4) / 160);
+
+ if ($total > 350) {
+ $this->canvas->set($x, $y);
+ // echo 'SET ' . $x . ' - ' . $y . PHP_EOL;
+ }
+ }
+
+ echo "\e[H\e[2J";
+ echo $this->canvas->frame();
+ $this->canvas->clear();
+
+ // echo 'Draw' . PHP_EOL;
+ }
+
+ public function fillRect($left, $top, $width, $height)
+ {
+ $this->canvas->clear();
+ // echo 'Fill' . PHP_EOL;
+ }
+} \ No newline at end of file
diff --git a/src/Gameboy/Opcode.php b/src/Gameboy/Opcode.php
new file mode 100644
index 0000000..cb2aae9
--- /dev/null
+++ b/src/Gameboy/Opcode.php
@@ -0,0 +1,1995 @@
+<?php
+namespace GameBoy;
+
+class Opcode
+{
+ public $functionsArray = [];
+
+ public function __construct()
+ {
+ //NOP
+ //#0x00:
+ $this->functionsArray[] = function ($parentObj) {
+ //Do Nothing...
+ };
+ //LD BC, nn
+ //#0x01:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC = $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->registerB = $parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF);
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ };
+ //LD (BC), A
+ //#0x02:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite(($parentObj->registerB << 8) + $parentObj->registerC, $parentObj->registerA);
+ };
+ //INC BC
+ //#0x03:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = ((($parentObj->registerB << 8) + $parentObj->registerC) + 1);
+ $parentObj->registerB = (($temp_var >> 8) & 0xFF);
+ $parentObj->registerC = ($temp_var & 0xFF);
+ };
+ //INC B
+ //#0x04:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB = (($parentObj->registerB + 1) & 0xFF);
+ $parentObj->FZero = ($parentObj->registerB == 0);
+ $parentObj->FHalfCarry = (($parentObj->registerB & 0xF) == 0);
+ $parentObj->FSubtract = false;
+ };
+ //DEC B
+ //#0x05:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB = $parentObj->unsbtub($parentObj->registerB - 1);
+ $parentObj->FZero = ($parentObj->registerB == 0);
+ $parentObj->FHalfCarry = (($parentObj->registerB & 0xF) == 0xF);
+ $parentObj->FSubtract = true;
+ };
+ //LD B, n
+ //#0x06:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB = $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ };
+ //RLCA
+ //#0x07:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerA & 0x80) == 0x80);
+ $parentObj->registerA = (($parentObj->registerA << 1) & 0xFF) | ($parentObj->registerA >> 7);
+ $parentObj->FZero = $parentObj->FSubtract = $parentObj->FHalfCarry = false;
+ };
+ //LD (nn), SP
+ //#0x08:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = ($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->memoryWrite($temp_var, $parentObj->stackPointer & 0xFF);
+ $parentObj->memoryWrite(($temp_var + 1) & 0xFFFF, $parentObj->stackPointer >> 8);
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ };
+ //ADD HL, BC
+ //#0x09:
+ $this->functionsArray[] = function ($parentObj) {
+ $n2 = ($parentObj->registerB << 8) + $parentObj->registerC;
+ $dirtySum = $parentObj->registersHL + $n2;
+ $parentObj->FHalfCarry = (($parentObj->registersHL & 0xFFF) + ($n2 & 0xFFF) > 0xFFF);
+ $parentObj->FCarry = ($dirtySum > 0xFFFF);
+ $parentObj->registersHL = ($dirtySum & 0xFFFF);
+ $parentObj->FSubtract = false;
+ };
+ //LD A, (BC)
+ //#0x0A:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = $parentObj->memoryRead(($parentObj->registerB << 8) + $parentObj->registerC);
+ };
+ //DEC BC
+ //#0x0B:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->unswtuw((($parentObj->registerB << 8) + $parentObj->registerC) - 1);
+ $parentObj->registerB = ($temp_var >> 8);
+ $parentObj->registerC = ($temp_var & 0xFF);
+ };
+ //INC C
+ //#0x0C:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC = (($parentObj->registerC + 1) & 0xFF);
+ $parentObj->FZero = ($parentObj->registerC == 0);
+ $parentObj->FHalfCarry = (($parentObj->registerC & 0xF) == 0);
+ $parentObj->FSubtract = false;
+ };
+ //DEC C
+ //#0x0D:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC = $parentObj->unsbtub($parentObj->registerC - 1);
+ $parentObj->FZero = ($parentObj->registerC == 0);
+ $parentObj->FHalfCarry = (($parentObj->registerC & 0xF) == 0xF);
+ $parentObj->FSubtract = true;
+ };
+ //LD C, n
+ //#0x0E:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC = $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ };
+ //RRCA
+ //#0x0F:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = (($parentObj->registerA & 1) == 1);
+ $parentObj->registerA = ($parentObj->registerA >> 1) + (($parentObj->registerA & 1) << 7);
+ $parentObj->FZero = $parentObj->FSubtract = $parentObj->FHalfCarry = false;
+ };
+ //STOP
+ //#0x10:
+ $this->functionsArray[] = function ($parentObj) {
+ if ($parentObj->cGBC) {
+ /*TODO: Emulate the speed switch delay:
+ Delay Amount:
+ 16 ms when going to double-speed.
+ 32 ms when going to single-speed.
+ Also, bits 4 and 5 of 0xFF00 should read as set (1), while the switch is in process.
+ */
+ if (($parentObj->memory[0xFF4D] & 0x01) == 0x01) { //Speed change requested.
+ if (($parentObj->memory[0xFF4D] & 0x80) == 0x80) { //Go back to single speed mode.
+ // cout("Going into single clock speed mode.", 0);
+ $parentObj->multiplier = 1; //TODO: Move this into the delay done code.
+ $parentObj->memory[0xFF4D] &= 0x7F; //Clear the double speed mode flag.
+ }
+ else { //Go to double speed mode.
+ // cout("Going into double clock speed mode.", 0);
+ $parentObj->multiplier = 2; //TODO: Move this into the delay done code.
+ $parentObj->memory[0xFF4D] |= 0x80; //Set the double speed mode flag.
+ }
+ $parentObj->memory[0xFF4D] &= 0xFE; //Reset the request bit.
+ }
+ }
+ };
+ //LD DE, nn
+ //#0x11:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE = $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->registerD = $parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF);
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ };
+ //LD (DE), A
+ //#0x12:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite(($parentObj->registerD << 8) + $parentObj->registerE, $parentObj->registerA);
+ };
+ //INC DE
+ //#0x13:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = ((($parentObj->registerD << 8) + $parentObj->registerE) + 1);
+ $parentObj->registerD = (($temp_var >> 8) & 0xFF);
+ $parentObj->registerE = ($temp_var & 0xFF);
+ };
+ //INC D
+ //#0x14:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD = (($parentObj->registerD + 1) & 0xFF);
+ $parentObj->FZero = ($parentObj->registerD == 0);
+ $parentObj->FHalfCarry = (($parentObj->registerD & 0xF) == 0);
+ $parentObj->FSubtract = false;
+ };
+ //DEC D
+ //#0x15:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD = $parentObj->unsbtub($parentObj->registerD - 1);
+ $parentObj->FZero = ($parentObj->registerD == 0);
+ $parentObj->FHalfCarry = (($parentObj->registerD & 0xF) == 0xF);
+ $parentObj->FSubtract = true;
+ };
+ //LD D, n
+ //#0x16:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD = $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ };
+ //RLA
+ //#0x17:
+ $this->functionsArray[] = function ($parentObj) {
+ $carry_flag = ($parentObj->FCarry) ? 1 : 0;
+ $parentObj->FCarry = (($parentObj->registerA & 0x80) == 0x80);
+ $parentObj->registerA = (($parentObj->registerA << 1) & 0xFF) | $carry_flag;
+ $parentObj->FZero = $parentObj->FSubtract = $parentObj->FHalfCarry = false;
+ };
+ //JR n
+ //#0x18:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->programCounter = $parentObj->nswtuw($parentObj->programCounter + $parentObj->usbtsb($parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter)) + 1);
+ };
+ //ADD HL, DE
+ //#0x19:
+ $this->functionsArray[] = function ($parentObj) {
+ $n2 = ($parentObj->registerD << 8) + $parentObj->registerE;
+ $dirtySum = $parentObj->registersHL + $n2;
+ $parentObj->FHalfCarry = (($parentObj->registersHL & 0xFFF) + ($n2 & 0xFFF) > 0xFFF);
+ $parentObj->FCarry = ($dirtySum > 0xFFFF);
+ $parentObj->registersHL = ($dirtySum & 0xFFFF);
+ $parentObj->FSubtract = false;
+ };
+ //LD A, (DE)
+ //#0x1A:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = $parentObj->memoryRead(($parentObj->registerD << 8) + $parentObj->registerE);
+ };
+ //DEC DE
+ //#0x1B:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->unswtuw((($parentObj->registerD << 8) + $parentObj->registerE) - 1);
+ $parentObj->registerD = ($temp_var >> 8);
+ $parentObj->registerE = ($temp_var & 0xFF);
+ };
+ //INC E
+ //#0x1C:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE = (($parentObj->registerE + 1) & 0xFF);
+ $parentObj->FZero = ($parentObj->registerE == 0);
+ $parentObj->FHalfCarry = (($parentObj->registerE & 0xF) == 0);
+ $parentObj->FSubtract = false;
+ };
+ //DEC E
+ //#0x1D:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE = $parentObj->unsbtub($parentObj->registerE - 1);
+ $parentObj->FZero = ($parentObj->registerE == 0);
+ $parentObj->FHalfCarry = (($parentObj->registerE & 0xF) == 0xF);
+ $parentObj->FSubtract = true;
+ };
+ //LD E, n
+ //#0x1E:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE = $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ };
+ //RRA
+ //#0x1F:
+ $this->functionsArray[] = function ($parentObj) {
+ $carry_flag = ($parentObj->FCarry) ? 0x80 : 0;
+ $parentObj->FCarry = (($parentObj->registerA & 1) == 1);
+ $parentObj->registerA = ($parentObj->registerA >> 1) + $carry_flag;
+ $parentObj->FZero = $parentObj->FSubtract = $parentObj->FHalfCarry = false;
+ };
+ //JR cc, n
+ //#0x20:
+ $this->functionsArray[] = function ($parentObj) {
+ if (!$parentObj->FZero) {
+ $parentObj->programCounter = $parentObj->nswtuw($parentObj->programCounter + $parentObj->usbtsb($parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter)) + 1);
+ $parentObj->CPUTicks++;
+ }
+ else {
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ }
+ };
+ //LD HL, nn
+ //#0x21:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ };
+ //LDI (HL), A
+ //#0x22:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->registerA);
+ $parentObj->registersHL = (($parentObj->registersHL + 1) & 0xFFFF);
+ };
+ //INC HL
+ //#0x23:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = (($parentObj->registersHL + 1) & 0xFFFF);
+ };
+ //INC H
+ //#0x24:
+ $this->functionsArray[] = function ($parentObj) {
+ $H = ((($parentObj->registersHL >> 8) + 1) & 0xFF);
+ $parentObj->FZero = ($H == 0);
+ $parentObj->FHalfCarry = (($H & 0xF) == 0);
+ $parentObj->FSubtract = false;
+ $parentObj->registersHL = ($H << 8) + ($parentObj->registersHL & 0xFF);
+ };
+ //DEC H
+ //#0x25:
+ $this->functionsArray[] = function ($parentObj) {
+ $H = $parentObj->unsbtub(($parentObj->registersHL >> 8) - 1);
+ $parentObj->FZero = ($H == 0);
+ $parentObj->FHalfCarry = (($H & 0xF) == 0xF);
+ $parentObj->FSubtract = true;
+ $parentObj->registersHL = ($H << 8) + ($parentObj->registersHL & 0xFF);
+ };
+ //LD H, n
+ //#0x26:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter) << 8) + ($parentObj->registersHL & 0xFF);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ };
+ //DAA
+ //#0x27:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->registerA;
+ if ($parentObj->FCarry) {
+ $temp_var |= 0x100;
+ }
+ if ($parentObj->FHalfCarry) {
+ $temp_var |= 0x200;
+ }
+ if ($parentObj->FSubtract) {
+ $temp_var |= 0x400;
+ }
+ $parentObj->registerA = ($temp_var = $parentObj->DAATable[$temp_var]) >> 8;
+ $parentObj->FZero = (($temp_var & 0x80) == 0x80);
+ $parentObj->FSubtract = (($temp_var & 0x40) == 0x40);
+ $parentObj->FHalfCarry = (($temp_var & 0x20) == 0x20);
+ $parentObj->FCarry = (($temp_var & 0x10) == 0x10);
+ };
+ //JR cc, n
+ //#0x28:
+ $this->functionsArray[] = function ($parentObj) {
+ if ($parentObj->FZero) {
+ $parentObj->programCounter = $parentObj->nswtuw($parentObj->programCounter + $parentObj->usbtsb($parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter)) + 1);
+ $parentObj->CPUTicks++;
+ }
+ else {
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ }
+ };
+ //ADD HL, HL
+ //#0x29:
+ $this->functionsArray[] = function ($parentObj) {;
+ $parentObj->FHalfCarry = (($parentObj->registersHL & 0xFFF) > 0x7FF);
+ $parentObj->FCarry = ($parentObj->registersHL > 0x7FFF);
+ $parentObj->registersHL = ((2 * $parentObj->registersHL) & 0xFFFF);
+ $parentObj->FSubtract = false;
+ };
+ //LDI A, (HL)
+ //#0x2A:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $parentObj->registersHL = (($parentObj->registersHL + 1) & 0xFFFF);
+ };
+ //DEC HL
+ //#0x2B:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = $parentObj->unswtuw($parentObj->registersHL - 1);
+ };
+ //INC L
+ //#0x2C:
+ $this->functionsArray[] = function ($parentObj) {
+ $L = (($parentObj->registersHL + 1) & 0xFF);
+ $parentObj->FZero = ($L == 0);
+ $parentObj->FHalfCarry = (($L & 0xF) == 0);
+ $parentObj->FSubtract = false;
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + $L;
+ };
+ //DEC L
+ //#0x2D:
+ $this->functionsArray[] = function ($parentObj) {
+ $L = $parentObj->unsbtub(($parentObj->registersHL & 0xFF) - 1);
+ $parentObj->FZero = ($L == 0);
+ $parentObj->FHalfCarry = (($L & 0xF) == 0xF);
+ $parentObj->FSubtract = true;
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + $L;
+ };
+ //LD L, n
+ //#0x2E:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ };
+ //CPL
+ //#0x2F:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA ^= 0xFF;
+ $parentObj->FSubtract = $parentObj->FHalfCarry = true;
+ };
+ //JR cc, n
+ //#0x30:
+ $this->functionsArray[] = function ($parentObj) {
+ if (!$parentObj->FCarry) {
+ $parentObj->programCounter = $parentObj->nswtuw($parentObj->programCounter + $parentObj->usbtsb($parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter)) + 1);
+ $parentObj->CPUTicks++;
+ }
+ else {
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ }
+ };
+ //LD SP, nn
+ //#0x31:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = ($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ };
+ //LDD (HL), A
+ //#0x32:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->registerA);
+ $parentObj->registersHL = $parentObj->unswtuw($parentObj->registersHL - 1);
+ };
+ //INC SP
+ //#0x33:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = ($parentObj->stackPointer + 1) & 0xFFFF;
+ };
+ //INC (HL)
+ //#0x34:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = (($parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) + 1) & 0xFF);
+ $parentObj->FZero = ($temp_var == 0);
+ $parentObj->FHalfCarry = (($temp_var & 0xF) == 0);
+ $parentObj->FSubtract = false;
+ $parentObj->memoryWrite($parentObj->registersHL, $temp_var);
+ };
+ //DEC (HL)
+ //#0x35:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->unsbtub($parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) - 1);
+ $parentObj->FZero = ($temp_var == 0);
+ $parentObj->FHalfCarry = (($temp_var & 0xF) == 0xF);
+ $parentObj->FSubtract = true;
+ $parentObj->memoryWrite($parentObj->registersHL, $temp_var);
+ };
+ //LD (HL), n
+ //#0x36:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter));
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ };
+ //SCF
+ //#0x37:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = true;
+ $parentObj->FSubtract = $parentObj->FHalfCarry = false;
+ };
+ //JR cc, n
+ //#0x38:
+ $this->functionsArray[] = function ($parentObj) {
+ if ($parentObj->FCarry) {
+ $parentObj->programCounter = $parentObj->nswtuw($parentObj->programCounter + $parentObj->usbtsb($parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter)) + 1);
+ $parentObj->CPUTicks++;
+ }
+ else {
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ }
+ };
+ //ADD HL, SP
+ //#0x39:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registersHL + $parentObj->stackPointer;
+ $parentObj->FHalfCarry = (($parentObj->registersHL & 0xFFF) + ($parentObj->stackPointer & 0xFFF) > 0xFFF);
+ $parentObj->FCarry = ($dirtySum > 0xFFFF);
+ $parentObj->registersHL = ($dirtySum & 0xFFFF);
+ $parentObj->FSubtract = false;
+ };
+ // LDD A, (HL)
+ //#0x3A:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $parentObj->registersHL = $parentObj->unswtuw($parentObj->registersHL - 1);
+ };
+ //DEC SP
+ //#0x3B:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ };
+ //INC A
+ //#0x3C:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = (($parentObj->registerA + 1) & 0xFF);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) == 0);
+ $parentObj->FSubtract = false;
+ };
+ //DEC A
+ //#0x3D:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = $parentObj->unsbtub($parentObj->registerA - 1);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) == 0xF);
+ $parentObj->FSubtract = true;
+ };
+ //LD A, n
+ //#0x3E:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ };
+ //CCF
+ //#0x3F:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FCarry = !$parentObj->FCarry;
+ $parentObj->FSubtract = $parentObj->FHalfCarry = false;
+ };
+ //LD B, B
+ //#0x40:
+ $this->functionsArray[] = function ($parentObj) {
+ //Do nothing...
+ };
+ //LD B, C
+ //#0x41:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB = $parentObj->registerC;
+ };
+ //LD B, D
+ //#0x42:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB = $parentObj->registerD;
+ };
+ //LD B, E
+ //#0x43:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB = $parentObj->registerE;
+ };
+ //LD B, H
+ //#0x44:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB = ($parentObj->registersHL >> 8);
+ };
+ //LD B, L
+ //#0x45:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB = ($parentObj->registersHL & 0xFF);
+ };
+ //LD B, (HL)
+ //#0x46:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ };
+ //LD B, A
+ //#0x47:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerB = $parentObj->registerA;
+ };
+ //LD C, B
+ //#0x48:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC = $parentObj->registerB;
+ };
+ //LD C, C
+ //#0x49:
+ $this->functionsArray[] = function ($parentObj) {
+ //Do nothing...
+ };
+ //LD C, D
+ //#0x4A:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC = $parentObj->registerD;
+ };
+ //LD C, E
+ //#0x4B:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC = $parentObj->registerE;
+ };
+ //LD C, H
+ //#0x4C:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC = ($parentObj->registersHL >> 8);
+ };
+ //LD C, L
+ //#0x4D:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC = ($parentObj->registersHL & 0xFF);
+ };
+ //LD C, (HL)
+ //#0x4E:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ };
+ //LD C, A
+ //#0x4F:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC = $parentObj->registerA;
+ };
+ //LD D, B
+ //#0x50:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD = $parentObj->registerB;
+ };
+ //LD D, C
+ //#0x51:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD = $parentObj->registerC;
+ };
+ //LD D, D
+ //#0x52:
+ $this->functionsArray[] = function ($parentObj) {
+ //Do nothing...
+ };
+ //LD D, E
+ //#0x53:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD = $parentObj->registerE;
+ };
+ //LD D, H
+ //#0x54:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD = ($parentObj->registersHL >> 8);
+ };
+ //LD D, L
+ //#0x55:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD = ($parentObj->registersHL & 0xFF);
+ };
+ //LD D, (HL)
+ //#0x56:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ };
+ //LD D, A
+ //#0x57:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerD = $parentObj->registerA;
+ };
+ //LD E, B
+ //#0x58:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE = $parentObj->registerB;
+ };
+ //LD E, C
+ //#0x59:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE = $parentObj->registerC;
+ };
+ //LD E, D
+ //#0x5A:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE = $parentObj->registerD;
+ };
+ //LD E, E
+ //#0x5B:
+ $this->functionsArray[] = function ($parentObj) {
+ //Do nothing...
+ };
+ //LD E, H
+ //#0x5C:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE = ($parentObj->registersHL >> 8);
+ };
+ //LD E, L
+ //#0x5D:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE = ($parentObj->registersHL & 0xFF);
+ };
+ //LD E, (HL)
+ //#0x5E:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ };
+ //LD E, A
+ //#0x5F:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE = $parentObj->registerA;
+ };
+ //LD H, B
+ //#0x60:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->registerB << 8) + ($parentObj->registersHL & 0xFF);
+ };
+ //LD H, C
+ //#0x61:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->registerC << 8) + ($parentObj->registersHL & 0xFF);
+ };
+ //LD H, D
+ //#0x62:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->registerD << 8) + ($parentObj->registersHL & 0xFF);
+ };
+ //LD H, E
+ //#0x63:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->registerE << 8) + ($parentObj->registersHL & 0xFF);
+ };
+ //LD H, H
+ //#0x64:
+ $this->functionsArray[] = function ($parentObj) {
+ //Do nothing...
+ };
+ //LD H, L
+ //#0x65:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = (($parentObj->registersHL & 0xFF) << 8) + ($parentObj->registersHL & 0xFF);
+ };
+ //LD H, (HL)
+ //#0x66:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL) << 8) + ($parentObj->registersHL & 0xFF);
+ };
+ //LD H, A
+ //#0x67:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->registerA << 8) + ($parentObj->registersHL & 0xFF);
+ };
+ //LD L, B
+ //#0x68:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + $parentObj->registerB;
+ };
+ //LD L, C
+ //#0x69:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + $parentObj->registerC;
+ };
+ //LD L, D
+ //#0x6A:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + $parentObj->registerD;
+ };
+ //LD L, E
+ //#0x6B:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + $parentObj->registerE;
+ };
+ //LD L, H
+ //#0x6C:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + ($parentObj->registersHL >> 8);
+ };
+ //LD L, L
+ //#0x6D:
+ $this->functionsArray[] = function ($parentObj) {
+ //Do nothing...
+ };
+ //LD L, (HL)
+ //#0x6E:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ };
+ //LD L, A
+ //#0x6F:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->registersHL & 0xFF00) + $parentObj->registerA;
+ };
+ //LD (HL), B
+ //#0x70:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->registerB);
+ };
+ //LD (HL), C
+ //#0x71:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->registerC);
+ };
+ //LD (HL), D
+ //#0x72:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->registerD);
+ };
+ //LD (HL), E
+ //#0x73:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->registerE);
+ };
+ //LD (HL), H
+ //#0x74:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, ($parentObj->registersHL >> 8));
+ };
+ //LD (HL), L
+ //#0x75:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, ($parentObj->registersHL & 0xFF));
+ };
+ //HALT
+ //#0x76:
+ $this->functionsArray[] = function ($parentObj) {
+ if ($parentObj->untilEnable == 1) {
+ /*VBA-M says this fixes Torpedo Range (Seems to work):
+ Involves an edge case where an EI is placed right before a HALT.
+ EI in this case actually is immediate, so we adjust (Hacky?).*/
+ $parentObj->programCounter = $parentObj->nswtuw($parentObj->programCounter - 1);
+ }
+ else {
+ if (!$parentObj->halt && !$parentObj->IME && !$parentObj->cGBC && !$parentObj->usedBootROM && ($parentObj->memory[0xFF0F] & $parentObj->memory[0xFFFF] & 0x1F) > 0) {
+ $parentObj->skipPCIncrement = true;
+ }
+ $parentObj->halt = true;
+ while ($parentObj->halt && ($parentObj->stopEmulator & 1) == 0) {
+ /*We're hijacking the main interpreter loop to do this dirty business
+ in order to not slow down the main interpreter loop code with halt state handling.*/
+ $bitShift = 0;
+ $testbit = 1;
+ $interrupts = $parentObj->memory[0xFFFF] & $parentObj->memory[0xFF0F];
+ while ($bitShift < 5) {
+ //Check to see if an interrupt is enabled AND requested.
+ if (($testbit & $interrupts) == $testbit) {
+ $parentObj->halt = false; //Get out of halt state if in halt state.
+ return; //Let the main interrupt handler compute the interrupt.
+ }
+ $testbit = 1 << ++$bitShift;
+ }
+ $parentObj->CPUTicks = 1; //1 machine cycle under HALT...
+ //Timing:
+ $parentObj->updateCore();
+ }
+
+ throw new \Exception("HALT_OVERRUN");
+
+ // throw(new Error("HALT_OVERRUN")); //Throw an error on purpose to exit out of the loop.
+ }
+ };
+ //LD (HL), A
+ //#0x77:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite($parentObj->registersHL, $parentObj->registerA);
+ };
+ //LD A, B
+ //#0x78:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = $parentObj->registerB;
+ };
+ //LD A, C
+ //#0x79:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = $parentObj->registerC;
+ };
+ //LD A, D
+ //#0x7A:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = $parentObj->registerD;
+ };
+ //LD A, E
+ //#0x7B:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = $parentObj->registerE;
+ };
+ //LD A, H
+ //#0x7C:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = ($parentObj->registersHL >> 8);
+ };
+ //LD A, L
+ //#0x7D:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = ($parentObj->registersHL & 0xFF);
+ };
+ //LD, A, (HL)
+ //#0x7E:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ };
+ //LD A, A
+ //#0x7F:
+ $this->functionsArray[] = function ($parentObj) {
+ //Do Nothing...
+ };
+ //ADD A, B
+ //#0x80:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA + $parentObj->registerB;
+ $parentObj->FHalfCarry = ($dirtySum & 0xF) < ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADD A, C
+ //#0x81:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA + $parentObj->registerC;
+ $parentObj->FHalfCarry = ($dirtySum & 0xF) < ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADD A, D
+ //#0x82:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA + $parentObj->registerD;
+ $parentObj->FHalfCarry = ($dirtySum & 0xF) < ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADD A, E
+ //#0x83:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA + $parentObj->registerE;
+ $parentObj->FHalfCarry = ($dirtySum & 0xF) < ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADD A, H
+ //#0x84:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA + ($parentObj->registersHL >> 8);
+ $parentObj->FHalfCarry = ($dirtySum & 0xF) < ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADD A, L
+ //#0x85:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA + ($parentObj->registersHL & 0xFF);
+ $parentObj->FHalfCarry = ($dirtySum & 0xF) < ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADD A, (HL)
+ //#0x86:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA + $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $parentObj->FHalfCarry = ($dirtySum & 0xF) < ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADD A, A
+ //#0x87:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA * 2;
+ $parentObj->FHalfCarry = ($dirtySum & 0xF) < ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADC A, B
+ //#0x88:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA + $parentObj->registerB + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) + ($parentObj->registerB & 0xF) + (($parentObj->FCarry) ? 1 : 0) > 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADC A, C
+ //#0x89:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA + $parentObj->registerC + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) + ($parentObj->registerC & 0xF) + (($parentObj->FCarry) ? 1 : 0) > 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADC A, D
+ //#0x8A:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA + $parentObj->registerD + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) + ($parentObj->registerD & 0xF) + (($parentObj->FCarry) ? 1 : 0) > 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADC A, E
+ //#0x8B:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA + $parentObj->registerE + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) + ($parentObj->registerE & 0xF) + (($parentObj->FCarry) ? 1 : 0) > 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADC A, H
+ //#0x8C:
+ $this->functionsArray[] = function ($parentObj) {
+ $tempValue = ($parentObj->registersHL >> 8);
+ $dirtySum = $parentObj->registerA + $tempValue + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) + ($tempValue & 0xF) + (($parentObj->FCarry) ? 1 : 0) > 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADC A, L
+ //#0x8D:
+ $this->functionsArray[] = function ($parentObj) {
+ $tempValue = ($parentObj->registersHL & 0xFF);
+ $dirtySum = $parentObj->registerA + $tempValue + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) + ($tempValue & 0xF) + (($parentObj->FCarry) ? 1 : 0) > 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADC A, (HL)
+ //#0x8E:
+ $this->functionsArray[] = function ($parentObj) {
+ $tempValue = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $dirtySum = $parentObj->registerA + $tempValue + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) + ($tempValue & 0xF) + (($parentObj->FCarry) ? 1 : 0) > 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //ADC A, A
+ //#0x8F:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = ($parentObj->registerA * 2) + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) + ($parentObj->registerA & 0xF) + (($parentObj->FCarry) ? 1 : 0) > 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ };
+ //SUB A, B
+ //#0x90:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - $parentObj->registerB;
+ $parentObj->FHalfCarry = ($parentObj->registerA & 0xF) < ($parentObj->registerB & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //SUB A, C
+ //#0x91:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - $parentObj->registerC;
+ $parentObj->FHalfCarry = ($parentObj->registerA & 0xF) < ($parentObj->registerC & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //SUB A, D
+ //#0x92:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - $parentObj->registerD;
+ $parentObj->FHalfCarry = ($parentObj->registerA & 0xF) < ($parentObj->registerD & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //SUB A, E
+ //#0x93:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - $parentObj->registerE;
+ $parentObj->FHalfCarry = ($parentObj->registerA & 0xF) < ($parentObj->registerE & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //SUB A, H
+ //#0x94:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->registersHL >> 8;
+ $dirtySum = $parentObj->registerA - $temp_var;
+ $parentObj->FHalfCarry = ($parentObj->registerA & 0xF) < ($temp_var & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //SUB A, L
+ //#0x95:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - ($parentObj->registersHL & 0xFF);
+ $parentObj->FHalfCarry = ($parentObj->registerA & 0xF) < ($parentObj->registersHL & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //SUB A, (HL)
+ //#0x96:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $dirtySum = $parentObj->registerA - $temp_var;
+ $parentObj->FHalfCarry = ($parentObj->registerA & 0xF) < ($temp_var & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //SUB A, A
+ //#0x97:
+ $this->functionsArray[] = function ($parentObj) {
+ //number - same number == 0
+ $parentObj->registerA = 0;
+ $parentObj->FHalfCarry = $parentObj->FCarry = false;
+ $parentObj->FZero = $parentObj->FSubtract = true;
+ };
+ //SBC A, B
+ //#0x98:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - $parentObj->registerB - (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) - ($parentObj->registerB & 0xF) - (($parentObj->FCarry) ? 1 : 0) < 0);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //SBC A, C
+ //#0x99:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - $parentObj->registerC - (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) - ($parentObj->registerC & 0xF) - (($parentObj->FCarry) ? 1 : 0) < 0);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //SBC A, D
+ //#0x9A:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - $parentObj->registerD - (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) - ($parentObj->registerD & 0xF) - (($parentObj->FCarry) ? 1 : 0) < 0);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //SBC A, E
+ //#0x9B:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - $parentObj->registerE - (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) - ($parentObj->registerE & 0xF) - (($parentObj->FCarry) ? 1 : 0) < 0);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //SBC A, H
+ //#0x9C:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->registersHL >> 8;
+ $dirtySum = $parentObj->registerA - $temp_var - (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) - ($temp_var & 0xF) - (($parentObj->FCarry) ? 1 : 0) < 0);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //SBC A, L
+ //#0x9D:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - ($parentObj->registersHL & 0xFF) - (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) - ($parentObj->registersHL & 0xF) - (($parentObj->FCarry) ? 1 : 0) < 0);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //SBC A, (HL)
+ //#0x9E:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $dirtySum = $parentObj->registerA - $temp_var - (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) - ($temp_var & 0xF) - (($parentObj->FCarry) ? 1 : 0) < 0);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //SBC A, A
+ //#0x9F:
+ $this->functionsArray[] = function ($parentObj) {
+ //Optimized SBC A:
+ if ($parentObj->FCarry) {
+ $parentObj->FZero = false;
+ $parentObj->FSubtract = $parentObj->FHalfCarry = $parentObj->FCarry = true;
+ $parentObj->registerA = 0xFF;
+ }
+ else {
+ $parentObj->FHalfCarry = $parentObj->FCarry = false;
+ $parentObj->FSubtract = $parentObj->FZero = true;
+ $parentObj->registerA = 0;
+ }
+ };
+ //AND B
+ //#0xA0:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= $parentObj->registerB;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = $parentObj->FCarry = false;
+ };
+ //AND C
+ //#0xA1:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= $parentObj->registerC;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = $parentObj->FCarry = false;
+ };
+ //AND D
+ //#0xA2:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= $parentObj->registerD;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = $parentObj->FCarry = false;
+ };
+ //AND E
+ //#0xA3:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= $parentObj->registerE;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = $parentObj->FCarry = false;
+ };
+ //AND H
+ //#0xA4:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= ($parentObj->registersHL >> 8);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = $parentObj->FCarry = false;
+ };
+ //AND L
+ //#0xA5:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= ($parentObj->registersHL & 0xFF);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = $parentObj->FCarry = false;
+ };
+ //AND (HL)
+ //#0xA6:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = $parentObj->FCarry = false;
+ };
+ //AND A
+ //#0xA7:
+ $this->functionsArray[] = function ($parentObj) {
+ //number & same number = same number
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = $parentObj->FCarry = false;
+ };
+ //XOR B
+ //#0xA8:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA ^= $parentObj->registerB;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FHalfCarry = $parentObj->FCarry = false;
+ };
+ //XOR C
+ //#0xA9:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA ^= $parentObj->registerC;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FHalfCarry = $parentObj->FCarry = false;
+ };
+ //XOR D
+ //#0xAA:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA ^= $parentObj->registerD;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FHalfCarry = $parentObj->FCarry = false;
+ };
+ //XOR E
+ //#0xAB:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA ^= $parentObj->registerE;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FHalfCarry = $parentObj->FCarry = false;
+ };
+ //XOR H
+ //#0xAC:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA ^= ($parentObj->registersHL >> 8);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FHalfCarry = $parentObj->FCarry = false;
+ };
+ //XOR L
+ //#0xAD:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA ^= ($parentObj->registersHL & 0xFF);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FHalfCarry = $parentObj->FCarry = false;
+ };
+ //XOR (HL)
+ //#0xAE:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA ^= $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FHalfCarry = $parentObj->FCarry = false;
+ };
+ //XOR A
+ //#0xAF:
+ $this->functionsArray[] = function ($parentObj) {
+ //number ^ same number == 0
+ $parentObj->registerA = 0;
+ $parentObj->FZero = true;
+ $parentObj->FSubtract = $parentObj->FHalfCarry = $parentObj->FCarry = false;
+ };
+ //OR B
+ //#0xB0:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= $parentObj->registerB;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FCarry = $parentObj->FHalfCarry = false;
+ };
+ //OR C
+ //#0xB1:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= $parentObj->registerC;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FCarry = $parentObj->FHalfCarry = false;
+ };
+ //OR D
+ //#0xB2:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= $parentObj->registerD;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FCarry = $parentObj->FHalfCarry = false;
+ };
+ //OR E
+ //#0xB3:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= $parentObj->registerE;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FCarry = $parentObj->FHalfCarry = false;
+ };
+ //OR H
+ //#0xB4:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= ($parentObj->registersHL >> 8);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FCarry = $parentObj->FHalfCarry = false;
+ };
+ //OR L
+ //#0xB5:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= ($parentObj->registersHL & 0xFF);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FCarry = $parentObj->FHalfCarry = false;
+ };
+ //OR (HL)
+ //#0xB6:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FCarry = $parentObj->FHalfCarry = false;
+ };
+ //OR A
+ //#0xB7:
+ $this->functionsArray[] = function ($parentObj) {
+ //number | same number == same number
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = $parentObj->FCarry = $parentObj->FHalfCarry = false;
+ };
+ //CP B
+ //#0xB8:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - $parentObj->registerB;
+ $parentObj->FHalfCarry = ($parentObj->unsbtub($dirtySum) & 0xF) > ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->FZero = ($dirtySum == 0);
+ $parentObj->FSubtract = true;
+ };
+ //CP C
+ //#0xB9:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - $parentObj->registerC;
+ $parentObj->FHalfCarry = ($parentObj->unsbtub($dirtySum) & 0xF) > ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->FZero = ($dirtySum == 0);
+ $parentObj->FSubtract = true;
+ };
+ //CP D
+ //#0xBA:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - $parentObj->registerD;
+ $parentObj->FHalfCarry = ($parentObj->unsbtub($dirtySum) & 0xF) > ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->FZero = ($dirtySum == 0);
+ $parentObj->FSubtract = true;
+ };
+ //CP E
+ //#0xBB:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - $parentObj->registerE;
+ $parentObj->FHalfCarry = ($parentObj->unsbtub($dirtySum) & 0xF) > ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->FZero = ($dirtySum == 0);
+ $parentObj->FSubtract = true;
+ };
+ //CP H
+ //#0xBC:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - ($parentObj->registersHL >> 8);
+ $parentObj->FHalfCarry = ($parentObj->unsbtub($dirtySum) & 0xF) > ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->FZero = ($dirtySum == 0);
+ $parentObj->FSubtract = true;
+ };
+ //CP L
+ //#0xBD:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - ($parentObj->registersHL & 0xFF);
+ $parentObj->FHalfCarry = ($parentObj->unsbtub($dirtySum) & 0xF) > ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->FZero = ($dirtySum == 0);
+ $parentObj->FSubtract = true;
+ };
+ //CP (HL)
+ //#0xBE:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - $parentObj->memoryReader[$parentObj->registersHL]($parentObj, $parentObj->registersHL);
+ $parentObj->FHalfCarry = ($parentObj->unsbtub($dirtySum) & 0xF) > ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->FZero = ($dirtySum == 0);
+ $parentObj->FSubtract = true;
+ };
+ //CP A
+ //#0xBF:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->FHalfCarry = $parentObj->FCarry = false;
+ $parentObj->FZero = $parentObj->FSubtract = true;
+ };
+ //RET !FZ
+ //#0xC0:
+ $this->functionsArray[] = function ($parentObj) {
+ if (!$parentObj->FZero) {
+ $parentObj->programCounter = ($parentObj->memoryRead(($parentObj->stackPointer + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->stackPointer]($parentObj, $parentObj->stackPointer);
+ $parentObj->stackPointer = ($parentObj->stackPointer + 2) & 0xFFFF;
+ $parentObj->CPUTicks += 3;
+ }
+ };
+ //POP BC
+ //#0xC1:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerC = $parentObj->memoryReader[$parentObj->stackPointer]($parentObj, $parentObj->stackPointer);
+ $parentObj->registerB = $parentObj->memoryRead(($parentObj->stackPointer + 1) & 0xFFFF);
+ $parentObj->stackPointer = ($parentObj->stackPointer + 2) & 0xFFFF;
+ };
+ //JP !FZ, nn
+ //#0xC2:
+ $this->functionsArray[] = function ($parentObj) {
+ if (!$parentObj->FZero) {
+ $parentObj->programCounter = ($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->CPUTicks++;
+ }
+ else {
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ }
+ };
+ //JP nn
+ //#0xC3:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->programCounter = ($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ };
+ //CALL !FZ, nn
+ //#0xC4:
+ $this->functionsArray[] = function ($parentObj) {
+ if (!$parentObj->FZero) {
+ $temp_pc = ($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter >> 8);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter & 0xFF);
+ $parentObj->programCounter = $temp_pc;
+ $parentObj->CPUTicks += 3;
+ }
+ else {
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ }
+ };
+ //PUSH BC
+ //#0xC5:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->registerB);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->registerC);
+ };
+ //ADD, n
+ //#0xC6:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->FHalfCarry = ($dirtySum & 0xF) < ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ };
+ //RST 0
+ //#0xC7:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter >> 8);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter & 0xFF);
+ $parentObj->programCounter = 0;
+ };
+ //RET FZ
+ //#0xC8:
+ $this->functionsArray[] = function ($parentObj) {
+ if ($parentObj->FZero) {
+ $parentObj->programCounter = ($parentObj->memoryRead(($parentObj->stackPointer + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->stackPointer]($parentObj, $parentObj->stackPointer);
+ $parentObj->stackPointer = ($parentObj->stackPointer + 2) & 0xFFFF;
+ $parentObj->CPUTicks += 3;
+ }
+ };
+ //RET
+ //#0xC9:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->programCounter = ($parentObj->memoryRead(($parentObj->stackPointer + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->stackPointer]($parentObj, $parentObj->stackPointer);
+ $parentObj->stackPointer = ($parentObj->stackPointer + 2) & 0xFFFF;
+ };
+ //JP FZ, nn
+ //#0xCA:
+ $this->functionsArray[] = function ($parentObj) {
+ if ($parentObj->FZero) {
+ $parentObj->programCounter = ($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->CPUTicks++;
+ }
+ else {
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ }
+ };
+ //Secondary OP Code Set:
+ //#0xCB:
+ $this->functionsArray[] = function ($parentObj) {
+ $opcode = $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ //Increment the program counter to the next instruction:
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ //Get how many CPU cycles the current 0xCBXX op code counts for:
+ $parentObj->CPUTicks = $parentObj->SecondaryTICKTable[$opcode];
+ //Execute secondary OP codes for the 0xCB OP code call.
+ $parentObj->CBOPCODE[$opcode]($parentObj);
+ };
+ //CALL FZ, nn
+ //#0xCC:
+ $this->functionsArray[] = function ($parentObj) {
+ if ($parentObj->FZero) {
+ $temp_pc = ($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter >> 8);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter & 0xFF);
+ $parentObj->programCounter = $temp_pc;
+ $parentObj->CPUTicks += 3;
+ }
+ else {
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ }
+ };
+ //CALL nn
+ //#0xCD:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_pc = ($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter >> 8);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter & 0xFF);
+ $parentObj->programCounter = $temp_pc;
+ };
+ //ADC A, n
+ //#0xCE:
+ $this->functionsArray[] = function ($parentObj) {
+ $tempValue = $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $dirtySum = $parentObj->registerA + $tempValue + (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) + ($tempValue & 0xF) + (($parentObj->FCarry) ? 1 : 0) > 0xF);
+ $parentObj->FCarry = ($dirtySum > 0xFF);
+ $parentObj->registerA = $dirtySum & 0xFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = false;
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ };
+ //RST 0x8
+ //#0xCF:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter >> 8);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter & 0xFF);
+ $parentObj->programCounter = 0x8;
+ };
+ //RET !FC
+ //#0xD0:
+ $this->functionsArray[] = function ($parentObj) {
+ if (!$parentObj->FCarry) {
+ $parentObj->programCounter = ($parentObj->memoryRead(($parentObj->stackPointer + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->stackPointer]($parentObj, $parentObj->stackPointer);
+ $parentObj->stackPointer = ($parentObj->stackPointer + 2) & 0xFFFF;
+ $parentObj->CPUTicks += 3;
+ }
+ };
+ //POP DE
+ //#0xD1:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerE = $parentObj->memoryReader[$parentObj->stackPointer]($parentObj, $parentObj->stackPointer);
+ $parentObj->registerD = $parentObj->memoryRead(($parentObj->stackPointer + 1) & 0xFFFF);
+ $parentObj->stackPointer = ($parentObj->stackPointer + 2) & 0xFFFF;
+ };
+ //JP !FC, nn
+ //#0xD2:
+ $this->functionsArray[] = function ($parentObj) {
+ if (!$parentObj->FCarry) {
+ $parentObj->programCounter = ($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->CPUTicks++;
+ }
+ else {
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ }
+ };
+ //0xD3 - Illegal
+ //#0xD3:
+ $this->functionsArray[] = function ($parentObj) {
+ // @TODO
+ // cout("Illegal op code 0xD3 called, pausing emulation.", 2);
+ // pause();
+ };
+ //CALL !FC, nn
+ //#0xD4:
+ $this->functionsArray[] = function ($parentObj) {
+ if (!$parentObj->FCarry) {
+ $temp_pc = ($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter >> 8);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter & 0xFF);
+ $parentObj->programCounter = $temp_pc;
+ $parentObj->CPUTicks += 3;
+ }
+ else {
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ }
+ };
+ //PUSH DE
+ //#0xD5:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->registerD);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->registerE);
+ };
+ //SUB A, n
+ //#0xD6:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $dirtySum = $parentObj->registerA - $temp_var;
+ $parentObj->FHalfCarry = ($parentObj->registerA & 0xF) < ($temp_var & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //RST 0x10
+ //#0xD7:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter >> 8);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter & 0xFF);
+ $parentObj->programCounter = 0x10;
+ };
+ //RET FC
+ //#0xD8:
+ $this->functionsArray[] = function ($parentObj) {
+ if ($parentObj->FCarry) {
+ $parentObj->programCounter = ($parentObj->memoryRead(($parentObj->stackPointer + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->stackPointer]($parentObj, $parentObj->stackPointer);
+ $parentObj->stackPointer = ($parentObj->stackPointer + 2) & 0xFFFF;
+ $parentObj->CPUTicks += 3;
+ }
+ };
+ //RETI
+ //#0xD9:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->programCounter = ($parentObj->memoryRead(($parentObj->stackPointer + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->stackPointer]($parentObj, $parentObj->stackPointer);
+ $parentObj->stackPointer = ($parentObj->stackPointer + 2) & 0xFFFF;
+ //$parentObj->IME = true;
+ $parentObj->untilEnable = 2;
+ };
+ //JP FC, nn
+ //#0xDA:
+ $this->functionsArray[] = function ($parentObj) {
+ if ($parentObj->FCarry) {
+ $parentObj->programCounter = ($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->CPUTicks++;
+ }
+ else {
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ }
+ };
+ //0xDB - Illegal
+ //#0xDB:
+ $this->functionsArray[] = function ($parentObj) {
+ // @TODO
+ // cout("Illegal op code 0xDB called, pausing emulation.", 2);
+ // pause();
+ };
+ //CALL FC, nn
+ //#0xDC:
+ $this->functionsArray[] = function ($parentObj) {
+ if ($parentObj->FCarry) {
+ $temp_pc = ($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter >> 8);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter & 0xFF);
+ $parentObj->programCounter = $temp_pc;
+ $parentObj->CPUTicks += 3;
+ }
+ else {
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ }
+ };
+ //0xDD - Illegal
+ //#0xDD:
+ $this->functionsArray[] = function ($parentObj) {
+ // @TODO
+ // cout("Illegal op code 0xDD called, pausing emulation.", 2);
+ // pause();
+ };
+ //SBC A, n
+ //#0xDE:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $dirtySum = $parentObj->registerA - $temp_var - (($parentObj->FCarry) ? 1 : 0);
+ $parentObj->FHalfCarry = (($parentObj->registerA & 0xF) - ($temp_var & 0xF) - (($parentObj->FCarry) ? 1 : 0) < 0);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->registerA = $parentObj->unsbtub($dirtySum);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FSubtract = true;
+ };
+ //RST 0x18
+ //#0xDF:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter >> 8);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter & 0xFF);
+ $parentObj->programCounter = 0x18;
+ };
+ //LDH (n), A
+ //#0xE0:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite(0xFF00 + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter), $parentObj->registerA);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ };
+ //POP HL
+ //#0xE1:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registersHL = ($parentObj->memoryRead(($parentObj->stackPointer + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->stackPointer]($parentObj, $parentObj->stackPointer);
+ $parentObj->stackPointer = ($parentObj->stackPointer + 2) & 0xFFFF;
+ };
+ //LD (C), A
+ //#0xE2:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite(0xFF00 + $parentObj->registerC, $parentObj->registerA);
+ };
+ //0xE3 - Illegal
+ //#0xE3:
+ $this->functionsArray[] = function ($parentObj) {
+ // @TODO
+ // cout("Illegal op code 0xE3 called, pausing emulation.", 2);
+ // pause();
+ };
+ //0xE4 - Illegal
+ //#0xE4:
+ $this->functionsArray[] = function ($parentObj) {
+ // @TODO
+ // cout("Illegal op code 0xE4 called, pausing emulation.", 2);
+ // pause();
+ };
+ //PUSH HL
+ //#0xE5:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->registersHL >> 8);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->registersHL & 0xFF);
+ };
+ //AND n
+ //#0xE6:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA &= $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->FHalfCarry = true;
+ $parentObj->FSubtract = $parentObj->FCarry = false;
+ };
+ //RST 0x20
+ //#0xE7:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter >> 8);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter & 0xFF);
+ $parentObj->programCounter = 0x20;
+ };
+ //ADD SP, n
+ //#0xE8:
+ $this->functionsArray[] = function ($parentObj) {
+ $signedByte = $parentObj->usbtsb($parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter));
+ $temp_value = $parentObj->nswtuw($parentObj->stackPointer + $signedByte);
+ $parentObj->FCarry = ((($parentObj->stackPointer ^ $signedByte ^ $temp_value) & 0x100) == 0x100);
+ $parentObj->FHalfCarry = ((($parentObj->stackPointer ^ $signedByte ^ $temp_value) & 0x10) == 0x10);
+ $parentObj->stackPointer = $temp_value;
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ $parentObj->FZero = $parentObj->FSubtract = false;
+ };
+ //JP, (HL)
+ //#0xE9:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->programCounter = $parentObj->registersHL;
+ };
+ //LD n, A
+ //#0xEA:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->memoryWrite(($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter), $parentObj->registerA);
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ };
+ //0xEB - Illegal
+ //#0xEB:
+ $this->functionsArray[] = function ($parentObj) {
+ // @TODO
+ // cout("Illegal op code 0xEB called, pausing emulation.", 2);
+ // pause();
+ };
+ //0xEC - Illegal
+ //#0xEC:
+ $this->functionsArray[] = function ($parentObj) {
+ // @TODO
+ // cout("Illegal op code 0xEC called, pausing emulation.", 2);
+ // pause();
+ };
+ //0xED - Illegal
+ //#0xED:
+ $this->functionsArray[] = function ($parentObj) {
+ // @TODO
+ // cout("Illegal op code 0xED called, pausing emulation.", 2);
+ // pause();
+ };
+ //XOR n
+ //#0xEE:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA ^= $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ $parentObj->FSubtract = $parentObj->FHalfCarry = $parentObj->FCarry = false;
+ };
+ //RST 0x28
+ //#0xEF:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter >> 8);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter & 0xFF);
+ $parentObj->programCounter = 0x28;
+ };
+ //LDH A, (n)
+ //#0xF0:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = $parentObj->memoryRead(0xFF00 + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter));
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ };
+ //POP AF
+ //#0xF1:
+ $this->functionsArray[] = function ($parentObj) {
+ $temp_var = $parentObj->memoryReader[$parentObj->stackPointer]($parentObj, $parentObj->stackPointer);
+ $parentObj->FZero = (($temp_var & 0x80) == 0x80);
+ $parentObj->FSubtract = (($temp_var & 0x40) == 0x40);
+ $parentObj->FHalfCarry = (($temp_var & 0x20) == 0x20);
+ $parentObj->FCarry = (($temp_var & 0x10) == 0x10);
+ $parentObj->registerA = $parentObj->memoryRead(($parentObj->stackPointer + 1) & 0xFFFF);
+ $parentObj->stackPointer = ($parentObj->stackPointer + 2) & 0xFFFF;
+ };
+ //LD A, (C)
+ //#0xF2:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = $parentObj->memoryRead(0xFF00 + $parentObj->registerC);
+ };
+ //DI
+ //#0xF3:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->IME = false;
+ $parentObj->untilEnable = 0;
+ };
+ //0xF4 - Illegal
+ //#0xF4:
+ $this->functionsArray[] = function ($parentObj) {
+ // @TODO
+ // cout("Illegal op code 0xF4 called, pausing emulation.", 2);
+ // pause();
+ };
+ //PUSH AF
+ //#0xF5:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->registerA);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, (($parentObj->FZero) ? 0x80 : 0) + (($parentObj->FSubtract) ? 0x40 : 0) + (($parentObj->FHalfCarry) ? 0x20 : 0) + (($parentObj->FCarry) ? 0x10 : 0));
+ };
+ //OR n
+ //#0xF6:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA |= $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->FZero = ($parentObj->registerA == 0);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ $parentObj->FSubtract = $parentObj->FCarry = $parentObj->FHalfCarry = false;
+ };
+ //RST 0x30
+ //#0xF7:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter >> 8);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter & 0xFF);
+ $parentObj->programCounter = 0x30;
+ };
+ //LDHL SP, n
+ //#0xF8:
+ $this->functionsArray[] = function ($parentObj) {
+ $signedByte = $parentObj->usbtsb($parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter));
+ $parentObj->registersHL = $parentObj->nswtuw($parentObj->stackPointer + $signedByte);
+ $parentObj->FCarry = ((($parentObj->stackPointer ^ $signedByte ^ $parentObj->registersHL) & 0x100) == 0x100);
+ $parentObj->FHalfCarry = ((($parentObj->stackPointer ^ $signedByte ^ $parentObj->registersHL) & 0x10) == 0x10);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ $parentObj->FZero = $parentObj->FSubtract = false;
+ };
+ //LD SP, HL
+ //#0xF9:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = $parentObj->registersHL;
+ };
+ //LD A, (nn)
+ //#0xFA:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->registerA = $parentObj->memoryRead(($parentObj->memoryRead(($parentObj->programCounter + 1) & 0xFFFF) << 8) + $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter));
+ $parentObj->programCounter = ($parentObj->programCounter + 2) & 0xFFFF;
+ };
+ //EI
+ //#0xFB:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->untilEnable = 2;
+ };
+ //0xFC - Illegal
+ //#0xFC:
+ $this->functionsArray[] = function ($parentObj) {
+ // @TODO
+ // cout("Illegal op code 0xFC called, pausing emulation.", 2);
+ // pause();
+ };
+ //0xFD - Illegal
+ //#0xFD:
+ $this->functionsArray[] = function ($parentObj) {
+ // @TODO
+ // cout("Illegal op code 0xFD called, pausing emulation.", 2);
+ // pause();
+ };
+ //CP n
+ //#0xFE:
+ $this->functionsArray[] = function ($parentObj) {
+ $dirtySum = $parentObj->registerA - $parentObj->memoryReader[$parentObj->programCounter]($parentObj, $parentObj->programCounter);
+ $parentObj->FHalfCarry = ($parentObj->unsbtub($dirtySum) & 0xF) > ($parentObj->registerA & 0xF);
+ $parentObj->FCarry = ($dirtySum < 0);
+ $parentObj->FZero = ($dirtySum == 0);
+ $parentObj->programCounter = ($parentObj->programCounter + 1) & 0xFFFF;
+ $parentObj->FSubtract = true;
+ };
+ //RST 0x38
+ //#0xFF:
+ $this->functionsArray[] = function ($parentObj) {
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter >> 8);
+ $parentObj->stackPointer = $parentObj->unswtuw($parentObj->stackPointer - 1);
+ $parentObj->memoryWrite($parentObj->stackPointer, $parentObj->programCounter & 0xFF);
+ $parentObj->programCounter = 0x38;
+ };
+ }
+
+ public function get()
+ {
+ return $this->functionsArray;
+ }
+} \ No newline at end of file
diff --git a/src/Gameboy/Settings.php b/src/Gameboy/Settings.php
new file mode 100644
index 0000000..36e64a7
--- /dev/null
+++ b/src/Gameboy/Settings.php
@@ -0,0 +1,77 @@
+<?php
+namespace GameBoy;
+
+class Settings
+{
+ //Some settings.
+ public static $settings = [
+ //Turn on sound.
+ false,
+
+ //Force Mono sound.
+ false,
+
+ //Give priority to GameBoy mode
+ true,
+
+ //Keyboard button map.
+ [39, 37, 38, 40, 88, 90, 16, 13],
+
+ //Frameskip Amount (Auto frameskip setting allows the script to change this.)
+ 0,
+
+ //Use the data URI BMP method over the canvas tag method?
+ false,
+
+ //How many tiles in each direction when using the BMP method (width * height).
+ [16, 12],
+
+ //Auto Frame Skip
+ true,
+
+ //Maximum Frame Skip
+ 29,
+
+ //Override to allow for MBC1 instead of ROM only (compatibility for broken 3rd-party cartridges).
+ true,
+
+ //Override MBC RAM disabling and always allow reading and writing to the banks.
+ true,
+
+ //Audio granularity setting (Sampling of audio every x many machine cycles)
+ 20,
+
+ //Frameskip base factor
+ 10,
+
+ //Target number of machine cycles per loop. (4,194,300 / 1000 * 17)
+ 17826,
+
+ //Sample Rate
+ 70000,
+
+ //How many bits per WAV PCM sample (For browsers that fall back to WAV PCM generation)
+ 0x10,
+
+ //Use the GBC BIOS?
+ false,
+
+ //Colorize GB mode?
+ false,
+
+ //Sample size for webkit audio.
+ 512,
+
+ //Whether to display the canvas at 144x160 on fullscreen or as stretched.
+ false,
+
+ //Interval for the emulator loop.
+ 17,
+
+ //Render nearest-neighbor scaling in javascript?
+ false,
+
+ //Disallow typed arrays?
+ false
+ ];
+} \ No newline at end of file
diff --git a/src/Gameboy/TickTables.php b/src/Gameboy/TickTables.php
new file mode 100644
index 0000000..d6b0824
--- /dev/null
+++ b/src/Gameboy/TickTables.php
@@ -0,0 +1,55 @@
+<?php
+namespace GameBoy;
+
+class TickTables
+{
+ public static $primary = [
+ //Number of machine cycles for each instruction:
+ /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F*/
+ 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, //0
+ 1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, //1
+ 2, 3, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, //2
+ 2, 3, 2, 2, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 1, //3
+
+ 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, //4
+ 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, //5
+ 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, //6
+ 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, //7
+
+ 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, //8
+ 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, //9
+ 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, //A
+ 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, //B
+
+ 2, 3, 3, 4, 3, 4, 2, 4, 2, 4, 3, 2, 3, 6, 2, 4, //C
+ 2, 3, 3, 1, 3, 4, 2, 4, 2, 4, 3, 1, 3, 1, 2, 4, //D
+ 3, 3, 2, 1, 1, 4, 2, 4, 4, 1, 4, 1, 1, 1, 2, 4, //E
+ 3, 3, 2, 1, 1, 4, 2, 4, 3, 2, 4, 1, 0, 1, 2, 4 //F
+
+ ];
+
+ public static $secondary = [
+ //Number of machine cycles for each 0xCBXX instruction:
+ /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F*/
+ 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, //0
+ 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, //1
+ 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, //2
+ 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, //3
+
+ 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, //4
+ 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, //5
+ 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, //6
+ 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, //7
+
+ 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, //8
+ 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, //9
+ 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, //A
+ 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, //B
+
+ 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, //C
+ 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, //D
+ 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, //E
+ 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2 //F
+ ];
+}
+
diff --git a/teste.txt b/teste.txt
new file mode 100644
index 0000000..45e5939
--- /dev/null
+++ b/teste.txt
Binary files differ
diff --git a/vendor/autoload.php b/vendor/autoload.php
new file mode 100644
index 0000000..e2e5709
--- /dev/null
+++ b/vendor/autoload.php
@@ -0,0 +1,7 @@
+<?php
+
+// autoload.php @generated by Composer
+
+require_once __DIR__ . '/composer' . '/autoload_real.php';
+
+return ComposerAutoloaderInitb66537925b69007c00fd3869eccc298b::getLoader();
diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php
new file mode 100644
index 0000000..ff6ecfb
--- /dev/null
+++ b/vendor/composer/ClassLoader.php
@@ -0,0 +1,413 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ * Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @see http://www.php-fig.org/psr/psr-0/
+ * @see http://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ // PSR-4
+ private $prefixLengthsPsr4 = array();
+ private $prefixDirsPsr4 = array();
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ private $prefixesPsr0 = array();
+ private $fallbackDirsPsr0 = array();
+
+ private $useIncludePath = false;
+ private $classMap = array();
+
+ private $classMapAuthoritative = false;
+
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
+ }
+
+ return array();
+ }
+
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array $classMap Class to filename map
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ (array) $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ (array) $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 base directories
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return bool|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ includeFile($file);
+
+ return true;
+ }
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
+ if ('\\' == $class[0]) {
+ $class = substr($class, 1);
+ }
+
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative) {
+ return false;
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if ($file === null && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if ($file === null) {
+ // Remember that this class does not exist.
+ return $this->classMap[$class] = false;
+ }
+
+ return $file;
+ }
+
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+ }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+ include $file;
+}
diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE
new file mode 100644
index 0000000..c8d57af
--- /dev/null
+++ b/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) 2015 Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
new file mode 100644
index 0000000..7a91153
--- /dev/null
+++ b/vendor/composer/autoload_classmap.php
@@ -0,0 +1,9 @@
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+);
diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php
new file mode 100644
index 0000000..e6d077a
--- /dev/null
+++ b/vendor/composer/autoload_namespaces.php
@@ -0,0 +1,11 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+ 'GameBoy' => array($baseDir . '/src'),
+ 'Drawille' => array($vendorDir . '/whatthejeff/drawille/src'),
+);
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
new file mode 100644
index 0000000..b265c64
--- /dev/null
+++ b/vendor/composer/autoload_psr4.php
@@ -0,0 +1,9 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+);
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
new file mode 100644
index 0000000..f25776e
--- /dev/null
+++ b/vendor/composer/autoload_real.php
@@ -0,0 +1,45 @@
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInitb66537925b69007c00fd3869eccc298b
+{
+ private static $loader;
+
+ public static function loadClassLoader($class)
+ {
+ if ('Composer\Autoload\ClassLoader' === $class) {
+ require __DIR__ . '/ClassLoader.php';
+ }
+ }
+
+ public static function getLoader()
+ {
+ if (null !== self::$loader) {
+ return self::$loader;
+ }
+
+ spl_autoload_register(array('ComposerAutoloaderInitb66537925b69007c00fd3869eccc298b', 'loadClassLoader'), true, true);
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
+ spl_autoload_unregister(array('ComposerAutoloaderInitb66537925b69007c00fd3869eccc298b', 'loadClassLoader'));
+
+ $map = require __DIR__ . '/autoload_namespaces.php';
+ foreach ($map as $namespace => $path) {
+ $loader->set($namespace, $path);
+ }
+
+ $map = require __DIR__ . '/autoload_psr4.php';
+ foreach ($map as $namespace => $path) {
+ $loader->setPsr4($namespace, $path);
+ }
+
+ $classMap = require __DIR__ . '/autoload_classmap.php';
+ if ($classMap) {
+ $loader->addClassMap($classMap);
+ }
+
+ $loader->register(true);
+
+ return $loader;
+ }
+}
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
new file mode 100644
index 0000000..7f62f30
--- /dev/null
+++ b/vendor/composer/installed.json
@@ -0,0 +1,56 @@
+[
+ {
+ "name": "whatthejeff/drawille",
+ "version": "v1.0.1",
+ "version_normalized": "1.0.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/whatthejeff/php-drawille.git",
+ "reference": "bafea427f5bf2445413f6807000a95f70cc83bfd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/whatthejeff/php-drawille/zipball/bafea427f5bf2445413f6807000a95f70cc83bfd",
+ "reference": "bafea427f5bf2445413f6807000a95f70cc83bfd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.1.*"
+ },
+ "time": "2014-05-26 13:45:31",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-0": {
+ "Drawille": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ }
+ ],
+ "description": "Terminal drawing with braille",
+ "homepage": "http://github.com/whatthejeff/php-drawille",
+ "keywords": [
+ "Turtle",
+ "braille",
+ "console",
+ "drawing",
+ "terminal"
+ ]
+ }
+]
diff --git a/vendor/whatthejeff/drawille/.gitignore b/vendor/whatthejeff/drawille/.gitignore
new file mode 100644
index 0000000..c776adf
--- /dev/null
+++ b/vendor/whatthejeff/drawille/.gitignore
@@ -0,0 +1,4 @@
+composer.lock
+composer.phar
+phpunit.xml
+vendor \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/.travis.yml b/vendor/whatthejeff/drawille/.travis.yml
new file mode 100644
index 0000000..dc2a600
--- /dev/null
+++ b/vendor/whatthejeff/drawille/.travis.yml
@@ -0,0 +1,12 @@
+language: php
+
+php:
+ - 5.4
+ - 5.5
+ - 5.6
+ - hhvm
+
+before_script:
+ - composer install --prefer-source
+
+script: vendor/bin/phpunit \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/ChangeLog.md b/vendor/whatthejeff/drawille/ChangeLog.md
new file mode 100644
index 0000000..5076a92
--- /dev/null
+++ b/vendor/whatthejeff/drawille/ChangeLog.md
@@ -0,0 +1,13 @@
+ChangeLog
+=========
+
+1.0.1
+-----
+
+* Fixed notice.
+* Added more examples.
+
+1.0.0
+-----
+
+* It begins! \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/LICENSE b/vendor/whatthejeff/drawille/LICENSE
new file mode 100644
index 0000000..96938ca
--- /dev/null
+++ b/vendor/whatthejeff/drawille/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Jeff Welch
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/README.md b/vendor/whatthejeff/drawille/README.md
new file mode 100644
index 0000000..57b3c88
--- /dev/null
+++ b/vendor/whatthejeff/drawille/README.md
@@ -0,0 +1,76 @@
+php-drawille
+============
+
+Terminal drawing with [braille](http://en.wikipedia.org/wiki/Braille).
+
+![Octocat example](docs/images/octocat.png)
+
+## Requirements
+
+php-drawille requires PHP 5.4.0 or later.
+
+## Installation
+
+The recommended way to install php-drawille is [through
+composer](http://getcomposer.org). Just create a `composer.json` file and
+run the `php composer.phar install` command to install it:
+
+~~~json
+{
+ "require": {
+ "whatthejeff/drawille": "~1.0"
+ }
+}
+~~~
+
+## Usage
+
+~~~php
+use Drawille\Canvas;
+
+$canvas = new Canvas();
+
+for($x = 0; $x <= 1800; $x += 10) {
+ $canvas->set($x / 10, 10 + sin($x * M_PI / 180) * 10);
+}
+
+echo $canvas->frame(), "\n";
+~~~
+
+![Usage example](docs/images/sin.png)
+
+~~~php
+use Drawille\Turtle;
+
+$turtle = new Turtle();
+
+for($x = 0; $x < 36; $x++) {
+ $turtle->right(10);
+
+ for($y = 0; $y < 36; $y++) {
+ $turtle->right(10);
+ $turtle->forward(8);
+ }
+}
+
+echo $turtle->frame(), "\n";
+~~~
+
+![Turtle example](docs/images/turtle.png)
+
+## Tests
+
+[![Build Status](https://travis-ci.org/whatthejeff/php-drawille.png?branch=master)](https://travis-ci.org/whatthejeff/php-drawille)
+
+To run the test suite, you need [composer](http://getcomposer.org).
+
+ $ php composer.phar install
+ $ vendor/bin/phpunit
+
+## Acknowledgements
+
+php-drawille is a port of [drawille](https://github.com/asciimoo/drawille).
+
+## License
+
+php-drawille is licensed under the [MIT license](LICENSE). \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/composer.json b/vendor/whatthejeff/drawille/composer.json
new file mode 100644
index 0000000..f07bf1a
--- /dev/null
+++ b/vendor/whatthejeff/drawille/composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "whatthejeff/drawille",
+ "description": "Terminal drawing with braille",
+ "keywords": ["terminal","console","braille","drawing","turtle"],
+ "homepage": "http://github.com/whatthejeff/php-drawille",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.1.*"
+ },
+ "autoload": {
+ "psr-0": {
+ "Drawille": "src/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ }
+} \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/docs/images/octocat.png b/vendor/whatthejeff/drawille/docs/images/octocat.png
new file mode 100644
index 0000000..af8d6ec
--- /dev/null
+++ b/vendor/whatthejeff/drawille/docs/images/octocat.png
Binary files differ
diff --git a/vendor/whatthejeff/drawille/docs/images/sin.png b/vendor/whatthejeff/drawille/docs/images/sin.png
new file mode 100644
index 0000000..22262bd
--- /dev/null
+++ b/vendor/whatthejeff/drawille/docs/images/sin.png
Binary files differ
diff --git a/vendor/whatthejeff/drawille/docs/images/turtle.png b/vendor/whatthejeff/drawille/docs/images/turtle.png
new file mode 100644
index 0000000..8ae6822
--- /dev/null
+++ b/vendor/whatthejeff/drawille/docs/images/turtle.png
Binary files differ
diff --git a/vendor/whatthejeff/drawille/examples/ImagePrinter.php b/vendor/whatthejeff/drawille/examples/ImagePrinter.php
new file mode 100644
index 0000000..d01ed67
--- /dev/null
+++ b/vendor/whatthejeff/drawille/examples/ImagePrinter.php
@@ -0,0 +1,76 @@
+<?php
+
+/*
+ * This file is part of php-drawille
+ *
+ * (c) Jeff Welch <whatthejeff@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Imagine\Gd\Imagine;
+use Imagine\Image\Box;
+use Imagine\Image\Point;
+
+use Drawille\Canvas;
+
+class ImagePrinter
+{
+ private $image;
+ private $threshold;
+ private $ratio;
+ private $invert;
+
+ public function __construct($image, $threshold = 385.2, $ratio = null, $invert = false) {
+ $this->image = $image;
+ $this->threshold = (float) $threshold;
+ $this->ratio = $ratio;
+ $this->invert = $invert;
+ }
+
+ public function run($terminalWidth, $terminalHeight) {
+ $imagine = new Imagine();
+ $image = $imagine->open($this->image);
+
+ $size = $image->getSize();
+ $width = $size->getWidth();
+ $height = $size->getHeight();
+
+ if ($this->ratio) {
+ $ratio = (float) $this->ratio;
+ $width = floor($width * $ratio);
+ $height = floor($height * $ratio);
+ $image->resize(new Box($width, $height));
+ }
+
+ else {
+ $height_ratio = $terminalHeight * 4 / $height;
+ $width_ratio = $terminalWidth * 2 / $width;
+ $ratio = min($height_ratio, $width_ratio);
+
+ if ($ratio < 1.0) {
+ $width = floor($width * $ratio);
+ $height = floor($height * $ratio);
+ $image->resize(new Box($width, $height));
+ }
+ }
+
+ $canvas = new Canvas();
+
+ for ($y = 0; $y < $height; $y++) {
+ for ($x = 0; $x < $width; $x++) {
+ $color = $image->getColorAt(new Point($x, $y));
+ $total = $color->getRed() + $color->getGreen() + $color->getBlue();
+
+ if (!$this->invert ^ $total > $this->threshold) {
+ $canvas->set($x, $y);
+ }
+ }
+ }
+
+ echo $canvas->frame(), "\n";
+ }
+}
+
+?> \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/examples/basic.php b/vendor/whatthejeff/drawille/examples/basic.php
new file mode 100644
index 0000000..a4835c9
--- /dev/null
+++ b/vendor/whatthejeff/drawille/examples/basic.php
@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of php-drawille
+ *
+ * (c) Jeff Welch <whatthejeff@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+require_once __DIR__ . '/../vendor/autoload.php';
+
+use Drawille\Canvas;
+
+$canvas = new Canvas();
+
+for ($x = 0; $x <= 1800; $x++) {
+ $canvas->set($x / 10, sin($x * M_PI / 180) * 10);
+}
+
+echo $canvas->frame(), "\n";
+$canvas->clear();
+
+for ($x = 0; $x <= 1800; $x += 10) {
+ $canvas->set($x / 10, 10 + sin($x * M_PI / 180) * 10);
+ $canvas->set($x / 10, 10 + cos($x * M_PI / 180) * 10);
+}
+
+echo $canvas->frame(), "\n";
+$canvas->clear();
+
+for ($x = 0; $x <= 3600; $x += 20) {
+ $canvas->set($x / 20, 4 + sin($x * M_PI / 180) * 4);
+}
+
+echo $canvas->frame(), "\n";
+$canvas->clear();
+
+for ($x = 0; $x <= 360; $x += 4) {
+ $canvas->set($x / 4, 30 + sin($x * M_PI / 180) * 30);
+}
+
+for ($x = 0; $x <= 30; $x++) {
+ for ($y = 0; $y <= 30; $y++) {
+ $canvas->set($x, $y);
+ $canvas->toggle($x+30, $y+30);
+ $canvas->toggle($x+60, $y);
+ }
+}
+
+echo $canvas->frame(), "\n"; \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/examples/composer.json b/vendor/whatthejeff/drawille/examples/composer.json
new file mode 100644
index 0000000..ec57a4e
--- /dev/null
+++ b/vendor/whatthejeff/drawille/examples/composer.json
@@ -0,0 +1,31 @@
+{
+ "name": "whatthejeff/drawille-examples",
+ "description": "drawille examples",
+ "keywords": ["terminal","console","braille","drawing","xkcd","octocat","image"],
+ "homepage": "http://github.com/whatthejeff/drawille",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.4.0",
+ "imagine/imagine": "~0.5.0",
+ "symfony/console": "~2.4",
+ "fabpot/Goutte": "~2.0",
+ "whatthejeff/drawille": "~1.0",
+ "ext-gd": "*"
+ },
+ "autoload": {
+ "psr-0": {
+ "ImagePrinter": ""
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ }
+}
diff --git a/vendor/whatthejeff/drawille/examples/img2term.php b/vendor/whatthejeff/drawille/examples/img2term.php
new file mode 100755
index 0000000..a2eba35
--- /dev/null
+++ b/vendor/whatthejeff/drawille/examples/img2term.php
@@ -0,0 +1,92 @@
+#!/usr/bin/env php
+<?php
+
+/*
+ * This file is part of php-drawille
+ *
+ * (c) Jeff Welch <whatthejeff@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+use Symfony\Component\Console\Application as ConsoleApplication;
+use Symfony\Component\Console\Command\Command as ConsoleCommand;
+
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputDefinition;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Application extends ConsoleApplication
+{
+ public function __construct() {
+ parent::__construct(basename(__FILE__), '1.0');
+ $this->add(new Command);
+ }
+
+ protected function getCommandName(InputInterface $input) {
+ return basename(__FILE__);
+ }
+
+ protected function getDefaultInputDefinition()
+ {
+ return new InputDefinition(array(
+ new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'),
+ new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version.')
+ ));
+ }
+}
+
+class Command extends ConsoleCommand
+{
+ protected function configure()
+ {
+ $this->setName(basename(__FILE__))
+ ->setDescription('convert an image to terminal')
+ ->addArgument(
+ 'image',
+ InputArgument::REQUIRED,
+ 'Image file path/url'
+ )
+ ->addOption(
+ 'threshold',
+ 't',
+ InputOption::VALUE_REQUIRED,
+ 'Color threshold',
+ 382.5
+ )
+ ->addOption(
+ 'ratio',
+ 'r',
+ InputOption::VALUE_REQUIRED,
+ 'Image resize ratio'
+ )
+ ->addOption(
+ 'invert',
+ 'i',
+ InputOption::VALUE_NONE,
+ 'Invert colors'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ list($terminalWidth, $terminalHeight) = $this->getApplication()->getTerminalDimensions();
+
+ $printer = new ImagePrinter(
+ $input->getArgument('image'),
+ $input->getOption('threshold'),
+ $input->getOption('ratio'),
+ $input->getOption('invert')
+ );
+
+ $printer->run($terminalWidth, $terminalHeight);
+ }
+}
+
+$console = new Application();
+$console->run(); \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/examples/octocat2term.php b/vendor/whatthejeff/drawille/examples/octocat2term.php
new file mode 100755
index 0000000..19cfd5b
--- /dev/null
+++ b/vendor/whatthejeff/drawille/examples/octocat2term.php
@@ -0,0 +1,125 @@
+#!/usr/bin/env php
+<?php
+
+/*
+ * This file is part of php-drawille
+ *
+ * (c) Jeff Welch <whatthejeff@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+use Symfony\Component\Console\Application as ConsoleApplication;
+use Symfony\Component\Console\Command\Command as ConsoleCommand;
+
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputDefinition;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+use Goutte\Client;
+
+class Application extends ConsoleApplication
+{
+ public function __construct() {
+ parent::__construct(basename(__FILE__), '1.0');
+ $this->add(new Command);
+ }
+
+ protected function getCommandName(InputInterface $input) {
+ return basename(__FILE__);
+ }
+
+ protected function getDefaultInputDefinition()
+ {
+ return new InputDefinition(array(
+ new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'),
+ new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version.')
+ ));
+ }
+}
+
+class Command extends ConsoleCommand
+{
+ protected function configure()
+ {
+ $this->setName(basename(__FILE__))
+ ->setDescription('convert an octocat to terminal')
+ ->addArgument(
+ 'cat',
+ InputArgument::REQUIRED,
+ 'Cat number, name, title, or "random"'
+ )
+ ->addOption(
+ 'threshold',
+ 't',
+ InputOption::VALUE_REQUIRED,
+ 'Color threshold',
+ 382.5
+ )
+ ->addOption(
+ 'ratio',
+ 'r',
+ InputOption::VALUE_REQUIRED,
+ 'Image resize ratio'
+ )
+ ->addOption(
+ 'invert',
+ 'i',
+ InputOption::VALUE_NONE,
+ 'Invert colors'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $url = 'https://octodex.github.com';
+ $cat = $input->getArgument('cat');
+
+ $client = new Client();
+ $crawler = $client->request('GET', $url);
+
+ try {
+ if (is_numeric($cat) || $cat == 'random') {
+ $filter = $crawler->filter('.preview-image > img');
+ $total = iterator_count($filter);
+
+ if($cat == 'random') {
+ $cat = mt_rand(1, $total);
+ }
+
+ $image = $filter->eq($total - $cat)->attr('data-src');
+ }
+
+ else if (substr($cat, 0, 4) == 'the ') {
+ $image = $crawler->filter("img[alt=\"$cat\"]")->attr('data-src');
+ }
+
+ else {
+ $image = $crawler->filter("a[href=\"/$cat\"] > img")->attr('data-src');
+ }
+ }
+
+ catch (InvalidArgumentException $exception) {
+ throw new RuntimeException('Octocat not found at: ' . $url);
+ }
+
+ list($terminalWidth, $terminalHeight) = $this->getApplication()->getTerminalDimensions();
+
+ $printer = new ImagePrinter(
+ 'https://octodex.github.com' . $image,
+ $input->getOption('threshold'),
+ $input->getOption('ratio'),
+ $input->getOption('invert')
+ );
+
+ $printer->run($terminalWidth, $terminalHeight);
+ }
+}
+
+$console = new Application();
+$console->run(); \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/examples/turtle.php b/vendor/whatthejeff/drawille/examples/turtle.php
new file mode 100644
index 0000000..4f51642
--- /dev/null
+++ b/vendor/whatthejeff/drawille/examples/turtle.php
@@ -0,0 +1,27 @@
+<?php
+
+/*
+ * This file is part of php-drawille
+ *
+ * (c) Jeff Welch <whatthejeff@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+require_once __DIR__ . '/../vendor/autoload.php';
+
+use Drawille\Turtle;
+
+$turtle = new Turtle();
+
+for ($x = 0; $x < 36; $x++) {
+ $turtle->right(10);
+
+ for ($y = 0; $y < 36; $y++) {
+ $turtle->right(10);
+ $turtle->forward(8);
+ }
+}
+
+echo $turtle->frame(), "\n"; \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/examples/xkcd2term.php b/vendor/whatthejeff/drawille/examples/xkcd2term.php
new file mode 100755
index 0000000..bbd9d82
--- /dev/null
+++ b/vendor/whatthejeff/drawille/examples/xkcd2term.php
@@ -0,0 +1,108 @@
+#!/usr/bin/env php
+<?php
+
+/*
+ * This file is part of php-drawille
+ *
+ * (c) Jeff Welch <whatthejeff@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+use Symfony\Component\Console\Application as ConsoleApplication;
+use Symfony\Component\Console\Command\Command as ConsoleCommand;
+
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputDefinition;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+use Goutte\Client;
+
+class Application extends ConsoleApplication
+{
+ public function __construct() {
+ parent::__construct(basename(__FILE__), '1.0');
+ $this->add(new Command);
+ }
+
+ protected function getCommandName(InputInterface $input) {
+ return basename(__FILE__);
+ }
+
+ protected function getDefaultInputDefinition()
+ {
+ return new InputDefinition(array(
+ new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'),
+ new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version.')
+ ));
+ }
+}
+
+class Command extends ConsoleCommand
+{
+ protected function configure()
+ {
+ $this->setName(basename(__FILE__))
+ ->setDescription('convert an xkcd comic to terminal')
+ ->addArgument(
+ 'comic',
+ InputArgument::REQUIRED,
+ 'Comic ID or "random"'
+ )
+ ->addOption(
+ 'threshold',
+ 't',
+ InputOption::VALUE_REQUIRED,
+ 'Color threshold',
+ 382.5
+ )
+ ->addOption(
+ 'ratio',
+ 'r',
+ InputOption::VALUE_REQUIRED,
+ 'Image resize ratio'
+ )
+ ->addOption(
+ 'invert',
+ 'i',
+ InputOption::VALUE_NONE,
+ 'Invert colors'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $comic = $input->getArgument('comic');
+ $url = $comic == 'random' ? 'http://c.xkcd.com/random/comic/' : "http://xkcd.com/$comic/";
+
+ $client = new Client();
+ $crawler = $client->request('GET', $url);
+
+ try {
+ $image = $crawler->filter('#comic > img')->attr('src');
+ }
+
+ catch(InvalidArgumentException $exception) {
+ throw new RuntimeException('No comic found on: ' . $url);
+ }
+
+ list($terminalWidth, $terminalHeight) = $this->getApplication()->getTerminalDimensions();
+
+ $printer = new ImagePrinter(
+ $image,
+ $input->getOption('threshold'),
+ $input->getOption('ratio'),
+ $input->getOption('invert')
+ );
+
+ $printer->run($terminalWidth, $terminalHeight);
+ }
+}
+
+$console = new Application();
+$console->run(); \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/phpunit.xml.dist b/vendor/whatthejeff/drawille/phpunit.xml.dist
new file mode 100644
index 0000000..37729ba
--- /dev/null
+++ b/vendor/whatthejeff/drawille/phpunit.xml.dist
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
+ backupGlobals="false"
+ verbose="true">
+ <testsuite name="Drawille">
+ <directory suffix="Test.php">tests</directory>
+ </testsuite>
+</phpunit>
diff --git a/vendor/whatthejeff/drawille/src/Drawille/Canvas.php b/vendor/whatthejeff/drawille/src/Drawille/Canvas.php
new file mode 100644
index 0000000..0defc86
--- /dev/null
+++ b/vendor/whatthejeff/drawille/src/Drawille/Canvas.php
@@ -0,0 +1,237 @@
+<?php
+
+/*
+ * This file is part of php-drawille
+ *
+ * (c) Jeff Welch <whatthejeff@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Drawille;
+
+/**
+ * Pixel surface
+ *
+ * @author Jeff Welch <whatthejeff@gmail.com>
+ */
+class Canvas
+{
+ /**
+ * Dots:
+ *
+ * ,___,
+ * |1 4|
+ * |2 5|
+ * |3 6|
+ * |7 8|
+ * `````
+ *
+ * @var array
+ * @see http://www.alanwood.net/unicode/braille_patterns.html
+ */
+ private static $pixel_map = [
+ [0x01, 0x08],
+ [0x02, 0x10],
+ [0x04, 0x20],
+ [0x40, 0x80]
+ ];
+
+ /**
+ * Braille characters starts at 0x2800
+ *
+ * @var integer
+ */
+ private static $braille_char_offset = 0x2800;
+
+ /**
+ * Canvas representation
+ *
+ * @var array
+ */
+ private $chars = [];
+
+ /**
+ * Constructor
+ */
+ public function __construct() {
+ $this->clear();
+ }
+
+ /**
+ * Clears the canvas
+ */
+ public function clear() {
+ $this->chars = [];
+ }
+
+ /**
+ * Sets a pixel at the given position
+ *
+ * @param integer $x x position
+ * @param integer $y y position
+ */
+ public function set($x, $y) {
+ list($x, $y, $px, $py) = $this->prime($x, $y);
+ $this->chars[$py][$px] |= $this->getDotFromMap($x, $y);
+ }
+
+ /**
+ * Unsets a pixel at the given position
+ *
+ * @param integer $x x position
+ * @param integer $y y position
+ */
+ public function reset($x, $y) {
+ list($x, $y, $px, $py) = $this->prime($x, $y);
+ $this->chars[$py][$px] &= ~$this->getDotFromMap($x, $y);
+ }
+
+ /**
+ * Gets the pixel state at a given position
+ *
+ * @param integer $x x position
+ * @param integer $y y position
+ *
+ * @return bool the pixel state
+ */
+ public function get($x, $y) {
+ list($x, $y, , , $char) = $this->prime($x, $y);
+ return (bool)($char & $this->getDotFromMap($x, $y));
+ }
+
+ /**
+ * Toggles the pixel state on/off at a given position
+ *
+ * @param integer $x x position
+ * @param integer $y y position
+ */
+ public function toggle($x, $y) {
+ $this->get($x, $y) ? $this->reset($x, $y) : $this->set($x, $y);
+ }
+
+ /**
+ * Gets a line
+ *
+ * @param integer $y y position
+ * @param array $options options
+ *
+ * @return string line
+ */
+ public function row($y, array $options = []) {
+ $row = isset($this->chars[$y]) ? $this->chars[$y] : [];
+
+ if(!isset($options['min_x']) || !isset($options['max_x'])) {
+ if(!($keys = array_keys($row))) {
+ return '';
+ }
+ }
+
+ $min = isset($options['min_x']) ? $options['min_x'] : min($keys);
+ $max = isset($options['max_x']) ? $options['max_x'] : max($keys);
+
+ return array_reduce(range($min, $max), function ($carry, $item) use ($row) {
+ return $carry .= $this->toBraille(isset($row[$item]) ? $row[$item] : 0);
+ }, '');
+ }
+
+ /**
+ * Gets all lines
+ *
+ * @param array $options options
+ *
+ * @return array line
+ */
+ public function rows(array $options = []) {
+ if(!isset($options['min_y']) || !isset($options['max_y'])) {
+ if(!($keys = array_keys($this->chars))) {
+ return [];
+ }
+ }
+
+ $min = isset($options['min_y']) ? $options['min_y'] : min($keys);
+ $max = isset($options['max_y']) ? $options['max_y'] : max($keys);
+
+ if(!isset($options['min_x']) || !isset($options['max_x'])) {
+ $flattened = array();
+ foreach($this->chars as $key => $char) {
+ $flattened = array_merge($flattened, array_keys($char));
+ }
+ }
+
+ $options['min_x'] = isset($options['min_x']) ? $options['min_x'] : min($flattened);
+ $options['max_x'] = isset($options['max_x']) ? $options['max_x'] : max($flattened);
+
+ return array_map(function ($i) use ($options) {
+ return $this->row($i, $options);
+ }, range($min, $max));
+ }
+
+ /**
+ * Gets a string representation of the canvas
+ *
+ * @param array $options options
+ *
+ * @return string representation
+ */
+ public function frame(array $options = []) {
+ return join("\n", $this->rows($options));
+ }
+
+ /**
+ * Gets the canvas representation.
+ *
+ * @return array characters
+ */
+ public function getChars() {
+ return $this->chars;
+ }
+
+ /**
+ * Gets a braille unicode character
+ *
+ * @param integer $code character code
+ *
+ * @return string braille
+ */
+ private function toBraille($code) {
+ return html_entity_decode('&#' . (self::$braille_char_offset + $code) . ';', ENT_NOQUOTES, 'UTF-8');
+ }
+
+ /**
+ * Gets a dot from the pixel map.
+ *
+ * @param integer $x x position
+ * @param integer $y y position
+ *
+ * @return integer dot
+ */
+ private function getDotFromMap($x, $y) {
+ $y = $y % 4;
+ $x = $x % 2;
+
+ return self::$pixel_map[$y < 0 ? 4 + $y : $y][$x < 0 ? 2 + $x : $x];
+ }
+
+ /**
+ * Autovivification for a canvas position.
+ *
+ * @param integer $x x position
+ * @param integer $y y position
+ *
+ * @return array
+ */
+ private function prime($x, $y) {
+ $x = round($x);
+ $y = round($y);
+ $px = floor($x / 2);
+ $py = floor($y / 4);
+
+ if(!isset($this->chars[$py][$px])) {
+ $this->chars[$py][$px] = 0;
+ }
+
+ return [$x, $y, $px, $py, $this->chars[$py][$px]];
+ }
+} \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/src/Drawille/Turtle.php b/vendor/whatthejeff/drawille/src/Drawille/Turtle.php
new file mode 100644
index 0000000..542d593
--- /dev/null
+++ b/vendor/whatthejeff/drawille/src/Drawille/Turtle.php
@@ -0,0 +1,242 @@
+<?php
+
+/*
+ * This file is part of php-drawille
+ *
+ * (c) Jeff Welch <whatthejeff@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Drawille;
+
+/**
+ * Basic turtle graphics interface
+ *
+ * @author Jeff Welch <whatthejeff@gmail.com>
+ * @see http://en.wikipedia.org/wiki/Turtle_graphics
+ */
+class Turtle extends Canvas
+{
+ /**
+ * Current x position
+ *
+ * @var integer
+ */
+ private $x = 0;
+ /**
+ * Current y position
+ *
+ * @var integer
+ */
+ private $y = 0;
+ /**
+ * Current canvas rotation
+ *
+ * @var integer
+ */
+ private $rotation = 0;
+
+ /**
+ * If the pen is up
+ *
+ * @var boolean
+ */
+ private $up = false;
+
+ /**
+ * Constructor
+ *
+ * @param int $y starting x position
+ * @param int $y starting y position
+ */
+ public function __construct($x = 0, $y = 0) {
+ parent::__construct();
+
+ $this->x = $x;
+ $this->y = $y;
+ }
+
+ /**
+ * Gets the current x position.
+ *
+ * @return integer x position
+ */
+ public function getX() {
+ return $this->x;
+ }
+
+ /**
+ * Gets the current y position.
+ *
+ * @return integer y position
+ */
+ public function getY() {
+ return $this->y;
+ }
+
+ /**
+ * Gets the current canvas rotation
+ *
+ * @return integer current canvas rotation
+ */
+ public function getRotation() {
+ return $this->rotation;
+ }
+
+ /**
+ * Push the pen down
+ */
+ public function down() {
+ $this->up = false;
+ }
+
+ /**
+ * Pull the pen up
+ */
+ public function up() {
+ $this->up = true;
+ }
+
+ /**
+ * Move the pen forward
+ *
+ * @param integer $length distance to move forward
+ */
+ public function forward($length) {
+ $theta = $this->rotation / 180.0 * M_PI;
+ $x = $this->x + $length * cos($theta);
+ $y = $this->y + $length * sin($theta);
+
+ $this->move($x, $y);
+ }
+
+ /**
+ * Move the pen backwards
+ *
+ * @param integer $length distance to move backwards
+ */
+ public function back($length) {
+ $this->forward(-$length);
+ }
+
+ /**
+ * Angle the canvas to the right.
+ *
+ * @param integer $angle degree to angle
+ */
+ public function right($angle) {
+ $this->rotation += $angle;
+ }
+
+ /**
+ * Angle the canvas to the left.
+ *
+ * @param integer $angle degree to angle
+ */
+ public function left($angle) {
+ $this->rotation -= $angle;
+ }
+
+ /**
+ * Move the pen, drawing if the pen is down.
+ *
+ * @param int $y new x position
+ * @param int $y new y position
+ */
+ public function move($x, $y) {
+ if(!$this->up) {
+ $x1 = round($this->x);
+ $y1 = round($this->y);
+ $x2 = $x;
+ $y2 = $y;
+
+ $xdiff = max($x1, $x2) - min($x1, $x2);
+ $ydiff = max($y1, $y2) - min($y1, $y2);
+
+ $xdir = $x1 <= $x2 ? 1 : -1;
+ $ydir = $y1 <= $y2 ? 1 : -1;
+
+ $r = max($xdiff, $ydiff);
+
+ for($i = 0; $i <= $r; $i++) {
+ $x = $x1;
+ $y = $y1;
+
+ if ($ydiff > 0) {
+ $y += ((float)$i*$ydiff)/$r*$ydir;
+ }
+
+ if($xdiff > 0) {
+ $x += ((float)$i*$xdiff)/$r*$xdir;
+ }
+
+ $this->set($x, $y);
+ }
+ }
+
+ $this->x = $x;
+ $this->y = $y;
+ }
+
+ /**
+ * Pull the pen up
+ */
+ public function pu() {
+ $this->up();
+ }
+
+ /**
+ * Push the pen up
+ */
+ public function pd() {
+ $this->down();
+ }
+
+ /**
+ * Move the pen forward
+ *
+ * @param integer $length distance to move forward
+ */
+ public function fd($length) {
+ $this->forward($length);
+ }
+
+ /**
+ * Move the pen, drawing if the pen is down.
+ *
+ * @param int $y new x position
+ * @param int $y new y position
+ */
+ public function mv($x, $y) {
+ $this->move($x, $y);
+ }
+
+ /**
+ * Angle the canvas to the right.
+ *
+ * @param integer $angle degree to angle
+ */
+ public function rt($angle) {
+ $this->right($angle);
+ }
+
+ /**
+ * Angle the canvas to the left.
+ *
+ * @param integer $angle degree to angle
+ */
+ public function lt($angle) {
+ $this->left($angle);
+ }
+
+ /**
+ * Move the pen backwards
+ *
+ * @param integer $length distance to move backwards
+ */
+ public function bk($length) {
+ $this->back($length);
+ }
+} \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/tests/Drawille/CanvasTest.php b/vendor/whatthejeff/drawille/tests/Drawille/CanvasTest.php
new file mode 100644
index 0000000..8ab9537
--- /dev/null
+++ b/vendor/whatthejeff/drawille/tests/Drawille/CanvasTest.php
@@ -0,0 +1,75 @@
+<?php
+
+/*
+ * This file is part of php-drawille
+ *
+ * (c) Jeff Welch <whatthejeff@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Drawille;
+
+class CanvasTest extends \PHPUnit_Framework_TestCase
+{
+ private $canvas;
+
+ protected function setUp() {
+ $this->canvas = new Canvas();
+ }
+
+ public function testSet() {
+ $this->canvas->set(0, 0);
+ $this->assertEquals([[1]], $this->canvas->getChars());
+ }
+
+ /**
+ * @depends testSet
+ */
+ public function testReset() {
+ $this->canvas->set(0, 0);
+ $this->canvas->reset(0, 0);
+ $this->assertEquals([[0]], $this->canvas->getChars());
+ }
+
+ /**
+ * @depends testSet
+ */
+ public function testClear() {
+ $this->canvas->set(0, 0);
+ $this->canvas->clear();
+ $this->assertEmpty($this->canvas->getChars());
+ }
+
+ public function testToggle() {
+ $this->canvas->toggle(0, 0);
+ $this->assertEquals([[1]], $this->canvas->getChars());
+
+ $this->canvas->toggle(0, 0);
+ $this->assertEquals([[0]], $this->canvas->getChars());
+ }
+
+ /**
+ * @depends testSet
+ */
+ public function testFrame() {
+ $this->assertEquals($this->canvas->frame(), '');
+
+ $this->canvas->set(0, 0);
+ $this->assertEquals($this->canvas->frame(), '⠁');
+ }
+
+ /**
+ * @depends testSet
+ */
+ public function testGet() {
+ $this->assertFalse($this->canvas->get(0, 0));
+ $this->canvas->set(0, 0);
+
+ $this->assertTrue($this->canvas->get(0, 0));
+ $this->assertFalse($this->canvas->get(1, 0));
+ $this->assertFalse($this->canvas->get(0, 1));
+ $this->assertFalse($this->canvas->get(1, 1));
+ }
+} \ No newline at end of file
diff --git a/vendor/whatthejeff/drawille/tests/Drawille/TurtleTest.php b/vendor/whatthejeff/drawille/tests/Drawille/TurtleTest.php
new file mode 100644
index 0000000..40c800b
--- /dev/null
+++ b/vendor/whatthejeff/drawille/tests/Drawille/TurtleTest.php
@@ -0,0 +1,57 @@
+<?php
+
+/*
+ * This file is part of php-drawille
+ *
+ * (c) Jeff Welch <whatthejeff@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Drawille;
+
+class TurtleTest extends \PHPUnit_Framework_TestCase
+{
+ private $turtle;
+
+ protected function setUp() {
+ $this->turtle = new Turtle();
+ }
+
+ public function testPosition() {
+ $this->assertEquals(0, $this->turtle->getX());
+ $this->assertEquals(0, $this->turtle->getY());
+
+ $this->turtle->move(1, 2);
+ $this->assertEquals(1, $this->turtle->getX());
+ $this->assertEquals(2, $this->turtle->getY());
+ }
+
+ public function testRotation() {
+ $this->assertEquals(0, $this->turtle->getRotation());
+
+ $this->turtle->right(30);
+ $this->assertEquals(30, $this->turtle->getRotation());
+
+
+ $this->turtle->left(30);
+ $this->assertEquals(0, $this->turtle->getRotation());
+ }
+
+ public function testBrush() {
+ $this->assertFalse($this->turtle->get($this->turtle->getX(), $this->turtle->getY()));
+
+ $this->turtle->forward(1);
+ $this->assertTrue($this->turtle->get(0, 0));
+ $this->assertTrue($this->turtle->get($this->turtle->getX(), $this->turtle->getY()));
+
+ $this->turtle->up();
+ $this->turtle->move(2, 0);
+ $this->assertFalse($this->turtle->get($this->turtle->getX(), $this->turtle->getY()));
+
+ $this->turtle->down();
+ $this->turtle->move(3, 0);
+ $this->assertTrue($this->turtle->get($this->turtle->getX(), $this->turtle->getY()));
+ }
+} \ No newline at end of file