diff options
author | Gabriel Rodrigues Couto <gabrielrcouto@gmail.com> | 2016-02-21 09:42:33 -0300 |
---|---|---|
committer | Gabriel Rodrigues Couto <gabrielrcouto@gmail.com> | 2016-02-21 09:42:33 -0300 |
commit | e8d613fb4757fd08806bdc4e506a9c483d8394a3 (patch) | |
tree | eef74fd742e4bf0c9b94f65e361551ff274a9e12 | |
download | php-terminal-gameboy-emulator-e8d613fb4757fd08806bdc4e506a9c483d8394a3.zip php-terminal-gameboy-emulator-e8d613fb4757fd08806bdc4e506a9c483d8394a3.tar.gz php-terminal-gameboy-emulator-e8d613fb4757fd08806bdc4e506a9c483d8394a3.tar.bz2 |
Initial commit
It Works! \o/
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 Binary files differnew file mode 100755 index 0000000..937b548 --- /dev/null +++ b/drmario.gb 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 Binary files differnew file mode 100644 index 0000000..45e5939 --- /dev/null +++ b/teste.txt 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). + + + +## 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"; +~~~ + + + +~~~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"; +~~~ + + + +## Tests + +[](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 Binary files differnew file mode 100644 index 0000000..af8d6ec --- /dev/null +++ b/vendor/whatthejeff/drawille/docs/images/octocat.png diff --git a/vendor/whatthejeff/drawille/docs/images/sin.png b/vendor/whatthejeff/drawille/docs/images/sin.png Binary files differnew file mode 100644 index 0000000..22262bd --- /dev/null +++ b/vendor/whatthejeff/drawille/docs/images/sin.png diff --git a/vendor/whatthejeff/drawille/docs/images/turtle.png b/vendor/whatthejeff/drawille/docs/images/turtle.png Binary files differnew file mode 100644 index 0000000..8ae6822 --- /dev/null +++ b/vendor/whatthejeff/drawille/docs/images/turtle.png 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 |