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:
parent
43f65b99ae
commit
768253751a
|
@ -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; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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'];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user