astrocade: worked on arcade emulation

This commit is contained in:
Steven Hugg 2023-12-13 16:22:17 -05:00
parent 0ede0e514b
commit d08d73f422
2 changed files with 160 additions and 23 deletions

View File

@ -71,6 +71,7 @@ const _BallyAstrocade = function(arcade:boolean) {
const swidth = arcade ? 320 : 160; const swidth = arcade ? 320 : 160;
const sheight = arcade ? 204 : 102; const sheight = arcade ? 204 : 102;
const swbytes = swidth >> 2; const swbytes = swidth >> 2;
const samask = arcade ? 0x3fff : 0xfff;
const INITIAL_WATCHDOG = 256; const INITIAL_WATCHDOG = 256;
const PIXEL_ON = 0xffeeeeee; const PIXEL_ON = 0xffeeeeee;
const PIXEL_OFF = 0xff000000; const PIXEL_OFF = 0xff000000;
@ -90,19 +91,19 @@ const _BallyAstrocade = function(arcade:boolean) {
var palinds = new Uint8Array(8); var palinds = new Uint8Array(8);
var refreshlines = 0; var refreshlines = 0;
var dirtylines = new Uint8Array(arcade ? 262 : 131); var dirtylines = new Uint8Array(arcade ? 262 : 131);
var vidactive = false;
var rotdata = new Uint8Array(4); var rotdata = new Uint8Array(4);
var rotcount = 0; var rotcount = 0;
var intst = 0; var intst = 0;
var waitstates = 0; var waitstates = 0;
var patboard = new Uint8Array(0x08);
var patdest = 0;
function ramwrite(a: number, v: number) { function ramwrite(a: number, v: number) {
// set RAM // set RAM
ram[a] = v; ram[a] = v;
waitstates++; waitstates++;
// mark scanline as dirty // mark scanline as dirty
dirtylines[((a & 0xfff) / swbytes) | 0] = 1; dirtylines[((a & samask) / swbytes) | 0] = 1;
// this was old behavior where we updated instantly // this was old behavior where we updated instantly
// but it had problems if we had mid-screen palette changes // but it had problems if we had mid-screen palette changes
//ramupdate(a, v); //ramupdate(a, v);
@ -201,7 +202,105 @@ const _BallyAstrocade = function(arcade:boolean) {
function refreshall() { function refreshall() {
refreshlines = sheight; refreshlines = sheight;
} }
// bally astrocade pattern board
// https://github.com/mamedev/mame/blob/7ff10685c56a6e123336c684e5e96fdb9e8b3674/src/mame/midway/astrocde_v.cpp#L726
function xfer_patboard() {
let m_pattern_source = patboard[0] | (patboard[1] << 8);
let m_pattern_mode = patboard[2] & 0x3f;
let m_pattern_skip = patboard[3];
let m_pattern_dest = (patdest & 0xff) | (patboard[4] << 8);
let m_pattern_width = patboard[5];
let m_pattern_height = patboard[6] + 1;
let curwidth: number;
let u13ff: number = 0;
let cycles: number = 0;
u13ff = 0;
if ((m_pattern_mode & 0x02) === 0) {
u13ff = 1;
}
function incrementSource(): void {
if (u13ff && (m_pattern_mode & 0x04) !== 0 && (curwidth !== 0 || (m_pattern_mode & 0x08) === 0)) {
m_pattern_source++;
}
if ((m_pattern_mode & 0x02) !== 0) {
u13ff ^= 1;
}
}
function incrementDest(): void {
if (curwidth !== 0) {
if ((m_pattern_mode & 0x20) !== 0) {
m_pattern_dest++;
} else {
m_pattern_dest--;
}
}
}
// Loop over height
while (m_pattern_height >= 0) {
let carry: number;
curwidth = m_pattern_width;
// Loop over width
while (curwidth >= 0) {
let busaddr: number;
let busdata: number;
// Read Phase
busaddr = (m_pattern_mode & 0x01) === 0 ? m_pattern_source : m_pattern_dest;
if (curwidth === 0 && (m_pattern_mode & 0x08) !== 0) {
busdata = 0;
} else {
busdata = membus.read(m_pattern_source);
}
if ((m_pattern_mode & 0x01) === 0) {
incrementSource();
} else {
incrementDest();
}
// Write Phase
busaddr = (m_pattern_mode & 0x01) !== 0 ? m_pattern_source : m_pattern_dest;
ramwrite(busaddr, busdata);
if ((m_pattern_mode & 0x01) === 0) {
incrementDest();
} else {
incrementSource();
}
cycles += 4;
curwidth--;
}
// At the end of each row, adjust m_pattern_dest
carry = ((m_pattern_dest & 0xff) + m_pattern_skip) & 0x100;
m_pattern_dest = (m_pattern_dest & 0xff00) | ((m_pattern_dest + m_pattern_skip) & 0xff);
if ((m_pattern_mode & 0x10) === 0) {
m_pattern_dest += carry;
} else {
m_pattern_dest -= carry ^ 0x100;
}
m_pattern_height--;
}
// Adjust m_maincpu.icount
// m_maincpu.adjust_icount(-cycles);
// Replace the above line with the actual adjustment of icount.
}
this.drawScanline = (sl:number) => { this.drawScanline = (sl:number) => {
// interrupt // interrupt
if (sl == inlin && (inmod & 0x8)) { if (sl == inlin && (inmod & 0x8)) {
@ -226,9 +325,9 @@ const _BallyAstrocade = function(arcade:boolean) {
ram = r; ram = r;
inputs = inp; inputs = inp;
psg = psgg; psg = psgg;
//bios = padBytes(ASTROCADE_MINIMAL_BIOS, 0x2000);
bios = padBytes(new lzgmini().decode(stringToByteArray(atob(ASTROLIBRE_BIOS_LZG))), 0x2000);
if (!arcade) { if (!arcade) {
//bios = padBytes(ASTROCADE_MINIMAL_BIOS, 0x2000);
bios = padBytes(new lzgmini().decode(stringToByteArray(atob(ASTROLIBRE_BIOS_LZG))), 0x2000);
// game console // game console
membus = { membus = {
read: newAddressDecoder([ read: newAddressDecoder([
@ -246,13 +345,14 @@ const _BallyAstrocade = function(arcade:boolean) {
membus = { membus = {
read: newAddressDecoder([ read: newAddressDecoder([
[0x4000, 0x7fff, 0x3fff, function(a) { return ram[a]; }], // screen RAM [0x4000, 0x7fff, 0x3fff, function(a) { return ram[a]; }], // screen RAM
[0xd000, 0xdfff, 0xfff, function(a) { return ram[a + 0x4000]; }], // static RAM [0xd000, 0xdfff, 0x0fff, function(a) { return ram[a + 0x4000]; }], // static RAM
[0x0000, 0xafff, 0xffff, function(a) { return rom ? rom[a] : 0; }], // ROM (0-3fff,8000-afff) [0x0000, 0x3fff, 0x3fff, function(a) { return rom ? rom[a] : 0; }], // ROM
[0x8000, 0xbfff, 0x3fff, function(a) { return rom ? rom[a + 0x4000] : 0; }], // ROM
]), ]),
write: newAddressDecoder([ write: newAddressDecoder([
[0x4000, 0x7fff, 0x3fff, ramwrite], [0x4000, 0x7fff, 0x3fff, ramwrite],
[0xd000, 0xdfff, 0xfff, function(a, v) { ramwrite(a + 0x4000, v); }], // static RAM
[0x0000, 0x3fff, 0x3fff, magicwrite], [0x0000, 0x3fff, 0x3fff, magicwrite],
[0xd000, 0xdfff, 0x0fff, function(a, v) { ramwrite(a + 0x4000, v); }], // static RAM
]), ]),
} }
} }
@ -273,6 +373,19 @@ const _BallyAstrocade = function(arcade:boolean) {
return rtn; return rtn;
}, },
write: function(addr, val) { write: function(addr, val) {
if (addr == 0xa55b) {
// TODO: protected_ram_enable_w
return;
}
addr &= 0xff;
// pattern board
if (addr > 0x78 && addr < 0x80) {
patboard[addr & 7] = val;
if (addr == 0x72) { patdest = 0; }
if (addr == 0x74) { patdest = (patdest + patboard[3]) & 0xff; }
if (addr == 0x7e) { xfer_patboard(); }
return;
}
addr &= 0x1f; addr &= 0x1f;
val &= 0xff; val &= 0xff;
switch (addr) { switch (addr) {
@ -332,8 +445,15 @@ const _BallyAstrocade = function(arcade:boolean) {
case 0x19: // XPAND case 0x19: // XPAND
xpand = val; xpand = val;
break; break;
case 0x1a:
case 0x1b:
case 0x1c:
case 0x1d:
case 0x1e:
//psg2.setACRegister(addr - 0x1a, val);
break;
default: default:
console.log('IO write', hex(addr, 4), hex(val, 2)); //console.log('IO write', hex(addr, 4), hex(val, 2));
break; break;
} }
} }
@ -372,6 +492,8 @@ const _BallyAstrocade = function(arcade:boolean) {
rotdata.set(state.rotdata); rotdata.set(state.rotdata);
intst = state.intst; intst = state.intst;
inputs.set(state.inputs); inputs.set(state.inputs);
patboard.set(state.patboard);
patdest = state.patdest;
refreshall(); refreshall();
} }
@ -393,7 +515,9 @@ const _BallyAstrocade = function(arcade:boolean) {
verbl: verbl, verbl: verbl,
rotcount: rotcount, rotcount: rotcount,
rotdata: rotdata.slice(0), rotdata: rotdata.slice(0),
intst: intst intst: intst,
patboard: patboard.slice(0),
patdest: patdest,
}; };
} }
this.reset = () => { this.reset = () => {
@ -450,6 +574,7 @@ export class BallyAstrocade extends BasicScanlineMachine implements AcceptsPaddl
ram : Uint8Array; ram : Uint8Array;
cpu : Z80; cpu : Z80;
m; // _BallyAstrocade m; // _BallyAstrocade
arcade : boolean;
psg: AstrocadeAudio; psg: AstrocadeAudio;
audioadapter; audioadapter;
@ -458,6 +583,7 @@ export class BallyAstrocade extends BasicScanlineMachine implements AcceptsPaddl
constructor(arcade:boolean) { constructor(arcade:boolean) {
super(); super();
this.arcade = arcade;
this.cpu = new Z80(); this.cpu = new Z80();
this.psg = new AstrocadeAudio(new MasterAudio()); this.psg = new AstrocadeAudio(new MasterAudio());
this.audioadapter = new TssChannelAdapter(this.psg.psg, audioOversample, this.sampleRate); this.audioadapter = new TssChannelAdapter(this.psg.psg, audioOversample, this.sampleRate);
@ -471,6 +597,12 @@ export class BallyAstrocade extends BasicScanlineMachine implements AcceptsPaddl
//this.cpuCyclesPerVisible = this.cpuCyclesPerLine - this.cpuCyclesPerHBlank; //this.cpuCyclesPerVisible = this.cpuCyclesPerLine - this.cpuCyclesPerHBlank;
this.m = new _BallyAstrocade(arcade); this.m = new _BallyAstrocade(arcade);
this.m.init(this, this.cpu, this.ram, this.inputs, this.psg); this.m.init(this, this.cpu, this.ram, this.inputs, this.psg);
if (arcade) {
this.inputs[0x10] = 0xff; // switches (active low)
this.inputs[0x11] = 0xff; // switches (active low)
this.inputs[0x12] = 0x00;
this.inputs[0x13] = 0x08; // dip switches
}
} }
read(a:number) : number { read(a:number) : number {
@ -556,8 +688,16 @@ export class BallyAstrocade extends BasicScanlineMachine implements AcceptsPaddl
case 'Astro': return this.m.toLongString(state); case 'Astro': return this.m.toLongString(state);
} }
} }
getRasterCanvasPosition() { return { x: this.getRasterX(), y: this.getRasterY() }; } getRasterCanvasPosition() {
return { x: this.getRasterX(), y: this.getRasterY() };
}
getVideoParams() {
if (this.arcade) {
return { width: 320, height: 204, rotate: 180 };
} else {
return { width: 160, height: 102 };
}
}
} }
///// /////
@ -591,16 +731,6 @@ class AstrocadeAudio extends AY38910_Audio {
} }
} }
export const _BallyArcade = function() {
this.__proto__ = new (_BallyAstrocade as any)(true);
// TODO: inputs[0x13] = 0xfe; // dip switch on arcade
// TODO: arcade controls, bit blit
var _in = this.saveControlsState();
_in.in[0x10] = 0xff; // switches
_in.in[0x13] = 0xfe; // dip switches
this.loadControlsState(_in);
}
///// /////
//http://glankonian.com/~lance/astrocade_palette.html //http://glankonian.com/~lance/astrocade_palette.html

View File

@ -26,6 +26,10 @@ const ASTROCADE_BIOS_PRESETS = [
{ id: 'bios.c', name: 'BIOS' }, { id: 'bios.c', name: 'BIOS' },
]; ];
const ASTROCADE_ARCADE_PRESETS = [
{ id: 'hello.c', name: 'Hello Graphics' },
];
class BallyAstrocadePlatform extends BaseZ80MachinePlatform<BallyAstrocade> implements Platform { class BallyAstrocadePlatform extends BaseZ80MachinePlatform<BallyAstrocade> implements Platform {
newMachine() { return new BallyAstrocade(false); } newMachine() { return new BallyAstrocade(false); }
@ -52,10 +56,13 @@ class BallyAstrocadeBIOSPlatform extends BallyAstrocadePlatform implements Platf
class BallyArcadePlatform extends BallyAstrocadePlatform implements Platform { class BallyArcadePlatform extends BallyAstrocadePlatform implements Platform {
newMachine() { return new BallyAstrocade(true); } newMachine() { return new BallyAstrocade(true); }
getPresets() { return ASTROCADE_ARCADE_PRESETS; }
getMemoryMap = function() { return { main:[ getMemoryMap = function() { return { main:[
{name:'ROM',start:0x0,size:0x4000,type:'rom'},
{name:'Magic RAM',start:0x0,size:0x4000,type:'ram'}, {name:'Magic RAM',start:0x0,size:0x4000,type:'ram'},
{name:'Screen RAM',start:0x4000,size:0x4000,type:'ram'}, {name:'Screen RAM',start:0x4000,size:0x4000,type:'ram'},
{name:'ROM',start:0x8000,size:0x4000,type:'rom'},
] } }; ] } };
} }