mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-12-26 22:31:14 +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
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user