Compare commits

...

32 Commits

Author SHA1 Message Date
Gabriel Rodrigues Couto
e3fb698a7a
Merge pull request #52 from knewzen/master
remove codesponsor
2018-01-15 21:30:08 -02:00
knewzen
fc2c91acfe remove codesponsor 2018-01-05 00:54:10 +08:00
Gabriel Rodrigues Couto
3fa8a25142 Update README.md
Add sponsor banner - It's money for helping to continue the development of this project
2017-09-08 00:52:54 -03:00
Gabriel Rodrigues Couto
89e12a4415 Some functions moved to LcdController
TerminalCanvas refactored, removed Drawille dependency, implemented a faster braille render. Thank you @whatthejeff for the inspiration :-)

Little FPS gain #46
2016-07-02 23:18:46 -03:00
Gabriel Rodrigues Couto
adffbf649d Merge pull request #45 from hasegawa-tomoki/fix-error-handler-for-php7
PHP7 wants Throwable for 1st parameter of set_exception_handler's closure.
2016-07-02 10:40:59 -03:00
HASEGAWA Tomoki
d894d500df Update boot.php 2016-05-05 15:14:26 +09:00
HASEGAWA Tomoki
43830cd31d Use global namespace for consistency. 2016-05-03 15:21:40 +09:00
Gabriel Rodrigues Couto
dca580237a Merge pull request #44 from hasegawa-tomoki/fix-class-not-found
Fix "class RuntimeException not found".
2016-05-01 16:56:56 -03:00
HASEGAWA Tomoki
afdbfae33b Fix "class RuntimeException not found". 2016-04-29 22:42:28 +09:00
HASEGAWA Tomoki
f5c638e243 PHP7 wants Throwable for 1st parameter of set_exception_handler's closure. 2016-04-29 22:36:15 +09:00
Gabriel Rodrigues Couto
2a05514b8a Updated TO-DO list on README 2016-03-04 00:04:49 -03:00
Gabriel Rodrigues Couto
785ba4e571 Code standard correction
Removed some unused variables
2016-03-03 23:51:12 -03:00
Gabriel Rodrigues Couto
f897b90064 Settings refactored 2016-03-03 23:35:40 -03:00
Gabriel Rodrigues Couto
a46af4b8de Memory Writer refactored.
Now, we don't have anonymous functions anymore.
2016-03-03 23:19:42 -03:00
Gabriel Rodrigues Couto
ff7b65b719 Memory Reader refactored
Some cleaning on Core
2016-03-03 09:17:39 -03:00
Gabriel Rodrigues Couto
765a7b77dd FPS gain - Working around 20 FPS on PHP 7
CanvasBuffer redesigned for black&white DrawContext
TerminalCanvas ignoring redraw of repeated frames
2016-03-02 00:25:41 -03:00
Gabriel Rodrigues Couto
cc403325cf Merge pull request #41 from ikari7789/refactor-opcode
Refactor Opcode and Cbopcode to use static methods
2016-02-29 12:12:02 -03:00
Andrew Miller
4620492b89 Updated Opcode to use hard functions instead of an array of functions.
Added version check for exception handler. Didn't work with PHP 7, which expects an Error rather than an Exception.
2016-02-29 20:24:14 +09:00
Gabriel Rodrigues Couto
c2c6a6ca84 Merge pull request #39 from diegocpires/master
Removing echo from input
2016-02-26 22:34:54 -03:00
Diego Pires
25b72dfcc9 Update Keyboard.php
Remove echo from Keyboard
2016-02-26 21:38:22 -03:00
Gabriel Rodrigues Couto
790f1298c5 Merge pull request #37 from reisraff/master
bin/build improvement packing only necessary files
2016-02-25 22:51:03 -03:00
Gabriel Rodrigues Couto
de77ce982b Merge pull request #38 from reisraff/fix_readme
Small fix in README.md
2016-02-25 22:50:22 -03:00
Rafael Reis
35f9c92378 Small fix in README.md 2016-02-25 20:36:42 -03:00
Rafael Reis
a189df7fd5 bin/build improvement packing only necessary files 2016-02-25 20:28:19 -03:00
Gabriel Rodrigues Couto
e73dc3d5c5 Added PHAR 2016-02-25 09:05:33 -03:00
Gabriel Rodrigues Couto
eb0faf9dbe Merge pull request #29 from reisraff/master
Adding build script to create php-gameboy.phar
2016-02-25 08:54:34 -03:00
Gabriel Rodrigues Couto
e0a2b96088 Merge pull request #26 from mauri870/master
[Proposal] Global installation
Changes on ROM file argument
Removed roms folder
2016-02-25 08:53:11 -03:00
Mauri de Souza Nunes
0c8f6fa8ee Update instructions for local usage
Made changes for global installation

