8bitworkshop/src/platform/pce.ts

3848 lines
112 KiB
TypeScript

import { DisasmLine, Platform, Preset, getToolForFilename_6502 } from "../common/baseplatform";
import { Keys, PLATFORMS, RasterVideo } from "../common/emu";
const PCE_PRESETS = [
{ id: 'test_conio.c', name: 'Hello World (conio)' },
{ id: 'siegegame.c', name: 'Siege Game (conio)' },
{ id: 'hello.wiz', name: 'Hello World (Wiz)' },
]
class PCEnginePlatform implements Platform {
mainElement: HTMLElement;
pce: PCE;
video: RasterVideo;
constructor(mainElement) {
this.mainElement = mainElement;
}
start(): void | Promise<void> {
this.pce = new PCE();
this.video = new RasterVideo(this.mainElement, 684, 262, { overscan: true, aspect: 4 / 3 });
this.video.create();
this.pce.SetCanvas(this.video.canvas);
}
reset(): void {
this.pce.Reset();
}
isRunning(): boolean {
return this.pce.TimerID != null;
}
pause(): void {
this.pce.Pause();
}
resume(): void {
this.pce.CreateAudioContext();
this.pce.Start();
}
getPresets(): Preset[] {
return PCE_PRESETS;
}
loadROM(title: string, rom: Uint8Array) {
this.pce.Pause();
this.pce.Init();
this.pce.SetROM(rom);
}
getPlatformName(): string {
return "PC Engine";
}
getToolForFilename(fn: string): string {
return getToolForFilename_6502(fn);
}
getDefaultExtension(): string {
return ".pce";
}
readAddress(addr: number): number {
return this.pce.Get(addr);
}
writeAddress(addr: number, value: number): void {
this.pce.Set(addr, value);
}
readVRAMAddress(addr: number): number {
return this.pce.VDC[0].VRAM[addr];
}
// TODO
/*
disassemble(pc: number, read: (addr: number) => number): DisasmLine {
return disassembleHuC6280(pc, read(pc), read(pc + 1), read(pc + 2));
}
*/
}
PLATFORMS['pce'] = PCEnginePlatform;
/*
https://github.com/yhzmr442/jspce
MIT License
Copyright (c) 2019 yhzmr442
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.
*/
class PCE {
SuperGrafx: boolean;
CountryTypePCE: number;
CountryTypeTG16: number;
CountryType: any;
GamePadButton6: boolean;
MultiTap: boolean;
TimerID: number | null; c
MainCanvas: HTMLCanvasElement | null = null;
Ctx: CanvasRenderingContext2D | null = null;
ImageData: ImageData | null = null;
DrawFlag: boolean;
Mapper: any;
OpCycles: number[];
OpBytes: number[];
A: number;
X: number;
Y: number;
PC: number;
S: number;
P: number;
NZCacheTable: Uint8Array;
NFlag: number;
VFlag: number;
TFlag: number;
BFlag: number;
DFlag: number;
IFlag: number;
ZFlag: number;
CFlag: number;
TIQFlag: number;
IRQ1Flag: number;
IRQ2Flag: number;
ProgressClock: number;
CPUBaseClock: number;
BaseClock1: number;
BaseClock3: number;
BaseClock5: number;
BaseClock7: number;
BaseClock10: number;
TransferSrc: number;
TransferDist: number;
TransferLen: number;
TransferAlt: number;
LastInt: number;
MPRSelect: any;
MPR: any;
RAM: Uint8Array;
RAMMask: number;
BRAM: Uint8Array;
BRAMUse: boolean;
INTIRQ2: number;
IntDisableRegister: number;
MapperBase = class {
ROM: number[];
Core: any;
constructor(rom, core) {
this.ROM = rom;
this.Core = core;
}
Init() {
}
Read(address) {
return 0xFF;
}
Write(address, data) {
}
};
Mapper0: typeof this.MapperBase;
Mapper1: typeof this.MapperBase;
Mapper2: typeof this.MapperBase;
VDC: any[];
INTTIQ: number;
Palette: any[];
PaletteData: any[];
MonoPaletteData: any[];
VCEBaseClock: number;
VCEControl: number;
VCEAddress: number;
VCEData: number;
VPCRegister: Uint8Array;
VPCWindow1: number;
VPCWindow2: number;
VPCPriority: Uint8Array;
VDCPutLineProgressClock: number;
VDCPutLine: number;
VDCLineClock: number;
ScreenSize: any[];
PutScreenSize: any[];
VScreenWidthArray: any[];
ReverseBit: Uint8Array;
ReverseBit16: Uint16Array;
ReverseBit256: Uint32Array;
SPAddressMask: any[];
WaveDataArray: any[];
WaveClockCounter: number;
WaveVolume: number;
WebAudioCtx: AudioContext;
WebAudioJsNode: any;
WebAudioGainNode: any;
WebAudioBufferSize: number;
PSGClock: number;
WaveLfoOn: any;
PSGChannel: any;
WaveVolumeLeft: any;
WaveVolumeRight: any;
PSGBaseClock: any;
PSGProgressClock: number;
WaveLfoControl: number;
WaveLfoFreqency: number;
TimerBaseClock: any;
TimerReload: number;
TimerFlag: boolean;
TimerCounter: number;
TimerPrescaler: number;
JoystickSEL: number;
JoystickCLR: number;
KeyUpFunction: null;
KeyDownFunction: null;
Keybord: any[];
GamePad: any[];
GamePadSelect: number;
GamePadButtonSelect: number;
GamePadBuffer: number;
GamePadData: any[];
GamePadKeyData: { index: number; data: number; }[];
GamePadPovData: number[];
VDCSelect: number = 0;
ScreenWidthMAX = 0;
ScreenHeightMAX = 0;
constructor() {
/* ***************** */
/* **** Setting **** */
/* ***************** */
this.SuperGrafx = false;
this.CountryTypePCE = 0x40;
this.CountryTypeTG16 = 0x00;
this.CountryType = this.CountryTypePCE;
this.GamePadButton6 = false;
this.MultiTap = false;
/* ******************* */
/* **** Construct **** */
/* ******************* */
this.EtcConstruct();
this.CPUConstruct();
this.StorageConstruct();
this.VCEConstruct();
this.VPCConstruct();
this.VDCConstruct();
this.SoundConstruct();
this.PSGConstruct();
this.TimerConstruct();
this.JoystickConstruct();
}
/* ************* */
/* **** ETC **** */
/* ************* */
EtcConstruct() {
this.TimerID = null;
this.MainCanvas = null;
this.Ctx = null;
this.ImageData = null;
}
UpdateAnimationFrame() {
this.TimerID = window.requestAnimationFrame(this.UpdateAnimationFrame.bind(this));
this.Run();
}
CancelAnimationFrame() {
window.cancelAnimationFrame(this.TimerID);
this.TimerID = null;
}
Pause() {
if (this.TimerID != null) {
this.JoystickEventRelease();
this.CancelAnimationFrame();
}
}
Start() {
if (this.TimerID == null) {
this.JoystickEventInit();
this.UpdateAnimationFrame();
}
}
Run() {
this.CheckGamePad();
this.DrawFlag = false;
while (!this.DrawFlag) {
this.CPURun();
this.VDCRun();
this.TimerRun();
this.PSGRun();
}
}
Reset() {
this.StorageReset();
this.Mapper.Init();
this.CPUInit();
this.VCEInit();
this.VPCInit();
this.VDCInit();
this.TimerInit();
this.JoystickInit();
this.PSGInit();
this.CPUReset();
}
Init() {
this.StorageInit();
this.CPUInit();
this.VCEInit();
this.VPCInit();
this.VDCInit();
this.TimerInit();
this.JoystickInit();
this.PSGInit();
}
SetCanvas(canvas: HTMLCanvasElement) {
this.MainCanvas = canvas;
if (!this.MainCanvas.getContext)
return false;
this.Ctx = this.MainCanvas.getContext("2d");
this.ImageData = this.Ctx.createImageData(this.MainCanvas.width, this.MainCanvas.height); // this.ScreenWidthMAX, this.ScreenHeightMAX);
for (let i = 0; i < this.MainCanvas.width * this.MainCanvas.height * 4; i += 4) {
this.ImageData.data[i] = 0;
this.ImageData.data[i + 1] = 0;
this.ImageData.data[i + 2] = 0;
this.ImageData.data[i + 3] = 255;
}
this.Ctx.putImageData(this.ImageData, 0, 0);
return true;
}
/* ************* */
/* **** CPU **** */
/* ************* */
CPUConstruct() {
this.OpCycles = [
8, 7, 3, 4, 6, 4, 6, 7, 3, 2, 2, 2, 7, 5, 7, 6,// 0x00
2, 7, 7, 4, 6, 4, 6, 7, 2, 5, 2, 2, 7, 5, 7, 6,// 0x10
7, 7, 3, 4, 4, 4, 6, 7, 3, 2, 2, 2, 5, 5, 7, 6,// 0x20
2, 7, 7, 2, 4, 4, 6, 7, 2, 5, 2, 2, 5, 5, 7, 6,// 0x30
7, 7, 3, 4, 8, 4, 6, 7, 3, 2, 2, 2, 4, 5, 7, 6,// 0x40
2, 7, 7, 5, 2, 4, 6, 7, 2, 5, 3, 2, 2, 5, 7, 6,// 0x50
7, 7, 2, 2, 4, 4, 6, 7, 3, 2, 2, 2, 7, 5, 7, 6,// 0x60
2, 7, 7, 0, 4, 4, 6, 7, 2, 5, 3, 2, 7, 5, 7, 6,// 0x70
4, 7, 2, 7, 4, 4, 4, 7, 2, 2, 2, 2, 5, 5, 5, 6,// 0x80
2, 7, 7, 8, 4, 4, 4, 7, 2, 5, 2, 2, 5, 5, 5, 6,// 0x90
2, 7, 2, 7, 4, 4, 4, 7, 2, 2, 2, 2, 5, 5, 5, 6,// 0xA0
2, 7, 7, 8, 4, 4, 4, 7, 2, 5, 2, 2, 5, 5, 5, 6,// 0xB0
2, 7, 2, 0, 4, 4, 6, 7, 2, 2, 2, 2, 5, 5, 7, 6,// 0xC0
2, 7, 7, 0, 2, 4, 6, 7, 2, 5, 3, 2, 2, 5, 7, 6,// 0xD0
2, 7, 2, 0, 4, 4, 6, 7, 2, 2, 2, 2, 5, 5, 7, 6,// 0xE0
2, 7, 7, 0, 2, 4, 6, 7, 2, 5, 3, 2, 2, 5, 7, 6];//0xF0
this.OpBytes = [
0, 2, 1, 2, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 0,// 0x00
0, 2, 2, 2, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 0,// 0x10
0, 2, 1, 2, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 0,// 0x20
0, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 0,// 0x30
0, 2, 1, 2, 0, 2, 2, 2, 1, 2, 1, 1, 0, 3, 3, 0,// 0x40
0, 2, 2, 2, 1, 2, 2, 2, 1, 3, 1, 1, 1, 3, 3, 0,// 0x50
0, 2, 1, 1, 2, 2, 2, 2, 1, 2, 1, 1, 0, 3, 3, 0,// 0x60
0, 2, 2, 0, 2, 2, 2, 2, 1, 3, 1, 1, 0, 3, 3, 0,// 0x70
0, 2, 1, 3, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 0,// 0x80
0, 2, 2, 4, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 0,// 0x90
2, 2, 2, 3, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 0,// 0xA0
0, 2, 2, 4, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 0,// 0xB0
2, 2, 1, 0, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 0,// 0xC0
0, 2, 2, 0, 1, 2, 2, 2, 1, 3, 1, 1, 1, 3, 3, 0,// 0xD0
2, 2, 1, 0, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 0,// 0xE0
0, 2, 2, 0, 1, 2, 2, 2, 1, 3, 1, 1, 1, 3, 3, 0];//0xF0
this.A = 0;
this.X = 0;
this.Y = 0;
this.PC = 0;
this.S = 0;
this.P = 0;
this.NZCacheTable = new Uint8Array(0x100);
this.NZCacheTable = this.NZCacheTable.map((d, i) => { return i & 0x80; });
this.NZCacheTable[0x00] = 0x02;
this.NFlag = 0x80;
this.VFlag = 0x40;
this.TFlag = 0x20;
this.BFlag = 0x10;
this.DFlag = 0x08;
this.IFlag = 0x04;
this.ZFlag = 0x02;
this.CFlag = 0x01;
this.TIQFlag = 0x04;
this.IRQ1Flag = 0x02;
this.IRQ2Flag = 0x01;
this.ProgressClock = 0;
this.CPUBaseClock = 0;
this.BaseClock1 = 12;
this.BaseClock3 = 6;
this.BaseClock5 = 4;
this.BaseClock7 = 3;
this.BaseClock10 = 2;
this.TransferSrc = 0;
this.TransferDist = 0;
this.TransferLen = 0;
this.TransferAlt = 0;
}
CPUReset() {
this.TransferSrc = 0;
this.TransferDist = 0;
this.TransferLen = 0;
this.TransferAlt = 0;
this.CPUBaseClock = this.BaseClock1;
this.SetIFlag();
this.PC = this.Get16(0xFFFE);
}
CPUInit() {
this.A = 0;
this.X = 0;
this.Y = 0;
this.PC = 0;
this.S = 0;
this.P = 0x00;
this.ProgressClock = 0;
this.CPUBaseClock = this.BaseClock1;
this.TransferSrc = 0;
this.TransferDist = 0;
this.TransferLen = 0;
this.TransferAlt = 0;
this.LastInt = 0x00;
}
CPURun() {
this.ProgressClock = 0;
let tmp = this.LastInt;
this.LastInt = (this.P & this.IFlag) == 0x00 ? this.GetIntStatus() : 0x00;
if (tmp != 0x00 && this.TransferLen == 0) {
this.LastInt = 0x00;
if ((tmp & this.TIQFlag) == this.TIQFlag) {//TIQ
this.Push(this.PCH());
this.Push(this.PCL());
this.Push(this.P);
this.P = 0x04;
this.PC = this.Get16(0xFFFA);
} else if ((tmp & this.IRQ1Flag) == this.IRQ1Flag) {//IRQ1
this.Push(this.PCH());
this.Push(this.PCL());
this.Push(this.P);
this.P = 0x04;
this.PC = this.Get16(0xFFF8);
} else if ((tmp & this.IRQ2Flag) == this.IRQ2Flag) {//IRQ2
this.Push(this.PCH());
this.Push(this.PCL());
this.Push(this.P);
this.SetIFlag();
this.PC = this.Get16(0xFFF6);
}
this.ProgressClock = 8 * this.CPUBaseClock;
} else
this.OpExec();
}
OpExec() {
let address;
let tmp;
let data;
let bit;
let i;
let op = this.Get(this.PC);
switch (op) {
case 0x69: // ADC IMM
this.ADC(this.PC + 1);
break;
case 0x65: // ADC ZP
this.ADC(this.ZP());
break;
case 0x75: // ADC ZP, X
this.ADC(this.ZP_X());
break;
case 0x72: // ADC (IND)
this.ADC(this.IND());
break;
case 0x61: // ADC (IND, X)
this.ADC(this.IND_X());
break;
case 0x71: // ADC (IND), Y
this.ADC(this.IND_Y());
break;
case 0x6D: // ADC ABS
this.ADC(this.ABS());
break;
case 0x7D: // ADC ABS, X
this.ADC(this.ABS_X());
break;
case 0x79: // ADC ABS, Y
this.ADC(this.ABS_Y());
break;
case 0xE9: // SBC IMM
this.SBC(this.PC + 1);
break;
case 0xE5: // SBC ZP
this.SBC(this.ZP());
break;
case 0xF5: // SBC ZP, X
this.SBC(this.ZP_X());
break;
case 0xF2: // SBC (IND)
this.SBC(this.IND());
break;
case 0xE1: // SBC (IND, X)
this.SBC(this.IND_X());
break;
case 0xF1: // SBC (IND), Y
this.SBC(this.IND_Y());
break;
case 0xED: // SBC ABS
this.SBC(this.ABS());
break;
case 0xFD: // SBC ABS, X
this.SBC(this.ABS_X());
break;
case 0xF9: // SBC ABS, Y
this.SBC(this.ABS_Y());
break;
case 0x29: // AND IMM
this.AND(this.PC + 1);
break;
case 0x25: // AND ZP
this.AND(this.ZP());
break;
case 0x35: // AND ZP, X
this.AND(this.ZP_X());
break;
case 0x32: // AND (IND)
this.AND(this.IND());
break;
case 0x21: // AND (IND, X)
this.AND(this.IND_X());
break;
case 0x31: // AND (IND), Y
this.AND(this.IND_Y());
break;
case 0x2D: // AND ABS
this.AND(this.ABS());
break;
case 0x3D: // AND ABS, X
this.AND(this.ABS_X());
break;
case 0x39: // AND ABS, Y
this.AND(this.ABS_Y());
break;
case 0x49: // EOR IMM
this.EOR(this.PC + 1);
break;
case 0x45: // EOR ZP
this.EOR(this.ZP());
break;
case 0x55: // EOR ZP, X
this.EOR(this.ZP_X());
break;
case 0x52: // EOR (IND)
this.EOR(this.IND());
break;
case 0x41: // EOR (IND, X)
this.EOR(this.IND_X());
break;
case 0x51: // EOR (IND), Y
this.EOR(this.IND_Y());
break;
case 0x4D: // EOR ABS
this.EOR(this.ABS());
break;
case 0x5D: // EOR ABS, X
this.EOR(this.ABS_X());
break;
case 0x59: // EOR ABS, Y
this.EOR(this.ABS_Y());
break;
case 0x09: // ORA IMM
this.ORA(this.PC + 1);
break;
case 0x05: // ORA ZP
this.ORA(this.ZP());
break;
case 0x15: // ORA ZP, X
this.ORA(this.ZP_X());
break;
case 0x12: // ORA (IND)
this.ORA(this.IND());
break;
case 0x01: // ORA (IND, X)
this.ORA(this.IND_X());
break;
case 0x11: // ORA (IND), Y
this.ORA(this.IND_Y());
break;
case 0x0D: // ORA ABS
this.ORA(this.ABS());
break;
case 0x1D: // ORA ABS, X
this.ORA(this.ABS_X());
break;
case 0x19: // ORA ABS, Y
this.ORA(this.ABS_Y());
break;
case 0x06: // ASL ZP
address = this.ZP();
this.Set(address, this.ASL(this.Get(address)));
break;
case 0x16: // ASL ZP, X
address = this.ZP_X();
this.Set(address, this.ASL(this.Get(address)));
break;
case 0x0E: // ASL ABS
address = this.ABS();
this.Set(address, this.ASL(this.Get(address)));
break;
case 0x1E: // ASL ABS, X
address = this.ABS_X();
this.Set(address, this.ASL(this.Get(address)));
break;
case 0x0A: // ASL A
this.A = this.ASL(this.A);
break;
case 0x46: // LSR ZP
address = this.ZP();
this.Set(address, this.LSR(this.Get(address)));
break;
case 0x56: // LSR ZP, X
address = this.ZP_X();
this.Set(address, this.LSR(this.Get(address)));
break;
case 0x4E: // LSR ABS
address = this.ABS();
this.Set(address, this.LSR(this.Get(address)));
break;
case 0x5E: // LSR ABS, X
address = this.ABS_X();
this.Set(address, this.LSR(this.Get(address)));
break;
case 0x4A: // LSR A
this.A = this.LSR(this.A);
break;
case 0x26: // ROL ZP
address = this.ZP();
this.Set(address, this.ROL(this.Get(address)));
break;
case 0x36: // ROL ZP, X
address = this.ZP_X();
this.Set(address, this.ROL(this.Get(address)));
break;
case 0x2E: // ROL ABS
address = this.ABS();
this.Set(address, this.ROL(this.Get(address)));
break;
case 0x3E: // ROL ABS, X
address = this.ABS_X();
this.Set(address, this.ROL(this.Get(address)));
break;
case 0x2A: // ROL A
this.A = this.ROL(this.A);
break;
case 0x66: // ROR ZP
address = this.ZP();
this.Set(address, this.ROR(this.Get(address)));
break;
case 0x76: // ROR ZP, X
address = this.ZP_X();
this.Set(address, this.ROR(this.Get(address)));
break;
case 0x6E: // ROR ABS
address = this.ABS();
this.Set(address, this.ROR(this.Get(address)));
break;
case 0x7E: // ROR ABS, X
address = this.ABS_X();
this.Set(address, this.ROR(this.Get(address)));
break;
case 0x6A: // ROR A
this.A = this.ROR(this.A);
break;
case 0x0F: // BBR0
this.BBRi(0);
break;
case 0x1F: // BBR1
this.BBRi(1);
break;
case 0x2F: // BBR2
this.BBRi(2);
break;
case 0x3F: // BBR3
this.BBRi(3);
break;
case 0x4F: // BBR4
this.BBRi(4);
break;
case 0x5F: // BBR5
this.BBRi(5);
break;
case 0x6F: // BBR6
this.BBRi(6);
break;
case 0x7F: // BBR7
this.BBRi(7);
break;
case 0x8F: // BBS0
this.BBSi(0);
break;
case 0x9F: // BBS1
this.BBSi(1);
break;
case 0xAF: // BBS2
this.BBSi(2);
break;
case 0xBF: // BBS3
this.BBSi(3);
break;
case 0xCF: // BBS4
this.BBSi(4);
break;
case 0xDF: // BBS5
this.BBSi(5);
break;
case 0xEF: // BBS6
this.BBSi(6);
break;
case 0xFF: // BBS7
this.BBSi(7);
break;
case 0x90: // BCC
this.Branch((this.P & this.CFlag) == 0x00, 1);
break;
case 0xB0: // BCS
this.Branch((this.P & this.CFlag) == this.CFlag, 1);
break;
case 0xD0: // BNE
this.Branch((this.P & this.ZFlag) == 0x00, 1);
break;
case 0xF0: // BEQ
this.Branch((this.P & this.ZFlag) == this.ZFlag, 1);
break;
case 0x10: // BPL
this.Branch((this.P & this.NFlag) == 0x00, 1);
break;
case 0x30: // BMI
this.Branch((this.P & this.NFlag) == this.NFlag, 1);
break;
case 0x50: // BVC
this.Branch((this.P & this.VFlag) == 0x00, 1);
break;
case 0x70: // BVS
this.Branch((this.P & this.VFlag) == this.VFlag, 1);
break;
case 0x80: // BRA
this.Branch(true, 1);
break;
case 0x44: // BSR
this.PC++;
this.Push(this.PCH());
this.Push(this.PCL());
this.Branch(true, 0);
break;
case 0x20: // JSR ABS
tmp = this.ABS();
this.PC += 2;
this.Push(this.PCH());
this.Push(this.PCL());
this.PC = tmp;
this.ClearTFlag();
break;
case 0x40: // RTI
this.P = this.Pull();
this.toPCL(this.Pull());
this.toPCH(this.Pull());
break;
case 0x60: // RTS
this.ClearTFlag();
this.toPCL(this.Pull());
this.toPCH(this.Pull());
this.PC++;
break;
case 0x4C: // JMP ABS
this.PC = this.ABS();
this.ClearTFlag();
break;
case 0x6C: // JMP (ABS)
this.PC = this.ABS_IND();
this.ClearTFlag();
break;
case 0x7C: // JMP (ABS, X)
this.PC = this.ABS_X_IND();
this.ClearTFlag();
break;
case 0x00: // BRK
this.PC += 2;
this.Push(this.PCH());
this.Push(this.PCL());
this.SetBFlag();
this.Push(this.P);
this.ClearDFlag();
this.ClearTFlag();
this.SetIFlag();
this.PC = this.Get16(0xFFF6);
break;
case 0x62: // CLA
this.A = 0x00;
this.ClearTFlag();
break;
case 0x82: // CLX
this.X = 0x00;
this.ClearTFlag();
break;
case 0xC2: // CLY
this.Y = 0x00;
this.ClearTFlag();
break;
case 0x18: // CLC
this.ClearCFlag();
this.ClearTFlag();
break;
case 0xD8: // CLD
this.ClearDFlag();
this.ClearTFlag();
break;
case 0x58: // CLI
this.ClearIFlag();
this.ClearTFlag();
break;
case 0xB8: // CLV
this.ClearVFlag();
this.ClearTFlag();
break;
case 0x38: // SEC
this.SetCFlag();
this.ClearTFlag();
break;
case 0xF8: // SED
this.SetDFlag();
this.ClearTFlag();
break;
case 0x78: // SEI
this.SetIFlag();
this.ClearTFlag();
break;
case 0xF4: // SET
this.SetTFlag();
break;
case 0xC9: // CMP IMM
this.Compare(this.A, this.PC + 1);
break;
case 0xC5: // CMP ZP
this.Compare(this.A, this.ZP());
break;
case 0xD5: // CMP ZP, X
this.Compare(this.A, this.ZP_X());
break;
case 0xD2: // CMP (IND)
this.Compare(this.A, this.IND());
break;
case 0xC1: // CMP (IND, X)
this.Compare(this.A, this.IND_X());
break;
case 0xD1: // CMP (IND), Y
this.Compare(this.A, this.IND_Y());
break;
case 0xCD: // CMP ABS
this.Compare(this.A, this.ABS());
break;
case 0xDD: // CMP ABS, X
this.Compare(this.A, this.ABS_X());
break;
case 0xD9: // CMP ABS, Y
this.Compare(this.A, this.ABS_Y());
break;
case 0xE0: // CPX IMM
this.Compare(this.X, this.PC + 1);
break;
case 0xE4: // CPX ZP
this.Compare(this.X, this.ZP());
break;
case 0xEC: // CPX ABS
this.Compare(this.X, this.ABS());
break;
case 0xC0: // CPY IMM
this.Compare(this.Y, this.PC + 1);
break;
case 0xC4: // CPY ZP
this.Compare(this.Y, this.ZP());
break;
case 0xCC: // CPY ABS
this.Compare(this.Y, this.ABS());
break;
case 0xC6: // DEC ZP
address = this.ZP();
this.Set(address, this.Decrement(this.Get(address)));
break;
case 0xD6: // DEC ZP, X
address = this.ZP_X();
this.Set(address, this.Decrement(this.Get(address)));
break;
case 0xCE: // DEC ABS
address = this.ABS();
this.Set(address, this.Decrement(this.Get(address)));
break;
case 0xDE: // DEC ABS, X
address = this.ABS_X();
this.Set(address, this.Decrement(this.Get(address)));
break;
case 0x3A: // DEC A
this.A = this.Decrement(this.A);
break;
case 0xCA: // DEX
this.X = this.Decrement(this.X);
break;
case 0x88: // DEY
this.Y = this.Decrement(this.Y);
break;
case 0xE6: // INC ZP
address = this.ZP();
this.Set(address, this.Increment(this.Get(address)));
break;
case 0xF6: // INC ZP, X
address = this.ZP_X();
this.Set(address, this.Increment(this.Get(address)));
break;
case 0xEE: // INC ABS
address = this.ABS();
this.Set(address, this.Increment(this.Get(address)));
break;
case 0xFE: // INC ABS, X
address = this.ABS_X();
this.Set(address, this.Increment(this.Get(address)));
break;
case 0x1A: // INC A
this.A = this.Increment(this.A);
break;
case 0xE8: // INX
this.X = this.Increment(this.X);
break;
case 0xC8: // INY
this.Y = this.Increment(this.Y);
break;
case 0x48: // PHA
this.Push(this.A);
this.ClearTFlag();
break;
case 0x08: // PHP
this.Push(this.P);
this.ClearTFlag();
break;
case 0xDA: // PHX
this.Push(this.X);
this.ClearTFlag();
break;
case 0x5A: // PHY
this.Push(this.Y);
this.ClearTFlag();
break;
case 0x68: // PLA
this.A = this.Pull();
this.SetNZFlag(this.A);
this.ClearTFlag();
break;
case 0x28: // PLP
this.P = this.Pull();
break;
case 0xFA: // PLX
this.X = this.Pull();
this.SetNZFlag(this.X);
this.ClearTFlag();
break;
case 0x7A: // PLY
this.Y = this.Pull();
this.SetNZFlag(this.Y);
this.ClearTFlag();
break;
case 0x07: // RMB0
this.RMBi(0);
break;
case 0x17: // RMB1
this.RMBi(1);
break;
case 0x27: // RMB2
this.RMBi(2);
break;
case 0x37: // RMB3
this.RMBi(3);
break;
case 0x47: // RMB4
this.RMBi(4);
break;
case 0x57: // RMB5
this.RMBi(5);
break;
case 0x67: // RMB6
this.RMBi(6);
break;
case 0x77: // RMB7
this.RMBi(7);
break;
case 0x87: // SMB0
this.SMBi(0);
break;
case 0x97: // SMB1
this.SMBi(1);
break;
case 0xA7: // SMB2
this.SMBi(2);
break;
case 0xB7: // SMB3
this.SMBi(3);
break;
case 0xC7: // SMB4
this.SMBi(4);
break;
case 0xD7: // SMB5
this.SMBi(5);
break;
case 0xE7: // SMB6
this.SMBi(6);
break;
case 0xF7: // SMB7
this.SMBi(7);
break;
case 0x22: // SAX
tmp = this.A;
this.A = this.X;
this.X = tmp;
this.ClearTFlag();
break;
case 0x42: // SAY
tmp = this.A;
this.A = this.Y;
this.Y = tmp;
this.ClearTFlag();
break;
case 0x02: // SXY
tmp = this.X;
this.X = this.Y;
this.Y = tmp;
this.ClearTFlag();
break;
case 0xAA: // TAX
this.X = this.A;
this.SetNZFlag(this.X);
this.ClearTFlag();
break;
case 0xA8: // TAY
this.Y = this.A;
this.SetNZFlag(this.Y);
this.ClearTFlag();
break;
case 0xBA: // TSX
this.X = this.S;
this.SetNZFlag(this.X);
this.ClearTFlag();
break;
case 0x8A: // TXA
this.A = this.X;
this.SetNZFlag(this.A);
this.ClearTFlag();
break;
case 0x9A: // TXS
this.S = this.X;
this.ClearTFlag();
break;
case 0x98: // TYA
this.A = this.Y;
this.SetNZFlag(this.A);
this.ClearTFlag();
break;
case 0x89: // BIT IMM
this.BIT(this.PC + 1);
break;
case 0x24: // BIT ZP
this.BIT(this.ZP());
break;
case 0x34: // BIT ZP, X
this.BIT(this.ZP_X());
break;
case 0x2C: // BIT ABS
this.BIT(this.ABS());
break;
case 0x3C: // BIT ABS, X
this.BIT(this.ABS_X());
break;
case 0x83: // TST IMM ZP
this.TST(this.PC + 1, 0x2000 | this.Get(this.PC + 2));
break;
case 0xA3: // TST IMM ZP, X
this.TST(this.PC + 1, 0x2000 | ((this.Get(this.PC + 2) + this.X) & 0xFF));
break;
case 0x93: // TST IMM ABS
this.TST(this.PC + 1, this.Get16(this.PC + 2));
break;
case 0xB3: // TST IMM ABS, X
this.TST(this.PC + 1, (this.Get16(this.PC + 2) + this.X) & 0xFFFF);
break;
case 0x14: // TRB ZP
this.TRB(this.ZP())
break;
case 0x1C: // TRB ABS
this.TRB(this.ABS())
break;
case 0x04: // TSB ZP
this.TSB(this.ZP())
break;
case 0x0C: // TSB ABS
this.TSB(this.ABS())
break;
case 0xA9: // LDA IMM
this.A = this.Load(this.PC + 1);
break;
case 0xA5: // LDA ZP
this.A = this.Load(this.ZP());
break;
case 0xB5: // LDA ZP, X
this.A = this.Load(this.ZP_X());
break;
case 0xB2: // LDA (IND)
this.A = this.Load(this.IND());
break;
case 0xA1: // LDA (IND, X)
this.A = this.Load(this.IND_X());
break;
case 0xB1: // LDA (IND), Y
this.A = this.Load(this.IND_Y());
break;
case 0xAD: // LDA ABS
this.A = this.Load(this.ABS());
break;
case 0xBD: // LDA ABS, X
this.A = this.Load(this.ABS_X());
break;
case 0xB9: // LDA ABS, Y
this.A = this.Load(this.ABS_Y());
break;
case 0xA2: // LDX IMM
this.X = this.Load(this.PC + 1);
break;
case 0xA6: // LDX ZP
this.X = this.Load(this.ZP());
break;
case 0xB6: // LDX ZP, Y
this.X = this.Load(this.ZP_Y());
break;
case 0xAE: // LDX ABS
this.X = this.Load(this.ABS());
break;
case 0xBE: // LDX ABS, Y
this.X = this.Load(this.ABS_Y());
break;
case 0xA0: // LDY IMM
this.Y = this.Load(this.PC + 1);
break;
case 0xA4: // LDY ZP
this.Y = this.Load(this.ZP());
break;
case 0xB4: // LDY ZP, X
this.Y = this.Load(this.ZP_X());
break;
case 0xAC: // LDY ABS
this.Y = this.Load(this.ABS());
break;
case 0xBC: // LDY ABS, X
this.Y = this.Load(this.ABS_X());
break;
case 0x85: // STA ZP
this.Store(this.ZP(), this.A);
break;
case 0x95: // STA ZP, X
this.Store(this.ZP_X(), this.A);
break;
case 0x92: // STA (IND)
this.Store(this.IND(), this.A);
break;
case 0x81: // STA (IND, X)
this.Store(this.IND_X(), this.A);
break;
case 0x91: // STA (IND), Y
this.Store(this.IND_Y(), this.A);
break;
case 0x8D: // STA ABS
this.Store(this.ABS(), this.A);
break;
case 0x9D: // STA ABS, X
this.Store(this.ABS_X(), this.A);
break;
case 0x99: // STA ABS, Y
this.Store(this.ABS_Y(), this.A);
break;
case 0x86: // STX ZP
this.Store(this.ZP(), this.X);
break;
case 0x96: // STX ZP, Y
this.Store(this.ZP_Y(), this.X);
break;
case 0x8E: // STX ABS
this.Store(this.ABS(), this.X);
break;
case 0x84: // STY ZP
this.Store(this.ZP(), this.Y);
break;
case 0x94: // STY ZP, X
this.Store(this.ZP_X(), this.Y);
break;
case 0x8C: // STY ABS
this.Store(this.ABS(), this.Y);
break;
case 0x64: // STZ ZP
this.Store(this.ZP(), 0x00);
break;
case 0x74: // STZ ZP, X
this.Store(this.ZP_X(), 0x00);
break;
case 0x9C: // STZ ABS
this.Store(this.ABS(), 0x00);
break;
case 0x9E: // STZ ABS, X
this.Store(this.ABS_X(), 0x00);
break;
case 0xEA: // NOP
this.ClearTFlag();
break;
case 0x03: // ST0
this.SetVDCRegister(this.Get(this.PC + 1), this.VDCSelect);
this.ClearTFlag();
break;
case 0x13: // ST1
this.SetVDCLow(this.Get(this.PC + 1), this.VDCSelect);
this.ClearTFlag();
break;
case 0x23: // ST2
this.SetVDCHigh(this.Get(this.PC + 1), this.VDCSelect);
this.ClearTFlag();
break;
case 0x53: // TAMi
data = this.Get(this.PC + 1);
bit = 0x01;
if (data == 0x00)
data = this.MPRSelect;
else
this.MPRSelect = data;
for (i = 0; i < 8; i++)
if ((data & (bit << i)) != 0x00)
this.MPR[i] = this.A << 13;
break;
case 0x43: // TMAi
data = this.Get(this.PC + 1);
bit = 0x01;
if (data == 0x00)
data = this.MPRSelect;
else
this.MPRSelect = data;
for (i = 0; i < 8; i++)
if ((data & (bit << i)) != 0x00)
this.A = this.MPR[i] >>> 13;
break;
case 0xF3: // TAI
if (this.TransferLen == 0) {
this.TransferSrc = this.Get16(this.PC + 1);
this.TransferDist = this.Get16(this.PC + 3);
this.TransferLen = this.Get16(this.PC + 5);
this.TransferAlt = 1;
this.ProgressClock = 17;
}
this.Set(this.TransferDist, this.Get(this.TransferSrc));
this.TransferSrc = (this.TransferSrc + this.TransferAlt) & 0xFFFF;
this.TransferDist = (this.TransferDist + 1) & 0xFFFF;
this.TransferLen = (this.TransferLen - 1) & 0xFFFF;
this.TransferAlt = this.TransferAlt == 1 ? -1 : 1;
this.ProgressClock += 6;
if (this.TransferLen == 0) {
this.ClearTFlag();
this.PC += 7;
}
break;
case 0xC3: // TDD
if (this.TransferLen == 0) {
this.TransferSrc = this.Get16(this.PC + 1);
this.TransferDist = this.Get16(this.PC + 3);
this.TransferLen = this.Get16(this.PC + 5);
this.ProgressClock = 17;
}
this.Set(this.TransferDist, this.Get(this.TransferSrc));
this.TransferSrc = (this.TransferSrc - 1) & 0xFFFF;
this.TransferDist = (this.TransferDist - 1) & 0xFFFF;
this.TransferLen = (this.TransferLen - 1) & 0xFFFF;
this.ProgressClock += 6;
if (this.TransferLen == 0) {
this.ClearTFlag();
this.PC += 7;
}
break;
case 0xE3: // TIA
if (this.TransferLen == 0) {
this.TransferSrc = this.Get16(this.PC + 1);
this.TransferDist = this.Get16(this.PC + 3);
this.TransferLen = this.Get16(this.PC + 5);
this.TransferAlt = 1;
this.ProgressClock = 17;
}
this.Set(this.TransferDist, this.Get(this.TransferSrc));
this.TransferSrc = (this.TransferSrc + 1) & 0xFFFF;
this.TransferDist = (this.TransferDist + this.TransferAlt) & 0xFFFF;
this.TransferLen = (this.TransferLen - 1) & 0xFFFF;
this.TransferAlt = this.TransferAlt == 1 ? -1 : 1;
this.ProgressClock += 6;
if (this.TransferLen == 0) {
this.ClearTFlag();
this.PC += 7;
}
break;
case 0x73: // TII
if (this.TransferLen == 0) {
this.TransferSrc = this.Get16(this.PC + 1);
this.TransferDist = this.Get16(this.PC + 3);
this.TransferLen = this.Get16(this.PC + 5);
this.ProgressClock = 17;
}
this.Set(this.TransferDist, this.Get(this.TransferSrc));
this.TransferSrc = (this.TransferSrc + 1) & 0xFFFF;
this.TransferDist = (this.TransferDist + 1) & 0xFFFF;
this.TransferLen = (this.TransferLen - 1) & 0xFFFF;
this.ProgressClock += 6;
if (this.TransferLen == 0) {
this.ClearTFlag();
this.PC += 7;
}
break;
case 0xD3: // TIN
if (this.TransferLen == 0) {
this.TransferSrc = this.Get16(this.PC + 1);
this.TransferDist = this.Get16(this.PC + 3);
this.TransferLen = this.Get16(this.PC + 5);
this.ProgressClock = 17;
}
this.Set(this.TransferDist, this.Get(this.TransferSrc));
this.TransferSrc = (this.TransferSrc + 1) & 0xFFFF;
this.TransferLen = (this.TransferLen - 1) & 0xFFFF;
this.ProgressClock += 6;
if (this.TransferLen == 0) {
this.ClearTFlag();
this.PC += 7;
}
break;
case 0xD4: // CSH
this.ClearTFlag();
this.CPUBaseClock = this.BaseClock7;
break;
case 0x54: // CSL
this.ClearTFlag();
this.CPUBaseClock = this.BaseClock1;
break;
default:
this.ClearTFlag();//NOP
break;
}
this.PC += this.OpBytes[op];
this.ProgressClock = (this.ProgressClock + this.OpCycles[op]) * this.CPUBaseClock;
}
Adder(address, neg) {
let data0;
let data1 = this.Get(address);
if (!neg && (this.P & this.TFlag) == this.TFlag) {
this.ProgressClock = 3;
data0 = this.Get(0x2000 | this.X);
} else
data0 = this.A;
if (neg)
data1 = ~data1 & 0xFF;
let carry = this.P & 0x01;
let tmp = data0 + data1 + carry;
if ((this.P & this.DFlag) == 0x00) {
if ((((~data0 & ~data1 & tmp) | (data0 & data1 & ~tmp)) & 0x80) == 0x80)
this.SetVFlag();
else
this.ClearVFlag();
} else {
this.ProgressClock += 1;
if (neg) {
if ((tmp & 0x0F) > 0x09)
tmp -= 0x06;
if ((tmp & 0xF0) > 0x90)
tmp -= 0x60;
} else {
if (((data0 & 0x0F) + (data1 & 0x0F) + carry) > 0x09)
tmp += 0x06;
if ((tmp & 0x1F0) > 0x90)
tmp += 0x60;
}
}
if (tmp > 0xFF)
this.SetCFlag();
else
this.ClearCFlag();
tmp &= 0xFF;
this.SetNZFlag(tmp);
if (!neg && (this.P & this.TFlag) == this.TFlag)
this.Set(0x2000 | this.X, tmp);
else
this.A = tmp;
this.ClearTFlag();
}
ADC(address) {
this.Adder(address, false);
}
SBC(address) {
this.Adder(address, true);
}
AND(address) {
let data0;
let data1 = this.Get(address);
if ((this.P & this.TFlag) == 0x00) {
data0 = this.A;
} else {
this.ProgressClock = 3;
data0 = this.Get(0x2000 | this.X);
}
let tmp = data0 & data1;
this.SetNZFlag(tmp);
if ((this.P & this.TFlag) == 0x00)
this.A = tmp;
else
this.Set(0x2000 | this.X, tmp);
this.ClearTFlag();
}
EOR(address) {
let data0;
let data1 = this.Get(address);
if ((this.P & this.TFlag) == 0x00) {
data0 = this.A;
} else {
this.ProgressClock = 3;
data0 = this.Get(0x2000 | this.X);
}
let tmp = data0 ^ data1;
this.SetNZFlag(tmp);
if ((this.P & this.TFlag) == 0x00)
this.A = tmp;
else
this.Set(0x2000 | this.X, tmp);
this.ClearTFlag();
}
ORA(address) {
let data0;
let data1 = this.Get(address);
if ((this.P & this.TFlag) == 0x00) {
data0 = this.A;
} else {
this.ProgressClock = 3;
data0 = this.Get(0x2000 | this.X);
}
let tmp = data0 | data1;
this.SetNZFlag(tmp);
if ((this.P & this.TFlag) == 0x00)
this.A = tmp;
else
this.Set(0x2000 | this.X, tmp);
this.ClearTFlag();
}
ASL(data) {
data <<= 1;
if (data > 0xFF)
this.SetCFlag();
else
this.ClearCFlag();
data &= 0xFF;
this.SetNZFlag(data);
this.ClearTFlag();
return data;
}
LSR(data) {
if ((data & 0x01) == 0x01)
this.SetCFlag();
else
this.ClearCFlag();
data >>= 1;
this.SetNZFlag(data);
this.ClearTFlag();
return data;
}
ROL(data) {
data = (data << 1) | (this.P & 0x01);
if (data > 0xFF)
this.SetCFlag();
else
this.ClearCFlag();
data &= 0xFF;
this.SetNZFlag(data);
this.ClearTFlag();
return data;
}
ROR(data) {
let tmp = this.P & this.CFlag;
if ((data & 0x01) == 0x01)
this.SetCFlag();
else
this.ClearCFlag();
data = (data >> 1) | (tmp << 7);
this.SetNZFlag(data);
this.ClearTFlag();
return data;
}
BBRi(bit) {
let tmp = this.Get(this.ZP());
tmp = (tmp >> bit) & 0x01;
this.Branch(tmp == 0, 2);
}
BBSi(bit) {
let tmp = this.Get(this.ZP());
tmp = (tmp >> bit) & 0x01;
this.Branch(tmp == 1, 2);
}
Branch(status, adr) {
this.ClearTFlag();
if (status) {
let tmp = this.Get(this.PC + adr);
if (tmp >= 0x80)
tmp |= 0xFF00;
this.PC = (this.PC + adr + 1 + tmp) & 0xFFFF;
this.ProgressClock = 2;
} else
this.PC += adr + 1;
}
Compare(data0, data1) {
data0 -= this.Get(data1);
if (data0 < 0)
this.ClearCFlag();
else
this.SetCFlag();
this.ClearTFlag();
this.SetNZFlag(data0 & 0xFF);
}
Decrement(data) {
data = (data - 1) & 0xFF;
this.SetNZFlag(data);
this.ClearTFlag();
return data;
}
Increment(data) {
data = (data + 1) & 0xFF;
this.SetNZFlag(data);
this.ClearTFlag();
return data;
}
Push(data) {
this.Set(0x2100 | this.S, data);
this.S = (this.S - 1) & 0xFF;
}
Pull() {
this.S = (this.S + 1) & 0xFF;
return this.Get(0x2100 | this.S);
}
RMBi(bit) {
let address = this.ZP();
this.Set(address, this.Get(address) & ~(0x01 << bit));
this.ClearTFlag();
}
SMBi(bit) {
let address = this.ZP();
this.Set(address, this.Get(address) | (0x01 << bit));
this.ClearTFlag();
}
BIT(address) {
let tmp = this.Get(address);
this.SetNZFlag(this.A & tmp);
this.P = (this.P & ~(this.NFlag | this.VFlag)) | (tmp & (this.NFlag | this.VFlag));
this.ClearTFlag();
}
TST(address0, address1) {
let tmp0 = this.Get(address0);
let tmp1 = this.Get(address1);
this.SetNZFlag(tmp0 & tmp1);
this.P = (this.P & ~(this.NFlag | this.VFlag)) | (tmp1 & (this.NFlag | this.VFlag));
this.ClearTFlag();
}
TRB(address) {
let tmp = this.Get(address);
let res = ~this.A & tmp;
this.Set(address, res);
this.SetNZFlag(res);
this.P = (this.P & ~(this.NFlag | this.VFlag)) | (tmp & (this.NFlag | this.VFlag));
this.ClearTFlag();
}
TSB(address) {
let tmp = this.Get(address);
let res = this.A | tmp;
this.Set(address, res);
this.SetNZFlag(res);
this.P = (this.P & ~(this.NFlag | this.VFlag)) | (tmp & (this.NFlag | this.VFlag));
this.ClearTFlag();
}
Load(address) {
let data = this.Get(address);
this.SetNZFlag(data);
this.ClearTFlag();
return data;
}
Store(address, data) {
this.Set(address, data);
this.ClearTFlag();
}
ZP() {
return 0x2000 | this.Get(this.PC + 1);
}
ZP_X() {
return 0x2000 | ((this.Get(this.PC + 1) + this.X) & 0xFF);
}
ZP_Y() {
return 0x2000 | ((this.Get(this.PC + 1) + this.Y) & 0xFF);
}
IND() {
return this.Get16(0x2000 | this.Get(this.PC + 1));
}
IND_X() {
return this.Get16(0x2000 | ((this.Get(this.PC + 1) + this.X) & 0xFF));
}
IND_Y() {
return (this.Get16(0x2000 | this.Get(this.PC + 1)) + this.Y) & 0xFFFF;
}
ABS() {
return this.Get16(this.PC + 1);
}
ABS_X() {
return (this.Get16(this.PC + 1) + this.X) & 0xFFFF;
}
ABS_Y() {
return (this.Get16(this.PC + 1) + this.Y) & 0xFFFF;
}
ABS_IND() {
return this.Get16(this.Get16(this.PC + 1));
}
ABS_X_IND() {
return this.Get16((this.Get16(this.PC + 1) + this.X) & 0xFFFF);
}
SetNZFlag(data) { // Set N Z Flags
this.P = (this.P & ~(this.NFlag | this.ZFlag)) | this.NZCacheTable[data];
}
SetVFlag() { // Set V Flag
this.P |= this.VFlag;
}
ClearVFlag() { // Clear V Flag
this.P &= ~this.VFlag;
}
SetTFlag() { // Set T Flag
this.P |= this.TFlag;
}
ClearTFlag() { // Clear T Flag
this.P &= ~this.TFlag;
}
SetBFlag() { // Set B Flag
this.P |= this.BFlag;
}
ClearBFlag() { // Clear B Flag
this.P &= ~this.BFlag;
}
SetDFlag() { // Set D Flag
this.P |= this.DFlag;
}
ClearDFlag() { // Clear D Flag
this.P &= ~this.DFlag;
}
SetIFlag() { // Set I Flag
this.P |= this.IFlag;
}
ClearIFlag() { // Clear I Flag
this.P &= ~this.IFlag;
}
SetCFlag() { // Set C Flag
this.P |= this.CFlag;
}
ClearCFlag() { // Clear C Flag
this.P &= ~this.CFlag;
}
PCH() {
return this.PC >> 8;
}
PCL() {
return this.PC & 0x00FF;
}
toPCH(data) {
this.PC = (this.PC & 0x00FF) | (data << 8);
}
toPCL(data) {
this.PC = (this.PC & 0xFF00) | data;
}
/* ***************** */
/* **** Storage **** */
/* ***************** */
StorageConstruct() {
this.MPR = new Array(8);
this.MPRSelect = 0;
this.RAM = new Uint8Array(0x8000);
this.RAMMask = 0x1FFF;
this.BRAM = new Uint8Array(0x2000).fill(0x00);
this.BRAMUse = false;
this.INTIRQ2 = 0x00;
this.IntDisableRegister = 0;
this.Mapper = null;
this.Mapper0 = class extends this.MapperBase {
Address: number;
constructor(rom, core) {
super(rom, core);
let tmp = this.ROM.length - 1;
this.Address = 0x80000;
while (this.Address > 0x0000) {
if ((this.Address & tmp) != 0x0000)
break;
this.Address >>>= 1;
}
}
Read(address) {
if (address >= this.ROM.length)
return this.ROM[(address & (this.Address - 1)) | this.Address];
else
return this.ROM[address];
}
};
this.Mapper1 = class extends this.MapperBase {
ROM: any;
Address: number;
constructor(rom, core) {
super(rom, core);
this.Address = 0;
}
Init() {
this.Address = 0;
}
Read(address) {
if (address < 0x80000)
return this.ROM[address];
else
return this.ROM[this.Address | (address & 0x7FFFF)];
}
Write(address, data) {
this.Address = ((address & 0x000F) + 1) << 19;
}
};
this.Mapper2 = class extends this.MapperBase {
ROM: any;
constructor(rom, core) {
super(rom, core);
this.ROM = rom;
}
Read(address) {
return this.ROM[address] || 0;
}
Write(address, data) {
if (address >= 0x80000)
this.ROM[address] = data;
}
};
}
GetIntStatus() {
return ~this.IntDisableRegister & this.GetIntReqest();
}
GetIntDisable() {
return this.IntDisableRegister;
}
SetIntDisable(data) {
this.IntDisableRegister = data;
this.TimerAcknowledge();
}
GetIntReqest() {
return (((this.VDC[0].VDCStatus | this.VDC[1].VDCStatus) & 0x3F) != 0x00 ? this.IRQ1Flag : 0x00) | this.INTIRQ2 | this.INTTIQ;
}
SetIntReqest(data) {
this.TimerAcknowledge();
}
SetROM(rom) {
this.Init();
let tmp = rom.slice(rom.length % 8192);
//if(tmp[0x001FFF] < 0xE0)
// tmp = tmp.map((d) => {return this.ReverseBit[d];});
this.Mapper = new this.Mapper0(tmp, this);
this.CPUReset();
}
StorageInit() {
this.RAM.fill(0x00);
this.RAMMask = this.SuperGrafx ? 0x7FFF : 0x1FFF;
this.StorageReset();
}
StorageReset() {
this.IntDisableRegister = 0x00;//IntInit
for (let i = 0; i < 7; i++)
this.MPR[i] = 0xFF << 13;
this.MPR[7] = 0x00;
this.MPRSelect = 0x01;
}
Get16(address) {
return (this.Get(address + 1) << 8) | this.Get(address);
}
Get(address) {
address = this.MPR[address >> 13] | (address & 0x1FFF);
if (address < 0x100000)// ROM
return this.Mapper.Read(address);
if (address < 0x1EE000)// NOT USE
return 0xFF;
if (address < 0x1F0000) {// BRAM
if (this.BRAMUse)
return this.BRAM[address & 0x1FFF];
else
return 0xFF;
}
if (address < 0x1F8000)// RAM
return this.RAM[address & this.RAMMask];
if (address < 0x1FE000)// NOT USE
return 0xFF;
if (address < 0x1FE400) {// VDC
if (this.SuperGrafx) {
let tmp = address & 0x00001F;
if (tmp < 0x00008) {
switch (address & 0x000003) {// VDC#1
case 0x00:
return this.GetVDCStatus(0);
case 0x01:
return 0x00;
case 0x02:
return this.GetVDCLow(0);
case 0x03:
return this.GetVDCHigh(0);
}
} else if (tmp < 0x00010) {// VPC
return this.GetVPC(tmp & 0x000007);
} else if (tmp < 0x00018) {// VDC#2
switch (address & 0x000003) {
case 0x00:
return this.GetVDCStatus(1);
case 0x01:
return 0x00;
case 0x02:
return this.GetVDCLow(1);
case 0x03:
return this.GetVDCHigh(1);
}
} else {
return 0xFF;
}
} else {
switch (address & 0x000003) {// VDC#1
case 0x00:
return this.GetVDCStatus(0);
case 0x01:
return 0x00;
case 0x02:
return this.GetVDCLow(0);
case 0x03:
return this.GetVDCHigh(0);
}
}
}
if (address < 0x1FE800) {// VCE
switch (address & 0x000007) {
case 0x04:
return this.GetVCEDataLow();
case 0x05:
return this.GetVCEDataHigh();
default:
return 0x00;
}
}
if (address < 0x1FEC00)// PSG
return this.GetPSG(address & 0x00000F);
if (address < 0x1FF000)// TIMER
return this.ReadTimerCounter();
if (address < 0x1FF400)// IO
return this.GetJoystick();
if (address < 0x1FF800) {// INT Register
switch (address & 0x000003) {
case 0x02:
return this.GetIntDisable();
case 0x03:
return this.GetIntReqest();
default:
return 0x00;
}
}
return 0xFF;//EXT
}
Set(address, data) {
address = this.MPR[address >> 13] | (address & 0x1FFF);
if (address < 0x100000) {// ROM
this.Mapper.Write(address, data);
return;
}
if (address < 0x1EE000)// NOT USE
return;
if (address < 0x1F0000) {// BRAM
if (this.BRAMUse)
this.BRAM[address & 0x1FFF] = data;
return;
}
if (address < 0x1F8000) {// RAM
this.RAM[address & this.RAMMask] = data;
return;
}
if (address < 0x1FE000)// NOT USE
return;
if (address < 0x1FE400) {// VDC
if (this.SuperGrafx) {
let tmp = address & 0x00001F;
if (tmp < 0x00008) {
switch (address & 0x000003) {// VDC#1
case 0x00:
this.SetVDCRegister(data, 0);
break;
case 0x02:
this.SetVDCLow(data, 0);
break;
case 0x03:
this.SetVDCHigh(data, 0);
break;
}
} else if (tmp < 0x00010) {// VPC
this.SetVPC(tmp & 0x000007, data);
} else if (tmp < 0x00018) {// VDC#2
switch (address & 0x000003) {
case 0x00:
this.SetVDCRegister(data, 1);
break;
case 0x02:
this.SetVDCLow(data, 1);
break;
case 0x03:
this.SetVDCHigh(data, 1);
break;
}
}
} else {
switch (address & 0x000003) {// VDC#1
case 0x00:
this.SetVDCRegister(data, 0);
break;
case 0x01:
break;
case 0x02:
this.SetVDCLow(data, 0);
break;
case 0x03:
this.SetVDCHigh(data, 0);
break;
}
}
return;
}
if (address < 0x1FE800) {// VCE
switch (address & 0x000007) {
case 0x00:
this.SetVCEControl(data);
break;
case 0x02:
this.SetVCEAddressLow(data);
break;
case 0x03:
this.SetVCEAddressHigh(data);
break;
case 0x04:
this.SetVCEDataLow(data);
break;
case 0x05:
this.SetVCEDataHigh(data);
break;
}
return;
}
if (address < 0x1FEC00) {// PSG
this.SetPSG(address & 0x00000F, data);
return;
}
if (address < 0x1FF000) {// TIMER
switch (address & 0x000001) {
case 0x00:
this.WirteTimerReload(data);
break;
case 0x01:
this.WirteTimerControl(data);
break;
}
return;
}
if (address < 0x1FF400) {// IO
this.SetJoystick(data);
return;
}
if (address < 0x1FF800) {// INT Register
switch (address & 0x000003) {
case 0x02:
this.SetIntDisable(data);
break;
case 0x03:
this.SetIntReqest(data);
break;
}
return;
}
}
/* ************* */
/* **** VCE **** */
/* ************* */
VCEConstruct() {
this.Palette = new Array(512);
this.PaletteData = new Array(512);
this.MonoPaletteData = new Array(512);
this.VCEBaseClock = 0;
this.VCEControl = 0;
this.VCEAddress = 0;
this.VCEData = 0;
}
VCEInit() {
this.Palette.fill(0x0000);
for (let i = 0; i < 512; i++) {
this.PaletteData[i] = { r: 0, g: 0, b: 0 };
this.MonoPaletteData[i] = { r: 0, g: 0, b: 0 };
}
this.VCEBaseClock = this.BaseClock5;
this.VCEControl = 0x00;
this.VCEAddress = 0x00;
this.VCEData = 0x00;
}
SetVCEControl(data) {
this.VCEControl = data;
switch (data & 0x03) {
case 0x00:
this.VCEBaseClock = this.BaseClock5;
break;
case 0x01:
this.VCEBaseClock = this.BaseClock7;
break;
case 0x02:
case 0x03:
this.VCEBaseClock = this.BaseClock10;
break;
}
}
SetVCEAddressLow(data) {
this.VCEAddress = (this.VCEAddress & 0xFF00) | data;
}
SetVCEAddressHigh(data) {
this.VCEAddress = ((this.VCEAddress & 0x00FF) | (data << 8)) & 0x01FF;
}
GetVCEDataLow() {
return this.Palette[this.VCEAddress] & 0x00FF;
}
GetVCEDataHigh() {
let tmp = (this.Palette[this.VCEAddress] & 0xFF00) >> 8;
this.VCEAddress = (this.VCEAddress + 1) & 0x01FF;
return tmp;
}
SetVCEDataLow(data) {
this.Palette[this.VCEAddress] = (this.Palette[this.VCEAddress] & 0xFF00) | data;
this.ToPalettes();
}
SetVCEDataHigh(data) {
this.Palette[this.VCEAddress] = (this.Palette[this.VCEAddress] & 0x00FF) | (data << 8);
this.ToPalettes();
this.VCEAddress = (this.VCEAddress + 1) & 0x01FF;
}
ToPalettes() {
let color = this.Palette[this.VCEAddress];
let tmp = this.PaletteData[this.VCEAddress];
tmp.r = ((color >> 3) & 0x07) * 36;
tmp.g = ((color >> 6) & 0x07) * 36;
tmp.b = (color & 0x07) * 36;
let mono = tmp.r * 0.299 + tmp.g * 0.587 + tmp.b * 0.114;
this.MonoPaletteData[this.VCEAddress].r = mono;
this.MonoPaletteData[this.VCEAddress].g = mono;
this.MonoPaletteData[this.VCEAddress].b = mono;
}
/* ************* */
/* **** VPC **** */
/* ************* */
VPCConstruct() {
this.VPCRegister = new Uint8Array(8);
this.VDCSelect = 0;
this.VPCWindow1 = 0;
this.VPCWindow2 = 0;
this.VPCPriority = new Uint8Array(4);
}
VPCInit() {
this.VPCRegister.fill(0x00);
this.VPCRegister[0] = 0x11;
this.VPCRegister[1] = 0x11;
this.VPCRegister[7] = 0xFF;
this.VDCSelect = 0;
this.VPCWindow1 = 0;
this.VPCWindow2 = 0;
this.VPCPriority.fill(0x01);
}
SetVPC(no, data) {
if (no == 0x07)
return;
this.VPCRegister[no] = data;
if (no == 0x06)
this.VDCSelect = data & 0x01;
if (no == 0x02 || no == 0x03) {
this.VPCWindow1 = (this.VPCRegister[0x02] | ((this.VPCRegister[0x03] & 0x03) << 8)) - 64;
if (this.VPCWindow1 < 0)
this.VPCWindow1 = 1024;
}
if (no == 0x04 || no == 0x05) {
this.VPCWindow2 = (this.VPCRegister[0x04] | ((this.VPCRegister[0x05] & 0x03) << 8)) - 64;
if (this.VPCWindow2 < 0)
this.VPCWindow2 = -1;
}
if (no == 0x00) {
this.VPCPriority[2] = this.VPCRegister[0x00] >> 4;
this.VPCPriority[3] = this.VPCRegister[0x00] & 0x0F;
}
if (no == 0x01) {
this.VPCPriority[0] = this.VPCRegister[0x01] >> 4;
this.VPCPriority[1] = this.VPCRegister[0x01] & 0x0F;
}
}
GetVPC(no) {
return this.VPCRegister[no];
}
/* ************* */
/* **** VDC **** */
/* ************* */
VDCConstruct() {
this.DrawFlag = false;
this.VDCPutLineProgressClock = 0;
this.VDCPutLine = 0;
this.VDC = new Array(2);
this.VDCLineClock = 1368;
this.ScreenSize = [];
this.ScreenSize[this.BaseClock5] = 342;
this.ScreenSize[this.BaseClock7] = 456;
this.ScreenSize[this.BaseClock10] = 684;
this.PutScreenSize = [];
this.PutScreenSize[this.BaseClock5] = 320;
this.PutScreenSize[this.BaseClock7] = 428;
this.PutScreenSize[this.BaseClock10] = 640;
this.ScreenHeightMAX = 262;
this.ScreenWidthMAX = 684;
this.VScreenWidthArray = [];
this.VScreenWidthArray[0x00] = 32;
this.VScreenWidthArray[0x10] = 64;
this.VScreenWidthArray[0x20] = 128;
this.VScreenWidthArray[0x30] = 128;
this.ReverseBit = new Uint8Array(0x100);
this.ReverseBit = this.ReverseBit.map((d, i) => {
return ((i & 0x80) >> 7) | ((i & 0x40) >> 5) |
((i & 0x20) >> 3) | ((i & 0x10) >> 1) |
((i & 0x08) << 1) | ((i & 0x04) << 3) |
((i & 0x02) << 5) | ((i & 0x01) << 7);
});
this.ReverseBit16 = new Uint16Array(0x10000).fill(0x00);
this.ReverseBit16 = this.ReverseBit16.map((d, i) => { return (this.ReverseBit[i & 0x00FF] << 8) | this.ReverseBit[(i & 0xFF00) >> 8]; });
this.ReverseBit256 = new Uint32Array(0x100).fill(0x00);
this.ReverseBit256 = this.ReverseBit256.map((d, i) => {
let b = this.ReverseBit[i];
return ((b & 0x80) << (28 - 7)) |
((b & 0x40) << (24 - 6)) |
((b & 0x20) << (20 - 5)) |
((b & 0x10) << (16 - 4)) |
((b & 0x08) << (12 - 3)) |
((b & 0x04) << (8 - 2)) |
((b & 0x02) << (4 - 1)) |
((b & 0x01) << (0 - 0));
});
this.SPAddressMask = [];
this.SPAddressMask[16] = [];
this.SPAddressMask[32] = [];
this.SPAddressMask[16][16] = 0x07FE;
this.SPAddressMask[16][32] = 0x07FE & 0x07FA;
this.SPAddressMask[16][64] = 0x07FE & 0x07F2;
this.SPAddressMask[32][16] = 0x07FC;
this.SPAddressMask[32][32] = 0x07FC & 0x07FA;
this.SPAddressMask[32][64] = 0x07FC & 0x07F2;
}
MakeSpriteLine(vdcno) {
let vdcc = this.VDC[vdcno];
let sp = vdcc.SPLine;
for (let i = 0; i < vdcc.ScreenWidth; i++) {
let spi = sp[i];
spi.data = 0x00;
spi.palette = 0x000;
spi.no = 255;
spi.priority = 0x00;
}
if ((vdcc.VDCRegister[0x05] & 0x0040) == 0x0000)
return;
let dotcount = 0;
let line = vdcc.DrawBGYLine - (vdcc.VDS + vdcc.VSW) + 64;
let vram = vdcc.VRAM;
let satb = vdcc.SATB;
let revbit16 = this.ReverseBit16;
for (let i = 0, s = 0; i < 64; i++, s += 4) {
let y = satb[s] & 0x3FF;
let attribute = satb[s + 3];
let height = ((attribute & 0x3000) >> 8) + 16;
height = height > 32 ? 64 : height;
if (line < y || line > (y + height - 1))
continue;
let x = (satb[s + 1] & 0x3FF) - 32;
let width = ((attribute & 0x0100) >> 4) + 16;
if ((x + width) <= 0)
continue;
let spy = line - y;
if ((attribute & 0x8000) == 0x8000)
spy = (height - 1) - spy;
let index = ((satb[s + 2] & this.SPAddressMask[width][height]) << 5) | (((spy & 0x30) << 3) | (spy & 0x0F));
let data0;
let data1;
let data2;
let data3;
if ((attribute & 0x0800) == 0x0000) {
data0 = revbit16[vram[index]];
data1 = revbit16[vram[index + 16]];
data2 = revbit16[vram[index + 32]];
data3 = revbit16[vram[index + 48]];
if (width == 32) {
data0 |= revbit16[vram[(index | 0x0040)]] << 16;
data1 |= revbit16[vram[(index | 0x0040) + 16]] << 16;
data2 |= revbit16[vram[(index | 0x0040) + 32]] << 16;
data3 |= revbit16[vram[(index | 0x0040) + 48]] << 16;
}
} else {
data0 = vram[index];
data1 = vram[index + 16];
data2 = vram[index + 32];
data3 = vram[index + 48];
if (width == 32) {
data0 = (data0 << 16) | vram[(index | 0x0040)];
data1 = (data1 << 16) | vram[(index | 0x0040) + 16];
data2 = (data2 << 16) | vram[(index | 0x0040) + 32];
data3 = (data3 << 16) | vram[(index | 0x0040) + 48];
}
}
let palette = ((attribute & 0x000F) << 4) | 0x0100;
let priority = attribute & 0x0080;
let j = 0;
if (x < 0) {
j -= x;
x = 0;
}
for (; j < width && x < vdcc.ScreenWidth; j++, x++) {
let spx = sp[x];
if (spx.data == 0x00) {
let dot = ((data0 >>> j) & 0x0001)
| (((data1 >>> j) << 1) & 0x0002)
| (((data2 >>> j) << 2) & 0x0004)
| (((data3 >>> j) << 3) & 0x0008);
if (dot != 0x00) {
spx.data = dot;
spx.palette = palette;
spx.priority = priority;
}
}
if (spx.no == 255)
spx.no = i;
if (i != 0 && spx.no == 0)
vdcc.VDCStatus |= vdcc.VDCRegister[0x05] & 0x0001;//SetSpriteCollisionINT
if (++dotcount == 256) {
vdcc.VDCStatus |= vdcc.VDCRegister[0x05] & 0x0002;//SetSpriteOverINT
if (vdcc.SpriteLimit)
return;
}
}
}
}
MakeBGLine(vdcno) {
let vdcc = this.VDC[vdcno];
let sp = vdcc.SPLine;
let bg = vdcc.BGLine;
let sw = vdcc.ScreenWidth;
let leftblank = ((vdcc.HDS + vdcc.HSW) << 3) + vdcc.DrawBGIndex;
if ((vdcc.VDCRegister[0x05] & 0x0080) == 0x0080) {
let WidthMask = vdcc.VScreenWidth - 1;
let x = vdcc.VDCRegister[0x07];
let index_x = (x >> 3) & WidthMask;
x = (x & 0x07) << 2;
let y = vdcc.DrawBGLine;
let index_y = ((y >> 3) & (vdcc.VScreenHeight - 1)) * vdcc.VScreenWidth;
y = y & 0x07;
let vram = vdcc.VRAM;
let bgx = 0;
let revbit = this.ReverseBit256;
while (bgx < sw) {
let tmp = vram[index_x + index_y];
let address = ((tmp & 0x0FFF) << 4) + y;
let palette = (tmp & 0xF000) >> 8;
let data0 = vram[address];
let data1 = vram[address + 8];
let data = (revbit[data0 & 0x00FF]) |
(revbit[(data0 & 0xFF00) >> 8] << 1) |
(revbit[data1 & 0x00FF] << 2) |
(revbit[(data1 & 0xFF00) >> 8] << 3);
for (; x < 32 && bgx < sw; x += 4, bgx++) {
let dot = (data >>> x) & 0x0F;
let spbgx = sp[bgx];
bg[bgx + leftblank] = spbgx.data != 0x00 && (dot == 0x00 || spbgx.priority == 0x0080) ?
spbgx.data | spbgx.palette : dot | (dot == 0x00 ? 0x00 : palette);
}
x = 0;
index_x = (index_x + 1) & WidthMask;
}
} else {
for (let i = 0; i < sw; i++)
bg[i + leftblank] = sp[i].data | sp[i].palette;
}
}
MakeBGColorLineVDC(vdcno) {
this.VDC[vdcno].BGLine.fill(0x100);
}
VDCProcessDMA(vdcno) {
let vdcc = this.VDC[vdcno];
if (vdcc.VRAMtoSATBCount > 0) {//VRAMtoSATB
vdcc.VRAMtoSATBCount -= this.ProgressClock;
if (vdcc.VRAMtoSATBCount <= 0)
vdcc.VDCStatus = (vdcc.VDCStatus & 0xBF) | ((vdcc.VDCRegister[0x0F] & 0x0001) << 3);//VRAMtoSATB INT
}
if (vdcc.VRAMtoVRAMCount > 0) {//VRAMtoVRAM
vdcc.VRAMtoVRAMCount -= this.ProgressClock;
if (vdcc.VRAMtoVRAMCount <= 0)
vdcc.VDCStatus = (vdcc.VDCStatus & 0xBF) | ((vdcc.VDCRegister[0x0F] & 0x0002) << 3);//VRAMtoVRAM INT
}
}
VDCProcess(vdcno) {
let vdcc = this.VDC[vdcno];
vdcc.VDCProgressClock -= this.VDCLineClock;
vdcc.DrawBGIndex = 0;
vdcc.BGLine.fill(0x100);
for (let i = 0; i < vdcc.ScreenSize; i += vdcc.DrawLineWidth) {
vdcc.DrawBGYLine++;
if (vdcc.DrawBGYLine == this.ScreenHeightMAX)
vdcc.DrawBGYLine = 0;
if (vdcc.DrawBGYLine < (vdcc.VDS + vdcc.VSW)) {//OVER SCAN
this.MakeBGColorLineVDC(vdcno);
} else if (vdcc.DrawBGYLine <= (vdcc.VDS + vdcc.VSW + vdcc.VDW)) {//ACTIVE DISPLAY
vdcc.DrawBGLine = (vdcc.DrawBGYLine == (vdcc.VDS + vdcc.VSW) ? vdcc.VDCRegister[0x08] : (vdcc.DrawBGLine + 1)) & vdcc.VScreenHeightMask;
if (!vdcc.VDCBurst) {
this.MakeSpriteLine(vdcno);
this.MakeBGLine(vdcno);
} else
this.MakeBGColorLineVDC(vdcno);
} else {//OVER SCAN
this.MakeBGColorLineVDC(vdcno);
}
let vline = vdcc.VDS + vdcc.VSW + vdcc.VDW + 1;
if (vline > 261)
vline -= 261;
if (vdcc.DrawBGYLine == vline) {
vdcc.VDCStatus |= (vdcc.VDCRegister[0x05] & 0x0008) << 2;//SetVSync INT
if (vdcc.VRAMtoSATBStartFlag) {//VRAMtoSATB
for (let i = 0, addr = vdcc.VDCRegister[0x13]; i < 256; i++, addr++)
vdcc.SATB[i] = vdcc.VRAM[addr];
vdcc.VRAMtoSATBCount = 256 * this.VCEBaseClock;
vdcc.VDCStatus |= 0x40;
vdcc.VRAMtoSATBStartFlag = (vdcc.VDCRegister[0x0F] & 0x0010) == 0x0010;
}
}
vdcc.RasterCount++;
if (vdcc.DrawBGYLine == (vdcc.VDS + vdcc.VSW - 1))
vdcc.RasterCount = 64;
if (vdcc.RasterCount == vdcc.VDCRegister[0x06] && (vdcc.VDCStatus & 0x20) == 0x00)
vdcc.VDCStatus |= vdcc.VDCRegister[0x05] & 0x0004;//SetRaster INT
vdcc.DrawBGIndex += vdcc.DrawLineWidth;
}
}
VDCRun() {
this.VDCProcessDMA(0);
this.VDC[0].VDCProgressClock += this.ProgressClock;
if (this.SuperGrafx) {
this.VDCProcessDMA(1);
this.VDC[1].VDCProgressClock += this.ProgressClock;
}
while (this.VDC[0].VDCProgressClock >= this.VDCLineClock) {
this.VDCProcess(0);
if (this.SuperGrafx)
this.VDCProcess(1);
}
this.VDCPutLineProgressClock += this.ProgressClock;
if (this.VDCPutLineProgressClock >= this.VDCLineClock) {
this.VDCPutLineProgressClock -= this.VDCLineClock;
this.VDCPutLine++;
if (this.VDCPutLine == this.ScreenHeightMAX) {
this.VDCPutLine = 0;
this.GetScreenSize(0);
this.VDC[0].DrawBGYLine = 0;
if (this.SuperGrafx) {
this.GetScreenSize(1);
this.VDC[1].DrawBGYLine = 0;
}
this.DrawFlag = true;
this.Ctx.putImageData(this.ImageData, 0, 0);
}
let palettes = (this.VCEControl & 0x80) == 0x00 ? this.PaletteData : this.MonoPaletteData;
let data = this.ImageData.data;
let imageIndex = this.VDCPutLine * this.ScreenWidthMAX * 4;
let black = palettes[0x100];
let sw = this.ScreenSize[this.VCEBaseClock];
let bgl0 = this.VDC[0].BGLine;
if (this.SuperGrafx) {//VPC
let window1 = this.VPCWindow1;
let window2 = this.VPCWindow2;
let priority = this.VPCPriority;
let bgl1 = this.VDC[1].BGLine;
for (let bgx = 0; bgx < sw; bgx++, imageIndex += 4) {
let wflag = 0x00;
if (bgx >= window1)
wflag |= 0x01;
if (bgx <= window2)
wflag |= 0x02;
let bg0 = bgl0[bgx];
let bg1 = bgl1[bgx];
let color;
switch (priority[wflag]) {
case 0x04 | 0x03:
if (bg0 > 0x100 || bg1 > 0x100)
color = palettes[bg0 > 0x100 ? bg0 : bg1];
else
color = palettes[(bg0 & 0x0FF) != 0x000 ? bg0 : bg1];
break;
case 0x08 | 0x03:
if (bg0 < 0x100 && bg0 != 0x000)
color = palettes[bg0];
else
color = palettes[(bg1 & 0x0FF) != 0x000 ? bg1 : bg0];
break;
case 0x00 | 0x03:
case 0x0C | 0x03:
color = palettes[(bg0 & 0x0FF) != 0x000 ? bg0 : bg1];
break;
case 0x00 | 0x01:
case 0x04 | 0x01:
case 0x08 | 0x01:
case 0x0C | 0x01:
color = palettes[bg0];
break;
case 0x00 | 0x02:
case 0x04 | 0x02:
case 0x08 | 0x02:
case 0x0C | 0x02:
color = palettes[bg1];
break;
default:
color = black;
break;
}
data[imageIndex] = color.r;
data[imageIndex + 1] = color.g;
data[imageIndex + 2] = color.b;
}
} else {
for (let bgx = 0; bgx < sw; bgx++, imageIndex += 4) {
let color = palettes[bgl0[bgx]];
data[imageIndex] = color.r;
data[imageIndex + 1] = color.g;
data[imageIndex + 2] = color.b;
}
}
}
}
GetScreenSize(vdcno) {
let vdcc = this.VDC[vdcno];
let r = vdcc.VDCRegister;
vdcc.VScreenWidth = this.VScreenWidthArray[r[0x09] & 0x0030];
vdcc.VScreenHeight = (r[0x09] & 0x0040) == 0x0000 ? 32 : 64;
vdcc.VScreenHeightMask = vdcc.VScreenHeight * 8 - 1;
vdcc.ScreenWidth = ((r[0x0B] & 0x007F) + 1) * 8;
if (vdcc.ScreenWidth > this.ScreenWidthMAX)
vdcc.ScreenWidth = this.ScreenWidthMAX;
vdcc.HDS = (r[0x0A] & 0x7F00) >> 8;
vdcc.HSW = r[0x0A] & 0x001F;
vdcc.HDE = (r[0x0B] & 0x7F00) >> 8;
vdcc.HDW = r[0x0B] & 0x007F;
vdcc.VDS = ((r[0x0C] & 0xFF00) >> 8);
vdcc.VSW = r[0x0C] & 0x001F;
vdcc.VDW = r[0x0D] & 0x01FF;
vdcc.VCR = r[0x0E] & 0x00FF;
vdcc.ScreenSize = this.ScreenSize[this.VCEBaseClock];
if (this.MainCanvas.width != vdcc.ScreenSize) {
//this.MainCanvas.style.width = (this.PutScreenSize[this.VCEBaseClock] * 2) + 'px';
this.MainCanvas.width = this.PutScreenSize[this.VCEBaseClock];
}
vdcc.DrawLineWidth = (vdcc.HDS + vdcc.HSW + vdcc.HDE + vdcc.HDW + 1) << 3;
if (vdcc.DrawLineWidth <= this.ScreenSize[this.BaseClock5])
vdcc.DrawLineWidth = this.ScreenSize[this.BaseClock5];
else if (vdcc.DrawLineWidth <= this.ScreenSize[this.BaseClock7])
vdcc.DrawLineWidth = this.ScreenSize[this.BaseClock7];
else vdcc.DrawLineWidth = this.ScreenSize[this.BaseClock10];
vdcc.VDCBurst = (r[0x05] & 0x00C0) == 0x0000 ? true : false;
}
VDCInit() {
this.VDCPutLineProgressClock = 0;
this.VDCPutLine = 0;
this.DrawFlag = false;
for (let vdcno = 0; vdcno < 2; vdcno++) {
this.VDC[vdcno] = {
VDCRegister: new Uint16Array(20).fill(0x0000),
VRAM: new Uint16Array(0x10000).fill(0x0000),
SATB: new Uint16Array(256).fill(0x0000),
VDCBurst: false,
SpriteLimit: false,
SPLine: new Array(this.ScreenWidthMAX),
BGLine: new Array(this.ScreenWidthMAX).fill(0x00),
VDCStatus: 0x00,
VDCRegisterSelect: 0x00,
WriteVRAMData: 0x0000,
VRAMtoSATBStartFlag: false,
VRAMtoSATBCount: 0,
VRAMtoVRAMCount: 0,
RasterCount: 64,
VDCProgressClock: 0,
DrawBGYLine: 0,
DrawBGLine: 0,
VScreenWidth: 0,
VScreenHeight: 0,
VScreenHeightMask: 0,
ScreenWidth: 0,
ScreenSize: 0,
DrawLineWidth: 0,
DrawBGIndex: 0,
HDS: 0,
HSW: 0,
HDE: 0,
HDW: 0,
VDS: 0,
VSW: 0,
VDW: 0,
VCR: 0
};
for (let i = 0; i < this.VDC[vdcno].SPLine.length; i++)
this.VDC[vdcno].SPLine[i] = { data: 0x00, no: 255, priority: 0x00 };
this.VDC[vdcno].VDCRegister[0x09] = 0x0010;
this.VDC[vdcno].VDCRegister[0x0A] = 0x0202;
this.VDC[vdcno].VDCRegister[0x0B] = 0x031F;
this.VDC[vdcno].VDCRegister[0x0C] = 0x0F02;
this.VDC[vdcno].VDCRegister[0x0D] = 0x00EF;
this.VDC[vdcno].VDCRegister[0x0E] = 0x0003;
}
this.GetScreenSize(0);
this.GetScreenSize(1);
}
SetVDCRegister(data, vdcno) {
this.VDC[vdcno].VDCRegisterSelect = data & 0x1F;
}
SetVDCLow(data, vdcno) {
let vdcc = this.VDC[vdcno];
if (vdcc.VDCRegisterSelect == 0x02)
vdcc.WriteVRAMData = data;
else
vdcc.VDCRegister[vdcc.VDCRegisterSelect] = (vdcc.VDCRegister[vdcc.VDCRegisterSelect] & 0xFF00) | data;
if (vdcc.VDCRegisterSelect == 0x01) {
vdcc.VDCRegister[0x02] = vdcc.VRAM[vdcc.VDCRegister[0x01]];
return;
}
if (vdcc.VDCRegisterSelect == 0x08) {
vdcc.DrawBGLine = vdcc.VDCRegister[0x08];
return;
}
if (vdcc.VDCRegisterSelect == 0x0F)
vdcc.VRAMtoSATBStartFlag = (vdcc.VDCRegister[0x0F] & 0x10) == 0x10;
}
SetVDCHigh(data, vdcno) {
let vdcc = this.VDC[vdcno];
if (vdcc.VDCRegisterSelect == 0x02) {
vdcc.VRAM[vdcc.VDCRegister[0x00]] = vdcc.WriteVRAMData | (data << 8);
vdcc.VDCRegister[0x00] = (vdcc.VDCRegister[0x00] + this.GetVRAMIncrement(vdcno)) & 0xFFFF;
return;
}
vdcc.VDCRegister[vdcc.VDCRegisterSelect] = (vdcc.VDCRegister[vdcc.VDCRegisterSelect] & 0x00FF) | (data << 8);
if (vdcc.VDCRegisterSelect == 0x01) {
vdcc.VDCRegister[0x02] = vdcc.VRAM[vdcc.VDCRegister[0x01]];
vdcc.VDCRegister[0x03] = vdcc.VDCRegister[0x02];
vdcc.VDCRegister[0x01] = (vdcc.VDCRegister[0x01] + this.GetVRAMIncrement(vdcno)) & 0xFFFF;
return;
}
if (vdcc.VDCRegisterSelect == 0x08) {
vdcc.DrawBGLine = vdcc.VDCRegister[0x08];
return;
}
if (vdcc.VDCRegisterSelect == 0x12) {//VRAMtoVRAM
let si = (vdcc.VDCRegister[0x0F] & 0x0004) == 0x0000 ? 1 : -1;
let di = (vdcc.VDCRegister[0x0F] & 0x0008) == 0x0000 ? 1 : -1;
let s = vdcc.VDCRegister[0x10];
let d = vdcc.VDCRegister[0x11];
let l = vdcc.VDCRegister[0x12] + 1;
vdcc.VRAMtoVRAMCount = l * this.VCEBaseClock;
vdcc.VDCStatus |= 0x40;
let vram = vdcc.VRAM;
for (; l > 0; l--) {
vram[d] = vram[s];
s = (s + si) & 0xFFFF;
d = (d + di) & 0xFFFF;
}
return;
}
if (vdcc.VDCRegisterSelect == 0x13)//VRAMtoSATB
vdcc.VRAMtoSATBStartFlag = true;
}
GetVRAMIncrement(vdcno) {
switch (this.VDC[vdcno].VDCRegister[0x05] & 0x1800) {
case 0x0000:
return 1;
case 0x0800:
return 32;
case 0x1000:
return 64;
case 0x1800:
return 128;
}
}
GetVDCStatus(vdcno) {
let tmp = this.VDC[vdcno].VDCStatus;
this.VDC[vdcno].VDCStatus &= 0x40;
return tmp;
}
GetVDCLow(vdcno) {
return this.VDC[vdcno].VDCRegister[this.VDC[vdcno].VDCRegisterSelect] & 0x00FF;
}
GetVDCHigh(vdcno) {
let vdcc = this.VDC[vdcno];
if (vdcc.VDCRegisterSelect == 0x02 || vdcc.VDCRegisterSelect == 0x03) {
let tmp = (vdcc.VDCRegister[0x02] & 0xFF00) >> 8;
vdcc.VDCRegister[0x02] = vdcc.VRAM[vdcc.VDCRegister[0x01]];
vdcc.VDCRegister[0x03] = vdcc.VDCRegister[0x02];
vdcc.VDCRegister[0x01] = (vdcc.VDCRegister[0x01] + this.GetVRAMIncrement(vdcno)) & 0xFFFF;
return tmp;
}
return (vdcc.VDCRegister[vdcc.VDCRegisterSelect] & 0xFF00) >> 8;
}
/* *************** */
/* **** Sound **** */
/* *************** */
SoundConstruct() {
this.WaveDataArray = [];
this.WaveClockCounter = 0;
this.WaveVolume = 1.0;
this.WebAudioCtx = null;
this.WebAudioJsNode = null;
this.WebAudioGainNode = null;
this.WebAudioBufferSize = 2048;
this.PSGClock = 3579545;
}
WebAudioFunction(e) {
let output = [];
let data = [];
for (let i = 0; i < 2; i++) {
output[i] = e.outputBuffer.getChannelData(i);
data[i] = new Float32Array(this.WebAudioBufferSize);
if (this.WaveDataArray[i].length < this.WebAudioBufferSize) {
data[i].fill(0.0);
} else {
for (let j = 0; j < data[i].length; j++)
data[i][j] = this.WaveDataArray[i].shift() / ((32 * 16 * 32) * 8 * 16);
if (this.WaveDataArray[i].length > this.WebAudioBufferSize * 2)
this.WaveDataArray[i] = this.WaveDataArray[i].slice(this.WebAudioBufferSize);
}
output[i].set(data[i]);
}
}
SoundInit() {
this.WaveClockCounter = 0;
this.WaveDataArray = [];
this.WaveDataArray[0] = [];
this.WaveDataArray[1] = [];
if (typeof AudioContext !== "undefined" && this.WebAudioCtx == null) {
this.CreateAudioContext();
}
}
CreateAudioContext() {
this.WebAudioCtx = new window.AudioContext();
this.WebAudioJsNode = this.WebAudioCtx.createScriptProcessor(this.WebAudioBufferSize, 0, 2);
this.WebAudioJsNode.onaudioprocess = this.WebAudioFunction.bind(this);
this.WebAudioGainNode = this.WebAudioCtx.createGain();
this.WebAudioJsNode.connect(this.WebAudioGainNode);
this.WebAudioGainNode.connect(this.WebAudioCtx.destination);
}
SoundSet() {
let waveoutleft;
let waveoutright;
let ch;
let i;
let j;
let out;
this.WaveClockCounter += this.WebAudioCtx.sampleRate;
if (this.WaveClockCounter >= this.PSGClock) {
this.WaveClockCounter -= this.PSGClock;
waveoutleft = 0;
waveoutright = 0;
for (j = 0; j < 6; j++) {
if (j != 1 || !this.WaveLfoOn) {
ch = this.PSGChannel[j];
if (j < 4 || !ch.noiseon)
out = ch.keyon ? (ch.dda ? ch.R[6] & 0x1F : ch.wave[ch.index]) : 0;
else
out = (ch.noise & 0x0001) == 0x0001 ? 0x0F : 0;
waveoutleft += out * ch.leftvol;
waveoutright += out * ch.rightvol;
}
}
this.WaveDataArray[0].push(waveoutleft * this.WaveVolumeLeft);
this.WaveDataArray[1].push(waveoutright * this.WaveVolumeRight);
this.WebAudioGainNode.gain.value = this.WaveVolume;
}
}
/* ************* */
/* **** PSG **** */
/* ************* */
PSGConstruct() {
this.PSGChannel = new Array(6);
this.PSGBaseClock = this.BaseClock3;
this.PSGProgressClock = 0;
this.WaveVolumeLeft = 0;
this.WaveVolumeRight = 0;
this.WaveLfoOn = false;
this.WaveLfoControl = 0;
this.WaveLfoFreqency = 0;
}
PSGInit() {
this.SoundInit();
for (let i = 0; i < this.PSGChannel.length; i++)
this.PSGChannel[i] = {
R: new Array(10).fill(0),
keyon: false,
dda: false,
freq: 0,
count: 0,
vol: 0,
leftvol: 0,
rightvol: 0,
noiseon: false,
noisefreq: 0,
noise: 0x8000,
noisestate: 0,
index: 0,
wave: new Uint8Array(32)
};
}
PSGRun() {
if (this.WebAudioCtx == null)
return;
let ch;
let i;
let j;
let ch0;
let ch1;
let freqtmp;
this.PSGProgressClock += this.ProgressClock;
i = (this.PSGProgressClock / this.PSGBaseClock) | 0;
this.PSGProgressClock %= this.PSGBaseClock;
while (i > 0) {
i--;
j = 0;
if (this.WaveLfoOn) {
ch0 = this.PSGChannel[0];
ch1 = this.PSGChannel[1];
if (ch0.keyon) {
if (ch0.count == 0) {
ch0.index = (ch0.index + 1) & 0x1F;
freqtmp = 0;
if (this.WaveLfoControl != 0x00) {
freqtmp = ch1.wave[ch1.index];
freqtmp = freqtmp > 0x0F ? freqtmp - 0x20 : freqtmp & 0x0F;
freqtmp <<= 4 * (this.WaveLfoControl - 1);
}
freqtmp = ch0.freq + freqtmp;
if (freqtmp < 0)
freqtmp = 0;
ch0.count = freqtmp;
} else
ch0.count--;
if (ch1.count == 0) {
ch1.index = (ch1.index + 1) & 0x1F;
ch1.count = ch1.freq * this.WaveLfoFreqency;
} else
ch1.count--;
}
j = 2;
}
for (; j < 6; j++) {
ch = this.PSGChannel[j];
if (j < 4 || !ch.noiseon) {
if (ch.keyon && !ch.dda) {
if (ch.count == 0) {
ch.index = (ch.index + 1) & 0x1F;
ch.count = ch.freq;
} else
ch.count--;
}
} else {
if (ch.keyon && !ch.dda) {
if (ch.count == 0) {
ch.index = (ch.index + 1) & 0x1F;
if (ch.index == 0)
ch.noise = (ch.noise >> 1) | (((ch.noise << 12) ^ (ch.noise << 15)) & 0x8000);
ch.count = ch.noisefreq;
} else
ch.count--;
}
}
}
this.SoundSet();
}
}
SetPSG(r, data) {
if (r == 0) {
this.PSGChannel[0].R[0] = data & 0x07;
return;
}
if (this.PSGChannel[0].R[0] > 5)
return;
let ch = this.PSGChannel[this.PSGChannel[0].R[0]];
ch.R[r] = data;
switch (r) {
case 1:
this.PSGChannel[0].R[1] = data;
this.WaveVolumeLeft = (data & 0xF0) >> 4;
this.WaveVolumeRight = data & 0x0F;
return;
case 2:
case 3:
ch.freq = ((ch.R[3] << 8) | ch.R[2]) & 0x0FFF;
return;
case 4:
ch.keyon = (data & 0x80) == 0x80 ? true : false;
ch.dda = (data & 0x40) == 0x40 ? true : false;
if ((data & 0x40) == 0x40)
ch.index = 0;
ch.vol = data & 0x1F;
case 5:
let vol = ch.R[4] & 0x1F;
ch.leftvol = ((ch.R[5] & 0xF0) >> 4) * vol;
ch.rightvol = (ch.R[5] & 0x0F) * vol;
return;
case 6:
if (!ch.dda) {
ch.wave[ch.index] = data & 0x1F;
ch.index = (ch.index + 1) & 0x1F;
}
return;
case 7:
ch.noiseon = (data & 0x80) == 0x80 ? true : false;
ch.noisefreq = (data & 0x1F) ^ 0x1F;
return;
case 8:
this.PSGChannel[0].R[8] = data;
this.WaveLfoFreqency = data;
return;
case 9:
this.PSGChannel[0].R[9] = data;
this.WaveLfoOn = (data & 0x80) == 0x80 ? true : false;
this.WaveLfoControl = data & 0x03;
return;
}
}
GetPSG(r) {
if (r > 9)
return 0xFF;
if (r == 0 || r == 1 || r == 8 || r == 9)
return this.PSGChannel[0].R[r];
if (this.PSGChannel[0].R[0] > 5)
return 0xFF;
return this.PSGChannel[this.PSGChannel[0].R[0]].R[r];
}
/* *************** */
/* **** TIMER **** */
/* *************** */
TimerConstruct() {
this.TimerBaseClock = this.BaseClock7;
this.TimerReload = 0;
this.TimerFlag = false;
this.TimerCounter = 0;
this.TimerPrescaler = 0;
this.INTTIQ = 0;
}
TimerInit() {
this.TimerReload = 0x00;
this.TimerFlag = false;
this.TimerCounter = 0x00;
this.TimerPrescaler = 0;
this.INTTIQ = 0x00;
}
ReadTimerCounter() {
return this.TimerCounter;
}
TimerAcknowledge() {
this.INTTIQ = 0x00;
}
WirteTimerReload(data) {
this.TimerReload = data & 0x7F;
}
WirteTimerControl(data) {
if (!this.TimerFlag && (data & 0x01) == 0x01) {
this.TimerCounter = this.TimerReload;
this.TimerPrescaler = 0;
}
this.TimerFlag = (data & 0x01) == 0x01 ? true : false;
}
TimerRun() {
if (this.TimerFlag) {
this.TimerPrescaler += this.ProgressClock;
while (this.TimerPrescaler >= (1024 * this.TimerBaseClock)) {
this.TimerPrescaler -= 1024 * this.TimerBaseClock;
this.TimerCounter--;
if (this.TimerCounter < 0) {
this.TimerCounter = this.TimerReload;
this.INTTIQ = this.TIQFlag;
}
}
}
}
/* ****************** */
/* **** Joystick **** */
/* ****************** */
JoystickConstruct() {
this.JoystickSEL = 0;
this.JoystickCLR = 0;
this.KeyUpFunction = null;
this.KeyDownFunction = null;
this.Keybord = new Array(5).fill([]);
this.Keybord = this.Keybord.map((d) => { return new Array(4); });
this.GamePad = new Array(5).fill([]);
this.GamePad = this.Keybord.map((d) => { return new Array(4); });
this.GamePadSelect = 0x00;
this.GamePadButtonSelect = 0x00;
this.GamePadBuffer = 0x00;
this.GamePadData = [];
this.GamePadData["STANDARD PAD"] = [
[[{ type: "B", index: 1 }],// SHOT1
[{ type: "B", index: 0 }],// SHOT2
[{ type: "B", index: 8 }],// SELECT
[{ type: "B", index: 9 }, { type: "B", index: 2 }],// RUN
[{ type: "B", index: 12 }],// UP
[{ type: "B", index: 13 }],// DOWN
[{ type: "B", index: 14 }],// LEFT
[{ type: "B", index: 15 }]],// RIGHT
[[{ type: "B", index: 1 }],// SHOT1
[{ type: "B", index: 0 }],// SHOT2
[{ type: "B", index: 8 }],// SELECT
[{ type: "B", index: 9 }],// RUN
[{ type: "B", index: 12 }],// UP
[{ type: "B", index: 13 }],// DOWN
[{ type: "B", index: 14 }],// LEFT
[{ type: "B", index: 15 }],// RIGHT
[{ type: "B", index: 7 }],// SHOT3
[{ type: "B", index: 5 }],// SHOT4
[{ type: "B", index: 2 }],// SHOT5
[{ type: "B", index: 3 }]]];// SHOT6
this.GamePadData["HORI PAD 3 TURBO (Vendor: 0f0d Product: 0009)"] = [// Chrome
[[{ type: "B", index: 2 }],// SHOT1
[{ type: "B", index: 1 }],// SHOT2
[{ type: "B", index: 8 }],// SELECT
[{ type: "B", index: 9 }, { type: "B", index: 0 }],// RUN
[{ type: "P", index: 9 }],// UP (POV)
[{ type: "N", index: 0 }],// DOWN (POV)
[{ type: "N", index: 0 }],// LEFT (POV)
[{ type: "N", index: 0 }]],// RIGHT (POV)
[[{ type: "B", index: 2 }],// SHOT1
[{ type: "B", index: 1 }],// SHOT2
[{ type: "B", index: 8 }],// SELECT
[{ type: "B", index: 9 }],// RUN
[{ type: "P", index: 9 }],// UP (POV)
[{ type: "N", index: 0 }],// DOWN (POV)
[{ type: "N", index: 0 }],// LEFT (POV)
[{ type: "N", index: 0 }],// RIGHT (POV)
[{ type: "B", index: 7 }],// SHOT3
[{ type: "B", index: 5 }],// SHOT4
[{ type: "B", index: 0 }],// SHOT5
[{ type: "B", index: 3 }]]];// SHOT6
this.GamePadData["0f0d-0009-HORI PAD 3 TURBO"] = this.GamePadData["HORI PAD 3 TURBO (Vendor: 0f0d Product: 0009)"];// Firefox
this.GamePadData["UNKNOWN PAD"] = this.GamePadData["HORI PAD 3 TURBO (Vendor: 0f0d Product: 0009)"];
this.GamePadKeyData = [{ index: 0, data: 0x01 }, { index: 0, data: 0x02 },
{ index: 0, data: 0x04 }, { index: 0, data: 0x08 },
{ index: 1, data: 0x01 }, { index: 1, data: 0x04 },
{ index: 1, data: 0x08 }, { index: 1, data: 0x02 },
{ index: 2, data: 0x01 }, { index: 2, data: 0x02 },
{ index: 2, data: 0x04 }, { index: 2, data: 0x08 }];
this.GamePadPovData = [0x01, 0x01 | 0x02, 0x02, 0x02 | 0x04, 0x04, 0x04 | 0x08, 0x08, 0x01 | 0x08];
}
JoystickInit() {
this.JoystickSEL = 0;
this.JoystickCLR = 0;
for (let i = 0; i < this.Keybord.length; i++) {
this.Keybord[i][0] = 0xBF;
this.Keybord[i][1] = 0xBF;
this.Keybord[i][2] = 0xBF;
this.Keybord[i][3] = 0xB0;
}
this.GamePadSelect = 0;
this.GamePadButtonSelect = 0x00;
this.GamePadBuffer = 0x00;
}
SetJoystick(data) {
let sel = data & 0x01;
let clr = (data & 0x02) >> 1;
if ((this.JoystickSEL == 1 && this.JoystickCLR == 0) && (sel == 1 && clr == 1)) {
this.JoystickSEL = 0;
this.JoystickCLR = 0;
this.GamePadSelect = 0;
if (this.GamePadButton6)
this.GamePadButtonSelect = this.GamePadButtonSelect ^ 0x02;
this.GamePadBuffer = 0xB0 | this.CountryType;
return;
}
if ((this.JoystickSEL == 0 && this.JoystickCLR == 0) && (sel == 1 && clr == 0))
this.GamePadSelect++;
this.JoystickSEL = sel;
this.JoystickCLR = clr;
let no = this.MultiTap ? this.GamePadSelect - 1 : 0;
if (no < 5) {
let tmp = this.GamePadButtonSelect | this.JoystickSEL;
this.GamePadBuffer = (this.Keybord[no][tmp] & this.GamePad[no][tmp]) | this.CountryType;
} else
this.GamePadBuffer = 0xB0 | this.CountryType;
}
GetJoystick() {
return this.GamePadBuffer;
}
UnsetButtonRUN(no) {
this.Keybord[no][0] |= 0x08;
}
UnsetButtonSELECT(no) {
this.Keybord[no][0] |= 0x04;
}
UnsetButtonSHOT2(no) {
this.Keybord[no][0] |= 0x02;
}
UnsetButtonSHOT1(no) {
this.Keybord[no][0] |= 0x01;
}
UnsetButtonLEFT(no) {
this.Keybord[no][1] |= 0x08;
}
UnsetButtonDOWN(no) {
this.Keybord[no][1] |= 0x04;
}
UnsetButtonRIGHT(no) {
this.Keybord[no][1] |= 0x02;
}
UnsetButtonUP(no) {
this.Keybord[no][1] |= 0x01;
}
UnsetButtonSHOT6(no) {
this.Keybord[no][2] |= 0x08;
}
UnsetButtonSHOT5(no) {
this.Keybord[no][2] |= 0x04;
}
UnsetButtonSHOT4(no) {
this.Keybord[no][2] |= 0x02;
}
UnsetButtonSHOT3(no) {
this.Keybord[no][2] |= 0x01;
}
SetButtonRUN(no) {
this.Keybord[no][0] &= ~0x08;
}
SetButtonSELECT(no) {
this.Keybord[no][0] &= ~0x04;
}
SetButtonSHOT2(no) {
this.Keybord[no][0] &= ~0x02;
}
SetButtonSHOT1(no) {
this.Keybord[no][0] &= ~0x01;
}
SetButtonLEFT(no) {
this.Keybord[no][1] &= ~0x08;
}
SetButtonDOWN(no) {
this.Keybord[no][1] &= ~0x04;
}
SetButtonRIGHT(no) {
this.Keybord[no][1] &= ~0x02;
}
SetButtonUP(no) {
this.Keybord[no][1] &= ~0x01;
}
SetButtonSHOT6(no) {
this.Keybord[no][2] &= ~0x08;
}
SetButtonSHOT5(no) {
this.Keybord[no][2] &= ~0x04;
}
SetButtonSHOT4(no) {
this.Keybord[no][2] &= ~0x02;
}
SetButtonSHOT3(no) {
this.Keybord[no][2] &= ~0x01;
}
CheckKeyUpFunction(evt) {
switch (evt.keyCode) {
case Keys['START'].c:// RUN 'S'
this.UnsetButtonRUN(0);
break;
case Keys['SELECT'].c:// SELECT 'A'
this.UnsetButtonSELECT(0);
break;
case Keys['A'].c:// SHOT2 'Z'
case Keys['GP_A'].c:// SHOT2 'Z'
this.UnsetButtonSHOT2(0);
break;
case Keys['B'].c:// SHOT1 'X'
case Keys['GP_B'].c:// SHOT1 'X'
this.UnsetButtonSHOT1(0);
break;
case 86:// SHOT2 'V'
this.UnsetButtonSHOT2(0);
break;
case 66:// SHOT1 'B'
this.UnsetButtonSHOT1(0);
break;
case 37:// LEFT
this.UnsetButtonLEFT(0);
break;
case 39:// RIGHT
this.UnsetButtonRIGHT(0);
break;
case 40:// DOWN
this.UnsetButtonDOWN(0);
break;
case 38:// UP
this.UnsetButtonUP(0);
break;
case 71:// SHOT6 'G'
this.UnsetButtonSHOT6(0);
break;
case 70:// SHOT5 'F'
this.UnsetButtonSHOT5(0);
break;
case 68:// SHOT4 'D'
this.UnsetButtonSHOT4(0);
break;
case 67:// SHOT3 'C'
this.UnsetButtonSHOT3(0);
break;
}
//evt.preventDefault();
}
CheckKeyDownFunction(evt) {
switch (evt.keyCode) {
case Keys['START'].c:// RUN 'S'
this.SetButtonRUN(0);
break;
case Keys['SELECT'].c:// SELECT 'A'
this.SetButtonSELECT(0);
break;
case Keys['A'].c:// SHOT2 'Z'
case Keys['GP_A'].c:// SHOT2 'Z'
this.SetButtonSHOT2(0);
break;
case Keys['B'].c:// SHOT2 'Z'
case Keys['GP_B'].c:// SHOT2 'Z'
this.SetButtonSHOT1(0);
break;
case 86:// SHOT2 'V'
this.SetButtonSHOT2(0);
break;
case 66:// SHOT1 'B'
this.SetButtonSHOT1(0);
break;
case 37:// LEFT
this.SetButtonLEFT(0);
break;
case 39:// RIGHT
this.SetButtonRIGHT(0);
break;
case 40:// DOWN
this.SetButtonDOWN(0);
break;
case 38:// UP
this.SetButtonUP(0);
break;
case 71:// SHOT6 'G'
this.SetButtonSHOT6(0);
break;
case 70:// SHOT5 'F'
this.SetButtonSHOT5(0);
break;
case 68:// SHOT4 'D'
this.SetButtonSHOT4(0);
break;
case 67:// SHOT3 'C'
this.SetButtonSHOT3(0);
break;
}
//evt.preventDefault();
}
JoystickEventInit() {
this.KeyUpFunction = this.CheckKeyUpFunction.bind(this);
this.KeyDownFunction = this.CheckKeyDownFunction.bind(this);
this.MainCanvas.addEventListener("keyup", this.KeyUpFunction, true);
this.MainCanvas.addEventListener("keydown", this.KeyDownFunction, true);
}
JoystickEventRelease() {
this.MainCanvas.removeEventListener("keyup", this.KeyUpFunction, true);
this.MainCanvas.removeEventListener("keydown", this.KeyDownFunction, true);
}
CheckGamePad() {
for (let i = 0; i < this.GamePad.length; i++) {
this.GamePad[i][0] = 0xBF;
this.GamePad[i][1] = 0xBF;
this.GamePad[i][2] = 0xBF;
this.GamePad[i][3] = 0xB0;
}
if (typeof navigator.getGamepads === "undefined")
return;
let pads = navigator.getGamepads();
for (let i = 0; i < 5; i++) {
let pad = pads[i];
if (typeof pad !== "undefined" && pad !== null) {
let paddata;
if (pad.mapping === "standard")
paddata = this.GamePadData["STANDARD PAD"];
else {
paddata = this.GamePadData[pad.id];
if (typeof paddata === "undefined")
paddata = this.GamePadData["UNKNOWN PAD"];
}
paddata = this.GamePadButton6 ? paddata[1] : paddata[0];
let tmp = 0;
for (const val0 of paddata) {
for (const val1 of val0) {
switch (val1.type) {
case "B":
if (pad.buttons[val1.index].pressed)
this.GamePad[i][this.GamePadKeyData[tmp].index] &= ~this.GamePadKeyData[tmp].data;
break;
case "A-":
if (pad.axes[val1.index] < -0.5)
this.GamePad[i][this.GamePadKeyData[tmp].index] &= ~this.GamePadKeyData[tmp].data;
break;
case "A+":
if (pad.axes[val1.index] > 0.5)
this.GamePad[i][this.GamePadKeyData[tmp].index] &= ~this.GamePadKeyData[tmp].data;
break;
case "AB":
if (pad.axes[val1.index] > -0.75)
this.GamePad[i][this.GamePadKeyData[tmp].index] &= ~this.GamePadKeyData[tmp].data;
break;
case "P":
let povtmp = ((pad.axes[val1.index] + 1) * 7 / 2 + 0.5) | 0;
this.GamePad[i][1] &= ~(povtmp <= 7 ? this.GamePadPovData[povtmp] : 0x00);
break;
}
}
tmp++;
}
}
}
}
}