1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-06-16 12:29:28 +00:00

cleaned up atari7800 platform (still have to log DMA via bus)

This commit is contained in:
Steven Hugg 2019-08-25 09:25:26 -04:00
parent 43f65b99ae
commit 768253751a
3 changed files with 75 additions and 89 deletions

View File

@ -248,8 +248,6 @@ export abstract class BasicMachine implements HasCPU, Bus, SampledAudioSource, A
abstract read(a:number) : number; abstract read(a:number) : number;
abstract write(a:number, v:number) : void; abstract write(a:number, v:number) : void;
abstract startScanline() : void;
abstract drawScanline() : void;
getAudioParams() : SampledAudioParams { getAudioParams() : SampledAudioParams {
return {sampleRate:this.sampleRate, stereo:false}; return {sampleRate:this.sampleRate, stereo:false};
@ -293,12 +291,12 @@ export abstract class BasicMachine implements HasCPU, Bus, SampledAudioSource, A
inputs:this.inputs.slice(0) inputs:this.inputs.slice(0)
}; };
} }
advanceMultiple(cycles : number) : number { advanceCPUMultiple(cycles : number) : number {
for (var i=0; i<cycles; i+=this.advance()) for (var i=0; i<cycles; i+=this.advanceCPU())
; ;
return i; return i;
} }
advance() { advanceCPU() {
var c = this.cpu as any; var c = this.cpu as any;
var n = 1; var n = 1;
this.probe.logExecute(this.cpu.getPC()); this.probe.logExecute(this.cpu.getPC());
@ -340,7 +338,11 @@ export abstract class BasicScanlineMachine extends BasicMachine implements Raste
abstract numTotalScanlines : number; abstract numTotalScanlines : number;
abstract cpuCyclesPerLine : number; abstract cpuCyclesPerLine : number;
abstract startScanline() : void;
abstract drawScanline() : void;
advanceFrame(maxClocks:number, trap) : number { advanceFrame(maxClocks:number, trap) : number {
this.preFrame();
var clock = 0; var clock = 0;
var endLineClock = 0; var endLineClock = 0;
this.probe.logNewFrame(); this.probe.logNewFrame();
@ -354,13 +356,16 @@ export abstract class BasicScanlineMachine extends BasicMachine implements Raste
sl = 999; sl = 999;
break; break;
} }
clock += this.advance(); clock += this.advanceCPU();
} }
this.drawScanline(); this.drawScanline();
this.probe.logNewScanline(); this.probe.logNewScanline();
} }
this.postFrame();
return clock; return clock;
} }
preFrame() { }
postFrame() { }
getRasterY() { return this.scanline; } getRasterY() { return this.scanline; }
getRasterX() { return this.frameCycles % this.cpuCyclesPerLine; } getRasterX() { return this.frameCycles % this.cpuCyclesPerLine; }
} }

View File

