mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-02-22 12:29:06 +00:00
started on MAME platform support; pause on page lose focus
This commit is contained in:
parent
992bf927e0
commit
4974e395e5
@ -193,6 +193,8 @@ a.dropdown-toggle {
|
||||
margin-bottom: 20px;
|
||||
background: #000;
|
||||
outline-color: #666;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
canvas.pixelated {
|
||||
image-rendering: optimizeSpeed; /* Older versions of FF */
|
||||
|
@ -29,3 +29,9 @@ debugjs/%.js: js/%.bc
|
||||
-s FORCE_FILESYSTEM=1 \
|
||||
$< -o $@ $(ARGS_$*) \
|
||||
|
||||
js/fssdcc.js:
|
||||
ln -s ./sdcc/device/include include
|
||||
ln -s ./sdcc/device/lib/build lib
|
||||
python $(EMSCRIPTEN)/tools/file_packager.py js/fssdcc.data \
|
||||
--preload include lib/z80 \
|
||||
--separate-metadata --js-output=js/fssdcc.js
|
||||
|
68
mame/cfg/coleco.cfg
Normal file
68
mame/cfg/coleco.cfg
Normal file
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is autogenerated; comments and unknown tags will be stripped -->
|
||||
<mameconfig version="10">
|
||||
<system name="coleco">
|
||||
<input>
|
||||
<port tag=":STD_KEYPAD1" type="KEYPAD" mask="1" defvalue="1">
|
||||
<newseq type="standard">
|
||||
KEYCODE_0
|
||||
</newseq>
|
||||
</port>
|
||||
<port tag=":STD_KEYPAD1" type="KEYPAD" mask="2" defvalue="2">
|
||||
<newseq type="standard">
|
||||
KEYCODE_1
|
||||
</newseq>
|
||||
</port>
|
||||
<port tag=":STD_KEYPAD1" type="KEYPAD" mask="4" defvalue="4">
|
||||
<newseq type="standard">
|
||||
KEYCODE_2
|
||||
</newseq>
|
||||
</port>
|
||||
<port tag=":STD_KEYPAD1" type="KEYPAD" mask="8" defvalue="8">
|
||||
<newseq type="standard">
|
||||
KEYCODE_3
|
||||
</newseq>
|
||||
</port>
|
||||
<port tag=":STD_KEYPAD1" type="KEYPAD" mask="16" defvalue="16">
|
||||
<newseq type="standard">
|
||||
KEYCODE_4
|
||||
</newseq>
|
||||
</port>
|
||||
<port tag=":STD_KEYPAD1" type="KEYPAD" mask="32" defvalue="32">
|
||||
<newseq type="standard">
|
||||
KEYCODE_5
|
||||
</newseq>
|
||||
</port>
|
||||
<port tag=":STD_KEYPAD1" type="KEYPAD" mask="64" defvalue="64">
|
||||
<newseq type="standard">
|
||||
KEYCODE_6
|
||||
</newseq>
|
||||
</port>
|
||||
<port tag=":STD_KEYPAD1" type="KEYPAD" mask="128" defvalue="128">
|
||||
<newseq type="standard">
|
||||
KEYCODE_7
|
||||
</newseq>
|
||||
</port>
|
||||
<port tag=":STD_KEYPAD1" type="KEYPAD" mask="256" defvalue="256">
|
||||
<newseq type="standard">
|
||||
KEYCODE_8
|
||||
</newseq>
|
||||
</port>
|
||||
<port tag=":STD_KEYPAD1" type="KEYPAD" mask="512" defvalue="512">
|
||||
<newseq type="standard">
|
||||
KEYCODE_9
|
||||
</newseq>
|
||||
</port>
|
||||
<port tag=":STD_KEYPAD1" type="KEYPAD" mask="1024" defvalue="1024">
|
||||
<newseq type="standard">
|
||||
KEYCODE_MINUS
|
||||
</newseq>
|
||||
</port>
|
||||
<port tag=":STD_KEYPAD1" type="KEYPAD" mask="2048" defvalue="2048">
|
||||
<newseq type="standard">
|
||||
KEYCODE_EQUALS
|
||||
</newseq>
|
||||
</port>
|
||||
</input>
|
||||
</system>
|
||||
</mameconfig>
|
131
src/emu.js
131
src/emu.js
@ -19,8 +19,6 @@ function __createCanvas(mainElement, width, height) {
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
canvas.classList.add("emuvideo");
|
||||
canvas.style.width = "100%";
|
||||
canvas.style.height = "100%";
|
||||
canvas.tabIndex = "-1"; // Make it focusable
|
||||
|
||||
fsElement.appendChild(canvas);
|
||||
@ -594,16 +592,18 @@ var BaseZ80Platform = function() {
|
||||
this.cpuStateToLongString = function(c) {
|
||||
return cpuStateToLongString_Z80(c);
|
||||
}
|
||||
this.getToolForFilename = function(fn) {
|
||||
this.getToolForFilename = getToolForFilename_z80;
|
||||
this.getDefaultExtension = function() { return ".c"; };
|
||||
// TODO
|
||||
//this.getOpcodeMetadata = function() { }
|
||||
}
|
||||
|
||||
function getToolForFilename_z80(fn) {
|
||||
if (fn.endsWith(".c")) return "sdcc";
|
||||
if (fn.endsWith(".s")) return "sdasz80";
|
||||
if (fn.endsWith(".ns")) return "naken";
|
||||
return "z80asm";
|
||||
}
|
||||
this.getDefaultExtension = function() { return ".c"; };
|
||||
// TODO
|
||||
//this.getOpcodeMetadata = function() { }
|
||||
}
|
||||
|
||||
////// 6809
|
||||
|
||||
@ -862,3 +862,120 @@ var BusProbe = function(bus) {
|
||||
bus.write(a,v);
|
||||
}
|
||||
}
|
||||
|
||||
/// MAME SUPPORT
|
||||
|
||||
var BaseMAMEPlatform = function() {
|
||||
var self = this;
|
||||
|
||||
var loaded = false;
|
||||
var romfn;
|
||||
var romdata;
|
||||
var video;
|
||||
var preload_files;
|
||||
|
||||
this.luacall = function(s) {
|
||||
//console.log(s);
|
||||
Module.ccall('_Z13js_lua_stringPKc', 'void', ['string'], [s+""]);
|
||||
}
|
||||
|
||||
this.clearDebug = function() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
this.pause = function() {
|
||||
if (loaded) this.luacall('emu.pause()');
|
||||
}
|
||||
|
||||
this.resume = function() {
|
||||
if (loaded) this.luacall('emu.unpause()');
|
||||
}
|
||||
|
||||
this.reset = function() {
|
||||
this.luacall('manager:machine():soft_reset()');
|
||||
}
|
||||
|
||||
this.isRunning = function() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
this.startModule = function(mainElement, opts) {
|
||||
romfn = opts.romfn;
|
||||
if (!romdata) romdata = new RAM(opts.romsize).mem;
|
||||
// create canvas
|
||||
video = new RasterVideo(mainElement, opts.width, opts.height);
|
||||
video.create();
|
||||
$(video.canvas).attr('id','canvas');
|
||||
// load asm.js module
|
||||
console.log("loading", opts.jsfile);
|
||||
var script = document.createElement('script');
|
||||
window.JSMESS = {};
|
||||
window.Module = {
|
||||
arguments: [opts.driver, '-verbose', '-window', '-nokeepaspect', '-resolution', canvas.width+'x'+canvas.height, '-cart', romfn],
|
||||
screenIsReadOnly: true,
|
||||
print: function (text) { console.log(text); },
|
||||
canvas:video.canvas,
|
||||
doNotCaptureKeyboard:true,
|
||||
keyboardListeningElement:video.canvas,
|
||||
preInit: function () {
|
||||
console.log("loading FS");
|
||||
ENV.SDL_EMSCRIPTEN_KEYBOARD_ELEMENT = 'canvas';
|
||||
if (opts.cfgfile) {
|
||||
FS.mkdir('/cfg');
|
||||
FS.writeFile('/cfg/' + opts.cfgfile, opts.cfgdata, {encoding:'utf8'});
|
||||
}
|
||||
if (opts.biosfile) {
|
||||
FS.mkdir('/roms');
|
||||
FS.mkdir('/roms/' + opts.driver);
|
||||
FS.writeFile('/roms/' + opts.biosfile, opts.biosdata, {encoding:'binary'});
|
||||
}
|
||||
FS.mkdir('/emulator');
|
||||
FS.writeFile(romfn, romdata, {encoding:'binary'});
|
||||
if (opts.preInit) {
|
||||
opts.preInit(self);
|
||||
}
|
||||
$(video.canvas).click(function(e) {
|
||||
video.canvas.focus();
|
||||
});
|
||||
loaded = true;
|
||||
}
|
||||
};
|
||||
// preload files
|
||||
// TODO: ensure loaded
|
||||
if (opts.cfgfile) {
|
||||
$.get('mame/cfg/' + opts.cfgfile, function(data) {
|
||||
opts.cfgdata = data;
|
||||
console.log("loaded " + opts.cfgfile);
|
||||
}, 'text');
|
||||
}
|
||||
if (opts.biosfile) {
|
||||
var oReq = new XMLHttpRequest();
|
||||
oReq.open("GET", 'mame/roms/' + opts.biosfile, true);
|
||||
oReq.responseType = "arraybuffer";
|
||||
oReq.onload = function(oEvent) {
|
||||
console.log("loaded " + opts.biosfile);
|
||||
opts.biosdata = new Uint8Array(oReq.response);
|
||||
};
|
||||
oReq.send();
|
||||
}
|
||||
// start loading script
|
||||
script.src = 'mame/' + opts.jsfile;
|
||||
document.getElementsByTagName('head')[0].appendChild(script);
|
||||
}
|
||||
|
||||
this.loadRegion = function(region, data) {
|
||||
romdata = data;
|
||||
if (loaded) {
|
||||
FS.writeFile(romfn, data, {encoding:'binary'});
|
||||
//self.luacall('cart=manager:machine().images["cart"]\nprint(cart:filename())\ncart:load("' + romfn + '")\n');
|
||||
var s = 'mem = manager:machine():memory().regions["' + region + '"]\n';
|
||||
for (var i=0; i<data.length; i+=4) {
|
||||
var v = data[i] + (data[i+1]<<8) + (data[i+2]<<16) + (data[i+3]<<24);
|
||||
s += 'mem:write_u32(' + i + ',' + v + ')\n'; // TODO: endian?
|
||||
}
|
||||
self.luacall(s);
|
||||
self.reset();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
217
src/platform/coleco.js
Normal file
217
src/platform/coleco.js
Normal file
@ -0,0 +1,217 @@
|
||||
"use strict";
|
||||
|
||||
// http://www.colecovision.eu/ColecoVision/development/tutorial1.shtml
|
||||
// http://www.colecovision.eu/ColecoVision/development/libcv.shtml
|
||||
// http://www.kernelcrash.com/blog/recreating-the-colecovision/2016/01/27/
|
||||
// http://www.atarihq.com/danb/files/CV-Tech.txt
|
||||
// http://www.colecoboxart.com/faq/FAQ05.htm
|
||||
|
||||
var ColecoVision_PRESETS = [
|
||||
{id:'minimal.c', name:'Minimal Example'},
|
||||
];
|
||||
|
||||
var ColecoVisionPlatform = function(mainElement) {
|
||||
var self = this;
|
||||
this.__proto__ = new BaseZ80Platform();
|
||||
|
||||
var cpu, ram, membus, iobus, rom, bios;
|
||||
var video, audio, psg, timer, pixels;
|
||||
var inputs = [0xff, 0xff, 0xff, 0xff^0x8]; // most things active low
|
||||
var palbank = 0;
|
||||
|
||||
var XTAL = 3579545*2;
|
||||
var totalScanlinesPerFrame = 262.5;
|
||||
var visibleScanlinesPerFrame = 192;
|
||||
var visiblePixelsPerScanline = 256;
|
||||
var cpuFrequency = XTAL/2;
|
||||
var hsyncFrequency = XTAL*3/(2*322);
|
||||
var vsyncFrequency = hsyncFrequency/totalScanlinesPerFrame;
|
||||
var cpuCyclesPerLine = cpuFrequency/hsyncFrequency;
|
||||
var framestats;
|
||||
|
||||
function RGB(r,g,b) {
|
||||
return (r << 0) + (g << 8) + (b << 16) | 0xff000000;
|
||||
}
|
||||
|
||||
var palette = [
|
||||
RGB(0x00,0x00,0x00),RGB(0x00,0x00,0x00),RGB(0x47,0xB7,0x3B),RGB(0x7C,0xCF,0x6F),
|
||||
RGB(0x5D,0x4E,0xFF),RGB(0x80,0x72,0xFF),RGB(0xB6,0x62,0x47),RGB(0x5D,0xC8,0xED),
|
||||
RGB(0xD7,0x6B,0x48),RGB(0xFB,0x8F,0x6C),RGB(0xC3,0xCD,0x41),RGB(0xD3,0xDA,0x76),
|
||||
RGB(0x3E,0x9F,0x2F),RGB(0xB6,0x64,0xC7),RGB(0xCC,0xCC,0xCC),RGB(0xFF,0xFF,0xFF)
|
||||
];
|
||||
|
||||
// videoram 0xc000-0xc3ff
|
||||
// RAM 0xc400-0xc7ff
|
||||
// charram 0xc800-0xcfff
|
||||
function drawScanline(pixels, sl) {
|
||||
if (sl >= visibleScanlinesPerFrame) return;
|
||||
var pixofs = sl * 256;
|
||||
var outi = pixofs; // starting output pixel in frame buffer
|
||||
var vramofs = (sl>>3)<<5; // offset in VRAM
|
||||
var yy = sl & 7; // y offset within tile
|
||||
for (var xx=0; xx<32; xx++) {
|
||||
var code = ram.mem[vramofs+xx];
|
||||
var data = ram.mem[0x800 + (code<<3) + yy];
|
||||
var col = (code>>5) + (palbank<<3);
|
||||
var color1 = palette[col&15];
|
||||
var color2 = 0;
|
||||
for (var i=0; i<8; i++) {
|
||||
var bm = 128>>i;
|
||||
pixels[outi] = (data&bm) ? color2 : color1;
|
||||
outi++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var CARNIVAL_KEYCODE_MAP = makeKeycodeMap([
|
||||
[Keys.VK_SPACE, 2, -0x20],
|
||||
[Keys.VK_SHIFT, 2, -0x40],
|
||||
[Keys.VK_LEFT, 1, -0x10],
|
||||
[Keys.VK_RIGHT, 1, -0x20],
|
||||
[Keys.VK_UP, 1, -0x40],
|
||||
[Keys.VK_DOWN, 1, -0x80],
|
||||
[Keys.VK_1, 2, -0x10],
|
||||
[Keys.VK_2, 3, -0x20],
|
||||
[Keys.VK_5, 3, 0x8],
|
||||
]);
|
||||
|
||||
this.getPresets = function() {
|
||||
return ColecoVision_PRESETS;
|
||||
}
|
||||
|
||||
this.start = function() {
|
||||
ram = new RAM(0x400);
|
||||
//bios = COLECO_BIOS;
|
||||
membus = {
|
||||
read: new AddressDecoder([
|
||||
[0x0000, 0x1fff, 0x1fff, function(a) { return bios ? bios[a] : null; }],
|
||||
[0x6000, 0x7fff, 0x3ff, function(a) { return ram.mem[a]; }],
|
||||
]),
|
||||
write: new AddressDecoder([
|
||||
[0x6000, 0x7fff, 0x3ff, function(a,v) { ram.mem[a] = v; }],
|
||||
]),
|
||||
isContended: function() { return false; },
|
||||
};
|
||||
this.readAddress = membus.read;
|
||||
iobus = {
|
||||
read: function(addr) {
|
||||
return inputs[addr&3];
|
||||
},
|
||||
write: function(addr, val) {
|
||||
console.log(addr,val);
|
||||
}
|
||||
};
|
||||
cpu = this.newCPU(membus, iobus);
|
||||
video = new RasterVideo(mainElement,visiblePixelsPerScanline,visibleScanlinesPerFrame);
|
||||
audio = new MasterAudio();
|
||||
psg = new AY38910_Audio(audio);
|
||||
//var speech = new VotraxSpeech();
|
||||
//audio.master.addChannel(speech);
|
||||
video.create();
|
||||
var idata = video.getFrameData();
|
||||
setKeyboardFromMap(video, inputs, CARNIVAL_KEYCODE_MAP);
|
||||
pixels = video.getFrameData();
|
||||
timer = new AnimationTimer(60, function() {
|
||||
if (!self.isRunning())
|
||||
return;
|
||||
var debugCond = self.getDebugCallback();
|
||||
var targetTstates = cpu.getTstates();
|
||||
for (var sl=0; sl<visibleScanlinesPerFrame; sl++) {
|
||||
drawScanline(pixels, sl);
|
||||
targetTstates += cpuCyclesPerLine;
|
||||
self.runCPU(cpu, targetTstates - cpu.getTstates());
|
||||
}
|
||||
video.updateFrame();
|
||||
self.restartDebugState();
|
||||
});
|
||||
}
|
||||
|
||||
this.loadROM = function(title, data) {
|
||||
rom = padBytes(data, 0x8000);
|
||||
self.reset();
|
||||
}
|
||||
|
||||
this.loadState = function(state) {
|
||||
cpu.loadState(state.c);
|
||||
ram.mem.set(state.b);
|
||||
inputs[0] = state.in0;
|
||||
inputs[1] = state.in1;
|
||||
inputs[2] = state.in2;
|
||||
inputs[3] = state.in3;
|
||||
palbank = state.pb;
|
||||
}
|
||||
this.saveState = function() {
|
||||
return {
|
||||
c:self.getCPUState(),
|
||||
b:ram.mem.slice(0),
|
||||
in0:inputs[0],
|
||||
in1:inputs[1],
|
||||
in2:inputs[2],
|
||||
in3:inputs[3],
|
||||
pb:palbank,
|
||||
};
|
||||
}
|
||||
this.getCPUState = function() {
|
||||
return cpu.saveState();
|
||||
}
|
||||
|
||||
this.isRunning = function() {
|
||||
return timer && timer.isRunning();
|
||||
}
|
||||
this.pause = function() {
|
||||
timer.stop();
|
||||
audio.stop();
|
||||
}
|
||||
this.resume = function() {
|
||||
timer.start();
|
||||
audio.start();
|
||||
}
|
||||
this.reset = function() {
|
||||
cpu.reset();
|
||||
psg.reset();
|
||||
if (!this.getDebugCallback()) cpu.setTstates(0); // TODO?
|
||||
}
|
||||
}
|
||||
|
||||
/// MAME support
|
||||
|
||||
var ColecoVisionMAMEPlatform = function(mainElement) {
|
||||
var self = this;
|
||||
this.__proto__ = new BaseMAMEPlatform();
|
||||
|
||||
//
|
||||
this.start = function() {
|
||||
self.startModule(mainElement, {
|
||||
jsfile:'mamecoleco.js',
|
||||
cfgfile:'coleco.cfg',
|
||||
biosfile:'coleco/313 10031-4005 73108a.u2',
|
||||
driver:'coleco',
|
||||
width:280*2,
|
||||
height:216*2,
|
||||
romfn:'/emulator/cart.rom',
|
||||
romsize:0x8000,
|
||||
preInit:function(_self) {
|
||||
/*
|
||||
console.log("Writing BIOS");
|
||||
var dir = '/roms/coleco';
|
||||
FS.mkdir('/roms');
|
||||
FS.mkdir(dir);
|
||||
FS.writeFile(dir + "/313 10031-4005 73108a.u2", COLECO_BIOS, {encoding:'binary'});
|
||||
*/
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
this.loadROM = function(title, data) {
|
||||
this.loadRegion(":coleco_cart:rom", data);
|
||||
}
|
||||
|
||||
this.getPresets = function() { return ColecoVision_PRESETS; }
|
||||
|
||||
this.getToolForFilename = getToolForFilename_z80;
|
||||
this.getDefaultExtension = function() { return ".c"; };
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
PLATFORMS['coleco'] = ColecoVisionMAMEPlatform;
|
@ -125,8 +125,6 @@ var VCSPlatform = function() {
|
||||
this.getDefaultExtension = function() { return ".a"; };
|
||||
};
|
||||
|
||||
PLATFORMS['vcs'] = VCSPlatform;
|
||||
|
||||
/// VCS TIMING ANALYSIS
|
||||
|
||||
var pc2minclocks = {};
|
||||
@ -268,3 +266,38 @@ function traceTiming() {
|
||||
trace_pending_at_pc = platform.getOriginPC();
|
||||
setCode(editor.getValue());
|
||||
}
|
||||
|
||||
///////////////
|
||||
|
||||
var VCSMAMEPlatform = function(mainElement) {
|
||||
var self = this;
|
||||
this.__proto__ = new BaseMAMEPlatform();
|
||||
|
||||
// MCFG_SCREEN_RAW_PARAMS( MASTER_CLOCK_NTSC, 228, 26, 26 + 160 + 16, 262, 24 , 24 + 192 + 31 )
|
||||
this.start = function() {
|
||||
self.startModule(mainElement, {
|
||||
jsfile:'mamea2600.js',
|
||||
driver:'a2600',
|
||||
width:176*2,
|
||||
height:223,
|
||||
romfn:'/emulator/cart.rom',
|
||||
romsize:0x1000,
|
||||
});
|
||||
}
|
||||
|
||||
this.loadROM = function(title, data) {
|
||||
this.loadRegion(":cartslot:cart:rom", data);
|
||||
}
|
||||
|
||||
this.getPresets = function() { return VCS_PRESETS; }
|
||||
|
||||
this.getToolForFilename = function(fn) {
|
||||
return "dasm";
|
||||
}
|
||||
this.getDefaultExtension = function() { return ".a"; };
|
||||
}
|
||||
|
||||
////////////////
|
||||
|
||||
PLATFORMS['vcs'] = VCSPlatform;
|
||||
PLATFORMS['vcs-mame'] = VCSMAMEPlatform;
|
||||
|
23
src/ui.js
23
src/ui.js
@ -1111,6 +1111,21 @@ function showBookLink() {
|
||||
$("#booklink_arcade").show();
|
||||
}
|
||||
|
||||
function addPageFocusHandlers() {
|
||||
document.addEventListener("visibilitychange", function() {
|
||||
if (document.visibilityState == 'hidden')
|
||||
platform.pause();
|
||||
else if (document.visibilityState == 'visible')
|
||||
platform.resume();
|
||||
});
|
||||
window.onfocus = function() {
|
||||
platform.resume();
|
||||
};
|
||||
window.onblur = function() {
|
||||
platform.pause();
|
||||
};
|
||||
}
|
||||
|
||||
function startPlatform() {
|
||||
initPlatform();
|
||||
if (!PLATFORMS[platform_id]) throw Error("Invalid platform '" + platform_id + "'.");
|
||||
@ -1124,6 +1139,7 @@ function startPlatform() {
|
||||
loadPreset(qs['file']);
|
||||
updateSelector();
|
||||
showBookLink();
|
||||
addPageFocusHandlers();
|
||||
return true;
|
||||
} else {
|
||||
// try to load last file (redirect)
|
||||
@ -1180,11 +1196,14 @@ function startUI(loadplatform) {
|
||||
// load and start platform object
|
||||
if (loadplatform) {
|
||||
var scriptfn = 'src/platform/' + platform_id.split('-')[0] + '.js';
|
||||
$.getScript(scriptfn, function() {
|
||||
var script = document.createElement('script');
|
||||
script.onload = function() {
|
||||
console.log("loaded platform", platform_id);
|
||||
startPlatform();
|
||||
showWelcomeMessage();
|
||||
});
|
||||
};
|
||||
script.src = scriptfn;
|
||||
document.getElementsByTagName('head')[0].appendChild(script);
|
||||
} else {
|
||||
startPlatform();
|
||||
showWelcomeMessage();
|
||||
|
Loading…
x
Reference in New Issue
Block a user