Rom files are loaded from the current directory

Loads the global autoload if possible, if not, load the local autoload

Update instructions for global installation

Fix path on parameter

Update require of autoload file

Remove getcwd()

Fix typo

Update instructions for local usage
2016-02-25 08:11:41 -03:00
Gabriel Rodrigues Couto
b865e6dc03 Removed some audio instructions
Little Canvas performance improvement
2016-02-25 07:16:40 -03:00
Gabriel Rodrigues Couto
1b30bc7eb0 Spaces correction
Added phpcs to gitignore
2016-02-25 06:58:50 -03:00
Gabriel Rodrigues Couto
45454e3377 Created a class for LCD Controller 2016-02-24 23:26:55 -03:00
Rafael Reis
15d04a5ca4 Adding build script to create php-gameboy-phar 2016-02-24 22:34:27 -03:00
16 changed files with 8054 additions and 4591 deletions

23
.gitignore vendored
View File

@ -1,4 +1,21 @@
material/
vendor/
roms/*rom
# root
cache.properties
# bin
bin/*
!bin/php-gameboy
!bin/php-gameboy.phar
# material
material/
# vendor
vendor/
# roms
roms/
# global
*.gb
*.gbc
*.rom

View File

@ -13,7 +13,7 @@ Want to play Dr Mario or Pokémon on your server terminal? That's for you!
+ [Running](#running)
+ [Controls](#controls)
+ [Tests](#tests)
+ [TO-DO](#todo)
+ [TO-DO](#to-do)
+ [Credits](#credits)
+ [Legal](#legal)
@ -38,14 +38,44 @@ The following PHP versions are supported:
You will need a good terminal! I've tested only on MacOSX and Linux. I'm sorry
about that Windows guys :disappointed:
## Running
## Installation
Before: Put your ROMs files (.gb or .gbc) on "roms/" folder.
Using composer:
```bash
$ composer g require gabrielrcouto/php-terminal-gameboy-emulator:dev-master
```
Using PHAR:
```bash
$ wget https://raw.githubusercontent.com/gabrielrcouto/php-terminal-gameboy-emulator/master/bin/php-gameboy.phar
$ chmod +x php-gameboy.phar
$ mv php-gameboy.phar /usr/local/bin/php-gameboy
```
## Running
Your roms are loaded from the directory you are running the `php-gameboy` command.
```bash
$ php-gameboy drmario.gb
$ php-gameboy pokemon.gbc
```
If you like to run this emulator locally, simple clone the repository:
```bash
$ git clone https://github.com/gabrielrcouto/php-terminal-gameboy-emulator.git
$ cd php-terminal-gameboy-emulator
$ composer install -o
$ bin/php-gameboy drmario.gb
```
For running roms, pass the full path to your rom or put then in the `php-terminal-gameboy-emulator` folder:
```bash
$ bin/php-gameboy pokemon.gbc
$ bin/php-gameboy /full/path/to/your/rom/drmario.gb
```
## Controls
@ -93,8 +123,8 @@ You can use the following command to run the most common checks, such as `php -l
Converting from the JS paradigm was a lot of work, and I still need to adapt somethings like:
- [x] Code standard - PSRs, please!
- [ ] Array of functions - Maybe in PHP it's not the best approach
- [ ] Pixel auxiliary array - Very CPU intersive to convert RGBA every time
- [x] Array of functions - Maybe in PHP it's not the best approach
- [x] Pixel auxiliary array - Very CPU intensive to convert RGBA every time
- [ ] Classes - Core is too big!
- [ ] Profiling and otimizing - XHProf to find the most intensive functions
- [ ] Save/Restore - I need to save my Pokémon, please!
@ -112,3 +142,5 @@ It does not have any commercial or profitable intentions.
The user is responsible to use this code and its content in the terms of the law.
The author is completely against piracy and respects all the copyrights, trademarks and patents of Nintendo.

74
bin/build Executable file
View File

@ -0,0 +1,74 @@
#!/usr/bin/env php
<?php
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
require_once(__DIR__ . '/../vendor/autoload.php');
$rootDir = __DIR__ . '/..';
$filename = 'php-gameboy.phar';
$filePath = $rootDir . '/bin/' . $filename;
$stub = <<<STUB
#!/usr/bin/env php
<?php
Phar::mapPhar("php-gameboy.phar");
require_once("phar://php-gameboy.phar/php-terminal-gameboy-emulator/boot.php");
__HALT_COMPILER();
STUB;
if (file_exists($filePath)) {
unlink($filePath);
}
$finderSort = function ($a, $b) {
return strcmp(strtr($a->getRealPath(), '\\', '/'), strtr($b->getRealPath(), '\\', '/'));
};
function addFile($phar, $file)
{
$path = strtr(str_replace(dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR, '', $file->getRealPath()), '\\', '/');
$content = file_get_contents($file);
$phar->addFromString($path, $content);
}
$phar = new Phar(
$filePath,
FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::KEY_AS_FILENAME,
$filename
);
$phar->setSignatureAlgorithm(\Phar::SHA1);
$phar->startBuffering();
addFile($phar, new SplFileInfo('boot.php', $rootDir . '/boot.php', 'boot.php'));
addFile($phar, new SplFileInfo('composer.json', $rootDir . '/composer.json', 'composer.json'));
addFile($phar, new SplFileInfo('vendor/autoload.php', $rootDir . '/vendor/autoload.php', 'vendor/autoload.php'));
$finder = new Finder();
$finder->files()
->ignoreVCS(true)
->name('*.php')
->in([
$rootDir . '/src/',
$rootDir . '/vendor/composer',
$rootDir . '/vendor/whatthejeff',
])
->sort($finderSort)
;
foreach ($finder as $file) {
addFile($phar, $file);
}
$phar->setStub($stub);
$phar->stopBuffering();
chmod($filePath, 0775);

BIN
bin/php-gameboy.phar Executable file

Binary file not shown.

View File

@ -1,23 +1,39 @@
<?php
require_once __DIR__.'/vendor/autoload.php';
foreach (['../../autoload.php', '../vendor/autoload.php', 'vendor/autoload.php'] as $autoload) {
$autoload = __DIR__.'/'.$autoload;
if (file_exists($autoload)) {
require $autoload;
break;
}
}
unset($autoload);
use GameBoy\Canvas\TerminalCanvas;
use GameBoy\Core;
use GameBoy\Keyboard;
set_exception_handler(function (Exception $exception) {
fwrite(STDERR, $exception->getMessage().PHP_EOL);
exit(254);
});
if (count($argv) < 2) {
throw new RuntimeException('You need to pass the ROM file name (Ex: drmario.rom)');
if (PHP_VERSION_ID >= 70000) {
set_exception_handler(function (\Throwable $exception) {
fwrite(STDERR, $exception->getMessage() . PHP_EOL);
exit(254);
});
} else {
set_exception_handler(function (Exception $exception) {
fwrite(STDERR, $exception->getMessage() . PHP_EOL);
exit(254);
});
}
$filename = 'roms/'.$argv[1];
if (count($argv) < 2) {
throw new \RuntimeException('You need to pass the ROM file name (Ex: drmario.rom)');
}
$filename = $argv[1];
if (!file_exists($filename)) {
throw new RuntimeException(sprintf('"%s" does not exist', $filename));
throw new \RuntimeException(sprintf('"%s" does not exist', $filename));
}
if (extension_loaded('xdebug')) {
@ -35,11 +51,11 @@ $keyboard = new Keyboard($core);
$core->start();
if (($core->stopEmulator & 2) == 0) {
throw new RuntimeException('The GameBoy core is already running.');
throw new \RuntimeException('The GameBoy core is already running.');
}
if ($core->stopEmulator & 2 != 2) {
throw new RuntimeException('GameBoy core cannot run while it has not been initialized.');
throw new \RuntimeException('GameBoy core cannot run while it has not been initialized.');
}
$core->stopEmulator &= 1;

View File

@ -11,12 +11,12 @@
}
],
"require": {
"php": ">=5.6.0",
"whatthejeff/drawille": "^1.0"
"php": ">=5.6.0"
},
"bin": ["bin/php-gameboy"],
"require-dev": {
"squizlabs/php_codesniffer": "2.0.*@dev"
"squizlabs/php_codesniffer": "2.0.*@dev",
"symfony/finder" : "*"
},
"config": {
"bin-dir": "bin"

108
composer.lock generated
View File

@ -4,62 +4,9 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "6f5eed549688ca9a4eb124eae054ffce",
"content-hash": "ccc583d4e7fb658a3842017f2fc17e17",
"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"
}
],
"hash": "1d43205f748238a612e736313fb85ead",
"content-hash": "650da37ee06349860cc3ce6988504476",
"packages": [],
"packages-dev": [
{
"name": "squizlabs/php_codesniffer",
@ -133,6 +80,55 @@
"standards"
],
"time": "2014-12-05 00:14:12"
},
{
"name": "symfony/finder",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "8201978de88a9fa0923e18601bb17f1df9c721e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/8201978de88a9fa0923e18601bb17f1df9c721e7",
"reference": "8201978de88a9fa0923e18601bb17f1df9c721e7",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Finder\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"time": "2016-06-29 05:41:56"
}
],
"aliases": [],

2
roms/.gitignore vendored
View File

@ -1,2 +0,0 @@
*.gb
*.gbc

View File

@ -11,9 +11,7 @@ interface DrawContextInterface
/**
* Draw image on canvas.
*
* @param array $canvasBuffer Each pixel => 4 items on array (RGBA)
* @param int $left
* @param int $top
* @param array $canvasBuffer If colored, each pixel => 4 items on array (RGBA)
*/
public function draw($canvasBuffer, $left, $top);
public function draw($canvasBuffer);
}

