refactored mw8080 to use BasicZ80ScanlinePlatform

This commit is contained in:
Steven Hugg 2019-05-22 21:07:27 -04:00
parent 9699cea117
commit d9de3981c9
5 changed files with 182 additions and 155 deletions

View File

@ -141,7 +141,7 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
<span class="dropdown">
<a class="btn dropdown-toggle hidden-xs toolbarMenuButton" id="platformsMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
PLATFORMS <span class="caret"></span>
<span>PLATFORMS</span> <span class="caret"></span>
</a>
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="platformsMenuButton">
<li class="dropdown dropdown-submenu">

70
package-lock.json generated
View File

@ -240,6 +240,12 @@
"graceful-readlink": ">= 1.0.0"
}
},
"commandpost": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/commandpost/-/commandpost-1.4.0.tgz",
"integrity": "sha512-aE2Y4MTFJ870NuB/+2z1cXBhSBBzRydVVjzhFC4gtenEhpnj15yu0qptWGJsO9YGrcPZ3ezX8AWb1VA391MKpQ==",
"dev": true
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -324,6 +330,26 @@
"safer-buffer": "^2.1.0"
}
},
"editorconfig": {
"version": "0.15.3",
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
"integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==",
"dev": true,
"requires": {
"commander": "^2.19.0",
"lru-cache": "^4.1.5",
"semver": "^5.6.0",
"sigmund": "^1.0.1"
},
"dependencies": {
"commander": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
"dev": true
}
}
},
"encoding": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
@ -669,6 +695,16 @@
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
"dev": true
},
"lru-cache": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
"dev": true,
"requires": {
"pseudomap": "^1.0.2",
"yallist": "^2.1.2"
}
},
"lzg": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/lzg/-/lzg-1.0.0.tgz",
@ -903,6 +939,12 @@
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
"dev": true
},
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
"dev": true
},
"psl": {
"version": "1.1.31",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
@ -1014,6 +1056,18 @@
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
"dev": true
},
"semver": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
"dev": true
},
"sigmund": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@ -1120,6 +1174,16 @@
"integrity": "sha512-Y21Xqe54TBVp+VDSNbuDYdGw0BpoR/Q6wo/+35M8PAU0vipahnyduJWirxxdxjsAkS7hue53x2zp8gz7F05u0A==",
"dev": true
},
"typescript-formatter": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/typescript-formatter/-/typescript-formatter-7.2.2.tgz",
"integrity": "sha512-V7vfI9XArVhriOTYHPzMU2WUnm5IMdu9X/CPxs8mIMGxmTBFpDABlbkBka64PZJ9/xgQeRpK8KzzAG4MPzxBDQ==",
"dev": true,
"requires": {
"commandpost": "^1.0.0",
"editorconfig": "^0.15.0"
}
},
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
@ -1231,6 +1295,12 @@
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-1.3.1.tgz",
"integrity": "sha512-tGkGJkN8XqCod7OT+EvGYK5Z4SfDQGD30zAa58OcnAa0RRWgzUEK72tkXhsX1FZd+rgnhRxFtmO+ihkp8LHSkw==",
"dev": true
},
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
"dev": true
}
}
}

View File

