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 { 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++; } } } } }