ゲームボーイエミュレータのテスト
32KB 程度のサイズの、初期のゲームしか動きません。
音も出ません。
Load Cartridge ボタンを押して、.gb ファイルをロードしてください。
選択したファイルはサーバにはアップロードされません。
Flash 内に読み込まれるだけです。
CPU のコードは TGB Dual のものを使用させていただきました。
// forked from hacker_h5m0j2oq's forked from: 2009-3-18 Gameboy Emulator
// forked from hikipuro's 2009-3-18 Gameboy Emulator
package
{
/**
* ゲームボーイエミュレータのテスト
* 32KB 程度のサイズの、初期のゲームしか動きません。
* 音も出ません。
*
* Load Cartridge ボタンを押して、.gb ファイルをロードしてください。
* 選択したファイルはサーバにはアップロードされません。
* Flash 内に読み込まれるだけです。
*
* CPU のコードは TGB Dual のものを使用させていただきました。
*
*/
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.SimpleButton;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.TextEvent;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFieldType;
import flash.ui.Keyboard;
import flash.utils.ByteArray;
import flash.utils.getTimer;
[SWF(backgroundColor=0xFFFFFF,frameRate=60)]
public class Main extends Sprite
{
private var data:ByteArray;
private var textField:TextField;
private var output:BitmapData;
private var gb:GB;
private var fileReference:FileReference;
private var buttonLoad:Button;
private var buttonReset:Button;
private var stop:Boolean = false;
private var keys:Vector.<Boolean>;
private var prevKeys:Vector.<Boolean>;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function prepare():void
{
var frame:GbFrame = new GbFrame();
frame.x = 10;
frame.y = 10;
addChild(frame);
// Load ボタンの作成
buttonLoad = new Button("Load Cartridge");
buttonLoad.addEventListener(MouseEvent.MOUSE_DOWN, onButtonLoadDown);
buttonLoad.x = 350;
buttonLoad.y = 10;
addChild(buttonLoad);
// Reset ボタンの作成
buttonReset = new Button("Reset");
buttonReset.addEventListener(MouseEvent.MOUSE_DOWN, onButtonResetDown);
buttonReset.x = 350;
buttonReset.y = 40;
addChild(buttonReset);
// 画面の準備
output = new BitmapData(160, 144);
var bitmap:Bitmap = new Bitmap(output);
bitmap.x = 85;
bitmap.y = 48;
addChild(bitmap);
// GBの準備
gb = new GB();
stop = true;
// キー入力情報の初期化
keys = new Vector.<Boolean>();
prevKeys = new Vector.<Boolean>();
for (var i:int = 0; i < 230; i++) {
keys[i] = new Boolean(false);
prevKeys[i] = new Boolean(false);
}
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
prepare();
gb.output = output;
addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
}
private function onButtonResetDown(event:MouseEvent):void
{
if (data == null)
return;
gb = new GB();
gb.loadRom(data);
}
private function onButtonLoadDown(event:MouseEvent):void
{
stop = true;
fileReference = new FileReference();
// イベントの登録
fileReference.addEventListener(Event.SELECT, onFileSelect);
fileReference.addEventListener(Event.CANCEL, onFileCancel);
fileReference.addEventListener(Event.COMPLETE, onFileLoadComplete);
// ファイル選択ダイアログを表示する
var fileFilter:FileFilter = new FileFilter("GB Cartridge (gb)", "*.gb");
fileReference.browse([fileFilter]);
}
private function onFileSelect(event:Event):void
{
fileReference.load();
stop = false;
}
private function onFileCancel(event:Event):void
{
stop = false;
}
private function onFileLoadComplete(event:Event):void
{
gb = new GB();
data = fileReference.data;
gb.loadRom(fileReference.data);
}
private function onKeyDown(event:KeyboardEvent):void
{
keys[event.keyCode] = true;
}
private function onKeyUp(event:KeyboardEvent):void
{
keys[event.keyCode] = false;
}
private function onEnterFrame(e:Event):void
{
if (stop || gb == null || !gb.dataLoaded)
return;
gb.setPad(Keyboard.LEFT, keys[Keyboard.LEFT]);
gb.setPad(Keyboard.RIGHT, keys[Keyboard.RIGHT]);
gb.setPad(Keyboard.UP, keys[Keyboard.UP]);
gb.setPad(Keyboard.DOWN, keys[Keyboard.DOWN]);
gb.setPad(88, keys[88]);
gb.setPad(90, keys[90]);
gb.setPad(83, keys[83]);
gb.setPad(65, keys[65]);
gb.exec();
gb.render(output);
}
}
}
import flash.display.Sprite;
class GbFrame extends Sprite
{
import flash.display.Graphics;
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFormat;
public function GbFrame()
{
var s:Sprite = new Sprite();
var g:Graphics = s.graphics;
s.cacheAsBitmap = true;
// 外枠
g.lineStyle(3, 0x808090, 1.0, true);
g.beginFill(0xF0F0F0);
g.drawRoundRect(0, 0, 317, 317, 10, 10);
g.endFill();
g.lineStyle(3, 0x808090, 1.0, true);
g.moveTo(1, 15);
g.lineTo(316, 15);
g.moveTo(30, 1);
g.lineTo(30, 15);
g.moveTo(286, 1);
g.lineTo(286, 15);
// 中心
g.lineStyle(3, 0x808090, 1.0, true);
g.beginFill(0x606066);
g.drawRoundRect(30, 20, 255, 170, 20, 20);
g.endFill();
g.lineStyle(1, 0xB08090);
g.moveTo(40, 26);
g.lineTo(115, 26);
g.moveTo(235, 26);
g.lineTo(275, 26);
g.lineStyle(0);
g.lineStyle(1, 0x000050);
g.moveTo(40, 29);
g.lineTo(115, 29);
g.moveTo(235, 29);
g.lineTo(275, 29);
g.lineStyle(0);
g.lineStyle(0);
g.beginFill(0xFF0000);
g.drawCircle(52, 75, 3);
g.endFill();
g.lineStyle(6, 0x505050);
g.beginFill(0);
g.drawRect(75, 38, 160, 144);
g.endFill();
// ボタン
g.lineStyle(2, 0x505050);
g.beginFill(0xC000C0);
g.drawCircle(230, 250, 13);
g.drawCircle(270, 250, 13);
g.endFill();
g.beginFill(0xC0C0C0);
g.drawRoundRect(160, 245, 30, 8, 5, 5);
g.drawRoundRect(115, 245, 30, 8, 5, 5);
g.endFill();
addChild(s);
var text1:TextField;
text1 = new TextField();
text1.selectable = false;
text1.x = 36;
text1.y = 80;
text1.htmlText = '<font size="6" color="#FFFFFF">BATTERY</font>';
addChild(text1);
var text2:TextField;
text2 = new TextField();
text2.selectable = false;
text2.width = 120;
text2.height = 10;
text2.x = 118;
text2.y = 23;
text2.htmlText = '<font size="6" color="#FFFFFF">DOT MATRIX WITHOUT STEREO SOUND</font>';
addChild(text2);
var text3:TextField;
text3 = new TextField();
text3.selectable = false;
text3.x = 32;
text3.y = 193;
text3.htmlText = '<font size="8" color="#000088"><b>Nintonde</b></font>';
addChild(text3);
var text4:TextField;
text4 = new TextField();
text4.selectable = false;
text4.x = 70;
text4.y = 190;
text4.htmlText = '<font size="12" color="#000088"><b>GAME BOY</b></font>';
addChild(text4);
var text5:TextField;
text5 = new TextField();
text5.selectable = false;
text5.x = 264;
text5.y = 270;
text5.htmlText = '<font size="12" color="#000088"><b>X</b></font>';
addChild(text5);
var text6:TextField;
text6 = new TextField();
text6.selectable = false;
text6.x = 224;
text6.y = 270;
text6.htmlText = '<font size="12" color="#000088"><b>Z</b></font>';
addChild(text6);
var text7:TextField;
text7 = new TextField();
text7.selectable = false;
text7.x = 169;
text7.y = 270;
text7.htmlText = '<font size="12" color="#000088"><b>S</b></font>';
addChild(text7);
var text8:TextField;
text8 = new TextField();
text8.selectable = false;
text8.x = 124;
text8.y = 270;
text8.htmlText = '<font size="12" color="#000088"><b>A</b></font>';
addChild(text8);
}
}
import flash.display.SimpleButton;
class Button extends SimpleButton
{
import flash.display.SimpleButton;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
public function Button(caption:String = "ボタン", width:uint = 100, height:uint = 20)
{
upState = makeButton(0xDDDDDD, width, height, height / 2, caption);
overState = makeButton(0xEEEEEE, width, height, height / 2, caption);
downState = makeButton(0xCCCCCC, width, height, height / 2, caption);
hitTestState = upState;
}
/**
* ボタンを作って返す
* @param color 色
* @param width 幅
* @param height 高さ
* @param round 角丸の大きさ
* @param text ボタンのテキスト
* @return ボタン
*/
private function makeButton(color:uint, width:int, height:int, round:int, text:String):Sprite
{
var t:TextField = new TextField();
var s:Sprite = new Sprite();
s.graphics.lineStyle(2);
s.graphics.beginFill(color);
s.graphics.drawRoundRect(0, 0, width, height, round);
s.graphics.endFill();
t.text = text;
t.selectable = false;
t.width = width;
t.autoSize = TextFieldAutoSize.CENTER;
s.addChild(t);
return s;
}
}
/**
* ...
* @author Hikipuro
*/
class GbCart
{
import flash.utils.ByteArray;
public var title:String = "";
public var type:String = "";
public var typeCode:uint = 0;
public var romBanks:uint = 0;
public var ramBanks:uint = 0;
public var haveSram:Boolean = false;
public var sram:Vector.<uint>;
private var rom_bank:uint;
private var rom:Vector.<uint>;
private var mbc1type:uint = 0;
private var mbc1_0x2000:uint = 0;
private var mbc1_0x4000:uint = 0;
private var mbc1_ram_bank:uint = 0;
public function GbCart(byteArray:ByteArray)
{
if (byteArray.length < 0x150)
return;
mbc1type = 0;
mbc1_0x2000 = 0;
mbc1_0x4000 = 0;
mbc1_ram_bank = 0;
rom_bank = 0;
rom = new Vector.<uint>();
rom.fixed = false;
rom.length = byteArray.length;
rom.fixed = true;
byteArray.position = 0;
for (var i:uint = 0; i < byteArray.length; i++)
rom[i] = (byteArray.readByte() & 0xFF);
sram = new Vector.<uint>();
sram.fixed = false;
sram.length = 8192 * 4;
sram.fixed = true;
byteArray.position = 0x134;
title = byteArray.readUTFBytes(15);
byteArray.position = 0x147;
typeCode = byteArray.readByte();
switch (typeCode)
{
case 0x00: type = "ROM Only"; break;
case 0x01: type = "ROM+MBC1"; break;
case 0x02: type = "ROM+MBC1+RAM"; haveSram = true; break;
case 0x03: type = "ROM+MBC1+RAM+BATTERY"; haveSram = true; break;
case 0x05: type = "ROM+MBC2"; break;
case 0x06: type = "ROM+MBC2+BATTERY"; break;
case 0x08: type = "ROM+RAM"; haveSram = true; break;
case 0x09: type = "ROM+RAM+BATTERY"; haveSram = true; break;
case 0x0B: type = "ROM+MMM01"; break;
case 0x0C: type = "ROM+MMM01+SRAM"; haveSram = true; break;
case 0x0D: type = "ROM+MMM01+SRAM+BATT"; haveSram = true; break;
case 0x0F: type = "ROM+MBC3+TIMER+BATT"; break;
case 0x10: type = "ROM+MBC3+TIMER+RAM+BATT"; haveSram = true; break;
case 0x11: type = "ROM+MBC3"; break;
case 0x12: type = "ROM+MBC3+RAM"; haveSram = true; break;
case 0x13: type = "ROM+MBC3+RAM+BATT"; haveSram = true; break;
case 0x19: type = "ROM+MBC5"; break;
case 0x1A: type = "ROM+MBC5+RAM"; haveSram = true; break;
case 0x1B: type = "ROM+MBC5+RAM+BATT"; haveSram = true; break;
case 0x1C: type = "ROM+MBC5+RUMBLE"; break;
case 0x1D: type = "ROM+MBC5+RUMBLE+SRAM"; haveSram = true; break;
case 0x1E: type = "ROM+MBC5+RUMBLE+SRAM+BATT"; haveSram = true; break;
case 0x1F: type = "Pocket Camera"; break;
case 0xFD: type = "Bandai TAMA5"; break;
case 0xFE: type = "Hudson HuC3"; break;
case 0xFF: type = "ROM+HuC1+RAM+BATTERY"; haveSram = true; break;
}
byteArray.position = 0x148;
switch (byteArray.readByte())
{
case 0x00: romBanks = 2; break;
case 0x01: romBanks = 4; break;
case 0x02: romBanks = 8; break;
case 0x03: romBanks = 16; break;
case 0x04: romBanks = 32; break;
case 0x05: romBanks = 64; break;
case 0x06: romBanks = 128; break;
case 0x52: romBanks = 72; break;
case 0x53: romBanks = 80; break;
case 0x54: romBanks = 96; break;
}
byteArray.position = 0x149;
switch (byteArray.readByte())
{
case 0x00: ramBanks = 0; break;
case 0x01: ramBanks = 1; break;
case 0x02: ramBanks = 1; break;
case 0x03: ramBanks = 4; break;
case 0x04: ramBanks = 16; break;
}
}
public function read(adr:uint):uint
{
return rom[adr];
}
public function readBank(adr:uint):uint
{
switch (typeCode)
{
case 0x00: // "ROM Only"
return rom[adr];
case 0x01: // "ROM+MBC1"
return readMBC1(adr);
case 0x02: // "ROM+MBC1+RAM"
return readMBC1(adr);
break;
case 0x03: // "ROM+MBC1+RAM+BATTERY"
return readMBC1(adr);
break;
case 0x05: // "ROM+MBC2"
return readMBC2(adr);
break;
case 0x06: // "ROM+MBC2+BATTERY"
return readMBC2(adr);
break;
case 0x08: // "ROM+RAM"
break;
case 0x09: // "ROM+RAM+BATTERY"
break;
case 0x0B: // "ROM+MMM01"
break;
case 0x0C: // "ROM+MMM01+SRAM"
break;
case 0x0D: // "ROM+MMM01+SRAM+BATT"
break;
case 0x0F: // "ROM+MBC3+TIMER+BATT"
break;
case 0x10: // "ROM+MBC3+TIMER+RAM+BATT"
break;
case 0x11: // "ROM+MBC3"
break;
case 0x12: // "ROM+MBC3+RAM"
break;
case 0x13: // "ROM+MBC3+RAM+BATT"
break;
case 0x19: // "ROM+MBC5"
break;
case 0x1A: // "ROM+MBC5+RAM"
break;
case 0x1B: // "ROM+MBC5+RAM+BATT"
break;
case 0x1C: // "ROM+MBC5+RUMBLE"
break;
case 0x1D: // "ROM+MBC5+RUMBLE+SRAM"
break;
case 0x1E: // "ROM+MBC5+RUMBLE+SRAM+BATT"
break;
case 0x1F: // "Pocket Camera"
break;
case 0xFD: // "Bandai TAMA5"
break;
case 0xFE: // "Hudson HuC3"
break;
case 0xFF: // "ROM+HuC1+RAM+BATTERY"
break;
}
return 0;
}
public function write(adr:uint, dat:uint):void
{
switch (typeCode)
{
case 0x00: // "ROM Only"
break;
case 0x01: // "ROM+MBC1"
writeMBC1(adr, dat);
break;
case 0x02: // "ROM+MBC1+RAM"
writeMBC1(adr, dat);
break;
case 0x03: // "ROM+MBC1+RAM+BATTERY"
writeMBC1(adr, dat);
break;
case 0x05: // "ROM+MBC2"
writeMBC2(adr, dat);
break;
case 0x06: // "ROM+MBC2+BATTERY"
writeMBC2(adr, dat);
break;
case 0x08: // "ROM+RAM"
break;
case 0x09: // "ROM+RAM+BATTERY"
break;
case 0x0B: // "ROM+MMM01"
break;
case 0x0C: // "ROM+MMM01+SRAM"
break;
case 0x0D: // "ROM+MMM01+SRAM+BATT"
break;
case 0x0F: // "ROM+MBC3+TIMER+BATT"
break;
case 0x10: // "ROM+MBC3+TIMER+RAM+BATT"
break;
case 0x11: // "ROM+MBC3"
break;
case 0x12: // "ROM+MBC3+RAM"
break;
case 0x13: // "ROM+MBC3+RAM+BATT"
break;
case 0x19: // "ROM+MBC5"
break;
case 0x1A: // "ROM+MBC5+RAM"
break;
case 0x1B: // "ROM+MBC5+RAM+BATT"
break;
case 0x1C: // "ROM+MBC5+RUMBLE"
break;
case 0x1D: // "ROM+MBC5+RUMBLE+SRAM"
break;
case 0x1E: // "ROM+MBC5+RUMBLE+SRAM+BATT"
break;
case 0x1F: // "Pocket Camera"
break;
case 0xFD: // "Bandai TAMA5"
break;
case 0xFE: // "Hudson HuC3"
break;
case 0xFF: // "ROM+HuC1+RAM+BATTERY"
break;
}
}
public function readSram(adr:uint):uint
{
var offset:uint;
switch (typeCode)
{
case 0x03: // "ROM+MBC1+RAM+BATTERY"
if (adr < 0x1000)
return sram[adr];
offset = mbc1_ram_bank * 0x1000;
return sram[adr + offset];
break;
}
return sram[adr];
}
public function writeSram(adr:uint, dat:uint):void
{
var offset:uint;
switch (typeCode)
{
case 0x03: // "ROM+MBC1+RAM+BATTERY"
if (adr < 0x1000)
sram[adr] = dat;
offset = mbc1_ram_bank * 0x1000;
sram[adr + offset] = dat;
break;
}
sram[adr] = dat;
}
public function readMBC1(adr:uint):uint
{
var offset:uint;
if (mbc1type == 0) {
offset = mbc1_0x4000 | mbc1_0x2000;
offset *= 0x4000;
} else {
offset = mbc1_0x2000;
offset *= 0x4000;
}
adr &= 0x3FFF;
return rom[adr + offset];
}
public function writeMBC1(adr:uint, dat:uint):void
{
if (adr >= 0x6000 && adr <= 0x7FFF) {
mbc1type = dat & 0x01;
return;
}
if (adr >= 0x2000 && adr <= 0x3FFF)
mbc1_0x2000 = (dat & 0x1F);
if (adr >= 0x4000 && adr <= 0x5FFF) {
mbc1_0x4000 = ((dat & 0x03) << 5);
mbc1_ram_bank = (dat & 0x03);
}
}
public function readMBC2(adr:uint):uint
{
var offset:uint;
offset = mbc1_0x4000 | mbc1_0x2000;
offset *= 0x4000;
adr &= 0x3FFF;
return rom[adr + offset];
}
public function writeMBC2(adr:uint, dat:uint):void
{
if (adr >= 0x6000 && adr <= 0x7FFF) {
mbc1type = dat & 0x01;
mbc1_0x2000 = 0;
mbc1_0x4000 = 0;
return;
}
if (mbc1type == 0) {
if (adr >= 0x2000 && adr <= 0x3FFF)
mbc1_0x2000 = (dat & 0x1F);
if (adr >= 0x4000 && adr <= 0x5FFF)
mbc1_0x4000 = ((dat & 0x03) << 5);
}
else {
if (adr >= 0x2000 && adr <= 0x3FFF)
mbc1_0x2000 = (dat & 0x1F);
mbc1_0x4000 = 0;
}
}
}
/**
* ...
*/
class GbApu
{
import flash.events.SampleDataEvent;
import flash.media.Sound;
import flash.utils.ByteArray;
private var NR10:uint, NR11:uint, NR12:uint, NR13:uint, NR14:uint;
private var NR21:uint, NR22:uint, NR23:uint, NR24:uint;
private var NR30:uint, NR31:uint, NR32:uint, NR33:uint, NR34:uint;
private var NR41:uint, NR42:uint, NR43:uint, NR44:uint;
private var NR50:uint, NR51:uint, NR52:uint;
private var WaveRAM:Vector.<uint>;
//private var sound:Sound;
private var counter1:int;
private var length1:uint;
private var sweep1:uint;
private var duty1:uint;
private var volume1:uint;
private var decay1:uint;
private var env_counter1:uint;
private var total_clock:int;
private var div_clock:int;
private var tick1d256:int;
private var ringBuffer:Vector.<Number>;
private var pointer:int;
private var pointerPlay:int;
private const duty12:Array = [0, 15, 15, 15, 15, 15, 15, 15];
private const duty25:Array = [0, 15, 15, 15];
private const duty50:Array = [0, 15];
private const duty75:Array = [0, 0, 0, 15];
public function GbApu()
{
WaveRAM = new Vector.<uint>();
WaveRAM.length = 0x10;
WaveRAM.fixed = true;
NR10 = 0x80; NR11 = 0xBF; NR12 = 0xF3; NR14 = 0xBF;
NR21 = 0x3F; NR22 = 0x00; NR24 = 0xBF;
NR30 = 0x7F; NR31 = 0xFF; NR32 = 0x9F; NR33 = 0xBF;
NR41 = 0xFF; NR42 = 0x00; NR43 = 0x00; NR44 = 0xBF;
NR50 = 0x77; NR51 = 0xF3; NR52 = 0xF1;
counter1 = 0;
length1 = 0;
sweep1 = 0;
volume1 = 0;
decay1 = 0;
env_counter1 = 0;
total_clock = 0;
div_clock = 0;
tick1d256 = 0;
ringBuffer = new Vector.<Number>();
ringBuffer.length = 8192 * 4;
ringBuffer.fixed = true;
pointer = 0;
pointerPlay = 0;
//sound = new Sound();
//sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
//sound.play(0, 1);
}
public function read(adr:uint):uint
{
adr &= 0x2F;
switch (adr)
{
case 0x00: return NR10 & 0xFF;
case 0x01: return NR11 & 0xC0;
case 0x02: return NR12 & 0xFF;
case 0x03: return 0;
case 0x04: return NR14 & 0x40;
case 0x05: return 0xFF;
case 0x06: return NR21 & 0xC0;
case 0x07: return NR22 & 0xFF;
case 0x08: return NR23 & 0xFF;
case 0x09: return NR24 & 0x40;
case 0x0A: return NR30 & 0x80;
case 0x0B: return NR31 & 0xFF;
case 0x0C: return NR32 & 0x60;
case 0x0D: return NR33 & 0xFF;
case 0x0E: return NR34 & 0x40;
case 0x0F: return 0xFF;
case 0x10: return NR41 & 0xFF;
case 0x11: return NR42 & 0xFF;
case 0x12: return NR43 & 0xFF;
case 0x13: return NR44 & 0x40;
case 0x14: return NR50 & 0xFF;
case 0x15: return NR51 & 0xFF;
case 0x16: return NR52 & 0x8F;
case 0x17: return 0xFF;
case 0x18: return 0xFF;
case 0x19: return 0xFF;
case 0x1A: return 0xFF;
case 0x1B: return 0xFF;
case 0x1C: return 0xFF;
case 0x1D: return 0xFF;
case 0x1E: return 0xFF;
case 0x1F: return 0xFF;
case 0x20: return WaveRAM[0x00] & 0xFF;
case 0x21: return WaveRAM[0x01] & 0xFF;
case 0x22: return WaveRAM[0x02] & 0xFF;
case 0x23: return WaveRAM[0x03] & 0xFF;
case 0x24: return WaveRAM[0x04] & 0xFF;
case 0x25: return WaveRAM[0x05] & 0xFF;
case 0x26: return WaveRAM[0x06] & 0xFF;
case 0x27: return WaveRAM[0x07] & 0xFF;
case 0x28: return WaveRAM[0x08] & 0xFF;
case 0x29: return WaveRAM[0x09] & 0xFF;
case 0x2A: return WaveRAM[0x0A] & 0xFF;
case 0x2B: return WaveRAM[0x0B] & 0xFF;
case 0x2C: return WaveRAM[0x0C] & 0xFF;
case 0x2D: return WaveRAM[0x0D] & 0xFF;
case 0x2E: return WaveRAM[0x0E] & 0xFF;
case 0x2F: return WaveRAM[0x0F] & 0xFF;
}
return 0xFF;
}
public function write(adr:uint, dat:uint):void
{
adr &= 0x2F;
dat &= 0xFF;
switch (adr)
{
case 0x00:
NR10 = dat;
break;
case 0x01:
NR11 = dat;
length1 = soundLength1;
break;
case 0x02:
NR12 = dat;
decay1 = ((NR12 >> 4) & 0x0F);
break;
case 0x03:
NR13 = dat;
break;
case 0x04:
NR14 = dat;
if (NR14 & 0x80)
counter1 = 0;
break;
case 0x05:
return;
case 0x06:
NR21 = dat;
break;
case 0x07:
NR22 = dat;
break;
case 0x08:
NR23 = dat;
break;
case 0x09:
NR24 = dat;
break;
case 0x0A:
NR30 = dat;
break;
case 0x0B:
NR31 = dat;
break;
case 0x0C:
NR32 = dat;
break;
case 0x0D:
NR33 = dat;
break;
case 0x0E:
NR34 = dat;
break;
case 0x0F:
return;
case 0x10:
NR41 = dat;
break;
case 0x11:
NR42 = dat;
break;
case 0x12:
NR43 = dat;
break;
case 0x13:
NR44 = dat;
break;
case 0x14:
NR50 = dat;
break;
case 0x15:
NR51 = dat;
break;
case 0x16:
NR52 = dat;
break;
case 0x17:
return;
case 0x18:
return;
case 0x19:
return;
case 0x1A:
return;
case 0x1B:
return;
case 0x1C:
return;
case 0x1D:
return;
case 0x1E:
return;
case 0x1F:
return;
case 0x20:
WaveRAM[0x00] = dat;
break;
case 0x21:
WaveRAM[0x01] = dat;
break;
case 0x22:
WaveRAM[0x02] = dat;
break;
case 0x23:
WaveRAM[0x03] = dat;
break;
case 0x24:
WaveRAM[0x04] = dat;
break;
case 0x25:
WaveRAM[0x05] = dat;
break;
case 0x26:
WaveRAM[0x06] = dat;
break;
case 0x27:
WaveRAM[0x07] = dat;
break;
case 0x28:
WaveRAM[0x08] = dat;
break;
case 0x29:
WaveRAM[0x09] = dat;
break;
case 0x2A:
WaveRAM[0x0A] = dat;
break;
case 0x2B:
WaveRAM[0x0B] = dat;
break;
case 0x2C:
WaveRAM[0x0C] = dat;
break;
case 0x2D:
WaveRAM[0x0D] = dat;
break;
case 0x2E:
WaveRAM[0x0E] = dat;
break;
case 0x2F:
WaveRAM[0x0F] = dat;
break;
}
}
private function get initialEnvelopeVolume1():uint
{
return ((NR12 >> 4) & 0x0F);
}
private function get envelopeDirection1():int
{
return ((NR12 >> 3) & 0x01) == 1 ? 1 : -1;
}
private function get envelopeWait1():uint
{
return (NR12 & 0x07);
}
private function get soundLength1():uint
{
return 64 - (NR11 & 0x3F);
}
private function get sweepWait1():uint
{
return ((NR10 >> 4) & 0x07);
}
private function get sweepDirection1():uint
{
return ((NR10 >> 3) & 0x01);
}
private function get sweepShift1():uint
{
return (NR10 & 0x07);
}
private function get wavePatternDuty1():uint
{
switch ((NR11 >> 6) & 0x03) {
case 0: return 2;
case 1: return 4;
case 2: return 8;
case 3: return 12;
}
return 0;
}
public function exec(clock:uint):void
{
if (!(NR52 & 0x80)) // sound off
return;
return;
total_clock += clock;
div_clock += clock;
var count:uint;
var i:uint;
if (NR52 & 0x01) { // Channel 1 enabled
counter1 += clock;
count = NR13 | ((NR14 & 0x07) << 8);
// programable counter & duty cycle
if (counter1 > count) {
counter1 -= count;
duty1++;
if (duty1 == 16)
duty1 = 0;
}
//for (i = 0; i < clock; i++)
//{
if (duty1 < wavePatternDuty1) {
if ((NR12 & 0x07) == 0) { // decay off
volume1 += (((NR12 >> 4) & 0x0F) * clock);
} else { // decay on
volume1 += (decay1 * clock);
}
}
//}
}
// 1/256 second
if (total_clock > 4096) {
total_clock -= 4096;
tick1d256++;
tick256();
switch (tick1d256) {
case 2:
tick128();
break;
case 4:
tick128();
tick64();
tick1d256 = 0;
break;
}
}
// 1/32768 (32) second
if (div_clock > 23) {
var v:Number;
v = 0;
div_clock -= 23;
// sound 1 enabled
if (NR52 & 0x01) {
if (length1 != 0)
v += volume1;
volume1 = 0;
}
v /= (23 * 16);
ringBuffer[pointer] = v;
pointer++;
if (pointer >= ringBuffer.length)
pointer = 0;
}
}
private function tick256():void
{
if (NR52 & 0x01) {
if (length1 > 0)
length1--;
/*if (soundLength1 <= length1) {
length1 = 0;
// sound1 off
if (NR14 & 0x40)
NR52 &= 0xFE;
}*/
}
}
private function tick128():void
{
var waveLen:int;
if ((NR52 & 0x01) && (length1 != 0) && (sweepShift1 != 0)) {
sweep1++;
if (sweep1 >= sweepWait1) {
sweep1 = 0;
waveLen = NR13 | ((NR14 & 0x07) << 8);
//if (waveLen <= 8)
// return;
switch (sweepDirection1) {
case 0: // positive
waveLen = waveLen + (waveLen >> sweepShift1);
//if (waveLen >= 0x800)
// waveLen = 0;
break;
case 1: // negative
waveLen = waveLen - (waveLen >> sweepShift1);
//if (newWaveLen < 0)
// newWaveLen = 0;
break;
}
NR13 = (waveLen & 0xFF);
NR14 &= 0xF8;
NR14 |= ((waveLen >> 8) & 0x07);
}
}
}
private function tick64():void
{
if (NR52 & 0x01) {
env_counter1++;
// decay calc & decay reload
if (env_counter1 >= envelopeWait1) {
env_counter1 = 0;
decay1 += envelopeDirection1;
//decay1 &= 0x0F;
if (decay1 <= 0)
decay1 = 0;
if (decay1 >= 16)
decay1 = 15;
//if (!(NR14 & 0x40)) {
// decay1 = initialEnvelopeVolume1;
//} else {
// ;//decay1 = 0;
//}
//}
//}
}
}
}
}
/**
* ...
*/
class GB
{
import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.ui.Keyboard;
import flash.utils.ByteArray;
private const CY:Array =
[
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
4,12, 8, 8, 4, 4, 8, 4,20, 8, 8, 8, 4, 4, 8, 4,//0
4,12, 8, 8, 4, 4, 8, 4,12, 8, 8, 8, 4, 4, 8, 4,//1
8,12, 8, 8, 4, 4, 8, 4, 8, 8, 8, 8, 4, 4, 8, 4,//2
8,12, 8, 8,12,12,12, 4, 8, 8, 8, 8, 4, 4, 8, 4,//3
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,//4
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,//5
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,//6
8, 8, 8, 8, 8, 8, 4, 8, 4, 4, 4, 4, 4, 4, 8, 4,//7
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,//8
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,//9
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,//A
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,//B
8,12,12,16,12,16, 8,16, 8,16,12, 0,12,24, 8,16,//C
8,12,12, 0,12,16, 8,16, 8,16,12, 0,12, 0, 8,16,//D
12,12, 8, 0, 0,16, 8,16,16, 4,16, 0, 0, 0, 8,16,//E
12,12, 8, 4, 0,16, 8,16,12, 8,16, 4, 0, 0, 8,16 //F
];
private const CY_CB:Array =
[
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,
8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,
8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,
8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8
];
private const ZT:Array =
[
ZF,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
];
private function io_read(adr:uint):uint
{
adr &= 0xFFFF;
var ret:uint;
switch (adr) {
case 0xFF00://P1(パッド制御)
if (P1 == 0x03) {
return 0xFF;
}
switch ((P1 >> 4) & 0x3) {
case 0:
return 0xC0|((padData&0x81?0:1)|(padData&0x42?0:2)|(padData&0x24?0:4)|(padData&0x18?0:8));
case 1:
return 0xD0|((padData&0x01?0:1)|(padData&0x02?0:2)|(padData&0x04?0:4)|(padData&0x08?0:8));
case 2:
return 0xE0|((padData&0x80?0:1)|(padData&0x40?0:2)|(padData&0x20?0:4)|(padData&0x10?0:8));
case 3:
return 0xFF;
}
return 0xDF;
case 0xFF01://SB(シリアル通信送受信)
return SB;
case 0xFF02://SC(シリアルコントロール)
return (SC & 0x83) | 0x7C;
case 0xFF03://不明
return 0xFF;
case 0xFF04: return DIV; //DIV(ディバイダー?)
case 0xFF05: return TIMA; //TIMA(タイマカウンタ)
case 0xFF06: return TMA; //TMA(タイマ調整)
case 0xFF07: return TAC | 0xF8; //TAC(タイマコントロール)
case 0xFF08: // 不明
case 0xFF09:
case 0xFF0A:
case 0xFF0B:
case 0xFF0C:
case 0xFF0D:
case 0xFF0E:
return 0xFF;
case 0xFF0F: return IF; //IF(割りこみフラグ)
case 0xFF40: return LCDC; //LCDC(LCDコントロール)
case 0xFF41: return STAT | 0x80; //STAT(LCDステータス)
case 0xFF42: return SCY; //SCY(スクロールY)
case 0xFF43: return SCX; //SCX(スクロールX)
case 0xFF44: return LY; //LY(LCDC Y座標)
case 0xFF45: return LYC; //LYC(LY比較)
case 0xFF46: return 0; //DMA(DMA転送)
case 0xFF47: return BGP; //BGP(背景パレット)
case 0xFF48: return OBP1; //OBP1(オブジェクトパレット1)
case 0xFF49: return OBP2; //OBP2(オブジェクトパレット2)
case 0xFF4A: return WY; //WY(ウインドウY座標)
case 0xFF4B: return WX; //WX(ウインドウX座標)
case 0xFF4C: return 0xFF;
//以下カラーでの追加
case 0xFF4D://KEY1システムクロック変更
return 0;
case 0xFF4F: return VBK; //VBK(内部VRAMバンク切り替え)
case 0xFF50: return 0xFF;// 不明
case 0xFF51: return dma_src >> 8; //HDMA1(転送元上位)
case 0xFF52: return dma_src & 0xff; //HDMA2(転送元下位)
case 0xFF53: return dma_dest >> 8; //HDMA3(転送先上位)
case 0xFF54: return dma_dest & 0xff; //HDMA4(転送先下位)
case 0xFF55://HDMA5(転送実行)
return 0xFF;
case 0xFF56://RP(赤外線)
return 0;
case 0xFF68: return BCPS; //BCPS(BGパレット書き込み指定)
case 0xFF69://BCPD(BGパレット書きこみデータ)
return ret;
case 0xFF6A: return OCPS; //OCPS(OBJパレット書きこみ指定)
case 0xFF6B://OCPD(OBJパレット書きこみデータ)
return ret;
case 0xFF70: return SVBK; //SVBK(内部RAMバンク切り替え)
case 0xFFFF: return IE; //IE(割りこみマスク)
// undocumented register
case 0xFF6C: return _ff6c & 1;
case 0xFF72: return _ff72;
case 0xFF73: return _ff73;
case 0xFF74: return _ff74;
case 0xFF75: return _ff75 & 0x70;
case 0xFF76: return 0;
case 0xFF77: return 0;
default:
if (adr > 0xFF0F && adr < 0xFF40) {
return apu.read(adr - 0xFF10);
}
else if ((adr > 0xff70) && (adr < 0xff80)) {
return ext_mem[adr - 0xff71] & 0xFF;
} else
return 0xFF;
}
return 0xFF;
}
private function io_write(adr:uint, dat:uint):void
{
adr &= 0xFFFF;
dat &= 0xFF;
switch(adr){
case 0xFF00://P1(パッド制御)
P1 = dat;
return;
case 0xFF01://SB(シリアルシリアル通信送受信)
SB = dat;
return;
case 0xFF02://SC(コントロール)
if (gb_type==1){
SC = dat & 0x81;
}
else{ // GBCでの拡張
SC = dat & 0x83;
}
return;
case 0xFF04://DIV(ディバイダー)
DIV = 0;
return;
case 0xFF05://TIMA(タイマカウンタ)
TIMA = dat;
return;
case 0xFF06://TMA(タイマ調整)
TMA = dat;
return;
case 0xFF07://TAC(タイマコントロール)
if ((dat & 0x04) && !(TAC & 0x04))
sys_clock=0;
TAC = dat;
return;
case 0xFF0F://IF(割りこみフラグ)
IF = dat;
return;
case 0xFF40://LCDC(LCDコントロール)
if ((dat & 0x80) && (!(LCDC & 0x80))) {
LY = 0;
//ref_gb->get_lcd()->clear_win_count();
}
LCDC = dat;
return;
case 0xFF41://STAT(LCDステータス)
if (gb_type == 1) // オリジナルGBにおいてこのような現象が起こるらしい
if (!(STAT & 0x02))
IF |= INT_LCDC;
STAT = (STAT & 0x7) | (dat & 0x78);
return;
case 0xFF42://SCY(スクロールY)
SCY = dat;
return;
case 0xFF43://SCX(スクロールX)
SCX = dat;
return;
case 0xFF44://LY(LCDC Y座標)
//ref_gb->get_lcd()->clear_win_count();
return;
case 0xFF45://LYC(LY比較)
LYC = dat;
return;
case 0xFF46://DMA(DMA転送)
if (dat > 0xF1)
return;
var i:uint;
dat *= 256;
for (i = 0; i < 0xA0; i++)
oam[i] = read(dat + i);
dma_remain = 167;
return;
case 0xFF47://BGP(背景パレット)
BGP = dat;
return;
case 0xFF48://OBP1(オブジェクトパレット1)
OBP1 = dat;
return;
case 0xFF49://OBP2(オブジェクトパレット2)
OBP2 = dat;
return;
case 0xFF4A://WY(ウインドウY座標)
WY = dat;
return;
case 0xFF4B://WX(ウインドウX座標)
WX = dat;
return;
//以下カラーでの追加
case 0xFF4D://KEY1システムクロック変更
KEY1 = dat & 1;
return;
case 0xFF4F://VBK(内部VRAMバンク切り替え)
return;
case 0xFF51://HDMA1(転送元上位)
dma_src &= 0x00F0;
dma_src |= (dat << 8);
return;
case 0xFF52://HDMA2(転送元下位)
dma_src &= 0xFF00;
dma_src |= (dat & 0xF0);
return;
case 0xFF53://HDMA3(転送先上位)
dma_dest &= 0x00F0;
dma_dest |= ((dat & 0xFF) << 8);
return;
case 0xFF54://HDMA4(転送先下位)
dma_dest &= 0xFF00;
dma_dest |= (dat & 0xF0);
return;
case 0xFF55://HDMA5(転送実行)
return;
case 0xFF56://RP(赤外線)
return;
case 0xFF68://BCPS(BGパレット書き込み指定)
BCPS = dat;
return;
case 0xFF69://BCPD(BGパレット書きこみデータ xBBBBBGG GGGRRRRR)
return;
case 0xFF6A://OCPS(OBJパレット書きこみ指定)
OCPS = dat;
return;
case 0xFF6B://OCPD(OBJパレット書きこみデータ)
return;
case 0xFF70://SVBK(内部RAMバンク切り替え)
return;
case 0xFFFF://IE(割りこみマスク)
IE = dat;
return;
// undocumented register
case 0xFF6C: _ff6c = dat & 1; return;
case 0xFF72: _ff72 = dat; return;
case 0xFF73: _ff73 = dat; return;
case 0xFF74: _ff74 = dat; return;
case 0xff75: _ff75 = dat & 0x70; return;
default:
if (adr > 0xFF0F && adr < 0xFF40) {
apu.write(adr - 0xFF10, dat);
//apu.write(adr,dat,total_clock);
return;
}
else if ((adr > 0xff70) && (adr < 0xff80))
ext_mem[adr - 0xff71] = dat;
}
}
public function ADD(arg:uint):void
{
A &= 0xFF;
arg &= 0xFF;
var tmp:uint = A + arg;
var tmpl:uint = tmp & 0xFF;
var tmph:uint = (tmp >> 8) & 0x01;
F = tmph | ZTable[tmpl] | ((A ^ arg ^ tmpl) & HF);
A = tmpl;
}
private function ADC(arg:uint):void
{
A &= 0xFF;
arg &= 0xFF;
var tmp:uint = A + arg + (F & CF);
var tmpl:uint = tmp & 0xFF;
var tmph:uint = (tmp >> 8) & 0x01;
F = tmph | ZTable[tmpl] | ((A ^ arg ^ tmpl) & HF);
A = tmpl;
}
private function SUB(arg:uint):void
{
A &= 0xFF;
arg &= 0xFF;
var tmp:uint = (A - arg) & 0x1FF;
var tmpl:uint = tmp & 0xFF;
var tmph:uint = (tmp >> 8) & 0x01;
F = NF | tmph | ZTable[tmpl] | ((A ^ arg ^ tmpl) & HF);
A = tmpl;
}
private function SBC(arg:uint):void
{
A &= 0xFF;
arg &= 0xFF;
var tmp:uint = (A - arg - (F & CF)) & 0x1FF;
var tmpl:uint = tmp & 0xFF;
var tmph:uint = (tmp >> 8) & 0x01;
F = NF | tmph | ZTable[tmpl] | ((A ^ arg ^ tmpl) & HF);
A = tmpl;
}
private function CP(arg:uint):void
{
A &= 0xFF;
arg &= 0xFF;
var tmp:uint = (A - arg) & 0x1FF;
var tmpl:uint = tmp & 0xFF;
var tmph:uint = (tmp >> 8) & 0x01;
F = NF | tmph | ZTable[tmpl] | ((A ^ arg ^ tmpl) & HF);
}
private function AND(arg:uint):void
{
A &= arg;
A &= 0xFF;
F = HF | ZTable[A];
}
private function OR(arg:uint):void
{
A |= arg;
A &= 0xFF;
F = ZTable[A];
}
private function XOR(arg:uint):void
{
A ^= arg;
A &= 0xFF;
F = ZTable[A];
}
private function INC(arg:uint):uint
{
arg++;
arg &= 0xFF;
F = (F & CF) | ZTable[arg] | ((arg & 0x0F) ? 0 : HF);
return arg;
}
private function DEC(arg:uint):uint
{
arg--;
arg &= 0xFF;
F = NF | (F & CF) | ZTable[arg] | (((arg & 0x0F) == 0x0F) ? HF : 0);
return arg;
}
private function ADDW(arg:uint):void
{
arg &= 0xFFFF;
var hl:uint = (HL & 0xFFFF);
var tmp:uint = hl + arg;
F = (F & ZF) | (((hl ^ arg ^ tmp) & 0x1000) ? HF : 0) | ((tmp & 0x10000) ? CF : 0);
H = (tmp >> 8) & 0xFF;
L = (tmp & 0xFF);
}
public var dataLoaded:Boolean = false;
public var output:BitmapData;
public var cart:GbCart;
private var apu:GbApu = new GbApu();
// VRAM のタイルを入れる
private var tile:Vector.<BitmapData>;
private var gb_type:uint = 1;
public var padData:uint;
public var stopPC:uint = 0;
public var stepFlag:Boolean = false;
public var A:uint, B:uint, C:uint, D:uint, E:uint, F:uint;
public var H:uint, L:uint, SP:uint, PC:uint, cycles:uint, I:uint;
private var int_desable:Boolean, halt:Boolean, tmp_clocks:uint;
private var last_int:uint;
public var count:uint = 0;
private var sys_clock:uint, rest_clock:int, div_clock:uint, total_clock:uint;
private var P1:uint, SB:uint, SC:uint, DIV:uint, TIMA:uint, TMA:uint, TAC:uint;
private var IF:uint, LCDC:uint, STAT:uint, SCY:uint, SCX:uint, LY:uint, LYC:uint;
private var DMA:uint, BGP:uint, OBP1:uint, OBP2:uint, WY:uint, WX:uint, IE:uint;
private var KEY1:uint, VBK:uint, HDMA1:uint, HDMA2:uint, HDMA3:uint, HDMA4:uint;
private var HDMA5:uint, RP:uint, BCPS:uint, BCPD:uint, OCPS:uint, OCPD:uint, SVBK:uint;
private var _ff6c:uint, _ff72:uint, _ff73:uint, _ff74:uint, _ff75:uint;
private var ext_mem:Vector.<uint>;
private var dma_src:uint, dma_dest:uint;
private var dma_remain:uint;
private var z802gb:Vector.<uint>, gb2z80:Vector.<uint>;
private var ram:Vector.<uint>;
private var vram:Vector.<uint>;
private var stack:Vector.<uint>;
private var oam:Vector.<uint>;
private var spare_oam:Vector.<uint>;
public const ZF:uint = 0x40;
public const HF:uint = 0x10;
public const NF:uint = 0x02;
public const CF:uint = 0x01;
public const INT_VBLANK:uint = 1;
public const INT_LCDC:uint = 2;
public const INT_TIMER:uint = 4;
public const INT_SERIAL:uint = 8;
public const INT_PAD:uint = 16;
private var cys:Vector.<uint>;
private var cys_cb:Vector.<uint>;
private var ZTable:Vector.<uint>;
public function GB()
{
initTables();
initRegisters();
initRam();
initRom();
}
private function get2digit(n:uint):String
{
var s:String = n.toString(16).toUpperCase();
if (s.length == 1)
s = "0" + s;
return s;
}
private function get4digit(n:uint):String
{
var s:String = n.toString(16).toUpperCase();
for (var i:uint = 4; i > s.length; i--)
s = "0" + s;
return s;
}
public function setPad(key:uint, value:Boolean):void
{
var padOld:uint;
padOld = padData;
switch (key) {
case Keyboard.RIGHT:
if (value) padData |= 0x80; else padData &= 0x7F;
break;
case Keyboard.LEFT:
if (value) padData |= 0x40; else padData &= 0xBF;
break;
case Keyboard.UP:
if (value) padData |= 0x20; else padData &= 0xDF;
break;
case Keyboard.DOWN:
if (value) padData |= 0x10; else padData &= 0xEF;
break;
case 88: // X (A Button)
if (value) padData |= 0x01; else padData &= 0xFE;
break;
case 90: // Z (B Button)
if (value) padData |= 0x02; else padData &= 0xFD;
break;
case 83: // S (Start Button)
if (value) padData |= 0x08; else padData &= 0xF7;
break;
case 65: // A (Select Button)
if (value) padData |= 0x04; else padData &= 0xFB;
break;
}
if ((padOld & 0x80) != (padData & 0x80))
irq(INT_PAD);
}
private function initRegisters():void
{
A = 0x01; B = 0x00; C = 0x13; D = 0x00;
E = 0xD8; F = 0xB0; H = 0x01; L = 0x4D;
SP = 0xFFFE; PC = 0x100; cycles = 0;
I = 0;
IF = 0;
int_desable = false;
halt = false;
tmp_clocks = 0;
sys_clock = 0;
rest_clock = 0;
div_clock = 0;
total_clock = 0;
DIV = 0x72;
TIMA = 0x00; TMA = 0x00; TAC = 0x00;
LCDC = 0x91; SCY = 0x00; SCX = 0x00; LYC = 0x00;
BGP = 0xFC; OBP1 = 0xFF; OBP2 = 0xFF;
WY = 0x00; WX = 0x00; IE = 0x00;
P1 = 0x00;
}
/**
* 配列になっているデータの初期化
*/
private function initTables():void
{
var i:uint;
z802gb = new Vector.<uint>();
gb2z80 = new Vector.<uint>();
z802gb.length = 256;
z802gb.fixed = true;
gb2z80.length = 256;
gb2z80.fixed = true;
for (i = 0; i < 256; i++) {
z802gb[i] = ((i & 0x40)?0x80:0) | ((i & 0x10)?0x20:0) | ((i & 0x02)?0x40:0) | ((i & 0x01)?0x10:0);
gb2z80[i] = ((i & 0x80)?0x40:0) | ((i & 0x40)?0x02:0) | ((i & 0x20)?0x10:0) | ((i & 0x10)?0x01:0);
}
ext_mem = new Vector.<uint>();
ext_mem.length = 0x10;
ext_mem.fixed = true;
cys = new Vector.<uint>();
cys.length = 0x102;
cys.fixed = true;
cys_cb = new Vector.<uint>();
cys_cb.length = 0x101;
cys_cb.fixed = true;
ZTable = new Vector.<uint>();
ZTable.length = 0x104;
ZTable.fixed = true;
for (i = 0; i < 256; i++) {
cys[i] = CY[i];
cys_cb[i] = CY_CB[i];
ZTable[i] = ZT[i];
}
tile = new Vector.<BitmapData>();
tile.length = 384;
tile.fixed = true;
for (i = 0; i < 384; i++) {
tile[i] = new BitmapData(8, 8);
}
}
public function read_debug_memory(adr:uint):String
{
var s:String = "";
adr &= 0xFFF0;
for (var j:uint = 0; j < 16; j++)
{
s += get4digit(adr + j * 16) + ":";
for (var i:uint = 0; i < 16; i++)
s += get2digit(read(adr + i + j * 16));
s += "\n";
}
return s;
}
private function writeVram(adr:uint, dat:uint):void
{
var i:uint = adr >> 4;
if (i >= 384)
return;
tile[i].lock();
var x:uint;
var y:uint = adr & 0x0F;
var dat2:uint;
var color:uint;
if (y % 2 == 0) {
dat2 = vram[i * 16 + y + 1];
for (x = 0; x < 8; x++)
{
color = 0;
if (dat & (0x80 >> x))
color = 0x555555;
if (dat2 & (0x80 >> x))
color |= 0xAAAAAA;
tile[i].setPixel(x, y / 2, 0xFFFFFF - color);
}
} else {
dat2 = vram[i * 16 + y - 1];
for (x = 0; x < 8; x++)
{
color = 0;
if (dat2 & (0x80 >> x))
color = 0x555555;
if (dat & (0x80 >> x))
color |= 0xAAAAAA;
tile[i].setPixel(x, y / 2, 0xFFFFFF - color);
}
}
tile[i].unlock();
}
public function render(output:BitmapData):void
{
if (!(LCDC & 0x80))
return;
var rect:Rectangle = new Rectangle(0, 0, 8, 8);
output.lock();
// 背景の転送
var x1:int;
var y1:int;
var dx:int;
var dy:int;
var ti:uint;
var ti2:uint;
if (LCDC & 0x01) { // 背景表示
for (y1 = 0; y1 < 32; y1++)
{
if (LCDC & 0x08)
ti = 0x1C00 + y1 * 32;
else
ti = 0x1800 + y1 * 32;
dy = y1 * 8 - SCY;
if (dy < -8)
dy += 256;
if (dy >= 144)
continue;
for (x1 = 0; x1 < 32; x1++)
{
dx = x1 * 8 - SCX;
if (dx < -8)
dx += 256;
if (dx >= 160)
continue;
if (LCDC & 0x10) {
ti2 = vram[ti + x1];
} else {
if (vram[ti + x1] < 128)
ti2 = vram[ti + x1] + 256;
else
ti2 = vram[ti + x1];
}
output.copyPixels(tile[ti2], rect, new Point(dx, dy));
}
}
}
if (LCDC & 0x20 && WX >= 0 && WX <= 166 && WY >= 0 && WY <= 143) { // ウインドウ表示
for (y1 = 0; y1 < 32; y1++)
{
if (LCDC & 0x40)
ti = 0x1C00 + y1 * 32;
else
ti = 0x1800 + y1 * 32;
dy = y1 * 8 + WY;
if (dy <= -8 || dy >= 144)
continue;
for (x1 = 0; x1 < 32; x1++)
{
dx = x1 * 8 + WX - 7;
if (dx <= -8 || dx >= 160)
continue;
if (LCDC & 0x10) {
ti2 = vram[ti + x1];
} else {
if (vram[ti + x1] < 128)
ti2 = vram[ti + x1] + 256;
else
ti2 = vram[ti + x1];
}
//copyPixel(output, tile[ti2], dx, dy, false, false);
output.copyPixels(tile[ti2], rect, new Point(dx, dy));
}
}
}
// スプライトの転送
if (LCDC & 0x02) { // スプライト表示
var y:int;
var x:int;
var p:uint;
var rx:Boolean;
var ry:Boolean;
if (LCDC & 0x04) { // 8x16
for (x1 = 0; x1 < 20; x1++)
{
y = oam[x1 * 8] - 16;
x = oam[x1 * 8 + 1] - 8;
p = oam[x1 * 8 + 2];
rx = (oam[x1 * 8 + 3] & 0x20) ? true : false;
ry = (oam[x1 * 8 + 3] & 0x40) ? true : false;
if (x <= -8 || y <= -8 || x >= 160 || y >= 144)
continue;
copyPixel(output, tile[p], x, y, rx, ry);
p = oam[x1 * 8 + 4 + 2];
copyPixel(output, tile[p], x, y + 8, rx, ry);
//output.copyPixels(tile[p], rect, new Point(x, y));
}
} else {
for (x1 = 0; x1 < 40; x1++)
{
y = oam[x1 * 4] - 16;
x = oam[x1 * 4 + 1] - 8;
p = oam[x1 * 4 + 2];
rx = (oam[x1 * 4 + 3] & 0x20) ? true : false;
ry = (oam[x1 * 4 + 3] & 0x40) ? true : false;
if (x <= -8 || y <= -8 || x >= 160 || y >= 144)
continue;
copyPixel(output, tile[p], x, y, rx, ry);
//output.copyPixels(tile[p], rect, new Point(x, y));
}
}
}
output.unlock();
}
private function copyPixel(dest:BitmapData, source:BitmapData, dx:int, dy:int, rx:Boolean, ry:Boolean):void
{
dest.lock();
//source.lock();
var y:int;
var x:int;
var color:uint;
for (y = 0; y < 8; y++)
{
for (x = 0; x < 8; x++)
{
var drx:int = x;
var dry:int = y;
if (rx)
drx = 7 - x;
if (ry)
dry = 7 - y;
color = source.getPixel(x, y);
if (color == 0xFFFFFF)
continue;
dest.setPixel(drx + dx, dry + dy, color);
}
}
//source.unlock();
dest.unlock();
}
private function initRam():void
{
ram = new Vector.<uint>();
ram.length = 0x2000; // 8KB
ram.fixed = true;
vram = new Vector.<uint>();
vram.length = 0x2000; // 8KB
vram.fixed = true;
stack = new Vector.<uint>();
stack.length = 0x80;
stack.fixed = true;
oam = new Vector.<uint>();
oam.length = 0xA0;
oam.fixed = true;
spare_oam = new Vector.<uint>();
spare_oam.length = 0xA0;
spare_oam.fixed = true;
}
private function initRom():void
{
}
public function loadRom(byteArray:ByteArray):void
{
cart = new GbCart(byteArray);
dataLoaded = true;
//trace("Title : " + cart.title);
//trace("Type : " + cart.type);
//trace("typeCode : " + cart.typeCode);
}
public function get AF():uint
{
A = (A & 0xFF);
F = (F & 0xFF);
return (A << 8) | z802gb[F];
}
public function set AF(arg:uint):void
{
A = ((arg >> 8) & 0xFF);
F = gb2z80[arg & 0xFF];
}
public function get BC():uint
{
B = (B & 0xFF);
C = (C & 0xFF);
return (B << 8) | C;
}
public function set BC(arg:uint):void
{
B = ((arg >> 8) & 0xFF);
C = (arg & 0xFF);
}
public function get DE():uint
{
D = (D & 0xFF);
E = (E & 0xFF);
return (D << 8) | E;
}
public function set DE(arg:uint):void
{
D = ((arg >> 8) & 0xFF);
E = (arg & 0xFF);
}
public function get HL():uint
{
H = (H & 0xFF);
L = (L & 0xFF);
return (H << 8) | L;
}
public function set HL(arg:uint):void
{
H = ((arg >> 8) & 0xFF);
L = (arg & 0xFF);
}
public function read(adr:uint):uint
{
//adr = (adr & 0xFFFF);
switch (adr >> 13)
{
case 0:
case 1:
return cart.read(adr);//ROM領域
case 2:
case 3:
return cart.readBank(adr);//バンク可能ROM
case 4:
return vram[adr & 0x1FFF];//8KBVRAM
case 5:
return cart.readSram(adr & 0x1FFF) & 0xFF;
break;
case 6:
return ram[adr & 0x1fff];// & 0xFF;
case 7:
if (adr < 0xFE00){
return ram[adr & 0x1fff];// & 0xFF; // ram_bank
}
else if (adr < 0xFEA0)
return oam[adr - 0xFE00];// & 0xFF;//object attribute memory
else if (adr < 0xFF00) {
var index:uint;
index = (adr - 0xFEA0) >> 5;
index <<= 3;
index |= (adr & 7);
return spare_