@ -1091,6 +1091,7 @@ export abstract class BasicZ80ScanlinePlatform extends BaseZ80Platform {
timer;
audio;
psg;
pixels : Uint32Array;
inputs = new Uint8Array(16);
mainElement : HTMLElement;
@ -1117,6 +1118,7 @@ export abstract class BasicZ80ScanlinePlatform extends BaseZ80Platform {
this.cpu = this.newCPU(this.membus, this.iobus);
this.video = new RasterVideo(this.mainElement, this.canvasWidth, this.numVisibleScanlines, this.getVideoOptions());
this.video.create();
this.pixels = this.video.getFrameData();
setKeyboardFromMap(this.video, this.inputs, this.getKeyboardMap(), this.getKeyboardFunction());
this.timer = new AnimationTimer(60, this.nextFrame.bind(this));
}

View File

@ -1,200 +1,154 @@
"use strict";
import { Platform, BaseZ80Platform } from "../baseplatform";
import { Platform, BasicZ80ScanlinePlatform, BaseZ80Platform } from "../baseplatform";
import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap } from "../emu";
import { hex } from "../util";
// http://www.computerarcheology.com/Arcade/
const MW8080BW_PRESETS = [
{id:'gfxtest.c', name:'Graphics Test'},
{id:'shifter.c', name:'Sprite w/ Bit Shifter'},
{id:'game2.c', name:'Cosmic Impalas'},
{ id: 'gfxtest.c', name: 'Graphics Test' },
{ id: 'shifter.c', name: 'Sprite w/ Bit Shifter' },
{ id: 'game2.c', name: 'Cosmic Impalas' },
];
const _Midway8080BWPlatform = function(mainElement) {
const SPACEINV_KEYCODE_MAP = makeKeycodeMap([
[Keys.VK_SPACE, 1, 0x10], // P1
[Keys.VK_LEFT, 1, 0x20],
[Keys.VK_RIGHT, 1, 0x40],
[Keys.VK_S, 2, 0x10], // P2
[Keys.VK_A, 2, 0x20],
[Keys.VK_D, 2, 0x40],
[Keys.VK_5, 1, 0x1],
[Keys.VK_1, 1, 0x4],
[Keys.VK_2, 1, 0x2],
]);
var cpu, ram, membus, iobus, rom;
var probe;
var video, timer, pixels, displayPCs;
var inputs = [0xe,0x8,0x0];
var bitshift_offset = 0;
var bitshift_register = 0;
var watchdog_counter;
const cpuFrequency = 1996800;
const cpuCyclesPerLine = cpuFrequency/(60*224); // TODO
const INITIAL_WATCHDOG = 256;
const PIXEL_ON = 0xffeeeeee;
const PIXEL_OFF = 0xff000000;
const INITIAL_WATCHDOG = 256;
const PIXEL_ON = 0xffeeeeee;
const PIXEL_OFF = 0xff000000;
const SPACEINV_KEYCODE_MAP = makeKeycodeMap([
[Keys.VK_SPACE, 1, 0x10], // P1
[Keys.VK_LEFT, 1, 0x20],
[Keys.VK_RIGHT, 1, 0x40],
[Keys.VK_S, 2, 0x10], // P2
[Keys.VK_A, 2, 0x20],
[Keys.VK_D, 2, 0x40],
[Keys.VK_5, 1, 0x1],
[Keys.VK_1, 1, 0x4],
[Keys.VK_2, 1, 0x2],
]);
class Midway8080BWPlatform extends BaseZ80Platform implements Platform {
getPresets() {
return MW8080BW_PRESETS;
}
class Midway8080BWPlatform extends BasicZ80ScanlinePlatform implements Platform {
cpuFrequency = 1996800; // MHz
canvasWidth = 256;
numTotalScanlines = 262;
numVisibleScanlines = 224;
defaultROMSize = 0x2000;
start = function() {
ram = new RAM(0x2000);
//displayPCs = new Uint16Array(new ArrayBuffer(0x2000*2));
membus = {
bitshift_offset = 0;
bitshift_register = 0;
watchdog_counter;
getPresets() { return MW8080BW_PRESETS; }
getKeyboardMap() { return SPACEINV_KEYCODE_MAP; }
getVideoOptions() { return { rotate: -90 }; }
newRAM() { return new Uint8Array(0x2000); }
newMembus() {
return {
read: newAddressDecoder([
[0x0000, 0x1fff, 0x1fff, function(a) { return rom ? rom[a] : 0; }],
[0x2000, 0x3fff, 0x1fff, function(a) { return ram.mem[a]; }],
]),
write: newAddressDecoder([
[0x2000, 0x23ff, 0x3ff, function(a,v) { ram.mem[a] = v; }],
[0x2400, 0x3fff, 0x1fff, function(a,v) {
ram.mem[a] = v;
var ofs = (a - 0x400)<<3;
for (var i=0; i<8; i++)
pixels[ofs+i] = (v & (1<<i)) ? PIXEL_ON : PIXEL_OFF;
if (displayPCs) displayPCs[a] = cpu.getPC(); // save program counter
}],
]),
[0x0000, 0x1fff, 0x1fff, (a) => { return this.rom ? this.rom[a] : 0; }],
[0x2000, 0x3fff, 0x1fff, (a) => { return this.ram[a]; }],
]),
write: newAddressDecoder([
[0x2000, 0x23ff, 0x3ff, (a, v) => { this.ram[a] = v; }],
[0x2400, 0x3fff, 0x1fff, (a, v) => {
this.ram[a] = v;
var ofs = (a - 0x400) << 3;
for (var i = 0; i < 8; i++) {
this.pixels[ofs + i] = (v & (1 << i)) ? PIXEL_ON : PIXEL_OFF;
}
//if (displayPCs) displayPCs[a] = cpu.getPC(); // save program counter
}],
]),
isContended: function() { return false; },
};
iobus = {
read: function(addr) {
addr &= 0x3;
}
newIOBus() {
return {
read: (addr) => {
addr &= 0x3;
//console.log('IO read', hex(addr,4));
switch (addr) {
case 0:
case 1:
case 2:
return inputs[addr];
return this.inputs[addr];
case 3:
return (bitshift_register >> (8-bitshift_offset)) & 0xff;
return (this.bitshift_register >> (8 - this.bitshift_offset)) & 0xff;
}
return 0;
},
write: function(addr, val) {
addr &= 0x7;
val &= 0xff;
},
write: (addr, val) => {
addr &= 0x7;
val &= 0xff;
//console.log('IO write', hex(addr,4), hex(val,2));
switch (addr) {
case 2:
bitshift_offset = val & 0x7;
this.bitshift_offset = val & 0x7;
break;
case 3:
case 5:
// TODO: sound
break;
case 4:
bitshift_register = (bitshift_register >> 8) | (val << 8);
this.bitshift_register = (this.bitshift_register >> 8) | (val << 8);
break;
case 6:
watchdog_counter = INITIAL_WATCHDOG;
this.watchdog_counter = INITIAL_WATCHDOG;
break;
}
}
}
};
cpu = this.newCPU(membus, iobus);
video = new RasterVideo(mainElement,256,224,{rotate:-90});
video.create();
}
startScanline(sl: number) {
}
drawScanline(sl: number) {
// at end of scanline
if (sl == 95)
this.cpu.requestInterrupt(0x8); // RST $8
else if (sl == 223)
this.cpu.requestInterrupt(0x10); // RST $10
}
advance(novideo: boolean) {
super.advance(novideo);
if (this.watchdog_counter-- <= 0) {
console.log("WATCHDOG FIRED"); // TODO: alert on video
this.reset();
}
}
loadState(state) {
super.loadState(state);
this.bitshift_register = state.bsr;
this.bitshift_offset = state.bso;
this.watchdog_counter = state.wdc;
}
saveState() {
var state: any = super.saveState();
state.bsr = this.bitshift_register;
state.bso = this.bitshift_offset;
state.wdc = this.watchdog_counter;
return state;
}
reset() {
super.reset();
this.watchdog_counter = INITIAL_WATCHDOG;
}
}
/*
$(video.canvas).click(function(e) {
var x = Math.floor(e.offsetX * video.canvas.width / $(video.canvas).width());
var y = Math.floor(e.offsetY * video.canvas.height / $(video.canvas).height());
var addr = (x>>3) + (y*32) + 0x400;
if (displayPCs) console.log(x, y, hex(addr,4), "PC", hex(displayPCs[addr],4));
});
var idata = video.getFrameData();
setKeyboardFromMap(video, inputs, SPACEINV_KEYCODE_MAP);
pixels = video.getFrameData();
timer = new AnimationTimer(60, this.nextFrame.bind(this));
}
*/
readAddress(addr) {
return membus.read(addr);
}
advance(novideo : boolean) {
for (var sl=0; sl<224; sl++) {
this.runCPU(cpu, cpuCyclesPerLine);
if (sl == 95)
cpu.requestInterrupt(0x8); // RST $8
else if (sl == 223)
cpu.requestInterrupt(0x10); // RST $10
}
if (!novideo) {
video.updateFrame();
}
if (watchdog_counter-- <= 0) {
console.log("WATCHDOG FIRED"); // TODO: alert on video
this.reset();
}
}
loadROM(title, data) {
rom = padBytes(data, 0x2000);
this.reset();
}
loadState(state) {
cpu.loadState(state.c);
ram.mem.set(state.b);
bitshift_register = state.bsr;
bitshift_offset = state.bso;
watchdog_counter = state.wdc;
inputs[0] = state.in0;
inputs[1] = state.in1;
inputs[2] = state.in2;
}
saveState() {
return {
c:this.getCPUState(),
b:ram.mem.slice(0),
bsr:bitshift_register,
bso:bitshift_offset,
wdc:watchdog_counter,
in0:inputs[0],
in1:inputs[1],
in2:inputs[2],
};
}
loadControlsState(state) {
inputs[0] = state.in0;
inputs[1] = state.in1;
inputs[2] = state.in2;
}
saveControlsState() {
return {
in0:inputs[0],
in1:inputs[1],
in2:inputs[2],
};
}
getCPUState() {
return cpu.saveState();
}
isRunning() {
return timer && timer.isRunning();
}
pause() {
timer.stop();
}
resume() {
timer.start();
}
reset() {
cpu.reset();
cpu.setTstates(0);
watchdog_counter = INITIAL_WATCHDOG;
}
}
return new Midway8080BWPlatform();
}
PLATFORMS['mw8080bw'] = _Midway8080BWPlatform;
PLATFORMS['mw8080bw'] = Midway8080BWPlatform;

View File

@ -1842,6 +1842,7 @@ export function startUI(loadplatform : boolean) {
platform_id = qs['platform'] = "vcs";
}
$("#item_platform_"+platform_id).addClass("dropdown-item-checked");
$("#platform_name").text(platform_id);
setupSplits();
// create store
store_id = repo_id || getBasePlatform(platform_id);