mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-11-28 08:50:22 +00:00
atari8: fixes, faked .xex loading
This commit is contained in:
parent
c572834c8c
commit
33b2e92396
@ -850,7 +850,7 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadROM(title, data) {
|
loadROM(title, data) {
|
||||||
this.machine.loadROM(data);
|
this.machine.loadROM(data, title);
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,7 +308,7 @@ export abstract class BasicMachine extends BasicHeadlessMachine implements Sampl
|
|||||||
abstract sampleRate : number;
|
abstract sampleRate : number;
|
||||||
overscan : boolean = false;
|
overscan : boolean = false;
|
||||||
rotate : number = 0;
|
rotate : number = 0;
|
||||||
aspectRatio : number = 1.0;
|
aspectRatio : number;
|
||||||
|
|
||||||
pixels : Uint32Array;
|
pixels : Uint32Array;
|
||||||
audio : SampledAudioSink;
|
audio : SampledAudioSink;
|
||||||
|
@ -746,8 +746,9 @@ export function gtia_ntsc_to_rgb(val: number) {
|
|||||||
let cr = (val >> 4) & 15;
|
let cr = (val >> 4) & 15;
|
||||||
let lm = val & 15;
|
let lm = val & 15;
|
||||||
let crlv = cr ? color : 0;
|
let crlv = cr ? color : 0;
|
||||||
let phase = ((cr - 1) * 25 - 38) * (2 * Math.PI / 360);
|
if (cr) lm += 1;
|
||||||
let y = 256 * bright * Math.pow((lm + 1) / 16, gamma);
|
let phase = ((cr - 1) * 25 - 25) * (2 * Math.PI / 360);
|
||||||
|
let y = 256 * bright * Math.pow(lm / 16, gamma);
|
||||||
let i = crlv * Math.cos(phase);
|
let i = crlv * Math.cos(phase);
|
||||||
let q = crlv * Math.sin(phase);
|
let q = crlv * Math.sin(phase);
|
||||||
var r = y + 0.956 * i + 0.621 * q;
|
var r = y + 0.956 * i + 0.621 * q;
|
||||||
|
@ -563,7 +563,15 @@ export class AddressHeatMapView extends ProbeBitmapViewBase implements ProjectVi
|
|||||||
this.canvas.onclick = (e) => {
|
this.canvas.onclick = (e) => {
|
||||||
var pos = getMousePos(this.canvas, e);
|
var pos = getMousePos(this.canvas, e);
|
||||||
var opaddr = Math.floor(pos.x) + Math.floor(pos.y) * 256;
|
var opaddr = Math.floor(pos.x) + Math.floor(pos.y) * 256;
|
||||||
runToPC(opaddr & 0xffff);
|
var lastpc = -1;
|
||||||
|
var runpc = -1;
|
||||||
|
this.redraw( (op,addr) => {
|
||||||
|
if (runpc < 0 && lastpc >= 0 && addr == opaddr) {
|
||||||
|
runpc = lastpc;
|
||||||
|
}
|
||||||
|
if (op == ProbeFlags.EXECUTE) lastpc = addr;
|
||||||
|
});
|
||||||
|
if (runpc >= 0) runToPC(runpc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { newPOKEYAudio, TssChannelAdapter } from "../common/audio";
|
import { newPOKEYAudio, TssChannelAdapter } from "../common/audio";
|
||||||
import { EmuState, Machine } from "../common/baseplatform";
|
import { Machine } from "../common/baseplatform";
|
||||||
import { MOS6502 } from "../common/cpu/MOS6502";
|
import { MOS6502 } from "../common/cpu/MOS6502";
|
||||||
import { AcceptsKeyInput, AcceptsPaddleInput, AcceptsROM, BasicScanlineMachine, FrameBased, Probeable, RasterFrameBased, TrapCondition, VideoSource } from "../common/devices";
|
import { AcceptsKeyInput, AcceptsPaddleInput, AcceptsROM, BasicScanlineMachine, FrameBased, Probeable, TrapCondition, VideoSource } from "../common/devices";
|
||||||
import { dumpRAM, EmuHalt, KeyFlags, Keys, makeKeycodeMap, newAddressDecoder, newKeyboardHandler } from "../common/emu";
|
import { KeyFlags, Keys, makeKeycodeMap, newAddressDecoder, newKeyboardHandler } from "../common/emu";
|
||||||
import { hex, lpad, lzgmini, rgb2bgr, safe_extend, stringToByteArray } from "../common/util";
|
import { hex } from "../common/util";
|
||||||
import { BaseWASIMachine } from "../common/wasmplatform";
|
import { BaseWASIMachine } from "../common/wasmplatform";
|
||||||
import { ANTIC, MODE_SHIFT } from "./chips/antic";
|
import { ANTIC, MODE_SHIFT } from "./chips/antic";
|
||||||
import { CONSOL, GTIA, TRIG0 } from "./chips/gtia";
|
import { CONSOL, GTIA, TRIG0 } from "./chips/gtia";
|
||||||
@ -46,12 +46,11 @@ export class Atari800 extends BasicScanlineMachine {
|
|||||||
cpuFrequency = 1789773;
|
cpuFrequency = 1789773;
|
||||||
numTotalScanlines = 262;
|
numTotalScanlines = 262;
|
||||||
cpuCyclesPerLine = 114;
|
cpuCyclesPerLine = 114;
|
||||||
canvasWidth = 348; // TODO?
|
canvasWidth = 336;
|
||||||
numVisibleScanlines = 224;
|
numVisibleScanlines = 224;
|
||||||
aspectRatio = 240 / 172;
|
aspectRatio = this.canvasWidth / this.numVisibleScanlines * 0.857;
|
||||||
firstVisibleScanline = 16;
|
firstVisibleScanline = 16;
|
||||||
firstVisibleClock = 44 * 2; // ... to 215 * 2
|
firstVisibleClock = (44 - 6) * 2; // ... to 215 * 2
|
||||||
// TODO: for 400/800/5200
|
|
||||||
defaultROMSize = 0x8000;
|
defaultROMSize = 0x8000;
|
||||||
overscan = true;
|
overscan = true;
|
||||||
audioOversample = 4;
|
audioOversample = 4;
|
||||||
@ -59,7 +58,6 @@ export class Atari800 extends BasicScanlineMachine {
|
|||||||
|
|
||||||
cpu: MOS6502;
|
cpu: MOS6502;
|
||||||
ram: Uint8Array;
|
ram: Uint8Array;
|
||||||
rom: Uint8Array;
|
|
||||||
bios: Uint8Array;
|
bios: Uint8Array;
|
||||||
bus;
|
bus;
|
||||||
audio_pokey;
|
audio_pokey;
|
||||||
@ -73,6 +71,7 @@ export class Atari800 extends BasicScanlineMachine {
|
|||||||
keycode = 0;
|
keycode = 0;
|
||||||
cart_80 = false;
|
cart_80 = false;
|
||||||
cart_a0 = false;
|
cart_a0 = false;
|
||||||
|
xexdata = null;
|
||||||
// TODO: save/load vars
|
// TODO: save/load vars
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -106,10 +105,12 @@ export class Atari800 extends BasicScanlineMachine {
|
|||||||
[0xd800, 0xffff, 0xffff, (a) => { return this.bios[a - 0xd800]; }],
|
[0xd800, 0xffff, 0xffff, (a) => { return this.bios[a - 0xd800]; }],
|
||||||
]),
|
]),
|
||||||
write: newAddressDecoder([
|
write: newAddressDecoder([
|
||||||
[0x0000, 0xbfff, 0xffff, (a, v) => { this.ram[a] = v; }],
|
[0x0000, 0xbffa, 0xffff, (a, v) => { this.ram[a] = v; }],
|
||||||
|
[0xbffb, 0xbfff, 0xffff, (a, v) => { this.ram[a] = v; this.initCartA(); }],
|
||||||
[0xd000, 0xd0ff, 0x1f, (a, v) => { this.gtia.setReg(a, v); }],
|
[0xd000, 0xd0ff, 0x1f, (a, v) => { this.gtia.setReg(a, v); }],
|
||||||
[0xd200, 0xd2ff, 0xf, (a, v) => { this.writePokey(a, v); }],
|
[0xd200, 0xd2ff, 0xf, (a, v) => { this.writePokey(a, v); }],
|
||||||
[0xd400, 0xd4ff, 0xf, (a, v) => { this.antic.setReg(a, v); }],
|
[0xd400, 0xd4ff, 0xf, (a, v) => { this.antic.setReg(a, v); }],
|
||||||
|
[0xd500, 0xd5ff, 0xff, (a, v) => { this.writeMapper(a, v); }],
|
||||||
]),
|
]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -119,11 +120,11 @@ export class Atari800 extends BasicScanlineMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
console.log(this.saveState());
|
|
||||||
super.reset();
|
super.reset();
|
||||||
this.antic.reset();
|
this.antic.reset();
|
||||||
this.gtia.reset();
|
this.gtia.reset();
|
||||||
this.keycode = 0;
|
this.keycode = 0;
|
||||||
|
if (this.xexdata) this.cart_a0 = true; // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
read(a) {
|
read(a) {
|
||||||
@ -197,8 +198,6 @@ export class Atari800 extends BasicScanlineMachine {
|
|||||||
// update GTIA
|
// update GTIA
|
||||||
// get X coordinate within scanline
|
// get X coordinate within scanline
|
||||||
let xofs = this.antic.h * 4 - this.firstVisibleClock;
|
let xofs = this.antic.h * 4 - this.firstVisibleClock;
|
||||||
// correct for HSCROL
|
|
||||||
if (this.antic.dliop & 0x10) xofs += (this.antic.regs[4] & 1) << 1;
|
|
||||||
// GTIA tick functions
|
// GTIA tick functions
|
||||||
let gtiatick1 = () => {
|
let gtiatick1 = () => {
|
||||||
this.gtia.clockPulse1();
|
this.gtia.clockPulse1();
|
||||||
@ -209,8 +208,17 @@ export class Atari800 extends BasicScanlineMachine {
|
|||||||
this.linergb[xofs++] = this.gtia.rgb;
|
this.linergb[xofs++] = this.gtia.rgb;
|
||||||
}
|
}
|
||||||
// tick 4 GTIA clocks for each CPU/ANTIC cycle
|
// tick 4 GTIA clocks for each CPU/ANTIC cycle
|
||||||
|
this.gtia.clockPulse4();
|
||||||
|
// correct for HSCROL -- bias antic +2, bias gtia -1
|
||||||
|
if ((this.antic.dliop & 0x10) && (this.antic.regs[4] & 1)) {
|
||||||
|
xofs += 2;
|
||||||
|
this.gtia.setBias(-1);
|
||||||
|
} else {
|
||||||
|
this.gtia.setBias(0);
|
||||||
|
}
|
||||||
let bp = MODE_SHIFT[this.antic.mode];
|
let bp = MODE_SHIFT[this.antic.mode];
|
||||||
if (bp < 8 || (xofs & 4) == 0) { this.gtia.an = this.antic.shiftout(); }
|
let odd = this.antic.h & 1;
|
||||||
|
if (bp < 8 || odd) { this.gtia.an = this.antic.shiftout(); }
|
||||||
gtiatick1();
|
gtiatick1();
|
||||||
if (bp == 1) { this.gtia.an = this.antic.shiftout(); }
|
if (bp == 1) { this.gtia.an = this.antic.shiftout(); }
|
||||||
gtiatick2();
|
gtiatick2();
|
||||||
@ -222,12 +230,12 @@ export class Atari800 extends BasicScanlineMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadState(state: any) {
|
loadState(state: any) {
|
||||||
|
this.loadControlsState(state);
|
||||||
this.cpu.loadState(state.c);
|
this.cpu.loadState(state.c);
|
||||||
this.ram.set(state.ram);
|
this.ram.set(state.ram);
|
||||||
this.antic.loadState(state.antic);
|
this.antic.loadState(state.antic);
|
||||||
this.gtia.loadState(state.gtia);
|
this.gtia.loadState(state.gtia);
|
||||||
this.irq_pokey.loadState(state.pokey);
|
this.irq_pokey.loadState(state.pokey);
|
||||||
this.loadControlsState(state);
|
|
||||||
this.lastdmabyte = state.lastdmabyte;
|
this.lastdmabyte = state.lastdmabyte;
|
||||||
this.keycode = state.keycode;
|
this.keycode = state.keycode;
|
||||||
}
|
}
|
||||||
@ -240,7 +248,7 @@ export class Atari800 extends BasicScanlineMachine {
|
|||||||
pokey: this.irq_pokey.saveState(),
|
pokey: this.irq_pokey.saveState(),
|
||||||
inputs: this.inputs.slice(0),
|
inputs: this.inputs.slice(0),
|
||||||
lastdmabyte: this.lastdmabyte,
|
lastdmabyte: this.lastdmabyte,
|
||||||
keycode: this.keycode, // TODO: inputs?
|
keycode: this.keycode,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
loadControlsState(state) {
|
loadControlsState(state) {
|
||||||
@ -300,9 +308,26 @@ export class Atari800 extends BasicScanlineMachine {
|
|||||||
this.probe.logInterrupt(1);
|
this.probe.logInterrupt(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadROM(rom: Uint8Array) {
|
loadROM(rom: Uint8Array, title: string) {
|
||||||
if (rom.length != 0x2000 && rom.length != 0x4000 && rom.length != 0x8000)
|
// XEX file?
|
||||||
throw new Error("Sorry, this platform can only load 8/16/32 KB cartridges at the moment.");
|
if (title && title.toLowerCase().endsWith('.xex') && rom[0] == 0xff && rom[1] == 0xff) {
|
||||||
|
// TODO: we fake a cartridge
|
||||||
|
this.xexdata = rom;
|
||||||
|
let cart = new Uint8Array(0x1000);
|
||||||
|
cart.set([0x00, 0x01, 0x00, 0x04, 0x00, 0x01], 0xffa);
|
||||||
|
this.loadCartridge(cart);
|
||||||
|
} else {
|
||||||
|
this.loadCartridge(rom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCartridge(rom: Uint8Array) {
|
||||||
|
// strip off header
|
||||||
|
if (rom[0] == 0x43 && rom[1] == 0x41 && rom[2] == 0x52 && rom[3] == 0x54) {
|
||||||
|
rom = rom.slice(16);
|
||||||
|
}
|
||||||
|
if (rom.length != 0x1000 && rom.length != 0x2000 && rom.length != 0x4000 && rom.length != 0x8000)
|
||||||
|
throw new Error("Sorry, this platform can only load 4/8/16/32 KB cartridges at the moment.");
|
||||||
// TODO: support other than 8 KB carts
|
// TODO: support other than 8 KB carts
|
||||||
// support 4/8/16/32 KB carts
|
// support 4/8/16/32 KB carts
|
||||||
let rom2 = new Uint8Array(0x8000);
|
let rom2 = new Uint8Array(0x8000);
|
||||||
@ -310,9 +335,63 @@ export class Atari800 extends BasicScanlineMachine {
|
|||||||
rom2.set(rom, i);
|
rom2.set(rom, i);
|
||||||
}
|
}
|
||||||
this.cart_a0 = true; // TODO
|
this.cart_a0 = true; // TODO
|
||||||
if (rom.length == 0x4000) { this.cart_80 = true; }
|
this.cart_80 = rom.length == 0x4000;
|
||||||
super.loadROM(rom2);
|
super.loadROM(rom2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeMapper(addr:number, value:number) {
|
||||||
|
if (addr == 0xff) {
|
||||||
|
if (value == 0x80) this.cart_80 = false;
|
||||||
|
if (value == 0xa0) this.cart_a0 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
loadXEX(rom: Uint8Array) {
|
||||||
|
let ofs = 2;
|
||||||
|
let cart = this.ram;
|
||||||
|
let cartofs = 0x100; // stub routine in stack page
|
||||||
|
while (ofs < rom.length) {
|
||||||
|
let start = rom[ofs+0] + rom[ofs+1] * 256;
|
||||||
|
let end = rom[ofs+2] + rom[ofs+3] * 256;
|
||||||
|
console.log('XEX', ofs, hex(start), hex(end));
|
||||||
|
ofs += 4;
|
||||||
|
for (let i=start; i<=end; i++) {
|
||||||
|
this.ram[i] = rom[ofs++];
|
||||||
|
}
|
||||||
|
var runaddr = this.ram[0x2e0] + this.ram[0x2e1]*256;
|
||||||
|
var initaddr = this.ram[0x2e2] + this.ram[0x2e3]*256;
|
||||||
|
console.log('XEX run', hex(runaddr), 'init', hex(initaddr));
|
||||||
|
if (initaddr) {
|
||||||
|
cart[cartofs++] = 0x20;
|
||||||
|
cart[cartofs++] = initaddr & 0xff;
|
||||||
|
cart[cartofs++] = initaddr >> 8;
|
||||||
|
}
|
||||||
|
if (ofs > rom.length) throw new Error("Bad .XEX file format");
|
||||||
|
}
|
||||||
|
if (runaddr) {
|
||||||
|
cart[cartofs++] = 0xa9; // lda #$a0
|
||||||
|
cart[cartofs++] = 0xa0;
|
||||||
|
cart[cartofs++] = 0x8d; // sta $d5ff (disable cart)
|
||||||
|
cart[cartofs++] = 0xff;
|
||||||
|
cart[cartofs++] = 0xd5;
|
||||||
|
cart[cartofs++] = 0x4c; // jmp runaddr
|
||||||
|
cart[cartofs++] = runaddr & 0xff;
|
||||||
|
cart[cartofs++] = runaddr >> 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initCartA() {
|
||||||
|
//console.log('init', hex(this.cpu.getPC()));
|
||||||
|
// disable cartridges and load XEX
|
||||||
|
if (this.cpu.getPC() == 0xf17f) {
|
||||||
|
if (this.xexdata) {
|
||||||
|
this.loadXEX(this.xexdata);
|
||||||
|
}
|
||||||
|
//this.cart_80 = this.cart_a0 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Atari5200 extends Atari800 {
|
export class Atari5200 extends Atari800 {
|
||||||
|
@ -10,8 +10,8 @@ import { hex, lpad, safe_extend } from "../../common/util";
|
|||||||
// http://www.atarimuseum.com/videogames/consoles/5200/conv_to_5200.html
|
// http://www.atarimuseum.com/videogames/consoles/5200/conv_to_5200.html
|
||||||
// https://www.virtualdub.org/downloads/Altirra%20Hardware%20Reference%20Manual.pdf
|
// https://www.virtualdub.org/downloads/Altirra%20Hardware%20Reference%20Manual.pdf
|
||||||
|
|
||||||
const PF_LEFT = [0, 29, 21, 13];
|
const PF_LEFT = [0, 25, 17, 9];
|
||||||
const PF_RIGHT = [0, 29 + 64, 21 + 80, 13 + 96];
|
const PF_RIGHT = [0, 25 + 64, 17 + 80, 9 + 96];
|
||||||
|
|
||||||
const DMACTL = 0;
|
const DMACTL = 0;
|
||||||
const CHACTL = 1;
|
const CHACTL = 1;
|
||||||
@ -38,8 +38,9 @@ const NMIST_CYCLE = 12;
|
|||||||
const NMI_CYCLE = 24;
|
const NMI_CYCLE = 24;
|
||||||
const WSYNC_CYCLE = 212;
|
const WSYNC_CYCLE = 212;
|
||||||
|
|
||||||
const ANTIC_LEFT = 17; // gtia 34
|
const ANTIC_LEFT = 17 - 4; // gtia 34, 4 cycle delay
|
||||||
const ANTIC_RIGHT = 110; // gtia 221
|
const ANTIC_RIGHT = 110 - 4; // gtia 221, 4 cycle delay
|
||||||
|
const LAST_DMA_H = 105; // last DMA cycle
|
||||||
|
|
||||||
const MODE_LINES = [0, 0, 8, 10, 8, 16, 8, 16, 8, 4, 4, 2, 1, 2, 1, 1];
|
const MODE_LINES = [0, 0, 8, 10, 8, 16, 8, 16, 8, 4, 4, 2, 1, 2, 1, 1];
|
||||||
// how many bits before DMA clock repeats?
|
// how many bits before DMA clock repeats?
|
||||||
@ -203,9 +204,12 @@ export class ANTIC {
|
|||||||
|
|
||||||
nextScreen(): number {
|
nextScreen(): number {
|
||||||
let b = this.read(this.scanaddr);
|
let b = this.read(this.scanaddr);
|
||||||
this.scanaddr = ((this.scanaddr + 1) & 0xfff) | (this.scanaddr & ~0xfff);
|
this.incScanAddr();
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
incScanAddr() {
|
||||||
|
this.scanaddr = ((this.scanaddr + 1) & 0xfff) | (this.scanaddr & ~0xfff);
|
||||||
|
}
|
||||||
|
|
||||||
dlDMAEnabled() { return this.regs[DMACTL] & 0b100000; }
|
dlDMAEnabled() { return this.regs[DMACTL] & 0b100000; }
|
||||||
|
|
||||||
@ -216,10 +220,10 @@ export class ANTIC {
|
|||||||
return this.dma_enabled && !this.linesleft;
|
return this.dma_enabled && !this.linesleft;
|
||||||
}
|
}
|
||||||
isPlayerDMAEnabled() {
|
isPlayerDMAEnabled() {
|
||||||
return this.dma_enabled && this.regs[DMACTL] & 0b1000;
|
return this.regs[DMACTL] & 0b1000;
|
||||||
}
|
}
|
||||||
isMissileDMAEnabled() {
|
isMissileDMAEnabled() {
|
||||||
return this.dma_enabled && this.regs[DMACTL] & 0b1100;
|
return this.regs[DMACTL] & 0b1100;
|
||||||
}
|
}
|
||||||
|
|
||||||
clockPulse(): boolean {
|
clockPulse(): boolean {
|
||||||
@ -280,7 +284,7 @@ export class ANTIC {
|
|||||||
}
|
}
|
||||||
this.output = 0; // background color (TODO: only for blank lines)
|
this.output = 0; // background color (TODO: only for blank lines)
|
||||||
if (this.mode >= 2 && this.period) {
|
if (this.mode >= 2 && this.period) {
|
||||||
let candma = this.h < 106;
|
let candma = this.h <= LAST_DMA_H;
|
||||||
this.dmaclock <<= 1;
|
this.dmaclock <<= 1;
|
||||||
if (this.dmaclock & (1 << this.period)) {
|
if (this.dmaclock & (1 << this.period)) {
|
||||||
this.dmaclock |= 1;
|
this.dmaclock |= 1;
|
||||||
@ -289,13 +293,21 @@ export class ANTIC {
|
|||||||
if (this.h == this.right) { this.dmaclock &= ~1; this.dmaidx++; }
|
if (this.h == this.right) { this.dmaclock &= ~1; this.dmaidx++; }
|
||||||
if (this.dmaclock & 1) {
|
if (this.dmaclock & 1) {
|
||||||
if (this.mode < 8 && this.yofs == 0) { // only read chars on 1st line
|
if (this.mode < 8 && this.yofs == 0) { // only read chars on 1st line
|
||||||
|
if (candma) {
|
||||||
this.linebuf[this.dmaidx] = this.nextScreen(); // read char name
|
this.linebuf[this.dmaidx] = this.nextScreen(); // read char name
|
||||||
|
} else {
|
||||||
|
this.incScanAddr();
|
||||||
|
}
|
||||||
did_dma = candma;
|
did_dma = candma;
|
||||||
}
|
}
|
||||||
this.dmaidx++;
|
this.dmaidx++;
|
||||||
} else if (this.dmaclock & 8) {
|
} else if (this.dmaclock & 8) {
|
||||||
this.ch = this.linebuf[this.dmaidx - 4 / this.period]; // latch char
|
this.ch = this.linebuf[this.dmaidx - 4 / this.period]; // latch char
|
||||||
|
if (candma) {
|
||||||
this.readBitmapData(); // read bitmap
|
this.readBitmapData(); // read bitmap
|
||||||
|
} else {
|
||||||
|
if (this.mode >= 8) this.incScanAddr();
|
||||||
|
}
|
||||||
did_dma = candma;
|
did_dma = candma;
|
||||||
}
|
}
|
||||||
this.output = this.h >= this.left + 3 && this.h <= this.right + 2 ? 4 : 0;
|
this.output = this.h >= this.left + 3 && this.h <= this.right + 2 ? 4 : 0;
|
||||||
|
@ -33,6 +33,8 @@ const P0PL = 0xc;
|
|||||||
export const TRIG0 = 0x10;
|
export const TRIG0 = 0x10;
|
||||||
export const CONSOL = 0x1f;
|
export const CONSOL = 0x1f;
|
||||||
|
|
||||||
|
const HOFFSET = -9; // bias to account for antic->gtia delay
|
||||||
|
|
||||||
const PRIOR_TABLE : number[] = [
|
const PRIOR_TABLE : number[] = [
|
||||||
0,1,2,3, 7,7,7,7, 8,8,8,8, 4,5,6,7, // 0001 - 0
|
0,1,2,3, 7,7,7,7, 8,8,8,8, 4,5,6,7, // 0001 - 0
|
||||||
0,1,2,3, 7,7,7,7, 8,8,8,8, 4,5,6,7, // 0001
|
0,1,2,3, 7,7,7,7, 8,8,8,8, 4,5,6,7, // 0001
|
||||||
@ -52,6 +54,13 @@ const PRIOR_TABLE : number[] = [
|
|||||||
2,3,4,5, 7,7,7,7, 8,8,8,8, 0,1,6,7, // 1000
|
2,3,4,5, 7,7,7,7, 8,8,8,8, 0,1,6,7, // 1000
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const MODE_9_LOOKUP = [
|
||||||
|
COLPM0+0, COLPM0+1, COLPM0+2, COLPM0+3,
|
||||||
|
COLPF0+0, COLPF0+1, COLPF0+2, COLPF0+3,
|
||||||
|
COLBK, COLBK, COLBK, COLBK,
|
||||||
|
COLPF0+0, COLPF0+1, COLPF0+2, COLPF0+3,
|
||||||
|
]
|
||||||
|
|
||||||
export class GTIA {
|
export class GTIA {
|
||||||
regs = new Uint8Array(0x20);
|
regs = new Uint8Array(0x20);
|
||||||
readregs = new Uint8Array(0x20);
|
readregs = new Uint8Array(0x20);
|
||||||
@ -62,6 +71,9 @@ export class GTIA {
|
|||||||
an = 0;
|
an = 0;
|
||||||
rgb = 0;
|
rgb = 0;
|
||||||
pmcol = 0;
|
pmcol = 0;
|
||||||
|
gtiacol = 0;
|
||||||
|
gtiacol2 = 0;
|
||||||
|
hbias = HOFFSET;
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
this.regs.fill(0);
|
this.regs.fill(0);
|
||||||
@ -77,6 +89,11 @@ export class GTIA {
|
|||||||
}
|
}
|
||||||
setReg(a: number, v: number) {
|
setReg(a: number, v: number) {
|
||||||
switch (a) {
|
switch (a) {
|
||||||
|
case COLPM0: case COLPM0+1: case COLPM0+2: case COLPM0+3:
|
||||||
|
case COLPF0: case COLPF0+1: case COLPF0+2: case COLPF0+3:
|
||||||
|
case COLBK:
|
||||||
|
v &= 0xfe; // bit 0 unused in color regs
|
||||||
|
break;
|
||||||
case HITCLR:
|
case HITCLR:
|
||||||
this.readregs.fill(0, 0, 16);
|
this.readregs.fill(0, 0, 16);
|
||||||
return;
|
return;
|
||||||
@ -93,6 +110,9 @@ export class GTIA {
|
|||||||
sync() {
|
sync() {
|
||||||
this.count = 0;
|
this.count = 0;
|
||||||
}
|
}
|
||||||
|
setBias(b: number) {
|
||||||
|
this.hbias = HOFFSET + b;
|
||||||
|
}
|
||||||
updateGfx(h: number, data: number) {
|
updateGfx(h: number, data: number) {
|
||||||
switch (h) {
|
switch (h) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -104,6 +124,10 @@ export class GTIA {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
getPlayfieldColor(): number {
|
getPlayfieldColor(): number {
|
||||||
|
// which GTIA mode?
|
||||||
|
switch (this.regs[PRIOR] >> 6) {
|
||||||
|
// normal mode
|
||||||
|
case 0:
|
||||||
switch (this.an) {
|
switch (this.an) {
|
||||||
case 0:
|
case 0:
|
||||||
return COLBK;
|
return COLBK;
|
||||||
@ -113,23 +137,19 @@ export class GTIA {
|
|||||||
// combine PF2 hue and PF1 luminance
|
// combine PF2 hue and PF1 luminance
|
||||||
return (this.regs[COLPF2] & 0xf0) | (this.regs[COLPF1] & 0x0f) | 0x100;
|
return (this.regs[COLPF2] & 0xf0) | (this.regs[COLPF1] & 0x0f) | 0x100;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
// mode 9 -- 16 luminances
|
||||||
|
case 1:
|
||||||
|
return (this.regs[COLBK] & 0xf0) | (this.gtiacol & 0xf) | 0x100;
|
||||||
|
// mode 10 -- 9 colors from registers
|
||||||
|
case 2:
|
||||||
|
return MODE_9_LOOKUP[this.gtiacol];
|
||||||
|
// mode 11 -- 16 hues
|
||||||
|
case 3:
|
||||||
|
return (this.regs[COLBK] & 0xf) | (this.gtiacol << 4) | 0x100;
|
||||||
|
}
|
||||||
return 0x100; // black
|
return 0x100; // black
|
||||||
}
|
}
|
||||||
clockPulse1(): void {
|
|
||||||
this.processPlayerMissile();
|
|
||||||
this.clockPulse2();
|
|
||||||
this.count++;
|
|
||||||
}
|
|
||||||
clockPulse2(): void {
|
|
||||||
var col: number;
|
|
||||||
if (this.pmcol >= 0) {
|
|
||||||
col = this.pmcol;
|
|
||||||
} else {
|
|
||||||
let pf = this.getPlayfieldColor();
|
|
||||||
col = pf & 0x100 ? pf & 0xff : this.regs[pf];
|
|
||||||
}
|
|
||||||
this.rgb = COLORS_RGBA[col];
|
|
||||||
}
|
|
||||||
anySpriteActive() {
|
anySpriteActive() {
|
||||||
return this.shiftregs[0] | this.shiftregs[1] | this.shiftregs[2]
|
return this.shiftregs[0] | this.shiftregs[1] | this.shiftregs[2]
|
||||||
| this.shiftregs[3] | this.shiftregs[4] | this.shiftregs[5]
|
| this.shiftregs[3] | this.shiftregs[4] | this.shiftregs[5]
|
||||||
@ -144,6 +164,8 @@ export class GTIA {
|
|||||||
this.pmcol = -1;
|
this.pmcol = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// TODO: multiple color player enable
|
||||||
|
// TODO: gtia, hi-res mode collisions
|
||||||
// compute gfx and collisions for players/missiles
|
// compute gfx and collisions for players/missiles
|
||||||
let priobias = (this.regs[PRIOR] & 15) << 4; // TODO
|
let priobias = (this.regs[PRIOR] & 15) << 4; // TODO
|
||||||
let topprio = PRIOR_TABLE[(this.an & 7) + 8 + priobias];
|
let topprio = PRIOR_TABLE[(this.an & 7) + 8 + priobias];
|
||||||
@ -175,7 +197,7 @@ export class GTIA {
|
|||||||
this.readregs[M0PF + i] |= 1 << pfset;
|
this.readregs[M0PF + i] |= 1 << pfset;
|
||||||
}
|
}
|
||||||
this.readregs[M0PL + i] |= ppmask;
|
this.readregs[M0PL + i] |= ppmask;
|
||||||
let prio = PRIOR_TABLE[i + 4 + priobias];
|
let prio = PRIOR_TABLE[i + priobias];
|
||||||
if (prio < topprio) {
|
if (prio < topprio) {
|
||||||
topobj = i + 4;
|
topobj = i + 4;
|
||||||
topprio = prio;
|
topprio = prio;
|
||||||
@ -194,7 +216,7 @@ export class GTIA {
|
|||||||
shiftObject(i: number) {
|
shiftObject(i: number) {
|
||||||
let bit = (this.shiftregs[i] & 0x80000000) != 0;
|
let bit = (this.shiftregs[i] & 0x80000000) != 0;
|
||||||
this.shiftregs[i] <<= 1;
|
this.shiftregs[i] <<= 1;
|
||||||
if (this.regs[HPOSP0 + i] - 1 == this.count) {
|
if (this.regs[HPOSP0 + i] + this.hbias == this.count) {
|
||||||
this.triggerObject(i);
|
this.triggerObject(i);
|
||||||
}
|
}
|
||||||
return bit;
|
return bit;
|
||||||
@ -221,6 +243,30 @@ export class GTIA {
|
|||||||
this.shiftregs[i] = data;
|
this.shiftregs[i] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clockPulse1(): void {
|
||||||
|
this.processPlayerMissile();
|
||||||
|
this.clockPulse2();
|
||||||
|
this.count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
clockPulse2(): void {
|
||||||
|
var col: number;
|
||||||
|
if (this.pmcol >= 0) {
|
||||||
|
col = this.pmcol;
|
||||||
|
} else {
|
||||||
|
let pf = this.getPlayfieldColor();
|
||||||
|
col = pf & 0x100 ? pf & 0xff : this.regs[pf];
|
||||||
|
}
|
||||||
|
this.rgb = COLORS_RGBA[col];
|
||||||
|
// TODO: hires modes return 8, so other modes wont work
|
||||||
|
this.gtiacol2 = (this.gtiacol2 << 1) | (this.an >> 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
clockPulse4() {
|
||||||
|
// latch GTIA buffer
|
||||||
|
this.gtiacol = this.gtiacol2 & 15;
|
||||||
|
}
|
||||||
|
|
||||||
static stateToLongString(state): string {
|
static stateToLongString(state): string {
|
||||||
let s = ''
|
let s = ''
|
||||||
s += `X: ${lpad(state.count, 3)} ANTIC: ${hex(state.an, 1)} PM: ${hex(state.pmcol, 3)}\n`;
|
s += `X: ${lpad(state.count, 3)} ANTIC: ${hex(state.an, 1)} PM: ${hex(state.pmcol, 3)}\n`;
|
||||||
|
@ -195,6 +195,7 @@ class Atari5200Platform extends Atari800Platform {
|
|||||||
///
|
///
|
||||||
|
|
||||||
PLATFORMS['atari8-800.xlmame'] = Atari800MAMEPlatform
|
PLATFORMS['atari8-800.xlmame'] = Atari800MAMEPlatform
|
||||||
|
PLATFORMS['atari8-800xl.mame'] = Atari800MAMEPlatform // for dithertron
|
||||||
PLATFORMS['atari8-5200.mame'] = Atari5200MAMEPlatform
|
PLATFORMS['atari8-5200.mame'] = Atari5200MAMEPlatform
|
||||||
PLATFORMS['atari8-800.xlwasm'] = Atari800WASMPlatform
|
PLATFORMS['atari8-800.xlwasm'] = Atari800WASMPlatform
|
||||||
PLATFORMS['atari8-800'] = Atari800Platform
|
PLATFORMS['atari8-800'] = Atari800Platform
|
||||||
|
@ -367,7 +367,7 @@ describe('Platform Replay', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('Should run atari5200', async () => {
|
it('Should run atari5200', async () => {
|
||||||
await testPlatform('atari8-5200', 'acid5200.rom', 1000, (platform, frameno) => {
|
await testPlatform('atari8-5200', 'acid5200.rom', 1100, (platform, frameno) => {
|
||||||
if (frameno == 999) {
|
if (frameno == 999) {
|
||||||
let s = '';
|
let s = '';
|
||||||
for (let i=0; i<40; i++) {
|
for (let i=0; i<40; i++) {
|
||||||
|
Loading…
Reference in New Issue
Block a user