mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-06-25 00:32:27 +00:00
Z80 machine; SavesInputState/AcceptsKeyInput
This commit is contained in:
parent
10a0d98f23
commit
9c1736b506
|
@ -1197,10 +1197,11 @@ export abstract class BasicZ80ScanlinePlatform extends BaseZ80Platform {
|
||||||
|
|
||||||
/// new style
|
/// new style
|
||||||
|
|
||||||
import { Bus, Resettable, FrameBased, VideoSource, SampledAudioSource, AcceptsROM, AcceptsInput, SavesState, HasCPU } from "./devices";
|
import { Bus, Resettable, FrameBased, VideoSource, SampledAudioSource, AcceptsROM, AcceptsKeyInput, SavesState, SavesInputState, HasCPU } from "./devices";
|
||||||
|
import { CPUClockHook, LogCPU, RasterFrameBased } from "./devices";
|
||||||
import { SampledAudio } from "./audio";
|
import { SampledAudio } from "./audio";
|
||||||
|
|
||||||
interface Machine extends Bus, Resettable, FrameBased, AcceptsROM, HasCPU, SavesState<EmuState> {
|
interface Machine extends Bus, Resettable, FrameBased, AcceptsROM, HasCPU, SavesState<EmuState>, SavesInputState<any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasVideo(arg:any): arg is VideoSource {
|
function hasVideo(arg:any): arg is VideoSource {
|
||||||
|
@ -1209,8 +1210,11 @@ function hasVideo(arg:any): arg is VideoSource {
|
||||||
function hasAudio(arg:any): arg is SampledAudioSource {
|
function hasAudio(arg:any): arg is SampledAudioSource {
|
||||||
return typeof arg.connectAudio === 'function';
|
return typeof arg.connectAudio === 'function';
|
||||||
}
|
}
|
||||||
function hasInput<CS>(arg:any): arg is AcceptsInput<CS> {
|
function hasKeyInput(arg:any): arg is AcceptsKeyInput {
|
||||||
return typeof arg.setInput === 'function';
|
return typeof arg.setKeyInput === 'function';
|
||||||
|
}
|
||||||
|
function isRaster(arg:any): arg is RasterFrameBased {
|
||||||
|
return typeof arg.getRasterY === 'function';
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPlatform implements Platform {
|
export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPlatform implements Platform {
|
||||||
|
@ -1238,15 +1242,15 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
|
||||||
getSP() { return this.machine.cpu.getSP(); }
|
getSP() { return this.machine.cpu.getSP(); }
|
||||||
getPC() { return this.machine.cpu.getPC(); }
|
getPC() { return this.machine.cpu.getPC(); }
|
||||||
getCPUState() { return this.machine.cpu.saveState(); }
|
getCPUState() { return this.machine.cpu.saveState(); }
|
||||||
loadControlsState(s) { if (hasInput(this.machine)) this.machine.loadControlsState(s); }
|
loadControlsState(s) { this.machine.loadControlsState(s); }
|
||||||
saveControlsState() { return hasInput(this.machine) && this.machine.saveControlsState(); }
|
saveControlsState() { return this.machine.saveControlsState(); }
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
var m = this.machine;
|
var m = this.machine;
|
||||||
this.timer = new AnimationTimer(60, this.nextFrame.bind(this));
|
this.timer = new AnimationTimer(60, this.nextFrame.bind(this));
|
||||||
if (hasVideo(m)) {
|
if (hasVideo(m)) {
|
||||||
var vp = m.getVideoParams();
|
var vp = m.getVideoParams();
|
||||||
this.video = new RasterVideo(this.mainElement, vp.width, vp.height);
|
this.video = new RasterVideo(this.mainElement, vp.width, vp.height, {overscan:vp.overscan});
|
||||||
this.video.create();
|
this.video.create();
|
||||||
m.connectVideo(this.video.getFrameData());
|
m.connectVideo(this.video.getFrameData());
|
||||||
}
|
}
|
||||||
|
@ -1256,9 +1260,9 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
|
||||||
this.audio.start();
|
this.audio.start();
|
||||||
m.connectAudio(this.audio);
|
m.connectAudio(this.audio);
|
||||||
}
|
}
|
||||||
if (hasInput(m)) {
|
if (hasKeyInput(m)) {
|
||||||
this.video.setKeyboardEvents(m.setInput.bind(m));
|
this.video.setKeyboardEvents(m.setKeyInput.bind(m));
|
||||||
this.poller = new ControllerPoller(m.setInput.bind(m));
|
this.poller = new ControllerPoller(m.setKeyInput.bind(m));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1346,8 +1350,24 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
getRasterScanline() {
|
||||||
|
return isRaster(this.machine) && this.machine.getRasterY();
|
||||||
|
}
|
||||||
|
/* TODO
|
||||||
|
startProfilingCPU(log:LogCPU) {
|
||||||
|
new CPUClockHook(this.machine.cpu, log);
|
||||||
|
}
|
||||||
|
stopProfilingCPU() {
|
||||||
|
}
|
||||||
|
startProfilingMemory() {
|
||||||
|
}
|
||||||
|
stopProfilingMemory() {
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: move debug info into CPU?
|
||||||
|
|
||||||
export abstract class Base6502MachinePlatform<T extends Machine> extends BaseMachinePlatform<T> {
|
export abstract class Base6502MachinePlatform<T extends Machine> extends BaseMachinePlatform<T> {
|
||||||
|
|
||||||
getOpcodeMetadata = getOpcodeMetadata_6502;
|
getOpcodeMetadata = getOpcodeMetadata_6502;
|
||||||
|
@ -1372,3 +1392,30 @@ export abstract class Base6502MachinePlatform<T extends Machine> extends BaseMac
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export abstract class BaseZ80MachinePlatform<T extends Machine> extends BaseMachinePlatform<T> {
|
||||||
|
|
||||||
|
//getOpcodeMetadata = getOpcodeMetadata_z80;
|
||||||
|
getToolForFilename = getToolForFilename_z80;
|
||||||
|
|
||||||
|
getDebugCategories() {
|
||||||
|
return ['CPU','Stack'];
|
||||||
|
}
|
||||||
|
getDebugInfo(category:string, state:EmuState) : string {
|
||||||
|
switch (category) {
|
||||||
|
case 'CPU': return cpuStateToLongString_Z80(state.c);
|
||||||
|
case 'Stack': {
|
||||||
|
var sp = (state.c.SP-1) & 0xffff;
|
||||||
|
var start = sp & 0xff00;
|
||||||
|
var end = start + 0xff;
|
||||||
|
if (sp == 0) sp = 0x10000;
|
||||||
|
console.log(sp,start,end);
|
||||||
|
return dumpStackToString(<Platform><any>this, [], start, end, sp, 0xcd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disassemble(pc:number, read:(addr:number)=>number) : DisasmLine {
|
||||||
|
return disassembleZ80(pc, read(pc), read(pc+1), read(pc+2), read(pc+3));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,8 @@ export interface VideoSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RasterFrameBased extends FrameBased, VideoSource {
|
export interface RasterFrameBased extends FrameBased, VideoSource {
|
||||||
|
getRasterY() : number;
|
||||||
|
getRasterX() : number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoParams {
|
export interface VideoParams {
|
||||||
|
@ -86,13 +88,16 @@ export interface Interruptable<IT> {
|
||||||
interrupt(type:IT) : void;
|
interrupt(type:IT) : void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
export interface SavesInputState<CS> {
|
||||||
export interface AcceptsInput<CS> {
|
|
||||||
setInput(key:number, code:number, flags:number) : void;
|
|
||||||
loadControlsState(cs:CS) : void;
|
loadControlsState(cs:CS) : void;
|
||||||
saveControlsState() : CS;
|
saveControlsState() : CS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
export interface AcceptsKeyInput {
|
||||||
|
setKeyInput(key:number, code:number, flags:number) : void;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO?
|
// TODO?
|
||||||
export function noise(x : number) : number {
|
export function noise(x : number) : number {
|
||||||
x ^= x << 13;
|
x ^= x << 13;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
import { MOS6502, MOS6502State } from "../cpu/MOS6502";
|
import { MOS6502, MOS6502State } from "../cpu/MOS6502";
|
||||||
import { Bus, RasterFrameBased, SavesState, AcceptsROM, AcceptsInput, noise, Resettable, SampledAudioSource, SampledAudioSink, HasCPU } from "../devices";
|
import { Bus, RasterFrameBased, SavesState, SavesInputState, AcceptsROM, AcceptsKeyInput, noise, Resettable, SampledAudioSource, SampledAudioSink, HasCPU } from "../devices";
|
||||||
import { KeyFlags } from "../emu"; // TODO
|
import { KeyFlags } from "../emu"; // TODO
|
||||||
import { lzgmini } from "../util";
|
import { lzgmini } from "../util";
|
||||||
|
|
||||||
|
@ -30,8 +29,8 @@ interface AppleIIState extends AppleIIStateBase, AppleIIControlsState {
|
||||||
grswitch : number;
|
grswitch : number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AppleII implements HasCPU, Bus, RasterFrameBased, SampledAudioSource, AcceptsROM,
|
export class AppleII implements HasCPU, Bus, RasterFrameBased, SampledAudioSource, AcceptsROM, AcceptsKeyInput,
|
||||||
AppleIIStateBase, SavesState<AppleIIState>, AcceptsInput<AppleIIControlsState> {
|
AppleIIStateBase, SavesState<AppleIIState>, SavesInputState<AppleIIControlsState> {
|
||||||
|
|
||||||
ram = new Uint8Array(0x13000); // 64K + 16K LC RAM - 4K hardware + 12K ROM
|
ram = new Uint8Array(0x13000); // 64K + 16K LC RAM - 4K hardware + 12K ROM
|
||||||
rom : Uint8Array;
|
rom : Uint8Array;
|
||||||
|
@ -53,6 +52,7 @@ export class AppleII implements HasCPU, Bus, RasterFrameBased, SampledAudioSourc
|
||||||
// bank 1 is E000-FFFF, bank 2 is D000-DFFF
|
// bank 1 is E000-FFFF, bank 2 is D000-DFFF
|
||||||
bank2rdoffset=0;
|
bank2rdoffset=0;
|
||||||
bank2wroffset=0;
|
bank2wroffset=0;
|
||||||
|
lastFrameCycles=0;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.rom = new lzgmini().decode(APPLEIIGO_LZG);
|
this.rom = new lzgmini().decode(APPLEIIGO_LZG);
|
||||||
|
@ -219,14 +219,18 @@ export class AppleII implements HasCPU, Bus, RasterFrameBased, SampledAudioSourc
|
||||||
advanceFrame(maxCycles, trap) : number {
|
advanceFrame(maxCycles, trap) : number {
|
||||||
maxCycles = Math.min(maxCycles, cpuCyclesPerFrame);
|
maxCycles = Math.min(maxCycles, cpuCyclesPerFrame);
|
||||||
for (var i=0; i<maxCycles; i++) {
|
for (var i=0; i<maxCycles; i++) {
|
||||||
if (trap && trap()) break;
|
if (trap && (this.lastFrameCycles=i)>=0 && trap()) break;
|
||||||
this.cpu.advanceClock();
|
this.cpu.advanceClock();
|
||||||
this.audio.feedSample(this.soundstate, 1);
|
this.audio.feedSample(this.soundstate, 1);
|
||||||
}
|
}
|
||||||
this.ap2disp && this.ap2disp.updateScreen();
|
this.ap2disp && this.ap2disp.updateScreen();
|
||||||
return i;
|
return (this.lastFrameCycles = i);
|
||||||
}
|
}
|
||||||
setInput(key:number, code:number, flags:number) : void {
|
|
||||||
|
getRasterX() { return this.lastFrameCycles % cpuCyclesPerLine; }
|
||||||
|
getRasterY() { return Math.floor(this.lastFrameCycles / cpuCyclesPerLine); }
|
||||||
|
|
||||||
|
setKeyInput(key:number, code:number, flags:number) : void {
|
||||||
if (flags & KeyFlags.KeyPress) {
|
if (flags & KeyFlags.KeyPress) {
|
||||||
// convert to uppercase for Apple ][
|
// convert to uppercase for Apple ][
|
||||||
if (code >= 0x61 && code <= 0x7a)
|
if (code >= 0x61 && code <= 0x7a)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import { MOS6502, MOS6502State } from "../cpu/MOS6502";
|
import { MOS6502, MOS6502State } from "../cpu/MOS6502";
|
||||||
import { Bus, RasterFrameBased, SavesState, AcceptsROM, AcceptsInput, noise, Resettable, SampledAudioSource, SampledAudioSink, HasCPU } from "../devices";
|
import { Bus, RasterFrameBased, SavesState, SavesInputState, AcceptsROM, AcceptsKeyInput, noise, Resettable, SampledAudioSource, SampledAudioSink, HasCPU } from "../devices";
|
||||||
import { KeyFlags } from "../emu"; // TODO
|
import { KeyFlags } from "../emu"; // TODO
|
||||||
import { hex, lzgmini, stringToByteArray, lpad, rpad, rgb2bgr } from "../util";
|
import { hex, lzgmini, stringToByteArray, lpad, rpad, rgb2bgr } from "../util";
|
||||||
|
|
||||||
|
@ -300,8 +300,8 @@ class MARIA {
|
||||||
|
|
||||||
// Atari 7800
|
// Atari 7800
|
||||||
|
|
||||||
export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSource, AcceptsROM,
|
export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSource, AcceptsROM, AcceptsKeyInput,
|
||||||
Atari7800StateBase, SavesState<Atari7800State>, AcceptsInput<Atari7800ControlsState> {
|
Atari7800StateBase, SavesState<Atari7800State>, SavesInputState<Atari7800ControlsState> {
|
||||||
|
|
||||||
cpu : MOS6502;
|
cpu : MOS6502;
|
||||||
ram : Uint8Array = new Uint8Array(0x1000);
|
ram : Uint8Array = new Uint8Array(0x1000);
|
||||||
|
@ -317,6 +317,7 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
|
||||||
pixels : Uint32Array;
|
pixels : Uint32Array;
|
||||||
audio : SampledAudioSink;
|
audio : SampledAudioSink;
|
||||||
handler; // TODO: type, or use ControllerPoller
|
handler; // TODO: type, or use ControllerPoller
|
||||||
|
lastFrameCycles : number = 0;
|
||||||
|
|
||||||
read : (a:number) => number;
|
read : (a:number) => number;
|
||||||
write : (a:number, v:number) => void;
|
write : (a:number, v:number) => void;
|
||||||
|
@ -368,7 +369,7 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
|
||||||
}
|
}
|
||||||
|
|
||||||
getVideoParams() {
|
getVideoParams() {
|
||||||
return {width:320, height:numVisibleLines};
|
return {width:320, height:numVisibleLines, overscan:true};
|
||||||
}
|
}
|
||||||
getAudioParams() {
|
getAudioParams() {
|
||||||
return {sampleRate:linesPerFrame*60*oversampling, stereo:false};
|
return {sampleRate:linesPerFrame*60*oversampling, stereo:false};
|
||||||
|
@ -383,11 +384,11 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
|
||||||
//TODO this.bios = new Uint8Array(0x1000);
|
//TODO this.bios = new Uint8Array(0x1000);
|
||||||
// TODO: TIA access wastes a cycle
|
// TODO: TIA access wastes a cycle
|
||||||
|
|
||||||
setInput(key:number, code:number, flags:number) : void {
|
setKeyInput(key:number, code:number, flags:number) : void {
|
||||||
this.handler(key,code,flags);
|
this.handler(key,code,flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
advanceFrame(maxClocks, debugCond) : number {
|
advanceFrame(maxClocks, trap) : number {
|
||||||
var idata = this.pixels;
|
var idata = this.pixels;
|
||||||
var iofs = 0;
|
var iofs = 0;
|
||||||
var rgb;
|
var rgb;
|
||||||
|
@ -406,24 +407,28 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
|
||||||
if (mariaClocks >= colorClocksPreDMA) {
|
if (mariaClocks >= colorClocksPreDMA) {
|
||||||
this.maria.WSYNC--;
|
this.maria.WSYNC--;
|
||||||
mariaClocks = colorClocksPreDMA; // 7 CPU cycles until DMA
|
mariaClocks = colorClocksPreDMA; // 7 CPU cycles until DMA
|
||||||
|
// TODO: frameClocks
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// next CPU clock
|
// next CPU clock
|
||||||
mariaClocks -= 4;
|
if (trap && (this.lastFrameCycles=frameClocks)>=0 && trap()) {
|
||||||
if (debugCond && debugCond()) {
|
trap = null;
|
||||||
debugCond = null;
|
|
||||||
sl = 999;
|
sl = 999;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
mariaClocks -= 4;
|
||||||
|
frameClocks += 4;
|
||||||
this.cpu.advanceClock();
|
this.cpu.advanceClock();
|
||||||
}
|
}
|
||||||
mariaClocks += colorClocksPerLine;
|
mariaClocks += colorClocksPerLine;
|
||||||
// is this scanline visible?
|
// is this scanline visible?
|
||||||
if (visible) {
|
if (visible) {
|
||||||
// do DMA for scanline?
|
// do DMA for scanline?
|
||||||
mariaClocks -= this.maria.doDMA(this);
|
let dmaClocks = this.maria.doDMA(this);
|
||||||
|
mariaClocks -= dmaClocks;
|
||||||
|
frameClocks += dmaClocks;
|
||||||
// copy line to frame buffer
|
// copy line to frame buffer
|
||||||
if (idata) {
|
if (idata) {
|
||||||
for (var i=0; i<320; i++) {
|
for (var i=0; i<320; i++) {
|
||||||
|
@ -453,9 +458,12 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
|
||||||
// TODO $(this.video.canvas).css('background-color', COLORS_WEB[bkcol]);
|
// TODO $(this.video.canvas).css('background-color', COLORS_WEB[bkcol]);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
return frameClocks;
|
return (this.lastFrameCycles = frameClocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getRasterX() { return this.lastFrameCycles % colorClocksPerLine; }
|
||||||
|
getRasterY() { return Math.floor(this.lastFrameCycles / colorClocksPerLine); }
|
||||||
|
|
||||||
loadROM(data) {
|
loadROM(data) {
|
||||||
if (data.length == 0xc080) data = data.slice(0x80); // strip header
|
if (data.length == 0xc080) data = data.slice(0x80); // strip header
|
||||||
this.rom = padBytes(data, romLength, true);
|
this.rom = padBytes(data, romLength, true);
|
||||||
|
@ -498,7 +506,7 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
|
||||||
in:this.inputs.slice(0)
|
in:this.inputs.slice(0)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
loadControlsState(state:Atari7800ControlsState) {
|
loadControlsState(state:Atari7800ControlsState) : void {
|
||||||
this.inputs.set(state.in);
|
this.inputs.set(state.in);
|
||||||
}
|
}
|
||||||
saveControlsState() : Atari7800ControlsState {
|
saveControlsState() : Atari7800ControlsState {
|
||||||
|
|
|
@ -70,7 +70,7 @@ function VirtualList(config) {
|
||||||
function onScroll(e) {
|
function onScroll(e) {
|
||||||
var scrollTop = e.target.scrollTop; // Triggers reflow
|
var scrollTop = e.target.scrollTop; // Triggers reflow
|
||||||
if (!lastRepaintY || Math.abs(scrollTop - lastRepaintY) > maxBuffer) {
|
if (!lastRepaintY || Math.abs(scrollTop - lastRepaintY) > maxBuffer) {
|
||||||
var first = (scrollTop / itemHeight) - screenItemsLen;
|
var first = Math.floor(scrollTop / itemHeight) - screenItemsLen;
|
||||||
self._renderChunk(self.container, first < 0 ? 0 : first);
|
self._renderChunk(self.container, first < 0 ? 0 : first);
|
||||||
lastRepaintY = scrollTop;
|
lastRepaintY = scrollTop;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user