View File

@ -2,50 +2,59 @@
namespace GameBoy\Canvas;
use Drawille\Canvas;
use GameBoy\Settings;
class TerminalCanvas implements DrawContextInterface
{
/**
* The blank brailler char
* @var String
*/
protected $brailleCharOffset;
protected $canvas;
/**
* If is a color enabled canvas, set to true
* @var Boolean
*/
public $colorEnabled = false;
protected $currentSecond = 0;
protected $framesInSecond = 0;
protected $fps = 0;
private $width = 0;
private $height = 0;
protected $height = 0;
protected $lastFrame;
protected $lastFrameCanvasBuffer;
/**
* Braille Pixel Matrix
* ,___,
* |1 4|
* |2 5|
* |3 6|
* |7 8|
* `````
* @var Array
*/
protected $pixelMap;
protected $width = 0;
public function __construct()
{
$this->canvas = new Canvas();
$this->brailleCharOffset = html_entity_decode('&#' . (0x2800) . ';', ENT_NOQUOTES, 'UTF-8');
$this->pixelMap = [
[html_entity_decode('&#' . (0x2801) . ';', ENT_NOQUOTES, 'UTF-8'), html_entity_decode('&#' . (0x2808) . ';', ENT_NOQUOTES, 'UTF-8')],
[html_entity_decode('&#' . (0x2802) . ';', ENT_NOQUOTES, 'UTF-8'), html_entity_decode('&#' . (0x2810) . ';', ENT_NOQUOTES, 'UTF-8')],
[html_entity_decode('&#' . (0x2804) . ';', ENT_NOQUOTES, 'UTF-8'), html_entity_decode('&#' . (0x2820) . ';', ENT_NOQUOTES, 'UTF-8')],
[html_entity_decode('&#' . (0x2840) . ';', ENT_NOQUOTES, 'UTF-8'), html_entity_decode('&#' . (0x2880) . ';', ENT_NOQUOTES, 'UTF-8')],
];
}
/**
* Draw image on canvas using braille font.
*
* @param object $canvasBuffer $data = Each pixel = 4 items on array (RGBA)
* @param int $left
* @param int $top
* @param object $canvasBuffer $data = Each pixel (true/false)
*/
public function draw($canvasBuffer, $left, $top)
public function draw($canvasBuffer)
{
//Corner pixel, to draw same size each time
$this->canvas->set(0, 0);
$this->canvas->set(159, 143);
for ($i = 0; $i < count($canvasBuffer); $i = $i + 4) {
// Sum of all colors, Ignore alpha
$total = $canvasBuffer[$i] + $canvasBuffer[$i + 1] + $canvasBuffer[$i + 2];
$x = ($i / 4) % 160;
$y = ceil(($i / 4) / 160);
// 350 is a good threshold for black and white
if ($total > 350) {
$this->canvas->set($x, $y);
}
}
//Calculate current FPS
if ($this->currentSecond != time()) {
$this->fps = $this->framesInSecond;
$this->currentSecond = time();
@ -54,21 +63,56 @@ class TerminalCanvas implements DrawContextInterface
++$this->framesInSecond;
}
$frame = $this->canvas->frame();
$content = "\e[H\e[2J";
//If the last frame changed, we draw
// @TODO - The FPS will be wrong, need to find a way to update
// without redraw
if ($canvasBuffer != $this->lastFrameCanvasBuffer) {
// Array with all braille chars of the frame, filled with the blank char
// 2880 = total braille chars per frame
$chars = array_fill(0, 2880, $this->brailleCharOffset);
if ($this->height > 0 && $this->width > 0) {
$content = "\e[{$this->height}A\e[{$this->width}D";
// Turn on the first and last pixels
$chars[0] |= $this->pixelMap[0][0];
$chars[2879] |= $this->pixelMap[0][0];
// Frame string - It's a big braille chars string
$frame = '';
for ($y = 0; $y < 144; $y++) {
for ($x = 0; $x < 160; $x++) {
$pixelCanvasNumber = $x + (160 * $y);
$charPosition = floor($x / 2) + (floor($y / 4) * 80);
if (isset($canvasBuffer[$pixelCanvasNumber]) && $canvasBuffer[$pixelCanvasNumber]) {
$chars[$charPosition] |= $this->pixelMap[$y % 4][$x % 2];
}
// Each braille frame has 8 pixels, when we reach the last pixel,
// we can append to the frame string
if ($x % 2 === 1 && $y % 4 === 3) {
$frame .= $chars[$charPosition];
if ($x % 159 === 0) {
$frame .= PHP_EOL;
}
}
}
}
$this->lastFrame = $frame;
$this->lastFrameCanvasBuffer = $canvasBuffer;
$content = "\e[H\e[2J";
if ($this->height > 0 && $this->width > 0) {
$content = "\e[{$this->height}A\e[{$this->width}D";
}
$content .= sprintf('FPS: %3d - Frame Skip: %3d' . PHP_EOL, $this->fps, Settings::$frameskipAmout) . $frame;
echo $content;
$this->height = 37;
$this->width = 80;
}
$content .= sprintf('FPS: %d - Frame Skip: %s'.PHP_EOL, $this->fps, Settings::$settings[4]);
$content .= $frame;
echo $content;
$this->canvas->clear();
$this->height = substr_count($frame, PHP_EOL) + 1;
$this->width = strpos($frame, PHP_EOL);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@ class Keyboard
public function __construct(Core $core)
{
$this->core = $core;
exec('stty -icanon');
exec('stty -icanon -echo');
$this->file = fopen('php://stdin', 'r');
stream_set_blocking($this->file, false);
}
@ -35,7 +35,7 @@ class Keyboard
//Maps a keyboard key to a gameboy key.
//Order: Right, Left, Up, Down, A, B, Select, Start
$keyIndex = array_search($key, Settings::$settings[3]);
$keyIndex = array_search($key, Settings::$keyboardButtonMap);
if ($keyIndex === false) {
return -1;

220
src/LcdController.php Normal file
View File

@ -0,0 +1,220 @@
<?php
namespace GameBoy;
class LcdController
{
//Actual scan line...
public $actualScanLine = 0;
protected $core;
//Is the emulated LCD controller on?
public $LCDisOn = false;
//Should we trigger an interrupt if LY==LYC?
public $LYCMatchTriggerSTAT = false;
//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 in mode 0?
public $mode0TriggerSTAT = false;
//Should we trigger an interrupt if in mode 1?
public $mode1TriggerSTAT = false;
//Should we trigger an interrupt if in mode 2?
public $mode2TriggerSTAT = false;
//Tracker for STAT triggering.
public $STATTracker = 0;
public function __construct($core)
{
$this->core = $core;
}
public function matchLYC()
{
// LY - LYC Compare
// If LY==LCY
if ($this->core->memory[0xFF44] == $this->core->memory[0xFF45]) {
$this->core->memory[0xFF41] |= 0x04; // set STAT bit 2: LY-LYC coincidence flag
if ($this->LYCMatchTriggerSTAT) {
$this->core->memory[0xFF0F] |= 0x2; // set IF bit 1
}
} else {
$this->core->memory[0xFF41] &= 0xFB; // reset STAT bit 2 (LY!=LYC)
}
}
public function notifyScanline()
{
if ($this->actualScanLine == 0) {
$this->core->windowSourceLine = 0;
}
// determine the left edge of the window (160 if window is inactive)
$windowLeft = ($this->core->gfxWindowDisplay && $this->core->memory[0xFF4A] <= $this->actualScanLine) ? min(160, $this->core->memory[0xFF4B] - 7) : 160;
// step 1: background+window
$skippedAnything = $this->core->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->core->drawSpritesForLine($this->actualScanLine);
// step 3: prio tiles+window
if ($skippedAnything) {
$this->core->drawBackgroundForLine($this->actualScanLine, $windowLeft, 0x80);
}
if ($windowLeft < 160) {
++$this->core->windowSourceLine;
}
}
/**
* Scan Line and STAT Mode Control
* @param int $line Memory Scanline
*/
public function scanLine($line)
{
//When turned off = Do nothing!
if ($this->LCDisOn) {
if ($line < 143) {
//We're on a normal scan line:
if ($this->core->LCDTicks < 20) {
$this->scanLineMode2(); // mode2: 80 cycles
} elseif ($this->core->LCDTicks < 63) {
$this->scanLineMode3(); // mode3: 172 cycles
} elseif ($this->core->LCDTicks < 114) {
$this->scanLineMode0(); // mode0: 204 cycles
} else {
//We're on a new scan line:
$this->core->LCDTicks -= 114;
$this->actualScanLine = ++$this->core->memory[0xFF44];
$this->matchLYC();
if ($this->STATTracker != 2) {
if ($this->core->hdmaRunning && !$this->core->halt && $this->LCDisOn) {
$this->core->performHdma(); //H-Blank DMA
}
if ($this->mode0TriggerSTAT) {
$this->core->memory[0xFF0F] |= 0x2; // set IF bit 1
}
}
$this->STATTracker = 0;
$this->scanLineMode2(); // mode2: 80 cycles
if ($this->core->LCDTicks >= 114) {
//We need to skip 1 or more scan lines:
$this->core->notifyScanline();
$this->scanLine($this->actualScanLine); //Scan Line and STAT Mode Control
}
}
} elseif ($line == 143) {
//We're on the last visible scan line of the LCD screen:
if ($this->core->LCDTicks < 20) {
$this->scanLineMode2(); // mode2: 80 cycles
} elseif ($this->core->LCDTicks < 63) {
$this->scanLineMode3(); // mode3: 172 cycles
} elseif ($this->core->LCDTicks < 114) {
$this->scanLineMode0(); // mode0: 204 cycles
} else {
//Starting V-Blank:
//Just finished the last visible scan line:
$this->core->LCDTicks -= 114;
$this->actualScanLine = ++$this->core->memory[0xFF44];
$this->matchLYC();
if ($this->mode1TriggerSTAT) {
$this->core->memory[0xFF0F] |= 0x2; // set IF bit 1
}
if ($this->STATTracker != 2) {
if ($this->core->hdmaRunning && !$this->core->halt && $this->LCDisOn) {
$this->core->performHdma(); //H-Blank DMA
}
if ($this->mode0TriggerSTAT) {
$this->core->memory[0xFF0F] |= 0x2; // set IF bit 1
}
}
$this->STATTracker = 0;
$this->modeSTAT = 1;
$this->core->memory[0xFF0F] |= 0x1; // set IF flag 0
//LCD off takes at least 2 frames.
if ($this->core->drewBlank > 0) {
--$this->core->drewBlank;
}
if ($this->core->LCDTicks >= 114) {
//We need to skip 1 or more scan lines:
$this->scanLine($this->actualScanLine); //Scan Line and STAT Mode Control
}
}
} elseif ($line < 153) {
//In VBlank
if ($this->core->LCDTicks >= 114) {
//We're on a new scan line:
$this->core->LCDTicks -= 114;
$this->actualScanLine = ++$this->core->memory[0xFF44];
$this->matchLYC();
if ($this->core->LCDTicks >= 114) {
//We need to skip 1 or more scan lines:
$this->scanLine($this->actualScanLine); //Scan Line and STAT Mode Control
}
}
} else {
//VBlank Ending (We're on the last actual scan line)
if ($this->core->memory[0xFF44] == 153) {
$this->core->memory[0xFF44] = 0; //LY register resets to 0 early.
$this->matchLYC(); //LY==LYC Test is early here (Fixes specific one-line glitches (example: Kirby2 intro)).
}
if ($this->core->LCDTicks >= 114) {
//We reset back to the beginning:
$this->core->LCDTicks -= 114;
$this->actualScanLine = 0;
$this->scanLineMode2(); // mode2: 80 cycles
if ($this->core->LCDTicks >= 114) {
//We need to skip 1 or more scan lines:
$this->scanLine($this->actualScanLine); //Scan Line and STAT Mode Control
}
}
}
}
}
public function scanLineMode0()
{
// H-Blank
if ($this->modeSTAT != 0) {
if ($this->core->hdmaRunning && !$this->core->halt && $this->LCDisOn) {
$this->performHdma(); //H-Blank DMA
}
if ($this->mode0TriggerSTAT || ($this->mode2TriggerSTAT && $this->STATTracker == 0)) {
$this->core->memory[0xFF0F] |= 0x2; // if STAT bit 3 -> set IF bit1
}
$this->notifyScanline();
$this->STATTracker = 2;
$this->modeSTAT = 0;
}
}
public function scanLineMode2()
{
// OAM in use
if ($this->modeSTAT != 2) {
if ($this->mode2TriggerSTAT) {
$this->core->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->core->memory[0xFF0F] |= 0x2; // set IF bit 1
}
$this->STATTracker = 1;
$this->modeSTAT = 3;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -4,70 +4,43 @@ namespace GameBoy;
class Settings
{
//Some settings.
public static $settings = [
//[0] - Turn on sound.
false,
//Audio granularity setting (Sampling of audio every x many machine cycles)
public static $audioGranularity = 20;
//[1] - Force Mono sound.
false,
//Auto Frame Skip
public static $autoFrameskip = true;
//[2] - Give priority to GameBoy mode
true,
//Colorize GB mode?
public static $colorize = false;
//[3] - Keyboard button map.
//Order: Right, Left, Up, Down, A, B, Select, Start
['d', 'a', 'w', 's', ',', '.', 'n', 'm'],
//Keyboard button map.
//Order: Right, Left, Up, Down, A, B, Select, Start
public static $keyboardButtonMap = ['d', 'a', 'w', 's', ',', '.', 'n', 'm'];
//[4] - Frameskip Amount (Auto frameskip setting allows the script to change this.)
0,
//Frameskip Amount (Auto frameskip setting allows the script to change this.)
public static $frameskipAmout = 0;
//[5] - Use the data URI BMP method over the canvas tag method?
false,
//Frameskip base factor
public static $frameskipBaseFactor = 10;
//[6] - How many tiles in each direction when using the BMP method (width * height).
[16, 12],
//Maximum Frame Skip
public static $frameskipMax = 29;
//[7] - Auto Frame Skip
true,
//Interval for the emulator loop.
public static $loopInterval = 17;
//[8] - Maximum Frame Skip
29,
//Target number of machine cycles per loop. (4,194,300 / 1000 * 17)
public static $machineCyclesPerLoop = 17826;
//[9] - 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.
public static $overrideMBC = true;
//[10] - Override MBC RAM disabling and always allow reading and writing to the banks.
true,
//Override to allow for MBC1 instead of ROM only (compatibility for broken 3rd-party cartridges).
public static $overrideMBC1 = true;
//[11] - Audio granularity setting (Sampling of audio every x many machine cycles)
20,
//Give priority to GameBoy mode
public static $priorizeGameBoyMode = true;
//[12] - Frameskip base factor
10,
//[13] - Target number of machine cycles per loop. (4,194,300 / 1000 * 17)
17826,
//[14] - Sample Rate
70000,
//[15] - How many bits per WAV PCM sample (For browsers that fall back to WAV PCM generation)
0x10,
//[16] - Use the GBC BIOS?
false,
//[17] - Colorize GB mode?
false,
//[18] - Sample size for webkit audio.
512,
//[19] - Whether to display the canvas at 144x160 on fullscreen or as stretched.
false,
//[20] - Interval for the emulator loop.
17,
];
//Sample Rate
public static $sampleRate = 70000;
}