@ -223,9 +223,9 @@ export class AppleII extends BasicScanlineMachine {
this.ap2disp && this.ap2disp.updateScreen(); this.ap2disp && this.ap2disp.updateScreen();
return clocks; return clocks;
} }
advance() { advanceCPU() {
this.audio.feedSample(this.soundstate, 1); this.audio.feedSample(this.soundstate, 1);
return super.advance(); return super.advanceCPU();
} }
setKeyInput(key:number, code:number, flags:number) : void { setKeyInput(key:number, code:number, flags:number) : void {

View File

@ -1,11 +1,9 @@
import { MOS6502, MOS6502State } from "../cpu/MOS6502"; import { MOS6502, MOS6502State } from "../cpu/MOS6502";
import { Bus, RasterFrameBased, SavesState, SavesInputState, AcceptsROM, AcceptsKeyInput, Resettable, SampledAudioSource, SampledAudioSink, HasCPU } from "../devices"; import { BasicMachine, Bus } from "../devices";
import { KeyFlags } from "../emu"; // TODO import { KeyFlags, newAddressDecoder, padBytes, Keys, makeKeycodeMap, newKeyboardHandler, EmuHalt, dumpRAM } from "../emu";
import { hex, lzgmini, stringToByteArray, lpad, rpad, rgb2bgr } from "../util"; import { TssChannelAdapter, MasterAudio, POKEYDeviceChannel } from "../audio";
import { hex, rgb2bgr } from "../util";
import { newAddressDecoder, padBytes, Keys, makeKeycodeMap, dumpRAM, EmuHalt, newKeyboardHandler } from "../emu";
import { MasterAudio, POKEYDeviceChannel } from "../audio";
// https://atarihq.com/danb/a7800.shtml // https://atarihq.com/danb/a7800.shtml
// https://atarihq.com/danb/files/maria_r1.txt // https://atarihq.com/danb/files/maria_r1.txt
@ -17,19 +15,19 @@ interface Atari7800StateBase {
} }
interface Atari7800ControlsState { interface Atari7800ControlsState {
in : Uint8Array; inputs : Uint8Array;
} }
interface Atari7800State extends Atari7800StateBase, Atari7800ControlsState { interface Atari7800State extends Atari7800StateBase, Atari7800ControlsState {
c : MOS6502State; c : MOS6502State;
tia : { tia : {
regs : Uint8Array, regs : Uint8Array,
}, };
maria : { maria : {
regs : Uint8Array, regs : Uint8Array,
offset,dll,dlstart : number; offset,dll,dlstart : number;
dli,h16,h8 : boolean; dli,h16,h8 : boolean;
}, };
} }
const SWCHA = 0; const SWCHA = 0;
@ -64,8 +62,8 @@ const linesPerFrame = 262;
const numVisibleLines = 258-16; const numVisibleLines = 258-16;
const colorClocksPerLine = 454; // 456? const colorClocksPerLine = 454; // 456?
const colorClocksPreDMA = 28; const colorClocksPreDMA = 28;
const romLength = 0xc000; const audioOversample = 4;
const oversampling = 4; const audioSampleRate = linesPerFrame*60*audioOversample;
// TIA chip // TIA chip
@ -299,29 +297,32 @@ class MARIA {
// Atari 7800 // Atari 7800
export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSource, AcceptsROM, AcceptsKeyInput, export class Atari7800 extends BasicMachine {
Atari7800StateBase, SavesState<Atari7800State>, SavesInputState<Atari7800ControlsState> {
cpuFrequency = 1789772;
canvasWidth = 320;
numTotalScanlines = 262;
numVisibleScanlines = 258-16;
defaultROMSize = 0xc000;
cpuCyclesPerLine = 113.5;
sampleRate = audioSampleRate;
cpu : MOS6502; cpu : MOS6502;
ram : Uint8Array = new Uint8Array(0x1000); ram : Uint8Array = new Uint8Array(0x1000);
rom : Uint8Array;
bios : Uint8Array;
inputs = new Uint8Array(16);
regs6532 = new Uint8Array(4); regs6532 = new Uint8Array(4);
scanline : number = 0;
tia : TIA = new TIA(); tia : TIA = new TIA();
maria : MARIA = new MARIA(); maria : MARIA = new MARIA();
pokey1; //TODO: type pokey1; //TODO: type
audioadapter;
pixels : Uint32Array;
audio : SampledAudioSink;
handler; // TODO: type, or use ControllerPoller handler; // TODO: type, or use ControllerPoller
lastFrameCycles : number = 0; lastFrameCycles = 0;
read : (a:number) => number; read : (a:number) => number;
write : (a:number, v:number) => void; write : (a:number, v:number) => void;
constructor() { constructor() {
super();
this.cpu = new MOS6502(); this.cpu = new MOS6502();
this.read = newAddressDecoder([ this.read = newAddressDecoder([
[0x0008, 0x000d, 0x0f, (a) => { return this.readInput(a); }], [0x0008, 0x000d, 0x0f, (a) => { return this.readInput(a); }],
@ -349,11 +350,10 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
[0xbfff, 0xbfff, 0xffff, (a,v) => { }], // TODO: bank switching? [0xbfff, 0xbfff, 0xffff, (a,v) => { }], // TODO: bank switching?
[0x0000, 0xffff, 0xffff, (a,v) => { throw new EmuHalt("Write @ " + hex(a,4) + " " + hex(v,2)); }], [0x0000, 0xffff, 0xffff, (a,v) => { throw new EmuHalt("Write @ " + hex(a,4) + " " + hex(v,2)); }],
]); ]);
this.cpu.connectMemoryBus(this); this.connectCPUMemoryBus(this);
this.handler = newKeyboardHandler(this.inputs, Atari7800_KEYCODE_MAP); this.handler = newKeyboardHandler(this.inputs, Atari7800_KEYCODE_MAP);
this.pokey1 = new POKEYDeviceChannel(); this.pokey1 = new POKEYDeviceChannel();
this.pokey1.setBufferLength(oversampling*2); this.audioadapter = new TssChannelAdapter(this.pokey1, audioOversample, audioSampleRate);
this.pokey1.setSampleRate(this.getAudioParams().sampleRate);
} }
readConst(a) { return this.read(a); } //TODO? readConst(a) { return this.read(a); } //TODO?
@ -367,19 +367,6 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
} }
} }
getVideoParams() {
return {width:320, height:numVisibleLines, overscan:true};
}
getAudioParams() {
return {sampleRate:linesPerFrame*60*oversampling, stereo:false};
}
connectVideo(pixels:Uint32Array) {
this.pixels = pixels;
}
connectAudio(audio:SampledAudioSink) {
this.audio = audio;
}
//TODO this.bios = new Uint8Array(0x1000); //TODO this.bios = new Uint8Array(0x1000);
// TODO: TIA access wastes a cycle // TODO: TIA access wastes a cycle
@ -391,43 +378,32 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
var idata = this.pixels; var idata = this.pixels;
var iofs = 0; var iofs = 0;
var rgb; var rgb;
var mariaClocks = colorClocksPreDMA; // 7 CPU cycles until DMA var mc = 0;
var frameClocks = 0; var fc = 0;
this.probe.logNewFrame();
//console.log(hex(this.cpu.getPC()), hex(this.maria.dll)); //console.log(hex(this.cpu.getPC()), hex(this.maria.dll));
// visible lines // visible lines
for (var sl=0; sl<linesPerFrame; sl++) { for (var sl=0; sl<linesPerFrame; sl++) {
this.scanline = sl; this.scanline = sl;
var visible = sl < numVisibleLines; var visible = sl < numVisibleLines;
this.maria.setVBLANK(!visible); this.maria.setVBLANK(!visible);
// iterate CPU with free clocks this.maria.WSYNC = 0;
while (mariaClocks > 0) { // pre-DMA clocks
// wait for WSYNC? (end of line) while (mc < colorClocksPreDMA) {
if (this.maria.WSYNC) { if (this.maria.WSYNC) break;
if (mariaClocks >= colorClocksPreDMA) { if (trap && trap()) {
this.maria.WSYNC--;
mariaClocks = colorClocksPreDMA; // 7 CPU cycles until DMA
// TODO: frameClocks
} else {
break;
}
}
// next CPU clock
if (trap && (this.lastFrameCycles=frameClocks)>=0 && trap()) {
trap = null; trap = null;
sl = 999; sl = 999;
break; break; // TODO?
} }
mariaClocks -= 4; mc += this.advanceCPU() << 2;
frameClocks += 4;
this.cpu.advanceClock();
} }
mariaClocks += colorClocksPerLine;
// is this scanline visible? // is this scanline visible?
if (visible) { if (visible) {
// do DMA for scanline? // do DMA for scanline?
let dmaClocks = this.maria.doDMA(this); let dmaClocks = this.maria.doDMA(this);
mariaClocks -= dmaClocks; this.probe.logClocks(dmaClocks >> 2);
frameClocks += dmaClocks; mc += 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++) {
@ -437,27 +413,36 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
} }
// do interrupt? (if visible or before 1st scanline) // do interrupt? (if visible or before 1st scanline)
if ((visible || sl == linesPerFrame-1) && this.maria.doInterrupt()) { if ((visible || sl == linesPerFrame-1) && this.maria.doInterrupt()) {
//this.profiler && this.profiler.logInterrupt(0); this.probe.logInterrupt(1); // TODO?
this.cpu.NMI(); this.cpu.NMI();
//console.log("NMI", hex(this.cpu.getPC()), hex(this.maria.dll)); //console.log("NMI", hex(this.cpu.getPC()), hex(this.maria.dll));
}
// post-DMA clocks
while (mc < colorClocksPerLine) {
if (this.maria.WSYNC) {
this.probe.logClocks((colorClocksPerLine - mc) >> 2);
mc = colorClocksPerLine;
break;
}
if (trap && trap()) {
trap = null;
sl = 999;
break;
}
mc += this.advanceCPU() << 2;
} }
// audio // audio
if (this.audio) { this.audio && this.audioadapter.generate(this.audio);
const audioGain = 1.0 / 8192; this.probe.logNewScanline(); // TODO: doesn't go in right place
this.pokey1.generate(oversampling*2); // update clocks
for (let i=0; i<oversampling; i++) mc -= colorClocksPerLine;
this.audio.feedSample(this.pokey1.getBuffer()[i*2] * audioGain, 1); fc += mc;
}
} }
// update video frame
/* /*
if (!novideo) {
// set background/border color
// TODO let bkcol = this.maria.regs[0x0]; // TODO let bkcol = this.maria.regs[0x0];
// TODO $(this.video.canvas).css('background-color', COLORS_WEB[bkcol]); // TODO $(this.video.canvas).css('background-color', COLORS_WEB[bkcol]);
}
*/ */
return (this.lastFrameCycles = frameClocks); return (this.lastFrameCycles = fc);
} }
getRasterX() { return this.lastFrameCycles % colorClocksPerLine; } getRasterX() { return this.lastFrameCycles % colorClocksPerLine; }
@ -465,7 +450,7 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
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, this.defaultROMSize, true);
} }
/* /*
loadBIOS(data) { loadBIOS(data) {
@ -473,7 +458,7 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
} }
*/ */
reset() { reset() {
this.cpu.reset(); super.reset();
this.tia.reset(); this.tia.reset();
this.maria.reset(); this.maria.reset();
this.inputs.fill(0x0); this.inputs.fill(0x0);
@ -502,22 +487,18 @@ export class Atari7800 implements HasCPU, Bus, RasterFrameBased, SampledAudioSou
tia:this.tia.saveState(), tia:this.tia.saveState(),
maria:this.maria.saveState(), maria:this.maria.saveState(),
regs6532:this.regs6532.slice(0), regs6532:this.regs6532.slice(0),
in:this.inputs.slice(0) inputs:this.inputs.slice(0)
}; };
} }
loadControlsState(state:Atari7800ControlsState) : void { loadControlsState(state:Atari7800ControlsState) : void {
this.inputs.set(state.in); this.inputs.set(state.inputs);
} }
saveControlsState() : Atari7800ControlsState { saveControlsState() : Atari7800ControlsState {
return { return {
in:this.inputs.slice(0) inputs:this.inputs.slice(0)
}; };
} }
getRasterScanline() {
return this.scanline;
}
getDebugCategories() { getDebugCategories() {
return ['CPU','Stack','TIA','MARIA']; return ['CPU','Stack','TIA','MARIA'];
} }