Z80 machine; SavesInputState/AcceptsKeyInput

This commit is contained in:
Steven Hugg 2019-08-23 18:52:59 -04:00
parent 10a0d98f23
commit 9c1736b506
5 changed files with 97 additions and 33 deletions

View File

@ -1197,10 +1197,11 @@ export abstract class BasicZ80ScanlinePlatform extends BaseZ80Platform {
/// 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";
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 {
@ -1209,8 +1210,11 @@ function hasVideo(arg:any): arg is VideoSource {
function hasAudio(arg:any): arg is SampledAudioSource {
return typeof arg.connectAudio === 'function';
}
function hasInput<CS>(arg:any): arg is AcceptsInput<CS> {
return typeof arg.setInput === 'function';
function hasKeyInput(arg:any): arg is AcceptsKeyInput {
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 {
@ -1238,15 +1242,15 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
getSP() { return this.machine.cpu.getSP(); }
getPC() { return this.machine.cpu.getPC(); }
getCPUState() { return this.machine.cpu.saveState(); }
loadControlsState(s) { if (hasInput(this.machine)) this.machine.loadControlsState(s); }
saveControlsState() { return hasInput(this.machine) && this.machine.saveControlsState(); }
loadControlsState(s) { this.machine.loadControlsState(s); }
saveControlsState() { return this.machine.saveControlsState(); }
start() {
var m = this.machine;
this.timer = new AnimationTimer(60, this.nextFrame.bind(this));
if (hasVideo(m)) {
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();
m.connectVideo(this.video.getFrameData());
}
@ -1256,9 +1260,9 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
this.audio.start();
m.connectAudio(this.audio);
}
if (hasInput(m)) {
this.video.setKeyboardEvents(m.setInput.bind(m));
this.poller = new ControllerPoller(m.setInput.bind(m));
if (hasKeyInput(m)) {
this.video.setKeyboardEvents(m.setKeyInput.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> {
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));
}
}

View File

@ -30,6 +30,8 @@ export interface VideoSource {
}
export interface RasterFrameBased extends FrameBased, VideoSource {
getRasterY() : number;
getRasterX() : number;
}
export interface VideoParams {
@ -86,13 +88,16 @@ export interface Interruptable<IT> {
interrupt(type:IT) : void;
}
// TODO
export interface AcceptsInput<CS> {
setInput(key:number, code:number, flags:number) : void;
export interface SavesInputState<CS> {
loadControlsState(cs:CS) : void;
saveControlsState() : CS;
}
// TODO
export interface AcceptsKeyInput {
setKeyInput(key:number, code:number, flags:number) : void;
}
// TODO?
export function noise(x : number) : number {
x ^= x << 13;

View File

@ -1,7 +1,6 @@
"use strict";
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 { lzgmini } from "../util";
@ -30,8 +29,8 @@ interface AppleIIState extends AppleIIStateBase, AppleIIControlsState {
grswitch : number;
}
export class AppleII implements HasCPU, Bus, RasterFrameBased, SampledAudioSource, AcceptsROM,
AppleIIStateBase, SavesState<AppleIIState>, AcceptsInput<AppleIIControlsState> {
export class AppleII implements HasCPU, Bus, RasterFrameBased, SampledAudioSource, AcceptsROM, AcceptsKeyInput,
AppleIIStateBase, SavesState<AppleIIState>, SavesInputState<AppleIIControlsState> {
ram = new Uint8Array(0x13000); // 64K + 16K LC RAM - 4K hardware + 12K ROM
rom : Uint8Array;
@ -53,6 +52,7 @@ export class AppleII implements HasCPU, Bus, RasterFrameBased, SampledAudioSourc
// bank 1 is E000-FFFF, bank 2 is D000-DFFF
bank2rdoffset=0;
bank2wroffset=0;
lastFrameCycles=0;
constructor() {
this.rom = new lzgmini().decode(APPLEIIGO_LZG);
@ -219,14 +219,18 @@ export class AppleII implements HasCPU, Bus, RasterFrameBased, SampledAudioSourc
advanceFrame(maxCycles, trap) : number {
maxCycles = Math.min(maxCycles, cpuCyclesPerFrame);
for (var i=0; i<maxCycles; i++) {
if (trap && trap()) break;
if (trap && (this.lastFrameCycles=i)>=0 && trap()) break;
this.cpu.advanceClock();
this.audio.feedSample(this.soundstate, 1);
}
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) {
// convert to uppercase for Apple ][
if (code >= 0x61 && code <= 0x7a)

View File

@ -1,7 +1,7 @@
"use strict";
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 { hex, lzgmini, stringToByteArray, lpad, rpad, rgb2bgr } from "../util";
@ -300,8 +300,8 @@ class MARIA {
// Atari 7800
export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSource, AcceptsROM,
Atari7800StateBase, SavesState<Atari7800State>, AcceptsInput<Atari7800ControlsState> {
export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSource, AcceptsROM, AcceptsKeyInput,
Atari7800StateBase, SavesState<Atari7800State>, SavesInputState<Atari7800ControlsState> {
cpu : MOS6502;
ram : Uint8Array = new Uint8Array(0x1000);
@ -317,6 +317,7 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
pixels : Uint32Array;
audio : SampledAudioSink;
handler; // TODO: type, or use ControllerPoller
lastFrameCycles : number = 0;
read : (a:number) => number;
write : (a:number, v:number) => void;
@ -368,7 +369,7 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
}
getVideoParams() {
return {width:320, height:numVisibleLines};
return {width:320, height:numVisibleLines, overscan:true};
}
getAudioParams() {
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: 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);
}
advanceFrame(maxClocks, debugCond) : number {
advanceFrame(maxClocks, trap) : number {
var idata = this.pixels;
var iofs = 0;
var rgb;
@ -406,24 +407,28 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
if (mariaClocks >= colorClocksPreDMA) {
this.maria.WSYNC--;
mariaClocks = colorClocksPreDMA; // 7 CPU cycles until DMA
// TODO: frameClocks
} else {
break;
}
}
// next CPU clock
mariaClocks -= 4;
if (debugCond && debugCond()) {
debugCond = null;
if (trap && (this.lastFrameCycles=frameClocks)>=0 && trap()) {
trap = null;
sl = 999;
break;
}
mariaClocks -= 4;
frameClocks += 4;
this.cpu.advanceClock();
}
mariaClocks += colorClocksPerLine;
// is this scanline visible?
if (visible) {
// do DMA for scanline?
mariaClocks -= this.maria.doDMA(this);
let dmaClocks = this.maria.doDMA(this);
mariaClocks -= dmaClocks;
frameClocks += dmaClocks;
// copy line to frame buffer
if (idata) {
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]);
}
*/
return frameClocks;
return (this.lastFrameCycles = frameClocks);
}
getRasterX() { return this.lastFrameCycles % colorClocksPerLine; }
getRasterY() { return Math.floor(this.lastFrameCycles / colorClocksPerLine); }
loadROM(data) {
if (data.length == 0xc080) data = data.slice(0x80); // strip header
this.rom = padBytes(data, romLength, true);
@ -498,7 +506,7 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
in:this.inputs.slice(0)
};
}
loadControlsState(state:Atari7800ControlsState) {
loadControlsState(state:Atari7800ControlsState) : void {
this.inputs.set(state.in);
}
saveControlsState() : Atari7800ControlsState {

View File

@ -70,7 +70,7 @@ function VirtualList(config) {
function onScroll(e) {
var scrollTop = e.target.scrollTop; // Triggers reflow
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);
lastRepaintY = scrollTop;
}