nemu: added audio, started on debugging

This commit is contained in:
Steven Hugg 2019-08-22 16:10:15 -04:00
parent 0f63282931
commit 2ae1232e61
8 changed files with 100 additions and 127 deletions

@ -1 +1 @@
Subproject commit bd17384fd2c6b020497e5e6a625a1b8130e39ea1
Subproject commit 4326d966e3adf16a8abe4a83f1c98359a825b5d9

View File

@ -472,3 +472,20 @@ export var SampleAudio = function(clockfreq) {
}
}
}
export class SampledAudio {
sa;
constructor(sampleRate : number) {
this.sa = new SampleAudio(sampleRate);
}
feedSample(value:number, count:number) {
this.sa.feedSample(value, count);
}
start() {
this.sa.start();
}
stop() {
this.sa.stop();
}
}

View File

@ -17,7 +17,7 @@ export interface OpcodeMetadata {
export interface CpuState {
PC:number;
EPC?:number; // effective PC (for bankswitching)
T?:number;
T?:number; // TODO: remove in favor of 'stable'
o?:number;/*opcode*/
SP?:number
/*

View File

@ -61,7 +61,7 @@ var _MOS6502 = function() {
// Internal decoding registers
var T : number = -1;
var opcode : number = -1;
var instruction;
var instruction : (() => void)[];
var data : number = 0;
var AD : number = 0;
var BA : number = 0;
@ -90,6 +90,7 @@ var _MOS6502 = function() {
const bC = 0;
// Auxiliary variables
// TODO
//noinspection JSUnusedGlobalSymbols
this.debug = false;
//noinspection JSUnusedGlobalSymbols
@ -1888,6 +1889,12 @@ var _MOS6502 = function() {
this.getSP = function() { return SP; }
this.getPC = function() { return PC; }
this.getT = function() { return T; }
this.isPCStable = function() {
// TODO: gotta fix base class first
//return T < 0 || T == instruction.length-1;
return T == 0;
}
};
export interface MOS6502State {
@ -1909,7 +1916,7 @@ export class MOS6502 implements CPU, ClockBased, SavesState<MOS6502State>, Inter
this.cpu.connectBus(bus);
}
advanceClock() {
if (this.interruptType && this.cpu.getT() == 0) {
if (this.interruptType && this.isStable()) {
switch (this.interruptType) {
case MOS6502Interrupts.NMI: this.cpu.setNMI(); break;
case MOS6502Interrupts.IRQ: this.cpu.setIRQ(); break;
@ -1920,7 +1927,7 @@ export class MOS6502 implements CPU, ClockBased, SavesState<MOS6502State>, Inter
advanceInsn() {
do {
this.advanceClock();
} while (this.cpu.getT() != 0);
} while (!this.isStable());
}
reset() {
this.cpu.reset();
@ -1944,6 +1951,9 @@ export class MOS6502 implements CPU, ClockBased, SavesState<MOS6502State>, Inter
this.cpu.loadState(s);
this.interruptType = s.it;
}
isStable() : boolean {
return this.cpu.isPCStable();
}
// TODO: metadata
// TODO: disassembler
}

View File

@ -1,7 +1,7 @@
"use strict";
import { MOS6502, MOS6502State } from "../cpu/MOS6502";
import { Bus, RasterFrameBased, SavesState, AcceptsROM, noise, Resettable } from "../nemu";
import { Bus, RasterFrameBased, SavesState, AcceptsROM, noise, Resettable, SampledAudioSource, SampledAudioSink } from "../nemu";
import { KeyFlags } from "../../emu"; // TODO
import { lzgmini } from "../../util";
@ -26,11 +26,13 @@ interface AppleIIState extends AppleIIStateBase {
c : MOS6502State;
}
export class AppleII implements Bus, Resettable, RasterFrameBased, AcceptsROM, AppleIIStateBase, SavesState<AppleIIState> {
export class AppleII implements Bus, Resettable, RasterFrameBased, SampledAudioSource, AcceptsROM,
AppleIIStateBase, SavesState<AppleIIState> {
ram = new Uint8Array(0x13000); // 64K + 16K LC RAM - 4K hardware + 12K ROM
rom : Uint8Array;
cpu = new MOS6502();
audio : SampledAudioSink;
pixels : Uint32Array;
grdirty = new Array(0xc000 >> 7);
grparams = {dirty:this.grdirty, grswitch:GR_TXMODE, mem:this.ram};
@ -75,6 +77,7 @@ export class AppleII implements Bus, Resettable, RasterFrameBased, AcceptsROM, A
loadState(s) {
this.cpu.loadState(s.c);
this.ram.set(s.ram);
// TODO
}
reset() {
this.cpu.reset();
@ -175,13 +178,22 @@ export class AppleII implements Bus, Resettable, RasterFrameBased, AcceptsROM, A
getVideoParams() {
return {width:280, height:192};
}
getAudioParams() {
return {sampleRate:cpuFrequency, stereo:false};
}
connectVideo(pixels:Uint32Array) {
this.pixels = pixels;
this.ap2disp = pixels && new Apple2Display(this.pixels, this.grparams);
}
advanceFrame() : number {
for (var i=0; i<cpuCyclesPerFrame; i++) {
connectAudio(audio:SampledAudioSink) {
this.audio = audio;
}
advanceFrame(maxCycles, trap) : number {
maxCycles = Math.min(maxCycles, cpuCyclesPerFrame);
for (var i=0; i<maxCycles; i++) {
if (trap && this.cpu.isStable() && trap()) break;
this.cpu.advanceClock();
this.audio.feedSample(this.soundstate, 1);
}
this.ap2disp && this.ap2disp.updateScreen();
return i;
@ -290,105 +302,6 @@ const GR_HIRES = 8;
type AppleGRParams = {dirty:boolean[], grswitch:number, mem:Uint8Array};
/*
const _Apple2Platform = function(mainElement) {
const cpuFrequency = 1023000;
const cpuCyclesPerLine = 65;
var cpu, ram, bus;
var video, ap2disp, audio, timer;
var grdirty = new Array(0xc000 >> 7);
var grswitch = GR_TXMODE;
var kbdlatch = 0;
var soundstate = 0;
var pgmbin;
// language card switches
var auxRAMselected = false;
var auxRAMbank = 1;
var writeinhibit = true;
// value to add when reading & writing each of these banks
// bank 1 is E000-FFFF, bank 2 is D000-DFFF
var bank2rdoffset=0, bank2wroffset=0;
var grparams : AppleGRParams;
var scanline : number;
class Apple2Platform extends Base6502Platform implements Platform {
getPresets() {
return APPLE2_PRESETS;
}
start() {
}
};
cpu.connectBus(bus);
// create video/audio
video = new RasterVideo(mainElement,280,192);
audio = new SampleAudio(cpuFrequency);
video.create();
video.setKeyboardEvents((key,code,flags) => {
});
var idata = video.getFrameData();
grparams = {dirty:grdirty, grswitch:grswitch, mem:ram.mem};
ap2disp = new Apple2Display(idata, grparams);
timer = new AnimationTimer(60, this.nextFrame.bind(this));
}
advance(novideo : boolean) {
// 262.5 scanlines per frame
var clock = 0;
var debugCond = this.getDebugCallback();
for (var sl=0; sl<262; sl++) {
scanline = sl;
for (var i=0; i<cpuCyclesPerLine; i++) {
if (debugCond && debugCond()) {
debugCond = null;
sl = 999;
break;
}
clock++;
cpu.clockPulse();
audio.feedSample(soundstate, 1);
}
}
if (!novideo) {
grparams.dirty = grdirty;
grparams.grswitch = grswitch;
ap2disp.updateScreen();
video.updateFrame();
}
}
getRasterScanline() : number {
return scanline;
}
loadROM(title, data) {
pgmbin = data;
this.reset();
}
isRunning() {
return timer.isRunning();
}
pause() {
timer.stop();
audio.stop();
}
resume() {
timer.start();
audio.start();
}
reset() {
cpu.reset();
// execute until $c600 boot
for (var i=0; i<2000000; i++) {
cpu.clockPulse();
if (this.getCPUState().PC == 0xc602) {
cpu.clockPulse();
cpu.clockPulse();
break;
}
}
}
readAddress(addr : number) {
return ((addr & 0xf000) != 0xc000) ? bus.read(addr) : null; // ignore I/O space
}
@ -426,10 +339,6 @@ const _Apple2Platform = function(mainElement) {
getCPUState() {
return this.fixPC(cpu.saveState());
}
}
return new Apple2Platform(); // return inner class from constructor
};
*/
var Apple2Display = function(pixels : Uint32Array, apple : AppleGRParams) {

View File

@ -18,16 +18,18 @@ export interface InstructionBased {
advanceInsn() : number;
}
export type TrapCondition = () => boolean;
export interface FrameBased {
advanceFrame() : number;
advanceFrame(maxClocks:number, trap:TrapCondition) : number;
}
export interface VideoFrameBased extends FrameBased {
export interface VideoSource {
getVideoParams() : VideoParams;
connectVideo(pixels:Uint32Array) : void;
}
export interface RasterFrameBased extends VideoFrameBased {
export interface RasterFrameBased extends FrameBased, VideoSource {
}
export interface VideoParams {
@ -44,9 +46,14 @@ export interface SampledAudioParams {
stereo : boolean;
}
export interface SampledAudio {
setAudioParams(params:SampledAudioParams) : void;
sendAudioFrame(samples:Uint16Array) : void;
export interface SampledAudioSink {
feedSample(value:number, count:number) : void;
//sendAudioFrame(samples:Uint16Array) : void;
}
export interface SampledAudioSource {
getAudioParams() : SampledAudioParams;
connectAudio(audio : SampledAudioSink) : void;
}
export interface AcceptsROM {
@ -94,7 +101,7 @@ export interface Hook<T> {
export class BusHook implements Hook<Bus> {
//target : Bus;
constructor(bus : Bus, profiler : ProfilerInterface) {
constructor(bus : Bus, profiler : LogBus) {
//this.target = bus;
var oldread = bus.read.bind(bus);
var oldwrite = bus.write.bind(bus);
@ -117,7 +124,7 @@ export class BusHook implements Hook<Bus> {
export class CPUClockHook implements Hook<CPU&ClockBased> {
//target : CPU&ClockBased;
constructor(cpu : CPU&ClockBased, profiler : ProfilerInterface) {
constructor(cpu : CPU&ClockBased, profiler : LogCPU) {
//this.target = cpu;
var oldclock = cpu.advanceClock.bind(cpu);
cpu.advanceClock = () => {
@ -133,7 +140,7 @@ export class CPUClockHook implements Hook<CPU&ClockBased> {
export class CPUInsnHook implements Hook<CPU&InstructionBased> {
//target : CPU&InstructionBased;
constructor(cpu : CPU&InstructionBased, profiler : ProfilerInterface) {
constructor(cpu : CPU&InstructionBased, profiler : LogCPU) {
//this.target = cpu;
var oldinsn = cpu.advanceInsn.bind(cpu);
cpu.advanceInsn = () => {
@ -149,13 +156,22 @@ export class CPUInsnHook implements Hook<CPU&InstructionBased> {
/// PROFILER
export interface ProfilerInterface {
export interface LogCPU {
logExecute(address:number);
logInterrupt(type:number);
}
export interface LogBus {
logRead(address:number);
logWrite(address:number);
}
export interface LogIO {
logIORead(address:number);
logIOWrite(address:number);
logInterrupt(type:number);
}
export interface LogAll extends LogCPU, LogBus, LogIO {
}
/// DEBUGGING

View File

@ -3,7 +3,7 @@
import { Platform, Base6502Platform, BaseMAMEPlatform, getOpcodeMetadata_6502, getToolForFilename_6502 } from "../baseplatform";
import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, KeyFlags, makeKeycodeMap, dumpRAM } from "../emu";
import { hex, lzgmini } from "../util";
import { SampleAudio } from "../audio";
import { SampleAudio, SampledAudio } from "../audio";
declare var jt; // 6502
@ -1068,7 +1068,7 @@ class Apple2MAMEPlatform extends BaseMAMEPlatform implements Platform {
///
// TODO: move to own place, debugging
import { CPU, Bus, ClockBased, SavesState, Interruptable } from "../nemu/nemu";
import { CPU, Bus, ClockBased, SavesState, Interruptable, CPUClockHook } from "../nemu/nemu";
import { MOS6502 } from "../nemu/cpu/MOS6502";
import { AppleII } from "../nemu/machine/apple2";
@ -1078,6 +1078,7 @@ class NewApple2Platform extends Base6502Platform implements Platform {
machine : AppleII;
timer : AnimationTimer;
video : RasterVideo;
audio : SampledAudio;
constructor(mainElement : HTMLElement) {
super();
@ -1097,9 +1098,13 @@ class NewApple2Platform extends Base6502Platform implements Platform {
this.timer = new AnimationTimer(60, this.nextFrame.bind(this));
var vp = this.machine.getVideoParams();
this.video = new RasterVideo(this.mainElement, vp.width, vp.height);
//this.audio = new SampleAudio(cpuFrequency);
this.video.create();
this.video.setKeyboardEvents(this.machine.setInput.bind(this.machine));
this.machine.connectVideo(this.video.getFrameData());
var ap = this.machine.getAudioParams();
this.audio = new SampledAudio(ap.sampleRate);
this.audio.start();
this.machine.connectAudio(this.audio);
}
reset() {
this.machine.reset();
@ -1109,7 +1114,7 @@ class NewApple2Platform extends Base6502Platform implements Platform {
this.reset();
}
advance(novideo:boolean) {
this.machine.advanceFrame();
this.machine.advanceFrame(999999, this.getDebugCallback());
if (!novideo) this.video.updateFrame();
}
isRunning() {
@ -1117,9 +1122,24 @@ class NewApple2Platform extends Base6502Platform implements Platform {
}
resume() {
this.timer.start();
this.audio.start();
}
pause() {
this.timer.stop();
this.audio.stop();
}
// debugging
restartDebugging() {
super.restartDebugging();
// TODO
var clock = 0;
var cpuhook = new CPUClockHook(this.machine.cpu, {
logExecute: (a:number) => {
clock++;
},
logInterrupt: (a:number) => {
}
});
}
}

View File

@ -958,6 +958,7 @@ function measureBuildTime() {
ga('send', 'timing', 'worker', 'build', (measureTimeBuild.getTime() - measureTimeLoad.getTime()));
measureTimeLoad = null; // only measure once
}
gaEvent('build', platform_id);
}
function setCompileOutput(data: WorkerResult) {