mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-01-02 23:30:21 +00:00
nemu: added audio, started on debugging
This commit is contained in:
parent
0f63282931
commit
2ae1232e61
@ -1 +1 @@
|
||||
Subproject commit bd17384fd2c6b020497e5e6a625a1b8130e39ea1
|
||||
Subproject commit 4326d966e3adf16a8abe4a83f1c98359a825b5d9
|
17
src/audio.ts
17
src/audio.ts
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
/*
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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) => {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user